From c4be9b1cba4f04d4831a31d3a9f9061ee36c3c00 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 19 Nov 2022 21:16:24 +0800 Subject: [PATCH 001/164] Initial commit, change of data structs containing data for model and animation assets. Changed all other references to previous mode and mesh asset names --- .../Asset Types/Models/SHAnimationAsset.h | 88 +++++++++++++++++++ .../Assets/Asset Types/Models/SHMeshAsset.h | 60 +++++++++++++ .../Asset Types/{ => Models}/SHModelAsset.h | 30 +++---- .../Assets/Asset Types/Models/SHRigAsset.h | 48 ++++++++++ .../src/Assets/Asset Types/SHAnimationAsset.h | 30 ------- .../src/Assets/Asset Types/SHAssetIncludes.h | 2 +- .../Libraries/Loaders/SHModelLoader.cpp | 8 +- .../Assets/Libraries/Loaders/SHModelLoader.h | 4 +- SHADE_Engine/src/Assets/SHAssetManager.cpp | 4 +- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 4 +- .../MiddleEnd/Meshes/SHPrimitiveGenerator.cpp | 16 ++-- .../MiddleEnd/Meshes/SHPrimitiveGenerator.h | 22 ++--- SHADE_Engine/src/Resource/SHResourceManager.h | 4 +- 13 files changed, 238 insertions(+), 82 deletions(-) create mode 100644 SHADE_Engine/src/Assets/Asset Types/Models/SHAnimationAsset.h create mode 100644 SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h rename SHADE_Engine/src/Assets/Asset Types/{ => Models}/SHModelAsset.h (61%) create mode 100644 SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h delete mode 100644 SHADE_Engine/src/Assets/Asset Types/SHAnimationAsset.h diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHAnimationAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHAnimationAsset.h new file mode 100644 index 00000000..d7128977 --- /dev/null +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHAnimationAsset.h @@ -0,0 +1,88 @@ +/*************************************************************************//** + * \file SHAnimationAsset.h + * \author Loh Xiao Qi + * \date October 2022 + * \brief + * + * Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. + *****************************************************************************/ +#pragma once + +#include "Math/SHMath.h" +#include "Assets/Asset Types/SHAssetData.h" + +#include + + +namespace SHADE +{ + enum class SHAnimationBehaviour : uint8_t + { + DEFAULT = 0x0, + CONSTANT = 0x1, + LINEAR = 0x2, + REPEAT = 0x3 + }; + + // Smallest data containers + struct PositionKey + { + float time; + SHVec3 value; + }; + + struct RotationKey + { + float time; + SHVec4 value; + }; + + struct ScaleKey + { + float time; + SHVec3 value; + }; + + // Headers for read/write + struct SHAnimNodeInfo + { + uint32_t charCount; + uint32_t posKeyCount; + uint32_t rotKeyCount; + uint32_t scaKeyCount; + }; + + struct SHAnimDataHeader + { + uint32_t charCount; + uint32_t animNodeCount; + std::vector nodeHeaders; + }; + + // Main data containers + struct SHAnimData + { + std::string name; + SHAnimationBehaviour pre; + SHAnimationBehaviour post; + + std::vector positionKeys; + std::vector rotationKeys; + std::vector scaleKeys; + + }; + + struct SH_API SHAnimAsset : SHAssetData + { + std::string name; + + double duration; + double ticksPerSecond; + + std::vector nodeChannels; + //std::vector meshChannels; + //std::vector morphMeshChannels; + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h new file mode 100644 index 00000000..c10e0cc3 --- /dev/null +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * \file SHMeshAsset.h + * \author Loh Xiao Qi + * \date 19 November 2022 + * \brief + * + * \copyright Copyright (c) 2021 Digipen Institute of Technology. Reproduction + * or disclosure of this file or its contents without the prior + * written consent of Digipen Institute of Technology is prohibited. + ******************************************************************************/ +#pragma once + +#include "Math/SHMath.h" +#include "Assets/Asset Types/SHAssetData.h" + +#include +#include + +namespace SHADE +{ + struct SHMeshDataHeader + { + uint32_t vertexCount; + uint32_t indexCount; + uint32_t charCount; + uint32_t boneCount; + std::string name; + }; + + struct MeshBoneInfo + { + uint32_t charCount; + uint32_t weightCount; // Size should be same as boneCount + }; + + struct BoneWeight + { + uint32_t index; + float weight; + }; + + struct MeshBone + { + std::string name; + SHMatrix offset; + std::vector weights; + }; + + struct SH_API SHMeshAsset : SHAssetData + { + SHMeshDataHeader header; + std::vector VertexPositions; + std::vector VertexTangents; + std::vector VertexNormals; + std::vector VertexTexCoords; + std::vector Indices; + std::vector BonesInfo; + std::vector Bones; + }; +} diff --git a/SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHModelAsset.h similarity index 61% rename from SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h rename to SHADE_Engine/src/Assets/Asset Types/Models/SHModelAsset.h index c057678b..4425f555 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHModelAsset.h @@ -13,36 +13,26 @@ #pragma once #include -#include "Math/SHMath.h" -#include "SHAssetData.h" + +#include "Assets/Asset Types/Models/SHAnimationAsset.h" +#include "Assets/Asset Types/Models/SHMeshAsset.h" +#include "Assets/Asset Types/Models/SHRigAsset.h" namespace SHADE { - struct SHMeshAssetHeader - { - uint32_t vertexCount; - uint32_t indexCount; - std::string name; - }; - struct SHModelAssetHeader { size_t meshCount; - }; - - struct SH_API SHMeshData : SHAssetData - { - SHMeshAssetHeader header; - std::vector VertexPositions; - std::vector VertexTangents; - std::vector VertexNormals; - std::vector VertexTexCoords; - std::vector Indices; + size_t animCount; }; struct SH_API SHModelAsset : SHAssetData { SHModelAssetHeader header; - std::vector subMeshes; + + SHRigAsset rig; + + std::vector meshes; + std::vector anims; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h new file mode 100644 index 00000000..09ce5658 --- /dev/null +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * \file SHRigAsset.h + * \author Loh Xiao Qi + * \date 19 November 2022 + * \brief + * + * \copyright Copyright (c) 2021 Digipen Institute of Technology. Reproduction + * or disclosure of this file or its contents without the prior + * written consent of Digipen Institute of Technology is prohibited. + ******************************************************************************/ +#pragma once + +#include "Math/SHMath.h" +#include "Assets/Asset Types/SHAssetData.h" + +#include + +namespace SHADE +{ + struct RigDataHeader + { + uint32_t nodeCount; + std::vector charCounts; + }; + + struct RigNodeData + { + std::string name; + SHMatrix transform; + }; + + struct RigNode + { + uint32_t idRef; + std::vector children; + }; + + struct SH_API SHRigAsset : SHAssetData + { + ~SHRigAsset() + { + delete root; + } + + std::map nodeDataCollection; + RigNode* root; + }; +} diff --git a/SHADE_Engine/src/Assets/Asset Types/SHAnimationAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHAnimationAsset.h deleted file mode 100644 index b411a11e..00000000 --- a/SHADE_Engine/src/Assets/Asset Types/SHAnimationAsset.h +++ /dev/null @@ -1,30 +0,0 @@ -/*************************************************************************//** - * \file SHAnimationAsset.h - * \author Loh Xiao Qi - * \date October 2022 - * \brief - * - * Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. - *****************************************************************************/ -#pragma once - -#include -#include -#include "SHAssetData.h" - -namespace SHADE -{ - struct SH_API SHAnimationAsset : SHAssetData - { - std::string name; - - std::vector nodeChannels; - std::vector meshChannels; - std::vector morphMeshChannels; - - double duration; - double ticksPerSecond; - }; -} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/SHAssetIncludes.h b/SHADE_Engine/src/Assets/Asset Types/SHAssetIncludes.h index f4fb49f0..eb656516 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHAssetIncludes.h +++ b/SHADE_Engine/src/Assets/Asset Types/SHAssetIncludes.h @@ -1,4 +1,4 @@ #pragma once -#include "SHModelAsset.h" +#include "Models/SHModelAsset.h" #include "SHTextureAsset.h" \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 74aa5350..1f825a97 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -24,7 +24,7 @@ namespace SHADE ); } - void SHModelLoader::ReadData(std::ifstream& file, SHMeshLoaderHeader const& header, SHMeshData& data) noexcept + void SHModelLoader::ReadData(std::ifstream& file, SHMeshLoaderHeader const& header, SHMeshAsset& data) noexcept { auto const vertexVec3Byte{ sizeof(SHVec3) * header.vertexCount }; auto const vertexVec2Byte{ sizeof(SHVec2) * header.vertexCount }; @@ -64,13 +64,13 @@ namespace SHADE ); std::vector headers(model.header.meshCount); - model.subMeshes.resize(model.header.meshCount); + model.meshes.resize(model.header.meshCount); for (auto i{ 0 }; i < model.header.meshCount; ++i) { - model.subMeshes[i] = new SHMeshData(); + model.meshes[i] = new SHMeshAsset(); ReadHeader(file, headers[i]); - ReadData(file, headers[i], *model.subMeshes[i]); + ReadData(file, headers[i], *model.meshes[i]); } file.close(); } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h index 35dc4514..2074169c 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h @@ -10,7 +10,7 @@ * of DigiPen Institute of Technology is prohibited. *****************************************************************************/ #pragma once -#include "Assets/Asset Types/SHModelAsset.h" +#include "Assets/Asset Types/Models/SHModelAsset.h" #include "SHAssetLoader.h" #include @@ -27,7 +27,7 @@ namespace SHADE void ReadHeader(std::ifstream& file, SHMeshLoaderHeader& header) noexcept; - void ReadData(std::ifstream& file, SHMeshLoaderHeader const& header, SHMeshData& data) noexcept; + void ReadData(std::ifstream& file, SHMeshLoaderHeader const& header, SHMeshAsset& data) noexcept; public: void LoadSHMesh(AssetPath path, SHModelAsset& model) noexcept; diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index 1569a13c..2695b2fa 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -530,7 +530,7 @@ namespace SHADE { assetData.emplace( parent.subAssets[i]->id, - parentModel->subMeshes[i] + parentModel->meshes[i] ); } } @@ -588,7 +588,7 @@ namespace SHADE SHModelAsset* const data = reinterpret_cast(LoadData(newAsset)); assetData.emplace(newAsset.id, data); - for(auto const& subMesh : data->subMeshes) + for(auto const& subMesh : data->meshes) { SHAsset subAsset{ .name = subMesh->header.name, diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 60262607..392590ed 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -14,7 +14,7 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include // Project Includes -#include "Assets/Asset Types/SHModelAsset.h" +#include "Assets/Asset Types/Models/SHModelAsset.h" #include "../Meshes/SHPrimitiveGenerator.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "SHGraphicsSystem.h" @@ -321,7 +321,7 @@ namespace SHADE { spherePoints.clear(); // Generate - static const SHMeshData SPHERE = SHPrimitiveGenerator::Sphere(); + static const SHMeshAsset SPHERE = SHPrimitiveGenerator::Sphere(); for (const auto& idx : SPHERE.Indices) { spherePoints.emplace_back(SPHERE.VertexPositions[idx] * radius + pos); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp index 60decded..bf60c7df 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp @@ -21,15 +21,15 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Static Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHMeshData SHPrimitiveGenerator::cubeMesh; - SHMeshData SHPrimitiveGenerator::sphereMesh; + SHMeshAsset SHPrimitiveGenerator::cubeMesh; + SHMeshAsset SHPrimitiveGenerator::sphereMesh; /*-----------------------------------------------------------------------------------*/ /* Primitive Generation Functions */ /*-----------------------------------------------------------------------------------*/ - SHMeshData SHPrimitiveGenerator::Cube() noexcept + SHMeshAsset SHPrimitiveGenerator::Cube() noexcept { - SHMeshData mesh; + SHMeshAsset mesh; mesh.VertexPositions = { @@ -214,9 +214,9 @@ namespace SHADE return addMeshDataTo(cubeMesh, gfxSystem); } - SHADE::SHMeshData SHPrimitiveGenerator::Sphere() noexcept + SHADE::SHMeshAsset SHPrimitiveGenerator::Sphere() noexcept { - SHMeshData meshData; + SHMeshAsset meshData; const int LAT_SLICES = 12; const int LONG_SLICES = 12; @@ -284,7 +284,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ - SHADE::Handle SHPrimitiveGenerator::addMeshDataTo(const SHMeshData& meshData, SHMeshLibrary& meshLibrary) noexcept + SHADE::Handle SHPrimitiveGenerator::addMeshDataTo(const SHMeshAsset& meshData, SHMeshLibrary& meshLibrary) noexcept { return meshLibrary.AddMesh ( @@ -298,7 +298,7 @@ namespace SHADE ); } - SHADE::Handle SHPrimitiveGenerator::addMeshDataTo(const SHMeshData& meshData, SHGraphicsSystem& gfxSystem) noexcept + SHADE::Handle SHPrimitiveGenerator::addMeshDataTo(const SHMeshAsset& meshData, SHGraphicsSystem& gfxSystem) noexcept { return gfxSystem.AddMesh ( diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h index 7719e4c3..7008ebb1 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h @@ -13,7 +13,7 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "Math/SHMath.h" -#include "Assets/Asset Types/SHModelAsset.h" +#include "Assets/Asset Types/Models/SHModelAsset.h" #include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h" #include "SH_API.h" @@ -47,13 +47,13 @@ namespace SHADE /***********************************************************************************/ /*! \brief - Produces a cube and stores the data in a SHMeshData object. + Produces a cube and stores the data in a SHMeshAsset object. \return - SHMeshData object containing vertex data for the cube. + SHMeshAsset object containing vertex data for the cube. */ /***********************************************************************************/ - [[nodiscard]] static SHMeshData Cube() noexcept; + [[nodiscard]] static SHMeshAsset Cube() noexcept; /***********************************************************************************/ /*! \brief @@ -83,13 +83,13 @@ namespace SHADE /***********************************************************************************/ /*! \brief - Produces a sphere and stores the data in a SHMeshData object. + Produces a sphere and stores the data in a SHMeshAsset object. \return - SHMeshData object containing vertex data for the sphere. + SHMeshAsset object containing vertex data for the sphere. */ /***********************************************************************************/ - [[nodiscard]] static SHMeshData Sphere() noexcept; + [[nodiscard]] static SHMeshAsset Sphere() noexcept; /***********************************************************************************/ /*! \brief @@ -121,13 +121,13 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - static Handle addMeshDataTo(const SHMeshData& meshData, SHMeshLibrary& meshLibrary) noexcept; - static Handle addMeshDataTo(const SHMeshData& meshData, SHGraphicsSystem& gfxSystem) noexcept; + static Handle addMeshDataTo(const SHMeshAsset& meshData, SHMeshLibrary& meshLibrary) noexcept; + static Handle addMeshDataTo(const SHMeshAsset& meshData, SHGraphicsSystem& gfxSystem) noexcept; /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - static SHMeshData cubeMesh; - static SHMeshData sphereMesh; + static SHMeshAsset cubeMesh; + static SHMeshAsset sphereMesh; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index dae20f99..e008afad 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -17,7 +17,7 @@ of DigiPen Institute of Technology is prohibited. #include "SH_API.h" #include "SHResourceLibrary.h" #include "Assets/SHAssetMacros.h" -#include "Assets/Asset Types/SHModelAsset.h" +#include "Assets/Asset Types/Models/SHModelAsset.h" #include "Assets/Asset Types/SHTextureAsset.h" #include "Assets/Asset Types/SHShaderAsset.h" #include "Graphics/Shaders/SHVkShaderModule.h" @@ -42,7 +42,7 @@ namespace SHADE /// template struct SHResourceLoader { using AssetType = void; }; - template<> struct SHResourceLoader { using AssetType = SHMeshData; }; + template<> struct SHResourceLoader { using AssetType = SHMeshAsset; }; template<> struct SHResourceLoader { using AssetType = SHTextureAsset; }; template<> struct SHResourceLoader { using AssetType = SHShaderAsset; }; template<> struct SHResourceLoader { using AssetType = SHMaterialAsset; }; From 6fa14aff8597debee5020795b5f18d5d7d90abc8 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 19 Nov 2022 21:27:30 +0800 Subject: [PATCH 002/164] Fixed sizeof bug when reading old model header --- SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 1f825a97..cd81a30a 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -58,9 +58,10 @@ namespace SHADE file.seekg(0); + //TODO Update to new mesh header with anim count when animation saving is done file.read( - reinterpret_cast(&model.header), - sizeof(SHModelAssetHeader) + reinterpret_cast(&model.header.meshCount), + sizeof(model.header.meshCount) ); std::vector headers(model.header.meshCount); From 841948b82cd56821036f2ccd115bc412e48d8d59 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 20 Nov 2022 01:33:55 +0800 Subject: [PATCH 003/164] Added support for Bone Weights and Bone Indices vertex attributes for meshes --- Assets/Shaders/DefaultSkinned_VS.glsl | 65 +++ .../Assets/Asset Types/Models/SHMeshAsset.h | 14 +- .../MiddleEnd/Interface/SHGraphicsConstants.h | 14 + .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 11 +- .../MiddleEnd/Interface/SHGraphicsSystem.h | 2 +- .../MiddleEnd/Interface/SHMeshLibrary.cpp | 400 ++++++++++-------- .../MiddleEnd/Interface/SHMeshLibrary.h | 334 ++++++++------- .../MiddleEnd/Meshes/SHPrimitiveGenerator.cpp | 8 +- 8 files changed, 505 insertions(+), 343 deletions(-) create mode 100644 Assets/Shaders/DefaultSkinned_VS.glsl diff --git a/Assets/Shaders/DefaultSkinned_VS.glsl b/Assets/Shaders/DefaultSkinned_VS.glsl new file mode 100644 index 00000000..22199ff5 --- /dev/null +++ b/Assets/Shaders/DefaultSkinned_VS.glsl @@ -0,0 +1,65 @@ +#version 450 +#extension GL_KHR_vulkan_glsl : enable + +//#include "ShaderDescriptorDefinitions.glsl" + + +layout(location = 0) in vec3 aVertexPos; +layout(location = 1) in vec2 aUV; +layout(location = 2) in vec3 aNormal; +layout(location = 3) in vec3 aTangent; +layout(location = 4) in mat4 worldTransform; +layout(location = 5) in ivec4 aBoneIndices; +layout(location = 6) in vec4 aBoneWeights; +layout(location = 8) in uvec2 integerData; + + +layout(location = 0) out struct +{ + vec4 vertPos; // location 0 + vec2 uv; // location = 1 + vec4 normal; // location = 2 + +} Out; + +// material stuff +layout(location = 3) out struct +{ + int materialIndex; + uint eid; + uint lightLayerIndex; + +} Out2; + +layout(set = 2, binding = 0) uniform CameraData +{ + vec4 position; + mat4 vpMat; + mat4 viewMat; + mat4 projMat; +} cameraData; + +void main() +{ + Out2.materialIndex = gl_InstanceIndex; + Out2.eid = integerData[0]; + Out2.lightLayerIndex = integerData[1]; + + // for transforming gBuffer position and normal data + mat4 modelViewMat = cameraData.viewMat * worldTransform; + + // gBuffer position will be in view space + Out.vertPos = modelViewMat * vec4(aVertexPos, 1.0f); + + // uvs for texturing in fragment shader + Out.uv = aUV; + + mat3 transposeInv = mat3 (transpose(inverse(modelViewMat))); + + // normals are also in view space + Out.normal.rgb = transposeInv * aNormal.rgb; + Out.normal.rgb = normalize (Out.normal.rgb); + + // clip space for rendering + gl_Position = cameraData.vpMat * worldTransform * vec4 (aVertexPos, 1.0f); +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h index c10e0cc3..e8780669 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h @@ -48,13 +48,13 @@ namespace SHADE struct SH_API SHMeshAsset : SHAssetData { - SHMeshDataHeader header; - std::vector VertexPositions; - std::vector VertexTangents; - std::vector VertexNormals; - std::vector VertexTexCoords; + SHMeshDataHeader header; + std::vector VertexPositions; + std::vector VertexTangents; + std::vector VertexNormals; + std::vector VertexTexCoords; std::vector Indices; - std::vector BonesInfo; - std::vector Bones; + std::vector VertexBoneIndices; + std::vector VertexBoneWeights; }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index 0a67cd9f..b1da9242 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -176,6 +176,20 @@ namespace SHADE */ /***************************************************************************/ static constexpr uint32_t INTEGER_DATA = 5; + /***************************************************************************/ + /*! + \brief + Vertex buffer bindings for the bone indices buffer. + */ + /***************************************************************************/ + static constexpr uint32_t BONE_INDICES = 6; + /***************************************************************************/ + /*! + \brief + Vertex buffer bindings for the bone weights buffer. + */ + /***************************************************************************/ + static constexpr uint32_t BONE_WEIGHTS = 7; }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 5b40a745..8f6ad9c9 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -432,6 +432,8 @@ namespace SHADE std::make_pair(meshLibrary.GetVertexTexCoordsBuffer(), SHGraphicsConstants::VertexBufferBindings::TEX_COORD), std::make_pair(meshLibrary.GetVertexNormalsBuffer(), SHGraphicsConstants::VertexBufferBindings::NORMAL), std::make_pair(meshLibrary.GetVertexTangentsBuffer(), SHGraphicsConstants::VertexBufferBindings::TANGENT), + std::make_pair(meshLibrary.GetVertexBoneIndicesBuffer(), SHGraphicsConstants::VertexBufferBindings::BONE_INDICES), + std::make_pair(meshLibrary.GetVertexBoneWeightsBuffer(), SHGraphicsConstants::VertexBufferBindings::BONE_WEIGHTS), std::make_pair(meshLibrary.GetIndexBuffer(), 0), }; @@ -483,6 +485,11 @@ namespace SHADE // Bind all the buffers required for meshes for (auto& [buffer, bindingPoint] : MESH_DATA) { + // Ignore invalid buffers + if (!buffer) + continue; + + // Assign based on type if (buffer->GetUsageBits() & vk::BufferUsageFlagBits::eVertexBuffer) currentCmdBuffer->BindVertexBuffer(bindingPoint, buffer, 0); else if (buffer->GetUsageBits() & vk::BufferUsageFlagBits::eIndexBuffer) @@ -721,9 +728,9 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Mesh Registration Functions */ /*---------------------------------------------------------------------------------*/ - SHADE::Handle SHGraphicsSystem::AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices) + SHADE::Handle SHGraphicsSystem::AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices, const SHMesh::VertexBoneIndices* const boneIndices, const SHMesh::VertexWeights* const boneWeights) { - return meshLibrary.AddMesh(vertexCount, positions, texCoords, tangents, normals, indexCount, indices); + return meshLibrary.AddMesh(vertexCount, positions, texCoords, tangents, normals, boneIndices, boneWeights, indexCount, indices); } void SHGraphicsSystem::RemoveMesh(Handle mesh) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 2a186041..d7e43aea 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -204,7 +204,7 @@ namespace SHADE */ /*******************************************************************************/ - Handle AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices); + Handle AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices, const SHMesh::VertexBoneIndices* const boneIndices = nullptr, const SHMesh::VertexWeights* const boneWeights = nullptr); /*******************************************************************************/ /*! diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp index d34c1b7d..dda5d423 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp @@ -3,7 +3,7 @@ \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Aug 30, 2022 -\brief Contains definitions for all of the functions of the classes that deal +\brief Contains definitions for all of the functions of the classes that deal with storage and management of vertex and index buffers of meshes. Copyright (C) 2022 DigiPen Institute of Technology. @@ -20,187 +20,239 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { - SHADE::Handle SHMeshLibrary::AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices) - { - isDirty = true; + SHADE::Handle SHMeshLibrary::AddMesh + ( + uint32_t vertexCount, const SHMesh::VertexPosition* const positions, + const SHMesh::VertexTexCoord* const texCoords, + const SHMesh::VertexTangent* const tangents, + const SHMesh::VertexNormal* const normals, + const SHMesh::VertexBoneIndices* const boneIndices, + const SHMesh::VertexWeights* const boneWeights, + uint32_t indexCount, const SHMesh::Index* const indices) + { + isDirty = true; - auto handle = meshes.Create(); - meshAddJobs.emplace_back( MeshAddJob - { - vertexCount, - positions, - texCoords, - tangents, - normals, - indexCount, - indices, - handle - }); - return handle; - } - - void SHMeshLibrary::RemoveMesh(Handle mesh) + auto handle = meshes.Create(); + meshAddJobs.emplace_back(MeshAddJob { - if (!mesh) - throw std::invalid_argument("Attempted to remove a Mesh that did not belong to the Mesh Library!"); - - meshRemoveJobs.emplace_back(mesh); - isDirty = true; + vertexCount, + positions, + texCoords, + tangents, + normals, + boneIndices, + boneWeights, + indexCount, + indices, + handle + }); + return handle; + } + + void SHMeshLibrary::RemoveMesh(Handle mesh) + { + if (!mesh) + throw std::invalid_argument("Attempted to remove a Mesh that did not belong to the Mesh Library!"); + + meshRemoveJobs.emplace_back(mesh); + isDirty = true; + } + + void SHMeshLibrary::BuildBuffers(Handle device, Handle cmdBuffer) + { + // No changes + if (!isDirty) + return; + + // Remove + if (!meshRemoveJobs.empty()) + { + // - Remove from order list + for (const auto& meshToRemove : meshRemoveJobs) + { + auto searchResult = std::find(meshOrder.begin(), meshOrder.end(), meshToRemove); + // Shouldn't happen, ignore + if (searchResult == meshOrder.end()) + continue; + + // Remove from mesh list + meshOrder.erase(searchResult); + } + meshRemoveJobs.clear(); + // - Shift existing elements in to close up the gaps + int32_t nextVertInsertPoint = 0; + uint32_t nextIdxInsertPoint = 0; + for (auto& mesh : meshOrder) + { + // Check if already in the correct place + if (nextVertInsertPoint != mesh->FirstVertex) + { + /* There's a gap, we need to shift */ + // Vertices + vertPosStorage.erase(vertPosStorage.begin() + nextVertInsertPoint, vertPosStorage.begin() + mesh->FirstVertex); + vertTexCoordStorage.erase(vertTexCoordStorage.begin() + nextVertInsertPoint, vertTexCoordStorage.begin() + mesh->FirstVertex); + vertTangentStorage.erase(vertTangentStorage.begin() + nextVertInsertPoint, vertTangentStorage.begin() + mesh->FirstVertex); + vertNormalStorage.erase(vertNormalStorage.begin() + nextVertInsertPoint, vertNormalStorage.begin() + mesh->FirstVertex); + vertBoneIdxStorage.erase(vertBoneIdxStorage.begin() + nextVertInsertPoint, vertBoneIdxStorage.begin() + mesh->FirstVertex); + vertBoneWeightStorage.erase(vertBoneWeightStorage.begin() + nextVertInsertPoint, vertBoneWeightStorage.begin() + mesh->FirstVertex); + // - Update mesh data + mesh->FirstVertex = nextVertInsertPoint; + + // Indices + indexStorage.erase(indexStorage.begin() + nextIdxInsertPoint, indexStorage.begin() + mesh->FirstIndex); + // - Update mesh data + mesh->FirstIndex = nextIdxInsertPoint; + + // Prepare for next + nextVertInsertPoint += mesh->VertexCount; + nextIdxInsertPoint += mesh->IndexCount; + } + } } - void SHMeshLibrary::BuildBuffers(Handle device, Handle cmdBuffer) + // Add + if (!meshAddJobs.empty()) { - // No changes - if (!isDirty) - return; - - // Remove - if (!meshRemoveJobs.empty()) + // - Compute updated size + size_t newVertElems = vertPosStorage.size(); + size_t newIdxElems = indexStorage.size(); + for (const auto& addJob : meshAddJobs) + { + newVertElems += addJob.VertexCount; + newIdxElems += addJob.IndexCount; + } + // - Reserve new memory + vertPosStorage.reserve(newVertElems); + vertTexCoordStorage.reserve(newVertElems); + vertTangentStorage.reserve(newVertElems); + vertNormalStorage.reserve(newVertElems); + vertBoneIdxStorage.reserve(newVertElems); + vertBoneWeightStorage.reserve(newVertElems); + indexStorage.reserve(newIdxElems); + // - Append new data + for (auto& addJob : meshAddJobs) + { + // Update handle + SHMesh& meshData = *addJob.Handle; + meshData = SHMesh { - // - Remove from order list - for (const auto& meshToRemove : meshRemoveJobs) - { - auto searchResult = std::find(meshOrder.begin(), meshOrder.end(), meshToRemove); - // Shouldn't happen, ignore - if (searchResult == meshOrder.end()) - continue; + .FirstVertex = static_cast(vertPosStorage.size()), + .VertexCount = static_cast(addJob.VertexCount), + .FirstIndex = static_cast(indexStorage.size()), + .IndexCount = static_cast(addJob.IndexCount), + }; - // Remove from mesh list - meshOrder.erase(searchResult); - } - meshRemoveJobs.clear(); - // - Shift existing elements in to close up the gaps - int32_t nextVertInsertPoint = 0; - uint32_t nextIdxInsertPoint = 0; - for (auto& mesh : meshOrder) - { - // Check if already in the correct place - if (nextVertInsertPoint != mesh->FirstVertex) - { - /* There's a gap, we need to shift */ - // Vertices - vertPosStorage.erase(vertPosStorage.begin() + nextVertInsertPoint, vertPosStorage.begin() + mesh->FirstVertex); - vertTexCoordStorage.erase(vertTexCoordStorage.begin() + nextVertInsertPoint, vertTexCoordStorage.begin() + mesh->FirstVertex); - vertTangentStorage.erase(vertTangentStorage.begin() + nextVertInsertPoint, vertTangentStorage.begin() + mesh->FirstVertex); - vertNormalStorage.erase(vertNormalStorage.begin() + nextVertInsertPoint, vertNormalStorage.begin() + mesh->FirstVertex); - // - Update mesh data - mesh->FirstVertex = nextVertInsertPoint; - - // Indices - indexStorage.erase(indexStorage.begin() + nextIdxInsertPoint, indexStorage.begin() + mesh->FirstIndex); - // - Update mesh data - mesh->FirstIndex = nextIdxInsertPoint; - - // Prepare for next - nextVertInsertPoint += mesh->VertexCount; - nextIdxInsertPoint += mesh->IndexCount; - } - } - } - - // Add - if (!meshAddJobs.empty()) + // Copy into storage + vertPosStorage.insert + ( + vertPosStorage.end(), + addJob.VertexPositions, addJob.VertexPositions + addJob.VertexCount + ); + vertTexCoordStorage.insert + ( + vertTexCoordStorage.end(), + addJob.VertexTexCoords, addJob.VertexTexCoords + addJob.VertexCount + ); + vertTangentStorage.insert + ( + vertTangentStorage.end(), + addJob.VertexTangents, addJob.VertexTangents + addJob.VertexCount + ); + vertNormalStorage.insert + ( + vertNormalStorage.end(), + addJob.VertexNormals, addJob.VertexNormals + addJob.VertexCount + ); + if (addJob.VertexBoneIndices) { - // - Compute updated size - size_t newVertElems = vertPosStorage.size(); - size_t newIdxElems = indexStorage.size(); - for (const auto& addJob : meshAddJobs) - { - newVertElems += addJob.VertexCount; - newIdxElems += addJob.IndexCount; - } - // - Reserve new memory - vertPosStorage .reserve(newVertElems); - vertTexCoordStorage.reserve(newVertElems); - vertTangentStorage .reserve(newVertElems); - vertNormalStorage .reserve(newVertElems); - indexStorage .reserve(newIdxElems); - // - Append new data - for (auto& addJob : meshAddJobs) - { - // Update handle - SHMesh& meshData = *addJob.Handle; - meshData = SHMesh - { - .FirstVertex = static_cast(vertPosStorage.size()), - .VertexCount = static_cast(addJob.VertexCount), - .FirstIndex = static_cast(indexStorage.size()), - .IndexCount = static_cast(addJob.IndexCount), - }; - - // Copy into storage - vertPosStorage.insert - ( - vertPosStorage.end(), - addJob.VertexPositions, addJob.VertexPositions + addJob.VertexCount - ); - vertTexCoordStorage.insert - ( - vertTexCoordStorage.end(), - addJob.VertexTexCoords, addJob.VertexTexCoords + addJob.VertexCount - ); - vertTangentStorage.insert - ( - vertTangentStorage.end(), - addJob.VertexTangents, addJob.VertexTangents + addJob.VertexCount - ); - vertNormalStorage.insert - ( - vertNormalStorage.end(), - addJob.VertexNormals, addJob.VertexNormals + addJob.VertexCount - ); - indexStorage.insert - ( - indexStorage.end(), - addJob.Indices, addJob.Indices + addJob.IndexCount - ); - } - meshAddJobs.clear(); + vertBoneIdxStorage.insert + ( + vertBoneIdxStorage.end(), + addJob.VertexBoneIndices, addJob.VertexBoneIndices + addJob.VertexCount * SHMesh::BONE_INDICES_PER_VERTEX + ); } - - // Send to GPU - using BuffUsage = vk::BufferUsageFlagBits; - SHVkUtil::EnsureBufferAndCopyData - ( - device, cmdBuffer, vertPosBuffer, - vertPosStorage.data(), - static_cast(vertPosStorage.size()) * sizeof(SHMesh::VertexPosition), - BuffUsage::eVertexBuffer, - "Mesh Library Vertex Positions" - ); - SHVkUtil::EnsureBufferAndCopyData - ( - device, cmdBuffer, vertTexCoordBuffer, - vertTexCoordStorage.data(), - static_cast(vertTexCoordStorage.size()) * sizeof(SHMesh::VertexTexCoord), - BuffUsage::eVertexBuffer, - "Mesh Library Vertex TexCoords" - ); - SHVkUtil::EnsureBufferAndCopyData - ( - device, cmdBuffer, vertTangentBuffer, - vertTangentStorage.data(), - static_cast(vertTangentStorage.size()) * sizeof(SHMesh::VertexTangent), - BuffUsage::eVertexBuffer, - "Mesh Library Vertex Tangents" - ); - SHVkUtil::EnsureBufferAndCopyData - ( - device, cmdBuffer, vertNormalBuffer, - vertNormalStorage.data(), - static_cast(vertNormalStorage.size()) * sizeof(SHMesh::VertexNormal), - BuffUsage::eVertexBuffer, - "Mesh Library Vertex Normals" - ); - SHVkUtil::EnsureBufferAndCopyData - ( - device, cmdBuffer, indexBuffer, - indexStorage.data(), - static_cast(indexStorage.size()) * sizeof(SHMesh::Index), - BuffUsage::eIndexBuffer, - "Mesh Library Indices" - ); - - isDirty = false; + if (addJob.VertexBoneWeights) + { + vertBoneWeightStorage.insert + ( + vertBoneWeightStorage.end(), + addJob.VertexBoneWeights, addJob.VertexBoneWeights + addJob.VertexCount + ); + } + indexStorage.insert + ( + indexStorage.end(), + addJob.Indices, addJob.Indices + addJob.IndexCount + ); + } + meshAddJobs.clear(); } + + // Send to GPU + using BuffUsage = vk::BufferUsageFlagBits; + SHVkUtil::EnsureBufferAndCopyData + ( + device, cmdBuffer, vertPosBuffer, + vertPosStorage.data(), + static_cast(vertPosStorage.size()) * sizeof(SHMesh::VertexPosition), + BuffUsage::eVertexBuffer, + "Mesh Library Vertex Positions" + ); + SHVkUtil::EnsureBufferAndCopyData + ( + device, cmdBuffer, vertTexCoordBuffer, + vertTexCoordStorage.data(), + static_cast(vertTexCoordStorage.size()) * sizeof(SHMesh::VertexTexCoord), + BuffUsage::eVertexBuffer, + "Mesh Library Vertex TexCoords" + ); + SHVkUtil::EnsureBufferAndCopyData + ( + device, cmdBuffer, vertTangentBuffer, + vertTangentStorage.data(), + static_cast(vertTangentStorage.size()) * sizeof(SHMesh::VertexTangent), + BuffUsage::eVertexBuffer, + "Mesh Library Vertex Tangents" + ); + SHVkUtil::EnsureBufferAndCopyData + ( + device, cmdBuffer, vertNormalBuffer, + vertNormalStorage.data(), + static_cast(vertNormalStorage.size()) * sizeof(SHMesh::VertexNormal), + BuffUsage::eVertexBuffer, + "Mesh Library Vertex Normals" + ); + if (!vertBoneIdxStorage.empty()) + { + SHVkUtil::EnsureBufferAndCopyData + ( + device, cmdBuffer, vertBoneIdxBuffer, + vertBoneIdxStorage.data(), + static_cast(vertBoneIdxStorage.size()) * sizeof(SHMesh::VertexBoneIndices), + BuffUsage::eVertexBuffer, + "Mesh Library Vertex Bone Indices" + ); + } + if (!vertBoneWeightStorage.empty()) + { + SHVkUtil::EnsureBufferAndCopyData + ( + device, cmdBuffer, vertBoneWeightBuffer, + vertBoneWeightStorage.data(), + static_cast(vertBoneWeightStorage.size()) * sizeof(SHMesh::VertexWeights), + BuffUsage::eVertexBuffer, + "Mesh Library Vertex Bone Weights" + ); + } + SHVkUtil::EnsureBufferAndCopyData + ( + device, cmdBuffer, indexBuffer, + indexStorage.data(), + static_cast(indexStorage.size()) * sizeof(SHMesh::Index), + BuffUsage::eIndexBuffer, + "Mesh Library Indices" + ); + + isDirty = false; + } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h index b0cbdce1..f61e4a20 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h @@ -19,170 +19,192 @@ of DigiPen Institute of Technology is prohibited. #include "Resource/SHResourceLibrary.h" #include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec3.h" +#include "Math/Vector/SHVec4.h" namespace SHADE { - /*---------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*---------------------------------------------------------------------------------*/ - class SHVkBuffer; - class SHVkLogicalDevice; - class SHVkCommandBuffer; + /*---------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*---------------------------------------------------------------------------------*/ + class SHVkBuffer; + class SHVkLogicalDevice; + class SHVkCommandBuffer; - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - /***********************************************************************************/ + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + /***********************************************************************************/ + /*! + \brief + Represents a single mesh that is stored in a SHMeshLibrary. + */ + /***********************************************************************************/ + class SHMesh + { + public: + /*-----------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------*/ + using Index = uint32_t; + using VertexPosition = SHVec3; + using VertexTexCoord = SHVec2; + using VertexTangent = SHVec3; + using VertexNormal = SHVec3; + using VertexBoneIndices = int; + using VertexWeights = SHVec4; + + /*-----------------------------------------------------------------------------*/ + /* Constants */ + /*-----------------------------------------------------------------------------*/ + static constexpr size_t BONE_INDICES_PER_VERTEX = 4; + + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + int32_t FirstVertex; + uint32_t VertexCount; + uint32_t FirstIndex; + uint32_t IndexCount; + }; + /***********************************************************************************/ + /*! + \brief + Manages storage for all Meshes in the Graphics System as a single set of Vertex + and Index Buffers. + */ + /***********************************************************************************/ + class SHMeshLibrary + { + public: + /*-----------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------*/ + /*******************************************************************************/ /*! - \brief - Represents a single mesh that is stored in a SHMeshLibrary. - */ - /***********************************************************************************/ - class SHMesh - { - public: - /*-----------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------*/ - using Index = uint32_t; - using VertexPosition = SHVec3; - using VertexTexCoord = SHVec2; - using VertexTangent = SHVec3; - using VertexNormal = SHVec3; - /*-----------------------------------------------------------------------------*/ - /* Data Members */ - /*-----------------------------------------------------------------------------*/ - int32_t FirstVertex; - uint32_t VertexCount; - uint32_t FirstIndex; - uint32_t IndexCount; - }; - /***********************************************************************************/ + \brief + Adds a mesh to the Mesh Library. But this does not mean that the meshes have + been added yet. A call to "BuildBuffers()" is required to transfer all + meshes into the GPU. + + \param vertexCount + Number of vertices in this Mesh. + \param positions + Pointer to the first in a contiguous array of SHMathVec3s that define vertex + positions. + \param texCoords + Pointer to the first in a contiguous array of SHMathVec2s that define vertex + texture coordinates. + \param tangents + Pointer to the first in a contiguous array of SHMathVec3s that define vertex + tangents. + \param normals + Pointer to the first in a contiguous array of SHMathVec3s that define vertex + normals. + \param indexCount + Number of indices in this mesh. + \param indices + Pointer to the first in a contiguous array of uint32_ts that define mesh + indicies. + + \return + Handle to the created Mesh. This is not valid to be used until a call to + BuildBuffers(). + + */ + /*******************************************************************************/ + Handle AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, + const SHMesh::VertexTexCoord* const texCoords, + const SHMesh::VertexTangent* const tangents, + const SHMesh::VertexNormal* const normals, + const SHMesh::VertexBoneIndices* const boneIndices, + const SHMesh::VertexWeights* const boneWeights, + uint32_t indexCount, const SHMesh::Index* const indices); + /*******************************************************************************/ /*! - \brief - Manages storage for all Meshes in the Graphics System as a single set of Vertex - and Index Buffers. + + \brief + Removes a mesh from the MeshLibrary. But this does not mean that the meshes + have been removed yet. A call to "BuildBuffers()" is required to finalise all + changes. + + \param mesh + Handle to the mesh to remove. + */ - /***********************************************************************************/ - class SHMeshLibrary + /*******************************************************************************/ + void RemoveMesh(Handle mesh); + /***************************************************************************/ + /*! + + \brief + Finalises all changes to the MeshLibrary into the GPU buffers. + + \param device + Device used to create and update the buffers. + \param cmdBuffer + Command buffer used to set up transfers of data in the GPU memory. This + call must be preceded by calls to cmdBuffer's BeginRecording() and ended + with EndRecording(). Do recall to also submit the cmdBuffer to a transfer + queue. + */ + /***************************************************************************/ + void BuildBuffers(Handle device, Handle cmdBuffer); + + /*-----------------------------------------------------------------------------*/ + /* Getter Functions */ + /*-----------------------------------------------------------------------------*/ + Handle GetVertexPositionsBuffer() const noexcept { return vertPosBuffer; } + Handle GetVertexTexCoordsBuffer() const noexcept { return vertTexCoordBuffer; } + Handle GetVertexTangentsBuffer() const noexcept { return vertTangentBuffer; } + Handle GetVertexNormalsBuffer() const noexcept { return vertNormalBuffer; } + Handle GetVertexBoneIndicesBuffer() const noexcept { return vertBoneIdxBuffer; } + Handle GetVertexBoneWeightsBuffer() const noexcept { return vertBoneWeightBuffer; } + Handle GetIndexBuffer() const noexcept { return indexBuffer; } + + private: + /*-----------------------------------------------------------------------------*/ + /* Type Definition */ + /*-----------------------------------------------------------------------------*/ + struct MeshAddJob { - public: - /*-----------------------------------------------------------------------------*/ - /* Usage Functions */ - /*-----------------------------------------------------------------------------*/ - /*******************************************************************************/ - /*! - - \brief - Adds a mesh to the Mesh Library. But this does not mean that the meshes have - been added yet. A call to "BuildBuffers()" is required to transfer all - meshes into the GPU. - - \param vertexCount - Number of vertices in this Mesh. - \param positions - Pointer to the first in a contiguous array of SHMathVec3s that define vertex - positions. - \param texCoords - Pointer to the first in a contiguous array of SHMathVec2s that define vertex - texture coordinates. - \param tangents - Pointer to the first in a contiguous array of SHMathVec3s that define vertex - tangents. - \param normals - Pointer to the first in a contiguous array of SHMathVec3s that define vertex - normals. - \param indexCount - Number of indices in this mesh. - \param indices - Pointer to the first in a contiguous array of uint32_ts that define mesh - indicies. - - \return - Handle to the created Mesh. This is not valid to be used until a call to - BuildBuffers(). - - */ - /*******************************************************************************/ - Handle AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices); - /*******************************************************************************/ - /*! - - \brief - Removes a mesh from the MeshLibrary. But this does not mean that the meshes - have been removed yet. A call to "BuildBuffers()" is required to finalise all - changes. - - \param mesh - Handle to the mesh to remove. - - */ - /*******************************************************************************/ - void RemoveMesh(Handle mesh); - /***************************************************************************/ - /*! - - \brief - Finalises all changes to the MeshLibrary into the GPU buffers. - - \param device - Device used to create and update the buffers. - \param cmdBuffer - Command buffer used to set up transfers of data in the GPU memory. This - call must be preceded by calls to cmdBuffer's BeginRecording() and ended - with EndRecording(). Do recall to also submit the cmdBuffer to a transfer - queue. - */ - /***************************************************************************/ - void BuildBuffers(Handle device, Handle cmdBuffer); - - /*-----------------------------------------------------------------------------*/ - /* Getter Functions */ - /*-----------------------------------------------------------------------------*/ - Handle GetVertexPositionsBuffer() const noexcept { return vertPosBuffer; } - Handle GetVertexTexCoordsBuffer() const noexcept { return vertTexCoordBuffer; } - Handle GetVertexTangentsBuffer() const noexcept { return vertTangentBuffer; } - Handle GetVertexNormalsBuffer() const noexcept { return vertNormalBuffer; } - Handle GetIndexBuffer() const { return indexBuffer; } - - private: - /*-----------------------------------------------------------------------------*/ - /* Type Definition */ - /*-----------------------------------------------------------------------------*/ - struct MeshAddJob - { - uint32_t VertexCount = 0; - const SHMesh::VertexPosition* VertexPositions = nullptr; - const SHMesh::VertexTexCoord* VertexTexCoords = nullptr; - const SHMesh::VertexTangent * VertexTangents = nullptr; - const SHMesh::VertexNormal * VertexNormals = nullptr; - uint32_t IndexCount = 0; - const SHMesh::Index * Indices = nullptr; - Handle Handle; - }; - /*-----------------------------------------------------------------------------*/ - /* Data Members */ - /*-----------------------------------------------------------------------------*/ - // Manipulation Queues - std::vector meshAddJobs; - std::vector> meshRemoveJobs; - // Tracking - SHResourceLibrary meshes{}; - std::vector> meshOrder; - // CPU Storage - std::vector vertPosStorage; - std::vector vertTexCoordStorage; - std::vector vertTangentStorage; - std::vector vertNormalStorage; - std::vector indexStorage; - // GPU Storage - Handle vertPosBuffer{}; - Handle vertTexCoordBuffer{}; - Handle vertTangentBuffer{}; - Handle vertNormalBuffer{}; - Handle indexBuffer {}; - // Flags - bool isDirty = true; + uint32_t VertexCount = 0; + const SHMesh::VertexPosition* VertexPositions = nullptr; + const SHMesh::VertexTexCoord* VertexTexCoords = nullptr; + const SHMesh::VertexTangent* VertexTangents = nullptr; + const SHMesh::VertexNormal* VertexNormals = nullptr; + const SHMesh::VertexBoneIndices* VertexBoneIndices = nullptr; + const SHMesh::VertexWeights* VertexBoneWeights = nullptr; + uint32_t IndexCount = 0; + const SHMesh::Index* Indices = nullptr; + Handle Handle; }; + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + // Manipulation Queues + std::vector meshAddJobs; + std::vector> meshRemoveJobs; + // Tracking + SHResourceLibrary meshes; + std::vector> meshOrder; + // CPU Storage + std::vector vertPosStorage; + std::vector vertTexCoordStorage; + std::vector vertTangentStorage; + std::vector vertNormalStorage; + std::vector vertBoneIdxStorage; // Must be in multiples of 4 + std::vector vertBoneWeightStorage; + std::vector indexStorage; + // GPU Storage + Handle vertPosBuffer{}; + Handle vertTexCoordBuffer{}; + Handle vertTangentBuffer{}; + Handle vertNormalBuffer{}; + Handle vertBoneIdxBuffer{}; + Handle vertBoneWeightBuffer{}; + Handle indexBuffer{}; + // Flags + bool isDirty = true; + }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp index bf60c7df..c6200929 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp @@ -288,11 +288,13 @@ namespace SHADE { return meshLibrary.AddMesh ( - static_cast(meshData.VertexPositions.size()), + static_cast(meshData.VertexPositions.size()), meshData.VertexPositions.data(), meshData.VertexTexCoords.data(), meshData.VertexTangents.data(), meshData.VertexNormals.data(), + nullptr, + nullptr, static_cast(meshData.Indices.size()), meshData.Indices.data() ); @@ -302,12 +304,12 @@ namespace SHADE { return gfxSystem.AddMesh ( - static_cast(meshData.VertexPositions.size()), + static_cast(meshData.VertexPositions.size()), meshData.VertexPositions.data(), meshData.VertexTexCoords.data(), meshData.VertexTangents.data(), meshData.VertexNormals.data(), - static_cast(meshData.Indices.size()), + static_cast(meshData.Indices.size()), meshData.Indices.data() ); } From 2a6db58cd992f013d0f8ac014a157e9455a994fe Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 21 Nov 2022 00:22:46 +0800 Subject: [PATCH 004/164] Added SHRig and stubs for SHAnimatorComponent and SHAnimationSystem --- .../src/Animation/SHAnimationSystem.cpp | 20 ++++ .../src/Animation/SHAnimationSystem.h | 32 +++++++ .../src/Animation/SHAnimatorComponent.cpp | 28 ++++++ .../src/Animation/SHAnimatorComponent.h | 36 ++++++++ SHADE_Engine/src/Animation/SHRig.cpp | 89 ++++++++++++++++++ SHADE_Engine/src/Animation/SHRig.h | 91 +++++++++++++++++++ 6 files changed, 296 insertions(+) create mode 100644 SHADE_Engine/src/Animation/SHAnimationSystem.cpp create mode 100644 SHADE_Engine/src/Animation/SHAnimationSystem.h create mode 100644 SHADE_Engine/src/Animation/SHAnimatorComponent.cpp create mode 100644 SHADE_Engine/src/Animation/SHAnimatorComponent.h create mode 100644 SHADE_Engine/src/Animation/SHRig.cpp create mode 100644 SHADE_Engine/src/Animation/SHRig.h diff --git a/SHADE_Engine/src/Animation/SHAnimationSystem.cpp b/SHADE_Engine/src/Animation/SHAnimationSystem.cpp new file mode 100644 index 00000000..28b1a80e --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimationSystem.cpp @@ -0,0 +1,20 @@ +/************************************************************************************//*! +\file SHAnimationSystem.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 20, 2022 +\brief Contains the function definitions of the SHAnimationSystem class. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Pre-compiled Header +#include "SHpch.h" +// Primary Include +#include "SHAnimationSystem.h" + +namespace SHADE +{ + +} diff --git a/SHADE_Engine/src/Animation/SHAnimationSystem.h b/SHADE_Engine/src/Animation/SHAnimationSystem.h new file mode 100644 index 00000000..eac839ad --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimationSystem.h @@ -0,0 +1,32 @@ +/************************************************************************************//*! +\file SHAnimationSystem.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 20, 2022 +\brief Contains the definition of the SHAnimationSystem class and related types. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// Project Includes +#include "SH_API.h" +#include "ECS_Base/System/SHSystem.h" +#include "ECS_Base/System/SHSystemRoutine.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + class SH_API SHAnimationSystem : public SHSystem + { + + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp new file mode 100644 index 00000000..271129f1 --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -0,0 +1,28 @@ +/************************************************************************************//*! +\file SHAnimatorComponent.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 20, 2022 +\brief Contains the definition of functions of the SHRenderable Component class. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Pre-compiled Header +#include "SHpch.h" +// Primary Include +#include "SHAnimatorComponent.h" + +namespace SHADE +{ + +} + +RTTR_REGISTRATION +{ + using namespace SHADE; + using namespace rttr; + + registration::class_("Animator Component"); +} \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h new file mode 100644 index 00000000..86744aec --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -0,0 +1,36 @@ +/************************************************************************************//*! +\file SHAnimatorComponent.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 20, 2022 +\brief Contains the definition of the SHAnimationSystem class and related types. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// External Dependencies +#include +// Project Includes +#include "ECS_Base/Components/SHComponent.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + class SH_API SHAnimatorComponent final : public SHComponent + { + private: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + RTTR_ENABLE() + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp new file mode 100644 index 00000000..0ef2a0e6 --- /dev/null +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -0,0 +1,89 @@ +/************************************************************************************//*! +\file SHRig.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 20, 2022 +\brief Contains the function definitions of the SHRig class. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Pre-compiled Header +#include "SHpch.h" +// Primary Header +#include "SHRig.h" +// STL Includes +#include +// Project Headers +#include "Assets/Asset Types/Models/SHRigAsset.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------------*/ + SHRig::SHRig(const SHRigAsset& asset) + { + // Don't bother if empty + if (asset.root == nullptr) + return; + + // Do a recursive depth first traversal to populate the rig + rootNode = recurseCreateNode(asset, asset.root); + } + + /*-----------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------------*/ + const std::string& SHRig::GetName(Handle node) const noexcept + { + static const std::string EMPTY_STRING = ""; + + if (nodeNames.contains(node)) + return nodeNames.at(node); + + return EMPTY_STRING; + } + + Handle SHRig::GetNode(const std::string& name) const noexcept + { + if (nodesByName.contains(name)) + return nodesByName.at(name); + + return {}; + } + + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------------*/ + Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const RigNode* sourceNode) + { + // Construct the node + auto newNode = nodeStore.Create(); + + // Fill the node with data + const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef); + newNode->Transform = NODE_DATA.transform; + + // Populate maps + if (!NODE_DATA.name.empty()) + { + nodeNames.emplace(newNode, NODE_DATA.name); + nodesByName.emplace(NODE_DATA.name, newNode); + } + + // Fill child nodes + for (const auto& child : sourceNode->children) + { + // Ignore nulls + if (child == nullptr) + continue; + + // Recursively create children + newNode->Children.emplace_back(recurseCreateNode(asset, child)); + } + + return newNode; + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h new file mode 100644 index 00000000..39dbe1c4 --- /dev/null +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -0,0 +1,91 @@ +/************************************************************************************//*! +\file SHRig.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 20, 2022 +\brief Contains the definition of the SHRig struct and related types. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// STL Includes +#include +#include +#include + +// Project Includes +#include "Math/SHMatrix.h" +#include "Resource/SHHandle.h" +#include "Resource/SHResourceLibrary.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + struct SHRigAsset; + struct RigNode; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + class SHRig + { + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + struct Node + { + SHMatrix Transform; + std::vector> Children; + }; + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + /// + /// + /// + /// + explicit SHRig(const SHRigAsset& asset); + + /*---------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Retrieves the name of a node. + /// + /// Node to get the name of. + /// + /// Name of the node. If it does not have a name or is invalid, an empty string will + /// be provided. + /// + const std::string& GetName(Handle node) const noexcept; + /// + /// Retrieves a node via name. + /// + /// Name of the node to retrieve. + /// + /// Node with the specified name. If it does not have a name or is invalid, an empty + /// handle will be provided. + /// + Handle GetNode(const std::string& name) const noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + Handle rootNode; + std::unordered_map, std::string> nodeNames; + std::unordered_map> nodesByName; + SHResourceLibrary nodeStore; + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + Handle recurseCreateNode(const SHRigAsset& asset, const RigNode* sourceNode); + }; +} \ No newline at end of file From 611744f5d4d9c284548fd4bb3a4a29bf6fe7db37 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 21 Nov 2022 15:09:15 +0800 Subject: [PATCH 005/164] Fleshed out SHAnimationComponent more and added preliminary implementation of SHBatch for bone data --- .../src/Animation/SHAnimatorComponent.cpp | 3 + .../src/Animation/SHAnimatorComponent.h | 28 ++- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 49 ++++- .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 206 +++++++++--------- 4 files changed, 177 insertions(+), 109 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 271129f1..3b4524e1 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -13,6 +13,9 @@ of DigiPen Institute of Technology is prohibited. #include "SHpch.h" // Primary Include #include "SHAnimatorComponent.h" +// Project Includes +#include "SHRig.h" +#include "Math/SHMatrix.h" namespace SHADE { diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 86744aec..be1bc742 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -11,26 +11,48 @@ of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #pragma once +// STL Includes +#include // External Dependencies #include // Project Includes #include "ECS_Base/Components/SHComponent.h" +#include "Resource/SHHandle.h" namespace SHADE { /*-----------------------------------------------------------------------------------*/ /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ + class SHRig; + struct SHMatrix; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /// + /// Component that holds and controls the animation related properties of a skinned + /// mesh. + /// class SH_API SHAnimatorComponent final : public SHComponent { + public: + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + const std::vector& GetBoneMatrices() const noexcept { return boneMatrices; } + private: - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + Handle rig; + std::vector boneMatrices; + float currPlaybackTime = 0.0f; + + /*---------------------------------------------------------------------------------*/ + /* RTTR */ + /*---------------------------------------------------------------------------------*/ RTTR_ENABLE() }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 9b4b02b0..6b7b9111 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -29,12 +29,13 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/Descriptors/SHVkDescriptorPool.h" #include "Scene/SHSceneManager.h" #include "UI/SHUIComponent.h" +#include "Animation/SHAnimatorComponent.h" namespace SHADE { - /*---------------------------------------------------------------------------------*/ - /* SHBatch - Usage Functions */ - /*---------------------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------------------*/ + /* SHBatch - Constructors/Destructors */ + /*-----------------------------------------------------------------------------------*/ SHBatch::SHBatch(Handle pipeline) : pipeline{ pipeline } { @@ -124,6 +125,9 @@ namespace SHADE } } + /*-----------------------------------------------------------------------------------*/ + /* SHBatch - Usage Functions */ + /*-----------------------------------------------------------------------------------*/ void SHBatch::Add(const SHRenderable* renderable) { // Ignore if null @@ -407,8 +411,6 @@ namespace SHADE // - EID data instancedIntegerData.reserve(numTotalElements); instancedIntegerData.clear(); - - // - Material Properties Data const Handle SHADER_INFO = pipeline->GetPipelineLayout()->GetShaderBlockInterface ( @@ -429,6 +431,10 @@ namespace SHADE matPropsDataSize = matPropTotalBytes; } } + // - Bone Data + boneMatrixData.clear(); + boneMatrixIndices.clear(); + boneMatrixIndices.reserve(numTotalElements); // Build Sub Batches uint32_t nextInstanceIndex = 0; @@ -499,6 +505,16 @@ namespace SHADE //propsCurrPtr += singleMatPropAlignedSize; propsCurrPtr += singleMatPropSize; } + + // Bone Data + auto animator = SHComponentManager::GetComponent_s(rendId); + if (animator) + { + boneMatrixIndices.emplace_back(static_cast(boneMatrixData.size())); + const auto& BONE_MATRICES = animator->GetBoneMatrices(); + boneMatrixData.insert(boneMatrixData.end(), BONE_MATRICES.cbegin(), BONE_MATRICES.cend()); + } + } } @@ -533,6 +549,24 @@ namespace SHADE ); // - Material Properties Buffer rebuildMaterialBuffers(frameIndex, descPool); + // - Bone Buffers + if (!boneMatrixIndices.empty()) + { + const uint32_t BONE_IDX_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneFirstIndexBuffer[frameIndex], boneMatrixIndices.data(), BONE_IDX_DATA_BYTES, + BuffUsage::eVertexBuffer, + "Batch Bone Index Buffer" + ); + const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(uint32_t)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneMatrixBuffer[frameIndex], boneMatrixData.data(), BONE_MTX_DATA_BYTES, + BuffUsage::eStorageBuffer, + "Batch Bone Matrix Buffer" + ); + } // Mark this frame as no longer dirty isDirty[frameIndex] = false; @@ -569,6 +603,11 @@ namespace SHADE dynamicOffset ); } + if (boneMatrixBuffer[frameIndex] && boneFirstIndexBuffer[frameIndex]) + { + cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::BONE_INDICES, boneFirstIndexBuffer[frameIndex], 0); + // TODO: Bind storage buffer + } cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); cmdBuffer->EndLabeledSegment(); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index dd4d33fd..08f07ea6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -27,114 +27,118 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + class SHVkBuffer; + class SHVkCommandBuffer; + class SHVkPipeline; + class SHMesh; + class SHRenderable; + class SHVkLogicalDevice; + class SHMaterialInstance; + class SHVkDescriptorSetGroup; + class SHVkDescriptorPool; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + /*************************************************************************************/ + /*! + \brief + Describes a segment of the sub batch operation. + */ + /*************************************************************************************/ + struct SHSubBatch + { + public: /*---------------------------------------------------------------------------------*/ - /* Forward Declarations */ + /* Data Members */ /*---------------------------------------------------------------------------------*/ - class SHVkBuffer; - class SHVkCommandBuffer; - class SHVkPipeline; - class SHMesh; - class SHRenderable; - class SHVkLogicalDevice; - class SHMaterialInstance; - class SHVkDescriptorSetGroup; - class SHVkDescriptorPool; + Handle Mesh; + std::unordered_set Renderables; + }; + /*************************************************************************************/ + /*! + \brief + Describes a segment of the sub batch operation. + */ + /*************************************************************************************/ + class SHBatch + { + public: + /*---------------------------------------------------------------------------------*/ + /* Constructor/Destructors */ + /*---------------------------------------------------------------------------------*/ + SHBatch(Handle pipeline); + SHBatch(const SHBatch&) = delete; + SHBatch(SHBatch&& rhs); + SHBatch& operator=(const SHBatch&) = delete; + SHBatch& operator=(SHBatch&& rhs); + ~SHBatch(); /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ + /* Usage Functions */ /*---------------------------------------------------------------------------------*/ - /***********************************************************************************/ - /*! - \brief - Describes a segment of the sub batch operation. - */ - /***********************************************************************************/ - struct SHSubBatch - { - public: - /*-----------------------------------------------------------------------------*/ - /* Data Members */ - /*-----------------------------------------------------------------------------*/ - Handle Mesh; - std::unordered_set Renderables; - }; - /***********************************************************************************/ - /*! - \brief - Describes a segment of the sub batch operation. - */ - /***********************************************************************************/ - class SHBatch - { - public: - /*-----------------------------------------------------------------------------*/ - /* Constructor/Destructors */ - /*-----------------------------------------------------------------------------*/ - SHBatch(Handle pipeline); - SHBatch(const SHBatch&) = delete; - SHBatch(SHBatch&& rhs); - SHBatch& operator=(const SHBatch&) = delete; - SHBatch& operator=(SHBatch&& rhs); - ~SHBatch(); + void Add(const SHRenderable* renderable); + void Remove(const SHRenderable* renderable); + void Clear(); + void UpdateMaterialBuffer(uint32_t frameIndex, Handle descPool); + void UpdateTransformBuffer(uint32_t frameIndex); + void UpdateInstancedIntegerBuffer(uint32_t frameIndex); + void Build(Handle device, Handle descPool, uint32_t frameIndex); + void Draw(Handle cmdBuffer, uint32_t frameIndex); - /*-----------------------------------------------------------------------------*/ - /* Usage Functions */ - /*-----------------------------------------------------------------------------*/ - void Add(const SHRenderable* renderable); - void Remove(const SHRenderable* renderable); - void Clear(); - void UpdateMaterialBuffer(uint32_t frameIndex, Handle descPool); - void UpdateTransformBuffer(uint32_t frameIndex); - void UpdateInstancedIntegerBuffer(uint32_t frameIndex); - void Build(Handle device, Handle descPool, uint32_t frameIndex) ; - void Draw(Handle cmdBuffer, uint32_t frameIndex); + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + Handle GetPipeline() const noexcept { return pipeline; }; + bool IsEmpty() const noexcept { return subBatches.empty(); } - /*-----------------------------------------------------------------------------*/ - /* Getter Functions */ - /*-----------------------------------------------------------------------------*/ - Handle GetPipeline() const noexcept { return pipeline; }; - bool IsEmpty() const noexcept { return subBatches.empty(); } + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definition */ + /*---------------------------------------------------------------------------------*/ + using TripleBool = std::array; + using TripleBuffer = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; + using TripleDescSet = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; - private: - /*-----------------------------------------------------------------------------*/ - /* Type Definition */ - /*-----------------------------------------------------------------------------*/ - using TripleBool = std::array; - using TripleBuffer = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; - using TripleDescSet = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + // Resources + Handle device; + // Batch Properties + Handle pipeline; + std::unordered_set> referencedMatInstances; + TripleBool matBufferDirty; + // Batch Tree + std::vector subBatches; + TripleBool isDirty; + // CPU Buffers + std::vector drawData; + std::vector transformData; + std::vector instancedIntegerData; + std::unique_ptr matPropsData; + Byte matPropsDataSize = 0; + Byte singleMatPropAlignedSize = 0; + Byte singleMatPropSize = 0; + std::vector boneMatrixData; + std::vector boneMatrixIndices; + bool isCPUBuffersDirty = true; + // GPU Buffers + TripleBuffer drawDataBuffer; + TripleBuffer transformDataBuffer; + TripleBuffer instancedIntegerBuffer; + TripleBuffer matPropsBuffer; + TripleDescSet matPropsDescSet; + TripleBuffer boneMatrixBuffer; + TripleBuffer boneFirstIndexBuffer; - /*-----------------------------------------------------------------------------*/ - /* Data Members */ - /*-----------------------------------------------------------------------------*/ - // Resources - Handle device; - // Batch Properties - Handle pipeline; - std::unordered_set> referencedMatInstances; - TripleBool matBufferDirty; - // Batch Tree - std::vector subBatches; - TripleBool isDirty; - // CPU Buffers - std::vector drawData; - std::vector transformData; - std::vector instancedIntegerData; - std::unique_ptr matPropsData; - Byte matPropsDataSize = 0; - Byte singleMatPropAlignedSize = 0; - Byte singleMatPropSize = 0; - bool isCPUBuffersDirty = true; - // GPU Buffers - TripleBuffer drawDataBuffer; - TripleBuffer transformDataBuffer; - TripleBuffer instancedIntegerBuffer; - TripleBuffer matPropsBuffer; - TripleDescSet matPropsDescSet; - - /*-----------------------------------------------------------------------------*/ - /* Helper Functions */ - /*-----------------------------------------------------------------------------*/ - void setAllDirtyFlags(); - void rebuildMaterialBuffers(uint32_t frameIndex, Handle descPool); - }; + /*-----------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------*/ + void setAllDirtyFlags(); + void rebuildMaterialBuffers(uint32_t frameIndex, Handle descPool); + }; } From 1a20eeed96de6fa21587bd83e378f53d9bf80802 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 21 Nov 2022 19:46:53 +0800 Subject: [PATCH 006/164] Added system routine stub for the SHAnimationSystem --- .../src/Animation/SHAnimationSystem.cpp | 20 +++++++++++++++++-- .../src/Animation/SHAnimationSystem.h | 18 ++++++++++++++++- .../src/Animation/SHAnimatorComponent.cpp | 10 ++++++++-- .../src/Animation/SHAnimatorComponent.h | 20 +++++++++++++++++++ SHADE_Engine/src/Animation/SHRig.cpp | 7 +++++++ SHADE_Engine/src/Animation/SHRig.h | 8 +++++++- 6 files changed, 77 insertions(+), 6 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimationSystem.cpp b/SHADE_Engine/src/Animation/SHAnimationSystem.cpp index 28b1a80e..8c514e7c 100644 --- a/SHADE_Engine/src/Animation/SHAnimationSystem.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationSystem.cpp @@ -9,12 +9,28 @@ Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ -// Pre-compiled Header +// Precompiled Header #include "SHpch.h" // Primary Include #include "SHAnimationSystem.h" +// Project Includes +#include "ECS_Base/Managers/SHComponentManager.h" +#include "SHAnimatorComponent.h" namespace SHADE { - + /*-----------------------------------------------------------------------------------*/ + /* System Routine Functions - UpdateRoutine */ + /*-----------------------------------------------------------------------------------*/ + SHAnimationSystem::UpdateRoutine::UpdateRoutine() + : SHSystemRoutine("Animation System Update", false) + {} + void SHAnimationSystem::UpdateRoutine::Execute(double dt) noexcept + { + auto& animators = SHComponentManager::GetDense(); + for (auto& animator : animators) + { + animator.Update(dt); + } + } } diff --git a/SHADE_Engine/src/Animation/SHAnimationSystem.h b/SHADE_Engine/src/Animation/SHAnimationSystem.h index eac839ad..96cd9a71 100644 --- a/SHADE_Engine/src/Animation/SHAnimationSystem.h +++ b/SHADE_Engine/src/Animation/SHAnimationSystem.h @@ -25,8 +25,24 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /// + /// System that is responsible for updating all animations. + /// class SH_API SHAnimationSystem : public SHSystem { - + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Responsible for updating the playback of all animator components and computing + /// the required bone matrices. + /// + class SH_API UpdateRoutine final : public SHSystemRoutine + { + public: + UpdateRoutine(); + void Execute(double dt) noexcept override final; + }; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 3b4524e1..61c0e6af 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -9,7 +9,7 @@ Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ -// Pre-compiled Header +// Precompiled Header #include "SHpch.h" // Primary Include #include "SHAnimatorComponent.h" @@ -19,7 +19,13 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { - + /*-----------------------------------------------------------------------------------*/ + /* Update Functions */ + /*-----------------------------------------------------------------------------------*/ + void SHAnimatorComponent::Update(float dt) + { + // Set everything to identity + } } RTTR_REGISTRATION diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index be1bc742..dd71cc0e 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -37,10 +37,29 @@ namespace SHADE class SH_API SHAnimatorComponent final : public SHComponent { public: + /*---------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*---------------------------------------------------------------------------------*/ + /*void Play(); + void PlayFromStart(); + void Pause(); + void Stop();*/ + /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ const std::vector& GetBoneMatrices() const noexcept { return boneMatrices; } + bool IsPlaying() const { return isPlaying; } + + /*---------------------------------------------------------------------------------*/ + /* Update Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Updates the current state of the animation if one is specified based on the + /// current animation clip and frames. This will update the bone matrices. + /// + /// Time passed since the last frame. + void Update(float dt); private: /*---------------------------------------------------------------------------------*/ @@ -49,6 +68,7 @@ namespace SHADE Handle rig; std::vector boneMatrices; float currPlaybackTime = 0.0f; + bool isPlaying = false; /*---------------------------------------------------------------------------------*/ /* RTTR */ diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 0ef2a0e6..2214baa5 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -30,6 +30,7 @@ namespace SHADE return; // Do a recursive depth first traversal to populate the rig + nodeCount = 0; rootNode = recurseCreateNode(asset, asset.root); } @@ -54,6 +55,11 @@ namespace SHADE return {}; } + int SHRig::GetNodeCount() const noexcept + { + return nodeCount; + } + /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ @@ -61,6 +67,7 @@ namespace SHADE { // Construct the node auto newNode = nodeStore.Create(); + ++nodeCount; // Fill the node with data const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef); diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index 39dbe1c4..af1b79ec 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -53,7 +53,7 @@ namespace SHADE explicit SHRig(const SHRigAsset& asset); /*---------------------------------------------------------------------------------*/ - /* Usage Functions */ + /* Getter Functions */ /*---------------------------------------------------------------------------------*/ /// /// Retrieves the name of a node. @@ -73,6 +73,11 @@ namespace SHADE /// handle will be provided. /// Handle GetNode(const std::string& name) const noexcept; + /// + /// Returns the number of nodes in the rig. This matches the number of bone matrices + /// needed. + /// + int GetNodeCount() const noexcept; private: /*---------------------------------------------------------------------------------*/ @@ -81,6 +86,7 @@ namespace SHADE Handle rootNode; std::unordered_map, std::string> nodeNames; std::unordered_map> nodesByName; + int nodeCount = 0; SHResourceLibrary nodeStore; /*---------------------------------------------------------------------------------*/ From 52dc993941676f93f0af4985c197b63e38072016 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 2 Dec 2022 17:44:44 +0800 Subject: [PATCH 007/164] goodbye react --- .../src/Application/SBApplication.cpp | 29 -- SHADE_Application/src/Scenes/SBMainScene.cpp | 10 - SHADE_Engine/src/Camera/SHCameraSystem.cpp | 28 +- .../Inspector/SHEditorComponentView.hpp | 4 +- .../Physics/Collision/SHCollisionListener.cpp | 254 ----------- .../Physics/Collision/SHCollisionListener.h | 80 ---- .../Physics/Collision/SHPhysicsRaycaster.cpp | 350 -------------- .../Physics/Collision/SHPhysicsRaycaster.h | 134 ------ .../Physics/Interface/SHColliderComponent.cpp | 131 +----- .../Physics/Interface/SHColliderComponent.h | 9 - .../Physics/Interface/SHCollisionShape.cpp | 82 ++-- .../Interface/SHRigidBodyComponent.cpp | 310 ++----------- .../Physics/Interface/SHRigidBodyComponent.h | 28 +- .../Physics/PhysicsObject/SHPhysicsObject.cpp | 431 ------------------ .../Physics/PhysicsObject/SHPhysicsObject.h | 111 ----- .../PhysicsObject/SHPhysicsObjectManager.cpp | 309 ------------- .../PhysicsObject/SHPhysicsObjectManager.h | 181 -------- .../System/SHPhysicsDebugDrawSystem.cpp | 334 -------------- .../Physics/System/SHPhysicsDebugDrawSystem.h | 138 ------ .../src/Physics/System/SHPhysicsSystem.cpp | 394 ---------------- .../src/Physics/System/SHPhysicsSystem.h | 151 +----- .../System/SHPhysicsSystemInterface.cpp | 96 +--- .../Physics/System/SHPhysicsSystemInterface.h | 9 +- .../System/SHPhysicsSystemRoutines.cpp | 307 +------------ SHADE_Managed/src/Components/Collider.cxx | 2 +- SHADE_Managed/src/Physics/Physics.cxx | 18 +- SHADE_Managed/src/Utility/Convert.cxx | 32 -- SHADE_Managed/src/Utility/Convert.hxx | 14 - 28 files changed, 131 insertions(+), 3845 deletions(-) delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionListener.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionListener.h delete mode 100644 SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h delete mode 100644 SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp delete mode 100644 SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.h delete mode 100644 SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp delete mode 100644 SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h delete mode 100644 SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp delete mode 100644 SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 5aa1eb00..ce0a939c 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -31,7 +31,6 @@ #include "Input/SHInputManager.h" #include "Math/Transform/SHTransformSystem.h" #include "Physics/System/SHPhysicsSystem.h" -#include "Physics/System/SHPhysicsDebugDrawSystem.h" #include "Scripting/SHScriptEngine.h" #include "UI/SHUISystem.h" @@ -74,9 +73,6 @@ namespace Sandbox SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); -#ifndef _PUBLISH - SHSystemManager::CreateSystem(); -#endif SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); @@ -116,10 +112,6 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); -#ifndef _PUBLISH - SHSystemManager::RegisterRoutine(); -#endif - SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); @@ -192,27 +184,6 @@ namespace Sandbox #else SHSystemManager::RunRoutines(false, SHFrameRateController::GetRawDeltaTime()); #endif - // TODO: Move into an Editor menu - static bool drawContacts = false; - if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::F9)) - { - drawContacts = !drawContacts; - SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACT_POINTS, drawContacts); - SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACT_NORMALS, drawContacts); - } - static bool drawColliders = false; - if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::F10)) - { - drawColliders = !drawColliders; - SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDER, drawColliders); - } - static bool drawRays = false; - if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::F11)) - { - drawRays = !drawRays; - SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS, drawRays); - } - } // Finish all graphics jobs first graphicsSystem->AwaitGraphicsExecution(); diff --git a/SHADE_Application/src/Scenes/SBMainScene.cpp b/SHADE_Application/src/Scenes/SBMainScene.cpp index 73926115..55ce32b0 100644 --- a/SHADE_Application/src/Scenes/SBMainScene.cpp +++ b/SHADE_Application/src/Scenes/SBMainScene.cpp @@ -51,16 +51,6 @@ namespace Sandbox return; } - #ifdef SHEDITOR - - physicsSystem->ForceBuild(SHSceneManager::GetCurrentSceneGraph()); - - #else - - physicsSystem->BuildScene(SHSceneManager::GetCurrentSceneGraph()); - - #endif - /*-----------------------------------------------------------------------*/ /* TESTING CODE */ /*-----------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 8ef7ff64..d94bd3f8 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -186,20 +186,20 @@ namespace SHADE //SHLOG_INFO("Ray position: {},{},{} direction:{},{},{}",pivot.ray.position.x, pivot.ray.position.y, pivot.ray.position.z,pivot.ray.direction.x, pivot.ray.direction.y, pivot.ray.direction.z) - auto result = physicsSystem->Raycast(pivot.ray ); - if (result && result.distance < pivot.GetArmLength()) - { - - SHVec3 newOffset = SHVec3{ 0.0f,0.0f, result.distance * 0.8f }; - newOffset = SHVec3::RotateX(newOffset, -(SHMath::DegreesToRadians(pivot.GetPitch()))); - newOffset = SHVec3::RotateY(newOffset, (SHMath::DegreesToRadians(pivot.GetYaw()))); - pivot.offset = newOffset; - //SHLOG_INFO("CAMERA COLLISION HIT, {}", result.distance); - } - else - { - //SHLOG_INFO("CAMERA COLLISION CANT HIT CAMERA"); - } + //auto result = physicsSystem->Raycast(pivot.ray ); + //if (result && result.distance < pivot.GetArmLength()) + //{ + // + // SHVec3 newOffset = SHVec3{ 0.0f,0.0f, result.distance * 0.8f }; + // newOffset = SHVec3::RotateX(newOffset, -(SHMath::DegreesToRadians(pivot.GetPitch()))); + // newOffset = SHVec3::RotateY(newOffset, (SHMath::DegreesToRadians(pivot.GetYaw()))); + // pivot.offset = newOffset; + // //SHLOG_INFO("CAMERA COLLISION HIT, {}", result.distance); + //} + //else + //{ + // //SHLOG_INFO("CAMERA COLLISION CANT HIT CAMERA"); + //} diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 0f3dce3e..f70ab889 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -285,8 +285,8 @@ namespace SHADE if(ImGui::CollapsingHeader("Debug Information", ImGuiTreeNodeFlags_DefaultOpen))//Dynamic or Kinematic only fields { SHEditorWidgets::DragFloat("Mass", [component] { return component->GetMass(); }, [](float value){}, "Mass", 0.1f, 0.0f, std::numeric_limits::infinity(), "%.3f", ImGuiSliderFlags_ReadOnly); - SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component] {return component->GetPosition(); }, [](SHVec3 const& value) {}, false, "Position", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); - SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] {return component->GetRotation(); }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); + //SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component] {return component->GetPosition(); }, [](SHVec3 const& value) {}, false, "Position", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); + //SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] {return component->GetRotation(); }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { SHEditorWidgets::DragVec3("Velocity", { "X", "Y", "Z" }, [component] {return component->GetLinearVelocity(); }, [](SHVec3 const& value) {}, false, "Linear Velocity", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionListener.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionListener.cpp deleted file mode 100644 index 3e485153..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionListener.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionListener.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Collision Listener. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollisionListener.h" - -// Project Headers -#include "ECS_Base/Managers/SHEntityManager.h" -#include "Physics/PhysicsObject/SHPhysicsObject.h" -#include "Physics/System/SHPhysicsSystem.h" -#include "Scene/SHSceneManager.h" - -/*-------------------------------------------------------------------------------------*/ -/* Local Helper Functions */ -/*-------------------------------------------------------------------------------------*/ - -uint32_t matchColliders(const SHADE::SHPhysicsObject&physicsObject, const rp3d::Entity colliderID) -{ - for (uint32_t i = 0; i < physicsObject.GetCollisionBody()->getNbColliders(); ++i) - { - const auto* collider = physicsObject.GetCollisionBody()->getCollider(i); - if (collider->getEntity() == colliderID) - return i; - } - - return std::numeric_limits::max(); -} - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollisionListener::SHCollisionListener() noexcept - : system { nullptr } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const std::vector& SHCollisionListener::GetCollisionInfoContainer() const noexcept - { - return collisionInfoContainer; - } - - const std::vector& SHCollisionListener::GetTriggerInfoContainer() const noexcept - { - return triggerInfoContainer; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionListener::BindToSystem(SHPhysicsSystem* physicsSystem) noexcept - { - system = physicsSystem; - } - - void SHCollisionListener::BindToWorld(rp3d::PhysicsWorld* world) noexcept - { - if (!world) - return; - - world->setEventListener(this); - } - - void SHCollisionListener::CleanContainers() noexcept - { - static const auto CLEAR = [](std::vector& container) - { - for (auto eventIter = container.begin(); eventIter != container.end();) - { - const SHCollisionInfo& C_INFO = *eventIter; - - const bool INVALID_ENTITY = !SHEntityManager::IsValidEID(C_INFO.GetEntityA()) || !SHEntityManager::IsValidEID(C_INFO.GetEntityB()); - if (INVALID_ENTITY) - { - eventIter = container.erase(eventIter); - continue; - } - else - { - const bool CLEAR_EVENT = C_INFO.GetCollisionState() == SHCollisionInfo::State::EXIT || C_INFO.GetCollisionState() == SHCollisionInfo::State::INVALID; - - const bool INACTIVE_OBJECT = !SHSceneManager::CheckNodeAndComponentsActive(C_INFO.GetEntityA()) - || !SHSceneManager::CheckNodeAndComponentsActive(C_INFO.GetEntityB()); - - if (CLEAR_EVENT || INACTIVE_OBJECT) - { - eventIter = container.erase(eventIter); - continue; - } - } - - ++eventIter; - } - }; - - CLEAR(collisionInfoContainer); - CLEAR(triggerInfoContainer); - } - - void SHCollisionListener::ClearContainers() noexcept - { - collisionInfoContainer.clear(); - triggerInfoContainer.clear(); - } - - void SHCollisionListener::onContact(const rp3d::CollisionCallback::CallbackData& callbackData) - { - for (uint32_t i = 0; i < callbackData.getNbContactPairs(); ++i) - { - const auto CONTACT_PAIR = callbackData.getContactPair(i); - const SHCollisionInfo NEW_INFO = generateCollisionInfo(CONTACT_PAIR); - - updateInfoContainers(NEW_INFO, collisionInfoContainer); - } - } - - void SHCollisionListener::onTrigger(const rp3d::OverlapCallback::CallbackData& callbackData) - { - for (uint32_t i = 0; i < callbackData.getNbOverlappingPairs(); ++i) - { - const auto OVERLAP_PAIR = callbackData.getOverlappingPair(i); - const SHCollisionInfo NEW_INFO = generateTriggerInfo(OVERLAP_PAIR); - - updateInfoContainers(NEW_INFO, triggerInfoContainer); - } - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionListener::updateInfoContainers(const SHCollisionInfo& collisionEvent, std::vector& container) noexcept - { - const auto IT = std::ranges::find_if(container.begin(), container.end(), [&](const SHCollisionInfo& info) - { - const bool ENTITY_MATCH = (info.ids[0] == collisionEvent.ids[0] && info.ids[1] == collisionEvent.ids[1]) - || (info.ids[0] == collisionEvent.ids[1] && info.ids[1] == collisionEvent.ids[0]); - const bool COLLIDERS_MATCH = (info.ids[2] == collisionEvent.ids[2] && info.ids[3] == collisionEvent.ids[3]) - || (info.ids[2] == collisionEvent.ids[3] && info.ids[3] == collisionEvent.ids[2]); - return ENTITY_MATCH && COLLIDERS_MATCH; - }); - - if (IT == container.end()) - container.emplace_back(collisionEvent); - else - IT->collisionState = collisionEvent.collisionState; - } - - SHCollisionInfo SHCollisionListener::generateCollisionInfo(const rp3d::CollisionCallback::ContactPair& cp) const noexcept - { - SHCollisionInfo cInfo; - - // Update collision state - cInfo.collisionState = static_cast(cp.getEventType()); - - // Match body and collider for collision event - const rp3d::Entity body1 = cp.getBody1()->getEntity(); - const rp3d::Entity body2 = cp.getBody2()->getEntity(); - const rp3d::Entity collider1 = cp.getCollider1()->getEntity(); - const rp3d::Entity collider2 = cp.getCollider2()->getEntity(); - - // Find and match both ids - bool matched[2] = { false, false }; - - - for (auto& [entityID, physicsObject] : system->GetPhysicsObjects()) - { - // Match body 1 - if (matched[SHCollisionInfo::ENTITY_A] == false && physicsObject.GetCollisionBody()->getEntity() == body1) - { - cInfo.ids[SHCollisionInfo::ENTITY_A] = entityID; - cInfo.ids[SHCollisionInfo::COLLIDER_A] = matchColliders(physicsObject, collider1); - - matched[SHCollisionInfo::ENTITY_A] = true; - } - - // Match body 2 - if (matched[SHCollisionInfo::ENTITY_B] == false && physicsObject.GetCollisionBody()->getEntity() == body2) - { - cInfo.ids[SHCollisionInfo::ENTITY_B] = entityID; - cInfo.ids[SHCollisionInfo::COLLIDER_B] = matchColliders(physicsObject, collider2); - - matched[SHCollisionInfo::ENTITY_B] = true; - } - - if (matched[SHCollisionInfo::ENTITY_A] == true && matched[SHCollisionInfo::ENTITY_B] == true) - return cInfo; - } - - return cInfo; - } - - SHCollisionInfo SHCollisionListener::generateTriggerInfo(const rp3d::OverlapCallback::OverlapPair& cp) const noexcept - { - SHCollisionInfo cInfo; - - // Update collision state - cInfo.collisionState = static_cast(cp.getEventType()); - - // Match body and collider for collision event - const rp3d::Entity body1 = cp.getBody1()->getEntity(); - const rp3d::Entity body2 = cp.getBody2()->getEntity(); - const rp3d::Entity collider1 = cp.getCollider1()->getEntity(); - const rp3d::Entity collider2 = cp.getCollider2()->getEntity(); - - // Find and match both ids - bool matched[2] = { false, false }; - - - for (auto& [entityID, physicsObject] : system->GetPhysicsObjects()) - { - // Match body 1 - if (matched[SHCollisionInfo::ENTITY_A] == false && physicsObject.GetCollisionBody()->getEntity() == body1) - { - cInfo.ids[SHCollisionInfo::ENTITY_A] = entityID; - cInfo.ids[SHCollisionInfo::COLLIDER_A] = matchColliders(physicsObject, collider1); - - matched[SHCollisionInfo::ENTITY_A] = true; - } - - // Match body 2 - if (matched[SHCollisionInfo::ENTITY_B] == false && physicsObject.GetCollisionBody()->getEntity() == body2) - { - cInfo.ids[SHCollisionInfo::ENTITY_B] = entityID; - cInfo.ids[SHCollisionInfo::COLLIDER_B] = matchColliders(physicsObject, collider2); - - matched[SHCollisionInfo::ENTITY_B] = true; - } - - if (matched[SHCollisionInfo::ENTITY_A] == true && matched[SHCollisionInfo::ENTITY_B] == true) - return cInfo; - } - - return cInfo; - } - - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h b/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h deleted file mode 100644 index 62882556..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionListener.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collision Listener. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// External Dependencies -#include - -// Project Headers -#include "SH_API.h" -#include "SHCollisionInfo.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - class SHPhysicsSystem; - - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - class SH_API SHCollisionListener final : public rp3d::EventListener - { - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHCollisionListener() noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] const std::vector& GetCollisionInfoContainer () const noexcept; - [[nodiscard]] const std::vector& GetTriggerInfoContainer () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - void BindToSystem (SHPhysicsSystem* physicsSystem) noexcept; - void BindToWorld (rp3d::PhysicsWorld* world) noexcept; - void CleanContainers () noexcept; - void ClearContainers () noexcept; - - void onContact (const rp3d::CollisionCallback::CallbackData& callbackData) override; - void onTrigger (const rp3d::OverlapCallback::CallbackData& callbackData) override; - - private: - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsSystem* system; - std::vector collisionInfoContainer; - std::vector triggerInfoContainer; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - static void updateInfoContainers (const SHCollisionInfo& collisionEvent, std::vector& container) noexcept; - - SHCollisionInfo generateCollisionInfo (const rp3d::CollisionCallback::ContactPair& cp) const noexcept; - SHCollisionInfo generateTriggerInfo (const rp3d::OverlapCallback::OverlapPair& cp) const noexcept; - }; - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp deleted file mode 100644 index cab5c93b..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsRaycaster.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Physics Raycaster. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHPhysicsRaycaster.h" - -/* - * TODO(DIREN): - * Once the physics engine has been rebuilt, this whole implementation should change - * and just call PhysicsWorld.Raycast etc. - * - * SHRaycastResult can be converted to a bool when necessary. - */ - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsRaycaster::SHPhysicsRaycaster() noexcept - : world { nullptr } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHPhysicsRaycaster::RaycastPairs& SHPhysicsRaycaster::GetRaycasts() const noexcept - { - return raycasts; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsRaycaster::SetObjectManager(SHPhysicsObjectManager* physicsObjectManager) noexcept - { - objectManager = physicsObjectManager; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsRaycaster::BindToWorld(rp3d::PhysicsWorld* physicsWorld) noexcept - { - world = physicsWorld; - } - - void SHPhysicsRaycaster::ClearFrame() noexcept - { - raycasts.clear(); - } - - SHPhysicsRaycastResult SHPhysicsRaycaster::Raycast(const SHRay& ray, float distance, const SHCollisionTag& collisionTag) noexcept - { - // Reset temp - temp = SHPhysicsRaycastResult{}; - temp.distance = distance; - - if (!world) - { - SHLOG_ERROR("Physics world missing for raycasting!") - return temp; - } - - // If distance in infinity, cast to the default max distance of 2 km. - if (distance == std::numeric_limits::infinity()) - { - world->raycast(ray, this, collisionTag); - } - else - { - const SHVec3 END_POINT = ray.position + ray.direction * distance; - const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; - world->raycast(RP3D_RAY, this, collisionTag); - } - - // If a hit was found, populate temp info for return. - if (temp.hit) - { - temp.distance = SHVec3::Distance(ray.position, temp.position); - temp.angle = SHVec3::Angle(ray.position, temp.position); - } - - raycasts.emplace_back(ray, temp); - return temp; - } - - SHPhysicsRaycastResult SHPhysicsRaycaster::Linecast(const SHVec3& start, const SHVec3& end, const SHCollisionTag& collisionTag) noexcept - { - temp = SHPhysicsRaycastResult{}; - temp.distance = SHVec3::Distance(start, end); - - if (!world) - { - SHLOG_ERROR("Physics world missing for raycasting!") - return temp; - } - - const rp3d::Ray RP3D_RAY{ start, end }; - world->raycast(RP3D_RAY, this, collisionTag); - - if (temp.hit) - { - temp.distance = SHVec3::Distance(start, temp.position); - temp.angle = SHVec3::Angle(start, temp.position); - } - - raycasts.emplace_back(RP3D_RAY, temp); - return temp; - } - - SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderRaycast(EntityID eid, const SHRay& ray, float distance) noexcept - { - SHPhysicsRaycastResult result; - result.distance = distance; - - // Get a valid physics object with at least 1 collider. - const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); - if (!PHYSICS_OBJECT) - return result; - - auto* rp3dBody = PHYSICS_OBJECT->GetCollisionBody(); - - // Data to populate - rp3d::RaycastInfo rp3dRaycastInfo; - bool hit = false; - - if (distance == std::numeric_limits::infinity()) - { - hit = rp3dBody->raycast(ray, rp3dRaycastInfo); - } - else - { - const SHVec3 END_POINT = ray.position + ray.direction * distance; - const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; - hit = rp3dBody->raycast(RP3D_RAY, rp3dRaycastInfo); - } - - if (hit) - { - result.hit = true; - result.position = rp3dRaycastInfo.worldPoint; - result.normal = rp3dRaycastInfo.worldPoint; - result.distance = SHVec3::Distance(ray.position, result.position); - result.angle = SHVec3::Angle(ray.position, result.position); - result.entityHit = eid; - result.shapeIndex = findColliderIndex(rp3dBody, rp3dRaycastInfo.collider->getEntity()); - } - - raycasts.emplace_back(ray, result); - return result; - } - - SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderRaycast(EntityID eid, int shapeIndex, const SHRay& ray, float distance) noexcept - { - SHPhysicsRaycastResult result; - result.distance = distance; - - // Get a valid physics object with at least 1 collider. - const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); - if (!PHYSICS_OBJECT) - return result; - - // Boundary check for shape index - if (shapeIndex < 0 || shapeIndex >= static_cast(PHYSICS_OBJECT->GetCollisionBody()->getNbColliders())) - { - SHLOGV_WARNING("Invalid collision shape index passed in") - return result; - } - - auto* rp3dCollider = PHYSICS_OBJECT->GetCollisionBody()->getCollider(shapeIndex); - - rp3d::RaycastInfo rp3dRaycastInfo; - bool hit = false; - if (distance == std::numeric_limits::infinity()) - { - hit = rp3dCollider->raycast(ray, rp3dRaycastInfo); - } - else - { - const SHVec3 END_POINT = ray.position + ray.direction * distance; - const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; - hit = rp3dCollider->raycast(RP3D_RAY, rp3dRaycastInfo); - } - - if (hit) - { - result.hit = true; - result.position = rp3dRaycastInfo.worldPoint; - result.normal = rp3dRaycastInfo.worldPoint; - result.distance = SHVec3::Distance(ray.position, result.position); - result.angle = SHVec3::Angle(ray.position, result.position); - result.entityHit = eid; - result.shapeIndex = shapeIndex; - } - - raycasts.emplace_back(ray, result); - return result; - } - - SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderLinecast(EntityID eid, const SHVec3& start, const SHVec3& end) noexcept - { - SHPhysicsRaycastResult result; - result.distance = SHVec3::Distance(start, end); - - const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); - if (!PHYSICS_OBJECT) - return result; - - auto* rp3dBody = PHYSICS_OBJECT->GetCollisionBody(); - - rp3d::RaycastInfo rp3dRaycastInfo; - - const rp3d::Ray RP3D_RAY{ start, end }; - if (rp3dBody->raycast(RP3D_RAY, rp3dRaycastInfo)) - { - result.hit = true; - result.position = rp3dRaycastInfo.worldPoint; - result.normal = rp3dRaycastInfo.worldPoint; - result.distance = SHVec3::Distance(start, result.position); - result.angle = SHVec3::Angle(end, result.position); - result.entityHit = eid; - result.shapeIndex = findColliderIndex(rp3dBody, rp3dRaycastInfo.collider->getEntity()); - } - - raycasts.emplace_back(RP3D_RAY, result); - return result; - } - - SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderLinecast(EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept - { - SHPhysicsRaycastResult result; - result.distance = SHVec3::Distance(start, end); - - const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); - if (!PHYSICS_OBJECT) - return result; - - if (shapeIndex < 0 || shapeIndex >= static_cast(PHYSICS_OBJECT->GetCollisionBody()->getNbColliders())) - { - SHLOGV_WARNING("Invalid collision shape index passed in") - return result; - } - - auto* rp3dCollider = PHYSICS_OBJECT->GetCollisionBody()->getCollider(shapeIndex); - - rp3d::RaycastInfo rp3dRaycastInfo; - - const rp3d::Ray RP3D_RAY{ start, end }; - if (rp3dCollider->raycast(RP3D_RAY, rp3dRaycastInfo)) - { - result.hit = true; - result.position = rp3dRaycastInfo.worldPoint; - result.normal = rp3dRaycastInfo.worldPoint; - result.distance = SHVec3::Distance(start, result.position); - result.angle = SHVec3::Angle(end, result.position); - result.entityHit = eid; - result.shapeIndex = shapeIndex; - } - - raycasts.emplace_back(RP3D_RAY, result); - return result; - } - - rp3d::decimal SHPhysicsRaycaster::notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) - { - temp.hit = true; - temp.position = raycastInfo.worldPoint; - temp.normal = raycastInfo.worldNormal; - - if (!objectManager) - { - SHLOGV_ERROR("No physics object manager linked with raycaster to match bodies") - return 0.0f; - } - - // Compare body IDs to find the matching physics object - const auto HIT_BODY_EID = raycastInfo.body->getEntity(); - - for (const auto& [entityID, physicsObject] : objectManager->GetPhysicsObjects()) - { - const auto RP3D_BODY = physicsObject.GetCollisionBody(); - - // Match rp3d bodies - if (RP3D_BODY->getEntity() != HIT_BODY_EID) - continue; - - temp.entityHit = entityID; - - // Find collider index - if (const int INDEX = findColliderIndex(RP3D_BODY, raycastInfo.collider->getEntity()); INDEX > -1) - { - temp.shapeIndex = INDEX; - break; - } - } - - return 0.0f; - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject* SHPhysicsRaycaster::validateColliderRaycast(EntityID eid) noexcept - { - if (!objectManager) - { - SHLOGV_ERROR("No physics object manager linked with raycaster to match bodies") - return nullptr; - } - - auto* physicsObject = objectManager->GetPhysicsObject(eid); - if (!physicsObject || physicsObject->GetCollisionBody()->getNbColliders() == 0) - { - SHLOGV_WARNING("Cannot cast ray at an entity without colliders!") - return nullptr; - } - - return physicsObject; - } - - int SHPhysicsRaycaster::findColliderIndex(const rp3d::CollisionBody* rp3dBody, rp3d::Entity rp3dColliderEID) noexcept - { - const int NUM_COLLISION_SHAPES = static_cast(rp3dBody->getNbColliders()); - for (int i = 0; i < NUM_COLLISION_SHAPES; ++i) - { - const auto COLLIDER_EID = rp3dBody->getCollider(i)->getEntity(); - if (COLLIDER_EID == rp3dColliderEID) - return i; - } - - return -1; - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h deleted file mode 100644 index 447207c7..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h +++ /dev/null @@ -1,134 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsRaycaster.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Physics Raycaster. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -#include - -// Project Headers -#include "Math/SHRay.h" -#include "Physics/PhysicsObject/SHPhysicsObjectManager.h" -#include "Physics/SHPhysicsWorld.h" -#include "SH_API.h" -#include "SHCollisionTags.h" -#include "SHPhysicsRaycastResult.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - class SH_API SHPhysicsRaycaster : public reactphysics3d::RaycastCallback - { - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - using RaycastPair = std::pair; - using RaycastPairs = std::vector; - - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsRaycaster() noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] const RaycastPairs& GetRaycasts() const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetObjectManager(SHPhysicsObjectManager* physicsObjectManager) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - void BindToWorld (rp3d::PhysicsWorld* physicsWorld) noexcept; - void ClearFrame () noexcept; - - // TODO(Diren): Filtering, return all shades ray hits - - SHPhysicsRaycastResult Raycast - ( - const SHRay& ray - , float distance = std::numeric_limits::infinity() - , const SHCollisionTag& collisionTag = SHCollisionTag{} - ) noexcept; - - SHPhysicsRaycastResult Linecast - ( - const SHVec3& start - , const SHVec3& end - , const SHCollisionTag& collisionTag = SHCollisionTag{} - ) noexcept; - - SHPhysicsRaycastResult ColliderRaycast - ( - EntityID eid - , const SHRay& ray - , float distance = std::numeric_limits::infinity() - ) noexcept; - - SHPhysicsRaycastResult ColliderRaycast - ( - EntityID eid - , int shapeIndex - , const SHRay& ray - , float distance = std::numeric_limits::infinity() - ) noexcept; - - SHPhysicsRaycastResult ColliderLinecast - ( - EntityID eid - , const SHVec3& start - , const SHVec3& end - ) noexcept; - - SHPhysicsRaycastResult ColliderLinecast - ( - EntityID eid - , int shapeIndex - , const SHVec3& start - , const SHVec3& end - ) noexcept; - - rp3d::decimal notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) override; - - private: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - rp3d::PhysicsWorld* world; - SHPhysicsObjectManager* objectManager; // For - SHPhysicsRaycastResult temp; // Holds the temporary result after casting into the world - RaycastPairs raycasts; // Used for debug drawing - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsObject* validateColliderRaycast (EntityID eid) noexcept; - static int findColliderIndex (const rp3d::CollisionBody* rp3dBody, rp3d::Entity rp3dColliderEID) noexcept; - }; - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp index d7db2c64..eef92493 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp @@ -25,34 +25,12 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHColliderComponent::SHColliderComponent() noexcept - : system { nullptr } {} /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - - const SHVec3& SHColliderComponent::GetPosition() const noexcept - { - return position; - } - - const SHQuaternion& SHColliderComponent::GetOrientation() const noexcept - { - return orientation; - } - - SHVec3 SHColliderComponent::GetRotation() const noexcept - { - return orientation.ToEuler(); - } - - const SHVec3& SHColliderComponent::GetScale() const noexcept - { - return scale; - } - const SHColliderComponent::CollisionShapes& SHColliderComponent::GetCollisionShapes() const noexcept { return collisionShapes; @@ -72,22 +50,7 @@ namespace SHADE void SHColliderComponent::OnCreate() { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (!physicsSystem) - { - SHLOG_ERROR("Physics System does not exist to link with Physics Components!") - return; - } - system = physicsSystem; - - // Sync with transform if one already exists - if (auto* transformComponent = SHComponentManager::GetComponent_s(GetEID()); transformComponent) - { - position = transformComponent->GetWorldPosition(); - orientation = transformComponent->GetWorldOrientation(); - scale = transformComponent->GetWorldScale(); - } } void SHColliderComponent::OnDestroy() @@ -97,112 +60,22 @@ namespace SHADE void SHColliderComponent::RecomputeCollisionShapes() noexcept { - for (auto& collisionShape : collisionShapes) - { - switch (collisionShape.GetType()) - { - case SHCollisionShape::Type::BOX: - { - auto* box = reinterpret_cast(collisionShape.shape); - const SHVec3& RELATIVE_EXTENTS = box->GetRelativeExtents(); - // Recompute world extents based on new scale and fixed relative extents - - const SHVec3 WORLD_EXTENTS = RELATIVE_EXTENTS * (scale * 0.5f); - box->SetWorldExtents(WORLD_EXTENTS); - - continue; - } - case SHCollisionShape::Type::SPHERE: - { - auto* sphere = reinterpret_cast(collisionShape.shape); - const float RELATIVE_RADIUS = sphere->GetRelativeRadius(); - - // Recompute world radius based on new scale and fixed radius - - const float MAX_SCALE = SHMath::Max({ scale.x, scale.y, scale.z }); - const float WORLD_RADIUS = RELATIVE_RADIUS * MAX_SCALE * 0.5f; - sphere->SetWorldRadius(WORLD_RADIUS); - - continue; - } - default: continue; - } - } } int SHColliderComponent::AddBoundingBox(const SHVec3& halfExtents, const SHVec3& posOffset, const SHVec3& rotOffset) noexcept { - if (!system) - { - SHLOG_ERROR("Physics system does not exist, unable to add Box Collider!") - return -1; - } - - static constexpr auto TYPE = SHCollisionShape::Type::BOX; - - auto& collider = collisionShapes.emplace_back(SHCollisionShape{ GetEID(), TYPE }); - - collider.entityID = GetEID(); - collider.SetPositionOffset(posOffset); - collider.SetRotationOffset(rotOffset); - collider.SetBoundingBox(halfExtents); - - // Notify Physics System - const int NEW_SHAPE_INDEX = static_cast(collisionShapes.size()) - 1; - - system->AddCollisionShape(GetEID(), NEW_SHAPE_INDEX); - return NEW_SHAPE_INDEX; + return 0; } int SHColliderComponent::AddBoundingSphere(float radius, const SHVec3& posOffset) noexcept { - if (!system) - { - SHLOG_ERROR("Physics system does not exist, unable to add Sphere Collider!") - return -1; - } - - static constexpr auto TYPE = SHCollisionShape::Type::SPHERE; - - auto& collider = collisionShapes.emplace_back(SHCollisionShape{ GetEID(), TYPE }); - - collider.entityID = GetEID(); - collider.SetPositionOffset(posOffset); - collider.SetBoundingSphere(radius); - - // Notify Physics System - const int NEW_SHAPE_INDEX = static_cast(collisionShapes.size()) - 1; - - system->AddCollisionShape(GetEID(), NEW_SHAPE_INDEX); - return NEW_SHAPE_INDEX; + return 0; } void SHColliderComponent::RemoveCollider(int index) { - if (index < 0 || static_cast(index) >= collisionShapes.size()) - throw std::invalid_argument("Out-of-range access!"); - int idx = 0; - auto it = collisionShapes.begin(); - for (; it != collisionShapes.end(); ++it) - { - if (idx == index) - break; - - ++idx; - } - - it = collisionShapes.erase(it); - - // Notify Physics System - if (!system) - { - SHLOG_ERROR("Physics system does not exist, unable to remove Collider!") - return; - } - - system->RemoveCollisionShape(GetEID(), index); } } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h index 0781f3cf..15a02173 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h @@ -69,11 +69,6 @@ namespace SHADE [[nodiscard]] bool HasChanged () const noexcept; - [[nodiscard]] const SHVec3& GetPosition () const noexcept; - [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; - [[nodiscard]] SHVec3 GetRotation () const noexcept; - [[nodiscard]] const SHVec3& GetScale () const noexcept; - [[nodiscard]] const CollisionShapes& GetCollisionShapes() const noexcept; [[nodiscard]] SHCollisionShape& GetCollisionShape (int index); @@ -97,10 +92,6 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHPhysicsSystem* system; - - SHVec3 position; - SHQuaternion orientation; SHVec3 scale; CollisionShapes collisionShapes; diff --git a/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp index f597077f..4dd506f9 100644 --- a/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp @@ -190,63 +190,63 @@ namespace SHADE void SHCollisionShape::SetBoundingBox(const SHVec3& halfExtents) { - dirty = true; + //dirty = true; - const auto* COLLIDER = SHComponentManager::GetComponent(entityID); - auto* box = reinterpret_cast(shape); + //const auto* COLLIDER = SHComponentManager::GetComponent(entityID); + //auto* box = reinterpret_cast(shape); - SHVec3 correctedHalfExtents = halfExtents; + //SHVec3 correctedHalfExtents = halfExtents; - // Get current relative halfExtents for error checking. 0 is ignored - const SHVec3& CURRENT_RELATIVE_EXTENTS = box->GetRelativeExtents(); - for (size_t i = 0; i < SHVec3::SIZE; ++i) - { - if (SHMath::CompareFloat(halfExtents[i], 0.0f)) - correctedHalfExtents[i] = CURRENT_RELATIVE_EXTENTS[i]; - } - - // Set the half extents relative to world scale - const SHVec3 WORLD_EXTENTS = correctedHalfExtents * COLLIDER->GetScale() * 0.5f; + //// Get current relative halfExtents for error checking. 0 is ignored + //const SHVec3& CURRENT_RELATIVE_EXTENTS = box->GetRelativeExtents(); + //for (size_t i = 0; i < SHVec3::SIZE; ++i) + //{ + // if (SHMath::CompareFloat(halfExtents[i], 0.0f)) + // correctedHalfExtents[i] = CURRENT_RELATIVE_EXTENTS[i]; + //} + // + //// Set the half extents relative to world scale + //const SHVec3 WORLD_EXTENTS = correctedHalfExtents * COLLIDER->GetScale() * 0.5f; - if (type != Type::BOX) - { - type = Type::BOX; + //if (type != Type::BOX) + //{ + // type = Type::BOX; - delete shape; - shape = new SHBox{ positionOffset, WORLD_EXTENTS }; - } + // delete shape; + // shape = new SHBox{ positionOffset, WORLD_EXTENTS }; + //} - box->SetWorldExtents(WORLD_EXTENTS); - box->SetRelativeExtents(correctedHalfExtents); + //box->SetWorldExtents(WORLD_EXTENTS); + //box->SetRelativeExtents(correctedHalfExtents); } void SHCollisionShape::SetBoundingSphere(float radius) { - dirty = true; + //dirty = true; - auto* sphere = reinterpret_cast(shape); - const auto* COLLIDER = SHComponentManager::GetComponent(entityID); + //auto* sphere = reinterpret_cast(shape); + //const auto* COLLIDER = SHComponentManager::GetComponent(entityID); - // Get current relative halfExtents for error checking. 0 is ignored - const float CURRENT_RELATIVE_RADIUS = sphere->GetRelativeRadius(); - if (SHMath::CompareFloat(radius, 0.0f)) - radius = CURRENT_RELATIVE_RADIUS; + //// Get current relative halfExtents for error checking. 0 is ignored + //const float CURRENT_RELATIVE_RADIUS = sphere->GetRelativeRadius(); + //if (SHMath::CompareFloat(radius, 0.0f)) + // radius = CURRENT_RELATIVE_RADIUS; - // Set the radius relative to world scale - const SHVec3 WORLD_SCALE = COLLIDER->GetScale(); - const float MAX_SCALE = SHMath::Max({ WORLD_SCALE.x, WORLD_SCALE.y, WORLD_SCALE.z }); - const float WORLD_RADIUS = radius * MAX_SCALE * 0.5f; + //// Set the radius relative to world scale + //const SHVec3 WORLD_SCALE = COLLIDER->GetScale(); + //const float MAX_SCALE = SHMath::Max({ WORLD_SCALE.x, WORLD_SCALE.y, WORLD_SCALE.z }); + //const float WORLD_RADIUS = radius * MAX_SCALE * 0.5f; - if (type != Type::SPHERE) - { - type = Type::SPHERE; + //if (type != Type::SPHERE) + //{ + // type = Type::SPHERE; - delete shape; - shape = new SHSphere{ positionOffset, WORLD_RADIUS }; - } + // delete shape; + // shape = new SHSphere{ positionOffset, WORLD_RADIUS }; + //} - sphere->SetWorldRadius(WORLD_RADIUS); - sphere->SetRelativeRadius(radius); + //sphere->SetWorldRadius(WORLD_RADIUS); + //sphere->SetRelativeRadius(radius); } void SHCollisionShape::SetIsTrigger(bool trigger) noexcept diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp index 330c3abe..6dad9020 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp @@ -28,18 +28,8 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHRigidBodyComponent::SHRigidBodyComponent() noexcept - : type { Type::DYNAMIC } - , flags { 0 } - , dirtyFlags { std::numeric_limits::max() } - , mass { 1.0f } - , drag { 0.01f } - , angularDrag { 0.01f } - , system { nullptr } { - // Initialise default flags - flags |= 1U << 0; // Gravity set to true - flags |= 1U << 1; // Sleeping allowed - flags |= 1U << 8; // Interpolate by default + } /*-----------------------------------------------------------------------------------*/ @@ -48,363 +38,166 @@ namespace SHADE bool SHRigidBodyComponent::IsGravityEnabled() const noexcept { - static constexpr int FLAG_POS = 0; - return flags & (1U << FLAG_POS); + return false; } bool SHRigidBodyComponent::IsAllowedToSleep() const noexcept { - static constexpr int FLAG_POS = 1; - return flags & (1U << FLAG_POS); + return false; } bool SHRigidBodyComponent::IsInterpolating() const noexcept { - static constexpr int FLAG_POS = 8; - return flags & (1U << FLAG_POS); + return false; } bool SHRigidBodyComponent::GetIsSleeping() const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - return physicsObject->GetRigidBody()->isSleeping(); - return false; } SHRigidBodyComponent::Type SHRigidBodyComponent::GetType() const noexcept { - return type; + return Type::STATIC; } bool SHRigidBodyComponent::GetFreezePositionX() const noexcept { - static constexpr int FLAG_POS = 2; - return flags & (1U << FLAG_POS); + return false; } bool SHRigidBodyComponent::GetFreezePositionY() const noexcept { - static constexpr int FLAG_POS = 3; - return flags & (1U << FLAG_POS); + return false; } bool SHRigidBodyComponent::GetFreezePositionZ() const noexcept { - static constexpr int FLAG_POS = 4; - return flags & (1U << FLAG_POS); + return false; } bool SHRigidBodyComponent::GetFreezeRotationX() const noexcept { - static constexpr int FLAG_POS = 5; - return flags & (1U << FLAG_POS); + return false; } bool SHRigidBodyComponent::GetFreezeRotationY() const noexcept { - static constexpr int FLAG_POS = 6; - return flags & (1U << FLAG_POS); + return false; } bool SHRigidBodyComponent::GetFreezeRotationZ() const noexcept { - static constexpr int FLAG_POS = 7; - return flags & (1U << FLAG_POS); + return false; } - //bool SHRigidBodyComponent::GetAutoMass() const noexcept - //{ - // static constexpr int FLAG_POS = 9; - // return flags & (1U << FLAG_POS); - //} - float SHRigidBodyComponent::GetMass() const noexcept { - return mass; + return 0.0f; } float SHRigidBodyComponent::GetDrag() const noexcept { - return drag; + return 0.0f; } float SHRigidBodyComponent::GetAngularDrag() const noexcept { - return angularDrag; + return 0.0f; } SHVec3 SHRigidBodyComponent::GetForce() const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - return physicsObject->GetRigidBody()->getForce(); - return SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetTorque() const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - return physicsObject->GetRigidBody()->getTorque(); - return SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetLinearVelocity() const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - return physicsObject->GetRigidBody()->getLinearVelocity(); - return SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetAngularVelocity() const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - return physicsObject->GetRigidBody()->getAngularVelocity(); - return SHVec3::Zero; } - const SHVec3& SHRigidBodyComponent::GetPosition() const noexcept - { - return position; - } - - const SHQuaternion& SHRigidBodyComponent::GetOrientation() const noexcept - { - return orientation; - } - - SHVec3 SHRigidBodyComponent::GetRotation() const noexcept - { - return orientation.ToEuler(); - } - /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ void SHRigidBodyComponent::SetType(Type newType) noexcept { - static constexpr int FLAG_POS = 8; - - if (type == newType) - return; - - type = newType; - dirtyFlags |= 1U << FLAG_POS; + } void SHRigidBodyComponent::SetGravityEnabled(bool enableGravity) noexcept { - static constexpr int FLAG_POS = 0; - - if (type != Type::DYNAMIC) - { - SHLOG_WARNING("Cannot enable gravity of a non-dynamic object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - enableGravity ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } void SHRigidBodyComponent::SetIsAllowedToSleep(bool isAllowedToSleep) noexcept { - static constexpr int FLAG_POS = 1; - - if (type != Type::DYNAMIC) - { - SHLOG_WARNING("Cannot enable sleeping of a non-dynamic object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - isAllowedToSleep ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } void SHRigidBodyComponent::SetFreezePositionX(bool freezePositionX) noexcept { - static constexpr int FLAG_POS = 2; - - if (type == Type::STATIC) - { - SHLOG_WARNING("Cannot set linear constraints of a static object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - freezePositionX ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } void SHRigidBodyComponent::SetFreezePositionY(bool freezePositionY) noexcept { - static constexpr int FLAG_POS = 3; - - if (type == Type::STATIC) - { - SHLOG_WARNING("Cannot set linear constraints of a static object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - freezePositionY ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } void SHRigidBodyComponent::SetFreezePositionZ(bool freezePositionZ) noexcept { - static constexpr int FLAG_POS = 4; - - if (type == Type::STATIC) - { - SHLOG_WARNING("Cannot set linear constraints of a static object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - freezePositionZ ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } void SHRigidBodyComponent::SetFreezeRotationX(bool freezeRotationX) noexcept { - static constexpr int FLAG_POS = 5; - - if (type == Type::STATIC) - { - SHLOG_WARNING("Cannot set angular constraints of a static object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - freezeRotationX ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } void SHRigidBodyComponent::SetFreezeRotationY(bool freezeRotationY) noexcept { - static constexpr int FLAG_POS = 6; - - if (type == Type::STATIC) - { - SHLOG_WARNING("Cannot set angular constraints of a static object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - freezeRotationY ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } void SHRigidBodyComponent::SetFreezeRotationZ(bool freezeRotationZ) noexcept { - static constexpr int FLAG_POS = 7; - - if (type == Type::STATIC) - { - SHLOG_WARNING("Cannot set angular constraints of a static object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - freezeRotationZ ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } void SHRigidBodyComponent::SetInterpolate(bool allowInterpolation) noexcept { - static constexpr int FLAG_POS = 8; - allowInterpolation ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); + } - //void SHRigidBodyComponent::SetAutoMass(bool autoMass) noexcept - //{ - // static constexpr int FLAG_POS = 9; - // autoMass ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); - - // dirtyFlags |= 1U << FLAG_POS; - //} - - //void SHRigidBodyComponent::SetMass(float newMass) noexcept - //{ - // static constexpr int FLAG_POS = 9; - - // if (newMass < 0.0f) - // return; - - // if (type != Type::DYNAMIC) - // { - // SHLOG_WARNING("Cannot set mass of a non-dynamic object {}", GetEID()) - // return; - // } - - // dirtyFlags |= 1U << FLAG_POS; - // mass = newMass; - - // // Turn off automass - // flags &= ~(1U << FLAG_POS); - //} - void SHRigidBodyComponent::SetDrag(float newDrag) noexcept { - static constexpr int FLAG_POS = 10; - - if (type != Type::DYNAMIC) - { - SHLOG_WARNING("Cannot set drag of a non-dynamic object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - drag = newDrag; + } void SHRigidBodyComponent::SetAngularDrag(float newAngularDrag) noexcept { - static constexpr int FLAG_POS = 11; - - if (type != Type::DYNAMIC) - { - SHLOG_WARNING("Cannot set angular drag of a non-dynamic object {}", GetEID()) - return; - } - - dirtyFlags |= 1U << FLAG_POS; - angularDrag = newAngularDrag; + } void SHRigidBodyComponent::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept { - - if (type == Type::STATIC) - { - SHLOG_WARNING("Cannot set linear velocity of a static object {}", GetEID()) - return; - } - - auto* physicsObject = system->GetPhysicsObject(GetEID()); - if (!physicsObject) - { - SHLOGV_ERROR("Unable to retrieve physics object of Entity {}", GetEID()) - return; - } - physicsObject->GetRigidBody()->setLinearVelocity(newLinearVelocity); + } void SHRigidBodyComponent::SetAngularVelocity(const SHVec3& newAngularVelocity) noexcept { - static constexpr int FLAG_POS = 13; - - if (type == Type::STATIC) - { - SHLOG_WARNING("Cannot set angular velocity of a static object {}", GetEID()) - return; - } - - auto* physicsObject = system->GetPhysicsObject(GetEID()); - if (!physicsObject) - { - SHLOGV_ERROR("Unable to retrieve physics object of Entity {}", GetEID()) - return; - } - physicsObject->GetRigidBody()->setAngularVelocity(newAngularVelocity); + } /*-----------------------------------------------------------------------------------*/ @@ -413,81 +206,57 @@ namespace SHADE void SHRigidBodyComponent::OnCreate() { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (!physicsSystem) - { - SHLOG_ERROR("Physics System does not exist to link with Physics Components!") - return; - } - - system = physicsSystem; - - // Sync with transform if one already exists - if (auto* transformComponent = SHComponentManager::GetComponent_s(GetEID()); transformComponent) - { - position = transformComponent->GetWorldPosition(); - orientation = transformComponent->GetWorldOrientation(); - } + } void SHRigidBodyComponent::AddForce(const SHVec3& force) const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->applyWorldForceAtCenterOfMass(force); + } void SHRigidBodyComponent::AddForceAtLocalPos(const SHVec3& force, const SHVec3& localPos) const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->applyWorldForceAtLocalPosition(force, localPos); + } void SHRigidBodyComponent::AddForceAtWorldPos(const SHVec3& force, const SHVec3& worldPos) const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->applyWorldForceAtWorldPosition(force, worldPos); + } void SHRigidBodyComponent::AddRelativeForce(const SHVec3& relativeForce) const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->applyLocalForceAtCenterOfMass(relativeForce); + } void SHRigidBodyComponent::AddRelativeForceAtLocalPos(const SHVec3& relativeForce, const SHVec3& localPos) const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->applyLocalForceAtLocalPosition(relativeForce, localPos); + } void SHRigidBodyComponent::AddRelativeForceAtWorldPos(const SHVec3& relativeForce, const SHVec3& worldPos) const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->applyLocalForceAtWorldPosition(relativeForce, worldPos); + } void SHRigidBodyComponent::AddTorque(const SHVec3& torque) const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->applyWorldTorque(torque); + } void SHRigidBodyComponent::AddRelativeTorque(const SHVec3& relativeTorque) const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->applyLocalTorque(relativeTorque); + } void SHRigidBodyComponent::ClearForces() const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->resetForce(); + } void SHRigidBodyComponent::ClearTorque() const noexcept { - if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) - physicsObject->GetRigidBody()->resetTorque(); + } } // namespace SHADE @@ -512,7 +281,6 @@ RTTR_REGISTRATION .property("Use Gravity" , &SHRigidBodyComponent::IsGravityEnabled , &SHRigidBodyComponent::SetGravityEnabled ) .property("Interpolate" , &SHRigidBodyComponent::IsInterpolating , &SHRigidBodyComponent::SetInterpolate ) .property("Sleeping Enabled" , &SHRigidBodyComponent::IsAllowedToSleep , &SHRigidBodyComponent::SetIsAllowedToSleep) - //.property("Auto Mass" , &SHRigidBodyComponent::GetAutoMass , &SHRigidBodyComponent::SetAutoMass ) .property("Freeze Position X" , &SHRigidBodyComponent::GetFreezePositionX , &SHRigidBodyComponent::SetFreezePositionX ) .property("Freeze Position Y" , &SHRigidBodyComponent::GetFreezePositionY , &SHRigidBodyComponent::SetFreezePositionY ) .property("Freeze Position Z" , &SHRigidBodyComponent::GetFreezePositionZ , &SHRigidBodyComponent::SetFreezePositionZ ) diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h index 532b3312..021743da 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h @@ -82,8 +82,6 @@ namespace SHADE [[nodiscard]] bool GetFreezeRotationY () const noexcept; [[nodiscard]] bool GetFreezeRotationZ () const noexcept; - //[[nodiscard]] bool GetAutoMass () const noexcept; - [[nodiscard]] float GetMass () const noexcept; [[nodiscard]] float GetDrag () const noexcept; [[nodiscard]] float GetAngularDrag () const noexcept; @@ -93,9 +91,9 @@ namespace SHADE [[nodiscard]] SHVec3 GetLinearVelocity () const noexcept; [[nodiscard]] SHVec3 GetAngularVelocity () const noexcept; - [[nodiscard]] const SHVec3& GetPosition () const noexcept; - [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; - [[nodiscard]] SHVec3 GetRotation () const noexcept; + //[[nodiscard]] const SHVec3& GetPosition () const noexcept; + //[[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; + //[[nodiscard]] SHVec3 GetRotation () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ @@ -144,26 +142,6 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - static constexpr size_t NUM_FLAGS = 8; - static constexpr size_t NUM_DIRTY_FLAGS = 12; - - Type type; - - uint16_t flags; // 0 0 0 0 0 0 am ip aZ aY aX lZ lY lX slp g - uint16_t dirtyFlags; // 0 0 0 0 aD d m t aZ aY aX lZ lY lX slp g - - float mass; - float drag; - float angularDrag; - - SHVec3 linearVelocity; - SHVec3 angularVelocity; - - SHPhysicsSystem* system; - - SHVec3 position; - SHQuaternion orientation; - RTTR_ENABLE() }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp deleted file mode 100644 index 71b08831..00000000 --- a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsObject.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Physics Object. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHPhysicsObject.h" - -// Project Headers -#include "ECS_Base/Managers/SHSystemManager.h" -#include "ECS_Base/Managers/SHComponentManager.h" -#include "Scene/SHSceneManager.h" - - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject::SHPhysicsObject(EntityID eid, rp3d::PhysicsCommon* physicsFactory, rp3d::PhysicsWorld* physicsWorld) noexcept - : entityID { eid } - , collidersActive { true } - , factory { physicsFactory } - , world { physicsWorld } - , rp3dBody { nullptr } - { - // Implicitly create a static body. - - const auto* TRANSFORM = SHComponentManager::GetComponent(eid); - - const rp3d::Transform RP3D_TRANSFORM { TRANSFORM->GetWorldPosition(), TRANSFORM->GetWorldOrientation() }; - - rp3dBody = world->createRigidBody(RP3D_TRANSFORM); - rp3dBody->setType(rp3d::BodyType::STATIC); - } - - SHPhysicsObject::~SHPhysicsObject() noexcept - { - factory = nullptr; - world = nullptr; - rp3dBody = nullptr; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHVec3 SHPhysicsObject::GetPosition() const noexcept - { - return rp3dBody->getTransform().getPosition(); - } - - SHQuaternion SHPhysicsObject::GetOrientation() const noexcept - { - return rp3dBody->getTransform().getOrientation(); - } - - SHVec3 SHPhysicsObject::GetRotation() const noexcept - { - return SHQuaternion{ rp3dBody->getTransform().getOrientation() }.ToEuler(); - } - - rp3d::CollisionBody* SHPhysicsObject::GetCollisionBody() const noexcept - { - return rp3dBody; - } - - rp3d::RigidBody* SHPhysicsObject::GetRigidBody() const noexcept - { - return rp3dBody; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsObject::SetStaticBody() const noexcept - { - if (!rp3dBody) - return; - - rp3dBody->setType(rp3d::BodyType::STATIC); - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - int SHPhysicsObject::AddCollisionShape(int index) - { - // Get collider component - auto* colliderComponent = SHComponentManager::GetComponent_s(entityID); - if (!colliderComponent) - { - SHLOGV_ERROR("Unable to add Collision Shape to Entity {} due to Missing Collider Component!", entityID) - return -1; - } - - auto& collisionShape = colliderComponent->GetCollisionShape(index); - switch (collisionShape.GetType()) - { - // TODO(Diren): Add more collider shapes - - case SHCollisionShape::Type::BOX: - { - addBoxShape(collisionShape); - break; - } - - case SHCollisionShape::Type::SPHERE: - { - addSphereShape(collisionShape); - break; - } - default: break; - } - - auto* rp3dCollider = rp3dBody->getCollider(rp3dBody->getNbColliders() - 1); - syncColliderProperties(collisionShape, rp3dCollider); - - if (rp3dBody->getType() == rp3d::BodyType::DYNAMIC) - { - rp3dBody->updateMassPropertiesFromColliders(); - - if (auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); rigidBodyComponent) - rigidBodyComponent->mass = rp3dBody->getMass(); - } - - return index; - } - - void SHPhysicsObject::RemoveCollisionShape(int index) - { - const int NUM_COLLIDERS = static_cast(rp3dBody->getNbColliders()); - if (NUM_COLLIDERS == 0) - return; - - if (index < 0 || index >= NUM_COLLIDERS) - throw std::invalid_argument("Index out of range!"); - - auto* collider = rp3dBody->getCollider(index); - rp3dBody->removeCollider(collider); - - if (rp3dBody->getType() == rp3d::BodyType::DYNAMIC) - { - rp3dBody->updateMassPropertiesFromColliders(); - - auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); - if (rigidBodyComponent) - rigidBodyComponent->mass = rp3dBody->getMass(); - } - } - - void SHPhysicsObject::RemoveAllCollisionShapes() const noexcept - { - int numColliders = static_cast(rp3dBody->getNbColliders()); - if (numColliders == 0) - return; - - while (numColliders - 1 >= 0) - { - auto* collider = rp3dBody->getCollider(numColliders - 1); - rp3dBody->removeCollider(collider); - - --numColliders; - } - } - - void SHPhysicsObject::SyncRigidBody(SHRigidBodyComponent& component) const noexcept - { - // This state is synced in the pre-update routine - if (!rp3dBody->isActive()) - return; - - if (component.dirtyFlags == 0) - return; - - for (size_t i = 0; i < SHRigidBodyComponent::NUM_DIRTY_FLAGS; ++i) - { - if (const bool IS_DIRTY = component.dirtyFlags & (1U << i); IS_DIRTY) - { - switch (i) - { - case 0: // Gravity - { - const bool IS_ENABLED = component.flags & (1U << i); - rp3dBody->enableGravity(IS_ENABLED); - - break; - } - case 1: // Sleeping - { - const bool IS_ENABLED = component.flags & (1U << i); - rp3dBody->setIsAllowedToSleep(IS_ENABLED); - - break; - } - case 2: // Lock Position X - { - const bool IS_ENABLED = component.flags & (1U << i); - - auto positionLock = rp3dBody->getLinearLockAxisFactor(); - positionLock.x = IS_ENABLED ? 0.0f : 1.0f; - rp3dBody->setLinearLockAxisFactor(positionLock); - - break; - } - case 3: // Lock Position Y - { - const bool IS_ENABLED = component.flags & (1U << i); - - auto positionLock = rp3dBody->getLinearLockAxisFactor(); - positionLock.y = IS_ENABLED ? 0.0f : 1.0f; - rp3dBody->setLinearLockAxisFactor(positionLock); - - break; - } - case 4: // Lock Position Z - { - const bool IS_ENABLED = component.flags & (1U << i); - - auto positionLock = rp3dBody->getLinearLockAxisFactor(); - positionLock.z = IS_ENABLED ? 0.0f : 1.0f; - rp3dBody->setLinearLockAxisFactor(positionLock); - - break; - } - case 5: // Lock Rotation X - { - const bool IS_ENABLED = component.flags & (1U << i); - - auto rotationLock = rp3dBody->getAngularLockAxisFactor(); - rotationLock.x = IS_ENABLED ? 0.0f : 1.0f; - rp3dBody->setAngularLockAxisFactor(rotationLock); - - break; - } - case 6: // Lock Rotation Y - { - const bool IS_ENABLED = component.flags & (1U << i); - - auto rotationLock = rp3dBody->getAngularLockAxisFactor(); - rotationLock.y = IS_ENABLED ? 0.0f : 1.0f; - rp3dBody->setAngularLockAxisFactor(rotationLock); - - break; - } - case 7: // Lock Rotation Z - { - const bool IS_ENABLED = component.flags & (1U << i); - - auto rotationLock = rp3dBody->getAngularLockAxisFactor(); - rotationLock.z = IS_ENABLED ? 0.0f : 1.0f; - rp3dBody->setAngularLockAxisFactor(rotationLock); - - break; - } - case 8: // Type - { - rp3dBody->setType(static_cast(component.type)); - - break; - } - case 9: // Mass - { - //rp3dBody->setMass(component.mass); - - //if (component.GetAutoMass()) - //{ - // rp3dBody->updateMassPropertiesFromColliders(); - // component.mass = rp3dBody->getMass(); - //} - //else - //{ - // rp3dBody->setMass(component.mass); - // rp3dBody->updateLocalCenterOfMassFromColliders(); - // rp3dBody->updateLocalInertiaTensorFromColliders(); - //} - - break; - } - case 10: // Drag - { - rp3dBody->setLinearDamping(component.drag); - - break; - } - case 11: // Angular Drag - { - rp3dBody->setAngularDamping(component.angularDrag); - - break; - } - default: break; - } - } - } - - component.dirtyFlags = 0; - } - - void SHPhysicsObject::SyncColliders(SHColliderComponent& component) const noexcept - { - // This state is synced in the pre-update routine - if (!rp3dBody->isActive()) - return; - - const int NUM_SHAPES = static_cast(rp3dBody->getNbColliders()); - for (int i = 0; i < NUM_SHAPES; ++i) - { - auto& collisionShape = component.collisionShapes[i]; - - if (!collisionShape.dirty) - continue; - - switch (collisionShape.GetType()) - { - case SHCollisionShape::Type::BOX: syncBoxShape(i, collisionShape); break; - case SHCollisionShape::Type::SPHERE: syncSphereShape(i, collisionShape); break; - default: break; - } - - auto* rp3dCollider = rp3dBody->getCollider(i); - syncColliderProperties(collisionShape, rp3dCollider); - - collisionShape.dirty = false; - } - - // Set rigidbody mass if dynamic - auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); - if (rigidBodyComponent) - { - // This is generally expensive, will be optimised in the future with my own engine. - rp3dBody->updateMassPropertiesFromColliders(); - rigidBodyComponent->mass = rp3dBody->getMass(); - } - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsObject::syncColliderProperties(const SHCollisionShape& collisionShape, rp3d::Collider* rp3dCollider) const noexcept - { - rp3dCollider->setIsTrigger(collisionShape.IsTrigger()); - - auto& rp3dMaterial = rp3dCollider->getMaterial(); - - rp3dMaterial.setFrictionCoefficient(collisionShape.GetFriction()); - rp3dMaterial.setBounciness(collisionShape.GetBounciness()); - rp3dMaterial.setMassDensity(collisionShape.GetDensity()); - - const unsigned short MASK_BITS = collisionShape.GetCollisionTag(); - rp3dCollider->setCollisionCategoryBits(MASK_BITS); - rp3dCollider->setCollideWithMaskBits(MASK_BITS); - } - - void SHPhysicsObject::addBoxShape(const SHCollisionShape& boxShape) const noexcept - { - const rp3d::Transform OFFSETS - { - boxShape.GetPositionOffset() - , boxShape.GetRotationOffset() - }; - - const auto* BOX = reinterpret_cast(boxShape.GetShape()); - rp3d::BoxShape* newBox = factory->createBoxShape(BOX->GetWorldExtents()); - - rp3dBody->addCollider(newBox, OFFSETS); - } - - void SHPhysicsObject::syncBoxShape(int index, const SHCollisionShape& boxShape) const noexcept - { - const auto* BOX = reinterpret_cast(boxShape.GetShape()); - - auto* rp3dCollider = rp3dBody->getCollider(index); - auto* rp3dBox = reinterpret_cast(rp3dCollider->getCollisionShape()); - - const rp3d::Transform OFFSETS - { - boxShape.GetPositionOffset() - , boxShape.GetRotationOffset() - }; - - rp3dCollider->setIsTrigger(boxShape.IsTrigger()); - rp3dCollider->setLocalToBodyTransform(OFFSETS); - - rp3dBox->setHalfExtents(BOX->GetWorldExtents()); - } - - void SHPhysicsObject::addSphereShape(const SHCollisionShape& sphereShape) const noexcept - { - const rp3d::Transform OFFSETS - { - sphereShape.GetPositionOffset() - , sphereShape.GetRotationOffset() - }; - - const auto* SPHERE = reinterpret_cast(sphereShape.GetShape()); - rp3d::SphereShape* newSphere = factory->createSphereShape(SPHERE->GetWorldRadius()); - - rp3dBody->addCollider(newSphere, OFFSETS); - } - - void SHPhysicsObject::syncSphereShape(int index, const SHCollisionShape& sphereShape) const noexcept - { - const auto* SPHERE = reinterpret_cast(sphereShape.GetShape()); - - auto* rp3dCollider = rp3dBody->getCollider(index); - auto* rp3dSphere = reinterpret_cast(rp3dCollider->getCollisionShape()); - - const rp3d::Transform OFFSETS - { - sphereShape.GetPositionOffset() - , sphereShape.GetRotationOffset() - }; - - rp3dCollider->setIsTrigger(sphereShape.IsTrigger()); - rp3dCollider->setLocalToBodyTransform(OFFSETS); - - rp3dSphere->setRadius(SPHERE->GetWorldRadius()); - } -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.h b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.h deleted file mode 100644 index c572ca2e..00000000 --- a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.h +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsObject.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Physics Object. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "Math/Transform/SHTransformComponent.h" -#include "Physics/Interface/SHRigidBodyComponent.h" -#include "Physics/Interface/SHColliderComponent.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - class SH_API SHPhysicsObject - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHPhysicsSystem; - friend class SHPhysicsObjectManager; - - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsObject (EntityID eid, rp3d::PhysicsCommon* physicsFactory, rp3d::PhysicsWorld* physicsWorld) noexcept; - SHPhysicsObject (const SHPhysicsObject& rhs) noexcept = default; - SHPhysicsObject (SHPhysicsObject&& rhs) noexcept = default; - virtual ~SHPhysicsObject () noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsObject& operator=(const SHPhysicsObject& rhs) noexcept = default; - SHPhysicsObject& operator=(SHPhysicsObject&& rhs) noexcept = default; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] SHVec3 GetPosition () const noexcept; - [[nodiscard]] SHQuaternion GetOrientation () const noexcept; - [[nodiscard]] SHVec3 GetRotation () const noexcept; - - [[nodiscard]] rp3d::CollisionBody* GetCollisionBody () const noexcept; - [[nodiscard]] rp3d::RigidBody* GetRigidBody () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetStaticBody () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - int AddCollisionShape (int index); - void RemoveCollisionShape (int index); - void RemoveAllCollisionShapes () const noexcept; - - void SyncRigidBody (SHRigidBodyComponent& component) const noexcept; - void SyncColliders (SHColliderComponent& component) const noexcept; - - private: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - EntityID entityID; - bool collidersActive; // Only used to sync with SHADE components - - rp3d::PhysicsCommon* factory; - rp3d::PhysicsWorld* world; - - rp3d::RigidBody* rp3dBody; - rp3d::Transform prevTransform; // Cached transform for interpolation - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - void syncColliderProperties (const SHCollisionShape& collisionShape, rp3d::Collider* rp3dCollider) const noexcept; - - // Box Shapes - - void addBoxShape (const SHCollisionShape& boxShape) const noexcept; - void syncBoxShape (int index, const SHCollisionShape& boxShape) const noexcept; - - // Sphere Shapes - - void addSphereShape (const SHCollisionShape& sphereShape) const noexcept; - void syncSphereShape (int index, const SHCollisionShape& sphereShape) const noexcept; - }; -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp deleted file mode 100644 index 7c111a2d..00000000 --- a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsObjectManager.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Physics Object Manager. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHPhysicsObjectManager.h" - -// Project Headers -#include "ECS_Base/Managers/SHEntityManager.h" -#include "Tools/Utilities/SHUtilities.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Static Data Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObjectManager::CommandFunctionPtr SHPhysicsObjectManager::componentFunc[3][2] - { - addRigidBody , addCollider , addCollisionShape - , removeRigidBody , removeCollider , removeCollisionShape - }; - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsObjectManager::SetFactory(rp3d::PhysicsCommon& physicsFactory) noexcept - { - factory = &physicsFactory; - } - - void SHPhysicsObjectManager::SetWorld(rp3d::PhysicsWorld* physicsWorld) noexcept - { - world = physicsWorld; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject* SHPhysicsObjectManager::GetPhysicsObject(EntityID eid) noexcept - { - const auto it = physicsObjects.find(eid); - if (it == physicsObjects.end()) - return nullptr; - - return &it->second; - } - - const SHPhysicsObjectManager::PhysicsObjectEntityMap SHPhysicsObjectManager::GetPhysicsObjects() const noexcept - { - return physicsObjects; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsObjectManager::AddRigidBody(EntityID eid) noexcept - { - const QueueCommand NEW_QUEUE_COMMAND - { - .eid = eid - , .command = QueueCommand::Command::ADD - , .component = PhysicsComponents::RIGID_BODY - }; - - commandQueue.push(NEW_QUEUE_COMMAND); - } - - void SHPhysicsObjectManager::AddCollider(EntityID eid) noexcept - { - const QueueCommand NEW_QUEUE_COMMAND - { - .eid = eid - , .command = QueueCommand::Command::ADD - , .component = PhysicsComponents::COLLIDER - }; - - commandQueue.push(NEW_QUEUE_COMMAND); - } - - void SHPhysicsObjectManager::AddCollisionShape(EntityID eid, int shapeIndex) noexcept - { - const QueueCommand NEW_QUEUE_COMMAND - { - .eid = eid - , .command = QueueCommand::Command::ADD - , .component = PhysicsComponents::COLLISION_SHAPE - , .shapeIndex = shapeIndex - }; - - commandQueue.push(NEW_QUEUE_COMMAND); - } - - void SHPhysicsObjectManager::RemoveRigidBody(EntityID eid) noexcept - { - const QueueCommand NEW_QUEUE_COMMAND - { - .eid = eid - , .command = QueueCommand::Command::REMOVE - , .component = PhysicsComponents::RIGID_BODY - }; - - commandQueue.push(NEW_QUEUE_COMMAND); - } - - void SHPhysicsObjectManager::RemoveCollider(EntityID eid) noexcept - { - const QueueCommand NEW_QUEUE_COMMAND - { - .eid = eid - , .command = QueueCommand::Command::REMOVE - , .component = PhysicsComponents::COLLIDER - }; - - commandQueue.push(NEW_QUEUE_COMMAND); - } - - void SHPhysicsObjectManager::RemoveCollisionShape(EntityID eid, int shapeIndex) noexcept - { - const QueueCommand NEW_QUEUE_COMMAND - { - .eid = eid - , .command = QueueCommand::Command::REMOVE - , .component = PhysicsComponents::COLLISION_SHAPE - , .shapeIndex = shapeIndex - }; - - commandQueue.push(NEW_QUEUE_COMMAND); - } - - void SHPhysicsObjectManager::UpdateCommands() - { - if (commandQueue.empty()) - return; - - while (!commandQueue.empty()) - { - const QueueCommand COMMAND = commandQueue.front(); - commandQueue.pop(); - - // Check validity of command - if (COMMAND.command == QueueCommand::Command::INVALID || COMMAND.component == PhysicsComponents::INVALID) - continue; - - // Get physics components - const PhysicsComponentGroup COMPONENT_GROUP - { - .eid = COMMAND.eid - , .rigidBodyComponent = SHComponentManager::GetComponent_s(COMMAND.eid) - , .colliderComponent = SHComponentManager::GetComponent_s(COMMAND.eid) - }; - - // Delete any object that is missing both components - // We infer that a remove command has been pushed for these, but we will ignore those if both components have already been removed. - if (!COMPONENT_GROUP.rigidBodyComponent && !COMPONENT_GROUP.colliderComponent) - { - destroyPhysicsObject(COMMAND.eid); - wakeAllObjects(); - continue; - } - - // Find the physics Object. If none found and attempting to add, create an object. - SHPhysicsObject* physicsObject = GetPhysicsObject(COMMAND.eid); - if (!physicsObject && COMMAND.command == QueueCommand::Command::ADD) - physicsObject = createPhysicsObject(COMMAND.eid); - - componentFunc[SHUtilities::ConvertEnum(COMMAND.command)][SHUtilities::ConvertEnum(COMMAND.component)](COMMAND, physicsObject, COMPONENT_GROUP); - - // If any removal was done, wake all objects - if (COMMAND.command == QueueCommand::Command::REMOVE) - wakeAllObjects(); - } - } - - void SHPhysicsObjectManager::RemoveAllObjects() - { - // Destroy all objects and clear - for (auto& physicsObject : physicsObjects | std::views::values) - { - world->destroyRigidBody(physicsObject.GetRigidBody()); - physicsObject.rp3dBody = nullptr; - } - - physicsObjects.clear(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject* SHPhysicsObjectManager::createPhysicsObject(EntityID eid) noexcept - { - // Force transforms to sync - SHVec3 worldPos = SHVec3::Zero; - SHQuaternion worldRot = SHQuaternion::Identity; - - const SHTransformComponent* TRANSFORM = nullptr; - if (SHEntityManager::IsValidEID(eid)) - TRANSFORM = SHComponentManager::GetComponent_s(eid); - - if (TRANSFORM) - { - worldPos = TRANSFORM->GetWorldPosition(); - worldRot = TRANSFORM->GetWorldOrientation(); - } - - const rp3d::Transform RP3D_TRANSFORM{ worldPos, worldRot }; - - auto& newPhysicsObject = physicsObjects.emplace(eid, SHPhysicsObject{ eid, factory, world }).first->second; - newPhysicsObject.GetRigidBody()->setTransform(RP3D_TRANSFORM); - newPhysicsObject.prevTransform = RP3D_TRANSFORM; - - return &newPhysicsObject; - } - - void SHPhysicsObjectManager::destroyPhysicsObject(EntityID eid) noexcept - { - const auto ITER = physicsObjects.find(eid); - if (ITER == physicsObjects.end()) - { - // Assume the object has already been successfully destroyed - return; - } - - world->destroyRigidBody(ITER->second.GetRigidBody()); - ITER->second.rp3dBody = nullptr; - - physicsObjects.erase(eid); - } - - void SHPhysicsObjectManager::addRigidBody(const QueueCommand&, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup) - { - SHASSERT(physicsObject != nullptr, "Valid physics object required to add body!") - - if (!componentGroup.rigidBodyComponent) - { - SHLOG_ERROR("Entity {} is missing a Rigidbody Component. Unable to update physics object!", componentGroup.eid) - return; - } - - // A static rigid body is implicitly created on creation of a physics object. - // Nothing is needed here. - } - - void SHPhysicsObjectManager::addCollider(const QueueCommand&, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup) - { - SHASSERT(physicsObject != nullptr, "Valid physics object required to add collider!") - - if (!componentGroup.colliderComponent) - { - SHLOG_ERROR("Entity {} is missing a Rigidbody Component. Unable to update physics object!", componentGroup.eid) - return; - } - - //const int NUM_SHAPES = static_cast(componentGroup.colliderComponent->GetCollisionShapes().size()); - //for (int i = 0; i < NUM_SHAPES; ++i) - // physicsObject->AddCollisionShape(i); - - physicsObject->SyncColliders(*componentGroup.colliderComponent); - } - - void SHPhysicsObjectManager::removeRigidBody(const QueueCommand&, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup) - { - SHASSERT(physicsObject != nullptr, "Valid physics object required to remove body!") - - if (componentGroup.colliderComponent) - physicsObject->SetStaticBody(); - } - - void SHPhysicsObjectManager::removeCollider(const QueueCommand&, SHPhysicsObject* physicsObject, const PhysicsComponentGroup&) - { - SHASSERT(physicsObject != nullptr, "Valid physics object required to remove collider!") - - physicsObject->RemoveAllCollisionShapes(); - } - - void SHPhysicsObjectManager::addCollisionShape(const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup&) - { - SHASSERT(physicsObject != nullptr, "Valid physics object required to add collision shape!") - - physicsObject->AddCollisionShape(command.shapeIndex); - } - - void SHPhysicsObjectManager::removeCollisionShape(const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup&) - { - SHASSERT(physicsObject != nullptr, "Valid physics object required to remove collision shape!") - - physicsObject->RemoveCollisionShape(command.shapeIndex); - } - - void SHPhysicsObjectManager::wakeAllObjects() noexcept - { - for (auto& physicsObject : physicsObjects | std::views::values) - physicsObject.GetRigidBody()->setIsSleeping(false); - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h deleted file mode 100644 index f41c62ad..00000000 --- a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsObjectManager.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Physics Object Manager. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include -#include - -#include - -// Project Headers -#include "SHPhysicsObject.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - class SH_API SHPhysicsObjectManager - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHPhysicsSystem; - - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - using PhysicsObjectEntityMap = std::unordered_map; - - public: - - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - enum class PhysicsComponents - { - RIGID_BODY - , COLLIDER - , COLLISION_SHAPE - - , TOTAL - , INVALID = -1 - }; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsObjectManager () = default; - ~SHPhysicsObjectManager () = default; - - SHPhysicsObjectManager (const SHPhysicsObjectManager&) = delete; - SHPhysicsObjectManager (SHPhysicsObjectManager&&) = delete; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsObjectManager& operator=(const SHPhysicsObjectManager&) = delete; - SHPhysicsObjectManager& operator=(SHPhysicsObjectManager&&) = delete; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] SHPhysicsObject* GetPhysicsObject (EntityID eid) noexcept; - [[nodiscard]] const PhysicsObjectEntityMap GetPhysicsObjects () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetFactory (rp3d::PhysicsCommon& physicsFactory) noexcept; - void SetWorld (rp3d::PhysicsWorld* physicsWorld) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - void AddRigidBody (EntityID eid) noexcept; - void AddCollider (EntityID eid) noexcept; - void AddCollisionShape (EntityID eid, int shapeIndex) noexcept; - - void RemoveRigidBody (EntityID eid) noexcept; - void RemoveCollider (EntityID eid) noexcept; - void RemoveCollisionShape (EntityID eid, int shapeIndex) noexcept; - - void UpdateCommands (); - void RemoveAllObjects (); - - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - struct QueueCommand - { - /*-------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-------------------------------------------------------------------------------*/ - - enum class Command - { - ADD - , REMOVE - - , INVALID = -1 - }; - - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - EntityID eid = MAX_EID; - Command command = Command::INVALID; - PhysicsComponents component = PhysicsComponents::INVALID; - int shapeIndex = -1; // Only used when adding & removing collision shapes - }; - - struct PhysicsComponentGroup - { - public: - - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - - EntityID eid = MAX_EID; - SHRigidBodyComponent* rigidBodyComponent = nullptr; - SHColliderComponent* colliderComponent = nullptr; - }; - - using CommandFunctionPtr = void(*)(const QueueCommand&, SHPhysicsObject*, const PhysicsComponentGroup&); - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static CommandFunctionPtr componentFunc[3][2]; // 3 components, 2 commands - - rp3d::PhysicsCommon* factory = nullptr; - rp3d::PhysicsWorld* world = nullptr; - - PhysicsObjectEntityMap physicsObjects; - std::queue commandQueue; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsObject* createPhysicsObject (EntityID eid) noexcept; - void destroyPhysicsObject (EntityID eid) noexcept; - - void wakeAllObjects () noexcept; - - static void addRigidBody (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); - static void addCollider (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); - static void removeRigidBody (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); - static void removeCollider (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); - - static void addCollisionShape (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); - static void removeCollisionShape (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); - - - }; - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp deleted file mode 100644 index 1ca7ae39..00000000 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsDebugDrawSystem.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Physics Debug Draw System - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHPhysicsDebugDrawSystem.h" - -// Project Headers -#include "ECS_Base/Managers/SHSystemManager.h" -#include "Editor/SHEditor.h" -#include "Scene/SHSceneManager.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Static Data Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHPhysicsDebugDrawSystem::DebugDrawFunction SHPhysicsDebugDrawSystem::drawFunctions[NUM_FLAGS] = - { - drawColliders - , drawColliderAABBs - , drawBroadPhaseAABBs - , drawContactPoints - , drawContactNormals - , drawRaycasts - }; - - SHVec3 SHPhysicsDebugDrawSystem::boxVertices[NUM_BOX_VERTICES]; - - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsDebugDrawSystem::SHPhysicsDebugDrawSystem() noexcept - : debugDrawFlags { 0 } - , physicsSystem { nullptr } - { - debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER)] = SHColour::GREEN; - debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER_AABB)] = SHColour::YELLOW; - debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::BROAD_PHASE_AABB)] = SHColour::CYAN; - debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_POINTS)] = SHColour::RED; - debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_NORMALS)] = SHColour::RED; - debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::RAYCASTS)] = SHColour::ORANGE; - } - - SHPhysicsDebugDrawSystem::PhysicsDebugDrawRoutine::PhysicsDebugDrawRoutine() - : SHSystemRoutine { "Physics Debug Draw", true } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHPhysicsDebugDrawSystem::GetDebugDrawFlag(DebugDrawFlags flag) const noexcept - { - const auto INT_FLAG = SHUtilities::ConvertEnum(flag); - if (INT_FLAG < 0 || INT_FLAG >= NUM_FLAGS) - { - SHLOG_ERROR("Invalid Debug Draw Flag Passed {} in. Unable to get debug draw state!", INT_FLAG) - return false; - } - - return debugDrawFlags & 1U << SHUtilities::ConvertEnum(flag); - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsDebugDrawSystem::SetDebugDrawFlag(DebugDrawFlags flag, bool value) noexcept - { - const auto INT_FLAG = SHUtilities::ConvertEnum(flag); - if (INT_FLAG < 0 || INT_FLAG >= NUM_FLAGS) - { - SHLOG_ERROR("Invalid Debug Draw Flag Passed {} in. Unable to set debug draw state!", INT_FLAG) - return; - } - - value ? (debugDrawFlags |= 1U << INT_FLAG) : (debugDrawFlags &= ~(1U << INT_FLAG)); - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsDebugDrawSystem::Init() - { - SystemFamily::GetID(); - - SHASSERT(physicsSystem == nullptr, "Non-existent physics system attached to the physics debug draw system!") - physicsSystem = SHSystemManager::GetSystem(); - - // Generate shapes - generateBox(); - } - - void SHPhysicsDebugDrawSystem::Exit() - { - physicsSystem = nullptr; - } - - void SHPhysicsDebugDrawSystem::PhysicsDebugDrawRoutine::Execute(double) noexcept - { - auto* system = reinterpret_cast(GetSystem()); - - auto* debugDrawSystem = SHSystemManager::GetSystem(); - if (debugDrawSystem == nullptr) - { - SHLOG_ERROR("Unable to get a debug draw system for Physics Debug Drawing!") - return; - } - - rp3d::DebugRenderer* rp3dRenderer = nullptr; - if (system->physicsSystem->worldState.world) - rp3dRenderer = &system->physicsSystem->worldState.world->getDebugRenderer(); - - for (int i = 0; i < SHUtilities::ConvertEnum(DebugDrawFlags::NUM_FLAGS); ++i) - { - const bool DRAW = (system->debugDrawFlags & (1U << i)) > 0; - if (DRAW) - { - drawFunctions[i](debugDrawSystem, rp3dRenderer); - } - else - { - if (rp3dRenderer && (i == 3 || i == 4)) - { - rp3dRenderer->setIsDebugItemDisplayed(reactphysics3d::DebugRenderer::DebugItem::CONTACT_POINT, false); - rp3dRenderer->setIsDebugItemDisplayed(reactphysics3d::DebugRenderer::DebugItem::CONTACT_NORMAL, false); - } - } - - } - - // Automatically clear the container of raycasts despite debug drawing state - // TODO(Diren): Move this somewhere else - system->physicsSystem->raycaster.ClearFrame(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsDebugDrawSystem::drawColliders(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept - { - const auto& COLLIDER_SET = SHComponentManager::GetDense(); - for (const auto& COLLIDER : COLLIDER_SET) - { - // Skip inactive colliders - if (!SHSceneManager::CheckNodeAndComponentsActive(COLLIDER.GetEID())) - continue; - - for (auto& collisionShape : COLLIDER.GetCollisionShapes()) - { - switch (collisionShape.GetType()) - { - case SHCollisionShape::Type::BOX: debugDrawBox(debugRenderer, COLLIDER, collisionShape); break; - case SHCollisionShape::Type::SPHERE: debugDrawSphere(debugRenderer, COLLIDER, collisionShape); break; - default: break; - } - } - } - } - - void SHPhysicsDebugDrawSystem::drawColliderAABBs(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept - { - - } - - void SHPhysicsDebugDrawSystem::drawBroadPhaseAABBs(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept - { - - } - - void SHPhysicsDebugDrawSystem::drawContactPoints(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept - { - #ifdef SHEDITOR - - const auto* EDITOR = SHSystemManager::GetSystem(); - if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) - { - rp3dRenderer->setIsDebugItemDisplayed(rp3d::DebugRenderer::DebugItem::CONTACT_POINT, true); - const int NUM_TRIS = static_cast(rp3dRenderer->getNbTriangles()); - if (NUM_TRIS == 0) - return; - - const auto& TRI_ARRAY = rp3dRenderer->getTrianglesArray(); - for (int i = 0; i < NUM_TRIS; ++i) - debugRenderer->DrawTri(SHColour::RED, TRI_ARRAY[i].point1, TRI_ARRAY[i].point2, TRI_ARRAY[i].point3); - } - - #else - - rp3dRenderer->setIsDebugItemDisplayed(rp3d::DebugRenderer::DebugItem::CONTACT_POINT, true); - const int NUM_TRIS = static_cast(rp3dRenderer->getNbTriangles()); - if (NUM_TRIS == 0) - return; - - const auto& TRI_ARRAY = rp3dRenderer->getTrianglesArray(); - for (int i = 0; i < NUM_TRIS; ++i) - debugRenderer->DrawTri(SHColour::RED, TRI_ARRAY[i].point1, TRI_ARRAY[i].point2, TRI_ARRAY[i].point3); - - #endif - } - - void SHPhysicsDebugDrawSystem::drawContactNormals(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept - { - #ifdef SHEDITOR - const auto* EDITOR = SHSystemManager::GetSystem(); - if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) - { - rp3dRenderer->setIsDebugItemDisplayed(rp3d::DebugRenderer::DebugItem::CONTACT_NORMAL, true); - const int NUM_LINES = static_cast(rp3dRenderer->getNbLines()); - if (NUM_LINES == 0) - return; - - const auto& LINE_ARRAY = rp3dRenderer->getLinesArray(); - for (int i = 0; i < NUM_LINES; ++i) - debugRenderer->DrawLine(SHColour::RED, LINE_ARRAY[i].point1, LINE_ARRAY[i].point2); - } - - #else - - rp3dRenderer->setIsDebugItemDisplayed(rp3d::DebugRenderer::DebugItem::CONTACT_NORMAL, true); - const int NUM_LINES = static_cast(rp3dRenderer->getNbLines()); - if (NUM_LINES == 0) - return; - - const auto& LINE_ARRAY = rp3dRenderer->getLinesArray(); - for (int i = 0; i < NUM_LINES; ++i) - debugRenderer->DrawLine(SHColour::RED, LINE_ARRAY[i].point1, LINE_ARRAY[i].point2); - - #endif - } - - void SHPhysicsDebugDrawSystem::drawRaycasts(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept - { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (!physicsSystem) - { - SHLOG_ERROR("Unable to retrieve physics system for debug drawing raycasts!") - return; - } - - const SHColour& RAY_COLOUR = SHColour::ORANGE; - - // Draw all raycast pairs - for (const auto& [ray, raycastResult] : physicsSystem->raycaster.GetRaycasts()) - { - // If infinity, it is an infinite raycast. If not, render the distance in raycastResult. - // Ignore the hit variable as it will always correspond to the length of the raycast, hit or miss. - const float RENDER_DIST = raycastResult.distance == std::numeric_limits::infinity() ? SHRay::MAX_RAYCAST_DIST : raycastResult.distance; - const SHVec3 END_POS = ray.position + (ray.direction * RENDER_DIST); - - debugRenderer->DrawLine(RAY_COLOUR, ray.position, END_POS); - } - } - - - void SHPhysicsDebugDrawSystem::generateBox() noexcept - { - boxVertices[0] = { 0.5f, 0.5f, -0.5f }; // TOP_RIGHT_BACK - boxVertices[1] = { -0.5f, 0.5f, -0.5f }; // TOP_LEFT_BACK - boxVertices[2] = { 0.5f, -0.5f, -0.5f }; // BTM_RIGHT_BACK - boxVertices[3] = { -0.5f, -0.5f, -0.5f }; // BTM_LEFT_BACK - boxVertices[4] = { 0.5f, 0.5f, 0.5f }; // TOP_RIGHT_FRONT - boxVertices[5] = { -0.5f, 0.5f, 0.5f }; // TOP_LEFT_FRONT - boxVertices[6] = { 0.5f, -0.5f, 0.5f }; // BTM_RIGHT_FRONT - boxVertices[7] = { -0.5f, -0.5f, 0.5f }; // BTM_LEFT_FRONT - } - - void SHPhysicsDebugDrawSystem::debugDrawBox(SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept - { - auto* BOX = reinterpret_cast(collisionShape.GetShape()); - - // Calculate final position & orientation - const SHVec3 COLLIDER_POS = colliderComponent.GetPosition(); - const SHVec3 BOX_POS = collisionShape.GetPositionOffset(); - const SHQuaternion COLLIDER_ROT = colliderComponent.GetOrientation(); - const SHQuaternion BOX_ROT = SHQuaternion::FromEuler(collisionShape.GetRotationOffset()); - - - const SHMatrix COLLIDER_TR = SHMatrix::Rotate(COLLIDER_ROT) * SHMatrix::Translate(COLLIDER_POS); - const SHMatrix BOX_TRS = SHMatrix::Scale(BOX->GetWorldExtents() * 2.0f) * SHMatrix::Rotate(BOX_ROT) * SHMatrix::Translate(BOX_POS); - - const SHMatrix FINAL_TRS = BOX_TRS * COLLIDER_TR; - - const SHColour COLLIDER_COLOUR = collisionShape.IsTrigger() ? SHColour::PURPLE : SHColour::GREEN; - - std::array transformedVertices; - for (uint32_t i = 0; i < NUM_BOX_VERTICES / 2; ++i) - { - const uint32_t IDX1 = i; - const uint32_t IDX2 = i + NUM_BOX_VERTICES / 2; - - transformedVertices[IDX1] = SHVec3::Transform(boxVertices[IDX1], FINAL_TRS); - transformedVertices[IDX2] = SHVec3::Transform(boxVertices[IDX2], FINAL_TRS); - - // Draw 4 line to connect the quads - debugRenderer->DrawLine(COLLIDER_COLOUR, transformedVertices[IDX1], transformedVertices[IDX2]); - } - - // A, B, C, D - std::array backQuad { transformedVertices[0], transformedVertices[1], transformedVertices[3], transformedVertices[2] }; - debugRenderer->DrawPoly(COLLIDER_COLOUR, backQuad.begin(), backQuad.end()); - - // E, F, G, H - std::array frontQuad { transformedVertices[4], transformedVertices[5], transformedVertices[7], transformedVertices[6] }; - debugRenderer->DrawPoly(COLLIDER_COLOUR, frontQuad.begin(), frontQuad.end()); - } - - void SHPhysicsDebugDrawSystem::debugDrawSphere(SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept - { - auto* SPHERE = reinterpret_cast(collisionShape.GetShape()); - - const SHColour COLLIDER_COLOUR = collisionShape.IsTrigger() ? SHColour::PURPLE : SHColour::GREEN; - - // Calculate final position & orientation - const SHQuaternion FINAL_ROT = colliderComponent.GetOrientation() * SHQuaternion::FromEuler(collisionShape.GetRotationOffset()); - const SHMatrix TR = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(colliderComponent.GetPosition()); - - debugRenderer->DrawSphere(COLLIDER_COLOUR, SHVec3::Transform(collisionShape.GetPositionOffset(), TR), SPHERE->GetWorldRadius()); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h deleted file mode 100644 index dc703092..00000000 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ /dev/null @@ -1,138 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsDebugDrawSystem.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for the Physics Debug Draw System - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "ECS_Base/System/SHSystemRoutine.h" -#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" -#include "Math/SHColour.h" -#include "SHPhysicsSystem.h" -#include "Tools/Utilities/SHUtilities.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - class SH_API SHPhysicsDebugDrawSystem final : public SHSystem - { - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - enum class DebugDrawFlags - { - COLLIDER - , COLLIDER_AABB - , BROAD_PHASE_AABB - , CONTACT_POINTS - , CONTACT_NORMALS - , RAYCASTS - - , NUM_FLAGS - }; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsDebugDrawSystem() noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - bool GetDebugDrawFlag(DebugDrawFlags flag) const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetDebugDrawFlag(DebugDrawFlags flag, bool value) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - void Init() override; - void Exit() override; - - /*---------------------------------------------------------------------------------*/ - /* System Routines */ - /*---------------------------------------------------------------------------------*/ - - class SH_API PhysicsDebugDrawRoutine final : public SHSystemRoutine - { - public: - /*-------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-------------------------------------------------------------------------------*/ - - PhysicsDebugDrawRoutine(); - - /*-------------------------------------------------------------------------------*/ - /* Function Members */ - /*-------------------------------------------------------------------------------*/ - void Execute(double dt) noexcept override; - }; - - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - using DebugDrawFunction = void(*)(SHDebugDrawSystem*, rp3d::DebugRenderer*) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr int NUM_FLAGS = SHUtilities::ConvertEnum(DebugDrawFlags::NUM_FLAGS); - static const DebugDrawFunction drawFunctions[NUM_FLAGS]; - - // SHAPES INFO - - static constexpr size_t NUM_BOX_VERTICES = 8; - static SHVec3 boxVertices[NUM_BOX_VERTICES]; - - - uint8_t debugDrawFlags; - SHPhysicsSystem* physicsSystem; - SHColour debugColours[NUM_FLAGS]; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - // Generic Draw Functions - - static void drawColliders (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; - static void drawColliderAABBs (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; - static void drawBroadPhaseAABBs (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; - static void drawContactPoints (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; - static void drawContactNormals (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; - static void drawRaycasts (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; - - // Shape Generation Functions - - static void generateBox () noexcept; - - // Shape Draw Functions - - static void debugDrawBox (SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept; - static void debugDrawSphere (SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept; - }; - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 768c9b77..eacd6ac9 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -45,32 +45,6 @@ namespace SHADE return 1.0 / fixedDT; } - const SHPhysicsWorldState::WorldSettings& SHPhysicsSystem::GetWorldSettings() const noexcept - { - return worldState.settings; - } - - const std::vector& SHPhysicsSystem::GetAllCollisionInfo() const noexcept - { - return collisionListener.GetCollisionInfoContainer(); - } - - const std::vector& SHPhysicsSystem::GetAllTriggerInfo() const noexcept - { - return collisionListener.GetTriggerInfoContainer(); - } - - const SHPhysicsObject* SHPhysicsSystem::GetPhysicsObject(EntityID eid) noexcept - { - return objectManager.GetPhysicsObject(eid); - } - - - const SHPhysicsObjectManager::PhysicsObjectEntityMap& SHPhysicsSystem::GetPhysicsObjects() const noexcept - { - return objectManager.physicsObjects; - } - /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -80,12 +54,6 @@ namespace SHADE fixedDT = 1.0 / fixedUpdateRate; } - void SHPhysicsSystem::SetWorldSettings(const SHPhysicsWorldState::WorldSettings& settings) noexcept - { - worldState.settings = settings; - worldState.UpdateSettings(); - } - /*-----------------------------------------------------------------------------------*/ /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -96,41 +64,10 @@ namespace SHADE std::filesystem::path defaultCollisionTagNameFilePath { ASSET_ROOT }; defaultCollisionTagNameFilePath.append("CollisionTags.SHConfig"); SHCollisionTagMatrix::Init(defaultCollisionTagNameFilePath); - - // Link Physics Object Manager with System & Raycaster - objectManager.SetFactory(factory); - raycaster.SetObjectManager(&objectManager); - - // Link Collision Listener with System - collisionListener.BindToSystem(this); - - // Subscribe to component events - const std::shared_ptr ADD_COMPONENT_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::addPhysicsComponent) }; - const ReceiverPtr ADD_COMPONENT_RECEIVER_PTR = std::dynamic_pointer_cast(ADD_COMPONENT_RECEIVER); - SHEventManager::SubscribeTo(SH_COMPONENT_ADDED_EVENT, ADD_COMPONENT_RECEIVER_PTR); - - const std::shared_ptr REMOVE_COMPONENT_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::removePhysicsComponent) }; - const ReceiverPtr REMOVE_COMPONENT_RECEIVER_PTR = std::dynamic_pointer_cast(REMOVE_COMPONENT_RECEIVER); - SHEventManager::SubscribeTo(SH_COMPONENT_REMOVED_EVENT, REMOVE_COMPONENT_RECEIVER_PTR); - - #ifdef SHEDITOR - - // Subscribe to Editor State Change Events - const std::shared_ptr ON_PLAY_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::onPlay) }; - const ReceiverPtr ON_PLAY_RECEIVER_PTR = std::dynamic_pointer_cast(ON_PLAY_RECEIVER); - SHEventManager::SubscribeTo(SH_EDITOR_ON_PLAY_EVENT, ON_PLAY_RECEIVER_PTR); - - const std::shared_ptr ON_STOP_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::onStop) }; - const ReceiverPtr ON_STOP_RECEIVER_PTR = std::dynamic_pointer_cast(ON_STOP_RECEIVER); - SHEventManager::SubscribeTo(SH_EDITOR_ON_STOP_EVENT, ON_STOP_RECEIVER_PTR); - - #endif - } void SHPhysicsSystem::Exit() { - worldState.DestroyWorld(factory); // Write collision tag names to file std::filesystem::path defaultCollisionTagNameFilePath { ASSET_ROOT }; @@ -138,344 +75,13 @@ namespace SHADE SHCollisionTagMatrix::Exit(defaultCollisionTagNameFilePath); } - void SHPhysicsSystem::BuildScene(SHSceneGraph& sceneGraph) - { - static const auto BUILD_NEW_SCENE_PHYSICS_OBJECT = [&](SHSceneNode* node) - { - const EntityID EID = node->GetEntityID(); - - if (SHComponentManager::HasComponent(EID)) - objectManager.AddRigidBody(EID); - - if (SHComponentManager::HasComponent(EID)) - { - objectManager.AddCollider(EID); - - auto* COLLIDER = SHComponentManager::GetComponent(EID); - for (size_t i = 0; i < COLLIDER->GetCollisionShapes().size(); ++i) - objectManager.AddCollisionShape(EID, i); - } - - }; - - //////////////////////////////// - - // Destroy an existing world - if (worldState.world != nullptr) - { - objectManager.RemoveAllObjects(); - objectManager.SetWorld(nullptr); - - collisionListener.ClearContainers(); - raycaster.ClearFrame(); - - worldState.DestroyWorld(factory); - } - - worldState.CreateWorld(factory); -#ifdef _PUBLISH - worldState.world->setIsDebugRenderingEnabled(false); -#else - worldState.world->setIsDebugRenderingEnabled(true); -#endif - - // Link Collision Listener & Raycaster - collisionListener.BindToWorld(worldState.world); - raycaster.BindToWorld(worldState.world); - - // Link with object manager & create all physics objects - objectManager.SetWorld(worldState.world); - - // When building a scene, clear the object manager command queue and build scene objects again. - // This is done to avoid duplicate adds. - while (!objectManager.commandQueue.empty()) - objectManager.commandQueue.pop(); - - sceneGraph.Traverse(BUILD_NEW_SCENE_PHYSICS_OBJECT); - objectManager.UpdateCommands(); - } - - void SHPhysicsSystem::ForceBuild(SHSceneGraph& sceneGraph) - { - // HACK: Band-aid fix. To be removed. - objectManager.UpdateCommands(); - } - - void SHPhysicsSystem::ForceUpdate() { - if (!worldState.world) - { - SHLOGV_ERROR("Unable to force update without a Physics world!") - return; - } - auto* scriptingSystem = SHSystemManager::GetSystem(); - if (scriptingSystem == nullptr) - { - SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!"); - } - - // Force the physics world to update once - if (scriptingSystem != nullptr) - scriptingSystem->ExecuteFixedUpdates(); - - worldState.world->update(static_cast(fixedDT)); - - // Sync transforms. No interpolation applied here - for (auto& [entityID, physicsObject] : objectManager.physicsObjects) - { - auto* transformComponent = SHComponentManager::GetComponent_s(entityID); - auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); - auto* colliderComponent = SHComponentManager::GetComponent_s(entityID); - - const auto& CURRENT_TF = physicsObject.GetRigidBody()->getTransform(); - const auto& RENDER_POS = CURRENT_TF.getPosition(); - const auto& RENDER_ROT = CURRENT_TF.getOrientation(); - - // Cache transform - physicsObject.prevTransform = CURRENT_TF; - - // Sync with physics components - if (rigidBodyComponent && SHSceneManager::CheckNodeAndComponentsActive(entityID)) - { - rigidBodyComponent->position = RENDER_POS; - rigidBodyComponent->orientation = RENDER_ROT; - } - - if (colliderComponent && SHSceneManager::CheckNodeAndComponentsActive(entityID)) - { - colliderComponent->position = RENDER_POS; - colliderComponent->orientation = RENDER_ROT; - } - - // Set transform for rendering - if (transformComponent) - { - transformComponent->SetWorldPosition(RENDER_POS); - transformComponent->SetWorldOrientation(RENDER_ROT); - } - } - } - - SHPhysicsRaycastResult SHPhysicsSystem::Raycast(const SHRay& ray, float distance, const SHCollisionTag& collisionTag) noexcept - { - return raycaster.Raycast(ray, distance, collisionTag); - } - - SHPhysicsRaycastResult SHPhysicsSystem::Linecast(const SHVec3& start, const SHVec3& end, const SHCollisionTag& collisionTag) noexcept - { - return raycaster.Linecast(start, end, collisionTag); - } - - SHPhysicsRaycastResult SHPhysicsSystem::ColliderRaycast(EntityID eid, const SHRay& ray, float distance) noexcept - { - return raycaster.ColliderRaycast(eid, ray, distance); - } - - SHPhysicsRaycastResult SHPhysicsSystem::ColliderRaycast(EntityID eid, int shapeIndex, const SHRay& ray, float distance) noexcept - { - return raycaster.ColliderRaycast(eid, shapeIndex, ray, distance); - } - - SHPhysicsRaycastResult SHPhysicsSystem::ColliderLinecast(EntityID eid, const SHVec3& start, const SHVec3& end) noexcept - { - return raycaster.ColliderLinecast(eid, start, end); - } - - SHPhysicsRaycastResult SHPhysicsSystem::ColliderLinecast(EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept - { - return raycaster.ColliderLinecast(eid, shapeIndex, start, end); - } - - void SHPhysicsSystem::AddCollisionShape(EntityID eid, int shapeIndex) - { - static const auto ADD_SHAPE = [&](EntityID entityID, int index) - { - objectManager.AddCollisionShape(entityID, index); - - auto* colliderComponent = SHComponentManager::GetComponent(entityID); - auto& collisionShape = colliderComponent->GetCollisionShape(index); - - const SHPhysicsColliderAddedEvent COLLIDER_ADDED_EVENT_DATA - { - .entityID = entityID - , .colliderType = collisionShape.GetType() - , .colliderIndex = index - }; - - SHEventManager::BroadcastEvent(COLLIDER_ADDED_EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT); - }; - - #ifdef SHEDITOR - - const auto* EDITOR = SHSystemManager::GetSystem(); - if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) - ADD_SHAPE(eid, shapeIndex); - - #else - - ADD_SHAPE(eid, shapeIndex); - - #endif - } - - void SHPhysicsSystem::RemoveCollisionShape(EntityID eid, int shapeIndex) - { - static const auto REMOVE_SHAPE = [&](EntityID entityID, int index) - { - objectManager.RemoveCollisionShape(entityID, index); - - const SHPhysicsColliderRemovedEvent COLLIDER_REMOVED_EVENT_DATA - { - .entityID = entityID - , .colliderIndex = index - }; - - SHEventManager::BroadcastEvent(COLLIDER_REMOVED_EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT); - }; - - #ifdef SHEDITOR - - const auto* EDITOR = SHSystemManager::GetSystem(); - if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) - REMOVE_SHAPE(eid, shapeIndex); - - #else - - REMOVE_SHAPE(eid, shapeIndex); - - #endif } /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHEventHandle SHPhysicsSystem::addPhysicsComponent(SHEventPtr addComponentEvent) noexcept - { - const auto& EVENT_DATA = reinterpret_cast*>(addComponentEvent.get()); - - static const auto RIGID_BODY_ID = ComponentFamily::GetID(); - static const auto COLLIDER_ID = ComponentFamily::GetID(); - - const auto ADDED_ID = EVENT_DATA->data->addedComponentType; - const bool IS_PHYSICS_COMPONENT = ADDED_ID == RIGID_BODY_ID || ADDED_ID == COLLIDER_ID; - if (IS_PHYSICS_COMPONENT) - { - const EntityID EID = EVENT_DATA->data->eid; - - // We only add tell the physics object manager to add a component if the scene is played - - #ifdef SHEDITOR - - const auto* EDITOR = SHSystemManager::GetSystem(); - - if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) - ADDED_ID == RIGID_BODY_ID ? objectManager.AddRigidBody(EID) : objectManager.AddCollider(EID); - - #else - - ADDED_ID == RIGID_BODY_ID ? objectManager.AddRigidBody(EID) : objectManager.AddCollider(EID); - - #endif - } - - return EVENT_DATA->handle; - } - - SHEventHandle SHPhysicsSystem::removePhysicsComponent(SHEventPtr removeComponentEvent) noexcept - { - const auto& EVENT_DATA = reinterpret_cast*>(removeComponentEvent.get()); - - static const auto RIGID_BODY_ID = ComponentFamily::GetID(); - static const auto COLLIDER_ID = ComponentFamily::GetID(); - - const auto REMOVED_ID = EVENT_DATA->data->removedComponentType; - const bool IS_PHYSICS_COMPONENT = REMOVED_ID == RIGID_BODY_ID || REMOVED_ID == COLLIDER_ID; - if (IS_PHYSICS_COMPONENT) - { - const EntityID EID = EVENT_DATA->data->eid; - - // We only add tell the physics object manager to remove a component if the scene is played - - #ifdef SHEDITOR - - const auto* EDITOR = SHSystemManager::GetSystem(); - - if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) - REMOVED_ID == RIGID_BODY_ID ? objectManager.RemoveRigidBody(EID) : objectManager.RemoveCollider(EID); - - #else - - REMOVED_ID == RIGID_BODY_ID ? objectManager.RemoveRigidBody(EID) : objectManager.RemoveCollider(EID); - - #endif - } - - return EVENT_DATA->handle; - } - - SHEventHandle SHPhysicsSystem::onPlay(SHEventPtr onPlayEvent) - { - static const auto BUILD_PHYSICS_OBJECT = [&](SHSceneNode* node) - { - const EntityID EID = node->GetEntityID(); - - if (SHComponentManager::HasComponent(EID)) - objectManager.AddRigidBody(EID); - - if (SHComponentManager::HasComponent(EID)) - { - objectManager.AddCollider(EID); - - auto* COLLIDER = SHComponentManager::GetComponent(EID); - for (size_t i = 0; i < COLLIDER->GetCollisionShapes().size(); ++i) - objectManager.AddCollisionShape(EID, i); - } - }; - - //////////////////////////////// - - // Create physics world - if (worldState.world != nullptr) - return onPlayEvent->handle; - - worldState.CreateWorld(factory); -#ifdef _PUBLISH - worldState.world->setIsDebugRenderingEnabled(false); -#else - worldState.world->setIsDebugRenderingEnabled(true); -#endif - - // Link Collision Listener & Raycaster - collisionListener.BindToWorld(worldState.world); - raycaster.BindToWorld(worldState.world); - - // Link with object manager & create all physics objects - objectManager.SetWorld(worldState.world); - - // Build scene - SHSceneManager::GetCurrentSceneGraph().Traverse(BUILD_PHYSICS_OBJECT); - objectManager.UpdateCommands(); - - return onPlayEvent->handle; - } - - SHEventHandle SHPhysicsSystem::onStop(SHEventPtr onStopEvent) - { - // Remove all physics objects - objectManager.RemoveAllObjects(); - objectManager.SetWorld(nullptr); - - // Clear all collision info - // Collision listener is automatically unbound when world is destroyed - collisionListener.ClearContainers(); - raycaster.ClearFrame(); - - // Destroy the world - worldState.DestroyWorld(factory); - - return onStopEvent->handle; - } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index 99db493e..3fe05c09 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -10,22 +10,13 @@ #pragma once -#include -#include - -// External Dependencies -#include - // Project Headers #include "ECS_Base/System/SHSystemRoutine.h" #include "ECS_Base/System/SHFixedSystemRoutine.h" #include "Math/Transform/SHTransformComponent.h" -#include "Physics/Collision/SHCollisionListener.h" -#include "Physics/Collision/SHPhysicsRaycaster.h" #include "Physics/Interface/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" -#include "Physics/PhysicsObject/SHPhysicsObjectManager.h" #include "Physics/SHPhysicsWorld.h" #include "Scene/SHSceneGraph.h" @@ -55,14 +46,9 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] double GetFixedUpdateRate () const noexcept; - [[nodiscard]] const SHPhysicsWorldState::WorldSettings& GetWorldSettings () const noexcept; + [[nodiscard]] double GetFixedUpdateRate () const noexcept; + [[nodiscard]] const SHPhysicsWorldState::WorldSettings& GetWorldSettings () const noexcept; - [[nodiscard]] const std::vector& GetAllCollisionInfo () const noexcept; - [[nodiscard]] const std::vector& GetAllTriggerInfo () const noexcept; - - [[nodiscard]] const SHPhysicsObject* GetPhysicsObject (EntityID eid) noexcept; - [[nodiscard]] const SHPhysicsObjectManager::PhysicsObjectEntityMap& GetPhysicsObjects () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ @@ -77,105 +63,8 @@ namespace SHADE void Init () override; void Exit () override; - void BuildScene (SHSceneGraph& sceneGraph); - void ForceBuild (SHSceneGraph& sceneGraph); void ForceUpdate (); - /** - * @brief Casts a ray into the world. - * @param ray The ray to cast. - * @param distance The distance to cast the ray. Defaults to infinity. - * @param collisionTag The collision tag to use for filtering the raycast. - * @return The result of the raycast. - */ - SHPhysicsRaycastResult Raycast - ( - const SHRay& ray - , float distance = std::numeric_limits::infinity() - , const SHCollisionTag& collisionTag = SHCollisionTag{} - ) noexcept; - - /** - * @brief Casts a bounded ray into the world. - * @param start The starting point of the ray. - * @param end The end point of the ray. - * @param collisionTag The collision tag to use for filtering the bounded raycast. - * @return The result of the raycast. - */ - SHPhysicsRaycastResult Linecast - ( - const SHVec3& start - , const SHVec3& end - , const SHCollisionTag& collisionTag = SHCollisionTag{} - ) noexcept; - - /** - * @brief Casts a ray at a body with colliders. - * @param eid The entity to cast to. - * @param ray The ray to cast. - * @param distance The distance to cast the ray. Defaults to infinity. - * @return The result of the raycast. - */ - SHPhysicsRaycastResult ColliderRaycast - ( - EntityID eid - , const SHRay& ray - , float distance = std::numeric_limits::infinity() - ) noexcept; - - /** - * @brief Casts a ray at a collider. - * @param eid The entity to cast to. - * @param shapeIndex The index of the collision shape. - * @param ray The ray to cast. - * @param distance The distance to cast the ray. Defaults to infinity. - * @return The result of the raycast. - */ - SHPhysicsRaycastResult ColliderRaycast - ( - EntityID eid - , int shapeIndex - , const SHRay& ray - , float distance = std::numeric_limits::infinity() - ) noexcept; - - /** - * @brief Casts a bounded ray at a body with colliders. - * @param eid - * @param start - * @param end - * @return The result of the raycast. - */ - SHPhysicsRaycastResult ColliderLinecast - ( - EntityID eid - , const SHVec3& start - , const SHVec3& end - ) noexcept; - - /** - * @brief - * @param eid - * @param shapeIndex - * @param start - * @param end - * @return The result of the raycast. - */ - SHPhysicsRaycastResult ColliderLinecast - ( - EntityID eid - , int shapeIndex - , const SHVec3& start - , const SHVec3& end - ) noexcept; - - // Specific Handling for Collision Shapes as they are not under the Component System. - // This is done as events need to be sent out. - // TODO(Diren): Consider using a static method through the ColliderComponent. - - void AddCollisionShape (EntityID eid, int shapeIndex); - void RemoveCollisionShape (EntityID eid, int shapeIndex); - /*---------------------------------------------------------------------------------*/ /* System Routines */ /*---------------------------------------------------------------------------------*/ @@ -200,17 +89,6 @@ namespace SHADE /* Function Members */ /*-------------------------------------------------------------------------------*/ - void syncRigidBodyActive (EntityID eid, SHPhysicsObject& physicsObject) const noexcept; - void syncColliderActive (EntityID eid, SHPhysicsObject& physicsObject) const noexcept; - static void syncOnPlay (EntityID eid, SHPhysicsObject& physicsObject) noexcept; - - static void preUpdateSyncTransform - ( - SHPhysicsObject& physicsObject - , SHTransformComponent* transformComponent - , SHRigidBodyComponent* rigidBodyComponent - , SHColliderComponent* colliderComponent - ) noexcept; }; class SH_API PhysicsFixedUpdate final : public SHFixedSystemRoutine @@ -250,14 +128,6 @@ namespace SHADE /* Function Members */ /*-------------------------------------------------------------------------------*/ - static void postUpdateSyncTransforms - ( - SHPhysicsObject& physicsObject - , SHTransformComponent* transformComponent - , SHRigidBodyComponent* rigidBodyComponent - , SHColliderComponent* colliderComponent - , double interpolationFactor - ) noexcept; }; private: @@ -271,26 +141,9 @@ namespace SHADE double interpolationFactor; double fixedDT; - // rp3d - - rp3d::PhysicsCommon factory; - - // Interface objects - - SHPhysicsWorldState worldState; - SHPhysicsObjectManager objectManager; - SHCollisionListener collisionListener; - SHPhysicsRaycaster raycaster; - /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ - SHEventHandle addPhysicsComponent (SHEventPtr addComponentEvent) noexcept; - SHEventHandle removePhysicsComponent (SHEventPtr removeComponentEvent) noexcept; - - SHEventHandle onPlay (SHEventPtr onPlayEvent); - SHEventHandle onStop (SHEventPtr onStopEvent); - SHEventHandle buildScene (SHEventPtr onSceneChangeEvent); }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp index 1fb11274..06f1b464 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp @@ -27,26 +27,26 @@ namespace SHADE { static std::vector emptyVec; - auto phySystem = SHSystemManager::GetSystem(); - if (phySystem) - { - return phySystem->GetAllCollisionInfo(); - } + //auto phySystem = SHSystemManager::GetSystem(); + //if (phySystem) + //{ + // return phySystem->GetAllCollisionInfo(); + //} - SHLOGV_WARNING("Failed to get collision events. Empty vector returned instead."); + //SHLOGV_WARNING("Failed to get collision events. Empty vector returned instead."); return emptyVec; } const std::vector& SHPhysicsSystemInterface::GetTriggerInfo() noexcept { static std::vector emptyVec; - auto phySystem = SHSystemManager::GetSystem(); - if (phySystem) - { - return phySystem->GetAllTriggerInfo(); - } + //auto phySystem = SHSystemManager::GetSystem(); + //if (phySystem) + //{ + // return phySystem->GetAllTriggerInfo(); + //} - SHLOGV_WARNING("Failed to get trigger events. Empty vector returned instead."); + //SHLOGV_WARNING("Failed to get trigger events. Empty vector returned instead."); return emptyVec; } @@ -61,76 +61,4 @@ namespace SHADE SHLOGV_WARNING("Failed to get fixed delta time. 0.0 returned instead."); return 0.0; } - - SHPhysicsRaycastResult SHPhysicsSystemInterface::Raycast(const SHRay& ray, float distance) noexcept - { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - { - return physicsSystem->Raycast(ray, distance); - } - - SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); - return SHPhysicsRaycastResult{}; - } - - SHPhysicsRaycastResult SHPhysicsSystemInterface::Linecast(const SHVec3& start, const SHVec3& end) noexcept - { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - { - return physicsSystem->Linecast(start, end); - } - - SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); - return SHPhysicsRaycastResult{}; - } - - SHPhysicsRaycastResult SHPhysicsSystemInterface::ColliderRaycast(EntityID eid, const SHRay& ray, float distance) noexcept - { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - { - return physicsSystem->ColliderRaycast(eid, ray, distance); - } - - SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); - return SHPhysicsRaycastResult{}; - } - - SHPhysicsRaycastResult SHPhysicsSystemInterface::ColliderRaycast(EntityID eid, int shapeIndex, const SHRay& ray, float distance) noexcept - { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - { - return physicsSystem->ColliderRaycast(eid, shapeIndex, ray, distance); - } - - SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); - return SHPhysicsRaycastResult{}; - } - - SHPhysicsRaycastResult SHPhysicsSystemInterface::ColliderLinecast(EntityID eid, const SHVec3& start, const SHVec3& end) noexcept - { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - { - return physicsSystem->ColliderLinecast(eid, start, end); - } - - SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); - return SHPhysicsRaycastResult{}; - } - - SHPhysicsRaycastResult SHPhysicsSystemInterface::ColliderLinecast(EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept - { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - { - return physicsSystem->ColliderLinecast(eid, shapeIndex, start, end); - } - - SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); - return SHPhysicsRaycastResult{}; - } } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h index 0065aee3..0664f367 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h @@ -16,6 +16,7 @@ of DigiPen Institute of Technology is prohibited. // Project Headers #include "ECS_Base/Entity/SHEntity.h" +#include "Physics/Collision/SHCollisionInfo.h" namespace SHADE @@ -24,7 +25,6 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ - class SHCollisionInfo; class SHVec3; struct SHRay; struct SHPhysicsRaycastResult; @@ -52,12 +52,5 @@ namespace SHADE [[nodiscard]] static const std::vector& GetCollisionInfo() noexcept; [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; [[nodiscard]] static double GetFixedDT () noexcept; - - [[nodiscard]] static SHPhysicsRaycastResult Raycast (const SHRay& ray, float distance = std::numeric_limits::infinity()) noexcept; - [[nodiscard]] static SHPhysicsRaycastResult Linecast (const SHVec3& start, const SHVec3& end) noexcept; - [[nodiscard]] static SHPhysicsRaycastResult ColliderRaycast (EntityID eid, const SHRay& ray, float distance = std::numeric_limits::infinity()) noexcept; - [[nodiscard]] static SHPhysicsRaycastResult ColliderRaycast (EntityID eid, int shapeIndex, const SHRay& ray, float distance = std::numeric_limits::infinity()) noexcept; - [[nodiscard]] static SHPhysicsRaycastResult ColliderLinecast (EntityID eid, const SHVec3& start, const SHVec3& end) noexcept; - [[nodiscard]] static SHPhysicsRaycastResult ColliderLinecast (EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept; }; } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp index a5ca957a..0029a8e7 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp @@ -55,85 +55,12 @@ namespace SHADE void SHPhysicsSystem::PhysicsPreUpdate::Execute(double) noexcept { auto* physicsSystem = reinterpret_cast(GetSystem()); - - #ifdef SHEDITOR - - // Only Sync on Play. - // Otherwise, Components are only holding data until the world is built on play. - const auto* EDITOR = SHSystemManager::GetSystem(); - if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) - { - physicsSystem->objectManager.UpdateCommands(); - - for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects) - { - // Ensure a valid physics Object - if (physicsObject.rp3dBody == nullptr) - continue; - - // Sync active states between SHADE & RP3D - syncRigidBodyActive(entityID, physicsObject); - syncColliderActive(entityID, physicsObject); - - syncOnPlay(entityID, physicsObject); - } - } - else - { - auto& rigidBodyDense = SHComponentManager::GetDense(); - auto& colliderDense = SHComponentManager::GetDense(); - - for (auto& rigidBodyComponent : rigidBodyDense) - { - const auto* TRANSFORM = SHComponentManager::GetComponent_s(rigidBodyComponent.GetEID()); - - if (TRANSFORM && TRANSFORM->HasChanged()) - { - rigidBodyComponent.position = TRANSFORM->GetWorldPosition(); - rigidBodyComponent.orientation = TRANSFORM->GetWorldOrientation(); - } - } - - for (auto& colliderComponent : colliderDense) - { - const auto* TRANSFORM = SHComponentManager::GetComponent_s(colliderComponent.GetEID()); - - if (TRANSFORM && TRANSFORM->HasChanged()) - { - colliderComponent.position = TRANSFORM->GetWorldPosition(); - colliderComponent.orientation = TRANSFORM->GetWorldOrientation(); - colliderComponent.scale = TRANSFORM->GetWorldScale(); - - colliderComponent.RecomputeCollisionShapes(); - } - } - } - - #else - - // Always sync Rigid Body & Collider Components with Physics Objects - // Do not check for an editor here - - physicsSystem->objectManager.UpdateCommands(); - - for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects) - { - // Ensure a valid physics Object - if (physicsObject.rp3dBody == nullptr) - continue; - - syncRigidBodyActive(entityID, physicsObject); - syncColliderActive(entityID, physicsObject); - - syncOnPlay(entityID, physicsObject); - } - - #endif } void SHPhysicsSystem::PhysicsFixedUpdate::Execute(double dt) noexcept { auto* physicsSystem = reinterpret_cast(GetSystem()); + auto* scriptingSystem = SHSystemManager::GetSystem(); if (scriptingSystem == nullptr) { @@ -141,20 +68,16 @@ namespace SHADE } const double FIXED_DT = physicsSystem->fixedDT; - // HACK: Clamp DT here to prevent a ridiculous amount of updates. This limits updates from large dt to 2. - // HACK: This should be done by the FRC and not here for predictable behaviour. accumulatedTime += dt; - //testFunction(); - int count = 0; while (accumulatedTime > FIXED_DT) { if (scriptingSystem != nullptr) scriptingSystem->ExecuteFixedUpdates(); - physicsSystem->worldState.world->update(static_cast(FIXED_DT)); + // TODO: Update World accumulatedTime -= FIXED_DT; ++count; @@ -177,236 +100,16 @@ namespace SHADE } // Interpolate transforms for rendering - if (physicsSystem->worldUpdated) - { - for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects) - { - auto* transformComponent = SHComponentManager::GetComponent_s(entityID); - auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); - auto* colliderComponent = SHComponentManager::GetComponent_s(entityID); - postUpdateSyncTransforms - ( - physicsObject - , transformComponent - , rigidBodyComponent - , colliderComponent - , physicsSystem->interpolationFactor - ); - } + // Collision & Trigger messages + if (scriptingSystem != nullptr) + scriptingSystem->ExecuteCollisionFunctions(); - // Collision & Trigger messages - if (scriptingSystem != nullptr) - scriptingSystem->ExecuteCollisionFunctions(); - - // Since this function never runs when editor in not in play, execute the function anyway - physicsSystem->collisionListener.CleanContainers(); - } } /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsSystem::PhysicsPreUpdate::syncRigidBodyActive(EntityID eid, SHPhysicsObject& physicsObject) const noexcept - { - if (!SHComponentManager::HasComponent(eid)) - return; - - const bool IS_ACTIVE_IN_SCENE = SHSceneManager::CheckNodeAndComponentsActive(eid); - const bool IS_RP3D_BODY_ACTIVE = physicsObject.GetRigidBody()->isActive(); - - if (IS_ACTIVE_IN_SCENE != IS_RP3D_BODY_ACTIVE) - physicsObject.GetRigidBody()->setIsActive(IS_ACTIVE_IN_SCENE); - } - - void SHPhysicsSystem::PhysicsPreUpdate::syncColliderActive(EntityID eid, SHPhysicsObject& physicsObject) const noexcept - { - const auto* COLLIDER = SHComponentManager::GetComponent_s(eid); - if (!COLLIDER) - return; - - const bool IS_ACTIVE_IN_SCENE = SHSceneManager::CheckNodeAndComponentsActive(eid); - const bool IS_RP3D_COLLIDER_ACTIVE = physicsObject.collidersActive; - - if (IS_ACTIVE_IN_SCENE != IS_RP3D_COLLIDER_ACTIVE) - { - // HACK: If active state turned off, remove all collision shapes. If turned on, add them back. - auto* physicsSystem = reinterpret_cast(GetSystem()); - - const int NUM_SHAPES = static_cast(COLLIDER->GetCollisionShapes().size()); - if (IS_ACTIVE_IN_SCENE) - { - for (int i = 0; i < NUM_SHAPES; ++i) - physicsSystem->objectManager.AddCollisionShape(eid, i); - } - else - { - for (int i = NUM_SHAPES - 1; i >= 0; --i) - physicsSystem->objectManager.RemoveCollisionShape(eid, i); - } - - physicsObject.collidersActive = IS_ACTIVE_IN_SCENE; - } - } - - void SHPhysicsSystem::PhysicsPreUpdate::syncOnPlay(EntityID eid, SHPhysicsObject& physicsObject) noexcept - { - auto* transformComponent = SHComponentManager::GetComponent_s(eid); - auto* rigidBodyComponent = SHComponentManager::GetComponent_s(eid); - auto* colliderComponent = SHComponentManager::GetComponent_s(eid); - - // Sync transforms & physics components transforms - if (transformComponent && transformComponent->HasChanged()) - { - physicsObject.GetRigidBody()->setIsSleeping(false); - - preUpdateSyncTransform - ( - physicsObject - , transformComponent - , rigidBodyComponent - , colliderComponent - ); - } - - // Sync Rigid Bodies - if (rigidBodyComponent) - physicsObject.SyncRigidBody(*rigidBodyComponent); - - // Sync Colliders - if (colliderComponent) - physicsObject.SyncColliders(*colliderComponent); - } - - void SHPhysicsSystem::PhysicsPreUpdate::preUpdateSyncTransform - ( - SHPhysicsObject& physicsObject - , SHTransformComponent* transformComponent - , SHRigidBodyComponent* rigidBodyComponent - , SHColliderComponent* colliderComponent - ) noexcept - { - if (!transformComponent) - return; - - const SHVec3& WORLD_POS = transformComponent->GetWorldPosition(); - const SHQuaternion& WORLD_ROT = transformComponent->GetWorldOrientation(); - const SHVec3& WORLD_SCL = transformComponent->GetWorldScale(); - - const rp3d::Transform RP3D_TRANSFORM { WORLD_POS, WORLD_ROT }; - physicsObject.GetRigidBody()->setTransform(RP3D_TRANSFORM); - physicsObject.prevTransform = RP3D_TRANSFORM; - - if (rigidBodyComponent && SHSceneManager::CheckNodeAndComponentsActive(physicsObject.entityID)) - { - rigidBodyComponent->position = WORLD_POS; - rigidBodyComponent->orientation = WORLD_ROT; - } - - if (colliderComponent && SHSceneManager::CheckNodeAndComponentsActive(physicsObject.entityID)) - { - colliderComponent->position = WORLD_POS; - colliderComponent->orientation = WORLD_ROT; - colliderComponent->scale = WORLD_SCL; - - colliderComponent->RecomputeCollisionShapes(); - } - } - - void SHPhysicsSystem::PhysicsPostUpdate::postUpdateSyncTransforms - ( - SHPhysicsObject& physicsObject - , SHTransformComponent* transformComponent - , SHRigidBodyComponent* rigidBodyComponent - , SHColliderComponent* colliderComponent - , double interpolationFactor - ) noexcept - { - const rp3d::Transform& CURRENT_TF = physicsObject.GetRigidBody()->getTransform(); - auto renderPos = CURRENT_TF.getPosition(); - auto renderRot = CURRENT_TF.getOrientation(); - - // Cache transforms - if (physicsObject.GetRigidBody()->isActive()) - physicsObject.prevTransform = CURRENT_TF; - - // Sync with rigid bodies - if (rigidBodyComponent && SHSceneManager::CheckNodeAndComponentsActive(physicsObject.entityID)) - { - // Skip static bodies - if (rigidBodyComponent->GetType() == SHRigidBodyComponent::Type::STATIC) - return; - - // Check if transform should be interpolated - if (rigidBodyComponent->IsInterpolating()) - { - // Interpolate transforms between current and predicted next transform - - const rp3d::Transform PREV_TF = physicsObject.prevTransform; - const rp3d::Transform INTERPOLATED_TF = rp3d::Transform::interpolateTransforms(PREV_TF, CURRENT_TF, static_cast(interpolationFactor)); - - renderPos = INTERPOLATED_TF.getPosition(); - renderRot = INTERPOLATED_TF.getOrientation(); - } - - rigidBodyComponent->position = CURRENT_TF.getPosition(); - rigidBodyComponent->orientation = CURRENT_TF.getOrientation(); - - // Sync with colliders - if (colliderComponent && SHSceneManager::CheckNodeAndComponentsActive(physicsObject.entityID)) - { - // Skip colliders without rigidbody components. If any transform was updated, it was done in pre-update. - - colliderComponent->position = CURRENT_TF.getPosition(); - colliderComponent->orientation = CURRENT_TF.getOrientation(); - } - - // Set transform for rendering - if (transformComponent) - { - transformComponent->SetWorldPosition(renderPos); - transformComponent->SetWorldOrientation(renderRot); - } - } - } } // namespace SHADE -///////////////////////////////////////////////////////////////////////////////////////// -void testFunction() -{ - using namespace SHADE; - - // Test movement - const float forceModifier = 25.0f; - EntityID eid = 65538; - - if (SHEntityManager::IsValidEID(eid)) - { - auto* rb = SHComponentManager::GetComponent_s(eid); - if (rb) - { - if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::W)) - rb->AddForce(SHVec3::UnitZ * forceModifier); - - if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A)) - rb->AddForce(-SHVec3::UnitX * forceModifier); - - if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::S)) - rb->AddForce(-SHVec3::UnitZ * forceModifier); - - if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::D)) - rb->AddForce(SHVec3::UnitX * forceModifier); - } - } - - // Cast rays - auto* tag = SHCollisionTagMatrix::GetTag(1); - tag->SetLayerState(SHCollisionTag::Layer::_1, false); - tag->SetLayerState(SHCollisionTag::Layer::_2, true); - - SHRay ray { SHVec3{3.0f, 3.5f, 0.0f}, -SHVec3::UnitX }; - auto* physicsSystem = SHSystemManager::GetSystem(); - physicsSystem->Raycast(ray, std::numeric_limits::infinity(), *tag); -} diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index b5f1444f..414e79b7 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -19,7 +19,7 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { - /*---------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ /* CollisionShape - Constructors */ /*---------------------------------------------------------------------------------*/ CollisionShape::CollisionShape(int arrayIdx, Entity attachedEntity) diff --git a/SHADE_Managed/src/Physics/Physics.cxx b/SHADE_Managed/src/Physics/Physics.cxx index 9e2c1413..fc54f527 100644 --- a/SHADE_Managed/src/Physics/Physics.cxx +++ b/SHADE_Managed/src/Physics/Physics.cxx @@ -43,47 +43,47 @@ namespace SHADE RaycastHit Physics::Raycast(Ray ray) { - return Convert::ToCLI(SHPhysicsSystemInterface::Raycast(Convert::ToNative(ray))); + return RaycastHit{}; } RaycastHit Physics::Raycast(Ray ray, float distance) { - return Convert::ToCLI(SHPhysicsSystemInterface::Raycast(Convert::ToNative(ray), distance)); + return RaycastHit{}; } RaycastHit Physics::Linecast(Vector3 start, Vector3 end) { - return Convert::ToCLI(SHPhysicsSystemInterface::Linecast(Convert::ToNative(start), Convert::ToNative(end))); + return RaycastHit{}; } RaycastHit Physics::ColliderRaycast(GameObject object, Ray ray) { - return Convert::ToCLI(SHPhysicsSystemInterface::ColliderRaycast(object.EntityId, Convert::ToNative(ray))); + return RaycastHit{}; } RaycastHit Physics::ColliderRaycast(GameObject object, Ray ray, float distance) { - return Convert::ToCLI(SHPhysicsSystemInterface::ColliderRaycast(object.EntityId, Convert::ToNative(ray), distance)); + return RaycastHit{}; } RaycastHit Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray) { - return Convert::ToCLI(SHPhysicsSystemInterface::ColliderRaycast(object.EntityId, shapeIndex, Convert::ToNative(ray))); + return RaycastHit{}; } RaycastHit Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, float distance) { - return Convert::ToCLI(SHPhysicsSystemInterface::ColliderRaycast(object.EntityId, shapeIndex, Convert::ToNative(ray), distance)); + return RaycastHit{}; } RaycastHit Physics::ColliderLineCast(GameObject object, Vector3 start, Vector3 end) { - return Convert::ToCLI(SHPhysicsSystemInterface::ColliderLinecast(object.EntityId, Convert::ToNative(start), Convert::ToNative(end))); + return RaycastHit{}; } RaycastHit Physics::ColliderLineCast(GameObject object, int shapeIndex, Vector3 start, Vector3 end) { - return Convert::ToCLI(SHPhysicsSystemInterface::ColliderLinecast(object.EntityId, shapeIndex, Convert::ToNative(start), Convert::ToNative(end))); + return RaycastHit{}; } /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Utility/Convert.cxx b/SHADE_Managed/src/Utility/Convert.cxx index 590a3cf0..1d277274 100644 --- a/SHADE_Managed/src/Utility/Convert.cxx +++ b/SHADE_Managed/src/Utility/Convert.cxx @@ -103,38 +103,6 @@ namespace SHADE /* Physics Conversions */ /*---------------------------------------------------------------------------------*/ - SHPhysicsRaycastResult Convert::ToNative(RaycastHit cli) - { - // This function shouldn't be used anyway, so we leave the entityHit empty. - - SHPhysicsRaycastResult native; - - native.hit = cli.Hit; - native.position = ToNative(cli.Position); - native.normal = ToNative(cli.Normal); - native.distance = cli.Distance; - native.shapeIndex = cli.CollisionShapeIndex; - - return native; - } - - RaycastHit Convert::ToCLI(const SHPhysicsRaycastResult& native) - { - RaycastHit cli; - - cli.Hit = native.hit; - cli.Position = ToCLI(native.position); - cli.Normal = ToCLI(native.normal); - cli.Distance = native.distance; - cli.CollisionShapeIndex = native.shapeIndex; - - cli.Other = SHEntityManager::IsValidEID(native.entityHit) - ? GameObject(native.entityHit) - : System::Nullable(); - - return cli; - } - /*---------------------------------------------------------------------------------*/ /* Handle Conversions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Utility/Convert.hxx b/SHADE_Managed/src/Utility/Convert.hxx index fb373c51..6fa4d935 100644 --- a/SHADE_Managed/src/Utility/Convert.hxx +++ b/SHADE_Managed/src/Utility/Convert.hxx @@ -141,20 +141,6 @@ namespace SHADE /* Physics Conversions */ /*-----------------------------------------------------------------------------*/ - /// - /// Converts from a managed RaycastHit to a native SHPhysicsRaycastResult - /// - /// The managed RaycastHit to convert from. - /// Native copy of a managed RaycastHit. - static SHPhysicsRaycastResult ToNative(RaycastHit cli); - - /// - /// Converts from native SHPhysicsRaycastResult to a managed RaycastHit. - /// - /// The native SHPhysicsRaycastResult to convert from. - /// Managed copy of a native SHPhysicsRaycastResult. - static RaycastHit ToCLI(const SHPhysicsRaycastResult& native); - /*-----------------------------------------------------------------------------*/ /* Handle Conversions */ /*-----------------------------------------------------------------------------*/ From 6cd203179a6bb7d6951ce4b06993a6b0489e7fd6 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 2 Dec 2022 19:01:08 +0800 Subject: [PATCH 008/164] Added Rigid Body --- Assets/Application.SHConfig | 2 +- Assets/Scenes/PhysicsSandbox.shade | 1 + Assets/Scenes/PhysicsSandbox.shade.shmeta | 3 + .../src/Physics/Dynamics/SHRigidBody.cpp | 96 +++++ .../src/Physics/Dynamics/SHRigidBody.h | 189 +++++++++ .../src/Physics/Dynamics/SHRigidBody.inl | 375 ++++++++++++++++++ 6 files changed, 665 insertions(+), 1 deletion(-) create mode 100644 Assets/Scenes/PhysicsSandbox.shade create mode 100644 Assets/Scenes/PhysicsSandbox.shade.shmeta create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHRigidBody.inl diff --git a/Assets/Application.SHConfig b/Assets/Application.SHConfig index 5673556d..d0fa83df 100644 --- a/Assets/Application.SHConfig +++ b/Assets/Application.SHConfig @@ -1,4 +1,4 @@ Start in Fullscreen: false -Starting Scene ID: 97158628 +Starting Scene ID: 97402985 Window Size: {x: 1920, y: 1080} Window Title: SHADE Engine \ No newline at end of file diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/Assets/Scenes/PhysicsSandbox.shade.shmeta b/Assets/Scenes/PhysicsSandbox.shade.shmeta new file mode 100644 index 00000000..97c00ca6 --- /dev/null +++ b/Assets/Scenes/PhysicsSandbox.shade.shmeta @@ -0,0 +1,3 @@ +Name: PhysicsSandbox +ID: 97402985 +Type: 5 diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp new file mode 100644 index 00000000..ad739847 --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -0,0 +1,96 @@ +/**************************************************************************************** + * \file SHRigidBody.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Rigid Body. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHRigidBody.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHRigidBody::SHRigidBody(EntityID eid, Type type) noexcept + : entityID { eid } + , bodyType { type } + , invMass { type == Type::DYNAMIC ? 1.0f : 0.0f } + , linearDrag { type != Type::STATIC ? 0.01f : 0.0f } + , gravityScale { 1.0f } + , flags { 0U } + { + // Set default flags + flags |= 1U << 0; // Body is active + flags |= 1U << 2; // Sleeping is enabled + flags |= 1U << 3; // Gravity is enabled + + // TODO: Compute inertia if body is dynamic + } + + SHRigidBody::SHRigidBody(const SHRigidBody& rhs) noexcept + : entityID { rhs.entityID } + , bodyType { rhs.bodyType } + , invMass { rhs.invMass } + , linearDrag { rhs.linearDrag } + , gravityScale { rhs.gravityScale } + , flags { rhs.flags } + { + // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. + } + + SHRigidBody::SHRigidBody(SHRigidBody&& rhs) noexcept + : entityID { rhs.entityID } + , bodyType { rhs.bodyType } + , invMass { rhs.invMass } + , linearDrag { rhs.linearDrag } + , gravityScale { rhs.gravityScale } + , flags { rhs.flags } + { + // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHRigidBody& SHRigidBody::operator=(const SHRigidBody& rhs) noexcept + { + // Handle self assignment + if (this == &rhs) + return *this; + + entityID = rhs.entityID; + bodyType = rhs.bodyType; + invMass = rhs.invMass; + linearDrag = rhs.linearDrag; + gravityScale = rhs.gravityScale; + flags = rhs.flags; + + // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. + + return *this; + } + + SHRigidBody& SHRigidBody::operator=(SHRigidBody&& rhs) noexcept + { + entityID = rhs.entityID; + bodyType = rhs.bodyType; + invMass = rhs.invMass; + linearDrag = rhs.linearDrag; + gravityScale = rhs.gravityScale; + flags = rhs.flags; + + // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. + + return *this; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h new file mode 100644 index 00000000..598ed3aa --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -0,0 +1,189 @@ +/**************************************************************************************** + * \file SHRigidBody.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Rigid Body. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "ECS_Base/SHECSMacros.h" +#include "Math/Vector/SHVec3.h" + +namespace SHADE +{ + /** + * @brief + * Encapsulates a Rigid Body used in Physics Simulations + */ + class SHRigidBody + { + private: + /*-----------------------------------------------------------------------------------*/ + /* Friends */ + /*-----------------------------------------------------------------------------------*/ + + friend class SHRigidBodyComponent; + + public: + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + enum class Type + { + STATIC // Immovable body with infinite mass + , KINEMATIC // Only movable by setting velocity, unaffected by forces. Has infinite mass. + , DYNAMIC // Affected by forces. + }; + + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-----------------------------------------------------------------------------------*/ + + SHRigidBody (EntityID eid, Type type) noexcept; + SHRigidBody (const SHRigidBody& rhs) noexcept; + SHRigidBody (SHRigidBody&& rhs) noexcept; + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------------*/ + + SHRigidBody& operator= (const SHRigidBody& rhs) noexcept; + SHRigidBody& operator= (SHRigidBody&& rhs) noexcept; + + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*-----------------------------------------------------------------------------------*/ + + [[nodiscard]] Type GetType () const noexcept; + + [[nodiscard]] float GetMass () const noexcept; + [[nodiscard]] float GetLinearDrag () const noexcept; + + [[nodiscard]] float GetGravityScale () const noexcept; + + [[nodiscard]] const SHVec3& GetAccumulatedForce () const noexcept; + [[nodiscard]] const SHVec3& GetLinearVelocity () const noexcept; + [[nodiscard]] const SHVec3& GetAngularVelocity () const noexcept; + + // Flags + + [[nodiscard]] bool IsActive () const noexcept; + [[nodiscard]] bool IsSleeping () const noexcept; + [[nodiscard]] bool IsSleepingEnabled () const noexcept; + [[nodiscard]] bool IsGravityEnabled () const noexcept; + [[nodiscard]] bool IsAutoMassEnabled () const noexcept; + [[nodiscard]] bool GetFreezePositionX () const noexcept; + [[nodiscard]] bool GetFreezePositionY () const noexcept; + [[nodiscard]] bool GetFreezePositionZ () const noexcept; + [[nodiscard]] bool GetFreezeRotationX () const noexcept; + [[nodiscard]] bool GetFreezeRotationY () const noexcept; + [[nodiscard]] bool GetFreezeRotationZ () const noexcept; + + /*-----------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Changing the type from non-Dynamic to Dynamic will set the default mass. + */ + void SetType (Type newType) noexcept; + + /** + * @brief + * Mass is only modifiable for Dynamic bodies. + * @param newMass + * The new mass to set. Values below 0 will be ignored. + */ + void SetMass (float newMass) noexcept; + + /** + * @brief + * Drag is only modifiable for non-Static bodies. + * @param newLinearDrag + * The new drag to set. Values below 0 will be ignored. + */ + void SetLinearDrag (float newLinearDrag) noexcept; + + + void SetGravityScale (float newGravityScale) noexcept; + + void SetLinearVelocity (const SHVec3& newLinearVelocity) noexcept; + + // Flags + + void SetIsActive (bool isActive) noexcept; + void SetIsSleeping (bool isSleeping) noexcept; + void SetSleepingEnabled (bool enableSleeping) noexcept; + void SetGravityEnabled (bool enableGravity) noexcept; + void SetAutoMassEnabled (bool enableAutoMass) noexcept; + void SetFreezePositionX (bool freezePositionX) noexcept; + void SetFreezePositionY (bool freezePositionY) noexcept; + void SetFreezePositionZ (bool freezePositionZ) noexcept; + void SetFreezeRotationX (bool freezeRotationX) noexcept; + void SetFreezeRotationY (bool freezeRotationY) noexcept; + void SetFreezeRotationZ (bool freezeRotationZ) noexcept; + + /*-----------------------------------------------------------------------------------*/ + /* Member Functions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Adds a force to the body with an offset from it's center of mass. + * @param force + * The force to add to the body. + * @param pos + * The position from the center of mass to offset the force. Defaults to zero. + */ + void AddForce (const SHVec3& force, const SHVec3& pos = SHVec3::Zero) noexcept; + + /** + * @brief + * Adds an impulse to the body with an offset from it's center of mass. + * @param impulse + * The impulse to add to the body. + * @param pos + * The position from the center of mass to offset the impulse. Defaults to zero. + */ + void AddImpulse (const SHVec3& impulse, const SHVec3& pos = SHVec3::Zero) noexcept; + + /** + * @brief + * Removes all the forces from the body. + */ + void ClearForces () noexcept; + + private: + /*-----------------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------------*/ + + // The entityID here is only meant for linking with the actual component in the engine. + EntityID entityID; + + Type bodyType; + + float invMass; + float linearDrag; + + float gravityScale; + + SHVec3 accumulatedForce; + + SHVec3 linearVelocity; + SHVec3 angularVelocity; + + // aZ aY aX pZ pY pX 0 0 0 0 0 autoMass enableGravity enableSleeping sleeping active + uint16_t flags; + }; + +} // namespace SHADE + +#include "SHRigidBody.inl" diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.inl b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.inl new file mode 100644 index 00000000..28f98d12 --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.inl @@ -0,0 +1,375 @@ +/**************************************************************************************** + * \file SHRigidBody.inl + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for inlined functions of a Rigid Body. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Primary Header +#include "SHRigidBody.h" + +// Project Headers +#include "Math/SHMathHelpers.h" +#include "Tools/Logger/SHLogger.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + inline SHRigidBody::Type SHRigidBody::GetType() const noexcept + { + return bodyType; + } + + inline float SHRigidBody::GetMass() const noexcept + { + return 1.0f/ invMass; + } + + inline float SHRigidBody::GetLinearDrag() const noexcept + { + return linearDrag; + } + + inline float SHRigidBody::GetGravityScale() const noexcept + { + return gravityScale; + } + + inline const SHVec3& SHRigidBody::GetAccumulatedForce() const noexcept + { + return accumulatedForce; + } + + inline const SHVec3& SHRigidBody::GetLinearVelocity() const noexcept + { + return linearVelocity; + } + + inline const SHVec3& SHRigidBody::GetAngularVelocity() const noexcept + { + return angularVelocity; + } + + // Flags + + inline bool SHRigidBody::IsActive() const noexcept + { + static constexpr unsigned int FLAG_POS = 0; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::IsSleeping() const noexcept + { + static constexpr unsigned int FLAG_POS = 1; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::IsSleepingEnabled() const noexcept + { + static constexpr unsigned int FLAG_POS = 2; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::IsGravityEnabled() const noexcept + { + static constexpr unsigned int FLAG_POS = 3; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::IsAutoMassEnabled() const noexcept + { + static constexpr unsigned int FLAG_POS = 4; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::GetFreezePositionX() const noexcept + { + static constexpr unsigned int FLAG_POS = 10; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::GetFreezePositionY() const noexcept + { + static constexpr unsigned int FLAG_POS = 11; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::GetFreezePositionZ() const noexcept + { + static constexpr unsigned int FLAG_POS = 12; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::GetFreezeRotationX() const noexcept + { + static constexpr unsigned int FLAG_POS = 13; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::GetFreezeRotationY() const noexcept + { + static constexpr unsigned int FLAG_POS = 14; + return flags & (1U << FLAG_POS); + } + + inline bool SHRigidBody::GetFreezeRotationZ() const noexcept + { + static constexpr unsigned int FLAG_POS = 15; + return flags & (1U << FLAG_POS); + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + inline void SHRigidBody::SetType(Type newType) noexcept + { + if (newType == bodyType) + return; + + bodyType = newType; + invMass = newType == Type::DYNAMIC ? 1.0f : 0.0f; + } + + inline void SHRigidBody::SetMass(float newMass) noexcept + { + if (bodyType != Type::DYNAMIC) + { + SHLOG_WARNING("Cannot set mass of a non-Dynamic Body {}", entityID) + return; + } + + if (newMass < 0.0f) + { + SHLOG_WARNING("Cannot set mass below 0. Object {}'s mass will remain unchanged.", entityID) + return; + } + + invMass = 1.0f / newMass; + + // TODO: Recompute inertia tensor + } + + inline void SHRigidBody::SetLinearDrag(float newLinearDrag) noexcept + { + if (bodyType == Type::STATIC) + { + SHLOG_WARNING("Cannot set linear drag of a Static Body {}", entityID) + return; + } + + if (newLinearDrag < 0.0f) + { + SHLOG_WARNING("Cannot set drag below 0. Object {}'s linear drag will remain unchanged.", entityID) + return; + } + + linearDrag = newLinearDrag; + } + + inline void SHRigidBody::SetGravityScale(float newGravityScale) noexcept + { + gravityScale = newGravityScale; + } + + inline void SHRigidBody::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept + { + linearVelocity = newLinearVelocity; + } + + inline void SHRigidBody::SetIsActive(bool isActive) noexcept + { + static constexpr unsigned int FLAG_POS = 0; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + isActive ? flags |= VALUE : flags &= ~VALUE; + } + + inline void SHRigidBody::SetIsSleeping(bool isSleeping) noexcept + { + static constexpr unsigned int FLAG_POS = 1; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + isSleeping ? flags |= VALUE : flags &= ~VALUE; + } + + inline void SHRigidBody::SetSleepingEnabled(bool enableSleeping) noexcept + { + static constexpr unsigned int FLAG_POS = 2; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (enableSleeping) + { + flags |= VALUE; + } + else + { + flags &= ~VALUE; + // Wake the body + SetIsSleeping(false); + } + } + + inline void SHRigidBody::SetGravityEnabled(bool enableGravity) noexcept + { + static constexpr unsigned int FLAG_POS = 3; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + enableGravity ? flags |= VALUE : flags &= ~VALUE; + } + + inline void SHRigidBody::SetAutoMassEnabled(bool enableAutoMass) noexcept + { + static constexpr unsigned int FLAG_POS = 4; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (enableAutoMass) + { + flags |= VALUE; + // TODO: Compute mass based on collider geometry + } + else + { + flags &= ~VALUE; + // Use default mass of 1 + invMass = 1.0f; + } + } + + inline void SHRigidBody::SetFreezePositionX(bool freezePositionX) noexcept + { + static constexpr unsigned int FLAG_POS = 10; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezePositionX) + { + flags |= VALUE; + // Reset linear velocity along X-axis + linearVelocity.x = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + inline void SHRigidBody::SetFreezePositionY(bool freezePositionY) noexcept + { + static constexpr unsigned int FLAG_POS = 11; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezePositionY) + { + flags |= VALUE; + // Reset linear velocity along Y-axis + linearVelocity.y = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + inline void SHRigidBody::SetFreezePositionZ(bool freezePositionZ) noexcept + { + static constexpr unsigned int FLAG_POS = 12; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezePositionZ) + { + flags |= VALUE; + // Reset linear velocity along Z-axis + linearVelocity.z = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + inline void SHRigidBody::SetFreezeRotationX(bool freezeRotationX) noexcept + { + static constexpr unsigned int FLAG_POS = 13; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezeRotationX) + { + flags |= VALUE; + // Reset angular velocity along X-axis + angularVelocity.x = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + inline void SHRigidBody::SetFreezeRotationY(bool freezeRotationY) noexcept + { + static constexpr unsigned int FLAG_POS = 14; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezeRotationY) + { + flags |= VALUE; + // Reset angular velocity along Y-axis + angularVelocity.y = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + inline void SHRigidBody::SetFreezeRotationZ(bool freezeRotationZ) noexcept + { + static constexpr unsigned int FLAG_POS = 15; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezeRotationZ) + { + flags |= VALUE; + // Reset angular velocity along Z-axis + angularVelocity.z = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + inline void SHRigidBody::AddForce(const SHVec3& force, const SHVec3& pos) noexcept + { + if (bodyType != Type::DYNAMIC) + return; + + accumulatedForce += force; + // Compute torque when force is offset + } + + inline void SHRigidBody::AddImpulse(const SHVec3& impulse, const SHVec3& pos) noexcept + { + if (bodyType != Type::DYNAMIC) + return; + + linearVelocity += impulse * invMass; + } + + inline void SHRigidBody::ClearForces() noexcept + { + accumulatedForce = SHVec3::Zero; + } + +} // namespace SHADE From ca45a12186f97621bfa4c91e1e76313bd7eda6ae Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 4 Dec 2022 17:31:22 +0800 Subject: [PATCH 009/164] Restructured Physics Systems & Interfaces --- Assets/Scenes/PhysicsSandbox.shade | 29 +- .../src/Application/SBApplication.cpp | 2 +- SHADE_Application/src/Scenes/SBMainScene.cpp | 9 +- SHADE_Application/src/Scenes/SBTestScene.cpp | 2 +- .../Inspector/SHEditorComponentView.hpp | 4 +- .../Inspector/SHEditorInspector.cpp | 2 +- SHADE_Engine/src/Events/SHEventDefines.h | 12 +- .../src/Physics/Collision/SHCollisionInfo.h | 2 +- .../src/Physics/Dynamics/SHRigidBody.cpp | 356 +++++++++++++++++ .../src/Physics/Dynamics/SHRigidBody.h | 16 +- .../src/Physics/Dynamics/SHRigidBody.inl | 375 ------------------ .../PhysicsObject/SHPhysicsObject.cpp | 74 ++++ .../Interface/PhysicsObject/SHPhysicsObject.h | 52 +++ .../PhysicsObject/SHPhysicsObjectManager.cpp | 138 +++++++ .../PhysicsObject/SHPhysicsObjectManager.h | 125 ++++++ .../SHRigidBodyComponent.cpp | 210 +++++++--- .../SHRigidBodyComponent.h | 35 +- SHADE_Engine/src/Physics/SHPhysicsWorld.cpp | 44 -- SHADE_Engine/src/Physics/SHPhysicsWorld.h | 11 - .../SHPhysicsPostUpdateRoutine.cpp} | 73 +--- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 62 +++ .../Routines/SHPhysicsUpdateRoutine.cpp | 62 +++ .../src/Physics/System/SHPhysicsSystem.cpp | 160 +++++++- .../src/Physics/System/SHPhysicsSystem.h | 125 +++--- .../src/Serialization/SHSerialization.cpp | 2 +- SHADE_Managed/src/Components/RigidBody.cxx | 2 +- SHADE_Managed/src/Components/RigidBody.hxx | 2 +- SHADE_Managed/src/Engine/ECS.cxx | 2 +- 28 files changed, 1333 insertions(+), 655 deletions(-) delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHRigidBody.inl create mode 100644 SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp create mode 100644 SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h create mode 100644 SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp create mode 100644 SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h rename SHADE_Engine/src/Physics/Interface/{ => RigidBodyComponent}/SHRigidBodyComponent.cpp (59%) rename SHADE_Engine/src/Physics/Interface/{ => RigidBodyComponent}/SHRigidBodyComponent.h (88%) rename SHADE_Engine/src/Physics/System/{SHPhysicsSystemRoutines.cpp => Routines/SHPhysicsPostUpdateRoutine.cpp} (50%) create mode 100644 SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp create mode 100644 SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 0637a088..a0b3c0c2 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -1 +1,28 @@ -[] \ No newline at end of file +- EID: 0 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: 1.77475965, z: 0} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 1, y: 1, z: 1} + IsActive: true + RigidBody Component: + Type: Static + Auto Mass: false + Mass: 1 + Drag: 0 + Angular Drag: 0 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false + Freeze Position Y: false + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Scripts: ~ \ No newline at end of file diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index ce0a939c..a58839c5 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -109,7 +109,7 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); - SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); diff --git a/SHADE_Application/src/Scenes/SBMainScene.cpp b/SHADE_Application/src/Scenes/SBMainScene.cpp index 55ce32b0..6c875518 100644 --- a/SHADE_Application/src/Scenes/SBMainScene.cpp +++ b/SHADE_Application/src/Scenes/SBMainScene.cpp @@ -11,7 +11,7 @@ #include "Scripting/SHScriptEngine.h" #include "Math/Transform/SHTransformComponent.h" #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" -#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" #include "Graphics/MiddleEnd/Lights/SHLightComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" @@ -44,13 +44,6 @@ namespace Sandbox { sceneName = SHSerialization::DeserializeSceneFromFile(sceneAssetID); - auto* physicsSystem = SHSystemManager::GetSystem(); - if (!physicsSystem) - { - SHLOGV_CRITICAL("Failed to get the physics system for building the scene!") - return; - } - /*-----------------------------------------------------------------------*/ /* TESTING CODE */ /*-----------------------------------------------------------------------*/ diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index bcc7f09d..9e3fa8ab 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -10,7 +10,7 @@ #include "Scripting/SHScriptEngine.h" #include "Math/Transform/SHTransformComponent.h" #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" -#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" #include "Graphics/MiddleEnd/Lights/SHLightComponent.h" diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index f70ab889..02614631 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -251,7 +251,7 @@ namespace SHADE if(rbType == SHRigidBodyComponent::Type::DYNAMIC) //Dynamic only fields { - SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetGravityEnabled(value);}, "Gravity"); + SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetIsGravityEnabled(value);}, "Gravity"); //SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); } if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields @@ -296,7 +296,7 @@ namespace SHADE { SHEditorWidgets::DragVec3("Force", { "X", "Y", "Z" }, [component] {return component->GetForce(); }, [](SHVec3 const& value) {}, false, "Force", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); SHEditorWidgets::DragVec3("Torque", { "X", "Y", "Z" }, [component] {return component->GetTorque(); }, [](SHVec3 const& value) {}, false, "Torque", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); - SHEditorWidgets::CheckBox("Is Asleep", [component] {return component->GetIsSleeping(); }, [](bool value) {}, "If the Rigid Body is asleep"); + SHEditorWidgets::CheckBox("Is Asleep", [component] {return component->IsSleeping(); }, [](bool value) {}, "If the Rigid Body is asleep"); } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index 83647da7..f0d3e3e5 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -14,7 +14,7 @@ #include "Scripting/SHScriptEngine.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" #include "Camera/SHCameraComponent.h" #include "Camera/SHCameraArmComponent.h" diff --git a/SHADE_Engine/src/Events/SHEventDefines.h b/SHADE_Engine/src/Events/SHEventDefines.h index d7bbf5f0..7bf82cd5 100644 --- a/SHADE_Engine/src/Events/SHEventDefines.h +++ b/SHADE_Engine/src/Events/SHEventDefines.h @@ -13,9 +13,11 @@ constexpr SHEventIdentifier SH_COMPONENT_REMOVED_EVENT { 4 }; constexpr SHEventIdentifier SH_SCENEGRAPH_CHANGE_PARENT_EVENT { 5 }; constexpr SHEventIdentifier SH_SCENEGRAPH_ADD_CHILD_EVENT { 6 }; constexpr SHEventIdentifier SH_SCENEGRAPH_REMOVE_CHILD_EVENT { 7 }; -constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_ADDED_EVENT { 8 }; -constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_REMOVED_EVENT { 9 }; -constexpr SHEventIdentifier SH_EDITOR_ON_PLAY_EVENT { 10 }; -constexpr SHEventIdentifier SH_EDITOR_ON_PAUSE_EVENT { 11 }; -constexpr SHEventIdentifier SH_EDITOR_ON_STOP_EVENT { 12 }; +constexpr SHEventIdentifier SH_SCENE_ON_INIT_EVENT { 8 }; +constexpr SHEventIdentifier SH_SCENE_ON_EXIT_EVENT { 9 }; +constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_ADDED_EVENT { 10 }; +constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_REMOVED_EVENT { 11 }; +constexpr SHEventIdentifier SH_EDITOR_ON_PLAY_EVENT { 12 }; +constexpr SHEventIdentifier SH_EDITOR_ON_PAUSE_EVENT { 13 }; +constexpr SHEventIdentifier SH_EDITOR_ON_STOP_EVENT { 14 }; diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h index d2dad647..dfcf4f22 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h @@ -12,7 +12,7 @@ // Project Headers #include "Physics/Interface/SHColliderComponent.h" -#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" namespace SHADE diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index ad739847..1867636c 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -13,6 +13,9 @@ // Primary Header #include "SHRigidBody.h" +// Project Headers +#include "Tools/Logger/SHLogger.h" + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -93,4 +96,357 @@ namespace SHADE return *this; } + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHRigidBody::Type SHRigidBody::GetType() const noexcept + { + return bodyType; + } + + float SHRigidBody::GetMass() const noexcept + { + return 1.0f/ invMass; + } + + float SHRigidBody::GetLinearDrag() const noexcept + { + return linearDrag; + } + + float SHRigidBody::GetGravityScale() const noexcept + { + return gravityScale; + } + + const SHVec3& SHRigidBody::GetAccumulatedForce() const noexcept + { + return accumulatedForce; + } + + const SHVec3& SHRigidBody::GetLinearVelocity() const noexcept + { + return linearVelocity; + } + + const SHVec3& SHRigidBody::GetAngularVelocity() const noexcept + { + return angularVelocity; + } + + // Flags + + bool SHRigidBody::IsActive() const noexcept + { + static constexpr unsigned int FLAG_POS = 0; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::IsSleeping() const noexcept + { + static constexpr unsigned int FLAG_POS = 1; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::IsSleepingEnabled() const noexcept + { + static constexpr unsigned int FLAG_POS = 2; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::IsGravityEnabled() const noexcept + { + static constexpr unsigned int FLAG_POS = 3; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::IsAutoMassEnabled() const noexcept + { + static constexpr unsigned int FLAG_POS = 4; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::GetFreezePositionX() const noexcept + { + static constexpr unsigned int FLAG_POS = 10; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::GetFreezePositionY() const noexcept + { + static constexpr unsigned int FLAG_POS = 11; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::GetFreezePositionZ() const noexcept + { + static constexpr unsigned int FLAG_POS = 12; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::GetFreezeRotationX() const noexcept + { + static constexpr unsigned int FLAG_POS = 13; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::GetFreezeRotationY() const noexcept + { + static constexpr unsigned int FLAG_POS = 14; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBody::GetFreezeRotationZ() const noexcept + { + static constexpr unsigned int FLAG_POS = 15; + return flags & (1U << FLAG_POS); + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHRigidBody::SetType(Type newType) noexcept + { + if (newType == bodyType) + return; + + bodyType = newType; + invMass = newType == Type::DYNAMIC ? 1.0f : 0.0f; + } + + void SHRigidBody::SetMass(float newMass) noexcept + { + if (bodyType != Type::DYNAMIC) + { + SHLOG_WARNING("Cannot set mass of a non-Dynamic Body {}", entityID) + return; + } + + if (newMass < 0.0f) + { + SHLOG_WARNING("Cannot set mass below 0. Object {}'s mass will remain unchanged.", entityID) + return; + } + + invMass = 1.0f / newMass; + + // TODO: Recompute inertia tensor + } + + void SHRigidBody::SetLinearDrag(float newLinearDrag) noexcept + { + if (bodyType == Type::STATIC) + { + SHLOG_WARNING("Cannot set linear drag of a Static Body {}", entityID) + return; + } + + if (newLinearDrag < 0.0f) + { + SHLOG_WARNING("Cannot set drag below 0. Object {}'s linear drag will remain unchanged.", entityID) + return; + } + + linearDrag = newLinearDrag; + } + + void SHRigidBody::SetGravityScale(float newGravityScale) noexcept + { + gravityScale = newGravityScale; + } + + void SHRigidBody::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept + { + linearVelocity = newLinearVelocity; + } + + void SHRigidBody::SetIsActive(bool isActive) noexcept + { + static constexpr unsigned int FLAG_POS = 0; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + isActive ? flags |= VALUE : flags &= ~VALUE; + } + + void SHRigidBody::SetIsSleeping(bool isSleeping) noexcept + { + static constexpr unsigned int FLAG_POS = 1; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + isSleeping ? flags |= VALUE : flags &= ~VALUE; + } + + void SHRigidBody::SetSleepingEnabled(bool enableSleeping) noexcept + { + static constexpr unsigned int FLAG_POS = 2; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (enableSleeping) + { + flags |= VALUE; + } + else + { + flags &= ~VALUE; + // Wake the body + SetIsSleeping(false); + } + } + + void SHRigidBody::SetGravityEnabled(bool enableGravity) noexcept + { + static constexpr unsigned int FLAG_POS = 3; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + enableGravity ? flags |= VALUE : flags &= ~VALUE; + } + + void SHRigidBody::SetAutoMassEnabled(bool enableAutoMass) noexcept + { + static constexpr unsigned int FLAG_POS = 4; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (enableAutoMass) + { + flags |= VALUE; + // TODO: Compute mass based on collider geometry + } + else + { + flags &= ~VALUE; + // Use default mass of 1 + invMass = 1.0f; + } + } + + void SHRigidBody::SetFreezePositionX(bool freezePositionX) noexcept + { + static constexpr unsigned int FLAG_POS = 10; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezePositionX) + { + flags |= VALUE; + // Reset linear velocity along X-axis + linearVelocity.x = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + void SHRigidBody::SetFreezePositionY(bool freezePositionY) noexcept + { + static constexpr unsigned int FLAG_POS = 11; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezePositionY) + { + flags |= VALUE; + // Reset linear velocity along Y-axis + linearVelocity.y = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + void SHRigidBody::SetFreezePositionZ(bool freezePositionZ) noexcept + { + static constexpr unsigned int FLAG_POS = 12; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezePositionZ) + { + flags |= VALUE; + // Reset linear velocity along Z-axis + linearVelocity.z = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + void SHRigidBody::SetFreezeRotationX(bool freezeRotationX) noexcept + { + static constexpr unsigned int FLAG_POS = 13; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezeRotationX) + { + flags |= VALUE; + // Reset angular velocity along X-axis + angularVelocity.x = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + void SHRigidBody::SetFreezeRotationY(bool freezeRotationY) noexcept + { + static constexpr unsigned int FLAG_POS = 14; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezeRotationY) + { + flags |= VALUE; + // Reset angular velocity along Y-axis + angularVelocity.y = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + void SHRigidBody::SetFreezeRotationZ(bool freezeRotationZ) noexcept + { + static constexpr unsigned int FLAG_POS = 15; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + if (freezeRotationZ) + { + flags |= VALUE; + // Reset angular velocity along Z-axis + angularVelocity.z = 0.0f; + } + else + { + flags &= ~VALUE; + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHRigidBody::AddForce(const SHVec3& force, const SHVec3& pos) noexcept + { + if (bodyType != Type::DYNAMIC) + return; + + accumulatedForce += force; + // Compute torque when force is offset + } + + void SHRigidBody::AddImpulse(const SHVec3& impulse, const SHVec3& pos) noexcept + { + if (bodyType != Type::DYNAMIC) + return; + + linearVelocity += impulse * invMass; + } + + void SHRigidBody::ClearForces() noexcept + { + accumulatedForce = SHVec3::Zero; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index 598ed3aa..77b29292 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -13,22 +13,20 @@ // Project Headers #include "ECS_Base/SHECSMacros.h" #include "Math/Vector/SHVec3.h" +#include "SH_API.h" namespace SHADE { + /*-------------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-------------------------------------------------------------------------------------*/ + /** * @brief * Encapsulates a Rigid Body used in Physics Simulations */ - class SHRigidBody + class SH_API SHRigidBody { - private: - /*-----------------------------------------------------------------------------------*/ - /* Friends */ - /*-----------------------------------------------------------------------------------*/ - - friend class SHRigidBodyComponent; - public: /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -185,5 +183,3 @@ namespace SHADE }; } // namespace SHADE - -#include "SHRigidBody.inl" diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.inl b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.inl deleted file mode 100644 index 28f98d12..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.inl +++ /dev/null @@ -1,375 +0,0 @@ -/**************************************************************************************** - * \file SHRigidBody.inl - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for inlined functions of a Rigid Body. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Primary Header -#include "SHRigidBody.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Tools/Logger/SHLogger.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - inline SHRigidBody::Type SHRigidBody::GetType() const noexcept - { - return bodyType; - } - - inline float SHRigidBody::GetMass() const noexcept - { - return 1.0f/ invMass; - } - - inline float SHRigidBody::GetLinearDrag() const noexcept - { - return linearDrag; - } - - inline float SHRigidBody::GetGravityScale() const noexcept - { - return gravityScale; - } - - inline const SHVec3& SHRigidBody::GetAccumulatedForce() const noexcept - { - return accumulatedForce; - } - - inline const SHVec3& SHRigidBody::GetLinearVelocity() const noexcept - { - return linearVelocity; - } - - inline const SHVec3& SHRigidBody::GetAngularVelocity() const noexcept - { - return angularVelocity; - } - - // Flags - - inline bool SHRigidBody::IsActive() const noexcept - { - static constexpr unsigned int FLAG_POS = 0; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::IsSleeping() const noexcept - { - static constexpr unsigned int FLAG_POS = 1; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::IsSleepingEnabled() const noexcept - { - static constexpr unsigned int FLAG_POS = 2; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::IsGravityEnabled() const noexcept - { - static constexpr unsigned int FLAG_POS = 3; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::IsAutoMassEnabled() const noexcept - { - static constexpr unsigned int FLAG_POS = 4; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::GetFreezePositionX() const noexcept - { - static constexpr unsigned int FLAG_POS = 10; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::GetFreezePositionY() const noexcept - { - static constexpr unsigned int FLAG_POS = 11; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::GetFreezePositionZ() const noexcept - { - static constexpr unsigned int FLAG_POS = 12; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::GetFreezeRotationX() const noexcept - { - static constexpr unsigned int FLAG_POS = 13; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::GetFreezeRotationY() const noexcept - { - static constexpr unsigned int FLAG_POS = 14; - return flags & (1U << FLAG_POS); - } - - inline bool SHRigidBody::GetFreezeRotationZ() const noexcept - { - static constexpr unsigned int FLAG_POS = 15; - return flags & (1U << FLAG_POS); - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - inline void SHRigidBody::SetType(Type newType) noexcept - { - if (newType == bodyType) - return; - - bodyType = newType; - invMass = newType == Type::DYNAMIC ? 1.0f : 0.0f; - } - - inline void SHRigidBody::SetMass(float newMass) noexcept - { - if (bodyType != Type::DYNAMIC) - { - SHLOG_WARNING("Cannot set mass of a non-Dynamic Body {}", entityID) - return; - } - - if (newMass < 0.0f) - { - SHLOG_WARNING("Cannot set mass below 0. Object {}'s mass will remain unchanged.", entityID) - return; - } - - invMass = 1.0f / newMass; - - // TODO: Recompute inertia tensor - } - - inline void SHRigidBody::SetLinearDrag(float newLinearDrag) noexcept - { - if (bodyType == Type::STATIC) - { - SHLOG_WARNING("Cannot set linear drag of a Static Body {}", entityID) - return; - } - - if (newLinearDrag < 0.0f) - { - SHLOG_WARNING("Cannot set drag below 0. Object {}'s linear drag will remain unchanged.", entityID) - return; - } - - linearDrag = newLinearDrag; - } - - inline void SHRigidBody::SetGravityScale(float newGravityScale) noexcept - { - gravityScale = newGravityScale; - } - - inline void SHRigidBody::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept - { - linearVelocity = newLinearVelocity; - } - - inline void SHRigidBody::SetIsActive(bool isActive) noexcept - { - static constexpr unsigned int FLAG_POS = 0; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - isActive ? flags |= VALUE : flags &= ~VALUE; - } - - inline void SHRigidBody::SetIsSleeping(bool isSleeping) noexcept - { - static constexpr unsigned int FLAG_POS = 1; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - isSleeping ? flags |= VALUE : flags &= ~VALUE; - } - - inline void SHRigidBody::SetSleepingEnabled(bool enableSleeping) noexcept - { - static constexpr unsigned int FLAG_POS = 2; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (enableSleeping) - { - flags |= VALUE; - } - else - { - flags &= ~VALUE; - // Wake the body - SetIsSleeping(false); - } - } - - inline void SHRigidBody::SetGravityEnabled(bool enableGravity) noexcept - { - static constexpr unsigned int FLAG_POS = 3; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - enableGravity ? flags |= VALUE : flags &= ~VALUE; - } - - inline void SHRigidBody::SetAutoMassEnabled(bool enableAutoMass) noexcept - { - static constexpr unsigned int FLAG_POS = 4; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (enableAutoMass) - { - flags |= VALUE; - // TODO: Compute mass based on collider geometry - } - else - { - flags &= ~VALUE; - // Use default mass of 1 - invMass = 1.0f; - } - } - - inline void SHRigidBody::SetFreezePositionX(bool freezePositionX) noexcept - { - static constexpr unsigned int FLAG_POS = 10; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezePositionX) - { - flags |= VALUE; - // Reset linear velocity along X-axis - linearVelocity.x = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - inline void SHRigidBody::SetFreezePositionY(bool freezePositionY) noexcept - { - static constexpr unsigned int FLAG_POS = 11; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezePositionY) - { - flags |= VALUE; - // Reset linear velocity along Y-axis - linearVelocity.y = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - inline void SHRigidBody::SetFreezePositionZ(bool freezePositionZ) noexcept - { - static constexpr unsigned int FLAG_POS = 12; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezePositionZ) - { - flags |= VALUE; - // Reset linear velocity along Z-axis - linearVelocity.z = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - inline void SHRigidBody::SetFreezeRotationX(bool freezeRotationX) noexcept - { - static constexpr unsigned int FLAG_POS = 13; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezeRotationX) - { - flags |= VALUE; - // Reset angular velocity along X-axis - angularVelocity.x = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - inline void SHRigidBody::SetFreezeRotationY(bool freezeRotationY) noexcept - { - static constexpr unsigned int FLAG_POS = 14; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezeRotationY) - { - flags |= VALUE; - // Reset angular velocity along Y-axis - angularVelocity.y = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - inline void SHRigidBody::SetFreezeRotationZ(bool freezeRotationZ) noexcept - { - static constexpr unsigned int FLAG_POS = 15; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezeRotationZ) - { - flags |= VALUE; - // Reset angular velocity along Z-axis - angularVelocity.z = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - /*-----------------------------------------------------------------------------------*/ - /* Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - inline void SHRigidBody::AddForce(const SHVec3& force, const SHVec3& pos) noexcept - { - if (bodyType != Type::DYNAMIC) - return; - - accumulatedForce += force; - // Compute torque when force is offset - } - - inline void SHRigidBody::AddImpulse(const SHVec3& impulse, const SHVec3& pos) noexcept - { - if (bodyType != Type::DYNAMIC) - return; - - linearVelocity += impulse * invMass; - } - - inline void SHRigidBody::ClearForces() noexcept - { - accumulatedForce = SHVec3::Zero; - } - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp new file mode 100644 index 00000000..44452868 --- /dev/null +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp @@ -0,0 +1,74 @@ +/**************************************************************************************** + * \file SHPhysicsObject.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Physics Object. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsObject.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructor & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject::SHPhysicsObject(EntityID eid) noexcept + : entityID { eid } + {} + + SHPhysicsObject::SHPhysicsObject(const SHPhysicsObject& rhs) noexcept + : entityID { rhs.entityID } + { + // Perform a deep copy of the components + *rigidBody = *rhs.rigidBody; + } + + SHPhysicsObject::SHPhysicsObject(SHPhysicsObject&& rhs) noexcept + : entityID { rhs.entityID } + { + // Perform a deep copy of the components + *rigidBody = *rhs.rigidBody; + } + + SHPhysicsObject::~SHPhysicsObject() noexcept + { + entityID = MAX_EID; + + delete rigidBody; + } + + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject& SHPhysicsObject::operator=(const SHPhysicsObject& rhs) noexcept + { + if (this == &rhs) + return *this; + + entityID = rhs.entityID; + + // Perform a deep copy of the components + *rigidBody = *rhs.rigidBody; + + return *this; + } + + SHPhysicsObject& SHPhysicsObject::operator=(SHPhysicsObject&& rhs) noexcept + { + entityID = rhs.entityID; + + // Perform a deep copy of the components + *rigidBody = *rhs.rigidBody; + + return *this; + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h new file mode 100644 index 00000000..2be5844d --- /dev/null +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h @@ -0,0 +1,52 @@ +/**************************************************************************************** + * \file SHPhysicsObject.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Physics Object. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Physics/Dynamics/SHRigidBody.h" + +namespace SHADE +{ + /*-------------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-------------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates a rigid body and a collider tied to an Entity. + */ + struct SH_API SHPhysicsObject + { + public: + /*-----------------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------------*/ + + EntityID entityID = MAX_EID; + SHRigidBody* rigidBody = nullptr; + + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject (EntityID eid) noexcept; + SHPhysicsObject (const SHPhysicsObject& rhs) noexcept; + SHPhysicsObject (SHPhysicsObject&& rhs) noexcept; + ~SHPhysicsObject () noexcept; + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject& operator=(const SHPhysicsObject& rhs) noexcept; + SHPhysicsObject& operator=(SHPhysicsObject&& rhs) noexcept; + }; +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp new file mode 100644 index 00000000..dccca97b --- /dev/null +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp @@ -0,0 +1,138 @@ +/**************************************************************************************** + * \file SHPhysicsObjectManager.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Physics Object Manager. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsObjectManager.h" + +// Project Headers +#include "Physics/Interface/SHColliderComponent.h" +#include "Physics/Interface/SHCollisionShape.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Tools/Utilities/SHUtilities.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObjectManager::EntityObjectMap& SHPhysicsObjectManager::GetPhysicsObjects() noexcept + { + return physicsObjects; + } + + const SHPhysicsObject* SHPhysicsObjectManager::GetPhysicsObject(EntityID entityID) noexcept + { + const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); + if (PHYSICS_OBJECT_ITERATOR == physicsObjects.end()) + { + SHLOG_ERROR("Cannot find physics object for entity {}!", entityID) + return nullptr; + } + + return &PHYSICS_OBJECT_ITERATOR->second; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsObjectManager::AddRigidBody(EntityID entityID) noexcept + { + SHPhysicsObject* physicsObject = nullptr; + + const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); + if (PHYSICS_OBJECT_ITERATOR == physicsObjects.end()) + physicsObject = createPhysicsObject(entityID); + else + physicsObject = &PHYSICS_OBJECT_ITERATOR->second; + + // Get the component + auto* rigidBodyComponent = SHComponentManager::GetComponent(entityID); + + // Create a new rigidbody in the physics object + // We assume none has already been made + + physicsObject->rigidBody = new SHRigidBody + { + entityID + , static_cast(rigidBodyComponent->GetType()) + }; + + // Link with the component + rigidBodyComponent->SetRigidBody(physicsObject->rigidBody); + + // TODO: Broadcast event + } + + void SHPhysicsObjectManager::RemoveRigidBody(EntityID entityID) noexcept + { + const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); + if (PHYSICS_OBJECT_ITERATOR != physicsObjects.end()) + { + SHPhysicsObject* physicsObject = &PHYSICS_OBJECT_ITERATOR->second; + + delete physicsObject->rigidBody; + physicsObject->rigidBody = nullptr; + + // Destroy empty physics objects + if (physicsObject->rigidBody == nullptr) + destroyPhysicsObject(entityID); + } + + // TODO: Broadcast event + } + + void SHPhysicsObjectManager::AddCollider(EntityID entityID) noexcept + { + // Link with the component + + // TODO: Broadcast event + } + + void SHPhysicsObjectManager::RemoveCollider(EntityID entityID) noexcept + { + // Unlink with the component + + // TODO: Broadcast event + } + + void SHPhysicsObjectManager::AddCollisionShape(EntityID entityID, uint8_t shapeID) noexcept + { + // Link with the component + + // TODO: Broadcast event + } + + void SHPhysicsObjectManager::RemoveCollisionShape(EntityID entityID, uint8_t shapeID) noexcept + { + // Unlink with the component + + // TODO: Broadcast event + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject* SHPhysicsObjectManager::createPhysicsObject(EntityID entityID) + { + const auto& NEW_OBJECT = physicsObjects.emplace(entityID, SHPhysicsObject{entityID}).first; + return &NEW_OBJECT->second; + } + + void SHPhysicsObjectManager::destroyPhysicsObject(EntityID entityID) + { + physicsObjects.erase(entityID); + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h new file mode 100644 index 00000000..17a1d3a1 --- /dev/null +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h @@ -0,0 +1,125 @@ +/**************************************************************************************** + * \file SHPhysicsObjectManager.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Physics Object Manager. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "SHPhysicsObject.h" + +namespace SHADE +{ + /*-------------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-------------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates a manager for physics objects that links raw physics components with the + * engine's components. + */ + class SHPhysicsObjectManager + { + private: + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + using EntityObjectMap = std::unordered_map; + + public: + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObjectManager() noexcept = default; + + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*-----------------------------------------------------------------------------------*/ + + [[nodiscard]] EntityObjectMap& GetPhysicsObjects () noexcept; + [[nodiscard]] const SHPhysicsObject* GetPhysicsObject (EntityID entityID) noexcept; + + /*-----------------------------------------------------------------------------------*/ + /* Member Functions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Creates a rigid body and links it with the rigid body component. + * @param entityID + * The entity to link the new rigid body to. + */ + void AddRigidBody (EntityID entityID) noexcept; + + /** + * @brief + * Destroys a rigid body and removes the link with the rigid body component. + * @param entityID + * The entity to destroy the rigid body of. + */ + void RemoveRigidBody (EntityID entityID) noexcept; + + /** + * @brief + * Creates a collider and links it with the collider component. + * @param entityID + * The entity to link the new collider to. + */ + void AddCollider (EntityID entityID) noexcept; + + /** + * @brief + * Destroys a collider and removes the link with the collider component. + * @param entityID + * The entity to destroy the collider of. + */ + void RemoveCollider (EntityID entityID) noexcept; + + /** + * @brief + * Creates a collision shape for composite colliders and links it with the + * collider component. + * @param eientityIDd + * The entity to create a collision shape for. + * @param shapeID + * The id of the shape being created. + */ + void AddCollisionShape (EntityID entityID, uint8_t shapeID) noexcept; + + /** + * @brief + * Destroys a collision shape for composite colliders and removes the link with the + * collider component. + * @param entityID + * The entity to destroy the collision shape of. + * @param shapeID + * The id of the shape being destroyed. + */ + void RemoveCollisionShape (EntityID entityID, uint8_t shapeID) noexcept; + + private: + /*-----------------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------------*/ + + EntityObjectMap physicsObjects; + + /*-----------------------------------------------------------------------------------*/ + /* Member Functions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject* createPhysicsObject (EntityID entityID); + void destroyPhysicsObject (EntityID entityID); + }; + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp similarity index 59% rename from SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp rename to SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp index 6dad9020..5924c612 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp @@ -18,7 +18,7 @@ // Project Headers #include "ECS_Base/Managers/SHSystemManager.h" -#include "Math/SHMathHelpers.h" +#include "Physics/Dynamics/SHRigidBody.h" #include "Physics/System/SHPhysicsSystem.h" namespace SHADE @@ -28,77 +28,127 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHRigidBodyComponent::SHRigidBodyComponent() noexcept + : type { Type::STATIC } + , interpolate { true } + , rigidBody { nullptr } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHRigidBodyComponent::Type SHRigidBodyComponent::GetType() const noexcept { - + return type; } - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - bool SHRigidBodyComponent::IsGravityEnabled() const noexcept { + if (rigidBody) + return rigidBody->IsGravityEnabled(); + return false; } bool SHRigidBodyComponent::IsAllowedToSleep() const noexcept { + if (rigidBody) + return rigidBody->IsSleepingEnabled(); + return false; } bool SHRigidBodyComponent::IsInterpolating() const noexcept { + return interpolate; + } + + bool SHRigidBodyComponent::IsSleeping() const noexcept + { + if (rigidBody) + return rigidBody->IsSleeping(); + return false; } - bool SHRigidBodyComponent::GetIsSleeping() const noexcept + bool SHRigidBodyComponent::GetAutoMass() const noexcept { - return false; - } + if (rigidBody) + return rigidBody->IsAutoMassEnabled(); - SHRigidBodyComponent::Type SHRigidBodyComponent::GetType() const noexcept - { - return Type::STATIC; + return false; } bool SHRigidBodyComponent::GetFreezePositionX() const noexcept { + if (rigidBody) + return rigidBody->GetFreezePositionX(); + return false; } bool SHRigidBodyComponent::GetFreezePositionY() const noexcept { + if (rigidBody) + return rigidBody->GetFreezePositionY(); + return false; } bool SHRigidBodyComponent::GetFreezePositionZ() const noexcept { + if (rigidBody) + return rigidBody->GetFreezePositionZ(); + return false; } bool SHRigidBodyComponent::GetFreezeRotationX() const noexcept { + if (rigidBody) + return rigidBody->GetFreezeRotationX(); + return false; } bool SHRigidBodyComponent::GetFreezeRotationY() const noexcept { + if (rigidBody) + return rigidBody->GetFreezeRotationY(); + return false; } bool SHRigidBodyComponent::GetFreezeRotationZ() const noexcept { + if (rigidBody) + return rigidBody->GetFreezeRotationZ(); + return false; } - float SHRigidBodyComponent::GetMass() const noexcept + float SHRigidBodyComponent::GetGravityScale() const noexcept { + if (rigidBody) + return rigidBody->GetGravityScale(); + return 0.0f; } + float SHRigidBodyComponent::GetMass() const noexcept + { + if (rigidBody) + return rigidBody->GetMass(); + + return -1.0f; + } + float SHRigidBodyComponent::GetDrag() const noexcept { - return 0.0f; + if (rigidBody) + return rigidBody->GetLinearDrag(); + + return -1.0f; } float SHRigidBodyComponent::GetAngularDrag() const noexcept @@ -108,6 +158,9 @@ namespace SHADE SHVec3 SHRigidBodyComponent::GetForce() const noexcept { + if (rigidBody) + return rigidBody->GetAccumulatedForce(); + return SHVec3::Zero; } @@ -118,6 +171,9 @@ namespace SHADE SHVec3 SHRigidBodyComponent::GetLinearVelocity() const noexcept { + if (rigidBody) + return rigidBody->GetLinearVelocity(); + return SHVec3::Zero; } @@ -127,91 +183,126 @@ namespace SHADE } /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ + /* Setter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ void SHRigidBodyComponent::SetType(Type newType) noexcept { - + if (newType == type) + return; + + type = newType; + + if (rigidBody) + rigidBody->SetType(static_cast(newType)); } - void SHRigidBodyComponent::SetGravityEnabled(bool enableGravity) noexcept + void SHRigidBodyComponent::SetRigidBody(SHRigidBody* rb) noexcept { - + rigidBody = rb; + } + + void SHRigidBodyComponent::SetIsGravityEnabled(bool enableGravity) noexcept + { + if (rigidBody) + rigidBody->SetGravityEnabled(enableGravity); } void SHRigidBodyComponent::SetIsAllowedToSleep(bool isAllowedToSleep) noexcept { - + if (rigidBody) + rigidBody->SetSleepingEnabled(isAllowedToSleep); + } + + void SHRigidBodyComponent::SetAutoMass(bool autoMass) noexcept + { + if (rigidBody) + rigidBody->SetAutoMassEnabled(autoMass); } void SHRigidBodyComponent::SetFreezePositionX(bool freezePositionX) noexcept { - + if (rigidBody) + rigidBody->SetFreezePositionX(freezePositionX); } void SHRigidBodyComponent::SetFreezePositionY(bool freezePositionY) noexcept { - + if (rigidBody) + rigidBody->SetFreezePositionY(freezePositionY); } void SHRigidBodyComponent::SetFreezePositionZ(bool freezePositionZ) noexcept { - + if (rigidBody) + rigidBody->SetFreezePositionZ(freezePositionZ); } void SHRigidBodyComponent::SetFreezeRotationX(bool freezeRotationX) noexcept { - + if (rigidBody) + rigidBody->SetFreezeRotationX(freezeRotationX); } void SHRigidBodyComponent::SetFreezeRotationY(bool freezeRotationY) noexcept { - + if (rigidBody) + rigidBody->SetFreezeRotationY(freezeRotationY); } void SHRigidBodyComponent::SetFreezeRotationZ(bool freezeRotationZ) noexcept { - + if (rigidBody) + rigidBody->SetFreezeRotationZ(freezeRotationZ); } void SHRigidBodyComponent::SetInterpolate(bool allowInterpolation) noexcept { - + interpolate = allowInterpolation; + } + + void SHRigidBodyComponent::SetGravityScale(float gravityScale) noexcept + { + if (rigidBody) + rigidBody->SetGravityScale(gravityScale); + } + + void SHRigidBodyComponent::SetMass(float newMass) noexcept + { + if (rigidBody) + rigidBody->SetMass(newMass); } void SHRigidBodyComponent::SetDrag(float newDrag) noexcept { - + if (rigidBody) + rigidBody->SetLinearDrag(newDrag); } void SHRigidBodyComponent::SetAngularDrag(float newAngularDrag) noexcept { - + } void SHRigidBodyComponent::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept { - + if (rigidBody) + rigidBody->SetLinearVelocity(newLinearVelocity); } void SHRigidBodyComponent::SetAngularVelocity(const SHVec3& newAngularVelocity) noexcept { - + } /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ + /* Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHRigidBodyComponent::OnCreate() - { - - } - void SHRigidBodyComponent::AddForce(const SHVec3& force) const noexcept { - + if (rigidBody) + rigidBody->AddForce(force); } void SHRigidBodyComponent::AddForceAtLocalPos(const SHVec3& force, const SHVec3& localPos) const noexcept @@ -221,12 +312,13 @@ namespace SHADE void SHRigidBodyComponent::AddForceAtWorldPos(const SHVec3& force, const SHVec3& worldPos) const noexcept { - + if (rigidBody) + rigidBody->AddForce(force, worldPos); } void SHRigidBodyComponent::AddRelativeForce(const SHVec3& relativeForce) const noexcept { - + } void SHRigidBodyComponent::AddRelativeForceAtLocalPos(const SHVec3& relativeForce, const SHVec3& localPos) const noexcept @@ -251,7 +343,8 @@ namespace SHADE void SHRigidBodyComponent::ClearForces() const noexcept { - + if (rigidBody) + rigidBody->ClearForces(); } void SHRigidBodyComponent::ClearTorque() const noexcept @@ -259,6 +352,15 @@ namespace SHADE } + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHRigidBodyComponent::OnCreate() + { + + } + } // namespace SHADE RTTR_REGISTRATION @@ -274,17 +376,19 @@ RTTR_REGISTRATION ); registration::class_("RigidBody Component") - .property("Type" , &SHRigidBodyComponent::GetType , &SHRigidBodyComponent::SetType ) - //.property("Mass" , &SHRigidBodyComponent::GetMass , &SHRigidBodyComponent::SetMass ) - .property("Drag" , &SHRigidBodyComponent::GetDrag , &SHRigidBodyComponent::SetDrag ) - .property("Angular Drag" , &SHRigidBodyComponent::GetAngularDrag , &SHRigidBodyComponent::SetAngularDrag ) - .property("Use Gravity" , &SHRigidBodyComponent::IsGravityEnabled , &SHRigidBodyComponent::SetGravityEnabled ) - .property("Interpolate" , &SHRigidBodyComponent::IsInterpolating , &SHRigidBodyComponent::SetInterpolate ) - .property("Sleeping Enabled" , &SHRigidBodyComponent::IsAllowedToSleep , &SHRigidBodyComponent::SetIsAllowedToSleep) - .property("Freeze Position X" , &SHRigidBodyComponent::GetFreezePositionX , &SHRigidBodyComponent::SetFreezePositionX ) - .property("Freeze Position Y" , &SHRigidBodyComponent::GetFreezePositionY , &SHRigidBodyComponent::SetFreezePositionY ) - .property("Freeze Position Z" , &SHRigidBodyComponent::GetFreezePositionZ , &SHRigidBodyComponent::SetFreezePositionZ ) - .property("Freeze Rotation X" , &SHRigidBodyComponent::GetFreezeRotationX , &SHRigidBodyComponent::SetFreezeRotationX ) - .property("Freeze Rotation Y" , &SHRigidBodyComponent::GetFreezeRotationY , &SHRigidBodyComponent::SetFreezeRotationY ) - .property("Freeze Rotation Z" , &SHRigidBodyComponent::GetFreezeRotationZ , &SHRigidBodyComponent::SetFreezeRotationZ ); + .property("Type" , &SHRigidBodyComponent::GetType , &SHRigidBodyComponent::SetType ) + .property("Auto Mass" , &SHRigidBodyComponent::GetAutoMass , &SHRigidBodyComponent::SetAutoMass ) + .property("Mass" , &SHRigidBodyComponent::GetMass , &SHRigidBodyComponent::SetMass ) + .property("Drag" , &SHRigidBodyComponent::GetDrag , &SHRigidBodyComponent::SetDrag ) + .property("Angular Drag" , &SHRigidBodyComponent::GetAngularDrag , &SHRigidBodyComponent::SetAngularDrag ) + .property("Use Gravity" , &SHRigidBodyComponent::IsGravityEnabled , &SHRigidBodyComponent::SetIsGravityEnabled ) + .property("Gravity Scale" , &SHRigidBodyComponent::GetGravityScale , &SHRigidBodyComponent::SetGravityScale ) + .property("Interpolate" , &SHRigidBodyComponent::IsInterpolating , &SHRigidBodyComponent::SetInterpolate ) + .property("Sleeping Enabled" , &SHRigidBodyComponent::IsAllowedToSleep , &SHRigidBodyComponent::SetIsAllowedToSleep ) + .property("Freeze Position X" , &SHRigidBodyComponent::GetFreezePositionX , &SHRigidBodyComponent::SetFreezePositionX ) + .property("Freeze Position Y" , &SHRigidBodyComponent::GetFreezePositionY , &SHRigidBodyComponent::SetFreezePositionY ) + .property("Freeze Position Z" , &SHRigidBodyComponent::GetFreezePositionZ , &SHRigidBodyComponent::SetFreezePositionZ ) + .property("Freeze Rotation X" , &SHRigidBodyComponent::GetFreezeRotationX , &SHRigidBodyComponent::SetFreezeRotationX ) + .property("Freeze Rotation Y" , &SHRigidBodyComponent::GetFreezeRotationY , &SHRigidBodyComponent::SetFreezeRotationY ) + .property("Freeze Rotation Z" , &SHRigidBodyComponent::GetFreezeRotationZ , &SHRigidBodyComponent::SetFreezeRotationZ ); } \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h similarity index 88% rename from SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h rename to SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h index 021743da..ae0bd3ec 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h @@ -15,10 +15,15 @@ // Project Headers #include "ECS_Base/Components/SHComponent.h" #include "Math/Vector/SHVec3.h" -#include "Math/SHQuaternion.h" namespace SHADE { + /*-------------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-------------------------------------------------------------------------------------*/ + + class SHRigidBody; + /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -67,13 +72,13 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ + [[nodiscard]] Type GetType () const noexcept; + [[nodiscard]] bool IsGravityEnabled () const noexcept; [[nodiscard]] bool IsAllowedToSleep () const noexcept; [[nodiscard]] bool IsInterpolating () const noexcept; - - [[nodiscard]] bool GetIsSleeping () const noexcept; - - [[nodiscard]] Type GetType () const noexcept; + [[nodiscard]] bool IsSleeping () const noexcept; + [[nodiscard]] bool GetAutoMass () const noexcept; [[nodiscard]] bool GetFreezePositionX () const noexcept; [[nodiscard]] bool GetFreezePositionY () const noexcept; @@ -82,6 +87,7 @@ namespace SHADE [[nodiscard]] bool GetFreezeRotationY () const noexcept; [[nodiscard]] bool GetFreezeRotationZ () const noexcept; + [[nodiscard]] float GetGravityScale () const noexcept; [[nodiscard]] float GetMass () const noexcept; [[nodiscard]] float GetDrag () const noexcept; [[nodiscard]] float GetAngularDrag () const noexcept; @@ -91,18 +97,18 @@ namespace SHADE [[nodiscard]] SHVec3 GetLinearVelocity () const noexcept; [[nodiscard]] SHVec3 GetAngularVelocity () const noexcept; - //[[nodiscard]] const SHVec3& GetPosition () const noexcept; - //[[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; - //[[nodiscard]] SHVec3 GetRotation () const noexcept; - /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ void SetType (Type newType) noexcept; - void SetGravityEnabled (bool enableGravity) noexcept; + void SetRigidBody (SHRigidBody* rb) noexcept; + + void SetIsGravityEnabled (bool enableGravity) noexcept; void SetIsAllowedToSleep (bool isAllowedToSleep) noexcept; + void SetAutoMass (bool autoMass) noexcept; + void SetFreezePositionX (bool freezePositionX) noexcept; void SetFreezePositionY (bool freezePositionY) noexcept; void SetFreezePositionZ (bool freezePositionZ) noexcept; @@ -110,9 +116,9 @@ namespace SHADE void SetFreezeRotationY (bool freezeRotationY) noexcept; void SetFreezeRotationZ (bool freezeRotationZ) noexcept; void SetInterpolate (bool allowInterpolation) noexcept; - //void SetAutoMass (bool autoMass) noexcept; - //void SetMass (float newMass) noexcept; + void SetGravityScale (float gravityScale) noexcept; + void SetMass (float newMass) noexcept; void SetDrag (float newDrag) noexcept; void SetAngularDrag (float newAngularDrag) noexcept; @@ -142,6 +148,11 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ + Type type; + bool interpolate; + SHRigidBody* rigidBody; + RTTR_ENABLE() }; + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp index 85e76702..d54d3f86 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp @@ -19,53 +19,9 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHPhysicsWorldState::SHPhysicsWorldState() noexcept - : world { nullptr } - {} /*-----------------------------------------------------------------------------------*/ /* Public Function Members Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsWorldState::CreateWorld(rp3d::PhysicsCommon& factory) - { - rp3d::PhysicsWorld::WorldSettings rp3dWorldSettings; - rp3dWorldSettings.gravity = settings.gravity; - rp3dWorldSettings.defaultVelocitySolverNbIterations = settings.numVelocitySolverIterations; - rp3dWorldSettings.defaultPositionSolverNbIterations = settings.numPositionSolverIterations; - rp3dWorldSettings.isSleepingEnabled = settings.sleepingEnabled; - - // These are my preferred default values. QoL for modifying these. - rp3dWorldSettings.defaultBounciness = 0.0f; - rp3dWorldSettings.defaultFrictionCoefficient = 0.4f; - - world = factory.createPhysicsWorld(rp3dWorldSettings); - world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::SPLIT_IMPULSES); - } - - void SHPhysicsWorldState::DestroyWorld(rp3d::PhysicsCommon& factory) - { - if (!world) - return; - - factory.destroyPhysicsWorld(world); - world = nullptr; - } - - void SHPhysicsWorldState::UpdateSettings() const noexcept - { - if (!world) - { - SHLOGV_ERROR("Unable to update Physics World settings without creating a world!") - return; - } - - world->setGravity(settings.gravity); - world->setNbIterationsVelocitySolver(settings.numVelocitySolverIterations); - world->setNbIterationsPositionSolver(settings.numPositionSolverIterations); - world->enableSleeping(settings.sleepingEnabled); - } - - - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/SHPhysicsWorld.h index c5152c44..e872cd3c 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/SHPhysicsWorld.h @@ -10,8 +10,6 @@ #pragma once -#include - // Project Headers #include "Math/SHMath.h" #include "SH_API.h" @@ -47,7 +45,6 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - rp3d::PhysicsWorld* world; WorldSettings settings; /*---------------------------------------------------------------------------------*/ @@ -60,14 +57,6 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - void CreateWorld (rp3d::PhysicsCommon& factory); - void DestroyWorld (rp3d::PhysicsCommon& factory); - - /** - * @brief Applies the current settings to the physics world. The world must be created - * before this is called. - */ - void UpdateSettings () const noexcept; }; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp similarity index 50% rename from SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp rename to SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp index 0029a8e7..9b1c0015 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHPhysicsSystemRoutines.h + * \file SHPhysicsPostUpdateRoutine.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Physics System Routines + * \brief Implementation for the Physics Post-Update Routine * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -11,24 +11,12 @@ #include // Primary Header -#include "SHPhysicsSystem.h" +#include "Physics/System/SHPhysicsSystem.h" + // Project Headers -#include "ECS_Base/Managers/SHEntityManager.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Editor/SHEditor.h" -#include "Scene/SHSceneManager.h" #include "Scripting/SHScriptEngine.h" -#include "Input/SHInputManager.h" -#include "Physics/Collision/SHCollisionTagMatrix.h" - -/*-------------------------------------------------------------------------------------*/ -/* Local Functions */ -/*-------------------------------------------------------------------------------------*/ - -void testFunction(); - -///////////////////////////////////////////////////////////////////////////////////////// namespace SHADE { @@ -36,59 +24,14 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHPhysicsSystem::PhysicsPreUpdate::PhysicsPreUpdate() - : SHSystemRoutine { "Physics PreUpdate", true } - {} - - SHPhysicsSystem::PhysicsFixedUpdate::PhysicsFixedUpdate() - : SHFixedSystemRoutine { DEFAULT_FIXED_STEP, "Physics FixedUpdate", false } - {} - SHPhysicsSystem::PhysicsPostUpdate::PhysicsPostUpdate() - : SHSystemRoutine { "Physics PostUpdate", false } + : SHSystemRoutine { "Physics Post-Update", false } {} /*-----------------------------------------------------------------------------------*/ /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsSystem::PhysicsPreUpdate::Execute(double) noexcept - { - auto* physicsSystem = reinterpret_cast(GetSystem()); - } - - void SHPhysicsSystem::PhysicsFixedUpdate::Execute(double dt) noexcept - { - auto* physicsSystem = reinterpret_cast(GetSystem()); - - auto* scriptingSystem = SHSystemManager::GetSystem(); - if (scriptingSystem == nullptr) - { - SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!"); - } - - const double FIXED_DT = physicsSystem->fixedDT; - - accumulatedTime += dt; - - int count = 0; - while (accumulatedTime > FIXED_DT) - { - if (scriptingSystem != nullptr) - scriptingSystem->ExecuteFixedUpdates(); - - // TODO: Update World - - accumulatedTime -= FIXED_DT; - ++count; - } - - stats.numSteps = count; - physicsSystem->worldUpdated = count > 0; - - physicsSystem->interpolationFactor = accumulatedTime / fixedTimeStep; - } - void SHPhysicsSystem::PhysicsPostUpdate::Execute(double) noexcept { auto* physicsSystem = reinterpret_cast(GetSystem()); @@ -110,6 +53,10 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ -} // namespace SHADE + void SHPhysicsSystem::PhysicsPostUpdate::syncTransforms(SHRigidBodyComponent* rbComponent) const noexcept + { + + } +} diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp new file mode 100644 index 00000000..a0f348f4 --- /dev/null +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -0,0 +1,62 @@ +/**************************************************************************************** + * \file SHPhysicsPreUpdateRoutine.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Physics Pre-Update Routine + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "Physics/System/SHPhysicsSystem.h" + +// Project Headers +#include "ECS_Base/Managers/SHComponentManager.h" +#include "Math/Transform/SHTransformComponent.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsSystem::PhysicsPreUpdate::PhysicsPreUpdate() + : SHSystemRoutine { "Physics Pre-Update", true } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsSystem::PhysicsPreUpdate::Execute(double) noexcept + { + auto* physicsSystem = reinterpret_cast(GetSystem()); + + // Get all physics objects & sync transforms + for (auto& physicsObject : physicsSystem->physicsObjectManager.GetPhysicsObjects() | std::views::values) + syncTransforms(&physicsObject); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsSystem::PhysicsPreUpdate::syncTransforms(SHPhysicsObject* physicsObject) const noexcept + { + const EntityID EID = physicsObject->entityID; + + // Get relevant components: Transform, Rigidbody & Collider + const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(EID); + + auto* rigidBodyComponent = SHComponentManager::GetComponent_s(EID); + auto* colliderComponent = SHComponentManager::GetComponent_s(EID); + + if (TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged()) + { + // Sync the objects transforms + } + } +} diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp new file mode 100644 index 00000000..77da8706 --- /dev/null +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp @@ -0,0 +1,62 @@ +/**************************************************************************************** + * \file SHPhysicsUpdateRoutine.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Physics Update Routine + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "Physics/System/SHPhysicsSystem.h" + +// Project Headers +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Scripting/SHScriptEngine.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsSystem::PhysicsUpdate::PhysicsUpdate() + : SHFixedSystemRoutine { DEFAULT_FIXED_STEP, "Physics Update", false } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsSystem::PhysicsUpdate::Execute(double dt) noexcept + { + auto* physicsSystem = reinterpret_cast(GetSystem()); + + auto* scriptEngine = SHSystemManager::GetSystem(); + if (!scriptEngine) + { + SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing ScriptEngine!") + } + + const double FIXED_DT = physicsSystem->fixedDT; + accumulatedTime += dt; + + int count = 0; + while (accumulatedTime > FIXED_DT) + { + + + accumulatedTime -= FIXED_DT; + ++count; + } + + stats.numSteps = count; + physicsSystem->worldUpdated = count > 0; + + physicsSystem->interpolationFactor = physicsSystem->worldUpdated ? accumulatedTime / FIXED_DT : 0.0; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index eacd6ac9..98a69087 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -20,9 +20,7 @@ #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.h" #include "Physics/Collision/SHCollisionTagMatrix.h" -#include "Physics/SHPhysicsEvents.h" -#include "Scene/SHSceneManager.h" -#include "Scripting/SHScriptEngine.h" +#include "Physics/Interface/SHColliderComponent.h" namespace SHADE { @@ -34,7 +32,18 @@ namespace SHADE : worldUpdated { false } , interpolationFactor { 0.0 } , fixedDT { DEFAULT_FIXED_STEP } - {} + { + // Add more events here to register them + + eventFunctions[0] = { &SHPhysicsSystem::onComponentAdded , SH_COMPONENT_ADDED_EVENT }; + eventFunctions[1] = { &SHPhysicsSystem::onComponentRemoved, SH_COMPONENT_REMOVED_EVENT }; + eventFunctions[2] = { &SHPhysicsSystem::onSceneInit , SH_SCENE_ON_INIT_EVENT }; + eventFunctions[3] = { &SHPhysicsSystem::onSceneExit , SH_SCENE_ON_EXIT_EVENT }; + + #ifdef SHEDITOR + eventFunctions[4] = { &SHPhysicsSystem::onEditorPlay , SH_EDITOR_ON_PLAY_EVENT }; + #endif + } /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ @@ -45,13 +54,48 @@ namespace SHADE return 1.0 / fixedDT; } + double SHPhysicsSystem::GetFixedDT() const noexcept + { + return fixedDT; + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsSystem::SetFixedUpdateRate(double fixedUpdateRate) noexcept + void SHPhysicsSystem::SetFixedUpdateRate(double fixedUpdateRate) noexcept { + // Handle invalid input + if (fixedUpdateRate <= 0.0) + { + SHLOGV_WARNING("Invalid value for setting fixed update rate! Fixed update rate unchanged.") + return; + } + fixedDT = 1.0 / fixedUpdateRate; + + // Handle potential incorrect / unintended input + if (fixedDT > 1.0) + { + SHLOGV_WARNING("Fixed Update Rate Time is set below 1. This may result in undesirable behaviour.") + } + } + + void SHPhysicsSystem::SetFixedDT(double fixedDt) noexcept + { + if (fixedDt <= 0.0) + { + SHLOGV_WARNING("Invalid value for setting fixed delta time! Fixed delta time unchanged.") + return; + } + + fixedDT = fixedDt; + + // Handle potential incorrect / unintended input + if (fixedDT > 1.0) + { + SHLOGV_WARNING("Fixed Delta Time is set above 1. This may result in undesirable behaviour.") + } } /*-----------------------------------------------------------------------------------*/ @@ -64,11 +108,21 @@ namespace SHADE std::filesystem::path defaultCollisionTagNameFilePath { ASSET_ROOT }; defaultCollisionTagNameFilePath.append("CollisionTags.SHConfig"); SHCollisionTagMatrix::Init(defaultCollisionTagNameFilePath); + + // Link Managers to system + + // Register Events + for (int i = 0; i < NUM_EVENT_FUNCTIONS; ++i) + { + const std::shared_ptr EVENT_RECEIVER = std::make_shared>(this, eventFunctions[i].first); + const ReceiverPtr EVENT_RECEIVER_PTR = std::dynamic_pointer_cast(EVENT_RECEIVER); + + SHEventManager::SubscribeTo(eventFunctions[i].second, EVENT_RECEIVER_PTR); + } } void SHPhysicsSystem::Exit() { - // Write collision tag names to file std::filesystem::path defaultCollisionTagNameFilePath { ASSET_ROOT }; defaultCollisionTagNameFilePath.append("CollisionTags.SHConfig"); @@ -84,4 +138,98 @@ namespace SHADE /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ + SHEventHandle SHPhysicsSystem::onSceneInit(SHEventPtr onSceneInitEvent) + { + /* + * TODO: + * If a world already exists, destroy it + * Recreate the world + * + * If there is an editor and editor mode is already playing, link all entities with the world immediately. + */ + + return onSceneInitEvent.get()->handle; + } + + SHEventHandle SHPhysicsSystem::onSceneExit(SHEventPtr onSceneExitEvent) + { + /* + * TODO: + * Destroy the physics world. + * Destroy all physics objects. + */ + + return onSceneExitEvent.get()->handle; + } + + SHEventHandle SHPhysicsSystem::onComponentAdded(SHEventPtr onComponentAddedEvent) + { + static const auto RIGID_BODY_COMPONENT_ID = ComponentFamily::GetID(); + static const auto COLLIDER_COMPONENT_ID = ComponentFamily::GetID(); + + const auto& EVENT_DATA = reinterpret_cast*>(onComponentAddedEvent.get())->data; + + const auto ADDED_ID = EVENT_DATA->addedComponentType; + + const bool IS_RIGID_BODY = ADDED_ID == RIGID_BODY_COMPONENT_ID; + const bool IS_COLLIDER = ADDED_ID == COLLIDER_COMPONENT_ID; + + // Check if its a physics component + if (IS_RIGID_BODY || IS_COLLIDER) + { + const EntityID EID = EVENT_DATA->eid; + + // Link engine components with physics object component + if (IS_RIGID_BODY) + physicsObjectManager.AddRigidBody(EID); + + if (IS_COLLIDER) + physicsObjectManager.AddCollider(EID); + } + + return onComponentAddedEvent.get()->handle; + } + + SHEventHandle SHPhysicsSystem::onComponentRemoved(SHEventPtr onComponentRemovedEvent) + { + static const auto RIGID_BODY_COMPONENT_ID = ComponentFamily::GetID(); + static const auto COLLIDER_COMPONENT_ID = ComponentFamily::GetID(); + + const auto& EVENT_DATA = reinterpret_cast*>(onComponentRemovedEvent.get())->data; + + const auto REMOVED_DATA = EVENT_DATA->removedComponentType; + + const bool IS_RIGID_BODY = REMOVED_DATA == RIGID_BODY_COMPONENT_ID; + const bool IS_COLLIDER = REMOVED_DATA == COLLIDER_COMPONENT_ID; + + // Check if its a physics component + if (IS_RIGID_BODY || IS_COLLIDER) + { + const EntityID EID = EVENT_DATA->eid; + + // Link engine components with physics object component + if (IS_RIGID_BODY) + physicsObjectManager.RemoveRigidBody(EID); + + if (IS_COLLIDER) + physicsObjectManager.RemoveCollider(EID); + } + + return onComponentRemovedEvent.get()->handle; + } + +#ifdef SHEDITOR + + SHEventHandle SHPhysicsSystem::onEditorPlay(SHEventPtr onEditorPlayEvent) + { + /* + * TODO: + * Link all entities with the world. + */ + + return onEditorPlayEvent.get()->handle; + } + +#endif + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index 3fe05c09..1562241f 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -11,14 +11,15 @@ #pragma once // Project Headers +#include "ECS_Base/System/SHSystem.h" #include "ECS_Base/System/SHSystemRoutine.h" #include "ECS_Base/System/SHFixedSystemRoutine.h" +#include "Events/SHEvent.h" -#include "Math/Transform/SHTransformComponent.h" -#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" -#include "Physics/SHPhysicsWorld.h" -#include "Scene/SHSceneGraph.h" + namespace SHADE { @@ -28,13 +29,6 @@ namespace SHADE class SH_API SHPhysicsSystem final : public SHSystem { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHPhysicsDebugDrawSystem; - public: /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -46,104 +40,121 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] double GetFixedUpdateRate () const noexcept; - [[nodiscard]] const SHPhysicsWorldState::WorldSettings& GetWorldSettings () const noexcept; + [[nodiscard]] double GetFixedUpdateRate() const noexcept; + [[nodiscard]] double GetFixedDT() const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetFixedUpdateRate (double fixedUpdateRate) noexcept; - void SetWorldSettings (const SHPhysicsWorldState::WorldSettings& settings) noexcept; + void SetFixedUpdateRate(double fixedUpdateRate) noexcept; + void SetFixedDT(double fixedDt) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ - void Init () override; - void Exit () override; + void Init() override; + void Exit() override; - void ForceUpdate (); + void ForceUpdate(); /*---------------------------------------------------------------------------------*/ /* System Routines */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * The physics update routine that runs before the simulation is updated. + * This is always running, regardless of the editor state. + *
+ * This update game logic is applied before the simulation runs. + */ class SH_API PhysicsPreUpdate final : public SHSystemRoutine { public: - /*-------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-------------------------------------------------------------------------------*/ - PhysicsPreUpdate(); - - /*-------------------------------------------------------------------------------*/ - /* Function Members */ - /*-------------------------------------------------------------------------------*/ - void Execute(double dt) noexcept override; private: - /*-------------------------------------------------------------------------------*/ - /* Function Members */ - /*-------------------------------------------------------------------------------*/ - + void syncTransforms(SHPhysicsObject* physicsObject) const noexcept; }; - class SH_API PhysicsFixedUpdate final : public SHFixedSystemRoutine + /** + * @brief + * The physics update routine that runs at a fixed rate. This is where the main + * simulation runs. If delta time is large enough, this may run more than once per + * frame. If delta time is small enough, this may not run at all. + */ + class SH_API PhysicsUpdate final : public SHFixedSystemRoutine { public: - /*-------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-------------------------------------------------------------------------------*/ - - PhysicsFixedUpdate(); - - /*-------------------------------------------------------------------------------*/ - /* Function Members */ - /*-------------------------------------------------------------------------------*/ - - void Execute (double dt) noexcept override; + PhysicsUpdate(); + void Execute(double dt) noexcept override; }; + /** + * @brief + * The physics update that runs after the simulation. This sets the rendering + * transforms and sends messages to scripting system for collision & trigger events. + */ class SH_API PhysicsPostUpdate final : public SHSystemRoutine { public: - /*-------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-------------------------------------------------------------------------------*/ - PhysicsPostUpdate(); - - /*-------------------------------------------------------------------------------*/ - /* Function Members */ - /*-------------------------------------------------------------------------------*/ - void Execute(double dt) noexcept override; private: - - /*-------------------------------------------------------------------------------*/ - /* Function Members */ - /*-------------------------------------------------------------------------------*/ - + void syncTransforms(SHRigidBodyComponent* rbComponent) const noexcept; }; private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using EventFunctionPair = std::pair; + /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ + #ifdef SHEDITOR + static constexpr int NUM_EVENT_FUNCTIONS = 5; + #else + static constexpr int NUM_EVENT_FUNCTIONS = 4; + #endif + + // Event function container for cleanly registering to events + EventFunctionPair eventFunctions[NUM_EVENT_FUNCTIONS]; + // System data bool worldUpdated; double interpolationFactor; double fixedDT; + // Sub-systems / managers + + SHPhysicsObjectManager physicsObjectManager; + /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ + SHEventHandle onSceneInit (SHEventPtr onSceneInitEvent); + SHEventHandle onSceneExit (SHEventPtr onSceneExitEvent); + + SHEventHandle onComponentAdded (SHEventPtr onComponentAddedEvent); + SHEventHandle onComponentRemoved (SHEventPtr onComponentRemovedEvent); + + + #ifdef SHEDITOR + + SHEventHandle onEditorPlay (SHEventPtr onEditorPlayEvent); + // We don't need an onEditorStop because on stop exits the scene, which is already handled above. + + #endif + }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index 99e4fa41..9d648185 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -15,7 +15,7 @@ #include "Camera/SHCameraArmComponent.h" #include "Math/Transform/SHTransformComponent.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h" -#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "UI/SHCanvasComponent.h" #include "UI/SHButtonComponent.h" #include "ECS_Base/Managers/SHSystemManager.h" diff --git a/SHADE_Managed/src/Components/RigidBody.cxx b/SHADE_Managed/src/Components/RigidBody.cxx index a564402f..7b3604f2 100644 --- a/SHADE_Managed/src/Components/RigidBody.cxx +++ b/SHADE_Managed/src/Components/RigidBody.cxx @@ -34,7 +34,7 @@ namespace SHADE } void RigidBody::IsGravityEnabled::set(bool value) { - return GetNativeComponent()->SetGravityEnabled(value); + return GetNativeComponent()->SetIsGravityEnabled(value); } bool RigidBody::IsAllowedToSleep::get() { diff --git a/SHADE_Managed/src/Components/RigidBody.hxx b/SHADE_Managed/src/Components/RigidBody.hxx index 8bfe34aa..6ca39316 100644 --- a/SHADE_Managed/src/Components/RigidBody.hxx +++ b/SHADE_Managed/src/Components/RigidBody.hxx @@ -15,7 +15,7 @@ of DigiPen Institute of Technology is prohibited. #pragma once // External Dependencies -#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" // Project Includes #include "Components/Component.hxx" diff --git a/SHADE_Managed/src/Engine/ECS.cxx b/SHADE_Managed/src/Engine/ECS.cxx index c388f0cd..3ace79ab 100644 --- a/SHADE_Managed/src/Engine/ECS.cxx +++ b/SHADE_Managed/src/Engine/ECS.cxx @@ -23,7 +23,7 @@ of DigiPen Institute of Technology is prohibited. #include "ECS_Base/Managers/SHEntityManager.h" #include "Math/Transform/SHTransformComponent.h" #include "Physics/Interface/SHColliderComponent.h" -#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "Scene/SHSceneManager.h" #include "Scene/SHSceneGraph.h" #include "Tools/Logger/SHLog.h" From 36ceec5855faa477d292e9e105ca37a5a93e9ed2 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 5 Dec 2022 00:19:48 +0800 Subject: [PATCH 010/164] Added SceneInit & SceneExit events --- SHADE_Engine/src/Scene/SHSceneEvents.h | 10 +++ SHADE_Engine/src/Scene/SHSceneGraph.cpp | 46 ++++++------- SHADE_Engine/src/Scene/SHSceneGraph.h | 81 +++++++++++++++++++---- SHADE_Engine/src/Scene/SHSceneManager.cpp | 9 ++- SHADE_Engine/src/Scene/SHSceneNode.h | 13 ++-- 5 files changed, 115 insertions(+), 44 deletions(-) diff --git a/SHADE_Engine/src/Scene/SHSceneEvents.h b/SHADE_Engine/src/Scene/SHSceneEvents.h index c0d7dbc1..e76894bd 100644 --- a/SHADE_Engine/src/Scene/SHSceneEvents.h +++ b/SHADE_Engine/src/Scene/SHSceneEvents.h @@ -38,4 +38,14 @@ namespace SHADE SHSceneNode* childRemoved = nullptr; }; + struct SHSceneInitEvent + { + uint32_t sceneID = std::numeric_limits::max(); + }; + + struct SHSceneExitEvent + { + uint32_t sceneID = std::numeric_limits::max(); + }; + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index 6240b7bf..c040808f 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -27,7 +27,7 @@ namespace SHADE : root { nullptr } { // The root is set to the maximum entity. It should not be interfaced with. - root = AllocateNode(MAX_EID); + root = allocateNode(MAX_EID); } SHSceneGraph::~SHSceneGraph() noexcept @@ -239,7 +239,7 @@ namespace SHADE if (newParent == nullptr) newParent = root; - ChangeParent(NODE_ITER->second, newParent); + changeParent(NODE_ITER->second, newParent); SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } @@ -284,7 +284,7 @@ namespace SHADE }; SHSceneNode* currentNode = NODE_ITER->second; - ChangeParent(currentNode, PARENT_ITER->second); + changeParent(currentNode, PARENT_ITER->second); SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } @@ -310,7 +310,7 @@ namespace SHADE return NODE_ITER->second; } - SHSceneNode* newNode = AllocateNode(entityID); + SHSceneNode* newNode = allocateNode(entityID); if (parent == nullptr) { @@ -320,7 +320,7 @@ namespace SHADE } else { - ChangeParent(newNode, parent); + changeParent(newNode, parent); } return newNode; @@ -347,9 +347,9 @@ namespace SHADE // Remove reference of current node from parent SHSceneNode* currentNode = NODE_ITER->second; if (currentNode->parent != nullptr) - RemoveChild(currentNode->parent, currentNode); + removeChild(currentNode->parent, currentNode); - ReleaseNode(currentNode); + releaseNode(currentNode); return true; } @@ -357,16 +357,16 @@ namespace SHADE { // Remove reference of current node from parent if (nodeToRemove->parent != nullptr) - RemoveChild(nodeToRemove->parent, nodeToRemove); + removeChild(nodeToRemove->parent, nodeToRemove); - ReleaseNode(nodeToRemove); + releaseNode(nodeToRemove); return true; } void SHSceneGraph::Reset() noexcept { for (auto* node : entityNodeMap | std::views::values) - ReleaseNode(node); + releaseNode(node); } bool SHSceneGraph::IsChildOf(EntityID entityID, SHSceneNode* targetNode) noexcept @@ -456,39 +456,39 @@ namespace SHADE void SHSceneGraph::Traverse (const UnaryFunction& function) const { - TraverseAndInvokeFunction(root, function); + traverseAndInvokeFunction(root, function); } /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHSceneNode* SHSceneGraph::AllocateNode(EntityID entityID) + SHSceneNode* SHSceneGraph::allocateNode(EntityID entityID) { SHSceneNode* newNode = new SHSceneNode{entityID}; entityNodeMap.emplace(entityID, newNode); return newNode; } - void SHSceneGraph::ReleaseNode(SHSceneNode* node) noexcept + void SHSceneGraph::releaseNode(SHSceneNode* node) noexcept { SHASSERT(node != nullptr, "Attempting to release Invalid Node!") // Remove parent's reference to this node if there is a parent if (node->parent != nullptr) - RemoveChild(node->parent, node); + removeChild(node->parent, node); // Remove child's references to this node. Children end up as floating nodes. for (auto* child : node->GetChildren()) { - ChangeParent(child, nullptr); + changeParent(child, nullptr); } entityNodeMap.erase(node->GetEntityID()); delete node; } - void SHSceneGraph::ChangeParent(SHSceneNode* node, SHSceneNode* newParent) + void SHSceneGraph::changeParent(SHSceneNode* node, SHSceneNode* newParent) { // Handle self assignment if (node->parent != nullptr && newParent != nullptr && node->parent->entityID == newParent->entityID) @@ -496,7 +496,7 @@ namespace SHADE // Remove child if (node->parent) - RemoveChild(node->parent, node); + removeChild(node->parent, node); if (newParent == nullptr) { @@ -506,16 +506,16 @@ namespace SHADE node->parent = newParent; // Update parent's children - AddChild(newParent, node); + addChild(newParent, node); } - void SHSceneGraph::AddChild(SHSceneNode* node, SHSceneNode* newChild) + void SHSceneGraph::addChild(SHSceneNode* node, SHSceneNode* newChild) { SHASSERT(node != nullptr, "Attempting to modify a non-existent scene node!") SHASSERT(newChild != nullptr, "Attempting to add a non-existent child to a SceneNode!") if (newChild->parent) - RemoveChild(newChild->parent, newChild); + removeChild(newChild->parent, newChild); newChild->parent = node; node->children.emplace_back(newChild); @@ -529,7 +529,7 @@ namespace SHADE SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_ADD_CHILD_EVENT); } - void SHSceneGraph::RemoveChild(SHSceneNode* node, SHSceneNode* childToRemove) + void SHSceneGraph::removeChild(SHSceneNode* node, SHSceneNode* childToRemove) { SHASSERT(node != nullptr, "Attempting to modify a non-existent scene node!") SHASSERT(childToRemove != nullptr, "Attempting to remove a non-existent child from a SceneNode!") @@ -550,12 +550,12 @@ namespace SHADE SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_REMOVE_CHILD_EVENT); } - void SHSceneGraph::TraverseAndInvokeFunction(const SHSceneNode* node, const UnaryFunction& function) + void SHSceneGraph::traverseAndInvokeFunction(const SHSceneNode* node, const UnaryFunction& function) { for (auto* child : node->children) { function(child); - TraverseAndInvokeFunction(child, function); + traverseAndInvokeFunction(child, function); } } diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index 37d0e063..b72b1fb4 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -24,6 +24,10 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates a hierarchical tree for objects in the scene. + */ class SH_API SHSceneGraph { public: @@ -64,22 +68,75 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * Changing the parent of a node will broadcast three events in the following order: + *
+ * 1. SHSceneGraphChangeParentEvent + * 2. SHSceneGraphRemoveChildEvent + * 3. SHSceneGraphAddChildEvent + *

+ * See the corresponding header file for the contents of the event struct. + */ void SetParent (EntityID entityID, SHSceneNode* newParent) noexcept; + + /** + * @brief + * Changing the parent of a node will broadcast three events in the following order: + *
+ * 1. SHSceneGraphChangeParentEvent + * 2. SHSceneGraphRemoveChildEvent + * 3. SHSceneGraphAddChildEvent + *

+ * See the corresponding header file for the contents of the event struct. + */ void SetParent (EntityID entityID, EntityID newParent) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * Adding a node will broadcast two events in the following order: + *
+ * 1. SHSceneGraphChangeParentEvent + * 2. SHSceneGraphAddChildEvent + *

+ * See the corresponding header file for the contents of the event struct. + */ SHSceneNode* AddNode (EntityID entityID, SHSceneNode* parent = nullptr); - bool RemoveNode (EntityID entityID) noexcept; - bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; - void Reset () noexcept; - bool IsChildOf (EntityID entityID, SHSceneNode* targetNode) noexcept; - bool IsChildOf (EntityID entityID, EntityID targetID) noexcept; + /** + * @brief + * Removing a node will broadcast the SHSceneGraphRemoveChildEvent. + * See the corresponding header file for the contents of the event struct. + */ + bool RemoveNode (EntityID entityID) noexcept; - void Traverse (const UnaryFunction& function) const; + /** + * @brief + * Removing a node will broadcast the SHSceneGraphRemoveChildEvent. + * See the corresponding header file for the contents of the event struct. + */ + bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; + + /** + * @brief + * Clears all the scene nodes in the scene graph. + */ + void Reset () noexcept; + + bool IsChildOf (EntityID entityID, SHSceneNode* targetNode) noexcept; + bool IsChildOf (EntityID entityID, EntityID targetID) noexcept; + + /** + * @brief + * Traverses the graph and a depth-first manner and applies the the function onto each node. + * @param function + * A unary function that takes in a SHSceneNode pointer. + */ + void Traverse (const UnaryFunction& function) const; private: /*---------------------------------------------------------------------------------*/ @@ -93,14 +150,14 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - SHSceneNode* AllocateNode (EntityID entityID); - void ReleaseNode (SHSceneNode* node) noexcept; + SHSceneNode* allocateNode (EntityID entityID); + void releaseNode (SHSceneNode* node) noexcept; - void ChangeParent (SHSceneNode* node, SHSceneNode* newParent); - void AddChild (SHSceneNode* node, SHSceneNode* newChild); - void RemoveChild (SHSceneNode* node, SHSceneNode* childToRemove); + void changeParent (SHSceneNode* node, SHSceneNode* newParent); + void addChild (SHSceneNode* node, SHSceneNode* newChild); + void removeChild (SHSceneNode* node, SHSceneNode* childToRemove); - static void TraverseAndInvokeFunction (const SHSceneNode* node, const UnaryFunction& function); + static void traverseAndInvokeFunction (const SHSceneNode* node, const UnaryFunction& function); }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Scene/SHSceneManager.cpp b/SHADE_Engine/src/Scene/SHSceneManager.cpp index 110aaea6..945e9ce1 100644 --- a/SHADE_Engine/src/Scene/SHSceneManager.cpp +++ b/SHADE_Engine/src/Scene/SHSceneManager.cpp @@ -17,6 +17,7 @@ #include "ECS_Base/Managers/SHSystemManager.h" //#include "FRC/SHFrameRateController.h" //#include "ECS_Base/System/SHApplication.h" +#include "SHSceneEvents.h" #include @@ -55,10 +56,11 @@ namespace SHADE if (currentScene) { currentScene->Free(); + SHEventManager::BroadcastEvent(SHSceneExitEvent{ currentSceneID }, SH_SCENE_ON_EXIT_EVENT); + currentScene->Unload(); SHEntityManager::DestroyAllEntity(); delete currentScene; - } if (!prevSceneReload) { @@ -84,6 +86,8 @@ namespace SHADE { currentScene->Load(); currentScene->Init(); + + SHEventManager::BroadcastEvent(SHSceneInitEvent{ currentSceneID }, SH_SCENE_ON_INIT_EVENT); } } else // restarting scene @@ -91,6 +95,8 @@ namespace SHADE nextSceneID = UINT32_MAX; currentScene->Free(); + SHEventManager::BroadcastEvent(SHSceneExitEvent{ currentSceneID }, SH_SCENE_ON_EXIT_EVENT); + if (cleanReload == true) { cleanReload = false; @@ -103,6 +109,7 @@ namespace SHADE SHEntityManager::DestroyAllEntity(); currentScene->Init(); + SHEventManager::BroadcastEvent(SHSceneInitEvent{ currentSceneID }, SH_SCENE_ON_INIT_EVENT); } } diff --git a/SHADE_Engine/src/Scene/SHSceneNode.h b/SHADE_Engine/src/Scene/SHSceneNode.h index 87bd8d0b..7f8e1dc3 100644 --- a/SHADE_Engine/src/Scene/SHSceneNode.h +++ b/SHADE_Engine/src/Scene/SHSceneNode.h @@ -15,20 +15,17 @@ // Project Headers #include "ECS_Base/Entity/SHEntity.h" #include "SH_API.h" -#include "SHSceneGraph.h" namespace SHADE { - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - class SHSceneGraph; - /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates an object in the scene that is part of the scene's hierarchy. + */ class SH_API SHSceneNode { private: @@ -66,7 +63,7 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetActive (bool newActiveState) noexcept; + void SetActive (bool newActiveState) noexcept; private: /*---------------------------------------------------------------------------------*/ From 38b1c46d1f96a77023fe2f47cd2631f0645e0c21 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 5 Dec 2022 00:20:29 +0800 Subject: [PATCH 011/164] Added physics world and tested applied gravity for linear movement --- Assets/Scenes/PhysicsSandbox.shade | 21 ++- .../src/ECS_Base/Managers/SHEntityManager.cpp | 2 +- .../Inspector/SHEditorComponentView.hpp | 10 +- .../src/Math/Transform/SHTransformSystem.cpp | 2 - .../src/Physics/Dynamics/SHMotionState.cpp | 100 ++++++++++++++ .../src/Physics/Dynamics/SHMotionState.h | 93 +++++++++++++ .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 122 ++++++++++++++++++ .../Physics/{ => Dynamics}/SHPhysicsWorld.h | 52 ++++++-- .../src/Physics/Dynamics/SHRigidBody.cpp | 120 +++++++++-------- .../src/Physics/Dynamics/SHRigidBody.h | 37 ++++-- .../PhysicsObject/SHPhysicsObject.cpp | 13 +- .../Interface/PhysicsObject/SHPhysicsObject.h | 6 + .../PhysicsObject/SHPhysicsObjectManager.cpp | 42 +++++- .../PhysicsObject/SHPhysicsObjectManager.h | 10 +- .../SHRigidBodyComponent.cpp | 9 ++ .../RigidBodyComponent/SHRigidBodyComponent.h | 2 + SHADE_Engine/src/Physics/SHPhysicsWorld.cpp | 27 ---- .../Routines/SHPhysicsPostUpdateRoutine.cpp | 43 ++++-- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 40 +++--- .../Routines/SHPhysicsUpdateRoutine.cpp | 4 + .../src/Physics/System/SHPhysicsSystem.cpp | 87 +++++++++++-- .../src/Physics/System/SHPhysicsSystem.h | 12 +- .../Physics/System/SHPhysicsSystemInterface.h | 7 +- 23 files changed, 693 insertions(+), 168 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHMotionState.h create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp rename SHADE_Engine/src/Physics/{ => Dynamics}/SHPhysicsWorld.h (58%) delete mode 100644 SHADE_Engine/src/Physics/SHPhysicsWorld.cpp diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index a0b3c0c2..d6b77c17 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -9,10 +9,10 @@ Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: - Type: Static + Type: Dynamic Auto Mass: false Mass: 1 - Drag: 0 + Drag: 1 Angular Drag: 0 Use Gravity: true Gravity Scale: 1 @@ -25,4 +25,21 @@ Freeze Rotation Y: false Freeze Rotation Z: false IsActive: true + Scripts: ~ +- EID: 1 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Camera Component: + Position: {x: 0, y: 2, z: 5} + Pitch: 0 + Yaw: 0 + Roll: 0 + Width: 1920 + Height: 1080 + Near: 0.00999999978 + Far: 10000 + Perspective: true + IsActive: true Scripts: ~ \ No newline at end of file diff --git a/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp b/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp index 1c603c57..f5f08674 100644 --- a/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp +++ b/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp @@ -162,7 +162,7 @@ namespace SHADE //SHSceneNode* parentNode = entityVec[eIndex]->GetSceneNode()->GetParent(); - //SHSceneGraph::RemoveChild(parentNode,entityVec[eIndex].get()); + //SHSceneGraph::removeChild(parentNode,entityVec[eIndex].get()); //TODO remove from parent and recursively delete child. diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 02614631..71d607f5 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -252,12 +252,13 @@ namespace SHADE if(rbType == SHRigidBodyComponent::Type::DYNAMIC) //Dynamic only fields { SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetIsGravityEnabled(value);}, "Gravity"); - //SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); + SHEditorWidgets::DragFloat("Gravity Scale", [component] { return component->GetGravityScale(); }, [component](float const& value) { component->SetGravityScale(value); }, "Gravity Scale", 0.1f, 0.0f); + SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); } if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { - SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag"); - SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag"); + SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag", 0.1f, 0.0f); + SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag", 0.1f, 0.0f); SHEditorWidgets::CheckBox("Interpolate", [component] {return component->IsInterpolating(); }, [component](bool const& value) {component->SetInterpolate(value); }, "Interpolate"); @@ -284,8 +285,7 @@ namespace SHADE //Debug Info (Read-Only) if(ImGui::CollapsingHeader("Debug Information", ImGuiTreeNodeFlags_DefaultOpen))//Dynamic or Kinematic only fields { - SHEditorWidgets::DragFloat("Mass", [component] { return component->GetMass(); }, [](float value){}, "Mass", 0.1f, 0.0f, std::numeric_limits::infinity(), "%.3f", ImGuiSliderFlags_ReadOnly); - //SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component] {return component->GetPosition(); }, [](SHVec3 const& value) {}, false, "Position", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); + SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component] {return component->GetPosition(); }, [](SHVec3 const& value) {}, false, "Position", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); //SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] {return component->GetRotation(); }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index 03de360d..0a0ec092 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -246,8 +246,6 @@ namespace SHADE tf.world.position = SHVec3::Transform(tf.local.position, localToWorld); tf.world.scale = tf.local.scale * (parent ? parent->GetLocalScale() : SHVec3::One); - - if (convertRotation) { tf.worldRotation = tf.localRotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero); diff --git a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp new file mode 100644 index 00000000..2cca1c45 --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp @@ -0,0 +1,100 @@ +/**************************************************************************************** + * \file SHMotionState.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Motion State. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHMotionState.h" + +// Project Headers +#include "Math/SHMathHelpers.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHMotionState::SHMotionState() noexcept + : hasMoved { false } + {} + + SHMotionState::SHMotionState(const SHMotionState& rhs) noexcept + : hasMoved { rhs.hasMoved } + , position { rhs.position } + , prevPosition { rhs.prevPosition } + {} + + SHMotionState::SHMotionState(SHMotionState&& rhs) noexcept + : hasMoved { rhs.hasMoved } + , position { rhs.position } + , prevPosition { rhs.prevPosition } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHMotionState& SHMotionState::operator=(const SHMotionState& rhs) noexcept + { + if (this == &rhs) + return *this; + + hasMoved = rhs.hasMoved; + position = rhs.position; + prevPosition = rhs.prevPosition; + + return *this; + } + + SHMotionState& SHMotionState::operator=(SHMotionState&& rhs) noexcept + { + hasMoved = rhs.hasMoved; + position = rhs.position; + prevPosition = rhs.prevPosition; + + return *this; + } + + SHMotionState::operator bool() const noexcept + { + return hasMoved; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definition */ + /*-----------------------------------------------------------------------------------*/ + + void SHMotionState::ForcePosition(const SHVec3& newPosition) noexcept + { + hasMoved = false; + + prevPosition = newPosition; + position = newPosition; + } + + void SHMotionState::IntegratePosition(const SHVec3& velocity, float dt) noexcept + { + // Velocities are 0 when objects are static or sleeping. We do not want to integrate them here. + // This call should never reach here. + + hasMoved = true; + + prevPosition = position; + position += velocity * dt; + } + + SHVec3 SHMotionState::InterpolatePositions(float factor) const noexcept + { + return SHVec3::ClampedLerp(prevPosition, position, factor); + } + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h new file mode 100644 index 00000000..4cff9a8f --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h @@ -0,0 +1,93 @@ +/**************************************************************************************** + * \file SHMotionState.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Motion State. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Math/Vector/SHVec3.h" + +namespace SHADE +{ + /*-------------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-------------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates the motion state of a rigid body in physics. + */ + struct SH_API SHMotionState + { + public: + /*-----------------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------------*/ + + bool hasMoved; + + SHVec3 position; + SHVec3 prevPosition; + + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-----------------------------------------------------------------------------------*/ + + SHMotionState () noexcept; + SHMotionState (const SHMotionState& rhs) noexcept; + SHMotionState (SHMotionState&& rhs) noexcept; + + ~SHMotionState() = default; + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------------*/ + + SHMotionState& operator= (const SHMotionState& rhs) noexcept; + SHMotionState& operator= (SHMotionState&& rhs) noexcept; + + operator bool () const noexcept; + + /*-----------------------------------------------------------------------------------*/ + /* Function Members */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Forcefully sets the position. Meant to be used when transform overrides the rigid body + * positions. + * @param newPosition + * The new position to set. + */ + void ForcePosition (const SHVec3& newPosition) noexcept; + + /** + * @brief + * Integrates the positions using velocity with respect to time. + * @param velocity + * The velocity to integrate. + * @param dt + * The delta time to integrate with respect to. + */ + void IntegratePosition (const SHVec3& velocity, float dt) noexcept; + + /** + * @brief + * Interpolates the position between the previous and the last using a given factor. + * @param factor + * The factor to interpolate by. Should be between 0 & 1. + * @returns + * The interpolated position meant for rendering. + */ + SHVec3 InterpolatePositions (float factor) const noexcept; + + }; + + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp new file mode 100644 index 00000000..7d6c7c5c --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -0,0 +1,122 @@ +/**************************************************************************************** + * \file SHPhysicsWorld.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Physics World. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsWorld.h" + +// Project Headers + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsWorld::SHPhysicsWorld(const WorldSettings& worldSettings) noexcept + : settings { worldSettings } + { + rigidBodies.clear(); + SHLOG_INFO_D("Creating Physics World") + } + + SHPhysicsWorld::~SHPhysicsWorld() noexcept + { + rigidBodies.clear(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsWorld::AddRigidBody(SHRigidBody* rigidBody) noexcept + { + const bool INSERTED = rigidBodies.emplace(rigidBody->entityID, rigidBody).second; + if (!INSERTED) + { + SHLOG_WARNING_D("Attempting to add duplicate rigid body {} to the Physics World!", rigidBody->entityID) + } + } + + void SHPhysicsWorld::RemoveRigidBody(SHRigidBody* rigidBody) noexcept + { + rigidBodies.erase(rigidBody->entityID); + } + + void SHPhysicsWorld::Step(float dt) + { + for (auto* rigidBody : rigidBodies | std::views::values) + integrateForces(*rigidBody, dt); + + for (auto* rigidBody : rigidBodies | std::views::values) + integrateVelocities(*rigidBody, dt); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsWorld::integrateForces(SHRigidBody& rigidBody, float dt) const noexcept + { + if (rigidBody.bodyType != SHRigidBody::Type::DYNAMIC) + return; + + // Integrate forces and gravity into linear velocity + const SHVec3 LINEAR_ACCELERATION = rigidBody.accumulatedForce * rigidBody.invMass; + const SHVec3 GRAVITATIONAL_ACCELERATION = rigidBody.IsGravityEnabled() ? settings.gravity * rigidBody.gravityScale : SHVec3::Zero; + + rigidBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * dt; + + // Apply drag (exponentially applied) + rigidBody.linearVelocity *= 1.0f / (1.0f + dt * rigidBody.linearDrag); + } + + void SHPhysicsWorld::integrateVelocities(SHRigidBody& rigidBody, float dt) const noexcept + { + static const auto ENFORCE_CONSTRAINED_VELOCITIES = [](SHRigidBody& rigidBody) + { + // Enforce linear constraints + rigidBody.linearVelocity = SHVec3 + { + rigidBody.GetFreezePositionX() ? 0.0f : rigidBody.linearVelocity.x + , rigidBody.GetFreezePositionY() ? 0.0f : rigidBody.linearVelocity.y + , rigidBody.GetFreezePositionZ() ? 0.0f : rigidBody.linearVelocity.z + }; + + // TODO: Enforce angular constraints + }; + + // Always reset movement flag + rigidBody.motionState.hasMoved = false; + + // Set all velocities of static bodies to 0 + if (rigidBody.bodyType == SHRigidBody::Type::STATIC) + { + rigidBody.linearVelocity = SHVec3::Zero; + } + else // Dynamic & Kinematic bodies + { + // Both dynamic and kinematic can sleep when their velocities are under the thresholds. + if (!rigidBody.IsSleeping()) + { + ENFORCE_CONSTRAINED_VELOCITIES(rigidBody); + + rigidBody.motionState.IntegratePosition(rigidBody.linearVelocity, dt); + // TODO: Integrate orientations + } + } + + // Clear all forces + // We clear forces for static bodies as well for redundancy + rigidBody.ClearForces(); + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h similarity index 58% rename from SHADE_Engine/src/Physics/SHPhysicsWorld.h rename to SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index e872cd3c..eb96a021 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -10,7 +10,10 @@ #pragma once +#include + // Project Headers +#include "SHRigidBody.h" #include "Math/SHMath.h" #include "SH_API.h" @@ -20,7 +23,7 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - struct SH_API SHPhysicsWorldState + class SH_API SHPhysicsWorld { public: @@ -41,22 +44,55 @@ namespace SHADE bool sleepingEnabled = true; }; - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - WorldSettings settings; - /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHPhysicsWorldState() noexcept; + SHPhysicsWorld (const WorldSettings& worldSettings = WorldSettings{}) noexcept; + ~SHPhysicsWorld () noexcept; + + SHPhysicsWorld (const SHPhysicsWorld&) = delete; + SHPhysicsWorld (SHPhysicsWorld&&) = delete; + + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsWorld& operator=(const SHPhysicsWorld&) = delete; + SHPhysicsWorld& operator=(SHPhysicsWorld&&) = delete; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ + void AddRigidBody (SHRigidBody* rigidBody) noexcept; + void RemoveRigidBody (SHRigidBody* rigidBody) noexcept; + + void Step (float dt); + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using RigidBodies = std::unordered_map; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + WorldSettings settings; + + RigidBodies rigidBodies; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept; + void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept; + }; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index 1867636c..8f99a8f5 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -26,7 +26,7 @@ namespace SHADE : entityID { eid } , bodyType { type } , invMass { type == Type::DYNAMIC ? 1.0f : 0.0f } - , linearDrag { type != Type::STATIC ? 0.01f : 0.0f } + , linearDrag { 0.01f } , gravityScale { 1.0f } , flags { 0U } { @@ -45,6 +45,7 @@ namespace SHADE , linearDrag { rhs.linearDrag } , gravityScale { rhs.gravityScale } , flags { rhs.flags } + , motionState { rhs.motionState } { // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. } @@ -56,6 +57,7 @@ namespace SHADE , linearDrag { rhs.linearDrag } , gravityScale { rhs.gravityScale } , flags { rhs.flags } + , motionState { std::move(rhs.motionState) } { // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. } @@ -70,28 +72,36 @@ namespace SHADE if (this == &rhs) return *this; - entityID = rhs.entityID; - bodyType = rhs.bodyType; - invMass = rhs.invMass; - linearDrag = rhs.linearDrag; - gravityScale = rhs.gravityScale; - flags = rhs.flags; + entityID = rhs.entityID; + bodyType = rhs.bodyType; + invMass = rhs.invMass; + linearDrag = rhs.linearDrag; + gravityScale = rhs.gravityScale; + flags = rhs.flags; + motionState = rhs.motionState; // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. + accumulatedForce = SHVec3::Zero; + linearVelocity = SHVec3::Zero; + angularVelocity = SHVec3::Zero; return *this; } SHRigidBody& SHRigidBody::operator=(SHRigidBody&& rhs) noexcept { - entityID = rhs.entityID; - bodyType = rhs.bodyType; - invMass = rhs.invMass; - linearDrag = rhs.linearDrag; - gravityScale = rhs.gravityScale; - flags = rhs.flags; + entityID = rhs.entityID; + bodyType = rhs.bodyType; + invMass = rhs.invMass; + linearDrag = rhs.linearDrag; + gravityScale = rhs.gravityScale; + flags = rhs.flags; + motionState = std::move(rhs.motionState); // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. + accumulatedForce = SHVec3::Zero; + linearVelocity = SHVec3::Zero; + angularVelocity = SHVec3::Zero; return *this; } @@ -105,118 +115,124 @@ namespace SHADE return bodyType; } - float SHRigidBody::GetMass() const noexcept + float SHRigidBody::GetMass() const noexcept { return 1.0f/ invMass; } - float SHRigidBody::GetLinearDrag() const noexcept + float SHRigidBody::GetLinearDrag() const noexcept { return linearDrag; } - float SHRigidBody::GetGravityScale() const noexcept + float SHRigidBody::GetGravityScale() const noexcept { return gravityScale; } - const SHVec3& SHRigidBody::GetAccumulatedForce() const noexcept + const SHVec3& SHRigidBody::GetAccumulatedForce() const noexcept { return accumulatedForce; } - const SHVec3& SHRigidBody::GetLinearVelocity() const noexcept + const SHVec3& SHRigidBody::GetLinearVelocity() const noexcept { return linearVelocity; } - const SHVec3& SHRigidBody::GetAngularVelocity() const noexcept + const SHVec3& SHRigidBody::GetAngularVelocity() const noexcept { return angularVelocity; } // Flags - bool SHRigidBody::IsActive() const noexcept + bool SHRigidBody::IsActive() const noexcept { static constexpr unsigned int FLAG_POS = 0; return flags & (1U << FLAG_POS); } - bool SHRigidBody::IsSleeping() const noexcept + bool SHRigidBody::IsSleeping() const noexcept { static constexpr unsigned int FLAG_POS = 1; return flags & (1U << FLAG_POS); } - bool SHRigidBody::IsSleepingEnabled() const noexcept + bool SHRigidBody::IsSleepingEnabled() const noexcept { static constexpr unsigned int FLAG_POS = 2; return flags & (1U << FLAG_POS); } - bool SHRigidBody::IsGravityEnabled() const noexcept + bool SHRigidBody::IsGravityEnabled() const noexcept { static constexpr unsigned int FLAG_POS = 3; return flags & (1U << FLAG_POS); } - bool SHRigidBody::IsAutoMassEnabled() const noexcept + bool SHRigidBody::IsAutoMassEnabled() const noexcept { static constexpr unsigned int FLAG_POS = 4; return flags & (1U << FLAG_POS); } - bool SHRigidBody::GetFreezePositionX() const noexcept + bool SHRigidBody::GetFreezePositionX() const noexcept { static constexpr unsigned int FLAG_POS = 10; return flags & (1U << FLAG_POS); } - bool SHRigidBody::GetFreezePositionY() const noexcept + bool SHRigidBody::GetFreezePositionY() const noexcept { static constexpr unsigned int FLAG_POS = 11; return flags & (1U << FLAG_POS); } - bool SHRigidBody::GetFreezePositionZ() const noexcept + bool SHRigidBody::GetFreezePositionZ() const noexcept { static constexpr unsigned int FLAG_POS = 12; return flags & (1U << FLAG_POS); } - bool SHRigidBody::GetFreezeRotationX() const noexcept + bool SHRigidBody::GetFreezeRotationX() const noexcept { static constexpr unsigned int FLAG_POS = 13; return flags & (1U << FLAG_POS); } - bool SHRigidBody::GetFreezeRotationY() const noexcept + bool SHRigidBody::GetFreezeRotationY() const noexcept { static constexpr unsigned int FLAG_POS = 14; return flags & (1U << FLAG_POS); } - bool SHRigidBody::GetFreezeRotationZ() const noexcept + bool SHRigidBody::GetFreezeRotationZ() const noexcept { static constexpr unsigned int FLAG_POS = 15; return flags & (1U << FLAG_POS); } + SHMotionState& SHRigidBody::GetMotionState() noexcept + { + return motionState; + } + + /*-----------------------------------------------------------------------------------*/ /* Setter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHRigidBody::SetType(Type newType) noexcept + void SHRigidBody::SetType(Type newType) noexcept { if (newType == bodyType) return; - bodyType = newType; - invMass = newType == Type::DYNAMIC ? 1.0f : 0.0f; + bodyType = newType; + invMass = newType == Type::DYNAMIC ? 1.0f : 0.0f; } - void SHRigidBody::SetMass(float newMass) noexcept + void SHRigidBody::SetMass(float newMass) noexcept { if (bodyType != Type::DYNAMIC) { @@ -235,7 +251,7 @@ namespace SHADE // TODO: Recompute inertia tensor } - void SHRigidBody::SetLinearDrag(float newLinearDrag) noexcept + void SHRigidBody::SetLinearDrag(float newLinearDrag) noexcept { if (bodyType == Type::STATIC) { @@ -252,17 +268,17 @@ namespace SHADE linearDrag = newLinearDrag; } - void SHRigidBody::SetGravityScale(float newGravityScale) noexcept + void SHRigidBody::SetGravityScale(float newGravityScale) noexcept { gravityScale = newGravityScale; } - void SHRigidBody::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept + void SHRigidBody::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept { linearVelocity = newLinearVelocity; } - void SHRigidBody::SetIsActive(bool isActive) noexcept + void SHRigidBody::SetIsActive(bool isActive) noexcept { static constexpr unsigned int FLAG_POS = 0; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -270,7 +286,7 @@ namespace SHADE isActive ? flags |= VALUE : flags &= ~VALUE; } - void SHRigidBody::SetIsSleeping(bool isSleeping) noexcept + void SHRigidBody::SetIsSleeping(bool isSleeping) noexcept { static constexpr unsigned int FLAG_POS = 1; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -278,7 +294,7 @@ namespace SHADE isSleeping ? flags |= VALUE : flags &= ~VALUE; } - void SHRigidBody::SetSleepingEnabled(bool enableSleeping) noexcept + void SHRigidBody::SetSleepingEnabled(bool enableSleeping) noexcept { static constexpr unsigned int FLAG_POS = 2; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -295,7 +311,7 @@ namespace SHADE } } - void SHRigidBody::SetGravityEnabled(bool enableGravity) noexcept + void SHRigidBody::SetGravityEnabled(bool enableGravity) noexcept { static constexpr unsigned int FLAG_POS = 3; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -303,7 +319,7 @@ namespace SHADE enableGravity ? flags |= VALUE : flags &= ~VALUE; } - void SHRigidBody::SetAutoMassEnabled(bool enableAutoMass) noexcept + void SHRigidBody::SetAutoMassEnabled(bool enableAutoMass) noexcept { static constexpr unsigned int FLAG_POS = 4; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -321,7 +337,7 @@ namespace SHADE } } - void SHRigidBody::SetFreezePositionX(bool freezePositionX) noexcept + void SHRigidBody::SetFreezePositionX(bool freezePositionX) noexcept { static constexpr unsigned int FLAG_POS = 10; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -338,7 +354,7 @@ namespace SHADE } } - void SHRigidBody::SetFreezePositionY(bool freezePositionY) noexcept + void SHRigidBody::SetFreezePositionY(bool freezePositionY) noexcept { static constexpr unsigned int FLAG_POS = 11; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -355,7 +371,7 @@ namespace SHADE } } - void SHRigidBody::SetFreezePositionZ(bool freezePositionZ) noexcept + void SHRigidBody::SetFreezePositionZ(bool freezePositionZ) noexcept { static constexpr unsigned int FLAG_POS = 12; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -372,7 +388,7 @@ namespace SHADE } } - void SHRigidBody::SetFreezeRotationX(bool freezeRotationX) noexcept + void SHRigidBody::SetFreezeRotationX(bool freezeRotationX) noexcept { static constexpr unsigned int FLAG_POS = 13; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -389,7 +405,7 @@ namespace SHADE } } - void SHRigidBody::SetFreezeRotationY(bool freezeRotationY) noexcept + void SHRigidBody::SetFreezeRotationY(bool freezeRotationY) noexcept { static constexpr unsigned int FLAG_POS = 14; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -406,7 +422,7 @@ namespace SHADE } } - void SHRigidBody::SetFreezeRotationZ(bool freezeRotationZ) noexcept + void SHRigidBody::SetFreezeRotationZ(bool freezeRotationZ) noexcept { static constexpr unsigned int FLAG_POS = 15; static constexpr uint16_t VALUE = 1U << FLAG_POS; @@ -424,10 +440,10 @@ namespace SHADE } /*-----------------------------------------------------------------------------------*/ - /* Member Function Definitions */ + /* Public Member Function Definition */ /*-----------------------------------------------------------------------------------*/ - void SHRigidBody::AddForce(const SHVec3& force, const SHVec3& pos) noexcept + void SHRigidBody::AddForce(const SHVec3& force, const SHVec3& pos) noexcept { if (bodyType != Type::DYNAMIC) return; @@ -436,7 +452,7 @@ namespace SHADE // Compute torque when force is offset } - void SHRigidBody::AddImpulse(const SHVec3& impulse, const SHVec3& pos) noexcept + void SHRigidBody::AddImpulse(const SHVec3& impulse, const SHVec3& pos) noexcept { if (bodyType != Type::DYNAMIC) return; @@ -444,7 +460,7 @@ namespace SHADE linearVelocity += impulse * invMass; } - void SHRigidBody::ClearForces() noexcept + void SHRigidBody::ClearForces() noexcept { accumulatedForce = SHVec3::Zero; } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index 77b29292..dbf2344f 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -13,7 +13,7 @@ // Project Headers #include "ECS_Base/SHECSMacros.h" #include "Math/Vector/SHVec3.h" -#include "SH_API.h" +#include "SHMotionState.h" namespace SHADE { @@ -27,6 +27,13 @@ namespace SHADE */ class SH_API SHRigidBody { + private: + /*-----------------------------------------------------------------------------------*/ + /* Friends */ + /*-----------------------------------------------------------------------------------*/ + + friend class SHPhysicsWorld; + public: /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -83,13 +90,16 @@ namespace SHADE [[nodiscard]] bool GetFreezeRotationY () const noexcept; [[nodiscard]] bool GetFreezeRotationZ () const noexcept; + [[nodiscard]] SHMotionState& GetMotionState () noexcept; + /*-----------------------------------------------------------------------------------*/ /* Setter Functions */ /*-----------------------------------------------------------------------------------*/ /** * @brief - * Changing the type from non-Dynamic to Dynamic will set the default mass. + * Changing the type from non-Dynamic to Dynamic will set the default + * mass and drag values. */ void SetType (Type newType) noexcept; @@ -111,7 +121,6 @@ namespace SHADE void SetGravityScale (float newGravityScale) noexcept; - void SetLinearVelocity (const SHVec3& newLinearVelocity) noexcept; // Flags @@ -164,22 +173,24 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ // The entityID here is only meant for linking with the actual component in the engine. - EntityID entityID; + EntityID entityID; - Type bodyType; + Type bodyType; - float invMass; - float linearDrag; + float invMass; + float linearDrag; - float gravityScale; + float gravityScale; + + SHVec3 accumulatedForce; - SHVec3 accumulatedForce; - - SHVec3 linearVelocity; - SHVec3 angularVelocity; + SHVec3 linearVelocity; + SHVec3 angularVelocity; // aZ aY aX pZ pY pX 0 0 0 0 0 autoMass enableGravity enableSleeping sleeping active - uint16_t flags; + uint16_t flags; + + SHMotionState motionState; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp index 44452868..b6e5ac40 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp @@ -44,9 +44,8 @@ namespace SHADE delete rigidBody; } - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ + /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ SHPhysicsObject& SHPhysicsObject::operator=(const SHPhysicsObject& rhs) noexcept @@ -71,4 +70,14 @@ namespace SHADE return *this; } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHPhysicsObject::IsEmpty() const noexcept + { + return rigidBody == nullptr; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h index 2be5844d..f297a6fb 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h @@ -48,5 +48,11 @@ namespace SHADE SHPhysicsObject& operator=(const SHPhysicsObject& rhs) noexcept; SHPhysicsObject& operator=(SHPhysicsObject&& rhs) noexcept; + + /*-----------------------------------------------------------------------------------*/ + /* Member Functions */ + /*-----------------------------------------------------------------------------------*/ + + bool IsEmpty() const noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp index dccca97b..e6e92ba5 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp @@ -14,6 +14,7 @@ #include "SHPhysicsObjectManager.h" // Project Headers +#include "Math/Transform/SHTransformComponent.h" #include "Physics/Interface/SHColliderComponent.h" #include "Physics/Interface/SHCollisionShape.h" #include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" @@ -21,16 +22,25 @@ namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObjectManager::~SHPhysicsObjectManager() noexcept + { + RemoveAllObjects(); + } + /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHPhysicsObjectManager::EntityObjectMap& SHPhysicsObjectManager::GetPhysicsObjects() noexcept + SHPhysicsObjectManager::EntityObjectMap& SHPhysicsObjectManager::GetPhysicsObjects() noexcept { return physicsObjects; } - const SHPhysicsObject* SHPhysicsObjectManager::GetPhysicsObject(EntityID entityID) noexcept + const SHPhysicsObject* SHPhysicsObjectManager::GetPhysicsObject(EntityID entityID) noexcept { const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); if (PHYSICS_OBJECT_ITERATOR == physicsObjects.end()) @@ -68,6 +78,17 @@ namespace SHADE , static_cast(rigidBodyComponent->GetType()) }; + SHMotionState& motionState = physicsObject->rigidBody->GetMotionState(); + + if (const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent(entityID); TRANSFORM_COMPONENT) + { + motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition()); + } + else + { + motionState.ForcePosition(SHVec3::Zero); + } + // Link with the component rigidBodyComponent->SetRigidBody(physicsObject->rigidBody); @@ -85,7 +106,7 @@ namespace SHADE physicsObject->rigidBody = nullptr; // Destroy empty physics objects - if (physicsObject->rigidBody == nullptr) + if (physicsObject->IsEmpty()) destroyPhysicsObject(entityID); } @@ -101,7 +122,15 @@ namespace SHADE void SHPhysicsObjectManager::RemoveCollider(EntityID entityID) noexcept { - // Unlink with the component + const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); + if (PHYSICS_OBJECT_ITERATOR != physicsObjects.end()) + { + SHPhysicsObject* physicsObject = &PHYSICS_OBJECT_ITERATOR->second; + + // Destroy empty physics objects + if (physicsObject->IsEmpty()) + destroyPhysicsObject(entityID); + } // TODO: Broadcast event } @@ -120,6 +149,11 @@ namespace SHADE // TODO: Broadcast event } + void SHPhysicsObjectManager::RemoveAllObjects() noexcept + { + physicsObjects.clear(); + } + /*-----------------------------------------------------------------------------------*/ /* Private Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h index 17a1d3a1..6449a2d8 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h @@ -40,7 +40,8 @@ namespace SHADE /* Constructors & Destructor */ /*-----------------------------------------------------------------------------------*/ - SHPhysicsObjectManager() noexcept = default; + SHPhysicsObjectManager () noexcept = default; + ~SHPhysicsObjectManager () noexcept; /*-----------------------------------------------------------------------------------*/ /* Getter Functions */ @@ -107,6 +108,13 @@ namespace SHADE */ void RemoveCollisionShape (EntityID entityID, uint8_t shapeID) noexcept; + /** + * @brief + * Removes all physics object in the manager. This is only meant to be called when + * the world is being destroyed or the scene is being changed. + */ + void RemoveAllObjects () noexcept; + private: /*-----------------------------------------------------------------------------------*/ /* Data Members */ diff --git a/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp index 5924c612..ddc51fbc 100644 --- a/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp @@ -182,6 +182,15 @@ namespace SHADE return SHVec3::Zero; } + SHVec3 SHRigidBodyComponent::GetPosition() const noexcept + { + if (rigidBody) + return rigidBody->GetMotionState().position; + + return SHVec3::Zero; + } + + /*-----------------------------------------------------------------------------------*/ /* Setter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h index ae0bd3ec..bfb1717b 100644 --- a/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h @@ -97,6 +97,8 @@ namespace SHADE [[nodiscard]] SHVec3 GetLinearVelocity () const noexcept; [[nodiscard]] SHVec3 GetAngularVelocity () const noexcept; + [[nodiscard]] SHVec3 GetPosition () const noexcept; + /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp deleted file mode 100644 index d54d3f86..00000000 --- a/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsWorld.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Physics World. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHPhysicsWorld.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Members Definitions */ - /*-----------------------------------------------------------------------------------*/ - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp index 9b1c0015..5f18c450 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp @@ -15,6 +15,7 @@ // Project Headers #include "ECS_Base/Managers/SHSystemManager.h" +#include "Math/Transform/SHTransformComponent.h" #include "Scripting/SHScriptEngine.h" @@ -39,24 +40,40 @@ namespace SHADE if (scriptingSystem == nullptr) { - SHLOGV_ERROR("Unable to invoke collision and trigger script events due to missing SHScriptEngine!"); + SHLOGV_ERROR("Unable to invoke collision and trigger script events due to missing SHScriptEngine!"); } - // Interpolate transforms for rendering + const float FACTOR = static_cast(physicsSystem->interpolationFactor); + + // Interpolate transforms for rendering. + // Only rigid bodies can move due to physics, so we run through the rigid body component dense set. + const auto& RIGIDBODY_DENSE = SHComponentManager::GetDense(); + for (auto& rigidBodyComponent : RIGIDBODY_DENSE) + { + auto* transformComponent = SHComponentManager::GetComponent_s(rigidBodyComponent.GetEID()); + if (!transformComponent) + continue; + + if (!rigidBodyComponent.rigidBody) + continue; + + if (const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); MOTION_STATE) + { + SHVec3 renderPosition = rigidBodyComponent.IsInterpolating() + ? MOTION_STATE.InterpolatePositions(FACTOR) + : MOTION_STATE.position; + + /* + * TODO: Test if the scene graph transforms abides by setting world position. Collisions will ignore the scene graph hierarchy. + */ + + transformComponent->SetWorldPosition(renderPosition); + // TODO: SetOrientation + } + } // Collision & Trigger messages if (scriptingSystem != nullptr) scriptingSystem->ExecuteCollisionFunctions(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsSystem::PhysicsPostUpdate::syncTransforms(SHRigidBodyComponent* rbComponent) const noexcept - { - - } - } diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index a0f348f4..eda69161 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -36,27 +36,27 @@ namespace SHADE auto* physicsSystem = reinterpret_cast(GetSystem()); // Get all physics objects & sync transforms - for (auto& physicsObject : physicsSystem->physicsObjectManager.GetPhysicsObjects() | std::views::values) - syncTransforms(&physicsObject); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsSystem::PhysicsPreUpdate::syncTransforms(SHPhysicsObject* physicsObject) const noexcept - { - const EntityID EID = physicsObject->entityID; - - // Get relevant components: Transform, Rigidbody & Collider - const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(EID); - - auto* rigidBodyComponent = SHComponentManager::GetComponent_s(EID); - auto* colliderComponent = SHComponentManager::GetComponent_s(EID); - - if (TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged()) + for (auto& [entityID, physicsObject] : physicsSystem->physicsObjectManager.GetPhysicsObjects()) { - // Sync the objects transforms + const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); + + // We assume that all engine components and physics object components have been successfully linked + + if (TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged()) + { + // Sync the objects transforms + const SHVec3 WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); + + if (physicsObject.rigidBody) + { + SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); + + motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition()); + // TODO: Force Orientation + } + } + + // TODO: Sync Collider Transform with World Transform } } } diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp index 77da8706..6ae53b31 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp @@ -47,7 +47,11 @@ namespace SHADE int count = 0; while (accumulatedTime > FIXED_DT) { + if (scriptEngine) + scriptEngine->ExecuteFixedUpdates(); + if (physicsSystem->physicsWorld) + physicsSystem->physicsWorld->Step(static_cast(FIXED_DT)); accumulatedTime -= FIXED_DT; ++count; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 98a69087..b4ff6961 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -28,10 +28,11 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHPhysicsSystem::SHPhysicsSystem() + SHPhysicsSystem::SHPhysicsSystem() noexcept : worldUpdated { false } , interpolationFactor { 0.0 } , fixedDT { DEFAULT_FIXED_STEP } + , physicsWorld { nullptr } { // Add more events here to register them @@ -45,6 +46,11 @@ namespace SHADE #endif } + SHPhysicsSystem::~SHPhysicsSystem() noexcept + { + delete physicsWorld; + } + /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -141,24 +147,66 @@ namespace SHADE SHEventHandle SHPhysicsSystem::onSceneInit(SHEventPtr onSceneInitEvent) { /* - * TODO: * If a world already exists, destroy it - * Recreate the world + * Recreate the world. * * If there is an editor and editor mode is already playing, link all entities with the world immediately. */ + // Destroy an existing world if there is one. + //! This should almost never happen. + if (physicsWorld) + { + // Remove all references of physics objects from the world + for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) + { + if (PHYSICS_OBJECT.rigidBody) + physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); + } + + delete physicsWorld; + physicsWorld = nullptr; + } + + // Create the physics world + physicsWorld = new SHPhysicsWorld; + + #ifdef SHEDITOR + + // Link all entities with the world if editor is already playing. + // This is for handling scene changes while the editor is active. + + const auto* EDITOR = SHSystemManager::GetSystem(); + if (EDITOR && EDITOR->editorState == SHEditor::State::PLAY) + { + for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) + { + if (PHYSICS_OBJECT.rigidBody) + physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); + } + } + + #endif + return onSceneInitEvent.get()->handle; } SHEventHandle SHPhysicsSystem::onSceneExit(SHEventPtr onSceneExitEvent) { /* - * TODO: * Destroy the physics world. * Destroy all physics objects. */ + for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) + { + if (PHYSICS_OBJECT.rigidBody) + physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); + } + + delete physicsWorld; + physicsWorld = nullptr; + return onSceneExitEvent.get()->handle; } @@ -181,8 +229,17 @@ namespace SHADE // Link engine components with physics object component if (IS_RIGID_BODY) + { physicsObjectManager.AddRigidBody(EID); + if (physicsWorld) + { + auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody; + physicsWorld->AddRigidBody(rigidBody); + } + } + + if (IS_COLLIDER) physicsObjectManager.AddCollider(EID); } @@ -209,7 +266,16 @@ namespace SHADE // Link engine components with physics object component if (IS_RIGID_BODY) + { + if (physicsWorld) + { + auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody; + physicsWorld->RemoveRigidBody(rigidBody); + } + physicsObjectManager.RemoveRigidBody(EID); + } + if (IS_COLLIDER) physicsObjectManager.RemoveCollider(EID); @@ -222,10 +288,15 @@ namespace SHADE SHEventHandle SHPhysicsSystem::onEditorPlay(SHEventPtr onEditorPlayEvent) { - /* - * TODO: - * Link all entities with the world. - */ + // Add all physics components to the physics world + for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) + { + // Add rigid body if it exists + if (PHYSICS_OBJECT.rigidBody) + physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); + + // TODO: Add Collider if it exists + } return onEditorPlayEvent.get()->handle; } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index 1562241f..7d7dece5 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -16,6 +16,7 @@ #include "ECS_Base/System/SHFixedSystemRoutine.h" #include "Events/SHEvent.h" +#include "Physics/Dynamics/SHPhysicsWorld.h" #include "Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h" #include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" @@ -34,7 +35,8 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHPhysicsSystem(); + SHPhysicsSystem () noexcept; + ~SHPhysicsSystem() noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ @@ -75,9 +77,6 @@ namespace SHADE public: PhysicsPreUpdate(); void Execute(double dt) noexcept override; - - private: - void syncTransforms(SHPhysicsObject* physicsObject) const noexcept; }; /** @@ -103,9 +102,6 @@ namespace SHADE public: PhysicsPostUpdate(); void Execute(double dt) noexcept override; - - private: - void syncTransforms(SHRigidBodyComponent* rbComponent) const noexcept; }; private: @@ -136,6 +132,8 @@ namespace SHADE // Sub-systems / managers + SHPhysicsWorld* physicsWorld; + SHPhysicsObjectManager physicsObjectManager; /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h index 0664f367..ed190cc9 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h @@ -49,8 +49,9 @@ namespace SHADE /* Static Usage Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static const std::vector& GetCollisionInfo() noexcept; - [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; - [[nodiscard]] static double GetFixedDT () noexcept; + [[nodiscard]] static const std::vector& GetCollisionInfo () noexcept; + [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; + [[nodiscard]] static double GetFixedDT () noexcept; + [[nodiscard]] static int GetFixedUpdateRate () noexcept; }; } From 74e50e10bdc5c7c203b96238c31617e6fdeb87aa Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 9 Dec 2022 01:15:43 +0800 Subject: [PATCH 012/164] Refactored the colliders? This took me 4 days omg --- SHADE_Application/src/Scenes/SBMainScene.cpp | 2 +- SHADE_Application/src/Scenes/SBTestScene.cpp | 30 +- .../Inspector/SHEditorComponentView.hpp | 34 +- .../Inspector/SHEditorInspector.cpp | 2 +- SHADE_Engine/src/Math/Geometry/SHSphere.cpp | 44 --- SHADE_Engine/src/Math/Geometry/SHSphere.h | 119 ++++-- .../CollisionShapes/SHCollisionShape.cpp | 191 +++++++++ .../CollisionShapes}/SHCollisionShape.h | 83 ++-- .../SHCollisionShapeFactory.cpp | 77 ++++ .../CollisionShapes/SHCollisionShapeFactory.h | 86 ++++ .../CollisionShapes/SHCollisionShapeID.h | 101 +++++ .../CollisionShapes/SHCollisionShapeID.hpp | 66 ++++ .../SHSphereCollisionShape.cpp | 183 +++++++++ .../CollisionShapes/SHSphereCollisionShape.h | 123 ++++++ .../src/Physics/Collision/SHCollider.cpp | 327 ++++++++++++++++ .../src/Physics/Collision/SHCollider.h | 170 ++++++++ .../src/Physics/Collision/SHCollisionInfo.cpp | 11 +- .../src/Physics/Collision/SHCollisionInfo.h | 2 +- .../SHPhysicsMaterial.cpp | 0 .../SHPhysicsMaterial.h | 0 .../src/Physics/Dynamics/SHMotionState.cpp | 2 +- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 27 +- .../src/Physics/Dynamics/SHPhysicsWorld.h | 20 +- .../src/Physics/Dynamics/SHRigidBody.cpp | 13 + .../src/Physics/Dynamics/SHRigidBody.h | 13 +- .../PhysicsObject/SHPhysicsObject.cpp | 89 ++++- .../Interface/PhysicsObject/SHPhysicsObject.h | 53 ++- .../PhysicsObject/SHPhysicsObjectManager.cpp | 110 +++--- .../PhysicsObject/SHPhysicsObjectManager.h | 45 +-- .../Physics/Interface/SHColliderComponent.cpp | 54 +-- .../Physics/Interface/SHColliderComponent.h | 38 +- .../Physics/Interface/SHCollisionShape.cpp | 368 ------------------ .../SHRigidBodyComponent.cpp | 8 - .../SHRigidBodyComponent.h | 7 +- SHADE_Engine/src/Physics/SHPhysicsEvents.h | 2 +- .../Routines/SHPhysicsPostUpdateRoutine.cpp | 23 +- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 33 +- .../src/Physics/System/SHPhysicsSystem.cpp | 97 +++-- .../src/Physics/System/SHPhysicsSystem.h | 2 +- .../src/Serialization/SHSerialization.cpp | 2 +- .../src/Serialization/SHYAMLConverters.h | 34 +- SHADE_Managed/src/Components/Collider.cxx | 49 ++- SHADE_Managed/src/Components/Collider.h++ | 6 +- SHADE_Managed/src/Components/RigidBody.hxx | 2 +- SHADE_Managed/src/Engine/ECS.cxx | 2 +- 45 files changed, 1934 insertions(+), 816 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp rename SHADE_Engine/src/Physics/{Interface => Collision/CollisionShapes}/SHCollisionShape.h (75%) create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h create mode 100644 SHADE_Engine/src/Physics/Collision/SHCollider.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/SHCollider.h rename SHADE_Engine/src/Physics/{Interface => Collision}/SHPhysicsMaterial.cpp (100%) rename SHADE_Engine/src/Physics/{Interface => Collision}/SHPhysicsMaterial.h (100%) delete mode 100644 SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp rename SHADE_Engine/src/Physics/Interface/{RigidBodyComponent => }/SHRigidBodyComponent.cpp (98%) rename SHADE_Engine/src/Physics/Interface/{RigidBodyComponent => }/SHRigidBodyComponent.h (96%) diff --git a/SHADE_Application/src/Scenes/SBMainScene.cpp b/SHADE_Application/src/Scenes/SBMainScene.cpp index 6c875518..dd713980 100644 --- a/SHADE_Application/src/Scenes/SBMainScene.cpp +++ b/SHADE_Application/src/Scenes/SBMainScene.cpp @@ -11,7 +11,7 @@ #include "Scripting/SHScriptEngine.h" #include "Math/Transform/SHTransformComponent.h" #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" #include "Graphics/MiddleEnd/Lights/SHLightComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index 9e3fa8ab..a300e6b9 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -10,7 +10,7 @@ #include "Scripting/SHScriptEngine.h" #include "Math/Transform/SHTransformComponent.h" #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" #include "Graphics/MiddleEnd/Lights/SHLightComponent.h" @@ -92,7 +92,7 @@ namespace Sandbox floorRigidBody.SetType(SHRigidBodyComponent::Type::STATIC); - floorCollider.AddBoundingBox(); + //floorCollider.AddBoundingBox(); // Create blank entity with a script //testObj = SHADE::SHEntityManager::CreateEntity(); @@ -113,9 +113,9 @@ namespace Sandbox racoonTransform.SetWorldScale({ 2.0f, 2.0f, 2.0f }); racoonTransform.SetWorldPosition({ -3.0f, -2.0f, -5.0f }); - racoonCollider.AddBoundingBox(); - racoonCollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f,0.5f,0.0f)); - racoonCollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + //racoonCollider.AddBoundingBox(); + //racoonCollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f,0.5f,0.0f)); + //racoonCollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); auto racoonItemLocation = SHEntityManager::CreateEntity(); auto& racoonItemLocationTransform = *SHComponentManager::GetComponent_s(racoonItemLocation); @@ -138,15 +138,15 @@ namespace Sandbox itemTransform.SetWorldScale({ 2.0f, 2.0f, 2.0f }); itemTransform.SetWorldPosition({ 0.0f, -2.0f, -5.0f }); - itemCollider.AddBoundingBox(); - itemCollider.AddBoundingBox(SHVec3(2.0f,2.0f,2.0f)); - itemCollider.GetCollisionShape(1).SetIsTrigger(true); + //itemCollider.AddBoundingBox(); + //itemCollider.AddBoundingBox(SHVec3(2.0f,2.0f,2.0f)); + //itemCollider.GetCollisionShape(1).SetIsTrigger(true); - itemCollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - itemCollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + //itemCollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + //itemCollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); - itemCollider.GetCollisionShape(1).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - itemCollider.GetCollisionShape(1).SetBoundingBox(SHVec3(1.0f, 1.0f, 1.0f)); + //itemCollider.GetCollisionShape(1).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + //itemCollider.GetCollisionShape(1).SetBoundingBox(SHVec3(1.0f, 1.0f, 1.0f)); itemRigidBody.SetInterpolate(false); itemRigidBody.SetFreezeRotationX(true); @@ -167,9 +167,9 @@ namespace Sandbox AITransform.SetWorldScale({ 2.0f, 2.0f, 2.0f }); AITransform.SetWorldPosition({ -8.0f, -2.0f, 2.5f }); - AICollider.AddBoundingBox(); - AICollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - AICollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + //AICollider.AddBoundingBox(); + //AICollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + //AICollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); AIRigidBody.SetInterpolate(false); AIRigidBody.SetFreezeRotationX(true); diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 71d607f5..774697f1 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -322,37 +322,37 @@ namespace SHADE { DrawContextMenu(component); - auto& colliders = component->GetCollisionShapes(); - int const size = static_cast(colliders.size()); - ImGui::BeginChild("Collision Shapes", { 0.0f, colliders.empty() ? 1.0f : 250.0f }, true); + auto* colliders = component->GetCollisionShapes(); + int const size = colliders ? static_cast(colliders->size()) : 0; + ImGui::BeginChild("Collision Shapes", { 0.0f, colliders->empty() ? 1.0f : 250.0f }, true); std::optional colliderToDelete{ std::nullopt }; for (int i{}; i < size; ++i) { ImGui::PushID(i); - SHCollisionShape* collider = &component->GetCollisionShape(i); + SHCollisionShape* collider = component->GetCollisionShape(i); auto cursorPos = ImGui::GetCursorPos(); //collider->IsTrigger if (collider->GetType() == SHCollisionShape::Type::BOX) { - SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - - const auto* BOX = reinterpret_cast(collider->GetShape()); - SHEditorWidgets::DragVec3 - ( - "Half Extents", { "X", "Y", "Z" }, - [BOX] { return BOX->GetRelativeExtents(); }, - [collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); }); + //SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); + // + //const auto* BOX = reinterpret_cast(collider->GetShape()); + //SHEditorWidgets::DragVec3 + //( + // "Half Extents", { "X", "Y", "Z" }, + // [BOX] { return BOX->GetRelativeExtents(); }, + // [collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); }); } else if (collider->GetType() == SHCollisionShape::Type::SPHERE) { SHEditorWidgets::BeginPanel(std::format("{} Sphere #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - const auto* SPHERE = reinterpret_cast(collider->GetShape()); + auto* SPHERE = reinterpret_cast(collider); SHEditorWidgets::DragFloat ( "Radius", [SPHERE] { return SPHERE->GetRelativeRadius(); }, - [collider](float const& value) { collider->SetBoundingSphere(value); }); + [SPHERE](float const& value) { SPHERE->SetRelativeRadius(value); }); } else if (collider->GetType() == SHCollisionShape::Type::CAPSULE) { @@ -393,7 +393,7 @@ namespace SHADE } if (colliderToDelete.has_value()) { - component->RemoveCollider(colliderToDelete.value()); + component->GetCollider()->RemoveCollisionShape(colliderToDelete.value()); } ImGui::EndChild(); @@ -401,11 +401,11 @@ namespace SHADE { if (ImGui::Selectable("Box Collider")) { - component->AddBoundingBox(); + //component->AddBoundingBox(); } if (ImGui::Selectable("Sphere Collider")) { - component->AddBoundingSphere(); + component->GetCollider()->AddSphereCollisionShape(1.0f); } ImGui::EndMenu(); } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index f0d3e3e5..83647da7 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -14,7 +14,7 @@ #include "Scripting/SHScriptEngine.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" #include "Camera/SHCameraComponent.h" #include "Camera/SHCameraArmComponent.h" diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp index 54935251..7dca00c0 100644 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp @@ -25,13 +25,11 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHSphere::SHSphere() noexcept - : RelativeRadius { 1.0f } { type = Type::SPHERE; } SHSphere::SHSphere(const SHVec3& center, float radius) noexcept - : RelativeRadius { 1.0f } { type = Type::SPHERE; @@ -48,7 +46,6 @@ namespace SHADE Center = rhs.Center; Radius = rhs.Radius; - RelativeRadius = rhs.RelativeRadius; } SHSphere::SHSphere(SHSphere&& rhs) noexcept @@ -57,7 +54,6 @@ namespace SHADE Center = rhs.Center; Radius = rhs.Radius; - RelativeRadius = rhs.RelativeRadius; } /*-----------------------------------------------------------------------------------*/ @@ -74,7 +70,6 @@ namespace SHADE { Center = rhs.Center; Radius = rhs.Radius; - RelativeRadius = rhs.RelativeRadius; } return *this; @@ -90,50 +85,11 @@ namespace SHADE { Center = rhs.Center; Radius = rhs.Radius; - RelativeRadius = rhs.RelativeRadius; } return *this; } - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHVec3 SHSphere::GetCenter() const noexcept - { - return Center; - } - - float SHSphere::GetWorldRadius() const noexcept - { - return Radius; - } - - float SHSphere::GetRelativeRadius() const noexcept - { - return RelativeRadius; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHSphere::SetCenter(const SHVec3& center) noexcept - { - Center = center; - } - - void SHSphere::SetWorldRadius(float newWorldRadius) noexcept - { - Radius = newWorldRadius; - } - - void SHSphere::SetRelativeRadius(float newRelativeRadius) noexcept - { - RelativeRadius = newRelativeRadius; - } - /*-----------------------------------------------------------------------------------*/ /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.h b/SHADE_Engine/src/Math/Geometry/SHSphere.h index e056f21a..91771a21 100644 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.h +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.h @@ -23,7 +23,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ class SH_API SHSphere : public SHShape, - private DirectX::BoundingSphere + public DirectX::BoundingSphere { public: /*---------------------------------------------------------------------------------*/ @@ -32,8 +32,8 @@ namespace SHADE SHSphere () noexcept; SHSphere (const SHVec3& center, float radius) noexcept; - SHSphere (const SHSphere& rhs) noexcept; - SHSphere (SHSphere&& rhs) noexcept; + SHSphere (const SHSphere& rhs) noexcept; + SHSphere (SHSphere&& rhs) noexcept; ~SHSphere () override = default; @@ -44,49 +44,100 @@ namespace SHADE SHSphere& operator= (const SHSphere& rhs) noexcept; SHSphere& operator= (SHSphere&& rhs) noexcept; - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] float GetWorldRadius () const noexcept; - [[nodiscard]] float GetRelativeRadius () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetCenter (const SHVec3& center) noexcept; - void SetWorldRadius (float newWorldRadius) noexcept; - void SetRelativeRadius (float newRelativeRadius) noexcept; - /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * Checks if a point is inside the sphere. + * @param point + * The point to check. + * @return + * True if the point is inside the sphere. + */ [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - [[nodiscard]] SHRaycastResult Raycast(const SHRay& ray) const noexcept override; - [[nodiscard]] bool Contains (const SHSphere& rhs) const noexcept; - [[nodiscard]] float Volume () const noexcept; - [[nodiscard]] float SurfaceArea () const noexcept; + /** + * @brief + * Casts a ray against the sphere. + * @param ray + * The ray to cast. + * @return + * The result of the raycast.
+ * See the corresponding header for the contents of the raycast result object. + */ + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + + /** + * @brief + * Checks if an entire other sphere is contained by this sphere. + * @param rhs + * The sphere to check. + * @return + * True if the other sphere is completely contained by this sphere. + */ + [[nodiscard]] bool Contains (const SHSphere& rhs) const noexcept; + + /** + * @brief + * Calculates the volume of the sphere. + */ + [[nodiscard]] float Volume () const noexcept; + + /** + * @brief + * Calculates the surface area of the sphere. + */ + [[nodiscard]] float SurfaceArea () const noexcept; /*---------------------------------------------------------------------------------*/ /* Static Function Members */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static SHSphere Combine (const SHSphere& lhs, const SHSphere& rhs) noexcept; - [[nodiscard]] static bool Intersect (const SHSphere& lhs, const SHSphere& rhs) noexcept; - [[nodiscard]] static SHSphere BuildFromSpheres (const SHSphere* spheres, size_t numSpheres) noexcept; + /** + * @brief + * Combines two spheres to form a larger sphere. + * If one sphere is completely contained by the other, the result is the larger sphere. + * @return + * The combined sphere. + */ + [[nodiscard]] static SHSphere Combine (const SHSphere& lhs, const SHSphere& rhs) noexcept; + + /** + * @brief + * Checks if two spheres are intersecting. + * @return + * True if they are intersecting. + */ + [[nodiscard]] static bool Intersect (const SHSphere& lhs, const SHSphere& rhs) noexcept; + + /** + * @brief + * Builds a single sphere from multiple spheres. + * @param spheres + * The set of spheres to build from. + * @param numSpheres + * The number of spheres in the set to build from. + * @return + * A sphere that contains all the spheres in the set. + */ + [[nodiscard]] static SHSphere BuildFromSpheres (const SHSphere* spheres, size_t numSpheres) noexcept; + + /** + * @brief + * Builds a sphere from a set of vertices. + * @param vertices + * The vertices to build a sphere from. + * @param numVertices + * The number of vertices in the set to build from. + * @param stride + * The stride between each vertex, in the instance there is data in between each + * vertex that does not define the geometry of the object. + * @return + * A sphere that contains all the vertices in the set. + */ [[nodiscard]] static SHSphere BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; - - private: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - float RelativeRadius; - }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp new file mode 100644 index 00000000..845634fe --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -0,0 +1,191 @@ +/**************************************************************************************** + * \file SHCollider.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Collider. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollisionShape.h" + +// Project Headers +#include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Reflection/SHReflectionMetadata.h" +#include "Tools/Utilities/SHUtilities.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionShape::SHCollisionShape(SHCollisionShapeID id, Type colliderType) + : id { id } + , flags { 0 } + , collisionTag { SHCollisionTagMatrix::GetTag(0) } + { + flags |= 1U << SHUtilities::ConvertEnum(colliderType); + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + float SHCollisionShape::GetFriction() const noexcept + { + return material.GetFriction(); + } + + float SHCollisionShape::GetBounciness() const noexcept + { + return material.GetBounciness(); + } + + float SHCollisionShape::GetDensity() const noexcept + { + return material.GetDensity(); + } + + const SHPhysicsMaterial& SHCollisionShape::GetMaterial() const noexcept + { + return material; + } + + const SHVec3& SHCollisionShape::GetPositionOffset() const noexcept + { + return positionOffset; + } + + const SHVec3& SHCollisionShape::GetRotationOffset() const noexcept + { + return rotationOffset; + } + + SHCollisionShape::Type SHCollisionShape::GetType() const noexcept + { + for (int i = 0; i < SHUtilities::ConvertEnum(Type::COUNT); ++i) + { + const uint8_t FLAG_VALUE = 1U << SHUtilities::ConvertEnum(static_cast(i)); + + if (flags & FLAG_VALUE) + return static_cast(i); + } + + return Type::INVALID; + } + + bool SHCollisionShape::IsTrigger() const noexcept + { + static constexpr int FLAG_POS = 3; + static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; + + return flags & FLAG_VALUE; + } + + bool SHCollisionShape::IsColliding() const noexcept + { + static constexpr int FLAG_POS = 4; + static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; + + return flags & FLAG_VALUE; + } + + bool SHCollisionShape::GetDebugDrawState() const noexcept + { + static constexpr int FLAG_POS = 6; + static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; + + return flags & FLAG_VALUE; + } + + const SHCollisionTag& SHCollisionShape::GetCollisionTag() const noexcept + { + return *collisionTag; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionShape::SetFriction(float friction) noexcept + { + material.SetFriction(friction); + } + + void SHCollisionShape::SetBounciness(float bounciness) noexcept + { + material.SetBounciness(bounciness); + } + + void SHCollisionShape::SetDensity(float density) noexcept + { + material.SetDensity(density); + } + + void SHCollisionShape::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept + { + material = newMaterial; + } + + void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept + { + positionOffset = posOffset; + } + + void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept + { + rotationOffset = rotOffset; + } + + void SHCollisionShape::SetIsTrigger(bool isTrigger) noexcept + { + static constexpr int FLAG_POS = 3; + static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; + + isTrigger ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; + } + + void SHCollisionShape::SetDebugDrawState(bool isDebugDrawing) noexcept + { + static constexpr int FLAG_POS = 6; + static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; + + isDebugDrawing ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; + } + + void SHCollisionShape::SetCollisionTag(SHCollisionTag* newCollisionTag) noexcept + { + collisionTag = newCollisionTag; + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + +} // namespace SHADE + +RTTR_REGISTRATION +{ + using namespace SHADE; + using namespace rttr; + + registration::enumeration("Collider Type") + ( + value("Box", SHCollisionShape::Type::BOX), + value("Sphere", SHCollisionShape::Type::SPHERE) + // TODO(Diren): Add More Shapes + ); + + registration::class_("Collider") + .property("IsTrigger" , &SHCollisionShape::IsTrigger , &SHCollisionShape::SetIsTrigger ) + .property("Friction" , &SHCollisionShape::GetFriction , &SHCollisionShape::SetFriction ) + .property("Bounciness" , &SHCollisionShape::GetBounciness , &SHCollisionShape::SetBounciness ) + .property("Density" , &SHCollisionShape::GetDensity , &SHCollisionShape::SetDensity ) + .property("Position Offset" , &SHCollisionShape::GetPositionOffset, &SHCollisionShape::SetPositionOffset) + .property("Rotation Offset" , &SHCollisionShape::GetRotationOffset, &SHCollisionShape::SetRotationOffset) (metadata(META::angleInRad, true)); +} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h similarity index 75% rename from SHADE_Engine/src/Physics/Interface/SHCollisionShape.h rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 597814a6..420c0d6e 100644 --- a/SHADE_Engine/src/Physics/Interface/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCollider.h + * \file SHCollisionShape.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collider. + * \brief Interface for a Base CollisionShape Class. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -16,8 +16,9 @@ #include "ECS_Base/Entity/SHEntity.h" #include "Math/Geometry/SHShape.h" #include "Math/SHQuaternion.h" -#include "SHPhysicsMaterial.h" #include "Physics/Collision/SHCollisionTags.h" +#include "Physics/Collision/SHPhysicsMaterial.h" +#include "SHCollisionShapeID.h" namespace SHADE { @@ -34,7 +35,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ friend class SHColliderComponent; - friend class SHPhysicsObject; + friend class SHCollisionShapeFactory; public: /*---------------------------------------------------------------------------------*/ @@ -46,87 +47,87 @@ namespace SHADE BOX , SPHERE , CAPSULE + + , COUNT + , INVALID = -1 }; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHCollisionShape (EntityID eid, Type colliderType = Type::BOX, const SHPhysicsMaterial& physicsMaterial = SHPhysicsMaterial::DEFAULT); + SHCollisionShape (SHCollisionShapeID id, Type colliderType = Type::BOX); - SHCollisionShape (const SHCollisionShape& rhs) noexcept; - SHCollisionShape (SHCollisionShape&& rhs) noexcept; - ~SHCollisionShape () noexcept; + SHCollisionShape (const SHCollisionShape& rhs) noexcept = default; + SHCollisionShape (SHCollisionShape&& rhs) noexcept = default; + virtual ~SHCollisionShape () noexcept = default; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHCollisionShape& operator=(const SHCollisionShape& rhs) noexcept; - SHCollisionShape& operator=(SHCollisionShape&& rhs) noexcept; + SHCollisionShape& operator=(const SHCollisionShape& rhs) noexcept = default; + SHCollisionShape& operator=(SHCollisionShape&& rhs) noexcept = default; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] bool HasChanged () const noexcept; - - [[nodiscard]] bool IsTrigger () const noexcept; - - [[nodiscard]] Type GetType () const noexcept; - - [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; + // Material Properties + // TODO: Remove individual setters once instanced materials are supported [[nodiscard]] float GetFriction () const noexcept; [[nodiscard]] float GetBounciness () const noexcept; [[nodiscard]] float GetDensity () const noexcept; [[nodiscard]] const SHPhysicsMaterial& GetMaterial () const noexcept; + // Offsets + [[nodiscard]] const SHVec3& GetPositionOffset () const noexcept; [[nodiscard]] const SHVec3& GetRotationOffset () const noexcept; - [[nodiscard]] const SHShape* GetShape () const noexcept; + // Flags + + [[nodiscard]] Type GetType () const noexcept; + [[nodiscard]] bool IsTrigger () const noexcept; + [[nodiscard]] bool IsColliding () const noexcept; + [[nodiscard]] bool GetDebugDrawState () const noexcept; + + + [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetBoundingBox (const SHVec3& halfExtents); - void SetBoundingSphere (float radius); - - void SetIsTrigger (bool isTrigger) noexcept; - void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; void SetFriction (float friction) noexcept; void SetBounciness (float bounciness) noexcept; void SetDensity (float density) noexcept; void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; - void SetPositionOffset (const SHVec3& posOffset) noexcept; - void SetRotationOffset (const SHVec3& rotOffset) noexcept; + void SetPositionOffset (const SHVec3& posOffset) noexcept; + void SetRotationOffset (const SHVec3& rotOffset) noexcept; - private: + // Flags + void SetIsTrigger (bool isTrigger) noexcept; + void SetDebugDrawState (bool isDebugDrawing) noexcept; + + void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; + + protected: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - Type type; - EntityID entityID; // The entity this collider belongs to - bool isTrigger; - bool dirty; + SHCollisionShapeID id; - SHShape* shape; - SHPhysicsMaterial material; + SHPhysicsMaterial material; - SHVec3 positionOffset; - SHVec3 rotationOffset; + SHVec3 positionOffset; + SHVec3 rotationOffset; - SHCollisionTag* collisionTag; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - void CopyShape(const SHShape* rhs); + uint8_t flags; // 0 debugDraw wasColliding isColliding trigger capsule sphere box + SHCollisionTag* collisionTag; RTTR_ENABLE() }; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp new file mode 100644 index 00000000..293d5c2a --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp @@ -0,0 +1,77 @@ +/**************************************************************************************** + * \file SHCollisionShapeFactory.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Collison Shape Factory Class. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollisionShapeFactory.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionShapeFactory::~SHCollisionShapeFactory() noexcept + { + // Free all shapes in each container + for (auto* sphereCollisionShape : spheres | std::views::values) + DestroyShape(sphereCollisionShape); + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHSphereCollisionShape* SHCollisionShapeFactory::CreateSphere(SHCollisionShapeID id, const SHSphereCreateInfo& createInfo) + { + const auto RESULT = spheres.emplace(id, new SHSphereCollisionShape{ id }); + if (RESULT.second) + { + SHSphereCollisionShape* sphere = RESULT.first->second; + + sphere->Center = createInfo.Center; + sphere->Radius = createInfo.Radius; + sphere->relativeRadius = createInfo.RelativeRadius; + sphere->scale = createInfo.Scale; + + return sphere; + } + + return spheres.find(id)->second; + } + + void SHCollisionShapeFactory::DestroyShape(SHCollisionShape* shape) + { + switch (shape->GetType()) + { + case SHCollisionShape::Type::BOX: + { + break; + } + case SHCollisionShape::Type::SPHERE: + { + SHSphereCollisionShape* sphere = spheres.find(shape->id)->second; + + delete sphere; + sphere = nullptr; + + spheres.erase(shape->id); + + break; + } + case SHCollisionShape::Type::CAPSULE: + { + break; + } + default: break; + } + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h new file mode 100644 index 00000000..f5820317 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h @@ -0,0 +1,86 @@ +/**************************************************************************************** + * \file SHCollisionShapeFactory.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Collison Shape Factory Class. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Header +#include "SHSphereCollisionShape.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates a class for Creating and Destroying Collision Shapes.
+ * All memory for collision shapes are handled in this factory class.
+ * TODO: Support instancing of shapes + */ + class SH_API SHCollisionShapeFactory final + { + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionShapeFactory () noexcept = default; + ~SHCollisionShapeFactory () noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Creates a sphere collision shape. + * @param id + * The ID of the shape. + * @param createInfo + * The info to create the sphere with. + * @return + * A newly sphere collision shape. + */ + SHSphereCollisionShape* CreateSphere (SHCollisionShapeID id, const SHSphereCreateInfo& createInfo); + + /** + * @brief + * Destroys a collision shape. + * * @param eid + * The entity the shape belongs to. + * @param shapeID + * The ID of the shape. + * @param shape + * The shape to destroy. + */ + void DestroyShape (SHCollisionShape* shape); + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + // We use unordered maps for fast lookup when deleting. + // Since we are not instancing shapes (yet?), I'd rather not iterate through an entire vector to find the shape. + + using Spheres = std::unordered_map; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + Spheres spheres; + // TODO: Add boxes, capsules and hulls + }; + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h new file mode 100644 index 00000000..a0f78177 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h @@ -0,0 +1,101 @@ +/**************************************************************************************** + * \file SHCollisionShapeKey.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Collison Shape ID Class and it's hashing function + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include "ECS_Base/Entity/SHEntity.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + struct SHCollisionShapeIDHash; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates an identifier for a collision shape. + */ + union SHCollisionShapeID + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend struct SHCollisionShapeIDHash; + + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionShapeID (EntityID eid, uint32_t shapeID) noexcept; + SHCollisionShapeID (const SHCollisionShapeID& rhs) noexcept; + SHCollisionShapeID (SHCollisionShapeID&& rhs) noexcept; + + ~SHCollisionShapeID () noexcept = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionShapeID& operator=(const SHCollisionShapeID& rhs) noexcept; + SHCollisionShapeID& operator=(SHCollisionShapeID&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + bool operator==(const SHCollisionShapeID& rhs) const; + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + struct IDs + { + public: + EntityID entityID = MAX_EID; + uint32_t shapeID = std::numeric_limits::max(); + }; + + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + uint64_t value; + IDs ids; + }; + + /** + * @brief + * Encapsulates a functor to hash a CollisionShapeKey + */ + struct SHCollisionShapeIDHash + { + public: + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + std::size_t operator()(const SHCollisionShapeID& id) const; + }; + +} // namespace SHADE + +#include "SHCollisionShapeID.hpp" diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp new file mode 100644 index 00000000..234b2f6f --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp @@ -0,0 +1,66 @@ +/**************************************************************************************** + * \file SHCollisionShapeKey.hpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Inlined Implementations for a Collison Shape ID Class and + * it's hashing function. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Primary Header +#include "SHCollisionShapeID.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + inline SHCollisionShapeID::SHCollisionShapeID(EntityID eid, uint32_t shapeID) noexcept + : ids { eid, shapeID } + {} + + inline SHCollisionShapeID::SHCollisionShapeID(const SHCollisionShapeID& rhs) noexcept + : ids { rhs.ids.entityID, rhs.ids.shapeID } + {} + + inline SHCollisionShapeID::SHCollisionShapeID(SHCollisionShapeID&& rhs) noexcept + : ids { rhs.ids.entityID, rhs.ids.shapeID } + {} + + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + inline SHCollisionShapeID& SHCollisionShapeID::operator=(const SHCollisionShapeID& rhs) noexcept + { + if (this == &rhs) + return *this; + + value = rhs.value; + + return *this; + } + + inline SHCollisionShapeID& SHCollisionShapeID::operator=(SHCollisionShapeID&& rhs) noexcept + { + value = rhs.value; + + return *this; + } + + inline bool SHCollisionShapeID::operator==(const SHCollisionShapeID& rhs) const + { + return value == rhs.value; + } + + inline std::size_t SHCollisionShapeIDHash::operator()(const SHCollisionShapeID& id) const + { + return std::hash()(id.value); + } +} diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp new file mode 100644 index 00000000..3f79b381 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -0,0 +1,183 @@ +/**************************************************************************************** + * \file SHSphereCollisionShape.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Sphere Collision Shape. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHSphereCollisionShape.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHSphereCollisionShape::SHSphereCollisionShape(SHCollisionShapeID id) noexcept + : SHCollisionShape (id, SHCollisionShape::Type::SPHERE) + , SHSphere () + , relativeRadius { 1.0f } + , scale { 1.0f } + {} + + SHSphereCollisionShape::SHSphereCollisionShape(const SHSphereCollisionShape& rhs) noexcept + : SHCollisionShape (rhs.id, SHCollisionShape::Type::SPHERE) + , SHSphere (rhs.Center, rhs.Radius) + , relativeRadius { rhs.relativeRadius } + , scale { rhs.scale } + { + + material = rhs.material; + positionOffset = rhs.positionOffset; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + } + + SHSphereCollisionShape::SHSphereCollisionShape(SHSphereCollisionShape&& rhs) noexcept + : SHCollisionShape (rhs.id, SHCollisionShape::Type::SPHERE) + , SHSphere (rhs.Center, rhs.Radius) + , relativeRadius { rhs.relativeRadius } + , scale { rhs.scale } + { + + material = rhs.material; + positionOffset = rhs.positionOffset; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHSphereCollisionShape& SHSphereCollisionShape::operator=(const SHSphereCollisionShape& rhs) noexcept + { + if (this == &rhs) + return *this; + + // Collision Shape Properties + + id = rhs.id; + material = rhs.material; + positionOffset = rhs.positionOffset; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + + // Sphere Properties + + Center = rhs.Center; + Radius = rhs.Radius; + + // Local Properties + + relativeRadius = rhs.relativeRadius; + scale = rhs.scale; + + return *this; + } + + SHSphereCollisionShape& SHSphereCollisionShape::operator=(SHSphereCollisionShape&& rhs) noexcept + { + // Collision Shape Properties + + id = rhs.id; + material = rhs.material; + positionOffset = rhs.positionOffset; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + + // Sphere Properties + + Center = rhs.Center; + Radius = rhs.Radius; + + // Local Properties + + relativeRadius = rhs.relativeRadius; + scale = rhs.scale; + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHVec3& SHSphereCollisionShape::GetCenter() const noexcept + { + return Center; + } + + float SHSphereCollisionShape::GetWorldRadius() const noexcept + { + return Radius; + } + + float SHSphereCollisionShape::GetRelativeRadius() const noexcept + { + return relativeRadius; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHSphereCollisionShape::SetCenter(const SHVec3& newCenter) noexcept + { + Center = newCenter; + } + + void SHSphereCollisionShape::SetWorldRadius(float newWorldRadius) noexcept + { + Radius = newWorldRadius; + + // Recompute Relative radius + relativeRadius = 2.0f * Radius / scale; + } + + void SHSphereCollisionShape::SetRelativeRadius(float newRelativeRadius) noexcept + { + relativeRadius = newRelativeRadius; + + // Recompute world radius + Radius = relativeRadius * scale * 0.5f; + } + + void SHSphereCollisionShape::SetScale(float maxScale) noexcept + { + scale = std::fabs(maxScale); + + // Recompute world radius + Radius = relativeRadius * scale * 0.5f; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHSphereCollisionShape::TestPoint(const SHVec3& point) const noexcept + { + return SHSphere::TestPoint(point); + } + + SHRaycastResult SHSphereCollisionShape::Raycast(const SHRay& ray) const noexcept + { + return SHSphere::Raycast(ray); + } + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h new file mode 100644 index 00000000..0a8c0321 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -0,0 +1,123 @@ +/**************************************************************************************** + * \file SHSphereCollisionShape.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Sphere Collision Shape. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Math/Geometry/SHSphere.h" +#include "SHCollisionShape.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates the information to create a sphere. + */ + struct SHSphereCreateInfo + { + public: + SHVec3 Center = SHVec3::Zero; + float Radius = 1.0f; + float RelativeRadius = 1.0f; + float Scale = 1.0f; + }; + + /** + * @brief + * Encapsulate a Sphere Collision Shape used for Physics Simulations. + */ + class SH_API SHSphereCollisionShape : public SHCollisionShape + , private SHSphere + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHCollider; + friend class SHCompositeCollider; + friend class SHCollisionShapeFactory; + + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHSphereCollisionShape (SHCollisionShapeID id) noexcept; + SHSphereCollisionShape (const SHSphereCollisionShape& rhs) noexcept; + SHSphereCollisionShape (SHSphereCollisionShape&& rhs) noexcept; + + ~SHSphereCollisionShape () override = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHSphereCollisionShape& operator= (const SHSphereCollisionShape& rhs) noexcept; + SHSphereCollisionShape& operator= (SHSphereCollisionShape&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] const SHVec3& GetCenter () const noexcept; + [[nodiscard]] float GetWorldRadius () const noexcept; + [[nodiscard]] float GetRelativeRadius () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetCenter (const SHVec3& newCenter) noexcept; + void SetWorldRadius (float newWorldRadius) noexcept; + void SetRelativeRadius (float newRelativeRadius) noexcept; + void SetScale (float maxScale) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Tests if a point is inside the sphere. + * @param point + * The point to test. + * @return + * True if the point is inside the sphere. + */ + bool TestPoint (const SHVec3& point) const noexcept override; + + /** + * @brief + * Casts a ray against this sphere. + * @param ray + * The ray to cast. + * @return + * An object holding the results of the raycast.
+ * See the corresponding header for the contents of the object. + */ + SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + + // TODO: Compute Moment of Inertia + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + float relativeRadius; + float scale; // Intended to be passed in by the base collider. + }; + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp new file mode 100644 index 00000000..e950c4fb --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -0,0 +1,327 @@ +/**************************************************************************************** + * \file SHCollider.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Base Collider Class. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollider.h" + +// Project Headers +#include "Math/SHMathHelpers.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept + : entityID { eid } + , shapeIDCounter { 0 } + , rigidBody { nullptr } + , shapeFactory { nullptr } + , transform { worldTransform } + {} + + SHCollider::SHCollider(const SHCollider& rhs) noexcept + : entityID { rhs.entityID } + , shapeIDCounter { rhs.shapeIDCounter } + , rigidBody { rhs.rigidBody } + , shapeFactory { rhs.shapeFactory } + , transform { rhs.transform } + { + if (!shapeFactory) + { + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) + return; + } + + copyShapes(rhs); + } + + SHCollider::SHCollider(SHCollider&& rhs) noexcept + : entityID { rhs.entityID } + , shapeIDCounter { rhs.shapeIDCounter } + , rigidBody { rhs.rigidBody } + , shapeFactory { rhs.shapeFactory } + , transform { rhs.transform } + { + if (!shapeFactory) + { + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) + return; + } + + copyShapes(rhs); + } + + SHCollider::~SHCollider() noexcept + { + if (!shapeFactory) + { + SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add destroy collider!", entityID) + return; + } + + for (auto* shape : shapes) + shapeFactory->DestroyShape(shape); + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollider& SHCollider::operator=(const SHCollider& rhs) noexcept + { + if (this == &rhs) + return *this; + + if (!shapeFactory) + { + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) + return *this; + } + + entityID = rhs.entityID; + rigidBody = rhs.rigidBody; + shapeFactory = rhs.shapeFactory; + transform = rhs.transform; + + copyShapes(rhs); + + return *this; + } + + SHCollider& SHCollider::operator=(SHCollider&& rhs) noexcept + { + if (!shapeFactory) + { + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) + return *this; + } + + entityID = rhs.entityID; + rigidBody = rhs.rigidBody; + shapeFactory = rhs.shapeFactory; + transform = rhs.transform; + + copyShapes(rhs); + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHTransform& SHCollider::GetTransform() const noexcept + { + return transform; + } + + const SHVec3& SHCollider::GetPosition() const noexcept + { + return transform.position; + } + + const SHQuaternion& SHCollider::GetOrientation() const noexcept + { + return transform.orientation; + } + + const SHVec3& SHCollider::GetScale() const noexcept + { + return transform.scale; + } + + const SHCollider::CollisionShapes& SHCollider::GetCollisionShapes() const noexcept + { + return shapes; + } + + SHCollisionShape* SHCollider::GetCollisionShape(int index) const + { + const int NUM_SHAPES = static_cast(shapes.size()); + + if (index < 0 || index >= NUM_SHAPES) + throw std::invalid_argument("Out-of-range index!"); + + return shapes[index]; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollider::SetRigidBody(SHRigidBody* rb) noexcept + { + rigidBody = rb; + } + + void SHCollider::SetTransform(const SHTransform& newTransform) noexcept + { + transform = newTransform; + } + + void SHCollider::SetPosition(const SHVec3& newPosition) noexcept + { + transform.position = newPosition; + } + + void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept + { + transform.orientation = newOrientation; + } + + void SHCollider::SetScale(const SHVec3& newScale) noexcept + { + transform.scale = newScale; + } + + void SHCollider::SetFactory(SHCollisionShapeFactory* factory) noexcept + { + shapeFactory = factory; + } + + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHMatrix& SHCollider::ComputeTransformTRS() noexcept + { + return transform.ComputeTRS(); + } + + int SHCollider::AddSphereCollisionShape(float relativeRadius, const SHVec3& posOffset, const SHVec3& rotOffset) + { + if (!shapeFactory) + { + SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add new shape!", entityID) + return -1; + } + + // Compute world radius + const float SPHERE_SCALE = std::fabs(SHMath::Max({ transform.scale.x, transform.scale.y, transform.scale.z })); + + // Compute center + const SHQuaternion FINAL_ROT = transform.orientation * SHQuaternion::FromEuler(rotOffset); + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(transform.position); + + // Create Sphere + const SHSphereCreateInfo SPHERE_CREATE_INFO + { + .Center = SHVec3::Transform(posOffset, TRS) + , .Radius = relativeRadius * SPHERE_SCALE * 0.5f + , .RelativeRadius = relativeRadius + , .Scale = SPHERE_SCALE + }; + + const SHCollisionShapeID NEW_SHAPE_ID{ entityID, shapeIDCounter }; + + SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); + ++shapeIDCounter; + + // Set offsets + sphere->positionOffset = posOffset; + sphere->rotationOffset = rotOffset; + + shapes.emplace_back(sphere); + return static_cast(shapes.size()); + } + + void SHCollider::RemoveCollisionShape(int index) + { + if (!shapeFactory) + { + SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add remove shape!", entityID) + return; + } + + const int NUM_SHAPES = static_cast(shapes.size()); + + if (index < 0 || index >= NUM_SHAPES) + throw std::invalid_argument("Out-of-range index!"); + + auto shapeIter = shapes.begin(); + for (int i = 0; i < NUM_SHAPES; ++i, ++shapeIter) + { + if (i == index) + break; + } + + shapeFactory->DestroyShape(*shapeIter); + *shapeIter = nullptr; + + // Remove the shape from the container to prevent accessing a nullptr + shapeIter = shapes.erase(shapeIter); + + SHLOG_INFO_D("Removing Collision Shape {} from Entity {}", index, entityID) + } + + void SHCollider::RecomputeShapes() const noexcept + { + + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollider::copyShapes(const SHCollider& rhsCollider) + { + shapeIDCounter = 0; + for (const auto* shape : rhsCollider.shapes) + { + copyShape(shape); + ++shapeIDCounter; + } + } + + void SHCollider::copyShape(const SHCollisionShape* rhsShape) + { + switch (rhsShape->GetType()) + { + case SHCollisionShape::Type::BOX: + { + break; + } + case SHCollisionShape::Type::SPHERE: + { + const SHSphereCollisionShape* RHS_SPHERE = dynamic_cast(rhsShape); + + const SHSphereCreateInfo SPHERE_CREATE_INFO + { + .Center = RHS_SPHERE->Center + , .Radius = RHS_SPHERE->Radius + , .RelativeRadius = RHS_SPHERE->relativeRadius + , .Scale = RHS_SPHERE->scale + }; + + const SHCollisionShapeID NEW_SHAPE_ID{ entityID, shapeIDCounter }; + + SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); + *sphere = *RHS_SPHERE; + + shapes.emplace_back(sphere); + + break; + } + case SHCollisionShape::Type::CAPSULE: + { + break; + } + default: break; + } + + ++shapeIDCounter; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h new file mode 100644 index 00000000..37d96caa --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -0,0 +1,170 @@ +/**************************************************************************************** + * \file SHCollider.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Base Collider Class. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "ECS_Base/Entity/SHEntity.h" +#include "Math/Transform/SHTransform.h" +#include "Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + class SHRigidBody; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Base class for a collider. + * There are only two collider types supported by SHADE Engine: Composite & Hull + */ + class SH_API SHCollider + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHPhysicsWorld; + + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using CollisionShapes = std::vector; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Constructor for a collider. + * @param eid + * The entity this collider belongs to. + * @param worldTransform + * The world transform for the collider. Defaults to the identity transform. + * This is particularly important for composite colliders for offsets & relative sizes. + * @return + */ + SHCollider (EntityID eid, const SHTransform& worldTransform = SHTransform::Identity) noexcept; + SHCollider (const SHCollider& rhs) noexcept; + SHCollider (SHCollider&& rhs) noexcept; + ~SHCollider () noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHCollider& operator=(const SHCollider& rhs) noexcept; + SHCollider& operator=(SHCollider&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] const SHTransform& GetTransform () const noexcept; + [[nodiscard]] const SHVec3& GetPosition () const noexcept; + [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; + [[nodiscard]] const SHVec3& GetScale () const noexcept; + + [[nodiscard]] const CollisionShapes& GetCollisionShapes () const noexcept; + [[nodiscard]] SHCollisionShape* GetCollisionShape (int index) const; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetRigidBody (SHRigidBody* rb) noexcept; + + void SetTransform (const SHTransform& newTransform) noexcept; + void SetPosition (const SHVec3& newPosition) noexcept; + void SetOrientation (const SHQuaternion& newOrientation) noexcept; + void SetScale (const SHVec3& newScale) noexcept; + + void SetFactory (SHCollisionShapeFactory* factory) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Computes the TRS for the collider's transform + * @return + * The computed TRS. + */ + const SHMatrix& ComputeTransformTRS() noexcept; + + /** + * @brief + * Adds a sphere collision shape. + * @param posOffset + * The position offset of the sphere from the center of the collider. Defaults to a Zero Vector. + * @param rotOffset + * The rotation offset of the sphere from the rotation of the collider. Defaults to a Zero Vector. + * @param relativeRadius + * The relative radius is constructed with respect to the world scale.
+ * Radius = max(scale.x, scale.y, scale.z) * 0.5 * relativeRadius + * @return + * The index of the newly added shape. + */ + int AddSphereCollisionShape (float relativeRadius, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero); + // TODO: Add Box & Capsule + + /** + * @brief + * Removes a shape from the container. Removal reduces the size of the container. + * If removing all, perform removal from back to front. + * @param index + * The index of the shape to remove. + * @throws + * Invalid argument for out-of-range indices. + */ + void RemoveCollisionShape (int index); + + /** + * @brief + * Recomputes the transforms for all shapes in this composite collider. + */ + void RecomputeShapes () const noexcept; + + protected: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + EntityID entityID; + uint32_t shapeIDCounter; // This increments everytime a shape is added to differentiate shapes. + + SHRigidBody* rigidBody; + SHCollisionShapeFactory* shapeFactory; + + SHTransform transform; + + CollisionShapes shapes; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + void copyShapes (const SHCollider& rhsCollider); + void copyShape (const SHCollisionShape* rhsShape); + }; + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp index 43ad05ca..a28686e0 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp @@ -13,6 +13,11 @@ // Primary Header #include "SHCollisionInfo.h" +// Project Headers +#include "Physics/Collision/SHCollider.h" +#include "Physics/Interface/SHColliderComponent.h" + + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -77,12 +82,14 @@ namespace SHADE const SHCollisionShape* SHCollisionInfo::GetColliderA() const noexcept { - return &SHComponentManager::GetComponent(ids[ENTITY_A])->GetCollisionShape(ids[COLLIDER_A]); + const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_A]); + return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[COLLIDER_A]); } const SHCollisionShape* SHCollisionInfo::GetColliderB() const noexcept { - return &SHComponentManager::GetComponent(ids[ENTITY_B])->GetCollisionShape(ids[COLLIDER_B]); + const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_B]); + return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[COLLIDER_B]); } SHCollisionInfo::State SHCollisionInfo::GetCollisionState() const noexcept diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h index dfcf4f22..d2dad647 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h @@ -12,7 +12,7 @@ // Project Headers #include "Physics/Interface/SHColliderComponent.h" -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" namespace SHADE diff --git a/SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.cpp b/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.cpp similarity index 100% rename from SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.cpp rename to SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.cpp diff --git a/SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.h b/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h similarity index 100% rename from SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.h rename to SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h diff --git a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp index 2cca1c45..91b5a688 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp @@ -74,7 +74,7 @@ namespace SHADE void SHMotionState::ForcePosition(const SHVec3& newPosition) noexcept { - hasMoved = false; + hasMoved = true; prevPosition = newPosition; position = newPosition; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index 7d6c7c5c..0d7434f6 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -40,7 +40,7 @@ namespace SHADE void SHPhysicsWorld::AddRigidBody(SHRigidBody* rigidBody) noexcept { - const bool INSERTED = rigidBodies.emplace(rigidBody->entityID, rigidBody).second; + const bool INSERTED = rigidBodies.emplace(rigidBody).second; if (!INSERTED) { SHLOG_WARNING_D("Attempting to add duplicate rigid body {} to the Physics World!", rigidBody->entityID) @@ -49,15 +49,34 @@ namespace SHADE void SHPhysicsWorld::RemoveRigidBody(SHRigidBody* rigidBody) noexcept { - rigidBodies.erase(rigidBody->entityID); + rigidBodies.erase(rigidBody); + + // Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep + } + + void SHPhysicsWorld::AddCollider(SHCollider* collider) noexcept + { + const bool INSERTED = colliders.emplace(collider).second; + if (!INSERTED) + { + SHLOG_WARNING_D("Attempting to add duplicate collider {} to the Physics World!", collider->entityID) + } + } + + void SHPhysicsWorld::RemoveCollider(SHCollider* collider) noexcept + { + colliders.erase(collider); + + // Get collider's rigid body + // Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep } void SHPhysicsWorld::Step(float dt) { - for (auto* rigidBody : rigidBodies | std::views::values) + for (auto* rigidBody : rigidBodies) integrateForces(*rigidBody, dt); - for (auto* rigidBody : rigidBodies | std::views::values) + for (auto* rigidBody : rigidBodies) integrateVelocities(*rigidBody, dt); } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index eb96a021..499230dd 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -10,12 +10,12 @@ #pragma once -#include +#include // Project Headers +#include "Physics/Collision/SHCollider.h" #include "SHRigidBody.h" -#include "Math/SHMath.h" -#include "SH_API.h" + namespace SHADE { @@ -66,8 +66,11 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - void AddRigidBody (SHRigidBody* rigidBody) noexcept; - void RemoveRigidBody (SHRigidBody* rigidBody) noexcept; + void AddRigidBody (SHRigidBody* rigidBody) noexcept; + void RemoveRigidBody (SHRigidBody* rigidBody) noexcept; + + void AddCollider (SHCollider* collider) noexcept; + void RemoveCollider (SHCollider* collider) noexcept; void Step (float dt); @@ -76,7 +79,11 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using RigidBodies = std::unordered_map; + //! I realise using a set may be dangerous with pointers. An unordered_map may be better, but I don't need the entityIDs so... + + using Colliders = std::unordered_set; + using RigidBodies = std::unordered_set; + /*---------------------------------------------------------------------------------*/ /* Data Members */ @@ -84,6 +91,7 @@ namespace SHADE WorldSettings settings; + Colliders colliders; RigidBodies rigidBodies; /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index 8f99a8f5..e79a8c91 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -14,6 +14,7 @@ #include "SHRigidBody.h" // Project Headers +#include "Physics/Collision/SHCollider.h" #include "Tools/Logger/SHLogger.h" namespace SHADE @@ -24,6 +25,7 @@ namespace SHADE SHRigidBody::SHRigidBody(EntityID eid, Type type) noexcept : entityID { eid } + , collider { nullptr } , bodyType { type } , invMass { type == Type::DYNAMIC ? 1.0f : 0.0f } , linearDrag { 0.01f } @@ -40,6 +42,7 @@ namespace SHADE SHRigidBody::SHRigidBody(const SHRigidBody& rhs) noexcept : entityID { rhs.entityID } + , collider { nullptr } , bodyType { rhs.bodyType } , invMass { rhs.invMass } , linearDrag { rhs.linearDrag } @@ -52,6 +55,7 @@ namespace SHADE SHRigidBody::SHRigidBody(SHRigidBody&& rhs) noexcept : entityID { rhs.entityID } + , collider { nullptr } , bodyType { rhs.bodyType } , invMass { rhs.invMass } , linearDrag { rhs.linearDrag } @@ -73,6 +77,8 @@ namespace SHADE return *this; entityID = rhs.entityID; + // Deep copy the collider + *collider = *rhs.collider; bodyType = rhs.bodyType; invMass = rhs.invMass; linearDrag = rhs.linearDrag; @@ -91,6 +97,8 @@ namespace SHADE SHRigidBody& SHRigidBody::operator=(SHRigidBody&& rhs) noexcept { entityID = rhs.entityID; + // Deep copy the collider + *collider = *rhs.collider; bodyType = rhs.bodyType; invMass = rhs.invMass; linearDrag = rhs.linearDrag; @@ -223,6 +231,11 @@ namespace SHADE /* Setter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ + void SHRigidBody::SetCollider(SHCollider* c) noexcept + { + collider = c; + } + void SHRigidBody::SetType(Type newType) noexcept { if (newType == bodyType) diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index dbf2344f..1edf5cf6 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -11,12 +11,18 @@ #pragma once // Project Headers -#include "ECS_Base/SHECSMacros.h" +#include "ECS_Base/Entity/SHEntity.h" #include "Math/Vector/SHVec3.h" #include "SHMotionState.h" namespace SHADE { + /*-------------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-------------------------------------------------------------------------------------*/ + + class SHCollider; + /*-------------------------------------------------------------------------------------*/ /* Type Definitions */ /*-------------------------------------------------------------------------------------*/ @@ -96,6 +102,8 @@ namespace SHADE /* Setter Functions */ /*-----------------------------------------------------------------------------------*/ + void SetCollider (SHCollider* c) noexcept; + /** * @brief * Changing the type from non-Dynamic to Dynamic will set the default @@ -174,6 +182,7 @@ namespace SHADE // The entityID here is only meant for linking with the actual component in the engine. EntityID entityID; + SHCollider* collider; Type bodyType; @@ -187,7 +196,7 @@ namespace SHADE SHVec3 linearVelocity; SHVec3 angularVelocity; - // aZ aY aX pZ pY pX 0 0 0 0 0 autoMass enableGravity enableSleeping sleeping active + // aZ aY aX pZ pY pX 0 0 0 0 inIsland autoMass enableGravity enableSleeping sleeping active uint16_t flags; SHMotionState motionState; diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp index b6e5ac40..ee33016a 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp @@ -26,15 +26,13 @@ namespace SHADE SHPhysicsObject::SHPhysicsObject(const SHPhysicsObject& rhs) noexcept : entityID { rhs.entityID } { - // Perform a deep copy of the components - *rigidBody = *rhs.rigidBody; + deepCopyComponents(rhs.rigidBody, rhs.collider); } SHPhysicsObject::SHPhysicsObject(SHPhysicsObject&& rhs) noexcept : entityID { rhs.entityID } { - // Perform a deep copy of the components - *rigidBody = *rhs.rigidBody; + deepCopyComponents(rhs.rigidBody, rhs.collider); } SHPhysicsObject::~SHPhysicsObject() noexcept @@ -42,6 +40,10 @@ namespace SHADE entityID = MAX_EID; delete rigidBody; + rigidBody = nullptr; + + delete collider; + collider = nullptr; } /*-----------------------------------------------------------------------------------*/ @@ -53,20 +55,18 @@ namespace SHADE if (this == &rhs) return *this; - entityID = rhs.entityID; + entityID = rhs.entityID; - // Perform a deep copy of the components - *rigidBody = *rhs.rigidBody; + deepCopyComponents(rhs.rigidBody, rhs.collider); return *this; } SHPhysicsObject& SHPhysicsObject::operator=(SHPhysicsObject&& rhs) noexcept { - entityID = rhs.entityID; + entityID = rhs.entityID; - // Perform a deep copy of the components - *rigidBody = *rhs.rigidBody; + deepCopyComponents(rhs.rigidBody, rhs.collider); return *this; } @@ -77,7 +77,74 @@ namespace SHADE bool SHPhysicsObject::IsEmpty() const noexcept { - return rigidBody == nullptr; + return rigidBody == nullptr && collider == nullptr; + } + + SHRigidBody* SHPhysicsObject::CreateRigidBody(SHRigidBody::Type bodyType) + { + if (rigidBody) + { + SHLOG_INFO_D("Rigid body for Entity {} has already been created!", entityID) + return rigidBody; + } + + rigidBody = new SHRigidBody{ entityID, bodyType }; + + // Link with collider if it exists + if (collider) + collider->SetRigidBody(rigidBody); + + return rigidBody; + } + + void SHPhysicsObject::DestroyRigidBody() noexcept + { + delete rigidBody; + rigidBody = nullptr; + + // Unlink with collider + if (collider) + collider->SetRigidBody(nullptr); + } + + SHCollider* SHPhysicsObject::CreateCollider(const SHTransform& transform) + { + if (collider) + { + SHLOG_INFO_D("Collider for Entity {} has already been created!", entityID) + return collider; + } + + collider = new SHCollider{ entityID, transform }; + + // Link with rigidBody if it exists + if (rigidBody) + rigidBody->SetCollider(collider); + + return collider; + } + + void SHPhysicsObject::DestroyCollider() noexcept + { + delete collider; + collider = nullptr; + + // Unlink with rigid body + if (rigidBody) + rigidBody->SetCollider(nullptr); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsObject::deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCollider* rhsCollider) + { + if (rhsRigidBody) + rigidBody = new SHRigidBody{ *rhsRigidBody }; + + if (rhsCollider) + collider = new SHCollider { *rhsCollider }; } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h index f297a6fb..2550543b 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h @@ -11,6 +11,7 @@ #pragma once // Project Headers +#include "Physics/Collision/SHCollider.h" #include "Physics/Dynamics/SHRigidBody.h" namespace SHADE @@ -32,6 +33,7 @@ namespace SHADE EntityID entityID = MAX_EID; SHRigidBody* rigidBody = nullptr; + SHCollider* collider = nullptr; /*-----------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -53,6 +55,55 @@ namespace SHADE /* Member Functions */ /*-----------------------------------------------------------------------------------*/ - bool IsEmpty() const noexcept; + /** + * @brief + * Checks if the physics object has a rigid body or a collider. + * @return + * True if the physics object has neither a rigid body nor a collider. + */ + [[nodiscard]] bool IsEmpty () const noexcept; + + /** + * @brief + * Creates a rigid body for this physics object. + * @param bodyType + * The rigid body's type. Can be modified after creation. + * @return + * Pointer to the rigid body that was created. The memory of this rigid body is managed + * by the physics object itself. + */ + SHRigidBody* CreateRigidBody (SHRigidBody::Type bodyType); + + /** + * @brief + * Destroys the rigid body of this physics object and frees the memory. + */ + void DestroyRigidBody () noexcept; + + /** + * @brief + * Creates a collider for this physics object. + * @param colliderType + * The collider's type. Should not be modified after creation. + * @param transform. + * The world transform of the collider. Defaults to the identity transform. + * @return + * Pointer to the collider that was created. The memory of this collider is managed + * by the physics object itself. + */ + SHCollider* CreateCollider (const SHTransform& transform = SHTransform::Identity); + + /** + * @brief + * Destroys the collider of this physics object and frees the memory. + */ + void DestroyCollider () noexcept; + + private: + /*-----------------------------------------------------------------------------------*/ + /* Member Functions */ + /*-----------------------------------------------------------------------------------*/ + + void deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCollider* rhsCollider); }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp index e6e92ba5..61a77f28 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp @@ -16,8 +16,7 @@ // Project Headers #include "Math/Transform/SHTransformComponent.h" #include "Physics/Interface/SHColliderComponent.h" -#include "Physics/Interface/SHCollisionShape.h" -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" #include "Tools/Utilities/SHUtilities.h" namespace SHADE @@ -58,66 +57,68 @@ namespace SHADE void SHPhysicsObjectManager::AddRigidBody(EntityID entityID) noexcept { - SHPhysicsObject* physicsObject = nullptr; - - const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); - if (PHYSICS_OBJECT_ITERATOR == physicsObjects.end()) - physicsObject = createPhysicsObject(entityID); - else - physicsObject = &PHYSICS_OBJECT_ITERATOR->second; + SHPhysicsObject* physicsObject = ensurePhysicsObject(entityID); // Get the component auto* rigidBodyComponent = SHComponentManager::GetComponent(entityID); // Create a new rigidbody in the physics object - // We assume none has already been made + const auto RIGID_BODY_TYPE = static_cast(rigidBodyComponent->GetType()); + auto* rigidBody = physicsObject->CreateRigidBody(RIGID_BODY_TYPE); - physicsObject->rigidBody = new SHRigidBody + SHVec3 worldPos = SHVec3::Zero; + // TODO: Force orientation + + if (const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); TRANSFORM_COMPONENT) { - entityID - , static_cast(rigidBodyComponent->GetType()) - }; - - SHMotionState& motionState = physicsObject->rigidBody->GetMotionState(); - - if (const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent(entityID); TRANSFORM_COMPONENT) - { - motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition()); - } - else - { - motionState.ForcePosition(SHVec3::Zero); + worldPos = TRANSFORM_COMPONENT->GetWorldPosition(); } + SHMotionState& motionState = rigidBody->GetMotionState(); + motionState.ForcePosition(worldPos); + // Link with the component - rigidBodyComponent->SetRigidBody(physicsObject->rigidBody); - - // TODO: Broadcast event + rigidBodyComponent->SetRigidBody(rigidBody); } void SHPhysicsObjectManager::RemoveRigidBody(EntityID entityID) noexcept { const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); - if (PHYSICS_OBJECT_ITERATOR != physicsObjects.end()) - { - SHPhysicsObject* physicsObject = &PHYSICS_OBJECT_ITERATOR->second; + if (PHYSICS_OBJECT_ITERATOR == physicsObjects.end()) + return; - delete physicsObject->rigidBody; - physicsObject->rigidBody = nullptr; + SHPhysicsObject* physicsObject = &PHYSICS_OBJECT_ITERATOR->second; - // Destroy empty physics objects - if (physicsObject->IsEmpty()) - destroyPhysicsObject(entityID); - } + physicsObject->DestroyRigidBody(); - // TODO: Broadcast event + // Destroy empty physics objects + if (physicsObject->IsEmpty()) + destroyPhysicsObject(entityID); } void SHPhysicsObjectManager::AddCollider(EntityID entityID) noexcept { - // Link with the component + SHPhysicsObject* physicsObject = ensurePhysicsObject(entityID); - // TODO: Broadcast event + // Get the component + auto* colliderComponent = SHComponentManager::GetComponent(entityID); + + SHTransform worldTransform = SHTransform::Identity; + if (const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); TRANSFORM_COMPONENT) + { + worldTransform.position = TRANSFORM_COMPONENT->GetWorldPosition(); + worldTransform.orientation = TRANSFORM_COMPONENT->GetWorldOrientation(); + worldTransform.scale = TRANSFORM_COMPONENT->GetWorldScale(); + + worldTransform.ComputeTRS(); + } + + // Create a new composite collider in the physics object + physicsObject->CreateCollider(worldTransform); + physicsObject->collider->SetFactory(&shapeFactory); + + // Link with the component + colliderComponent->SetCollider(physicsObject->collider); } void SHPhysicsObjectManager::RemoveCollider(EntityID entityID) noexcept @@ -127,30 +128,17 @@ namespace SHADE { SHPhysicsObject* physicsObject = &PHYSICS_OBJECT_ITERATOR->second; + physicsObject->DestroyCollider(); + // Destroy empty physics objects if (physicsObject->IsEmpty()) destroyPhysicsObject(entityID); } - - // TODO: Broadcast event - } - - void SHPhysicsObjectManager::AddCollisionShape(EntityID entityID, uint8_t shapeID) noexcept - { - // Link with the component - - // TODO: Broadcast event - } - - void SHPhysicsObjectManager::RemoveCollisionShape(EntityID entityID, uint8_t shapeID) noexcept - { - // Unlink with the component - - // TODO: Broadcast event } void SHPhysicsObjectManager::RemoveAllObjects() noexcept { + // Physics objects itself will delete the object physicsObjects.clear(); } @@ -164,6 +152,18 @@ namespace SHADE return &NEW_OBJECT->second; } + SHPhysicsObject* SHPhysicsObjectManager::ensurePhysicsObject(EntityID entityID) + { + const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); + + SHPhysicsObject* physicsObject = PHYSICS_OBJECT_ITERATOR == physicsObjects.end() + ? createPhysicsObject(entityID) + : &PHYSICS_OBJECT_ITERATOR->second; + + return physicsObject; + } + + void SHPhysicsObjectManager::destroyPhysicsObject(EntityID entityID) { physicsObjects.erase(entityID); diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h index 6449a2d8..ca260a02 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h @@ -14,6 +14,7 @@ // Project Headers #include "SHPhysicsObject.h" +#include "Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h" namespace SHADE { @@ -26,7 +27,7 @@ namespace SHADE * Encapsulates a manager for physics objects that links raw physics components with the * engine's components. */ - class SHPhysicsObjectManager + class SH_API SHPhysicsObjectManager { private: /*-----------------------------------------------------------------------------------*/ @@ -60,7 +61,7 @@ namespace SHADE * @param entityID * The entity to link the new rigid body to. */ - void AddRigidBody (EntityID entityID) noexcept; + void AddRigidBody (EntityID entityID) noexcept; /** * @brief @@ -68,66 +69,48 @@ namespace SHADE * @param entityID * The entity to destroy the rigid body of. */ - void RemoveRigidBody (EntityID entityID) noexcept; + void RemoveRigidBody (EntityID entityID) noexcept; /** * @brief - * Creates a collider and links it with the collider component. + * Creates a composite collider and links it with the collider component. * @param entityID * The entity to link the new collider to. */ - void AddCollider (EntityID entityID) noexcept; + void AddCollider (EntityID entityID) noexcept; /** * @brief - * Destroys a collider and removes the link with the collider component. + * Destroys a composite collider and removes the link with the collider component. * @param entityID * The entity to destroy the collider of. */ - void RemoveCollider (EntityID entityID) noexcept; - - /** - * @brief - * Creates a collision shape for composite colliders and links it with the - * collider component. - * @param eientityIDd - * The entity to create a collision shape for. - * @param shapeID - * The id of the shape being created. - */ - void AddCollisionShape (EntityID entityID, uint8_t shapeID) noexcept; - - /** - * @brief - * Destroys a collision shape for composite colliders and removes the link with the - * collider component. - * @param entityID - * The entity to destroy the collision shape of. - * @param shapeID - * The id of the shape being destroyed. - */ - void RemoveCollisionShape (EntityID entityID, uint8_t shapeID) noexcept; + void RemoveCollider (EntityID entityID) noexcept; /** * @brief * Removes all physics object in the manager. This is only meant to be called when * the world is being destroyed or the scene is being changed. */ - void RemoveAllObjects () noexcept; + void RemoveAllObjects () noexcept; private: /*-----------------------------------------------------------------------------------*/ /* Data Members */ /*-----------------------------------------------------------------------------------*/ - EntityObjectMap physicsObjects; + EntityObjectMap physicsObjects; + SHCollisionShapeFactory shapeFactory; /*-----------------------------------------------------------------------------------*/ /* Member Functions */ /*-----------------------------------------------------------------------------------*/ SHPhysicsObject* createPhysicsObject (EntityID entityID); + SHPhysicsObject* ensurePhysicsObject (EntityID entityID); void destroyPhysicsObject (EntityID entityID); + + }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp index eef92493..d345290c 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp @@ -25,57 +25,43 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHColliderComponent::SHColliderComponent() noexcept + : collider { nullptr } {} /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - const SHColliderComponent::CollisionShapes& SHColliderComponent::GetCollisionShapes() const noexcept + SHCollider* const SHColliderComponent::GetCollider() const noexcept { - return collisionShapes; + return collider; } - SHCollisionShape& SHColliderComponent::GetCollisionShape(int index) + const SHCollider::CollisionShapes* const SHColliderComponent::GetCollisionShapes() const noexcept { - if (index < 0 || static_cast(index) >= collisionShapes.size()) - throw std::invalid_argument("Out-of-range access!"); + if (!collider) + return nullptr; - return collisionShapes[index]; + return &collider->GetCollisionShapes(); } + + SHCollisionShape* const SHColliderComponent::GetCollisionShape(int index) const + { + if (!collider) + return nullptr; + + return collider->GetCollisionShape(index); + } + + /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ + /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHColliderComponent::OnCreate() + void SHColliderComponent::SetCollider(SHCollider* c) noexcept { - - } - - void SHColliderComponent::OnDestroy() - { - - } - - void SHColliderComponent::RecomputeCollisionShapes() noexcept - { - - } - - int SHColliderComponent::AddBoundingBox(const SHVec3& halfExtents, const SHVec3& posOffset, const SHVec3& rotOffset) noexcept - { - return 0; - } - - int SHColliderComponent::AddBoundingSphere(float radius, const SHVec3& posOffset) noexcept - { - return 0; - } - - void SHColliderComponent::RemoveCollider(int index) - { - + collider = c; } } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h index 15a02173..83fb235c 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h @@ -14,14 +14,7 @@ // Project Headers #include "ECS_Base/Components/SHComponent.h" -#include "Math/Geometry/SHBox.h" -#include "Math/Geometry/SHSphere.h" -#include "SHCollisionShape.h" - -//namespace SHADE -//{ -// class SHPhysicsSystem; -//} +#include "Physics/Collision/SHCollider.h" namespace SHADE { @@ -39,12 +32,6 @@ namespace SHADE friend class SHPhysicsSystem; friend class SHPhysicsObject; - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - using CollisionShapes = std::vector; - public: /*---------------------------------------------------------------------------------*/ @@ -67,33 +54,22 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] bool HasChanged () const noexcept; - - [[nodiscard]] const CollisionShapes& GetCollisionShapes() const noexcept; - [[nodiscard]] SHCollisionShape& GetCollisionShape (int index); + [[nodiscard]] SHCollider* const GetCollider () const noexcept; + [[nodiscard]] const SHCollider::CollisionShapes* const GetCollisionShapes() const noexcept; + [[nodiscard]] SHCollisionShape* const GetCollisionShape (int index) const; /*---------------------------------------------------------------------------------*/ - /* Function Members */ + /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void OnCreate () override; - void OnDestroy () override; - - void RecomputeCollisionShapes () noexcept; - - void RemoveCollider (int index); - - int AddBoundingBox (const SHVec3& halfExtents = SHVec3::One, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero) noexcept; - int AddBoundingSphere (float radius = 1.0f, const SHVec3& posOffset = SHVec3::Zero) noexcept; + void SetCollider(SHCollider* c) noexcept; private: - /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHVec3 scale; - CollisionShapes collisionShapes; + SHCollider* collider; RTTR_ENABLE() }; diff --git a/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp deleted file mode 100644 index 4dd506f9..00000000 --- a/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/**************************************************************************************** - * \file SHCollider.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Collider. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollisionShape.h" -// Project Headers -#include "Math/Geometry/SHBox.h" -#include "Math/Geometry/SHSphere.h" -#include "Math/SHMathHelpers.h" -#include "Physics/Collision/SHCollisionTagMatrix.h" -#include "Reflection/SHReflectionMetadata.h" -#include "SHColliderComponent.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollisionShape::SHCollisionShape(EntityID eid, Type colliderType, const SHPhysicsMaterial& physicsMaterial) - : type { colliderType } - , entityID { eid } - , isTrigger { false } - , dirty { true } - , shape { nullptr } - , material { physicsMaterial } - , collisionTag { SHCollisionTagMatrix::GetTag(0) } - { - switch (type) - { - case Type::BOX: - { - shape = new SHBox{ SHVec3::Zero, SHVec3::One }; - break; - } - case Type::SPHERE: - { - shape = new SHSphere{ SHVec3::Zero, 0.5f }; - break; - } - default: break; - } - } - - SHCollisionShape::SHCollisionShape(const SHCollisionShape& rhs) noexcept - : type { rhs.type} - , entityID { rhs.entityID } - , isTrigger { rhs.isTrigger } - , dirty { true } - , shape { nullptr } - , material { rhs.material } - , positionOffset { rhs.positionOffset } - , rotationOffset { rhs.rotationOffset } - , collisionTag { rhs.collisionTag } - { - CopyShape(rhs.shape); - } - - SHCollisionShape::SHCollisionShape(SHCollisionShape&& rhs) noexcept - : type { rhs.type} - , entityID { rhs.entityID } - , isTrigger { rhs.isTrigger } - , dirty { true } - , shape { nullptr } - , material { rhs.material } - , positionOffset { rhs.positionOffset } - , rotationOffset { rhs.rotationOffset } - , collisionTag { rhs.collisionTag } - { - CopyShape(rhs.shape); - } - - SHCollisionShape::~SHCollisionShape() noexcept - { - shape = nullptr; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollisionShape& SHCollisionShape::operator=(const SHCollisionShape& rhs) noexcept - { - if (this == &rhs) - return *this; - - type = rhs.type; - entityID = rhs.entityID; - isTrigger = rhs.isTrigger; - dirty = true; - material = rhs.material; - positionOffset = rhs.positionOffset; - rotationOffset = rhs.rotationOffset; - collisionTag = rhs.collisionTag; - - delete shape; - CopyShape(rhs.shape); - - return *this; - } - - SHCollisionShape& SHCollisionShape::operator=(SHCollisionShape&& rhs) noexcept - { - type = rhs.type; - entityID = rhs.entityID; - isTrigger = rhs.isTrigger; - dirty = true; - material = rhs.material; - positionOffset = rhs.positionOffset; - rotationOffset = rhs.rotationOffset; - collisionTag = rhs.collisionTag; - - delete shape; - CopyShape(rhs.shape); - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHCollisionShape::HasChanged() const noexcept - { - return dirty; - } - - bool SHCollisionShape::IsTrigger() const noexcept - { - return isTrigger; - } - - SHCollisionShape::Type SHCollisionShape::GetType() const noexcept - { - return type; - } - - const SHCollisionTag& SHCollisionShape::GetCollisionTag() const noexcept - { - return *collisionTag; - } - - float SHCollisionShape::GetFriction() const noexcept - { - return material.GetFriction(); - } - - float SHCollisionShape::GetBounciness() const noexcept - { - return material.GetBounciness(); - } - - float SHCollisionShape::GetDensity() const noexcept - { - return material.GetDensity(); - } - - const SHPhysicsMaterial& SHCollisionShape::GetMaterial() const noexcept - { - return material; - } - - const SHVec3& SHCollisionShape::GetPositionOffset() const noexcept - { - return positionOffset; - } - - const SHVec3& SHCollisionShape::GetRotationOffset() const noexcept - { - return rotationOffset; - } - - const SHShape* SHCollisionShape::GetShape() const noexcept - { - return shape; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionShape::SetBoundingBox(const SHVec3& halfExtents) - { - //dirty = true; - - //const auto* COLLIDER = SHComponentManager::GetComponent(entityID); - //auto* box = reinterpret_cast(shape); - - //SHVec3 correctedHalfExtents = halfExtents; - - //// Get current relative halfExtents for error checking. 0 is ignored - //const SHVec3& CURRENT_RELATIVE_EXTENTS = box->GetRelativeExtents(); - //for (size_t i = 0; i < SHVec3::SIZE; ++i) - //{ - // if (SHMath::CompareFloat(halfExtents[i], 0.0f)) - // correctedHalfExtents[i] = CURRENT_RELATIVE_EXTENTS[i]; - //} - // - //// Set the half extents relative to world scale - //const SHVec3 WORLD_EXTENTS = correctedHalfExtents * COLLIDER->GetScale() * 0.5f; - - //if (type != Type::BOX) - //{ - // type = Type::BOX; - - // delete shape; - // shape = new SHBox{ positionOffset, WORLD_EXTENTS }; - //} - - //box->SetWorldExtents(WORLD_EXTENTS); - //box->SetRelativeExtents(correctedHalfExtents); - } - - void SHCollisionShape::SetBoundingSphere(float radius) - { - //dirty = true; - - //auto* sphere = reinterpret_cast(shape); - //const auto* COLLIDER = SHComponentManager::GetComponent(entityID); - - //// Get current relative halfExtents for error checking. 0 is ignored - //const float CURRENT_RELATIVE_RADIUS = sphere->GetRelativeRadius(); - //if (SHMath::CompareFloat(radius, 0.0f)) - // radius = CURRENT_RELATIVE_RADIUS; - - //// Set the radius relative to world scale - //const SHVec3 WORLD_SCALE = COLLIDER->GetScale(); - //const float MAX_SCALE = SHMath::Max({ WORLD_SCALE.x, WORLD_SCALE.y, WORLD_SCALE.z }); - //const float WORLD_RADIUS = radius * MAX_SCALE * 0.5f; - - //if (type != Type::SPHERE) - //{ - // type = Type::SPHERE; - - // delete shape; - // shape = new SHSphere{ positionOffset, WORLD_RADIUS }; - //} - - //sphere->SetWorldRadius(WORLD_RADIUS); - //sphere->SetRelativeRadius(radius); - } - - void SHCollisionShape::SetIsTrigger(bool trigger) noexcept - { - dirty = true; - isTrigger = trigger; - } - - void SHCollisionShape::SetCollisionTag(SHCollisionTag* newCollisionTag) noexcept - { - dirty = true; - collisionTag = newCollisionTag; - } - - void SHCollisionShape::SetFriction(float friction) noexcept - { - dirty = true; - material.SetFriction(friction); - } - - void SHCollisionShape::SetBounciness(float bounciness) noexcept - { - dirty = true; - material.SetBounciness(bounciness); - } - - void SHCollisionShape::SetDensity(float density) noexcept - { - dirty = true; - material.SetDensity(density); - } - - void SHCollisionShape::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept - { - dirty = true; - material = newMaterial; - } - - void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept - { - dirty = true; - positionOffset = posOffset; - - switch (type) - { - case Type::BOX: - { - reinterpret_cast(shape)->SetCenter(positionOffset); - break; - } - case Type::SPHERE: - { - reinterpret_cast(shape)->SetCenter(positionOffset); - break; - } - default: break; - } - } - - void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept - { - dirty = true; - rotationOffset = rotOffset; - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionShape::CopyShape(const SHShape* rhs) - { - switch (type) - { - case Type::BOX: - { - const auto* RHS_BOX = reinterpret_cast(rhs); - - shape = new SHBox{ positionOffset, RHS_BOX->GetWorldExtents() }; - auto* lhsBox = reinterpret_cast(shape); - lhsBox->SetRelativeExtents(RHS_BOX->GetRelativeExtents()); - - break; - } - case Type::SPHERE: - { - const auto* RHS_SPHERE = reinterpret_cast(rhs); - - shape = new SHSphere{ positionOffset, RHS_SPHERE->GetWorldRadius() }; - auto* lhsSphere = reinterpret_cast(shape); - lhsSphere->SetRelativeRadius(RHS_SPHERE->GetRelativeRadius()); - - break; - } - default: break; - } - } - -} // namespace SHADE - -RTTR_REGISTRATION -{ - using namespace SHADE; - using namespace rttr; - - registration::enumeration("Collider Type") - ( - value("Box", SHCollisionShape::Type::BOX), - value("Sphere", SHCollisionShape::Type::SPHERE) - // TODO(Diren): Add More Shapes - ); - - registration::class_("Collider") - .property("IsTrigger" , &SHCollisionShape::IsTrigger , &SHCollisionShape::SetIsTrigger ) - .property("Friction" , &SHCollisionShape::GetFriction , &SHCollisionShape::SetFriction ) - .property("Bounciness" , &SHCollisionShape::GetBounciness , &SHCollisionShape::SetBounciness ) - .property("Density" , &SHCollisionShape::GetDensity , &SHCollisionShape::SetDensity ) - .property("Position Offset" , &SHCollisionShape::GetPositionOffset, &SHCollisionShape::SetPositionOffset) - .property("Rotation Offset" , &SHCollisionShape::GetRotationOffset, &SHCollisionShape::SetRotationOffset) (metadata(META::angleInRad, true)); -} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp similarity index 98% rename from SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp rename to SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp index ddc51fbc..da848f09 100644 --- a/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp @@ -10,17 +10,9 @@ #include -// External Dependencies -#include - // Primary Header #include "SHRigidBodyComponent.h" -// Project Headers -#include "ECS_Base/Managers/SHSystemManager.h" -#include "Physics/Dynamics/SHRigidBody.h" -#include "Physics/System/SHPhysicsSystem.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h similarity index 96% rename from SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h rename to SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h index bfb1717b..7d377184 100644 --- a/SHADE_Engine/src/Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h @@ -15,15 +15,10 @@ // Project Headers #include "ECS_Base/Components/SHComponent.h" #include "Math/Vector/SHVec3.h" +#include "Physics/Dynamics/SHRigidBody.h" namespace SHADE { - /*-------------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-------------------------------------------------------------------------------------*/ - - class SHRigidBody; - /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/SHPhysicsEvents.h b/SHADE_Engine/src/Physics/SHPhysicsEvents.h index ae48a75b..35217347 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsEvents.h +++ b/SHADE_Engine/src/Physics/SHPhysicsEvents.h @@ -11,7 +11,7 @@ #pragma once // Project Headers -#include "Interface/SHCollisionShape.h" +#include "Collision/CollisionShapes/SHCollisionShape.h" namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp index 5f18c450..cd4b1cff 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp @@ -57,19 +57,20 @@ namespace SHADE if (!rigidBodyComponent.rigidBody) continue; - if (const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); MOTION_STATE) - { - SHVec3 renderPosition = rigidBodyComponent.IsInterpolating() - ? MOTION_STATE.InterpolatePositions(FACTOR) - : MOTION_STATE.position; + const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); - /* - * TODO: Test if the scene graph transforms abides by setting world position. Collisions will ignore the scene graph hierarchy. - */ + if (!MOTION_STATE) + continue; - transformComponent->SetWorldPosition(renderPosition); - // TODO: SetOrientation - } + SHVec3 renderPosition = rigidBodyComponent.IsInterpolating() ? MOTION_STATE.InterpolatePositions(FACTOR) : MOTION_STATE.position; + + /* + * TODO: Test if the scene graph transforms abides by setting world position. Collisions will ignore the scene graph hierarchy. + */ + + + transformComponent->SetWorldPosition(renderPosition); + // TODO: SetOrientation } // Collision & Trigger messages diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index eda69161..c03571db 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -40,23 +40,32 @@ namespace SHADE { const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); + if (!TRANSFORM_COMPONENT || !TRANSFORM_COMPONENT->HasChanged()) + return; + + const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); + const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation(); + const SHVec3& WORLD_SCL = TRANSFORM_COMPONENT->GetWorldScale(); + // We assume that all engine components and physics object components have been successfully linked - if (TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged()) + if (physicsObject.rigidBody) { - // Sync the objects transforms - const SHVec3 WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); + SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); - if (physicsObject.rigidBody) - { - SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); - - motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition()); - // TODO: Force Orientation - } + motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition()); + // TODO: Force Orientation } - // TODO: Sync Collider Transform with World Transform + if (physicsObject.collider) + { + physicsObject.collider->SetPosition(WORLD_POS); + physicsObject.collider->SetOrientation(WORLD_ROT); + physicsObject.collider->SetScale(WORLD_SCL); + + physicsObject.collider->RecomputeShapes(); + } } } -} + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index b4ff6961..d7bece3b 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -162,6 +162,9 @@ namespace SHADE { if (PHYSICS_OBJECT.rigidBody) physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); + + if (PHYSICS_OBJECT.collider) + physicsWorld->RemoveCollider(PHYSICS_OBJECT.collider); } delete physicsWorld; @@ -177,13 +180,16 @@ namespace SHADE // This is for handling scene changes while the editor is active. const auto* EDITOR = SHSystemManager::GetSystem(); - if (EDITOR && EDITOR->editorState == SHEditor::State::PLAY) + if (!EDITOR || EDITOR->editorState != SHEditor::State::PLAY) + return onSceneInitEvent.get()->handle; + + for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) { - for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) - { - if (PHYSICS_OBJECT.rigidBody) - physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); - } + if (PHYSICS_OBJECT.rigidBody) + physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); + + if (PHYSICS_OBJECT.collider) + physicsWorld->AddCollider(PHYSICS_OBJECT.collider); } #endif @@ -202,6 +208,9 @@ namespace SHADE { if (PHYSICS_OBJECT.rigidBody) physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); + + if (PHYSICS_OBJECT.collider) + physicsWorld->RemoveCollider(PHYSICS_OBJECT.collider); } delete physicsWorld; @@ -223,25 +232,34 @@ namespace SHADE const bool IS_COLLIDER = ADDED_ID == COLLIDER_COMPONENT_ID; // Check if its a physics component - if (IS_RIGID_BODY || IS_COLLIDER) + const bool IS_PHYSICS_COMPONENT = IS_RIGID_BODY || IS_COLLIDER; + if (!IS_PHYSICS_COMPONENT) + return onComponentAddedEvent.get()->handle; + + const EntityID EID = EVENT_DATA->eid; + + // Link engine components with physics object component + if (IS_RIGID_BODY) { - const EntityID EID = EVENT_DATA->eid; + physicsObjectManager.AddRigidBody(EID); - // Link engine components with physics object component - if (IS_RIGID_BODY) + if (physicsWorld) { - physicsObjectManager.AddRigidBody(EID); - - if (physicsWorld) - { - auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody; - physicsWorld->AddRigidBody(rigidBody); - } + auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody; + physicsWorld->AddRigidBody(rigidBody); } + } - if (IS_COLLIDER) - physicsObjectManager.AddCollider(EID); + if (IS_COLLIDER) + { + physicsObjectManager.AddCollider(EID); + + if (physicsWorld) + { + auto* collider = physicsObjectManager.GetPhysicsObject(EID)->collider; + physicsWorld->AddCollider(collider); + } } return onComponentAddedEvent.get()->handle; @@ -260,27 +278,35 @@ namespace SHADE const bool IS_COLLIDER = REMOVED_DATA == COLLIDER_COMPONENT_ID; // Check if its a physics component - if (IS_RIGID_BODY || IS_COLLIDER) + const bool IS_PHYSICS_COMPONENT = IS_RIGID_BODY || IS_COLLIDER; + if (!IS_PHYSICS_COMPONENT) + return onComponentRemovedEvent.get()->handle; + + const EntityID EID = EVENT_DATA->eid; + + if (IS_RIGID_BODY) { - const EntityID EID = EVENT_DATA->eid; - - // Link engine components with physics object component - if (IS_RIGID_BODY) + if (physicsWorld) { - if (physicsWorld) - { - auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody; - physicsWorld->RemoveRigidBody(rigidBody); - } - - physicsObjectManager.RemoveRigidBody(EID); + auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody; + physicsWorld->RemoveRigidBody(rigidBody); } - - if (IS_COLLIDER) - physicsObjectManager.RemoveCollider(EID); + physicsObjectManager.RemoveRigidBody(EID); } + if (IS_COLLIDER) + { + if (physicsWorld) + { + auto* collider = physicsObjectManager.GetPhysicsObject(EID)->collider; + physicsWorld->RemoveCollider(collider); + } + + physicsObjectManager.RemoveCollider(EID); + } + + return onComponentRemovedEvent.get()->handle; } @@ -295,7 +321,8 @@ namespace SHADE if (PHYSICS_OBJECT.rigidBody) physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); - // TODO: Add Collider if it exists + if (PHYSICS_OBJECT.collider) + physicsWorld->AddCollider(PHYSICS_OBJECT.collider); } return onEditorPlayEvent.get()->handle; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index 7d7dece5..2a351e86 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -18,7 +18,7 @@ #include "Physics/Dynamics/SHPhysicsWorld.h" #include "Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h" -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index 9d648185..99e4fa41 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -15,7 +15,7 @@ #include "Camera/SHCameraArmComponent.h" #include "Math/Transform/SHTransformComponent.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h" -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" #include "UI/SHCanvasComponent.h" #include "UI/SHButtonComponent.h" #include "ECS_Base/Managers/SHSystemManager.h" diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index e1cb8181..cbbe8505 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -1,9 +1,7 @@ #pragma once #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" -#include "Math/Geometry/SHBox.h" -#include "Math/Geometry/SHSphere.h" -#include "Physics/Interface/SHCollisionShape.h" +#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h" #include "Resource/SHResourceManager.h" #include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec3.h" @@ -134,14 +132,14 @@ namespace YAML { case SHCollisionShape::Type::BOX: { - const auto* BOX = reinterpret_cast(rhs.GetShape()); - node[HalfExtents] = BOX->GetRelativeExtents(); + //const auto* BOX = reinterpret_cast(rhs.GetShape()); + //node[HalfExtents] = BOX->GetRelativeExtents(); } break; case SHCollisionShape::Type::SPHERE: { - const auto* SPHERE = reinterpret_cast(rhs.GetShape()); - node[Radius] = SPHERE->GetRelativeRadius(); + const auto& SPHERE = dynamic_cast(rhs); + node[Radius] = SPHERE.GetRelativeRadius(); } break; case SHCollisionShape::Type::CAPSULE: break; @@ -172,14 +170,17 @@ namespace YAML { case SHCollisionShape::Type::BOX: { - if (node[HalfExtents].IsDefined()) - rhs.SetBoundingBox(node[HalfExtents].as()); + //if (node[HalfExtents].IsDefined()) + // rhs.SetBoundingBox(node[HalfExtents].as()); } break; case SHCollisionShape::Type::SPHERE: { if (node[Radius].IsDefined()) - rhs.SetBoundingSphere(node[Radius].as()); + { + auto* sphere = dynamic_cast(&rhs); + sphere->SetRelativeRadius(node[Radius].as()); + } } break; case SHCollisionShape::Type::CAPSULE: break; @@ -207,11 +208,10 @@ namespace YAML static Node encode(SHColliderComponent& rhs) { Node node, collidersNode; - auto const& colliders = rhs.GetCollisionShapes(); - int const numColliders = static_cast(colliders.size()); + int const numColliders = static_cast(rhs.GetCollisionShapes()->size()); for (int i = 0; i < numColliders; ++i) { - auto& collider = rhs.GetCollisionShape(i); + auto& collider = *rhs.GetCollisionShape(i); Node colliderNode = convert::encode(collider); if (colliderNode.IsDefined()) collidersNode[i] = colliderNode; @@ -233,14 +233,16 @@ namespace YAML if (!ok) return false; + auto* collider = rhs.GetCollider(); + switch (colliderType) { - case SHCollisionShape::Type::BOX: rhs.AddBoundingBox(); break; - case SHCollisionShape::Type::SPHERE: rhs.AddBoundingSphere(); break; + case SHCollisionShape::Type::BOX: break; + case SHCollisionShape::Type::SPHERE: collider->AddSphereCollisionShape(1.0f); break; case SHCollisionShape::Type::CAPSULE: break; default:; } - YAML::convert::decode(colliderNode, rhs.GetCollisionShape(numColliders++)); + YAML::convert::decode(colliderNode, *collider->GetCollisionShape(numColliders++)); } } return true; diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index 414e79b7..1c84637a 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -107,7 +107,7 @@ namespace SHADE try { - auto& shape = collider->GetCollisionShape(arrayIndex); + auto& shape = *collider->GetCollisionShape(arrayIndex); return shape; } catch (std::invalid_argument&) @@ -128,35 +128,39 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 BoxCollider::Center::get() { - return Convert::ToCLI(getNativeCollisionShape().GetCenter()); + //return Convert::ToCLI(getNativeCollisionShape().GetCenter()); + return Vector3::Zero; } void BoxCollider::Center::set(Vector3 value) { - getNativeCollisionShape().SetCenter(Convert::ToNative(value)); + //getNativeCollisionShape().SetCenter(Convert::ToNative(value)); } Vector3 BoxCollider::HalfExtents::get() { - return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); + //return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); + return Vector3::Zero; } void BoxCollider::HalfExtents::set(Vector3 value) { - getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); + //getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); } Vector3 BoxCollider::Min::get() { - return Convert::ToCLI(getNativeCollisionShape().GetMin()); + //return Convert::ToCLI(getNativeCollisionShape().GetMin()); + return Vector3::Zero; } void BoxCollider::Min::set(Vector3 value) { - getNativeCollisionShape().SetMin(Convert::ToNative(value)); + //getNativeCollisionShape().SetMin(Convert::ToNative(value)); } Vector3 BoxCollider::Max::get() { - return Convert::ToCLI(getNativeCollisionShape().GetMax()); + //return Convert::ToCLI(getNativeCollisionShape().GetMax()); + return Vector3::Zero; } void BoxCollider::Max::set(Vector3 value) { - getNativeCollisionShape().SetMax(Convert::ToNative(value)); + //getNativeCollisionShape().SetMax(Convert::ToNative(value)); } /*---------------------------------------------------------------------------------*/ @@ -164,11 +168,13 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ bool BoxCollider::TestPoint(Vector3 point) { - return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); + //return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); + return false; } bool BoxCollider::Raycast(Ray ray, float maxDistance) { - return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); + //return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); + return false; } /*---------------------------------------------------------------------------------*/ @@ -176,19 +182,19 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 SphereCollider::Center::get() { - return Convert::ToCLI(getNativeCollisionShape().GetCenter()); + return Convert::ToCLI(getNativeCollisionShape().GetCenter()); } void SphereCollider::Center::set(Vector3 value) { - getNativeCollisionShape().SetCenter(Convert::ToNative(value)); + getNativeCollisionShape().SetCenter(Convert::ToNative(value)); } float SphereCollider::Radius::get() { - return getNativeCollisionShape().GetWorldRadius(); + return getNativeCollisionShape().GetWorldRadius(); } void SphereCollider::Radius::set(float value) { - getNativeCollisionShape().SetWorldRadius(value); + getNativeCollisionShape().SetWorldRadius(value); } /*---------------------------------------------------------------------------------*/ @@ -196,11 +202,11 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ bool SphereCollider::TestPoint(Vector3 point) { - return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); + return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); } bool SphereCollider::Raycast(Ray ray, float maxDistance) { - return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); + return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); } /*---------------------------------------------------------------------------------*/ @@ -231,7 +237,10 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ int Collider::CollisionShapeCount::get() { - return static_cast(GetNativeComponent()->GetCollisionShapes().size()); + if (const auto* shapes = GetNativeComponent()->GetCollisionShapes()) + return static_cast(shapes->size()); + + return -1; } /*---------------------------------------------------------------------------------*/ @@ -311,10 +320,10 @@ namespace SHADE // Populate the list int i = 0; - for (const auto& collider : GetNativeComponent()->GetCollisionShapes()) + for (const auto* collider : *GetNativeComponent()->GetCollisionShapes()) { CollisionShape^ bound = nullptr; - switch (collider.GetType()) + switch (collider->GetType()) { case SHCollisionShape::Type::BOX: bound = gcnew BoxCollider(i, Owner.GetEntity()); diff --git a/SHADE_Managed/src/Components/Collider.h++ b/SHADE_Managed/src/Components/Collider.h++ index 8ea648aa..c2e732f4 100644 --- a/SHADE_Managed/src/Components/Collider.h++ +++ b/SHADE_Managed/src/Components/Collider.h++ @@ -27,11 +27,11 @@ namespace SHADE try { - auto& shape = collider->GetCollisionShape(arrayIndex); - if (shape.GetType() != SHCollisionShape::Type::BOX) + auto* shape = collider->GetCollisionShape(arrayIndex); + if (!shape || shape->GetType() == SHCollisionShape::Type::INVALID) throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); - return reinterpret_cast(shape); + return dynamic_cast(*shape); } catch (std::invalid_argument&) { diff --git a/SHADE_Managed/src/Components/RigidBody.hxx b/SHADE_Managed/src/Components/RigidBody.hxx index 6ca39316..8bfe34aa 100644 --- a/SHADE_Managed/src/Components/RigidBody.hxx +++ b/SHADE_Managed/src/Components/RigidBody.hxx @@ -15,7 +15,7 @@ of DigiPen Institute of Technology is prohibited. #pragma once // External Dependencies -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" // Project Includes #include "Components/Component.hxx" diff --git a/SHADE_Managed/src/Engine/ECS.cxx b/SHADE_Managed/src/Engine/ECS.cxx index 3ace79ab..c388f0cd 100644 --- a/SHADE_Managed/src/Engine/ECS.cxx +++ b/SHADE_Managed/src/Engine/ECS.cxx @@ -23,7 +23,7 @@ of DigiPen Institute of Technology is prohibited. #include "ECS_Base/Managers/SHEntityManager.h" #include "Math/Transform/SHTransformComponent.h" #include "Physics/Interface/SHColliderComponent.h" -#include "Physics/Interface/RigidBodyComponent/SHRigidBodyComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" #include "Scene/SHSceneManager.h" #include "Scene/SHSceneGraph.h" #include "Tools/Logger/SHLog.h" From c1d77029142e5631a0293b7e9bd2f1018de89525 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 11 Dec 2022 20:12:26 +0800 Subject: [PATCH 013/164] Moved debug draw state to colliders. Synced collider positions with rigid bodies --- Assets/Scenes/PhysicsSandbox.shade | 11 +++ .../src/Application/SBApplication.cpp | 3 + .../Inspector/SHEditorComponentView.hpp | 37 +++++----- .../CollisionShapes/SHCollisionShape.cpp | 18 +---- .../CollisionShapes/SHCollisionShape.h | 11 +-- .../SHCollisionTagMatrix.cpp | 0 .../SHCollisionTagMatrix.h | 0 .../{ => CollisionTags}/SHCollisionTags.cpp | 0 .../{ => CollisionTags}/SHCollisionTags.h | 0 .../src/Physics/Collision/SHCollider.cpp | 74 ++++++++++++++++++- .../src/Physics/Collision/SHCollider.h | 33 ++++++--- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 19 +++-- .../Routines/SHPhysicsDebugDrawRoutine.cpp | 60 +++++++++++++++ .../Routines/SHPhysicsPostUpdateRoutine.cpp | 4 +- .../src/Physics/System/SHPhysicsSystem.cpp | 10 +-- .../src/Physics/System/SHPhysicsSystem.h | 21 +++++- 16 files changed, 230 insertions(+), 71 deletions(-) rename SHADE_Engine/src/Physics/Collision/{ => CollisionTags}/SHCollisionTagMatrix.cpp (100%) rename SHADE_Engine/src/Physics/Collision/{ => CollisionTags}/SHCollisionTagMatrix.h (100%) rename SHADE_Engine/src/Physics/Collision/{ => CollisionTags}/SHCollisionTags.cpp (100%) rename SHADE_Engine/src/Physics/Collision/{ => CollisionTags}/SHCollisionTags.h (100%) create mode 100644 SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index d6b77c17..f600c4ae 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -25,6 +25,17 @@ Freeze Rotation Y: false Freeze Rotation Z: false IsActive: true + Collider Component: + Colliders: + - Is Trigger: false + Type: Sphere + Radius: 1 + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true Scripts: ~ - EID: 1 Name: Default diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index a58839c5..3cb504c0 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -111,6 +111,9 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); +#ifdef SHEDITOR + SHSystemManager::RegisterRoutine(); +#endif SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 774697f1..dfc89df1 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -322,18 +322,21 @@ namespace SHADE { DrawContextMenu(component); - auto* colliders = component->GetCollisionShapes(); - int const size = colliders ? static_cast(colliders->size()) : 0; - ImGui::BeginChild("Collision Shapes", { 0.0f, colliders->empty() ? 1.0f : 250.0f }, true); + auto* collider = component->GetCollider(); + SHEditorWidgets::CheckBox("Draw Colliders", [collider] { return collider->GetDebugDrawState(); }, [collider](bool value) { collider->SetDebugDrawState(value); }); + + auto* collisionShapes = component->GetCollisionShapes(); + int const size = collisionShapes ? static_cast(collisionShapes->size()) : 0; + ImGui::BeginChild("Collision Shapes", { 0.0f, collisionShapes->empty() ? 1.0f : 250.0f }, true); std::optional colliderToDelete{ std::nullopt }; for (int i{}; i < size; ++i) { ImGui::PushID(i); - SHCollisionShape* collider = component->GetCollisionShape(i); + SHCollisionShape* shape = component->GetCollisionShape(i); auto cursorPos = ImGui::GetCursorPos(); //collider->IsTrigger - if (collider->GetType() == SHCollisionShape::Type::BOX) + if (shape->GetType() == SHCollisionShape::Type::BOX) { //SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); // @@ -344,42 +347,42 @@ namespace SHADE // [BOX] { return BOX->GetRelativeExtents(); }, // [collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); }); } - else if (collider->GetType() == SHCollisionShape::Type::SPHERE) + else if (shape->GetType() == SHCollisionShape::Type::SPHERE) { SHEditorWidgets::BeginPanel(std::format("{} Sphere #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - auto* SPHERE = reinterpret_cast(collider); + auto* SPHERE = reinterpret_cast(shape); SHEditorWidgets::DragFloat ( "Radius", [SPHERE] { return SPHERE->GetRelativeRadius(); }, [SPHERE](float const& value) { SPHERE->SetRelativeRadius(value); }); } - else if (collider->GetType() == SHCollisionShape::Type::CAPSULE) + else if (shape->GetType() == SHCollisionShape::Type::CAPSULE) { } { - SHEditorWidgets::CheckBox("Is Trigger", [collider] { return collider->IsTrigger(); }, [collider](bool value) { collider->SetIsTrigger(value); }); + SHEditorWidgets::CheckBox("Is Trigger", [shape] { return shape->IsTrigger(); }, [shape](bool value) { shape->SetIsTrigger(value); }); if(ImGui::CollapsingHeader("Physics Material")) { - SHEditorWidgets::DragFloat("Friction", [collider] { return collider->GetFriction(); }, [collider](float value) { collider->SetFriction(value); }, "Friction", 0.05f, 0.0f, 1.0f); - SHEditorWidgets::DragFloat("Bounciness", [collider] { return collider->GetBounciness(); }, [collider](float value) { collider->SetBounciness(value); }, "Bounciness", 0.05f, 0.0f, 1.0f); - SHEditorWidgets::DragFloat("Mass Density", [collider] { return collider->GetDensity(); }, [collider](float value) { collider->SetDensity(value); }, "Mass Density", 0.1f, 0.0f); + SHEditorWidgets::DragFloat("Friction", [shape] { return shape->GetFriction(); }, [shape](float value) { shape->SetFriction(value); }, "Friction", 0.05f, 0.0f, 1.0f); + SHEditorWidgets::DragFloat("Bounciness", [shape] { return shape->GetBounciness(); }, [shape](float value) { shape->SetBounciness(value); }, "Bounciness", 0.05f, 0.0f, 1.0f); + SHEditorWidgets::DragFloat("Mass Density", [shape] { return shape->GetDensity(); }, [shape](float value) { shape->SetDensity(value); }, "Mass Density", 0.1f, 0.0f); } SHEditorWidgets::BeginPanel("Offsets",{ ImGui::GetContentRegionAvail().x, 30.0f }); - SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [&collider] {return collider->GetPositionOffset(); }, [&collider](SHVec3 const& vec) {collider->SetPositionOffset(vec); }); + SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [&shape] {return shape->GetPositionOffset(); }, [&shape](SHVec3 const& vec) {shape->SetPositionOffset(vec); }); SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, - [&collider] + [&shape] { - auto offset = collider->GetRotationOffset(); + auto offset = shape->GetRotationOffset(); return offset; }, - [&collider](SHVec3 const& vec) + [&shape](SHVec3 const& vec) { - collider->SetRotationOffset(vec); + shape->SetRotationOffset(vec); }, true); SHEditorWidgets::EndPanel(); } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp index 845634fe..2f4e3819 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -14,7 +14,7 @@ #include "SHCollisionShape.h" // Project Headers -#include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Reflection/SHReflectionMetadata.h" #include "Tools/Utilities/SHUtilities.h" @@ -95,14 +95,6 @@ namespace SHADE return flags & FLAG_VALUE; } - bool SHCollisionShape::GetDebugDrawState() const noexcept - { - static constexpr int FLAG_POS = 6; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - return flags & FLAG_VALUE; - } - const SHCollisionTag& SHCollisionShape::GetCollisionTag() const noexcept { return *collisionTag; @@ -150,14 +142,6 @@ namespace SHADE isTrigger ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; } - void SHCollisionShape::SetDebugDrawState(bool isDebugDrawing) noexcept - { - static constexpr int FLAG_POS = 6; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - isDebugDrawing ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; - } - void SHCollisionShape::SetCollisionTag(SHCollisionTag* newCollisionTag) noexcept { collisionTag = newCollisionTag; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 420c0d6e..c05baddd 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -16,7 +16,7 @@ #include "ECS_Base/Entity/SHEntity.h" #include "Math/Geometry/SHShape.h" #include "Math/SHQuaternion.h" -#include "Physics/Collision/SHCollisionTags.h" +#include "Physics/Collision/CollisionTags/SHCollisionTags.h" #include "Physics/Collision/SHPhysicsMaterial.h" #include "SHCollisionShapeID.h" @@ -44,8 +44,8 @@ namespace SHADE enum class Type { - BOX - , SPHERE + SPHERE + , BOX , CAPSULE , COUNT @@ -91,8 +91,6 @@ namespace SHADE [[nodiscard]] Type GetType () const noexcept; [[nodiscard]] bool IsTrigger () const noexcept; [[nodiscard]] bool IsColliding () const noexcept; - [[nodiscard]] bool GetDebugDrawState () const noexcept; - [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; @@ -110,7 +108,6 @@ namespace SHADE // Flags void SetIsTrigger (bool isTrigger) noexcept; - void SetDebugDrawState (bool isDebugDrawing) noexcept; void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; @@ -126,7 +123,7 @@ namespace SHADE SHVec3 positionOffset; SHVec3 rotationOffset; - uint8_t flags; // 0 debugDraw wasColliding isColliding trigger capsule sphere box + uint8_t flags; // 0 0 wasColliding isColliding trigger capsule sphere box SHCollisionTag* collisionTag; RTTR_ENABLE() diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.cpp b/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp similarity index 100% rename from SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.cpp rename to SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.h b/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.h similarity index 100% rename from SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.h rename to SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.h diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionTags.cpp b/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTags.cpp similarity index 100% rename from SHADE_Engine/src/Physics/Collision/SHCollisionTags.cpp rename to SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTags.cpp diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionTags.h b/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTags.h similarity index 100% rename from SHADE_Engine/src/Physics/Collision/SHCollisionTags.h rename to SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTags.h diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index e950c4fb..1d29a585 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -25,14 +25,22 @@ namespace SHADE SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept : entityID { eid } , shapeIDCounter { 0 } +#ifdef SHEDITOR + , debugDraw { false } +#endif , rigidBody { nullptr } , shapeFactory { nullptr } , transform { worldTransform } - {} + { + + } SHCollider::SHCollider(const SHCollider& rhs) noexcept : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } +#ifdef SHEDITOR + , debugDraw { rhs.debugDraw } +#endif , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -49,6 +57,9 @@ namespace SHADE SHCollider::SHCollider(SHCollider&& rhs) noexcept : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } +#ifdef SHEDITOR + , debugDraw { rhs.debugDraw } +#endif , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -94,6 +105,10 @@ namespace SHADE shapeFactory = rhs.shapeFactory; transform = rhs.transform; + #ifdef SHEDITOR + debugDraw = rhs.debugDraw; + #endif + copyShapes(rhs); return *this; @@ -112,6 +127,10 @@ namespace SHADE shapeFactory = rhs.shapeFactory; transform = rhs.transform; + #ifdef SHEDITOR + debugDraw = rhs.debugDraw; + #endif + copyShapes(rhs); return *this; @@ -121,6 +140,13 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ +#ifdef SHEDITOR + bool SHCollider::GetDebugDrawState() const noexcept + { + return debugDraw; + } +#endif + const SHTransform& SHCollider::GetTransform() const noexcept { return transform; @@ -160,6 +186,13 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ +#ifdef SHEDITOR + void SHCollider::SetDebugDrawState(bool state) noexcept + { + debugDraw = state; + } +#endif + void SHCollider::SetRigidBody(SHRigidBody* rb) noexcept { rigidBody = rb; @@ -190,7 +223,6 @@ namespace SHADE shapeFactory = factory; } - /*-----------------------------------------------------------------------------------*/ /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -266,9 +298,28 @@ namespace SHADE SHLOG_INFO_D("Removing Collision Shape {} from Entity {}", index, entityID) } - void SHCollider::RecomputeShapes() const noexcept + void SHCollider::RecomputeShapes() noexcept { - + for (auto* shape : shapes) + { + switch (shape->GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + recomputeSphere(dynamic_cast(shape)); + break; + } + case SHCollisionShape::Type::BOX: + { + break; + } + case SHCollisionShape::Type::CAPSULE: + { + break; + } + default: break; + } + } } /*-----------------------------------------------------------------------------------*/ @@ -324,4 +375,19 @@ namespace SHADE ++shapeIDCounter; } + void SHCollider::recomputeSphere(SHSphereCollisionShape* sphere) noexcept + { + // Recompute world radius + const float SPHERE_SCALE = std::fabs(SHMath::Max({ transform.scale.x, transform.scale.y, transform.scale.z })); + sphere->SetScale(SPHERE_SCALE); + + // Recompute center + const SHQuaternion FINAL_ROT = transform.orientation * SHQuaternion::FromEuler(sphere->rotationOffset); + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(transform.position); + + const SHVec3 NEW_CENTER = SHVec3::Transform(sphere->positionOffset, TRS); + sphere->SetCenter(NEW_CENTER); + } + + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index 37d96caa..cab433a5 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -78,6 +78,10 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ + #ifdef SHEDITOR + [[nodiscard]] bool GetDebugDrawState () const noexcept; + #endif + [[nodiscard]] const SHTransform& GetTransform () const noexcept; [[nodiscard]] const SHVec3& GetPosition () const noexcept; [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; @@ -90,14 +94,18 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetRigidBody (SHRigidBody* rb) noexcept; + #ifdef SHEDITOR + void SetDebugDrawState (bool state) noexcept; + #endif - void SetTransform (const SHTransform& newTransform) noexcept; - void SetPosition (const SHVec3& newPosition) noexcept; - void SetOrientation (const SHQuaternion& newOrientation) noexcept; - void SetScale (const SHVec3& newScale) noexcept; + void SetRigidBody (SHRigidBody* rb) noexcept; - void SetFactory (SHCollisionShapeFactory* factory) noexcept; + void SetTransform (const SHTransform& newTransform) noexcept; + void SetPosition (const SHVec3& newPosition) noexcept; + void SetOrientation (const SHQuaternion& newOrientation) noexcept; + void SetScale (const SHVec3& newScale) noexcept; + + void SetFactory (SHCollisionShapeFactory* factory) noexcept; /*---------------------------------------------------------------------------------*/ /* Member Functions */ @@ -125,6 +133,7 @@ namespace SHADE * The index of the newly added shape. */ int AddSphereCollisionShape (float relativeRadius, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero); + // TODO: Add Box & Capsule /** @@ -142,7 +151,7 @@ namespace SHADE * @brief * Recomputes the transforms for all shapes in this composite collider. */ - void RecomputeShapes () const noexcept; + void RecomputeShapes () noexcept; protected: /*---------------------------------------------------------------------------------*/ @@ -152,6 +161,10 @@ namespace SHADE EntityID entityID; uint32_t shapeIDCounter; // This increments everytime a shape is added to differentiate shapes. + #ifdef SHEDITOR + bool debugDraw; + #endif + SHRigidBody* rigidBody; SHCollisionShapeFactory* shapeFactory; @@ -163,8 +176,10 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - void copyShapes (const SHCollider& rhsCollider); - void copyShape (const SHCollisionShape* rhsShape); + void copyShapes (const SHCollider& rhsCollider); + void copyShape (const SHCollisionShape* rhsShape); + + void recomputeSphere (SHSphereCollisionShape* sphere) noexcept; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index 0d7434f6..e6c02168 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -122,15 +122,20 @@ namespace SHADE { rigidBody.linearVelocity = SHVec3::Zero; } - else // Dynamic & Kinematic bodies + // Dynamic & Kinematic bodies + // Both dynamic and kinematic can sleep when their velocities are under the thresholds. + else if (!rigidBody.IsSleeping()) { - // Both dynamic and kinematic can sleep when their velocities are under the thresholds. - if (!rigidBody.IsSleeping()) - { - ENFORCE_CONSTRAINED_VELOCITIES(rigidBody); + ENFORCE_CONSTRAINED_VELOCITIES(rigidBody); - rigidBody.motionState.IntegratePosition(rigidBody.linearVelocity, dt); - // TODO: Integrate orientations + rigidBody.motionState.IntegratePosition(rigidBody.linearVelocity, dt); + // TODO: Integrate orientations + + // Sync with collider transforms if a collider is present + if (rigidBody.collider) + { + rigidBody.collider->SetPosition(rigidBody.motionState.position); + // TODO: Sync orientations } } diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp new file mode 100644 index 00000000..c73e8ab7 --- /dev/null +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -0,0 +1,60 @@ +/**************************************************************************************** + * \file SHPhysicsDebugDrawRoutine.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Physics Debug-Draw Routine + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "Physics/System/SHPhysicsSystem.h" + +// Project Headers +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" + +#include "Scripting/SHScriptEngine.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsSystem::PhysicsDebugDraw::PhysicsDebugDraw() + : SHSystemRoutine { "Physics Debug Draw", false } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsSystem::PhysicsDebugDraw::Execute(double) noexcept + { + auto* physicsSystem = reinterpret_cast(GetSystem()); + + // Get debug drawing system + auto* debugDrawSystem = SHSystemManager::GetSystem(); + if (!debugDrawSystem) + { + SHLOG_ERROR("Debug draw system unavailable! Colliders cannot be drawn!") + return; + } + + //SHPhysicsDebugDraw& physicsDebugRenderer = physicsSystem->debugRenderer; + + //if (!physicsDebugRenderer.GetDebugDrawState()) + // return; + + //// Draw colliders + //if (physicsDebugRenderer.GetDrawSpecificCollidersState()) + // physicsDebugRenderer.DrawSpecificColliders(debugDrawSystem); + //else if (physicsDebugRenderer.GetDrawAllCollidersState()) + // physicsDebugRenderer.DrawAllColliders(debugDrawSystem); + } +} diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp index cd4b1cff..d5492cdf 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp @@ -15,6 +15,7 @@ // Project Headers #include "ECS_Base/Managers/SHSystemManager.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" #include "Math/Transform/SHTransformComponent.h" #include "Scripting/SHScriptEngine.h" @@ -77,4 +78,5 @@ namespace SHADE if (scriptingSystem != nullptr) scriptingSystem->ExecuteCollisionFunctions(); } -} + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index d7bece3b..59cd68fb 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -19,7 +19,7 @@ #include "ECS_Base/Managers/SHEntityManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.h" -#include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Physics/Interface/SHColliderComponent.h" namespace SHADE @@ -42,7 +42,7 @@ namespace SHADE eventFunctions[3] = { &SHPhysicsSystem::onSceneExit , SH_SCENE_ON_EXIT_EVENT }; #ifdef SHEDITOR - eventFunctions[4] = { &SHPhysicsSystem::onEditorPlay , SH_EDITOR_ON_PLAY_EVENT }; + eventFunctions[4] = { &SHPhysicsSystem::onEditorPlay , SH_EDITOR_ON_PLAY_EVENT }; #endif } @@ -189,7 +189,7 @@ namespace SHADE physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); if (PHYSICS_OBJECT.collider) - physicsWorld->AddCollider(PHYSICS_OBJECT.collider); + physicsWorld->AddCollider(PHYSICS_OBJECT.collider); } #endif @@ -210,7 +210,7 @@ namespace SHADE physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); if (PHYSICS_OBJECT.collider) - physicsWorld->RemoveCollider(PHYSICS_OBJECT.collider); + physicsWorld->RemoveCollider(PHYSICS_OBJECT.collider); } delete physicsWorld; @@ -322,7 +322,7 @@ namespace SHADE physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); if (PHYSICS_OBJECT.collider) - physicsWorld->AddCollider(PHYSICS_OBJECT.collider); + physicsWorld->AddCollider(PHYSICS_OBJECT.collider); } return onEditorPlayEvent.get()->handle; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index 2a351e86..ca73df81 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -21,7 +21,6 @@ #include "Physics/Interface/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -95,7 +94,8 @@ namespace SHADE /** * @brief * The physics update that runs after the simulation. This sets the rendering - * transforms and sends messages to scripting system for collision & trigger events. + * transforms and sends messages to scripting system for collision & trigger events.
+ * */ class SH_API PhysicsPostUpdate final : public SHSystemRoutine { @@ -104,6 +104,21 @@ namespace SHADE void Execute(double dt) noexcept override; }; + #ifdef SHEDITOR + /** + * @brief + * If the editor is enabled, this routine invokes debug drawing for colliders. + * and collision information. + */ + class SH_API PhysicsDebugDraw final : public SHSystemRoutine + { + public: + PhysicsDebugDraw(); + void Execute(double dt) noexcept override; + }; + + #endif + private: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -125,7 +140,6 @@ namespace SHADE EventFunctionPair eventFunctions[NUM_EVENT_FUNCTIONS]; // System data - bool worldUpdated; double interpolationFactor; double fixedDT; @@ -133,7 +147,6 @@ namespace SHADE // Sub-systems / managers SHPhysicsWorld* physicsWorld; - SHPhysicsObjectManager physicsObjectManager; /*---------------------------------------------------------------------------------*/ From bf8a410fa2a933656b68d5df749010c18c5182ee Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 11 Dec 2022 20:33:30 +0800 Subject: [PATCH 014/164] Fixed bug where colliders were not properly deserialised --- .../Collision/CollisionShapes/SHCollisionShapeFactory.cpp | 3 +-- SHADE_Engine/src/Serialization/SHYAMLConverters.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp index 293d5c2a..d6ef5f0d 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp @@ -59,12 +59,11 @@ namespace SHADE case SHCollisionShape::Type::SPHERE: { SHSphereCollisionShape* sphere = spheres.find(shape->id)->second; + spheres.erase(shape->id); delete sphere; sphere = nullptr; - spheres.erase(shape->id); - break; } case SHCollisionShape::Type::CAPSULE: diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index cbbe8505..73b22fd6 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -237,8 +237,8 @@ namespace YAML switch (colliderType) { - case SHCollisionShape::Type::BOX: break; case SHCollisionShape::Type::SPHERE: collider->AddSphereCollisionShape(1.0f); break; + case SHCollisionShape::Type::BOX: break; case SHCollisionShape::Type::CAPSULE: break; default:; } From 0cebedeee09f32c092966a9aba30e4da1a51616a Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 11 Dec 2022 20:44:40 +0800 Subject: [PATCH 015/164] Fixed compile errors with merged scene init and exit events --- SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp | 4 ++-- SHADE_Engine/src/Scene/SHSceneManager.cpp | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 59cd68fb..388adadc 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -38,8 +38,8 @@ namespace SHADE eventFunctions[0] = { &SHPhysicsSystem::onComponentAdded , SH_COMPONENT_ADDED_EVENT }; eventFunctions[1] = { &SHPhysicsSystem::onComponentRemoved, SH_COMPONENT_REMOVED_EVENT }; - eventFunctions[2] = { &SHPhysicsSystem::onSceneInit , SH_SCENE_ON_INIT_EVENT }; - eventFunctions[3] = { &SHPhysicsSystem::onSceneExit , SH_SCENE_ON_EXIT_EVENT }; + eventFunctions[2] = { &SHPhysicsSystem::onSceneInit , SH_SCENE_INIT_POST }; + eventFunctions[3] = { &SHPhysicsSystem::onSceneExit , SH_SCENE_EXIT_POST }; #ifdef SHEDITOR eventFunctions[4] = { &SHPhysicsSystem::onEditorPlay , SH_EDITOR_ON_PLAY_EVENT }; diff --git a/SHADE_Engine/src/Scene/SHSceneManager.cpp b/SHADE_Engine/src/Scene/SHSceneManager.cpp index d3b84d6a..79e8818a 100644 --- a/SHADE_Engine/src/Scene/SHSceneManager.cpp +++ b/SHADE_Engine/src/Scene/SHSceneManager.cpp @@ -62,8 +62,6 @@ namespace SHADE SHEventManager::BroadcastEvent(exitEvent, SH_SCENE_EXIT_PRE); currentScene->Free(); - SHEventManager::BroadcastEvent(SHSceneExitEvent{ currentSceneID }, SH_SCENE_ON_EXIT_EVENT); - currentScene->Unload(); SHEntityManager::DestroyAllEntity(); @@ -112,8 +110,6 @@ namespace SHADE SHEventManager::BroadcastEvent(exitEvent, SH_SCENE_EXIT_PRE); currentScene->Free(); - SHEventManager::BroadcastEvent(SHSceneExitEvent{ currentSceneID }, SH_SCENE_ON_EXIT_EVENT); - if (cleanReload == true) { cleanReload = false; From af3a5e7dc9313c5cbbc14123b3b8e533af715812 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 13 Dec 2022 03:54:37 +0800 Subject: [PATCH 016/164] Re-implemented Collider Debug Drawing --- Assets/Scenes/PhysicsSandbox.shade | 1 + .../src/Application/SBApplication.cpp | 7 +- .../Inspector/SHEditorComponentView.hpp | 3 +- SHADE_Engine/src/Events/SHEventDefines.h | 26 +-- .../src/Physics/Collision/SHCollider.cpp | 62 ++++--- .../src/Physics/Collision/SHCollider.h | 6 - .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 2 + .../Physics/Interface/SHColliderComponent.cpp | 19 ++- .../Physics/Interface/SHColliderComponent.h | 10 +- SHADE_Engine/src/Physics/SHPhysicsEvents.h | 10 +- .../Routines/SHPhysicsDebugDrawRoutine.cpp | 78 ++++++--- .../Routines/SHPhysicsPostUpdateRoutine.cpp | 1 - .../System/SHPhysicsDebugDrawSystem.cpp | 158 ++++++++++++++++++ .../Physics/System/SHPhysicsDebugDrawSystem.h | 150 +++++++++++++++++ .../src/Physics/System/SHPhysicsSystem.cpp | 3 +- .../src/Physics/System/SHPhysicsSystem.h | 33 ++-- .../src/Serialization/SHYAMLConverters.h | 8 + 17 files changed, 482 insertions(+), 95 deletions(-) create mode 100644 SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp create mode 100644 SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index f600c4ae..0005b35e 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -26,6 +26,7 @@ Freeze Rotation Z: false IsActive: true Collider Component: + DrawColliders: true Colliders: - Is Trigger: false Type: Sphere diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 3cb504c0..aa5a4f0e 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -31,6 +31,7 @@ #include "Input/SHInputManager.h" #include "Math/Transform/SHTransformSystem.h" #include "Physics/System/SHPhysicsSystem.h" +#include "Physics/System/SHPhysicsDebugDrawSystem.h" #include "Scripting/SHScriptEngine.h" #include "UI/SHUISystem.h" @@ -83,7 +84,6 @@ namespace Sandbox SHSystemManager::CreateSystem(); SHGraphicsSystem* graphicsSystem = static_cast(SHSystemManager::GetSystem()); - SHPhysicsSystem* physicsSystem = SHSystemManager::GetSystem(); // Link up SHDebugDraw SHSystemManager::CreateSystem(); @@ -98,6 +98,8 @@ namespace Sandbox editor->SetSDLWindow(sdlWindow); editor->SetSHWindow(&window); } + + SHSystemManager::CreateSystem(); #endif // Create Routines @@ -111,8 +113,9 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); + #ifdef SHEDITOR - SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); #endif SHSystemManager::RegisterRoutine(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index dfc89df1..dd531708 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -322,8 +322,7 @@ namespace SHADE { DrawContextMenu(component); - auto* collider = component->GetCollider(); - SHEditorWidgets::CheckBox("Draw Colliders", [collider] { return collider->GetDebugDrawState(); }, [collider](bool value) { collider->SetDebugDrawState(value); }); + SHEditorWidgets::CheckBox("Draw Colliders", [component] { return component->GetDebugDrawState(); }, [component](bool value) { component->SetDebugDrawState(value); }); auto* collisionShapes = component->GetCollisionShapes(); int const size = collisionShapes ? static_cast(collisionShapes->size()) : 0; diff --git a/SHADE_Engine/src/Events/SHEventDefines.h b/SHADE_Engine/src/Events/SHEventDefines.h index bdc4c505..2a45da1b 100644 --- a/SHADE_Engine/src/Events/SHEventDefines.h +++ b/SHADE_Engine/src/Events/SHEventDefines.h @@ -10,16 +10,18 @@ constexpr SHEventIdentifier SH_ENTITY_DESTROYED_EVENT { 1 }; constexpr SHEventIdentifier SH_ENTITY_CREATION_EVENT { 2 }; constexpr SHEventIdentifier SH_COMPONENT_ADDED_EVENT { 3 }; constexpr SHEventIdentifier SH_COMPONENT_REMOVED_EVENT { 4 }; -constexpr SHEventIdentifier SH_SCENEGRAPH_CHANGE_PARENT_EVENT { 5 }; -constexpr SHEventIdentifier SH_SCENEGRAPH_ADD_CHILD_EVENT { 6 }; -constexpr SHEventIdentifier SH_SCENEGRAPH_REMOVE_CHILD_EVENT { 7 }; -constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_ADDED_EVENT { 8 }; -constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_REMOVED_EVENT { 9 }; -constexpr SHEventIdentifier SH_EDITOR_ON_PLAY_EVENT { 10 }; -constexpr SHEventIdentifier SH_EDITOR_ON_PAUSE_EVENT { 11 }; -constexpr SHEventIdentifier SH_EDITOR_ON_STOP_EVENT { 12 }; -constexpr SHEventIdentifier SH_SCENE_INIT_PRE { 13 }; -constexpr SHEventIdentifier SH_SCENE_INIT_POST { 14 }; -constexpr SHEventIdentifier SH_SCENE_EXIT_PRE { 15 }; -constexpr SHEventIdentifier SH_SCENE_EXIT_POST { 16 }; +constexpr SHEventIdentifier SH_SCENE_INIT_PRE { 5 }; +constexpr SHEventIdentifier SH_SCENE_INIT_POST { 6 }; +constexpr SHEventIdentifier SH_SCENE_EXIT_PRE { 7 }; +constexpr SHEventIdentifier SH_SCENE_EXIT_POST { 8 }; +constexpr SHEventIdentifier SH_SCENEGRAPH_CHANGE_PARENT_EVENT { 9 }; +constexpr SHEventIdentifier SH_SCENEGRAPH_ADD_CHILD_EVENT { 10 }; +constexpr SHEventIdentifier SH_SCENEGRAPH_REMOVE_CHILD_EVENT { 11 }; +constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_ADDED_EVENT { 12 }; +constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_REMOVED_EVENT { 13 }; +constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_DRAW_EVENT { 14 }; +constexpr SHEventIdentifier SH_EDITOR_ON_PLAY_EVENT { 15 }; +constexpr SHEventIdentifier SH_EDITOR_ON_PAUSE_EVENT { 16 }; +constexpr SHEventIdentifier SH_EDITOR_ON_STOP_EVENT { 17 }; + diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index 1d29a585..b64bab53 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -14,7 +14,10 @@ #include "SHCollider.h" // Project Headers +#include "Events/SHEvent.h" #include "Math/SHMathHelpers.h" +#include "Physics/SHPhysicsEvents.h" + namespace SHADE { @@ -25,9 +28,7 @@ namespace SHADE SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept : entityID { eid } , shapeIDCounter { 0 } -#ifdef SHEDITOR , debugDraw { false } -#endif , rigidBody { nullptr } , shapeFactory { nullptr } , transform { worldTransform } @@ -38,9 +39,7 @@ namespace SHADE SHCollider::SHCollider(const SHCollider& rhs) noexcept : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } -#ifdef SHEDITOR , debugDraw { rhs.debugDraw } -#endif , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -57,9 +56,7 @@ namespace SHADE SHCollider::SHCollider(SHCollider&& rhs) noexcept : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } -#ifdef SHEDITOR , debugDraw { rhs.debugDraw } -#endif , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -101,14 +98,11 @@ namespace SHADE } entityID = rhs.entityID; + debugDraw = rhs.debugDraw; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; transform = rhs.transform; - #ifdef SHEDITOR - debugDraw = rhs.debugDraw; - #endif - copyShapes(rhs); return *this; @@ -123,14 +117,11 @@ namespace SHADE } entityID = rhs.entityID; + debugDraw = rhs.debugDraw; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; transform = rhs.transform; - #ifdef SHEDITOR - debugDraw = rhs.debugDraw; - #endif - copyShapes(rhs); return *this; @@ -140,12 +131,10 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ -#ifdef SHEDITOR bool SHCollider::GetDebugDrawState() const noexcept { return debugDraw; } -#endif const SHTransform& SHCollider::GetTransform() const noexcept { @@ -186,12 +175,23 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ -#ifdef SHEDITOR void SHCollider::SetDebugDrawState(bool state) noexcept { debugDraw = state; + + #ifdef SHEDITOR + + // Broadcast event for the Debug Draw system to catch + const SHColliderOnDebugDrawEvent EVENT_DATA + { + .entityID = entityID + , .debugDrawState = debugDraw + }; + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_DRAW_EVENT); + + #endif } -#endif void SHCollider::SetRigidBody(SHRigidBody* rb) noexcept { @@ -236,7 +236,7 @@ namespace SHADE { if (!shapeFactory) { - SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add new shape!", entityID) + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add new shape!", entityID) return -1; } @@ -266,6 +266,17 @@ namespace SHADE sphere->rotationOffset = rotOffset; shapes.emplace_back(sphere); + + // Broadcast Event for adding a shape + const SHPhysicsColliderAddedEvent EVENT_DATA + { + .entityID = entityID + , .colliderType = SHCollisionShape::Type::SPHERE + , .colliderIndex = static_cast(shapes.size()) + }; + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT); + return static_cast(shapes.size()); } @@ -273,7 +284,7 @@ namespace SHADE { if (!shapeFactory) { - SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add remove shape!", entityID) + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add remove shape!", entityID) return; } @@ -289,12 +300,22 @@ namespace SHADE break; } + const SHPhysicsColliderRemovedEvent EVENT_DATA + { + .entityID = entityID + , .colliderType = (*shapeIter)->GetType() + , .colliderIndex = index + }; + shapeFactory->DestroyShape(*shapeIter); *shapeIter = nullptr; // Remove the shape from the container to prevent accessing a nullptr shapeIter = shapes.erase(shapeIter); + // Broadcast Event for removing a shape + SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT); + SHLOG_INFO_D("Removing Collision Shape {} from Entity {}", index, entityID) } @@ -386,6 +407,7 @@ namespace SHADE const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(transform.position); const SHVec3 NEW_CENTER = SHVec3::Transform(sphere->positionOffset, TRS); + sphere->SetCenter(NEW_CENTER); } diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index cab433a5..927c6155 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -78,9 +78,7 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - #ifdef SHEDITOR [[nodiscard]] bool GetDebugDrawState () const noexcept; - #endif [[nodiscard]] const SHTransform& GetTransform () const noexcept; [[nodiscard]] const SHVec3& GetPosition () const noexcept; @@ -94,9 +92,7 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - #ifdef SHEDITOR void SetDebugDrawState (bool state) noexcept; - #endif void SetRigidBody (SHRigidBody* rb) noexcept; @@ -161,9 +157,7 @@ namespace SHADE EntityID entityID; uint32_t shapeIDCounter; // This increments everytime a shape is added to differentiate shapes. - #ifdef SHEDITOR bool debugDraw; - #endif SHRigidBody* rigidBody; SHCollisionShapeFactory* shapeFactory; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index e6c02168..7422d6ff 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -136,6 +136,8 @@ namespace SHADE { rigidBody.collider->SetPosition(rigidBody.motionState.position); // TODO: Sync orientations + + rigidBody.collider->RecomputeShapes(); } } diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp index d345290c..7425f418 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp @@ -54,7 +54,14 @@ namespace SHADE return collider->GetCollisionShape(index); } + bool SHColliderComponent::GetDebugDrawState() const noexcept + { + if (!collider) + return false; + return collider->GetDebugDrawState(); + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -64,6 +71,13 @@ namespace SHADE collider = c; } + void SHColliderComponent::SetDebugDrawState(bool state) noexcept + { + if (collider) + collider->SetDebugDrawState(state); + } + + } // namespace SHADE RTTR_REGISTRATION @@ -71,5 +85,6 @@ RTTR_REGISTRATION using namespace rttr; using namespace SHADE; - registration::class_("Collider Component"); -} \ No newline at end of file + registration::class_("Collider Component") + .property("Is Debug Drawing", &SHColliderComponent::GetDebugDrawState, &SHColliderComponent::SetDebugDrawState); +} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h index 83fb235c..d62b9b90 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h @@ -58,11 +58,19 @@ namespace SHADE [[nodiscard]] const SHCollider::CollisionShapes* const GetCollisionShapes() const noexcept; [[nodiscard]] SHCollisionShape* const GetCollisionShape (int index) const; + // Required for serialisation + + [[nodiscard]] bool GetDebugDrawState () const noexcept; + /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetCollider(SHCollider* c) noexcept; + void SetCollider (SHCollider* c) noexcept; + + // Required for serialisation + + void SetDebugDrawState (bool state) noexcept; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/SHPhysicsEvents.h b/SHADE_Engine/src/Physics/SHPhysicsEvents.h index 35217347..2f78b6ee 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsEvents.h +++ b/SHADE_Engine/src/Physics/SHPhysicsEvents.h @@ -13,7 +13,6 @@ // Project Headers #include "Collision/CollisionShapes/SHCollisionShape.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -28,9 +27,16 @@ namespace SHADE }; struct SHPhysicsColliderRemovedEvent + { + EntityID entityID; + SHCollisionShape::Type colliderType; + int colliderIndex; + }; + + struct SHColliderOnDebugDrawEvent { EntityID entityID; - int colliderIndex; + bool debugDrawState; }; diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp index c73e8ab7..72b2aad5 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHPhysicsDebugDrawRoutine.cpp + * \file SHPhysicsDebugDrawRutine.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Physics Debug-Draw Routine + * \brief Implementation for the Physics Debut Draw Routine * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -11,14 +11,13 @@ #include // Primary Header -#include "Physics/System/SHPhysicsSystem.h" +#include "Physics/System/SHPhysicsDebugDrawSystem.h" // Project Headers #include "ECS_Base/Managers/SHSystemManager.h" #include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" - -#include "Scripting/SHScriptEngine.h" - +#include "Math/Transform/SHTransformComponent.h" +#include "Physics/System/SHPhysicsSystem.h" namespace SHADE { @@ -26,35 +25,64 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHPhysicsSystem::PhysicsDebugDraw::PhysicsDebugDraw() - : SHSystemRoutine { "Physics Debug Draw", false } + SHPhysicsDebugDrawSystem::PhysicsDebugDraw::PhysicsDebugDraw() + : SHSystemRoutine { "Physics Debug-Draw", true } {} /*-----------------------------------------------------------------------------------*/ /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsSystem::PhysicsDebugDraw::Execute(double) noexcept + void SHPhysicsDebugDrawSystem::PhysicsDebugDraw::Execute(double) noexcept { - auto* physicsSystem = reinterpret_cast(GetSystem()); - - // Get debug drawing system - auto* debugDrawSystem = SHSystemManager::GetSystem(); - if (!debugDrawSystem) - { - SHLOG_ERROR("Debug draw system unavailable! Colliders cannot be drawn!") + auto* physicsDebugDrawSystem = reinterpret_cast(GetSystem()); + + if (!physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::ACTIVE)) return; + + auto* physicsSystem = SHSystemManager::GetSystem(); + auto* debugDrawSystem = SHSystemManager::GetSystem(); + + const bool DRAW_COLLIDERS = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::COLLIDERS); + const bool DRAW_CONTACTS = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::CONTACTS); + const bool DRAW_RAYCASTS = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::RAYCASTS); + const bool DRAW_BROADPHASE = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::BROADPHASE); + + // If draw all colliders is active, get all colliders from the dense set and draw. + // Else we check if any colliders have been flagged for drawing. + if (DRAW_COLLIDERS) + { + const auto& COLLIDER_COMPONENT_DENSE = SHComponentManager::GetDense(); + for (const auto& COLLIDER_COMPONENT : COLLIDER_COMPONENT_DENSE) + { + const auto* COLLIDER = COLLIDER_COMPONENT.GetCollider(); + drawCollider(debugDrawSystem, *COLLIDER); + } + } + else if (!physicsDebugDrawSystem->collidersToDraw.empty()) + { + for (const auto EID : physicsDebugDrawSystem->collidersToDraw) + { + const auto* COLLIDER = SHComponentManager::GetComponent(EID)->GetCollider(); + drawCollider(debugDrawSystem, *COLLIDER); + } } - //SHPhysicsDebugDraw& physicsDebugRenderer = physicsSystem->debugRenderer; + if (DRAW_CONTACTS) + { + // TODO + } - //if (!physicsDebugRenderer.GetDebugDrawState()) - // return; + if (DRAW_RAYCASTS) + { + // TODO + physicsDebugDrawSystem->raysToDraw.clear(); + } - //// Draw colliders - //if (physicsDebugRenderer.GetDrawSpecificCollidersState()) - // physicsDebugRenderer.DrawSpecificColliders(debugDrawSystem); - //else if (physicsDebugRenderer.GetDrawAllCollidersState()) - // physicsDebugRenderer.DrawAllColliders(debugDrawSystem); + if (DRAW_BROADPHASE) + { + // TODO + } } -} + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp index d5492cdf..8e7450ce 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp @@ -15,7 +15,6 @@ // Project Headers #include "ECS_Base/Managers/SHSystemManager.h" -#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" #include "Math/Transform/SHTransformComponent.h" #include "Scripting/SHScriptEngine.h" diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp new file mode 100644 index 00000000..77241ae9 --- /dev/null +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -0,0 +1,158 @@ +/**************************************************************************************** + * \file SHPhysicsDebugDrawSystem.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Physics Debug Draw System + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsDebugDrawSystem.h" + +// Project Header +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Math/Transform/SHTransformComponent.h" +#include "Physics/SHPhysicsEvents.h" +#include "Tools/Utilities/SHUtilities.h" + +namespace SHADE +{ + const SHColour SHPhysicsDebugDrawSystem::DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::COUNT)] + { + SHColour::GREEN // Colliders + , SHColour::PURPLE // Triggers + , SHColour::RED // Contacts + , SHColour::ORANGE // Raycasts + , SHColour::CYAN // Broadphase + }; + + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsDebugDrawSystem::SHPhysicsDebugDrawSystem() noexcept + : flags { 0 } + { + collidersToDraw.clear(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHPhysicsDebugDrawSystem::GetFlagState(DebugDrawFlags flag) const noexcept + { + const uint8_t ENUM_VALUE = SHUtilities::ConvertEnum(flag); + return flags & ENUM_VALUE; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsDebugDrawSystem::SetFlagState(DebugDrawFlags flag, bool state) noexcept + { + const uint8_t ENUM_VALUE = SHUtilities::ConvertEnum(flag); + state ? flags |= ENUM_VALUE : flags &= ~ENUM_VALUE; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsDebugDrawSystem::Init() + { + SystemFamily::GetID(); + + // Register collider draw event + const std::shared_ptr EVENT_RECEIVER = std::make_shared>(this, &SHPhysicsDebugDrawSystem::onColliderDraw); + const ReceiverPtr EVENT_RECEIVER_PTR = std::dynamic_pointer_cast(EVENT_RECEIVER); + + SHEventManager::SubscribeTo(SH_PHYSICS_COLLIDER_DRAW_EVENT, EVENT_RECEIVER_PTR); + } + + void SHPhysicsDebugDrawSystem::Exit() + { + + } + + void SHPhysicsDebugDrawSystem::AddRaycast(const SHRay& ray, const SHPhysicsRaycastResult& result) noexcept + { + + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHEventHandle SHPhysicsDebugDrawSystem::onColliderDraw(SHEventPtr onColliderDrawEvent) + { + const auto& EVENT_DATA = reinterpret_cast*>(onColliderDrawEvent.get())->data; + + if (EVENT_DATA->debugDrawState) + { + if (collidersToDraw.empty()) + SetFlagState(DebugDrawFlags::ACTIVE, true); + + collidersToDraw.emplace(EVENT_DATA->entityID); + } + else + { + collidersToDraw.erase(EVENT_DATA->entityID); + + if (collidersToDraw.empty()) + SetFlagState(DebugDrawFlags::ACTIVE, false); + } + + return onColliderDrawEvent.get()->handle; + } + + void SHPhysicsDebugDrawSystem::drawCollider(SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept + { + for (const auto* SHAPE : collider.GetCollisionShapes()) + { + switch (SHAPE->GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + const SHSphereCollisionShape* SPHERE = dynamic_cast(SHAPE); + drawSphere(debugDrawSystem, *SPHERE); + + break; + } + case SHCollisionShape::Type::BOX: + { + break; + } + case SHCollisionShape::Type::CAPSULE: + { + break; + } + default: break; + } + } + } + + void SHPhysicsDebugDrawSystem::drawSphere(SHDebugDrawSystem* debugDrawSystem, const SHSphereCollisionShape& sphere) noexcept + { + const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(sphere.IsTrigger() ? Colours::TRIGGER : Colours::COLLIDER)]; + debugDrawSystem->DrawSphere(DRAW_COLOUR, sphere.GetCenter(), sphere.GetWorldRadius()); + } + + void SHPhysicsDebugDrawSystem::drawContact(SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Contact& contactInfo) noexcept + { + static const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::CONTACT)]; + } + + void SHPhysicsDebugDrawSystem::drawRaycast(SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept + { + static const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::RAYCAST)]; + } + + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h new file mode 100644 index 00000000..423ab62a --- /dev/null +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -0,0 +1,150 @@ +/**************************************************************************************** + * \file SHPhysicsDebugDrawSystem.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for the Physics Debug Draw System + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "ECS_Base/Entity/SHEntity.h" +#include "ECS_Base/System/SHSystem.h" +#include "ECS_Base/System/SHSystemRoutine.h" +#include "Events/SHEvent.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" +#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHPhysicsRaycastResult.h" +#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + class SH_API SHPhysicsDebugDrawSystem final : public SHSystem + { + public: + + enum class DebugDrawFlags : uint8_t + { + ACTIVE = 0x0001 + , COLLIDERS = 0x0002 + , CONTACTS = 0x0004 + , RAYCASTS = 0x0008 + , BROADPHASE = 0x0010 + }; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsDebugDrawSystem () noexcept; + ~SHPhysicsDebugDrawSystem() noexcept = default; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] bool GetFlagState (DebugDrawFlags flag) const noexcept; + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetFlagState (DebugDrawFlags flag, bool state) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + void Init () override; + void Exit () override; + + void AddRaycast (const SHRay& ray, const SHPhysicsRaycastResult& result) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* System Routines */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * If the editor is enabled, this routine invokes debug drawing for colliders and collision information. + */ + class SH_API PhysicsDebugDraw final : public SHSystemRoutine + { + public: + PhysicsDebugDraw(); + void Execute(double dt) noexcept override; + }; + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + union DebugDrawInfo + { + struct Contact + { + SHVec3 worldPos; + SHVec3 normal; + } contact; + + struct Raycast + { + SHVec3 start; + SHVec3 end; + } raycast; + }; + + using Colliders = std::unordered_set; + using Raycasts = std::vector; + using Contacts = std::vector; + + enum class Colours + { + COLLIDER + , TRIGGER + , CONTACT + , RAYCAST + , BROADPHASE + + , COUNT + }; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static const SHColour DEBUG_DRAW_COLOURS[static_cast(Colours::COUNT)]; + + // 0 0 0 drawBroadphase drawRaycasts drawContacts drawAllColliders debugDrawActive + uint8_t flags; + + Colliders collidersToDraw; + Raycasts raysToDraw; + Contacts contactToDraw; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + SHEventHandle onColliderDraw(SHEventPtr onColliderDrawEvent); + + static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; + static void drawSphere (SHDebugDrawSystem* debugDrawSystem, const SHSphereCollisionShape& sphere) noexcept; + + static void drawContact (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Contact& contactInfo) noexcept; + static void drawRaycast (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept; + + }; + + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 388adadc..20084ebe 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -110,13 +110,12 @@ namespace SHADE void SHPhysicsSystem::Init() { + // TODO(Diren): Consider using a non-static collision tag matrix. // Initialise collision tags std::filesystem::path defaultCollisionTagNameFilePath { ASSET_ROOT }; defaultCollisionTagNameFilePath.append("CollisionTags.SHConfig"); SHCollisionTagMatrix::Init(defaultCollisionTagNameFilePath); - // Link Managers to system - // Register Events for (int i = 0; i < NUM_EVENT_FUNCTIONS; ++i) { diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index ca73df81..ce1f1396 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -27,6 +27,10 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates a system for running and managing the physics simulation of the engine. + */ class SH_API SHPhysicsSystem final : public SHSystem { public: @@ -41,23 +45,27 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] double GetFixedUpdateRate() const noexcept; - [[nodiscard]] double GetFixedDT() const noexcept; + [[nodiscard]] double GetFixedUpdateRate() const noexcept; + [[nodiscard]] double GetFixedDT() const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ void SetFixedUpdateRate(double fixedUpdateRate) noexcept; - void SetFixedDT(double fixedDt) noexcept; + void SetFixedDT(double fixedDt) noexcept; /*---------------------------------------------------------------------------------*/ - /* Function Members */ + /* Member Functions */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * - Initialises the static collision tag matrix. + * - Registers the system to catch specific events. + */ void Init() override; void Exit() override; - void ForceUpdate(); /*---------------------------------------------------------------------------------*/ @@ -104,21 +112,6 @@ namespace SHADE void Execute(double dt) noexcept override; }; - #ifdef SHEDITOR - /** - * @brief - * If the editor is enabled, this routine invokes debug drawing for colliders. - * and collision information. - */ - class SH_API PhysicsDebugDraw final : public SHSystemRoutine - { - public: - PhysicsDebugDraw(); - void Execute(double dt) noexcept override; - }; - - #endif - private: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index 73b22fd6..b225a9a4 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -204,10 +204,15 @@ namespace YAML template<> struct convert { + static constexpr const char* DrawColliders = "DrawColliders"; static constexpr const char* Colliders = "Colliders"; + static Node encode(SHColliderComponent& rhs) { Node node, collidersNode; + + node[DrawColliders] = rhs.GetDebugDrawState(); + int const numColliders = static_cast(rhs.GetCollisionShapes()->size()); for (int i = 0; i < numColliders; ++i) { @@ -221,6 +226,9 @@ namespace YAML } static bool decode(Node const& node, SHColliderComponent& rhs) { + if (node[DrawColliders].IsDefined()) + rhs.SetDebugDrawState(node[DrawColliders].as()); + if (node[Colliders].IsDefined()) { int numColliders{}; From 53edffebac0e69feaf190ab2594ed16be07b04fc Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 15 Dec 2022 02:08:25 +0800 Subject: [PATCH 017/164] Added (untested) rotational motion to rigidbodies Also added a temporary solution for debug drawing rotated spheres --- .../Inspector/SHEditorComponentView.hpp | 9 +- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 12 +- .../MiddleEnd/Interface/SHDebugDrawSystem.h | 5 +- .../CollisionShapes/SHCollisionShape.h | 6 + .../SHSphereCollisionShape.cpp | 15 ++ .../CollisionShapes/SHSphereCollisionShape.h | 16 +- .../src/Physics/Dynamics/SHMotionState.cpp | 31 +++ .../src/Physics/Dynamics/SHMotionState.h | 50 ++++- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 23 +- .../src/Physics/Dynamics/SHRigidBody.cpp | 206 +++++++++++++++--- .../src/Physics/Dynamics/SHRigidBody.h | 74 ++++++- .../Interface/SHRigidBodyComponent.cpp | 144 ++++++------ .../Physics/Interface/SHRigidBodyComponent.h | 2 +- .../Routines/SHPhysicsPostUpdateRoutine.cpp | 18 +- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 2 +- .../System/SHPhysicsDebugDrawSystem.cpp | 9 +- .../Physics/System/SHPhysicsDebugDrawSystem.h | 4 +- SHADE_Engine/src/Tools/SHDebugDraw.cpp | 2 +- SHADE_Managed/src/Components/RigidBody.cxx | 6 - SHADE_Managed/src/Components/RigidBody.hxx | 1 - 20 files changed, 468 insertions(+), 167 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index dd531708..2bbc0305 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -286,7 +286,14 @@ namespace SHADE if(ImGui::CollapsingHeader("Debug Information", ImGuiTreeNodeFlags_DefaultOpen))//Dynamic or Kinematic only fields { SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component] {return component->GetPosition(); }, [](SHVec3 const& value) {}, false, "Position", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); - //SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] {return component->GetRotation(); }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); + SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] + { + // Convert it to degrees... + auto rot = component->GetRotation(); + for (size_t i = 0; i < SHVec3::SIZE; ++i) + rot[i] = SHMath::RadiansToDegrees(rot[i]); + return rot; + }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { SHEditorWidgets::DragVec3("Velocity", { "X", "Y", "Z" }, [component] {return component->GetLinearVelocity(); }, [](SHVec3 const& value) {}, false, "Linear Velocity", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 0bfa89a2..215c7fdb 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -203,9 +203,9 @@ namespace SHADE drawCube(points, color, pos, size); } - void SHDebugDrawSystem::DrawSphere(const SHVec4& color, const SHVec3& pos, double radius) + void SHDebugDrawSystem::DrawSphere(const SHVec4& color, const SHVec3& pos, const SHVec3& rot, double radius) { - drawSphere(points, color, pos, radius); + drawSphere(points, color, pos, rot, radius); } /*---------------------------------------------------------------------------------*/ @@ -238,7 +238,7 @@ namespace SHADE void SHDebugDrawSystem::DrawPersistentSphere(const SHVec4& color, const SHVec3& pos, double radius) { - drawSphere(persistentPoints, color, pos, radius); + drawSphere(persistentPoints, color, pos, SHVec3::Zero, radius); } void SHDebugDrawSystem::ClearPersistentDraws() @@ -315,7 +315,7 @@ namespace SHADE ); } - void SHDebugDrawSystem::drawSphere(std::vector& storage, const SHVec4& color, const SHVec3& pos, double radius) + void SHDebugDrawSystem::drawSphere(std::vector& storage, const SHVec4& color, const SHVec3& pos, const SHVec3& rot, double radius) { //if (spherePoints.empty()) { @@ -324,7 +324,9 @@ namespace SHADE static const SHMeshData SPHERE = SHPrimitiveGenerator::Sphere(); for (const auto& idx : SPHERE.Indices) { - spherePoints.emplace_back(SPHERE.VertexPositions[idx] * radius + pos); + SHVec3 SCALE { static_cast(radius) }; + const SHMatrix TRS = SHMatrix::Transform(pos, rot, { static_cast(radius) }); + spherePoints.emplace_back(SHVec3::Transform(SPHERE.VertexPositions[idx], TRS)); } } drawLineSet(storage, color, spherePoints.begin(), spherePoints.end()); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index 20ddcd42..f974ed9d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -123,8 +123,9 @@ namespace SHADE ///
/// Colour of the sphere. /// Position where the sphere wil be centered at. + /// Rotation of the sphere. /// Size of the rendered sphere. - void DrawSphere(const SHVec4& color, const SHVec3& pos, double radius); + void DrawSphere(const SHVec4& color, const SHVec3& pos, const SHVec3& rot, double radius); /*---------------------------------------------------------------------------------*/ /* Persistent Draw Functions */ @@ -246,7 +247,7 @@ namespace SHADE template void drawPoly(std::vector& storage, const SHVec4& color, IterType pointListBegin, IterType pointListEnd); void drawCube(std::vector& storage, const SHVec4& color, const SHVec3& pos, const SHVec3& size); - void drawSphere(std::vector& storage, const SHVec4& color, const SHVec3& pos, double radius); + void drawSphere(std::vector& storage, const SHVec4& color, const SHVec3& pos, const SHVec3& rot, double radius); }; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index c05baddd..6a08f940 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -111,6 +111,12 @@ namespace SHADE void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; + protected: /*---------------------------------------------------------------------------------*/ /* Data Members */ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp index 3f79b381..40671a96 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -13,6 +13,9 @@ // Primary Header #include "SHSphereCollisionShape.h" +// Project Headers +#include "Math/SHMatrix.h" + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -179,5 +182,17 @@ namespace SHADE return SHSphere::Raycast(ray); } + SHMatrix SHSphereCollisionShape::GetInertiaTensor(float mass) const noexcept + { + static constexpr float TWO_OVER_FIVE = 2.0f / 5.0f; + + const float DIAGONAL = TWO_OVER_FIVE * mass * (Radius * Radius); + + SHMatrix result; + result.m[0][0] = result.m[1][1] = result.m[2][2] = DIAGONAL; + return result; + } + + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 0a8c0321..72ea05f5 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -96,7 +96,7 @@ namespace SHADE * @return * True if the point is inside the sphere. */ - bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; /** * @brief @@ -107,9 +107,19 @@ namespace SHADE * An object holding the results of the raycast.
* See the corresponding header for the contents of the object. */ - SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; - // TODO: Compute Moment of Inertia + /** + * @brief + * Computes the inertia tensor of the sphere. + * @param mass + * The mass of the sphere. + * @return + * The inertia tensor of the sphere. + */ + [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; + + private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp index 91b5a688..cc014050 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp @@ -80,6 +80,15 @@ namespace SHADE position = newPosition; } + void SHMotionState::ForceOrientation(const SHQuaternion& newOrientation) noexcept + { + hasMoved = true; + + prevOrientation = newOrientation; + orientation = newOrientation; + } + + void SHMotionState::IntegratePosition(const SHVec3& velocity, float dt) noexcept { // Velocities are 0 when objects are static or sleeping. We do not want to integrate them here. @@ -91,10 +100,32 @@ namespace SHADE position += velocity * dt; } + void SHMotionState::IntegrateOrientation(const SHVec3& velocity, float dt) noexcept + { + // Velocities are 0 when objects are static or sleeping. We do not want to integrate them here. + // This call should never reach here. + + hasMoved = true; + + prevOrientation = orientation; + + SHQuaternion qv{ velocity.x * dt, velocity.y * dt, velocity.z * dt, 0.0f }; + qv *= orientation; + + orientation += qv * 0.5f; + orientation = SHQuaternion::Normalise(orientation); + } + + SHVec3 SHMotionState::InterpolatePositions(float factor) const noexcept { return SHVec3::ClampedLerp(prevPosition, position, factor); } + SHQuaternion SHMotionState::InterpolateOrientations(float factor) const noexcept + { + return SHQuaternion::ClampedSlerp(prevOrientation, orientation, factor); + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h index 4cff9a8f..778e6a8b 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h @@ -11,8 +11,10 @@ #pragma once // Project Headers +#include "Math/SHQuaternion.h" #include "Math/Vector/SHVec3.h" + namespace SHADE { /*-------------------------------------------------------------------------------------*/ @@ -30,10 +32,13 @@ namespace SHADE /* Data Members */ /*-----------------------------------------------------------------------------------*/ - bool hasMoved; + bool hasMoved; - SHVec3 position; - SHVec3 prevPosition; + SHVec3 position; + SHVec3 prevPosition; + + SHQuaternion orientation; + SHQuaternion prevOrientation; /*-----------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -65,17 +70,36 @@ namespace SHADE * @param newPosition * The new position to set. */ - void ForcePosition (const SHVec3& newPosition) noexcept; + void ForcePosition (const SHVec3& newPosition) noexcept; /** * @brief - * Integrates the positions using velocity with respect to time. + * Forcefully sets the orientation. Meant to be used when transform overrides the rigid body + * orientations + * @param newOrientation + * The new orientation to set. + */ + void ForceOrientation (const SHQuaternion& newOrientation) noexcept; + + /** + * @brief + * Integrates the positions using linear velocity with respect to time. * @param velocity - * The velocity to integrate. + * The linear velocity to integrate. * @param dt * The delta time to integrate with respect to. */ - void IntegratePosition (const SHVec3& velocity, float dt) noexcept; + void IntegratePosition (const SHVec3& velocity, float dt) noexcept; + + /** + * @brief + * Integrates the orientation using angular velocity with respect to time. + * @param velocity + * The angular velocity to integrate. + * @param dt + * The delta time to integrate with respect to. + */ + void IntegrateOrientation (const SHVec3& velocity, float dt) noexcept; /** * @brief @@ -85,7 +109,17 @@ namespace SHADE * @returns * The interpolated position meant for rendering. */ - SHVec3 InterpolatePositions (float factor) const noexcept; + SHVec3 InterpolatePositions (float factor) const noexcept; + + /** + * @brief + * Interpolates the orientation between the previous and the last using a given factor. + * @param factor + * The factor to interpolate by. Should be between 0 & 1. + * @returns + * The interpolated orientation meant for rendering. + */ + SHQuaternion InterpolateOrientations (float factor) const noexcept; }; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index 7422d6ff..b1b94792 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -74,7 +74,10 @@ namespace SHADE void SHPhysicsWorld::Step(float dt) { for (auto* rigidBody : rigidBodies) + { + rigidBody->ComputeWorldData(); integrateForces(*rigidBody, dt); + } for (auto* rigidBody : rigidBodies) integrateVelocities(*rigidBody, dt); @@ -93,10 +96,14 @@ namespace SHADE const SHVec3 LINEAR_ACCELERATION = rigidBody.accumulatedForce * rigidBody.invMass; const SHVec3 GRAVITATIONAL_ACCELERATION = rigidBody.IsGravityEnabled() ? settings.gravity * rigidBody.gravityScale : SHVec3::Zero; - rigidBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * dt; + rigidBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * dt; + + // Integrate torque into angular velocity + rigidBody.angularVelocity += rigidBody.worldInvInertia * (rigidBody.accumulatedTorque * dt); // Apply drag (exponentially applied) - rigidBody.linearVelocity *= 1.0f / (1.0f + dt * rigidBody.linearDrag); + rigidBody.linearVelocity *= 1.0f / (1.0f + dt * rigidBody.linearDrag); + rigidBody.angularVelocity *= 1.0f / (1.0f + dt * rigidBody.angularDrag); } void SHPhysicsWorld::integrateVelocities(SHRigidBody& rigidBody, float dt) const noexcept @@ -111,7 +118,13 @@ namespace SHADE , rigidBody.GetFreezePositionZ() ? 0.0f : rigidBody.linearVelocity.z }; - // TODO: Enforce angular constraints + // Enforce angular constraints + rigidBody.angularVelocity = SHVec3 + { + rigidBody.GetFreezeRotationX() ? 0.0f : rigidBody.angularVelocity.x + , rigidBody.GetFreezeRotationY() ? 0.0f : rigidBody.angularVelocity.y + , rigidBody.GetFreezeRotationZ() ? 0.0f : rigidBody.angularVelocity.z + }; }; // Always reset movement flag @@ -129,13 +142,13 @@ namespace SHADE ENFORCE_CONSTRAINED_VELOCITIES(rigidBody); rigidBody.motionState.IntegratePosition(rigidBody.linearVelocity, dt); - // TODO: Integrate orientations + rigidBody.motionState.IntegrateOrientation(rigidBody.angularVelocity, dt); // Sync with collider transforms if a collider is present if (rigidBody.collider) { rigidBody.collider->SetPosition(rigidBody.motionState.position); - // TODO: Sync orientations + rigidBody.collider->SetOrientation(rigidBody.motionState.orientation); rigidBody.collider->RecomputeShapes(); } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index e79a8c91..c9f3b097 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -27,9 +27,10 @@ namespace SHADE : entityID { eid } , collider { nullptr } , bodyType { type } + , gravityScale { 1.0f } , invMass { type == Type::DYNAMIC ? 1.0f : 0.0f } , linearDrag { 0.01f } - , gravityScale { 1.0f } + , angularDrag { 0.01f } , flags { 0U } { // Set default flags @@ -41,14 +42,19 @@ namespace SHADE } SHRigidBody::SHRigidBody(const SHRigidBody& rhs) noexcept - : entityID { rhs.entityID } - , collider { nullptr } - , bodyType { rhs.bodyType } - , invMass { rhs.invMass } - , linearDrag { rhs.linearDrag } - , gravityScale { rhs.gravityScale } - , flags { rhs.flags } - , motionState { rhs.motionState } + : entityID { rhs.entityID } + , collider { nullptr } + , bodyType { rhs.bodyType } + , gravityScale { rhs.gravityScale } + , invMass { rhs.invMass } + , linearDrag { rhs.linearDrag } + , angularDrag { rhs.angularDrag } + , localInvInertia { rhs.localInvInertia } + , worldInvInertia { rhs.worldInvInertia } + , localCentroid { rhs.localCentroid } + , worldCentroid { rhs.worldCentroid } + , flags { rhs.flags } + , motionState { rhs.motionState } { // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. } @@ -57,9 +63,14 @@ namespace SHADE : entityID { rhs.entityID } , collider { nullptr } , bodyType { rhs.bodyType } + , gravityScale { rhs.gravityScale } , invMass { rhs.invMass } , linearDrag { rhs.linearDrag } - , gravityScale { rhs.gravityScale } + , angularDrag { rhs.angularDrag } + , localInvInertia { rhs.localInvInertia } + , worldInvInertia { rhs.worldInvInertia } + , localCentroid { rhs.localCentroid } + , worldCentroid { rhs.worldCentroid } , flags { rhs.flags } , motionState { std::move(rhs.motionState) } { @@ -77,17 +88,24 @@ namespace SHADE return *this; entityID = rhs.entityID; + // Deep copy the collider - *collider = *rhs.collider; - bodyType = rhs.bodyType; - invMass = rhs.invMass; - linearDrag = rhs.linearDrag; - gravityScale = rhs.gravityScale; - flags = rhs.flags; - motionState = rhs.motionState; + *collider = *rhs.collider; + bodyType = rhs.bodyType; + gravityScale = rhs.gravityScale; + invMass = rhs.invMass; + linearDrag = rhs.linearDrag; + angularDrag = rhs.angularDrag; + localInvInertia = rhs.localInvInertia; + worldInvInertia = rhs.worldInvInertia; + localCentroid = rhs.localCentroid; + worldCentroid = rhs.worldCentroid; + flags = rhs.flags; + motionState = rhs.motionState; // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. accumulatedForce = SHVec3::Zero; + accumulatedTorque = SHVec3::Zero; linearVelocity = SHVec3::Zero; angularVelocity = SHVec3::Zero; @@ -97,17 +115,24 @@ namespace SHADE SHRigidBody& SHRigidBody::operator=(SHRigidBody&& rhs) noexcept { entityID = rhs.entityID; + // Deep copy the collider *collider = *rhs.collider; bodyType = rhs.bodyType; + gravityScale = rhs.gravityScale; invMass = rhs.invMass; linearDrag = rhs.linearDrag; - gravityScale = rhs.gravityScale; + angularDrag = rhs.angularDrag; + localInvInertia = rhs.localInvInertia; + worldInvInertia = rhs.worldInvInertia; + localCentroid = rhs.localCentroid; + worldCentroid = rhs.worldCentroid; flags = rhs.flags; motionState = std::move(rhs.motionState); // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. accumulatedForce = SHVec3::Zero; + accumulatedTorque = SHVec3::Zero; linearVelocity = SHVec3::Zero; angularVelocity = SHVec3::Zero; @@ -123,6 +148,11 @@ namespace SHADE return bodyType; } + float SHRigidBody::GetGravityScale() const noexcept + { + return gravityScale; + } + float SHRigidBody::GetMass() const noexcept { return 1.0f/ invMass; @@ -133,16 +163,41 @@ namespace SHADE return linearDrag; } - float SHRigidBody::GetGravityScale() const noexcept + float SHRigidBody::GetAngularDrag() const noexcept { - return gravityScale; + return angularDrag; } - const SHVec3& SHRigidBody::GetAccumulatedForce() const noexcept + const SHMatrix& SHRigidBody::GetLocalInvInertia() const noexcept + { + return localInvInertia; + } + + const SHMatrix& SHRigidBody::GetWorldInvInertia() const noexcept + { + return worldInvInertia; + } + + const SHVec3& SHRigidBody::GetLocalCentroid() const noexcept + { + return localCentroid; + } + + const SHVec3& SHRigidBody::GetWorldCentroid() const noexcept + { + return worldCentroid; + } + + const SHVec3& SHRigidBody::GetForce() const noexcept { return accumulatedForce; } + const SHVec3& SHRigidBody::GetTorque() const noexcept + { + return accumulatedTorque; + } + const SHVec3& SHRigidBody::GetLinearVelocity() const noexcept { return linearVelocity; @@ -226,7 +281,6 @@ namespace SHADE return motionState; } - /*-----------------------------------------------------------------------------------*/ /* Setter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -245,6 +299,11 @@ namespace SHADE invMass = newType == Type::DYNAMIC ? 1.0f : 0.0f; } + void SHRigidBody::SetGravityScale(float newGravityScale) noexcept + { + gravityScale = newGravityScale; + } + void SHRigidBody::SetMass(float newMass) noexcept { if (bodyType != Type::DYNAMIC) @@ -261,7 +320,7 @@ namespace SHADE invMass = 1.0f / newMass; - // TODO: Recompute inertia tensor + ComputeMassData(); } void SHRigidBody::SetLinearDrag(float newLinearDrag) noexcept @@ -281,16 +340,45 @@ namespace SHADE linearDrag = newLinearDrag; } - void SHRigidBody::SetGravityScale(float newGravityScale) noexcept + void SHRigidBody::SetAngularDrag(float newAngularDrag) noexcept { - gravityScale = newGravityScale; + if (bodyType == Type::STATIC) + { + SHLOG_WARNING("Cannot set angular drag of a Static Body {}", entityID) + return; + } + + if (newAngularDrag < 0.0f) + { + SHLOG_WARNING("Cannot set drag below 0. Object {}'s angular drag will remain unchanged.", entityID) + return; + } + + angularDrag = newAngularDrag; } void SHRigidBody::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept { + if (bodyType == Type::STATIC) + { + SHLOG_WARNING("Cannot set linear velocity of a Static Body {}", entityID) + return; + } + linearVelocity = newLinearVelocity; } + void SHRigidBody::SetAngularVelocity(const SHVec3& newAngularVelocity) noexcept + { + if (bodyType == Type::STATIC) + { + SHLOG_WARNING("Cannot set angular velocity of a Static Body {}", entityID) + return; + } + + angularVelocity = newAngularVelocity; + } + void SHRigidBody::SetIsActive(bool isActive) noexcept { static constexpr unsigned int FLAG_POS = 0; @@ -304,7 +392,18 @@ namespace SHADE static constexpr unsigned int FLAG_POS = 1; static constexpr uint16_t VALUE = 1U << FLAG_POS; - isSleeping ? flags |= VALUE : flags &= ~VALUE; + if (isSleeping) + { + flags |= VALUE; + + ClearForces(); + linearVelocity = SHVec3::Zero; + angularVelocity = SHVec3::Zero; + } + else + { + flags &= ~VALUE; + } } void SHRigidBody::SetSleepingEnabled(bool enableSleeping) noexcept @@ -411,6 +510,8 @@ namespace SHADE flags |= VALUE; // Reset angular velocity along X-axis angularVelocity.x = 0.0f; + // Set inertia tensor on the x-axis to 0 + localInvInertia.m[0][0] = worldInvInertia.m[0][0] = 0.0f; } else { @@ -428,6 +529,8 @@ namespace SHADE flags |= VALUE; // Reset angular velocity along Y-axis angularVelocity.y = 0.0f; + // Set inertia tensor on the y-axis to 0 + localInvInertia.m[1][1] = worldInvInertia.m[1][1] = 0.0f; } else { @@ -445,6 +548,8 @@ namespace SHADE flags |= VALUE; // Reset angular velocity along Z-axis angularVelocity.z = 0.0f; + // Set inertia tensor on the z-axis to 0 + localInvInertia.m[2][2] = worldInvInertia.m[2][2] = 0.0f; } else { @@ -461,21 +566,60 @@ namespace SHADE if (bodyType != Type::DYNAMIC) return; - accumulatedForce += force; - // Compute torque when force is offset + accumulatedForce += force; + accumulatedTorque += SHVec3::Cross(pos, force); } void SHRigidBody::AddImpulse(const SHVec3& impulse, const SHVec3& pos) noexcept { if (bodyType != Type::DYNAMIC) - return; + return; - linearVelocity += impulse * invMass; + linearVelocity += impulse * invMass; + angularVelocity += worldInvInertia * SHVec3::Cross(pos, impulse); + } + + void SHRigidBody::AddTorque(const SHVec3& torque) noexcept + { + if (bodyType != Type::DYNAMIC) + return; + + accumulatedTorque += torque; } void SHRigidBody::ClearForces() noexcept { - accumulatedForce = SHVec3::Zero; + accumulatedForce = SHVec3::Zero; + accumulatedTorque = SHVec3::Zero; + } + + void SHRigidBody::ComputeWorldData() noexcept + { + const SHMatrix ROTATION = SHMatrix::Rotate(motionState.orientation); + + // Compute world inertia + worldInvInertia = ROTATION * localInvInertia * SHMatrix::Transpose(ROTATION); + + // Compute world centroid + worldCentroid = (ROTATION * localCentroid) + motionState.position; + } + + void SHRigidBody::ComputeMassData() noexcept + { + // TODO: Compute total inertia and centroid from composited colliders using the Parallel Axis Theorem. + // TODO: If auto mass in enabled, compute total mass based from each collider. + // TODO: If auto mass disabled, compute inertia tensor for each shape using the ratio of its mass / total mass by comparing the volume / total volume. + + if (collider && !collider->GetCollisionShapes().empty()) + { + // HACK: For now, take only the first shape as we are testing with non-composited colliders. We are using the center as the centroid. + const auto* FIRST_SHAPE = collider->GetCollisionShape(0); + localInvInertia = SHMatrix::Inverse(FIRST_SHAPE->GetInertiaTensor(1.0f / invMass)); + } + else + { + localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass; + } } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index 1edf5cf6..0d1457d9 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -12,6 +12,7 @@ // Project Headers #include "ECS_Base/Entity/SHEntity.h" +#include "Math/SHMatrix.h" #include "Math/Vector/SHVec3.h" #include "SHMotionState.h" @@ -73,12 +74,20 @@ namespace SHADE [[nodiscard]] Type GetType () const noexcept; - [[nodiscard]] float GetMass () const noexcept; - [[nodiscard]] float GetLinearDrag () const noexcept; - [[nodiscard]] float GetGravityScale () const noexcept; - [[nodiscard]] const SHVec3& GetAccumulatedForce () const noexcept; + [[nodiscard]] float GetMass () const noexcept; + [[nodiscard]] float GetLinearDrag () const noexcept; + [[nodiscard]] float GetAngularDrag () const noexcept; + + [[nodiscard]] const SHMatrix& GetLocalInvInertia () const noexcept; + [[nodiscard]] const SHMatrix& GetWorldInvInertia () const noexcept; + + [[nodiscard]] const SHVec3& GetLocalCentroid () const noexcept; + [[nodiscard]] const SHVec3& GetWorldCentroid () const noexcept; + + [[nodiscard]] const SHVec3& GetForce () const noexcept; + [[nodiscard]] const SHVec3& GetTorque () const noexcept; [[nodiscard]] const SHVec3& GetLinearVelocity () const noexcept; [[nodiscard]] const SHVec3& GetAngularVelocity () const noexcept; @@ -111,6 +120,8 @@ namespace SHADE */ void SetType (Type newType) noexcept; + void SetGravityScale (float newGravityScale) noexcept; + /** * @brief * Mass is only modifiable for Dynamic bodies. @@ -127,9 +138,16 @@ namespace SHADE */ void SetLinearDrag (float newLinearDrag) noexcept; + /** + * @brief + * Drag is only modifiable for non-Static bodies. + * @param newAngularDrag + * The new drag to set. Values below 0 will be ignored. + */ + void SetAngularDrag (float newAngularDrag) noexcept; - void SetGravityScale (float newGravityScale) noexcept; void SetLinearVelocity (const SHVec3& newLinearVelocity) noexcept; + void SetAngularVelocity (const SHVec3& newAngularVelocity)noexcept; // Flags @@ -151,29 +169,55 @@ namespace SHADE /** * @brief - * Adds a force to the body with an offset from it's center of mass. + * Adds a force to the body with an offset from it's center of mass.
+ * Non-dynamic bodies will be ignored. * @param force * The force to add to the body. * @param pos * The position from the center of mass to offset the force. Defaults to zero. */ - void AddForce (const SHVec3& force, const SHVec3& pos = SHVec3::Zero) noexcept; + void AddForce (const SHVec3& force, const SHVec3& pos = SHVec3::Zero) noexcept; /** * @brief - * Adds an impulse to the body with an offset from it's center of mass. + * Adds an impulse to the body with an offset from it's center of mass.
+ * Non-dynamic bodies will be ignored. * @param impulse * The impulse to add to the body. * @param pos * The position from the center of mass to offset the impulse. Defaults to zero. */ - void AddImpulse (const SHVec3& impulse, const SHVec3& pos = SHVec3::Zero) noexcept; + void AddImpulse (const SHVec3& impulse, const SHVec3& pos = SHVec3::Zero) noexcept; + + /** + * @brief + * Adds torque to rotate the body about it's centroid.
+ * Non-dynamic bodies will be ignored. + * @param torque + * The torque to add to the body. + */ + void AddTorque (const SHVec3& torque) noexcept; /** * @brief * Removes all the forces from the body. */ - void ClearForces () noexcept; + void ClearForces () noexcept; + + /** + * @brief + * Computes the centroid and invInertia in world space. + */ + void ComputeWorldData () noexcept; + + /** + * @brief + * Computes the centroid and inertia of the object.
+ * If auto-mass is enabled, computes the mass.
+ * If auto-mass is disabled, the inertia is computed based on the ratio each shape's volume over the total volume. + * + */ + void ComputeMassData () noexcept; private: /*-----------------------------------------------------------------------------------*/ @@ -186,12 +230,20 @@ namespace SHADE Type bodyType; + float gravityScale; + float invMass; float linearDrag; + float angularDrag; - float gravityScale; + SHMatrix localInvInertia; + SHMatrix worldInvInertia; + + SHVec3 localCentroid; + SHVec3 worldCentroid; SHVec3 accumulatedForce; + SHVec3 accumulatedTorque; SHVec3 linearVelocity; SHVec3 angularVelocity; diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp index da848f09..1d1c06bf 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp @@ -36,18 +36,12 @@ namespace SHADE bool SHRigidBodyComponent::IsGravityEnabled() const noexcept { - if (rigidBody) - return rigidBody->IsGravityEnabled(); - - return false; + return rigidBody ? rigidBody->IsGravityEnabled() : false; } bool SHRigidBodyComponent::IsAllowedToSleep() const noexcept { - if (rigidBody) - return rigidBody->IsSleepingEnabled(); - - return false; + return rigidBody ? rigidBody->IsSleepingEnabled() : false; } bool SHRigidBodyComponent::IsInterpolating() const noexcept @@ -57,131 +51,93 @@ namespace SHADE bool SHRigidBodyComponent::IsSleeping() const noexcept { - if (rigidBody) - return rigidBody->IsSleeping(); - - return false; + return rigidBody ? rigidBody->IsSleeping() : false; } bool SHRigidBodyComponent::GetAutoMass() const noexcept { - if (rigidBody) - return rigidBody->IsAutoMassEnabled(); - - return false; + return rigidBody ? rigidBody->IsAutoMassEnabled() : false; } bool SHRigidBodyComponent::GetFreezePositionX() const noexcept { - if (rigidBody) - return rigidBody->GetFreezePositionX(); - - return false; + return rigidBody ? rigidBody->GetFreezePositionX() : false; } bool SHRigidBodyComponent::GetFreezePositionY() const noexcept { - if (rigidBody) - return rigidBody->GetFreezePositionY(); - - return false; + return rigidBody ? rigidBody->GetFreezePositionY() : false; } bool SHRigidBodyComponent::GetFreezePositionZ() const noexcept { - if (rigidBody) - return rigidBody->GetFreezePositionZ(); - - return false; + return rigidBody ? rigidBody->GetFreezePositionZ() : false; } bool SHRigidBodyComponent::GetFreezeRotationX() const noexcept { - if (rigidBody) - return rigidBody->GetFreezeRotationX(); - - return false; + return rigidBody ? rigidBody->GetFreezeRotationX() : false; } bool SHRigidBodyComponent::GetFreezeRotationY() const noexcept { - if (rigidBody) - return rigidBody->GetFreezeRotationY(); - - return false; + return rigidBody ? rigidBody->GetFreezeRotationY() : false; } bool SHRigidBodyComponent::GetFreezeRotationZ() const noexcept { - if (rigidBody) - return rigidBody->GetFreezeRotationZ(); - - return false; + return rigidBody ? rigidBody->GetFreezeRotationZ() : false; } float SHRigidBodyComponent::GetGravityScale() const noexcept { - if (rigidBody) - return rigidBody->GetGravityScale(); - - return 0.0f; + return rigidBody ? rigidBody->GetGravityScale() : 0.0f; } float SHRigidBodyComponent::GetMass() const noexcept { - if (rigidBody) - return rigidBody->GetMass(); - - return -1.0f; + return rigidBody ? rigidBody->GetMass() : -1.0f; } float SHRigidBodyComponent::GetDrag() const noexcept { - if (rigidBody) - return rigidBody->GetLinearDrag(); - - return -1.0f; + return rigidBody ? rigidBody->GetLinearDrag() : -1.0f; } float SHRigidBodyComponent::GetAngularDrag() const noexcept { - return 0.0f; + return rigidBody ? rigidBody->GetAngularDrag() : -1.0f; } SHVec3 SHRigidBodyComponent::GetForce() const noexcept { - if (rigidBody) - return rigidBody->GetAccumulatedForce(); - - return SHVec3::Zero; + return rigidBody ? rigidBody->GetForce() : SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetTorque() const noexcept { - return SHVec3::Zero; + return rigidBody ? rigidBody->GetTorque() : SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetLinearVelocity() const noexcept { - if (rigidBody) - return rigidBody->GetLinearVelocity(); - - return SHVec3::Zero; + return rigidBody ? rigidBody->GetLinearVelocity() : SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetAngularVelocity() const noexcept { - return SHVec3::Zero; + return rigidBody ? rigidBody->GetAngularVelocity() : SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetPosition() const noexcept { - if (rigidBody) - return rigidBody->GetMotionState().position; - - return SHVec3::Zero; + return rigidBody ? rigidBody->GetMotionState().position : SHVec3::Zero; } + SHVec3 SHRigidBodyComponent::GetRotation() const noexcept + { + return rigidBody ? rigidBody->GetMotionState().orientation.ToEuler() : SHVec3::Zero; + } /*-----------------------------------------------------------------------------------*/ /* Setter Functions Definitions */ @@ -282,7 +238,8 @@ namespace SHADE void SHRigidBodyComponent::SetAngularDrag(float newAngularDrag) noexcept { - + if (rigidBody) + rigidBody->SetAngularDrag(newAngularDrag); } void SHRigidBodyComponent::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept @@ -293,7 +250,8 @@ namespace SHADE void SHRigidBodyComponent::SetAngularVelocity(const SHVec3& newAngularVelocity) noexcept { - + if (rigidBody) + rigidBody->SetAngularVelocity(newAngularVelocity); } /*-----------------------------------------------------------------------------------*/ @@ -308,38 +266,67 @@ namespace SHADE void SHRigidBodyComponent::AddForceAtLocalPos(const SHVec3& force, const SHVec3& localPos) const noexcept { - + if (rigidBody) + rigidBody->AddForce(force, localPos); } void SHRigidBodyComponent::AddForceAtWorldPos(const SHVec3& force, const SHVec3& worldPos) const noexcept { if (rigidBody) - rigidBody->AddForce(force, worldPos); + { + // Convert world pos into local space of the body + const SHVec3 LOCAL_POS = worldPos - rigidBody->GetWorldCentroid(); + rigidBody->AddForce(force, LOCAL_POS); + } } void SHRigidBodyComponent::AddRelativeForce(const SHVec3& relativeForce) const noexcept { - + if (rigidBody) + { + // Rotate force into world space + const SHVec3 FORCE = SHVec3::Rotate(relativeForce, rigidBody->GetMotionState().orientation); + rigidBody->AddForce(FORCE); + } } void SHRigidBodyComponent::AddRelativeForceAtLocalPos(const SHVec3& relativeForce, const SHVec3& localPos) const noexcept { - + if (rigidBody) + { + // Rotate force into world space + const SHVec3 FORCE = SHVec3::Rotate(relativeForce, rigidBody->GetMotionState().orientation); + rigidBody->AddForce(FORCE, localPos); + } } void SHRigidBodyComponent::AddRelativeForceAtWorldPos(const SHVec3& relativeForce, const SHVec3& worldPos) const noexcept { - + if (rigidBody) + { + // Rotate force into world space + const SHVec3 FORCE = SHVec3::Rotate(relativeForce, rigidBody->GetMotionState().orientation); + // Convert world pos into local space of the body + const SHVec3 LOCAL_POS = worldPos - rigidBody->GetWorldCentroid(); + + rigidBody->AddForce(FORCE, LOCAL_POS); + } } void SHRigidBodyComponent::AddTorque(const SHVec3& torque) const noexcept { - + if (rigidBody) + rigidBody->AddTorque(torque); } void SHRigidBodyComponent::AddRelativeTorque(const SHVec3& relativeTorque) const noexcept { - + if (rigidBody) + { + // Rotate force into world space + const SHVec3 TORQUE = SHVec3::Rotate(relativeTorque, rigidBody->GetMotionState().orientation); + rigidBody->AddTorque(TORQUE); + } } void SHRigidBodyComponent::ClearForces() const noexcept @@ -348,11 +335,6 @@ namespace SHADE rigidBody->ClearForces(); } - void SHRigidBodyComponent::ClearTorque() const noexcept - { - - } - /*-----------------------------------------------------------------------------------*/ /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h index 7d377184..d215c3f8 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h @@ -93,6 +93,7 @@ namespace SHADE [[nodiscard]] SHVec3 GetAngularVelocity () const noexcept; [[nodiscard]] SHVec3 GetPosition () const noexcept; + [[nodiscard]] SHVec3 GetRotation () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ @@ -138,7 +139,6 @@ namespace SHADE void AddRelativeTorque (const SHVec3& relativeTorque) const noexcept; void ClearForces () const noexcept; - void ClearTorque () const noexcept; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp index 8e7450ce..200642e1 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp @@ -62,15 +62,23 @@ namespace SHADE if (!MOTION_STATE) continue; - SHVec3 renderPosition = rigidBodyComponent.IsInterpolating() ? MOTION_STATE.InterpolatePositions(FACTOR) : MOTION_STATE.position; + if (rigidBodyComponent.IsInterpolating()) + { + const SHVec3 RENDER_POSITION = MOTION_STATE.InterpolatePositions(FACTOR); + const SHQuaternion RENDER_ORIENTATION = MOTION_STATE.InterpolateOrientations(FACTOR); + + transformComponent->SetWorldPosition(RENDER_POSITION); + transformComponent->SetWorldOrientation(RENDER_ORIENTATION); + } + else + { + transformComponent->SetWorldPosition(MOTION_STATE.position); + transformComponent->SetWorldOrientation(MOTION_STATE.orientation); + } /* * TODO: Test if the scene graph transforms abides by setting world position. Collisions will ignore the scene graph hierarchy. */ - - - transformComponent->SetWorldPosition(renderPosition); - // TODO: SetOrientation } // Collision & Trigger messages diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index c03571db..a26a414d 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -54,7 +54,7 @@ namespace SHADE SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition()); - // TODO: Force Orientation + motionState.ForceOrientation(TRANSFORM_COMPONENT->GetWorldOrientation()); } if (physicsObject.collider) diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index 77241ae9..62654dbb 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -120,7 +120,10 @@ namespace SHADE case SHCollisionShape::Type::SPHERE: { const SHSphereCollisionShape* SPHERE = dynamic_cast(SHAPE); - drawSphere(debugDrawSystem, *SPHERE); + + // Compute rotation of sphere + const SHVec3 ROTATION = collider.GetOrientation().ToEuler() + SPHERE->GetRotationOffset(); + drawSphere(debugDrawSystem, *SPHERE, ROTATION); break; } @@ -137,10 +140,10 @@ namespace SHADE } } - void SHPhysicsDebugDrawSystem::drawSphere(SHDebugDrawSystem* debugDrawSystem, const SHSphereCollisionShape& sphere) noexcept + void SHPhysicsDebugDrawSystem::drawSphere(SHDebugDrawSystem* debugDrawSystem, const SHSphereCollisionShape& sphere, const SHVec3& rotation) noexcept { const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(sphere.IsTrigger() ? Colours::TRIGGER : Colours::COLLIDER)]; - debugDrawSystem->DrawSphere(DRAW_COLOUR, sphere.GetCenter(), sphere.GetWorldRadius()); + debugDrawSystem->DrawSphere(DRAW_COLOUR, sphere.GetCenter(), rotation, sphere.GetWorldRadius()); } void SHPhysicsDebugDrawSystem::drawContact(SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Contact& contactInfo) noexcept diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index 423ab62a..1eb4e967 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -138,8 +138,8 @@ namespace SHADE SHEventHandle onColliderDraw(SHEventPtr onColliderDrawEvent); - static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; - static void drawSphere (SHDebugDrawSystem* debugDrawSystem, const SHSphereCollisionShape& sphere) noexcept; + static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; + static void drawSphere (SHDebugDrawSystem* debugDrawSystem, const SHSphereCollisionShape& sphere, const SHVec3& rotation) noexcept; static void drawContact (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Contact& contactInfo) noexcept; static void drawRaycast (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept; diff --git a/SHADE_Engine/src/Tools/SHDebugDraw.cpp b/SHADE_Engine/src/Tools/SHDebugDraw.cpp index b8aa8b0e..e8c8af9d 100644 --- a/SHADE_Engine/src/Tools/SHDebugDraw.cpp +++ b/SHADE_Engine/src/Tools/SHDebugDraw.cpp @@ -68,7 +68,7 @@ namespace SHADE void SHDebugDraw::Sphere(const SHVec4& color, const SHVec3& pos, double radius) { - dbgDrawSys->DrawSphere(color, pos, radius); + dbgDrawSys->DrawSphere(color, pos, SHVec3::Zero, radius); } void SHDebugDraw::PersistentLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) diff --git a/SHADE_Managed/src/Components/RigidBody.cxx b/SHADE_Managed/src/Components/RigidBody.cxx index 7b3604f2..6237f3a9 100644 --- a/SHADE_Managed/src/Components/RigidBody.cxx +++ b/SHADE_Managed/src/Components/RigidBody.cxx @@ -217,10 +217,4 @@ namespace SHADE { return Convert::ToCLI(GetNativeComponent()->GetTorque()); } - - void RigidBody::ClearTorque() - { - GetNativeComponent()->ClearTorque(); - } - } \ No newline at end of file diff --git a/SHADE_Managed/src/Components/RigidBody.hxx b/SHADE_Managed/src/Components/RigidBody.hxx index 8bfe34aa..8293cca4 100644 --- a/SHADE_Managed/src/Components/RigidBody.hxx +++ b/SHADE_Managed/src/Components/RigidBody.hxx @@ -155,7 +155,6 @@ namespace SHADE void AddRelativeTorque(Vector3 relativeForce); Vector3 GetTorque(); - void ClearTorque(); }; } \ No newline at end of file From 27c7a1739776e2de7a135e145220f42bd11b1a35 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 15 Dec 2022 22:59:55 +0800 Subject: [PATCH 018/164] Fixed computation of global inverse inertia tensor --- SHADE_Engine/src/Physics/Collision/SHCollider.cpp | 7 +++++++ SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index b64bab53..d7ca4b14 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -17,6 +17,7 @@ #include "Events/SHEvent.h" #include "Math/SHMathHelpers.h" #include "Physics/SHPhysicsEvents.h" +#include "Physics/Dynamics/SHRigidBody.h" namespace SHADE @@ -277,6 +278,9 @@ namespace SHADE SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT); + if (rigidBody) + rigidBody->ComputeMassData(); + return static_cast(shapes.size()); } @@ -316,6 +320,9 @@ namespace SHADE // Broadcast Event for removing a shape SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT); + if (rigidBody) + rigidBody->ComputeMassData(); + SHLOG_INFO_D("Removing Collision Shape {} from Entity {}", index, entityID) } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index c9f3b097..00df0640 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -598,7 +598,7 @@ namespace SHADE const SHMatrix ROTATION = SHMatrix::Rotate(motionState.orientation); // Compute world inertia - worldInvInertia = ROTATION * localInvInertia * SHMatrix::Transpose(ROTATION); + worldInvInertia = SHMatrix::Transpose(ROTATION) * localInvInertia * ROTATION; // Compute world centroid worldCentroid = (ROTATION * localCentroid) + motionState.position; From 27760a95c98fa3a0a4e0574377f61af0f9483ca5 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 15 Dec 2022 23:00:15 +0800 Subject: [PATCH 019/164] Tested physics interactions with scripts --- Assets/Scenes/PhysicsSandbox.shade | 12 +- Assets/Scripts/Tests/PhysicsTestObj.cs | 112 ++++++++++++++++++ Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta | 3 + 3 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 Assets/Scripts/Tests/PhysicsTestObj.cs create mode 100644 Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 0005b35e..0952fd3c 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -5,7 +5,7 @@ Components: Transform Component: Translate: {x: 0, y: 1.77475965, z: 0} - Rotate: {x: -0, y: 0, z: -0} + Rotate: {x: 0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: @@ -13,8 +13,8 @@ Auto Mass: false Mass: 1 Drag: 1 - Angular Drag: 0 - Use Gravity: true + Angular Drag: 1 + Use Gravity: false Gravity Scale: 1 Interpolate: true Sleeping Enabled: true @@ -37,7 +37,11 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true - Scripts: ~ + Scripts: + - Type: PhysicsTestObj + Enabled: true + forceAmount: 50 + torqueAmount: 500 - EID: 1 Name: Default IsActive: true diff --git a/Assets/Scripts/Tests/PhysicsTestObj.cs b/Assets/Scripts/Tests/PhysicsTestObj.cs new file mode 100644 index 00000000..c36537b3 --- /dev/null +++ b/Assets/Scripts/Tests/PhysicsTestObj.cs @@ -0,0 +1,112 @@ +using SHADE; +using System; +using System.Collections.Generic; +using static Item; + + +public class PhysicsTestObj : Script +{ + public RigidBody rb { get; set; } + + // Movement input booleans + public enum Direction + { + UP, + DOWN, + FORWARD, + BACK, + LEFT, + RIGHT + }; + + internal bool[] move = new bool[6]; + internal bool[] rotate = new bool[6]; + + internal Vector3[] moveVec = new Vector3[6] + { + Vector3.Up, + Vector3.Down, + Vector3.Back, + Vector3.Forward, + Vector3.Left, + Vector3.Right + }; + + internal Vector3[] rotateVec = new Vector3[6] + { + Vector3.Right, + Vector3.Left, + Vector3.Forward, + Vector3.Down, + Vector3.Up, + Vector3.Down + }; + + internal Input.KeyCode[] moveInputKeys = new Input.KeyCode[6] + { + Input.KeyCode.Space, + Input.KeyCode.LeftControl, + Input.KeyCode.W, + Input.KeyCode.S, + Input.KeyCode.A, + Input.KeyCode.D + }; + + internal Input.KeyCode[] rotateInputKeys = new Input.KeyCode[6] + { + Input.KeyCode.I, + Input.KeyCode.K, + Input.KeyCode.U, + Input.KeyCode.O, + Input.KeyCode.J, + Input.KeyCode.L + }; + + public float forceAmount = 50.0f; + public float torqueAmount = 500.0f; + + protected override void awake() + { + rb = GetComponent(); + + for (int i = 0; i < 6; ++i) + { + move[i] = false; + rotate[i] = false; + } + } + + protected override void update() + { + for (int i = 0; i < 6; ++i) + { + if (Input.GetKeyDown(moveInputKeys[i])) + move[i] = true; + + //if (Input.GetKeyDown(rotateInputKeys[i])) + // rotate[i] = true; + } + } + + protected override void fixedUpdate() + { + for (int i = 0; i < 6; ++i) + { + bool shouldMove = move[i]; + bool shouldRotate = rotate[i]; + + if (shouldMove) + { + Vector3 offset = new Vector3(0.25f, 0.0f, 0.0f); + rb.AddForceAtLocalPos(moveVec[i] * forceAmount, offset); + move[i] = false; + } + + if (shouldRotate) + { + rb.AddTorque(rotateVec[i] * torqueAmount); + rotate[i] = false; + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta b/Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta new file mode 100644 index 00000000..8c096651 --- /dev/null +++ b/Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta @@ -0,0 +1,3 @@ +Name: PhysicsTestObj +ID: 159293012 +Type: 9 From 6b8232ae9150bdd25cff47165c8531f5498e3728 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 16 Dec 2022 02:02:20 +0800 Subject: [PATCH 020/164] Fixed bug where intertia tensors were not reset when unlocking axes --- Assets/Scenes/PhysicsSandbox.shade | 2 +- Assets/Scripts/Tests/PhysicsTestObj.cs | 9 ++-- .../src/Physics/Dynamics/SHRigidBody.cpp | 54 ++++++++++++++++--- .../src/Physics/Dynamics/SHRigidBody.h | 10 ++++ .../PhysicsObject/SHPhysicsObject.cpp | 10 ++++ 5 files changed, 74 insertions(+), 11 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 0952fd3c..7381b70c 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -11,7 +11,7 @@ RigidBody Component: Type: Dynamic Auto Mass: false - Mass: 1 + Mass: 10 Drag: 1 Angular Drag: 1 Use Gravity: false diff --git a/Assets/Scripts/Tests/PhysicsTestObj.cs b/Assets/Scripts/Tests/PhysicsTestObj.cs index c36537b3..20437eb3 100644 --- a/Assets/Scripts/Tests/PhysicsTestObj.cs +++ b/Assets/Scripts/Tests/PhysicsTestObj.cs @@ -83,8 +83,8 @@ public class PhysicsTestObj : Script if (Input.GetKeyDown(moveInputKeys[i])) move[i] = true; - //if (Input.GetKeyDown(rotateInputKeys[i])) - // rotate[i] = true; + if (Input.GetKeyDown(rotateInputKeys[i])) + rotate[i] = true; } } @@ -97,8 +97,9 @@ public class PhysicsTestObj : Script if (shouldMove) { - Vector3 offset = new Vector3(0.25f, 0.0f, 0.0f); - rb.AddForceAtLocalPos(moveVec[i] * forceAmount, offset); + //Vector3 offset = new Vector3(0.25f, 0.0f, 0.0f); + //rb.AddForceAtLocalPos(moveVec[i] * forceAmount, offset); + rb.AddForce(moveVec[i] * forceAmount); move[i] = false; } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index 00df0640..aa8027c7 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -511,11 +511,12 @@ namespace SHADE // Reset angular velocity along X-axis angularVelocity.x = 0.0f; // Set inertia tensor on the x-axis to 0 - localInvInertia.m[0][0] = worldInvInertia.m[0][0] = 0.0f; + localInvInertia.m[0][0] = 0.0f; } else { flags &= ~VALUE; + computeInertiaTensor(); } } @@ -530,11 +531,12 @@ namespace SHADE // Reset angular velocity along Y-axis angularVelocity.y = 0.0f; // Set inertia tensor on the y-axis to 0 - localInvInertia.m[1][1] = worldInvInertia.m[1][1] = 0.0f; + localInvInertia.m[1][1] = 0.0f; } else { flags &= ~VALUE; + computeInertiaTensor(); } } @@ -549,11 +551,12 @@ namespace SHADE // Reset angular velocity along Z-axis angularVelocity.z = 0.0f; // Set inertia tensor on the z-axis to 0 - localInvInertia.m[2][2] = worldInvInertia.m[2][2] = 0.0f; + localInvInertia.m[2][2] = 0.0f; } else { flags &= ~VALUE; + computeInertiaTensor(); } } @@ -606,20 +609,59 @@ namespace SHADE void SHRigidBody::ComputeMassData() noexcept { - // TODO: Compute total inertia and centroid from composited colliders using the Parallel Axis Theorem. + computeCentroid(); + computeMassAndInertiaTensor(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Function Definition */ + /*-----------------------------------------------------------------------------------*/ + + void SHRigidBody::computeCentroid() noexcept + { + // TODO + } + + void SHRigidBody::computeMass() noexcept + { // TODO: If auto mass in enabled, compute total mass based from each collider. // TODO: If auto mass disabled, compute inertia tensor for each shape using the ratio of its mass / total mass by comparing the volume / total volume. + } - if (collider && !collider->GetCollisionShapes().empty()) + void SHRigidBody::computeInertiaTensor() noexcept + { + // TODO: Compute total inertia from composited colliders using the Parallel Axis Theorem. + + if (!collider || collider->GetCollisionShapes().empty()) + { + localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass; + } + else { // HACK: For now, take only the first shape as we are testing with non-composited colliders. We are using the center as the centroid. const auto* FIRST_SHAPE = collider->GetCollisionShape(0); localInvInertia = SHMatrix::Inverse(FIRST_SHAPE->GetInertiaTensor(1.0f / invMass)); } - else + } + + void SHRigidBody::computeMassAndInertiaTensor() noexcept + { + // TODO: If auto mass in enabled, compute total mass based from each collider. + // TODO: If auto mass disabled, compute inertia tensor for each shape using the ratio of its mass / total mass by comparing the volume / total volume. + + // TODO: Compute total inertia from composited colliders using the Parallel Axis Theorem. + + if (!collider || collider->GetCollisionShapes().empty()) { localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass; } + else + { + // HACK: For now, take only the first shape as we are testing with non-composited colliders. We are using the center as the centroid. + const auto* FIRST_SHAPE = collider->GetCollisionShape(0); + localInvInertia = SHMatrix::Inverse(FIRST_SHAPE->GetInertiaTensor(1.0f / invMass)); + } + } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index 0d1457d9..ab30bbcf 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -252,6 +252,16 @@ namespace SHADE uint16_t flags; SHMotionState motionState; + + /*-----------------------------------------------------------------------------------*/ + /* Member Functions */ + /*-----------------------------------------------------------------------------------*/ + + void computeCentroid () noexcept; + void computeMass () noexcept; + void computeInertiaTensor () noexcept; + void computeMassAndInertiaTensor() noexcept; + }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp index ee33016a..56c2ac7b 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp @@ -92,8 +92,12 @@ namespace SHADE // Link with collider if it exists if (collider) + { collider->SetRigidBody(rigidBody); + rigidBody->SetCollider(collider); + } + rigidBody->ComputeMassData(); return rigidBody; } @@ -119,7 +123,10 @@ namespace SHADE // Link with rigidBody if it exists if (rigidBody) + { rigidBody->SetCollider(collider); + collider->SetRigidBody(rigidBody); + } return collider; } @@ -131,7 +138,10 @@ namespace SHADE // Unlink with rigid body if (rigidBody) + { rigidBody->SetCollider(nullptr); + rigidBody->ComputeMassData(); + } } /*-----------------------------------------------------------------------------------*/ From ddf2d8bde9b424d0f140b1c1fbf3eb4815a930fc Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 16 Dec 2022 14:38:01 +0800 Subject: [PATCH 021/164] Fixed warnings for subscript operator in vectors and removed react conversions --- SHADE_Engine/src/Math/SHColour.cpp | 8 ++++++++ SHADE_Engine/src/Math/SHQuaternion.cpp | 20 ------------------- SHADE_Engine/src/Math/SHQuaternion.h | 8 -------- SHADE_Engine/src/Math/SHRay.cpp | 12 ------------ SHADE_Engine/src/Math/SHRay.h | 6 ------ SHADE_Engine/src/Math/Vector/SHVec2.cpp | 17 ++++++++-------- SHADE_Engine/src/Math/Vector/SHVec2.h | 6 ------ SHADE_Engine/src/Math/Vector/SHVec3.cpp | 26 ++++++++----------------- SHADE_Engine/src/Math/Vector/SHVec3.h | 7 ------- SHADE_Engine/src/Math/Vector/SHVec4.cpp | 8 ++++++++ 10 files changed, 32 insertions(+), 86 deletions(-) diff --git a/SHADE_Engine/src/Math/SHColour.cpp b/SHADE_Engine/src/Math/SHColour.cpp index fc2f2a08..7c372376 100644 --- a/SHADE_Engine/src/Math/SHColour.cpp +++ b/SHADE_Engine/src/Math/SHColour.cpp @@ -260,6 +260,8 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; + // This will never hit + default: return x; } } @@ -274,6 +276,8 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; + // This will never hit + default: return x; } } @@ -288,6 +292,8 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; + // This will never hit + default: return x; } } @@ -302,6 +308,8 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; + // This will never hit + default: return x; } } diff --git a/SHADE_Engine/src/Math/SHQuaternion.cpp b/SHADE_Engine/src/Math/SHQuaternion.cpp index 8904cb05..021f0a6a 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.cpp +++ b/SHADE_Engine/src/Math/SHQuaternion.cpp @@ -44,16 +44,6 @@ namespace SHADE : XMFLOAT4( _x, _y, _z, _w ) {} - SHQuaternion::SHQuaternion(const reactphysics3d::Vector3& rp3dEuler) noexcept - : XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) - { - XMStoreFloat4(this, XMQuaternionRotationRollPitchYawFromVector(SHVec3 { rp3dEuler })); - } - - SHQuaternion::SHQuaternion(const reactphysics3d::Quaternion& rp3dQuat) noexcept - : XMFLOAT4( rp3dQuat.x, rp3dQuat.y, rp3dQuat.z, rp3dQuat.w ) - {} - /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -141,16 +131,6 @@ namespace SHADE return XMQuaternionNotEqual(*this, rhs); } - SHQuaternion::operator reactphysics3d::Quaternion() const noexcept - { - return reactphysics3d::Quaternion{ x, y, z, w }; - } - - SHQuaternion::operator reactphysics3d::Vector3() const noexcept - { - return reactphysics3d::Vector3{ ToEuler() }; - } - SHQuaternion::operator XMVECTOR() const noexcept { return XMLoadFloat4(this); diff --git a/SHADE_Engine/src/Math/SHQuaternion.h b/SHADE_Engine/src/Math/SHQuaternion.h index fa5b5d36..93c546ca 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.h +++ b/SHADE_Engine/src/Math/SHQuaternion.h @@ -11,7 +11,6 @@ #pragma once #include -#include #include @@ -52,11 +51,6 @@ namespace SHADE SHQuaternion (const SHVec4& vec4) noexcept; SHQuaternion (float x, float y, float z, float w) noexcept; - // Conversion from other math types - - SHQuaternion (const reactphysics3d::Vector3& rp3dEuler) noexcept; - SHQuaternion (const reactphysics3d::Quaternion& rp3dQuat) noexcept; - /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ @@ -82,8 +76,6 @@ namespace SHADE // Conversion to other math types used by SHADE - operator reactphysics3d::Quaternion () const noexcept; - operator reactphysics3d::Vector3 () const noexcept; operator DirectX::XMVECTOR () const noexcept; /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/SHRay.cpp b/SHADE_Engine/src/Math/SHRay.cpp index c4931aba..622087c6 100644 --- a/SHADE_Engine/src/Math/SHRay.cpp +++ b/SHADE_Engine/src/Math/SHRay.cpp @@ -30,12 +30,6 @@ namespace SHADE , direction { dir } {} - SHRay::SHRay(const reactphysics3d::Ray rp3dRay) noexcept - : position { rp3dRay.point1 } - , direction { SHVec3::Normalise(rp3dRay.point2 - rp3dRay.point1) } - {} - - /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -62,12 +56,6 @@ namespace SHADE return XMVector3NotEqual(LHS_POS, RHS_POS) || XMVector3NotEqual(LHS_DIR, RHS_DIR); } - SHRay::operator reactphysics3d::Ray() const noexcept - { - // We use 2km. Temp solution. - return reactphysics3d::Ray{ position, position + (direction * MAX_RAYCAST_DIST) }; - } - SHRaycastResult::operator bool() const noexcept { return hit; diff --git a/SHADE_Engine/src/Math/SHRay.h b/SHADE_Engine/src/Math/SHRay.h index 18efc224..56599018 100644 --- a/SHADE_Engine/src/Math/SHRay.h +++ b/SHADE_Engine/src/Math/SHRay.h @@ -10,10 +10,7 @@ #pragma once -#include - // Project Headers -#include "SH_API.h" #include "Vector/SHVec3.h" namespace SHADE @@ -40,7 +37,6 @@ namespace SHADE SHRay () noexcept; SHRay (const SHVec3& pos, const SHVec3& dir) noexcept; - SHRay (const reactphysics3d::Ray rp3dRay) noexcept; SHRay (const SHRay&) noexcept = default; SHRay (SHRay&& ) noexcept = default; @@ -55,8 +51,6 @@ namespace SHADE [[nodiscard]] bool operator==(const SHRay& rhs) const noexcept; [[nodiscard]] bool operator!=(const SHRay& rhs) const noexcept; - - operator reactphysics3d::Ray() const noexcept; }; struct SH_API SHRaycastResult diff --git a/SHADE_Engine/src/Math/Vector/SHVec2.cpp b/SHADE_Engine/src/Math/Vector/SHVec2.cpp index 9573be01..da0215f4 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec2.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec2.cpp @@ -50,10 +50,6 @@ namespace SHADE : XMFLOAT2( _x, _y ) {} - SHVec2::SHVec2(const reactphysics3d::Vector2& rp3dVec2) noexcept - : XMFLOAT2( rp3dVec2.x, rp3dVec2.y ) - {} - /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -165,6 +161,8 @@ namespace SHADE { case 0: return x; case 1: return y; + // This will never hit + default: return x; } } @@ -177,6 +175,8 @@ namespace SHADE { case 0: return x; case 1: return y; + // This will never hit + default: return x; } } @@ -189,6 +189,8 @@ namespace SHADE { case 0: return x; case 1: return y; + // This will never hit + default: return x; } } @@ -201,14 +203,11 @@ namespace SHADE { case 0: return x; case 1: return y; + // This will never hit + default: return x; } } - SHVec2::operator reactphysics3d::Vector2() const noexcept - { - return reactphysics3d::Vector2{ x, y }; - } - SHVec2 operator* (float lhs, const SHVec2& rhs) noexcept { SHVec2 result; diff --git a/SHADE_Engine/src/Math/Vector/SHVec2.h b/SHADE_Engine/src/Math/Vector/SHVec2.h index e780d3ac..78c1e6e8 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec2.h +++ b/SHADE_Engine/src/Math/Vector/SHVec2.h @@ -11,7 +11,6 @@ #pragma once #include -#include #include #include @@ -59,10 +58,6 @@ namespace SHADE SHVec2 (float n) noexcept; SHVec2 (float x, float y) noexcept; - // Conversion from other math types to SHADE - - SHVec2 (const reactphysics3d::Vector2& rp3dVec2) noexcept; - /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ @@ -73,7 +68,6 @@ namespace SHADE // Conversion to other math types used by SHADE operator DirectX::XMVECTOR () const noexcept; - operator reactphysics3d::Vector2 () const noexcept; SHVec2& operator+= (const SHVec2& rhs) noexcept; SHVec2& operator-= (const SHVec2& rhs) noexcept; diff --git a/SHADE_Engine/src/Math/Vector/SHVec3.cpp b/SHADE_Engine/src/Math/Vector/SHVec3.cpp index 4b77636a..6b042c61 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec3.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec3.cpp @@ -57,14 +57,6 @@ namespace SHADE : XMFLOAT3( _x, _y, _z ) {} - SHVec3::SHVec3(const reactphysics3d::Vector3& rp3dVec3) noexcept - : XMFLOAT3( rp3dVec3.x, rp3dVec3.y, rp3dVec3.z ) - {} - - SHVec3::SHVec3(const reactphysics3d::Quaternion& rp3dVec3) noexcept - : XMFLOAT3( SHQuaternion{rp3dVec3}.ToEuler() ) - {} - /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -179,6 +171,8 @@ namespace SHADE case 0: return x; case 1: return y; case 2: return z; + // This will never hit + default: return x; } } @@ -192,6 +186,8 @@ namespace SHADE case 0: return x; case 1: return y; case 2: return z; + // This will never hit + default: return x; } } @@ -205,6 +201,8 @@ namespace SHADE case 0: return x; case 1: return y; case 2: return z; + // This will never hit + default: return x; } } @@ -218,19 +216,11 @@ namespace SHADE case 0: return x; case 1: return y; case 2: return z; + // This will never hit + default: return x; } } - SHVec3::operator reactphysics3d::Vector3() const noexcept - { - return reactphysics3d::Vector3{ x, y , z }; - } - - SHVec3::operator reactphysics3d::Quaternion() const noexcept - { - return reactphysics3d::Quaternion::fromEulerAngles(x, y, z); - } - SHVec3 operator* (float lhs, const SHVec3& rhs) noexcept { SHVec3 result; diff --git a/SHADE_Engine/src/Math/Vector/SHVec3.h b/SHADE_Engine/src/Math/Vector/SHVec3.h index de37d6a5..657e167e 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec3.h +++ b/SHADE_Engine/src/Math/Vector/SHVec3.h @@ -11,8 +11,6 @@ #pragma once #include -#include -#include #include #include @@ -69,9 +67,6 @@ namespace SHADE // Conversion from other math types to SHADE - SHVec3 (const reactphysics3d::Vector3& rp3dVec3) noexcept; - SHVec3 (const reactphysics3d::Quaternion& rp3dVec3) noexcept; - /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ @@ -81,8 +76,6 @@ namespace SHADE // Conversion to other math types used by SHADE - operator reactphysics3d::Vector3 () const noexcept; - operator reactphysics3d::Quaternion () const noexcept; operator DirectX::XMVECTOR () const noexcept; SHVec3& operator+= (const SHVec3& rhs) noexcept; diff --git a/SHADE_Engine/src/Math/Vector/SHVec4.cpp b/SHADE_Engine/src/Math/Vector/SHVec4.cpp index c6f01d9e..c164e7f4 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec4.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec4.cpp @@ -164,6 +164,8 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; + // This will never hit + default: return x; } } @@ -178,6 +180,8 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; + // This will never hit + default: return x; } } @@ -192,6 +196,8 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; + // This will never hit + default: return x; } } @@ -206,6 +212,8 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; + // This will never hit + default: return x; } } From 1b91f60c4ae3619feb75833d3e047eb09e5fedca Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 16 Dec 2022 14:38:22 +0800 Subject: [PATCH 022/164] Fixed warning with wrongly declared friends --- SHADE_Engine/src/Physics/Interface/SHColliderComponent.h | 2 +- SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h index d62b9b90..d0cc7064 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h @@ -30,7 +30,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ friend class SHPhysicsSystem; - friend class SHPhysicsObject; + friend struct SHPhysicsObject; public: diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h index d215c3f8..1173c1d5 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h @@ -31,7 +31,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ friend class SHPhysicsSystem; - friend class SHPhysicsObject; + friend struct SHPhysicsObject; public: /*---------------------------------------------------------------------------------*/ From 2bd90e7c143ddd7a665c4e5d5f58f07d16203c11 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 16 Dec 2022 14:38:46 +0800 Subject: [PATCH 023/164] Adjusted physics debug draw to fit new debug draw interface --- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 5 +++-- .../System/SHPhysicsDebugDrawSystem.cpp | 19 +++++++++++-------- .../Physics/System/SHPhysicsDebugDrawSystem.h | 4 +--- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index a26a414d..88ea8d79 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -36,12 +36,13 @@ namespace SHADE auto* physicsSystem = reinterpret_cast(GetSystem()); // Get all physics objects & sync transforms - for (auto& [entityID, physicsObject] : physicsSystem->physicsObjectManager.GetPhysicsObjects()) + auto& physicsObjects = physicsSystem->physicsObjectManager.GetPhysicsObjects(); + for (auto& [entityID, physicsObject] : physicsObjects) { const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); if (!TRANSFORM_COMPONENT || !TRANSFORM_COMPONENT->HasChanged()) - return; + continue; const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation(); diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index 62654dbb..dda437bc 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -93,6 +93,7 @@ namespace SHADE { const auto& EVENT_DATA = reinterpret_cast*>(onColliderDrawEvent.get())->data; + // Add to the container to draw all colliders if (EVENT_DATA->debugDrawState) { if (collidersToDraw.empty()) @@ -115,15 +116,23 @@ namespace SHADE { for (const auto* SHAPE : collider.GetCollisionShapes()) { + const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(SHAPE->IsTrigger() ? Colours::TRIGGER : Colours::COLLIDER)]; + switch (SHAPE->GetType()) { case SHCollisionShape::Type::SPHERE: { const SHSphereCollisionShape* SPHERE = dynamic_cast(SHAPE); - // Compute rotation of sphere + // Compute transforms of sphere + const SHVec3 POSITION = SPHERE->GetCenter(); // Position offset is already computed here const SHVec3 ROTATION = collider.GetOrientation().ToEuler() + SPHERE->GetRotationOffset(); - drawSphere(debugDrawSystem, *SPHERE, ROTATION); + const SHVec3 SCALE { SPHERE->GetWorldRadius() }; + + // Compute TRS for the sphere + const SHMatrix TRS = SHMatrix::Transform(POSITION, ROTATION, SCALE); + + debugDrawSystem->DrawWireSphere(TRS, DRAW_COLOUR, true); break; } @@ -140,12 +149,6 @@ namespace SHADE } } - void SHPhysicsDebugDrawSystem::drawSphere(SHDebugDrawSystem* debugDrawSystem, const SHSphereCollisionShape& sphere, const SHVec3& rotation) noexcept - { - const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(sphere.IsTrigger() ? Colours::TRIGGER : Colours::COLLIDER)]; - debugDrawSystem->DrawSphere(DRAW_COLOUR, sphere.GetCenter(), rotation, sphere.GetWorldRadius()); - } - void SHPhysicsDebugDrawSystem::drawContact(SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Contact& contactInfo) noexcept { static const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::CONTACT)]; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index 1eb4e967..79b88d3f 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -138,9 +138,7 @@ namespace SHADE SHEventHandle onColliderDraw(SHEventPtr onColliderDrawEvent); - static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; - static void drawSphere (SHDebugDrawSystem* debugDrawSystem, const SHSphereCollisionShape& sphere, const SHVec3& rotation) noexcept; - + static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; static void drawContact (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Contact& contactInfo) noexcept; static void drawRaycast (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept; From a6e1064e64dcc8515a76c445e95f9349d6e9f706 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 16 Dec 2022 15:03:55 +0800 Subject: [PATCH 024/164] Fixed bug where collider offsets were not recomputed --- .../CollisionShapes/SHCollisionShape.cpp | 31 ++++++++ .../CollisionShapes/SHCollisionShape.h | 35 ++++++--- .../CollisionShapes/SHSphereCollisionShape.h | 4 +- .../src/Physics/Collision/SHCollider.cpp | 78 ++++++++++++++----- .../src/Physics/Collision/SHCollider.h | 10 ++- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 15 ++-- 6 files changed, 135 insertions(+), 38 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp index 2f4e3819..d0706ed0 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -95,6 +95,15 @@ namespace SHADE return flags & FLAG_VALUE; } + bool SHCollisionShape::IsDirty() const noexcept + { + static constexpr int FLAG_POS = 6; + static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; + + return flags & FLAG_VALUE; + } + + const SHCollisionTag& SHCollisionShape::GetCollisionTag() const noexcept { return *collisionTag; @@ -116,21 +125,25 @@ namespace SHADE void SHCollisionShape::SetDensity(float density) noexcept { + setDirty(true); material.SetDensity(density); } void SHCollisionShape::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept { + setDirty(true); material = newMaterial; } void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept { + setDirty(true); positionOffset = posOffset; } void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept { + setDirty(true); rotationOffset = rotOffset; } @@ -147,10 +160,28 @@ namespace SHADE collisionTag = newCollisionTag; } + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionShape::ClearDirty() noexcept + { + setDirty(false); + } + /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ + void SHCollisionShape::setDirty(bool isDirty) noexcept + { + static constexpr int FLAG_POS = 6; + static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; + + isDirty ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; + } + + } // namespace SHADE RTTR_REGISTRATION diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 6a08f940..90020727 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -91,6 +91,7 @@ namespace SHADE [[nodiscard]] Type GetType () const noexcept; [[nodiscard]] bool IsTrigger () const noexcept; [[nodiscard]] bool IsColliding () const noexcept; + [[nodiscard]] bool IsDirty () const noexcept; [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; @@ -98,24 +99,31 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetFriction (float friction) noexcept; - void SetBounciness (float bounciness) noexcept; - void SetDensity (float density) noexcept; - void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; + void SetFriction (float friction) noexcept; + void SetBounciness (float bounciness) noexcept; + void SetDensity (float density) noexcept; + void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; - void SetPositionOffset (const SHVec3& posOffset) noexcept; - void SetRotationOffset (const SHVec3& rotOffset) noexcept; + void SetPositionOffset (const SHVec3& posOffset) noexcept; + void SetRotationOffset (const SHVec3& rotOffset) noexcept; // Flags - void SetIsTrigger (bool isTrigger) noexcept; + + void SetIsTrigger (bool isTrigger) noexcept; - void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; + void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; + /** + * @brief + * Clears the dirty flag of the collider. + */ + void ClearDirty () noexcept; + + [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ @@ -129,10 +137,17 @@ namespace SHADE SHVec3 positionOffset; SHVec3 rotationOffset; - uint8_t flags; // 0 0 wasColliding isColliding trigger capsule sphere box + uint8_t flags; // 0 dirty wasColliding isColliding trigger capsule sphere box SHCollisionTag* collisionTag; RTTR_ENABLE() + + private: + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + void setDirty (bool isDirty) noexcept; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 72ea05f5..8276578e 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -37,8 +37,8 @@ namespace SHADE * @brief * Encapsulate a Sphere Collision Shape used for Physics Simulations. */ - class SH_API SHSphereCollisionShape : public SHCollisionShape - , private SHSphere + class SH_API SHSphereCollisionShape final : public SHCollisionShape + , private SHSphere { private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index d7ca4b14..9aedef16 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -30,6 +30,7 @@ namespace SHADE : entityID { eid } , shapeIDCounter { 0 } , debugDraw { false } + , dirty { true } , rigidBody { nullptr } , shapeFactory { nullptr } , transform { worldTransform } @@ -41,6 +42,7 @@ namespace SHADE : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } , debugDraw { rhs.debugDraw } + , dirty { rhs.dirty } , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -58,6 +60,7 @@ namespace SHADE : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } , debugDraw { rhs.debugDraw } + , dirty { rhs.dirty } , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -100,6 +103,7 @@ namespace SHADE entityID = rhs.entityID; debugDraw = rhs.debugDraw; + dirty = rhs.dirty; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; transform = rhs.transform; @@ -119,6 +123,7 @@ namespace SHADE entityID = rhs.entityID; debugDraw = rhs.debugDraw; + dirty = rhs.dirty; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; transform = rhs.transform; @@ -201,21 +206,25 @@ namespace SHADE void SHCollider::SetTransform(const SHTransform& newTransform) noexcept { + dirty = true; transform = newTransform; } void SHCollider::SetPosition(const SHVec3& newPosition) noexcept { + dirty = true; transform.position = newPosition; } void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept { + dirty = true; transform.orientation = newOrientation; } void SHCollider::SetScale(const SHVec3& newScale) noexcept { + dirty = true; transform.scale = newScale; } @@ -284,6 +293,28 @@ namespace SHADE return static_cast(shapes.size()); } + void SHCollider::Update() + { + // Forcefully recompute everything since the transform was changed + if (dirty) + { + RecomputeShapes(); + dirty = false; + return; + } + + // Recompute any shapes set to dirty + for (auto* shape : shapes) + { + if (shape->IsDirty()) + { + recomputeShape(shape); + shape->ClearDirty(); + } + } + } + + void SHCollider::RemoveCollisionShape(int index) { if (!shapeFactory) @@ -329,25 +360,7 @@ namespace SHADE void SHCollider::RecomputeShapes() noexcept { for (auto* shape : shapes) - { - switch (shape->GetType()) - { - case SHCollisionShape::Type::SPHERE: - { - recomputeSphere(dynamic_cast(shape)); - break; - } - case SHCollisionShape::Type::BOX: - { - break; - } - case SHCollisionShape::Type::CAPSULE: - { - break; - } - default: break; - } - } + recomputeShape(shape); } /*-----------------------------------------------------------------------------------*/ @@ -403,6 +416,33 @@ namespace SHADE ++shapeIDCounter; } + void SHCollider::recomputeShape(SHCollisionShape* shape) noexcept + { + if (!shape) + { + SHLOGV_ERROR_D("Shape missing from Collider {}", entityID) + return; + } + + switch (shape->GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + recomputeSphere(dynamic_cast(shape)); + break; + } + case SHCollisionShape::Type::BOX: + { + break; + } + case SHCollisionShape::Type::CAPSULE: + { + break; + } + default: break; + } + } + void SHCollider::recomputeSphere(SHSphereCollisionShape* sphere) noexcept { // Recompute world radius diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index 927c6155..8a183dcc 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -132,6 +132,12 @@ namespace SHADE // TODO: Add Box & Capsule + /** + * @brief + * Recomputes any shapes that were set to dirty. + */ + void Update (); + /** * @brief * Removes a shape from the container. Removal reduces the size of the container. @@ -158,6 +164,7 @@ namespace SHADE uint32_t shapeIDCounter; // This increments everytime a shape is added to differentiate shapes. bool debugDraw; + bool dirty; SHRigidBody* rigidBody; SHCollisionShapeFactory* shapeFactory; @@ -173,7 +180,8 @@ namespace SHADE void copyShapes (const SHCollider& rhsCollider); void copyShape (const SHCollisionShape* rhsShape); - void recomputeSphere (SHSphereCollisionShape* sphere) noexcept; + void recomputeShape (SHCollisionShape* shape) noexcept; + void recomputeSphere (SHSphereCollisionShape* sphere) noexcept; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index 88ea8d79..0094153e 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -41,7 +41,7 @@ namespace SHADE { const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); - if (!TRANSFORM_COMPONENT || !TRANSFORM_COMPONENT->HasChanged()) + if (!TRANSFORM_COMPONENT) continue; const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); @@ -50,7 +50,7 @@ namespace SHADE // We assume that all engine components and physics object components have been successfully linked - if (physicsObject.rigidBody) + if (physicsObject.rigidBody && TRANSFORM_COMPONENT->HasChanged()) { SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); @@ -60,11 +60,14 @@ namespace SHADE if (physicsObject.collider) { - physicsObject.collider->SetPosition(WORLD_POS); - physicsObject.collider->SetOrientation(WORLD_ROT); - physicsObject.collider->SetScale(WORLD_SCL); + if (TRANSFORM_COMPONENT->HasChanged()) + { + physicsObject.collider->SetPosition(WORLD_POS); + physicsObject.collider->SetOrientation(WORLD_ROT); + physicsObject.collider->SetScale(WORLD_SCL); + } - physicsObject.collider->RecomputeShapes(); + physicsObject.collider->Update(); } } } From 6a20e93704b2eda4d680f913cb6d430d87081e1b Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 16 Dec 2022 18:34:29 +0800 Subject: [PATCH 025/164] Refactored colliders to use a parent-child transform logic --- .../CollisionShapes/SHCollisionShape.cpp | 56 ++++-------- .../CollisionShapes/SHCollisionShape.h | 30 +++---- .../SHSphereCollisionShape.cpp | 37 +++++++- .../CollisionShapes/SHSphereCollisionShape.h | 14 ++- .../src/Physics/Collision/SHCollider.cpp | 85 ++----------------- .../src/Physics/Collision/SHCollider.h | 10 --- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 15 ++-- .../System/SHPhysicsDebugDrawSystem.cpp | 12 +-- 8 files changed, 82 insertions(+), 177 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp index d0706ed0..5dd17d35 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -25,9 +25,10 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHCollisionShape::SHCollisionShape(SHCollisionShapeID id, Type colliderType) - : id { id } - , flags { 0 } - , collisionTag { SHCollisionTagMatrix::GetTag(0) } + : id { id } + , flags { 0 } + , parentTransform { nullptr } + , collisionTag { SHCollisionTagMatrix::GetTag(0) } { flags |= 1U << SHUtilities::ConvertEnum(colliderType); } @@ -58,7 +59,7 @@ namespace SHADE const SHVec3& SHCollisionShape::GetPositionOffset() const noexcept { - return positionOffset; + return transform.position; } const SHVec3& SHCollisionShape::GetRotationOffset() const noexcept @@ -95,15 +96,6 @@ namespace SHADE return flags & FLAG_VALUE; } - bool SHCollisionShape::IsDirty() const noexcept - { - static constexpr int FLAG_POS = 6; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - return flags & FLAG_VALUE; - } - - const SHCollisionTag& SHCollisionShape::GetCollisionTag() const noexcept { return *collisionTag; @@ -125,26 +117,32 @@ namespace SHADE void SHCollisionShape::SetDensity(float density) noexcept { - setDirty(true); material.SetDensity(density); } void SHCollisionShape::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept { - setDirty(true); material = newMaterial; } + void SHCollisionShape::SetParentTransform(SHTransform& parentTF) noexcept + { + parentTransform = &parentTF; + } + void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept { - setDirty(true); - positionOffset = posOffset; + transform.position = posOffset; + + ComputeTransforms(); } void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept { - setDirty(true); rotationOffset = rotOffset; + transform.orientation = SHQuaternion::FromEuler(rotationOffset); + + ComputeTransforms(); } void SHCollisionShape::SetIsTrigger(bool isTrigger) noexcept @@ -160,28 +158,6 @@ namespace SHADE collisionTag = newCollisionTag; } - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionShape::ClearDirty() noexcept - { - setDirty(false); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionShape::setDirty(bool isDirty) noexcept - { - static constexpr int FLAG_POS = 6; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - isDirty ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; - } - - } // namespace SHADE RTTR_REGISTRATION diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 90020727..3c08e21a 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -14,11 +14,10 @@ // Project Headers #include "ECS_Base/Entity/SHEntity.h" -#include "Math/Geometry/SHShape.h" -#include "Math/SHQuaternion.h" #include "Physics/Collision/CollisionTags/SHCollisionTags.h" #include "Physics/Collision/SHPhysicsMaterial.h" #include "SHCollisionShapeID.h" +#include "Math/Transform/SHTransform.h" namespace SHADE { @@ -91,7 +90,6 @@ namespace SHADE [[nodiscard]] Type GetType () const noexcept; [[nodiscard]] bool IsTrigger () const noexcept; [[nodiscard]] bool IsColliding () const noexcept; - [[nodiscard]] bool IsDirty () const noexcept; [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; @@ -104,6 +102,8 @@ namespace SHADE void SetDensity (float density) noexcept; void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; + void SetParentTransform (SHTransform& parentTF) noexcept; + void SetPositionOffset (const SHVec3& posOffset) noexcept; void SetRotationOffset (const SHVec3& rotOffset) noexcept; @@ -117,13 +117,9 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Clears the dirty flag of the collider. - */ - void ClearDirty () noexcept; - - [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; + virtual void ComputeTransforms () noexcept = 0; + [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; + [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ @@ -134,20 +130,16 @@ namespace SHADE SHPhysicsMaterial material; - SHVec3 positionOffset; + SHTransform* parentTransform; + SHTransform transform; + + // Needed for conversion to euler angles SHVec3 rotationOffset; - uint8_t flags; // 0 dirty wasColliding isColliding trigger capsule sphere box + uint8_t flags; // 0 0 wasColliding isColliding trigger capsule sphere box SHCollisionTag* collisionTag; RTTR_ENABLE() - - private: - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - void setDirty (bool isDirty) noexcept; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp index 40671a96..e4bf68c3 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -14,6 +14,7 @@ #include "SHSphereCollisionShape.h" // Project Headers +#include "Math/SHMathHelpers.h" #include "Math/SHMatrix.h" namespace SHADE @@ -37,7 +38,8 @@ namespace SHADE { material = rhs.material; - positionOffset = rhs.positionOffset; + parentTransform = rhs.parentTransform; + transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. @@ -52,7 +54,8 @@ namespace SHADE { material = rhs.material; - positionOffset = rhs.positionOffset; + parentTransform = rhs.parentTransform; + transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. @@ -72,7 +75,8 @@ namespace SHADE id = rhs.id; material = rhs.material; - positionOffset = rhs.positionOffset; + parentTransform = rhs.parentTransform; + transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. @@ -97,7 +101,8 @@ namespace SHADE id = rhs.id; material = rhs.material; - positionOffset = rhs.positionOffset; + parentTransform = rhs.parentTransform; + transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. @@ -172,6 +177,18 @@ namespace SHADE /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ + void SHSphereCollisionShape::ComputeTransforms() noexcept + { + const float SPHERE_SCALE = std::fabs(SHMath::Max({ parentTransform->scale.x, parentTransform->scale.y, parentTransform->scale.z })); + SetScale(SPHERE_SCALE); + + // Recompute center + const SHQuaternion FINAL_ROT = parentTransform->orientation * transform.orientation; + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(parentTransform->position); + + Center = SHVec3::Transform(transform.position, TRS); + } + bool SHSphereCollisionShape::TestPoint(const SHVec3& point) const noexcept { return SHSphere::TestPoint(point); @@ -193,6 +210,18 @@ namespace SHADE return result; } + SHMatrix SHSphereCollisionShape::ComputeWorldTransform() const noexcept + { + const SHQuaternion ROTATION = parentTransform->orientation * transform.orientation; + const SHVec3 SCALE{ Radius }; + + return SHMatrix::Transform + ( + Center + , ROTATION + , SCALE +); + } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 8276578e..798c70b6 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -88,6 +88,12 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * Recomputes the transform of this sphere. + */ + void ComputeTransforms () noexcept override; + /** * @brief * Tests if a point is inside the sphere. @@ -96,7 +102,7 @@ namespace SHADE * @return * True if the point is inside the sphere. */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; /** * @brief @@ -107,7 +113,7 @@ namespace SHADE * An object holding the results of the raycast.
* See the corresponding header for the contents of the object. */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; /** * @brief @@ -117,9 +123,9 @@ namespace SHADE * @return * The inertia tensor of the sphere. */ - [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; + [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; - + [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index 9aedef16..a6527e48 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -30,19 +30,15 @@ namespace SHADE : entityID { eid } , shapeIDCounter { 0 } , debugDraw { false } - , dirty { true } , rigidBody { nullptr } , shapeFactory { nullptr } , transform { worldTransform } - { - - } + {} SHCollider::SHCollider(const SHCollider& rhs) noexcept : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } , debugDraw { rhs.debugDraw } - , dirty { rhs.dirty } , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -60,7 +56,6 @@ namespace SHADE : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } , debugDraw { rhs.debugDraw } - , dirty { rhs.dirty } , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -103,7 +98,6 @@ namespace SHADE entityID = rhs.entityID; debugDraw = rhs.debugDraw; - dirty = rhs.dirty; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; transform = rhs.transform; @@ -123,7 +117,6 @@ namespace SHADE entityID = rhs.entityID; debugDraw = rhs.debugDraw; - dirty = rhs.dirty; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; transform = rhs.transform; @@ -206,25 +199,21 @@ namespace SHADE void SHCollider::SetTransform(const SHTransform& newTransform) noexcept { - dirty = true; transform = newTransform; } void SHCollider::SetPosition(const SHVec3& newPosition) noexcept { - dirty = true; transform.position = newPosition; } void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept { - dirty = true; transform.orientation = newOrientation; } void SHCollider::SetScale(const SHVec3& newScale) noexcept { - dirty = true; transform.scale = newScale; } @@ -272,8 +261,9 @@ namespace SHADE ++shapeIDCounter; // Set offsets - sphere->positionOffset = posOffset; - sphere->rotationOffset = rotOffset; + sphere->SetParentTransform(transform); + sphere->SetPositionOffset(posOffset); + sphere->SetRotationOffset(rotOffset); shapes.emplace_back(sphere); @@ -293,28 +283,6 @@ namespace SHADE return static_cast(shapes.size()); } - void SHCollider::Update() - { - // Forcefully recompute everything since the transform was changed - if (dirty) - { - RecomputeShapes(); - dirty = false; - return; - } - - // Recompute any shapes set to dirty - for (auto* shape : shapes) - { - if (shape->IsDirty()) - { - recomputeShape(shape); - shape->ClearDirty(); - } - } - } - - void SHCollider::RemoveCollisionShape(int index) { if (!shapeFactory) @@ -360,7 +328,7 @@ namespace SHADE void SHCollider::RecomputeShapes() noexcept { for (auto* shape : shapes) - recomputeShape(shape); + shape->ComputeTransforms(); } /*-----------------------------------------------------------------------------------*/ @@ -416,47 +384,4 @@ namespace SHADE ++shapeIDCounter; } - void SHCollider::recomputeShape(SHCollisionShape* shape) noexcept - { - if (!shape) - { - SHLOGV_ERROR_D("Shape missing from Collider {}", entityID) - return; - } - - switch (shape->GetType()) - { - case SHCollisionShape::Type::SPHERE: - { - recomputeSphere(dynamic_cast(shape)); - break; - } - case SHCollisionShape::Type::BOX: - { - break; - } - case SHCollisionShape::Type::CAPSULE: - { - break; - } - default: break; - } - } - - void SHCollider::recomputeSphere(SHSphereCollisionShape* sphere) noexcept - { - // Recompute world radius - const float SPHERE_SCALE = std::fabs(SHMath::Max({ transform.scale.x, transform.scale.y, transform.scale.z })); - sphere->SetScale(SPHERE_SCALE); - - // Recompute center - const SHQuaternion FINAL_ROT = transform.orientation * SHQuaternion::FromEuler(sphere->rotationOffset); - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(transform.position); - - const SHVec3 NEW_CENTER = SHVec3::Transform(sphere->positionOffset, TRS); - - sphere->SetCenter(NEW_CENTER); - } - - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index 8a183dcc..566bae3e 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -132,12 +132,6 @@ namespace SHADE // TODO: Add Box & Capsule - /** - * @brief - * Recomputes any shapes that were set to dirty. - */ - void Update (); - /** * @brief * Removes a shape from the container. Removal reduces the size of the container. @@ -164,7 +158,6 @@ namespace SHADE uint32_t shapeIDCounter; // This increments everytime a shape is added to differentiate shapes. bool debugDraw; - bool dirty; SHRigidBody* rigidBody; SHCollisionShapeFactory* shapeFactory; @@ -179,9 +172,6 @@ namespace SHADE void copyShapes (const SHCollider& rhsCollider); void copyShape (const SHCollisionShape* rhsShape); - - void recomputeShape (SHCollisionShape* shape) noexcept; - void recomputeSphere (SHSphereCollisionShape* sphere) noexcept; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index 0094153e..88ea8d79 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -41,7 +41,7 @@ namespace SHADE { const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); - if (!TRANSFORM_COMPONENT) + if (!TRANSFORM_COMPONENT || !TRANSFORM_COMPONENT->HasChanged()) continue; const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); @@ -50,7 +50,7 @@ namespace SHADE // We assume that all engine components and physics object components have been successfully linked - if (physicsObject.rigidBody && TRANSFORM_COMPONENT->HasChanged()) + if (physicsObject.rigidBody) { SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); @@ -60,14 +60,11 @@ namespace SHADE if (physicsObject.collider) { - if (TRANSFORM_COMPONENT->HasChanged()) - { - physicsObject.collider->SetPosition(WORLD_POS); - physicsObject.collider->SetOrientation(WORLD_ROT); - physicsObject.collider->SetScale(WORLD_SCL); - } + physicsObject.collider->SetPosition(WORLD_POS); + physicsObject.collider->SetOrientation(WORLD_ROT); + physicsObject.collider->SetScale(WORLD_SCL); - physicsObject.collider->Update(); + physicsObject.collider->RecomputeShapes(); } } } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index dda437bc..958ac61a 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -122,17 +122,7 @@ namespace SHADE { case SHCollisionShape::Type::SPHERE: { - const SHSphereCollisionShape* SPHERE = dynamic_cast(SHAPE); - - // Compute transforms of sphere - const SHVec3 POSITION = SPHERE->GetCenter(); // Position offset is already computed here - const SHVec3 ROTATION = collider.GetOrientation().ToEuler() + SPHERE->GetRotationOffset(); - const SHVec3 SCALE { SPHERE->GetWorldRadius() }; - - // Compute TRS for the sphere - const SHMatrix TRS = SHMatrix::Transform(POSITION, ROTATION, SCALE); - - debugDrawSystem->DrawWireSphere(TRS, DRAW_COLOUR, true); + debugDrawSystem->DrawWireSphere(SHAPE->ComputeWorldTransform(), DRAW_COLOUR, true); break; } From 24b13ed6e42815573900870a65906a61afa975d3 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 16 Dec 2022 18:34:29 +0800 Subject: [PATCH 026/164] Refactored colliders to use parent-child transform logic --- .../CollisionShapes/SHCollisionShape.cpp | 56 ++++-------- .../CollisionShapes/SHCollisionShape.h | 30 +++---- .../SHSphereCollisionShape.cpp | 37 +++++++- .../CollisionShapes/SHSphereCollisionShape.h | 14 ++- .../src/Physics/Collision/SHCollider.cpp | 85 ++----------------- .../src/Physics/Collision/SHCollider.h | 10 --- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 15 ++-- .../System/SHPhysicsDebugDrawSystem.cpp | 12 +-- 8 files changed, 82 insertions(+), 177 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp index d0706ed0..5dd17d35 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -25,9 +25,10 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHCollisionShape::SHCollisionShape(SHCollisionShapeID id, Type colliderType) - : id { id } - , flags { 0 } - , collisionTag { SHCollisionTagMatrix::GetTag(0) } + : id { id } + , flags { 0 } + , parentTransform { nullptr } + , collisionTag { SHCollisionTagMatrix::GetTag(0) } { flags |= 1U << SHUtilities::ConvertEnum(colliderType); } @@ -58,7 +59,7 @@ namespace SHADE const SHVec3& SHCollisionShape::GetPositionOffset() const noexcept { - return positionOffset; + return transform.position; } const SHVec3& SHCollisionShape::GetRotationOffset() const noexcept @@ -95,15 +96,6 @@ namespace SHADE return flags & FLAG_VALUE; } - bool SHCollisionShape::IsDirty() const noexcept - { - static constexpr int FLAG_POS = 6; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - return flags & FLAG_VALUE; - } - - const SHCollisionTag& SHCollisionShape::GetCollisionTag() const noexcept { return *collisionTag; @@ -125,26 +117,32 @@ namespace SHADE void SHCollisionShape::SetDensity(float density) noexcept { - setDirty(true); material.SetDensity(density); } void SHCollisionShape::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept { - setDirty(true); material = newMaterial; } + void SHCollisionShape::SetParentTransform(SHTransform& parentTF) noexcept + { + parentTransform = &parentTF; + } + void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept { - setDirty(true); - positionOffset = posOffset; + transform.position = posOffset; + + ComputeTransforms(); } void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept { - setDirty(true); rotationOffset = rotOffset; + transform.orientation = SHQuaternion::FromEuler(rotationOffset); + + ComputeTransforms(); } void SHCollisionShape::SetIsTrigger(bool isTrigger) noexcept @@ -160,28 +158,6 @@ namespace SHADE collisionTag = newCollisionTag; } - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionShape::ClearDirty() noexcept - { - setDirty(false); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionShape::setDirty(bool isDirty) noexcept - { - static constexpr int FLAG_POS = 6; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - isDirty ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; - } - - } // namespace SHADE RTTR_REGISTRATION diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 90020727..3c08e21a 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -14,11 +14,10 @@ // Project Headers #include "ECS_Base/Entity/SHEntity.h" -#include "Math/Geometry/SHShape.h" -#include "Math/SHQuaternion.h" #include "Physics/Collision/CollisionTags/SHCollisionTags.h" #include "Physics/Collision/SHPhysicsMaterial.h" #include "SHCollisionShapeID.h" +#include "Math/Transform/SHTransform.h" namespace SHADE { @@ -91,7 +90,6 @@ namespace SHADE [[nodiscard]] Type GetType () const noexcept; [[nodiscard]] bool IsTrigger () const noexcept; [[nodiscard]] bool IsColliding () const noexcept; - [[nodiscard]] bool IsDirty () const noexcept; [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; @@ -104,6 +102,8 @@ namespace SHADE void SetDensity (float density) noexcept; void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; + void SetParentTransform (SHTransform& parentTF) noexcept; + void SetPositionOffset (const SHVec3& posOffset) noexcept; void SetRotationOffset (const SHVec3& rotOffset) noexcept; @@ -117,13 +117,9 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Clears the dirty flag of the collider. - */ - void ClearDirty () noexcept; - - [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; + virtual void ComputeTransforms () noexcept = 0; + [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; + [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ @@ -134,20 +130,16 @@ namespace SHADE SHPhysicsMaterial material; - SHVec3 positionOffset; + SHTransform* parentTransform; + SHTransform transform; + + // Needed for conversion to euler angles SHVec3 rotationOffset; - uint8_t flags; // 0 dirty wasColliding isColliding trigger capsule sphere box + uint8_t flags; // 0 0 wasColliding isColliding trigger capsule sphere box SHCollisionTag* collisionTag; RTTR_ENABLE() - - private: - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - void setDirty (bool isDirty) noexcept; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp index 40671a96..e4bf68c3 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -14,6 +14,7 @@ #include "SHSphereCollisionShape.h" // Project Headers +#include "Math/SHMathHelpers.h" #include "Math/SHMatrix.h" namespace SHADE @@ -37,7 +38,8 @@ namespace SHADE { material = rhs.material; - positionOffset = rhs.positionOffset; + parentTransform = rhs.parentTransform; + transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. @@ -52,7 +54,8 @@ namespace SHADE { material = rhs.material; - positionOffset = rhs.positionOffset; + parentTransform = rhs.parentTransform; + transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. @@ -72,7 +75,8 @@ namespace SHADE id = rhs.id; material = rhs.material; - positionOffset = rhs.positionOffset; + parentTransform = rhs.parentTransform; + transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. @@ -97,7 +101,8 @@ namespace SHADE id = rhs.id; material = rhs.material; - positionOffset = rhs.positionOffset; + parentTransform = rhs.parentTransform; + transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. @@ -172,6 +177,18 @@ namespace SHADE /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ + void SHSphereCollisionShape::ComputeTransforms() noexcept + { + const float SPHERE_SCALE = std::fabs(SHMath::Max({ parentTransform->scale.x, parentTransform->scale.y, parentTransform->scale.z })); + SetScale(SPHERE_SCALE); + + // Recompute center + const SHQuaternion FINAL_ROT = parentTransform->orientation * transform.orientation; + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(parentTransform->position); + + Center = SHVec3::Transform(transform.position, TRS); + } + bool SHSphereCollisionShape::TestPoint(const SHVec3& point) const noexcept { return SHSphere::TestPoint(point); @@ -193,6 +210,18 @@ namespace SHADE return result; } + SHMatrix SHSphereCollisionShape::ComputeWorldTransform() const noexcept + { + const SHQuaternion ROTATION = parentTransform->orientation * transform.orientation; + const SHVec3 SCALE{ Radius }; + + return SHMatrix::Transform + ( + Center + , ROTATION + , SCALE +); + } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 8276578e..798c70b6 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -88,6 +88,12 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * Recomputes the transform of this sphere. + */ + void ComputeTransforms () noexcept override; + /** * @brief * Tests if a point is inside the sphere. @@ -96,7 +102,7 @@ namespace SHADE * @return * True if the point is inside the sphere. */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; /** * @brief @@ -107,7 +113,7 @@ namespace SHADE * An object holding the results of the raycast.
* See the corresponding header for the contents of the object. */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; /** * @brief @@ -117,9 +123,9 @@ namespace SHADE * @return * The inertia tensor of the sphere. */ - [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; + [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; - + [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index 9aedef16..a6527e48 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -30,19 +30,15 @@ namespace SHADE : entityID { eid } , shapeIDCounter { 0 } , debugDraw { false } - , dirty { true } , rigidBody { nullptr } , shapeFactory { nullptr } , transform { worldTransform } - { - - } + {} SHCollider::SHCollider(const SHCollider& rhs) noexcept : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } , debugDraw { rhs.debugDraw } - , dirty { rhs.dirty } , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -60,7 +56,6 @@ namespace SHADE : entityID { rhs.entityID } , shapeIDCounter { rhs.shapeIDCounter } , debugDraw { rhs.debugDraw } - , dirty { rhs.dirty } , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } , transform { rhs.transform } @@ -103,7 +98,6 @@ namespace SHADE entityID = rhs.entityID; debugDraw = rhs.debugDraw; - dirty = rhs.dirty; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; transform = rhs.transform; @@ -123,7 +117,6 @@ namespace SHADE entityID = rhs.entityID; debugDraw = rhs.debugDraw; - dirty = rhs.dirty; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; transform = rhs.transform; @@ -206,25 +199,21 @@ namespace SHADE void SHCollider::SetTransform(const SHTransform& newTransform) noexcept { - dirty = true; transform = newTransform; } void SHCollider::SetPosition(const SHVec3& newPosition) noexcept { - dirty = true; transform.position = newPosition; } void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept { - dirty = true; transform.orientation = newOrientation; } void SHCollider::SetScale(const SHVec3& newScale) noexcept { - dirty = true; transform.scale = newScale; } @@ -272,8 +261,9 @@ namespace SHADE ++shapeIDCounter; // Set offsets - sphere->positionOffset = posOffset; - sphere->rotationOffset = rotOffset; + sphere->SetParentTransform(transform); + sphere->SetPositionOffset(posOffset); + sphere->SetRotationOffset(rotOffset); shapes.emplace_back(sphere); @@ -293,28 +283,6 @@ namespace SHADE return static_cast(shapes.size()); } - void SHCollider::Update() - { - // Forcefully recompute everything since the transform was changed - if (dirty) - { - RecomputeShapes(); - dirty = false; - return; - } - - // Recompute any shapes set to dirty - for (auto* shape : shapes) - { - if (shape->IsDirty()) - { - recomputeShape(shape); - shape->ClearDirty(); - } - } - } - - void SHCollider::RemoveCollisionShape(int index) { if (!shapeFactory) @@ -360,7 +328,7 @@ namespace SHADE void SHCollider::RecomputeShapes() noexcept { for (auto* shape : shapes) - recomputeShape(shape); + shape->ComputeTransforms(); } /*-----------------------------------------------------------------------------------*/ @@ -416,47 +384,4 @@ namespace SHADE ++shapeIDCounter; } - void SHCollider::recomputeShape(SHCollisionShape* shape) noexcept - { - if (!shape) - { - SHLOGV_ERROR_D("Shape missing from Collider {}", entityID) - return; - } - - switch (shape->GetType()) - { - case SHCollisionShape::Type::SPHERE: - { - recomputeSphere(dynamic_cast(shape)); - break; - } - case SHCollisionShape::Type::BOX: - { - break; - } - case SHCollisionShape::Type::CAPSULE: - { - break; - } - default: break; - } - } - - void SHCollider::recomputeSphere(SHSphereCollisionShape* sphere) noexcept - { - // Recompute world radius - const float SPHERE_SCALE = std::fabs(SHMath::Max({ transform.scale.x, transform.scale.y, transform.scale.z })); - sphere->SetScale(SPHERE_SCALE); - - // Recompute center - const SHQuaternion FINAL_ROT = transform.orientation * SHQuaternion::FromEuler(sphere->rotationOffset); - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(transform.position); - - const SHVec3 NEW_CENTER = SHVec3::Transform(sphere->positionOffset, TRS); - - sphere->SetCenter(NEW_CENTER); - } - - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index 8a183dcc..566bae3e 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -132,12 +132,6 @@ namespace SHADE // TODO: Add Box & Capsule - /** - * @brief - * Recomputes any shapes that were set to dirty. - */ - void Update (); - /** * @brief * Removes a shape from the container. Removal reduces the size of the container. @@ -164,7 +158,6 @@ namespace SHADE uint32_t shapeIDCounter; // This increments everytime a shape is added to differentiate shapes. bool debugDraw; - bool dirty; SHRigidBody* rigidBody; SHCollisionShapeFactory* shapeFactory; @@ -179,9 +172,6 @@ namespace SHADE void copyShapes (const SHCollider& rhsCollider); void copyShape (const SHCollisionShape* rhsShape); - - void recomputeShape (SHCollisionShape* shape) noexcept; - void recomputeSphere (SHSphereCollisionShape* sphere) noexcept; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index 0094153e..88ea8d79 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -41,7 +41,7 @@ namespace SHADE { const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); - if (!TRANSFORM_COMPONENT) + if (!TRANSFORM_COMPONENT || !TRANSFORM_COMPONENT->HasChanged()) continue; const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); @@ -50,7 +50,7 @@ namespace SHADE // We assume that all engine components and physics object components have been successfully linked - if (physicsObject.rigidBody && TRANSFORM_COMPONENT->HasChanged()) + if (physicsObject.rigidBody) { SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); @@ -60,14 +60,11 @@ namespace SHADE if (physicsObject.collider) { - if (TRANSFORM_COMPONENT->HasChanged()) - { - physicsObject.collider->SetPosition(WORLD_POS); - physicsObject.collider->SetOrientation(WORLD_ROT); - physicsObject.collider->SetScale(WORLD_SCL); - } + physicsObject.collider->SetPosition(WORLD_POS); + physicsObject.collider->SetOrientation(WORLD_ROT); + physicsObject.collider->SetScale(WORLD_SCL); - physicsObject.collider->Update(); + physicsObject.collider->RecomputeShapes(); } } } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index dda437bc..958ac61a 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -122,17 +122,7 @@ namespace SHADE { case SHCollisionShape::Type::SPHERE: { - const SHSphereCollisionShape* SPHERE = dynamic_cast(SHAPE); - - // Compute transforms of sphere - const SHVec3 POSITION = SPHERE->GetCenter(); // Position offset is already computed here - const SHVec3 ROTATION = collider.GetOrientation().ToEuler() + SPHERE->GetRotationOffset(); - const SHVec3 SCALE { SPHERE->GetWorldRadius() }; - - // Compute TRS for the sphere - const SHMatrix TRS = SHMatrix::Transform(POSITION, ROTATION, SCALE); - - debugDrawSystem->DrawWireSphere(TRS, DRAW_COLOUR, true); + debugDrawSystem->DrawWireSphere(SHAPE->ComputeWorldTransform(), DRAW_COLOUR, true); break; } From 51479d2bd09d3670722e0405cf7751e3065d2c64 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 16 Dec 2022 21:43:33 +0800 Subject: [PATCH 027/164] Fixed bug where meshes added to the mesh library will not have correct corresponding "null" vertex bone weights and indices --- .../src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp index dda5d423..df21343e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp @@ -171,6 +171,10 @@ namespace SHADE addJob.VertexBoneIndices, addJob.VertexBoneIndices + addJob.VertexCount * SHMesh::BONE_INDICES_PER_VERTEX ); } + else + { + vertBoneIdxStorage.resize(vertBoneIdxStorage.size() + addJob.VertexCount * SHMesh::BONE_INDICES_PER_VERTEX); + } if (addJob.VertexBoneWeights) { vertBoneWeightStorage.insert @@ -179,6 +183,10 @@ namespace SHADE addJob.VertexBoneWeights, addJob.VertexBoneWeights + addJob.VertexCount ); } + else + { + vertBoneWeightStorage.resize(vertBoneWeightStorage.size() + addJob.VertexCount); + } indexStorage.insert ( indexStorage.end(), From cf9d4ef04bf518a3a370ad6312dbbdbe352a5f42 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 19 Dec 2022 16:56:34 +0800 Subject: [PATCH 028/164] Implemented backbone for collision detection with broadphase --- Assets/Scenes/PhysicsSandbox.shade | 42 +- SHADE_Engine/src/Math/Geometry/SHBox.h | 2 +- .../Broadphase/SHDynamicAABBTree.cpp | 607 ++++++++++++++++++ .../Collision/Broadphase/SHDynamicAABBTree.h | 150 +++++ .../CollisionShapes/SHCollisionShape.h | 6 +- .../CollisionShapes/SHCollisionShapeID.h | 14 +- .../CollisionShapes/SHCollisionShapeID.hpp | 20 +- .../SHSphereCollisionShape.cpp | 7 +- .../CollisionShapes/SHSphereCollisionShape.h | 3 + .../Collision/Contacts/SHCollisionEvents.h | 60 ++ .../Collision/Contacts/SHCollisionID.cpp | 152 +++++ .../Collision/Contacts/SHCollisionID.h | 121 ++++ .../Physics/Collision/Contacts/SHContact.h | 70 ++ .../Physics/Collision/Contacts/SHManifold.h | 46 ++ .../Collision/Narrowphase/SHCollision.h | 52 ++ .../Narrowphase/SHCollisionDispatch.cpp | 95 +++ .../Narrowphase/SHCollisionDispatch.h | 87 +++ .../Narrowphase/SHSphereVsSphere.cpp | 78 +++ .../src/Physics/Collision/SHCollider.cpp | 56 +- .../src/Physics/Collision/SHCollider.h | 4 +- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 373 ++++++++++- .../src/Physics/Dynamics/SHPhysicsWorld.h | 74 ++- 22 files changed, 2064 insertions(+), 55 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h create mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h create mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.h create mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h create mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 7381b70c..8817b62e 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,7 +4,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 1.77475965, z: 0} + Translate: {x: 0, y: 3, z: 0} Rotate: {x: 0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -58,4 +58,44 @@ Far: 10000 Perspective: true IsActive: true + Scripts: ~ +- EID: 2 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: 1, z: 0} + Rotate: {x: 0, y: 0, z: 0} + Scale: {x: 1, y: 1, z: 1} + IsActive: true + RigidBody Component: + Type: Static + Auto Mass: false + Mass: .inf + Drag: 0.00999999978 + Angular Drag: 0.00999999978 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false + Freeze Position Y: false + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: true + Colliders: + - Is Trigger: false + Type: Sphere + Radius: 1 + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true Scripts: ~ \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.h b/SHADE_Engine/src/Math/Geometry/SHBox.h index a0ca9458..63383c00 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBox.h +++ b/SHADE_Engine/src/Math/Geometry/SHBox.h @@ -77,7 +77,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - [[nodiscard]] SHRaycastResult Raycast(const SHRay& ray) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; [[nodiscard]] bool Contains (const SHBox& rhs) const noexcept; [[nodiscard]] float Volume () const noexcept; diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp new file mode 100644 index 00000000..db30222b --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp @@ -0,0 +1,607 @@ +/**************************************************************************************** + * \file SHDynamicAABBTree.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Dynamic AABB Tree for broadphase collision detection. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +#include + +// Primary Header +#include "SHDynamicAABBTree.h" + +// Project Headers +#include "Math/SHMathHelpers.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHAABBTree::SHAABBTree() noexcept + : root { NULL_NODE } + , nodes { nullptr } + , nodeCount { 0 } + , capacity { 1024 } + , freeList { NULL_NODE } + { + // Build initial tree + nodes = new Node[1024]; + + addToFreeList(0); + } + + SHAABBTree::~SHAABBTree() noexcept + { + delete[] nodes; + } + + SHAABBTree::Node::Node() noexcept + : id { MAX_EID, std::numeric_limits::max() } + , parent { NULL_NODE } + , left { NULL_NODE } + , right { NULL_NODE } + , height { NULL_NODE } + {} + + SHAABBTree::Node::Node(const Node& rhs) noexcept + : AABB { rhs.AABB } + , id { rhs.id } + , next { rhs.next } + , left { rhs.left } + , right { rhs.right } + , height { rhs.height } + {} + + SHAABBTree::Node::Node(Node&& rhs) noexcept + : AABB { rhs.AABB } + , id { rhs.id } + , next { rhs.next } + , left { rhs.left } + , right { rhs.right } + , height { rhs.height } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHAABBTree::Node& SHAABBTree::Node::operator=(const Node& rhs) noexcept + { + if (this == &rhs) + return *this; + + AABB = rhs.AABB; + id = rhs.id; + parent = rhs.parent; + next = rhs.next; + left = rhs.left; + right = rhs.right; + height = rhs.height; + + return *this; + } + + SHAABBTree::Node& SHAABBTree::Node::operator=(Node&& rhs) noexcept + { + AABB = std::move(rhs.AABB); + id = std::move(rhs.id); + parent = rhs.parent; + next = rhs.next; + left = rhs.left; + right = rhs.right; + height = rhs.height; + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const std::vector& SHAABBTree::GetAABBs() const noexcept + { + static std::vector aabbs; + static std::stack nodeIndices; + + aabbs.clear(); + + nodeIndices.push(root); + while (!nodeIndices.empty()) + { + // Pop the top node + const int INDEX = nodeIndices.top(); + nodeIndices.pop(); + + // Skip null nodes + if (INDEX == NULL_NODE) + continue; + + const Node& CURRENT_NODE = nodes[INDEX]; + + aabbs.emplace_back(CURRENT_NODE.AABB); + + if (!isLeaf(INDEX)) + { + nodeIndices.push(CURRENT_NODE.left); + nodeIndices.push(CURRENT_NODE.right); + } + } + + return aabbs; + } + + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHAABBTree::Insert(SHCollisionShapeID id, const SHBox& AABB) + { + const int32_t NEW_INDEX = allocateNode(); + + if (!nodeMap.emplace(id, NEW_INDEX).second) + { + // Attempted to add a duplicate node + freeNode(NEW_INDEX); + return; + } + + Node& newNode = nodes[NEW_INDEX]; + newNode.AABB = AABB; + newNode.id = id; + newNode.height = 0; + + // Fatten the AABB + const SHVec3 EXTENSION{ AABB_EXTENSION }; + + const SHVec3 newMin = newNode.AABB.GetMin() - EXTENSION; + const SHVec3 newMax = newNode.AABB.GetMax() + EXTENSION; + + newNode.AABB.SetMin(newMin); + newNode.AABB.SetMax(newMax); + + insertLeaf(NEW_INDEX); + } + + void SHAABBTree::Update(SHCollisionShapeID id, const SHBox& AABB) + { + // Get node index + const int32_t INDEX_TO_UPDATE = nodeMap[id]; + + Node& nodeToUpdate = nodes[INDEX_TO_UPDATE]; + + // If new AABB has not moved enough, skip. + if (nodeToUpdate.AABB.Contains(AABB)) + return; + + removeLeaf(INDEX_TO_UPDATE); + + nodeToUpdate.AABB = AABB; + + // Fatten the AABB + const SHVec3 EXTENSION{ AABB_EXTENSION }; + + const SHVec3 newMin = nodeToUpdate.AABB.GetMin() - EXTENSION; + const SHVec3 newMax = nodeToUpdate.AABB.GetMax() + EXTENSION; + + nodeToUpdate.AABB.SetMin(newMin); + nodeToUpdate.AABB.SetMax(newMax); + + insertLeaf(INDEX_TO_UPDATE); + } + + void SHAABBTree::Remove(SHCollisionShapeID id) noexcept + { + // Get node index + const int32_t INDEX_TO_REMOVE = nodeMap[id]; + + removeLeaf(INDEX_TO_REMOVE); + freeNode(INDEX_TO_REMOVE); + } + + const std::vector& SHAABBTree::Query(SHCollisionShapeID id, const SHBox& AABB) const noexcept + { + static std::vector potentialCollisions; + static std::stack nodeIndices; + + potentialCollisions.clear(); + + // We use this to ignore shapes on the same entity + const EntityID EID = id.GetEntityID(); + + nodeIndices.push(root); + while (!nodeIndices.empty()) + { + const int32_t INDEX = nodeIndices.top(); + nodeIndices.pop(); + + if (INDEX == NULL_NODE) + continue; + + const Node& NODE = nodes[INDEX]; + if (!SHBox::Intersect(AABB, NODE.AABB)) + continue; + + // Avoid checking against shapes of the same composite collider (and itself) + if (isLeaf(INDEX) && NODE.id.GetEntityID() != EID) + { + potentialCollisions.emplace_back(NODE.id); + } + else + { + nodeIndices.push(NODE.left); + nodeIndices.push(NODE.right); + } + } + + return potentialCollisions; + } + + const std::vector& SHAABBTree::Query(const SHRay& ray, float distance) const noexcept + { + static std::vector potentialHits; + + potentialHits.clear(); + + return potentialHits; + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHAABBTree::isLeaf(int32_t index) const noexcept + { + const Node& NODE = nodes[index]; + return NODE.left == NULL_NODE && NODE.right == NULL_NODE; + } + + int32_t SHAABBTree::allocateNode() + { + if (freeList == NULL_NODE) + { + // No more free nodes available, so we need to resize the tree for more nodes + capacity *= 2; + + Node* newNodes = new Node[capacity]; + + // Copy all the nodes manually. I do this instead of memcpy to guarantee it copies properly. + for (int32_t i = 0; i < nodeCount; ++i) + { + newNodes[i].AABB = nodes[i].AABB; + newNodes[i].id = nodes[i].id; + newNodes[i].parent = nodes[i].parent; + newNodes[i].left = nodes[i].left; + newNodes[i].right = nodes[i].right; + newNodes[i].height = nodes[i].height; + } + + delete[] nodes; + nodes = newNodes; + + addToFreeList(nodeCount); + } + + const int32_t FREE_NODE = freeList; + freeList = nodes[FREE_NODE].next; + + // Set node to default + Node& newNode = nodes[FREE_NODE]; + newNode.parent = NULL_NODE; + newNode.left = NULL_NODE; + newNode.right = NULL_NODE; + newNode.height = NULL_NODE; + + ++nodeCount; + return FREE_NODE; + } + + void SHAABBTree::freeNode(int32_t index) noexcept + { + SHASSERT(index >= 0 && index < capacity, "Trying to free an invalid AABB Tree node!") + + nodes[index].next = NULL_NODE; + nodes[index].height = NULL_NODE; + + // Put it back on the free list + freeList = index; + + --nodeCount; + } + + void SHAABBTree::insertLeaf(int32_t index) + { + // If there is no root, the first insert must make the root + if (root == NULL_NODE) + { + root = index; + nodes[root].parent = NULL_NODE; + return; + } + + // Find best sibling for new leaf + // Utilise Surface Area Heuristic + const SHBox& LEAF_AABB = nodes[index].AABB; + + uint32_t searchIndex = root; + while (!isLeaf(searchIndex)) + { + const SHBox COMBINED_AABB = SHBox::Combine(LEAF_AABB, nodes[searchIndex].AABB); + const float COMBINED_AREA = COMBINED_AABB.SurfaceArea(); + + const float INHERITED_COST = 2.0f * (COMBINED_AREA - nodes[searchIndex].AABB.SurfaceArea()); + + const int32_t LEFT_INDEX = nodes[searchIndex].left; + const int32_t RIGHT_INDEX = nodes[searchIndex].right; + + float leftCost = 0.0f; + float rightCost = 0.0f; + + const float LEFT_COMBINED_AREA = SHBox::Combine(LEAF_AABB, nodes[LEFT_INDEX].AABB).SurfaceArea(); + const float RIGHT_COMBINED_AREA = SHBox::Combine(LEAF_AABB, nodes[RIGHT_INDEX].AABB).SurfaceArea(); + + // Compute cost for descending into the left + if (isLeaf(LEFT_INDEX)) + leftCost = LEFT_COMBINED_AREA + INHERITED_COST; + else + leftCost = LEFT_COMBINED_AREA - nodes[LEFT_INDEX].AABB.SurfaceArea() + INHERITED_COST; + + // Compute cost for descending into the right + if (isLeaf(RIGHT_INDEX)) + rightCost = RIGHT_COMBINED_AREA + INHERITED_COST; + else + rightCost = RIGHT_COMBINED_AREA - nodes[RIGHT_INDEX].AABB.SurfaceArea() + INHERITED_COST; + + // Early out + const float BRANCH_COST = 2.0f * COMBINED_AREA; + if (BRANCH_COST < leftCost && BRANCH_COST < rightCost) + break; + + // Traverse + searchIndex = leftCost < rightCost ? LEFT_INDEX : RIGHT_INDEX; + } + + const int32_t BEST_SIBLING = searchIndex; + + // Create a new parent for the leaf + const int32_t OLD_PARENT = nodes[BEST_SIBLING].parent; + const int32_t NEW_PARENT = allocateNode(); + + Node& newParent = nodes[NEW_PARENT]; + newParent.parent = OLD_PARENT; + newParent.id = SHCollisionShapeID{ MAX_EID, std::numeric_limits::max() }; + newParent.AABB = SHBox::Combine(LEAF_AABB, nodes[BEST_SIBLING].AABB); + newParent.height = nodes[BEST_SIBLING].height + 1; + + newParent.left = BEST_SIBLING; + newParent.right = index; + + nodes[BEST_SIBLING].parent = NEW_PARENT; + nodes[index].parent = NEW_PARENT; + + // If sibling was the root + if (OLD_PARENT == NULL_NODE) + root = NEW_PARENT; + else + (nodes[OLD_PARENT].left == BEST_SIBLING ? nodes[OLD_PARENT].left : nodes[OLD_PARENT].right) = NEW_PARENT; + + syncHierarchy(NEW_PARENT); + } + + void SHAABBTree::removeLeaf(int32_t index) + { + if (index == root) + { + root = NULL_NODE; + return; + } + + const int32_t PARENT = nodes[index].parent; + const int32_t GRANDPARENT = nodes[PARENT].parent; + const int32_t SIBLING = nodes[PARENT].left == index ? nodes[PARENT].right : nodes[PARENT].left; + + if (GRANDPARENT != NULL_NODE) + { + // Replace parent with sibling + (nodes[GRANDPARENT].left == PARENT ? nodes[GRANDPARENT].left : nodes[GRANDPARENT].right) = SIBLING; + nodes[SIBLING].parent = GRANDPARENT; + } + else + { + // Parent was root + root = SIBLING; + nodes[SIBLING].parent = NULL_NODE; + } + + freeNode(PARENT); + syncHierarchy(GRANDPARENT); + } + + void SHAABBTree::syncHierarchy(int32_t index) + { + while (index != NULL_NODE) + { + index = balance(index); + + const int32_t LEFT_INDEX = nodes[index].left; + const Node& LEFT_NODE = nodes[LEFT_INDEX]; + + const int32_t RIGHT_INDEX = nodes[index].right; + const Node& RIGHT_NODE = nodes[RIGHT_INDEX]; + + nodes[index].height = 1 + SHMath::Max(LEFT_NODE.height, RIGHT_NODE.height); + nodes[index].AABB = SHBox::Combine(LEFT_NODE.AABB, RIGHT_NODE.AABB); + + // Sync up to the root + index = nodes[index].parent; + } + } + + int32_t SHAABBTree::balance(int32_t index) + { + if (isLeaf(index) || nodes[index].height == 1) + return index; + + Node& nodeA = nodes[index]; + + const int32_t LEFT = nodeA.left; + const int32_t RIGHT = nodeA.right; + + const int32_t DIFF = nodes[RIGHT].height - nodes[LEFT].height; + + if (DIFF > 1) + return rotateLeft(index); + + if (DIFF < -1) + return rotateRight(index); + + return index; + } + + int32_t SHAABBTree::rotateLeft(int32_t index) + { + /**************************** + A C + / \ / \ + B C --> A F/G + / \ / \ + F G B G/F + ****************************/ + + // Promote C + + Node& nodeA = nodes[index]; + + const int32_t B = nodeA.left; + const int32_t C = nodeA.right; + + Node& nodeB = nodes[B]; + Node& nodeC = nodes[C]; + + const int32_t F = nodeC.left; + const int32_t G = nodeC.right; + + Node& nodeF = nodes[F]; + Node& nodeG = nodes[G]; + + if (nodeA.parent != NULL_NODE) + (nodes[nodeA.parent].left == index ? nodes[nodeA.parent].left : nodes[nodeA.parent].right) = C; + else + root = C; + + nodeC.left = index; + nodeC.parent = nodeA.parent; + nodeA.parent = C; + + if (nodeF.height > nodeG.height) + { + nodeC.right = F; + nodeA.right = G; + nodeG.parent = index; + + nodeA.AABB = SHBox::Combine(nodeB.AABB, nodeG.AABB); + nodeC.AABB = SHBox::Combine(nodeA.AABB, nodeF.AABB); + + nodeA.height = 1 + SHMath::Max(nodeB.height, nodeG.height); + nodeC.height = 1 + SHMath::Max(nodeA.height, nodeF.height); + } + else + { + nodeC.right = G; + nodeA.right = F; + nodeF.parent = index; + + nodeA.AABB = SHBox::Combine(nodeB.AABB, nodeF.AABB); + nodeC.AABB = SHBox::Combine(nodeA.AABB, nodeG.AABB); + + nodeA.height = 1 + SHMath::Max(nodeB.height, nodeF.height); + nodeC.height = 1 + SHMath::Max(nodeA.height, nodeG.height); + } + + return C; + } + + int32_t SHAABBTree::rotateRight(int32_t index) + { + /************************* + A B + / \ / \ + B C --> D/E A + / \ / \ + D E E/D C + *************************/ + + // Promote B + + Node& nodeA = nodes[index]; + + const int32_t B = nodeA.left; + const int32_t C = nodeA.right; + + Node& nodeB = nodes[B]; + Node& nodeC = nodes[C]; + + const int32_t D = nodeB.left; + const int32_t E = nodeB.right; + + Node& nodeD = nodes[D]; + Node& nodeE = nodes[E]; + + if (nodeA.parent != NULL_NODE) + (nodes[nodeA.parent].left == index ? nodes[nodeA.parent].left : nodes[nodeA.parent].right) = B; + else + root = B; + + nodeB.right = index; + nodeB.parent = nodeA.parent; + nodeA.parent = B; + + if (nodeD.height > nodeE.height) + { + nodeB.left = D; + nodeA.left = E; + nodeE.parent = index; + + nodeA.AABB = SHBox::Combine(nodeC.AABB, nodeE.AABB); + nodeB.AABB = SHBox::Combine(nodeA.AABB, nodeD.AABB); + + nodeA.height = 1 + SHMath::Max(nodeC.height, nodeE.height); + nodeB.height = 1 + SHMath::Max(nodeA.height, nodeD.height); + } + else + { + nodeB.left = E; + nodeA.left = D; + nodeD.parent = index; + + nodeA.AABB = SHBox::Combine(nodeC.AABB, nodeD.AABB); + nodeB.AABB = SHBox::Combine(nodeA.AABB, nodeE.AABB); + + nodeA.height = 1 + SHMath::Max(nodeC.height, nodeD.height); + nodeB.height = 1 + SHMath::Max(nodeA.height, nodeE.height); + } + + return B; + } + + void SHAABBTree::addToFreeList(int32_t index) noexcept + { + for (int32_t i = index; i < capacity; ++i) + { + nodes[i].next = i + 1; + nodes[i].height = NULL_NODE; + } + + nodes[capacity - 1].next = NULL_NODE; + nodes[capacity - 1].height = NULL_NODE; + + freeList = index; + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h new file mode 100644 index 00000000..973631a1 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h @@ -0,0 +1,150 @@ +/**************************************************************************************** + * \file SHDynamicAABBTree.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Dynamic AABB Tree for broadphase collision detection. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates a dynamic AABB Tree for collision detection. + */ + class SH_API SHAABBTree + { + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr int NULL_NODE = -1; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHAABBTree () noexcept; + ~SHAABBTree () noexcept; + + SHAABBTree(const SHAABBTree& other) = delete; + SHAABBTree(SHAABBTree&& other) noexcept = delete; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHAABBTree& operator=(const SHAABBTree& other) = delete; + SHAABBTree& operator=(SHAABBTree&& other) noexcept = delete; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] const std::vector& GetAABBs () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + void Insert (SHCollisionShapeID id, const SHBox& AABB); + void Update (SHCollisionShapeID id, const SHBox& AABB); + void Remove (SHCollisionShapeID id) noexcept; + + [[nodiscard]] const std::vector& Query(SHCollisionShapeID id, const SHBox& AABB) const noexcept; + [[nodiscard]] const std::vector& Query(const SHRay& ray, float distance) const noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + struct Node + { + public: + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + Node () noexcept; + Node (const Node& rhs) noexcept; + Node (Node&& rhs) noexcept; + + ~Node () noexcept = default; + + /*-------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-------------------------------------------------------------------------------*/ + + Node& operator=(const Node& rhs) noexcept; + Node& operator=(Node&& rhs) noexcept; + + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + SHBox AABB; + SHCollisionShapeID id; // Used to lookup the collision shape & entity for culling against itself + + union + { + int32_t parent; + int32_t next; + }; + + + int32_t left; + int32_t right; + int32_t height; // Leaves have a height of 0. Free nodes have a height of -1 + }; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr float AABB_EXTENSION = 0.2f; + + // For quick access + std::unordered_map nodeMap; + + int32_t root; + Node* nodes; // Dynamically allocated array of nodes. I use dynamic allocation as in the past, using a vector causes weird issues. + int32_t nodeCount; + int32_t capacity; // Used for resizing the tree. + int32_t freeList; // Stores the next available node on the free list. + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + bool isLeaf (int32_t index) const noexcept; + + int32_t allocateNode (); + void freeNode (int32_t index) noexcept; + + void insertLeaf (int32_t index); + void removeLeaf (int32_t index); + void syncHierarchy (int32_t index); + int32_t balance (int32_t index); + int32_t rotateLeft (int32_t index); + int32_t rotateRight (int32_t index); + + void addToFreeList (int32_t index) noexcept; + }; + + +} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 3c08e21a..dad59881 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -17,6 +17,7 @@ #include "Physics/Collision/CollisionTags/SHCollisionTags.h" #include "Physics/Collision/SHPhysicsMaterial.h" #include "SHCollisionShapeID.h" +#include "Math/Geometry/SHBox.h" #include "Math/Transform/SHTransform.h" namespace SHADE @@ -33,8 +34,10 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ + friend class SHCollider; friend class SHColliderComponent; friend class SHCollisionShapeFactory; + friend class SHPhysicsWorld; public: /*---------------------------------------------------------------------------------*/ @@ -120,6 +123,7 @@ namespace SHADE virtual void ComputeTransforms () noexcept = 0; [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; + [[nodiscard]] virtual SHBox ComputeAABB () const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ @@ -136,7 +140,7 @@ namespace SHADE // Needed for conversion to euler angles SHVec3 rotationOffset; - uint8_t flags; // 0 0 wasColliding isColliding trigger capsule sphere box + uint8_t flags; // 0 0 0 isColliding trigger capsule sphere box SHCollisionTag* collisionTag; RTTR_ENABLE() diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h index a0f78177..09f712d1 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h @@ -10,6 +10,7 @@ #pragma once +// Project Headers #include "ECS_Base/Entity/SHEntity.h" namespace SHADE @@ -55,11 +56,14 @@ namespace SHADE SHCollisionShapeID& operator=(const SHCollisionShapeID& rhs) noexcept; SHCollisionShapeID& operator=(SHCollisionShapeID&& rhs) noexcept; + bool operator==(const SHCollisionShapeID& rhs) const; + /*---------------------------------------------------------------------------------*/ - /* Member Functions */ + /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - bool operator==(const SHCollisionShapeID& rhs) const; + [[nodiscard]] EntityID GetEntityID () const noexcept; + [[nodiscard]] uint32_t GetShapeIndex () const noexcept; private: /*---------------------------------------------------------------------------------*/ @@ -69,8 +73,8 @@ namespace SHADE struct IDs { public: - EntityID entityID = MAX_EID; - uint32_t shapeID = std::numeric_limits::max(); + EntityID entityID = MAX_EID; + uint32_t shapeIndex = std::numeric_limits::max(); }; @@ -84,7 +88,7 @@ namespace SHADE /** * @brief - * Encapsulates a functor to hash a CollisionShapeKey + * Encapsulates a functor to hash a CollisionShapeID */ struct SHCollisionShapeIDHash { diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp index 234b2f6f..bb9ccb1f 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp @@ -25,11 +25,11 @@ namespace SHADE {} inline SHCollisionShapeID::SHCollisionShapeID(const SHCollisionShapeID& rhs) noexcept - : ids { rhs.ids.entityID, rhs.ids.shapeID } + : ids { rhs.ids.entityID, rhs.ids.shapeIndex } {} inline SHCollisionShapeID::SHCollisionShapeID(SHCollisionShapeID&& rhs) noexcept - : ids { rhs.ids.entityID, rhs.ids.shapeID } + : ids { rhs.ids.entityID, rhs.ids.shapeIndex } {} @@ -61,6 +61,20 @@ namespace SHADE inline std::size_t SHCollisionShapeIDHash::operator()(const SHCollisionShapeID& id) const { - return std::hash()(id.value); + return std::hash{}(id.value); + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + inline EntityID SHCollisionShapeID::GetEntityID() const noexcept + { + return ids.entityID; + } + + inline uint32_t SHCollisionShapeID::GetShapeIndex() const noexcept + { + return ids.shapeIndex; } } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp index e4bf68c3..a871a5eb 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -220,7 +220,12 @@ namespace SHADE Center , ROTATION , SCALE -); + ); + } + + SHBox SHSphereCollisionShape::ComputeAABB() const noexcept + { + return SHBox{ Center, SHVec3{ Radius } }; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 798c70b6..1325fc56 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -46,6 +46,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ friend class SHCollider; + friend class SHCollision; friend class SHCompositeCollider; friend class SHCollisionShapeFactory; @@ -127,6 +128,8 @@ namespace SHADE [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; + [[nodiscard]] SHBox ComputeAABB () const noexcept override; + private: /*---------------------------------------------------------------------------------*/ /* Data Members */ diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h new file mode 100644 index 00000000..7abb397b --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h @@ -0,0 +1,60 @@ +/**************************************************************************************** + * \file SHCollisionID.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for Collision Information for Collision & Triggers. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "SHCollisionID.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + enum class SHCollisionState + { + ENTER + , STAY + , EXIT + + , TOTAL + , INVALID = -1 + }; + + /** + * @brief + * Encapsulates the event for an intersection between two collision shapes that do not + * have physical resolution. + */ + struct SH_API SHTriggerEvent + { + public: + SHCollisionID info; + SHCollisionState state; + }; + + /** + * @brief + * Encapsulates the event for an intersection between two collision shapes that do + * have physical resolution. + */ + struct SH_API SHCollisionEvent + { + public: + static constexpr int MAX_NUM_CONTACTS = 4; + + SHCollisionID info; + SHCollisionState state; + SHVec3 normal; + SHVec3 contactPoints[MAX_NUM_CONTACTS]; + }; + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.cpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.cpp new file mode 100644 index 00000000..fae14aca --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.cpp @@ -0,0 +1,152 @@ +/**************************************************************************************** + * \file SHCollisionInfo.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for Collision Info. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollisionID.h" + +// Project Headers +#include "Physics/Collision/SHCollider.h" +#include "Physics/Interface/SHColliderComponent.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionID::SHCollisionID() noexcept + { + ids[ENTITY_A] = MAX_EID; + ids[ENTITY_B] = MAX_EID; + ids[SHAPE_INDEX_A] = std::numeric_limits::max(); + ids[SHAPE_INDEX_B] = std::numeric_limits::max(); + } + + SHCollisionID::SHCollisionID(const SHCollisionID& rhs) noexcept + { + value[0] = rhs.value[0]; + value[1] = rhs.value[1]; + } + + SHCollisionID::SHCollisionID(SHCollisionID&& rhs) noexcept + { + value[0] = rhs.value[0]; + value[1] = rhs.value[1]; + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionID& SHCollisionID::operator=(const SHCollisionID& rhs) noexcept + { + if (this == &rhs) + return *this; + + value[0] = rhs.value[0]; + value[1] = rhs.value[1]; + + return *this; + } + + SHCollisionID& SHCollisionID::operator=(SHCollisionID&& rhs) noexcept + { + value[0] = rhs.value[0]; + value[1] = rhs.value[1]; + + return *this; + } + + bool SHCollisionID::operator==(const SHCollisionID& rhs) const + { + // When checking for equal, check both ways. + // Exact Match (A, idxA, B, idxB) + const bool EXACT_MATCH = value[0] == rhs.value[0] && value[1] == rhs.value[1]; + + // Flipped Match: (B, idxB, A, idxA) + const bool FLIPPED_MATCH = value[0] == rhs.value[1] && value[1] == rhs.value[0]; + + return EXACT_MATCH || FLIPPED_MATCH; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + EntityID SHCollisionID::GetEntityA() const noexcept + { + return ids[ENTITY_A]; + } + + EntityID SHCollisionID::GetEntityB() const noexcept + { + return ids[ENTITY_B]; + } + + uint32_t SHCollisionID::GetShapeIndexA() const noexcept + { + return ids[SHAPE_INDEX_A]; + } + + uint32_t SHCollisionID::GetShapeIndexB() const noexcept + { + return ids[SHAPE_INDEX_B]; + } + + const SHRigidBodyComponent* SHCollisionID::GetRigidBodyA() const noexcept + { + return SHComponentManager::GetComponent_s(ids[ENTITY_A]); + } + + const SHRigidBodyComponent* SHCollisionID::GetRigidBodyB() const noexcept + { + return SHComponentManager::GetComponent_s(ids[ENTITY_B]); + } + + const SHCollisionShape* SHCollisionID::GetCollisionShapeA() const noexcept + { + const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_A]); + return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[SHAPE_INDEX_A]); + } + + const SHCollisionShape* SHCollisionID::GetCollisionShapeB() const noexcept + { + const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_B]); + return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[SHAPE_INDEX_B]); + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionID::SetEntityA(EntityID entityID) noexcept + { + ids[ENTITY_A] = entityID; + } + + void SHCollisionID::SetEntityB(EntityID entityID) noexcept + { + ids[ENTITY_B] = entityID; + } + + void SHCollisionID::SetCollisionShapeA(uint32_t shapeIndexA) noexcept + { + ids[SHAPE_INDEX_A] = shapeIndexA; + } + + void SHCollisionID::SetCollisionShapeB(uint32_t shapeIndexB) noexcept + { + ids[SHAPE_INDEX_B] = shapeIndexB; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.h new file mode 100644 index 00000000..78b28a96 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.h @@ -0,0 +1,121 @@ +/**************************************************************************************** + * \file SHCollisionID.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for Collision Information for Collision & Triggers. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Physics/Interface/SHColliderComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + struct SHCollisionIDHash; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates the information when two colliders intersect and do not have physical + * resolution. + */ + class SH_API SHCollisionID + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend struct SHCollisionIDHash; + + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionID () noexcept; + SHCollisionID (const SHCollisionID& rhs) noexcept; + SHCollisionID (SHCollisionID&& rhs) noexcept; + + ~SHCollisionID () noexcept = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionID& operator= (const SHCollisionID& rhs) noexcept; + SHCollisionID& operator= (SHCollisionID&& rhs) noexcept; + + bool operator==(const SHCollisionID& rhs) const; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] EntityID GetEntityA () const noexcept; + [[nodiscard]] EntityID GetEntityB () const noexcept; + [[nodiscard]] uint32_t GetShapeIndexA () const noexcept; + [[nodiscard]] uint32_t GetShapeIndexB () const noexcept; + [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyA () const noexcept; + [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyB () const noexcept; + [[nodiscard]] const SHCollisionShape* GetCollisionShapeA () const noexcept; + [[nodiscard]] const SHCollisionShape* GetCollisionShapeB () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetEntityA (EntityID entityID) noexcept; + void SetEntityB (EntityID entityID) noexcept; + void SetCollisionShapeA (uint32_t shapeIndexA) noexcept; + void SetCollisionShapeB (uint32_t shapeIndexB) noexcept; + + private: + + static constexpr uint32_t ENTITY_A = 0; + static constexpr uint32_t SHAPE_INDEX_A = 1; + static constexpr uint32_t ENTITY_B = 2; + static constexpr uint32_t SHAPE_INDEX_B = 3; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + union + { + uint64_t value[2]; // EntityValue, ShapeIndexValue + uint32_t ids [4]; // EntityA, EntityB, ShapeIndexA, ShapeIndexB + }; + }; + + /** + * @brief + * Encapsulates a functor to hash a CollisionID + */ + struct SHCollisionIDHash + { + public: + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + inline std::size_t operator()(const SHCollisionID& id) const + { + return std::hash{}(std::u32string_view(reinterpret_cast::const_pointer>(id.ids), 4)); + } + }; + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h new file mode 100644 index 00000000..c70f6e39 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h @@ -0,0 +1,70 @@ +/**************************************************************************************** + * \file SHManifold.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Collision Manifold + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Primary Header +#include "Physics/Dynamics/SHRigidBody.h" +#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates a value that represents the touching features of a contact. + */ + union SHContactFeatures + { + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + struct + { + uint8_t incomingIncident; + uint8_t outgoingIncident; + uint8_t incomingReference; + uint8_t outgoingReference; + }; + + uint32_t key = 0; + }; + + /** + * @brief + * Encapsulates a physical collision contact. + */ + struct SH_API SHContact + { + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr int NUM_TANGENTS = 2; + + float penetration = 0.0f; + float bias = 0.0f; + float normalImpulse = 0.0f; + float normalMass = 0.0f; + float tangentImpulse[NUM_TANGENTS] = { 0.0f }; + float tangentMass[NUM_TANGENTS] = { 0.0f }; + + SHVec3 position; + SHContactFeatures featurePair; + }; +} + +#pragma once diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h new file mode 100644 index 00000000..da6fe233 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h @@ -0,0 +1,46 @@ +/**************************************************************************************** + * \file SHManifold.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Collision Manifold + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Primary Header +#include "Physics/Dynamics/SHRigidBody.h" +#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" +#include "SHContact.h" +#include "SHCollisionEvents.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + struct SH_API SHManifold + { + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + // We only need 4 contact points to build a stable manifold. + static constexpr int MAX_NUM_CONTACTS = 4; + + SHCollisionShape* A = nullptr; + SHCollisionShape* B = nullptr; + + SHVec3 normal; + SHVec3 tangents[SHContact::NUM_TANGENTS]; + + SHContact contacts[MAX_NUM_CONTACTS]; + uint32_t numContacts = 0; + SHCollisionState state; + }; + +} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h new file mode 100644 index 00000000..befb16f6 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -0,0 +1,52 @@ +/**************************************************************************************** + * \file SHCollision.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for the Detecting Collisions between two shapes + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" +#include "Physics/Collision/Contacts/SHManifold.h" +#include "Physics/Collision/Contacts/SHCollisionID.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates static methods for testing for collision between two shapes. + */ + class SH_API SHCollision + { + public: + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /* Spheres VS X */ + + [[nodiscard]] static bool SphereVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool SphereVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + + /* Capsule VS X */ + + /* Polygon VS X */ + + private: + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + }; +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp new file mode 100644 index 00000000..c06e30ae --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp @@ -0,0 +1,95 @@ +/**************************************************************************************** + * \file SHCollisionDispatch.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the static Collision Dispatcher + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollisionDispatch.h" + +// Project Header +#include "SHCollision.h" +#include "Tools/Utilities/SHUtilities.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Static Data Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHCollisionDispatcher::ManifoldCollide SHCollisionDispatcher::manifoldCollide[NUM_SHAPES][NUM_SHAPES] + { + // TODO + // vs Sphere / Box / Capsule + + { SHCollision::SphereVsSphere, nullptr, nullptr } // Sphere + , { nullptr, nullptr, nullptr } // Box + , { nullptr, nullptr, nullptr } // Capsule + }; + + const SHCollisionDispatcher::TriggerCollide SHCollisionDispatcher::triggerCollide[NUM_SHAPES][NUM_SHAPES] + { + // TODO + // vs Sphere / Box / Capsule + + { SHCollision::SphereVsSphere, nullptr, nullptr } // Sphere + , { nullptr, nullptr, nullptr } // Box + , { nullptr, nullptr, nullptr } // Capsule + }; + + const bool SHCollisionDispatcher::collisionTable[NUM_TYPES][NUM_TYPES] + { + /* S ST K KT D DT */ + /* S */ { false, false, false, true, true, true } + , /* ST */ { false, false, true, true, true, true } + , /* K */ { false, true, false, true, true, true } + , /* KT */ { true, true, true, true, true, true } + , /* D */ { true, true, true, true, true, true } + , /* DT */ { true, true, true, true, true, true } + }; + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollisionDispatcher::ShouldCollide(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + // Filter through collision table + const int TYPE_A = SHUtilities::ConvertEnum(A.GetType()) + A.IsTrigger() ? TYPE_OFFSET : 0; + const int TYPE_B = SHUtilities::ConvertEnum(B.GetType()) + B.IsTrigger() ? TYPE_OFFSET : 0; + + if (!collisionTable[TYPE_A][TYPE_B]) + return false; + + // Filter through tags + const uint16_t TAG_A = A.GetCollisionTag().GetMask(); + const uint16_t TAG_B = B.GetCollisionTag().GetMask(); + + const uint16_t MATCH = TAG_A & TAG_B; + return MATCH > 0; + } + + bool SHCollisionDispatcher::Collide(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + const int TYPE_A = SHUtilities::ConvertEnum(A.GetType()); + const int TYPE_B = SHUtilities::ConvertEnum(B.GetType()); + + return manifoldCollide[TYPE_A][TYPE_B](manifold, A, B); + } + + bool SHCollisionDispatcher::Collide(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + const int TYPE_A = SHUtilities::ConvertEnum(A.GetType()); + const int TYPE_B = SHUtilities::ConvertEnum(B.GetType()); + + return triggerCollide[TYPE_A][TYPE_B](A, B); + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h new file mode 100644 index 00000000..4c132746 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h @@ -0,0 +1,87 @@ +/**************************************************************************************** + * \file SHCollisionDispatch.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for the static Collision Dispatcher + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Physics/Collision/Contacts/SHManifold.h" +#include "Physics/Collision/Contacts/SHCollisionID.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates static methods for running narrow-phase collision detection. + */ + class SH_API SHCollisionDispatcher + { + public: + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Filters the collision through the collision table and layer matching. + * @param A + * A Collision Shape. + * @param B + * A Collision Shape. + * @return + * True if both shapes should be tested for collision. + */ + static bool ShouldCollide (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + static bool Collide (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + static bool Collide (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using ManifoldCollide = bool(*)(SHManifold&, const SHCollisionShape& A, const SHCollisionShape& B); + using TriggerCollide = bool(*)(const SHCollisionShape& A, const SHCollisionShape& B); + + enum class Types + { + STATIC + , KINEMATIC + , DYNAMIC + , STATIC_TRIGGER + , KINEMATIC_TRIGGER + , DYNAMIC_TRIGGER + + , COUNT + }; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + // Read the Types enum class, then see where it's used and it'll make sense + static constexpr int TYPE_OFFSET = 3; + + static constexpr int NUM_SHAPES = static_cast(SHCollisionShape::Type::COUNT); + static constexpr int NUM_TYPES = static_cast(Types::COUNT); + + static const ManifoldCollide manifoldCollide [NUM_SHAPES][NUM_SHAPES]; + static const TriggerCollide triggerCollide [NUM_SHAPES][NUM_SHAPES]; + + static const bool collisionTable [NUM_TYPES][NUM_TYPES]; + + }; + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp new file mode 100644 index 00000000..b26a33dd --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp @@ -0,0 +1,78 @@ +/**************************************************************************************** + * \file SHSphereVsSphere.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Detecting Collisions between two spheres + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollision.h" + +// Project Headers +#include "Math/SHMathHelpers.h" +#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollision::SphereVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + const SHSphereCollisionShape& SPHERE_A = dynamic_cast(A); + const SHSphereCollisionShape& SPHERE_B = dynamic_cast(B); + + return SHSphere::Intersect(SPHERE_A, SPHERE_B); + } + + bool SHCollision::SphereVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + // Convert to spheres + const SHSphereCollisionShape& SPHERE_A = dynamic_cast(A); + const SHSphereCollisionShape& SPHERE_B = dynamic_cast(B); + + const SHVec3 A_TO_B = SPHERE_B.GetCenter() - SPHERE_A.GetCenter(); + const float DISTANCE_BETWEEN_CENTERS_SQUARED = A_TO_B.LengthSquared(); + + const float COMBINED_RADIUS = (SPHERE_A.GetWorldRadius() + SPHERE_B.GetWorldRadius()); + const float COMBINED_RADIUS_SQUARED = COMBINED_RADIUS * COMBINED_RADIUS; + + if (DISTANCE_BETWEEN_CENTERS_SQUARED > COMBINED_RADIUS_SQUARED) + return false; + + // Only populate the manifold if there is a collision + + uint32_t numContacts = 0; + + SHContact contact; + contact.featurePair.key = 0; + + if (SHMath::CompareFloat(DISTANCE_BETWEEN_CENTERS_SQUARED, 0.0f)) + { + manifold.normal = SHVec3::UnitY; + contact.position = SPHERE_A.GetCenter(); + contact.penetration = SPHERE_B.GetWorldRadius(); + + manifold.contacts[numContacts++] = contact; + } + else + { + manifold.normal = SHVec3::Normalise(A_TO_B); + contact.position = SPHERE_B.GetCenter() - (manifold.normal * SPHERE_B.GetWorldRadius()); + contact.penetration = COMBINED_RADIUS - A_TO_B.Length(); + + manifold.contacts[numContacts++] = contact; + } + + manifold.numContacts = numContacts; + + return true; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index a6527e48..24ba26b7 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -14,6 +14,7 @@ #include "SHCollider.h" // Project Headers +#include "Broadphase/SHDynamicAABBTree.h" #include "Events/SHEvent.h" #include "Math/SHMathHelpers.h" #include "Physics/SHPhysicsEvents.h" @@ -28,19 +29,21 @@ namespace SHADE SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept : entityID { eid } - , shapeIDCounter { 0 } , debugDraw { false } + , hasMoved { true } , rigidBody { nullptr } , shapeFactory { nullptr } + , broadphase { nullptr } , transform { worldTransform } {} SHCollider::SHCollider(const SHCollider& rhs) noexcept : entityID { rhs.entityID } - , shapeIDCounter { rhs.shapeIDCounter } , debugDraw { rhs.debugDraw } + , hasMoved { rhs.hasMoved } , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } + , broadphase { rhs.broadphase } , transform { rhs.transform } { if (!shapeFactory) @@ -54,10 +57,11 @@ namespace SHADE SHCollider::SHCollider(SHCollider&& rhs) noexcept : entityID { rhs.entityID } - , shapeIDCounter { rhs.shapeIDCounter } , debugDraw { rhs.debugDraw } + , hasMoved { rhs.hasMoved } , rigidBody { rhs.rigidBody } , shapeFactory { rhs.shapeFactory } + , broadphase { rhs.broadphase } , transform { rhs.transform } { if (!shapeFactory) @@ -98,8 +102,10 @@ namespace SHADE entityID = rhs.entityID; debugDraw = rhs.debugDraw; + hasMoved = rhs.hasMoved; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; + broadphase = rhs.broadphase; transform = rhs.transform; copyShapes(rhs); @@ -117,8 +123,10 @@ namespace SHADE entityID = rhs.entityID; debugDraw = rhs.debugDraw; + hasMoved = rhs.hasMoved; rigidBody = rhs.rigidBody; shapeFactory = rhs.shapeFactory; + broadphase = rhs.broadphase; transform = rhs.transform; copyShapes(rhs); @@ -199,21 +207,25 @@ namespace SHADE void SHCollider::SetTransform(const SHTransform& newTransform) noexcept { + hasMoved = true; transform = newTransform; } void SHCollider::SetPosition(const SHVec3& newPosition) noexcept { + hasMoved = true; transform.position = newPosition; } void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept { + hasMoved = true; transform.orientation = newOrientation; } void SHCollider::SetScale(const SHVec3& newScale) noexcept { + hasMoved = true; transform.scale = newScale; } @@ -255,10 +267,10 @@ namespace SHADE , .Scale = SPHERE_SCALE }; - const SHCollisionShapeID NEW_SHAPE_ID{ entityID, shapeIDCounter }; + const uint32_t NEW_INDEX = static_cast(shapes.size()); + const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); - ++shapeIDCounter; // Set offsets sphere->SetParentTransform(transform); @@ -267,12 +279,15 @@ namespace SHADE shapes.emplace_back(sphere); + if (broadphase) + broadphase->Insert(NEW_SHAPE_ID, sphere->ComputeAABB()); + // Broadcast Event for adding a shape const SHPhysicsColliderAddedEvent EVENT_DATA { .entityID = entityID , .colliderType = SHCollisionShape::Type::SPHERE - , .colliderIndex = static_cast(shapes.size()) + , .colliderIndex = static_cast(NEW_INDEX) }; SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT); @@ -280,7 +295,7 @@ namespace SHADE if (rigidBody) rigidBody->ComputeMassData(); - return static_cast(shapes.size()); + return static_cast(NEW_INDEX); } void SHCollider::RemoveCollisionShape(int index) @@ -296,8 +311,8 @@ namespace SHADE if (index < 0 || index >= NUM_SHAPES) throw std::invalid_argument("Out-of-range index!"); - auto shapeIter = shapes.begin(); - for (int i = 0; i < NUM_SHAPES; ++i, ++shapeIter) + auto shape = shapes.begin(); + for (int i = 0; i < NUM_SHAPES; ++i, ++shape) { if (i == index) break; @@ -306,15 +321,21 @@ namespace SHADE const SHPhysicsColliderRemovedEvent EVENT_DATA { .entityID = entityID - , .colliderType = (*shapeIter)->GetType() + , .colliderType = (*shape)->GetType() , .colliderIndex = index }; - shapeFactory->DestroyShape(*shapeIter); - *shapeIter = nullptr; + // Remove from broadphase + if (broadphase) + broadphase->Remove((*shape)->id); + + shapeFactory->DestroyShape(*shape); + *shape = nullptr; // Remove the shape from the container to prevent accessing a nullptr - shapeIter = shapes.erase(shapeIter); + shape = shapes.erase(shape); + + // Broadcast Event for removing a shape SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT); @@ -337,12 +358,8 @@ namespace SHADE void SHCollider::copyShapes(const SHCollider& rhsCollider) { - shapeIDCounter = 0; for (const auto* shape : rhsCollider.shapes) - { copyShape(shape); - ++shapeIDCounter; - } } void SHCollider::copyShape(const SHCollisionShape* rhsShape) @@ -365,7 +382,8 @@ namespace SHADE , .Scale = RHS_SPHERE->scale }; - const SHCollisionShapeID NEW_SHAPE_ID{ entityID, shapeIDCounter }; + const uint32_t NEW_INDEX = static_cast(shapes.size()); + const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); *sphere = *RHS_SPHERE; @@ -380,8 +398,6 @@ namespace SHADE } default: break; } - - ++shapeIDCounter; } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index 566bae3e..89ba5ff2 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -22,6 +22,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ class SHRigidBody; + class SHAABBTree; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -155,12 +156,13 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ EntityID entityID; - uint32_t shapeIDCounter; // This increments everytime a shape is added to differentiate shapes. bool debugDraw; + bool hasMoved; SHRigidBody* rigidBody; SHCollisionShapeFactory* shapeFactory; + SHAABBTree* broadphase; SHTransform transform; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index b1b94792..4ff94b7d 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -14,6 +14,8 @@ #include "SHPhysicsWorld.h" // Project Headers +#include "Physics/Collision/Narrowphase/SHCollision.h" +#include "Physics/Collision/Narrowphase/SHCollisionDispatch.h" namespace SHADE @@ -25,13 +27,52 @@ namespace SHADE SHPhysicsWorld::SHPhysicsWorld(const WorldSettings& worldSettings) noexcept : settings { worldSettings } { - rigidBodies.clear(); SHLOG_INFO_D("Creating Physics World") } SHPhysicsWorld::~SHPhysicsWorld() noexcept { - rigidBodies.clear(); + + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHPhysicsWorld::TriggerEvents& SHPhysicsWorld::GetTriggerEvents() const noexcept + { + static TriggerEvents triggerEvents; + + triggerEvents.clear(); + + for (auto& [id, state] : triggers) + triggerEvents.emplace_back(SHTriggerEvent{ id, state }); + + return triggerEvents; + } + + const SHPhysicsWorld::CollisionEvents& SHPhysicsWorld::GetCollisionEvents() const noexcept + { + static CollisionEvents collisionEvents; + + collisionEvents.clear(); + + for (auto& [id, manifold] : manifolds) + { + SHCollisionEvent collisionEvent + { + .info = id + , .state = manifold.state + , .normal = manifold.normal + }; + + for (uint32_t i = 0; i < manifold.numContacts; ++i) + collisionEvent.contactPoints[i] = manifold.contacts[i].position; + + collisionEvents.emplace_back(collisionEvent); + } + + return collisionEvents; } /*-----------------------------------------------------------------------------------*/ @@ -40,7 +81,7 @@ namespace SHADE void SHPhysicsWorld::AddRigidBody(SHRigidBody* rigidBody) noexcept { - const bool INSERTED = rigidBodies.emplace(rigidBody).second; + const bool INSERTED = rigidBodies.emplace(rigidBody->entityID, rigidBody).second; if (!INSERTED) { SHLOG_WARNING_D("Attempting to add duplicate rigid body {} to the Physics World!", rigidBody->entityID) @@ -49,37 +90,89 @@ namespace SHADE void SHPhysicsWorld::RemoveRigidBody(SHRigidBody* rigidBody) noexcept { - rigidBodies.erase(rigidBody); + rigidBodies.erase(rigidBody->entityID); - // Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep + // Attempt to remove any invalidated manifolds + if (manifolds.empty()) + return; + + for (auto manifoldPair = manifolds.begin(); manifoldPair != manifolds.end();) + { + const auto& ID = manifoldPair->first; + + const bool MATCHES_A = ID.GetEntityA() == rigidBody->entityID; + const bool MATCHES_B = ID.GetEntityB() == rigidBody->entityID; + + if (MATCHES_A || MATCHES_B) + manifoldPair = manifolds.erase(manifoldPair); + else + ++manifoldPair; + } + + // TODO: Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep } void SHPhysicsWorld::AddCollider(SHCollider* collider) noexcept { - const bool INSERTED = colliders.emplace(collider).second; + const bool INSERTED = colliders.emplace(collider->entityID, collider).second; if (!INSERTED) { SHLOG_WARNING_D("Attempting to add duplicate collider {} to the Physics World!", collider->entityID) + return; } + + collider->broadphase = &broadphase; + + // Add all existing shapes to the broadphase + for (const auto* shape : collider->shapes) + broadphase.Insert(shape->id, shape->ComputeAABB()); } void SHPhysicsWorld::RemoveCollider(SHCollider* collider) noexcept { - colliders.erase(collider); + colliders.erase(collider->entityID); - // Get collider's rigid body - // Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep + const uint32_t NUM_SHAPES = static_cast(collider->shapes.size()); + if (NUM_SHAPES == 0) + return; + + for (uint32_t i = 0; i < NUM_SHAPES; ++i) + { + const SHCollisionShape* SHAPE = collider->shapes[i]; + + broadphase.Remove(SHAPE->id); + + if (SHAPE->IsTrigger()) + removeInvalidatedTrigger(collider->entityID, i); + else + removeInvalidatedManifold(collider->entityID, i); + } + + /* + * TODO: + * Get collider's rigid body + * Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep + */ + + } void SHPhysicsWorld::Step(float dt) { - for (auto* rigidBody : rigidBodies) + // Clear containers of exit state collisions + updateEvents(); + + // TODO: Profile each of these + runBroadphase (); + runNarrowphase(); + + for (auto* rigidBody : rigidBodies | std::views::values) { rigidBody->ComputeWorldData(); integrateForces(*rigidBody, dt); } - for (auto* rigidBody : rigidBodies) + for (auto* rigidBody : rigidBodies | std::views::values) integrateVelocities(*rigidBody, dt); } @@ -158,4 +251,262 @@ namespace SHADE // We clear forces for static bodies as well for redundancy rigidBody.ClearForces(); } + + void SHPhysicsWorld::runBroadphase() noexcept + { + // Update any colliders that have moved + for (auto& collider : colliders | std::views::values) + { + if (!collider->hasMoved) + continue; + + // Clear hasMoved flag here + collider->hasMoved = false; + + // Update moved shapes in broadphase + for (auto* shape : collider->shapes) + broadphase.Update(shape->id, shape->ComputeAABB()); + } + + // Query: Kinematic Triggers, Awake Dynamic Bodies & Dynamic Triggers + for (auto& collider : colliders | std::views::values) + { + // Default static bodies + if (!collider->rigidBody) + continue; + + // Explicit static bodies + const bool IS_STATIC = collider->rigidBody->GetType() == SHRigidBody::Type::STATIC; + if (IS_STATIC) + continue; + + // All remaining are kinematic or dynamic + // Iterate through shapes: if kinematic / dynamic trigger, else if dynamic & awake + if (collider->rigidBody->GetType() == SHRigidBody::Type::KINEMATIC) + queryKinematic(collider); + + if (collider->rigidBody->GetType() == SHRigidBody::Type::DYNAMIC) + queryDynamic(collider); + } + + } + + void SHPhysicsWorld::queryKinematic(SHCollider* collider) noexcept + { + for (auto* shape : collider->shapes) + { + // For kinematic shapes, we only query triggers against everything else + if (!shape->IsTrigger()) + continue; + + auto& potentialCollisions = broadphase.Query(shape->id, shape->ComputeAABB()); + + // Build narrow-phase pairs + auto* shapeA = shape; + + const EntityID ID_A = shape->id.GetEntityID(); + const uint32_t INDEX_A = shape->id.GetShapeIndex(); + + for (auto& id : potentialCollisions) + { + // Get corresponding shape + const EntityID ID_B = id.GetEntityID(); + const uint32_t INDEX_B = id.GetShapeIndex(); + + auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B); + + // Build collision ID + SHCollisionID collisionKey; + collisionKey.SetEntityA(ID_A); + collisionKey.SetEntityB(ID_B); + collisionKey.SetCollisionShapeA(INDEX_A); + collisionKey.SetCollisionShapeB(INDEX_B); + + // Check if it already exists. If it doesn't, put into batch. + auto narrowphasePair = narrowphaseBatch.find(collisionKey); + if (narrowphasePair == narrowphaseBatch.end()) + narrowphaseBatch.emplace(collisionKey, NarrowphasePair{ shapeA, shapeB }); + } + } + } + + void SHPhysicsWorld::queryDynamic(SHCollider* collider) noexcept + { + for (auto* shape : collider->shapes) + { + auto& potentialCollisions = broadphase.Query(shape->id, shape->ComputeAABB()); + + // Build narrow-phase pairs + auto* shapeA = shape; + + const EntityID ID_A = shape->id.GetEntityID(); + const uint32_t INDEX_A = shape->id.GetShapeIndex(); + + for (auto& id : potentialCollisions) + { + // Get corresponding shape + const EntityID ID_B = id.GetEntityID(); + const uint32_t INDEX_B = id.GetShapeIndex(); + + auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B); + + // Build collision ID + SHCollisionID collisionKey; + collisionKey.SetEntityA(ID_A); + collisionKey.SetEntityB(ID_B); + collisionKey.SetCollisionShapeA(INDEX_A); + collisionKey.SetCollisionShapeB(INDEX_B); + + // Check if it already exists. If it doesn't, put into batch. + auto narrowphasePair = narrowphaseBatch.find(collisionKey); + if (narrowphasePair == narrowphaseBatch.end()) + narrowphaseBatch.emplace(collisionKey, NarrowphasePair{ shapeA, shapeB }); + } + } + } + + void SHPhysicsWorld::runNarrowphase() noexcept + { + for (auto& [id, narrowphasePair] : narrowphaseBatch) + { + const bool IS_A_TRIGGER = narrowphasePair.A->IsTrigger(); + const bool IS_B_TRIGGER = narrowphasePair.B->IsTrigger(); + + // Check if ID exists in trigger + if (IS_A_TRIGGER || IS_B_TRIGGER) + collideTriggers(id, narrowphasePair); + else + collideManifolds(id, narrowphasePair); + } + } + + void SHPhysicsWorld::collideTriggers(const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept + { + const auto* A = narrowphasePair.A; + const auto* B = narrowphasePair.B; + + const bool COLLIDING = SHCollisionDispatcher::Collide(*A, *B); + + auto trigger = triggers.find(id); + + // If id not found, emplace new object. + // New object is in the invalid state + if (trigger == triggers.end()) + trigger = triggers.emplace(id, SHCollisionState::INVALID).first; + + SHCollisionState& state = trigger->second; + updateCollisionState(COLLIDING, state); + + // If it was a false positive, remove the manifold immediately. + // Remove using iterator as it is on average faster. + if (state == SHCollisionState::INVALID) + trigger = triggers.erase(trigger); + } + + void SHPhysicsWorld::collideManifolds(const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept + { + auto* A = narrowphasePair.A; + auto* B = narrowphasePair.B; + + SHManifold newManifold { .A = A, .B = B }; + const bool COLLIDING = SHCollisionDispatcher::Collide(newManifold, *A, *B); + + auto manifold = manifolds.find(id); + + // If id not found, emplace new manifold + if (manifold == manifolds.end()) + manifold = manifolds.emplace(id, newManifold).first; + else + { + // TODO: Update existing manifolds with new data + } + + SHCollisionState& state = manifold->second.state; + updateCollisionState(COLLIDING, state); + + // If it was a false positive, remove the manifold immediately. + // Remove using iterator as it is on average faster. + if (state == SHCollisionState::INVALID) + manifold = manifolds.erase(manifold); + } + + void SHPhysicsWorld::updateCollisionState(bool isColliding, SHCollisionState& state) noexcept + { + if (isColliding) + state = state == SHCollisionState::INVALID ? SHCollisionState::ENTER : SHCollisionState::STAY; + else + state = SHCollisionState::EXIT; + } + + void SHPhysicsWorld::updateEvents() noexcept + { + // Clear expired or invalid collisions + for (auto manifold = manifolds.begin(); manifold != manifolds.end();) + { + const auto COLLISION_STATE = manifold->second.state; + + const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT; + const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID; + + if (IS_EXIT || IS_INVALID) + manifold = manifolds.erase(manifold); + else + ++manifold; + } + + // Clear expired or invalid triggers + for (auto trigger = triggers.begin(); trigger != triggers.end();) + { + const auto COLLISION_STATE = trigger->second; + + const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT; + const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID; + + if (IS_EXIT || IS_INVALID) + trigger = triggers.erase(trigger); + else + ++trigger; + } + } + + void SHPhysicsWorld::removeInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) + { + if (triggers.empty()) + return; + + for (auto invalidatedTrigger = triggers.begin(); invalidatedTrigger != triggers.end();) + { + const auto& ID = invalidatedTrigger->first; + + const bool MATCHES_A = ID.GetEntityA() == eid && ID.GetShapeIndexA() == shapeIndex; + const bool MATCHES_B = ID.GetEntityB() == eid && ID.GetShapeIndexB() == shapeIndex; + + if (MATCHES_A || MATCHES_B) + invalidatedTrigger = triggers.erase(invalidatedTrigger); + else + ++invalidatedTrigger; + } + } + + void SHPhysicsWorld::removeInvalidatedManifold(EntityID eid, uint32_t shapeIndex) + { + if (manifolds.empty()) + return; + + for (auto invalidatedManifold = manifolds.begin(); invalidatedManifold != manifolds.end();) + { + const auto& ID = invalidatedManifold->first; + + const bool MATCHES_A = ID.GetEntityA() == eid && ID.GetShapeIndexA() == shapeIndex; + const bool MATCHES_B = ID.GetEntityB() == eid && ID.GetShapeIndexB() == shapeIndex; + + if (MATCHES_A || MATCHES_B) + invalidatedManifold = manifolds.erase(invalidatedManifold); + else + ++invalidatedManifold; + } + } + + + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index 499230dd..f0d2cd5b 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -10,9 +10,12 @@ #pragma once -#include +#include // Project Headers +#include "Physics/Collision/Broadphase/SHDynamicAABBTree.h" +#include "Physics/Collision/Contacts/SHCollisionEvents.h" +#include "Physics/Collision/Contacts/SHManifold.h" #include "Physics/Collision/SHCollider.h" #include "SHRigidBody.h" @@ -44,6 +47,9 @@ namespace SHADE bool sleepingEnabled = true; }; + using TriggerEvents = std::vector; + using CollisionEvents = std::vector; + /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ @@ -54,7 +60,6 @@ namespace SHADE SHPhysicsWorld (const SHPhysicsWorld&) = delete; SHPhysicsWorld (SHPhysicsWorld&&) = delete; - /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ @@ -63,7 +68,14 @@ namespace SHADE SHPhysicsWorld& operator=(SHPhysicsWorld&&) = delete; /*---------------------------------------------------------------------------------*/ - /* Function Members */ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + const TriggerEvents& GetTriggerEvents () const noexcept; + const CollisionEvents& GetCollisionEvents () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ /*---------------------------------------------------------------------------------*/ void AddRigidBody (SHRigidBody* rigidBody) noexcept; @@ -79,27 +91,67 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - //! I realise using a set may be dangerous with pointers. An unordered_map may be better, but I don't need the entityIDs so... + struct NarrowphasePair + { + SHCollisionShape* A = nullptr; + SHCollisionShape* B = nullptr; + }; + + // EntityIDs are used to map resolved contraints back to bodies + using RigidBodies = std::unordered_map; + using Colliders = std::unordered_map; + + // Collisions + + using NarrowphaseBatch = std::unordered_map; + using Manifolds = std::unordered_map; + using Triggers = std::unordered_map; - using Colliders = std::unordered_set; - using RigidBodies = std::unordered_set; /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - WorldSettings settings; + WorldSettings settings; - Colliders colliders; - RigidBodies rigidBodies; + // Containers + + RigidBodies rigidBodies; + Colliders colliders; + + NarrowphaseBatch narrowphaseBatch; + Manifolds manifolds; + Triggers triggers; + + // World components + + SHAABBTree broadphase; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ - void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept; - void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept; + // TODO: Move to island when islands are set up + void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept; + void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept; + + // Broadphase helpers + + void runBroadphase () noexcept; + void queryKinematic (SHCollider* collider) noexcept; + void queryDynamic (SHCollider* collider) noexcept; + + // Narrowphase helpers + + void runNarrowphase () noexcept; + void collideTriggers (const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept; + void collideManifolds (const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept; + void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; + + void updateEvents () noexcept; + void removeInvalidatedTrigger (EntityID eid, uint32_t shapeIndex); + void removeInvalidatedManifold (EntityID eid, uint32_t shapeIndex); }; From 751a16dcc3a64a9f22eae66f11a66d858c766c1a Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 20 Dec 2022 02:13:06 +0800 Subject: [PATCH 029/164] Tested collision detection with collision states --- Assets/Scenes/PhysicsSandbox.shade | 10 ++--- .../src/Application/SBApplication.cpp | 7 ++++ SHADE_Engine/src/Math/Geometry/SHBox.cpp | 2 +- .../Broadphase/SHDynamicAABBTree.cpp | 2 +- .../Collision/Broadphase/SHDynamicAABBTree.h | 10 ++++- .../SHSphereCollisionShape.cpp | 2 +- .../CollisionShapes/SHSphereCollisionShape.h | 6 +-- .../Physics/Collision/Contacts/SHManifold.h | 7 ++-- .../Narrowphase/SHSphereVsSphere.cpp | 10 ++--- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 30 ++++++++++++-- .../src/Physics/Dynamics/SHPhysicsWorld.h | 14 ++++++- .../Routines/SHPhysicsDebugDrawRoutine.cpp | 17 +++++++- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 2 + .../src/Physics/System/SHPhysicsSystem.cpp | 40 +++++++++++++------ .../src/Physics/System/SHPhysicsSystem.h | 13 +++--- 15 files changed, 125 insertions(+), 47 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 8817b62e..21840aca 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -14,7 +14,7 @@ Mass: 10 Drag: 1 Angular Drag: 1 - Use Gravity: false + Use Gravity: true Gravity Scale: 1 Interpolate: true Sleeping Enabled: true @@ -48,7 +48,7 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 2, z: 5} + Position: {x: 0, y: 2, z: 3} Pitch: 0 Yaw: 0 Roll: 0 @@ -70,12 +70,12 @@ Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: - Type: Static + Type: Dynamic Auto Mass: false - Mass: .inf + Mass: 1 Drag: 0.00999999978 Angular Drag: 0.00999999978 - Use Gravity: true + Use Gravity: false Gravity Scale: 1 Interpolate: true Sleeping Enabled: true diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index aa5a4f0e..8607702d 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -185,6 +185,13 @@ namespace Sandbox #endif SHSceneManager::SceneUpdate(0.016f); #ifdef SHEDITOR + static bool drawBP = false; + if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::RIGHT_CTRL)) + { + drawBP = !drawBP; + SHSystemManager::GetSystem()->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE, drawBP); + } + SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, SHFrameRateController::GetRawDeltaTime()); editor->PollPicking(); #else diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.cpp b/SHADE_Engine/src/Math/Geometry/SHBox.cpp index 7261749b..05595fe1 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBox.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHBox.cpp @@ -199,7 +199,7 @@ namespace SHADE bool SHBox::Contains(const SHBox& rhs) const noexcept { - return BoundingBox::Contains(rhs); + return BoundingBox::Contains(rhs) == CONTAINS; } float SHBox::Volume() const noexcept diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp index db30222b..540d5375 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp @@ -107,7 +107,7 @@ namespace SHADE const std::vector& SHAABBTree::GetAABBs() const noexcept { - static std::vector aabbs; + static AABBs aabbs; static std::stack nodeIndices; aabbs.clear(); diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h index 973631a1..f23fd946 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h @@ -28,6 +28,12 @@ namespace SHADE class SH_API SHAABBTree { public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using AABBs = std::vector; + /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ @@ -55,7 +61,7 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] const std::vector& GetAABBs () const noexcept; + [[nodiscard]] const AABBs& GetAABBs () const noexcept; /*---------------------------------------------------------------------------------*/ /* Member Functions */ @@ -116,7 +122,7 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - static constexpr float AABB_EXTENSION = 0.2f; + static constexpr float AABB_EXTENSION = 0.1f; // For quick access std::unordered_map nodeMap; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp index a871a5eb..b1110489 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -125,7 +125,7 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - const SHVec3& SHSphereCollisionShape::GetCenter() const noexcept + SHVec3 SHSphereCollisionShape::GetCenter() const noexcept { return Center; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 1325fc56..eb41f93c 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -72,9 +72,9 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] const SHVec3& GetCenter () const noexcept; - [[nodiscard]] float GetWorldRadius () const noexcept; - [[nodiscard]] float GetRelativeRadius () const noexcept; + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] float GetWorldRadius () const noexcept; + [[nodiscard]] float GetRelativeRadius () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h index da6fe233..0534f749 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h @@ -35,12 +35,13 @@ namespace SHADE SHCollisionShape* A = nullptr; SHCollisionShape* B = nullptr; + uint32_t numContacts = 0; + SHCollisionState state = SHCollisionState::INVALID; + SHVec3 normal; SHVec3 tangents[SHContact::NUM_TANGENTS]; - SHContact contacts[MAX_NUM_CONTACTS]; - uint32_t numContacts = 0; - SHCollisionState state; + }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp index b26a33dd..8b529241 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp @@ -25,8 +25,8 @@ namespace SHADE bool SHCollision::SphereVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - const SHSphereCollisionShape& SPHERE_A = dynamic_cast(A); - const SHSphereCollisionShape& SPHERE_B = dynamic_cast(B); + const SHSphereCollisionShape& SPHERE_A = reinterpret_cast(A); + const SHSphereCollisionShape& SPHERE_B = reinterpret_cast(B); return SHSphere::Intersect(SPHERE_A, SPHERE_B); } @@ -34,10 +34,10 @@ namespace SHADE bool SHCollision::SphereVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { // Convert to spheres - const SHSphereCollisionShape& SPHERE_A = dynamic_cast(A); - const SHSphereCollisionShape& SPHERE_B = dynamic_cast(B); + const SHSphereCollisionShape& SPHERE_A = reinterpret_cast(A); + const SHSphereCollisionShape& SPHERE_B = reinterpret_cast(B); - const SHVec3 A_TO_B = SPHERE_B.GetCenter() - SPHERE_A.GetCenter(); + const SHVec3 A_TO_B = SPHERE_B.GetCenter() - SPHERE_A.GetCenter(); const float DISTANCE_BETWEEN_CENTERS_SQUARED = A_TO_B.LengthSquared(); const float COMBINED_RADIUS = (SPHERE_A.GetWorldRadius() + SPHERE_B.GetWorldRadius()); diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index 4ff94b7d..ff362d69 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -39,6 +39,11 @@ namespace SHADE /* Getter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ + const SHAABBTree::AABBs& SHPhysicsWorld::GetBroadphaseAABBs() const noexcept + { + return broadphase.GetAABBs(); + } + const SHPhysicsWorld::TriggerEvents& SHPhysicsWorld::GetTriggerEvents() const noexcept { static TriggerEvents triggerEvents; @@ -153,8 +158,13 @@ namespace SHADE * Get collider's rigid body * Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep */ + } - + void SHPhysicsWorld::UpdateBroadphase(SHCollider* collider) noexcept + { + auto& shapes = collider->GetCollisionShapes(); + for (auto* shape : shapes) + broadphase.Update(shape->id, shape->ComputeAABB()); } void SHPhysicsWorld::Step(float dt) @@ -367,6 +377,9 @@ namespace SHADE void SHPhysicsWorld::runNarrowphase() noexcept { + if (narrowphaseBatch.empty()) + return; + for (auto& [id, narrowphasePair] : narrowphaseBatch) { const bool IS_A_TRIGGER = narrowphasePair.A->IsTrigger(); @@ -378,6 +391,9 @@ namespace SHADE else collideManifolds(id, narrowphasePair); } + + // Clear every frame + narrowphaseBatch.clear(); } void SHPhysicsWorld::collideTriggers(const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept @@ -432,10 +448,18 @@ namespace SHADE void SHPhysicsWorld::updateCollisionState(bool isColliding, SHCollisionState& state) noexcept { - if (isColliding) + if (isColliding) + { + // New states start at invalid. In the first frame of collision, move to enter. + // If it already in enter, move to stay state = state == SHCollisionState::INVALID ? SHCollisionState::ENTER : SHCollisionState::STAY; + } else - state = SHCollisionState::EXIT; + { + // New states start at invalid. In false positive, remain unchanged. + // If previously colliding, move to exit. + state = state == SHCollisionState::INVALID ? SHCollisionState::INVALID : SHCollisionState::EXIT; + } } void SHPhysicsWorld::updateEvents() noexcept diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index f0d2cd5b..e0e4f796 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -71,8 +71,10 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - const TriggerEvents& GetTriggerEvents () const noexcept; - const CollisionEvents& GetCollisionEvents () const noexcept; + const SHAABBTree::AABBs& GetBroadphaseAABBs () const noexcept; + + const TriggerEvents& GetTriggerEvents () const noexcept; + const CollisionEvents& GetCollisionEvents () const noexcept; /*---------------------------------------------------------------------------------*/ /* Member Functions */ @@ -84,6 +86,14 @@ namespace SHADE void AddCollider (SHCollider* collider) noexcept; void RemoveCollider (SHCollider* collider) noexcept; + /** + * @brief + * Invoke this method to update the broadphase of a collider while the simulation + * is not running. + * @param collider + * The collider to update. + */ + void UpdateBroadphase (SHCollider* collider) noexcept; void Step (float dt); private: diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp index 72b2aad5..b43b5b77 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -18,6 +18,7 @@ #include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" #include "Math/Transform/SHTransformComponent.h" #include "Physics/System/SHPhysicsSystem.h" +#include "Tools/Utilities/SHUtilities.h" namespace SHADE { @@ -40,7 +41,6 @@ namespace SHADE if (!physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::ACTIVE)) return; - auto* physicsSystem = SHSystemManager::GetSystem(); auto* debugDrawSystem = SHSystemManager::GetSystem(); const bool DRAW_COLLIDERS = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::COLLIDERS); @@ -68,6 +68,10 @@ namespace SHADE } } + auto* physicsSystem = SHSystemManager::GetSystem(); + if (!physicsSystem) + return; + if (DRAW_CONTACTS) { // TODO @@ -81,7 +85,16 @@ namespace SHADE if (DRAW_BROADPHASE) { - // TODO + const SHColour& AABB_COLOUR = physicsDebugDrawSystem->DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::BROADPHASE)]; + + auto& broadphaseAABBs = physicsSystem->GetWorld()->GetBroadphaseAABBs(); + + for (auto& aabb : broadphaseAABBs) + { + // Compute AABB Transform + const SHMatrix TRS = SHMatrix::Transform(aabb.GetCenter(), SHQuaternion::Identity, aabb.GetWorldExtents() * 2.0f); + debugDrawSystem->DrawWireCube(TRS, AABB_COLOUR); + } } } diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index 88ea8d79..178a6120 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -65,6 +65,8 @@ namespace SHADE physicsObject.collider->SetScale(WORLD_SCL); physicsObject.collider->RecomputeShapes(); + + physicsSystem->physicsWorld->UpdateBroadphase(physicsObject.collider); } } } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 20084ebe..975c561b 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -41,9 +41,9 @@ namespace SHADE eventFunctions[2] = { &SHPhysicsSystem::onSceneInit , SH_SCENE_INIT_POST }; eventFunctions[3] = { &SHPhysicsSystem::onSceneExit , SH_SCENE_EXIT_POST }; - #ifdef SHEDITOR - eventFunctions[4] = { &SHPhysicsSystem::onEditorPlay , SH_EDITOR_ON_PLAY_EVENT }; - #endif + //#ifdef SHEDITOR + // eventFunctions[4] = { &SHPhysicsSystem::onEditorPlay , SH_EDITOR_ON_PLAY_EVENT }; + //#endif } SHPhysicsSystem::~SHPhysicsSystem() noexcept @@ -55,6 +55,11 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ + const SHPhysicsWorld* SHPhysicsSystem::GetWorld() const noexcept + { + return physicsWorld; + } + double SHPhysicsSystem::GetFixedUpdateRate() const noexcept { return 1.0 / fixedDT; @@ -173,15 +178,6 @@ namespace SHADE // Create the physics world physicsWorld = new SHPhysicsWorld; - #ifdef SHEDITOR - - // Link all entities with the world if editor is already playing. - // This is for handling scene changes while the editor is active. - - const auto* EDITOR = SHSystemManager::GetSystem(); - if (!EDITOR || EDITOR->editorState != SHEditor::State::PLAY) - return onSceneInitEvent.get()->handle; - for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) { if (PHYSICS_OBJECT.rigidBody) @@ -191,7 +187,25 @@ namespace SHADE physicsWorld->AddCollider(PHYSICS_OBJECT.collider); } - #endif + //#ifdef SHEDITOR + + // // Link all entities with the world if editor is already playing. + // // This is for handling scene changes while the editor is active. + // + // const auto* EDITOR = SHSystemManager::GetSystem(); + // if (!EDITOR || EDITOR->editorState != SHEditor::State::PLAY) + // return onSceneInitEvent.get()->handle; + + // for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) + // { + // if (PHYSICS_OBJECT.rigidBody) + // physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); + + // if (PHYSICS_OBJECT.collider) + // physicsWorld->AddCollider(PHYSICS_OBJECT.collider); + // } + + //#endif return onSceneInitEvent.get()->handle; } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index ce1f1396..f9866ebe 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -45,8 +45,9 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] double GetFixedUpdateRate() const noexcept; - [[nodiscard]] double GetFixedDT() const noexcept; + [[nodiscard]] const SHPhysicsWorld* GetWorld () const noexcept; + [[nodiscard]] double GetFixedUpdateRate() const noexcept; + [[nodiscard]] double GetFixedDT () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ @@ -123,11 +124,11 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - #ifdef SHEDITOR - static constexpr int NUM_EVENT_FUNCTIONS = 5; - #else + //#ifdef SHEDITOR + // static constexpr int NUM_EVENT_FUNCTIONS = 5; + //#else static constexpr int NUM_EVENT_FUNCTIONS = 4; - #endif + //#endif // Event function container for cleanly registering to events EventFunctionPair eventFunctions[NUM_EVENT_FUNCTIONS]; From 5def5392a1986250fbb0ddc6d90aa92646fbfad0 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 20 Dec 2022 02:26:31 +0800 Subject: [PATCH 030/164] Cleaned up CollisionKey object --- .../Collision/Contacts/SHCollisionEvents.h | 8 +-- .../{SHCollisionID.cpp => SHCollisionKey.cpp} | 52 ++++++++++++------- .../{SHCollisionID.h => SHCollisionKey.h} | 32 +++++------- .../Collision/Narrowphase/SHCollision.h | 2 +- .../Narrowphase/SHCollisionDispatch.h | 2 +- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 8 +-- .../src/Physics/Dynamics/SHPhysicsWorld.h | 10 ++-- .../src/Physics/System/SHPhysicsSystem.cpp | 49 ++--------------- .../src/Physics/System/SHPhysicsSystem.h | 13 ----- 9 files changed, 66 insertions(+), 110 deletions(-) rename SHADE_Engine/src/Physics/Collision/Contacts/{SHCollisionID.cpp => SHCollisionKey.cpp} (66%) rename SHADE_Engine/src/Physics/Collision/Contacts/{SHCollisionID.h => SHCollisionKey.h} (83%) diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h index 7abb397b..db55ca50 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHCollisionID.h + * \file SHCollisionKey.h * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Interface for Collision Information for Collision & Triggers. * @@ -11,7 +11,7 @@ #pragma once // Project Headers -#include "SHCollisionID.h" +#include "SHCollisionKey.h" namespace SHADE { @@ -37,7 +37,7 @@ namespace SHADE struct SH_API SHTriggerEvent { public: - SHCollisionID info; + SHCollisionKey info; SHCollisionState state; }; @@ -51,7 +51,7 @@ namespace SHADE public: static constexpr int MAX_NUM_CONTACTS = 4; - SHCollisionID info; + SHCollisionKey info; SHCollisionState state; SHVec3 normal; SHVec3 contactPoints[MAX_NUM_CONTACTS]; diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.cpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp similarity index 66% rename from SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.cpp rename to SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp index fae14aca..4bb22697 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.cpp +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHCollisionID.h" +#include "SHCollisionKey.h" // Project Headers #include "Physics/Collision/SHCollider.h" @@ -24,7 +24,7 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollisionID::SHCollisionID() noexcept + SHCollisionKey::SHCollisionKey() noexcept { ids[ENTITY_A] = MAX_EID; ids[ENTITY_B] = MAX_EID; @@ -32,13 +32,13 @@ namespace SHADE ids[SHAPE_INDEX_B] = std::numeric_limits::max(); } - SHCollisionID::SHCollisionID(const SHCollisionID& rhs) noexcept + SHCollisionKey::SHCollisionKey(const SHCollisionKey& rhs) noexcept { value[0] = rhs.value[0]; value[1] = rhs.value[1]; } - SHCollisionID::SHCollisionID(SHCollisionID&& rhs) noexcept + SHCollisionKey::SHCollisionKey(SHCollisionKey&& rhs) noexcept { value[0] = rhs.value[0]; value[1] = rhs.value[1]; @@ -48,7 +48,7 @@ namespace SHADE /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollisionID& SHCollisionID::operator=(const SHCollisionID& rhs) noexcept + SHCollisionKey& SHCollisionKey::operator=(const SHCollisionKey& rhs) noexcept { if (this == &rhs) return *this; @@ -59,7 +59,7 @@ namespace SHADE return *this; } - SHCollisionID& SHCollisionID::operator=(SHCollisionID&& rhs) noexcept + SHCollisionKey& SHCollisionKey::operator=(SHCollisionKey&& rhs) noexcept { value[0] = rhs.value[0]; value[1] = rhs.value[1]; @@ -67,7 +67,7 @@ namespace SHADE return *this; } - bool SHCollisionID::operator==(const SHCollisionID& rhs) const + bool SHCollisionKey::operator==(const SHCollisionKey& rhs) const { // When checking for equal, check both ways. // Exact Match (A, idxA, B, idxB) @@ -83,43 +83,43 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - EntityID SHCollisionID::GetEntityA() const noexcept + EntityID SHCollisionKey::GetEntityA() const noexcept { return ids[ENTITY_A]; } - EntityID SHCollisionID::GetEntityB() const noexcept + EntityID SHCollisionKey::GetEntityB() const noexcept { return ids[ENTITY_B]; } - uint32_t SHCollisionID::GetShapeIndexA() const noexcept + uint32_t SHCollisionKey::GetShapeIndexA() const noexcept { return ids[SHAPE_INDEX_A]; } - uint32_t SHCollisionID::GetShapeIndexB() const noexcept + uint32_t SHCollisionKey::GetShapeIndexB() const noexcept { return ids[SHAPE_INDEX_B]; } - const SHRigidBodyComponent* SHCollisionID::GetRigidBodyA() const noexcept + const SHRigidBodyComponent* SHCollisionKey::GetRigidBodyA() const noexcept { return SHComponentManager::GetComponent_s(ids[ENTITY_A]); } - const SHRigidBodyComponent* SHCollisionID::GetRigidBodyB() const noexcept + const SHRigidBodyComponent* SHCollisionKey::GetRigidBodyB() const noexcept { return SHComponentManager::GetComponent_s(ids[ENTITY_B]); } - const SHCollisionShape* SHCollisionID::GetCollisionShapeA() const noexcept + const SHCollisionShape* SHCollisionKey::GetCollisionShapeA() const noexcept { const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_A]); return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[SHAPE_INDEX_A]); } - const SHCollisionShape* SHCollisionID::GetCollisionShapeB() const noexcept + const SHCollisionShape* SHCollisionKey::GetCollisionShapeB() const noexcept { const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_B]); return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[SHAPE_INDEX_B]); @@ -129,24 +129,38 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollisionID::SetEntityA(EntityID entityID) noexcept + void SHCollisionKey::SetEntityA(EntityID entityID) noexcept { ids[ENTITY_A] = entityID; } - void SHCollisionID::SetEntityB(EntityID entityID) noexcept + void SHCollisionKey::SetEntityB(EntityID entityID) noexcept { ids[ENTITY_B] = entityID; } - void SHCollisionID::SetCollisionShapeA(uint32_t shapeIndexA) noexcept + void SHCollisionKey::SetCollisionShapeA(uint32_t shapeIndexA) noexcept { ids[SHAPE_INDEX_A] = shapeIndexA; } - void SHCollisionID::SetCollisionShapeB(uint32_t shapeIndexB) noexcept + void SHCollisionKey::SetCollisionShapeB(uint32_t shapeIndexB) noexcept { ids[SHAPE_INDEX_B] = shapeIndexB; } + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + std::size_t SHCollisionKeyHash::operator()(const SHCollisionKey& id) const + { + static constexpr int NUM_IDS = ARRAYSIZE(id.ids); + + // Hashable is not a word. Sue me. + auto hashablePtr = reinterpret_cast::const_pointer>(id.ids); + return std::hash{}(std::u32string_view(hashablePtr, NUM_IDS)); + } + + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.h similarity index 83% rename from SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.h rename to SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.h index 78b28a96..811f8375 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionID.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.h @@ -21,7 +21,7 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ - struct SHCollisionIDHash; + struct SHCollisionKeyHash; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -29,37 +29,36 @@ namespace SHADE /** * @brief - * Encapsulates the information when two colliders intersect and do not have physical - * resolution. + * Encapsulates the information when two collision shapes intersect. */ - class SH_API SHCollisionID + class SH_API SHCollisionKey { private: /*---------------------------------------------------------------------------------*/ /* Friends */ /*---------------------------------------------------------------------------------*/ - friend struct SHCollisionIDHash; + friend struct SHCollisionKeyHash; public: /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHCollisionID () noexcept; - SHCollisionID (const SHCollisionID& rhs) noexcept; - SHCollisionID (SHCollisionID&& rhs) noexcept; + SHCollisionKey () noexcept; + SHCollisionKey (const SHCollisionKey& rhs) noexcept; + SHCollisionKey (SHCollisionKey&& rhs) noexcept; - ~SHCollisionID () noexcept = default; + ~SHCollisionKey () noexcept = default; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHCollisionID& operator= (const SHCollisionID& rhs) noexcept; - SHCollisionID& operator= (SHCollisionID&& rhs) noexcept; + SHCollisionKey& operator= (const SHCollisionKey& rhs) noexcept; + SHCollisionKey& operator= (SHCollisionKey&& rhs) noexcept; - bool operator==(const SHCollisionID& rhs) const; + bool operator==(const SHCollisionKey& rhs) const; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ @@ -103,19 +102,16 @@ namespace SHADE /** * @brief - * Encapsulates a functor to hash a CollisionID + * Encapsulates a functor to hash a CollisionKey */ - struct SHCollisionIDHash + struct SHCollisionKeyHash { public: /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ - inline std::size_t operator()(const SHCollisionID& id) const - { - return std::hash{}(std::u32string_view(reinterpret_cast::const_pointer>(id.ids), 4)); - } + std::size_t operator()(const SHCollisionKey& id) const; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index befb16f6..a64c101c 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -13,7 +13,7 @@ // Project Headers #include "Physics/Collision/CollisionShapes/SHCollisionShape.h" #include "Physics/Collision/Contacts/SHManifold.h" -#include "Physics/Collision/Contacts/SHCollisionID.h" +#include "Physics/Collision/Contacts/SHCollisionKey.h" namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h index 4c132746..2dea7f1d 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h @@ -12,7 +12,7 @@ // Project Headers #include "Physics/Collision/Contacts/SHManifold.h" -#include "Physics/Collision/Contacts/SHCollisionID.h" +#include "Physics/Collision/Contacts/SHCollisionKey.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index ff362d69..8d0b69f8 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -326,7 +326,7 @@ namespace SHADE auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B); // Build collision ID - SHCollisionID collisionKey; + SHCollisionKey collisionKey; collisionKey.SetEntityA(ID_A); collisionKey.SetEntityB(ID_B); collisionKey.SetCollisionShapeA(INDEX_A); @@ -361,7 +361,7 @@ namespace SHADE auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B); // Build collision ID - SHCollisionID collisionKey; + SHCollisionKey collisionKey; collisionKey.SetEntityA(ID_A); collisionKey.SetEntityB(ID_B); collisionKey.SetCollisionShapeA(INDEX_A); @@ -396,7 +396,7 @@ namespace SHADE narrowphaseBatch.clear(); } - void SHPhysicsWorld::collideTriggers(const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept + void SHPhysicsWorld::collideTriggers(const SHCollisionKey& id, NarrowphasePair& narrowphasePair) noexcept { const auto* A = narrowphasePair.A; const auto* B = narrowphasePair.B; @@ -419,7 +419,7 @@ namespace SHADE trigger = triggers.erase(trigger); } - void SHPhysicsWorld::collideManifolds(const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept + void SHPhysicsWorld::collideManifolds(const SHCollisionKey& id, NarrowphasePair& narrowphasePair) noexcept { auto* A = narrowphasePair.A; auto* B = narrowphasePair.B; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index e0e4f796..a0743a30 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -113,9 +113,9 @@ namespace SHADE // Collisions - using NarrowphaseBatch = std::unordered_map; - using Manifolds = std::unordered_map; - using Triggers = std::unordered_map; + using NarrowphaseBatch = std::unordered_map; + using Manifolds = std::unordered_map; + using Triggers = std::unordered_map; @@ -155,8 +155,8 @@ namespace SHADE // Narrowphase helpers void runNarrowphase () noexcept; - void collideTriggers (const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept; - void collideManifolds (const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept; + void collideTriggers (const SHCollisionKey& id, NarrowphasePair& narrowphasePair) noexcept; + void collideManifolds (const SHCollisionKey& id, NarrowphasePair& narrowphasePair) noexcept; void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; void updateEvents () noexcept; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 975c561b..65bbc5d4 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -40,10 +40,6 @@ namespace SHADE eventFunctions[1] = { &SHPhysicsSystem::onComponentRemoved, SH_COMPONENT_REMOVED_EVENT }; eventFunctions[2] = { &SHPhysicsSystem::onSceneInit , SH_SCENE_INIT_POST }; eventFunctions[3] = { &SHPhysicsSystem::onSceneExit , SH_SCENE_EXIT_POST }; - - //#ifdef SHEDITOR - // eventFunctions[4] = { &SHPhysicsSystem::onEditorPlay , SH_EDITOR_ON_PLAY_EVENT }; - //#endif } SHPhysicsSystem::~SHPhysicsSystem() noexcept @@ -178,6 +174,10 @@ namespace SHADE // Create the physics world physicsWorld = new SHPhysicsWorld; + // Immediately add all existing bodies and colliders to the world. + // Since we recreated the scene and the world, the initial data has been reset and determinism is guaranteed. + // Only if the current scene data changes, then so would the results of the simulation. + for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) { if (PHYSICS_OBJECT.rigidBody) @@ -187,26 +187,6 @@ namespace SHADE physicsWorld->AddCollider(PHYSICS_OBJECT.collider); } - //#ifdef SHEDITOR - - // // Link all entities with the world if editor is already playing. - // // This is for handling scene changes while the editor is active. - // - // const auto* EDITOR = SHSystemManager::GetSystem(); - // if (!EDITOR || EDITOR->editorState != SHEditor::State::PLAY) - // return onSceneInitEvent.get()->handle; - - // for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) - // { - // if (PHYSICS_OBJECT.rigidBody) - // physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); - - // if (PHYSICS_OBJECT.collider) - // physicsWorld->AddCollider(PHYSICS_OBJECT.collider); - // } - - //#endif - return onSceneInitEvent.get()->handle; } @@ -262,7 +242,6 @@ namespace SHADE physicsWorld->AddRigidBody(rigidBody); } } - if (IS_COLLIDER) { @@ -323,24 +302,4 @@ namespace SHADE return onComponentRemovedEvent.get()->handle; } -#ifdef SHEDITOR - - SHEventHandle SHPhysicsSystem::onEditorPlay(SHEventPtr onEditorPlayEvent) - { - // Add all physics components to the physics world - for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) - { - // Add rigid body if it exists - if (PHYSICS_OBJECT.rigidBody) - physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); - - if (PHYSICS_OBJECT.collider) - physicsWorld->AddCollider(PHYSICS_OBJECT.collider); - } - - return onEditorPlayEvent.get()->handle; - } - -#endif - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index f9866ebe..fca12498 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -124,11 +124,7 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - //#ifdef SHEDITOR - // static constexpr int NUM_EVENT_FUNCTIONS = 5; - //#else static constexpr int NUM_EVENT_FUNCTIONS = 4; - //#endif // Event function container for cleanly registering to events EventFunctionPair eventFunctions[NUM_EVENT_FUNCTIONS]; @@ -152,14 +148,5 @@ namespace SHADE SHEventHandle onComponentAdded (SHEventPtr onComponentAddedEvent); SHEventHandle onComponentRemoved (SHEventPtr onComponentRemovedEvent); - - - #ifdef SHEDITOR - - SHEventHandle onEditorPlay (SHEventPtr onEditorPlayEvent); - // We don't need an onEditorStop because on stop exits the scene, which is already handled above. - - #endif - }; } // namespace SHADE \ No newline at end of file From b402a44d95c99fbd0afd4289043dabe4dfc20677 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 20 Dec 2022 17:26:27 +0800 Subject: [PATCH 031/164] Added SHAnimationClip stub --- .../src/Animation/SHAnimationClip.cpp | 32 +++++++++ SHADE_Engine/src/Animation/SHAnimationClip.h | 71 +++++++++++++++++++ .../src/Animation/SHAnimatorComponent.cpp | 22 ++++++ .../src/Animation/SHAnimatorComponent.h | 5 ++ 4 files changed, 130 insertions(+) create mode 100644 SHADE_Engine/src/Animation/SHAnimationClip.cpp create mode 100644 SHADE_Engine/src/Animation/SHAnimationClip.h diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp new file mode 100644 index 00000000..1f199b3f --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -0,0 +1,32 @@ +/************************************************************************************//*! +\file SHAnimationClip.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 20, 2022 +\brief Contains the function definitions of the SHAnimationClip class. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Pre-compiled Header +#include "SHpch.h" +// Primary Header +#include "SHAnimationClip.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------------*/ + + + /*-----------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------------*/ + + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------------*/ + +} \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h new file mode 100644 index 00000000..ed1b7b81 --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -0,0 +1,71 @@ +/************************************************************************************//*! +\file SHAnimationClip.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Dec 12, 2022 +\brief Contains the definition of the SHAnimationClip struct and related types. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// STL Includes + +// Project Includes +#include "Math/SHMatrix.h" +#include "Assets/Asset Types/Models/SHAnimationAsset.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + struct SHAnimationKeyFrame + { + float TimeStamp; + SHVec3 Position; + SHQuaternion Orientation; + SHVec3 Scale; + SHMatrix TransformationMatrix; + }; + + class SHAnimationClip + { + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + struct Channel + { + std::string Name; + std::vector KeyFrames; + }; + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + explicit SHAnimationClip(const SHAnimAsset& asset); + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + std::vector Channels; + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 61c0e6af..debb2fb2 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -19,6 +19,28 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*-----------------------------------------------------------------------------------*/ + void SHAnimatorComponent::SetRig(Handle newRig) + { + // Same rig, don't bother + if (rig == newRig) + return; + + rig = newRig; + + // Populate bone matrices based on new rig's default pose + if (rig) + { + + } + else + { + boneMatrices.clear(); + } + } + /*-----------------------------------------------------------------------------------*/ /* Update Functions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 9e596130..446014cc 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -45,6 +45,11 @@ namespace SHADE void Pause(); void Stop();*/ + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + void SetRig(Handle newRig); + /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ From b58b475c04cb0496515e576be9d7f1e057554eb8 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 20 Dec 2022 23:10:23 +0800 Subject: [PATCH 032/164] Separated collision detection and added contact manager --- SHADE_Engine/src/Math/SHMathHelpers.h | 12 +- .../Collision/Broadphase/SHDynamicAABBTree.h | 2 +- .../CollisionShapes/SHCollisionShape.h | 2 +- .../src/Physics/Collision/SHCollider.cpp | 34 ++ .../src/Physics/Collision/SHCollider.h | 6 +- .../src/Physics/Collision/SHCollisionInfo.cpp | 100 ----- .../src/Physics/Collision/SHCollisionInfo.h | 102 ----- .../Physics/Collision/SHCollisionSpace.cpp | 223 +++++++++ .../src/Physics/Collision/SHCollisionSpace.h | 131 ++++++ .../src/Physics/Dynamics/SHContactManager.cpp | 173 +++++++ .../src/Physics/Dynamics/SHContactManager.h | 103 +++++ .../src/Physics/Dynamics/SHContactManager.hpp | 61 +++ .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 425 ++---------------- .../src/Physics/Dynamics/SHPhysicsWorld.h | 99 ++-- .../Routines/SHPhysicsDebugDrawRoutine.cpp | 5 +- .../Routines/SHPhysicsPostUpdateRoutine.cpp | 13 +- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 50 ++- .../src/Physics/System/SHPhysicsSystem.cpp | 99 +++- .../src/Physics/System/SHPhysicsSystem.h | 12 +- .../System/SHPhysicsSystemInterface.cpp | 56 +-- .../Physics/System/SHPhysicsSystemInterface.h | 12 +- SHADE_Managed/src/Scripts/ScriptStore.cxx | 25 +- 22 files changed, 1011 insertions(+), 734 deletions(-) delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h create mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactManager.h create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp diff --git a/SHADE_Engine/src/Math/SHMathHelpers.h b/SHADE_Engine/src/Math/SHMathHelpers.h index 427011a6..b053beff 100644 --- a/SHADE_Engine/src/Math/SHMathHelpers.h +++ b/SHADE_Engine/src/Math/SHMathHelpers.h @@ -46,14 +46,16 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /** Standard Epsilon value for comparing Single-Precision Floating-Point values. */ - static constexpr float EPSILON = 0.001f; + static constexpr float EPSILON = 0.001f; /** Single-Precision Floating-Point value of infinity */ - static constexpr float INF = std::numeric_limits::infinity(); + static constexpr float INF = std::numeric_limits::infinity(); - static constexpr float PI = std::numbers::pi_v; - static constexpr float HALF_PI = PI * 0.5f; - static constexpr float TWO_PI = 2.0f * PI; + static constexpr float PI = std::numbers::pi_v; + static constexpr float HALF_PI = PI * 0.5f; + static constexpr float TWO_PI = 2.0f * PI; + + static constexpr float EULER_CONSTANT = std::numbers::egamma_v; /*---------------------------------------------------------------------------------*/ /* Static Function Members */ diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h index f23fd946..420f30e7 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h @@ -122,7 +122,7 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - static constexpr float AABB_EXTENSION = 0.1f; + static constexpr float AABB_EXTENSION = 0.2f; // For quick access std::unordered_map nodeMap; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index dad59881..6e779eea 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -37,7 +37,7 @@ namespace SHADE friend class SHCollider; friend class SHColliderComponent; friend class SHCollisionShapeFactory; - friend class SHPhysicsWorld; + friend class SHCollisionSpace; public: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index 24ba26b7..928945c9 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -29,6 +29,7 @@ namespace SHADE SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept : entityID { eid } + , active { true } , debugDraw { false } , hasMoved { true } , rigidBody { nullptr } @@ -39,6 +40,7 @@ namespace SHADE SHCollider::SHCollider(const SHCollider& rhs) noexcept : entityID { rhs.entityID } + , active { rhs.active } , debugDraw { rhs.debugDraw } , hasMoved { rhs.hasMoved } , rigidBody { rhs.rigidBody } @@ -57,6 +59,7 @@ namespace SHADE SHCollider::SHCollider(SHCollider&& rhs) noexcept : entityID { rhs.entityID } + , active { rhs.active } , debugDraw { rhs.debugDraw } , hasMoved { rhs.hasMoved } , rigidBody { rhs.rigidBody } @@ -101,6 +104,7 @@ namespace SHADE } entityID = rhs.entityID; + active = rhs.active; debugDraw = rhs.debugDraw; hasMoved = rhs.hasMoved; rigidBody = rhs.rigidBody; @@ -122,6 +126,7 @@ namespace SHADE } entityID = rhs.entityID; + active = rhs.active; debugDraw = rhs.debugDraw; hasMoved = rhs.hasMoved; rigidBody = rhs.rigidBody; @@ -138,6 +143,16 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ + EntityID SHCollider::GetEntityID() const noexcept + { + return entityID; + } + + bool SHCollider::IsActive() const noexcept + { + return active; + } + bool SHCollider::GetDebugDrawState() const noexcept { return debugDraw; @@ -182,6 +197,25 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ + void SHCollider::SetIsActive(bool state) noexcept + { + if (active == state) + return; + + active = state; + + if (!broadphase) + return; + + for (auto* shape : shapes) + { + if (active) // Previously inactive + broadphase->Insert(shape->id, shape->ComputeAABB()); + else // Previously active + broadphase->Remove(shape->id); + } + } + void SHCollider::SetDebugDrawState(bool state) noexcept { debugDraw = state; diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index 89ba5ff2..c31be5eb 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -40,7 +40,7 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ - friend class SHPhysicsWorld; + friend class SHCollisionSpace; public: /*---------------------------------------------------------------------------------*/ @@ -79,6 +79,8 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ + [[nodiscard]] EntityID GetEntityID () const noexcept; + [[nodiscard]] bool IsActive () const noexcept; [[nodiscard]] bool GetDebugDrawState () const noexcept; [[nodiscard]] const SHTransform& GetTransform () const noexcept; @@ -93,6 +95,7 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ + void SetIsActive (bool state) noexcept; void SetDebugDrawState (bool state) noexcept; void SetRigidBody (SHRigidBody* rb) noexcept; @@ -157,6 +160,7 @@ namespace SHADE EntityID entityID; + bool active; bool debugDraw; bool hasMoved; diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp deleted file mode 100644 index a28686e0..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionInfo.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for Collision Info. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollisionInfo.h" - -// Project Headers -#include "Physics/Collision/SHCollider.h" -#include "Physics/Interface/SHColliderComponent.h" - - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollisionInfo::SHCollisionInfo() noexcept - : collisionState { State::INVALID } - { - ids[ENTITY_A] = MAX_EID; - ids[ENTITY_B] = MAX_EID; - ids[COLLIDER_A] = std::numeric_limits::max(); - ids[COLLIDER_B] = std::numeric_limits::max(); - } - - SHCollisionInfo::SHCollisionInfo(EntityID entityA, EntityID entityB) noexcept - : collisionState { State::INVALID } - { - ids[ENTITY_A] = entityA; - ids[ENTITY_B] = entityB; - ids[COLLIDER_A] = std::numeric_limits::max(); - ids[COLLIDER_B] = std::numeric_limits::max(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHCollisionInfo::operator==(const SHCollisionInfo& rhs) const noexcept - { - return value[0] == rhs.value[0] && value[1] == rhs.value[1]; - } - - bool SHCollisionInfo::operator!=(const SHCollisionInfo& rhs) const noexcept - { - return value[0] != rhs.value[0] || value[1] != rhs.value[1]; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - EntityID SHCollisionInfo::GetEntityA() const noexcept - { - return ids[ENTITY_A]; - } - - EntityID SHCollisionInfo::GetEntityB() const noexcept - { - return ids[ENTITY_B]; - } - - const SHRigidBodyComponent* SHCollisionInfo::GetRigidBodyA() const noexcept - { - return SHComponentManager::GetComponent_s(ids[ENTITY_A]); - } - - const SHRigidBodyComponent* SHCollisionInfo::GetRigidBodyB() const noexcept - { - return SHComponentManager::GetComponent_s(ids[ENTITY_B]); - } - - const SHCollisionShape* SHCollisionInfo::GetColliderA() const noexcept - { - const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_A]); - return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[COLLIDER_A]); - } - - const SHCollisionShape* SHCollisionInfo::GetColliderB() const noexcept - { - const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_B]); - return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[COLLIDER_B]); - } - - SHCollisionInfo::State SHCollisionInfo::GetCollisionState() const noexcept - { - return collisionState; - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h deleted file mode 100644 index d2dad647..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionInfo.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for Collision Information for Collision & Triggers. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "Physics/Interface/SHColliderComponent.h" -#include "Physics/Interface/SHRigidBodyComponent.h" - - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - class SH_API SHCollisionInfo - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHCollisionListener; - - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - enum class State - { - ENTER - , STAY - , EXIT - - , TOTAL - , INVALID = -1 - }; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHCollisionInfo () noexcept; - SHCollisionInfo (EntityID entityA, EntityID entityB) noexcept; - - - SHCollisionInfo (const SHCollisionInfo& rhs) = default; - SHCollisionInfo (SHCollisionInfo&& rhs) = default; - ~SHCollisionInfo () = default; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - bool operator== (const SHCollisionInfo& rhs) const noexcept; - bool operator!= (const SHCollisionInfo& rhs) const noexcept; - - SHCollisionInfo& operator= (const SHCollisionInfo& rhs) = default; - SHCollisionInfo& operator= (SHCollisionInfo&& rhs) = default; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] EntityID GetEntityA () const noexcept; - [[nodiscard]] EntityID GetEntityB () const noexcept; - [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyA () const noexcept; - [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyB () const noexcept; - [[nodiscard]] const SHCollisionShape* GetColliderA () const noexcept; - [[nodiscard]] const SHCollisionShape* GetColliderB () const noexcept; - [[nodiscard]] State GetCollisionState () const noexcept; - - private: - - static constexpr uint32_t ENTITY_A = 0; - static constexpr uint32_t ENTITY_B = 1; - static constexpr uint32_t COLLIDER_A = 2; - static constexpr uint32_t COLLIDER_B = 3; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - union - { - uint64_t value[2]; // EntityValue, ColliderIndexValue - uint32_t ids [4]; // EntityA, EntityB, ColliderIndexA, ColliderIndexB - }; - - State collisionState; - }; - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp new file mode 100644 index 00000000..bc76d14a --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp @@ -0,0 +1,223 @@ +/**************************************************************************************** + * \file SHCollisionSpace.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Collision Space that handles collision detetction. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + + +#include + +// Primary Header +#include "SHCollisionSpace.h" + +#include "Narrowphase/SHCollisionDispatch.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHAABBTree::AABBs& SHCollisionSpace::GetBroadphaseAABBs() const noexcept + { + return broadphase.GetAABBs(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionSpace::SetContactManager(SHContactManager* _contactManager) noexcept + { + contactManager = _contactManager; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionSpace::AddCollider(SHCollider* collider) noexcept + { + const bool INSERTED = colliders.emplace(collider->entityID, collider).second; + if (!INSERTED) + { + SHLOG_WARNING_D("Attempting to add duplicate collider {} to the Physics World!", collider->entityID) + return; + } + + collider->broadphase = &broadphase; + + // Add all existing shapes to the broadphase + for (const auto* shape : collider->shapes) + broadphase.Insert(shape->id, shape->ComputeAABB()); + } + + void SHCollisionSpace::RemoveCollider(SHCollider* collider) noexcept + { + colliders.erase(collider->entityID); + + const uint32_t NUM_SHAPES = static_cast(collider->shapes.size()); + if (NUM_SHAPES == 0) + return; + + for (uint32_t i = 0; i < NUM_SHAPES; ++i) + broadphase.Remove(collider->shapes[i]->id); + + if (contactManager) + { + contactManager->RemoveInvalidatedTrigger(collider->entityID); + contactManager->RemoveInvalidatedManifold(collider->entityID); + } + + /* + * TODO: + * Get collider's rigid body + * Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep + */ + } + + void SHCollisionSpace::UpdateBroadphase() noexcept + { + // Update any colliders that have moved + for (auto& collider : colliders | std::views::values) + { + const bool IS_ACTIVE = collider->active; + const bool HAS_MOVED = collider->hasMoved; + + if (!IS_ACTIVE || !HAS_MOVED) + continue; + + // Clear hasMoved flag here + collider->hasMoved = false; + + // Update moved shapes in broadphase + for (auto* shape : collider->shapes) + broadphase.Update(shape->id, shape->ComputeAABB()); + } + } + + void SHCollisionSpace::DetectCollisions() noexcept + { + /* + * Broad-phase + */ + + // Broadphase Queries: Kinematic Triggers, Awake Dynamic Bodies & Dynamic Triggers + for (auto& collider : colliders | std::views::values) + { + // Colliders without bodies are considered to be static bodies + // This is specific to this engine because of Unity's stupid convention. + const bool IS_IMPLICIT_STATIC = !collider->rigidBody; + const bool IS_EXPLICIT_STATIC = collider->rigidBody->GetType() == SHRigidBody::Type::STATIC; + const bool IS_ACTIVE = collider->active; + + // Skip inactive colliders + if (!IS_ACTIVE || IS_IMPLICIT_STATIC || IS_EXPLICIT_STATIC) + continue; + + // All remaining are kinematic or dynamic + // Iterate through shapes: if kinematic / dynamic trigger, else if dynamic & awake + // Results are loaded into the narrowphase batch + broadphaseQuery(collider->rigidBody->GetType(), collider); + } + + /* + * Narrow-phase + */ + + // If no potential collisions, we can skip the entire narrow phase. No further updates necessary. + // All contact / trigger states persist in this step. + if (narrowphaseBatch.empty()) + return; + + // All narrowphase IDs are unique, there should be no duplicate collision checks. + // This applies both ways: A -> B and B -> A. + for (auto& [key, narrowphasePair] : narrowphaseBatch) + { + const bool IS_A_TRIGGER = narrowphasePair.A->IsTrigger(); + const bool IS_B_TRIGGER = narrowphasePair.B->IsTrigger(); + + if (IS_A_TRIGGER || IS_B_TRIGGER) + collideTriggers(key, narrowphasePair); + else + collideManifolds(key, narrowphasePair); + } + + // Clear every frame + narrowphaseBatch.clear(); + } + + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionSpace::broadphaseQuery(SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept + { + for (auto* shape : collider->shapes) + { + // For kinematic shapes, we only query triggers against everything else + if (rigidBodyType == SHRigidBody::Type::KINEMATIC && !shape->IsTrigger()) + continue; + + auto& potentialCollisions = broadphase.Query(shape->id, shape->ComputeAABB()); + + // Build narrow-phase pairs + auto* shapeA = shape; + + const EntityID ID_A = shape->id.GetEntityID(); + const uint32_t INDEX_A = shape->id.GetShapeIndex(); + + for (auto& id : potentialCollisions) + { + // Get corresponding shape + const EntityID ID_B = id.GetEntityID(); + const uint32_t INDEX_B = id.GetShapeIndex(); + + auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B); + + // Build collision ID + SHCollisionKey collisionKey; + collisionKey.SetEntityA(ID_A); + collisionKey.SetEntityB(ID_B); + collisionKey.SetCollisionShapeA(INDEX_A); + collisionKey.SetCollisionShapeB(INDEX_B); + + // Check if it already exists. If it doesn't, put into batch. + // The overloaded equality operator ensures no duplicate collision tests are performed. + auto narrowphasePair = narrowphaseBatch.find(collisionKey); + if (narrowphasePair == narrowphaseBatch.end()) + narrowphaseBatch.emplace(collisionKey, NarrowphasePair{ shapeA, shapeB }); + } + } + } + + void SHCollisionSpace::collideTriggers(const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept + { + const auto* A = narrowphasePair.A; + const auto* B = narrowphasePair.B; + + const bool COLLIDING = SHCollisionDispatcher::Collide(*A, *B); + + // Send results to contact manager + if (contactManager) + contactManager->AddTrigger(COLLIDING, key); + } + + void SHCollisionSpace::collideManifolds(const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept + { + auto* A = narrowphasePair.A; + auto* B = narrowphasePair.B; + + SHManifold newManifold { .A = A, .B = B }; + const bool COLLIDING = SHCollisionDispatcher::Collide(newManifold, *A, *B); + + // Send results to contact manager + if (contactManager) + contactManager->AddManifold(COLLIDING, key, newManifold); + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h new file mode 100644 index 00000000..d89404dd --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h @@ -0,0 +1,131 @@ +/**************************************************************************************** + * \file SHCollisionSpace.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Collision Space that handles collision detetction. + * This is to separate the logic between dynamics and collision detection, + * but the collision space does send information to the contact manager + * for dynamic resolution and collision state reporting. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Broadphase/SHDynamicAABBTree.h" +#include "Contacts/SHCollisionEvents.h" +#include "Physics/Dynamics/SHContactManager.h" +#include "SHCollider.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Allows collision detection to be performed with the use of colliders & collision shapes. + * The space will generate manifold data for resolution when needed. + */ + class SH_API SHCollisionSpace + { + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionSpace () noexcept = default; + ~SHCollisionSpace () noexcept = default; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + const SHAABBTree::AABBs& GetBroadphaseAABBs () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetContactManager(SHContactManager* contactManager) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Adds a collider to the collision space for it to be tested for collision with + * other colliders. + * @param collider + * A collider to add. Duplicates will be ignored. + */ + void AddCollider (SHCollider* collider) noexcept; + + /** + * @brief + * Removes a collider from the collision space. This will prevent any collisions + * being detected between it and other colliders unless manually tested. + * @param collider + * A collider to remove. If a reference to it doesn't exist, it will be ignored. + */ + void RemoveCollider (SHCollider* collider) noexcept; + + /** + * @brief + * Invoke this method to update the broadphase of colliders that have been moved since + * the last frame. + */ + void UpdateBroadphase () noexcept; + + /** + * @brief + * Detects collisions between all colliders. Results are sent to the attached contact + * manager for resolution. + */ + void DetectCollisions () noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + struct NarrowphasePair + { + SHCollisionShape* A = nullptr; + SHCollisionShape* B = nullptr; + }; + + using Colliders = std::unordered_map; + using NarrowphaseBatch = std::unordered_map; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + SHContactManager* contactManager = nullptr; + + Colliders colliders; + NarrowphaseBatch narrowphaseBatch; + + SHAABBTree broadphase; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + // Broadphase helpers + + void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; + + // Narrowphase helpers + + void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; + void collideManifolds (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; + + }; + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp new file mode 100644 index 00000000..be65128d --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -0,0 +1,173 @@ +/**************************************************************************************** + * \file SHContactManager.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Contact Manager that stores collision information and + * resolves contact constraints. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHContactManager.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHContactManager::TriggerEvents& SHContactManager::GetTriggerEvents() const noexcept + { + static TriggerEvents triggerEvents; + + triggerEvents.clear(); + + for (auto& [id, state] : triggers) + triggerEvents.emplace_back(SHTriggerEvent{ id, state }); + + return triggerEvents; + } + + const SHContactManager::CollisionEvents& SHContactManager::GetCollisionEvents() const noexcept + { + static CollisionEvents collisionEvents; + + collisionEvents.clear(); + + for (auto& [id, manifold] : manifolds) + { + SHCollisionEvent collisionEvent + { + .info = id + , .state = manifold.state + , .normal = manifold.normal + }; + + for (uint32_t i = 0; i < manifold.numContacts; ++i) + collisionEvent.contactPoints[i] = manifold.contacts[i].position; + + collisionEvents.emplace_back(collisionEvent); + } + + return collisionEvents; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHContactManager::Update() noexcept + { + // Clear expired or invalid collisions + for (auto manifold = manifolds.begin(); manifold != manifolds.end();) + { + const auto COLLISION_STATE = manifold->second.state; + + const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT; + const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID; + + if (IS_EXIT || IS_INVALID) + manifold = manifolds.erase(manifold); + else + ++manifold; + } + + // Clear expired or invalid triggers + for (auto trigger = triggers.begin(); trigger != triggers.end();) + { + const auto COLLISION_STATE = trigger->second; + + const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT; + const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID; + + if (IS_EXIT || IS_INVALID) + trigger = triggers.erase(trigger); + else + ++trigger; + } + } + + void SHContactManager::AddTrigger(bool isColliding, const SHCollisionKey& key) noexcept + { + auto trigger = triggers.find(key); + + // If id not found, emplace new object. + // New object is in the invalid state + if (trigger == triggers.end()) + trigger = triggers.emplace(key, SHCollisionState::INVALID).first; + + SHCollisionState& state = trigger->second; + updateCollisionState(isColliding, state); + + // If it was a false positive, remove the manifold immediately. + // Remove using iterator as it is on average faster. + if (state == SHCollisionState::INVALID) + trigger = triggers.erase(trigger); + } + + void SHContactManager::AddManifold(bool isColliding, const SHCollisionKey& key, const SHManifold& newManifold) noexcept + { + auto manifold = manifolds.find(key); + + // If id not found, emplace new manifold + if (manifold == manifolds.end()) + manifold = manifolds.emplace(key, newManifold).first; + else + { + // TODO: Update existing manifolds with new data + } + + SHCollisionState& state = manifold->second.state; + updateCollisionState(isColliding, state); + + // If it was a false positive, remove the manifold immediately. + // Remove using iterator as it is on average faster. + if (state == SHCollisionState::INVALID) + manifold = manifolds.erase(manifold); + } + + void SHContactManager::RemoveInvalidatedTrigger(EntityID eid) noexcept + { + remove(triggers, eid); + } + + void SHContactManager::RemoveInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) noexcept + { + remove(triggers, eid, shapeIndex); + } + + void SHContactManager::RemoveInvalidatedManifold(EntityID eid) noexcept + { + remove(manifolds, eid); + } + + void SHContactManager::RemoveInvalidatedManifold(EntityID eid, uint32_t shapeIndex) noexcept + { + remove(manifolds, eid, shapeIndex); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHContactManager::updateCollisionState(bool isColliding, SHCollisionState& state) noexcept + { + if (isColliding) + { + // New states start at invalid. In the first frame of collision, move to enter. + // If it already in enter, move to stay + state = state == SHCollisionState::INVALID ? SHCollisionState::ENTER : SHCollisionState::STAY; + } + else + { + // New states start at invalid. In false positive, remain unchanged. + // If previously colliding, move to exit. + state = state == SHCollisionState::INVALID ? SHCollisionState::INVALID : SHCollisionState::EXIT; + } + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h new file mode 100644 index 00000000..06a03a6a --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h @@ -0,0 +1,103 @@ +/**************************************************************************************** + * \file SHContactManager.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Contact Manager that stores collision information and + * resolves contact constraints. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "Physics/Collision/Contacts/SHCollisionEvents.h" +#include "Physics/Collision/Contacts/SHCollisionKey.h" +#include "Physics/Collision/Contacts/SHManifold.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + class SH_API SHContactManager + { + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using TriggerEvents = std::vector; + using CollisionEvents = std::vector; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHContactManager () noexcept = default; + ~SHContactManager () noexcept = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + const TriggerEvents& GetTriggerEvents () const noexcept; + const CollisionEvents& GetCollisionEvents () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Removes any invalidated contacts and triggers. + * @return + */ + void Update () noexcept; + + void AddTrigger (bool isColliding, const SHCollisionKey& key) noexcept; + void AddManifold (bool isColliding, const SHCollisionKey& key, const SHManifold& newManifold) noexcept; + + void RemoveInvalidatedTrigger (EntityID eid) noexcept; + void RemoveInvalidatedTrigger (EntityID eid, uint32_t shapeIndex) noexcept; + void RemoveInvalidatedManifold (EntityID eid) noexcept; + void RemoveInvalidatedManifold (EntityID eid, uint32_t shapeIndex) noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using Manifolds = std::unordered_map; + using Triggers = std::unordered_map; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + Manifolds manifolds; + Triggers triggers; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; + + // Removal Helpers + + template + void remove (std::unordered_map& container, EntityID eid); + template + void remove (std::unordered_map& container, EntityID eid, uint32_t shapeIndex); + + }; + + +} // namespace SHADE + +#include "SHContactManager.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp new file mode 100644 index 00000000..1403cd79 --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp @@ -0,0 +1,61 @@ +/**************************************************************************************** + * \file SHContactManager.hpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for templated methods of the Contact Manager. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Primary Header +#include "SHContactManager.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Private Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + template + void SHContactManager::remove(std::unordered_map& container, EntityID eid) + { + if (container.empty()) + return; + + for (auto invalidated = container.begin(); invalidated != container.end();) + { + const auto& ID = invalidated->first; + + const bool MATCHES_A = ID.GetEntityA() == eid; + const bool MATCHES_B = ID.GetEntityB() == eid; + + if (MATCHES_A || MATCHES_B) + invalidated = container.erase(invalidated); + else + ++invalidated; + } + } + + template + void SHContactManager::remove(std::unordered_map& container, EntityID eid, uint32_t shapeIndex) + { + if (container.empty()) + return; + + for (auto invalidated = container.begin(); invalidated != container.end();) + { + const auto& ID = invalidated->first; + + const bool MATCHES_A = ID.GetEntityA() == eid && ID.GetShapeIndexA() == shapeIndex; + const bool MATCHES_B = ID.GetEntityB() == eid && ID.GetShapeIndexB() == shapeIndex; + + if (MATCHES_A || MATCHES_B) + invalidated = container.erase(invalidated); + else + ++invalidated; + } + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index 8d0b69f8..b5dba378 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -13,11 +13,6 @@ // Primary Header #include "SHPhysicsWorld.h" -// Project Headers -#include "Physics/Collision/Narrowphase/SHCollision.h" -#include "Physics/Collision/Narrowphase/SHCollisionDispatch.h" - - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -25,59 +20,39 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHPhysicsWorld::SHPhysicsWorld(const WorldSettings& worldSettings) noexcept - : settings { worldSettings } + : settings { worldSettings } + , collisionSpace { nullptr } { SHLOG_INFO_D("Creating Physics World") } SHPhysicsWorld::~SHPhysicsWorld() noexcept { - + collisionSpace = nullptr; } /*-----------------------------------------------------------------------------------*/ /* Getter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - const SHAABBTree::AABBs& SHPhysicsWorld::GetBroadphaseAABBs() const noexcept + const SHContactManager::TriggerEvents& SHPhysicsWorld::GetTriggerEvents() const noexcept { - return broadphase.GetAABBs(); + return contactManager.GetTriggerEvents(); } - const SHPhysicsWorld::TriggerEvents& SHPhysicsWorld::GetTriggerEvents() const noexcept + const SHContactManager::CollisionEvents& SHPhysicsWorld::GetCollisionEvents() const noexcept { - static TriggerEvents triggerEvents; - - triggerEvents.clear(); - - for (auto& [id, state] : triggers) - triggerEvents.emplace_back(SHTriggerEvent{ id, state }); - - return triggerEvents; + return contactManager.GetCollisionEvents(); } - const SHPhysicsWorld::CollisionEvents& SHPhysicsWorld::GetCollisionEvents() const noexcept + /*-----------------------------------------------------------------------------------*/ + /* Setter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsWorld::SetCollisionSpace(SHCollisionSpace* _collisionSpace) noexcept { - static CollisionEvents collisionEvents; - - collisionEvents.clear(); - - for (auto& [id, manifold] : manifolds) - { - SHCollisionEvent collisionEvent - { - .info = id - , .state = manifold.state - , .normal = manifold.normal - }; - - for (uint32_t i = 0; i < manifold.numContacts; ++i) - collisionEvent.contactPoints[i] = manifold.contacts[i].position; - - collisionEvents.emplace_back(collisionEvent); - } - - return collisionEvents; + collisionSpace = _collisionSpace; + _collisionSpace->SetContactManager(&contactManager); } /*-----------------------------------------------------------------------------------*/ @@ -97,93 +72,54 @@ namespace SHADE { rigidBodies.erase(rigidBody->entityID); - // Attempt to remove any invalidated manifolds - if (manifolds.empty()) - return; - - for (auto manifoldPair = manifolds.begin(); manifoldPair != manifolds.end();) - { - const auto& ID = manifoldPair->first; - - const bool MATCHES_A = ID.GetEntityA() == rigidBody->entityID; - const bool MATCHES_B = ID.GetEntityB() == rigidBody->entityID; - - if (MATCHES_A || MATCHES_B) - manifoldPair = manifolds.erase(manifoldPair); - else - ++manifoldPair; - } + // Contact manager to remove invalidated contacts + contactManager.RemoveInvalidatedManifold(rigidBody->entityID); // TODO: Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep } - void SHPhysicsWorld::AddCollider(SHCollider* collider) noexcept - { - const bool INSERTED = colliders.emplace(collider->entityID, collider).second; - if (!INSERTED) - { - SHLOG_WARNING_D("Attempting to add duplicate collider {} to the Physics World!", collider->entityID) - return; - } - - collider->broadphase = &broadphase; - - // Add all existing shapes to the broadphase - for (const auto* shape : collider->shapes) - broadphase.Insert(shape->id, shape->ComputeAABB()); - } - - void SHPhysicsWorld::RemoveCollider(SHCollider* collider) noexcept - { - colliders.erase(collider->entityID); - - const uint32_t NUM_SHAPES = static_cast(collider->shapes.size()); - if (NUM_SHAPES == 0) - return; - - for (uint32_t i = 0; i < NUM_SHAPES; ++i) - { - const SHCollisionShape* SHAPE = collider->shapes[i]; - - broadphase.Remove(SHAPE->id); - - if (SHAPE->IsTrigger()) - removeInvalidatedTrigger(collider->entityID, i); - else - removeInvalidatedManifold(collider->entityID, i); - } - - /* - * TODO: - * Get collider's rigid body - * Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep - */ - } - - void SHPhysicsWorld::UpdateBroadphase(SHCollider* collider) noexcept - { - auto& shapes = collider->GetCollisionShapes(); - for (auto* shape : shapes) - broadphase.Update(shape->id, shape->ComputeAABB()); - } void SHPhysicsWorld::Step(float dt) { - // Clear containers of exit state collisions - updateEvents(); + // Contact manager to clear expired contacts + contactManager.Update(); - // TODO: Profile each of these - runBroadphase (); - runNarrowphase(); + /* + * Detect Collisions + */ + + if (collisionSpace) + collisionSpace->DetectCollisions(); + + /* + * Integrate Forces + */ for (auto* rigidBody : rigidBodies | std::views::values) { + if (!rigidBody->IsActive()) + continue; + rigidBody->ComputeWorldData(); integrateForces(*rigidBody, dt); } + + /* + * TODO: Resolve Contacts + */ + + /* + * Integrate Velocities + */ + for (auto* rigidBody : rigidBodies | std::views::values) + { + if (!rigidBody->IsActive()) + continue; + integrateVelocities(*rigidBody, dt); + } } /*-----------------------------------------------------------------------------------*/ @@ -262,275 +198,4 @@ namespace SHADE rigidBody.ClearForces(); } - void SHPhysicsWorld::runBroadphase() noexcept - { - // Update any colliders that have moved - for (auto& collider : colliders | std::views::values) - { - if (!collider->hasMoved) - continue; - - // Clear hasMoved flag here - collider->hasMoved = false; - - // Update moved shapes in broadphase - for (auto* shape : collider->shapes) - broadphase.Update(shape->id, shape->ComputeAABB()); - } - - // Query: Kinematic Triggers, Awake Dynamic Bodies & Dynamic Triggers - for (auto& collider : colliders | std::views::values) - { - // Default static bodies - if (!collider->rigidBody) - continue; - - // Explicit static bodies - const bool IS_STATIC = collider->rigidBody->GetType() == SHRigidBody::Type::STATIC; - if (IS_STATIC) - continue; - - // All remaining are kinematic or dynamic - // Iterate through shapes: if kinematic / dynamic trigger, else if dynamic & awake - if (collider->rigidBody->GetType() == SHRigidBody::Type::KINEMATIC) - queryKinematic(collider); - - if (collider->rigidBody->GetType() == SHRigidBody::Type::DYNAMIC) - queryDynamic(collider); - } - - } - - void SHPhysicsWorld::queryKinematic(SHCollider* collider) noexcept - { - for (auto* shape : collider->shapes) - { - // For kinematic shapes, we only query triggers against everything else - if (!shape->IsTrigger()) - continue; - - auto& potentialCollisions = broadphase.Query(shape->id, shape->ComputeAABB()); - - // Build narrow-phase pairs - auto* shapeA = shape; - - const EntityID ID_A = shape->id.GetEntityID(); - const uint32_t INDEX_A = shape->id.GetShapeIndex(); - - for (auto& id : potentialCollisions) - { - // Get corresponding shape - const EntityID ID_B = id.GetEntityID(); - const uint32_t INDEX_B = id.GetShapeIndex(); - - auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B); - - // Build collision ID - SHCollisionKey collisionKey; - collisionKey.SetEntityA(ID_A); - collisionKey.SetEntityB(ID_B); - collisionKey.SetCollisionShapeA(INDEX_A); - collisionKey.SetCollisionShapeB(INDEX_B); - - // Check if it already exists. If it doesn't, put into batch. - auto narrowphasePair = narrowphaseBatch.find(collisionKey); - if (narrowphasePair == narrowphaseBatch.end()) - narrowphaseBatch.emplace(collisionKey, NarrowphasePair{ shapeA, shapeB }); - } - } - } - - void SHPhysicsWorld::queryDynamic(SHCollider* collider) noexcept - { - for (auto* shape : collider->shapes) - { - auto& potentialCollisions = broadphase.Query(shape->id, shape->ComputeAABB()); - - // Build narrow-phase pairs - auto* shapeA = shape; - - const EntityID ID_A = shape->id.GetEntityID(); - const uint32_t INDEX_A = shape->id.GetShapeIndex(); - - for (auto& id : potentialCollisions) - { - // Get corresponding shape - const EntityID ID_B = id.GetEntityID(); - const uint32_t INDEX_B = id.GetShapeIndex(); - - auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B); - - // Build collision ID - SHCollisionKey collisionKey; - collisionKey.SetEntityA(ID_A); - collisionKey.SetEntityB(ID_B); - collisionKey.SetCollisionShapeA(INDEX_A); - collisionKey.SetCollisionShapeB(INDEX_B); - - // Check if it already exists. If it doesn't, put into batch. - auto narrowphasePair = narrowphaseBatch.find(collisionKey); - if (narrowphasePair == narrowphaseBatch.end()) - narrowphaseBatch.emplace(collisionKey, NarrowphasePair{ shapeA, shapeB }); - } - } - } - - void SHPhysicsWorld::runNarrowphase() noexcept - { - if (narrowphaseBatch.empty()) - return; - - for (auto& [id, narrowphasePair] : narrowphaseBatch) - { - const bool IS_A_TRIGGER = narrowphasePair.A->IsTrigger(); - const bool IS_B_TRIGGER = narrowphasePair.B->IsTrigger(); - - // Check if ID exists in trigger - if (IS_A_TRIGGER || IS_B_TRIGGER) - collideTriggers(id, narrowphasePair); - else - collideManifolds(id, narrowphasePair); - } - - // Clear every frame - narrowphaseBatch.clear(); - } - - void SHPhysicsWorld::collideTriggers(const SHCollisionKey& id, NarrowphasePair& narrowphasePair) noexcept - { - const auto* A = narrowphasePair.A; - const auto* B = narrowphasePair.B; - - const bool COLLIDING = SHCollisionDispatcher::Collide(*A, *B); - - auto trigger = triggers.find(id); - - // If id not found, emplace new object. - // New object is in the invalid state - if (trigger == triggers.end()) - trigger = triggers.emplace(id, SHCollisionState::INVALID).first; - - SHCollisionState& state = trigger->second; - updateCollisionState(COLLIDING, state); - - // If it was a false positive, remove the manifold immediately. - // Remove using iterator as it is on average faster. - if (state == SHCollisionState::INVALID) - trigger = triggers.erase(trigger); - } - - void SHPhysicsWorld::collideManifolds(const SHCollisionKey& id, NarrowphasePair& narrowphasePair) noexcept - { - auto* A = narrowphasePair.A; - auto* B = narrowphasePair.B; - - SHManifold newManifold { .A = A, .B = B }; - const bool COLLIDING = SHCollisionDispatcher::Collide(newManifold, *A, *B); - - auto manifold = manifolds.find(id); - - // If id not found, emplace new manifold - if (manifold == manifolds.end()) - manifold = manifolds.emplace(id, newManifold).first; - else - { - // TODO: Update existing manifolds with new data - } - - SHCollisionState& state = manifold->second.state; - updateCollisionState(COLLIDING, state); - - // If it was a false positive, remove the manifold immediately. - // Remove using iterator as it is on average faster. - if (state == SHCollisionState::INVALID) - manifold = manifolds.erase(manifold); - } - - void SHPhysicsWorld::updateCollisionState(bool isColliding, SHCollisionState& state) noexcept - { - if (isColliding) - { - // New states start at invalid. In the first frame of collision, move to enter. - // If it already in enter, move to stay - state = state == SHCollisionState::INVALID ? SHCollisionState::ENTER : SHCollisionState::STAY; - } - else - { - // New states start at invalid. In false positive, remain unchanged. - // If previously colliding, move to exit. - state = state == SHCollisionState::INVALID ? SHCollisionState::INVALID : SHCollisionState::EXIT; - } - } - - void SHPhysicsWorld::updateEvents() noexcept - { - // Clear expired or invalid collisions - for (auto manifold = manifolds.begin(); manifold != manifolds.end();) - { - const auto COLLISION_STATE = manifold->second.state; - - const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT; - const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID; - - if (IS_EXIT || IS_INVALID) - manifold = manifolds.erase(manifold); - else - ++manifold; - } - - // Clear expired or invalid triggers - for (auto trigger = triggers.begin(); trigger != triggers.end();) - { - const auto COLLISION_STATE = trigger->second; - - const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT; - const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID; - - if (IS_EXIT || IS_INVALID) - trigger = triggers.erase(trigger); - else - ++trigger; - } - } - - void SHPhysicsWorld::removeInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) - { - if (triggers.empty()) - return; - - for (auto invalidatedTrigger = triggers.begin(); invalidatedTrigger != triggers.end();) - { - const auto& ID = invalidatedTrigger->first; - - const bool MATCHES_A = ID.GetEntityA() == eid && ID.GetShapeIndexA() == shapeIndex; - const bool MATCHES_B = ID.GetEntityB() == eid && ID.GetShapeIndexB() == shapeIndex; - - if (MATCHES_A || MATCHES_B) - invalidatedTrigger = triggers.erase(invalidatedTrigger); - else - ++invalidatedTrigger; - } - } - - void SHPhysicsWorld::removeInvalidatedManifold(EntityID eid, uint32_t shapeIndex) - { - if (manifolds.empty()) - return; - - for (auto invalidatedManifold = manifolds.begin(); invalidatedManifold != manifolds.end();) - { - const auto& ID = invalidatedManifold->first; - - const bool MATCHES_A = ID.GetEntityA() == eid && ID.GetShapeIndexA() == shapeIndex; - const bool MATCHES_B = ID.GetEntityB() == eid && ID.GetShapeIndexB() == shapeIndex; - - if (MATCHES_A || MATCHES_B) - invalidatedManifold = manifolds.erase(invalidatedManifold); - else - ++invalidatedManifold; - } - } - - - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index a0743a30..8248299a 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -13,10 +13,8 @@ #include // Project Headers -#include "Physics/Collision/Broadphase/SHDynamicAABBTree.h" -#include "Physics/Collision/Contacts/SHCollisionEvents.h" -#include "Physics/Collision/Contacts/SHManifold.h" -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCollisionSpace.h" +#include "SHContactManager.h" #include "SHRigidBody.h" @@ -26,10 +24,15 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates the overall simulation of physics. The bulk of dynamics are handled here, + * with the collision detection handled by an attached collision space.
+ * A collision space must be created separately and attached with the world. + */ class SH_API SHPhysicsWorld { public: - /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ @@ -47,9 +50,6 @@ namespace SHADE bool sleepingEnabled = true; }; - using TriggerEvents = std::vector; - using CollisionEvents = std::vector; - /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ @@ -71,29 +71,44 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - const SHAABBTree::AABBs& GetBroadphaseAABBs () const noexcept; + const SHContactManager::TriggerEvents& GetTriggerEvents () const noexcept; + const SHContactManager::CollisionEvents& GetCollisionEvents () const noexcept; - const TriggerEvents& GetTriggerEvents () const noexcept; - const CollisionEvents& GetCollisionEvents () const noexcept; + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetCollisionSpace(SHCollisionSpace* collisionSpace) noexcept; /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * Adds a rigid body to the world for it to be simulated using motion dynamics. + * @param rigidBody + * A rigid body to add. Duplicates will be ignored. + */ void AddRigidBody (SHRigidBody* rigidBody) noexcept; - void RemoveRigidBody (SHRigidBody* rigidBody) noexcept; - - void AddCollider (SHCollider* collider) noexcept; - void RemoveCollider (SHCollider* collider) noexcept; /** * @brief - * Invoke this method to update the broadphase of a collider while the simulation - * is not running. - * @param collider - * The collider to update. + * Removes a rigid body from the world. It's motion will not be affected unless + * explicitly modified. + * @param rigidBody + * A rigid body to add. Duplicates will be ignored. + */ + void RemoveRigidBody (SHRigidBody* rigidBody) noexcept; + + /** + * @brief + * Performs a single simulation step.
+ * Detect Collisions -> Integrate Forces -> Resolve Contacts -> Integrate Velocities + * @param dt + * A discrete time step for the simulation. This should be consistent for deteministic + * behaviour. */ - void UpdateBroadphase (SHCollider* collider) noexcept; void Step (float dt); private: @@ -101,42 +116,18 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - struct NarrowphasePair - { - SHCollisionShape* A = nullptr; - SHCollisionShape* B = nullptr; - }; - // EntityIDs are used to map resolved contraints back to bodies using RigidBodies = std::unordered_map; - using Colliders = std::unordered_map; - - // Collisions - - using NarrowphaseBatch = std::unordered_map; - using Manifolds = std::unordered_map; - using Triggers = std::unordered_map; - - /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ WorldSettings settings; - - // Containers + SHCollisionSpace* collisionSpace; RigidBodies rigidBodies; - Colliders colliders; - - NarrowphaseBatch narrowphaseBatch; - Manifolds manifolds; - Triggers triggers; - - // World components - - SHAABBTree broadphase; + SHContactManager contactManager; /*---------------------------------------------------------------------------------*/ /* Function Members */ @@ -146,22 +137,8 @@ namespace SHADE void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept; void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept; - // Broadphase helpers - - void runBroadphase () noexcept; - void queryKinematic (SHCollider* collider) noexcept; - void queryDynamic (SHCollider* collider) noexcept; - - // Narrowphase helpers - - void runNarrowphase () noexcept; - void collideTriggers (const SHCollisionKey& id, NarrowphasePair& narrowphasePair) noexcept; - void collideManifolds (const SHCollisionKey& id, NarrowphasePair& narrowphasePair) noexcept; - void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; - void updateEvents () noexcept; - void removeInvalidatedTrigger (EntityID eid, uint32_t shapeIndex); - void removeInvalidatedManifold (EntityID eid, uint32_t shapeIndex); + }; diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp index b43b5b77..9bf43115 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -87,9 +87,8 @@ namespace SHADE { const SHColour& AABB_COLOUR = physicsDebugDrawSystem->DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::BROADPHASE)]; - auto& broadphaseAABBs = physicsSystem->GetWorld()->GetBroadphaseAABBs(); - - for (auto& aabb : broadphaseAABBs) + const auto& BROADPHASE_AABBS = physicsSystem->collisionSpace->GetBroadphaseAABBs(); + for (auto& aabb : BROADPHASE_AABBS) { // Compute AABB Transform const SHMatrix TRS = SHMatrix::Transform(aabb.GetCenter(), SHQuaternion::Identity, aabb.GetWorldExtents() * 2.0f); diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp index 200642e1..5b0e5563 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp @@ -16,6 +16,7 @@ // Project Headers #include "ECS_Base/Managers/SHSystemManager.h" #include "Math/Transform/SHTransformComponent.h" +#include "Scene/SHSceneManager.h" #include "Scripting/SHScriptEngine.h" @@ -50,15 +51,25 @@ namespace SHADE const auto& RIGIDBODY_DENSE = SHComponentManager::GetDense(); for (auto& rigidBodyComponent : RIGIDBODY_DENSE) { - auto* transformComponent = SHComponentManager::GetComponent_s(rigidBodyComponent.GetEID()); + const EntityID EID = rigidBodyComponent.GetEID(); + + // Skip missing transforms + auto* transformComponent = SHComponentManager::GetComponent_s(EID); if (!transformComponent) continue; + // Skip invalid bodies (Should not occur) if (!rigidBodyComponent.rigidBody) continue; + // Skip inactive bodies + const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(EID); + if (!IS_ACTIVE) + continue; + const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); + // Skip objects that have not moved if (!MOTION_STATE) continue; diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index 178a6120..17f3f83d 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -16,6 +16,7 @@ // Project Headers #include "ECS_Base/Managers/SHComponentManager.h" #include "Math/Transform/SHTransformComponent.h" +#include "Scene/SHSceneManager.h" namespace SHADE { @@ -40,35 +41,54 @@ namespace SHADE for (auto& [entityID, physicsObject] : physicsObjects) { const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); - - if (!TRANSFORM_COMPONENT || !TRANSFORM_COMPONENT->HasChanged()) - continue; - - const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); - const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation(); - const SHVec3& WORLD_SCL = TRANSFORM_COMPONENT->GetWorldScale(); + // Assume transform is always active + const bool UPDATE_TRANSFORM = TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged(); // We assume that all engine components and physics object components have been successfully linked if (physicsObject.rigidBody) { - SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); + const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(entityID); + const bool RIGIDBODY_ACTIVE = physicsObject.rigidBody->IsActive(); - motionState.ForcePosition(TRANSFORM_COMPONENT->GetWorldPosition()); - motionState.ForceOrientation(TRANSFORM_COMPONENT->GetWorldOrientation()); + if (IS_ACTIVE != RIGIDBODY_ACTIVE) + physicsObject.rigidBody->SetIsActive(IS_ACTIVE); + + if (UPDATE_TRANSFORM) + { + const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); + const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation(); + + SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); + motionState.ForcePosition(WORLD_POS); + motionState.ForceOrientation(WORLD_ROT); + } } if (physicsObject.collider) { - physicsObject.collider->SetPosition(WORLD_POS); - physicsObject.collider->SetOrientation(WORLD_ROT); - physicsObject.collider->SetScale(WORLD_SCL); + const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(entityID); + const bool COLLIDER_ACTIVE = physicsObject.collider->IsActive(); - physicsObject.collider->RecomputeShapes(); + if (IS_ACTIVE != COLLIDER_ACTIVE) + physicsObject.collider->SetIsActive(IS_ACTIVE); - physicsSystem->physicsWorld->UpdateBroadphase(physicsObject.collider); + if (UPDATE_TRANSFORM) + { + const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); + const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation(); + const SHVec3& WORLD_SCL = TRANSFORM_COMPONENT->GetWorldScale(); + + physicsObject.collider->SetPosition(WORLD_POS); + physicsObject.collider->SetOrientation(WORLD_ROT); + physicsObject.collider->SetScale(WORLD_SCL); + + physicsObject.collider->RecomputeShapes(); + } } } + + physicsSystem->collisionSpace->UpdateBroadphase(); } } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 65bbc5d4..508c19ad 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -21,6 +21,7 @@ #include "Editor/SHEditor.h" #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Physics/Interface/SHColliderComponent.h" +#include "Scripting/SHScriptEngine.h" namespace SHADE { @@ -33,6 +34,7 @@ namespace SHADE , interpolationFactor { 0.0 } , fixedDT { DEFAULT_FIXED_STEP } , physicsWorld { nullptr } + , collisionSpace { nullptr } { // Add more events here to register them @@ -44,6 +46,7 @@ namespace SHADE SHPhysicsSystem::~SHPhysicsSystem() noexcept { + delete collisionSpace; delete physicsWorld; } @@ -51,11 +54,6 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - const SHPhysicsWorld* SHPhysicsSystem::GetWorld() const noexcept - { - return physicsWorld; - } - double SHPhysicsSystem::GetFixedUpdateRate() const noexcept { return 1.0 / fixedDT; @@ -66,6 +64,16 @@ namespace SHADE return fixedDT; } + const std::vector& SHPhysicsSystem::GetTriggerInfo() const noexcept + { + return physicsWorld->GetTriggerEvents(); + } + + const std::vector& SHPhysicsSystem::GetCollisionInfo() const noexcept + { + return physicsWorld->GetCollisionEvents(); + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -137,7 +145,48 @@ namespace SHADE void SHPhysicsSystem::ForceUpdate() { + if (!physicsWorld) + return; + auto* scriptingSystem = SHSystemManager::GetSystem(); + + if (scriptingSystem == nullptr) + { + SHLOGV_ERROR("Unable to invoke collision and trigger script events due to missing SHScriptEngine!"); + } + + scriptingSystem->ExecuteFixedUpdates(); + physicsWorld->Step(static_cast(fixedDT)); + + const auto& RIGIDBODY_DENSE = SHComponentManager::GetDense(); + for (auto& rigidBodyComponent : RIGIDBODY_DENSE) + { + const EntityID EID = rigidBodyComponent.GetEID(); + + // Skip missing transforms + auto* transformComponent = SHComponentManager::GetComponent_s(EID); + if (!transformComponent) + continue; + + // Skip invalid bodies (Should not occur) + if (!rigidBodyComponent.rigidBody) + continue; + + // Skip inactive bodies + const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(EID); + if (!IS_ACTIVE) + continue; + + const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); + + // Skip objects that have not moved + if (!MOTION_STATE) + continue; + + // We ignore interpolations here because we are only stepping once + transformComponent->SetWorldPosition(MOTION_STATE.position); + transformComponent->SetWorldOrientation(MOTION_STATE.orientation); + } } /*-----------------------------------------------------------------------------------*/ @@ -162,17 +211,29 @@ namespace SHADE { if (PHYSICS_OBJECT.rigidBody) physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); - - if (PHYSICS_OBJECT.collider) - physicsWorld->RemoveCollider(PHYSICS_OBJECT.collider); } delete physicsWorld; physicsWorld = nullptr; } - // Create the physics world - physicsWorld = new SHPhysicsWorld; + if (collisionSpace) + { + for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) + { + if (PHYSICS_OBJECT.collider) + collisionSpace->RemoveCollider(PHYSICS_OBJECT.collider); + } + + delete collisionSpace; + collisionSpace = nullptr; + } + + // Create the physics world & collision space + physicsWorld = new SHPhysicsWorld; + collisionSpace = new SHCollisionSpace; + + physicsWorld->SetCollisionSpace(collisionSpace); // Immediately add all existing bodies and colliders to the world. // Since we recreated the scene and the world, the initial data has been reset and determinism is guaranteed. @@ -184,7 +245,7 @@ namespace SHADE physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); if (PHYSICS_OBJECT.collider) - physicsWorld->AddCollider(PHYSICS_OBJECT.collider); + collisionSpace->AddCollider(PHYSICS_OBJECT.collider); } return onSceneInitEvent.get()->handle; @@ -203,9 +264,12 @@ namespace SHADE physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); if (PHYSICS_OBJECT.collider) - physicsWorld->RemoveCollider(PHYSICS_OBJECT.collider); + collisionSpace->RemoveCollider(PHYSICS_OBJECT.collider); } + delete collisionSpace; + collisionSpace = nullptr; + delete physicsWorld; physicsWorld = nullptr; @@ -247,10 +311,10 @@ namespace SHADE { physicsObjectManager.AddCollider(EID); - if (physicsWorld) + if (collisionSpace) { auto* collider = physicsObjectManager.GetPhysicsObject(EID)->collider; - physicsWorld->AddCollider(collider); + collisionSpace->AddCollider(collider); } } @@ -289,17 +353,16 @@ namespace SHADE if (IS_COLLIDER) { - if (physicsWorld) + if (collisionSpace) { auto* collider = physicsObjectManager.GetPhysicsObject(EID)->collider; - physicsWorld->RemoveCollider(collider); + collisionSpace->RemoveCollider(collider); } physicsObjectManager.RemoveCollider(EID); } - return onComponentRemovedEvent.get()->handle; } -} // namespace SHADE \ No newline at end of file +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index fca12498..ab28a299 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -33,6 +33,13 @@ namespace SHADE */ class SH_API SHPhysicsSystem final : public SHSystem { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHPhysicsDebugDrawSystem; + public: /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -45,7 +52,6 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] const SHPhysicsWorld* GetWorld () const noexcept; [[nodiscard]] double GetFixedUpdateRate() const noexcept; [[nodiscard]] double GetFixedDT () const noexcept; @@ -56,6 +62,9 @@ namespace SHADE void SetFixedUpdateRate(double fixedUpdateRate) noexcept; void SetFixedDT(double fixedDt) noexcept; + const std::vector& GetTriggerInfo () const noexcept; + const std::vector& GetCollisionInfo () const noexcept; + /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ @@ -137,6 +146,7 @@ namespace SHADE // Sub-systems / managers SHPhysicsWorld* physicsWorld; + SHCollisionSpace* collisionSpace; SHPhysicsObjectManager physicsObjectManager; /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp index 06f1b464..f72eb271 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp @@ -1,8 +1,6 @@ /************************************************************************************//*! \file SHPhysicsSystemInterface.cpp -\author Tng Kah Wei, kahwei.tng, 390009620 -\par email: kahwei.tng\@digipen.edu -\date Oct 31, 2022 +\author Diren D Bharwani, diren.dbharwani, 390002520 \brief Contains the definitions of the functions of the static SHPhysicsSystemInterface class. @@ -10,10 +8,13 @@ Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ -// Precompiled Headers + + #include "SHpch.h" + // Primary Header #include "SHPhysicsSystemInterface.h" + // Project Includes #include "ECS_Base/Managers/SHSystemManager.h" #include "Physics/System/SHPhysicsSystem.h" @@ -23,42 +24,47 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Static Usage Functions */ /*-----------------------------------------------------------------------------------*/ - const std::vector& SHPhysicsSystemInterface::GetCollisionInfo() noexcept + const std::vector& SHPhysicsSystemInterface::GetCollisionInfo() noexcept { - static std::vector emptyVec; + static std::vector emptyVec; - //auto phySystem = SHSystemManager::GetSystem(); - //if (phySystem) - //{ - // return phySystem->GetAllCollisionInfo(); - //} + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + return physicsSystem->GetCollisionInfo(); - //SHLOGV_WARNING("Failed to get collision events. Empty vector returned instead."); + SHLOGV_WARNING("Failed to get collision events. Empty vector returned instead."); return emptyVec; } - const std::vector& SHPhysicsSystemInterface::GetTriggerInfo() noexcept + const std::vector& SHPhysicsSystemInterface::GetTriggerInfo() noexcept { - static std::vector emptyVec; + static std::vector emptyVec; - //auto phySystem = SHSystemManager::GetSystem(); - //if (phySystem) - //{ - // return phySystem->GetAllTriggerInfo(); - //} + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + return physicsSystem->GetTriggerInfo(); - //SHLOGV_WARNING("Failed to get trigger events. Empty vector returned instead."); + SHLOGV_WARNING("Failed to get trigger events. Empty vector returned instead."); return emptyVec; } double SHPhysicsSystemInterface::GetFixedDT() noexcept { - auto phySystem = SHSystemManager::GetSystem(); - if (phySystem) - { - return 1.0 / phySystem->GetFixedUpdateRate(); - } + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + return physicsSystem->GetFixedDT(); SHLOGV_WARNING("Failed to get fixed delta time. 0.0 returned instead."); return 0.0; } + + int SHPhysicsSystemInterface::GetFixedUpdateRate() noexcept + { + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + return physicsSystem->GetFixedUpdateRate(); + + SHLOGV_WARNING("Failed to get fixed update rate. 0.0 returned instead."); + return 0.0; + } + } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h index ed190cc9..e6103e87 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h @@ -1,22 +1,20 @@ /************************************************************************************//*! \file SHPhysicsSystemInterface.h -\author Tng Kah Wei, kahwei.tng, 390009620 -\par email: kahwei.tng\@digipen.edu -\date Oct 31, 2022 +\author Diren D Bharwani, diren.dbharwani, 390002520 \brief Contains the definition of the SHGraphicsSystemInterface static class. Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ + #pragma once -// STL Includes #include // Project Headers #include "ECS_Base/Entity/SHEntity.h" -#include "Physics/Collision/SHCollisionInfo.h" +#include "Physics/Collision/Contacts/SHCollisionEvents.h" namespace SHADE @@ -49,8 +47,8 @@ namespace SHADE /* Static Usage Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static const std::vector& GetCollisionInfo () noexcept; - [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; + [[nodiscard]] static const std::vector& GetCollisionInfo () noexcept; + [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; [[nodiscard]] static double GetFixedDT () noexcept; [[nodiscard]] static int GetFixedUpdateRate () noexcept; }; diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index a5a0ebc7..1393fe33 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -30,7 +30,6 @@ of DigiPen Institute of Technology is prohibited. #include "Engine/Application.hxx" #include "Physics/System/SHPhysicsSystemInterface.h" #include "Physics/SHPhysicsEvents.h" -#include "Physics/Collision/SHCollisionInfo.h" namespace SHADE { @@ -605,8 +604,8 @@ namespace SHADE { auto entities = { - std::make_pair(collisionInfo.GetEntityA(), collisionInfo.GetEntityB()), - std::make_pair(collisionInfo.GetEntityB(), collisionInfo.GetEntityA()) + std::make_pair(collisionInfo.info.GetEntityA(), collisionInfo.info.GetEntityB()), + std::make_pair(collisionInfo.info.GetEntityB(), collisionInfo.info.GetEntityA()) }; for (auto entity : entities) { @@ -625,15 +624,15 @@ namespace SHADE for (int i = 0; i < entityScripts->Count; ++i) { Script^ script = entityScripts[i]; - switch (collisionInfo.GetCollisionState()) + switch (collisionInfo.state) { - case SHCollisionInfo::State::ENTER: + case SHCollisionState::ENTER: script->OnCollisionEnter(info); break; - case SHCollisionInfo::State::STAY: + case SHCollisionState::STAY: script->OnCollisionStay(info); break; - case SHCollisionInfo::State::EXIT: + case SHCollisionState::EXIT: script->OnCollisionExit(info); break; } @@ -647,8 +646,8 @@ namespace SHADE { auto entities = { - std::make_pair(triggerInfo.GetEntityA(), triggerInfo.GetEntityB()), - std::make_pair(triggerInfo.GetEntityB(), triggerInfo.GetEntityA()) + std::make_pair(triggerInfo.info.GetEntityA(), triggerInfo.info.GetEntityB()), + std::make_pair(triggerInfo.info.GetEntityB(), triggerInfo.info.GetEntityA()) }; for (auto entity : entities) { @@ -667,15 +666,15 @@ namespace SHADE for (int i = 0; i < entityScripts->Count; ++i) { Script^ script = entityScripts[i]; - switch (triggerInfo.GetCollisionState()) + switch (triggerInfo.state) { - case SHCollisionInfo::State::ENTER: + case SHCollisionState::ENTER: script->OnTriggerEnter(info); break; - case SHCollisionInfo::State::STAY: + case SHCollisionState::STAY: script->OnTriggerStay(info); break; - case SHCollisionInfo::State::EXIT: + case SHCollisionState::EXIT: script->OnTriggerExit(info); break; } From 265a5bece8aa8ab0804a692550faa2c2697bd730 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 21 Dec 2022 00:40:01 +0800 Subject: [PATCH 033/164] Slight refactor to fix collision states for very fast moving objects --- Assets/Scenes/PhysicsSandbox.shade | 8 +- .../Physics/Collision/Contacts/SHManifold.h | 26 ++- .../Physics/Collision/Contacts/SHManifold.hpp | 151 ++++++++++++++++++ .../Physics/Collision/SHCollisionSpace.cpp | 30 ++-- .../src/Physics/Dynamics/SHContactManager.cpp | 118 +++++++------- .../src/Physics/Dynamics/SHContactManager.h | 34 ++-- .../src/Physics/Dynamics/SHContactManager.hpp | 4 +- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 3 - 8 files changed, 283 insertions(+), 91 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 21840aca..d983c22b 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,8 +4,8 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 3, z: 0} - Rotate: {x: 0, y: 0, z: -0} + Translate: {x: 0, y: 0.0579863191, z: 0} + Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: @@ -15,7 +15,7 @@ Drag: 1 Angular Drag: 1 Use Gravity: true - Gravity Scale: 1 + Gravity Scale: 10 Interpolate: true Sleeping Enabled: true Freeze Position X: false @@ -48,7 +48,7 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 2, z: 3} + Position: {x: 0, y: 0.5, z: 2} Pitch: 0 Yaw: 0 Roll: 0 diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h index 0534f749..c2b6ddb2 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h @@ -25,6 +25,23 @@ namespace SHADE struct SH_API SHManifold { public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHManifold (SHCollisionShape* a, SHCollisionShape* b) noexcept; + SHManifold (const SHManifold& rhs) noexcept; + SHManifold (SHManifold&& rhs) noexcept; + + ~SHManifold () noexcept = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHManifold& operator=(const SHManifold& rhs) noexcept; + SHManifold& operator=(SHManifold&& rhs) noexcept; + /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ @@ -32,8 +49,8 @@ namespace SHADE // We only need 4 contact points to build a stable manifold. static constexpr int MAX_NUM_CONTACTS = 4; - SHCollisionShape* A = nullptr; - SHCollisionShape* B = nullptr; + SHCollisionShape* A; + SHCollisionShape* B; uint32_t numContacts = 0; SHCollisionState state = SHCollisionState::INVALID; @@ -44,4 +61,7 @@ namespace SHADE }; -} \ No newline at end of file +} // namespace SHADE + +#include "SHManifold.hpp" + diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp new file mode 100644 index 00000000..f0e1374f --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp @@ -0,0 +1,151 @@ +/**************************************************************************************** + * \file SHManifold.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Collision Manifold + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Primary Header +#include "SHManifold.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + inline SHManifold::SHManifold(SHCollisionShape* a, SHCollisionShape* b) noexcept + : A { a } + , B { b } + {} + + inline SHManifold::SHManifold(const SHManifold& rhs) noexcept + : A { rhs.A } + , B { rhs.B } + , numContacts { rhs.numContacts } + , state { rhs.state } + , normal { rhs.normal } + { + for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) + tangents[i] = rhs.tangents[i]; + + for (int i = 0; i < MAX_NUM_CONTACTS; ++i) + { + contacts[i].penetration = rhs.contacts[i].penetration; + contacts[i].bias = rhs.contacts[i].bias; + contacts[i].normalImpulse = rhs.contacts[i].normalImpulse; + contacts[i].normalMass = rhs.contacts[i].normalMass; + + for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) + { + contacts[i].tangentImpulse[j] = rhs.contacts[i].tangentImpulse[j]; + contacts[i].tangentMass[j] = rhs.contacts[i].tangentMass[j]; + } + + contacts[i].position = rhs.contacts[i].position; + contacts[i].featurePair.key = rhs.contacts[i].featurePair.key; + } + } + + inline SHManifold::SHManifold(SHManifold&& rhs) noexcept + : A { rhs.A } + , B { rhs.B } + , numContacts { rhs.numContacts } + , state { rhs.state } + , normal { rhs.normal } + { + for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) + tangents[i] = rhs.tangents[i]; + + for (int i = 0; i < MAX_NUM_CONTACTS; ++i) + { + contacts[i].penetration = rhs.contacts[i].penetration; + contacts[i].bias = rhs.contacts[i].bias; + contacts[i].normalImpulse = rhs.contacts[i].normalImpulse; + contacts[i].normalMass = rhs.contacts[i].normalMass; + + for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) + { + contacts[i].tangentImpulse[j] = rhs.contacts[i].tangentImpulse[j]; + contacts[i].tangentMass[j] = rhs.contacts[i].tangentMass[j]; + } + + contacts[i].position = rhs.contacts[i].position; + contacts[i].featurePair.key = rhs.contacts[i].featurePair.key; + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + inline SHManifold& SHManifold::operator=(const SHManifold& rhs) noexcept + { + if (this == &rhs) + return *this; + + A = rhs.A; + B = rhs.B; + numContacts = rhs.numContacts; + state = rhs.state; + normal = rhs.normal; + + for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) + tangents[i] = rhs.tangents[i]; + + for (int i = 0; i < MAX_NUM_CONTACTS; ++i) + { + contacts[i].penetration = rhs.contacts[i].penetration; + contacts[i].bias = rhs.contacts[i].bias; + contacts[i].normalImpulse = rhs.contacts[i].normalImpulse; + contacts[i].normalMass = rhs.contacts[i].normalMass; + + for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) + { + contacts[i].tangentImpulse[j] = rhs.contacts[i].tangentImpulse[j]; + contacts[i].tangentMass[j] = rhs.contacts[i].tangentMass[j]; + } + + contacts[i].position = rhs.contacts[i].position; + contacts[i].featurePair.key = rhs.contacts[i].featurePair.key; + } + + return *this; + } + + inline SHManifold& SHManifold::operator=(SHManifold&& rhs) noexcept + { + A = rhs.A; + B = rhs.B; + numContacts = rhs.numContacts; + state = rhs.state; + normal = rhs.normal; + + for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) + tangents[i] = rhs.tangents[i]; + + for (int i = 0; i < MAX_NUM_CONTACTS; ++i) + { + contacts[i].penetration = rhs.contacts[i].penetration; + contacts[i].bias = rhs.contacts[i].bias; + contacts[i].normalImpulse = rhs.contacts[i].normalImpulse; + contacts[i].normalMass = rhs.contacts[i].normalMass; + + for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) + { + contacts[i].tangentImpulse[j] = rhs.contacts[i].tangentImpulse[j]; + contacts[i].tangentMass[j] = rhs.contacts[i].tangentMass[j]; + } + + contacts[i].position = rhs.contacts[i].position; + contacts[i].featurePair.key = rhs.contacts[i].featurePair.key; + } + + return *this; + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp index bc76d14a..ab4520ee 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp @@ -102,6 +102,8 @@ namespace SHADE void SHCollisionSpace::DetectCollisions() noexcept { + // TODO: Profile broad-phase and narrow-phase + /* * Broad-phase */ @@ -112,11 +114,14 @@ namespace SHADE // Colliders without bodies are considered to be static bodies // This is specific to this engine because of Unity's stupid convention. const bool IS_IMPLICIT_STATIC = !collider->rigidBody; - const bool IS_EXPLICIT_STATIC = collider->rigidBody->GetType() == SHRigidBody::Type::STATIC; const bool IS_ACTIVE = collider->active; // Skip inactive colliders - if (!IS_ACTIVE || IS_IMPLICIT_STATIC || IS_EXPLICIT_STATIC) + if (!IS_ACTIVE || IS_IMPLICIT_STATIC) + continue; + + const bool IS_EXPLICIT_STATIC = collider->rigidBody->GetType() == SHRigidBody::Type::STATIC; + if (IS_EXPLICIT_STATIC) continue; // All remaining are kinematic or dynamic @@ -149,6 +154,10 @@ namespace SHADE // Clear every frame narrowphaseBatch.clear(); + + // Test all collisions + if (contactManager) + contactManager->Update(); } @@ -198,14 +207,12 @@ namespace SHADE void SHCollisionSpace::collideTriggers(const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept { - const auto* A = narrowphasePair.A; - const auto* B = narrowphasePair.B; + auto* A = narrowphasePair.A; + auto* B = narrowphasePair.B; - const bool COLLIDING = SHCollisionDispatcher::Collide(*A, *B); - - // Send results to contact manager + // Send to contact manager if (contactManager) - contactManager->AddTrigger(COLLIDING, key); + contactManager->AddTrigger(key, A, B); } void SHCollisionSpace::collideManifolds(const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept @@ -213,11 +220,8 @@ namespace SHADE auto* A = narrowphasePair.A; auto* B = narrowphasePair.B; - SHManifold newManifold { .A = A, .B = B }; - const bool COLLIDING = SHCollisionDispatcher::Collide(newManifold, *A, *B); - - // Send results to contact manager + // Send to contact manager if (contactManager) - contactManager->AddManifold(COLLIDING, key, newManifold); + contactManager->AddManifold(key, A, B); } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index be65128d..0ab20b38 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -14,6 +14,9 @@ // Primary Header #include "SHContactManager.h" +// Project Headers +#include "Physics/Collision/Narrowphase/SHCollisionDispatch.h" + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -26,8 +29,8 @@ namespace SHADE triggerEvents.clear(); - for (auto& [id, state] : triggers) - triggerEvents.emplace_back(SHTriggerEvent{ id, state }); + for (auto& [id, trigger] : triggers) + triggerEvents.emplace_back(SHTriggerEvent{ id, trigger.state }); return triggerEvents; } @@ -62,92 +65,83 @@ namespace SHADE void SHContactManager::Update() noexcept { - // Clear expired or invalid collisions - for (auto manifold = manifolds.begin(); manifold != manifolds.end();) + // Clear expired or invalid collisions. If not, test collision. + for (auto manifoldPair = manifolds.begin(); manifoldPair != manifolds.end();) { - const auto COLLISION_STATE = manifold->second.state; + // Test collision of every manifold. + SHManifold& oldManifold = manifoldPair->second; + SHManifold newManifold = oldManifold; - const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT; - const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID; + const bool IS_COLLIDING = SHCollisionDispatcher::Collide(newManifold, *newManifold.A, *newManifold.B); - if (IS_EXIT || IS_INVALID) - manifold = manifolds.erase(manifold); - else - ++manifold; + auto& collisionState = newManifold.state; + updateCollisionState(IS_COLLIDING, collisionState); + + const bool IS_INVALID = collisionState == SHCollisionState::INVALID; + if (IS_INVALID) + { + manifoldPair = manifolds.erase(manifoldPair); + continue; + } + + updateManifold(oldManifold, newManifold); + ++manifoldPair; } - // Clear expired or invalid triggers - for (auto trigger = triggers.begin(); trigger != triggers.end();) + // Clear expired or invalid triggers, If not, test collision. + for (auto triggerPair = triggers.begin(); triggerPair != triggers.end();) { - const auto COLLISION_STATE = trigger->second; + // Test collision of every trigger. + Trigger& oldTrigger = triggerPair->second; + Trigger newTrigger = oldTrigger; - const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT; - const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID; + const bool IS_COLLIDING = SHCollisionDispatcher::Collide(*newTrigger.A, *newTrigger.B); - if (IS_EXIT || IS_INVALID) - trigger = triggers.erase(trigger); + auto& collisionState = newTrigger.state; + updateCollisionState(IS_COLLIDING, collisionState); + + const bool IS_INVALID = collisionState == SHCollisionState::INVALID; + if (IS_INVALID) + triggerPair = triggers.erase(triggerPair); else - ++trigger; + ++triggerPair; } } - void SHContactManager::AddTrigger(bool isColliding, const SHCollisionKey& key) noexcept + void SHContactManager::AddTrigger(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept { + // If id not found, emplace new trigger. auto trigger = triggers.find(key); - - // If id not found, emplace new object. - // New object is in the invalid state if (trigger == triggers.end()) - trigger = triggers.emplace(key, SHCollisionState::INVALID).first; - - SHCollisionState& state = trigger->second; - updateCollisionState(isColliding, state); - - // If it was a false positive, remove the manifold immediately. - // Remove using iterator as it is on average faster. - if (state == SHCollisionState::INVALID) - trigger = triggers.erase(trigger); + triggers.emplace(key, Trigger{ A, B, SHCollisionState::INVALID }).first; } - void SHContactManager::AddManifold(bool isColliding, const SHCollisionKey& key, const SHManifold& newManifold) noexcept + void SHContactManager::AddManifold(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept { - auto manifold = manifolds.find(key); - // If id not found, emplace new manifold + auto manifold = manifolds.find(key); if (manifold == manifolds.end()) - manifold = manifolds.emplace(key, newManifold).first; - else - { - // TODO: Update existing manifolds with new data - } - - SHCollisionState& state = manifold->second.state; - updateCollisionState(isColliding, state); - - // If it was a false positive, remove the manifold immediately. - // Remove using iterator as it is on average faster. - if (state == SHCollisionState::INVALID) - manifold = manifolds.erase(manifold); + manifolds.emplace(key, SHManifold{ A, B }).first; } void SHContactManager::RemoveInvalidatedTrigger(EntityID eid) noexcept { - remove(triggers, eid); + removeInvalidObject(triggers, eid); } void SHContactManager::RemoveInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) noexcept { - remove(triggers, eid, shapeIndex); + removeInvalidObject(triggers, eid, shapeIndex); } void SHContactManager::RemoveInvalidatedManifold(EntityID eid) noexcept { - remove(manifolds, eid); + removeInvalidObject(manifolds, eid); } void SHContactManager::RemoveInvalidatedManifold(EntityID eid, uint32_t shapeIndex) noexcept { - remove(manifolds, eid, shapeIndex); + removeInvalidObject(manifolds, eid, shapeIndex); } /*-----------------------------------------------------------------------------------*/ @@ -164,10 +158,26 @@ namespace SHADE } else { - // New states start at invalid. In false positive, remain unchanged. + // If already exited and still not colliding, the collision has expired. + // Invalid states are removed in the next frame + if (state == SHCollisionState::EXIT) + state = SHCollisionState::INVALID; + // If previously colliding, move to exit. - state = state == SHCollisionState::INVALID ? SHCollisionState::INVALID : SHCollisionState::EXIT; + if (state == SHCollisionState::ENTER || state == SHCollisionState::STAY) + state = SHCollisionState::EXIT; } } + void SHContactManager::updateManifold(SHManifold& oldManifold, SHManifold& newManifold) noexcept + { + oldManifold.state = newManifold.state; + + // Early out since exiting a collision does not require an update beyond updating the state + if (newManifold.state == SHCollisionState::EXIT) + return; + + + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h index 06a03a6a..9e082db1 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h @@ -54,26 +54,35 @@ namespace SHADE /** * @brief - * Removes any invalidated contacts and triggers. + * Removes any invalidated contacts and triggers, then performs narrowphase collision + * detection on existing triggers and manifolds. * @return */ - void Update () noexcept; + void Update () noexcept; - void AddTrigger (bool isColliding, const SHCollisionKey& key) noexcept; - void AddManifold (bool isColliding, const SHCollisionKey& key, const SHManifold& newManifold) noexcept; + void AddTrigger (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept; + void AddManifold (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept; - void RemoveInvalidatedTrigger (EntityID eid) noexcept; - void RemoveInvalidatedTrigger (EntityID eid, uint32_t shapeIndex) noexcept; - void RemoveInvalidatedManifold (EntityID eid) noexcept; - void RemoveInvalidatedManifold (EntityID eid, uint32_t shapeIndex) noexcept; + void RemoveInvalidatedTrigger (EntityID eid) noexcept; + void RemoveInvalidatedTrigger (EntityID eid, uint32_t shapeIndex) noexcept; + void RemoveInvalidatedManifold (EntityID eid) noexcept; + void RemoveInvalidatedManifold (EntityID eid, uint32_t shapeIndex) noexcept; private: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ + struct Trigger + { + SHCollisionShape* A = nullptr; + SHCollisionShape* B = nullptr; + + SHCollisionState state = SHCollisionState::INVALID; + }; + using Manifolds = std::unordered_map; - using Triggers = std::unordered_map; + using Triggers = std::unordered_map; /*---------------------------------------------------------------------------------*/ /* Data Members */ @@ -86,14 +95,15 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; + static void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; + static void updateManifold (SHManifold& oldManifold, SHManifold& newManifold) noexcept; // Removal Helpers template - void remove (std::unordered_map& container, EntityID eid); + static void removeInvalidObject (std::unordered_map& container, EntityID eid) noexcept; template - void remove (std::unordered_map& container, EntityID eid, uint32_t shapeIndex); + static void removeInvalidObject (std::unordered_map& container, EntityID eid, uint32_t shapeIndex) noexcept; }; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp index 1403cd79..04a4b234 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp @@ -20,7 +20,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ template - void SHContactManager::remove(std::unordered_map& container, EntityID eid) + void SHContactManager::removeInvalidObject(std::unordered_map& container, EntityID eid) noexcept { if (container.empty()) return; @@ -40,7 +40,7 @@ namespace SHADE } template - void SHContactManager::remove(std::unordered_map& container, EntityID eid, uint32_t shapeIndex) + void SHContactManager::removeInvalidObject(std::unordered_map& container, EntityID eid, uint32_t shapeIndex) noexcept { if (container.empty()) return; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index b5dba378..bb42364d 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -81,9 +81,6 @@ namespace SHADE void SHPhysicsWorld::Step(float dt) { - // Contact manager to clear expired contacts - contactManager.Update(); - /* * Detect Collisions */ From 33ef5e0d3d1ffbe433993944d1644b3eec6c897b Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 21 Dec 2022 01:10:28 +0800 Subject: [PATCH 034/164] Implemented accumulated impulses untested --- .../src/Physics/Dynamics/SHContactManager.cpp | 58 ++++++++++++++++--- .../src/Physics/Dynamics/SHContactManager.h | 4 +- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index 0ab20b38..1159f445 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -15,6 +15,7 @@ #include "SHContactManager.h" // Project Headers +#include "Math/SHMathHelpers.h" #include "Physics/Collision/Narrowphase/SHCollisionDispatch.h" namespace SHADE @@ -69,12 +70,12 @@ namespace SHADE for (auto manifoldPair = manifolds.begin(); manifoldPair != manifolds.end();) { // Test collision of every manifold. - SHManifold& oldManifold = manifoldPair->second; - SHManifold newManifold = oldManifold; + SHManifold& manifold = manifoldPair->second; + SHManifold oldManifold = manifold; - const bool IS_COLLIDING = SHCollisionDispatcher::Collide(newManifold, *newManifold.A, *newManifold.B); + const bool IS_COLLIDING = SHCollisionDispatcher::Collide(manifold, *manifold.A, *manifold.B); - auto& collisionState = newManifold.state; + auto& collisionState = oldManifold.state; updateCollisionState(IS_COLLIDING, collisionState); const bool IS_INVALID = collisionState == SHCollisionState::INVALID; @@ -84,7 +85,7 @@ namespace SHADE continue; } - updateManifold(oldManifold, newManifold); + updateManifold(manifold, oldManifold); ++manifoldPair; } @@ -169,15 +170,56 @@ namespace SHADE } } - void SHContactManager::updateManifold(SHManifold& oldManifold, SHManifold& newManifold) noexcept + void SHContactManager::updateManifold(SHManifold& manifold, const SHManifold& oldManifold) noexcept { - oldManifold.state = newManifold.state; + manifold.state = oldManifold.state; // Early out since exiting a collision does not require an update beyond updating the state - if (newManifold.state == SHCollisionState::EXIT) + if (manifold.state == SHCollisionState::EXIT) return; + const SHVec3& NORMAL = manifold.normal; + SHVec3& tangent0 = manifold.tangents[0]; + SHVec3& tangent1 = manifold.tangents[1]; + const SHVec3& OLD_TANGENT_0 = oldManifold.tangents[0]; + const SHVec3& OLD_TANGENT_1 = oldManifold.tangents[1]; + + // Compute tangents + if (std::fabs(NORMAL.x) >= SHMath::EULER_CONSTANT) + tangent0 = SHVec3{ NORMAL.y, -NORMAL.x, 0.0f }; + else + tangent0 = SHVec3{ 0.0f, NORMAL.z, -NORMAL.y }; + + tangent0 = SHVec3::Normalise(tangent0); + tangent1 = SHVec3::Cross(NORMAL, tangent0); + + // Accumulate impulses + for (uint32_t i = 0; i < manifold.numContacts; ++i) + { + SHContact& contact = manifold.contacts[i]; + + // Reset impulses + contact.normalImpulse = 0.0f; + contact.tangentImpulse[0] = contact.tangentImpulse[1] = 0.0f; + + for (uint32_t j = 0; j < oldManifold.numContacts; ++j) + { + const SHContact& OLD_CONTACT = oldManifold.contacts[j]; + + if (OLD_CONTACT.featurePair.key != contact.featurePair.key) + continue; + + // If contact features persists, re-project old solution + contact.normalImpulse = OLD_CONTACT.normalImpulse; + + const SHVec3 FRICTION_FORCE = OLD_TANGENT_0 * OLD_CONTACT.tangentImpulse[0] + OLD_TANGENT_1 * OLD_CONTACT.tangentImpulse[1]; + contact.tangentImpulse[0] = SHVec3::Dot(FRICTION_FORCE, tangent0); + contact.tangentImpulse[1] = SHVec3::Dot(FRICTION_FORCE, tangent1); + + break; + } + } } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h index 9e082db1..ecbf6dd4 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h @@ -95,8 +95,8 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - static void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; - static void updateManifold (SHManifold& oldManifold, SHManifold& newManifold) noexcept; + static void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; + static void updateManifold (SHManifold& manifold, const SHManifold& newManifold) noexcept; // Removal Helpers From d109d06764fc653dfe7ca34e1ec1ed96a025285e Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 21 Dec 2022 18:57:10 +0800 Subject: [PATCH 035/164] Implemented sequential impulses using baumgarte stabilisation There is a bug with masses of static bodies not being properly set --- Assets/Scenes/PhysicsSandbox.shade | 8 +- .../CollisionShapes/SHCollisionShape.cpp | 10 + .../CollisionShapes/SHCollisionShape.h | 3 + .../Collision/Contacts/SHCollisionEvents.h | 2 +- .../Physics/Collision/Contacts/SHContact.h | 30 +- .../Physics/Collision/Contacts/SHManifold.hpp | 80 +---- .../Constraints/SHContactConstraint.h | 57 ++++ .../src/Physics/Dynamics/SHContactManager.cpp | 37 ++- .../src/Physics/Dynamics/SHContactManager.h | 30 +- .../src/Physics/Dynamics/SHContactSolver.cpp | 290 ++++++++++++++++++ .../src/Physics/Dynamics/SHContactSolver.h | 108 +++++++ .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 26 +- .../src/Physics/Dynamics/SHPhysicsWorld.h | 4 +- .../src/Physics/Dynamics/SHRigidBody.cpp | 16 +- .../src/Physics/Dynamics/SHRigidBody.h | 1 + 15 files changed, 599 insertions(+), 103 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp create mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index d983c22b..8323a26e 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,7 +4,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 0.0579863191, z: 0} + Translate: {x: 0, y: 3, z: 0} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -15,7 +15,7 @@ Drag: 1 Angular Drag: 1 Use Gravity: true - Gravity Scale: 10 + Gravity Scale: 1 Interpolate: true Sleeping Enabled: true Freeze Position X: false @@ -48,7 +48,7 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 0.5, z: 2} + Position: {x: 0, y: 0.5, z: 3} Pitch: 0 Yaw: 0 Roll: 0 @@ -70,7 +70,7 @@ Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: - Type: Dynamic + Type: Static Auto Mass: false Mass: 1 Drag: 0.00999999978 diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp index 5dd17d35..d3f26a8b 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -37,6 +37,16 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ + EntityID SHCollisionShape::GetEntityID() const noexcept + { + return id.GetEntityID(); + } + + uint32_t SHCollisionShape::GetIndex() const noexcept + { + return id.GetShapeIndex(); + } + float SHCollisionShape::GetFriction() const noexcept { return material.GetFriction(); diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 6e779eea..2cc156e1 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -75,6 +75,9 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ + [[nodiscard]] EntityID GetEntityID () const noexcept; + [[nodiscard]] uint32_t GetIndex () const noexcept; + // Material Properties // TODO: Remove individual setters once instanced materials are supported diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h index db55ca50..3dbb16d0 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h @@ -51,7 +51,7 @@ namespace SHADE public: static constexpr int MAX_NUM_CONTACTS = 4; - SHCollisionKey info; + SHCollisionKey info; SHCollisionState state; SHVec3 normal; SHVec3 contactPoints[MAX_NUM_CONTACTS]; diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h index c70f6e39..15c24ef2 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h @@ -27,16 +27,26 @@ namespace SHADE union SHContactFeatures { public: + /*---------------------------------------------------------------------------------*/ + /* Type Definit */ + /*---------------------------------------------------------------------------------*/ + + enum class Type : uint8_t + { + VERTEX = 0 + , FACE = 1 + }; + /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ struct { - uint8_t incomingIncident; - uint8_t outgoingIncident; - uint8_t incomingReference; - uint8_t outgoingReference; + uint8_t indexA; + uint8_t indexB; + uint8_t typeA; + uint8_t typeB; }; uint32_t key = 0; @@ -56,13 +66,15 @@ namespace SHADE static constexpr int NUM_TANGENTS = 2; float penetration = 0.0f; - float bias = 0.0f; - float normalImpulse = 0.0f; - float normalMass = 0.0f; - float tangentImpulse[NUM_TANGENTS] = { 0.0f }; - float tangentMass[NUM_TANGENTS] = { 0.0f }; + float bias = 0.0f; // Restitution + Baumguarte factor + float normalImpulse = 0.0f; // Accumulated normal impulse + float normalMass = 0.0f; // Effective mass along the normal + float tangentImpulse[NUM_TANGENTS] = { 0.0f }; // Accumulated tangent impulses + float tangentMass[NUM_TANGENTS] = { 0.0f }; // Effective masses along the tangents SHVec3 position; + SHVec3 rA; // Vector from COM of A to the contact + SHVec3 rB; // Vector from COM of B to the contact SHContactFeatures featurePair; }; } diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp index f0e1374f..e43b9625 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp @@ -31,25 +31,11 @@ namespace SHADE , state { rhs.state } , normal { rhs.normal } { + static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); + memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); + for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) tangents[i] = rhs.tangents[i]; - - for (int i = 0; i < MAX_NUM_CONTACTS; ++i) - { - contacts[i].penetration = rhs.contacts[i].penetration; - contacts[i].bias = rhs.contacts[i].bias; - contacts[i].normalImpulse = rhs.contacts[i].normalImpulse; - contacts[i].normalMass = rhs.contacts[i].normalMass; - - for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) - { - contacts[i].tangentImpulse[j] = rhs.contacts[i].tangentImpulse[j]; - contacts[i].tangentMass[j] = rhs.contacts[i].tangentMass[j]; - } - - contacts[i].position = rhs.contacts[i].position; - contacts[i].featurePair.key = rhs.contacts[i].featurePair.key; - } } inline SHManifold::SHManifold(SHManifold&& rhs) noexcept @@ -59,25 +45,11 @@ namespace SHADE , state { rhs.state } , normal { rhs.normal } { + static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); + memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); + for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) tangents[i] = rhs.tangents[i]; - - for (int i = 0; i < MAX_NUM_CONTACTS; ++i) - { - contacts[i].penetration = rhs.contacts[i].penetration; - contacts[i].bias = rhs.contacts[i].bias; - contacts[i].normalImpulse = rhs.contacts[i].normalImpulse; - contacts[i].normalMass = rhs.contacts[i].normalMass; - - for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) - { - contacts[i].tangentImpulse[j] = rhs.contacts[i].tangentImpulse[j]; - contacts[i].tangentMass[j] = rhs.contacts[i].tangentMass[j]; - } - - contacts[i].position = rhs.contacts[i].position; - contacts[i].featurePair.key = rhs.contacts[i].featurePair.key; - } } /*-----------------------------------------------------------------------------------*/ @@ -95,26 +67,12 @@ namespace SHADE state = rhs.state; normal = rhs.normal; + static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); + memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); + for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) tangents[i] = rhs.tangents[i]; - for (int i = 0; i < MAX_NUM_CONTACTS; ++i) - { - contacts[i].penetration = rhs.contacts[i].penetration; - contacts[i].bias = rhs.contacts[i].bias; - contacts[i].normalImpulse = rhs.contacts[i].normalImpulse; - contacts[i].normalMass = rhs.contacts[i].normalMass; - - for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) - { - contacts[i].tangentImpulse[j] = rhs.contacts[i].tangentImpulse[j]; - contacts[i].tangentMass[j] = rhs.contacts[i].tangentMass[j]; - } - - contacts[i].position = rhs.contacts[i].position; - contacts[i].featurePair.key = rhs.contacts[i].featurePair.key; - } - return *this; } @@ -126,26 +84,12 @@ namespace SHADE state = rhs.state; normal = rhs.normal; + static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); + memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); + for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) tangents[i] = rhs.tangents[i]; - for (int i = 0; i < MAX_NUM_CONTACTS; ++i) - { - contacts[i].penetration = rhs.contacts[i].penetration; - contacts[i].bias = rhs.contacts[i].bias; - contacts[i].normalImpulse = rhs.contacts[i].normalImpulse; - contacts[i].normalMass = rhs.contacts[i].normalMass; - - for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) - { - contacts[i].tangentImpulse[j] = rhs.contacts[i].tangentImpulse[j]; - contacts[i].tangentMass[j] = rhs.contacts[i].tangentMass[j]; - } - - contacts[i].position = rhs.contacts[i].position; - contacts[i].featurePair.key = rhs.contacts[i].featurePair.key; - } - return *this; } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h new file mode 100644 index 00000000..55e195a6 --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h @@ -0,0 +1,57 @@ +/**************************************************************************************** + * \file SHContactConstraint.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Contact Constraint. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Physics/Collision/Contacts/SHManifold.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + struct SH_API SHContactConstraint + { + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + // Use the entity IDs to map resolved constraints back to the bodies + + EntityID idA = MAX_EID; + EntityID idB = MAX_EID; + + uint32_t numContacts = 0; + + // Material Data + + float friction = 0.0f; + float restitution = 0.0f; + + // Mass Data + + float invMassA = 0.0f; + float invMassB = 0.0f; + SHMatrix invInertiaA; + SHMatrix invInertiaB; + SHVec3 centerOfMassA; + SHVec3 centerOfMassB; + + // Collision Data + + SHVec3 normal; + SHVec3 tangents[SHContact::NUM_TANGENTS]; + SHContact contacts[SHManifold::MAX_NUM_CONTACTS]; + }; +} // namespace SHADE + diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index 1159f445..3ad3c0b3 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -60,6 +60,25 @@ namespace SHADE return collisionEvents; } + const SHContactManager::ContactPoints& SHContactManager::GetContactPoints() const noexcept + { + static ContactPoints contactPoints; + + contactPoints.clear(); + + for (auto& manifold : manifolds | std::views::values) + { + // Skip exit state manifolds + if (manifold.state == SHCollisionState::EXIT) + continue; + + for (uint32_t i = 0; i < manifold.numContacts; ++i) + contactPoints.emplace_back(manifold.contacts[i].position); + } + + return contactPoints; + } + /*-----------------------------------------------------------------------------------*/ /* Public Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -207,17 +226,17 @@ namespace SHADE { const SHContact& OLD_CONTACT = oldManifold.contacts[j]; - if (OLD_CONTACT.featurePair.key != contact.featurePair.key) - continue; + if (OLD_CONTACT.featurePair.key == contact.featurePair.key) + { + // If contact features persists, re-project old solution + contact.normalImpulse = OLD_CONTACT.normalImpulse; - // If contact features persists, re-project old solution - contact.normalImpulse = OLD_CONTACT.normalImpulse; + const SHVec3 FRICTION_FORCE = OLD_TANGENT_0 * OLD_CONTACT.tangentImpulse[0] + OLD_TANGENT_1 * OLD_CONTACT.tangentImpulse[1]; + contact.tangentImpulse[0] = SHVec3::Dot(FRICTION_FORCE, tangent0); + contact.tangentImpulse[1] = SHVec3::Dot(FRICTION_FORCE, tangent1); - const SHVec3 FRICTION_FORCE = OLD_TANGENT_0 * OLD_CONTACT.tangentImpulse[0] + OLD_TANGENT_1 * OLD_CONTACT.tangentImpulse[1]; - contact.tangentImpulse[0] = SHVec3::Dot(FRICTION_FORCE, tangent0); - contact.tangentImpulse[1] = SHVec3::Dot(FRICTION_FORCE, tangent1); - - break; + break; + } } } } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h index ecbf6dd4..692dc2e8 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h @@ -1,8 +1,7 @@ /**************************************************************************************** * \file SHContactManager.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Contact Manager that stores collision information and - * resolves contact constraints. + * \brief Interface for a Contact Manager that stores collision information. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -24,8 +23,19 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates a class that stores collision information. + */ class SH_API SHContactManager { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHPhysicsWorld; + public: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -33,6 +43,7 @@ namespace SHADE using TriggerEvents = std::vector; using CollisionEvents = std::vector; + using ContactPoints = std::vector; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -42,11 +53,12 @@ namespace SHADE ~SHContactManager () noexcept = default; /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ + /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - const TriggerEvents& GetTriggerEvents () const noexcept; - const CollisionEvents& GetCollisionEvents () const noexcept; + const TriggerEvents& GetTriggerEvents () const noexcept; + const CollisionEvents& GetCollisionEvents () const noexcept; + const ContactPoints& GetContactPoints () const noexcept; /*---------------------------------------------------------------------------------*/ /* Member Functions */ @@ -58,7 +70,7 @@ namespace SHADE * detection on existing triggers and manifolds. * @return */ - void Update () noexcept; + void Update () noexcept; void AddTrigger (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept; void AddManifold (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept; @@ -88,15 +100,15 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - Manifolds manifolds; - Triggers triggers; + Manifolds manifolds; + Triggers triggers; /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ static void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; - static void updateManifold (SHManifold& manifold, const SHManifold& newManifold) noexcept; + static void updateManifold (SHManifold& manifold, const SHManifold& oldManifold) noexcept; // Removal Helpers diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp new file mode 100644 index 00000000..84805c57 --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp @@ -0,0 +1,290 @@ +/**************************************************************************************** + * \file SHContactSolver.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Contact Solver. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +***************************************************************************************/ + +#include + +// Primary Header +#include "SHContactSolver.h" + +// Project Headers +#include "Math/SHMathHelpers.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHContactSolver::VelocityStates& SHContactSolver::GetVelocities() const noexcept + { + return velocityStates; + } + + const SHContactSolver::ContactConstraints& SHContactSolver::GetContantConstraints() const noexcept + { + return contactConstraints; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHContactSolver::AddContact(const SHManifold& manifold, const SHRigidBody* rigidBodyA, const SHRigidBody* rigidBodyB) noexcept + { + SHContactConstraint& newConstraint = contactConstraints.emplace_back(SHContactConstraint{}); + + const auto* SHAPE_A = manifold.A; + const auto* SHAPE_B = manifold.B; + + newConstraint.idA = SHAPE_A->GetEntityID(); + newConstraint.idB = SHAPE_B->GetEntityID(); + + // Add velocities if it doesn't already exist + velocityStates.emplace(newConstraint.idA, VelocityState{ rigidBodyA->linearVelocity, rigidBodyB->angularVelocity }); + velocityStates.emplace(newConstraint.idB, VelocityState{ rigidBodyB->linearVelocity, rigidBodyB->angularVelocity }); + + // Mix friction & restitution + const float FRICTION_A = manifold.A->GetFriction(); + const float RESTITUTION_A = manifold.A->GetBounciness(); + + const float FRICTION_B = manifold.B->GetFriction(); + const float RESTITUTION_B = manifold.B->GetBounciness(); + + newConstraint.friction = std::sqrtf(FRICTION_A * FRICTION_B); + newConstraint.restitution = std::max(RESTITUTION_A, RESTITUTION_B); + + // Mass data + + newConstraint.invMassA = rigidBodyA->invMass; + newConstraint.invMassB = rigidBodyB->invMass; + + newConstraint.invInertiaA = rigidBodyA->worldInvInertia; + newConstraint.invInertiaB = rigidBodyB->worldInvInertia; + + newConstraint.centerOfMassA = rigidBodyA->worldCentroid; + newConstraint.centerOfMassB = rigidBodyB->worldCentroid; + + // Collision data + + newConstraint.numContacts = manifold.numContacts; + + newConstraint.normal = manifold.normal; + + static constexpr size_t TANGENTS_SIZE = sizeof(SHVec3) * SHContact::NUM_TANGENTS; + static constexpr size_t CONTACTS_SIZE = sizeof(SHContact) * SHManifold::MAX_NUM_CONTACTS; + + memcpy_s(newConstraint.tangents, TANGENTS_SIZE, manifold.tangents, TANGENTS_SIZE); + memcpy_s(newConstraint.contacts, CONTACTS_SIZE, manifold.contacts, CONTACTS_SIZE); + + // Compute rA & rB for contacts + for (uint32_t i = 0; i < newConstraint.numContacts; ++i) + { + newConstraint.contacts[i].rA = newConstraint.contacts[i].position - newConstraint.centerOfMassA; + newConstraint.contacts[i].rB = newConstraint.contacts[i].position - newConstraint.centerOfMassB; + } + } + + void SHContactSolver::ClearContacts() noexcept + { + contactConstraints.clear(); + } + + void SHContactSolver::SolveContacts(int numIterations, float dt) noexcept + { + preSolve(dt); + + for (int i = 0; i < numIterations; ++i) + solve(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHContactSolver::preSolve(float dt) noexcept + { + const float INV_DT = 1.0f / dt; + + for (auto& constraint : contactConstraints) + { + const float INV_MASS_SUM = constraint.invMassA + constraint.invMassB; + + SHVec3 vA = velocityStates[constraint.idA].linearVelocity; + SHVec3 wA = velocityStates[constraint.idA].angularVelocity; + SHVec3 vB = velocityStates[constraint.idB].linearVelocity; + SHVec3 wB = velocityStates[constraint.idB].angularVelocity; + + for (uint32_t i = 0; i < constraint.numContacts; ++i) + { + SHContact& contact = constraint.contacts[i]; + + // Calculate JM-1JT (Effective mass) + /* + * rXnT * I-1 * rXn: + * + * 1. 3x3 * 3x1 = 3x1 + * 2. 1x3 * 3x1 = 1x1 + * + * First do I-1 * rXn + * | ix 0 0 || x | | ix * x | + * | 0 iy 0 || y | = | iy * y | + * | 0 0 iz || z | | iz * z | + * + * Then dot product the result with rXnT + * | ix * x |[ u v w ] + * | iy * y | = [ ix * x * w + iy * y * v + iz * z * w ] + * | iz * z | + * + * Simplified: + * + * rXnT /dot (I-1 * rXn) + */ + + // Effective mass along Normal + const SHVec3 RA_CROSS_N = SHVec3::Cross(contact.rA, constraint.normal); + const SHVec3 RB_CROSS_N = SHVec3::Cross(contact.rB, constraint.normal); + + contact.normalMass = INV_MASS_SUM; + contact.normalMass += SHVec3::Dot(RA_CROSS_N, constraint.invInertiaA * RA_CROSS_N); + contact.normalMass += SHVec3::Dot(RB_CROSS_N, constraint.invInertiaB * RB_CROSS_N); + + // Invert the normal mass (we want the actual mass, not the inverse mass) + contact.normalMass = contact.normalMass == 0.0f ? 0.0f : 1.0f / contact.normalMass; + + // Effective mass along tangents (same steps as above) + + for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) + { + const SHVec3 RA_CROSS_T = SHVec3::Cross(contact.rA, constraint.tangents[j]); + const SHVec3 RB_CROSS_T = SHVec3::Cross(contact.rB, constraint.tangents[j]); + + contact.tangentMass[j] = INV_MASS_SUM; + contact.tangentMass[j] += SHVec3::Dot(RA_CROSS_T, constraint.invInertiaA * RA_CROSS_T); + contact.tangentMass[j] += SHVec3::Dot(RB_CROSS_T, constraint.invInertiaB * RB_CROSS_T); + + contact.tangentMass[j] = contact.tangentMass[j] == 0.0f ? 0.0f : 1.0f / contact.tangentMass[j]; + } + + // Warm starting + // Compute impulses + SHVec3 impulse = constraint.normal * contact.normalImpulse; + for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) + impulse += constraint.tangents[j] * contact.tangentImpulse[j]; + + // Apply impulses onto velocities + vA -= impulse * constraint.invMassA; + wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, impulse); + + vB += impulse * constraint.invMassB; + wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, impulse); + + // Calculate bias per contact + /* + * error bias = baumgarte factor / dt * penetration + * restituion bias = restitution * (relative velocity /dot normal) + */ + + const SHVec3 RV_A = vA + SHVec3::Cross(wA, contact.rA); + const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB); + const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal); + + const float ERROR_BIAS = BAUMGARTE_FACTOR * INV_DT * contact.penetration; + const float RESTITUTION_BIAS = -constraint.restitution * RV_N; + + contact.bias = ERROR_BIAS + RESTITUTION_BIAS; + } + + velocityStates[constraint.idA].linearVelocity = vA; + velocityStates[constraint.idA].angularVelocity = wA; + velocityStates[constraint.idB].linearVelocity = vB; + velocityStates[constraint.idB].angularVelocity = wB; + + } + } + + void SHContactSolver::solve() noexcept + { + for (auto& constraint : contactConstraints) + { + SHVec3 vA = velocityStates[constraint.idA].linearVelocity; + SHVec3 wA = velocityStates[constraint.idA].angularVelocity; + SHVec3 vB = velocityStates[constraint.idB].linearVelocity; + SHVec3 wB = velocityStates[constraint.idB].angularVelocity; + + for (uint32_t i = 0; i < constraint.numContacts; ++i) + { + SHContact& contact = constraint.contacts[i]; + + // Compute relative velocity + SHVec3 velocityA = vA + SHVec3::Cross(wA, contact.rA); + SHVec3 velocityB = vB + SHVec3::Cross(wB, contact.rB); + SHVec3 relativeVelocity = velocityB - velocityA; + + // Solve tangent impulse + for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) + { + // Get scalar of relative velocity along tangent + const float VT = SHVec3::Dot(relativeVelocity, constraint.tangents[j]); + + // Compute true tangent impulse + const float MAX_TANGENT_IMPULSE = constraint.friction * contact.normalImpulse; + const float OLD_TANGENT_IMPULSE = contact.tangentImpulse[j]; + + // We cannot exceed the maximum frictional force (coulumb's law) + // Compute true tangent impulse + float newTangentImpulse = -VT * contact.tangentMass[j]; + contact.tangentImpulse[j] = std::clamp(OLD_TANGENT_IMPULSE + newTangentImpulse, -MAX_TANGENT_IMPULSE, MAX_TANGENT_IMPULSE); + newTangentImpulse = contact.tangentImpulse[j] - OLD_TANGENT_IMPULSE; + + const SHVec3 TANGENT_IMPULSE = newTangentImpulse * constraint.tangents[j]; + + // Apply impulses + vA -= TANGENT_IMPULSE * constraint.invMassA; + wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, TANGENT_IMPULSE); + + vB += TANGENT_IMPULSE * constraint.invMassB; + wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, TANGENT_IMPULSE); + } + + // Solve normal impulse + // Re-compute relative velocity + velocityA = vA + SHVec3::Cross(wA, contact.rA); + velocityB = vB + SHVec3::Cross(wB, contact.rB); + relativeVelocity = velocityB - velocityA; + + // Get scalar of relative velocity along the normal + const float VN = SHVec3::Dot(relativeVelocity, constraint.normal); + + // Compute true normal impulse + const float OLD_NORMAL_IMPULSE = contact.normalImpulse; + + float newNormalImpulse = -(VN + contact.bias) * contact.normalMass; + contact.normalImpulse = std::max(OLD_NORMAL_IMPULSE + newNormalImpulse, 0.0f); + newNormalImpulse = contact.normalImpulse - OLD_NORMAL_IMPULSE; + + const SHVec3 NORMAL_IMPULSE = newNormalImpulse * constraint.normal; + + // Apply impulses + vA -= NORMAL_IMPULSE * constraint.invMassA; + wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, NORMAL_IMPULSE); + + vB += NORMAL_IMPULSE * constraint.invMassB; + wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE); + } + + velocityStates[constraint.idA].linearVelocity = vA; + velocityStates[constraint.idA].angularVelocity = wA; + velocityStates[constraint.idB].linearVelocity = vB; + velocityStates[constraint.idB].angularVelocity = wB; + } + } + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h new file mode 100644 index 00000000..28d5de6f --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h @@ -0,0 +1,108 @@ +/**************************************************************************************** + * \file SHContactSolver.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Contact Solver that builds contacct constraints and solves + * them. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Constraints/SHContactConstraint.h" +#include "SHContactManager.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates an object that builds contact constraints and solves them. + */ + class SH_API SHContactSolver + { + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + struct VelocityState + { + // Velocities + + SHVec3 linearVelocity; + SHVec3 angularVelocity; + }; + + using VelocityStates = std::unordered_map; + using ContactConstraints = std::vector; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHContactSolver () noexcept = default; + ~SHContactSolver () noexcept = default; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] const VelocityStates& GetVelocities () const noexcept; + [[nodiscard]] const ContactConstraints& GetContantConstraints () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Build a contact constraint from a new manifold. + * @param manifold + * A manifold to build a contact constraint from. + * @param rigidBodyA + * The rigid body belonging to the first collision shape. + * @param rigidBodyB + * The rigid body belonging to the second collision shape. + */ + void AddContact (const SHManifold& manifold, const SHRigidBody* rigidBodyA, const SHRigidBody* rigidBodyB) noexcept; + + void ClearContacts () noexcept; + + /** + * @brief + * Solves all the contact constraints. + * @param numIterations + * The number of times to iterate over constraints when solving them. + * @param dt + * The delta time of the simulation step. + */ + void SolveContacts (int numIterations, float dt) noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr float BAUMGARTE_FACTOR = 0.2f; + static constexpr float PENETRATION_SLOP = 0.05f; + + VelocityStates velocityStates; + ContactConstraints contactConstraints; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + void preSolve (float dt) noexcept; + void solve () noexcept; + + }; + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index bb42364d..cb453d24 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -103,9 +103,33 @@ namespace SHADE /* - * TODO: Resolve Contacts + * Resolve Contacts */ + // Build constraints + for (auto& [id, manifold] : contactManager.manifolds) + { + SHRigidBody* bodyA = rigidBodies[id.GetEntityA()]; + SHRigidBody* bodyB = rigidBodies[id.GetEntityB()]; + + contactSolver.AddContact(manifold, bodyA, bodyB); + } + + // Solve contacts + contactSolver.SolveContacts(settings.numVelocitySolverIterations, dt); + + // Map velocities back to bodies + const auto& VELOCITY_STATES = contactSolver.GetVelocities(); + for (auto& [id, velocityState] : VELOCITY_STATES) + { + SHRigidBody* body = rigidBodies[id]; + body->linearVelocity = velocityState.linearVelocity; + body->angularVelocity = velocityState.angularVelocity; + } + + // Clear contacts + contactSolver.ClearContacts(); + /* * Integrate Velocities */ diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index 8248299a..9bc2199e 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -15,6 +15,7 @@ // Project Headers #include "Physics/Collision/SHCollisionSpace.h" #include "SHContactManager.h" +#include "SHContactSolver.h" #include "SHRigidBody.h" @@ -46,7 +47,7 @@ namespace SHADE SHVec3 gravity = SHVec3{ 0.0f, -9.81f, 0.0f }; uint16_t numVelocitySolverIterations = 10; - uint16_t numPositionSolverIterations = 5; + uint16_t numPositionSolverIterations = 5; // Unused until PGS is implemented bool sleepingEnabled = true; }; @@ -128,6 +129,7 @@ namespace SHADE RigidBodies rigidBodies; SHContactManager contactManager; + SHContactSolver contactSolver; /*---------------------------------------------------------------------------------*/ /* Function Members */ diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index aa8027c7..542b6082 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -296,7 +296,18 @@ namespace SHADE return; bodyType = newType; - invMass = newType == Type::DYNAMIC ? 1.0f : 0.0f; + + if (bodyType != Type::DYNAMIC) + { + invMass = 0.0f; + localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = 0.0f; + worldInvInertia.m[0][0] = worldInvInertia.m[1][1] = worldInvInertia.m[2][2] = 0.0f; + } + else + { + invMass = 1.0f; + localInvInertia = SHMatrix::Identity; + } } void SHRigidBody::SetGravityScale(float newGravityScale) noexcept @@ -598,6 +609,9 @@ namespace SHADE void SHRigidBody::ComputeWorldData() noexcept { + if (bodyType == Type::STATIC) + return; + const SHMatrix ROTATION = SHMatrix::Rotate(motionState.orientation); // Compute world inertia diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index ab30bbcf..606895ee 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -40,6 +40,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ friend class SHPhysicsWorld; + friend class SHContactSolver; public: /*-----------------------------------------------------------------------------------*/ From 92ed8a29ffaa97895631f3b787df635b357073b3 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 21 Dec 2022 19:04:10 +0800 Subject: [PATCH 036/164] Fixed bug with non-dynamic masses being overriden --- Assets/Scenes/PhysicsSandbox.shade | 2 +- SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp | 1 + SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 8323a26e..f2a5dd95 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -72,7 +72,7 @@ RigidBody Component: Type: Static Auto Mass: false - Mass: 1 + Mass: .inf Drag: 0.00999999978 Angular Drag: 0.00999999978 Use Gravity: false diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index 542b6082..b724321b 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -319,6 +319,7 @@ namespace SHADE { if (bodyType != Type::DYNAMIC) { + invMass = 0.0f; SHLOG_WARNING("Cannot set mass of a non-Dynamic Body {}", entityID) return; } diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp index 1d1c06bf..04bb5b6b 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp @@ -20,7 +20,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHRigidBodyComponent::SHRigidBodyComponent() noexcept - : type { Type::STATIC } + : type { Type::DYNAMIC } , interpolate { true } , rigidBody { nullptr } {} From f4f6cb7eae178a333730ab6fed7f461b94c57829 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 22 Dec 2022 01:10:25 +0800 Subject: [PATCH 037/164] Fixed sequential impulses --- Assets/Scenes/PhysicsSandbox.shade | 2 +- SHADE_Engine/src/Math/Geometry/SHSphere.cpp | 2 +- .../CollisionShapes/SHCollisionShape.cpp | 7 +- .../CollisionShapes/SHCollisionShape.h | 21 ++-- .../SHSphereCollisionShape.cpp | 20 ++-- .../Physics/Collision/Contacts/SHContact.h | 2 +- .../Physics/Collision/Contacts/SHManifold.h | 7 +- .../Physics/Collision/Contacts/SHManifold.hpp | 33 ++++-- .../src/Physics/Collision/SHCollider.cpp | 2 +- .../src/Physics/Collision/SHCollider.h | 3 +- .../Constraints/SHContactConstraint.h | 7 +- .../src/Physics/Dynamics/SHContactManager.cpp | 110 ++++++++++++------ .../src/Physics/Dynamics/SHContactManager.h | 25 ++-- .../src/Physics/Dynamics/SHContactSolver.cpp | 87 +++++++------- .../src/Physics/Dynamics/SHContactSolver.h | 11 +- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 25 +--- 16 files changed, 201 insertions(+), 163 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index f2a5dd95..90cba8d0 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,7 +4,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 3, z: 0} + Translate: {x: 0.186280191, y: 4.3224473, z: 0} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp index 7dca00c0..7dbc8f41 100644 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp @@ -115,7 +115,7 @@ namespace SHADE bool SHSphere::Contains(const SHSphere& rhs) const noexcept { - return BoundingSphere::Contains(rhs); + return BoundingSphere::Contains(rhs) == CONTAINS; } float SHSphere::Volume() const noexcept diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp index d3f26a8b..3696dd50 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -27,7 +27,7 @@ namespace SHADE SHCollisionShape::SHCollisionShape(SHCollisionShapeID id, Type colliderType) : id { id } , flags { 0 } - , parentTransform { nullptr } + , collider { nullptr } , collisionTag { SHCollisionTagMatrix::GetTag(0) } { flags |= 1U << SHUtilities::ConvertEnum(colliderType); @@ -135,11 +135,6 @@ namespace SHADE material = newMaterial; } - void SHCollisionShape::SetParentTransform(SHTransform& parentTF) noexcept - { - parentTransform = &parentTF; - } - void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept { transform.position = posOffset; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 2cc156e1..0690b071 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -22,6 +22,12 @@ namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + class SHRigidBody; + /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -34,10 +40,11 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ - friend class SHCollider; - friend class SHColliderComponent; - friend class SHCollisionShapeFactory; - friend class SHCollisionSpace; + friend class SHCollider; + friend class SHColliderComponent; + friend class SHCollisionShapeFactory; + friend class SHCollisionSpace; + friend struct SHManifold; public: /*---------------------------------------------------------------------------------*/ @@ -108,8 +115,6 @@ namespace SHADE void SetDensity (float density) noexcept; void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; - void SetParentTransform (SHTransform& parentTF) noexcept; - void SetPositionOffset (const SHVec3& posOffset) noexcept; void SetRotationOffset (const SHVec3& rotOffset) noexcept; @@ -137,13 +142,13 @@ namespace SHADE SHPhysicsMaterial material; - SHTransform* parentTransform; + SHCollider* collider; // The collider it belongs to. SHTransform transform; // Needed for conversion to euler angles SHVec3 rotationOffset; - uint8_t flags; // 0 0 0 isColliding trigger capsule sphere box + uint8_t flags; // 0 0 0 isColliding trigger capsule sphere box SHCollisionTag* collisionTag; RTTR_ENABLE() diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp index b1110489..30497f36 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -16,6 +16,7 @@ // Project Headers #include "Math/SHMathHelpers.h" #include "Math/SHMatrix.h" +#include "Physics/Collision/SHCollider.h" namespace SHADE { @@ -38,7 +39,7 @@ namespace SHADE { material = rhs.material; - parentTransform = rhs.parentTransform; + collider = rhs.collider; transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; @@ -54,7 +55,7 @@ namespace SHADE { material = rhs.material; - parentTransform = rhs.parentTransform; + collider = rhs.collider; transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; @@ -75,7 +76,7 @@ namespace SHADE id = rhs.id; material = rhs.material; - parentTransform = rhs.parentTransform; + collider = rhs.collider; transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; @@ -101,7 +102,7 @@ namespace SHADE id = rhs.id; material = rhs.material; - parentTransform = rhs.parentTransform; + collider = rhs.collider; transform = rhs.transform; rotationOffset = rhs.rotationOffset; flags = rhs.flags; @@ -179,12 +180,14 @@ namespace SHADE void SHSphereCollisionShape::ComputeTransforms() noexcept { - const float SPHERE_SCALE = std::fabs(SHMath::Max({ parentTransform->scale.x, parentTransform->scale.y, parentTransform->scale.z })); + const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); + + const float SPHERE_SCALE = std::fabs(SHMath::Max({ PARENT_TRANSFORM.scale.x, PARENT_TRANSFORM.scale.y, PARENT_TRANSFORM.scale.z })); SetScale(SPHERE_SCALE); // Recompute center - const SHQuaternion FINAL_ROT = parentTransform->orientation * transform.orientation; - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(parentTransform->position); + const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); Center = SHVec3::Transform(transform.position, TRS); } @@ -212,7 +215,8 @@ namespace SHADE SHMatrix SHSphereCollisionShape::ComputeWorldTransform() const noexcept { - const SHQuaternion ROTATION = parentTransform->orientation * transform.orientation; + const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); + const SHQuaternion ROTATION = PARENT_TRANSFORM.orientation * transform.orientation; const SHVec3 SCALE{ Radius }; return SHMatrix::Transform diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h index 15c24ef2..0c137f2f 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h @@ -49,7 +49,7 @@ namespace SHADE uint8_t typeB; }; - uint32_t key = 0; + uint32_t key = std::numeric_limits::max(); }; /** diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h index c2b6ddb2..15741276 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h @@ -49,8 +49,11 @@ namespace SHADE // We only need 4 contact points to build a stable manifold. static constexpr int MAX_NUM_CONTACTS = 4; - SHCollisionShape* A; - SHCollisionShape* B; + SHCollisionShape* shapeA; + SHCollisionShape* shapeB; + + SHRigidBody* bodyA = nullptr; + SHRigidBody* bodyB = nullptr; uint32_t numContacts = 0; SHCollisionState state = SHCollisionState::INVALID; diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp index e43b9625..23be8c79 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp @@ -20,13 +20,18 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ inline SHManifold::SHManifold(SHCollisionShape* a, SHCollisionShape* b) noexcept - : A { a } - , B { b } - {} + : shapeA { a } + , shapeB { b } + { + bodyA = shapeA->collider->rigidBody; + bodyB = shapeB->collider->rigidBody; + } inline SHManifold::SHManifold(const SHManifold& rhs) noexcept - : A { rhs.A } - , B { rhs.B } + : shapeA { rhs.shapeA } + , shapeB { rhs.shapeB } + , bodyA { rhs.bodyA } + , bodyB { rhs.bodyB } , numContacts { rhs.numContacts } , state { rhs.state } , normal { rhs.normal } @@ -39,8 +44,10 @@ namespace SHADE } inline SHManifold::SHManifold(SHManifold&& rhs) noexcept - : A { rhs.A } - , B { rhs.B } + : shapeA { rhs.shapeA } + , shapeB { rhs.shapeB } + , bodyA { rhs.bodyA } + , bodyB { rhs.bodyB } , numContacts { rhs.numContacts } , state { rhs.state } , normal { rhs.normal } @@ -61,8 +68,10 @@ namespace SHADE if (this == &rhs) return *this; - A = rhs.A; - B = rhs.B; + shapeA = rhs.shapeA; + shapeB = rhs.shapeB; + bodyA = rhs.bodyA; + bodyB = rhs.bodyB; numContacts = rhs.numContacts; state = rhs.state; normal = rhs.normal; @@ -78,8 +87,10 @@ namespace SHADE inline SHManifold& SHManifold::operator=(SHManifold&& rhs) noexcept { - A = rhs.A; - B = rhs.B; + shapeA = rhs.shapeA; + shapeB = rhs.shapeB; + bodyA = rhs.bodyA; + bodyB = rhs.bodyB; numContacts = rhs.numContacts; state = rhs.state; normal = rhs.normal; diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index 928945c9..7d7d1a60 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -307,7 +307,7 @@ namespace SHADE SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); // Set offsets - sphere->SetParentTransform(transform); + sphere->collider = this; sphere->SetPositionOffset(posOffset); sphere->SetRotationOffset(rotOffset); diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index c31be5eb..51b33cfb 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -40,7 +40,8 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ - friend class SHCollisionSpace; + friend class SHCollisionSpace; + friend struct SHManifold; public: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h index 55e195a6..04be00b4 100644 --- a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h +++ b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h @@ -28,10 +28,7 @@ namespace SHADE // Use the entity IDs to map resolved constraints back to the bodies - EntityID idA = MAX_EID; - EntityID idB = MAX_EID; - - uint32_t numContacts = 0; + SHCollisionKey key; // Material Data @@ -52,6 +49,8 @@ namespace SHADE SHVec3 normal; SHVec3 tangents[SHContact::NUM_TANGENTS]; SHContact contacts[SHManifold::MAX_NUM_CONTACTS]; + + uint32_t numContacts = 0; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index 3ad3c0b3..3b80b3b3 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -83,6 +83,42 @@ namespace SHADE /* Public Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ + void SHContactManager::AddTrigger(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept + { + // If id not found, emplace new trigger. + auto trigger = triggers.find(key); + if (trigger == triggers.end()) + triggers.emplace(key, Trigger{ A, B, SHCollisionState::INVALID }); + } + + void SHContactManager::AddManifold(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept + { + // If id not found, emplace new manifold + auto manifold = manifolds.find(key); + if (manifold == manifolds.end()) + manifolds.emplace(key, SHManifold{ A, B }); + } + + void SHContactManager::RemoveInvalidatedTrigger(EntityID eid) noexcept + { + removeInvalidObject(triggers, eid); + } + + void SHContactManager::RemoveInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) noexcept + { + removeInvalidObject(triggers, eid, shapeIndex); + } + + void SHContactManager::RemoveInvalidatedManifold(EntityID eid) noexcept + { + removeInvalidObject(manifolds, eid); + } + + void SHContactManager::RemoveInvalidatedManifold(EntityID eid, uint32_t shapeIndex) noexcept + { + removeInvalidObject(manifolds, eid, shapeIndex); + } + void SHContactManager::Update() noexcept { // Clear expired or invalid collisions. If not, test collision. @@ -92,9 +128,9 @@ namespace SHADE SHManifold& manifold = manifoldPair->second; SHManifold oldManifold = manifold; - const bool IS_COLLIDING = SHCollisionDispatcher::Collide(manifold, *manifold.A, *manifold.B); + const bool IS_COLLIDING = SHCollisionDispatcher::Collide(manifold, *manifold.shapeA, *manifold.shapeB); - auto& collisionState = oldManifold.state; + auto& collisionState = manifold.state; updateCollisionState(IS_COLLIDING, collisionState); const bool IS_INVALID = collisionState == SHCollisionState::INVALID; @@ -128,41 +164,45 @@ namespace SHADE } } - void SHContactManager::AddTrigger(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept + void SHContactManager::SolveCollisions(int numIterations, float dt) noexcept { - // If id not found, emplace new trigger. - auto trigger = triggers.find(key); - if (trigger == triggers.end()) - triggers.emplace(key, Trigger{ A, B, SHCollisionState::INVALID }).first; + static constexpr int SIZE_CONTACTS = sizeof(SHContact) * SHManifold::MAX_NUM_CONTACTS; + + // Build constraints + for (auto& [key, manifold] : manifolds) + contactSolver.AddContact(key, manifold); + + // Solve contacts + contactSolver.SolveContacts(numIterations, dt); + + // Map impulses back to manifolds + const auto& CONTACT_CONSTRAINTS = contactSolver.GetContantConstraints(); + const auto& VELOCITY_STATES = contactSolver.GetVelocities(); + + for (auto& [key, contactConstraint] : CONTACT_CONSTRAINTS) + { + SHManifold& manifold = manifolds.find(key)->second; + + for (uint32_t i = 0; i < contactConstraint.numContacts; ++i) + manifold.contacts[i] = contactConstraint.contacts[i]; + + // Assign velocities back to the bodies + SHRigidBody* bodyA = manifold.bodyA; + SHRigidBody* bodyB = manifold.bodyB; + + const auto& STATE_A = VELOCITY_STATES.find(key.GetEntityA())->second; + const auto& STATE_B = VELOCITY_STATES.find(key.GetEntityB())->second; + + bodyA->SetLinearVelocity(STATE_A.linearVelocity); + bodyB->SetLinearVelocity(STATE_B.linearVelocity); + + bodyA->SetAngularVelocity(STATE_A.angularVelocity); + bodyB->SetAngularVelocity(STATE_B.angularVelocity); + } + + contactSolver.Reset(); } - void SHContactManager::AddManifold(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept - { - // If id not found, emplace new manifold - auto manifold = manifolds.find(key); - if (manifold == manifolds.end()) - manifolds.emplace(key, SHManifold{ A, B }).first; - } - - void SHContactManager::RemoveInvalidatedTrigger(EntityID eid) noexcept - { - removeInvalidObject(triggers, eid); - } - - void SHContactManager::RemoveInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) noexcept - { - removeInvalidObject(triggers, eid, shapeIndex); - } - - void SHContactManager::RemoveInvalidatedManifold(EntityID eid) noexcept - { - removeInvalidObject(manifolds, eid); - } - - void SHContactManager::RemoveInvalidatedManifold(EntityID eid, uint32_t shapeIndex) noexcept - { - removeInvalidObject(manifolds, eid, shapeIndex); - } /*-----------------------------------------------------------------------------------*/ /* Private Member Functions Definitions */ @@ -191,8 +231,6 @@ namespace SHADE void SHContactManager::updateManifold(SHManifold& manifold, const SHManifold& oldManifold) noexcept { - manifold.state = oldManifold.state; - // Early out since exiting a collision does not require an update beyond updating the state if (manifold.state == SHCollisionState::EXIT) return; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h index 692dc2e8..c2433345 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h @@ -16,6 +16,7 @@ #include "Physics/Collision/Contacts/SHCollisionEvents.h" #include "Physics/Collision/Contacts/SHCollisionKey.h" #include "Physics/Collision/Contacts/SHManifold.h" +#include "SHContactSolver.h" namespace SHADE { @@ -64,14 +65,6 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Removes any invalidated contacts and triggers, then performs narrowphase collision - * detection on existing triggers and manifolds. - * @return - */ - void Update () noexcept; - void AddTrigger (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept; void AddManifold (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept; @@ -80,6 +73,20 @@ namespace SHADE void RemoveInvalidatedManifold (EntityID eid) noexcept; void RemoveInvalidatedManifold (EntityID eid, uint32_t shapeIndex) noexcept; + /** + * @brief + * Removes any invalidated contacts and triggers, then performs narrowphase collision + * detection on existing triggers and manifolds. + */ + void Update () noexcept; + + /** + * @brief + * Builds contact constraints and solves them. Results are stored in the corresponding + * manifolds abiding by the sequential impulse method. + */ + void SolveCollisions (int numIterations, float dt) noexcept; + private: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -103,6 +110,8 @@ namespace SHADE Manifolds manifolds; Triggers triggers; + SHContactSolver contactSolver; + /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp index 84805c57..c14c9eb5 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp @@ -36,40 +36,39 @@ namespace SHADE /* Public Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHContactSolver::AddContact(const SHManifold& manifold, const SHRigidBody* rigidBodyA, const SHRigidBody* rigidBodyB) noexcept + void SHContactSolver::AddContact(const SHCollisionKey& key, const SHManifold& manifold) noexcept { - SHContactConstraint& newConstraint = contactConstraints.emplace_back(SHContactConstraint{}); + SHContactConstraint& newConstraint = contactConstraints.emplace(key, SHContactConstraint{}).first->second; - const auto* SHAPE_A = manifold.A; - const auto* SHAPE_B = manifold.B; + const auto* SHAPE_A = manifold.shapeA; + const auto* SHAPE_B = manifold.shapeB; - newConstraint.idA = SHAPE_A->GetEntityID(); - newConstraint.idB = SHAPE_B->GetEntityID(); + const auto* BODY_A = manifold.bodyA; + const auto* BODY_B = manifold.bodyB; // Add velocities if it doesn't already exist - velocityStates.emplace(newConstraint.idA, VelocityState{ rigidBodyA->linearVelocity, rigidBodyB->angularVelocity }); - velocityStates.emplace(newConstraint.idB, VelocityState{ rigidBodyB->linearVelocity, rigidBodyB->angularVelocity }); + velocityStates.emplace(key.GetEntityA(), VelocityState{ BODY_A->linearVelocity, BODY_A->angularVelocity }); + velocityStates.emplace(key.GetEntityB(), VelocityState{ BODY_B->linearVelocity, BODY_B->angularVelocity }); // Mix friction & restitution - const float FRICTION_A = manifold.A->GetFriction(); - const float RESTITUTION_A = manifold.A->GetBounciness(); + const float FRICTION_A = SHAPE_A->GetFriction(); + const float RESTITUTION_A = SHAPE_A->GetBounciness(); - const float FRICTION_B = manifold.B->GetFriction(); - const float RESTITUTION_B = manifold.B->GetBounciness(); + const float FRICTION_B = SHAPE_B->GetFriction(); + const float RESTITUTION_B = SHAPE_B->GetBounciness(); - newConstraint.friction = std::sqrtf(FRICTION_A * FRICTION_B); - newConstraint.restitution = std::max(RESTITUTION_A, RESTITUTION_B); + newConstraint.friction = std::sqrtf(FRICTION_A * FRICTION_B); + newConstraint.restitution = std::max(RESTITUTION_A, RESTITUTION_B); // Mass data - newConstraint.invMassA = rigidBodyA->invMass; - newConstraint.invMassB = rigidBodyB->invMass; + newConstraint.invMassA = BODY_A->invMass; + newConstraint.invInertiaA = BODY_A->worldInvInertia; + newConstraint.centerOfMassA = BODY_A->worldCentroid; - newConstraint.invInertiaA = rigidBodyA->worldInvInertia; - newConstraint.invInertiaB = rigidBodyB->worldInvInertia; - - newConstraint.centerOfMassA = rigidBodyA->worldCentroid; - newConstraint.centerOfMassB = rigidBodyB->worldCentroid; + newConstraint.invMassB = BODY_B->invMass; + newConstraint.invInertiaB = BODY_B->worldInvInertia; + newConstraint.centerOfMassB = BODY_B->worldCentroid; // Collision data @@ -91,8 +90,9 @@ namespace SHADE } } - void SHContactSolver::ClearContacts() noexcept + void SHContactSolver::Reset() noexcept { + velocityStates.clear(); contactConstraints.clear(); } @@ -112,14 +112,14 @@ namespace SHADE { const float INV_DT = 1.0f / dt; - for (auto& constraint : contactConstraints) + for (auto& [key, constraint] : contactConstraints) { const float INV_MASS_SUM = constraint.invMassA + constraint.invMassB; - SHVec3 vA = velocityStates[constraint.idA].linearVelocity; - SHVec3 wA = velocityStates[constraint.idA].angularVelocity; - SHVec3 vB = velocityStates[constraint.idB].linearVelocity; - SHVec3 wB = velocityStates[constraint.idB].angularVelocity; + SHVec3 vA = velocityStates[key.GetEntityA()].linearVelocity; + SHVec3 wA = velocityStates[key.GetEntityA()].angularVelocity; + SHVec3 vB = velocityStates[key.GetEntityB()].linearVelocity; + SHVec3 wB = velocityStates[key.GetEntityB()].angularVelocity; for (uint32_t i = 0; i < constraint.numContacts; ++i) { @@ -156,7 +156,7 @@ namespace SHADE contact.normalMass += SHVec3::Dot(RB_CROSS_N, constraint.invInertiaB * RB_CROSS_N); // Invert the normal mass (we want the actual mass, not the inverse mass) - contact.normalMass = contact.normalMass == 0.0f ? 0.0f : 1.0f / contact.normalMass; + contact.normalMass = 1.0f / contact.normalMass; // Effective mass along tangents (same steps as above) @@ -169,7 +169,7 @@ namespace SHADE contact.tangentMass[j] += SHVec3::Dot(RA_CROSS_T, constraint.invInertiaA * RA_CROSS_T); contact.tangentMass[j] += SHVec3::Dot(RB_CROSS_T, constraint.invInertiaB * RB_CROSS_T); - contact.tangentMass[j] = contact.tangentMass[j] == 0.0f ? 0.0f : 1.0f / contact.tangentMass[j]; + contact.tangentMass[j] = 1.0f / contact.tangentMass[j]; } // Warm starting @@ -195,28 +195,27 @@ namespace SHADE const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB); const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal); - const float ERROR_BIAS = BAUMGARTE_FACTOR * INV_DT * contact.penetration; + const float ERROR_BIAS = BAUMGARTE_FACTOR * INV_DT * std::min(0.0f, -contact.penetration + PENETRATION_SLOP); const float RESTITUTION_BIAS = -constraint.restitution * RV_N; contact.bias = ERROR_BIAS + RESTITUTION_BIAS; } - velocityStates[constraint.idA].linearVelocity = vA; - velocityStates[constraint.idA].angularVelocity = wA; - velocityStates[constraint.idB].linearVelocity = vB; - velocityStates[constraint.idB].angularVelocity = wB; - + velocityStates[key.GetEntityA()].linearVelocity = vA; + velocityStates[key.GetEntityA()].angularVelocity = wA; + velocityStates[key.GetEntityB()].linearVelocity = vB; + velocityStates[key.GetEntityB()].angularVelocity = wB; } } void SHContactSolver::solve() noexcept { - for (auto& constraint : contactConstraints) + for (auto& [key, constraint] : contactConstraints) { - SHVec3 vA = velocityStates[constraint.idA].linearVelocity; - SHVec3 wA = velocityStates[constraint.idA].angularVelocity; - SHVec3 vB = velocityStates[constraint.idB].linearVelocity; - SHVec3 wB = velocityStates[constraint.idB].angularVelocity; + SHVec3 vA = velocityStates[key.GetEntityA()].linearVelocity; + SHVec3 wA = velocityStates[key.GetEntityA()].angularVelocity; + SHVec3 vB = velocityStates[key.GetEntityB()].linearVelocity; + SHVec3 wB = velocityStates[key.GetEntityB()].angularVelocity; for (uint32_t i = 0; i < constraint.numContacts; ++i) { @@ -279,10 +278,10 @@ namespace SHADE wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE); } - velocityStates[constraint.idA].linearVelocity = vA; - velocityStates[constraint.idA].angularVelocity = wA; - velocityStates[constraint.idB].linearVelocity = vB; - velocityStates[constraint.idB].angularVelocity = wB; + velocityStates[key.GetEntityA()].linearVelocity = vA; + velocityStates[key.GetEntityA()].angularVelocity = wA; + velocityStates[key.GetEntityB()].linearVelocity = vB; + velocityStates[key.GetEntityB()].angularVelocity = wB; } } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h index 28d5de6f..7a4f2d47 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h @@ -13,7 +13,6 @@ // Project Headers #include "Constraints/SHContactConstraint.h" -#include "SHContactManager.h" namespace SHADE { @@ -41,7 +40,7 @@ namespace SHADE }; using VelocityStates = std::unordered_map; - using ContactConstraints = std::vector; + using ContactConstraints = std::unordered_map; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -66,14 +65,10 @@ namespace SHADE * Build a contact constraint from a new manifold. * @param manifold * A manifold to build a contact constraint from. - * @param rigidBodyA - * The rigid body belonging to the first collision shape. - * @param rigidBodyB - * The rigid body belonging to the second collision shape. */ - void AddContact (const SHManifold& manifold, const SHRigidBody* rigidBodyA, const SHRigidBody* rigidBodyB) noexcept; + void AddContact (const SHCollisionKey& key, const SHManifold& manifold) noexcept; - void ClearContacts () noexcept; + void Reset () noexcept; /** * @brief diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index cb453d24..dfa3f482 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -103,32 +103,11 @@ namespace SHADE /* + * TODO: A lot of this needs to be cleaned up. * Resolve Contacts */ - // Build constraints - for (auto& [id, manifold] : contactManager.manifolds) - { - SHRigidBody* bodyA = rigidBodies[id.GetEntityA()]; - SHRigidBody* bodyB = rigidBodies[id.GetEntityB()]; - - contactSolver.AddContact(manifold, bodyA, bodyB); - } - - // Solve contacts - contactSolver.SolveContacts(settings.numVelocitySolverIterations, dt); - - // Map velocities back to bodies - const auto& VELOCITY_STATES = contactSolver.GetVelocities(); - for (auto& [id, velocityState] : VELOCITY_STATES) - { - SHRigidBody* body = rigidBodies[id]; - body->linearVelocity = velocityState.linearVelocity; - body->angularVelocity = velocityState.angularVelocity; - } - - // Clear contacts - contactSolver.ClearContacts(); + contactManager.SolveCollisions(settings.numVelocitySolverIterations, dt); /* * Integrate Velocities From b667e4df872316bebb69afb1a117f43e8f5db47b Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 22 Dec 2022 03:11:14 +0800 Subject: [PATCH 038/164] Implemented axis locking constraints --- Assets/Scenes/PhysicsSandbox.shade | 162 +++++++++++------- .../Dynamics/Constraints/SHVelocityState.h | 56 ++++++ .../src/Physics/Dynamics/SHContactManager.cpp | 12 +- .../src/Physics/Dynamics/SHContactSolver.cpp | 94 +++++----- .../src/Physics/Dynamics/SHContactSolver.h | 11 +- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 25 +-- .../src/Physics/Dynamics/SHRigidBody.cpp | 19 +- .../src/Physics/Dynamics/SHRigidBody.h | 5 +- 8 files changed, 249 insertions(+), 135 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 90cba8d0..1438b558 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,7 +4,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0.186280191, y: 4.3224473, z: 0} + Translate: {x: 0, y: 3, z: 0} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -19,6 +19,107 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false + Freeze Position Y: true + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: true + Colliders: + - Is Trigger: false + Type: Sphere + Radius: 1 + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true + Scripts: + - Type: PhysicsTestObj + Enabled: true + forceAmount: 50 + torqueAmount: 500 +- EID: 1 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Camera Component: + Position: {x: 0, y: 0.5, z: 5} + Pitch: 0 + Yaw: 0 + Roll: 0 + Width: 1920 + Height: 1080 + Near: 0.00999999978 + Far: 10000 + Perspective: true + IsActive: true + Scripts: ~ +- EID: 2 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: -2.5, z: 0} + Rotate: {x: 0, y: 0, z: 0} + Scale: {x: 1, y: 1, z: 1} + IsActive: true + RigidBody Component: + Type: Static + Auto Mass: false + Mass: .inf + Drag: 0.00999999978 + Angular Drag: 0.00999999978 + Use Gravity: false + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false + Freeze Position Y: false + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: true + Colliders: + - Is Trigger: false + Type: Sphere + Radius: 5 + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true + Scripts: ~ +- EID: 3 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: 5, z: 0} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 1, y: 1, z: 1} + IsActive: true + RigidBody Component: + Type: Dynamic + Auto Mass: false + Mass: 1 + Drag: 1 + Angular Drag: 1 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false Freeze Position Y: false Freeze Position Z: false Freeze Rotation X: false @@ -41,61 +142,4 @@ - Type: PhysicsTestObj Enabled: true forceAmount: 50 - torqueAmount: 500 -- EID: 1 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Camera Component: - Position: {x: 0, y: 0.5, z: 3} - Pitch: 0 - Yaw: 0 - Roll: 0 - Width: 1920 - Height: 1080 - Near: 0.00999999978 - Far: 10000 - Perspective: true - IsActive: true - Scripts: ~ -- EID: 2 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 0, y: 1, z: 0} - Rotate: {x: 0, y: 0, z: 0} - Scale: {x: 1, y: 1, z: 1} - IsActive: true - RigidBody Component: - Type: Static - Auto Mass: false - Mass: .inf - Drag: 0.00999999978 - Angular Drag: 0.00999999978 - Use Gravity: false - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: false - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true - Collider Component: - DrawColliders: true - Colliders: - - Is Trigger: false - Type: Sphere - Radius: 1 - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ \ No newline at end of file + torqueAmount: 500 \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h new file mode 100644 index 00000000..6cbb6efb --- /dev/null +++ b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h @@ -0,0 +1,56 @@ +/**************************************************************************************** + * \file SHVelocityState.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Velocity State for constraint solving. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Physics/Dynamics/SHRigidBody.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + struct SH_API SHVelocityState + { + public: + SHVec3 LinearVelocity; + SHVec3 AngularVelocity; + + SHVec3 LinearLockFactor; + SHVec3 AngularLockFactor; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHVelocityState (const SHRigidBody* rigidBody) noexcept + { + LinearVelocity = rigidBody->GetLinearVelocity(); + AngularVelocity = rigidBody->GetAngularVelocity(); + + LinearLockFactor = SHVec3 + { + rigidBody->GetFreezePositionX() ? 0.0f : 1.0f + , rigidBody->GetFreezePositionY() ? 0.0f : 1.0f + , rigidBody->GetFreezePositionZ() ? 0.0f : 1.0f + }; + + AngularLockFactor = SHVec3 + { + rigidBody->GetFreezeRotationX() ? 0.0f : 1.0f + , rigidBody->GetFreezeRotationY() ? 0.0f : 1.0f + , rigidBody->GetFreezeRotationZ() ? 0.0f : 1.0f + }; + } + }; + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index 3b80b3b3..83047635 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -190,14 +190,14 @@ namespace SHADE SHRigidBody* bodyA = manifold.bodyA; SHRigidBody* bodyB = manifold.bodyB; - const auto& STATE_A = VELOCITY_STATES.find(key.GetEntityA())->second; - const auto& STATE_B = VELOCITY_STATES.find(key.GetEntityB())->second; + const auto& STATE_A = VELOCITY_STATES.find(key.GetEntityA())->second; + const auto& STATE_B = VELOCITY_STATES.find(key.GetEntityB())->second; - bodyA->SetLinearVelocity(STATE_A.linearVelocity); - bodyB->SetLinearVelocity(STATE_B.linearVelocity); + bodyA->SetLinearVelocity(STATE_A.LinearVelocity); + bodyB->SetLinearVelocity(STATE_B.LinearVelocity); - bodyA->SetAngularVelocity(STATE_A.angularVelocity); - bodyB->SetAngularVelocity(STATE_B.angularVelocity); + bodyA->SetAngularVelocity(STATE_A.AngularVelocity); + bodyB->SetAngularVelocity(STATE_B.AngularVelocity); } contactSolver.Reset(); diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp index c14c9eb5..ed4c9aa8 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp @@ -47,8 +47,8 @@ namespace SHADE const auto* BODY_B = manifold.bodyB; // Add velocities if it doesn't already exist - velocityStates.emplace(key.GetEntityA(), VelocityState{ BODY_A->linearVelocity, BODY_A->angularVelocity }); - velocityStates.emplace(key.GetEntityB(), VelocityState{ BODY_B->linearVelocity, BODY_B->angularVelocity }); + velocityStates.emplace(key.GetEntityA(), SHVelocityState{ BODY_A }); + velocityStates.emplace(key.GetEntityB(), SHVelocityState{ BODY_B }); // Mix friction & restitution const float FRICTION_A = SHAPE_A->GetFriction(); @@ -116,10 +116,18 @@ namespace SHADE { const float INV_MASS_SUM = constraint.invMassA + constraint.invMassB; - SHVec3 vA = velocityStates[key.GetEntityA()].linearVelocity; - SHVec3 wA = velocityStates[key.GetEntityA()].angularVelocity; - SHVec3 vB = velocityStates[key.GetEntityB()].linearVelocity; - SHVec3 wB = velocityStates[key.GetEntityB()].angularVelocity; + SHVelocityState& velocityStateA = velocityStates.find(key.GetEntityA())->second; + SHVelocityState& velocityStateB = velocityStates.find(key.GetEntityB())->second; + + SHVec3 vA = velocityStateA.LinearVelocity; + SHVec3 wA = velocityStateA.AngularVelocity; + SHVec3 vB = velocityStateB.LinearVelocity; + SHVec3 wB = velocityStateB.AngularVelocity; + + const SHVec3& LINEAR_LOCK_A = velocityStateA.LinearLockFactor; + const SHVec3& ANGULAR_LOCK_A = velocityStateA.AngularLockFactor; + const SHVec3& LINEAR_LOCK_B = velocityStateB.LinearLockFactor; + const SHVec3& ANGULAR_LOCK_B = velocityStateB.AngularLockFactor; for (uint32_t i = 0; i < constraint.numContacts; ++i) { @@ -172,19 +180,6 @@ namespace SHADE contact.tangentMass[j] = 1.0f / contact.tangentMass[j]; } - // Warm starting - // Compute impulses - SHVec3 impulse = constraint.normal * contact.normalImpulse; - for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) - impulse += constraint.tangents[j] * contact.tangentImpulse[j]; - - // Apply impulses onto velocities - vA -= impulse * constraint.invMassA; - wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, impulse); - - vB += impulse * constraint.invMassB; - wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, impulse); - // Calculate bias per contact /* * error bias = baumgarte factor / dt * penetration @@ -199,12 +194,25 @@ namespace SHADE const float RESTITUTION_BIAS = -constraint.restitution * RV_N; contact.bias = ERROR_BIAS + RESTITUTION_BIAS; + + // Warm starting + // Compute impulses + SHVec3 impulse = constraint.normal * contact.normalImpulse; + for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) + impulse += constraint.tangents[j] * contact.tangentImpulse[j]; + + // Apply impulses onto velocities + vA -= impulse * constraint.invMassA * LINEAR_LOCK_A; + wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, impulse) * ANGULAR_LOCK_A; + + vB += impulse * constraint.invMassB * LINEAR_LOCK_B; + wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, impulse) * ANGULAR_LOCK_B; } - velocityStates[key.GetEntityA()].linearVelocity = vA; - velocityStates[key.GetEntityA()].angularVelocity = wA; - velocityStates[key.GetEntityB()].linearVelocity = vB; - velocityStates[key.GetEntityB()].angularVelocity = wB; + velocityStateA.LinearVelocity = vA; + velocityStateA.AngularVelocity = wA; + velocityStateB.LinearVelocity = vB; + velocityStateB.AngularVelocity = wB; } } @@ -212,10 +220,18 @@ namespace SHADE { for (auto& [key, constraint] : contactConstraints) { - SHVec3 vA = velocityStates[key.GetEntityA()].linearVelocity; - SHVec3 wA = velocityStates[key.GetEntityA()].angularVelocity; - SHVec3 vB = velocityStates[key.GetEntityB()].linearVelocity; - SHVec3 wB = velocityStates[key.GetEntityB()].angularVelocity; + SHVelocityState& velocityStateA = velocityStates.find(key.GetEntityA())->second; + SHVelocityState& velocityStateB = velocityStates.find(key.GetEntityB())->second; + + SHVec3 vA = velocityStateA.LinearVelocity; + SHVec3 wA = velocityStateA.AngularVelocity; + SHVec3 vB = velocityStateB.LinearVelocity; + SHVec3 wB = velocityStateB.AngularVelocity; + + const SHVec3& LINEAR_LOCK_A = velocityStateA.LinearLockFactor; + const SHVec3& ANGULAR_LOCK_A = velocityStateA.AngularLockFactor; + const SHVec3& LINEAR_LOCK_B = velocityStateB.LinearLockFactor; + const SHVec3& ANGULAR_LOCK_B = velocityStateB.AngularLockFactor; for (uint32_t i = 0; i < constraint.numContacts; ++i) { @@ -245,11 +261,11 @@ namespace SHADE const SHVec3 TANGENT_IMPULSE = newTangentImpulse * constraint.tangents[j]; // Apply impulses - vA -= TANGENT_IMPULSE * constraint.invMassA; - wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, TANGENT_IMPULSE); + vA -= TANGENT_IMPULSE * constraint.invMassA * LINEAR_LOCK_A; + wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, TANGENT_IMPULSE) * ANGULAR_LOCK_A; - vB += TANGENT_IMPULSE * constraint.invMassB; - wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, TANGENT_IMPULSE); + vB += TANGENT_IMPULSE * constraint.invMassB * LINEAR_LOCK_B; + wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, TANGENT_IMPULSE) * ANGULAR_LOCK_B; } // Solve normal impulse @@ -271,17 +287,17 @@ namespace SHADE const SHVec3 NORMAL_IMPULSE = newNormalImpulse * constraint.normal; // Apply impulses - vA -= NORMAL_IMPULSE * constraint.invMassA; - wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, NORMAL_IMPULSE); + vA -= NORMAL_IMPULSE * constraint.invMassA * LINEAR_LOCK_A; + wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, NORMAL_IMPULSE) * ANGULAR_LOCK_A; - vB += NORMAL_IMPULSE * constraint.invMassB; - wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE); + vB += NORMAL_IMPULSE * constraint.invMassB * LINEAR_LOCK_B; + wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE) * ANGULAR_LOCK_B; } - velocityStates[key.GetEntityA()].linearVelocity = vA; - velocityStates[key.GetEntityA()].angularVelocity = wA; - velocityStates[key.GetEntityB()].linearVelocity = vB; - velocityStates[key.GetEntityB()].angularVelocity = wB; + velocityStateA.LinearVelocity = vA; + velocityStateA.AngularVelocity = wA; + velocityStateB.LinearVelocity = vB; + velocityStateB.AngularVelocity = wB; } } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h index 7a4f2d47..56955f74 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h @@ -13,6 +13,7 @@ // Project Headers #include "Constraints/SHContactConstraint.h" +#include "Constraints/SHVelocityState.h" namespace SHADE { @@ -31,15 +32,7 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - struct VelocityState - { - // Velocities - - SHVec3 linearVelocity; - SHVec3 angularVelocity; - }; - - using VelocityStates = std::unordered_map; + using VelocityStates = std::unordered_map; using ContactConstraints = std::unordered_map; /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index dfa3f482..8451bd5b 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -143,29 +143,13 @@ namespace SHADE // Apply drag (exponentially applied) rigidBody.linearVelocity *= 1.0f / (1.0f + dt * rigidBody.linearDrag); rigidBody.angularVelocity *= 1.0f / (1.0f + dt * rigidBody.angularDrag); + + rigidBody.constrainLinearVelocities(); + rigidBody.constrainAngularVelocities(); } void SHPhysicsWorld::integrateVelocities(SHRigidBody& rigidBody, float dt) const noexcept { - static const auto ENFORCE_CONSTRAINED_VELOCITIES = [](SHRigidBody& rigidBody) - { - // Enforce linear constraints - rigidBody.linearVelocity = SHVec3 - { - rigidBody.GetFreezePositionX() ? 0.0f : rigidBody.linearVelocity.x - , rigidBody.GetFreezePositionY() ? 0.0f : rigidBody.linearVelocity.y - , rigidBody.GetFreezePositionZ() ? 0.0f : rigidBody.linearVelocity.z - }; - - // Enforce angular constraints - rigidBody.angularVelocity = SHVec3 - { - rigidBody.GetFreezeRotationX() ? 0.0f : rigidBody.angularVelocity.x - , rigidBody.GetFreezeRotationY() ? 0.0f : rigidBody.angularVelocity.y - , rigidBody.GetFreezeRotationZ() ? 0.0f : rigidBody.angularVelocity.z - }; - }; - // Always reset movement flag rigidBody.motionState.hasMoved = false; @@ -178,7 +162,8 @@ namespace SHADE // Both dynamic and kinematic can sleep when their velocities are under the thresholds. else if (!rigidBody.IsSleeping()) { - ENFORCE_CONSTRAINED_VELOCITIES(rigidBody); + rigidBody.constrainLinearVelocities(); + rigidBody.constrainAngularVelocities(); rigidBody.motionState.IntegratePosition(rigidBody.linearVelocity, dt); rigidBody.motionState.IntegrateOrientation(rigidBody.angularVelocity, dt); diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index b724321b..3e24fa27 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -200,6 +200,8 @@ namespace SHADE const SHVec3& SHRigidBody::GetLinearVelocity() const noexcept { + // Check if linear velocity needs to be constrained + return linearVelocity; } @@ -378,6 +380,7 @@ namespace SHADE } linearVelocity = newLinearVelocity; + constrainLinearVelocities(); } void SHRigidBody::SetAngularVelocity(const SHVec3& newAngularVelocity) noexcept @@ -389,6 +392,7 @@ namespace SHADE } angularVelocity = newAngularVelocity; + constrainAngularVelocities(); } void SHRigidBody::SetIsActive(bool isActive) noexcept @@ -676,7 +680,20 @@ namespace SHADE const auto* FIRST_SHAPE = collider->GetCollisionShape(0); localInvInertia = SHMatrix::Inverse(FIRST_SHAPE->GetInertiaTensor(1.0f / invMass)); } - + } + + void SHRigidBody::constrainLinearVelocities() noexcept + { + linearVelocity.x = GetFreezePositionX() ? 0.0f : linearVelocity.x; + linearVelocity.y = GetFreezePositionY() ? 0.0f : linearVelocity.y; + linearVelocity.z = GetFreezePositionZ() ? 0.0f : linearVelocity.z; + } + + void SHRigidBody::constrainAngularVelocities() noexcept + { + angularVelocity.x = GetFreezeRotationX() ? 0.0f : angularVelocity.x; + angularVelocity.y = GetFreezeRotationY() ? 0.0f : angularVelocity.y; + angularVelocity.z = GetFreezeRotationZ() ? 0.0f : angularVelocity.z; } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index 606895ee..34424a1c 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -249,7 +249,7 @@ namespace SHADE SHVec3 linearVelocity; SHVec3 angularVelocity; - // aZ aY aX pZ pY pX 0 0 0 0 inIsland autoMass enableGravity enableSleeping sleeping active + // aZ aY aX rotLockActive pZ pY pX posLockActive 0 0 inIsland autoMass enableGravity enableSleeping sleeping active uint16_t flags; SHMotionState motionState; @@ -262,6 +262,9 @@ namespace SHADE void computeMass () noexcept; void computeInertiaTensor () noexcept; void computeMassAndInertiaTensor() noexcept; + + void constrainLinearVelocities () noexcept; + void constrainAngularVelocities () noexcept; }; From 22c0a14081e2109de18a977382dd4412cbc38b4e Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 23 Dec 2022 00:55:36 +0800 Subject: [PATCH 039/164] Renamed SHBox to SHAABB for clarity The future SHBox will represent an OBB --- .../src/Camera/SHCameraArmComponent.h | 2 +- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 2 +- .../Inspector/SHEditorComponentView.hpp | 2 +- .../Math/Geometry/{SHBox.cpp => SHAABB.cpp} | 74 +++++------- .../src/Math/Geometry/{SHBox.h => SHAABB.h} | 114 ++++++++++++++---- SHADE_Engine/src/Math/Geometry/SHSphere.h | 16 +-- .../Broadphase/SHDynamicAABBTree.cpp | 38 +++--- .../Collision/Broadphase/SHDynamicAABBTree.h | 10 +- .../CollisionShapes/SHCollisionShape.h | 4 +- .../SHSphereCollisionShape.cpp | 4 +- .../CollisionShapes/SHSphereCollisionShape.h | 2 +- .../src/Serialization/SHYAMLConverters.h | 5 +- SHADE_Managed/src/Components/Collider.cxx | 20 +-- 13 files changed, 171 insertions(+), 122 deletions(-) rename SHADE_Engine/src/Math/Geometry/{SHBox.cpp => SHAABB.cpp} (71%) rename SHADE_Engine/src/Math/Geometry/{SHBox.h => SHAABB.h} (55%) diff --git a/SHADE_Engine/src/Camera/SHCameraArmComponent.h b/SHADE_Engine/src/Camera/SHCameraArmComponent.h index 9d8ec853..dfae1cd5 100644 --- a/SHADE_Engine/src/Camera/SHCameraArmComponent.h +++ b/SHADE_Engine/src/Camera/SHCameraArmComponent.h @@ -10,7 +10,7 @@ namespace SHADE { - class SHBox; + class SHAABB; class SHRay; class SH_API SHCameraArmComponent final: public SHComponent diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index d94bd3f8..af7d0f98 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -10,7 +10,7 @@ #include "Scene/SHSceneManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.h" -#include "Math/Geometry/SHBox.h" +#include "Math/Geometry/SHAABB.h" #include "Math/SHRay.h" #include "Physics/System/SHPhysicsSystem.h" diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 2bbc0305..622a2d37 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -346,7 +346,7 @@ namespace SHADE { //SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); // - //const auto* BOX = reinterpret_cast(collider->GetShape()); + //const auto* BOX = reinterpret_cast(collider->GetShape()); //SHEditorWidgets::DragVec3 //( // "Half Extents", { "X", "Y", "Z" }, diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.cpp b/SHADE_Engine/src/Math/Geometry/SHAABB.cpp similarity index 71% rename from SHADE_Engine/src/Math/Geometry/SHBox.cpp rename to SHADE_Engine/src/Math/Geometry/SHAABB.cpp index 05595fe1..8a44457f 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBox.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHAABB.cpp @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHBox.h" +#include "SHAABB.h" // Project Headers #include "Math/SHMathHelpers.h" #include "Math/SHRay.h" @@ -24,14 +24,12 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHBox::SHBox() noexcept - : RelativeExtents { SHVec3::One } + SHAABB::SHAABB() noexcept { type = Type::BOX; } - SHBox::SHBox(const SHVec3& c, const SHVec3& hE) noexcept - : RelativeExtents { SHVec3::One } + SHAABB::SHAABB(const SHVec3& c, const SHVec3& hE) noexcept { type = Type::BOX; @@ -40,7 +38,7 @@ namespace SHADE } - SHBox::SHBox(const SHBox& rhs) noexcept + SHAABB::SHAABB(const SHAABB& rhs) noexcept { if (this == &rhs) return; @@ -49,23 +47,21 @@ namespace SHADE Center = rhs.Center; Extents = rhs.Extents; - RelativeExtents = rhs.RelativeExtents; } - SHBox::SHBox(SHBox&& rhs) noexcept + SHAABB::SHAABB(SHAABB&& rhs) noexcept { type = Type::BOX; Center = rhs.Center; Extents = rhs.Extents; - RelativeExtents = rhs.RelativeExtents; } /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHBox& SHBox::operator=(const SHBox& rhs) noexcept + SHAABB& SHAABB::operator=(const SHAABB& rhs) noexcept { if (rhs.type != Type::BOX) { @@ -75,13 +71,12 @@ namespace SHADE { Center = rhs.Center; Extents = rhs.Extents; - RelativeExtents = rhs.RelativeExtents; } return *this; } - SHBox& SHBox::operator=(SHBox&& rhs) noexcept + SHAABB& SHAABB::operator=(SHAABB&& rhs) noexcept { if (rhs.type != Type::BOX) { @@ -91,7 +86,6 @@ namespace SHADE { Center = rhs.Center; Extents = rhs.Extents; - RelativeExtents = rhs.RelativeExtents; } return *this; @@ -101,27 +95,22 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHVec3 SHBox::GetCenter() const noexcept + SHVec3 SHAABB::GetCenter() const noexcept { return Center; } - SHVec3 SHBox::GetWorldExtents() const noexcept + SHVec3 SHAABB::GetWorldExtents() const noexcept { return Extents; } - const SHVec3& SHBox::GetRelativeExtents() const noexcept - { - return RelativeExtents; - } - - SHVec3 SHBox::GetMin() const noexcept + SHVec3 SHAABB::GetMin() const noexcept { return SHVec3{ Center.x - Extents.x, Center.y - Extents.y, Center.z - Extents.z }; } - SHVec3 SHBox::GetMax() const noexcept + SHVec3 SHAABB::GetMax() const noexcept { return SHVec3{ Center.x + Extents.x, Center.y + Extents.y, Center.z + Extents.z }; } @@ -130,22 +119,17 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHBox::SetCenter(const SHVec3& newCenter) noexcept + void SHAABB::SetCenter(const SHVec3& newCenter) noexcept { Center = newCenter; } - void SHBox::SetWorldExtents(const SHVec3& newWorldExtents) noexcept + void SHAABB::SetWorldExtents(const SHVec3& newWorldExtents) noexcept { Extents = newWorldExtents; } - void SHBox::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept - { - RelativeExtents = newRelativeExtents; - } - - void SHBox::SetMin(const SHVec3& min) noexcept + void SHAABB::SetMin(const SHVec3& min) noexcept { const SHVec3 MAX = GetMax(); @@ -153,7 +137,7 @@ namespace SHADE Extents = SHVec3::Abs((MAX - min) * 0.5f); } - void SHBox::SetMax(const SHVec3& max) noexcept + void SHAABB::SetMax(const SHVec3& max) noexcept { const SHVec3 MIN = GetMin(); @@ -161,13 +145,13 @@ namespace SHADE Extents = SHVec3::Abs((max - MIN) * 0.5f); } - void SHBox::SetMinMax(const SHVec3& min, const SHVec3& max) noexcept + void SHAABB::SetMinMax(const SHVec3& min, const SHVec3& max) noexcept { Center = SHVec3::Lerp(min, max, 0.5f); Extents = SHVec3::Abs((max - min) * 0.5f); } - std::vector SHBox::GetVertices() const noexcept + std::vector SHAABB::GetVertices() const noexcept { std::vector vertices{ 8 }; GetCorners(vertices.data()); @@ -178,12 +162,12 @@ namespace SHADE /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - bool SHBox::TestPoint(const SHVec3& point) const noexcept + bool SHAABB::TestPoint(const SHVec3& point) const noexcept { return BoundingBox::Contains(point); } - SHRaycastResult SHBox::Raycast(const SHRay& ray) const noexcept + SHRaycastResult SHAABB::Raycast(const SHRay& ray) const noexcept { SHRaycastResult result; @@ -197,17 +181,17 @@ namespace SHADE return result; } - bool SHBox::Contains(const SHBox& rhs) const noexcept + bool SHAABB::Contains(const SHAABB& rhs) const noexcept { return BoundingBox::Contains(rhs) == CONTAINS; } - float SHBox::Volume() const noexcept + float SHAABB::Volume() const noexcept { return 8.0f * (Extents.x * Extents.y * Extents.z); } - float SHBox::SurfaceArea() const noexcept + float SHAABB::SurfaceArea() const noexcept { return 8.0f * ((Extents.x * Extents.y) + (Extents.x * Extents.z) @@ -218,21 +202,21 @@ namespace SHADE /* Static Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHBox SHBox::Combine(const SHBox& lhs, const SHBox& rhs) noexcept + SHAABB SHAABB::Combine(const SHAABB& lhs, const SHAABB& rhs) noexcept { - SHBox result; + SHAABB result; CreateMerged(result, lhs, rhs); return result; } - bool SHBox::Intersect(const SHBox& lhs, const SHBox& rhs) noexcept + bool SHAABB::Intersect(const SHAABB& lhs, const SHAABB& rhs) noexcept { return lhs.Intersects(rhs); } - SHBox SHBox::BuildFromBoxes(const SHBox* boxes, size_t numBoxes) noexcept + SHAABB SHAABB::BuildFromBoxes(const SHAABB* boxes, size_t numBoxes) noexcept { - SHBox result; + SHAABB result; for (size_t i = 1; i < numBoxes; ++i) CreateMerged(result, boxes[i - 1], boxes[i]); @@ -240,9 +224,9 @@ namespace SHADE return result; } - SHBox SHBox::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept + SHAABB SHAABB::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept { - SHBox result; + SHAABB result; CreateFromPoints(result, numVertices, vertices, stride); return result; } diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.h b/SHADE_Engine/src/Math/Geometry/SHAABB.h similarity index 55% rename from SHADE_Engine/src/Math/Geometry/SHBox.h rename to SHADE_Engine/src/Math/Geometry/SHAABB.h index 63383c00..46608ded 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBox.h +++ b/SHADE_Engine/src/Math/Geometry/SHAABB.h @@ -22,7 +22,7 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - class SH_API SHBox : public SHShape, + class SH_API SHAABB : public SHShape, private DirectX::BoundingBox { public: @@ -36,19 +36,19 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - ~SHBox () override = default; + ~SHAABB () override = default; - SHBox () noexcept; - SHBox (const SHVec3& center, const SHVec3& halfExtents) noexcept; - SHBox (const SHBox& rhs) noexcept; - SHBox (SHBox&& rhs) noexcept; + SHAABB () noexcept; + SHAABB (const SHVec3& center, const SHVec3& halfExtents) noexcept; + SHAABB (const SHAABB& rhs) noexcept; + SHAABB (SHAABB&& rhs) noexcept; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHBox& operator= (const SHBox& rhs) noexcept; - SHBox& operator= (SHBox&& rhs) noexcept; + SHAABB& operator= (const SHAABB& rhs) noexcept; + SHAABB& operator= (SHAABB&& rhs) noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ @@ -56,7 +56,6 @@ namespace SHADE [[nodiscard]] SHVec3 GetCenter () const noexcept; [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; - [[nodiscard]] const SHVec3& GetRelativeExtents () const noexcept; [[nodiscard]] SHVec3 GetMin () const noexcept; [[nodiscard]] SHVec3 GetMax () const noexcept; [[nodiscard]] std::vector GetVertices () const noexcept; @@ -67,7 +66,6 @@ namespace SHADE void SetCenter (const SHVec3& newCenter) noexcept; void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; - void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; void SetMin (const SHVec3& min) noexcept; void SetMax (const SHVec3& max) noexcept; void SetMinMax (const SHVec3& min, const SHVec3& max) noexcept; @@ -76,28 +74,96 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + /** + * @brief + * Checks if a point is inside the aabb. + * @param point + * The point to check. + * @return + * True if the point is inside the aabb. + */ + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - [[nodiscard]] bool Contains (const SHBox& rhs) const noexcept; - [[nodiscard]] float Volume () const noexcept; - [[nodiscard]] float SurfaceArea () const noexcept; + /** + * @brief + * Casts a ray against the aabb. + * @param ray + * The ray to cast. + * @return + * The result of the raycast.
+ * See the corresponding header for the contents of the raycast result object. + */ + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + + /** + * @brief + * Checks if an entire other aabb is contained by this aabb. + * @param rhs + * The aabb to check. + * @return + * True if the other sphere is completely contained by this aabb. + */ + [[nodiscard]] bool Contains (const SHAABB& rhs) const noexcept; + + /** + * @brief + * Calculates the volume of the aabb. + */ + [[nodiscard]] float Volume () const noexcept; + + /** + * @brief + * Calculates the surface area of the aabb. + */ + [[nodiscard]] float SurfaceArea () const noexcept; /*---------------------------------------------------------------------------------*/ /* Static Function Members */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static SHBox Combine (const SHBox& lhs, const SHBox& rhs) noexcept; - [[nodiscard]] static bool Intersect (const SHBox& lhs, const SHBox& rhs) noexcept; - [[nodiscard]] static SHBox BuildFromBoxes (const SHBox* boxes, size_t numBoxes) noexcept; - [[nodiscard]] static SHBox BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; + /** + * @brief + * Combines two aabbs to form a larger aabb. + * If one aabb is completely contained by the other, the result is the larger aabb. + * @return + * The combined aabb. + */ + [[nodiscard]] static SHAABB Combine (const SHAABB& lhs, const SHAABB& rhs) noexcept; - private: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ + /** + * @brief + * Checks if two aabbs are intersecting. + * @return + * True if they are intersecting. + */ + [[nodiscard]] static bool Intersect (const SHAABB& lhs, const SHAABB& rhs) noexcept; - SHVec3 RelativeExtents; + /** + * @brief + * Builds a single aabb from multiple aabbs. + * @param spheres + * The set of aabbs to build from. + * @param numSpheres + * The number of aabbs in the set to build from. + * @return + * An aabb that contains all the spheres in the set. + */ + [[nodiscard]] static SHAABB BuildFromBoxes (const SHAABB* boxes, size_t numBoxes) noexcept; + + /** + * @brief + * Builds a aabb from a set of vertices. + * @param vertices + * The vertices to build a aabb from. + * @param numVertices + * The number of vertices in the set to build from. + * @param stride + * The stride between each vertex, in the instance there is data in between each + * vertex that does not define the geometry of the object. + * @return + * An aabb that contains all the vertices in the set. + */ + [[nodiscard]] static SHAABB BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; }; diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.h b/SHADE_Engine/src/Math/Geometry/SHSphere.h index 91771a21..80a21aa4 100644 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.h +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.h @@ -56,7 +56,7 @@ namespace SHADE * @return * True if the point is inside the sphere. */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; /** * @brief @@ -67,7 +67,7 @@ namespace SHADE * The result of the raycast.
* See the corresponding header for the contents of the raycast result object. */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; /** * @brief @@ -77,19 +77,19 @@ namespace SHADE * @return * True if the other sphere is completely contained by this sphere. */ - [[nodiscard]] bool Contains (const SHSphere& rhs) const noexcept; + [[nodiscard]] bool Contains (const SHSphere& rhs) const noexcept; /** * @brief * Calculates the volume of the sphere. */ - [[nodiscard]] float Volume () const noexcept; + [[nodiscard]] float Volume () const noexcept; /** * @brief * Calculates the surface area of the sphere. */ - [[nodiscard]] float SurfaceArea () const noexcept; + [[nodiscard]] float SurfaceArea () const noexcept; /*---------------------------------------------------------------------------------*/ @@ -103,7 +103,7 @@ namespace SHADE * @return * The combined sphere. */ - [[nodiscard]] static SHSphere Combine (const SHSphere& lhs, const SHSphere& rhs) noexcept; + [[nodiscard]] static SHSphere Combine (const SHSphere& lhs, const SHSphere& rhs) noexcept; /** * @brief @@ -111,7 +111,7 @@ namespace SHADE * @return * True if they are intersecting. */ - [[nodiscard]] static bool Intersect (const SHSphere& lhs, const SHSphere& rhs) noexcept; + [[nodiscard]] static bool Intersect (const SHSphere& lhs, const SHSphere& rhs) noexcept; /** * @brief @@ -123,7 +123,7 @@ namespace SHADE * @return * A sphere that contains all the spheres in the set. */ - [[nodiscard]] static SHSphere BuildFromSpheres (const SHSphere* spheres, size_t numSpheres) noexcept; + [[nodiscard]] static SHSphere BuildFromSpheres (const SHSphere* spheres, size_t numSpheres) noexcept; /** * @brief diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp index 540d5375..5fa93021 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp @@ -105,7 +105,7 @@ namespace SHADE /* Getter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - const std::vector& SHAABBTree::GetAABBs() const noexcept + const std::vector& SHAABBTree::GetAABBs() const noexcept { static AABBs aabbs; static std::stack nodeIndices; @@ -142,7 +142,7 @@ namespace SHADE /* Public Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHAABBTree::Insert(SHCollisionShapeID id, const SHBox& AABB) + void SHAABBTree::Insert(SHCollisionShapeID id, const SHAABB& AABB) { const int32_t NEW_INDEX = allocateNode(); @@ -170,7 +170,7 @@ namespace SHADE insertLeaf(NEW_INDEX); } - void SHAABBTree::Update(SHCollisionShapeID id, const SHBox& AABB) + void SHAABBTree::Update(SHCollisionShapeID id, const SHAABB& AABB) { // Get node index const int32_t INDEX_TO_UPDATE = nodeMap[id]; @@ -206,7 +206,7 @@ namespace SHADE freeNode(INDEX_TO_REMOVE); } - const std::vector& SHAABBTree::Query(SHCollisionShapeID id, const SHBox& AABB) const noexcept + const std::vector& SHAABBTree::Query(SHCollisionShapeID id, const SHAABB& AABB) const noexcept { static std::vector potentialCollisions; static std::stack nodeIndices; @@ -226,7 +226,7 @@ namespace SHADE continue; const Node& NODE = nodes[INDEX]; - if (!SHBox::Intersect(AABB, NODE.AABB)) + if (!SHAABB::Intersect(AABB, NODE.AABB)) continue; // Avoid checking against shapes of the same composite collider (and itself) @@ -328,12 +328,12 @@ namespace SHADE // Find best sibling for new leaf // Utilise Surface Area Heuristic - const SHBox& LEAF_AABB = nodes[index].AABB; + const SHAABB& LEAF_AABB = nodes[index].AABB; uint32_t searchIndex = root; while (!isLeaf(searchIndex)) { - const SHBox COMBINED_AABB = SHBox::Combine(LEAF_AABB, nodes[searchIndex].AABB); + const SHAABB COMBINED_AABB = SHAABB::Combine(LEAF_AABB, nodes[searchIndex].AABB); const float COMBINED_AREA = COMBINED_AABB.SurfaceArea(); const float INHERITED_COST = 2.0f * (COMBINED_AREA - nodes[searchIndex].AABB.SurfaceArea()); @@ -344,8 +344,8 @@ namespace SHADE float leftCost = 0.0f; float rightCost = 0.0f; - const float LEFT_COMBINED_AREA = SHBox::Combine(LEAF_AABB, nodes[LEFT_INDEX].AABB).SurfaceArea(); - const float RIGHT_COMBINED_AREA = SHBox::Combine(LEAF_AABB, nodes[RIGHT_INDEX].AABB).SurfaceArea(); + const float LEFT_COMBINED_AREA = SHAABB::Combine(LEAF_AABB, nodes[LEFT_INDEX].AABB).SurfaceArea(); + const float RIGHT_COMBINED_AREA = SHAABB::Combine(LEAF_AABB, nodes[RIGHT_INDEX].AABB).SurfaceArea(); // Compute cost for descending into the left if (isLeaf(LEFT_INDEX)) @@ -377,7 +377,7 @@ namespace SHADE Node& newParent = nodes[NEW_PARENT]; newParent.parent = OLD_PARENT; newParent.id = SHCollisionShapeID{ MAX_EID, std::numeric_limits::max() }; - newParent.AABB = SHBox::Combine(LEAF_AABB, nodes[BEST_SIBLING].AABB); + newParent.AABB = SHAABB::Combine(LEAF_AABB, nodes[BEST_SIBLING].AABB); newParent.height = nodes[BEST_SIBLING].height + 1; newParent.left = BEST_SIBLING; @@ -437,7 +437,7 @@ namespace SHADE const Node& RIGHT_NODE = nodes[RIGHT_INDEX]; nodes[index].height = 1 + SHMath::Max(LEFT_NODE.height, RIGHT_NODE.height); - nodes[index].AABB = SHBox::Combine(LEFT_NODE.AABB, RIGHT_NODE.AABB); + nodes[index].AABB = SHAABB::Combine(LEFT_NODE.AABB, RIGHT_NODE.AABB); // Sync up to the root index = nodes[index].parent; @@ -506,8 +506,8 @@ namespace SHADE nodeA.right = G; nodeG.parent = index; - nodeA.AABB = SHBox::Combine(nodeB.AABB, nodeG.AABB); - nodeC.AABB = SHBox::Combine(nodeA.AABB, nodeF.AABB); + nodeA.AABB = SHAABB::Combine(nodeB.AABB, nodeG.AABB); + nodeC.AABB = SHAABB::Combine(nodeA.AABB, nodeF.AABB); nodeA.height = 1 + SHMath::Max(nodeB.height, nodeG.height); nodeC.height = 1 + SHMath::Max(nodeA.height, nodeF.height); @@ -518,8 +518,8 @@ namespace SHADE nodeA.right = F; nodeF.parent = index; - nodeA.AABB = SHBox::Combine(nodeB.AABB, nodeF.AABB); - nodeC.AABB = SHBox::Combine(nodeA.AABB, nodeG.AABB); + nodeA.AABB = SHAABB::Combine(nodeB.AABB, nodeF.AABB); + nodeC.AABB = SHAABB::Combine(nodeA.AABB, nodeG.AABB); nodeA.height = 1 + SHMath::Max(nodeB.height, nodeF.height); nodeC.height = 1 + SHMath::Max(nodeA.height, nodeG.height); @@ -569,8 +569,8 @@ namespace SHADE nodeA.left = E; nodeE.parent = index; - nodeA.AABB = SHBox::Combine(nodeC.AABB, nodeE.AABB); - nodeB.AABB = SHBox::Combine(nodeA.AABB, nodeD.AABB); + nodeA.AABB = SHAABB::Combine(nodeC.AABB, nodeE.AABB); + nodeB.AABB = SHAABB::Combine(nodeA.AABB, nodeD.AABB); nodeA.height = 1 + SHMath::Max(nodeC.height, nodeE.height); nodeB.height = 1 + SHMath::Max(nodeA.height, nodeD.height); @@ -581,8 +581,8 @@ namespace SHADE nodeA.left = D; nodeD.parent = index; - nodeA.AABB = SHBox::Combine(nodeC.AABB, nodeD.AABB); - nodeB.AABB = SHBox::Combine(nodeA.AABB, nodeE.AABB); + nodeA.AABB = SHAABB::Combine(nodeC.AABB, nodeD.AABB); + nodeB.AABB = SHAABB::Combine(nodeA.AABB, nodeE.AABB); nodeA.height = 1 + SHMath::Max(nodeC.height, nodeD.height); nodeB.height = 1 + SHMath::Max(nodeA.height, nodeE.height); diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h index 420f30e7..32b5095a 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h @@ -32,7 +32,7 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using AABBs = std::vector; + using AABBs = std::vector; /*---------------------------------------------------------------------------------*/ /* Data Members */ @@ -67,11 +67,11 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - void Insert (SHCollisionShapeID id, const SHBox& AABB); - void Update (SHCollisionShapeID id, const SHBox& AABB); + void Insert (SHCollisionShapeID id, const SHAABB& AABB); + void Update (SHCollisionShapeID id, const SHAABB& AABB); void Remove (SHCollisionShapeID id) noexcept; - [[nodiscard]] const std::vector& Query(SHCollisionShapeID id, const SHBox& AABB) const noexcept; + [[nodiscard]] const std::vector& Query(SHCollisionShapeID id, const SHAABB& AABB) const noexcept; [[nodiscard]] const std::vector& Query(const SHRay& ray, float distance) const noexcept; private: @@ -103,7 +103,7 @@ namespace SHADE /* Data Members */ /*-------------------------------------------------------------------------------*/ - SHBox AABB; + SHAABB AABB; SHCollisionShapeID id; // Used to lookup the collision shape & entity for culling against itself union diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 0690b071..61f2b43c 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -17,7 +17,7 @@ #include "Physics/Collision/CollisionTags/SHCollisionTags.h" #include "Physics/Collision/SHPhysicsMaterial.h" #include "SHCollisionShapeID.h" -#include "Math/Geometry/SHBox.h" +#include "Math/Geometry/SHAABB.h" #include "Math/Transform/SHTransform.h" namespace SHADE @@ -131,7 +131,7 @@ namespace SHADE virtual void ComputeTransforms () noexcept = 0; [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; - [[nodiscard]] virtual SHBox ComputeAABB () const noexcept = 0; + [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp index 30497f36..71d965c5 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -227,9 +227,9 @@ namespace SHADE ); } - SHBox SHSphereCollisionShape::ComputeAABB() const noexcept + SHAABB SHSphereCollisionShape::ComputeAABB() const noexcept { - return SHBox{ Center, SHVec3{ Radius } }; + return SHAABB{ Center, SHVec3{ Radius } }; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index eb41f93c..6e119cd1 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -128,7 +128,7 @@ namespace SHADE [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; - [[nodiscard]] SHBox ComputeAABB () const noexcept override; + [[nodiscard]] SHAABB ComputeAABB () const noexcept override; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index b225a9a4..59ee532a 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -132,7 +132,7 @@ namespace YAML { case SHCollisionShape::Type::BOX: { - //const auto* BOX = reinterpret_cast(rhs.GetShape()); + //const auto* BOX = reinterpret_cast(rhs.GetShape()); //node[HalfExtents] = BOX->GetRelativeExtents(); } break; @@ -170,8 +170,7 @@ namespace YAML { case SHCollisionShape::Type::BOX: { - //if (node[HalfExtents].IsDefined()) - // rhs.SetBoundingBox(node[HalfExtents].as()); + } break; case SHCollisionShape::Type::SPHERE: diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index 1c84637a..3dd1446e 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -128,39 +128,39 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 BoxCollider::Center::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetCenter()); + //return Convert::ToCLI(getNativeCollisionShape().GetCenter()); return Vector3::Zero; } void BoxCollider::Center::set(Vector3 value) { - //getNativeCollisionShape().SetCenter(Convert::ToNative(value)); + //getNativeCollisionShape().SetCenter(Convert::ToNative(value)); } Vector3 BoxCollider::HalfExtents::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); + //return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); return Vector3::Zero; } void BoxCollider::HalfExtents::set(Vector3 value) { - //getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); + //getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); } Vector3 BoxCollider::Min::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetMin()); + //return Convert::ToCLI(getNativeCollisionShape().GetMin()); return Vector3::Zero; } void BoxCollider::Min::set(Vector3 value) { - //getNativeCollisionShape().SetMin(Convert::ToNative(value)); + //getNativeCollisionShape().SetMin(Convert::ToNative(value)); } Vector3 BoxCollider::Max::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetMax()); + //return Convert::ToCLI(getNativeCollisionShape().GetMax()); return Vector3::Zero; } void BoxCollider::Max::set(Vector3 value) { - //getNativeCollisionShape().SetMax(Convert::ToNative(value)); + //getNativeCollisionShape().SetMax(Convert::ToNative(value)); } /*---------------------------------------------------------------------------------*/ @@ -168,12 +168,12 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ bool BoxCollider::TestPoint(Vector3 point) { - //return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); + //return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); return false; } bool BoxCollider::Raycast(Ray ray, float maxDistance) { - //return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); + //return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); return false; } From 89f1f600640c9d7420a674b4192ae8ad950f8c58 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 24 Dec 2022 02:19:53 +0800 Subject: [PATCH 040/164] Added physics settings menu for easily toggling debug draw states --- Assets/Scenes/PhysicsSandbox.shade | 16 +++++++------- .../src/Application/SBApplication.cpp | 6 ------ .../EditorWindow/MenuBar/SHEditorMenuBar.cpp | 21 +++++++++++++++++++ .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 5 +++++ .../src/Physics/Dynamics/SHPhysicsWorld.h | 1 + .../Routines/SHPhysicsDebugDrawRoutine.cpp | 11 ++++++++-- .../System/SHPhysicsDebugDrawSystem.cpp | 16 +++++++++++--- .../Physics/System/SHPhysicsDebugDrawSystem.h | 20 ++++++++++-------- .../src/Physics/System/SHPhysicsSystem.h | 10 ++++----- 9 files changed, 73 insertions(+), 33 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 1438b558..60a97885 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,7 +4,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 3, z: 0} + Translate: {x: 0.0700113177, y: 2.5, z: 0} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -19,14 +19,14 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: true + Freeze Position Y: false Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false Freeze Rotation Z: false IsActive: true Collider Component: - DrawColliders: true + DrawColliders: false Colliders: - Is Trigger: false Type: Sphere @@ -65,8 +65,8 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: -2.5, z: 0} - Rotate: {x: 0, y: 0, z: 0} + Translate: {x: 0, y: 0, z: 0} + Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: @@ -87,11 +87,11 @@ Freeze Rotation Z: false IsActive: true Collider Component: - DrawColliders: true + DrawColliders: false Colliders: - Is Trigger: false Type: Sphere - Radius: 5 + Radius: 2.5 Friction: 0.400000006 Bounciness: 0 Density: 1 @@ -127,7 +127,7 @@ Freeze Rotation Z: false IsActive: true Collider Component: - DrawColliders: true + DrawColliders: false Colliders: - Is Trigger: false Type: Sphere diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 8607702d..dfeb781b 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -185,12 +185,6 @@ namespace Sandbox #endif SHSceneManager::SceneUpdate(0.016f); #ifdef SHEDITOR - static bool drawBP = false; - if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::RIGHT_CTRL)) - { - drawBP = !drawBP; - SHSystemManager::GetSystem()->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE, drawBP); - } SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, SHFrameRateController::GetRawDeltaTime()); editor->PollPicking(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index a1335e19..a08f0f75 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -25,6 +25,7 @@ #include "Serialization/SHSerialization.h" #include "Serialization/Configurations/SHConfigurationManager.h" #include "Editor/EditorWindow/SHEditorWindowManager.h" +#include "Physics/System/SHPhysicsDebugDrawSystem.h" const std::string LAYOUT_FOLDER_PATH{ std::string(ASSET_ROOT) + "/Editor/Layouts" }; @@ -202,6 +203,26 @@ namespace SHADE ImGui::EndMenu(); } + if (ImGui::BeginMenu("Physics Settings")) + { + if (auto* physicsDebugDraw = SHSystemManager::GetSystem()) + { + bool drawColliders = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDERS); + if (ImGui::Checkbox("Draw Colliders", &drawColliders)) + physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDERS, drawColliders); + + bool drawContactPoints = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS); + if (ImGui::Checkbox("Draw Contact Points", &drawContactPoints)) + physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS, drawContactPoints); + + bool drawBroadphase = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE); + if (ImGui::Checkbox("Draw Broadphase", &drawBroadphase)) + physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE, drawBroadphase); + } + + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index 8451bd5b..4e6e8165 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -35,6 +35,11 @@ namespace SHADE /* Getter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ + const SHContactManager::ContactPoints& SHPhysicsWorld::GetContactPoints() const noexcept + { + return contactManager.GetContactPoints(); + } + const SHContactManager::TriggerEvents& SHPhysicsWorld::GetTriggerEvents() const noexcept { return contactManager.GetTriggerEvents(); diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index 9bc2199e..f63bac40 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -72,6 +72,7 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ + const SHContactManager::ContactPoints& GetContactPoints () const noexcept; const SHContactManager::TriggerEvents& GetTriggerEvents () const noexcept; const SHContactManager::CollisionEvents& GetCollisionEvents () const noexcept; diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp index 9bf43115..8aad19f5 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -38,7 +38,7 @@ namespace SHADE { auto* physicsDebugDrawSystem = reinterpret_cast(GetSystem()); - if (!physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::ACTIVE)) + if (!physicsDebugDrawSystem->IsDebugDrawActive()) return; auto* debugDrawSystem = SHSystemManager::GetSystem(); @@ -74,7 +74,14 @@ namespace SHADE if (DRAW_CONTACTS) { - // TODO + const SHColour& CONTACT_COLOUR = physicsDebugDrawSystem->DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::CONTACT)]; + + const auto& CONTACT_POINTS = physicsSystem->physicsWorld->GetContactPoints(); + for (auto& contactPoint : CONTACT_POINTS) + { + const SHMatrix TRS = SHMatrix::Transform(contactPoint, SHQuaternion::Identity, SHVec3{ 0.1f }); + debugDrawSystem->DrawCube(TRS, CONTACT_COLOUR); + } } if (DRAW_RAYCASTS) diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index 958ac61a..9940ad20 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -44,6 +44,11 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ + bool SHPhysicsDebugDrawSystem::IsDebugDrawActive() const noexcept + { + return flags & ACTIVE_FLAG; + } + bool SHPhysicsDebugDrawSystem::GetFlagState(DebugDrawFlags flag) const noexcept { const uint8_t ENUM_VALUE = SHUtilities::ConvertEnum(flag); @@ -58,6 +63,10 @@ namespace SHADE { const uint8_t ENUM_VALUE = SHUtilities::ConvertEnum(flag); state ? flags |= ENUM_VALUE : flags &= ~ENUM_VALUE; + + // If no other debug drawing state is active, turn active off. Otherwise, we maintain + // the active state. + flags == ACTIVE_FLAG ? flags = 0 : flags |= ACTIVE_FLAG; } /*-----------------------------------------------------------------------------------*/ @@ -97,7 +106,7 @@ namespace SHADE if (EVENT_DATA->debugDrawState) { if (collidersToDraw.empty()) - SetFlagState(DebugDrawFlags::ACTIVE, true); + flags |= ACTIVE_FLAG; collidersToDraw.emplace(EVENT_DATA->entityID); } @@ -105,8 +114,9 @@ namespace SHADE { collidersToDraw.erase(EVENT_DATA->entityID); - if (collidersToDraw.empty()) - SetFlagState(DebugDrawFlags::ACTIVE, false); + // if no colliders queued for drawing and no other debug drawing is enabled, disable debug drawing + if (collidersToDraw.empty() && flags == ACTIVE_FLAG) + flags = 0; } return onColliderDrawEvent.get()->handle; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index 79b88d3f..14f86364 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -35,11 +35,10 @@ namespace SHADE enum class DebugDrawFlags : uint8_t { - ACTIVE = 0x0001 - , COLLIDERS = 0x0002 - , CONTACTS = 0x0004 - , RAYCASTS = 0x0008 - , BROADPHASE = 0x0010 + COLLIDERS = 0x02 + , CONTACTS = 0x04 + , RAYCASTS = 0x08 + , BROADPHASE = 0x10 }; /*---------------------------------------------------------------------------------*/ @@ -53,7 +52,8 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] bool GetFlagState (DebugDrawFlags flag) const noexcept; + [[nodiscard]] bool IsDebugDrawActive () const noexcept; + [[nodiscard]] bool GetFlagState (DebugDrawFlags flag) const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ @@ -64,10 +64,10 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - void Init () override; - void Exit () override; + void Init () override; + void Exit () override; - void AddRaycast (const SHRay& ray, const SHPhysicsRaycastResult& result) noexcept; + void AddRaycast (const SHRay& ray, const SHPhysicsRaycastResult& result) noexcept; /*---------------------------------------------------------------------------------*/ /* System Routines */ @@ -123,6 +123,8 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ + static constexpr uint8_t ACTIVE_FLAG = 0x01; + static const SHColour DEBUG_DRAW_COLOURS[static_cast(Colours::COUNT)]; // 0 0 0 drawBroadphase drawRaycasts drawContacts drawAllColliders debugDrawActive diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index ab28a299..c7dff6c6 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -52,8 +52,11 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] double GetFixedUpdateRate() const noexcept; - [[nodiscard]] double GetFixedDT () const noexcept; + [[nodiscard]] double GetFixedUpdateRate() const noexcept; + [[nodiscard]] double GetFixedDT () const noexcept; + + [[nodiscard]] const std::vector& GetTriggerInfo () const noexcept; + [[nodiscard]] const std::vector& GetCollisionInfo () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ @@ -62,9 +65,6 @@ namespace SHADE void SetFixedUpdateRate(double fixedUpdateRate) noexcept; void SetFixedDT(double fixedDt) noexcept; - const std::vector& GetTriggerInfo () const noexcept; - const std::vector& GetCollisionInfo () const noexcept; - /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ From 0df6e09ed6334de0ca070b1bc24d20456e6d4ee6 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 24 Dec 2022 13:32:50 +0800 Subject: [PATCH 041/164] Added box collision shapes --- Assets/Scenes/PhysicsSandbox.shade | 5 +- .../Inspector/SHEditorComponentView.hpp | 24 +- SHADE_Engine/src/Math/Geometry/SHAABB.cpp | 20 +- SHADE_Engine/src/Math/Geometry/SHAABB.h | 22 +- SHADE_Engine/src/Math/Geometry/SHBox.cpp | 169 +++++++++++ SHADE_Engine/src/Math/Geometry/SHBox.h | 135 +++++++++ SHADE_Engine/src/Math/Geometry/SHShape.h | 7 +- SHADE_Engine/src/Math/SHQuaternion.cpp | 4 + SHADE_Engine/src/Math/SHQuaternion.h | 7 +- .../Broadphase/SHDynamicAABBTree.cpp | 9 + .../CollisionShapes/SHBoxCollisionShape.cpp | 265 ++++++++++++++++++ .../CollisionShapes/SHBoxCollisionShape.h | 158 +++++++++++ .../SHCollisionShapeFactory.cpp | 24 ++ .../CollisionShapes/SHCollisionShapeFactory.h | 21 +- .../CollisionShapes/SHSphereCollisionShape.h | 24 +- .../src/Physics/Collision/SHCollider.cpp | 54 ++++ .../src/Physics/Collision/SHCollider.h | 29 +- .../Routines/SHPhysicsDebugDrawRoutine.cpp | 2 +- .../System/SHPhysicsDebugDrawSystem.cpp | 2 +- .../src/Serialization/SHYAMLConverters.h | 10 +- SHADE_Managed/src/Components/Collider.cxx | 4 +- 21 files changed, 929 insertions(+), 66 deletions(-) create mode 100644 SHADE_Engine/src/Math/Geometry/SHBox.cpp create mode 100644 SHADE_Engine/src/Math/Geometry/SHBox.h create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 60a97885..e001867a 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -106,7 +106,7 @@ Components: Transform Component: Translate: {x: 0, y: 5, z: 0} - Rotate: {x: -0, y: 0, z: -0} + Rotate: {x: -0, y: 0, z: 0} Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: @@ -130,8 +130,7 @@ DrawColliders: false Colliders: - Is Trigger: false - Type: Sphere - Radius: 1 + Type: Box Friction: 0.400000006 Bounciness: 0 Density: 1 diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 622a2d37..cf5cdeb1 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -344,24 +344,24 @@ namespace SHADE //collider->IsTrigger if (shape->GetType() == SHCollisionShape::Type::BOX) { - //SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - // - //const auto* BOX = reinterpret_cast(collider->GetShape()); - //SHEditorWidgets::DragVec3 - //( - // "Half Extents", { "X", "Y", "Z" }, - // [BOX] { return BOX->GetRelativeExtents(); }, - // [collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); }); + SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); + + auto* box = reinterpret_cast(shape); + SHEditorWidgets::DragVec3 + ( + "Half Extents", { "X", "Y", "Z" }, + [box] { return box->GetRelativeExtents(); }, + [box](SHVec3 const& vec) { box->SetRelativeExtents(vec); }); } else if (shape->GetType() == SHCollisionShape::Type::SPHERE) { SHEditorWidgets::BeginPanel(std::format("{} Sphere #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - auto* SPHERE = reinterpret_cast(shape); + auto* sphere = reinterpret_cast(shape); SHEditorWidgets::DragFloat ( "Radius", - [SPHERE] { return SPHERE->GetRelativeRadius(); }, - [SPHERE](float const& value) { SPHERE->SetRelativeRadius(value); }); + [sphere] { return sphere->GetRelativeRadius(); }, + [sphere](float const& value) { sphere->SetRelativeRadius(value); }); } else if (shape->GetType() == SHCollisionShape::Type::CAPSULE) { @@ -410,7 +410,7 @@ namespace SHADE { if (ImGui::Selectable("Box Collider")) { - //component->AddBoundingBox(); + component->GetCollider()->AddBoxCollisionShape(SHVec3::One); } if (ImGui::Selectable("Sphere Collider")) { diff --git a/SHADE_Engine/src/Math/Geometry/SHAABB.cpp b/SHADE_Engine/src/Math/Geometry/SHAABB.cpp index 8a44457f..30683216 100644 --- a/SHADE_Engine/src/Math/Geometry/SHAABB.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHAABB.cpp @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHBox.cpp + * \file SHAABB.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Implementation for a 3-Dimensional Axis Aligned Bounding Box * @@ -26,12 +26,12 @@ namespace SHADE SHAABB::SHAABB() noexcept { - type = Type::BOX; + type = Type::AABB; } SHAABB::SHAABB(const SHVec3& c, const SHVec3& hE) noexcept { - type = Type::BOX; + type = Type::AABB; Center = c; Extents = hE; @@ -43,7 +43,7 @@ namespace SHADE if (this == &rhs) return; - type = Type::BOX; + type = Type::AABB; Center = rhs.Center; Extents = rhs.Extents; @@ -51,7 +51,7 @@ namespace SHADE SHAABB::SHAABB(SHAABB&& rhs) noexcept { - type = Type::BOX; + type = Type::AABB; Center = rhs.Center; Extents = rhs.Extents; @@ -63,7 +63,7 @@ namespace SHADE SHAABB& SHAABB::operator=(const SHAABB& rhs) noexcept { - if (rhs.type != Type::BOX) + if (rhs.type != Type::AABB) { SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") } @@ -78,7 +78,7 @@ namespace SHADE SHAABB& SHAABB::operator=(SHAABB&& rhs) noexcept { - if (rhs.type != Type::BOX) + if (rhs.type != Type::AABB) { SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") } @@ -100,7 +100,7 @@ namespace SHADE return Center; } - SHVec3 SHAABB::GetWorldExtents() const noexcept + SHVec3 SHAABB::GetExtents() const noexcept { return Extents; } @@ -124,9 +124,9 @@ namespace SHADE Center = newCenter; } - void SHAABB::SetWorldExtents(const SHVec3& newWorldExtents) noexcept + void SHAABB::SetExtents(const SHVec3& newHalfExtents) noexcept { - Extents = newWorldExtents; + Extents = newHalfExtents; } void SHAABB::SetMin(const SHVec3& min) noexcept diff --git a/SHADE_Engine/src/Math/Geometry/SHAABB.h b/SHADE_Engine/src/Math/Geometry/SHAABB.h index 46608ded..76e0b2b7 100644 --- a/SHADE_Engine/src/Math/Geometry/SHAABB.h +++ b/SHADE_Engine/src/Math/Geometry/SHAABB.h @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHBox.h + * \file SHAABB.h * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Interface for a 3-Dimensional Axis Aligned Bounding Box * @@ -54,21 +54,21 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; - [[nodiscard]] SHVec3 GetMin () const noexcept; - [[nodiscard]] SHVec3 GetMax () const noexcept; - [[nodiscard]] std::vector GetVertices () const noexcept; + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] SHVec3 GetExtents () const noexcept; + [[nodiscard]] SHVec3 GetMin () const noexcept; + [[nodiscard]] SHVec3 GetMax () const noexcept; + [[nodiscard]] std::vector GetVertices () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetCenter (const SHVec3& newCenter) noexcept; - void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; - void SetMin (const SHVec3& min) noexcept; - void SetMax (const SHVec3& max) noexcept; - void SetMinMax (const SHVec3& min, const SHVec3& max) noexcept; + void SetCenter (const SHVec3& newCenter) noexcept; + void SetExtents (const SHVec3& newHalfExtents) noexcept; + void SetMin (const SHVec3& min) noexcept; + void SetMax (const SHVec3& max) noexcept; + void SetMinMax (const SHVec3& min, const SHVec3& max) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.cpp b/SHADE_Engine/src/Math/Geometry/SHBox.cpp new file mode 100644 index 00000000..9dacc623 --- /dev/null +++ b/SHADE_Engine/src/Math/Geometry/SHBox.cpp @@ -0,0 +1,169 @@ +/**************************************************************************************** + * \file SHBox.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a 3-Dimensional Oriented Bounding Box + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHBox.h" +// Project Headers +#include "Math/SHMathHelpers.h" +#include "Math/SHRay.h" +#include "Math/SHQuaternion.h" + +using namespace DirectX; + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHBox::SHBox() noexcept + { + type = Type::BOX; + } + + SHBox::SHBox(const SHVec3& c, const SHVec3& hE, const SHQuaternion& o) noexcept + { + type = Type::BOX; + + Center = c; + Extents = hE; + Orientation = o; + } + + + SHBox::SHBox(const SHBox& rhs) noexcept + { + if (this == &rhs) + return; + + type = Type::BOX; + + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; + } + + SHBox::SHBox(SHBox&& rhs) noexcept + { + type = Type::BOX; + + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHBox& SHBox::operator=(const SHBox& rhs) noexcept + { + if (rhs.type != Type::BOX) + { + SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") + } + else if (this != &rhs) + { + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; + } + + return *this; + } + + SHBox& SHBox::operator=(SHBox&& rhs) noexcept + { + if (rhs.type != Type::BOX) + { + SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") + } + else + { + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; + } + + return *this; + } + + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + std::vector SHBox::GetVertices() const noexcept + { + std::vector vertices; + vertices.resize(8); + GetCorners(vertices.data()); + return vertices; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHBox::TestPoint(const SHVec3& point) const noexcept + { + return BoundingOrientedBox::Contains(point); + } + + SHRaycastResult SHBox::Raycast(const SHRay& ray) const noexcept + { + SHRaycastResult result; + + result.hit = Intersects(ray.position, ray.direction, result.distance); + if (result.hit) + { + result.position = ray.position + ray.direction * result.distance; + result.angle = SHVec3::Angle(ray.position, result.position); + } + + return result; + } + + bool SHBox::Contains(const SHBox& rhs) const noexcept + { + return BoundingOrientedBox::Contains(rhs) == CONTAINS; + } + + float SHBox::Volume() const noexcept + { + return 8.0f * (Extents.x * Extents.y * Extents.z); + } + + float SHBox::SurfaceArea() const noexcept + { + return 8.0f * ((Extents.x * Extents.y) + + (Extents.x * Extents.z) + + (Extents.y * Extents.z)); + } + + /*-----------------------------------------------------------------------------------*/ + /* Static Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHBox::Intersect(const SHBox& lhs, const SHBox& rhs) noexcept + { + return lhs.Intersects(rhs); + } + + SHBox SHBox::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept + { + SHBox result; + CreateFromPoints(result, numVertices, vertices, stride); + return result; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.h b/SHADE_Engine/src/Math/Geometry/SHBox.h new file mode 100644 index 00000000..bb1098bc --- /dev/null +++ b/SHADE_Engine/src/Math/Geometry/SHBox.h @@ -0,0 +1,135 @@ +/**************************************************************************************** + * \file SHBox.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a 3-Dimensional Oriented Bounding Box + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "SHShape.h" +#include "SH_API.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + class SH_API SHBox : public SHShape, + public DirectX::BoundingOrientedBox + { + public: + /*---------------------------------------------------------------------------------*/ + /* Static Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr size_t NUM_VERTICES = 8; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + ~SHBox () override = default; + + SHBox () noexcept; + SHBox (const SHVec3& center, const SHVec3& halfExtents, const SHQuaternion& orientation) noexcept; + SHBox (const SHBox& rhs) noexcept; + SHBox (SHBox&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHBox& operator= (const SHBox& rhs) noexcept; + SHBox& operator= (SHBox&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] std::vector GetVertices () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Checks if a point is inside the box. + * @param point + * The point to check. + * @return + * True if the point is inside the box. + */ + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + + /** + * @brief + * Casts a ray against the box. + * @param ray + * The ray to cast. + * @return + * The result of the raycast.
+ * See the corresponding header for the contents of the raycast result object. + */ + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + + /** + * @brief + * Checks if an entire other box is contained by this box. + * @param rhs + * The box to check. + * @return + * True if the other sphere is completely contained by this box. + */ + [[nodiscard]] bool Contains (const SHBox& rhs) const noexcept; + + /** + * @brief + * Calculates the volume of the box. + */ + [[nodiscard]] float Volume () const noexcept; + + /** + * @brief + * Calculates the surface area of the box. + */ + [[nodiscard]] float SurfaceArea () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Static Function Members */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Checks if two boxes are intersecting. + * @return + * True if they are intersecting. + */ + [[nodiscard]] static bool Intersect (const SHBox& lhs, const SHBox& rhs) noexcept; + + /** + * @brief + * Builds a box from a set of vertices. + * @param vertices + * The vertices to build a box from. + * @param numVertices + * The number of vertices in the set to build from. + * @param stride + * The stride between each vertex, in the instance there is data in between each + * vertex that does not define the geometry of the object. + * @return + * An box that contains all the vertices in the set. + */ + [[nodiscard]] static SHBox BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; + }; + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHShape.h b/SHADE_Engine/src/Math/Geometry/SHShape.h index 812cb169..2b644917 100644 --- a/SHADE_Engine/src/Math/Geometry/SHShape.h +++ b/SHADE_Engine/src/Math/Geometry/SHShape.h @@ -30,10 +30,9 @@ namespace SHADE enum class Type { - BOX - , SPHERE - , CAPSULE - , CONVEX_HULL + SPHERE + , AABB + , BOX , COUNT , NONE = -1 diff --git a/SHADE_Engine/src/Math/SHQuaternion.cpp b/SHADE_Engine/src/Math/SHQuaternion.cpp index 021f0a6a..1f4645df 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.cpp +++ b/SHADE_Engine/src/Math/SHQuaternion.cpp @@ -40,6 +40,10 @@ namespace SHADE : XMFLOAT4( vec4.x, vec4.y, vec4.z, vec4.w ) {} + SHQuaternion::SHQuaternion(const XMFLOAT4& xmfloat4) noexcept + : XMFLOAT4( xmfloat4 ) + {} + SHQuaternion::SHQuaternion(float _x, float _y, float _z, float _w) noexcept : XMFLOAT4( _x, _y, _z, _w ) {} diff --git a/SHADE_Engine/src/Math/SHQuaternion.h b/SHADE_Engine/src/Math/SHQuaternion.h index 93c546ca..3342c4cb 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.h +++ b/SHADE_Engine/src/Math/SHQuaternion.h @@ -47,9 +47,10 @@ namespace SHADE SHQuaternion (const SHQuaternion& rhs) = default; SHQuaternion (SHQuaternion&& rhs) = default; - SHQuaternion () noexcept; - SHQuaternion (const SHVec4& vec4) noexcept; - SHQuaternion (float x, float y, float z, float w) noexcept; + SHQuaternion () noexcept; + SHQuaternion (const SHVec4& vec4) noexcept; + SHQuaternion (const XMFLOAT4& xmfloat4) noexcept; + SHQuaternion (float x, float y, float z, float w) noexcept; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp index 5fa93021..127494cb 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp @@ -204,6 +204,8 @@ namespace SHADE removeLeaf(INDEX_TO_REMOVE); freeNode(INDEX_TO_REMOVE); + + nodeMap.erase(id); } const std::vector& SHAABBTree::Query(SHCollisionShapeID id, const SHAABB& AABB) const noexcept @@ -404,6 +406,13 @@ namespace SHADE } const int32_t PARENT = nodes[index].parent; + + if (PARENT == NULL_NODE) + { + freeNode(index); + return; + } + const int32_t GRANDPARENT = nodes[PARENT].parent; const int32_t SIBLING = nodes[PARENT].left == index ? nodes[PARENT].right : nodes[PARENT].left; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp new file mode 100644 index 00000000..67e497a8 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp @@ -0,0 +1,265 @@ +/**************************************************************************************** + * \file SHBoxCollisionShape.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Box Collision Shape. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHBoxCollisionShape.h" + +// Project Headers +#include "Math/SHMathHelpers.h" +#include "Math/SHMatrix.h" +#include "Physics/Collision/SHCollider.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHBoxCollisionShape::SHBoxCollisionShape(SHCollisionShapeID id) noexcept + : SHCollisionShape (id, SHCollisionShape::Type::BOX) + , SHBox () + , relativeExtents { SHVec3::One } + , scale { SHVec3::One } + {} + + SHBoxCollisionShape::SHBoxCollisionShape(const SHBoxCollisionShape& rhs) noexcept + : SHCollisionShape (rhs.id, SHCollisionShape::Type::BOX) + , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) + , relativeExtents { rhs.relativeExtents } + , scale { rhs.scale } + { + + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + } + + SHBoxCollisionShape::SHBoxCollisionShape(SHBoxCollisionShape&& rhs) noexcept + : SHCollisionShape (rhs.id, SHCollisionShape::Type::BOX) + , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) + , relativeExtents { rhs.relativeExtents } + , scale { rhs.scale } + { + + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHBoxCollisionShape& SHBoxCollisionShape::operator=(const SHBoxCollisionShape& rhs) noexcept + { + if (this == &rhs) + return *this; + + // Collision Shape Properties + + id = rhs.id; + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + + // Box Properties + + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; + + // Local Properties + + relativeExtents = rhs.relativeExtents; + scale = rhs.scale; + + return *this; + } + + SHBoxCollisionShape& SHBoxCollisionShape::operator=(SHBoxCollisionShape&& rhs) noexcept + { + // Collision Shape Properties + + id = rhs.id; + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + + // Box Properties + + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; + + // Local Properties + + relativeExtents = rhs.relativeExtents; + scale = rhs.scale; + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHVec3 SHBoxCollisionShape::GetCenter() const noexcept + { + return Center; + } + + SHVec3 SHBoxCollisionShape::GetWorldExtents() const noexcept + { + return Extents; + } + + SHVec3 SHBoxCollisionShape::GetRelativeExtents() const noexcept + { + return relativeExtents; + } + + SHQuaternion SHBoxCollisionShape::GetOrientation() const noexcept + { + return Orientation; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHBoxCollisionShape::SetCenter(const SHVec3& newCenter) noexcept + { + Center = newCenter; + } + + void SHBoxCollisionShape::SetWorldExtents(const SHVec3& newWorldExtents) noexcept + { + Extents = newWorldExtents; + + // Recompute Relative radius + relativeExtents = 2.0f * Extents / scale; + } + + void SHBoxCollisionShape::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept + { + relativeExtents = newRelativeExtents; + + // Recompute world radius + Extents = relativeExtents * scale * 0.5f; + } + + void SHBoxCollisionShape::SetOrientation(const SHQuaternion& newOrientation) noexcept + { + Orientation = newOrientation; + } + + + void SHBoxCollisionShape::SetScale(const SHVec3& newScale) noexcept + { + scale = SHVec3::Abs(newScale); + + // Recompute world radius + Extents = relativeExtents * scale * 0.5f; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHBoxCollisionShape::ComputeTransforms() noexcept + { + const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); + + SetScale(PARENT_TRANSFORM.scale); + + // Recompute center + const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); + + Center = SHVec3::Transform(transform.position, TRS); + } + + bool SHBoxCollisionShape::TestPoint(const SHVec3& point) const noexcept + { + return SHBox::TestPoint(point); + } + + SHRaycastResult SHBoxCollisionShape::Raycast(const SHRay& ray) const noexcept + { + return SHBox::Raycast(ray); + } + + SHMatrix SHBoxCollisionShape::GetInertiaTensor(float mass) const noexcept + { + static constexpr float ONE_OVER_TWELVE = (1.0f / 12.0f); + + const float H2_PLUS_D2 = Extents.y * Extents.y + Extents.z * Extents.z; + const float W2_PLUS_H2 = Extents.x * Extents.x + Extents.y * Extents.y; + const float W2_PLUS_D2 = Extents.x * Extents.x + Extents.z * Extents.z; + + SHMatrix result; + result.m[0][0] = ONE_OVER_TWELVE * mass * H2_PLUS_D2; + result.m[1][1] = ONE_OVER_TWELVE * mass * W2_PLUS_H2; + result.m[2][2] = ONE_OVER_TWELVE * mass * W2_PLUS_D2; + return result; + } + + SHMatrix SHBoxCollisionShape::ComputeWorldTransform() const noexcept + { + const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); + const SHQuaternion ROTATION = PARENT_TRANSFORM.orientation * transform.orientation; + const SHVec3 SCALE = SHVec3{ Extents } *2.0f; + + return SHMatrix::Transform + ( + Center + , ROTATION + , SCALE + ); + } + + SHAABB SHBoxCollisionShape::ComputeAABB() const noexcept + { + SHVec3 min{ std::numeric_limits::max() }; + SHVec3 max{ std::numeric_limits::lowest() }; + + const auto& VERTICES = GetVertices(); + for (auto& vtx : VERTICES) + { + min = SHVec3::Min({ vtx, min }); + max = SHVec3::Max({ vtx, max }); + } + + const SHVec3 HALF_EXTENTS = (max - min) * 0.5f; + const SHVec3 CENTROID = min + HALF_EXTENTS; + + return SHAABB{ CENTROID, HALF_EXTENTS }; + } + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h new file mode 100644 index 00000000..35da5e6e --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h @@ -0,0 +1,158 @@ +/**************************************************************************************** + * \file SHBoxCollisionShape.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Box Collision Shape. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "Math/Geometry/SHBox.h" +#include "SHCollisionShape.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates the information to create a box. + */ + struct SHBoxCreateInfo + { + public: + SHVec3 Center = SHVec3::Zero; + SHVec3 Extents = SHVec3::One * 0.5f; + SHVec3 RelativeExtents = SHVec3::One; + SHQuaternion Orientation = SHQuaternion::Identity; + SHVec3 Scale = SHVec3::One; + }; + + /** + * @brief + * Encapsulate a Box Collision Shape used for Physics Simulations. + */ + class SH_API SHBoxCollisionShape final : public SHCollisionShape + , private SHBox + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHCollider; + friend class SHCollision; + friend class SHCompositeCollider; + friend class SHCollisionShapeFactory; + + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHBoxCollisionShape (SHCollisionShapeID id) noexcept; + SHBoxCollisionShape (const SHBoxCollisionShape& rhs) noexcept; + SHBoxCollisionShape (SHBoxCollisionShape&& rhs) noexcept; + + ~SHBoxCollisionShape () override = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHBoxCollisionShape& operator= (const SHBoxCollisionShape& rhs) noexcept; + SHBoxCollisionShape& operator= (SHBoxCollisionShape&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; + [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; + [[nodiscard]] SHQuaternion GetOrientation () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetCenter (const SHVec3& newCenter) noexcept; + void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; + void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; + void SetOrientation (const SHQuaternion& newOrientation) noexcept; + void SetScale (const SHVec3& newScale) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Recomputes the transform of this box. + */ + void ComputeTransforms () noexcept override; + + /** + * @brief + * Tests if a point is inside the box. + * @param point + * The point to test. + * @return + * True if the point is inside the box. + */ + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + + /** + * @brief + * Casts a ray against this box. + * @param ray + * The ray to cast. + * @return + * An object holding the results of the raycast.
+ * See the corresponding header for the contents of the object. + */ + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + + /** + * @brief + * Computes the inertia tensor of the box. + * @param mass + * The mass of the sphere. + * @return + * The inertia tensor of the box. + */ + [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; + + /** + * @brief + * Computes the transformation matrix of the box. + * @return + * The transformation matrix of the box. + */ + [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; + + /** + * @brief + * Computes the a tight-fitting AABB that contains this box. + * @return + * A tight-fitting AABB that contains this box. + */ + [[nodiscard]] SHAABB ComputeAABB () const noexcept override; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + SHVec3 relativeExtents; + SHVec3 scale; // Intended to be passed in by the base collider. + }; + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp index d6ef5f0d..97409cf0 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp @@ -24,6 +24,10 @@ namespace SHADE // Free all shapes in each container for (auto* sphereCollisionShape : spheres | std::views::values) DestroyShape(sphereCollisionShape); + + // Free all shapes in each container + for (auto* boxCollisionShape : boxes | std::views::values) + DestroyShape(boxCollisionShape); } /*-----------------------------------------------------------------------------------*/ @@ -48,6 +52,26 @@ namespace SHADE return spheres.find(id)->second; } + SHBoxCollisionShape* SHCollisionShapeFactory::CreateBox(SHCollisionShapeID id, const SHBoxCreateInfo& createInfo) + { + const auto RESULT = boxes.emplace(id, new SHBoxCollisionShape{ id }); + if (RESULT.second) + { + SHBoxCollisionShape* box = RESULT.first->second; + + box->Center = createInfo.Center; + box->Extents = createInfo.Extents; + box->relativeExtents = createInfo.RelativeExtents; + box->Orientation = createInfo.Orientation; + box->scale = createInfo.Scale; + + return box; + } + + return boxes.find(id)->second; + } + + void SHCollisionShapeFactory::DestroyShape(SHCollisionShape* shape) { switch (shape->GetType()) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h index f5820317..c82d7e26 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h @@ -14,6 +14,7 @@ // Project Header #include "SHSphereCollisionShape.h" +#include "SHBoxCollisionShape.h" namespace SHADE { @@ -49,10 +50,22 @@ namespace SHADE * @param createInfo * The info to create the sphere with. * @return - * A newly sphere collision shape. + * A new sphere collision shape. */ SHSphereCollisionShape* CreateSphere (SHCollisionShapeID id, const SHSphereCreateInfo& createInfo); + /** + * @brief + * Creates a box collision shape. + * @param id + * The ID of the shape. + * @param createInfo + * The info to create the box with. + * @return + * A new box collision shape. + */ + SHBoxCollisionShape* CreateBox (SHCollisionShapeID id, const SHBoxCreateInfo& createInfo); + /** * @brief * Destroys a collision shape. @@ -63,7 +76,7 @@ namespace SHADE * @param shape * The shape to destroy. */ - void DestroyShape (SHCollisionShape* shape); + void DestroyShape (SHCollisionShape* shape); private: /*---------------------------------------------------------------------------------*/ @@ -74,13 +87,15 @@ namespace SHADE // Since we are not instancing shapes (yet?), I'd rather not iterate through an entire vector to find the shape. using Spheres = std::unordered_map; + using Boxes = std::unordered_map; /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ Spheres spheres; - // TODO: Add boxes, capsules and hulls + Boxes boxes; + // TODO: Add capsules and hulls }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 6e119cd1..a1ebf6a3 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -93,7 +93,7 @@ namespace SHADE * @brief * Recomputes the transform of this sphere. */ - void ComputeTransforms () noexcept override; + void ComputeTransforms () noexcept override; /** * @brief @@ -103,7 +103,7 @@ namespace SHADE * @return * True if the point is inside the sphere. */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; /** * @brief @@ -114,7 +114,7 @@ namespace SHADE * An object holding the results of the raycast.
* See the corresponding header for the contents of the object. */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; /** * @brief @@ -124,11 +124,23 @@ namespace SHADE * @return * The inertia tensor of the sphere. */ - [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; + [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; - [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; + /** + * @brief + * Computes the transformation matrix of the sphere. + * @return + * The transformation matrix of the sphere. + */ + [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; - [[nodiscard]] SHAABB ComputeAABB () const noexcept override; + /** + * @brief + * Computes the a tight-fitting AABB that contains this sphere. + * @return + * A tight-fitting AABB that contains this sphere. + */ + [[nodiscard]] SHAABB ComputeAABB () const noexcept override; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index 7d7d1a60..4ce819ac 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -332,6 +332,60 @@ namespace SHADE return static_cast(NEW_INDEX); } + int SHCollider::AddBoxCollisionShape(const SHVec3& relativeExtents, const SHVec3& posOffset, const SHVec3& rotOffset) + { + if (!shapeFactory) + { + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add new shape!", entityID) + return -1; + } + + // Compute center + const SHQuaternion FINAL_ROT = transform.orientation * SHQuaternion::FromEuler(rotOffset); + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(transform.position); + + // Create Sphere + const SHBoxCreateInfo BOX_CREATE_INFO + { + .Center = SHVec3::Transform(posOffset, TRS) + , .Extents = relativeExtents * SHVec3::Abs(transform.scale) * 0.5f + , .RelativeExtents = relativeExtents + , .Orientation = FINAL_ROT + , .Scale = SHVec3::Abs(transform.scale) + }; + + const uint32_t NEW_INDEX = static_cast(shapes.size()); + const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; + + SHBoxCollisionShape* box = shapeFactory->CreateBox(NEW_SHAPE_ID, BOX_CREATE_INFO); + + // Set offsets + box->collider = this; + box->SetPositionOffset(posOffset); + box->SetRotationOffset(rotOffset); + + shapes.emplace_back(box); + + if (broadphase) + broadphase->Insert(NEW_SHAPE_ID, box->ComputeAABB()); + + // Broadcast Event for adding a shape + const SHPhysicsColliderAddedEvent EVENT_DATA + { + .entityID = entityID + , .colliderType = SHCollisionShape::Type::BOX + , .colliderIndex = static_cast(NEW_INDEX) + }; + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT); + + if (rigidBody) + rigidBody->ComputeMassData(); + + return static_cast(NEW_INDEX); + } + + void SHCollider::RemoveCollisionShape(int index) { if (!shapeFactory) diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index 51b33cfb..3d80118e 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -123,19 +123,34 @@ namespace SHADE /** * @brief * Adds a sphere collision shape. + * @param relativeRadius + * The relative radius is constructed with respect to the world scale.
+ * Radius = max(scale.x, scale.y, scale.z) * 0.5 * relativeRadius * @param posOffset * The position offset of the sphere from the center of the collider. Defaults to a Zero Vector. * @param rotOffset * The rotation offset of the sphere from the rotation of the collider. Defaults to a Zero Vector. - * @param relativeRadius - * The relative radius is constructed with respect to the world scale.
- * Radius = max(scale.x, scale.y, scale.z) * 0.5 * relativeRadius * @return * The index of the newly added shape. */ - int AddSphereCollisionShape (float relativeRadius, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero); + int AddSphereCollisionShape (float relativeRadius, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero); - // TODO: Add Box & Capsule + /** + * @brief + * Adds a box collision shape. + * @param relativeExtents + * The relative extents are constructed with respect to the world scale.
+ * Extents = scale * 0.5 * relativeExtents + * @param posOffset + * The position offset of the box from the center of the collider. Defaults to a Zero Vector. + * @param rotOffset + * The rotation offset of the box from the rotation of the collider. Defaults to a Zero Vector. + * @return + * The index of the newly added shape. + */ + int AddBoxCollisionShape (const SHVec3& relativeExtents, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero); + + // TODO: Add Capsule /** * @brief @@ -146,13 +161,13 @@ namespace SHADE * @throws * Invalid argument for out-of-range indices. */ - void RemoveCollisionShape (int index); + void RemoveCollisionShape (int index); /** * @brief * Recomputes the transforms for all shapes in this composite collider. */ - void RecomputeShapes () noexcept; + void RecomputeShapes () noexcept; protected: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp index 8aad19f5..f4a72f82 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -98,7 +98,7 @@ namespace SHADE for (auto& aabb : BROADPHASE_AABBS) { // Compute AABB Transform - const SHMatrix TRS = SHMatrix::Transform(aabb.GetCenter(), SHQuaternion::Identity, aabb.GetWorldExtents() * 2.0f); + const SHMatrix TRS = SHMatrix::Transform(aabb.GetCenter(), SHQuaternion::Identity, aabb.GetExtents() * 2.0f); debugDrawSystem->DrawWireCube(TRS, AABB_COLOUR); } } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index 9940ad20..f0add8f2 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -133,11 +133,11 @@ namespace SHADE case SHCollisionShape::Type::SPHERE: { debugDrawSystem->DrawWireSphere(SHAPE->ComputeWorldTransform(), DRAW_COLOUR, true); - break; } case SHCollisionShape::Type::BOX: { + debugDrawSystem->DrawWireCube(SHAPE->ComputeWorldTransform(), DRAW_COLOUR, true); break; } case SHCollisionShape::Type::CAPSULE: diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index 59ee532a..909aa665 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -132,8 +132,8 @@ namespace YAML { case SHCollisionShape::Type::BOX: { - //const auto* BOX = reinterpret_cast(rhs.GetShape()); - //node[HalfExtents] = BOX->GetRelativeExtents(); + const auto& BOX = dynamic_cast(rhs); + node[HalfExtents] = BOX.GetRelativeExtents(); } break; case SHCollisionShape::Type::SPHERE: @@ -170,7 +170,11 @@ namespace YAML { case SHCollisionShape::Type::BOX: { - + if (node[HalfExtents].IsDefined()) + { + auto* box = dynamic_cast(&rhs); + box->SetRelativeExtents(node[HalfExtents].as()); + } } break; case SHCollisionShape::Type::SPHERE: diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index 3dd1446e..79140d2b 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -137,12 +137,12 @@ namespace SHADE } Vector3 BoxCollider::HalfExtents::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); + //return Convert::ToCLI(getNativeCollisionShape().GetExtents()); return Vector3::Zero; } void BoxCollider::HalfExtents::set(Vector3 value) { - //getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); + //getNativeCollisionShape().SetExtents(Convert::ToNative(value)); } Vector3 BoxCollider::Min::get() { From f49ecdbb14b2d3aa44b51ca99820bf672e80eb3f Mon Sep 17 00:00:00 2001 From: SHAM-DP Date: Tue, 27 Dec 2022 16:23:53 +0800 Subject: [PATCH 042/164] Remove a few redundancies. Audio System now creates handles to audio clips --- .../src/AudioSystem/SHAudioSystem.cpp | 143 +++++++++++------- SHADE_Engine/src/AudioSystem/SHAudioSystem.h | 30 ++-- 2 files changed, 107 insertions(+), 66 deletions(-) diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp index e98d895a..cd04f841 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp @@ -325,27 +325,70 @@ namespace SHADE return std::nullopt; } - AudioClip* SHAudioSystem::CreateAudioClip(const char* path) + Handle SHAudioSystem::CreateAudioClip(const char* path) { - AudioClipID newID{}; - AudioClip* clip = nullptr; - auto it = eventMap.find(path); - if (it != eventMap.end()) + Handle audioClipHandle{}; + + if(auto it = eventMap.find(path); it != eventMap.end()) { - FMOD::Studio::EventInstance* event = nullptr; - it->second->createInstance(&event); - if (event) - { - //event->start(); - newID = clipID; - clipID++; - eventInstances.emplace(newID, AudioClip(newID, event)); - clip = &eventInstances[newID]; - } + audioClipHandle = audioClipLibrary.Create(); + it->second->createInstance(&audioClipHandle->instance); } - return clip; + + return audioClipHandle; } + void SHAudioSystem::AddAudioClipToBGMChannelGroup(Handle handle) + { + if(!handle->instance) + return; + FMOD::ChannelGroup* channelGroup; + handle->instance->getChannelGroup(&channelGroup); + + if(!channelGroup) + { + SHLOG_ERROR("Event instance has no channel group") + return; + } + bgmChannelGroup->addGroup(channelGroup); + } + + void SHAudioSystem::AddAudioClipToSFXChannelGroup(Handle handle) + { + if (!handle->instance) + return; + FMOD::ChannelGroup* channelGroup; + handle->instance->getChannelGroup(&channelGroup); + + if (!channelGroup) + { + SHLOG_ERROR("Event instance has no channel group") + return; + } + sfxChannelGroup->addGroup(channelGroup); + } + + //AudioClip* SHAudioSystem::CreateAudioClip(const char* path) + //{ + // AudioClipID newID{}; + // AudioClip* clip = nullptr; + // auto it = eventMap.find(path); + // if (it != eventMap.end()) + // { + // FMOD::Studio::EventInstance* event = nullptr; + // it->second->createInstance(&event); + // if (event) + // { + // //event->start(); + // newID = clipID; + // clipID++; + // eventInstances.emplace(newID, AudioClip(newID, event)); + // clip = &eventInstances[newID]; + // } + // } + // return clip; + //} + //std::vector SHAudioSystem::GetAllEvents() //{ // int count{}; @@ -489,41 +532,39 @@ namespace SHADE } } - AudioClip::AudioClip(AudioClipID clipID, FMOD::Studio::EventInstance* inst) - :instance(inst), id(clipID) + void AudioClip::Play() { - } - - AudioClip::~AudioClip() - { - } - - void AudioClip::Play(bool isSfx) - { - if (!instance) + if(!instance) return; instance->start(); - auto audioSystem = SHSystemManager::GetSystem(); - instance->setVolume(audioSystem->GetMasterVolume() * (isSfx ? audioSystem->GetSfxVolume() : audioSystem->GetBgmVolume())); } - void AudioClip::Play(SHVec3 position, bool isSfx) - { - if (!instance) - return; - instance->start(); - FMOD_3D_ATTRIBUTES attributes{ {} }; - attributes.forward.z = 1.0f; - attributes.up.y = 1.0f; + //void AudioClip::Play(bool isSfx) + //{ + // if (!instance) + // return; + // instance->start(); + // auto audioSystem = SHSystemManager::GetSystem(); + // instance->setVolume(audioSystem->GetMasterVolume() * (isSfx ? audioSystem->GetSfxVolume() : audioSystem->GetBgmVolume())); + //} - auto audioSystem = SHSystemManager::GetSystem(); - SHVec3 listenerPos = audioSystem->GetListenerPosition(); - attributes.position.x = position[0]; - attributes.position.y = position[1]; - attributes.position.z = listenerPos[2]; - instance->set3DAttributes(&attributes); - instance->setVolume(audioSystem->GetMasterVolume() * (isSfx ? audioSystem->GetSfxVolume() : audioSystem->GetBgmVolume())); - } + //void AudioClip::Play(SHVec3 position, bool isSfx) + //{ + // if (!instance) + // return; + // instance->start(); + // FMOD_3D_ATTRIBUTES attributes{ {} }; + // attributes.forward.z = 1.0f; + // attributes.up.y = 1.0f; + + // auto audioSystem = SHSystemManager::GetSystem(); + // SHVec3 listenerPos = audioSystem->GetListenerPosition(); + // attributes.position.x = position[0]; + // attributes.position.y = position[1]; + // attributes.position.z = listenerPos[2]; + // instance->set3DAttributes(&attributes); + // instance->setVolume(audioSystem->GetMasterVolume() * (isSfx ? audioSystem->GetSfxVolume() : audioSystem->GetBgmVolume())); + //} void AudioClip::Stop(bool fadeOut) { @@ -557,12 +598,12 @@ namespace SHADE instance->setParameterByName(paramName, value); } - void AudioClip::SetParameterLabel(const char* paramName, const char* label) - { - if (!instance) - return; - instance->setParameterByNameWithLabel(paramName, label); - } + //void AudioClip::SetParameterLabel(const char* paramName, const char* label) + //{ + // if (!instance) + // return; + // instance->setParameterByNameWithLabel(paramName, label); + //} float AudioClip::GetParameterValue(const char* paramName) { diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h index 777334e6..08505ea9 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h @@ -13,6 +13,7 @@ #include "Events/SHEvent.h" #include "SH_API.h" +#include #define AUDIO_SYS_MAX_CHANNELS 1024 namespace SHADE @@ -22,27 +23,22 @@ namespace SHADE class SHAudioListenerComponent; - typedef uint64_t AudioClipID; - class AudioClip { public: - AudioClip() = default; - AudioClip(AudioClipID clipID, FMOD::Studio::EventInstance* inst); - ~AudioClip(); - void Play(bool isSfx = true); - void Play(SHVec3 position, bool isSfx = true); + void Play(); + //void Play(SHVec3 position); void Stop(bool fadeOut = true); void SetPause(bool pause); bool IsPaused(); void SetParameter(const char* paramName, float value); - void SetParameterLabel(const char* paramName, const char* label); + //void SetParameterLabel(const char* paramName, const char* label); float GetParameterValue(const char* paramName); friend class SHAudioSystem; private: - FMOD::Studio::EventInstance* instance; - AudioClipID id; + FMOD::Studio::EventInstance* instance = nullptr; + //SHTransformComponent* transformRef; }; class SH_API SHAudioSystem : public SHSystem @@ -62,7 +58,7 @@ namespace SHADE void Exit(); int GetAvailableChannelIndex(); - /*std::vector::size_type CreateSound(const char* filepath, bool loop = false);*/ + void PlaySFX(EntityID id, EntityID eid, const bool& loop, const bool& spatial, float min = 5.0f, float max = 1000.0f); void PlayBGM(EntityID id, EntityID eid, const bool& loop, const bool& spatial, float min = 5.0f, float max = 1000.0f); void PlayEventOnce(const char* path, bool isSFX = true, EntityID eid = MAX_EID, bool spatial = false); @@ -71,8 +67,10 @@ namespace SHADE void StopAllSounds(); std::optional GetEventGUID(const char* path); - AudioClip* CreateAudioClip(const char* path); - //std::vector GetAllEvents(); + //AudioClip* CreateAudioClip(const char* path); + Handle CreateAudioClip(const char* path); + void AddAudioClipToBGMChannelGroup(Handle handle); + void AddAudioClipToSFXChannelGroup(Handle handle); float GetBgmVolume(); float GetSfxVolume(); @@ -84,6 +82,7 @@ namespace SHADE bool GetPaused() const; SHVec3 GetListenerPosition(); void LoadBank(const char* path); + private: FMOD::Studio::System* fmodStudioSystem; FMOD::System* fmodSystem; @@ -95,7 +94,9 @@ namespace SHADE //std::unordered_map bankMap; std::unordered_map bankMap; std::unordered_map eventMap; - std::unordered_map eventInstances; + //std::unordered_map eventInstances; + SHResourceLibrary audioClipLibrary{}; + FMOD::ChannelGroup* bgmChannelGroup, * sfxChannelGroup, * masterGroup; FMOD::Channel* audioChannels[AUDIO_SYS_MAX_CHANNELS]; FMOD_RESULT result; @@ -105,7 +106,6 @@ namespace SHADE SHBank masterBank, stringsBank, musicBank, sfxBank; //To do: change to map of banks loaded by resource manager std::vector* denseListener; - AudioClipID clipID = 0; SHEventHandle onPlay(SHEventPtr onStopEvent); SHEventHandle onStop(SHEventPtr onStopEvent); From b14ddac1e692cb5a1edf4ee80003e5794b54a3c5 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 00:42:10 +0800 Subject: [PATCH 043/164] Added missing serialisation for box colliders --- SHADE_Engine/src/Serialization/SHYAMLConverters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index 909aa665..8584c3fe 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -249,7 +249,7 @@ namespace YAML switch (colliderType) { case SHCollisionShape::Type::SPHERE: collider->AddSphereCollisionShape(1.0f); break; - case SHCollisionShape::Type::BOX: break; + case SHCollisionShape::Type::BOX: collider->AddBoxCollisionShape(SHVec3::One); break; case SHCollisionShape::Type::CAPSULE: break; default:; } From 8ead885d0df7d661643e186289a7bf461fad6810 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 00:44:08 +0800 Subject: [PATCH 044/164] Renamed CollisionShapeFactory to CollisionShapeLibrary --- .../CollisionShapes/SHBoxCollisionShape.h | 2 +- .../CollisionShapes/SHCollisionShape.h | 4 +- ...actory.cpp => SHCollisionShapeLibrary.cpp} | 47 ++++++++++++++++--- ...apeFactory.h => SHCollisionShapeLibrary.h} | 23 +++++---- .../CollisionShapes/SHSphereCollisionShape.h | 2 +- .../src/Physics/Collision/SHCollider.cpp | 40 ++++++++-------- .../src/Physics/Collision/SHCollider.h | 6 +-- .../PhysicsObject/SHPhysicsObjectManager.cpp | 6 ++- .../PhysicsObject/SHPhysicsObjectManager.h | 4 +- 9 files changed, 89 insertions(+), 45 deletions(-) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHCollisionShapeFactory.cpp => SHCollisionShapeLibrary.cpp} (71%) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHCollisionShapeFactory.h => SHCollisionShapeLibrary.h} (81%) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h index 35da5e6e..0cb925f2 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h @@ -49,7 +49,7 @@ namespace SHADE friend class SHCollider; friend class SHCollision; friend class SHCompositeCollider; - friend class SHCollisionShapeFactory; + friend class SHCollisionShapeLibrary; public: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 61f2b43c..6a2e81e4 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -42,7 +42,7 @@ namespace SHADE friend class SHCollider; friend class SHColliderComponent; - friend class SHCollisionShapeFactory; + friend class SHCollisionShapeLibrary; friend class SHCollisionSpace; friend struct SHManifold; @@ -65,7 +65,7 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHCollisionShape (SHCollisionShapeID id, Type colliderType = Type::BOX); + SHCollisionShape (SHCollisionShapeID id, Type colliderType = Type::SPHERE); SHCollisionShape (const SHCollisionShape& rhs) noexcept = default; SHCollisionShape (SHCollisionShape&& rhs) noexcept = default; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp similarity index 71% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp index 97409cf0..6d2fdc06 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHCollisionShapeFactory.cpp + * \file SHCollisionShapeLibrary.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Implementation for a Collison Shape Factory Class. * @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHCollisionShapeFactory.h" +#include "SHCollisionShapeLibrary.h" namespace SHADE { @@ -19,7 +19,12 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollisionShapeFactory::~SHCollisionShapeFactory() noexcept + SHCollisionShapeLibrary::SHCollisionShapeLibrary() noexcept + { + createBoxPolyhedron(); + } + + SHCollisionShapeLibrary::~SHCollisionShapeLibrary() noexcept { // Free all shapes in each container for (auto* sphereCollisionShape : spheres | std::views::values) @@ -34,7 +39,7 @@ namespace SHADE /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHSphereCollisionShape* SHCollisionShapeFactory::CreateSphere(SHCollisionShapeID id, const SHSphereCreateInfo& createInfo) + SHSphereCollisionShape* SHCollisionShapeLibrary::CreateSphere(SHCollisionShapeID id, const SHSphereCreateInfo& createInfo) { const auto RESULT = spheres.emplace(id, new SHSphereCollisionShape{ id }); if (RESULT.second) @@ -52,7 +57,7 @@ namespace SHADE return spheres.find(id)->second; } - SHBoxCollisionShape* SHCollisionShapeFactory::CreateBox(SHCollisionShapeID id, const SHBoxCreateInfo& createInfo) + SHBoxCollisionShape* SHCollisionShapeLibrary::CreateBox(SHCollisionShapeID id, const SHBoxCreateInfo& createInfo) { const auto RESULT = boxes.emplace(id, new SHBoxCollisionShape{ id }); if (RESULT.second) @@ -72,7 +77,7 @@ namespace SHADE } - void SHCollisionShapeFactory::DestroyShape(SHCollisionShape* shape) + void SHCollisionShapeLibrary::DestroyShape(SHCollisionShape* shape) { switch (shape->GetType()) { @@ -97,4 +102,34 @@ namespace SHADE default: break; } } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionShapeLibrary::createBoxPolyhedron() noexcept + { + /* + * Vertices (Front/Back Face): + * + * 3/7 ---------- 2/6 + * | | + * | | + * | | + * 0/4 ---------- 1/5 + * + * Faces: + * + * Front: 0 (0,1,2,3) + * Right: 1 (1,5,6,2) + * Back: 2 (5,4,7,6) + * Left: 3 (4,0,3,7) + * Top: 4 (3,2,6,7) + * Bottom: 5 (4,5,1,0) + * + */ + + // Create face data + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h similarity index 81% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h index c82d7e26..e5958f96 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCollisionShapeFactory.h + * \file SHCollisionShapeLibrary.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collison Shape Factory Class. + * \brief Interface for a Collison Shape Library. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -24,19 +24,18 @@ namespace SHADE /** * @brief - * Encapsulates a class for Creating and Destroying Collision Shapes.
- * All memory for collision shapes are handled in this factory class.
- * TODO: Support instancing of shapes + * Encapsulates a class for Creating, Storing and Destroying Collision Shapes.
+ * All memory for collision shapes are stored in this factory class.
*/ - class SH_API SHCollisionShapeFactory final + class SH_API SHCollisionShapeLibrary final { public: /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHCollisionShapeFactory () noexcept = default; - ~SHCollisionShapeFactory () noexcept; + SHCollisionShapeLibrary () noexcept; + ~SHCollisionShapeLibrary () noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ @@ -92,10 +91,18 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ + + SHConvexPolyhedron boxPolyhedron; Spheres spheres; Boxes boxes; // TODO: Add capsules and hulls + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + void createBoxPolyhedron() noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index a1ebf6a3..75492505 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -48,7 +48,7 @@ namespace SHADE friend class SHCollider; friend class SHCollision; friend class SHCompositeCollider; - friend class SHCollisionShapeFactory; + friend class SHCollisionShapeLibrary; public: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp index 4ce819ac..021605c7 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -33,7 +33,7 @@ namespace SHADE , debugDraw { false } , hasMoved { true } , rigidBody { nullptr } - , shapeFactory { nullptr } + , shapeLibrary { nullptr } , broadphase { nullptr } , transform { worldTransform } {} @@ -44,11 +44,11 @@ namespace SHADE , debugDraw { rhs.debugDraw } , hasMoved { rhs.hasMoved } , rigidBody { rhs.rigidBody } - , shapeFactory { rhs.shapeFactory } + , shapeLibrary { rhs.shapeLibrary } , broadphase { rhs.broadphase } , transform { rhs.transform } { - if (!shapeFactory) + if (!shapeLibrary) { SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) return; @@ -63,11 +63,11 @@ namespace SHADE , debugDraw { rhs.debugDraw } , hasMoved { rhs.hasMoved } , rigidBody { rhs.rigidBody } - , shapeFactory { rhs.shapeFactory } + , shapeLibrary { rhs.shapeLibrary } , broadphase { rhs.broadphase } , transform { rhs.transform } { - if (!shapeFactory) + if (!shapeLibrary) { SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) return; @@ -78,14 +78,14 @@ namespace SHADE SHCollider::~SHCollider() noexcept { - if (!shapeFactory) + if (!shapeLibrary) { SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add destroy collider!", entityID) return; } for (auto* shape : shapes) - shapeFactory->DestroyShape(shape); + shapeLibrary->DestroyShape(shape); } /*-----------------------------------------------------------------------------------*/ @@ -97,7 +97,7 @@ namespace SHADE if (this == &rhs) return *this; - if (!shapeFactory) + if (!shapeLibrary) { SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) return *this; @@ -108,7 +108,7 @@ namespace SHADE debugDraw = rhs.debugDraw; hasMoved = rhs.hasMoved; rigidBody = rhs.rigidBody; - shapeFactory = rhs.shapeFactory; + shapeLibrary = rhs.shapeLibrary; broadphase = rhs.broadphase; transform = rhs.transform; @@ -119,7 +119,7 @@ namespace SHADE SHCollider& SHCollider::operator=(SHCollider&& rhs) noexcept { - if (!shapeFactory) + if (!shapeLibrary) { SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) return *this; @@ -130,7 +130,7 @@ namespace SHADE debugDraw = rhs.debugDraw; hasMoved = rhs.hasMoved; rigidBody = rhs.rigidBody; - shapeFactory = rhs.shapeFactory; + shapeLibrary = rhs.shapeLibrary; broadphase = rhs.broadphase; transform = rhs.transform; @@ -263,9 +263,9 @@ namespace SHADE transform.scale = newScale; } - void SHCollider::SetFactory(SHCollisionShapeFactory* factory) noexcept + void SHCollider::SetFactory(SHCollisionShapeLibrary* factory) noexcept { - shapeFactory = factory; + shapeLibrary = factory; } /*-----------------------------------------------------------------------------------*/ @@ -279,7 +279,7 @@ namespace SHADE int SHCollider::AddSphereCollisionShape(float relativeRadius, const SHVec3& posOffset, const SHVec3& rotOffset) { - if (!shapeFactory) + if (!shapeLibrary) { SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add new shape!", entityID) return -1; @@ -304,7 +304,7 @@ namespace SHADE const uint32_t NEW_INDEX = static_cast(shapes.size()); const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); + SHSphereCollisionShape* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); // Set offsets sphere->collider = this; @@ -334,7 +334,7 @@ namespace SHADE int SHCollider::AddBoxCollisionShape(const SHVec3& relativeExtents, const SHVec3& posOffset, const SHVec3& rotOffset) { - if (!shapeFactory) + if (!shapeLibrary) { SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add new shape!", entityID) return -1; @@ -357,7 +357,7 @@ namespace SHADE const uint32_t NEW_INDEX = static_cast(shapes.size()); const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - SHBoxCollisionShape* box = shapeFactory->CreateBox(NEW_SHAPE_ID, BOX_CREATE_INFO); + SHBoxCollisionShape* box = shapeLibrary->CreateBox(NEW_SHAPE_ID, BOX_CREATE_INFO); // Set offsets box->collider = this; @@ -388,7 +388,7 @@ namespace SHADE void SHCollider::RemoveCollisionShape(int index) { - if (!shapeFactory) + if (!shapeLibrary) { SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add remove shape!", entityID) return; @@ -417,7 +417,7 @@ namespace SHADE if (broadphase) broadphase->Remove((*shape)->id); - shapeFactory->DestroyShape(*shape); + shapeLibrary->DestroyShape(*shape); *shape = nullptr; // Remove the shape from the container to prevent accessing a nullptr @@ -473,7 +473,7 @@ namespace SHADE const uint32_t NEW_INDEX = static_cast(shapes.size()); const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); + SHSphereCollisionShape* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); *sphere = *RHS_SPHERE; shapes.emplace_back(sphere); diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h index 3d80118e..c4a4ad17 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -13,7 +13,7 @@ // Project Headers #include "ECS_Base/Entity/SHEntity.h" #include "Math/Transform/SHTransform.h" -#include "Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h" +#include "Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h" namespace SHADE { @@ -106,7 +106,7 @@ namespace SHADE void SetOrientation (const SHQuaternion& newOrientation) noexcept; void SetScale (const SHVec3& newScale) noexcept; - void SetFactory (SHCollisionShapeFactory* factory) noexcept; + void SetFactory (SHCollisionShapeLibrary* factory) noexcept; /*---------------------------------------------------------------------------------*/ /* Member Functions */ @@ -181,7 +181,7 @@ namespace SHADE bool hasMoved; SHRigidBody* rigidBody; - SHCollisionShapeFactory* shapeFactory; + SHCollisionShapeLibrary* shapeLibrary; SHAABBTree* broadphase; SHTransform transform; diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp index 61a77f28..81fb25e1 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp @@ -67,15 +67,17 @@ namespace SHADE auto* rigidBody = physicsObject->CreateRigidBody(RIGID_BODY_TYPE); SHVec3 worldPos = SHVec3::Zero; - // TODO: Force orientation + SHQuaternion worldRot = SHQuaternion::Identity; if (const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); TRANSFORM_COMPONENT) { worldPos = TRANSFORM_COMPONENT->GetWorldPosition(); + worldRot = TRANSFORM_COMPONENT->GetWorldOrientation(); } SHMotionState& motionState = rigidBody->GetMotionState(); motionState.ForcePosition(worldPos); + motionState.ForceOrientation(worldRot); // Link with the component rigidBodyComponent->SetRigidBody(rigidBody); @@ -115,7 +117,7 @@ namespace SHADE // Create a new composite collider in the physics object physicsObject->CreateCollider(worldTransform); - physicsObject->collider->SetFactory(&shapeFactory); + physicsObject->collider->SetFactory(&shapeLibrary); // Link with the component colliderComponent->SetCollider(physicsObject->collider); diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h index ca260a02..11818316 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h @@ -14,7 +14,7 @@ // Project Headers #include "SHPhysicsObject.h" -#include "Physics/Collision/CollisionShapes/SHCollisionShapeFactory.h" +#include "Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h" namespace SHADE { @@ -100,7 +100,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ EntityObjectMap physicsObjects; - SHCollisionShapeFactory shapeFactory; + SHCollisionShapeLibrary shapeLibrary; /*-----------------------------------------------------------------------------------*/ /* Member Functions */ From ea1dd57996bf684e8d954feebb3161079a03efd2 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 00:45:01 +0800 Subject: [PATCH 045/164] Added stub functions for collision detection algorithms --- .../Narrowphase/SHCapsuleVsCapsule.cpp | 35 +++++++++++ .../Narrowphase/SHCapsuleVsConvex.cpp | 49 +++++++++++++++ .../Collision/Narrowphase/SHCollision.h | 47 +++++++++++++- .../Narrowphase/SHCollisionDispatch.cpp | 14 ++--- .../Narrowphase/SHConvexVsConvex.cpp | 62 +++++++++++++++++++ .../Narrowphase/SHSphereVsCapsule.cpp | 48 ++++++++++++++ .../Narrowphase/SHSphereVsConvex.cpp | 58 +++++++++++++++++ 7 files changed, 302 insertions(+), 11 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsCapsule.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsCapsule.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsCapsule.cpp new file mode 100644 index 00000000..c75437cb --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsCapsule.cpp @@ -0,0 +1,35 @@ +/**************************************************************************************** + * \file SHCapsuleVsCapsule.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Detecting Collisions between two capsules + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollision.h" + +// Project Headers +#include "Math/SHMathHelpers.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollision::CapsuleVsCapsule(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return false; + } + + bool SHCollision::CapsuleVsCapsule(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return false; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp new file mode 100644 index 00000000..1b03feab --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp @@ -0,0 +1,49 @@ +/**************************************************************************************** + * \file SHCapsuleVsConvex.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Detecting Collisions between a capsule and a convex + * polyhedron. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollision.h" + +// Project Headers +#include "Math/SHMathHelpers.h" +#include "Physics/Collision/CollisionShapes/SHBoxCollisionShape.h" + +// TODO + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollision::CapsuleVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return false; + } + + bool SHCollision::ConvexVsCapsule(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return CapsuleVsConvex(B, A); + } + + bool SHCollision::CapsuleVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return false; + } + + bool SHCollision::ConvexVsCapsule(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return CapsuleVsConvex(manifold, B, A); + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index a64c101c..b99cc202 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -35,18 +35,59 @@ namespace SHADE /* Spheres VS X */ - [[nodiscard]] static bool SphereVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool SphereVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - + [[nodiscard]] static bool SphereVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool SphereVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + [[nodiscard]] static bool SphereVsCapsule (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool SphereVsCapsule (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + [[nodiscard]] static bool SphereVsConvex (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool SphereVsConvex (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; /* Capsule VS X */ + [[nodiscard]] static bool CapsuleVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool CapsuleVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + [[nodiscard]] static bool CapsuleVsCapsule (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool CapsuleVsCapsule (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + [[nodiscard]] static bool CapsuleVsConvex (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool CapsuleVsConvex (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + /* Polygon VS X */ + [[nodiscard]] static bool ConvexVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool ConvexVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + [[nodiscard]] static bool ConvexVsCapsule (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool ConvexVsCapsule (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + + [[nodiscard]] static bool ConvexVsConvex (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + [[nodiscard]] static bool ConvexVsConvex (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + private: /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ + static bool isMinkowskiFace(const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; + + // TODO: buildMinkowskiFace, queryEdgeDirection, distanceBetweenTwoEdges, + + /* + * TODO: + * static FaceQuery queryFaceDirections (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + * static EdgeQuery queryEdgeDirections (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; + * static bool buildMinkowskiFace (const SHHalfEdge& edgeA, const SHHalfEdge& edgeB) noexcept; + * static float distanceBetweenEdges(const SHHalfEdge& edgeA, const SHHalfEdge& edgeB, SHCollisionShape& poly) noexcept; + * static uint32_t clip + * + * ! References + * https://ia801303.us.archive.org/30/items/GDC2013Gregorius/GDC2013-Gregorius.pdf + * https://github.com/RandyGaul/qu3e/blob/master/src/collision/q3Collide.cpp + */ + + }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp index c06e30ae..4a949d77 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp @@ -26,22 +26,20 @@ namespace SHADE const SHCollisionDispatcher::ManifoldCollide SHCollisionDispatcher::manifoldCollide[NUM_SHAPES][NUM_SHAPES] { - // TODO // vs Sphere / Box / Capsule - { SHCollision::SphereVsSphere, nullptr, nullptr } // Sphere - , { nullptr, nullptr, nullptr } // Box - , { nullptr, nullptr, nullptr } // Capsule + { SHCollision::SphereVsSphere, SHCollision::SphereVsConvex, SHCollision::SphereVsCapsule } // Sphere + , { SHCollision::ConvexVsSphere, SHCollision::ConvexVsConvex, SHCollision::ConvexVsCapsule } // Box + , { SHCollision::CapsuleVsSphere, SHCollision::CapsuleVsConvex, SHCollision::CapsuleVsCapsule } // Capsule }; const SHCollisionDispatcher::TriggerCollide SHCollisionDispatcher::triggerCollide[NUM_SHAPES][NUM_SHAPES] { - // TODO // vs Sphere / Box / Capsule - { SHCollision::SphereVsSphere, nullptr, nullptr } // Sphere - , { nullptr, nullptr, nullptr } // Box - , { nullptr, nullptr, nullptr } // Capsule + { SHCollision::SphereVsSphere, SHCollision::SphereVsConvex, SHCollision::SphereVsCapsule } // Sphere + , { SHCollision::ConvexVsSphere, SHCollision::ConvexVsConvex, SHCollision::ConvexVsCapsule } // Box + , { SHCollision::CapsuleVsSphere, SHCollision::CapsuleVsConvex, SHCollision::CapsuleVsCapsule } // Capsule }; const bool SHCollisionDispatcher::collisionTable[NUM_TYPES][NUM_TYPES] diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp new file mode 100644 index 00000000..d73097bb --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -0,0 +1,62 @@ +/**************************************************************************************** + * \file SHConvexVsConvex.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Detecting Collisions between two convex polyhedrons. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollision.h" + +// Project Headers +#include "Math/SHMathHelpers.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollision::ConvexVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + /* + * TODO: + * + * 1. Query face directiosn of a to b. Exit early if separation found. + * 2. query face directions of b to a. Exit early if separation found. + * 3. Query edge directions of a & b. Exit early if separation found. + */ + + return false; + } + + bool SHCollision::ConvexVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + /* + * TODO: + * + * 1. Query face directions of a to b. Exit early if separation found. + * 2. Query face directions of b to a. Exit early if separation found. + * 3. Query edge directions of a & b. Exit early if separation found. + * + * (*)!! Apply weight to improve frame coherence of normal directions. DONT FORGET FLIP FLOP! + * 4. From above, save the axis of minimum penetration (reference face) + * 5. Find the most anti-parallel face on other shape (incident face) + * 6. Clip incident face against side planes of reference face. (Sutherland-Hodgeman Clipping). + * Keep all vertices below reference face. + * 7. Reduce manifold to 4 contact points. We only need 4 contact points for a stable manifold. + * + * Remember to save IDs in queries. + * During generation of incident face, store IDs of face. + * + */ + + return false; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp new file mode 100644 index 00000000..3e308aed --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp @@ -0,0 +1,48 @@ +/**************************************************************************************** + * \file SHSphereVsCapsule.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Detecting Collisions between a sphere and a capsule. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollision.h" + +// Project Headers +#include "Math/SHMathHelpers.h" +#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h" + +// TODO + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollision::SphereVsCapsule(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return false; + } + + bool SHCollision::CapsuleVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return SphereVsCapsule(B, A); + } + + bool SHCollision::SphereVsCapsule(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return false; + } + + bool SHCollision::CapsuleVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return SphereVsCapsule(manifold, B, A); + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp new file mode 100644 index 00000000..a8a9cd48 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -0,0 +1,58 @@ +/**************************************************************************************** + * \file SHSphereVsConvex.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Detecting Collisions between a sphere and a convex + * polyhedron. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollision.h" + +// Project Headers +#include "Math/SHMathHelpers.h" +#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" +#include "Physics/Collision/CollisionShapes/SHBoxCollisionShape.h" + +// When testing against convex polyhedrons, we do not care so much as whether it is a box +// or something else. We only need the vertices to build half edge structures for use +// with a gauss map. Regardless, we still cast it to the type just to get vertices +// since spheres and capsules do not implement the same method. + +// I did consider having another base class that encapsulates methods that convex polyhedrons +// would implement, but for the sake of my sanity, I won't do that. + +// TODO + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Public Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollision::SphereVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return false; + } + + bool SHCollision::ConvexVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return SphereVsConvex(B, A); + } + + bool SHCollision::SphereVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return false; + } + + bool SHCollision::ConvexVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return SphereVsConvex(manifold, B, A); + } + +} // namespace SHADE \ No newline at end of file From 400cbb35d9b43685f02a79c651b89e4be4583f11 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 00:45:37 +0800 Subject: [PATCH 046/164] Partial implementation of a generic convex polyhedron object --- Assets/Scenes/PhysicsSandbox.shade | 1 + .../CollisionShapes/SHBoxCollisionShape.cpp | 18 ++ .../CollisionShapes/SHBoxCollisionShape.h | 24 +- .../CollisionShapes/SHConvexPolyhedron.cpp | 216 ++++++++++++++++++ .../CollisionShapes/SHConvexPolyhedron.h | 144 ++++++++++++ 5 files changed, 392 insertions(+), 11 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index e001867a..67d35bb5 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -131,6 +131,7 @@ Colliders: - Is Trigger: false Type: Box + Half Extents: {x: 1, y: 1, z: 1} Friction: 0.400000006 Bounciness: 0 Density: 1 diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp index 67e497a8..6d7308da 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp @@ -18,6 +18,19 @@ #include "Math/SHMatrix.h" #include "Physics/Collision/SHCollider.h" +/* + * Local box vertices, faces & half-edges + * + * Vertices (Front/Back Face): + * + * 3/7 ---------- 2/6 + * | | + * | | + * | | + * 0/4 ---------- 1/5 + * + */ + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -29,6 +42,7 @@ namespace SHADE , SHBox () , relativeExtents { SHVec3::One } , scale { SHVec3::One } + , polyhedron { nullptr } {} SHBoxCollisionShape::SHBoxCollisionShape(const SHBoxCollisionShape& rhs) noexcept @@ -36,6 +50,7 @@ namespace SHADE , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) , relativeExtents { rhs.relativeExtents } , scale { rhs.scale } + , polyhedron { rhs.polyhedron } { material = rhs.material; @@ -52,6 +67,7 @@ namespace SHADE , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) , relativeExtents { rhs.relativeExtents } , scale { rhs.scale } + , polyhedron { rhs.polyhedron } { material = rhs.material; @@ -93,6 +109,7 @@ namespace SHADE relativeExtents = rhs.relativeExtents; scale = rhs.scale; + polyhedron = rhs.polyhedron; return *this; } @@ -120,6 +137,7 @@ namespace SHADE relativeExtents = rhs.relativeExtents; scale = rhs.scale; + polyhedron = rhs.polyhedron; return *this; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h index 0cb925f2..142858e0 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h @@ -13,6 +13,7 @@ // Project Headers #include "Math/Geometry/SHBox.h" #include "SHCollisionShape.h" +#include "SHConvexPolyhedron.h" namespace SHADE { @@ -73,20 +74,20 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; - [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; - [[nodiscard]] SHQuaternion GetOrientation () const noexcept; + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; + [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; + [[nodiscard]] SHQuaternion GetOrientation () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetCenter (const SHVec3& newCenter) noexcept; - void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; - void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; - void SetOrientation (const SHQuaternion& newOrientation) noexcept; - void SetScale (const SHVec3& newScale) noexcept; + void SetCenter (const SHVec3& newCenter) noexcept; + void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; + void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; + void SetOrientation (const SHQuaternion& newOrientation) noexcept; + void SetScale (const SHVec3& newScale) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ @@ -150,8 +151,9 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHVec3 relativeExtents; - SHVec3 scale; // Intended to be passed in by the base collider. + SHVec3 relativeExtents; + SHVec3 scale; // Intended to be passed in by the base collider. + SHConvexPolyhedron* polyhedron; // Defines the polyhedron by it's half edges. }; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp new file mode 100644 index 00000000..cb5fad76 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp @@ -0,0 +1,216 @@ +/**************************************************************************************** + * \file SHConvexPolyhedron.cpps + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a convex polyhedron structure. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHConvexPolyhedron.h" + +// Helper Macros + +#define BUILD_UINT64_FROM_UINT32S(a, b) (uint64_t)a << 32 | b + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHConvexPolyhedron::HalfEdge::HalfEdge() noexcept + : tailVertexIndex { -1 } + , headVertexIndex { -1 } + , edgeIndex { -1 } + , twinEdgeIndex { -1 } + , faceIndex { -1 } + {} + + SHConvexPolyhedron::HalfEdge::HalfEdge(const HalfEdge& rhs) noexcept + : tailVertexIndex { rhs.tailVertexIndex } + , headVertexIndex { rhs.headVertexIndex } + , edgeIndex { rhs.edgeIndex } + , twinEdgeIndex { rhs.twinEdgeIndex } + , faceIndex { rhs.faceIndex } + {} + + SHConvexPolyhedron::HalfEdge::HalfEdge(HalfEdge&& rhs) noexcept + : tailVertexIndex { rhs.tailVertexIndex } + , headVertexIndex { rhs.headVertexIndex } + , edgeIndex { rhs.edgeIndex } + , twinEdgeIndex { rhs.twinEdgeIndex } + , faceIndex { rhs.faceIndex } + {} + + SHConvexPolyhedron::Face::Face(const Face& rhs) noexcept + : normal { rhs.normal } + { + std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); + } + + SHConvexPolyhedron::Face::Face(Face&& rhs) noexcept + : normal { rhs.normal } + { + std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::HalfEdge::operator=(const HalfEdge& rhs) noexcept + { + if (this == &rhs) + return *this; + + tailVertexIndex = rhs.tailVertexIndex; + headVertexIndex = rhs.headVertexIndex; + edgeIndex = rhs.edgeIndex ; + twinEdgeIndex = rhs.twinEdgeIndex ; + faceIndex = rhs.faceIndex ; + + return *this; + } + + SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::HalfEdge::operator=(HalfEdge&& rhs) noexcept + { + tailVertexIndex = rhs.tailVertexIndex; + headVertexIndex = rhs.headVertexIndex; + edgeIndex = rhs.edgeIndex ; + twinEdgeIndex = rhs.twinEdgeIndex ; + faceIndex = rhs.faceIndex ; + + return *this; + } + + SHConvexPolyhedron::Face& SHConvexPolyhedron::Face::operator=(const Face& rhs) noexcept + { + if (this == &rhs) + return *this; + + normal = rhs.normal; + + vertexIndices.clear(); + std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); + + return *this; + } + + SHConvexPolyhedron::Face& SHConvexPolyhedron::Face::operator=(Face&& rhs) noexcept + { + normal = rhs.normal; + + vertexIndices.clear(); + std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + int32_t SHConvexPolyhedron::GetFaceCount() const noexcept + { + return static_cast(faces.size()); + } + + int32_t SHConvexPolyhedron::GetHalfEdgeCount() const noexcept + { + return static_cast(halfEdges.size()); + } + + const SHConvexPolyhedron::Face& SHConvexPolyhedron::GetFace(int32_t index) const + { + if (index < 0 || index >= static_cast(faces.size())) + throw std::invalid_argument("Index out-of-range!"); + + return faces[index]; + } + + const SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::GetHalfEdge(int32_t index) const + { + if (index < 0 || index >= static_cast(halfEdges.size())) + throw std::invalid_argument("Index out-of-range!"); + + return halfEdges[index]; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHConvexPolyhedron::AddFace(const Face& face) + { + faces.emplace_back(face); + } + + void SHConvexPolyhedron::BuildPolyhedron() noexcept + { + // We use the pair of vertex IDs on a half-edge to prevent duplicates + std::unordered_map edgeMap; + edgeMap.clear(); + + if (faces.empty()) + { + SHLOGV_CRITICAL("Unable to build convex polyhedron, no faces have been added!") + return; + } + + // For each face, build half edges + for (size_t i = 0; i < faces.size(); ++i) + { + const Face& FACE = faces[i]; + + // Iterate through vertices and build half-edges + for (size_t j = 0; j < FACE.vertexIndices.size(); ++j) + { + const int32_t TAIL = FACE.vertexIndices[j]; + const int32_t HEAD = (TAIL + 1) > FACE.vertexIndices.back() ? FACE.vertexIndices.front() : TAIL + 1; // Wrap around + + const uint64_t NEW_EDGE_ID = BUILD_UINT64_FROM_UINT32S(TAIL, HEAD); + const uint64_t TWIN_EDGE_ID = BUILD_UINT64_FROM_UINT32S(HEAD, TAIL); + + // Check if the half-edge has already been inserted + auto newEdgeIter = edgeMap.find(NEW_EDGE_ID); + if (newEdgeIter == edgeMap.end()) + { + // Reuse the iterator for mapping with the twin + newEdgeIter = edgeMap.emplace(NEW_EDGE_ID, HalfEdge{}).first; + + HalfEdge& newHalfEdge = newEdgeIter->second; + newHalfEdge.tailVertexIndex = TAIL; + newHalfEdge.headVertexIndex = HEAD; + newHalfEdge.faceIndex = static_cast(i); + + // Set edge index of the newly inserted edge as the size of the map - 1 + // Since it is an unordered map, it will just be at the back + newHalfEdge.edgeIndex = static_cast(edgeMap.size()) - 1; + } + + // Find twin edge if one exists + auto twinEdgeIter = edgeMap.find(TWIN_EDGE_ID); + if (twinEdgeIter != edgeMap.end()) + { + // Set the twin index of both the edges + HalfEdge& newHalfEdge = newEdgeIter->second; + HalfEdge& twinHalfEdge = twinEdgeIter->second; + + newHalfEdge.twinEdgeIndex = twinHalfEdge.edgeIndex; + twinHalfEdge.twinEdgeIndex = newHalfEdge.edgeIndex; + } + } + } + + // Copy all half edges into the vector + // At this point, no duplicates should be in the map and all edges should be linked. + for (auto& halfEdge : edgeMap | std::views::values) + halfEdges.emplace_back(halfEdge); + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h new file mode 100644 index 00000000..762368a3 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h @@ -0,0 +1,144 @@ +/**************************************************************************************** + * \file SHConvexPolyhedron.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a convex polyhedron structure. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "Math/Vector/SHVec3.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates data for a convex polyhedron's geometry represented as faces & half edges. + */ + class SH_API SHConvexPolyhedron + { + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + struct HalfEdge + { + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + //Head and tail forms the edge. + //Head <----- Tail + int32_t tailVertexIndex; + + // Head is also tail of the next edge. + int32_t headVertexIndex; + + int32_t edgeIndex; + // Other half of the edge on a different face. + // Important for extrapolating face normals. + int32_t twinEdgeIndex; + + // Adjacent face of this edge. + int32_t faceIndex; + + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + HalfEdge () noexcept; + HalfEdge (const HalfEdge& rhs) noexcept; + HalfEdge (HalfEdge&& rhs) noexcept; + ~HalfEdge () noexcept = default; + + /*-------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-------------------------------------------------------------------------------*/ + + HalfEdge& operator= (const HalfEdge& rhs) noexcept; + HalfEdge& operator= (HalfEdge&& rhs) noexcept; + }; + + struct Face + { + public: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + SHVec3 normal; + std::vector vertexIndices; // Must be in CCW order + + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + Face () noexcept = default; + Face (const Face& rhs) noexcept; + Face (Face&& rhs) noexcept; + ~Face () noexcept = default; + + /*-------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-------------------------------------------------------------------------------*/ + + Face& operator= (const Face& rhs) noexcept; + Face& operator= (Face&& rhs) noexcept; + }; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] int32_t GetFaceCount () const noexcept; + [[nodiscard]] int32_t GetHalfEdgeCount () const noexcept; + + [[nodiscard]] const Face& GetFace (int32_t index) const; + [[nodiscard]] const HalfEdge& GetHalfEdge (int32_t index) const; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Adds a face to the polyhedron. The face must be constructed outside the polyhedron. + * @param face + * The face to insert. + */ + void AddFace (const Face& face); + + /** + * @brief + * Builds the half-edges of the polyhedron using the faces.
+ * Before this method is invoked, there must be some faces. + * @return + */ + void BuildPolyhedron() noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + float radius = 0.2f; // Default Radius is 2 cm + + // Store the faces and half-edges + + std::vector faces; + std::vector halfEdges; + + }; + +} // namespace SHADE From fba338eaef197eb2e0abeae46de86a52cb9ed62f Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 01:14:40 +0800 Subject: [PATCH 047/164] Fixed half edge builder and built box polyhedron --- .../SHCollisionShapeLibrary.cpp | 51 ++++++++++++++++--- .../CollisionShapes/SHConvexPolyhedron.cpp | 8 ++- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp index 6d2fdc06..ad5e3c44 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp @@ -70,6 +70,9 @@ namespace SHADE box->Orientation = createInfo.Orientation; box->scale = createInfo.Scale; + // Set convex polyhedron for the box + box->polyhedron = &boxPolyhedron; + return box; } @@ -109,6 +112,9 @@ namespace SHADE void SHCollisionShapeLibrary::createBoxPolyhedron() noexcept { + static constexpr int NUM_VERTICES_PER_FACE = 4; + static constexpr int NUM_FACES = 6; + /* * Vertices (Front/Back Face): * @@ -120,16 +126,49 @@ namespace SHADE * * Faces: * - * Front: 0 (0,1,2,3) - * Right: 1 (1,5,6,2) - * Back: 2 (5,4,7,6) - * Left: 3 (4,0,3,7) - * Top: 4 (3,2,6,7) - * Bottom: 5 (4,5,1,0) + * Front: 0 (0,1,2,3) Normal: Z + * Right: 1 (1,5,6,2) Normal: X + * Back: 2 (5,4,7,6) Normal: -Z + * Left: 3 (4,0,3,7) Normal: -X + * Top: 4 (3,2,6,7) Normal: Y + * Bottom: 5 (4,5,1,0) Normal: -Y * */ // Create face data + + const SHVec3 FACE_NORMALS[NUM_FACES] + { + -SHVec3::UnitZ + , SHVec3::UnitX + , SHVec3::UnitZ + , -SHVec3::UnitX + , SHVec3::UnitY + , -SHVec3::UnitY + }; + + const int32_t FACE_VERTICES[NUM_FACES][NUM_VERTICES_PER_FACE] + { + { 0, 1, 2, 3 } + , { 1, 5, 6, 2 } + , { 5, 4, 7, 6 } + , { 4, 0, 3, 7 } + , { 3, 2, 6, 7 } + , { 4, 5, 1, 0 } + }; + + for (int i = 0; i < NUM_FACES; ++i) + { + SHConvexPolyhedron::Face newFace; + newFace.normal = FACE_NORMALS[i]; + + for (int j = 0; j < NUM_VERTICES_PER_FACE; ++j) + newFace.vertexIndices.emplace_back(FACE_VERTICES[i][j]); + + boxPolyhedron.AddFace(newFace); + } + + boxPolyhedron.BuildPolyhedron(); } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp index cb5fad76..ca049ebb 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp @@ -167,11 +167,17 @@ namespace SHADE { const Face& FACE = faces[i]; + if (FACE.vertexIndices.empty()) + { + SHLOGV_CRITICAL("Unable to build convex polyhedron, no vertices have been added to face {}!", i) + return; + } + // Iterate through vertices and build half-edges for (size_t j = 0; j < FACE.vertexIndices.size(); ++j) { const int32_t TAIL = FACE.vertexIndices[j]; - const int32_t HEAD = (TAIL + 1) > FACE.vertexIndices.back() ? FACE.vertexIndices.front() : TAIL + 1; // Wrap around + const int32_t HEAD = j + 1 < FACE.vertexIndices.size() ? FACE.vertexIndices[j + 1] : FACE.vertexIndices[0]; const uint64_t NEW_EDGE_ID = BUILD_UINT64_FROM_UINT32S(TAIL, HEAD); const uint64_t TWIN_EDGE_ID = BUILD_UINT64_FROM_UINT32S(HEAD, TAIL); From 6bab419428fa6ef5d16c890882210156a153c21e Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 01:23:14 +0800 Subject: [PATCH 048/164] Fixed collision tag bugs --- .../Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp | 2 +- .../src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp | 2 +- SHADE_Engine/src/Serialization/SHYAMLConverters.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp index 8169aa5c..9a04b812 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp @@ -1,7 +1,7 @@ #include "SHpch.h" #include "SHColliderTagPanel.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Editor/SHEditorWidgets.hpp" namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 3f7c36b7..6117dfe8 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -18,7 +18,7 @@ #include "Physics/Interface/SHColliderComponent.h" #include "Reflection/SHReflectionMetadata.h" #include "Resource/SHResourceManager.h" -#include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" namespace SHADE { template diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index ed25104a..9a76a87f 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -12,7 +12,7 @@ #include "Physics/Interface/SHColliderComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHFont.h" -#include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" namespace YAML { From 7b1b4873ec52a986ac47440810ea169d4afb71fd Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 01:27:31 +0800 Subject: [PATCH 049/164] dumb dumb energy --- .../Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp index ca049ebb..0291de15 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp @@ -177,7 +177,7 @@ namespace SHADE for (size_t j = 0; j < FACE.vertexIndices.size(); ++j) { const int32_t TAIL = FACE.vertexIndices[j]; - const int32_t HEAD = j + 1 < FACE.vertexIndices.size() ? FACE.vertexIndices[j + 1] : FACE.vertexIndices[0]; + const int32_t HEAD = FACE.vertexIndices[(j + 1) % FACE.vertexIndices.size()]; const uint64_t NEW_EDGE_ID = BUILD_UINT64_FROM_UINT32S(TAIL, HEAD); const uint64_t TWIN_EDGE_ID = BUILD_UINT64_FROM_UINT32S(HEAD, TAIL); From 50e3ddf0dd5d77681141a582b1a4fcf5b2549a0d Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 17:59:59 +0800 Subject: [PATCH 050/164] Fixed box inertia tensor calculation --- Assets/Scenes/PhysicsSandbox.shade | 5 ++++- .../CollisionShapes/SHBoxCollisionShape.cpp | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 67d35bb5..4e5e40c2 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -19,7 +19,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: false + Freeze Position Y: true Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -29,6 +29,7 @@ DrawColliders: false Colliders: - Is Trigger: false + Collision Tag: 1 Type: Sphere Radius: 1 Friction: 0.400000006 @@ -90,6 +91,7 @@ DrawColliders: false Colliders: - Is Trigger: false + Collision Tag: 1 Type: Sphere Radius: 2.5 Friction: 0.400000006 @@ -130,6 +132,7 @@ DrawColliders: false Colliders: - Is Trigger: false + Collision Tag: 1 Type: Box Half Extents: {x: 1, y: 1, z: 1} Friction: 0.400000006 diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp index 6d7308da..f94e850f 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp @@ -236,9 +236,17 @@ namespace SHADE { static constexpr float ONE_OVER_TWELVE = (1.0f / 12.0f); - const float H2_PLUS_D2 = Extents.y * Extents.y + Extents.z * Extents.z; - const float W2_PLUS_H2 = Extents.x * Extents.x + Extents.y * Extents.y; - const float W2_PLUS_D2 = Extents.x * Extents.x + Extents.z * Extents.z; + const float WIDTH = 2.0f * Extents.x; + const float HEIGHT = 2.0f * Extents.y; + const float DEPTH = 2.0f * Extents.z; + + const float WIDTH_SQUARED = WIDTH * WIDTH; + const float HEIGHT_SQUARED = HEIGHT * HEIGHT; + const float DEPTH_SQUARED = DEPTH * DEPTH; + + const float H2_PLUS_D2 = HEIGHT_SQUARED + DEPTH_SQUARED; + const float W2_PLUS_H2 = WIDTH_SQUARED + HEIGHT_SQUARED; + const float W2_PLUS_D2 = WIDTH_SQUARED + DEPTH_SQUARED; SHMatrix result; result.m[0][0] = ONE_OVER_TWELVE * mass * H2_PLUS_D2; From a36d03b03bb2b1164faaa4e58efb5c60a1361d78 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 21:42:44 +0800 Subject: [PATCH 051/164] Contacts now draw normals --- SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp | 10 +++++++++- SHADE_Engine/src/Physics/Dynamics/SHContactManager.h | 8 +++++++- .../System/Routines/SHPhysicsDebugDrawRoutine.cpp | 3 ++- .../src/Physics/System/SHPhysicsDebugDrawSystem.cpp | 5 ----- .../src/Physics/System/SHPhysicsDebugDrawSystem.h | 1 - 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index 83047635..c8bd2abe 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -73,7 +73,15 @@ namespace SHADE continue; for (uint32_t i = 0; i < manifold.numContacts; ++i) - contactPoints.emplace_back(manifold.contacts[i].position); + { + const ContactInfo INFO + { + .position = manifold.contacts[i].position + , .normal = manifold.normal + }; + + contactPoints.emplace_back(INFO); + } } return contactPoints; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h index c2433345..81f37146 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h @@ -42,9 +42,15 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ + struct ContactInfo + { + SHVec3 position; + SHVec3 normal; + }; + using TriggerEvents = std::vector; using CollisionEvents = std::vector; - using ContactPoints = std::vector; + using ContactPoints = std::vector; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp index f4a72f82..3d1b7ba0 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -79,8 +79,9 @@ namespace SHADE const auto& CONTACT_POINTS = physicsSystem->physicsWorld->GetContactPoints(); for (auto& contactPoint : CONTACT_POINTS) { - const SHMatrix TRS = SHMatrix::Transform(contactPoint, SHQuaternion::Identity, SHVec3{ 0.1f }); + const SHMatrix TRS = SHMatrix::Transform(contactPoint.position, SHQuaternion::Identity, SHVec3{ 0.1f }); debugDrawSystem->DrawCube(TRS, CONTACT_COLOUR); + debugDrawSystem->DrawLine(contactPoint.position, contactPoint.position + contactPoint.normal * 0.3f, CONTACT_COLOUR, true); } } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index f0add8f2..6d69181c 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -149,11 +149,6 @@ namespace SHADE } } - void SHPhysicsDebugDrawSystem::drawContact(SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Contact& contactInfo) noexcept - { - static const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::CONTACT)]; - } - void SHPhysicsDebugDrawSystem::drawRaycast(SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept { static const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::RAYCAST)]; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index 14f86364..3b4552a5 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -141,7 +141,6 @@ namespace SHADE SHEventHandle onColliderDraw(SHEventPtr onColliderDrawEvent); static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; - static void drawContact (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Contact& contactInfo) noexcept; static void drawRaycast (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept; }; From d98d6a9e06a978150b35e20066f9075b417085b3 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 21:43:06 +0800 Subject: [PATCH 052/164] Refactored polyhedron and half-edge structures --- .../CollisionShapes/SHBoxCollisionShape.cpp | 149 +++++++++--------- .../CollisionShapes/SHBoxCollisionShape.h | 27 ++-- .../SHCollisionShapeLibrary.cpp | 20 +-- .../CollisionShapes/SHCollisionShapeLibrary.h | 6 +- .../SHConvexPolyhedronCollisionShape.cpp | 100 ++++++++++++ .../SHConvexPolyhedronCollisionShape.h | 78 +++++++++ ...HConvexPolyhedron.cpp => SHHalfEdgeDS.cpp} | 36 ++--- .../{SHConvexPolyhedron.h => SHHalfEdgeDS.h} | 8 +- .../CollisionShapes/SHSphereCollisionShape.h | 3 +- 9 files changed, 304 insertions(+), 123 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHConvexPolyhedron.cpp => SHHalfEdgeDS.cpp} (83%) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHConvexPolyhedron.h => SHHalfEdgeDS.h} (97%) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp index f94e850f..7abd6067 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp @@ -18,19 +18,6 @@ #include "Math/SHMatrix.h" #include "Physics/Collision/SHCollider.h" -/* - * Local box vertices, faces & half-edges - * - * Vertices (Front/Back Face): - * - * 3/7 ---------- 2/6 - * | | - * | | - * | | - * 0/4 ---------- 1/5 - * - */ - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -38,45 +25,44 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHBoxCollisionShape::SHBoxCollisionShape(SHCollisionShapeID id) noexcept - : SHCollisionShape (id, SHCollisionShape::Type::BOX) - , SHBox () - , relativeExtents { SHVec3::One } - , scale { SHVec3::One } - , polyhedron { nullptr } + : SHConvexPolyhedronCollisionShape (id, SHCollisionShape::Type::BOX) + , SHBox () + , relativeExtents { SHVec3::One } + , scale { SHVec3::One } {} SHBoxCollisionShape::SHBoxCollisionShape(const SHBoxCollisionShape& rhs) noexcept - : SHCollisionShape (rhs.id, SHCollisionShape::Type::BOX) - , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) - , relativeExtents { rhs.relativeExtents } - , scale { rhs.scale } - , polyhedron { rhs.polyhedron } + : SHConvexPolyhedronCollisionShape (rhs.id, SHCollisionShape::Type::BOX) + , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) + , relativeExtents { rhs.relativeExtents } + , scale { rhs.scale } { + halfEdgeStructure = rhs.halfEdgeStructure; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + collisionTag = rhs.collisionTag; } SHBoxCollisionShape::SHBoxCollisionShape(SHBoxCollisionShape&& rhs) noexcept - : SHCollisionShape (rhs.id, SHCollisionShape::Type::BOX) - , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) - , relativeExtents { rhs.relativeExtents } - , scale { rhs.scale } - , polyhedron { rhs.polyhedron } + : SHConvexPolyhedronCollisionShape (rhs.id, SHCollisionShape::Type::BOX) + , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) + , relativeExtents { rhs.relativeExtents } + , scale { rhs.scale } { + halfEdgeStructure = rhs.halfEdgeStructure; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + collisionTag = rhs.collisionTag; } /*-----------------------------------------------------------------------------------*/ @@ -90,26 +76,26 @@ namespace SHADE // Collision Shape Properties - id = rhs.id; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; + id = rhs.id; + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + collisionTag = rhs.collisionTag; // Box Properties - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; // Local Properties - relativeExtents = rhs.relativeExtents; - scale = rhs.scale; - polyhedron = rhs.polyhedron; + relativeExtents = rhs.relativeExtents; + scale = rhs.scale; + halfEdgeStructure = rhs.halfEdgeStructure; return *this; } @@ -118,26 +104,26 @@ namespace SHADE { // Collision Shape Properties - id = rhs.id; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; + id = rhs.id; + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + collisionTag = rhs.collisionTag; // Box Properties - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; // Local Properties - relativeExtents = rhs.relativeExtents; - scale = rhs.scale; - polyhedron = rhs.polyhedron; + relativeExtents = rhs.relativeExtents; + scale = rhs.scale; + halfEdgeStructure = rhs.halfEdgeStructure; return *this; } @@ -166,6 +152,28 @@ namespace SHADE return Orientation; } + SHVec3 SHBoxCollisionShape::GetVertex(int index) const + { + static constexpr int NUM_VERTICES = 8; + + if (index < 0 || index >= NUM_VERTICES) + throw std::invalid_argument("Index out-of-range!"); + + // DirectX already puts vertex 0 - 4 on the front face for our case. + // Otherwise, it would need to be wrapped around for the correct vertex. + return GetVertices()[index]; + } + + SHVec3 SHBoxCollisionShape::GetNormal(int faceIndex) const + { + // Get local normal + const SHVec3& LOCAL_NORMAL = halfEdgeStructure->GetFace(faceIndex).normal; + + // Rotate normal into world space + return SHVec3::Rotate(LOCAL_NORMAL, Orientation); + + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -191,12 +199,6 @@ namespace SHADE Extents = relativeExtents * scale * 0.5f; } - void SHBoxCollisionShape::SetOrientation(const SHQuaternion& newOrientation) noexcept - { - Orientation = newOrientation; - } - - void SHBoxCollisionShape::SetScale(const SHVec3& newScale) noexcept { scale = SHVec3::Abs(newScale); @@ -219,7 +221,8 @@ namespace SHADE const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); - Center = SHVec3::Transform(transform.position, TRS); + Orientation = FINAL_ROT; + Center = SHVec3::Transform(transform.position, TRS); } bool SHBoxCollisionShape::TestPoint(const SHVec3& point) const noexcept diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h index 142858e0..f34870f5 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h @@ -12,8 +12,7 @@ // Project Headers #include "Math/Geometry/SHBox.h" -#include "SHCollisionShape.h" -#include "SHConvexPolyhedron.h" +#include "SHConvexPolyhedronCollisionShape.h" namespace SHADE { @@ -37,9 +36,9 @@ namespace SHADE /** * @brief - * Encapsulate a Box Collision Shape used for Physics Simulations. + * Encapsulate a Box Shape used for Physics Simulations. */ - class SH_API SHBoxCollisionShape final : public SHCollisionShape + class SH_API SHBoxCollisionShape final : public SHConvexPolyhedronCollisionShape , private SHBox { private: @@ -49,7 +48,6 @@ namespace SHADE friend class SHCollider; friend class SHCollision; - friend class SHCompositeCollider; friend class SHCollisionShapeLibrary; public: @@ -57,7 +55,7 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHBoxCollisionShape (SHCollisionShapeID id) noexcept; + SHBoxCollisionShape (SHCollisionShapeID id) noexcept; SHBoxCollisionShape (const SHBoxCollisionShape& rhs) noexcept; SHBoxCollisionShape (SHBoxCollisionShape&& rhs) noexcept; @@ -74,10 +72,13 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; - [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; - [[nodiscard]] SHQuaternion GetOrientation () const noexcept; + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; + [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; + [[nodiscard]] SHQuaternion GetOrientation () const noexcept; + + [[nodiscard]] SHVec3 GetVertex (int index) const override; + [[nodiscard]] SHVec3 GetNormal (int faceIndex) const override; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ @@ -86,7 +87,6 @@ namespace SHADE void SetCenter (const SHVec3& newCenter) noexcept; void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; - void SetOrientation (const SHQuaternion& newOrientation) noexcept; void SetScale (const SHVec3& newScale) noexcept; /*---------------------------------------------------------------------------------*/ @@ -151,9 +151,8 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHVec3 relativeExtents; - SHVec3 scale; // Intended to be passed in by the base collider. - SHConvexPolyhedron* polyhedron; // Defines the polyhedron by it's half edges. + SHVec3 relativeExtents; + SHVec3 scale; // Intended to be passed in by the base collider. }; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp index ad5e3c44..e9832f9c 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp @@ -64,14 +64,14 @@ namespace SHADE { SHBoxCollisionShape* box = RESULT.first->second; - box->Center = createInfo.Center; - box->Extents = createInfo.Extents; - box->relativeExtents = createInfo.RelativeExtents; - box->Orientation = createInfo.Orientation; - box->scale = createInfo.Scale; + box->Center = createInfo.Center; + box->Extents = createInfo.Extents; + box->relativeExtents = createInfo.RelativeExtents; + box->Orientation = createInfo.Orientation; + box->scale = createInfo.Scale; - // Set convex polyhedron for the box - box->polyhedron = &boxPolyhedron; + // Set halfEdge data structure for the box + box->halfEdgeStructure = &boxHalfEdgeDS; return box; } @@ -159,16 +159,16 @@ namespace SHADE for (int i = 0; i < NUM_FACES; ++i) { - SHConvexPolyhedron::Face newFace; + SHHalfEdgeDS::Face newFace; newFace.normal = FACE_NORMALS[i]; for (int j = 0; j < NUM_VERTICES_PER_FACE; ++j) newFace.vertexIndices.emplace_back(FACE_VERTICES[i][j]); - boxPolyhedron.AddFace(newFace); + boxHalfEdgeDS.AddFace(newFace); } - boxPolyhedron.BuildPolyhedron(); + boxHalfEdgeDS.BuildPolyhedron(); } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h index e5958f96..b20abb7f 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h @@ -92,10 +92,10 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHConvexPolyhedron boxPolyhedron; + SHHalfEdgeDS boxHalfEdgeDS; - Spheres spheres; - Boxes boxes; + Spheres spheres; + Boxes boxes; // TODO: Add capsules and hulls /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp new file mode 100644 index 00000000..586d67f4 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp @@ -0,0 +1,100 @@ +/**************************************************************************************** + * \file SHConvexPolyhedronCollisionShape.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a convex polyhedron collision shape. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHConvexPolyhedronCollisionShape.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHConvexPolyhedronCollisionShape::SHConvexPolyhedronCollisionShape(SHCollisionShapeID id,Type polyhedronType) noexcept + : SHCollisionShape (id, polyhedronType) + , halfEdgeStructure { nullptr } + {} + + SHConvexPolyhedronCollisionShape::SHConvexPolyhedronCollisionShape(const SHConvexPolyhedronCollisionShape& rhs) noexcept + : SHCollisionShape (rhs.id, rhs.GetType()) + , halfEdgeStructure { nullptr } + { + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + } + + SHConvexPolyhedronCollisionShape::SHConvexPolyhedronCollisionShape(SHConvexPolyhedronCollisionShape&& rhs) noexcept + : SHCollisionShape (rhs.id, rhs.GetType()) + , halfEdgeStructure { nullptr } + { + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHConvexPolyhedronCollisionShape& SHConvexPolyhedronCollisionShape::operator=(const SHConvexPolyhedronCollisionShape& rhs) noexcept + { + if (this == &rhs) + return *this; + + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + + // Local Properties + halfEdgeStructure = rhs.halfEdgeStructure; + + return *this; + } + + SHConvexPolyhedronCollisionShape& SHConvexPolyhedronCollisionShape::operator=(SHConvexPolyhedronCollisionShape&& rhs) noexcept + { + material = rhs.material; + collider = rhs.collider; + transform = rhs.transform; + rotationOffset = rhs.rotationOffset; + flags = rhs.flags; + // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. + collisionTag = rhs.collisionTag; + + // Local Properties + halfEdgeStructure = rhs.halfEdgeStructure; + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHHalfEdgeDS* SHConvexPolyhedronCollisionShape::GetHalfEdgeStructure() const noexcept + { + return halfEdgeStructure; + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h new file mode 100644 index 00000000..6748793c --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h @@ -0,0 +1,78 @@ +/**************************************************************************************** + * \file SHConvexPolyhedronCollisionShape.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a convex polyhedron collision shape. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "SHCollisionShape.h" +#include "SHHalfEdgeDS.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates a convex polyhedron shape used for Physics Simulations.. + */ + class SH_API SHConvexPolyhedronCollisionShape : public SHCollisionShape + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHCollider; + friend class SHCollision; + friend class SHCollisionShapeLibrary; + + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr float RADIUS = 0.1f; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHConvexPolyhedronCollisionShape (SHCollisionShapeID id, Type polyhedronType) noexcept; + SHConvexPolyhedronCollisionShape (const SHConvexPolyhedronCollisionShape& rhs) noexcept; + SHConvexPolyhedronCollisionShape (SHConvexPolyhedronCollisionShape&& rhs) noexcept; + + ~SHConvexPolyhedronCollisionShape () override = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHConvexPolyhedronCollisionShape& operator=(const SHConvexPolyhedronCollisionShape& rhs) noexcept; + SHConvexPolyhedronCollisionShape& operator=(SHConvexPolyhedronCollisionShape&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] const SHHalfEdgeDS* GetHalfEdgeStructure () const noexcept; + [[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0; + [[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0; + + protected: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + SHHalfEdgeDS* halfEdgeStructure; // Defines the polyhedron by it's half edges. + }; + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.cpp similarity index 83% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.cpp index 0291de15..3bf48608 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.cpp @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHConvexPolyhedron.cpps + * \file SHHalfEdgeDS.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a convex polyhedron structure. + * \brief Implementation for a half-edge data structure to represent polyhedra. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHConvexPolyhedron.h" +#include "SHHalfEdgeDS.h" // Helper Macros @@ -23,7 +23,7 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHConvexPolyhedron::HalfEdge::HalfEdge() noexcept + SHHalfEdgeDS::HalfEdge::HalfEdge() noexcept : tailVertexIndex { -1 } , headVertexIndex { -1 } , edgeIndex { -1 } @@ -31,7 +31,7 @@ namespace SHADE , faceIndex { -1 } {} - SHConvexPolyhedron::HalfEdge::HalfEdge(const HalfEdge& rhs) noexcept + SHHalfEdgeDS::HalfEdge::HalfEdge(const HalfEdge& rhs) noexcept : tailVertexIndex { rhs.tailVertexIndex } , headVertexIndex { rhs.headVertexIndex } , edgeIndex { rhs.edgeIndex } @@ -39,7 +39,7 @@ namespace SHADE , faceIndex { rhs.faceIndex } {} - SHConvexPolyhedron::HalfEdge::HalfEdge(HalfEdge&& rhs) noexcept + SHHalfEdgeDS::HalfEdge::HalfEdge(HalfEdge&& rhs) noexcept : tailVertexIndex { rhs.tailVertexIndex } , headVertexIndex { rhs.headVertexIndex } , edgeIndex { rhs.edgeIndex } @@ -47,13 +47,13 @@ namespace SHADE , faceIndex { rhs.faceIndex } {} - SHConvexPolyhedron::Face::Face(const Face& rhs) noexcept + SHHalfEdgeDS::Face::Face(const Face& rhs) noexcept : normal { rhs.normal } { std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); } - SHConvexPolyhedron::Face::Face(Face&& rhs) noexcept + SHHalfEdgeDS::Face::Face(Face&& rhs) noexcept : normal { rhs.normal } { std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); @@ -63,7 +63,7 @@ namespace SHADE /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::HalfEdge::operator=(const HalfEdge& rhs) noexcept + SHHalfEdgeDS::HalfEdge& SHHalfEdgeDS::HalfEdge::operator=(const HalfEdge& rhs) noexcept { if (this == &rhs) return *this; @@ -77,7 +77,7 @@ namespace SHADE return *this; } - SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::HalfEdge::operator=(HalfEdge&& rhs) noexcept + SHHalfEdgeDS::HalfEdge& SHHalfEdgeDS::HalfEdge::operator=(HalfEdge&& rhs) noexcept { tailVertexIndex = rhs.tailVertexIndex; headVertexIndex = rhs.headVertexIndex; @@ -88,7 +88,7 @@ namespace SHADE return *this; } - SHConvexPolyhedron::Face& SHConvexPolyhedron::Face::operator=(const Face& rhs) noexcept + SHHalfEdgeDS::Face& SHHalfEdgeDS::Face::operator=(const Face& rhs) noexcept { if (this == &rhs) return *this; @@ -101,7 +101,7 @@ namespace SHADE return *this; } - SHConvexPolyhedron::Face& SHConvexPolyhedron::Face::operator=(Face&& rhs) noexcept + SHHalfEdgeDS::Face& SHHalfEdgeDS::Face::operator=(Face&& rhs) noexcept { normal = rhs.normal; @@ -115,17 +115,17 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - int32_t SHConvexPolyhedron::GetFaceCount() const noexcept + int32_t SHHalfEdgeDS::GetFaceCount() const noexcept { return static_cast(faces.size()); } - int32_t SHConvexPolyhedron::GetHalfEdgeCount() const noexcept + int32_t SHHalfEdgeDS::GetHalfEdgeCount() const noexcept { return static_cast(halfEdges.size()); } - const SHConvexPolyhedron::Face& SHConvexPolyhedron::GetFace(int32_t index) const + const SHHalfEdgeDS::Face& SHHalfEdgeDS::GetFace(int32_t index) const { if (index < 0 || index >= static_cast(faces.size())) throw std::invalid_argument("Index out-of-range!"); @@ -133,7 +133,7 @@ namespace SHADE return faces[index]; } - const SHConvexPolyhedron::HalfEdge& SHConvexPolyhedron::GetHalfEdge(int32_t index) const + const SHHalfEdgeDS::HalfEdge& SHHalfEdgeDS::GetHalfEdge(int32_t index) const { if (index < 0 || index >= static_cast(halfEdges.size())) throw std::invalid_argument("Index out-of-range!"); @@ -145,12 +145,12 @@ namespace SHADE /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHConvexPolyhedron::AddFace(const Face& face) + void SHHalfEdgeDS::AddFace(const Face& face) { faces.emplace_back(face); } - void SHConvexPolyhedron::BuildPolyhedron() noexcept + void SHHalfEdgeDS::BuildPolyhedron() noexcept { // We use the pair of vertex IDs on a half-edge to prevent duplicates std::unordered_map edgeMap; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.h similarity index 97% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.h index 762368a3..272d893c 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHConvexPolyhedron.h + * \file SHHalfEdgeDS.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a convex polyhedron structure. + * \brief Interface for a half-edge data structure to represent polyhedra. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -25,7 +25,7 @@ namespace SHADE * @brief * Encapsulates data for a convex polyhedron's geometry represented as faces & half edges. */ - class SH_API SHConvexPolyhedron + class SH_API SHHalfEdgeDS { public: /*---------------------------------------------------------------------------------*/ @@ -77,6 +77,8 @@ namespace SHADE /* Data Members */ /*-------------------------------------------------------------------------------*/ + // TODO: Store face offset + SHVec3 normal; std::vector vertexIndices; // Must be in CCW order diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 75492505..54e04fe6 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -35,7 +35,7 @@ namespace SHADE /** * @brief - * Encapsulate a Sphere Collision Shape used for Physics Simulations. + * Encapsulate a Sphere Shape used for Physics Simulations. */ class SH_API SHSphereCollisionShape final : public SHCollisionShape , private SHSphere @@ -47,7 +47,6 @@ namespace SHADE friend class SHCollider; friend class SHCollision; - friend class SHCompositeCollider; friend class SHCollisionShapeLibrary; public: From 3586c7ffdc675f7d5a244fc6716948fc729929ad Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 21:43:22 +0800 Subject: [PATCH 053/164] Added mostly working sphere vs convex polyhedron collision detection --- Assets/Scenes/PhysicsSandbox.shade | 20 +-- .../Narrowphase/SHSphereVsConvex.cpp | 170 ++++++++++++++++++ .../Narrowphase/SHSphereVsSphere.cpp | 10 +- 3 files changed, 185 insertions(+), 15 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 4e5e40c2..cd74173e 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,7 +4,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0.0700113177, y: 2.5, z: 0} + Translate: {x: 0, y: 2.5, z: 0} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -30,8 +30,8 @@ Colliders: - Is Trigger: false Collision Tag: 1 - Type: Sphere - Radius: 1 + Type: Box + Half Extents: {x: 1, y: 1, z: 1} Friction: 0.400000006 Bounciness: 0 Density: 1 @@ -49,9 +49,9 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 0.5, z: 5} + Position: {x: 3, y: 4, z: 0} Pitch: 0 - Yaw: 0 + Yaw: 90 Roll: 0 Width: 1920 Height: 1080 @@ -107,8 +107,8 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 5, z: 0} - Rotate: {x: -0, y: 0, z: 0} + Translate: {x: 0, y: 5, z: 0.834425449} + Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: @@ -122,7 +122,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: false + Freeze Position Y: true Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -133,8 +133,8 @@ Colliders: - Is Trigger: false Collision Tag: 1 - Type: Box - Half Extents: {x: 1, y: 1, z: 1} + Type: Sphere + Radius: 1 Friction: 0.400000006 Bounciness: 0 Density: 1 diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index a8a9cd48..8f12475a 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -47,6 +47,176 @@ namespace SHADE bool SHCollision::SphereVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { + // Convert to underlying types + // For the convex, we only need the convex polyhedron shape since the get vertex is pure virtual. + const SHSphereCollisionShape& SPHERE = dynamic_cast(A); + const SHConvexPolyhedronCollisionShape& CONVEX = dynamic_cast(B); + + // Ensure a gap between A & B + const float TOTAL_RADIUS = SPHERE.GetWorldRadius() + CONVEX.RADIUS; + + // Find closest face of polygon to circle + + int32_t closestFaceIndex = -1; + int32_t closestPointIndex = -1; + float bestDistance = std::numeric_limits::lowest(); + + const SHHalfEdgeDS* HALF_EDGE_STRUCTURE = CONVEX.GetHalfEdgeStructure(); + + /* + * Test against each face + * + * TODO: + * This check is now O(n^2) because we find the closest point. + * It can be optimised to O(n) by utilising the following steps: + * 1. Rotate sphere into polyhedron's space + * 2. Build a plane equation from the face in point-normal form. We need the plane's offset from the origin. + * 3. Compute distance to the face. + */ + for (int32_t i = 0; i < HALF_EDGE_STRUCTURE->GetFaceCount(); ++i) + { + const SHHalfEdgeDS::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(i); + + // TODO: Remove and optimise + // Find the closest point on the face to the sphere + const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); + for (int32_t j = 0; j < NUM_VERTICES; ++j) + { + // Get vector from center to a vertex on the face + const SHVec3 A_TO_B = SPHERE.GetCenter() - CONVEX.GetVertex(FACE.vertexIndices[j]); + + const float PROJECTION = SHVec3::Dot(A_TO_B, FACE.normal); + + // Early out + if (PROJECTION > TOTAL_RADIUS) + return false; + + if (PROJECTION > bestDistance) + { + bestDistance = PROJECTION; + closestFaceIndex = i; + closestPointIndex = j; + } + } + } + + uint32_t numContacts = 0; + const float penetration = TOTAL_RADIUS - bestDistance; + + // Rotate the normal into the world space + const SHVec3& BEST_NORMAL = CONVEX.GetNormal(closestFaceIndex); + + // Check if center is inside polyhedron (below the face) + if (bestDistance < SHMath::EPSILON) + { + SHContact newContact; + newContact.penetration = penetration; + newContact.position = SPHERE.GetCenter(); + newContact.featurePair.key = 0; + + manifold.contacts[numContacts++] = newContact; + manifold.normal = BEST_NORMAL; + + manifold.numContacts = numContacts; + return true; + } + + // Check against voronoi regions of the face to determine the type of the intersection test + // We have 3 voronoi regions to check: cp -> prev, cp -> next and cp -> center + // If none of these are true, the sphere is above the face but not separating + + const SHHalfEdgeDS::Face& CLOSEST_FACE = HALF_EDGE_STRUCTURE->GetFace(closestFaceIndex); + const int32_t NUM_VERTICES_ON_FACE = static_cast(CLOSEST_FACE.vertexIndices.size()); + + const SHVec3& CLOSEST_POINT = CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[closestPointIndex]); + const SHVec3 CP_TO_CENTER = SPHERE.GetCenter() - CLOSEST_POINT; + + // Check closest point -> prev point + { + const int32_t PREV_POINT_INDEX = closestPointIndex == 0 ? NUM_VERTICES_ON_FACE - 1 : closestPointIndex - 1; + const SHVec3& PREV_POINT = CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[PREV_POINT_INDEX]); + + const SHVec3 CP_TO_PREV = SHVec3::Normalise(PREV_POINT - CLOSEST_POINT); + + float projection = SHVec3::Dot(CP_TO_CENTER, CP_TO_PREV); + if (projection >= 0.0f) + { + // Sphere is inside this region, check if distance from center is lesser than radius + if (penetration >= TOTAL_RADIUS) + return false; + + SHContact newContact; + newContact.penetration = penetration; + newContact.position = SPHERE.GetCenter() - BEST_NORMAL * TOTAL_RADIUS; + newContact.featurePair.key = 0; + + manifold.contacts[numContacts++] = newContact; + manifold.normal = BEST_NORMAL; + + manifold.numContacts = numContacts; + return true; + } + } + + // Check closest point -> next point + { + const int32_t NEXT_POINT_INDEX = closestPointIndex + 1 % NUM_VERTICES_ON_FACE; + const SHVec3& NEXT_POINT = CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[NEXT_POINT_INDEX]); + + const SHVec3 CP_TO_NEXT = SHVec3::Normalise(NEXT_POINT - CLOSEST_POINT); + + float projection = SHVec3::Dot(CP_TO_CENTER, CP_TO_NEXT); + if (projection >= 0.0f) + { + // Sphere is inside this region, check if distance from center is lesser than radius + if (penetration >= TOTAL_RADIUS) + return false; + + SHContact newContact; + newContact.penetration = penetration; + newContact.position = SPHERE.GetCenter() - BEST_NORMAL * TOTAL_RADIUS; + newContact.featurePair.key = 0; + + manifold.contacts[numContacts++] = newContact; + manifold.normal = BEST_NORMAL; + + manifold.numContacts = numContacts; + return true; + } + } + + // Check if it hit the closest point + { + if (CP_TO_CENTER.LengthSquared() < TOTAL_RADIUS * TOTAL_RADIUS) + { + SHContact newContact; + newContact.penetration = penetration; + newContact.position = CLOSEST_POINT; + newContact.featurePair.key = 0; + + manifold.contacts[numContacts++] = newContact; + manifold.normal = SHVec3::Normalise(CP_TO_CENTER); + + manifold.numContacts = numContacts; + return true; + } + } + + // It is above the closest face + if (penetration <= TOTAL_RADIUS) + { + SHContact newContact; + newContact.penetration = penetration; + newContact.position = SPHERE.GetCenter() - BEST_NORMAL * TOTAL_RADIUS; + newContact.featurePair.key = 0; + + manifold.contacts[numContacts++] = newContact; + manifold.normal = BEST_NORMAL; + + manifold.numContacts = numContacts; + return true; + } + return false; } diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp index 8b529241..07b6c9a6 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp @@ -25,17 +25,17 @@ namespace SHADE bool SHCollision::SphereVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - const SHSphereCollisionShape& SPHERE_A = reinterpret_cast(A); - const SHSphereCollisionShape& SPHERE_B = reinterpret_cast(B); + const SHSphereCollisionShape& SPHERE_A = dynamic_cast(A); + const SHSphereCollisionShape& SPHERE_B = dynamic_cast(B); return SHSphere::Intersect(SPHERE_A, SPHERE_B); } bool SHCollision::SphereVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - // Convert to spheres - const SHSphereCollisionShape& SPHERE_A = reinterpret_cast(A); - const SHSphereCollisionShape& SPHERE_B = reinterpret_cast(B); + // Convert to underlying types + const SHSphereCollisionShape& SPHERE_A = dynamic_cast(A); + const SHSphereCollisionShape& SPHERE_B = dynamic_cast(B); const SHVec3 A_TO_B = SPHERE_B.GetCenter() - SPHERE_A.GetCenter(); const float DISTANCE_BETWEEN_CENTERS_SQUARED = A_TO_B.LengthSquared(); From 82d46fce9962d6e2e0b5610014376f296c6c3a4a Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 30 Dec 2022 23:53:45 +0800 Subject: [PATCH 054/164] Fixed voronoi region tests for sphere vs convex polyhedron --- Assets/Scenes/PhysicsSandbox.shade | 28 ++-- .../CollisionShapes/SHBoxCollisionShape.cpp | 2 - .../Narrowphase/SHSphereVsConvex.cpp | 121 +++++++++--------- .../Routines/SHPhysicsDebugDrawRoutine.cpp | 2 +- 4 files changed, 77 insertions(+), 76 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index cd74173e..7360d543 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,22 +4,22 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 2.5, z: 0} - Rotate: {x: -0, y: 0, z: -0} - Scale: {x: 1, y: 1, z: 1} + Translate: {x: -1.80977702, y: 3, z: 0} + Rotate: {x: -0, y: 0, z: -0.506194055} + Scale: {x: 3.27252102, y: 0.999997199, z: 1} IsActive: true RigidBody Component: - Type: Dynamic + Type: Static Auto Mass: false - Mass: 10 - Drag: 1 - Angular Drag: 1 + Mass: .inf + Drag: 0.00999999978 + Angular Drag: 0.00999999978 Use Gravity: true Gravity Scale: 1 Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: true + Freeze Position Y: false Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -49,9 +49,9 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 3, y: 4, z: 0} + Position: {x: 0, y: 4, z: 5} Pitch: 0 - Yaw: 90 + Yaw: 0 Roll: 0 Width: 1920 Height: 1080 @@ -107,7 +107,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 5, z: 0.834425449} + Translate: {x: -1.97624588, y: 5, z: 0} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -115,14 +115,14 @@ Type: Dynamic Auto Mass: false Mass: 1 - Drag: 1 - Angular Drag: 1 + Drag: 0.00999999978 + Angular Drag: 0.00999999978 Use Gravity: true Gravity Scale: 1 Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: true + Freeze Position Y: false Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp index 7abd6067..0fa8c861 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp @@ -159,8 +159,6 @@ namespace SHADE if (index < 0 || index >= NUM_VERTICES) throw std::invalid_argument("Index out-of-range!"); - // DirectX already puts vertex 0 - 4 on the front face for our case. - // Otherwise, it would need to be wrapped around for the correct vertex. return GetVertices()[index]; } diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 8f12475a..034e4d57 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -101,22 +101,22 @@ namespace SHADE } uint32_t numContacts = 0; - const float penetration = TOTAL_RADIUS - bestDistance; // Rotate the normal into the world space const SHVec3& BEST_NORMAL = CONVEX.GetNormal(closestFaceIndex); + const float PENETRATION = TOTAL_RADIUS - bestDistance; // Check if center is inside polyhedron (below the face) if (bestDistance < SHMath::EPSILON) { + manifold.normal = -BEST_NORMAL; + SHContact newContact; - newContact.penetration = penetration; - newContact.position = SPHERE.GetCenter(); + newContact.penetration = PENETRATION; + newContact.position = SPHERE.GetCenter() - BEST_NORMAL * PENETRATION; newContact.featurePair.key = 0; manifold.contacts[numContacts++] = newContact; - manifold.normal = BEST_NORMAL; - manifold.numContacts = numContacts; return true; } @@ -125,95 +125,98 @@ namespace SHADE // We have 3 voronoi regions to check: cp -> prev, cp -> next and cp -> center // If none of these are true, the sphere is above the face but not separating + /* + * | A + * _ _ _ _ _ _ | _ _ _ + * / / + * | / | / regionA + * |/ _ _ _ _ _|/ _ _ _ + * B/ regionB /C + * / / regionC + */ + const SHHalfEdgeDS::Face& CLOSEST_FACE = HALF_EDGE_STRUCTURE->GetFace(closestFaceIndex); const int32_t NUM_VERTICES_ON_FACE = static_cast(CLOSEST_FACE.vertexIndices.size()); - const SHVec3& CLOSEST_POINT = CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[closestPointIndex]); - const SHVec3 CP_TO_CENTER = SPHERE.GetCenter() - CLOSEST_POINT; + const SHVec3 C = CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[closestPointIndex]); + const SHVec3 C_TO_CENTER = SPHERE.GetCenter() - C; - // Check closest point -> prev point + const int32_t INDEX_A = (closestPointIndex + 1) % NUM_VERTICES_ON_FACE; + const int32_t INDEX_B = closestPointIndex == 0 ? NUM_VERTICES_ON_FACE - 1 : closestPointIndex - 1; + + const SHVec3 POINTS[2] = { - const int32_t PREV_POINT_INDEX = closestPointIndex == 0 ? NUM_VERTICES_ON_FACE - 1 : closestPointIndex - 1; - const SHVec3& PREV_POINT = CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[PREV_POINT_INDEX]); + CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[INDEX_A]) // A + , CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[INDEX_B]) // B + }; - const SHVec3 CP_TO_PREV = SHVec3::Normalise(PREV_POINT - CLOSEST_POINT); + // To be inside either region A or B, 2 conditions must be satisfied + // 1. Same side as tangent + // 2. Same side as normal from edge to sphere - float projection = SHVec3::Dot(CP_TO_CENTER, CP_TO_PREV); + // Check in regions A & B + for (int i = 0; i < 2; ++i) + { + const SHVec3 TANGENT = SHVec3::Normalise(POINTS[i] - C); + + float projection = SHVec3::Dot(C_TO_CENTER, TANGENT); if (projection >= 0.0f) { - // Sphere is inside this region, check if distance from center is lesser than radius - if (penetration >= TOTAL_RADIUS) - return false; + // Check 2nd condition + // Find closest point + const SHVec3 CP = C + projection * TANGENT; + const SHVec3 CP_TO_CENTER = SHVec3::Normalise(C - CP); - SHContact newContact; - newContact.penetration = penetration; - newContact.position = SPHERE.GetCenter() - BEST_NORMAL * TOTAL_RADIUS; - newContact.featurePair.key = 0; + projection = SHVec3::Dot(C_TO_CENTER, CP_TO_CENTER); + if (projection >= 0.0f) + { + // Sphere Within region A + manifold.normal = CP_TO_CENTER; - manifold.contacts[numContacts++] = newContact; - manifold.normal = BEST_NORMAL; + SHContact newContact; + newContact.penetration = TOTAL_RADIUS - projection; + newContact.position = CP; + newContact.featurePair.key = 0; - manifold.numContacts = numContacts; - return true; + manifold.contacts[numContacts++] = newContact; + manifold.numContacts = numContacts; + + return true; + } } } - // Check closest point -> next point + // Check region C (closest point) { - const int32_t NEXT_POINT_INDEX = closestPointIndex + 1 % NUM_VERTICES_ON_FACE; - const SHVec3& NEXT_POINT = CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[NEXT_POINT_INDEX]); - - const SHVec3 CP_TO_NEXT = SHVec3::Normalise(NEXT_POINT - CLOSEST_POINT); - - float projection = SHVec3::Dot(CP_TO_CENTER, CP_TO_NEXT); - if (projection >= 0.0f) + if (C_TO_CENTER.LengthSquared() < TOTAL_RADIUS * TOTAL_RADIUS) { - // Sphere is inside this region, check if distance from center is lesser than radius - if (penetration >= TOTAL_RADIUS) - return false; + manifold.normal = SHVec3::Normalise(C_TO_CENTER); SHContact newContact; - newContact.penetration = penetration; - newContact.position = SPHERE.GetCenter() - BEST_NORMAL * TOTAL_RADIUS; + newContact.penetration = PENETRATION; + newContact.position = C; newContact.featurePair.key = 0; manifold.contacts[numContacts++] = newContact; - manifold.normal = BEST_NORMAL; - manifold.numContacts = numContacts; + return true; } } - // Check if it hit the closest point + // Region D + if (PENETRATION <= TOTAL_RADIUS) { - if (CP_TO_CENTER.LengthSquared() < TOTAL_RADIUS * TOTAL_RADIUS) - { - SHContact newContact; - newContact.penetration = penetration; - newContact.position = CLOSEST_POINT; - newContact.featurePair.key = 0; + manifold.normal = -BEST_NORMAL; - manifold.contacts[numContacts++] = newContact; - manifold.normal = SHVec3::Normalise(CP_TO_CENTER); - - manifold.numContacts = numContacts; - return true; - } - } - - // It is above the closest face - if (penetration <= TOTAL_RADIUS) - { SHContact newContact; - newContact.penetration = penetration; + newContact.penetration = PENETRATION; newContact.position = SPHERE.GetCenter() - BEST_NORMAL * TOTAL_RADIUS; newContact.featurePair.key = 0; manifold.contacts[numContacts++] = newContact; - manifold.normal = BEST_NORMAL; - manifold.numContacts = numContacts; + return true; } diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp index 3d1b7ba0..b1de5851 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -81,7 +81,7 @@ namespace SHADE { const SHMatrix TRS = SHMatrix::Transform(contactPoint.position, SHQuaternion::Identity, SHVec3{ 0.1f }); debugDrawSystem->DrawCube(TRS, CONTACT_COLOUR); - debugDrawSystem->DrawLine(contactPoint.position, contactPoint.position + contactPoint.normal * 0.3f, CONTACT_COLOUR, true); + debugDrawSystem->DrawLine(contactPoint.position, contactPoint.position + contactPoint.normal * 0.5f, CONTACT_COLOUR, true); } } From 896b47c1a01e28fb98ebc4fe6a9ecbcb577f7e9f Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 31 Dec 2022 01:11:03 +0800 Subject: [PATCH 055/164] Fixed and optimised sphere vs convex polyhedron Improved sphere vs convex polyhedron from O(n^2) to O(n). Math is amazing. --- Assets/Scenes/PhysicsSandbox.shade | 231 ++++++++++++++---- .../CollisionShapes/SHBoxCollisionShape.cpp | 35 +++ .../SHCollisionShapeLibrary.cpp | 4 +- .../Narrowphase/SHSphereVsConvex.cpp | 92 +++---- .../src/Physics/Dynamics/SHContactManager.cpp | 1 - 5 files changed, 272 insertions(+), 91 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 7360d543..f93d0cc8 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,9 +4,9 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.80977702, y: 3, z: 0} - Rotate: {x: -0, y: 0, z: -0.506194055} - Scale: {x: 3.27252102, y: 0.999997199, z: 1} + Translate: {x: 2.58071685, y: 0.456140399, z: 0} + Rotate: {x: -0, y: 0, z: 0.469853699} + Scale: {x: 4.61071014, y: 0.999995053, z: 1} IsActive: true RigidBody Component: Type: Static @@ -19,7 +19,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: false + Freeze Position Y: true Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -60,54 +60,13 @@ Perspective: true IsActive: true Scripts: ~ -- EID: 2 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 0, y: 0, z: 0} - Rotate: {x: -0, y: 0, z: -0} - Scale: {x: 1, y: 1, z: 1} - IsActive: true - RigidBody Component: - Type: Static - Auto Mass: false - Mass: .inf - Drag: 0.00999999978 - Angular Drag: 0.00999999978 - Use Gravity: false - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: false - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 1 - Type: Sphere - Radius: 2.5 - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ - EID: 3 Name: Default IsActive: true NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.97624588, y: 5, z: 0} + Translate: {x: -2.24715948, y: 6.47200441, z: 0} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -141,6 +100,186 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true + Scripts: + - Type: PhysicsTestObj + Enabled: true + forceAmount: 50 + torqueAmount: 500 +- EID: 2 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: 4.09544182, z: 0} + Rotate: {x: -0, y: 0, z: -0.437829614} + Scale: {x: 4.61071014, y: 0.999995887, z: 1} + IsActive: true + RigidBody Component: + Type: Static + Auto Mass: false + Mass: .inf + Drag: 0.00999999978 + Angular Drag: 0.00999999978 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: true + Freeze Position Y: true + Freeze Position Z: true + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: false + Colliders: + - Is Trigger: false + Collision Tag: 1 + Type: Box + Half Extents: {x: 1, y: 1, z: 1} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true + Scripts: + - Type: PhysicsTestObj + Enabled: true + forceAmount: 50 + torqueAmount: 500 +- EID: 4 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: -1.74209237, z: 0} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 10, y: 0.5, z: 10} + IsActive: true + RigidBody Component: + Type: Static + Auto Mass: false + Mass: .inf + Drag: 0.00999999978 + Angular Drag: 0.00999999978 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false + Freeze Position Y: true + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: false + Colliders: + - Is Trigger: false + Collision Tag: 1 + Type: Box + Half Extents: {x: 1, y: 1, z: 1} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true + Scripts: + - Type: PhysicsTestObj + Enabled: true + forceAmount: 50 + torqueAmount: 500 +- EID: 5 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: -4.80025721, y: 3, z: 0} + Rotate: {x: -0, y: 0, z: 1.57079601} + Scale: {x: 9.99975109, y: 0.499992192, z: 10} + IsActive: true + RigidBody Component: + Type: Static + Auto Mass: false + Mass: .inf + Drag: 0.00999999978 + Angular Drag: 0.00999999978 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false + Freeze Position Y: true + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: false + Colliders: + - Is Trigger: false + Collision Tag: 1 + Type: Box + Half Extents: {x: 1, y: 1, z: 1} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true + Scripts: + - Type: PhysicsTestObj + Enabled: true + forceAmount: 50 + torqueAmount: 500 +- EID: 6 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 4.80000019, y: 3, z: 0} + Rotate: {x: -0, y: 0, z: 1.57079601} + Scale: {x: 9.99975109, y: 0.499992192, z: 10} + IsActive: true + RigidBody Component: + Type: Static + Auto Mass: false + Mass: .inf + Drag: 0.00999999978 + Angular Drag: 0.00999999978 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false + Freeze Position Y: true + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: false + Colliders: + - Is Trigger: false + Collision Tag: 1 + Type: Box + Half Extents: {x: 1, y: 1, z: 1} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true Scripts: - Type: PhysicsTestObj Enabled: true diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp index 0fa8c861..621b560b 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp @@ -160,6 +160,41 @@ namespace SHADE throw std::invalid_argument("Index out-of-range!"); return GetVertices()[index]; + + //// Rotate half extents + //const SHVec3 ROTATED_EXTENTS = SHVec3::Rotate(Extents, Orientation); + //const SHVec3 CENTER { Center }; + + ///* + // * Front: -Z + // * + // * 3 _____ 2 + // * | | + // * | | + // * |_____| + // * 0 1 + // * + // * Back: +Z + // * + // * 7 _____ 6 + // * | | + // * | | + // * |_____| + // * 4 5 + // * + // */ + + //switch (index) + //{ + // case 0: return CENTER + SHVec3 { -ROTATED_EXTENTS.x, -ROTATED_EXTENTS.y, -ROTATED_EXTENTS.z } + // case 1: return CENTER + SHVec3 { + // case 2: return CENTER + SHVec3 { + // case 3: return CENTER + SHVec3 { + // case 4: return CENTER + SHVec3 { + // case 5: return CENTER + SHVec3 { + // case 6: return CENTER + SHVec3 { + // case 7: return CENTER + SHVec3 { + //} } SHVec3 SHBoxCollisionShape::GetNormal(int faceIndex) const diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp index e9832f9c..58af5dc4 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp @@ -126,9 +126,9 @@ namespace SHADE * * Faces: * - * Front: 0 (0,1,2,3) Normal: Z + * Front: 0 (0,1,2,3) Normal: -Z * Right: 1 (1,5,6,2) Normal: X - * Back: 2 (5,4,7,6) Normal: -Z + * Back: 2 (5,4,7,6) Normal: Z * Left: 3 (4,0,3,7) Normal: -X * Top: 4 (3,2,6,7) Normal: Y * Bottom: 5 (4,5,1,0) Normal: -Y diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 034e4d57..152c9460 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -52,8 +52,8 @@ namespace SHADE const SHSphereCollisionShape& SPHERE = dynamic_cast(A); const SHConvexPolyhedronCollisionShape& CONVEX = dynamic_cast(B); - // Ensure a gap between A & B - const float TOTAL_RADIUS = SPHERE.GetWorldRadius() + CONVEX.RADIUS; + const SHVec3 CENTER = SPHERE.GetCenter(); + const float TOTAL_RADIUS = SPHERE.GetWorldRadius() + SHConvexPolyhedronCollisionShape::RADIUS; // Find closest face of polygon to circle @@ -64,56 +64,49 @@ namespace SHADE const SHHalfEdgeDS* HALF_EDGE_STRUCTURE = CONVEX.GetHalfEdgeStructure(); /* - * Test against each face + * Test against each face. * - * TODO: - * This check is now O(n^2) because we find the closest point. - * It can be optimised to O(n) by utilising the following steps: - * 1. Rotate sphere into polyhedron's space - * 2. Build a plane equation from the face in point-normal form. We need the plane's offset from the origin. - * 3. Compute distance to the face. + * 1. For each face, build a plane in point-normal form. + * 2. Find the signed distance from plane to center of sphere. + * 3. Save best distance and face. */ for (int32_t i = 0; i < HALF_EDGE_STRUCTURE->GetFaceCount(); ++i) { const SHHalfEdgeDS::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(i); - // TODO: Remove and optimise - // Find the closest point on the face to the sphere - const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); - for (int32_t j = 0; j < NUM_VERTICES; ++j) + // Build plane equation + + const SHVec3 POINT = CONVEX.GetVertex(FACE.vertexIndices[0]); // use the first vertex to build a plane + const SHVec3& NORMAL = CONVEX.GetNormal(i); + const float D = -SHVec3::Dot(NORMAL, POINT); + + // Find signed distance of center to plane + const float SIGNED_DIST = SHVec3::Dot(NORMAL, CENTER) + D; + + // Early out: + // If face is facing away from center, signed dist is negative. + // Therefore signed distance is only positive when sphere is in front of the face. + if (SIGNED_DIST > TOTAL_RADIUS) + return false; + + if (SIGNED_DIST > bestDistance) { - // Get vector from center to a vertex on the face - const SHVec3 A_TO_B = SPHERE.GetCenter() - CONVEX.GetVertex(FACE.vertexIndices[j]); - - const float PROJECTION = SHVec3::Dot(A_TO_B, FACE.normal); - - // Early out - if (PROJECTION > TOTAL_RADIUS) - return false; - - if (PROJECTION > bestDistance) - { - bestDistance = PROJECTION; - closestFaceIndex = i; - closestPointIndex = j; - } + bestDistance = SIGNED_DIST; + closestFaceIndex = i; } } uint32_t numContacts = 0; - - // Rotate the normal into the world space - const SHVec3& BEST_NORMAL = CONVEX.GetNormal(closestFaceIndex); const float PENETRATION = TOTAL_RADIUS - bestDistance; // Check if center is inside polyhedron (below the face) if (bestDistance < SHMath::EPSILON) { - manifold.normal = -BEST_NORMAL; + manifold.normal = CONVEX.GetNormal(closestFaceIndex); SHContact newContact; newContact.penetration = PENETRATION; - newContact.position = SPHERE.GetCenter() - BEST_NORMAL * PENETRATION; + newContact.position = SPHERE.GetCenter(); newContact.featurePair.key = 0; manifold.contacts[numContacts++] = newContact; @@ -135,10 +128,25 @@ namespace SHADE * / / regionC */ - const SHHalfEdgeDS::Face& CLOSEST_FACE = HALF_EDGE_STRUCTURE->GetFace(closestFaceIndex); - const int32_t NUM_VERTICES_ON_FACE = static_cast(CLOSEST_FACE.vertexIndices.size()); + const SHHalfEdgeDS::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(closestFaceIndex); + const SHVec3& NORMAL = CONVEX.GetNormal(closestFaceIndex); - const SHVec3 C = CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[closestPointIndex]); + const int32_t NUM_VERTICES_ON_FACE = static_cast(FACE.vertexIndices.size()); + + // Find closest point on face + closestPointIndex = 0; + float smallestDist = SHVec3::Dot(CENTER - CONVEX.GetVertex(FACE.vertexIndices[0]), NORMAL); + + for (int32_t i = 1; i < NUM_VERTICES_ON_FACE; ++i) + { + const SHVec3 POINT = CONVEX.GetVertex(FACE.vertexIndices[i]); + const float PROJECTION = SHVec3::Dot(CENTER - POINT, NORMAL); + + if (PROJECTION < smallestDist) + closestPointIndex = i; + } + + const SHVec3 C = CONVEX.GetVertex(FACE.vertexIndices[closestPointIndex]); const SHVec3 C_TO_CENTER = SPHERE.GetCenter() - C; const int32_t INDEX_A = (closestPointIndex + 1) % NUM_VERTICES_ON_FACE; @@ -146,8 +154,8 @@ namespace SHADE const SHVec3 POINTS[2] = { - CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[INDEX_A]) // A - , CONVEX.GetVertex(CLOSEST_FACE.vertexIndices[INDEX_B]) // B + CONVEX.GetVertex(FACE.vertexIndices[INDEX_A]) // A + , CONVEX.GetVertex(FACE.vertexIndices[INDEX_B]) // B }; // To be inside either region A or B, 2 conditions must be satisfied @@ -170,7 +178,7 @@ namespace SHADE projection = SHVec3::Dot(C_TO_CENTER, CP_TO_CENTER); if (projection >= 0.0f) { - // Sphere Within region A + // Sphere Within region manifold.normal = CP_TO_CENTER; SHContact newContact; @@ -190,7 +198,7 @@ namespace SHADE { if (C_TO_CENTER.LengthSquared() < TOTAL_RADIUS * TOTAL_RADIUS) { - manifold.normal = SHVec3::Normalise(C_TO_CENTER); + manifold.normal = -SHVec3::Normalise(C_TO_CENTER); SHContact newContact; newContact.penetration = PENETRATION; @@ -207,11 +215,11 @@ namespace SHADE // Region D if (PENETRATION <= TOTAL_RADIUS) { - manifold.normal = -BEST_NORMAL; + manifold.normal = -NORMAL; SHContact newContact; newContact.penetration = PENETRATION; - newContact.position = SPHERE.GetCenter() - BEST_NORMAL * TOTAL_RADIUS; + newContact.position = SPHERE.GetCenter() - NORMAL * TOTAL_RADIUS; newContact.featurePair.key = 0; manifold.contacts[numContacts++] = newContact; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index c8bd2abe..34e3dabf 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -63,7 +63,6 @@ namespace SHADE const SHContactManager::ContactPoints& SHContactManager::GetContactPoints() const noexcept { static ContactPoints contactPoints; - contactPoints.clear(); for (auto& manifold : manifolds | std::views::values) From 987a1fa515fbad167de0f7c5a27a2c6926547f98 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 31 Dec 2022 01:18:35 +0800 Subject: [PATCH 056/164] Fixed false positives with convex polyhedron radii --- .../SHConvexPolyhedronCollisionShape.h | 6 ------ .../Collision/Narrowphase/SHSphereVsConvex.cpp | 16 ++++++++-------- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 1 - 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h index 6748793c..f459e87e 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h @@ -36,12 +36,6 @@ namespace SHADE friend class SHCollisionShapeLibrary; public: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr float RADIUS = 0.1f; - /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 152c9460..d7813d85 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -52,8 +52,8 @@ namespace SHADE const SHSphereCollisionShape& SPHERE = dynamic_cast(A); const SHConvexPolyhedronCollisionShape& CONVEX = dynamic_cast(B); - const SHVec3 CENTER = SPHERE.GetCenter(); - const float TOTAL_RADIUS = SPHERE.GetWorldRadius() + SHConvexPolyhedronCollisionShape::RADIUS; + const SHVec3 CENTER = SPHERE.GetCenter(); + const float RADIUS = SPHERE.GetWorldRadius(); // Find closest face of polygon to circle @@ -86,7 +86,7 @@ namespace SHADE // Early out: // If face is facing away from center, signed dist is negative. // Therefore signed distance is only positive when sphere is in front of the face. - if (SIGNED_DIST > TOTAL_RADIUS) + if (SIGNED_DIST > RADIUS) return false; if (SIGNED_DIST > bestDistance) @@ -97,7 +97,7 @@ namespace SHADE } uint32_t numContacts = 0; - const float PENETRATION = TOTAL_RADIUS - bestDistance; + const float PENETRATION = RADIUS - bestDistance; // Check if center is inside polyhedron (below the face) if (bestDistance < SHMath::EPSILON) @@ -182,7 +182,7 @@ namespace SHADE manifold.normal = CP_TO_CENTER; SHContact newContact; - newContact.penetration = TOTAL_RADIUS - projection; + newContact.penetration = RADIUS - projection; newContact.position = CP; newContact.featurePair.key = 0; @@ -196,7 +196,7 @@ namespace SHADE // Check region C (closest point) { - if (C_TO_CENTER.LengthSquared() < TOTAL_RADIUS * TOTAL_RADIUS) + if (C_TO_CENTER.LengthSquared() < RADIUS * RADIUS) { manifold.normal = -SHVec3::Normalise(C_TO_CENTER); @@ -213,13 +213,13 @@ namespace SHADE } // Region D - if (PENETRATION <= TOTAL_RADIUS) + if (PENETRATION <= RADIUS) { manifold.normal = -NORMAL; SHContact newContact; newContact.penetration = PENETRATION; - newContact.position = SPHERE.GetCenter() - NORMAL * TOTAL_RADIUS; + newContact.position = SPHERE.GetCenter() - NORMAL * RADIUS; newContact.featurePair.key = 0; manifold.contacts[numContacts++] = newContact; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index 4e6e8165..d5c9b029 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -108,7 +108,6 @@ namespace SHADE /* - * TODO: A lot of this needs to be cleaned up. * Resolve Contacts */ From 6451ca5e954bcb53e6a6ef161b317d73dd6888d8 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 31 Dec 2022 01:40:28 +0800 Subject: [PATCH 057/164] forgot to flip a normal --- Assets/Scenes/PhysicsSandbox.shade | 2 +- .../src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index f93d0cc8..731df7ce 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -2.24715948, y: 6.47200441, z: 0} + Translate: {x: -1.98839664, y: 7.7662077, z: 0} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index d7813d85..314e3ff9 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -102,7 +102,7 @@ namespace SHADE // Check if center is inside polyhedron (below the face) if (bestDistance < SHMath::EPSILON) { - manifold.normal = CONVEX.GetNormal(closestFaceIndex); + manifold.normal = -CONVEX.GetNormal(closestFaceIndex); SHContact newContact; newContact.penetration = PENETRATION; @@ -179,7 +179,7 @@ namespace SHADE if (projection >= 0.0f) { // Sphere Within region - manifold.normal = CP_TO_CENTER; + manifold.normal = -CP_TO_CENTER; SHContact newContact; newContact.penetration = RADIUS - projection; From 136b7e7bfcb07f90b251d8d5f915d09845a6701a Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 31 Dec 2022 01:47:42 +0800 Subject: [PATCH 058/164] Renamed HalfEdgeDS to HalfEdgeStructure for clarity do not abbreviate. abbreviation are usually bad!! --- .../CollisionShapes/SHCollisionShape.h | 2 +- .../SHCollisionShapeLibrary.cpp | 4 +-- .../CollisionShapes/SHCollisionShapeLibrary.h | 2 +- .../SHConvexPolyhedronCollisionShape.cpp | 2 +- .../SHConvexPolyhedronCollisionShape.h | 10 +++--- ...HalfEdgeDS.cpp => SHHalfEdgeStructure.cpp} | 34 +++++++++---------- .../{SHHalfEdgeDS.h => SHHalfEdgeStructure.h} | 6 ++-- .../Narrowphase/SHSphereVsConvex.cpp | 8 ++--- 8 files changed, 34 insertions(+), 34 deletions(-) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHHalfEdgeDS.cpp => SHHalfEdgeStructure.cpp} (84%) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHHalfEdgeDS.h => SHHalfEdgeStructure.h} (98%) diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 6a2e81e4..0a320444 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -131,7 +131,7 @@ namespace SHADE virtual void ComputeTransforms () noexcept = 0; [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; - [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; + [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp index 58af5dc4..338083c7 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp @@ -159,7 +159,7 @@ namespace SHADE for (int i = 0; i < NUM_FACES; ++i) { - SHHalfEdgeDS::Face newFace; + SHHalfEdgeStructure::Face newFace; newFace.normal = FACE_NORMALS[i]; for (int j = 0; j < NUM_VERTICES_PER_FACE; ++j) @@ -168,7 +168,7 @@ namespace SHADE boxHalfEdgeDS.AddFace(newFace); } - boxHalfEdgeDS.BuildPolyhedron(); + boxHalfEdgeDS.Build(); } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h index b20abb7f..bb9c2807 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h @@ -92,7 +92,7 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHHalfEdgeDS boxHalfEdgeDS; + SHHalfEdgeStructure boxHalfEdgeDS; Spheres spheres; Boxes boxes; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp index 586d67f4..db6d3621 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp @@ -93,7 +93,7 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - const SHHalfEdgeDS* SHConvexPolyhedronCollisionShape::GetHalfEdgeStructure() const noexcept + const SHHalfEdgeStructure* SHConvexPolyhedronCollisionShape::GetHalfEdgeStructure() const noexcept { return halfEdgeStructure; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h index f459e87e..7a0f6df0 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h @@ -12,7 +12,7 @@ // Project Headers #include "SHCollisionShape.h" -#include "SHHalfEdgeDS.h" +#include "SHHalfEdgeStructure.h" namespace SHADE { @@ -57,16 +57,16 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] const SHHalfEdgeDS* GetHalfEdgeStructure () const noexcept; - [[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0; - [[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0; + [[nodiscard]] const SHHalfEdgeStructure* GetHalfEdgeStructure () const noexcept; + [[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0; + [[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0; protected: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHHalfEdgeDS* halfEdgeStructure; // Defines the polyhedron by it's half edges. + SHHalfEdgeStructure* halfEdgeStructure; // Defines the polyhedron by it's half edges. }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp similarity index 84% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.cpp rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp index 3bf48608..4c9304c4 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHHalfEdgeDS.cpp + * \file SHHalfEdgeStructure.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Implementation for a half-edge data structure to represent polyhedra. * @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHHalfEdgeDS.h" +#include "SHHalfEdgeStructure.h" // Helper Macros @@ -23,7 +23,7 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHHalfEdgeDS::HalfEdge::HalfEdge() noexcept + SHHalfEdgeStructure::HalfEdge::HalfEdge() noexcept : tailVertexIndex { -1 } , headVertexIndex { -1 } , edgeIndex { -1 } @@ -31,7 +31,7 @@ namespace SHADE , faceIndex { -1 } {} - SHHalfEdgeDS::HalfEdge::HalfEdge(const HalfEdge& rhs) noexcept + SHHalfEdgeStructure::HalfEdge::HalfEdge(const HalfEdge& rhs) noexcept : tailVertexIndex { rhs.tailVertexIndex } , headVertexIndex { rhs.headVertexIndex } , edgeIndex { rhs.edgeIndex } @@ -39,7 +39,7 @@ namespace SHADE , faceIndex { rhs.faceIndex } {} - SHHalfEdgeDS::HalfEdge::HalfEdge(HalfEdge&& rhs) noexcept + SHHalfEdgeStructure::HalfEdge::HalfEdge(HalfEdge&& rhs) noexcept : tailVertexIndex { rhs.tailVertexIndex } , headVertexIndex { rhs.headVertexIndex } , edgeIndex { rhs.edgeIndex } @@ -47,13 +47,13 @@ namespace SHADE , faceIndex { rhs.faceIndex } {} - SHHalfEdgeDS::Face::Face(const Face& rhs) noexcept + SHHalfEdgeStructure::Face::Face(const Face& rhs) noexcept : normal { rhs.normal } { std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); } - SHHalfEdgeDS::Face::Face(Face&& rhs) noexcept + SHHalfEdgeStructure::Face::Face(Face&& rhs) noexcept : normal { rhs.normal } { std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); @@ -63,7 +63,7 @@ namespace SHADE /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHHalfEdgeDS::HalfEdge& SHHalfEdgeDS::HalfEdge::operator=(const HalfEdge& rhs) noexcept + SHHalfEdgeStructure::HalfEdge& SHHalfEdgeStructure::HalfEdge::operator=(const HalfEdge& rhs) noexcept { if (this == &rhs) return *this; @@ -77,7 +77,7 @@ namespace SHADE return *this; } - SHHalfEdgeDS::HalfEdge& SHHalfEdgeDS::HalfEdge::operator=(HalfEdge&& rhs) noexcept + SHHalfEdgeStructure::HalfEdge& SHHalfEdgeStructure::HalfEdge::operator=(HalfEdge&& rhs) noexcept { tailVertexIndex = rhs.tailVertexIndex; headVertexIndex = rhs.headVertexIndex; @@ -88,7 +88,7 @@ namespace SHADE return *this; } - SHHalfEdgeDS::Face& SHHalfEdgeDS::Face::operator=(const Face& rhs) noexcept + SHHalfEdgeStructure::Face& SHHalfEdgeStructure::Face::operator=(const Face& rhs) noexcept { if (this == &rhs) return *this; @@ -101,7 +101,7 @@ namespace SHADE return *this; } - SHHalfEdgeDS::Face& SHHalfEdgeDS::Face::operator=(Face&& rhs) noexcept + SHHalfEdgeStructure::Face& SHHalfEdgeStructure::Face::operator=(Face&& rhs) noexcept { normal = rhs.normal; @@ -115,17 +115,17 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - int32_t SHHalfEdgeDS::GetFaceCount() const noexcept + int32_t SHHalfEdgeStructure::GetFaceCount() const noexcept { return static_cast(faces.size()); } - int32_t SHHalfEdgeDS::GetHalfEdgeCount() const noexcept + int32_t SHHalfEdgeStructure::GetHalfEdgeCount() const noexcept { return static_cast(halfEdges.size()); } - const SHHalfEdgeDS::Face& SHHalfEdgeDS::GetFace(int32_t index) const + const SHHalfEdgeStructure::Face& SHHalfEdgeStructure::GetFace(int32_t index) const { if (index < 0 || index >= static_cast(faces.size())) throw std::invalid_argument("Index out-of-range!"); @@ -133,7 +133,7 @@ namespace SHADE return faces[index]; } - const SHHalfEdgeDS::HalfEdge& SHHalfEdgeDS::GetHalfEdge(int32_t index) const + const SHHalfEdgeStructure::HalfEdge& SHHalfEdgeStructure::GetHalfEdge(int32_t index) const { if (index < 0 || index >= static_cast(halfEdges.size())) throw std::invalid_argument("Index out-of-range!"); @@ -145,12 +145,12 @@ namespace SHADE /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHHalfEdgeDS::AddFace(const Face& face) + void SHHalfEdgeStructure::AddFace(const Face& face) { faces.emplace_back(face); } - void SHHalfEdgeDS::BuildPolyhedron() noexcept + void SHHalfEdgeStructure::Build() noexcept { // We use the pair of vertex IDs on a half-edge to prevent duplicates std::unordered_map edgeMap; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h similarity index 98% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.h rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h index 272d893c..c08803ce 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeDS.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHHalfEdgeDS.h + * \file SHHalfEdgeStructure.h * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Interface for a half-edge data structure to represent polyhedra. * @@ -25,7 +25,7 @@ namespace SHADE * @brief * Encapsulates data for a convex polyhedron's geometry represented as faces & half edges. */ - class SH_API SHHalfEdgeDS + class SH_API SHHalfEdgeStructure { public: /*---------------------------------------------------------------------------------*/ @@ -127,7 +127,7 @@ namespace SHADE * Before this method is invoked, there must be some faces. * @return */ - void BuildPolyhedron() noexcept; + void Build() noexcept; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 314e3ff9..e62bc241 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -61,7 +61,7 @@ namespace SHADE int32_t closestPointIndex = -1; float bestDistance = std::numeric_limits::lowest(); - const SHHalfEdgeDS* HALF_EDGE_STRUCTURE = CONVEX.GetHalfEdgeStructure(); + const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = CONVEX.GetHalfEdgeStructure(); /* * Test against each face. @@ -72,7 +72,7 @@ namespace SHADE */ for (int32_t i = 0; i < HALF_EDGE_STRUCTURE->GetFaceCount(); ++i) { - const SHHalfEdgeDS::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(i); + const SHHalfEdgeStructure::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(i); // Build plane equation @@ -128,8 +128,8 @@ namespace SHADE * / / regionC */ - const SHHalfEdgeDS::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(closestFaceIndex); - const SHVec3& NORMAL = CONVEX.GetNormal(closestFaceIndex); + const SHHalfEdgeStructure::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(closestFaceIndex); + const SHVec3& NORMAL = CONVEX.GetNormal(closestFaceIndex); const int32_t NUM_VERTICES_ON_FACE = static_cast(FACE.vertexIndices.size()); From 3a7336fe15d918ec281252f833da001040811bd4 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sat, 31 Dec 2022 18:43:46 +0800 Subject: [PATCH 059/164] Improved stability of sphere vs convex polyhedron except for one edge case --- Assets/Scenes/PhysicsSandbox.shade | 10 +- .../SHCollisionShapeLibrary.cpp | 6 +- .../CollisionShapes/SHCollisionShapeLibrary.h | 6 +- .../CollisionShapes/SHHalfEdgeStructure.cpp | 68 +-- .../CollisionShapes/SHHalfEdgeStructure.h | 53 ++- .../Collision/Narrowphase/SHCollision.h | 16 + .../Narrowphase/SHSphereVsConvex.cpp | 391 +++++++++++------- .../src/Physics/Collision/SHPhysicsMaterial.h | 4 + 8 files changed, 304 insertions(+), 250 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 731df7ce..4230dcb8 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -4,9 +4,9 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 2.58071685, y: 0.456140399, z: 0} - Rotate: {x: -0, y: 0, z: 0.469853699} - Scale: {x: 4.61071014, y: 0.999995053, z: 1} + Translate: {x: 2.72256827, y: 0.501797795, z: -0.0273017883} + Rotate: {x: -1.48352849, y: -4.78713309e-06, z: 0.469859391} + Scale: {x: 4.61070776, y: 0.99999392, z: 0.999996722} IsActive: true RigidBody Component: Type: Static @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.98839664, y: 7.7662077, z: 0} + Translate: {x: -2.49145222, y: 6.17996597, z: 0.811235607} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -112,7 +112,7 @@ Components: Transform Component: Translate: {x: 0, y: 4.09544182, z: 0} - Rotate: {x: -0, y: 0, z: -0.437829614} + Rotate: {x: -0, y: 0, z: 0} Scale: {x: 4.61071014, y: 0.999995887, z: 1} IsActive: true RigidBody Component: diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp index 338083c7..46adb47b 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp @@ -71,7 +71,7 @@ namespace SHADE box->scale = createInfo.Scale; // Set halfEdge data structure for the box - box->halfEdgeStructure = &boxHalfEdgeDS; + box->halfEdgeStructure = &boxHalfEdgeStructure; return box; } @@ -165,10 +165,10 @@ namespace SHADE for (int j = 0; j < NUM_VERTICES_PER_FACE; ++j) newFace.vertexIndices.emplace_back(FACE_VERTICES[i][j]); - boxHalfEdgeDS.AddFace(newFace); + boxHalfEdgeStructure.AddFace(newFace); } - boxHalfEdgeDS.Build(); + boxHalfEdgeStructure.Build(); } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h index bb9c2807..dbfc4fc7 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h @@ -92,10 +92,10 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHHalfEdgeStructure boxHalfEdgeDS; + SHHalfEdgeStructure boxHalfEdgeStructure; - Spheres spheres; - Boxes boxes; + Spheres spheres; + Boxes boxes; // TODO: Add capsules and hulls /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp index 4c9304c4..a25b5b8b 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp @@ -23,30 +23,6 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHHalfEdgeStructure::HalfEdge::HalfEdge() noexcept - : tailVertexIndex { -1 } - , headVertexIndex { -1 } - , edgeIndex { -1 } - , twinEdgeIndex { -1 } - , faceIndex { -1 } - {} - - SHHalfEdgeStructure::HalfEdge::HalfEdge(const HalfEdge& rhs) noexcept - : tailVertexIndex { rhs.tailVertexIndex } - , headVertexIndex { rhs.headVertexIndex } - , edgeIndex { rhs.edgeIndex } - , twinEdgeIndex { rhs.twinEdgeIndex } - , faceIndex { rhs.faceIndex } - {} - - SHHalfEdgeStructure::HalfEdge::HalfEdge(HalfEdge&& rhs) noexcept - : tailVertexIndex { rhs.tailVertexIndex } - , headVertexIndex { rhs.headVertexIndex } - , edgeIndex { rhs.edgeIndex } - , twinEdgeIndex { rhs.twinEdgeIndex } - , faceIndex { rhs.faceIndex } - {} - SHHalfEdgeStructure::Face::Face(const Face& rhs) noexcept : normal { rhs.normal } { @@ -63,31 +39,6 @@ namespace SHADE /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHHalfEdgeStructure::HalfEdge& SHHalfEdgeStructure::HalfEdge::operator=(const HalfEdge& rhs) noexcept - { - if (this == &rhs) - return *this; - - tailVertexIndex = rhs.tailVertexIndex; - headVertexIndex = rhs.headVertexIndex; - edgeIndex = rhs.edgeIndex ; - twinEdgeIndex = rhs.twinEdgeIndex ; - faceIndex = rhs.faceIndex ; - - return *this; - } - - SHHalfEdgeStructure::HalfEdge& SHHalfEdgeStructure::HalfEdge::operator=(HalfEdge&& rhs) noexcept - { - tailVertexIndex = rhs.tailVertexIndex; - headVertexIndex = rhs.headVertexIndex; - edgeIndex = rhs.edgeIndex ; - twinEdgeIndex = rhs.twinEdgeIndex ; - faceIndex = rhs.faceIndex ; - - return *this; - } - SHHalfEdgeStructure::Face& SHHalfEdgeStructure::Face::operator=(const Face& rhs) noexcept { if (this == &rhs) @@ -165,19 +116,19 @@ namespace SHADE // For each face, build half edges for (size_t i = 0; i < faces.size(); ++i) { - const Face& FACE = faces[i]; + Face& face = faces[i]; - if (FACE.vertexIndices.empty()) + if (face.vertexIndices.empty()) { SHLOGV_CRITICAL("Unable to build convex polyhedron, no vertices have been added to face {}!", i) return; } // Iterate through vertices and build half-edges - for (size_t j = 0; j < FACE.vertexIndices.size(); ++j) + for (size_t j = 0; j < face.vertexIndices.size(); ++j) { - const int32_t TAIL = FACE.vertexIndices[j]; - const int32_t HEAD = FACE.vertexIndices[(j + 1) % FACE.vertexIndices.size()]; + const int32_t TAIL = face.vertexIndices[j].index; + const int32_t HEAD = face.vertexIndices[(j + 1) % face.vertexIndices.size()].index; const uint64_t NEW_EDGE_ID = BUILD_UINT64_FROM_UINT32S(TAIL, HEAD); const uint64_t TWIN_EDGE_ID = BUILD_UINT64_FROM_UINT32S(HEAD, TAIL); @@ -197,6 +148,9 @@ namespace SHADE // Set edge index of the newly inserted edge as the size of the map - 1 // Since it is an unordered map, it will just be at the back newHalfEdge.edgeIndex = static_cast(edgeMap.size()) - 1; + + // Map vertex to this edge index + face.vertexIndices[j].edgeIndex = newHalfEdge.edgeIndex; } // Find twin edge if one exists @@ -217,6 +171,12 @@ namespace SHADE // At this point, no duplicates should be in the map and all edges should be linked. for (auto& halfEdge : edgeMap | std::views::values) halfEdges.emplace_back(halfEdge); + + // Sort based on edge indices + std::ranges::sort(halfEdges.begin(), halfEdges.end(), [](const HalfEdge& lhs, const HalfEdge& rhs) + { + return lhs.edgeIndex < rhs.edgeIndex; + }); } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h index c08803ce..8982ae23 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h @@ -32,6 +32,10 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates the data half edge of a face on a polyhedron. + */ struct HalfEdge { /*-------------------------------------------------------------------------------*/ @@ -40,36 +44,35 @@ namespace SHADE //Head and tail forms the edge. //Head <----- Tail - int32_t tailVertexIndex; + int32_t tailVertexIndex = -1; // Head is also tail of the next edge. - int32_t headVertexIndex; + int32_t headVertexIndex = -1; - int32_t edgeIndex; + int32_t edgeIndex = -1; // Other half of the edge on a different face. // Important for extrapolating face normals. - int32_t twinEdgeIndex; + int32_t twinEdgeIndex = -1; // Adjacent face of this edge. - int32_t faceIndex; - - /*-------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-------------------------------------------------------------------------------*/ - - HalfEdge () noexcept; - HalfEdge (const HalfEdge& rhs) noexcept; - HalfEdge (HalfEdge&& rhs) noexcept; - ~HalfEdge () noexcept = default; - - /*-------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*-------------------------------------------------------------------------------*/ - - HalfEdge& operator= (const HalfEdge& rhs) noexcept; - HalfEdge& operator= (HalfEdge&& rhs) noexcept; + int32_t faceIndex = -1; }; + struct Vertex + { + public: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + int32_t index = -1; + int32_t edgeIndex = -1; // the half-edge that this vertex is a tail of. + }; + + /** + * @brief + * Encapsulates the data of a face on a polyhedron. + */ struct Face { public: @@ -77,10 +80,8 @@ namespace SHADE /* Data Members */ /*-------------------------------------------------------------------------------*/ - // TODO: Store face offset - SHVec3 normal; - std::vector vertexIndices; // Must be in CCW order + std::vector vertexIndices; // Must be in CCW order /*-------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -133,10 +134,6 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - - float radius = 0.2f; // Default Radius is 2 cm - - // Store the faces and half-edges std::vector faces; std::vector halfEdges; diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index b99cc202..942c6919 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -67,10 +67,26 @@ namespace SHADE [[nodiscard]] static bool ConvexVsConvex (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + struct FaceQuery + { + bool colliding = false; + int32_t closestFace = -1; + float bestDistance = std::numeric_limits::lowest(); + }; + /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ + // Sphere VS Convex + + static FaceQuery findClosestFace (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept; + static int32_t findClosestPoint (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept; + static bool isMinkowskiFace(const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; // TODO: buildMinkowskiFace, queryEdgeDirection, distanceBetweenTwoEdges, diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index e62bc241..e84bd16b 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -19,16 +19,6 @@ #include "Physics/Collision/CollisionShapes/SHCollisionShape.h" #include "Physics/Collision/CollisionShapes/SHBoxCollisionShape.h" -// When testing against convex polyhedrons, we do not care so much as whether it is a box -// or something else. We only need the vertices to build half edge structures for use -// with a gauss map. Regardless, we still cast it to the type just to get vertices -// since spheres and capsules do not implement the same method. - -// I did consider having another base class that encapsulates methods that convex polyhedrons -// would implement, but for the sake of my sanity, I won't do that. - -// TODO - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -37,6 +27,21 @@ namespace SHADE bool SHCollision::SphereVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept { + const SHSphereCollisionShape& SPHERE = dynamic_cast(A); + const SHConvexPolyhedronCollisionShape& POLYHEDRON = dynamic_cast(B); + + const SHVec3 CENTER = SPHERE.GetCenter(); + const float RADIUS = SPHERE.GetWorldRadius(); + + const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = POLYHEDRON.GetHalfEdgeStructure(); + + const FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON); + if (!FACE_QUERY.colliding) + return false; + + if (FACE_QUERY.bestDistance < SHMath::EPSILON) + return true; + return false; } @@ -49,19 +54,197 @@ namespace SHADE { // Convert to underlying types // For the convex, we only need the convex polyhedron shape since the get vertex is pure virtual. - const SHSphereCollisionShape& SPHERE = dynamic_cast(A); - const SHConvexPolyhedronCollisionShape& CONVEX = dynamic_cast(B); + const SHSphereCollisionShape& SPHERE = dynamic_cast(A); + const SHConvexPolyhedronCollisionShape& POLYHEDRON = dynamic_cast(B); const SHVec3 CENTER = SPHERE.GetCenter(); const float RADIUS = SPHERE.GetWorldRadius(); + const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = POLYHEDRON.GetHalfEdgeStructure(); + + const FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON); + if (!FACE_QUERY.colliding) + return false; + + uint32_t numContacts = 0; + const float PENETRATION = RADIUS - FACE_QUERY.bestDistance; + + SHContact contact; + contact.featurePair.key = 0; + + // Check if center is inside polyhedron (below the face) + if (FACE_QUERY.bestDistance < SHMath::EPSILON) + { + manifold.normal = -POLYHEDRON.GetNormal(FACE_QUERY.closestFace); + + contact.penetration = PENETRATION; + contact.position = SPHERE.GetCenter(); + + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; + return true; + } + // Find closest face of polygon to circle + const int32_t CLOSEST_POINT = findClosestPoint(SPHERE, POLYHEDRON, FACE_QUERY.closestFace); - int32_t closestFaceIndex = -1; - int32_t closestPointIndex = -1; - float bestDistance = std::numeric_limits::lowest(); + const SHHalfEdgeStructure::Face& FACE = POLYHEDRON.GetHalfEdgeStructure()->GetFace(FACE_QUERY.closestFace); - const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = CONVEX.GetHalfEdgeStructure(); + const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace); + const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); + + // Check against voronoi regions of the face to determine the type of the intersection test + // We have 3 voronoi regions to check: cp -> prev, cp -> next and cp -> center + // If none of these are true, the sphere is above the face but not separating + + /* + * | 2 + * _ _ _ _ _ _ | _ _ _ + * / / + * | / regionD | / regionA + * |/ _ _ _ _ _|/ _ _ _ + * 3/ regionB /1 + * / / regionC + * + */ + + const SHVec3 P1 = POLYHEDRON.GetVertex(FACE.vertexIndices[CLOSEST_POINT].index); + const SHVec3 P1_TO_CENTER = CENTER - P1; + + // To be inside either region A or B, 2 conditions must be satisfied + // 1. Same side as tangent + // 2. Same side as adjacent normal + + // Check in regions A + { + const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; + const SHVec3 P2 = POLYHEDRON.GetVertex(FACE.vertexIndices[P2_INDEX].index); + + const SHVec3 TANGENT = SHVec3::Normalise(P2 - P1); + + float projection = SHVec3::Dot(P1_TO_CENTER, TANGENT); + + if (projection >= 0.0f) + { + // Find closest point + const SHVec3 CP = P1 + projection * TANGENT; + + // Check 2nd condition + // Get adjacent normal from twin half edge + const int32_t EDGE_INDEX = FACE.vertexIndices[CLOSEST_POINT].edgeIndex; + const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; + + const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; + const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); + + projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); + if (projection >= 0.0f) + { + // Must be smaller than radius + if (projection >= RADIUS) + return false; + + const SHVec3 CP_TO_CENTER = CENTER - CP; + + manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); + + contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); + contact.position = CP; + + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; + + return true; + } + } + } + + // Check in region B + { + const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1; + const SHVec3 P3 = POLYHEDRON.GetVertex(FACE.vertexIndices[P3_INDEX].index); + + const SHVec3 TANGENT = SHVec3::Normalise(P3 - P1); + + float projection = SHVec3::Dot(P1_TO_CENTER, TANGENT); + + if (projection >= 0.0f) + { + // Find closest point + const SHVec3 CP = P1 + projection * TANGENT; + + // Check 2nd condition + // Get adjacent normal from twin half edge + const int32_t EDGE_INDEX = FACE.vertexIndices[P3_INDEX].edgeIndex; + const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; + + const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; + const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); + + projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); + if (projection >= 0.0f) + { + // Must be smaller than radius + if (projection >= RADIUS) + return false; + + const SHVec3 CP_TO_CENTER = CENTER - CP; + + manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); + + contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); + contact.position = CP; + + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; + + return true; + } + } + } + + // Either region C or D. Take whichever depth is smaller + + const float C_PENETRATION = RADIUS - P1_TO_CENTER.Length(); + + if (std::fabs(C_PENETRATION) < RADIUS && std::fabs(C_PENETRATION) < PENETRATION) + { + manifold.normal = -SHVec3::Normalise(P1_TO_CENTER); + + contact.penetration = C_PENETRATION; + contact.position = P1; + } + else + { + manifold.normal = -FACE_NORMAL; + + contact.penetration = PENETRATION; + contact.position = CENTER - FACE_NORMAL * RADIUS; + } + + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; + + return true; + } + + bool SHCollision::ConvexVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + { + return SphereVsConvex(manifold, B, A); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollision::FaceQuery SHCollision::findClosestFace(const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept + { + FaceQuery faceQuery; + + const SHVec3 CENTER = sphere.GetCenter(); + const float RADIUS = sphere.GetWorldRadius(); + + const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = polyhedron.GetHalfEdgeStructure(); /* * Test against each face. @@ -76,8 +259,8 @@ namespace SHADE // Build plane equation - const SHVec3 POINT = CONVEX.GetVertex(FACE.vertexIndices[0]); // use the first vertex to build a plane - const SHVec3& NORMAL = CONVEX.GetNormal(i); + const SHVec3 POINT = polyhedron.GetVertex(FACE.vertexIndices[0].index); // use the first vertex to build a plane + const SHVec3& NORMAL = polyhedron.GetNormal(i); const float D = -SHVec3::Dot(NORMAL, POINT); // Find signed distance of center to plane @@ -87,153 +270,47 @@ namespace SHADE // If face is facing away from center, signed dist is negative. // Therefore signed distance is only positive when sphere is in front of the face. if (SIGNED_DIST > RADIUS) - return false; + return faceQuery; - if (SIGNED_DIST > bestDistance) + if (SIGNED_DIST > faceQuery.bestDistance) { - bestDistance = SIGNED_DIST; - closestFaceIndex = i; + faceQuery.bestDistance = SIGNED_DIST; + faceQuery.closestFace = i; } } - uint32_t numContacts = 0; - const float PENETRATION = RADIUS - bestDistance; - - // Check if center is inside polyhedron (below the face) - if (bestDistance < SHMath::EPSILON) - { - manifold.normal = -CONVEX.GetNormal(closestFaceIndex); - - SHContact newContact; - newContact.penetration = PENETRATION; - newContact.position = SPHERE.GetCenter(); - newContact.featurePair.key = 0; - - manifold.contacts[numContacts++] = newContact; - manifold.numContacts = numContacts; - return true; - } - - // Check against voronoi regions of the face to determine the type of the intersection test - // We have 3 voronoi regions to check: cp -> prev, cp -> next and cp -> center - // If none of these are true, the sphere is above the face but not separating - - /* - * | A - * _ _ _ _ _ _ | _ _ _ - * / / - * | / | / regionA - * |/ _ _ _ _ _|/ _ _ _ - * B/ regionB /C - * / / regionC - */ - - const SHHalfEdgeStructure::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(closestFaceIndex); - const SHVec3& NORMAL = CONVEX.GetNormal(closestFaceIndex); - - const int32_t NUM_VERTICES_ON_FACE = static_cast(FACE.vertexIndices.size()); - - // Find closest point on face - closestPointIndex = 0; - float smallestDist = SHVec3::Dot(CENTER - CONVEX.GetVertex(FACE.vertexIndices[0]), NORMAL); - - for (int32_t i = 1; i < NUM_VERTICES_ON_FACE; ++i) - { - const SHVec3 POINT = CONVEX.GetVertex(FACE.vertexIndices[i]); - const float PROJECTION = SHVec3::Dot(CENTER - POINT, NORMAL); - - if (PROJECTION < smallestDist) - closestPointIndex = i; - } - - const SHVec3 C = CONVEX.GetVertex(FACE.vertexIndices[closestPointIndex]); - const SHVec3 C_TO_CENTER = SPHERE.GetCenter() - C; - - const int32_t INDEX_A = (closestPointIndex + 1) % NUM_VERTICES_ON_FACE; - const int32_t INDEX_B = closestPointIndex == 0 ? NUM_VERTICES_ON_FACE - 1 : closestPointIndex - 1; - - const SHVec3 POINTS[2] = - { - CONVEX.GetVertex(FACE.vertexIndices[INDEX_A]) // A - , CONVEX.GetVertex(FACE.vertexIndices[INDEX_B]) // B - }; - - // To be inside either region A or B, 2 conditions must be satisfied - // 1. Same side as tangent - // 2. Same side as normal from edge to sphere - - // Check in regions A & B - for (int i = 0; i < 2; ++i) - { - const SHVec3 TANGENT = SHVec3::Normalise(POINTS[i] - C); - - float projection = SHVec3::Dot(C_TO_CENTER, TANGENT); - if (projection >= 0.0f) - { - // Check 2nd condition - // Find closest point - const SHVec3 CP = C + projection * TANGENT; - const SHVec3 CP_TO_CENTER = SHVec3::Normalise(C - CP); - - projection = SHVec3::Dot(C_TO_CENTER, CP_TO_CENTER); - if (projection >= 0.0f) - { - // Sphere Within region - manifold.normal = -CP_TO_CENTER; - - SHContact newContact; - newContact.penetration = RADIUS - projection; - newContact.position = CP; - newContact.featurePair.key = 0; - - manifold.contacts[numContacts++] = newContact; - manifold.numContacts = numContacts; - - return true; - } - } - } - - // Check region C (closest point) - { - if (C_TO_CENTER.LengthSquared() < RADIUS * RADIUS) - { - manifold.normal = -SHVec3::Normalise(C_TO_CENTER); - - SHContact newContact; - newContact.penetration = PENETRATION; - newContact.position = C; - newContact.featurePair.key = 0; - - manifold.contacts[numContacts++] = newContact; - manifold.numContacts = numContacts; - - return true; - } - } - - // Region D - if (PENETRATION <= RADIUS) - { - manifold.normal = -NORMAL; - - SHContact newContact; - newContact.penetration = PENETRATION; - newContact.position = SPHERE.GetCenter() - NORMAL * RADIUS; - newContact.featurePair.key = 0; - - manifold.contacts[numContacts++] = newContact; - manifold.numContacts = numContacts; - - return true; - } - - return false; + faceQuery.colliding = true; + return faceQuery; } - bool SHCollision::ConvexVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + int32_t SHCollision::findClosestPoint(const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept { - return SphereVsConvex(manifold, B, A); + // Find closest point on face + int32_t closestPointIndex = -1; + + const SHVec3 CENTER = sphere.GetCenter(); + const float RADIUS = sphere.GetWorldRadius(); + + const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = polyhedron.GetHalfEdgeStructure(); + + + const SHHalfEdgeStructure::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(faceIndex); + const int32_t NUM_VERITICES = static_cast(FACE.vertexIndices.size()); + + float smallestDist = std::numeric_limits::max(); + for (int32_t i = 0; i < NUM_VERITICES; ++i) + { + const SHVec3 POINT = polyhedron.GetVertex(FACE.vertexIndices[i].index); + const float DIST = SHVec3::DistanceSquared(CENTER, POINT); + + if (DIST < smallestDist) + { + smallestDist = DIST; + closestPointIndex = i; + } + } + + return closestPointIndex; } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h b/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h index b3db1655..773ac4c1 100644 --- a/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h +++ b/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h @@ -19,6 +19,10 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates the data of a physics material for physics simulations. + */ class SH_API SHPhysicsMaterial { public: From 00f8726e465303d1d12e7a139ae443206803fa06 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 1 Jan 2023 02:42:44 +0800 Subject: [PATCH 060/164] Solved edge case for sphere vs convex polyhedron --- Assets/Scenes/PhysicsSandbox.shade | 6 +- .../Narrowphase/SHSphereVsConvex.cpp | 138 +++++++++--------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 4230dcb8..632dd485 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -5,7 +5,7 @@ Components: Transform Component: Translate: {x: 2.72256827, y: 0.501797795, z: -0.0273017883} - Rotate: {x: -1.48352849, y: -4.78713309e-06, z: 0.469859391} + Rotate: {x: 0, y: 0, z: 0.436332315} Scale: {x: 4.61070776, y: 0.99999392, z: 0.999996722} IsActive: true RigidBody Component: @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -2.49145222, y: 6.17996597, z: 0.811235607} + Translate: {x: -2.01111817, y: 7, z: 0.695722342} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -112,7 +112,7 @@ Components: Transform Component: Translate: {x: 0, y: 4.09544182, z: 0} - Rotate: {x: -0, y: 0, z: 0} + Rotate: {x: -0, y: 0, z: -0.436332315} Scale: {x: 4.61071014, y: 0.999995887, z: 1} IsActive: true RigidBody Component: diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index e84bd16b..568d33a3 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -73,6 +73,7 @@ namespace SHADE contact.featurePair.key = 0; // Check if center is inside polyhedron (below the face) + // TODO: Change to a point in polygon test. if (FACE_QUERY.bestDistance < SHMath::EPSILON) { manifold.normal = -POLYHEDRON.GetNormal(FACE_QUERY.closestFace); @@ -116,112 +117,111 @@ namespace SHADE // 2. Same side as adjacent normal // Check in regions A + const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; + const SHVec3 P2 = POLYHEDRON.GetVertex(FACE.vertexIndices[P2_INDEX].index); + + SHVec3 tangent = SHVec3::Normalise(P2 - P1); + + float projection = SHVec3::Dot(P1_TO_CENTER, tangent); + + if (projection >= 0.0f) { - const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; - const SHVec3 P2 = POLYHEDRON.GetVertex(FACE.vertexIndices[P2_INDEX].index); + // Find closest point + const SHVec3 CP = P1 + projection * tangent; - const SHVec3 TANGENT = SHVec3::Normalise(P2 - P1); + // Check 2nd condition + // Get adjacent normal from twin half edge + const int32_t EDGE_INDEX = FACE.vertexIndices[CLOSEST_POINT].edgeIndex; + const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; - float projection = SHVec3::Dot(P1_TO_CENTER, TANGENT); + const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; + const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); + projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); if (projection >= 0.0f) { - // Find closest point - const SHVec3 CP = P1 + projection * TANGENT; + // Must be smaller than radius + if (projection >= RADIUS) + return false; - // Check 2nd condition - // Get adjacent normal from twin half edge - const int32_t EDGE_INDEX = FACE.vertexIndices[CLOSEST_POINT].edgeIndex; - const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; + const SHVec3 CP_TO_CENTER = CENTER - CP; - const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; - const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); + manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); - projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); - if (projection >= 0.0f) - { - // Must be smaller than radius - if (projection >= RADIUS) - return false; + contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); + contact.position = CP; - const SHVec3 CP_TO_CENTER = CENTER - CP; + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; - manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); - - contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); - contact.position = CP; - - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - return true; - } + return true; } } // Check in region B + const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1; + const SHVec3 P3 = POLYHEDRON.GetVertex(FACE.vertexIndices[P3_INDEX].index); + + tangent = SHVec3::Normalise(P3 - P1); + + projection = SHVec3::Dot(P1_TO_CENTER, tangent); + + if (projection >= 0.0f) { - const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1; - const SHVec3 P3 = POLYHEDRON.GetVertex(FACE.vertexIndices[P3_INDEX].index); + // Find closest point + const SHVec3 CP = P1 + projection * tangent; - const SHVec3 TANGENT = SHVec3::Normalise(P3 - P1); + // Check 2nd condition + // Get adjacent normal from twin half edge + const int32_t EDGE_INDEX = FACE.vertexIndices[P3_INDEX].edgeIndex; + const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; - float projection = SHVec3::Dot(P1_TO_CENTER, TANGENT); + const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; + const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); + projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); if (projection >= 0.0f) { - // Find closest point - const SHVec3 CP = P1 + projection * TANGENT; + // Must be smaller than radius + if (projection >= RADIUS) + return false; - // Check 2nd condition - // Get adjacent normal from twin half edge - const int32_t EDGE_INDEX = FACE.vertexIndices[P3_INDEX].edgeIndex; - const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; + const SHVec3 CP_TO_CENTER = CENTER - CP; - const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; - const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); + manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); - projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); - if (projection >= 0.0f) - { - // Must be smaller than radius - if (projection >= RADIUS) - return false; + contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); + contact.position = CP; - const SHVec3 CP_TO_CENTER = CENTER - CP; + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; - manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); - - contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); - contact.position = CP; - - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - return true; - } + return true; } } - // Either region C or D. Take whichever depth is smaller + // Region C has a negative dot product with any of the tangents. - const float C_PENETRATION = RADIUS - P1_TO_CENTER.Length(); - - if (std::fabs(C_PENETRATION) < RADIUS && std::fabs(C_PENETRATION) < PENETRATION) + projection = SHVec3::Dot(P1_TO_CENTER, tangent); + if (projection < 0) { manifold.normal = -SHVec3::Normalise(P1_TO_CENTER); - contact.penetration = C_PENETRATION; + contact.penetration = RADIUS - P1_TO_CENTER.Length(); contact.position = P1; - } - else - { - manifold.normal = -FACE_NORMAL; - contact.penetration = PENETRATION; - contact.position = CENTER - FACE_NORMAL * RADIUS; + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; + + return true; } + // Region D + manifold.normal = -FACE_NORMAL; + + contact.penetration = PENETRATION; + contact.position = CENTER - FACE_NORMAL * RADIUS; + manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; From 67907b1ca98a4355a8f9708b994be9ed7ca38ab1 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 1 Jan 2023 02:48:02 +0800 Subject: [PATCH 061/164] Replaced twin-edge dependency on sphere vs convex polyhedron --- Assets/Scenes/PhysicsSandbox.shade | 2 +- .../Narrowphase/SHSphereVsConvex.cpp | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 632dd485..266447e3 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -2.01111817, y: 7, z: 0.695722342} + Translate: {x: -0.903104782, y: 7, z: -0.782080948} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 568d33a3..4523fc5b 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -130,12 +130,12 @@ namespace SHADE const SHVec3 CP = P1 + projection * tangent; // Check 2nd condition - // Get adjacent normal from twin half edge - const int32_t EDGE_INDEX = FACE.vertexIndices[CLOSEST_POINT].edgeIndex; - const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; + // Get adjacent normal from the cross product (tangent x normal) + //const int32_t EDGE_INDEX = FACE.vertexIndices[CLOSEST_POINT].edgeIndex; + //const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; - const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; - const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); + //const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; + const SHVec3 ADJ_NORMAL = SHVec3::Cross(tangent, FACE_NORMAL); projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); if (projection >= 0.0f) @@ -172,12 +172,14 @@ namespace SHADE const SHVec3 CP = P1 + projection * tangent; // Check 2nd condition - // Get adjacent normal from twin half edge - const int32_t EDGE_INDEX = FACE.vertexIndices[P3_INDEX].edgeIndex; - const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; + // Get adjacent normal from the cross product (normal x tangent) + //const int32_t EDGE_INDEX = FACE.vertexIndices[P3_INDEX].edgeIndex; + //const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; + + //const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; + //const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); + const SHVec3 ADJ_NORMAL = SHVec3::Cross(FACE_NORMAL, tangent); - const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; - const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); if (projection >= 0.0f) From f3c0bdbcfd45872ebbc4aa5666017db2be18ce10 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 1 Jan 2023 03:24:34 +0800 Subject: [PATCH 062/164] Clean up --- Assets/Scenes/PhysicsSandbox.shade | 2 +- .../SHConvexPolyhedronCollisionShape.cpp | 24 ++ .../SHConvexPolyhedronCollisionShape.h | 11 +- .../Collision/Narrowphase/SHCollision.h | 1 + .../Narrowphase/SHSphereVsConvex.cpp | 239 ++++++++---------- 5 files changed, 144 insertions(+), 133 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 266447e3..a8730476 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -0.903104782, y: 7, z: -0.782080948} + Translate: {x: -1.96328545, y: 7, z: 0.743723333} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp index db6d3621..51d4e684 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp @@ -97,4 +97,28 @@ namespace SHADE { return halfEdgeStructure; } + + int32_t SHConvexPolyhedronCollisionShape::GetFaceCount() const noexcept + { + return halfEdgeStructure->GetFaceCount(); + } + + int32_t SHConvexPolyhedronCollisionShape::GetHalfEdgeCount() const noexcept + { + return halfEdgeStructure->GetHalfEdgeCount(); + } + + const SHHalfEdgeStructure::Face& SHConvexPolyhedronCollisionShape::GetFace(int index) const + { + // Assume it has already been initialised + return halfEdgeStructure->GetFace(index); + } + + const SHHalfEdgeStructure::HalfEdge& SHConvexPolyhedronCollisionShape::GetHalfEdge(int index) const + { + // Assume it has already been initialised + return halfEdgeStructure->GetHalfEdge(index); + } + + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h index 7a0f6df0..6095cbca 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h @@ -57,9 +57,14 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] const SHHalfEdgeStructure* GetHalfEdgeStructure () const noexcept; - [[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0; - [[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0; + [[nodiscard]] const SHHalfEdgeStructure* GetHalfEdgeStructure () const noexcept; + [[nodiscard]] int32_t GetFaceCount () const noexcept; + [[nodiscard]] const SHHalfEdgeStructure::Face& GetFace (int index) const; + [[nodiscard]] int32_t GetHalfEdgeCount () const noexcept; + [[nodiscard]] const SHHalfEdgeStructure::HalfEdge& GetHalfEdge (int index) const; + + [[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0; + [[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0; protected: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 942c6919..4db1fc68 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -86,6 +86,7 @@ namespace SHADE static FaceQuery findClosestFace (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept; static int32_t findClosestPoint (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept; + static int32_t findVoronoiRegion (const SHSphereCollisionShape& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; static bool isMinkowskiFace(const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 4523fc5b..9ee6f4b9 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -33,8 +33,6 @@ namespace SHADE const SHVec3 CENTER = SPHERE.GetCenter(); const float RADIUS = SPHERE.GetWorldRadius(); - const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = POLYHEDRON.GetHalfEdgeStructure(); - const FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON); if (!FACE_QUERY.colliding) return false; @@ -60,8 +58,6 @@ namespace SHADE const SHVec3 CENTER = SPHERE.GetCenter(); const float RADIUS = SPHERE.GetWorldRadius(); - const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = POLYHEDRON.GetHalfEdgeStructure(); - const FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON); if (!FACE_QUERY.colliding) return false; @@ -83,147 +79,75 @@ namespace SHADE manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; + return true; } // Find closest face of polygon to circle const int32_t CLOSEST_POINT = findClosestPoint(SPHERE, POLYHEDRON, FACE_QUERY.closestFace); - const SHHalfEdgeStructure::Face& FACE = POLYHEDRON.GetHalfEdgeStructure()->GetFace(FACE_QUERY.closestFace); + const SHHalfEdgeStructure::Face& FACE = POLYHEDRON.GetFace(FACE_QUERY.closestFace); - const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace); - const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); + const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace); + const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); - // Check against voronoi regions of the face to determine the type of the intersection test - // We have 3 voronoi regions to check: cp -> prev, cp -> next and cp -> center - // If none of these are true, the sphere is above the face but not separating - - /* - * | 2 - * _ _ _ _ _ _ | _ _ _ - * / / - * | / regionD | / regionA - * |/ _ _ _ _ _|/ _ _ _ - * 3/ regionB /1 - * / / regionC - * - */ + // Get points and build tangents + const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; + const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1; const SHVec3 P1 = POLYHEDRON.GetVertex(FACE.vertexIndices[CLOSEST_POINT].index); - const SHVec3 P1_TO_CENTER = CENTER - P1; - - // To be inside either region A or B, 2 conditions must be satisfied - // 1. Same side as tangent - // 2. Same side as adjacent normal - - // Check in regions A - const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; const SHVec3 P2 = POLYHEDRON.GetVertex(FACE.vertexIndices[P2_INDEX].index); - - SHVec3 tangent = SHVec3::Normalise(P2 - P1); - - float projection = SHVec3::Dot(P1_TO_CENTER, tangent); - - if (projection >= 0.0f) - { - // Find closest point - const SHVec3 CP = P1 + projection * tangent; - - // Check 2nd condition - // Get adjacent normal from the cross product (tangent x normal) - //const int32_t EDGE_INDEX = FACE.vertexIndices[CLOSEST_POINT].edgeIndex; - //const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; - - //const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; - const SHVec3 ADJ_NORMAL = SHVec3::Cross(tangent, FACE_NORMAL); - - projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); - if (projection >= 0.0f) - { - // Must be smaller than radius - if (projection >= RADIUS) - return false; - - const SHVec3 CP_TO_CENTER = CENTER - CP; - - manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); - - contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); - contact.position = CP; - - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - return true; - } - } - - // Check in region B - const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1; const SHVec3 P3 = POLYHEDRON.GetVertex(FACE.vertexIndices[P3_INDEX].index); - tangent = SHVec3::Normalise(P3 - P1); + const SHVec3 TANGENT_1 = SHVec3::Normalise(P2 - P1); + const SHVec3 TANGENT_2 = SHVec3::Normalise(P3 - P1); - projection = SHVec3::Dot(P1_TO_CENTER, tangent); + // Get the voronoi region it belongs in + const int32_t REGION = findVoronoiRegion(SPHERE, P1, FACE_NORMAL, TANGENT_1, TANGENT_2); + if (REGION == 0) + return false; - if (projection >= 0.0f) + // Create contact information based on region + + const SHVec3 P1_TO_CENTER = CENTER - P1; + switch (REGION) { - // Find closest point - const SHVec3 CP = P1 + projection * tangent; - - // Check 2nd condition - // Get adjacent normal from the cross product (normal x tangent) - //const int32_t EDGE_INDEX = FACE.vertexIndices[P3_INDEX].edgeIndex; - //const int32_t TWIN_EDGE = HALF_EDGE_STRUCTURE->GetHalfEdge(EDGE_INDEX).twinEdgeIndex; - - //const int32_t ADJ_FACE = HALF_EDGE_STRUCTURE->GetHalfEdge(TWIN_EDGE).faceIndex; - //const SHVec3& ADJ_NORMAL = POLYHEDRON.GetNormal(ADJ_FACE); - const SHVec3 ADJ_NORMAL = SHVec3::Cross(FACE_NORMAL, tangent); - - - projection = SHVec3::Dot(P1_TO_CENTER, ADJ_NORMAL); - if (projection >= 0.0f) + case 1: // Region A + case 2: // Region B { - // Must be smaller than radius - if (projection >= RADIUS) - return false; - + // Find closest point + const SHVec3& TANGENT = REGION == 1 ? TANGENT_1 : TANGENT_2; + const SHVec3 CP = P1 + TANGENT * SHVec3::Dot(P1_TO_CENTER, TANGENT); const SHVec3 CP_TO_CENTER = CENTER - CP; - manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); + manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); - contact.position = CP; + contact.position = CP; - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - return true; + break; } + case 3: // Region C + { + manifold.normal = -SHVec3::Normalise(P1_TO_CENTER); + + contact.penetration = RADIUS - P1_TO_CENTER.Length(); + contact.position = P1; + + break; + } + case 4: // Region D + { + manifold.normal = -FACE_NORMAL; + + contact.penetration = PENETRATION; + contact.position = CENTER - FACE_NORMAL * RADIUS; + + break; + } + default: return false; // Should never happen } - // Region C has a negative dot product with any of the tangents. - - projection = SHVec3::Dot(P1_TO_CENTER, tangent); - if (projection < 0) - { - manifold.normal = -SHVec3::Normalise(P1_TO_CENTER); - - contact.penetration = RADIUS - P1_TO_CENTER.Length(); - contact.position = P1; - - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - return true; - } - - // Region D - manifold.normal = -FACE_NORMAL; - - contact.penetration = PENETRATION; - contact.position = CENTER - FACE_NORMAL * RADIUS; - manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; @@ -246,8 +170,6 @@ namespace SHADE const SHVec3 CENTER = sphere.GetCenter(); const float RADIUS = sphere.GetWorldRadius(); - const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = polyhedron.GetHalfEdgeStructure(); - /* * Test against each face. * @@ -255,9 +177,9 @@ namespace SHADE * 2. Find the signed distance from plane to center of sphere. * 3. Save best distance and face. */ - for (int32_t i = 0; i < HALF_EDGE_STRUCTURE->GetFaceCount(); ++i) + for (int32_t i = 0; i < polyhedron.GetFaceCount(); ++i) { - const SHHalfEdgeStructure::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(i); + const SHHalfEdgeStructure::Face& FACE = polyhedron.GetFace(i); // Build plane equation @@ -291,12 +213,8 @@ namespace SHADE int32_t closestPointIndex = -1; const SHVec3 CENTER = sphere.GetCenter(); - const float RADIUS = sphere.GetWorldRadius(); - - const SHHalfEdgeStructure* HALF_EDGE_STRUCTURE = polyhedron.GetHalfEdgeStructure(); - - const SHHalfEdgeStructure::Face& FACE = HALF_EDGE_STRUCTURE->GetFace(faceIndex); + const SHHalfEdgeStructure::Face& FACE = polyhedron.GetFace(faceIndex); const int32_t NUM_VERITICES = static_cast(FACE.vertexIndices.size()); float smallestDist = std::numeric_limits::max(); @@ -315,4 +233,67 @@ namespace SHADE return closestPointIndex; } + int32_t SHCollision::findVoronoiRegion(const SHSphereCollisionShape& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept + { + static constexpr int NUM_TANGENTS = 2; + + // Check against voronoi regions of the face to determine the type of the intersection test + // We have 3 voronoi regions to check: cp -> prev, cp -> next and cp -> center + // If none of these are true, the sphere is above the face but not separating + + /* + * | 2 + * _ _ _ _ _ _ | _ _ _ + * / / + * | / regionD | / regionA + * |/ _ _ _ _ _|/ _ _ _ + * 3/ regionB /1 + * / / regionC + * + */ + + const SHVec3& CENTER = sphere.GetCenter(); + const float RADIUS = sphere.GetWorldRadius(); + + const SHVec3 TANGENTS [NUM_TANGENTS] { tangent1, tangent2 }; + const SHVec3 ADJACENT_NORMALS [NUM_TANGENTS] { SHVec3::Cross(tangent1, faceNormal), SHVec3::Cross(faceNormal, tangent2) }; + + const SHVec3 FACE_TO_CENTER = CENTER - faceVertex; + + // To be inside either region A or B, 2 conditions must be satisfied + // 1. Same side as tangent + // 2. Same side as adjacent normal + + // Check Region A & B + for (int i = 0; i < NUM_TANGENTS; ++i) + { + float projection = SHVec3::Dot(FACE_TO_CENTER, TANGENTS[i]); + if (projection >= 0.0f) + { + // Find closest point + const SHVec3 CLOSEST_POINT = faceVertex + projection * TANGENTS[i]; + + projection = SHVec3::Dot(FACE_TO_CENTER, ADJACENT_NORMALS[i]); + if (projection >= 0.0f) + { + if (projection > RADIUS) + return 0; + + // Region 1 or 2 ( A or B) + return i + 1; + } + } + } + + // Check Region C + // Face to vertex is in the opposite direction of any tangent. + const float PROJECTION = SHVec3::Dot(FACE_TO_CENTER, tangent1); + if (PROJECTION < 0) + return 3; + + // Belongs in region D by default + return 4; + } + + } // namespace SHADE \ No newline at end of file From 38764e79b31cdcef31f5df8ae4eb2271f264387f Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 1 Jan 2023 03:32:59 +0800 Subject: [PATCH 063/164] Added trigger check for sphere vs convex polyhedron --- .../Narrowphase/SHSphereVsConvex.cpp | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 9ee6f4b9..bc1f5cf6 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -33,14 +33,37 @@ namespace SHADE const SHVec3 CENTER = SPHERE.GetCenter(); const float RADIUS = SPHERE.GetWorldRadius(); + // Find closest face const FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON); if (!FACE_QUERY.colliding) return false; + // If center of sphere is inside the polyhedron (below the face) if (FACE_QUERY.bestDistance < SHMath::EPSILON) return true; - return false; + // Find closest face of polygon to circle + const int32_t CLOSEST_POINT = findClosestPoint(SPHERE, POLYHEDRON, FACE_QUERY.closestFace); + + const auto& FACE = POLYHEDRON.GetFace(FACE_QUERY.closestFace); + const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace); + const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); + + // Get points and build tangents + const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; + const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1; + + const SHVec3 P1 = POLYHEDRON.GetVertex(FACE.vertexIndices[CLOSEST_POINT].index); + const SHVec3 P2 = POLYHEDRON.GetVertex(FACE.vertexIndices[P2_INDEX].index); + const SHVec3 P3 = POLYHEDRON.GetVertex(FACE.vertexIndices[P3_INDEX].index); + + const SHVec3 TANGENT_1 = SHVec3::Normalise(P2 - P1); + const SHVec3 TANGENT_2 = SHVec3::Normalise(P3 - P1); + + // Get the voronoi region it belongs in + const int32_t REGION = findVoronoiRegion(SPHERE, P1, FACE_NORMAL, TANGENT_1, TANGENT_2); + + return REGION > 0; } bool SHCollision::ConvexVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept @@ -69,13 +92,12 @@ namespace SHADE contact.featurePair.key = 0; // Check if center is inside polyhedron (below the face) - // TODO: Change to a point in polygon test. if (FACE_QUERY.bestDistance < SHMath::EPSILON) { manifold.normal = -POLYHEDRON.GetNormal(FACE_QUERY.closestFace); - contact.penetration = PENETRATION; - contact.position = SPHERE.GetCenter(); + contact.penetration = PENETRATION; + contact.position = SPHERE.GetCenter(); manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; @@ -86,10 +108,9 @@ namespace SHADE // Find closest face of polygon to circle const int32_t CLOSEST_POINT = findClosestPoint(SPHERE, POLYHEDRON, FACE_QUERY.closestFace); - const SHHalfEdgeStructure::Face& FACE = POLYHEDRON.GetFace(FACE_QUERY.closestFace); - - const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace); - const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); + const auto& FACE = POLYHEDRON.GetFace(FACE_QUERY.closestFace); + const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace); + const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); // Get points and build tangents const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; @@ -238,7 +259,7 @@ namespace SHADE static constexpr int NUM_TANGENTS = 2; // Check against voronoi regions of the face to determine the type of the intersection test - // We have 3 voronoi regions to check: cp -> prev, cp -> next and cp -> center + // We have 3 voronoi regions to check: 1 -> 2, 1 -> 3 and 1 -> center // If none of these are true, the sphere is above the face but not separating /* From 6f55f202b9a65608c6fe3cb82b8113619483e0ad Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 1 Jan 2023 16:53:13 +0800 Subject: [PATCH 064/164] Added planes --- Assets/Scenes/PhysicsSandbox.shade | 2 +- SHADE_Engine/src/Math/Geometry/SHAABB.cpp | 39 ++--- SHADE_Engine/src/Math/Geometry/SHAABB.h | 11 +- SHADE_Engine/src/Math/Geometry/SHBox.cpp | 27 +--- SHADE_Engine/src/Math/Geometry/SHBox.h | 9 +- SHADE_Engine/src/Math/Geometry/SHPlane.cpp | 141 ++++++++++++++++++ SHADE_Engine/src/Math/Geometry/SHPlane.h | 121 +++++++++++++++ SHADE_Engine/src/Math/Geometry/SHShape.cpp | 35 ----- SHADE_Engine/src/Math/Geometry/SHShape.h | 49 +----- SHADE_Engine/src/Math/Geometry/SHSphere.cpp | 40 ++--- SHADE_Engine/src/Math/Geometry/SHSphere.h | 11 +- .../Narrowphase/SHSphereVsConvex.cpp | 9 +- 12 files changed, 320 insertions(+), 174 deletions(-) create mode 100644 SHADE_Engine/src/Math/Geometry/SHPlane.cpp create mode 100644 SHADE_Engine/src/Math/Geometry/SHPlane.h delete mode 100644 SHADE_Engine/src/Math/Geometry/SHShape.cpp diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index a8730476..95434172 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.96328545, y: 7, z: 0.743723333} + Translate: {x: -1.45715916, y: 7, z: 0.319963396} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Math/Geometry/SHAABB.cpp b/SHADE_Engine/src/Math/Geometry/SHAABB.cpp index 30683216..727ea993 100644 --- a/SHADE_Engine/src/Math/Geometry/SHAABB.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHAABB.cpp @@ -26,14 +26,12 @@ namespace SHADE SHAABB::SHAABB() noexcept { - type = Type::AABB; + Extents = SHVec3::One * 0.5f; } SHAABB::SHAABB(const SHVec3& c, const SHVec3& hE) noexcept { - type = Type::AABB; - - Center = c; + Center = c; Extents = hE; } @@ -43,18 +41,14 @@ namespace SHADE if (this == &rhs) return; - type = Type::AABB; - - Center = rhs.Center; - Extents = rhs.Extents; + Center = rhs.Center; + Extents = rhs.Extents; } SHAABB::SHAABB(SHAABB&& rhs) noexcept { - type = Type::AABB; - - Center = rhs.Center; - Extents = rhs.Extents; + Center = rhs.Center; + Extents = rhs.Extents; } /*-----------------------------------------------------------------------------------*/ @@ -63,14 +57,10 @@ namespace SHADE SHAABB& SHAABB::operator=(const SHAABB& rhs) noexcept { - if (rhs.type != Type::AABB) + if (this != &rhs) { - SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") - } - else if (this != &rhs) - { - Center = rhs.Center; - Extents = rhs.Extents; + Center = rhs.Center; + Extents = rhs.Extents; } return *this; @@ -78,15 +68,8 @@ namespace SHADE SHAABB& SHAABB::operator=(SHAABB&& rhs) noexcept { - if (rhs.type != Type::AABB) - { - SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") - } - else - { - Center = rhs.Center; - Extents = rhs.Extents; - } + Center = rhs.Center; + Extents = rhs.Extents; return *this; } diff --git a/SHADE_Engine/src/Math/Geometry/SHAABB.h b/SHADE_Engine/src/Math/Geometry/SHAABB.h index 76e0b2b7..e6536fa5 100644 --- a/SHADE_Engine/src/Math/Geometry/SHAABB.h +++ b/SHADE_Engine/src/Math/Geometry/SHAABB.h @@ -14,7 +14,6 @@ // Project Headers #include "SHShape.h" -#include "SH_API.h" namespace SHADE { @@ -22,7 +21,11 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - class SH_API SHAABB : public SHShape, + /** + * @brief + * Encapsulates a 3D Axis-Aligned Bounding Box. + */ + class SH_API SHAABB : public SHShape, private DirectX::BoundingBox { public: @@ -71,7 +74,7 @@ namespace SHADE void SetMinMax (const SHVec3& min, const SHVec3& max) noexcept; /*---------------------------------------------------------------------------------*/ - /* Function Members */ + /* Member Functions */ /*---------------------------------------------------------------------------------*/ /** @@ -118,7 +121,7 @@ namespace SHADE [[nodiscard]] float SurfaceArea () const noexcept; /*---------------------------------------------------------------------------------*/ - /* Static Function Members */ + /* Static Member Functions */ /*---------------------------------------------------------------------------------*/ /** diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.cpp b/SHADE_Engine/src/Math/Geometry/SHBox.cpp index 9dacc623..d79f932a 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBox.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHBox.cpp @@ -27,13 +27,11 @@ namespace SHADE SHBox::SHBox() noexcept { - type = Type::BOX; + Extents = SHVec3::One * 0.5f; } SHBox::SHBox(const SHVec3& c, const SHVec3& hE, const SHQuaternion& o) noexcept { - type = Type::BOX; - Center = c; Extents = hE; Orientation = o; @@ -45,8 +43,6 @@ namespace SHADE if (this == &rhs) return; - type = Type::BOX; - Center = rhs.Center; Extents = rhs.Extents; Orientation = rhs.Orientation; @@ -54,8 +50,6 @@ namespace SHADE SHBox::SHBox(SHBox&& rhs) noexcept { - type = Type::BOX; - Center = rhs.Center; Extents = rhs.Extents; Orientation = rhs.Orientation; @@ -67,11 +61,7 @@ namespace SHADE SHBox& SHBox::operator=(const SHBox& rhs) noexcept { - if (rhs.type != Type::BOX) - { - SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") - } - else if (this != &rhs) + if (this != &rhs) { Center = rhs.Center; Extents = rhs.Extents; @@ -83,16 +73,9 @@ namespace SHADE SHBox& SHBox::operator=(SHBox&& rhs) noexcept { - if (rhs.type != Type::BOX) - { - SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") - } - else - { - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - } + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; return *this; } diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.h b/SHADE_Engine/src/Math/Geometry/SHBox.h index bb1098bc..7815ce2c 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBox.h +++ b/SHADE_Engine/src/Math/Geometry/SHBox.h @@ -14,7 +14,6 @@ // Project Headers #include "SHShape.h" -#include "SH_API.h" namespace SHADE { @@ -22,6 +21,10 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates a 3D-oriented bounding box. + */ class SH_API SHBox : public SHShape, public DirectX::BoundingOrientedBox { @@ -57,7 +60,7 @@ namespace SHADE [[nodiscard]] std::vector GetVertices () const noexcept; /*---------------------------------------------------------------------------------*/ - /* Function Members */ + /* Member Functions */ /*---------------------------------------------------------------------------------*/ /** @@ -104,7 +107,7 @@ namespace SHADE [[nodiscard]] float SurfaceArea () const noexcept; /*---------------------------------------------------------------------------------*/ - /* Static Function Members */ + /* Static Member Functions */ /*---------------------------------------------------------------------------------*/ /** diff --git a/SHADE_Engine/src/Math/Geometry/SHPlane.cpp b/SHADE_Engine/src/Math/Geometry/SHPlane.cpp new file mode 100644 index 00000000..1ffbfaba --- /dev/null +++ b/SHADE_Engine/src/Math/Geometry/SHPlane.cpp @@ -0,0 +1,141 @@ +/**************************************************************************************** + * \file SHPlane.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a plane. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +#include + +// Primary Header +#include "SHPlane.h" + +#include "Input/SHInputManager.h" +#include "Math/SHMathHelpers.h" + +using namespace DirectX; + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPlane::SHPlane() noexcept + : planeEquation { SHVec3::One } + { + planeEquation.w = 0.0f; + } + + SHPlane::SHPlane(const SHVec3& point, const SHVec3& normal) noexcept + { + XMStoreFloat4(&planeEquation, XMPlaneFromPointNormal(point, normal)); + } + + SHPlane::SHPlane(float a, float b, float c, float d) noexcept + : planeEquation { a, b, c, d } + {} + + SHPlane::SHPlane(const SHVec3& normal, float distance) noexcept + : planeEquation { normal } + { + planeEquation.w = distance; + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHPlane::operator==(const SHPlane& rhs) const noexcept + { + return XMPlaneEqual(planeEquation, rhs.planeEquation); + } + + bool SHPlane::operator!=(const SHPlane& rhs) const noexcept + { + return XMPlaneNotEqual(planeEquation, rhs.planeEquation); + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHVec3 SHPlane::GetNormal() const noexcept + { + return SHVec3{ planeEquation.x, planeEquation.y, planeEquation.z }; + } + + float SHPlane::GetDistance() const noexcept + { + return planeEquation.w; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPlane::SetNormal(const SHVec3& normal) noexcept + { + planeEquation.x = normal.x; + planeEquation.y = normal.y; + planeEquation.z = normal.z; + } + + void SHPlane::SetDistance(float distance) noexcept + { + planeEquation.w = distance; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHPlane::TestPoint(const SHVec3& point) const noexcept + { + const float DISTANCE = SignedDistance(point); + return SHMath::CompareFloat(DISTANCE, 0.0f); + } + + SHRaycastResult SHPlane::Raycast(const SHRay& ray) const noexcept + { + SHRaycastResult result; + + const SHVec3 N = GetNormal(); + + // Check if ray is parallel to plane + const float N_DOT_D = N.Dot(ray.direction); + if (SHMath::CompareFloat(N_DOT_D, 0.0f)) + { + result.hit = false; + return result; + } + + const float DIST = (planeEquation.w - N.Dot(ray.position)) / N_DOT_D; + if (DIST < 0.0f || !SHMath::CompareFloat(DIST, 0.0f)) + { + result.hit = false; + return result; + } + + result.hit = true; + result.distance = DIST; + result.position = ray.position + ray.direction * DIST; + result.angle = SHVec3::Angle(ray.position, result.position); + + // The normal is either the plane's normal or the reverse if the ray came from below + result.normal = N_DOT_D < 0.0f ? -N : N; + + return result; + } + + float SHPlane::SignedDistance(const SHVec3& point) const noexcept + { + return XMVectorGetX(XMPlaneDotCoord(planeEquation, point)); + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHPlane.h b/SHADE_Engine/src/Math/Geometry/SHPlane.h new file mode 100644 index 00000000..581b1c03 --- /dev/null +++ b/SHADE_Engine/src/Math/Geometry/SHPlane.h @@ -0,0 +1,121 @@ +/**************************************************************************************** + * \file SHPlane.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a plane. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "SHShape.h" +#include "Math/Vector/SHVec4.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Encapsulates a 3D plane in point-normal form. + */ + class SH_API SHPlane : public SHShape + { + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + ~SHPlane () override = default; + SHPlane (const SHPlane& rhs) noexcept = default; + SHPlane (SHPlane&& rhs) noexcept = default; + + SHPlane () noexcept; + SHPlane (const SHVec3& point, const SHVec3& normal) noexcept; + SHPlane (float a, float b, float c, float d) noexcept; + SHPlane (const SHVec3& normal, float distance) noexcept; + + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHPlane& operator= (const SHPlane& rhs) noexcept = default; + SHPlane& operator= (SHPlane&& rhs) noexcept = default; + + bool operator== (const SHPlane& rhs) const noexcept; + bool operator!= (const SHPlane& rhs) const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] SHVec3 GetNormal () const noexcept; + [[nodiscard]] float GetDistance () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetNormal (const SHVec3& normal) noexcept; + void SetDistance (float distance) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Checks if a point is on the plane. + * @param point + * The point to check. + * @return + * True if the point is on the plane. + */ + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + + /** + * @brief + * Casts a ray against the plane. + * @param ray + * The ray to cast. + * @return + * The result of the raycast.
+ * See the corresponding header for the contents of the raycast result object. + */ + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + + /** + * @brief + * Gets the signed distance from a point to the plane. + * @param point + * The point to check. + * @return + * The signed distance of the point to a plane.
+ * If the signed distance is negative, the point is behind the plane.
+ * If the signed distance is zero, the point is on the plane.
+ * If the signed distance is positive, the point is in front of the plane.
+ */ + [[nodiscard]] float SignedDistance (const SHVec3& point) const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Static Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /* + * TODO: + * Transform plane + * Intersection Tests + */ + + private: + + SHVec4 planeEquation; + }; + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHShape.cpp b/SHADE_Engine/src/Math/Geometry/SHShape.cpp deleted file mode 100644 index 2f869029..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHShape.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/**************************************************************************************** - * \file SHShape.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a shape. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHShape.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHShape::SHShape() - : type { Type::NONE } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHShape::Type SHShape::GetType() const noexcept - { - return type; - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHShape.h b/SHADE_Engine/src/Math/Geometry/SHShape.h index 2b644917..7781a5a4 100644 --- a/SHADE_Engine/src/Math/Geometry/SHShape.h +++ b/SHADE_Engine/src/Math/Geometry/SHShape.h @@ -11,58 +11,26 @@ #pragma once // Project Headers -#include "SH_API.h" #include "Math/SHRay.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates a base class for any shape. + */ class SH_API SHShape { public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - enum class Type - { - SPHERE - , AABB - , BOX - - , COUNT - , NONE = -1 - }; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - bool isIntersecting; - /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - virtual ~SHShape () = default; - - SHShape (const SHShape&) = default; - SHShape (SHShape&&) = default; - - SHShape& operator=(const SHShape&) = default; - SHShape& operator=(SHShape&&) = default; - - SHShape(); - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] Type GetType () const noexcept; + virtual ~SHShape () = default; /*---------------------------------------------------------------------------------*/ /* Function Members */ @@ -70,12 +38,5 @@ namespace SHADE [[nodiscard]] virtual bool TestPoint (const SHVec3& point) const noexcept = 0; [[nodiscard]] virtual SHRaycastResult Raycast (const SHRay& ray) const noexcept = 0; - - protected: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - Type type; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp index 7dbc8f41..ab05188b 100644 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHBoundingSphere.cpp + * \file SHSphere.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Implementation for a Bounding Sphere * @@ -26,13 +26,11 @@ namespace SHADE SHSphere::SHSphere() noexcept { - type = Type::SPHERE; + Radius = 0.5f; } SHSphere::SHSphere(const SHVec3& center, float radius) noexcept { - type = Type::SPHERE; - Center = center; Radius = radius; } @@ -42,18 +40,14 @@ namespace SHADE if (this == &rhs) return; - type = Type::SPHERE; - - Center = rhs.Center; - Radius = rhs.Radius; + Center = rhs.Center; + Radius = rhs.Radius; } SHSphere::SHSphere(SHSphere&& rhs) noexcept { - type = Type::SPHERE; - - Center = rhs.Center; - Radius = rhs.Radius; + Center = rhs.Center; + Radius = rhs.Radius; } /*-----------------------------------------------------------------------------------*/ @@ -62,14 +56,10 @@ namespace SHADE SHSphere& SHSphere::operator=(const SHSphere& rhs) noexcept { - if (rhs.type != Type::SPHERE) + if (this != &rhs) { - SHLOG_WARNING("Cannot assign a non-sphere to a sphere!") - } - else if (this != &rhs) - { - Center = rhs.Center; - Radius = rhs.Radius; + Center = rhs.Center; + Radius = rhs.Radius; } return *this; @@ -77,15 +67,9 @@ namespace SHADE SHSphere& SHSphere::operator=(SHSphere&& rhs) noexcept { - if (rhs.type != Type::SPHERE) - { - SHLOG_WARNING("Cannot assign a non-sphere to a sphere!") - } - else - { - Center = rhs.Center; - Radius = rhs.Radius; - } + + Center = rhs.Center; + Radius = rhs.Radius; return *this; } diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.h b/SHADE_Engine/src/Math/Geometry/SHSphere.h index 80a21aa4..3c534ac4 100644 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.h +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.h @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHBoundingSphere.h + * \file SHSphere.h * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Interface for a Bounding Sphere. * @@ -14,7 +14,6 @@ // Project Headers #include "SHShape.h" -#include "SH_API.h" namespace SHADE { @@ -22,6 +21,10 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /** + * @brief + * Encapsulates a 3D Sphere. + */ class SH_API SHSphere : public SHShape, public DirectX::BoundingSphere { @@ -45,7 +48,7 @@ namespace SHADE SHSphere& operator= (SHSphere&& rhs) noexcept; /*---------------------------------------------------------------------------------*/ - /* Function Members */ + /* Member Functions */ /*---------------------------------------------------------------------------------*/ /** @@ -93,7 +96,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ - /* Static Function Members */ + /* Static Member Functions */ /*---------------------------------------------------------------------------------*/ /** diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index bc1f5cf6..3827dc2f 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -15,6 +15,7 @@ #include "SHCollision.h" // Project Headers +#include "Math/Geometry/SHPlane.h" #include "Math/SHMathHelpers.h" #include "Physics/Collision/CollisionShapes/SHCollisionShape.h" #include "Physics/Collision/CollisionShapes/SHBoxCollisionShape.h" @@ -203,13 +204,11 @@ namespace SHADE const SHHalfEdgeStructure::Face& FACE = polyhedron.GetFace(i); // Build plane equation - - const SHVec3 POINT = polyhedron.GetVertex(FACE.vertexIndices[0].index); // use the first vertex to build a plane - const SHVec3& NORMAL = polyhedron.GetNormal(i); - const float D = -SHVec3::Dot(NORMAL, POINT); + // Use first vertex to build the plane + const SHPlane FACE_PLANE { polyhedron.GetVertex(FACE.vertexIndices[0].index), polyhedron.GetNormal(i) }; // Find signed distance of center to plane - const float SIGNED_DIST = SHVec3::Dot(NORMAL, CENTER) + D; + const float SIGNED_DIST = FACE_PLANE.SignedDistance(CENTER); // Early out: // If face is facing away from center, signed dist is negative. From 50de3a8ef07a6acd515ab0292257eb64532a53d6 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 1 Jan 2023 17:15:49 +0800 Subject: [PATCH 065/164] Added some todo comments --- .../Collision/Narrowphase/SHCollision.h | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 4db1fc68..9255a4bc 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -84,21 +84,54 @@ namespace SHADE // Sphere VS Convex - static FaceQuery findClosestFace (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept; - static int32_t findClosestPoint (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept; - static int32_t findVoronoiRegion (const SHSphereCollisionShape& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; + static FaceQuery findClosestFace + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron - static bool isMinkowskiFace(const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; + ) noexcept; - // TODO: buildMinkowskiFace, queryEdgeDirection, distanceBetweenTwoEdges, + static int32_t findClosestPoint + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + , int32_t faceIndex + + ) noexcept; + + static int32_t findVoronoiRegion + ( + const SHSphereCollisionShape& sphere + , const SHVec3& faceVertex + , const SHVec3& faceNormal + , const SHVec3& tangent1 + , const SHVec3& tangent2 + + ) noexcept; + + // Capsule VS Convex + + // TODO: Capsule VS Convex uses the same gauss map optimisation as convex vs convex + + // Convex VS Convex + + static bool isMinkowskiFace + ( + const SHVec3& a + , const SHVec3& b + , const SHVec3& c + , const SHVec3& d + + ) noexcept; /* * TODO: - * static FaceQuery queryFaceDirections (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - * static EdgeQuery queryEdgeDirections (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - * static bool buildMinkowskiFace (const SHHalfEdge& edgeA, const SHHalfEdge& edgeB) noexcept; - * static float distanceBetweenEdges(const SHHalfEdge& edgeA, const SHHalfEdge& edgeB, SHCollisionShape& poly) noexcept; - * static uint32_t clip + * static FaceQuery queryFaceDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + * static EdgeQuery queryEdgeDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + * static bool buildMinkowskiFace (const SHConvexPolyhedron::HalfEdge& edgeA, const SHConvexPolyhedron::HalfEdge& edgeB) noexcept; + * static float distanceBetweenEdges(const SHConvexPolyhedron::HalfEdge& edgeA, const SHConvexPolyhedron::HalfEdge& edgeB, SHConvexPolyhedron& poly) noexcept; + * static int32_t findIncidentFace (SHConvexPolyhedron& poly, const SHVec3& normal) noexcept; + * static uint32_t clip [sutherland-hodgemann clipping] * * ! References * https://ia801303.us.archive.org/30/items/GDC2013Gregorius/GDC2013-Gregorius.pdf From 7a92c2c86f09ed57abd980f1218962f070c7afb7 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 1 Jan 2023 17:23:06 +0800 Subject: [PATCH 066/164] Reverted a change --- .../Collision/Narrowphase/SHCollision.h | 36 +++---------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 9255a4bc..14ff5509 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -84,30 +84,9 @@ namespace SHADE // Sphere VS Convex - static FaceQuery findClosestFace - ( - const SHSphereCollisionShape& sphere - , const SHConvexPolyhedronCollisionShape& polyhedron - - ) noexcept; - - static int32_t findClosestPoint - ( - const SHSphereCollisionShape& sphere - , const SHConvexPolyhedronCollisionShape& polyhedron - , int32_t faceIndex - - ) noexcept; - - static int32_t findVoronoiRegion - ( - const SHSphereCollisionShape& sphere - , const SHVec3& faceVertex - , const SHVec3& faceNormal - , const SHVec3& tangent1 - , const SHVec3& tangent2 - - ) noexcept; + static FaceQuery findClosestFace (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept; + static int32_t findClosestPoint (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept; + static int32_t findVoronoiRegion (const SHSphereCollisionShape& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; // Capsule VS Convex @@ -115,14 +94,7 @@ namespace SHADE // Convex VS Convex - static bool isMinkowskiFace - ( - const SHVec3& a - , const SHVec3& b - , const SHVec3& c - , const SHVec3& d - - ) noexcept; + static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; /* * TODO: From ddfbc71400d176e14fd6d57d4222620ae04e8a82 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 1 Jan 2023 19:39:16 +0800 Subject: [PATCH 067/164] Added implementation for raycasting into the collision space --- SHADE_Engine/src/Math/Geometry/SHAABB.cpp | 2 + SHADE_Engine/src/Math/Geometry/SHBox.cpp | 2 + SHADE_Engine/src/Math/Geometry/SHSphere.cpp | 2 + .../Broadphase/SHDynamicAABBTree.cpp | 28 ++++ .../CollisionShapes/SHCollisionShape.h | 9 +- .../Physics/Collision/SHCollisionSpace.cpp | 157 ++++++++++++++++++ .../src/Physics/Collision/SHCollisionSpace.h | 93 ++++++++++- 7 files changed, 281 insertions(+), 12 deletions(-) diff --git a/SHADE_Engine/src/Math/Geometry/SHAABB.cpp b/SHADE_Engine/src/Math/Geometry/SHAABB.cpp index 727ea993..f5d9fd60 100644 --- a/SHADE_Engine/src/Math/Geometry/SHAABB.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHAABB.cpp @@ -159,6 +159,8 @@ namespace SHADE { result.position = ray.position + ray.direction * result.distance; result.angle = SHVec3::Angle(ray.position, result.position); + + // TODO: Compute normal } return result; diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.cpp b/SHADE_Engine/src/Math/Geometry/SHBox.cpp index d79f932a..bcdb7324 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBox.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHBox.cpp @@ -111,6 +111,8 @@ namespace SHADE { result.position = ray.position + ray.direction * result.distance; result.angle = SHVec3::Angle(ray.position, result.position); + + // TODO: Compute Normal } return result; diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp index ab05188b..9d7a7c68 100644 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp @@ -92,6 +92,8 @@ namespace SHADE { result.position = ray.position + ray.direction * result.distance; result.angle = SHVec3::Angle(ray.position, result.position); + + // TODO: Compute Normal } return result; diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp index 127494cb..dc87d706 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp @@ -249,9 +249,37 @@ namespace SHADE const std::vector& SHAABBTree::Query(const SHRay& ray, float distance) const noexcept { static std::vector potentialHits; + static std::stack nodeIndices; potentialHits.clear(); + nodeIndices.push(root); + while (!nodeIndices.empty()) + { + const int32_t INDEX = nodeIndices.top(); + nodeIndices.pop(); + + if (INDEX == NULL_NODE) + continue; + + const Node& NODE = nodes[INDEX]; + + const auto& RESULT = NODE.AABB.Raycast(ray); + if (!RESULT || RESULT.distance > distance) + continue; + + if (isLeaf(INDEX)) + { + potentialHits.emplace_back(NODE.id); + } + else + { + // Non-leaf nodes need to be traversed further + nodeIndices.push(NODE.left); + nodeIndices.push(NODE.right); + } + } + return potentialHits; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 0a320444..729eecf7 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -19,6 +19,7 @@ #include "SHCollisionShapeID.h" #include "Math/Geometry/SHAABB.h" #include "Math/Transform/SHTransform.h" +#include "Physics/Collision/SHPhysicsRaycastResult.h" namespace SHADE { @@ -128,10 +129,10 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - virtual void ComputeTransforms () noexcept = 0; - [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; - [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; - [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; + virtual void ComputeTransforms () noexcept = 0; + [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; + [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; + [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp index ab4520ee..cbca890e 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp @@ -160,6 +160,163 @@ namespace SHADE contactManager->Update(); } + const SHCollisionSpace::RaycastHits& SHCollisionSpace::Raycast(const SHRay& ray, float distance, uint16_t layer) noexcept + { + raycastHits.clear(); + + const auto& POTENTIAL_HITS = broadphase.Query(ray, distance); + if (POTENTIAL_HITS.empty()) + return raycastHits; + + // Test potential hits individually + // Cull entities that are on different layers + for (auto& shapeID : POTENTIAL_HITS) + { + // Get shape + const EntityID EID = shapeID.GetEntityID(); + const uint32_t IDX = shapeID.GetShapeIndex(); + + const auto* COLLIDER = colliders.find(EID)->second; + const auto* SHAPE = COLLIDER->GetCollisionShape(IDX); + + // Cull by layer + const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & layer; + if (!LAYER_MATCH) + continue; + + // Well this is awkward...I honestly don't have the mental capacity to solve this oversight right now. + // Cast the underlying shape to raycast. Convex hulls do not have an inherited raycast function + + SHRaycastResult result; + switch (SHAPE->GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + result = dynamic_cast(SHAPE)->Raycast(ray); + break; + } + case SHCollisionShape::Type::BOX: + { + result = dynamic_cast(SHAPE)->Raycast(ray); + break; + } + case SHCollisionShape::Type::CAPSULE: + { + // TODO + break; + } + default: break; + } + + // If distance is greater than specified, skip this result + if (!result || result.distance > distance) + continue; + + SHPhysicsRaycastResult physicsResult; + physicsResult.hit = result.hit; + physicsResult.distance = result.distance; + physicsResult.angle = result.angle; + physicsResult.position = result.position; + physicsResult.normal = result.normal; + physicsResult.entityHit = EID; + physicsResult.shapeIndex = IDX; + + raycastHits.emplace_back(physicsResult); + } + + // Sort by distance + std::ranges::sort(raycastHits.begin(), raycastHits.end(), [](const SHPhysicsRaycastResult& lhs, const SHPhysicsRaycastResult& rhs) + { + return lhs.distance < rhs.distance; + }); + + return raycastHits; + } + + const SHCollisionSpace::RaycastHits& SHCollisionSpace::Linecast(const SHVec3& start, const SHVec3& end, uint16_t layer) noexcept + { + // Create bounded ray and reuse the raycast function + const SHRay BOUNDED_RAY{ start, SHVec3::Normalise(end - start) }; + const float DISTANCE = SHVec3::Distance(end, start); + + return Raycast(BOUNDED_RAY, DISTANCE, layer); + } + + const SHCollisionSpace::RaycastHits& SHCollisionSpace::ColliderRaycast(EntityID colliderEID, const SHRay& ray, float distance, uint16_t layer) noexcept + { + raycastHits.clear(); + + const auto& POTENTIAL_HITS = broadphase.Query(ray, distance); + if (POTENTIAL_HITS.empty()) + return raycastHits; + + // Test potential hits individually + // Cull entities that are on different layers + for (auto& shapeID : POTENTIAL_HITS) + { + // Get shape + const EntityID EID = shapeID.GetEntityID(); + const auto* COLLIDER = colliders.find(EID)->second; + // Cull any shapes on the same entity + if (EID == colliderEID) + continue; + + const uint32_t IDX = shapeID.GetShapeIndex(); + const auto* SHAPE = COLLIDER->GetCollisionShape(IDX); + + // Cull by layer + const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & layer; + if (!LAYER_MATCH) + continue; + + // Well this is awkward...I honestly don't have the mental capacity to solve this oversight right now. + // Cast the underlying shape to raycast. Convex hulls do not have an inherited raycast function + + SHRaycastResult result; + switch (SHAPE->GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + result = dynamic_cast(SHAPE)->Raycast(ray); + break; + } + case SHCollisionShape::Type::BOX: + { + result = dynamic_cast(SHAPE)->Raycast(ray); + break; + } + case SHCollisionShape::Type::CAPSULE: + { + // TODO + break; + } + default: break; + } + + // If distance is greater than specified, skip this result + if (!result || result.distance > distance) + continue; + + SHPhysicsRaycastResult physicsResult; + physicsResult.hit = result.hit; + physicsResult.distance = result.distance; + physicsResult.angle = result.angle; + physicsResult.position = result.position; + physicsResult.normal = result.normal; + physicsResult.entityHit = EID; + physicsResult.shapeIndex = IDX; + + raycastHits.emplace_back(physicsResult); + } + + // Sort by distance + std::ranges::sort(raycastHits.begin(), raycastHits.end(), [](const SHPhysicsRaycastResult& lhs, const SHPhysicsRaycastResult& rhs) + { + return lhs.distance < rhs.distance; + }); + + return raycastHits; + } /*-----------------------------------------------------------------------------------*/ /* Private Member Functions Definitions */ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h index d89404dd..9d52a140 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h @@ -15,9 +15,10 @@ // Project Headers #include "Broadphase/SHDynamicAABBTree.h" -#include "Contacts/SHCollisionEvents.h" #include "Physics/Dynamics/SHContactManager.h" #include "SHCollider.h" +#include "SHPhysicsRaycastResult.h" +#include "CollisionTags/SHCollisionTags.h" namespace SHADE { @@ -33,6 +34,12 @@ namespace SHADE class SH_API SHCollisionSpace { public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using RaycastHits = std::vector; + /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ @@ -63,7 +70,7 @@ namespace SHADE * @param collider * A collider to add. Duplicates will be ignored. */ - void AddCollider (SHCollider* collider) noexcept; + void AddCollider (SHCollider* collider) noexcept; /** * @brief @@ -72,21 +79,87 @@ namespace SHADE * @param collider * A collider to remove. If a reference to it doesn't exist, it will be ignored. */ - void RemoveCollider (SHCollider* collider) noexcept; + void RemoveCollider (SHCollider* collider) noexcept; /** * @brief * Invoke this method to update the broadphase of colliders that have been moved since * the last frame. */ - void UpdateBroadphase () noexcept; + void UpdateBroadphase () noexcept; /** * @brief * Detects collisions between all colliders. Results are sent to the attached contact * manager for resolution. */ - void DetectCollisions () noexcept; + void DetectCollisions () noexcept; + + /** + * @brief + * Casts a ray into the collision space. + * @param ray + * The ray to cast. The direction of the ray must be normalised. + * @param distance + * The distance to cast the ray. Defaults to infinity. + * @param layer + * The layer(s) the ray is casting on. Defaults to all layers. + * @return + * A container of all the objects the raycast hit, ordered by distance.
+ * The first object in the container is the first object hit etc. + */ + [[nodiscard]] const RaycastHits& Raycast + ( + const SHRay& ray + , float distance = std::numeric_limits::infinity() + , uint16_t layer = static_cast(SHCollisionTag::Layer::ALL) + ) noexcept; + + /** + * @brief + * Casts a bounded ray into the collision space. + * @param start + * The origin of the ray. + * @param end + * The end of the ray. + * @param layer + * The layer(s) the ray is casting on. Defaults to all layers. + * @return + * A container of all the objects the raycast hit, ordered by distance.
+ * The first object in the container is the first object hit etc. + */ + [[nodiscard]] const RaycastHits& Linecast + ( + const SHVec3& start + , const SHVec3& end + , uint16_t layer = static_cast(SHCollisionTag::Layer::ALL) + ) noexcept; + + /** + * @brief + * Casts a ray into the collision space from a collider's position.
+ * The collider and all it's shapes will be ignored. + * @param colliderEID + * The entityID of the collider to cast from. + * @param ray + * The ray to cast.
+ * The position of the ray is the position offset from the collider's position.
+ * The direction of the ray must be normalised. + * @param distance + * The distance to cast the ray. Defaults to infinity. + * @param layer + * The layer(s) the ray is casting on. Defaults to all layers. + * @return + * A container of all the objects the raycast hit, ordered by distance.
+ * The first object in the container is the first object hit etc. + */ + [[nodiscard]] const RaycastHits& ColliderRaycast + ( + EntityID colliderEID + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + , uint16_t layer = static_cast(SHCollisionTag::Layer::ALL) + ) noexcept; private: /*---------------------------------------------------------------------------------*/ @@ -111,20 +184,24 @@ namespace SHADE Colliders colliders; NarrowphaseBatch narrowphaseBatch; + RaycastHits raycastHits; // Reusable container for raycast results + SHAABBTree broadphase; + + /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ // Broadphase helpers - void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; + void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; // Narrowphase helpers - void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; - void collideManifolds (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; + void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; + void collideManifolds (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; }; From 58a44997b2dd50a63e5fc57f6e1a0123110eb417 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 2 Jan 2023 22:31:48 +0800 Subject: [PATCH 068/164] Reworked raycasting on engine side. Re-added raycasting to scripting --- Assets/Scenes/PhysicsSandbox.shade | 2 +- .../Collision/Narrowphase/SHCollision.h | 24 +- .../Narrowphase/SHSphereVsConvex.cpp | 22 +- .../Physics/Collision/SHCollisionSpace.cpp | 160 +++------- .../src/Physics/Collision/SHCollisionSpace.h | 126 ++++---- .../src/Physics/System/SHPhysicsSystem.cpp | 20 ++ .../src/Physics/System/SHPhysicsSystem.h | 28 ++ .../System/SHPhysicsSystemInterface.cpp | 12 + .../Physics/System/SHPhysicsSystemInterface.h | 13 +- SHADE_Managed/src/Components/Collider.cxx | 34 +- SHADE_Managed/src/Components/Collider.hxx | 25 +- SHADE_Managed/src/Physics/Physics.cxx | 293 ++++++++++++++++-- SHADE_Managed/src/Physics/Physics.hxx | 114 +++++-- SHADE_Managed/src/Utility/Convert.cxx | 33 ++ SHADE_Managed/src/Utility/Convert.hxx | 14 + 15 files changed, 627 insertions(+), 293 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 95434172..b07549cc 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.45715916, y: 7, z: 0.319963396} + Translate: {x: -1.45715916, y: 7, z: 0.64831841} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 14ff5509..0c65a94f 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -84,9 +84,27 @@ namespace SHADE // Sphere VS Convex - static FaceQuery findClosestFace (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept; - static int32_t findClosestPoint (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept; - static int32_t findVoronoiRegion (const SHSphereCollisionShape& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; + static FaceQuery findClosestFace + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + ) noexcept; + + static int32_t findClosestPoint + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + , int32_t faceIndex + ) noexcept; + + static int32_t findVoronoiRegion + ( + const SHSphereCollisionShape& sphere + , const SHVec3& faceVertex + , const SHVec3& faceNormal + , const SHVec3& tangent1 + , const SHVec3& tangent2 + ) noexcept; // Capsule VS Convex diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 3827dc2f..8245d672 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -185,7 +185,11 @@ namespace SHADE /* Private Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollision::FaceQuery SHCollision::findClosestFace(const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept + SHCollision::FaceQuery SHCollision::findClosestFace + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + ) noexcept { FaceQuery faceQuery; @@ -227,7 +231,12 @@ namespace SHADE return faceQuery; } - int32_t SHCollision::findClosestPoint(const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept + int32_t SHCollision::findClosestPoint + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + , int32_t faceIndex + ) noexcept { // Find closest point on face int32_t closestPointIndex = -1; @@ -253,7 +262,14 @@ namespace SHADE return closestPointIndex; } - int32_t SHCollision::findVoronoiRegion(const SHSphereCollisionShape& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept + int32_t SHCollision::findVoronoiRegion + ( + const SHSphereCollisionShape& sphere + , const SHVec3& faceVertex + , const SHVec3& faceNormal + , const SHVec3& tangent1 + , const SHVec3& tangent2 + ) noexcept { static constexpr int NUM_TANGENTS = 2; diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp index cbca890e..4dcc7cb7 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp @@ -160,44 +160,53 @@ namespace SHADE contactManager->Update(); } - const SHCollisionSpace::RaycastHits& SHCollisionSpace::Raycast(const SHRay& ray, float distance, uint16_t layer) noexcept + const SHCollisionSpace::RaycastResults& SHCollisionSpace::Raycast(const RaycastInfo& info) noexcept { - raycastHits.clear(); + static RaycastResults results; + results.clear(); + + const bool FILTER_COLLIDER = info.colliderEntityID.has_value(); + + // Cast ray into the broadphase scene + const auto& POTENTIAL_HITS = broadphase.Query(info.ray, info.distance); - const auto& POTENTIAL_HITS = broadphase.Query(ray, distance); if (POTENTIAL_HITS.empty()) - return raycastHits; + return results; - // Test potential hits individually - // Cull entities that are on different layers - for (auto& shapeID : POTENTIAL_HITS) + // Iterate through all potential hits + const int NUM_HITS = static_cast(POTENTIAL_HITS.size()); + for (const int i : std::ranges::views::iota(0, NUM_HITS)) { + const auto HIT_ID = POTENTIAL_HITS[i]; + + const EntityID EID = HIT_ID.GetEntityID(); + + if (FILTER_COLLIDER && EID == info.colliderEntityID.value()) + continue; + // Get shape - const EntityID EID = shapeID.GetEntityID(); - const uint32_t IDX = shapeID.GetShapeIndex(); + const uint32_t IDX = HIT_ID.GetShapeIndex(); + const auto* SHAPE = colliders.find(EID)->second->GetCollisionShape(IDX); - const auto* COLLIDER = colliders.find(EID)->second; - const auto* SHAPE = COLLIDER->GetCollisionShape(IDX); - - // Cull by layer - const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & layer; + // Filter the layers + const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & info.layers; if (!LAYER_MATCH) continue; - // Well this is awkward...I honestly don't have the mental capacity to solve this oversight right now. - // Cast the underlying shape to raycast. Convex hulls do not have an inherited raycast function + // We cast to the underlying shape. THis is done because a convex hull will not have an inherited raycast method. + // Kinda awkward oversight...oops - SHRaycastResult result; + SHRaycastResult baseResult; switch (SHAPE->GetType()) { case SHCollisionShape::Type::SPHERE: { - result = dynamic_cast(SHAPE)->Raycast(ray); + baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); break; } case SHCollisionShape::Type::BOX: { - result = dynamic_cast(SHAPE)->Raycast(ray); + baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); break; } case SHCollisionShape::Type::CAPSULE: @@ -205,118 +214,25 @@ namespace SHADE // TODO break; } - default: break; + default: continue; // Redundant case } - // If distance is greater than specified, skip this result - if (!result || result.distance > distance) + if (!baseResult || baseResult.distance > info.distance) continue; - SHPhysicsRaycastResult physicsResult; - physicsResult.hit = result.hit; - physicsResult.distance = result.distance; - physicsResult.angle = result.angle; - physicsResult.position = result.position; - physicsResult.normal = result.normal; - physicsResult.entityHit = EID; - physicsResult.shapeIndex = IDX; + // Copy to a physics raycast result + SHPhysicsRaycastResult result; + memcpy_s(&result, sizeof(SHRaycastResult), &baseResult, sizeof(SHRaycastResult)); - raycastHits.emplace_back(physicsResult); + result.entityHit = EID; + result.shapeIndex = IDX; + + results.emplace_back(result); } - // Sort by distance - std::ranges::sort(raycastHits.begin(), raycastHits.end(), [](const SHPhysicsRaycastResult& lhs, const SHPhysicsRaycastResult& rhs) - { - return lhs.distance < rhs.distance; - }); - - return raycastHits; + return results; } - const SHCollisionSpace::RaycastHits& SHCollisionSpace::Linecast(const SHVec3& start, const SHVec3& end, uint16_t layer) noexcept - { - // Create bounded ray and reuse the raycast function - const SHRay BOUNDED_RAY{ start, SHVec3::Normalise(end - start) }; - const float DISTANCE = SHVec3::Distance(end, start); - - return Raycast(BOUNDED_RAY, DISTANCE, layer); - } - - const SHCollisionSpace::RaycastHits& SHCollisionSpace::ColliderRaycast(EntityID colliderEID, const SHRay& ray, float distance, uint16_t layer) noexcept - { - raycastHits.clear(); - - const auto& POTENTIAL_HITS = broadphase.Query(ray, distance); - if (POTENTIAL_HITS.empty()) - return raycastHits; - - // Test potential hits individually - // Cull entities that are on different layers - for (auto& shapeID : POTENTIAL_HITS) - { - // Get shape - const EntityID EID = shapeID.GetEntityID(); - const auto* COLLIDER = colliders.find(EID)->second; - // Cull any shapes on the same entity - if (EID == colliderEID) - continue; - - const uint32_t IDX = shapeID.GetShapeIndex(); - const auto* SHAPE = COLLIDER->GetCollisionShape(IDX); - - // Cull by layer - const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & layer; - if (!LAYER_MATCH) - continue; - - // Well this is awkward...I honestly don't have the mental capacity to solve this oversight right now. - // Cast the underlying shape to raycast. Convex hulls do not have an inherited raycast function - - SHRaycastResult result; - switch (SHAPE->GetType()) - { - case SHCollisionShape::Type::SPHERE: - { - result = dynamic_cast(SHAPE)->Raycast(ray); - break; - } - case SHCollisionShape::Type::BOX: - { - result = dynamic_cast(SHAPE)->Raycast(ray); - break; - } - case SHCollisionShape::Type::CAPSULE: - { - // TODO - break; - } - default: break; - } - - // If distance is greater than specified, skip this result - if (!result || result.distance > distance) - continue; - - SHPhysicsRaycastResult physicsResult; - physicsResult.hit = result.hit; - physicsResult.distance = result.distance; - physicsResult.angle = result.angle; - physicsResult.position = result.position; - physicsResult.normal = result.normal; - physicsResult.entityHit = EID; - physicsResult.shapeIndex = IDX; - - raycastHits.emplace_back(physicsResult); - } - - // Sort by distance - std::ranges::sort(raycastHits.begin(), raycastHits.end(), [](const SHPhysicsRaycastResult& lhs, const SHPhysicsRaycastResult& rhs) - { - return lhs.distance < rhs.distance; - }); - - return raycastHits; - } /*-----------------------------------------------------------------------------------*/ /* Private Member Functions Definitions */ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h index 9d52a140..8607cf45 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h @@ -13,12 +13,15 @@ #pragma once +#include + // Project Headers #include "Broadphase/SHDynamicAABBTree.h" #include "Physics/Dynamics/SHContactManager.h" #include "SHCollider.h" #include "SHPhysicsRaycastResult.h" #include "CollisionTags/SHCollisionTags.h" +#include "CollisionTags/SHCollisionTags.h" namespace SHADE { @@ -38,7 +41,52 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using RaycastHits = std::vector; + /** + * @brief + * Contains information to cast a ray into the collision space. + * The collider entityID and shape index is optional. + */ + struct RaycastInfo + { + private: + /*-------------------------------------------------------------------------------*/ + /* Friends */ + /*-------------------------------------------------------------------------------*/ + + friend class SHCollisionSpace; + + public: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + bool continuous = false; + uint16_t layers = static_cast(SHCollisionTag::Layer::ALL); + float distance = std::numeric_limits::infinity(); + SHRay ray; + + /*-------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*-------------------------------------------------------------------------------*/ + + /** + * @brief + * Sets the collider ID for the raycast. Setting this specifies that the ray + * should ignore this collider. + * @param eid + * The entity ID of the collider. + */ + void SetColliderID(EntityID eid) noexcept { colliderEntityID = eid; } + + private: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + std::optional colliderEntityID; + }; + + using RaycastResults = std::vector; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -98,68 +146,13 @@ namespace SHADE /** * @brief * Casts a ray into the collision space. - * @param ray - * The ray to cast. The direction of the ray must be normalised. - * @param distance - * The distance to cast the ray. Defaults to infinity. - * @param layer - * The layer(s) the ray is casting on. Defaults to all layers. + * @param info + * Contains the information for the raycast. * @return - * A container of all the objects the raycast hit, ordered by distance.
- * The first object in the container is the first object hit etc. + * A container of the objects hit by the ray. If nothing was hit, the container + * will be empty. */ - [[nodiscard]] const RaycastHits& Raycast - ( - const SHRay& ray - , float distance = std::numeric_limits::infinity() - , uint16_t layer = static_cast(SHCollisionTag::Layer::ALL) - ) noexcept; - - /** - * @brief - * Casts a bounded ray into the collision space. - * @param start - * The origin of the ray. - * @param end - * The end of the ray. - * @param layer - * The layer(s) the ray is casting on. Defaults to all layers. - * @return - * A container of all the objects the raycast hit, ordered by distance.
- * The first object in the container is the first object hit etc. - */ - [[nodiscard]] const RaycastHits& Linecast - ( - const SHVec3& start - , const SHVec3& end - , uint16_t layer = static_cast(SHCollisionTag::Layer::ALL) - ) noexcept; - - /** - * @brief - * Casts a ray into the collision space from a collider's position.
- * The collider and all it's shapes will be ignored. - * @param colliderEID - * The entityID of the collider to cast from. - * @param ray - * The ray to cast.
- * The position of the ray is the position offset from the collider's position.
- * The direction of the ray must be normalised. - * @param distance - * The distance to cast the ray. Defaults to infinity. - * @param layer - * The layer(s) the ray is casting on. Defaults to all layers. - * @return - * A container of all the objects the raycast hit, ordered by distance.
- * The first object in the container is the first object hit etc. - */ - [[nodiscard]] const RaycastHits& ColliderRaycast - ( - EntityID colliderEID - , const SHRay& ray - , float distance = std::numeric_limits::infinity() - , uint16_t layer = static_cast(SHCollisionTag::Layer::ALL) - ) noexcept; + [[nodiscard]] const RaycastResults& Raycast(const RaycastInfo& info) noexcept; private: /*---------------------------------------------------------------------------------*/ @@ -184,25 +177,20 @@ namespace SHADE Colliders colliders; NarrowphaseBatch narrowphaseBatch; - RaycastHits raycastHits; // Reusable container for raycast results - SHAABBTree broadphase; - - /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ // Broadphase helpers - void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; + void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; // Narrowphase helpers - void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; - void collideManifolds (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; - + void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; + void collideManifolds (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 508c19ad..f9e7a3cd 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -19,6 +19,7 @@ #include "ECS_Base/Managers/SHEntityManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.h" +#include "Physics/Collision/SHCollisionSpace.h" #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Physics/Interface/SHColliderComponent.h" #include "Scripting/SHScriptEngine.h" @@ -189,6 +190,25 @@ namespace SHADE } } + const std::vector& SHPhysicsSystem::Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept + { + auto& results = collisionSpace->Raycast(info); + + // Load start and end points into the container for debug drawing + #ifdef SHEDITOR + + SHVec3 endPos = info.ray.position + info.ray.direction * SHRay::MAX_RAYCAST_DIST; + + if (!results.empty()) + endPos = results.back().position; + + raycastHits.emplace_back(info.ray.position, endPos); + + #endif + + return results; + } + /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index c7dff6c6..712727ec 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -76,8 +76,25 @@ namespace SHADE */ void Init() override; void Exit() override; + + /** + * @brief + * Forces the world to take a single step. Interpolation of bodies cannot be done when + * forcing an update. + */ void ForceUpdate(); + /** + * @brief + * Casts a ray into the collision space. + * @param info + * Contains the information for the raycast. + * @return + * A container of the objects hit by the ray. If nothing was hit, the container + * will be empty. + */ + [[nodiscard]] const std::vector& Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept; + /*---------------------------------------------------------------------------------*/ /* System Routines */ /*---------------------------------------------------------------------------------*/ @@ -129,6 +146,12 @@ namespace SHADE using EventFunctionPair = std::pair; + struct RaycastHit + { + SHVec3 start; + SHVec3 end; + }; + /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ @@ -149,6 +172,11 @@ namespace SHADE SHCollisionSpace* collisionSpace; SHPhysicsObjectManager physicsObjectManager; + // For the debug drawer to draw rays + #ifdef SHEDITOR + std::vector raycastHits; + #endif + /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp index f72eb271..b6ed9d56 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp @@ -67,4 +67,16 @@ namespace SHADE return 0.0; } + const std::vector& SHPhysicsSystemInterface::Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept + { + static std::vector emptyVec; + + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + return physicsSystem->Raycast(info); + + SHLOGV_WARNING("Failed to get fixed update rate. 0.0 returned instead."); + return emptyVec; + } + } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h index e6103e87..8a1f6be8 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h @@ -14,6 +14,7 @@ of DigiPen Institute of Technology is prohibited. // Project Headers #include "ECS_Base/Entity/SHEntity.h" +#include "Physics/Collision/SHCollisionSpace.h" #include "Physics/Collision/Contacts/SHCollisionEvents.h" @@ -26,6 +27,7 @@ namespace SHADE class SHVec3; struct SHRay; struct SHPhysicsRaycastResult; + struct SHCollisionSpace::RaycastInfo; /*-----------------------------------------------------------------------------------*/ @@ -47,9 +49,12 @@ namespace SHADE /* Static Usage Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static const std::vector& GetCollisionInfo () noexcept; - [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; - [[nodiscard]] static double GetFixedDT () noexcept; - [[nodiscard]] static int GetFixedUpdateRate () noexcept; + [[nodiscard]] static const std::vector& GetCollisionInfo () noexcept; + [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; + [[nodiscard]] static double GetFixedDT () noexcept; + [[nodiscard]] static int GetFixedUpdateRate () noexcept; + [[nodiscard]] static const std::vector& Raycast (const SHCollisionSpace::RaycastInfo& info) noexcept; + + }; } diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index 79140d2b..9ed46289 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -128,39 +128,19 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 BoxCollider::Center::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetCenter()); - return Vector3::Zero; - } - void BoxCollider::Center::set(Vector3 value) - { - //getNativeCollisionShape().SetCenter(Convert::ToNative(value)); + return Convert::ToCLI(getNativeCollisionShape().GetCenter()); } Vector3 BoxCollider::HalfExtents::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetExtents()); - return Vector3::Zero; + return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); } void BoxCollider::HalfExtents::set(Vector3 value) { - //getNativeCollisionShape().SetExtents(Convert::ToNative(value)); + getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); } - Vector3 BoxCollider::Min::get() + Quaternion BoxCollider::Orientation::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetMin()); - return Vector3::Zero; - } - void BoxCollider::Min::set(Vector3 value) - { - //getNativeCollisionShape().SetMin(Convert::ToNative(value)); - } - Vector3 BoxCollider::Max::get() - { - //return Convert::ToCLI(getNativeCollisionShape().GetMax()); - return Vector3::Zero; - } - void BoxCollider::Max::set(Vector3 value) - { - //getNativeCollisionShape().SetMax(Convert::ToNative(value)); + return Convert::ToCLI(getNativeCollisionShape().GetOrientation()); } /*---------------------------------------------------------------------------------*/ @@ -184,10 +164,6 @@ namespace SHADE { return Convert::ToCLI(getNativeCollisionShape().GetCenter()); } - void SphereCollider::Center::set(Vector3 value) - { - getNativeCollisionShape().SetCenter(Convert::ToNative(value)); - } float SphereCollider::Radius::get() { return getNativeCollisionShape().GetWorldRadius(); diff --git a/SHADE_Managed/src/Components/Collider.hxx b/SHADE_Managed/src/Components/Collider.hxx index a649483f..19235571 100644 --- a/SHADE_Managed/src/Components/Collider.hxx +++ b/SHADE_Managed/src/Components/Collider.hxx @@ -142,15 +142,14 @@ namespace SHADE /* Properties */ /*-----------------------------------------------------------------------------*/ /// - /// Center of the Bounding Box formed by this bound. + /// Center of the box collider. /// property Vector3 Center { Vector3 get(); - void set(Vector3 value); } /// - /// Half of the scale of the Bounding Box formed by this bound. + /// Half of the scale of the box collider. /// property Vector3 HalfExtents { @@ -158,22 +157,11 @@ namespace SHADE void set(Vector3 value); } /// - /// Position of the bottom left back corner of the Bounding Box formed by this - /// bound. + /// The orientation of the box. /// - property Vector3 Min + property Quaternion Orientation { - Vector3 get(); - void set(Vector3 value); - } - /// - /// Position of the top right front corner of the Bounding Box formed by this - /// bound. - /// - property Vector3 Max - { - Vector3 get(); - void set(Vector3 value); + Quaternion get(); } /*-----------------------------------------------------------------------------*/ @@ -201,12 +189,11 @@ namespace SHADE /* Properties */ /*-----------------------------------------------------------------------------*/ /// - /// Center of the Bounding Sphere formed by this bound. + /// Center of the sphere. /// property Vector3 Center { Vector3 get(); - void set(Vector3 value); } /// /// Radius of the Bounding Sphere formed by this bound. diff --git a/SHADE_Managed/src/Physics/Physics.cxx b/SHADE_Managed/src/Physics/Physics.cxx index fc54f527..bfb79b75 100644 --- a/SHADE_Managed/src/Physics/Physics.cxx +++ b/SHADE_Managed/src/Physics/Physics.cxx @@ -15,10 +15,15 @@ // External Dependencies #include "Physics/System/SHPhysicsSystemInterface.h" // Project Header +#include "Components/Collider.hxx" +#include "Components/Transform.hxx" #include "Engine/GameObject.hxx" +#include "Physics/Collision/SHCollisionSpace.h" #include "Utility/Convert.hxx" #include "Utility/Debug.hxx" +using namespace System::Collections::Generic; + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -41,49 +46,301 @@ namespace SHADE /* Raycast Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - RaycastHit Physics::Raycast(Ray ray) + List^ Physics::Raycast(Ray ray, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Cast natively + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.continuous = continuous; + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::Raycast(Ray ray, float distance) + List^ Physics::Raycast(Ray ray, float distance, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Cast natively + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.distance = distance; + raycastInfo.continuous = continuous; + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::Linecast(Vector3 start, Vector3 end) + List^ Physics::Linecast(Vector3 start, Vector3 end, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Cast natively + Vector3 direction = end - start; + direction.Normalise(); + const Ray CLI_RAY( start, direction ); + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(CLI_RAY); + raycastInfo.distance = (end - start).GetMagnitude(); + raycastInfo.continuous = continuous; + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderRaycast(GameObject object, Ray ray) + List^ Physics::ColliderRaycast(GameObject object, Ray ray, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position (same as the transform) + Transform^ managedTransform = object.GetComponent(); + if (!managedTransform) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); + + const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; + ray.Position += COLLIDER_POS; + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.continuous = continuous; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderRaycast(GameObject object, Ray ray, float distance) + List^ Physics::ColliderRaycast(GameObject object, Ray ray, float distance, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position (same as the transform) + Transform^ managedTransform = object.GetComponent(); + if (!managedTransform) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); + + const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; + ray.Position += COLLIDER_POS; + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.distance = distance; + raycastInfo.continuous = continuous; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray) + List^ Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position + Vector3 shapePos = Vector3::Zero; + Collider^ managedCollider = object.GetComponent(); + if (!managedCollider) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); + + CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); + if (!managedShape) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); + + const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); + switch (NATIVE_SHAPE.GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + case SHCollisionShape::Type::BOX: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + default: break; + } + + ray.Position += shapePos; + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.continuous = continuous; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, float distance) + List^ Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, float distance, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position + Vector3 shapePos = Vector3::Zero; + Collider^ managedCollider = object.GetComponent(); + if (!managedCollider) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); + + CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); + if (!managedShape) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); + + const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); + switch (NATIVE_SHAPE.GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + case SHCollisionShape::Type::BOX: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + default: break; + } + + ray.Position += shapePos; + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.continuous = continuous; + raycastInfo.distance = distance; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderLineCast(GameObject object, Vector3 start, Vector3 end) + List^ Physics::ColliderLineCast(GameObject object, Vector3 start, Vector3 end, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position (same as the transform) + Transform^ managedTransform = object.GetComponent(); + if (!managedTransform) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); + + const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; + start += COLLIDER_POS; + + Vector3 direction = end - start; + direction.Normalise(); + const Ray CLI_RAY( start, direction ); + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(CLI_RAY); + raycastInfo.distance = (end - start).GetMagnitude(); + raycastInfo.continuous = continuous; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderLineCast(GameObject object, int shapeIndex, Vector3 start, Vector3 end) + List^ Physics::ColliderLineCast(GameObject object, int shapeIndex, Vector3 start, Vector3 end, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position + Vector3 shapePos = Vector3::Zero; + Collider^ managedCollider = object.GetComponent(); + if (!managedCollider) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); + + CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); + if (!managedShape) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); + + const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); + switch (NATIVE_SHAPE.GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + case SHCollisionShape::Type::BOX: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + default: break; + } + + start += shapePos; + + Vector3 direction = end - start; + direction.Normalise(); + const Ray CLI_RAY( start, direction ); + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(CLI_RAY); + raycastInfo.continuous = continuous; + raycastInfo.distance = (end - start).GetMagnitude(); + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Physics/Physics.hxx b/SHADE_Managed/src/Physics/Physics.hxx index f13e5952..abbd1891 100644 --- a/SHADE_Managed/src/Physics/Physics.hxx +++ b/SHADE_Managed/src/Physics/Physics.hxx @@ -10,6 +10,7 @@ #pragma once + // Project Includes #include "Math/Ray.hxx" #include "RaycastHit.hxx" @@ -39,83 +40,146 @@ namespace SHADE /* Raycast Function Members */ /*---------------------------------------------------------------------------------*/ + // TODO(Diren): Add layers for raycasting + /// - /// Casts an infinite ray into the world. + /// Casts an infinite ray into the world.
+ /// This raycast will stop at the first object hit. ///
/// The ray to cast. - /// The result of the raycast. - static RaycastHit Raycast (Ray ray); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ Raycast (Ray ray, bool continuous); /// /// Casts a ray for a given distance into the world. /// /// The ray to cast. /// The distance to cast the ray. - /// The result of the raycast. - static RaycastHit Raycast (Ray ray, float distance); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ Raycast (Ray ray, float distance, bool continuous); /// /// Casts a bounded ray into the world. /// /// The start of the bounded ray. /// The end of the bounded ray. - /// The result of the raycast. - static RaycastHit Linecast (Vector3 start, Vector3 end); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ Linecast (Vector3 start, Vector3 end, bool continuous); /// /// Casts an infinite ray w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// The ray to cast. - /// The result of the raycast. - static RaycastHit ColliderRaycast (GameObject object, Ray ray); + /// + /// The ray to cast.
+ /// The position of the ray is offset from the collider's position. + /// + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderRaycast (GameObject object, Ray ray, bool continuous); /// /// Casts a ray for a given distance w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// The ray to cast. + /// + /// The ray to cast.
+ /// The position of the ray is offset from the collider's position. + /// /// The distance to cast the ray. - /// The result of the raycast. - static RaycastHit ColliderRaycast (GameObject object, Ray ray, float distance); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderRaycast (GameObject object, Ray ray, float distance, bool continuous); /// /// Casts an infinite ray w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// The ray to cast. - /// The result of the raycast. - static RaycastHit ColliderRaycast (GameObject object, int shapeIndex, Ray ray); + /// + /// The ray to cast.
+ /// The position of the ray is offset from the collider's position. + /// + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderRaycast (GameObject object, int shapeIndex, Ray ray, bool continuous); /// /// Casts a ray for a given distance w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// The ray to cast. + /// + /// The ray to cast.
+ /// The position of the ray is offset from the collider's position. + /// /// The distance to cast the ray. - /// The result of the raycast. - static RaycastHit ColliderRaycast (GameObject object, int shapeIndex, Ray ray, float distance); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderRaycast (GameObject object, int shapeIndex, Ray ray, float distance, bool continuous); /// /// Casts a bounded ray w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// The start of the bounded ray. + /// + /// The start of the bounded ray.
+ /// The start of the ray is offset from the collider's position. /// - /// The result of the raycast. - static RaycastHit ColliderLineCast (GameObject object, Vector3 start, Vector3 end); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderLineCast (GameObject object, Vector3 start, Vector3 end, bool continuous); /// /// Casts a bounded ray w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// The start of the bounded ray. + /// + /// The start of the bounded ray.
+ /// The start of the ray is offset from the collider's position. /// The end of the bounded ray. - /// The result of the raycast. - static RaycastHit ColliderLineCast (GameObject object, int shapeIndex, Vector3 start, Vector3 end); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned./// + static System::Collections::Generic::List^ ColliderLineCast (GameObject object, int shapeIndex, Vector3 start, Vector3 end, bool continuous); private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Utility/Convert.cxx b/SHADE_Managed/src/Utility/Convert.cxx index 1d277274..e3a53661 100644 --- a/SHADE_Managed/src/Utility/Convert.cxx +++ b/SHADE_Managed/src/Utility/Convert.cxx @@ -103,6 +103,39 @@ namespace SHADE /* Physics Conversions */ /*---------------------------------------------------------------------------------*/ + SHPhysicsRaycastResult Convert::ToNative(RaycastHit cli) + { + // This function shouldn't be used anyway, so we leave the entityHit empty. + + SHPhysicsRaycastResult native; + + native.hit = cli.Hit; + native.position = ToNative(cli.Position); + native.normal = ToNative(cli.Normal); + native.distance = cli.Distance; + native.shapeIndex = cli.CollisionShapeIndex; + + return native; + } + + RaycastHit Convert::ToCLI(const SHPhysicsRaycastResult& native) + { + RaycastHit cli; + + cli.Hit = native.hit; + cli.Position = ToCLI(native.position); + cli.Normal = ToCLI(native.normal); + cli.Distance = native.distance; + cli.CollisionShapeIndex = native.shapeIndex; + + cli.Other = SHEntityManager::IsValidEID(native.entityHit) + ? GameObject(native.entityHit) + : System::Nullable(); + + return cli; + } + + /*---------------------------------------------------------------------------------*/ /* Handle Conversions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Utility/Convert.hxx b/SHADE_Managed/src/Utility/Convert.hxx index 6fa4d935..f48eb66d 100644 --- a/SHADE_Managed/src/Utility/Convert.hxx +++ b/SHADE_Managed/src/Utility/Convert.hxx @@ -141,6 +141,20 @@ namespace SHADE /* Physics Conversions */ /*-----------------------------------------------------------------------------*/ + /// + /// Converts from a managed RaycastHit to a native SHPhysicsRaycastResult + /// + /// The managed RaycastHit to convert from. + /// Native copy of a managed RaycastHit. + static SHPhysicsRaycastResult ToNative(RaycastHit cli); + + /// + /// Converts from a native SHPhysicsRaycastResult to a managed RaycastHit + /// + /// The native SHPhysicsRaycastResult to convert from. + /// Managed copy of a native SHPhysicsRaycastResult. + static RaycastHit ToCLI(const SHPhysicsRaycastResult& native); + /*-----------------------------------------------------------------------------*/ /* Handle Conversions */ /*-----------------------------------------------------------------------------*/ From 1f2a9820d1cc96a7d78e2842969a04e8f7c27cc4 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 2 Jan 2023 22:49:12 +0800 Subject: [PATCH 069/164] Readded collision tags and moved collision filtering to an earlier stage --- Assets/Scenes/PhysicsSandbox.shade | 4 ++-- .../EditorWindow/Inspector/SHEditorComponentView.hpp | 1 + .../Physics/Collision/Narrowphase/SHCollisionDispatch.cpp | 7 ------- SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp | 8 ++++++++ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index b07549cc..a02f3b24 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.45715916, y: 7, z: 0.64831841} + Translate: {x: -1.45715916, y: 7, z: 0.0329093337} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -136,7 +136,7 @@ DrawColliders: false Colliders: - Is Trigger: false - Collision Tag: 1 + Collision Tag: 2 Type: Box Half Extents: {x: 1, y: 1, z: 1} Friction: 0.400000006 diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 6117dfe8..a46e2550 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -376,6 +376,7 @@ namespace SHADE { SHEditorWidgets::CheckBox("Is Trigger", [shape] { return shape->IsTrigger(); }, [shape](bool value) { shape->SetIsTrigger(value); }); + SHEditorWidgets::ComboBox("Tag", collisionTagNames, [shape]{return SHCollisionTagMatrix::GetTagIndex(shape->GetCollisionTag().GetName());}, [shape](int const& value){shape->SetCollisionTag(SHCollisionTagMatrix::GetTag(value));}); if(ImGui::CollapsingHeader("Physics Material")) { diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp index 4a949d77..e11902e0 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp @@ -65,13 +65,6 @@ namespace SHADE if (!collisionTable[TYPE_A][TYPE_B]) return false; - - // Filter through tags - const uint16_t TAG_A = A.GetCollisionTag().GetMask(); - const uint16_t TAG_B = B.GetCollisionTag().GetMask(); - - const uint16_t MATCH = TAG_A & TAG_B; - return MATCH > 0; } bool SHCollisionDispatcher::Collide(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp index 4dcc7cb7..88fdac40 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp @@ -143,6 +143,14 @@ namespace SHADE // This applies both ways: A -> B and B -> A. for (auto& [key, narrowphasePair] : narrowphaseBatch) { + // Filter through tags before attempting narrow-phase + const uint16_t TAG_A = narrowphasePair.A->GetCollisionTag().GetMask(); + const uint16_t TAG_B = narrowphasePair.B->GetCollisionTag().GetMask(); + + const bool MATCH_TAG = TAG_A & TAG_B; + if (!MATCH_TAG) + continue; + const bool IS_A_TRIGGER = narrowphasePair.A->IsTrigger(); const bool IS_B_TRIGGER = narrowphasePair.B->IsTrigger(); From c1910db2af0a6022a2670f691a31c0a4f5f9b6ca Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 3 Jan 2023 00:23:37 +0800 Subject: [PATCH 070/164] Fleshed out SHAnimatorComponent --- .../src/Animation/SHAnimationClip.cpp | 4 + SHADE_Engine/src/Animation/SHAnimationClip.h | 22 ++- .../src/Animation/SHAnimatorComponent.cpp | 132 +++++++++++++++++- .../src/Animation/SHAnimatorComponent.h | 58 +++++++- SHADE_Engine/src/Animation/SHRig.cpp | 12 ++ SHADE_Engine/src/Animation/SHRig.h | 7 + .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 1 + 7 files changed, 220 insertions(+), 16 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp index 1f199b3f..4ef91a39 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -19,6 +19,10 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Constructors */ /*-----------------------------------------------------------------------------------*/ + SHAnimationClip::SHAnimationClip(const SHAnimAsset& asset) + { + + } /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h index ed1b7b81..d8415942 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.h +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -19,15 +19,12 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - + /// + /// Defines a single key frame in an animation. + /// struct SHAnimationKeyFrame { float TimeStamp; @@ -37,11 +34,19 @@ namespace SHADE SHMatrix TransformationMatrix; }; + /// + /// Represents a animation clip of a 3D animation that is made for a specific model + /// rig. + /// class SHAnimationClip { + public: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ + /// + /// Defines the animations of a single bone in a rig. + /// struct Channel { std::string Name; @@ -56,12 +61,15 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ + const std::vector& GetChannels() const noexcept { return channels; } + float GetTotalTime() const noexcept { return totalTime; } private: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - std::vector Channels; + std::vector channels; + float totalTime; /*---------------------------------------------------------------------------------*/ /* Helper Functions */ diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index debb2fb2..629453a6 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -13,12 +13,50 @@ of DigiPen Institute of Technology is prohibited. #include "SHpch.h" // Primary Include #include "SHAnimatorComponent.h" +// STL Includes +#include // Project Includes #include "SHRig.h" #include "Math/SHMatrix.h" +#include "SHAnimationClip.h" +#include "Graphics/SHVkUtil.h" +#include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" +#include "ECS_Base/Managers/SHSystemManager.h" namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------------*/ + void SHAnimatorComponent::Play() + { + isPlaying = false; + } + + void SHAnimatorComponent::Play(Handle clip) + { + currClip = clip; + currPlaybackTime = 0.0f; + Play(); + } + + void SHAnimatorComponent::PlayFromStart() + { + isPlaying = true; + currPlaybackTime = 0.0f; + } + + void SHAnimatorComponent::Pause() + { + isPlaying = false; + } + + void SHAnimatorComponent::Stop() + { + isPlaying = false; + currPlaybackTime = 0.0f; + } + /*-----------------------------------------------------------------------------------*/ /* Setter Functions */ /*-----------------------------------------------------------------------------------*/ @@ -31,14 +69,21 @@ namespace SHADE rig = newRig; // Populate bone matrices based on new rig's default pose + boneMatrices.clear(); if (rig) { + std::fill_n(std::back_inserter(boneMatrices), rig->GetNodeCount(), SHMatrix::Identity); + } + } - } - else - { - boneMatrices.clear(); - } + void SHAnimatorComponent::SetClip(Handle newClip) + { + // No change + if (currClip == newClip) + return; + + currClip = newClip; + updatePoseWithClip(0.0f); } /*-----------------------------------------------------------------------------------*/ @@ -46,10 +91,85 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ void SHAnimatorComponent::Update(float dt) { - // Set everything to identity + // Nothing to animate + if (!currClip || !isPlaying) + return; + + // Update time on the playback + currPlaybackTime += dt; + if (currPlaybackTime > currClip->GetTotalTime()) + { + currPlaybackTime = currPlaybackTime - currClip->GetTotalTime(); + } + + // Reset all matrices + for (auto& mat : boneMatrices) + { + mat = SHMatrix::Identity; + } + + // Play the clip + updatePoseWithClip(currPlaybackTime); } + + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------------*/ + void SHAnimatorComponent::updatePoseWithClip(float poseTime) + { + for (const auto& channel : currClip->GetChannels()) + { + // Get the bone + std::queue> bones; + bones.push(rig->GetNode(channel.Name)); + while (!bones.empty()) + { + // Select bone at front of the queue + auto bone = bones.front(); bones.pop(); + if (!bone) + continue; + + // Add any children to the queue + for (auto child : bone->Children) + { + bones.push(child); + } + + // Get interpolated frame + auto firstKeyFrame = channel.KeyFrames.end(); + auto nextKeyFrame = channel.KeyFrames.end(); + for (auto iter = channel.KeyFrames.begin(); iter != channel.KeyFrames.end(); ++iter) + { + const auto& KEYFRAME = *iter; + + if(KEYFRAME.TimeStamp <= poseTime) + { + firstKeyFrame = iter; + } + else if (KEYFRAME.TimeStamp > poseTime) + { + nextKeyFrame = iter; + break; + } + } + // Calculate the matrix + const int BONE_MTX_IDX = rig->GetNodeIndex(bone); + const float T = (poseTime - firstKeyFrame->TimeStamp) / (nextKeyFrame->TimeStamp - firstKeyFrame->TimeStamp); + boneMatrices[BONE_MTX_IDX] = boneMatrices[BONE_MTX_IDX] * SHMatrix::Transform + ( + SHVec3::Lerp(firstKeyFrame->Position, nextKeyFrame->Position, T), + SHQuaternion::Slerp(firstKeyFrame->Orientation, nextKeyFrame->Orientation, T), + SHVec3::Lerp(firstKeyFrame->Scale, nextKeyFrame->Scale, T) + ); + } + } + } + } +/*-------------------------------------------------------------------------------------*/ +/* RTTR Registration */ +/*-------------------------------------------------------------------------------------*/ RTTR_REGISTRATION { using namespace SHADE; diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 446014cc..056aa536 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -26,6 +26,8 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ class SHRig; + class SHAnimationClip; + class SHVkBuffer; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -40,20 +42,61 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Usage Functions */ /*---------------------------------------------------------------------------------*/ - /*void Play(); + /// + /// Plays the currently loaded animation from the last time. + /// + void Play(); + /// + /// Plays the specified animation clip from the start. + /// + /// + void Play(Handle clip); + /// + /// Plays the currently loaded animation clip from the start. + /// void PlayFromStart(); + /// + /// Pauses the animation at the current time. + /// void Pause(); - void Stop();*/ + /// + /// Stops the animation and resets the play time back to 0. + /// + void Stop(); /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ + /// + /// Sets the animation rig for this animator. + /// + /// Rig to use. void SetRig(Handle newRig); + /// + /// Sets the animation clip of this animator without playing it. + /// This will set the pose of the model to it's initial pose. + /// If the clip is the same as the current clip, nothing happens. + /// + /// Clip to use. + void SetClip(Handle newClip); /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ + /// + /// Retrieves all the bone matrices of this animator. + /// + /// Reference to a vector of the bone matrices. const std::vector& GetBoneMatrices() const noexcept { return boneMatrices; } + /// + /// Retrieve the currently set animation clip. + /// + /// Handle to the currently set animation clip. + Handle GetCurrentClip() const noexcept { return currClip; } + /// + /// Checks if an animation is currently playing. + /// + /// True if an animation clip is currently playing. bool IsPlaying() const { return isPlaying; } /*---------------------------------------------------------------------------------*/ @@ -70,10 +113,19 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ + // Resources Handle rig; - std::vector boneMatrices; + Handle currClip; + // Playback Tracking float currPlaybackTime = 0.0f; bool isPlaying = false; + // Buffer + std::vector boneMatrices; + + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void updatePoseWithClip(float poseTime); /*---------------------------------------------------------------------------------*/ /* RTTR */ diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 2214baa5..7c3059de 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -60,6 +60,16 @@ namespace SHADE return nodeCount; } + int SHRig::GetNodeIndex(Handle node) const noexcept + { + if (nodeIndexMap.contains(node)) + { + return nodeIndexMap.at(node); + } + + return -1; + } + /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ @@ -78,6 +88,8 @@ namespace SHADE { nodeNames.emplace(newNode, NODE_DATA.name); nodesByName.emplace(NODE_DATA.name, newNode); + nodeIndexMap.emplace(newNode, nodes.size()); + nodes.emplace_back(newNode); } // Fill child nodes diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index af1b79ec..5c4f720d 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -34,6 +34,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ class SHRig { + public: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ @@ -78,6 +79,10 @@ namespace SHADE /// needed. ///
int GetNodeCount() const noexcept; + /// + /// Retrieves the index in the node storage. + /// + int GetNodeIndex(Handle node) const noexcept; private: /*---------------------------------------------------------------------------------*/ @@ -86,6 +91,8 @@ namespace SHADE Handle rootNode; std::unordered_map, std::string> nodeNames; std::unordered_map> nodesByName; + std::vector> nodes; + std::unordered_map, int> nodeIndexMap; int nodeCount = 0; SHResourceLibrary nodeStore; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index 08f07ea6..477a5720 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -134,6 +134,7 @@ namespace SHADE TripleDescSet matPropsDescSet; TripleBuffer boneMatrixBuffer; TripleBuffer boneFirstIndexBuffer; + TripleDescSet boneMatricesDescSet; /*-----------------------------------------------------------------------------*/ /* Helper Functions */ From 1b5024793ca71587230f711c91be8f2b3250d27f Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 3 Jan 2023 10:14:39 +0800 Subject: [PATCH 071/164] Added debug drawing for rays --- Assets/Scenes/PhysicsSandbox.shade | 32 ++++--------------- .../Implemented/LeafNodes/LeafSearch.cs | 2 +- .../Gameplay/Player/SC_PickAndThrow.cs | 11 +++++-- Assets/Scripts/Tests/PhysicsTestObj.cs | 14 +++++--- .../EditorWindow/MenuBar/SHEditorMenuBar.cpp | 4 +++ .../Routines/SHPhysicsDebugDrawRoutine.cpp | 10 ++++-- .../System/SHPhysicsDebugDrawSystem.cpp | 7 ---- .../Physics/System/SHPhysicsDebugDrawSystem.h | 2 -- 8 files changed, 37 insertions(+), 45 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index a02f3b24..c94f5ec7 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -38,11 +38,7 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true - Scripts: - - Type: PhysicsTestObj - Enabled: true - forceAmount: 50 - torqueAmount: 500 + Scripts: ~ - EID: 1 Name: Default IsActive: true @@ -81,7 +77,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: false + Freeze Position Y: true Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -145,11 +141,7 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true - Scripts: - - Type: PhysicsTestObj - Enabled: true - forceAmount: 50 - torqueAmount: 500 + Scripts: ~ - EID: 4 Name: Default IsActive: true @@ -190,11 +182,7 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true - Scripts: - - Type: PhysicsTestObj - Enabled: true - forceAmount: 50 - torqueAmount: 500 + Scripts: ~ - EID: 5 Name: Default IsActive: true @@ -235,11 +223,7 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true - Scripts: - - Type: PhysicsTestObj - Enabled: true - forceAmount: 50 - torqueAmount: 500 + Scripts: ~ - EID: 6 Name: Default IsActive: true @@ -280,8 +264,4 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true - Scripts: - - Type: PhysicsTestObj - Enabled: true - forceAmount: 50 - torqueAmount: 500 \ No newline at end of file + Scripts: ~ \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs b/Assets/Scripts/Gameplay/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs index 7c68712c..b5b03629 100644 --- a/Assets/Scripts/Gameplay/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs +++ b/Assets/Scripts/Gameplay/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs @@ -159,7 +159,7 @@ public partial class LeafSearch : BehaviourTreeNode //Since transform position is often the raccoon's base and the ray needs to hit somewhere higher to be more reliable Vector3 rayDestination = plrT.GlobalPosition + plrT.GlobalScale * playerCollider.PositionOffset; Ray sightRay = new Ray(eyePosition, rayDestination - eyePosition); - RaycastHit sightRayHit = Physics.Raycast(sightRay); + RaycastHit sightRayHit = Physics.Raycast(sightRay, false)[0]; //As of November 2022, RaycastHit contains only the FIRST object hit by //the ray in the Other GameObject data member //Diren may likely add ALL objects hit by the ray over December diff --git a/Assets/Scripts/Gameplay/Player/SC_PickAndThrow.cs b/Assets/Scripts/Gameplay/Player/SC_PickAndThrow.cs index 9c879314..8323dba2 100644 --- a/Assets/Scripts/Gameplay/Player/SC_PickAndThrow.cs +++ b/Assets/Scripts/Gameplay/Player/SC_PickAndThrow.cs @@ -1,6 +1,7 @@ using SHADE; using SHADE_Scripting; using System; +using System.Collections.Generic; using static PlayerController; using static Item; @@ -203,9 +204,13 @@ public class PickAndThrow : Script Vector3 playerRayPos = pc.tranform.GlobalPosition; playerRayPos.y += 0.05f; dirNor.Normalise(); - RaycastHit ray1 = Physics.Raycast(new Ray(playerRayPos, Vector3.RotateY(dirNor, SHADE.Math.DegreesToRadians(22.5f))), rayDistance); - RaycastHit ray2 = Physics.Raycast(new Ray(playerRayPos, Vector3.RotateY(dirNor, SHADE.Math.DegreesToRadians(-22.5f))), rayDistance); - RaycastHit ray3 = Physics.Raycast(new Ray(playerRayPos, dirNor), rayDistance * 0.75f); + List rayList1 = Physics.Raycast(new Ray(playerRayPos, Vector3.RotateY(dirNor, SHADE.Math.DegreesToRadians(22.5f))), rayDistance, false); + List rayList2 = Physics.Raycast(new Ray(playerRayPos, Vector3.RotateY(dirNor, SHADE.Math.DegreesToRadians(-22.5f))), rayDistance, false); + List rayList3 = Physics.Raycast(new Ray(playerRayPos, dirNor), rayDistance * 0.75f, false); + + RaycastHit ray1 = rayList1[0]; + RaycastHit ray2 = rayList2[0]; + RaycastHit ray3 = rayList3[0]; inRange = CheckForItem(ray1) || CheckForItem(ray2) || CheckForItem(ray3); } } diff --git a/Assets/Scripts/Tests/PhysicsTestObj.cs b/Assets/Scripts/Tests/PhysicsTestObj.cs index 20437eb3..2348894f 100644 --- a/Assets/Scripts/Tests/PhysicsTestObj.cs +++ b/Assets/Scripts/Tests/PhysicsTestObj.cs @@ -6,7 +6,8 @@ using static Item; public class PhysicsTestObj : Script { - public RigidBody rb { get; set; } + public RigidBody body { get; set; } + public Collider collider { get; set; } // Movement input booleans public enum Direction @@ -67,7 +68,8 @@ public class PhysicsTestObj : Script protected override void awake() { - rb = GetComponent(); + body = GetComponent(); + collider = GetComponent(); for (int i = 0; i < 6; ++i) { @@ -78,6 +80,10 @@ public class PhysicsTestObj : Script protected override void update() { + Ray colliderRay = new Ray(); + colliderRay.Direction = Vector3.Right; + Physics.ColliderRaycast(collider.Owner, colliderRay, false); + for (int i = 0; i < 6; ++i) { if (Input.GetKeyDown(moveInputKeys[i])) @@ -99,13 +105,13 @@ public class PhysicsTestObj : Script { //Vector3 offset = new Vector3(0.25f, 0.0f, 0.0f); //rb.AddForceAtLocalPos(moveVec[i] * forceAmount, offset); - rb.AddForce(moveVec[i] * forceAmount); + body.AddForce(moveVec[i] * forceAmount); move[i] = false; } if (shouldRotate) { - rb.AddTorque(rotateVec[i] * torqueAmount); + body.AddTorque(rotateVec[i] * torqueAmount); rotate[i] = false; } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index 31b527f3..2e40e92e 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -220,6 +220,10 @@ namespace SHADE if (ImGui::Checkbox("Draw Contact Points", &drawContactPoints)) physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS, drawContactPoints); + bool drawRays = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS); + if (ImGui::Checkbox("Draw Rays", &drawRays)) + physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS, drawRays); + bool drawBroadphase = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE); if (ImGui::Checkbox("Draw Broadphase", &drawBroadphase)) physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE, drawBroadphase); diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp index b1de5851..69317e59 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp @@ -87,8 +87,14 @@ namespace SHADE if (DRAW_RAYCASTS) { - // TODO - physicsDebugDrawSystem->raysToDraw.clear(); + const SHColour& RAY_COLOUR = physicsDebugDrawSystem->DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::RAYCAST)]; + + const auto& RAYS = physicsSystem->raycastHits; + for (const auto& hit : RAYS) + debugDrawSystem->DrawLine(hit.start, hit.end, RAY_COLOUR, true); + + // Clear rays for the physics system + physicsSystem->raycastHits.clear(); } if (DRAW_BROADPHASE) diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index 6d69181c..3bd26925 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -149,11 +149,4 @@ namespace SHADE } } - void SHPhysicsDebugDrawSystem::drawRaycast(SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept - { - static const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::RAYCAST)]; - } - - - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index 3b4552a5..efc0738e 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -131,8 +131,6 @@ namespace SHADE uint8_t flags; Colliders collidersToDraw; - Raycasts raysToDraw; - Contacts contactToDraw; /*---------------------------------------------------------------------------------*/ /* Member Functions */ From 0460d776b09d0cb21166d708ec375a4ff3b9d621 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 3 Jan 2023 10:40:02 +0800 Subject: [PATCH 072/164] Fixed collision tag panel fallacies and saving of tag masks --- Assets/CollisionTags.SHConfig | 32 ++++++++--------- Assets/Scenes/PhysicsSandbox.shade | 2 +- .../ColliderTagPanel/SHColliderTagPanel.cpp | 8 ++--- .../CollisionTags/SHCollisionTagMatrix.cpp | 35 ++++++++++++++++--- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/Assets/CollisionTags.SHConfig b/Assets/CollisionTags.SHConfig index d3ebe7e2..9488270c 100644 --- a/Assets/CollisionTags.SHConfig +++ b/Assets/CollisionTags.SHConfig @@ -1,16 +1,16 @@ -0 1 -1 2 -2 3 -3 4 -4 5 -5 6 -6 7 -7 8 -8 9 -9 10 -10 11 -11 12 -12 13 -13 14 -14 15 -15 16 +0 1 3 +1 2 3 +2 3 65535 +3 4 65535 +4 5 65535 +5 6 65535 +6 7 65535 +7 8 65535 +8 9 65535 +9 10 65535 +10 11 65535 +11 12 65535 +12 13 65535 +13 14 65535 +14 15 65535 +15 16 65535 diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index c94f5ec7..b8db2555 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -77,7 +77,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: true + Freeze Position Y: false Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false diff --git a/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp index 9a04b812..f28e29c5 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp @@ -15,7 +15,7 @@ namespace SHADE ImGui::TableNextRow(); ImGui::PushID("CollisionTagNames"); - for (int i = SHCollisionTag::NUM_LAYERS; i >= 0; --i) + for (int i = SHCollisionTag::NUM_LAYERS; i >= 1; --i) { ImGui::TableNextColumn(); if(i == SHCollisionTag::NUM_LAYERS) continue; @@ -29,7 +29,7 @@ namespace SHADE ImGui::PopID(); } ImGui::PopID(); - for (int i = 0; i < SHCollisionTag::NUM_LAYERS; ++i) + for (int i = 0; i < SHCollisionTag::NUM_LAYERS - 1; ++i) { std::string tagName = SHCollisionTagMatrix::GetTagName(i); auto tag = SHCollisionTagMatrix::GetTag(i); @@ -53,8 +53,8 @@ namespace SHADE tagName2 = std::to_string(idx); ImGui::TableNextColumn(); - //if(i == idx) - // continue; + if(i == idx) + continue; std::string label = std::format("##{} vs {}", tagName, tagName2); SHEditorWidgets::CheckBox(label, [tag, &idx]{return tag->GetLayerState(idx);}, [tag, i, idx](bool const& value){tag->SetLayerState(idx, value); SHCollisionTagMatrix::GetTag(idx)->SetLayerState(i, value);}, label.substr(2)); } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp b/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp index b687c6ca..4d3980b0 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp @@ -14,6 +14,9 @@ // Primary Header #include "SHCollisionTagMatrix.h" +// Project Headers +#include "Tools/Utilities/SHUtilities.h" + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -145,8 +148,9 @@ namespace SHADE /** * I HATE FILE IO * - * Each line in the file should be "indextag name". - * If the line fails to follow this format, use the default tag name (index + 1) + * Each line in the file should be "indextag namemask". + * If the line fails to follow this format, use the default tag name (index + 1) and default mask. + * If no mask was read, use a default mask. */ // Populate tag names with default @@ -187,18 +191,40 @@ namespace SHADE { SHLOG_ERROR ( - "Collision tag file line {} does not match the required format of 'indextag name'. Default tag used for index {}" + "Collision tag file line {} does not match the required format of 'indextag namemask'. Default tag used for index {}" , linesRead + 1 , tagIndex ) // Use default collisionTags[tagIndex].SetName(std::to_string(tagIndex + 1)); + collisionTags[tagIndex].SetMask(SHUtilities::ConvertEnum(SHCollisionTag::Layer::ALL)); continue; } collisionTags[tagIndex].SetName(tagName); + // Next element is the mask value + std::string maskString; + ss >> maskString; + + uint16_t mask = std::numeric_limits::max(); + if (maskString.empty()) + { + SHLOG_ERROR + ( + "Collision tag file line {} does not match the required format of 'indextag namemask'. Default mask used for index {}" + , linesRead + 1 + , tagIndex + ) + } + else + { + mask = static_cast(std::stoi(maskString)); + } + + collisionTags[tagIndex].SetMask(mask); + ss.clear(); } @@ -215,8 +241,9 @@ namespace SHADE return; } + // Index Name Mask for (int i = 0; i < SHCollisionTag::NUM_LAYERS; ++i) - collisionTagNamesFile << i << " " << collisionTags[i].GetName() << std::endl; + collisionTagNamesFile << i << " " << collisionTags[i].GetName() << " " << collisionTags[i].GetMask() << std::endl; collisionTagNamesFile.close(); } From b2645fb584009c22383dab40f1cd1ca108f103c8 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 3 Jan 2023 18:53:21 +0800 Subject: [PATCH 073/164] Added support for composite colliders --- Assets/Scenes/PhysicsSandbox.shade | 17 +- .../Inspector/SHEditorComponentView.hpp | 11 +- .../CollisionShapes/SHBoxCollisionShape.cpp | 59 ++--- .../CollisionShapes/SHBoxCollisionShape.h | 8 +- .../CollisionShapes/SHCollisionShape.cpp | 16 ++ .../CollisionShapes/SHCollisionShape.h | 11 +- .../SHSphereCollisionShape.cpp | 20 ++ .../CollisionShapes/SHSphereCollisionShape.h | 11 +- .../src/Physics/Dynamics/SHRigidBody.cpp | 212 ++++++++++++------ .../src/Physics/Dynamics/SHRigidBody.h | 9 +- 10 files changed, 246 insertions(+), 128 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index b8db2555..9fa2c0f1 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,7 +45,7 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 4, z: 5} + Position: {x: 0, y: 2, z: 5} Pitch: 0 Yaw: 0 Roll: 0 @@ -62,14 +62,14 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.45715916, y: 7, z: 0.0329093337} + Translate: {x: -1.45715916, y: 7, z: 0.227711335} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: Type: Dynamic Auto Mass: false - Mass: 1 + Mass: 0.52359879 Drag: 0.00999999978 Angular Drag: 0.00999999978 Use Gravity: true @@ -77,7 +77,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: false + Freeze Position Y: true Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -95,6 +95,15 @@ Density: 1 Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} + - Is Trigger: false + Collision Tag: 1 + Type: Sphere + Radius: 0.5 + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0.75, y: 0.5, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true Scripts: - Type: PhysicsTestObj diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index a46e2550..7a528b0b 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -251,9 +251,16 @@ namespace SHADE if(rbType == SHRigidBodyComponent::Type::DYNAMIC) //Dynamic only fields { - SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetIsGravityEnabled(value);}, "Gravity"); + SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetIsGravityEnabled(value);}, "Whether Gravity is enabled for this body"); SHEditorWidgets::DragFloat("Gravity Scale", [component] { return component->GetGravityScale(); }, [component](float const& value) { component->SetGravityScale(value); }, "Gravity Scale", 0.1f, 0.0f); - SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); + + SHEditorWidgets::CheckBox("Auto Mass", [component]{return component->GetAutoMass();}, [component](bool const& value){component->SetAutoMass(value);}, "If mass should be automatically computed"); + + const bool autoMass = component->GetAutoMass(); + if (autoMass) + SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {}, "Mass", 0.1f, 0.0f, 0.0f, "%.3f", ImGuiSliderFlags_ReadOnly); + else + SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); } if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp index 621b560b..7a6ab7d2 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp @@ -147,11 +147,6 @@ namespace SHADE return relativeExtents; } - SHQuaternion SHBoxCollisionShape::GetOrientation() const noexcept - { - return Orientation; - } - SHVec3 SHBoxCollisionShape::GetVertex(int index) const { static constexpr int NUM_VERTICES = 8; @@ -160,41 +155,6 @@ namespace SHADE throw std::invalid_argument("Index out-of-range!"); return GetVertices()[index]; - - //// Rotate half extents - //const SHVec3 ROTATED_EXTENTS = SHVec3::Rotate(Extents, Orientation); - //const SHVec3 CENTER { Center }; - - ///* - // * Front: -Z - // * - // * 3 _____ 2 - // * | | - // * | | - // * |_____| - // * 0 1 - // * - // * Back: +Z - // * - // * 7 _____ 6 - // * | | - // * | | - // * |_____| - // * 4 5 - // * - // */ - - //switch (index) - //{ - // case 0: return CENTER + SHVec3 { -ROTATED_EXTENTS.x, -ROTATED_EXTENTS.y, -ROTATED_EXTENTS.z } - // case 1: return CENTER + SHVec3 { - // case 2: return CENTER + SHVec3 { - // case 3: return CENTER + SHVec3 { - // case 4: return CENTER + SHVec3 { - // case 5: return CENTER + SHVec3 { - // case 6: return CENTER + SHVec3 { - // case 7: return CENTER + SHVec3 { - //} } SHVec3 SHBoxCollisionShape::GetNormal(int faceIndex) const @@ -204,7 +164,26 @@ namespace SHADE // Rotate normal into world space return SHVec3::Rotate(LOCAL_NORMAL, Orientation); + } + SHVec3 SHBoxCollisionShape::GetPosition() const noexcept + { + return Center; + } + + SHQuaternion SHBoxCollisionShape::GetOrientation() const noexcept + { + return Orientation; + } + + float SHBoxCollisionShape::GetVolume() const noexcept + { + return Volume(); + } + + SHVec3 SHBoxCollisionShape::GetLocalCentroid() const noexcept + { + return SHVec3::Zero; } /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h index f34870f5..a5be0d70 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h @@ -75,10 +75,14 @@ namespace SHADE [[nodiscard]] SHVec3 GetCenter () const noexcept; [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; - [[nodiscard]] SHQuaternion GetOrientation () const noexcept; [[nodiscard]] SHVec3 GetVertex (int index) const override; - [[nodiscard]] SHVec3 GetNormal (int faceIndex) const override; + [[nodiscard]] SHVec3 GetNormal (int faceIndex) const override; + + [[nodiscard]] SHVec3 GetPosition () const noexcept override; + [[nodiscard]] SHQuaternion GetOrientation () const noexcept override; + [[nodiscard]] float GetVolume () const noexcept override; + [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp index 3696dd50..b412a227 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -14,6 +14,7 @@ #include "SHCollisionShape.h" // Project Headers +#include "Physics/Collision/SHCollider.h" #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Reflection/SHReflectionMetadata.h" #include "Tools/Utilities/SHUtilities.h" @@ -111,6 +112,21 @@ namespace SHADE return *collisionTag; } + SHVec3 SHCollisionShape::GetPosition() const noexcept + { + const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); + + const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); + + return SHVec3::Transform(transform.position, TRS); + } + + SHQuaternion SHCollisionShape::GetOrientation() const noexcept + { + return collider->GetOrientation() * transform.orientation; + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 729eecf7..42e9fb0e 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -94,6 +94,8 @@ namespace SHADE [[nodiscard]] float GetDensity () const noexcept; [[nodiscard]] const SHPhysicsMaterial& GetMaterial () const noexcept; + + // Offsets [[nodiscard]] const SHVec3& GetPositionOffset () const noexcept; @@ -107,6 +109,14 @@ namespace SHADE [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; + // Virtual methods + + [[nodiscard]] virtual SHVec3 GetPosition () const noexcept; + [[nodiscard]] virtual SHQuaternion GetOrientation () const noexcept; + [[nodiscard]] virtual float GetVolume () const noexcept = 0; + [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; + [[nodiscard]] virtual SHVec3 GetLocalCentroid () const noexcept = 0; + /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ @@ -130,7 +140,6 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ virtual void ComputeTransforms () noexcept = 0; - [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp index 71d965c5..a5c82017 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp @@ -141,6 +141,26 @@ namespace SHADE return relativeRadius; } + SHVec3 SHSphereCollisionShape::GetPosition() const noexcept + { + return Center; + } + + SHQuaternion SHSphereCollisionShape::GetOrientation() const noexcept + { + return collider->GetTransform().orientation * transform.orientation; + } + + float SHSphereCollisionShape::GetVolume() const noexcept + { + return Volume(); + } + + SHVec3 SHSphereCollisionShape::GetLocalCentroid() const noexcept + { + return SHVec3::Zero; + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h index 54e04fe6..7dbdf13d 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h @@ -71,9 +71,14 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] float GetWorldRadius () const noexcept; - [[nodiscard]] float GetRelativeRadius () const noexcept; + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] float GetWorldRadius () const noexcept; + [[nodiscard]] float GetRelativeRadius () const noexcept; + + [[nodiscard]] SHVec3 GetPosition () const noexcept override; + [[nodiscard]] SHQuaternion GetOrientation () const noexcept override; + [[nodiscard]] float GetVolume () const noexcept override; + [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index 3e24fa27..c4b27a42 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -14,6 +14,8 @@ #include "SHRigidBody.h" // Project Headers +#include + #include "Physics/Collision/SHCollider.h" #include "Tools/Logger/SHLogger.h" @@ -60,19 +62,19 @@ namespace SHADE } SHRigidBody::SHRigidBody(SHRigidBody&& rhs) noexcept - : entityID { rhs.entityID } - , collider { nullptr } - , bodyType { rhs.bodyType } - , gravityScale { rhs.gravityScale } - , invMass { rhs.invMass } - , linearDrag { rhs.linearDrag } - , angularDrag { rhs.angularDrag } + : entityID { rhs.entityID } + , collider { nullptr } + , bodyType { rhs.bodyType } + , gravityScale { rhs.gravityScale } + , invMass { rhs.invMass } + , linearDrag { rhs.linearDrag } + , angularDrag { rhs.angularDrag } , localInvInertia { rhs.localInvInertia } , worldInvInertia { rhs.worldInvInertia } , localCentroid { rhs.localCentroid } , worldCentroid { rhs.worldCentroid } - , flags { rhs.flags } - , motionState { std::move(rhs.motionState) } + , flags { rhs.flags } + , motionState { std::move(rhs.motionState) } { // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. } @@ -114,21 +116,21 @@ namespace SHADE SHRigidBody& SHRigidBody::operator=(SHRigidBody&& rhs) noexcept { - entityID = rhs.entityID; + entityID = rhs.entityID; // Deep copy the collider - *collider = *rhs.collider; - bodyType = rhs.bodyType; - gravityScale = rhs.gravityScale; - invMass = rhs.invMass; - linearDrag = rhs.linearDrag; - angularDrag = rhs.angularDrag; + *collider = *rhs.collider; + bodyType = rhs.bodyType; + gravityScale = rhs.gravityScale; + invMass = rhs.invMass; + linearDrag = rhs.linearDrag; + angularDrag = rhs.angularDrag; localInvInertia = rhs.localInvInertia; worldInvInertia = rhs.worldInvInertia; localCentroid = rhs.localCentroid; worldCentroid = rhs.worldCentroid; - flags = rhs.flags; - motionState = std::move(rhs.motionState); + flags = rhs.flags; + motionState = std::move(rhs.motionState); // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. accumulatedForce = SHVec3::Zero; @@ -242,6 +244,12 @@ namespace SHADE return flags & (1U << FLAG_POS); } + bool SHRigidBody::IsTriggerInMassData() const noexcept + { + static constexpr unsigned int FLAG_POS = 6; + return flags & (1U << FLAG_POS); + } + bool SHRigidBody::GetFreezePositionX() const noexcept { static constexpr unsigned int FLAG_POS = 10; @@ -334,6 +342,11 @@ namespace SHADE invMass = 1.0f / newMass; + // Turn off automass + static constexpr unsigned int AUTO_MASS_FLAG = 4; + static constexpr uint16_t VALUE = 1U << AUTO_MASS_FLAG; + + flags &= ~(VALUE); ComputeMassData(); } @@ -455,7 +468,6 @@ namespace SHADE if (enableAutoMass) { flags |= VALUE; - // TODO: Compute mass based on collider geometry } else { @@ -463,6 +475,16 @@ namespace SHADE // Use default mass of 1 invMass = 1.0f; } + + ComputeMassData(); + } + + void SHRigidBody::SetTriggerInMassData(bool triggerInMassData) noexcept + { + static constexpr unsigned int FLAG_POS = 6; + static constexpr uint16_t VALUE = 1U << FLAG_POS; + + triggerInMassData ? flags |= VALUE : flags &= ~VALUE; } void SHRigidBody::SetFreezePositionX(bool freezePositionX) noexcept @@ -532,7 +554,7 @@ namespace SHADE else { flags &= ~VALUE; - computeInertiaTensor(); + ComputeMassData(); } } @@ -552,7 +574,7 @@ namespace SHADE else { flags &= ~VALUE; - computeInertiaTensor(); + ComputeMassData(); } } @@ -572,7 +594,7 @@ namespace SHADE else { flags &= ~VALUE; - computeInertiaTensor(); + ComputeMassData(); } } @@ -628,60 +650,110 @@ namespace SHADE void SHRigidBody::ComputeMassData() noexcept { - computeCentroid(); - computeMassAndInertiaTensor(); + // Reset centroid + localCentroid = SHVec3::Zero; + localInvInertia = SHMatrix::Identity; + + // In the instance the body is a particle + if (!collider || collider->GetCollisionShapes().empty()) + { + localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass; + return; + } + + const float CUSTOM_MASS = 1.0f / invMass; + const bool AUTO_MASS = IsAutoMassEnabled(); + const bool INCLUDE_TRIGGERS = IsTriggerInMassData(); + const auto& SHAPES = collider->GetCollisionShapes(); + + // Compute Total mass and store individual masses if custom mass is being used. + // Compute local centroid at the same time + + // Zero matrix; + SHMatrix tmpLocalTensor; + tmpLocalTensor -= SHMatrix::Identity; + + float totalMass = 0.0f; + std::vector trueMass; + + for (auto* shape : SHAPES) + { + // We skip triggers by default + if (shape->IsTrigger() && !INCLUDE_TRIGGERS) + continue; + + shape->ComputeTransforms(); + + // p = m/v, therefore m = pv. This is the true mass of the shape. + const float MASS = shape->GetDensity() * shape->GetVolume(); + totalMass += MASS; + + trueMass.emplace_back(MASS); + + // Weighted sum of masses contribute to the centroid's location using the collider's local position. + localCentroid += MASS * (shape->GetPosition() - collider->GetPosition()); + } + + if (totalMass > 0.0f) + { + localCentroid /= totalMass; + + if (AUTO_MASS) + invMass = 1.0f / totalMass; + } + + const SHMatrix R = SHMatrix::Rotate(motionState.orientation); + const SHMatrix RT = SHMatrix::Transpose(R); + + worldCentroid = (R * localCentroid) + motionState.position; + + for (size_t i = 0; i < SHAPES.size(); ++i) + { + const auto* SHAPE = SHAPES[i]; + + // We skip triggers by default + if (SHAPE->IsTrigger() && !INCLUDE_TRIGGERS) + continue; + + // If using custom mass, take the ratio of the mass + float actualMass = trueMass[i]; + if (!AUTO_MASS) + actualMass *= CUSTOM_MASS / totalMass; + + // Convert inertia tensor into local-space of the body + SHMatrix I = SHAPE->GetInertiaTensor( actualMass ) * RT; + I = R * I; + + // Parallel Axis Theorem + const SHVec3 O = SHAPE->GetPosition() - worldCentroid; + const float O2 = O.LengthSquared(); + + SHMatrix offsetMatrix; + offsetMatrix -= SHMatrix::Identity; + offsetMatrix.m[0][0] = offsetMatrix.m[1][1] = offsetMatrix.m[2][2] = O2; + + const SHVec3 NOX = O * -O.x; + const SHVec3 NOY = O * -O.y; + const SHVec3 NOZ = O * -O.z; + + offsetMatrix += SHMatrix{ NOX, NOY, NOZ, SHVec4::Zero }; + offsetMatrix *= actualMass; + + tmpLocalTensor += I + offsetMatrix; + } + + // Set diagonals then invert + localInvInertia.m[0][0] = tmpLocalTensor.m[0][0]; + localInvInertia.m[1][1] = tmpLocalTensor.m[1][1]; + localInvInertia.m[2][2] = tmpLocalTensor.m[2][2]; + + localInvInertia = SHMatrix::Inverse(localInvInertia); } /*-----------------------------------------------------------------------------------*/ /* Private Member Function Definition */ /*-----------------------------------------------------------------------------------*/ - void SHRigidBody::computeCentroid() noexcept - { - // TODO - } - - void SHRigidBody::computeMass() noexcept - { - // TODO: If auto mass in enabled, compute total mass based from each collider. - // TODO: If auto mass disabled, compute inertia tensor for each shape using the ratio of its mass / total mass by comparing the volume / total volume. - } - - void SHRigidBody::computeInertiaTensor() noexcept - { - // TODO: Compute total inertia from composited colliders using the Parallel Axis Theorem. - - if (!collider || collider->GetCollisionShapes().empty()) - { - localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass; - } - else - { - // HACK: For now, take only the first shape as we are testing with non-composited colliders. We are using the center as the centroid. - const auto* FIRST_SHAPE = collider->GetCollisionShape(0); - localInvInertia = SHMatrix::Inverse(FIRST_SHAPE->GetInertiaTensor(1.0f / invMass)); - } - } - - void SHRigidBody::computeMassAndInertiaTensor() noexcept - { - // TODO: If auto mass in enabled, compute total mass based from each collider. - // TODO: If auto mass disabled, compute inertia tensor for each shape using the ratio of its mass / total mass by comparing the volume / total volume. - - // TODO: Compute total inertia from composited colliders using the Parallel Axis Theorem. - - if (!collider || collider->GetCollisionShapes().empty()) - { - localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass; - } - else - { - // HACK: For now, take only the first shape as we are testing with non-composited colliders. We are using the center as the centroid. - const auto* FIRST_SHAPE = collider->GetCollisionShape(0); - localInvInertia = SHMatrix::Inverse(FIRST_SHAPE->GetInertiaTensor(1.0f / invMass)); - } - } - void SHRigidBody::constrainLinearVelocities() noexcept { linearVelocity.x = GetFreezePositionX() ? 0.0f : linearVelocity.x; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index 34424a1c..3c869ad2 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -99,6 +99,7 @@ namespace SHADE [[nodiscard]] bool IsSleepingEnabled () const noexcept; [[nodiscard]] bool IsGravityEnabled () const noexcept; [[nodiscard]] bool IsAutoMassEnabled () const noexcept; + [[nodiscard]] bool IsTriggerInMassData () const noexcept; [[nodiscard]] bool GetFreezePositionX () const noexcept; [[nodiscard]] bool GetFreezePositionY () const noexcept; [[nodiscard]] bool GetFreezePositionZ () const noexcept; @@ -157,6 +158,7 @@ namespace SHADE void SetSleepingEnabled (bool enableSleeping) noexcept; void SetGravityEnabled (bool enableGravity) noexcept; void SetAutoMassEnabled (bool enableAutoMass) noexcept; + void SetTriggerInMassData(bool triggerInMassData) noexcept; void SetFreezePositionX (bool freezePositionX) noexcept; void SetFreezePositionY (bool freezePositionY) noexcept; void SetFreezePositionZ (bool freezePositionZ) noexcept; @@ -249,7 +251,7 @@ namespace SHADE SHVec3 linearVelocity; SHVec3 angularVelocity; - // aZ aY aX rotLockActive pZ pY pX posLockActive 0 0 inIsland autoMass enableGravity enableSleeping sleeping active + // aZ aY aX pZ pY pX 0 0 0 addTriggersToMassData inIsland autoMass enableGravity enableSleeping sleeping active uint16_t flags; SHMotionState motionState; @@ -258,11 +260,6 @@ namespace SHADE /* Member Functions */ /*-----------------------------------------------------------------------------------*/ - void computeCentroid () noexcept; - void computeMass () noexcept; - void computeInertiaTensor () noexcept; - void computeMassAndInertiaTensor() noexcept; - void constrainLinearVelocities () noexcept; void constrainAngularVelocities () noexcept; From 06b7db14d58aed8e61c735d5768787107158a29c Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 3 Jan 2023 18:57:14 +0800 Subject: [PATCH 074/164] Added code for bone matrices --- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 101 +++++++++++++----- .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 1 + 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index bb717ea5..7a1d46ed 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -449,13 +449,13 @@ namespace SHADE if (CURR_INSTANCES > 0) { drawData.emplace_back(vk::DrawIndexedIndirectCommand - { - .indexCount = subBatch.Mesh->IndexCount, - .instanceCount = CURR_INSTANCES, - .firstIndex = subBatch.Mesh->FirstIndex, - .vertexOffset = subBatch.Mesh->FirstVertex, - .firstInstance = nextInstanceIndex - }); + { + .indexCount = subBatch.Mesh->IndexCount, + .instanceCount = CURR_INSTANCES, + .firstIndex = subBatch.Mesh->FirstIndex, + .vertexOffset = subBatch.Mesh->FirstVertex, + .firstInstance = nextInstanceIndex + }); } nextInstanceIndex += CURR_INSTANCES; @@ -520,7 +520,6 @@ namespace SHADE const auto& BONE_MATRICES = animator->GetBoneMatrices(); boneMatrixData.insert(boneMatrixData.end(), BONE_MATRICES.cbegin(), BONE_MATRICES.cend()); } - } } @@ -558,23 +557,7 @@ namespace SHADE // - Material Properties Buffer rebuildMaterialBuffers(frameIndex, descPool); // - Bone Buffers - if (!boneMatrixIndices.empty()) - { - const uint32_t BONE_IDX_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, boneFirstIndexBuffer[frameIndex], boneMatrixIndices.data(), BONE_IDX_DATA_BYTES, - BuffUsage::eVertexBuffer, - "Batch Bone Index Buffer" - ); - const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(uint32_t)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, boneMatrixBuffer[frameIndex], boneMatrixData.data(), BONE_MTX_DATA_BYTES, - BuffUsage::eStorageBuffer, - "Batch Bone Matrix Buffer" - ); - } + rebuildBoneBuffers(frameIndex, descPool); } // Mark this frame as no longer dirty @@ -605,7 +588,7 @@ namespace SHADE if (matPropsDescSet[frameIndex]) { cmdBuffer->BindDescriptorSet - ( + ( matPropsDescSet[frameIndex], SH_PIPELINE_TYPE::GRAPHICS, SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, @@ -615,7 +598,13 @@ namespace SHADE if (boneMatrixBuffer[frameIndex] && boneFirstIndexBuffer[frameIndex]) { cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::BONE_INDICES, boneFirstIndexBuffer[frameIndex], 0); - // TODO: Bind storage buffer + cmdBuffer->BindDescriptorSet + ( + boneMatricesDescSet[frameIndex], + SH_PIPELINE_TYPE::GRAPHICS, + SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, + dynamicOffset + ); } cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); cmdBuffer->EndLabeledSegment(); @@ -673,4 +662,62 @@ namespace SHADE } } + void SHBatch::rebuildBoneBuffers(uint32_t frameIndex, Handle descPool) + { + // Using Declarations + using BuffUsage = vk::BufferUsageFlagBits; + + // Nothing to rebuild + if (boneMatrixData.empty()) + return; + + // Update GPU Buffers + const uint32_t BONE_IDX_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneFirstIndexBuffer[frameIndex], boneMatrixIndices.data(), BONE_IDX_DATA_BYTES, + BuffUsage::eVertexBuffer, + "Batch Bone Index Buffer" + ); + const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(uint32_t)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneMatrixBuffer[frameIndex], boneMatrixData.data(), BONE_MTX_DATA_BYTES, + BuffUsage::eStorageBuffer, + "Batch Bone Matrix Buffer" + ); + + // Update descriptor set buffer + if (!boneMatricesDescSet[frameIndex]) + { + boneMatricesDescSet[frameIndex] = descPool->Allocate + ( + { SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE] }, + { 0 } + ); + +#ifdef _DEBUG + const auto& DESC_SETS = boneMatricesDescSet[frameIndex]->GetVkHandle(); + for (auto descSet : DESC_SETS) + { + SET_VK_OBJ_NAME(device, vk::ObjectType::eDescriptorSet, descSet, "[Descriptor Set] Batch Bone Data"); + } +#endif + } + std::array, 1> bufferList = { boneMatrixBuffer[frameIndex] }; + boneMatricesDescSet[frameIndex]->ModifyWriteDescBuffer + ( + SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, + SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, + bufferList, + 0, static_cast(boneMatrixData.size() * sizeof(SHMatrix)) + ); + boneMatricesDescSet[frameIndex]->UpdateDescriptorSetBuffer + ( + SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, + SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA + ); + + // TODO: Need to merge this with the material one + } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index 477a5720..66260c7d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -141,5 +141,6 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ void setAllDirtyFlags(); void rebuildMaterialBuffers(uint32_t frameIndex, Handle descPool); + void rebuildBoneBuffers(uint32_t frameIndex, Handle descPool); }; } From f7e867098dff21acd65bbbfb4a25ce0b1cae967e Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 3 Jan 2023 20:30:20 +0800 Subject: [PATCH 075/164] Small changes to rigidbody tooltips --- Assets/Scenes/PhysicsSandbox.shade | 4 +-- .../Inspector/SHEditorComponentView.hpp | 25 ++++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 9fa2c0f1..34e78cc3 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,7 +45,7 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 2, z: 5} + Position: {x: 0, y: 4, z: 5} Pitch: 0 Yaw: 0 Roll: 0 @@ -69,7 +69,7 @@ RigidBody Component: Type: Dynamic Auto Mass: false - Mass: 0.52359879 + Mass: 10 Drag: 0.00999999978 Angular Drag: 0.00999999978 Use Gravity: true diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 7a528b0b..16961fe0 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -252,38 +252,33 @@ namespace SHADE if(rbType == SHRigidBodyComponent::Type::DYNAMIC) //Dynamic only fields { SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetIsGravityEnabled(value);}, "Whether Gravity is enabled for this body"); - SHEditorWidgets::DragFloat("Gravity Scale", [component] { return component->GetGravityScale(); }, [component](float const& value) { component->SetGravityScale(value); }, "Gravity Scale", 0.1f, 0.0f); + SHEditorWidgets::DragFloat("Gravity Scale", [component] { return component->GetGravityScale(); }, [component](float const& value) { component->SetGravityScale(value); }, "Per-object Gravity Scale", 0.1f, 0.0f); - SHEditorWidgets::CheckBox("Auto Mass", [component]{return component->GetAutoMass();}, [component](bool const& value){component->SetAutoMass(value);}, "If mass should be automatically computed"); - - const bool autoMass = component->GetAutoMass(); - if (autoMass) - SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {}, "Mass", 0.1f, 0.0f, 0.0f, "%.3f", ImGuiSliderFlags_ReadOnly); - else - SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); + SHEditorWidgets::CheckBox("Auto Mass", [component]{return component->GetAutoMass();}, [component](bool const& value){component->SetAutoMass(value);}, "If mass should be automatically computed. Setting mass will turn this off."); + SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); } if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag", 0.1f, 0.0f); SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag", 0.1f, 0.0f); - SHEditorWidgets::CheckBox("Interpolate", [component] {return component->IsInterpolating(); }, [component](bool const& value) {component->SetInterpolate(value); }, "Interpolate"); + SHEditorWidgets::CheckBox("Interpolate", [component] {return component->IsInterpolating(); }, [component](bool const& value) {component->SetInterpolate(value); }, "If the position between frames should be interpolated."); SHEditorWidgets::BeginPanel(std::format("{} Constraints", ICON_FA_LOCK).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); SHEditorWidgets::TextLabel("Freeze Position"); ImGui::PushID("FreezePos"); - SHEditorWidgets::CheckBox("X", [component] {return component->GetFreezePositionX(); }, [component](bool const& value) {component->SetFreezePositionX(value); }, "Freeze Position - X"); ImGui::SameLine(); - SHEditorWidgets::CheckBox("Y", [component] {return component->GetFreezePositionY(); }, [component](bool const& value) {component->SetFreezePositionY(value); }, "Freeze Position - Y"); ImGui::SameLine(); - SHEditorWidgets::CheckBox("Z", [component] {return component->GetFreezePositionZ(); }, [component](bool const& value) {component->SetFreezePositionZ(value); }, "Freeze Position - Z"); + SHEditorWidgets::CheckBox("X", [component] {return component->GetFreezePositionX(); }, [component](bool const& value) {component->SetFreezePositionX(value); }, "Stops any displacement along the X-axis."); ImGui::SameLine(); + SHEditorWidgets::CheckBox("Y", [component] {return component->GetFreezePositionY(); }, [component](bool const& value) {component->SetFreezePositionY(value); }, "Stops any displacement along the Y-axis."); ImGui::SameLine(); + SHEditorWidgets::CheckBox("Z", [component] {return component->GetFreezePositionZ(); }, [component](bool const& value) {component->SetFreezePositionZ(value); }, "Stops any displacement along the Z-axis."); ImGui::PopID(); SHEditorWidgets::TextLabel("Freeze Rotation"); ImGui::PushID("FreezeRot"); - SHEditorWidgets::CheckBox("X", [component] {return component->GetFreezeRotationX(); }, [component](bool const& value) {component->SetFreezeRotationX(value); }, "Freeze Rotation - X"); ImGui::SameLine(); - SHEditorWidgets::CheckBox("Y", [component] {return component->GetFreezeRotationY(); }, [component](bool const& value) {component->SetFreezeRotationY(value); }, "Freeze Rotation - Y"); ImGui::SameLine(); - SHEditorWidgets::CheckBox("Z", [component] {return component->GetFreezeRotationZ(); }, [component](bool const& value) {component->SetFreezeRotationZ(value); }, "Freeze Rotation - Z"); + SHEditorWidgets::CheckBox("X", [component] {return component->GetFreezeRotationX(); }, [component](bool const& value) {component->SetFreezeRotationX(value); }, "Stops any rotation about the X-axis."); ImGui::SameLine(); + SHEditorWidgets::CheckBox("Y", [component] {return component->GetFreezeRotationY(); }, [component](bool const& value) {component->SetFreezeRotationY(value); }, "Stops any rotation about the Y-axis."); ImGui::SameLine(); + SHEditorWidgets::CheckBox("Z", [component] {return component->GetFreezeRotationZ(); }, [component](bool const& value) {component->SetFreezeRotationZ(value); }, "Stops any rotation about the Z-axis."); ImGui::PopID(); SHEditorWidgets::EndPanel(); From a49c674c2b445032ff2bb1f159c16b3c97abb728 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 4 Jan 2023 15:03:58 +0800 Subject: [PATCH 076/164] Generalised the Parallel Axis Theorem for computing inertia tensors --- Assets/Scenes/PhysicsSandbox.shade | 2 +- .../EditorWindow/MenuBar/SHEditorMenuBar.cpp | 34 +++++++-------- SHADE_Engine/src/Math/SHMatrix.cpp | 8 ++++ SHADE_Engine/src/Math/SHMatrix.h | 1 + SHADE_Engine/src/Math/Vector/SHVec3.cpp | 24 +++++++++++ SHADE_Engine/src/Math/Vector/SHVec3.h | 41 ++++++++++--------- .../src/Physics/Dynamics/SHRigidBody.cpp | 38 +++++++---------- 7 files changed, 87 insertions(+), 61 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 34e78cc3..6dea778f 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -77,7 +77,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: true + Freeze Position Y: false Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index 5f9bf4d7..8e8c88f3 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -309,28 +309,28 @@ namespace SHADE void SHEditorMenuBar::DrawPhysicsSettings() noexcept { if (ImGui::BeginMenu("Physics Settings")) + { + if (auto* physicsDebugDraw = SHSystemManager::GetSystem()) { - if (auto* physicsDebugDraw = SHSystemManager::GetSystem()) - { - bool drawColliders = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDERS); - if (ImGui::Checkbox("Draw Colliders", &drawColliders)) - physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDERS, drawColliders); + bool drawColliders = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDERS); + if (ImGui::Checkbox("Draw Colliders", &drawColliders)) + physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDERS, drawColliders); - bool drawContactPoints = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS); - if (ImGui::Checkbox("Draw Contact Points", &drawContactPoints)) - physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS, drawContactPoints); + bool drawContactPoints = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS); + if (ImGui::Checkbox("Draw Contact Points", &drawContactPoints)) + physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS, drawContactPoints); - bool drawRays = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS); - if (ImGui::Checkbox("Draw Rays", &drawRays)) - physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS, drawRays); + bool drawRays = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS); + if (ImGui::Checkbox("Draw Rays", &drawRays)) + physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS, drawRays); - bool drawBroadphase = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE); - if (ImGui::Checkbox("Draw Broadphase", &drawBroadphase)) - physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE, drawBroadphase); - } - - ImGui::EndMenu(); + bool drawBroadphase = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE); + if (ImGui::Checkbox("Draw Broadphase", &drawBroadphase)) + physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE, drawBroadphase); } + + ImGui::EndMenu(); + } } }//namespace SHADE diff --git a/SHADE_Engine/src/Math/SHMatrix.cpp b/SHADE_Engine/src/Math/SHMatrix.cpp index 3d450a88..2cbd5ef6 100644 --- a/SHADE_Engine/src/Math/SHMatrix.cpp +++ b/SHADE_Engine/src/Math/SHMatrix.cpp @@ -34,6 +34,14 @@ namespace SHADE 0.0f, 0.0f, 0.0f, 1.0f }; + const SHMatrix SHMatrix::Zero + { + SHVec4::Zero + , SHVec4::Zero + , SHVec4::Zero + , SHVec4::Zero + }; + /*-----------------------------------------------------------------------------------*/ /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/SHMatrix.h b/SHADE_Engine/src/Math/SHMatrix.h index 6af8fdc9..ffd8ce89 100644 --- a/SHADE_Engine/src/Math/SHMatrix.h +++ b/SHADE_Engine/src/Math/SHMatrix.h @@ -45,6 +45,7 @@ namespace SHADE static constexpr size_t NUM_COLS = 4U; static const SHMatrix Identity; + static const SHMatrix Zero; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ diff --git a/SHADE_Engine/src/Math/Vector/SHVec3.cpp b/SHADE_Engine/src/Math/Vector/SHVec3.cpp index 6b042c61..72d2b0a2 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec3.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec3.cpp @@ -372,6 +372,30 @@ namespace SHADE return lhs.Cross(rhs); } + SHMatrix SHVec3::OuterProduct(const SHVec3& lhs, const SHVec3& rhs) noexcept + { + /* + * Outer product is a matrix multiplication u * vT + * 3x1 * 1x3 = 3x3 + * + * | u1 | | v1 v2 v3 | | u1v1 u1v2 u1v3 | + * | u2 | = | u2v1 u2v2 u2v3 | + * | u3 | | u3v1 u3v2 u3v3 | + */ + + SHMatrix u = SHMatrix::Zero; + SHMatrix vT = SHMatrix::Zero; + + for (int i = 0; i < SIZE; ++i) + { + u.m[0][i] = lhs[i]; + vT.m[i][0] = rhs[i]; + } + + return u * vT; + } + + SHVec3 SHVec3::Project(const SHVec3& v, const SHVec3& u) noexcept { SHVec3 result; diff --git a/SHADE_Engine/src/Math/Vector/SHVec3.h b/SHADE_Engine/src/Math/Vector/SHVec3.h index 657e167e..4b0112c5 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec3.h +++ b/SHADE_Engine/src/Math/Vector/SHVec3.h @@ -115,27 +115,28 @@ namespace SHADE /* Static Function Members */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static SHVec3 Normalise (const SHVec3& v) noexcept; - [[nodiscard]] static SHVec3 Abs (const SHVec3& v) noexcept; - [[nodiscard]] static SHVec3 Min (const std::initializer_list& vs) noexcept; - [[nodiscard]] static SHVec3 Max (const std::initializer_list& vs) noexcept; - [[nodiscard]] static SHVec3 Clamp (const SHVec3& v, const SHVec3& vMin, const SHVec3& vMax) noexcept; - [[nodiscard]] static SHVec3 Lerp (const SHVec3& a, const SHVec3& b, float t) noexcept; - [[nodiscard]] static SHVec3 ClampedLerp (const SHVec3& a, const SHVec3& b, float t, float tMin = 0.0f, float tMax = 1.0f) noexcept; + [[nodiscard]] static SHVec3 Normalise (const SHVec3& v) noexcept; + [[nodiscard]] static SHVec3 Abs (const SHVec3& v) noexcept; + [[nodiscard]] static SHVec3 Min (const std::initializer_list& vs) noexcept; + [[nodiscard]] static SHVec3 Max (const std::initializer_list& vs) noexcept; + [[nodiscard]] static SHVec3 Clamp (const SHVec3& v, const SHVec3& vMin, const SHVec3& vMax) noexcept; + [[nodiscard]] static SHVec3 Lerp (const SHVec3& a, const SHVec3& b, float t) noexcept; + [[nodiscard]] static SHVec3 ClampedLerp (const SHVec3& a, const SHVec3& b, float t, float tMin = 0.0f, float tMax = 1.0f) noexcept; - [[nodiscard]] static float Distance (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static float DistanceSquared (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static float Angle (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static float Dot (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static SHVec3 Cross (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static SHVec3 Project (const SHVec3& v, const SHVec3& u) noexcept; - [[nodiscard]] static SHVec3 Reflect (const SHVec3& v, const SHVec3& normal) noexcept; - [[nodiscard]] static SHVec3 Rotate (const SHVec3& v, const SHVec3& axis, float angleInRad) noexcept; - [[nodiscard]] static SHVec3 Rotate (const SHVec3& v, const SHQuaternion& q) noexcept; - [[nodiscard]] static SHVec3 RotateX (const SHVec3& v, float angleInRad) noexcept; - [[nodiscard]] static SHVec3 RotateY (const SHVec3& v, float angleInRad) noexcept; - [[nodiscard]] static SHVec3 RotateZ (const SHVec3& v, float angleInRad) noexcept; - [[nodiscard]] static SHVec3 Transform (const SHVec3& v, const SHMatrix& transformMtx) noexcept; + [[nodiscard]] static float Distance (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static float DistanceSquared (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static float Angle (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static float Dot (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static SHVec3 Cross (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static SHMatrix OuterProduct (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static SHVec3 Project (const SHVec3& v, const SHVec3& u) noexcept; + [[nodiscard]] static SHVec3 Reflect (const SHVec3& v, const SHVec3& normal) noexcept; + [[nodiscard]] static SHVec3 Rotate (const SHVec3& v, const SHVec3& axis, float angleInRad) noexcept; + [[nodiscard]] static SHVec3 Rotate (const SHVec3& v, const SHQuaternion& q) noexcept; + [[nodiscard]] static SHVec3 RotateX (const SHVec3& v, float angleInRad) noexcept; + [[nodiscard]] static SHVec3 RotateY (const SHVec3& v, float angleInRad) noexcept; + [[nodiscard]] static SHVec3 RotateZ (const SHVec3& v, float angleInRad) noexcept; + [[nodiscard]] static SHVec3 Transform (const SHVec3& v, const SHMatrix& transformMtx) noexcept; }; SHVec3 operator* (float lhs, const SHVec3& rhs) noexcept; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index c4b27a42..3b2de659 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -14,6 +14,7 @@ #include "SHRigidBody.h" // Project Headers +#include #include #include "Physics/Collision/SHCollider.h" @@ -664,26 +665,24 @@ namespace SHADE const float CUSTOM_MASS = 1.0f / invMass; const bool AUTO_MASS = IsAutoMassEnabled(); const bool INCLUDE_TRIGGERS = IsTriggerInMassData(); - const auto& SHAPES = collider->GetCollisionShapes(); + // Compute Total mass and store individual masses if custom mass is being used. // Compute local centroid at the same time // Zero matrix; - SHMatrix tmpLocalTensor; - tmpLocalTensor -= SHMatrix::Identity; + SHMatrix J = SHMatrix::Zero; float totalMass = 0.0f; - std::vector trueMass; + std::vector trueMass; // We store the true masses here for calculating the ratio with custom masses. + const auto& SHAPES = collider->GetCollisionShapes(); for (auto* shape : SHAPES) { // We skip triggers by default if (shape->IsTrigger() && !INCLUDE_TRIGGERS) continue; - shape->ComputeTransforms(); - // p = m/v, therefore m = pv. This is the true mass of the shape. const float MASS = shape->GetDensity() * shape->GetVolume(); totalMass += MASS; @@ -705,6 +704,7 @@ namespace SHADE const SHMatrix R = SHMatrix::Rotate(motionState.orientation); const SHMatrix RT = SHMatrix::Transpose(R); + // We need the world centroid to compute the offset of the collider from the body's centroid worldCentroid = (R * localCentroid) + motionState.position; for (size_t i = 0; i < SHAPES.size(); ++i) @@ -721,31 +721,23 @@ namespace SHADE actualMass *= CUSTOM_MASS / totalMass; // Convert inertia tensor into local-space of the body + // R * I * RT = R * (I * RT) SHMatrix I = SHAPE->GetInertiaTensor( actualMass ) * RT; I = R * I; // Parallel Axis Theorem - const SHVec3 O = SHAPE->GetPosition() - worldCentroid; - const float O2 = O.LengthSquared(); + // J = I + m((R /dot R)E_3 - R /outerProduct R) + const SHVec3 R = SHAPE->GetPosition() - worldCentroid; + const float R_MAG2 = R.LengthSquared(); + const SHMatrix R_OX_R = SHVec3::OuterProduct(R, R); - SHMatrix offsetMatrix; - offsetMatrix -= SHMatrix::Identity; - offsetMatrix.m[0][0] = offsetMatrix.m[1][1] = offsetMatrix.m[2][2] = O2; - - const SHVec3 NOX = O * -O.x; - const SHVec3 NOY = O * -O.y; - const SHVec3 NOZ = O * -O.z; - - offsetMatrix += SHMatrix{ NOX, NOY, NOZ, SHVec4::Zero }; - offsetMatrix *= actualMass; - - tmpLocalTensor += I + offsetMatrix; + J += I + actualMass * (SHMatrix::Identity * R_MAG2 - R_OX_R); } // Set diagonals then invert - localInvInertia.m[0][0] = tmpLocalTensor.m[0][0]; - localInvInertia.m[1][1] = tmpLocalTensor.m[1][1]; - localInvInertia.m[2][2] = tmpLocalTensor.m[2][2]; + localInvInertia.m[0][0] = J.m[0][0]; + localInvInertia.m[1][1] = J.m[1][1]; + localInvInertia.m[2][2] = J.m[2][2]; localInvInertia = SHMatrix::Inverse(localInvInertia); } From 7da89def501cba684043a81cca5b3290c5612007 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 4 Jan 2023 17:42:02 +0800 Subject: [PATCH 077/164] Refactored SHBatch to support animation data in the same descriptor set --- Assets/Shaders/TestCube_VS.glsl | 3 +- Assets/Shaders/TestCube_VS.shshaderb | Bin 3689 -> 3857 bytes .../MaterialInspector/SHMaterialInspector.cpp | 2 +- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 186 +++++++++--------- .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 7 +- .../GlobalData/SHGraphicsPredefinedData.cpp | 70 +++++-- .../GlobalData/SHGraphicsPredefinedData.h | 16 +- .../GlobalData/SHPredefinedDescriptorTypes.h | 1 + .../MiddleEnd/Interface/SHGraphicsConstants.h | 18 +- .../MiddleEnd/Interface/SHMaterial.cpp | 2 +- .../Interface/SHMaterialInstance.cpp | 2 +- .../Serialization/SHSerializationHelper.hpp | 2 +- 12 files changed, 185 insertions(+), 124 deletions(-) diff --git a/Assets/Shaders/TestCube_VS.glsl b/Assets/Shaders/TestCube_VS.glsl index 554ce379..e5cf236c 100644 --- a/Assets/Shaders/TestCube_VS.glsl +++ b/Assets/Shaders/TestCube_VS.glsl @@ -10,7 +10,8 @@ layout(location = 2) in vec3 aNormal; layout(location = 3) in vec3 aTangent; layout(location = 4) in mat4 worldTransform; layout(location = 8) in uvec2 integerData; - +layout(location = 9) in uvec4 aBoneIndices; +layout(location = 10) in vec4 aBoneWeights; layout(location = 0) out struct { diff --git a/Assets/Shaders/TestCube_VS.shshaderb b/Assets/Shaders/TestCube_VS.shshaderb index a1138f75c885a67d6cb7ac2abb1928fe9de3a08e..e8028449dc8c8eb67ba7d745680831fbe63e5194 100644 GIT binary patch delta 209 zcmaDUGf_^Ak%ONB449de*%=rZ8MqjD8EPkrnKBAa^s!{E0?O5FY#e(=A~pNrxt@Wg2ZcJ;^C>8=@})(n-iIDFv_|!uz+>Q0cAOX7{mvu1o62xo3VN` xdx`^vDu7&|%|I3dg94DO1k#K^3=+47@lNNT*dL86#y(N9?k#& delta 54 zcmbOz_fkfTF@cW(449de*%=rZ8MqjD87d}GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface ( mappings.at(SHPredefinedDescriptorTypes::MATERIALS), - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA + SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA ); if (!interface) return; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 78cc90c3..44fc87b0 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -65,13 +65,13 @@ namespace SHADE , transformDataBuffer { rhs.transformDataBuffer } , instancedIntegerBuffer { rhs.instancedIntegerBuffer } , matPropsBuffer { rhs.matPropsBuffer } - , matPropsDescSet { rhs.matPropsDescSet } + , instanceDataDescSet { rhs.instanceDataDescSet } { rhs.drawDataBuffer = {}; rhs.transformDataBuffer = {}; rhs.instancedIntegerBuffer = {}; rhs.matPropsBuffer = {}; - rhs.matPropsDescSet = {}; + rhs.instanceDataDescSet = {}; } SHBatch& SHBatch::operator=(SHBatch&& rhs) @@ -97,14 +97,14 @@ namespace SHADE transformDataBuffer = rhs.transformDataBuffer ; instancedIntegerBuffer = rhs.instancedIntegerBuffer ; matPropsBuffer = rhs.matPropsBuffer ; - matPropsDescSet = rhs.matPropsDescSet ; + instanceDataDescSet = rhs.instanceDataDescSet ; // Unset values rhs.drawDataBuffer = {}; rhs.transformDataBuffer = {}; rhs.instancedIntegerBuffer = {}; rhs.matPropsBuffer = {}; - rhs.matPropsDescSet = {}; + rhs.instanceDataDescSet = {}; return *this; } @@ -122,8 +122,8 @@ namespace SHADE instancedIntegerBuffer[i].Free(); if (matPropsBuffer[i]) matPropsBuffer[i].Free(); - if (matPropsDescSet[i]) - matPropsDescSet[i].Free(); + if (instanceDataDescSet[i]) + instanceDataDescSet[i].Free(); } } @@ -289,7 +289,7 @@ namespace SHADE } // Transfer to GPU - rebuildMaterialBuffers(frameIndex, descPool); + rebuildDescriptorSetBuffers(frameIndex, descPool); // This frame is updated matBufferDirty[frameIndex] = false; @@ -421,7 +421,7 @@ namespace SHADE const Handle SHADER_INFO = pipeline->GetPipelineLayout()->GetShaderBlockInterface ( descMappings.at(SHPredefinedDescriptorTypes::MATERIALS), - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, + SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, vk::ShaderStageFlagBits::eFragment ); const bool EMPTY_MAT_PROPS = !SHADER_INFO; @@ -557,10 +557,8 @@ namespace SHADE BuffUsage::eVertexBuffer, "Batch Instance Data Buffer" ); - // - Material Properties Buffer - rebuildMaterialBuffers(frameIndex, descPool); - // - Bone Buffers - rebuildBoneBuffers(frameIndex, descPool); + // - Material and bone buffers/descriptor sets + rebuildDescriptorSetBuffers(frameIndex, descPool); } // Mark this frame as no longer dirty @@ -588,15 +586,14 @@ namespace SHADE cmdBuffer->BindPipeline(pipeline); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::INTEGER_DATA, instancedIntegerBuffer[frameIndex], 0); - if (matPropsDescSet[frameIndex]) - { - auto const& descMappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); + auto const& descMappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); + if (instanceDataDescSet[frameIndex]) + { cmdBuffer->BindDescriptorSet ( - matPropsDescSet[frameIndex], + instanceDataDescSet[frameIndex], SH_PIPELINE_TYPE::GRAPHICS, - //SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, descMappings.at(SHPredefinedDescriptorTypes::MATERIALS), dynamicOffset ); @@ -604,13 +601,6 @@ namespace SHADE if (boneMatrixBuffer[frameIndex] && boneFirstIndexBuffer[frameIndex]) { cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::BONE_INDICES, boneFirstIndexBuffer[frameIndex], 0); - cmdBuffer->BindDescriptorSet - ( - boneMatricesDescSet[frameIndex], - SH_PIPELINE_TYPE::GRAPHICS, - SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - dynamicOffset - ); } cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); cmdBuffer->EndLabeledSegment(); @@ -651,107 +641,109 @@ namespace SHADE isCPUBuffersDirty = true; } - void SHBatch::rebuildMaterialBuffers(uint32_t frameIndex, Handle descPool) + void SHBatch::rebuildDescriptorSetBuffers(uint32_t frameIndex, Handle descPool) { - if (matPropsData && !drawData.empty()) - { - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, matPropsBuffer[frameIndex], matPropsData.get(), static_cast(matPropsDataSize), - vk::BufferUsageFlagBits::eStorageBuffer, - "Batch Material Data" - ); + // Using Declarations and constants + using BuffUsage = vk::BufferUsageFlagBits; + using PreDefDescLayoutType = SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes; + static constexpr uint32_t MATERIAL_DESC_SET_INDEX = 0; - if (!matPropsDescSet[frameIndex]) + // Flags + bool descSetUpdateRequired = false; + + /* Create Descriptor Sets if Needed */ + PreDefDescLayoutType layoutTypes = {}; + if (matPropsData) + layoutTypes |= PreDefDescLayoutType::MATERIALS; + if (!boneMatrixData.empty()) + layoutTypes |= PreDefDescLayoutType::BONES; + + if (matPropsData || !boneMatrixData.empty()) + { + // Make sure that we have a descriptor set if we don't already have one + if (!instanceDataDescSet[frameIndex]) { - matPropsDescSet[frameIndex] = descPool->Allocate + instanceDataDescSet[frameIndex] = descPool->Allocate ( - SHGraphicsPredefinedData::GetPredefinedDescSetLayouts(SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIALS), + SHGraphicsPredefinedData::GetPredefinedDescSetLayouts(layoutTypes), { 0 } ); #ifdef _DEBUG - const auto& DESC_SETS = matPropsDescSet[frameIndex]->GetVkHandle(); + const auto& DESC_SETS = instanceDataDescSet[frameIndex]->GetVkHandle(); for (auto descSet : DESC_SETS) { SET_VK_OBJ_NAME(device, vk::ObjectType::eDescriptorSet, descSet, "[Descriptor Set] Batch Material Data"); } #endif } + } - static constexpr uint32_t MATERIAL_DESC_SET_INDEX = 0; + /* Material Data */ + if (matPropsData && !drawData.empty()) + { + // Update GPU buffer + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, matPropsBuffer[frameIndex], matPropsData.get(), static_cast(matPropsDataSize), + BuffUsage::eStorageBuffer, + "Batch Material Data" + ); + // Update descriptor set buffer std::array, 1> bufferList = { matPropsBuffer[frameIndex] }; - matPropsDescSet[frameIndex]->ModifyWriteDescBuffer + instanceDataDescSet[frameIndex]->ModifyWriteDescBuffer ( MATERIAL_DESC_SET_INDEX, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, + SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, bufferList, 0, static_cast(matPropsDataSize) ); - matPropsDescSet[frameIndex]->UpdateDescriptorSetBuffer + + descSetUpdateRequired = true; + } + + /* Animation Bone Data */ + if (!boneMatrixData.empty()) + { + // Update GPU Buffers + const uint32_t BONE_IDX_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneFirstIndexBuffer[frameIndex], boneMatrixIndices.data(), BONE_IDX_DATA_BYTES, + BuffUsage::eVertexBuffer, + "Batch Bone Indices Buffer" + ); + const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(uint32_t)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneMatrixBuffer[frameIndex], boneMatrixData.data(), BONE_MTX_DATA_BYTES, + BuffUsage::eStorageBuffer, + "Batch Bone Matrix Buffer" + ); + + // Update descriptor set buffer + std::array, 1> bufferList = { boneMatrixBuffer[frameIndex] }; + instanceDataDescSet[frameIndex]->ModifyWriteDescBuffer ( MATERIAL_DESC_SET_INDEX, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA + SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA, + bufferList, + static_cast(matPropsDataSize), + static_cast(boneMatrixData.size() * sizeof(SHMatrix)) ); + + descSetUpdateRequired = true; } - } - void SHBatch::rebuildBoneBuffers(uint32_t frameIndex, Handle descPool) - { - // Using Declarations - using BuffUsage = vk::BufferUsageFlagBits; - - // Nothing to rebuild - if (boneMatrixData.empty()) - return; - - // Update GPU Buffers - const uint32_t BONE_IDX_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, boneFirstIndexBuffer[frameIndex], boneMatrixIndices.data(), BONE_IDX_DATA_BYTES, - BuffUsage::eVertexBuffer, - "Batch Bone Index Buffer" - ); - const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(uint32_t)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, boneMatrixBuffer[frameIndex], boneMatrixData.data(), BONE_MTX_DATA_BYTES, - BuffUsage::eStorageBuffer, - "Batch Bone Matrix Buffer" - ); - - // Update descriptor set buffer - if (!boneMatricesDescSet[frameIndex]) - { - boneMatricesDescSet[frameIndex] = descPool->Allocate + // Build and prepare the descriptor set if necessary + if (descSetUpdateRequired) + { + // Update the descriptor set buffer + instanceDataDescSet[frameIndex]->UpdateDescriptorSetBuffer ( - { SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE] }, - { 0 } + MATERIAL_DESC_SET_INDEX, + SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA ); - -#ifdef _DEBUG - const auto& DESC_SETS = boneMatricesDescSet[frameIndex]->GetVkHandle(); - for (auto descSet : DESC_SETS) - { - SET_VK_OBJ_NAME(device, vk::ObjectType::eDescriptorSet, descSet, "[Descriptor Set] Batch Bone Data"); - } -#endif } - std::array, 1> bufferList = { boneMatrixBuffer[frameIndex] }; - boneMatricesDescSet[frameIndex]->ModifyWriteDescBuffer - ( - SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, - bufferList, - 0, static_cast(boneMatrixData.size() * sizeof(SHMatrix)) - ); - boneMatricesDescSet[frameIndex]->UpdateDescriptorSetBuffer - ( - SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA - ); - - // TODO: Need to merge this with the material one } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index 02223b75..318e97d8 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -133,15 +133,14 @@ namespace SHADE TripleBuffer transformDataBuffer; TripleBuffer instancedIntegerBuffer; TripleBuffer matPropsBuffer; - TripleDescSet matPropsDescSet; TripleBuffer boneMatrixBuffer; TripleBuffer boneFirstIndexBuffer; - TripleDescSet boneMatricesDescSet; + TripleDescSet instanceDataDescSet; + /*-----------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------*/ void setAllDirtyFlags(); - void rebuildMaterialBuffers(uint32_t frameIndex, Handle descPool); - void rebuildBoneBuffers(uint32_t frameIndex, Handle descPool); + void rebuildDescriptorSetBuffers(uint32_t frameIndex, Handle descPool); }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp index ffe29b36..2d741f7f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp @@ -20,14 +20,21 @@ namespace SHADE //SHGraphicsPredefinedData::PerSystem SHGraphicsPredefinedData::renderGraphNodeComputeData; void SHGraphicsPredefinedData::InitDescMappings(void) noexcept - { - + { perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].descMappings.AddMappings ({ {SHPredefinedDescriptorTypes::STATIC_DATA, 0}, {SHPredefinedDescriptorTypes::CAMERA, 1}, {SHPredefinedDescriptorTypes::MATERIALS, 2}, }); + + perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING_ANIM)].descMappings.AddMappings + ({ + {SHPredefinedDescriptorTypes::STATIC_DATA, 0}, + {SHPredefinedDescriptorTypes::CAMERA, 1}, + {SHPredefinedDescriptorTypes::MATERIALS, 2}, + {SHPredefinedDescriptorTypes::BONES, 3}, + }); perSystemData[SHUtilities::ConvertEnum(SystemType::TEXT_RENDERING)].descMappings.AddMappings ({ @@ -48,9 +55,13 @@ namespace SHADE void SHGraphicsPredefinedData::InitDummyPipelineLayouts(Handle logicalDevice) noexcept { - perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].dummyPipelineLayout = logicalDevice->CreatePipelineLayoutDummy(SHPipelineLayoutParamsDummy{ perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].descSetLayouts }); - perSystemData[SHUtilities::ConvertEnum(SystemType::TEXT_RENDERING)].dummyPipelineLayout = logicalDevice->CreatePipelineLayoutDummy(SHPipelineLayoutParamsDummy{ perSystemData[SHUtilities::ConvertEnum(SystemType::TEXT_RENDERING)].descSetLayouts }); - perSystemData[SHUtilities::ConvertEnum(SystemType::RENDER_GRAPH_NODE_COMPUTE)].dummyPipelineLayout = logicalDevice->CreatePipelineLayoutDummy(SHPipelineLayoutParamsDummy{ perSystemData[SHUtilities::ConvertEnum(SystemType::RENDER_GRAPH_NODE_COMPUTE)].descSetLayouts }); + for (int i = 0; i < SYSTEM_TYPE_COUNT; ++i) + { + perSystemData[i].dummyPipelineLayout = logicalDevice->CreatePipelineLayoutDummy + ( + SHPipelineLayoutParamsDummy { perSystemData[i].descSetLayouts } + ); + } } /*-----------------------------------------------------------------------------------*/ @@ -122,8 +133,8 @@ namespace SHADE SHVkDescriptorSetLayout::Binding materialDataBinding { .Type = vk::DescriptorType::eStorageBufferDynamic, - .Stage = vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eVertex, - .BindPoint = SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, + .Stage = vk::ShaderStageFlagBits::eFragment, + .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, .DescriptorCount = 1, }; Handle materialDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout({ materialDataBinding }); @@ -150,12 +161,25 @@ namespace SHADE Handle fontDataDescSetLayout = logicalDevice->CreateDescriptorSetLayout({ fontBitmapBinding, fontMatrixBinding }); SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, fontDataDescSetLayout->GetVkHandle(), "[Descriptor Set Layout] Font Data"); + // Bone matrix data + SHVkDescriptorSetLayout::Binding boneMatrixBinding + { + .Type = vk::DescriptorType::eStorageBuffer, + .Stage = vk::ShaderStageFlagBits::eVertex, + .BindPoint = SHGraphicsConstants::DescriptorSetBindings::BONE_MATRIX_DATA, + .DescriptorCount = 1, + }; + + Handle boneMatricesDescSetLayout = logicalDevice->CreateDescriptorSetLayout({ boneMatrixBinding }); + SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, boneMatricesDescSetLayout->GetVkHandle(), "[Descriptor Set Layout] Bone Matrix Data"); predefinedLayouts.push_back(staticGlobalLayout); predefinedLayouts.push_back(lightDataDescSetLayout); predefinedLayouts.push_back(cameraDataGlobalLayout); predefinedLayouts.push_back(materialDataPerInstanceLayout); predefinedLayouts.push_back(fontDataDescSetLayout); + predefinedLayouts.push_back({}); + predefinedLayouts.push_back(boneMatricesDescSetLayout); perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].descSetLayouts = GetPredefinedDescSetLayouts ( @@ -164,6 +188,14 @@ namespace SHADE SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIALS ); + perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING_ANIM)].descSetLayouts = GetPredefinedDescSetLayouts + ( + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::STATIC_DATA | + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::CAMERA | + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIALS | + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::BONES + ); + perSystemData[SHUtilities::ConvertEnum(SystemType::TEXT_RENDERING)].descSetLayouts = GetPredefinedDescSetLayouts ( SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::STATIC_DATA | @@ -181,12 +213,14 @@ namespace SHADE void SHGraphicsPredefinedData::InitDefaultVertexInputState(void) noexcept { - defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // positions at binding 0 - defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_2D) }); // UVs at binding 1 - defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Normals at binding 2 - defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Tangents at binding 3 - defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Transform at binding 4 - 7 (4 slots) + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // positions at binding 0 + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_2D) }); // UVs at binding 1 + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Normals at binding 2 + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Tangents at binding 3 + defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Transform at binding 4 - 7 (4 slots) defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::UINT32_2D) }); // Instanced integer data at index 8 + defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::UINT32_4D) }); // Instanced bone indices at index 9 + defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); // Instanced bone weights at index 10 } void SHGraphicsPredefinedData::Init(Handle logicalDevice) noexcept @@ -232,12 +266,24 @@ namespace SHADE return static_cast(static_cast(lhs) | static_cast(rhs)); } + SHADE::SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes& operator|=(SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes& lhs, SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes rhs) noexcept + { + lhs = lhs | rhs; + return lhs; + } + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes operator&(SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes lhs, SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes rhs) noexcept { return static_cast(static_cast(lhs) & static_cast(rhs)); } + SHADE::SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes& operator&=(SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes& lhs, SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes rhs) noexcept + { + lhs = lhs & rhs; + return lhs; + } + //SHGraphicsPredefinedData::PerSystem const& SHGraphicsPredefinedData::GetBatchingSystemData(void) noexcept //{ // return batchingSystemData; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h index 11bfc469..5f574b2d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h @@ -23,20 +23,24 @@ namespace SHADE // This enum is mainly to initialize a bit field to retrieve bit fields from SHPRedefinedData enum class PredefinedDescSetLayoutTypes : uint64_t { - STATIC_DATA = 0x01, - LIGHTS = 0x02, - CAMERA = 0x04, - MATERIALS = 0x08, - FONT = 0x10, + STATIC_DATA = 0b0000001, + LIGHTS = 0b0000010, + CAMERA = 0b0000100, + MATERIALS = 0b0001000, + FONT = 0b0010000, + SHADOW = 0b0100000, + BONES = 0b1000000, }; enum class SystemType { BATCHING = 0, + BATCHING_ANIM, TEXT_RENDERING, RENDER_GRAPH_NODE_COMPUTE, NUM_TYPES }; + static constexpr int SYSTEM_TYPE_COUNT = static_cast(SystemType::NUM_TYPES); struct PerSystem { @@ -98,5 +102,7 @@ namespace SHADE }; SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes operator| (SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes lhs, SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes rhs) noexcept; + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes& operator|=(SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes& lhs, SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes rhs) noexcept; SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes operator& (SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes lhs, SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes rhs) noexcept; + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes& operator&=(SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes& lhs, SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes rhs) noexcept; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h index 931101f4..64a7fa6d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h @@ -12,6 +12,7 @@ namespace SHADE LIGHTS, CAMERA, MATERIALS, + BONES, FONT, RENDER_GRAPH_RESOURCE, RENDER_GRAPH_NODE_COMPUTE_RESOURCE, diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index 3f0ec30e..3e8e379e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -138,7 +138,15 @@ namespace SHADE DescriptorSet binding for per instance material values. */ /***************************************************************************/ - static constexpr uint32_t BATCHED_PER_INST_DATA = 0; + static constexpr uint32_t PER_INST_MATERIAL_DATA = 0; + + /***************************************************************************/ + /*! + \brief + DescriptorSet binding for per instance bone values. + */ + /***************************************************************************/ + static constexpr uint32_t PER_INST_BONE_DATA = 1; /***************************************************************************/ /*! @@ -158,6 +166,14 @@ namespace SHADE /***************************************************************************/ static constexpr uint32_t FONT_MATRIX_DATA = 1; + /***************************************************************************/ + /*! + \brief + Descriptor set binding for bone matrix data. + + */ + /***************************************************************************/ + static constexpr uint32_t BONE_MATRIX_DATA = 0; }; struct VertexBufferBindings diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.cpp index 3e944a5f..59f3ba73 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.cpp @@ -102,7 +102,7 @@ namespace SHADE ( mappings.at (SHPredefinedDescriptorTypes::MATERIALS), //SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, + SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, vk::ShaderStageFlagBits::eFragment ); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.cpp index 1506cf71..70204401 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.cpp @@ -83,7 +83,7 @@ namespace SHADE ( SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING).at(SHPredefinedDescriptorTypes::MATERIALS), //SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, + SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, vk::ShaderStageFlagBits::eFragment ); } diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index f37c7277..ece0e452 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -325,7 +325,7 @@ namespace SHADE { auto fragShader = SHResourceManager::LoadOrGet(spec.fragShader); auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); - auto interface = fragShader->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(mappings.at(SHPredefinedDescriptorTypes::MATERIALS), SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA); + auto interface = fragShader->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(mappings.at(SHPredefinedDescriptorTypes::MATERIALS), SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA); int const varCount = static_cast(interface->GetVariableCount()); for (int i = 0; i < varCount; ++i) From dd2fc934a2dd6c12439dea9d49fbe8bae891895c Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 4 Jan 2023 17:48:08 +0800 Subject: [PATCH 078/164] Removed Redundant Geometry --- .../Inspector/SHEditorComponentView.hpp | 4 +- SHADE_Engine/src/Math/Geometry/SHBox.cpp | 154 ---------- SHADE_Engine/src/Math/Geometry/SHBox.h | 138 --------- SHADE_Engine/src/Math/Geometry/SHSphere.cpp | 150 ---------- SHADE_Engine/src/Math/Geometry/SHSphere.h | 146 ---------- .../{SHBoxCollisionShape.cpp => SHBox.cpp} | 275 +++++++++--------- .../{SHBoxCollisionShape.h => SHBox.h} | 116 +++----- .../CollisionShapes/SHCollisionShape.cpp | 31 +- .../CollisionShapes/SHCollisionShape.h | 104 +++++-- .../SHCollisionShapeLibrary.cpp | 14 +- .../CollisionShapes/SHCollisionShapeLibrary.h | 12 +- ...lisionShape.cpp => SHConvexPolyhedron.cpp} | 22 +- ...nCollisionShape.h => SHConvexPolyhedron.h} | 16 +- ...HSphereCollisionShape.cpp => SHSphere.cpp} | 184 ++++++------ .../{SHSphereCollisionShape.h => SHSphere.h} | 98 ++----- .../Collision/Contacts/SHCollisionKey.cpp | 2 +- .../Narrowphase/SHCapsuleVsConvex.cpp | 2 +- .../Collision/Narrowphase/SHCollision.h | 24 +- .../Narrowphase/SHSphereVsCapsule.cpp | 2 +- .../Narrowphase/SHSphereVsConvex.cpp | 42 +-- .../Narrowphase/SHSphereVsSphere.cpp | 41 ++- .../Physics/Collision/SHCollisionSpace.cpp | 10 +- .../src/Physics/Collision/SHCollisionSpace.h | 10 +- ...SHCollider.cpp => SHCompositeCollider.cpp} | 72 ++--- .../{SHCollider.h => SHCompositeCollider.h} | 16 +- .../src/Physics/Dynamics/SHRigidBody.cpp | 9 +- .../src/Physics/Dynamics/SHRigidBody.h | 6 +- .../PhysicsObject/SHPhysicsObject.cpp | 8 +- .../Interface/PhysicsObject/SHPhysicsObject.h | 8 +- .../Physics/Interface/SHColliderComponent.cpp | 6 +- .../Physics/Interface/SHColliderComponent.h | 10 +- .../System/SHPhysicsDebugDrawSystem.cpp | 6 +- .../Physics/System/SHPhysicsDebugDrawSystem.h | 6 +- .../src/Serialization/SHYAMLConverters.h | 10 +- SHADE_Managed/src/Components/Collider.cxx | 18 +- SHADE_Managed/src/Physics/Physics.cxx | 45 +-- 36 files changed, 556 insertions(+), 1261 deletions(-) delete mode 100644 SHADE_Engine/src/Math/Geometry/SHBox.cpp delete mode 100644 SHADE_Engine/src/Math/Geometry/SHBox.h delete mode 100644 SHADE_Engine/src/Math/Geometry/SHSphere.cpp delete mode 100644 SHADE_Engine/src/Math/Geometry/SHSphere.h rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHBoxCollisionShape.cpp => SHBox.cpp} (51%) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHBoxCollisionShape.h => SHBox.h} (53%) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHConvexPolyhedronCollisionShape.cpp => SHConvexPolyhedron.cpp} (76%) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHConvexPolyhedronCollisionShape.h => SHConvexPolyhedron.h} (83%) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHSphereCollisionShape.cpp => SHSphere.cpp} (51%) rename SHADE_Engine/src/Physics/Collision/CollisionShapes/{SHSphereCollisionShape.h => SHSphere.h} (55%) rename SHADE_Engine/src/Physics/Collision/{SHCollider.cpp => SHCompositeCollider.cpp} (81%) rename SHADE_Engine/src/Physics/Collision/{SHCollider.h => SHCompositeCollider.h} (92%) diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index ed6b7bd4..765dbc9b 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -358,7 +358,7 @@ namespace SHADE { SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - auto* box = reinterpret_cast(shape); + auto* box = reinterpret_cast(shape); SHEditorWidgets::DragVec3 ( "Half Extents", { "X", "Y", "Z" }, @@ -368,7 +368,7 @@ namespace SHADE else if (shape->GetType() == SHCollisionShape::Type::SPHERE) { SHEditorWidgets::BeginPanel(std::format("{} Sphere #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - auto* sphere = reinterpret_cast(shape); + auto* sphere = reinterpret_cast(shape); SHEditorWidgets::DragFloat ( "Radius", diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.cpp b/SHADE_Engine/src/Math/Geometry/SHBox.cpp deleted file mode 100644 index bcdb7324..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHBox.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************************** - * \file SHBox.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a 3-Dimensional Oriented Bounding Box - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHBox.h" -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Math/SHRay.h" -#include "Math/SHQuaternion.h" - -using namespace DirectX; - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHBox::SHBox() noexcept - { - Extents = SHVec3::One * 0.5f; - } - - SHBox::SHBox(const SHVec3& c, const SHVec3& hE, const SHQuaternion& o) noexcept - { - Center = c; - Extents = hE; - Orientation = o; - } - - - SHBox::SHBox(const SHBox& rhs) noexcept - { - if (this == &rhs) - return; - - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - } - - SHBox::SHBox(SHBox&& rhs) noexcept - { - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHBox& SHBox::operator=(const SHBox& rhs) noexcept - { - if (this != &rhs) - { - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - } - - return *this; - } - - SHBox& SHBox::operator=(SHBox&& rhs) noexcept - { - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - - return *this; - } - - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - std::vector SHBox::GetVertices() const noexcept - { - std::vector vertices; - vertices.resize(8); - GetCorners(vertices.data()); - return vertices; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHBox::TestPoint(const SHVec3& point) const noexcept - { - return BoundingOrientedBox::Contains(point); - } - - SHRaycastResult SHBox::Raycast(const SHRay& ray) const noexcept - { - SHRaycastResult result; - - result.hit = Intersects(ray.position, ray.direction, result.distance); - if (result.hit) - { - result.position = ray.position + ray.direction * result.distance; - result.angle = SHVec3::Angle(ray.position, result.position); - - // TODO: Compute Normal - } - - return result; - } - - bool SHBox::Contains(const SHBox& rhs) const noexcept - { - return BoundingOrientedBox::Contains(rhs) == CONTAINS; - } - - float SHBox::Volume() const noexcept - { - return 8.0f * (Extents.x * Extents.y * Extents.z); - } - - float SHBox::SurfaceArea() const noexcept - { - return 8.0f * ((Extents.x * Extents.y) - + (Extents.x * Extents.z) - + (Extents.y * Extents.z)); - } - - /*-----------------------------------------------------------------------------------*/ - /* Static Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHBox::Intersect(const SHBox& lhs, const SHBox& rhs) noexcept - { - return lhs.Intersects(rhs); - } - - SHBox SHBox::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept - { - SHBox result; - CreateFromPoints(result, numVertices, vertices, stride); - return result; - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.h b/SHADE_Engine/src/Math/Geometry/SHBox.h deleted file mode 100644 index 7815ce2c..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHBox.h +++ /dev/null @@ -1,138 +0,0 @@ -/**************************************************************************************** - * \file SHBox.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a 3-Dimensional Oriented Bounding Box - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "SHShape.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a 3D-oriented bounding box. - */ - class SH_API SHBox : public SHShape, - public DirectX::BoundingOrientedBox - { - public: - /*---------------------------------------------------------------------------------*/ - /* Static Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr size_t NUM_VERTICES = 8; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - ~SHBox () override = default; - - SHBox () noexcept; - SHBox (const SHVec3& center, const SHVec3& halfExtents, const SHQuaternion& orientation) noexcept; - SHBox (const SHBox& rhs) noexcept; - SHBox (SHBox&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHBox& operator= (const SHBox& rhs) noexcept; - SHBox& operator= (SHBox&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] std::vector GetVertices () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Checks if a point is inside the box. - * @param point - * The point to check. - * @return - * True if the point is inside the box. - */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - - /** - * @brief - * Casts a ray against the box. - * @param ray - * The ray to cast. - * @return - * The result of the raycast.
- * See the corresponding header for the contents of the raycast result object. - */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; - - /** - * @brief - * Checks if an entire other box is contained by this box. - * @param rhs - * The box to check. - * @return - * True if the other sphere is completely contained by this box. - */ - [[nodiscard]] bool Contains (const SHBox& rhs) const noexcept; - - /** - * @brief - * Calculates the volume of the box. - */ - [[nodiscard]] float Volume () const noexcept; - - /** - * @brief - * Calculates the surface area of the box. - */ - [[nodiscard]] float SurfaceArea () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Static Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Checks if two boxes are intersecting. - * @return - * True if they are intersecting. - */ - [[nodiscard]] static bool Intersect (const SHBox& lhs, const SHBox& rhs) noexcept; - - /** - * @brief - * Builds a box from a set of vertices. - * @param vertices - * The vertices to build a box from. - * @param numVertices - * The number of vertices in the set to build from. - * @param stride - * The stride between each vertex, in the instance there is data in between each - * vertex that does not define the geometry of the object. - * @return - * An box that contains all the vertices in the set. - */ - [[nodiscard]] static SHBox BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; - }; - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp deleted file mode 100644 index 9d7a7c68..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/**************************************************************************************** - * \file SHSphere.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Bounding Sphere - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHSphere.h" -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Math/SHRay.h" - -using namespace DirectX; - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHSphere::SHSphere() noexcept - { - Radius = 0.5f; - } - - SHSphere::SHSphere(const SHVec3& center, float radius) noexcept - { - Center = center; - Radius = radius; - } - - SHSphere::SHSphere(const SHSphere& rhs) noexcept - { - if (this == &rhs) - return; - - Center = rhs.Center; - Radius = rhs.Radius; - } - - SHSphere::SHSphere(SHSphere&& rhs) noexcept - { - Center = rhs.Center; - Radius = rhs.Radius; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHSphere& SHSphere::operator=(const SHSphere& rhs) noexcept - { - if (this != &rhs) - { - Center = rhs.Center; - Radius = rhs.Radius; - } - - return *this; - } - - SHSphere& SHSphere::operator=(SHSphere&& rhs) noexcept - { - - Center = rhs.Center; - Radius = rhs.Radius; - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHSphere::TestPoint(const SHVec3& point) const noexcept - { - return BoundingSphere::Contains(point); - } - - SHRaycastResult SHSphere::Raycast(const SHRay& ray) const noexcept - { - SHRaycastResult result; - - result.hit = Intersects(ray.position, ray.direction, result.distance); - if (result.hit) - { - result.position = ray.position + ray.direction * result.distance; - result.angle = SHVec3::Angle(ray.position, result.position); - - // TODO: Compute Normal - } - - return result; - } - - bool SHSphere::Contains(const SHSphere& rhs) const noexcept - { - return BoundingSphere::Contains(rhs) == CONTAINS; - } - - float SHSphere::Volume() const noexcept - { - return (4.0f / 3.0f) * SHMath::PI * (Radius * Radius * Radius); - } - - float SHSphere::SurfaceArea() const noexcept - { - return 4.0f * SHMath::PI * (Radius * Radius); - } - - /*-----------------------------------------------------------------------------------*/ - /* Static Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHSphere SHSphere::Combine(const SHSphere& lhs, const SHSphere& rhs) noexcept - { - SHSphere result; - CreateMerged(result, lhs, rhs); - return result; - } - - bool SHSphere::Intersect(const SHSphere& lhs, const SHSphere& rhs) noexcept - { - return lhs.Intersects(rhs); - } - - SHSphere SHSphere::BuildFromSpheres(const SHSphere* spheres, size_t numSpheres) noexcept - { - SHSphere result; - - for (size_t i = 1; i < numSpheres; ++i) - CreateMerged(result, spheres[i - 1], spheres[i]); - - return result; - } - - SHSphere SHSphere::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept - { - SHSphere result; - CreateFromPoints(result, numVertices, vertices, stride); - return result; - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.h b/SHADE_Engine/src/Math/Geometry/SHSphere.h deleted file mode 100644 index 3c534ac4..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHSphere.h +++ /dev/null @@ -1,146 +0,0 @@ -/**************************************************************************************** - * \file SHSphere.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Bounding Sphere. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "SHShape.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a 3D Sphere. - */ - class SH_API SHSphere : public SHShape, - public DirectX::BoundingSphere - { - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHSphere () noexcept; - SHSphere (const SHVec3& center, float radius) noexcept; - SHSphere (const SHSphere& rhs) noexcept; - SHSphere (SHSphere&& rhs) noexcept; - - ~SHSphere () override = default; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHSphere& operator= (const SHSphere& rhs) noexcept; - SHSphere& operator= (SHSphere&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Checks if a point is inside the sphere. - * @param point - * The point to check. - * @return - * True if the point is inside the sphere. - */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - - /** - * @brief - * Casts a ray against the sphere. - * @param ray - * The ray to cast. - * @return - * The result of the raycast.
- * See the corresponding header for the contents of the raycast result object. - */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; - - /** - * @brief - * Checks if an entire other sphere is contained by this sphere. - * @param rhs - * The sphere to check. - * @return - * True if the other sphere is completely contained by this sphere. - */ - [[nodiscard]] bool Contains (const SHSphere& rhs) const noexcept; - - /** - * @brief - * Calculates the volume of the sphere. - */ - [[nodiscard]] float Volume () const noexcept; - - /** - * @brief - * Calculates the surface area of the sphere. - */ - [[nodiscard]] float SurfaceArea () const noexcept; - - - /*---------------------------------------------------------------------------------*/ - /* Static Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Combines two spheres to form a larger sphere. - * If one sphere is completely contained by the other, the result is the larger sphere. - * @return - * The combined sphere. - */ - [[nodiscard]] static SHSphere Combine (const SHSphere& lhs, const SHSphere& rhs) noexcept; - - /** - * @brief - * Checks if two spheres are intersecting. - * @return - * True if they are intersecting. - */ - [[nodiscard]] static bool Intersect (const SHSphere& lhs, const SHSphere& rhs) noexcept; - - /** - * @brief - * Builds a single sphere from multiple spheres. - * @param spheres - * The set of spheres to build from. - * @param numSpheres - * The number of spheres in the set to build from. - * @return - * A sphere that contains all the spheres in the set. - */ - [[nodiscard]] static SHSphere BuildFromSpheres (const SHSphere* spheres, size_t numSpheres) noexcept; - - /** - * @brief - * Builds a sphere from a set of vertices. - * @param vertices - * The vertices to build a sphere from. - * @param numVertices - * The number of vertices in the set to build from. - * @param stride - * The stride between each vertex, in the instance there is data in between each - * vertex that does not define the geometry of the object. - * @return - * A sphere that contains all the vertices in the set. - */ - [[nodiscard]] static SHSphere BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; - }; -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.cpp similarity index 51% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.cpp index 7a6ab7d2..b46c92da 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.cpp @@ -11,12 +11,12 @@ #include // Primary Header -#include "SHBoxCollisionShape.h" +#include "SHBox.h" // Project Headers #include "Math/SHMathHelpers.h" #include "Math/SHMatrix.h" -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" namespace SHADE { @@ -24,66 +24,46 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHBoxCollisionShape::SHBoxCollisionShape(SHCollisionShapeID id) noexcept - : SHConvexPolyhedronCollisionShape (id, SHCollisionShape::Type::BOX) - , SHBox () - , relativeExtents { SHVec3::One } - , scale { SHVec3::One } - {} - - SHBoxCollisionShape::SHBoxCollisionShape(const SHBoxCollisionShape& rhs) noexcept - : SHConvexPolyhedronCollisionShape (rhs.id, SHCollisionShape::Type::BOX) - , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) - , relativeExtents { rhs.relativeExtents } - , scale { rhs.scale } + SHBox::SHBox(SHCollisionShapeID id) noexcept + : SHConvexPolyhedron (id, Type::BOX) + , relativeExtents { SHVec3::One } + , scale { SHVec3::One } { - halfEdgeStructure = rhs.halfEdgeStructure; - - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + Extents = SHVec3::One * 0.5f; } - SHBoxCollisionShape::SHBoxCollisionShape(SHBoxCollisionShape&& rhs) noexcept - : SHConvexPolyhedronCollisionShape (rhs.id, SHCollisionShape::Type::BOX) - , SHBox (rhs.Center, rhs.Extents, rhs.Orientation) - , relativeExtents { rhs.relativeExtents } - , scale { rhs.scale } + SHBox::SHBox(const SHBox& rhs) noexcept + : SHConvexPolyhedron ( rhs ) + , relativeExtents { rhs.relativeExtents } + , scale { rhs.scale } { - halfEdgeStructure = rhs.halfEdgeStructure; + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; + } - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + SHBox::SHBox(SHBox&& rhs) noexcept + : SHConvexPolyhedron ( rhs ) + , relativeExtents { rhs.relativeExtents } + , scale { rhs.scale } + { + Center = rhs.Center; + Extents = rhs.Extents; + Orientation = rhs.Orientation; } /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHBoxCollisionShape& SHBoxCollisionShape::operator=(const SHBoxCollisionShape& rhs) noexcept + SHBox& SHBox::operator=(const SHBox& rhs) noexcept { if (this == &rhs) return *this; // Collision Shape Properties - id = rhs.id; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + SHConvexPolyhedron::operator=(rhs); // Box Properties @@ -95,23 +75,15 @@ namespace SHADE relativeExtents = rhs.relativeExtents; scale = rhs.scale; - halfEdgeStructure = rhs.halfEdgeStructure; return *this; } - SHBoxCollisionShape& SHBoxCollisionShape::operator=(SHBoxCollisionShape&& rhs) noexcept + SHBox& SHBox::operator=(SHBox&& rhs) noexcept { // Collision Shape Properties - id = rhs.id; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + SHConvexPolyhedron::operator=(rhs); // Box Properties @@ -123,7 +95,6 @@ namespace SHADE relativeExtents = rhs.relativeExtents; scale = rhs.scale; - halfEdgeStructure = rhs.halfEdgeStructure; return *this; } @@ -132,32 +103,28 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHVec3 SHBoxCollisionShape::GetCenter() const noexcept - { - return Center; - } - - SHVec3 SHBoxCollisionShape::GetWorldExtents() const noexcept + SHVec3 SHBox::GetWorldExtents() const noexcept { return Extents; } - SHVec3 SHBoxCollisionShape::GetRelativeExtents() const noexcept + SHVec3 SHBox::GetRelativeExtents() const noexcept { return relativeExtents; } - SHVec3 SHBoxCollisionShape::GetVertex(int index) const + SHVec3 SHBox::GetVertex(int index) const { - static constexpr int NUM_VERTICES = 8; - if (index < 0 || index >= NUM_VERTICES) throw std::invalid_argument("Index out-of-range!"); - return GetVertices()[index]; + SHVec3 vertices[NUM_VERTICES]; + GetCorners(vertices); + + return vertices[index]; } - SHVec3 SHBoxCollisionShape::GetNormal(int faceIndex) const + SHVec3 SHBox::GetNormal(int faceIndex) const { // Get local normal const SHVec3& LOCAL_NORMAL = halfEdgeStructure->GetFace(faceIndex).normal; @@ -166,88 +133,47 @@ namespace SHADE return SHVec3::Rotate(LOCAL_NORMAL, Orientation); } - SHVec3 SHBoxCollisionShape::GetPosition() const noexcept + SHVec3 SHBox::GetWorldCentroid() const noexcept { return Center; } - SHQuaternion SHBoxCollisionShape::GetOrientation() const noexcept + SHVec3 SHBox::GetRelativeCentroid() const noexcept { - return Orientation; + if (collider) + return SHVec3{ Center } - collider->GetPosition(); + + return Center; } - float SHBoxCollisionShape::GetVolume() const noexcept - { - return Volume(); - } - - SHVec3 SHBoxCollisionShape::GetLocalCentroid() const noexcept + SHVec3 SHBox::GetLocalCentroid() const noexcept { return SHVec3::Zero; } - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHBoxCollisionShape::SetCenter(const SHVec3& newCenter) noexcept + SHQuaternion SHBox::GetWorldOrientation() const noexcept { - Center = newCenter; + return Orientation; } - void SHBoxCollisionShape::SetWorldExtents(const SHVec3& newWorldExtents) noexcept + SHQuaternion SHBox::GetRelativeOrientation() const noexcept { - Extents = newWorldExtents; - - // Recompute Relative radius - relativeExtents = 2.0f * Extents / scale; + return transform.orientation; } - void SHBoxCollisionShape::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept + float SHBox::GetVolume() const noexcept { - relativeExtents = newRelativeExtents; - - // Recompute world radius - Extents = relativeExtents * scale * 0.5f; + return 8.0f * (Extents.x * Extents.y * Extents.z); } - void SHBoxCollisionShape::SetScale(const SHVec3& newScale) noexcept + float SHBox::GetSurfaceArea() const noexcept { - scale = SHVec3::Abs(newScale); - - // Recompute world radius - Extents = relativeExtents * scale * 0.5f; + return 8.0f * (Extents.x * Extents.y + + Extents.x * Extents.z + + Extents.y * Extents.z); } - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHBoxCollisionShape::ComputeTransforms() noexcept - { - const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); - - SetScale(PARENT_TRANSFORM.scale); - - // Recompute center - const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); - - Orientation = FINAL_ROT; - Center = SHVec3::Transform(transform.position, TRS); - } - - bool SHBoxCollisionShape::TestPoint(const SHVec3& point) const noexcept - { - return SHBox::TestPoint(point); - } - - SHRaycastResult SHBoxCollisionShape::Raycast(const SHRay& ray) const noexcept - { - return SHBox::Raycast(ray); - } - - SHMatrix SHBoxCollisionShape::GetInertiaTensor(float mass) const noexcept + SHMatrix SHBox::GetInertiaTensor(float mass) const noexcept { static constexpr float ONE_OVER_TWELVE = (1.0f / 12.0f); @@ -270,30 +196,93 @@ namespace SHADE return result; } - SHMatrix SHBoxCollisionShape::ComputeWorldTransform() const noexcept - { - const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); - const SHQuaternion ROTATION = PARENT_TRANSFORM.orientation * transform.orientation; - const SHVec3 SCALE = SHVec3{ Extents } *2.0f; + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ - return SHMatrix::Transform - ( - Center - , ROTATION - , SCALE - ); + void SHBox::SetWorldExtents(const SHVec3& newWorldExtents) noexcept + { + Extents = newWorldExtents; + + // Recompute Relative radius + relativeExtents = 2.0f * Extents / scale; } - SHAABB SHBoxCollisionShape::ComputeAABB() const noexcept + void SHBox::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept + { + relativeExtents = newRelativeExtents; + + // Recompute world radius + Extents = relativeExtents * scale * 0.5f; + } + + void SHBox::SetScale(const SHVec3& newScale) noexcept + { + scale = SHVec3::Abs(newScale); + + // Recompute world radius + Extents = relativeExtents * scale * 0.5f; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHBox::Update() noexcept + { + const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); + + SetScale(PARENT_TRANSFORM.scale); + + // Recompute center + const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; + const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); + + Orientation = FINAL_ROT; + Center = SHVec3::Transform(transform.position, TRS); + } + + bool SHBox::TestPoint(const SHVec3& point) const noexcept + { + return Contains(point); + } + + SHRaycastResult SHBox::Raycast(const SHRay& ray) const noexcept + { + SHRaycastResult result; + + result.hit = Intersects(ray.position, ray.direction, result.distance); + if (result.hit) + { + result.position = ray.position + ray.direction * result.distance; + result.angle = SHVec3::Angle(ray.position, result.position); + + // TODO: Compute Normal: Test which face the position belongs in. The normal is that face's normal. + } + + return result; + } + + SHMatrix SHBox::GetTRS() const noexcept + { + const SHQuaternion ROTATION = collider ? collider->GetTransform().orientation * transform.orientation : Orientation; + const SHVec3 SCALE = SHVec3{ Extents } *2.0f; + + return SHMatrix::Transform(Center, ROTATION, SCALE); + } + + SHAABB SHBox::ComputeAABB() const noexcept { SHVec3 min{ std::numeric_limits::max() }; SHVec3 max{ std::numeric_limits::lowest() }; - const auto& VERTICES = GetVertices(); - for (auto& vtx : VERTICES) + SHVec3 vertices[NUM_VERTICES]; + GetCorners(vertices); + + for (auto& vertex : vertices) { - min = SHVec3::Min({ vtx, min }); - max = SHVec3::Max({ vtx, max }); + min = SHVec3::Min({ vertex, min }); + max = SHVec3::Max({ vertex, max }); } const SHVec3 HALF_EXTENTS = (max - min) * 0.5f; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.h similarity index 53% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.h index a5be0d70..bf250726 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBoxCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.h @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHBoxCollisionShape.h + * \file SHBox.h * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Interface for a Box Collision Shape. * @@ -10,9 +10,10 @@ #pragma once +#include + // Project Headers -#include "Math/Geometry/SHBox.h" -#include "SHConvexPolyhedronCollisionShape.h" +#include "SHConvexPolyhedron.h" namespace SHADE { @@ -38,57 +39,67 @@ namespace SHADE * @brief * Encapsulate a Box Shape used for Physics Simulations. */ - class SH_API SHBoxCollisionShape final : public SHConvexPolyhedronCollisionShape - , private SHBox + class SH_API SHBox final : public SHConvexPolyhedron + , private DirectX::BoundingOrientedBox { private: /*---------------------------------------------------------------------------------*/ /* Friends */ /*---------------------------------------------------------------------------------*/ - friend class SHCollider; + friend class SHCompositeCollider; friend class SHCollision; friend class SHCollisionShapeLibrary; public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr int NUM_VERTICES = 8; + /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHBoxCollisionShape (SHCollisionShapeID id) noexcept; - SHBoxCollisionShape (const SHBoxCollisionShape& rhs) noexcept; - SHBoxCollisionShape (SHBoxCollisionShape&& rhs) noexcept; + SHBox (SHCollisionShapeID id) noexcept; + SHBox (const SHBox& rhs) noexcept; + SHBox (SHBox&& rhs) noexcept; - ~SHBoxCollisionShape () override = default; + ~SHBox () override = default; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHBoxCollisionShape& operator= (const SHBoxCollisionShape& rhs) noexcept; - SHBoxCollisionShape& operator= (SHBoxCollisionShape&& rhs) noexcept; + SHBox& operator= (const SHBox& rhs) noexcept; + SHBox& operator= (SHBox&& rhs) noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; - [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; + [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; + [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; - [[nodiscard]] SHVec3 GetVertex (int index) const override; - [[nodiscard]] SHVec3 GetNormal (int faceIndex) const override; + // Overriden Methods - [[nodiscard]] SHVec3 GetPosition () const noexcept override; - [[nodiscard]] SHQuaternion GetOrientation () const noexcept override; - [[nodiscard]] float GetVolume () const noexcept override; - [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override; + [[nodiscard]] SHVec3 GetVertex (int index) const override; + [[nodiscard]] SHVec3 GetNormal (int faceIndex) const override; + + [[nodiscard]] SHVec3 GetWorldCentroid () const noexcept override; + [[nodiscard]] SHVec3 GetRelativeCentroid () const noexcept override; + [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override; + [[nodiscard]] SHQuaternion GetWorldOrientation () const noexcept override; + [[nodiscard]] SHQuaternion GetRelativeOrientation () const noexcept override; + [[nodiscard]] float GetVolume () const noexcept override; + [[nodiscard]] float GetSurfaceArea () const noexcept override; + [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetCenter (const SHVec3& newCenter) noexcept; void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; void SetScale (const SHVec3& newScale) noexcept; @@ -97,66 +108,19 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Recomputes the transform of this box. - */ - void ComputeTransforms () noexcept override; - - /** - * @brief - * Tests if a point is inside the box. - * @param point - * The point to test. - * @return - * True if the point is inside the box. - */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - - /** - * @brief - * Casts a ray against this box. - * @param ray - * The ray to cast. - * @return - * An object holding the results of the raycast.
- * See the corresponding header for the contents of the object. - */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; - - /** - * @brief - * Computes the inertia tensor of the box. - * @param mass - * The mass of the sphere. - * @return - * The inertia tensor of the box. - */ - [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; - - /** - * @brief - * Computes the transformation matrix of the box. - * @return - * The transformation matrix of the box. - */ - [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; - - /** - * @brief - * Computes the a tight-fitting AABB that contains this box. - * @return - * A tight-fitting AABB that contains this box. - */ - [[nodiscard]] SHAABB ComputeAABB () const noexcept override; + void Update () noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHMatrix GetTRS () const noexcept override; + [[nodiscard]] SHAABB ComputeAABB () const noexcept override; private: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHVec3 relativeExtents; - SHVec3 scale; // Intended to be passed in by the base collider. + SHVec3 relativeExtents; + SHVec3 scale; }; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp index b412a227..f089c02c 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp @@ -14,7 +14,7 @@ #include "SHCollisionShape.h" // Project Headers -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Reflection/SHReflectionMetadata.h" #include "Tools/Utilities/SHUtilities.h" @@ -112,25 +112,15 @@ namespace SHADE return *collisionTag; } - SHVec3 SHCollisionShape::GetPosition() const noexcept - { - const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); - - const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); - - return SHVec3::Transform(transform.position, TRS); - } - - SHQuaternion SHCollisionShape::GetOrientation() const noexcept - { - return collider->GetOrientation() * transform.orientation; - } - /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ + void SHCollisionShape::SetCollisionTag(SHCollisionTag* newCollisionTag) noexcept + { + collisionTag = newCollisionTag; + } + void SHCollisionShape::SetFriction(float friction) noexcept { material.SetFriction(friction); @@ -155,7 +145,7 @@ namespace SHADE { transform.position = posOffset; - ComputeTransforms(); + Update(); } void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept @@ -163,7 +153,7 @@ namespace SHADE rotationOffset = rotOffset; transform.orientation = SHQuaternion::FromEuler(rotationOffset); - ComputeTransforms(); + Update(); } void SHCollisionShape::SetIsTrigger(bool isTrigger) noexcept @@ -174,11 +164,6 @@ namespace SHADE isTrigger ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; } - void SHCollisionShape::SetCollisionTag(SHCollisionTag* newCollisionTag) noexcept - { - collisionTag = newCollisionTag; - } - } // namespace SHADE RTTR_REGISTRATION diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h index 42e9fb0e..b4ce9529 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h @@ -41,7 +41,7 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ - friend class SHCollider; + friend class SHCompositeCollider; friend class SHColliderComponent; friend class SHCollisionShapeLibrary; friend class SHCollisionSpace; @@ -57,6 +57,7 @@ namespace SHADE SPHERE , BOX , CAPSULE + , CONVEX_HULL , COUNT , INVALID = -1 @@ -83,44 +84,47 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] EntityID GetEntityID () const noexcept; - [[nodiscard]] uint32_t GetIndex () const noexcept; + [[nodiscard]] EntityID GetEntityID () const noexcept; + [[nodiscard]] uint32_t GetIndex () const noexcept; // Material Properties // TODO: Remove individual setters once instanced materials are supported - [[nodiscard]] float GetFriction () const noexcept; - [[nodiscard]] float GetBounciness () const noexcept; - [[nodiscard]] float GetDensity () const noexcept; - [[nodiscard]] const SHPhysicsMaterial& GetMaterial () const noexcept; - - + [[nodiscard]] float GetFriction () const noexcept; + [[nodiscard]] float GetBounciness () const noexcept; + [[nodiscard]] float GetDensity () const noexcept; + [[nodiscard]] const SHPhysicsMaterial& GetMaterial () const noexcept; // Offsets - [[nodiscard]] const SHVec3& GetPositionOffset () const noexcept; - [[nodiscard]] const SHVec3& GetRotationOffset () const noexcept; + [[nodiscard]] const SHVec3& GetPositionOffset () const noexcept; + [[nodiscard]] const SHVec3& GetRotationOffset () const noexcept; // Flags - [[nodiscard]] Type GetType () const noexcept; - [[nodiscard]] bool IsTrigger () const noexcept; - [[nodiscard]] bool IsColliding () const noexcept; + [[nodiscard]] Type GetType () const noexcept; + [[nodiscard]] bool IsTrigger () const noexcept; + [[nodiscard]] bool IsColliding () const noexcept; - [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; + [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; // Virtual methods - [[nodiscard]] virtual SHVec3 GetPosition () const noexcept; - [[nodiscard]] virtual SHQuaternion GetOrientation () const noexcept; - [[nodiscard]] virtual float GetVolume () const noexcept = 0; - [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; - [[nodiscard]] virtual SHVec3 GetLocalCentroid () const noexcept = 0; + [[nodiscard]] virtual SHVec3 GetWorldCentroid () const noexcept = 0; + [[nodiscard]] virtual SHVec3 GetRelativeCentroid () const noexcept = 0; + [[nodiscard]] virtual SHVec3 GetLocalCentroid () const noexcept = 0; + [[nodiscard]] virtual SHQuaternion GetWorldOrientation () const noexcept = 0; + [[nodiscard]] virtual SHQuaternion GetRelativeOrientation () const noexcept = 0; + [[nodiscard]] virtual float GetVolume () const noexcept = 0; + [[nodiscard]] virtual float GetSurfaceArea () const noexcept = 0; + [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ + void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; + void SetFriction (float friction) noexcept; void SetBounciness (float bounciness) noexcept; void SetDensity (float density) noexcept; @@ -130,18 +134,55 @@ namespace SHADE void SetRotationOffset (const SHVec3& rotOffset) noexcept; // Flags - - void SetIsTrigger (bool isTrigger) noexcept; - void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; + // Forces rigidbody to recompute mass if one exists + void SetIsTrigger (bool isTrigger) noexcept; /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ - virtual void ComputeTransforms () noexcept = 0; - [[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0; - [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; + /** + * @brief + * Computes the transform of the shape. + */ + virtual void Update () noexcept = 0; + + /** + * @brief + * Tests if a point is inside this shape. + * @param point + * The point to test against the shape. + * @return + * True if the point is inside the shape. False otherwise. + */ + [[nodiscard]] virtual bool TestPoint (const SHVec3& point) const noexcept = 0; + + /** + * @brief + * Casts a ray at this shape. + * @param ray + * The ray to cast at the shape. + * @return + * The result of the ray cast. See the corresponding struct for it's contents. + */ + [[nodiscard]] virtual SHRaycastResult Raycast (const SHRay& ray) const noexcept = 0; + + /** + * @brief + * Computes the TRS matrix for rendering the shape. + * @return + * The model-to-world matrix for rendering the shape. + */ + [[nodiscard]] virtual SHMatrix GetTRS () const noexcept = 0; + + /** + * @brief + * Computes a tight-fitting AABB around this shape. + * @return + * An AABB. + */ + [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ @@ -150,16 +191,17 @@ namespace SHADE SHCollisionShapeID id; - SHPhysicsMaterial material; + SHCompositeCollider* collider; // The collider it belongs to. + SHCollisionTag* collisionTag; + SHPhysicsMaterial material; // TODO: Change to pointer once instancing is supported - SHCollider* collider; // The collider it belongs to. - SHTransform transform; + SHTransform transform; // Stores the local position and rotation. // Needed for conversion to euler angles SHVec3 rotationOffset; - uint8_t flags; // 0 0 0 isColliding trigger capsule sphere box - SHCollisionTag* collisionTag; + uint8_t flags; // 0 0 0 trigger convexHull capsule sphere box + RTTR_ENABLE() }; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp index 46adb47b..e0202d67 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp @@ -39,12 +39,12 @@ namespace SHADE /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHSphereCollisionShape* SHCollisionShapeLibrary::CreateSphere(SHCollisionShapeID id, const SHSphereCreateInfo& createInfo) + SHSphere* SHCollisionShapeLibrary::CreateSphere(SHCollisionShapeID id, const SHSphereCreateInfo& createInfo) { - const auto RESULT = spheres.emplace(id, new SHSphereCollisionShape{ id }); + const auto RESULT = spheres.emplace(id, new SHSphere{ id }); if (RESULT.second) { - SHSphereCollisionShape* sphere = RESULT.first->second; + SHSphere* sphere = RESULT.first->second; sphere->Center = createInfo.Center; sphere->Radius = createInfo.Radius; @@ -57,12 +57,12 @@ namespace SHADE return spheres.find(id)->second; } - SHBoxCollisionShape* SHCollisionShapeLibrary::CreateBox(SHCollisionShapeID id, const SHBoxCreateInfo& createInfo) + SHBox* SHCollisionShapeLibrary::CreateBox(SHCollisionShapeID id, const SHBoxCreateInfo& createInfo) { - const auto RESULT = boxes.emplace(id, new SHBoxCollisionShape{ id }); + const auto RESULT = boxes.emplace(id, new SHBox{ id }); if (RESULT.second) { - SHBoxCollisionShape* box = RESULT.first->second; + SHBox* box = RESULT.first->second; box->Center = createInfo.Center; box->Extents = createInfo.Extents; @@ -90,7 +90,7 @@ namespace SHADE } case SHCollisionShape::Type::SPHERE: { - SHSphereCollisionShape* sphere = spheres.find(shape->id)->second; + SHSphere* sphere = spheres.find(shape->id)->second; spheres.erase(shape->id); delete sphere; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h index dbfc4fc7..462b8dd8 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h @@ -13,8 +13,8 @@ #include // Project Header -#include "SHSphereCollisionShape.h" -#include "SHBoxCollisionShape.h" +#include "SHSphere.h" +#include "SHBox.h" namespace SHADE { @@ -51,7 +51,7 @@ namespace SHADE * @return * A new sphere collision shape. */ - SHSphereCollisionShape* CreateSphere (SHCollisionShapeID id, const SHSphereCreateInfo& createInfo); + SHSphere* CreateSphere (SHCollisionShapeID id, const SHSphereCreateInfo& createInfo); /** * @brief @@ -63,7 +63,7 @@ namespace SHADE * @return * A new box collision shape. */ - SHBoxCollisionShape* CreateBox (SHCollisionShapeID id, const SHBoxCreateInfo& createInfo); + SHBox* CreateBox (SHCollisionShapeID id, const SHBoxCreateInfo& createInfo); /** * @brief @@ -85,8 +85,8 @@ namespace SHADE // We use unordered maps for fast lookup when deleting. // Since we are not instancing shapes (yet?), I'd rather not iterate through an entire vector to find the shape. - using Spheres = std::unordered_map; - using Boxes = std::unordered_map; + using Spheres = std::unordered_map; + using Boxes = std::unordered_map; /*---------------------------------------------------------------------------------*/ /* Data Members */ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp similarity index 76% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp index 51d4e684..a2aef745 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHConvexPolyhedronCollisionShape.h" +#include "SHConvexPolyhedron.h" namespace SHADE { @@ -19,12 +19,12 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHConvexPolyhedronCollisionShape::SHConvexPolyhedronCollisionShape(SHCollisionShapeID id,Type polyhedronType) noexcept + SHConvexPolyhedron::SHConvexPolyhedron(SHCollisionShapeID id,Type polyhedronType) noexcept : SHCollisionShape (id, polyhedronType) , halfEdgeStructure { nullptr } {} - SHConvexPolyhedronCollisionShape::SHConvexPolyhedronCollisionShape(const SHConvexPolyhedronCollisionShape& rhs) noexcept + SHConvexPolyhedron::SHConvexPolyhedron(const SHConvexPolyhedron& rhs) noexcept : SHCollisionShape (rhs.id, rhs.GetType()) , halfEdgeStructure { nullptr } { @@ -37,7 +37,7 @@ namespace SHADE collisionTag = rhs.collisionTag; } - SHConvexPolyhedronCollisionShape::SHConvexPolyhedronCollisionShape(SHConvexPolyhedronCollisionShape&& rhs) noexcept + SHConvexPolyhedron::SHConvexPolyhedron(SHConvexPolyhedron&& rhs) noexcept : SHCollisionShape (rhs.id, rhs.GetType()) , halfEdgeStructure { nullptr } { @@ -54,7 +54,7 @@ namespace SHADE /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHConvexPolyhedronCollisionShape& SHConvexPolyhedronCollisionShape::operator=(const SHConvexPolyhedronCollisionShape& rhs) noexcept + SHConvexPolyhedron& SHConvexPolyhedron::operator=(const SHConvexPolyhedron& rhs) noexcept { if (this == &rhs) return *this; @@ -73,7 +73,7 @@ namespace SHADE return *this; } - SHConvexPolyhedronCollisionShape& SHConvexPolyhedronCollisionShape::operator=(SHConvexPolyhedronCollisionShape&& rhs) noexcept + SHConvexPolyhedron& SHConvexPolyhedron::operator=(SHConvexPolyhedron&& rhs) noexcept { material = rhs.material; collider = rhs.collider; @@ -93,28 +93,28 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - const SHHalfEdgeStructure* SHConvexPolyhedronCollisionShape::GetHalfEdgeStructure() const noexcept + const SHHalfEdgeStructure* SHConvexPolyhedron::GetHalfEdgeStructure() const noexcept { return halfEdgeStructure; } - int32_t SHConvexPolyhedronCollisionShape::GetFaceCount() const noexcept + int32_t SHConvexPolyhedron::GetFaceCount() const noexcept { return halfEdgeStructure->GetFaceCount(); } - int32_t SHConvexPolyhedronCollisionShape::GetHalfEdgeCount() const noexcept + int32_t SHConvexPolyhedron::GetHalfEdgeCount() const noexcept { return halfEdgeStructure->GetHalfEdgeCount(); } - const SHHalfEdgeStructure::Face& SHConvexPolyhedronCollisionShape::GetFace(int index) const + const SHHalfEdgeStructure::Face& SHConvexPolyhedron::GetFace(int index) const { // Assume it has already been initialised return halfEdgeStructure->GetFace(index); } - const SHHalfEdgeStructure::HalfEdge& SHConvexPolyhedronCollisionShape::GetHalfEdge(int index) const + const SHHalfEdgeStructure::HalfEdge& SHConvexPolyhedron::GetHalfEdge(int index) const { // Assume it has already been initialised return halfEdgeStructure->GetHalfEdge(index); diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h similarity index 83% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h index 6095cbca..cdc7f45f 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedronCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h @@ -24,14 +24,14 @@ namespace SHADE * @brief * Encapsulates a convex polyhedron shape used for Physics Simulations.. */ - class SH_API SHConvexPolyhedronCollisionShape : public SHCollisionShape + class SH_API SHConvexPolyhedron : public SHCollisionShape { private: /*---------------------------------------------------------------------------------*/ /* Friends */ /*---------------------------------------------------------------------------------*/ - friend class SHCollider; + friend class SHCompositeCollider; friend class SHCollision; friend class SHCollisionShapeLibrary; @@ -40,18 +40,18 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHConvexPolyhedronCollisionShape (SHCollisionShapeID id, Type polyhedronType) noexcept; - SHConvexPolyhedronCollisionShape (const SHConvexPolyhedronCollisionShape& rhs) noexcept; - SHConvexPolyhedronCollisionShape (SHConvexPolyhedronCollisionShape&& rhs) noexcept; + SHConvexPolyhedron (SHCollisionShapeID id, Type polyhedronType) noexcept; + SHConvexPolyhedron (const SHConvexPolyhedron& rhs) noexcept; + SHConvexPolyhedron (SHConvexPolyhedron&& rhs) noexcept; - ~SHConvexPolyhedronCollisionShape () override = default; + ~SHConvexPolyhedron () override = default; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHConvexPolyhedronCollisionShape& operator=(const SHConvexPolyhedronCollisionShape& rhs) noexcept; - SHConvexPolyhedronCollisionShape& operator=(SHConvexPolyhedronCollisionShape&& rhs) noexcept; + SHConvexPolyhedron& operator=(const SHConvexPolyhedron& rhs) noexcept; + SHConvexPolyhedron& operator=(SHConvexPolyhedron&& rhs) noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.cpp similarity index 51% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.cpp index a5c82017..04f8718e 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.cpp @@ -11,12 +11,12 @@ #include // Primary Header -#include "SHSphereCollisionShape.h" +#include "SHSphere.h" // Project Headers #include "Math/SHMathHelpers.h" #include "Math/SHMatrix.h" -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" namespace SHADE { @@ -24,64 +24,42 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHSphereCollisionShape::SHSphereCollisionShape(SHCollisionShapeID id) noexcept - : SHCollisionShape (id, SHCollisionShape::Type::SPHERE) - , SHSphere () + SHSphere::SHSphere(SHCollisionShapeID id) noexcept + : SHCollisionShape (id, Type::SPHERE) , relativeRadius { 1.0f } , scale { 1.0f } - {} - - SHSphereCollisionShape::SHSphereCollisionShape(const SHSphereCollisionShape& rhs) noexcept - : SHCollisionShape (rhs.id, SHCollisionShape::Type::SPHERE) - , SHSphere (rhs.Center, rhs.Radius) - , relativeRadius { rhs.relativeRadius } - , scale { rhs.scale } { - - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + Radius = 0.5f; } - SHSphereCollisionShape::SHSphereCollisionShape(SHSphereCollisionShape&& rhs) noexcept - : SHCollisionShape (rhs.id, SHCollisionShape::Type::SPHERE) - , SHSphere (rhs.Center, rhs.Radius) + SHSphere::SHSphere(const SHSphere& rhs) noexcept + : SHCollisionShape ( rhs ) , relativeRadius { rhs.relativeRadius } , scale { rhs.scale } { + Center = rhs.Center; + Radius = rhs.Radius; + } - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + SHSphere::SHSphere(SHSphere&& rhs) noexcept + : SHCollisionShape ( rhs ) + , relativeRadius { rhs.relativeRadius } + , scale { rhs.scale } + { + Center = rhs.Center; + Radius = rhs.Radius; } /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHSphereCollisionShape& SHSphereCollisionShape::operator=(const SHSphereCollisionShape& rhs) noexcept + SHSphere& SHSphere::operator=(const SHSphere& rhs) noexcept { if (this == &rhs) return *this; - // Collision Shape Properties - - id = rhs.id; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + SHCollisionShape::operator=(rhs); // Sphere Properties @@ -96,18 +74,9 @@ namespace SHADE return *this; } - SHSphereCollisionShape& SHSphereCollisionShape::operator=(SHSphereCollisionShape&& rhs) noexcept + SHSphere& SHSphere::operator=(SHSphere&& rhs) noexcept { - // Collision Shape Properties - - id = rhs.id; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + SHCollisionShape::operator=(rhs); // Sphere Properties @@ -126,51 +95,73 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHVec3 SHSphereCollisionShape::GetCenter() const noexcept - { - return Center; - } - - float SHSphereCollisionShape::GetWorldRadius() const noexcept + float SHSphere::GetWorldRadius() const noexcept { return Radius; } - float SHSphereCollisionShape::GetRelativeRadius() const noexcept + float SHSphere::GetRelativeRadius() const noexcept { return relativeRadius; } - SHVec3 SHSphereCollisionShape::GetPosition() const noexcept + SHVec3 SHSphere::GetWorldCentroid() const noexcept { return Center; } - SHQuaternion SHSphereCollisionShape::GetOrientation() const noexcept + SHVec3 SHSphere::GetRelativeCentroid() const noexcept { - return collider->GetTransform().orientation * transform.orientation; + if (collider) + return SHVec3{ Center } - collider->GetPosition(); + + return Center; } - float SHSphereCollisionShape::GetVolume() const noexcept - { - return Volume(); - } - - SHVec3 SHSphereCollisionShape::GetLocalCentroid() const noexcept + SHVec3 SHSphere::GetLocalCentroid() const noexcept { return SHVec3::Zero; } + SHQuaternion SHSphere::GetWorldOrientation() const noexcept + { + if (collider) + return collider->GetOrientation() * transform.orientation; + + return transform.orientation; + } + + SHQuaternion SHSphere::GetRelativeOrientation() const noexcept + { + return transform.orientation; + } + + float SHSphere::GetVolume() const noexcept + { + return (4.0f / 3.0f) * SHMath::PI * (Radius * Radius * Radius); + } + + float SHSphere::GetSurfaceArea() const noexcept + { + return 4.0f * SHMath::PI * (Radius * Radius); + } + + SHMatrix SHSphere::GetInertiaTensor(float mass) const noexcept + { + static constexpr float TWO_OVER_FIVE = 2.0f / 5.0f; + + const float DIAGONAL = TWO_OVER_FIVE * mass * (Radius * Radius); + + SHMatrix result; + result.m[0][0] = result.m[1][1] = result.m[2][2] = DIAGONAL; + return result; + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHSphereCollisionShape::SetCenter(const SHVec3& newCenter) noexcept - { - Center = newCenter; - } - - void SHSphereCollisionShape::SetWorldRadius(float newWorldRadius) noexcept + void SHSphere::SetWorldRadius(float newWorldRadius) noexcept { Radius = newWorldRadius; @@ -178,7 +169,7 @@ namespace SHADE relativeRadius = 2.0f * Radius / scale; } - void SHSphereCollisionShape::SetRelativeRadius(float newRelativeRadius) noexcept + void SHSphere::SetRelativeRadius(float newRelativeRadius) noexcept { relativeRadius = newRelativeRadius; @@ -186,7 +177,7 @@ namespace SHADE Radius = relativeRadius * scale * 0.5f; } - void SHSphereCollisionShape::SetScale(float maxScale) noexcept + void SHSphere::SetScale(float maxScale) noexcept { scale = std::fabs(maxScale); @@ -198,7 +189,7 @@ namespace SHADE /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHSphereCollisionShape::ComputeTransforms() noexcept + void SHSphere::Update() noexcept { const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); @@ -212,42 +203,35 @@ namespace SHADE Center = SHVec3::Transform(transform.position, TRS); } - bool SHSphereCollisionShape::TestPoint(const SHVec3& point) const noexcept + bool SHSphere::TestPoint(const SHVec3& point) const noexcept { - return SHSphere::TestPoint(point); + return Contains(point); } - SHRaycastResult SHSphereCollisionShape::Raycast(const SHRay& ray) const noexcept + SHRaycastResult SHSphere::Raycast(const SHRay& ray) const noexcept { - return SHSphere::Raycast(ray); - } + SHRaycastResult result; - SHMatrix SHSphereCollisionShape::GetInertiaTensor(float mass) const noexcept - { - static constexpr float TWO_OVER_FIVE = 2.0f / 5.0f; + result.hit = Intersects(ray.position, ray.direction, result.distance); + if (result.hit) + { + result.position = ray.position + ray.direction * result.distance; + result.angle = SHVec3::Angle(ray.position, result.position); + result.normal = SHVec3::Normalise(result.position - Center); + } - const float DIAGONAL = TWO_OVER_FIVE * mass * (Radius * Radius); - - SHMatrix result; - result.m[0][0] = result.m[1][1] = result.m[2][2] = DIAGONAL; return result; } - SHMatrix SHSphereCollisionShape::ComputeWorldTransform() const noexcept + SHMatrix SHSphere::GetTRS() const noexcept { - const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); - const SHQuaternion ROTATION = PARENT_TRANSFORM.orientation * transform.orientation; - const SHVec3 SCALE{ Radius }; + const SHQuaternion ROTATION = collider ? collider->GetTransform().orientation * transform.orientation : transform.orientation; + const SHVec3 SCALE { Radius }; - return SHMatrix::Transform - ( - Center - , ROTATION - , SCALE - ); + return SHMatrix::Transform(Center, ROTATION, SCALE); } - SHAABB SHSphereCollisionShape::ComputeAABB() const noexcept + SHAABB SHSphere::ComputeAABB() const noexcept { return SHAABB{ Center, SHVec3{ Radius } }; } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.h similarity index 55% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h rename to SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.h index 7dbdf13d..c4e97fe0 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphereCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.h @@ -10,8 +10,9 @@ #pragma once +#include + // Project Headers -#include "Math/Geometry/SHSphere.h" #include "SHCollisionShape.h" namespace SHADE @@ -37,15 +38,15 @@ namespace SHADE * @brief * Encapsulate a Sphere Shape used for Physics Simulations. */ - class SH_API SHSphereCollisionShape final : public SHCollisionShape - , private SHSphere + class SH_API SHSphere final : public SHCollisionShape + , private DirectX::BoundingSphere { private: /*---------------------------------------------------------------------------------*/ /* Friends */ /*---------------------------------------------------------------------------------*/ - friend class SHCollider; + friend class SHCompositeCollider; friend class SHCollision; friend class SHCollisionShapeLibrary; @@ -54,37 +55,39 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHSphereCollisionShape (SHCollisionShapeID id) noexcept; - SHSphereCollisionShape (const SHSphereCollisionShape& rhs) noexcept; - SHSphereCollisionShape (SHSphereCollisionShape&& rhs) noexcept; + SHSphere (SHCollisionShapeID id) noexcept; + SHSphere (const SHSphere& rhs) noexcept; + SHSphere (SHSphere&& rhs) noexcept; - ~SHSphereCollisionShape () override = default; + ~SHSphere () override = default; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHSphereCollisionShape& operator= (const SHSphereCollisionShape& rhs) noexcept; - SHSphereCollisionShape& operator= (SHSphereCollisionShape&& rhs) noexcept; + SHSphere& operator= (const SHSphere& rhs) noexcept; + SHSphere& operator= (SHSphere&& rhs) noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] float GetWorldRadius () const noexcept; - [[nodiscard]] float GetRelativeRadius () const noexcept; + [[nodiscard]] float GetWorldRadius () const noexcept; + [[nodiscard]] float GetRelativeRadius () const noexcept; - [[nodiscard]] SHVec3 GetPosition () const noexcept override; - [[nodiscard]] SHQuaternion GetOrientation () const noexcept override; - [[nodiscard]] float GetVolume () const noexcept override; - [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override; + [[nodiscard]] SHVec3 GetWorldCentroid () const noexcept override; + [[nodiscard]] SHVec3 GetRelativeCentroid () const noexcept override; + [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override; + [[nodiscard]] SHQuaternion GetWorldOrientation () const noexcept override; + [[nodiscard]] SHQuaternion GetRelativeOrientation () const noexcept override; + [[nodiscard]] float GetVolume () const noexcept override; + [[nodiscard]] float GetSurfaceArea () const noexcept override; + [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetCenter (const SHVec3& newCenter) noexcept; void SetWorldRadius (float newWorldRadius) noexcept; void SetRelativeRadius (float newRelativeRadius) noexcept; void SetScale (float maxScale) noexcept; @@ -93,58 +96,11 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Recomputes the transform of this sphere. - */ - void ComputeTransforms () noexcept override; - - /** - * @brief - * Tests if a point is inside the sphere. - * @param point - * The point to test. - * @return - * True if the point is inside the sphere. - */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - - /** - * @brief - * Casts a ray against this sphere. - * @param ray - * The ray to cast. - * @return - * An object holding the results of the raycast.
- * See the corresponding header for the contents of the object. - */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; - - /** - * @brief - * Computes the inertia tensor of the sphere. - * @param mass - * The mass of the sphere. - * @return - * The inertia tensor of the sphere. - */ - [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; - - /** - * @brief - * Computes the transformation matrix of the sphere. - * @return - * The transformation matrix of the sphere. - */ - [[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override; - - /** - * @brief - * Computes the a tight-fitting AABB that contains this sphere. - * @return - * A tight-fitting AABB that contains this sphere. - */ - [[nodiscard]] SHAABB ComputeAABB () const noexcept override; + void Update () noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHMatrix GetTRS () const noexcept override; + [[nodiscard]] SHAABB ComputeAABB () const noexcept override; private: /*---------------------------------------------------------------------------------*/ @@ -152,7 +108,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ float relativeRadius; - float scale; // Intended to be passed in by the base collider. + float scale; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp index 4bb22697..8bd26e91 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp @@ -14,7 +14,7 @@ #include "SHCollisionKey.h" // Project Headers -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" #include "Physics/Interface/SHColliderComponent.h" diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp index 1b03feab..e7da5f8b 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp @@ -16,7 +16,7 @@ // Project Headers #include "Math/SHMathHelpers.h" -#include "Physics/Collision/CollisionShapes/SHBoxCollisionShape.h" +#include "Physics/Collision/CollisionShapes/SHBox.h" // TODO diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 0c65a94f..e98983b4 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -84,27 +84,9 @@ namespace SHADE // Sphere VS Convex - static FaceQuery findClosestFace - ( - const SHSphereCollisionShape& sphere - , const SHConvexPolyhedronCollisionShape& polyhedron - ) noexcept; - - static int32_t findClosestPoint - ( - const SHSphereCollisionShape& sphere - , const SHConvexPolyhedronCollisionShape& polyhedron - , int32_t faceIndex - ) noexcept; - - static int32_t findVoronoiRegion - ( - const SHSphereCollisionShape& sphere - , const SHVec3& faceVertex - , const SHVec3& faceNormal - , const SHVec3& tangent1 - , const SHVec3& tangent2 - ) noexcept; + static FaceQuery findClosestFace (const SHSphere& sphere, const SHConvexPolyhedron& polyhedron) noexcept; + static int32_t findClosestPoint (const SHSphere& sphere, const SHConvexPolyhedron& polyhedron, int32_t faceIndex) noexcept; + static int32_t findVoronoiRegion (const SHSphere& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; // Capsule VS Convex diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp index 3e308aed..d7eb02fc 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp @@ -15,7 +15,7 @@ // Project Headers #include "Math/SHMathHelpers.h" -#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h" +#include "Physics/Collision/CollisionShapes/SHSphere.h" // TODO diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 8245d672..1b97a7c6 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -18,7 +18,7 @@ #include "Math/Geometry/SHPlane.h" #include "Math/SHMathHelpers.h" #include "Physics/Collision/CollisionShapes/SHCollisionShape.h" -#include "Physics/Collision/CollisionShapes/SHBoxCollisionShape.h" +#include "Physics/Collision/CollisionShapes/SHBox.h" namespace SHADE { @@ -28,10 +28,10 @@ namespace SHADE bool SHCollision::SphereVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - const SHSphereCollisionShape& SPHERE = dynamic_cast(A); - const SHConvexPolyhedronCollisionShape& POLYHEDRON = dynamic_cast(B); + const SHSphere& SPHERE = dynamic_cast(A); + const SHConvexPolyhedron& POLYHEDRON = dynamic_cast(B); - const SHVec3 CENTER = SPHERE.GetCenter(); + const SHVec3 CENTER = SPHERE.Center; const float RADIUS = SPHERE.GetWorldRadius(); // Find closest face @@ -76,10 +76,10 @@ namespace SHADE { // Convert to underlying types // For the convex, we only need the convex polyhedron shape since the get vertex is pure virtual. - const SHSphereCollisionShape& SPHERE = dynamic_cast(A); - const SHConvexPolyhedronCollisionShape& POLYHEDRON = dynamic_cast(B); + const SHSphere& SPHERE = dynamic_cast(A); + const SHConvexPolyhedron& POLYHEDRON = dynamic_cast(B); - const SHVec3 CENTER = SPHERE.GetCenter(); + const SHVec3 CENTER = SPHERE.Center; const float RADIUS = SPHERE.GetWorldRadius(); const FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON); @@ -98,7 +98,7 @@ namespace SHADE manifold.normal = -POLYHEDRON.GetNormal(FACE_QUERY.closestFace); contact.penetration = PENETRATION; - contact.position = SPHERE.GetCenter(); + contact.position = CENTER; manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; @@ -187,13 +187,13 @@ namespace SHADE SHCollision::FaceQuery SHCollision::findClosestFace ( - const SHSphereCollisionShape& sphere - , const SHConvexPolyhedronCollisionShape& polyhedron + const SHSphere& sphere + , const SHConvexPolyhedron& polyhedron ) noexcept { FaceQuery faceQuery; - const SHVec3 CENTER = sphere.GetCenter(); + const SHVec3 CENTER = sphere.Center; const float RADIUS = sphere.GetWorldRadius(); /* @@ -233,15 +233,15 @@ namespace SHADE int32_t SHCollision::findClosestPoint ( - const SHSphereCollisionShape& sphere - , const SHConvexPolyhedronCollisionShape& polyhedron - , int32_t faceIndex + const SHSphere& sphere + , const SHConvexPolyhedron& polyhedron + , int32_t faceIndex ) noexcept { // Find closest point on face int32_t closestPointIndex = -1; - const SHVec3 CENTER = sphere.GetCenter(); + const SHVec3 CENTER = sphere.Center; const SHHalfEdgeStructure::Face& FACE = polyhedron.GetFace(faceIndex); const int32_t NUM_VERITICES = static_cast(FACE.vertexIndices.size()); @@ -264,11 +264,11 @@ namespace SHADE int32_t SHCollision::findVoronoiRegion ( - const SHSphereCollisionShape& sphere - , const SHVec3& faceVertex - , const SHVec3& faceNormal - , const SHVec3& tangent1 - , const SHVec3& tangent2 + const SHSphere& sphere + , const SHVec3& faceVertex + , const SHVec3& faceNormal + , const SHVec3& tangent1 + , const SHVec3& tangent2 ) noexcept { static constexpr int NUM_TANGENTS = 2; @@ -288,7 +288,7 @@ namespace SHADE * */ - const SHVec3& CENTER = sphere.GetCenter(); + const SHVec3 CENTER = sphere.Center; const float RADIUS = sphere.GetWorldRadius(); const SHVec3 TANGENTS [NUM_TANGENTS] { tangent1, tangent2 }; diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp index 07b6c9a6..aa270b16 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp @@ -15,7 +15,7 @@ // Project Headers #include "Math/SHMathHelpers.h" -#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h" +#include "Physics/Collision/CollisionShapes/SHSphere.h" namespace SHADE { @@ -25,22 +25,41 @@ namespace SHADE bool SHCollision::SphereVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - const SHSphereCollisionShape& SPHERE_A = dynamic_cast(A); - const SHSphereCollisionShape& SPHERE_B = dynamic_cast(B); + const SHSphere& SPHERE_A = dynamic_cast(A); + const SHSphere& SPHERE_B = dynamic_cast(B); - return SHSphere::Intersect(SPHERE_A, SPHERE_B); + const SHVec3 CENTER_A = SPHERE_A.Center; + const float RADIUS_A = SPHERE_A.Radius; + const SHVec3 CENTER_B = SPHERE_B.Center; + const float RADIUS_B = SPHERE_B.Radius; + + + const SHVec3 A_TO_B = CENTER_B - CENTER_A; + const float DISTANCE_BETWEEN_CENTERS_SQUARED = A_TO_B.LengthSquared(); + + const float COMBINED_RADIUS = RADIUS_B + RADIUS_A; + const float COMBINED_RADIUS_SQUARED = COMBINED_RADIUS * COMBINED_RADIUS; + + if (DISTANCE_BETWEEN_CENTERS_SQUARED > COMBINED_RADIUS_SQUARED) + return false; } bool SHCollision::SphereVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { // Convert to underlying types - const SHSphereCollisionShape& SPHERE_A = dynamic_cast(A); - const SHSphereCollisionShape& SPHERE_B = dynamic_cast(B); + const SHSphere& SPHERE_A = dynamic_cast(A); + const SHSphere& SPHERE_B = dynamic_cast(B); - const SHVec3 A_TO_B = SPHERE_B.GetCenter() - SPHERE_A.GetCenter(); + const SHVec3 CENTER_A = SPHERE_A.Center; + const float RADIUS_A = SPHERE_A.Radius; + const SHVec3 CENTER_B = SPHERE_B.Center; + const float RADIUS_B = SPHERE_B.Radius; + + + const SHVec3 A_TO_B = CENTER_B - CENTER_A; const float DISTANCE_BETWEEN_CENTERS_SQUARED = A_TO_B.LengthSquared(); - const float COMBINED_RADIUS = (SPHERE_A.GetWorldRadius() + SPHERE_B.GetWorldRadius()); + const float COMBINED_RADIUS = RADIUS_B + RADIUS_A; const float COMBINED_RADIUS_SQUARED = COMBINED_RADIUS * COMBINED_RADIUS; if (DISTANCE_BETWEEN_CENTERS_SQUARED > COMBINED_RADIUS_SQUARED) @@ -56,15 +75,15 @@ namespace SHADE if (SHMath::CompareFloat(DISTANCE_BETWEEN_CENTERS_SQUARED, 0.0f)) { manifold.normal = SHVec3::UnitY; - contact.position = SPHERE_A.GetCenter(); - contact.penetration = SPHERE_B.GetWorldRadius(); + contact.position = CENTER_A; + contact.penetration = RADIUS_B; manifold.contacts[numContacts++] = contact; } else { manifold.normal = SHVec3::Normalise(A_TO_B); - contact.position = SPHERE_B.GetCenter() - (manifold.normal * SPHERE_B.GetWorldRadius()); + contact.position = CENTER_B - manifold.normal * RADIUS_B; contact.penetration = COMBINED_RADIUS - A_TO_B.Length(); manifold.contacts[numContacts++] = contact; diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp index 88fdac40..e410ac36 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp @@ -40,7 +40,7 @@ namespace SHADE /* Public Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollisionSpace::AddCollider(SHCollider* collider) noexcept + void SHCollisionSpace::AddCollider(SHCompositeCollider* collider) noexcept { const bool INSERTED = colliders.emplace(collider->entityID, collider).second; if (!INSERTED) @@ -56,7 +56,7 @@ namespace SHADE broadphase.Insert(shape->id, shape->ComputeAABB()); } - void SHCollisionSpace::RemoveCollider(SHCollider* collider) noexcept + void SHCollisionSpace::RemoveCollider(SHCompositeCollider* collider) noexcept { colliders.erase(collider->entityID); @@ -209,12 +209,12 @@ namespace SHADE { case SHCollisionShape::Type::SPHERE: { - baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); + baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); break; } case SHCollisionShape::Type::BOX: { - baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); + baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); break; } case SHCollisionShape::Type::CAPSULE: @@ -246,7 +246,7 @@ namespace SHADE /* Private Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollisionSpace::broadphaseQuery(SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept + void SHCollisionSpace::broadphaseQuery(SHRigidBody::Type rigidBodyType, SHCompositeCollider* collider) noexcept { for (auto* shape : collider->shapes) { diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h index 8607cf45..fa1f0d16 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h @@ -18,7 +18,7 @@ // Project Headers #include "Broadphase/SHDynamicAABBTree.h" #include "Physics/Dynamics/SHContactManager.h" -#include "SHCollider.h" +#include "SHCompositeCollider.h" #include "SHPhysicsRaycastResult.h" #include "CollisionTags/SHCollisionTags.h" #include "CollisionTags/SHCollisionTags.h" @@ -118,7 +118,7 @@ namespace SHADE * @param collider * A collider to add. Duplicates will be ignored. */ - void AddCollider (SHCollider* collider) noexcept; + void AddCollider (SHCompositeCollider* collider) noexcept; /** * @brief @@ -127,7 +127,7 @@ namespace SHADE * @param collider * A collider to remove. If a reference to it doesn't exist, it will be ignored. */ - void RemoveCollider (SHCollider* collider) noexcept; + void RemoveCollider (SHCompositeCollider* collider) noexcept; /** * @brief @@ -165,7 +165,7 @@ namespace SHADE SHCollisionShape* B = nullptr; }; - using Colliders = std::unordered_map; + using Colliders = std::unordered_map; using NarrowphaseBatch = std::unordered_map; /*---------------------------------------------------------------------------------*/ @@ -185,7 +185,7 @@ namespace SHADE // Broadphase helpers - void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; + void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCompositeCollider* collider) noexcept; // Narrowphase helpers diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp similarity index 81% rename from SHADE_Engine/src/Physics/Collision/SHCollider.cpp rename to SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp index 021605c7..97fef331 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHCollider.h" +#include "SHCompositeCollider.h" // Project Headers #include "Broadphase/SHDynamicAABBTree.h" @@ -27,7 +27,7 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept + SHCompositeCollider::SHCompositeCollider(EntityID eid, const SHTransform& worldTransform) noexcept : entityID { eid } , active { true } , debugDraw { false } @@ -38,7 +38,7 @@ namespace SHADE , transform { worldTransform } {} - SHCollider::SHCollider(const SHCollider& rhs) noexcept + SHCompositeCollider::SHCompositeCollider(const SHCompositeCollider& rhs) noexcept : entityID { rhs.entityID } , active { rhs.active } , debugDraw { rhs.debugDraw } @@ -57,7 +57,7 @@ namespace SHADE copyShapes(rhs); } - SHCollider::SHCollider(SHCollider&& rhs) noexcept + SHCompositeCollider::SHCompositeCollider(SHCompositeCollider&& rhs) noexcept : entityID { rhs.entityID } , active { rhs.active } , debugDraw { rhs.debugDraw } @@ -76,7 +76,7 @@ namespace SHADE copyShapes(rhs); } - SHCollider::~SHCollider() noexcept + SHCompositeCollider::~SHCompositeCollider() noexcept { if (!shapeLibrary) { @@ -92,7 +92,7 @@ namespace SHADE /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollider& SHCollider::operator=(const SHCollider& rhs) noexcept + SHCompositeCollider& SHCompositeCollider::operator=(const SHCompositeCollider& rhs) noexcept { if (this == &rhs) return *this; @@ -117,7 +117,7 @@ namespace SHADE return *this; } - SHCollider& SHCollider::operator=(SHCollider&& rhs) noexcept + SHCompositeCollider& SHCompositeCollider::operator=(SHCompositeCollider&& rhs) noexcept { if (!shapeLibrary) { @@ -143,47 +143,47 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - EntityID SHCollider::GetEntityID() const noexcept + EntityID SHCompositeCollider::GetEntityID() const noexcept { return entityID; } - bool SHCollider::IsActive() const noexcept + bool SHCompositeCollider::IsActive() const noexcept { return active; } - bool SHCollider::GetDebugDrawState() const noexcept + bool SHCompositeCollider::GetDebugDrawState() const noexcept { return debugDraw; } - const SHTransform& SHCollider::GetTransform() const noexcept + const SHTransform& SHCompositeCollider::GetTransform() const noexcept { return transform; } - const SHVec3& SHCollider::GetPosition() const noexcept + const SHVec3& SHCompositeCollider::GetPosition() const noexcept { return transform.position; } - const SHQuaternion& SHCollider::GetOrientation() const noexcept + const SHQuaternion& SHCompositeCollider::GetOrientation() const noexcept { return transform.orientation; } - const SHVec3& SHCollider::GetScale() const noexcept + const SHVec3& SHCompositeCollider::GetScale() const noexcept { return transform.scale; } - const SHCollider::CollisionShapes& SHCollider::GetCollisionShapes() const noexcept + const SHCompositeCollider::CollisionShapes& SHCompositeCollider::GetCollisionShapes() const noexcept { return shapes; } - SHCollisionShape* SHCollider::GetCollisionShape(int index) const + SHCollisionShape* SHCompositeCollider::GetCollisionShape(int index) const { const int NUM_SHAPES = static_cast(shapes.size()); @@ -197,7 +197,7 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollider::SetIsActive(bool state) noexcept + void SHCompositeCollider::SetIsActive(bool state) noexcept { if (active == state) return; @@ -216,7 +216,7 @@ namespace SHADE } } - void SHCollider::SetDebugDrawState(bool state) noexcept + void SHCompositeCollider::SetDebugDrawState(bool state) noexcept { debugDraw = state; @@ -234,36 +234,36 @@ namespace SHADE #endif } - void SHCollider::SetRigidBody(SHRigidBody* rb) noexcept + void SHCompositeCollider::SetRigidBody(SHRigidBody* rb) noexcept { rigidBody = rb; } - void SHCollider::SetTransform(const SHTransform& newTransform) noexcept + void SHCompositeCollider::SetTransform(const SHTransform& newTransform) noexcept { hasMoved = true; transform = newTransform; } - void SHCollider::SetPosition(const SHVec3& newPosition) noexcept + void SHCompositeCollider::SetPosition(const SHVec3& newPosition) noexcept { hasMoved = true; transform.position = newPosition; } - void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept + void SHCompositeCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept { hasMoved = true; transform.orientation = newOrientation; } - void SHCollider::SetScale(const SHVec3& newScale) noexcept + void SHCompositeCollider::SetScale(const SHVec3& newScale) noexcept { hasMoved = true; transform.scale = newScale; } - void SHCollider::SetFactory(SHCollisionShapeLibrary* factory) noexcept + void SHCompositeCollider::SetFactory(SHCollisionShapeLibrary* factory) noexcept { shapeLibrary = factory; } @@ -272,12 +272,12 @@ namespace SHADE /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - const SHMatrix& SHCollider::ComputeTransformTRS() noexcept + const SHMatrix& SHCompositeCollider::ComputeTransformTRS() noexcept { return transform.ComputeTRS(); } - int SHCollider::AddSphereCollisionShape(float relativeRadius, const SHVec3& posOffset, const SHVec3& rotOffset) + int SHCompositeCollider::AddSphereCollisionShape(float relativeRadius, const SHVec3& posOffset, const SHVec3& rotOffset) { if (!shapeLibrary) { @@ -304,7 +304,7 @@ namespace SHADE const uint32_t NEW_INDEX = static_cast(shapes.size()); const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - SHSphereCollisionShape* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); + SHSphere* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); // Set offsets sphere->collider = this; @@ -332,7 +332,7 @@ namespace SHADE return static_cast(NEW_INDEX); } - int SHCollider::AddBoxCollisionShape(const SHVec3& relativeExtents, const SHVec3& posOffset, const SHVec3& rotOffset) + int SHCompositeCollider::AddBoxCollisionShape(const SHVec3& relativeExtents, const SHVec3& posOffset, const SHVec3& rotOffset) { if (!shapeLibrary) { @@ -357,7 +357,7 @@ namespace SHADE const uint32_t NEW_INDEX = static_cast(shapes.size()); const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - SHBoxCollisionShape* box = shapeLibrary->CreateBox(NEW_SHAPE_ID, BOX_CREATE_INFO); + SHBox* box = shapeLibrary->CreateBox(NEW_SHAPE_ID, BOX_CREATE_INFO); // Set offsets box->collider = this; @@ -386,7 +386,7 @@ namespace SHADE } - void SHCollider::RemoveCollisionShape(int index) + void SHCompositeCollider::RemoveCollisionShape(int index) { if (!shapeLibrary) { @@ -434,23 +434,23 @@ namespace SHADE SHLOG_INFO_D("Removing Collision Shape {} from Entity {}", index, entityID) } - void SHCollider::RecomputeShapes() noexcept + void SHCompositeCollider::RecomputeShapes() noexcept { for (auto* shape : shapes) - shape->ComputeTransforms(); + shape->Update(); } /*-----------------------------------------------------------------------------------*/ /* Private Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollider::copyShapes(const SHCollider& rhsCollider) + void SHCompositeCollider::copyShapes(const SHCompositeCollider& rhsCollider) { for (const auto* shape : rhsCollider.shapes) copyShape(shape); } - void SHCollider::copyShape(const SHCollisionShape* rhsShape) + void SHCompositeCollider::copyShape(const SHCollisionShape* rhsShape) { switch (rhsShape->GetType()) { @@ -460,7 +460,7 @@ namespace SHADE } case SHCollisionShape::Type::SPHERE: { - const SHSphereCollisionShape* RHS_SPHERE = dynamic_cast(rhsShape); + const SHSphere* RHS_SPHERE = dynamic_cast(rhsShape); const SHSphereCreateInfo SPHERE_CREATE_INFO { @@ -473,7 +473,7 @@ namespace SHADE const uint32_t NEW_INDEX = static_cast(shapes.size()); const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - SHSphereCollisionShape* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); + SHSphere* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); *sphere = *RHS_SPHERE; shapes.emplace_back(sphere); diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h similarity index 92% rename from SHADE_Engine/src/Physics/Collision/SHCollider.h rename to SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h index c4a4ad17..e5ce8609 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h @@ -33,7 +33,7 @@ namespace SHADE * Base class for a collider. * There are only two collider types supported by SHADE Engine: Composite & Hull */ - class SH_API SHCollider + class SH_API SHCompositeCollider { private: /*---------------------------------------------------------------------------------*/ @@ -64,17 +64,17 @@ namespace SHADE * This is particularly important for composite colliders for offsets & relative sizes. * @return */ - SHCollider (EntityID eid, const SHTransform& worldTransform = SHTransform::Identity) noexcept; - SHCollider (const SHCollider& rhs) noexcept; - SHCollider (SHCollider&& rhs) noexcept; - ~SHCollider () noexcept; + SHCompositeCollider (EntityID eid, const SHTransform& worldTransform = SHTransform::Identity) noexcept; + SHCompositeCollider (const SHCompositeCollider& rhs) noexcept; + SHCompositeCollider (SHCompositeCollider&& rhs) noexcept; + ~SHCompositeCollider () noexcept; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHCollider& operator=(const SHCollider& rhs) noexcept; - SHCollider& operator=(SHCollider&& rhs) noexcept; + SHCompositeCollider& operator=(const SHCompositeCollider& rhs) noexcept; + SHCompositeCollider& operator=(SHCompositeCollider&& rhs) noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ @@ -192,7 +192,7 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - void copyShapes (const SHCollider& rhsCollider); + void copyShapes (const SHCompositeCollider& rhsCollider); void copyShape (const SHCollisionShape* rhsShape); }; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index 3b2de659..9688caa2 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -17,7 +17,7 @@ #include #include -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" #include "Tools/Logger/SHLogger.h" namespace SHADE @@ -296,7 +296,7 @@ namespace SHADE /* Setter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHRigidBody::SetCollider(SHCollider* c) noexcept + void SHRigidBody::SetCollider(SHCompositeCollider* c) noexcept { collider = c; } @@ -690,7 +690,7 @@ namespace SHADE trueMass.emplace_back(MASS); // Weighted sum of masses contribute to the centroid's location using the collider's local position. - localCentroid += MASS * (shape->GetPosition() - collider->GetPosition()); + localCentroid += MASS * shape->GetRelativeCentroid(); } if (totalMass > 0.0f) @@ -726,8 +726,9 @@ namespace SHADE I = R * I; // Parallel Axis Theorem + // https://en.wikipedia.org/wiki/Parallel_axis_theorem // J = I + m((R /dot R)E_3 - R /outerProduct R) - const SHVec3 R = SHAPE->GetPosition() - worldCentroid; + const SHVec3 R = SHAPE->GetWorldCentroid() - worldCentroid; const float R_MAG2 = R.LengthSquared(); const SHMatrix R_OX_R = SHVec3::OuterProduct(R, R); diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index 3c869ad2..44ae3f48 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -22,7 +22,7 @@ namespace SHADE /* Forward Declarations */ /*-------------------------------------------------------------------------------------*/ - class SHCollider; + class SHCompositeCollider; /*-------------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -113,7 +113,7 @@ namespace SHADE /* Setter Functions */ /*-----------------------------------------------------------------------------------*/ - void SetCollider (SHCollider* c) noexcept; + void SetCollider (SHCompositeCollider* c) noexcept; /** * @brief @@ -229,7 +229,7 @@ namespace SHADE // The entityID here is only meant for linking with the actual component in the engine. EntityID entityID; - SHCollider* collider; + SHCompositeCollider* collider; Type bodyType; diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp index 56c2ac7b..07e08b78 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp @@ -111,7 +111,7 @@ namespace SHADE collider->SetRigidBody(nullptr); } - SHCollider* SHPhysicsObject::CreateCollider(const SHTransform& transform) + SHCompositeCollider* SHPhysicsObject::CreateCollider(const SHTransform& transform) { if (collider) { @@ -119,7 +119,7 @@ namespace SHADE return collider; } - collider = new SHCollider{ entityID, transform }; + collider = new SHCompositeCollider{ entityID, transform }; // Link with rigidBody if it exists if (rigidBody) @@ -148,13 +148,13 @@ namespace SHADE /* Private Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsObject::deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCollider* rhsCollider) + void SHPhysicsObject::deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCompositeCollider* rhsCollider) { if (rhsRigidBody) rigidBody = new SHRigidBody{ *rhsRigidBody }; if (rhsCollider) - collider = new SHCollider { *rhsCollider }; + collider = new SHCompositeCollider { *rhsCollider }; } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h index 2550543b..f1ee042d 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h @@ -11,7 +11,7 @@ #pragma once // Project Headers -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" #include "Physics/Dynamics/SHRigidBody.h" namespace SHADE @@ -33,7 +33,7 @@ namespace SHADE EntityID entityID = MAX_EID; SHRigidBody* rigidBody = nullptr; - SHCollider* collider = nullptr; + SHCompositeCollider* collider = nullptr; /*-----------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -91,7 +91,7 @@ namespace SHADE * Pointer to the collider that was created. The memory of this collider is managed * by the physics object itself. */ - SHCollider* CreateCollider (const SHTransform& transform = SHTransform::Identity); + SHCompositeCollider* CreateCollider (const SHTransform& transform = SHTransform::Identity); /** * @brief @@ -104,6 +104,6 @@ namespace SHADE /* Member Functions */ /*-----------------------------------------------------------------------------------*/ - void deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCollider* rhsCollider); + void deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCompositeCollider* rhsCollider); }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp index 7425f418..5569c713 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp @@ -32,12 +32,12 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollider* const SHColliderComponent::GetCollider() const noexcept + SHCompositeCollider* const SHColliderComponent::GetCollider() const noexcept { return collider; } - const SHCollider::CollisionShapes* const SHColliderComponent::GetCollisionShapes() const noexcept + const SHCompositeCollider::CollisionShapes* const SHColliderComponent::GetCollisionShapes() const noexcept { if (!collider) return nullptr; @@ -66,7 +66,7 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHColliderComponent::SetCollider(SHCollider* c) noexcept + void SHColliderComponent::SetCollider(SHCompositeCollider* c) noexcept { collider = c; } diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h index d0cc7064..3c8e6342 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h @@ -14,7 +14,7 @@ // Project Headers #include "ECS_Base/Components/SHComponent.h" -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" namespace SHADE { @@ -54,8 +54,8 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHCollider* const GetCollider () const noexcept; - [[nodiscard]] const SHCollider::CollisionShapes* const GetCollisionShapes() const noexcept; + [[nodiscard]] SHCompositeCollider* const GetCollider () const noexcept; + [[nodiscard]] const SHCompositeCollider::CollisionShapes* const GetCollisionShapes() const noexcept; [[nodiscard]] SHCollisionShape* const GetCollisionShape (int index) const; // Required for serialisation @@ -66,7 +66,7 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetCollider (SHCollider* c) noexcept; + void SetCollider (SHCompositeCollider* c) noexcept; // Required for serialisation @@ -77,7 +77,7 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHCollider* collider; + SHCompositeCollider* collider; RTTR_ENABLE() }; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index 3bd26925..1b1055be 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -122,7 +122,7 @@ namespace SHADE return onColliderDrawEvent.get()->handle; } - void SHPhysicsDebugDrawSystem::drawCollider(SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept + void SHPhysicsDebugDrawSystem::drawCollider(SHDebugDrawSystem* debugDrawSystem, const SHCompositeCollider& collider) noexcept { for (const auto* SHAPE : collider.GetCollisionShapes()) { @@ -132,12 +132,12 @@ namespace SHADE { case SHCollisionShape::Type::SPHERE: { - debugDrawSystem->DrawWireSphere(SHAPE->ComputeWorldTransform(), DRAW_COLOUR, true); + debugDrawSystem->DrawWireSphere(SHAPE->GetTRS(), DRAW_COLOUR, true); break; } case SHCollisionShape::Type::BOX: { - debugDrawSystem->DrawWireCube(SHAPE->ComputeWorldTransform(), DRAW_COLOUR, true); + debugDrawSystem->DrawWireCube(SHAPE->GetTRS(), DRAW_COLOUR, true); break; } case SHCollisionShape::Type::CAPSULE: diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index efc0738e..c284792f 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -18,9 +18,9 @@ #include "ECS_Base/System/SHSystemRoutine.h" #include "Events/SHEvent.h" #include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" -#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" #include "Physics/Collision/SHPhysicsRaycastResult.h" -#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h" +#include "Physics/Collision/CollisionShapes/SHSphere.h" namespace SHADE @@ -138,7 +138,7 @@ namespace SHADE SHEventHandle onColliderDraw(SHEventPtr onColliderDrawEvent); - static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; + static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCompositeCollider& collider) noexcept; static void drawRaycast (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept; }; diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index a746df1b..18d8d63f 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -1,7 +1,7 @@ #pragma once #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" -#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h" +#include "Physics/Collision/CollisionShapes/SHSphere.h" #include "Resource/SHResourceManager.h" #include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec3.h" @@ -144,13 +144,13 @@ namespace YAML { case SHCollisionShape::Type::BOX: { - const auto& BOX = dynamic_cast(rhs); + const auto& BOX = dynamic_cast(rhs); node[HalfExtents] = BOX.GetRelativeExtents(); } break; case SHCollisionShape::Type::SPHERE: { - const auto& SPHERE = dynamic_cast(rhs); + const auto& SPHERE = dynamic_cast(rhs); node[Radius] = SPHERE.GetRelativeRadius(); } break; @@ -188,7 +188,7 @@ namespace YAML { if (node[HalfExtents].IsDefined()) { - auto* box = dynamic_cast(&rhs); + auto* box = dynamic_cast(&rhs); box->SetRelativeExtents(node[HalfExtents].as()); } } @@ -197,7 +197,7 @@ namespace YAML { if (node[Radius].IsDefined()) { - auto* sphere = dynamic_cast(&rhs); + auto* sphere = dynamic_cast(&rhs); sphere->SetRelativeRadius(node[Radius].as()); } } diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index 9ed46289..f4b4f09d 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -128,19 +128,19 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 BoxCollider::Center::get() { - return Convert::ToCLI(getNativeCollisionShape().GetCenter()); + return Convert::ToCLI(getNativeCollisionShape().GetWorldCentroid()); } Vector3 BoxCollider::HalfExtents::get() { - return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); + return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); } void BoxCollider::HalfExtents::set(Vector3 value) { - getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); + getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); } Quaternion BoxCollider::Orientation::get() { - return Convert::ToCLI(getNativeCollisionShape().GetOrientation()); + return Convert::ToCLI(getNativeCollisionShape().GetWorldOrientation()); } /*---------------------------------------------------------------------------------*/ @@ -162,15 +162,15 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 SphereCollider::Center::get() { - return Convert::ToCLI(getNativeCollisionShape().GetCenter()); + return Convert::ToCLI(getNativeCollisionShape().GetWorldCentroid()); } float SphereCollider::Radius::get() { - return getNativeCollisionShape().GetWorldRadius(); + return getNativeCollisionShape().GetWorldRadius(); } void SphereCollider::Radius::set(float value) { - getNativeCollisionShape().SetWorldRadius(value); + getNativeCollisionShape().SetWorldRadius(value); } /*---------------------------------------------------------------------------------*/ @@ -178,11 +178,11 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ bool SphereCollider::TestPoint(Vector3 point) { - return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); + return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); } bool SphereCollider::Raycast(Ray ray, float maxDistance) { - return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); + return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); } /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Physics/Physics.cxx b/SHADE_Managed/src/Physics/Physics.cxx index bfb79b75..60aba58d 100644 --- a/SHADE_Managed/src/Physics/Physics.cxx +++ b/SHADE_Managed/src/Physics/Physics.cxx @@ -179,20 +179,7 @@ namespace SHADE throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); - switch (NATIVE_SHAPE.GetType()) - { - case SHCollisionShape::Type::SPHERE: - { - shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); - break; - } - case SHCollisionShape::Type::BOX: - { - shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); - break; - } - default: break; - } + shapePos = Convert::ToCLI(NATIVE_SHAPE.GetWorldCentroid()); ray.Position += shapePos; @@ -226,20 +213,7 @@ namespace SHADE throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); - switch (NATIVE_SHAPE.GetType()) - { - case SHCollisionShape::Type::SPHERE: - { - shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); - break; - } - case SHCollisionShape::Type::BOX: - { - shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); - break; - } - default: break; - } + shapePos = Convert::ToCLI(NATIVE_SHAPE.GetWorldCentroid()); ray.Position += shapePos; @@ -306,20 +280,7 @@ namespace SHADE throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); - switch (NATIVE_SHAPE.GetType()) - { - case SHCollisionShape::Type::SPHERE: - { - shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); - break; - } - case SHCollisionShape::Type::BOX: - { - shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); - break; - } - default: break; - } + shapePos = Convert::ToCLI(NATIVE_SHAPE.GetWorldCentroid()); start += shapePos; From 9d173282629e88b27c0d0d28aad8dac495c78b15 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 4 Jan 2023 18:20:04 +0800 Subject: [PATCH 079/164] Added bone animation supported vertex shader --- Assets/Shaders/Anim_VS.glsl | 75 ++++++++++++++++++ Assets/Shaders/Anim_VS.shshaderb | Bin 0 -> 5633 bytes Assets/Shaders/Anim_VS.shshaderb.shmeta | 3 + .../MaterialInspector/SHMaterialInspector.cpp | 2 +- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 6 +- .../GlobalData/SHGraphicsPredefinedData.cpp | 33 +++----- .../GlobalData/SHGraphicsPredefinedData.h | 1 - .../GlobalData/SHPredefinedDescriptorTypes.h | 2 +- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 9 ++- .../MiddleEnd/Interface/SHGraphicsSystem.h | 2 + .../MiddleEnd/Interface/SHMaterial.cpp | 2 +- .../Interface/SHMaterialInstance.cpp | 2 +- .../Serialization/SHSerializationHelper.hpp | 2 +- 13 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 Assets/Shaders/Anim_VS.glsl create mode 100644 Assets/Shaders/Anim_VS.shshaderb create mode 100644 Assets/Shaders/Anim_VS.shshaderb.shmeta diff --git a/Assets/Shaders/Anim_VS.glsl b/Assets/Shaders/Anim_VS.glsl new file mode 100644 index 00000000..c9aa64aa --- /dev/null +++ b/Assets/Shaders/Anim_VS.glsl @@ -0,0 +1,75 @@ +#version 450 +#extension GL_KHR_vulkan_glsl : enable + +//#include "ShaderDescriptorDefinitions.glsl" + + +layout(location = 0) in vec3 aVertexPos; +layout(location = 1) in vec2 aUV; +layout(location = 2) in vec3 aNormal; +layout(location = 3) in vec3 aTangent; +layout(location = 4) in mat4 worldTransform; +layout(location = 8) in uvec2 integerData; +layout(location = 9) in uvec4 aBoneIndices; +layout(location = 10) in vec4 aBoneWeights; + +layout(location = 0) out struct +{ + vec4 vertPos; // location 0 + vec2 uv; // location = 1 + vec4 normal; // location = 2 + +} Out; + +// material stuff +layout(location = 3) out struct +{ + int materialIndex; + uint eid; + uint lightLayerIndex; + +} Out2; + +layout(set = 1, binding = 0) uniform CameraData +{ + vec4 position; + mat4 vpMat; + mat4 viewMat; + mat4 projMat; +} cameraData; + +layout (std430, set = 2, binding = 1) buffer AnimBoneMatrices +{ + mat4 data[]; +} BoneMatrices; + +void main() +{ + Out2.materialIndex = gl_InstanceIndex; + Out2.eid = integerData[0]; + Out2.lightLayerIndex = integerData[1]; + + // for transforming gBuffer position and normal data + mat4 modelViewMat = cameraData.viewMat * worldTransform; + + // gBuffer position will be in view space + Out.vertPos = modelViewMat * vec4(aVertexPos, 1.0f); + + // uvs for texturing in fragment shader + Out.uv = aUV; + + mat3 transposeInv = mat3 (transpose(inverse(modelViewMat))); + + // normals are also in view space + Out.normal.rgb = transposeInv * aNormal.rgb; + Out.normal.rgb = normalize (Out.normal.rgb); + + // Compute bone matrix + mat4 boneMatrix = BoneMatrices.data[aBoneIndices[0]] * aBoneWeights[0]; + boneMatrix += BoneMatrices.data[aBoneIndices[1]] * aBoneWeights[1]; + boneMatrix += BoneMatrices.data[aBoneIndices[2]] * aBoneWeights[2]; + boneMatrix += BoneMatrices.data[aBoneIndices[3]] * aBoneWeights[3]; + + // clip space for rendering + gl_Position = cameraData.vpMat * worldTransform * boneMatrix * vec4 (aVertexPos, 1.0f); +} \ No newline at end of file diff --git a/Assets/Shaders/Anim_VS.shshaderb b/Assets/Shaders/Anim_VS.shshaderb new file mode 100644 index 0000000000000000000000000000000000000000..b1940e9865db2bbb8bfda8a10170d1e63280ac24 GIT binary patch literal 5633 zcmZ9O`FmYe6^2j7VhfZR6eu?>6124nmRhEk&|;_t5^O0dXgK5^(t|hm#+#e81(XzJ zaD?J4gQ$SuJP#;}2r866h+lXf-}jue(qng?_3UqbYuIb=wfDK{d+W@k7z6zqRwYSa zvN|~_d9$RFQe&$z5I7syP#Ps;&a8Yi%ZV>ns0g6Gm+*3Hw_?shXfSlG<$i6lvu zH&=YEua(d4?@gqKvTo_^8t!#+vF8?(A@aKK+1B*#_Chah&t#Dn+fZ>HdApa*X5C$B zFNGOSF2sMX)681?^6bD3X|K#Pl)M|TGo8!2Y2k|JaKUMx?=0lKyi=U@Fj@4q-lh36 zL2%Z*RHY5h&dql_cj;+Oqv$iWb*#yr9q4ph&Ar{Uy>MrzJLld8lA`(8%oTVk>-MHP z3vh!4XB~@6$9$}zO@_2p=J236uVQWQM7oaz*+EkjYmCX=ybnB_jDvfYz=k(b_E+60DcQC@Pque^6P)j%in zDu=xHx8QHdycI=m`M<)`|Ea87ZVVN+i$nZL!r_vDqizmBX{~eWiQT_H5QJ?$&s9XV<|SukM{)gKVsH->r7v!S&?u zemORj6Wrj+og0D+ANQfpDaUbqU@ESRb5Hd+b>pUx$R&4tob}e@)Qx)-i8#548)t{s zZZ7vZ%Df)2&d^7|4t4Ln`}Vt`?p;-PsC!@Q`dFn$KEG|+W4#W+2P=bh6>zO)S%dohQ$D~WM$?niE8 zmD8FW4fGS4&2Jw4)xG0#+Sf80t3BfV71Mq;_S2YM+nV)1omrc_`P6+Qa>nah{hSv5 zXH~YS|DEWrrylE_3pRhmp9ePIS$H;>6>Ax(u;+aa`g&$Lb!*$eY+m_w?8y7BT|Gp< zi_rTK?e0N+wBod1Tw!C>H!}Aj>iVm1syOR;=KTz_K62(&_ua~Q4*a|p z>qbxgVRub={na}aXTDyAM=HCz?>y>UTEWNad+zbdeSf82jeQt=GUB@( zBg3L4-@7@~^@$wkgU#W4j2wQ` z=VORm^mH%S?~P~gUT#M8ksl+k-=A9$&(gc)J$GLX=8q%#J6uaYxrqA&IO4q9 z5%)>3an^5~esU3a8#v-_1xMVcz{Ytd#_1;)cjVLHh;x4<=+lUNoZ;;iC+~KkBt6}cMVZzA&1t8amglaD;#2G@J_9dtQI?EAZ5 zV;#|}?}7Ez7JlCc8*5*JdmL#HsNo&+0fUxIrIY;3((KS0-4TlhT%IB~x|}2S{Y$X1j_B2|!1`(nzh8rmt@r9T=*HH2 z^;>j(wMFdjz{b{l^?P(<>%IB|y1v>X_K#p=>%ICDy0P_M{TW?fZPBa0fZdBVnOmRe zl{U}!V&=aiqsXa9oZUYvKJ=H-HzD!A*+0Shyo`j;D`3w%eEtR2PhOub%&#I_5%GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface ( - mappings.at(SHPredefinedDescriptorTypes::MATERIALS), + mappings.at(SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH), SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA ); if (!interface) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 44fc87b0..50fce74e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -420,7 +420,7 @@ namespace SHADE // - Material Properties Data const Handle SHADER_INFO = pipeline->GetPipelineLayout()->GetShaderBlockInterface ( - descMappings.at(SHPredefinedDescriptorTypes::MATERIALS), + descMappings.at(SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH), SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, vk::ShaderStageFlagBits::eFragment ); @@ -581,7 +581,7 @@ namespace SHADE return; // Bind all required objects before drawing - static std::array dynamicOffset{ 0 }; + static std::array dynamicOffset{ 0U, static_cast(boneMatrixData.size() * sizeof(SHMatrix)) }; cmdBuffer->BeginLabeledSegment("SHBatch for Pipeline #" + std::to_string(pipeline.GetId().Data.Index)); cmdBuffer->BindPipeline(pipeline); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0); @@ -594,7 +594,7 @@ namespace SHADE ( instanceDataDescSet[frameIndex], SH_PIPELINE_TYPE::GRAPHICS, - descMappings.at(SHPredefinedDescriptorTypes::MATERIALS), + descMappings.at(SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH), dynamicOffset ); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp index 2d741f7f..bbd1a1b1 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp @@ -23,17 +23,9 @@ namespace SHADE { perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].descMappings.AddMappings ({ - {SHPredefinedDescriptorTypes::STATIC_DATA, 0}, - {SHPredefinedDescriptorTypes::CAMERA, 1}, - {SHPredefinedDescriptorTypes::MATERIALS, 2}, - }); - - perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING_ANIM)].descMappings.AddMappings - ({ - {SHPredefinedDescriptorTypes::STATIC_DATA, 0}, - {SHPredefinedDescriptorTypes::CAMERA, 1}, - {SHPredefinedDescriptorTypes::MATERIALS, 2}, - {SHPredefinedDescriptorTypes::BONES, 3}, + {SHPredefinedDescriptorTypes::STATIC_DATA, 0}, + {SHPredefinedDescriptorTypes::CAMERA, 1}, + {SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH, 2}, }); perSystemData[SHUtilities::ConvertEnum(SystemType::TEXT_RENDERING)].descMappings.AddMappings @@ -137,7 +129,14 @@ namespace SHADE .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, .DescriptorCount = 1, }; - Handle materialDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout({ materialDataBinding }); + SHVkDescriptorSetLayout::Binding boneDataBinding + { + .Type = vk::DescriptorType::eStorageBufferDynamic, + .Stage = vk::ShaderStageFlagBits::eVertex, + .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA, + .DescriptorCount = 1, + }; + Handle materialDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout({ materialDataBinding, boneDataBinding }); SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, materialDataPerInstanceLayout->GetVkHandle(), "[Descriptor Set Layout] Material Globals"); // font bitmap data (texture) @@ -185,15 +184,7 @@ namespace SHADE ( SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::STATIC_DATA | SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::CAMERA | - SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIALS - ); - - perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING_ANIM)].descSetLayouts = GetPredefinedDescSetLayouts - ( - SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::STATIC_DATA | - SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::CAMERA | - SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIALS | - SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::BONES + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIALS ); perSystemData[SHUtilities::ConvertEnum(SystemType::TEXT_RENDERING)].descSetLayouts = GetPredefinedDescSetLayouts diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h index 5f574b2d..8c304e4c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h @@ -35,7 +35,6 @@ namespace SHADE enum class SystemType { BATCHING = 0, - BATCHING_ANIM, TEXT_RENDERING, RENDER_GRAPH_NODE_COMPUTE, NUM_TYPES diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h index 64a7fa6d..7a7bb826 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h @@ -11,7 +11,7 @@ namespace SHADE STATIC_DATA, LIGHTS, CAMERA, - MATERIALS, + PER_INSTANCE_BATCH, BONES, FONT, RENDER_GRAPH_RESOURCE, diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 8a9737c2..a2be7405 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -126,6 +126,7 @@ namespace SHADE // Load Built In Shaders static constexpr AssetID VS_DEFAULT = 39210065; defaultVertShader = SHResourceManager::LoadOrGet(VS_DEFAULT); + static constexpr AssetID VS_ANIM = 47911992; animtVertShader = SHResourceManager::LoadOrGet(VS_ANIM); static constexpr AssetID FS_DEFAULT = 46377769; defaultFragShader = SHResourceManager::LoadOrGet(FS_DEFAULT); static constexpr AssetID VS_DEBUG = 48002439; debugVertShader = SHResourceManager::LoadOrGet(VS_DEBUG); static constexpr AssetID FS_DEBUG = 36671027; debugFragShader = SHResourceManager::LoadOrGet(FS_DEBUG); @@ -424,10 +425,16 @@ namespace SHADE // Create default materials defaultMaterial = AddMaterial ( - defaultVertShader, defaultFragShader, + defaultVertShader, defaultFragShader, renderGraph->GetNode("G-Buffer")->GetSubpass("G-Buffer Write") ); defaultMaterial->SetProperty("data.textureIndex", defaultTexture->TextureArrayIndex); + defaultAnimMaterial = AddMaterial + ( + animtVertShader, defaultFragShader, + renderGraph->GetNode("G-Buffer")->GetSubpass("G-Buffer Write") + ); + defaultAnimMaterial->SetProperty("data.textureIndex", defaultTexture->TextureArrayIndex); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 76c14700..c2151b21 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -457,6 +457,7 @@ namespace SHADE // Built-In Shaders Handle defaultVertShader; + Handle animtVertShader; Handle defaultFragShader; Handle debugVertShader; Handle debugFragShader; @@ -474,6 +475,7 @@ namespace SHADE // Built-In Materials Handle defaultMaterial; + Handle defaultAnimMaterial; Handle debugDrawPipeline; Handle debugDrawDepthPipeline; Handle debugDrawLineMeshPipeline; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.cpp index 59f3ba73..2ee56c51 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterial.cpp @@ -100,7 +100,7 @@ namespace SHADE auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); return pipeline->GetPipelineLayout()->GetShaderBlockInterface ( - mappings.at (SHPredefinedDescriptorTypes::MATERIALS), + mappings.at (SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH), //SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, vk::ShaderStageFlagBits::eFragment diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.cpp index 70204401..6ebb2661 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMaterialInstance.cpp @@ -81,7 +81,7 @@ namespace SHADE { return baseMaterial->GetPipeline()->GetPipelineLayout()->GetShaderBlockInterface ( - SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING).at(SHPredefinedDescriptorTypes::MATERIALS), + SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING).at(SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH), //SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, vk::ShaderStageFlagBits::eFragment diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index ece0e452..08f1262f 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -325,7 +325,7 @@ namespace SHADE { auto fragShader = SHResourceManager::LoadOrGet(spec.fragShader); auto const& mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); - auto interface = fragShader->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(mappings.at(SHPredefinedDescriptorTypes::MATERIALS), SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA); + auto interface = fragShader->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(mappings.at(SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH), SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA); int const varCount = static_cast(interface->GetVariableCount()); for (int i = 0; i < varCount; ++i) From dffdec9d9c428573ecb3c6dfe2cae0ad4cd68055 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 4 Jan 2023 19:45:41 +0800 Subject: [PATCH 080/164] Cleaned up colliders --- .../Inspector/SHEditorComponentView.hpp | 8 +- SHADE_Engine/src/Math/Geometry/SHAABB.h | 11 +- SHADE_Engine/src/Math/Geometry/SHPlane.h | 10 +- SHADE_Engine/src/Math/Geometry/SHShape.h | 42 -- .../Collision/Broadphase/SHDynamicAABBTree.h | 2 +- .../Collision/Contacts/SHCollisionKey.cpp | 2 +- .../Physics/Collision/Contacts/SHContact.h | 2 +- .../Physics/Collision/Contacts/SHManifold.h | 2 +- .../Narrowphase/SHCapsuleVsConvex.cpp | 2 +- .../Collision/Narrowphase/SHCollision.h | 1 - .../Narrowphase/SHConvexVsConvex.cpp | 2 + .../Narrowphase/SHSphereVsCapsule.cpp | 2 +- .../Narrowphase/SHSphereVsConvex.cpp | 4 +- .../Narrowphase/SHSphereVsSphere.cpp | 2 +- .../src/Physics/Collision/SHCollider.cpp | 393 ++++++++++++++++++ .../src/Physics/Collision/SHCollider.h | 178 ++++++++ .../Physics/Collision/SHCollisionSpace.cpp | 14 +- .../src/Physics/Collision/SHCollisionSpace.h | 10 +- .../Physics/Collision/SHCompositeCollider.cpp | 339 +-------------- .../Physics/Collision/SHCompositeCollider.h | 141 +------ .../{CollisionShapes => Shapes}/SHBox.cpp | 2 +- .../{CollisionShapes => Shapes}/SHBox.h | 1 + .../SHCollisionShape.cpp | 2 +- .../SHCollisionShape.h | 3 +- .../SHCollisionShapeID.h | 0 .../SHCollisionShapeID.hpp | 0 .../SHCollisionShapeLibrary.cpp | 0 .../SHCollisionShapeLibrary.h | 0 .../SHConvexPolyhedron.cpp | 44 +- .../SHConvexPolyhedron.h | 2 +- .../SHHalfEdgeStructure.cpp | 0 .../SHHalfEdgeStructure.h | 0 .../{CollisionShapes => Shapes}/SHSphere.cpp | 2 +- .../{CollisionShapes => Shapes}/SHSphere.h | 1 + .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 2 +- .../src/Physics/Dynamics/SHRigidBody.cpp | 4 +- .../src/Physics/Dynamics/SHRigidBody.h | 6 +- .../PhysicsObject/SHPhysicsObject.cpp | 9 +- .../Interface/PhysicsObject/SHPhysicsObject.h | 16 +- .../PhysicsObject/SHPhysicsObjectManager.cpp | 12 +- .../PhysicsObject/SHPhysicsObjectManager.h | 4 +- .../Physics/Interface/SHColliderComponent.cpp | 7 +- .../Physics/Interface/SHColliderComponent.h | 4 +- SHADE_Engine/src/Physics/SHPhysicsEvents.h | 2 +- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 2 +- .../System/SHPhysicsDebugDrawSystem.cpp | 2 +- .../Physics/System/SHPhysicsDebugDrawSystem.h | 6 +- .../src/Physics/System/SHPhysicsSystem.cpp | 4 +- .../src/Serialization/SHYAMLConverters.h | 12 +- 49 files changed, 697 insertions(+), 619 deletions(-) delete mode 100644 SHADE_Engine/src/Math/Geometry/SHShape.h create mode 100644 SHADE_Engine/src/Physics/Collision/SHCollider.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/SHCollider.h rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHBox.cpp (99%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHBox.h (99%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHCollisionShape.cpp (99%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHCollisionShape.h (98%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHCollisionShapeID.h (100%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHCollisionShapeID.hpp (100%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHCollisionShapeLibrary.cpp (100%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHCollisionShapeLibrary.h (100%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHConvexPolyhedron.cpp (66%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHConvexPolyhedron.h (99%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHHalfEdgeStructure.cpp (100%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHHalfEdgeStructure.h (100%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHSphere.cpp (98%) rename SHADE_Engine/src/Physics/Collision/{CollisionShapes => Shapes}/SHSphere.h (99%) diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 765dbc9b..db9c161f 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -21,6 +21,7 @@ #include "Serialization/SHSerializationHelper.hpp" #include "Tools/Utilities/SHClipboardUtilities.h" #include "SHInspectorCommands.h" +#include "Physics/Collision/SHCompositeCollider.h" #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" namespace SHADE { @@ -420,17 +421,20 @@ namespace SHADE } ImGui::EndChild(); + // TODO: Handle differences between composite & hull collider if (ImGui::BeginMenu("Add Collider")) { int newColl = -1; if (ImGui::Selectable("Box Collider")) { - component->GetCollider()->AddBoxCollisionShape(SHVec3::One); + auto* compositeCollider = dynamic_cast(component->GetCollider()); + compositeCollider->AddBoxCollisionShape(SHVec3::One); } if (ImGui::Selectable("Sphere Collider")) { - component->GetCollider()->AddSphereCollisionShape(1.0f); + auto* compositeCollider = dynamic_cast(component->GetCollider()); + compositeCollider->AddSphereCollisionShape(1.0f); } //No idea why this doesn't work diff --git a/SHADE_Engine/src/Math/Geometry/SHAABB.h b/SHADE_Engine/src/Math/Geometry/SHAABB.h index e6536fa5..9b62c85b 100644 --- a/SHADE_Engine/src/Math/Geometry/SHAABB.h +++ b/SHADE_Engine/src/Math/Geometry/SHAABB.h @@ -13,7 +13,7 @@ #include // Project Headers -#include "SHShape.h" +#include "Math/SHRay.h" namespace SHADE { @@ -25,8 +25,7 @@ namespace SHADE * @brief * Encapsulates a 3D Axis-Aligned Bounding Box. */ - class SH_API SHAABB : public SHShape, - private DirectX::BoundingBox + class SH_API SHAABB : private DirectX::BoundingBox { public: /*---------------------------------------------------------------------------------*/ @@ -39,7 +38,7 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - ~SHAABB () override = default; + ~SHAABB () noexcept = default; SHAABB () noexcept; SHAABB (const SHVec3& center, const SHVec3& halfExtents) noexcept; @@ -85,7 +84,7 @@ namespace SHADE * @return * True if the point is inside the aabb. */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept; /** * @brief @@ -96,7 +95,7 @@ namespace SHADE * The result of the raycast.
* See the corresponding header for the contents of the raycast result object. */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept; /** * @brief diff --git a/SHADE_Engine/src/Math/Geometry/SHPlane.h b/SHADE_Engine/src/Math/Geometry/SHPlane.h index 581b1c03..6593c627 100644 --- a/SHADE_Engine/src/Math/Geometry/SHPlane.h +++ b/SHADE_Engine/src/Math/Geometry/SHPlane.h @@ -11,7 +11,7 @@ #pragma once // Project Headers -#include "SHShape.h" +#include "Math/SHRay.h" #include "Math/Vector/SHVec4.h" namespace SHADE @@ -24,14 +24,14 @@ namespace SHADE * @brief * Encapsulates a 3D plane in point-normal form. */ - class SH_API SHPlane : public SHShape + class SH_API SHPlane { public: /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - ~SHPlane () override = default; + ~SHPlane () noexcept = default; SHPlane (const SHPlane& rhs) noexcept = default; SHPlane (SHPlane&& rhs) noexcept = default; @@ -77,7 +77,7 @@ namespace SHADE * @return * True if the point is on the plane. */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept; /** * @brief @@ -88,7 +88,7 @@ namespace SHADE * The result of the raycast.
* See the corresponding header for the contents of the raycast result object. */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept; /** * @brief diff --git a/SHADE_Engine/src/Math/Geometry/SHShape.h b/SHADE_Engine/src/Math/Geometry/SHShape.h deleted file mode 100644 index 7781a5a4..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHShape.h +++ /dev/null @@ -1,42 +0,0 @@ -/**************************************************************************************** - * \file SHShape.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a shape. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "Math/SHRay.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a base class for any shape. - */ - class SH_API SHShape - { - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - virtual ~SHShape () = default; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] virtual bool TestPoint (const SHVec3& point) const noexcept = 0; - [[nodiscard]] virtual SHRaycastResult Raycast (const SHRay& ray) const noexcept = 0; - }; -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h index 32b5095a..292c3528 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h @@ -13,7 +13,7 @@ #include // Project Headers -#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" +#include "Physics/Collision/Shapes/SHCollisionShape.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp index 8bd26e91..4bb22697 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp @@ -14,7 +14,7 @@ #include "SHCollisionKey.h" // Project Headers -#include "Physics/Collision/SHCompositeCollider.h" +#include "Physics/Collision/SHCollider.h" #include "Physics/Interface/SHColliderComponent.h" diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h index 0c137f2f..70e53794 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h @@ -12,7 +12,7 @@ // Primary Header #include "Physics/Dynamics/SHRigidBody.h" -#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" +#include "Physics/Collision/Shapes/SHCollisionShape.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h index 15741276..3b591875 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h @@ -12,7 +12,7 @@ // Primary Header #include "Physics/Dynamics/SHRigidBody.h" -#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" +#include "Physics/Collision/Shapes/SHCollisionShape.h" #include "SHContact.h" #include "SHCollisionEvents.h" diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp index e7da5f8b..6c82c3a5 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp @@ -16,7 +16,7 @@ // Project Headers #include "Math/SHMathHelpers.h" -#include "Physics/Collision/CollisionShapes/SHBox.h" +#include "Physics/Collision/Shapes/SHConvexPolyhedron.h" // TODO diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index e98983b4..0ce1f688 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -11,7 +11,6 @@ #pragma once // Project Headers -#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" #include "Physics/Collision/Contacts/SHManifold.h" #include "Physics/Collision/Contacts/SHCollisionKey.h" diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index d73097bb..6753c453 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -15,6 +15,8 @@ // Project Headers #include "Math/SHMathHelpers.h" +#include "Physics/Collision/Shapes/SHConvexPolyhedron.h" + namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp index d7eb02fc..25515fd7 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp @@ -15,7 +15,7 @@ // Project Headers #include "Math/SHMathHelpers.h" -#include "Physics/Collision/CollisionShapes/SHSphere.h" +#include "Physics/Collision/Shapes/SHSphere.h" // TODO diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 1b97a7c6..2b08313b 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -17,8 +17,8 @@ // Project Headers #include "Math/Geometry/SHPlane.h" #include "Math/SHMathHelpers.h" -#include "Physics/Collision/CollisionShapes/SHCollisionShape.h" -#include "Physics/Collision/CollisionShapes/SHBox.h" +#include "Physics/Collision/Shapes/SHSphere.h" +#include "Physics/Collision/Shapes/SHConvexPolyhedron.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp index aa270b16..1a76924e 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp @@ -15,7 +15,7 @@ // Project Headers #include "Math/SHMathHelpers.h" -#include "Physics/Collision/CollisionShapes/SHSphere.h" +#include "Physics/Collision/Shapes/SHSphere.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp new file mode 100644 index 00000000..2e7cc3c6 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp @@ -0,0 +1,393 @@ +/**************************************************************************************** + * \file SHCollider.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Base Collider Class. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollider.h" + +// Project Headers +#include "Broadphase/SHDynamicAABBTree.h" +#include "Events/SHEvent.h" +#include "Math/SHMathHelpers.h" +#include "Physics/SHPhysicsEvents.h" +#include "Physics/Dynamics/SHRigidBody.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept + : entityID { eid } + , flags { 0 } + , rigidBody { nullptr } + , shapeLibrary { nullptr } + , broadphase { nullptr } + , transform { worldTransform } + { + flags |= ACTIVE_FLAG; + flags |= MOVED_FLAG; + } + + SHCollider::SHCollider(const SHCollider& rhs) noexcept + : entityID { rhs.entityID } + , flags { rhs.flags } + , rigidBody { rhs.rigidBody } + , shapeLibrary { rhs.shapeLibrary } + , broadphase { rhs.broadphase } + , transform { rhs.transform } + {} + + SHCollider::SHCollider(SHCollider&& rhs) noexcept + : entityID { rhs.entityID } + , flags { rhs.flags } + , rigidBody { rhs.rigidBody } + , shapeLibrary { rhs.shapeLibrary } + , broadphase { rhs.broadphase } + , transform { rhs.transform } + {} + + SHCollider::~SHCollider() noexcept + { + if (!shapeLibrary) + { + SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add destroy collider!", entityID) + return; + } + + for (auto* shape : shapes) + shapeLibrary->DestroyShape(shape); + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollider& SHCollider::operator=(const SHCollider& rhs) noexcept + { + if (this == &rhs) + return *this; + + if (!shapeLibrary) + { + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) + return *this; + } + + entityID = rhs.entityID; + flags = rhs.flags; + rigidBody = rhs.rigidBody; + shapeLibrary = rhs.shapeLibrary; + broadphase = rhs.broadphase; + transform = rhs.transform; + + copyShapes(rhs); + + return *this; + } + + SHCollider& SHCollider::operator=(SHCollider&& rhs) noexcept + { + if (!shapeLibrary) + { + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) + return *this; + } + + entityID = rhs.entityID; + flags = rhs.flags; + rigidBody = rhs.rigidBody; + shapeLibrary = rhs.shapeLibrary; + broadphase = rhs.broadphase; + transform = rhs.transform; + + copyShapes(rhs); + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + EntityID SHCollider::GetEntityID() const noexcept + { + return entityID; + } + + SHCollider::Type SHCollider::GetType() const noexcept + { + if (flags & COMPOSITE_FLAG) + return Type::COMPOSITE; + + if (flags & HULL_FLAG) + return Type::HULL; + + return Type::INVALID; + } + + bool SHCollider::IsActive() const noexcept + { + return flags & ACTIVE_FLAG; + } + + bool SHCollider::GetDebugDrawState() const noexcept + { + return flags & DRAW_FLAG; + } + + const SHTransform& SHCollider::GetTransform() const noexcept + { + return transform; + } + + const SHVec3& SHCollider::GetPosition() const noexcept + { + return transform.position; + } + + const SHQuaternion& SHCollider::GetOrientation() const noexcept + { + return transform.orientation; + } + + const SHVec3& SHCollider::GetScale() const noexcept + { + return transform.scale; + } + + const SHCollider::CollisionShapes& SHCollider::GetCollisionShapes() const noexcept + { + return shapes; + } + + SHCollisionShape* SHCollider::GetCollisionShape(int index) const + { + const int NUM_SHAPES = static_cast(shapes.size()); + + if (index < 0 || index >= NUM_SHAPES) + throw std::invalid_argument("Out-of-range index!"); + + return shapes[index]; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollider::SetType(Type type) noexcept + { + if (type == Type::COMPOSITE) + flags |= COMPOSITE_FLAG; + + if (type == Type::HULL) + flags |= HULL_FLAG; + } + + void SHCollider::SetIsActive(bool state) noexcept + { + const bool PREV_STATE = flags & ACTIVE_FLAG; + state ? flags |= ACTIVE_FLAG : flags &= ~(ACTIVE_FLAG); + + if (!broadphase) + return; + + for (auto* shape : shapes) + { + if (PREV_STATE) // Previously inactive + broadphase->Insert(shape->id, shape->ComputeAABB()); + else // Previously active + broadphase->Remove(shape->id); + } + } + + void SHCollider::SetDebugDrawState(bool state) noexcept + { + state ? flags |= DRAW_FLAG : flags &= ~(DRAW_FLAG); + + #ifdef SHEDITOR + + // Broadcast event for the Debug Draw system to catch + const SHColliderOnDebugDrawEvent EVENT_DATA + { + .entityID = entityID + , .debugDrawState = state + }; + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_DRAW_EVENT); + + #endif + } + + void SHCollider::SetRigidBody(SHRigidBody* rb) noexcept + { + rigidBody = rb; + } + + void SHCollider::SetTransform(const SHTransform& newTransform) noexcept + { + flags |= MOVED_FLAG; + transform = newTransform; + } + + void SHCollider::SetPosition(const SHVec3& newPosition) noexcept + { + flags |= MOVED_FLAG; + transform.position = newPosition; + } + + void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept + { + flags |= MOVED_FLAG; + transform.orientation = newOrientation; + } + + void SHCollider::SetScale(const SHVec3& newScale) noexcept + { + flags |= MOVED_FLAG; + transform.scale = newScale; + } + + void SHCollider::SetLibrary(SHCollisionShapeLibrary* factory) noexcept + { + shapeLibrary = factory; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHMatrix& SHCollider::ComputeTRS() noexcept + { + return transform.ComputeTRS(); + } + + void SHCollider::RemoveCollisionShape(int index) + { + if (!shapeLibrary) + { + SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add remove shape!", entityID) + return; + } + + const int NUM_SHAPES = static_cast(shapes.size()); + + if (index < 0 || index >= NUM_SHAPES) + throw std::invalid_argument("Out-of-range index!"); + + auto shape = shapes.begin(); + for (int i = 0; i < NUM_SHAPES; ++i, ++shape) + { + if (i == index) + break; + } + + const SHPhysicsColliderRemovedEvent EVENT_DATA + { + .entityID = entityID + , .colliderType = (*shape)->GetType() + , .colliderIndex = index + }; + + // Remove from broadphase + if (broadphase) + broadphase->Remove((*shape)->id); + + shapeLibrary->DestroyShape(*shape); + *shape = nullptr; + + // Remove the shape from the container to prevent accessing a nullptr + shape = shapes.erase(shape); + + + + // Broadcast Event for removing a shape + SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT); + + if (rigidBody) + rigidBody->ComputeMassData(); + + SHLOG_INFO_D("Removing Collision Shape {} from Entity {}", index, entityID) + } + + void SHCollider::Update() noexcept + { + for (auto* shape : shapes) + shape->Update(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Member Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollider::copyShapes(const SHCollider& rhsCollider) + { + for (const auto* shape : rhsCollider.shapes) + { + switch (shape->GetType()) + { + case SHCollisionShape::Type::BOX: + { + const SHBox* RHS_BOX = dynamic_cast(shape); + + const SHBoxCreateInfo BOX_CREATE_INFO + { + .Center = RHS_BOX->Center + , .Extents = RHS_BOX->Extents + , .RelativeExtents = RHS_BOX->relativeExtents + , .Orientation = RHS_BOX->Orientation + , .Scale = RHS_BOX->scale + }; + + const uint32_t NEW_INDEX = static_cast(shapes.size()); + const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; + + SHBox* box = shapeLibrary->CreateBox(NEW_SHAPE_ID, BOX_CREATE_INFO); + *box = *RHS_BOX; + + shapes.emplace_back(box); + + break; + } + case SHCollisionShape::Type::SPHERE: + { + const SHSphere* RHS_SPHERE = dynamic_cast(shape); + + const SHSphereCreateInfo SPHERE_CREATE_INFO + { + .Center = RHS_SPHERE->Center + , .Radius = RHS_SPHERE->Radius + , .RelativeRadius = RHS_SPHERE->relativeRadius + , .Scale = RHS_SPHERE->scale + }; + + const uint32_t NEW_INDEX = static_cast(shapes.size()); + const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; + + SHSphere* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); + *sphere = *RHS_SPHERE; + + shapes.emplace_back(sphere); + + break; + } + case SHCollisionShape::Type::CAPSULE: + { + break; + } + default: break; + } + } + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h new file mode 100644 index 00000000..a326215f --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHCollider.h @@ -0,0 +1,178 @@ +/**************************************************************************************** + * \file SHCollider.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Base Collider Class. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Headers +#include "ECS_Base/Entity/SHEntity.h" +#include "Math/Transform/SHTransform.h" +#include "Physics/Collision/Shapes/SHCollisionShapeLibrary.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + class SHRigidBody; + class SHAABBTree; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + /** + * @brief + * Base class for a collider. + * There are only two collider types supported by SHADE Engine: Composite & Hull + */ + class SH_API SHCollider + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHCollisionSpace; + friend struct SHManifold; + + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + enum class Type + { + COMPOSITE + , HULL + + , TOTAL + , INVALID = -1 + }; + + using CollisionShapes = std::vector; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Constructor for a collider. + * @param eid + * The entity this collider belongs to. + * @param worldTransform + * The world transform for the collider. Defaults to the identity transform. + * This is particularly important for composite colliders for offsets & relative sizes. + * @return + */ + SHCollider (EntityID eid, const SHTransform& worldTransform = SHTransform::Identity) noexcept; + SHCollider (const SHCollider& rhs) noexcept; + SHCollider (SHCollider&& rhs) noexcept; + virtual ~SHCollider () noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHCollider& operator=(const SHCollider& rhs) noexcept; + SHCollider& operator=(SHCollider&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] EntityID GetEntityID () const noexcept; + + [[nodiscard]] Type GetType () const noexcept; + [[nodiscard]] bool IsActive () const noexcept; + [[nodiscard]] bool GetDebugDrawState () const noexcept; + + [[nodiscard]] const SHTransform& GetTransform () const noexcept; + [[nodiscard]] const SHVec3& GetPosition () const noexcept; + [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; + [[nodiscard]] const SHVec3& GetScale () const noexcept; + + [[nodiscard]] const CollisionShapes& GetCollisionShapes () const noexcept; + [[nodiscard]] SHCollisionShape* GetCollisionShape (int index) const; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetType (Type type) noexcept; + void SetIsActive (bool state) noexcept; + void SetDebugDrawState (bool state) noexcept; + + void SetRigidBody (SHRigidBody* rb) noexcept; + + void SetTransform (const SHTransform& newTransform) noexcept; + void SetPosition (const SHVec3& newPosition) noexcept; + void SetOrientation (const SHQuaternion& newOrientation) noexcept; + void SetScale (const SHVec3& newScale) noexcept; + + void SetLibrary (SHCollisionShapeLibrary* factory) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Computes the TRS for the collider's transform + * @return + * The computed TRS. + */ + const SHMatrix& ComputeTRS() noexcept; + + /** + * @brief + * Removes a shape from the container. Removal reduces the size of the container. + * If removing all, perform removal from back to front. + * @param index + * The index of the shape to remove. + * @throws + * Invalid argument for out-of-range indices. + */ + void RemoveCollisionShape (int index); + + /** + * @brief + * Recomputes the transforms for all shapes in this composite collider. + */ + void Update () noexcept; + + protected: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr uint8_t COMPOSITE_FLAG = 1U << static_cast(Type::COMPOSITE); + static constexpr uint8_t HULL_FLAG = 1U << static_cast(Type::HULL); + static constexpr uint8_t ACTIVE_FLAG = 1U << 2; + static constexpr uint8_t DRAW_FLAG = 1U << 3; + static constexpr uint8_t MOVED_FLAG = 1U << 4; + + EntityID entityID; + uint8_t flags; // 0 0 0 hasMoved debugDraw active hull composite + SHRigidBody* rigidBody; + SHCollisionShapeLibrary* shapeLibrary; + SHAABBTree* broadphase; + SHTransform transform; + CollisionShapes shapes; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + void copyShapes (const SHCollider& rhsCollider); + }; + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp index e410ac36..c643e05e 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp @@ -40,7 +40,7 @@ namespace SHADE /* Public Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollisionSpace::AddCollider(SHCompositeCollider* collider) noexcept + void SHCollisionSpace::AddCollider(SHCollider* collider) noexcept { const bool INSERTED = colliders.emplace(collider->entityID, collider).second; if (!INSERTED) @@ -56,7 +56,7 @@ namespace SHADE broadphase.Insert(shape->id, shape->ComputeAABB()); } - void SHCollisionSpace::RemoveCollider(SHCompositeCollider* collider) noexcept + void SHCollisionSpace::RemoveCollider(SHCollider* collider) noexcept { colliders.erase(collider->entityID); @@ -85,14 +85,14 @@ namespace SHADE // Update any colliders that have moved for (auto& collider : colliders | std::views::values) { - const bool IS_ACTIVE = collider->active; - const bool HAS_MOVED = collider->hasMoved; + const bool IS_ACTIVE = collider->IsActive(); + const bool HAS_MOVED = collider->flags & SHCollider::MOVED_FLAG; if (!IS_ACTIVE || !HAS_MOVED) continue; // Clear hasMoved flag here - collider->hasMoved = false; + collider->flags &= ~SHCollider::MOVED_FLAG; // Update moved shapes in broadphase for (auto* shape : collider->shapes) @@ -114,7 +114,7 @@ namespace SHADE // Colliders without bodies are considered to be static bodies // This is specific to this engine because of Unity's stupid convention. const bool IS_IMPLICIT_STATIC = !collider->rigidBody; - const bool IS_ACTIVE = collider->active; + const bool IS_ACTIVE = collider->IsActive(); // Skip inactive colliders if (!IS_ACTIVE || IS_IMPLICIT_STATIC) @@ -246,7 +246,7 @@ namespace SHADE /* Private Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollisionSpace::broadphaseQuery(SHRigidBody::Type rigidBodyType, SHCompositeCollider* collider) noexcept + void SHCollisionSpace::broadphaseQuery(SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept { for (auto* shape : collider->shapes) { diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h index fa1f0d16..8607cf45 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h @@ -18,7 +18,7 @@ // Project Headers #include "Broadphase/SHDynamicAABBTree.h" #include "Physics/Dynamics/SHContactManager.h" -#include "SHCompositeCollider.h" +#include "SHCollider.h" #include "SHPhysicsRaycastResult.h" #include "CollisionTags/SHCollisionTags.h" #include "CollisionTags/SHCollisionTags.h" @@ -118,7 +118,7 @@ namespace SHADE * @param collider * A collider to add. Duplicates will be ignored. */ - void AddCollider (SHCompositeCollider* collider) noexcept; + void AddCollider (SHCollider* collider) noexcept; /** * @brief @@ -127,7 +127,7 @@ namespace SHADE * @param collider * A collider to remove. If a reference to it doesn't exist, it will be ignored. */ - void RemoveCollider (SHCompositeCollider* collider) noexcept; + void RemoveCollider (SHCollider* collider) noexcept; /** * @brief @@ -165,7 +165,7 @@ namespace SHADE SHCollisionShape* B = nullptr; }; - using Colliders = std::unordered_map; + using Colliders = std::unordered_map; using NarrowphaseBatch = std::unordered_map; /*---------------------------------------------------------------------------------*/ @@ -185,7 +185,7 @@ namespace SHADE // Broadphase helpers - void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCompositeCollider* collider) noexcept; + void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; // Narrowphase helpers diff --git a/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp index 97fef331..28b6aafe 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCollider.cpp + * \file SHCompositeCollider.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Base Collider Class. + * \brief Implementation for a Composite Collider Class. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -15,12 +15,10 @@ // Project Headers #include "Broadphase/SHDynamicAABBTree.h" -#include "Events/SHEvent.h" #include "Math/SHMathHelpers.h" #include "Physics/SHPhysicsEvents.h" #include "Physics/Dynamics/SHRigidBody.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -28,65 +26,18 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHCompositeCollider::SHCompositeCollider(EntityID eid, const SHTransform& worldTransform) noexcept - : entityID { eid } - , active { true } - , debugDraw { false } - , hasMoved { true } - , rigidBody { nullptr } - , shapeLibrary { nullptr } - , broadphase { nullptr } - , transform { worldTransform } - {} + : SHCollider( eid, worldTransform ) + { + flags |= COMPOSITE_FLAG; + } SHCompositeCollider::SHCompositeCollider(const SHCompositeCollider& rhs) noexcept - : entityID { rhs.entityID } - , active { rhs.active } - , debugDraw { rhs.debugDraw } - , hasMoved { rhs.hasMoved } - , rigidBody { rhs.rigidBody } - , shapeLibrary { rhs.shapeLibrary } - , broadphase { rhs.broadphase } - , transform { rhs.transform } - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) - return; - } - - copyShapes(rhs); - } + : SHCollider( rhs ) + {} SHCompositeCollider::SHCompositeCollider(SHCompositeCollider&& rhs) noexcept - : entityID { rhs.entityID } - , active { rhs.active } - , debugDraw { rhs.debugDraw } - , hasMoved { rhs.hasMoved } - , rigidBody { rhs.rigidBody } - , shapeLibrary { rhs.shapeLibrary } - , broadphase { rhs.broadphase } - , transform { rhs.transform } - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) - return; - } - - copyShapes(rhs); - } - - SHCompositeCollider::~SHCompositeCollider() noexcept - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add destroy collider!", entityID) - return; - } - - for (auto* shape : shapes) - shapeLibrary->DestroyShape(shape); - } + : SHCollider( rhs ) + {} /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ @@ -97,186 +48,22 @@ namespace SHADE if (this == &rhs) return *this; - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) - return *this; - } - - entityID = rhs.entityID; - active = rhs.active; - debugDraw = rhs.debugDraw; - hasMoved = rhs.hasMoved; - rigidBody = rhs.rigidBody; - shapeLibrary = rhs.shapeLibrary; - broadphase = rhs.broadphase; - transform = rhs.transform; - - copyShapes(rhs); + SHCollider::operator=(rhs); return *this; } SHCompositeCollider& SHCompositeCollider::operator=(SHCompositeCollider&& rhs) noexcept { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) - return *this; - } - - entityID = rhs.entityID; - active = rhs.active; - debugDraw = rhs.debugDraw; - hasMoved = rhs.hasMoved; - rigidBody = rhs.rigidBody; - shapeLibrary = rhs.shapeLibrary; - broadphase = rhs.broadphase; - transform = rhs.transform; - - copyShapes(rhs); + SHCollider::operator=(rhs); return *this; } - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - EntityID SHCompositeCollider::GetEntityID() const noexcept - { - return entityID; - } - - bool SHCompositeCollider::IsActive() const noexcept - { - return active; - } - - bool SHCompositeCollider::GetDebugDrawState() const noexcept - { - return debugDraw; - } - - const SHTransform& SHCompositeCollider::GetTransform() const noexcept - { - return transform; - } - - const SHVec3& SHCompositeCollider::GetPosition() const noexcept - { - return transform.position; - } - - const SHQuaternion& SHCompositeCollider::GetOrientation() const noexcept - { - return transform.orientation; - } - - const SHVec3& SHCompositeCollider::GetScale() const noexcept - { - return transform.scale; - } - - const SHCompositeCollider::CollisionShapes& SHCompositeCollider::GetCollisionShapes() const noexcept - { - return shapes; - } - - SHCollisionShape* SHCompositeCollider::GetCollisionShape(int index) const - { - const int NUM_SHAPES = static_cast(shapes.size()); - - if (index < 0 || index >= NUM_SHAPES) - throw std::invalid_argument("Out-of-range index!"); - - return shapes[index]; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCompositeCollider::SetIsActive(bool state) noexcept - { - if (active == state) - return; - - active = state; - - if (!broadphase) - return; - - for (auto* shape : shapes) - { - if (active) // Previously inactive - broadphase->Insert(shape->id, shape->ComputeAABB()); - else // Previously active - broadphase->Remove(shape->id); - } - } - - void SHCompositeCollider::SetDebugDrawState(bool state) noexcept - { - debugDraw = state; - - #ifdef SHEDITOR - - // Broadcast event for the Debug Draw system to catch - const SHColliderOnDebugDrawEvent EVENT_DATA - { - .entityID = entityID - , .debugDrawState = debugDraw - }; - - SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_DRAW_EVENT); - - #endif - } - - void SHCompositeCollider::SetRigidBody(SHRigidBody* rb) noexcept - { - rigidBody = rb; - } - - void SHCompositeCollider::SetTransform(const SHTransform& newTransform) noexcept - { - hasMoved = true; - transform = newTransform; - } - - void SHCompositeCollider::SetPosition(const SHVec3& newPosition) noexcept - { - hasMoved = true; - transform.position = newPosition; - } - - void SHCompositeCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept - { - hasMoved = true; - transform.orientation = newOrientation; - } - - void SHCompositeCollider::SetScale(const SHVec3& newScale) noexcept - { - hasMoved = true; - transform.scale = newScale; - } - - void SHCompositeCollider::SetFactory(SHCollisionShapeLibrary* factory) noexcept - { - shapeLibrary = factory; - } - /*-----------------------------------------------------------------------------------*/ /* Public Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - const SHMatrix& SHCompositeCollider::ComputeTransformTRS() noexcept - { - return transform.ComputeTRS(); - } - int SHCompositeCollider::AddSphereCollisionShape(float relativeRadius, const SHVec3& posOffset, const SHVec3& rotOffset) { if (!shapeLibrary) @@ -386,106 +173,4 @@ namespace SHADE } - void SHCompositeCollider::RemoveCollisionShape(int index) - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add remove shape!", entityID) - return; - } - - const int NUM_SHAPES = static_cast(shapes.size()); - - if (index < 0 || index >= NUM_SHAPES) - throw std::invalid_argument("Out-of-range index!"); - - auto shape = shapes.begin(); - for (int i = 0; i < NUM_SHAPES; ++i, ++shape) - { - if (i == index) - break; - } - - const SHPhysicsColliderRemovedEvent EVENT_DATA - { - .entityID = entityID - , .colliderType = (*shape)->GetType() - , .colliderIndex = index - }; - - // Remove from broadphase - if (broadphase) - broadphase->Remove((*shape)->id); - - shapeLibrary->DestroyShape(*shape); - *shape = nullptr; - - // Remove the shape from the container to prevent accessing a nullptr - shape = shapes.erase(shape); - - - - // Broadcast Event for removing a shape - SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT); - - if (rigidBody) - rigidBody->ComputeMassData(); - - SHLOG_INFO_D("Removing Collision Shape {} from Entity {}", index, entityID) - } - - void SHCompositeCollider::RecomputeShapes() noexcept - { - for (auto* shape : shapes) - shape->Update(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCompositeCollider::copyShapes(const SHCompositeCollider& rhsCollider) - { - for (const auto* shape : rhsCollider.shapes) - copyShape(shape); - } - - void SHCompositeCollider::copyShape(const SHCollisionShape* rhsShape) - { - switch (rhsShape->GetType()) - { - case SHCollisionShape::Type::BOX: - { - break; - } - case SHCollisionShape::Type::SPHERE: - { - const SHSphere* RHS_SPHERE = dynamic_cast(rhsShape); - - const SHSphereCreateInfo SPHERE_CREATE_INFO - { - .Center = RHS_SPHERE->Center - , .Radius = RHS_SPHERE->Radius - , .RelativeRadius = RHS_SPHERE->relativeRadius - , .Scale = RHS_SPHERE->scale - }; - - const uint32_t NEW_INDEX = static_cast(shapes.size()); - const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - - SHSphere* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); - *sphere = *RHS_SPHERE; - - shapes.emplace_back(sphere); - - break; - } - case SHCollisionShape::Type::CAPSULE: - { - break; - } - default: break; - } - } - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h index e5ce8609..8ab71186 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h +++ b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCollider.h + * \file SHCompositeCollider.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Base Collider Class. + * \brief Interface for a Composite Collider Class. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -11,63 +11,26 @@ #pragma once // Project Headers -#include "ECS_Base/Entity/SHEntity.h" -#include "Math/Transform/SHTransform.h" -#include "Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h" +#include "SHCollider.h" namespace SHADE { - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - class SHRigidBody; - class SHAABBTree; - - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - /** * @brief - * Base class for a collider. - * There are only two collider types supported by SHADE Engine: Composite & Hull + * Encapsulates the behaviour of a collider with composited shapes.
+ * Contains no data members but methods to add multiple shapes. */ - class SH_API SHCompositeCollider + class SH_API SHCompositeCollider : public SHCollider { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHCollisionSpace; - friend struct SHManifold; - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - using CollisionShapes = std::vector; - /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Constructor for a collider. - * @param eid - * The entity this collider belongs to. - * @param worldTransform - * The world transform for the collider. Defaults to the identity transform. - * This is particularly important for composite colliders for offsets & relative sizes. - * @return - */ - SHCompositeCollider (EntityID eid, const SHTransform& worldTransform = SHTransform::Identity) noexcept; - SHCompositeCollider (const SHCompositeCollider& rhs) noexcept; - SHCompositeCollider (SHCompositeCollider&& rhs) noexcept; - ~SHCompositeCollider () noexcept; + SHCompositeCollider(EntityID eid, const SHTransform& worldTransform = SHTransform::Identity) noexcept; + SHCompositeCollider(const SHCompositeCollider& rhs) noexcept; + SHCompositeCollider(SHCompositeCollider&& rhs) noexcept; + ~SHCompositeCollider () noexcept override = default; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ @@ -76,50 +39,10 @@ namespace SHADE SHCompositeCollider& operator=(const SHCompositeCollider& rhs) noexcept; SHCompositeCollider& operator=(SHCompositeCollider&& rhs) noexcept; - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] EntityID GetEntityID () const noexcept; - [[nodiscard]] bool IsActive () const noexcept; - [[nodiscard]] bool GetDebugDrawState () const noexcept; - - [[nodiscard]] const SHTransform& GetTransform () const noexcept; - [[nodiscard]] const SHVec3& GetPosition () const noexcept; - [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; - [[nodiscard]] const SHVec3& GetScale () const noexcept; - - [[nodiscard]] const CollisionShapes& GetCollisionShapes () const noexcept; - [[nodiscard]] SHCollisionShape* GetCollisionShape (int index) const; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetIsActive (bool state) noexcept; - void SetDebugDrawState (bool state) noexcept; - - void SetRigidBody (SHRigidBody* rb) noexcept; - - void SetTransform (const SHTransform& newTransform) noexcept; - void SetPosition (const SHVec3& newPosition) noexcept; - void SetOrientation (const SHQuaternion& newOrientation) noexcept; - void SetScale (const SHVec3& newScale) noexcept; - - void SetFactory (SHCollisionShapeLibrary* factory) noexcept; - /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Computes the TRS for the collider's transform - * @return - * The computed TRS. - */ - const SHMatrix& ComputeTransformTRS() noexcept; - /** * @brief * Adds a sphere collision shape. @@ -151,49 +74,7 @@ namespace SHADE int AddBoxCollisionShape (const SHVec3& relativeExtents, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero); // TODO: Add Capsule - - /** - * @brief - * Removes a shape from the container. Removal reduces the size of the container. - * If removing all, perform removal from back to front. - * @param index - * The index of the shape to remove. - * @throws - * Invalid argument for out-of-range indices. - */ - void RemoveCollisionShape (int index); - - /** - * @brief - * Recomputes the transforms for all shapes in this composite collider. - */ - void RecomputeShapes () noexcept; - - protected: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - EntityID entityID; - - bool active; - bool debugDraw; - bool hasMoved; - - SHRigidBody* rigidBody; - SHCollisionShapeLibrary* shapeLibrary; - SHAABBTree* broadphase; - - SHTransform transform; - - CollisionShapes shapes; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - void copyShapes (const SHCompositeCollider& rhsCollider); - void copyShape (const SHCollisionShape* rhsShape); }; + } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp similarity index 99% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.cpp rename to SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp index b46c92da..efe40f7d 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.cpp +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp @@ -16,7 +16,7 @@ // Project Headers #include "Math/SHMathHelpers.h" #include "Math/SHMatrix.h" -#include "Physics/Collision/SHCompositeCollider.h" +#include "Physics/Collision/SHCollider.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h similarity index 99% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.h rename to SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h index bf250726..044af40f 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHBox.h +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h @@ -47,6 +47,7 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ + friend class SHCollider; friend class SHCompositeCollider; friend class SHCollision; friend class SHCollisionShapeLibrary; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.cpp similarity index 99% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp rename to SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.cpp index f089c02c..dfbed047 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.cpp @@ -14,7 +14,7 @@ #include "SHCollisionShape.h" // Project Headers -#include "Physics/Collision/SHCompositeCollider.h" +#include "Physics/Collision/SHCollider.h" #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Reflection/SHReflectionMetadata.h" #include "Tools/Utilities/SHUtilities.h" diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.h similarity index 98% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h rename to SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.h index b4ce9529..3e57b74a 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShape.h +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.h @@ -41,6 +41,7 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ + friend class SHCollider; friend class SHCompositeCollider; friend class SHColliderComponent; friend class SHCollisionShapeLibrary; @@ -191,7 +192,7 @@ namespace SHADE SHCollisionShapeID id; - SHCompositeCollider* collider; // The collider it belongs to. + SHCollider* collider; // The collider it belongs to. SHCollisionTag* collisionTag; SHPhysicsMaterial material; // TODO: Change to pointer once instancing is supported diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.h similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.h rename to SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.h diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.hpp similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeID.hpp rename to SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.hpp diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.cpp rename to SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.h similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h rename to SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.h diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.cpp similarity index 66% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp rename to SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.cpp index a2aef745..8cccd837 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.cpp +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.cpp @@ -25,30 +25,14 @@ namespace SHADE {} SHConvexPolyhedron::SHConvexPolyhedron(const SHConvexPolyhedron& rhs) noexcept - : SHCollisionShape (rhs.id, rhs.GetType()) - , halfEdgeStructure { nullptr } - { - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; - } + : SHCollisionShape ( rhs ) + , halfEdgeStructure { rhs.halfEdgeStructure } + {} SHConvexPolyhedron::SHConvexPolyhedron(SHConvexPolyhedron&& rhs) noexcept - : SHCollisionShape (rhs.id, rhs.GetType()) - , halfEdgeStructure { nullptr } - { - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; - } + : SHCollisionShape ( rhs ) + , halfEdgeStructure { rhs.halfEdgeStructure } + {} /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ @@ -59,13 +43,7 @@ namespace SHADE if (this == &rhs) return *this; - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + SHCollisionShape::operator=(rhs); // Local Properties halfEdgeStructure = rhs.halfEdgeStructure; @@ -75,13 +53,7 @@ namespace SHADE SHConvexPolyhedron& SHConvexPolyhedron::operator=(SHConvexPolyhedron&& rhs) noexcept { - material = rhs.material; - collider = rhs.collider; - transform = rhs.transform; - rotationOffset = rhs.rotationOffset; - flags = rhs.flags; - // Since all collision tags are taken from the matrix, we do not need to do a deep copy here. - collisionTag = rhs.collisionTag; + SHCollisionShape::operator=(rhs); // Local Properties halfEdgeStructure = rhs.halfEdgeStructure; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h similarity index 99% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h rename to SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h index cdc7f45f..dd4595d1 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHConvexPolyhedron.h +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h @@ -31,7 +31,7 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ - friend class SHCompositeCollider; + friend class SHCollider; friend class SHCollision; friend class SHCollisionShapeLibrary; diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.cpp similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.cpp rename to SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.cpp diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.h similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHHalfEdgeStructure.h rename to SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.h diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.cpp similarity index 98% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.cpp rename to SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.cpp index 04f8718e..5708bf91 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.cpp +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.cpp @@ -16,7 +16,7 @@ // Project Headers #include "Math/SHMathHelpers.h" #include "Math/SHMatrix.h" -#include "Physics/Collision/SHCompositeCollider.h" +#include "Physics/Collision/SHCollider.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.h similarity index 99% rename from SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.h rename to SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.h index c4e97fe0..b874d2fe 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionShapes/SHSphere.h +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.h @@ -46,6 +46,7 @@ namespace SHADE /* Friends */ /*---------------------------------------------------------------------------------*/ + friend class SHCollider; friend class SHCompositeCollider; friend class SHCollision; friend class SHCollisionShapeLibrary; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index d5c9b029..3595ec99 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -178,7 +178,7 @@ namespace SHADE rigidBody.collider->SetPosition(rigidBody.motionState.position); rigidBody.collider->SetOrientation(rigidBody.motionState.orientation); - rigidBody.collider->RecomputeShapes(); + rigidBody.collider->Update(); } } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index 9688caa2..d76b79b8 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -17,7 +17,7 @@ #include #include -#include "Physics/Collision/SHCompositeCollider.h" +#include "Physics/Collision/SHCollider.h" #include "Tools/Logger/SHLogger.h" namespace SHADE @@ -296,7 +296,7 @@ namespace SHADE /* Setter Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHRigidBody::SetCollider(SHCompositeCollider* c) noexcept + void SHRigidBody::SetCollider(SHCollider* c) noexcept { collider = c; } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h index 44ae3f48..3c869ad2 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h @@ -22,7 +22,7 @@ namespace SHADE /* Forward Declarations */ /*-------------------------------------------------------------------------------------*/ - class SHCompositeCollider; + class SHCollider; /*-------------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -113,7 +113,7 @@ namespace SHADE /* Setter Functions */ /*-----------------------------------------------------------------------------------*/ - void SetCollider (SHCompositeCollider* c) noexcept; + void SetCollider (SHCollider* c) noexcept; /** * @brief @@ -229,7 +229,7 @@ namespace SHADE // The entityID here is only meant for linking with the actual component in the engine. EntityID entityID; - SHCompositeCollider* collider; + SHCollider* collider; Type bodyType; diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp index 07e08b78..2353e3f3 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp @@ -13,6 +13,9 @@ // Primary Header #include "SHPhysicsObject.h" +#include "Physics/Collision/SHCollider.h" +#include "Physics/Collision/SHCompositeCollider.h" + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -111,7 +114,7 @@ namespace SHADE collider->SetRigidBody(nullptr); } - SHCompositeCollider* SHPhysicsObject::CreateCollider(const SHTransform& transform) + SHCollider* SHPhysicsObject::CreateCompositeCollider(const SHTransform& transform) { if (collider) { @@ -148,13 +151,13 @@ namespace SHADE /* Private Member Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsObject::deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCompositeCollider* rhsCollider) + void SHPhysicsObject::deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCollider* rhsCollider) { if (rhsRigidBody) rigidBody = new SHRigidBody{ *rhsRigidBody }; if (rhsCollider) - collider = new SHCompositeCollider { *rhsCollider }; + collider = new SHCollider { *rhsCollider }; } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h index f1ee042d..9736f8d0 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h @@ -11,7 +11,7 @@ #pragma once // Project Headers -#include "Physics/Collision/SHCompositeCollider.h" +#include "Physics/Collision/SHCollider.h" #include "Physics/Dynamics/SHRigidBody.h" namespace SHADE @@ -33,7 +33,7 @@ namespace SHADE EntityID entityID = MAX_EID; SHRigidBody* rigidBody = nullptr; - SHCompositeCollider* collider = nullptr; + SHCollider* collider = nullptr; /*-----------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -61,7 +61,7 @@ namespace SHADE * @return * True if the physics object has neither a rigid body nor a collider. */ - [[nodiscard]] bool IsEmpty () const noexcept; + [[nodiscard]] bool IsEmpty () const noexcept; /** * @brief @@ -72,13 +72,13 @@ namespace SHADE * Pointer to the rigid body that was created. The memory of this rigid body is managed * by the physics object itself. */ - SHRigidBody* CreateRigidBody (SHRigidBody::Type bodyType); + SHRigidBody* CreateRigidBody (SHRigidBody::Type bodyType); /** * @brief * Destroys the rigid body of this physics object and frees the memory. */ - void DestroyRigidBody () noexcept; + void DestroyRigidBody () noexcept; /** * @brief @@ -91,19 +91,19 @@ namespace SHADE * Pointer to the collider that was created. The memory of this collider is managed * by the physics object itself. */ - SHCompositeCollider* CreateCollider (const SHTransform& transform = SHTransform::Identity); + SHCollider* CreateCompositeCollider (const SHTransform& transform = SHTransform::Identity); /** * @brief * Destroys the collider of this physics object and frees the memory. */ - void DestroyCollider () noexcept; + void DestroyCollider () noexcept; private: /*-----------------------------------------------------------------------------------*/ /* Member Functions */ /*-----------------------------------------------------------------------------------*/ - void deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCompositeCollider* rhsCollider); + void deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCollider* rhsCollider); }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp index 81fb25e1..956f8078 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp @@ -98,7 +98,7 @@ namespace SHADE destroyPhysicsObject(entityID); } - void SHPhysicsObjectManager::AddCollider(EntityID entityID) noexcept + void SHPhysicsObjectManager::AddCollider(EntityID entityID, SHCollider::Type type) noexcept { SHPhysicsObject* physicsObject = ensurePhysicsObject(entityID); @@ -116,11 +116,15 @@ namespace SHADE } // Create a new composite collider in the physics object - physicsObject->CreateCollider(worldTransform); - physicsObject->collider->SetFactory(&shapeLibrary); + if (type == SHCollider::Type::COMPOSITE) + physicsObject->CreateCompositeCollider(worldTransform); + + // TODO: Hull collider + + physicsObject->collider->SetLibrary(&shapeLibrary); // Link with the component - colliderComponent->SetCollider(physicsObject->collider); + colliderComponent->SetCollider(dynamic_cast(physicsObject->collider)); } void SHPhysicsObjectManager::RemoveCollider(EntityID entityID) noexcept diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h index 11818316..8b4e79c1 100644 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h +++ b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h @@ -14,7 +14,7 @@ // Project Headers #include "SHPhysicsObject.h" -#include "Physics/Collision/CollisionShapes/SHCollisionShapeLibrary.h" +#include "Physics/Collision/Shapes/SHCollisionShapeLibrary.h" namespace SHADE { @@ -77,7 +77,7 @@ namespace SHADE * @param entityID * The entity to link the new collider to. */ - void AddCollider (EntityID entityID) noexcept; + void AddCollider (EntityID entityID, SHCollider::Type type) noexcept; /** * @brief diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp index 5569c713..a36fb730 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp @@ -13,11 +13,6 @@ // Primary Header #include "SHColliderComponent.h" -// Project Headers -#include "ECS_Base/Managers/SHSystemManager.h" -#include "Math/SHMathHelpers.h" -#include "Physics/System/SHPhysicsSystem.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -37,7 +32,7 @@ namespace SHADE return collider; } - const SHCompositeCollider::CollisionShapes* const SHColliderComponent::GetCollisionShapes() const noexcept + const SHCollider::CollisionShapes* const SHColliderComponent::GetCollisionShapes() const noexcept { if (!collider) return nullptr; diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h index 3c8e6342..39552949 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h @@ -54,8 +54,8 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHCompositeCollider* const GetCollider () const noexcept; - [[nodiscard]] const SHCompositeCollider::CollisionShapes* const GetCollisionShapes() const noexcept; + [[nodiscard]] SHCompositeCollider* const GetCollider () const noexcept; + [[nodiscard]] const SHCollider::CollisionShapes* const GetCollisionShapes() const noexcept; [[nodiscard]] SHCollisionShape* const GetCollisionShape (int index) const; // Required for serialisation diff --git a/SHADE_Engine/src/Physics/SHPhysicsEvents.h b/SHADE_Engine/src/Physics/SHPhysicsEvents.h index 2f78b6ee..a192ec3f 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsEvents.h +++ b/SHADE_Engine/src/Physics/SHPhysicsEvents.h @@ -11,7 +11,7 @@ #pragma once // Project Headers -#include "Collision/CollisionShapes/SHCollisionShape.h" +#include "Collision/Shapes/SHCollisionShape.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp index 17f3f83d..3f2de93f 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp @@ -83,7 +83,7 @@ namespace SHADE physicsObject.collider->SetOrientation(WORLD_ROT); physicsObject.collider->SetScale(WORLD_SCL); - physicsObject.collider->RecomputeShapes(); + physicsObject.collider->Update(); } } } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index 1b1055be..ae25216f 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -122,7 +122,7 @@ namespace SHADE return onColliderDrawEvent.get()->handle; } - void SHPhysicsDebugDrawSystem::drawCollider(SHDebugDrawSystem* debugDrawSystem, const SHCompositeCollider& collider) noexcept + void SHPhysicsDebugDrawSystem::drawCollider(SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept { for (const auto* SHAPE : collider.GetCollisionShapes()) { diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index c284792f..336dff0c 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -18,9 +18,9 @@ #include "ECS_Base/System/SHSystemRoutine.h" #include "Events/SHEvent.h" #include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" -#include "Physics/Collision/SHCompositeCollider.h" +#include "Physics/Collision/SHCollider.h" #include "Physics/Collision/SHPhysicsRaycastResult.h" -#include "Physics/Collision/CollisionShapes/SHSphere.h" +#include "Physics/Collision/Shapes/SHSphere.h" namespace SHADE @@ -138,7 +138,7 @@ namespace SHADE SHEventHandle onColliderDraw(SHEventPtr onColliderDrawEvent); - static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCompositeCollider& collider) noexcept; + static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; static void drawRaycast (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept; }; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index f9e7a3cd..97b555af 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -308,6 +308,8 @@ namespace SHADE const bool IS_RIGID_BODY = ADDED_ID == RIGID_BODY_COMPONENT_ID; const bool IS_COLLIDER = ADDED_ID == COLLIDER_COMPONENT_ID; + // TODO: Hull Collider + // Check if its a physics component const bool IS_PHYSICS_COMPONENT = IS_RIGID_BODY || IS_COLLIDER; if (!IS_PHYSICS_COMPONENT) @@ -329,7 +331,7 @@ namespace SHADE if (IS_COLLIDER) { - physicsObjectManager.AddCollider(EID); + physicsObjectManager.AddCollider(EID, SHCollider::Type::COMPOSITE); if (collisionSpace) { diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index 18d8d63f..6f3b2336 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -1,7 +1,6 @@ #pragma once #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" -#include "Physics/Collision/CollisionShapes/SHSphere.h" #include "Resource/SHResourceManager.h" #include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec3.h" @@ -12,6 +11,7 @@ #include "Physics/Interface/SHColliderComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHFont.h" +#include "Physics/Collision/SHCompositeCollider.h" #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" namespace YAML @@ -261,14 +261,14 @@ namespace YAML return false; auto* collider = rhs.GetCollider(); - switch (colliderType) { - case SHCollisionShape::Type::SPHERE: collider->AddSphereCollisionShape(1.0f); break; - case SHCollisionShape::Type::BOX: collider->AddBoxCollisionShape(SHVec3::One); break; - case SHCollisionShape::Type::CAPSULE: break; - default:; + case SHCollisionShape::Type::SPHERE: collider->AddSphereCollisionShape(1.0f); break; + case SHCollisionShape::Type::BOX: collider->AddBoxCollisionShape(SHVec3::One); break; + case SHCollisionShape::Type::CAPSULE: break; + default:; } + YAML::convert::decode(colliderNode, *collider->GetCollisionShape(numColliders++)); } } From c484a088fdc13471a1e16d057dae61409ebeec76 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 5 Jan 2023 01:12:25 +0800 Subject: [PATCH 081/164] Added first half of Gauss Map Optimised SAT --- Assets/Scenes/PhysicsSandbox.shade | 88 ++++++++++- .../Collision/Narrowphase/SHCollision.h | 25 ++- .../Narrowphase/SHConvexVsConvex.cpp | 144 ++++++++++++++++-- .../src/Physics/Collision/Shapes/SHBox.cpp | 24 +++ .../src/Physics/Collision/Shapes/SHBox.h | 11 +- .../Shapes/SHCollisionShapeLibrary.cpp | 8 +- .../Collision/Shapes/SHConvexPolyhedron.h | 20 ++- 7 files changed, 291 insertions(+), 29 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 6dea778f..d0985215 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,7 +45,7 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 4, z: 5} + Position: {x: 1, y: 10, z: 3} Pitch: 0 Yaw: 0 Roll: 0 @@ -62,7 +62,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.45715916, y: 7, z: 0.227711335} + Translate: {x: -1.45715916, y: 7.37748241, z: 0.227711335} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -77,7 +77,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: false + Freeze Position Y: true Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -273,4 +273,86 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true + Scripts: ~ +- EID: 7 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0.899999976, y: 10, z: 0} + Rotate: {x: -0, y: 0, z: 0.785398185} + Scale: {x: 1, y: 1, z: 1} + IsActive: true + RigidBody Component: + Type: Dynamic + Auto Mass: false + Mass: 1 + Drag: 0.00999999978 + Angular Drag: 0.00999999978 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false + Freeze Position Y: true + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: false + Colliders: + - Is Trigger: true + Collision Tag: 1 + Type: Box + Half Extents: {x: 1, y: 1, z: 1} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true + Scripts: ~ +- EID: 8 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: -0.5, y: 10, z: 0} + Rotate: {x: -0, y: 0.785398185, z: -0} + Scale: {x: 1, y: 1, z: 1} + IsActive: true + RigidBody Component: + Type: Dynamic + Auto Mass: false + Mass: 1 + Drag: 0.00999999978 + Angular Drag: 0.00999999978 + Use Gravity: true + Gravity Scale: 1 + Interpolate: true + Sleeping Enabled: true + Freeze Position X: false + Freeze Position Y: true + Freeze Position Z: false + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false + IsActive: true + Collider Component: + DrawColliders: false + Colliders: + - Is Trigger: true + Collision Tag: 1 + Type: Box + Half Extents: {x: 1, y: 1, z: 1} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Rotation Offset: {x: 0, y: 0, z: 0} + IsActive: true Scripts: ~ \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 0ce1f688..8a465d4a 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -13,6 +13,7 @@ // Project Headers #include "Physics/Collision/Contacts/SHManifold.h" #include "Physics/Collision/Contacts/SHCollisionKey.h" +#include "Physics/Collision/Shapes/SHHalfEdgeStructure.h" namespace SHADE @@ -72,11 +73,18 @@ namespace SHADE struct FaceQuery { - bool colliding = false; + bool colliding = false; // Allows for early out int32_t closestFace = -1; float bestDistance = std::numeric_limits::lowest(); }; + struct EdgeQuery + { + int32_t halfEdgeA = -1; + int32_t halfEdgeB = -1; + float bestDistance = std::numeric_limits::lowest(); + }; + /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ @@ -93,14 +101,19 @@ namespace SHADE // Convex VS Convex - static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; + static FaceQuery queryFaceDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + static EdgeQuery queryEdgeDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + + static bool buildMinkowskiFace (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; + static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; + static float distanceBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; /* * TODO: - * static FaceQuery queryFaceDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; - * static EdgeQuery queryEdgeDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; - * static bool buildMinkowskiFace (const SHConvexPolyhedron::HalfEdge& edgeA, const SHConvexPolyhedron::HalfEdge& edgeB) noexcept; - * static float distanceBetweenEdges(const SHConvexPolyhedron::HalfEdge& edgeA, const SHConvexPolyhedron::HalfEdge& edgeB, SHConvexPolyhedron& poly) noexcept; + * + * + * + * * static int32_t findIncidentFace (SHConvexPolyhedron& poly, const SHVec3& normal) noexcept; * static uint32_t clip [sutherland-hodgemann clipping] * diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index 6753c453..5eedc2c3 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -15,9 +15,9 @@ // Project Headers #include "Math/SHMathHelpers.h" +#include "Math/Geometry/SHPlane.h" #include "Physics/Collision/Shapes/SHConvexPolyhedron.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -26,15 +26,22 @@ namespace SHADE bool SHCollision::ConvexVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - /* - * TODO: - * - * 1. Query face directiosn of a to b. Exit early if separation found. - * 2. query face directions of b to a. Exit early if separation found. - * 3. Query edge directions of a & b. Exit early if separation found. - */ + const SHConvexPolyhedron& POLY_A = dynamic_cast(A); + const SHConvexPolyhedron& POLY_B = dynamic_cast(B); - return false; + const FaceQuery FACE_QUERY_A = queryFaceDirections(POLY_A, POLY_B); + if (FACE_QUERY_A.bestDistance > 0.0f) + return false; + + const FaceQuery FACE_QUERY_B = queryFaceDirections(POLY_B, POLY_A); + if (FACE_QUERY_B.bestDistance > 0.0f) + return false; + + const EdgeQuery EDGE_QUERY = queryEdgeDirections(POLY_A, POLY_B); + if (EDGE_QUERY.bestDistance > 0.0f) + return false; + + return true; } bool SHCollision::ConvexVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept @@ -61,4 +68,123 @@ namespace SHADE return false; } + /*-----------------------------------------------------------------------------------*/ + /* Private Member Functions Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollision::FaceQuery SHCollision::queryFaceDirections(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept + { + FaceQuery faceQuery; + + const int32_t NUM_FACES = A.GetFaceCount(); + for (const int32_t i : std::views::iota(0, NUM_FACES)) + { + const SHHalfEdgeStructure::Face& FACE_A = A.GetFace(i); + const SHVec3 NORMAL_A = A.GetNormal(i); + + // Smallest penetration is point closest to face normal + const SHVec3 SUPPORT_POINT = B.FindSupportPoint(-NORMAL_A); + + const SHVec3 VERTEX_A = A.GetVertex(FACE_A.vertexIndices[0].index); + + const SHPlane FACE_PLANE { VERTEX_A, NORMAL_A }; + const float DISTANCE = FACE_PLANE.SignedDistance(SUPPORT_POINT); + + if (DISTANCE > faceQuery.bestDistance) + { + faceQuery.bestDistance = DISTANCE; + faceQuery.closestFace = i; + } + } + + return faceQuery; + } + + SHCollision::EdgeQuery SHCollision::queryEdgeDirections(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept + { + EdgeQuery edgeQuery; + + const int32_t EDGE_COUNT_A = A.GetHalfEdgeCount(); + const int32_t EDGE_COUNT_B = B.GetHalfEdgeCount(); + + for (int32_t i = 0; i < EDGE_COUNT_A; i += 2) + { + for (int32_t j = 0; j < EDGE_COUNT_B; j += 2) + { + const bool IS_MINKOWSKI_FACE = buildMinkowskiFace(A, B, i, j); + if (!IS_MINKOWSKI_FACE) + continue; + + const float SEPARATION = distanceBetweenEdges(A, B, i, j); + if (SEPARATION > edgeQuery.bestDistance) + { + edgeQuery.bestDistance = SEPARATION; + edgeQuery.halfEdgeA = i; + edgeQuery.halfEdgeB = j; + } + } + } + + return edgeQuery; + } + + bool SHCollision::buildMinkowskiFace(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept + { + // Get Half Edge from both polygons + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); + const SHHalfEdgeStructure::HalfEdge& TWIN_EDGE_A = A.GetHalfEdge(HALF_EDGE_A.twinEdgeIndex); + + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(edgeB); + const SHHalfEdgeStructure::HalfEdge& TWIN_EDGE_B = B.GetHalfEdge(HALF_EDGE_B.twinEdgeIndex); + + + + // Get normals from face and twin edge face + const SHVec3 NA = A.GetNormal(HALF_EDGE_A.faceIndex); + const SHVec3 NB = A.GetNormal(TWIN_EDGE_A.faceIndex); + const SHVec3 NC = B.GetNormal(HALF_EDGE_B.faceIndex); + const SHVec3 ND = B.GetNormal(TWIN_EDGE_B.faceIndex); + + return isMinkowskiFace(NA, NB, -NC, -ND); + } + + bool SHCollision::isMinkowskiFace(const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept + { + const SHVec3 BXA = SHVec3::Cross(b, a); + const SHVec3 DXC = SHVec3::Cross(d, c); + + const float CBA = SHVec3::Dot(c, BXA); + const float DBA = SHVec3::Dot(d, BXA); + const float ADC = SHVec3::Dot(a, DXC); + const float BDC = SHVec3::Dot(b, DXC); + + return CBA * DBA < 0.0f && ADC * BDC < 0.0f && CBA * BDC > 0.0f; + } + + float SHCollision::distanceBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept + { + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(edgeB); + + const SHVec3 HEAD_A = A.GetVertex(HALF_EDGE_A.headVertexIndex); + const SHVec3 TAIL_A = A.GetVertex(HALF_EDGE_A.tailVertexIndex); + const SHVec3 HEAD_B = B.GetVertex(HALF_EDGE_B.headVertexIndex); + const SHVec3 TAIL_B = B.GetVertex(HALF_EDGE_B.tailVertexIndex); + + const SHVec3 DIR_A = SHVec3::Normalise(HEAD_A - TAIL_A); + const SHVec3 DIR_B = SHVec3::Normalise(HEAD_B - TAIL_B); + + // Check if the edges are parallel (abs dot product is 1) + const float DOT_BETWEEN_EDGES = std::fabs(SHVec3::Dot(DIR_A, DIR_B)); + if (SHMath::CompareFloat(DOT_BETWEEN_EDGES, 1.0f)) + return std::numeric_limits::lowest(); + + SHVec3 normal = SHVec3::Cross(DIR_A, DIR_B); + // Flip normal if need to ( A -> B) + if (SHVec3::Dot(normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) + normal = -normal; + + return SHVec3::Dot(normal, HEAD_B - HEAD_A); + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp index efe40f7d..8022e487 100644 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp @@ -291,5 +291,29 @@ namespace SHADE return SHAABB{ CENTROID, HALF_EXTENTS }; } + SHVec3 SHBox::FindSupportPoint(const SHVec3& direction) const noexcept + { + float bestDistance = std::numeric_limits::lowest(); + + + SHVec3 vertices[NUM_VERTICES]; + GetCorners(vertices); + + // No reason to put the center really.. + SHVec3 bestPoint = Center; + for (auto& vertex : vertices) + { + const float PROJECTION = SHVec3::Dot(vertex, direction); + + if (PROJECTION > bestDistance) + { + bestDistance = PROJECTION; + bestPoint = vertex; + } + } + + return bestPoint; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h index 044af40f..480b2501 100644 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h @@ -109,11 +109,12 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - void Update () noexcept override; - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; - [[nodiscard]] SHMatrix GetTRS () const noexcept override; - [[nodiscard]] SHAABB ComputeAABB () const noexcept override; + void Update () noexcept override; + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; + [[nodiscard]] SHMatrix GetTRS () const noexcept override; + [[nodiscard]] SHAABB ComputeAABB () const noexcept override; + [[nodiscard]] SHVec3 FindSupportPoint (const SHVec3& direction) const noexcept override; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp index e0202d67..c70728f3 100644 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp @@ -150,11 +150,11 @@ namespace SHADE const int32_t FACE_VERTICES[NUM_FACES][NUM_VERTICES_PER_FACE] { { 0, 1, 2, 3 } - , { 1, 5, 6, 2 } + , { 5, 6, 2, 1 } , { 5, 4, 7, 6 } - , { 4, 0, 3, 7 } - , { 3, 2, 6, 7 } - , { 4, 5, 1, 0 } + , { 0, 3, 7, 4 } + , { 2, 6, 7, 3 } + , { 5, 1, 0, 4 } }; for (int i = 0; i < NUM_FACES; ++i) diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h index dd4595d1..3944c556 100644 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h +++ b/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h @@ -63,8 +63,24 @@ namespace SHADE [[nodiscard]] int32_t GetHalfEdgeCount () const noexcept; [[nodiscard]] const SHHalfEdgeStructure::HalfEdge& GetHalfEdge (int index) const; - [[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0; - [[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0; + // Virtual Methods + + [[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0; + [[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0; + + /*---------------------------------------------------------------------------------*/ + /* Member Functions */ + /*---------------------------------------------------------------------------------*/ + + /** + * @brief + * Finds the most extreme point on the polygon in a given direction. + * @param direction + * The direction to find the support point in. + * @return + * The most extreme vertex in the given direction. + */ + [[nodiscard]] virtual SHVec3 FindSupportPoint (const SHVec3& direction) const noexcept = 0; protected: /*---------------------------------------------------------------------------------*/ From 68e11ba48ed0d27d7ea78fc180738b787920acd9 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 5 Jan 2023 13:42:17 +0800 Subject: [PATCH 082/164] Added edge vs edge contacts for convex polyhedron collisions --- Assets/Scenes/PhysicsSandbox.shade | 26 ++--- .../Collision/Narrowphase/SHCollision.h | 11 +- .../Narrowphase/SHConvexVsConvex.cpp | 100 ++++++++++++++++++ 3 files changed, 119 insertions(+), 18 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index d0985215..b405fac2 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,9 +45,9 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 1, y: 10, z: 3} + Position: {x: -0.5, y: 10, z: -3} Pitch: 0 - Yaw: 0 + Yaw: 180 Roll: 0 Width: 1920 Height: 1080 @@ -84,7 +84,7 @@ Freeze Rotation Z: false IsActive: true Collider Component: - DrawColliders: false + DrawColliders: true Colliders: - Is Trigger: false Collision Tag: 1 @@ -95,7 +95,7 @@ Density: 1 Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} - - Is Trigger: false + - Is Trigger: true Collision Tag: 1 Type: Sphere Radius: 0.5 @@ -280,9 +280,9 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0.899999976, y: 10, z: 0} - Rotate: {x: -0, y: 0, z: 0.785398185} - Scale: {x: 1, y: 1, z: 1} + Translate: {x: 0.524352431, y: 11.1989822, z: 0.0463808179} + Rotate: {x: -0, y: 0.785398066, z: 0.785398185} + Scale: {x: 0.999999821, y: 0.999999821, z: 0.999999881} IsActive: true RigidBody Component: Type: Dynamic @@ -295,7 +295,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: true + Freeze Position Y: false Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -304,7 +304,7 @@ Collider Component: DrawColliders: false Colliders: - - Is Trigger: true + - Is Trigger: false Collision Tag: 1 Type: Box Half Extents: {x: 1, y: 1, z: 1} @@ -322,13 +322,13 @@ Components: Transform Component: Translate: {x: -0.5, y: 10, z: 0} - Rotate: {x: -0, y: 0.785398185, z: -0} + Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: - Type: Dynamic + Type: Static Auto Mass: false - Mass: 1 + Mass: .inf Drag: 0.00999999978 Angular Drag: 0.00999999978 Use Gravity: true @@ -345,7 +345,7 @@ Collider Component: DrawColliders: false Colliders: - - Is Trigger: true + - Is Trigger: false Collision Tag: 1 Type: Box Half Extents: {x: 1, y: 1, z: 1} diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 8a465d4a..57777cca 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -101,12 +101,13 @@ namespace SHADE // Convex VS Convex - static FaceQuery queryFaceDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; - static EdgeQuery queryEdgeDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + static FaceQuery queryFaceDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + static EdgeQuery queryEdgeDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; - static bool buildMinkowskiFace (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; - static float distanceBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; + static bool buildMinkowskiFace (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; + static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; + static float distanceBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; + static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB, SHVec3& normal) noexcept; /* * TODO: diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index 5eedc2c3..0f899443 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -17,6 +17,7 @@ #include "Math/SHMathHelpers.h" #include "Math/Geometry/SHPlane.h" #include "Physics/Collision/Shapes/SHConvexPolyhedron.h" +#include "Tools/Utilities/SHUtilities.h" namespace SHADE { @@ -65,6 +66,55 @@ namespace SHADE * */ + const SHConvexPolyhedron& POLY_A = dynamic_cast(A); + const SHConvexPolyhedron& POLY_B = dynamic_cast(B); + + const FaceQuery FACE_QUERY_A = queryFaceDirections(POLY_A, POLY_B); + if (FACE_QUERY_A.bestDistance > 0.0f) + return false; + + const FaceQuery FACE_QUERY_B = queryFaceDirections(POLY_B, POLY_A); + if (FACE_QUERY_B.bestDistance > 0.0f) + return false; + + const EdgeQuery EDGE_QUERY = queryEdgeDirections(POLY_A, POLY_B); + if (EDGE_QUERY.bestDistance > 0.0f) + return false; + + const FaceQuery& BEST_FACE_QUERY = FACE_QUERY_A.bestDistance > FACE_QUERY_B.bestDistance ? FACE_QUERY_A : FACE_QUERY_B; + + // If an edge pair contains the closest distance,vwe ignore any face queries and find the closest points on + // each edge and use that as the contact point. + // Artificially increase the depth of this collision by a small tolerance to tend towards picking a face query in the next frame. + if (EDGE_QUERY.bestDistance > BEST_FACE_QUERY.bestDistance) + { + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = POLY_A.GetHalfEdge(EDGE_QUERY.halfEdgeA); + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = POLY_B.GetHalfEdge(EDGE_QUERY.halfEdgeB); + + // In this scenario, we only have one contact + + SHContactFeatures featurePair; + featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); + featurePair.indexA = HALF_EDGE_A.tailVertexIndex; + featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); + featurePair.indexB = HALF_EDGE_B.tailVertexIndex; + + SHContact contact; + contact.featurePair = featurePair; + + contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB, manifold.normal); + contact.penetration = EDGE_QUERY.bestDistance; + + manifold.contacts[0] = contact; + manifold.numContacts = 1; + + return true; + } + + // Use a bias to favour a normal in the direction of A -> B + + + return false; } @@ -187,4 +237,54 @@ namespace SHADE return SHVec3::Dot(normal, HEAD_B - HEAD_A); } + SHVec3 SHCollision::findClosestPointBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB, SHVec3& normal) noexcept + { + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(edgeB); + + const SHVec3 HEAD_A = A.GetVertex(HALF_EDGE_A.headVertexIndex); + const SHVec3 TAIL_A = A.GetVertex(HALF_EDGE_A.tailVertexIndex); + const SHVec3 HEAD_B = B.GetVertex(HALF_EDGE_B.headVertexIndex); + const SHVec3 TAIL_B = B.GetVertex(HALF_EDGE_B.tailVertexIndex); + + const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); + const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); + + normal = SHVec3::Cross(VB, VA); + // Flip normal if need to ( A -> B) + if (SHVec3::Dot(normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) + normal = -normal; + + + /* + * Find a1, a2, b1, b2, c1, c2 to solve the simultaneous equation + * C' = TAIL_B - TAIL_A + * U = VA / VB + * + * a = VBxUx + VByUy + VBzUz + * b = -VAxUx - VAyUy - VAzUz + * c = (Cx'Ux + Cy'Uy + Cz'Uz) + */ + + const SHVec3 C = TAIL_B - TAIL_A; + + const float A1 = VB.x * VA.x + VB.y * VA.y + VB.z * VA.z; + const float A2 = VB.x * VB.x + VB.y * VB.y + VB.z * VB.z; + const float B1 = -VA.x * VA.x + -VA.y * VA.y + -VA.z * VA.z; + const float B2 = -VA.x * VB.x + -VA.y * VB.y + -VA.z * VB.z; + const float C1 = C.x * VA.x + C.y + VA.y + C.z + VA.z; + const float C2 = C.x * VB.x + C.y + VB.y + C.z + VB.z; + + /* + * R = -c2 / ( b2 - (a2 * (c1 + b1)) / a1 ) + * S = (b1 / a1) * (c2 / ( b2 - (a2 * b1) / a1 )) - (c1 / a1) + */ + + const float R = -C2 / (B2 - (A2 * (C1 + B1)) / A1); + + // Just take a point from A since it's A -> B + return TAIL_A + R * VA; + } + + } // namespace SHADE \ No newline at end of file From 016f6c804da9cb64c3721bcbdb5191e1ae2120bd Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 5 Jan 2023 14:40:06 +0800 Subject: [PATCH 083/164] Added more comments and clarity for --- Assets/Scenes/PhysicsSandbox.shade | 2 +- .../Collision/Narrowphase/SHCollision.h | 2 +- .../Narrowphase/SHConvexVsConvex.cpp | 83 ++++++++++++------- 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index b405fac2..0b732f64 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -280,7 +280,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0.524352431, y: 11.1989822, z: 0.0463808179} + Translate: {x: 0.524352431, y: 11.5, z: 0.0463808179} Rotate: {x: -0, y: 0.785398066, z: 0.785398185} Scale: {x: 0.999999821, y: 0.999999821, z: 0.999999881} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 57777cca..0fe0b3f7 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -107,7 +107,7 @@ namespace SHADE static bool buildMinkowskiFace (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; static float distanceBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB, SHVec3& normal) noexcept; + static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; /* * TODO: diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index 0f899443..5924ee73 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -83,6 +83,8 @@ namespace SHADE const FaceQuery& BEST_FACE_QUERY = FACE_QUERY_A.bestDistance > FACE_QUERY_B.bestDistance ? FACE_QUERY_A : FACE_QUERY_B; + uint32_t numContacts = 0; + // If an edge pair contains the closest distance,vwe ignore any face queries and find the closest points on // each edge and use that as the contact point. // Artificially increase the depth of this collision by a small tolerance to tend towards picking a face query in the next frame. @@ -91,22 +93,31 @@ namespace SHADE const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = POLY_A.GetHalfEdge(EDGE_QUERY.halfEdgeA); const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = POLY_B.GetHalfEdge(EDGE_QUERY.halfEdgeB); + const SHVec3 HEAD_A = POLY_A.GetVertex(HALF_EDGE_A.headVertexIndex); + const SHVec3 TAIL_A = POLY_A.GetVertex(HALF_EDGE_A.tailVertexIndex); + const SHVec3 HEAD_B = POLY_B.GetVertex(HALF_EDGE_B.headVertexIndex); + const SHVec3 TAIL_B = POLY_B.GetVertex(HALF_EDGE_B.tailVertexIndex); + + const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); + const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); + + manifold.normal = SHVec3::Cross(VB, VA); + // Flip normal if need to ( A -> B) + if (SHVec3::Dot(manifold.normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) + manifold.normal = -manifold.normal; + // In this scenario, we only have one contact - - SHContactFeatures featurePair; - featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); - featurePair.indexA = HALF_EDGE_A.tailVertexIndex; - featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); - featurePair.indexB = HALF_EDGE_B.tailVertexIndex; - SHContact contact; - contact.featurePair = featurePair; + contact.featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); + contact.featurePair.indexA = HALF_EDGE_A.tailVertexIndex; + contact.featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); + contact.featurePair.indexB = HALF_EDGE_B.tailVertexIndex; - contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB, manifold.normal); + contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); contact.penetration = EDGE_QUERY.bestDistance; - manifold.contacts[0] = contact; - manifold.numContacts = 1; + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; return true; } @@ -237,8 +248,22 @@ namespace SHADE return SHVec3::Dot(normal, HEAD_B - HEAD_A); } - SHVec3 SHCollision::findClosestPointBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB, SHVec3& normal) noexcept + SHVec3 SHCollision::findClosestPointBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept { + /* + * The two edges can be parameterised in the form p + tv + * + * LA(r) = A + rVA + * LB(s) = B + sVB + * + * The vector between the closest points is the cross product of VA and VB. + * Since the cross product is orthogonal to VA and VB, we can generalise this as: + * (LB(s) - LA(r)) /dot VA = 0 + * (LB(s) - LA(r)) /dot VB = 0 + * + * Where LB(s) - LA(r) is the same vector as VB X VA. + */ + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(edgeB); @@ -250,37 +275,35 @@ namespace SHADE const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); - normal = SHVec3::Cross(VB, VA); - // Flip normal if need to ( A -> B) - if (SHVec3::Dot(normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) - normal = -normal; - - /* * Find a1, a2, b1, b2, c1, c2 to solve the simultaneous equation - * C' = TAIL_B - TAIL_A - * U = VA / VB * - * a = VBxUx + VByUy + VBzUz - * b = -VAxUx - VAyUy - VAzUz - * c = (Cx'Ux + Cy'Uy + Cz'Uz) + * C' = TAIL_B - TAIL_A + * + * a = VB /dot U + * b = -VA /dot U + * c = C' /dot U + * + * U is either VA or VB */ const SHVec3 C = TAIL_B - TAIL_A; - const float A1 = VB.x * VA.x + VB.y * VA.y + VB.z * VA.z; - const float A2 = VB.x * VB.x + VB.y * VB.y + VB.z * VB.z; - const float B1 = -VA.x * VA.x + -VA.y * VA.y + -VA.z * VA.z; - const float B2 = -VA.x * VB.x + -VA.y * VB.y + -VA.z * VB.z; - const float C1 = C.x * VA.x + C.y + VA.y + C.z + VA.z; - const float C2 = C.x * VB.x + C.y + VB.y + C.z + VB.z; + const float VB_DOT_VA = SHVec3::Dot(VB, VA); + const float VB_DOT_VB = SHVec3::Dot(VB, VB); + const float AV_DOT_VA = SHVec3::Dot(-VA, VA); + const float AV_DOT_VB = SHVec3::Dot(-VA, VB); + const float C_DOT_VA = SHVec3::Dot(C, VA); + const float C_DOT_VB = SHVec3::Dot(C, VB); /* * R = -c2 / ( b2 - (a2 * (c1 + b1)) / a1 ) * S = (b1 / a1) * (c2 / ( b2 - (a2 * b1) / a1 )) - (c1 / a1) + * + * We only need to solve for R */ - const float R = -C2 / (B2 - (A2 * (C1 + B1)) / A1); + const float R = -C_DOT_VB / (AV_DOT_VB - (VB_DOT_VB * (C_DOT_VA + AV_DOT_VA)) / VB_DOT_VA); // Just take a point from A since it's A -> B return TAIL_A + R * VA; From 0c92e7ff6cee8ff37d9da3d7c52e4fe270915635 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 5 Jan 2023 14:40:06 +0800 Subject: [PATCH 084/164] Added more comments and clarity for polyhedron edge contacts --- Assets/Scenes/PhysicsSandbox.shade | 2 +- .../Collision/Narrowphase/SHCollision.h | 2 +- .../Narrowphase/SHConvexVsConvex.cpp | 83 ++++++++++++------- 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index b405fac2..0b732f64 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -280,7 +280,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0.524352431, y: 11.1989822, z: 0.0463808179} + Translate: {x: 0.524352431, y: 11.5, z: 0.0463808179} Rotate: {x: -0, y: 0.785398066, z: 0.785398185} Scale: {x: 0.999999821, y: 0.999999821, z: 0.999999881} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 57777cca..0fe0b3f7 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -107,7 +107,7 @@ namespace SHADE static bool buildMinkowskiFace (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; static float distanceBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB, SHVec3& normal) noexcept; + static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; /* * TODO: diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index 0f899443..5924ee73 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -83,6 +83,8 @@ namespace SHADE const FaceQuery& BEST_FACE_QUERY = FACE_QUERY_A.bestDistance > FACE_QUERY_B.bestDistance ? FACE_QUERY_A : FACE_QUERY_B; + uint32_t numContacts = 0; + // If an edge pair contains the closest distance,vwe ignore any face queries and find the closest points on // each edge and use that as the contact point. // Artificially increase the depth of this collision by a small tolerance to tend towards picking a face query in the next frame. @@ -91,22 +93,31 @@ namespace SHADE const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = POLY_A.GetHalfEdge(EDGE_QUERY.halfEdgeA); const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = POLY_B.GetHalfEdge(EDGE_QUERY.halfEdgeB); + const SHVec3 HEAD_A = POLY_A.GetVertex(HALF_EDGE_A.headVertexIndex); + const SHVec3 TAIL_A = POLY_A.GetVertex(HALF_EDGE_A.tailVertexIndex); + const SHVec3 HEAD_B = POLY_B.GetVertex(HALF_EDGE_B.headVertexIndex); + const SHVec3 TAIL_B = POLY_B.GetVertex(HALF_EDGE_B.tailVertexIndex); + + const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); + const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); + + manifold.normal = SHVec3::Cross(VB, VA); + // Flip normal if need to ( A -> B) + if (SHVec3::Dot(manifold.normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) + manifold.normal = -manifold.normal; + // In this scenario, we only have one contact - - SHContactFeatures featurePair; - featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); - featurePair.indexA = HALF_EDGE_A.tailVertexIndex; - featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); - featurePair.indexB = HALF_EDGE_B.tailVertexIndex; - SHContact contact; - contact.featurePair = featurePair; + contact.featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); + contact.featurePair.indexA = HALF_EDGE_A.tailVertexIndex; + contact.featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); + contact.featurePair.indexB = HALF_EDGE_B.tailVertexIndex; - contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB, manifold.normal); + contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); contact.penetration = EDGE_QUERY.bestDistance; - manifold.contacts[0] = contact; - manifold.numContacts = 1; + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; return true; } @@ -237,8 +248,22 @@ namespace SHADE return SHVec3::Dot(normal, HEAD_B - HEAD_A); } - SHVec3 SHCollision::findClosestPointBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB, SHVec3& normal) noexcept + SHVec3 SHCollision::findClosestPointBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept { + /* + * The two edges can be parameterised in the form p + tv + * + * LA(r) = A + rVA + * LB(s) = B + sVB + * + * The vector between the closest points is the cross product of VA and VB. + * Since the cross product is orthogonal to VA and VB, we can generalise this as: + * (LB(s) - LA(r)) /dot VA = 0 + * (LB(s) - LA(r)) /dot VB = 0 + * + * Where LB(s) - LA(r) is the same vector as VB X VA. + */ + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(edgeB); @@ -250,37 +275,35 @@ namespace SHADE const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); - normal = SHVec3::Cross(VB, VA); - // Flip normal if need to ( A -> B) - if (SHVec3::Dot(normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) - normal = -normal; - - /* * Find a1, a2, b1, b2, c1, c2 to solve the simultaneous equation - * C' = TAIL_B - TAIL_A - * U = VA / VB * - * a = VBxUx + VByUy + VBzUz - * b = -VAxUx - VAyUy - VAzUz - * c = (Cx'Ux + Cy'Uy + Cz'Uz) + * C' = TAIL_B - TAIL_A + * + * a = VB /dot U + * b = -VA /dot U + * c = C' /dot U + * + * U is either VA or VB */ const SHVec3 C = TAIL_B - TAIL_A; - const float A1 = VB.x * VA.x + VB.y * VA.y + VB.z * VA.z; - const float A2 = VB.x * VB.x + VB.y * VB.y + VB.z * VB.z; - const float B1 = -VA.x * VA.x + -VA.y * VA.y + -VA.z * VA.z; - const float B2 = -VA.x * VB.x + -VA.y * VB.y + -VA.z * VB.z; - const float C1 = C.x * VA.x + C.y + VA.y + C.z + VA.z; - const float C2 = C.x * VB.x + C.y + VB.y + C.z + VB.z; + const float VB_DOT_VA = SHVec3::Dot(VB, VA); + const float VB_DOT_VB = SHVec3::Dot(VB, VB); + const float AV_DOT_VA = SHVec3::Dot(-VA, VA); + const float AV_DOT_VB = SHVec3::Dot(-VA, VB); + const float C_DOT_VA = SHVec3::Dot(C, VA); + const float C_DOT_VB = SHVec3::Dot(C, VB); /* * R = -c2 / ( b2 - (a2 * (c1 + b1)) / a1 ) * S = (b1 / a1) * (c2 / ( b2 - (a2 * b1) / a1 )) - (c1 / a1) + * + * We only need to solve for R */ - const float R = -C2 / (B2 - (A2 * (C1 + B1)) / A1); + const float R = -C_DOT_VB / (AV_DOT_VB - (VB_DOT_VB * (C_DOT_VA + AV_DOT_VA)) / VB_DOT_VA); // Just take a point from A since it's A -> B return TAIL_A + R * VA; From 8ca4045d555d3002d66ff6d358c758dd606b0145 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 5 Jan 2023 17:53:48 +0800 Subject: [PATCH 085/164] R for retard --- Assets/Scenes/PhysicsSandbox.shade | 4 +- .../Collision/Narrowphase/SHCollision.h | 3 +- .../Narrowphase/SHConvexVsConvex.cpp | 109 ++++++++++++------ .../Narrowphase/SHSphereVsConvex.cpp | 1 + .../src/Physics/Dynamics/SHContactSolver.cpp | 5 +- .../src/Physics/Dynamics/SHContactSolver.h | 3 - SHADE_Engine/src/Physics/SHPhysicsConstants.h | 44 +++++++ 7 files changed, 128 insertions(+), 41 deletions(-) create mode 100644 SHADE_Engine/src/Physics/SHPhysicsConstants.h diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 0b732f64..d30c8f07 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -280,7 +280,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0.524352431, y: 11.5, z: 0.0463808179} + Translate: {x: 0.524352431, y: 13.5, z: 0.0463808179} Rotate: {x: -0, y: 0.785398066, z: 0.785398185} Scale: {x: 0.999999821, y: 0.999999821, z: 0.999999881} IsActive: true @@ -291,7 +291,7 @@ Drag: 0.00999999978 Angular Drag: 0.00999999978 Use Gravity: true - Gravity Scale: 1 + Gravity Scale: 0.5 Interpolate: true Sleeping Enabled: true Freeze Position X: false diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 0fe0b3f7..980914c1 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -108,6 +108,7 @@ namespace SHADE static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; static float distanceBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; + static int32_t findIncidentFace (const SHConvexPolyhedron& poly, const SHVec3& normal) noexcept; /* * TODO: @@ -115,7 +116,7 @@ namespace SHADE * * * - * static int32_t findIncidentFace (SHConvexPolyhedron& poly, const SHVec3& normal) noexcept; + * * static uint32_t clip [sutherland-hodgemann clipping] * * ! References diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index 5924ee73..be43af8f 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -17,6 +17,7 @@ #include "Math/SHMathHelpers.h" #include "Math/Geometry/SHPlane.h" #include "Physics/Collision/Shapes/SHConvexPolyhedron.h" +#include "Physics/SHPhysicsConstants.h" #include "Tools/Utilities/SHUtilities.h" namespace SHADE @@ -47,24 +48,7 @@ namespace SHADE bool SHCollision::ConvexVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - /* - * TODO: - * - * 1. Query face directions of a to b. Exit early if separation found. - * 2. Query face directions of b to a. Exit early if separation found. - * 3. Query edge directions of a & b. Exit early if separation found. - * - * (*)!! Apply weight to improve frame coherence of normal directions. DONT FORGET FLIP FLOP! - * 4. From above, save the axis of minimum penetration (reference face) - * 5. Find the most anti-parallel face on other shape (incident face) - * 6. Clip incident face against side planes of reference face. (Sutherland-Hodgeman Clipping). - * Keep all vertices below reference face. - * 7. Reduce manifold to 4 contact points. We only need 4 contact points for a stable manifold. - * - * Remember to save IDs in queries. - * During generation of incident face, store IDs of face. - * - */ + static constexpr float TOLERANCE = 0.1f + SHPHYSICS_LINEAR_SLOP; const SHConvexPolyhedron& POLY_A = dynamic_cast(A); const SHConvexPolyhedron& POLY_B = dynamic_cast(B); @@ -81,14 +65,33 @@ namespace SHADE if (EDGE_QUERY.bestDistance > 0.0f) return false; - const FaceQuery& BEST_FACE_QUERY = FACE_QUERY_A.bestDistance > FACE_QUERY_B.bestDistance ? FACE_QUERY_A : FACE_QUERY_B; + // Apply weight to improve frame coherence of normal directions. + // We want a normal in the direction from A -> B, so we flip the normal if needed. + bool flipNormal = false; + const SHConvexPolyhedron* referencePoly = nullptr; + const SHConvexPolyhedron* incidentPoly = nullptr; + + FaceQuery minFaceQuery; + if (FACE_QUERY_A.bestDistance + TOLERANCE > FACE_QUERY_B.bestDistance) + { + minFaceQuery = FACE_QUERY_A; + referencePoly = &POLY_A; + incidentPoly = &POLY_B; + } + else + { + minFaceQuery = FACE_QUERY_B; + referencePoly = &POLY_B; + incidentPoly = &POLY_A; + flipNormal = true; + } uint32_t numContacts = 0; + // If an edge pair contains the closest distance,vwe ignore any face queries and find the closest points on // each edge and use that as the contact point. - // Artificially increase the depth of this collision by a small tolerance to tend towards picking a face query in the next frame. - if (EDGE_QUERY.bestDistance > BEST_FACE_QUERY.bestDistance) + if (EDGE_QUERY.bestDistance > minFaceQuery.bestDistance + TOLERANCE) { const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = POLY_A.GetHalfEdge(EDGE_QUERY.halfEdgeA); const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = POLY_B.GetHalfEdge(EDGE_QUERY.halfEdgeB); @@ -122,7 +125,24 @@ namespace SHADE return true; } - // Use a bias to favour a normal in the direction of A -> B + const SHVec3 REFERENCE_NORMAL = referencePoly->GetNormal(minFaceQuery.closestFace); + const int32_t INCIDENT_FACE_IDX = findIncidentFace(*incidentPoly, REFERENCE_NORMAL); + + + /* + * TODO: + * + * !! + * 4. From above, save the axis of minimum penetration (reference face) + * 5. Find the most anti-parallel face on other shape (incident face) + * 6. Clip incident face against side planes of reference face. (Sutherland-Hodgeman Clipping). + * Keep all vertices below reference face. + * 7. Reduce manifold to 4 contact points. We only need 4 contact points for a stable manifold. + * + * Remember to save IDs in queries. + * During generation of incident face, store IDs of face. + * + */ @@ -289,25 +309,48 @@ namespace SHADE const SHVec3 C = TAIL_B - TAIL_A; - const float VB_DOT_VA = SHVec3::Dot(VB, VA); - const float VB_DOT_VB = SHVec3::Dot(VB, VB); - const float AV_DOT_VA = SHVec3::Dot(-VA, VA); - const float AV_DOT_VB = SHVec3::Dot(-VA, VB); - const float C_DOT_VA = SHVec3::Dot(C, VA); - const float C_DOT_VB = SHVec3::Dot(C, VB); + const float VB_DOT_VA = SHVec3::Dot(VB, VA); // a1 + const float VB_DOT_VB = SHVec3::Dot(VB, VB); // a2 + const float AV_DOT_VA = SHVec3::Dot(-VA, VA); // b1 + const float AV_DOT_VB = SHVec3::Dot(-VA, VB); // b2 + const float C_DOT_VA = SHVec3::Dot(C, VA); // c1 + const float C_DOT_VB = SHVec3::Dot(C, VB); // c2 /* - * R = -c2 / ( b2 - (a2 * (c1 + b1)) / a1 ) - * S = (b1 / a1) * (c2 / ( b2 - (a2 * b1) / a1 )) - (c1 / a1) - * * We only need to solve for R + * R = (c1a2 / a1 - c2) / ( b2 - a2b1/a1 ) */ - - const float R = -C_DOT_VB / (AV_DOT_VB - (VB_DOT_VB * (C_DOT_VA + AV_DOT_VA)) / VB_DOT_VA); + + const float A2_OVER_A1 = VB_DOT_VB / VB_DOT_VA; + const float NUMERATOR = C_DOT_VA * A2_OVER_A1 - C_DOT_VB; + const float DENOMINATOR = AV_DOT_VB - AV_DOT_VA * A2_OVER_A1; + const float R = NUMERATOR / DENOMINATOR; // Just take a point from A since it's A -> B return TAIL_A + R * VA; } + int32_t SHCollision::findIncidentFace(const SHConvexPolyhedron& poly, const SHVec3& normal) noexcept + { + // Get the most anti-parallel face to the normal + int32_t bestFace = 0; + float bestProjection = std::numeric_limits::max(); + + const int32_t NUM_FACES = poly.GetFaceCount(); + for (const int32_t i : std::views::iota(0, NUM_FACES)) + { + const SHVec3 INC_NORMAL = poly.GetNormal(i); + const float PROJECTION = SHVec3::Dot(INC_NORMAL, normal); + + if (PROJECTION < bestProjection) + { + bestProjection = PROJECTION; + bestFace = i; + } + } + + return bestFace; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 2b08313b..e6a961e0 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -19,6 +19,7 @@ #include "Math/SHMathHelpers.h" #include "Physics/Collision/Shapes/SHSphere.h" #include "Physics/Collision/Shapes/SHConvexPolyhedron.h" +#include "Physics/SHPhysicsConstants.h" namespace SHADE { diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp index ed4c9aa8..fa6f1266 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp @@ -15,6 +15,7 @@ // Project Headers #include "Math/SHMathHelpers.h" +#include "Physics/SHPhysicsConstants.h" namespace SHADE { @@ -190,8 +191,8 @@ namespace SHADE const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB); const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal); - const float ERROR_BIAS = BAUMGARTE_FACTOR * INV_DT * std::min(0.0f, -contact.penetration + PENETRATION_SLOP); - const float RESTITUTION_BIAS = -constraint.restitution * RV_N; + const float ERROR_BIAS = SHPHYSICS_BAUMGARTE * INV_DT * std::min(0.0f, -contact.penetration + SHPHYSICS_LINEAR_SLOP); + const float RESTITUTION_BIAS = std::fabs(RV_N) > SHPHYSICS_RESTITUTION_THRESHOLD ? -constraint.restitution * RV_N : 0.0f; contact.bias = ERROR_BIAS + RESTITUTION_BIAS; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h index 56955f74..3146a743 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h @@ -78,9 +78,6 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - static constexpr float BAUMGARTE_FACTOR = 0.2f; - static constexpr float PENETRATION_SLOP = 0.05f; - VelocityStates velocityStates; ContactConstraints contactConstraints; diff --git a/SHADE_Engine/src/Physics/SHPhysicsConstants.h b/SHADE_Engine/src/Physics/SHPhysicsConstants.h new file mode 100644 index 00000000..0d8f6fc8 --- /dev/null +++ b/SHADE_Engine/src/Physics/SHPhysicsConstants.h @@ -0,0 +1,44 @@ +/**************************************************************************************** + * \file SHPhysicsConstants.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Definitions for constants used in physics simulations + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Project Includes +#include "Math/SHMathHelpers.h" + +namespace SHADE +{ + /** + * @brief + * The number of simulations length for every real world unit meter.
+ * Modify this to change the global scale of the simulation. + */ + static constexpr float SHPHYSICS_LENGTHS_PER_UNIT_METER = 1.0f; + + /** + * @brief + * Linear Collision & Constraint tolerance. + */ + static constexpr float SHPHYSICS_LINEAR_SLOP = 0.005f * SHPHYSICS_LENGTHS_PER_UNIT_METER; + + /** + * @brief + * Velocity threshold for restitution to be applied. + */ + static constexpr float SHPHYSICS_RESTITUTION_THRESHOLD = 1.0f; + + /** + * @brief + * Scaling factor to control how fast overlaps are resolved.
+ * 1 is ideal for instant correction, but values close to 1 can lead to overshoot. + */ + static constexpr float SHPHYSICS_BAUMGARTE = 0.2f; + +} From ab766d9304645f3ce85f9677a71b89f918522252 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 7 Jan 2023 16:42:13 +0800 Subject: [PATCH 086/164] Updated Model asset and rig loading --- .../Assets/Asset Types/Models/SHMeshAsset.h | 3 +- .../Assets/Asset Types/Models/SHModelAsset.h | 4 +- .../Assets/Asset Types/Models/SHRigAsset.cpp | 26 ++ .../Assets/Asset Types/Models/SHRigAsset.h | 18 +- .../Libraries/Loaders/SHModelLoader.cpp | 258 +++++++++++++++--- .../Assets/Libraries/Loaders/SHModelLoader.h | 21 +- 6 files changed, 263 insertions(+), 67 deletions(-) create mode 100644 SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h index e8780669..c772f717 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h @@ -24,7 +24,6 @@ namespace SHADE uint32_t indexCount; uint32_t charCount; uint32_t boneCount; - std::string name; }; struct MeshBoneInfo @@ -48,7 +47,7 @@ namespace SHADE struct SH_API SHMeshAsset : SHAssetData { - SHMeshDataHeader header; + std::string name; std::vector VertexPositions; std::vector VertexTangents; std::vector VertexNormals; diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHModelAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHModelAsset.h index 4425f555..2ddc6a5a 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHModelAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHModelAsset.h @@ -29,9 +29,11 @@ namespace SHADE struct SH_API SHModelAsset : SHAssetData { SHModelAssetHeader header; - SHRigAsset rig; + std::vector meshHeaders; + std::vector animHeaders; + std::vector meshes; std::vector anims; }; diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp new file mode 100644 index 00000000..251fdc91 --- /dev/null +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp @@ -0,0 +1,26 @@ +#include "SHpch.h" +#include "SHRigAsset.h" + +#include + +namespace SHADE +{ + SHRigAsset::~SHRigAsset() + { + std::queue nodeQueue; + nodeQueue.push(root); + + while(!nodeQueue.empty()) + { + auto curr = nodeQueue.front(); + nodeQueue.pop(); + + for (auto child : curr->children) + { + nodeQueue.push(child); + } + + delete curr; + } + } +} diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h index 09ce5658..86b497ae 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h @@ -17,32 +17,30 @@ namespace SHADE { - struct RigDataHeader + struct SHRigDataHeader { uint32_t nodeCount; std::vector charCounts; }; - struct RigNodeData + struct SHRigNodeData { std::string name; SHMatrix transform; }; - struct RigNode + struct SHRigNode { uint32_t idRef; - std::vector children; + std::vector children; }; struct SH_API SHRigAsset : SHAssetData { - ~SHRigAsset() - { - delete root; - } + ~SHRigAsset(); - std::map nodeDataCollection; - RigNode* root; + SHRigDataHeader header; + std::vector nodeDataCollection; + SHRigNode* root; }; } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index e2d80821..fb809a66 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -13,42 +13,231 @@ #include "SHpch.h" #include "SHModelLoader.h" #include +#include namespace SHADE { - void SHModelLoader::ReadHeader(std::ifstream& file, SHMeshLoaderHeader& header) noexcept + void SHModelLoader::ReadHeaders(FileReference file, SHModelAsset& asset) { file.read( - reinterpret_cast(&header), - sizeof(SHMeshLoaderHeader) + reinterpret_cast(&asset.header), + sizeof(asset.header) + ); + + asset.meshHeaders.resize(asset.header.meshCount); + asset.animHeaders.resize(asset.header.animCount); + + file.read( + reinterpret_cast(asset.meshHeaders.data()), + sizeof(asset.header.meshCount) * sizeof(SHMeshDataHeader) + ); + + file.read( + reinterpret_cast(asset.animHeaders.data()), + sizeof(asset.header.animCount) * sizeof(SHAnimDataHeader) ); } - void SHModelLoader::ReadData(std::ifstream& file, SHMeshLoaderHeader const& header, SHMeshAsset& data) noexcept + void SHModelLoader::ReadData(FileReference file, SHModelAsset& asset) { - auto const vertexVec3Byte{ sizeof(SHVec3) * header.vertexCount }; - auto const vertexVec2Byte{ sizeof(SHVec2) * header.vertexCount }; + ReadMeshData(file, asset.meshHeaders, asset.meshes); + ReadAnimData(file, asset.animHeaders, asset.anims); - data.VertexPositions.resize(header.vertexCount); - data.VertexTangents.resize(header.vertexCount); - data.VertexNormals.resize(header.vertexCount); - data.VertexTexCoords.resize(header.vertexCount); - data.Indices.resize(header.indexCount); - data.header.name.resize(header.charCount); - - file.read(data.header.name.data(), header.charCount); - file.read(reinterpret_cast(data.VertexPositions.data()), vertexVec3Byte); - file.read(reinterpret_cast(data.VertexTangents.data()), vertexVec3Byte); - file.read(reinterpret_cast(data.VertexNormals.data()), vertexVec3Byte); - file.read(reinterpret_cast(data.VertexTexCoords.data()), vertexVec2Byte); - file.read(reinterpret_cast(data.Indices.data()), sizeof(uint32_t) * header.indexCount); - - data.header.vertexCount = header.vertexCount; - data.header.indexCount = header.indexCount; + // Not eof yet, animation exists + if (file.peek() != EOF) + { + ReadRigHeader(file, asset.rig.header); + ReadRigData(file, asset.rig.header, asset.rig.nodeDataCollection); + ReadRigTree(file, asset.rig.header, asset.rig.root); + } } - void SHModelLoader::LoadSHMesh(AssetPath path, SHModelAsset& model) noexcept + void SHModelLoader::ReadAnimNode(FileReference file, SHAnimNodeInfo const& info, SHAnimData& data) { + file.read( + data.name.data(), + info.charCount + ); + + file.read( + reinterpret_cast(&data.pre), + sizeof(SHAnimationBehaviour) + ); + + file.read( + reinterpret_cast(&data.post), + sizeof(SHAnimationBehaviour) + ); + + uint32_t keySize {0}; + file.read( + reinterpret_cast(&keySize), + sizeof(uint32_t) + ); + + data.positionKeys.resize(keySize); + data.rotationKeys.resize(keySize); + data.scaleKeys.resize(keySize); + + file.read( + reinterpret_cast(data.positionKeys.data()), + sizeof(PositionKey) * keySize + ); + + file.read( + reinterpret_cast(data.rotationKeys.data()), + sizeof(RotationKey) * keySize + ); + + file.read( + reinterpret_cast(data.scaleKeys.data()), + sizeof(ScaleKey) * keySize + ); + } + + void SHModelLoader::ReadRigHeader(FileReference file, SHRigDataHeader& header) + { + file.read( + reinterpret_cast(&header.nodeCount), + sizeof(uint32_t) + ); + + header.charCounts.resize(header.nodeCount); + file.read( + reinterpret_cast(header.charCounts.data()), + sizeof(uint32_t) * header.nodeCount + ); + } + + void SHModelLoader::ReadRigData(FileReference file, SHRigDataHeader const& header, std::vector& data) + { + data.resize(header.nodeCount); + + for (auto i {0}; i < header.nodeCount; ++i) + { + data[i].name.resize(header.charCounts[i]); + file.read( + data[i].name.data(), + header.charCounts[i] + ); + + file.read( + reinterpret_cast(&data[i].transform), + sizeof(SHMatrix) + ); + } + } + + void SHModelLoader::ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode* root) + { + // Read All nodes into one contiguous data block + struct NodeTemp + { + uint32_t id, numChild; + }; + + NodeTemp* dst = new NodeTemp[header.nodeCount]; + + file.read( + reinterpret_cast(dst), + sizeof(NodeTemp) * header.nodeCount + ); + + // Build and populate tree + SHRigNode* nodePool = new SHRigNode[header.nodeCount]; + root = nodePool; + + std::queue> nodeQueue; + nodeQueue.emplace(std::make_pair(nodePool, dst)); + + auto depthPtr = nodePool + 1; + auto depthTempPtr = dst + 1; + + while(!nodeQueue.empty()) + { + auto currPair = nodeQueue.front(); + nodeQueue.pop(); + auto currNode = currPair.first; + auto currTemp = currPair.second; + + currNode->idRef = currTemp->id; + + for (auto i{0}; i < currTemp->numChild; ++i) + { + currNode->children.push_back(depthPtr); + nodeQueue.emplace(depthPtr++, depthTempPtr++); + } + } + } + + void SHModelLoader::ReadMeshData(FileReference file, std::vector const& headers, + std::vector& meshes) + { + meshes.resize(headers.size()); + for (auto i {0}; i < headers.size(); ++i) + { + auto const& header = headers[i]; + auto& data = *new SHMeshAsset; + + auto const vertexVec3Byte{ sizeof(SHVec3) * header.vertexCount }; + auto const vertexVec2Byte{ sizeof(SHVec2) * header.vertexCount }; + + data.name.resize(header.charCount); + data.VertexPositions.resize(header.vertexCount); + data.VertexTangents.resize(header.vertexCount); + data.VertexNormals.resize(header.vertexCount); + data.VertexTexCoords.resize(header.vertexCount); + data.Indices.resize(header.indexCount); + + file.read(data.name.data(), header.charCount); + file.read(reinterpret_cast(data.VertexPositions.data()), vertexVec3Byte); + file.read(reinterpret_cast(data.VertexTangents.data()), vertexVec3Byte); + file.read(reinterpret_cast(data.VertexNormals.data()), vertexVec3Byte); + file.read(reinterpret_cast(data.VertexTexCoords.data()), vertexVec2Byte); + file.read(reinterpret_cast(data.Indices.data()), sizeof(uint32_t) * header.indexCount); + + meshes[i] = &data; + } + } + + void SHModelLoader::ReadAnimData(FileReference file, std::vector const& headers, + std::vector& anims) + { + anims.resize(headers.size()); + for (auto i {0}; i < headers.size(); ++i) + { + auto const& header = headers[i]; + auto& animAsset = *new SHAnimAsset; + + file.read( + animAsset.name.data(), + header.charCount + ); + + file.read( + reinterpret_cast(&animAsset.duration), + sizeof(double) + ); + + file.read( + reinterpret_cast(&animAsset.ticksPerSecond), + sizeof(double) + ); + + animAsset.nodeChannels.resize(header.animNodeCount); + for (auto i {0}; i < header.animNodeCount; ++i) + { + ReadAnimNode(file, header.nodeHeaders[i], animAsset.nodeChannels[i]); + } + + anims[i] = &animAsset; + } + } + + SHAssetData* SHModelLoader::Load(AssetPath path) + { + auto result = new SHModelAsset(); + std::ifstream file{ path.string(), std::ios::in | std::ios::binary }; if (!file.is_open()) { @@ -58,29 +247,10 @@ namespace SHADE file.seekg(0); - //TODO Update to new mesh header with anim count when animation saving is done - file.read( - reinterpret_cast(&model.header.meshCount), - sizeof(model.header.meshCount) - ); + ReadHeaders(file, *result); + ReadData(file, *result); - std::vector headers(model.header.meshCount); - model.meshes.resize(model.header.meshCount); - - for (auto i{ 0 }; i < model.header.meshCount; ++i) - { - model.meshes[i] = new SHMeshAsset(); - ReadHeader(file, headers[i]); - ReadData(file, headers[i], *model.meshes[i]); - } file.close(); - } - - SHAssetData* SHModelLoader::Load(AssetPath path) - { - auto result = new SHModelAsset(); - - LoadSHMesh(path, *result); return result; } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h index 2074169c..05e58b6c 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h @@ -18,19 +18,20 @@ namespace SHADE { class SHModelLoader : public SHAssetLoader { - struct SHMeshLoaderHeader - { - uint32_t vertexCount; - uint32_t indexCount; - uint32_t charCount; - }; + using FileReference = std::ifstream&; + + void ReadAnimNode(FileReference file, SHAnimNodeInfo const& info, SHAnimData& data); + void ReadRigHeader(FileReference file, SHRigDataHeader& header); + void ReadRigData(FileReference file, SHRigDataHeader const& header, std::vector& data); + void ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode* root); + + void ReadMeshData(FileReference file, std::vector const& headers, std::vector& meshes); + void ReadAnimData(FileReference file, std::vector const& headers, std::vector& anims); - void ReadHeader(std::ifstream& file, SHMeshLoaderHeader& header) noexcept; - void ReadData(std::ifstream& file, SHMeshLoaderHeader const& header, SHMeshAsset& data) noexcept; - + void ReadHeaders(FileReference file, SHModelAsset& asset); + void ReadData(FileReference file, SHModelAsset& asset); public: - void LoadSHMesh(AssetPath path, SHModelAsset& model) noexcept; SHAssetData* Load(AssetPath path) override; void Write(SHAssetData const* data, AssetPath path) override; }; From 73a1aaa48099109d3b5ac76a1fa61a6790b749f9 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 7 Jan 2023 21:00:11 +0800 Subject: [PATCH 087/164] Updated model loading to match new model binary implementation Changed some calls in resource to match new names and defines --- SHADE_Engine/src/Animation/SHRig.cpp | 2 +- SHADE_Engine/src/Animation/SHRig.h | 4 +- .../Libraries/Loaders/SHModelLoader.cpp | 56 +++++++++++++------ SHADE_Engine/src/Assets/SHAssetManager.cpp | 4 +- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 7c3059de..0d777c27 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -73,7 +73,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ - Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const RigNode* sourceNode) + Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNode* sourceNode) { // Construct the node auto newNode = nodeStore.Create(); diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index 5c4f720d..66bb37ad 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -27,7 +27,7 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ struct SHRigAsset; - struct RigNode; + struct SHRigNode; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -99,6 +99,6 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - Handle recurseCreateNode(const SHRigAsset& asset, const RigNode* sourceNode); + Handle recurseCreateNode(const SHRigAsset& asset, const SHRigNode* sourceNode); }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index fb809a66..bfb97fda 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -23,19 +23,44 @@ namespace SHADE reinterpret_cast(&asset.header), sizeof(asset.header) ); - - asset.meshHeaders.resize(asset.header.meshCount); - asset.animHeaders.resize(asset.header.animCount); - - file.read( - reinterpret_cast(asset.meshHeaders.data()), - sizeof(asset.header.meshCount) * sizeof(SHMeshDataHeader) - ); - file.read( - reinterpret_cast(asset.animHeaders.data()), - sizeof(asset.header.animCount) * sizeof(SHAnimDataHeader) - ); + if (asset.header.meshCount > 0) + { + asset.meshHeaders.resize(asset.header.meshCount); + file.read( + reinterpret_cast(asset.meshHeaders.data()), + asset.header.meshCount * sizeof(SHMeshDataHeader) + ); + } + + if (asset.header.animCount > 0) + { + asset.animHeaders.resize(asset.header.animCount); + for (auto i {0}; i < asset.header.animCount; ++i) + { + auto& animHeader = asset.animHeaders[i]; + file.read( + reinterpret_cast(&animHeader.charCount), + sizeof(uint32_t) + ); + + file.read( + reinterpret_cast(&animHeader.animNodeCount), + sizeof(uint32_t) + ); + + animHeader.nodeHeaders.resize(animHeader.animNodeCount); + for (auto j {0}; j < animHeader.animNodeCount; ++j) + { + auto& nodeHeader = animHeader.nodeHeaders[j]; + + file.read( + reinterpret_cast(&nodeHeader), + sizeof(SHAnimNodeInfo) + ); + } + } + } } void SHModelLoader::ReadData(FileReference file, SHModelAsset& asset) @@ -208,7 +233,8 @@ namespace SHADE { auto const& header = headers[i]; auto& animAsset = *new SHAnimAsset; - + + animAsset.name.resize(header.charCount); file.read( animAsset.name.data(), header.charCount @@ -242,11 +268,9 @@ namespace SHADE if (!file.is_open()) { SHLOG_ERROR("[Model Loader] Unable to open SHModel File: {}", path.string()); - return; + return nullptr; } - file.seekg(0); - ReadHeaders(file, *result); ReadData(file, *result); diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index dcc95695..0a3dd765 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -437,7 +437,7 @@ namespace SHADE return; } - if (genMeta) + if (true) { GenerateNewMeta(newPath); } @@ -610,7 +610,7 @@ namespace SHADE for(auto const& subMesh : data->meshes) { SHAsset subAsset{ - .name = subMesh->header.name, + .name = subMesh->name, .id = GenerateAssetID(AssetType::MESH), .type = AssetType::MESH, .isSubAsset = true, From ae024e9757f0d031e420c61e92d2298d8a4480bb Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 7 Jan 2023 21:13:59 +0800 Subject: [PATCH 088/164] Changed order of init in asset manager to init all loaders first before building directory to avoid crash --- SHADE_Engine/src/Assets/SHAssetManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index 0a3dd765..df982d28 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -492,8 +492,8 @@ namespace SHADE ****************************************************************************/ void SHAssetManager::Load() noexcept { - BuildAssetCollection(); InitLoaders(); + BuildAssetCollection(); //CompileAll(); //LoadAllData(); } From c3d027f5b18b62ef97882e83310f5b431423057a Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 7 Jan 2023 21:32:16 +0800 Subject: [PATCH 089/164] AssetID check for 0 --- SHADE_Engine/src/Assets/SHAssetManager.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SHADE_Engine/src/Assets/SHAssetManager.hpp b/SHADE_Engine/src/Assets/SHAssetManager.hpp index 4f372938..8fb22240 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.hpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.hpp @@ -6,6 +6,11 @@ namespace SHADE template std::enable_if_t, T* const> SHAssetManager::GetData(AssetID id) noexcept { + if (id == 0) + { + return nullptr; + } + if (!assetData.contains(id)) { for (auto const& asset : std::ranges::views::values(assetCollection)) From de0dc5db612e91ee60dae9cefa5c9f89c882689d Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 7 Jan 2023 21:48:31 +0800 Subject: [PATCH 090/164] Recompiled models --- Assets/Models/ExteriorMeshs.shmodel | Bin 102626 -> 102666 bytes Assets/Models/HouseModular.shmodel | Bin 252695 -> 284143 bytes Assets/Models/KitchenShelves1.shmodel | Bin 13031 -> 13767 bytes Assets/Models/MD_BreakableObjects1.shmodel | Bin 173714 -> 173866 bytes Assets/Models/MD_FoodItems.shmodel | Bin 35927 -> 36299 bytes Assets/Models/MD_Homeowner-NoRig.shmodel | Bin 187015 -> 187027 bytes Assets/Models/racoon.shmodel | Bin 254874 -> 404346 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Assets/Models/ExteriorMeshs.shmodel b/Assets/Models/ExteriorMeshs.shmodel index 6121ad27acc754d0028e1fdde7e5764f693eb32d..d7da02b67bbe497f062bb3bb91cc5f4db2a357f6 100644 GIT binary patch delta 33844 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 delta 33796 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 diff --git a/Assets/Models/HouseModular.shmodel b/Assets/Models/HouseModular.shmodel index 4fb25c50088efff700561b6f5f02efc4fdaa134a..bf11c8d8e0354d4430002fe94683aaec697b0a76 100644 GIT binary patch literal 284143 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

t-rrEAA+sgWS>*nE>0rm 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 mX94`vIRVq3YmJ=bh&>vf?I>%Z7W diff --git a/Assets/Models/MD_BreakableObjects1.shmodel b/Assets/Models/MD_BreakableObjects1.shmodel index 9d846e440be618cc2ee8536fe4c084367a765008..752d936d29dfdb77c67d17b04f6e78c023431dde 100644 GIT binary patch delta 65572 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 w=gbS1S(31#SVvlR6~*f;Eun}%86>4d6kklC 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 JfZ@1?{|CM8SbqQj delta 16896 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 diff --git a/Assets/Models/MD_Homeowner-NoRig.shmodel b/Assets/Models/MD_Homeowner-NoRig.shmodel index eb044ae2c366b285edcceddbc2532b9e6429cc10..639d49816c5b7378159ea6e24688410fd708f857 100644 GIT binary patch delta 59277 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%0DSdIV&2X$eAiCo>E8V@zr>q8hR#4nGaq)W9EXfb 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 delta 59280 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ྱIad~^hx$tsy*N5EV%t=)=O?#Kka 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 diff --git a/Assets/Models/racoon.shmodel b/Assets/Models/racoon.shmodel index 9c5d866ac663720e0c1ab614522d71438f2510e5..f3420348caac368b4424f89b139dfc3149ad017f 100644 GIT binary patch literal 404346 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%0DuO!wt9<`Jkz5(5^o7C9SQc<2W;n>mw> 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 Fe*wWo#Z&+Q From 1a2a514f985da126c4b0416a7e8f11ffde7f53da Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 7 Jan 2023 22:00:46 +0800 Subject: [PATCH 091/164] Updated all models --- Assets/Models/ExteriorMeshs.shmodel | Bin 102666 -> 103436 bytes Assets/Models/HouseModular.shmodel | Bin 284143 -> 285698 bytes Assets/Models/KitchenAddOns1.shmodel | Bin 132379 -> 133022 bytes Assets/Models/KitchenCounterEmpty.shmodel | Bin 10153 -> 10165 bytes Assets/Models/KitchenCounterMeshs.shmodel | Bin 223223 -> 235098 bytes Assets/Models/KitchenShelves1.shmodel | Bin 13767 -> 14362 bytes Assets/Models/MD_BreakableObjects1.shmodel | Bin 173866 -> 175288 bytes Assets/Models/MD_FoodItems.shmodel | Bin 36299 -> 36626 bytes Assets/Models/MD_SkyDome01.shmodel | Bin 34162 -> 34174 bytes Assets/Models/Quad.shmodel | Bin 224 -> 236 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Assets/Models/ExteriorMeshs.shmodel b/Assets/Models/ExteriorMeshs.shmodel index d7da02b67bbe497f062bb3bb91cc5f4db2a357f6..90def7442a650455d53c4ab25f2e3660796a60e9 100644 GIT binary patch delta 788 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 diff --git a/Assets/Models/KitchenCounterEmpty.shmodel b/Assets/Models/KitchenCounterEmpty.shmodel index ed1158389c75d4a1f1eb36ef2fc1c558cc374e1b..fcffa6b702f61deea64157a5506d1e71c77e51db 100644 GIT binary patch literal 10165 zcmeI2d$8426~~W;a4V5CUV^+bQIG=V5zq4zkVGB@3K)tA29*%;2q8n{cmu=&MWF~N zgwm9o3d$o2xmQxA8p>g^8Jwoc&>Ute5{-(nM*4pCZ>@9p`EkzW?`H0q`TG6twZCia zz1QC7dKow`SNeHD!&lDB<(gY~_}%mFoI8ENj1hC@Exbpux?BFcE_v_FnS4Wfsyye* zi>H#_v~gpxP6eGrYgN=y{iKqP>L-=-HtomuoK(M!2zSjNS-Yc$KPP3NB%jp9X(sl8~deGl{ANaTB##){s z?9=>3{r0#t&OT$U+#yk&qSpTNUguoedK>)ZbabwKL)vrkWVP>;c&uxF^Pl`|d;eMO zcR@VXYgVsKsumy56ECN!pRLI|^!l!;Sx#Hc+Nges_WI-=dvsmsNkjUS?%w^e*}AXzV~^2O2xj*n$7!4iv)gLOMVpJw&1I{M@uX^#>{> znmU(9^H~oS$W2)_`ds-t!p*)=N;;o^(Z&>p6w~S=ZrtiuCE=|DAbR0 zg=Gi#?bet)#r=b?bU)Q|*Wgo}PqB`zSD$<4%(CFQW?zfmW&D6FyJ>|p;sn&&(PW2U8ZxzX#7mZ@3V z{}Us(kZyG!&6yiGTT<%D<+cy5ws^cx{aK{?f)Mw@@!Ko6fOf>JKXPuC%_#t25tsk7w7dkE7DNFb;uqzW8qK zoF#kk82KfA@Z9q8;4MU+q};movC_TWHU6;f!#~+|)9;RE3pjrM^sCQBb=YHwGbgEW zslPrR=Cv>Usb~NA!+nVMI^3N1z??quSt@Dw`Cfew<@^nKyiSn|sPvQO*;^@iG~J)? zHM?%5xZk<^spekavya{5abKDFQcsz^;iM{;>%COJJ%=(P8yzkY~$)aYLCXXE$mi#K|m@MxwA~|t!r=;t^>0x|H%kE)*=##m~zP|5g$Zy+({AcEL2>HZW52lXq)8qKt z>-^8k-$VT%&UmKQr$4OsZqKgCu+trrV`Eeh;ujDsx)g_sw{Lzaq5BZ0tbq;wi>K^Ee@6+!w z-oM{(ynphz@3>zp?Ov$o`S5VNPwx{x&x4tVnfEyNfe&UK*!zax?+a!<`i|f43&!X7 z5wpKjzZdd(ubk8G$^FFrUg)#m%S+KcW$(l9!S99nxZm8npNBqjPt5=NhAH8mvds5_ z_sV(Tj#`KZ<_Ir<+`n&_@czxa(@tA#m-k-n2nDO&2owNJwH{fsi z$$w~;Onu};kzcN<3g?K=wS4BOFh51UQSZEyrw)B4?tc1DJs)%43unGn9P{(T-#u~G z_4D;n9(Cb`6Hey+TvhVw)rs09YV4vjslsH_@DyQ&Kn@->e5ZhSn(zhF@aKiE2>9@z;V%iZg}Vei zXqcR@3iE_nrr|llTw%Vj*fe~%Fw@4a;d_LI!XjaXY4~fx*KOjTw>Zi)%-U;&4Z=Fp@Hd3} zg@=T(rs35BIpl6P4L>P#77D`8gwDo45r0>BMEH(rc!V%UxL)|SY4}^hCxwmz{X@g+ zg?=`64G$A02{Q%W3>xM;^F84i;RmMS?+ZV&v1@pX@VGH!G`vl~|CI1!(=fI72-IW^ zH2kdayfI@myi>rlPuOo7CVz#nNcf>?c&jj4;OD{9rr{cabJLT(rr{mJa)GclL*d**9nA?DPr^(>uPe ToVj2czE>c&Rp9-g;ezl#2pxYg delta 3270 zcmZuxeN2^Q6n`NRG7&GLpoU5+A?k?Ft|ITFu%_imbbbY>b84Bk{K#CI@?J??&8gK( zYf)w&hCod5f?r+X1&xN9B$nc=X|2g@X#+>(W@_j6y!YJuTo!+D-gBPc`JMBe-+Atl zS(URKL7M${l1wW(;fG^8!pwhS!Y6tUT$cAknoUl_T3fTPUejE{A9q=1lVeDt3Jv%U zfFjL|i;Nnm=F4Evu;_4a&`vigGBhBfBosoxkLZH(zPE8@@?)Wj>4vS?W)t zzH^s7ra$uORy9*LxH>U7^ZG-&-7N8}O3qVWaR`Gv2L&Qc;XYC=n07;tJWY7kX&`3_; z?zo{v?L}!4L>Ss0_r1&h?E%qxngoa#Pb$0VW|=%W&ZV6R$W<|hO7wfxwXAJ#7Fi2k z-kC<@l|&eC&HXtIjoUU`tqCjKS93#T!CN$5l?Y92^8L8Lb6FJ|T4~&xHu?);r8JE5 z$uVG8p&N0+yx+eD#9$A8SP(xqxcr0}6f?+daSTi+-)6^2;J59so_(%P9{F(g zPwf!jd$8~xPW4NzK{{TG9jG2CF98Oo-|Ks2l{)=Cq;FA!KdHFqKr( zvYQsxPu!2G*%$MtI>y|4+8i1)-2MqLOQRiq7H-g-W|w1hZKvaKn9(vNLOk{1TwNR6 zqu<@VPVc@mK+HJ0UY~j?R>mJ+*-vPD*6C3rM+lq)-`iekUTf!j=F9ww(s>exIP&jO z9QlZY7rx~2%s2SMXBnVcGyuQ1J^tfN5!H2G-&db4UUi+;x5qvs;(uw<<0mhbeq>al zJfC)Tv5W&>oN-p-Bd4|KRi=?A@^c#W2M6-Rl)i0x_K3McKh`RVm=G2Ouq8_;%X9XD zFY7rY@gPJT7C6Ll4j+xdM+465EwTalB?bXGk%`a%61f3xXh*>XNl-qfSrQDR2DB%b)} zqP}KLjwoB+uD9QKQYU2ucG`_juSF}qi)GOSH@XG zj)psBE!7*a#_A0y(KkYj7Wf@rY1rq<2LJ&8_yCds2#e){<7DUCtefB;*Io3d7%fH+HoQL{9g6{&hl!cwp_I9En4wdV^N z7$vj<;QQ%?Qmft;uLE0)oq+c6@r-zzAq@!OBMu*vgyyWhYQc|PAZy4EwE-*}0IKB% zu!Ovfss$d)$+mZ+8Ix}5&rk2v7r%GgXWyv$i!MU%U%lD(72Oh0NWg;7Pyr&08gV-z zPf7qn_HpC>D{lCF{EqPUBaXs+!8FPPFYI@hLdAoZyrT^xAQ!}l;Y$FWv*vi7XpG}1 Y%!S^j206|RKvKl>sbKBaQO?l+0Pg0zQ~&?~ diff --git a/Assets/Models/KitchenCounterMeshs.shmodel b/Assets/Models/KitchenCounterMeshs.shmodel index 5c49eecb77e5fc1f0c50964e2a0395b7ec7fc3c5..243f69b0bdc9aca05c62c00fb803f9beb2d6d42b 100644 GIT binary patch literal 235098 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 literal 223223 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 diff --git a/Assets/Models/KitchenShelves1.shmodel b/Assets/Models/KitchenShelves1.shmodel index 498933d421d9c0d98a5da02f575de2be8edef931..8c31fff30c8432bcddb1f6319fa6366a2fd00d17 100644 GIT binary patch delta 607 zcmX?}J*!~DanpKs1_lNeAm#*OHYm*pr8$6fkiUNj14Dy7kckF}7jw!lDap_E$xOjC zs2*e}@g{}j7hpApG*g@sle3fa%TkNNLhD0K{{OFs`ELJXi@yB~3=SqxufoJ}y3sSw zDYc|LH8n3dBQ+;gKLhCi diff --git a/Assets/Models/MD_BreakableObjects1.shmodel b/Assets/Models/MD_BreakableObjects1.shmodel index 752d936d29dfdb77c67d17b04f6e78c023431dde..6d83f9fd823dfe2743dec9bb8b572236a5e8106c 100644 GIT binary patch delta 1340 zcmZ2=j%&wFu7(!IElgU=>-iZN7+8Q<7>GsDun>?hf`kQuY>+x+9OUmG!objA4-`fN zM2STJH8t1=|ErJhXHP{195p_T|WUIr3y%0qZL!K@^xEsO;kq!1C%}r2CV4`3{L>!Hf zP2u4?4z(I$%9XQ{C{B5HS`=yvswwrx$fhhV*MvG4VhYC{W)xHY2;4>DL)_<-U!FsL zK!Xwk8fdVe-YCc@;(+Y1-1Yx#Aj%*Pi!>}ka!7;y{o@AKsKx+IQS?PK<(*t3R2RgQ z#gE!hOqp@;G7=wT9>guesG-!R-GyR|yWB)1Z4LIZZw}a?JI1-RfN&r^cASGf)z_mM z2X+|5#9&zJ)-y0LL=Lesb}uW4E{I=iwwod)0Eif>sSWm+MncmV)czUa@+e6)cfJ== zs1s`@N{WkEk_rtgi1wV%T_{E-1l1 vK+FgY6DBCl45p(XB^rp$3YB1k((F*014?s3X)Y+u4W)UYG%u6}Sq1_CtIM-i delta 13 UcmdmSlWWyEu7(!IElgU=0W6;dssI20 diff --git a/Assets/Models/MD_FoodItems.shmodel b/Assets/Models/MD_FoodItems.shmodel index ec27a1bb3c1c185005c437b376defc3cd74d1755..4466830ede4bb1b32f7adca2d78f344fd9f3a6b8 100644 GIT binary patch delta 339 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 W=`qCfjXi)*-1`sD{oVxMA^!(h?gy9v diff --git a/Assets/Models/Quad.shmodel b/Assets/Models/Quad.shmodel index aebbb0a667149deddd7e27627fbbaa0dc3ae4497..da1214125a991c35d5abf38f8753b3b0d676f2e1 100644 GIT binary patch literal 236 zcmZQ%Km{y7E*lVoXb=c2O-un&`$0HUQOOuc+as|-;`TuC%}{X|8^|RMWGXHKn%w|0 cn}HZM#B_*y1RKPMh%o{MnIRHjnhD4T01(?1i~s-t delta 65 zcmaFE_<&K6kpTi&fD{`LvrLq=oVZD5;x>_qZ6<6D_6!US_7gWqh=9~FLzIGPCLkLC De#8mY From 1eab15d129bc6f47c8b3285eb908556386baaf32 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 7 Jan 2023 22:04:11 +0800 Subject: [PATCH 092/164] Root nullptr check when destroying rig tree --- SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp index 251fdc91..6e1ef2b3 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp @@ -7,6 +7,11 @@ namespace SHADE { SHRigAsset::~SHRigAsset() { + if (root == nullptr) + { + return; + } + std::queue nodeQueue; nodeQueue.push(root); From 67db3e636c5355535ba99aadbd482bf7d72d4d56 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sun, 8 Jan 2023 12:37:48 +0800 Subject: [PATCH 093/164] Updated racoon model and files --- Assets/Models/racoon.fbx | Bin 703020 -> 0 bytes Assets/Models/racoon.gltf | 3792 ++++++++-------------------------- Assets/Models/racoon.shmodel | Bin 404346 -> 603308 bytes 3 files changed, 904 insertions(+), 2888 deletions(-) delete mode 100644 Assets/Models/racoon.fbx diff --git a/Assets/Models/racoon.fbx b/Assets/Models/racoon.fbx deleted file mode 100644 index 4d823d9d32fd1d077dc4e87503058d5318c79759..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 703020 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�__Y> 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

!9MAnS#ia3Wk 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 UNbk|?%M4sE@ zAiKT6hf|V0?Yp#6f~@Sz(^*smcA84NS$93}Tk)9xwNh9YGss;=AwwfzHA{Fx+nqSr zDs-=3b*4ZRi^a8faqi@{KWVF2=~L$ygIN~NoUL*VD=b?GVCl$}5e~lWQ4{D*cqA@n zh?=2>v$*iU*H>?jUF+HRjOIITdUwC{_!KUA#e>Y73~t zmVZ!TT4f^|d$77lH&p-UP2I_4@+;XP-?&>{#JIw$kk4X;Wq%3n~-lnkVh%WVm&s5mr_>ZtWbMkIt_q^~%gFNB5eD7z3ccjkz<@YZGXy; z@6l*EF>U|lCSOE?UW9PD^E=-t;V!zD>Xrvw_upB*&HrO?Ay`E4r;uVsmM5)^f)1{m z!zof1IpbrBCK}cco?T6mAR9{fu^P!rQGX!Z*_q67T4dnqE`J&F`@ztF=)glakvDqd1JRr%mA?LJhz zXB256`ebm7yE>c&>1&6X1S5|9hG8iAn8n=KtRP zMua)C>0tx4usZ2s#6!XhRTE+j6`1s}0t=`%XnE+2khSzz%L4;~Oj!Ln<WXorfs42$VIdwsUq}V3lrX536%bvYHx78GR zBpp7n7^U2j3Yoglw<5GO>)F?wG3w@Xs;sf%$%PtEC!H}c4ikOX46ktMLRC{`Ok9Cg zK<7L4ablpXlw#nr-+{;$jrjR*C+-hBJiE$SuO57xQ)t^< zGaIRg#9*OK=1AhZeM*;w=Y@U^>>nO{yHA;a-}g(0=VniKKN2>cU%GJOU7S?T2M<^3 z8I`R4>G6*mRN2DnOuu^xCG@*VsPK6ye;@B|H88@HOEeY6hOtxa3!2k;Atu zW4v?~_N2Ef8k@IC4DROWWu3EqSC}B^?IG_d2ost3s*=sU)AFbJn|G%?HSG^BJx^o! zvQlC?X)(OGcP{SEcsR*G?f_2r*CqY#Lth2RPhMddpL`*#wU0w@pRLCd@D(6!6Tb~!q%F_d$O=gZt7C1tLVa@v3xwe-{{>X>v>`J zr>4ueK`(f5xy)9*C3c4n6ULu{95+n14Kaod{zL>?b?T2$d|W$f!61>76Z9P&yzmZW zh}J^!+N%o!5i0E{hfcdw3o(}6-qGj!+o3nDDdInK=+Gt~$f5s7AN+U+xI}cj z^S|fNsjQF=9lCW`b?EU5n#eZ*L7xbRj(nK+22On%bqW4*>SPw1I`y|ejJ|aLzzCBH z#}Gy&zupM7aZy&GwSadiWT8{a>TkX8-ZtAM{6O@@rGy=RRP zYFVxp@t+wXv>OXD!vE+KI*C9vqOauto)O|YAdL`u0LH2jK9J^zIs~X!zgamN*k~zT zGqK#bZ{d!*2!EMjG!Sgkx7cNkym-)r#qYjFDaaYRqB7tE^eyVkoP{%iGlD1kl}N zdT7$Y{W9~TJpQ9ZsnExFEM0!~?05VSoCFI#_SCsbz%arf(D|T>zMsKm6@vhK?#6B* zHS>)3?5;z$FfGQL9wVpV?$l%VB=dR+^zL#MG4g?1C{Cq#nhCbo6(m(DuPYA?nvd}v zDn=|PJz`zGTV(bFhlgz?w5F76`M`NG-wS8&Q|u18uOxajIAWjTc|YQ$yE~*SljA~; zvtJ7~Ww(wzWNPvxz=8iKqr}5~8g`$>I26Dl*7SXxB`52gi)7AG^e8uvQtCzTW1<4WB-+f0>neS2$SsC*6yQwn*xO=V{(? zh}1@MIo1s=*PbjB|8cwO*U$_{#(T-`V;}dIk2*5!uQo4tss2G7Pf_P`{q0ZFifQ(sj%H1>?Y{m3rpla=6^Q{T`p*yiJ* zYQDSHsDxB2A@BRusO{&@QPHQ5q%A!oY;y@S&eB(B^!J)SKBgl<_pyM?Zs)$;g`)Rm zcv}f`Pha!W{mg~85a8QV>pEODBH_NIGg((X`zNURb6e&SC`wKvNfFU z(;Z@~Q46}JBITFj1*~bY#>z3ag1i*dhwSkTQhke~_@z!K_)`-NcRy%k9v2FDRwe!7 z-13jWizNo-8NOvkL4-rIA^7rIt*~zUgc@Iko9%)rwzYcx!*J1h{?WrvBMij(+mbDD z^U{SDuCa^Vk5VCwzI@aDmUEr2VKRAN_^-kGq%@O{cqy(gnl7v`bnypEp6o3>6NIhk1^$l?({1%oP*`5~?r6v}*z_=8@i^a;6b)C!CJdq zP|SX{h?}=@peX;59Tym%)I7PSnE!ii9KIK(Hm>Ak3pZ51O3-tOm9JgH+Q#XCrV=uz zzuGu2AlRgh%Wy)TM+5oYKet_wAp9=bm#^|fI23r==NX6Iozg6}lu%5AkL1}{T3QNw zZDV>I@wp~ZEK#Qa^^fl@)+cla%S&grt$5b%-UR}qulCuWXIV6MGeqyA?>|R~Y4?FF zsZzxG)1M=1_vuD_U^C?M$tfWz7wpaOy0yCaKl;f%TT(^Y-B0c-%Hut_q$o$wmjgRt z|K&4`LGJYDv#b{C-6Y{T#fS!_x%*&t&U=lKw4z@;s0|*z%-6-=WOzRs(&cfLqK_mE zw7J_A-OiZATSNKj18peV9-(|+-SaF@?xOZ$fc+k($q6w#p$6;VRgv5| z%-DS+!9F|==TfuQu`tKHzU?$~B}N5f4;%_UiW*(pzMDScWU(J@2>4p$=rIOAv z#jvz!vJ`!?7pm{CeoKpa32kKg@x4giAuUP>+Dwr=WI$sw)Q5x5SE3Vmjq_3%uh`nX z66-G)X5vXgnX(T@hbU9?4=x2D<_({MW_0HcrFPb~K6>neLqvXdF#yfwg z++)%RXFFwy_U$nmm`Tp$iaXL|c!90`p72EPzF-BBPPk9PJY^dsD9WwKK9I0H0Z+gy zCpd9At;a^1^{qN@)9LZ@loNLjXgraTH3+HcRH6TP)o&ZgTo>scIf64sX&)z?PNHWZ zxkt-OTO;Ae*L#`OqHbYiLNc}{=d0GJP@Pchf(Mu)m~io=)MGLaJ8y6|`PPC%$is+v z`ioQx)0Zz6&~+$XynJtoay<3^&{5?jnHR)H5)23L^p3_g4zeCAn!h0MO|`Gg?aLWi z_S?&X>U%T&y*%Zo?0B zC1TEU&p0gV6HRrl3Qd04YZRC8fLVHOQd+~8i_3%d%vVVlY5UIuqf(`dt%zOZMdRZPRS0`#RY!ex%F3_s}&V+{2qCYMvY2g)AhD1Co+idG*I&g&NoiIOw1D~2)I;3I$SZs{bMxQV~6v{``TTV zEMkLf$8B)z9Ti@?S1Dw;Q{ax?;fz1uED!;scW;^mulE&1zlJ&i}Wj)57=3FsouV5~& z9_A&SQD1VX~E-c46W0T)Ash6*zDI9?x}Ey_KA%0U5+T?oRmitYzbjO>2 z%tY2+@WLdrJw$%@v7%*ZGS-qBmffn0@5h8gK>pVjm8-Kr~ESrDv}7BTwl9z&3C^%e|AN#l~llpLjgXQ zvD{!?*mLbzZRs7#GQo-%{Vy5aBrGDTS^m=ou7vTaHuAg!;x@^{4S9-_qG#lHcp7|t zl{+RJe)aP&;kmA)(DCWXQ;mJ+OL8k34qfx+$evNp(;m&Xo}gXgyJnlv(rnD5WgZ>H z!Q->M7@56oVxYGv)`%*?v!D0z5gJ)9<|ga%zJT3h$0|<6I+j_pS4dE`C)v4?1*XPy zy{^-WZTR%7sqdYQdY|aBKA&mY9Aj6nIA3e-nwE;OTc$;$P9L<(PBs z3(`_4yw4o%X{hSiBXZTVr`Ge9k^5Et*k0mqb>B$_Kf44^*$>Y?rm}JiycR1XtZ*dT z?bWPXF141*qG#6~JGWq-_B#%4ins-1I9i!qcX;Z?B?wdtSjx$Xb>`x!v`X}=$T`0M zx+PE`Xg=4ua3k+J2TR}Hy!N&NqRH(&ZeCOwi!;S+D-sz!1>_bvsiPSyg(`VHh0n4U zXXfIlI3h9^f92?1zRG^AoT0<1$M&v}0pG#yXqzKn0=?-!*@R@N%8#E3OmJnBbk6Nz zWwmV{PHQ%J8CG8WD@e=7v+0)t|8nHY`&>_)Q;M9W@gA#PtpERefjG6vpZ@9vN_(yr z@xP-Ni1D2k^J|Lvzt;=A8-(lyuHW~qYTUwu=mkRizU2+{%r=}p(FVOpjNLy^AlRh) zH{^{xPZiq4fbRZrgA<^{WD%Pf$i69ilJB6vYhSs6t7jMZ@in_{i7^Gi@ssb)Io>5w zW8=TOSIz24Zf*lrCQT7M{#Qrm+>X&vp}rm4uEU6mZW5g*D1Ih=!t{VV75y_Ap@Gj{ zw~-rJxgLJLA$65Rg7xa>+ix-xgs6T>F~|GSh4FSu(*`q!NYg#2wR_CkB9$N@^i!JS zQ@vE|zR&pcyot?G??J@|NV^W%N-WuVAiJa)@JigV=JX)pCc{au;!sbFos)uW0hTZe_2X z#Im~{>vLZBlhx%^il{vOl*>D^n*PrBY9028h`K+vS({(ZxSf^KCHHOOpw-0w(Q)e0 z30#`=>p`;-3bXg?8Y`H%Gj6>!*AozE{J`$ugSXAKu}XN_*30`y=SfN4%2R>1H$tO3 zPaE-8@zLK`)O%Qe-|%!2UT`L(|K-f51QsTer%#oTp!X>!eu`w@r+boepAO4=irQW0 zIQ>7~)nV(RP+&R`QNhT>bt_p`kDm+NU#z`CD%B)m>34flw2!{1Fg#`w^PDO$_QqQH>z?G!vejFIGr~7^ zG2OgiN_0-nR59w>0FkK1GX5i$g=;qhkUzGY;K35VJI?4In+;l8x*^OpiEO6B4vM?;=xQkwNr2C#aYbqC_ zCF0QJdC;bWbRtUg5T%l zD9SzNxpW`<+`CN^_v_e-S9-qpFDfd|gU=6%6wgRFxX&*qoBMy4cy?80kjI%XGp0bN zkw!@Glu6Y?JxlkYp95VgMhT2V+3sn3r`6s(yftxQA@+FbTjsL`-n}C?{F8m^=wI=R zFX6X|^yx17C>zw{7WF-5}Dk7jZTa^c%ndHhh9J6n!O0Yj@<;GD0Avahq9ux?I|MfHz!r=Qs8 zkMZB9VSDdl?@jMN7=6~ILP?^n?boTzCm{ zUyQR(rdHJ1A79SdUP;ZULp3zFS0#H<*fL=dtfzj|e`$%+g-v(Lcz-7o*L@{d7V|Kr zZ#m!Xl9;+hnv%`GFxDhTs5s^;OFM_9moaPmXR+ty=09;F{WN;=9H z641xA@76;up18bzKf#9DHsi>kUq_dSqZFlG)!AkOyaqbiSY)UQ8?vIFPDm;Fz$`|@ z79CBMWfqOx^ytc>tm^b)WgB##$>$I%x9rrWvAD`R+*jBcchL~`olCLNs%4O5O5waH zTa|F)%J2*!@q~opY)q@$j+GqtN9V1tarRty^tl(Kb=$eJTb4!UMe(B3!o|Dc!#js; z2Gbp1JT~o|e8hhFj>vmqf3f%1UZum7R%Xmzd%DOG=;m-A>FNnBqOfWTOmL4D2%8qM zR^8vKCCo@L+M;Nbp`;jmujl?zdsYACCyI8hh2o!tOf+wM)Ve%*=ka-TPb;(9u*O|p zw+d6i*o$`xxcd6Ze6zEqZ%!ZKtx0YyBpl{X+O-nv+^@^oByw16c)OT!>x&riT$B9m zGyYU>xp2Go>$kV2`RM`DCKt+w$J(*qNv9{>>tk6VaOZOMwZKdu)X+og|bF_DUxYtr&hx1#S@yW0q4ICa)8ot&v zMx~A&X9^9F$wzoQ4rFExd*4C4<`w~H+2X`5um!IqSM|<(u7pX>sl0mX z#`aCI_09D>p@-uF9FOxXw;gc`<&B#m;jlYqeU_d6)ieE0KbNrC1oi`-;)gr$55S@? zo{BLN;;*rp;CA$LO|e*1A%77oM8k1_yvMTc-0M2YOF{K1N!pD zqRCqcUuRd~HyW&r9^ST0-_H7WhR%&}j%aYne^U5pyIIQ28QhfvV?wyG`m7(Azn6cI zq#Sj;HKW?Jpx)&kF}Zx%DYgu3;QZ=dQU0N(+bnX%xp_f##69k+!m_ecY=zi|!X}|s zNtd?cd3B_%Nz1)r`62|fmwVk`OfHAHdRFBT8n&*CXIF6Cnfbtcc=Uw<{haCH{-x}h zmUnz}m(oHP`P5(@nj_32si~o(MjCmeGxpa9U{l+FoS*WRe!}mKKj0&BF~8{ivC#?H z&On0hm7OKb6Aac~rA{7rPDFBoL^1fECSWeqEOpyEoN0v!GyATO3)aWrS5A1f(NkCK zjKS9_+5RKgDNd7?`q`z<>p9|rMDBk0y(7Cii{{(%{Z~A68J)CKr|=1@RXcH6qt6Md zbUI%M5f!9(vtRJSv0LrepMTyisJ-;!q^5gDfYZH9Ji!b7?(pr3GpX&@`L1lIe$8A) zL|9Ea23v}9f-|u(QvW1=4O ze{{c1Wc2!Ie@@AEWtiZFTP52yZGFZU(q2$l?Jx7jPf_>6=l+H31TUk)Bb;rM*UfXu z=X0X&CaM##B$KTSr@XptNwD!joBB!|fSbe`$|fa0V|@rb>F$)G`;Ar+-+9 zJl8rCz}Qw!O)@iU*%>pX-mP3Bp(x(0&nn0>L!uzJ&;80P`r0On&XZ=teCqPi2U~pG z+?akf?U3w>R>`O2r8#qp*Q)oxit7GfS{ig(aj_<%1&?` z4eSCWsii{CSrb!bZl)^TPnF3`l}t>%V#oiL>stfsw}xYHxW7tV=a-&+=R4=Za)D#F z7W4O8muS-VUuyF;Dg1V3W&0O#UR&nC)jQn(|8K(KjAy?imK$_(&qK=>Ko}FLT~oyW zj+=0dFJJTox3TC+t^d87@Y63a-Go6Ev*X^TH{l+U z$g5@u-4~#5!XNHL-h`_n?hDAiE7?)_9>lBXmJV%tWAOv}*Ai%v7by?@9i=njATqtO z#5?%v4#g?m5?^VY>pO_=XrJ|!JJLxKG|Na!=O-mg5)yK^JCXKivXtDRPa%Pm`m_v@ z6|x7@Ljs1nX_<2>^51z@AJ--+* z@Fk8(?R%Mn!|PCuAwxpf@8ur*uiX-~Ga%Zr5Kt{cH&y}1q zd&_H~YR$d-9dpVNYZU~0cPluT*=ml3Tmj1#teneTg^A8nbU+WWqVelhx}Zw}qZKUy z^Ch|meBPEY2=A7Xq9+NxnaIe%cT7t9_{GrMnTZTSloe7;oT0Z;^ce*BDx}$MLVfbO z8AR|{r0Di2-Hazq;62Ln|55iAU{Q7JqqvF^(jXu?bR*r}3Q~fkgfvnD(jC$%DAK8b zpfpH>w8Q|?Qqo<+05dRfXTbW-cRqjT{_nld|2gY&^AP_m~vagUb?f zLlaJA zPn-d|VYp9V2*RA*C!Yl&iSOf!gV3AGs3_$k|H9PX>wE{cCVNn)Y>kDVuqmM7K7Et7!LRj98-LiK3v58)bc~@>Q$vh5MO)$Qdcb$$&*6OqBE60SqYXk){ zX75yAKGDcS<}Hx5T~|%?7|y$jX(jW6&1SQbwVoV!bF^0u;8sc`#4vtq2krvnRliEz*a=f&lJ&(RIBZM1L7b?fF zd78mZtOrE@_rdhb9AW&GYiQZTeO8i+x`WAl6>rG5)+FSG1}|K#O|MC+cnqe99lQa+ zh?^ylIbTy@nB8tAZn-PRnamk!Mz)aDEJM$k#B|?`;-v4|P`KDfWbrpy4neVdtKQ6BI(87AoB*y2Ub}pD z7%sKRb)366rEu}ZcpKH7y-<~FcrQZRXsMXG?<(JX@FILTmx^uet_m{Oi?Ci(Dwc^Z zWh}=R5mSp)?2s;%t2ZBne!7*#*v_bortlzqC?Sh=iBaVS^MkO@8d=OE`O2729z;wI zXWaqkt00jKg?8{{F(I7U=?;aDR%EelS*oB24TW6@{VwHPGY47huvOp{WUkPbf0gS1 zulp=upUnf#%y7?3U(>Og5%=h{C}@@@^A?$rZ%j1H@s%dAZJLOq2y{_lIR)jAEeJ3O zbTcVC1(&BTh!B13qPyetyztS25dX(+Han+~x~T;*Y_~2N3Q$m<$buldTQ`dVD7dDN!kY!Zh3|9|$k&w+}#;7RxS=~QeBb)1C zz5?$%^}yueY(D3|iLzf+Wb@iuDhf}2@sEC%&8@$xz+d|%F#RB#-+NV29LnwI4;N1& z#MVCq#kYF|I=KHTNd4}75pMb5)v@B$lIYbw=G9v5)p>N<9CO+RJpELB+Oc!ml5yHT zdm7_=oG5$RY5b}r!Hm`728&MnlhKk_&P5hTq!VrCRV6QNZ7fiPC)(`}OA@_*S_&kX zkgZ&25Nx;VfBMQPb>$jSO`F+=r!TD^tswK)wA*e!P4t}lCrgK{AYA3^aWZ>xmSUpU z{(r>L|5w2H9L3(r%!IYzlJZK1yVw0Gk^Mn=MZqiGu5Kl}muT`PcOV61V^-v&_Blow zu``F4Rh7wV5VoN6%spu4jhsT((M^yg^rx(o3Z%XpD0NF_7?}X~PQ>f0R;IJ2xHvS_ z0XH9+sq5v60znkkr%zbx%%8k9f45`Uvq<2-&O4*(lc!`(T8s)4SnLw}4Gra@PFDC{>(H142i<)8q#R z`LrBs$x66>?aAy*K}#-ok`Q0f83H1MKdu>V4ijk{Ee0GO2t})hBnnV09h#7V^Xvnt z@}As>7Jpzxs_m2I+%{jw5^JiZnU#ixb!76EyU>=}s<~N6RcB((t7cCI(~bffYQ-L( zR){WGFyB!&E=)3;+`!Xf0*IjuMR!#6wg3)iJgk($--aGZEtt&rl<$wft*A6#Mhw%-OxG3wq-yDD3;7 z(CWZaGrbP%>oeyCbmEOM2R=cKu1?EoDkbKvP6AAr&u7o<1S6j=o zZTogel3yWDRC$Zr_tRcB518>zJ|CO80rS3EVK{a}sd&E#*nkvSXW^SJLeyX`W@!}tm$Sb{g_re*TWZ?!{UguWwA(`&p%R~A~xQ)5)Tv7i%6gMhj^*3_;o5Xqe5jaqVWX#nhQvHzr8&q5873_bip!(la=od7VZDLHCQs@Fr7VrQyAko8FN=J^Ipz1Ax+#4jYaj zyLu0ircU?MRkY6BMpw3foj8ynesj`#olU{GVSTR;?O{QvPg^NJWi;3)PIR+oKRB>VsT8&El-F+8!R|Hg2;eN( zWo(4*gk^99&dws$>`%imU}}kJGQQKFVlosa+e3U8@Pj*e^JpvdGUlK@=E9@k<3}b3 zv(ZJZC#x0lFtS+eb)saW=_+1%C)iA41Xw6NRd(AxV#P9A1S43<^P`x!V9IRE)U1P; zZx!)y9=kyDfpnDJ-EI9A@Vhm#55i@4o?{xGF`hNb`V1-a3j>zy&T%WM&mOlmuB92- zOaL$xJ6^~N#i@G)Kx2-$?(m^%d^x{fD;U`XFkJQ?jC#Zs>#omgDHgY%dNtCw&+FXA zwXKoXcUI)wK6F8=*A*Q~jm4s~ZO%D+u-00in46)+;R2fICn62-Xt3j0J&eBo(7D^1 zGaQ^EfR(l4*0#KWW?E{MX)i>kk@uEs=8SQ*>gg1i+*j77Z2S&|`wa(y{oDEJ8P_Ho zV6UqS={B55%fuGyUWgT+ZyeYHPn}>ppXvu*KQr`carh_#80NRVVH@@`v-p;F)Aqd% z-?^hnq8AYLClnx-=&K{yeZ46n8B4C-Nb9Lziq4bvMyypR*|AnUbE@6i9=Fo&e=^84 z94Fve;k)t>my6IFr_{3oeM51Dv(e@1?G!Q;Y5cQj_9OCzb&HnsZKtY2_cYb;u_$Tf z{o2{?ov67+dAJtCQ8;lYNtY+uJ<9&7eq3#2-kvvDM+s*`zJ}u1z{`HYN341`BB%E} zY!3{k-mq|NJSnU>d;Q5wje_~sjjgYGB@{K}C=#%jn<-GBxpxFruXqjN0YBN5>1^|^ zm>$Cwk5Lnrd=G+aN=(}u_xp^;Espv4bJA*`39L`gEE##lY)JPmco){ts8aX{V?cxw z-M1mcq0;B2Hok7y#SBL~(cZ9}@qnz@jNK3{i?J z&P|$`%)|Hxb=yYXOdmpAA|6@jSd3asD`~FO5>ps^clAlu2z5Ifx~}?-Yf!-o_eF2Z z2g`c-EX$xTu0`*QFz^}I-6Yde&tsY&$m7e`)VjKG`-9VR;t0-099VKT``YSF#fF0J z{Z_7>Cwq|Rf=!PN5~VUdf);Ka>9@J{DP=a6Qdpwg^#%0EZSw7lZ9a`o1bj(fQvd4z zR;05V%R*SI9J9DQe&@MizmMBP^ra^Tm}O(nZO%RWExxiE8{|`r==iQ<7&qA7vV>|% z)#w*Uh@J^`MA_f)PMS6n5lI+Z>ln|N134+pO1r-Xg1bb9NlFcKYuL%%Xb#g3;|#N> z8lh}QIc9v>Q>kNs6AYL(-)c3M8ClHZrZ)iRsSxm5I4rCw_s${PhxNKCpv}`v{nU1E z;Fop@*yuXlIH2#`%|Yr*X4~~m^=)e*yo94xjER1qm)eq}sa+?o>fNp%`&UDb)1qTa zr*72-pHL2x^No;ZtJu6#6nff3A=`O8IV`MspgbMmo1jLyeRgQ*o9NFUD?}TOZg8Q5%3=WF$L^ouPJO=Cb z$krb-K*z(QN3$~))E_#Kr>$?^*H0g8avL#T&Jz*`BekJ$%#+H<&o+lO;hd#4O07#< z?g1)}3HAh`1j=5RDNvGmU&}qwiep*@FFY0>g^#br!U<>T4DQXgKT`3P8_Q>Q@ zXT7@casI%wmkKJ9b=5oTsjxSdVMt?~9?jVdJyc=OVGrEqdQ*0FrQL8a(Xo1{lY3t8 zdCS3Gz_G5}sc<6HV1CSMuf6x=V58{RT+T~~y)l`{@D6x7_1J;$OCG*BFg#eRBE$cS{Z$YH%v&%RbXr;pb%L>o?{PO$0 z&^HcOU=Pw14S8MO8m=4S`8wk`YYJpQ*%NYR9=s{QABh;&ufliE1>A+1*=3zM>g6|m znk{HbT`lGafcAu+Il>B>KH03ZYXw4k8dYF#WPAk&4bPm`*=e9XO1Wn@)AF0<^M(!g zZh^JP?!gF-isnvUWHp_ttg`!(9*KXGUT3F+Dn^`@Wj95xB7TOQan&5{AIbQxS-|gv z9;j-UK>&LR^e)p8!C898eZ;$*nc#Zc&(Z1mDmxG+%{Z8`RDRqLmY1|+n0L0%vM9`> zen{GILMh%j9#+&l-qhiuaFD3i1AB=FGb_AT2cKzm9JD9&EF6V?*JDKqsdAOw^~Ed_ z8)6bSZO_B&RpYi&SJ~O1J;3BoP*GS{8k;2eT;I1+to5WzyYtJA;^*-scY_11u9e1N z!&fnfA6hgG@{Seu_a#1f<6*EtL~mLY_>+~LG{?{~f__lnK zvYL=($mJVoal#wlg*Ua64>|Q_sO=%J^Mh(HN)Cjc4J9s?o+jVOwD%&r57s&c3>k7t z`&tbe(o6fw4jOuN3mk4}PFN7WIpc~yZmd-_1Be8iP=gs7tIv?!?b?0he5Y?acpves z)HMZcsEFtwFCC5zQ*E2Av(IL-9LBkjc&mPiIx8r6ihXD(79v)Ff3D&BM&$|p(^-P; zT$T02Q1QvQvwVo(nWeUZFHfVAFHg~1agtXJ$ayfa`(9GtV7 z0hKyX8Nq_)y%U3}tB0JpCbq)rJmXJ1Va!KIVlgMa+)*dKu54g`LwWDHJA~65$sV5$ z-lm7@<@0Zf4U(=rh#A+)SN~+W{^BVbIQ+w*$@I2tabB$f_M85_?1)pT^-K;wp)N3i zcfvZ|pt|zGFt&%{v!<_7X>VOXo&<)Ucdxebqhb#kqAL1s3g`OBbiFnFHj^iYI2 zS+Gl;A-~o61S_;}B-i)(k^}1+kz)f_XS;n#nUAPslVoC3(w2Sh?40MA9GTWbDtZ3v z&fQNkNz?$(o@;Moch~BIToOV?4zPzY!8l*f_D^EMeM|gdWvy-Ex7p5G4BNyRJ5Tsp zb2E4+cZGyMe(2h-<%_khhkH{B{*v^~H!u3isqMcc!MY8b?0Y-q^r)9{~pZ2HPbOC8clfsV_h^fc}Lc zCjr5{FB2l2iomr;_~}3@Y`UL5xqYK&6*ofT+S+Yux)>3q0bdGA9*}y1h<{JJxn`55x!A z6V)hal|kntE0P}PYXWZ;jLlMe#qXuX<RZbesyWe9g zC(KZKPW*OjXpeEu_v}0^z=7#@;`!=VqTY}Ih$;u5(HGsPqmwzG-A(1@Onksmt`Rw- zHphXV{)p-Lou98F zd@cTK`We*wSAgt`E{OgJ_g()}AgD~TNf`-5KM&uj@7LcIXMk(E{(JRwAKzc<>Db|q z_3K!0&;~xD;)*%}{2xM3_s65@ao<d#s3TjDZj&D=Km%PBKrR)FxcFcs~`bi2Fg{Q`JXUY z2!D(Jy)bB5e+h$}@W=WEgQesyZ19RJ>O}B=_YI|O9=lkX*!=U!bfOQJYw$NHP4YUzT$?&b^}Cw% zXfWW!>QYnsHz_P!fjbZ}BQq4jh_KU^!EgPgw^3$wh@?!9qm8|bmjkzB9*m@V`I~}) zXDI2j*xz-?zmEDLf(`x6N*b=#vZ63$(H(g4-5S_aXdq7cS7aghF}zHohOk_0HG~Q- zj6V1(hAB=U@h8<_&Q$IB+ma$Q2s3jz_*uFfVrga*;!d~kX4F=>+%GZSa;6dY(#Grb z(nkoIa$hJhFQX6@nZoUa<>407mYCV62Oq5*lUsqN7 z?@I|^KLupO@k#R+@Z*#5?PO&-UDSl-L2p=TLY5GrlUNwx2F}`jB%@`~QlsVm)O-(m z%@y)95izIWCjk2$qd|}6s1rZ-5`1us+JFaqs{YvbZ}dA;-|ieS68L>LeJ;p~Hbfk< z+#_uQq4@3-`GT;|_kC3ZP~Rf7$Z#8Rj=wD+_AVv5PTPgTVzDF@ZsTro$n-8c=_? ztN!sdoS-7U2DVr3kAM>-oiWa}RpuB9x!i4)Jj0Zj@r^FqiV?i(T!yy2O0-J+rc69SFYkTUv@FXAYI#|z!rtq=1A@#n%@hOSepdy7N2x1g^l~1xYU2=mNs}eGv&zK{6x>q*)DV|INEnAXF)c{i}gW34B_8nbxyMxFp4c8ix07Z{1Cry?ERcsn0 ztv8G~4rH&qKA#9W1&i@7WGULEoUHT*=zl4yiq&5*WWay8(43L!#8Qpl z9$ZyZLiENAe<`dgguL$=NlWak=TT zquGR~qlpcg@-}ve;hI}RE3>9r0OGc{zFbB6mg+`cJ2@Dss3@qSyJZk~GBW+%{;~e? zIJZnq4h>X}NZ(<{W8ujnLBDmTSzdSkn44~d5N{#BPDQ`l4E9Q^b8%WO_^s4%pMqYd z{qRsW{0L=Zr@}W^Ut1sF=9dbqk@UlTw$mGTLDev|j8RtDH7gzU5a-k9t-M1|a{YH6 zdm8sTlb=5InH)7Y&v^By9ddgu{;7diIFP+dUE{fPu>4D%*~2XXi@uGW6424IJE(T! z*shY7@aXN6j2yTp#Gyx8XFM_c#5p%hic25M!oy9B0yGGDS5XYkS5Y?g27Tz%SPrCp z^DQQ(?`=3+zruY9n{Em9Bo@W2h|s-u9yLZafTs9N6-z23qUnWZ^SKm}%=NCZnFY|liZS^HNfR_M553pDfL%&#^fWRluY85% z*rJy88ux-~OysjRvXaRf@fZ5Oyw_lqitfx-ycW75n-hk9p94B$2?63??+#z<$%MqU ztK~+a=3S=-=-~N()WSM8==*>Q>U_}3sWqi87oSnXdxn1C6#+A=>0usP*UOML8mz&{ zUbRb7T%0#zRoghTV^`Zo=h!Lc!E-LpAHG&r7(>!`_VHn`C*h=FVwc_Zv=1G%!H|I` zT`4`Gvk6Ym^BagA2__$eq($8cOe45wS-MEx1qd>3c)f1l!C)F_+!Cs*%TA3hk@FC5 zvm^G<{9LcENzzBui(*-p?mcp)+Vj`0t&h*`7x_i(Q|b5ZOm??7x_5fdW}n2kFIsHw z+JTk_PjC$PZtrk!I8SZ*?!5K=l+J|3!b3=Lk5CF?@&dj-MG$7aZ6-lw+K7Co0VdWVT5jVK>=tkYsCmx1LgLne=9izj{V= zNEolAYimczmbQ^Kk|3X%kIFe)>7QZT@M`p&ubqh>kV^CQG5piOyXn9_?oF?g zo7cYFm`a{^kQHG7vCKH3xR z57x|PcYh$-C|&K;5D0_eIegHaqOz=76(3kXw9oG>v59KL^GWKW@kpSGx$T4D-KwV~ zmr=%q`H_KbPGmhE;`%g$RXf{`vypDQyCezIF~zga%6ISs-Hap~H!IRC|F%jjjBR2w z@r7J(={8^$sfj8C2Q#89ONOub!VnH|88nK>D&p$wR$j;?glguHV3Rp_Xt4~#$-A}AbRU#DPD@?xZGAW$voR_S^a=6E zZ2%%Ky4TdlT56AwJvZ=PpjNB971Nmb(tBWsdCn1Y0_=zOT1)=}?~1J()@Dah)?05- zccC`B?#y+3+dT^6tRv1H=I!hGY1LPBW;wyPI&%O?Cxpo9?P6)+ETQXWSI#m@#k}{@ zH`oZ1cigJ$@!F!Q^w3<$LLh?&+vb1@Vw9#m&poPR@%?E9Qze}*EevUS0a)q-;=O~; zgjPP>u6NWFU%ti{d_NpmvF=#UpmyrT^CI7OKINWi;*f*NI*{WOi*aN8+3|Z_GD>Ex zbJ0*?8_(FT%4XJVktB=tV~o?YuFOt|>Dar4T~C{-M-TlzIzZe|+CvII@{0~ziSHjo z%lg1r_@}tfhg`TPUV@op6PXuHL`{0dZiCOy--G*nJH*IaV>6VPWv02zugrt5kJ;=; z)T);IG~s#Wkr1TFyEHgCS(@yk47YcVY{@kO*=ev1aWI0FQ@Q2^H7k29rK zp|evo1Q5KcPJc=NJ(;XLn*alA+sX5|P|%1q+pfON{tV-O`E)BshD~7SgH=z@r}nHI z5Yp8(7LJ|XcHE=S6ne&X6Z)M-$NZS>Cp{GBxKg^Cq)qAhn5p6QP$cqX2roZOlqIV@ zVoEmjh(%uCCNV;M)VV~mLHB;5%#g@5!E8>kw%;t93&X8ps7wnxb})l_YO)8WGpev> zJyBWVIbWF~@cuy|p9gY(7@ZtOp!bU?!4)F5*pM^;&OMi95hIeg>P=3v$u}|ltIm_? zBH9rUqn)otV&f;9OeHE6twMwZe4SfOs#mxwkm@dJqd&_`j3_!UW6ZRiT=@2Gu0anH zMKCholb<>N8$_8hF*fFcZ~YgU$=vsGnUtLy2nJ6AfdBTQ-}muzkm^x!M7;)kC4yfy zTFEf^$q2stZHj;9SLe#SnI#aHOzqTehG&$u7$;(g@{a_smw^9nF1hSSDL^v*{Wcdp z=FZ4l5&ex&Q-6&6i`yDMs;d}?@tNl|Qi!~n4{zf8_+4>5ILG(j%ddWH|9AXq>VzFB z{5gN{tLm2a&NilIj{j)k-1&VC{>HB^!yEpWEmu+YeerpA;;J z-J@1PoC?0n5O}|=i#`PI%w_Sal1Oe6JpAx;29YIVgnu@Jy=4CSCGs7F6Slo1cCRJU zSxAeD&t8i7=@MX?yyCAmy)u8j*P=LeacECT>*d=!{yp68kGHpR5pI_*!Bcfa_EzVZ z8VF1D?FL6E-eJl>g*hrR&mLz@pGFJ^lu}}TAGl|$y zsdMBafVY-xM+u%PMFm}vH%pbN=yyGb_CnIjEr1iEb&;I74cp@~9j_m$J&+oX%3^4V zCvKP7*gMDweR3Z3EPJzniE`3kD2CCIEG4XdmXg$GqmdN4$+=gu(P2wD857`&Kf(y) zqb|I?PY?%i-J}U`VX~5$R*zGu7)4pa(RrYM>tLunKW3?luw~m;L#A$Lpi+kOnfU00 zy*E}`$a8XfeS)_>As-8HlVlqBZ^Fh+^zW4EjB707TOYomZe-f#nfF{i4?QlsQeF&t z1DFHeot0gDvW82&ar31NE`Q7LiD{1C`CY+76Ps_(#%+m(ahu$_c(R{Za=GkIb?@?T zOB(Qs-dySBHZILr^&kR+SdPk>lwI7)LaTrz2P};RQuU0t6C*?lL%xY^F`~>@+I!QD zMji66@G_EVSb-DoR!d2<N8RT^;!MrQ_>CcKz%3r|yR%hAfn|wmJ9?6H zYxe>0;oGkj24X43P#yFBhjuk%3bosiD=Qs`=YfrjMAbMJ49IYjH5=Favzb4o@PY(@ zxni>I&k23^-SWi5<^?0bsR%6Zb-b7YDjW*J;@0?$OvwGlb}V|6KS+Fq_y5p zjb-Jepw1{8E^0j~0h|*C^2X`yy%AIWn%w_&^32spHw?XHNi{?x|}l?u^hcGkdFg zeS7lawdWoO?AhqC8Ar;kO;kIp>It3Us6q<2A~bUh_QGc$uMTEp8EicxG-|rH)GkAw zeq9g1QzbBdMUOt(f3~rfD`tMNb;Yi2M<^bv7I_)3eJ1djXrdZTII~BdG}NY^B_!Vw zNZ)Dz#y!KYsWrkr;J-tQxAlDEsR8xMDg7!yjyh(f?D~1N4cIjHjxneT4-&fU)dtyS zq?`P3dy}S?er{$sxxI;UTcR*2-Ui+2?R$TQyNUefbB!Oh?&PB+N>JQ8if1F;6qbi#J&^O6epQg$geZzS%{YE#*%RvdV!x;~`Z z)@kHM^DZ%Adh_x8eGXA`#?vj^VLc6S?6V}t?DYKkYl3T24Y!=fIrVmZyJM%#?Vh|M z`vg5^lGPUHB3hxNFf^2{%D4X{5}H`f{^|UydT+V@%NU>Ians)T5q;}rZy8VG_BGj3 zH~D=5`HWHjcO*kP4WGn&Aa=cIbwemlG==2jhDWo(PD_m~`6@De3uzlkbx$e0s%E3Y z&aZBeJrh@N`$Yx(v;05v^u-MXokO`-M}(kzuB7ZO{e@6jz15DOkLI%RFO)t;fj9pW zV9U>V@nW2WtKE$Yx+!+Ir zr;&-)t$C3fWgma1ci^?~>APiXO2BhJrKcDDD2Wg0o8jdjDN}!oRsvDTUczgJXU!i* zF6b{r6(SjRSCUG=^SLwwDMX$bvGR+aDuh!c2=tGjO#l1NiWz*qq^JHG<+nU@garOL zSJW?~{L0!G+q?cFF?FlsasmEEOmV{-{y4=OMYkv&Hc+b&GycIcWjN%0z^geaE)cM}E6cDDiLar*LlMs4RQ&*|*pe-O;Z( z?2OWL-G+hZ#P>2N)-}5>pUMF~o1Z4tKW1kNuSA6YQ(+8rfcvV?(5IzFc{946$XFSO z#8zYD=rotz+vI_bYQ~a<1~#ag=nmJ#8lAMwvN20ii$No$qoK=bkZl#oeG~r>FU3bwk1ee^c$9zfTly`kL=wr{#V_)4lTqW+3U z(<$;B`OV;`Ay;@AO88~Yc20#tl04$O3i3VMJolWvRQP*z>Q~O&eRGSK<1N}}6XoqY zk(V^+G(?dXd2E4C?~vmkEsHbmXQDyAzl1+?eG+1ej07TYwgaz!zy7Yc8vHv)d{_USyA){P za+mt67x(+9HgKf~e3!cP;x1?JX!h9A_8<4D`tM8dw|(j>yx|Y~)Oye5(w)F>^>d#} zfEN(!xefnA>{AaPsKSfQoy}~k+p4*WR@eQn!hK2A71Ll28BISI-tF1$jSjlrk`E8L zv=v$2FSrwM5pJpQL(O)up4|xv$#whSkQU!Gf>kGC{(0#qyKU_5bD{V8{?1Vn1ffsH^vv% zcQw>*Jy$WN(g+CVCXkcoSd|nn%f=W|yr~w5CN)p9m-~PbO>&VY(oXCXS8o|0skrc- zSa~Tc;LQvtcOP!$ebQ@nIqKZH3R1zbqT?H9P;JlR^8Usnn~vA@=4%EVCilIV`zRgN z9!MuBb+}}|@|lV}tLl{+Mt@ZoYNLn-73slfs&(rPZ*(|O%r;jnpi@W_rIDE-8Iv?E z>|hQH?<8A)ptyv8%%v3^x?f=JJkWR#CNnbhETyZ+^8+4lApu0>tv#XGd62z~d*;v$ z&5xN7qe&5Hh)^Sby8>~AZn}`4$C1wP(p`D>eh9Br;T~sKT!2rnnm$@(WI%sNJ_}|c z*h}z;yx)05FziVt_UV2f(twvd>$3VIb;APS`TbSUM5jgOd31I$0iBm*ziCyl$0){W z*+_tx^^8qktw!S}B{P%5N3GY6v+{kCi_J_#SVgAMf99moX2m zz^Q@15$J@0n|J|xLrjH+?&Mt!m0Ld7L!`eE1v5??+84g9wYh0e!>lf7B2!gLc@zAe zN=b(IuBXzig{?H1H$sO@aoDsA6pt}Fg&CLmVmnC>QZetvFPbP3g@igLAvrs1@h_`+ zi4=7?&GOvWaN2}I7+n{iTpe>@Ki7{GRlAmK*s}9X6dAwup!R4YRxuS`?+d}~x>rJg+a8E5Dr5V2 z2JaMrb!=T54g9s0{xj+9xBdbZvHKc{rV=8Mg&+9*A{-(7=S6BpC#)2O|B}GYJw%Yi zMlWCg1u0#*#dtwF&jp>KAjZ#W3oiYRlqj`?E=gxq)Ko$B?NNJ9Oa;XVkhNoTe<=>|H-$y#jj$I-pHu$sr zGM!;i5Ti#t-S;qscOToC{v$?G9=v=_f5S+&@P$nyafo;P6C=HV-xTrAe}yST zpvAePz@|7S@6ZKWDJe7aT}BVAB|x@W}Hq(3GfFz%_p(>H2kEX-w`Z zRGJ361b%n`sgw>9i~8#OyH#QINt@(59>81bO5D>J+zs}K*s>c)Ag@&#K#IM;3M)Jk zVFtAK5-G8GZ=?SRGux8bST;z1iIlcVzT7c9>9F(kvM%;MJn293+08-15?U;wiH3Fq zqXoBv;*}xoR!u?+a7X%ebJ??x9482x>ejLU5^O3D>BpL+T%x5faI_?O3uQMA$! z{?sJTotd87QC3`J`?V&9%dk}+QOcv$Ji{V+ zaaMXFZ=e^_vlE2@mHVfWkL)~)UV09Z#JK`?29upWsOWB-?JKjXmm0Xu*@V2RMozBC z-pogFU(+YrzuS(w#AsQ>3K*UESmLD6!?i$P$-L6db5NtIH4)fluE+#6?rEi(UdgFe zTZtY;OMBBQ=|l?h79veY(FWFrm(J8WVtW%dekwlMtg+Q#sXrusU+KyM{gk3`Lib22 z?MC99P;Zw=FY!y;RlURF1&?TycbU9I5wK$~j(+?;-_ED~aduP1r|zg@bWl$<{+x)+ z`cpj6Tie?w$ytJ`W*Ki+%It}^TkqeT^I8*^A5*W!8e_3Q;Vk}uJm}r0m2GF^IwUqb zW|W>$GFTfdE--c%TbeH*KC7}K!x5(5)sz2Wl#nWAZ_IK!?LH$cSE1hDCTLMAZadf} zCCJNx z=D2We5oIVvr}%uHXbU(W5$FDxsAdxL{gpn@shX*Y6~?P6xavVy8Y3h#nFaxtfEmqBhi5oqQP^4b8l*q#6zg z8a+9W8VC2V?V|%BbKhb2&uq?>)k-wP);SkFet}Vp=$rfyY|YPPXOw$U7C>^l$p1yT zMH%yb+)5=HVvBEPm&-w5pdZO-Qt$$!WCp3YBHE2Y!?h8Z37&SvLq;z@FT@o8j8U3l zL3f+sy(H4RA0l!ifuynTmw}hyYzw?z0-j5pUH0?Ca2Wl)owAvx7lG)Si}Pl`*Z)Pi zMY(^U6EU6=b5$IX`|-#RA^OA9E*zeIP4mAGqx4MtJLOiZ1T!Pz>Ao9dA~4DyO@ed3 zufgB2N-MnK4_L)~{MXw3GhNdRhaLnJW`2i42`iRY7tn^7B0Nbb$md*?`|sIZK-WJ& zA>|J!tVK$@=yy5%io|y)^yB#wd~u)rJ&o^Ruq%!UU!ebtuK8CelsLwn_+P~jE@!g16k6h8Q$O@f&xFJIN)pzt2N;SW%#GkrPk6oJQoIFb5c1i5p8#|cKv zTyYSdoH46PuQFF+^6}LY+bY@~aIV6&t5vcRIK$W!vTy1pw7ENOqsxVlJBw5$+zrGA zqywJLIsrWcuU)$qfR=SVD@ZMTNaVB0cmFPka2Mek)Zv!QaE0l=agotB_|wx1#R^*b zB2)S@ar%U@r2>+7$jdcYcoa4>|`hf-}6}aD;2XP8AxJqWmp>J+JERb`4q8hI|Odkz*G3-oB z5Fev)8S(;f^FtRw-n- z8%{%R)iB~wEL!b@kyUFE)0iF3CojO zAN>~1!(f@fV2RgY1#@PR?bRM1!d1=8V}e5WMBbt$(b16W*Z)~be3v%t$D-WRiOD5f zDvoQvWBuNDvxs?ay4V$^%{& z86&(TpLjKg6fNM3EpfxGiT}w17IhpgRy;gVNp74nauOL;M2@Lbo>2bRXwQ}q7Ruz?jxi@9bKgq-^1|Q_TVi&_)u%ypmdlsl1H%6Ir?}L8oK8l_o?Bt(+y68CdU~#(3Szv@RkzVvR2B4t3Vc5pKl{oi>Ol0SML8&!4 zUJRAz@f|$3o=i=??0)k)POp1OZQJSgSdtjmLN9a+GTW0u$bJ}yF;HEN9dofDIG{A zJbMT}=MhFbBxBihoJutv;;NQs9;|m0LpqCm_0Im~n&7|~^C7;w+u4y!D|r8Ec9K@2 z%5X^2c{C%kCwVXhAW_~l{wo<(awYB)eN{9lyQ2r$bKfs0PrP@_O+pn9n|+E*OsNiX zsVjkWiCjaIudTa)kL~i&v<LwMzpn{U;padzF6UE8#ZB@KLz_RNQL)vjzO?JeExMQ0~PB_ZD8 zK=FE%m7I}G`jKcJ3U@Y~6z32ys+i&P5xaM6hju@$NLA?lJx(jT`_Q;95zO&QQBszB z5%hKJ)MrbXB&)s18JBgD^#!qgrE%8>6;h~*_l&PVyPpHG_DO-m-ou+d ztz;2-g=)pyRdUl!`7uD%)YIBlx<%mK8b4Bf=A7w zUxb8qsGC+HYV>O8RbP=C$McrgTWnLc1YBCZ?%C?&o$lEhS?E(3D{GTxSeBLFIb0{7 z#i>S}9DBFCes36WRW744vz7lfsqKR3;-K9oO4JNeNwH>pB<$lHQ~ z!GdtYy$iXX#tV8Nohy3^0)&!e+#oRlWUml zF?bZX^M=hsM^^Xav$mthI?w4xF`M*IUReZYPuz-K>NNC+Jz$$x0usEYY2+t&>de;P zGa%__*;7vX#$83}v(7lhVgN3Lo-v53hLCv}^eGKZ7v5rErEm>3mzdXIJ(eEuTFV$X z?ROsTnXGR10yWz#K7PgZvJ=MGU&x=a&;6cX?7g_+a1oJehT2mU*f(^#bw2vZ{4>Gc z8#S$>2l~~OYDWUXT=Aozy{5pThg^+?YWqBd;2S!yIsQe}?7=dd+i`50V)2$fK7ARC z*sR#}>)R4JZ*_|*G#O5a`e-E{bsT@hUdN1B;@92 z@r);(U-6&62T!jyS@su(q+uH8OP|)-sbvBr>T2-}SyXdHg?O3_kha5?fPJSfX3t3*llU>qUZ=}y9e8HzD z+FoZW!r1DWYwep4xhWP^6AzBatQ>68mrKumG=&cXW@+6k@0XfjNNDMgpsPT&z7B~; zt@M$@nUBgPiw*f`m;)B6!r{@QYq>KunGdNp)QC78L@zA0-+!VS&GcYrYYsN{_~?VE z(r{gru>7s=sE;8XE_twA2yu^Oi52Tv(`}24If~sGaWa}Z!NPB(+4mufjB((#K+on; zm?)sunqyfBeSVc8LjPt{QK9-?+BkCf0uAzbuVo1m0^Wr>rYP&pLYh- z<-YMnpK>jzWcOEA{3J}f#WxZ^>J5HCt*IS?AtCVB+<7G3ZYTuZ+hW-`|RP$@Dby|0~i@$z}e>{^*#Nz9n5#< zq}RJn$kon5+64giq7>lrM%ei|`F?_rD4;1rx18>nB3qraA7}ztXdsl;^kUzVQcR5p z1M;AF4u)$%CYi&wb`R3H2Gzii`#Dn8zj~25r{p+}K;Gj&wxBaKvIBo^Tn)5pC_1&a zE-i*UamU?%NS7~IT__}_E1u}@bN9$-iXrK#4e3PXmi_S2*1{2;l|wNn@t)r3dxo-_ zDD6Dq#y;9Ur-c`x=Esny*%&?d{aw5;ZAZleMpXpqKG|N#*D_|#_iw%OBa8nMZTBYL0J??sGl=PNFHy_V3mKPQ86 zA>~PyTZ49$lh_0gM~ciS(FMbtN{jX zV$)*ogdLs&J~wbBps)Pm0RNdu{oM{2!9*hLfG?O*1P2382s>az`A>Gh7tH4$?0^yN z|JDw;?6)cYSyb}79k2vqI&eGS?@T1T1W(`Xga2d)jOhO-JK$z``@gjVHi5HwKX~c? zJ|?w#@82=0Hc4yf@aO%(r2da~z`*Zo@HZxP8{Y5-CN*dMa_zEy7r_6pBu#)Mp+M5Q5p6>=Zx9%C?mPN+4xa14MFt!FDb?A=}V}G|zQ|#keO+MSuAq3PNmcAnL*k{y1-uiBBE@+%MJ zemj{MZ$46m(I#J&8<3Z0gjL^Kg(O5H;BOhMqD=caRO?IFlxxs+hUspab<{2Dp*fQ=q%@ztW62F1CBzIG*SAw!Nmk?zQgyJZta$to1ngrRCalrpA5o zcTU^pllzvhYstF^k2TZZUdCtmo)*b`r82m^zKMF_cJBM+%af%$ zq-sADABa|YT~^hb*K}P{QTA!=kr=#fy zuS(d`PJNh>c*`~I$=Ds;Q&~JyT@cdsd8?O0`DvZE2E1|ELBl0#&5o>@9yIM;JN-Ys z@=q(UQXUk)GWTws_C_6*MiUO}_401#_1AWDb_6D!ag!72-otG1Rk3_5>mvWj7NuIF z^s%(Nr?Ule?wYOZ{>D=Jfi>}}J8k4?LU+$++I#sx6#5>07T$wx` z$>x75bCqp-WtnBk=X9`PynLhzM_Kroa?JPhmuMT_*7KUpNRF0woXR@#QipOPpC!zV z+%Hj)ecOK9A(btGws(u|npME|J_$o2%9i|37CtsfT%u^uskt9a8);zg-+#vC2&r1( z%>FN=pZ06(WVLgf-=_`mfBP#W?$*LrCfQJtxBk~unDg`4Jbn}|@V`vnXRtZ+>Ox)r z^OS+|w_$~yNm9NAQ@1#-+wRYM{?6l!5clkMzo%IxRdFmA>830$d@-OoR@l@~LQ-4I znBMN*`-Y}%_JiWgfnn1-Z}~pWlX9h6d#DfLl>hIiQTXP+aJmD{f0xWpfcYeM)T8nx8?_jPg~BkT{9`LDVGar0mLVRQ#9n?nhJ1R`^&N=!K)aG}K){nFqB z_NEK?gJyL)R;yIO^nK#9zRKf`+m7yTXL|6W@qFt{gWkz9Cx*jQ2Lg|MWA}>LS)Qx& ztN8_Mef-x~=H)71FZVa53?idBiE{Y}-sgq~j{)yVBg(jF^fH zvJ~a0l@jX0@NIu%a!F+xluBfBf-Ns6$2_<;miASqp-kYhKE1G#ypp`jt<4=src?Db z3!-z=U@zqygSoB7HexA8CVmt>X*c4YrpwA@=!Ad{+HA(l??a1I3Zl`V%U)RI@fU~S2hE!ZFQf${}=SIo=XWN^O z*u8NsHPPSQ-%RhQaGmq*wY^_8g7#=^$k@$`>@wVz!@E7=`J0_0zjCtmK6K<%v(6V> zqcZ5!HH-6Uj-c-pOp6){t1j2kzT=!LG-ftOR!JSrJ%Jeo;-AIwt$(2~f!4qHcR#fL zg+hv$^)D<(%=#Cm6SV$~7{lHD#Dx^T^)CW82U!0~fg?6R>t7@|A!hxH0O`c6e_?ro z*1sy?Hix$r?8!gxS#NL_Eu{8=*5eGVK8+*MA0(lfle)o5*1wsUE?6d}8bAUOG4*2t z-F36zDvM$YYwkJ-BB7Z2EWiZ*L%MWK1o_bM>C*0WPBNnX`Sz1V%u%}tKZB1hG3$~p zo2K(|(3-vZv^VSL>(7!iRAOyW>;=@K{XUQHh=0o^lRvy}^~a(uePkW8X4w?GLOZzv zkMFM&`ZRX|Z2)XgaiGdIPuDQ|@aCBh7+b4^3l2`Me>wp%7QY}T8z3j2Bai+QkA ze|oY0Ouxu?&uiB=h(i-AvF^9P^0+HiuyXp70nYk(e%#+8G3(niJ*%vaL>Ct4&evQK z&fOV|`p#WxA}!OOJzAUwulY+(^?Mk&f@Kxgj85(CnMU_VFX*#H(TMGjW)KO@e_a$A z)EJ7af%$LD^(+P%ZD9WEarW@mC$j6+Zb9?kOX@s9UTnh~+=!xhY z)pTtdD!kKZ`s8VLo!U^v?OidnL6U1Mn;jl;>opG0op`xv&Z)g^wGI1oDWCR>3AecG zfcdZDI7hF7vwKXg?%D)RQ!3es$Q%5{qU$d1eS6{)&&}_gUJEvX{0cg|5Ix3|O6AT8 zd77iEdZt6e<#zJYI{g@DH_P2aF;be?FsRwH@GNbr`&oar1%2GhqFU?kf*Mips@`z& z>+r>;j9c1vQr5AMsZ|~;%87~o&S~z=8p+|SoHD-m#>kLFQ;^z?*)Mb2C7ZUF-8{x< z%5L*H$Gnzu_LuuR!%8;u*U8EEJ?DKEizJbruF1MhGYLo8#N$y@7!yVJM8n1;vPg;SESh!2{ z^^uYk@-x~MJ*LGTCxmYpE!4Jdq@FC3?tdASv&NUoeB>=ZQv2md&KDIe2dgWt>8@*a ze+@eXZnlvfVo@sAso?oyla@hqs=#I6L?mNSpjP%!o{l5$xTcV^SgH1BCEaSSx^uu= zie$()em-4}dD8Ix7pZ+`aDw#r^JaY8U#L2u?JvGcg0{c#^&>IcUs#Tq?JrCxA!Pfz zA4p5AtS7FL@NIt)pf+gxi>ek@4Fa_NMS>$S+g}7oCuaK#%M-NyZO3copQn-}N!OA9 zui_+p;18#g+HXpUfe5tiuet$I+h3(Q^e`>cN9G`bh(2_IJZ`!nVJ+rXp3O=6fna zLR>O}_n&*JoWbn3UvL-4(U<4_Yt3$#44vm=<#&xW>FZEFU#My-VN<9TT(6i{3k<|H0|AX~LM9N$`W`C;1P#M$@OS4Nc4L z_afVyH(2lcu}rN@t@`9?$(WQ&PF1N7Op1=1pQs$!`rS4nldHH(vsp{)U|R2~2;j=^xT)%34Tnp84f%7|x|Tf4d{RL&8yis*ihM=elWCl;yA1 z6{kD@qXtiAYOUY9C1kKyyxcB5dW!Ny-1e;S)GB}b z(H7;eW?-MTaEm85K4#>Xs_tdIDqlcZQWlpe%%CPQzbkdXROXY)i}znkICcadbC6Bf zkjWJyt=q*|CzxIB>TE!gQ0lScVhDYJPA; zUcPJ2AHADt2mJsd{ml&&)|y51__J>>@6e3gx>H0&cI4BV z&Z8Qtyt}z2gt#=Od3vUzw(cis2;CTWhf0<+bUeV6D`*sjI+$Z?2SZ@XdvX4V3P^xfhNNARU@4tP7n@wesrU9=I(zaie|VME&P; zwMPv)@)$>0Lzxy<9clHFY`_qg_!ADk;V+cW(C`;N(JTgF_=`FxV)%Ol1R^TMU=6PCem|+fH~fWcfrh{MEQE%? zsBmm*UUeUg(~D| z777D+xJivwPdwa-nnVLW;vO?Na@0WD#?nC3$i~#f?zbzOxYwb_YZ(<4Kmrje_F~Er z7l>>@9w-}hL~0LeB}7!4R|p(4Y(PZH=jDJC#4n*oaO|UMmTsl)`3_wU;pKr^34sVO z_rovGb`ZU`uI zl@M*L=zNHajbGs%fDKWHw7$U0d(v+yN8K4iOKs3eFe@S2HlSBRAZcEpY}P@MAt@O! znAkh!&!gg(^=K*z0W3HwHkN$ zh}|C@@gh1B%02|`EkAsjh|GC}ieKD=A4zirKb_>D;|r%s-EaCRAu59t3c3 zMBK3XJme0E1`GIELPVk~b0jIzBesl;r$GV{GLme-jr8xy*w&7ga!5syZ74ETSOw?6 zR$>C8nrinU_&->YgCP;xaRervP4uuYDu0{XaabNNA1a3-Ihu?lQA`=IyeB>xWAU0u zoQ$9(dOcV7N%XoeP7)nRE^LUZ{bB|$kH9#JfaFDD6X7wQsuYqBOKL>K@g-zz!;p~; zvkgYn=?G3FEC=tIlVCWp_spM1#vvL)3fjk2ni`@*DQNY-sGv(R9kGmx-$4QqD&C^T zb>Q!*h_9d_RiJ`~V}=b-L8Hc)h=PXYh$(28PEbJ$fL_9mF>xy5D`*648&J^rRD=o| z38IN9Xw;oRL_wp<6H?F}c+LFtsOUsTNI~a3SKHM93ITkL$wUlM!D*qeur7v;=G=CO`$94gV*ipi%jWC}>DVf(lxZ0oO$0 zWCRr~Drgi5fr7>-BUI3^`NR~oBuK}SnvjAokdJ35hGWF zv)d0?JC=*f0~=iVsS>WD$%=#Y$A-N(=sbIP?z&F*WSL0!RORYhyuYrQXq_~<&D3{c zD9~WGo5o`*I5sS){17E?@_T_j@yYUc?1jnKUuE-m6;?yAvkPm;Jv_q^q^h(PWmc^A z;^sy!>3a<3GTxyK8=pNS-x;JXFSoY}K6rCvkP<;$-E zPxqgracL-a$N20Km6y%U-_mhx-;wLQ8pi|+cx~khF4J);U){k|Q>%KEc}%EUk^Vz! zwbJ&VcGX81n@6jUvT|;e+9ms7VbEFAf_)o$3@E{Op~Vm5*fE#~gXg%aE9kglko1<~ z*lVXx$^AGJ`|Q=VO;_Hpi~e!)VH8(^>a=TM{QL2H8Ew*A$T=#g*P7q0A=f=auC<5f zoN}g)(itr)?`{0L?wz02r#5znRtXBS=u>1mO#iA76dW8lw6G9K`z~_R=N%hm&YpOG zA}UR_nmpAwbb!Rk++xe!&dN8WI*v+vs(sJ9+!>>rSmn(%xbtr2r@1dxO&@R1?7DJs>2*;I+XfQF4apjIC^?V^ymYK891AYIS6_$Ssvm{%Nl0G%~tl zwBdS$_1nQS*Ts6A{U=`C6HQAxL~ZeUkg@)WZomXz+OA?j*#<@b|tk*ip5oa5@;UOoQR))&o(P5tMj}vq;dW85sS1OpNW9Y_Wq|foZ;&! z3z&7Gu37IYlN@tlY(XjZu4}y0w%lR~^GV6e?|E}MZ`rDR6Yq+gygKJ4$cYf$cRS6G zwNUit_k>kV!OkWmGZHG!SH}JN`0ldaE)p%DIvPi+)}K&3_Mu1Y$|y2m-IG2|mb)e; z=c4AK1X;O9#FIVi(~TKDQ_o$JN_1%#9X5AcKfZ^)iqnF1^J*t&&Vo$K&b_L+jka{$ zEH}LAw2x#OQ%iqPf2K=b_vrnv!JQ>z-Oe7o=>GNye_Jxf<#bZ@&M`nztB)D16S`FuSUhS&b_((*i$M0h2y;A-_J8 znA?N+n?0Lzc-;DrIM{Xs%|SNXOZ$=NYk^%)!6^Q@9DKS1 z%|ZCAikqe>Xg_3y_%!Ii&oKi<^NYWor;)Lu;V&cLE|5TkfScIR<-UR|EY8#H!3pfk zO7I7b35)lV8p3xDmxeWCZfU(t6VYb(BBSjyYwA5)uYZ_6iH(lmXz)v7F8>qmGWU1(hIe|UU*ZWoP{<5bco{hlD@%o`Mp{(lf%0svEi4Cb_Som zRCnMr-&?HV-+)V7w8CbI@!ET+%|8o=X_a}MPHcP!Yyh2ma%d!{J5%^`r@OOQ-KV?W zNX<{1+MAFckoVgrX!TCF?^tMZOX}9`6~L1E!U=M)cbj3$Vaj<@!5L}xM;vIlcA!fZ zr$kt|rl={=&QF$#8fstB)KBu~3=%01>@lhS>RH+)J)Cu+xO`or`ef=`MD zhx4in?xbQ2DLqb0Rqn+T;MKyhwcwPN(q1XMXpYqsey1{M0u~k|=O(49Y(-6b!YSD0 z3?522Q59|tq)GD%(&A1XU2m47%UC=gGQPH4SL;*6K8rg7+k0yUMT@5j>vDu#caWYR zAJ`VB{5itYdC-%7_bt=y4OIfS_bXg4mVMk_&X$reyt96PvYx&m7waYIUzR;W2dZ)v z3{GYj_Qz5mm$o??2AI8mWQKfx8VeJ{I3B#V9Zmn0t5v+Jg2Mn5t?jc2uEke=&F z%sb_ANN}T0qIuby6l-LgkH93Opmh`%JsN zE1z4XGwEb}r^;w-x_aT{h2yFTgW;x<>GrF2NogcyBpz3h-A8mVY0_uSsn*9YAf1zX zm>CjJMg)eYpDGt^Z5D}-q;@IHh!Ym6y>9)z+DFoWs!Psem8L>cYizoYOxz~rXVP6} z`6jyUX2v;o^i_wesQlRUipyurT{IaP_{td%2D7?RmPNZ&D4peP^?qO)Gkkekc>EC$ zg{uAD9s8@Y9Hj($_&%!%uXC#6DDE`LyD+)7$3JPPDK%s7fc6~|k;hhV19#MJtD_3{ z7qPq&(?Y+>K1=leSSjmFh-tQKtZvsU3Z1ErTzQv7CeM1FR$6zy4HelK40vuY}*$@c9k`>}=^ULlsADuhl@p-Xk}@LP}OteG9Xq2fiP z2?-esybG(&3rn>9tkh(!j0}fgM2{ zzc;S%#^7JZz$YMq2m_0_(fvjZI&X3I@c>*DcIGPZ17;tU3W7|AB%a^=>qHI~e`w}2 zR`g*l;QknM!ysDRIQ6t!LY>PLv;1}r3cB`Q(L--0hO}(ZPCptL&TyR4pf^3zl78-UUuRcHw0N2hgN$w2*P^nW_q=CR&*~4X zKV9&>;lh^l-8lzd4}P8IfRA*gU)jVC?dIc?63x~5T zwJ&4#%G&;XMOJ;0%Q4EVxy`#iu>WWOjC+fryRD5#t?|U3IQ{HMDifI#>u-zGtv(*? zvZCm-8T-mF7bv9*-G}{MHn5=1k@g?$N%IZ+TmspWT`mi#A^0ThtJBwvx{`qQDg?OzD>5Sa|Mifc{{YYvEm&irW@W z){U=S=aUBd-EQ{%ynl$R?!1MJaNdULJNAtcEzu=f`i!mqY40;0&i#l@eBCP2!*P3Z z)1Ax>*4)=P7N(C?PI*z>&Ptk)KGA6?9C*(u@0?MZ-06WI?O6gUXGH@FyYDFXTQCm@ zd-XZ{s8##Dji9>Cy3mp~JUGf`ku%MDLrcnfRnLR3vC>9CrZNRH7wP*<)!Tm{^}D_p zIG3d()EZO7dxw)dsFEA%C%5%BhZfn+J-a!Zd-;|3#8aDvi>&5P#^nqwJi^y3Ecll^ z|K_npd>d75!mF3A2K7yB5m|BZUE&N|1Kw1N$WF9g8FueK5$Ca2O*Q+d@0ABhEg_SP zl85An?(yP8@c;dcsx9mAd#KFVfW<;k{T6@t$wM?Peg9IW9Z3{nci2v_d z==svU1hS9>pM`mNP5fuD5IM$ydZ&oQXW>OKj@X;!&tswOK{N~Zg3iZHsdQ7D!S(p(d%0_4Qy1WIqE=e2uA9qLC1qBPXS-9LKNoiVpkPKHA>Y zx%o{-!RdiZkz~h)?kXJWrciMzveh5DcLaFeue`u-c-SAk12(&c3R;SZ{6uF=e*Q7{5I~C4_|Xc{YmNFoeB}pcQ%H=FMnpby%gB* zXQkM$Zadeq#~bnWaXt%W zVY|8qH96x8vOZRSf}z-2mhXnp@rLlNO=Ai+*E{c3cwbr@G!-RrXbS`7umG!&<0^9v z88!C;#yfUnL-9gKhkRr19gA)Actv@&O2X0Og;bXTPH_Pb?l9X_^&0`)0o7iv79yQ}3UDvTCaGn_hou zy0kdXUthA5ZagckLDJwbPj3mG*k0rSMWNG|RPtIqYo3w(+@5)pn1-?T*-H{}N&9ZS z_P4iLH>@|I)gEyuDfLEsS6H1-2jdMkWznl@tnO0`y-$Ockxoxz*Uo7@@A`htN&SkG zQChx7wd5I%3Q>;MyIRs`KNV|UX&`;b`SKvEZDFMtlh2+*UhY4toen%PF$tI($R(G} zruJBCTiju*z|peMEZP6L>eEf@zwZX` z&X#m>n_mNU_Z~b-&D#(k=AYdqIm^l`@rrzEyMP)^-64B2^Ar2#wtNg)6Yst7Cc(u= zYi@>0E|={mcR=B>ug8ND6Lr52QqFU%x9&a>v!45P53+7DCNJr6Waa>8#ZCLcxaI}E z#~-!lrZh(DnAA#=k{vouW$tq&{b^c1Nq2Kv-H7tL!|s^b%N*UF-c05kmN|m+Y_p`5 zmU00pqW)%O+deXmAB>HhXWf*`yKbT8%56&UAyGq(Uvf+E;n1f~+QWx0?rZVB;8Wy6 zm*Mu%Angr<`zAf3RxU4NtFEcwpQ4tRuVlX{>3(!%NN^%M;=5^}QY8ts2u@f1Uh5l{ z&1^-@32&OATzRt<)#PJdcLB*vLV{SQSOcwCko2`Er*MWHtxNpxcqY9^m212A6cyjb znTWEDMiqwT<0sN#{Rqd~g>cS^u&W6}X@5AWOT%j-ajlPx#kc_1aoLRk)v)cK5X4ji z&cOLq&fQ3GY_qUG36D`88}=c<5gQEq7Dy1_#51uPHMb<8N;nD6u>pDK<~Afa@%+Uo zfB?rfI`aFA%{&M6=AWzeNhJuqNU5eX+YE4G*WeztGBNqB(yzqeU#9ebf&?N;-%AwT zZ`Z(e7L`7<%z~YH8T`S|I6AkajllMQgWilDtS0fcgSUMY-+d{{tm}t! z=XZOBM#>lMs<-@-YVvi_UnM`6@*`1)Z*PujCpN#e?`gTFpfW$%7r79T<2cscd_%?H z+$Z+V3cV7qA)}AL@uk*nzvHf?{tJ9-X2j6Vf(Ac}pIfla(gKlS*P3c6#nqR93mlO( z0n$dPwRUQ%vOctO$MtJfH!8|{Tt_~BTKM|e1HB#lWX^NT6n-k3=T6wn7cATvK*wcn zQmC@g_v$>irpEgjW!BumoUDlfA4SpR>&$XHZZjFY((ND-2+_Ko66zuUDEAQ0=wqJ1 zog*DVn*_%NdJ`4>?zfkc)sl``#Oqg>=nRK`zL-f`)wieaXl2>u@8KtkuZsj2cnHRy zt=x4tjH+L|@oVD2a(5T%IXkwh^Xbug51PEiwfc1jK1zkX57&>oe^*6+&C9%v1A))J z-%Jr6Kt$423YqTwboP~vJwxkGhcT;*T6^|4C0%$v^RREC_*<(q!?|)vt>mYhb)4Vu zrksE3v%uln%wDM*dBrWp`=b9tk&FJ@W~J+uj`|;pecROc%8GiXE7aCMWyqbST_Vwk zb*}#UTCL26BgxJ^Z^)EKy^PE**8QS69U0^jA&$F?elL$a2iBl07La6)KcGo~Awnh* z9SifdnG*$Vw7jt{IeFlt<3s1XflJ{-8Wk*!v8eR$CVo z@r_n)$@$qN$NbB0$-VIKP>TWxhG*bvabH5#iKFdFJ^%s zHcD5Yd1-kaeAhNO)q9j=2!RG{1v+I>(qRD`fk?0kO`xS>pc+Fl|DJs@J2=0Rzb2E~ zkr1$kYB!mSmX|uiRdsS{8v2_^M)Li;HdfuJ*f`F?lbQVXHOFJ0wKckYZqHIKOcfzF zXR)-54?TbaO75xZZYx6j7%ii@|ZHE*}CI=3dOw$RTj1{hW zEIFyrJyPCX(%>vuKN9~;$hBm*n*~YYKytpl$IFVWORde^sqEe_~;y!N7{ zu7?+hnVZb&qZt|Mi8v=Xj=W ziT1ohl&+f2+N4&GyKz(QO`N-KJ`pBacD51|bG2a7) zI$|^Mqtp@?ti(0`cXc$C}B?GaF`1Lt7$3_$^%DiK-jkLLV^>+ujf(u z3~zD^z;kTylrhN!?7(|YOX7wcz*o7M%8RbhDXj&WbnMmq%IrnnYhouS>$&4E#^TYw}p#N zT5mOlyOy0z=6M!+E_3TS=dZjeImUM!K7d`zD4~bQqr77gCM|E8*6(pMypIy?Gzo9= zb7RbNX zZ|*e8x~Saqi~cU3Z$QuZ?FaVZrJkV;8^+kBo_F&!cavs}?QZ!rp&O+hu=n$nIdbmD zo=WM2j5ROP0u3%I%f6*dRPppRND34<`dvEG&OduyS$5(OdupM?J!;OBk4^`Iy!U5q z-a8TG)+}``Q|EKOh)`nE`uk(7s$$$J^UmS2c0ExZN{$S@w3Iz%r<%Qc8jLu54b#Wj zo&85NrL`;63*)#s}DrerA$Pe~zPso3HD2OFk4jvh(tm51%&g#WPr+Mr=-%5hG+mxIe^C@b@`xu^AxbewNtq4c|u!0~ANu=O{m zKu&Ll(!%6Hnrv3%?AgQ2rKbKO0lLPkD$9)}$hh|$+0|dL+ql!A#)i&5#k0?b*&;F9 z{|sm4Q717l;m`bIeVWE&SXxK?P{>`My#O+t+qi*l2 zGqaMl#%d+56<+Rp5=m;g`bov=1)KMB-p>~uZt)2FXfS6bHXU>7$oT@-lhoahww5|@ z3r1Sq?raa6UHBq$RzA^OA@cn(D+-hSp*P(oz3)gDNGV>`BcWBqN%r3>AGU888)>n* zE~sSM4q5zyM{Gb|cPm82yM-}8X&luhNQ;rms4Sj}B;^v|Kf@kOyhj)VzE+6R{h0>O z4Ih{Xz;cE^&ON}S4@tejYa(&wgIqZWNe7P6gYw~BxMG~H&#df zdCEsg1+9FR+Ob-!35V;C&~~igyN9*OZyz|SFz}b@o&k_RME87FLU&soxXhyNaRVo? zFH685)IFcnQ4bvavkk%yk(|ouANni{4yPwDS|;~}huAW%zv{y3I$COq+F4R+cC_5u zV?)eUwu+0KenMx1z)qMJ;lFl0G`m;&?BV&)ALkyLL$AXY5mTE_ngtX-IC(y#YyMZ# zCM{i)v;+0Jra4r?jVMKOE{pkcqsbeI@kY};jDKP5=9dfMzw%OWn_nho3|tSh%J_LF zYanCr$_*B${+qE8P6OV-`c6M@znNd1(7peS%yBeOZG3Hl1*(M7CVZdNFcz!_;ve)~9N)mpp4M;Zk559F_Uhb8Y-8rSFrl z{x{v1{7>|;?Kvs>s8D7q{sGlVL0$dO>%*PiRV6RZY|EUhZgFVOs64l0L)RDXPSOJu z{e6|x`PU;3`X%MhPWkKQh#mcLgLgPr+4ST0oM$Fu$NkfKWrwdkAS)kY;c(eF`2Nj- zvv$ow!g(Ld+Sz+8vZo$b#!rniCVcm;V&~7wwK(yaKRej$mm#%$+a`fNy<73wIYF)6 zQMwmmCk9^TGkHC}q#juy(Wj7B6#MdI&(j;PTQBlTvkYmp7|RN#uId>nxl|&2_{YpR z6}Mn>b^0#0)-W&LiyO9mb&%bZl<&IMLwdp?DE3mGAT!O!D%S(`b@B_xADws^)@@)< zJ(paoHd?hy>#=p3ZOAmMo!Y$aRd= z6Cz&rk`j03Viaw=LYTiBC|6cH-BddJji)+SDxsv`)o*<5caP%G-hipZ;Dx86rM_$2 zO__W?I_=+oVusOy>OeskhH7xSzOqn9NRo{~;f*-fih7L={zV10Q!F20d{i4|%Ntrp zM+r#9VjL-(L00u^=dv5>G z1>@QFpIM852~em@0I~Sz3iT>Av{2s%u;4ysvpc^=zt)DoD4|Z&q9P8{1b{HVg^b=!W@Wb=bhR*3b?%AIbX+{q+4WBt+CLB_Yx;dg%lS zLqa5mgl&gp84&2ohos)&HS^D#S!8Jwnpx=ZzlW^E&Mb(nkl|5BkJ2&%DuM(e1l+BG zE(aIGEY2*jOGffRB$!!LYb&odBJuv%N_MpUh1~V6z197Ed&v*e${}U8wr}?HH8F{} zF*4iL!tF&HsQN@MWc`WrPioDppBxS&r50^}NUto~5l*=`qdl}vp4=sjz4Hr~irkfl zUK~5iG~)u)W2FN(TZ%PZJQ^(SJepyOyh|p3NZ{PNAg&0fZ}+#_J~*5GtCGy(oE|%G z$@dF=HBS{HSIKtsmY)1o%4m^0^(|P+Mm(1dU7^Wt&F2B?rl}S*#=DN`yy|3SE<7lk zasrLe9)C)KMQDbaQtZ43A5fW!$adT9cNO>)ai^JlKhOI7c>6ToQzLLq%x&M8Bi%dG zYmX~$e*W~{_3a@IM`$;dd!6988P1_2{HdQKW4!u`FZqsrH8nTl2EWF#wsuv7PgRk# z3z^DY&Zd=_c279DpdrdKc)eaZwL&IrD~+{upOl2rld3?n(5^>&$KRb6<6eD39r1Ot zcDJgsCd;bZ^{2?<7A_mav^wSIxnFF%Y_|W0iAqYaWP!W$k0|d}(Rbt5y1z`E?)L_6 zQTN91eXDl9%|#w`D@Po0g6s1uMqQUXPNTWH)!YZQO(IUpqrcyCgl@z~y4tAQJ$uB=s`1+SO8L&9g7V6vYp9pXWRFSn{eEI#> z07+%O&ylz5TAMVxBQ09%wpR=Hm6 zjaoFGi=(l0T;KR$z?I~L(gZ^sUtJEF$%Pv~68f#!b0g0Y<+B_*qu?gL1{+(J#=@NX#D7(!vSS-^=>nt#W~K;9tUjydVLk*$J(RnZs)$aaj+V zvSby@zcda16qOHeRV02I(N!@>kdxr5m_#5gu`21$ll2H)LKBV&VI2b7k z6u&+et`oZEGC&^iLjP46;q=7RY>ZIi_L+-mZ&S*d35b*r`k(aFTIMLCrL9 zXZO9cb(>A^pZXksmnS==eKSj5ecB%uIw0Mr)R5$VJH98=Fj6z_5OcDzM1{$I&0xxwuak|1Cn@>-!`j_)3W_-qzzlv z!>L=9b*kaN(hyzqUo47399$gpzC9FsZ?o@h;51i;45wK4`r5j@(-kkXjLkz`I5h*0 zIw~JXyx9}fe5EU%|0Vww`5kN zZR{WU-77s^wcexFjN{*giU&~xRF0B*tHsDT<0eM~Z zO#B`e&H&GpB>66{B=)II1sUCLXRL`3$GvD3!ySBDxH*c#v{B4}K z_OOiX+A}%=Jv%$PCBj9zbXf+Ut`n7z(|6zrbBuJmto6Rg;Z3CLJVopepZnpLDsLTI z@2e0m-|Xy{+1*kyxMw&bzFX|pzNibm5>#WZ9!2tG9LDhefEPm%uOHh$uGU zGq}F{zn|X|TpiPl*TjDY3kj}{xdX=W&t>7Rb7&R{FJa-awo_~<7BYcf)E36aOiWkW z)_nof5zDB^a+W9+^FcDU1OIy}{%(T>2nQ2X4}pL1@}Z)Ur_iqz*<26qfRCI})&}92 z_krL`pb4&t{~IdeJ!0x0Q4Lu3L-0`)zS~BCtjIg)50;3v4{;wXw}QdM_T-=UsF`Gj zrlJslhZBglO}o*r6}N%tMJf{AHP#2y4a|4D3a^7?kjcbcZ%FDFUK3X)A`%o|Dg17y54>`Oi3n_U5g?Nx z)>c=Ejt!oJFeDWNhVsuP;vow(5tnZ3KDLD!t(jQ>#3e)|`mtq`Il3#Bkx>XF5Fz7d zOu3b(iGK$fJxOEomXXo@JZ_Z#8D#XtkTL3BI@&-Tl93+_=buZ)VoO2_ntRfk3f^kC zZQcKZf{wy;#4;-8fdnE{JaYlpfyCva6l~K90Z-ojRxbWdLBsk9DrgGOOV}}9nTm)N znh2Bs8wHI7URAo%+GG1EXB~U7#fi)>xpEa>PA_D4Bu!H&0fgGC@iYY+> z;@7~u$5aJ1pygt{tLJKlKkN0U$A|>Okmvhf=DM5W$AO=pPA+z(xP~_W@k=j{xz+7X8C=#1{R-@&p(CgLqsBbR;lt-OI3f zS=U0aB-cUq74A3u1;j{L1}q@P#|9S=BS18<1;j8vu?57a^4OOW_zQ?ZIb5)IB7p53 z1_6$f;-nu@n^m0w*;l}ZzfKMbE(F)fEx`uX$ss^Iv2}7Vo!B}#m`-q=9Eit-qs|1d zMPLx%unkSupeF8;Ao~i~ka){5ASSpB10NfRLiyMvwhRO2C$! zn==Lh4%?{XN4U0)WE;r-7qLNWWY|V%jf{^CS|g+4iC7~;y=O?o8W{o730fnAcw8WL zC4fx}g8+xE{22pEmW=;(Y|zLRVt_`j_}JKhkt@`-#Ee|=u@N$IMZofej9fuHF0>ND zCIBMg=-{w%=eh5Ig=#P`nk%?=ox^TTY^|`GV{#+;5-<{+@A*&yt1MNJu9e z31hSMAb_n1g8;`#J&{zDoID4z|5a@Io37-5OM5OXpq6OEX5fcK?^ie%AgE-RA9cU| zX8Uby9Z^h;atLr@=?#3lx)=mF zYzL0D3xaw`rdP&>WDAr-f~-*#rCO+?Vaz0c+u0mrv=AWO^Y}$nKDMpnyI?+Sh&ugq z2LhhNbkQNf0hw60Wc|KjX&P!v__jy-G6;|!b*tw$*g6Pd69$oRba2?RzL$xD2$FhC z3K2PJSZ*(agn8@4wc!!LIK-6)&oRN!Zw!SEIVyq9R}i}wflUNQq_!I`5*(xAVm2e- z(I9mRwl+7k?_vbTT`baStQS-NzdQ%zT_Z4VbRcpmHtPL$d%<-`k})Ynutm98Dc_cU@ZwDVhfFm}r710SI!AaXQ3Do!@$;_8v zLnLePAwW8UJr)6vZ7ghEgt3Kr5x_RE3|r#ZWl#+X$sLd#q9%e3Ip?K_1X&Ofr6?b& zh6h7F!}|vtqCh+XoFL}W`8+pVLc0UTWMD&bRzMCTlVV#j<(AxUY#8U~Y*c9H=bl0e zH5ktS%eXms6Tnu6L4f0=m-SUDceYqJ44 ziMckcfdnf*iOETr-;ju$M1u5{LcL**;l}ZFDDVO4M0vx z!m^kE-ObY+@i1jt&l6Sy~(?h#+aiq!5uM_;L~fiajL+pgxjxObQWf_;L~fiajn{=0nD!GITt~9)TkWTAHmxMuwBO>z+qF>0QL(Y>;l)wSFIEEo zp}9i<+ZhZ39JbhRZ75Ez2H97@hR;a^yu{z>&mLZHG4v6_CW%3S!&Yy{CIrj0tc(rY z-~T5r(id^Te~SGHjN3L42}cKq&HaiF1I(U@Ng<*GK(0kjLT-`#ogTO0^%le9y9BU} zFT-|awIUBJ^8jRDfs-KDl6m{$rT*m~yze(U$bU)0oTBQ%%ZG|4>MC_fJstrBu+?A? z;Kt2PLXHELvjN%vA=n7I;9P=D?;Zhci5LVpYz-M{sL$5QAp8Fa8)yc>altkZju%G0 zF5v-DH*?s+sG~?A0c^e)1UPIp3TE43{@oz^KLi`Wb(%}C(FYO0b`FC8hb`Awj|`Su z2eSV|un}CYvjp29A#5@j1UPIj6g0VDx%Ph!HiC;lmS8KtPhi|wKqMR;9JU+%*AK#S zxtJ6pIspHhFai|$TQ`D-ZCnho4+vnJUWSdSau+-X!__h*mqGRwu;G6bMt~xJ>)xrbjf=tHApva37z8+MPpuy_!ZPY0 z`wG~Qc;AHK^Y-7mHQypG_>Xu90c>|L2yobkd`0jf;UX zlmNE#7z8+M3Le`8VVU(H`(MNczX?NZf9u9ai@4xFBZRQYV-Vo5*&KPd5oYgR85_v8 zWF7&F{U-d!^AaSekl;oiOD;L^fzk7zkD?w1!coJJchgX<{WP20=Y*rWqIBfg<&$GjF)FAsm1RFtf?RD=&3HVCy1&Bq+DBMgGD)hRupS4oPLY@~f z<{uvbgyY}mG$C<_Py>EJQZ>17+1&QGdx~>DBXB=J2Fid0A`I-pl>3J;P|g75QktYg zxHlU@hsy%|V*hCjB)ED(0*oQ{M)~s?_~8YbfzXHw$H2?4eNeM;lr!IBD>(CghUtJM zA{kP!5F`+xpxJX=$NgRi;(L~aZ?>UlNsO=pXW+{r@GJ?xAQJN|3Cj`lED41yLC=yj zpm(riOI!%zzfeSi>i*V6CP=(y{&^HsNgyNyA7;6scZYyN@R$Kn&yw6P(ZjTifeIjj z2m^aD<%kPGd|!f)Nzj)dJYoa%B?uWv%$Fd{Pt2DfWFSFbf&uY=m4W!LeUYHLzjY~) z3>ZV~jq>L)@M97o240U)Lr(+I3?%AH@C63{G6t4{1R@N)kcjKI-^)OJ$7Ya8(6Jdl z14)2mGn5P@;@Ax4HzeZN3^matCra|vU@Q4l2yAdj$h$cyEKr8{FCl*LF=!pfNfY1{QJjXi0AR*w11rm^i zh$j}v7=oTyHiN#wjx2Ezh$SHW&IPOcTh~4o<2CcoBcRS3Gy(U4Zo@r^Ft4tI?@-Vi zqoW8&bng*?H1tR&oLqQNhq5+pJ94ar8`~nh)(2zG1-FXO*XK{CQ8_=DwM+HG7*d6`TggOm4YctT& z@}(7MKgcPlbJ#06Ik{NWbnsC?@+S5$5k$!yMY({SiOdwI^|?~H3X`j^AlQ7?Yd)d7 z|6FF@iRa2x=jmklWGEgB+mpxc9g9<0L*?^i|8X-V!5A}TO5GaY`qXH?pFYndn7=D> ziP6hypZOv0;9MQ3|9+#2_4HkY0o{)(l6Cjw1g`x*>aGNyitha@m3EcVYH8D+HY&xv zwpMFW(PAehOO{Zg6-C-5LRz#atxCexB83v|3tB8CTZst$&zU*H+t%W7lDF*j z2_f}k%?IyXlGW?rrJ$lX^WkUbSShVulBtqwqTeCR!F*`=i!AAcOTp#Vd$PNG?7cnS zM+F(MFR`!)ATo1 zhqD@s*aY}on#gmiW-#s}Y`#mtaO*eb3VZrtqMu&T9 zyPEdUU9Xa}N6+nKmx*q}4?4QVN~nI)9#ma(QnFLc3Aw-;J2COOL&DU%+IH0JKdbZY z{%Lb&O+36XeZoN(fQoC_y$#YAc59Ed?KIyceeI$PdyS7@JggLFBl9V4#1*eSYzTm<2+shv$-t&>I+C6(_aVPzH$)nq&UZ|wuoNDMmdWk&2 zsdA9%*=F29uZ20VXHpR|F&j#?dU6r!ek#Y!=EGKxBAxCIiFu>(x-LeAU>4BUM5- z>MmR%CnGWYz(J{Vp+BTO^oke7^xxR4de!3qxe578j%a+mSE9HnJN!&Q*0B%64C}^> z8nWX@dORm6ZA4*I#rn<(CA()0kz8py-`dx^a`>x0;vtqZ6?2b_X9T{?`2}TQQ^zS85hoH1&)$mdT#7#iL&<|QuPrg5`oI=lP?c# zx|rg5{O*v7+>vqdYE{#>|ETHlWwr8mA2|>A$I4Zf2EINN!t0Z zV}|Xx^K-dRczpDVneP@1X}sUwM9aP|rOmTQ{cWttqxYUFHn#3jpDBK{i_6*vql4~w zrOw`;SQ$6_&6guiy-Y$cv4?j=!kEU zwUeKA3ZCNFAag1ZGXEA5v%ECuh&EfZ&Uat!9eioho zBGz$6jqL-uP0dR-eo*_GDP7rgXz{ZS%L99EnEd|vxaFF?>s9S~l>2#?ng%yr7W=Ty zYl!dU8)GV>4j(9b82+>dB2k7 zM=BQPE7d&R?tZV~^D4WzWB&DpH$Ack)ToC9NO(PBvBR(TX_89bVOl!qQq^380-0d7 z2f^3mAHMOH_R5IR__+FB(#<<(`q|mt{Sa}`i|sV2;@8X%K1UbNxYqG|ucQR^f(Le; z8{+n@m(EN15+>_^;nzXc(x~s#>Ic76@OV8XMs{lMz-4OHlWSN$nekdDyoc{K(|r{- zzjxozqm?SGt_?ly%j-XuNEClqUKnf1(%f+Pt;YTtG26}aZ>>Mr^ZS7bbt}f}==%Fq z4Ud?AZh4Se^%&ip)AlL%iuxbka?{JX?bnZK9V%sio|O80ezFtWY;A7N08{;L(?e8V zU1FCO44R`YXELXE%`Mq4+f(iv=j+KCspRM@PmpRl;Pg~8?z$awc^3VLf)Ow`la2 zUlHNx@A&k|wTrvd4u^P_H65O@_?JS&(wVgpnUyb>?hYLpujN~07FJ-e@n+pMiNLW2 zK08#DbRxxOT$!(I*fr&qe@3Rn^cm*19bPundYy_jnKsCKQB!1k-Oj$M-QQkx`DxUn zZ?(F9%-7{tWV@Dk@Hs4DJH6@QLycdaFD91G(SE+jAmV-(f7v_hB@5LqzB1b__gZy8 z{ihLD29nP%4y-);Udqs}wro|+{b3K&t{*lj{(L|w_me}&=7NeB6%CK?HT^1GWmdZI zO8M-!aW}iUKc8En+Sg3a{b#rNw^TNKw<*<({N_^Py-}&=T=64!pR=DjsAgP#WGUm~ zsL`p&CV9L(t4VA3;H4EmOL}~sRIx_)Ny^*4w^IvGo5y_W6*a2iC?^K_nJk&BK5AMxDO1-wNl9+n78g+nd`Fl zpU?Ikt1gqZIwYo9*-~5KeL+-*Bd<6{b1x*VyPI@0v+ZNE$v(=@K0Np3)Pa`KI!FwV5-HKesAT~`DIorte(8?we?o(44V0D6DVRUs*DEW z!rxkXrF>(H5|}Fgy%YVOkEka4PG5UtcgJHBePd^5ffM~01OnegKMe>NCi+X$aqza2 zC;F$+MBfXW==aIXv+Mx|k8`5PbevVDX~H#!v9D{-*o=F9_QW;Qgf*`dg5^$?y;FPF z7NlOgcWVt(=(K6r@c#Gbs?S@n=kS!{!voxok2uuTdYDw$eT_wz_8b}XaoARawR^&6 zejF}WKZ>p1+3?Vit%mz8d&jmLK1$pEfx*GG`(xYoIjv=L#^AuJg(D(5t}@tX3g*M^ zr)T>N$k{z>%E?Z;_Oo1v*sISseSBE?$_bhBPwd$$kHf~hof!D`le)h5<8U=F8E)#& zVRx8#XmD4XeuiD`+6@h~?R~8O?Ir0GBRlC>_u3bx(bXs{-OYBwK9ell?mSatOOv#D z@0Z+N_3XkycL({%U|OJ6)tPPn|~$QR40>tAH5)w=p^8T5X_>}d}s?sDC{FXH`_ zmPzsZNsF&PP&)6rCH&2M<#pFHRV%*v`OkbmQQz~y2#R1H!#b3 z@R*kwdfnId(ioYTp)31!Z%AYB!9g*-k4+msa_*>kM%yp#KQ^<^sX4<(8*MwY@R-Uw zErTtGpTsD;pPH}wInQtVlo;$3>#)?^mQ$>i`VuO+fe&krDmImZGpb1eX&m~?Z}hbY3X>gtRjR*L`YCtz!6zN}Z@TK{ zXx!nS}bV{NYnyziOyxUVI=s`O+ru<{*!P8R_qiExIlD z=%(LI-7@0~SI-#wHax6K%sJ1>DjM+qN`sQh)1hX`H8};f*`;;4R@Hf*>wam3UH{I? zzWriJ&64IT8@J_bd13MP{G*uAVUbJSGMml>_KgiLcN?nM{#xGp)xMq?235!8ypMPu z8n{;dle*Tf!Rvonyoh^loa>W*YRLEH&+3N%azA?F!dc4+cHW+qmZhg_JvvprUcRlf z(jEh`DeW5jtB!~?P8d|?qjU7}w1F8am-ju8Uc9{1n4b|_-d^^N)R3O^?xsd_@(hP> z&9Mq!Ok4u)ZM>kxsxdJ;aMm@su+8Tlv-M&&4)q%SxyZkOEvH?t`PsM6(TTO!JT_!l zZ96o3vh(Q1!<`P~#205g*sWhN?8%ALsfR!B&{sM0P&YF|uIH~=S?fC6#4BEF6Ls^~ zB@N|8;jz2@2YG5At$*@r$+pAVAyc+Z+BLjQ?2qVF%MwEs{ekK~vsm&nb*_!4^Dk-D zXcv6h>b3uN?w*imh2oJ@;@e~_-m#~2=i}b%<|a?Itn3*tu{oY&mK$2A-=MVMNX3!y zXI&?I|9ZsPKj)3qUH9wvR?X6oeD%zy>fRasEl$Zl4em@`@0fTh|5I(xJE2jEo_!}T zmRnphbKRw_1x4#Ny-AgIUp3tD;=(#>91 zqGwK$C_ei$&b(tyS_j|F4d2z;XGCpNoaJNtvCHuf&SRoKy*$tmn$Z7d=BF{Gb;IuV z^q$yxbF}2fugV5-gOf8(O3S$aXqwRO$j-F;Q$igx-$hNosabvN*(UwV55pSDue50l zm_9k$uDozS?&2S=rCZbnPn`esL?4eCF$Vj)y^?&bifAsgN!7HIfkc3?9yphoD(tEZs6w+<;SHyzj0RcyXm>2eRZ>Q2hT}+ zZwz&^pAqTiGEBy+^X%1G;a6U4S!e6muR3An#PXx1k}d@i0|F|yf0M4VR#x2f(9Qp7 z@7To?K9x1-Jjvd%CR>Q`lBdo|0; zRKX$AQ~cY|b+ZS{CAxpGfAaICZjI){8&WY2{jV$cZc3clwczgE;Ta)Iy)D#~u1TI^ zrS%DLdi7Ex0aQc3;9x!)y+z5TQ={;1xE`llI2Zlg}9%{6N~c##hU{_- z*YfEnt>r#Ga_fz4S2Oi99u=8Ss`@5=y0cP3^97AXpVuxMtf(sfWNgV`#pxx>wyR7n zKUe5K{A12C@7_Zr6YQsbm9{y$FeF5(_LTILJ+-9~FH6TnRfZ(?$i9}N-?J#7X>^^c z-#Cf8c}-6YBFYZlzoYLp^XaGgKW{Hv80>W*@1Vu`6WO*;ENW$TsGiZ4lMPW(NLpjG zGALrJ3Z^MO^rLUaVinG#>80DOhZW{33wXFVl<>aArQgi*^ zOK$9_7L?;%qj^|&O4R*DIk6p0J0=HYjeOr!s`|qA>yj^XeSF5d9hXSj#Cm+a_eSky zlN%lmiLfXuQ?@y~;rj{Ki*?r9C7MN>aEhyu_q2TbIY4oZ^26X42a}5% zyLo=-pZL-4u!_gX!YNi&Wk-foy#|! zH+tAQPyTOA=P6%O*bYpH|K905D;J;6bKl52dUpi89T>lnSI@?3HSGoaRjBuT^Y}m@ zV3@~0M0)U>b1fI_u{J@*FtD8gTStKFBCxFl+bXa*fNek6PJ!(V*rw*-&>O=_h|hcy zmCON|Aeq}d(1mL`oaKvU;a;p{j5FM`Pv+w<9-6D?2Y*Mqj+uqYm#SI9ePM?GI=Gkb z+uM`%3N`fK@D|Q6jhCs=#P`Y#aMcW?D%PgQtu*{^cngQ>wU?6s-@wRWuVnlCR=HB2 zP&#|$vruCLXsP}x{at#O=yu~!H6*c;$esD2l07hYG3`pkhmCtak zvVz}m6%{1>dpA7gL4K(U_#+SUA-_OkqnnEHUw~)ueTbZl3$Kv8C7;y>Ktl|yko@~! zyqfe9$;`ixf11y6{=qkax301lJ&kP#2f_%xj2ZHeaixFIQ+%NKI5-0c7&w@Z^q6B{ z3kO5I?m{m4@o*6S7Qn$mVK`{6+wC162e&;Fm4ovCi&w}hgHa@0x&V;9 z2)0XLy9~A~V7m&o1h8EL+jX!df-MPb{-`n=pw}{gASzn|YXrfrmmKz@@@OsRBiJpZ zuVxj(z2A24dvMQLG!2_4L^o%2!GdqxV?((2D=fucOfMhg*dP9m-lcOH(_8f6mfGl- ztqh=#=jO8a%AiVN+E1(sPGXm>yd=sBmIJAZ^_XNLb37-%ijxC$;j1{I*)8e+^OeP$0UN{3L_a&p1PVNrYHfw2psRCiZ=S$sQGy2j6ASSS*XJK^X-u93k z^3_RwAzQ@4tBbgXx3u?N$g08^DeMj7=36>*%|iSvKVo|9b&l;ZwjIyOBkM#$KsE5Csff(r7cl?-^X1YV!8j5~{(TYsPyxWT7?Hnuz(5v#gcj2GE9$?EiVrB*>X3zUS29TYZgX9^P!U_ zz;?tGcWi>?K&oQBBALicLGrt8IY9SceFykXFo=II1!dmj6y&~#HE!u@I0nQ$th1cf z+E}bzLrcP11OXoj`xG*g@H!9^(9x67@AKY{kR0Z!5z>$xe&2t6Ba@KRMf$2L$Y)C) zuZ1KuKX8bPgkrr%VsWr=YqJE@FI+Och^#NaSOt%R1|1_X^4%Rb8{0z?n!Rf^k(q?# zmk@KHIsfL%+IxUuB#`|3;$YNAoP^^5Y^^ifPhPSxgLsx?j)f!r`MoFVKOX}_fq)?l zs(fh0jh=yGrB^$p0!s>ay}%e~u8`_Sp3jLnnjgo0PZRgV_B``J`4FU6aQ);p4k%nT&IDw+dy|J7IO?*(J{_cCyJ3Dtb? zpiDOYP%%CqgbZXn!*50y@Uc*)h>?XHASSS*XCVi$2eL34v5;*)<{+7c?0K_~VZR^F zb;Q_3!$Nt2g*U{HkXgt^EcBa_w;gNeRQ19Z7TOiJnkX6z|J9El^aJBaAo=&j!D-c0 zEL4BAMji~i7L;v8TJ!Ya&(r5jmyCr;ib61beQb()< zj_P^rFgjA~W;ZR*dp(?btNnPlJv)QlP8*JQO&qiA)9A%X=_{WP_@H7H z?3z3;iW_kZd z-VeX6sEP@_`K3?1cXnBR)yZo&%VjFPAN^FTIvTR!i&RuYR!K_LiTDlW0}C1+)@`gh z7S;HrZ)!t!Wph<*YGb+FuZBl{y$|IsJgFRPuWRQ$`Osq>TjiZ5*|r|8hw_#cD~EVx z+imQ!Bu7I!AwYYat;_r+xeE>^1lf$U^BS|{@w`O|JB-iSy02c6XZkE*m-{(8pMfuO zW+^8G>aMYMUHT$-Q9?rSnl*OoXS{f<=aH~;dAaSn^)K>P)Fy=Zl-v1AOv_R4?h&9V zQ|vr@TCU+Pk02|V5>N4NlU{VvbK5(3yUNQ^dhUnwj=Xay>1TW0^+?Hvq00(2`X;`Z z6MFs0$k4<$ea^cbIJ?~H%IaX3BNx*X^Sh;o7tWd*wsLRy+xLsECBBtW_$gP!KG1VQ zB|gRn06AJNybxm?05Uz>3_q~mz*u|Gy>&S{Sqq=sSaEyiu{_OwG7-Ja_C<8vaQyzr zNw>2YX3z0j)w%SHb8v?uh28Nzr48EXsvA6hGEKg(xOaCL@Kw>5j-|UJ`VA>7i`4Huv~iuq zO!o|p80D+ZteV`XdkP=qEQ>kR)F{6v9->KJKD+VZOStme2ET^I|SIoEtiC`n&FK*?|%PscAC(O3uEX7-?nXUEV*Z-Bx9zOVS(WpX`jEcOTfsQ#=M6VO>BvUbt{)A)e2x|$yA z;_OT%tjxY_S*E>Szx}hFI_F2MO`R~q@aJmJ{*RYE-q~=GHP7YOt?~!TAFnm<+j2cG z@Tc9^VHHtI&m=6H=fro*2!Fl9du|l_$zn-0S-lTy_e<3|bl2Y`c|{3yBENRX&XKp` zKO|jpc`JE5cwJ_d>amK$xoz~->o@#bu_Ii)R%eLZ_qW|+q)i?Ncz5%f*7L*t;o^ZE z>Q=_Md{P?gcV$WBr>!1O;zq13w;WlVeSNt`Z<&4L)$4{&=g7ZSI2;?*ZOQ~+U0>fJ zX0O|(2HJRZv0rnxZd8_>ar0C|+37o*4#&q0?``L3(aUR#&#D01PjNf7nwpQ8#(Wu* z(*ErsH~)+#=_@_9`AdEtbNThVz8*tmzbh&FbiedH696g~OTVaF4TI z@kLXunl4T=k}A7+%WPKYj194JH$y7>j|T7g1aJ$`Rj;D7?h*s0B(7OTat{(UEHtMiPBKYUwkrM094gI`tpR(mmU&Rf! zHDm717?P=1p}9eCaqp*@rAzkfOqnq=H##}}i2P@lro$5}E_i6BehEJA6_c&|-SFsq z)n|znJH0<$OgvLy6mBe|ykUon^2pkCAGghL-nize`bv|hTh>qhsW8|q>&k5jjX4qF zvE9?A2P++!ZJ*NrdVjUt5w_}?`3r6?9edPs#NDg*`&`s)bxkx(Ki(T*d2@o-_&pMD zG<{bU+TPwZ2EII)Ix`PzWv?)Ub;)iPst${dilmVu#9{(3wyBKJzqKc1|pK zqPTNGalK|n-tu0BzN2ytC7<2wRW|AQnyPCn3hy**e=WXJ_toPOC$;Mfy1M&r_1XAL zd_Xt%CHim9&u+Rp&VPTw@|`&aw$4x5M}#WgNqli|SkdbF2jA}sF3#)8Dfd46hHmUocENS- z<0&D&mvSY_LJkFPwD=VGZEj}K?96w+I{aeadA_{=j>c)e>Q7t?GPNVkrhHz#EdT1E zE1s%LA$Zzs?~GAHLV}wQj4bY&SywOF z{c-Z}JQS#z7k$VVs^OFkUZ^Hd3E_om_?ziMH8#~_xKIt{lozVKt6Jw;%u_=0g=#i5 z=imHh)EF?Sf-@M1Ic=FeiSt6RX?lXZ;91#x~DVY@vwcm6Q54W%dWZ_W*fF@>ipf~N){=A3N$|Amvzh1gG^ZfPChrx2ufvKiC&b2sR9^g%tEz>IkFME&)rdRuY zbg~Wm-P1o5_b#e<(xi2xy;gsdxLTZRqVQn;)Znm0*GcEjZyTcQ?GmRoag^yA-KIBB zb-yhb>}Buqe#MRP$r(!(Ho8nT&HUP{xw>|Pudh$-hA7vEm0l^2rnA!fUHj54#5U-@ z)%iIw8z<)8Xk-~TMOtZ1KmT#)o<~;}$ww_(ADChEz_G{6A2~C$Tur?s42xyHvO@>2 zD1Et4R=uwAllq3IwY{q??@f-W$n@^ibhNgux6NmXI~HN_0hSLQ*J+*e?f3IUUQPG< za`7LLD+9`o9jbr&X2hBEY6g<|fj2&?pLL8p|W;uUu64O;eb>5HDF z;;G7mnGP&vWtlqm!BNW5Umfdci@_Jm%S5}ux zU&~JI)Sl9>d01kkLH*Hq2DD>*YgWpeTnE#+icpRj?~Ulh*dEXq1i-_0`e)%h>VANAvB z&fXuU_G7&2#6wYTVL`t(m*{pV^2!YfoEGc0dbHt{;tlC4{$pm&84f`T8y?!S~X|^S)gw_C>1f_~rk3X(v|AyIvof zA9h=L;+wJeA%F(!m4aA6&QTe$6T497T(apyzA#2$?8ttGw zx?SOTm|y7rcDmZwhSuSvcMS5vA0#kb_`{k2|HA|q{;(z(7XF|f)50H=Q!f1FkxXQE z4D!Ms8=CcRe$;X+pw{2(7}CwSV{kA0nM9n2w-@0He+$e6E&Np?2>4v1dn2Q3Tm)hQ zI(ib47yckw;KCo7gmB>xp4~7l{6RgYg+C}4qgwb|($s1qGYQEHe{4|OzxwsjPGA@b zB>%p%n?oSh!N|wG@aOV}|pe%orE`Tu}e{7`Ph<7=rWUU#+;&GmyOShlMM! z@CUEwor`XG8yj|IG*pJ}Mps0)~L7)vgsldMe8A_r=zr*;h)iJ0YSAw}z1q$?T|E zsmZ`vcJ-7aDPSL+%l%}8-z{8^z10`H6N0_RK8UO@KMlVZr_gd0_OMXl8=J34Ud1XS znaE5<@`r9Yz>I(O&#nTbgLDlznjfAve*!@*Ggy+lmyj1v)_ z?6wkdMnEw3g<2z7kj+Iz#yfKkAt?CBcnt^`$Y|QB6+-%`Soj`~a-2C+2Vi8%atuud z_7RDl(`_{NydbzE2ayPW4h$8LpRTHwW19tWQ!7c+tf=oTt69-!k8417e)1Of&kI(Bb`k3J@n5702S!2>iFk>-E!ciI6!0N84l1apdU?jfbIh@B1V`wCXx@(aH0th&}4t{d!T^*F5F#I1@k$- zel6jEeM@?8kxt~ZK_DqhGAnkF#k+G8_NCMl={HmAXTx_NoM|iL^SV3b%16+ zzXY%7h+zk4E+R4>pr`l1M~sh*OMrlZj6abcb5tZBpfS>c1GFl%5+!(mz6t+tJ@E1! zpt14{2WUt}$^&#vPhrUjDrh-CVm3ke_=UzbOnive|Q$24=0kOa#*H$WBa8r z->+T?n;>#7c-}|#RI!EtXo!Jj4u+rxq2A62zk7#xX~Mw_;QK@Q_D1=0gYIH(Hm`Ux z8>q0yTx#WC9?8tVkAK#EasF|?Ai!yA8NQqX?~sA~V~l~Seei+eqkb~ub*MTFnpG@sZFH2pCA%eqiee|L;l2PO5r{M?n$^<47j{ zH%Q3tHFg?C#_0k5;2xwZ)^ae6gs=R4B>W>akItYfC(?nb|a zau@ZVkAWY7fPsPjGOf7%?-^K_q>X){Rz5T@mIb>Ghju&whZx%b1_R~4$0@RbZP9v1 zdU3!Wq$*Yc$;`ixfolij4CG!@o^iNyci21hauLWt#yB`%4j(E$7OnyU1{QXcZyn<<5$&h-K z-$|Yd&>_Z_nS$i+B(p(v@H@#Eqrv|$$!{JyngjNQtKFBteY9`a32d+@l6l!LF*f+& zcRmLTbYk7kErGv@#Mu=%fCGN`Rrj652K&NZ9S@-Tt5~HZGygscu2Y~o6zdh-h4(q& zhvEgy-$^zYh7S`T1D$|?fq}h-w+=5o1Igb>hD?IrNrrnO!S5tP1~Ppo8J1`IPBI)y z%I_p6l1yY~Ao)AVY)~EiPBNK+>`wDWazM6xbqdBnPIkBq{7oc|>&q{&JZG*Imhb0U z!NtJnZHclRAeWEcGK>xOg)G$?7TAMS#aac%kZ_d0kAd|gsTf#k=Q$kq4&7kL_?_fj z)PFt(Rs#V81NV<;#f_eUYILhD0z{YVn1Gz5?4$?2i*XiJ8K`;?y_)hXu)O|hz)&Kzm0Yk^O z!X}u2@H@$nXz)A9WCFtPB*WjtCu*s_lMD&S^qpil@|536Rs+}&BP$#M;dhc@b?`gM za8D$TsS!r7=tfqh8pxDy%d?j9WWrZ0Qlra;^5O>V5W%o2{15^C24)w*eEEr%?b(P=!a<6yBP;|7!FQ4& z+2D7Q$s~l|Nro#|1PQ6WlMG47^qpkL7Rv7=-zJ$T90}ofl3{i7;5*51PbB&F=5Ekz z&e)UKEAn_r$bQOALQc@p9*}HAB1p(VBrKe%*_(|E!CjAF7ztnb`$(8EnJNmd&<}#2 zCdR)O4<0pPSR8qcy3a?z1|VP{;OvPwZ17I8md60G3*sOH#$-`moXkO$*}%$(4=)gv zEdiMz6%H&`f`jTOc}E%UqgOgxVS{YZc0Am3g0hxk`|5ev<5cElQ>@`{_8T_#0S5E; zHa)TOmTuKBlKFjbDps?a)H?X|ZbY|a18U(-|9FW9GmUDAr!yE~f}hN81Z=^UZg_2? z`2CNUcsQUk$|as8l9_*>8|f-j#qt*llZHU>uq7VMjTmFO!W4X@_*_XF2pC+c6zTn$ zg>GQ)Z;_C4i6=zqzeYmJB_1{yM#5MAJ`z5fhLezci6?T)P8p1Z@XTrf^AgV~)PFt( z-U9*#2CkagirfD_3{o!f^aS7#L;D{IgH%gAw@7CGeGD|7fisYMiKlR6IUWY_*YXHl z;u$|3A1Xc;>H+}+3#*Xce5iNj#fr#x`t)t9LL_`c|7i}V)$k5&d z39kq65>c%3^kqmu&iuVsU^|@N8aG;qc$Q?MU?OsWx}ZdNj~Pji@&v{YtoX1&A#-BI zXI?LP_#31u)-o`ZguncKM6A`oi3qPPwG#2Rxit3joYuz%VN8TXWIR!MgrMLfV+9Z} zka3TCD}?`hGWvGlt}T8HZIZ~pke)#2k6@*69rR| zEyzY7|D6Lg2k28CpxwY=5+3vSQL*zJsspsvi--8V7z04BTd2r*fc}7B;3Hz&S&T$H z0mKA+%tS;7Okfdx`kezb77M8k&^ohQO%zN-4!w0PlHWN%!}=)?&`l&W|2`sy&Z9a& zkLfh3KSZtdu|c5@(9VF>kW_qR3;_a$s5ohE>q!6a$w+yCen~R%zd=UI1N06soP=Bd zePr~SkCSmK;7~U`CuiWg1le5hTkH<1WhD3?)B+dK<~pvlS!?eGhYUmB#?gHZhB5YM zF0_j)m4hl^CN!qAE@KH{q3Q160CJ5wgCFRBeOp`bqcth&;e!f~-8{5C7Of5WBW47LnD{2KAl9a#_8C|^ z1T;w`F4pz|fww|Cy*c}Giz63>x#6{aIAo%07y}1CVExgkO(Y9pu zPCVGcP{rFde&VVdwvG@Naao{hB5`#j0ciX=;`*&u-P20kMo{5noH z{6QKAOU1Uv-wmMd->2~{y;!F8x%+4LtN@JQH^3yl~SVxQ( z7mc@@()6bZX#9WCSQ9k*`!x1KwfqK+V?Y%IEW9*MCdqhcoQFi*G_FOt!qWI78eEE# zwswx<9U;rGlQ!Z#F@o6Z0qTRDVPNlbP=rWa?0oTVwPQhr0$3XiiV%rwxcz|OkF(av z9PgnbXcA2ws#HGWF&7?NczVW9+Zxm(Xc-r6znanX!yX9!9ButlK18?DHXhXd`?M`W zwfqKcy8~S5Xgi7|2&y1p;iYXDNybClcqHPcZ7#|cmbPiO zw6rypS~moa3icI?mS;l*g!qI1MOPJhl{rTLCr+s>c?)N@#kpEJxzE9>i&J&b_J%xT7HAJN}vil+8U5#JhZh% zB5v9SqFiBV>t&A*D#bLxbN`Tza8$4*3H)i2)}TZmZo7aQ1@P7Z6d@AVc$)#iALp(9 zYP^f2X#%?w3wD^evdcXK;wSDC)Fo&d7jbh}(R5@k5d41;Hw4uE`^3GAYWWS~R)8w# zh}#K(PRMwOD~Ckf#MMB#!V-7713s!0S9Xn0>%og*_$4u}d#;7yZzeK?A?}U^Z4-%$ zzj8qE$N4Mmi1(CqW%q4vR6DGPEmwBQ_X^=h7JeFs1M7$};i7Ta8k+v}0D?b9V~&&D z$=1`fMWF8Ar*ROfGF2l1Xx8RE<(HpN{|rhU;}qQ!H31P>}_q`U2Qa-9Gun~!P@cv zz(1s0vZBMnK4B4z`=8Nk`KnW2V`*>W=*C_K(V0(kQsunu}Ks&1Br zl^ZY*_KI{59M3IdXOQ}2TpePk;l9S1q)U4%9ViM?1L}?j_#d@zf zXaRLBN5vKI&m_FQ?qEmMM>2}OL+(g_9IoF9>aVnSw0E>|brpccb)>3? zFp#u5Vr)SgzSa$I!O^g=BfO8t4{rj)h!Dc%hXqK$;DyAfAoKo|AKpR+{WCwzVKpIrk^OKwn++HMq5Q{w zIGw#0RPks1@Ch>N|HBX8dEtHtR{|v#+IYITueH%pjtYPic6^c3(!$lo%nBZE%-nFf zSQ}IjJ18#l#GMhkY&-0xTo@=&iADC-Hcpxj_RdbI6a=@$Wgmbau}B~f{_ldc^eooc zU<<4aZ5JUu_+Fm&x;EDK7Dgk$zY8p^tel)24c6MAx~#!Hr4!9GKqsb;x3+c_YlE4j z47v>?$V{+xoMrD|gEa}yTr^dd8M}hpP?veyTR6Ea)3R}0?dIgXOi;a^sKVi(iXIDW z;LXz>Hn3tx*cW+_E2xGipavpw1)qCB0AVA|xqyX4XvA)TwsdlGzy<^R!+#4jQ7^WC zxW07`)H39yb#~BQ@b|)4I5@AgpdM{4Z(&AzIV#0B+PjeeF4}R8Hk^4;H=B!8@XI3D zK#u{18R2wxv>QKR!bCLKLqUiC++c&cZBT{(aIirQX`luoaSiqrAo!Dm9p^*g-+@Du z@%0Hf3=BSou}OJ@Fe9#yO7V@jKN84;rnp92K-(!u4`Ph`8&-ja9n1~PusMwxaPKK3 z$L-e+<%^FleEC;< zEU@Vq+Yf^0;bNjhLo9~CC@sAaM-NV2@mSst{0AkcfP_d~T?z*R*rgR<1Jh1e-oV0b zCH6GOd?WCP4tPES-jZv)7DcGv2(qA>)^r=$3LEbP1ERgNu7xx9=tge<3zx%VLHIWs zUW6)yWaQ_9Jji7XNAS2qPO_WuVS^JUG;A{MQXxuWMk2lQF{s+qNG730xw^?iBXk{Z*1wer&&Dcr>3HJD#&f|N&_}bDxfCQ@n zgM_PL{cU)2LqTq@mJ!f&U+ZB5F97S@FL%NpROkT;EX2`mkz&WY52}Bsq z@c~FMR$6aZLVrnw4ZgE@Qw-F`r({B}BzDQ>I7^92$&_A+_>v=GHqSn3JB|rtcPlHg zUh8NpIpSB&v3n{ovS9)YiW9&N=zEY%66ncEE$~0qvt%TMz3VXF#tY1X=AHtVd@4{r ze8@!tZ{1);77p&f$$}TH#Lko2kVKlm zAvB%b9o@K^Aq<2{SQ-&PkM(Bh#n2Qn>=9BD^)Re^`cq?#5n>b)@?vd@@`c7a5Ge@2 zdiS`Ys&KoEiVMcNoMee;SPuY*!I6V)h=%pPKzgiW!hJD1P-5+an&jm*=0^vRkQeK8 zlrJ>a-;sgS6>x3~C?}S6m$m1biWqF0SUbA2CAAZxLq+l@e22g-r)jDKW%V!d#lYR1iJ?>^|4R zi}%#N-URG`L(RoMqGx?};zC~j%|iJ?^UobA2;g5Eu`9RW7KMrn_Vt$}OGNYa4gfKN z(2*4U|6wy4*E$n<16s=-ipi*Lvt5vKb zDkbK)S_N-LqDiaw(DTo{N=6x~Q6ATgcjHu|;9nJM4HEM5FB0Vo&A)7RH>Ah%=^Y~IU z<$PfqYLcJVVuRF2j+7F3sh2cUhg8cAsVma z4>QNr$iYf@9HG4q5Cdu;5?5Ru0|a~_lI}V{c4JD$lkcNB=2S{_adpUQGr!R@6H^aR zDbdB%^xKAeK3RWAC2Z#34~(GapVwCG)oj#pwS72+uM<6!K|)^s>7jg~`R9Wa1n}?V z`Ne5)i$%o+`}#YQC8GIycYqkdanbnae}o=uQwdMlM{2BHP?KD~P7opx33;);f%1jM z`V&$Rfc3(6_mkl!1$sl^!0hX~sHEt=?oDb#G_1cx(qk>FYe|pwTht^M)`TB@M?zk# z6+mkc3!$+#MhXJ3)*Uf06>h$$xL{rN1Eq2*CAzo@lnS8|*wZA}qxAgCQrSHUs!<-- zmjf^%T3q}Czp48pJXQJoto9i!$o=100n$cyzxlrJ=|&mjc?Sl0oZ;r1LA7mRgJ z&~IYYMZ;PRX^PHkvlGm5wL$sxV2s;vzDju=U;(IsNL=&P*T5)I;woFdGh3|(sEtpF zDXzj-Y-&>}F~n71?(<~AX8wKVNqYX}P4R$4p^mFpfgNxFd3_!0SvC^#@~;`?3(dcA zz&zNB0RFW#6q^LM#i+Po{_Q~}p(Ud6FNM^GXudx66g}2bgEZxUDb!dGj-|#Ld*|wO zB;>_<70MSH>oBAs0P8npfivND8x*wL7h>8ow+8&h@o!19RZHUI}YiF3_sv@Tb zyOW0get7b5Ad16VtrdVJx0qS{I-CAzrUv%8P&HivyFN2rwO;;LbI z^6J4uzN->8^Y4eo(etnL*)sg$2CCzFx3d(!PV{Uz67uqI5y}^uf7_6P0AKfa&sKt4 zJSr}jf1gN}i013k0I`4O>mldpv6hG{=nY4Q^4!1+HOb}c1R?eyAuraqP`=PuS0e=h zSdXy|zXG>`pf>~#%+~>oQAyE#eKV;I(R}^qd3vm0W}JcFDyPP}2sO!tHQ`51NXU!z zc+eWeLTId)BLx9icj;;;_3%r6a-t?`>gy5e_lxK2WsO}Vv4JEItN+0 zR7wnS)$jP{(G2TWc?tCViwvBMF^_T%l??2F1IX*^SkIm!AusgPr zzjg)w$#646#Rc;(jAV&u{7WabAsYW!*XgkyJ!3rti+T<<>KZlHVvrE(NXU!zT9hxe zuOCGU0tV)=6X7-$6&H-P3o0r4d?Aw5hG@J_y}=w;J-;2o@us~W z;1Z~TNL+Ds1`t57g^H`8#$VFUdJJJZQz_BK)k~*3kI0){z&b&tL>E_;rWqFp2idR* zoB8)+ZqoD5|Jq`DU+j2A9Nzr|M2dNFwd|f=19&25rA@tX>%TbeDSQCEKJ_YYHFV@N^Uudjtkb(fL zovg=d!Yu?97tHJXBuhl&buFn4(Xj5zVUDX)IN0S?hU);GK@CLWimUs;Fha0}imRX; zr!6>x%b-%Ci>pA%9G>X2eu{J~s0ymoDs(qV=*U*oaBOx!=yHLK+SSKR|0a(idoZ(i2iVMbiFz7ci>Y`z-hcrc>FF4)B zv8H=J!0gRyRdSIdA=qQq4{wJ%3m@}M?8C5E^v)?MWi%a}@uA+CzC zpLIwfZ06s;PN(NzI}-)`6%ACreizt56<4vIy+uM^{&fef5u+~D`I0J95Wv403rSVD z*`eZs`FE6LiD>+LNNPhgU!R>pkF`rzZTe?0Cf=jQ8Y9G9B;>`~8|4e_>!*=|0IZ#J z_Nc-w4;2@TbvMv&V$?;$S_Nr}?(3KD(_?KvOC5jz7!|LNpeA{bVVECXMM7Szi%C5c zir0OBc@Po-Sc_d!z5q8CDlQmnFH}hZ(R^J2AO>AR zbYG9krpG$DZJ;Wj6#aa#J3yt*}q#%ITU(%1?f?Fsm zE*R^_Buhl&bu+0A(Xf`wVUDZPj}-9dhGAT#d@i6DsDVgaaWxzaqtI~`)PUVRpF^cY z7gq_T_P_+bmgwRtZY$GufSr%&`8P)41^xAXcVGwfLN5LhMg}7xFaJ_dzR>(DM+yS? zM?4qM7xadRdCb@R3{gqZeSIUT4bk{lmrIYe@(*$P=UCsNCMmJ5M?zk#M}yWN7D8if zf)s?rdJ8Hp*w>RumWbx-pGj?qhPBlbdaR2jFVo+@s+&j6Yk*`G%LWN~u|A0Mg~s|W zQV@VOaqmSXDlQo7QJ~+%sEda6BBTl15Dn{fPnqND_74hUK&1@V0ak$;h{P3FzX7B8 zq8;6Q71Urp&Vi*i64b`0#1L2If9V}%EvHgqimN$Z?bHaH`S%~5(eqE+*aB0aydNMN z*Z~KS*VnP06(J!n|D-`{#Hb5(zNC#51n_UQ#m|Xwb3(-h^Y1jt64ChgjMRo`{L_Cy zkM+V0PwCgKraz~~8Y6@u67pib8RZM@>lcxN0IciRbykJjYgAk?)_p*~iBT5~YYn6+ zy00g_q{n)^-PeAQ*p$3JftuvywHWlHWF+Lpx`Nb0p?EC=%!7~!z}oNO&{3f9=kd(!VFr2iO6Jfs23OH`cQQNXW~-bd)bN|Gpyy0sJGb1IU5i5ZqwC z4q$>xitg*%No|Pc>%ZR9V=be;ocC!u_yv9>@80{~vxfp+-<~18)y#@(+v5rLfLSvnc6a--1 z7TgN}w>nf@FxC@6zabk$=k*GtDSBM>En<$VJNjt0USFkrKfqd01ChAm>Q7)4U$mo} zuM#y(0JZTcF~!w=E|*z0R7wnSm1vA+Usg=dza7#Y>7T893hbbYt5`ebNXW}SSO#$-bdiDp{t=y9hl&g4-$jxoqWOA0sSVM5eQ_y0*1m0*je@6mQV@Xkp`dV8xP3sy1!FA(`b~_wXjsofnxgyq?N9VrXKb*BM<{Bn&!HwM zj$vYAcae}6>snF|h2r&aU><}-0M^h-nDkL`!B_{PlA_1e1X3HK@%s5^=D7OPZaV}F zzrITOy?_Ux1|o6ARU;sPy29SSid7OorNk6hftFt~mC`?ttD0Zv`ImUL7yTObxH1}F zhh5V~LSFuPpnRcy{RC1Fl7Ej-al!oS1p58Yd|er7itg+2<@8vWjv9?~kLrGaFw`WK zulrp@LSC%jlX@tWulEGz3Fqs6kccx-alu%-qmrWgdJL%z(R_ViB|X-o2bI!4Gu*y{ zf!8uf$cwce$`_i~K1e}GUdN*1g0X%_vP86FSa*OJ9DdPxtyskzSJzM31^dbHT)+@e z1ChAm>RDhE1Y3l-TE!})Qeuv)RjfY11iqG-<0{j2fbeR1{_S0RUmo_Bat^f(*a3%u z!q=-Vto?j3yt+tq#z{Lzff_(SWgH2hUSaTYg?o#dR+DY&Ky^Lmd=53k>P#- zZ%_k~xZsfRv2G^yP$*uH1Li?U1Ymu=&5}g8Ek?x! zW4#BJ6g{q{klGN9*98sCadm*-B_)j8w9f_Pf*Od#6<3!6fzWX^#bD=&67YV2-Be0+ zarMa-an*v^1*vzbl<4B>z-96G!TSL?gw6c>`Hl4ad)K8NQ=s&9rJoeOPV`I{33>Uq z5#*tVy0RE|7lTwG4?SRX}Aa``$z zh(sjh#riX;heG*!e_);vzTTkLkODVNR9rCDKB%PVzJ7+(hG@P%^cOwWP3Nky2i7UE z?$%7fYr>C)BOx!=i%`DMyxxWs1n}C?dhBhu#iQbavHnD|M6_d=G(Zdvzv#T4DkdQ& z4m9bWV~r`chk!9$2N(@%AQD$xy$p96Ob8XdY(_oNI?d!q74mb>4z7Bq4Jv)Jfy!?BH@`dJKdtjarzD~R! zKoJ!e%s+cnQgmNGNNPhgUzZf8$GYA9C-}Y7RM%JQQIot_`$0nVY>Pw0i?s&I7aD69 zq#(f8+ky3IxP_zQg0X%|vP3jrZwnCnXTH9^9X-~&f}YZ!huNSed9lX)$Oj2|u|ALT zg~s|VQV@VO@qPda&>I2==IdMPsHEt;b|keS8m~jzGso40m;LZ-TyVZhc|X8rPy>;; z;%XNl;EQ$)*8!rt4o_#XsgxMvs#xv72$nmQ5<^@St4jKzO4!W5|Ji|_f5rnN)T zalHuGK^0fAo;4vMFaO4a)}WHm{9BF`1n|#hN`J>{3eLan2iDE^T z%!c;MZC|aHO{GK^S7o%fZgNat&LM2(-y3zM=U>f=8Q5hH%DAf9g~Hc~o*5${FaNfq ze4%~)DpC-@zYR-#Q{YyBiVNmnf6#Bj6GY?RETk#AucvmS$J(<;J^g(3G-{H|*9k(T zAt5i;Ur9X_%GU=2^B^Pwu--F1U@F|^qvC?G-iAuTQ5OyCOQbeL^Yt;3^jMp$dq)30 zp+4Oycun}xI3(o7dIicCn%BWdK>*fnlPhk)?HVdB80%`1C8A+13lM|DFFLPTJ(%O_ z<#%PR?_HsME?^R4z7Bq4Jv)bly!^{Y`9kxr8!%4@Uniamn2L%E=AR2H zDY~!!AA8>(XJhsKe-J|MA%u~Jkjv1J%gmV}gxnHB?hQ#uLKFrcgb%_8A@_!m%Me0y zBXSAhgCrpzgocq2!f)+!*8aAiefHWHfB&A>Ydf{qT6?Yae%@<6XZCr{a~23b+-ki@ zK^p7oN5%!O=_;%<70_T^6ooR@O>o{C>!E01sCAC@QdBo%eM*SLt=4k_VvknqW1gh3 z9yV(-{XN3I=u+l&ppM3(P{w)_&Rb)B9t{jwKZM^5WFs$R!*S5>12jfWcU}(`e7Ny? zb|HOVon10(@N+_PA7CP^zz4_6s!`YL$x zMe&$JU$YdU`M09&N9^>d_W{x&4pm@{EI^a1OMPRSUzK8Dyo}n zeUA`{Tdm&`e7Nzi|5F;QPgZ<^#JWA4Bo)>HLJUBmjP(+nx5oMq8W^ySshu9h#ywOw zV;u>3v+!9YZdi9mOXkB3>yt%kth@L8B%H*0C%TmTY8Y5VokF3Ebsq4>Gqn2KAPNl( zSi_pnvC#|F%~&rMB5}j|fZ)Ro>ubgIeYM8vy}`eG$$fxdVFiA8`f48#FtE$P$kehm z`*iHhkEp)-S6Yu^KPP;&d6CM9>Z?JcH=>TJjHtefp0@IsLti^SP4lm1m#Xx0DjO76 z)H=_s6AESiO~HBVT2Dd)1OHyB<%?(IBC4DDR~qu>mEgv|=4k0&>)(~2u}*HbI*gqi z<(^9-y7bgKC&V5U%2?kOGPJ7o@(><_#DMkL%I7Pv(HhmwSWiYx_rAJS@ZnbL)l1P> z59xN9{(g3ek~FWkMxs#0x+l(C^Li#4n6aJ#SkFj7bu-p?gh!)!+&l{=Za4RA1#rl^}!=PgGy!Mm*-w*A1m< z{-ux3!Y&Z%TAvSbu+!kF_2Bv${*5S<`F8>5t@&37!n3G#{+)F{s+;*Y6gAyzeVO3H zt=3DGp|L);<_`Vcwj9rBur7^48S7VZ-WuysXke&yj`b>3H)DNXh{Ubd3jkt|R_l|V zrLlf_#B3VtLFiJ+>&YmTu};Q$Ypm1Jz!K{skPXK{zYov~HQjkVLh#|n>xJR^zWQsC zcNm(Q?*&YU75L%ltHnVO((S1CtFS_tFK0}0OO=tPuf`9Hn&uy?GSc?d_#dKp%%QKj z%F+CL_+>WD^M4@@cB*Cm1v1NBHYk+&7mf4Q{OgMb2L5Gg>08gnY*aVb`XM0_w_1N7 z_;BOjkmqQu@BF%q{%&PAI7uq31B4igLK*9oIB$*h2{bTZ{ogAS>avjqvazf6DAaVX z^|u8dZdjiyPhg)-KK!5hQE8tVpVV8Ghv`&VT)`lGs; z*DHib+<1LV@ZpB_?F#z7`pJweVXX7e&k4K?EAYe9R|kT?y00$DIpt=~>06?|P#IBu zb>eIFE?u}YJ^GZ&i0Z3}d0&}ucJVjKJm%2X9?#SK>vM1fyVY0yezjReMXmG9;!r5_ zZx+s5^KT~_82I=4C*Mc1aSheY{3{Q6^Ga~zUt6?fKHT_suo8{+xMi{Qz3N-grKi?8 zAr7HX#yT^2;~84*14N*K0qdFvXTQKkM^rasJyVFp4eRZK4>zpqRH3on`&L`}JHz3X z6};wk6oo<=>wY+I&FlGSV8Hr`yyrKv@g1t0vCaf}^Rw=Tb!D`4=XHy!`o6lYYp-A* zA@>35!3zBF^woV3lyzSXt^nWKht)!isJ_aLB2`9IU*$$T=Frz|5j6k4?3+S=2PgsJ zU?)c-w3T6IX#d&M~m4NV=7X$zJ-=>XF-ORsHsL3L6zOE&vEGIA)>z*}152z+K{gx*{XRf@)O6?dIKhV-ua`&a`|6tE=feYdllKG6 zg%$YW>8oWxz`!mC!}z%s=%l`pzA@!>m65Km_V4nUf3nI**H=G!@mbA%Rt0O){2P0A zC}WqJfA=8{RbLHcR;Wf$DD$r&&Rg?u5E>Zxcj4`|No*`cb#tws5F&A_^=yC`=eQgH zM!ra6{r1J5>E8?JjV?oKoe^Rb3T3R<0aya z2|nDgzEX?Ey5^os!8%vn({>bHhG5O=C>@0|)+NCk!@?TtW@uo*`pk~fwb&Sf>SkUi z3X!<+`WL~68`ckN>-*}Bgj!Do`UrVHz#p&zKRkW)Ll7{qt@`TWtmVr2;@hUKQyJ;{ zDsz2aWu)t?JYMx{pSm>vez@9>-iK;aCrBj^|9ED7Q7H3o0nS_VZ$BCs_?OA|)pj;+ zqq>=Y5s){p1ULS5MoahiP{*Tatk?K2u^iRcurze(!J41X6DX9i&I#UlhF1Fkwa~zT zHTT;C)y-JX7b0=PdavNat=8-NX{_foevkfcTcv0PuX)Z5P$*+P80W2doqz_GSRX`n zGuAmEZ+_O@u&#lY?!0anqwlMKt-l@oTa4TXXaXzn!_!xfKv33wHMl~C|8ZC?#7N&) zGyG91BYj`hJtuHSEX}{~f9U)yi&gpe(rSoeEvezj;l4b~ATl(FuP^VV2TLjy~!x1qWj z>zhI(ZdgA9h_Tb}UhDH-qOsohaRR;PG8SDbc|9M6GS&xh-Wu!sXkdwTImm|Np#R&T z8)~}qdWzt~jn``%==*BZ^ezAFuPUDpumo1%ho`Sr1VKoP)Z7o?>$<5)bXS#;rmvnU z`O-T7OqG$guclVid~Q{|5zW8BdtQ%V*(&>L)`mo_v&>4MQ08BAoVVuRa5OOSZ$q1E zJ{ZeU-CXNug-G0LJr5wpIqt^4@h{U@ciIwFmBe}gx|C}@K!^z_l(F85^VV2jLIVTV zx5Gw9u~87R;W+4Py%}n{*ZMHQha1*68q-*>$hM2-^(k~IV-0(E6_anGP{z6}cw<;t zW8DS~3|RYoofFv@iRxxvZxJGKha1+}o9g@O(HnDv_qve#0C$@ZeRT{7tov%q zy;T>KezHZ(*D52buU6a|(Ph-byD8}^BdV`HXzSaYezj-}t623h@D-YW9iLuZlUXVI zY9|1bQOUzUp4lK2%KTe~^Va-3iUtP$+je}MtQKNK^;IzHnhA^=sEnw- z3Pv&d=L76(Me}dcxxMt?OB*4MkiN<@+mAw-e-CipntzobJm$r~zwGebIyTy&x|x5| zP?JUC#=m62hg+@JY)xbR1#CBwFZr)zcV};U3#uz5JI4i5>O~(eGKQVvCazNS@8NU zJR5+GDyVM8x;JXN^Lm!x!;ROQ+vxl1%4xp^?;&J;Rk;ta5?0`cr>|BAK}d_#>;tf; z>7~4-GSc+bz+>U&tBf>#)$p9aGVN&o6icTLv{!@g8OVKr%&-DKJbiUi2Zz4O zpQe|pGNSq_J{B(drpk!wtN47g7#?%z>xXa9{Hy&_cpYY;?5lAA1*4LOe>}6{D3tlP z2IsB$cM1&*{Nv9F%nsSu)p{&yy4QL?!G|0FE_R}^UiU@iFcRxS=+c8VC&VQb%2*cz zZ%(z|7!3?q!!rUmvoR3W&9%N-h{Ubde-eDSVcoh5jrF~}-RbXOM0Hm1n%7Yq6v|kS z!+C37Z$twF*6@tLdTg9Vbu-pQA#Z-x-LP(mmduA6*1fvw`|63v5B_3x&2#SGS4u;kJtL(4yr(m@ZBdV{$!huKdHB%W;eHDxX&o_ft;yBWc=HJ$i=jiXT zCP5q_eHEMpf;@^snSa^98$at-dw!8-TWpy4Dw?fhE@aP~D7mR>+&5bvLZ5p{0ARuX>Zlx=Hb`SdQxV z3p3HB=NbmefjU}^LK*8{gbb~Cog2bqkQlJ$&k3xF>SnA5pr-r#g$05SH(sa2>HF&9 z88!d;+)B9*upU<6ho`UB0YONM)a(O<9s54f|E|hN(^tzXqS)As>Sq337b0=1^^$-X zD+%{npZyk%b)lv;>CXs9q05k3XM~u8LK*A5IB$*h9W*dteKB9tC^pJKHXH~2K0qhb zbg%V^f)BS^&+;~n^_1{l?BuEYYC5`mq&heKqbzcZR0s`2aa#1%7z?>I^uI)_rx%f%js1_Uhx`tumte z>d>(KAAkD4UsCR?jHtev|H&!0@9*27>8oSjq4{?u@y+1#y;R>z=?hS>vhwhcXEqjv zGXFN=yfy#Mqk(~c)#`Mt!$w}n#;(>Iqo#YU4;Fm5@$Xt+8teHJTZc1dDfe7Xpi2+d zoDkPhC}aH$cw<;tW8Df33|N2r?CoSWK16kMt#1?}ajW%T1s`r$ckEANUHIy!fihNN z-JqX>*SwB8p-{$p3eH<&orDGktTVbSh+^X+s++Mc4SDmk?uK=9v}8WquFNXafI~Mpoa|qDHO{5%LCr{S-1LoDGCh?{Nv9D=!NQL{w)?FapT_s z!G|0F8V#heE>~w~@VkDhYgoj48mt?mP{w)~&Rf^|3N*09`Ut9-p%?a}DFX-iSgO>kC4LR=h3*;W0=ovG$|78S9~_>HdCUnc%~X z*WV4&_tjOa76t$ACGQ8=3M=r#(^u<*Af!d=_5r$f>X!0>%1F~!<6kMAkg{53r0J_+ z&7)SO@R&nis}G_1*SF^BqRc|sS4#|5*LoPsEE0t>|9axQb*;}t0|WmuUCFtPjTBTj z^Y4xjiCe9g1;kiMxYzo^4`{3xw7*C{CvXC~lxsaeh{Y(Bu|ACR)>vnP@GNTm&79?v z*r;FBymVUo_6J5$!2kI#I&>%!I*3md`jdfo%Fz|Z7 z<@QNz%tm!H)`x^h+<5&!@ZpAasSovibxiwanFD=5oKT9RzW( zD)jJ=XEqsyGXIis-kN{uXkg%9vFNY1vrz=H;XO~k=hq4~-D`b>;KPl7w};bMw;c3j zMH1_?=+c8VC&V2T%2-zbZww1-tlOi30qg%f-6fihF{o~?^&}w@w_3j}_;ACz$4DCM zFXvSaem<@G+o0J91+RG>#i3BfdKS)GW4#j%3|LQv-wb5q8mgPIE)RM0v+jm>R7kheyk8A`YW^Io7Ggy8)u7u9ep$}^nt%Htj*z|z{sNhuN1@EW!r+acb*sOZ8lZuJfBb%c{-|!| z-wGiTH~t+He7Nzi#TXjv7hip{G&{x0zYS`QR$~qN8U9u%l(8O-^VYSVhz16%`JT&3 zR5xQ?2=eA<-3{w{Xz5<-+x|skeXB|F>Lk_)=+biy1MNT^C81Ep`l^tj6|YM`cnlH) z*7xB%0cq71WY=;&2;pwYQK@id+HGMU{ z%<;C-@hT%tU+sFm`9l9jm64{e`YP_K8O>u3eXTQ|=HF+Bmeaq(5w(OoP$=`S zAI@9X`g}An@bBp@o2s$#9jcr8mkIKIv|6u>mhQE_{9_vH#$P1SdoI(^rCjR)Laacc zjP*}KhE}zn1H!YY^+`kS)@7qQs++Oyi<<7WK3DMJR_g^P(pVR*U4iEHeRL^f9jK#1 z6M_)QSU1FZYhDjR0|T#Lk4TPSV`!Bpsf4q#KnmbCz_N`{ZVB^_0`fb6K`}n za3du*gb?D1>Z`qa=6PrSAHQq%S7-d2=3kD)_Vm3!!yyiK8a({tnaxC@%)ebYZ_U4( zXkg&q-nLEB*eC_raBk@L0otRcd##TXe7M#6!^t$(b3S{gHe;6Z{puxj>A{*4A``62 za+a~KhV#}~cS8dM)*0_DNMU0Vs+((lrx1x-t^XnTaKpOKR2u6~3m z^+lnK^#Yu?#(F;*7_h$bpI#|!+(vaX))A05V}m=dJENt0Umf|0zOOc%QZmps>3x9r zUCEY9U5cUj?@Tqd1ij)mOnNUcV1;Wg5-D5v%Iaef22B z!A_v3)_IWWD3tkE61?%VZuK6j85$V)2fuOcXJZJeoB5Y0MB-NKzX(3uYQ5bI8tZ-| zhp?d4eKmHv2J7}Hl(C+O^VYS#1r02*K8NaNtV=-N{H(iS-2^S&YkkLmXsoAyzmc(4 zjrD4DDf2o|M>|m{V|`1=(2CdL5FUfXfc4+72f#)PR5xQi0X5zG>L$U58?TSg)c4h^ z70;Cp;7#rW?1dHh;pwZbK@id+HGLHxqEapSW0jGnuXas7x5%HQGSc)_=&Vm@_5te8 zrunz;twzrU@KEjVSDK}+^+09~P$=_nFwR@odIB04_~-M@NMz$6s+;+j1M+^fTCahY z?zO)5QyS}5dw!swAwCCP%C#OKL?Q}htbY|Uw5s*|5S~S?-zbqiij6v`ZpL~LYP#3@ z62XUCtrwrG!Fpcb;QJ>Pud~jfcnx(_0);Zx&2iqE*Td1k$m=(&`h0IzU5@Hztj`LO zxLw2Y0AlR)yYsrrJbhoSHe{>eet>6S1%7z?>M97zx~~R*`Th5(Gb$shuMRu3w_w~y zaY=Kg^hBM4d;e_AD|m*y4U&?!G~L|XJ0^L{nzNz^fTowRb0i!3{*GQ`hFo2w_3j|_;AB|;6fVf8Xp91%T~W%?F1)@;Pvy#gHR}A zy$t8Au|A3h2CRY8iEKPXbu-qrAa54FJFk19rF&l;zewL#m)w{ae1FE?uPW~c7zQiw z!_!x@fFPvXY2UBHKER7ABdV_k-Dddvs*I?<%8hu;p|3X<)BKxvcNsfHYW|&qI70Xr zTtCBq6NNJW%7Qn3)~(({wLt>||M+LrBT?PVzb!%}Znb_v@ZnbLU6#^Ve|~KxeNS8C zB^s=|p-{$pI?h|ydKwy7VtobG%~*#+-u$e)Vf`vvy4U)?&uOgZO<1FO{w2EfT*El8 z_oGn8`hk$46|XBncnlIttlOcw8S81N>E2h91s`s_KAoWNtM$5O3f_M~?gJc#75L%l zs~th$*jIhNPgF+wzUuSsR2k{}Dvwz8+H^V1zbVs}(Z3B6xs0rJpRXATW&Vx8dFxtV zjRr>k1#>%&>Sq4shrA!H)}ztVz1BB>L1W!}{X6tNz+!Z%sCA!j3kqedFAEu3)p}6~ z&#KmgSl35&GuFdV)4kSL3O?Lwz067)>x`Sls<0fCpAqI>q2YBn3T3R@;=DDl$D)BH zuh*iw8S6_zByQKR!hje%{qDT3xk}$x6AL^Myw`>GRptEv6<`H^c>3xF2+F#z@?Y2c zO=V>2t0f?W5KoWZSC@WC^KbLIet~3_{F@AMu+!kFb)H!Q3T6Hs!+C4|Wrgr8YMuXX zU=>t1^RG8*y4U(F!G~L|=Uqc%-S%=_#yWMa-$9oitOKV8{>i^O2$76+J)F13dH@<& z*7`hDH)DNNh{UbdGXr9eR_hH2G4c5I;C}TYr=dH2cg9esZ-#~RU z)|DV{e%9Tv?tqreha1*MzM`@2_vaS{7*mzJPC}Q8zS=(KC<1Q&oK$ju4&Ipl)LK*8ngbb}}y)=Z! zATeMaf3Ip38%lL@sSkJ3onEoA>LR-|l4irx%6v|k4#(8UA zPeua+)&ZyR&t_CNV|`tS#EsV_0Wo&^-FfZbrthoGevb(x61aa=xerhcR^W%HuWo~& zT>5HYkqniQuCIbsd^m&<;_1=*>Z)&O{!K48GLi+V?5i^&4t5$sYMoiEMxo5VUvS=< zf4Lz%i&|$s*-;bK&HNjHn(no}K=9#K>qU}jtb11q@_H-^Wvn;hyfxP6(ZGx~ zqqQ$DWW#aL?*lYOO?O@o7JRtzdUmS5uhwq+XYg}EavxwKtiTUXUo8lNkZz~n2gvZh ztTNK~)eQe|m65)$W@w(jnq@o9zg^d&*vVA))pUr1NfrFd@Mld63T6J)zzY=^lh3cNl_HdF&tOuY_#(D|PTVs6) z4J@&~hw5gmBOz}VzI&~AM@#0z4eOKN(pV2__9VT(x)WV0d3_3nGS+#(8_&?{Z-Xc_ zu*A9-s++N1EJWhQ>jQ!hH>|Ji)c4hb)1D~rta$pq~Yjh%V(?4-jGx3T3SC3K?3} zdU*(sL1MsqdPeKIY_vvoGuD$))4i{56@0kWdiA|D)@@#BU5-_P^6#(`dnjJ}&O}9` zP{z6^&Rg?(CK?#9h9KFU3sq83-Hi1eArd!Umj%Sw>38RKqka0m+V1UVOT)sN-wUV> zEAYe9S9d^A)_wI@qi;*U|I1UUf2oY9zS`%<&n6a1o}E$&LJ09h_0{^Hy}RP+J4ZC{ zS2ui5^KaL;AJDZvAL3xA!BguzvyCW}`F8>5t@&37!n3IL#W!}Pvf)Q{GyjI7rhBa~ z6MVSUdZ`06*3o~iugI9Cto0oG6p|$!!)snglTj#Ros9F=Sf`_b0qgU-7e=#D1hU~c z==T9yp{6^pM+iRLc)jokeP8XBFE(3%+vGmLbXb8Op1xWf1R>o{vk!2FeQUp^%82T# z;5P86vau>7s;`2P;rRf$4%7TAe(pv3`4l%Hj*z|z{sNihJ`@zn{ENnUYyR~`0|Wo~ z@2ky5b#tvB5+ZS{^#_6vH~tMdLSsE`+C7G*y4JhFNm5}QAjD7<%2=<&d26gspn(Bv z{_MIekd0lfN1>*Bt-me!aKrlCe`&0Hlzoo=Uci2I>FKMyj?SY{#=0? zSo7xt^hb3wuU80>xbga!;KL2;+eh_%HPeX`|MXSmKEP#Ifghf}IuHadeKoMi7b+u7 zU-iAwZL9y3%1F~!!Ba+E9&_kxkK;7|20xScF=nBB57q3Ly4C|O;!r5_Zx+s5^KT~_ z82HEYx`ygz{*{Nkc_p~1R5ByK9loE6dN5;-Hi22Ard#Nw+lYpu&(nHjrD*}G`}SleuCt6M%^eB%2@Zq zd23$JM*{=a@U{qcEaJ<3hw5gmGeO?`th-@d878tx7DCfTFgVjQebbS?E$49D+^nH~_ta{z{GtIvoN9WV;R}&x(b^=3coq0(@q0GOl zIB(6r5)dBqV&ot5>hm>1bu<4)p(cyOjelzdA8xf??iU*CK`*@&&N5K;)%>SutmDh0 zP{z6g&Rb(W0S(Mp$3vj;8&KVhb-ECV8`i}DF?RahYkkIP8tZ-ir$Vwn6f^0Yr`h9@*sOiq@ae@ywUN1kR@2e-4wJylApq~>s7gpeh zr>~X)0Rx+zFYDh8d|hRv@2eU9$toj#U)9|YQ1Dlpf7f%=ie%X;YyCdNLG;xOf1$HM zq0GOAIB(6rL1^%FuQZnd5b5aS$oCDyqh8;*m%)*GOvd#%4G_;ADe%KvDri+xjpA*tr|QFN)qIvs^F z)+NCk!@?TtW@uoE^$=7y^Ey$8#EsX#2tM4fet2HrS0_9i8@&I5{9eExumV3kef2{S zxb#)n^=uryPGzL&tEt;IEcTyQ8R`4#qHlQ2p|5=|()=5`aseZYntyFB1gRwRFOXSZ z6w3Ttfb-V;+m8kY{{8*1MKv~Vqq>=Y5s){p1ULS5MoahiP{)6xu^!!aIsJXBG;}Fr z9U#OB6v|lV1aCY;t9^i4Xkftlm-)G)*yw@kW~}E6k+@;KSMcFh>-8_wSf_n*zZA*q zN|)5U27^W4G(e$@^T zf$C=dO+ZZ+i5vem2|nCvy~#<>ro`g`;SQkxKV9f~;fkGMU?l^Ca^)xgvU>%=p zw4aS_sBXskrVxo6*3ST9?DV_W`n+p2)~W42pugKT7F~LH&Fg4B3T3Ph;Jh`~_tC(B z_3cIv)7U5n*>D{6e;af|O?O^T5q!AudToZjul|&hIfyrTKfn@Lfghf}S`h>xEmCto zKv(vA0bNx_R9_9c&G64u8Bu)|jKY|wpt0)T;(yTm`zTv`mXo^HvtC!!Iw*H#{z{-w z=3jH1x8~n)G%)Zl3w$Sljpe9ruJyA*ByP2y2N2^NcjMpq8#LCHnnXpCSPwv#o?2(x z4C@Ifl(F85^VV2jLIVTVu(cZa4n{%9hU1{G^=7E)UhBgIA8uIR_>;!ES+%~w-?3AD z2jdjFl(7zDSl>jUjCEP?#;~x)x(yl_u;#ymF%s3yyxt;2;>PO>f)6*Wv)|J9)jBhJ z8}Q0Ct9 z^Rw=Tbu?NsA8uH8xu@@|;maHS^L;Dj{Q$4R3jFZAUp)yyS@+f0N}ms258n&O2djk` zQGK;ylYlnCB;rn;_ggg8R_D$i^` z3T6I1z0x*6;HLL_cjR{+G=>36U7r4MPWuiUx8 z7FAYP#3@Xu*eDt>1f$#`?_N4Enck&!bBv*7s2; zV_g}%F)XaH?t})GSdT|_Gq2NxNZfdRRq)}4b>3|HzWPi|Iq^gYcebwi?sWQ^{RUWbG^~3Bm|BCFIA23n&)i{8HQAxo+pKmw{W&W+f zd29ZiLIWfJg1KdfZ0u@17B$^#y`SL2jei#(r?IX+WCQ&?t3&8giS;EE%2*czZ%(z| z7!5439*FAZT3;y2n&iS=nz zH)CBC^5$pV4eN$z$$YqB-7A;AufAO^OYr^+_I_2l56}Tt;D@KLo`awm*jD?i_*DIv zr(m@ZBST+}Z>BPO^uBuJ37UTmVn3kYLnT2RA$^r+b`*s&|FVHMe%7t_{36l7z(4rC zfO>3nL3K0#<_M9v@o$&l!;OFbJT%sGk2T86n5DdimCvof8hIEWi$WRe_i*02))%9J z0c-FZ&&EDfH)EX@^5$pV4eM%X>0axr^3qt}N>2*De^Tvl%|w^VYuIWO%2@v*WN5|f z+z=jv#Edm4!SgRs-Hi1B)O3Hput4zP#_N=P`o3EK=>@^xZX@>r*24AP8xZ zntcFRB*Xu%%1GZ=GyDlEBYj`h-49T)0L{M|U3LeUr>ylt`N>+(@K-{i%)ib!Z(Zw? z(ZI;R;AhmEQQgeH>p~=MwO$esV$3~eSig`nAN}37QRq@p>lywzD3r0@i}Ti4 z-$4UQtjj<)90&bAKqu66ul0$754T#+Qi#TSaL;R%SQRUIosKS*SZ94Qn6r#^4V<^e zIt~piv7Uw^U!Zv4CU6pi(p+eQRF5>?jv33MrA z9U#PY6v|jX1Kt=G)>yYf0|VBh>%X#Vf+! z{<(iuxew4CR^W%HuU>(mto!QxJ?)y-=+n_32CIb_QGNBo`DUdy-v1$`jmn7Xt6#6) z5b@G?32?t5b)0;f=3nsEg+MWKO#f9ulNwa zli4_e>SnBSL*D$XyJ1}$E!}H$$>dtPfW_l9^S4@)~v% zUCLMo>L~ZKL5O6mqjBCE>%M4U;PocJnT^?~ZpQkM5Q!VF9|%6&ur5_r-&gPDc-8Q` zfdya%et7!oJUERmebv{m_-OwRDkEKAt@80#DOn+e5Ko%E>Z?^@gQl-eDo68gG%9vp{9GSj}Uyg@$dF? zG}b@gy39gR*ZNs>DPtYa2yq96GS(Hq8^gjH>-K11!1|-u&y&~~gX-p5PZA<=tM$u* z4>zoPRG_iGnAACp#JX8|g4e8$;!r4KJqzcpvEGRW2CR3tniIvwHB>iaT^{o0XWb3! zwrI(GxM4k{qQ0+g$~7za{UY*yfVW`!PNqK`*)x?{s*iKe)BJ0Htv|c1RbA`*Ar56hGlnXkg$U z^U03>sBY%p3Lz3V{v8v1xbd$=Wg6=v8JQSZYOHHjqOp!|g+dwY(Kv5i>xpP!#yTDX zjX#O%W~>WA-u$e)VOw{JGef5oA(}K?yChrH>4lD4((^s2mhQE_ygH5b50!^KOJY3@U3zLgcrG*iD^Mt7 z{gaTPRjucM@GNSbV_hB9%~Zdmaze`Bo4^RwN;D@KLE`^}n z`fB_O(Lbt;G<~&e^0`I++z>*DCv9I{lwb3FfEhJu{vG}AP5K%3!yyiK8bbL8naxC@ z%)ebYZ_U4(Xkg^u=Mg^N=MkkK8_o^=K0tfabg%Vsf)BS^fA}Jeb)}Xg7+C6S*d=r+ zV;vwwCRml_EMr{_=dH2sh6V<#Z)5!B>SnAXAaBM7cV2f!OZUDyvW~v5KGq}0 zKhL#R-Vg8|tiTUXU%dxGF|e)PuRdMk#{*4EWJFbk)k2J@zPjtHLEr69cq2JZWkmJW zztVaX`#ItHXdZLu>y^4R|2ln@9xzbu?;nLYLi#GtEFFb1|4M>4e%7ttLp4JK1OGZ- zj<3ST5L7qwFHwlZt=4}Le7MzmyJ#Bg$O11`Wne3@j*U`a%?Z&Sg)-I?ao)Ptx1fOm z>wcBbuV>>Ns++Mc0eSPY?uK;}v~;ia9ex_?k*6Edyk3niJ=ZW^M>|m{V|`1=(2CdL z5FUfXfc4qRaS?2^Ky@?L6HwE=uWk~2xbgaUjJ~f9{?ix60H=R1U@xq|4^Llh4T6vs zso!7C@PDi_qWWskZH7NdWkmH=Zp32_eXU=Q=HG+t1=!;h)U{qIR#EH0^)vhpP$=_n zFwR@odIB04_{aY?J&5XN{^fwYAFbAFprw1QudPpG{jW*UVI|seTv}2t=6+Qrm=2OKAOH)_&U0ju?`R-$IHQ- zWvpxCyfxOn(ZGOp&zEOKu`vVH&9%N?h{Ubd?+QNLupZci#`@sY*J-Rf!AVl{+E+Ds z5DI0im*KoM)<@C6fHhph64`i&>SnBKLEbEUcV72IOZUDyzNx;iW?cFDpZlzo`vAjW z1%7z?Y8DWLbUW((>d2oW4rD%1Ip#%`5!F{8>}~qre6Jlz>8moL`f9$G)n4m%<*^tZ zbLi`hS7`oC?D7{oJ?i(Xry!0H{_)IiqEO~vS@6csy48EAHfUhrU*UHL$FeaJ)y@3d zB1Ga=>lXwcZnfT}IgRz3SH2JKL8R2v@jdrMR#(ElRy7$#&!G{~KPq)p&faqfo}WEzVo>dMp|kc>Oov%*I+&H)DNCh{Ww0Ru~Xtr{A5|HQVa@>N9yp zGr%;@39JAs@WazrH$YI#*6MRB&iy~+bSYyUAVmJxgAmDB*TZ>htOuZhq1NBbSw4x4 zd8ls2`lt|zTdijX#2&5IKkPtbUG>+krAc1Lp-UO-KphQ7p^Wt!oVUjM6dD+?9&ovR z5*yhe8;*m1e=8O>-Fe+l@ZrYmsU7uwHL~`Wq5-_weOAhSfYGo5KRkUk7YG>GR(-YU zQ`_qA7~eKEMrB0x)j$7gK4AK}eJO)fMpR#o%=hQd19o+a;W3B4-g|@Q-^zK}7`xPc z^*qGEPPK=BJhS^Kl=)W~yfGtd{&hkF1OJLef3=;B@u+UD^)w+8w_3j{_;BN2ug)~q z(P7U%Ph#DwlLBi_h~6lav7U?b)>!XB0|VCodAdt78#hqhjCCc*o1b+ztUI73^Wlc| zkuEgW8m|JVBJ??g;e&}`KKx)s;>s!X88B3 zjHtc}MvL_O0Ij>z{2SG9JzeWj-4wOXGi!rFnSbMO-n!N|qJe>b{PzM*qq>=YMImoq z32yvrh?efPp3;NHx>D|$k?a&J|29~GEX9*dgp@1fQUKHO@(VjPWiO3wIjhMf}YLT{>h9jK#9D3r18jPusKo{R+3=!ZoDoDh_Tb}&TD^9eP1oIH1(g)t(5lzRD%`x;pwZ}ASed5)%#Vp!V~|# z5tX4b()87sj`5%R!y$waPny0855@f>ipL!Kx~dn=zYpI}r+?pSCd9!`LrAT&%vPgN z=HD+kZ_U5l5S~S?ckTLF6*g+3x|x3iP}9BE7YIJwYQ0Er8tXTQEN0wM*LtS6)K~`y zQ51zT)=hBU8tb8GV5s$rt3jFZ&)q)@h>2}op0MAbOE9da`*OFgW8Bu+;?XZRUZh!K5)Nqv%)mIPn zJkY1|#>|?}t+Mo``PVRWS^D{_=@3UqU*(x)eJ3cC`Bwwyt@#&+1_u89wr|*GHm0Jw zxz_gxk+{|REy0Hy|N8f%vA$7gK}E9G+rvo;!I~3d019QSm*BiL)`!r*fc2K~&sJjN z9;%zMj)c5f`0lmd9W9v;H>^+gr?Eb9YEC$b^-gr@>8resPN7i7IuCf`8Cv~q5QPQ? ztOu?r^dcL*P~D96Vj&VYULO#AxM6+mU436odE)7Rp1-Qx2ly3M;D@KL_5lF{+xq<~ z`wMQ7%82T#LAM$Hqbeh+uW}Y zx32XhG%)aw|805^)y@1X4SDlQaN}Qdv~;ia?*`IXxA@UVf44FbU3zMrW4#B3GS+v6 z46SOtJcP#}vBbJHs++N%jGFF!b*tdRt=6j#qOtBU`dE2}o|4xk-q-Lt5`{9>J#pTe z*E7+;66+LHH)DNAh{TQ8WdSjE`rUcmXt2Joo}5}KGJrSRUsdh{)P@!K;pwY8ASk!K zx}|6IUn(O_UyZNd@ne4_2qDCizOUBMd@o?b5So9L(|=@0sQEV^;$WvCwALZBjVP4) zcLC?E`Bw+?b+Zna(j5PP&*pEQidy5h{d^zT~@LYJYu z_SHz9j6xafWSqCgIvouRSo?hI64@vM*>D{6`v9#_)1B8N1Rrj^UihKDuYMYL#_*iL z>97JnJbkq|2tv9YwGS|NatZo(1G~XV3c;EaVkin_tXJZ^HP$E4z<_nT(u4hMWPxn# zYCQ@y-D~}A!G{~x=SI?4cb?gt<*4TMest;StGtfRqfo}WFnD8FSYzD)4GdT>-L@-+ zjsB=^=Jg685;tBS6MVQ~eLG&?R}%{j3x02tydU5)tiTUXUmXYn>%Iyr#ISvUFH}ZU zUj?JC?4$itDkG||f>FHwet;gMY5o-+ajO!npypq*QHojzW&Y8~7M2yqC7GS->F z8_&>cA0Pq^3|RB;1$0DpGuAVONZhdAF8FZ6y3SY{>oPg=R|WLcybk}Dn%99kibA1` zbw8Z9=Jk9uFksEU7w{dbo3YLWdGoXGhIM7Mbmw)8ar(Zx`ug92wi)=`O1TeE4_4rZ zr?2jVpj`T@@5`L^l6|mRh>@nR`q=#dkt!oiU-kLE{4t8h9QwL#Jk7rwWg|*53+4OO z1c-y3K)KcfnI)l6=HFGEx8`372#oqU8V?nF2&i^riH6uiM6v|k4zmT7>05&$Dx*6+qArd#NiveQn^t;#k zjEOYXBQs^t-?ti$E<S!hkWvqAMyfxN0(ZGPUuj|grY?Oj*I1c)KfcB{A&g*f4 z4>w*fpQP`r3!hp0&;6^)`vK;{3jFZ&)iNLmX_3@E0RPndb(Im-SHF5^M6rew*Cb6= z8Bu-pw|eh?4EFdfwtJ%l}*>D{6wcY?V-D~|l!G{~xSEkZfFKg6>eunr_bm_sG*HJnOWvokrH-?2Z z*3Hnsfc5(;3VqGS5L7qwI#Gzkjn}^jKHRW=_=&!+_WETF15ERrz&~IGet7!ohah0K zPWJ<>QyEcxHRv|Oe_mxo^;K@fV-9`oGo9vNmHw-P*BRAkDQ%_&spR1w_tzJNGXECf zyfy#!qk(~c{CmI0X#(KUGi5u2?1s`r$*Plsa{d2=_>CXr&{YS&=1}K!V9*pzW zyiPy^ORNu~x*6*nkT*Z;ZdlhqOLtzko2BooUtAaveBLN|KR^>$fghf}dIW-U>8tV4 zzb{UC999c4()HCmwb%QjR7SeK8gne0=6--3vuXa_9=|(+to79p2Rngstp_sOi9(rw zw{YH?f8h`w^J3uNpWX5%vC#t6&HS5ynk*7G{%sO`xYc@)P1$+r)sQy zj1Unhl(FuP^VV2TLjwcW@BT0?ij8fkZpQki5Q!Vs&j4cV^t;#kyty>ipVw#`d>*Lk z{lZvuDf8OL>S#U+Wvmb2yfxPM(ZGPUZ+(q*Y?Om+I1c*14Z5MGJFlk*KHPY{cAmbk zp8f9o;C>?OtIBzmuYyGSciCeAb0mL}R-S{_t0gd%u9ue=XIB5`o22&zpn(JGeYhIyb3Gu z!}EUiBn0KsSAG48k50)4tA!Y8`fA|k;v1-pbbXcm?m3S+^mX5IntxxcTN^M?@^2%= zq3WxF%=V*D=HCOHx8`3Z2#3B-l*x$>sf*iH(qaE zrSGemSNi{X?uv3BU?r@;4^LmM4uX&tN$msh72Z-AY5FR&i=VGDqWUWT-E;0S^tH@t znt%Jde_WMWDEn&eFV(dka1o9|nSX6@-kN`7(ZEpaJg>E=Zsy-5AriM*FARu1TCGoA zLu1`NHlrqq^-y#fQtMz5KMjR4*4uI38tdz5V8%M0`7I9Fa2)je0Bumyz1BwyKHO^k z-dY;#o(B&;Lt=d%U3#$Qb#xzvGS-#B8^gjH>rQB3!203C>O0sNkLqS#rwNg`@%pOZ z!wu`aiTb|!;hNVX19+4B0GVM0et7!oq;<}HHN&5(GSc_e4F64)5!F|NvFi2#K3q@p zZ$Z`n()a$v0Thf%9{zEE!%-;nZw<~{^Y0WI82HEkUdj&H*wuP0YP#2YKf#9^|1NHz zv2OL;=m>_M@)~vsT`IA@ghCnXV&KiG)*GXNCDsE`-CXOdg-G0L{U^bP8`iBi(pYC% z9Yx;{5cQRY*KJTJV?7S%t$Do>4J@%fjp}Bsi$dP~th-^|5G|PxH>`VY()ZQm`KyC{ zgxm+{04wmr(^t)$piKR5xQi05#p;FDwvzxbZq= zo4&8c-S{N9$Hn@pavxwltiTUXU#$azkQQm$2Y6Ryr0J{7b9{oz=+XOX#Uz@42i6=7 zq^s)-`b68tXVTFk>C4r>UrJ=Jg&S5;tDo5`4H}T_jE4SNF~D zUYw;7_#UhBIe|H01%7z?>I^uIj(s)5zguOb@2eU9`zj-SU)AjcjM+}}@AS3y!QW9- z@vkpH!OBX(zYPCa6w3VDg!9(?JC6oN{>8&*bMgMXkd0lfH%3kOS|2RUr zmh}!0OL+}Dfi9I;Uq_*g^)uj&VPTDRD>Sgg`a@JV*ZM{w61Q6aRq)}4b;q4F*7dKy zK|hnR!M7S-cS50z^%R`9#ySZNEU~_b>SnAA z0Nr5)et7!o6$r|uucl6(8<89atA!Zp`s%x-migPLjP!k#N3421`5n!_0zXct_W^c7 z9ICz=$m|pfW&Y&>Z~UxV{k;^01_u81`|rOK+31DpX8tV}B5~v20l|k`tvA|3V_p4~ zkBYGKqwK2@yVY3x7$F*?P{w)~&Rf^|3N$ca9e=NC6dOlS-Hdf^$eW*aH>_);rF*S! z*h^y_@ms+cNUZ0hOT{(J=i7)v8S4u|hE}{T1mQ7A3|O-$+2KcZGuA^<)BXLzGQo!% zufN-;@2hP;?vb5kLH}OBR#<@_p1xWi1R*Wbv=8us%1GB&!FBvX3)$03c{=M{j z4f^k;65kWG&hUvuq0GOYIB#9+Gtt13e<`SL=HDG561Q3}3y86jaIf`+2WYG#8ZM*v z{3f7FRjtP_Mxl)LVVt+dIunFvSL^YRetbn#H)9=#n(nneUGU*n>$!fQvECi~=97?t z`Wki9l8Q2b=TV?ovP#Nj_YKA{6gb?CM-&b|}0Fw^W{5xJ?N9F(?s(pY# z5C^M51^+VqlTj%1FB#{p`In9cM*ao&0g6C2yyxln{92)=d##TUe7N!N_7NKE?^i6Q zpLKf{T`IA@gF+eW3gC@lVU2ZrG_b^a462)JJxPeft=2CKKHRYG0Ur&pFQv0DQ09s4 z*Sl}G_j~v6SAWoZgX3N~o3C3{m{#}IX8+ajIu3<0*0XTl8ta{CV2SlLR5xQ?9`fdA z-3{xuXvuuIVLjxSzOVk#t3X5mZ*m{tZCHUHp1yhmf(q$&y7#MpWUG?=9IO^%r0J_& zCl_4q@1!!)^ws!_tr}^b4{+`{&A+D03xo&oQ1#XQ5Qnm_vdqq-Q08A@@W#)&)!$1E z(7?dIPilUU$VPuuH}h|W5Q!WAjtM^8_}AhDjrE6*=VD-~YrWQwYODi(MxG zUF(TxV8FW1XD>#vaT3+dSQmo4`B`_vx*l4(*ZQ`fXspjJez7)*bppDSuVH~YN&t(kvA*!mbA{R2QSPr!LzkXf=Y&{+LK*9ygbb}}JqLtmQR~!f}o}sb+aNWh=b3jz@7w)4=57xYn3Y`u@BxBtW=dF1?2n`Ip{=VhJ8f+{? zbu-o{gh<@3Vc7sNcKY3UUGA*Duh#vgc<{3Wavz`=tiTUXUtJ18S@+f83K{+%RYp`_ zuNY}jdeFP zu*7;2s+((lrx1x-t^XnTaKpOKc^d0*Um5!I*f#&u@VYMwWvmzAyfxPQ(ZCYx+o*2F zIs)=$Y;fmwXS8(ht0OPy`|5=EmIptlA@>2^gBAGU>8tl3sE}@_+XwKii%6~ttA!Y8 z`s$hFbBp|ODkE)Qo%^Kb{pyvAH2>aQw3_~m`Y6Pq?yHblItpd}l>~46tXsW@YK8_T z{>4vD^uZW{>Sq2W3X!~CtU1B7UgLK*9cIB#9+ zThPFO_3aYbqu4ly>SnAsvyGR=f^}@E9ZptbIRxUX_g&sBXr30&2SV)lGsAH(npVqVKD3w9Lp8z?=0|<@W;i z!V3KG^wriN2x*a2U)|EU$n3EEC!#)98Bu+;S(Q>%=T(^-m83GF`s&}k-umO;Gqy+Z zm_uLdr_=m<=|NulJL{ExSJXPstN{vT{td=?>sn7h0|WoEPS{ z23oq;`r4~B*3XvN%_>=4>vPbhr`9Y5){i~3nRjucT@GNTmr;iRj&qf_oH)A~r zHQj4{iQvPn){AG*SbsWkL{$>&tk)F0=5$g68vN9XXQQeI7 zSs@a)YgisYjGcaWURSxU@2ltIMi}-1o`Dtk;pwZZASmm;8eAd6e@10Q^;IwmV~;8; z1R;cYqWUWL6ns_vJMRyge^1S?S(Mo*uj^wW4t5$mwI1}4;h&E}nSTdx-kN{+(ZIkz zz7J3ivf)JSP zjdgD{Fkqbt_FLGPf$HX3-!DYsR_k{KA8uF=yh&qSuxf1Z`ARCRJHbgJc|8b)GSSq3J5h8J`^$UUzw_5LVhsHX+>=^nvfsOxCV;vwwHx$ZPPse%d zT2Dg*1J=n)#z(Pn1=Y=1heO`{th-_TDq6bN`o6m~);}hE7D@7YBf6BYVF7~eN1=@M z10h2zURQ$f7$gR)gRJ)X+M&7`>uIRz-dB?aA8x!peNW$4hb;Mk1+KXt;4rMf4^Ln1 z2m%JS)%^f`g-=vQn!d{H;&-Zy9=)$Ny-)M6_qjJ&KI*<2`M0{(nSrkv3T6I{zfA#r^d+E;&azE7YIvj;E)~(^sHU?cW z_~-wyX3ub9leyua4>I-W8t|o6n*LVTR#tq z%X0YQPi3$A4&`joY2%4e?@zn2c1zgZ-P;FcembVZze{f^`ckiNCydN;XxpvN4xD-H zaNWbXH^;vJM#6+rMLzlE-TON;<=g*ejpLsde|h{XIo`;&WcKjxGyj+Aus`$QL1W%p zdvoZ+m_MKHe7;`iu=|zo-R+St{oR#y{+;*b7Kh(`F~^dHUky*Zko|Da7VW;wa;U_C z#EF?>?jEa`Rz9r5__I&V3;Sfvq*W^)J5=P>Z%LUBH90)>+`r=AZ+!mGNnv+?9Mr!^ zx3EgxcC9P%Qm7}8jN~>``t;C!-f^?f3irv{nLBQteoxh zOuxR+GNlrn8wN}&1E*=+(6-&)85Gy>o&Me4@5wRS0HO~vW%|O`?jihF;JBSB+P~=A zsxePw{yL^tqr$P%kF|;YdHuxLe8-l@{*{yx8-L+o?9^jNV@qE+9NTg5-q`j9lVV3S zSQR^P*38%%6^F+DwlOyLPQGlhbIMJPIkV``=pj|Rr50P1o_b?MwY2FEd#9bwIwfuB z*Gtp#jNh1c^}_bFtS$DXrB^$UHus&~X+Qn&b=th`i_@N7Gd8VESo5^e=klfHe?Kwx z+L#KdufJL|g$4M{sFpvq!_3SzF%31{Ya&_jp{)sqrRO-EIam{AV77qwp6=%T-;8^u zPNTe)m;wD9hr_S}KfKpJ5U`?fxc+7MKT#R!uYVc-ohl>!^-uTxji!&$eWGyHne^{E zL}m$+SkaT=`S&Q4`@{&Gx4-_Of${nmyl>+;s+;+jAM$23=+3`rw0wl?Usf9H&SRId z9Es~6x>RDl1%)!!mxT=fuO|n-j{|PlCq4Xl?48=Z*u8i~Av^|&CD!#(-Hi2c)O5#s zrQkzb>pU->ldnv68tbY(6E)YrY#LsNqfo}WEzVo>dMp}PV!amC%~)R&B60g0t}r0R zPQN>^Yd)^;tA9109_%CJeH#^E1%7z?>IMkPv9J1kzp0FLeYN)GS^g3bLWn1QU-c#N zm_uKe=Aikv@zieme$2@b2RjXlTKD-9P$={77|vVsFDry+RqMgbs-U`=f4x!Dz1C+5 zKHO?OZ!Q|^1}pxc@6o!0E<>A+*SLY|W~?hg-u$e)Vch{OnGZLtKP*UNJ*4h6 zn%7C_(!*Rja0c)${k%q>0gK&u{NpWs7V6CwLGQ=imd{}#5m6?~Qq`@2H<+|ALj0zbUh zKM+`7|AH%I_+wN?)b$UH&afZv9HcU$u7BJUk2&=9ULm?qs*$4BmJrwAw@Hga(G5%0oF+u#)|0OaKD7J`*+b}6gvR>G{DtW~ zgjR*sSO*Bv8-+5~b8+7O`iBMvtRFrc>1X2xs++N{1bOqb?uK;-v~<7z9eIkz`q>&^ zYOa6i(sTXeygrIT8S8A|jb~`Z>qs;(V9j~m1=Y=1&k-VV!+MwC!;RM$i|YI8k)+)w|9+K`uCIRd;SnCRqNe+MsP%#m zw_2}QlE!-Vy;AhwaD_^!dF^9$R0)ML)}3+Qn%9%jz<~9c9i?lru^H9PSYH<+apQGK zK#ZM!cV7ET>HF%)nBM< z@ucgkJYMzdsxX>=quMtKe(y-NUo#WpV5h-T>pZj7D3tm43(i~fFE@l|QR|s}Uu|cj zCaRnHHvl!=Ykh&>!>!hfJVRsM>gTTXb2l@UR$$G~XHgW&SU16WYpjQ&fuYv9-=(N- z#`=^HiCeAb1jHV#*2k2gv3_a5X?BZ&`tPv5=+eV$p7U4~%2;p0d26iCqk$#Xc_AB) zgMJ^pF>1Q=da&Tbjn}iE)%Vr5U1|mYJ|Oo2Cc+B*@buM!APDJp)INA{g$)18DkFVg z&F~Lb8R`3~?!JvIWoiD6ZJ$8@{zf{)5z<$AW?937LYaRxaNe4KacE%VU+}(-si3V|@q>EU~_a>SnAXA#WDG zd#!gzOXkB3>yyvXSWiqo%5qfq)t%^4(N{D4r%)(kod>+}46Xh)h(ZHPtb3ul8SBMD zByPMuAoy^@`dWE?U#-*U&%mBo;Pb`Te3{rSbAczIvb*J4vorB4tiTUXU+n_|m%e)D zw(NVp-|@E z6r8uN^&~Vf@UPLc7bDoXi0Wqkm4>`|CAjggIa<2c`ghOMSZ}+3usUOwaxXg(U53;; zBg7sQ%2?kOGPJ7o@(><_#DMi*Lvlv3(HhmwSWiYx_rAJS@ZnbL)hp9jH;s+2Nn%~1 zlA6~(R!5O2l(FuK^VYnci3SF&yOtgr!A1(Io3XwlMB>KlvVa&n{qDSOR7Kxcqa(9b z4B*Y)uPXNeYQqZr@buLk5R`Ra-F@hGzS)KA$NZ%-qWbDvi^rxe|MPfqB?uwJ6V+EY zT>d<9-J)B`Jm%2X4OMCWeO>pDfPu2s=R+LqGapQRbu<5lqNaPTFB5#Y)q1IFG}bTte2BiMJ4b{9YfgyLD3q~&73Zz79)$*mT8~ah z*~G>wR5xRNUWml4)(Ze)k5=oGs?%6!yIv^pPC?1*LFm%MYhFi_Q7B`bjPur5r=x)Z z>v35(Zf2thWW#aL?*p_#O?O_85PZ1tdf^NDzFPXp#Ng*NB^1 z50J_}+Ha{cqWUVh4H%788Bu)|jKbJ68FF;lBL6>r^2MBQpg)-Ke!5h!eY9Am14GdVH$@zH{8y!*IjP*<*5;v^33qIVit`kjT zeX{W`y03;uQM`saibA1`bw8Z9=Jk9uFktQTg;izaJ5)DgoeA>hXWb3!%4q4%>lS`} zUyVEy8GLROxerheR^W%HukM4Oto!Pm0+%0bxLG092djk`QGNBBfuHrbS7(bqQe{N- z)zP`T#vOX|oaX)Nwiud!FAsUA6w6lm_fi7H!A_v3)_G=0D3tkk73Zz_R|3LgUJU$O zw7Ojy8;wxi%)e2n$s%#%-x|S(TdkL?M`K<7*FKLyC~BZ;kZ? zG%#R&^7Ni4Ha4KT8S8W*5;v@i0b=a*yVv@R`ZU&2EncJV2N;enJ-p_1G!um~*1K@t z8ta>AV8D7(+2VdSNofq{SgUiF2jZm#tcLL_dro(&M=9Czd2$VN2Q&n}ox zKOe3)y7bgKU>zXDC=|+Aug7_7tk0r>0qa|^-@-;N$cE#fuk{9~>0aya2|nDgzVb4S z^;>!Ng|kXf^7<&cl(7!fQ924`tV@D7hJ`iO&CtMr^&P;OjUlLR=5?YFi5stf5q!8| z{jjmVuYPiMj^TcQKVSuZc>3yxAYfqI?*oAHI+c;Gud?TEo>v)Z`YQOz_=d+E`r4-{ z&A(M^;_1(*+cXJMN#sF>%D>x zH>~S7r?LL__h3g=_SH(wNM2`r(*T7s)`M~0n%4&9`)cdg%Kr0xE9LzFO<)Cnc>3xQ2+FyyJ`Sse7-{+{^Bf2JArbo2b}Cgq0GNqIB(6ra0riiG4d~%TMJY-^KSxbvPj(c zw@L8fR_j$>rLn&EUdQKI4oa+xwxY3)k3gY}b$6V%#(EkWn6ZwBK;yTex*6-6LL_cj zKLd!d)9+sE_1n-`4{P&uBrK}NdMvtB@_IfBWvutL4#xTapW|Tj9EXJ0f@r0}IWRxR zft@LKoPHSLKRzWq=3kA!j2W}?iP)_rUXFct_o&!bx#q{7?6V~qF{^v=1II?I=*Uij+q zw3F9srIqa2Gi}-MacOs2&q_;*T#}af`HHmXN`IMFqQ>$xU#Yoik*CI`jla@4?S-oq z)BgR-_0+D>|4AJ^Jx^*>_o68*zSixU&pn8lnQI~k@beL`e`sl}iIC?wv}>!s{`L8D z`ak!>DWBue1XkdO_xcAxgqMPJuo9f3j_>+U#jjrBA%Fkb(HSZ_mhGuAhSNF1+!fY_s5|K@d|v0gXoL;61B zvFK89{e$2CL7|NG0i3tS`aT+1VqFfh;W+5`#=4=VJFlk*KHPY{wxhnU-in_cXq)u+ zHI6CdLXkBD3tlv9Ote1HyjNN{QJGoo7LG^j_T%GKPyDyR_l2HG0t%}{*CWU zW1atv^-r?1qx`#K0J@ZGJwS*FD3r0@iu2Z3UqS-|)(eK*t;0ax@ z1Rri#-{?YP-7n!FeV_3ubSY!)V|8>Bg)-Jn%bg zZoIxA_;ABIdpCVwopZfh@ID#xzKy$GiM~1p1lD~u&(*7`Cz_N`{aR&2_0_dCKIs0& zfgApGl@ZlfOLXWqaPc3%Yd&8b*q!EI%d4H?ZXBZ4I{}!CN*@04%m$%Q=HD`$x8~nb zG%)b*K-(s1Y&=ADGyiHq-nVpi2+doDgSGC}Uj! zyzvaJ_Q7M(z<~9+_ZIls=!5EJtP_Mt+^{|@_;ACzX&jC9uKz5cKg*4LQ^9LqN6kZ^C@=QwPH zI70d=&ul*mW&S zR}H(tz*7HhFd1EXu3?}YsG|fF%2*%6d26h*LU# z6>blyrun{cnZ7jtrVN{ton@>1+cft(>RR`)%)(J9^RF$=Tk~%$8W?KbclKN&8*5SB z%)d)QByP1{7!Z53TA$jF#`^X*olBEg4@H-9tp^A(4TUn++i~6+>+5J>!1`9$=qNUd zLpB@-{XRe&)O4@)(Si@RTEEwy#(MwN+)t5MpGTK6)`2>@k3t#i%HWM*VU2YsG%#T8 zi=Uau#&}dW^Eyq4#EsWi1s`r$=N+K$tMhB&Zy=ES0GVM0et7!oq<0C6JLtj69kLF*@{44B4s{3jjK*6Zw z;UCXzI0|L{t-*O~{+&Vt1OJ{}{6Rf7vO_j@wH}L_?zP@e@ZrY4ivww_2e-UNe=qJ3 zy7XYp32_O9GS1S$32hrO>RB$ZLI&{wh!NO9n#l1*P|ZK&Fbju3VjG)<2Y)}H>$ZF%is6f*E@Lw z54o<{|7@iBy3KLVxlYe?<5Bl5*VTDTb05Ctx>LXC>uRF$#WGp>c3cNASOol&>gqk# zDKpvL&Q~MNE4XwlnyryjU1hr1GDr@we|`Y$fq8yK8NKSK0`-|Mlyj5qL*>%=V2=IbQKJ?Anw_F$J zEqx#B8wNS&dcuq3xg2vno9|LOhcVh3N1OOYHP^S?6nf?B#H@Q}N<8HHJzE1XU!^$i zIoG{7>HBTA4_ZYvil(lzP9=+G9iYHaXa6-H@td>%QV*fWx}~fT->CYpC4b-Ru|AnM z@ZdjJt~L8_TNjlp7=J_v#^YrL&Ep5J}1IC*NZzOEowT8Tyq)0cd3qb%MkPU zMm5(b`1@Yx`Xg`PA=jDys9fHB=wf z!GBjWga(_hq)d%>W4$IPeIM)n-1gy{|6Yu8&UL}%0nWL;&UY#08V&K1Z&Y)go$bxc zh1a=mz#DkTb>nRP4w$dO9QQodce?e&x3PZLZ6Chny4e`#T&F#^*ZFfv)kYJ(#&Ohw zZ&Y(Vg}?80uJ`c<9&)|n*Q1f<>mkQI=Q5xXtqX9b(J-;Zb>$#8adTf)+jRi=K=m4=j^{m z39_2Uoj$K0Vg0bxRR#x}%5L(Fs{fL(z2Q;!>UyaHZ{Wdyc#pah$36RRnOjeM^WPb_ zefT!kYfNy?b(#i&&Yz1bHlEJ4Wr&)5qnhhM{C%&-`a0ggL$2{2^+k?*&UIp@8;`ng zxh~6F`aafoOmxn5n?L5mdY|$jg7uzq4-5#ObB4r9Ky#?el`QO)%OH-%pLIyLK_ znGz4V#_uT&;<)Er59FkI)P2kKYPWs(=Ih@lMPFABtT_AacgVtA6ir=?OmuvE&?c%;G$b5z~A>e*D+Z4ycp{pzx%O*`6|G1&$;fvN#Do%47YvwHr5kPbI$cp zeUI~ZX1wLQRC8^Oqr_7oMQW}q^7p;YbtrG($=7@xU=GJU=lZ-`Pki(BSGRrmmg}t3 zqpz#sK}F%7tm8UB3KjwXq`JC*9Y(L~YVGU~X2m>HIQSgZ$f>SA8Rvf`d83O#aab2@ zdvdC)Z+rhxs?C$wk=SSZzmsM-`)~Bsx6Yrf?92LLjzYv~L z_%C(OewEBuTBZhTUi52z4LIrhSdVbqhj0FSKhrtao%PVT=FuV7SO4O>lyZ%R_{cY^ zxz5k_X6C}{TcX}(@?+;gt;G2M97eam$d-qPHMZ@KROWAt@3 zIO`1f+_2+1Kvxz4|D?M5nsv%n?V_Epva991ESjy6Q(ZN8YhKc~pc*;VRo2G3R>VHr z|GhWI*?$oSyQeo>kYoKc>xZqbLVWi9_k5%3zcg%bc+|bRUaH0$c<>*SX?-rB2gg18 zZ=G9DeDmKGw|)5Lzk2hWb3HR6ojEA!T$h>akZaQr4fsYi*CYA+UXS%{yn%;Ye_;2g z&DWnC_nhn0OgA2N-*R1MCX8jKA;oSYN~&c<|r0j#KuTuirWD*?%#Z?*DD97v?Q}AM0xtJLme{ zpaV%udBrpmGQUgh~qjy1{MMTq`JC-b;|3y`hLmNAExB!5p<1e zz2Hw?{#(9>gen0lU&QqE=BXZ4&f{U{z-N9GwYPC+C?*8WkI5Xin3_7Mox7Vb{iGc zfokMbSFsWH+5Yd7HO~HPoXy{Peg87+hnX#DtmFQk@r|ngvar43QTJ*NRfjk5;6Gdk z=+AM_{@do(6W_-AEw_F6HrAW2bI$dFZizCQ*(0wFDy@ws*E-**=6WK3-|Mmd3vb|M zuJ3W&bFMQp-FVb}%XKZ@()Y1`XuWf;KQ-Ky(lOVY_%2CbAK@F-T)%Tu=#{T?vF@2E z@iN!-IPN*u6FBL6UESrj58r%!bwl)ZwOqAZ0am^p*8xtj2>2(})eyG9RwARRtJc-> zc&bq}bv3x>_?idyQH`Rjt8*@{iDtf9Ws|f2dS`F%d~I52BR$qFW!3mb)qnl?`(BUr zRlI=*|KU2oS&n=5pC8lxzm4@k-qQE6zGbs>uJ14FSkm$yb!|G2?@}G>mLazBjcTs% zx+(N(tS4vP^J1)ztfK8PU*$ONIoEwT>HAn;?zRu##(KuB&bj^?qn7jUmd4pa``TrW zqs)Axn(Mm!eXo5zlsE9;>)i`0?lWI&IPN*uf4TLuQ%AwN@+}-zw-1)yS!?elD}+*48iQf>N_C*!JX9SJ%WZ zf9mwnRngSd1>2qdmoo1Q=XIzttRLoKkj6SHTf{f2{yW3p_u7A7cmogqOI+>WvgRuf zQ-j`!ejT6i=3~%z~~A;ajeI?{v;}@kcAOn4_4?bqjWoY`(^E)R%8m zbG@3s?{%&(^9CMr9Tr%4pZWTaw|t}OzuatZc+|a`LpA0NJoxVmyFYEdhH>1p|9)}niEm^5 zf!jWO8|$rpanALGdHKv_(PO>lZiif($F>dMsOEYGf8Xn|evmitkn5c+&*tk1$35pd z7t@VL-M3sfnHa(=elR{_BkDMy_4@!I)^dZ8b_!3Mm5)8-4uG|>q4x1W=cHd z`e&AB^VO8&o^w5&lfKv0{cij4&DXbojlQlH%f6|om2by&fD0@F{z-MU8QWkc+pEv9 zS`p&c-9L?L6kT22e{X5f5vox%b=BqCl_{Ff1=QZ>?7u#VLY%*MC15W-)?F^9tPbC( z`Y(dN@AX*U#2a|O`I{n$;RS7SXr>z)^5{cYxW70g!^j(g7aP)_}}H-rwqCuPq$+oa=wwdg9wTEDg(;dH8+z zb*Y2V*VTtVR);k%v#yfg50IZlz(1+3?qHqrx~>j7e2CbK8e+zMgtC z`nq~JRUY^)470A1>i{EJ1pJfgYC^WbR_&r$2Vg;>f`X|=PIVP_8x_=-YUET`u@Uy! z{_p4Co&DEz#BTUpFLhqO&-!7jtGK@}e52~W!fbD5Bd`6}f;aHsKYSiw9LGJ6^@DCb z@olU>ciV?={_AwyIoDwmZ#v(jZg4D`TzBCc)m+c#?|Yr=S{Z-!I!$qKD|+M|6Hn3G<7xd z*B#4(PE(Ddud7S~U-yRU=JM_*S%2Jg2(})n8es%w&7DzPc~$U10Tw86u;oMox9Lc9Na>!zx7`$i=!~+mlmW zjlK2j#jlTog0au`e>Yrk_TShxFP-PBvspjP!yt`yRJMt4RQ-30zwfpG60`1kG1iOE zxDaW+N^#t?|9WxK_p!d%Z6Cgk^|Y6qb6q>`$>QeGA?s?4i$t!`5b60wHP^NH`(EdI zAaCGdtoKSd@__kT!Ew*IzUkHz-^O|Zma+eBtdF|voa?`P?{NMMMkwE<4&=e7^ud_Ct%^mVmdu2OI<=C}?p zkww5isjjAE8*J4snstEI<~qQ4R3oRl3cHO88cH>Cs;jJ#zqu!9O#N@HYtH^_8#mN> zzWR*y!#vcI|JZ-GHuo3jDr{8!7s%iD+J7B*0}uX-#l9cFd`;!J=dpg=ttY;X^>=Rj z@Xdcct~=*?#?=|l_fm9rkf>a086Scl|gzr-IwZEC`n|!01>m+P%sL-oxg9^NXhg`>C zo;F{dIPN*u%iMb6o3GEf?ZdZRzxXryy4s*=ehm^S!Ip4S1#&@ZXb;}UP z`9?L@pWGCBHP-X7?wKj^kn3&>OII*oAsqLd>&cw-y{_(Y+lOysy~G{oTo(x`Vhv;R z+9307+Sk@N3g8>nTzBN}d+qDlyn%;Ym!Dd8yZJi6anHH_=++b8e4U$R%sl+Q`?|(o z(bv@}vyQ;^f#dT4WmyFLlj`ar)+w*+YTuc;y2VdEaQ_FYkyBk=neD{weT7a|EX2BC z+mlmW-H>c%c=}rBqp7Pq?mGLgRp=q-`RXFp5A!fcV;z<4c(q?Z4EldtQw7 zD&vpsFke9&_w2ucob-LHuXfvqZ(}{{-_E(t^tQiqUG=+1n#d~M*k=Um@+>xpk;Jq63y|2Ebq-FMFQ_j~p`-?!?^cPaTA$I)cIQO)&! z{=V0_e#RSk$n~lE6L*=fv`h`=A^LTI2AuTW*AZ^}@Xgmt9zxZqbLj0(p#1CPk>c5KoeXso&${Tp_A6}cz;kf6qe%`GozK!*-Zu{`ffBpY)&h>la zx$}33w_yj#G1mk6Mm5*#`TJhy`Z{mmWv*i}HQtT&3Y_$Ptao+Whi|#Q_t-htqk1iH z{w{{oe3w#PMPJ|N8`WH=VS6)k;dQR7@djSzx(CNS`+A*QPki(B6}Nr(mTSlk|KC=- z6Nvv#Sfxi;Sci6fy7UO^UE~QI5c8WyV^j%=$^NNVPdm3hn^|mKn)yHGUlS5Ln6a&y z+HcrU602XLk@ce+BHBt4tG1=Tc>rBv?snU<3q$@DiS@d6*gT3Z4f*s>DW#)( zTrwTv(vS}(v2wA_TiWC_qCQcLaNfEbt|7mZ#7?eG1rm_IL1K9i zL|ErXp)}SrN@>?FN%L8KLq0!=ol5sTV8}Nhv1X-y0;R|gAhE|aj#$?K8uCj>?A^SZ zS*^a|PLGmUfOgla#v1a^NbJ`~C(2mSkWclTN=TAoeXMz$hI}~^+x)hl8O)_2-9jziA9XiQ&~!pZ$V-sVtxll0ZYwj66;f;sc`2vYusL|OvTWZLEB(eSNnpqDoi)j8Ysf4Vr(a2i6(2%b|V&k5UwZ584L<=La zOlNY#PLZEYV%xv22(;3R{9Y1U)nf%zOXTm7*wQ|Et)CmxkWcW6Qd(nJ2B;y(7bCIo zn!lN6^>3$-?@8>pfWP1ji2N`TOVsp(pA>C1iM?A{8WdP8x*opvFy$kM6c&?7&M#J0s-YCYqvA^$Upt*+P2 zdSp>d32u?tz(-H4Z_Y|7K}U~-&bydbgTC%(5nIxZ#{Y2H!^$#rTOxECiF6-X>{ z^Cs9SPDt95nE#(G;k1hrj7cOGU%Lad8axATCov=OGZ+gv!MILhr6$h>r8vQGy`z-& zUDX*zJx)mSkl6A3$x2xV3nv(LNocn41x10PB0FU z*q-#i!Kn==7>`M;NT-w5eN;A6g$YUW_f$fvW&RBF5S(C?Cb0zFjzFow2}Uat%h0rr z^~jf)V2mZPE3+ctnBoLu6NzQY{?WSmuHgjZ5{VTIt_hbFI3f8=Vp_#aFmu2OMs`+< z%~B>`@>GxU2kaCl7&S?(YOP%05S)?g5>b-KU= z11BW+Ni6riJaA=%6O2S3DWwyS+=2Rr6Os}n*1AD{$Qw>D3=#`l8q>O)tl@-Y1c{Z2 z*a^h}Cm3r1(V@ds0ZOT!6CV-gE({m%N~Tn#4}LrAPp>c+XOXgI-GL1OcM z&IZR6CnP6G?BIu7P%UwS@sh;m+*}V82`3oo{-qMKELU#u8%{`qNNmHYk}%oEGhjy& zOR({5F-s}(lSwSil^Z zCPzNyS1KW0Pvn6Tjb*SbiNy|!1tv$n4T(jS{Tup*WpF%+Rhz!1tmP2ow~(0Y#xOX` zU>UqZVn<_Mf!d4w7ZRK3zYwMbSZZ?qM*As<0vol2zulVh}1B!*XE8uF(|YWY1Mn6dj7wp)#fh2~fcn$fbB(^PcKRDN8 zwBJb#FH1G#pOe_a>tS#*!f2`EPzk}SR}J~{B(`JeWtcf&w00zh7r7ep6G=>q-yJGB zM%zYWcrC0Ue~rZQ)T(HG)=jwgKN7=BW)1n=ajAqH3Wg_RMYK93h7&Cf`9368D^(Vl zN}4f!)9b}(`)tcPHN@rH$EX; zj$yPaB!-JB8uDF9EN852# z<#|XU62nUv4f#eSR{TauSdhfN4JI*M@X?T8PGb7sgXHt+aT3D|A`ST{5;K0fDW7H1 zCZZC8%SRgW6-n$|iq7(ks{@JQDwBr%4hh;pM!B`~wop z`=N(i$rC4|l;X;vhI~m9E7Gc4I_b)&8HwQnqlWxQ67w$`YzF&wowbg{aH&B<{v3&w zxzrL?Fz}w{dlI`6I~yzzA)h5Vl@PI*fP6I)ySlSvIZFaY>rP@~F#-9RB-U@+c$k7> zv^^vy788*Fi^S&sIuwkA(c-6|l#0a!ALVgm9FNNj8ECNRyzXah)0 zEG8hogv9P&UIXiv80{#DiNyrupOM({mwn~FrAkdDL@XvCUyj7G9*Bh7Uf8#`BqkOU zke@(e8AC!~VusPSl9*UbK>jL;ZEV&JiYi9?N@8L$0r^~MsD%78FbgcgU^I=y#9{*S zy-DoH%%|o0HlM`AVgm99NvzV@P=BfRJ|Z!(n1FoJw3O0U#}ddRE0Dy*Vgm9lNUX`| zx9}h(=4~{IiNyruH;|aVa5GGgG1>(Z6N?GRe)3DQ$a#bN^T#Yn8&_{nfH z2>bRuiHXGoile{hD8nT+i#9{*Sr%CKwtc`Gyf_-~KVq!4?`HUH;galvR9!nZo zl}Su2CLrI1#O5CS1uiSFZ_`LjEG8iTGl}&m^+X;Ew@6GZCLkXtBc(KE-1)NK3X+&u zOh7(_#M;h^50^ZsbP$P&#RTM+k=TqSH(+rdqa7nLv6z7T3lb|gKLIRWVYD=vsDy~c z1mr7_Sd|eqVL=k3wI?yLn1K8w603Xr2;3sUXxm9lEG8g-oy1;r8UPPhU^G`|N~u^( zKt2zN{V{N9N@*fP7yP%T(`yJhB#&m{?3e{t$_^yV#(#)VIeZCKeNrPo9NJ zNXvRZz-?B{TWJy#iwVfLBC%_^i_4>aEQyK51mri7*y2%>;IT~X+a(eciwVeoCb2tt zpUHj8o|Q_7SWG~^CW&1f`y3v>!M^n*F|n9{{2UU?nWnNlN7_$fVle^v`y_U#bsBi~ z82gsUpHeCo6Ob=KVuvrKmq)!pVq!4?`4J>m_1YPE)UPEmv6z7TSrRMYmk%CWLZ$CW zOe`iKpE(e-!W;=7pRP(`Yi=KegN2Vz4`G;j*9bSX8+YN)(qp%SNi1{h15nDamz_xL@}TmNBJ9o-5+H3u{`ANBv% zv|2zIEYM<%5L0FQA>Y>}@sVkoLSa&HeJF`9UoZ?lTc_a0N`{*|GCho)si!qQ`46n# zq1+ohs_ZX;3AtuLGUYbx(Z|x7Fejz8)8ltAC%^!xwJeE;Xlvn4pMvXMNIb`aYVfqI zf*aEqZr9pzQbH)BsP%Uq)x58TDXFgsOlUQyT&C8Lc5S+M3G4KZ>`d#Z%usBwJxa}$ zi^@!@=OIux6iQ+K=DyyhA zhA~iXs@#;^M7_^JVOMay3W>+tToYWZ;6_)5+vP6K766?=xyyM}*~J1Aa#a_fCwnt+ zbO@~1VF1+X%0p>2;tc>7E4W^S#Iw|COX5ZohTF9sOIa6EgIXu^sIrR%CbTLp-b;4o zRM}~8mlxZk)F&iH`v-}q%v1&L3@fnBU64#>%+&n z;LbB@4Plshnw4EFFrig(@krjx^vSmp{@vG!aK8-OqttCA{(E3D$c%#PcSyX-llCy# zQg9<)0ZQs`ownz&@{Up~Gt54t5Ex$4QZDYro62$*PbdMa0vH43E+z5&$!lV+`LIm~yee@J0va;!?b+EH}2E8z$%& z1Lbxk@m`6l!8u>S_1PrerFtc}R#9+cFT?F}cU5=|zCjni;ZbE53rxsWU7Vu`q)FC# zwc;HpAs7I)HYD)_v)4kkRB(L+iQjpU2Cgs^+*r$SyVf*k`R8#^>unxYuEhcqS``;3 zDN0E#koy|c18k2{E0B2LhgdLCRB*jJi9blX15U9DZp>u3UFz6-xeG`xKEtERGYWy> z4PDB`U&szsYuv`%flI>}D7RoSDm#saTn3*hxZaG!3wL@2tH}y(jAXc7?yfwVHOR#T zY7dX9xEL`ZS9S3}WN)rtxx`(}1EAJ)#VM^DHy41@6n2Z_dJPf}3W!KxWm3V7FoxT; z<{6qjv4x|HSMjK_iv=dMDlWc6b|&_wM{sqA?NMs15|q@;m0G|AM8WmqB!2N`axlGu z8#=@7QYWUa0vCfQbrz2*yI5d&tAcXzA+kdg)5M3#AI3nruSk4Y%;s>_px}D8l2mq% z4QcOZ9V!JkzGJvu?&nTp;gpYZhx4eiiv=d+sxIC__J)1+2NYKffLd>n_@jLMBf1K% zCk~*r=Fjv9=9UU>lw`PF>)?`8;9?N9cH>cH7Yj^iRa`uu?97btkmnM?U&;)0a0)xNhwNhhT+=-r215yVWwQ!#R3y@RTuZ=?PRTg zCH!bccWD>^wXP)bg|~EgbW6eYizNPK(@Sfnt423IG2HIruFU_z_n z;?}&GEVW&V5#}2%4cnvCsU#lz_@x3AuJ0%DDL-5W(cLjC>Ph`O=Y=_GcNSE`j0VCZUl+fiS;ksdQxzG6N#rC)dW^(72LSQaJ$?_dz!(c zL?|~=S<1x%6LM7-m*eeZt#4YEi)(4c0I0Pqi4REj5e}__>+?ul?|%iZFcjQ~WVl`H zj=!GB*8uN%RN2J>6IvA)=Pm~+lBFIl;exptwnwRrNxa0xv5g*mC>N(LPsv@^WC*OZVGNX8mB#-p01wtExZaz@+jg9m z%kqta8}k`%mm7Hd1lSB+e1S)mYq7wDT-C)fDo|SMw<-$Pniv4J7A5ihbvD5OS8%-* ziT7{w7^b2MZj5EPU2DN@2?|=NL9P3ERN2J>6IvA)KP5Z!Y-B8Rmo5$4qtuKQsmxs2 z{1N6_3a-~8@$Vw$khsx{;dZHa({}fhq;BL z^&N?iU0n&L2nw#}3Z_zXqH_~CZz{NrT#_Y2MRueB@G4FlU1UmRxEQ79`I3cqZGsKQa2C1548-XhV!Vhiv@-cLr^YW zMs{e-Ujt#BU<{Odj>Nk@yaDGy1=s&0@lwbCf&MGFk-IV__w}kWrXRnpp|)h0eO@du zAy;+rWZq6b&7OPS6vh+=K&^X8d~?u0aQ&s=`V$hLS2G+YTMBNZs6uJ|u>Cz42M=vc zhM8Jr7Yj^iRa`uPH$esg6&c28WR8Yb$mFi3a(!v@ss0MLEjbJ_`-0zi)%mo zMINxFtAbS7#R9{}+b9=z;!S0_{f>VR&uL-|l=~xzxA>(3EXgRieuTu=#XU+!H=Z)w zF8A{5`B3Tc$mXm@xmaLAuIl0t-cHuqEYqe^)*!(EsC6WXFDkwr9*I$KeH)3dTkV&C zif&wExLxatNjCsTtx2m>E*6;3s<^l!ZzfBfbngWC4BMmBP!hj3w}3f#T$+OG3rW08 z-!U++RB+=E!|hVnPk#g_HI(|5N0nVHFnpMra&e&=kR@4e`sF{EZ?G?4Q+^+R{zySG@=Q58f*J6PQt%{3d*QBKGX?728!C-ro zT7tya&nXR;YYMKnCGlDre}c1=f*TVUZkM`jW+}Ni9p+JG7Yhs@MyFi-n(WY&sd-=m zg)vZW_F7bSQr9j8UkMs7}QNyYXk^&^SrDR;z7p-WS6J+DS(W?4vD^N84RqaMTUQa5Z^3LZnL z6M0nG#R9{RqEIgWh3rshsoUwT4q*(G`-sFB>knaADY%}t4kh>ROk3c{D!5UR;dZ&B z^g1!E&Y;|WJgV$sfeE>)i&v4osgiLFJRyhyQ0pZUuirOS0joC(uE(lNY2CP>C=^!( zH}W&wuC>MT?@d2`y9Q{(qslH8n9!=YcpBN69WIYvV!Z+N&H={ zjBp29!Hv}QD5-V&w17&FhqXS#%u4{-#R9_*K2k0o#+%ABiiPLbm>G6y7z5>QCh?kU z_krmZT)#=;>C5IPqZ_g7Q*v{5;rAI(Zh3~8a%C3_OvqJT+>N)Bwfb$-;Y5M~Q0sgW zpSQ3(+-FsA{WyuwO>+hAwkx<1#c;cet53-S101#HX#iSf7Yj^iRb1SJH`olR>!S$a=e8#DraL!b4<4=a$r9Llk55@>eP1BHavB2;HX_Skr@usre zlbvhiwOoubP;MU*Z*cq^T;?mdzJkUVgurLKZCG?raD6I?2M(~#;Vw}4YhC_&0VEy|?K2)#cCo;O zR>j4c8bOL=sS7@>j$s{EY>!fFllaOP>)|Y=;QAmEUoc@aTstbbv5et%sg+yDfYOap zZ}6ycEfyI5LKEfUgpDb=Q>L$mH5QD4a?6rA@QiU8H!lRRB&S&!|ign zrAP#o9_1e6QRP}JFdfePOBzaa zV+h0TS}P^Y0EtJfJ9t#t#R3yr6&K$nJCk8+Wsr*PQEKw0l+;3d_a(5TD!3j@;$d&r zrl4@66T|INFMQ|%ra`I8c~sfO0>h8uQ7%4DcBp#K!saM;X&3|Ly1u96Hji@wCh7{V z7a{QlCDOo>jDj0Y7;cwa=V`Fnmv3{$DLks|Vu1;{s*CrLy_u0`7(Awd0Z{8x67N!C zBy?WE^^7`|nhqBdf-M!?sLXJ?*0N3BKt01lJCH|}T`Vx6RdMlJvNK0tRDfEH?NRDg z63-b^GsC(x1=r&ml+?a;Qo(>#aH9~z?NU=t&z?g%tetsO*~J3G4_8wzov?4 z(Y}2E;^^T4(0L4iTDOyU#W&jvSe;jJ{Vx(<8QYpgyEFwi;y0(Xc1l($u5@UF7-njf zT`Vx6RdI0`ZzfC4c&{`}(y={CT}0y3r+)_1E4Y4&#M|Z03nNg$jn@pfyEw-6>$23s zEkLU5Vu9foA5kuD!JEo*`(;mRrp~2d43zr=iRU~zA_;}-zmj;bX?(FtiEi9wxLxk@ z?OWygl%XZ%Vu1;{s*7v#cCuE#%ZuR9VgS@SfW-S&tqLn`3a+mq@smBK!iZIH;|#;? zTCcATNoi#gM{Jx{l#2x>v??wx$(zYiU;34S(+;*rsqIKS!S`$V@-pug$MqQ`o?&l( zKdbLbbmJF>+oewUZw!=fl=_B8m0c__{2D6C#W`9-mSnjD*DsD?bqHgi+=e7RvdJ2l zKqN~ba zsrlMcnJHAO0+?RG_3uf%PVrD!I#Y0C7{l#S%gyW}yLbYvyCR&;ESQeTkx{3l!BjI7{#mJU>A?tF@ZHAn?FsxjOy^{1$w@R%qb z*5N#=>|%l8*NRdu-a>XL?a@W1T9<|~Q0^@fFVTMoT=6Klp130=H*r`^C}#?8lw`PF zZr9?mpmd|$?mVjOVu1;{s*4wpy{T4d030g}fLc$G`1~t1;Fhw2>mNz{-LrjgtQ6ev z??h>>l4&t4ZlTtu471OR1tzpAE*{I9$*0)BR>w@vrD1!Nx|769CP)TzEd|&ACh>k9 zUzi58MK=<5rlj8MZpgPOt24}$D!W)<_?2Uni~I7XvfOb=l9aPLgfUR=N)m7XYXmF+ zE4Y4<#19N;1lNuVZhT_6-Nk3Vtdq~O#k+u9*~J1Aa#a_%;q7FtZ8toILyG}W>ogLN z|NU5Vzb;L|^#dfH;IGC|pcLGAz;L_P1uL6D=|&f4?n=2>U_z_nVvRSGrDhEs3zZ(* zqtwA9J}-HHIIIe;uP5;f1unw6zJeR)8E%*Q*{`9z(w3+j{KmV-@KC*i>+?wb;l7ssmJt-(h-A23?yiJC!t@4@><1oIcCo;OT-C*S zx-POTs_bHc3Aw6^pOL+3@zXQ2GcFASpw>)1sMG|cC=E}cD!5*o#E0FkPDVGv z8E)6w?~Tjf>I@#*bv&x z0+1O6H(D^cOMRE*6;3sN9e z5!_={aQ$BrPtsL~%qX~#qcP{&>;oapOARbyL-&txikegQuLwZ29*6pz5`v0VWwQ!#R3y@RTmHB?PRU% z7sU0mdV>K_>sk`8=%2G7h3i*Id|c8@Fa(w8##e^hT^#@SjwP(znZ0ux#l z7q{olWT_*nZ->5Pdz3np#0w>!4J&O5uKz~j2NDd=W%XUbjei(!mzs7(q+FbG_M==Z zF#Nh;%EckPsVsMJpJ%Z8gfUR=ND^<~e;F+8E4aRm#FroV0j?Sp+_=VYyWF{>cfm(- z@yI6YPq|oNLayrKAl^>aT0LfNxRS*Hs5OklU#DJNz&ch6t}i0->o@1Yp;d6>FvIOy z4w{` zZfs$=UFx4x3&9w{!}^#-|Z* z!0e>3`lR5-Vusu0&h2m~v6UT^dxb}pYq7wDT-C*K22om9PL41^mxcjQYe^C>wm&nh zv?;jWj>KEet_v4b3T{kfxLxbE)PrCuhguKvsIrR%CbTLpjv_mgCgPoG5%%B{-wjI5 zI+)5#l|8#bs)FlvNj!&Na+nt@xY3v4cBz|Q{wo)!tvssiVu9h03{ft=O?K#Y?qYD= zjxkVfk|C7b&EGYJ+g}Qi@lI+dh z>7!xY5(A*tPb9uz

wiE=|Gpd_$?!tgg`*uJaY#sLyb_)=sxJ%ag@%JgV$sfeEdO zi+7QoDbiyTWD?t>)cYiU_|KwHEfri(6+ub8QT;H~G6grvG2AY7?So$Co8KPt>c^wX zE*2R61P|rnRb+?Wk2?lO7Gt2?OC;`>?HODkDYzbM7$tXQU=z4HRd6Fe!|iha`u!9P zDXdRzc~sfO0uypo7f&a9^PpF6=nV!yt&t@DXhU3>A}F~2g2XSr_!n+tD7cYkIHfi5 z%p@>hL9KNdW?loxE*6;3sJ^_?W%@$6Z6s9wR18w|H=9lW`bJXuURigK~QgjU7Hm3cE+>f)l8;5>ruQED#| zZ;*E+)G`Ivmy&p_O%K& zXEtAk`56X4ts05qeB5o3Fl`h~<#ty^ZM(4{H3UT^}HnZJ^?fs>ko z8zBt0OMTTT1yp*JI)z7-T`VyCsX)rb`^XN}UXucni7`;_QxYE>KOtO`D7c<+A}M!S zX?V6k!Hvocx66$^$qz2`QSKlfRd%t!gk06d>&V`$%g29169b^uYb5@ld)_qGp;d4_ z-Xu!v;H?i~Qm^1fVTRkamcNxzeq6Bwk1D%ZU_z_n;#p*8`ZTEr=X`9BQV)@Mj5%w~ z-MTad*I$u%E>{jHeF|=*|ACS^A$$!~4?L`m7-pXr3k-iUkaF=T-c&xzwqLpll8G@; z?sgI{F?yz%MVF@F`d=j8-{@}c&W0QDCsT5p*N>8)EeK|qDOYx}z=T}Y#XWdCS!>m= zNnxUk0Z{8=63?0An4i^o1=mlL_}qyRP@ojtc*AhJi|==ckX>A03TTyGEHI%}ak0Uh z$x_oTDh2nCusuqhK;m-(GQzT-g6q3UJY~^JaQ&j-#%+e%rB+{Y-hA`h8byYwl#2z1 zKfq17xF&BZ%N-N38EP@cK)C}*{8{c%FmqLKeGQ3UTeT1_VHDgr!*ILY{>uj>m*mEs zM!8sELayrK0Nzg4+T~t*xMhO@P-}Y2%4e_Wk%anc!-+?_X%!Fdp4pxlZiUTxhgIF~569!lb0K1P_? zb!iH2%wo7*Ztlq!&8qwD$e!X+wdS8mrKWjWe|Shu z!F8R)`~F{#zXrvk1D%ZU_z_n;s<1B5_g;o4;Em1l$v@LB{fy@ zG+;{w*Q=6v&cKZzRl$vJ47W?YxNa`9!dL)&}p1Q%lrlpA|C zC3kE6zoDEdxL$(9H+{|uMo@6WV7OiGu;4CoeVW0e$}Sd|kgK}*AlaJ{dM%i+U;xw_ zMdA%^v<9CkxSsV#Dm7Jm-Ogc+5d}A@Gu*DVNvksEo8Lz4P##rwvA~2@#l;)R&WzDl z!5Rg&N2!03c(PerV69uh^#pS$sT22KhSIIzMlpulrIzdV1}-(x#oc*S*~J3GA91E! zynyV``DGzc{xAm0Jwf8XemM*GgcMx=NaD3C4KHOKDg`(E=TdSj2E;8O9og?0W}g=e zOvqJTJdU@MPqVwrzcQn{Gz@@RcaeC>8|UFfsNnj25|8^bwP`S0bR*F`N^7=M+vGbu zRTyS!m0c__p;d8lIBzCPU7eQyLJGD=smnr52wLQe_tl41aW;a&a5pRF?a6{z51?7z5=_Bk@BuAH$47!Sw?q{%6gjAXmYS z2Mo8%4Y(5^-}T6{fO4_Ggk06db$B~j>v#RT!vZh{K&?YaJm1}JrmI|K21!SydB zKI-vYn6D_fk#i-bHPQHD^4Fm@W0-wjEHI%}aq&dnOg_c_lzvDK>o{S1l==&ahi91z zmum{HKO*r7FOtKILcxust0<|#wfn*~J|5Ou3^S$5E*4mlVtuSJMY(t&Zz{`eKH@j% z5XL~cYe{@@hon%>6kNYb;*oP#n7MRm3T}L5xZTAKt8Io88_Eq_4RU1{3rxsWUEG1U zleJE`R3FYe7yz}-BJqOD14vvyMB-`B?S=V@65V*raJ$y*=`zbtp=Mh{xmaLAtK#DN zyqPSuQG%>sOKgu)hmm-i27xenQgD4UiMRP- z))_FjM6C~bRCz`rFrig(ahmm%)QE%K;ocp#N2%3Fd}c&Cc;HsS^*$t?XL%?{Rd8bg z!|hVfta}bFK&e-FRJj%lEWGA#);W`^#c?)Ja$`@J4%Z|Y1Lc+^@pqRSm|0}s(ZGAg zalIXh7wgKu#!11Ai43>PEtaT^zZD(j{>G!qE*6-OtGf6l*_*gkcEe&f20*R;8>!Ts zNI#s!^?D>e{QV`EuPD)tehjy3eUfW=3`y%I9#wX+z=T%C#W%>#G|uJ^#=-U|HQ^>o z>Va;F-~l@Y*UOUlr-r*yT76e=qYcCDQp;7l1s=o0x{ybeT`aIfO+WaV;VBoNBs=a*4K%X=8#<6n@5#h zEHI%}aq)7pGoOwvg6m{#k5bQ*_=I`+;BrmDb=Ou(YR}^Bb6R~@a3c@H?NWd$+Q-btjQ>@pQ67eG-L0GBF0qjU@5b@7{wg6Mx(qXYExTA?LayrKp}d_uqX?`S22%tKfLb?_c#==$;p$Go^*>3xZnz7s zlNH>Ext-E_I8`1~)wjuF8HSl!Wfu!fXjNR?nKzTAHXX4WrlQy$rOqMou!%RJxGK1Q zl*9{8{s>DN3T`}OxZTC!_pc?9q~_fLQe_tlY{Ju?)?zp1;-f$QAovd|io0o8C zF#u`}C-K^GPZqb5ui*MJ692pR4w!%_xN(f(cCF=dl>+0S*8g}^*~J1AS``-;-U%s^ zrM8V5%Fmg6Hz>6QiO;w?1F8+~j^p|dB>woPKsZhcZtP&VU221-`QCuIdUmNVQgcgn0IU<5p} zfAFYsEf$!NtGd|lXG&|U&_i%&F#u{UP2!o}_6MyBu6H8w@AvG70;S-_6o%WiuD)~$ z&dR9u2#+eeSYSe{;^H@CXQJA4hv^NrN2xh>Q<-^hJcnz11=kyr_^4hBU>dF9hUq== z7x7C1@&DsAYSXPl?};p04ECNmdx=-2Z+Lizus$Is(~F_z1~Y-NO*F*JLZ$G~Hhubr zcNpUQe=4=>)1^mPGTz)QI`OM_>E5Ako6rs+<`{8qDS|?X!hPCwE6u`~HyO>N?x*!= z*QR@;E}j-R diff --git a/Assets/Models/racoon.gltf b/Assets/Models/racoon.gltf index 459a542d..609b04fb 100644 --- a/Assets/Models/racoon.gltf +++ b/Assets/Models/racoon.gltf @@ -1,6 +1,6 @@ { "asset" : { - "generator" : "Khronos glTF Blender I/O v3.3.27", + "generator" : "Khronos glTF Blender I/O v3.3.32", "version" : "2.0" }, "extensionsUsed" : [ @@ -12,152 +12,123 @@ { "name" : "Scene", "nodes" : [ - 55 + 32 ] } ], "nodes" : [ { - "name" : "L_Toe_end", + "name" : "L_Toe", "rotation" : [ - -1.304514398725587e-07, - -4.8278069232242024e-14, - -3.113858042524953e-07, - 1 + 0.3270236551761627, + -3.256429081943679e-08, + 1.1268872057712542e-08, + 0.9450162649154663 + ], + "scale" : [ + 1, + 1, + 1.0000001192092896 ], "translation" : [ - 2.9270432744255004e-09, - 0.02392714098095894, - 1.3476908478082805e-10 + -8.958276787041086e-09, + 0.03380584716796875, + 1.3245511354398332e-09 ] }, { "children" : [ 0 ], - "name" : "L_Toe", + "name" : "L_Feet", "rotation" : [ - 0.32702386379241943, - 1.1310142156162328e-07, - 1.641405731334089e-07, - 0.945016086101532 + 0.5162935256958008, + -0.020581424236297607, + -0.054522376507520676, + 0.8544266819953918 ], "scale" : [ 1, 0.9999999403953552, - 0.9999999403953552 + 1 ], "translation" : [ - -8.650776095464607e-09, - 0.03380582109093666, - -2.448857117087755e-09 + 7.901586940306515e-09, + 0.06353945285081863, + 1.1932570487260818e-09 ] }, { "children" : [ 1 ], - "name" : "L_Feet", + "name" : "L_Shin", "rotation" : [ - 0.516292929649353, - -0.020581310614943504, - -0.05452270060777664, - 0.854426920413971 + -0.05422692000865936, + 0.0003495164855848998, + -0.002708675805479288, + 0.9985249042510986 + ], + "scale" : [ + 0.9999998807907104, + 1, + 0.9999999403953552 ], "translation" : [ - 1.2865877252465907e-09, - 0.06353945285081863, - 2.6193447411060333e-10 + -4.906437034435385e-09, + 0.012935783714056015, + -9.89530235528946e-10 ] }, { "children" : [ 2 ], - "name" : "L_Shin", + "name" : "L_Knee", "rotation" : [ - -0.054226718842983246, - 0.00034972387948073447, - -0.0027083493769168854, - 0.9985249042510986 + -0.117364302277565, + -0.00023321543994825333, + -0.005352922715246677, + 0.9930744767189026 ], "scale" : [ - 0.9999998807907104, 0.9999999403953552, - 0.9999998807907104 + 1.0000001192092896, + 0.9999999403953552 ], "translation" : [ - -8.217813984856548e-09, - 0.012935775332152843, - -1.1059455573558807e-09 + 9.019347047001247e-09, + 0.08009882271289825, + 3.725290298461914e-09 ] }, { "children" : [ 3 ], - "name" : "L_Knee", - "rotation" : [ - -0.117364302277565, - -0.00023353073629550636, - -0.005353146698325872, - 0.9930744767189026 - ], - "scale" : [ - 1, - 1.0000001192092896, - 1 - ], - "translation" : [ - -7.161837345392996e-09, - 0.08009886741638184, - -3.725290298461914e-09 - ] - }, - { - "children" : [ - 4 - ], "name" : "L_Thigh", "rotation" : [ - 0.005340703763067722, - -0.08032803982496262, + 0.005341275129467249, + -0.08032795786857605, -0.9945576786994934, - 0.06613556295633316 + 0.06613563001155853 ], "scale" : [ - 1.0000009536743164, + 1.0000007152557373, 1.0000001192092896, - 1.0000014305114746 + 1.0000009536743164 ], "translation" : [ 0.06634333729743958, - 0.021777987480163574, + 0.021777957677841187, -0.000205356627702713 ] }, { - "name" : "Head_end", - "rotation" : [ - 0, - 3.552713678800501e-15, - 0, - 1 - ], - "translation" : [ - -8.470329472543003e-22, - 0.11583378911018372, - 0 - ] - }, - { - "children" : [ - 6 - ], "name" : "Head", "rotation" : [ 0, - 5.960462701182223e-08, + 5.960463766996327e-08, 0, 1 ], @@ -168,403 +139,250 @@ ], "translation" : [ 0, - 0.022377878427505493, + 0.022377848625183105, 0 ] }, + { + "children" : [ + 5 + ], + "name" : "Neck", + "translation" : [ + 0, + 0.10304808616638184, + 0 + ] + }, + { + "name" : "L_Hand", + "rotation" : [ + -0.10859402269124985, + -0.0013416738947853446, + -0.01228082925081253, + 0.9940094351768494 + ], + "scale" : [ + 0.9999999403953552, + 1, + 1 + ], + "translation" : [ + -4.470348358154297e-08, + 0.030574601143598557, + 4.925682528522657e-09 + ] + }, { "children" : [ 7 ], - "name" : "Neck", + "name" : "L_Forearm", + "rotation" : [ + 0.03182180970907211, + -0.0101229939609766, + -0.053867410868406296, + 0.9979895949363708 + ], + "scale" : [ + 1, + 0.9999999403953552, + 0.9999999403953552 + ], "translation" : [ - 0, - 0.10304805636405945, - 0 + 1.1274243760794889e-08, + 0.011892775073647499, + 1.3969838619232178e-09 ] }, { - "name" : "L_Hand_end", + "children" : [ + 8 + ], + "name" : "L_Elbow", "rotation" : [ - 1.3239958462918366e-08, - -2.4324227076988336e-09, - 1.4901161193847656e-08, - 1 + 0.1340317726135254, + 0.0004457758041098714, + 0.022963767871260643, + 0.9907108545303345 + ], + "scale" : [ + 0.9999999403953552, + 0.9999998211860657, + 0.9999998211860657 ], "translation" : [ - 2.2351740014414645e-08, - 0.016836093738675117, - -5.329070518200751e-15 + -2.1535759842095104e-08, + 0.07338029146194458, + -2.7939677238464355e-09 ] }, { "children" : [ 9 ], - "name" : "L_Hand", + "name" : "L_Shoulder", "rotation" : [ - -0.10859407484531403, - -0.0013414795976132154, - -0.012280543334782124, - 0.9940094351768494 - ], - "scale" : [ - 1, - 1, - 0.9999999403953552 + -0.05528344586491585, + 0.015805501490831375, + -0.274429589509964, + 0.9598866701126099 ], "translation" : [ - -5.215407838932151e-08, - 0.030574528500437737, - 4.579678858362968e-09 + 1.30385160446167e-08, + 0.03457435593008995, + 2.220446049250313e-16 ] }, { "children" : [ 10 ], - "name" : "L_Forearm", + "name" : "L_Clavicle", "rotation" : [ - 0.03182216361165047, - -0.010124370455741882, - -0.05386859551072121, - 0.9979895353317261 + -2.5817980642273142e-08, + -2.2547874678480184e-09, + -0.6586140394210815, + 0.7524809241294861 ], "scale" : [ - 1, 0.9999999403953552, - 0.9999999403953552 + 0.9999998807907104, + 1 ], "translation" : [ - -1.4001724224499412e-08, - 0.011892830953001976, - -4.656612873077393e-10 + 0.03500552102923393, + 0.07119834423065186, + -6.64637900271714e-10 ] }, { - "children" : [ - 11 - ], - "name" : "L_Elbow", + "name" : "R_Hand", "rotation" : [ - 0.13403145968914032, - 0.0004466302052605897, - 0.0229647234082222, - 0.9907108545303345 + -0.10859402269124985, + 0.0013416738947853446, + 0.01228082925081253, + 0.9940094351768494 + ], + "scale" : [ + 0.9999999403953552, + 1, + 1 ], "translation" : [ - 9.490547014934236e-09, - 0.07338026165962219, - 1.862645149230957e-09 + 4.470348358154297e-08, + 0.030574601143598557, + 4.925682528522657e-09 ] }, { "children" : [ 12 ], - "name" : "L_Shoulder", + "name" : "R_Forearm", "rotation" : [ - -0.05528340861201286, - 0.01580565795302391, - -0.27442947030067444, - 0.9598866701126099 + 0.03182180970907211, + 0.0101229939609766, + 0.053867410868406296, + 0.9979895949363708 + ], + "scale" : [ + 1, + 0.9999999403953552, + 0.9999999403953552 ], "translation" : [ - 1.1175854908174188e-08, - 0.034574370831251144, - -3.3306690738754696e-15 + -1.1274243760794889e-08, + 0.011892775073647499, + 1.3969838619232178e-09 ] }, { "children" : [ 13 ], - "name" : "L_Clavicle", + "name" : "R_Elbow", "rotation" : [ - -4.527326780134899e-08, - -2.4482876170850432e-08, - -0.6586140990257263, - 0.7524808645248413 + 0.1340317726135254, + -0.0004457758041098714, + -0.022963767871260643, + 0.9907108545303345 ], "scale" : [ - 0.9999998807907104, - 0.9999998807907104, - 1 + 0.9999999403953552, + 0.9999998211860657, + 0.9999998211860657 ], "translation" : [ - 0.03500552102923393, - 0.07119831442832947, - -6.646381223163189e-10 + 2.1535759842095104e-08, + 0.07338029146194458, + -2.7939677238464355e-09 ] }, { - "name" : "R_Hand_end", + "children" : [ + 14 + ], + "name" : "R_Shoulder", "rotation" : [ - 1.3239958462918366e-08, - 2.4324227076988336e-09, - -1.4901161193847656e-08, - 1 + -0.05528344586491585, + -0.015805501490831375, + 0.274429589509964, + 0.9598866701126099 ], "translation" : [ - -2.2351740014414645e-08, - 0.016836093738675117, - -5.329070518200751e-15 + -1.30385160446167e-08, + 0.03457435593008995, + 2.220446049250313e-16 ] }, { "children" : [ 15 ], - "name" : "R_Hand", + "name" : "R_Clavicle", "rotation" : [ - -0.10859407484531403, - 0.0013414795976132154, - 0.012280543334782124, - 0.9940094351768494 + -2.5817980642273142e-08, + 2.2547874678480184e-09, + 0.6586140394210815, + 0.7524809241294861 ], "scale" : [ - 1, - 1, - 0.9999999403953552 + 0.9999999403953552, + 0.9999998807907104, + 1 ], "translation" : [ - 5.215407838932151e-08, - 0.030574528500437737, - 4.579678858362968e-09 + -0.03500552102923393, + 0.07119834423065186, + -6.64637900271714e-10 ] }, { "children" : [ + 6, + 11, 16 ], - "name" : "R_Forearm", - "rotation" : [ - 0.03182216361165047, - 0.010124370455741882, - 0.05386859551072121, - 0.9979895353317261 - ], - "scale" : [ - 1, - 0.9999999403953552, - 0.9999999403953552 - ], + "name" : "Upper_Spine", "translation" : [ - 1.4001724224499412e-08, - 0.011892830953001976, - -4.656612873077393e-10 + 0, + 0.06622837483882904, + 0 ] }, { "children" : [ 17 ], - "name" : "R_Elbow", - "rotation" : [ - 0.13403145968914032, - -0.0004466302052605897, - -0.0229647234082222, - 0.9907108545303345 - ], - "translation" : [ - -9.490547014934236e-09, - 0.07338026165962219, - 1.862645149230957e-09 - ] - }, - { - "children" : [ - 18 - ], - "name" : "R_Shoulder", - "rotation" : [ - -0.05528340861201286, - -0.01580565795302391, - 0.27442947030067444, - 0.9598866701126099 - ], - "translation" : [ - -1.1175854908174188e-08, - 0.034574370831251144, - -3.3306690738754696e-15 - ] - }, - { - "children" : [ - 19 - ], - "name" : "R_Clavicle", - "rotation" : [ - -4.527326780134899e-08, - 2.4482876170850432e-08, - 0.6586140990257263, - 0.7524808645248413 - ], - "scale" : [ - 0.9999998807907104, - 0.9999998807907104, - 1 - ], - "translation" : [ - -0.03500552102923393, - 0.07119831442832947, - -6.646381223163189e-10 - ] - }, - { - "name" : "L_IK_Arm_Pole_end", - "rotation" : [ - -8.14913803104389e-10, - -2.8273916541365907e-08, - 3.597233089180918e-08, - 1 - ], - "translation" : [ - 1.3742706528319104e-08, - 0.04507105425000191, - 1.6264998237147665e-08 - ] - }, - { - "children" : [ - 21 - ], - "name" : "L_IK_Arm_Pole", - "rotation" : [ - -0.3575689494609833, - -0.6109033823013306, - 0.6082502007484436, - 0.3591284155845642 - ], - "scale" : [ - 0.9999999403953552, - 0.9999998807907104, - 0.9999999403953552 - ], - "translation" : [ - 0.0021197572350502014, - -0.04126967862248421, - -0.053202081471681595 - ] - }, - { - "children" : [ - 22 - ], - "name" : "L_IK_Arm_Target", - "rotation" : [ - -0.0011026781285181642, - 0.0018760154489427805, - -0.8620717525482178, - 0.5067814588546753 - ], - "scale" : [ - 0.9999999403953552, - 1, - 1 - ], - "translation" : [ - 0.17300567030906677, - 0.02745041251182556, - 3.304734264020226e-10 - ] - }, - { - "name" : "R_IK_Arm_Pole_end", - "rotation" : [ - -8.14913803104389e-10, - 2.8273916541365907e-08, - -3.597233089180918e-08, - 1 - ], - "translation" : [ - -1.3742706528319104e-08, - 0.04507105425000191, - 1.6264998237147665e-08 - ] - }, - { - "children" : [ - 24 - ], - "name" : "R_IK_Arm_Pole", - "rotation" : [ - -0.3575689494609833, - 0.6109033823013306, - -0.6082502007484436, - 0.3591284155845642 - ], - "scale" : [ - 0.9999999403953552, - 0.9999998807907104, - 0.9999999403953552 - ], - "translation" : [ - -0.0021197572350502014, - -0.04126967862248421, - -0.053202081471681595 - ] - }, - { - "children" : [ - 25 - ], - "name" : "R_IK_Arm_Target", - "rotation" : [ - -0.0011026781285181642, - -0.0018760154489427805, - 0.8620717525482178, - 0.5067814588546753 - ], - "scale" : [ - 0.9999999403953552, - 1, - 1 - ], - "translation" : [ - -0.17300567030906677, - 0.02745041251182556, - 3.304734264020226e-10 - ] - }, - { - "children" : [ - 8, - 14, - 20, - 23, - 26 - ], - "name" : "Upper_Spine", - "translation" : [ - 0, - 0.06622835993766785, - 0 - ] - }, - { - "children" : [ - 27 - ], "name" : "Lower_Spine", "translation" : [ 0, - 0.06622838973999023, + 0.06622837483882904, 0 ] }, { - "name" : "Tail_end", - "translation" : [ - 0, - 0.07595176249742508, - -1.3838050705317073e-09 - ] - }, - { - "children" : [ - 29 - ], "name" : "Tail", "rotation" : [ -0.7071068286895752, @@ -574,215 +392,159 @@ ], "translation" : [ -5.8597615213960615e-18, - 0.03983837366104126, - -0.09847982972860336 + 0.039838358759880066, + -0.09847983717918396 ] }, { - "name" : "L_Hip_end", - "translation" : [ - 0, - 0.032987553626298904, - -1.5967565047958487e-09 - ] - }, - { - "children" : [ - 31 - ], "name" : "L_Hip", "translation" : [ 0.06953180581331253, - 0.04957667365670204, + 0.04957665503025055, 0.061330340802669525 ] }, { - "name" : "L_Butt_end", - "translation" : [ - 0, - 0.03298754245042801, - 1.3750955929481279e-09 - ] - }, - { - "children" : [ - 33 - ], "name" : "L_Butt", "translation" : [ 0.06953180581331253, - -0.0007792188553139567, + -0.0007792264223098755, -0.04653617739677429 ] }, { - "name" : "R_Toe_end", + "name" : "R_Toe", "rotation" : [ - -1.304514398725587e-07, - 4.8278069232242024e-14, - 3.113858042524953e-07, - 1 + 0.3270236551761627, + 3.256429081943679e-08, + -1.1268872057712542e-08, + 0.9450162649154663 + ], + "scale" : [ + 1, + 1, + 1.0000001192092896 ], "translation" : [ - -2.9270432744255004e-09, - 0.02392714098095894, - 1.3476908478082805e-10 + 8.958276787041086e-09, + 0.03380584716796875, + 1.3245511354398332e-09 ] }, { "children" : [ - 35 + 22 ], - "name" : "R_Toe", + "name" : "R_Feet", "rotation" : [ - 0.32702386379241943, - -1.1310142156162328e-07, - -1.641405731334089e-07, - 0.945016086101532 + 0.5162935256958008, + 0.020581424236297607, + 0.054522376507520676, + 0.8544266819953918 ], "scale" : [ 1, 0.9999999403953552, - 0.9999999403953552 + 1 ], "translation" : [ - 8.650776095464607e-09, - 0.03380582109093666, - -2.448857117087755e-09 - ] - }, - { - "children" : [ - 36 - ], - "name" : "R_Feet", - "rotation" : [ - 0.516292929649353, - 0.020581310614943504, - 0.05452270060777664, - 0.854426920413971 - ], - "translation" : [ - -1.2865877252465907e-09, + -7.901586940306515e-09, 0.06353945285081863, - 2.6193447411060333e-10 + 1.1932570487260818e-09 ] }, { "children" : [ - 37 + 23 ], "name" : "R_Shin", "rotation" : [ - -0.054226718842983246, - -0.00034972387948073447, - 0.0027083493769168854, + -0.05422692000865936, + -0.0003495164855848998, + 0.002708675805479288, 0.9985249042510986 ], "scale" : [ 0.9999998807907104, - 0.9999999403953552, - 0.9999998807907104 + 1, + 0.9999999403953552 ], "translation" : [ - 8.217813984856548e-09, - 0.012935775332152843, - -1.1059455573558807e-09 + 4.906437034435385e-09, + 0.012935783714056015, + -9.89530235528946e-10 ] }, { "children" : [ - 38 + 24 ], "name" : "R_Knee", "rotation" : [ -0.117364302277565, - 0.00023353073629550636, - 0.005353146698325872, + 0.00023321543994825333, + 0.005352922715246677, 0.9930744767189026 ], "scale" : [ - 1, + 0.9999999403953552, 1.0000001192092896, - 1 + 0.9999999403953552 ], "translation" : [ - 7.161837345392996e-09, - 0.08009886741638184, - -3.725290298461914e-09 + -9.019347047001247e-09, + 0.08009882271289825, + 3.725290298461914e-09 ] }, { "children" : [ - 39 + 25 ], "name" : "R_Thigh", "rotation" : [ - 0.005340703763067722, - 0.08032803982496262, + 0.005341275129467249, + 0.08032795786857605, 0.9945576786994934, - 0.06613556295633316 + 0.06613563001155853 ], "scale" : [ - 1.0000009536743164, + 1.0000007152557373, 1.0000001192092896, - 1.0000014305114746 + 1.0000009536743164 ], "translation" : [ -0.06634333729743958, - 0.021777987480163574, + 0.021777957677841187, -0.000205356627702713 ] }, { - "name" : "R_Hip_end", - "translation" : [ - 0, - 0.032987553626298904, - -1.5967565047958487e-09 - ] - }, - { - "children" : [ - 41 - ], "name" : "R_Hip", "translation" : [ -0.06953180581331253, - 0.04957667365670204, + 0.04957665503025055, 0.061330340802669525 ] }, { - "name" : "R_Butt_end", - "translation" : [ - 0, - 0.03298754245042801, - 1.3750955929481279e-09 - ] - }, - { - "children" : [ - 43 - ], "name" : "R_Butt", "translation" : [ -0.06953180581331253, - -0.0007792188553139567, + -0.0007792264223098755, -0.04653617739677429 ] }, { "children" : [ - 5, - 28, - 30, - 32, - 34, - 40, - 42, - 44 + 4, + 18, + 19, + 20, + 21, + 26, + 27, + 28 ], "name" : "Pelvis", "translation" : [ @@ -791,98 +553,6 @@ 0 ] }, - { - "name" : "L_IK_Leg_Pole_end", - "translation" : [ - 0, - 0.04320859909057617, - 2.2203057170600005e-09 - ] - }, - { - "children" : [ - 46 - ], - "name" : "L_IK_Leg_Pole", - "rotation" : [ - 0, - 0, - -1, - 0 - ], - "translation" : [ - -0.008841380476951599, - -0.08020301908254623, - 0.0748630166053772 - ] - }, - { - "children" : [ - 47 - ], - "name" : "L_IK_Leg_Target", - "rotation" : [ - -0.7071068286895752, - 0, - 0, - 0.7071068286895752 - ], - "translation" : [ - 0.08565311133861542, - 0.027707800269126892, - 0.00015427125617861748 - ] - }, - { - "name" : "R_IK_Leg_Pole_end", - "translation" : [ - 0, - 0.04320859909057617, - 2.2203057170600005e-09 - ] - }, - { - "children" : [ - 49 - ], - "name" : "R_IK_Leg_Pole", - "rotation" : [ - 0, - 0, - -1, - 0 - ], - "translation" : [ - 0.008841380476951599, - -0.08020301908254623, - 0.0748630166053772 - ] - }, - { - "children" : [ - 50 - ], - "name" : "R_IK_Leg_Target", - "rotation" : [ - -0.7071068286895752, - 0, - 0, - 0.7071068286895752 - ], - "translation" : [ - -0.08565311133861542, - 0.027707800269126892, - 0.00015427125617861748 - ] - }, - { - "children" : [ - 45, - 48, - 51 - ], - "name" : "Root" - }, { "mesh" : 0, "name" : "Bag", @@ -895,11 +565,11 @@ }, { "children" : [ - 53, - 54, - 52 + 30, + 31, + 29 ], - "name" : "Armature" + "name" : "RaccoonMaster" } ], "animations" : [ @@ -908,420 +578,420 @@ { "sampler" : 0, "target" : { - "node" : 52, + "node" : 29, "path" : "translation" } }, { "sampler" : 1, "target" : { - "node" : 52, + "node" : 29, "path" : "rotation" } }, { "sampler" : 2, "target" : { - "node" : 52, + "node" : 29, "path" : "scale" } }, { "sampler" : 3, "target" : { - "node" : 45, + "node" : 4, "path" : "translation" } }, { "sampler" : 4, "target" : { - "node" : 45, + "node" : 4, "path" : "rotation" } }, { "sampler" : 5, "target" : { - "node" : 45, + "node" : 4, "path" : "scale" } }, { "sampler" : 6, "target" : { - "node" : 5, + "node" : 3, "path" : "translation" } }, { "sampler" : 7, "target" : { - "node" : 5, + "node" : 3, "path" : "rotation" } }, { "sampler" : 8, "target" : { - "node" : 5, + "node" : 3, "path" : "scale" } }, { "sampler" : 9, "target" : { - "node" : 4, + "node" : 2, "path" : "translation" } }, { "sampler" : 10, "target" : { - "node" : 4, + "node" : 2, "path" : "rotation" } }, { "sampler" : 11, "target" : { - "node" : 4, + "node" : 2, "path" : "scale" } }, { "sampler" : 12, "target" : { - "node" : 3, + "node" : 1, "path" : "translation" } }, { "sampler" : 13, "target" : { - "node" : 3, + "node" : 1, "path" : "rotation" } }, { "sampler" : 14, "target" : { - "node" : 3, + "node" : 1, "path" : "scale" } }, { "sampler" : 15, "target" : { - "node" : 2, + "node" : 0, "path" : "translation" } }, { "sampler" : 16, "target" : { - "node" : 2, + "node" : 0, "path" : "rotation" } }, { "sampler" : 17, "target" : { - "node" : 2, + "node" : 0, "path" : "scale" } }, { "sampler" : 18, "target" : { - "node" : 1, + "node" : 18, "path" : "translation" } }, { "sampler" : 19, "target" : { - "node" : 1, + "node" : 18, "path" : "rotation" } }, { "sampler" : 20, "target" : { - "node" : 1, + "node" : 18, "path" : "scale" } }, { "sampler" : 21, "target" : { - "node" : 0, + "node" : 17, "path" : "translation" } }, { "sampler" : 22, "target" : { - "node" : 0, + "node" : 17, "path" : "rotation" } }, { "sampler" : 23, "target" : { - "node" : 0, + "node" : 17, "path" : "scale" } }, { "sampler" : 24, "target" : { - "node" : 28, + "node" : 6, "path" : "translation" } }, { "sampler" : 25, "target" : { - "node" : 28, + "node" : 6, "path" : "rotation" } }, { "sampler" : 26, "target" : { - "node" : 28, + "node" : 6, "path" : "scale" } }, { "sampler" : 27, "target" : { - "node" : 27, + "node" : 5, "path" : "translation" } }, { "sampler" : 28, "target" : { - "node" : 27, + "node" : 5, "path" : "rotation" } }, { "sampler" : 29, "target" : { - "node" : 27, + "node" : 5, "path" : "scale" } }, { "sampler" : 30, "target" : { - "node" : 8, + "node" : 11, "path" : "translation" } }, { "sampler" : 31, "target" : { - "node" : 8, + "node" : 11, "path" : "rotation" } }, { "sampler" : 32, "target" : { - "node" : 8, + "node" : 11, "path" : "scale" } }, { "sampler" : 33, "target" : { - "node" : 7, + "node" : 10, "path" : "translation" } }, { "sampler" : 34, "target" : { - "node" : 7, + "node" : 10, "path" : "rotation" } }, { "sampler" : 35, "target" : { - "node" : 7, + "node" : 10, "path" : "scale" } }, { "sampler" : 36, "target" : { - "node" : 6, + "node" : 9, "path" : "translation" } }, { "sampler" : 37, "target" : { - "node" : 6, + "node" : 9, "path" : "rotation" } }, { "sampler" : 38, "target" : { - "node" : 6, + "node" : 9, "path" : "scale" } }, { "sampler" : 39, "target" : { - "node" : 14, + "node" : 8, "path" : "translation" } }, { "sampler" : 40, "target" : { - "node" : 14, + "node" : 8, "path" : "rotation" } }, { "sampler" : 41, "target" : { - "node" : 14, + "node" : 8, "path" : "scale" } }, { "sampler" : 42, "target" : { - "node" : 13, + "node" : 7, "path" : "translation" } }, { "sampler" : 43, "target" : { - "node" : 13, + "node" : 7, "path" : "rotation" } }, { "sampler" : 44, "target" : { - "node" : 13, + "node" : 7, "path" : "scale" } }, { "sampler" : 45, "target" : { - "node" : 12, + "node" : 16, "path" : "translation" } }, { "sampler" : 46, "target" : { - "node" : 12, + "node" : 16, "path" : "rotation" } }, { "sampler" : 47, "target" : { - "node" : 12, + "node" : 16, "path" : "scale" } }, { "sampler" : 48, "target" : { - "node" : 11, + "node" : 15, "path" : "translation" } }, { "sampler" : 49, "target" : { - "node" : 11, + "node" : 15, "path" : "rotation" } }, { "sampler" : 50, "target" : { - "node" : 11, + "node" : 15, "path" : "scale" } }, { "sampler" : 51, "target" : { - "node" : 10, + "node" : 14, "path" : "translation" } }, { "sampler" : 52, "target" : { - "node" : 10, + "node" : 14, "path" : "rotation" } }, { "sampler" : 53, "target" : { - "node" : 10, + "node" : 14, "path" : "scale" } }, { "sampler" : 54, "target" : { - "node" : 9, + "node" : 13, "path" : "translation" } }, { "sampler" : 55, "target" : { - "node" : 9, + "node" : 13, "path" : "rotation" } }, { "sampler" : 56, "target" : { - "node" : 9, + "node" : 13, "path" : "scale" } }, { "sampler" : 57, "target" : { - "node" : 20, + "node" : 12, "path" : "translation" } }, { "sampler" : 58, "target" : { - "node" : 20, + "node" : 12, "path" : "rotation" } }, { "sampler" : 59, "target" : { - "node" : 20, + "node" : 12, "path" : "scale" } }, @@ -1349,1508 +1019,644 @@ { "sampler" : 63, "target" : { - "node" : 18, + "node" : 20, "path" : "translation" } }, { "sampler" : 64, "target" : { - "node" : 18, + "node" : 20, "path" : "rotation" } }, { "sampler" : 65, "target" : { - "node" : 18, + "node" : 20, "path" : "scale" } }, { "sampler" : 66, "target" : { - "node" : 17, + "node" : 21, "path" : "translation" } }, { "sampler" : 67, "target" : { - "node" : 17, + "node" : 21, "path" : "rotation" } }, { "sampler" : 68, "target" : { - "node" : 17, + "node" : 21, "path" : "scale" } }, { "sampler" : 69, "target" : { - "node" : 16, + "node" : 26, "path" : "translation" } }, { "sampler" : 70, "target" : { - "node" : 16, + "node" : 26, "path" : "rotation" } }, { "sampler" : 71, "target" : { - "node" : 16, + "node" : 26, "path" : "scale" } }, { "sampler" : 72, "target" : { - "node" : 15, + "node" : 25, "path" : "translation" } }, { "sampler" : 73, "target" : { - "node" : 15, + "node" : 25, "path" : "rotation" } }, { "sampler" : 74, "target" : { - "node" : 15, + "node" : 25, "path" : "scale" } }, { "sampler" : 75, "target" : { - "node" : 23, + "node" : 24, "path" : "translation" } }, { "sampler" : 76, "target" : { - "node" : 23, + "node" : 24, "path" : "rotation" } }, { "sampler" : 77, "target" : { - "node" : 23, + "node" : 24, "path" : "scale" } }, { "sampler" : 78, "target" : { - "node" : 22, + "node" : 23, "path" : "translation" } }, { "sampler" : 79, "target" : { - "node" : 22, + "node" : 23, "path" : "rotation" } }, { "sampler" : 80, "target" : { - "node" : 22, + "node" : 23, "path" : "scale" } }, { "sampler" : 81, "target" : { - "node" : 21, + "node" : 22, "path" : "translation" } }, { "sampler" : 82, "target" : { - "node" : 21, + "node" : 22, "path" : "rotation" } }, { "sampler" : 83, "target" : { - "node" : 21, + "node" : 22, "path" : "scale" } }, { "sampler" : 84, "target" : { - "node" : 26, + "node" : 27, "path" : "translation" } }, { "sampler" : 85, "target" : { - "node" : 26, + "node" : 27, "path" : "rotation" } }, { "sampler" : 86, "target" : { - "node" : 26, + "node" : 27, "path" : "scale" } }, { "sampler" : 87, "target" : { - "node" : 25, + "node" : 28, "path" : "translation" } }, { "sampler" : 88, "target" : { - "node" : 25, + "node" : 28, "path" : "rotation" } }, { "sampler" : 89, "target" : { - "node" : 25, - "path" : "scale" - } - }, - { - "sampler" : 90, - "target" : { - "node" : 24, - "path" : "translation" - } - }, - { - "sampler" : 91, - "target" : { - "node" : 24, - "path" : "rotation" - } - }, - { - "sampler" : 92, - "target" : { - "node" : 24, - "path" : "scale" - } - }, - { - "sampler" : 93, - "target" : { - "node" : 30, - "path" : "translation" - } - }, - { - "sampler" : 94, - "target" : { - "node" : 30, - "path" : "rotation" - } - }, - { - "sampler" : 95, - "target" : { - "node" : 30, - "path" : "scale" - } - }, - { - "sampler" : 96, - "target" : { - "node" : 29, - "path" : "translation" - } - }, - { - "sampler" : 97, - "target" : { - "node" : 29, - "path" : "rotation" - } - }, - { - "sampler" : 98, - "target" : { - "node" : 29, - "path" : "scale" - } - }, - { - "sampler" : 99, - "target" : { - "node" : 32, - "path" : "translation" - } - }, - { - "sampler" : 100, - "target" : { - "node" : 32, - "path" : "rotation" - } - }, - { - "sampler" : 101, - "target" : { - "node" : 32, - "path" : "scale" - } - }, - { - "sampler" : 102, - "target" : { - "node" : 31, - "path" : "translation" - } - }, - { - "sampler" : 103, - "target" : { - "node" : 31, - "path" : "rotation" - } - }, - { - "sampler" : 104, - "target" : { - "node" : 31, - "path" : "scale" - } - }, - { - "sampler" : 105, - "target" : { - "node" : 34, - "path" : "translation" - } - }, - { - "sampler" : 106, - "target" : { - "node" : 34, - "path" : "rotation" - } - }, - { - "sampler" : 107, - "target" : { - "node" : 34, - "path" : "scale" - } - }, - { - "sampler" : 108, - "target" : { - "node" : 33, - "path" : "translation" - } - }, - { - "sampler" : 109, - "target" : { - "node" : 33, - "path" : "rotation" - } - }, - { - "sampler" : 110, - "target" : { - "node" : 33, - "path" : "scale" - } - }, - { - "sampler" : 111, - "target" : { - "node" : 40, - "path" : "translation" - } - }, - { - "sampler" : 112, - "target" : { - "node" : 40, - "path" : "rotation" - } - }, - { - "sampler" : 113, - "target" : { - "node" : 40, - "path" : "scale" - } - }, - { - "sampler" : 114, - "target" : { - "node" : 39, - "path" : "translation" - } - }, - { - "sampler" : 115, - "target" : { - "node" : 39, - "path" : "rotation" - } - }, - { - "sampler" : 116, - "target" : { - "node" : 39, - "path" : "scale" - } - }, - { - "sampler" : 117, - "target" : { - "node" : 38, - "path" : "translation" - } - }, - { - "sampler" : 118, - "target" : { - "node" : 38, - "path" : "rotation" - } - }, - { - "sampler" : 119, - "target" : { - "node" : 38, - "path" : "scale" - } - }, - { - "sampler" : 120, - "target" : { - "node" : 37, - "path" : "translation" - } - }, - { - "sampler" : 121, - "target" : { - "node" : 37, - "path" : "rotation" - } - }, - { - "sampler" : 122, - "target" : { - "node" : 37, - "path" : "scale" - } - }, - { - "sampler" : 123, - "target" : { - "node" : 36, - "path" : "translation" - } - }, - { - "sampler" : 124, - "target" : { - "node" : 36, - "path" : "rotation" - } - }, - { - "sampler" : 125, - "target" : { - "node" : 36, - "path" : "scale" - } - }, - { - "sampler" : 126, - "target" : { - "node" : 35, - "path" : "translation" - } - }, - { - "sampler" : 127, - "target" : { - "node" : 35, - "path" : "rotation" - } - }, - { - "sampler" : 128, - "target" : { - "node" : 35, - "path" : "scale" - } - }, - { - "sampler" : 129, - "target" : { - "node" : 42, - "path" : "translation" - } - }, - { - "sampler" : 130, - "target" : { - "node" : 42, - "path" : "rotation" - } - }, - { - "sampler" : 131, - "target" : { - "node" : 42, - "path" : "scale" - } - }, - { - "sampler" : 132, - "target" : { - "node" : 41, - "path" : "translation" - } - }, - { - "sampler" : 133, - "target" : { - "node" : 41, - "path" : "rotation" - } - }, - { - "sampler" : 134, - "target" : { - "node" : 41, - "path" : "scale" - } - }, - { - "sampler" : 135, - "target" : { - "node" : 44, - "path" : "translation" - } - }, - { - "sampler" : 136, - "target" : { - "node" : 44, - "path" : "rotation" - } - }, - { - "sampler" : 137, - "target" : { - "node" : 44, - "path" : "scale" - } - }, - { - "sampler" : 138, - "target" : { - "node" : 43, - "path" : "translation" - } - }, - { - "sampler" : 139, - "target" : { - "node" : 43, - "path" : "rotation" - } - }, - { - "sampler" : 140, - "target" : { - "node" : 43, - "path" : "scale" - } - }, - { - "sampler" : 141, - "target" : { - "node" : 48, - "path" : "translation" - } - }, - { - "sampler" : 142, - "target" : { - "node" : 48, - "path" : "rotation" - } - }, - { - "sampler" : 143, - "target" : { - "node" : 48, - "path" : "scale" - } - }, - { - "sampler" : 144, - "target" : { - "node" : 47, - "path" : "translation" - } - }, - { - "sampler" : 145, - "target" : { - "node" : 47, - "path" : "rotation" - } - }, - { - "sampler" : 146, - "target" : { - "node" : 47, - "path" : "scale" - } - }, - { - "sampler" : 147, - "target" : { - "node" : 46, - "path" : "translation" - } - }, - { - "sampler" : 148, - "target" : { - "node" : 46, - "path" : "rotation" - } - }, - { - "sampler" : 149, - "target" : { - "node" : 46, - "path" : "scale" - } - }, - { - "sampler" : 150, - "target" : { - "node" : 51, - "path" : "translation" - } - }, - { - "sampler" : 151, - "target" : { - "node" : 51, - "path" : "rotation" - } - }, - { - "sampler" : 152, - "target" : { - "node" : 51, - "path" : "scale" - } - }, - { - "sampler" : 153, - "target" : { - "node" : 50, - "path" : "translation" - } - }, - { - "sampler" : 154, - "target" : { - "node" : 50, - "path" : "rotation" - } - }, - { - "sampler" : 155, - "target" : { - "node" : 50, - "path" : "scale" - } - }, - { - "sampler" : 156, - "target" : { - "node" : 49, - "path" : "translation" - } - }, - { - "sampler" : 157, - "target" : { - "node" : 49, - "path" : "rotation" - } - }, - { - "sampler" : 158, - "target" : { - "node" : 49, - "path" : "scale" - } - }, - { - "sampler" : 159, - "target" : { - "node" : 55, - "path" : "translation" - } - }, - { - "sampler" : 160, - "target" : { - "node" : 55, - "path" : "rotation" - } - }, - { - "sampler" : 161, - "target" : { - "node" : 55, + "node" : 28, "path" : "scale" } } ], - "name" : "Armature|Armature|ArmatureAction", + "name" : "ArmatureAction", "samplers" : [ { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 15 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 16 - }, - { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 17 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 18 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 19 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 20 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 21 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 22 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 23 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 24 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 25 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 26 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 27 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 28 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 29 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 30 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 31 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 32 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 33 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 34 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 35 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 36 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 37 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 38 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 39 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 40 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 41 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 42 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 43 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 44 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 45 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 46 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 47 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 48 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 49 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 50 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 51 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 52 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 53 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 54 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 55 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 56 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 57 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 58 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 59 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 60 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 61 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 62 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 63 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 64 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 65 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 66 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 67 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 68 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 69 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 70 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 71 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 72 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 73 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 74 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 75 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 76 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 77 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 78 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 79 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 80 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 81 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 82 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 83 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 84 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 85 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 86 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 87 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 88 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 89 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 90 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 91 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 92 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 93 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 94 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 95 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 96 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 97 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 98 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 99 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 100 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 101 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 102 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 103 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 104 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 105 }, { - "input" : 14, + "input" : 16, "interpolation" : "LINEAR", "output" : 106 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 107 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 108 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 109 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 110 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 111 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 112 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 113 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 114 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 115 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 116 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 117 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 118 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 119 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 120 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 121 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 122 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 123 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 124 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 125 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 126 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 127 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 128 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 129 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 130 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 131 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 132 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 133 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 134 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 135 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 136 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 137 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 138 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 139 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 140 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 141 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 142 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 143 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 144 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 145 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 146 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 147 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 148 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 149 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 150 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 151 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 152 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 153 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 154 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 155 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 156 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 157 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 158 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 159 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 160 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 161 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 162 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 163 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 164 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 165 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 166 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 167 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 168 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 169 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 170 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 171 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 172 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 173 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 174 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 175 - }, - { - "input" : 14, - "interpolation" : "LINEAR", - "output" : 176 } ] } @@ -2871,7 +1677,6 @@ } }, { - "alphaMode" : "BLEND", "doubleSided" : true, "extensions" : { "KHR_materials_specular" : { @@ -2905,11 +1710,12 @@ "attributes" : { "POSITION" : 0, "NORMAL" : 1, - "TEXCOORD_0" : 2, - "JOINTS_0" : 3, - "WEIGHTS_0" : 4 + "TANGENT" : 2, + "TEXCOORD_0" : 3, + "JOINTS_0" : 4, + "WEIGHTS_0" : 5 }, - "indices" : 5, + "indices" : 6, "material" : 0 } ] @@ -2919,14 +1725,15 @@ "primitives" : [ { "attributes" : { - "POSITION" : 7, - "NORMAL" : 8, - "TEXCOORD_0" : 9, - "COLOR_0" : 10, - "JOINTS_0" : 11, - "WEIGHTS_0" : 12 + "POSITION" : 8, + "NORMAL" : 9, + "TANGENT" : 10, + "TEXCOORD_0" : 11, + "COLOR_0" : 12, + "JOINTS_0" : 13, + "WEIGHTS_0" : 14 }, - "indices" : 13, + "indices" : 15, "material" : 1 } ] @@ -2934,2060 +1741,1269 @@ ], "skins" : [ { - "inverseBindMatrices" : 6, + "inverseBindMatrices" : 7, "joints" : [ - 52, - 45, - 5, + 29, 4, 3, 2, 1, 0, - 28, - 27, - 8, - 7, + 18, + 17, 6, - 14, - 13, - 12, + 5, 11, 10, 9, - 20, - 19, - 18, - 17, + 8, + 7, 16, 15, - 23, - 22, + 14, + 13, + 12, + 19, + 20, 21, 26, 25, 24, - 30, - 29, - 32, - 31, - 34, - 33, - 40, - 39, - 38, - 37, - 36, - 35, - 42, - 41, - 44, - 43, - 48, - 47, - 46, - 51, - 50, - 49 + 23, + 22, + 27, + 28 ], - "name" : "Armature" + "name" : "RaccoonMaster" } ], "accessors" : [ { "bufferView" : 0, "componentType" : 5126, - "count" : 506, + "count" : 512, "max" : [ - 0.1090814545750618, + 0.1090814620256424, 0.40452075004577637, - 0.0857388824224472 + 0.08573893457651138 ], "min" : [ -0.09462108463048935, - 0.2630254030227661, - -0.11617939174175262 + 0.2630254328250885, + -0.11617933958768845 ], "type" : "VEC3" }, { "bufferView" : 1, "componentType" : 5126, - "count" : 506, + "count" : 512, "type" : "VEC3" }, { "bufferView" : 2, "componentType" : 5126, - "count" : 506, - "type" : "VEC2" - }, - { - "bufferView" : 3, - "componentType" : 5121, - "count" : 506, + "count" : 512, "type" : "VEC4" }, { - "bufferView" : 4, + "bufferView" : 3, "componentType" : 5126, - "count" : 506, + "count" : 512, + "type" : "VEC2" + }, + { + "bufferView" : 4, + "componentType" : 5121, + "count" : 512, "type" : "VEC4" }, { "bufferView" : 5, + "componentType" : 5126, + "count" : 512, + "type" : "VEC4" + }, + { + "bufferView" : 6, "componentType" : 5123, "count" : 2346, "type" : "SCALAR" }, - { - "bufferView" : 6, - "componentType" : 5126, - "count" : 53, - "type" : "MAT4" - }, { "bufferView" : 7, "componentType" : 5126, - "count" : 3484, - "max" : [ - 0.2035536766052246, - 0.5987313389778137, - 0.09013944119215012 - ], - "min" : [ - -0.19493983685970306, - -0.0017474208725616336, - -0.19020147621631622 - ], - "type" : "VEC3" + "count" : 30, + "type" : "MAT4" }, { "bufferView" : 8, "componentType" : 5126, "count" : 3484, + "max" : [ + 0.2035536766052246, + 0.5987313389778137, + 0.0901394784450531 + ], + "min" : [ + -0.19493985176086426, + -0.0017474147025495768, + -0.19020144641399384 + ], "type" : "VEC3" }, { "bufferView" : 9, "componentType" : 5126, "count" : 3484, - "type" : "VEC2" + "type" : "VEC3" }, { "bufferView" : 10, + "componentType" : 5126, + "count" : 3484, + "type" : "VEC4" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 3484, + "type" : "VEC2" + }, + { + "bufferView" : 12, "componentType" : 5123, "count" : 3484, "normalized" : true, "type" : "VEC4" }, { - "bufferView" : 11, + "bufferView" : 13, "componentType" : 5121, "count" : 3484, "type" : "VEC4" }, { - "bufferView" : 12, + "bufferView" : 14, "componentType" : 5126, "count" : 3484, "type" : "VEC4" }, { - "bufferView" : 13, + "bufferView" : 15, "componentType" : 5123, "count" : 17472, "type" : "SCALAR" }, { - "bufferView" : 14, + "bufferView" : 16, "componentType" : 5126, - "count" : 51, + "count" : 221, "max" : [ - 2.125 + 7.333333333333333 ], "min" : [ - 0.041666666666666664 + 0 ], "type" : "SCALAR" }, - { - "bufferView" : 15, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 16, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, { "bufferView" : 17, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 18, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 19, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 20, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 21, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 22, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 23, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 24, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 25, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 26, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 27, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 28, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 29, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 30, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 31, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 32, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 33, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 34, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 35, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 36, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 37, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 38, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 39, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 40, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 41, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 42, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 43, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 44, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 45, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 46, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 47, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 48, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 49, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 50, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 51, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 52, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 53, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 54, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 55, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 56, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 57, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 58, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 59, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 60, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 61, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 62, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 63, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 64, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 65, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 66, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 67, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 68, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 69, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 70, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 71, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 72, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 73, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 74, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 75, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 76, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 77, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 78, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 79, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 80, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 81, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 82, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 83, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 84, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 85, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 86, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 87, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 88, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 89, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 90, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 91, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 92, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 93, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 94, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 95, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 96, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 97, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 98, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 99, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 100, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 101, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 102, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 103, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" + "count" : 221, + "type" : "VEC3" }, { "bufferView" : 104, "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" }, { "bufferView" : 105, "componentType" : 5126, - "count" : 51, - "type" : "VEC3" + "count" : 221, + "type" : "VEC4" }, { "bufferView" : 106, "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 107, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 108, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 109, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 110, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 111, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 112, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 113, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 114, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 115, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 116, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 117, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 118, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 119, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 120, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 121, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 122, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 123, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 124, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 125, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 126, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 127, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 128, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 129, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 130, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 131, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 132, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 133, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 134, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 135, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 136, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 137, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 138, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 139, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 140, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 141, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 142, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 143, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 144, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 145, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 146, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 147, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 148, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 149, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 150, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 151, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 152, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 153, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 154, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 155, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 156, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 157, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 158, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 159, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 160, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 161, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 162, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 163, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 164, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 165, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 166, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 167, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 168, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 169, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 170, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 171, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 172, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 173, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 174, - "componentType" : 5126, - "count" : 51, - "type" : "VEC3" - }, - { - "bufferView" : 175, - "componentType" : 5126, - "count" : 51, - "type" : "VEC4" - }, - { - "bufferView" : 176, - "componentType" : 5126, - "count" : 51, + "count" : 221, "type" : "VEC3" } ], "bufferViews" : [ { "buffer" : 0, - "byteLength" : 6072, + "byteLength" : 6144, "byteOffset" : 0, "target" : 34962 }, { "buffer" : 0, - "byteLength" : 6072, - "byteOffset" : 6072, + "byteLength" : 6144, + "byteOffset" : 6144, "target" : 34962 }, { "buffer" : 0, - "byteLength" : 4048, - "byteOffset" : 12144, + "byteLength" : 8192, + "byteOffset" : 12288, "target" : 34962 }, { "buffer" : 0, - "byteLength" : 2024, - "byteOffset" : 16192, + "byteLength" : 4096, + "byteOffset" : 20480, "target" : 34962 }, { "buffer" : 0, - "byteLength" : 8096, - "byteOffset" : 18216, + "byteLength" : 2048, + "byteOffset" : 24576, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 8192, + "byteOffset" : 26624, "target" : 34962 }, { "buffer" : 0, "byteLength" : 4692, - "byteOffset" : 26312, + "byteOffset" : 34816, "target" : 34963 }, { "buffer" : 0, - "byteLength" : 3392, - "byteOffset" : 31004 + "byteLength" : 1920, + "byteOffset" : 39508 }, { "buffer" : 0, "byteLength" : 41808, - "byteOffset" : 34396, + "byteOffset" : 41428, "target" : 34962 }, { "buffer" : 0, "byteLength" : 41808, - "byteOffset" : 76204, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 27872, - "byteOffset" : 118012, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 27872, - "byteOffset" : 145884, - "target" : 34962 - }, - { - "buffer" : 0, - "byteLength" : 13936, - "byteOffset" : 173756, + "byteOffset" : 83236, "target" : 34962 }, { "buffer" : 0, "byteLength" : 55744, - "byteOffset" : 187692, + "byteOffset" : 125044, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 27872, + "byteOffset" : 180788, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 27872, + "byteOffset" : 208660, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 13936, + "byteOffset" : 236532, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 55744, + "byteOffset" : 250468, "target" : 34962 }, { "buffer" : 0, "byteLength" : 34944, - "byteOffset" : 243436, + "byteOffset" : 306212, "target" : 34963 }, { "buffer" : 0, - "byteLength" : 204, - "byteOffset" : 278380 + "byteLength" : 884, + "byteOffset" : 341156 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 278584 + "byteLength" : 2652, + "byteOffset" : 342040 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 279196 + "byteLength" : 3536, + "byteOffset" : 344692 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 280012 + "byteLength" : 2652, + "byteOffset" : 348228 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 280624 + "byteLength" : 2652, + "byteOffset" : 350880 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 281236 + "byteLength" : 3536, + "byteOffset" : 353532 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 282052 + "byteLength" : 2652, + "byteOffset" : 357068 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 282664 + "byteLength" : 2652, + "byteOffset" : 359720 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 283276 + "byteLength" : 3536, + "byteOffset" : 362372 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 284092 + "byteLength" : 2652, + "byteOffset" : 365908 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 284704 + "byteLength" : 2652, + "byteOffset" : 368560 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 285316 + "byteLength" : 3536, + "byteOffset" : 371212 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 286132 + "byteLength" : 2652, + "byteOffset" : 374748 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 286744 + "byteLength" : 2652, + "byteOffset" : 377400 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 287356 + "byteLength" : 3536, + "byteOffset" : 380052 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 288172 + "byteLength" : 2652, + "byteOffset" : 383588 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 288784 + "byteLength" : 2652, + "byteOffset" : 386240 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 289396 + "byteLength" : 3536, + "byteOffset" : 388892 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 290212 + "byteLength" : 2652, + "byteOffset" : 392428 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 290824 + "byteLength" : 2652, + "byteOffset" : 395080 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 291436 + "byteLength" : 3536, + "byteOffset" : 397732 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 292252 + "byteLength" : 2652, + "byteOffset" : 401268 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 292864 + "byteLength" : 2652, + "byteOffset" : 403920 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 293476 + "byteLength" : 3536, + "byteOffset" : 406572 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 294292 + "byteLength" : 2652, + "byteOffset" : 410108 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 294904 + "byteLength" : 2652, + "byteOffset" : 412760 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 295516 + "byteLength" : 3536, + "byteOffset" : 415412 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 296332 + "byteLength" : 2652, + "byteOffset" : 418948 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 296944 + "byteLength" : 2652, + "byteOffset" : 421600 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 297556 + "byteLength" : 3536, + "byteOffset" : 424252 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 298372 + "byteLength" : 2652, + "byteOffset" : 427788 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 298984 + "byteLength" : 2652, + "byteOffset" : 430440 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 299596 + "byteLength" : 3536, + "byteOffset" : 433092 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 300412 + "byteLength" : 2652, + "byteOffset" : 436628 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 301024 + "byteLength" : 2652, + "byteOffset" : 439280 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 301636 + "byteLength" : 3536, + "byteOffset" : 441932 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 302452 + "byteLength" : 2652, + "byteOffset" : 445468 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 303064 + "byteLength" : 2652, + "byteOffset" : 448120 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 303676 + "byteLength" : 3536, + "byteOffset" : 450772 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 304492 + "byteLength" : 2652, + "byteOffset" : 454308 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 305104 + "byteLength" : 2652, + "byteOffset" : 456960 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 305716 + "byteLength" : 3536, + "byteOffset" : 459612 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 306532 + "byteLength" : 2652, + "byteOffset" : 463148 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 307144 + "byteLength" : 2652, + "byteOffset" : 465800 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 307756 + "byteLength" : 3536, + "byteOffset" : 468452 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 308572 + "byteLength" : 2652, + "byteOffset" : 471988 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 309184 + "byteLength" : 2652, + "byteOffset" : 474640 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 309796 + "byteLength" : 3536, + "byteOffset" : 477292 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 310612 + "byteLength" : 2652, + "byteOffset" : 480828 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 311224 + "byteLength" : 2652, + "byteOffset" : 483480 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 311836 + "byteLength" : 3536, + "byteOffset" : 486132 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 312652 + "byteLength" : 2652, + "byteOffset" : 489668 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 313264 + "byteLength" : 2652, + "byteOffset" : 492320 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 313876 + "byteLength" : 3536, + "byteOffset" : 494972 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 314692 + "byteLength" : 2652, + "byteOffset" : 498508 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 315304 + "byteLength" : 2652, + "byteOffset" : 501160 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 315916 + "byteLength" : 3536, + "byteOffset" : 503812 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 316732 + "byteLength" : 2652, + "byteOffset" : 507348 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 317344 + "byteLength" : 2652, + "byteOffset" : 510000 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 317956 + "byteLength" : 3536, + "byteOffset" : 512652 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 318772 + "byteLength" : 2652, + "byteOffset" : 516188 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 319384 + "byteLength" : 2652, + "byteOffset" : 518840 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 319996 + "byteLength" : 3536, + "byteOffset" : 521492 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 320812 + "byteLength" : 2652, + "byteOffset" : 525028 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 321424 + "byteLength" : 2652, + "byteOffset" : 527680 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 322036 + "byteLength" : 3536, + "byteOffset" : 530332 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 322852 + "byteLength" : 2652, + "byteOffset" : 533868 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 323464 + "byteLength" : 2652, + "byteOffset" : 536520 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 324076 + "byteLength" : 3536, + "byteOffset" : 539172 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 324892 + "byteLength" : 2652, + "byteOffset" : 542708 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 325504 + "byteLength" : 2652, + "byteOffset" : 545360 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 326116 + "byteLength" : 3536, + "byteOffset" : 548012 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 326932 + "byteLength" : 2652, + "byteOffset" : 551548 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 327544 + "byteLength" : 2652, + "byteOffset" : 554200 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 328156 + "byteLength" : 3536, + "byteOffset" : 556852 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 328972 + "byteLength" : 2652, + "byteOffset" : 560388 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 329584 + "byteLength" : 2652, + "byteOffset" : 563040 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 330196 + "byteLength" : 3536, + "byteOffset" : 565692 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 331012 + "byteLength" : 2652, + "byteOffset" : 569228 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 331624 + "byteLength" : 2652, + "byteOffset" : 571880 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 332236 + "byteLength" : 3536, + "byteOffset" : 574532 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 333052 + "byteLength" : 2652, + "byteOffset" : 578068 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 333664 + "byteLength" : 2652, + "byteOffset" : 580720 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 334276 + "byteLength" : 3536, + "byteOffset" : 583372 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 335092 + "byteLength" : 2652, + "byteOffset" : 586908 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 335704 + "byteLength" : 2652, + "byteOffset" : 589560 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 336316 + "byteLength" : 3536, + "byteOffset" : 592212 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 337132 + "byteLength" : 2652, + "byteOffset" : 595748 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 337744 + "byteLength" : 2652, + "byteOffset" : 598400 }, { "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 338356 + "byteLength" : 3536, + "byteOffset" : 601052 }, { "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 339172 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 339784 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 340396 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 341212 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 341824 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 342436 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 343252 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 343864 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 344476 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 345292 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 345904 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 346516 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 347332 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 347944 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 348556 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 349372 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 349984 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 350596 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 351412 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 352024 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 352636 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 353452 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 354064 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 354676 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 355492 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 356104 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 356716 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 357532 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 358144 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 358756 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 359572 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 360184 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 360796 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 361612 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 362224 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 362836 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 363652 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 364264 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 364876 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 365692 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 366304 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 366916 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 367732 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 368344 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 368956 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 369772 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 370384 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 370996 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 371812 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 372424 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 373036 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 373852 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 374464 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 375076 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 375892 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 376504 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 377116 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 377932 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 378544 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 379156 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 379972 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 380584 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 381196 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 382012 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 382624 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 383236 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 384052 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 384664 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 385276 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 386092 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 386704 - }, - { - "buffer" : 0, - "byteLength" : 816, - "byteOffset" : 387316 - }, - { - "buffer" : 0, - "byteLength" : 612, - "byteOffset" : 388132 + "byteLength" : 2652, + "byteOffset" : 604588 } ], "buffers" : [ { - "byteLength" : 388744, - "uri" : "data:application/octet-stream;base64,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CQgAAAkIAAAJCAAACQgAAAkVCAAJFQgACQgAAAkIAAAJCAAACQgAAAkIAAAJAAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkAAAAJAAAACQAAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJAAAACQgAAAkIAAAJAAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJAAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkAAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkAAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkAAAAJAAAACQAAAAkIAAAJCAAACQgAAAkAAAAJAAAACQAAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJAAAACQgAAAkAAAAJCAAACQAAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJAAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAgJExQJCBMUCAkTAAkIEwAICRMACQgTAAgJEwAJCBMACAkTAAgJEwAICRMACAkTAAgJEwAICRMACAkTAAgJEwAICQAACAkAAAgJAAAICQAACAkTAAgJEwAJCAAACAkAAAkIAAAJCAAACQgAAAkIAAAJCBMACQgTAAkAAAAJAAAACRMAAAkAAAAJEwgUCRMIFAkTFAgJExQICRMUCBMJFAgTFAkAExQJABMUCQATFAkAExQJABMUCQATFAAAFBMAABMUAAAUEwAAExQAABMUAAATFAAAExQJABMUCQgTFAkIEwgUCQkTCBQIEwkUCQgTFAgTCRQJCBMUCAkTFAkIExQICRMACQgTAAkIExQJCBMACAkTAAkIEwAJCBMACAkTAAkIEwAICRMACAkTAAkIEwAICRMACAkTAAgJEwAICRMACAkTAAgJEwAICRMACAkTAAgJEwAICRMACAkAAAgJAAAICQAACAkAAAgJAAAICQAACAkAAAgJAAAICQAACAkTAAgJAAAJCAAACQgTAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIEwAJCBMACQgTAAkIEwAJAAAACQAAAAkAAAAJAAAACQAAAAkTCBQJEwgUCRMIFAkTCBQJExQICRMUCAkTFAgTCRQIExQJABMUCQATFAkAExQJABMUCQATFAkAExQJABMUCQATFAkAExQAABQTAAAUEwAAExQAABQTAAAUEwAAExQAABQTAAATFAAAExQAABQTAAATFAAAExQJCBMUCQATFAkIExQICRMJFAgTCQgUEwgJFAkTCBQJCBMUCBMJFAkIExQJCBMUCAkTFAkIExQJCBMUCAkTFAkIExQJCBMACQgTAAkIEwAICRMACAkTAAkIEwAICRMACAkAAAgJAAAICQAACAkAAAkIAAAJCAAACQgAAAkIAAAJAAAACRMIFAkTCBQJExQIExQJABMUCQATFAkAExQAABQTAAAUEwAAFBMAABMUCQATCRQICRMIFAkIExQJCBMUCQgTFAgJExQJCBMUCAkTAAkIEwAICRMACQgTAAkIEwAJCBMACAkTAAgJEwAICRMACAkTAAgJEwAICRMACAkTAAgJEwAICQAACAkAAAgJAAAICQAACAkTAAgJEwAICRMACAkTAAkIEwAJCBMACQgAAAkIEwAJCBMACQgTAAkTCAAJEwgUCRMIAAkTCBQJEwgUCRMIFAkTFAgTCRQIEwkUCBMJFAgTFAkAExQJABMUCQATFAkAExQJABMUCQATFAAAFBMAABMUAAAUEwAAExQAABMUAAATFAAAExQAABMUCQATFAkAExQJCAkTFAgTCRQICRMIFBMJCBQJCBMUCAkTFAkIExQICRMACQgTAAkIEwAJCBMACQgTAAkIEwAJCBMACQgTAAgJEwAICRMACAkTAAgJEwAICRMACAkTAAgJAAAICQAACAkAAAgJAAAICQAACAkAAAgJEwAICRMACQgTAAkIEwAJCAAACQgTAAkIAAAJCBMACQgTAAkIEwAJEwgACRMIFAkAAAAJEwgUCRMIFAkTCBQJExQIEwkUCBMUCQATFAkAExQJABMUCQATFAkAExQJABMUAAAUEwAA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XwAVAF8ALQAXACMAbgAXAG4AZQAvABcAZQAvAGUAaQAtAF8APwAtAD8AFAAjABcAVgAjAFYAVwAWAC4AWAAWAFgAWQAvABYAWQAvAFkAWgAXAC8AWgAXAFoAVgBbAEIAXABbAFwAPwBCACMAVwBCAFcAXAAuAFQAXQAuAF0AWABUAF4APgBUAD4AXQBeAFUAXwBeAF8APgBVAFsAPwBVAD8AXwBiAHYAgQBiAIEAeQBkAG0AfgBkAH4AegBoAGQAegBoAHoAfAB3AHEAfwB3AH8AgQAuABYAYQAuAGEAbwBVAF4AYwBVAGMAawAjAEIAdAAjAHQAbgAWAC8AaQAWAGkAYQBbAFUAawBbAGsAZwBUAC4AbwBUAG8AdQB7AHoAfgB7AH4AgACBAH8AeACBAHgAeQB5AHgAfAB5AHwAfQB9AHwAegB9AHoAewBzAGYAewBzAHsAgABwAGAAeABwAHgAfwBqAGIAeQBqAHkAfQBsAHIAgABsAIAAfgBgAGgAfABgAHwAeABmAGoAfQBmAH0AewAhAA8AhAAhAIQAiwAOACEAiwAOAIsAggAcAIkAhQAcAIUADADmAfYBigDmAYoAHQBIAIcAgwBIAIMAKwDxAfcBiADxAYgASQAwAEUAhgAwAIYAhQBFACsAgwBFAIMAhgApABEAjwApAI8AlQAQACkAlQAQAJUAjQAnAJMAjwAnAI8AEQDpAfgBlADpAZQAKABGAJEAjQBGAI0AKgDwAfkBkgDwAZIARwAsAFwAkAAsAJAAjgBcAFcAjABcAIwAkACWANgANgGWADYB1gDYAJcA1wDYANcANgE2AdcAmQA2AZkA2QDWADYB2QDWANkAmACYANkANwGYADcB2gDZAJkA2wDZANsANwE3AdsAmwA3AZsA3ADaADcB3ADaANwAmgCaANwAOAGaADgB3QDcAJsA3gDcAN4AOAE4Ad4AnQA4AZ0A3wDdADgB3wDdAN8AnACcAN8AOQGcADkB4ADfAJ0A4QDfAOEAOQE5AeEAnwA5AZ8A4gDgADkB4gDgAOIAngCeAOIAOgGeADoB4wDiAJ8A5ADiAOQAOgE6AeQAoQA6AaEA5QDjADoB5QDjAOUAoACgAOUAOwGgADsB5gDlAKEA5wDlAOcAOwE7AecAowA7AaMA6ADmADsB6ADmAOgAogCiAOgAPAGiADwB6QDoAKMA6gDoAOoAPAE8AeoApQA8AaUA6wDpADwB6wDpAOsApACkAOsAPQGkAD0B7ADrAKUA7QDrAO0APQE9Ae0ApwA9AacA7gDsAD0B7gDsAO4ApgCmAO4APgGmAD4B7wDuAKcA8ADuAPAAPgE+AfAAqQA+AakA8QDvAD4B8QDvAPEAqACoAPEAPwGoAD8B8gDxAKkA8wDxAPMAPwE/AfMAqwA/AasA9ADyAD8B9ADyAPQAqgCqAPQAQAGqAEAB9QD0AKsA9gD0APYAQAFAAfYArQBAAa0A9wD1AEAB9wD1APcArACsAPcAQQGsAEEB+AD3AK0A+QD3APkAQQFBAfkArwBBAa8A+gD4AEEB+gD4APoArgCuAPoAQgGuAEIB+wD6AK8A/AD6APwAQgFCAfwAsQBCAbEA/QD7AEIB/QD7AP0AsACwAP0AQwGwAEMB/gD9ALEA/wD9AP8AQwFDAf8AswBDAbMAAAH+AEMBAAH+AAABsgCyAAABRAGyAEQBAQEAAbMAAgEAAQIBRAFEAQIBtQBEAbUAAwEBAUQBAwEBAQMBtAC0AAMBRQG0AEUBBAEDAbUABQEDAQUBRQFFAQUBtwBFAbcABgEEAUUBBgEEAQYBtgC2AAYBRgG2AEYBBwEGAbcACAEGAQgBRgFGAQgBuQBGAbkACQEHAUYBCQEHAQkBuAC4AAkBRwG4AEcBCgEJAbkACwEJAQsBRwFHAQsBuwBHAbsADAEKAUcBDAEKAQwBugC6AAwBSAG6AEgBDQEMAbsADgEMAQ4BSAFIAQ4BvQBIAb0ADwENAUgBDwENAQ8BvAC8AA8BSQG8AEkBEAEPAb0AEQEPAREBSQFJAREBvwBJAb8AEgEQAUkBEgEQARIBvgC+ABIBSgG+AEoBEwESAb8AFAESARQBSgFKARQBwQBKAcEAFQETAUoBFQETARUBwADAABUBSwHAAEsBFgEVAcEAFwEVARcBSwFLARcBwwBLAcMAGAEWAUsBGAEWARgBwgDCABgBTAHCAEwBGQEYAcMAGgEYARoBTAFMARoBxQBMAcUAGwEZAUwBGwEZARsBxADEABsBTQHEAE0BHAEbAcUAHQEbAR0BTQFNAR0BxwBNAccAHgEcAU0BHgEcAR4BxgDGAB4BTgHGAE4BHwEeAccAIAEeASABTgFOASAByQBOAckAIQEfAU4BIQEfASEByADIACEBTwHIAE8BIgEhAckAIwEhASMBTwFPASMBywBPAcsAJAEiAU8BJAEiASQBygDKACQBUAHKAFABJQEkAcsAJgEkASYBUAFQASYBzQBQAc0AJwElAVABJwElAScBzADMACcBUQHMAFEBKAEnAc0AKQEnASkBUQFRASkBzwBRAc8AKgEoAVEBKgEoASoBzgDOACoBUgHOAFIBKwEqAc8ALAEqASwBUgFSASwB0QBSAdEALQErAVIBLQErAS0B0ADQAC0BUwHQAFMBLgEtAdEALwEtAS8BUwFTAS8B0wBTAdMAMAEuAVMBMAEuATAB0gDSADABVAHSAFQBMQEwAdMAMgEwATIBVAFUATIB1QBUAdUAMwExAVQBMwExATMB1ADUADMBVQHUAFUBNAEzAdUANQEzATUBVQFVATUBlwBVAZcA2AA0AVUB2AA0AdgAlgCWANYAlgGWAJYBVgHWAJgAWAHWAFgBlgGZANcAlwGZAJcBWQHXAJcAVwHXAFcBlwGYANoAmAGYAJgBWAHaAJoAWgHaAFoBmAGbANsAmQGbAJkBWwHbAJkAWQHbAFkBmQGaAN0AmgGaAJoBWgHdAJwAXAHdAFwBmgGdAN4AmwGdAJsBXQHeAJsAWwHeAFsBmwGcAOAAnAGcAJwBXAHgAJ4AXgHgAF4BnAGfAOEAnQGfAJ0BXwHhAJ0AXQHhAF0BnQGeAOMAngGeAJ4BXgHjAKAAYAHjAGABngGhAOQAnwGhAJ8BYQHkAJ8AXwHkAF8BnwGgAOYAoAGgAKABYAHmAKIAYgHmAGIBoAGjAOcAoQGjAKEBYwHnAKEAYQHnAGEBoQGiAOkAogGiAKIBYgHpAKQAZAHpAGQBogGlAOoAowGlAKMBZQHqAKMAYwHqAGMBowGkAOwApAGkAKQBZAHsAKYAZgHsAGYBpAGnAO0ApQGnAKUBZwHtAKUAZQHtAGUBpQGmAO8ApgGmAKYBZgHvAKgAaAHvAGgBpgGpAPAApwGpAKcBaQHwAKcAZwHwAGcBpwGoAPIAqAGoAKgBaAHyAKoAagHyAGoBqAGrAPMAqQGrAKkBawHzAKkAaQHzAGkBqQGqAPUAqgGqAKoBagH1AKwAbAH1AGwBqgGtAPYAqwGtAKsBbQH2AKsAawH2AGsBqwGsAPgArAGsAKwBbAH4AK4AbgH4AG4BrAGvAPkArQGvAK0BbwH5AK0AbQH5AG0BrQGuAPsArgGuAK4BbgH7ALAAcAH7AHABrgGxAPwArwGxAK8BcQH8AK8AbwH8AG8BrwGwAP4AsAGwALABcAH+ALIAcgH+AHIBsAGzAP8AsQGzALEBcwH/ALEAcQH/AHEBsQGyAAEBsgGyALIBcgEBAbQAdAEBAXQBsgG1AAIBswG1ALMBdQECAbMAcwECAXMBswG0AAQBtAG0ALQBdAEEAbYAdgEEAXYBtAG3AAUBtQG3ALUBdwEFAbUAdQEFAXUBtQG2AAcBtgG2ALYBdgEHAbgAeAEHAXgBtgG5AAgBtwG5ALcBeQEIAbcAdwEIAXcBtwG4AAoBuAG4ALgBeAEKAboAegEKAXoBuAG7AAsBuQG7ALkBewELAbkAeQELAXkBuQG6AA0BugG6ALoBegENAbwAfAENAXwBugG9AA4BuwG9ALsBfQEOAbsAewEOAXsBuwG8ABABvAG8ALwBfAEQAb4AfgEQAX4BvAG/ABEBvQG/AL0BfwERAb0AfQERAX0BvQG+ABMBvgG+AL4BfgETAcAAgAETAYABvgHBABQBvwHBAL8BgQEUAb8AfwEUAX8BvwHAABYBwAHAAMABgAEWAcIAggEWAYIBwAHDABcBwQHDAMEBgwEXAcEAgQEXAYEBwQHCABkBwgHCAMIBggEZAcQAhAEZAYQBwgHFABoBwwHFAMMBhQEaAcMAgwEaAYMBwwHEABwBxAHEAMQBhAEcAcYAhgEcAYYBxAHHAB0BxQHHAMUBhwEdAcUAhQEdAYUBxQHGAB8BxgHGAMYBhgEfAcgAiAEfAYgBxgHJACABxwHJAMcBiQEgAccAhwEgAYcBxwHIACIByAHIAMgBiAEiAcoAigEiAYoByAHLACMByQHLAMkBiwEjAckAiQEjAYkByQHKACUBygHKAMoBigElAcwAjAElAYwBygHNACYBywHNAMsBjQEmAcsAiwEmAYsBywHMACgBzAHMAMwBjAEoAc4AjgEoAY4BzAHPACkBzQHPAM0BjwEpAc0AjQEpAY0BzQHOACsBzgHOAM4BjgErAdAAkAErAZABzgHRACwBzwHRAM8BkQEsAc8AjwEsAY8BzwHQAC4B0AHQANABkAEuAdIAkgEuAZIB0AHTAC8B0QHTANEBkwEvAdEAkQEvAZEB0QHSADEB0gHSANIBkgExAdQAlAExAZQB0gHVADIB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x6buul52vb1wdoc9CbY+vELSt72/bxY9Khviu15Bsb1LPYc9ALqjvCWFsL0eLhY9Fg82vDe8nL2BsoY90O22vAZIpb1ryxU9lClCvA03iL23J4Y9niHKvOcKmr27aBU9E0ROvEI7d735CIU9uc6CvBEgkr1WJhU95cYMvHh/b71JSoQ9+I0Aunh/b71JSoQ9+I0Auro5j73JuhQ9lBU0u7o5j73JuhQ9lBU0u7MFgL2onXQ9OccRPJYHkr1U9xM9gHFKO54HXL3+oao9D/JwPJe+WL3yabM9w/yWPBYxlb17UgM9ZsoWO8zAmb22WBM99obrO9wxnL0IFwM9oOy/OzDanb2acO48DnisO+vtpb3GjQM96aa7O/mpr72FBAQ9MWG3O+G5pr1nfO88mL+rO5SZr70yiPA8JAerO35Jp71zW9Q8jATgO35Jp71zW9Q8jATgO/YEn70ZONM8kKPeO/YEn70ZONM8kKPeOweOr73NftU8i2XhOweOr73NftU8i2XhO0aMp72Z94A8kiurPKmIr71pLII8EjCrPOWPn72MhX88DyerPEWMp73t0Wo8U1q/POOPn738cGg8Llq/PKmIr73cMm08eFq/PEWMp73HkVQ8XH3VPKmIr73x61Y8UH3VPOOPn72fN1I8ZX3VPEWMp72DLu07UbMbPeOPn73gg+g7RbMbPamIr70j2fE7XLMbPUWMp72ZXNo7ar4gPamIr72rBt87ar4gPeOPn72HstU7Z74gPUWMp70pAMg7Ht4lPamIr70Aqsw7Gd4lPeOPn71SVsM7I94lPUWMp72gYis7krNGPeOPn73RDyI7lbNGPamIr71utTQ7j7NGPUWMp72VTKg4t1ZTPeOPn73rDIK4uFZTPamIr70SU2k5tFZTPUWMp71mfZK6RrtGPUWMp71mfZK6RrtGPeOPn738IqW6SbtGPeOPn738IqW6SbtGPamIr72er3+6QrtGPamIr72er3+6QrtGPUaMp71ExsK6oeAlPeOPn73ia9W6neAlPaqIr72oILC6peAlPUaMp72lZci6TakgPeOPn708C9u6SqkgPaqIr70OwLW6UakgPUaMp73HLsu6v2gbPaqIr70xibi6xWgbPeOPn71a1N26uGgbPUaMp70WZNK6fDvVPOOPn72sCeW6DzrVPKqIr71/vr+65zzVPEaMp70bZNK6/Vy+PKqIr72Gvr+65l++POOPn72xCeW6Flq+PEaMp70gZNK67z6pPOOPn723CeW6kTapPKqIr72Jvr+6S0epPLKJp714ntK6PILoO4Qdob3Ft+C68gvmO+H1rb0rhcS6iPjqO2GEp72pWKm6kkWNO0HQnb2jALy628eHO4I4sb2xsJa6SsOSO2mSp73dtc06gskvudXztL3FOOQ6TNkrOdXztL3FOOQ6TNkrOfwwmr3yMrc6GNsCuvwwmr3yMrc6GNsCuqLkp70ye1o70y/ou9mlsb0vz2M7nX7fu9mlsb0vz2M7nX7fu2sjnr00J1E7COHwu2sjnr00J1E7COHwu7AiqL1BALM7RtwlvLAiqL1BALM7RtwlvEWnob3tea87A4sovEWnob3tea87A4sovB2err2XhrY7iC0jvB2err2XhrY7iC0jvCcqqL0kQFQ8fgIkvAkEsL2oeVY8f9IgvEdQoL2kBlI8fTInvC/wp73OeLs8wqEXvC/wp73OeLs8wqEXvEaRn73lV7o8fJ8bvEaRn73lV7o8fJ8bvBdPsL26mbw8CKQTvBdPsL26mbw8CKQTvHRZp72yi+08f00avNJXnr0Sf+w8eRcgvBRbsL1TmO48hYMUvF6Dpr0HwQQ9hKYpvBZssL3xOAU95U4hvKmanL0dSQQ9JP4xvPVflb0hAgQ9n/D1u2+ckr2EvAM9YNwsu2+ckr2EvAM9YNwsuzqPl71b/Os8FdrduyADmb0x37k8Ss3RuyADmb0x37k8Ss3Ru/HSlL0dXuw8JVcZu/HSlL0dXuw8JVcZu1i6lb1m1Lk8hhODuVi6lb1m1Lk8hhODuVi6lb1m1Lk8hhODuSpRl73k5O08OqUJO865mL2KB9A8w5uJO865mL2KB9A8w5uJOxpSmb0cFHY8APqpPBpSmb0NbWA8AfS+PJxslb3GuUo8hcmoPJxslb0YEzk8r46+PBMclL25xNk7QEeoPBMclL3xD8U7hmW+PAQHlr1VYT080vglup1slb2g/4A6HV+oPJ1slb2g/4A6HV+oPJxslb0/HUs6NWG+PJxslb0/HUs6NWG+PBlSmb1JeMC6j8moPBpSmb1k5sO6E16+PBlSmb3rR8e6IurUPJ1slb2WBBU6tJbUPJ1slb2WBBU6tJbUPKtYmb0OGM+62WobPT1amb3YVs26gKggPVePlb1byqG543AbPVePlb1byqG543AbPdmflb2Xw865P6ggPdmflb2Xw865P6ggPQpLlL0/VCw703cbPf5klL109Bc72aYgPWSPlb13drg7DoMbPRMclL3grbA7OHHUPJxslb0dyCc8wZXUPBpSmb0tZks8zgfVPLFYmb2uveA7JaYbPTlamb29ds07c7EgPfVcmb3HUrk7E78lPZGflb0MFqc7B6UgPXTBlb0kmpM7ubclPZuclL3P5QE7K8klPfhMlr2zFyk7ntc0PS1vmb2b2B87GnI/PXq5lb0Bty06asA4PfhMlr0LKy66otw0PfhMlr0LKy66otw0PaVgmb12zAu4J51KPS1vmb17MaC6VHg/PS1vmb17MaC6VHg/PQldmb326ce6z9slPXbClb007fW55dQlPXbClb007fW55dQlPcAXmr0EbUs8AR/guxMKt72/eQU9qjrLu74/ub1VWQU98NDhur4/ub1VWQU98NDhugmItr0DK+88NMa7u8q0uL3wzu88Yu27usq0uL3wzu88Yu27ugJDtr3BPL08iMO1uwJDtr3BPL08iMO1uwsGub3nhL0802slOgsGub3nhL0802slOgsGub3nhL0802slOoaltb0UCFI8YnjHu7seub3FskQ8vR+EOYTLur3IuOk7iJqoPIXLur13mdQ7eXe+PLeCub2MhL06TKWoPLeCub2MhL06TKWoPLWCub1jz1I8UxOpPLWCub3r3kA8fZ6+PNextb3mcn08mCKqPNextb2Ul2c8kPu+PDvLtb3ActM8B0iYOzvLtb3ActM8B0iYO5cOtr0FE/E8GGciOxKNtr0NyAQ96twiO9extb39Z1I8gQrVPLWCub1tWi8825vUPGartb3xhu47XqYbPemptb3jO9s7erEgPblgub2fRsc7LIMbPetQub1c3bU7F6UgPaWdur3BQUo79XcbPUqEur2E0TU75aYgPcZgub1yQBU5/nAbPcZgub1yQBU5/nAbPYLLur1l5L87hnjUPLeCub1D/4U6Yp3UPLeCub1D/4U6Yp3UPLWCub0tf6E6k3C+PLWCub0tf6E6k3C+PNextb1YNJC6Qu/UPNextb1fxIy6Imm+PGurtb31EZi682obPeSptb1AUpa6jaggPSentb0455C629slPaVQub2/Tms4TKggPaVQub2/Tms4TKggPdMuub3QW6G3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iYdoP0mcvT7AAdg+wzQ/Prg+Yj8i9T0+IbBbP8InOj7Ml1M/ZxA3PoEISz9mBQ4+xq9IPxJVzD0DWkY/zqnqPhDAlT7wl9c+aJaYPjwoxj643Z4+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8LDQoOCw0KDgsNCg4LDQoOCw0KDgsKDQ4LCg0JCwoNCQsKDQkLCg0JCwoNCQsKDQkLCg0JCwoNCQsKDQ4LCg0JCwoNEwsKDQ4LCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoTDQsKDRMLChMNCwoNEwsKEw0LCg0TCwoTDQsKDRMLCg0TCwoTDQsKEw0LCg0TCwoTDQsKDRMLChMNCwoNEwsKEw0LCg0TCwoTDQsKDRMLChMNCwoNCQsKEw0LCg0JCwoNCQsKEw0LChMNCwoNCQsKDQkKCxMNCgsNCQoLCRMKCw0JCQ0KCw0JCgsJEw0KCQ0TCAkTDQgNCQ4ICQgNEwkNCA4JCBMNCAkNDggJDQ4ICRMNCAkBDQgJAQ4ICQEhAQgJIQgBCSEIAQkhAQgCKwEhCAIBCCErAQIhJQECJQgBAiEAAQIlIQECISsBAiUAAQIlIQIBISUBAiUhAQIlIQIBJSEBAiUhAQIjJQECIyUBAiMAAQIjAAECJQABAiUAAQIfJQECIx8BAiUfAQIjJQECIy0BAiUfAQIfIwEfAiUBAiMfAR8CJQECHyMBHwIlAR8CJQEfAiUBHwIlAR8CJQEfAiUBHwIjAR8CIx8BAiUfAQIlHwECJQEfAiMfAQIAHwECJR8BAiUfAQIjHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwEjAB8BAAAfAQgJHwEICR8BCAkBHwgJAR8ICQEfCAkBHwgJHwEICR8BCAkBHwgJAR8ICQEIHwkBCB8JAQgJHwEIHwkIAQkfAQgJAggBCR8ICQENCAkBHwgJAQ0ICQ0OCAkNDggJDRMJCA0OCQgNEwkIDQ4JDRMIDQkOCAkNEwgJDQoLCQ0TCg0JCgsKCw0JCgsNCQoLCRMLCg0JCwoTDQsKDQkLCg0JCwoNCQsKDQkLCg0JCwoTDQsKEw0LCg0JCwoNCQsKEw0LCg0JCwoTDQsKDQkLCg0TCwoTDQsKDQ4LCg0TCwoNAAsKEw0LCg0ACwoNEwsKDQALCg0ACw0KAAsKAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALCgAACwoAAAsKAAALCgAACwoAAAsKAAALCgAACwoAAAsKAAALCgAACwoAAAsKAAALCgAACwoAAAsKDQALCg0ACwoNAAsKAAALCg0TCwoNEwsKDQALCg0TCwoNEwsKDQALCg0ACwoNEwsKDRMLCg0TCwoNEwsKEw0LCg0TCwoTDQsKDRMLCg0TCwoTDQsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDRMLCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNDgsKDQ4LCg0ACwoNDgsKDQ4LCg0OCwoODQsKDQ4LDgoNCw4NCgsODQoLDg0KCw4NCgsODQoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoNCw4KCw0OCg0LDgoNCw4KDQ4LAA0OCwoODQsADg0LAA4NCwAODQsADg0LAA4NCwAODQsADg0LAA4NCwAODQsADg0AAA4NAAAODQAADg0AAA4PAAAODwAADg8AAA4PAAAODwAADw4QAA4PEAAODxAADg8AAA8OAAAODxANDg8QAA4NDwkODxANDg0PCA4PEAAODxANDg8QDQ4PDQgODw0IDw4QAA8OEAAODxANDg8NCQ8OEAAODxANDg0PCQ8OEAAODxAADw4QAA4PDQAODxAADw4QAA4PAAAODQAADg0LAA4NAAAODQsADg0JIQ0OCQsODQkIDQ4JCA4JDQgNDgkLDQ4JIQ0OCwoNDgsKDQ4LCg0LDgoNCwoODQsOCgsNCg4LDQ4KCw0OCgsNDgoNCw4KCw0OCgsNDgoNDgsKDQsOCg0OCwAODQsADg0LAAsNDgoLDQoOCw0KDgsKDQ4LCg0OCwoNDg0KCwkNCQoLDQkOCAkNDggJCA0OCQ0IDggJDQ4ICQINAQgJIQEICQIBIQgCAQIhCCECAQACASEAAiEBAAIhAQACIQEAAiEBAAIhAwECAwQhAiEBAwIhAwACAwQAAgMhAAIhASMCAwQAAgMjBAIDIwQCASMhAgEjIQMCBAADAgQAAiMDBAIjAQADAgQAAiMBAwIjAQAEAgMAAgMBIwIjAQAEAgMjAiMEAQIEAyMCASMEAgQDIwIBIwMCASMDAgMEAAIDBAACAQMhAgMEAQIDIQQCAwQAAgMEAAIDBCECAwQAAgMEAAIDBAADAgQAAwIEAAQDAgADBAIAAwIEAAQDAgAEAwIAAwQCAAQAAAAEBQAABAMAAAQAAAAEBQAABAAAAAQFAAAEAwIABAMCAAQDAgAEBQAABAUAAAQDAgAEAAAABAIDAAQAAAAEAgMABAAAAAQCAwAEAAAABAIDAAQAAAAEAgMABAIDAAQAAAAEAAAABAMCAAQAAAADAgQAAwIEAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAEBQAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQAAAAUGAAAFAAAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABgAAAAYFAAAGBQAABgAAAAYFAAAGBQAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABQYEAAUGBAAFBgQABQYEAAUGBAAFBAYABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQCAAQFAgAEBQIABAUCAAQFAgAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAFBAIABQQCAAQFAAAEBQAABQQAAAUEAAAFBAAABAUAAAUEAAAFBAAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABQQAAAUGAAAFBgAABQYAAAUGAAAFBgAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAFBgAABQYAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABQQCAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBgAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABQYAAAUEAAAFBAAABAUAAAQFAAAFBgAABQYAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABQYAAAUGAAAGBQAABgUAAAYFAAAGBQAABgAAAAYFAAAGBQAABgUAAAYFAAAGBQAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGBQAABgUAAAUGAAADBAIAAwQCAAMCBAAjAgEfAQIjHyMCAQABIwIAASMCAAEjAh8BIwgCAQgjAgEjAgABCAkCAQgJAggJDSMICQIfCAkNDggJDQ4JDQ4IDQ4JCA0JDggNDgkIDQkLCg0OCwkNCwoJDQsKDgsKDQkLDQoOCwoNCQsKDQkLDQoOCw0KDgsKDQkLDQoOCw0KDgsNCg4LDQoOCw0KDgsKDQ4LCg4NCw0KAAsNCg4LDQAACw0AAAsAAAALDQAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsKAAALAAAACwAAAAsAAAALAAAACw0AAAsNAAALDQoACw0KAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCgAACwoAAAsAAAALAAAACwAAAAsAAAALCgAACwoAAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALDQAACw0AAAsNAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACw0AAAsNAAALDQAACw0AAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKAAALCgAACwoAAAsAAAALAAAACwAAAAsAAAALCgAACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACw4KDQsKDQALDg0ACw0AAAsNAAALDg0ACw0OAAsODQALDg0ACw0OAAsNDgALDQ4ACw4NCgsODQALDQ4ACw0OAAsNAAALDQAACwAAAAsNAAALDQAACwAAAAsAAAALDQAACwAAAAsNAAALAAAACw0AAAsAAAALDQAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALDQAACwAAAAsNAAALDQAACw0OAAsNAAALDQ4KCw4KDQsNCg4LDgoNCw0OCgsODQoLDQ4KCw0OCgsNCg4LDQ4KCw0OCgsNDgoLDQoOCw0KDgsNCg4LDQoOCw0OCg0LCg4NCw4KDQ4LCg0OCwoNDgkLDQ4JCw4NCQgODQkIDg0JCA4NCAkODQgPDg0IDwgJDQ4ICQ0CCAkNAggJAgEICQIBCAkCAQgBCQIIAQkCCAEJAgECIwgBAiMhAQIjIQIBIyECASMhAgEjIQIjAQACIwEhAiMBIQIhAQACASEAAQIhCAgBAiEICQIhCAkODQ4JDQ8LAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAAQgJHwEfCAkBAiMfAR8CCAECIx8BHwIjAR8CIwEfAiMBHwIjAR8IAgEfCAIfAQIIAR8ICQEfCAkfAQgJHwEjAB8BIwAfASMtHwEjAB8BAAAfAQAAHwEAAB8BAAAfAAAAHwAAAB8AAAAfAAAAHwEAAB8tIwEfAQIjDw4QAA8OEAAQDw4AEBEAABAPDhEQDwAAEA8AABARAAAQEQAAEA8AABARAAAPEA4AEA8AAA8QDgAQEQAAEA8OERARDwAPEA4AEA8OERARAAAPEA4AEA8OERAPDhEQEQAAEBEAABAPDhEQEQAAEA8OERARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEAAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAQEQAAEBEAABARAAAQEQAAERAAABEQAAAREAAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAADw4QAA8OEAAPDhAADw4QAA8QDgAPDhAADxAOAA8OEAAPDhAADw4QAA8OEAAODxAACwoNAAsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTAAsKEwALChMNCwoTAAsKEwALChMACwoAAAsKEwALCgAACwoAAAsKAAALCgAACwoAAAsKAAALCgAACwoAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsKEwALEwoACwoTAAsKEwALChMNCwoTAAsKEw0LChMUCwoTCQsKEwkLChMJCwoTCQsKEwkLChMJCwoTCQsKEwkLChMJCwoTCQoLEwkKCxMJCRMKCxMJCgsJEw0IEwkUCAkIExQJCBMUCAkTFAgJExQICQETCAkBEwgBCR8BCAklAQgfCQEICR8BHwgJAQgfCQEfCAkBHwgJAR8ICQEfCAkfAQgJHwEICR8BAAAfAS0AHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfASUAHwElLR8BJQIBHyUtAR8lAgEfJQIBHyUtAR8lLQEfJQIBJR8tASUfLQElLR8BJS0CASUtIwElHwIBJS0fASUtAgElLQIBJS0AASUtAAElAislAQIrASUCKyUBKwIBJQIrASUrIQElKwIBJSsAAQglIQErCCUBCAkrCAEJKwgJARMICQEUCAkTFAgJExQJCBMNCRMIFAkTDQgTCRQICRMKCxMJCgsKCxMJCgsTCQsKEwkLChMJCwoTCQsKEwkLChMJCwoTCQsKEwkLChMJCwoTDQsKEwkLChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKExQLChMJCwoTFAsKEwkLChMUCwoTCQsKEwkLEwoUCxMKFAsKEwkLEwoUEwoLCRMLChQTCQoLExQLChMJFAgTFAkrCRMUCBMUCQgJCBMUCRMIFAgJExQICSUTAQgJKwEICSUBKwglASUrCCslAQAlASsAJSsBACUrAQAlKwEAJSsBACUrJgElJicrJSsBJiUrJgAlJicAJSYrACUrAS0lJicAJSYtJyUmLSclAS0rJQEtKyYlJwAmJScAJS0mJyUtAQAmJScAJS0BJiUtAQAnJSYAJSYBLSUtAQAnJSYtJS0nASUnJi0lAS0nJScmLSUBLSYlAS0mJSYnACUmJwAlASYrJSYnASUmKyclJicAJSYnACUmJyslJicAJSYnACUmJwAmJScAJiUnACcmJQAmJyUAJiUnACcmJQAnJiUAJiclACcAAAAnKAAAJyYAACcAAAAnKAAAJwAAACcoAAAnJiUAJyYlACcmJQAnKAAAJygAACcmJQAnAAAAJyUmACcAAAAnJSYAJwAAACclJgAnAAAAJyUmACcAAAAnJSYAJyUmACcAAAAnAAAAJyYlACcAAAAmJScAJiUnACcoAAAnKAAAJygAACcoAAAnKAAAJygAACcoAAAnKAAAKCcAACgnAAAoJwAAKCcAACgnAAAoJwAAKAAAACgpAAAoAAAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKQAAACkoAAApKAAAKQAAACkoAAApKAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKCknACgpJwAoKScAKCknACgpJwAoJykAKCcAACgnAAAoJwAAKCcAACgnAAAoJwAAKCcAACgnAAAoJwAAKCcAACgnAAAoJwAAKCclACcoJQAnKCUAJyglACcoJQAnKAAAJygAACcoAAAnKAAAJygAACcoAAAnKAAAJygAACcoAAAnKAAAJygAACcoAAAoJyUAKCclACcoAAAnKAAAKCcAACgnAAAoJwAAJygAACgnAAAoJwAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKCcAACgpAAAoKQAAKCkAACgpAAAoKQAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAAoKQAAKCkAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKCclACcoAAAnKAAAJygAACcoAAAnKAAAJygAACgnAAAoJwAAKCcAACgnAAAoJwAAKCcAACgnAAAoKQAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKCkAACgnAAAoJwAAJygAACcoAAAoKQAAKCkAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKCkAACgpAAApKAAAKSgAACkoAAApKAAAKQAAACkoAAApKAAAKSgAACkoAAApKAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApKAAAKSgAACgpAAAmJyUAJiclACYlJwAtJQEfASUtHy0lAQABLSUAAS0lAAEtJR8BLQglAQgtJQEtJQABCAklAQgJJQgJEy0ICSUfCAkTFAgJExQJExQIExQJCBMJFAgTFAkIEwkLChMUCwkTCwoJEwsKFAsKEwkLEwoUCwoTCQsKEwkLEwoUCxMKFAsKEwkLEwoUCxMKFAsTChQLEwoUCxMKFAsKExQLChQTCxMKAAsTChQLEwAACxMAAAsAAAALEwAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsKAAALAAAACwAAAAsAAAALAAAACxMAAAsTAAALEwoACxMKAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALCgAACwoAAAsAAAALAAAACwAAAAsAAAALCgAACwoAAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALEwAACxMAAAsTAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACxMAAAsTAAALEwAACxMAAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKAAALCgAACwoAAAsAAAALAAAACwAAAAsAAAALCgAACwoTAAsKEwALChMACwoTAAsKEwALEwoACwoTAAsKEwALEwoUCwoTFAsKEwALChMUCwoTFAsKExQLChMUCxQKEwsUEwoLFBMKCxQTCgsUEwoLFBMKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKEwsUCgsTFAoTCxQKEwsUChMUCwATFAsKFBMLABQTCwAUEwsAFBMLABQTCwAUEwsAFBMLABQTCwAUEwsAFBMLABQTAAAUEwAAFBMAABQTAAAUFQAAFBUAABQVAAAUFQAAFBUAABUUFgAUFRYAFBUWABQVAAAVFAAAFBUWExQVFgAUExUJFBUWExQTFQgUFRYAFBUWExQVFhMUFRMIFBUTCBUUFgAVFBYAFBUWExQVEwkVFBYAFBUWExQTFQkVFBYAFBUWABUUFgAUFRMAFBUWABUUFgAUFQAAFBMAABQTCwAUEwAAFBMLABQTCSsTFAkLFBMJCBQJEwgTFAkLExQLChMUCwoTCxQKEwsUCgsTChQLExQKCxMKFAsTChQLExQKCxMUCgsTChQLChMUCxMUCgsTChQLChMUCwoTFAsTFAoLExQKEwsUChMUCwoTCxQKExQLABQTCwAUEwsAFAkTFQgJFBMUEwgVFBMIFQgJEyUICRMlFBMICQgJExQUEwkIFBMJCBQTCQgTFAkLExQJCxMUCwoTFAsKEwsKFBMLFAoLEwoUCxMUCgsTChQLEwoUCxMUCgsTFAoLEwoUCxMKFAsTFAoLExQKCxMUCgsTFAoLFBMKCxQTCgsUEwALExQACxMUAAsUChMLExQACxQKEwsTChQLExQKCxMAAAsTAAALEwAACxMAAAsTAAALAAAACxMAAAsAAAALEwAACxMAAAsTFAALExQACxMUAAsTFAALFBMACxQTAAsUEwALFBMACxQKEwsTAAALEwAACxMAAAsTCgALEwAACwAAAAsTAAALAAAACxMAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACAklAQgJJQEICSUBCAEJJQgBCSUIAQklASUtCAElLSsBJS0rJQEtKyUBLSslAS0rJS0BACUtASslLQErJSsBACUBKwABJSsICAElKwgJJSsVFBYAFRQWABYVFAAWFwAAFhUUFxYVAAAWFQAAFhcAABYXAAAWFQAAFhcAABUWFAAWFQAAFRYUABYXAAAWFRQXFhcVABUWFAAWFRQXFhcAABUWFAAWFRQXFhUUFxYXAAAWFwAAFhUUFxYXAAAWFRQXFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFwAAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABYXAAAWFwAAFhcAABYXAAAXFgAAFxYAABcWAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAVFBYAFRQWABUUFgAVFBYAFRYUABUUFgAVFhQAFRQWABUUFgAVFBYAFRQWABQVFgALChMUCwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMNCwoTAAsKEwALChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEwALChMACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAEICR8BHwgJASUtHwEfJQgBJS0fAR8lLQEfJS0BHyUtAR8lLQEfCCUBHwglHwElCAEfCAkBHwgJHwEICR8BLQAfAS0AHwEjLR8BLQAfAQAAHwEAAB8BAAAfAQAAHwAAAB8AAAAfAAAAHwAAAB8BAAAfIy0BHwElLQsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAA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AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAACt6n8/F5WqOQAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA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got/P6P76DoAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/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AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA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AACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/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AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAsMoyP7tlhz5K66Q8+2KLPDGyQD/N2mo+g+OSPAAAAAAihVI/AscCPkLtKj1JkgY8ZChSP/4FiT3hE3k97FlSPWQoUj/+BYk94RN5PexZUj1sADc//mxSPmd3Vj27m9886pc1P+EpJj6SkwA+VLg4Ow4ZET/HYh0+cjcVPpIBCT4HThg/KVRGPg+KLD6cpi89Xw0bP4qImD7W/rk9CUG3OzgYHT99l68+i4sFPXnUMDw4GB0/fZevPouLBT151DA8DIsRP1uNwz6Kawk93/GCPP3sIj+ToTk+xgItPiF7WjywyjI/u2WHPkrrpDz7Yos8MbJAP83aaj6D45I8AAAAACKFUj8CxwI+Qu0qPUmSBjxkKFI//gWJPeETeT3sWVI9ZChSP/4FiT3hE3k97FlSPWwANz/+bFI+Z3dWPbub3zzqlzU/4SkmPpKTAD5UuDg7DhkRP8diHT5yNxU+kgEJPgdOGD8qVEY+D4osPp6mLz1fDRs/ioiYPtf+uT0HQbc7OBgdP32Xrz6LiwU9edQwPDgYHT99l68+i4sFPXnUMDwMixE/W43DPoprCT3g8YI8/ewiP5KhOT7HAi0+I3taPAAABgAKAAAACgACAAAAAgDHAQAAxwEDANUBBQABANUBAQAEAAUACwAHAAUABwABAAYACABHAAYARwAKAAsADAAJAAsACQAHAA8ADAALAA8ACwANAA0ACwAFAA0ABQAOAA0ADgARAA0AEQAQAA8ADQAQAA8AEAASABUAEwAQABUAEAARABMAFAASABMAEgAQABYAFwAUABYAFAATABgAFgATABgAEwAVABsAFwAWABsAFgAZABkAFgAYABkAGAAaACEAHQAZACEAGQAaAB0AHwAbAB0AGwAZACIAIwAeACIAHgAcACQAIgAcACQAHAAgACUAJwAjACUAIwAiACYAJQAiACYAIgAkACkAKAAlACkAJQAmACgAKgAnACgAJwAlACsAKgAoACsAKAApACsALQAsACsALAAqACoALAAuACoALgAnACwALQAvACwALwAuACcALgAwACcAMAAjAC4ALwAxAC4AMQAwADAAMQA0ADAANAAyACMAMAAyACMAMgAeADMANQA3ADMANwA2AB8AMwA2AB8ANgAbADYAOAAXADYAFwAbADcAOQA4ADcAOAA2ADgAOQA7ADgAOwA6ABcAOAA6ABcAOgAUADoAOwA9ADoAPQA8ABQAOgA8ABQAPAASADwAPgAPADwADwASAD0APwA+AD0APgA8AD4AQAAMAD4ADAAPAD8AQQBAAD8AQAA+AEAAQQBFAEAARQBDAAwAQABDAAwAQwAJAEIARgBHAEIARwAIAEQASABGAEQARgBCAEcARgBJAEcASQBLAEYASABKAEYASgBJAEkASgBOAEkATgBMAEsASQBMAEsATABNAE0ATABPAE0ATwBRAEwATgBQAEwAUABPAE8AUABUAE8AVABSAFEATwBSAFEAUgBTAFMAUgBVAFMAVQBWAFIAVABXAFIAVwBVAFUAWABZAFUAWQBWAFcAWgBYAFcAWABVAFkAWABbAFkAWwBdAFgAWgBcAFgAXABbAFsAXABgAFsAYABeAF0AWwBeAF0AXgBfAF8AXgBhAF8AYQBjAF4AYABiAF4AYgBhAGEAYgBmAGEAZgBkAGMAYQBkAGMAZABlAGUAZABnAGUAZwBoAGQAZgBpAGQAaQBnAGcAagBrAGcAawBoAGkAbABqAGkAagBnAGsAagBuAGsAbgBwAGoAbAByAGoAcgBuAG0AcwB0AG0AdABvAHEAdQBzAHEAcwBtAHQAcwB2AHQAdgB3AHMAdQB4AHMAeAB2AHkAewB3AHkAdwB2AHoAeQB2AHoAdgB4AH4AfAB5AH4AeQB6AHwAfQB7AHwAewB5AH8AhAB9AH8AfQB8AIEAfwB8AIEAfAB+AIYAgACDAIYAgwCIAIkAhQCAAIkAgACGAIYAiACMAIYAjACKAIkAhgCKAIkAigCNAIoAjACRAIoAkQCOAI0AigCOAI0AjgCPAI4AkgCVAI4AlQCPAJEAlACSAJEAkgCOAJIAlACZAJIAmQCWAJUAkgCWAJUAlgCXAJYAmgCdAJYAnQCXAJkAmwCaAJkAmgCWAJoAmwCfAJoAnwCeAJ0AmgCeAJ0AngChAJ4AnwCkAJ4ApACiAKEAngCiAKEAogCjAKMAogClAKMApQCmAKIApACnAKIApwClAKgAqgCmAKgApgClAKkAqAClAKkApQCnAK0AqwCoAK0AqACpAKsArACqAKsAqgCoAK4ArwCsAK4ArACrALAArgCrALAAqwCtALMArwCuALMArgCxALEArgCwALEAsACyALYAtACxALYAsQCyALQAtQCzALQAswCxALkAtQC0ALkAtAC3ALcAtAC2ALcAtgC4ALwAugC3ALwAtwC4ALoAuwC5ALoAuQC3AL0AvgC7AL0AuwC6AL8AvQC6AL8AugC8AL0AwQDDAL0AwwC+AL8AxQDBAL8AwQC9AMIAwADGAMIAxgDIAMAAxADHAMAAxwDGAMYAxwDLAMYAywDJAMgAxgDJAMgAyQDKAMoAyQDMAMoAzADNAMkAywDOAMkAzgDMAM8A0QDNAM8AzQDMANAAzwDMANAAzADOANQA0gDPANQAzwDQANIA0wDRANIA0QDPANUA1wDTANUA0wDSANYA1QDSANYA0gDUANoA2ADVANoA1QDWANgA2QDXANgA1wDVANsA3QDZANsA2QDYANwA2wDYANwA2ADaAOAA3gDbAOAA2wDcAN4A3wDdAN4A3QDbAOEA4wDfAOEA3wDeAOIA4QDeAOIA3gDgAOgA5ADhAOgA4QDiAOQA5gDjAOQA4wDhAOoA6wDnAOoA5wDlAOwA6gDlAOwA5QDpAO8A6wDqAO8A6gDtAO0A6gDsAO0A7ADuAPAA7QDuAPAA7gDxAPIA7wDtAPIA7QDwAPUA8wDwAPUA8ADxAPMA9ADyAPMA8gDwAPgA9ADzAPgA8wD2APYA8wD1APYA9QD3APsA+QD2APsA9gD3APkA+gD4APkA+AD2AP4A+gD5AP4A+QD8APwA+QD7APwA+wD9AAEB/wD8AAEB/AD9AP8AAAH+AP8A/gD8AAQBAAH/AAQB/wACAQIB/wABAQIBAQEDAQMBBgEFAQMBBQECAQIBBQEHAQIBBwEEAQUBBgEKAQUBCgEIAQcBBQEIAQcBCAEJAQkBCAELAQkBCwEPAQgBCgENAQgBDQELARMBEQEMARMBDAEOAREBEgEQAREBEAEMARYBEgERARYBEQEUARQBEQETARQBEwEVARcBFAEVARcBFQEYARkBFgEUARkBFAEXARoBFwEYARoBGAEbARwBGQEXARwBFwEaARoBGwEfARoBHwEdARwBGgEdARwBHQEeAR4BHQEgAR4BIAEhAR0BHwEiAR0BIgEgASABIwEkASABJAEhASIBJQEjASIBIwEgASYBJwEkASYBJAEjASgBJgEjASgBIwElAScBJgEpAScBKQErASYBKAEqASYBKgEpASoBLQEsASoBLAEpASkBLAEuASkBLgErASwBLQExASwBMQEvAS4BLAEvAS4BLwEwATQBMAEvATQBLwEyATIBLwExATIBMQEzAToBNgEyAToBMgEzATYBOAE0ATYBNAEyATsBPAE3ATsBNwE1AT0BOwE1AT0BNQE5AT4BQAE8AT4BPAE7AT8BPgE7AT8BOwE9AUIBQQE+AUIBPgE/AUEBQwFAAUEBQAE+ASsAQwFBASsAQQFCAUABQwFEAUABRAFFASsARAFDASsASAFGASsARgFEAUUBRAFGAUUBRgFHAUcBRgFIAUcBSAFJAUkBSAEpAEkBKQAmACsAKQBIAUsBRwFJAUsBSQFKAUkBJgAkAEkBJABKAUsBSgFMAUsBTAFPAUoBJAAgAEoBIABMAU0BIQAaAE0BGgBOAVABTQFOAVABTgFRAVUBSwFPAVUBTwFSAVMBUAFRAVMBUQFUATgBUwFUATgBVAE0ATwBVQFSATwBUgE3AVUBRQFHAVUBRwFLAUABRQFVAUABVQE8AVYBVAFRAVYBUQFXAVQBVgEwAVQBMAE0AVYBVwFaAVYBWgFYAVgBLgEwAVgBMAFWAScBWAFZAScBWQEkAS4BWAEnAS4BJwErAVgBWgFcAVgBXAFZAVoBVwGABVoBgAVbAVwBWgFbAVwBWwFdAV4BXAFdAV4BXQFgAV8BWQFcAV8BXAFeAWMBXwFeAWMBXgFhAWEBXgFgAWEBYAFiAWQBYQFiAWQBYgFlAWMBYQFkAWMBZAFmAWcBZAFlAWcBZQFqAWgBZgFkAWgBZAFnAWcBawFtAWcBbQFoAWwBawFnAWwBZwFqAWwBcAFvAWwBbwFrAWsBbwFzAWsBcwFtAXABzQFyAXABcgFvAW8BcgF4AW8BeAFzAX0BeQF0AX0BdAF3AXkBewFuAXkBbgF0AXoBbwR7AXoBewF5AX8BegF5AX8BeQF9AYEBfgF8AYEBfAGAAYABfAF2AYABdgGDAYUBgQGAAYUBgAGEAYQBgAGDAYQBgwGHAYkBhQGEAYkBhAGIAYgBhAGHAYgBhwGLAY0BiQGIAY0BiAGMAYwBiAGLAYwBiwGPAZEBjQGMAZEBjAGQAZABjAGPAZABjwGTAZUBkQGQAZUBkAGUAZQBkAGTAZQBkwGXAZwBlQGUAZwBlAGYAZgBlAGXAZgBlwGbAZ0BnAGYAZ0BmAGZAZkBmAGbAZkBmwHHBKABlQGcAaABnAGeAZ8BngGcAZ8BnAGdAZ4BnwGjAZ4BowGhAaABngGhAaABoQGiAaIBoQGlAaIBpQGnAaMBqQGlAaMBpQGhAaYBpAGqAaYBqgGrAagBrAGqAagBqgGkAasBqgGtAasBrQGuAawBrwGtAawBrQGqAbIBrgGtAbIBrQGwAa8BsQGwAa8BsAGtAbQBswGwAbQBsAGxAbMBtQGyAbMBsgGwAcYEmgGzAcYEswG0AZoBlgG1AZoBtQGzAZYBkgG2AZYBtgG1AbUBtgG4AbUBuAGyAZIBjgG3AZIBtwG2AbYBtwG5AbYBuQG4AbkBuwG6AbkBugG4AbgBugGuAbgBrgGyAbwBvgGuAbwBrgG6Ab8BvAG6Ab8BugG7Ab0B3gG+Ab0BvgG8AcABvQG8AcABvAG/AcIBwAG/AcIBvwHBAcEBvwG7AcEBuwHDAcUBwgHBAcUBwQHEAcQBwQHDAcQBwwHGAQIAxQHEAQIAxAHHAccBxAHGAccBxgHIAQMAxwHIAQMAyAHJAcsByAHGAcsBxgHOAckByAHLAckBywHMAc8BcQHMAc8BzAHLAdABzwHLAdABywHOAYIBdQFxAYIBcQHPAYYBggHPAYYBzwHQAYoBhgHQAYoB0AHRAdEB0AHOAdEBzgHSAY4BigHRAY4B0QG3AbcB0QHSAbcB0gG5Ac4BxgHDAc4BwwHSAdIBwwG7AdIBuwG5AXAB0wHKAXABygHNAdMB1QEEANMBBADKAdQB1gHVAdQB1QHTAdQB0wFwAdQBcAFsAdYBDgAFANYBBQDVAREADgDWAREA1gHXAdcB1gHUAdcB1AHYAREA1wFgAREAYAFdAWAB1wHYAWAB2AFiAdgBagFlAdgBZQFiAdQBbAFqAdQBagHYAQIACgDZAQIA2QHFAdkB2gHCAdkBwgHFAUsATQDaAUsA2gHZAQoARwBLAAoASwDZAdoBTQBRANoBUQDbAcIB2gHbAcIB2wHAAdsB3AG9AdsBvQHAAVEAUwDcAVEA3AHbAdwBUwBWANwBVgDdAb0B3AHdAb0B3QHeAVkA3wHdAVkA3QFWAN8B4AHeAd8B3gHdAV0A4QHfAV0A3wFZAOEB4gHgAeEB4AHfAeMB5AHiAeMB4gHhAV8A4wHhAV8A4QFdAGMA5QHjAWMA4wFfAOUB5gHkAeUB5AHjAecB6AHmAecB5gHlAWUA5wHlAWUA5QFjAOkB5wFlAOkBZQBoAOoB6AHnAeoB5wHpAY0N7QHrAY0N6wGADYAN6wHuAYAN7gGBDe0BDALsAe0B7AHrAesB7AHvAesB7wHuAfIB8AHuAfIB7gHvAYIN8QHqAYIN6gGBDYQN9gHxAYQN8QGCDfgB9AHwAfgB8AHyAYUN+gH1AYUN9QGDDfsB+QHzAfsB8wH3AYYN/QH6AYYN+gGFDf4B/AH5Af4B+QH7AYYN/AH/AYYN/wGHDfwB/gEBAvwBAQL/AYcN/wECAocNAgKIDf8BAQIDAv8BAwICAokNdAB3AIkNdwCIDQUCBAICAgUCAgIDAgYCBAIFAgYCBQIIAooNiQ0EAooNBAIGAgsCCgIHAgsCBwIJAowNawBwAIwNcACLDe0BCgILAu0BCwIMAo0NjA0KAo0NCgLtAQwCCwINAgwCDQIPAgkCDgINAgkCDQILAhACDwINAhACDQIOAhMCEQIPAhMCDwIQAg8CEQLsAQ8C7AEMAhQCEgIRAhQCEQITAhECEgLvAREC7wHsARcCFQITAhcCEwIQAhUCFgIUAhUCFAITAhgCGQIWAhgCFgIVAhoCGAIVAhoCFQIXAhsCHgIZAhsCGQIYAh0CGwIYAh0CGAIaAhwCHwIeAhwCHgIbAjoCHAIbAjoCGwIdAiECIAIeAiECHgIfAiACIgIZAiACGQIeAiYCJAIgAiYCIAIhAiQC+AEiAiQCIgIgAigCJwIjAigCIwIlAicC+wH3AScC9wEjAioCKQInAioCJwIoAikC/gH7ASkC+wEnAikCKwIBAikCAQL+ASoCLAIrAioCKwIpAisCLQIDAisCAwIBAiwCLgItAiwCLQIrAi4CMAIvAi4CLwItAi0CLwIFAi0CBQIDAjACMwIxAjACMQIvAi8CMQIIAi8CCAIFAjQCNgI1AjQCNQIyAjICNQI3AjICNwIJAjYCOgIdAjYCHQI1AjUCHQIaAjUCGgI3AgkCNwI4AgkCOAIOAhoCFwI4AhoCOAI3AhACDgI4AhACOAIXAjkCOwI6AjkCOgI2Ap4COQI2Ap4CNgI0AjsCPQIcAjsCHAI6AjwCPwI9AjwCPQI7AqcCPAI7AqcCOwI5Aj0CPgIfAj0CHwIcAj8CQAI+Aj8CPgI9AkECPwI8AkECPAJDAkUCQAI/AkUCPwJBAkcCQgJEAkcCRAJJAkgCRgJCAkgCQgJHAkwCSAJHAkwCRwJKAkoCRwJJAkoCSQJLAk8CTQJKAk8CSgJLAk0CTgJMAk0CTAJKAk4CTQJQAk4CUAJSAk0CTwJRAk0CUQJQAlACUQJVAlACVQJTAlICUAJTAlICUwJUAlQCUwJWAlQCVgJXAlMCVQJYAlMCWAJWAlYCWQJbAlYCWwJXAlgCWgJZAlgCWQJWAlkCWgJdAlkCXQJcAlsCWQJcAlsCXAJeAlwCXQJhAlwCYQJfAl4CXAJfAl4CXwJjAmUCYAJiAmUCYgJmAmcCZAJgAmcCYAJlAmkCaAJlAmkCZQJmAmgCagJnAmgCZwJlAm0CawJoAm0CaAJpAmsCbAJqAmsCagJoAm4CcAJsAm4CbAJrAm8CbgJrAm8CawJtAm4CbwJzAm4CcwJxAnACbgJxAnACcQJyAnECdAJ2AnECdgJyAnMCdQJ0AnMCdAJxAnUCeAJ3AnUCdwJ0AnQCdwJ5AnQCeQJ2AncCeAJ7AncCewJ6AnkCdwJ6AnkCegJ8AnoCewKBAnoCgQJ9AnwCegJ9AnwCfQJ/AoIChAJ/AoICfwJ9AoYCggJ9AoYCfQKBAowChAKCAowCggKIAogCggKGAogChgKKAo8CjQKHAo8ChwKJAo0CjgKLAo0CiwKHApUCjgKNApUCjQKRApECjQKPApECjwKTApACkgKXApAClwKWApQCkAKWApQClgKYApYClwKbApYCmwKZApgClgKZApgCmQKaApkCLAIqApkCKgKaApsCLgIsApsCLAKZApcCnwKcApcCnAKbApsCnAIwApsCMAIuApwCnQIzApwCMwIwAp8CogKdAp8CnQKcAqACpAKiAqACogKfApICoAKfApICnwKXAqMCpwI5AqMCOQKeAqUCqAKnAqUCpwKjAqgCQwI8AqgCPAKnAqwCqgKpAqwCqQKmAqoCSQJEAqoCRAKpAqsCSwJJAqsCSQKqAq0CqwKqAq0CqgKsAq8CrQKsAq8CrAKuAq4CrAKmAq4CpgKwArMCrwKuArMCrgKxArECrgKwArECsAKAArYCtAKyArYCsgK1AnsCtQKyAnsCsgKBAnMCtgK1AnMCtQJ1AngCdQK1AngCtQJ7ArcCuQK0ArcCtAK2Am8CtwK2Am8CtgJzArkCtwK6ArkCugK9AroCtwJvAroCbwJtAr0CugK7Ar0CuwK/AmkCuwK6AmkCugJtAr4CwQLAAr4CwAK8AsACwwK4AsACuAK8AsICwALBAsICwQLJAsQCwwLAAsQCwALCAsMCxAKtAsMCrQKvArgCwwKvArgCrwKzAsQCxQKrAsQCqwKtAsYCxQLEAsYCxALCAsUCTwJLAsUCSwKrAk8CxQLGAk8CxgJRAlECxgLHAlECxwJVAskCxwLGAskCxgLCAlUCxwLIAlUCyAJYAsoCyALHAsoCxwLJAskCwQLLAskCywLKAsgCygLMAsgCzALNAsoCywLOAsoCzgLMAs0CzALOAs0CzgLRAssC1QLPAssCzwLOAtECzgLPAtECzwLSAloCzQLRAloC0QJdAl0C0QLSAl0C0gJhAtYC1ALTAtYC0wLQAtQCZgJiAtQCYgLTAmYC1AK7AmYCuwJpAr8CuwLUAr8C1ALWAtUCywLBAtUCwQK+AlgCyALNAlgCzQJaArAC1wKFArAChQKAAqYCoQLXAqYC1wKwAqECkwKPAqECjwLXAtcCjwKJAtcCiQKFApgCmgLYApgC2ALbApoCKgIoApoCKALYAtgCKAIlAtgCJQLZAtsC2ALZAtsC2QLcApQCmALbApQC2wLeAt4C2wLcAt4C3ALgAt8C4gLkAt8C5ALjApUC3wLjApUC4wKOAuMC5AJ+AuMCfgKDAo4C4wKDAo4CgwKLAukC5QLkAukC5ALiAuUC5wJ+AuUCfgLkAuYC/gLnAuYC5wLlAuoC5gLlAuoC5QLpAuwC6gLpAuwC6QLrAusC6QLiAusC4gLuAkwC7ALrAkwC6wJIAkgC6wLuAkgC7gJGAu0C7wJAAu0CQAJFAuEC3QLvAuEC7wLtAt0C2gLwAt0C8ALvAu8C8AI+Au8CPgJAAtoCJgIhAtoCIQLwAvACIQIfAvACHwI+AvEC8gLqAvEC6gLsAk4C8QLsAk4C7AJMAvIC8QLzAvIC8wL1AvMC8QJOAvMCTgJSAvUC8wL0AvUC9AL2AlQC9ALzAlQC8wJSAvYC+AL3AvYC9wL1AvcC+wLyAvcC8gL1AvkC9wL4AvkC+AIFA/wC+wL3AvwC9wL5AvsC/AL+AvsC/gLmAvIC+wLmAvIC5gLqAv0CAAMBA/0CAQP/AgIDAAP9AgID/QL6AgADcAJyAgADcgIBA3ACAAMCA3ACAgNsAmwCAgMDA2wCAwNqAgYDAwMCAwYDAgP6AmoCAwMEA2oCBANnAggDBAMDAwgDAwMGAwUD+AIJAwUDCQMHAwQDCAMLAwQDCwMNAwcDCQMOAwcDDgMKAw4DEAMMAw4DDAMKAwkDEwMPAwkDDwMOAw8DEQMQAw8DEAMOAxEDWwJeAhEDXgIQAxADXgJjAhADYwIMAxIDVwJbAhIDWwIRAxMDEgMRAxMDEQMPA1cCEgP0AlcC9AJUAvYC9AISA/YCEgMTAxMDCQP4AhMD+AL2AmcCBAMNA2cCDQNkAgEDcgJ2AgEDdgIUA/8CAQMUA/8CFAPoAhQDdgJ5AhQDeQJ8AugCFAN8AugCfAJ/AhkCIgIVAxkCFQMWAvgBFgMVA/gBFQMiAhQCFgIVAxQCFQMWAxQCFgMXAxQCFwMSAvgB8gEXA/gBFwMWA+8BEgIXA+8BFwPyARkDGAMAAhkDAAJ3ABgDGgP9ARgD/QEAAh0DGwMYAx0DGAMZAxsDHAMaAxsDGgMYAx4DIAMcAx4DHAMbAx8DHgMbAx8DGwMdA80AIQMeA80AHgMfAyEDIgMgAyEDIAMeAyMDJAMiAyMDIgMhA9EAIwMhA9EAIQPNACUDIwPRACUD0QDTACYDJAMjAyYDIwMlAyUD0wDXACUD1wAnAyYDJQMnAyYDJwMoAygDJwMpAygDKQMqAycD1wDZACcD2QApAykD2QDdACkD3QArAyoDKQMrAyoDKwMsAywDKwMtAywDLQMuAysD3QDfACsD3wAtAy0D3wDjAC0D4wAvAy4DLQMvAy4DLwMwAzADLwMxAzADMQMzAy8D4wDmAC8D5gAxA+sANQMyA+sAMgPnADUDNgM0AzUDNAMyA+8ANwM1A+8ANQPrADcDOAM2AzcDNgM1AzoDOAM3AzoDNwM5AzkDNwPvADkD7wDyADkDOwM8AzkDPAM6A/IA9AA7A/IAOwM5AzsD9AD4ADsD+AA9AzwDOwM9AzwDPQM+Az8DQAM+Az8DPgM9A/oAPwM9A/oAPQP4AEEDPwP6AEED+gD+AEIDQAM/A0IDPwNBAwABQwNBAwABQQP+AEMDRANCA0MDQgNBA0UDQwMAAUUDAAEEAUYDRANDA0YDQwNFA0cDRQMEAUcDBAEHAUgDRgNFA0gDRQNHA0cDSQNLA0cDSwNIAwcBSgNJAwcBSQNHA0kDSgNOA0kDTgNMA0sDSQNMA0sDTANNA08DUQNNA08DTQNMA1ADTwNMA1ADTANOA1MDTwNQA1MDUANVA1cDUQNPA1cDTwNTA1IDVANZA1IDWQNYA1YDUgNYA1YDWANaA1gDWQNeA1gDXgNcA1oDWANcA1oDXANgA1sDXQNiA1sDYgNhA18DWwNhA18DYQNjA2EDYgNlA2EDZQNkA2MDYQNkA2MDZANmA2kDZwNkA2kDZANlA2cDaANmA2cDZgNkA2wDaANnA2wDZwNqA2oDZwNpA2oDaQNrA28DbQNqA28DagNrA20DbgNsA20DbANqA3QDbgNtA3QDbQNwA3ADbQNvA3ADbwNyA3gDdgNxA3gDcQNzA3YDdwN1A3YDdQNxA3oDdwN2A3oDdgN5A3kDdgN4A3kDeAMQARIBewN5AxIBeQMQAXsDfAN6A3sDegN5A30DewMSAX0DEgEWAX4DfAN7A34DewN9A30DFgEZAX0DGQF/A34DfQN/A34DfwOAA38DGQEcAX8DHAGBA4ADfwOBA4ADgQOCA4QDgwOBA4QDgQMcAYMDhQOCA4MDggOBA4gDhgODA4gDgwOEA4YDhwOFA4YDhQODA4YDigOOA4YDjgOHA4gDjAOKA4gDigOGA4kDiwORA4kDkQOPA40DiQOPA40DjwOQA48DkgOUA48DlAOQA5EDkwOSA5EDkgOPA5IDkwOXA5IDlwOVA5QDkgOVA5QDlQOWA5UDmAOaA5UDmgOWA5cDmQOYA5cDmAOVA5sDmgOYA5sDmAOZA5YDmgOcA5YDnAOdA5sDngOcA5sDnAOaA58DnQOcA58DnAOeA5sDogOgA5sDoAOeA58DngOgA58DoAOhA6MDoQOgA6MDoAOiA5sDpgOkA5sDpAOiA6MDogOkA6MDpAOlA6cDpQOkA6cDpAOmA6cDpgOoA6cDqAOpA5sDqAOmA5sDrAOqA5sDqgOoA6kDqAOqA6kDqgOrA60DqwOqA60DqgOsA5sDrgOsA60DrAOuA60DrgOvA5sDmQOwA5sDsAOuA68DrgOwA68DsAOxA5cDsQOwA5cDsAOZA7IDtAOvA7IDrwOxA5MDsgOxA5MDsQOXA7MDtQO0A7MDtAOyA5EDswOyA5EDsgOTA7cDtgO0A7cDtAO1A7QDtgOtA7QDrQOvA7YDuAOrA7YDqwOtA7cDuQO4A7cDuAO2A7kDuwO6A7kDugO4A7gDugOpA7gDqQOrA7oDvAOnA7oDpwOpA70DvAO6A70DugO7A70DvwO+A70DvgO8A7wDvgOlA7wDpQOnA78DwQPAA78DwAO+A74DwAOjA74DowOlA8ADwgOhA8ADoQOjA8EDwwPCA8EDwgPAA8MDxQPEA8MDxAPCA8IDxAOfA8IDnwOhA8QDxgOdA8QDnQOfA8UDxwPGA8UDxgPEA8cDkAOUA8cDlAPGA8YDlAOWA8YDlgOdA8gDjQOQA8gDkAPHA8sDyAPHA8sDxwPFA8oDhwOOA8oDjgPJA80DygPJA80DyQPMA84DywPFA84DxQPDA9ADzQPMA9ADzAPPA9MD0APPA9MDzwPSA9EDzgPDA9EDwwPBA9QD0QPBA9QDwQO/A9YD0wPSA9YD0gPVA9kD1gPVA9kD1QPYA9cD1AO/A9cDvwO9A9kD2APbA9kD2wPcA9oD1wO9A9oDvQO7A90D2gO7A90DuwO5A98D3APbA98D2wPeA+ID3wPeA+ID3gPhA+AD3QO5A+ADuQO3A+MD4AO3A+MDtwO1A+ID4QPkA+ID5APlA+gD5QPkA+gD5APnA+YD4wO1A+YDtQOzA4gD6APnA4gD5wOMA4sD5gOzA4sDswORA+oD5QPoA+oD6APpA+kD6AOIA+kDiAOEAyEB6gPpAyEB6QMeAR4B6QOEAx4BhAMcAeID5QPqA+ID6gPrA+sD6gMhAesDIQHsA+0D3wPiA+0D4gPrA+8D7QPrA+8D6wPsA+4D3APfA+4D3wPtA/AD7gPtA/AD7QPvA1kBXwHvA1kB7wPsA+8DXwFjAe8DYwHwA/ID7gPwA/ID8APxA/ADYwFmAfADZgHxA/MD9QPyA/MD8gPxA2YB9gPzA2YB8wPxA/MD9gP3A/MD9wP0A/QDDgT1A/QD9QPzA2gB+AP2A2gB9gNmAfYD+AP6A/YD+gP3A2kB/QP8A2kB/AP5A/kD/AP/A/kD/wP7A/0DbQT+A/0D/gP8A/wD/gMABPwDAAT/AwAEAgQBBAAEAQT/A/8DAQQFBP8DBQT7AwIEXQQDBAIEAwQBBAEEAwQHBAEEBwQFBPcD+gMEBPcDBAQIBAgEBAQGBAgEBgQJBPcDCAQKBPcDCgT0AwoECAQJBAoECQQLBAoEDAQOBAoEDgT0AwsEDQQMBAsEDAQKBA0EzQPQAw0E0AMMBAwE0APTAwwE0wMOBA4E0wPWAw4E1gP1A8oDzQMNBMoDDQQPBA8EDQQLBA8ECwQQBIcDygMPBIcDDwSFA4UDDwQQBIUDEASCAwkEEQQQBAkEEAQLBBAEEQSAAxAEgAOCAwkEBgQSBAkEEgQRBBEEEgR+AxEEfgOAAxMEEgQGBBMEBgQUBHwDfgMSBHwDEgQTBHoDfAMTBHoDEwQVBBUEEwQUBBUEFAQWBHoDFQQXBHoDFwR3AxUEFgQYBBUEGAQXBBcEGAQcBBcEHAQaBHcDFwQaBHcDGgR1A3QDGQQdBHQDHQRuAxkEGwQeBBkEHgQdBB0EHgQgBB0EIAQfBG4DHQQfBG4DHwRsA2wDHwQhBGwDIQRoAx8EIAQiBB8EIgQhBCEEIgQkBCEEJAQjBGgDIQQjBGgDIwRmAyUEYwNmAyUEZgMjBCgEJQQjBCgEIwQkBCYEXwNjAyYEYwMlBCkEJgQlBCkEJQQoBCsEKAQkBCsEJAQuBCwEKQQoBCwEKAQrBDAELAQrBDAEKwQvBC8EKwQuBC8ELgQyBDQEMAQvBDQELwQzBDMELwQyBDMEMgQ2BDkENAQzBDkEMwQ3BDcEMwQ2BDcENgQ4BDsENwQ4BDsEOAQ+BDwEOQQ3BDwENwQ7BBsEPAQ7BBsEOwQeBB4EOwQ+BB4EPgQgBD4EOAQ2BD4ENgQ/BCAEPgQ/BCAEPwQiBD8ENgQyBD8EMgQuBCIEPwQuBCIELgQkBEAEQQQ6BEAEOgQ9BBgEQAQ9BBgEPQQcBBQEBgRBBBQEQQRABBYEFARABBYEQAQYBEEEQgQ1BEEENQQ6BAYEQwRCBAYEQgRBBEMERgRFBEMERQRCBEIERQQxBEIEMQQ1BEUESAQtBEUELQQxBEYESQRIBEYESARFBEkETARLBEkESwRIBEgESwQqBEgEKgQtBEsETgQnBEsEJwQqBEwETwROBEwETgRLBE8EVgNaA08EWgNOBE4EWgNgA04EYAMnBFEDVwNQBFEDUARRBFEEUARNBFEETQRSBE0DUQNRBE0DUQRTBFMEUQRSBFMEUgRUBFUESwNNA1UETQNTBFcEVQRTBFcEUwRUBFYESANLA1YESwNVBFgEVgRVBFgEVQRXBFoEWARXBFoEVwRZBFkEVwRUBFkEVARbBF0EWgRZBF0EWQRcBFwEWQRbBFwEWwReBAMEXQRcBAMEXARfBF8EXAReBF8EXgRgBAcEAwRfBAcEXwREBEQEXwRgBEQEYARHBFsEYQRgBFsEYAReBGEESgRHBGEERwRgBFIETQRKBFIESgRhBFQEUgRhBFQEYQRbBGIEXQQCBGIEAgRlBGIEYwRaBGIEWgRdBGQEngRjBGQEYwRiBGcEZARiBGcEYgRlBGUEAgQABGUEAARmBGgEZwRlBGgEZQRmBEADQgNkBEADZARnBD4DQANnBD4DZwRoBGkEPAM+A2kEPgNoBGsEaQRoBGsEaARmBGoEOgM8A2oEPANpBGwEagRpBGwEaQRrBP4DawRmBP4DZgQABGwEawT+A2wE/gNtBHAEagRsBHAEbARuBG0EbwRuBG0EbgRsBG4EbwR6AW4EegFxBHQEcARuBHQEbgRxBHYEdARxBHYEcQRzBHEEegF/AXEEfwFzBDYDOANwBDYDcAR0BDQDNgN0BDQDdAR2BHUEdwQwA3UEMAMzA3IEeAR3BHIEdwR1BHgEegR5BHgEeQR3BHcEeQQuA3cELgMwA3oEfAR7BHoEewR5BHkEewQsA3kELAMuA3wEfgR9BHwEfQR7BHsEfQQqA3sEKgMsA34EgAR/BH4EfwR9BH0EfwQoA30EKAMqA4AEoAGBBIAEgQR/BH8EgQQmA38EJgMoA4EEggSFBIEEhQQmA6ABogGCBKABggSBBKIBpwGEBKIBhASCBIIEhASHBIIEhwSFBIcEigSIBIcEiASFBIUEiAQkA4UEJAMmA4oEjQSLBIoEiwSIBIgEiwQiA4gEIgMkA40EjwSOBI0EjgSLBIsEjgQgA4sEIAMiA48EkgSRBI8EkQSOBI4EkQQcA44EHAMgA5IElQSUBJIElASRBJEElAQaA5EEGgMcA5UE9QH6AZUE+gGUBJQE+gH9AZQE/QEaA5cE8QH2AZcE9gGWBJgElwSWBJgElgSTBOgB6gHxAegB8QGXBOYB6AGXBOYBlwSYBOQB5gGYBOQBmASZBJkEmASTBJkEkwSQBOIB5AGZBOIBmQSaBJoEmQSQBJoEkASMBOAB4gGaBOABmgSbBJsEmgSMBJsEjASJBN4B4AGbBN4BmwScBJwEmwSJBJwEiQSGBJ0EvgHeAZ0E3gGcBIMEnQScBIMEnASGBKsBrgG+AasBvgGdBKYBqwGdBKYBnQSDBIAEkQGVAYAElQGgAY0BkQGABI0BgAR+BHwEiQGNAXwEjQF+BIUBiQF8BIUBfAR6BHgEgQGFAXgEhQF6BHIEfgGBAXIEgQF4BDoDagRwBDoDcAQ4A20E/QN7AW0EewFvBEIDRAOeBEIDngRkBEQDRgOfBEQDnwSeBJ4EnwSgBJ4EoARjBEgDVgSfBEgDnwRGA58EVgRYBJ8EWASgBFoEYwSgBFoEoARYBHsB/QNpAXsBaQFuAfUD1gPZA/UD2QPyA9kD3APuA9kD7gPyAyQBWQHsAyQB7AMhAaMEEAF4A6MEeAOhBKEEeANzA6EEcwOlBFQDowShBFQDoQRZA1kDoQSlBFkDpQReA10DpASmBF0DpgRiA6QEcgNvA6QEbwOmBKYEbwNrA6YEawNpA2IDpgRpA2IDaQNlA6cEogRVA6cEVQNQAwkBDwGiBAkBogSnBAcBCQGnBAcBpwRKA0oDpwRQA0oDUANOA6gEygDNAKgEzQAfA6oEqAQfA6oEHwMdA6kEyADKAKkEygCoBKsEqQSoBKsEqASqBKwErQSrBKwEqwSqBBkDrASqBBkDqgQdA3sAfQCtBHsArQSsBHcAewCsBHcArAQZA30AhACuBH0ArgStBK0ErgSxBK0EsQSrBIUAiQCwBIUAsASvBK8EsASzBK8EswSyBLMEtgS1BLMEtQSyBLEEtASpBLEEqQSrBLYEvgDDALYEwwC1BLQEwgDIALQEyACpBL4AtgS3BL4AtwS7ALYEswS4BLYEuAS3BLcEuAS6BLcEugS5BLsAtwS5BLsAuQS5ALkEugS8BLkEvAS7BLkAuQS7BLkAuwS1ALsEvAS+BLsEvgS9BLUAuwS9BLUAvQSzAL0EvgTABL0EwAS/BLMAvQS/BLMAvwSvAL8EwASmAL8EpgCqAK8AvwSqAK8AqgCsAMAEwQSjAMAEowCmAL4EwgTBBL4EwQTABMIElwCdAMIEnQDBBMEEnQChAMEEoQCjAMMElQCXAMMElwDCBLwEwwTCBLwEwgS+BMQEjwCVAMQElQDDBLoExATDBLoEwwS8BMUEjQCPAMUEjwDEBLgExQTEBLgExAS6BLAEiQCNALAEjQDFBLMEsATFBLMExQS4BMsExgS0AcsEtAHIBMgEtAGxAcgEsQHKBM0EywTIBM0EyATJBMkEyATKBMkEygTkBM8E0QTHBM8ExwTMBNAEzwTMBNAEzATOBNQE0gTPBNQEzwTQBNIE0wTRBNIE0QTPBNIE1QTXBNIE1wTTBNQE1gTVBNQE1QTSBNUE1gTZBNUE2QTYBNcE1QTYBNcE2ATaBNkE3gTcBNkE3ATYBNgE3ASpAdgEqQHaBNsE3QTgBNsE4ATfBKgB2wTfBKgB3wSsAeAE4gThBOAE4QTfBN8E4QSvAd8ErwGsAeEEygSxAeEEsQGvAeIE5ATKBOIEygThBOAEZQXjBOAE4wTiBOQE4gTjBOQE4wTlBOgEyQTkBOgE5ATlBOsE6ATlBOsE5QTmBOMEXgXmBOME5gTlBOkEzQTJBOkEyQToBO0E6QToBO0E6ATrBPEE7wTsBPEE7ATwBPAE7ATnBPAE5wTyBPEE8ATzBPEE8wT2BPAE8gT0BPAE9ATzBPME9AQYBfMEGAX1BPYE8wT1BPYE9QT3BPkE8QT2BPkE9gT4BPgE9gT3BPgE9wT6BP0E+QT4BP0E+AT7BPsE+AT6BPsE+gT8BP4E+wT8BP4E/AQABf8E/QT7BP8E+wT+BAIF/wT+BAIF/gQBBQEF/gQABQEFAAUDBQUFAgUBBQUFAQUEBQQFAQUDBQQFAwUGBQUFBAUHBQUFBwUKBQQFBgUIBQQFCAUHBQcFCAUfBQcFHwUJBQoFBwUJBQoFCQULBQwFCgULBQwFCwUOBQ0FBQUKBQ0FCgUMBRAFDQUMBRAFDAUPBQ8FDAUOBQ8FDgURBRAFDwUSBRAFEgXyBBIFDwURBRIFEQUTBfQE8gQSBfQEEgUUBRQFEgUTBRQFEwUVBRgF9AQUBRgFFAUWBRYFFAUVBRYFFQUXBRkFFgUXBRkFFwUbBRoFGAUWBRoFFgUZBR0FGgUZBR0FGQUcBRwFGQUbBRwFGwUeBQYFHQUcBQYFHAUIBQgFHAUeBQgFHgUfBR4FGwUhBR4FIQUgBR8FHgUgBR8FIAUjBSAFIQUzBSAFMwUiBSMFIAUiBSMFIgUkBSUFIwUkBSUFJAUmBQkFHwUjBQkFIwUlBQsFCQUlBQsFJQUnBScFJQUmBScFJgUoBSkFJwUoBSkFKAUqBQ4FCwUnBQ4FJwUpBREFDgUpBREFKQUrBSsFKQUqBSsFKgUsBREFKwUtBREFLQUTBS0FKwUsBS0FLAUuBRUFEwUtBRUFLQUvBS8FLQUuBS8FLgUwBRcFFQUvBRcFLwUxBTEFLwUwBTEFMAUyBSEFMQUyBSEFMgUzBRcFMQUhBRcFIQUbBTAFNAUzBTAFMwUyBTQFNQUiBTQFIgUzBSwFNAUwBSwFMAUuBSwFKgU1BSwFNQU0BSoFKAUmBSoFJgU1BTUFJgUkBTUFJAUiBR0FBgU3BR0FNwU2BRoFHQU2BRoFNgU5BTYFNwVJBTYFSQU4BTkFNgU4BTkFOAU6BRoFOQU7BRoFOwUYBTsFOQU6BTsFOgU8BT0FOwU8BT0FPAU+BfUEGAU7BfUEOwU9BfcE9QQ9BfcEPQU/BT8FPQU+BT8FPgVABfoE9wQ/BfoEPwVBBUEFPwVABUEFQAVCBfwE+gRBBfwEQQVDBUMFQQVCBUMFQgVEBUUFQwVEBUUFRAVGBfwEQwVFBfwERQUABQMFAAVFBQMFRQVHBUcFRQVGBUcFRgVIBQYFAwVHBQYFRwU3BTcFRwVIBTcFSAVJBUgFRgVLBUgFSwVKBUkFSAVKBUkFSgVNBUoFSwVdBUoFXQVMBU0FSgVMBU0FTAVOBU8FTQVOBU8FTgVQBTgFSQVNBTgFTQVPBToFOAVPBToFTwVRBVEFTwVQBVEFUAVSBToFUQVTBToFUwU8BVMFUQVSBVMFUgVUBVUFUwVUBVUFVAVWBT4FPAVTBT4FUwVVBUAFPgVVBUAFVQVXBVcFVQVWBVcFVgVYBUIFQAVXBUIFVwVZBVkFVwVYBVkFWAVaBUQFQgVZBUQFWQVbBVsFWQVaBVsFWgVcBUYFRAVbBUYFWwVLBUsFWwVcBUsFXAVdBV0FXAVaBV0FWgVMBUwFWgVYBUwFWAVOBU4FWAVWBU4FVgVQBVAFVgVUBVAFVAVSBecEXwUQBecEEAXyBGEFDQUQBWEFEAVfBWUFYAVeBWUFXgXjBGQFBQUNBWQFDQVhBWYFYgVgBWYFYAVlBd0EZgVlBd0EZQXgBGgFagVjBWgFYwVnBdkEaAVnBdkEZwXeBNYEaQVoBdYEaAXZBGkFbAVqBWkFagVoBW0F/wQCBW0FAgVrBWsFAgUFBWsFBQVkBXAFbgVsBXAFbAVpBW0FbwX9BG0F/QT/BP0EbwVyBf0EcgX5BG4FcAVzBW4FcwVxBXEFcwXqBHEF6gTuBPkEcgXvBPkE7wTxBHAF1ATQBHAF0ARzBXMF0ATOBHMFzgTqBNYE1ARwBdYEcAVpBdcE2gR0BdcEdAV2BakBdQV0BakBdAXaBHcFdgV0BXcFdAV1BXgFdgV3BXgFdwV5BdME1wR2BdMEdgV4BdMEeAV6BdMEegXRBHkFewV6BXkFegV4BccE0QR6BccEegV7BccEewV8BccEfAWZAXkFfQV8BXkFfAV7BZ0BmQF8BZ0BfAV9BXcFfgV9BXcFfQV5BX0FfgWfAX0FnwGdAZ8BfgV/BZ8BfwWjAXcFdQV/BXcFfwV+BakBowF/BakBfwV1BVsBFQARAFsBEQBdAYAFGAAVAIAFFQBbAVcBUQFOAVcBTgGABRoAGACABRoAgAVOAUIBPwGDBUIBgwWBBSsAQgGBBSsAgQWCBYEFgwWEBYEFhAWCBT8BPQGFBT8BhQWDBYMFhQWGBYMFhgWEBYUFhwWJBYUFiQWGBT0BOQGHBT0BhwWFBYgFiwWMBYgFjAWKBToBMwGLBToBiwWIBYsFMwExAYsFMQGNBYwFiwWNBYwFjQWOBZAFjgWNBZAFjQWPBY8FjQUxAY8FMQEtASoBkQWPBSoBjwUtAZEFkgWQBZEFkAWPBZMFkQUqAZMFKgEoAZQFkgWRBZQFkQWTBZMFlQWWBZMFlgWUBSgBJQGVBSgBlQWTBZcFmAWWBZcFlgWVBSIBlwWVBSIBlQUlAZoFmAWXBZoFlwWZBZkFlwUiAZkFIgEfAZwFmgWZBZwFmQWbBZsFmQUfAZsFHwEbAZsFGwEYAZsFGAGdBZwFmwWdBZwFnQWeBZ0FGAEVAZ0FFQGfBZ4FnQWfBZ4FnwWgBZ8FFQETAZ8FEwGhBaAFnwWhBaAFoQWiBaEFpAWmBaEFpgWiBRMBDgGkBRMBpAWhBacFowUNAacFDQEKAagFpQWjBagFowWnBaoFqAWnBaoFpwWpBakFpwUKAakFCgEGAQMBqwWpBQMBqQUGAasFrAWqBasFqgWpBasFAwEBAasFAQGtBawFqwWtBawFrQWuBa0FrwWwBa0FsAWuBQEB/QCvBQEBrwWtBa8F/QD7AK8F+wCxBbAFrwWxBbAFsQWyBbEFswW0BbEFtAWyBfsA9wCzBfsAswWxBbMF9wD1ALMF9QC1BbQFswW1BbQFtQW2BbUFtwW4BbUFuAW2BfUA8QC3BfUAtwW1BbcF8QDuALcF7gC5BbgFtwW5BbgFuQW6BbkF7gDsALkF7AC7BboFuQW7BboFuwW8BbsFvgXABbsFwAW8BewA6QC+BewAvgW7Bb0FwQXCBb0FwgW/BegA4gDBBegAwQW9BeIA4ADDBeIAwwXBBcEFwwXEBcEFxAXCBcMFxQXGBcMFxgXEBeAA3ADFBeAAxQXDBdwA2gDHBdwAxwXFBcUFxwXIBcUFyAXGBccFyQXKBccFygXIBdoA1gDJBdoAyQXHBdYA1ADLBdYAywXJBckFywXMBckFzAXKBcsFzQXOBcsFzgXMBdQA0ADNBdQAzQXLBdAAzgDPBdAAzwXNBc0FzwXQBc0F0AXOBdIF0AXPBdIFzwXRBdEFzwXOANEFzgDLANQF0gXRBdQF0QXTBdMF0QXLANMFywDHANUF0wXHANUFxwDEANcF1AXTBdcF0wXVBdkF2gXYBdkF2AXWBb8A2QXWBb8A1gXFANkF2wXcBdkF3AXaBb8AvADbBb8A2wXZBdsF3QXeBdsF3gXcBbwAuADdBbwA3QXbBd0FuAC2AN0FtgDfBd4F3QXfBd4F3wXgBd8F4QXiBd8F4gXgBbYAsgDhBbYA4QXfBeEFsgCwAOEFsADjBeIF4QXjBeIF4wXkBeMF5QXmBeMF5gXkBbAArQDlBbAA5QXjBeUF5wXoBeUF6AXmBa0AqQDnBa0A5wXlBakApwDpBakA6QXnBecF6QXqBecF6gXoBewF6gXpBewF6QXrBesF6QWnAOsFpwCkAO4F7AXrBe4F6wXtBe0F6wWkAO0FpACgAO8F7QWgAO8FoACcAPAF7gXtBfAF7QXvBZgA8QXvBZgA7wWcAPEF8gXwBfEF8AXvBfQF8gXxBfQF8QXzBfMF8QWYAPMFmACTAJAA9QXzBZAA8wWTAPUF9gX0BfUF9AXzBfgF9gX1BfgF9QX3BfcF9QWQAPcFkACLAPkF9wWLAPkFiwCHAPoF+AX3BfoF9wX5BfkFhwCCAPkFggD8BfoF+QX8BfoF/AX+BYEAfgD/BYEA/wX7BfsF/wUABvsFAAb9Bf8FAQYCBv8FAgYABn4AegABBn4AAQb/BXoAeAADBnoAAwYBBgEGAwYEBgEGBAYCBgYGBAYDBgYGAwYFBgUGAwZ4AAUGeAB1AAcGCQYGBgcGBgYFBnEABwYFBnEABQZ1AAwGCgYIBgwGCAYLBgsGCAZyAAsGcgBsAA0GDgYMBg0GDAYLBmkADQYLBmkACwZsABAGDgYNBhAGDQYPBg8GDQZpAA8GaQBmABIGEAYPBhIGDwYRBhEGDwZmABEGZgBiABMGEQZiABMGYgBgABQGEgYRBhQGEQYTBhYGFAYTBhYGEwYVBhUGEwZgABUGYABcABcGFQZcABcGXABaABgGFgYVBhgGFQYXBhkGGgYYBhkGGAYXBlcAGQYXBlcAFwZaABwGGgYZBhwGGQYbBhsGGQZXABsGVwBUAB4GHAYbBh4GGwYdBh0GGwZUAB0GVABQAB8GHQZQAB8GUABOACAGHgYdBiAGHQYfBiIGIAYfBiIGHwYhBiEGHwZOACEGTgBKACMGIQZKACMGSgBIACQGIgYhBiQGIQYjBiUGJwYkBiUGJAYjBkQAJQYjBkQAIwZIACoGKAYmBioGJgYpBikGJgZFACkGRQBBAD8AKwYpBj8AKQZBACsGLAYqBisGKgYpBi0GLgYsBi0GLAYrBj0ALQYrBj0AKwY/ADAGLgYtBjAGLQYvBi8GLQY9AC8GPQA7ADEGLwY7ADEGOwA5ADIGMAYvBjIGLwYxBjcAMwYxBjcAMQY5ADMGNAYyBjMGMgYxBjgGNAYzBjgGMwY2BjYGMwY3ADYGNwA1ADkGNQY0ADkGNAAxADoGNwY1BjoGNQY5BjsGOQYxADsGMQAvADwGOgY5BjwGOQY7Bj4GPAY7Bj4GOwY9Bj0GOwYvAD0GLwAtACsAPgY9BisAPQYtACsAQAY/BisAPwY+Bj8GQQY8Bj8GPAY+BkAGQgZBBkAGQQY/BkIGRAZDBkIGQwZBBkEGQwY6BkEGOgY8BkMGRQY3BkMGNwY6BkQGRwZFBkQGRQZDBkYGSQY0BkYGNAY4BkgGSgZJBkgGSQZGBkkGSgZMBkkGTAZLBjQGSQZLBjQGSwYyBksGTQYwBksGMAYyBkwGTgZNBkwGTQZLBk0GTwYuBk0GLgYwBk4GUAZPBk4GTwZNBlEGTwZQBlEGUAZSBiwGLgZPBiwGTwZRBlEGUgZUBlEGVAZTBiwGUQZTBiwGUwYqBlMGVgYoBlMGKAYqBlQGWAZWBlQGVgZTBlUGWQYkBlUGJAYnBlcGWgZZBlcGWQZVBlkGWwYiBlkGIgYkBloGXAZbBloGWwZZBlsGXAZeBlsGXgZdBiIGWwZdBiIGXQYgBl0GXwYeBl0GHgYgBl4GYAZfBl4GXwZdBl8GYAZiBl8GYgZhBh4GXwZhBh4GYQYcBmEGYwYaBmEGGgYcBmIGZAZjBmIGYwZhBhgGGgZjBhgGYwZlBmUGYwZkBmUGZAZmBhYGGAZlBhYGZQZnBmcGZQZmBmcGZgZoBmkGZwZoBmkGaAZqBhQGFgZnBhQGZwZpBhIGFAZpBhIGaQZrBmsGaQZqBmsGagZsBm0GawZsBm0GbAZuBhAGEgZrBhAGawZtBm8GDgYQBm8GEAZtBnAGbwZtBnAGbQZuBpsNjg1xBpsNcQZzBo4Njw10Bo4NdAZxBnMGcQZyBnMGcgaSBnEGdAZ1BnEGdQZyBngGdQZ0BngGdAZ2BpANjw1wBpANcAZ3BpINkA13BpINdwZ8Bn4GeAZ2Bn4GdgZ6BpMNkQ17BpMNewaABoEGfQZ5BoEGeQZ/BpQNkw2ABpQNgAaDBoQGgQZ/BoQGfwaCBpQNlQ2FBpQNhQaCBoIGhQaHBoIGhwaEBpUNlg2IBpUNiAaFBoUGiAaJBoUGiQaHBpcNlg0EBpcNBAYGBosGiQaIBosGiAaKBowGjgaLBowGiwaKBpgNjAaKBpgNigaXDZEGjwaNBpEGjQaQBpoNmQ0KBpoNCgYMBnMGkgaRBnMGkQaQBpsNcwaQBpsNkAaaDZIGlQaTBpIGkwaRBo8GkQaTBo8GkwaUBpYGlAaTBpYGkwaVBpkGlgaVBpkGlQaXBpUGkgZyBpUGcgaXBpoGmQaXBpoGlwaYBpcGcgZ1BpcGdQaYBp0GlgaZBp0GmQabBpsGmQaaBpsGmgacBp4GmwacBp4GnAafBqAGnQabBqAGmwaeBqEGngafBqEGnwakBqMGoAaeBqMGngahBqIGoQakBqIGpAalBsAGowahBsAGoQaiBqcGpQakBqcGpAamBqYGpAafBqYGnwaoBqwGpwamBqwGpgaqBqoGpgaoBqoGqAZ+Bq4GqwapBq4GqQatBq0GqQZ9Bq0GfQaBBrAGrgatBrAGrQavBq8GrQaBBq8GgQaEBq8GhAaHBq8GhwaxBrAGrwaxBrAGsQayBrEGhwaJBrEGiQazBrIGsQazBrIGswa0BrQGswa1BrQGtQa2BrMGiQaLBrMGiwa1BrYGtQa3BrYGtwa5BrUGiwaOBrUGjga3BroGuAa7BroGuwa8BrgGjwa9BrgGvQa7BrwGuwajBrwGowbABrsGvQagBrsGoAajBo8GlAa+Bo8Gvga9BqAGvQa+BqAGvgadBpYGnQa+BpYGvgaUBr8GvAbABr8GwAbBBiQHuga8BiQHvAa/BsEGwAaiBsEGogbDBsIGwQbDBsIGwwbFBi0HvwbBBi0HwQbCBsMGogalBsMGpQbEBsUGwwbEBsUGxAbGBscGyQbCBscGwgbFBssGxwbFBssGxQbGBs0GzwbKBs0GygbIBs4GzQbIBs4GyAbMBtIG0AbNBtIGzQbOBtAG0QbPBtAGzwbNBtUG0QbQBtUG0AbTBtMG0AbSBtMG0gbUBtQG2AbWBtQG1gbTBtMG1gbXBtMG1wbVBtYG2QbbBtYG2wbXBtgG2gbZBtgG2QbWBtoG3QbcBtoG3AbZBtkG3AbeBtkG3gbbBtwG3QbhBtwG4QbfBt4G3AbfBt4G3wbgBt8G4gbjBt8G4wbgBuEG5AbiBuEG4gbfBuIG5QbnBuIG5wbjBuQG6QblBuQG5QbiBusG7AboBusG6AbmBu0G6wbmBu0G5gbqBu8G7AbrBu8G6wbuBu4G6wbtBu4G7QbwBvMG7wbuBvMG7gbxBvEG7gbwBvEG8AbyBvQG8QbyBvQG8gb2BvUG8wbxBvUG8Qb0BvQG9wb5BvQG+Qb1BvYG+Ab3BvYG9wb0BvcG+Ab8BvcG/Ab6BvkG9wb6BvkG+gb7BvsG+gb9BvsG/Qb+BvoG/Ab/BvoG/wb9Bv0GAAcBB/0GAQf+Bv8GAgcAB/8GAAf9BgAHAwcHBwAHBwcBBwIHBQcDBwIHAwcABwgHAwcFBwgHBQcKBwwHBwcDBwwHAwcIBxIHDgcIBxIHCAcKBw4HEAcMBw4HDAcIBxUHDwcNBxUHDQcTBxMHDQcRBxMHEQcUBxsHFwcTBxsHEwcUBxcHGQcVBxcHFQcTBxYHHAcdBxYHHQcYBxoHHgccBxoHHAcWBxwHHwchBxwHIQcdBx4HIAcfBx4HHwccBx8HIAewBh8HsAayBiEHHweyBiEHsga0Bh0HIQciBx0HIgclByEHtAa2BiEHtgYiByIHtga5BiIHuQYjByUHIgcjByUHIwcoByYHJQcoByYHKAcqBxgHHQclBxgHJQcmBykHJAe/BikHvwYtBysHKQctBysHLQcuBy4HLQfCBi4HwgbJBjIHLAcvBzIHLwcwBzAHLwfKBjAHygbPBjEHMAfPBjEHzwbRBjMHMgcwBzMHMAcxBzUHNAcyBzUHMgczBzQHNgcsBzQHLAcyBzkHNwc0BzkHNAc1BzcHBgc2BzcHNgc0BzwHOwc4BzwHOAc6BwEHBwc4BwEHOAc7B/kG+wY7B/kGOwc8B/4GAQc7B/4GOwf7Bj0HPAc6Bz0HOgc/B/UG+QY8B/UGPAc9Bz8HQwdABz8HQAc9B0AH8wb1BkAH9QY9B0MHRQdBB0MHQQdAB+8G8wZAB+8GQAdBB0QHQgdGB0QHRgdHB0YHQgc+B0YHPgdJB0gHTwdHB0gHRwdGB0oHSAdGB0oHRgdJB0kHNQczB0kHMwdKBz4HOQc1Bz4HNQdJB0oHMwcxB0oHMQdLB0wHSAdKB0wHSgdLB0sHMQfRBksH0QbVBtUG1wZMB9UGTAdLB9cG2wZNB9cGTQdMB08HSAdMB08HTAdNB9sG3gZOB9sGTgdNB1AHTwdNB1AHTQdOB08HUAdRB08HUQdHB04HUwdSB04HUgdQB1AHUgdUB1AHVAdRB1MHVwdUB1MHVAdSB1EHVAdVB1EHVQdbB1cHWAdVB1cHVQdUB+AG4wZXB+AGVwdTB+MG5wZYB+MGWAdXB1wHVgdZB1wHWQdaB1oHWQfoBloH6AbsBuwG7wZBB+wGQQdaB0UHXAdaB0UHWgdBB1sHRAdHB1sHRwdRB94G4AZTB94GUwdOBzYHBgcLBzYHCwddBywHNgddBywHXQcnBycHXQcVBycHFQcZB10HCwcPB10HDwcVBx4HYQdeBx4HXgcgByAHXgeuBiAHrgawBl4HXwerBl4HqwauBmEHYgdfB2EHXwdeBxoHZAdhBxoHYQceB2QHZgdiB2QHYgdhB2UHaQdqB2UHagdoBxsHFAdpBxsHaQdlB2kHCQcEB2kHBAdqBxQHEQcJBxQHCQdpB28HaAdqB28HagdrB2sHagcEB2sHBAdtB2wHawdtB2wHbQeEB3AHbwdrB3AHawdsB3IHcQdvB3IHbwdwB3EHdAdoB3EHaAdvB9IGzgZxB9IGcQdyB84GzAZ0B84GdAdxB3MHywbGBnMHxgZ1B2cHcwd1B2cHdQdjB2MHdQd2B2MHdgdgB3UHxgbEBnUHxAZ2B2AHdgenBmAHpwasBnYHxAalBnYHpQanBncHcgdwB3cHcAd4B9QG0gZyB9QGcgd3B3gHewd5B3gHeQd3B3kH2AbUBnkH1AZ3B3sHfAd6B3sHegd5B9oG2AZ5B9oGeQd6B3wHewd9B3wHfQd+B30Hewd4B30HeAeBB38Hiwd+B38Hfgd9B4IHfwd9B4IHfQeBB4EHbAeEB4EHhAeCB3gHcAdsB3gHbAeBB4MHhQeHB4MHhweGB4gHgAeDB4gHgweGB4YHhwf4BoYH+Ab2BvYG8gaIB/YGiAeGB/IG8AaJB/IGiQeIB4wHgAeIB4wHiAeJB/AG7QaKB/AGigeJB44HjAeJB44HiQeKB4sHjQePB4sHjwd+B4oHkweRB4oHkQeOB40HkAeUB40HlAePB5QHkAeSB5QHkgeWB48HlAeVB48HlQeZB5UHlAeWB5UHlgeXB5cHlgfkBpcH5AbhBpYHkgfpBpYH6QbkBpgHlwfhBpgH4QbdBpkHlQeXB5kHlweYB90G2gZ6B90GegeYB3wHmQeYB3wHmAd6B5kHfAd+B5kHfgePB+0G6gaTB+0GkweKB4cHmgf8BocH/Ab4BoUHbgeaB4UHmgeHB5oHAgf/BpoH/wb8Bm4HBQcCB24HAgeaB58GnAabB58GmweoBn4GqAabB34GmwecB5oGnAebB5oGmwecBpoGmAadB5oGnQecB34GnAedB34GnQd4BnUGeAadB3UGnQeYBp8HBAaGBp8HhgaeB54HhgaDBp4HgwagB6MHnweeB6MHngehB6EHngegB6EHoAeiB6QHoQeiB6QHogemB6UHowehB6UHoQekB9AFpQekB9AFpAenB6cHpAemB6cHpgeoB6kHpweoB6kHqAeqB84F0AWnB84FpwepB6sHzAXOBasHzgWpB6wHqwepB6wHqQeqB6sHrQfKBasHygXMBawHrgetB6wHrQerB64HsAevB64HrwetB60HrwfIBa0HyAXKBa8HsQfGBa8HxgXIBbAHsgexB7AHsQevB7IHtAezB7IHswexB7EHswfEBbEHxAXGBbMHtQfCBbMHwgXEBbQHtge1B7QHtQezB7YHuQe3B7YHtwe1B7UHtwe/BbUHvwXCBbwFwAW4B7wFuAe7B7sHuAe6B7sHuge8B7oFvAW7B7oFuwe9B70Huwe8B70HvAe+B8AHvwe9B8AHvQe+B78HuAW6Bb8HugW9B78HwAfCB78HwgfBB7gFvwfBB7gFwQe2BcEHwwe0BcEHtAW2BcIHxAfDB8IHwwfBB8UHwwfEB8UHxAfGB7IFtAXDB7IFwwfFB8cHsAWyBccHsgXFB8gHxwfFB8gHxQfGB64FsAXHB64FxwfJB8kHxwfIB8kHyAfKB8sHrAWuBcsHrgXJB8wHywfJB8wHyQfKB80HqgWsBc0HrAXLB84HzQfLB84HywfMB80HzgfRB80H0QfPB6oFzQfPB6oFzwfQB88H0gfUB88H1AfQB9EH0wfSB9EH0gfPB9UH0gfTB9UH0wfXB9YH1AfSB9YH0gfVB9gH2gfWB9gH1gfVB9wH2AfVB9wH1QfXB9kH3gffB9kH3wfbB90H4AfeB90H3gfZB94H4QfjB94H4wffB+AH5gfhB+AH4QfeB+IH5wfoB+IH6AfkB+UH6QfnB+UH5wfiB+cH6gfrB+cH6wfoB+kH7AfqB+kH6gfnB+8H6wfqB+8H6gftB+0H6gfsB+0H7AfuB/IH8AftB/IH7QfuB/AH8QfvB/AH7wftB/UH8QfwB/UH8AfzB/MH8AfyB/MH8gf0B/sH9wfzB/sH8wf0B/cH+Qf1B/cH9QfzB/4H+Af2B/4H9gf8B/wH9gf6B/wH+gf9BwAI/wf8BwAI/Af9B/8HpgX+B/8H/gf8B6IFpgX/B6IF/wcBCAEI/wcACAEIAAgCCAMIoAWiBQMIogUBCAQIAwgBCAQIAQgCCAMIBQieBQMIngWgBQQIBggFCAQIBQgDCAUIBwicBQUInAWeBQYICAgHCAYIBwgFCAoInAUHCAoIBwgJCAkIBwgICAkICAgLCA4ICggJCA4ICQgMCAwICQgLCAwICwgNCAwIDQgUCAwIFAgQCA4IDAgQCA4IEAgSCA8IFQgXCA8IFwgRCBMIFggVCBMIFQgPCBUIFggaCBUIGggYCBcIFQgYCBcIGAgZCBgIGwgdCBgIHQgZCBoIHAgbCBoIGwgYCBsIHAggCBsIIAgeCB0IGwgeCB0IHggfCCEIHwgeCCEIHgggCBwIIwgiCBwIIgggCCEIIAgiCCEIIggkCCUIJAgiCCUIIggjCCEIJAgmCCEIJggoCCUIJwgmCCUIJggkCCkIKAgmCCkIJggnCCEIKAgqCCEIKggsCCkIKwgqCCkIKggoCC0ILAgqCC0IKggrCC0ILwguCC0ILggsCCEILAguCCEILggwCCEIMAgyCC8IMQgwCC8IMAguCDMIMggwCDMIMAgxCCEIMgg0CDMINQg0CDMINAgyCCEINAg2CCEINggfCDUINwg2CDUINgg0CB0IHwg2CB0INgg3CDgINwg1CDgINQg6CBkIHQg3CBkINwg4CDkIOAg6CDkIOgg7CBcIGQg4CBcIOAg5CD0IOwg6CD0IOgg8CDoINQgzCDoIMwg8CDwIMwgxCDwIMQg+CD0IPAg+CD0IPgg/CD8IPghACD8IQAhBCD4IMQgvCD4ILwhACEAILwgtCEAILQhCCEMIQQhACEMIQAhCCEMIQghECEMIRAhFCEIILQgrCEIIKwhECEUIRAhGCEUIRghHCEQIKwgpCEQIKQhGCEYIKQgnCEYIJwhICEcIRghICEcISAhJCEkISAhKCEkISghLCEgIJwglCEgIJQhKCEoIJQgjCEoIIwhMCEsISghMCEsITAhNCE0ITAgaCE0IGggWCEwIIwgcCEwIHAgaCE4ITQgWCE4IFggTCFEISwhNCFEITQhOCFAITwgUCFAIFAgNCFMIUghPCFMITwhQCFQISQhLCFQISwhRCFYIVQhSCFYIUghTCFkIWAhVCFkIVQhWCFcIRwhJCFcISQhUCFoIRQhHCFoIRwhXCFwIWwhYCFwIWAhZCF8IXghbCF8IWwhcCF0IQwhFCF0IRQhaCF8IYghhCF8IYQheCGAIQQhDCGAIQwhdCGMIPwhBCGMIQQhgCGUIZAhhCGUIYQhiCGgIZwhkCGgIZAhlCGYIPQg/CGYIPwhjCGkIOwg9CGkIPQhmCGgIawhqCGgIaghnCG4IbQhqCG4IaghrCGwIOQg7CGwIOwhpCA4IEghtCA4IbQhuCBEIFwg5CBEIOQhsCHAIbwhuCHAIbghrCG8ICggOCG8IDghuCJgFmgVvCJgFbwhwCJoFnAUKCJoFCghvCGgIcQhwCGgIcAhrCHEIcgiYBXEImAVwCHMIcQhoCHMIaAhlCHUIcghxCHUIcQhzCHQIcwhlCHQIZQhiCHYIdQhzCHYIcwh0CHgIcgh1CHgIdQh3CHUIdgh5CHUIeQh3CHcIegh7CHcIewh4CHkIfQh6CHkIegh3CHoIfAhcCnoIXAp7CH0Ifgh8CH0IfAh6CH8IgAh+CH8Ifgh9CHkIgQh/CHkIfwh9CIIIhQiACIIIgAh/CIQIggh/CIQIfwiBCIIIhAiJCIIIiQiGCIcIhQiCCIcIggiGCIcIhgiKCIcIigiLCIYIiQiPCIYIjwiKCIsIigiNCIsIjQjrCIoIjwiTCIoIkwiNCJgIkgiOCJgIjgiUCJQIjgiICJQIiAiWCJUIlAiWCJUIlggOCZoImAiUCJoIlAiVCJwImwiXCJwIlwiZCJsIngiRCJsIkQiXCKAInwibCKAImwicCJ8IogieCJ8IngibCKQIowifCKQInwigCKMIpgiiCKMIogifCKgIpwijCKgIowikCKcIqgimCKcIpgijCKwIqwinCKwIpwioCKsIrgiqCKsIqginCLAIrwirCLAIqwisCK8IsgiuCK8IrgirCLcIswivCLcIrwiwCLMItgiyCLMIsgivCLgItAizCLgIswi3CLQIowm2CLQItgizCLsIuQi3CLsItwiwCLoIuAi3CLoItwi5CLkIvAi+CLkIvgi6CLsIvQi8CLsIvAi5CL0IwgjACL0IwAi8CL4IvAjACL4IwAjECMEIxgjFCMEIxQi/CMMIvwjFCMMIxQjHCMYIyQjICMYIyAjFCMcIxQjICMcIyAjKCM0IywjICM0IyAjJCMoIyAjLCMoIywjMCM8IzAjLCM8IywjOCM4IywjNCM4IzQjQCKIJzwjOCKIJzgi1CLUIzgjQCLUI0AixCLEI0AjRCLEI0QitCNAIzQjTCNAI0wjRCK0I0QjSCK0I0gipCNEI0wjUCNEI1AjSCNQI0wjVCNQI1QjWCNMIzQjJCNMIyQjVCNcI1QjJCNcIyQjYCNkI1gjVCNkI1QjXCGIG1wjYCGIG2AhkBmAG2QjXCGAG1whiBl4G2gjZCF4G2QhgBtoI2wjWCNoI1gjZCFwG3AjaCFwG2gheBtwI3QjbCNwI2wjaCFoG3gjcCFoG3AhcBt4I3wjdCN4I3QjcCFcG4AjeCFcG3ghaBuAI4gjfCOAI3wjeCOQI4QhYBuQIWAZUBuYI4wjhCOYI4QjkCOUI5AhUBuUIVAZSBucI5gjkCOcI5AjlCIsI6wjjCIsI4wjmCOcIhwiLCOcIiwjmCOkI6AjnCOkI5wjlCOcI6AiFCOcIhQiHCHwIfgjoCHwI6AjpCOgIfgiACOgIgAiFCFAG6QjlCFAG5QhSBlAGXAp8CFAGfAjpCOII6gjsCOII7AjfCO4I7AjqCO4I6giMCO8I7QjsCO8I7AjuCOwI7QjdCOwI3QjfCJ0I7giMCJ0IjAiQCKEI7wjuCKEI7gidCKUI8AjvCKUI7wihCPAI8QjtCPAI7QjvCKkI0gjwCKkI8AilCNII1AjxCNII8QjwCO0I8QjbCO0I2wjdCPEI1AjWCPEI1gjbCPII8whkBvIIZAbYCMYI8gjYCMYI2AjJCMEI9AjyCMEI8gjGCPQI9gjzCPQI8wjyCL0I+Aj1CL0I9QjCCPgI+Qj3CPgI9wj1CLsI+gj4CLsI+Ai9CPoIrAf5CPoI+Qj4CPsIrgesB/sIrAf6CPwI+wj6CPwI+gi7CP4I/Qj7CP4I+wj8CP0IsAeuB/0Irgf7CAAJ/wj9CAAJ/Qj+CP8IsgewB/8IsAf9CAIJAQn/CAIJ/wgACQEJtAeyBwEJsgf/CAQJAwkBCQQJAQkCCQMJtge0BwMJtAcBCQcJBQkDCQcJAwkECQUJuQe2BwUJtgcDCboHBgkJCboHCQm8BwYJCAkLCQYJCwkJCbwHCQkKCbwHCgm+BwkJCwkMCQkJDAkKCQwJCwmVCAwJlQgOCQsJCAmaCAsJmgiVCA8JDQkMCQ8JDAkOCQoJDAkNCQoJDQkXCQ8JDgmWCA8JlggQCRAJEwkRCRAJEQkPCQ0JDwkRCQ0JEQkUCREJEgkVCREJFQkUCRMJIwkSCRMJEgkRCRQJFQkYCRQJGAkWCQ0JFAkWCQ0JFgkXCRcJFgnCBxcJwgfABxYJGAnEBxYJxAfCB8QHGAkZCcQHGQnGBxgJFQkbCRgJGwkZCcYHGQkaCcYHGgnIBxkJGwkcCRkJHAkaCRwJGwkdCRwJHQkeCRsJFQkSCRsJEgkdCR0JHwkgCR0JIAkeCRIJIwkfCRIJHwkdCR8JIQk3CR8JNwkgCSMJJAkhCSMJIQkfCScJJAkjCScJIwkTCSYJNAkiCSYJIgklCSkJJgklCSkJJQkoCYMIJwkTCYMIEwkQCYQIgQgpCYQIKQkoCYEIKwkqCYEIKgkpCSoJLAkmCSoJJgkpCSoJKwkvCSoJLwktCSwJKgktCSwJLQkuCS4JLQlcCC4JXAhZCC0JLwlfCC0JXwhcCF8ILwl0CF8IdAhiCC8JKwl2CC8Jdgh0CDAJLglZCDAJWQhWCDIJLAkuCTIJLgkwCTEJMAlWCDEJVghTCDMJMgkwCTMJMAkxCSYJLAkyCSYJMgk0CTIJMwk1CTIJNQk0CTQJNQk4CTQJOAkiCTUJMwmMCTUJjAk2CTUJNgk5CTUJOQk4CYsJOgk4CYsJOAk5CToJQQk9CToJPQk4CTgJPQlACTgJQAk8CTcJOwmECTcJhAkgCUEJQgk+CUEJPgk9CT0JPgljCT0JYwlACUUJRAlBCUUJQQk6CUQJRglCCUQJQglBCUkJSAlECUkJRAlFCUgJSglGCUgJRglECQAI/QdICQAISAlJCf0H+gdKCf0HSglICfsH9AdMCfsHTAlLCUsJTAlNCUsJTQlHCUwJTglPCUwJTwlNCfQH8gdOCfQHTglMCfIH7gdQCfIHUAlOCU4JUAlRCU4JUQlPCVAJUglTCVAJUwlRCe4H7AdSCe4HUglQCVQJUgnsB1QJ7AfpB1cJUwlSCVcJUglUCVUJVAnpB1UJ6QflB1kJVwlUCVkJVAlVCVoJXQlTCVoJUwlXCVwJWglXCVwJVwlZCWAJXglaCWAJWglcCV4JYQldCV4JXQlaCWQJYgleCWQJXglgCWIJZQlhCWIJYQleCT8JZgliCT8JYglkCWYJZwllCWYJZQliCWgJaQlnCWgJZwlmCUMJaAlmCUMJZgk/CUcJTQloCUcJaAlDCU0JTwlpCU0JaQloCWkJagllCWkJZQlnCU8JUQlqCU8JaglpCWoJXQlhCWoJYQllCVEJUwldCVEJXQlqCUAJYwlfCUAJXwlrCWsJXwlbCWsJWwluCTwJQAlrCTwJawltCW0JawluCW0JbglwCXAJbglxCXAJcQlzCW4JWwlYCW4JWAlxCXEJWAlWCXEJVgl0CXMJcQl0CXMJdAl2CXYJdAngB3YJ4AfdB3QJVgnmB3QJ5gfgB9cHdwl1CdcHdQncB3cJeAlyCXcJcgl1CdMHeQl3CdMHdwnXB3kJegl4CXkJeAl3CXsJeQnTB3sJ0wfRB30Jegl5CX0JeQl7CXwJewnRB3wJ0QfOB34JfQl7CX4Jewl8CYAJfwl9CYAJfQl+CX8JgQl6CX8Jegl9CR4Jggl/CR4JfwmACYIJgwmBCYIJgQl/CSAJhAmCCSAJggkeCYQJhQmDCYQJgwmCCTsJbAmFCTsJhQmECYEJgwmFCYEJhQmGCYYJhQlsCYYJbAlvCXgJhglvCXgJbwlyCXoJgQmGCXoJhgl4CRwJHgmACRwJgAmHCYAJfgmICYAJiAmHCYkJhwmICYkJiAmKCRoJHAmHCRoJhwmJCcoHiQmKCcoHignMB8gHGgmJCcgHiQnKB84HzAeKCc4Higl8CYoJiAl+CYoJfgl8CQAISQmLCQAIiwkCCEkJRQk6CUkJOgmLCQIIiwk5CQIIOQkECDYJBggECDYJBAg5CYwJCAgGCIwJBgg2CY0JjAkzCY0JMwkxCQsICAiMCQsIjAmNCVAIjQkxCVAIMQlTCA0ICwiNCQ0IjQlQCHYIKwmBCHYIgQh5CMAHvgcKCcAHCgkXCZYIiAiDCJYIgwgQCQcJBAmcCAcJnAiZCAQJAgmgCAQJoAicCKAIAgkACaAIAAmkCAAJ/gioCAAJqAikCKgI/gj8CKgI/AisCPwIuwiwCPwIsAisCPcI+QiOCfcIjgmQCfkIrAeqB/kIqgeOCZAJjgmRCZAJkQmTCY4JqgeoB44JqAeRCZMJkQmUCZMJlAmVCZEJqAemB5EJpgeUCZUJlAmXCZUJlwmYCZQJpgeiB5QJogeXCZgJlwmaCZgJmgmbCZcJogegB5cJoAeaCZsJmgmABpsJgAZ7BpoJoAeDBpoJgwaABp0JnAl8Bp0JfAZ3Bp4JmQmcCZ4JnAmdCW4GnQl3Bm4GdwZwBmwGngmdCWwGnQluBmoGnwmeCWoGnglsBp8JlgmZCZ8JmQmeCWgGoAmfCWgGnwlqBqAJkgmWCaAJlgmfCWYGoQmgCWYGoAloBqEJjwmSCaEJkgmgCWQG8wihCWQGoQlmBvMI9giPCfMIjwmhCacJpAnPCKcJzwiiCaQJpgnMCKQJzAjPCKkJpQmkCakJpAmnCaUJwAmmCaUJpgmkCasJqAmjCasJowmtCawJqgmoCawJqAmrCbAJrAmrCbAJqwmuCa4JqwmtCa4JrQmvCa4JrwmzCa4JswmxCbAJrgmxCbAJsQmyCbEJtAm1CbEJtQmyCbMJtgm0CbMJtAmxCbUJtAm4CbUJuAm6CbQJtgnECLQJxAi4CbcJuwm8CbcJvAm5CcMIxwi7CcMIuwm3CbwJuwm9CbwJvQm+CbsJxwjKCLsJygi9Cb0JygjMCL0JzAimCb4JvQmmCb4JpgnACbwJvgm/CbwJvwlBCsAJwQm/CcAJvwm+CcQJwQnACcQJwAmlCccJwgnBCccJwQnECb8JwQnCCb8Jwgk6CsUJxAmlCcUJpQmpCckJxwnECckJxAnFCc0JzAnICc0JyAnLCcwJzgnDCcwJwwnICc0J0gnPCc0JzwnMCcwJzwnQCcwJ0AnOCc8J0Qn0Cc8J9AnQCdIJ0wnRCdIJ0QnPCdUJ1AnSCdUJ0gnNCdQJ1gnTCdQJ0wnSCdkJ1wnUCdkJ1AnVCdcJ2AnWCdcJ1gnUCdoJ3AnYCdoJ2AnXCdsJ2gnXCdsJ1wnZCd4J3QnaCd4J2gnbCd0J3wncCd0J3AnaCeEJ4AndCeEJ3QneCeAJ4gnfCeAJ3wndCeEJ5gnjCeEJ4wngCeAJ4wnkCeAJ5AniCeMJ5Qn7CeMJ+wnkCeYJ5wnlCeYJ5QnjCegJ6gnnCegJ5wnmCekJ6AnmCekJ5gnhCewJ6wnoCewJ6AnpCesJ7QnqCesJ6gnoCewJzgnuCewJ7gnrCe4J7wntCe4J7QnrCdAJ8AnuCdAJ7gnOCfAJ8QnvCfAJ7wnuCfQJ8gnwCfQJ8AnQCfIJ8wnxCfIJ8QnwCfUJ9wnzCfUJ8wnyCfYJ9QnyCfYJ8gn0CfkJ+An1CfkJ9Qn2CfgJ+gn3CfgJ9wn1CeIJ5An4CeIJ+An5CeQJ+wn6CeQJ+gn4CfoJ/An9CfoJ/Qn3CfsJ/wn8CfsJ/An6CfwJ/gkPCvwJDwr9Cf8JAAr+Cf8J/gn8CQEKAgoACgEKAAr/CeUJAQr/CeUJ/wn7CecJAwoBCucJAQrlCQMKBAoCCgMKAgoBCgUKBgoECgUKBAoDCuoJBQoDCuoJAwrnCe0JBwoFCu0JBQrqCQcKCAoGCgcKBgoFCu0J7wkJCu0JCQoHCgkKCgoICgkKCAoHCvEJCwoJCvEJCQrvCQsKDAoKCgsKCgoJCvMJDQoLCvMJCwrxCQ0KDgoMCg0KDAoLCv0JDwoOCv0JDgoNCvMJ9wn9CfMJ/QkNCgwKDgoPCgwKDwoQChAKDwr+CRAK/gkRCggKCgoMCggKDAoQCggKEAoRCggKEQoGCgYKEQoCCgYKAgoEChEK/gkAChEKAAoCCvkJEgoTCvkJEwriCfYJFQoSCvYJEgr5CRIKFAolChIKJQoTChUKFgoUChUKFAoSCvYJ9AkXCvYJFwoVChcKGAoWChcKFgoVChkKGgoYChkKGAoXCtEJGQoXCtEJFwr0CdMJGwoZCtMJGQrRCRsKHAoaChsKGgoZCtYJHQobCtYJGwrTCR0KHgocCh0KHAobCtgJHwodCtgJHQrWCR8KIAoeCh8KHgodCiEKIgogCiEKIAofCtgJ3AkhCtgJIQofCt8JIwohCt8JIQrcCSMKJAoiCiMKIgohCuIJEwojCuIJIwrfCRMKJQokChMKJAojCiQKJgonCiQKJwoiCiUKKQomCiUKJgokCiYKKAo5CiYKOQonCikKKgooCikKKAomCisKLAoqCisKKgopChQKKwopChQKKQolChYKLQorChYKKwoUCi0KLgosCi0KLAorChYKGAovChYKLwotCi8KMAouCi8KLgotCjEKMgowCjEKMAovChoKMQovChoKLwoYChwKMwoxChwKMQoaCjMKNAoyCjMKMgoxCh4KNQozCh4KMwocCjUKNgo0CjUKNAozCiAKNwo1CiAKNQoeCjcKOAo2CjcKNgo1CiIKJwo3CiIKNwogCicKOQo4CicKOAo3CjkKKAo2CjkKNgo4CigKKgo0CigKNAo2CioKLAoyCioKMgo0CiwKLgowCiwKMAoyCsMJzgnsCcMJ7Ak7Cj0KOwrsCT0K7AnpCUEKvwk6CkEKOgo8CkAKPQrpCUAK6QnhCUIKQQo8CkIKPAo+CrkJvAlBCrkJQQpCCkQKQwo/CkQKPwpGCrUJuglDCrUJQwpECrIJtQlECrIJRApFCkUKRApGCkUKRgpICkkKRwreCUkK3gnbCUcKQArhCUcK4QneCUwKRQpICkwKSApKCkkK2wnZCUkK2QlLCtkJ1QlOCtkJTgpLCkoKTQpPCkoKTwpMCk0KygnGCU0KxglPCtUJzQnLCdUJywlOCkwKTwqsCUwKrAmwCU8KxgmqCU8KqgmsCbIJRQpMCrIJTAqwCbMJUgpQCrMJUAq2CcQItglQCsQIUApRClMKUQpQClMKUApSClQKVQpTClQKUwpSCq8JVApSCq8JUgqzCa8JrQlWCq8JVgpUClUKVApWClUKVgpXCqMJVwpWCqMJVgqtCaMJtAhYCqMJWApXClUKVwpYClUKWApZCrgIWQpYCrgIWAq0CFMKVQpZClMKWQpaClkKuAi6CFkKughaCroIvghbCroIWwpaClMKWgpbClMKWwpRCsQIUQpbCsQIWwq+CF0KXApQBl0KUAZOBnsIXApdCnsIXQpeCl4KXQpfCl4KXwpgCl8KXQpOBl8KTgZMBmAKXwphCmAKYQpkCkoGYQpfCkoGXwpMBmMKYQpKBmMKSgZIBmcKZAphCmcKYQpjCmoKZQpkCmoKZApnCnMKYApkCnMKZAplCmgKZgpiCmgKYgpyCmsKaQpmCmsKZgpoCooFjAVlCooFZQpqCoYFiQVpCoYFaQprCmsKaApuCmsKbgpsCoQFhgVrCoQFawpsCmwKbgpwCmwKcAptCoQFbAptCoQFbQqCBWgKcgpvCmgKbwpuCm4KbwpxCm4KcQpwCisAbQpwCisAcApxCm8KQgZABm8KQAZxCisAcQpABm8KcgpEBm8KRAZCBnIKYgpHBnIKRwZEBisAggVtCmUKjAWOBWUKjgVzCnMKdApeCnMKXgpgCnQKcwqOBXQKjgWQBZQFlgV4CJQFeAh0CpAFkgWUBZAFlAV0CnQKeAh7CHQKewheCpYFmAVyCJYFcgh4CHcKdQr+B3cK/gemBXUKeAr4B3UK+Af+B9sH3wd1CtsHdQp3Ct8H4wd4Ct8HeAp1CuQH6Ad6CuQHegp5CnkKegr1B3kK9Qf5B3oK7wfxB3oK8Qf1B+gH6wfvB+gH7wd6CnsK1gfaB3sK2gd2CqgFewp2CqgFdgqlBaoF0Ad7CqoFewqoBdAH1AfWB9AH1gd7CnwKpQfQBXwK0AXSBX4KowelB34KpQd8Cn0KfArSBX0K0gXUBX8Kfgp8Cn8KfAp9CoAKfgp/CoAKfwqBCp8Howd+Cp8HfgqACgIGgAqBCgIGgQoABgQGnweACgQGgAoCBgAGgQqCCgAGggr9BYEKfwqFCoEKhQqCCv4FgwqECv4FhAr6BYMKhgqHCoMKhwqECocKhgqJCocKiQqKCoUKfwp9CoUKfQqICooKiQrYBYoK2AXaBYgKfQrUBYgK1AXXBdoF3AWLCtoFiwqKCooKiwqMCooKjAqHCosKjQqOCosKjgqMCtwF3gWNCtwFjQqLCo0KjwqQCo0KkAqOCt4F4AWPCt4FjwqNCo8KkQqSCo8KkgqQCuAF4gWRCuAFkQqPCpEKkwqUCpEKlAqSCuIF5AWTCuIFkwqRCpMK6AXqBZMK6gWUCuQF5gXoBeQF6AWTCpQK6gXsBZQK7AWVCpIKlAqVCpIKlQqWCpYKlQrwBZYK8AXyBZUK7AXuBZUK7gXwBZcKlgryBZcK8gX0BZAKkgqWCpAKlgqXCpgKlwr0BZgK9AX2BY4KkAqXCo4KlwqYCpkKmAr2BZkK9gX4BYwKjgqYCowKmAqZCoQKmQr4BYQK+AX6BYcKjAqZCocKmQqECpoKmwrDCpoKwwqcCp0KmgqcCp0KnAqgCp8KnQqgCp8KoAqiCqIKoAqjCqIKowqlCqAKnAqmCqAKpgqjCqUKowqnCqUKpwqpCqYKqgqnCqYKpwqjCqwKqQqnCqwKpwqqCqwKqgqtCqwKrQqwCqYKrgqtCqYKrQqqCrIKsAqtCrIKrQquCrEKtAqzCrEKswqvCqsKrwqzCqsKswq1CrYKtQqzCrYKswq0CqsKtQq3CqsKtwqoCrYKuAq3CrYKtwq1CqQKqAq3CqQKtwq4CroKuQq4CroKuAq2CrkKoQqkCrkKpAq4CrwKuwq5CrwKuQq6CrsKngqhCrsKoQq5Cr0KvAq6Cr0KugrACr4KvQrACr4KwArBCsEKwAq0CsEKtAqxCsAKugq2CsAKtgq0CsMKwgqyCsMKsgquCpsKvwrCCpsKwgrDCpwKwwquCpwKrgqmCsQKxwrtCsQK7QrGCskKywrICskKyArFCsoKzArLCsoKywrJCswKzgrNCswKzQrLCssKzQrPCssKzwrICs4K0grRCs4K0QrNCs8KzQrRCs8K0QrTCtUK0wrRCtUK0QrSCtYK2QrXCtYK1wrUCtAK1ArXCtAK1wrYCtoK2ArXCtoK1wrZCtoK2QrbCtoK2wrcCtYK3grbCtYK2wrZCt8K3ArbCt8K2wreCtUK0grhCtUK4QrdCuAK3QrhCuAK4QriCs4K4grhCs4K4QrSCuUK4AriCuUK4grjCuMK4grOCuMKzgrMCugK5QrjCugK4wrmCuYK4wrMCuYKzArKCukK6wrkCukK5ArnCuoK7ArrCuoK6wrpCuwK2grcCuwK3ArrCusK3ArfCusK3wrkCu0K2AraCu0K2grsCsYK7QrsCsYK7ArqCscK0ArYCscK2ArtCu4K8QoXC+4KFwvwCvMK9QryCvMK8grvCvQK9gr1CvQK9QrzCvYK+Ar3CvYK9wr1CvUK9wr6CvUK+gryCvgK/Ar7CvgK+wr3CvoK9wr7CvoK+wr+CgAL/gr7CgAL+wr8Cv8KAwsBC/8KAQv9CvkK/QoBC/kKAQsCCwQLAgsBCwQLAQsDCwQLAwsFCwQLBQsGC/8KBwsFC/8KBQsDCwkLBgsFCwkLBQsHCwAL/AoLCwALCwsICwoLCAsLCwoLCwsMC/gKDAsLC/gKCwv8Cg8LCgsMCw8LDAsNCw0LDAv4Cg0L+Ar2ChILDwsNCxILDQsQCxALDQv2ChAL9gr0ChMLFQsOCxMLDgsRCxQLFgsVCxQLFQsTCxYLBAsGCxYLBgsVCxULBgsJCxULCQsOCxcLAgsECxcLBAsWC/AKFwsWC/AKFgsUC/EK+QoCC/EKAgsXCxgLGgtBCxgLQQsZCxsLHgsaCxsLGgsYCxwLHwseCxwLHgsbCx8LIgshCx8LIQseCx4LIQskCx4LJAsaCyILJgslCyILJQshCyQLIQslCyQLJQsoCykLKAslCykLJQsmCykLLQsrCykLKwsoCyQLKAsrCyQLKwssCy8LLAsrCy8LKwstCzALLgsxCzALMQsyCyoLMwsxCyoLMQsuCzQLMgsxCzQLMQszCyoLJws1CyoLNQszCzQLMws1CzQLNQs2CyMLNgs1CyMLNQsnCzgLNAs2CzgLNgs3CzcLNgsjCzcLIwsgCzoLOAs3CzoLNws5CzkLNwsgCzkLIAsdCzsLPgs4CzsLOAs6Cz0LQAs+Cz0LPgs7C0ALMAsyC0ALMgs+Cz4LMgs0Cz4LNAs4C0ELLAsvC0ELLws/CxkLQQs/CxkLPws8CxoLJAssCxoLLAtBC0MLRgtrC0MLawtEC0cLSQtFC0cLRQtCC0gLSgtJC0gLSQtHC0oLTAtLC0oLSwtJC0kLSwtNC0kLTQtFC0wLUAtPC0wLTwtLC00LSwtPC00LTwtRC1MLUQtPC1MLTwtQC1QLVwtVC1QLVQtSC04LUgtVC04LVQtWC1gLVgtVC1gLVQtXC1gLVwtZC1gLWQtaC1QLXAtZC1QLWQtXC14LWgtZC14LWQtcC1MLUAtfC1MLXwtbC10LWwtfC10LXwtgC0wLYAtfC0wLXwtQC2ILXQtgC2ILYAthC2ELYAtMC2ELTAtKC2ULYgthC2ULYQtkC2QLYQtKC2QLSgtIC2cLaQtjC2cLYwtmC2gLagtpC2gLaQtnC2oLWAtaC2oLWgtpC2kLWgteC2kLXgtjC2sLVgtYC2sLWAtqC0QLawtqC0QLagtoC0YLTgtWC0YLVgtrC20LcAuVC20LlQtuC3ELcwtvC3ELbwtsC3ILdAtzC3ILcwtxC3QLdgt1C3QLdQtzC3MLdQt3C3MLdwtvC3YLegt5C3YLeQt1C3cLdQt5C3cLeQt7C30Lewt5C30LeQt6C34LgQt/C34Lfwt8C3gLfAt/C3gLfwuAC4ILgAt/C4ILfwuBC4ILgQuDC4ILgwuEC34LhguDC34LgwuBC4gLhAuDC4gLgwuGC30LeguJC30LiQuFC4cLhQuJC4cLiQuKC3YLiguJC3YLiQt6C4wLhwuKC4wLiguLC4sLigt2C4sLdgt0C48LjAuLC48LiwuOC44Liwt0C44LdAtyC5ELkwuNC5ELjQuQC5ILlAuTC5ILkwuRC5QLgguEC5QLhAuTC5MLhAuIC5MLiAuNC5ULgAuCC5ULgguUC24LlQuUC24LlAuSC3ALeAuAC3ALgAuVC5cLmgu/C5cLvwuYC5sLnQuZC5sLmQuWC5wLngudC5wLnQubC54LoAufC54LnwudC50LnwuhC50LoQuZC6ALpAujC6ALowufC6ELnwujC6ELowumC6gLpgujC6gLowukC6cLqwupC6cLqQulC6ILpQupC6ILqQuqC6wLqgupC6wLqQurC6wLqwutC6wLrQuuC6cLrwutC6cLrQurC7ELrgutC7ELrQuvC6gLpAuzC6gLswuwC7ILsAuzC7ILswu0C6ALtAuzC6ALswukC7YLsgu0C7YLtAu1C7ULtAugC7ULoAueC7kLtgu1C7kLtQu4C7gLtQueC7gLngucC7sLvQu3C7sLtwu6C7wLvgu9C7wLvQu7C74LrAuuC74Lrgu9C70LrguxC70LsQu3C78LqgusC78LrAu+C5gLvwu+C5gLvgu8C5oLoguqC5oLqgu/C8ELwgvpC8EL6QvEC8ULwAvDC8ULwwvHC8YLxQvHC8YLxwvIC8gLxwvJC8gLyQvKC8cLwwvLC8cLywvJC8oLyQvNC8oLzQvOC8sLzwvNC8sLzQvJC9ELzgvNC9ELzQvPC9IL0AvTC9IL0wvVC8wL1AvTC8wL0wvQC9YL1QvTC9YL0wvUC9YL2AvXC9YL1wvVC9IL1QvXC9IL1wvaC9wL2gvXC9wL1wvYC9EL2QvdC9EL3QvOC9sL3gvdC9sL3QvZC8oLzgvdC8oL3QveC+AL3wveC+AL3gvbC98LyAvKC98LygveC+ML4gvfC+ML3wvgC+ILxgvIC+ILyAvfC+UL5AvhC+UL4QvnC+YL5QvnC+YL5wvoC+gL5wvYC+gL2AvWC+cL4QvcC+cL3AvYC+kL6AvWC+kL1gvUC8IL5gvoC8IL6AvpC8QL6QvUC8QL1AvMC+oL6wsTDOoLEwzsC+0L6gvsC+0L7AvwC+8L7QvwC+8L8AvyC/IL8AvzC/IL8wv1C/AL7Av2C/AL9gvzC/UL8wv3C/UL9wv5C/YL+gv3C/YL9wvzC/wL+Qv3C/wL9wv6C/wL+gv9C/wL/QsADPYL/gv9C/YL/Qv6CwIMAAz9CwIM/Qv+CwEMBAwDDAEMAwz/C/sL/wsDDPsLAwwFDAYMBQwDDAYMAwwEDPsLBQwHDPsLBwz4CwYMCAwHDAYMBwwFDPQL+AsHDPQLBwwIDAoMCQwIDAoMCAwGDAkM8Qv0CwkM9AsIDAwMCwwJDAwMCQwKDAsM7gvxCwsM8QsJDA0MDAwKDA0MCgwQDA4MDQwQDA4MEAwRDBEMEAwEDBEMBAwBDBAMCgwGDBAMBgwEDBMMEgwCDBMMAgz+C+sLDwwSDOsLEgwTDOwLEwz+C+wL/gv2CxQMGQyPDBQMjwwXDBgMIAwbDBgMGwwVDBwMHwwaDBwMGgwWDB4MLAwfDB4MHwwcDCAMIgwdDCAMHQwbDCEMIwwiDCEMIgwgDHAMIQwgDHAMIAwYDCMMJQwkDCMMJAwiDCIMJAwnDCIMJwwdDCUMggwmDCUMJgwkDCQMJgwpDCQMKQwnDCgMKgwsDCgMLAweDCkMLQwqDCkMKgwoDCoMKwyQDCoMkAwsDC0MLgwrDC0MKwwqDC8MMQwuDC8MLgwtDCYMLwwtDCYMLQwpDDAMMgwxDDAMMQwvDIIMMAwvDIIMLwwmDDIMNAwzDDIMMwwxDDEMMww2DDEMNgwuDDQMjgw1DDQMNQwzDDMMNQw3DDMMNww2DDYMNww7DDYMOww4DC4MNgw4DC4MOAwrDCsMOAw5DCsMOQyQDDgMOww9DDgMPQw5DDUMPgw6DDUMOgw3DD4MQAw8DD4MPAw6DD8MQQxADD8MQAw+DI4MPww+DI4MPgw1DEEMQwxCDEEMQgxADEAMQgxGDEAMRgw8DEMMVwxFDEMMRQxCDEIMRQxJDEIMSQxGDDkMPQxHDDkMRwxLDEsMRwxKDEsMSgxODJAMOQxLDJAMSwxMDEwMSwxODEwMTgxQDE8MTQxRDE8MUQxSDE0MSAxEDE0MRAxRDFIMUQxTDFIMUwxUDFEMRAxWDFEMVgxTDFQMUwxVDFQMVQxoDFMMVgxYDFMMWAxVDFwMWQxXDFwMVwxDDFUMWAxaDFUMWgxlDF8MWwxZDF8MWQxcDF0MXAxDDF0MQwxBDGEMXwxcDGEMXAxdDGUMWgxeDGUMXgxiDGIMXgxgDGIMYAxkDGMMYgxkDGMMZAx5DGYMZQxiDGYMYgxjDGgMVQxlDGgMZQxmDGkMaAxmDGkMZgxnDGcMZgxjDGcMYwx3DGoMVAxoDGoMaAxpDG8MagxpDG8MaQxrDGsMaQxnDGsMZwxtDG4MdAxxDG4McQxsDGwMcQwhDGwMIQxwDHEMcgwjDHEMIwwhDHQMdgxyDHQMcgxxDHMMdwx4DHMMeAx1DG0MZwx3DG0MdwxzDHcMYwx5DHcMeQx4DHgMeQx7DHgMewx6DHUMeAx6DHUMegx9DHoMewyGDHoMhgx8DH0Megx8DH0MfAx/DH4MfwyBDH4MgQyADHYMfgyADHYMgAxyDHIMgAwlDHIMJQwjDIAMgQyCDIAMggwlDIEMgwwwDIEMMAyCDH8MfAyDDH8MgwyBDIMMhAwyDIMMMgwwDHwMhgyEDHwMhAyDDIQMhQw0DIQMNAwyDIYMhwyFDIYMhQyEDHsMiQyHDHsMhwyGDIkMigyIDIkMiAyHDIcMiAyNDIcMjQyFDHkMZAyJDHkMiQx7DGQMYAyKDGQMigyJDIsMYQxdDIsMXQyMDIgMiwyMDIgMjAyNDIwMXQxBDIwMQQw/DI0MjAw/DI0MPwyODIUMjQyODIUMjgw0DBcMjwxqDBcMagxvDI8MUgxUDI8MVAxqDBkMTwxSDBkMUgyPDB8MTAxQDB8MUAwaDCwMkAxMDCwMTAwfDJEMkwzGDJEMxgySDJIMlwyUDJIMlAyRDJQMlgyTDJQMkwyRDJUMogyWDJUMlgyUDJcMmQyVDJcMlQyUDJgMmgyZDJgMmQyXDMUMmAyXDMUMlwySDJoMnAybDJoMmwyZDJkMmwyeDJkMngyVDJwMyQydDJwMnQybDJsMnQyfDJsMnwyeDJ4MoAyiDJ4MogyVDJ8MowygDJ8MoAyeDKAMoQzHDKAMxwyiDKMMpAyhDKMMoQygDKUMpwykDKUMpAyjDJ0MpQyjDJ0MowyfDKYMqAynDKYMpwylDMkMpgylDMkMpQydDKgMqgypDKgMqQynDKcMqQysDKcMrAykDKoMyAyrDKoMqwypDKkMqwytDKkMrQysDKwMrQywDKwMsAyuDKQMrAyuDKQMrgyhDKEMrgyvDKEMrwzHDK4MsAyxDK4MsQyvDKsMsgywDKsMsAytDLIMtAyxDLIMsQywDLMMtQy0DLMMtAyyDMgMswyyDMgMsgyrDLUMtwy2DLUMtgy0DLQMtgy5DLQMuQyxDLcMwwy4DLcMuAy2DLYMuAy6DLYMugy5DK8MsQy5DK8MuQy7DLsMuQy6DLsMugy9DMcMrwy7DMcMuwy8DLwMuwy9DLwMvQy+DL4MvQy/DL4MvwzADL0Mugy4DL0MuAy/DMAMvwzBDMAMwQzCDL8MuAzDDL8MwwzBDMYMwAzCDMYMwgzEDJIMxgzEDJIMxAzFDJMMvgzADJMMwAzGDJYMvAy+DJYMvgyTDKIMxwy8DKIMvAyWDMoMywz/DMoM/wzMDMsMygzNDMsMzQzQDM0MygzMDM0MzAzPDM4MzQzPDM4MzwzbDNAMzQzODNAMzgzSDNEM0AzSDNEM0gzTDP4MywzQDP4M0AzRDNMM0gzUDNMM1AzVDNIMzgzXDNIM1wzUDNUM1AzWDNUM1gwCDdQM1wzYDNQM2AzWDNcMzgzbDNcM2wzZDNgM1wzZDNgM2QzcDNkM2wwADdkMAA3aDNwM2QzaDNwM2gzdDN4M3AzdDN4M3QzgDNYM2AzcDNYM3AzeDN8M3gzgDN8M4AzhDAIN1gzeDAIN3gzfDOEM4AziDOEM4gzjDOAM3QzlDOAM5QziDOMM4gzkDOMM5AwBDeIM5QzmDOIM5gzkDOUM5wzpDOUM6QzmDN0M2gznDN0M5wzlDNoMAA3oDNoM6AznDOcM6AzqDOcM6gzpDOQM5gzpDOQM6QzrDOsM6QzqDOsM6gztDOwM6wztDOwM7QzuDAEN5AzrDAEN6wzsDO4M7QzvDO4M7wzwDO0M6gzyDO0M8gzvDPAM7wzxDPAM8Qz8DO8M8gzzDO8M8wzxDOgM9AzyDOgM8gzqDPQM9gzzDPQM8wzyDAAN9Qz0DAAN9AzoDPUM9wz2DPUM9gz0DPcM+Qz4DPcM+Az2DPYM+AzxDPYM8QzzDPkM+wz6DPkM+gz4DPgM+gz8DPgM/AzxDP8M/Qz7DP8M+wz5DMsM/gz9DMsM/Qz/DMwM/wz5DMwM+Qz3DM8MzAz3DM8M9wz1DNsMzwz1DNsM9QwADQMNCA1+DQMNfg0GDQcNDw0KDQcNCg0EDQsNDg0JDQsNCQ0FDQ0NGw0ODQ0NDg0LDQ8NEQ0MDQ8NDA0KDRANEg0RDRANEQ0PDV8NEA0PDV8NDw0HDRINFA0TDRINEw0RDRENEw0WDRENFg0MDRQNcQ0VDRQNFQ0TDRMNFQ0YDRMNGA0WDRcNGQ0bDRcNGw0NDRgNHA0ZDRgNGQ0XDRkNGg1/DRkNfw0bDRwNHQ0aDRwNGg0ZDR4NIA0dDR4NHQ0cDRUNHg0cDRUNHA0YDR8NIQ0gDR8NIA0eDXENHw0eDXENHg0VDSENIw0iDSENIg0gDSANIg0lDSANJQ0dDSMNfQ0kDSMNJA0iDSINJA0mDSINJg0lDSUNJg0qDSUNKg0nDR0NJQ0nDR0NJw0aDRoNJw0oDRoNKA1/DScNKg0sDScNLA0oDSQNLQ0pDSQNKQ0mDS0NLw0rDS0NKw0pDS4NMA0vDS4NLw0tDX0NLg0tDX0NLQ0kDTANMg0xDTANMQ0vDS8NMQ01DS8NNQ0rDTINRg00DTINNA0xDTENNA04DTENOA01DSgNLA02DSgNNg06DToNNg05DToNOQ09DX8NKA06DX8NOg07DTsNOg09DTsNPQ0/DT4NPA1ADT4NQA1BDTwNNw0zDTwNMw1ADUENQA1CDUENQg1DDUANMw1FDUANRQ1CDUMNQg1EDUMNRA1XDUINRQ1HDUINRw1EDUsNSA1GDUsNRg0yDUQNRw1JDUQNSQ1UDU4NSg1IDU4NSA1LDUwNSw0yDUwNMg0wDVANTg1LDVANSw1MDVQNSQ1NDVQNTQ1RDVENTQ1PDVENTw1TDVINUQ1TDVINUw1oDVUNVA1RDVUNUQ1SDVcNRA1UDVcNVA1VDVgNVw1VDVgNVQ1WDVYNVQ1SDVYNUg1mDVkNQw1XDVkNVw1YDV4NWQ1YDV4NWA1aDVoNWA1WDVoNVg1cDV0NYw1gDV0NYA1bDVsNYA0QDVsNEA1fDWANYQ0SDWANEg0QDWMNZQ1hDWMNYQ1gDWINZg1nDWINZw1kDVwNVg1mDVwNZg1iDWYNUg1oDWYNaA1nDWcNaA1qDWcNag1pDWQNZw1pDWQNaQ1sDWkNag11DWkNdQ1rDWwNaQ1rDWwNaw1uDW0Nbg1wDW0NcA1vDWUNbQ1vDWUNbw1hDWENbw0UDWENFA0SDW8NcA1xDW8NcQ0UDXANcg0fDXANHw1xDW4Naw1yDW4Ncg1wDXINcw0hDXINIQ0fDWsNdQ1zDWsNcw1yDXMNdA0jDXMNIw0hDXUNdg10DXUNdA1zDWoNeA12DWoNdg11DXgNeQ13DXgNdw12DXYNdw18DXYNfA10DWgNUw14DWgNeA1qDVMNTw15DVMNeQ14DXoNUA1MDXoNTA17DXcNeg17DXcNew18DXsNTA0wDXsNMA0uDXwNew0uDXwNLg19DXQNfA19DXQNfQ0jDQYNfg1ZDQYNWQ1eDX4NQQ1DDX4NQw1ZDQgNPg1BDQgNQQ1+DQ4NOw0/DQ4NPw0JDRsNfw07DRsNOw0ODWgAawCMDWgAjA2NDQoCjA2LDQoCiw0HAm8AdACJDW8AiQ2KDQQCiQ2IDQQCiA0CAgAChw2IDQACiA13AP0Bhg2HDf0Bhw0AAvwBhg2FDfwBhQ35AfkBhQ2DDfkBgw3zAfQBhA2CDfQBgg3wAfABgg2BDfABgQ3uAekBgA2BDekBgQ3qAWgAjQ2ADWgAgA3pAQ4Gmw2aDQ4Gmg0MBpAGjQaZDZAGmQ2aDQkGmA2XDQkGlw0GBooGiAaWDYoGlg2XDYYGBAaWDYYGlg2VDYMGhgaVDYMGlQ2UDYIGfwaTDYIGkw2UDX8GeQaRDX8GkQ2TDXoGdgaQDXoGkA2SDXYGdAaPDXYGjw2QDW8GcAaPDW8Gjw2ODQ4GbwaODQ4Gjg2bDauqKj2rqqo9AAAAPquqKj5VVVU+AACAPlVVlT6rqqo+AADAPlVV1T6rquo+AAAAP6uqCj9VVRU/AAAgP6uqKj9VVTU/AABAP6uqSj9VVVU/AABgP6uqaj9VVXU/AACAP1VVhT+rqoo/AACQP1VVlT+rqpo/AACgP1VVpT+rqqo/AACwP1VVtT+rqro/AADAP1VVxT+rqso/AADQP1VV1T+rqto/AADgP1VV5T+rquo/AADwP1VV9T+rqvo/AAAAQKuqAkBVVQVAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEzvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAP0zvrjMAAAAAAAAAAAAAgD9M764zAAAAAAAAAAAAAIA/TO+uMwAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAi+iI+AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/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HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPxUAgD8DAIA/HQCAPwTGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMATGADLfCqQ9sFQMMLJe8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P7Je8L2I33S572qvuxo6fj+yXvC9iN90ue9qr7saOn4/sl7wvYjfdLnvaq+7Gjp+P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P///fz/1/38/9/9/P8QOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUscQOsrGc8FM8s4QUsaUgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P6UgXr1UW7c5i4Exu1Gffz+lIF69VFu3OYuBMbtRn38/pSBevVRbtzmLgTG7UZ9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P6eZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsaeZErL3III9CjiPsY4bBD9v9Ye8IW9lvd/EWj+OGwQ/b/WHvCFvZb3fxFo/jhsEP2/1h7whb2W938RaP44bBD9v9Ye8IW9lvd/EWj+OGwQ/b/WHvCFvZb3fxFo/jhsEP2/1h7whb2W938RaP44bBD9v9Ye8IW9lvd/EWj+OGwQ/b/WHvCFvZb3fxFo/jhsEP2/1h7whb2W938RaP44bBD9v9Ye8IW9lvd/EWj+OGwQ/b/WHvCFvZb3fxFo/jhsEP2/1h7whb2W938RaP44bBD9v9Ye8IW9lvd/EWj+OGwQ/b/WHvCFvZb3fxFo/jhsEP2/1h7whb2W938RaP44bBD9v9Ye8IW9lvd/EWj+OGwQ/b/WHvCFvZb3fxFo/jhsEP2/1h7whb2W938RaP44bBD9v9Ye8IW9lvd/EWj+OGwQ/b/WHvCFvZb3fxFo/jhsEP2/1h7whb2W938RaP44bBD9v9Ye8IW9lvd/EWj+OGwQ/b/WHvCFvZb3fxFo/jhsEP2/1h7whb2W938RaP44bBD9v9Ye8IW9lvd/EWj+OGwQ/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AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz8AAIA/AQCAP54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WL54O+LHYAsQ88W6WLzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPzoSDLSK16OpiSyntAAAgD86Egy0itejqYksp7QAAIA/OhIMtIrXo6mJLKe0AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9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cYN/P/9M8jBS438z7EzyvFTjfz+LHQIw7P1/M4cdArzv/X8//v//D/z/fzMAAAAcAACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAP///fz///38/AACAP/3/fz/+/38/AACAPwAAgD///38/AACAPwAAgD///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwEAgD///38/AACAP///fz///38/AACAP///fz///38/AACAPwAAgD///38/AACAP///fz/+/38/AACAPwAAgD8AAIA/AACAPwEAgD///38/AACAPwEAgD8AAIA/AACAPwAAgD///38/AACAP/7/fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAP///fz///38/AACAPwEAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAP///fz///38/AACAPwAAgD///38/AACAPwAAgD/+/38/AACAPwAAgD/9/38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD8AAIA/AACAP/3/fz/+/38/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAPwEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKAEAgJw8Ou09AQAAKKj//zBCOu09AAAApwDAFag/Ou09AAAUqACA3KdKOu09AACMqAAAgqdIOu09AACIqP//vzJBOu09AACgJwEAADJCOu09AAAAp////7JAOu09//9/p///v7JAOu09//8/GwEAgDI/Ou09AQAgp/3/f7JBOu09AABgp///37I/Ou09/v+/pgYAgDE+Ou09AABAJwAAJCdCOu09AAAAp/z//7FCOu09AABAp/7//7E+Ou09/v9/GgAAwLJDOu09AgCAJgEAgLJBOu09AAAAKACApKdAOu09AABgKN//fzFAOu09AADAp70AALBBOu09AADYpsT/fzE4Ou09AAAgJ/f//zI8Ou09AAAQKOz/fzJBOu09AAAAKAoAgLJCOu09////pvv/PzM+Ou09//+fJ9X//zE+Ou09AADAp9b//zE+Ou09AADAJwAApKhBOu09AAAQKAoAoLJAOu09AADgpwUAMLM9Ou09/f//pq//fzFEOu09AACAJgoAwLJAOu09AADAp+v/fzJAOu09AAAgKBUAALI8Ou09AAAgKNf//zFAOu09//9/Jtv//zFDOu09AAAQKOD//zE6Ou09AABAJ8j/fzFCOu09AAAEKABIRKhCOu09AABiqAEAgJw8Ou09AQAAKIAAAIAAAIAnAACAkAAAgD+AAACAAACAJwAAgJAAAIA/gAAAgAAAgCcAAICQAACAP4AAAIAAAIAnAACAkAAAgD+AAACAAACAJwAAgJAAAIA/gAAAgAAAgCcAAICQAACAP4AAAIAAAIAnAACAkAAAgD+AAACAAACAJwAAgJAAAIA/gAAAgAAAgCcAAICQAACAP4AAAIAAAIAnAACAkAAAgD+AAACAAACAJwAAgJAAAIA/AACAmAAAuCcAAICwAACAPwAA4KcAAIAnAAAAMgAAgD8AAICnAACAJwAAgA8AAIA/AABApwAAoCcAAICyAACAPwAAACcAAEAnAAAAjwAAgD8AAIAmAACIJwAAgDIAAIA/AACApgAAkCcAAACyAACAPwAAgCYAADAnAAAAMgAAgD8AAECmAAB4JwAAQA4AAIA/AAAAAAAAmCcAAAAAAACAPwAAgKUAAIwnAACADQAAgD8AAIAmAACgJ////7EAAIA/AAAAAAAAcCcAAAAAAACAPwAAAAAAAKAnAAAAAAAAgD8AAIAmAACQJwAAgI4AAIA/AAAAAAAAkCcAAAAAAACAPwIAgCYAANAnAgCAjgAAgD8BACAoAQDgJwEAALIAAIA/AAAAJwAAoCcAAACPAACAPwAA/KcAAAgnAAD8DwAAgD8AAGAnAAAgKAAAYI8AAIA///9/JgAAgCcAAICyAACAPwEAgCcAAIAnAQAAsgAAgD8AAKCnAAAAJwAAoA8AAIA/AACApgAAoKcAAICyAACAPwAAQKcAAACnAAAAsgAAgD8AAAAAAAAkKAAAAAAAAIA/AAAApgAAWCcAAAAOAACAPwAAAKYAAIQnAAAADgAAgD8AAIAZAABIKAAAgDEAAIA/AAAAmgAAgKcAAACyAACAP///f6YAAMCmAAAAMgAAgD8AAICnAACAJwAAgA8AAIA/AACAJgAAoCcAAICOAACAP/z/fyb8/3+n/P9/jgAAgD8AAICmAQDAJ///fw4AAIA/AACApgAAgCYAAIAOAACAPwAAAKYAAACmAAAADgAAgD8AAHinAAC8JwAAgDAAAIA/gAAAgAAAgCcAAICQAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/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t/8/rm71ni3e64k8tf5vrvB1ZTKSYSuxdCIAMgAAgD/wdWUykmErsXQiADIAAIA/8HVlMpJhK7F0IgAyAACAP/B1ZTKSYSuxdCIAMgAAgD/wdWUykmErsXQiADIAAIA/8HVlMpJhK7F0IgAyAACAP/B1ZTKSYSuxdCIAMgAAgD/wdWUykmErsXQiADIAAIA/8HVlMpJhK7F0IgAyAACAP/B1ZTKSYSuxdCIAMgAAgD/wdWUykmErsXQiADIAAIA/8LVjMmNiJbGuPIAyAACAP/D1ZDKCYiWxAADsLQAAgD/wdWgyUmIlsZw3ADIAAIA/8MVjMtJhJrEA2H6xAACAP/BiYDKy4Sex0NGAMAAAgD/we2UyMmInscg5ATEAAIA/8HVjMjJiJbGwo/+xAACAP/BVYzKSYiOxfFP/sQAAgD/wNWMy8mElsXhw/7EAAIA/8DVkMrJhJ7EAEHgsAACAP/D1YTLSYSWxAG6OLQAAgD/w9WMy8mAtsb66/zEAAIA/8BVlMlJiIbFZSAAyAACAP/DlYTJSYiexALCkLQAAgD/wrWQysmEnsQCwgiwAAIA/sJxkMtJBJ7EACEwtAACAP/ARZTIy4iWxARgDLQAAgD/w1WMyUmEmsebxP7IAAIA/8NVhMvJhJ7FIn/+xAACAP/D1ZTIyYiWxANTMLQAAgD/w9WEyVGEtsRLwvzIAAIA/8HVlMgRiLbFkEMAyAACAP/B1ZDLDYC2xA4d/MgAAgD/w9WAylGElsRICADMAAIA/8PVhMsNhJbHvC4AyAACAP/A1ZDKxYiexMod/sgAAgD/wlWQykmArsVAa/zEAAIA/8HVkMnNiKbE/LIAyAACAP/AVZjLiYSWxQKd/sQAAgD/whWMy0mEnsQAwFiwAAIA/8LViMoJhKbFUh38xAACAP/B1YTJiYiWxAJrALQAAgD/wtWEygmIlsQC8yy0AAIA/8HViMgRhLbE04L8yAACAP/B1YjKEYjGxli/AMgAAgD/wdWUygmEpscAWADIAAIA/8PViMlNjKbEza4AyAACAP/B1ZDLkYSmxiBvAMgAAgD/w9WMys2EpsY/8fzIAAIA/8HVlMpJhK7F0IgAyAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP/7/fz///38///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AQCAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38/AACAPwAAgD///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAP///fz///38///9/PwAAgD///38///9/PwAAgD8AAIA///9/PwEAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/P///fz8AAIA/AACAPwAAgD///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP/RhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPRhD71v0JE95rE2sPNhD7110JE9C7I2sPZhD7100JE95rE2sPVhD71v0JE9ybE2sPVhD71w0JE9TbE2sPJhD71w0JE987E2sPJhD71v0JE9A7I2sO9hD71y0JE9a7I2sOthD71y0JE9u7E2sPdhD71q0JE9TLE2sPthD71t0JE9/7E2sPxhD71v0JE9DrI2sPNhD71z0JE9S7I2sPFhD71y0JE9GrI2sPVhD71s0JE90bE2sPFhD71y0JE96LE2sO1hD71q0JE9MrI2sPRhD7110JE927E2sPVhD71x0JE9GLI2sPdhD71w0JE9ArI2sPVhD71y0JE9KrI2sPRhD7110JE9QbI2sPVhD7100JE9KLI2sPJhD7110JE9b7I2sPNhD71u0JE9Y7E2sPNhD71w0JE94bE2sPphD7100JE9RrI2sPthD71y0JE90LE2sPthD7100JE9ybE2sPZhD71y0JE9FbI2sPphD71w0JE9R7I2sOthD71u0JE9ZLE2sABiD7120JE9WrI2sABiD71v0JE9crI2sPlhD71u0JE9n7E2sPZhD71s0JE9rrE2sPdhD71v0JE9zLE2sPVhD71w0JE9erE2sPVhD71u0JE947E2sPZhD71o0JE9TrE2sPRhD71v0JE95rE2sHtyQrNpTtIy75ooP5aiQD97ckKzaU7SMu+aKD+WokA/e3JCs2lO0jLvmig/lqJAP3tyQrNpTtIy75ooP5aiQD97ckKzaU7SMu+aKD+WokA/e3JCs2lO0jLvmig/lqJAP3tyQrNpTtIy75ooP5aiQD97ckKzaU7SMu+aKD+WokA/e3JCs2lO0jLvmig/lqJAP3tyQrNpTtIy75ooP5aiQD97ckKzaU7SMu+aKD+WokA/e3JCs2pO0jLvmig/lqJAP3pyQrNoTtIy75ooP5aiQD98ckKzbE7SMu+aKD+WokA/e3JCs2pO0jLvmig/lqJAP3xyQrNqTtIy75ooP5aiQD97ckKza07SMu6aKD+XokA/e3JCs2pO0jLvmig/lqJAP3pyQrNoTtIy7pooP5eiQD97ckKzaE7SMu+aKD+WokA/e3JCs2xO0jLvmig/lqJAP3pyQrNoTtIy75ooP5aiQD97ckKzaE7SMu6aKD+XokA/e3JCs2ZO0jLvmig/lqJAP3pyQrNsTtIy75ooP5aiQD96ckKzaU7SMu+aKD+WokA/fHJCs21O0jLvmig/lqJAP3pyQrNmTtIy75ooP5aiQD98ckKzaU7SMu+aKD+WokA/fHJCs2xO0jLvmig/lqJAP3xyQrNsTtIy75ooP5aiQD97ckKzak7SMu+aKD+WokA/fHJCs2xO0jLvmig/lqJAP3xyQrNsTtIy75ooP5aiQD97ckKzaU7SMu+aKD+WokA/fHJCs2lO0jLvmig/lqJAP3tyQrNqTtIy75ooP5aiQD96ckKzbU7SMu+aKD+WokA/enJCs2lO0jLvmig/lqJAP3tyQrNpTtIy75ooP5aiQD96ckKzZ07SMu6aKD+XokA/e3JCs2pO0jLvmig/lqJAP3pyQrNqTtIy75ooP5aiQD98ckKzak7SMu+aKD+WokA/enJCs2hO0jLvmig/lqJAP3pyQrNsTtIy75ooP5aiQD97ckKzbE7SMu+aKD+WokA/enJCs2lO0jLvmig/lqJAP3tyQrNsTtIy75ooP5aiQD97ckKzaU7SMu+aKD+WokA/e3JCs2lO0jLvmig/lqJAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/8/38/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAP/7/fz/9/38/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAP/7/fz/9/38/AACAPwAAgD/+/38/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAPwAAgD/+/38/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAPwAAgD/+/38/AACAP/7/fz/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD8AAIA/AACAP/7/fz8AAIA/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAP/7/fz8AAIA/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAP/7/fz/9/38/AACAP/7/fz/+/38/AACAP/7/fz/9/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAPw+fmbLfnQ09omqQsA+fmbLfnQ09omqQsA+fmbLfnQ09omqQsA+fmbLfnQ09omqQsA+fmbLfnQ09omqQsA+fmbLfnQ09omqQsA+fmbLfnQ09omqQsA+fmbLfnQ09omqQsA+fmbLfnQ09omqQsA+fmbLfnQ09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/v9/P/3/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz///38//v9/P/7/fz/9/38//v9/P///fz8AAIA//v9/P/7/fz///38//v9/P/3/fz/8/38//v9/P/z/fz/9/38//v9/P/3/fz/9/38//v9/P/z/fz8AAIA//v9/P/7/fz/9/38//v9/P/3/fz/9/38//v9/P/z/fz/8/38//v9/P/7/fz///38//v9/P/3/fz/9/38//v9/P/3/fz/9/38//v9/P/z/fz/9/38//v9/P/7/fz8AAIA//v9/P/3/fz/9/38//v9/P/3/fz/9/38//v9/P/7/fz///38//v9/P/7/fz/9/38//v9/P///fz/9/38//v9/P/3/fz/9/38//v9/P///fz/9/38//v9/P/3/fz/9/38//v9/P/3/fz/9/38//v9/P/7/fz///38//v9/P/3/fz/9/38//v9/P/3/fz/7/38//v9/P/z/fz/9/38//v9/P/3/fz/9/38//v9/P/z/fz/7/38//v9/P/3/fz/9/38//v9/P/z/fz/9/38//v9/PxfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvRfrCjthCim9b+pZvafqCjthCim9b+pZvbrqCjtkCim9b+pZvT7qCjtlCim9b+pZvZfqCjtnCim9b+pZvX3qCjtrCim9cOpZvfnpCjtmCim9b+pZvZDqCjtpCim9cOpZvcPqCjtnCim9b+pZvZzrCjttCim9b+pZvV3qCjtmCim9cOpZvRXqCjtnCim9cOpZvcnpCjtpCim9b+pZvZfqCjtwCim9bupZvcvqCjtiCim9b+pZvYnqCjtoCim9b+pZveDpCjtsCim9bepZvcfpCjtcCim9bupZvQjrCjtlCim9b+pZvdnpCjtkCim9b+pZvaHqCjthCim9cOpZvR3rCjtmCim9b+pZvZ/qCjtqCim9cOpZvfrqCjtiCim9b+pZvarqCjtfCim9b+pZveTpCjtuCim9bupZvRPqCjtnCim9b+pZvRLqCjtjCim9bupZvb7qCjtmCim9b+pZvVTqCjtrCim9b+pZvYHqCjtkCim9bupZvXrqCjtjCim9b+pZvWTqCjtoCim9b+pZvSjqCjtmCim9b+pZve3pCjtzCim9bupZvRfqCjtnCim9cOpZvULrCjtgCim9b+pZvYHqCjtnCim9cepZvW3rCjtkCim9b+pZvZ3qCjtqCim9b+pZvRfrCjthCim9b+pZvUcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5IE7e+KWQcv0q2Gz+w37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkgTt74pZBy/SrYbP6/ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+SBO3vilkHL9Kths/r9+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+SBO3vilkHL9Kths/sN+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+SBO3vilkHL9Kths/sN+3PkcTt74qZBy/SbYbP67ftz5IE7e+KWQcv0q2Gz+w37c+SBO3vilkHL9Kths/r9+3PkgTt74pZBy/SrYbP7Dftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Kths/rt+3PkcTt74qZBy/SbYbP67ftz5HE7e+KmQcv0m2Gz+u37c+RxO3vipkHL9Jths/rt+3PkcTt74qZBy/SrYbP67ftz5HE7e+KmQcv0q2Gz+u37c+RxO3vipkHL9Jths/rt+3Pv//fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//v9/P/3/fz8AAIA//f9/P///fz8AAIA//v9/P///fz8AAIA//f9/P/3/fz8AAIA//v9/P///fz8AAIA//f9/P/3/fz8AAIA//v9/P///fz8AAIA/AACAP///fz/+/38//f9/PwAAgD8AAIA/AACAPwAAgD8AAIA//P9/PwAAgD8AAIA/AACAP/3/fz/+/38//v9/P/3/fz8AAIA//v9/P/3/fz8AAIA//v9/P///fz/+/38//f9/PwAAgD/+/38//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/PwAAgD8AAIA//v9/P///fz8AAIA//f9/PwAAgD8AAIA//v9/P///fz/+/38//P9/P/3/fz8AAIA//P9/P///fz8AAIA//v9/PwAAgD8AAIA//v9/P///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP/3/fz8AAIA//v9/P///fz8AAIA//v9/P///fz8AAIA/AACAPwAAgD8AAIA//f9/P///fz8AAIA//v9/P///fz8AAIA//f9/P///fz8AAIA//v9/P///fz8BAIA/AACAP///fz8AAIA//f9/P///fz8AAIA//f9/P///fz8AAIA//f9/P0TwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbskTwELJrnDg9Q2SbshfPArJrnDg9nUn7sgdz+7JsnDg9V67qssE/orFsnDg95jaqs8MvZDBtnDg9dQppskgyirJtnDg98O4UMd0vTbFunDg9jWNJsj/dhrJunDg990sNs0nRfTJsnDg9ZuiAsvyqgLBsnDg9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/v9/P/3/fz///38//v9/P/7/fz/9/38//v9/P///fz8AAIA//v9/P/7/fz///38//v9/P/3/fz/9/38//v9/P/7/fz/9/38//v9/P/7/fz///38//v9/P///fz/9/38//v9/P/3/fz/9/38//v9/P/7/fz/9/38//v9/P/3/fz/9/38//v9/P/3/fz/9/38//v9/P/3/fz///38//v9/P/3/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/3/fz/8/38//v9/P/3/fz/9/38//v9/P///fz8AAIA//v9/P/z/fz/9/38//v9/PxfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvRfrCrthCim9b+pZvT/rCrtlCim9b+pZvULqCrtnCim9b+pZvU3qCrtvCim9bupZvTDqCrteCim9b+pZvaLqCrtiCim9b+pZvW3qCrtpCim9b+pZvT7qCrtkCim9cOpZvczqCrtoCim9b+pZvYHqCrtcCim9b+pZvenqCrtkCim9cOpZvYPqCrtgCim9cOpZvXDqCrtiCim9b+pZvbjqCrtqCim9b+pZvXrqCrtfCim9bupZvWDqCrtkCim9b+pZvQrqCrtmCim9bupZvffpCrtoCim9b+pZvc3qCrtnCim9b+pZvXvrCrtwCim9cepZvWLqCrttCim9b+pZvcnpCrtoCim9bepZvbDqCrtkCim9b+pZvcXqCrtsCim9cOpZvX3qCrtrCim9cOpZvWfqCrtqCim9b+pZvfvpCrthCim9b+pZvcDqCrtlCim9b+pZvUvqCrttCim9cOpZvYrrCrtqCim9b+pZvbPqCrtvCim9b+pZvVnqCrtuCim9bupZvfPqCrtnCim9b+pZvX/qCrtbCim9b+pZvWzqCrtnCim9bupZvTXqCrtrCim9b+pZvZHqCrtpCim9cOpZvTfqCrtlCim9b+pZvUbqCrtoCim9b+pZvW/qCrtjCim9b+pZvRfrCrthCim9b+pZvUcTt74qZBw/SbYbv67ftz5HE7e+KmQcP0m2G7+u37c+RxO3vipkHD9Jthu/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zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JveIv2KKQLSM9zK/JvfQENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAACZjJs9AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP7Fmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PbFmjj3nEEs9hjV7PSNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PyNdBjirB4q73O8wOmf/fz8jXQY4qweKu9zvMDpn/38/I10GOKsHirvc7zA6Z/9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAk7HzHQc9AABkMAAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAACwAAMCnAAAArgAAgD8AAAAsAADApwAAAK4AAIA/AAAALAAAwKcAAACuAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP7Fmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vbFmjj2CQ0y6Fp4+vdu4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P9u4gTc71Re7eaHCOdL/fz/buIE3O9UXu3mhwjnS/38/27iBNzvVF7t5ocI50v9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/P/3/fz+0/38/tv9/PwCAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMACAjbH3HQc9ANCkMAAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAYCsAAGopAgCmKgAAgD8AAGArAABqKQIApioAAIA/AABgKwAAaikCAKYqAACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuQPfh723Z7I8G1VXuSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPSrAjzrMBqU9c5l+PxYgiD0qwI86zAalPXOZfj8WIIg9KsCPOswGpT1zmX4/FiCIPRUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAPxUAgD8CAIA/HwCAP0G75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sUG75DHhCqQ9Aq16sZBd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+P5Bd8L2p33Q5FWqvOx46fj+QXfC9qd90ORVqrzseOn4/kF3wvanfdDkVaq87Hjp+PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/PwAAgD/3/38/+f9/P2fBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MGfBGTGa8FM8kdG2MHseXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/P3seXr1WW7e5zn8xO1Offz97Hl69Vlu3uc5/MTtTn38/ex5evVZbt7nOfzE7U59/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PwAAgD/9/38//P9/PxwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsRwVTTL4III9KBaYsYwbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaP4wbBD+U9Yc8Sm9lPeTEWj+MGwQ/lPWHPEpvZT3kxFo/jBsEP5T1hzxKb2U95MRaPwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/PwUAgD8MAIA/+f9/P9SRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02MdSRgjH5dwo9xi02Ma9vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP69vpz53ZumzYJUttJLscT+vb6c+d2bps2CVLbSS7HE/r2+nPndm6bNglS20kuxxP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72L7ywdrDaAsQ81s72LzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPzoSDLQHHQkpiSynNAAAgD86Egy0Bx0JKYkspzQAAIA/OhIMtAcdCSmJLKc0AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/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AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAApDHzHQc9AACNsAAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAECyA/3+vAAAALgAAgD8AABAsgP9/rwAAAC4AAIA/AAAQLID/f68AAAAuAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP7Fmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vbFmjr2CQ0y6uJw+vWgblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P2gblDcR1Rc7BZ7CudL/fz9oG5Q3EdUXOwWewrnS/38/aBuUNxHVFzsFnsK50v9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/P/3/fz+1/38/t/9/PwBAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsABAcLL1HQc9ALDlsAAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAQCsA4P8uABB6LQAAgD8AAEArAOD/LgAQei0AAIA/AABAKwDg/y4AEHotAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP+Zqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOeZqrz14++I84MMhOfQENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP3DbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPXDbELx8QaS9yFGZPQAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP27UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP+Zqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOeZqr714++I84MMhOfQENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1P/QENb8AAAAAAAAAAPIENT/0BDW/AAAAAAAAAADyBDU/9AQ1vwAAAAAAAAAA8gQ1PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP3DbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPXDbEDx8QaS9yFGZPQAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAAIAAAACAAACAvy69OzMAAACAAAAAgAAAgL8uvTszAAAAgAAAAIAAAIC/Lr07MwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP27UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAG7UjLGA+zA9AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAgEvvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAP0vvrrMAAAAAAAAAgAAAgD9L766zAAAAAAAAAIAAAIA/S++uswAAAAAAAACAAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPw==" + "byteLength" : 607240, + "uri" : "data:application/octet-stream;base64,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BgcPAAcGDwAGBw8ABwYPAAYHDwAHBg8ABgcPAAYHDwAGBw8ABgcPAAYHDwAGBw8ABgcPAAYHDwAGBwAABgcAAAYHAAAGBwAABgcPAAYHDwAHBgAABgcAAAcGAAAHBgAABwYAAAcGAAAHBg8ABwYPAAcAAAAHAAAABw8AAAcAAAAHDwYQBw8GEAcPEAYHDxAGBw8QBg8HEAYPEAcADxAHAA8QBwAPEAcADxAHAA8QBwAPEAAAEA8AAA8QAAAQDwAADxAAAA8QAAAPEAAADxAHAA8QBwYPEAcGDwYQBwcPBhAGDwcQBwYPEAYPBxAHBg8QBgcPEAcGDxAGBw8ABwYPAAcGDxAHBg8ABgcPAAcGDwAHBg8ABgcPAAcGDwAGBw8ABgcPAAcGDwAGBw8ABgcPAAYHDwAGBw8ABgcPAAYHDwAGBw8ABgcPAAYHDwAGBw8ABgcAAAYHAAAGBwAABgcAAAYHAAAGBwAABgcAAAYHAAAGBwAABgcPAAYHAAAHBgAABwYPAAcGAAAHBgAABwYAAAcGAAAHBgAABwYAAAcGDwAHBg8ABwYPAAcGDwAHAAAABwAAAAcAAAAHAAAABwAAAAcPBhAHDwYQBw8GEAcPBhAHDxAGBw8QBgcPEAYPBxAGDxAHAA8QBwAPEAcADxAHAA8QBwAPEAcADxAHAA8QBwAPEAcADxAAABAPAAAQDwAADxAAABAPAAAQDwAADxAAABAPAAAPEAAADxAAABAPAAAPEAAADxAHBg8QBwAPEAcGDxAGBw8HEAYPBwYQDwYHEAcPBhAHBg8QBg8HEAcGDxAHBg8QBgcPEAcGDxAHBg8QBgcPEAcGDxAHBg8ABwYPAAcGDwAGBw8ABgcPAAcGDwAGBw8ABgcAAAYHAAAGBwAABgcAAAcGAAAHBgAABwYAAAcGAAAHAAAABw8GEAcPBhAHDxAGDxAHAA8QBwAPEAcADxAAABAPAAAQDwAAEA8AAA8QBwAPBxAGBw8GEAcGDxAHBg8QBwYPEAYHDxAHBg8QBgcPAAcGDwAGBw8ABwYPAAcGDwAHBg8ABgcPAAYHDwAGBw8ABgcPAAYHDwAGBw8ABgcPAAYHDwAGBwAABgcAAAYHAAAGBwAABgcPAAYHDwAGBw8ABgcPAAcGDwAHBg8ABwYAAAcGDwAHBg8ABwYPAAcPBgAHDwYQBw8GAAcPBhAHDwYQ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EACQADoARgAqADoAKgAZAO8B9QFIAO8BSAA2APwB6wEgAPwBIACJAIoAHwANAIoADQCFADcASQArADcAKwAbAP4B9wFKAP4BSgCXAPYB7gE0APYBNABLAEwANQAaAEwAGgAyAJgATAAyAJgAMgCSAO4B+AFNAO4BTQA0APgB7wE2APgBNgBNAE4ANwAbAE4AGwAzADUATgAzADUAMwAaAPsB+QFQAPsBUACMAPkB8AE5APkBOQBQAE8AOAAYAE8AGAAwAIsATwAwAIsAMACHAPAB+gFSAPABUgA5APoB8QE7APoBOwBSAFEAOgAZAFEAGQAxADgAUQAxADgAMQAYAJAAmQBTAJAAUwAqAJkAkgAyAJkAMgBTAFMAMgAaAFMAGgA8ACoAUwA8ACoAPAAZABkAPABUABkAVAAxADwAGgAzADwAMwBUAFQAMwAbAFQAGwA9ADEAVAA9ADEAPQAYAIQAjQBfAIQAXwAiAI0AhgBaAI0AWgBfAGAAVQB5AGAAeQBlACIAXwA+ACIAPgAVABUAPgBhABUAYQAtABcAIwBwABcAcABnAC8AFwBnAC8AZwBrAC0AYQA/AC0APwAUACMAFwBXACMAVwBZABYALgBaABYAWgBbAC8AFgBbAC8AWwBcABcALwBcABcAXABXAF0AQgBeAF0AXgA/AEIAIwBZAEIAWQBeAC4AVQBfAC4AXwBaAFUAYAA+AFUAPgBfAGAAVgBhAGAAYQA+AFYAXQA/AFYAPwBhAGQAdwCDAGQAgwB7AGYAbgCAAGYAgAB8AGoAZgB8AGoAfAB+AHgAcgCBAHgAgQCDAC4AFgBjAC4AYwBzAFYAYABlAFYAZQBtACMAQgB2ACMAdgBwABYALwBrABYAawBjAF0AVgBtAF0AbQBpAFUALgBzAFUAcwB5AH0AfACAAH0AgACCAIMAgQB6AIMAegB7AHsAegB+AHsAfgB/AH8AfgB8AH8AfAB9AHQAaAB9AHQAfQCCAHEAYgB6AHEAegCBAGwAZAB7AGwAewB/AG8AdQCCAG8AggCAAGIAagB+AGIAfgB6AGgAbAB/AGgAfwB9ACEADwCGACEAhgCNAA4AIQCNAA4AjQCEAB0AiwCHAB0AhwAMAOoB+wGMAOoBjAAcAEkAigCFAEkAhQArAPUB/AGJAPUBiQBIADAARQCIADAAiACHAEUAKwCFAEUAhQCIACkAEQCSACkAkgCZABAAKQCZABAAmQCQACgAmACSACgAkgARAO0B/QGWAO0BlgAnAEYAlACQAEYAkAAqAPQB/wGVAPQBlQBHACwAXgCTACwAkwCRAF4AWQCPAF4AjwCTAJoA3AA6AZoAOgHaANwAmwDbANwA2wA6AToB2wCdADoBnQDdANoAOgHdANoA3QCcAJwA3QA7AZwAOwHeAN0AnQDfAN0A3wA7ATsB3wCfADsBnwDgAN4AOwHgAN4A4ACeAJ4A4AA8AZ4APAHhAOAAnwDiAOAA4gA8ATwB4gChADwBoQDjAOEAPAHjAOEA4wCgAKAA4wA9AaAAPQHkAOMAoQDlAOMA5QA9AT0B5QCjAD0BowDmAOQAPQHmAOQA5gCiAKIA5gA+AaIAPgHnAOYAowDoAOYA6AA+AT4B6AClAD4BpQDpAOcAPgHpAOcA6QCkAKQA6QA/AaQAPwHqAOkApQDrAOkA6wA/AT8B6wCnAD8BpwDsAOoAPwHsAOoA7ACmAKYA7ABAAaYAQAHtAOwApwDuAOwA7gBAAUAB7gCpAEABqQDvAO0AQAHvAO0A7wCoAKgA7wBBAagAQQHwAO8AqQDxAO8A8QBBAUEB8QCrAEEBqwDyAPAAQQHyAPAA8gCqAKoA8gBCAaoAQgHzAPIAqwD0APIA9ABCAUIB9ACtAEIBrQD1APMAQgH1APMA9QCsAKwA9QBDAawAQwH2APUArQD3APUA9wBDAUMB9wCvAEMBrwD4APYAQwH4APYA+ACuAK4A+ABEAa4ARAH5APgArwD6APgA+gBEAUQB+gCxAEQBsQD7APkARAH7APkA+wCwALAA+wBFAbAARQH8APsAsQD9APsA/QBFAUUB/QCzAEUBswD+APwARQH+APwA/gCyALIA/gBGAbIARgH/AP4AswAAAf4AAAFGAUYBAAG1AEYBtQABAf8ARgEBAf8AAQG0ALQAAQFHAbQARwECAQEBtQADAQEBAwFHAUcBAwG3AEcBtwAEAQIBRwEEAQIBBAG2ALYABAFIAbYASAEFAQQBtwAGAQQBBgFIAUgBBgG5AEgBuQAHAQUBSAEHAQUBBwG4ALgABwFJAbgASQEIAQcBuQAJAQcBCQFJAUkBCQG7AEkBuwAKAQgBSQEKAQgBCgG6ALoACgFKAboASgELAQoBuwAMAQoBDAFKAUoBDAG9AEoBvQANAQsBSgENAQsBDQG8ALwADQFLAbwASwEOAQ0BvQAPAQ0BDwFLAUsBDwG/AEsBvwAQAQ4BSwEQAQ4BEAG+AL4AEAFMAb4ATAERARABvwASARABEgFMAUwBEgHBAEwBwQATAREBTAETAREBEwHAAMAAEwFNAcAATQEUARMBwQAVARMBFQFNAU0BFQHDAE0BwwAWARQBTQEWARQBFgHCAMIAFgFOAcIATgEXARYBwwAYARYBGAFOAU4BGAHFAE4BxQAZARcBTgEZARcBGQHEAMQAGQFPAcQATwEaARkBxQAbARkBGwFPAU8BGwHHAE8BxwAcARoBTwEcARoBHAHGAMYAHAFQAcYAUAEdARwBxwAeARwBHgFQAVABHgHJAFAByQAfAR0BUAEfAR0BHwHIAMgAHwFRAcgAUQEgAR8ByQAhAR8BIQFRAVEBIQHLAFEBywAiASABUQEiASABIgHKAMoAIgFSAcoAUgEjASIBywAkASIBJAFSAVIBJAHNAFIBzQAlASMBUgElASMBJQHMAMwAJQFTAcwAUwEmASUBzQAnASUBJwFTAVMBJwHPAFMBzwAoASYBUwEoASYBKAHOAM4AKAFUAc4AVAEpASgBzwAqASgBKgFUAVQBKgHRAFQB0QArASkBVAErASkBKwHQANAAKwFVAdAAVQEsASsB0QAtASsBLQFVAVUBLQHTAFUB0wAuASwBVQEuASwBLgHSANIALgFWAdIAVgEvAS4B0wAwAS4BMAFWAVYBMAHVAFYB1QAxAS8BVgExAS8BMQHUANQAMQFXAdQAVwEyATEB1QAzATEBMwFXAVcBMwHXAFcB1wA0ATIBVwE0ATIBNAHWANYANAFYAdYAWAE1ATQB1wA2ATQBNgFYAVgBNgHZAFgB2QA3ATUBWAE3ATUBNwHYANgANwFZAdgAWQE4ATcB2QA5ATcBOQFZAVkBOQGbAFkBmwDcADgBWQHcADgB3ACaAJoA2gCaAZoAmgFaAdoAnABcAdoAXAGaAZ0A2wCbAZ0AmwFdAdsAmwBbAdsAWwGbAZwA3gCcAZwAnAFcAd4AngBeAd4AXgGcAZ8A3wCdAZ8AnQFfAd8AnQBdAd8AXQGdAZ4A4QCeAZ4AngFeAeEAoABgAeEAYAGeAaEA4gCfAaEAnwFhAeIAnwBfAeIAXwGfAaAA5ACgAaAAoAFgAeQAogBiAeQAYgGgAaMA5QChAaMAoQFjAeUAoQBhAeUAYQGhAaIA5wCiAaIAogFiAecApABkAecAZAGiAaUA6ACjAaUAowFlAegAowBjAegAYwGjAaQA6gCkAaQApAFkAeoApgBmAeoAZgGkAacA6wClAacApQFnAesApQBlAesAZQGlAaYA7QCmAaYApgFmAe0AqABoAe0AaAGmAakA7gCnAakApwFpAe4ApwBnAe4AZwGnAagA8ACoAagAqAFoAfAAqgBqAfAAagGoAasA8QCpAasAqQFrAfEAqQBpAfEAaQGpAaoA8wCqAaoAqgFqAfMArABsAfMAbAGqAa0A9ACrAa0AqwFtAfQAqwBrAfQAawGrAawA9gCsAawArAFsAfYArgBuAfYAbgGsAa8A9wCtAa8ArQFvAfcArQBtAfcAbQGtAa4A+QCuAa4ArgFuAfkAsABwAfkAcAGuAbEA+gCvAbEArwFxAfoArwBvAfoAbwGvAbAA/ACwAbAAsAFwAfwAsgByAfwAcgGwAbMA/QCxAbMAsQFzAf0AsQBxAf0AcQGxAbIA/wCyAbIAsgFyAf8AtAB0Af8AdAGyAbUAAAGzAbUAswF1AQABswBzAQABcwGzAbQAAgG0AbQAtAF0AQIBtgB2AQIBdgG0AbcAAwG1AbcAtQF3AQMBtQB1AQMBdQG1AbYABQG2AbYAtgF2AQUBuAB4AQUBeAG2AbkABgG3AbkAtwF5AQYBtwB3AQYBdwG3AbgACAG4AbgAuAF4AQgBugB6AQgBegG4AbsACQG5AbsAuQF7AQkBuQB5AQkBeQG5AboACwG6AboAugF6AQsBvAB8AQsBfAG6Ab0ADAG7Ab0AuwF9AQwBuwB7AQwBewG7AbwADgG8AbwAvAF8AQ4BvgB+AQ4BfgG8Ab8ADwG9Ab8AvQF/AQ8BvQB9AQ8BfQG9Ab4AEQG+Ab4AvgF+AREBwACAAREBgAG+AcEAEgG/AcEAvwGBARIBvwB/ARIBfwG/AcAAFAHAAcAAwAGAARQBwgCCARQBggHAAcMAFQHBAcMAwQGDARUBwQCBARUBgQHBAcIAFwHCAcIAwgGCARcBxACEARcBhAHCAcUAGAHDAcUAwwGFARgBwwCDARgBgwHDAcQAGgHEAcQAxAGEARoBxgCGARoBhgHEAccAGwHFAccAxQGHARsBxQCFARsBhQHFAcYAHQHGAcYAxgGGAR0ByACIAR0BiAHGAckAHgHHAckAxwGJAR4BxwCHAR4BhwHHAcgAIAHIAcgAyAGIASABygCKASABigHIAcsAIQHJAcsAyQGLASEByQCJASEBiQHJAcoAIwHKAcoAygGKASMBzACMASMBjAHKAc0AJAHLAc0AywGNASQBywCLASQBiwHLAcwAJgHMAcwAzAGMASYBzgCOASYBjgHMAc8AJwHNAc8AzQGPAScBzQCNAScBjQHNAc4AKQHOAc4AzgGOASkB0ACQASkBkAHOAdEAKgHPAdEAzwGRASoBzwCPASoBjwHPAdAALAHQAdAA0AGQASwB0gCSASwBkgHQAdMALQHRAdMA0QGTAS0B0QCRAS0BkQHRAdIALwHSAdIA0gGSAS8B1ACUAS8BlAHSAdUAMAHTAdUA0wGVATAB0wCTATABkwHTAdQAMgHUAdQA1AGUATIB1gCWATIBlgHUAdcAMwHVAdcA1QGXATMB1QCVATMBlQHVAdYANQHWAdYA1gGWATUB2ACYATUBmAHWAdkANgHXAdkA1wGZATYB1wCXATYBlwHXAdgAOAHYAdgA2AGYATgBmgBaATgBWgHYAZsAOQHZAZsA2QFbATkB2QCZATkBmQHZAekB6AHfAekB3wHdAegB5wHlAegB5QHfAecB5gHjAecB4wHlAd4B2gHgAd4B4AHkAeUB4gHcAeUB3AHfAdsB3gHoAdsB6AHpAd4B5AHnAd4B5wHoAeQB4QHmAeQB5gHnASwAkQD/ASwA/wH0ARMAjwD9ARMA/QHtAVoAhgD8AVoA/AH1AQ4AhAD7AQ4A+wHqAS0AFADxAS0A8QH6ARUALQD6ARUA+gHwASIAFQDwASIA8AH5AYQAIgD5AYQA+QH7AVwAWwDvAVwA7wH4AVcAXAD4AVcA+AHuAVkAVwDuAVkA7gH2AY4AWAD3AY4A9wH+AYYADwDrAYYA6wH8AVsAWgD1AVsA9QHvAZEAEgDsAZEA7AH/ARQALAD0ARQA9AHxASQAEwDtASQA7QHzARIAJADzARIA8wHsARwAQQDyARwA8gHqAUEAIADrAUEA6wHyAQAAgD8AAACAAAAAAAAAAIAAAACAAACAPwAAAIAAAAAAAAAAAAAAAIAAAIA/AAAAgAAAAIAi+iK+AAAAgAAAgD/pvn2/ctQFPpkRrrwAAACAaZYHvvB0er+L5CI+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exU0u7MFgL2qnXQ9Q8cRPJYHkr1X9xM9mXFKO58HXL3/oao9HvJwPJe+WL3zabM9y/yWPBYxlb1+UgM9fcoWO8zAmb24WBM9AofrO9wxnL0KFwM9rOy/OzDanb2fcO48GHisO+vtpb3JjQM99Ka7O/qpr72IBAQ9PGG3O+G5pr1rfO88or+rO5SZr703iPA8LgerO39Jp714W9Q8lgTgO39Jp714W9Q8lgTgO/YEn70eONM8mqPeO/YEn70eONM8mqPeOwiOr73SftU8lGXhOwiOr73SftU8lGXhO0eMp72c94A8kyurPKmIr71tLII8EzCrPOWPn72ThX88ESerPEWMp73z0Wo8VFq/POOPn70DcWg8L1q/PKmIr73jMm08elq/PEWMp73NkVQ8XX3VPKmIr73361Y8Un3VPOOPn72lN1I8Z33VPEWMp72KLu07UrMbPeOPn73og+g7RrMbPamIr70q2fE7XbMbPUWMp72gXNo7ar4gPamIr72yBt87ar4gPeOPn72OstU7aL4gPUWMp70vAMg7H94lPamIr70Gqsw7Gt4lPeOPn71YVsM7JN4lPUWMp72oYis7krNGPeOPn73YDyI7lrNGPamIr712tTQ7j7NGPUWMp70wTag4t1ZTPeOPn71QDIK4uFZTPamIr71gU2k5tFZTPUWMp71YfZK6RrtGPUWMp71YfZK6RrtGPeOPn73uIqW6SbtGPeOPn73uIqW6SbtGPamIr72Dr3+6QrtGPamIr72Dr3+6QrtGPUeMp70sxsK6oeAlPeOPn73Ja9W6neAlPaqIr72QILC6peAlPUeMp72KZci6TakgPeOPn70hC9u6SqkgPaqIr73zv7W6UakgPUeMp72qLsu6v2gbPaqIr70Vibi6xWgbPeOPn70+1N26uGgbPUeMp73pY9K6fDvVPOOPn71/CeW6DzrVPKqIr71Svr+66DzVPEeMp73qY9K6/Vy+PKqIr71Uvr+65l++POOPn72ACeW6Flq+PEeMp73rY9K67z6pPOOPn72CCeW6kTapPKqIr71Vvr+6S0epPLKJp70wntK6PILoO4Qdob19t+C68gvmO+H1rb3khMS6iPjqO2GEp71eWKm6kkWNO0HQnb1XALy62seHO4I4sb1lsJa6SsOSO2mSp70vts06cMkvudbztL0XOeQ6YNkrOdbztL0XOeQ6YNkrOfwwmr1FM7c6FNsCuvwwmr1FM7c6FNsCuqLkp71ge1o70i/ou9mlsb1ez2M7nH7fu9mlsb1ez2M7nH7fu2sjnr1iJ1E7COHwu2sjnr1iJ1E7COHwu7EiqL1aALM7RdwlvLEiqL1aALM7RdwlvEWnob0Feq87A4sovEWnob0Feq87A4sovB6err2vhrY7iC0jvB6err2vhrY7iC0jvCgqqL0xQFQ8fAIkvAkEsL20eVY8fdIgvEhQoL2wBlI8ezInvC/wp73VeLs8vqEXvC/wp73VeLs8vqEXvEaRn73rV7o8eJ8bvEaRn73rV7o8eJ8bvBdPsL3Ambw8BKQTvBdPsL3Ambw8BKQTvHRZp724i+08ek0avNJXnr0Yf+w8dBcgvBVbsL1amO48gIMUvF+Dpr0KwQQ9f6YpvBZssL30OAU94E4hvKmanL0gSQQ9H/4xvPVflb0kAgQ9lPD1u2+ckr2HvAM9Stwsu2+ckr2HvAM9StwsuzqPl71h/Os8DNrduyADmb0337k8Qs3RuyADmb0337k8Qs3Ru/HSlL0iXuw8EVcZu/HSlL0iXuw8EVcZu1i6lb1s1Lk8CBODuVi6lb1s1Lk8CBODuVi6lb1s1Lk8CBODuSpRl73p5O08T6UJO8+5mL2PB9A8zJuJO8+5mL2PB9A8zJuJOxpSmb0jFHY8AfqpPBpSmb0TbWA8AvS+PJxslb3NuUo8h8moPJxslb0fEzk8sI6+PBMclL3GxNk7QUeoPBMclL3+D8U7h2W+PAQHlr1gYT08svglup1slb3V/4A6HV+oPJ1slb3V/4A6HV+oPJxslb2iHUs6NmG+PJxslb2iHUs6NmG+PBlSmb0UeMC6j8moPBpSmb0z5sO6E16+PBlSmb29R8e6IurUPJ1slb3yBBU6tZbUPJ1slb3yBBU6tZbUPKtYmb3yF8+62WobPT1amb29Vs26gKggPVePlb3oyaG543AbPVePlb3oyaG543AbPdmflb0sw865P6ggPdmflb0sw865P6ggPQpLlL1OVCw703cbPf5klL2C9Bc72aYgPWSPlb1/drg7D4MbPRMclL3srbA7OHHUPJxslb0jyCc8w5XUPBpSmb0zZks8zwfVPLFYmb22veA7JqYbPTlamb3Eds07c7EgPfVcmb3OUrk7E78lPZGflb0TFqc7B6UgPXTBlb0rmpM7ubclPZuclL3c5QE7LMklPfhMlr2+Fyk7n9c0PS1vmb2k2B87G3I/PXq5lb0mty06asA4PfhMlr3jKi66otw0PfhMlr3jKi66otw0PaVgmb3gygu4KJ1KPS1vmb1rMaC6VHg/PS1vmb1rMaC6VHg/PQldmb3e6ce6z9slPXbClb3Q7PW55dQlPXbClb3Q7PW55dQlPcAXmr0QbUs8/h7guxMKt73CeQU9nzrLu78/ub1YWQU9wtDhur8/ub1YWQU9wtDhugmItr0JK+88Ksa7u8q0uL32zu88Oe27usq0uL32zu88Oe27ugNDtr3GPL08gMO1uwNDtr3GPL08gMO1uwwGub3thL08FGwlOgwGub3thL08FGwlOgwGub3thL08FGwlOoeltb0gCFI8XXjHu7seub3QskQ8ACCEOYTLur3WuOk7iZqoPIXLur2EmdQ7ene+PLeCub3BhL06TaWoPLeCub3BhL06TaWoPLaCub1qz1I8VBOpPLaCub3y3kA8f56+PNextb3tcn08miKqPNextb2bl2c8kvu+PDvLtb3FctM8EEiYOzvLtb3FctM8EEiYO5cOtr0KE/E8LWciOxKNtr0QyAQ9Ad0iO9extb0CaFI8ggrVPLaCub1zWi883JvUPGertb34hu47XqYbPemptb3qO9s7e7EgPbpgub2nRsc7LYMbPetQub1i3bU7F6UgPaWdur3QQUo79XcbPUqEur2S0TU75qYgPcdgub1YQRU5/3AbPcdgub1YQRU5/3AbPYPLur1w5L87hnjUPLeCub1x/4U6Yp3UPLeCub1x/4U6Yp3UPLaCub1ff6E6k3C+PLaCub1ff6E6k3C+PNextb0rNJC6Q+/UPNextb0uxIy6I2m+PGyrtb3YEZi682obPeSptb0lUpa6jaggPSintb0f55C629slPaVQub0gUms4TKggPaVQub0gUms4TKggPdMuub2AVaG38dQlPdMuub2AVaG38dQlPfJNur2Crx87OcklPYCnuL2oT2O5W9w0PYCnuL2oT2O5W9w0PWyVtb0jcVK6Nng/PWyVtb0jcVK6Nng/PZs3ub2305E6K8A4PYCnuL2ibUY7Vtc0PaKjtb1MiMo5G51KPWyVtb0uVTs7+3E/PTyntb36Fcc79b4lPc8vub1CWaI70LclPdextb2DQ4m6/PSoPMervr1R4bI9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0lMNP4Dt+j1qgQQ/YIT2PdJTDT9ghPY9kA4FP4Dt+j2QDgU/YIT2PbebBT+A7fo9t5sFP2CE9j3eKAY/gO36PbebBT+oVv896wQKP8R9CD7eKAY/qFb/PcR3CT/EfQg+nuoIP8R9CD4EtgY/qFb/PcR3CT8oSQY+xHcJP5AUBD6e6gg/KEkGPipDBz+oVv89nuoIP5AUBD5Q0Ac/qFb/PSpDBz+A7fo9KkMHP2CE9j1Q0Ac/gO36PQS2Bj+A7fo9BLYGP2CE9j0EtgY/IBvyPd4oBj9ghPY93igGPyAb8j23mwU/IBvyPd4oBj/ose09xHcJPxgN3D0EtgY/6LHtPZ7qCD8YDdw9t5sFP+ix7T3rBAo/GA3cPcR3CT9QduA9xHcJP3Df5D0qQwc/6LHtPZ7qCD9QduA9nuoIP3Df5D1Q0Ac/6LHtPSpDBz8gG/I9UNAHPyAb8j1Q0Ac/YIT2PXhdCD8gG/I9eF0IP2CE9j2e6gg/IBvyPXhdCD/ose09nuoIP6hI6T2e6gg/6LHtPcR3CT+oSOk9xHcJP+ix7T3rBAo/6LHtPcR3CT8gG/I9xHcJP2CE9j3Edwk/gO36PZ7qCD9ghPY9nuoIP4Dt+j2e6gg/qFb/PXhdCD+A7fo9nuoIP/TfAT54XQg/qFb/PcR3CT/03wE+xHcJP6hW/z3rBAo/qFb/PZAOBT8gG/I9hDkMP2CE9j3U+8E+RPIWPpS0vT6wqfA9JHe8PnANvD1Im70+QIyLPTdNPj4tRg0/xgQ/PpyMEz+Yzj0+VhkaP3kNOj4aMCI/ygE3PjS+Kj9D/A0+9hgtP3pOzD2mby8/sqnqPkRJSj7Al9c+0JxEPvInxj5wDjg+TPzBPsxrrz4+tb0+hLq+Pvl3vD6E4cs+R4U+PomHaD9JnL0+wAHYPsM0Pz64PmI/IvU9PiGwWz/CJzo+zJdTP2cQNz6BCEs/ZgUOPsavSD/Oqeo+EMCVPhJVzD0DWkY/8JfXPmiWmD48KMY+uN2ePv//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////CQoICwkKCAsJCggLCQoICwkKCAsJCAoLCQgKBwkICgcJCAoHCQgKBwkICgcJCAoHCQgKBwkICgcJCAoLCQgKBwkICg8JCAoLCQgKDwkICg8JCAoPCQgKDwkICg8JCAoPCQgKDwkICg8JCAoPCQgKDwkICg8JCAoPCQgKDwkICg8JCAoPCQgKDwkICg8JCAoPCQgKDwkICg8JCAoPCQgKDwkICg8JCAoPCQgKDwkIDwoJCAoPCQgPCgkICg8JCA8KCQgKDwkIDwoJCAoPCQgKDwkIDwoJCA8KCQgKDwkIDwoJCAoPCQgPCgkICg8JCA8KCQgKDwkIDwoJCAoPCQgPCgkICgcJCA8KCQgKBwkICgcJCA8KCQgPCgkICgcJCAoHCAkPCggJCgcICQcPCAkKBwcKCAkKBwgJBw8KCAcKDwYHDwoGCgcLBgcGCg8HCgYLBwYPCgYHCgsGBwoLBgcPCgYHAAoGBwALBgcAFQAGBxUGAAcVBgAHFQAGARwAFQYBAAYVHAABFRcAARcGAAEVAAABFxUAARUcAAEXAAABFxUBABUXAAEXFQABFxUBABcVAAEXFQABFhcAARYXAAEWAAABFgAAARcAAAEXAAABFBcAARYUAAEXFAABFhcAARYdAAEXFAABFBYAFAEXAAEWFAAUARcAARQWABQBFwAUARcAFAEXABQBFwAUARcAFAEXABQBFgAUARYUAAEXFAABFxQAARcAFAEWFAABABQAARcUAAEXFAABFhQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAFgAUAAAAFAAGBxQABgcUAAYHABQGBwAUBgcAFAYHABQGBxQABgcUAAYHABQGBwAUBgcABhQHAAYUBwAGBxQABhQHBgAHFAAGBwEGAAcUBgcACgYHABQGBwAKBgcKCwYHCgsGBwoPBwYKCwcGCg8HBgoLBwoPBgoHCwYHCg8GBwoICQcKDwgKBwgJCAkKBwgJCgcICQcPCQgKBwkIDwoJCAoHCQgKBwkICgcJCAoHCQgKBwkIDwoJCA8KCQgKBwkICgcJCA8KCQgKBwkIDwoJCAoHCQgKDwkIDwoJCAoLCQgKDwkICgAJCA8KCQgKAAkICg8JCAoACQgKAAkKCAAJCAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJCAoACQgKAAkICgAJCAAACQgKDwkICg8JCAoACQgKDwkICg8JCAoACQgKAAkICg8JCAoPCQgKDwkICg8JCA8KCQgKDwkIDwoJCAoPCQgKDwkIDwoJCAoPCQgKDwkICg8JCAoPCQgKDwkICg8JCAoPCQgKDwkICg8JCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoPCQgKAAkICgAJCAoACQgKAAkICgAJCAoACQgKAAkICgsJCAoLCQgKAAkICgsJCAoLCQgKCwkICwoJCAoLCQsICgkLCggJCwoICQsKCAkLCggJCwoICQoLCAkKCwgJCgsICQoLCAkKCwgJCgsICQoLCAkKCwgJCgsICQoLCAkKCwgJCgsICQoLCAkKCwgJCgsICQoLCAkKCwgJCgsICQoLCAkKCwgJCgsICgkLCAkKCwgKCQsICgkLCAoLCQAKCwkICwoJAAsKCQALCgkACwoJAAsKCQALCgkACwoJAAsKCQALCgkACwoJAAsKAAALCgAACwoAAAsKAAALDAAACwwAAAsMAAALDAAACwwAAAwLDQALDA0ACwwNAAsMAAAMCwAACwwNCgsMDQALCgwHCwwNCgsKDAYLDA0ACwwNCgsMDQoLDAoGCwwKBgwLDQAMCw0ACwwNCgsMCgcMCw0ACwwNCgsKDAcMCw0ACwwNAAwLDQALDAoACwwNAAwLDQALDAAACwoAAAsKCQALCgAACwoJAAsKBxUKCwcJCwoHBgoLBwYLBwoGCgsHCQoLBxUKCwkICgsJCAoLCQgKCQsICgkICwoJCwgJCggLCQoLCAkKCwgJCgsICgkLCAkKCwgJCgsICgsJCAoJCwgKCwkACwoJAAsKCQAJCgsICQoICwkKCAsJCAoLCQgKCwkICgsKCAkHCgcICQoHCwYHCgsGBwYKCwcKBgsGBwoLBgcBCgAGBxUABgcBABUGAQABFQYVAQAAAQAVAAEVAAABFQAAARUAAAEVAAABFQIAAQIDFQEVAAIBFQIAAQIDAAECFQABFQAWAQIDAAECFgMBAhYDAQAWFQEAFhUCAQMAAgEDAAEWAgMBFgAAAgEDAAEWAAIBFgAAAwECAAECABYBFgAAAwECFgEWAwABAwIWAQAWAwEDAhYBABYCAQAWAgECAwABAgMAAQACFQECAwABAhUDAQIDAAECAwABAgMVAQIDAAECAwABAgMAAgEDAAIBAwADAgEAAgMBAAIBAwADAgEAAwIBAAIDAQADAAAAAwQAAAMCAAADAAAAAwQAAAMAAAADBAAAAwIBAAMCAQADAgEAAwQAAAMEAAADAgEAAwAAAAMBAgADAAAAAwECAAMAAAADAQIAAwAAAAMBAgADAAAAAwECAAMBAgADAAAAAwAAAAMCAQADAAAAAgEDAAIBAwADBAAAAwQAAAMEAAADBAAAAwQAAAMEAAADBAAAAwQAAAQDAAAEAwAABAMAAAQDAAAEAwAABAMAAAQAAAAEBQAABAAAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAUAAAAFBAAABQQAAAUAAAAFBAAABQQAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAwAEBQMABAUDAAQFAwAEBQMABAMFAAQDAAAEAwAABAMAAAQDAAAEAwAABAMAAAQDAAAEAwAABAMAAAQDAAAEAwAABAMAAAQDAQADBAEAAwQBAAMEAQADBAEAAwQAAAMEAAADBAAAAwQAAAMEAAADBAAAAwQAAAMEAAADBAAAAwQAAAMEAAADBAAABAMBAAQDAQADBAAAAwQAAAQDAAAEAwAABAMAAAMEAAAEAwAABAMAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQDAAAEBQAABAUAAAQFAAAEBQAABAUAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABAUAAAQFAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAQDAQADBAAAAwQAAAMEAAADBAAAAwQAAAMEAAAEAwAABAMAAAQDAAAEAwAABAMAAAQDAAAEAwAABAUAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAEAwAABAMAAAMEAAADBAAABAUAAAQFAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAQFAAAEBQAABQQAAAUEAAAFBAAABQQAAAUAAAAFBAAABQQAAAUEAAAFBAAABQQAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQQAAAUEAAAEBQAAAgMBAAIDAQACAQMAFgEAFAABFhQWAQAAABYBAAAWAQAAFgEUABYGAQAGFgEAFgEAAAYHAQAGBwEGBwoWBgcBFAYHCgsGBwoLBwoLBgoLBwYKBwsGCgsHBgoHCQgKCwkHCgkIBwoJCAsJCAoHCQoICwkICgcJCAoHCQoICwkKCAsJCAoHCQoICwkKCAsJCggLCQoICwkKCAsJCAoLCQgLCgkKCAAJCggLCQoAAAkKAAAJAAAACQoAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJCAAACQAAAAkAAAAJAAAACQAAAAkKAAAJCgAACQoIAAkKCAAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoACQgAAAkIAAAJAAAACQAAAAkAAAAJAAAACQgAAAkIAAAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoACQoAAAkKAAAJCgAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkKAAAJCgAACQoAAAkKAAAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAoACQgKAAkICgAJCAAACQgAAAkIAAAJAAAACQAAAAkAAAAJAAAACQgAAAkICgAJCAoACQgKAAkICgAJCAoACQgKAAkLCAoJCAoACQsKAAkKAAAJCgAACQsKAAkKCwAJCwoACQsKAAkKCwAJCgsACQoLAAkLCggJCwoACQoLAAkKCwAJCgAACQoAAAkAAAAJCgAACQoAAAkAAAAJAAAACQoAAAkAAAAJCgAACQAAAAkKAAAJAAAACQoAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQoAAAkAAAAJCgAACQoAAAkKCwAJCgAACQoLCAkLCAoJCggLCQsICgkKCwgJCwoICQoLCAkKCwgJCggLCQoLCAkKCwgJCgsICQoICwkKCAsJCggLCQoICwkKCwgKCQgLCgkLCAoLCQgKCwkICgsHCQoLBwkLCgcGCwoHBgsKBwYLCgYHCwoGDAsKBgwGBwoLBgcKAQYHCgEGBwEABgcBAAYHAQAGAAcBBgAHAQYABwEAARYGAAEWFQABFhUBABYVAQAWFQEAFhUBFgAAARYAFQEWABUBFQAAAQAVAAABFQYGAAEVBgcBFQYHCwoLBwoMCQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAAGBxQAFAYHAAEWFAAUAQYAARYUABQBFgAUARYAFAEWABQBFgAUBgEAFAYBFAABBgAUBgcAFAYHFAAGBxQAFgAUABYAFAAWHRQAFgAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUHRYAFAABFgwLDQAMCw0ADQwLAA0OAAANDAsODQwAAA0MAAANDgAADQ4AAA0MAAANDgAADA0LAA0MAAAMDQsADQ4AAA0MCw4NDgwADA0LAA0MCw4NDgAADA0LAA0MCw4NDAsODQ4AAA0OAAANDAsODQ4AAA0MCw4NDgAADQ4AAA0OAAANDgAADQ4AAA0OAAANDgAADQ4AAA0OAAANDgAADQ4AAA0OAAANDgAADQ4AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAOAAAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAODQAADg0AAA4NAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADQ4AAA0OAAANDgAADQ4AAA4NAAAODQAADg0AAA0OAAANDgAADQ4AAA0OAAANDgAADQ4AAA0OAAANDgAADQ4AAA0OAAANDgAADQ4AAA0OAAANDgAADQ4AAAwLDQAMCw0ADAsNAAwLDQAMDQsADAsNAAwNCwAMCw0ADAsNAAwLDQAMCw0ACwwNAAkICgAJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwAJCA8ACQgPCgkIDwAJCA8ACQgPAAkIAAAJCA8ACQgAAAkIAAAJCAAACQgAAAkIAAAJCAAACQgAAAkIAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJCA8ACQ8IAAkIDwAJCA8ACQgPCgkIDwAJCA8KCQgPEAkIDwcJCA8HCQgPBwkIDwcJCA8HCQgPBwkIDwcJCA8HCQgPBwkIDwcICQ8HCAkPBwcPCAkPBwgJBw8KBg8HEAYHBg8QBwYPEAYHDxAGBw8QBgcADwYHAA8GAAcUAAYHFwAGFAcABgcUABQGBwAGFAcAFAYHABQGBwAUBgcAFAYHFAAGBxQABgcUAAAAFAAdABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAXABQAFx0UABcBABQXHQAUFwEAFBcBABQXHQAUFx0AFBcBABcUHQAXFB0AFx0UABcdAQAXHRYAFxQBABcdFAAXHQEAFx0BABcdAAAXHQAAFwEcFwABHAAXARwXABwBABcBHAAXHBUAFxwBABccAAAGFxUAHAYXAAYHHAYABxwGBwAPBgcAEAYHDxAGBw8QBwYPCgcPBhAHDwoGDwcQBgcPCAkPBwgJCAkPBwgJDwcJCA8HCQgPBwkIDwcJCA8HCQgPBwkIDwcJCA8HCQgPBwkIDwoJCA8HCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8QCQgPBwkIDxAJCA8HCQgPEAkIDwcJCA8HCQ8IEAkPCBAJCA8HCQ8IEA8ICQcPCQgQDwcICQ8QCQgPBxAGDxAHHAcPEAYPEAcGBwYPEAcPBhAGBw8QBgcXDwAGBxwABgcXABwGFwAXHAYcFwAAFwAcABccAAAXHAAAFxwAABccAAAXHBgAFxgZHBccABgXHBgAFxgZABcYHAAXHAAdFxgZABcYHRkXGB0ZFwAdHBcAHRwYFxkAGBcZABcdGBkXHQAAGBcZABcdABgXHQAAGRcYABcYAB0XHQAAGRcYHRcdGQAXGRgdFwAdGRcZGB0XAB0YFwAdGBcYGQAXGBkAFwAYHBcYGQAXGBwZFxgZABcYGQAXGBkcFxgZABcYGQAXGBkAGBcZABgXGQAZGBcAGBkXABgXGQAZGBcAGRgXABgZFwAZAAAAGRoAABkYAAAZAAAAGRoAABkAAAAZGgAAGRgXABkYFwAZGBcAGRoAABkaAAAZGBcAGQAAABkXGAAZAAAAGRcYABkAAAAZFxgAGQAAABkXGAAZAAAAGRcYABkXGAAZAAAAGQAAABkYFwAZAAAAGBcZABgXGQAZGgAAGRoAABkaAAAZGgAAGRoAABkaAAAZGgAAGRoAABoZAAAaGQAAGhkAABoZAAAaGQAAGhkAABoAAAAaGwAAGgAAABobAAAaGwAAGhsAABobAAAaGwAAGhsAABsAAAAbGgAAGxoAABsAAAAbGgAAGxoAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbGgAAGxoAABsaAAAbGgAAGxoAABsaAAAbGgAAGxoAABobAAAaGwAAGhsAABobAAAaGwAAGhsAABobAAAaGwAAGhsAABobGQAaGxkAGhsZABobGQAaGxkAGhkbABoZAAAaGQAAGhkAABoZAAAaGQAAGhkAABoZAAAaGQAAGhkAABoZAAAaGQAAGhkAABoZFwAZGhcAGRoXABkaFwAZGhcAGRoAABkaAAAZGgAAGRoAABkaAAAZGgAAGRoAABkaAAAZGgAAGRoAABkaAAAZGgAAGhkXABoZFwAZGgAAGRoAABoZAAAaGQAAGhkAABkaAAAaGQAAGhkAABobAAAaGwAAGhsAABobAAAaGwAAGhsAABoZAAAaGwAAGhsAABobAAAaGwAAGhsAABsaAAAbGgAAGxoAABsaAAAbGgAAGxoAABsaAAAbGgAAGxoAABsaAAAbGgAAGxoAABsaAAAbGgAAGhsAABobAAAbGgAAGxoAABsaAAAbGgAAGxoAABsaAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABoZFwAZGgAAGRoAABkaAAAZGgAAGRoAABkaAAAaGQAAGhkAABoZAAAaGQAAGhkAABoZAAAaGQAAGhsAABobAAAaGwAAGhsAABobAAAaGwAAGhsAABobAAAaGQAAGhkAABkaAAAZGgAAGhsAABobAAAbGgAAGxoAABsaAAAbGgAAGxoAABsaAAAbGgAAGxoAABsaAAAbGgAAGxoAABobAAAaGwAAGxoAABsaAAAbGgAAGxoAABsAAAAbGgAAGxoAABsaAAAbGgAAGxoAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGxoAABsaAAAaGwAAGBkXABgZFwAYFxkAHRcAFAAXHRQdFwAAAB0XAAAdFwAAHRcUAB0GFwAGHRcAHRcAAAYHFwAGBxcGBw8dBgcXFAYHDxAGBw8QBw8QBg8QBwYPBxAGDxAHBg8HCQgPEAkHDwkIBw8JCBAJCA8HCQ8IEAkIDwcJCA8HCQ8IEAkPCBAJCA8HCQ8IEAkPCBAJDwgQCQ8IEAkPCBAJCA8QCQgQDwkPCAAJDwgQCQ8AAAkPAAAJAAAACQ8AAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJCAAACQAAAAkAAAAJAAAACQAAAAkPAAAJDwAACQ8IAAkPCAAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgAAAkIAAAJAAAACQAAAAkAAAAJAAAACQgAAAkIAAAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQ8AAAkPAAAJDwAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkPAAAJDwAACQ8AAAkPAAAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCAAACQgAAAkIAAAJAAAACQAAAAkAAAAJAAAACQgAAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQ8IAAkIDwAJCA8ACQ8IEAkIDxAJCA8ACQgPEAkIDxAJCA8QCQgPEAkQCA8JEA8ICRAPCAkQDwgJEA8ICRAPCAkPEAgJDxAICQ8QCAkPEAgJDxAICQ8QCAkPEAgJDxAICQ8QCAkPEAgJDxAICQ8QCAkPEAgJDxAICQ8QCAkPEAgJDxAICQ8QCAkPEAgJDxAICQ8QCA8JEAgJDxAIDwkQCA8JEAgPEAkADxAJCBAPCQAQDwkAEA8JABAPCQAQDwkAEA8JABAPCQAQDwkAEA8JABAPCQAQDwAAEA8AABAPAAAQDwAAEBEAABARAAAQEQAAEBEAABARAAAREBIAEBESABAREgAQEQAAERAAABAREg8QERIAEA8RBxAREg8QDxEGEBESABAREg8QERIPEBEPBhARDwYREBIAERASABAREg8QEQ8HERASABAREg8QDxEHERASABAREgAREBIAEBEPABAREgAREBIAEBEAABAPAAAQDwkAEA8AABAPCQAQDwccDxAHCRAPBwYQBw8GDxAHCQ8QCQgPEAkIDwkQCA8JEAgJDwgQCQ8QCAkPCBAJDwgQCQ8QCAkPEAgJDwgQCQgPEAkPEAgJDwgQCQgPEAkIDxAJDxAICQ8QCA8JEAgPEAkIDwkQCA8QCQAQDwkAEA8JABAHDxEGBxAPEA8GERAPBhEGBw8XBgcPFxAPBgcGBw8QEA8HBhAPBwYQDwcGDxAHCQ8QBwkPEAkIDxAJCA8JCBAPCRAICQ8IEAkPEAgJDwgQCQ8IEAkPEAgJDxAICQ8IEAkPCBAJDxAICQ8QCAkPEAgJDxAICRAPCAkQDwgJEA8ACQ8QAAkPEAAJEAgPCQ8QAAkQCA8JDwgQCQ8QCAkPAAAJDwAACQ8AAAkPAAAJDwAACQAAAAkPAAAJAAAACQ8AAAkPAAAJDxAACQ8QAAkPEAAJDxAACRAPAAkQDwAJEA8ACRAPAAkQCA8JDwAACQ8AAAkPAAAJDwgACQ8AAAkAAAAJDwAACQAAAAkPAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAYHFwAGBxcABgcXAAYABxcGAAcXBgAHFwAXHQYAFx0cABcdHBcAHRwXAB0cFwAdHBcdAAAXHQAcFx0AHBccAAAXABwAABccBgYAFxwGBxccERASABEQEgASERAAEhMAABIREBMSEQAAEhEAABITAAASEwAAEhEAABITAAAREhAAEhEAABESEAASEwAAEhEQExITEQAREhAAEhEQExITAAAREhAAEhEQExIREBMSEwAAEhMAABIREBMSEwAAEhEQExITAAASEwAAEhMAABITAAASEwAAEhMAABITAAASEwAAEhMAABITAAASEwAAEhMAABITAAASEwAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMAAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMSAAATEgAAExIAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAASEwAAEhMAABITAAASEwAAExIAABMSAAATEgAAEhMAABITAAASEwAAEhMAABITAAASEwAAEhMAABITAAASEwAAEhMAABITAAASEwAAEhMAABITAAASEwAAERASABEQEgAREBIAERASABESEAAREBIAERIQABEQEgAREBIAERASABEQEgAQERIACQgPEAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgPAAkIDwAJCA8ACQgPCgkIDwAJCA8ACQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8KCQgPCgkIDwoJCA8ACQgPAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAABgcUABQGBwAXHRQAFBcGABcdFAAUFx0AFBcdABQXHQAUFx0AFAYXABQGFxQAFwYAFAYHABQGBxQABgcUAB0AFAAdABQAHRYUAB0AFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFBYdABQAFx0JAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAACQAAAAkAAAAJAAAA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AACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/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AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA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AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAALDKMj+7ZYc+SuukPPtiizwxskA/zdpqPoPjkjwAAAAAIoVSPwLHAj5C7So9SZIGPGQoUj/+BYk94RN5PexZUj1kKFI//gWJPeETeT3sWVI9bAA3P/5sUj5nd1Y9u5vfPOqXNT/hKSY+kpMAPlS4ODsOGRE/x2IdPnI3FT6SAQk+B04YPylURj4Piiw+nKYvPV8NGz+KiJg+1v65PQlBtzs4GB0/fZevPouLBT151DA8OBgdP32Xrz6LiwU9edQwPAyLET9bjcM+imsJPd/xgjz97CI/k6E5PsYCLT4he1o8sMoyP7tlhz5K66Q8+2KLPDGyQD/N2mo+g+OSPAAAAAAihVI/AscCPkLtKj1JkgY8ZChSP/4FiT3hE3k97FlSPWQoUj/+BYk94RN5PexZUj1sADc//mxSPmd3Vj27m9886pc1P+EpJj6SkwA+VLg4Ow4ZET/HYh0+cjcVPpIBCT4HThg/KlRGPg+KLD6epi89Xw0bP4qImD7X/rk9B0G3OzgYHT99l68+i4sFPXnUMDw4GB0/fZevPouLBT151DA8DIsRP1uNwz6Kawk94PGCPP3sIj+SoTk+xwItPiN7WjwAAAYACgAAAAoAAgAAAAIAxwEAAMcBAwDVAQUAAQDVAQEABAAFAAsABwAFAAcAAQAGAAgARwAGAEcACgALAAwACQALAAkABwAPAAwACwAPAAsADQANAAsABQANAAUADgANAA4AEQANABEAEAAPAA0AEAAPABAAEgAVABMAEAAVABAAEQATABQAEgATABIAEAAWABcAFAAWABQAEwAYABYAEwAYABMAFQAbABcAFgAbABYAGQAZABYAGAAZABgAGgAhAB0AGQAhABkAGgAdAB8AGwAdABsAGQAiACMAHgAiAB4AHAAkACIAHAAkABwAIAAlACcAIwAlACMAIgAmACUAIgAmACIAJAApACgAJQApACUAJgAoACoAJwAoACcAJQArACoAKAArACgAKQArAC0ALAArACwAKgAqACwALgAqAC4AJwAsAC0ALwAsAC8ALgAnAC4AMAAnADAAIwAuAC8AMQAuADEAMAAwADEANAAwADQAMgAjADAAMgAjADIAHgAzADUANwAzADcANgAfADMANgAfADYAGwA2ADgAFwA2ABcAGwA3ADkAOAA3ADgANgA4ADkAOwA4ADsAOgAXADgAOgAXADoAFAA6ADsAPQA6AD0APAAUADoAPAAUADwAEgA8AD4ADwA8AA8AEgA9AD8APgA9AD4APAA+AEAADAA+AAwADwA/AEEAQAA/AEAAPgBAAEEARQBAAEUAQwAMAEAAQwAMAEMACQBCAEYARwBCAEcACABEAEgARgBEAEYAQgBHAEYASQBHAEkASwBGAEgASgBGAEoASQBJAEoATgBJAE4ATABLAEkATABLAEwATQBNAEwATwBNAE8AUQBMAE4AUABMAFAATwBPAFAAVABPAFQAUgBRAE8AUgBRAFIAUwBTAFIAVQBTAFUAVgBSAFQAVwBSAFcAVQBVAFgAWQBVAFkAVgBXAFoAWABXAFgAVQBZAFgAWwBZAFsAXQBYAFoAXABYAFwAWwBbAFwAYABbAGAAXgBdAFsAXgBdAF4AXwBfAF4AYQBfAGEAYwBeAGAAYgBeAGIAYQBhAGIAZgBhAGYAZABjAGEAZABjAGQAZQBlAGQAZwBlAGcAaABkAGYAaQBkAGkAZwBnAGoAawBnAGsAaABpAGwAagBpAGoAZwBrAGoAbgBrAG4AcABqAGwAcgBqAHIAbgBtAHMAdABtAHQAbwBxAHUAcwBxAHMAbQB0AHMAdgB0AHYAdwBzAHUAeABzAHgAdgB5AHsAdwB5AHcAdgB6AHkAdgB6AHYAeAB+AHwAeQB+AHkAegB8AH0AewB8AHsAeQB/AIQAfQB/AH0AfACCAH8AfACCAHwAfgCGAIAAgwCGAIMAiACJAIUAgACJAIAAhgCGAIgAjACGAIwAigCJAIYAigCJAIoAjQCKAIwAkQCKAJEAjgCNAIoAjgCNAI4AjwCOAJIAlQCOAJUAjwCRAJQAkgCRAJIAjgCSAJQAmQCSAJkAlgCVAJIAlgCVAJYAlwCWAJoAnQCWAJ0AlwCZAJwAmgCZAJoAlgCaAJwAoACaAKAAngCdAJoAngCdAJ4AoQCeAKAApACeAKQAogChAJ4AogChAKIAowCjAKIApQCjAKUApgCiAKQApwCiAKcApQCoAKoApgCoAKYApQCpAKgApQCpAKUApwCtAKsAqACtAKgAqQCrAKwAqgCrAKoAqACuAK8ArACuAKwAqwCwAK4AqwCwAKsArQCzAK8ArgCzAK4AsQCxAK4AsACxALAAsgC2ALQAsQC2ALEAsgC0ALUAswC0ALMAsQC5ALUAtAC5ALQAtwC3ALQAtgC3ALYAuAC8ALoAtwC8ALcAuAC6ALsAuQC6ALkAtwC9AL4AuwC9ALsAugC/AL0AugC/ALoAvAC9AMEAwwC9AMMAvgC/AMQAwQC/AMEAvQDCAMAAxgDCAMYAyADAAMUAxwDAAMcAxgDGAMcAywDGAMsAyQDIAMYAyQDIAMkAygDKAMkAzADKAMwAzQDJAMsAzgDJAM4AzADPANEAzQDPAM0AzADQAM8AzADQAMwAzgDUANIAzwDUAM8A0ADSANMA0QDSANEAzwDVANcA0wDVANMA0gDWANUA0gDWANIA1ADaANgA1QDaANUA1gDYANkA1wDYANcA1QDbAN0A2QDbANkA2ADcANsA2ADcANgA2gDgAN4A2wDgANsA3ADeAN8A3QDeAN0A2wDhAOMA3wDhAN8A3gDiAOEA3gDiAN4A4ADoAOQA4QDoAOEA4gDkAOYA4wDkAOMA4QDqAOsA5wDqAOcA5QDsAOoA5QDsAOUA6QDvAOsA6gDvAOoA7QDtAOoA7ADtAOwA7gDwAO0A7gDwAO4A8QDyAO8A7QDyAO0A8AD1APMA8AD1APAA8QDzAPQA8gDzAPIA8AD4APQA8wD4APMA9gD2APMA9QD2APUA9wD7APkA9gD7APYA9wD5APoA+AD5APgA9gD+APoA+QD+APkA/AD8APkA+wD8APsA/QABAf8A/AABAfwA/QD/AAAB/gD/AP4A/AAEAQAB/wAEAf8AAgECAf8AAQECAQEBAwEDAQYBBQEDAQUBAgECAQUBBwECAQcBBAEFAQYBCgEFAQoBCAEHAQUBCAEHAQgBCQEJAQgBDAEJAQwBEAEIAQoBDQEIAQ0BDAETAREBCwETAQsBDgERARIBDwERAQ8BCwEWARIBEQEWAREBFAEUAREBEwEUARMBFQEXARQBFQEXARUBGAEZARYBFAEZARQBFwEaARcBGAEaARgBGwEcARkBFwEcARcBGgEaARsBHwEaAR8BHQEcARoBHQEcAR0BHgEeAR0BIAEeASABIQEdAR8BIgEdASIBIAEgASMBJAEgASQBIQEiASUBIwEiASMBIAEmAScBJAEmASQBIwEoASYBIwEoASMBJQEnASYBKQEnASkBKwEmASgBKgEmASoBKQEqAS0BLAEqASwBKQEpASwBLgEpAS4BKwEsAS0BMQEsATEBLwEuASwBLwEuAS8BMAE0ATABLwE0AS8BMgEyAS8BMQEyATEBMwE6ATYBMgE6ATIBMwE2ATgBNAE2ATQBMgE7ATwBNwE7ATcBNQE9ATsBNQE9ATUBOQE+AUABPAE+ATwBOwE/AT4BOwE/ATsBPQFCAUEBPgFCAT4BPwFBAUMBQAFBAUABPgErAEMBQQErAEEBQgFAAUMBRAFAAUQBRQErAEQBQwErAEgBRgErAEYBRAFFAUQBRgFFAUYBRwFHAUYBSAFHAUgBSQFJAUgBKQBJASkAJgArACkASAFLAUcBSQFLAUkBSgFJASYAJABJASQASgFLAUoBTAFLAUwBTwFKASQAIABKASAATAFNASEAGgBNARoATgFQAU0BTgFQAU4BUQFVAUsBTwFVAU8BUgFTAVABUQFTAVEBVAE4AVMBVAE4AVQBNAE8AVUBUgE8AVIBNwFVAUUBRwFVAUcBSwFAAUUBVQFAAVUBPAFWAVQBUQFWAVEBVwFUAVYBMAFUATABNAFWAVcBWgFWAVoBWAFYAS4BMAFYATABVgEnAVgBWQEnAVkBJAEuAVgBJwEuAScBKwFYAVoBXAFYAVwBWQFaAVcBgAVaAYAFWwFcAVoBWwFcAVsBXQFeAVwBXQFeAV0BYAFfAVkBXAFfAVwBXgFjAV8BXgFjAV4BYQFhAV4BYAFhAWABYgFkAWEBYgFkAWIBZQFjAWEBZAFjAWQBZgFnAWQBZQFnAWUBagFpAWYBZAFpAWQBZwFnAWsBbQFnAW0BaQFsAWsBZwFsAWcBagFsAXABbwFsAW8BawFrAW8BcwFrAXMBbQFwAc0BcgFwAXIBbwFvAXIBdgFvAXYBcwF8AXkBdAF8AXQBeAF5AXsBbgF5AW4BdAF6AW8EewF6AXsBeQF+AXoBeQF+AXkBfAGBAX8BfQGBAX0BgAGAAX0BdwGAAXcBggGFAYEBgAGFAYABhAGEAYABggGEAYIBhgGJAYUBhAGJAYQBiAGIAYQBhgGIAYYBigGNAYkBiAGNAYgBjAGMAYgBigGMAYoBjgGRAY0BjAGRAYwBkAGQAYwBjgGQAY4BkgGVAZEBkAGVAZABlAGUAZABkgGUAZIBlgGcAZUBlAGcAZQBmAGYAZQBlgGYAZYBmgGdAZwBmAGdAZgBmQGZAZgBmgGZAZoBxgSgAZUBnAGgAZwBngGfAZ4BnAGfAZwBnQGeAZ8BowGeAaMBoQGgAZ4BoQGgAaEBogGiAaEBpQGiAaUBpwGjAakBpQGjAaUBoQGmAaQBqgGmAaoBqwGoAawBqgGoAaoBpAGrAaoBrQGrAa0BrgGsAa8BrQGsAa0BqgGyAa4BrQGyAa0BsAGvAbEBsAGvAbABrQG0AbMBsAG0AbABsQGzAbUBsgGzAbIBsAHHBJsBswHHBLMBtAGbAZcBtQGbAbUBswGXAZMBtgGXAbYBtQG1AbYBuAG1AbgBsgGTAY8BtwGTAbcBtgG2AbcBuQG2AbkBuAG5AbsBugG5AboBuAG4AboBrgG4Aa4BsgG8Ab4BrgG8Aa4BugG/AbwBugG/AboBuwG9Ad4BvgG9Ab4BvAHAAb0BvAHAAbwBvwHCAcABvwHCAb8BwQHBAb8BuwHBAbsBwwHFAcIBwQHFAcEBxAHEAcEBwwHEAcMBxgECAMUBxAECAMQBxwHHAcQBxgHHAcYByAEDAMcByAEDAMgByQHLAcgBxgHLAcYBzgHJAcgBywHJAcsBzAHPAXEBzAHPAcwBywHQAc8BywHQAcsBzgGDAXUBcQGDAXEBzwGHAYMBzwGHAc8B0AGLAYcB0AGLAdAB0QHRAdABzgHRAc4B0gGPAYsB0QGPAdEBtwG3AdEB0gG3AdIBuQHOAcYBwwHOAcMB0gHSAcMBuwHSAbsBuQFwAdMBygFwAcoBzQHTAdUBBADTAQQAygHUAdYB1QHUAdUB0wHUAdMBcAHUAXABbAHWAQ4ABQDWAQUA1QERAA4A1gERANYB1wHXAdYB1AHXAdQB2AERANcBYAERAGABXQFgAdcB2AFgAdgBYgHYAWoBZQHYAWUBYgHUAWwBagHUAWoB2AECAAoA2QECANkBxQHZAdoBwgHZAcIBxQFLAE0A2gFLANoB2QEKAEcASwAKAEsA2QHaAU0AUQDaAVEA2wHCAdoB2wHCAdsBwAHbAdwBvQHbAb0BwAFRAFMA3AFRANwB2wHcAVMAVgDcAVYA3QG9AdwB3QG9Ad0B3gFZAN8B3QFZAN0BVgDfAeAB3gHfAd4B3QFdAOEB3wFdAN8BWQDhAeIB4AHhAeAB3wHjAeQB4gHjAeIB4QFfAOMB4QFfAOEBXQBjAOUB4wFjAOMBXwDlAeYB5AHlAeQB4wHnAegB5gHnAeYB5QFlAOcB5QFlAOUBYwDpAecBZQDpAWUAaADqAegB5wHqAecB6QGNDe0B6wGNDesBgA2ADesB7gGADe4BgQ3tAQwC7AHtAewB6wHrAewB7wHrAe8B7gHyAfAB7gHyAe4B7wGCDfEB6gGCDeoBgQ2DDfUB8QGDDfEBgg33AfMB8AH3AfAB8gGFDfoB9gGFDfYBhA37AfkB9AH7AfQB+AGGDf0B+gGGDfoBhQ3+AfwB+QH+AfkB+wGGDfwB/wGGDf8Bhw38Af4BAQL8AQEC/wGHDf8BAgKHDQICiA3/AQECAwL/AQMCAgKJDXQAdwCJDXcAiA0FAgQCAgIFAgICAwIGAgQCBQIGAgUCCAKKDYkNBAKKDQQCBgILAgoCBwILAgcCCQKMDWsAcACMDXAAiw3tAQoCCwLtAQsCDAKNDYwNCgKNDQoC7QEMAgsCDQIMAg0CDwIJAg4CDQIJAg0CCwIQAg8CDQIQAg0CDgITAhECDwITAg8CEAIPAhEC7AEPAuwBDAIUAhICEQIUAhECEwIRAhIC7wERAu8B7AEXAhUCEwIXAhMCEAIVAhYCFAIVAhQCEwIYAhkCFgIYAhYCFQIaAhgCFQIaAhUCFwIbAh4CGQIbAhkCGAIdAhsCGAIdAhgCGgIcAh8CHgIcAh4CGwI6AhwCGwI6AhsCHQIhAiACHgIhAh4CHwIgAiICGQIgAhkCHgIlAiMCIAIlAiACIQIjAvcBIgIjAiICIAIoAicCJAIoAiQCJgInAvsB+AEnAvgBJAIqAikCJwIqAicCKAIpAv4B+wEpAvsBJwIpAisCAQIpAgEC/gEqAiwCKwIqAisCKQIrAi0CAwIrAgMCAQIsAi4CLQIsAi0CKwIuAjACLwIuAi8CLQItAi8CBQItAgUCAwIwAjMCMQIwAjECLwIvAjECCAIvAggCBQI0AjYCNQI0AjUCMgIyAjUCNwIyAjcCCQI2AjoCHQI2Ah0CNQI1Ah0CGgI1AhoCNwIJAjcCOAIJAjgCDgIaAhcCOAIaAjgCNwIQAg4COAIQAjgCFwI5AjsCOgI5AjoCNgKeAjkCNgKeAjYCNAI7Aj0CHAI7AhwCOgI8Aj8CPQI8Aj0COwKnAjwCOwKnAjsCOQI9Aj4CHwI9Ah8CHAI/AkACPgI/Aj4CPQJBAj8CPAJBAjwCQwJFAkACPwJFAj8CQQJHAkICRAJHAkQCSQJIAkYCQgJIAkICRwJMAkgCRwJMAkcCSgJKAkcCSQJKAkkCSwJPAk0CSgJPAkoCSwJNAk4CTAJNAkwCSgJOAk0CUAJOAlACUgJNAk8CUQJNAlECUAJQAlECVQJQAlUCUwJSAlACUwJSAlMCVAJUAlMCVgJUAlYCVwJTAlUCWAJTAlgCVgJWAlkCWwJWAlsCVwJYAloCWQJYAlkCVgJZAloCXQJZAl0CXAJbAlkCXAJbAlwCXgJcAl0CYQJcAmECYAJeAlwCYAJeAmACYwJlAl8CYgJlAmICZgJnAmQCXwJnAl8CZQJpAmgCZQJpAmUCZgJoAmoCZwJoAmcCZQJtAmsCaAJtAmgCaQJrAmwCagJrAmoCaAJuAnACbAJuAmwCawJvAm4CawJvAmsCbQJuAm8CcwJuAnMCcQJwAm4CcQJwAnECcgJxAnQCdgJxAnYCcgJzAnUCdAJzAnQCcQJ1AngCdwJ1AncCdAJ0AncCeQJ0AnkCdgJ3AngCewJ3AnsCegJ5AncCegJ5AnoCfAJ6AnsCgQJ6AoECfQJ8AnoCfQJ8An0CfwKCAoQCfwKCAn8CfQKGAoICfQKGAn0CgQKMAoQCggKMAoICiAKIAoIChgKIAoYCigKPAo0ChwKPAocCiQKNAo4CiwKNAosChwKVAo4CjQKVAo0CkQKRAo0CjwKRAo8CkwKQApIClwKQApcClgKUApAClgKUApYCmAKWApcCmwKWApsCmQKYApYCmQKYApkCmgKZAiwCKgKZAioCmgKbAi4CLAKbAiwCmQKXAp8CnAKXApwCmwKbApwCMAKbAjACLgKcAp0CMwKcAjMCMAKfAqICnQKfAp0CnAKgAqYCogKgAqICnwKSAqACnwKSAp8ClwKjAqcCOQKjAjkCngKkAqgCpwKkAqcCowKoAkMCPAKoAjwCpwKsAqoCqQKsAqkCpQKqAkkCRAKqAkQCqQKrAksCSQKrAkkCqgKtAqsCqgKtAqoCrAKvAq0CrAKvAqwCrgKuAqwCpQKuAqUCsAKzAq8CrgKzAq4CsQKxAq4CsAKxArACgAK2ArQCsgK2ArICtQJ7ArUCsgJ7ArICgQJzArYCtQJzArUCdQJ4AnUCtQJ4ArUCewK3ArkCtAK3ArQCtgJvArcCtgJvArYCcwK5ArcCugK5AroCvQK6ArcCbwK6Am8CbQK9AroCuwK9ArsCvwJpArsCugJpAroCbQK+AsECwAK+AsACvALAAsMCuALAArgCvALCAsACwQLCAsECyQLEAsMCwALEAsACwgLDAsQCrQLDAq0CrwK4AsMCrwK4Aq8CswLEAsUCqwLEAqsCrQLGAsUCxALGAsQCwgLFAk8CSwLFAksCqwJPAsUCxgJPAsYCUQJRAsYCxwJRAscCVQLJAscCxgLJAsYCwgJVAscCyAJVAsgCWALKAsgCxwLKAscCyQLJAsECywLJAssCygLIAsoCzALIAswCzQLKAssCzgLKAs4CzALNAswCzgLNAs4C0QLLAtUCzwLLAs8CzgLRAs4CzwLRAs8C0gJaAs0C0QJaAtECXQJdAtEC0gJdAtICYQLWAtQC0wLWAtMC0ALUAmYCYgLUAmIC0wJmAtQCuwJmArsCaQK/ArsC1AK/AtQC1gLVAssCwQLVAsECvgJYAsgCzQJYAs0CWgKwAtcChQKwAoUCgAKlAqEC1wKlAtcCsAKhApMCjwKhAo8C1wLXAo8CiQLXAokChQKYApoC2AKYAtgC2wKaAioCKAKaAigC2ALYAigCJgLYAiYC2gLbAtgC2gLbAtoC3QKUApgC2wKUAtsC3gLeAtsC3QLeAt0C4ALfAuIC5ALfAuQC4wKVAt8C4wKVAuMCjgLjAuQCfgLjAn4CgwKOAuMCgwKOAoMCiwLpAuUC5ALpAuQC4gLlAucCfgLlAn4C5ALmAv4C5wLmAucC5QLqAuYC5QLqAuUC6QLsAuoC6QLsAukC6wLrAukC4gLrAuIC7gJMAuwC6wJMAusCSAJIAusC7gJIAu4CRgLtAu8CQALtAkACRQLhAtwC7wLhAu8C7QLcAtkC8ALcAvAC7wLvAvACPgLvAj4CQALZAiUCIQLZAiEC8ALwAiECHwLwAh8CPgLxAvIC6gLxAuoC7AJOAvEC7AJOAuwCTALyAvEC8wLyAvMC9QLzAvECTgLzAk4CUgL1AvMC9AL1AvQC9gJUAvQC8wJUAvMCUgL2AvgC9wL2AvcC9QL3AvsC8gL3AvIC9QL5AvcC+AL5AvgCBQP8AvsC9wL8AvcC+QL7AvwC/gL7Av4C5gLyAvsC5gLyAuYC6gL9AgADAQP9AgED/wICAwAD/QICA/0C+gIAA3ACcgIAA3ICAQNwAgADAgNwAgIDbAJsAgIDAwNsAgMDagIGAwMDAgMGAwID+gJqAgMDBANqAgQDZwIIAwQDAwMIAwMDBgMFA/gCCQMFAwkDBwMEAwgDCwMEAwsDDQMHAwkDDgMHAw4DCgMOAxADDAMOAwwDCgMJAxMDDwMJAw8DDgMPAxEDEAMPAxADDgMRA1sCXgIRA14CEAMQA14CYwIQA2MCDAMSA1cCWwISA1sCEQMTAxIDEQMTAxEDDwNXAhID9AJXAvQCVAL2AvQCEgP2AhIDEwMTAwkD+AITA/gC9gJnAgQDDQNnAg0DZAIBA3ICdgIBA3YCFAP/AgEDFAP/AhQD6AIUA3YCeQIUA3kCfALoAhQDfALoAnwCfwIZAiICFQMZAhUDFgL3ARYDFQP3ARUDIgIUAhYCFQMUAhUDFgMUAhYDFwMUAhcDEgL3AfIBFwP3ARcDFgPvARICFwPvARcD8gEZAxgDAAIZAwACdwAYAxoD/QEYA/0BAAIdAxsDGAMdAxgDGQMbAxwDGgMbAxoDGAMeAyADHAMeAxwDGwMfAx4DGwMfAxsDHQPNACEDHgPNAB4DHwMhAyIDIAMhAyADHgMjAyQDIgMjAyIDIQPRACMDIQPRACEDzQAlAyMD0QAlA9EA0wAmAyQDIwMmAyMDJQMlA9MA1wAlA9cAJwMmAyUDJwMmAycDKAMoAycDKQMoAykDKgMnA9cA2QAnA9kAKQMpA9kA3QApA90AKwMqAykDKwMqAysDLAMsAysDLQMsAy0DLgMrA90A3wArA98ALQMtA98A4wAtA+MALwMuAy0DLwMuAy8DMAMwAy8DMQMwAzEDMwMvA+MA5gAvA+YAMQPrADUDMgPrADID5wA1AzYDNAM1AzQDMgPvADcDNQPvADUD6wA3AzgDNgM3AzYDNQM6AzgDNwM6AzcDOQM5AzcD7wA5A+8A8gA5AzsDPAM5AzwDOgPyAPQAOwPyADsDOQM7A/QA+AA7A/gAPQM8AzsDPQM8Az0DPgM/A0ADPgM/Az4DPQP6AD8DPQP6AD0D+ABBAz8D+gBBA/oA/gBCA0ADPwNCAz8DQQMAAUMDQQMAAUED/gBDA0QDQgNDA0IDQQNFA0MDAAFFAwABBAFGA0QDQwNGA0MDRQNHA0UDBAFHAwQBBwFIA0YDRQNIA0UDRwNHA0kDSwNHA0sDSAMHAUoDSQMHAUkDRwNJA0oDTgNJA04DTANLA0kDTANLA0wDTQNPA1EDTQNPA00DTANQA08DTANQA0wDTgNTA08DUANTA1ADVQNWA1EDTwNWA08DUwNSA1QDWQNSA1kDWANXA1IDWANXA1gDWgNYA1kDXgNYA14DXANaA1gDXANaA1wDYANbA10DYgNbA2IDYQNfA1sDYQNfA2EDYwNhA2IDZQNhA2UDZANjA2EDZANjA2QDZgNpA2cDZANpA2QDZQNnA2gDZgNnA2YDZANsA2gDZwNsA2cDagNqA2cDaQNqA2kDawNvA20DagNvA2oDawNtA24DbANtA2wDagN0A24DbQN0A20DcANwA20DbwNwA28DcgN4A3YDcQN4A3EDcwN2A3cDdQN2A3UDcQN6A3cDdgN6A3YDeQN5A3YDeAN5A3gDDwESAXsDeQMSAXkDDwF7A3wDegN7A3oDeQN9A3sDEgF9AxIBFgF+A3wDewN+A3sDfQN9AxYBGQF9AxkBfwN+A30DfwN+A38DgAN/AxkBHAF/AxwBgQOAA38DgQOAA4EDggOEA4MDgQOEA4EDHAGDA4UDggODA4IDgQOIA4YDgwOIA4MDhAOGA4cDhQOGA4UDgwOGA4oDjgOGA44DhwOIA4wDigOIA4oDhgOJA4sDkQOJA5EDjwONA4kDjwONA48DkAOPA5IDlAOPA5QDkAORA5MDkgORA5IDjwOSA5MDlwOSA5cDlQOUA5IDlQOUA5UDlgOVA5gDmgOVA5oDlgOXA5kDmAOXA5gDlQObA5oDmAObA5gDmQOWA5oDnAOWA5wDnQObA54DnAObA5wDmgOfA50DnAOfA5wDngObA6IDoAObA6ADngOfA54DoAOfA6ADoQOjA6EDoAOjA6ADogObA6YDpAObA6QDogOjA6IDpAOjA6QDpQOnA6UDpAOnA6QDpgOnA6YDqAOnA6gDqQObA6gDpgObA6wDqgObA6oDqAOpA6gDqgOpA6oDqwOtA6sDqgOtA6oDrAObA64DrAOtA6wDrgOtA64DrwObA5kDsAObA7ADrgOvA64DsAOvA7ADsQOXA7EDsAOXA7ADmQOyA7QDrwOyA68DsQOTA7IDsQOTA7EDlwOzA7UDtAOzA7QDsgORA7MDsgORA7IDkwO3A7YDtAO3A7QDtQO0A7YDrQO0A60DrwO2A7gDqwO2A6sDrQO3A7kDuAO3A7gDtgO5A7sDugO5A7oDuAO4A7oDqQO4A6kDqwO6A7wDpwO6A6cDqQO9A7wDugO9A7oDuwO9A78DvgO9A74DvAO8A74DpQO8A6UDpwO/A8EDwAO/A8ADvgO+A8ADowO+A6MDpQPAA8IDoQPAA6EDowPBA8MDwgPBA8IDwAPDA8UDxAPDA8QDwgPCA8QDnwPCA58DoQPEA8YDnQPEA50DnwPFA8cDxgPFA8YDxAPHA5ADlAPHA5QDxgPGA5QDlgPGA5YDnQPIA40DkAPIA5ADxwPLA8gDxwPLA8cDxQPKA4cDjgPKA44DyQPNA8oDyQPNA8kDzAPOA8sDxQPOA8UDwwPQA80DzAPQA8wDzwPTA9ADzwPTA88D0gPRA84DwwPRA8MDwQPUA9EDwQPUA8EDvwPWA9MD0gPWA9ID1QPZA9YD1QPZA9UD2APXA9QDvwPXA78DvQPZA9gD2wPZA9sD3APaA9cDvQPaA70DuwPdA9oDuwPdA7sDuQPfA9wD2wPfA9sD3gPiA98D3gPiA94D4QPgA90DuQPgA7kDtwPjA+ADtwPjA7cDtQPiA+ED5APiA+QD5QPoA+UD5APoA+QD5wPmA+MDtQPmA7UDswOIA+gD5wOIA+cDjAOLA+YDswOLA7MDkQPqA+UD6APqA+gD6QPpA+gDiAPpA4gDhAMhAeoD6QMhAekDHgEeAekDhAMeAYQDHAHiA+UD6gPiA+oD6wPrA+oDIQHrAyEB7APtA98D4gPtA+ID6wPvA+0D6wPvA+sD7APuA9wD3wPuA98D7QPwA+4D7QPwA+0D7wNZAV8B7wNZAe8D7APvA18BYwHvA2MB8APyA+4D8APyA/AD8QPwA2MBZgHwA2YB8QPzA/UD8gPzA/ID8QNmAfYD8wNmAfMD8QPzA/YD9wPzA/cD9AP0Aw4E9QP0A/UD8wNpAfkD9gNpAfYDZgH2A/kD+wP2A/sD9wNoAf0D/ANoAfwD+AP4A/wD/wP4A/8D+gP9A20E/gP9A/4D/AP8A/4DAAT8AwAE/wMABAIEAQQABAEE/wP/AwEEBAT/AwQE+gMCBF0EAwQCBAMEAQQBBAMEBgQBBAYEBAT3A/sDBQT3AwUECAQIBAUEBwQIBAcECQT3AwgECgT3AwoE9AMKBAgECQQKBAkECwQKBAwEDgQKBA4E9AMLBA0EDAQLBAwECgQNBM0D0AMNBNADDAQMBNAD0wMMBNMDDgQOBNMD1gMOBNYD9QPKA80DDQTKAw0EDwQPBA0ECwQPBAsEEASHA8oDDwSHAw8EhQOFAw8EEASFAxAEggMJBBEEEAQJBBAECwQQBBEEgAMQBIADggMJBAcEEgQJBBIEEQQRBBIEfgMRBH4DgAMTBBIEBwQTBAcEFAR8A34DEgR8AxIEEwR6A3wDEwR6AxMEFQQVBBMEFAQVBBQEFgR6AxUEFwR6AxcEdwMVBBYEGAQVBBgEFwQXBBgEHAQXBBwEGgR3AxcEGgR3AxoEdQN0AxkEHQR0Ax0EbgMZBBsEHgQZBB4EHQQdBB4EIAQdBCAEHwRuAx0EHwRuAx8EbANsAx8EIQRsAyEEaAMfBCAEIgQfBCIEIQQhBCIEJAQhBCQEIwRoAyEEIwRoAyMEZgMlBGMDZgMlBGYDIwQoBCUEIwQoBCMEJAQmBF8DYwMmBGMDJQQpBCYEJQQpBCUEKAQrBCgEJAQrBCQELgQsBCkEKAQsBCgEKwQwBCwEKwQwBCsELwQvBCsELgQvBC4EMgQ0BDAELwQ0BC8EMwQzBC8EMgQzBDIENgQ5BDQEMwQ5BDMENwQ3BDMENgQ3BDYEOAQ7BDcEOAQ7BDgEPgQ8BDkENwQ8BDcEOwQbBDwEOwQbBDsEHgQeBDsEPgQeBD4EIAQ+BDgENgQ+BDYEPwQgBD4EPwQgBD8EIgQ/BDYEMgQ/BDIELgQiBD8ELgQiBC4EJARABEEEOgRABDoEPQQYBEAEPQQYBD0EHAQUBAcEQQQUBEEEQAQWBBQEQAQWBEAEGARBBEIENQRBBDUEOgQHBEQEQgQHBEIEQQREBEcERQREBEUEQgRCBEUEMQRCBDEENQRFBEgELQRFBC0EMQRHBEoESARHBEgERQRKBE0ESwRKBEsESARIBEsEKgRIBCoELQRLBE4EJwRLBCcEKgRNBFAETgRNBE4ESwRQBFcDWgNQBFoDTgROBFoDYANOBGADJwRRA1YDTwRRA08EUQRRBE8ETARRBEwEUgRNA1EDUQRNA1EEUwRTBFEEUgRTBFIEVARVBEsDTQNVBE0DUwRXBFUEUwRXBFMEVARWBEgDSwNWBEsDVQRYBFYEVQRYBFUEVwRaBFgEVwRaBFcEWQRZBFcEVARZBFQEWwRdBFoEWQRdBFkEXARcBFkEWwRcBFsEXgQDBF0EXAQDBFwEXwRfBFwEXgRfBF4EYAQGBAMEXwQGBF8EQwRDBF8EYARDBGAERgRbBGEEYARbBGAEXgRhBEkERgRhBEYEYARSBEwESQRSBEkEYQRUBFIEYQRUBGEEWwRiBF0EAgRiBAIEZQRiBGMEWgRiBFoEXQRkBJ4EYwRkBGMEYgRnBGQEYgRnBGIEZQRlBAIEAARlBAAEZgRoBGcEZQRoBGUEZgRAA0IDZARAA2QEZwQ+A0ADZwQ+A2cEaARpBDwDPgNpBD4DaARrBGkEaARrBGgEZgRqBDoDPANqBDwDaQRsBGoEaQRsBGkEawT+A2sEZgT+A2YEAARsBGsE/gNsBP4DbQRwBGoEbARwBGwEbgRtBG8EbgRtBG4EbARuBG8EegFuBHoBcQR0BHAEbgR0BG4EcQR2BHQEcQR2BHEEcwRxBHoBfgFxBH4BcwQ2AzgDcAQ2A3AEdAQ0AzYDdAQ0A3QEdgR1BHcEMAN1BDADMwNyBHgEdwRyBHcEdQR4BHoEeQR4BHkEdwR3BHkELgN3BC4DMAN6BHwEewR6BHsEeQR5BHsELAN5BCwDLgN8BH4EfQR8BH0EewR7BH0EKgN7BCoDLAN+BIAEfwR+BH8EfQR9BH8EKAN9BCgDKgOABKABgQSABIEEfwR/BIEEJgN/BCYDKAOBBIIEhQSBBIUEJgOgAaIBggSgAYIEgQSiAacBhASiAYQEggSCBIQEhwSCBIcEhQSHBIkEiASHBIgEhQSFBIgEJAOFBCQDJgOJBIwEiwSJBIsEiASIBIsEIgOIBCIDJAOMBI8EjgSMBI4EiwSLBI4EIAOLBCADIgOPBJIEkQSPBJEEjgSOBJEEHAOOBBwDIAOSBJYElASSBJQEkQSRBJQEGgORBBoDHAOWBPYB+gGWBPoBlASUBPoB/QGUBP0BGgOXBPEB9QGXBPUBlQSYBJcElQSYBJUEkwToAeoB8QHoAfEBlwTmAegBlwTmAZcEmATkAeYBmATkAZgEmQSZBJgEkwSZBJMEkATiAeQBmQTiAZkEmgSaBJkEkASaBJAEjQTgAeIBmgTgAZoEmwSbBJoEjQSbBI0EigTeAeABmwTeAZsEnAScBJsEigScBIoEhgSdBL4B3gGdBN4BnASDBJ0EnASDBJwEhgSrAa4BvgGrAb4BnQSmAasBnQSmAZ0EgwSABJEBlQGABJUBoAGNAZEBgASNAYAEfgR8BIkBjQF8BI0BfgSFAYkBfASFAXwEegR4BIEBhQF4BIUBegRyBH8BgQFyBIEBeAQ6A2oEcAQ6A3AEOANtBP0DewFtBHsBbwRCA0QDngRCA54EZAREA0YDnwREA58EngSeBJ8EoASeBKAEYwRIA1YEnwRIA58ERgOfBFYEWASfBFgEoARaBGMEoARaBKAEWAR7Af0DaAF7AWgBbgH1A9YD2QP1A9kD8gPZA9wD7gPZA+4D8gMkAVkB7AMkAewDIQGiBA8BeAOiBHgDoQShBHgDcwOhBHMDpQRUA6IEoQRUA6EEWQNZA6EEpQRZA6UEXgNdA6QEpgRdA6YEYgOkBHIDbwOkBG8DpgSmBG8DawOmBGsDaQNiA6YEaQNiA2kDZQOnBKMEVQOnBFUDUAMJARABowQJAaMEpwQHAQkBpwQHAacESgNKA6cEUANKA1ADTgOoBMoAzQCoBM0AHwOqBKgEHwOqBB8DHQOpBMgAygCpBMoAqASrBKkEqASrBKgEqgSsBK0EqwSsBKsEqgQZA6wEqgQZA6oEHQN7AH0ArQR7AK0ErAR3AHsArAR3AKwEGQN9AIQArgR9AK4ErQStBK4EsQStBLEEqwSFAIkAsASFALAErwSvBLAEswSvBLMEsgSzBLYEtQSzBLUEsgSxBLQEqQSxBKkEqwS2BL4AwwC2BMMAtQS0BMIAyAC0BMgAqQS+ALYEtwS+ALcEuwC2BLMEuAS2BLgEtwS3BLgEugS3BLoEuQS7ALcEuQS7ALkEuQC5BLoEvAS5BLwEuwS5ALkEuwS5ALsEtQC7BLwEvgS7BL4EvQS1ALsEvQS1AL0EswC9BL4EwAS9BMAEvwSzAL0EvwSzAL8ErwC/BMAEpgC/BKYAqgCvAL8EqgCvAKoArADABMEEowDABKMApgC+BMIEwQS+BMEEwATCBJcAnQDCBJ0AwQTBBJ0AoQDBBKEAowDDBJUAlwDDBJcAwgS8BMMEwgS8BMIEvgTEBI8AlQDEBJUAwwS6BMQEwwS6BMMEvATFBI0AjwDFBI8AxAS4BMUExAS4BMQEugSwBIkAjQCwBI0AxQSzBLAExQSzBMUEuATMBMcEtAHMBLQByATIBLQBsQHIBLEBygTOBMwEyATOBMgEyQTJBMgEygTJBMoE5ATPBNEExgTPBMYEywTQBM8EywTQBMsEzQTUBNIEzwTUBM8E0ATSBNME0QTSBNEEzwTSBNUE1wTSBNcE0wTUBNYE1QTUBNUE0gTVBNYE2QTVBNkE2ATXBNUE2ATXBNgE2gTZBN4E3ATZBNwE2ATYBNwEqQHYBKkB2gTbBN0E4ATbBOAE3wSoAdsE3wSoAd8ErAHgBOIE4QTgBOEE3wTfBOEErwHfBK8BrAHhBMoEsQHhBLEBrwHiBOQEygTiBMoE4QTgBGUF4wTgBOME4gTkBOIE4wTkBOME5QToBMkE5AToBOQE5QTrBOgE5QTrBOUE5gTjBF4F5gTjBOYE5QTqBM4EyQTqBMkE6ATvBOoE6ATvBOgE6wTxBO4E7ATxBOwE8ATwBOwE5wTwBOcE8gTxBPAE8wTxBPME9gTwBPIE9ATwBPQE8wTzBPQEGAXzBBgF9QT2BPME9QT2BPUE9wT5BPEE9gT5BPYE+AT4BPYE9wT4BPcE+gT9BPkE+AT9BPgE+wT7BPgE+gT7BPoE/AT+BPsE/AT+BPwEAAX/BP0E+wT/BPsE/gQCBf8E/gQCBf4EAQUBBf4EAAUBBQAFAwUFBQIFAQUFBQEFBAUEBQEFAwUEBQMFBgUFBQQFBwUFBQcFCgUEBQYFCAUEBQgFBwUHBQgFHwUHBR8FCQUKBQcFCQUKBQkFCwUMBQoFCwUMBQsFDgUNBQUFCgUNBQoFDAUQBQ0FDAUQBQwFDwUPBQwFDgUPBQ4FEQUQBQ8FEgUQBRIF8gQSBQ8FEQUSBREFEwX0BPIEEgX0BBIFFAUUBRIFEwUUBRMFFQUYBfQEFAUYBRQFFgUWBRQFFQUWBRUFFwUZBRYFFwUZBRcFGwUaBRgFFgUaBRYFGQUdBRoFGQUdBRkFHAUcBRkFGwUcBRsFHgUGBR0FHAUGBRwFCAUIBRwFHgUIBR4FHwUeBRsFIQUeBSEFIAUfBR4FIAUfBSAFIwUgBSEFMwUgBTMFIgUjBSAFIgUjBSIFJAUlBSMFJAUlBSQFJgUJBR8FIwUJBSMFJQULBQkFJQULBSUFJwUnBSUFJgUnBSYFKAUpBScFKAUpBSgFKgUOBQsFJwUOBScFKQURBQ4FKQURBSkFKwUrBSkFKgUrBSoFLAURBSsFLQURBS0FEwUtBSsFLAUtBSwFLgUVBRMFLQUVBS0FLwUvBS0FLgUvBS4FMAUXBRUFLwUXBS8FMQUxBS8FMAUxBTAFMgUhBTEFMgUhBTIFMwUXBTEFIQUXBSEFGwUwBTQFMwUwBTMFMgU0BTUFIgU0BSIFMwUsBTQFMAUsBTAFLgUsBSoFNQUsBTUFNAUqBSgFJgUqBSYFNQU1BSYFJAU1BSQFIgUdBQYFNwUdBTcFNgUaBR0FNgUaBTYFOQU2BTcFSQU2BUkFOAU5BTYFOAU5BTgFOgUaBTkFOwUaBTsFGAU7BTkFOgU7BToFPAU9BTsFPAU9BTwFPgX1BBgFOwX1BDsFPQX3BPUEPQX3BD0FPwU/BT0FPgU/BT4FQAX6BPcEPwX6BD8FQQVBBT8FQAVBBUAFQgX8BPoEQQX8BEEFQwVDBUEFQgVDBUIFRAVFBUMFRAVFBUQFRgX8BEMFRQX8BEUFAAUDBQAFRQUDBUUFRwVHBUUFRgVHBUYFSAUGBQMFRwUGBUcFNwU3BUcFSAU3BUgFSQVIBUYFSwVIBUsFSgVJBUgFSgVJBUoFTQVKBUsFXQVKBV0FTAVNBUoFTAVNBUwFTgVPBU0FTgVPBU4FUAU4BUkFTQU4BU0FTwU6BTgFTwU6BU8FUQVRBU8FUAVRBVAFUgU6BVEFUwU6BVMFPAVTBVEFUgVTBVIFVAVVBVMFVAVVBVQFVgU+BTwFUwU+BVMFVQVABT4FVQVABVUFVwVXBVUFVgVXBVYFWAVCBUAFVwVCBVcFWQVZBVcFWAVZBVgFWgVEBUIFWQVEBVkFWwVbBVkFWgVbBVoFXAVGBUQFWwVGBVsFSwVLBVsFXAVLBVwFXQVdBVwFWgVdBVoFTAVMBVoFWAVMBVgFTgVOBVgFVgVOBVYFUAVQBVYFVAVQBVQFUgXnBF8FEAXnBBAF8gRhBQ0FEAVhBRAFXwVlBWAFXgVlBV4F4wRjBQUFDQVjBQ0FYQVmBWIFYAVmBWAFZQXdBGYFZQXdBGUF4ARoBWsFZAVoBWQFZwXZBGgFZwXZBGcF3gTWBGkFaAXWBGgF2QRpBW0FawVpBWsFaAVsBf8EAgVsBQIFagVqBQIFBQVqBQUFYwVwBW8FbQVwBW0FaQVsBW4F/QRsBf0E/wT9BG4FcQX9BHEF+QRvBXAFcwVvBXMFcgVyBXMF6QRyBekE7QT5BHEF7gT5BO4E8QRwBdQE0ARwBdAEcwVzBdAEzQRzBc0E6QTWBNQEcAXWBHAFaQXXBNoEdAXXBHQFdgWpAXUFdAWpAXQF2gR3BXYFdAV3BXQFdQV4BXYFdwV4BXcFeQXTBNcEdgXTBHYFeAXTBHgFegXTBHoF0QR5BXsFegV5BXoFeAXGBNEEegXGBHoFewXGBHsFfAXGBHwFmQF5BX0FfAV5BXwFewWdAZkBfAWdAXwFfQV3BX4FfQV3BX0FeQV9BX4FnwF9BZ8BnQGfAX4FfwWfAX8FowF3BXUFfwV3BX8FfgWpAaMBfwWpAX8FdQVbARUAEQBbAREAXQGABRgAFQCABRUAWwFXAVEBTgFXAU4BgAUaABgAgAUaAIAFTgFCAT8BgwVCAYMFgQUrAEIBgQUrAIEFggWBBYMFhAWBBYQFggU/AT0BhQU/AYUFgwWDBYUFhgWDBYYFhAWFBYcFiQWFBYkFhgU9ATkBhwU9AYcFhQWIBYsFjAWIBYwFigU6ATMBiwU6AYsFiAWLBTMBMQGLBTEBjQWMBYsFjQWMBY0FjgWQBY4FjQWQBY0FjwWPBY0FMQGPBTEBLQEqAZEFjwUqAY8FLQGRBZIFkAWRBZAFjwWTBZEFKgGTBSoBKAGUBZIFkQWUBZEFkwWTBZUFlgWTBZYFlAUoASUBlQUoAZUFkwWXBZgFlgWXBZYFlQUiAZcFlQUiAZUFJQGaBZgFlwWaBZcFmQWZBZcFIgGZBSIBHwGcBZoFmQWcBZkFmwWbBZkFHwGbBR8BGwGbBRsBGAGbBRgBnQWcBZsFnQWcBZ0FngWdBRgBFQGdBRUBnwWeBZ0FnwWeBZ8FoAWfBRUBEwGfBRMBoQWgBZ8FoQWgBaEFogWhBaMFpQWhBaUFogUTAQ4BowUTAaMFoQWnBaQFDQGnBQ0BCgGoBaYFpAWoBaQFpwWqBagFpwWqBacFqQWpBacFCgGpBQoBBgEDAasFqQUDAakFBgGrBawFqgWrBaoFqQWrBQMBAQGrBQEBrQWsBasFrQWsBa0FrgWtBa8FsAWtBbAFrgUBAf0ArwUBAa8FrQWvBf0A+wCvBfsAsQWwBa8FsQWwBbEFsgWxBbMFtAWxBbQFsgX7APcAswX7ALMFsQWzBfcA9QCzBfUAtQW0BbMFtQW0BbUFtgW1BbcFuAW1BbgFtgX1APEAtwX1ALcFtQW3BfEA7gC3Be4AuQW4BbcFuQW4BbkFugW5Be4A7AC5BewAuwW6BbkFuwW6BbsFvAW7Bb4FwAW7BcAFvAXsAOkAvgXsAL4FuwW9BcEFwgW9BcIFvwXoAOIAwQXoAMEFvQXiAOAAwwXiAMMFwQXBBcMFxAXBBcQFwgXDBcUFxgXDBcYFxAXgANwAxQXgAMUFwwXcANoAxwXcAMcFxQXFBccFyAXFBcgFxgXHBckFygXHBcoFyAXaANYAyQXaAMkFxwXWANQAywXWAMsFyQXJBcsFzAXJBcwFygXLBc0FzgXLBc4FzAXUANAAzQXUAM0FywXQAM4AzwXQAM8FzQXNBc8F0AXNBdAFzgXSBdAFzwXSBc8F0QXRBc8FzgDRBc4AywDUBdIF0QXUBdEF0wXTBdEFywDTBcsAxwDWBdMFxwDWBccAxQDYBdQF0wXYBdMF1gXZBdoF1wXZBdcF1QW/ANkF1QW/ANUFxADZBdsF3AXZBdwF2gW/ALwA2wW/ANsF2QXbBd0F3gXbBd4F3AW8ALgA3QW8AN0F2wXdBbgAtgDdBbYA3wXeBd0F3wXeBd8F4AXfBeEF4gXfBeIF4AW2ALIA4QW2AOEF3wXhBbIAsADhBbAA4wXiBeEF4wXiBeMF5AXjBeUF5gXjBeYF5AWwAK0A5QWwAOUF4wXlBecF6AXlBegF5gWtAKkA5wWtAOcF5QWpAKcA6QWpAOkF5wXnBekF6gXnBeoF6AXsBeoF6QXsBekF6wXrBekFpwDrBacApADuBewF6wXuBesF7QXtBesFpADtBaQAnwDvBe0FnwDvBZ8AmwDwBe4F7QXwBe0F7wWYAPEF7wWYAO8FmwDxBfIF8AXxBfAF7wX0BfIF8QX0BfEF8wXzBfEFmADzBZgAkwCQAPUF8wWQAPMFkwD1BfYF9AX1BfQF8wX4BfYF9QX4BfUF9wX3BfUFkAD3BZAAiwD5BfcFiwD5BYsAhwD6BfgF9wX6BfcF+QX5BYcAgQD5BYEA+wX6BfkF+wX6BfsF/QWCAH4A/wWCAP8F/AX8Bf8FAAb8BQAG/gX/BQEGAgb/BQIGAAZ+AHoAAQZ+AAEG/wV6AHgAAwZ6AAMGAQYBBgMGBAYBBgQGAgYGBgQGAwYGBgMGBQYFBgMGeAAFBngAdQAIBgoGBgYIBgYGBQZxAAgGBQZxAAUGdQAMBgkGBwYMBgcGCwYLBgcGcgALBnIAbAANBg4GDAYNBgwGCwZpAA0GCwZpAAsGbAAQBg4GDQYQBg0GDwYPBg0GaQAPBmkAZgASBhAGDwYSBg8GEQYRBg8GZgARBmYAYgATBhEGYgATBmIAYAAUBhIGEQYUBhEGEwYWBhQGEwYWBhMGFQYVBhMGYAAVBmAAXAAXBhUGXAAXBlwAWgAYBhYGFQYYBhUGFwYZBhoGGAYZBhgGFwZXABkGFwZXABcGWgAcBhoGGQYcBhkGGwYbBhkGVwAbBlcAVAAeBhwGGwYeBhsGHQYdBhsGVAAdBlQAUAAfBh0GUAAfBlAATgAgBh4GHQYgBh0GHwYiBiAGHwYiBh8GIQYhBh8GTgAhBk4ASgAjBiEGSgAjBkoASAAkBiIGIQYkBiEGIwYlBicGJAYlBiQGIwZEACUGIwZEACMGSAAqBigGJgYqBiYGKQYpBiYGRQApBkUAQQA/ACsGKQY/ACkGQQArBiwGKgYrBioGKQYtBi4GLAYtBiwGKwY9AC0GKwY9ACsGPwAwBi4GLQYwBi0GLwYvBi0GPQAvBj0AOwAxBi8GOwAxBjsAOQAyBjAGLwYyBi8GMQY3ADMGMQY3ADEGOQAzBjQGMgYzBjIGMQY4BjQGMwY4BjMGNgY2BjMGNwA2BjcANQA5BjUGNAA5BjQAMQA6BjcGNQY6BjUGOQY7BjkGMQA7BjEALwA8BjoGOQY8BjkGOwY+BjwGOwY+BjsGPQY9BjsGLwA9Bi8ALQArAD4GPQYrAD0GLQArAEAGPwYrAD8GPgY/BkEGPAY/BjwGPgZABkIGQQZABkEGPwZCBkQGQwZCBkMGQQZBBkMGOgZBBjoGPAZDBkUGNwZDBjcGOgZEBkcGRQZEBkUGQwZGBkkGNAZGBjQGOAZIBkoGSQZIBkkGRgZJBkoGTAZJBkwGSwY0BkkGSwY0BksGMgZLBk0GMAZLBjAGMgZMBk4GTQZMBk0GSwZNBk8GLgZNBi4GMAZOBlAGTwZOBk8GTQZRBk8GUAZRBlAGUgYsBi4GTwYsBk8GUQZRBlIGVAZRBlQGUwYsBlEGUwYsBlMGKgZTBlYGKAZTBigGKgZUBlgGVgZUBlYGUwZVBlkGJAZVBiQGJwZXBloGWQZXBlkGVQZZBlsGIgZZBiIGJAZaBlwGWwZaBlsGWQZbBlwGXgZbBl4GXQYiBlsGXQYiBl0GIAZdBl8GHgZdBh4GIAZeBmAGXwZeBl8GXQZfBmAGYgZfBmIGYQYeBl8GYQYeBmEGHAZhBmMGGgZhBhoGHAZiBmQGYwZiBmMGYQYYBhoGYwYYBmMGZQZlBmMGZAZlBmQGZgYWBhgGZQYWBmUGZwZnBmUGZgZnBmYGaAZpBmcGaAZpBmgGagYUBhYGZwYUBmcGaQYSBhQGaQYSBmkGawZrBmkGagZrBmoGbAZtBmsGbAZtBmwGbgYQBhIGawYQBmsGbQZvBg4GEAZvBhAGbQZwBm8GbQZwBm0GbgabDY4NcQabDXEGcwaODY8NdAaODXQGcQZzBnEGcgZzBnIGkgZxBnQGdQZxBnUGcgZ4BnUGdAZ4BnQGdgaQDY8NcAaQDXAGdwaSDZANdwaSDXcGfAZ+BngGdgZ+BnYGegaTDZENewaTDXsGgAaBBn0GeQaBBnkGfwaUDZMNgAaUDYAGgwaEBoEGfwaEBn8GggaUDZUNhQaUDYUGggaCBoUGhwaCBocGhAaVDZYNiAaVDYgGhQaFBogGiQaFBokGhwaXDZYNBAaXDQQGBgaLBokGiAaLBogGigaNBo8GiwaNBosGigaZDY0GigaZDYoGlw2RBo4GjAaRBowGkAaaDZgNCQaaDQkGDAZzBpIGkQZzBpEGkAabDXMGkAabDZAGmg2SBpUGkwaSBpMGkQaOBpEGkwaOBpMGlAaWBpQGkwaWBpMGlQaZBpYGlQaZBpUGlwaVBpIGcgaVBnIGlwaaBpkGlwaaBpcGmAaXBnIGdQaXBnUGmAadBpYGmQadBpkGmwabBpkGmgabBpoGnAaeBpsGnAaeBpwGnwagBp0GmwagBpsGngahBp4GnwahBp8GpAajBqAGngajBp4GoQaiBqEGpAaiBqQGpQbABqMGoQbABqEGoganBqUGpAanBqQGpgamBqQGnwamBp8GqAasBqcGpgasBqYGqgaqBqYGqAaqBqgGfgauBqsGqQauBqkGrQatBqkGfQatBn0GgQawBq4GrQawBq0GrwavBq0GgQavBoEGhAavBoQGhwavBocGsQawBq8GsQawBrEGsgaxBocGiQaxBokGswayBrEGswayBrMGtAa0BrMGtQa0BrUGtgazBokGiwazBosGtQa2BrUGuAa2BrgGuga1BosGjwa1Bo8GuAa5BrcGuwa5BrsGvAa3Bo4GvQa3Br0Guwa8BrsGowa8BqMGwAa7Br0GoAa7BqAGowaOBpQGvgaOBr4GvQagBr0GvgagBr4GnQaWBp0GvgaWBr4GlAa/BrwGwAa/BsAGwQYjB7kGvAYjB7wGvwbBBsAGogbBBqIGwwbCBsEGwwbCBsMGxQYtB78GwQYtB8EGwgbDBqIGpQbDBqUGxAbFBsMGxAbFBsQGxgbIBsoGwgbIBsIGxQbMBsgGxQbMBsUGxgbNBs8GyQbNBskGxwbOBs0GxwbOBscGywbSBtAGzQbSBs0GzgbQBtEGzwbQBs8GzQbVBtEG0AbVBtAG0wbTBtAG0gbTBtIG1AbUBtgG1gbUBtYG0wbTBtYG1wbTBtcG1QbWBtkG2wbWBtsG1wbYBtoG2QbYBtkG1gbaBt0G3AbaBtwG2QbZBtwG3gbZBt4G2wbcBt0G4QbcBuEG3wbeBtwG3wbeBt8G4AbfBuIG4wbfBuMG4AbhBuQG4gbhBuIG3wbiBuUG6AbiBugG4wbkBuoG5QbkBuUG4gbrBuwG5wbrBucG5gbtBusG5gbtBuYG6QbvBuwG6wbvBusG7gbuBusG7QbuBu0G8AbzBu8G7gbzBu4G8QbxBu4G8AbxBvAG8gb0BvEG8gb0BvIG9gb1BvMG8Qb1BvEG9Ab0BvcG+Qb0BvkG9Qb2BvgG9wb2BvcG9Ab3BvgG/Ab3BvwG+gb5BvcG+gb5BvoG+wb7BvoG/Qb7Bv0G/gb6BvwG/wb6Bv8G/Qb9BgAHAQf9BgEH/gb/BgIHAAf/BgAH/QYABwMHBgcABwYHAQcCBwQHAwcCBwMHAAcIBwMHBAcIBwQHCQcLBwYHAwcLBwMHCAcRBw0HCAcRBwgHCQcNBw8HCwcNBwsHCAcVBxAHDgcVBw4HEwcTBw4HEgcTBxIHFAcaBxYHEwcaBxMHFAcWBxgHFQcWBxUHEwcXBxwHHQcXBx0HGQcbBx4HHAcbBxwHFwccBx8HIQccByEHHQceByAHHwceBx8HHAcfByAHsAYfB7AGsgYhBx8HsgYhB7IGtAYdByEHIgcdByIHJQchB7QGtgYhB7YGIgciB7YGugYiB7oGJAclByIHJAclByQHKQcnByUHKQcnBykHKgcZBx0HJQcZByUHJwcoByMHvwYoB78GLQcsBygHLQcsBy0HLwcvBy0HwgYvB8IGygYyBysHLgcyBy4HMAcwBy4HyQYwB8kGzwYxBzAHzwYxB88G0QYzBzIHMAczBzAHMQc1BzQHMgc1BzIHMwc0BzYHKwc0BysHMgc6BzgHNAc6BzQHNQc4BwcHNgc4BzYHNAc8BzsHNwc8BzcHOQcBBwYHNwcBBzcHOwf5BvsGOwf5BjsHPAf+BgEHOwf+BjsH+wY9BzwHOQc9BzkHPgf1BvkGPAf1BjwHPQc+B0IHQAc+B0AHPQdAB/MG9QZAB/UGPQdCB0QHQQdCB0EHQAfvBvMGQAfvBkAHQQdFB0MHRgdFB0YHRwdGB0MHPwdGBz8HSQdIB08HRwdIB0cHRgdKB0gHRgdKB0YHSQdJBzUHMwdJBzMHSgc/BzoHNQc/BzUHSQdKBzMHMQdKBzEHSwdMB0gHSgdMB0oHSwdLBzEH0QZLB9EG1QbVBtcGTAfVBkwHSwfXBtsGTQfXBk0HTAdPB0gHTAdPB0wHTQfbBt4GTgfbBk4HTQdQB08HTQdQB00HTgdPB1AHUQdPB1EHRwdOB1MHUgdOB1IHUAdQB1IHVAdQB1QHUQdTB1cHVAdTB1QHUgdRB1QHVgdRB1YHXAdXB1kHVgdXB1YHVAfgBuMGVwfgBlcHUwfjBugGWQfjBlkHVwdbB1UHWAdbB1gHWgdaB1gH5wZaB+cG7AbsBu8GQQfsBkEHWgdEB1sHWgdEB1oHQQdcB0UHRwdcB0cHUQfeBuAGUwfeBlMHTgc2BwcHDAc2BwwHXQcrBzYHXQcrB10HJgcmB10HFQcmBxUHGAddBwwHEAddBxAHFQceB2EHXgceB14HIAcgB14HrgYgB64GsAZeB18HqwZeB6sGrgZhB2IHXwdhB18HXgcbB2UHYQcbB2EHHgdlB2gHYgdlB2IHYQdkB2kHagdkB2oHZgcaBxQHaQcaB2kHZAdpBwoHBQdpBwUHagcUBxIHCgcUBwoHaQdvB2YHagdvB2oHawdrB2oHBQdrBwUHbgdsB2sHbgdsB24HhQdwB28HawdwB2sHbAdyB3EHbwdyB28HcAdxB3MHZgdxB2YHbwfSBs4GcQfSBnEHcgfOBssGcwfOBnMHcQd0B8wGxgZ0B8YGdQdnB3QHdQdnB3UHYwdjB3UHdgdjB3YHYAd1B8YGxAZ1B8QGdgdgB3YHpwZgB6cGrAZ2B8QGpQZ2B6UGpwZ3B3IHcAd3B3AHeAfUBtIGcgfUBnIHdwd4B3sHeQd4B3kHdwd5B9gG1AZ5B9QGdwd7B3wHegd7B3oHeQfaBtgGeQfaBnkHegd8B3sHfQd8B30Hfgd9B3sHeAd9B3gHgQeAB4wHfgeAB34HfQeDB4AHfQeDB30HgQeBB2wHhQeBB4UHgwd4B3AHbAd4B2wHgQeCB4QHhweCB4cHhgeIB38HggeIB4IHhgeGB4cH+AaGB/gG9gb2BvIGiAf2BogHhgfyBvAGiQfyBokHiAeLB38HiAeLB4gHiQfwBu0GigfwBooHiQeNB4sHiQeNB4kHigeMB44HjweMB48HfgeKB5IHkAeKB5AHjQeOB5EHlAeOB5QHjweUB5EHkweUB5MHlgePB5QHlQePB5UHmQeVB5QHlgeVB5YHlweXB5YH5AaXB+QG4QaWB5MH6gaWB+oG5AaYB5cH4QaYB+EG3QaZB5UHlweZB5cHmAfdBtoGegfdBnoHmAd8B5kHmAd8B5gHegeZB3wHfgeZB34HjwftBukGkgftBpIHigeHB5oH/AaHB/wG+AaEB20HmgeEB5oHhweaBwIH/waaB/8G/AZtBwQHAgdtBwIHmgefBpwGmwefBpsHqAZ+BqgGmwd+BpsHnAeaBpwHmweaBpsHnAaaBpgGnQeaBp0HnAd+BpwHnQd+Bp0HeAZ1BngGnQd1Bp0HmAafBwQGhgafB4YGngeeB4YGgwaeB4MGoAejB58HngejB54HoQehB54HoAehB6AHogekB6EHogekB6IHpgelB6MHoQelB6EHpAfQBaUHpAfQBaQHpwenB6QHpgenB6YHqAepB6cHqAepB6gHqgfOBdAFpwfOBacHqQerB8wFzgWrB84FqQesB6sHqQesB6kHqgerB60HygWrB8oFzAWsB64HrQesB60HqweuB7AHrweuB68HrQetB68HyAWtB8gFygWvB7EHxgWvB8YFyAWwB7IHsQewB7EHrweyB7QHsweyB7MHsQexB7MHxAWxB8QFxgWzB7UHwgWzB8IFxAW0B7YHtQe0B7UHswe2B7kHtwe2B7cHtQe1B7cHvwW1B78FwgW8BcAFuAe8BbgHuwe7B7gHuge7B7oHvAe6BbwFuwe6BbsHvQe9B7sHvAe9B7wHvgfAB78HvQfAB70Hvge/B7gFugW/B7oFvQe/B8AHwge/B8IHwQe4Bb8HwQe4BcEHtgXBB8MHtAXBB7QFtgXCB8QHwwfCB8MHwQfFB8MHxAfFB8QHxgeyBbQFwweyBcMHxQfHB7AFsgXHB7IFxQfIB8cHxQfIB8UHxgeuBbAFxweuBccHyQfJB8cHyAfJB8gHygfLB6wFrgXLB64FyQfMB8sHyQfMB8kHygfNB6oFrAXNB6wFywfOB80HywfOB8sHzAfNB84H0QfNB9EHzweqBc0HzweqBc8H0AfPB9IH1AfPB9QH0AfRB9MH0gfRB9IHzwfVB9IH0wfVB9MH1wfWB9QH0gfWB9IH1QfZB9sH1gfZB9YH1QfcB9kH1QfcB9UH1wfYB94H3wfYB98H2gfdB+AH3gfdB94H2AfeB+IH5AfeB+QH3wfgB+YH4gfgB+IH3gfhB+cH6AfhB+gH4wflB+kH5wflB+cH4QfnB+oH6wfnB+sH6AfpB+wH6gfpB+oH5wfvB+sH6gfvB+oH7QftB+oH7AftB+wH7gfyB/AH7QfyB+0H7gfwB/EH7wfwB+8H7Qf1B/EH8Af1B/AH8wfzB/AH8gfzB/IH9Af6B/YH8wf6B/MH9Af2B/gH9Qf2B/UH8wf+B/kH9wf+B/cH/Af8B/cH+wf8B/sH/QcACP8H/AcACPwH/Qf/B6UF/gf/B/4H/AeiBaUF/weiBf8HAQgBCP8HAAgBCAAIAggDCKAFogUDCKIFAQgECAMIAQgECAEIAggDCAUIngUDCJ4FoAUECAYIBQgECAUIAwgFCAcInAUFCJwFngUGCAgIBwgGCAcIBQgKCJwFBwgKCAcICQgJCAcICAgJCAgICwgOCAoICQgOCAkIDAgMCAkICwgMCAsIDQgMCA0IEwgMCBMIDwgOCAwIDwgOCA8IEQgQCBUIFwgQCBcIEggUCBYIFQgUCBUIEAgVCBYIGggVCBoIGAgXCBUIGAgXCBgIGQgYCBsIHQgYCB0IGQgaCBwIGwgaCBsIGAgbCBwIIAgbCCAIHggdCBsIHggdCB4IHwghCB8IHgghCB4IIAgcCCMIIggcCCIIIAghCCAIIgghCCIIJAglCCQIIgglCCIIIwghCCQIJgghCCYIKAglCCcIJgglCCYIJAgpCCgIJggpCCYIJwghCCgIKgghCCoILAgpCCsIKggpCCoIKAgtCCwIKggtCCoIKwgtCC8ILggtCC4ILAghCCwILgghCC4IMAghCDAIMggvCDEIMAgvCDAILggzCDIIMAgzCDAIMQghCDIINAgzCDUINAgzCDQIMgghCDQINgghCDYIHwg1CDcINgg1CDYINAgdCB8INggdCDYINwg4CDcINQg4CDUIOggZCB0INwgZCDcIOAg5CDgIOgg5CDoIOwgXCBkIOAgXCDgIOQg9CDsIOgg9CDoIPAg6CDUIMwg6CDMIPAg8CDMIMQg8CDEIPgg9CDwIPgg9CD4IPwg/CD4IQAg/CEAIQQg+CDEILwg+CC8IQAhACC8ILQhACC0IQghDCEEIQAhDCEAIQghDCEIIRAhDCEQIRQhCCC0IKwhCCCsIRAhFCEQIRghFCEYIRwhECCsIKQhECCkIRghGCCkIJwhGCCcISAhHCEYISAhHCEgISQhJCEgISghJCEoISwhICCcIJQhICCUISghKCCUIIwhKCCMITAhLCEoITAhLCEwITQhNCEwIGghNCBoIFghMCCMIHAhMCBwIGghPCE0IFghPCBYIFAhSCEsITQhSCE0ITwhQCE4IEwhQCBMIDQhTCFEITghTCE4IUAhUCEkISwhUCEsIUghWCFUIUQhWCFEIUwhZCFgIVQhZCFUIVghXCEcISQhXCEkIVAhaCEUIRwhaCEcIVwhcCFsIWAhcCFgIWQhfCF4IWwhfCFsIXAhdCEMIRQhdCEUIWghfCGIIYQhfCGEIXghgCEEIQwhgCEMIXQhjCD8IQQhjCEEIYAhlCGQIYQhlCGEIYghoCGcIZAhoCGQIZQhmCD0IPwhmCD8IYwhpCDsIPQhpCD0IZghoCGsIaghoCGoIZwhuCGwIaghuCGoIawhtCDkIOwhtCDsIaQgOCBEIbAgOCGwIbggSCBcIOQgSCDkIbQhwCG8IbghwCG4IawhvCAoIDghvCA4IbgiYBZoFbwiYBW8IcAiaBZwFCgiaBQoIbwhoCHEIcAhoCHAIawhxCHIImAVxCJgFcAhzCHEIaAhzCGgIZQh1CHIIcQh1CHEIcwh0CHMIZQh0CGUIYgh2CHUIcwh2CHMIdAh4CHIIdQh4CHUIdwh1CHYIeQh1CHkIdwh3CHoIewh3CHsIeAh5CH0Iegh5CHoIdwh6CHwIXAp6CFwKewh9CH4IfAh9CHwIegh/CIAIfgh/CH4IfQh5CIEIfwh5CH8IfQiCCIUIgAiCCIAIfwiECIIIfwiECH8IgQiCCIQIiAiCCIgIhgiHCIUIggiHCIIIhgiHCIYIigiHCIoIiwiGCIgIjgiGCI4IigiLCIoIjAiLCIwI6wiKCI4IkgiKCJIIjAiYCJMIjwiYCI8IlAiUCI8IiQiUCIkIlgiVCJQIlgiVCJYIDgmaCJgIlAiaCJQIlQicCJsIlwicCJcImQibCJ4IkAibCJAIlwigCJ8ImwigCJsInAifCKIIngifCJ4ImwikCKMInwikCJ8IoAijCKYIogijCKIInwioCKcIowioCKMIpAinCKoIpginCKYIowisCKsIpwisCKcIqAirCK4IqgirCKoIpwiwCK8IqwiwCKsIrAivCLIIrgivCK4Iqwi3CLMIrwi3CK8IsAizCLYIsgizCLIIrwi4CLQIswi4CLMItwi0CKMJtgi0CLYIswi7CLkItwi7CLcIsAi6CLgItwi6CLcIuQi5CLwIvgi5CL4Iugi7CL0IvAi7CLwIuQi9CMEIvwi9CL8IvAi+CLwIvwi+CL8IwwjCCMYIxQjCCMUIwAjECMAIxQjECMUIxwjGCMkIyAjGCMgIxQjHCMUIyAjHCMgIygjNCMsIyAjNCMgIyQjKCMgIywjKCMsIzAjPCMwIywjPCMsIzgjOCMsIzQjOCM0I0AiiCc8IzgiiCc4ItQi1CM4I0Ai1CNAIsQixCNAI0QixCNEIrQjQCM0I0wjQCNMI0QitCNEI0gitCNIIqQjRCNMI1AjRCNQI0gjUCNMI1QjUCNUI1gjTCM0IyQjTCMkI1QjXCNUIyQjXCMkI2AjZCNYI1QjZCNUI1whiBtcI2AhiBtgIZAZgBtkI1whgBtcIYgZeBtoI2QheBtkIYAbaCNsI1gjaCNYI2QhcBtwI2ghcBtoIXgbcCN0I2wjcCNsI2ghaBt4I3AhaBtwIXAbeCN8I3QjeCN0I3AhXBuAI3ghXBt4IWgbgCOII3wjgCN8I3gjkCOEIWAbkCFgGVAbmCOMI4QjmCOEI5AjlCOQIVAblCFQGUgbnCOYI5AjnCOQI5QiLCOsI4wiLCOMI5gjnCIcIiwjnCIsI5gjpCOgI5wjpCOcI5QjnCOgIhQjnCIUIhwh8CH4I6Ah8COgI6QjoCH4IgAjoCIAIhQhQBukI5QhQBuUIUgZQBlwKfAhQBnwI6QjiCOoI7AjiCOwI3wjuCOwI6gjuCOoIjQjvCO0I7AjvCOwI7gjsCO0I3QjsCN0I3widCO4IjQidCI0IkQihCO8I7gihCO4InQilCPAI7wilCO8IoQjwCPEI7QjwCO0I7wipCNII8AipCPAIpQjSCNQI8QjSCPEI8AjtCPEI2wjtCNsI3QjxCNQI1gjxCNYI2wjyCPMIZAbyCGQG2AjGCPII2AjGCNgIyQjCCPUI8gjCCPIIxgj1CPcI8wj1CPMI8gi9CPgI9Ai9CPQIwQj4CPkI9gj4CPYI9Ai7CPoI+Ai7CPgIvQj6CKwH+Qj6CPkI+Aj7CK4HrAf7CKwH+gj8CPsI+gj8CPoIuwj+CP0I+wj+CPsI/Aj9CLAHrgf9CK4H+wgACf8I/QgACf0I/gj/CLIHsAf/CLAH/QgCCQEJ/wgCCf8IAAkBCbQHsgcBCbIH/wgECQMJAQkECQEJAgkDCbYHtAcDCbQHAQkHCQUJAwkHCQMJBAkFCbkHtgcFCbYHAwm6BwYJCQm6BwkJvAcGCQgJCwkGCQsJCQm8BwkJCgm8BwoJvgcJCQsJDAkJCQwJCgkMCQsJlQgMCZUIDgkLCQgJmggLCZoIlQgPCQ0JDAkPCQwJDgkKCQwJDQkKCQ0JFwkPCQ4JlggPCZYIEAkQCRMJEQkQCREJDwkNCQ8JEQkNCREJFAkRCRIJFQkRCRUJFAkTCSMJEgkTCRIJEQkUCRUJGAkUCRgJFgkNCRQJFgkNCRYJFwkXCRYJwgcXCcIHwAcWCRgJxAcWCcQHwgfEBxgJGQnEBxkJxgcYCRUJGwkYCRsJGQnGBxkJGgnGBxoJyAcZCRsJHAkZCRwJGgkcCRsJHQkcCR0JHgkbCRUJEgkbCRIJHQkdCR8JIAkdCSAJHgkSCSMJHwkSCR8JHQkfCSEJNwkfCTcJIAkjCSQJIQkjCSEJHwknCSQJIwknCSMJEwkmCTQJIgkmCSIJJQkpCSYJJQkpCSUJKAmDCCcJEwmDCBMJEAmECIEIKQmECCkJKAmBCCsJKgmBCCoJKQkqCSwJJgkqCSYJKQkqCSsJLwkqCS8JLQksCSoJLQksCS0JLgkuCS0JXAguCVwIWQgtCS8JXwgtCV8IXAhfCC8JdAhfCHQIYggvCSsJdggvCXYIdAgwCS4JWQgwCVkIVggyCSwJLgkyCS4JMAkxCTAJVggxCVYIUwgzCTIJMAkzCTAJMQkmCSwJMgkmCTIJNAkyCTMJNQkyCTUJNAk0CTUJOAk0CTgJIgk1CTMJjAk1CYwJNgk1CTYJOQk1CTkJOAmLCToJOAmLCTgJOQk6CUEJPQk6CT0JOAk4CT0JQAk4CUAJPAk3CTsJhAk3CYQJIAlBCUMJPwlBCT8JPQk9CT8JZAk9CWQJQAlFCUQJQQlFCUEJOglECUcJQwlECUMJQQlJCUgJRAlJCUQJRQlICUsJRwlICUcJRAkACP0HSAkACEgJSQn9B/sHSwn9B0sJSAn6B/QHTAn6B0wJSglKCUwJTQlKCU0JRglMCU4JTwlMCU8JTQn0B/IHTgn0B04JTAnyB+4HUAnyB1AJTglOCVAJUQlOCVEJTwlQCVIJUwlQCVMJUQnuB+wHUgnuB1IJUAlUCVIJ7AdUCewH6QdXCVMJUglXCVIJVAlVCVQJ6QdVCekH5QdYCVcJVAlYCVQJVQlaCV0JUwlaCVMJVwlbCVoJVwlbCVcJWAlfCV4JWglfCVoJWwleCWEJXQleCV0JWgljCWIJXgljCV4JXwliCWUJYQliCWEJXgk+CWYJYgk+CWIJYwlmCWcJZQlmCWUJYgloCWkJZwloCWcJZglCCWgJZglCCWYJPglGCU0JaAlGCWgJQglNCU8JaQlNCWkJaAlpCWoJZQlpCWUJZwlPCVEJaglPCWoJaQlqCV0JYQlqCWEJZQlRCVMJXQlRCV0JaglACWQJYAlACWAJawlrCWAJXAlrCVwJbgk8CUAJawk8CWsJbQltCWsJbgltCW4JcAlwCW4JcQlwCXEJcwluCVwJWQluCVkJcQlxCVkJVglxCVYJdAlzCXEJdAlzCXQJdgl2CXQJ4Ad2CeAH3Qd0CVYJ5gd0CeYH4AfXB3cJdQnXB3UJ3Ad3CXgJcgl3CXIJdQnTB3kJdwnTB3cJ1wd5CXoJeAl5CXgJdwl7CXkJ0wd7CdMH0Qd9CXoJeQl9CXkJewl8CXsJ0Qd8CdEHzgd+CX0Jewl+CXsJfAmACX8JfQmACX0Jfgl/CYEJegl/CXoJfQkeCYIJfwkeCX8JgAmCCYMJgQmCCYEJfwkgCYQJggkgCYIJHgmECYUJgwmECYMJggk7CWwJhQk7CYUJhAmBCYMJhQmBCYUJhgmGCYUJbAmGCWwJbwl4CYYJbwl4CW8Jcgl6CYEJhgl6CYYJeAkcCR4JgAkcCYAJhwmACX4JiAmACYgJhwmJCYcJiAmJCYgJigkaCRwJhwkaCYcJiQnKB4kJignKB4oJzAfIBxoJiQnIB4kJygfOB8wHignOB4oJfAmKCYgJfgmKCX4JfAkACEkJiwkACIsJAghJCUUJOglJCToJiwkCCIsJOQkCCDkJBAg2CQYIBAg2CQQIOQmMCQgIBgiMCQYINgmNCYwJMwmNCTMJMQkLCAgIjAkLCIwJjQlQCI0JMQlQCDEJUwgNCAsIjQkNCI0JUAh2CCsJgQh2CIEIeQjAB74HCgnABwoJFwmWCIkIgwiWCIMIEAkHCQQJnAgHCZwImQgECQIJoAgECaAInAigCAIJAAmgCAAJpAgACf4IqAgACagIpAioCP4I/AioCPwIrAj8CLsIsAj8CLAIrAj2CPkIjgn2CI4JkAn5CKwHqgf5CKoHjgmQCY4JkQmQCZEJkwmOCaoHqAeOCagHkQmTCZEJlAmTCZQJlgmRCagHpgeRCaYHlAmWCZQJlwmWCZcJmQmUCaYHogeUCaIHlwmZCZcJmgmZCZoJmwmXCaIHoAeXCaAHmgmbCZoJgAabCYAGewaaCaAHgwaaCYMGgAadCZwJfAadCXwGdwaeCZgJnAmeCZwJnQluBp0JdwZuBncGcAZsBp4JnQlsBp0JbgZqBp8JnglqBp4JbAafCZUJmAmfCZgJngloBqAJnwloBp8JagagCZIJlQmgCZUJnwlmBqEJoAlmBqAJaAahCY8JkgmhCZIJoAlkBvMIoQlkBqEJZgbzCPcIjwnzCI8JoQmnCaQJzwinCc8IogmkCaYJzAikCcwIzwipCaUJpAmpCaQJpwmlCcAJpgmlCaYJpAmrCagJowmrCaMJrQmsCaoJqAmsCagJqwmwCawJqwmwCasJrgmuCasJrQmuCa0JrwmuCa8JswmuCbMJsQmwCa4JsQmwCbEJsgmxCbQJtQmxCbUJsgmzCbYJtAmzCbQJsQm1CbQJtwm1CbcJuQm0CbYJwwi0CcMItwm4CbsJvAm4CbwJugnECMcIuwnECLsJuAm8CbsJvQm8Cb0Jvgm7CccIygi7CcoIvQm9CcoIzAi9CcwIpgm+Cb0Jpgm+CaYJwAm8Cb4Jvwm8Cb8JQQrACcEJvwnACb8JvgnECcEJwAnECcAJpQnICcMJwQnICcEJxAm/CcEJwwm/CcMJOwrFCcQJpQnFCaUJqQnJCcgJxAnJCcQJxQnNCcwJxwnNCccJygnMCc4JwgnMCcIJxwnNCdIJzwnNCc8JzAnMCc8J0AnMCdAJzgnPCdEJ9AnPCfQJ0AnSCdMJ0QnSCdEJzwnVCdQJ0gnVCdIJzQnUCdYJ0wnUCdMJ0gnZCdcJ1AnZCdQJ1QnXCdgJ1gnXCdYJ1AnaCdwJ2AnaCdgJ1wnbCdoJ1wnbCdcJ2QneCd0J2gneCdoJ2wndCd8J3AndCdwJ2gnhCeAJ3QnhCd0J3gngCeIJ3wngCd8J3QnhCeYJ4wnhCeMJ4AngCeMJ5AngCeQJ4gnjCeUJ+wnjCfsJ5AnmCecJ5QnmCeUJ4wnoCeoJ5wnoCecJ5gnpCegJ5gnpCeYJ4QnsCesJ6AnsCegJ6QnrCe0J6gnrCeoJ6AnsCc4J7gnsCe4J6wnuCe8J7QnuCe0J6wnQCfAJ7gnQCe4JzgnwCfEJ7wnwCe8J7gn0CfIJ8An0CfAJ0AnyCfMJ8QnyCfEJ8An1CfcJ8wn1CfMJ8gn2CfUJ8gn2CfIJ9An5CfgJ9Qn5CfUJ9gn4CfoJ9wn4CfcJ9QniCeQJ+AniCfgJ+QnkCfsJ+gnkCfoJ+An6CfwJ/Qn6Cf0J9wn7Cf8J/An7CfwJ+gn8Cf4JDwr8CQ8K/Qn/CQAK/gn/Cf4J/AkBCgIKAAoBCgAK/wnlCQEK/wnlCf8J+wnnCQMKAQrnCQEK5QkDCgQKAgoDCgIKAQoFCgYKBAoFCgQKAwrqCQUKAwrqCQMK5wntCQcKBQrtCQUK6gkHCggKBgoHCgYKBQrtCe8JCQrtCQkKBwoJCgoKCAoJCggKBwrxCQsKCQrxCQkK7wkLCgwKCgoLCgoKCQrzCQ0KCwrzCQsK8QkNCg4KDAoNCgwKCwr9CQ8KDgr9CQ4KDQrzCfcJ/QnzCf0JDQoMCg4KDwoMCg8KEAoQCg8K/gkQCv4JEQoICgoKDAoICgwKEAoIChAKEQoIChEKBgoGChEKAgoGCgIKBAoRCv4JAAoRCgAKAgr5CRIKEwr5CRMK4gn2CRUKEgr2CRIK+QkSChQKJQoSCiUKEwoVChYKFAoVChQKEgr2CfQJFwr2CRcKFQoXChgKFgoXChYKFQoZChoKGAoZChgKFwrRCRkKFwrRCRcK9AnTCRsKGQrTCRkK0QkbChwKGgobChoKGQrWCR0KGwrWCRsK0wkdCh4KHAodChwKGwrYCR8KHQrYCR0K1gkfCiAKHgofCh4KHQohCiIKIAohCiAKHwrYCdwJIQrYCSEKHwrfCSMKIQrfCSEK3AkjCiQKIgojCiIKIQriCRMKIwriCSMK3wkTCiUKJAoTCiQKIwokCiYKJwokCicKIgolCikKJgolCiYKJAomCigKOQomCjkKJwopCioKKAopCigKJgorCiwKKgorCioKKQoUCisKKQoUCikKJQoWCi0KKwoWCisKFAotCi4KLAotCiwKKwoWChgKLwoWCi8KLQovCjAKLgovCi4KLQoxCjIKMAoxCjAKLwoaCjEKLwoaCi8KGAocCjMKMQocCjEKGgozCjQKMgozCjIKMQoeCjUKMwoeCjMKHAo1CjYKNAo1CjQKMwogCjcKNQogCjUKHgo3CjgKNgo3CjYKNQoiCicKNwoiCjcKIAonCjkKOAonCjgKNwo5CigKNgo5CjYKOAooCioKNAooCjQKNgoqCiwKMgoqCjIKNAosCi4KMAosCjAKMgrCCc4J7AnCCewJOgo8CjoK7Ak8CuwJ6QlBCr8JOwpBCjsKPQo/CjwK6Qk/CukJ4QlDCkEKPQpDCj0KQAq6CbwJQQq6CUEKQwpECkIKPgpECj4KRgq1CbkJQgq1CUIKRAqyCbUJRAqyCUQKRQpFCkQKRgpFCkYKSApJCkcK3glJCt4J2wlHCj8K4QlHCuEJ3glMCkUKSApMCkgKSgpJCtsJ2QlJCtkJSwrZCdUJTgrZCU4KSwpKCk0KTwpKCk8KTApNCssJxglNCsYJTwrVCc0JygnVCcoJTgpMCk8KrAlMCqwJsAlPCsYJqglPCqoJrAmyCUUKTAqyCUwKsAmzCVIKUAqzCVAKtgnDCLYJUArDCFAKUQpTClEKUApTClAKUgpUClUKUwpUClMKUgqvCVQKUgqvCVIKswmvCa0JVgqvCVYKVApVClQKVgpVClYKVwqjCVcKVgqjCVYKrQmjCbQIWAqjCVgKVwpVClcKWApVClgKWQq4CFkKWAq4CFgKtAhTClUKWQpTClkKWgpZCrgIughZCroIWgq6CL4IWwq6CFsKWgpTCloKWwpTClsKUQrDCFEKWwrDCFsKvghdClwKUAZdClAGTgZ7CFwKXQp7CF0KXgpeCl0KXwpeCl8KYApfCl0KTgZfCk4GTAZgCl8KYQpgCmEKZApKBmEKXwpKBl8KTAZjCmEKSgZjCkoGSAZnCmQKYQpnCmEKYwpqCmUKZApqCmQKZwpzCmAKZApzCmQKZQpoCmYKYgpoCmIKcgprCmkKZgprCmYKaAqKBYwFZQqKBWUKagqGBYkFaQqGBWkKawprCmgKbgprCm4KbAqEBYYFawqEBWsKbApsCm4KcApsCnAKbQqEBWwKbQqEBW0KggVoCnIKbwpoCm8KbgpuCm8KcQpuCnEKcAorAG0KcAorAHAKcQpvCkIGQAZvCkAGcQorAHEKQAZvCnIKRAZvCkQGQgZyCmIKRwZyCkcGRAYrAIIFbQplCowFjgVlCo4FcwpzCnQKXgpzCl4KYAp0CnMKjgV0Co4FkAWUBZYFeAiUBXgIdAqQBZIFlAWQBZQFdAp0CngIewh0CnsIXgqWBZgFcgiWBXIIeAh2CnUK/gd2Cv4HpQV1CnkK+Qd1CvkH/gfaB98HdQraB3UKdgrfB+QHeQrfB3kKdQrjB+gHegrjB3oKeAp4CnoK9Qd4CvUH+Ad6Cu8H8Qd6CvEH9QfoB+sH7wfoB+8Hegp7CtYH2wd7CtsHdwqoBXsKdwqoBXcKpgWqBdAHewqqBXsKqAXQB9QH1gfQB9YHewp8CqUH0AV8CtAF0gV+CqMHpQd+CqUHfAp9CnwK0gV9CtIF1AV/Cn4KfAp/CnwKfQqACn4KfwqACn8KgQqfB6MHfgqfB34KgAoCBoAKgQoCBoEKAAYEBp8HgAoEBoAKAgYABoEKgwoABoMK/gWBCn8KhgqBCoYKgwr9BYIKhAr9BYQK+gWCCoUKhwqCCocKhAqHCoUKiAqHCogKigqGCn8KfQqGCn0KiQqKCogK1wWKCtcF2gWJCn0K1AWJCtQF2AXaBdwFiwraBYsKigqKCosKjAqKCowKhwqLCo0KjgqLCo4KjArcBd4FjQrcBY0KiwqNCo8KkAqNCpAKjgreBeAFjwreBY8KjQqPCpEKkgqPCpIKkArgBeIFkQrgBZEKjwqRCpMKlAqRCpQKkgriBeQFkwriBZMKkQqTCugF6gWTCuoFlArkBeYF6AXkBegFkwqUCuoF7AWUCuwFlQqSCpQKlQqSCpUKlgqWCpUK8AWWCvAF8gWVCuwF7gWVCu4F8AWXCpYK8gWXCvIF9AWQCpIKlgqQCpYKlwqYCpcK9AWYCvQF9gWOCpAKlwqOCpcKmAqZCpgK9gWZCvYF+AWMCo4KmAqMCpgKmQqECpkK+AWECvgF+gWHCowKmQqHCpkKhAqaCpsKwwqaCsMKnAqdCpoKnAqdCpwKoAqeCp0KoAqeCqAKoQqhCqAKowqhCqMKpAqgCpwKpgqgCqYKowqkCqMKpwqkCqcKqAqmCqoKpwqmCqcKowqrCqgKpwqrCqcKqgqrCqoKrQqrCq0KrwqmCq4KrQqmCq0KqgqxCq8KrQqxCq0KrgqyCrQKswqyCrMKsAqsCrAKswqsCrMKtQq2CrUKswq2CrMKtAqsCrUKtwqsCrcKqQq2CrgKtwq2CrcKtQqlCqkKtwqlCrcKuAq6CrkKuAq6CrgKtgq5CqIKpQq5CqUKuAq8CrsKuQq8CrkKugq7Cp8Kogq7CqIKuQq9CrwKugq9CroKwAq/Cr0KwAq/CsAKwgrCCsAKtArCCrQKsgrACroKtgrACrYKtArDCsEKsQrDCrEKrgqbCr4KwQqbCsEKwwqcCsMKrgqcCq4KpgrFCsgK7QrFCu0KxgrJCssKxwrJCscKxArKCswKywrKCssKyQrMCs4KzQrMCs0KywrLCs0KzwrLCs8KxwrOCtIK0QrOCtEKzQrPCs0K0QrPCtEK0wrWCtMK0QrWCtEK0grVCtkK1wrVCtcK1ArQCtQK1wrQCtcK2AraCtgK1wraCtcK2QraCtkK2wraCtsK3ArVCt0K2wrVCtsK2QrfCtwK2wrfCtsK3QrWCtIK4QrWCuEK3grgCt4K4QrgCuEK4grOCuIK4QrOCuEK0grlCuAK4grlCuIK4wrjCuIKzgrjCs4KzAroCuUK4wroCuMK5grmCuMKzArmCswKygrpCusK5ArpCuQK5wrqCuwK6wrqCusK6QrsCtoK3ArsCtwK6wrrCtwK3wrrCt8K5ArtCtgK2grtCtoK7ArGCu0K7ArGCuwK6grICtAK2ArICtgK7QrvCvIKFwvvChcL8ArzCvUK8QrzCvEK7gr0CvYK9Qr0CvUK8wr2CvgK9wr2CvcK9Qr1CvcK+Qr1CvkK8Qr4CvwK+wr4CvsK9wr5CvcK+wr5CvsK/QoAC/0K+woAC/sK/Ar/CgMLAQv/CgEL/gr6Cv4KAQv6CgELAgsECwILAQsECwELAwsECwMLBQsECwULBgv/CgcLBQv/CgULAwsJCwYLBQsJCwULBwsAC/wKCwsACwsLCAsKCwgLCwsKCwsLDAv4CgwLCwv4CgsL/AoPCwoLDAsPCwwLDQsNCwwL+AoNC/gK9goSCw8LDQsSCw0LEAsQCw0L9goQC/YK9AoTCxULDgsTCw4LEQsUCxYLFQsUCxULEwsWCwQLBgsWCwYLFQsVCwYLCQsVCwkLDgsXCwILBAsXCwQLFgvwChcLFgvwChYLFAvyCvoKAgvyCgILFwsYCxoLQQsYC0ELGQsbCx4LGgsbCxoLGAsdCyALHgsdCx4LGwsgCyMLIQsgCyELHgseCyELJAseCyQLGgsjCycLJQsjCyULIQskCyELJQskCyULKAspCygLJQspCyULJwspCy0LKwspCysLKAskCygLKwskCysLLAsvCywLKwsvCysLLQswCy4LMQswCzELMgsqCzMLMQsqCzELLgs0CzILMQs0CzELMwsqCyYLNQsqCzULMws0CzMLNQs0CzULNgsiCzYLNQsiCzULJgs4CzQLNgs4CzYLNws3CzYLIgs3CyILHws6CzgLNws6CzcLOQs5CzcLHws5Cx8LHAs7Cz4LOAs7CzgLOgs9C0ALPgs9Cz4LOwtACzALMgtACzILPgs+CzILNAs+CzQLOAtBCywLLwtBCy8LPwsZC0ELPwsZCz8LPAsaCyQLLAsaCywLQQtDC0YLawtDC2sLRAtHC0kLRQtHC0ULQgtIC0oLSQtIC0kLRwtKC0wLSwtKC0sLSQtJC0sLTQtJC00LRQtMC1ALTwtMC08LSwtNC0sLTwtNC08LUQtUC1ELTwtUC08LUAtTC1cLVQtTC1ULUgtOC1ILVQtOC1ULVgtYC1YLVQtYC1ULVwtYC1cLWQtYC1kLWgtTC1wLWQtTC1kLVwteC1oLWQteC1kLXAtUC1ALXwtUC18LWwtdC1sLXwtdC18LYAtMC2ALXwtMC18LUAtiC10LYAtiC2ALYQthC2ALTAthC0wLSgtlC2ILYQtlC2ELZAtkC2ELSgtkC0oLSAtnC2kLYwtnC2MLZgtoC2oLaQtoC2kLZwtqC1gLWgtqC1oLaQtpC1oLXgtpC14LYwtrC1YLWAtrC1gLagtEC2sLagtEC2oLaAtGC04LVgtGC1YLawttC3ALlQttC5ULbgtxC3MLbwtxC28LbAtyC3QLcwtyC3MLcQt0C3YLdQt0C3ULcwtzC3ULdwtzC3cLbwt2C3oLeQt2C3kLdQt3C3ULeQt3C3kLewt+C3sLeQt+C3kLegt9C4ELfwt9C38LfAt4C3wLfwt4C38LgAuCC4ALfwuCC38LgQuCC4ELgwuCC4MLhAt9C4YLgwt9C4MLgQuIC4QLgwuIC4MLhgt+C3oLiQt+C4kLhQuHC4ULiQuHC4kLigt2C4oLiQt2C4kLeguMC4cLiguMC4oLiwuLC4oLdguLC3YLdAuPC4wLiwuPC4sLjguOC4sLdAuOC3QLcguRC5MLjQuRC40LkAuSC5QLkwuSC5MLkQuUC4ILhAuUC4QLkwuTC4QLiAuTC4gLjQuVC4ALgguVC4ILlAtuC5ULlAtuC5QLkgtwC3gLgAtwC4ALlQuWC5kLvwuWC78LmAubC50LmgubC5oLlwucC54LnQucC50LmwueC6ALnwueC58LnQudC58LogudC6ILmgugC6QLowugC6MLnwuiC58LowuiC6MLpguoC6YLowuoC6MLpAunC6sLqQunC6kLpQuhC6ULqQuhC6kLqgusC6oLqQusC6kLqwusC6sLrQusC60LrgunC68LrQunC60LqwuxC64LrQuxC60LrwuoC6QLswuoC7MLsAuyC7ALswuyC7MLtAugC7QLswugC7MLpAu3C7ILtAu3C7QLtQu1C7QLoAu1C6ALngu6C7cLtQu6C7ULuAu4C7ULngu4C54LnAu7C70Ltgu7C7YLuQu8C74LvQu8C70Luwu+C6wLrgu+C64LvQu9C64LsQu9C7ELtgu/C6oLrAu/C6wLvguYC78LvguYC74LvAuZC6ELqguZC6oLvwvAC8IL6QvAC+kLwwvFC8ELxAvFC8QLxwvGC8ULxwvGC8cLyAvIC8cLyQvIC8kLygvHC8QLzAvHC8wLyQvKC8kLzQvKC80LzgvMC9ALzQvMC80LyQvSC84LzQvSC80L0AvRC88L0wvRC9ML1QvLC9QL0wvLC9MLzwvWC9UL0wvWC9ML1AvWC9gL1wvWC9cL1QvRC9UL1wvRC9cL2gvbC9oL1wvbC9cL2AvSC9kL3QvSC90LzgvcC94L3QvcC90L2QvKC84L3QvKC90L3gvhC98L3gvhC94L3AvfC8gLygvfC8oL3gvkC+IL3wvkC98L4QviC8YLyAviC8gL3wvlC+ML4AvlC+AL5wvmC+UL5wvmC+cL6AvoC+cL2AvoC9gL1gvnC+AL2wvnC9sL2AvpC+gL1gvpC9YL1AvCC+YL6AvCC+gL6QvDC+kL1AvDC9QLywvqC+sLEwzqCxMM7AvtC+oL7AvtC+wL8AvuC+0L8AvuC/AL8QvxC/AL8wvxC/ML9AvwC+wL9gvwC/YL8wv0C/ML9wv0C/cL+Av2C/oL9wv2C/cL8wv8C/gL9wv8C/cL+gv8C/oL/Qv8C/0L/wv2C/4L/Qv2C/0L+gsBDP8L/QsBDP0L/gsCDAQMAwwCDAMMAAz7CwAMAwz7CwMMBQwGDAUMAwwGDAMMBAz7CwUMBwz7CwcM+QsGDAgMBwwGDAcMBQz1C/kLBwz1CwcMCAwKDAkMCAwKDAgMBgwJDPIL9QsJDPULCAwMDAsMCQwMDAkMCgwLDO8L8gsLDPILCQwNDAwMCgwNDAoMEAwPDA0MEAwPDBAMEgwSDBAMBAwSDAQMAgwQDAoMBgwQDAYMBAwTDBEMAQwTDAEM/gvrCw4MEQzrCxEMEwzsCxMM/gvsC/4L9gsVDBoMjwwVDI8MFwwYDCAMHAwYDBwMFgwbDB8MGQwbDBkMFAwdDCwMHwwdDB8MGwwgDCIMHgwgDB4MHAwhDCMMIgwhDCIMIAxvDCEMIAxvDCAMGAwjDCUMJAwjDCQMIgwiDCQMKAwiDCgMHgwlDIIMJgwlDCYMJAwkDCYMKQwkDCkMKAwnDCoMLAwnDCwMHQwpDC0MKgwpDCoMJwwqDCsMkAwqDJAMLAwtDC4MKwwtDCsMKgwvDDEMLgwvDC4MLQwmDC8MLQwmDC0MKQwwDDIMMQwwDDEMLwyCDDAMLwyCDC8MJgwyDDQMMwwyDDMMMQwxDDMMNgwxDDYMLgw0DI4MNQw0DDUMMwwzDDUMNwwzDDcMNgw2DDcMOgw2DDoMOAwuDDYMOAwuDDgMKwwrDDgMOQwrDDkMkAw4DDoMPAw4DDwMOQw1DD4MOww1DDsMNww+DEAMPQw+DD0MOww/DEEMQAw/DEAMPgyODD8MPgyODD4MNQxBDEMMQgxBDEIMQAxADEIMRwxADEcMPQxDDFYMRQxDDEUMQgxCDEUMSgxCDEoMRww5DDwMRgw5DEYMSwxLDEYMSAxLDEgMTQyQDDkMSwyQDEsMTAxMDEsMTQxMDE0MTwxQDE4MUQxQDFEMUgxODEkMRAxODEQMUQxSDFEMUwxSDFMMVAxRDEQMVwxRDFcMUwxUDFMMVQxUDFUMaAxTDFcMWQxTDFkMVQxcDFgMVgxcDFYMQwxVDFkMWwxVDFsMZQxeDFoMWAxeDFgMXAxdDFwMQwxdDEMMQQxgDF4MXAxgDFwMXQxlDFsMXwxlDF8MYgxiDF8MYQxiDGEMZAxjDGIMZAxjDGQMeQxmDGUMYgxmDGIMYwxoDFUMZQxoDGUMZgxpDGgMZgxpDGYMZwxnDGYMYwxnDGMMdwxqDFQMaAxqDGgMaQxwDGoMaQxwDGkMbAxsDGkMZwxsDGcMbgxtDHMMcQxtDHEMawxrDHEMIQxrDCEMbwxxDHIMIwxxDCMMIQxzDHUMcgxzDHIMcQx0DHcMeAx0DHgMdgxuDGcMdwxuDHcMdAx3DGMMeQx3DHkMeAx4DHkMewx4DHsMegx2DHgMegx2DHoMfgx6DHsMhgx6DIYMfAx+DHoMfAx+DHwMfwx9DH8MgQx9DIEMgAx1DH0MgAx1DIAMcgxyDIAMJQxyDCUMIwyADIEMggyADIIMJQyBDIMMMAyBDDAMggx/DHwMgwx/DIMMgQyDDIQMMgyDDDIMMAx8DIYMhAx8DIQMgwyEDIUMNAyEDDQMMgyGDIcMhQyGDIUMhAx7DIkMhwx7DIcMhgyJDIsMiAyJDIgMhwyHDIgMjQyHDI0MhQx5DGQMiQx5DIkMewxkDGEMiwxkDIsMiQyKDGAMXQyKDF0MjAyIDIoMjAyIDIwMjQyMDF0MQQyMDEEMPwyNDIwMPwyNDD8MjgyFDI0MjgyFDI4MNAwXDI8MagwXDGoMcAyPDFIMVAyPDFQMagwaDFAMUgwaDFIMjwwfDEwMTwwfDE8MGQwsDJAMTAwsDEwMHwyRDJMMxgyRDMYMkgySDJcMlAySDJQMkQyUDJYMkwyUDJMMkQyVDKIMlgyVDJYMlAyXDJkMlQyXDJUMlAyYDJoMmQyYDJkMlwzFDJgMlwzFDJcMkgyaDJwMmwyaDJsMmQyZDJsMngyZDJ4MlQycDMkMnQycDJ0MmwybDJ0MnwybDJ8MngyeDKAMogyeDKIMlQyfDKMMoAyfDKAMngygDKEMxwygDMcMogyjDKQMoQyjDKEMoAylDKcMpAylDKQMowydDKUMowydDKMMnwymDKgMpwymDKcMpQzJDKYMpQzJDKUMnQyoDKoMqQyoDKkMpwynDKkMrAynDKwMpAyqDMgMqwyqDKsMqQypDKsMrQypDK0MrAysDK0MsAysDLAMrgykDKwMrgykDK4MoQyhDK4MrwyhDK8MxwyuDLAMsQyuDLEMrwyrDLIMsAyrDLAMrQyyDLQMsQyyDLEMsAyzDLUMtAyzDLQMsgzIDLMMsgzIDLIMqwy1DLcMtgy1DLYMtAy0DLYMuQy0DLkMsQy3DMMMuAy3DLgMtgy2DLgMugy2DLoMuQyvDLEMuQyvDLkMuwy7DLkMugy7DLoMvQzHDK8MuwzHDLsMvAy8DLsMvQy8DL0Mvgy+DL0Mvwy+DL8MwAy9DLoMuAy9DLgMvwzADL8MwQzADMEMwgy/DLgMwwy/DMMMwQzGDMAMwgzGDMIMxAySDMYMxAySDMQMxQyTDL4MwAyTDMAMxgyWDLwMvgyWDL4MkwyiDMcMvAyiDLwMlgzKDMsM/wzKDP8MzAzLDMoMzQzLDM0M0AzNDMoMzAzNDMwMzwzODM0MzwzODM8M2wzQDM0MzgzQDM4M0gzRDNAM0gzRDNIM0wz+DMsM0Az+DNAM0QzTDNIM1AzTDNQM1QzSDM4M1wzSDNcM1AzVDNQM1gzVDNYMAg3UDNcM2AzUDNgM1gzXDM4M2wzXDNsM2QzYDNcM2QzYDNkM3AzZDNsMAA3ZDAAN2gzcDNkM2gzcDNoM3QzeDNwM3QzeDN0M4AzWDNgM3AzWDNwM3gzfDN4M4AzfDOAM4QwCDdYM3gwCDd4M3wzhDOAM4gzhDOIM4wzgDN0M5QzgDOUM4gzjDOIM5AzjDOQMAQ3iDOUM5gziDOYM5AzlDOcM6QzlDOkM5gzdDNoM5wzdDOcM5QzaDAAN6AzaDOgM5wznDOgM6gznDOoM6QzkDOYM6QzkDOkM6wzrDOkM6gzrDOoM7QzsDOsM7QzsDO0M7gwBDeQM6wwBDesM7AzuDO0M7wzuDO8M8AztDOoM8gztDPIM7wzwDO8M8QzwDPEM/AzvDPIM8wzvDPMM8QzoDPQM8gzoDPIM6gz0DPYM8wz0DPMM8gwADfUM9AwADfQM6Az1DPcM9gz1DPYM9Az3DPkM+Az3DPgM9gz2DPgM8Qz2DPEM8wz5DPsM+gz5DPoM+Az4DPoM/Az4DPwM8Qz/DP0M+wz/DPsM+QzLDP4M/QzLDP0M/wzMDP8M+QzMDPkM9wzPDMwM9wzPDPcM9QzbDM8M9QzbDPUMAA0EDQkNfg0EDX4NBg0HDQ8NCw0HDQsNBQ0KDQ4NCA0KDQgNAw0MDRsNDg0MDQ4NCg0PDRENDQ0PDQ0NCw0QDRINEQ0QDRENDw1fDRANDw1fDQ8NBw0SDRQNEw0SDRMNEQ0RDRMNFw0RDRcNDQ0UDXENFQ0UDRUNEw0TDRUNGA0TDRgNFw0WDRkNGw0WDRsNDA0YDRwNGQ0YDRkNFg0ZDRoNfw0ZDX8NGw0cDR0NGg0cDRoNGQ0eDSANHQ0eDR0NHA0VDR4NHA0VDRwNGA0fDSENIA0fDSANHg1xDR8NHg1xDR4NFQ0hDSMNIg0hDSINIA0gDSINJQ0gDSUNHQ0jDX0NJA0jDSQNIg0iDSQNJg0iDSYNJQ0lDSYNKQ0lDSkNJw0dDSUNJw0dDScNGg0aDScNKA0aDSgNfw0nDSkNKw0nDSsNKA0kDS0NKg0kDSoNJg0tDS8NLA0tDSwNKg0uDTANLw0uDS8NLQ19DS4NLQ19DS0NJA0wDTINMQ0wDTENLw0vDTENNg0vDTYNLA0yDUYNNA0yDTQNMQ0xDTQNOQ0xDTkNNg0oDSsNNQ0oDTUNOg06DTUNOA06DTgNPQ1/DSgNOg1/DToNOw07DToNPQ07DT0NPw0+DTwNQA0+DUANQQ08DTcNMw08DTMNQA1BDUANQg1BDUINQw1ADTMNRQ1ADUUNQg1DDUINRA1DDUQNVw1CDUUNRw1CDUcNRA1LDUgNRg1LDUYNMg1EDUcNSg1EDUoNVA1NDUkNSA1NDUgNSw1MDUsNMg1MDTINMA1PDU0NSw1PDUsNTA1UDUoNTg1UDU4NUQ1RDU4NUA1RDVANUw1SDVENUw1SDVMNaA1VDVQNUQ1VDVENUg1XDUQNVA1XDVQNVQ1YDVcNVQ1YDVUNVg1WDVUNUg1WDVINZg1ZDUMNVw1ZDVcNWA1eDVkNWA1eDVgNWg1aDVgNVg1aDVYNXA1dDWMNYA1dDWANWw1bDWANEA1bDRANXw1gDWENEg1gDRINEA1jDWQNYQ1jDWENYA1iDWYNZw1iDWcNZQ1cDVYNZg1cDWYNYg1mDVINaA1mDWgNZw1nDWgNag1nDWoNaQ1lDWcNaQ1lDWkNbA1pDWoNdQ1pDXUNaw1sDWkNaw1sDWsNbg1tDW4NcA1tDXANbw1kDW0Nbw1kDW8NYQ1hDW8NFA1hDRQNEg1vDXANcQ1vDXENFA1wDXINHw1wDR8NcQ1uDWsNcg1uDXINcA1yDXMNIQ1yDSENHw1rDXUNcw1rDXMNcg1zDXQNIw1zDSMNIQ11DXYNdA11DXQNcw1qDXgNdg1qDXYNdQ14DXoNdw14DXcNdg12DXcNfA12DXwNdA1oDVMNeA1oDXgNag1TDVANeg1TDXoNeA15DU8NTA15DUwNew13DXkNew13DXsNfA17DUwNMA17DTANLg18DXsNLg18DS4NfQ10DXwNfQ10DX0NIw0GDX4NWQ0GDVkNXg1+DUENQw1+DUMNWQ0JDT4NQQ0JDUENfg0ODTsNPw0ODT8NCA0bDX8NOw0bDTsNDg1oAGsAjA1oAIwNjQ0KAowNiw0KAosNBwJvAHQAiQ1vAIkNig0EAokNiA0EAogNAgIAAocNiA0AAogNdwD9AYYNhw39AYcNAAL8AYYNhQ38AYUN+QH5AYUNhA35AYQN9AHzAYMNgg3zAYIN8AHwAYINgQ3wAYEN7gHpAYANgQ3pAYEN6gFoAI0NgA1oAIAN6QEOBpsNmg0OBpoNDAaQBowGmA2QBpgNmg0KBpkNlw0KBpcNBgaKBogGlg2KBpYNlw2GBgQGlg2GBpYNlQ2DBoYGlQ2DBpUNlA2CBn8Gkw2CBpMNlA1/BnkG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AAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/9tJfOgAAAAAAAAAA+v9/P3EQMzsAAAAAAAAAAMP/fz+MFJc7AAAAAAAAAABO/38/jg+zOwAAAAAAAAAABv9/P4wUlzsAAAAAAAAAAE7/fz9xEDM7AAAAAAAAAADD/38/9tJfOgAAAAAAAAAA+v9/P0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/R0SQMwAAAAAAAAAAAACAP0dEkDMAAAAAAAAAAAAAgD9HRJAzAAAAAAAAAAAAAIA/9tJfOgAAAAAAAAAA+v9/P3EQMzsAAAAAAAAAAMP/fz+MFJc7AAAAAAAAAABO/38/jg+zOwAAAAAAAAAA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AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP/3/fz/9/38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP/3/fz/9/38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/Bd+HPX5nsjxBAli5Bd+HPX5nsjxNAli5Bd+HPX5nsjxNAli5Bd+HPX5nsjxNAli5Bd+HPX5nsjxNAli5Bd+HPX5nsjxNAli5Bd+HPX5nsjxNAli5Bd+HPX5nsjxNAli5Bd+HPX5nsjxNAli5Bd+HPX5nsjxNAli5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AQCAPx0AgD8TAIA/AACAPxwAgD8TAIA/AQCAPxwAgD8TAIA/AACAPx0AgD8TAIA/AQCAPx0AgD8UAIA/AQCAPxwAgD8UAIA/AQCAPx0AgD8TAIA/AACAPxwAgD8UAIA/AQCAPxwAgD8UAIA/AgCAPxwAgD8UAIA/AQCAPxwAgD8UAIA/AgCAPxwAgD8TAIA/AgCAPxwAgD8TAIA/AgCAPxwAgD8UAIA/AgCAPxwAgD8TAIA/AgCAPxsAgD8UAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/BgCAPxsAgD8UAIA/CQCAPxoAgD8TAIA/BQCAPxkAgD8TAIA/BACAPxkAgD8UAIA/BQCAPxcAgD8UAIA/BgCAPxgAgD8TAIA/BgCAPxgAgD8UAIA/CACAPxoAgD8UAIA/AwCAPxsAgD8TAIA/AgCAPxwAgD8UAIA/AQCAPxwAgD8TAIA/AgCAPxwAgD8TAIA/AwCAPxwAgD8TAIA/BQCAPxwAgD8UAIA/AQCAPxwAgD8TAIA/AACAPxsAgD8TAIA/AQCAPxoAgD8TAIA/AQCAPxsAgD8UAIA/AgCAPxwAgD8TAIA/BwCAPx0AgD8UAIA/BACAPxwAgD8UAIA/AgCAPxwAgD8UAIA/AwCAPxwAgD8UAIA/BQCAPxoAgD8UAIA/BgCAPxkAgD8TAIA/AgCAPxwAgD8UAIA/AwCAPxsAgD8TAIA/AwCAPxwAgD8TAIA/AgCAPxsAgD8UAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/BQCAPxwAgD8UAIA//v9/PxsAgD8TAIA/AwCAPxwAgD8UAIA/AwCAPxwAgD8UAIA///9/PxkAgD8UAIA/+/9/PxgAgD8UAIA//f9/PxkAgD8UAIA//v9/PxkAgD8UAIA//f9/PxoAgD8UAIA//P9/PxcAgD8UAIA///9/PxoAgD8UAIA/AwCAPxwAgD8TAIA/AgCAPxwAgD8UAIA//v9/PxsAgD8TAIA/AgCAPxwAgD8UAIA/AgCAPxwAgD8UAIA/AwCAPxwAgD8TAIA/AgCAPxsAgD8UAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/AwCAPxwAgD8UAIA/AwCAPxwAgD8UAIA/AQCAPxwAgD8UAIA/AgCAPx0AgD8UAIA/AgCAPxwAgD8UAIA/AgCAPx0AgD8UAIA/AQCAPxwAgD8UAIA/AQCAPxwAgD8TAIA/AQCAPx0AgD8TAIA/AACAPxwAgD8TAIA/AQCAPxwAgD8TAIA/AACAPx0AgD8TAIA/AQCAPx0AgD8UAIA/AQCAPxwAgD8UAIA/AQCAPx0AgD8TAIA/AACAPxwAgD8UAIA/AQCAPxwAgD8UAIA/AgCAPxwAgD8UAIA/AQCAPxwAgD8UAIA/AgCAPxwAgD8TAIA/AgCAPxwAgD8TAIA/AgCAPxwAgD8UAIA/AgCAPxwAgD8TAIA/AgCAPxsAgD8UAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/BgCAPxsAgD8TAIA/CQCAPxoAgD8TAIA/BQCAPxgAgD8TAIA/BgCAPxgAgD8UAIA/BQCAPxcAgD8UAIA/BgCAPxgAgD8TAIA/BgCAPxgAgD8UAIA/CQCAPxoAgD8UAIA/AwCAPxsAgD8TAIA/AgCAPxwAgD8UAIA/AQCAPxwAgD8TAIA/AgCAPx0AgD8TAIA/AwCAPxwAgD8TAIA/BQCAPxwAgD8UAIA/AACAPxwAgD8TAIA/AACAPxwAgD8TAIA/AQCAPxoAgD8UAIA/AQCAPxoAgD8UAIA/AgCAPxwAgD8TAIA/CACAPx0AgD8UAIA/BACAPxwAgD8UAIA/AgCAPxwAgD8UAIA/AwCAPxwAgD8UAIA/BQCAPxoAgD8UAIA/BgCAPxkAgD8TAIA/AgCAPxwAgD8UAIA/AwCAPxsAgD8TAIA/AwCAPxwAgD8TAIA/AgCAPxsAgD8UAIA/AwCAPxsAgD8TAIA/AwCAPxsAgD8TAIA/BQCAPxwAgD8UAIA//v9/PxsAgD8TAIA/AwCAPxwAgD8UAIA/AwCAPxwAgD8VAIA//v9/PxkAgD8UAIA/+v9/PxcAgD8UAIA//f9/PxkAgD8UAIA//v9/PxkAgD8VAIA//f9/PxkAgD8TAIA//P9/PxcAgD8TAIA/AACAPxkAgD8UAIA/AwCAPxwAgD8TAIA/AwCAPxwAgD8UAIA/AQCAPxwAgD8TAIA/AgCAPxwAgD8UAIA/AwCAPxwAgD8UAIA/AwCAPxwAgD8TAIA/AgCAPxsAgD8UAIA/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/v9/P/7/fz/6/38/+/9/P///fz/+/38//P9/P/3/fz/+/38//P9/P/3/fz/7/38/+/9/P/7/fz/9/38/+/9/P/7/fz/9/38/+/9/P/7/fz/9/38/+/9/P/v/fz/x/38/BgCAP/z/fz/x/38/BgCAP/3/fz/y/38/CwCAP/r/fz/u/38/CwCAP/z/fz/n/38/CgCAP/z/fz/o/38/DACAP/r/fz/w/38/CQCAP/v/fz/u/38/BACAP/r/fz/x/38/BACAP/r/fz/4/38/BACAP/r/fz/9/38/AwCAP/3/fz/+/38/AgCAP///fz8AAIA//v9/P///fz/6/38/AgCAP///fz///38/CACAP/7/fz/2/38/CACAP/7/fz/0/38/BwCAP/7/fz/5/38/CACAP/7/fz8AAIA/BwCAP/z/fz/4/38///9/P/3/fz/5/38/9f9/P/7/fz8AAIA/AACAP/j/fz/3/38/AwCAP/z/fz/z/38/BwCAP/7/fz/z/38/BgCAP///fz///38///9/P/z/fz/9/38//P9/PwEAgD8BAIA///9/P///fz/+/38//P9/P/3/fz/+/38//P9/P/3/fz/7/38/+/9/P/r/fz8BAIA/AwCAP/3/fz8AAIA/BACAP/n/fz/7/38/BACAP/3/fz/3/38/CACAP/3/fz/x/38/CgCAP/3/fz/o/38/CgCAP/7/fz/m/38/CgCAP/v/fz/l/38/DgCAP///fz/n/38/DACAP/v/fz/n/38/CQCAP/7/fz/z/38/CgCAP/z/fz/1/38/BgCAP/3/fz/7/38/AwCAP/z/fz8AAIA/AwCAP/3/fz8AAIA/AgCAP/z/fz/9/38/+/9/P///fz///38///9/P///fz/+/38//P9/P/3/fz/+/38//P9/P/3/fz/7/38/+/9/P/7/fz/+/38//v9/P/3/fz/7/38//v9/P/3/fz///38///9/P/7/fz///38/AACAP/3/fz/+/38/AACAP/z/fz/+/38/AACAP/r/fz/7/38/AgCAP/v/fz/7/38/AgCAP/f/fz/7/38/AwCAP/7/fz/9/38/AwCAP/3/fz/7/38/AwCAP/7/fz8AAIA/AwCAP/3/fz/7/38/AwCAP///fz/9/38/AwCAP/3/fz/9/38/AwCAP/v/fz///38/AwCAP/n/fz8BAIA/AgCAP/3/fz8AAIA/AACAP/v/fz8AAIA/AgCAP/z/fz///38/AACAP/z/fz///38///9/P/3/fz///38//v9/P/7/fz/6/38/+/9/P///fz/+/38//P9/P/3/fz/+/38//P9/P/3/fz/7/38/+/9/P/7/fz/9/38/+/9/P/7/fz/9/38/+/9/P/7/fz/9/38/+/9/P/v/fz/x/38/BgCAP/j/fz/v/38/AwCAP/3/fz/0/38/CgCAP/n/fz/v/38/CQCAP/z/fz/n/38/CgCAP/v/fz/n/38/CQCAP/n/fz/v/38/CgCAP/v/fz/w/38/AwCAP/r/fz/x/38/BACAP/r/fz/4/38/BACAP/r/fz/7/38/AwCAP/3/fz///38/AACAP///fz8AAIA//v9/P///fz/7/38/AACAP///fz/9/38/CQCAP///fz/2/38/CQCAP/7/fz/0/38/BwCAP/z/fz/4/38/BwCAP/7/fz8AAIA/BwCAP/z/fz/5/38//f9/P/3/fz/5/38/9f9/P/z/fz8AAIA/AACAP/n/fz/5/38/AwCAP///fz/1/38/CACAP///fz/z/38/BgCAP///fz///38///9/P/z/fz/9/38//P9/PwEAgD8BAIA///9/P///fz/+/38//P9/P/3/fz/+/38//P9/P/3/fz/7/38/+/9/P/r/fz8BAIA/AwCAP/3/fz8AAIA/BACAP/v/fz/5/38/BACAP/3/fz/3/38/CQCAP///fz/z/38/CQCAP/7/fz/m/38/CgCAP/z/fz/j/38/CQCAP/v/fz/l/38/DgCAP/z/fz/j/38/CQCAP/z/fz/q/38/CQCAP/v/fz/1/38/CQCAP/z/fz/2/38/BgCAP///fz/7/38/BACAP/z/fz8BAIA/AwCAP/3/fz8AAIA/AgCAP/z/fz/9/38/+v9/P///fz///38///9/P///fz/+/38//P9/P/3/fz/+/38//P9/P/7/fz8BAIA/AACAP/z/fz8AAIA/AwCAPwAAgD8AAIA/CACAP/z/fz/2/38/AwCAP/n/fz/o/38/CQCAP///fz/b/38/EACAP///fz/W/38/CwCAP///fz/f/38/CQCAP///fz/h/38/CACAP/7/fz/j/38/BwCAP/7/fz/p/38/CQCAP/7/fz/1/38/CACAP/z/fz/9/38/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+P9/P+v/fz8MAIA/9v9/P+//fz8HAIA/9/9/P/T/fz8EAIA/9v9/P/v/fz8GAIA/9v9/P/z/fz8EAIA/+/9/P/7/fz8CAIA//f9/P/7/fz/9/38/+v9/P/3/fz8BAIA/+v9/P/j/fz8IAIA/+P9/P/H/fz8EAIA/9v9/P/b/fz8GAIA/9f9/P/T/fz8EAIA/+P9/P/z/fz8GAIA/+/9/P/7/fz/8/38//P9/P/r/fz/3/38/+/9/P/3/fz8BAIA/9/9/P/7/fz8HAIA/9/9/P/X/fz8GAIA/+P9/P/T/fz8GAIA/+P9/P/7/fz/+/38//P9/P///fz/9/38//f9/PwAAgD/+/38//f9/P/3/fz/8/38/+f9/P///fz8AAIA//P9/PwAAgD/9/38/+f9/P/3/fz8CAIA/+P9/P/3/fz8FAIA/9v9/P/L/fz8BAIA/+P9/P/b/fz8GAIA/9v9/P+v/fz8EAIA/9v9/P+H/fz8KAIA/8P9/P+X/fz8IAIA/+P9/P97/fz8LAIA/9f9/P+P/fz8JAIA/+f9/P+P/fz8JAIA/+v9/P+//fz8EAIA//P9/P/b/fz8FAIA/+P9/P/j/fz8CAIA/+/9/P/r/fz8GAIA//f9/PwEAgD8FAIA//v9/P///fz/+/38/+/9/P/3/fz/6/38//f9/P/3/fz/8/38/+f9/P///fz8AAIA//P9/PwAAgD/9/38//f9/P///fz/+/38/9/9/PwAAgD/9/38//f9/P///fz/+/38/+/9/PwAAgD8DAIA/+P9/P///fz8CAIA//f9/P///fz8EAIA/+v9/PwEAgD8EAIA/9v9/P/v/fz8CAIA/+/9/P/v/fz8EAIA/+/9/P/7/fz8EAIA/+/9/P/z/fz8EAIA/+/9/P/v/fz8EAIA/+v9/P/7/fz8FAIA/+f9/P/3/fz8FAIA/9f9/P/z/fz8EAIA/+v9/P/z/fz8DAIA//P9/PwAAgD8GAIA/+P9/P/z/fz8DAIA/+/9/P/7/fz8EAIA/+v9/P/3/fz8BAIA//v9/P/3/fz8AAIA//P9/P/7/fz/+/38//f9/P///fz/+/38//f9/P/3/fz/8/38/+f9/P///fz8AAIA//P9/PwAAgD/9/38/+/9/P///fz/+/38/+/9/P///fz/+/38/+/9/P///fz/+/38/+P9/P/T/fz8HAIA/+P9/P+7/fz8FAIA/+/9/P/D/fz8JAIA/8/9/P+3/fz8FAIA/8/9/P+f/fz8MAIA/9v9/P+D/fz8JAIA/9v9/P+j/fz8MAIA/9/9/P+7/fz8GAIA/9/9/P/T/fz8EAIA/9/9/P/3/fz8FAIA/9/9/P/7/fz8FAIA/+v9/P/7/fz8BAIA//f9/P/7/fz/9/38/+v9/P/3/fz8CAIA/+v9/P/b/fz8HAIA/9/9/P/H/fz8EAIA/9v9/P/b/fz8GAIA/9v9/P/T/fz8DAIA/+P9/P/z/fz8GAIA//P9/P///fz/6/38//P9/P/r/fz/3/38//P9/P///fz8AAIA/9/9/P/z/fz8GAIA/9v9/P/T/fz8FAIA/+P9/P/T/fz8GAIA/+P9/P/7/fz/+/38//P9/P///fz/9/38//f9/PwAAgD/+/38//f9/P/3/fz/8/38/+f9/P///fz8AAIA//P9/PwAAgD/9/38/+f9/P/3/fz8CAIA/+P9/P/3/fz8FAIA/9/9/P/T/fz8BAIA/+P9/P/L/fz8BAIA/9P9/P+z/fz8EAIA/+P9/P+X/fz8MAIA/8/9/P+T/fz8JAIA/+P9/P97/fz8LAIA/9v9/P+j/fz8HAIA/+P9/P+X/fz8JAIA/+P9/P+3/fz8CAIA//P9/P/j/fz8FAIA/+v9/P/v/fz8EAIA/+/9/P/7/fz8FAIA//f9/PwAAgD8FAIA//v9/P///fz/+/38/+/9/P/3/fz/6/38//f9/P/3/fz/8/38/+f9/P///fz8AAIA/+v9/P/z/fz8DAIA/+/9/P/r/fz8EAIA/+f9/P///fz8GAIA/9f9/P/3/fz8CAIA/8f9/P+z/fz8JAIA/9v9/P9r/fz8VAIA/9P9/P9n/fz8YAIA/+P9/P9v/fz8WAIA/8/9/P9//fz8TAIA/9f9/P+P/fz8QAIA/+v9/P+z/fz8MAIA/9/9/P/j/fz8IAIA/+v9/PwEAgD8HAIA/+P9/P///fz8HAIA/9v9/P/b/fz8DAIA/+/9/P/n/fz8FAIA/+P9/P/r/fz8DAIA/+/9/P///fz/+/38/+/9/P/3/fz/9/38/+/9/P/z/fz/9/38/+f9/P9D/fz8WAIA/9P9/P9z/fz8QAIA/8/9/P9v/fz8QAIA/9f9/P9z/fz8QAIA/9f9/P+3/fz8KAIA//f9/P/P/fz8KAIA//v9/P/v/fz8HAIA/+v9/P///fz8DAIA/9v9/P/7/fz8DAIA/+P9/P/3/fz8DAIA/+P9/P/3/fz8CAIA/9/9/P/7/fz8DAIA/+P9/P/3/fz8DAIA/9/9/P/z/fz8DAIA/9/9/P/3/fz8EAIA/9/9/P/3/fz8DAIA/9/9/P/3/fz8DAIA//P9/P/3/fz8FAIA/+/9/P/n/fz8IAIA//f9/P/X/fz8IAIA/+f9/P+7/fz8JAIA/9/9/P9n/fz8NAIA/9f9/P9v/fz8QAIA/+P9/P9r/fz8TAIA/+v9/P9X/fz8VAIA//P9/PwAAgD/6/38/+/9/PwAAgD8AAIA//v9/PwAAgD/6/38//f9/P///fz/6/38//f9/P/3/fz/8/38/+f9/P///fz8AAIA/29bcsfoggj1ketWxd6Desfoggj0E472xd6Desfoggj0E472xd6Desfoggj0E472xd6Desfoggj0E472xd6Desfoggj0E472xd6Desfoggj0E472xd6Desfoggj0E472xd6Desfoggj0E472xd6Desfoggj0E472xd6Desfoggj0E472xrFhLMPoggj1C+lEvAsGqsPkggj0OGuKxlnynMfkggj1M8AWxz+zTsfkggj2vCWGxpKlvMvsggj3WDXWxtI83Mvkggj01q3SxxxDdsPoggj2ICF0wxFRJsvgggj1X5dGxGUYqsvcggj1XyYSx8zDnMPgggj09UHWxM+ZMsPoggj2UbuOwnzWuMfoggj3gd92vRKPfsPsggj0g564wHh6CMPoggj0CVKux84g1sfkggj0t5q2x0iqzsfkggj0yeqqx4S6TMfkggj04z1uxaPRXMvkggj1QisGx86twMfgggj3NKwqy75CBMvkggj3jUsexB4DUsfgggj1OWJqxa2Y1Mfgggj2iVZmxC1musfkggj2WuM+xqu5tsvkggj1qIPyxnptSMPkggj2cMeuxpidlL/kggj3/fcCxdIjYsfoggj24Zryx29bcsfoggj1ketWxd6Desfoggj0E472xgge7Mfoggj03BKcxz1jTsPoggj2ECpayJh1Esvkggj1yxhkxSX9rsfkggj3BtQKyiGrHsfoggj3X6a6yvsdRsfoggj1HUBSyWyqRsfkggj2IEcCxbnltMvkggj3P5hAxgr6Tsfkggj0oGaMtZGh+Mvoggj3kj4uxCEoEsvgggj217NIwwMPMsPsggj0PkaGx+cNesvsggj0EvNqxtAQUsvkggj1eZruxmAi3sfkggj2ctUSxw+2qsfoggj1mCByxprIhsfkggj3GcRGxehYvr/gggj1ZN4axQ4SHMvkggj2FfM8v35MJMvkggj1t3jIwAtYesfsggj2SLbyw3CSlsfkggj0bbagxv4brL/gggj3rnR2w3GbPMfkggj1p22WxHtQCMvgggj0LEqixuue7MPkggj0k/dOxeFSAsvoggj0Ohsaxglsesvgggj22aTexqu5tsvkggj1qIPyxnptSMPkggj2cMeuxpidlL/kggj3/fcCxwvfQMfgggj1WBsOwrNAVsvoggj0Bq8Gxy1ILsvoggj1xEeSxEGwIMvkggj12qRmyV7lCsfsggj2gAnGxrckdsPkggj3fkfuxMJs+sfkggj1JENqxYjOAsfkggj0XYSiyO09PMPoggj2NesKvbQ2Csvgggj3RZZaxb/GZsfgggj1p+YOx3jENsfkggj2oEmmxdySisfkggj2GqyKyJMUWMvgggj2P3jWxzaGfMfkggj2LcJKx/ZVCsvoggj0ii0qxYEJQsvgggj2UzLKxqu5tsvkggj1qIPyxnptSMPkggj2cMeuxpidlL/kggj3/fcCxbLUzMPoggj3euaiwWDOjsPkggj2eTvqxlnynMfkggj1M8AWxz+zTsfkggj2vCWGxpKlvMvsggj3WDXWxtI83Mvkggj01q3SxxxDdsPoggj2ICF0wxFRJsvgggj1X5dGxGUYqsvcggj1XyYSx8zDnMPgggj09UHWxM+ZMsPoggj2UbuOwnzWuMfoggj3gd92vRKPfsPsggj0g564wHh6CMPoggj0CVKux84g1sfkggj0t5q2x0iqzsfkggj0yeqqx4S6TMfkggj04z1uxaPRXMvkggj1QisGx86twMfgggj3NKwqy75CBMvkggj3jUsexB4DUsfgggj1OWJqxa2Y1Mfgggj2iVZmxC1musfkggj2WuM+xqu5tsvkggj1qIPyxnptSMPkggj2cMeuxpidlL/kggj3/fcCxdIjYsfoggj24Zryx29bcsfoggj1ketWxd6Desfoggj0E472xgge7Mfoggj03BKcxFsQYMfoggj2+YqOyk36psfkggj173IGxfAYFsvoggj29VyayiGrHsfoggj3X6a6y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AgCAPwcAgD8IAIA/BgCAPwQAgD8GAIA///9/PwkAgD8EAIA/8v9/PxAAgD8DAIA/6f9/PxcAgD8BAIA/9f9/PxMAgD8FAIA/DQCAP/z/fz8FAIA/DACAP/z/fz8FAIA/DQCAP/3/fz8FAIA/DACAP/3/fz8FAIA/DACAP/v/fz8EAIA/DACAP/z/fz8FAIA/CQCAPwEAgD8IAIA/AgCAPwgAgD8IAIA/+v9/PwoAgD8JAIA/9v9/PwsAgD8MAIA/9v9/PwkAgD8NAIA/+P9/PwYAgD8PAIA/9v9/PwQAgD8RAIA/+f9/PwAAgD8RAIA/9v9/PwQAgD8QAIA/8/9/PwYAgD8NAIA/8/9/PwoAgD8JAIA/9v9/PwwAgD8IAIA/+v9/PwsAgD8FAIA//f9/PwgAgD8GAIA/CACAPwEAgD8GAIA/DACAP/3/fz8FAIA/DQCAP/3/fz8FAIA/DACAP/3/fz8FAIA/DACAP/v/fz8EAIA/DACAP/z/fz8EAIA/CwCAP/v/fz8FAIA/CgCAP/7/fz8FAIA/CACAP///fz8FAIA/BwCAPwIAgD8GAIA/BgCAPwMAgD8GAIA/BACAPwUAgD8GAIA/AgCAPwcAgD8GAIA/AQCAPwcAgD8FAIA//f9/PwcAgD8HAIA///9/PwkAgD8HAIA///9/PwoAgD8HAIA//f9/PwoAgD8HAIA///9/PwkAgD8HAIA///9/PwkAgD8HAIA/AACAPwkAgD8IAIA/AQCAPwgAgD8GAIA/AgCAPwYAgD8GAIA/AgCAPwUAgD8FAIA/BACAPwMAgD8EAIA/BwCAPwEAgD8GAIA/CgCAPwIAgD8FAIA/CgCAP///fz8FAIA/DACAP/7/fz8FAIA/DACAP/3/fz8FAIA/DACAP/v/fz8EAIA/DACAP/z/fz8FAIA/DACAP/z/fz8FAIA/DACAP/z/fz8FAIA/DACAP/z/fz8DAIA/9f9/PxMAgD/9/38/8/9/PxYAgD/3/38/9f9/Px0AgD/0/38//f9/PxsAgD/w/38/+f9/PxwAgD/v/38/8/9/Px8AgD/0/38/5/9/PyIAgD8AAIA/7v9/PxsAgD8DAIA/7/9/PxUAgD8GAIA/9v9/PxAAgD8GAIA/AACAPwwAgD8GAIA/CACAPwQAgD8FAIA/DQCAP/z/fz8HAIA/BgCAPwUAgD8HAIA/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vdwLs/CYQTKW7HE/pG+nPsfcC7MFmUEyluxxPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD8AAIA/AACAPwAAgD/+/38///9/PwAAgD/+/38/AQCAPwAAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD/+/38///9/PwAAgD/+/38/AQCAPwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/9/38//v9/PwAAgD8AAIA/AQCAPwAAgD8AAIA/AQCAPwAAgD/+/38///9/PwAAgD8AAIA/AQCAPwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/8/38//v9/PwAAgD8AAIA///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/+/38/AQCAPwAAgD/+/38/AQCAPwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/9/38/AACAPwAAgD8AAIA/AQCAPwAAgD8AAIA/AQCAPwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD8AAIA/AACAPwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/9/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD8AAIA/AQCAPwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AQCAPwAAgD/+/38/AQCAPwAAgD8AAIA///9/PwAAgD/+/38//v9/PwAAgD/+/38///9/PwAAgD/+/38//v9/PwAAgD/+/38/AACAPwAAgD/9/38///9/PwAAgD/+/38//v9/PwAAgD/+/38/AACAPwAAgD/7/38//v9/PwAAgD/+/38//v9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/9/38/AQCAPwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD/+/38///9/PwAAgD/+/38/AQCAPwAAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD/+/38///9/PwAAgD/+/38/AQCAPwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/9/38//v9/PwAAgD8AAIA/AQCAPwAAgD8AAIA/AQCAPwAAgD/+/38///9/PwAAgD8AAIA/AQCAPwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/8/38//v9/PwAAgD8AAIA///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/9/38///9/PwAAgD/+/38/AQCAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD8AAIA/AQCAPwAAgD/9/38///9/PwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD8AAIA/AACAPwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD8AAIA/AQCAPwAAgD/9/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/9/38///9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD8AAIA/AQCAPwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AQCAPwAAgD/+/38/AQCAPwAAgD8AAIA///9/PwAAgD/9/38///9/PwAAgD8AAIA/AQCAPwAAgD/+/38/AACAPwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD/+/38//v9/PwAAgD/9/38//f9/PwAAgD/+/38///9/PwAAgD/9/38///9/PwAAgD/+/38///9/PwAAgD/9/38///9/PwAAgD/+/38/AACAPwAAgD8AAIA///9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAgD/+/38/AQCAPwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD8AAIA/AgCAPwAAgD/9/38///9/PwAAgD/9/38///9/PwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/9/38///9/PwAAgD/9/38//v9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/+/38/AQCAPwAAgD/8/38//v9/PwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD8AAIA/AQCAPwAAgD/+/38///9/PwAAgD8AAIA/AACAPwAAgD/9/38///9/PwAAgD/9/38///9/PwAAgD/9/38//v9/PwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD8AAIA///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAgD/+/38///9/PwAAgD/+/38/AQCAPwAAgD8AAIA///9/PwAAgD8AAIA///9/PwAAgD/9/38//v9/PwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD/+/38/AACAPwAAgD/+/38///9/PwAAgD/9/38///9/PwAAgD/+/38///9/PwAAgD/9/38///9/PwAAgD/+/38//v9/PwAAgD/+/38///9/PwAAgD/+/38/AQCAPwAAgD/+/38///9/PwAAgD/+/38///9/PwAAgD8AAIA///9/PwAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAArQAAAAC9ooc9AAAAAAAAAADAooc9AAAAAAAAAAC+ooc9AAAAAAAAAADAooc9AACArgAAAAC9ooc9AACArgAAAAC+ooc9AACALQAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAArQAAAAC9ooc9AAAAAAAAAADAooc9AAAAAAAAAAC+ooc9AAAAAAAAAADAooc9AACArgAAAAC9ooc9AACArgAAAAC+ooc9AACALQAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAADAooc9AAAArgAAAAC+ooc9AACArgAAAAC8ooc9AADALwAAAAC9ooc9AAAAAAAAAAC9ooc9AACArwAAAAC9ooc9AAAAAAAAAAC9ooc9AAAAAAAAAAC/ooc9AAAAAAAAAAC9ooc9AAAAAAAAAAC9ooc9AAAAAAAAAAC/ooc9AACALwAAAAC9ooc9AAAAMAAAAAC8ooc9AACALwAAAAC+ooc9AACArgAAAADAooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAADAooc9AADArQAAAAC9ooc9AAAAAAAAAAC+ooc9AACALgAAAAC+ooc9AAAAAAAAAAC+ooc9AACALgAAAAC9ooc9AACArgAAAADAooc9AAAArgAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAADAooc9AACArQAAAAC9ooc9AAAAAAAAAAC+ooc9AACALgAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC9ooc9AACArgAAAADAooc9AAAArQAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAADAooc9AACArQAAAAC+ooc9AACArgAAAAC+ooc9AACArwAAAAC/ooc9AAAAsAAAAAC/ooc9AABAMAAAAAC/ooc9AAAAMAAAAAC/ooc9AAAAsAAAAAC/ooc9AAAAAAAAAAC9ooc9AAAAAAAAAAC/ooc9AAAAAAAAAAC/ooc9AAAAMAAAAAC9ooc9AAAAMAAAAAC+ooc9AADArwAAAAC+ooc9AAAAAAAAAAC+ooc9AAAALgAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAABe/ooc9AAAosgAAAAC/ooc9AADAsQAAAAC+ooc9AAAQMgAAAAC+ooc9AAAAsQAAAAC+ooc9AACAsQAAAAC9ooc9AACAMQAAAAC+ooc9AABAMQAAAAC+ooc9AAAgMgAAAAC+ooc9AAAosgAAAAC+ooc9AAAosgAAAAC+ooc9AAAosgAAAAC+ooc9AAAosgAAAAC+ooc9AAAosgAAgJe+ooc9AACAMQAAgBe+ooc9AAAgMQAAgBe+ooc9AABAMQAAABe/ooc9AAAosgAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC9ooc9AACArwAAAAC+ooc9AACALwAAAAC9ooc9AACALwAAAAC8ooc9AAAAAAAAAAC9ooc9AACALwAAAAC8ooc9AACArgAAAAC8ooc9AABALwAAAAC+ooc9AACALQAAAAC8ooc9AACALgAAAAC8ooc9AACALgAAAAC8ooc9AACALgAAAAC8ooc9AACALgAAAAC8ooc9AACALgAAAAC8ooc9AACALgAAAAC8ooc9AACALgAAAAC8ooc9AACALgAAAAC8ooc9AACALgAAAAC+ooc9AAAArgAAAAC+ooc9AABArwAAAAC8ooc9AACALgAAAAC9ooc9AACALwAAAAC8ooc9AAAAMAAAAAC9ooc9AACArwAAAAC8ooc9AABAMAAAAAC9ooc9AACArwAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAC+ooc9AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD+CvPQ9AAAAAAAAAABjKn4/1CTzPQAAAAAAAAAAgDB+P+Dx7j0AAAAAAAAAAGxAfj+oAek9AAAAAAAAAAB4Vn4/PzLiPQAAAAAAAAAADW9+Pz5i2z0AAAAAAAAAAOiGfj9ccNU9AAAAAAAAAAAfm34/qzvRPQAAAAAAAAAAFql+Pzyjzz0AAAAAAAAAAFCufj9ccNU9AAAAAAAAAAAfm34/QjLiPQAAAAAAAAAADW9+P97x7j0AAAAAAAAAAGxAfj+CvPQ9AAAAAAAAAABjKn4/1CTzPQAAAAAAAAAAgDB+P+Dx7j0AAAAAAAAAAGxAfj+oAek9AAAAAAAAAAB4Vn4/PzLiPQAAAAAAAAAADW9+Pz5i2z0AAAAAAAAAAOiGfj9ccNU9AAAAAAAAAAAfm34/qzvRPQAAAAAAAAAAFql+Pzyjzz0AAAAAAAAAAFCufj9ccNU9AAAAAAAAAAAfm34/QjLiPQAAAAAAAAAADW9+P97x7j0AAAAAAAAAAGxAfj+CvPQ9AAAAAAAAAABjKn4/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD9G7V49AAAAAAAAAADenn8//dPyPQAAAAAAAAAAtDF+P5ttKj4AAAAAAAAAAPJtfD9yd00+AAAAAAAAAAAwy3o/BCJlPgAAAAAAAAAAQIJ5Py7Rcz4AAAAAAAAAADGjeD8+uXs+AAAAAAAAAAABJXg/ivN+PgAAAAAAAAAAQfB3PyyRfz4AAAAAAAAAABzmdz+K834+AAAAAAAAAABB8Hc/Prl7PgAAAAAAAAAAASV4Py7Rcz4AAAAAAAAAADGjeD8JImU+AAAAAAAAAABAgnk/cndNPgAAAAAAAAAAMMt6P5ptKj4AAAAAAAAAAPJtfD/70/I9AAAAAAAAAAC0MX4/Ru1ePQAAAAAAAAAA3p5/PwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAP0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz9G7V49AAAAAAAAAADenn8/Ru1ePQAAAAAAAAAA3p5/P0btXj0AAAAAAAAAAN6efz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAP4K89D0AAAAAAAAAAGMqfj/VJPM9AAAAAAAAAACAMH4/4fHuPQAAAAAAAAAAbEB+P6YB6T0AAAAAAAAAAHhWfj8/MuI9AAAAAAAAAAANb34/PmLbPQAAAAAAAAAA6IZ+P11w1T0AAAAAAAAAAB+bfj+qO9E9AAAAAAAAAAAWqX4/PKPPPQAAAAAAAAAAUK5+P11w1T0AAAAAAAAAAB+bfj9CMuI9AAAAAAAAAAANb34/3vHuPQAAAAAAAAAAbEB+P4K89D0AAAAAAAAAAGMqfj/TJPM9AAAAAAAAAACAMH4/3/HuPQAAAAAAAAAAbEB+P6cB6T0AAAAAAAAAAHhWfj8/MuI9AAAAAAAAAAANb34/PWLbPQAAAAAAAAAA6IZ+P15w1T0AAAAAAAAAAB+bfj+rO9E9AAAAAAAAAAAWqX4/PKPPPQAAAAAAAAAAUK5+P11w1T0AAAAAAAAAAB+bfj9CMuI9AAAAAAAAAAANb34/3vHuPQAAAAAAAAAAbEB+P4K89D0AAAAAAAAAAGMqfj8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAP0btXj0AAAAAAAAAAN6efz8pqNs7AAAAAAAAAACH/n8/moinvQAAAAAAAAAAWyR/P4jmA74AAAAAAAAAAAzefT9F+Vc9AAAAAAAAAADWpH8/L9FzPgAAAAAAAAAAL6N4P74TfD4AAAAAAAAAAEIfeD9mIX8+AAAAAAAAAABN7Xc/LJF/PgAAAAAAAAAAHOZ3P4rzfj4AAAAAAAAAAEHwdz9AuXs+AAAAAAAAAAABJXg/L9FzPgAAAAAAAAAAL6N4PwgiZT4AAAAAAAAAAECCeT9nd00+AAAAAAAAAAAvy3o/lW0qPgAAAAAAAAAA8m18P+7T8j0AAAAAAAAAALUxfj9G7V49AAAAAAAAAADenn8/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/bMSFPSCIkyQpfzWjD3R/P7wcRD5LXpQkooY2o+ZCez/ddqc+1O+UJK45N6NV63E/Y0ndPgQvlCR4TDaj5dpmP0KE8j4No48keWuiI+F1YT9ChPI+DaOPJHlroiPhdWE/QoTyPg2jjyR5a6Ij4XVhP0KE8j4No48keWuiI+F1YT9ChPI+DaOPJHlroiPhdWE/NeftPvCkjySYbaIjm69iP6AR4T5urY8kNXeiI2vwZT+pbc0+nbGPJPB7oiOUfWo/Mme0PkX2iCROYO0jEpVvP5oolz4A2Ygkli3tI8iWdD9E4mo+lceIJGEP7SOiLHk/8/0cPmPTiCTWI+0jOPl8P2zEhT0giJMkKX81ow90fz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD/WwqE+AAAAAAAAAAA143I/WA6bPgAAAAAAAAAAcfpzP+MciT4AAAAAAAAAAE2mdj8QYV4+AAAAAAAAAADy43k/r9AhPgAAAAAAAAAAk8h8P0NUyT0AAAAAAAAAAJDCfj+Icj09AAAAAAAAAADfuX8/8e0ePAAAAAAAAAAA6/x/P/9mhbsAAAAAAAAAAHX/fz//ZoW7AAAAAAAAAAB1/38//2aFuwAAAAAAAAAAdf9/P/9mhbsAAAAAAAAAAHX/fz//ZoW7AAAAAAAAAAB1/38//2aFuwAAAAAAAAAAdf9/P/9mhbsAAAAAAAAAAHX/fz//ZoW7AAAAAAAAAAB1/38//2aFuwAAAAAAAAAAdf9/P+vtHjwAAAAAAAAAAOv8fz+Lcj09AAAAAAAAAADfuX8/QFTJPQAAAAAAAAAAkMJ+P7DQIT4AAAAAAAAAAJPIfD8OYV4+AAAAAAAAAADy43k/4xyJPgAAAAAAAAAATaZ2P1gOmz4AAAAAAAAAAHH6cz/WwqE+AAAAAAAAAAA143I/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP/7/fz/+/38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAP///fz///38/AACAP/7/fz/+/38/AACAP///fz///38/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAMCihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAACxAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAACxAAAAAMCihz0AAACxAAAAALyihz0AAAAAAAAAAMCihz0AAAAAAAAAAMCihz0AAAAAAAAAALyihz0AAAAAAAAAAMCihz0AAAAAAAAAAMCihz0AAAAAAAAAALyihz0AAAAAAAAAAMCihz0AAACxAAAAALyihz0AAACxAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAAMCihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAKAxAAAAAL6ihz0AAICwAAAAALuihz0AAIAwAAAAALyihz0AAACxAAAAAL+ihz0AAICwAAAAAL6ihz0AAICxAAAAAMKihz0AAECxAAAAAL2ihz0AAKAxAAAAAL+ihz0AAECxAAAAAL6ihz0AAIAwAAAAAL+ihz0AAEAxAAAAAMKihz0AABCyAAAAAL6ihz0AAKAxAAAAAL6ihz0AAICwAAAAALuihz0AAIAwAAAAALyihz0AAACxAAAAAL+ihz0AAICwAAAAAL6ihz0AAICxAAAAAMKihz0AAECxAAAAAL2ihz0AAKAxAAAAAL+ihz0AAECxAAAAAL6ihz0AAIAwAAAAAL+ihz0AAEAxAAAAAMKihz0AABCyAAAAAL6ihz0AAKAxAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAMCihz0AAAAAAAAAAMGihz0AAACxAAAAAL2ihz0AAICxAAAAAL6ihz0AAEAyAAAAAL6ihz0AAMAxAAAAAL2ihz0AAACyAAAAAL+ihz0AAICxAAAAAL+ihz0AAAAyAAAAAMCihz0AAIAxAAAAAL+ihz0AAAAyAAAAAL+ihz0AAICxAAAAAL2ihz0AAACyAAAAAL2ihz0AAIAxAAAAAL6ihz0AAEAyAAAAAMGihz0AAICxAAAAAMGihz0AAAAyAAAAAMCihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAMCihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAACxAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAACxAAAAAMCihz0AAACxAAAAALyihz0AAAAAAAAAAMCihz0AAAAAAAAAAMCihz0AAAAAAAAAALyihz0AAAAAAAAAAMCihz0AAAAAAAAAAMCihz0AAAAAAAAAALyihz0AAAAAAAAAAMCihz0AAACxAAAAALyihz0AAACxAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAALyihz0AAAAAAAAAAMCihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAKAxAAAAAMKihz0AAICwAAAAALuihz0AAECxAAAAALyihz0AAAAAAAAAAL+ihz0AAICwAAAAALyihz0AAIAwAAAAAMKihz0AAMCxAAAAAMGihz0AAMAxAAAAAL+ihz0AAECxAAAAAL6ihz0AAAAxAAAAAL+ihz0AAEAxAAAAAMKihz0AABCyAAAAAL6ihz0AAKAxAAAAAL6ihz0AAEAxAAAAALqihz0AAKCxAAAAALyihz0AAAAyAAAAAL+ihz0AAICwAAAAALyihz0AAICwAAAAAMKihz0AAICxAAAAAL2ihz0AAKAxAAAAAL+ihz0AAECxAAAAAL6ihz0AAAAxAAAAAL+ihz0AAEAxAAAAAMKihz0AABCyAAAAAL6ihz0AAKAxAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAL6ihz0AAAAAAAAAAMCihz0AAAAAAAAAAL+ihz0AAAAvAAAAAL+ihz0AAAAxAAAAAL6ihz0AAIAxAAAAAL+ihz0AAIAwAAAAAL6ihz0AAIAxAAAAAMCihz0AAIAxAAAAAL6ihz0AAAAAAAAAAMCihz0AAIAxAAAAAL2ihz0AAAAAAAAAAL2ihz0AAAAAAAAAAL6ihz0AAIAxAAAAAL2ihz0AAIAxAAAAAL6ihz0AAMAxAAAAAMCihz0AAMCx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AACAPwAAgD8AAIA///9/P///fz8AAIA//v9/P/7/fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA//v9/P/7/fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AQCAPwEAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA//v9/P/7/fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA//v9/P/7/fz8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AQCAPwEAgD8AAIA//v9/P/7/fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA//v9/P/7/fz8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA//v9/P/7/fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA//v9/P/7/fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA//f9/P/3/fz8AAIA//v9/P/7/fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA2grTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4grTPQAAAAAAAAAA4ArTPQAAwDEAAAAA4QrTPQAAgLEAAAAA4QrTPQAAALEAAAAA4ArTPQAAAAAAAAAA4grTPQAAAAAAAAAA4wrTPQAAwDEAAAAA3wrTPQAAADIAAAAA3QrTPQAAgDEAAAAA3grTPQAAgDEAAAAA3wrTPQAAALIAAAAA3grTPQAAgDEAAAAA3QrTPQAAgDEAAAAA3wrTPQAAADIAAAAA4wrTPQAAADEAAAAA3grTPQAAALEAAAAA4ArTPQAAAAAAAAAA5ArTPQAAADEAAAAA4QrTPQAAgLEAAAAA4ArTPQAAwDEAAAAA3grTPQAAADEAAAAA4ArTPQAAADEAAAAA2grTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA3grTPQAAgDEAAAAA4wrTPQAAgLIAAAAA3wrTPQAAAAAAAAAA4ArTPQAAgDIAAAAA3wrTPQAAADIAAAAA4ArTPQAAgDEAAAAA4QrTPQAAALIAAAAA3ArTPQAAgLEAAAAA3ArTPQAAgDEAAAAA3QrTPQAAgDEAAAAA3ArTPQAAAAAAAAAA5grTPQAAALIAAAAA3grTPQAAgDEAAAAA3QrTPQAAgLEAAAAA4wrTPQAAgDEAAAAA4ArTPQAAgDIAAAAA3wrTPQAAADIAAAAA4ArTPQAAgDEAAAAA4QrTPQAAALIAAAAA3ArTPQAAgLEAAAAA3ArTPQAAgDEAAAAA2grTPQAAgDEAAAAA2wrTPQAAADIAAAAA3grTPQAAgLEAAAAA3grTPQAAgDEAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA2grTPQAAAAAAAAAA4QrTPQAAALIAAAAA4ArTPQAAAAAAAAAA3QrTPQAAADIAAAAA4wrTPQAAADIAAAAA4grTPQAAgDIAAAAA4QrTPQAAgLIAAAAA4ArTPQAAAAAAAAAA4QrTPQAAwDIAAAAA3wrTPQAAALIAAAAA3wrTPQAAgLIAAAAA3grTPQAAADMAAAAA4ArTPQAAwLIAAAAA3wrTPQAAALIAAAAA4ArTPQAAAAAAAAAA4ArTPQAAgDEAAAAA2grTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA2grTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4grTPQAAAAAAAAAA4ArTPQAAwDEAAAAA4QrTPQAAgLEAAAAA4QrTPQAAALEAAAAA4ArTPQAAAAAAAAAA4grTPQAAAAAAAAAA4wrTPQAAwDEAAAAA3wrTPQAAADIAAAAA3QrTPQAAgDEAAAAA3grTPQAAgDEAAAAA3wrTPQAAALIAAAAA3grTPQAAgDEAAAAA3QrTPQAAgDEAAAAA3wrTPQAAADIAAAAA4wrTPQAAADEAAAAA3grTPQAAALEAAAAA4ArTPQAAAAAAAAAA5ArTPQAAADEAAAAA4QrTPQAAgLEAAAAA4ArTPQAAwDEAAAAA3grTPQAAADEAAAAA4ArTPQAAADEAAAAA2grTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA3grTPQAAgDEAAAAA4wrTPQAAAAAAAAAA3wrTPQAAADIAAAAA4QrTPQAAQDIAAAAA3wrTPQAAADIAAAAA3ArTPQAAAAAAAAAA4QrTPQAAgLEAAAAA5ArTPQAAAAAAAAAA3ArTPQAAgDEAAAAA3grTPQAAAAAAAAAA3ArTPQAAAAAAAAAA5grTPQAAALIAAAAA3grTPQAAgDEAAAAA3QrTPQAAgLEAAAAA4wrTPQAAALIAAAAA4grTPQAAQLIAAAAA3wrTPQAAADIAAAAA4wrTPQAAADIAAAAA4QrTPQAAALIAAAAA3ArTPQAAAAAAAAAA3ArTPQAAgDEAAAAA2grTPQAAAAAAAAAA2wrTPQAAADIAAAAA3grTPQAAgLEAAAAA3grTPQAAgDEAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA2grTPQAAAAAAAAAA4QrTPQAAADEAAAAA3grTPQAAAAAAAAAA3QrTPQAAADIAAAAA4grTPQAAALEAAAAA5ArTPQAAAAAAAAAA3QrTPQAAADMAAAAA3grTPQAAADIAAAAA4QrTPQAAwDIAAAAA3wrTPQAAQLIAAAAA4ArTPQAAgDIAAAAA5ArTPQAAAAAAAAAA3ArTPQAAAAAAAAAA5ArTPQAAgLIAAAAA4grTPQAAAAAAAAAA4wrTPQAAgLEAAAAA2grTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAIAX4grTPQAAgLIAACCY4ArTPQAAgDEAAICX4ArTPQAAQLIAAICX3wrTPQAAADIAADgY4ArTPQAAgDIAAKAX3wrTPQAAgDIAAACY4ArTPQAAQDIAAACW4ArTPQAAgDIAAAAY4ArTPQAAgDEAAECY4ArTPQAAQDIAAOCX3wrTPQAAADIAAMAX4ArTPQAAAAAAAACX4QrTPQAAEDIAAACY4ArTPQAAAAAAAACX3wrTPQAAgDEAAAAX3wrTPQAAALIAAIAX4grTPQAAgLIAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAALIAAAAA4ArTPQAAgDEAAAAA4grTPQAAwLEAAAAA3wrTPQAAQDIAAAAA3grTPQAAADIAAAAA4grTPQAAAAAAAAAA4wrTPQAAALEAAAAA3ArTPQAAADAAAAAA4QrTPQAAADEAAAAA4QrTPQAAADEAAAAA4QrTPQAAADEAAAAA4QrTPQAAADEAAAAA4QrTPQAAADEAAAAA4QrTPQAAADEAAAAA4QrTPQAAADEAAAAA4QrTPQAAADEAAAAA4QrTPQAAADEAAAAA4ArTPQAAwC8AAAAA4wrTPQAAALEAAAAA4ArTPQAAALIAAAAA4ArTPQAAALIAAAAA3wrTPQAAQDIAAAAA4QrTPQAAALEAAAAA4ArTPQAAILIAAAAA4ArTPQAAALIAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAA4ArTPQAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAsgAAAAAAAAAAAACAPwAAALIAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAACyAAAAAAAAAAAAAIA/AAAAsgAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwEAALIAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AACAMgAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwEAALIAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAsgAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAgDIAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AQCAsgAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAALIAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAACyAAAAAAAAAAAAAIA/AAAAsgAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAALIAAAAAAAAAAAAAgD8AAACyAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAsgAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAACyAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8BAIAyAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAICyAAAAAAAAAAAAAIA/AAAAsgAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAP1FDxL6ahvIXDpYKl+hxbD9RQ8S++y97mA+WCpjocWw/UUPEvhCWCpcQlgqX6nFsP1FDxL4QloqYD5aKl+hxbD9RQ8S+NJKkGBGWChfocWw/UUPEvjSSpBgRlgoX6HFsP1FDxL40kqQYEZYKF+hxbD9RQ8S+NJKkGBGWChfocWw/UUPEvjSSpBgRlgoX6HFsP1FDxL4QlgoX0ugbmOhxbD9RQ8S+m4ZyFw6WipfocWw/UUPEvnA/E5ka4c+X6HFsP1FDxL6v7IEYGeFPF+hxbD9RQ8S+RSoSGJQ7rRfocWw/UUPEvlaOvheUO60X6nFsP1FDxL6UOy2YG+HPF+pxbD9RQ8S+mobyFw6WCpfocWw/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/mEpdvgAAAAAAAAAAZvN5P5dKXb4AAAAAAAAAAGbzeT+YSl2+AAAAAAAAAABm83k/mEpdvgAAAAAAAAAAZvN5P5hKXb4AAAAAAAAAAGbzeT+YSl2+AAAAAAAAAABm83k/mEpdvgAAAAAAAAAAZfN5P5dKXb4AAAAAAAAAAGbzeT+YSl2+AAAAAAAAAABl83k/mEpdvgAAAAAAAAAAZfN5P5hKXb4AAAAAAAAAAGXzeT+YSl2+AAAAAAAAAABl83k/mEpdvgAAAAAAAAAAZfN5P5hKXb4AAAAAAAAAAGXzeT+YSl2+AAAAAAAAAABl83k/mEpdvgAAAAAAAAAAZfN5P5hKXb4AAAAAAAAAAGXzeT+YSl2+AAAAAAAAAABm83k/mEpdvgAAAAAAAAAAZfN5P5dKXb4AAAAAAAAAAGbzeT+YSl2+AAAAAAAAAABl83k/lUpdvgAAAAAAAAAAZfN5P5hKXb4AAAAAAAAAAGbzeT+VSl2+AAAAAAAAAABl83k/mEpdvgAAAAAAAAAAZvN5PwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38///9/PwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD/+/38//v9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD/+/38//v9/PwAAgD/+/38//v9/PwAAgD/+/38//v9/PwAAgD/+/38//v9/PwAAgD/+/38//v9/PwAAgD///38///9/PwAAgD///38///9/PwAAgD///38///9/PwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/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6FajPi3RaDPP59Syj59yP+hWoz4t0Wgzz+fUso+fcj/oVqM+LdFoM8/n1LKPn3I/6FajPi3RaDPP59Syj59yP4ZolD6mFWszWpPGsnsCdT+bd1U+CyxwM/xOnrItYHo/iFWsPV+bdDPHKUCykhd/PyuKhr3B13QzIbgfsW9yfz87Mlu+uJRvM3Jm4jHuEHo/TXarvrQDZzM3xm8y2DdxP79e077WPV8z6DOhMqkqaT/2i+G+ME1wM/qIsDJx0mU/AAAAAP7/fzMAAAAAAACAPwAAAAD+/38zAAAAAAAAgD8AAAAA/v9/MwAAAAAAAIA/AAAAAP7/fzMAAAAAAACAPwAAAAD+/38zAAAAAAAAgD8AAAAA/v9/MwAAAAAAAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP/7/fz/+/38/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP/7/fz/+/38/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP/7/fz/+/38/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz///38/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz///38/AACAP///fz///38/AACAP///fz///38/AACAP/7/fz///38/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8BAIA/AACAP///fz8BAIA/AACAP///fz8AAIA/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8BAIA/AACAP///fz8BAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8BAIA/AACAP///fz8BAIA/AACAP///fz8BAIA/AACAP///fz8BAIA/AACAP///fz8BAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP/3/fz///38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8BAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP/3/fz/+/38/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAPwAAgD8BAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP/7/fz/+/38/AACAP///fz8AAIA/AACAPwAAgD8BAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw9GEPPW3QkT0wDsiw9GEPPWnQkT08/iYy9GEPPXLQkT3i+Bsx9mEPPWzQkT0uDsiw9GEPPXDQkT0bB2Sx9WEPPW7QkT0zDsiw9mEPPWrQkT0UB2Sx9WEPPW3QkT10/I0x9mEPPW7QkT1z/I0x9GEPPXDQkT0ex98v9mEPPWzQkT10/M0x9WEPPW3QkT0vDsiw9GEPPXHQkT3HARmy9WEPPW3QkT0vDsiw9mEPPWzQkT10/M0x9GEPPXDQkT0ex98v9mEPPW7QkT0wx98v92EPPW3QkT3n+Bsx9mEPPWrQkT0UB2Sx9mEPPXLQkT0fB2Sx9GEPPXDQkT0bB2Sx9mEPPWzQkT0uDsiw9GEPPW7QkT10/M0x9GEPPWnQkT13/I0x9GEPPW3QkT0wDsiw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw9GEPPW3QkT0XB2Sx9mEPPXDQkT3HATmy9WEPPWzQkT2LA/Kx82EPPXDQkT1z/I0x9GEPPW/QkT0aB2Sx9WEPPXHQkT0eB2Sx9GEPPW/QkT2NA/Kx9mEPPWvQkT2KA/Kx9GEPPWrQkT3s+Bsx9GEPPW/QkT00Dsiw92EPPWfQkT14/I0x9WEPPXHQkT3HATmy9GEPPW3QkT0XB2Sx9GEPPWzQkT2LA/Kx92EPPW/QkT2OA/Kx9GEPPW7QkT1z/I0x9GEPPW/QkT0aB2Sx9WEPPXHQkT0eB2Sx9GEPPW/QkT2NA/Kx9mEPPWvQkT2KA/Kx9GEPPWrQkT3s+Bsx92EPPWfQkT14/M0x9GEPPWzQkT0XB2Sx9mEPPWvQkT0VB2Sx9GEPPW3QkT0XB2Sx82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw9GEPPW3QkT0wDsiw9WEPPW7QkT2MA/Kx82EPPXDQkT3HAXmy9WEPPWnQkT13/I0x9GEPPXHQkT1y/I0x82EPPXPQkT0Hx98v9GEPPW/QkT3kgJyy9GEPPXHQkT3HAXmy9GEPPXHQkT2OA/Kx9GEPPXLQkT2PA/Kx9GEPPW7QkT2MA/Kx8mEPPXDQkT0cf4My9mEPPW3QkT3jgLyy9GEPPWzQkT11/I0x82EPPXDQkT3HAXmy9WEPPW3QkT07x98v9GEPPW3QkT0wDsiw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw9GEPPW3QkT0wDsiw9GEPPWnQkT08/iYy9GEPPXLQkT3i+Bsx9mEPPWzQkT0uDsiw9GEPPXDQkT0bB2Sx9WEPPW7QkT0zDsiw9mEPPWrQkT0UB2Sx9WEPPW3QkT10/I0x9mEPPW7QkT1z/I0x9GEPPXDQkT0ex98v9mEPPWzQkT10/M0x9WEPPW3QkT0vDsiw9GEPPXHQkT3HARmy9WEPPW3QkT0vDsiw9mEPPWzQkT10/M0x9GEPPXDQkT0ex98v9mEPPW7QkT0wx98v92EPPW3QkT3n+Bsx9mEPPWrQkT0UB2Sx9mEPPXLQkT0fB2Sx9GEPPXDQkT0bB2Sx9mEPPWzQkT0uDsiw9GEPPW7QkT10/M0x9GEPPWnQkT13/I0x9GEPPW3QkT0wDsiw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw9GEPPW3QkT0XB2Sx92EPPW/QkT0cB2Sx9WEPPW3QkT0XB2Sx9GEPPXDQkT05/kYy9GEPPW/QkT0aB2Sx9WEPPW3QkT3GATmy9GEPPXDQkT0bB2Sx92EPPW/QkT2NA7Kx9GEPPWrQkT3s+Bsx9GEPPW/QkT2NA7Kx92EPPWfQkT14/I0x9WEPPXHQkT3HATmy9GEPPW3QkT0XB2Sx9GEPPWzQkT2LA/Kx92EPPW/QkT3HATmy9WEPPWrQkT1fx98v9GEPPW/QkT0aB2Sx9GEPPXHQkT0dB2Sx9GEPPXDQkT3HATmy9mEPPWvQkT2LA/Kx9GEPPWrQkT3s+Bsx92EPPWfQkT0jDsiw9GEPPWzQkT0XB2Sx9mEPPWvQkT0VB2Sx9GEPPW3QkT0XB2Sx82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw9GEPPW3QkT0wDsiw9GEPPXLQkT3D8Zcw9mEPPWrQkT2KA/Kx9GEPPXHQkT05/kYy9GEPPW7QkT3P8bcw9GEPPXPQkT0fB2Sx82EPPXDQkT05/kYy9GEPPXDQkT1z/I0x9GEPPXHQkT2OA/Kx9GEPPXDQkT3kgPyy9GEPPXDQkT2NA/Kx9GEPPXPQkT0fB2Sx9WEPPWzQkT11/I0x9GEPPXDQkT3HATmy9mEPPWzQkT0df4My9GEPPXPQkT3IATmy9GEPPW3QkT0wDsiw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw9WEPPW7QkT3GATmy9GEPPW/QkT1z/I0x9GEPPW7QkT0YB2Sx9WEPPW7QkT06/gYy9GEPPW/QkT1z/I0x9GEPPW/QkT1z/I0x9GEPPW7QkT10/I0x9GEPPW7QkT06/kYy9GEPPW7QkT1z/I0x9WEPPW/QkT0cf4My9WEPPXDQkT1y/I0x9WEPPW/QkT2OA7Kx9GEPPW7QkT06/kYy9GEPPXDQkT0gx98v9WEPPW/QkT05/gYy9WEPPW7QkT0ZB2Sx9WEPPW7QkT3GATmy82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw9WEPPW/QkT1z/I0x9GEPPW/QkT05/gYy9GEPPXDQkT2R428w9GEPPXHQkT3kgLyy9GEPPXHQkT3kgLyy82EPPW/QkT3GATmy9GEPPXHQkT06Dsiw82EPPW/QkT3LOICv9WEPPXXQkT3e+Bsx9WEPPXXQkT3e+Bsx9WEPPXXQkT3e+Bsx9WEPPXXQkT3e+Bsx9WEPPXXQkT3e+Bsx9WEPPXXQkT3e+Bsx9WEPPXXQkT3e+Bsx9WEPPXXQkT3e+Bsx9WEPPXXQkT3e+Bsx9WEPPXPQkT1ADpiw9GEPPXHQkT0dB2Sx9GEPPWrQkT12/I0x9GEPPXDQkT2OA/Kx9GEPPW/QkT1z/I0x9GEPPXHQkT1y/I0x9GEPPW/QkT02Dsiw9WEPPW/QkT1z/I0x82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTaw82EPPXPQkT3asTawV8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAPyzX77JSVLix7poov5eiQD9Yxt2yqfIase6aKL+XokA/cLDFsnbkhC/umii/l6JAP+LI4LJN3wWx7poov5eiQD+22fKyo8qtse6aKL+XokA/4sjgsk3fBbHumii/l6JAP1jG3bKp8hqx7poov5eiQD9Yxt2yqfIase6aKL+XokA/+7LIssK/FjDumii/l6JAP27L47Lhl+Gw7poov5eiQD9uy+Oy4ZfhsO6aKL+XokA/WMbdsqnyGrHumii/l6JAP7bZ8rKjyq2x7poov5eiQD9Yxt2yqfIase6aKL+XokA/bsvjsuGX4bDumii/l6JAP27L47Lhl+Gw7poov5eiQD9Yxt2yqfIase6aKL+XokA/zsPasggGMLHumii/l6JAP1jG3bKp8hqx7poov5eiQD9Yxt2yqfIase6aKL+XokA/ttnysqPKrbHumii/l6JAP+LI4LJN3wWx7poov5eiQD/mrcKystmOru6aKL+XokA/4sjgsk3fBbHumii/l6JAPyzX77JSVLix7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1a73bL76hqxfcckv/jqQz9Wu92y++oasX3HJL/46kM/t8DjslOE4bB9xyS/+OpDPxFvj7KgeyQyfcckv/jqQz+3wOOyU4ThsH3HJL/46kM/VrvdsvvqGrF9xyS/+OpDP8b0BrM/Dfmxfcckv/jqQz9n2fuyPiUcsn3HJL/46kM/wM/yspLLrbF9xyS/+OpDP8DP8rKSy62xfcckv/jqQz9fyuyy/N/CsX3HJL/36kM/FfIDs9QQB7J9xyS/+OpDP/S117LNE0Wxfcckv/jqQz/ll7myFkuIMX3HJL/46kM/wYensrAbEjF9xyS/+OpDP+WXubIUS4gxfcckv/jqQz/0tdey0BNFsX3HJL/46kM/GcbpsqoyjbB9xyS/+OpDP8b0BrM/Dfmxfcckv/jqQz9n2fuyPiUcsn3HJL/46kM/wM/yspLLrbF9xyS/+OpDP8DP8rKSy62xfcckv/jqQz+5wOOyU4ThsH3HJL/46kM/5Ze5shhLiDF9xyS/+OpDP1a73bL76hqxfcckv/jqQz9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAPyzX77JSVLix7poov5eiQD+H2Bi67e8nOjoBKr+lZj8/NVYQu+vQHjuxqC2/Dhc8P8bRmLvUa6g7XLsyvwhFNz+ojv67tIcMPBBnOL8DjDE/xWI5vIEdTTz76D2/Sp0rP9x8d7ycPok8JZVCv+1AJj+DRZu8UrOsPM/VRb9kTyI/J+G5vNRwzzzmHke/mqggPybr1rwzyvA8QbxDv/uvJD9kCfK8jiQIPbAIOr8xfi8/bscEvZL3FT2a8Cq/oSc+P+XEDb0atCA9IgcYv6eITT/8fBO9JrYnPXvsA78j8lo/jkUWvQFNKz3QyOO+rdNkP5ceF71SjSw98zDKvmzBaj90MBe9orwsPWuIwL6wxmw/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/dDAXvaK8LD1riMC+sMZsP3MwF72kvCw9a4jAvrDGbD90MBe9o7wsPWuIwL6wxmw/dTAXvaS8LD1riMC+sMZsP3MwF72kvCw9a4jAvrDGbD91MBe9pLwsPWuIwL6wxmw/cDAXvaO8LD1riMC+sMZsP3IwF72lvCw9a4jAvrDGbD9zMBe9pLwsPWuIwL6wxmw/czAXvaS8LD1riMC+sMZsP3IwF72kvCw9a4jAvrDGbD9zMBe9prwsPW6IwL6vxmw/cjAXvaS8LD1riMC+sMZsP3MwF72mvCw9bojAvq/GbD9yMBe9pLwsPWuIwL6wxmw/czAXvaS8LD1riMC+sMZsP3QwF72jvCw9a4jAvrDGbD9yMBe9o7wsPWuIwL6wxmw/cDAXvaO8LD1riMC+sMZsP3UwF72kvCw9a4jAvrDGbD9zMBe9pLwsPWuIwL6wxmw/dTAXvaS8LD1riMC+sMZsP3MwF72lvCw9a4jAvrDGbD9zMBe9pLwsPWuIwL6wxmw/dDAXvaK8LD1riMC+sMZsP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/dTAXvaO8LD1riMC+sMZsP3cwF72ivCw9a4jAvrDGbD90MBe9prwsPWuIwL6wxmw/bjAXvai8LD1riMC+sMZsP3UwF72ivCw9a4jAvrDGbD90MBe9orwsPWuIwL6wxmw/dTAXvaO8LD1riMC+sMZsP3IwF72kvCw9a4jAvrDGbD9zMBe9pLwsPWuIwL6wxmw/cjAXvaS8LD1riMC+sMZsP3QwF72lvCw9a4jAvrDGbD9yMBe9pLwsPWuIwL6wxmw/dTAXvaO8LD1riMC+sMZsP3MwF72mvCw9a4jAvrDGbD9yMBe9pLwsPWuIwL6wxmw/cDAXvam8LD1uiMC+r8ZsP3UwF72ivCw9a4jAvrDGbD9zMBe9pLwsPWuIwL6wxmw/dTAXvaO8LD1riMC+sMZsP3MwF72kvCw9a4jAvrDGbD9zMBe9pLwsPWuIwL6wxmw/dDAXvaS8LD1riMC+sMZsP3YwF72kvCw9a4jAvrDGbD91MBe9o7wsPWuIwL6wxmw/dTAXvaO8LD1riMC+sMZsP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/dDAXvaK8LD1riMC+sMZsP5EeF71SjSw98zDKvmzBaj+JRRa9AU0rPdDI476t02Q/A30TvSO2Jz187AO/IvJaP+fEDb0ftCA9IwcYv6eITT90xwS9jfcVPZvwKr+gJz4/aQnyvIskCD2yCDq/L34vPyjr1rwsyvA8QbxDv/yvJD8n4bm81HDPPOYeR7+aqCA/hEWbvFmzrDzP1UW/ZE8iP/B8d7yVPok8J5VCv+xAJj/NYjm8fR1NPPzoPb9JnSs/tY7+u6SHDDwQZzi/A4wxP7bRmLvGa6g7W7syvwhFNz/eVRC7HdEeO7CoLb8OFzw/EtkYug/vJzo6ASq/pWY/PyzX77JSVLix7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD+3LEI+cFxzvoa1Pb9oQhk/tixCPnBcc76GtT2/aEIZP7UsQj5wXHO+hrU9v2hCGT+1LEI+cFxzvoa1Pb9oQhk/tixCPnBcc76GtT2/aEIZPxNNHz7GYT++e88/v1+zHT/k95w9xzuPvcmmQb8IUSU/agB+u56QRj23dD+/g3wpPzRvIr3Sds49AzM9vxE1Kj81byK91HbOPQMzPb8RNSo/OW8ivdJ2zj0DMz2/ETUqPzhvIr3Sds49AzM9vxE1Kj82byK90nbOPQMzPb8RNSo/FgB+u52QRj24dD+/g3wpP+X3nD3IO4+9yaZBvwhRJT8RTR8+xGE/vnnPP79isx0/tyxCPnBcc76GtT2/aEIZP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAPws9ab7e83G++7oOv580Qz9A63i+LhhlvsVPAr+beUs/juOBvjhEWr5jTvG+wz5RP39whb5erVG+XjbjvtgkVT8Ak4e+L0VLvkex2b7/qVc/Va+IviLWRr4k69O+9i1ZP+8bib7sFUS+ifrQvs/6WT/bJom+T7BCvm7sz76jTVo/RhqJvgJNQr6ayc++bl1aP0Yaib4CTUK+msnPvm5dWj9GGom+Ak1CvprJz75uXVo/RhqJvgJNQr6ayc++bl1aP0Yaib4CTUK+msnPvm5dWj9GGom+Ak1CvprJz75uXVo/RhqJvgJNQr6ayc++bl1aP0Yaib4CTUK+msnPvm5dWj9GGom+Ak1CvprJz75uXVo/3CaJvk+wQr5u7M++o01aP+0bib7oFUS+kPrQvs76WT9Vr4i+I9ZGvijr0771LVk/AZOHvjFFS75Gsdm+/6lXP35whb5crVG+YTbjvtgkVT+N44G+OERavmRO8b7DPlE/QOt4vi4YZb7GTwK/m3lLPws9ab7e83G++7oOv580Qz9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD9Xxt2ypPIase6aKL+XokA/V8bdsqTyGrHumii/l6JAP1fG3bKk8hqx7poov5eiQD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD/+/38//v9/P///fz8AAIA//v9/PwAAgD///38//v9/P///fz///38//v9/PwAAgD/+/38//v9/P///fz///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/P///fz8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD/+/38//v9/P///fz/+/38//v9/P///fz8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD/+/38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD/9/38//v9/P///fz/9/38//v9/P///fz/+/38//v9/P///fz///38//v9/P///fz/+/38//v9/P///fz///38//v9/PwAAgD///38//v9/PwAAgD/+/38//v9/P///fz///38//v9/PwAAgD/+/38//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD/9/38//v9/P///fz/9/38//v9/P///fz/+/38//v9/P///fz/9/38//v9/P///fz/+/38//v9/PwAAgD/9/38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/P/7/fz///38/AACAPwAAgD8AAIA//v9/PwEAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwEAgD8AAIA//v9/PwAAgD///38//v9/P///fz8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/P///fz///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/P///fz8AAIA//v9/PwAAgD///38/AACAPwAAgD///38//v9/PwAAgD///38//v9/P///fz///38//v9/P///fz///38//v9/PwAAgD8AAIA//v9/P///fz8AAIA//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/P///fz///38//v9/PwAAgD///38//v9/P///fz///38//v9/PwAAgD///38//v9/PwAAgD///38/AACAPwAAgD8AAIA//v9/PwAAgD///38//v9/P///fz///38//v9/P///fz///38//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/P///fz8AAIA/AACAP///fz///38//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/P///fz8AAIA//v9/PwAAgD///38//v9/P///fz///38//v9/P///fz8AAIA//v9/P///fz8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/P/7/fz///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/P///fz///38//v9/PwAAgD///38//v9/P///fz8AAIA//v9/P///fz8AAIA/AACAPwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwEAgD8AAIA/AACAPwAAgD8AAIA//v9/PwEAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwEAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwEAgD/+/38/AACAP///fz///38//v9/P///fz///38/AACAPwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD/+/38//v9/PwAAgD///38//v9/PwAAgD/+/38//v9/P///fz///38//v9/PwAAgD/+/38//v9/P///fz/+/38//v9/P///fz///38//v9/PwAAgD/+/38//v9/P///fz/+/38//v9/PwAAgD/+/38//v9/P///fz///38//v9/PwAAgD///38//v9/P///fz8AAIA//v9/P///fz///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA/AACAPwEAgD///38/AACAPwAAgD///38//v9/PwAAgD/+/38//v9/P///fz///38//v9/PwAAgD///38//v9/PwAAgD///38/AACAPwEAgD///38/AACAPwEAgD///38/AACAPwEAgD///38/AACAPwEAgD///38/AACAPwEAgD///38/AACAPwEAgD///38/AACAPwEAgD///38/AACAPwEAgD///38/AACAPwEAgD///38//v9/PwAAgD/+/38//v9/PwAAgD/+/38//v9/P/7/fz8AAIA//v9/PwEAgD///38//v9/PwAAgD///38//v9/PwAAgD8AAIA//v9/PwEAgD///38/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AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwMAgD/8/38/AACAPwIAgD/8/38/AgCAPwIAgD/8/38/AgCAPwMAgD/9/38/AACAPwMAgD/8/38/AQCAPwQAgD8BAIA/AQCAPwMAgD/6/38/AACAPwMAgD/+/38/AwCAPwUAgD8BAIA/AwCAPwQAgD/8/38/AwCAPwIAgD/8/38/AQCAPwQAgD8AAIA///9/PwIAgD/8/38/AQCAPwQAgD8AAIA///9/PwIAgD/8/38/AgCAPwMAgD/8/38/AgCAPwUAgD8BAIA/AgCAPwMAgD/+/38/AgCAPwMAgD/6/38/AACAPwUAgD8CAIA/AACAPwIAgD/8/38/AgCAPwMAgD/9/38/AQCAPwAAgD/6/38/AQCAPwIAgD8BAIA/AQCAPwMAgD/8/38/AACAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwIAgD/4/38/AgCAPwMAgD8AAIA/AwCAPwIAgD/+/38/AQCAPwQAgD/8/38//P9/PwMAgD8AAIA/AgCAPwIAgD8AAIA/AACAPwQAgD/8/38/AQCAPwIAgD/9/38//v9/PwIAgD/+/38/AQCAPwIAgD/8/38//f9/PwQAgD/+/38/AACAPwQAgD/8/38/AACAPwIAgD/4/38/AACAPwIAgD/8/38/AQCAPwQAgD8BAIA/BACAPwQAgD/9/38/AgCAPwIAgD8AAIA/AwCAPwMAgD8AAIA///9/PwQAgD/9/38/AgCAPwIAgD/9/38///9/PwIAgD8BAIA/AQCAPwIAgD/8/38//v9/PwAAgD/8/38/AQCAPwMAgD8AAIA/AgCAPwIAgD/4/38/AgCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwMAgD/8/38/AACAPwMAgD/+/38/AACAPwAAgD/9/38/AgCAPwAAgD/4/38//v9/PwMAgD/+/38/AACAPwMAgD/+/38/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AQCAPwAAgD8AAIA///9/P/7/fz/8/38///9/PwIAgD/8/38//f9/PwIAgD/9/38/AQCAPwAAgD/8/38//v9/PwAAgD8AAIA/AgCAPwQAgD/+/38/AgCAP/3/fz/4/38/AQCAPwIAgD/8/38///9/PwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwIAgD/8/38///9/PwAAgD/7/38/9v9/PwAAgD/+/38/AACAPwAAgD8AAIA/AACAPwQAgD8AAIA/AACAPwMAgD/9/38/AgCAPwMAgD/+/38/BQCAPwMAgD8BAIA/AgCAPwQAgD8CAIA/AgCAPwMAgD8CAIA/AACAPwAAgD/9/38///9/P/7/fz/8/38//f9/PwMAgD8BAIA/AgCAP/7/fz/8/38//f9/PwAAgD/9/38///9/PwMAgD8CAIA/AACAPwQAgD8BAIA/AgCAPwMAgD8BAIA/AgCAPwMAgD/+/38/BQCAPwMAgD/9/38/AgCAPwQAgD8AAIA/AACAPwAAgD8AAIA/AACAPwMAgD8BAIA/AgCAPwIAgD8BAIA//v9/PwIAgD/8/38///9/PwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwIAgD8BAIA/AQCAPwAAgD/6/38//v9/PwIAgD8AAIA/AACAPwIAgD/+/38/AgCAPwMAgD8AAIA/AgCAPwIAgD8CAIA/AQCAPwMAgD/+/38/AwCAPwQAgD8BAIA/AgCAP/7/fz/4/38//v9/P/7/fz/8/38/AQCAP///fz8BAIA//P9/PwIAgD/+/38/AACAPwIAgD8BAIA/AQCAPwQAgD8CAIA/AgCAPwQAgD/9/38/AgCAPwMAgD8CAIA/AgCAPwMAgD8AAIA/AgCAPwAAgD8DAIA/AQCAPwMAgD/+/38/AwCAPwAAgD8BAIA//v9/P/7/fz/4/38//v9/PwIAgD8CAIA/AQCAPwAAgD8BAIA/AwCAPwAAgD/9/38/AACAPwIAgD8BAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwIAgD/8/38///9/PwIAgD/8/38/AQCAPwAAgD/9/38//f9/PwAAgD/8/38/AQCAPwIAgD/8/38/+/9/PwQAgD8BAIA//f9/P/3/fz/8/38/+/9/PwAAgD8CAIA/AACAPwAAgD8AAIA///9/P///fz/8/38//v9/PwAAgD/+/38/AACAPwIAgD/+/38/AQCAPwAAgD/7/38/AACAPwMAgD8CAIA/AACAPwIAgD8AAIA/AACAPwIAgD/7/38//v9/PwMAgD/8/38/AACAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwAAgD/2/38//v9/PwMAgD/9/38/AACAPwAAgD/+/38///9/PwIAgD8AAIA/AgCAP///fz/7/38/AACAPwIAgD8AAIA/AgCAPwAAgD/9/38/AgCAPwAAgD/8/38/AACAPwMAgD8BAIA///9/P/7/fz/4/38/+f9/P/r/fz/9/38//f9/P/7/fz8BAIA//f9/P/z/fz/8/38//v9/PwAAgD/7/38/AQCAPwIAgD8BAIA/+/9/P/7/fz/+/38/+f9/PwAAgD/2/38//v9/PwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwMAgD8CAIA/AQCAPwAAgD8AAIA///9/PwAAgD/8/38/AQCAPwAAgD8AAIA/AQCAPwMAgD8BAIA/AgCAPwAAgD8DAIA/AQCAPwAAgD/9/38//v9/PwIAgD/7/38/AACAPwMAgD8AAIA/AQCAPwMAgD8AAIA/AQCAPwMAgD8AAIA/AQCAPwMAgD8AAIA/AQCAPwMAgD8AAIA/AQCAPwMAgD8AAIA/AQCAPwMAgD8AAIA/AQCAPwMAgD8AAIA/AQCAPwMAgD8AAIA/AQCAPwIAgD8BAIA/AACAPwQAgD8CAIA/AACAPwAAgD8BAIA///9/P/r/fz/2/38///9/PwAAgD/9/38///9/P/7/fz/8/38//v9/P/7/fz/+/38/AQCAPwMAgD8CAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPwQAgD8AAIA/AQCAPxHWlbNpSJY9cue0sBHWlbNpSJY9cue0sBHWlbNpSJY9cue0sBHWlbNpSJY9cue0sBHWlbNpSJY9cue0sBHWlbNpSJY9cue0sBHWlbNpSJY9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jZxkP1P9CD+Vi5u8floCvSQOWD+k9xc/F0O2vIUvK71TqE0/JlcPP4XipryofxO9vddTP9Ms1T5HXE68RypVvJO1aD97FDA+idmKuu3znTxyI3w/1kC8O1FneDu5KJc7wP1/P9NoVT6QBCq7RP15PPBYej/Y97U+kp4bvBKvhbuIRW8/5O6LPlcCtbu7qus7Cj52P50pvDvrZng7axeXO8H9fz94Pwk+ELvpOZ4evDw8n30/eD8JPhC76TmeHrw8PJ99P3g/CT4Qu+k5nh68PDyffT8n7OE+6P9jvPkSirxfqWU/EdyYPtUG3LtE0Hs7CFF0P1UUvDu3ZXg7eweXO8H9fz+khLw7A2p4O1xblzu//X8/VsC8OxlseDuUh5c7vv1/P8vcvDvVbXg7MZ2XO7/9fz9+6rw7lG14O12nlzu9/X8/juu8OwdueDsCqJc7vf1/PwDqvDtfbXg7mKaXO739fz8A6rw7X214O5imlzu9/X8/AOq8O19teDuYppc7vf1/PwDqvDtfbXg7mKaXO739fz8A6rw7X214O5imlzu9/X8/AOq8O19teDuYppc7vf1/PwDqvDtfbXg7mKaXO739fz8A6rw7X214O5imlzu9/X8/AOq8O19teDuYppc7vf1/P67rvDvzbXg79qeXO739fz+46rw7P214O8Gnlzu9/X8/Bd28O1dteDtKnZc7v/1/P/W/vDsdbHg7mIeXO779fz9GhLw7pml4O05blzu//X8/rRS8O6NleDuSB5c7wf1/Pw3cmD7kBty7RdB7OwlRdD8n7OE+6P9jvPkSirxfqWU/eD8JPhC76TmeHrw8PJ99P3g/CT4Qu+k5nh68PDyffT94Pwk+ELvpOZ4evDw8n30/eD8JPhC76TmeHrw8PJ99P3g/CT4Qu+k5nh68PDyffT94Pwk+ELvpOZ4evDw8n30///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38/+/9/P/3/fz/7/38/+f9/P/z/fz/6/38//f9/P/r/fz///38//P9/P/z/fz/6/38///9/P/z/fz8BAIA/9v9/P/n/fz/6/38/+f9/P/v/fz/6/38/+v9/P/j/fz/3/38/+v9/P/f/fz///38/+P9/P/3/fz/4/38/AgCAPwIAgD///38/+v9/P/r/fz/9/38///9/P/3/fz/8/38/+v9/P/r/fz/9/38/AgCAPwEAgD/7/38/+v9/P/z/fz/6/38/+P9/P/X/fz/8/38/+f9/P/j/fz/4/38/+f9/P/v/fz/6/38/+P9/P/v/fz/1/38//v9/P/z/fz8BAIA/+f9/P/z/fz/6/38//v9/P/j/fz/8/38/+v9/P/r/fz/4/38/+/9/P/3/fz/7/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38/+v9/P/3/fz///38/AgCAP/3/fz8DAIA//P9/P/z/fz/6/38///9/P/z/fz/8/38//P9/P/j/fz/8/38/AgCAP///fz///38///9/P/3/fz8BAIA/+P9/P/v/fz/6/38//f9/P/z/fz///38/AACAP/r/fz///38/9f9/P/v/fz/9/38/+/9/P///fz///38/9/9/P/n/fz/9/38/AQCAP/v/fz8CAIA/+v9/P/b/fz/9/38//f9/P/3/fz/7/38/AACAP/j/fz/8/38/AQCAP/z/fz/9/38//f9/P/z/fz8BAIA/9/9/P/v/fz/6/38//f9/P/3/fz/9/38/+P9/P/3/fz///38/AACAP/3/fz/7/38//v9/P/3/fz8CAIA/+v9/P/3/fz///38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38/+/9/P/3/fz/7/38/AgCAP///fz8BAIA//P9/P/j/fz/7/38/+/9/P/v/fz///38/9v9/P/v/fz/6/38/AACAP/j/fz///38/AQCAP/v/fz/6/38//v9/P/v/fz/6/38/AQCAP/j/fz///38/AQCAP/b/fz/7/38/+v9/P/z/fz/8/38//P9/P/r/fz8DAIA//f9/P/3/fz8DAIA//P9/P/r/fz///38/+/9/P/f/fz/6/38/AACAP/r/fz/9/38//f9/P///fz///38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38//f9/P///fz///38//f9/P///fz/7/38/+v9/P/z/fz/8/38//f9/P/r/fz/9/38/+/9/P///fz8BAIA/+/9/P/j/fz///38//f9/P/z/fz/9/38/AACAP/v/fz///38//P9/P/3/fz/9/38//v9/P///fz///38/AACAP/3/fz/8/38//P9/P/r/fz///38/+v9/P/z/fz8BAIA//P9/P/r/fz///38/AACAP/3/fz/8/38//v9/P///fz///38//v9/P/3/fz///38/AACAP///fz///38//f9/P/z/fz/9/38//P9/P/z/fz///38/+/9/P///fz8BAIA//f9/P/r/fz/9/38/+/9/P/3/fz/9/38///9/PwEAgD///38//f9/P///fz///38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/z/fz/7/38/+f9/P/3/fz/9/38//P9/P/r/fz/6/38//f9/PwEAgD/4/38/AACAP/z/fz/8/38/+/9/P/3/fz/4/38//P9/P/3/fz/6/38//v9/PwEAgD/9/38/+/9/P/v/fz/8/38///9/P/r/fz///38/+/9/P///fz/2/38/+/9/P/v/fz/6/38///9/P/z/fz/7/38//P9/P///fz/6/38/AQCAP///fz/4/38/+/9/P///fz8DAIA/AACAP/z/fz/8/38//P9/PwEAgD///38//P9/P/3/fz/6/38//f9/PwEAgD8BAIA/+/9/P/v/fz/8/38//f9/P/f/fz/6/38///9/P/3/fz8BAIA//v9/PwIAgD8BAIA///9/P/z/fz/7/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38//f9/P///fz///38///9/P/r/fz/8/38//f9/P/n/fz/6/38/+/9/P/n/fz/6/38/9/9/P/n/fz/1/38/AQCAP/3/fz8BAIA//P9/P/j/fz/3/38/AACAP/3/fz/8/38/AQCAP/j/fz///38//f9/P/r/fz/8/38///9/P/z/fz/6/38//v9/P/j/fz/8/38/AACAP///fz/6/38///9/P/n/fz/9/38//v9/P/n/fz/7/38/9/9/P/v/fz/8/38/+/9/P/3/fz/7/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38/+f9/P/z/fz///38/AQCAPwEAgD/6/38//f9/P/3/fz/9/38/+f9/P/H/fz/2/38/+v9/P///fz/7/38/AACAP/v/fz/7/38//f9/P/r/fz8BAIA///9/P///fz8BAIA/AACAP/z/fz/4/38/9/9/P///fz///38/AQCAP/f/fz/7/38/AQCAP///fz8BAIA/+f9/P///fz///38//P9/P/3/fz///38/+/9/P/z/fz/9/38/AQCAP/v/fz/1/38/+f9/P/z/fz///38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38//P9/P/v/fz/7/38//v9/P/v/fz/6/38///9/P/3/fz/6/38/+/9/P/z/fz/0/38/+v9/P/3/fz/6/38/+/9/P/3/fz/8/38/+v9/P/r/fz/7/38///9/P///fz/3/38/+f9/P/v/fz/z/38/+f9/P/v/fz/z/38/+f9/P/v/fz/z/38/+f9/P/v/fz/z/38/+f9/P/v/fz/z/38/+f9/P/v/fz/z/38/+f9/P/v/fz/z/38/+f9/P/v/fz/z/38/+f9/P/v/fz/z/38/AACAP/3/fz/4/38//v9/P/j/fz/9/38/+v9/P/3/fz/6/38//P9/P///fz8BAIA///9/P/r/fz/6/38/+f9/P/v/fz///38/AQCAP/z/fz/7/38//P9/P/v/fz/7/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38///9/P/j/fz/6/38/+kBMM+vZQjx53y4x+kBMM+vZQjx53y4x+kBMM+vZQjx53y4x+kBMM+vZQjx53y4x+kBMM+vZQjx53y4x+kBMM+vZQjx53y4x+kBMM+vZQjx53y4x+kBMM+vZQjx53y4x+kBMM+vZQjx53y4x+kBMM+vZQjx53y4xpisbMr/ZQjyGczgyOmGRs/zZQjxcVCix+z1QM9rZQjxxkg0xnirkMtfZQjy2pbSyZTqKMvLZQjzD6w0ytsc5M+rZQjwJhN2xLjrXMv7ZQjzcQgixBoVfM9PZQjwI+AmxKvrZMr7ZQjz+QB4ydr+ws7LZQjyFglqy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+v9/P/3/fz/+/38//P9/P///fz8AAIA/+/9/P/3/fz/8/38///9/P///fz8BAIA//f9/P/r/fz8BAIA//P9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz/+/38/+/9/P/v/fz/z/38/+/9/P/z/fz8AAIA/+/9/P///fz8AAIA///9/P/r/fz/2/38///9/P/3/fz/7/38///9/P/v/fz/7/38//f9/P/z/fz/+/38//f9/PwAAgD8BAIA/AACAPwAAgD/8/38/+v9/P/z/fz/7/38/+v9/P/n/fz/7/38//f9/P///fz/+/38/AQCAP///fz/4/38/AACAP/r/fz/3/38//v9/P/f/fz/9/38//f9/P/f/fz/6/38//f9/P/3/fz/7/38//v9/P/n/fz/9/38//v9/P///fz/9/38/+/9/PwAAgD8CAIA///9/P/3/fz/8/38/AACAP/r/fz/5/38//f9/PwAAgD/+/38/AACAPwIAgD/+/38/+/9/P/v/fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8BAIA//P9/P///fz/9/38/+P9/P/f/fz/9/38//f9/PwEAgD/7/38/+/9/PwEAgD8BAIA///9/P/7/fz/7/38//f9/P/7/fz///38//P9/P///fz/+/38/+v9/P/7/fz8DAIA/AACAPwEAgD/9/38///9/P///fz8BAIA/AACAP///fz8AAIA/+f9/PwAAgD8AAIA/+P9/P/v/fz/4/38/9/9/P///fz/4/38/+/9/P///fz/8/38///9/PwEAgD/9/38/9v9/P/n/fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz/9/38/9v9/P/n/fz/7/38/9v9/P/r/fz8AAIA/+P9/P/v/fz8AAIA/+/9/P/r/fz/6/38/+f9/P/n/fz8BAIA/9f9/P/7/fz8DAIA//f9/PwEAgD8AAIA/+P9/P/v/fz8AAIA/+f9/P/7/fz8AAIA//f9/P/3/fz/9/38/9f9/P/r/fz/+/38/9v9/P/3/fz8AAIA//P9/P/z/fz/+/38/9v9/P/3/fz/9/38/9f9/P/r/fz8AAIA//f9/P/3/fz/7/38//P9/P///fz8DAIA/9v9/PwAAgD8DAIA//f9/PwEAgD8BAIA/9v9/P/7/fz/6/38/+f9/P/n/fz8AAIA/+/9/P/r/fz///38/+P9/P///fz8BAIA/9/9/P/v/fz/9/38/9v9/P/n/fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz/7/38/9/9/PwAAgD/9/38/+f9/P/3/fz8AAIA/+P9/P/v/fz/7/38/+v9/P/3/fz/+/38/+P9/P/7/fz/6/38/9P9/P/r/fz8DAIA/+v9/PwAAgD/8/38/+/9/PwEAgD8CAIA/+f9/P///fz8AAIA/+f9/P/n/fz///38/9/9/P/z/fz/7/38/+P9/P/z/fz/7/38/9/9/PwAAgD/5/38/+f9/P/v/fz/3/38/+f9/P/f/fz///38/9v9/P/3/fz/+/38/+P9/P/7/fz8AAIA//P9/PwAAgD8DAIA/+v9/PwAAgD8AAIA/+/9/P/3/fz8CAIA/+f9/P///fz/9/38//f9/PwAAgD8CAIA//f9/P/3/fz8BAIA/+v9/P/v/fz/7/38/9/9/PwAAgD8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz/9/38/9v9/P/n/fz/2/38/+P9/P/n/fz/9/38/+f9/PwAAgD8BAIA//v9/P/3/fz/+/38/+/9/P/7/fz/3/38//P9/P/z/fz8BAIA//v9/P///fz8AAIA///9/P/7/fz8DAIA/AACAPwEAgD8CAIA/8/9/P/3/fz///38/+/9/PwEAgD/4/38//f9/P/j/fz/1/38/+/9/P/f/fz/+/38///9/P/3/fz/8/38/+/9/P/z/fz/+/38/+/9/P/7/fz8BAIA//P9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz8DAIA///9/P///fz/+/38/9f9/P/n/fz/+/38/+f9/P/v/fz/9/38//f9/P/3/fz8BAIA/AgCAP/3/fz/8/38//f9/P///fz/5/38/AQCAP/3/fz/8/38/+P9/P/z/fz8AAIA///9/PwAAgD/8/38//v9/P/3/fz8BAIA/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X1N9PxW59z2M/aq9AACovFsqfT9N//Q9DpGzvXSE2LwAFH0/Tf/0PQ6Rs710hNi8ABR9P03/9D0OkbO9dITYvAAUfT9N//Q9DpGzvXSE2LwAFH0/Tf/0PQ6Rs710hNi8ABR9P03/9D0OkbO9dITYvAAUfT9N//Q9DpGzvXSE2LwAFH0/Tf/0PQ6Rs710hNi8ABR9P03/9D0OkbO9dITYvAAUfT8cufc9hP2qvSMAqLxbKn0/Mf8APrAgj72fr7W7X1N9P5pUCz4txS+96I9qPMpafT8GlRs+OsZCO0EjET053Xw/7V0xPg0slD36jWM9+wt7P2zASj6toyk+ilibPfmOdj+T/tK9fT2ePoqGiD13bnE/BimMvmW16T5mkZ47xbhYP6e14b2sqSG8cgoUvOJqfj+nteG9rKkhvHIKFLzian4/p7XhvaypIbxyChS84mp+P6e14b2sqSG8cgoUvOJqfj+nteG9rKkhvHIKFLzian4/p7XhvaypIbxyChS84mp+P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz/4/38//v9/P/3/fz/8/38//f9/P/r/fz/8/38//v9/P/z/fz/5/38//v9/P/3/fz/6/38/AACAP/r/fz/+/38//v9/P///fz/4/38///9/P/7/fz/8/38/AQCAP/r/fz/7/38///9/P/7/fz/6/38/AACAP/3/fz/7/38//f9/P/3/fz/8/38//f9/P/r/fz/8/38/AACAP/3/fz/9/38//f9/P/z/fz/6/38/+v9/P/7/fz/3/38/AACAP/z/fz/8/38//v9/P/z/fz/8/38///9/P///fz/4/38///9/P/n/fz/5/38//f9/P/7/fz/5/38/AQCAP/3/fz/6/38//v9/P/r/fz/7/38//f9/P/3/fz/7/38//P9/P/z/fz/4/38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/3/fz/8/38/AACAP/r/fz/9/38/AACAP/v/fz/8/38//P9/P///fz/9/38//f9/P/z/fz/9/38//f9/P/v/fz/7/38//f9/P/3/fz/7/38//P9/P/n/fz///38//P9/P/7/fz/7/38//P9/P/z/fz/7/38//f9/P/r/fz/+/38/+/9/P/n/fz/9/38//f9/P/z/fz/+/38///9/P/z/fz/9/38/+f9/P/z/fz8BAIA//f9/P/z/fz/4/38/+v9/P/3/fz/9/38//f9/P/z/fz/9/38//P9/P/z/fz/8/38//P9/P/v/fz/7/38//P9/P///fz/+/38//v9/P/3/fz/9/38//v9/P/z/fz/8/38/+v9/P/z/fz/7/38/AQCAP/3/fz/8/38/AACAP/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz/4/38//v9/P/3/fz/6/38//v9/P/3/fz/6/38///9/P/3/fz/7/38//f9/P/3/fz/4/38//P9/P/n/fz/7/38/AACAP/z/fz/8/38///9/P/j/fz/9/38/AACAP/z/fz/7/38//f9/P/7/fz/2/38/AQCAP/j/fz///38//f9/P/7/fz/5/38//P9/P///fz/2/38/AACAP/r/fz/9/38//f9/P///fz/5/38/+v9/P/f/fz/6/38/AACAPwAAgD/3/38/AACAP/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/PwAAgD/3/38/AACAPwMAgD/5/38/9v9/P/v/fz/5/38/AgCAPwIAgD/3/38//f9/P/7/fz/5/38///9/P///fz/2/38/AQCAP/z/fz/5/38/AQCAP///fz/7/38//v9/P/v/fz/4/38///9/PwEAgD/6/38//P9/PwEAgD/3/38/+f9/P/v/fz/7/38//v9/P/r/fz/6/38/AACAP/v/fz/7/38//v9/PwEAgD/3/38/+f9/PwEAgD/6/38//P9/P/f/fz/6/38/AQCAPwIAgD/1/38//f9/P/z/fz/5/38/AQCAP/3/fz/5/38///9/P/7/fz/5/38///9/PwIAgD/3/38//f9/P/v/fz/3/38/AQCAPwMAgD/4/38/9f9/PwAAgD/3/38/AACAP/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/j/fz/8/38/AQCAP/j/fz/9/38/AgCAP/3/fz/2/38/AACAP/7/fz/6/38//v9/P///fz/6/38///9/P/v/fz/3/38///9/P/z/fz/1/38/AQCAP/T/fz/9/38/AwCAP/z/fz/2/38/AACAP/7/fz/4/38///9/PwIAgD/3/38/9v9/P/z/fz/5/38/AQCAP/j/fz/8/38/AQCAP/r/fz/8/38///9/P///fz/3/38//f9/P/b/fz/6/38/BACAP///fz/6/38///9/P/f/fz/7/38/AgCAP/z/fz/1/38/AQCAPwAAgD/5/38//P9/P/z/fz/2/38/AACAP/v/fz/4/38/AACAP/3/fz/1/38/AACAPwEAgD/4/38//v9/P/j/fz/8/38/AQCAP/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/PwAAgD/3/38/AACAP/j/fz/4/38/AgCAP///fz/2/38//f9/P///fz/9/38//P9/PwEAgD/7/38/+P9/P/3/fz/5/38//f9/P/z/fz/7/38//v9/P/r/fz/8/38/AQCAP/z/fz/7/38//f9/P///fz/7/38/+f9/P/T/fz/7/38/AgCAP/n/fz/8/38/AQCAP/z/fz/6/38/+/9/P/n/fz/7/38//f9/P/7/fz/7/38///9/P/3/fz/4/38/AACAP/z/fz/4/38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/PwAAgD/y/38/AACAP///fz/9/38/9v9/P/n/fz///38//f9/P/r/fz/+/38//v9/P/H/fz///38/AQCAP/b/fz8BAIA///9/P/j/fz/3/38/AgCAP/f/fz///38///9/PwAAgD/1/38/AACAPwAAgD/3/38//f9/PwMAgD/u/38//v9/P///fz/5/38///9/P///fz/2/38/AQCAPwAAgD/1/38///9/PwAAgD/5/38//P9/P/3/fz8FAIA/7f9/PwAAgD/y/38/AACAP/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P///fz/6/38/+v9/P/n/fz/8/38///9/P/n/fz/3/38/AQCAP/r/fz/4/38//v9/P/v/fz/6/38///9/P/z/fz/5/38/AACAP/v/fz/6/38//v9/P/z/fz/5/38/AACAP/v/fz/+/38//v9/P/v/fz/+/38//v9/P/v/fz/+/38//v9/P/v/fz/+/38//v9/P/v/fz/+/38//v9/P/v/fz/+/38//v9/P/v/fz/+/38//v9/P/v/fz/+/38//v9/P/v/fz/+/38//v9/P/z/fz/7/38//f9/P/v/fz/4/38//v9/P/v/fz/8/38/+v9/P/v/fz/5/38/AQCAP/z/fz/6/38//v9/P/j/fz/8/38/AQCAP/v/fz/8/38///9/P///fz/6/38/+v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/z/fz///38//v9/P/NhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPRhD71t0JE9MA7IsPRhD71p0JE9PP4mMvRhD71y0JE94vgbMfZhD71s0JE9Lg7IsPRhD71w0JE9GwdksfVhD71u0JE9Mw7IsPZhD71q0JE9FAdksfVhD71t0JE9dPyNMfZhD71u0JE9c/yNMfRhD71w0JE9HsffL/ZhD71s0JE9dPzNMfVhD71t0JE9Lw7IsPRhD71x0JE9xwEZsvVhD71t0JE9Lw7IsPZhD71s0JE9dPzNMfRhD71w0JE9HsffL/ZhD71u0JE9MMffL/dhD71t0JE95/gbMfZhD71q0JE9FAdksfZhD71y0JE9HwdksfRhD71w0JE9GwdksfZhD71s0JE9Lg7IsPRhD71u0JE9dPzNMfRhD71p0JE9d/yNMfRhD71t0JE9MA7IsPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPRhD71t0JE9FwdksfZhD71w0JE9xwE5svVhD71s0JE9iwPysfNhD71w0JE9c/yNMfRhD71v0JE9GgdksfVhD71x0JE9HgdksfRhD71v0JE9jQPysfZhD71r0JE9igPysfRhD71q0JE97PgbMfRhD71v0JE9NA7IsPdhD71n0JE9ePyNMfVhD71x0JE9xwE5svRhD71t0JE9FwdksfRhD71s0JE9iwPysfdhD71v0JE9jgPysfRhD71u0JE9c/yNMfRhD71v0JE9GgdksfVhD71x0JE9HgdksfRhD71v0JE9jQPysfZhD71r0JE9igPysfRhD71q0JE97PgbMfdhD71n0JE9ePzNMfRhD71s0JE9FwdksfZhD71r0JE9FQdksfRhD71t0JE9FwdksfNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPRhD71t0JE9MA7IsPVhD71u0JE9jAPysfNhD71w0JE9xwF5svVhD71p0JE9d/yNMfRhD71x0JE9cvyNMfNhD71z0JE9B8ffL/RhD71v0JE95ICcsvRhD71x0JE9xwF5svRhD71x0JE9jgPysfRhD71y0JE9jwPysfRhD71u0JE9jAPysfJhD71w0JE9HH+DMvZhD71t0JE944C8svRhD71s0JE9dfyNMfNhD71w0JE9xwF5svVhD71t0JE9O8ffL/RhD71t0JE9MA7IsPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPRhD71t0JE9MA7IsPRhD71p0JE9PP4mMvRhD71y0JE94vgbMfZhD71s0JE9Lg7IsPRhD71w0JE9GwdksfVhD71u0JE9Mw7IsPZhD71q0JE9FAdksfVhD71t0JE9dPyNMfZhD71u0JE9c/yNMfRhD71w0JE9HsffL/ZhD71s0JE9dPzNMfVhD71t0JE9Lw7IsPRhD71x0JE9xwEZsvVhD71t0JE9Lw7IsPZhD71s0JE9dPzNMfRhD71w0JE9HsffL/ZhD71u0JE9MMffL/dhD71t0JE95/gbMfZhD71q0JE9FAdksfZhD71y0JE9HwdksfRhD71w0JE9GwdksfZhD71s0JE9Lg7IsPRhD71u0JE9dPzNMfRhD71p0JE9d/yNMfRhD71t0JE9MA7IsPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPRhD71t0JE9FwdksfdhD71v0JE9HAdksfVhD71t0JE9FwdksfRhD71w0JE9Of5GMvRhD71v0JE9GgdksfVhD71t0JE9xgE5svRhD71w0JE9GwdksfdhD71v0JE9jQOysfRhD71q0JE97PgbMfRhD71v0JE9jQOysfdhD71n0JE9ePyNMfVhD71x0JE9xwE5svRhD71t0JE9FwdksfRhD71s0JE9iwPysfdhD71v0JE9xwE5svVhD71q0JE9X8ffL/RhD71v0JE9GgdksfRhD71x0JE9HQdksfRhD71w0JE9xwE5svZhD71r0JE9iwPysfRhD71q0JE97PgbMfdhD71n0JE9Iw7IsPRhD71s0JE9FwdksfZhD71r0JE9FQdksfRhD71t0JE9FwdksfNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPRhD71t0JE9MA7IsPRhD71y0JE9w/GXMPZhD71q0JE9igPysfRhD71x0JE9Of5GMvRhD71u0JE9z/G3MPRhD71z0JE9HwdksfNhD71w0JE9Of5GMvRhD71w0JE9c/yNMfRhD71x0JE9jgPysfRhD71w0JE95ID8svRhD71w0JE9jQPysfRhD71z0JE9HwdksfVhD71s0JE9dfyNMfRhD71w0JE9xwE5svZhD71s0JE9HX+DMvRhD71z0JE9yAE5svRhD71t0JE9MA7IsPNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPVhD71u0JE9xgE5svRhD71v0JE9c/yNMfRhD71u0JE9GAdksfVhD71u0JE9Ov4GMvRhD71v0JE9c/yNMfRhD71v0JE9c/yNMfRhD71u0JE9dPyNMfRhD71u0JE9Ov5GMvRhD71u0JE9c/yNMfVhD71v0JE9HH+DMvVhD71w0JE9cvyNMfVhD71v0JE9jgOysfRhD71u0JE9Ov5GMvRhD71w0JE9IMffL/VhD71v0JE9Of4GMvVhD71u0JE9GQdksfVhD71u0JE9xgE5svNhD71z0JE92rE2sPNhD71z0JE92rE2sPNhD71z0JE92rE2sPVhD71v0JE9c/yNMfRhD71v0JE9Of4GMvRhD71w0JE9keNvMPRhD71x0JE95IC8svRhD71x0JE95IC8svNhD71v0JE9xgE5svRhD71x0JE9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prwsvWuIwD6wxmw/djAXvaS8LL1riMA+sMZsP3MwF72pvCy9a4jAPrDGbD9zMBe9prwsvWuIwD6wxmw/dDAXvaa8LL1riMA+sMZsP3QwF72kvCy9a4jAPrDGbD90MBe9pbwsvWuIwD6wxmw/dTAXvaS8LL1riMA+sMZsP3IwF72mvCy9a4jAPrDGbD91MBe9pLwsvWuIwD6wxmw/dDAXvaW8LL1riMA+sMZsP3QwF72kvCy9a4jAPrDGbD91MBe9pbwsvWuIwD6wxmw/dDAXvaS8LL1riMA+sMZsP3MwF72pvCy9a4jAPrDGbD91MBe9pLwsvWuIwD6wxmw/dDAXvaa8LL1riMA+sMZsP3QwF72mvCy9a4jAPrDGbD90MBe9prwsvWuIwD6wxmw/dDAXvaa8LL1riMA+sMZsP3UwF72lvCy9a4jAPrDGbD9Xxt2ypPIaMe6aKD+XokA/V8bdsqTyGjHumig/l6JAP1fG3bKk8hox7pooP5eiQD9Xxt2ypPIaMe6aKD+XokA/V8bdsqTyGjHumig/l6JAP3UwF72kvCy9a4jAPrDGbD94MBe9prwsvWuIwD6wxmw/czAXvai8LL1riMA+sMZsP28wF72pvCy9a4jAPrDGbD90MBe9prwsvWuIwD6wxmw/djAXvaS8LL1riMA+sMZsP3gwF72kvCy9a4jAPrDGbD90MBe9qLwsvWuIwD6wxmw/djAXvaW8LL1riMA+sMZsP3QwF72mvCy9a4jAPrDGbD91MBe9prwsvWuIwD6wxmw/djAXvai8LL1riMA+sMZsP3UwF72kvCy9a4jAPrDGbD92MBe9pLwsvWuIwD6wxmw/czAXvaa8LL1riMA+sMZsP3QwF72jvCy9a4jAPrDGbD90MBe9prwsvWuIwD6wxmw/cjAXvaW8LL1riMA+sMZsP3gwF72kvCy9a4jAPrDGbD90MBe9pbwsvWuIwD6wxmw/djAXvaW8LL1riMA+sMZsP3QwF72mvCy9a4jAPrDGbD91MBe9prwsvW6IwD6vxmw/djAXvaS8LL1riMA+sMZsP3UwF72kvCy9a4jAPrDGbD9Xxt2ypPIaMe6aKD+XokA/V8bdsqTyGjHumig/l6JAP1fG3bKk8hox7pooP5eiQD9Xxt2ypPIaMe6aKD+XokA/V8bdsqTyGjHumig/l6JAP3UwF72lvCy9a4jAPrDGbD8uGBe9pIUsvWdTyj4Kumo/SSwWvfMuK71eN+Q+SbhkP95GE70+die9okUEP6q8Wj9ucQ29bFIgvS9pGD+EQE0/aWUEvZ+FFb2UPis/CuI9P6Fb8bwvwAe9eTM6P1RRLz9uhda8YFXwvEfIQz/5oSQ/J+G5vNRwz7zmHkc/mqggP5yvm7zEK628S9pFP7JJIj+++Hi8MBWKvEijQj8RMCY/OzE7vMslT7yXAD4/34IrP8QDAbxbeg68H4M4P65uMT/KeZu7F2Wru07UMj+dLDc/B1ETu/YlIrs+uC0/rAg8PwtiHLqO4Su6JQYqP0ZiPz8s1++yUlS4Me6aKD+XokA/V8bdsqTyGjHumig/l6JAP1fG3bKk8hox7pooP5eiQD9Xxt2ypPIaMe6aKD+XokA/tixCPnBccz6GtT0/aEIZP7csQj5yXHM+hbU9P2lCGT+3LEI+cFxzPoW1PT9pQhk/tyxCPnJccz6FtT0/aUIZP7YsQj5wXHM+hrU9P2hCGT+2LEI+cFxzPoa1PT9oQhk/tixCPnBccz6GtT0/aEIZP7YsQj5wXHM+hrU9P2hCGT+2LEI+cFxzPoa1PT9oQhk/tixCPm5ccz6GtT0/aEIZP7csQj5wXHM+hbU9P2lCGT+3LEI+clxzPoW1PT9pQhk/tyxCPnBccz6FtT0/aUIZP7gsQj5yXHM+hbU9P2lCGT+3LEI+blxzPoa1PT9oQhk/tSxCPnBccz6GtT0/aEIZP7YsQj5wXHM+hrU9P2hCGT9Xxt2ypPIaMe6aKD+XokA/V8bdsqTyGjHumig/l6JAP1fG3bKk8hox7pooP5eiQD8MPWm+3vNxPvy6Dj+fNEM/Put4viwYZT7GTwI/m3lLP47jgb43RFo+Y07xPsM+UT9/cIW+Xa1RPmA24z7YJFU/ApOHvjNFSz5Jsdk+/qlXP1WviL4i1kY+J+vTPvYtWT/vG4m+6xVEPpD60D7P+lk/3SaJvlGwQj5y7M8+pE1aP0Yaib4CTUI+msnPPm5dWj9GGom+Ak1CPprJzz5uXVo/RhqJvgJNQj6ayc8+bl1aP0Yaib4CTUI+msnPPm5dWj9GGom+Ak1CPprJzz5uXVo/RhqJvgJNQj6ayc8+bl1aP0Yaib4CTUI+msnPPm5dWj9GGom+Ak1CPprJzz5uXVo/RhqJvgJNQj6ayc8+bl1aP94mib5UsEI+bOzPPqNNWj/xG4m+7BVEPo360D7O+lk/U6+IviDWRj4n69M+9i1ZPwGTh74wRUs+SbHZPv6pVz9/cIW+Xa1RPmA24z7YJFU/jeOBvjhEWj5jTvE+wz5RP0DreL4sGGU+xk8CP5t5Sz8MPWm+3vNxPvy6Dj+fNEM/V8bdsqTyGjHumig/l6JAP1fG3bKk8hox7pooP5eiQD9Xxt2ypPIaMe6aKD+XokA/V8bdsqTyGjHumig/l6JAP1fG3bKk8hox7pooP5eiQD9Xxt2ypPIaMe6aKD+XokA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA//v9/P/7/fz///38/AACAP/7/fz8AAIA///9/P/7/fz///38///9/P/7/fz8AAIA//v9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA//v9/P/7/fz///38//v9/P/7/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA//v9/P/7/fz8AAIA///9/P/7/fz8AAIA/AACAP/7/fz8AAIA///9/P/7/fz8AAIA//f9/P/7/fz///38//f9/P/7/fz///38//v9/P/7/fz///38///9/P/7/fz///38//v9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA//v9/P/7/fz///38///9/P/7/fz8AAIA//v9/P/7/fz8AAIA/AACAP/7/fz8AAIA///9/P/7/fz8AAIA//f9/P/7/fz///38//f9/P/7/fz///38//v9/P/7/fz///38//f9/P/7/fz///38//v9/P/7/fz8AAIA//f9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA/AACAP/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA//v9/P/7/fz/+/38///9/P/7/fz8AAIA/AACAP/7/fz8BAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA///9/P/7/fz8BAIA/AACAP/7/fz///38///9/PwAAgD///38///9/P/7/fz8BAIA//f9/P/7/fz8AAIA/AACAP/7/fz8AAIA///9/P/7/fz8AAIA///9/PwAAgD/+/38///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/3/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA//v9/P/7/fz8AAIA//v9/P/7/fz///38//v9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA//v9/P/7/fz///38///9/P/7/fz8AAIA//v9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/3/fz///38///9/P/3/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz///38///9/P/7/fz8AAIA///9/P/3/fz///38///9/P/7/fz///38///9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz///38//v9/P/3/fz8AAIA///9/P/3/fz///38///9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/3/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA/AACAP/7/fz8AAIA/AACAPwAAgD8BAIA///9/P/7/fz8AAIA/AACAP/7/fz///38///9/P/7/fz8BAIA///9/P/7/fz8AAIA///9/PwAAgD8BAIA/AACAP/7/fz8BAIA//v9/P/7/fz8AAIA///9/P/7/fz///38///9/P/7/fz8AAIA///9/P/7/fz8AAIA/AACAP/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA//v9/P/7/fz8AAIA//v9/P/7/fz8AAIA//f9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8BAIA//v9/P/7/fz8AAIA//f9/P/7/fz8AAIA//v9/P/7/fz8AAIA//v9/P/7/fz8AAIA//v9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA///9/P/7/fz8AAIA//v9/P/7/fz8AAIA/AACAPwAAgD8AAIA///9/P/7/fz8AAIA///9/P/7/fz8BAIA///9/P/7/fz8AAIA///9/PwAAgD8AAIA///9/P/7/fz8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA/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AACAPwEAgD8DAIA//f9/PwIAgD8DAIA//v9/PwEAgD8CAIA/AACAPwIAgD8AAIA/+/9/P/v/fz8DAIA//P9/PwEAgD8AAIA//P9/PwEAgD8DAIA//v9/P/7/fz8DAIA/AgCAPwIAgD8EAIA/AACAP///fz8CAIA//v9/PwEAgD8AAIA/+v9/P/v/fz8AAIA/AACAP/7/fz8DAIA//P9/P/3/fz8AAIA//P9/P/z/fz///38/AACAP///fz8AAIA/AACAPwEAgD8DAIA//f9/PwMAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8DAIA//f9/PwMAgD8FAIA//P9/PwIAgD8CAIA//P9/PwAAgD///38//P9/P///fz8CAIA//f9/PwAAgD8DAIA/AQCAPwIAgD8EAIA/AwCAPwQAgD8DAIA//P9/PwMAgD8CAIA/AwCAPwAAgD8CAIA/AwCAPwEAgD8AAIA/AQCAPwEAgD8EAIA/+v9/PwMAgD8DAIA//v9/PwMAgD8EAIA/+v9/PwMAgD8AAIA/AQCAPwEAgD8CAIA/AwCAPwEAgD8DAIA/AgCAPwIAgD8DAIA//P9/PwIAgD8EAIA/AwCAPwQAgD8CAIA/AQCAPwMAgD8CAIA//f9/PwAAgD///38//P9/P///fz8CAIA//P9/PwAAgD8FAIA//P9/PwMAgD8DAIA//f9/PwMAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8CAIA//f9/PwEAgD8AAIA//v9/PwMAgD8CAIA//P9/PwAAgD8DAIA/AACAPwIAgD8DAIA/AACAPwMAgD8CAIA/AACAPwAAgD///38//P9/PwEAgD///38/AACAPwIAgD8AAIA//P9/P/3/fz8DAIA/AACAPwMAgD/+/38/9v9/P/7/fz8CAIA/AACAPwIAgD8CAIA//f9/PwEAgD8CAIA//v9/PwIAgD8DAIA/AACAPwIAgD8CAIA/AACAPwIAgD8DAIA/AACAPwMAgD8DAIA/AwCAPwIAgD///38//P9/PwEAgD8AAIA//P9/PwIAgD8AAIA//P9/P/3/fz8DAIA//P9/PwEAgD8CAIA/AwCAPwMAgD8CAIA/AACAPwEAgD8CAIA//f9/PwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8DAIA//f9/PwMAgD8AAIA//P9/PwIAgD8CAIA/AQCAPwUAgD8AAIA//v9/PwAAgD8CAIA/AgCAP///fz8CAIA/AACAPwAAgD/+/38//P9/PwEAgD/+/38//P9/P///fz8EAIA/AACAP///fz8DAIA/AACAPwEAgD/+/38/AACAP/7/fz/9/38/+v9/P///fz8EAIA/+v9/PwEAgD8FAIA/+/9/P/r/fz8DAIA//v9/P/7/fz8DAIA/+v9/P///fz8DAIA//f9/PwIAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD/6/38//P9/P/7/fz///38//v9/PwAAgD8CAIA/AQCAP///fz///38/AACAPwAAgD8AAIA/AACAPwEAgD8AAIA//v9/PwIAgD8AAIA/AACAPwMAgD/+/38//v9/PwMAgD8AAIA/AACAPwIAgD8AAIA/AgCAPwQAgD8EAIA/AgCAPwIAgD8AAIA//v9/P///fz8AAIA/AgCAPwEAgD8CAIA//v9/PwAAgD8CAIA/AACAPwEAgD8CAIA/AACAPwEAgD/6/38//P9/P/7/fz8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8DAIA/AgCAPwIAgD8AAIA/AACAPwAAgD8CAIA/AwCAPwMAgD8AAIA//f9/PwEAgD8EAIA/AwCAPwEAgD/+/38/AACAP/7/fz8DAIA/AgCAPwEAgD/6/38//f9/P///fz8DAIA//v9/P///fz8DAIA//v9/P///fz8DAIA//v9/P///fz8DAIA//v9/P///fz8DAIA//v9/P///fz8DAIA//v9/P///fz8DAIA//v9/P///fz8DAIA//v9/P///fz8DAIA//v9/P///fz8CAIA//v9/PwAAgD8AAIA//P9/PwAAgD/+/38/AwCAP/7/fz8AAIA/AgCAP///fz///38/AACAP/7/fz8AAIA/AACAPwEAgD8AAIA//f9/P/7/fz8DAIA/AgCAPwIAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/AACAPwEAgD8EAIA/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+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/n/fz/6/38/+/9/P/j/fz/8/38/+v9/PwAAgD/5/38//P9/P/j/fz/6/38/+P9/PwAAgD///38/AgCAP/n/fz/6/38/+/9/P/v/fz/7/38/9/9/P/b/fz/4/38/9f9/P/v/fz/0/38/+P9/P/n/fz/9/38/+v9/PwIAgD8EAIA///9/P/7/fz/5/38//P9/P///fz/9/38/+/9/P/7/fz/5/38//P9/PwIAgD8EAIA///9/P/n/fz/9/38/+/9/P/r/fz/3/38/+v9/P/r/fz/4/38/+/9/P/v/fz/7/38/9/9/P/n/fz/7/38/+P9/PwEAgD///38/AgCAP/3/fz/9/38/+v9/PwAAgD/5/38//f9/P/n/fz/4/38/9f9/P/n/fz/6/38/+/9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/n/fz/5/38//f9/PwIAgD/9/38/AgCAP/v/fz/9/38//P9/P/v/fz/7/38//f9/PwAAgD/5/38//P9/PwIAgD///38///9/P/3/fz/7/38///9/P/z/fz/7/38/+v9/P///fz/8/38///9/PwAAgD///38/AgCAP/z/fz/8/38//f9/P/n/fz/9/38//f9/P/r/fz/9/38//f9/PwEAgD/7/38/AgCAP/3/fz/5/38//f9/P/3/fz/9/38/+/9/P/3/fz/2/38/+/9/PwEAgD///38///9/P/z/fz/9/38/AQCAP/n/fz/8/38/+v9/P/3/fz/4/38/+/9/P/v/fz///38/AgCAPwAAgD/9/38//f9/P/7/fz/8/38/AwCAP/n/fz/5/38//f9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/n/fz/6/38/+/9/P/z/fz/8/38/+v9/P/3/fz/6/38//P9/P/z/fz/6/38//f9/PwEAgD/7/38//f9/P/f/fz/8/38//P9/P/n/fz/6/38/+v9/P/j/fz/7/38//P9/P/7/fz/6/38/AQCAP/f/fz/6/38//f9/PwIAgD/3/38//P9/PwAAgD///38//f9/P///fz/8/38/AgCAP/v/fz/2/38//P9/P/f/fz/4/38/+P9/PwAAgD/7/38/+/9/PwEAgD/9/38//P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/PwEAgD/9/38//P9/P/n/fz/8/38/+P9/PwIAgD/9/38/+/9/P/v/fz/6/38//f9/P/3/fz/5/38//P9/P/7/fz/4/38//P9/P///fz/2/38/+/9/P/v/fz/6/38/+/9/PwMAgD/9/38/AwCAP/v/fz/5/38//f9/P/7/fz/5/38/+v9/P/f/fz/7/38/+v9/P/r/fz/8/38/+/9/P/f/fz/7/38/+v9/P/7/fz/5/38/+v9/P/v/fz/5/38//f9/PwEAgD/9/38///9/P/n/fz/7/38//P9/P///fz/2/38/+/9/P/z/fz/7/38//P9/P/3/fz/5/38//P9/P/v/fz/6/38//f9/PwEAgD///38///9/P/v/fz///38/+/9/PwEAgD/9/38//P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/v/fz/5/38/+P9/P/f/fz/9/38///9/P/7/fz/9/38//f9/PwAAgD///38/+/9/PwEAgD///38//f9/P/z/fz8BAIA/+/9/P/3/fz/2/38/+P9/PwAAgD///38//P9/P///fz/6/38///9/P///fz/5/38///9/P///fz/9/38/+P9/P/3/fz/8/38/+P9/P/v/fz/5/38/+P9/P/r/fz/7/38///9/P/3/fz/2/38/+v9/P/z/fz///38/AgCAPwEAgD///38//f9/P/v/fz///38//P9/P/3/fz/2/38/+P9/PwAAgD///38/AQCAP///fz/6/38///9/P/n/fz/6/38/+P9/P/z/fz/3/38/+/9/PwAAgD///38//f9/P/v/fz/5/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/P/3/fz/1/38/+P9/PwEAgD/9/38//P9/P///fz/9/38//f9/P/3/fz/8/38/+/9/P/X/fz/5/38/+P9/P/3/fz///38//P9/PwAAgD///38/AQCAP///fz/9/38///9/P/7/fz/5/38/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/P9/P/r/fz/+/38//f9/P/3/fz8AAIA//P9/P/r/fz/+/38/AQCAP/v/fz/8/38/AgCAP/r/fz/9/38/AQCAP/3/fz///38//P9/P/z/fz/+/38///9/P/j/fz/5/38/+v9/P/n/fz/8/38/+/9/P/3/fz8AAIA//f9/P/v/fz/+/38//f9/PwEAgD///38/AACAP///fz/5/38/AgCAP/3/fz8AAIA/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38//f9/PwEAgD///38/9f9/P/v/fz/7/38/AQCAP///fz/+/38/+/9/P/7/fz/6/38/9/9/P/3/fz/9/38//P9/PwAAgD/+/38/+v9/P/7/fz///38//P9/P/z/fz/9/38/AwCAP///fz///38///9/P/v/fz/6/38//v9/P/v/fz/5/38//f9/P/z/fz/8/38//P9/P/n/fz/6/38/+f9/PwAAgD/+/38/+P9/P///fz/8/38/AACAP/v/fz/7/38/+/9/P/7/fz8AAIA//P9/P///fz/9/38//v9/PwAAgD///38/+/9/P/7/fz8AAIA/AQCAP/7/fz8AAIA//P9/P/7/fz/7/38/9f9/P/v/fz8AAIA//f9/P///fz/9/38//f9/PwEAgD///38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AgCAP/3/fz8AAIA/AQCAP/f/fz/7/38/+f9/P/7/fz/+/38/+v9/P/7/fz/+/38//P9/P/n/fz/9/38//v9/P/n/fz/9/38/AACAP/n/fz///38/+v9/P/n/fz/9/38/AgCAP/7/fz///38/AQCAP///fz8AAIA/AwCAP/7/fz///38//P9/P///fz/9/38/AgCAP/n/fz8AAIA/AgCAP/v/fz8BAIA//P9/PwAAgD/7/38/AQCAP///fz8BAIA/+P9/P///fz/3/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/+P9/P///fz/3/38//f9/P///fz/+/38/AQCAPwAAgD///38/AACAP///fz/+/38//f9/PwAAgD/8/38/AACAP/z/fz8AAIA//v9/P/3/fz/9/38/AACAP/z/fz/9/38//f9/P///fz/7/38/+/9/P/n/fz/9/38/+f9/P/3/fz/+/38/BACAPwMAgD8AAIA/AQCAP///fz/+/38/BACAPwMAgD8AAIA/+f9/P/3/fz/+/38/+/9/P/n/fz/9/38/+P9/P/z/fz/5/38/AACAP///fz/7/38//v9/P/3/fz/9/38/AgCAP/n/fz///38//f9/PwAAgD/8/38/AACAP///fz/+/38/AQCAPwAAgD8AAIA/+v9/P/3/fz/8/38/+P9/P///fz/3/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AQCAPwAAgD///38/AACAP///fz/9/38/AACAP///fz/7/38/AQCAP/v/fz/+/38//f9/P/f/fz/8/38///9/P/v/fz/9/38/AQCAP/7/fz8AAIA//v9/PwAAgD/7/38/AACAP///fz/+/38/AgCAP/z/fz/5/38/AQCAP/7/fz/8/38/+f9/P/z/fz/6/38/AQCAPwAAgD///38//P9/P///fz/7/38//f9/PwEAgD///38/AACAP/n/fz/5/38//f9/P/f/fz/8/38/AgCAPwEAgD8BAIA/AQCAP/7/fz8AAIA///9/P///fz/7/38/AACAP///fz/+/38/+/9/P/7/fz/+/38/BACAPwEAgD///38//v9/P/v/fz/8/38/AQCAPwAAgD///38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/+P9/P///fz/3/38/BACAP///fz///38///9/P/v/fz/7/38//P9/P/z/fz/3/38///9/P/3/fz/8/38/AQCAP/7/fz/8/38/AwCAP/v/fz/+/38/+/9/P/r/fz/8/38/AgCAP/7/fz///38/AQCAP/z/fz/7/38/AQCAP/r/fz///38/+P9/P/X/fz/6/38//P9/P/n/fz8AAIA/+f9/P///fz///38///9/P/7/fz8BAIA/+v9/P/f/fz///38/AgCAP/3/fz8AAIA/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/AwCAPwAAgD/+/38/9P9/P/f/fz/6/38/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+/9/P///fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/6/38//P9/P///fz/6/38//v9/P///fz/+/38///9/P/7/fz/7/38//P9/P/3/fz/6/38/+v9/P///fz/7/38/+/9/P/z/fz/9/38//P9/P///fz/9/38/+/9/P/v/fz/8/38//P9/P/3/fz/8/38/+/9/P/z/fz/9/38/+v9/P/v/fz/8/38//f9/P/z/fz/8/38//f9/P///fz/7/38/+P9/P/z/fz/8/38//v9/P/3/fz/5/38//f9/P/3/fz/8/38//v9/P/v/fz/8/38/+/9/P/3/fz/9/38//P9/P/7/fz/5/38//f9/P/v/fz/8/38/+/9/P/v/fz/8/38//f9/P/7/fz/9/38/+v9/PwEAgD/4/38///9/P/z/fz/6/38//P9/P///fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/8/38/+/9/P///fz/9/38//f9/P/j/fz/5/38//f9/PwAAgD/6/38/+v9/PwAAgD8AAIA/+v9/P/r/fz/2/38//v9/PwAAgD/8/38/+/9/P///fz/1/38//f9/PwAAgD/2/38//v9/PwAAgD/4/38//f9/PwEAgD8DAIA/+/9/P/L/fz/4/38/+/9/P/7/fz8BAIA/8v9/P/r/fz8EAIA/8/9/P/j/fz/8/38//f9/P/3/fz8AAIA/+f9/P/3/fz///38/+v9/P/v/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz///38/+v9/P/v/fz/7/38/+f9/P/7/fz8DAIA/+v9/P/v/fz/7/38/9v9/PwEAgD/+/38//P9/P/v/fz/7/38/+v9/P/7/fz/6/38/+P9/P/7/fz/7/38/9/9/PwIAgD///38/+v9/P/r/fz/0/38/+/9/PwQAgD/4/38//P9/PwEAgD/+/38/9/9/PwAAgD/+/38/+P9/P/7/fz/+/38/9/9/PwAAgD/4/38//P9/PwEAgD/0/38/+/9/PwQAgD/2/38/+/9/PwAAgD/6/38/+v9/PwEAgD/6/38/+P9/P/7/fz8AAIA/+/9/P/7/fz/+/38//P9/P/v/fz/7/38/9v9/PwEAgD8AAIA//P9/P///fz/7/38/+v9/PwAAgD///38/+v9/P/v/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/+/38/+f9/PwEAgD/t/38/+v9/PwUAgD8AAIA/9/9/P/7/fz8DAIA/9/9/P/b/fz/+/38/9/9/P///fz/8/38/9P9/P/3/fz/7/38/+P9/PwEAgD/4/38/+/9/PwAAgD///38//f9/P/z/fz8BAIA/9v9/P/z/fz8CAIA/9v9/P/r/fz/7/38/+f9/PwEAgD/+/38/+f9/PwEAgD/y/38/+v9/PwQAgD/+/38/+v9/P///fz/6/38//f9/PwAAgD/+/38/9/9/P///fz/4/38/9v9/PwAAgD/7/38/+P9/PwEAgD/6/38/+P9/PwIAgD///38//f9/P/z/fz/8/38/+P9/P///fz/7/38/+f9/PwEAgD///38//f9/P/n/fz/+/38/+f9/PwEAgD/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz///38/+v9/P/v/fz8AAIA/+f9/P/r/fz/8/38/+f9/PwAAgD///38//f9/P/v/fz/9/38//P9/P/r/fz///38//P9/P/v/fz/9/38//f9/P/r/fz/8/38//P9/P///fz/2/38//v9/PwAAgD/2/38/+/9/P///fz/6/38/+v9/P///fz/6/38/+f9/PwEAgD/1/38//f9/PwIAgD/7/38/+/9/P/z/fz/7/38/AQCAP/v/fz///38//f9/P/z/fz/8/38/+/9/P///fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz///38/9/9/PwAAgD8CAIA/8/9/P/z/fz/7/38///9/P/j/fz/1/38/AgCAP/v/fz/8/38/AgCAP/v/fz/6/38//v9/P/n/fz/+/38/+v9/P/3/fz8BAIA/9v9/P/7/fz8AAIA/9/9/P/v/fz/9/38//f9/P/r/fz/+/38/+v9/P/3/fz8AAIA/8/9/PwEAgD/6/38/+v9/PwEAgD8BAIA//f9/P/n/fz8AAIA/+v9/P/f/fz8BAIA/8v9/P/3/fz///38/9/9/PwAAgD/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38/+/9/P/7/fz/7/38//f9/P/v/fz/5/38/+f9/PwEAgD/5/38/+f9/PwAAgD/7/38//P9/P/z/fz/9/38/+/9/PwAAgD/8/38//f9/P/7/fz/8/38/+P9/P/7/fz/8/38//P9/P/7/fz/8/38//P9/P/7/fz/8/38//P9/P/7/fz/8/38//P9/P/7/fz/8/38//P9/P/7/fz/8/38//P9/P/7/fz/8/38//P9/P/7/fz/8/38//P9/P/7/fz/8/38//P9/P/7/fz/9/38/+v9/P/3/fz/7/38/+v9/P/7/fz/9/38///9/P/n/fz/6/38/+v9/P/3/fz/9/38/+/9/P/3/fz/9/38/+v9/PwEAgD/9/38/+/9/P/7/fz/9/38/+/9/P/7/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/9/38///9/P/3/fz/iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iikS0jPc2vyb3iL9iiii0jPcyvyb3iL9iijS0jPcuvyb3iL9iiiy0jPc2vyb3iL9iijS0jPcuvyb3iL9iiii0jPcyvyb3iL9iikS0jPc2vyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iikS0jPc2vyb3iL9iiii0jPcyvyb3iL9iijS0jPcuvyb3iL9iiiy0jPc2vyb3iL9iijS0jPcuvyb3iL9iiii0jPcyvyb3iL9iikS0jPc2vyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijy0jPcuvyb3iL9iiiy0jPcyvyb3iL9iiiy0jPcyvyb3iL9iijS0jPc2vyb3iL9iiii0jPcyvyb3iL9iiji0jPcuvyb3iL9iiiy0jPc2vyb3iL9iiii0jPcyvyb3iL9iiiy0jPc2vyb3iL9iiji0jPcuvyb3iL9iiii0jPcyvyb3iL9iijS0jPc2vyb3iL9iiiy0jPcyvyb3iL9iiiy0jPcyvyb3iL9iijy0jPcuvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iikS0jPc2vyb3iL9iiii0jPcyvyb3iL9iijC0jPcyvyb3iL9iiiy0jPc2vyb3iL9iiiC0jPcyvyb3iL9iiii0jPcyvyb3iL9iikS0jPc2vyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iikS0jPcyvyb3iL9iiii0jPcyvyb3iL9iijS0jPcuvyb3iL9iiiy0jPc2vyb3iL9iiiS0jPcuvyb3iL9iiii0jPcyvyb3iL9iikS0jPc2vyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijy0jPcuvyb3iL9iiiy0jPcyvyb3iL9iijy0jPcyvyb3iL9iikC0jPc2vyb3iL9iijS0jPcyvyb3iL9iiiS0jPcqvyb3iL9iijS0jPc2vyb3iL9iiii0jPcyvyb3iL9iiiy0jPc2vyb3iL9iiiy0jPcqvyb3iL9iijS0jPcyvyb3iL9iijS0jPc2vyb3iL9iiiy0jPc2vyb3iL9iiiy0jPcuvyb3iL9iijy0jPcuvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3hL9iijS0jPcuvyb3iL9iijy0jPcyvyb3iL9iiii0jPcyvyb3iL9iijC0jPcqvyb3iL9iiji0jPc2vyb3iL9iiji0jPcyvyb3iL9iikC0jPcyvyb3iL9iiii0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3kL9iijC0jPcyvyb3gL9iiiS0jPcyvyb3gL9iijC0jPcmvyb3hL9iijS0jPcuvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iiiS0jPcyvyb3iL9iijC0jPcuvyb3iL9iiiy0jPcyvyb3iL9iijS0jPcuvyb3iL9iihi0jPcuvyb3iL9iiii0jPcyvyb3iL9iijS0jPcyvyb3iL9iiii0jPc2vyb3iL9iihi0jPcyvyb3iL9iihi0jPcyvyb3iL9iihi0jPcyvyb3iL9iihi0jPcyvyb3iL9iihi0jPcyvyb3iL9iihi0jPcyvyb3iL9iihi0jPcyvyb3iL9iihi0jPcyvyb3iL9iihi0jPcyvyb3iL9iiii0jPc2vyb3iL9iiiS0jPcuvyb3iL9iihi0jPcyvyb3iL9iihi0jPcuvyb3iL9iiiS0jPcuvyb3iL9iiiy0jPcyvyb3iL9iiji0jPcuvyb3iL9iiiS0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb3iL9iijC0jPcyvyb30BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1P/QENb8AAAAAAAAAAPQENT/0BDW/AAAAAAAAAAD0BDU/9AQ1vwAAAAAAAAAA9AQ1PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP7Fmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9QMV4PbFmjj3kEEs9PxtzPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9yaF0PbFmjj3kEEs9L795PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9e7mCPbBmjj3uEEs9fbmCPbBmjj3eEEs9fLmCPbFmjj3kEEs9e7mCPbBmjj3gEEs9e7mCPbFmjj3kEEs9e7mCPbBmjj3fEEs948R3PbBmjj3mEEs9FvhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9qsV9PbBmjj3mEEs9FvhwPbBmjj3gEEs9FvhwPbFmjj3kEEs9F/hwPbBmjj3oEEs9FPhwPbFmjj3kEEs9F/hwPbBmjj3eEEs9lExzPbBmjj3uEEs9fbmCPbFmjj3kEEs9e7mCPbFmjj3kEEs9e7mCPbFmjj3kEEs9e7mCPbFmjj3kEEs9e7mCPbFmjj3kEEs9e7mCPbFmjj3kEEs9YmVzPbFmjj3kEEs93mt8PbFmjj3kEEs9ewl7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3pEEs9FfhwPbBmjj3jEEs9FfhwPbFmjj3jEEs9FPhwPbBmjj3jEEs9FvhwPbFmjj3eEEs9FPhwPbJmjj3ZEEs9FfhwPbBmjj3iEEs9FvhwPbBmjj3iEEs9FfhwPbBmjj3iEEs9FvhwPbJmjj3ZEEs9FfhwPbFmjj3eEEs9FPhwPbBmjj3jEEs9F/hwPbFmjj3jEEs9FPhwPbBmjj3jEEs9FfhwPbFmjj3oEEs9FPhwPbFmjj3kEEs91659PbFmjj3kEEs9gNh6PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9P8V4PbFmjj3kEEs9PxtzPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9FfhwPbFmjj3kEEs9yaF0PbFmjj3kEEs9L795PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9hjV7PbFmjj3kEEs9e7mCPbBmjj3uEEs9fbmCPbBmjj3eEEs9fLmCPbFmjj3kEEs9e7mCPbBmjj3gEEs9e7mCPbFmjj3kEEs9e7mCPbBmjj3fEEs9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G514PVfKcT91L66+F4niPeCjqj3pGm4/OkS7vm1kpj3PEtg9j89rP3v9vr6RNzo99dL/PVISaz8slb6+kGvRPDoPCT5vB2s/wYCOPcmfkbuV0DG6f2B/PyXtJ7xvnYq7scRROvX7fz96UtI6C/mJuxN5KzpU/38/rmFZuRsKirtdwzE6Z/9/P/UMFzijB4q7u+wwOmf/fz8x7QM4sweKu93wMDpn/38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAP///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38///9/P///fz///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD///38/AACAP///fz8AAIA///9/P///fz///38/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwEAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD///38/AACAP///fz8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38///9/PwAAgD8AAIA/AACAP///fz8BAIA///9/PwAAgD8AAIA/AACAP/3/fz/+/38///9/P/3/fz/8/38/AACAPwEAgD8BAIA/AACAP///fz///38/AACAPwEAgD8BAIA/AACAP/3/fz/+/38///9/P/7/fz/+/38///9/PwAAgD8AAIA/AACAPwAAgD///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA///9/P///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAP///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38///9/P///fz///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAPwEAgD8BAIA/AACAP///fz8AAIA///9/PwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA///9/P///fz///38/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA///9/PwEAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD///38/AACAP///fz8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAP/3/fz/9/38/AACAP/7/fz/8/38/AACAPwEAgD8BAIA/AACAP///fz///38/AACAPwEAgD8BAIA/AACAP/7/fz/+/38/AACAP/3/fz/+/38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz/+/38/AACAPwAAgD8AAIA///9/P///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAPwEAgD8CAIA/AACAP///fz8AAIA/AACAPwAAgD///38///9/P///fz8AAIA/AACAPwAAgD8AAIA///9/P/7/fz///38/AACAP///fz///38/AACAPwAAgD/+/38/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAP///fz///38/AACAPwEAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP/3/fz/+/38///9/PwEAgD8BAIA///9/PwAAgD8AAIA/AACAPwEAgD8BAIA/AACAPwEAgD8BAIA/AACAPwEAgD8BAIA///9/PwEAgD8BAIA/AACAPwEAgD8CAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwEAgD8BAIA/AACAPwEAgD8BAIA/AACAPwEAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA///9/PwEAgD8BAIA/AACAP/3/fz/9/38/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m6LCOdL/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz8AAIA/Gv9/Pxv/fz///38//f9/P/7/fz8AAIA/AQCAPwIAgD8AAIA/AACAPwAAgD8AAIA///9/P/7/fz8BAIA/AgCAPwIAgD8AAIA///9/PwAAgD8AAIA/AQCAPwAAgD8BAIA/AgCAPwEAgD8BAIA//v9/P/3/fz8AAIA///9/P///fz///38//f9/P/7/fz8AAIA/AACAPwEAgD/+/38///9/PwEAgD/+/38/AACAPwAAgD///38/AACAPwAAgD///38//f9/P/7/fz8AAIA//v9/P/3/fz/+/38/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD8BAIA/ev9/P3r/fz///38/tv9/P7b/fz///38/t/9/P7f/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz8BAIA/AwCAPwMAgD/+/38///9/PwEAgD8AAIA/AwCAPwMAgD8AAIA/AwCAPwEAgD///38///9/PwAAgD///38/AwCAPwIAgD8AAIA/AgCAPwMAgD8AAIA/AACAPwAAgD///38/AACAPwAAgD/+/38//v9/P/7/fz8CAIA/AQCAP/7/fz/+/38/+/9/P/r/fz8BAIA/E/9/PxP/fz///38/AwCAPwIAgD8AAIA/AACAPwAAgD/9/38/AQCAPwEAgD8BAIA///9/P///fz8AAIA/AQCAP///fz/+/38/AQCAPwIAgD/+/38/AACAPwEAgD///38//v9/P/7/fz8BAIA//P9/P/z/fz8AAIA/AQCAPwEAgD8BAIA/AQCAPwAAgD///38/AQCAPwIAgD8AAIA/AACAPwAAgD8BAIA/kv9/P5H/fz/+/38/s/9/P7X/fz///38/tv9/P7b/fz///38/t/9/P7f/fz///38/tv9/P7b/fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38//v9/P/7/fz///38/AQCAPwAAgD///38//f9/P/z/fz8BAIA/AQCAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwEAgD///38//P9/P/3/fz/9/38/+/9/P/3/fz///38/AACAP///fz8BAIA/AACAP///fz8AAIA//v9/P/7/fz8AAIA/AgCAPwIAgD8BAIA/Hf9/Px3/fz8AAIA/sv9/P7L/fz///38/tv9/P7b/fz///38/t/9/P7f/fz///38/tv9/P7b/fz8AAIA/Gv9/Pxv/fz///38//f9/P/7/fz8AAIA/AQCAPwIAgD8AAIA/AACAPwAAgD8AAIA///9/P/7/fz8BAIA/AgCAPwIAgD8AAIA///9/PwAAgD8AAIA/AQCAPwAAgD8BAIA/AgCAPwEAgD8BAIA//v9/P/3/fz8AAIA///9/P///fz///38//f9/P/7/fz8AAIA/AACAPwEAgD/+/38///9/PwEAgD/+/38/AACAPwAAgD///38/AACAPwAAgD///38//f9/P/7/fz8AAIA//v9/P/3/fz/+/38/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD8BAIA/ev9/P3r/fz///38/tv9/P7b/fz///38/t/9/P7f/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz///38/tv9/P7b/fz8BAIA/AwCAPwMAgD/9/38/AQCAPwEAgD///38/AgCAPwIAgD/9/38/AACAPwAAgD///38///9/PwAAgD8CAIA/BQCAPwQAgD8AAIA/AQCAPwEAgD8AAIA//v9/P///fz///38/AACAPwAAgD/+/38//v9/P/7/fz8CAIA/AQCAP///fz/+/38/+/9/P/r/fz8BAIA/E/9/PxP/fz///38/AACAPwAAgD8BAIA//v9/P/7/fz/9/38/AACAPwAAgD8BAIA///9/P///fz8AAIA/AACAP///fz/+/38/AQCAPwIAgD/+/38/AACAPwAAgD///38//v9/P/7/fz8BAIA/AQCAP///fz8AAIA///9/PwAAgD8BAIA/AACAPwAAgD///38/AQCAPwIAgD8AAIA/AACAPwAAgD8BAIA/kv9/P5H/fz/+/38/s/9/P7X/fz///38/tv9/P7b/fz///38/t/9/P7f/fz///38/tv9/P7b/fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwEAgD///38/AQCAPwEAgD8BAIA//v9/P/7/fz8AAIA/AACAP///fz8CAIA/AACAPwEAgD8AAIA/AACAP///fz///38/AQCAPwEAgD/+/38//f9/P/3/fz///38///9/P/7/fz///38/AACAP///fz8BAIA/AQCAPwAAgD8AAIA/AQCAPwIAgD8AAIA/AACAPwEAgD8BAIA/Hf9/Px3/fz8AAIA/sv9/P7L/fz///38/tv9/P7b/fz///38/t/9/P7f/fz///38/AgCAPwcAgD8AAIA/AwCAPwMAgD8BAIA/AgCAPwEAgD8AAIA/AwCAPwIAgD///38///9/PwEAgD/9/38/AACAPwEAgD8AAIA/AQCAPwEAgD8AAIA//P9/P/3/fz/+/38/AACAPwEAgD8BAIA/AQCAPwAAgD8CAIA///9/P/7/fz8AAIA/AgCAPwIAgD8BAIA/AwCAPwIAgD/+/38///9/PwAAgD8BAIA/AACAP/7/fz8BAIA/AwCAPwQAgD8AAIA//P9/PwAAgD8BAIA/AACAP///fz///38/ov9/P6L/fz/+/38/tf9/P7X/fz8AAIA/AQCAPwAAgD8BAIA/AgCAPwIAgD///38//v9/P///fz8BAIA/AgCAPwAAgD///38/AACAP/7/fz8AAIA/AQCAPwEAgD///38///9/P///fz/9/38/AgCAPwEAgD///38/AACAPwAAgD/+/38/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA///9/P///fz///38/AQCAPwEAgD/+/38/AQCAPwIAgD/9/38//P9/P/z/fz/9/38//P9/P/z/fz/9/38//P9/P/z/fz/8/38/AACAPwAAgD/9/38//v9/P/3/fz///38/AACAPwEAgD/+/38//f9/P/3/fz///38//v9/P///fz8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD///38//v9/P/3/fz8BAIA/AACAPwAAgD8AAIA/Bf5/PwX+fz8AAIA/qv9/P6v/fz8BAIA/uP9/P7j/fz///38/tv9/P7b/fz///38/t/9/P7f/fz8E34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPJJUV7kE34e9r2eyPJJUV7kE34e9r2eyPJJUV7kE34e9r2eyPBFVV7kE34e9r2eyPJJUV7kF34e9sGeyPJJUV7kE34e9r2eyPJJUV7kE34e9t2eyPLVVV7kE34e9r2eyPJJUV7kE34e9r2eyPJJUV7kE34e9r2eyPJJUV7kF34e9sGeyPBFVV7kE34e9r2eyPJJUV7kE34e9r2eyPJJUV7kE34e9r2eyPJJUV7kE34e9t2eyPLVVV7kE34e9r2eyPJJUV7kF34e9sGeyPJJUV7kE34e9r2eyPJJUV7kE34e9r2eyPBFVV7kE34e9r2eyPJJUV7kE34e9r2eyPJJUV7kE34e9r2eyPJJUV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9zGeyPI9VV7kF34e9omeyPGlUV7kE34e9smeyPPlUV7kE34e9s2eyPLZUV7kE34e9smeyPP1UV7kF34e9omeyPOpTV7kE34e9zGeyPJBVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9zGeyPI9VV7kF34e9omeyPGlUV7kE34e9smeyPPlUV7kE34e9s2eyPLZUV7kE34e9smeyPP1UV7kF34e9omeyPOpTV7kE34e9zGeyPJBVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9r2eyPBFVV7kE34e9sWeyPMlUV7kE34e9smeyPDxUV7kE34e9rWeyPPpUV7kE34e9q2eyPPpUV7kF34e9tWeyPAVVV7kE34e9smeyPKBUV7kF34e9r2eyPL5UV7kE34e9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HACAPxQAgD8CAIA/HACAPxQAgD8CAIA/HACAPxMAgD8BAIA/HACAPxQAgD8BAIA/HACAPxMAgD8BAIA/HACAPxMAgD8BAIA/HACAPxQAgD8AAIA/HACAPxQAgD8AAIA/HQCAPxMAgD8BAIA/HACAPxMAgD8BAIA/HACAPxMAgD8BAIA/HACAPxQAgD8AAIA/HQCAPxQAgD8AAIA/HACAPxMAgD8BAIA/HACAPxMAgD8BAIA/HACAPxQAgD8BAIA/HACAPxMAgD8BAIA/HACAPxQAgD8CAIA/HACAPxQAgD8CAIA/HACAPxQAgD8CAIA/HACAPxMAgD8DAIA/HACAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxQAgD8FAIA/HACAPxMAgD8BAIA/GwCAPxUAgD/+/38/HACAPxUAgD8BAIA/GgCAPxUAgD8BAIA/GgCAPxQAgD8CAIA/HACAPxQAgD8HAIA/HACAPxQAgD8EAIA/HACAPxMAgD8DAIA/HACAPxQAgD8DAIA/HACAPxQAgD8FAIA/GwCAPxMAgD8GAIA/GgCAPxQAgD8JAIA/GgCAPxMAgD8GAIA/GgCAPxQAgD8DAIA/GQCAPxMAgD8EAIA/FwCAPxMAgD8FAIA/GACAPxMAgD8GAIA/GQCAPxQAgD8IAIA/GgCAPxQAgD8DAIA/GgCAPxMAgD8CAIA/HACAPxUAgD8CAIA/HACAPxMAgD8CAIA/HACAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxQAgD8FAIA/HACAPxQAgD/+/38/HACAPxMAgD8CAIA/HACAPxMAgD8CAIA/HACAPxQAgD/8/38/GACAPxMAgD/7/38/FwCAPxQAgD/7/38/GgCAPxQAgD/7/38/GgCAPxQAgD/6/38/GgCAPxMAgD/7/38/FwCAPxQAgD/9/38/GgCAPxMAgD8CAIA/HQCAPxMAgD8CAIA/HACAPxQAgD/+/38/HACAPxQAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8DAIA/HACAPxQAgD8CAIA/HACAPxQAgD8CAIA/HACAPxQAgD8CAIA/HACAPxMAgD8BAIA/HACAPxQAgD8BAIA/HACAPxMAgD8BAIA/HACAPxMAgD8BAIA/HACAPxQAgD8AAIA/HACAPxQAgD8AAIA/HQCAPxMAgD8BAIA/HACAPxMAgD8BAIA/HACAPxMAgD8BAIA/HACAPxQAgD8AAIA/HQCAPxQAgD8AAIA/HACAPxMAgD8BAIA/HACAPxMAgD8BAIA/HACAPxQAgD8BAIA/HACAPxMAgD8BAIA/HACAPxQAgD8CAIA/HACAPxQAgD8CAIA/HACAPxQAgD8CAIA/HACAPxMAgD8DAIA/HACAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxQAgD8FAIA/HACAPxMAgD8BAIA/HACAPxUAgD8AAIA/HACAPxUAgD8BAIA/GgCAPxUAgD8BAIA/GwCAPxQAgD8CAIA/HACAPxMAgD8HAIA/HACAPxQAgD8EAIA/HACAPxMAgD8CAIA/HQCAPxQAgD8DAIA/HACAPxQAgD8FAIA/GgCAPxMAgD8GAIA/GgCAPxQAgD8JAIA/GgCAPxMAgD8GAIA/GgCAPxUAgD8DAIA/GQCAPxMAgD8EAIA/FwCAPxQAgD8EAIA/GACAPxIAgD8GAIA/GACAPxQAgD8IAIA/GgCAPxQAgD8DAIA/GgCAPxIAgD8CAIA/HACAPxUAgD8BAIA/HACAPxMAgD8CAIA/HACAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxQAgD8FAIA/HACAPxQAgD/+/38/HACAPxQAgD8CAIA/HACAPxMAgD8CAIA/HACAPxQAgD/9/38/GQCAPxQAgD/6/38/GACAPxQAgD/6/38/GgCAPxQAgD/7/38/GgCAPxQAgD/7/38/GQCAPxMAgD/5/38/GACAPxQAgD/9/38/GgCAPxMAgD8CAIA/HQCAPxMAgD8DAIA/HACAPxMAgD8AAIA/HQCAPxQAgD8DAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8AAIA/HACAPxMAgD8BAIA/HACAPxQAgD8CAIA/GgCAPxIAgD/5/38/GACAPxMAgD/1/38/HQCAPxQAgD/9/38/HQCAPxMAgD/6/38/HwCAPxMAgD/1/38/IACAPxQAgD/0/38/IgCAPxQAgD/0/38/IgCAPxQAgD/0/38/IACAPxQAgD/5/38/HwCAPxIAgD8BAIA/HACAPxMAgD8BAIA/GQCAPxMAgD8AAIA/GQCAPxMAgD8BAIA/HACAPxMAgD8AAIA/HACAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxQAgD8AAIA/GgCAPxUAgD8BAIA/GwCAPxQAgD8CAIA/GwCAPxMAgD8CAIA/HACAPxMAgD8AAIA/GgCAPxQAgD8AAIA/HACAPxQAgD8DAIA/HACAPxMAgD8BAIA/HACAPxMAgD8AAIA/HACAPxMAgD8AAIA/HACAPxMAgD8AAIA/HACAPxMAgD8AAIA/HACAPxMAgD8AAIA/HACAPxMAgD8AAIA/HACAPxMAgD8AAIA/HACAPxMAgD8AAIA/HACAPxMAgD8AAIA/HACAPxMAgD8BAIA/HACAPxQAgD8DAIA/HACAPxQAgD8AAIA/HACAPxMAgD8AAIA/GgCAPxQAgD8CAIA/HACAPxQAgD8CAIA/GwCAPxQAgD8BAIA/GwCAPxQAgD8AAIA/GgCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAPxMAgD8CAIA/GwCAP5GtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MJGtszHbCqQ9KPc4MChLbbLcCqQ9/dFlL+OzCzLcCqQ9/BQKMZ3qpbLbCqQ9rueFsYeopDHfCqQ9qJkFsrU5lTHbCqQ90EK0sIveqjHfCqQ9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/v9/P/v/fz8DAIA/+/9/P/7/fz8CAIA/+/9/P/3/fz8AAIA//v9/PwAAgD8AAIA///9/PwAAgD8CAIA//f9/PwAAgD/9/38/AACAP///fz/+/38//v9/PwAAgD///38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38/+/9/P/n/fz8AAIA/AACAPwAAgD8IAIA/+/9/P/j/fz8KAIA///9/P/P/fz8IAIA//f9/P/n/fz8IAIA/AACAPwIAgD8JAIA/+/9/P/r/fz///38/+/9/P/v/fz/3/38//P9/P/7/fz/9/38/+v9/P/n/fz8DAIA/+/9/P/L/fz8DAIA//f9/P/H/fz8GAIA//P9/P/P/fz8DAIA///9/P/L/fz8KAIA//f9/P+7/fz8LAIA///9/P+n/fz8JAIA//v9/P+z/fz8KAIA///9/P/H/fz8LAIA/+/9/P+3/fz8EAIA//P9/P/X/fz8GAIA/+/9/P/b/fz8EAIA//v9/PwAAgD8DAIA//v9/P///fz///38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38//f9/PwAAgD8DAIA/+v9/PwAAgD8GAIA/+v9/P/n/fz8EAIA///9/P/n/fz8HAIA//v9/P/H/fz8JAIA//P9/P+f/fz8JAIA//f9/P+L/fz8MAIA///9/P+L/fz8PAIA///9/P+L/fz8LAIA//P9/P+f/fz8JAIA//v9/P/P/fz8JAIA//v9/P/b/fz8HAIA/+v9/P/n/fz8EAIA/+v9/PwAAgD8GAIA/9/9/PwAAgD8AAIA///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38//v9/PwAAgD///38/AACAP///fz/+/38//f9/PwAAgD/9/38///9/P///fz///38//v9/PwAAgD8AAIA/+/9/P/3/fz8AAIA/+/9/P/7/fz8CAIA//v9/P/v/fz8DAIA//P9/P/3/fz8DAIA//f9/P/7/fz8DAIA/+v9/P/v/fz8DAIA//P9/P/v/fz8DAIA/+v9/P/v/fz8DAIA//f9/P/7/fz8DAIA//P9/P/3/fz8DAIA//v9/P/v/fz8DAIA/+/9/P/7/fz8CAIA/+/9/P/3/fz8AAIA//v9/PwAAgD8AAIA///9/PwAAgD8CAIA//f9/PwAAgD/9/38/AACAP///fz/+/38//v9/PwAAgD///38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38//P9/P/3/fz8CAIA/AQCAPwEAgD8JAIA/+/9/P/b/fz8IAIA///9/P/P/fz8IAIA//v9/P/f/fz8IAIA/AACAPwIAgD8IAIA//P9/P/v/fz///38/+/9/P/v/fz/3/38//f9/P///fz/8/38/+v9/P/n/fz8DAIA//f9/P/X/fz8HAIA//f9/P/H/fz8GAIA//f9/P/L/fz8DAIA///9/P/L/fz8KAIA//P9/P+3/fz8LAIA///9/P+n/fz8JAIA///9/P+v/fz8LAIA//v9/P/D/fz8LAIA//P9/P/H/fz8EAIA//P9/P/X/fz8GAIA/+v9/P/X/fz8DAIA//v9/P///fz8DAIA//v9/P/7/fz/+/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38//f9/PwAAgD8DAIA/+v9/PwAAgD8GAIA/+f9/P/n/fz8EAIA//v9/P/n/fz8IAIA//f9/P/D/fz8KAIA//P9/P+b/fz8JAIA//f9/P+T/fz8LAIA///9/P+L/fz8PAIA//P9/P+D/fz8LAIA//P9/P+f/fz8KAIA//P9/P/H/fz8KAIA//v9/P/b/fz8HAIA//P9/P/v/fz8EAIA/+f9/PwAAgD8EAIA/9/9/PwAAgD8AAIA///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38/+v9/PwAAgD8DAIA//f9/P/7/fz8GAIA///9/P/j/fz8JAIA//f9/P+r/fz8KAIA///9/P8//fz8QAIA//f9/P9//fz8GAIA//v9/P+v/fz8AAIA/AACAP/j/fz8DAIA///9/P/v/fz///38//P9/P/X/fz8DAIA//f9/P+v/fz8DAIA/+/9/P+z/fz8GAIA///9/P/n/fz8EAIA//f9/PwAAgD///38/AACAPwIAgD8GAIA//v9/PwEAgD8EAIA/+v9/PwAAgD8DAIA///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38//f9/P93/fz8OAIA//P9/P+D/fz8OAIA///9/P+L/fz8JAIA//P9/P+X/fz8HAIA//f9/P/P/fz8IAIA/+/9/P/b/fz8EAIA///9/PwAAgD8HAIA///9/P///fz8DAIA//v9/PwIAgD8HAIA//v9/PwIAgD8HAIA//v9/PwIAgD8HAIA//v9/PwIAgD8HAIA//v9/PwIAgD8HAIA//v9/PwIAgD8HAIA//v9/PwIAgD8HAIA//v9/PwIAgD8HAIA//v9/PwIAgD8HAIA//v9/P/3/fz8DAIA//v9/P///fz8GAIA//f9/P/L/fz8EAIA//P9/P/P/fz8IAIA//P9/P+D/fz8GAIA///9/P+L/fz8JAIA///9/P+T/fz8OAIA//f9/P93/fz8OAIA///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38///9/P/7/fz/8/38/30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8w30cgspHwUzzQKK8wIenCMZfwUzw8id8wlTKXMZbwUzw/Dravif6YL5/wUzw1RAcv7qAqsZvwUzx+sMCw7CQXsorwUzwgYysxCeKXMJnwUzxhY08xu/pGsp/wUzzMW8uvT7RcMZ3wUzwyAFcxxydcMpLwUzw6nvCvrvKkr5XwUzzecisxcKSnMYrwUzwGK/Ewu7/gsZjwUzy6GSuxcKSnMYrwUzwGK/EwrvKkr5XwUzzecisxxydcMpLwUzw6nvCvT7RcMZ3wUzwyAFcxu/pGsp/wUzzMW8uvCeKXMJnwUzxhY08x7CQXsorwUzwgYysx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/v9/P/z/fz/9/38//v9/P/z/fz/8/38/+/9/PwIAgD/7/38/+v9/PwcAgD/x/38/7v9/PwQAgD/5/38/9P9/PwgAgD/2/38/8/9/PwMAgD/4/38/+/9/PwgAgD/7/38/+v9/P/n/fz/7/38//P9/P/f/fz/6/38///9/P/z/fz/6/38///9/PwUAgD/3/38/8/9/PwQAgD/5/38/9P9/PwYAgD/3/38/7/9/PwMAgD/0/38/7v9/PwoAgD/2/38/6f9/PwYAgD/x/38/6f9/PwsAgD/0/38/5/9/PwYAgD/6/38/6v9/PwwAgD/4/38/7P9/PwYAgD/2/38/9v9/PwYAgD/9/38/+P9/PwcAgD/3/38//P9/PwQAgD/8/38/AACAPwEAgD/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/7/38/+/9/PwQAgD/9/38/+/9/PwUAgD/5/38/9P9/PwMAgD/7/38/9f9/PwYAgD/1/38/6/9/PwIAgD/3/38/4f9/PwsAgD/0/38/3/9/PwoAgD/5/38/3P9/PxAAgD/0/38/4P9/PwwAgD/3/38/4f9/PwsAgD/2/38/7P9/PwMAgD/8/38/9P9/PwYAgD/5/38/9P9/PwMAgD/9/38/+/9/PwUAgD/+/38/AQCAPwUAgD/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/+/38///9/P/7/fz/7/38//v9/P/7/fz/7/38//v9/PwAAgD/9/38//v9/PwIAgD/5/38//f9/PwIAgD/9/38///9/PwUAgD/8/38///9/PwUAgD/4/38///9/PwQAgD/5/38//P9/PwMAgD/5/38//f9/PwQAgD/3/38//v9/PwUAgD/2/38//f9/PwQAgD/3/38//v9/PwUAgD/5/38//f9/PwQAgD/5/38//P9/PwMAgD/4/38///9/PwQAgD/8/38///9/PwUAgD/9/38///9/PwUAgD/5/38//f9/PwIAgD/8/38///9/PwEAgD/7/38//v9/PwAAgD/7/38//v9/P/7/fz/+/38///9/P/7/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//f9/PwMAgD/6/38/+P9/PwgAgD/0/38/7/9/PwMAgD/5/38/9P9/PwgAgD/3/38/8v9/PwQAgD/3/38/+/9/PwYAgD/8/38//P9/P/z/fz/7/38//P9/P/f/fz/8/38//P9/P/3/fz/6/38///9/PwUAgD/4/38/9P9/PwgAgD/5/38/9P9/PwYAgD/4/38/8P9/PwUAgD/0/38/7v9/PwoAgD/2/38/6P9/PwYAgD/x/38/6f9/PwsAgD/x/38/4f9/PwkAgD/5/38/6v9/Pw0AgD/2/38/7v9/PwUAgD/2/38/9v9/PwYAgD/7/38/+v9/PwUAgD/4/38//f9/PwQAgD/7/38///9/PwMAgD/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/7/38/+/9/PwQAgD/9/38/+/9/PwUAgD/3/38/9v9/PwIAgD/6/38/7/9/PwEAgD/3/38/6/9/PwYAgD/1/38/4v9/PwsAgD/0/38/4P9/PwsAgD/5/38/3P9/PxAAgD/0/38/3v9/PwwAgD/1/38/4f9/PwsAgD/4/38/7P9/PwYAgD/8/38/9P9/PwYAgD/4/38/+P9/PwQAgD/9/38//P9/PwUAgD/+/38/AgCAPwUAgD/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38//v9/P/z/fz/0/38/+/9/PwQAgD/5/38/9f9/PwMAgD/3/38/+/9/PwYAgD/z/38/8/9/PwQAgD/z/38/yv9/Px0AgD/1/38/3f9/PxUAgD/2/38/7P9/PwkAgD/4/38/+f9/PwgAgD/6/38/AACAPwUAgD/3/38/9v9/PwcAgD/2/38/7P9/PwwAgD/1/38/8P9/PwoAgD/5/38//f9/PwcAgD/5/38//P9/PwUAgD/2/38/+P9/PwAAgD/9/38/+/9/PwQAgD/0/38/+/9/PwQAgD/9/38//v9/P/z/fz/9/38//v9/P/z/fz/9/38/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FACAPwAAgD/1/38/FQCAP/b/fz/u/38/GwCAP/D/fz/4/38/GwCAP/D/fz/3/38/HACAP+z/fz/y/38/HQCAP/P/fz/p/38/HwCAP///fz/p/38/HACAPwIAgD/p/38/FACAPwUAgD/x/38/EQCAPwUAgD8AAIA/CgCAPwUAgD8IAIA/AwCAPwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwYAgD8JAIA/AQCAPwcAgD8AAIA/CQCAPwgAgD/6/38/CwCAPwkAgD/z/38/CwCAPwwAgD/3/38/CgCAPwwAgD/4/38/CACAPw4AgD/2/38/BgCAPxAAgD/6/38/BQCAPwwAgD/4/38/BACAPwwAgD/4/38/CACAPwsAgD/2/38/CgCAPwkAgD/2/38/DACAPwgAgD/6/38/CwCAPwcAgD8AAIA/CQCAPwUAgD8IAIA/AACAPwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8LAIA//f9/PwQAgD8JAIA/AACAPwcAgD8HAIA/AgCAPwUAgD8EAIA/BACAPwUAgD8DAIA/BgCAPwYAgD8DAIA/BwCAPwYAgD8BAIA/BwCAPwYAgD8AAIA/CQCAPwYAgD/9/38/CACAPwcAgD///38/CQCAPwcAgD8AAIA/CQCAPwcAgD///38/CQCAPwYAgD/9/38/CACAPwYAgD8AAIA/CQCAPwYAgD8BAIA/BwCAPwYAgD8DAIA/BwCAPwUAgD8DAIA/BgCAPwUAgD8EAIA/BACAPwYAgD8HAIA/AQCAPwQAgD8JAIA/AACAPwUAgD8LAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8MAIA//f9/PwUAgD8DAIA/BACAPwgAgD/5/38/CwCAPwgAgD/2/38/DQCAPwsAgD/2/38/CwCAPwoAgD/y/38/CwCAPwgAgD/w/38/DACAPwgAgD8BAIA/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wNwLM+CYQbKW7HE/pG+nPr3cCzPwmEGyluxxP6Rvpz693Asz8JhBspbscT+kb6c+vdwLM/CYQbKW7HE/pG+nPr3cCzPwmEGyluxxP6Rvpz693Asz8JhBspbscT+kb6c+vdwLM/CYQbKW7HE/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz/+/38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/3/fz/+/38/AACAP/z/fz/+/38/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/7/fz///38/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/7/fz///38/AACAP/z/fz/+/38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz/+/38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD8BAIA/AACAP/7/fz/+/38/AACAP/7/fz/+/38/AACAPwAAgD8AAIA/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz///38/AACAPwAAgD8AAIA/AACAP/3/fz/+/38/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/3/fz/+/38/AACAP/7/fz/+/38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/3/fz/+/38/AACAP/z/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz8BAIA/AACAP/3/fz/+/38/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz/+/38/AACAP/3/fz/+/38/AACAP/3/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz/+/38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/3/fz/+/38/AACAP/z/fz/+/38/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/7/fz///38/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/7/fz///38/AACAP/z/fz/+/38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz/+/38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/3/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz/+/38/AACAPwAAgD///38/AACAPwAAgD8AAIA/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAPwAAgD///38/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAPwAAgD8AAIA/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/3/fz/+/38/AACAP/3/fz///38/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz8BAIA/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz8AAIA/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz8BAIA/AACAP/3/fz///38/AACAP/3/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/z/fz///38/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/3/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAPwAAgD8BAIA/AACAP/z/fz///38/AACAP/7/fz8AAIA/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/3/fz///38/AACAP/7/fz///38/AACAP/3/fz/+/38/AACAP/7/fz///38/AACAPwAAgD///38/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/3/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAPwAAgD8AAIA/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz8AAIA/AACAP/3/fz///38/AACAP/3/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/AACAP/7/fz///38/sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sGaOvegQSz2HNXs9sGaOvd8QSz2HNXs9sWaOveQQSz2GNXs9sGaOveIQSz2HNXs9sWaOveQQSz2GNXs9sGaOvd8QSz2HNXs9sGaOvegQSz2HNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sGaOvegQSz2HNXs9sGaOvd8QSz2HNXs9sWaOveQQSz2GNXs9sGaOveIQSz2HNXs9sWaOveQQSz2GNXs9sGaOvd8QSz2HNXs9sGaOvegQSz2HNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOvecQSz2GNXs9sGaOveEQSz2GNXs9sWaOveUQSz2GNXs9sGaOveUQSz2FNXs9sWaOveQQSz2CNXs9smaOveAQSz2ENXs9sGaOveUQSz2INXs9sGaOveIQSz2HNXs9sGaOveUQSz2INXs9smaOveAQSz2ENXs9sWaOveQQSz2CNXs9sGaOveUQSz2GNXs9sWaOveUQSz2GNXs9sGaOveEQSz2GNXs9sWaOvecQSz2FNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sGaOvegQSz2HNXs9sGaOvd8QSz2HNXs9sWaOveQQSz2GNXs9sGaOveIQSz2HNXs9sWaOveQQSz2GNXs9sGaOvd8QSz2HNXs9sGaOvegQSz2HNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sGaOvegQSz2GNXs9sGaOvd8QSz2HNXs9sWaOvegQSz2GNXs9sGaOveIQSz2HNXs9sWaOveQQSz2GNXs9sGaOvd8QSz2HNXs9sGaOvegQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOvecQSz2GNXs9sGaOveEQSz2GNXs9sWaOveUQSz2GNXs9sGaOvekQSz2GNXs9sWaOvekQSz2ENXs9smaOvegQSz2GNXs9sGaOvecQSz2INXs9sGaOveIQSz2HNXs9sGaOveUQSz2INXs9smaOveQQSz2FNXs9sWaOvegQSz2CNXs9sGaOveUQSz2GNXs9sWaOveQQSz2GNXs9sGaOveQQSz2FNXs9sWaOvecQSz2FNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9smaOveUQSz2GNXs9sWaOveYQSz2DNXs9sWaOveUQSz2ENXs9sWaOveMQSz2INXs9sWaOveMQSz2HNXs9sWaOveUQSz2FNXs9sWaOveUQSz2ENXs9sWaOveYQSz2ENXs9sWaOveYQSz2DNXs9sWaOveYQSz2DNXs9sWaOveYQSz2DNXs9sWaOveYQSz2DNXs9sWaOveYQSz2DNXs9sWaOveQQSz2KNXs9smaOveMQSz2CNXs9sWaOveQQSz2FNXs9smaOveUQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sGaOveIQSz2GNXs9smaOveQQSz2INXs9sGaOveEQSz2INXs9sGaOveIQSz2GNXs9smaOvd4QSz2FNXs9sWaOveEQSz2GNXs9sWaOveIQSz2HNXs9sWaOvd4QSz2GNXs9sWaOveQQSz2INXs9sWaOveQQSz2INXs9sWaOveQQSz2INXs9sWaOveQQSz2INXs9sWaOveQQSz2INXs9sWaOveQQSz2INXs9sWaOveQQSz2INXs9sWaOveQQSz2INXs9sWaOveQQSz2INXs9sWaOvd4QSz2GNXs9sWaOveIQSz2HNXs9sWaOvd0QSz2GNXs9smaOvd4QSz2FNXs9sGaOveIQSz2GNXs9sGaOveEQSz2INXs9smaOveQQSz2INXs9sGaOveIQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9sWaOveQQSz2GNXs9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AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAPwEAgD8AAIA/AACAP///fz8AAIA///9/PwAAgD///38/AACAPwAAgD///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAP///fz8AAIA/AACAPwEAgD8AAIA/AACAPwAAgD8AAIA/AACAPwEAgD///38/AACAP///fz8AAIA/AACAPwAAgD///38///9/PwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA//v9/PwAAgD8AAIA///9/P///fz///38/AACAP///fz8AAIA/AACAPwAAgD8AAIA//f9/P/7/fz///38//f9/P/z/fz8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD8AAIA/AQCAPwEAgD///38//f9/P/z/fz8AAIA//f9/P/7/fz8AAIA/AQCAPwAAgD///38/AACAP///fz8AAIA///9/P///fz8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38///9/PwAAgD///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA///9/PwAAgD///38/AACAPwAAgD8AAIA/AACAP///fz///38/AACAPwAAgD///38///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD///38/AACAPwEAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD///38/AACAPwEAgD///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/PwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwEAgD///38/AACAP///fz8AAIA/AACAPwAAgD///38/AACAPwAAgD///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA//v9/PwAAgD8AAIA///9/P///fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA//v9/P/7/fz///38//v9/P/3/fz8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD///38/AQCAPwEAgD8AAIA//f9/P/3/fz8AAIA//v9/P/7/fz8AAIA/AQCAPwAAgD///38/AACAP///fz8AAIA//v9/PwAAgD8AAIA//v9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA///9/P///fz8AAIA/AgCAPwEAgD///38///9/PwAAgD8AAIA/AACAP///fz///38/AACAP///fz8AAIA//v9/P///fz8AAIA/AACAP///fz8AAIA///9/P///fz8AAIA///9/PwAAgD8AAIA/AACAP/3/fz8AAIA///9/P///fz8AAIA///9/P/7/fz8AAIA///9/P///fz8AAIA/AACAP///fz8AAIA/AACAP///fz8AAIA/AQCAPwEAgD8AAIA///9/P///fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8AAIA/AACAPwAAgD///38/AQCAPwAAgD8AAIA//f9/P/z/fz8AAIA/AQCAPwEAgD8AAIA/AACAPwAAgD8AAIA/AQCAPwAAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwEAgD8AAIA/AQCAPwAAgD8AAIA/AACAPwAAgD8AAIA/AQCAPwEAgD8AAIA//f9/P/z/fz8AAIA/AACAP///fz8AAIA///9/PwAAgD8AAIA///9/PwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD+xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2wZo69H0RMuricPr2wZo69y0VMurmcPr2xZo69eUVMurmcPr2wZo69q0RMuricPr2xZo69eEVMurmcPr2wZo69ykVMurmcPr2wZo69HkRMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2wZo69H0RMuricPr2wZo69y0VMurmcPr2xZo69eUVMurmcPr2wZo69q0RMuricPr2xZo69eEVMurmcPr2wZo69ykVMurmcPr2wZo69HkRMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo694kVMuricPr2wZo69y0NMuricPr2xZo690EVMurmcPr2wZo693kRMurqcPr2xZo69eERMurecPr2yZo69rEVMurecPr2wZo69cEVMuricPr2wZo69iEVMuricPr2wZo69aEVMuricPr2yZo69rEVMurecPr2xZo698ENMuracPr2wZo692ERMurqcPr2xZo690kVMurmcPr2wZo69y0NMuricPr2xZo692kVMurmcPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2wZo69HERMuricPr2wZo69y0VMurmcPr2xZo69/ERMurmcPr2wZo69q0RMuricPr2xZo69/ERMurmcPr2wZo69ykVMurmcPr2wZo69HERMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2wZo69IkRMuricPr2wZo69y0VMurmcPr2xZo69d0VMurmcPr2wZo69q0RMuricPr2xZo69dkVMurmcPr2wZo69ykVMurmcPr2wZo69IERMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo694kVMuricPr2wZo69y0NMuricPr2xZo691EVMurqcPr2wZo696kRMurmcPr2xZo69iERMurecPr2yZo69PEVMuracPr2wZo696ERMuricPr2wZo69iEVMuricPr2wZo69cEVMuricPr2yZo69PEVMurecPr2xZo69gERMurecPr2wZo692ERMurqcPr2xZo69wkVMurmcPr2wZo69uUVMurmcPr2xZo692kVMurmcPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2yZo69IEVMuracPr2xZo69QERMurqcPr2xZo69wERMurucPr2xZo69YERMurycPr2xZo69AEVMurScPr2xZo69AEVMurWcPr2xZo69gERMurecPr2xZo69QERMurmcPr2xZo69wERMurqcPr2xZo69wERMurqcPr2xZo69wERMurqcPr2xZo69wERMurqcPr2xZo69wERMurqcPr2xZo69oEVMuricPr2yZo69wEVMurecPr2xZo69AEVMuricPr2yZo69IEVMuracPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2wZo69EEVMuricPr2yZo69pERMuricPr2wZo69VEVMuricPr2wZo69ZEVMurecPr2yZo69HEVMurecPr2xZo69ZEZMurecPr2xZo69xERMuricPr2xZo69J0ZMurecPr2xZo69nkZMuracPr2xZo69nkZMuracPr2xZo69nkZMuracPr2xZo69nkZMuracPr2xZo69nkZMuracPr2xZo69nkZMuracPr2xZo69nkZMuracPr2xZo69nkZMuracPr2xZo69nkZMuracPr2xZo69K0ZMurecPr2xZo69xkRMuricPr2xZo69aEZMurecPr2yZo69FkVMurecPr2wZo69aEVMurecPr2wZo69VEVMurecPr2yZo69qERMuricPr2wZo69EEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr2xZo69AEVMuricPr1lG5Q3EdUXOwWewrnS/38/ZRuUNxHVFzsFnsK50v9/P2UblDcR1Rc7BZ7CudL/fz9lG5Q3EdUXOwWewrnS/38/ZRuUNxHVFzsFnsK50v9/P2UblDcR1Rc7BZ7CudL/fz9lG5Q3EdUXOwWewrnS/38/ZRuUNxHVFzsFnsK50v9/P2UblDcR1Rc7BZ7CudL/fz9lG5Q3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AACAP///fz8BAIA/AQCAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP/3/fz8AAIA/AACAPwEAgD8BAIA/AQCAP/3/fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8BAIA/AQCAP/3/fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8DAIA/AACAPwAAgD8AAIA/AACAPwAAgD/+/38//v9/PwEAgD8CAIA/AQCAP///fz8a/38/G/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz8BAIA/AQCAPwAAgD8BAIA/AQCAP/7/fz8AAIA/AACAP/7/fz///38/AACAPwAAgD8AAIA/AACAP///fz8BAIA/AgCAP///fz8BAIA/AQCAPwAAgD8CAIA/AgCAP///fz8CAIA/AgCAP///fz///38//v9/P///fz/7/38/+/9/PwAAgD8BAIA/AACAPwAAgD8BAIA/AgCAP/3/fz/+/38/AACAP///fz8EAIA/BACAP/7/fz8AAIA/AQCAP///fz8CAIA/AgCAPwAAgD8AAIA/AQCAPwIAgD8EAIA/AgCAP/3/fz8AAIA/AgCAPwIAgD8CAIA/AgCAP/3/fz/8/38//f9/PwEAgD///38///9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/PwAAgD8BAIA/AQCAP///fz/9/38//f9/P/7/fz///38/AQCAPwEAgD8DAIA/AgCAP/z/fz/8/38//v9/P/7/fz///38//v9/PwIAgD8CAIA/AQCAPwEAgD8AAIA//v9/PwIAgD8DAIA/AgCAP/7/fz///38//v9/P///fz///38//v9/PwEAgD8DAIA/AQCAP/7/fz///38/AQCAP///fz/9/38//f9/PwAAgD8AAIA/AACAP///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz8a/38/G/9/PwEAgD8CAIA/AQCAPwAAgD/+/38//v9/PwAAgD8CAIA/AgCAPwAAgD8DAIA/AACAPwAAgD8AAIA/AACAP/3/fz8AAIA/AACAP///fz8BAIA/AQCAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP/3/fz8AAIA/AACAPwEAgD8BAIA/AQCAP/3/fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAP///fz8BAIA/AQCAP/3/fz8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8DAIA/AACAPwAAgD8AAIA/AACAPwAAgD/+/38//v9/PwEAgD8CAIA/AQCAP///fz8a/38/G/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz8CAIA/AgCAPwAAgD8BAIA/AQCAP/7/fz8AAIA/AQCAP/7/fz///38/AACAPwAAgD8AAIA/AACAPwAAgD8BAIA/AgCAP///fz8CAIA/AQCAPwAAgD8CAIA/AgCAP///fz8BAIA/AQCAP///fz///38//v9/P///fz8BAIA/AgCAPwAAgD8BAIA/AACAPwEAgD8AAIA/AACAP/3/fz/+/38/AACAP///fz8CAIA/AgCAP/7/fz8AAIA/AQCAP/3/fz///38/AACAPwAAgD8AAIA/AQCAPwIAgD8CAIA/AQCAP/3/fz8AAIA/AgCAPwIAgD8CAIA/AgCAP/3/fz/9/38///9/PwEAgD///38//v9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/PwAAgD8BAIA/AQCAP///fz/9/38//f9/P/3/fz/+/38///9/PwEAgD8CAIA/AQCAP/z/fz/9/38/AACAPwAAgD8AAIA//v9/PwIAgD8CAIA/AQCAPwEAgD8AAIA//v9/PwIAgD8DAIA/AACAPwAAgD8BAIA///9/P/z/fz/+/38///9/PwEAgD8DAIA/AQCAP/3/fz8AAIA/AACAP///fz/+/38//v9/PwAAgD8AAIA/AACAP///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P/3/fz/9/38/AACAPwAAgD8CAIA/AQCAPwAAgD8CAIA/AACAP///fz8AAIA/AACAPwAAgD/9/38//P9/PwAAgD8CAIA/AQCAP/7/fz/6/38//P9/PwAAgD8CAIA/AACAPwAAgD///38///9/PwEAgD8BAIA///9/P///fz8AAIA/AACAP///fz8DAIA/AgCAPwEAgD8BAIA/AACAP///fz9Y+38/Vvt/PwEAgD+w/H8/r/x/PwIAgD8DAIA/AQCAP/3/fz/9/38/AACAP///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/PwAAgD///38///9/P/3/fz/9/38/AACAPwAAgD8BAIA/AACAP///fz/9/38/AACAPwAAgD/+/38//v9/PwIAgD8BAIA/AQCAP/7/fz8AAIA///9/P///fz8BAIA/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAP/7/fz///38/AQCAPwAAgD8CAIA/AQCAP/7/fz8AAIA///9/PwEAgD/+/38//f9/PwAAgD/+/38//v9/P///fz///38///9/PwEAgD8CAIA/AACAPwAAgD///38/AACAPwAAgD///38///9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/P///fz+3/38/t/9/Pw==" } ] } diff --git a/Assets/Models/racoon.shmodel b/Assets/Models/racoon.shmodel index f3420348caac368b4424f89b139dfc3149ad017f..95cb9f3fd35b3b2b80eec3c4263239e6645144f2 100644 GIT binary patch literal 603308 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

b4)!N@HOEA`k_0c@O^C7w6+6uyITP{H!C; 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

~g2X{Mbdu+6`({VR|g?9mKv~ybD-2fK$ z$3{E5{swnDtG-4%E7oXd#oi|#mZ#CqdbcqgQv1)RjdliWw6m6{(axl~@eHD!6>GFJ z*!{U1z{34iqn&lXg(IS;7=CE6MmuY|MmsBp6Iy+ZcGh%_c2=y>&aQ1zulI?Y_X!j2 z>~w;=owffO?X2Z#w6mt`-Ol87^A+6ftm&TTh4bM#UWhf?S@k`)3)3~)S<^kI3)9cI zwD_w2HxBvFRRhDA=iU7eF1&uj_Nc)CK`*Q27k@hWic!=Px`?87IaQHgGpa0R6-={AAP4ur4+(Go=cjUiD zaMJ?@NQ~v4;$H<{Ex5pyS1peIm4fR_I{c1w>Kn0llEkbwaOvn@FBreJme*qSR=FWA z+oej)4x{IS66E6Zg|%6Nr&HQd&v*K%PhZdy8c|L55M*ALHoXz z{=)BU`{8$B#^Z3;-;wF^=s)~U>tlT2chpD!T>FLJff?VMq`&Yxu#P|c&dv|~&SLnT z#qc|e;dd6p@3j1e(mwbd?PI=HS2{srw&%O?WPICFF5){n$ItNw8^KJYun`wB_Fne`xkWi6lf z!S6ELzu94JFZ_=DwC`T24}OP!xSn>T>+|j3l`h}6|EF}iZ~w26_V01&+J5}SVSC?_ zn8`{B>Z3i3FMeX}|6zjRceefTJ3Bw{JB#6W7Q^o>hTmBXzti%0e&KhFAN~KY8~+AQ z-_Q3gN%$S<>GdbRU-C1b*D?O$_to+bkmmt@M>^yCMag3>=2`VuSGpw$zf;Wkr#W(jIL;{Eqgc-$mL7 zze8W2A7Y-h{eHjgoPptYHXVLv^TY4Jy8q#`1?&EYzn1pWKKFc!uazF_(7AAXln zzW83Xe)d23o!$TNxstB!hu?v9|AXIYd2as~KdaW~&xau{o&M2&_#OJX{@{0Doj<m)cUr#N|0L#F_lsuiUlQ}I=T@tbOYPS|&{yWttE`yc*N z)nCQ_DZWzGcl*EaJ1u`wm83lX<6OG#AMiUZk97E5xSz;=E5F@C`F=j7e2IBRpYfr6 z_(Q?E|H1FHKDVDs%rp6^?^2mh_?_0r{s+GUQ+{*7_&&k@`8vVf@XY+Ne{&w-^CUgp ze(`r|dyjJa&4Vso_docZ=4bzd-;qDve(`VG`GMbA48OA&erGZK&SHF-T0Z+9{0_|e zT=~<~T=*UBq5bYW5`ITI{de;#zDqFcbNx??bMae(d0%tqyYM^eV|<*x_$x^VyYo?E zp0zyoKm3%M{zb`N7xS#xoiE~_1gFave#d<2eh0r(edZH>r_WP!*)QRDntu)Fqxd4h z>E}nj&6+Vj!tX4`|7bD%j{51pThGGpD3A3{|KN9E(i_Qq!tYf7#C_G}J2Hzh^x=0l zKm1PfyZ1fecZyGx=L_GWV)yZ>G#U>1HaS!l;`}2f&LNf zXW~Ot{S&0U_z%JKr?FfAE7F+$<2SV91HZEverGZKPRn=eL1Lb15ACNu_#K$`Pu@N} z7k&q3{e2_#NewpZ4Gr)btZ&eZ%iGeckaT^YH~z{|%zQMc%LAcNW9%EQa4% z48OA&erGZK&SLnT#qc|e;dfxxcegr^l)~?{{p>fyJcD^2Z{&Ozeg|fJ-20pO^}xFR z;CGtey`P9bPt!ZO{b!4-FRd@%X0`JJzq1&AXEFTFV)&iK@H>m~)}6oV)8tb>H!1sm zTAO84XLS=BMz!wy%f{^|gWK(|SFJcIgX2YI@VuxDt{0WT_o6a5UsML~JNCmLlIQe( z+4N&^abM}%v!-wGziUcgPu}>&ojKZHIN;}NK1-gtwOTqaJn*HDeooGQ`OS>b6pq!4IP{IAXLs{D9H+;R}rF!55?D z!5O16d9V5(^7fCvz4GH^jq17SF@Qft>wrT>W%vpAYoBn*{l{-y{HqsVO(s9Ocb<*` zKEtRUyfQk!aLedAfL}&saLlL-o*9+FHKQ{4=6Op$P5$+5ld{pPTP^$J+hP~<9F@U4 z7cbwQJonCRrK$(_JY@KN$tOp(%2yfuGpYv%jg|)wUG+=#1+m(Hrt5`^Uh&ai$)eWH zl3E^obou(9lMk$WF|7wDjmqGqm%gzxxp(WUPVaZ=2i)|qpQ|j`SoeT*d5)hh`Y`#{ zp^0>P_#nTW`bx6F=@at8dbh<_jDIFs+O%F;23JL|zR@G*`4=f~gDDT6WaCvklF#K& z3dc%*nP~t1{`x=kch7!(gTqGq3y*yzG9PeR+Skam4?Y_mdpK=$9l&d&GPrG22EUEU z;J8s4JU6xa89+tn@G`8e-LOfNrY!3$m2zn@q(;Ov&NHd-c* z-u6oQ#%tDh{i?+&%cjjeLgLt3C9=Q!puFIXAG@AZUcBtD`K`p>nVlF=^XKw=Th%Bm zt#RVAyfHcM_iV3TT{ZQ+pK}X~^OwB4kws7c#5%F%Jg9~zaxnWHjzbF@6T^Yv24Q>G61 zGkUX)3=VxmL=Qh{G%s8_st2FGCQ=@p`Z|$4ZR&tmN6Uj-M`iG9@@BMII5v87q`a$T ztmZRT!n31#aP6obd^@TK=iaYg;oZ@3fqRoTW2}UKNBav0kB$pGJX#07)~KGGM@}ze ztb~*AmsfcC{_{J0zjYvI*?uxOdUU8Zl;RPC47Fru@X)n)q~elhaG3QeN+#AAFUUTPaU>z z@cjMq3fE_CJZsh|e1E_G3g_Ri&BFVm_2S!&>cRh`GH!ULZyC>w_<3)P$gl-O>%blm zmErS^t^@1>Q5m*@s0{l+RECWpD#K0?opWpjlOy{i_JWC#{gm%XR~db1>L}TFZf1y zD(4n4&x_=Id&-<|*cM)rb8wZMgBJzoAohhVed;c4{!WW7rNMcLjiKI0jg}m2&QMr-C*3LO;S&d$;C8qu5#FbfNmd?2U z#Y}tKn#a~P6EPLX(Vi)t=?Yo;RIy4$s@%q`7mrcLrba~%clel;L*ox!h z7gqe|te+}Y7WHyI>)WmwS8>UUy(%v0w7p_et3KXu{oweDoW$uBr<8qE@z1m8%lxiO z^qD`QqU8rCSG4->y^7!3U*KY%e`q?fV&`G;iivwRRIJ}~K@1zmC)pD#&MxgKj_Z+cvoZMt$MddTQS4>;GzT)bO@k=14!VhwPI6ek1+8U;6tt{S})C<8qXYOB)%NUu9gdi&T>F%aidtIT$~D z-(zH)`^h->*Krp6$fRIC1_bkg4_xPMRWNVFJV)mjUwCvKU@wWvu$e?<*iE7`Y$s6} z_LHa#8w&n&eE9h3v7cn(aTyr9NoIPrVvDhtSd6X2V(cUqVqJ|FX} z7@LSq#~z~b2$Z8S&!PUFbor!X1Ietfw%oJi#{)UG51WqN!(wb6wtnm#7HiBiWs*;0 zp2PO|m}kYOA#4W})q3xv|*d=WGdBF|O+FtAtHXU1p#`hq{4q-7i2rZv< z><^lsPizl19lL|3(;jRN7GrP7&=;S$O~=ll>Ey@8V8<8xg4O5y)V95Rm)c^!N3G?N z4|{@5$CjY!)Po&C)4|vfY<<`dEXH;sC? z!8QPPZFlW&mX2{ErU;)nXh zUoq+U#4XmCXDtt&Z`0xU7Q^o?)|h9_53jfBaC+Mx_`Ig`sWH!*A0CgKJpQjoTHm+- zv@}K!?rzJ2w<~t~LCiDdBiEQ`tq-nl_2KE{({zn_*8K2ut53|cVpqQKa>c&={WCCp zoOGWi^Ceu|)&~#Q{CvW}E%x_-h3BEhJZpRLiK{;4!nrMmZ(9u4wiupmF&x`sjd|Ab zurbf*WeKmg`fzI7Klrpwhf6EwQ*U_I{=lJaIzDlWHRf5{qcP8#AI_}$v;)4Z7#+B> zV$%IRVBz?}ku%!we)Tw9ANa&IKcDbo#pHt%E9TSx;*rnq^#6yLXVrJ@5e{rI{MTZ* zuf_0Qi{ZQ$!*?zA_ke}{(U@l)KR9lNzVKVcE@sl-1D0OT{vI%29_ese)u%u3S&KF1 znfjc*`_*I99^tSV`odpzKAgUAS8XeL@K)956VA$~A4_SE@Kw#vr^Y;M8Sqri@AJ&h z!0=O>4mY(JUTQI%)MEH3b!mNYQJW4A)pX?Y{*aLl|Fr3FPmAH5wmdkeO^0tPrhjlv z#pHu$+Wc@##eBjqb^M&Za7#@`4zIKrPH8cG(qg!z#l$>Y42Sgd5X3NxPn>j>;S;N${Lo^!p~dh*i{XS8!v`&f3t9{h1k(rl2nW>u zfZ>0NolX$*%>QXWywB#xCvLIEJgYuj&!)rkEQaG*48OA&Zf7yP&SE&7#b;c4?%?4! zTs2_ebz#i&Tkm!(*!Xzu!Zr)aUCeVP9@pJs_?^Y_epqZV{LW(doyG7wi{W<`!|yDH z-~GLJMw0ejdfY0B(OO(QWY?tp_XTfXeF8D7#WSAhxnQ&4B{LUD4CAKaIeS(ucu#QF znC@~9?2h8btu9>fj^OLh?j$j^dx{f_Zb^P5c;9(5iTR8-IP1ZJ^@7V!zb6;}cGbN( z1s@8YmwG_-8^mL^hdA78)3w|y8*h5+9R(jqdY6+=lY8Q_<99bISg=WO<43cJfs7wL zrp*G{*Kz1kN%-BvL)R>z{5n53$cNwAbbQ!0Km0DU{J!yfSDoVKXUB(a^Wk?(=N;qP z^UMQBNqdH5v>$%gc=EZfe`6LENc%^|ZU5nSc6{J>8%{gdjo*X;jfr84f3@Qj*Pk!{ z>M1c%lj3&#;dgd^;CB|o?<|JjSq#6k*zvpntSWd*#%JHw4#ZH!kD6KC^|!;@k4b;$ z$7fvoNIv8J+~c#{JP!S9Z}=K-f*H0w>)PkheFhb?$l(tk^=bTQA1-}vnbB`z zF8uD$ZpkF;r9;W{`S82rHs9^q^Jt?dB&K>-e9+Op{kv<%r2WG0GTQ%Zr2Ts`+7G|` zrtQP7{SS3$<6@o%)L)giM(Vq?{S6W`TqW_rO>emNTrmAz8NX@?+kW_+oges}#qc|e z;dd6p?<|Jj+4Tdz+tjj60ng{C&+E$kHb^Y`HP<~)0~*Rbqr&fwXj)|f&)-KiYZSom z#%%v5$@mW#JVIhPndIZE@VjeA)+qr0{^mpOp65d< zH%tB^`2N4!Neo`cjPZfr+3|wkSq#6k`Qdk6PpXs8^D^?*Q4%v3OAMZ0FQ4b(%Qv5w z7}1jwo16WV$N1Ij_n@rz;>67j+vjf(e9DykeE8jmx8@hHAAZ}u+{HZKc>5bk(wm+; zIT!zI-?L`tzbbfpb3_xt|LjJ*AJV=iZ6KzT1%hso;8tT`%K*UPe0n&gO^TWt1=cZqrA7 z3V2?oU2%Q~{H}vu_^6cr&0bhZ_S^mm+kW`nn+J6$pg%AC5|`(BU`GGpcXoW>cm3Wu z!HwTR=O5`}p3A?S@1E~(H?)_S#$l21AD%Hk@Vkup5`Jef{LW(doyG9Gr`8|l_Ul21 zJx~U}8~EEn`Cm%=&;H{i+3&_AvTKfW`@_6Zweqrr-wnQGtkYk9<&iGt`LVu_=Wh}H z)Qq!}{|8&=9VbQ6G+=U)j2x0PlDXR>2zNUN+zv^Ck`WZipkz>T6eJ0fb4GH!J@W1h zIjAT>kPL#T1ObsK@vEnAhqrI|{@CAcRj2Ce-kE)>>*nCQrPHeTxjv_vUYE%tZDJ-)(fMxPwB>6VnIKQ~|H zJSVbW&O57YZs^A!pa0y;&-0xEtJiPBpW8k@8OJ$i$!BBzTpxOz*ed&HW@7t=@AUP7 z?{pcy(`EQhm*G2IhVS(6FMOA|LgvWtW&8=A_vHJt&|LRHKXrW?`gb1>=a-#Mk5}@@ zjDa}LF4OPJqkd1cD*9yve0Rp1C7kuGRpO!S&!y&}o@tan`&2CEhwlnM-m3QV((#Kt z@ST4B?Td(<^F3{9nni&FJ_il>*stj{G&o1u7Cbb%jG%StIUIU%c}i! z@sQ-W3g6|Q)<)e=)78!#ug)Yd){Ufo-)EIn&hz+WoBUi~S|v#u4&Uk5fA}tuf8-2O zm*G2IhVOa?7Hs7EUb=Q4KYZ8byA9#zMczKoD}NIZZ+2aAz)yMeCCS3!yW&e)#Uts3&n;Pg+$O$Nz~iuRom?$Ngwjt95?(Zu5-N9?Fvk z4B7uX%xYy`MIcWf>G#8TYdhvw{#cMm#UR2Zzv|LK`k{J$sB{NX#@ zKk%I{!*{w2-;EnLC=Pq7y?@gW-&L)WIUcz};Vk_AF;{i_cq991)9o@Ia-Pr3NUE;4 z-6qda&w1`!v#I}@lz-T=h=+5*hU0$sFrPnamXQC$aKt?PZSe^Fm$qr%2>5PJ=6aDo zhe!Q*L=f+5Z>KalJOF_4}*eAoZMQ-T&~NzCQ4sF2i@a4BzQ8 ze5cFsoi1};cP)K$EBgMy==oK@u`BQWLY>*YsT*D0t%L2{I{415gYn!tIM1zv_1rpm z&#i;`+&Z}L$dIDm0v|+MMf*Il+t+DLEGD*n@_gREm(;SJbbVqUsnJ>({cvomOz(K3 zq8eJ|+CR1@PG}9Y$8O)8&D&vA3#;$=hxXmHpK^{8`y_1^@08y@v1VntZ*TH{Ds7*} zb_vVuow~ZMHKo))_Np)2;49zygPFY#BRW`})7`d>nr)@O48HDvWc8lj-O+k*`G&na ztevzC^ZoU9n0L|4PS%2pf7;)kX~$WEFZa(my}fRAvfe6x&3<=8dudzDcej0B?|{3V ztfbkl+AmUekhbM~S;Go=uOIDXO0V#h(!CGhOScZjbnD)_2bTZeiptAx zPx?yLULU+|-3_kZ_pfc17hlO4@{K0%l?zrg(*LHH|Dr)e|BB%XJ64x_bkIsV?-YK+g{e<6FH+e;>$Pdh2@!3 z$XJ~Dj-7XND_OT=zF_hHtQ%iM7+VkjV^?m}QpP>$n|k%SRn=3`n4I;2J*m$}d>4H0 z7rSV6xLm>5e(s^YqxeTM-wVFxQZJX(TPXD=NJ=2P`{T*6l`U z-KxvF!L#o5g=yXU0IqfGU|Y8izIE$hT(=I+b?;|bw^aS#y{8&4wnpAcva7`B_*wqO zw`W^pLes6*=n9YQB}tmAoaa@eR~T_+ms*E9ow57OZ6<4;%Z&K&m{Da#JF8^IO?G6t z<}%+r=G6*+8+$LD50>oym7Sx$Z~){mH&)|-6~kwN9XfCzax81c=&4H)5eCM zve;Meb@mSVy}inLzSC}*(ZAbN`-kh1-pi>w$sQ_cM%$H)icikiNvvGnF4@|tv(Ih) zGkO=6er6B5^?`T9s83|RlJgwa(7ONee|FZt#(Gzc|3saE);-tAnw0Z~-RH|S-Z>N6 z%KAo{#hQAe3x0gbPFMPz_r*gQS6JEYXL#AIgPGkrxY^ww>^w}y2$L})WQBG zblF5#NOVaaG2d!ti;ymJNC!u^7F`~x_m1dZN?TaEqv*1Wu1KgqxjvhfM8_BzLOS(# z#G=b9x;#!>;p(L!UBQqJww^A!Oj56a=#q*p@O4AcWffg9(P1Bq-ClI5L|0aH$+1~D zd#mX3i;i)z57u_qgSXxN!Q5|*E8Ok25BA1T@WEzyOGE}7U2 z*KZHiW3Q?GB5XfKbnH`S4EWwXUl>1IsGc)lIKQpv*c?fVhBLM{K~YBR=5P5hHNxh!eQ?GqHk0Cw`0Wk@pvSWbUNKg^F+N+&saMDCOQTa87kA0u(MbHD^n>g%kH*fi8=V^JeUP(i3^9aE9W%t# zZ1BPw^!4rNS2r5UwdA`u?jOVwz8mw@So>;9^w6t&t-M!0mi1j>_WR|LF}GQxsPB(f zvOgXp_Zf*Pw5xKOagn^Js&8ac6UNcAT``cg7dH zH(5`fowUzyXwGjl^Ze#EM&(cUSd|Ojw?981_dz*B&b#4HZ>MI?D}Sb{LYa$ zgZutVtl}@}Pzk=QT zhj#oXH0KvtV8s7i*3Ol>mwn)6rx;=om8N}d)H!(F>g<_p_v-bT+{?C@#Rt7(l%M~# zwJP~SyUmRrF~lOO7P{&Ex$J6dePFGPnyYc^)br)bIE%3e!RVwC{HIvDm{L0z+u4&>wf`zuUFAG~*d$%d?}1zPnB7Oc_doN< z`UcFl`~Nons*z;Z#NYfz~Ciuwsx;*DC@iD4gV3laQm6~guEZvHe6FZ{M8E)+}{)qh1{<*BN%Ua_9a zc|Q1by-~B61YcV8u+J}OBJ2C3IsVxaBkp{hy`g<=`_p=@`0Z_)C1x3aOg%UaVZNB+ikq**8X`8+P+6cc%ti!D%-r?2} z^Kk2kd$@JPKHNIuA8s8n5Vwvvh+9W2q;F^*J!KyAWgf&s>W1cfcX?>O5)+vueu)>q z%nkX4oaa5_zZ&AdO5(o@;y=#52ZsFow&Q1sk7SbdxFGBCy{t!VSr1|)jb+^?%ewhw z-NwthaSrZYUt%TheL%d#ts`dQ))6;x>xiAWb;M8HI$|g(rKjI+t&*#v5eUv|aodc0U$>c1cC-#vtR2P}J2ko&3nV5$&enS7{R5@dl zi$JVHFXv30_D(r54yT-V;v4i!|H$$i_VxJ4#5E|#zJ&T+64_5Y1A7zdQh#8g{)uDI z9{(iFKjAsg%wO3r@eA7WnHRAO#~$TRiCZ}KtNIeN&}HHkx=gG>mx)v8GBFCqlmFM4 zFdvCeFdkziyl+(HuX9Kw=b85ShZivkr@orA#3TMUcgYcwScFp_dx=9({=dB6!-@JQ z{y;f>Q-|0C?HM1Nh&wp_Aro_;KK3X0=fdmu3I4gF>hqa)#2I*}Z`yGt&im{6N_@eY z5BrDMf?mI=CXS3f)FC#IsJ_GnbeWg{^_>^- zfCRrd_7{3xuD*|QCQiTDgG>y-@fSAmoM&V{Gk@5hz8!yZChm+yJ(!>Q(kC)!;ly!&7HKZWNV`*?@to&J#Fc>L%1 z8-{oMnNsxdJ2JL2E@$Gj=QA(3opSYA-eGq7r9bS0*B$?&gVpu^;B>v*$$56#!{>?m z7dB^n#~!#`?++$NrcdmL$B~`+!Q%S-;c(qQFu2|yInT_E@sv-6y^)>$Le8_(pITpG zZe1qlS(jmLU52xD8OCp*!`6N2cL|IYq_a~+xSU|h!@Ws{tVBRl@*Ox&r@cb+qG`lqd` zFHFlj^(lvEX-^y8ot$SrvyN~q@6^X{Ff3(!R`rEnX^YGluq%4{=AAQf{OI%#vpSzC zhgXrQulg5Or9R_P4yPvak1(n(!>76oo6?SJow8B5l>VIcgGni;jCsMMjy>uPNoTzn!+Fj#eKH>y(wQ&i&U2p8 zA(Qj$%wPFOxREkP2QwlwcWj0ispt3`R-~@eADpPmFrqHQhq??K>M~rY%P^rX!-KjE z3+gf)==d9(U_kt(<|gl)i95C`{|NiBzW5*6dCoKQMdnPLdB0v?;XP;l@E@$l_|BMc zp1wXXo-V_8x(wUtGF+$2Fr6;fZEbdK+oRwA5}vdCIwQVIt(o55e{EI?&xz&H)pZ%T z(`ET_7^};`oh}1+x(wXuGH|EMz@07wcN>zWi|j1+y_j+)9Jm`detmpLk?+>8C<#l4 zV>|8sB>r=eC!9{E5}tob;`j6rd1vSHlCXLsw*Arl9yPym`8NW0ee+!N)8FaMLnYxQ zr7zX1wSL-}go1 zw0g*M$%5-V^j9yks62$ZoNv&pwW_?fXN)A2R`Ru&-$nH|W#D*8V2w#s4&2QcHC?q& zap$2VaDL!xQQC<7O!^xzw67$DHTR|XqNE48@22vSpwh;7Y1~z1PnlJlC1I*dBKv{6 zUdc~-u>aRNNhP6lps!-<9jd>%MSFw;cb?We{q(ne^f+1X(Y}oXo2d2gerIGHhtm!3 zFN~+$_o4iwbAdbE{~Su|>m%!xNCxioa^OyvfjeCW?)rTAt(xD2e#<4nbd7J#nT3A* z-FoRxNyyyj>vo}dB?3X2t+R6Ie@I8A!CX(?wmFX@1zwOI7Xpg_A$X9(a z@=)-5zEX?(sr}Jv%xJOyp>Jr#-H@y|=&?JZs7J4pGd zy|Xs~cNvd9^t1nOoxG$T@_cGw=6L*jx_vSEKBY8u{{wgW`fxC;%fOv319!R%-03oK zr|%!&?!8qbl)oqJ?=JR-nZ0*bRsO6owptu;ck^XLJpOpU_wlm8-Ltaem4Ck-woD~F zH-2=~&-@y9lmtm5Zw-VYswe*SkdZ z_fg{sVt+YvWTD)iUeewye=h>Kd-?by_5IJ?J&z=$Mw%1*wTojuQ>#Xc|0^W&4{)dZ z3%Ju|;7)H3+`Sy~*w6R7$jwP{z}+wHmnwVuRt}Q{t!ifLV&gWolk$5Xj+OlqYYrcK zO4ZLdhy+B_%yr;#Tze^R5>vnf+$CFOdXVehtRoNglm}8ZNf}T3GOe%4_o1#?v_~3s zeI2{-1F^rpnKONrc;;I@vYvcDJ}|#J^_J@You>7~{|(LIuad{%&tK>A5a7n<`i!fU z{~9%0EdFnrs2sS{+XHur`WLvX(06Y+_V3-_21EdNN3RxE`*(K#I+AeSCXxNXUFw?G z{Cq#UEL$%Lc^wkXAGp)~1KdrnG$M}u^eja&zk<8S(ed0rI`8HW5W2bi-y_lbaG;uK zy#(%b8MxDB;7*r;yJMvqc<}d=_1}yC2b*y<(?|0C-0@aYKX8|=?IaKLX?p44#v}rF z4FgNn{%n;ynM!zmmN8O&UsiltOcD-9n$=t9^s}B5ZzYca?oRePulBqBUlBiWSA1%6 zHQ$u=-c||Ey=HGz*T(~%r18LA@;Vz;`Zf+{6A&ccErDa96tcpK)Bj zibdv-gxhh6?B_7_Y^I0m`nz>n50&t|?vs4#`jl_nYDm$dS>6T+S6Oc|EPLOxnE2%6WK3t*ZOD%HU6{P9aO?| z#O$f+_rQ&tcRj#ep1)Sab3Kl%^OM|9XC}5^;7(s3;7*r;J6#6ubQ!qQW#CT#{sMQM z?$=QF6T8MFxqdA)FYb7yzQ05B7F7w)Np38UV?Ru>x5-1(7n?t3s;9nRxvpGL4|%R? zjZpQAKRzhex22|)d9V7u4a@wn9B@~E#x!-kXVN3=N`4L}pI0gx$@Qh{#5?7HyS2$b_HaMy^6A-4z+IkpWz_xZ&qb5DUz=a` zx~=xhv(IkF0e6in?NR%!!P+pnUam`Y{Ri$6`A6VRmw`K72JW`C`80yxBV~)Ml7xT1 zd8psZaQ5HYX2oRx$D4za2I46HJz04Va2FQ;LY04hv83EDwwS+mZKM1#eqlb9@O(S4 zNYy{Txr9o1UN>){pY|VR`YwWm=XcBYiQxLRdE(3SE4BY85aQ9vQYjMoK#bvKbc%E`8tKtbWM`u59S7>rS_4}$z zuHWPjh|ZZO=Ekb~P0vN6CE@?PdE(M!bv-+gbIK;*u5jni)%WMe4~s`|nD^=B;v3jc zwI1GA3D3I!fjfPDfID3V?sOTr(`DdJmw`K7=CH1ROj;vz;6~+z`GV2Wi{zhD%YogS zy3udwV7oW#rHpm!U_7@D&U5QvJ+}_tbL(I}w+`;Bvt@*FuG!2$sUHhO7hKv;=CLhy z&+HM#q+~M#eXAFYKD)dh!HL)x)kheGGRz2!n_4J3SzJGWJvJ(CxUszZv_Spui$wpm zuOEjfu~n>LM%Nru0>_@c8=d|#ZJ)-ro;t)Rnru>FpQm*6SIPS0E8mry1C7a1;{!8V zMnn%Q(_i|_;46EzpRx4j=)kRNm7>#p(qGz!`Kqq!Yb2Iv`_h+a-nDIjyH@}QK>-D(JeGSFF>tbJ3 zvG1u5_<2Z0X(y@TFS^W4d*4rdtPVx^?j8quL#f z=EqtDl6P$pefzTkB-Qv%zTe)MTfA|gP_2)n(~gyY>Mh*)^iVG&!+?Q-dii3bw_E)s zkRI=wziXuNsLz~0quzO#tL!;^F#7u2LkURvT1dTZBN|nHDfI?Q zJvjAVyZ4M{<+tthNq?QCKUnpsj5~Z_?ZD2-?W60fac}rO5c@ibedP-kjlLxI!K@i% z9)o2bxnv&IWFBzq7@2RR%(r-|T~SBWd>{HUi(j^iU!FAF6ZMw(1%4eR{u?3wyVl}P z)FkmA3_C~s{6hTPRs38{{0zsQkoCCUe_&vbtj7*n4-%dq%DTOfbxSJimRi;go^`J; zOzYkUaIISh+q!k|ty>43CIH&UuJImkiDf1088|~O&R8EyS z_(}Ivfz*q7%7N57X5|LUj0N$}0w2|%TIImP-s1aQ=E@5*jako51)ANN6xCsIUzu+n zbN=;j4deOlz}*x-L`_=z1=k#NZt2O!tHXN&E%qOZnzuyunjCP}EI-3I{p48Se7Xfu zEw4yh;o$!H<{Eu}y&TBctYcKCe0?SGU&QR$d$ZBKY|dcq_B{gKzLg{_Iqa;G_KGoL zNZVl6%VUBAuE-x~$bn~!oiwKS$w|T1zg!BI7%K_)Jm{%U%9t&&GlK1J-V3&?MbcSm zv)zTe#>HO-1!vx07tEJclJSI(S9IED*nj5@roJ^bko8q}2^dG1$6MAn>a-0D+?oAV z^y;kxWql*f0?C3=o!a-0`u4w>QTtv<;8|GN?Pqw|t%I4}I=I>0AMCt8X)tPwjIl(< z*ehee&)2H_QR@nNY=tfJuPEK3lYuf%Uy6GVujJ-^B8KmtLr>$@{x@@9b z6w<-k?t1XHyFZxwjd6v$sdr1po#Bit?2Ya((JgSs75+x2&ld*2B)Z&E&#?s#cl%HM z@v+p?{S1#|%NS`pH8ciH&KmtBx>3%!!sTzSZ;v)>>^4pw*T;C1Gk zR{Xp=WDCrWeb>akrXd~N&OC;SZkuDXusc5ZLfVGCt~WyU$NmdA{RzXfPp63Pcc-mz zJmbz6o#nI@mR}*d%Ay+=(#ieT>?68fGR6j{tuTE)`EJEYy%D10y9L+xm3m&OH{WS1 zZ132&!f7jf@18G=kM4%_Hz!n&L)Ye$hI=a%?f$HG6+trcApXcWUe*G) zyItZ71^$?Cj54kVhTX3iReXIPb%1<3xWd@-e)8bw$s;OPOe^;C{V17LB%>sUOI+f+$2nqh#BL3a zE?3HGv`UhAB{os4S*DoLS62jA4a;U(ryI%o2FzbdpE7E;svoRbvPp2-md-K6C1micSdVAO}5`bX_3J}YWB^Ob|>UphB2CZ{MF@Vyui{ZDhb zPfNVw%;PP_)P=c&SvQUjj5T|x1L`%*oE8A#4p@B zVi;~6ag2BWzG7T|)h_s7*73pCtL2YGC6#w#5BGe6^u&$S^ub3EoVjTlDTnc-E?;_DrZ-@ua21&Wvli5(fsj3FzQsd{!tMN z66z7(77zzjXBye!BPy8gYobZrmr<}M+ z;_`%tJUivYOZ0MLCAv(UM3;$?=rZvUT_!d{@&pM6yb~885rhN->{T(5&}Z5a4{;Jq zC?^(z9vuk?#6d`2ppVdeeSD^GWa1yZGoQ!uBKCpIXZj)TK^gY&PRxU3240jC@1Q;} z>JaNd&u45P&Y_nR<8YE8uz~mn<<9K9-_x|mn1*gY@eK6zMH^xnPC|xC zz>qiw^_~8SVNgyRbi^+l`_K`)pfAVVT)Wz-{Pq07W8beUL%E)%EFWnvWcL;vWB zPv8&aghZ8%KJ0SrB`!fdN5`2spZ{lnLc%k3coB=>o&K0VaR}-<>rD)TGWwt%@dpxK zunC#i1AcUTK-_`Pl&Sg>b6{`aQ~Dv^z&O-l9>f~xi#1B~{isKLfOlThAvS>Tkd-|W7jXPVIWYmp z-;@&%V1BRbODuqL=0knr0LV_C!~kfA4ZPrg-qErDV1I1qg$(yQ<@5*hGZuS*Ht@c) ze`o{i^G;n=UpSw4$G2hsWuk7j?5rQm zO_?J*4|#TESX(cLvvnE9M#f(3fUliv3FE@nj=!)uiEuS~>|+cPp7Dn>XU@c(`B2W8 zIAyeyH(Pko<+krUZ3d`x?%4QJxWuk9BucIHnV&cq#isK-N|DR;h)urRhbZQx+$&ozkl za{o$fzwj^hU$39AuP(#Ax(xH`GQ6wHur6bx;|1sPj&HCR#-*G!SN;^fWj>C~nYeR* z;XVY{Vz)CNn3jI9mwHaZGv(+x6Q@4qj0?w7=8O-+qNgr8_!a+9ADxr%?AYu)vxh`7e5#kjru0o;=;2c4!wY+1Qu=mY@F?}~ z$&3BUnK<=m%lL38`;E`MU{J>&^a+1bmlrbZ>FiHzhC7*`QwQdxo-=276WM7GYhusq z`ofvag)z~=nAqXyI1{HmI`#Wa*ixTATuD2}e=wyk!;`uUOVao2`3gs}UdZ?bhNQmZ z1NhOg=Yi;9N9sA{oQYG8OusNA<-E|ti}c3}pTLUPPuYEW!HK#IBkD4IsLQaSF2jYo z3=`@yJgCdCpf1CKtS9#4Lm1H6@8~!aPc&a)Kjz7N)P4v(;t{V(PDY1O)sx6W3N9&ov= zq>!epo>(58NCxi`m#2)?W$;dy!8=_B?{pcw(`E2Zm%+OgrE+_AiG5%9d5;v8*eY$N z#6K6gRpa73qb~ND_k+lDBKMk_K~jXz#4ap(&2w7ht;;w2!MhGs#(Vx1dEYHbArZXW zdZ=>bO_9@%x)>+(^_8u1+VfK63)Ry|3Qz`Ls?n_@e-Sy&leE%*n6Kp1vmRUImsRsg zieEn8m%BE_`$Zl-@*`3vebpYsN4^lb-`6)Hz`Kr_ua)~#q%ix_ZgLk?N z-sv)UH@wK=_yaQDo+?HB;9ZmFwLRBG{=DQ{;?Iq~F()#|UlDo4%YKpq81GwnU{?GS zk)LnK%zp6A?tQ~^MC76$lo$I0zS6IxlM`LMXu z;-75=Z0Y~7?}U9M{zoZ)l%j`9d4B8C5kLMum*cLi$1z_qGgtgck#E;-5(nO$i!TzF zLfRjyuwB;wj4#dX)N$C;dq(a^@UH)*dH(Yv|MEv(*>6{TTa#>v%qsO)^nO~h`8)bc8`jU69;W;Yu^1BnnUrEi<%}0hKrx-g@ zQjn6HgHNsZuN66O-_ElBDNWt~;GMoc91ZI-c&E$Yoi2lSx(wdw`v<&BH>8EyZ=0uP zl9a13^ZUvp;x~vr^(Hq_DbE>)eHpP^qP$-cUa`u9krcrK=DF|Y`B~4BlUs;C3z;{5-4sr_XX!)_c$e(xsko%F z9-H3JBq>kD%=ZpW^Rs_89NHl%X2s2oCkm+b_q-}EDNQBKW3|7GJSFAB7uENJcd5oU zkN8LA-|EMKnB_%J?EIjjW1nYM;{R^?b zLL&cwce=m8J6#6v^!DIg+0{Nj*QauOd#jY^6JfjKlgM~wivO-so>TNoqps&&UmWy+ zcYR{h#&bQ&+xA)-#cz-sjA3Nr*ddBl_pH_&V{e$*tRm$^}B6Z@qUMyc&TKr$v z?7#578t>}bFC>MwzWK+1aglsKQ~IZi|35JIo!_Ibx4~jf#QzP=VzQ2Rr9AH7LHRy5 zHanE~BqAtsq4{N0%ClY$-s$bZyF~p9-kHBN^<0(qb2ju*DbKU(G>GT>^5{iM@qZgr zw;#OQnC^~0i_GWBf-~}rYWaV<+Wf&g-9O;no%yBXsej>&Jo8hISKm5YFrM}86R}S0 z@8uw@*?IN0}S;kBeL)@;e_~miymmGkx4o5nDuF+GdWVe12`7tQr~r zK;*hDiu=L4!yD5@s_WUqydLnbVbi3M^jGf3i7Mq;w;#MqwPAJy_uI5tws^q1H3ze* z{n(~yRzE4v-!x1bnOXduEPF!_N1%!97rfj5)%@^B(*E4+tt#a?k^O>q89R;l{4C}7 z&*cjT?-JQBcy~JOlQ{0@v16~uewk*rPHOp?@7}@J)H9y%hRyPGJ$!QFnOy&8Ch|YW zoBH~2w5iMBoi2lSx(wdwGI*zdf5E$(*3P(vGJdlkFQ}B~>OEHYnNOK9W8*l!j5;#k z!~G`Z!`<>H1dGkRGZy#*B4=ygLQ-y*n0MNg@LM7$ExAnYM@!8+y$VJi5P90=-y^`g zVXH@aeieConP41vr(gfTyKO}md$`}%sW49N?<>r6A1sgKe!su{m~im!$gPo{zeV1- zKvLS}X!1hdqJH-0k`doZ%I9h`z0q0y{+M|qxjbWgjXC$d0}(f*+$xtsQa;z3<@QgG z-zoA`J3_8E>k?i6!MjBM5xmo7@J^S(yDJ$#^mG4@-z3*_!MoowB==ts`AO>SJ*1$Ij++hKliUXLr3%b#B9ANVgW1;Xw)c`^1O87IJG}36&#|T@tkSfhwm80D+Y8*<$g$pq#k1q@h8+meoPOj<5B~dk%JDex zPWL~@c>4N)ce)JT=`why%ix_ZgLk^jab4DC$-GC;6%F2B*2;<*Ob8=o^*42+-_XH! zZ`Mm0>(;?|ZXKNG*1>vi9lYn(!F+BV+*dU+wUKpW?_j=;^Q?By2NQ~keUc=#v1v^2 z;H`%9tm-c%bwc>BOV?D!)Ue*cOJVb@pOULI&)6|NQyP7n^$IrHG}kJTVF<@4v9nX8 zFiOSt42I{KYfZ^Tn&;El@y5=hx=M=e&zk)oo>O>pUt+iR-?ZR zzHVPW^>)bhc`#>-S=OfJv<>r3|NVh?%(KqHJr!qK^?ETzKHua%cfDWU=osvldxmvv zENzSVUf4Ii&GK~!mMJ#f+B=`N<$TY3{pIa=pB=8#|D$eH55_r!nYLjHp#$BUm^i=Uf>{0vX_ko6cT>v1)-9x!E+q^XUA zvTk=n>jqc4*B7>Q?*sVKt%EV$Iylpg`sr&~4`I)lY0?@=^7jok%Q@fr;ilS? zKHsKyjQ2gmgLU6pYJK*;>`7tJCpill^{;*v+%S8&weI&pgz5`-Z$x&nQ-*=1}b%zK06j^F5t)=#}uVC$z#;D4@f^`vZ$8#CPc={)I6Cw-mogn5fCED;Q@Z)3IWs=`;k?~AXD zZt+3h;H+04SpUwE5SB3NXSc(=X{Fvksn<&C!Kn?Tzt5$=jG_Ku)g14h@J3iIgDDDq zYb96X-tgV6mC_h2_Jxami^V>e^(UD}J()*Ona2{D2i*FP%y*c~H;K&mJvHBlzB}TV z81c)-kYC`}-r~Oo;=fknzs}-67||Mw|H3*2&&qoIEb9Twek1EP zLDp??Xx-ph_xi%L?tK8)x^=LvTL<5|bug}52j{x?GpyS_Wn*LZ!dbzH>)WhVEl8yg zGndtGW_|lqCJFI8L2dE>>qg$#Q!xv8urac~K{a8=Zzl3|c%`0tWcr_+?Y{F{m zL}y897xpbzyMR%-^jErqJ{iO{$DF>WfbnI^uY!MvEw?WID|<~C`01Jw#{4s1 z2hCoqtosv4hc9FRs!nTA!<< z&xxg08t3kRAAEc4Z&vDRqu*lc8b+v;0X^1d#vWI@}whIk{@f*fk zO}h=1HIFotoH-sC{LZyN=Fe9KDm^DOT;UBr!^>{p!_00S-0bcTc79jJ=q_WV2#vw< zXmZi*6kRH3T=}!%tEZ0#)`~8BNC!t3lzMeUmo=n=r7MVTujqdMEM&7Bmu3+i^Y|^K z=rW7$pO6l&{!7N_A-cDnafPjOOT8STD;Uzj*J(vpOLQeeIv5*W4bfe7#ud&k zB)XQO%NDA~QL4KhyzTA}=6+*b;qDt^-yG>Lk29_ut)gqD*#dvN=L>`XF7?_u`hR5HRd6bs6$wK48U-C+1w z(UlQhb*HTy)0PljQ_(eY#t@dzFS;*97ar2V^X#=;qKk0a3ez)gUeQ$v>EL?$`%rXI zPFrDnz8_sg=gdv+-EYhn#xElE3QN7cq4j|C!$mhpbOoGoh4udtUyT%h+Bsb!G%# zUEN|;STFY|i6gl0AH))Nmi*7#Wd3Kt`PpY#Uwp#v-4$lTTGPA*-u4AItsY?={As8< zGH$o3z4y@3O2HXZ`&ttV$hZ<$I5KpDcVGGX!I-!)*4;rvRm$@NGp+Hee&1kR_9KefI3EY-+zfUyy0hw zH@J1g9NaqM4r`O22z;93TA;&(m4S-fk0kbx``_b%)uJmqG(kuFA^P_5z<$wX9h9IW z2663=T;?lB&3mUdGMe_B70kSQn-%q` z_*vo+y~-{$e*AQAu^v>)Rb%6iFM>C^ zB()!y^8Z>SHqo>G31fcyyTNxWKef!Von(Cj=G*fo8GqN=66|~Tn69e z?tdN~{Oz}aR@^K3GdPJ;oXLA4ur5rlLyJQ5m80nKNp^Yv>()3}yTSx(rcdtE60hj{ zS$iYSyD*q!k!7`LE%$SYS+uB{)384q9^7ATsny0LrG3vE{v&qb_A~Jdw~iQwTSput zL!-IIM}>9;zv^+usxzv$N_n1EEZo@MW?Zo7Z)>bLul&xDc*d`P9}oO0zV9_KVIIUZ z4s|{exbyb4z?5HC2FlBNaBRJA&56Lvtk(jaQ?F7wj;`naa3WAebiVwn0v@SHe520U z6M?*AcD4Xgnjzli9i|A{T0#?=QwrxMBr`~xyF`As7I{Bts~yy))Dh?>xg@} zb;Lg0I^rL09WfBMjyQ;0M=Yd)%%hsjs2}nlv5{@!=lkO4UXGt7KJv4y$5mO6v!V6i2z-L9+eU5Oh?BV2msp8= z9}q8b>xh}Sb;M2FI$|eo9q|*lju;B5M;x2;B7Txs9-T-gZj!h>rFw?4)1G*VUQVn; zmx+_;GBFZeCO)Fe#73MHf&}~i_l#%K4v2}6NLcgE%P< zl*@co$}edP^hf-I`h2FG*a!8U`4RUZU5ax0B9pn*aPj6@iB1+ zr$5HvOq~Ak5i;=x(pl(_HpCi8{hkC0 zzN&wT1JIVbyyW|<`llS3GjTqvwU&3-9~pa{=6A}F;eE#z#&c4h*`umY z;e6dcFupFs_qyz)JUi{-dh9@EjxfD$AAiD(OdZN$dEObD7aWgWq|+(?2*Wd;<9{dR z*_jjU?)W3Y|MitjeO@p-@ARka5niVr_MzuYoc8#H7o4t_!{~Z@_?-Iacr_6=XD*IU zoo77LKlNa8-F|qS`BBa|oQdo6hr?;-_y-1O-Iy=$@Hg$*o78bqp6L(&k@Bps56rF0 zq&({~tgXv%wl2fijFa$Pko_in&Ab_lch1DI*IADw!qtxL3FRYRr|dCbc$)c=uE@Ld zjA!*(`r%9*U(yHjgrT_(Fdp;gOq}tMdBM);u}`gEDCHS@kYQ%set6kwiyl^{Ejl$v z;bi)A_6LlN-SnsI7d~cu)wjGm&v<@qzi_d$ehK?yN@Dv%DbJKq4;E&A$h=5-X5RFN zeRBUwY`^fYzCN(8F2lXL4D;$TysOJj%Cob7;9Nd4Uwi`NVi%vW7rw3K?j3U&RmuKawd+isLMNN;^-NZ`f#T+A7q%5GWL_|UwBjX z6Y3wgDQBV6}4f=dYR!2Vy7oDEoy0nLjf2awd+ym?QOIKc1od z+CRd5&VFD`ITNQoFYItqp7Aw(!g{*@;XHkPU_4!h?{pcq(`C3$mti_xuG_j+o9^xZ zm+xG-n(dj^?`d?@zIRl^5@6KO8!FzUwn(){>dV@ z>Y7UW5A$uEQ!k$WF272l@}2i?-0GqJ;7otZc*T5&lT?hKDe{;`*(86YoUdoC@W@#r z=WMt@{8`EOQSMBUQ$@blv9IK}#3U+*?TQ^;?xBA8h(+WN`X1&f5IIrgMyn@CK3H>K zSloI)_I6IOU+ih)OBMfL+}9${noPcImqhl%cGo_eqx$>E8X)xt`eL>h_0!+Y!N03~ z=k$G&`{#;mc6r|q+odUYQ~Bff3bp*O-I5ERL^7YSUC+cH6Mee>VLN?&U^`ug?Q|Ko z(`DFBmtnhmm45V3k@2$iDj@kIYkd7ZwzB7k8DEM0lJ6Yc=^rBHyF2|U`J?f^!KW6f z`7MiB!+!9k%+@%P{X4bZWU)WsTlDfL5A)mnVPVM!-Q~NJJ}LtL@18CB-NJVBnpRiq zF@D5umG7MS?%nWZQvS&S$>$Qb8=HEcf3e6{Xa2bnw!2gEVccMm-|v=J`akI__vb|a zJdxw;HfO*2%H}@f87}hiTS3YHy5L*f?n4j#6*=Bk_QMt5%AD6Cu&?6m_UaMOzx=c` zj`kBSelF{M!#AkLdw%@gJ*u$uf7{or+&a$$ky8|0EcV~?eN{7i_*WudN?Tm=S0DNc zw@MwkQsnmE4`)C7#{Qnn!~UFmaGK$VY`?2=S3_RdFO&jD&M(C*Gu7iAK$)ua06^tx=`^5+ShLOrF_5hnIn>Q zji9{YKRG2ouz>kpmbS_td3I)$e6&Jl=0B6f;os!xa;SXg;Y;_b>qFtn#pL@{%$!{I zALWm?j@H=(+j%-vjpurD>GFQ{i04}8Z>jY-{a-oti02uPSE}pH(Y-}vf0i?Izw=4t zH&XxTyC121=Mum5@Qf5W{#+l~uaV}rJ>H2QDssWUH_9WDDr zzdhqc_8eR!_SZ6}Ml|pY6M1m_kWH}N`^GX4*W2ZJUu=Ty3aoiw?dKC)-je-O-+bI9 zpuQi6$~WP9WxieckBALY|7F+`mG9go!w;T~BENgSt>j}iHk)s{9I;O1YulEH|C^e6 zIc%r5hwT#eFKl-rHk*g*UG04D$|EyBF)yY}qQ3u|()N+>SDQrk!*-<~oQmW7Q{bOm zk+7XUf7nj<4{W!waYH}fr&9l>+6db1e~ntQ!~@$U_J5B=>jT>*S}$QcU54#+ z8Mf19*e+G^NDud?>etiA{uylUDLzYG-)oc@Am3+UyRS#6`_Zn`HRTbj!_5)h@xb>Uk6y&t zA`h6qjO)MIciz_#++Vh*&LsJ^Uz_!Mn(F)T@v@aF-??4qVrqX^-26)%`OZ_$>J=-U)XLynq}&KSK!DZ$v2#4 z-m84ngMW6{Y%BSm)6G@Qhi&9~arfP>a{ZZ^*nVL{%=0|5CqHJZ$%2+^V>xB8QdU=ZEbgsz#{m;a|;b`eD00+pqY)lk#VI$N4$4 zJlkr3hx_@g>@y`Ff2q0Ygg@e2k#l`!d0@Mf{xJ3ZOkTR1AGXu4f3RKBEU{`o<=NFy z?(Zwi18=`j*Ng7GDyT<1kG)h~-9Nk!9?A7&mD#b=w77B7f2oOQBVfB3LvBUz`)A*X z`f`0-V;=u6%Fq7!e#b~Z`OfwqnIiFb!&Ts@a z$8N1uk9hWs&82>?#@;O-0oy&ubWPbm``xd@VY?=&7RPbFU0!*J%6GouSsI5u@%d7F zV7n7r)~WmP!Uj9|mm%h^w&~*deUR?6^P9+bKD;+Rj{Vnje`S^Lynb{6bv=2$`*}nY zVY_^PnmsI_)F5ce++f_|9IF9!7$}g9{U)hsr{;-|yAJ|TpVLM%h?XDY^ z`h7M2>x_QbZouw9x?O(Gid|Iu$?Z$cDfAP=`w7m%dnj;!*&-(9q}&_dz!8f%KkrN4*RN} zXMo6~w!9q=+jX80?cw+6EBmJS|C~90aG3f%-hJALaL)AlZ_BT)CmmLwk-xXNV3s#* z5BuliJ?&J!bG8i|lz+2Tm+QB%o$i0wPG29`PM2XjU54#+8Mf19*iM%@t6RC{inVTa z!C>!nZwIe_Ciw<(UiYSM^cy|X~zv#!=_YPF4*Ts+}FbMZ8L;+|(#(z(s7eLLiFZ^D47 zz0>TGgrnd@$O|*{qMWE#`as{CIn4?bP;#M`NwO`qQ?YuhX@0wsAJK z?Hf43GN$yAaVz<*jTvVzPM^lE()k;!-P}IJ?R}l4-g{E7lGO7`J$Ny@^!JtYcT@WN zQ~HA$88=?Wz4v+rSfFGIf44Ln((0pOY)Z&+x z;+H!EI$1l#FL2}!;=fbkzb+yFaZc#?xl_o`@FeRIkoCAWrDIxU2Zx>Vu0HycwXAn>t8Vs$eCGvk9rbQa za=|LTqoMWwdGd}o`j%xL>owEfwr+K9Z(XaOknjAmd5h>Y(~{V|zUgc|k$iUebL$+# zDmwBl+qlrhs&*qG-+A^YC#^GW!t4rVds=hbB;-5Sev!*=e>}fkbmwquaZAZ<5f;5w z#%phFUCf@fb+Wafzj|c3@5-ZkcGf?N+eOE$vM$Rbk73e9%j?=B_ZG9~=RIV#&GDtI z`7vMd?N#iy(+k+Q*4(iA@8i+ZCw+4l=d&aGzhhsml+wQagL>S#uUFFy_VP#B>=}(S z*eRMyeu^+^4XKw->OGZu_oN=2`e<7fyOH$wm-IJC`r{mOkBs}3j5|%n{Zx&6!?#H6 zOE31tihWnrBdu@y(v(;@xZXJB<*1@=L9h~dl&#>;uRh#UN1OBsC*ZU_> z!`ofv8)nw}e72qR_pElq4K<@<=E&nSg?T^B__6(Q(rA0ymph_=?(v2AK9~7rt916E z(%tO3sa{4`9wzH6?7O_iZ0r22N%jYk<-PYV_vM;nKA4r)nlxdlJ^b%6-j@%1sq@Vp zm*RpKvTm{;?Eb;q^qGt+9Q;{$)8O3JTWrtSj7D33SBd){~9n!%`2H&Mvx{qFWT|51ytTV=NBo zVCo#An<~0hPFvw>+M-(<(!tglM7KzE>qLhw@O7?`&asbt=QN^QDfO^#FE$Hj7YOyY zKGYwq?XCxJyZeK=-xyc8+p&40^v8O@-tWj5vt?X#_yGQP&ld*I6tV@sd@uFjaJT

+ zw&45CVhg^9*_j9XV4>)guY}t(hirD%5_V@E%cLHAd_O)Fe)ouOl<4M&?g;$}!@KrS^ z_<&nSjKHlUPT=0p#0oO}Qp1?FKJ%`^dA!D5yQW+Z7Mc@pKJlh5lx0`*M=gxTO=_$2 z;FGhLdwXTgyz9!2XrrQ0Pn`+>QZt3oxJ>$8^9qmjHclbeY>6ElTltl@f5WGCg)+7` zZ_kfqeV3ZW(hssKzkkX;exic0b91{GVh9~~d~2mLg7&oqF~+44oz)q!`~E>J;nDQW z-kPy~d*qfe-Vzo07Xd5GAEU>6lT=%7kNte<_IVrR{wC+e>8{uJZeBUrE@3CLe$OHQ z+Ckz93#X;AZe8kS7hQTecyFZqt&PMM9(T)WXS!0=?v&^KV9mb#R#{{IwC!vA*Mr&Z zV8_XUQu+I;eCIJ~*V>ocJhhT|Y6c5M%eZoe?7sgJYk0%Y5^r$ph&i}*#2uFTP8nC) z4L0h(iinw#B8{AD`prgDPZ?#O^foF#h=|FZDy=$OewgnE3T(CDs3#J&Y8pWR5#wAmn>%dqs%c` zzR1V#Ni$!-H0Jls6;tD@2gY}2bE|ykade`ezoE)@Ek{hM zd7F)Ij#N_T&I2!gYyZ)}GG-_L%Se$UR@OIQKFbkfAN1aiUbZ$>%#@n)9h4YF5zkof z^jrh&cZ;4jCcWKSaxg5@7awh8{C1t4V%92S*zs2C5zhtAeHYdBQ9=7?v3tg*5}&BE z=!4het+4Os8ynjuiMe`H9xW{~i-v_e8w0ImF{vx1i0PNNn5^%fH~dHJ!tH0`7j7Lf z47ZLrMye9u*d4aqFgi|I7v>FM5Q6We*8y(NDqCUK3I({mdm_EnFnUpR}m@uiBtO~X) z$#2o0&3dmMcr#9kxB8Ac6HM~9@PfoT+&bbNZXGcXw~n}nTSx4}tt0;7))517>xhH6 zb?R@wWgeqs9)&~mARZDS^WDUJH-_dbF_F3Amonm)>)$5$g}6wn8M%!r2dhU_3;B=O z$Zhd+2J!P#$IlWU=_>27Ue;s0tjB+{9-MzqlXV*+>oz{LZp2C4>r1S}y$^_&xOK!# z+&bbWZXK}`w~qLUTSp9q^Lx(rIpZgOl2{&{NG5KQxICqLq_WeVc!^$4tVEZIljt%r z5?v-fqRYfakjc$Ruy0!|xd-S}K83_Y$SXjmoOlSI$wfdW7D7GhsC)#8gYcQMg#J>h z{9yWL4#Ypu(+7DR#6GA`ZUp7TJt%kjBj!Q>lqcBpzkFw8Vja|_KgK7{f!=u$ zyohhmzse1h`kaYV&I>(p4M$F}r%NLHiDxkP*ZDP}d}n0lLmY##8H@giVet92{Sv>R z9GP*5UGVPspST4wV<*@%Gm%WZLN6y)q07W6beR|h0C5BTi#N$fzEi5uuLF#}yDUZBgw3Xq-s8pv{CyK3LwF5A%TIo%t|-7@qNHj|{(4 zPM@^nOq|b-kKlIdF+bYD?8uBqAMiT$o%XOgbsc}e>3TVguD6HJ6ZJ1_?)VcuT+aN+ z|4Z&UmqfUl`QT69VQS`y%sV{o%m*2krf=q={3jeu zE;M~IPZ*l=*Zm7W)33AMu(RVY>cGv^clHa+tlJMSV+(bZJ;KVo<3GmaOq_nGLmnZF zO!;g3g^#Jr92nPm#Pe(WL;22(&loUqBKw7hX^Tzj`zI{SxN0q#Zz$iHa^}gIcq03S ze;wN?hkbP!?$u?OSC`>kT~>eFE8{x*2hL?YUd#{1W&Zd?`A5#gk@-wNoQbn;&Ya*{ z%9y{ZFHB24%GG`lo^||BJI=(Na#deAmj0y+RAlNR%h^LB89vp^VN?3~SJf4JgiC4582F4caqMw) z@F=#@x9VS5l*c_&&U`o%ryZZs!Jyd2`l$UTXX1ROjFXJ`L#CXPMSQT`O>PNyfn^ zyf_ng#zGH6A~PT5AK^#pyM}g2%dns>!-3d`4nMRFlX2gnfWUJgz}v!Q}Y$( zqb>C*hxeTJ_zTwKo%+1sJnEvOJ&dQz@SQHhcDfAL=`u{G%MDw{c5U-zmv&t{{4eME zew$2IpJP+J=i(}Eg6|T`qZ7&SUE=bTvAPW3=`wt$%kZ5p!*{w2-{~@Z_dx9HCiaDA zJS_HYQ*sxPqhA)2oVUYD?k)1wC%IM5^CPL>Rphc#AHJh~Pmyz{ZXd@v_d}7piyW!? zOX;KjXCl8X7R1=PHzw2CGNlZ>+!>7JoxT$$iBa1zT!_+?${6C(I5WEujV`Ob$`qs zzH`RIKkyynF+XL$@EtP#?I-hv?~on;!*}}na3-zG@SQHhce)JU=`wuh^q=GZ*gEg{ zD2lg%gAh6*3J6G(E;YGOle-O)TdMR9D!unAARR)lO7Fdw?;rQsJkPXeo|&D!?=y27u@An(KE~_Ul$GU7<~HRo#&b%lzr}v9Do;A& z0pC%+x5&BaukaoI#ozhpukf9fK26HQchuiq(kF{7^GBuQzX!}8;X5mTIq?sChrc^Z z`e5dR@SRmZ_Q7|7_Fq=AWiNb3dF-1m?Xizc{{PLVyGiXO+W#=A{zUtqB&8?X|J-K( zE0u2958wUI-lK9RETu_%*n@xJJFEZMMTYNm`{6r%eBe7>hVOJ4zSCv+PM6_3tA6fZ z_zwTk|EbFVIaL0{@gCxV??_LYf5LZ^XFU7if8jf;{6Ng@esa4PO?Y%?r)K?C(p>S4)_j#G5>Flk~31mcjU*P1F=W; zbg{>>AHKtW@`sCk@E!TB`v<`z>8D+b8$ot_Tg>E+=&WNZC{?~twakNsNg#Xfa^ z3*V8Becb=>o#lVlKlqOP*7^b8S@yI3!FQxv^A*0c>SO(b@2vJ%Kj1s7yjtIc@5pbh zfAF224&UkJ;k$tPh3~BPS^wZWef?w4m2}H~_zu}x|KK~TKDGV}-&yS?J|DWOboz(= z@E!TB`3K)2TjK}c>Ei?6=`wt$%kZ5p!*{w2-&ysm^-uWDS}*djehJ@M{!;g^@SWA4 z%I~VlnflR6w$?xR&dR@^^;7uH%CFXc;XAATX{kJN|3|5GYyIHNGx@2HbolOn>xrzl z!gti4IG$3!@SScye1~kUf9m(F{`%B$A0PNmm*G2I zhVOJ4zSCv+&Z?jF557ZYe*XMqUNC%zJ=m|FN5Xfc(|_>OdB^?Ve*v+860W1rGWzcuQOI`eGF>iHskhn!SDzh@QMTJPXHD?j52 z-&yxlURf{UJFENwo{z$J$VvB)@Lhm^gzt12zSCv+j`r!ln$N;_)W`g!f9lM$KRu_6 zAAD!!4||u!!Cs@DAHLIN_zpSo{+IP!_|B5UzVMKmA|ss3*RAQPcAk8e@v3;KYXYA2fou~_)eGMJF9** zAB68{ANy$!zO(F^b+dOc`-8~LzgzOWV{cHG*%zdK%3}|FXQhY9{D$wW^uv`#ZG-P< zf1u>QD&Mc*J6(qFbQ!+WW%y2);X7T1?{pcy(`EQhm*G2P=699%=gV&{&L`Q=dc&D# z`osM=i08ZT9WvvkzP}0IAzSm0GtXB4)%O$OJ1f0{T7Ryp_LA}o-|6E6-{~@Zr_1o2 zF2i@a4BzQ8`*qFsU-7K|J9o&Vg~cQ0t*jz3jG(H?by3N5uwC+eL6LSHjAz%ud3GJF zXV<}db{))T*TH?I&OP=tY`jzJM{Hzs>DvaaZ}4BA=wCd8ZjIY!^;gC;^2UMRJ$0@H zC6$E(cU<_}v+B^6Bpobh&nLce+uMK%?K-&dwda86r#(5Mt#)CI~OHae#LudHzB{%2{t5t&~mwE>HEzVO;(6v^YT>Vrr39{=0(-Qg`s`CwAJ4lcFp zVAJe>r}CcuAZ=28@Tt9T>_LwG&g=QIXQ!C|wR_!NaM~76l-sFv|A=2;Rb8iiCHs+f zzpy81Z-afwYsK*EGkt|e4pF&rAq1RHuQa8{;`!Jw)P5%fPEby z;l6W4>iOoSFe0vvDJlB{`OH244)&GGKG|1eRn>^B!9`_Hpn&=Pwne^QOK$PizGy~x zY8I4zs)A;*>$`lX`yBWEk*|8h!u16t_FvfS7j@S6Rg=GbH4a2ZyjoFM?RPG0^~6_p zZ`$~8Lc=1OHYh7G&f;drj;Z5UM`e%y-Y6N7@rFz8nG$BtzM11YHY^Zdx@*peaa9t} zJYTH#QGD2-V(}}?)DateEv3I@&AO#>#6K=mGXCp|w~U%QJE%R-vN=DFUlUa-zI~%Z zM#kOU)IR9IVA1(X#dn{$*NEQVL;71jnV;DYwfi1sw(H<#dwsC;0BK{b)&~4+&j&-> z^RZ8A*TK?*q`uW!eeiUD(QVLlFf}^8UAWp_A8d_IuMfUPr~3uQ#%4WV3gPU2wl-jG zdp>yEULVYztX<)5yI){$dp`Kv-d`9zS-ZmFl+}H}UaMUv&!c2InB1NZF1OnUo7?9B zd~Vml=yn~PZr8!;b{)KK?;CrvgKd3-+wseKZ62f$cJD1ZeQv<-_P)XJ$>x=CJZ

z&Y5S*>TSUDl-1`8OmA-kuBRXRc)<3Q)$4=r?fr%E?e@X>_Bqa8F72+;d;ss;ZGrij zy8$)}_b2PGus>z>eDHs={z?qM-Uer$t^W3s^<|r^FT?`u{U9EYtiKWyu+IbH0(Kp- z0lSX)fL%w7z^)@sK)ZV1h!vEPb#k?=lbvOq%pmI|@q(JNo<7yqQ+_9XRn}cY*4>q| z?rxNImwn-0vOf2a^|`EneI|C$Ro3xkvX0M|bv%Qt#c)_cx8_mbyA z8+k5Nm*>Juc`mR|Y=3?bOIR$=nSI)GM!sK}3GzG&)}BY~8HdYr>#+9RV&8b5Jl|Sr z&o^QVhvYfULUat zyRNCkBkVe25_TPN3A>KHXLP-En^kBYnOt2Hu0HK+yxIMJ#IkacYOmV3KRBUO&W^_ZdhbOn z8d6)nZyYeE-yEGFZyGY%*L`CAY&3`(LKOrGh z-tbW~=7RBVLo*rQ!)B9h6B7!at!5Ov|C{lmbPKhA{rHnf3HP(RjhU}b7{_0=POkagJ)aCndaBr>e2=;BlkZS8@+Zti!G&-eK1f^RVlPd)Rfv zKI}T;A9fuv5W9{zh+Ri4F)#S?=Udb-Sq0Q-nX=;_v(XKOr(LvYr` zl8HfB^;3@cgH@g@u?Ib!xPz6BJ;WSznRr7$e%TY((}^=!>6GWpv+iHw3wnNH3%b3W zdDdmlJX`fqj(CEePAtJnryb%5Ryr~<1id}t2f9q`K$nRd=rS<_T_#?j%ft%w{t+k8 z(}@x2vU(4ge|)f;*npl+T)?W2Ge*P&NLPImnRtNpx4KF!z$#CE;s928^m6|ul~?r$ z|Lgf-f8Af~iCg6p+etk0jIGG*iK}|#Z*=U5TlJ~@!uh%ko(_{+>G&5O*VEY(*JU^y+42tzuFLRuKz?CwU52}L8Rphycw3iYZC!@5bs5Ii zW%$~Xxx&`E3|Ct+{e`J@8J^Z<_QWljD;#af%3s3J$d*6gXDgj6?5xXhvn8wfEX=IS z@UkUyg_SK?C6f*x>oR-dmduqsamE{aReoV&^hx#$4+rEI7Pe&a!@;@? z1M4z-;<^m`>N4D`%P_Al!@If+>*_L`i)`5o!?ISo%I`n(Y}E(Dl5W)pzgp#0eqmSr{(@Vr`nbZZmP|Q#RhMB^t3Ix9s-Di7 zXI+L*bs09*Ww=zg2PV~Jcog4S{ewmI@^Gjw!=Snhf9f*qsmpMuB`5kn@g2`rd+?^- zKCG$R2WRRsd*ZqbUt0Ame+pabGF++4Fr_ZTle%o3dA981%(LDe45`{-?BoMK>g8ca zOIH0AZnR{TU-rb2tvc8f*VAD|t8MbYiMk9U>N0$&%dnv?!-cvG6Y4TNsLQaRF2jMk zeK4RkUg+UJ^7BCt`|16I`>g!*hdpsU9p1CbtN!}WJXr-c*1Ldfyx(wgxvV1>`)MfZim*G2I zhVOJ4zSCv+PM6`kw{Mm@u&;Hw{c=VtHnQu(X`VA8-~6>QXI3MZE~_7US>)X-ymE%| zeB|0!`$K;dImL*oyhk@OSK*eSCq?dGuY#PReHCeLAL6(m^1G%hIP>YwQTKcAaglws zrv$UVo%&5#?>UjT#(yXIbGRcicU5wO^L?eheC|(%jrE?D^zdr69Pr)boS~uTMb5RL zC}$wu#YPkl#lG_03(0#lE4t@&KM+d&@Bf`cz2{j^hwt?A@Lgd2&E3=YS6Ab6_go1F zd>6O%GiA@Z?+S@MT?6cg?{dv*r202vd#Kny)UEd)zSI2!-<_z{NcnGKhg_UtbN_L- zy6VsE*Y)L0)HJv5fA~%xANWp};X7T1?{pcy(`ChX?e}|+ihtf+E5jKoccB$&RDa7H zTP*$E=3d%&z6XDAT(VNm_<7x)6l+2mzYA$=$a{)o-Q_M%R`X*)&SkuJ(cP(76_x%p zeME@jyFcFdG9FEe9tnZ(N=^ArMl-#l*}tblI(6o`L$>`f2c*5$r3cEH;Z)|?Zw@JYS}ZyhqWDg;AHLJa2fou~_)eGM zJ6(qFbQ!+W=MQ|>`F2+?{@GBtWGH-B@B3erJxx2cbij8lH*9fSl=KRzS32Ok+J&#E z`+Z{F5y9|X_7u587@sl`Gr1qlrH4Y5|0*vZr_MajTwTJ0e|M2z_-^3X)T;d8#(U(w zr-jYtJNGzPA6}gMNalA@v&j5VFZt8%P8G`j?#e=+I2hksk4wvY$4i*S2K*dzMdaS? zvpV3r>Kn6%B0v3hmW*#%^Z3yP-a{gnyR|9=zT3EJz88N_+0jP5=lMqZ-(!x6{Bu-s zIfGX|z(4Sv?l1UGm*G3TJbahI@k-5~3BBdatMFX`UuNace}X@8z<2A*f8@O;?G@Tl zUEXURX)a#!uj7fxe?EVp&OFa*L(cLld-P$%IJ-0=! znfeDgqyL5ZRi6qT>YJK#nVgxgYu5CXQ0t%f>Br*#`eyCRg}l_ab$^%xzH4{pu;Yfv zy*><*`?0ZkX3HH9>tDj&2hs4Io(|vX<>9-4`i1YRmb;|x|DdIF)O(&|`*&9M4Y>bU z>~9-jKYTawuU4vmd-mp#`O`6=|L~peANX$RgU`Ibi+#su4O3^Hb^o%r8tDJt0pkPT z1&o*Qoi4+7x(wgxGJJP<@J#i5`tke;nLoqKkJofl>s6^~DP{hQFuPaS;$XftXxl*E zvpUNBy~Vbe>mtXEJ}LM2X!Fts4Lq!eZA&J^z;~Nlqz}C#a)CLM<$fP)e$;ohTEC`! zI8@%VI?gB&x=`s_6y%-IQ*p- zf4m4;>0z(aS)q66J&|v&t|ITboo%Kw_Nn=F=D$+%e3=_)zwn(tKJc9`!*{w2-{~@Z zr_1o2K7Zi5^xO0O@T=G}e&+NT_%7w#{T|kb-^<6zncHRZlI>|ReE;ZJGgT;jx1-A- zH9u;Poe<5wCf8jng73g3+^`-xf)4>qdmf$y?CeNU~I^&Qz|Jy~yt?>VOGdogB+1HKFYs;2|{ z@;qrK@44M*UYPLE!+PBRk6d2(?!uvCA*|o+I+c?3a&y4?58nm&NBB;c;X7T1@1{MN zsM?P|xHJa7t8`&c4C{4?Crdr>UE4u>L$Iey>L1j5p5MJ~9nAQzUff&OtLAJ!Z=ypDBOt?DMglxs~@kkAE))fA-2Q-#>-#^!~$lx_{t1 zU54*;8NQn|>Dv(O|Gf4+^`7TvWe+KTrrDHFo?i#d)SIWO{yc79AQZj}pPEsv2N%Ad zCeMRI=7xSv96S%givB77KWwJ_Xr%hSeE(2?zAu?A8&)qT?g~o zb#ULxuH#*2n(cKyX`3>lT-d>gh6J38#-|eg(lrmy|8+jL*@ZZ9_Q(ZlN-stR} zCPhTpgD=^0iA?jwY*)5E-#bG-d~0;u`IXw|t9WsVt5oM+&in0O8XGo$EoEOt`buwb z9j+VV46gm$m>bht?f3n9b-T+*dpG>*`%jFoJ#D1E%e|qJ-|o1#w~dI@?b$zY&+c{4wX1cFxN;M3 z8Uy>cm$GHuU+@3h^|a9FxZ=;Q8#muETZX=plLEp6mtM^TCVu z`e4S9((czS^md!OCy9Msf2tX_$!{O*cw72$W?SEIkMv`s^aFmh_ZNmt5WmEVU&{LZ z0!O;Uf2B45!IJNZpWoN~3{TFK@o3^7k4iEgFy+7bC%TLo{~I^B(muYhrF|a2mv$YD zY1hG-b{(u~*TI`#6ulDP@ml$Wofn@QyNb6YM(JMED|^CsV=5%fPqnoquK46Co^r2Fdp(1iHs=i;w@dB><9B=brb zbaVH@3740Qjeq~gJBD{lYu;VpJ~umkLXY5$zBeWB87p74k-e9n-Azs(ia&CDvvcWh z_l>9o=G6gr-7LN0&s^;78k6RsVLWQ9_8h;NIL6n%!A{q-UJs343mCT(?){NFoePHC za~&@5&(NvIn4KFEHEC=sp}gXbj8^CV+(cW zIY+v~Mu}R7d@;9Ujjm;zt9{LjDIOZf(v*td6FuFiUPR912>UKM^PjP|O3(N#&uSU7 zS~X+MF?-cd6_IN0$oN}J-?*yWY_9e=hcrzU(L1_w{P6HWuC%wsSHi&^a;Azn_JbK# zxcC5P&x@_pUgyHvFO4)^=DK{PC;OWI@-^#~*|q*TVUQ`-3-uN%Fb(HZ9HNR-;aOU{xs*$18 z#rXf8caZ*;Pv&QM+3tIo*{*||?e)RVq0&Z>v=J(8qkNcx-_CI>DR&K$;Y>MvUvcXCz~6>=yn~PZr8!;b{)JvP;AK{ zwv_Pu0A{}~^M$#QUUc{#Ztp4i81qj=$C$(J!$ike7Vwva-|=Z)(Y=)U@{TbVhR-EB z>SGR39~?ixU*CuR`e6BrqT`;)EIQ^8JRjw+k2yqrF#Rmi<(7Pm8~Nb+vZ7=DbK_OVQCTHmi1p_mlNkn7@M5 z$K1tc)nDQMWc?NP?xdE9b;Jqm^O;z|o~Sp@Pr95lQbeXqh&|s?)`O+yPR9w? z{RwxBU)twLIN7(gde8IJF5`?vpFKCSWi6R7XmCSy<~h^2zm3!r9vaVARZJNEL0AN_ zgV{5)MVw!I#%LUpGNF6UV0kXAG)FDT5z(pj5~GEyYkZ}KP_1({R@3rRe-%>{8YQ8AG;Pdy5dY9yVyb@Cg zI*>AAM7^l^h_tPY9eJ9_GiAM*qxxfG;DrkD&5C_zjJ@4Ny)Wxn@59Ciy-xc^-r8Xl zuHBsPW@d20LgTN9Cce@AE*g77ew!uY6tEA{Ry`}3DrLoz>0 zyuq#`=3v(mcUV?>wDJ4>-(3wp&9kT5=Y~4-+_2ZrMv=BPjpb*rCHz;XmfYuF^S6|9 zjrIjr7)8@`O=z=G7((`#`<*Fm)cSU_G5q@OxJTJ~%Kf|DEY;z#t3;mJhO=lbV@U5_ z5yT=oo3=t_$d=N45r}nPv8S~3IjCiwV=G+mlqw_@k8yOQH zJ1C`k&vVvG`63P$&k>Q(qCvvZEYA(%6~zjC95Ex)zedY0yAv`DODB7xd(FO&_89%f zbumhp$hzm{M2C#;kIDQ;?85G6;um%uF$}woIEL?3Tch5gUdD$9GemrLteQIW-2CxW zW7gCShWq*rV{#37=efi)GT#5$xEy-eI9_j)QFERAc28m&LznF`+P&Uqd1VS_(tu|<{7_#ztMQJ zrle7#tbF&DJ?z1aW*EWS*BGrEerzm>WPLtn7HBrrXmE0s5i~K2@l9L#P9w1nyN-B= zT}RBrt|RVY*Ae@$>xh5Yb;LmII^rO99kGx>(vQ;8k1qax5D%fhH>AJstu1MMMt^0` zd#3nhgZO2R-!H^P4vGIh7yo@K{<|XnBQ`Qz{QQ;p`D4q^5+6w?<8e{O<5wAv+A<#O zfirG1wQ(a(Vjo{(CH8qhyu_{}W@6V7H?ix8o!E86PwYBkD7L-n}5X3Tc zdx>KpTm2!1Vfm9X)|qG8OKg9d%5V9fxP?AG#4L1~c!e$#tI%cQ6uL}|f_8aV9(E9) zP{={vaNKY}rpNg7z$b6NjK4=0l?HfXYwX=sEMumHy&C zVh{R!A?`qZlqa2-17)$FHiBJ6nnYe*26Eo0d z;sv@)tblf{`9qul8GC78#R$|j(f{m;TlQizd*bA``fr_iwq#-gR(;gTnP+7B#D`db zWe@&fPaM6~J~05=C5efnUXd8U0n?1@t^`MI(uZk4Bhus-FJ##cDs zvM1616$51WUQdVZ_406ikkm)p^aG~nnrMsI?LYI3tokb~Pde>m7aUK0w2fZYAJrZ@ z+J)cwoAO*?ck=PK>X+<^TQYUP?CANM54=u2_*0b;R_BT@xw0p&r^D!adH6h_eqnRU zq2mLWBU}B4$*B(=S9qL!*n$ogN4ENJoq4wW1A~)}to$SV9pslO4|{X9#*Z`4v}4%} zbL%o^o^=`4)@3+bmtkznKIKp0YwEWA1zYR(r4X)Gc1a%eFtsIfg{RR|hx+)>Jkvil zKK?V$q>&GX=5NxKkA$D~`e0{l;XSon;byCTbrojjN?GcMmyyZK6;`(DQ~8CH)m75C zvL|k(EBl3yx$;r@{AZq%>=!P!>?Iv0wq#|$@GxbQ@(T--&#Ds+R`rSwd*%713A7N3;SIU0jP-Mze4hFT-m0#q{GiiK~VNWYv*)QCQ%-_mRVNR~deBe#1e(ZoX zt^BILa^{(QN%jk4B9n)7_)?c)OTGVarS2b?QkUULU4|v;2X*q{%rkBM?{h%4M;_H* z;YZS}`vG>OKGKx^!i|>gTwzA5er3P#BKA^Vjf1cv>EuO*6LlFz)MfZkmtjL)h6{BW zCe&qkP?uprU4{ekgVhHZkp42ixLRkPtunA5ddgrw+^6~_GIg*gPC6guAK^XzrvIeF zdemk4AI{Uq2gcK7_)eE$J6(qBbQz}8<%X@z9_@O6^S=Yn^Y15wcB?Vh6}sklS>P^E zjtY>0yTJ6INL>c*bQ!qQW#CSifjeCW?sOTr+ce~*r?c4iU5VZtP>Ea`+$6M@$Wt@V z4iE%Vv)>;n43f#@zHzu^B$OG!8 zk^^5s?s+4tdZ_Ppw%T$4BC~r>o~w>dA`hFGTI$c?UbTC?gZhK!&EpH;OUDDT$o*)NuE4rT-pBvgiA+uGb^vfJ$5Uh%2V@hb!(cc?ogX0Q-TvLM=Bt z+DQJ*%}UCFz@hHJ+p8;k=G>dX0Xg^0x)(fsCEa1%7Jo*$+r=$b>E za^=rO8=7+<&8_<%xYNf6xYK3ePM3i@T?X!S8Mw>)Ho}9y$M;yFUhrSa;j(=^Xgy;C~5Xf1JBcuV63ZbD~O+ z7r4v$Q*k9fd{$cyfd1_M#OVtr|HD2@WIh~lPb+iA!T4OtomCEa9&y)v_)N9;^4X_y zAo+y*dh>i<#y@ycPC3AN+P(XJW-s&QV(b?V;I8f=Q`LW}O%o2lxYKO^Cz|}ft&SJ} zUw2AUBN&R=-ujW4YFy7B9$^jdJyLbQI^7N#9ff^p* z?!URg4*Juj*$J`#xqIU4C0_hBZ^;@tu=mRC`>?0-S9?QVax8GS@aY{@-_o|JL{4c= zzw?ua`JR91C^>MO%G_5qM9ufd89!qFo4WsjJAHhBJ6#6ubQ!qQW#CSifjfQv0Cy3e z9tpwzOz)z^|2fQB17nnbzgxA?1Kb_UUpEy0jQFK#C~#M0S}7%$T>e%Lu;npx>|3Pn z#~-={b3dAgoTZ}~k6ybn#{hQ^tF%z_X-oOlG9L<>n=92;{);%4R~>l1lkT4KZ-vO* zGQW$OMLtZa>R&VVaWHVVe(3k={;rs+nT%%%bL^KJLzo{i>!zs#&pYNXi(x(5)JtCS zDRB48@2{0TPwzV%z}>KYlhk~Sef3EUaM!bZUS-dvr$gibSor||uz{=l3%Ju|;7%_O z+~vAgDzuNZKlE5bb>KPWr`^5nMZP{ZgS-^?b92YyEFS!~I5LZajoMl~;x=8$A-^@3#k@Cm$Ypd0P=gI4*JD6`fzIZ4H0vnqLZl?>SzJwy<9KfBP z4&3SGfxCeE1@6udZlcz^0#iDw1J9>VY;rK(MZTRY_O}hNAGmun^|V^Q-;67*4m|7q z2kvzL0C!tcmi4k8wjA)j9B}Mz4)J}j>i;a|e7PTc+Wg-;V0?hPfbkNz(`DdJmw`K7 z2JVjLov5Bi4dUv`{2698Uw$ft`88>7l+2$I=8%uN#k3Wj^{RjU?;OBg*yIkX|GzA5A@|Ezb8Wly-qw+VW`F45w0(ss}3$$P0Ze_X)${uI2 z{c_-NMxgxyckj2%tJbqYMyn9uF2H_)yYd&yZRL6Kai7{TY;^AXB()cRmeM$Dj3=`)zf=`Ul)q&QVT{|CKfe6O6=EepC zcS}#jc(8AUi2qHQgiY<3@91wWT50H+)=f^^)gI z*#?B*f3sB|`GLjefb}1^3-FI@Kn z-1lQO|DP83sh2!QuYMWA^Y>ieDze^gH|uTBr~LKXpA$T6GJbM=y;|SypZzEXxNG^% zZ|ZqnbE+Kh7P$MY^JwMoSJNEoCC@uLWL3|rd)IQ7VN>zy&og3J|C*1^Df2(hY~Svt zhxS?z+Y$rZ{dVoHs=w*H>gpxWU)5--o+q`Z7x40f)`6k7V#r@*>|eobAa+09DVp`# zv8Y8baCfWRwqVxF&MC9X{Qoha|G=H@AK*@xfjeCW?!Fk`M2*+w&0}JKyDfi?RrdZ= ze1UvFIbbe`y{7z^E;vmLn}gd6_Xy>F9sGKntVf5;&@^||^JL9?a)4RjE_aTP)cT+9 zvp-~gJ!0Nzm`Tl-1!Ypn`g7D=wzHU$r&g68Pzc=h+Wsh*@vA-9CqKA29v}mEdOC2Y z%fOv319!R%-03oKr^~>dE(3ShyNp)rQHq~Cdx5)t<3hbW-+z4i$^qPkwXES_y$!3D z-V5AK@vaW$dGl;!F`56D%xsg2s_%2vg0HIs&nq&Hb}(L-)>R5oKWMG+K+V4_soKi= zb2Y&Kz@0umz@07wce)JR=`wJq%fOv3vspJdBE4~I_uXAf^Z9(vWy2+a5mYs~E-IN0 zwo9HbDAKNj@$5P{&#r^@>^gYQu7mmPI=C+=-EcXCF)e&~L}_2$jDrcpL}vc?TVu)Y zDdCOkm-S7^GgzSdK;*SYgN;jFCWP0nSl-vLj{JjcVZd=84lpi_7!|&8qv88_q5Okv z;lSEMdm3w;y}~1h*7mLWkFu{K`&8*@q`KBLd||IvzPv++;4Am58Lf<6UBbib73<~e znR=+ym)X6+Ti@6hpC|mpsL{S_^M*><9Pag*O(W)?S78UdQ+zc_%RfsNHf%AfvazE` zeAuR6=Jd`FbuIM&LeKK6ueyAI$irv|FfXeAtGhcALB9tGV$??2Gr?2Rp8j ze(aQfJoonlezf-&hQu#7t~L!H>-P&BSws9cT>SUB_;0oN50)G+em*6B9`5%uJb7Ej zW-=R-UuG zuZ-m$ce_5DjrwgeIg5V1D{N&t`G@bqqU*+NFsg1(>#Shr4cjzL{bO$Tx@XIcj8mV5 zn>lap?wYDU0W|mL`DYs&yhp>MjkR%2G7glvcEa7Hdz7)i--__>s%G`oEBuYLd)mFU zLSN(hrNQC9RdV}=?N|4L`=6Sfj0z`Ogs1J%!uPP-5CT%}{*tebtI{E z4!*VPU|hQn&b7~HSa;TfRYuC?Z^C~)I$^i#yMEH&9A<+DD-Gw^SK(ntmhJAmt)DvZ zyzlH>#a6G|GQhm@SaE;{&~}hzF*!9mGKQVSEclYXYDvR zF8bx%xS!riuvJ*u?q_(}u7jEFI=Im4&7AyypuqBDx7yS>b8&wG!Pyzm5%4>U%Bq#rkz{HMaaCx@}fj zVe6koS60e;tg^z_l*Q(rejOXAv=J`(;;pj6*;hpuBKc1Gb+EQQAG~d^59UtRu5dT@ z9hCaMvf7o6)qL+I+Ok@7KQl(c-}e5(;IwgF$}aYoWs{YBy8mEt^67qt$1jQQQ|a3R ztE@0Nwp`WfgUgeTZ|`LD06tGPH-ypcIyl{~gVpUicpaOMh%L?h_QC8|MAubxt^GQ< zeV^!pq#rZ=I@tXu(J?o+C*>Qh>YF8Ht^UIB^!J?T23Tc<<7xMr=(bp8h2;l`u8Pz* z)vtr+X?K<6+hCOyrbowk%=hcqw55$-L>Fh36}HFs4ykXnRaW@k-d`A>e9Y4%n}ze& zimtZQXWbvL{zcJ6NWLppyTbd_7tmi}e#WxA#xKF?DK%QfL%vyz^)@cVAl~Nu1!H%kp) z>;k@TtTl&LDP`P!)FZ5Y)fGNx9rTFQXB0n6xuz1)*fGx{bIAk6{egiYP_0T zF07Dejc?ERVX|?y!Tj`aCu6(ODmC@r9p}xv?tqytp4( z>ttiORykkz$xeggvge9afGHw`aWGb^b{OpRHzFuv-2Ay@CItyb9?9-Py8cxrdPPnvJ<&NI4{ zxDY=1pQyM!g9ga>?lfD@YG%yK6C8fx+(6&laA6YJbiOubq%nEl%MnLd2pygqUeAi-SxeQ9G|+Hb=y=3VZ)X}~0V%sz$X-WMOV z`zX;b#4KKx_l4gQU6BHbej#pQ_aCtfyPt_)*mcA(>^kBY#UETZQl{wOeEH#IXX_0e z)kbyd%kzz>G?&8{p8Pg0Lj&=Z#50;@OI&jrr%kkvn8xaHvTk%89M`hdytq9wmc%s% z?v-a~kHK+=Cd`XVP~&^pbYGEmyyxJ!DoYac5#QK(&llc8bYs@di;JhM#5hje_sQ@6 z2gg-fpQuwm?7r&@?_aCv5 z+v4Z_;^#J&pJnrVnv6$J*7mgi`$#sxGs?Ifm2oR8xiMSInO`Z=FlVYlR!BtKqhVym>v|V%fw4`nOKP~6DQGSVkEju zd_BIP~(w zH>~=}&z?B#U|V8;+bS7bNGGPjl@ED{XHXuQJj60Mi1DBNO*_Ofu*I^U7>4CPbi^+x zZ}p$p1u|``_9SjWK5M=Zv(RPY6}n8ULYIkC=rS=1>gR)=_yl(HH& zbi^bq`_-WpiAPY5{_-IfL0R%66Nexla-u&EsPREZI`Id6z7TsLjSuw@cd%?lmieZ} zgTMI@Z@}NwpJ@Me)xK4RID?g!JnV@hGv|DAPzAE>Pxau;s@kS8efSW=rVBwT_$Fr%ft(GnOFgJTl0rF zf#uIc`*W!L{EePHajS3CLu|nECo*vX=DAe|F#)T7>R?aY@&$Q_1#rKSru-)do~?UH z)h{ss{AAT>9eBonq{IIFOW(TmFUd zbs4_bW!PRX57*OQ>Y_cE9=j+*d+dpGrN8K4d9GF&I39mcHqrhXs(;q}gWs+4Tw!67 zCvKG|9}G=d#)zx+l4tBehMl=m2llZi&fk<{TVsm8V`9IKY14U-Z`a z!oIo;_v$jttIP1NF2lOmi4Hs9Tj|>glj4c4LpKU$~TcLz~Djsdaxa#_*`+AC+HNl=9@K4mi{* zj}8VUKWT~n-`Z@57-n#$bOxj{@QZI~& zf2fNOd`UUVD0_r0_5Q<^oCLN{1au zL*~k!xaBLJ2QVY;Q3v(Hi^xg&WltP`(+Apv6LlFz)MfZkmtjL)h6{BWCe&qkP?upr zU4{d()#?Kbh;8Vwk3Dg&eDD|SM_$W*xX&8vr=sV;GkGl^u_umf)eq}YH+AuW^Yrn7 z@pKu!(`DFBm*F~HhUs*%fAZlVacMk-f^(72fW+2;H>A6$hUpp$q}V|?pg1J#2gSg zW5TB#nRK@-8{*Azv8uD}yxVd_x~%($u)EQZCH=y+bMnsYite3l8#)e)ob`!28oblf z!8^S?c(-Lh-cal>bY`H`-`rihXju>AlXul@IU?HDUD1)w8z-`-Mx^wwYk>XW-HQra zynjl1xeM=!{X^a7({=XV6}d%hV~)tV+seBb>2K4ryyGy+-T!PU&k>O$hKI=!#Yyhc z<92wCio7>?wHzUz=GOhswzNJz;GHgmce)JT=`why%i!IS1GODDq`iXYk8*^{?WZO#!S_m?q? zyiY}b@mP+43EtgrxyAFbjL)|-?}dPOzF9rJ^zT*mJj@^W7+;If%_1+X-QNS=t($tx z!F(I_=@{|v3HR^U(u7_TdHMU}<(;Od-RB;s4gEvp%vY;1-`s8M=LuzgbZ>OP1Kv$r ze8H1S+WTX^xq*$Z=5QkRMLV5wKKdx`Bhr;-A3_DvAGsOx^!% z59{N@*03&vce)JT=`why%ix_pf55vB@)q$vllF^M86y74VXmAN=3&048a-)9}t%Xs8A*Yr>4VgCMjI9BY>V;=jch3C4+a~7AA@yTa~83kjQ&!sknc)+{i zzr69B5c%PM)#ONELG#9qrw-;z#@N1c1hBAK@L*|mKey@kfgHIkYPL(LuEx9BtmtU) zuJfZtp8Jyj(U#mYo+ZpWNA5Wo|0Tyu$dTbvX4A2uG0dN$6;_J5-qn}CFWgac@fOmR2c&C>K z?*i%C?xomY*1O4Cp_2 zr~60NPqRj@F#Zz@W;W;a^!HF`6BP-keed+7_dq1 z|0uJ=otP)w? z0W1EQ6ktDiSM}a;M-dtS^WzW5J8q|#I}*BhS>Mu5Xzu~<(zR&seJFC-Mo;Aan-*xl z;9apU%RCvR{H+%4Wj@acv|sS9_Os?*p7(V>nk>(YS%LNo-t}(V(Xm(3rxgEI-m&_< zd2sLz&pD9`E|4R=f_ItTyQA*!=J)T({FxhQzu=udKH!}$gLk?N-sv)Ur_11-e*c1Z zS@YI(q?i898~jWic|N+Lj(4uum$$?VdB^rLGvZBC2lwarIrG$!=Nik0gnoiQ9=4Nr z46iT?|2ER|qojY(XPw+%E6wA+IgWos9{6^+I`aH9M z%&#@^pUC%%wdU?}IYO_9-1fY~0p307a#f{2p0{2dd0sQ)ribyW{LOF&c()IfZ>Pp1Uu+{e^0~>ZbmWfuzVd!?Q`Vc!0qZ~8kOBS?ywhdyPM5*EbZ^`q zz7Jf?B|pg$yz@S*>0o`HwJ5(l-@N9olZPDK50Sq;@PK!x!^?#*pL-vYcV-LTrOo`l zhxO~s)#>WUvstC6gZp7)zTO`2Zq$o&A>99tw-2_ly|{DyZy~JrW_TF~c(-gpdL=jS zfN=v6Vu=yM%Hj)$?G&`i1h2 z^8~Zng;@@sXGdJ}6E4BK+{5ds@otdA;Q{ZKcPij{BI{<_|8l03f-n4fg7q`sft*`DA4?^d<`&%^yQ@wYo= zz`HFEvxfdA@(&fg+rT^Xv;1D3zb)2JJr8iXl<3B7f>(4RM zQ-6$_uP-~cQ15uwW$;dy!8=_B?{pcw(`E2Zm%%$-2Jdtky!(3TiV*IfvVX?N{6B9# z`{4s`jL842Os$SQZ!P>P&^dbk&!xh+%{Ro?Qp$*>$j`2jT-Wx2!`ViO960=rt9I26&e_r5yF%7d_En^-_C;5#^Itn- z4$pKgyha;A?)CEyx{eKQ?p)Y*n(K1D5mH}f_x?LOT;a_cIFC)9uT0o&i^V$xo#|#qqD+>eS>DYUR)Md_-mBwr?ZqT>R!KaxT||#r*lczP?woXLI%Q! zBYy7ST6nsmv+{xgu3iNgql)fV{ad*j6|CT#-ngIZLJ0|l2rJt2!Hf3#V8(&c?%&ex z%A|IiEBjuHeW}9|?SmcnN&t&B%r8ILJ49p#Xm1mP2hlfEw ztz*18Gut_I_;y#F_#uSx+)GP0GCEYB=Ul!x))mxAj@Ju|KDb%Om{xd>^TLnYT|+!W zq}>B<_jto_zMJkm_Ib4HS(F@a5hi`Krk$933tDQ%toaX-#BA$ zEpg58$x(V?)1!Yqa*d7cRxcy$bQ+CV?t}^XP?b-O*=35f-ve7$u~yw z{VMrVOyOR*?rtUZ6_fh5N`14WK3FyP-eSfyY4@14J4Lm7*IiQV3m5wiiha+;KJ_zQ z>Bns8$7$)uVCe_kx++Mr|G2@k_VI;j?ehSxwd-J8yAHm!>tI~FPW`mk zwqC)y+ruM`HD_l!yQT2Bc2-kAl`u;NRW**coat;lGscy_+0eu-&6%Z)i?1d)|Cq4B zRj;f3R6@9S(e#|g+rp!q-@jYoI`xH&udwe2{jwTGVuw0Ue6h?GJZ~6lj(PIOoJPUQ zqnwYju5@j!CC8G4f!ho!X2iUXa^_vK&h=o89N`xZp4-l0wETRsbK#CnuJ;Sd+!YoM z{`YHRSBAyTzPsaG-~YgowW8*u`*V!fz2ck$R-AG5ijX7i!o;Umc#Y@7PCIKz7^f^Alm@IcGPxt_=CYd3*hM*U8BvWPC%-4ChbAwa9rZykz%v;T7M=Iw`Dd z_cOd~*TKwo9o%fM4|e|h+{w7Fq>bGEHsI$7X}7=V`dIDC7HKxo9S~hnzfS#hSakJ8 zSI)14rT>xo#)+=6RaSU9x8!Rsx{v%in7WJTOwqZmvclD6Mb}+)J*=|A*7-%(PITF< zvclI{MAuDph5b4hyT0hgh_0jc7?l3NPT)g*jfz} zT~o~#_}kuJ7#!V~k}t_u!r`?{$>)>>|4pz78;PwB+mY!luA-^pgdH&Szmq@=3Zl@ppB;T`c{`o9h zvspzqQFNAH;P-n{UrW(_Xw@eS@05I{L>FfDS2*4!x>lkaV6`DEk53sRr(XxpKau(x zh|Xb^6{asO`NBk3(658*!$nt6bXL2t{b!RP%8vE7K`eka@XLEvyAlsb)?bMU*yjOp0lSXafL%v?z^)@kVAl~Ru&-Cd3a*!^ z;~TgjwX@UrC0v*G$afXl4!-qC1=qX!Wu3#Sc6Gh1FYBbl40d)Z>iVZ|Dd(BK9bMI0 z%XdfF7QS)lWL&KDtw!6#`A6)aQ;l-YSv7Jv&t$CbI=4y6O8j8OcOM#`FZkBkVD~cD z@+tECkS*f>me)3Rhs}02c=3a4&MEn>B5?%!^MhEzwEIt8S(bEh)=E9sH9@{-6HjOp z*WdN!!Z2sHw1Zs*v&eT_i7Avm@rU!90)?ClpEq~KjF)z0+xYE`wyx%ND>^sc?(d2& zBwMKxTgdtBva7`9*3K18XSs5p;k(L4bL;j3Mr_lu&Ve7UakYOc&oYTIYI}x>wga8c9gAIMFUb*ai9ys{6=m$cyT!TW?IG7_`9C|vAu1G` zXFU7H=S=8!*0mv{9MhJq=8O-<7-x5Fa;_?P(DhBxL42Pyiwr4aoZL0hnI~eSt5QSh zuf!w@#{T5mcfN`9yML#+W>plfkhnytQ>l!jdHOilrCsRimPdRgv58Tae=yEWIOB{< zb=P%mnY>$C;uBwW%ox#VU~bpu9hr?~(;Gz)qllb$!H5ld;T#n4&UHZkKLc@!q4S#= zVIM4TRw%y9wYji-H$Mb3gz3aEIh0zA^5?skq%)Z-swSVtsfBWhKTj{^F^) zPLl6ezm7P^g>$FkDvR#3&l2;om2KA%@38BLdDwNtJ?uJSA9fw_54(;Sh+Rh<#I7S2 zGF$qQRcsC_nP@Zdkg?L=htl5yR)1y7yNdYbOUakV?-$}Cm&AYnh|cNvAF+`(;^z$6 zZ-uw8{4DX2co~mlG9Ewp$AcJ2l#E-Pj9Xs+xDh9@k1w$j`#d0CV%HHfvFnJN*mcBC z>^kBnb{#Pkb(BrE{W&^9{3KA03XqAL1f~b6cPMjAg7U;m^mJk+x=frzmx+<+GVu{z zCN{#61-A8RgSZIC50KT71c`}o)B>4f2E;?Sa_oQ)u@LJB0`?IH;Yt}k#6Y-mM1!LV z#6L*qZ|WiTLETn<;vU#To#=^qSjQm}^OsdeRI!bEh;@*U5B3x1AfJ^^j6*L^e1rN) zPprSWO6PBEAg)1qKGaW4L${xJ24(+e|4?-VHmQC8k!S2rv@c58hYhNJiCF zP>1TH#3K^NA#pwui?Do6pNKp;ya#6<3s#`D{WAY*aPh%;}7Bvl(XuP`KIb4 zP34z(gUTnqq8zaX<`#8QhByO$q&`0EiE~ZN%MrGRsy_154zUF*4IO*p0r@4SfPbt! z#1m)-nS8_&EPv82aRlnM>L-SPZ26e@fj&OO4s@Bgfi4p>&}HHUx=gG=j;A~@pT!#*JapVFAvvS zKeWD%y`&W1#Td<#W>&P=Y?1ba3zq!i#lXPT|df|5~9hp6Gt35Sd|Id+U%h$>t z|2v+oKCmZl`9s+woX*cftaKP%FaQ4?d8Yr?9DvKQkNzdjpSA(^!{gR?k;gjnZ1o8a zx5{G=3~u=kJ^ao5q0eeuWlucN|Gfjo2j;f=LVX;0)@4{*m*H$(hOw>ou@}C!?864w z+LBd&g{#TW-^$;@)Z}AsVJ~~))I&OY_Qc7rY?CW{;@02ffuXJXm0tK6|5^Tqovrfd z;AX~)zwr}$;=29tGHI3#){$rOpkq&*_Napod*T813m?-y`BlBb##}9%Ir2Z25*VurTw@Y99_pPaf=)=U1Tp!oT|Xz`nW+_v$jttIP1NF2lO|{DE_=pGVOL z7}x3xHo>>p#fLG4ZAr&R=-^t*AIg4VTH3Sv4A1g6V?#PDi$Ccz^}w-|CyjhCEHe2? zhhMRmXNDSIVOR1pR`?liN0Hl z%wM0fN4S)Fu?ZO_wS0r!@F@Kvk80C@M~rY%P^rX!-KjE3+gf)h<$v}!+`v4%^~Z^Gdg?*`_Xsu zk_YZX#y|9tJ#ovQ$`8VOq*?1Rtf%`Q&eO*S#?xi^PM2XjU54v)8K%?ahOKL~?bYFb z=bit!kis$Nn^(?SpQe}dA3;?E<){D|whK%TiqvJ;PM2XjU54#+8Mf19*iM&WyPo@& zJBEvW<9m(ad`jentjA)eh+O82BAnNVT)k|QW2(sRKlaKikm*qUs z-N;=-D>{~mTzcXSb>6wns|=o*A_w*O+yUDeS+;v9f3I6o-yFAv*wSQ_V`{aQJm%6Z!6?lGq?II#D#zo&@(ZQb?O z40bFR`RC^$a$dG;fc>!Ds5)6ab0z&RjtoDZGkuHWD7p}%7?wO8kzb^pV5`uMIu$?Z$c57#s z^3Im_uBFZDh3%^5*{Ay7;>if{|2B8AL4O5f@8Yvvf?>OFhvZV@*S%JL&cC}ik6!E< zDe2`aw-x*2+zXyQkBJgF{CO^Q-Z^WpFC61VHh-$>fbFivta2XoHyxuzem3c_yz=|HJ7VmIUgp=kL6_wG~enUp?lS~h04Ar@vlN)yHd0JIp#_FpkpDyuwA(}IYY3&nd_j~|H^&; z_CF5h+aIoa>b&!mocGlHd=r#i&P%2=Hyy~~A^qJ;&VQvco0XXtGehKJKUU_vzp48l zw$sN4w$o+UPM2XjU54#+8Mf2s4{UdE?A~pQrTrNR+hu;{Fo&I4rsnJQzb89jyDI-Y zkC`Rui;6FEz;-7)-zmfVTybrKys|crnR3YMVEmD(VLI_&KJ%{`AFBD=EoE;xZ(G1D ze(i0{_mW=x>)SG313Y#|t%w56I=+D)Q2ekL0{|`2hdGcDlb{J6(qD^zyJ>=vNhD)=7P# zZ+6N2h%h_;>j}Z$dH>2Q&V}uUm0uMzR^%_w9F+MIX?EUoF=m6vzm3@}_M2wAQ*o;Q z#hymU`dPzld35|X{8=^ab#>nPc-HZrRU-f1y{9_w{5su7-qj*+x-~tT^Uj^G<#aH9 zOD-?-aNc>`vin=PU&3nrA?M8-nyJ?P=$S9_Ple<>vansks~zh8969G>Id9%HARV^T z%fog7^~=6%;a$ImFkkQF|3zMz`L(&}*Jm-Thfhb15&PQ)*bm#aYT@x<-=QUo3dw){{4RN=ad-3X4>9U~>G`{yUlXaO(KdWSpVSPE7+YE7qp1KR-paDb_FUQa^Sy%i%NQ6yC2p*joBgc zsLid^E1utdFe!M1$SF$Cm-T0Efd6^Lvpznsoi4+6x(wUtGHj>Iu$?}CV7uP!HhVnM z{%_sm<^EY}?#q@YhV^1c-6nG0e3|)rZsQpGKdxFUxu2GsOY^LEFhBdHFXMpis$SgU z;eHtX{_}0@S8n*WxR>#sn0;gjY?r!p6$kfYy)yOW_XVp1)<4*;&B{?8?)RAHjpV%f zS~E0bb`Q_LQY%t=V7mv;_p0^y(B!@z_9g%ME>=Cyx{VqZ1KWMKJKVwdgP~`}$@;j_ z%=ODC)n4Hn`_*~pU;oMyO8rsk>&tqxIbi*V?E?HGY^Texoi4+6Q3TQpWrrb?*TsRq^d@ z4>?H^BHc9mJwLFW*07z{uwBt3 zvps)RZdP^gC!W`@-spXQII{ABe81We-dE|e=Wq87IFt=1ZSk!+y2l*1Olhc3({?zUEocpZ;8K4QyBDv0uIYB>dLHvj2P@ zK3#lcGT&z(9N0kqy~UnP8n)Bru$|Vhoz}3O*07z{u$|Vhoz}2j@j>~x4(v*+8zzm3`A$~| zJoNlcz8o{vyJRz(1+Ie`xvVq`!ipVf!DA)$Q3Ju(`%o{|)a;{(^8} z$p_oVs(jQW(Cy12&WK9h&#i{~-Ptm>H0lJ7bn!dmCJ&ajHA0Wwc55tFzGa|Nmmbb{ zKgdtD3M1ZHvvw@!r=h^!1|yv2W#nh!gcIL*s79=2S~yUo?~6|MTZRy~&r%OwwEKe@ zpO)(`k?S7IxNfIV>BRV+NQ@7594PluNABaO+($vV5BSl(Ul_8G%u6eomnAYUIb~kp z$W)oXoHBpy67vU3GS4++o{QZapJ#ZoequfLCDsF`EHCR;Le|Zdb-N<#M!vJXzObeJ z9Ke^h4aT%>aHeg8HEkQbIpBeUv4wvW3rxSiU?ArMl7A`8S^c#KW8+)Y4mfSkIzR5> zepiL&WqUEUerJ=w!vnTD7YYs}ju@&|XmV`nT7TfzgYP=sBLjFQLnjALj(ziXv%s@S z1D)@xOOB^7=%@W(iv4(@e&FU=NIMvtgbI!kC$yxYHJl}cO^ZT6@dp>iP53J?1 zzA2vXyjtqzlzO!nRB{$bJvj9_>F*8c&q?$Lt8P4hHac9cJ6Wzf!n^Lt(8n^qzpm#9 zJR;+(EaQV&FUWl~mHU{UxDU9sh}>^+x!jBH&CF?d%)~$i8+fi9Jc-CHD znAUy{;9A=T+uAnx*0#a8whhj;pJ!P2?qBA_ZrI)=uyXZ&x57xtKN8+;`}^qF&pSH@ z3brp4>)hgL?>w`^wwq%c=M4{3o>MAT=e1{K&C7%bT#dNXYfTP3KBH8u^q=xv3Hw&* zThD#v)U3ed@}**rZ5trZL51*%gR`RDzFHWlakzBsi(*fE=bIa@cZ}{1tqiJ_|N(eL}ztd7ijQJq1dBoeZ6zeUF`?ETaSDgnA0dF)+6sz>{sE*&y|TCYWzVU z+rw|hn%?oK#5Ze&|Elz4Y{Gr-22Qlz8VfiN%4eo_c&Y!{Snh?h167J1jpZJCub1z< z_rfi)wp~9Aynp^^Z0dxza=*gI#fQ3iD}5a}weQoIZ&f?*{PWV|W1ack&IOuQnire? zN_$z~M&YVoRSitv|3_f%g}$-vPu}mHgWB^9FWWYl*|x#Wc7L#QcDcqx*q-qTVj4 z$9ZWHvArX<1yXMtZRO1LMzOsmw&h}r->>95i*2&l7Kv>qZH2G7?tHN=6B}cJvGa+I zu`p)(gR@JE?Y%@jY_PUn58k%>gSoR@SGYUB^!K*(wCSR-SD=c|bAHL-Dx9n6(5ePyw|F1Gg)Hn^TOpP8_6 zUD&>`*yf6jb>kW5?AE?t7{8p<)8m8lSvS_)tQ+UJWyQw$W=nrOwU4f%NCimBay<13qu&+2x#SAsZf!UH-kbcOJa3`p3@s%Xx#<<)7Q0taP7uCS2m;O@T-_ckuS7XSn^3 zcJ$7LFK>U<$$t7`pxxdz?(96>WPRs{pTB3m8@hZTFu!8?Sd;G_3lc-fx6pNuy|_70 zYi+ey|0kdH&WLUQK`f!m>`6|E8`cD>FWm3eOY0}=yC__~XRg4sZ7Ty`6?)knl-@5$ zOkvOcg#yXRivoj--s*lFcv|+@CE?yJo^s}VI5W^a?IpKP7x_*ov4s}Pr@FHzy%H$; z%E#{BYtQoCEWGsfdtx8F(<_i?`%mupN92E#Am_(V1!ly4Y2y!k`0ZBrm5u$qGi2NU z5^KmZ&k}F2ZNwaG8*zu1s@~$%zgQ*E@zB}W_FEqb5__0@qH5sW=vuxWy9#Z(b7VX5 zTWi8U?O5(~?>r&)(6(}$Ub*ZK5`#F{X|dDilXJ1WZH%F_;z?xyQi{m>hovEK-t**05OU`YnR)!r(f<($7>X^J zReDp(x^JU>SKaRAI~RKDpi}7Yyqn$}Unf@85kDzSe`MxWPP_}nLlC|_B<26ux-RJY#VWmd^b;uJ-+T_EbX;9k*ym# zdHK#mu6^N-bmqjyjeFG{+Mj=4wIlrK=DP#y-oH6EvC}*5UsHs^B&PA}!^wdXb*H%( zCO_u>T|oZ5pu{zjrk4$@`=n*0b=9J7v+w15ti(3nxxYZ5=GOX7qt~8_e%eL8KTCY0 z?)9X=2bHEe|Eyf+>()WOKTC`w$HqUMS^q3^UQSx#f2g*6f0j5$%JqxR_1D%oFTH%w zpHy7_eWb)XY#Z?o+eXa8wh{NRZNxron|$ZbVj~7(*CP&M+q`q`4SzcC$$dPPxDVnX zjpTlp%l+;)_bd6%6J%Zn%e)Mk8lM;9BEL;58@O+C%SZ#6zZ982Vk4(zo^O(Q&OhZb z_bT%&@saMb9xG)%#>;wKk@X-(GEvrTl&sruS+^mwZk&PJ>r1S}eh!G2*fwG&wvD)n zZ6kJK+lZgoHex88({qN;JMojuI{p*SrkuD*=JNPYJezXjCAyqgiPpqPv?fNPHSrOx ziH(@t39dn0gxmx3A|^t)X+u1OzfBGUu@LH#Z-JdSh{;_bPk|T+{bHjH@elel?Y%Q` z(b&ji;7r_ zkDqvk>7RFE8QcrkpdN7y`Zf1Q41=*@^X5n57xYV8G_ec*#zuSM7TEdQt1mGNt%+A? zO{_v|;uKmFqhKstBi{eAO!>*gCKwxKyc3tu<0B?PdtTHb9znUOLo9;pnf{4GpiLcl ze!Tn3xL=7snEU0O*aP+HpEj}wdF7^0dEPQK*XK-}daNJqi8b(U=9f5wc^2t|GjUTM z??0aJ%-@s~TQJw656;9*d#}F46ll-iyiC3`ZQ}El&C5q({9ay{#1R-9W2GH21j_gu z8}S3$@;C3q4zwn2pfxcAt%(5nsUU7s^?u1h)dK@5O=A?EIaygdGwcQn_4{i#P8*XK9D z$XDd&9pmGV=R4CMFV4gnmsulNpZlgQeR3vFJ#4(g_*%pFTEq6bJzP&e^zW^&Fg@eX z=wEoAa_*UN!}8QKa|FjzPW|{C-s!DB_u}2J@H^$?Ymt`+yPNCNhBI;U@2JB(!0fc8 zf363w(+};L2Us0#`iIkXIgGB`!{?d$7dGc!xE}q%<&1+D*W*l_zcc!mGjZxspXc|&;u#z31aoIvFX3&iVQsD9Y^`Bz#>aYc zKk)T$@;BGZCEDaWGoRE;60T;w(cA-P;@HuQ$^67KbI5gIX~xa`c}nf3VLW%F6_o)2MV%4y4W;AH;x#wPv3$eG43e9ZW` z9^>OTz%q?rxS00TVO%hArtu38Gj7VUb0)6G3kRF}+yiIgnZ_^ttJeqi)f(>A8s^m+ z-qjk`<$8>nalpCU7uUuH<1*g(Smb`?Ox%o*`kaZ=A9cL?awbkWnsS(ya$c0fvy}0o z9%tgrCHLsfr*JHqwv@xLwBzN~7k=gMjP(_EHT$Wz9|*V7mKWE6S^1l1hIe?Ce$1FT z6E|bU4yRIP>WF{Oq~TLt4x3UpV?PltWgP6k^b3=6UFxvr@F?S@jyE1*QR*2DhjM>r z&0$df#>RC`zO$J#*pqqV`t-+{IQ4kZ2Ikc5;Z5o@KIX*y#4~l#oQd;Jd*&F%q(0?b zgM4So`3!mE5w_I#4_9Kx#yd=@H9V;`EQx09jFW#i%iqQZLoyDt-teQ*j0<+8K6A(m zZv3|`{$aN_zu4#>UZfstfrb@LTQr=gHH@e=e5f^Ss5M-uHB6{AJg7A+s5KnOSj~FF zfZQM28^7=$<;*Yh0Q*svQD3;v+&}LypXtwAU*SDd?%l7jp4M<4+N>Xpr!{=1HEgFf zT&Fcmr}h0kdiVV|+xdt32j@?!SJm0QYNrd?W!7UeX~-^fd9GGkLv~t2c3MMrT0?eP zLv~t2c3MMrT0?ePLv~t2c3MMrT0?ePLv~t2c3MMrT0?ePLv~t2c3MMrT0?ePLv~t| z?W{Fqr!{1!HDsqXWT!P`r!{1!H7C`Z&c%W1CGkR96GA7qC%>jBwm4cTc8 z*=Y^gX${$F&1tRHoYZO!*=Y^gX)XItCe6vLE{E*2hU~P4?6ijLw1(`ohU~P4?6ijL zwC*Tmr!{1!HDsqXC#_mTc3MMrT0?ePLv~t2c3MMrT0?eP^DkMohU~P4?6ijLw1(`^ zX8(Zf(B^qh60+0vAv?6$KOj5wtBKDOWT)G6LW(y1Lw4wl=Tpc|YsgM($WCj>PHQjQ zInh6#4^BZT<au9op=FkR3W>{6co<@rlnfWT)HnFImxM{E!_wWBfvPT0?ePLv~t2 zc3P9|tTkk(HDsqXWT!P`r!{1!HDrf2`vYW$Hv1oBXUdsB?^l|n{I$gXN47KN%%8Xa z2-%^TuTQBjWM|6R{~$Z`zw^J)(`LRQJIc|t=Va2f_xcyIGn)Q5ku>$uke$)^W5^C| z?hmpo^6BoY#YpH+u%Oi2K(7I_|LY%fVK?| zv~94UZG#7G8%${1;6mF58`?Jb(6+&dwhd0SZLp$kgBNWZ%xK%-M%xBE+BW#nw!x6L z4UV*Ju%vB+Cv6){Y1`mR+Xh?OHu%!E!I-uU&a`c?rfq{aZ5zyK+u%;y27B5zc_y>i zI9;^s!J)Pd7PW2iJZG`Nq;@^H)V9Hw+Xg$^Hu%}L!O*r1j<#*Ev~7c@Z5vE&+u&;3=AD{az8k)_>%rKz4bHZ0u(oZ3 zw{4qunrdHHxZAD=d)qen+qS{rwha!qZLqj)gU4+fOm5rYa@z)*+cx;zw!!GO4NkXh zu)1x7*KHfjZrk8?+XlPaHu&AP!SJ>Xj<;>FylsQ$Z5yYxwhgYgZLqy<8?go3Mts4x5o54z#2IWGu?E{lyur2+bFgj19c&x12iwNUvTY*#9+`_gIyRdDdglek8fwi4U0ZJb=&Hcqc?8*vWXMy$iO5$~{V#5`;p zaSz)@?8CMZ|FCVuKx`Xv5Zgv9#I^}9WU&zwvFmZ-ZQF>A*fvhSZ5uHX+eWstZQ~T& zwh=F}ZNyA$8*vlcM(o745kIkQ@yLl}VMt~JFXAWW@3YhoW-6Zg=Xn1|NHJG3U& zp*3+1t%-4HO?*RZVjEf$*U*}nhStP0v?i9JHE|5BiD76>{6cGD7g`gy(3+Tq*2F8c zCRU*}aSE-8QD{wkLTh3ZS`(MhnwW&v#3QsO7NIq92(5`hXifYn1j~D z8?+|Ypfzy@t%)&cO?*LXVhdUmSJ0Z6g4VHXhO&maL zVgR~5{I50apQ*lZzt%9n)+XE8*kFD0xA!ibuQiOXHGHo%Y_Bz3uQg1sH9W61EUz^j zuQd#>wfS{tV}sqz-`=~NiE9nBYYne!4XbMnr)v$PYYm@k&6&8?aJkknxz_Nw*08wN zaJbemxYqEu*08tMaJSYlx7P5s*08qLaJJSkw$|{q)+XCI!zNsv@wYtRFtyh3wAQe+ z)^N1eFtpb2v(~V))^M}dFtgV1vevM&*5=opjSWUNfAbC>YYiJ~4Hs(-6Kf3*YYhu) z4F_ut18WWcY7P5p4fkpd^J)$6Y7OgZZL*z>4aPNpd+)-xTEn(l!?kF$|G~6c!?U_R zEUPsft2GR(+rzI~!>*a?3%6Zi?nveH6G!BcA53qOd7PyT%N0y)}Wo%pqXK0rHD?yZ-g9pyX^l!JC@qQ6PB2kof;gJ`e5pq`Jlza0jXs0!3 zXLL~ZKhQ2i%l-%28SUM#pq(9US~KhVzf?|mKw?acVR&!ZfHQlHO<_jwYuquhL6Ks(bP<)9tq+&}vtXooiQ z1KOGT-u^FWr!{D&HE5?bXs0!3XEggCXlFG0A82Q^xBm&+8SOnEf_7Sic3OjWXztgW zA3;0r$LxO`T~cl|Xh-=kiTMKUC};iR{$jqjf0_Lbw4*-z4f74!p(*#)SJ2Mr`2M%Z zYtQ^r4%(Uiz50T7)Mq^2dI{Q@@zEZ%qnz=1;}Nu@+^i>Phh{uq%l-%28O{C&+8NFK zfp%JhcI?mG?{NIPpdIB#gLdej66K(sY41I6a?FTk{AFakpq(i%#r+D}8GWN@&<@Rf zdHzR^713tCK|8HEPShH-(;BqXnj=J`86RlJ{V_j9MT2&fqrK-@&`#e!XlKfQ|KK^k z&t&N3MdkSvv@_+&CB>fz+L`tC?pM%`{>*xUcA3^!(9W!<_k7B+Aob0B@DEw(pZTU7 zv_sRM_dE#NX${)x`kG1(9UQ+FQA>)9Mx5MICsG*)|x@ zw!wL}4c4=5@Sbgh`D`29XWL-EvmbpF*|%+Sq}b{yDaqI59~uk)Ej>0bdUKPvBaJ)U z;Ct`_|2Y3(s~+JJ?w}bTL|U$R$~W~3jsQ-!n%JeAyLiO*$nS42_w{&}wpUv9eBw>_ z&9x^Y!2&<|wzv1@DztO^TDM_~Jkj|N7xwqPD*xkXVZ!LHFWg=o%SLAxujB7NTYgAa zxbV`3U)}fbs2^?kV#wdLIX}2tAvEdFq}cp-T14L--NpaGX?|R{MrhrMyz+y6;9pT^BSAHB!Sn-*X z1!MCTbcw$H<`DnL7ifEDD1B6(SZk+UwD9^!@se`^n|z(E}wP@*lX$ zbvuQwF8;-R=apNc-&YL#kF{saJwn;G?{>d?xlAJ+`0X8M?c+C z*gvTd|G55{P^n!L-QIP6iL843C*R4(n5*HT0)_5%_uR5AGQQt(U-@PHh`@0R3p z|5&g*QlWWo->2U)e`7;uy1f+LxN2r(PWk-4^?yiPVam!6^o|UeJvs8(`3WiKW!>P) zOE;8k`o27;kzeD_DQubiTaKn{#PVi!`($F^`E7qX6@CcE(pt(77Yc)} zzgRLbZ*uj(`u$hj_jd3@-J3#%YUK9Kj1-q2mo4n(t0ZeKEL!&KiSChQi=EV!1O4~D z#2W1g{S@5n2Akz~PM$yMZ#hSvKVi~SUmtUKRGsKgdU>;d#{KahcRu@l)Gd}a+xJ7& zKK=&Bq^+>&=Z{u%&)j}KeaNpXeYHOIJ`15SjX(2s{dRn0$=hX8D#_f#sPEQW@1}o| zJv#b@?EV^;xbCq~cuUjRZJXytTl}=dKPq*ET=%C?k2`wCCVXGSeQR$ar((Tfe2zkI zes+KC=ieK`4s#GEw$(C9~R$dAqm)Be`r-RRAaPmGNGrgzHF-+3nUg^S$Z(%16L_(;EnHBu^Y zOekzSzR))tgEvfw6d2kt@|8eKgsSjc` z^ImnEub$)lQM|w0Z5owDyB=Z1T3P(OW_>|Gs>Dj_QY>kDhn`Ty1Ln5<2i$G@57^uGAMm&BKVWd%f573k|A579{{fHN{sShr z{Rdobe=a$8wVwm{+xBUkUzeW7V`{F-76aO(+_Cz?oW%aoKSe)=5uzW4? zA76|AI4%C;KJg#$yzM_YuEl?B5&v=RrMUlq>o1A_C@TKrLc)K*_MeFV_*MB2_XPkKVBFAQB(W}N4PD-f84742gkVhkFnxErX~CbN4d8D@Q!oEfBY=| zyt`HW#|z>=mWcnTivN%}z~Y4en4It* z!~*7v|9DIM$LaUu{)2b`{-e71k9Ed>NKC-?AH)T0|3PdZi~o@LK$hoJVg&YcN}Rxc zPKg!R{)3}n+kX%<*d_j>p!ko}g#RFJfd4oq{-aXDe-Jysf9x0k@p!_2a5UUk{6~B7 zAG3}BkQf5~qn`MWP{Mx@N3i_|$He)?f2b!{R?46#v2T@f7hN zYsG&wN%#-q3=7k z*zjJ?z^>h$oWVIij?Q^fzUN5%p>}kJvnt%$>0NTVFXf7S1|F)jDR?55MV>#AVh*hrG#I00X)<|L%cRi63+f(k8`%l`d8!G1<&iB~x@jp+w z$0|G$yZr>N}*I{&! z*hT%@9*#~tG9mI@sfSYfUgkUD$KmdO^TzaSa|hecgv480!<(28J$~4SVQ6+s|$B^ zSBkA&bUuiFx$7r>nbGY{Sp?ivCeo+DhExt58>Wwb+VZ?G?Q$Hew&2g}b_` zVoSZ;D|$t2#6Q-EZI###CTzq&Y#VV9+eR$pj&(qKR8FrGOe zOJ7`f+TYkm_UFUly3=E^*ZS=9rxeZ?csfrX`O&*0;VomPI6L2aJu-c7a{BY#<=K#U zN%h}qMxUNNA(HyZeJPi&%6CVJnGA3LMM{Co<0Hj})k(RZ?}-vOS=poW#0*KmHz1cQ!WSA^P~ASO{gNoH)q8e|zcOnbJdvf9P^z zADR1$r#qWIiFug6W&U zPW*v#W8+NR)c0(ObZ1j8&s)azu@P_3?f*B@o#{6|7mWYEm+oxFM=Zho?cImO5ws?T zpzqJ5J7?5Mq&uT|{coi^oBoIqWL%5&Nu)bt%s8)`tZ9h|F6=WO`kBk`I~om-L&`K zh1HGbI{%Y&XWjn)i*#q*|NqT&XU1XDoz3%U(w#HLC|sTKx6CI@jW)LbD&0AwzyFZ# zY}$IyK_cDR*#4_@XHyO{^S61KbZ1lkKTUTw?O|j7_CCwp@Bbj(IioM0KjC1lVPNz8 zV>ju}rjAK>MtipZrF3W0AB=0R6`%jC>CU>`q&u_U@iOVo>{Z_Wmq>Ru>uu7V&Gk&W zvniJ|0`LCKerM91&Hf6z>g)5*bj^J64zp?vubOMo=YL9fHs$et|NH6AUj0P6Gvnt6 zn_*I}AMZ=nSEW09HepfTP5FOHcczT7dUX=%&Zdn{cc#w&Q@S&4{yXW;-gRWmj2p%@ zRJELJj#&7yF>CQ%*bZ4V=x-&KyP~SiNmuWnSbZ7H<`>)cS zGv*+X?rik`G~L;>|9_wE-150+pUs@_oD{w%y~Lb={M=nU-}zr1&v!Ojo&_)8*=VWn z5n_Ej3=(Iy`4U7YKjZn zdF8#grQcSviEHwmlbT;jf3klgsUOdGZvJY~NX0|-xnD0objYe;|beCIK*bc*;sswVa0`OeL^JQ`_sb5*Gy&v$NKzIWtM z?JClLJl{E~P@hPX?<>2o9pf8QII&c)s(PbKN8Ro~bJR$Mc<=|Isn>Ypv?i ze>~rL^MX4fAM~s#_2c=@&AZ(ed8JfssUOdGPMTaL@cV#3{A`b;e}B4(j4z(=JSOF}^l#exT+XJw@_!ylpLEQT>&Nq*S+Cae zd~hc1m5VMUn)7I*d45d3vsqu0?`*WmcQ%^yXWDNrRls*ghpy6pJm2}{Ik|j|hdw0z z$Mc>4_WOOmBt0(UkLNoN_;85tMC*Ps{&>Fgm0C~xzP{8)#vjjjZg>Bd6!~An%Y4N1 zovXDSn%?`k--YeSKe;j`f27cP<|m%-ob+Lx$lo8;k@3g#oyWY?GV*Q1S~9=!eCIJ6 z>O@|ba?X?)@0iE-q%V5xHkqGzzH`#xI_aMt56bxC`ObR>*NW^7)s_C^`OfWHycW6k zVrl6=p6`4pZAIkOdy7c_C%y43EU_TcwOujkKc4TrWA77@z2mCL_~ZG`fsbaUH(!z> zcnR1>F&VtSJ!1KX5u+cmpCf|8ym*3MnOylzy&v%~k;A4@^MXS0d-+4jR`H|+o z7L)mk=R3c=ru{iX}?|0 zNhhY~b4|W;gN-vIlfN$^KwQZ-DH7NiUUlIRmA>+5b5I zq}=R(Cf|99?0;X#{%7)?&HiWdom(dMKh8V7@h0{^&N@9U``-`je~El&v;UcVXZF9o zvj4$$UVXFwCGwrk{%7)?SugfKlkc3d|0VLBDfj$iBHtP9eSQ=9&St$$zVnX6{s-G} z|7QO)`Ocrn{uhw_&*VElC;MNz?0+WT`99hIZj=2FwqyVD%l=o8{V$R4d{*|qXJ!90 z`OaqlgYCE;``-j_|BL54FO&VRpzMDp-}z11|BA}~2is9UWB*I!I~SJyZ<6ePupQ-% z>&5p!*pB%eylq$d&$9oSeCMRZ{%7)?lM?$M=a1Ak^Jns%&HR~s=W~_jrCh5g>y^lN zo;&VD%Bd}#_&yL{zux|oSAK8fntbPTqdrQX(X)v>|M7h1y?a(fhTdMpHTllnf2|(9 zd%GLue9>F~f1ZdQ{IOHi==E-eIaV{onOACYIMP^TcRf4`NWu=kxK80|4QUL-+J=R$c#ry@%_YGzcI;o zN7h~!|B=XdPTE+lS;Izm%JU!3cdpc6Qpzn8?{iJQ^O#BF)B7Lv@qH#^{&wY;=QEM- zoV2q|+&?Gsoxl2}bhN-{yQ8oj&&#-<|B9@ib3O{&v3^6^T#NJxoQcAA=;LQjN2YJQ z9Oa*~Qh&mqzYbQ{36G@4W28 z9Fbo{n|$X}qQ4al+i^XkO}?|yCg0g;lkZ&X(}t0wi)-`!&1)a}ZfxYOkIKpX$Mc=f z-jEg<@OvSd|9HN0nTHQVI^CVa`*~;Pr?`7Ql2+;u`F;@3cV0R1dgPTMzeP>HbILo} zqMy_~9X0vRqu`YRa9Tjxo))J@AHdHY3u%cjw`L}*H{7v0|F zw?xj3Ip`ZOlxtK79jQFU4RzZenYH_z?~6xhTO)Mg^jmK2qCZEr{FBXJ`EJ_Q50!lE zT{ruK=Ob-9@;mO)g6AVShv)UbAE51> zAzzp2ZsCbPMSg9b-M_aD*KHU2_2fi%^U|*(=bkz5+aUSLaO1_s&%4uZ-yFGI`x{@W z(X{OmIz6_B`%v9wkwe*C-|Shm?H#H=IOtAFnig3Zn&UhBA=iB-H2K@o?x237BYzDY z>Dwx~s&HiEuYQcSE!rirXRhCu^JmU2UI^7K@Mg66et+a}n2m$;9yp;9jwiJrJr zGqUi^jFjYmq^&SzshLTUkGGYLZ2h`a$_iOGxN<<*X-yi*bDC>T{5gd!r|lfoBrLXf z=ErUDCC@=Ku}w-m&oJiqiD!It!UkvB&kL+++u+S9xfVKkTefvNe^4V@;w#D55az7Z zWwEnp)N{^>x%;C{XG(61aOc=Fzd9$5yy|=~W1Kszx|iD$>V3FuplZqIojtxi?(4UV z@Xi*`RV^3j{KN#Oc9HMhl|K!a`;COo^xkF0J_Iy>90P^?akA%jR#mlzILtl}i>M7`Vu6r!BDbH^A-Lc8h`Y)&W4@fRNta^3F zKkhS~=SI61pX2}aH*)JwhCc0i+I=&;Gy2>k`~3B*$y^Du&i;Ic+ppuh(WcF}`cv!j z44n@Z8(uwn=hvm9eaesVZ#mEXUJ5<8({<9<4T~%sKi{|RIP-iZbmg@|fxI(1MP@vj zE#+b6N*MO?mUJh%_<2*)OGui=#Fu-Sjlm9D-<>t)Qsvh7-TcUIna zi_<4ZjmWhgJb$^ueVw%zH^GP5x%C@`Wcn zus`MQjU^+m-@Cq9lz9=hU0m&%jp0KjBgfl);_aRAt^J(Bxb|}j=i1LHtb1|7g=no( zT_ShvspU)kfq56BD{b>j&EVe;&>H)(@xD8|!YW_=EN%M_c-r= z`ww{B_8&00?LXjh`#FHk?dJeKx1R$T-S!`FdKOO{6|~y9}n{HoFv~lE#W_|&x`vHSiZ6NkDTH^ z3N4BI4|v}8A22=s<1Xb-4 zKe{FS2b_=pm@fXKZo+@S`uLAN;y=13{0F>m`wy6ZocNC(;y)UT|5z>l1Mau|2khTV z{Kr?~Kh`Gv2mEjQ4`Kj^#ecLG|Iyd@4~YX9|B;gLAH)LiA4|l46iN6G;sN*%SNun5 z<3A)OVEYf^0=EAkHju@CNPHm6b1E?c`#B}w*?vxm75r}b58?&3{~%_7|M*t?$MuB& zAZ~#FxL5qg?1cXyc5qJo$4>DdHzoWB@q^dIe=HRLF~Il_i6J~G{^JYrA8iu;gE)fi zKZqr~D*j`X_>XQG{$o+NzW9#@;y)fp_zz+V--`dZP5j3{3I9P{A&dWz*uqxvADzU1 zlo$VTLi`8ug(~7dmW%&rDgL9p_zz+XVeucox9u2do$w#T8I1p!oA4jR8f^bTyun@% zVh;9t5O>&c%ahJ+H)eNA?|C{ps=r)UVh@#kZ#X3job^x7{b00Y6a3biaJwQkoew`h z;!mkr&9{BE_$WDFzBYYDbm!on{#V-Macq{wCWO5H2bYx5s>w!SI;QvU6A z(d9413bvi^E`I5xKl0&VzE6hlEdEJs!klI9i7lT*`wWo$P&sdY{rZVmsdZ!AV$~jU zU%C9Km+!oF)Y;hLJomUI7L0VSbm`%pJFm$7eXM1LHtxM!2DxhsJt2NS5}saaeJsz` ze(swOv~#~`F8`)U@|_Fz8ylMyn&SRer=ojvyTS6^#|_8om5x2TWVQRz*Sn$@Z8kgMAslI#`7yTaiJu>U#Qju-h&o|3{P3B7C7pI<> zkurUA$w;Y{>CLv|_a%n0=!5DT*BmSvsk-mu`1df0V?-*Y6MQ8>~r)~KJ~dFjjmr}9t3g2XiX-tNT44}8(tHMNOT zfA?_t4)SUEyW*8&cRlyE^Z1VI{(_g}dyd35ep>XG`_QZ3ZHUEj6{#x2fykqUj3(=d!=G>MP?QuZbO1|@!y1z%8i0zH+Nzo2s zBks|@+V9Z@V!NC-DcVtN#6B8S|2^7BY>mWrKW!!c(M@a(#nv`aj~Iw;BMxHQh=sg( zTU)o|@(-PSb&EtAEtc<{5)WDM$$GcN>ebHSsYU&(ugiCSiHUr=F=wps@rBObTUPjQ zJ}b|L#6=#uuU71{ucteCCggW|e;~QilJES_?v}Bgoklr1z79AaPmp<*_(;1S?u%6! zmEycxXP|R-zx?}1Isd+~@dL4ihxYnQ51#5we`lDN@7(62jrsP>m zzVo~T<(%wyREp$V_wmNq9(gt-UQ(**#>k~%r6Mb0KR4Sd&k!+_Na_@&V6O5EgzmA`K|b)aOV;)M7!MC`==>=Qq+Kl{W`I9vZP@gjbbS&z-6iJN3D&*kM0 zoA$&@bUCpSt%;LpO^ifq;v-rU8_}A$2>A(RWV~hNMNEWplaE0>#NkJ4}17!I`)oA90R&zlnTI ziE)@*8~P!>fi`uBZP4Cm;u^X?Vj7g0>l4q=?TKa3uW3&lgTIZ97=|hH-X(rP*>Jfg zFJc#5&#W(T3-tQLio(Hc# z`izf1;%Vx8`7#oVz|P;;h(mbwW@8*AiPRxO_Vq)V( zyn(Tp>l16x*CWorb3z+l#29#|jdu?cU!cCR5nJGTXvRTY!5c@S4lxDVVdF(Sfx6_r zdHHV=OECRmCyv0o_qV(gL!dpH{!PAf#+W5`K-n8o=XH4zH{d#J@6?nLqd*ZRQWQ*X3|M?alrN(^Jk^8H34p=9=;6rB%lL(+-a3oi<*5VR%!Q`tZ9c z_v$Bp-r3wU+-~ag4zu%4UyK1>H|@CwtggqW^POo2qtli;X!txnuK4E@Huw6WOkQxg z?hhu%ZmtiH>-Mm?*KeXd98P^>gTc|}1%Fd!_CMI0>zVa|yU}KSU~XLwZ)*)}Yt5Os z)-X2LW53`=P-8!8OqqGX&h$rn>cP#Fnf(uDrp)Yr@Urd?R;Jwa2PfSgpVJWO5Fr}=qjGbT8gGSeOgrp)wj@|{gN>`R&1|KMJ(m9hT` z^U^-!`4HYko96@8)#Y%m>6>>L*J$d&x88f=9$;HEW8ekX@}6 z?fBqE;4ZTsn@IzAnagvv(i*ta8o1LMxYHWA(;B$b8o1LMxHI~V`ybp0+-VKmX${;N z&HVv)M$;a+%O&@}TJ9eW-05--w$baQe4Vrh?sPeDr!{b=HE?J8=lZ~%F6RJSYv4|6 z;0|rB58UZ;;Lh~V_<=iJ4%`{-U0>kN==lAQ&7?V;*5$yR(X$S?ezxkqjbAJMNl&6Wl?5*E=Pp=XEp=b`9P5D3Gd~C{Sf2xD5 zx4@n0|241w9iBGNKZnbd8x7oP&7rc^z@65>9s1w#AIrEt;|K02H{%EH(EpDAw~X@m z_|IpwkB|RSM*s2gU-7gVFL0;F!$Gmuz@65>oz}pe)*K9L4cuuB+-VKmX${GmA%qSs1&o+scA&Gq|IU*HaH#>auKY0vn9J2dy#NyZ1 z;Ewu?Kf9Czcj%1q%i$-Q@z-Gd0(YjpH-3RT)Bgne7r4{c=de?2;7)7cPHW&!Yv4|6 z;7)7cPHXXZnKb|J9$gOHX${<=*&lX!&)*zRoBa>Cqn!QiH7N(~&?`mP;(i70O!z#W?PWPbzh&}RPw?$FHlJ}C$8(DeT+^#$(GT;KDj0(a<){ZHV|lsEO} zf0?Jbem7~)VI<|;-*@ycaECU}7jUOFaHlnJr!{bgWaH;V@DDChorQeWT>&HQ-&K;RC|{H0M};0{gyeEvvyMzdbbKZkfmd;Un^ zPHW&!Yv4|6;7)7cPHPV9_V1gT@>TAI&Y-tP2b)%U)q&}<+Qw$F!FE~e90#$VZjPxe1UOQ0-FlU z4`>aa&Rm83=coE|=4~7Z`$q&zwS8OqD;WA>wKH@J`?D4a!By}14|rO(XIzC+WodXPbCt6){V9*YSFGYIH91-~X zxxv8~F8nNOxhd4)kFwE{z5Z}gua6A&7_-TNMQ28aM>ot`A)^YujL3+Xmm-HW=5o!MU~#*6mOrzp7k+Q@ z$PEJuejB*q^y9&+2XhDD-=%rqi{$)FMo4ZZ)`N#hWT>q|9aPH=C02Y2()te&q;9*SlPD0%eD<>wry~;ZG)ZdeTc)+Ec=x(be4Th zINJ6f9!qE0C+`f~`zcIq@4Ild?LRm;wfAxO+TQPB>@0pkINSCgu(n+f-nRRLxwBkX zxZ55d>}}r%{B7Sa2duVVg~M(C0gK!743FFE0h8P72A5}D->0+K;B&hkjBeZDblV22 z+ctRJw!!Q*#D5$T|8X_pKRA@VV)+l){g=v>d{YwsQ50q5KPgM(cB$BKmixJ~>Ayl?vt4s;ue|F}#1 z$64_o)x>|m{dbA~7?AKE^~8U0usc`$$K474v0eNJ{BQdYVgNI3{~>XJPU1hl7ynUN z{KqlzAH)I{i2wLe{6_)tAAa#4!~^X0B_?3ohzr;@Vgt5~_<(IAMqt~B6WBIl1-Ad- zFxcKti5XlK|FJybKiY`@Aa3xk?9Ua&fBYr>qq_JHVh1}0j@z(|47AuNDM*yg{k5fh$GnkgIK~8xepGHH;BJ%DE=}c{<63DOJWSg#eX~{{-Z|1uM%gl=Z{!} zZ6n@b+lV>XHsTJy92gNjSEzH~xoX3L1K(OB|8t$RaK5K<7 zCcI(im+r9}CkJXjBmZ-c5_biNLENybe5}JQ#{zvmdLTIJ|85WRD^b5Czc36{!V zBiN;KrvR~tGrgNdi%#nn+;jD}zynK*2Z%>px%p_Mzym#k7drkL*cLwK{ZFTkq+N<^ z{1AH{B8_Cvp#q9_>1j>w_hwA{PyEiS>N>Vgnh#!^NMr`p8BRx@cin{ zd>0MZ?=;!>&X3K5t*X=res=hPLyY2N*W3K3kK_#wk?{rZ-6Nl0H#}m;_ zeN-e+=+Nar=e3=K?_DksAf7QNtwNymlyiYMFLVvw*>tZ%Ok?1znt@NhJRIoybC2K) zTi2+cR){Qucy}ras?B{^P zaN9=A#I_MPv2DaoY#Z?t+eQqfm>j5+JWy6%#7{Eov6(b+lg#D0T4_zZL~CLtS`#PH zniz@J#7DFyHlj6g5i|)T<>W<7q`15@el+nAt%-$bO&o;wBoR=b7zpJ?6aUcV#6Gkp z?m>N$65`jZk)g|b_WGIX6YJ3JiF0U8jKlOtd*T~L$NTSO)$fr>6VuS+C!UeH|KVP_ znIGa9)Hj+KhStO{Xm941*ag}=AH*%RCT5{E@d~YpRcK9|LTh3aS`(jOJS6OK|HLLJ zXMK1+iA&rh?NJ%^6pn+3Z_3a@dVnN^(B_Td>Bm}L6;LlFxtBxi63Z9>_BVc23iv{(3*IG z*2D_5CQiV3|6Sh_p3YcLi4U0i@%1Q^NfQ^KJxNQ<4>1AilYq(m5f7jo9e*CGWYWX| zD8G^@CkCL~!~c|%oD^Tr+8Nj9^9lFs@xlDmH{*x*(dP34>zn$_FPyJ6jIT9(uQhD1 zHC&H2&m&B4uID|^!t+{lCayIcPkXc9$^PS&8x6nfa@bvKxSjsY{s*%g&HTXYnd%Fx zYYnGs4WsM!@VU|44`<@KK3uLfOiq0>et2A$!{U_x`~3IHq+xJf4u8|$%rESXHtR{k zv(_-T*6_C0u(sB4w$?DV*6=msF`p0EnsRf0Ny629p3VHj)Ox>zr;Uz3Z!cxKKRBBH z%>D;Mb3L>F!Ovzq-t#W(Ou3m4xY>*^{`ng3)i?VeyiB>-|6pbMGv#nHk2 zv;V=zW;}epVdER+o%Y^-C|pdr+5cc-H2rzw5gw+#S#Qq7&3y1Y!NKPKyw8g;u+iTB zC;Y25?5j20t2NB4HN2}etgAJg%XrQD!nkO&f55lsjPVQGn)cr3LAaK3_9x~KrbV-# zdGjMYYc%r#%c3*(L*ZDX;H11J^V_!+5ccybA9i55N<`A=K*F#^E^`S@oJ*n zn@?d?(;f|{Y7L`m4WDWao1)G04ws_M=K&^Vyv&z(zrv%ezqub+l=_$C{=D%Ehno9C z!=RKie--7OGjUVDl4#hI`WL0VioC;}TEm=1d-a7k(OjSYU`@tj#tUbf@iYH0CYt+0 z!3`x1^4}LW5z2{BX5pCMT zjix`!VMcVu^B}xP{fzq+Ry6Is@e3zv4I^p|A8HL7Y7G}^4HIe&4{8kyY7GaX&G=xz zOyd{+)AtAanfA;d+($X{<;{;UpV8iY3h$xm9}Vl7{%8;9(Vv-b7*A{XPHWgsYq(Bp zm`>~V-CFnW+4J9g=hs)PN&Ytf0;krWO&h>=nf2IA8n(+^o~xDCu$|Vhoz}3O*07z{ zu$|Vhoz}2jt=9e0zLW9(v#)1;*zU(Q1sbCNE}lObwwrxwVH)?d@|Urxu-z+579<}L zz2xm&X|Ub8X4Ad<-P>~QYS?b#;bmTVp)zM0z;^3TEN<|<)Gu;nK`Lyw`qe*D>Hphb z`lZ2k$=gq)9u_@#Wsx+w-%yJVgT3or`njz1UqAHd#Sv+=&;3S98f>?{W~a16qJJ5* zH5Ing<*=P@58Gw#zf-2`b7rl_2ixiK!*=@qIH!K6<)~D~Q}2Mg8n)B(1Ka8OgYESE z!ghN8VLPp1JFQ_mtzkQ@VLPp1JFQ{6g&jJiek0?(>y`Ip{#S)=YI)ilf4+sa(qOw! zhW1NiJzwmUKUJQGP~9of)!bkCNkh|MyEE(F_SR?XN0rlHyOJAfuVB4a-cTnQwtFPG zb29zcocmq_d4585?;Y>0U$dOsk~v$Rwl3^FUk{WY*8sLVIjo{retA@-2C&^%7q6wV zo|C5>Uk%&U>+?VZ^vxZ*CBt^UL#tAG{%#w;HW{{Cyy~H}qoNN_a#LZu6Te*X$|vp| zFVEA-Q2T{1c;js`bW{aL*FqCImiFowZ#JerY*%VlPw#m@p6}jd*ly=%jlAdik4E8S z*iO$sXTw^%;m)zqIdEPJZ=k6HVV zOy=|7v+In8u$>+sY^TQ$+g)n1+Z%7a-7}J5JAMDKoz}3O*07z{u$|Vho&J2nc9}lE z!giTH-@HP(^)B7K6r}rn=PVZl^U8em_*iP?%u$?Z4?R0zC zE>r))cKZ6TogN=-r^gT5>HCB2^!~>gtDYa&PR}1~r{@>8%RK*2XVS2pE{E;3hV8V5 z?X-sNw1(|YjhO81e-F>t-T=1yWAjg`>>uw8`F?ehuwCvG|owLxB1ZfJRQzcrU7iX_5C(!?0>Ipt0kZRv0=Uc!FIjx8*>ZKf1dX1 zlQ|dF`=56vdabhOZ~q!NZ8hhidjEs%hFAK`+rQ37mZ!pYdjEs%N=GVq{;}Q6`^Dc* z4(t67wk!B>wubEIZ>PSr8n(N?c5`q44J4R`8@vh?=z;=57_2a1nt#qV^?JZ|TEli)!**K3c3Q)B zTEljF|AX!H{sG(Ny57)`$M?PT_JD3Ur~H# z8f>@y;B0SxPTjU(HEdUM!@J(|{B-$44Pm{hOk}awwJum^EBt#RM;+i@a@%no)12>i0>!if%*4%kRji%Nx!}1*i@fJ)XtS!a|Lh9aXkFg(-+MdMX$adbX8qO?NrVIKR#I7`~K7M!#t^+0cs7~X${+H4cloA+i4BkX${+H z4cloA+wE`G%=O*juY2>?p!S=ooaNo|#$(>|xn|%f*?$gant#|%uMcdeHEgFf zY^ODBr!{P+HD`4bA3o>aGx6DG4WC@)tB}I~P$EttnTQ^XC zluH<}+|=r^^PBoK8*uOMzVq+#KN3FKs!D5r>`Jy?&33QI@2@gf@>+!je}ArhY*@KR zo0Yq}tpDiP|3lY#$46CseV8I50#XGJy%Wz)KJOp<+5MiGd+(Hc=j?OlWFtR% zDm>WG-#c2nSJ&jONAC2E=_WrACQLZyhY`_{=Q}6Abhx3feClvHwoLG3@$u2}mpddE zDH!tITxS^j48egLCP#bCYn$Aw^n<J|BCz`}?- z%T15o|48fPmuEcYD|5q8xo(5tcTL`jeidz%{CL~0zWRrUNZcoos~)`Qo)2csecjHv z+6Ozj{eU0c{=$&%xWJKRWc&`w_-&N&t0vPrCB~Q@Zm8SGw~HTe>#*(zU^u zt_{v~ZLp?mgEw6p%$aBM6Hw`xX ziu)hmAKg;^_T*}>&-e8j(1+Myu*utJqK~wxkUZnhrM`vhdPuxAGkCK5*U_edJChG> zpX2K^wJ$Ne;9E=PM&G@!Me?6N_w}ur@Emcu;HWk~MYh~JH2K~WOH;NzWbWtSmU}B_ zt$$-&^6+mDCLfU>BZf(DuJnE6^b13i3uZ4#DJQunaOuP|>Cs{3{K;c)f5o>e|1)yk zy}>h|oQ>|8RW5mWkeAxHN)V6ZnbHOITlG!i3alGN<4;}I~|EI0lldO2hjoJOi{Mqo^3Ojvk zpMKcvOYZyaa(%p_m=$Nc7c#P zzc8(9gKJ$IZ0p+KTh|8Tx;8l1wZXdOw%3amf45I^x8enSjs9fKDH9sMabk4dymra? z%Cz#0pC~_6D9jt3`DJui|3=9_fA*R$p~@f``*NWx-yDtBexz#h@*0bLw|pW$)+p@z zQ;%!W<)I?UgBL`6n{R%cH7B$(qj2`lL%%n?IAFJLWB*QOfAjgTOJ)D|$)Sd|79a8L zIP;LqQPt2Lo6BTh==^oViseuGrmt@!asTQe>V5h7p@uc2-Y%&J54-1siMj5+V3Fj0 z_PUbqO#9w?v}*DSuYIsF{b;880Ux{lg^`QNxYUwyc~r(FMaBhAcE=A^c5U#oYlE3x z8{F*LU}tw7f}eA(SHjS_)-~a1*MGp$x!1`SA$L86soixKu6F$gZ0)Y&@U^?%!`QC> z;O~lE{{d^e_26yyd@y&e>k4Yo3A-(CL!!=DlVai#H{$-BgVJS_eLj>muO4z)T_d3MCKp{0E%x z`VUyYtoVc19KL(2bfcIVh0rTTOhRinA5z+F#?8Dz`4dqLLS^Wr}e#eWbt zV12$_*5@z1^_kegFS3sJmUaADZyhIouuA;LT_=8T_>=gL_4p5oA>bG0EI-unPS1Z3 zM{xZIv4mls|EQMZKNcJR(dx+W4gcr)Ba`ne*L|&Nk>o-4x)N8&Zdb&H`RSo=674@@s(Smm;bGuy!gf$zHx!4&EEB+&n<{9%Dyl8(TW3ouatT& zK#Zg9if5x|K7BN~!1Hx|W732NCC>5HkO!jgFMlez=Z$52vrdgPKiuWohK+oZ}$2@Jfx%aH+@a4zvJnz?00vQap`%j zcJe}RT!@P>etTv7R(az`Y=m)sVf()gOWSdl_z3ec<=}twL5!rT%-hS$vm0j1y!DcK zBTnMZFR>E$J|JG=+K8FBHsU6(jo686BYxuAh@tQUVdUG8LrDB2UdMjm*_IPGi7$`+ zz_TqUUZTs1m1s?zL~CLsS`#19n%Ice#6`Fk`4`xUiP+EBiHDG@gN=7$A=I-k;vn{T z>JS4V7swt%`~yuLauSGr@EIF99mG8tC+ee#dGMJ!XyP5@&e;0II;d|oaSmNhj6)w! zd?Wt+o#U=gTtl~ym z$DTOH)1KJ_HqJx*Kx<+LS`#6)foI0w{J^Kg1!ybd!FUl9pxr-snmuvOe@b3O zq?}j)*GCuQ{1OMSn(-tCppS?DIY0Af&M)kbW<0q*+^^dQ^XvA*``kzNo`v;w|KWVC zVSKIOd#z!6t>Jpw!}+np^!Br{3(wo}<(+(I>e&|@Z;y|)u|`(Sc3_2~~h zPW@QlXn#-Bf3#W8g~M%K-eGXc7$1&vDKoYgk)rI9qEN zTWk2*_CK~>k1;>+N&cK!AB3yvFZZKa--M|--mZV}G;QM^H0z=F1J9hx)EAEC{G5a1 zU}!$`GWV_UGvh)T^$(RTfVhiNa`+>gS-9M3sT`-Owi_5}k|hBo~b z{?!`x)f(>A8s^m+-qjk`)f&!ae7NUm4~$ED(X4;)Eo&Vw`Ul(6HZ=1G*K$1Lfq#H$ zY4`W?;{JeVDgRDh#$O1_a(%o0!Ligo;gyU3HJbI)_*2;vr=0OM=NES6`Z?>La4Y59 zFR}4oYT8Tt(eSEX|6ohcZ7*%WdRBPDOjt}Dnm(oAl6!Uk>O?|!((0_Q8@}J~o z+Al21`F6>xxRk@8lpmLu@i)StoQwO7KIHL!;Fd+3DQfqipYgm%& z9hKKja!xpsW0Je!pN(oa5<(sV}^U=4Iwf zSdq5UKg#V7JX;MT>T>u{YuHe0xKL}DP-}QlYgkZgI1o*r%zOz0qG@kz{J%B*Wxa}x z{~s+?hT{^q90Y zX|UZd)yrl~5?%Qz$$u5LD|BjR!fesqpL$U8ABzSvuiTfx@!KEUwhXp=@QW^qb44fR zc{mZa`|9>CNz+6(ns-62S2OteZyzpuNA$60?~?r1I>Bc?T$0H7ufOY88P5j6%U*e0R z{q6HhKJW72*v>{_sV~ZB0+%pNKxt;b0nUH|*Jm(z*VcA~#Du^6ucN z{l6zLevdXjC;7;GgT2}pO?p#wz0S3hV7r!Q+b40p+k34u`OaTmekGmq0mTE7PjoUk zwL+ov$)f+cIZ^Vd&je?+K9Ej(=k|DueEeY3?ISa|pZ1^en|$ZzGY+KB6aC=9h}}qk;;-!RW3B|vDRk1mv`$Z z`P$_}NiA1rERyoN`$qCShQ>{7XZrWY3zgGgyOs~PPvm)ASG0)aqg4(4Q*Co1Oyz<3&6ImZmukDfs+qIq&YhSDC z#gkw=-G12apTx51+%IRZyIaP;Ze0IiJ3St-oz}3O*07z{u$_KBVY|5JSJ*D@`4+a* z>mO_vzdp1G>GcD))9VXtr`JFBWcB(4+v)WSwu@WegzfbD2ixg#*iIi0+r^z<*iK&` zw$tr{?R5KLJKaCnPOpFLvFh=G?ezG;c6xkayZG@R6sKW3T@Kr64cloA+i4BkX${+b z@2_K?@0U)#WAdFx)t{Qq^MB@4Y9f24EBEwBWW8AMiahtic6$AT?S9(xRl;;BzgoKa zGWJQ|u0Jkuis%*3?KJt$6K>d-hQB=f%|6seO z4cjH+FW>!llB~BALwfy#?QZyVMLO+E`!zWYw$tk$Y**{{S?R2oA2iJne>)|l*FV_q z;0v?Ndj3TAj6~S({Lwz9{Zoc!C&G4z{;ZHOL5{z;x`;fVZ-w;w2iskHWqlg$TXFTJ zMD{t`{Bp?nzbaE7PGgVrfq!D{8$TdjzCX-}8-MmT^?bl~TEli)!**K3c3Q)BTEljF z{e$iF`T^Sw&YQ=4|2Q;eUm|Sx&DhdO^r!P*#S&q=b64{$XFZ#IOulaj+lB8flEn9u zF8xL(!ge2S`#6F1ZT5jlN$gYV^$)fywP{8jzCZ0)o-X6RIMn|2+2;GxmqoXleCLFV zvokpVbNlKhknbF~{t4Uds`YRN>+AZT?-ze3Y!|L=zF%E_B{31U8+!bXbiO}@3SVCb zw$uHG?X-sNw1(}phV9OG?3$4#=j+kGM+R&+d41#y>N(b;__L{OjpC zx0!tBZDUF#@w`necfZMZexl3S1pLRs>O+!XyROIDneQjhPrEM(w!7*5`AH+Cyw0ha z>FgcebGn)t|DW5;mivEgT>oJ^JwC9V*07z{uwDMAt7Xu>j`izHzW=6Bm+L>TgMYoh z`55{BusPK9(Nbpqn@+ej9k#nKxYevL2Rpo!0NVuzmook|Wy^sC*zSj35%YZ|?*kR( z?~%5Lx{sWa@wS|AarNVg>;-0&OELFHiOjjO{_G4bI$I^^pko?rr!{P+HEgFf zY^ODBr!{P+HEgFfY^ODBx3a1)W1+OC$EF?py;|t@yC)^m{<43MO=NHHg{Kduaeth8 z=8W9`heCnACC&GX_H{o`fbI6ot!L)D$Y&8*e~yHn?_1n_zqsl6)Mc<;iTWR#`hOLe zmH^x7@rUj7{J?fv!**K3c3Q)BTEli)vsaf^@$pE{z{+sV@rfzz=bQL_)7-Z4xoogq z?s^59xi%QjwZVC=4c2pQ@Sbaf`CJ>^=h|Suc9(`nMlM?!-m)W$O$qfIyBa*Vc7P+NY znoZwOJi2C1Ryf~_$5WE~k7lnUcqFSv^!S&N@E?``O*u7pl$@_F!M}U-iLN}hCY-)C(U+&q2)S;9;LTh6MSuHiO?bepy1smOl3NhS zRS#Zt&j&N+zHaAS?SmcNe!!1ze_=>>T;Ry>W&B#p_+9nJ50-St8J=|K1EzH64X$+O z7q)b5@TF^mFDpjT*9LF8Hkk9vi{+zrn?}M9p87Rq!yobwn}s{aG@TNyyLVl< z-o%G}&))dF?3*nQrhXQV&Tq9bye@Hyui=D&GH)w`Z8vO*&OWd)+_cqfUtOR419D-| z5k0?%b}zj#yrJPl-&>O@5{Yz`5 zH2v7z&%tZ6s`~PdO%IoP{YZGDm+ySF-Tjeoe*QB2%^Ouyb}WnSIqsAW*>4=-Tc{80-M6CPHgfeXFbMq z6#TG$!R*UrH-&%rVY@HA#UpavbHTM!ug`wC(x&j-^V@v8mOUc<{WZ8@{nhA~JvN0W zor?M{?|t0tOFsP2k!VJ6Q#dVkiLZIV-aLcB`@e}qOAg%_zWCr&-+Nz@+k7q9tn-5C zeT6oJWz>A{mVHiac|%V<-7i|@(wcD9wsn0o>&SB?EZb;W-ss|Wnc>rKZ%)Z`!92eq zcYa}7*9Obd~*p%Apd;H)MA#xG9{s|4!es z#hqo2s)hzXUN}4L+l}E9HTL*^c)yd`=cL{Zg*Jr?c=h06_k1w%K)LSZZn5h=&2@!~ zX zZLqVu4#Cg4)+_#I*zq6ik-Gi^md?FSwg|cFDNOCIyKuGZKVWNj9fz;o^&ZA{{RjEZ zuK$3w-Foo0dp?*u*L8)v-S)xWZa?5}x4$sBJ1%gz>p#rj9y{XVb*MGq9Bk>=l{_`Jj{4?S|T8sbK@A(h%ohS5e7db5cW4Px(;Ca`7 z!1OcQ-yb4*R0q?v11Ln^b|B)d6W4ic{-Qqvse*8x@@gEaC{{j2sKk|zI z_}KFw@W1OnhygSa|8eC8pJ;Z-p7XNYWf1_o6o{?*Q zi3zxKPF%pX5gTxA#0Oj(F#^{{oWQjaD{%b>@d9@}C1%iC*4_OR)`t&>|EMhfgSf!~ zS)UKd`kYVv$2{>L#14kYI(~!rkL}_=ddoUa{DAeoqxg><)_+J00l%<7{K5^MUm%X) z`VV3W)H@`8<~phOx%e643C-nvb-nZL6@Ns&GuIv4U0qk=3c37;#1@)MKWd2o*x>m= z;tQ{czg!{yvX1!6FT`IGV@MYN@q+k|GU7k>iT@zZ;Eo@$2G>Ts!L<={aBajLS`O4nHa5Eb)gEA03VsYO*ODU9;HNuuks) zF^K*r3uphiYh!rsPkVerT6YrPy&}}RW9jS@^*4rt@15{feNFOkB^EJsQQqv)E#-UD zqz%5wA9dmTWazcZo1#~dKJoh!lZdpN5Pf;%x^V4s5Ba{%lE0&rxJ0A+ zZKEX*XNL#3uk3qzhVX~PCa!;VYV>@O_2DMZb@4@~@|`deppOKYVIq_<@UW_}(fx(Ck6)7&LTrv81mq_^V zj~7!uKRt@?v7sj$Rftx)FA{F@z;7uxU6Q{gm6*i~t6E3Tre%l!O|Rg~-XL=%aSM0+ zh+VkjO#H&N5yNn8#4%p3Ryuo2%Eqw2?Frw~r4N~X>XcF?vM-I;7~Zq;u&?aycJlm6 zJmaMsvdhXnS>w=7-+@Lf_W! z`^fxm3tivx)98S-4dGE0U-oq`Bzff$-?*#LxahTWYr~r>hkT!|l)r107{|cjiP6dJ zqT#B2Zt(q9L3lyp9NoSs6uqutR(RpTy(#TJ8Ey8lT^sQZ*GA04wGsDlZNxrY8}Sd< zMhwKY5eIQ?#6pHhKb~tF4cGGeVd5du--Exb4X>5{enx*KCUT>UON)#R;gR0B5Eo(m zmdNvcZmr!jo0JjG;x#o@&YEm*d9;3M3)mQ(V94k z*2GA(CO)Dyu@S9_i`e`G-ie8jf5B&7#6zqm2ZLCMJ)Rs3;vnRnU_%oF;WIe{{N^I@ z5AqPKW>1{l7qrRmk+=uPQzw=$QPXJ7Ve%s+-a)<(ZQ*>xI>-&N<-|F3IWZ2~p4j;x za?am5?)tD==KxOu;+{QcVtd|a%hNS=v?flWH8BdUiBE7H+7xU5GSfbCeoVfM#3jhZB43#CA|^q9`Ta$c-zV`1 z#+&>U=7(4W^T+upCl0}9&c|_b{}|0RnHToNDWm_Uzh1sGn)xK|Kt0aS^@%yy>wEbx zru=_WZt{I3)60^>=Y*m%jFxO}EB=Khp80-t#{xF6*HH~Bbreu*FG`5<!0YCr(*TKBL41_)LBo_XjZn`gc-Zj34m; z#_zbiOun1M0yy6hd6DnNo;c;?doiBG0QB+jKV!-Ga(&pJ@!}Z97w*^XgZXv);eE!E zv86w-zV1JquQiOXHGHo%Y_GM=cjkWNIxs!s!)IRn&tt2ZV_4oE&v?P{v>#jS`D$Ux zIUntX-|4T_u)ALW;C7Cuju{_ec6&bN6JBRLX-n*R>0ri_>roD;>v9-f9}l0$onP2o zUmq^l?SsiVw{0&xZqH}>D=cp7GbeDk9v>LoYT9h`o$dI--gf*r5Bbho!`xcK+giig zTEp2|!`NEG*JxghFKo^8&ls5XO}LtS(XKBrHT}hA@?nLi8Q+|IJz;4yeKhNXm+wqp zc)`$=@tJz?vn`_yHs3jCe1)57i@hGqY_D&g58-9X$;UPKldv*%?fM5N^L+g%FH>I_ zS+9TaF`DPuv0WdHU)U4Z8b;I_KGYgE)EX|-8Ya{l9@H8Z)EW+CYHSYVLGkbb-S-;&msTiJFn~4Kj}*4m;6J=oRa*f0!`!f_&5#Q#g`XorZsG*HEgFf zY^ODBr!{P+HEgFfZ1+IRY6*WzdymfgARV@QfBWSG+Q0p!0qL+^k$c)2J-SpC@=2Q= z$@h0U<)^PaVDg-1_NMjt!ghN6VLPp1JFQ_mtzkQ@VLPp1JFQ{61-Cz!&UiIHw?)QtS@5=+^UVB} z?9)-&zdTrB{N^;q|6;h1jPJ_exdAN_sNem-0?8N73{LMq(ai7Sf93b}gzY|l@H=yS z;(ZI#VY|Qk7B%<#)pPvb_vYY=3SXG|Q$MVb0o(mqWRaP#!LQzL@|`!duWah~xx0|$ z&+ZKt?Ni<8>RVowe6fSUL1!x`{vp>-IQCEyf6MyW*@R`ZuhUm~6WK=|T)I>eW2GQaQqzsx%mC_gb~o8(WO3)XzJoGIThVnqUMw|o3h)4my@o6YY# z56POHL4PmQd_vlPC3x`V1LpcYM?NL_kJo~&cifc0`SL$MaT)u>_nsbS#`AdoQ}TP4 z`9rmv9yEIK=~LYQAwB-Eoz}3O*07z{u$|Vhoz}3O*09~Y^A*f|JW})(p07~XMYGKF z8f^ZT`F-b$`D&Txr(D8M=J%ZoSL$f`|HP8UlCNDpH2(A1=J-C()aQ8&g@(Ok>Q9Ky zs14hlopZ^IU)H+Laz9iJogUlNj91dhmFD-Ii@$r9IsS&SpUU%IGwyiUZujrQP5FbP zOGrN7ouLjVQqA#|hSW)i?e3_0hnfEYUwy~(9@l=@?%<4x%b1@wy$;L$S~sr$u$>+c z*iLKMPHWgsYuHXdpRirr^DAr@_k0W6>Gcn`i(emFg!K9W+v)WMw$tk$Y^T>J_G9(> z1>5QMk9}CZ{=s&-9JbTP!*+4!7q-*ahwXIxU_0G@*iQG4eOA5x!FGClU^_j2u$>-X z*e-tj2gPaFPM5=WTEli)!**K3c3Q)B7f07I_t$gZ{V4a(n9z(npE37;+Jocd_ibMc zCFjpdyDInBxsoke&qI3sW3RM-#|3Gu_ghC+mgnW=(9LI?nftfn#MAQos;`8KmY!~& zkHN#b$@)1yq}M;#uISh{8LVHAY$=oh+v)X>J<)4RuFK&1b$;z9zo$Pjq}M<8Lbn{q zlkvBdfBI8$2K%6T{e$hcetpFBXZkHo8f-!Z@M{C3hCiL|#&vziI)alU$J zgel+KhVKtE;>I7g)AIq_X${+H4cloA+i4BkX${-y^$)hw>mPfUYb0@)@t>I;`+eu- z&*eA$J$0M>eT=YOgH1P@`CPI3Mf3a4e-Hk_)PMf@U((5Uo}K!VDW7@b0a@=pj9dR; zyS{t!neuBR&Y9nLo-nVkIlfMtAJbvGZ96|O&&Tq?hB`$?$IoB7Q5jU7j)$$I%|T>oJ^Jsz-~*07z{u-%M-1u~e=t8aW@ z@|_#x8=rAW^y$qFOuqA#!?z?}7M=3rOXl~TyY)#(XTHA}wp0E-W<}_U$DTL$^N9JI z(%ECoo^#2R4?b0w?+>Bp-`HdPVe&hD)7e|xe5rU6>-VN>;LdgF&Y2W zq4GOMn*QIEcOl<5LKA$|&H2x*E11q+V#{MS&GF-_&6RxrNGSi~_00XZYQl{P+s*iLKMPHWh%&}$!> z=k4PoKcvHU>El|P_Fwi*lkdZuL(?M-P5Tx-_E|b?cgy~1=KISBP2~HJu-(#U7nuI{ z>i)Z|KifiCC*Dox{w#m^F1{~?ZYueY(UVt9OlL1}N7kceJWgg#ll5n3Xi~lh&G??m z_|W{mv(~Vk*07z{u$|Vhoz}3O*07z{u$|Vh-9y!Dnft%`>JH-n4~8CYbHI$pwgBB_Ev8v-{*zxrfi;WbnnIU6Jfjii*7gfd%5ZSzWI^R?fE;I<7eHm-2A?C zue%qR@xEcy?ljm=k3Vdu=Lfda8n)9Kw$mE6(;BwZn!UOiw_O*=fB7|Eo@aXck8XIB z_?tu zTO}_>`0wzdLV@Qy_x06&y{ErXr-#k{-k~`+1(v)T^nF>lm;e5<=67kEmHxU^pj!Rg ze8v9l<=;M5e)ClJ`34@lB@k|XBBgkjKK@F3S}|6^S6;6W$R0f@)Kn14F-qS?G$+F z-zOu#)Og4LZo?ai_XRtz>JUh-(kfD5!rT7(ONzwy7^^l9^t$I%)`E=5{>d9EnSI9I zOREPyoqM{`yO&<`x4W;ITz7MD?E12Sn)!XcN2b2)e{)o8v)@>9Z23T=z{wQf{;~c~ zF1D0=ZFg`=LQ>%3d0CF%9^v0mp@Cd?Z*b*L4FgTOAIm!a{S<$RdNO12D%>%c6R4(h$BFp^EdRCO{o(x`1ek`!;KsA5!%?JG-T+L(l zA@jZ7FYxxuOZ+>VUiN=eJ3Ezp=aaWT6R0q7o&UbG`BP^%o0|%=cD$}%pw#tm_y?Z7 zE_K|~Yg6IY@l78N^!r~q|C*ks{Et0Q(CkZ&UK$8|IQfG{S4J=PZ|Gl3u6reTU)99G z4S&zfT0ZDCf2F@0$T(jMmRNXap!E1ES*Hh$@_%`Es@U>|I&G;OxT9vRRWI)8@4xq> z`(&Rcf2c&XM4s+TdE(2HUze_|~<-xULP(b#1Wj{u(6% zUmdOG%kyzB|A9~Bx3GnG-+BA?K6rQU3*2M@dFgNbjG>wYWOz0+P-xcF&l-xJcl8PdMf(mvSO?FW49_7_IpC*$(9 zj7tF-mxD4caI!mou(E4|mt7mo?AqXF*9JSg>k#~$YrPVN&b6)yN4x$5md?FSwg|cF zDNOCIyKuGZKVWNj9fz;o^&ZA{{Rf=w`VUy!tp{(r=YzR(U01lr;+2C`x9*pkV;B?mptGhOM-L=8&Veubj7Jib_ zN&H7s@gHzI{-d4vkMG5Q{7?J`?2iBVLj1=;@gL>Hf57jq|A67~ANPv?xIz5Kz2ZOM z_&+`W@xJFjVEOLiKho!)ZZt>yM`!UL@Vx6kVES9de?(JHrX-91_(=Q*T<`i1*gh=& zqq+Exlj1*$iT{A_UH<{&?-T!Veo48k$Had$75@R}yZ!^#&ny1pP4OSQ#eWbh>HK{BK`yRA1nT2-;@s;T^0YaUi=6A@A?m70L8_B zloJ1OO#H`6{Dnz{~#XV&Mz?m*G62xwGkU| zZNvv$8!-acMx4O45i4;02k`=TJtb!Fqxg@d;y=cU|5z;kgFRvVM;}?A-}d|mv4e`T zjvtnFyq2uvEyRDwcObEIieK<~et|fG>pzGk+#&UT^8Cki<3ARg z^DUC|osjb#5`RQYf$N@->)!485B`oim;aF10{&y3^ka(5 zi7|X5{^JMnANj<891{OQoWUJGVhyg1c!O&r=HS|hI~<%`EYPk}*~o8u2l{VLX)n)j zM(AwQ zy>@;2z2a!TV&~_js7iND)4}7xL?CxgI`KGyBQzvh^=vy&lgn!x4 zerDf!)TO^uZ?Cr4w|37f{&C-R<-2I;vu?El(;v@R{d9P$fA-&j05OVJhWZ1wXSK+B zyUs-aEiLYn=Qk3n`0zb}DqrVK>GJU)|B)>z0b&)+`;`p58$FS-?X_P1cgFC4JF-I` zzH(z=@Pmc2E)03bKX6Sa*$-V4TJ%-1z>_~*$eQ`}bN>F7WvpaR+8sY)7w$L{zi@5D zFkBmPjNzGs0$*I1m{q=C)zo}{%ugkjari+0!2HPEl#C+fQx9EEPbHr5Sd*s$y*~~3 z^Y$o`nv#7ym6%534Q&IH+I-@_?&gdB7tR+od)9~NGz=84b<*Ez?jHZ@Tknzi-4-hF z_N{?$cjQkkzBKHAd$;@sx5PIJG`Wy^u4~EEZ392_zh71Ur-sBh%1`mDD}UCi=(qd&cZzyEft-u8o+7Ya{OA+K7F)HsT+yjTne) zBM#!)h=o-3`Y~7faY*_>JS3mj->;;gfqp)S570L9r<_&-FPU_27QpKA2y(AKs_G=3Md)>+Al*`C7yHTEq8R!}eOk z^;*O9Xxhv(LcTNoqRq@X`OdVD7xxJqPkrj}lJ&=Eo(FTk3%~Pj*B96wowNQ3x0`Vl z8`pr@(RTfV*SVHm|6p}JK5)7&htc)%@Oj+%h0W3S`fxegwhtz!Keqkwxb7b;PJOOv z?l<9ZJw7lv_3ik<-)QFAjHj@-*5o^D4RdP^Z)*)}YYk^>4P$E!U!&;{?S!qlm&mcB zjd_Ht(cF(F|5upW-mkpF)7-bLAEv&rH1%0y=o1``w&MvyqwSc&&wS=yVa#A>+GE#0 zxS6)GzLb@Bm|3rX@G|G7KIO17pY8ewC+qbOM%L>ee2nIKugv*{jaf^-lUG&Ia53j+ zy))~lFfrG+>mNK!dpX{$f5O7C`#iS(!NHX0^j8=dZRZ>Q)f)EI8t&B^=G7YB)f(2- z8qVd~JUjFQ#?{XUd`p>K|6p6@6HOc8TIQAY&&;ndE%yOq#Cg~gM{~b$U07DHe{d}8 zqg@Zh|K_Zx+z0S0*I~}g`Gs9+4;uR?!mZS^?SonA4{Hr}coofO<^@*O{fASvhEcVK zPql_k>A$@OTuS@tKkb1@@w4RfVuwfZi@QV{e=RJ^eZY8dAHbosjr+Hhyu+Yek8)FA z_!CWCuF0M_nq$m-$)32qpBNXIlX~Qf8~-l6N%UpAmlKyA1Pd z+An+J{H*~t^E?VSqFG<6$uThFY5B}tGw$#rx%6DieE$(vw7K=>`-$W`YYii64IgR^ z8)^*~Y7G-=4G(G!3u+Aq9+&pffAW!GK-QL=_6z^HSYVLGkbb?Y#w=YQ$Wn`-6F z$UFQ-|B+FPzBe<1}a&UtXY@)}Wo%pq+LtkBwftP@xS$>%&`ysZXs5>)wA14c+G!2i zX${(G4cci9+G!2iX${)#>-wshpWho+mGN8_3=FSs#`D;3RiS)F&;5C+jPJ@| zudde;>0cnbd>UxizvK_*{-|H`D2#nYc2Eh&Oh&YBa!>_+B<&p^UibAzcj~he4$ekXgB`47Uur? zw{TBMUpp84X#T6||49Af#oC&mcMhK@YwquN(#D%~=dtJWnfr5cC;2%_LA&dhU7tk# z>(j@|_{dgqtMlgk-S;h&`OX`f`{o(bo=V9bB>gLYXvOzu&H47VJ;?nZ(&Nw8u-2fR z)}Wo%pqF2VB{T3;hG5nZq367%!UPfom=hVH$oU?ONYXYHq^ zyyU2v=I5O|m#u7$KhrVF^BZ?OTf31CH=6SAg545HciuIi<1)^-b6VQnpq*}?K|9rc z&~DrQB4$0fz5i?(|GIJg2krEDfOcAgc3OjWT7!1_`2_9ao?k(`xaV8YPOpEUUHtmc zBBa+3wr2JE0@~^Ik1bifK7n?6{Q~Xu`Ul$S^$)bu<)EED9<+-)zo4DIK4_=g2iocO zgLb-qpq*a-*kaY=1KR2F1MT$qvaK3F{)6H)Xs63TJFP)GtwB4jK|8HMyZy5Mv0l`E zx_~^-V?u{AFPi72?*0YK@(9|^8GM~tpN1^0k;ZnZUjIP5!X-myeS7c7jq<#_9O_x* zq&eTNzaCfy+Ff|+8*{#-rL*MuA0N`|A80q|@+PxhEW5jUnn63Y{(*LnEInY>*VM_f zz6sjt^$)bWAnPB`>w>9G(m^}D{(*Lj58RtU|C*L4odMeE^$)b0J2KfkulYaDn*rM8 zonP6UZ|a0Q%+EU)3!F69FL&b)l5Y4`NUwjO-F1UYBys+>zsb+r3)j!ApyTDj8 ze=8%??g8yaU%Jk$N3XyCfc(7h!qB_Rn;3ud?4xyLeS1H2eZKr=eA>P7O#*1QdGuN{ zeh)?4CW3Z){R8dBMzc(L=!3no{w)rrmaA<1$xV%FCxUkKyz{qM+FjCZKZ;-f1nn-) zi_L#ZnVK^GOG6LrNi^*(lUXVOv^(4HIiu_TJS7pd)BOkSv-Xixu+N%-64^X1Q<1np)FJDJ9M{Z)ky=I5P#Qe$k zg?E~C=NI1DY|g){!J0at-FG){HP2V?j8_ubN^D-|`6SLiHT$PJpxx3Lo72&=hum&{ z-ua)aQ_S`DKUYBfdv?fI_P&Jcs9)`g+MwN?d$WvxUb1n5-2ZFi`VZRa@d53$2JN&4 z?S31X-(0U|ks2o5x#`@6X8ekEJjD0s&@&x&C-S^b&zF|Q)?jjv?@a$5m|7{3bmzi- z@)&=1se_zH&~AIZtEPQByZytz!w7}ecQN;8h5xlSKkuAbtdcq3Ck<|vKd;&usyZUg z)c^YJQRe5JwFd392JN&4?X(8%vDgSxqu# z{4!X=7X_N;wvEqagY9zHE6~if!Fa9>&U0isTliCworuJgf)ZasL>Uc;UbX0*2duG=}+ z`Cv!4AMhi`>i)uz)MHHTxWJLrTPL={UO!+-YhXdNx=T+YYh)VUHCawe?DQ*MLc}alc&h>cOShc8cwQJyy0IvC-yV zJsW&#=lrnPxYuA*dktHUbZ2b)!o@9fO)aew;DNfUe4IE#Afdo z*q8I|7TXDLT;N}hWvuMDu)Rq=j{VoG2M1G+=l8O`u55EsFI#NidiCI8_k1uhHl7c= zzQD!UIQDDre6TS#-4FN}8)I+#3nOz~+G6i3IN8>-ehXH1ZSb;dgPC0$-0a$5XKZ>M zf}gR)tyjX(*y7eT;b?5yf56i8BYvH1Ve0Ai6sD#g*R}owuC~{(_dRTlP5Te{+S=@T z4`Xva?LXjb_k6InTMyoL&j)i`+kf-hCD-|2Z?_-tH^=J!!r;`?;{u0MPx}v89Gf0z zcpRIa511UAo;SE0n|`L*c6Dv=xod;bT^pS4+F*6p2Cus|n4SJ={{gpCZ>!9CHSd{* z-L0*F1LnuJM{L);zQO(2 z@MXJfe`Q#OB?_j&lcag%=cp1=Eb&BZ0GHDB_4oH-&e#0 zTpMu#*G6o>wGkh1ZNvy%8*u{HMy!Cb*ZzZefvsoPQ(^|x<9oyo=?8v>xB)iC#jdf$ z4zMx5jJ@$k5 zuEZ5MHqL)YYylh3`Cs0A5MRK?eUS4#M`8?|kNdf_T$eTzXP};*H)0L0jd+7=Bj(`R zh&y28JKS-vEyNzMF{b!c?jhn2)@IvG3<4YLL3wXnNO#8erPwxl#}bRcMt{$EHsTTV zjXD2WY-WxmCSmLSWRI1&1oc=abG{46wlg-`$GQ=l-%N8X>$6>Rh*5Apu5qi^KH?PE z80Q;on`Ijs8)KUD9FguUHlEdNuPwwZxGwhr{=-~X;uh4?<45emp3kla#4lVMF$~v6 z90MEoDgBK-znjc;xyC=XzY@=|_b|h164$`S_~p!z#5Qa_yS@VzD%~inIrCd zJ8#5EsHf+bSP3@$OcO70ZNyAm8*vlYM(o735kGNl#8611xF%clq^J=;iPz)fG;x#o z@&e7YCSIa7u@bF`lW0whL~G(BS`!=5nz)G7w3(QQ)wG{@h}OhH(4>aM>R&OM6a=*N z*QBS~{uBSO$D4E$iG5hj`H6d=c~LI?jnl+Cbba>3wIgC>(uX_ zqhs~^=4jeSJcHwH|A=LvZU2a4XiW@5YvLDHa}2Qyt4;qUZlN_X3$2M)Xicm_YvL4I z6Qj_Y_=MKPCbVWxTx((yTC*pP<`o-{>^Myv!j?0Ba{uHUZ~80o2YWs=u?Jh_coS=|<-8MTu-c5L#2B@{u3$CC6H`Fj7x4r>+xwkZ zLR@``BUsJ5-2XY+^he?cw%nXwVh36iH_)1xf!4$ev?f-dHE{x~W8+`OXxm=m1GYRi zALVkiX^+GOY&n{k0NNf;JOIu8&pWXITc3B*ovr5j!~oFtc=+Fz^A7uK4fk73`(S>w zJs#e-n)bo^wtlSr_r_@$Uzfx8TEq5Q!}V5keVE>AbAI7@tx0!A+xEfn=>OJ-7IF3A zcYC}UA7OW^W9@HaG&Xa;3$ydtYIr@azOcI1aJtqoI+~ZcAB4}f?kIcWx;|WPwW%*m zZZ++L$I-Tbu(&RV!?lLNwT8c~HuEd&Z8hhIyS0Y7wT8E~hPAbZv$ck?wT7>?hOM>E zBV4UDOszFMtu=e%Xy%{x!_l^!cNp4g(_i6dG%xD2CvG+Ga5L}JrJOx+tCzt;)f$eq zn)8YO&CwhWzuI!%*%Q~AbZ4tgdxTl7HuZ&9wT4x#HuZ&5wT4l(hEKJIO|^zgtv3A? zCPmx(8y?jf7Pa+Fe}zM}hC$J+7nHLnuFKgIx0-i;0*rTiJ@&+HxjDb^rq-~g)x4AL zY~JOwX}>Tg+Fl>NjH@qfX*K7EE479xwT36Ph9%Lqy`($qa{2y~Q_eHZp13Y&PaMt5 zjE``mE{7Sd<{e(--Chq?wB@G#!iieLh+4ykTEm7~!-ZPIgj&OcTEl``!+~1EfN1-I z|8zNf;#$LfTEl!+^Uj~Q@^1SN>uC+=+4^QYh4Hk8@3e;Pw1(@nhUv6!*R4h0XZ}lf zPVe_6hPJ?#3 z9JJFKw9^{2(;BqX8nn}zEorSmJFP)GtwB4jK|8HMJFP*xG`Sytl>6~DxgSA0^l?vv zcDg=jNBJpRU(imMv(2oJ2kqj|zd5cxTg&=*&`xVvO5!wVr_0$&jz9m&xa)&Jq_AXe#X<+lBx9p&_o_JVfyd>qdfuGXNP)}Wo%pq&`xX6PHWIkYtT+>&`xX6PHWIkYtT+> z&<<_aKhTc;*!2Unquj24Y+F)()*COjEGb8`-hpCTkf^$)a*t1oD$HE5?bXs0!3 zNBit}fOeFBFYS9-+Q)VynsUYiv_qd3ZPtH5JFEFV!4{*fZ~7}}XSEqmK|3_(M}u}& zb3M>bYtT+>wi2}l?X(8%(2Un4IUniH^pEx$efVCuFkNoj z_*^#FE_b~G&0HId=i1;r*9PmkHh9mq!F;X_?sILh-`w}tWiH$_CFSMM-&mb^jnMk$ zW|!xDl-0b`?39sPi)1$b)oiyk`+RHVNRRrTr6iAfIznV{$nE*Jq%_F* zB(vLOj=j?Ci#-b>OGf;hQmpZJneCHeTYdSfY>G_mTFCcU;HIpdU&(*U7A9P=<%dYy z`W1ca^WBm4aECE+Y?`^K3}()`Ldq- zi}_s^40UQ2{o%9seX|~2l(jYS1yWmrZ!a7bow~B5zh#Ajk?S@MlX+Vi{H^|w=s%ea z{LOdNiL5U-oL@HxzWc!K(Z9Ya=C81)R-`~}^IyP&_e?AwnK$MQU-rz?S*Jdgdreq0 zbXU#PjkQmt%*raARcfaES8Um8ta;n3siQ}3N@>4!$?6q14dd4Wf&<x(pmrB zcQ83`-Vt)I?G7HRvE3gYSv#`5O})%FM@yT9O`Fv_>F+b?y~vHelPTrXxc?3Yv&Y<= zntpI~Bz$d#uW?oR@8iO#ALY9P&&$30Yp~*wg}x%UmyABQGQmH-%xJSMdC#afA{q0RL=wIomeqtd%hqJg z@_Qq9oh}&Z`RZ?3y&oSV<9sbRX4KV4o(AKyuC3aXb?A5b&)~wb4?h2GWZ=cgnTuNv z&RRQOjun<|Gpu8ze&Tg0=Z-DU{AQ7Penam3!nCdpu61p&t!smCT^o$++TdK*2J6o0 z-6isQ(x#MxeP?G@ozI$6Ce-)cV$l~9AN0N2uXWb(0WvPayekX$h<5F<)wkhNTGn3! zM#$Kg3l*+0BU<{;d;Ep=UCa8U+Ysq*`OvlV>CyKWy(+&je|w~0GV@U}^zW7R(W*Nm z{=Eg;MLzwtpZPVXJKx(LeYWle|MtbhBl8-{e`*&Fp0;LZ^qKbgQ=bftk1TB2+iY{* zE%lz4db_0FT&V{SyXS+6ugZ1j$#pa2x;JoL;bPiXQrcHo+P75N2OGQnfREk&!pI|J zT)vQTsqBploa~MttnAw0W!DBXyEeGlwZYErIs`xGTCaqmbFFK_(XRi1rE{;7Ekf>k z3RAo5E?n*U57^pW$Kh*ty@#<~{{d&a{sY!_>%rUZ`C#r`*A?z|+Xs8Q{eZvS{=(qy zxWM7A|A58aafZj;`GCpYd4tPy&+njIHu&7F2cx?-INi0u>aGo5cWp5HPVpb<;y>Dm z{}?L%18&EEd@ugvUC)2O?lZ-Iyd(bO0r4OC#ecx>uK$4H-xL4wr}&Rbp8tU3%ZvY* zFaBe#_>a@#KVbPD;y>1i|9D0GM@jJ?@Vx6kNOx{4{-dV&j}grdCg&Ib0oS|!1GcXx z{-c-pkIa;q|A6mZ{{iDK5&tn+{6{U%f57>!|A6(M5&zL$`47^aUH`#$?j-RaTg89e zD*oeP@gH!1LGd3&#eX~_{^M2gAFzK-@gG^@KMHvMgRNcHf3Ura|M;u^_^hkee@Gml zqxg@(;y=Qk{~#7HN&LsH;y=nCkNFSc0q*<~6L4+B1za1k0oO)+z_k%0aBaj1TpO_h z*MAT%aMx2}25ZEBbQAxv#q%G;4OpMc%KBW~Tc3#?;6LWeI-chF58?-Zi~p!D{^N-C z9}+{be&K7+FAzs?{RgoG>WvWpvB&c>#1rydT_3$$&UakS_lfu;VhUXM1Le1fE9CMY z5?fdy{pcwDSmX7B_(E6lm;J$>Q{9tHgkn@q{7xJ0fiv4`jO{vN3?rj!4clD9^xEaH2~^3dV?szz&; ztC0G|_Vtll#taV-gJ@d*&1hEZH&V|Z`6%*hyME%kSA>%4)yy8bvr+1`ij(~-l6~e^ zwSL{#KD%VSW&RJ}``X`rL1Dg6hKdgBlzrxt4*u!4t?-v{7k%@Sof^**`F$irIGlvcrAZnN?f(+wUFkfBNOhd>0LU`(tAE zkX{q~^J`V|pUB@p(l4??+duy$`ryxP{2LEe$$Du1ljaw<3fHL@{rRR&DbEd`nw5R| z1+xvkZpz1*L+-DVm6Z|BJaa|X9En%#Z~fZpC+F2oX|=6N)~kP$j$fKIJxAge?)VYAaL1YWg=-^*;o8ifZMQldJ+bwRlsuz~MH8kzX?~Tf$MloY zlWz{ox}lZ)*NS>i@%=eee#yaTm*ifN{tJJLJoQq~0Nd3M=Gz!Ovw2OV#iN@dWwZO5 zUw007UlM&`(Ur&tX>Uh5)EOl6yDhXc^`+?Hp(UcNI{70#=g4;&iElItBt^e{wqmr~ z=A~KHe;6Jh#_{UwTO*~a*NC?NYG9+uPsmtFoMYO!(UAtnYewe{ujiXqT)y*5ti!bt z?{ICzJX{-b57$QQ!?h9raBajuTpMu^*G4SlE$K%I=||Izv3?K_Ns#^?mj1T2{gs%= zH!?0qWL(5VauEG8ztlSp*McSM%v0aAC+;wPsX_n<1FzJ=HqdhkG|e~5F>d~ z=53wKTOpaZA~J90&%&MgWgFbR4@h@*ZNyAm8*vlYM(o735kGNl#8B8Sw=d!+_A~F| zW8*Y&llbyjy0f(rFR`Cv@3C}eYa>o#Kl4tEL~G(BS`!=5nz#sQ0&)s<^V z`~q!lHr?5_iMWOR9D9$YJ6jv^3j3LNVij5wr_h=hh1SF;v?ey8HE{{sHqwQNN!W5@ z_tKqh9rncSXX+7$(3%*8J)U>s54_tt#2$1xaR;r5Iatjx#2c)Ry~om>t&Kf#KL2CX0eo9=AOi7D7~@ov+dZGB=1*liu+2wKbiZ_;IKec}i9vw8Q@ zovqELJ6lc6zq>Cb-HuRR>mA}+eCc8wv~5c1NK<+?xj1EPDFZ^i3!L% znv^k{?#$TnvgywD95&tAmYW!WI^L!`=bYb5cQ)se^TGYPeK0@gw(Ym+&Q{xWXM24( zKj*U?2jgpP)17l{Ub?f@aJ@Y@HkjVF$JFuCo%xKNJ#k%b)1CjbwTLUX>CUzscDJ9+ z`GwofyPThQnB88BdN$oTr;eBIZ0p!`XIl=V+cI+q=XgN^NH%5AzcHu?_}+jE(BFWuQ5!=AV;#|8&$4FlV{ zyxVkVYqROjR@-!Et6^UInRlD+oMZFSovqgC&ZfSX?o69ZyM%9b`(Rsptf{ZkolSbW zFfH}@%$jA>of#WmHr+Yr9Kx~Gx8;Umr5?xf(&^6T{KBprOMTu+ced^09cIOD>Qd+b zNq4r_hg0q6*n2G9+1hNnv(+};nQNKrdg;#mnl5dENps3X+jM7+H}!=@&AX@B6Q_*! zVuwNPwM;!P-I>q4Y`U{4ljCfCW~Tn9_bW?_RpIwb^uMt8Kb7nsdUCwv2c95xbplo9;|s@vm?r^=$iKMt!_Z zcecmdbZ1*1PSnT4h+5lp=bZXpy0g_b-PvlGP#CRT$bZ4t! zI{TUTcHNo|9Wo?7-#JgH)#_F6rAA;o_Rs&*v3zH%VY~S9SiZ9@hwXGZ``}u`c3Q)B zTEli)!**K3c3Q)BTEli)+k9u+KG;r|!**J;x2-j7r!{P+HEgFfd)ZpUc3Q)BTC;zx zHEgFfY^Sx&ceee3?Q}V8r#1W3TC+#3HEgFfY^ODBr!{P+waizXhV67YY^ODBr!{P+ zHEc(INTp!xeuVAN+vNTX%l!%4QGbM|VLQq{vGu)t=i#=#m+!pQ*7x$AE86tZ@}2){e=OhmzxK!So$dVq z+v)b$d}mv3^PR1R?R0&c?`+F$zO&Uf-`Q%|P9Jacoo%_zcedK*JD-yIV7+1g*UWD- zZ~gJ|op*clW%Hen$$YJ*zL)PjT=YuOupRkDpGtkS&3Asrp5M!Np5SSl@7%}Ju$?~M z<~v`I_I%0ty?p0`-t}$1^EYxl_akhl+i&xo4|(M_-`V!x<~v&r+v)MM`Odc7<~v($ z^PR1R?ey_B-`SSid}pg|zO&V`{=^+`^PO$E&3Cri<~v&r+v)4ud}mv3^PR1>`Oa3u zcKUdm?`+F$zO&Uf-`Q%L?`*ZrcedK*J6mn@ovnuL^!07Nvn{vz&Q{xeXRBd5eZ0+g zw&gb8*=n2bY&C4BkGJ{G<-PUE<~!Tb2kIJLk0D%Xc2;Js+^0UjJY_&iA~1zPxOAsc$@ER%Wb~1 z)i&SRu7B)b>h;6sJMWeLv)`Oa3ucKUdm?`+F$ zzO&Uf-}x@velOp7lWo73@7%|>-^+I{JM;a)jE|S^%=n>gzVk=k`E0(k9e?)l zXs?|g*iLJk?`+F$zO&Uf-`Q%|P9M)sUC-O5Hz={MY)XN7>%!j%f5LRRZR2y(&HFNEKgKmG22bBPH}Y_ULn%vY{E|5>g=1?57vH}mGHB}0Df{bQ z%Y3K`$JPlB+3;CpK-)i3n&d5z^?os~+aUO9iL^-j`Bzgut6VTEu@G^eK(2c5qI*7= zG52*l=W3r}N2ed~quXB?(j6B#^0H+H(eXdIk;ep$f|GOOxeBp&CJPZ%+a#o+}#Hv2Q%yX9!jg9Rb(WFWK*8z5b%)stSMZyT4%c%(QL3A2+Yddi-6Y{$A!>SoFI`SNb1&d|1i{6+g_J_y_sFn}dI^ zE}h!HQkRt1%QRZOq=n=x3zH5Vmp}E+S3k^LKX6d;m=omI?+#vn{0aZLgf&^Oytip} zLGt&7O^XDE`&YklcVvC@;VBzhnP(yR+VzY48=JI=Jo0%>U+KMLh(`tc{*vkcV#m{w z!v+55YyBkGJsGUK{~!OdbuUKFY?|UvJu^zKdnQ=#OtsYJgPx4^K6cQbabP6RQE=hH z8mSu=KP?GOKl@t^mt1Mtk9;!!SN_*WEQu7nYrcPI4aozNeCO1XNxl<_3nTCTu+CSv zBK^G-yy5rqk2_dwWu(<*t%h zOY=OI)w(;jyrHjN8x(0;t6b*QQ`cpUnx@`c|WTKZi0`eP^EEkUPIH zt!sm8T^nrc+TdH)2IIOmIM=nox_MvwEo(#BM^ozUs+GC?6#Xp|>eq2zr2DhqrDTmd zmAR~;v{{(<^)7!${`|9>?~c9AvOdl$e`6tgntRXJjDG#}THlu+Psuv;yyU_Q`@X*U zzG%3A8GpX1nOXnW%DD$uRb65HVt5&RfDc-EYJ5x;88q=xl6&rr(u+t_SMMkAo$U}ML-g6?OK*h&YRHRr&qK=LPkt))WNbLHZdsedV z4rKhJcV=hbwf5fY?7hy(S?Bxqx@Y~zoR49#HSf*vNh&632@*+DPal8`-y4RFTUY7j!mwsjvrv@uJ_4tG5db%V`}TZ3$C`~2iV%akAtu6`#l)jjvwG` zJAQz*?S9~G+a8#^%W-8-)t;Y^y{&bCzwPyc!EL{Q!|nJ17PtKj9=Fd2nA|>Z;PS5L zx2%f}d~WvxquV-gx~&7N+dA;Ntpl^ql=$(Y#E*CoKfvuPC4Rgo@nb>|KfvyDC4PJ> z@#8=cKiHeK;|Ca?_;HcMk57a6;paO`{Fox~qi+yD!1AZf*x*c(__0*tM~%b}@Vp&A z!1OaDeoT<~vHp%^`~cV6@dIq19>kB^g7^Wxx8n!{HP7$2m89cC4R&uezaEiO?)Zw zgS}llevt1>{K%E~@w~f+2iWJA{a#xK zFJSB725cStfUScguyybRwhpeqjvw#^_Wcyj;4z6GZ4y651n~pjV5i)lcgg)Z9^9Yd z4*n3_$G;EmnJ@nfvU56O2XE^IgA2RwouKj0F&#*b%Xjbg7-?EOOQEtYr$ zr|{<>Znf)ig;(ejKZINOovh=4tmB+u9qG^W5A^4WAh-}&;#k9gaPKL~HC z&UP+&{}zAGxo!4Wp4;|j_=yWIbOwA|;O{#(UVPBI?B=h-%lct-_^$tiH`l~mW<)KU@2IwRKOsV=HzfhA!n9_|4ep>08{Ux$E3DXF7?ecFD81?I3(VdpFE$8L8<(Xf&4qFG`Ve8;LY#qFZt%Lip zb?_gy4i3cD!GqX3xR6(59geJHPOuL4x<>@-{iCi|IFawgFOP~}V&az};urFrE5(1M zhX3G3^2N^w#LuhL&%%%Flk@QxIUncA`N)y;0Y`F@oVPQK^9E01pI^8V`#OLxv2}1J zwhrFJ*1?_FI`|V?2ZzGm{Dz>wpQOt7r^xUosqMY|d}19BUt+eym6$R-i7CU8m@@o` zDZ`DJGQ0@#ASr{Tz=@EXp!qNGA?*8;YvSjd2p57(I|UvDInDo0zE|?~&gdxcAATN7 zAj5rdJk5WA_t3t{{++LPPU=UcjEC#cvDEM!srEZx@634kja2)|d}p-*uVIdd(=hwP zXCUkP;4)I$gM4T84;%)z)qn68rhnlsO#j1Mm@=G&DZ^KoGF*iz!&8_t9EB;vPat=$ zH<|COGQ0$H*EPXOAT!3-1^Ld}4i};JSR*`y%GiX1ps!vZ@DH@B4EKOc@sBIK1K-<% z!k#$eleWmeOXfQxV*{>%^QCi!XJC9!X`{d~Xq#_C_yyYa`1*QhJqEl2ZH!IYKhkkM zAj2o<*rePe#_`Ej@~<1=5jYOVpvd*_=Qnly{|bMAUS+rgQ-(J%WjF&xB^qw zd}rq1uU9w%+L;>#en8JB?QjFsT+jY`g%@Bg6#BslXn$(<#C83?ec=L<$4TB3-~nh? zdvE~ec}GlZ^~eNQwHCgGT7dfHQ!n9 ze_(p%!J7Q}1o_U)k)p46R-b6TbLahGTuQza_}#xw#2#zVd}nO?_K%bA+_7H4>*jc{ zx{jp=r+!(O$Q1v265Ooqi~%#NeQNMBWBE=2D|5bd&fsLm>+!(I+QvBW zG5zV|+c5H-X$KeUSiiquV!a;x`3N4?c53#-X;+)zV8-csz`%Uh`yKe#l)=8H4DK~$ zFs~_tcTE|rYs#AM%pCmr3&y2g=Lo*#yPi|9E&W)F-(PTT$Gu#1U|RJxW7red_0u0L zOHCi&&w^uV@A&`VH@hO!hk1Zs_1=oiZ*^&-KQ;Nz%ukOAW~CptefvSav$lg(X;=S( zQ%xC+YRceKQwE!M_(qNoF2%O`15BziHF%WqdVGDoGd5V4<~!?nFevjr613~ z*AK2_y!sPNY0BV9Q`UTEbQEx;X%7s^yqJT(-XPzZKFFHy+;M&dH>&R#17`HkyL`t! zc#*ne&4LwaR~3@hq^M56BhyYzp zq_g4J{qpjs78T!KbV74g^Mdz!MeaO(ZKCH#HO}nXKHlN^3!9(%G$T@XdqcQyZr0^T zuRkyH@LB=r({9+*X_jx*(Y&$+h4b>Kloyv4b~ht$RgJu7SU<;}(bR(ClE6kaJwG0` zYgI7fC9ahRN1p53knhc+f9cJ2uS>3^`H|@)U%TCT07+yB4xH5SQozn3)zRb&;yN|{ znu28Z<69wBqH|1jG@16&slm$R6bF0a;IKH@6$eKw2fy5%gM$}|r_3c-_K#nYrsp5u z%8yx5Uj8pj3k$}%N~3{_uhdIi5sD=+_lT``l*2_T<4V z|84fd70=hVEoz8ADR0jj`Hwx$%7atF%fEVe(?#3&xXTu?v%RFwUe<)dTYt^+QuRnl z-QM(fQ!cz?^~=j2Q&Lb-d}~SJb*uVBmU>sa0uN-xQqcJmgh1qYFQil~w-F<<8t^ z&dzEZKBy7viTW$L%x1D}saY&FiX~SpMJ-E>-CHue6Bq9^iFYm)@8nqC8Qq&f{mkn7F@ec68Xv19Jab zeMzd1s-xoy3+AXl+kf~WOWV0K^Zk`c8^WR&ukbncx!`P<-B~iHa9+=e)1yz9^p37w zewUn?S()o5^=Tfl_lD@KN4Gg2JbAL{E_ms+;jQiq6FB45=O?Y|U;e=@OU+_Q{xxSc zh^44y>9X!E8QyWlJC1m#LA>Kw-U)Z-9l4{I`8VOdhpuR>s4Hr0{p_rUP^f6lBav&P zyRwHhhwe&u(nEjB`XGB)bcN`u`>pTd;=KG=$?UQPyPNKb?rs_FKJ?sYInNy3*l_2+ zvz^gPDx%8{*LX$aYMsrm{XJ)YTTNurTk;;EP;R|?C0XMe(~Ki#;$kLK%(#}Bc=u*H zmy{(-%915z%91X$mekn&lE#+JD=e5d*Qr<(P3+z6T=m4}#Ito*W&Y~EJ?_Ews%UBb zJ?_4LT^-(Cwlq9`;(gA}xm-@Kbn(sP{8U4(7z&G_1~KGVh7#Qwk|LMF;iqF^YHBJ^PHZi&u6Xg`mW!4{(1h;TJQCKUDvg*d+&Ykv-dftbI#UK``>@+ zD*q-!=elZYnsVGrR!RdkwZ3+0YIRg`YuS6tsx7NW4b`f_l&9ru*8jcye~bV6SHHYT z{}`|Se~JISJ^zli{?%Xazr_FEpMP)9zxU_AjrZ^R|Lgd=|Jr`}u>E7c8vQH&pDzF3 z=I_59um3hb|K9(9FaO`-|F-`BR{!_-|2{td)A{jl# zp?vi?@2;)oy&L62_@T-C?dY+*{Ou`tQ89=Q9T(5Lt8qA#zK5s#d+#4vR*dddm@Lb~-U;V8YKXi6G#C(k9`+Hne zYzp-5N=vmNcGVdSUKV>QJG5!QRJ-?Gj)QRBw0eSE|=r}iOwu$A|N`slBuk*|W zhndB%wNSPHJa2h6ja_TF3I<(0@DC32KF5BI+6Ymvw(*+$CifuQHZ2zJH(bR7woYTa zqaz`GT{2H?+maieE`~!3hVfjl0A@5a3KmT(=Nnp^D+h~Y*t_dFFO-b9YwZ)zGD686 zFQ+S9XXHY5+iQHslVyCz{%mO1doI7C1m)N38=&E)RIX#Pml^L$fq=uyxq8V_MY2H- zocE02$`KJP!)zbaJDl+k9+Y^vph*8XT(6tR|H0RW@}DiY!f3~QKBxv;EZM4fWVIS% zKb_?@`K@{OR{c;rUX&Z+k1A{^~e< z4;B1?d%QwDHye7@E9FN{1+uq`cf*zXSGk2|CX4h*1BdAIe2i~XzIE{a)3{^2#fD>w7eDi0`qT@&miriX&UObBCEnq4>m)EU^+YHb`Gj8?K7uzLB48vt z$u}>FR<@h2gs=l=xb@RpwjRkDP}%gwKhC?F=U~m{YjVwVvnJQvhMHXSv09UB?t4wH zd9M8@u6Zu}C$9PU`A=N)So|jr$_`h~DdM1Ra)K8I#3)xgD&fb<)BJ7SiOj@xCrrP2 zlYhK7nprm80sZ~Uc!x*Z75PJQp~vSdJkBppF)c0|j@-M!)x(vyOB0Z=yqE{3Wiof0G}y(?^C>6%S=`!4XdRKvJB9_Z*+Zfr=k-Ib zdtxsO4o?BYE6e#FwRNoU$p+BNd(6Mac(R=ZD`3FIWB*{gu7_FihpkX2W(TjyhO^?? z?$hzmYxh#_;T~+$w<;a_&d=a&E05czZ`upXoVV~I;yZf z?Yt`HlVVHHO<=zY=rH$vD{|d7N!vv3mCkDYjoVhd^>M|1Lp#H3;iO-E26-E&OmN; zu9$URlK^*u%z3RoH`(}EYoMymN#%6QYs{cL9>$yHDUE&avyh%^V9Cu^wgaPXvlrvy zVWamEn`6E=S;)FI5IXar;-~91Huq^f_-US2@R<_pq!|r{^PSkSl4ES|oCNT@)SKO_ zyNT(sSeUVAIZK+cizPf<569QeU?1&6n54fJHuXtjN#9p6=Q>|71PP zn7)mLG<0P@=choYnd{kwXXT1*$mqX=-JN>aw$a2Ofmb=_^?6)xG(OaR;FiX0i+48}K~q0toP$!E|{b zKf0&@a*CbV`)@L{1w7C36nr!?W3Jh|c=pO-$aSsD zdMV;~@~Jamc(68$4LHh`&rZQBr@e{*y)AtI)$_2v`Bue&l4HDI+-b-fxyGi&rXAd) z#YO1(ZoQ4yuzY^2UJ0ZeOi~U_-NMsPoClY|(aP6*ck-VX<#UQ_@{WeFJgWQ*oJszn z-2E(yFLf(~x%;~FmnDIG<(N}&AfyYQc+;CdPAGsaHXginV{5MQst6ufy7G06I`a#9 zhhSFLVy8NBlMe#L@Ko8iIOEWUo6CJXM64$BL-^Uqv|^;w$=>0Nj8psy{N@Ao9QZ)@BpP9EMqkkx9m3A`(l_^=BRY;U6t zfGaofxpQM!m`*$#pOL`pD3Y1~;Y7GLei?6*m(I3cUkjyk7xAUtb6Hj67+8DMpGQwR z%BCD!2MZltc)`NsEX67g#*Ve&byl2b>olVv=!zltEjq^rC#;2>x3zg}!F4uZPYigy zP%7*8xyu^ctbrR%ER`4TmNM&w@xV8BR$6|&!?GM>VDQi%HiOsPWdnw+fyTAxD_o6l zvC<{+aHi=fMVH6d*+^v!v@^P{xKnYCd3TJ0LoM1eb-QycEPgE%gf?WO%nR9%K5@`w zeSfCcDU&_bje+mAgW21pgDf$29drsF$!2xl%BUE<`>LXF>UQm0h|Xfj%heF8{ML{oaOzwj zOY?W*{Z<`;^N|x-){1WY-YN;!e;&&m$ByG}dnFj_?Zk>tFXKf_f>R^=Gcy}szWq`$ zJWRD^si7<7*Z0Sv>RC6I^Vep6Kv@JjU0br%AuBm_Jq_`NP1(`coA|_?r{Gc(Ew+^9 z@MDfAp=!tz#V@BM{&ey==wflx;}1Fo2G0A^XO;!pQfDCaEBhwuHC^7O@* zl=Y4Bpw+l&J}53*dE;Ov=#?gNFPyJjuqjV|pB2j=iXKW&^DL+px{~_9unN_~l<}4U@Cz&_zYoU;~?|=pmck)^N%M@+P)1bV;ZobsjjP>r50uQ!k@C$pV zFlBin1kT>VUpTE}#go^8c)f=2Jadq3m>v(0W5@FY-OjTd$5`l9tixY7C}V7F4CvME zrt}?I##Z~t*M~z>6g#(`V+mtop;;eeraL}|>5YhoGFNZ*d43%0`Xd4CCq%N%%1JE3 zE)jkjB(gasChXggO<+}P3wwL$xMH|@8Z>^rmG#zdsaX9q9Zm*pV`q&<76eY+4fVRD zFw0eIlmQR3;Mj&(Ho{YbR~j7ztFcSi*#-SLUsV7d#W=R(_FQhh;uxrFAlub_EpPkb z1O!AiXJd=gxyGo|u*&_R!gF;F_kVX5eABEHmvnNu{j0N3c|cFu^=mqJTXq^223=H& zzOg*?%?TK?z=(e=oXVH&ItJ3d{`{`HCEpjNgm*!Hyqrbx1s`@pi}8EJ#H%>n0C#!Zoppf@k`>Z8~7+jeBT0}LwE8P zsY0>RaVJC#PT`F$J}ZjNHi5y~UHs$cHY{6y{o{Q!g`W>VcIER1sJlO%PuVk!J#yU) zfA!nQ+f~kE`5hDBvqvIN*tmkZ zXluLn_BHm}X)W|xly8$b{0gfxJqpxsUQ@8w7udJc@^x6dP%*PCp9LjH!`zqwjE&~3 zu=_fw|Enu&d@_j{r^G_gj77{THl4YS*#O4=6WOWHfsA{s1-r%Z>{YYnEPdogu*0S7 zWb0mRzvc!IGdHnaF(a5}Rth+7h+$4&A1dspB*XC6TUhS4#w_Gb8ffoHV4d$dC{o2S%s47t@uS{O_;F}GtMmAg&5`77@MUT$v)^~h_M_QeNUm7NygE-(KKZl@f{WwX zvXdJNE(GoegTB%1>FYWwGKi+Cm)u3sV?tw zKOb@iO<-Srr}J%X3Sd~V9ov}Xz(3AE2CeG=n=&VcyY(pqzkVj{@aoyz@#IN3(ziA9 z`na7(JUb5KLw+hGjN-}7&w$UkuZo?qc|5`J6gZcrD4d>dMq*}$;Odo!bPwIIJUOKH&D(5Bh)fML;e`>h0-s^3Aw0SWEI=@$< z`5LZmdj@89s!$r=UB#PR5wQ7;3BMaVmAANl5`wc@@%}5v@t^Yc4?H`BpE_#E%|eUd z$r*dD@u5Bcn0^onT7~e>dK!Fal@izyKR!j{p0d&@8x9^@&4=3`P=2yH3~nV$`R;j> zmC2!dVBpaWd_v`of{l}Np#IJn9=QL9ZLaG!*iw|v=jqK*I3CV`CiOP)2OV=18Znz; ztotthtU!~kX^{?V%v;$~MX_8l@~o z{(o2T&FKoac~@EEfVHr6vytt(J0_7;ZuMuU z-XudITgBe|Wx(bdB*Ez(sjRJ=0OXzqZ?H@Zdash} z?}al@Rx^IAkJ3&j7he8e!9311;!c@4;FC3nT?@aiEW0Kj-^D>}iTMcLr`r*DwZM(F z9Nw9`m`m{bnG<_3dnqr!#i4XbFV?o_MESY97|yAgvrU;Bxl7j~SbJWNeSQ|f+4>R~ zR;0@s=w@<<4JRO}-xY;^K?3(lI}5|Z&nc=N9py#cN?_!XZi-drseEGDd2l+_UZFEP zpU3qqfhRNTD?e{ZfLi56O5fWF{Aku$@C-SkbiBNQ?=va_ zJClaI)uknT#Fi2WnO={Vt)I{RqBxYb>%$*LIq};UiXrA!4{m&?AAdjN2=wwE&y8b@ z_@Ijdu21&h2S(K855DJu%Y~&ptn8k$qhUULx;mGiow7&SHzX56_2YS>kH4~^dmfa% ziR1>4O9~o9?gF<}DO{~$(qZEXS+G!h9q;R+z<*04!#FmLljYm5n;O**v@agekij2vd;MeD5R+EpW z&Qwl1xeM;OrLmpGOBBa+Hp9Kd{VXo2m5rgYs4hdK}WoD5^Gwz&k1ZtvrF{4?3$ z`Dvhb{VboFeO!^!@*oUKIL8k~MDT*{dBC)bxQlvEu31qJxco`WpT9pd@Mi}8%)p-+_%j24 zX5h~Z{F#A2Gw^2y{>;Fi8Tc~;e`es%4E&jaKQr)W2L8;zpBeZw1Ak`V|C1TGJjPz| zuRo+y&reG=dC=#!LeHrw4tM?pHTivLOEIW!Q{M2hgZP(qf(-!~UZBTSU~ihPubyu2iM)(LD1BBwU{;QZMRBHF5CVjE+VeAN9D0x zsZC@TQ7f+_vKN_fSbaCFnllmML^1@QN(85uQ*e4@Lp+hx={KdqPc~8s#h0bZ<#rcrmZZH!s&0J6MO((7qQSgnsE@!Fnx;y|}6@YT6FN?u*X zOc!G`d+{A=7q$_5M(knE2i)*wnYJ+PyAIq=$&a1k?M30dU9i2fJudxeAuJs0W1n>{ zSl_i11bmXeljShM_Rj7=@#JT!6gJ>8EIKe&{7f)rtb+#*xvnMJ2bCyBX-)wzm-)gr zrH{y}DuOo^djDXrfxE$Z&ozjC?}0UW(v!z9qjpa$T;4#`3DFx=2+=izQM`KOS&1)gvJs5y$`%(H&Y#$d5;R|iiesC-CpEzZW zhP1cy8~E~LJpLy(9#mI8uSa6h_6G2u*nU=`Wc;kXq+dT+d^`0O!Unlwr=)o4X%t?|1e+!Be${O@9U+!@XC2ZAvIVgUF=$0>^L=Cth0U()1Gv}d765{>w*K1sjDOQr?`nTCI)z9U^7&Y zuP>gPRkAnUorO<{LJSGfMg12Y@W(L?p%hD@$0`tR-&=@gAKIgtFvQtDV621n?hF-y)BT{OGy>0!*2cag0h)Dr06V<BH8S2h8z&1%0((3sSq^T?IacJYiVCK^m-4olRS6~bAtmLf32Xw=X`jN1B zj1?ZRG(kPBj$*+1Y$+kz3|%fQf^DyCu=Gh=^a?N)ZCW3cx_0l3&cnl?Su_{<2qvY=cVAayBCHwGR5=1dy3E=Tcj%++v1)S6VNy|5MT6aficNLgwLc! z(v;{1m>1ZF`JVK`)gOLAt3?5#b{{MR8^vEVNr!b9-gu1)ab`(j~#THUrxx2NKG zcr>r;y%-n&b}m?ZZ-zM3K8ZJ~7{H zY}WUc6s2V?Cf(jD=`L(3!aBsUkk0NnuW^0xa-xMWD#@0rAM=E? z|Amdnc$N!c4|?O{Ud@DNO1YGEssRZr|JS~cg7Q0dJuTSR@5DO5VoA^D-M5d zAj)f5WBHHcW3!}sE6-{&4#C6*Fd9%0hnSMBh4H7TMFszg8AA{VSIo& zhUDv^Wpf9x`Wg!tmIvWOw`}R%$qMO{y$c41--pF_J7bUFrg*2a4tGBj1D3h7aZY># zameA8-v5OL5-2+S9y0s%NKkg#6gD7we55kt}9mImbBa(Mk7u^0V0&@HH!IFx0 z*lqR{$=hrL3^+dn2gP;}<@@ubo$bu9Udnu!_s|wU4{M9>PKQdJ%Tr*{g6Zh1*F|KN z=SbbBm||DQaCp1V8e2Cu!FFDeQdHF@h;KFxt&epR7oGMfoVAkCR$Z}X4`?Vh=k~;KQ6~LvHd=gn)KoH5 z(-03)*k=5ghLx?(O1{l2rR7nz@t(VtuqZ+?_GpY0QD!U*N*tLkdg6}<>f%|w3zE0> zS;+3@DtfiD6lOCsr4?=4ijZ}WSlNZ)*!-lPm~|#c%CFo9H>$=9^P?8RZ`FP&-ngCk zkv|RAeq-1e+X&q`TP16QYzXk5Bui8y1AN(;PnHgPCl9P z_co`CfUQkM-q%cNUzD{_ubT{-&Neu?xP@r^<`~z{*#!%B2MGiDJ!7ZtCnetw{luJ> z5=4wQ$66t+#nWGF*pJ+;(9dqB*jHHxbVFjHVp52>;1vZ%_m{(y7yiP1!e8j$WHI#n zJwkZgY>o|Dc83W6fg-76Km5_}C(~0i5w25aVEc2w+2aj)(vlbR(Y}j0#G{t9uHJl9 z>)0QxOtj#`=E*qsL3_w;cN11jvBc{M&7u8-9_Vqi4hF3q0XNfJ&}B(ETnPz)1&6#a z7~-Kp;7ahTnvP~aC%}Y$F>uZ|5OYR8V~JVuF!x{x?lNdAwR^D|s_KPcW}>HbnZ-d5 z&p_DYeECh6|4?GUc?LF-B7Qb+GHa6ICO1EOjR zw;6`Gbx{l4px#1U>DLcSAI^Y4i)LbR>Hu6+G*LR_p)RDd7T71csTi{3j%51c8T>pu zNZ4+dq~>Oap{wm!k+(QQayQrqk18e#v!_v#+s|x}cKHbXFQ0hB9~n@zFHoolMDcvH z3gd|_nJS`5`#tdkNx0emUu?78<6ZLe(4M#U&@@#XyiGBqv>pB@tl3FQ_ zbb2WbYv(O)rbZUz`sm~DVb1t*(l%SarLU#*@JV9Rk$#GZp*q;*f*ZPJ%`SL(_nG8S z^3hm@yV}Jj40L5%T$PXN+Le&ao-owQyli4-6YMpVzCXFH$DDi<=`$ZN(yO?5gID zQI%5*^iTbkbc{U3yoI;;_batA^S%fAHHc%WD@&wCXZ^&|EM3W8`3BU(CSZ8lJ@#&3 zhE&TTNL+n?gZECk2t_CSuvq?P@lwTQDP{CjF>bMf&(OUHEwZQL(OJH1#^@K)?hW3; zP2FC(f5m&);5QMCe{WD63jQh03>+sCL+$goPSL;zLp(9nV3Wd*KkM%>$RTz(zTacsl-}BBW_M zv9|6&F+8jzPx9-CFRl9F@w;y9WfMcu)@`^@Kc1uvcWjMUx(q_T!Ik~hv60w)%SG&8 zgUYh|28hi@;EEP%?E5c0k?iLzI%L)7cT4r~QJ^zwI(1;Tn(2z({oRCrYJqK=DjjU+ z;f8VZ`YNZ*(G^QH+{Bj|Z4^dL8=>zx7p$$kscd&bN8DNHCJqf(J8Hd38}ai1v|X)k~BoeZmB7f75 zFZp^+8duj({J6G(Eh@VN3zPhCr0EfUs`)wT!U{ieyg(hAkADRjK@-tQQNlM{f01r? z9VfhQ-DP?awef<(Xne4HCV&52OB~1^C4%cmut-)PUvGBDd-Ctn;tI6Hr^-=cnyo)C z>s%kbv)!@IY;6W*Po*i}CyGtZb)-9W)o_XYIGp+M1hc%9C7F&05cxN2NiHMr!NM<- zaYBbOb~F8;bTB$VMBUlMb;3@AXY4d|PTa_%dRIuL29t%s`+Vh_9rvJxn=kh4ZOm#_ zzK|5Ny~X3a!h(XyYB*q?7tReVv~4v;ON`&+A@tXm*<9hZaPgBdsQ*ZjztjIO5jcO0 zxUl+`t)g*b3~D|C+Xn>O%sZkZ#;|n-0Z{RjG=V+HHi@WqT2Syf*LkyeSSX7=oX44k}zN+X?Z_PQ=Ex;XO{b!BuAV z_;I=+o15NJm>h8s4pVe_&QVj$zlwM+EP}mxsw>=%x{3~6R~Mx8GsNWf!!h0TvBF*3 zK)lZzAsnqgDLW?0+c{-8#@+yS+`6H-G1FBndc0McTB3_xi(PSw)^f#%hq~g*URPni zW1k}0prQPKGFL3=@>V(POaqbEex#^>H@3h?eqZFB$mFrF92fV%;Ad zE2i<<_nV1{p9Tx}`nMH%o>r)=)g8CaDdhJ@nutvf14OSSYRpV(f}1-!q4&;V%Ava( z3#;tmqDTGvw%+?pQ9qzBTF-u?j6Qpb-}ycdm;4wh)#Nsd7h`XWg-jaMSy_{Zm)4VJ zYEDO!(-KVEITq_J83T2#-oP}o#p3ai8T@WvZ(fsMeAkAqV`qt>ekR&}!yn&GCx zCe%?JFm8f%FIY*Jy1AnJpvRKWgB)pj!#Fn6(OJBc-&fUovId5`&%in3BBU<<=BSz5 z39ZADCHI66aJSM4Kc(hKapT8`Koeg{uj4yu&Z6hi(8mViXRw*rl0HLpUVn)znjMx( zD-6LtejaxC&{r~VIv8UY=%7jXGRZw47`_;Ih^rgq_cHe%L+O-mQ2Fd9Sj?XQ6c;Ad z`a3Sw*}*7ZU8lTxuolvCkyQprd5UQ{imU6@d5R~b^@CAot#RBP`L&lm;OvWiF|rh( z-D?j_Z|eywCNBVsjP0=QaUulhq{4_!195o7c)Z<`T%FPjKvLRkaV>rIHP{}hf~S%B&dEpTStGq9`D9L+z>$B#~VaKz*Z6Sk%p z)VeM9sI2p<=Yhf8ysqm`O77T(_l?YoV{@jIH~ zgZu?x>pTRHtQdenqu()?Lz6Jp$r`oN6CrZH{CmMv`F~!ULh;&`(Qr|}8(Men2`~LJ zz`98rEHj^kh2j8o4eyV3yX13X!&opIR08?(?`D5B2}W^eUvlx{e`o~X<@{}rSO>V1K&H-p}{4izC0aB0+JOj@xICVlb4e9d=Yz$VH2+7f4d9fcP5 zfG>-jG4A>TI62)DrHVbE@!lD~$#+wju)q=fuCin4Eyv@C27CXG3mr6BD)+?V%X=WY zUN}(wuK_;jp4Sp7ruwzplkm&e78q5x&foo4O1yEF?pvUk>S?zp;G!XKV1ww)vLE$j zhdvEtG)@~0ADp+>8auBWi%N`yhTmuZ-M=JjF4A(e{D{}J|6ZQP2|qmv!~0uf@$1o; zvTo(y%Tu0~r{!q-sgImu+CGZODW>hAn7n#iifO&%v|W^^m^?#igDIN6aBV~^luzn~ z@XiZsHD3x&X5Ha#zBwv?kA~5wy5V1;xzOcRf4tfv2pi^(0_8U&2->Q^&vC}+mp&90 zZFNSq4)Y{Ig0L#W9l2fvw07JFFWm%N)*%MqcP;RrwGL+ejAk)v+o8SP zRkmz*CQL9N%v_=+XdRl(YIiJ#ijLixn#*12%hof0&)bk~*nq8W{Tt4mk6emLIiBCP2ij87L7K%ZmYXjt?BPF)By@iU%{8t-qqRRkJC#EW zD~DM}55R)SA7Djc5?nla1MU>WLz@QoKx@_nIG10BBgcbC^ zk_}7T^q@}0Cg@vk2EJ-ZVERN2+#5te=`C~kk~tstC22s+^-!p&w1K$g1L5Vv<}hvL z1n7HuFr53`3QQZ?LBqy&V7+%dq|f`zN}Q*|&X6{+sN+(|6FXTzOcFe9@QPKePlH0w zC+w|VJj}|=VNsqLP!ZwJ6u0-ogL;A~?jHp+!)T@*a||jxZQ0<0^Dyt~HWrq39{M^5 zu%K6E5ZR~|`?2p8oEx;7_5J<=v^0mX>~6on`e$txRD06Ucryx={*JZ zP=5!Kw=HJu{{@$OTH!_MDfp#y#jENc;d`Vh`u9`A!Qb29%URDsn$c9A*DCOg*2fkL z)$nzQHm;uc0^G*_h4H>spm6&Mt4!7K(uMaB{jdT`lFMQH=nvpI{1yyb^9#Pby#Tfy zpFmV;5k$GXfpf7(;8N?a;H0w;KBV7;oGYd6nD3SQH(kK*pd!~?OMTm z;~kKFpbmUHnE?ANyF!D2I7mCy6naFj2J34-S?wjuAZ2b(@GO`Ib}ib0TZ?f}ct{&c zJ!inYW&NRirYkg*dc&{G?r>?E1?0-{wTZ3}bJr3atXyE*ss@mh=?+=OmXHw^3=Zpb zA&Jd{bKP&ThOfh6pSeCnNvq*f#u;`cA_da3Q&{n@JV;*^&VGD34~05o*jvw+;MdHa z4ffMQEqh?40f*99&t8vxm>2f&h3t)Z6fWUyYX1`c5>;ESnz z@0FL)5E)S)Lcb?N>51{I`Tp&2WX2wnu2Qv90RXC^RdO_U2wB}GHzaf9sX(-f*tQ1fy{jq zuqyi*WDWE~UyqaEI?Myxcdvp=iDR+F!>dpr$^V;a@Eu$OoH1+kUFdhPFIpT{!w5IT zrIrujdFP&}Z=`_}JiDNy>tnd!(iV5G*1%5P8>7_hDflGn;QZ+t*y@WK)_L><;_yAJ zI;)Nj1$V*1;1LuWUIGmhHH>pE0Iw1Ez{36jylea!S_UP;r;uQRnp@@J0M zI>6)HNU*!Ul3DLf0<9rGSiPW~@Zf8ARyZmL+FM^>?uin7$<|`aYF(B;FVAB23a>!+ zsF#Xu$!{TOUowkY{2Cf+oL1aeUJv~<;#uZ*EgVr{#BQ6l#%!%**8Oc$wCk{nO`dLr zYApn-eA^Z6;vcbAaZb2xhdR7@+z<0tn*#Qngcrt`!|~Cs_+y42jQ0!14L05|5+|Vc zhxL%47K%UgSHZ-A3xZOh?zs)%ZPg*{N_S=uaqys--*5}>e_SbD_St}hx%4^K8K-c{iv9RIr)R>!EavHF&gN1=-=3S(ouE zptT`_$FC6(RQQPn%?pCQ--p5KCauAGK?hiL%N=T+p9pXLzp%H3&hV|k3MRK-3`ag3 zW(xao*t5w9JcHulb^I5YH#h{%6V>r3&PQ9zCVzX#lIB>QC#O6)E&r7Dc`G!u_rYm*_CZ`*a~xcj4xPW6F{8!sk&~X6A{lP_tC^hU{*}CqZ)FWqF}ba*uX2v|jrN6@e66f_Sthb*+la}dW!cEGm(^Lt z6p_PRkEB_Oip7_j*dMYYhrS$jgUphl8zlQd3RZ~PKv9UoYoyJYrQNN6_eAkryR{6 z%_A{6)kws&@5JN^S=2*uHIvi2NyM%yCZ}~%j(TYxiOH!(BBpsKCa3dl%sXix`@fC zHc8eTS>Y-spD$~&tQoQvshB)emY*uFX7Vsub7cj~TC8Gn8jH4>=4Yac$pdB4HqiFb zu_h*;E{o=X_J5X&$pd837&M=B?1;%}EZQgPr(R<6DY9rzXx+3fV)EIt7RaJ)ru`u% zUm}aPm9}}FipgmnNW`>{#N-QABBrq@Ca3X2Wck)$$}N!;x=MZ{ciBK_l7g_WiMbAl*DkgW7Wg%;ztQ9IIr*%+{ z`lz3noNAq9HIfypV)FH}8mi)ICZ}s>M_CE7XgOkX>ZjKZ^xU>i#pI^4>}AEvqF!S1 z&a&t^oSvuGs+fF`EHhc2vf@-s9;3?9IUA*7a;j~RRVu5uJQgwe4OK2t_M0jury7ZP zqber9C5w8fR?Xx|DiK@BHHyh^%TBqCvdUCUPBjwoCRI#+M;7%^t(wV`RU)>QYZQ~; zm7Q|bdr!sWR3i~@R>kD^Wl;~+s+l}RCE`AEjbic#vQv(FAF7y~Y9!(-(Dq$)C$kIqH3(VsfgHhB7>V2hRa;lMtcd26X*RrUGYSm1>Qzc@MYZQ~ek)3kX`&Px|R3j1ZR>kD+WKj>* zs+l}PC1R9o6qCP~opRLsLB-@$BN6Xa#pEAlQ4iIsnS75*#C_!&#pG48Q;vE+shFH< zB;rg}O#WFG^-!&v$@i&5+)u7iO#Vf7%2DrE6_ZnqM7&=WlYf&%Jyfe^@+_5z`^ztCZ`&S_<$-VS5x&+ zt(wVm<&Z>dr;5qdWl@fLHB?MaH4^bbxkgN`DLeI0T+QTpa*aehP!*Hcl0`Y{{Y%B< zR3i}|mTSc1wPmLsimRFYkX$1X+pA*oI?Jry7a)s9Yl^uPZzCP+ZOAN8}ob zc#tY4uP2Lg)LUQ0hD^<<|WimRDil4~U5!K#>CUl!%4 z*FeSOR3i}|lWWA}jb*1EimREtP_B`Php1w5Ls^tNCfAy%n4D@P;^T6SnA}Kq>Y=!r z$&2I~iFl|gCO4KvIqGeyVsfgHh)>8hV)ACPQxC<}OkOP4NW{ZbF?n-Yl%w7jDki5I ziTIRUBPMStJM~ap&EzNL8j09R6_dA;MLFtitzvSjk%&*rHDdBMvQrPm)l6O@*GR;} zRWW&6S(KyRb}A;P8j1L1geoRCkwrP`?Vw_Es*#A#%Qa&1 zj)llPFFdMK`D@@sO9L_AUzllPQGIqL1DVsfgH zh!f=+F?p#h>Y=!r$v3EUgIuot9n0=6i*iY_JXB0h%WV8#jJSVPrfe3=h0~a# zVy3JXL30mjgsGV6R2s@0&9h@MX3A=8#v>ZxDrU-Nv0Nk#FBLOowJ4gaq!FQFrqgIB zb2QJ6#h59pu^Ck~B2~uj&9h@MX3A=@l%LaxRWVaGi{;q$`fL?5Wwm&k zdqE>k#Z2eWQ08c!9g8tjR%0_>(Kx1Jrfe3=9j7r@#Y|Z(f#zP*h*vSwc{G$cnrFvi z%#_vGj5jontC%U9#d3)>yj9GU)skrLEsX>fGo4REnWK4jEXGV(jm>yRBT>an*({c0 z*Xs*Z%#_uVY3@CZBo#AVNJE*Ud3G$uOj(W1_(7SGKrpst3b2QJ6#h59pu^B&Uq^X!Go5gbJG?uHF zDXXz-_FptktC;Bu8p<5avtu!4%4%%J9~$W@X3A!<+!-1xRm_yt&eGgp8W}2Px{8J} zNAv7hjG3|;o6(r5ikY%mEXS_hSF4yQtDUDg4XS5V%ybP6WscU^u^2ODH8!IuQx!91 zvsmr|t*uotQ&zi3bIqupS25FdG?Y17W5;65l-1Y_Ev71F%4V_LC0g@UF;iB{q`Br) zFRGa7dK$_ct+8VrZlIye(Hc7zW2UUe zX0&FiVy0{s%L%l$QN>JIja{?rP~}z3)Q^TTM{Dd@jG3|;o1w>4#Z1{OmQ&E$CKWSf zwJe&`rz)zL>1G(3DsRK-l$ES6)}?g1)h%4+PIy*pn!O{{%PMBNorW?;YwTEznX($2(TS;wnX*|dca7EpRm_yt@@US4>Qxmp z-9bZ{qcwIc#!Oj_&FI2Z#Z1{OmSflMJ5|h-)z~$ASE_j`X1a@pGDmCdSd5vn8k^Cb zsfwAhSuB@NYr9p{yJMvKpJwi>Zp4vRN!wNNamk%#_t` z(p+z<1uAB`mxeM&YwTEznX($2(U+-;nX*|d$FAM?shBCNv1|5zRBx)7X)q0Cj@H<* z7&B!xHlsgN6*FbCSgwTD_N$mFtFdeL0aS}s%=7>aWscU^u^2ODH8#VHsfwAhSuA&( z)pn%$CWsfw8% zqM^*u8aozarmV(hSTR*GQ#Om`4%3>aikY$+yWSs6^^S^}PNbpC(b^#uGi9}BG@jCU zsA8t~Rk$(qONZ)g)%hUNd{Gx-?#>m??YB?3nDmV6UAqQ+90j zUK-GNsbZ$=J!S6!d#~Ah%a|#9FM3jD?=^dG88dxHgUw)nmY6AKGv>7bbq*h70=ZVj7;B~tp@}w7@qvCcQLj-0 zj!kR>NAz!DKzluK$$CYSN{aE^!iL;iOd{^R=`AZmI)FN_pZS4IiZ8;t5BiXp`jiCh zDM6v69b}svCqpZX@jy&xxa9VTbjrSk^=taTjznGfKKnMkonv>H=o3u>ev}}0-4t#Q zy-6xG?%;Hn@erQd3>@t5;=^y_;GfIEq%f-#{hp5pueI@{o;<)HvI&Ie%Shd?d+2Gt z0VdTK5lz2xtT?^}LKRDiR=Y|(GJ( z6`t@q2$Pch>1_)|EYod--E*mP&8?=DLBvsw#XQ8w}I8j3%Ks zDsj@PB~Tcl zS@od^duMfQ!0h=43q{!eO($i)#Ix6ar2{w(mpoSI-weJvpV!-yZFYarZ7j zY}qIQhFk0*AYn*kLQ#=?SZcjO%tl>PCBlTa(-5*#O0WQvUy8vKq` z=FK||Mu%g`u>GhjYn$v8GNSykl3Kv@b-=b?y(thj*bLYYtKN zSPbokZs$ToL(d(=`iv>owLPQE_ubVWcju&w&*#*V;SbGmg3nnoz-$(o@yH7O*YM)7 z=OW3SV1w%x3(Edq-j=v~lU&qIA4`@M+hDiItOm@U&k5JT_%2tW?EnAb;`ym$@Hl&% zuqInnXQ87j$%(MR?A_OukL}QMFn+t2*MK+PK1qB`?Qu7~rM&RcjkNi9FzVf{Q1(P! zw8W^T4S8eiX|jjd;&yl>Zuvf${PMNLYM**>Ncsg*GZ@^HvNjCU|q=F=9N;nTim4cN~!kF;oOg)^oMPz;EhMb=wbW7Y~= zWlw#R!T2)Iz5&-wQjnisHh9F?TG4;l2J*8JhfRJGMeWafV+B^Px?8Hlm9}OK8 zu`T>b$Qoz#-hzro1(l?~rVGZ9|7L!FItop%3{^DCTTHr!d*J;KpgdMtxjUZAaZp^_ zbb~CM?1{g+*(+>+k0Bne)A8syd&T#2CrR|(Y1rNBzhkw!HXU`A5CuPTBDt437iT+J zC^mLDM@)lek zSB2G-31o)B0t_wISA5k^C;Z}h*mjV)`l1=+(;iBQM#N*3jNE??K*kRmD<@xa9&Ky+OzZL@*BC+tA zf&Gi_h$Fg1kR+RFIR8tTvPZsU7Jf*)D_%P2NY>IG!vi-Pu&+xjQS_LGE>{ahb!O+1 z`Jq`@?p36mBk}wCS>(jeS@>P2K$#D}GZ){`TePKTD9D9wGf-n>zF2M=N}}rKqu2aA zk>8~OD-!46c8hDu{u|bd(K7nF_-J+s(f41C*VI6P#nXb5H&Vh!_DeYoc%sd z*?;pvDB1*`7Ijn45=YN?{8<#H%$GNd$Fn!0#SdEDAvG}`9m_(+qNU+vcv%L%`?W(n zo!=bZa%V8J(RO8j>;h{0sNXGyzV;`6OR{k9W*=pKed{b-@O_CmBDIQKzMh5J{pO2T z6*I|luNzo5WsKNVE|SliZs3ikqm}(7op0dB1!F~V(>Ss;|0a&;#Qirv;wHY%A0qy< z=M-sDeiM5QL2=yAG2~FAVmv>MG~l{cDI~LHF>3nBM0I9=U*Y$Pu)Z}aXG;8g-2^gB zC`Okl4x(LXItdbsG4bCa%AS5^V_XhNkBKin#{Q$$-6O7&>mP*vCDR{F_*TYtJ1O*V$`> zVs-^4Uq7tu&kL=C@J%Nj zxjLLVGDD1h-Wguss>9{K=`Et^YrL5B78}Q(7oX2RM|Z500d zKPWAw8P}m%s5qjv5xBl?#?`DkAnx9Inl!6y%}u{OQ2E$7y>z%ErdHyjrzVhaxHZ>j zow-=vKZY#rsK;%J%ogN3TEnhXUG7A!LSWC?gRYQuACoJb(TXAdntI&rhl6-^W<8(I z(tP1#xjMTQvDbm0$sZ089&oO5b8 zsr>yf$q`y|es0>JoLSkD`}2Sh3%aKHT zQ?;`H-Ie{M%(WVa`z?X{2?vQ^pKAQ-y&r1k?ju@})p$uS1iVZSlg2%&amA;D4S1_n z1bIsHO@c#V{=WUBAfOtJ8Xa!HUbjL?adb7Vu09Nkh5p3)W;M>!JlcTKY#R}q)S&aI zFwknVg?^n>W6RSA;mLwSBzxi$9P&OKE{!-zD#zBK^*=}8%9pF84Lt|?y@>$L*k5Gk zm{<5})IRv0{DI8Od4f$t55V)=? zTM&L1*Pxxb9~gA9f{*3Y4ma5!d~Vo5AFm2bT{jJqY*xW;(<(Gt=><+f^TE>bA$GPO z3r4?I!>IAk@%+l!u<-Cv2x(n`-zW5k>U}|woAUsdCz--G-BcL6uNsej>IKHD65+^$ zJE&nMC&iiqn7ZA=-QguNbsp@n7jmob;K@Ku@G+y`lT&-=pa-7Bar8Dg5n4(uqXOAm z8V*|CWq44lwY@seEZqfRzNKihb^y^#_5-gkw^80Rj>Jw01hWyP7)LGoQ9Uem|4u9b;xaY5h^?|ps%XPVM=0Fg% zoluG$#0PS9e()j$*0;HZmwY^he+KS^3xi9rI9nm)T|Wv3``^MjT$Ws&pP8nBc)1We zEbtNzCdR_Bxdr&}=@sEjlXUR-Scv;e@5x^8M%U!(+^VWI_8op6w=Q=SB6qh(BYIo!7=s~# zb+#T_k2sG_iX3=#{(hk)K8?@7rllu^4!fu=(=iR>n?wmYyZ*q);c+r*3y z$ajr#MQ#MT^-JT`xwqqE$Z-hA%=IgHb*?wl!&Q$XvGJoPygE;vu7UGjL}Kv|j!axo z1)UQj@Ycu8WGknKy>3O}Tn{}mYjzVXPme=Gw~NGNQYBQJKZd$9zmh-w4KUg`4tF;R zBG;z3#?;5@`2EHvGQq&FT=@adCSE|lPvPWNxh{sTIfudb>SPyJ8l(Tu z(>T^uA@i8h2!H#_F~__eAq$^E%lK^kP}G^MugHV=SJ$w=b5}B={1I&LBw~JTBB`8~ z4XOWLK|wc}T=U5W_u@1x?a~%pqvB!Wavqy)ZVLeg$2Dh zq{cG~n(U9pPz?hZ&}2Vc`*jh$2J{E3$`$Z%%XvKg55k7RWl&~(1?Ovb0!_`4aC&ks zwmi`fG}3lKboXmGAV?d!Uw4H}r$X%M)C#r@>knF3fHnQw!6&i=g1v8|N5~&Se|!Ny zJ``j3E*j9jW)c*fF2RWdpvHu?0rx+{TlekCMyj|HAXQ+t}>W z;Rak=ycdq0E5?_X;)r+KUGU_}ZS0k1Od2gZ3{4J|;2GUs|)@P2$D4you()_yn+ z1D@vNB#RTIz-Jtob-#_{-h3wYy~jc3pi+FZK^xw0wSq(Ow{ZJDO=x+;0(#fn#+}_JY5UFShEo9eTdI0C)cS;)!@?m`{HWIBkpvT`{s9D$5k$R_=iw&ZD6+ z}4_`~`Km*IDj4SvfV27a+2;PkE+Zhqhm8>U}{fZ)D3CV3G3S#>bntL=cl zZph$$`4teh8)K*Jj!>}qDC|$szzH=S;LqDQ*mw6nO!!huG&@fLTb;%jwcY~uzrPGG zMmEKkE{(xb^D^}4P!1nbipi)4;n$NrgzT7>qTRMyQ zl~=*hMt@;Jhud;>w%hg+*4cDM=gZr8bw&f=2dPf?v%*h)jP4H<#X&tFu@-2TW}gU=j7g zUY*b083eALUt#y9%cT3^8L)f9ZO|x5Ao+K;z|YWoFr`aVNXqtsE}L&aMcf$@+Hxff z+NyvhAyLHt%QpCYvH%XW|3MUHyWq!k1?0gqvN$&$PUsXuPV!^o9-IqaD+}SB>?*l= zJ^|8xi%^+#ldg5lg;|jzeA2%_GFK!)mq{70Gh84mb#tMrJ{?5*Gls@xdtmy74Digj zMIL=P0o95`5F%4ZiD@oeUz!MCw8F^i^TF^#CkeIJ!iGymJL%p(x)hW^b|XRXo#kPU1Hd5Lv$b>7sm7?!Wy z0WB_|FnjF{$XeD8{xPoMH$BS*7uW%fo* zYb8|7*#)DVx54+ljpXWF{wNl1$lE|@TPt3jOOp@4iKx|Jm#0rIq{M^!H$&L6!dX^1 zb~F5mI|;fUmy>&%8{y-^vvB#>VPbtJ5jMCr0q^{Fvf8;RaAA@md=0FX{kn4snzwHP zv2U_uV>g|K@a1(x$2V1`9dZ$_?QH_D5_y@CYYuF0+7t%Px+rTr{SwUI_>LUSN|Bv| z9I)K^p7hj8kww3~1goZ&kv&bT8gRShbTG0lCHvzmW$N6=G6!yMxpcjD!DN16Xn>q*8@X8kcXZe$*1*0a^@t!(^RV_^BJtuJN6DYH2}9(<5_ zQD!}1Uga_$MOS4$Mf0(eS^thRcVzQvp7r!=bY0fDvX`=_(aA!Y5oOln=6PAxgEH%h zo!ws+a(09=e@nN!>=0#ktfl9zWWkhK&!N#LY%h9FQ1;Ze3b=HMGV3vzu+Z)tW!B^E z7a||Q|JN@uJJzr63Gz`iug>hT(Wf`dhtj+{v-f$}wOn~z)ql^kX3-sa6y?nVKbJKQ&w~Ej zugG_Pe=YOxn+eI2lI0=8_sV9iJ`M4W1o^|f9kOE;xsZ{5Snhl|M7GO16CRzvX&-RI zJoC$uYjD-}sr~2&{q5Ab)14IfXnE1T!$S?ZI_K`p1j`LmWToaW`T9|p!Mu2mENau^ z1{{=k8m#(^mo=|`&#QCEwM=kmbXGQ^qL-k~^IXot+x4eqO@jIg>I_4(;Yi>G*-q0Q zf;u}DWkSy(?qrijPodul`ggkClWe}-Rha)d1CDFD5kJd5!r0JEDDve=Ze05Y>^(Sx zUVqBT#u#HkooznlfILhf&5s)j>Kyv}5}XNoOE!<`D17{R8VokSA?w?B6|DZ|z=Wf9 zWc$`O!lpi#Kr5#yY)kAX4A#qq54O$VnQJ%U2Bv|@sHV{K&p@F~(f^yswHj9AMr!z&shdO&Lqw^Z#7`Ob%|gy{SE0g(TG=P-|0!< zU6x4#4Yy|gU+nzl1U!BEme?n>kiDCB5_W5qkjoLf8nE5hlhCQnTGHr8GGW|$$xPDt zvw{eENpLOGnQXstLdhKr?a1kU3Nk1=0Wz;VkPUG=?W#+I_1f`zb;eG zIo!0Z{kg*px!~7H(CJVtf8b*b)Bc=<`<^%DTTXO^b%T;%YJObiljX(@xZW`dj$AIv z!geT6ahZeLiVom8@5yF-)enGAPM<@o zc1bd=gsu=eF9~{vACzTY>;?RgL@0jNSl0hWFX-?g3C_0fCyTyk1!D|TAmV-(S;v(e zgiT9=#urWPeX?zpJ=*=vX~6g?6S@7__9K{e(9BLXZQ({KzUtQnQZ+!1-aL|0QPJX?MEk^ zXux-9eonC)nP8Yq7}r(~A%4*c^35X;p0D&HN4uX?a_EuyWdBbEnYt_wvR9T8t&vA% z>U{A`9{oP&1L-ioyUcRnb!dIPDZH-eE@N|k&vzsKE$=AjFPUOa9!8Xrz_EGoA+Sz1 zot_&lT=U?G?4j&mdTu;uS^zO4@?-<(Ii~$3AG{u&m$j$o##IM;4eQ`6`$Er2pT32# z?bcw~GkQ)wr~NC3w6*&~&uw)+Mqm59r&+Q<-!kRvb^N>Ces)wtX0O+DbDaIbyi&6$IZ!{O3z_+ z4zkMw#}#gJYkJKN7wf8`bhz}As8H|IwT?b)+-YId?^Q(|Bz#L~SaTo1< z!yxC;5Dd>cC>oP1aP|OV?1Y)(=>^lExSu1=@wg~Xw>k`ivOLf?&|LHqmUREIHmh2ICv`&M) zy>{S)dy~cJj{Xq#`w%`1+AhkzCBl58BY4u)O(cV7z_CFI_-@u#aoE6pa40(wtsc)2 z2knwZ>6jvH6=5x|)~Dx3W)X(=G#5Sg){*)p#khKfRRg{< z$_RGeD8|serlLAKt@=ua8kFFy06TH~%+BCBy994UT8fSRn}M0(Ej+r(Nj$!~KeQfy z3kNt35fAn02AxyOuvg>~@!}&_NO)Ig)8h(!A`2ER?`?zysgLmL?m*F} zl{-}BKf`td!^PCLJK?YX3mk8}Pc&IEg}!g^P)k2Xyi0#KGtTk@&S`c)?7Ctqq~EK@ zeH~&%(A^D@5kIhrFM!Gp=iRfOy|y0;Fwd#X0#~i}7=|fqbSm=N$xM(1-0{@2|_54c8XStQWwy)p}f^ zZ4)sy?+9$#t3%iR8H($LNXVzR*E)9nrqJ8u6jUVXbG~;9gud<+q=i0 z$rVG+?2NDA#P5Z;nTDK}dyvrk!$p|arw!*SOc8oah=)Vl+i)+Qm+d$xK3}&;o>k}ozE>h2KMg^xwMs&`9=Es;Pod1uG5Y%{^!jLu{$L-f%XP$^!t5Vt_7F8S0F!a_362%$+f8& z4)ZmJLqd`!y=|*Gye7KvEwM4@-`fxFTqmGgrNL>voCi&ob%qk3znItAA53olBD=hQ z;|%)%IQNKv>G?*SuFE>uG_NPD+wlvRtX~g_uWjJP!{6A?a4iJ->Vt#rPrSZ;162L) z1-tEj;!^h&4OnPl1kUgS3mdNnb?zVxg5Gw&@ZQU%pw2F)z2SE054>-+2uz|ZVUqkO zHcwpuqqlX0&+qE-t>sd%IN1w|{eIvGt=TXqrXxh2s>iu2=D~~Q1HowAPc-Q{12jJM zpg+&8$1VmFV9Oy>C|LUgs{+OXmUVzab!D0_I|_L<&NX#k{n4FxEmYk51EgD(A9u1(Y@e6qzHc4Y#zpZ^gr>srC* zYyUv~w~zQ&W(xr|X5hc*1NPB%fXtd6aOc%WtX|GRfJINRfAInRJx4-^0u$)|_Y=;y za)39-J3^4gC)_;U6-MNAfTcga;L3>MU^3niELwcU6)ipBi)IIq+kL|r-%()qv<uUdb)T?ZzrnD2jteAC z{e;h36DZRk1-ARYV1&CROg0`3IfuTW=OkO`Y2gIl#cx>C*#@fHIfK!YZ`fl8!0Nk0 zVS8{r&dM7K)#7k?P*RVV;+!Grt_=Kd{Xj2t13zPgssTUIbmLT5S~~@zoSi8hXy<~eI|r& z|BiAq1n*hn!7i4bGuLDdSj?CQLqopc3i?_`1dWFg!$0B6a(X_iahtUH(EiFt+;IT` zPmBiN*&ndZaa04okunSXLf_-$$IhU;b1<0vdWUV^Pi(+Lw*$Buy~8torh#U&-q0lB zJ-%u`qXBEV^nq2+-l3(@Jh=Ueo>y8QamC+;-1AQxn6~}{Cf!;LspEBFsNq+fF>PrB z9+uY(a_G6#Zp>OR_1A~YVc#*ra76cPamr9%i0s%DYGUf}LR|p3M)reZw>oT0|9_ZTKL)l8c#ACz=fD!T z0r1`G4faZ12kw7o!7KmQc-v+^Xk~;$?=G+LmyIKMMFzsCZFM+q!bs3?J^`;gzQb3w zyrB{;6>*>U`OBQenYK5cWgc^&v=R5Ej|z-B_GyCen7vDr%2-KM{vsZ z3FifT|Ss-r7|@5|LW_ERO~eyhQ!U#1E#-pL_!(0vTDu@zjlK7eUw>1QX_ zKM;nVehwqd>hR4&Um@jX4g4;6i!}zX1ic;=kh6&Xo9VDzc;R0T_Lu50tMhx|WJwW3 z_H4olVeUfi$1*Tl^#gNenakDr(EtIAdo|@+?f{uO8`X-?#^5K~j7ul&2PeZlJq@m- z#&gonG7cKe{fX_NC$wn4A2LSKwa2gRz;EStI8Bf13xnYH=k*Xe>o>Oe=>q*`?}iDU zUocnJ4@M}a!;&_QIHT~n(BbJaaMIM^Zsm-FIp?>-n%_;iKO8+@@(+T?Of62^Y!zwW zaX;t}Xu*xdEwU?43E;Fti_43(k{6W*fp$VmF6c}M-@-B+9EY{wc2y?ubz}Fyyv5pF z3yUT~(V++^Kh=UuN_@rdk?n)_5!&3(75@n83{mtmE-hMe8`3)o>ikV-KVfxVx-V>&lzZOUyu9#-A~Zg%Y=I=y4=&^&4LsChhE`j!1)w)5|ZYKK;{~9^A88| z>a6LP1x3U4x%S50gk|3^!$~Itu6fFSUY&QPUIw#kx?JV5Zo;%I}c&Qn{(^^-FbBu2cLlP2by!m9YA>c=>#OKYtF6MmCvhlab60v^=QG3?H?#~ z8+0BTAJgXA+64=bd{4rx`&wKd{fRt0xCFDjw75<0eq78gy$r@SEx89-X7W{$H^6aB z3vSx?fxJ2o>Qo4IZaSR3Nrn96k6W;!kq)OlVvs{x~*}A>hIiwiI%UW|Sn?(ur?l(d3ZO$#pNE1^2Cw$&e2D=`$;yNVu5IpXefjnQAyBgk~{*Lk@j4Jz$UZJzZ zL5og9#Q_a&*6G<|ACGuge)A_jedjN}HWuN`?$7AFYKnNRT!2pRU*V6`@uKUt1c(m* zh|kTph?9E6!-82aG3DBNvC`@&lqXl>3yWpqP(5Fmef$m{P97_U4b1?p7msmG-AJ+g zTrdReyNgZM4HAvC&%hP)`u)F)6uN-1=Pf+$uiJo+j+H?dgIj28+)J#@wu1E=OK{h&t_?Vt z{*HXw`eH1-+)Z46eGH^5qQBqYp(pM$9|8Q$0(uMIzT!36Oc>klCRX{k6FnBshi&3z ztX^d$9-h4wTHLydEi$@@7j6bY?+q7mgrlR_Z%Z_+ES6(x`~KpE!UQ;QFd5q}94Trn z=HYACG#npoE-vjSz_4FY*w)BGw2QnB2kVaF%YVy+;o*f~W_}dEt(eBEvsHLDn72QO z1z(;CM(^|BaD4~{eI3fHbMo>$SRJ(kO|st$%?xhA_mG{qUT(#!bN!c6Xt#1D_FMQ} z7_sm%WYw+4j?TcV^Y_w6&@9#i19N`~fq!0s-4Rb*(`*K>&VM>Tfl@cbf75;l-Gn;0 z5)Rn9+MicvNPPlBI+^2#+B)IODS8W&F=p7edKa(GzYjlwr#1a?#@;mIZ2AN|+sm+> za}GK3=@AsCdSbavAxSYQgUQ?1;f~asWWxD8=vBH4y_;VpQ>W)Z&bNa|dY>cr=jKDv z=rHWMFr4Jghz6gk#$4p&&EgvWqwu6vGtRe=cr1fy1MwiR&Fibe+F$o6I+Y$#?Bnbn$ zbppjTPwWwKP0W4rn%w>Ei3Pp$#S1gL!ix7!XrFjR%#EriMi!%~C+NEP>aHOyv$01= z5X9#bUlY%rLvZ-$Tyd=9Z4xue5sBl~2COxuge;onh%=Af6nz{jNQ=K?u+}uc0q+QY zKLrfGzi5CAB>!V(5@E(b@hcxjt(Wa{ezwU6Uu|h`THP z+WAZ@QZKQBH_lgvE^1{x7p5kJ>228!=ijI}K3e6=S ziOj|ewHJ3)l+ z!qd^qDVSI1N#T8P+QJliJDq*9`kX;%e=iY}F3M!PjeFyF%VeCpyayR4>x<&s6!Zw2 z!K?G5zk|@iAqhXmcjeW&+5LX_eDz8EeWZhMK5h_}7AE4yfBy0wI$iNsr!?FWK2X?k zqc7?@pTI^sO9c0Oow2ZA3XVUuTM&3lboWWXX*t7$f>k|n*{fLW>l!R{_O!tK`dHK) z>nW@|&;dI`qB8&W2QTedc)0JO*D47{IHu@%t_qFOSAo-)_jx#+qZd<1whe+gmnoU^mnOZ?jFa*&F;wVoVUhq zQ;y@xK{p$4V&!0bubqhANB@zz=vku{y=8?E+?`iv_cb;+-ZufahIW;ybJI8*G~P0`1T;mAgx)sr7@k^aDQIO4LEW4k$Y(+JlbQAb+@B2XV7PU z@#nF3Que$vOc$L(XlELzn{D!Ti4tMj;KThQY+iNS&Cf-ISOyr$4|!*8;;Y?_HRD*8ub+9@5f+sYbq-o?<{dbK1@ zioy7AYAkL|eIna5g67{x{h!6*lVzPr8*5uEofC^un;prx-7@SOABW=t zO~{#P_PE26p7TA2ktLdl6~|)n{bdu<9SJ6_j=?`KhLUd;L$FnF9L6+#ETea?$Jct% zxNJppvNC)Kp6x?FPk8R3toshyzc&g8IGB=aX@KA9`|!&wQ#S86ha*QtVNEM9**{tY zo8OJZ%=%Wu?U_A}JP?WJFFcgRF1AO<8Id?%2Z+lpdwl&d5-s&6ko`|=@ma@6EH9Zs zbPn6$ACpMjF{Ce9lw^z9?;}wE^Jr4m$`*gQMk1c+MqCcsAZZ?nZwHJgvy!bbJwFmP zllqb*!5UA@j>Kzy#*okf)|loNg^?CW_8+ju=wp$1Z17Cd)6*KK)J0)HygTVl`%8_Y zaPr?}MEk4_4*VXCF+=B($-8V&uX!|T?DZwvh#j^sjK&Agy~zV_`g}5@(8|h>oQtu? zO)k;crD87WwAv2)9F9WQ$-d-uFZy+{FA~?Tp))VqU_xXh>K|TDo^Q9otpg)*&Xq+( zhu*rzSrLiXCwG%IZtsY3+aqzv%PxHG1SizViNKE&TFKJrEy=ot(A&6_T#=dRjl?&L zB5`hu3$mG)hNF*9ES4P0mVNm>3f~CPSZbOfTR3(E4)l)0&qobq%Ojo9b59J0UTq?) zUoisriE+4mKrbG$ozePk3>tmx#fP;RfrsYDVcn=Y-eih1YVM1{n`JNfKhDE3YfUV= z&mJh84sI*;bA0l%Rll`37lAE4_u`9co$>enaMX9N;FAMfaGpj4P8`)m@U9qxUWzb09b3RB zraRMrC&JOAXCHo8=@=ZJ5{7XJeR;hv&h+1^a4bGoW`DMJ3|`F*!=3-^vDb}tL4C~# z)R=ihR#ZF&C-ex%*SBZN1{@rNe-1|C!m1G2`K4oVlXe7-iJBra;au^9K@^6~yJDXU z<8X9MIF@eqx3}_f#e)t}IO=pCe&+IVsQEG+&kpOyS8KUqMY||0=I`^r+l)io=iz7< zc!Rff7=zP}M&eI16T!%0EH2iiuUA7`&^$8+&vy>Ti0Q3_w@0in=SM8+Zn!T~=Y3XI z=vxtkYj)bn)VbFXEBb$23&L5ewT~q> zxgLWii!=qZN=rQYCl)*3bP~qQw!%aA>HlRr|MKb_s<6U=Ut{Tehk`oaSz(O>Cmlx% zPa8p<#ryPi6&}Z+vqZ3y*`V&)1pFEOiC5=+rv{_0W+G;h@4R7=AKX)k$&d|r3uC4bP&b-8wu@o zg}mf5D$#Bympn=z{lTUh*{c>3Tyhd7}(6KJbz^%BQvoFZrsh z0-Fnxuj<_Usl4RLda%<qbJza;ctP^(dDwmtB#+)rhmnHh_0!&6D@gvn{hvJq$tPG$9dVLR zurO_^AbAY6zx@#;Utwv<1VQpA3gnw0`4d}z9x6!QMzdZo1j*0nwxhow`68D-EfyqS zWL?ZxUh-594TupWALZl+$-Lyp+_YkaAbBtwrS0J*FXw74cR})UCZ6->C9kMDSAHKU zNM2EO-oyv;lK<5G+ZaLeobHn)@sfA7#e}7TSyUwEI?VWymmmqnB zH?1`mC6Dk$dt*WJ7k>#*2$H|}X+||wFZtyI?2ihPU*2m(1uuE+mo7RWNM8GCjgooEv;S!SGC}g@`~8&j|MUJ|-6BXH z|Elxncxi{g^bs2bX@>yGNaCek1iZsiLE1Uc^mG{iOSRJ=fSV~uI}J7l2l3Kwgr8m9 z1ZfAt#!vpdv}57t){%m=OQC1y?Yy+lL7k7Kxd_s(26b*}e1eyDMC?jgDo8sb`UEu= zr2P`}OOFfEPKj&6VnNzXu~nnFDD9>=-n6eE?X`G1xRdlQQ;_y% zsPm2Sy#;9(hgSFvLE6P(Gjf9l-6KO@h6>W&l9_vYi_)$VpW!&@zRc$)bm(c?N>CPw9K|EV;CMXJgP+UfQSBbJTcyX;+U= z)IeU^p)@W0jJ>qmhm0+jOS_W%JhJSi9YN~+>;8GUv@1!SpZn+9OS^{pZ#gZOb|P7h zxM45tCJOx)Czo~~WllEz-!7xNnl1LyzN7DoHgahv(&v(vytET(tDhe)?Nf^STgFTK zl%|+$q(tY9F5xt|FXq@7piK3@@}omcj`hXrY0RHATTJYv_xo}%QZyYZ%jDEY-cA958X|J~Jmdr|U^=@xDfCGVbXikB$)$j-MqEJ{AI zJ^w5eB_CPd;7C#OkqtiJD@s1HrY;Ghcuz8By|) zP5ZS)lze1U<#e}>%12gL{;w$c$V#lwijt2kN zb)w`Wn{N>+Ng-;Vo~yu`P8)+B_G*rYiCjNkwLhQDEY|L*)M2_DEY|L zdDUJ$QSy=1j3J`rBXgfKU6g!e^L?E}$w%g%>nch1#z%@{tX_eNmKrWYZloM9D|iYkRsV`N(X9 zNKx{UX^u<~B_CO9o1LQMBYV*@T$FrdLuKnk$w#K&HAIwrWWJl1iIR^@o#l4HqU0k} z=d;bWh?0-&uvUmD`N*QRyhO=Ic0@5rlze3E1|AY6A6a2`m?-(kDh5W1l8?-yJW7;& zWVLS-M9D{1pL|-Bd}P&`iK65q3k^FhNydn9tVi;Zu^!1s#(MrA_Rjk&s-^qWCO|+y0RsX? zOdv@>VpWj@MF9~66_p@Ck*J`kAVv&`n8kqcDvB9Ufj(6SbHV3HW@o9cw_@Q@1o$5iTU@VZ4^8*F;6MlPQfGl zZ>;aNlY&Q9QkY4>BNKDqNxLX`WKsr?Y;WXt3Lcr1!6Vbq*+#)5lQMW@T@-du@W`@+ z>%k)vv-EMm9TT&b@VwxWN!I`#ne=w>$fS=09+{X=OxZ=jBSX`6QSiw8GPYCj$i)0| z|8@!Lm{R!Dd!6OS19tS)!>FWiLOuByX$fRoqkL;YV9`MMlh4p|(RyTbY z1&?gAumU6 zU+~DptTfBZU+^GBGQzy+FYuyPaP^!6OrM zC#MS(JTfsW%NJ7c$o?Cr9yw3JBa`$iq~MXQx>H2KBU>#ncw`q>6jJcW#C%@39Xv8A zgGcuKoG^dwuSX_j@W^cMoTuQCiFv2$1qvRSdin(l9+~vM;E_omA3UV3dn^kncx2M+!6Q2)%+3RkOnNVGb3O%+?9SFB6g;xa@pCD7WZ`r6Qt-%HD`ryg$h5t;Q}D=I zD{LuvWUhS|QSiuWT>DV)$P!e>Q}D>t>?=9&$bR__q~MY1xE6EZk!35(QSiui{mJ3L zBMaYB#eqlmtTmehk4(%8ohvx-$i#fR>l z6g;vGgHtJZWK;X3QSitV7VV+nkxhw7px}}95!^QL$UKb_DR^Y^?nfzjWS!4!rr?o@ zIe}k5!6Os1+n;O-9+{Z8-PuIJBisHek%C9|dc+Y59+|e%CJG*zm<>84Q}D>d+;7`K z3Lcr5d!P&o9+_S9QVJefm$rQrJTh(9GzuP>flD$4k8EK1P6{5GL0lpQk4zb@rQnf` znJv6We?79*1v4mkWN-JnQSiuOpT<(~$Q)mIQt-%F&tM838B0VIJTgg`D+P~C%z>9i zQSivb+;q*Kf=4E1<=vJPJTk``FA5%+;RhWG9$Dd7EeamlkY-m39@($wN)$XYa|0&| z9@)?{T_|{D9SUeAF?mgR7P1CK0z zyA}nHjOccx;E{zL(xl*#Z47+Ffk$@Wd?yMXnV1__lyl&biTTC+?i4&SF?ajiiGoM= z)<=zkN7g5?k^_(IcTgt^9+}JON)9}-?BS{uJhIC<9VmEY_U=j)JhIl_RUCL^x=MdI z@W_-q)NtUD6`$-v!6Qq#(TRdbrnt8o1&_?!S%ZQ{cKNg#1&?ggS}h75*;KP$6g;wO zr+ySXvM28}DR^Wy-}+PV$Z*Hr6g;v>&HfZTvimkYD0pP!;`&nX$nuveQ1HkccPmry z$QJGHK*1w(`qqJhM|M`OjsuTuN?|1j9+`6W4-P!C!X6(u@W>*PzjNS`Ra<`Kz$3eS z;{yjC*$dYX9C&0}v2Qr=$ja}(=D;IUeN(}KN0wb&!GT9MX-P*49@&1)P82*c`5vki zJTmueH3}Zt>D&D&cx2dYFa?inPQD%mkIYV3$#Wbjcx3WUMie|UF+T})rr?qN2^c}aBNOww zS(t)HcAuzI@W{m68TF;$k=@`rQSivxai=a6JhH(~Vm zoIXt!JThCsBQsP|q~MVmAIG#d`G@egzTxQYo53?FdsEl?E(igSHFMvqHUgE zu=e<1^wdh5YCb5#$S4_wurdy2uIdXu0L)X>ZSkbm!aP;0In$AXlQV^Rs@AosG+LOU zy2Q%>t#~@0!VFcd*xgiFn4y~Xa|rrVvW-e-s5TfD*_{c_p)f;L%x*RdsdR?wR$+#! z+xq1cW~jPM45ctbHOhD$g&C@1UN$q9!VJ~xfzv3=Q0*8shr$e1G3(SXqA){M%;6<5 z6lSRY|L}ReSPC;#W2^ls%uwZ9R#TXv8a^k4!VFb07p|B{VTP)hZEuBBn4zkG+%?|uV zVTS6y+rKExP!)5liX4L(sv8Pa8O%@}TB*QbhN_sqeiG*U|DB<#a8a4T4Aq05D=5rR z4RPztV1}x<`%MZnRIj8|P?(|m%ls~d8LHa`cVaL@wU2Ezg&C@1mfwF*_`dv~8LDFb zK3##q3{^4j>{CNwhN|V&5(+a^=SL|pn4x-TKplk{s%?39Da=rZPs<3}&ciA5dp7Lv?-kDhe}Hb*??4Fhg~!)ddPORELhf zOIQD$p~?zRQ<$N8^!jNEGgOV6HdC0PI%`-ag&C@sb5Bv2o7#sZQJ9-*yJ`Z3S*cev zVkyi=ElV+=FdsFqb{vJ7sB}tS3Nulk@3NpU6V+#?CWW)5#9XgrOkq~4n9tevpfI!a z_A(U;GfUG38B&;WTKH}xg&C(xeJrSSe(8x=W28{0L}9M!s@Z-N=B$d@M0+NMS*+Xr z5-H4FO)HD0Fmv_8^;HyRuD*M{hr-NNU-zvPX08?#7f^72Y>UoNaDs-^7g2Dj#N0Ig z0tJ6c%vy~XD0o9N5L=i^w6N-qggN0r{JTVOzc6yUlViMs^JveH!)99=tIGM z%iA!Vf;TtusR;#d&Ld?E1rc?@UV5gft6x=|ol+6_UL8A{_DR_kn=QdOD z5N|I$LBT`xRXs|BT zwQc)8px}Lrd9Okt1!p|f(etR_}z~_CsFXb zn_Ny(@WC54?WEv?rw*D;!QG~7b11mvVjeVf1qG-3pTk=zc--EN7b*C;V%D|Drr`6c zY(GrF9TxK;<6H`!FME@Z%f>XFRbqpMopx}m|9lV=@1Ku&?Fa-x(Be95r$GzUSn1aVG=B6l)g2(;E<}wA3Tg;7L z3MhEod-WLwk6X;n_#6d~d(fQo6g+M*AHS7H!Q=MWMk#pQ7tS1~;Bg;FI8VXjel%w% z1&{mT;x!aJ?g)C6g2!E5y^Dg!?XfYBg2&yf?KlOG`_E5G!Q*~;Rzkt!u02^y!QF1F z<0!b>Po7_<;BH%(U!mY`Yuw19;A_9F+eX1}&6aGS;J{9Pn@Yiftt?NXFrVSYo(UA@ zImj=tr7-7V-QDvPJmSw$JOz(f%rCtzQ}Bp~Ub{!ZBNp=ueMG?{eyaF}f=4Xox780Rc*Oea9#Zg#7kb{M;1Q=Sx=g_%HtTVL zf=8V9j|KyuRLrh7`!VoIzgOuq@JSbs(`De3I?U2 z%ZD=XN#94RFz`uJyY*(^lZtr|HywIExnFMvKIx&7p$vS|$pL*B_@rFOFa|zp zmYhBVpY*JnJ_Darb=*h>KI!$3h75dCG4JSR$iOGH#fA)gQZYLpHe%qDin+GLh=EV~ z-?-B|69zu1ys{|+pET;DF$14;(Nc2;KI!p0W(<7NnLn)<_@ur6F=ybD4%4(|;FB)5 zHD}wq`jr|p@JVA8M=|h8-%K}R;FGG)AI-og zEh#c(;FG#38Zq!m?R$@A;FGS4GG*YC+MP9I;FDgyW5K{DEx&ETz$g7@r5OXCw9miB z41ChC(MAk>QgdY^20rNmOG5@e>8>*)8Th0g;|&@3q>F+L8Th1D6(bq=q%rZr?SFmJ zywAoAd{Vg(V+KB{L%}EpKIx$};r;sSlg===VBnK#M4B=1NxvvrG4M&{qAeKsq`@~W z8Th0c=Y{M4`lJ`%m^1K6eGDua_@r7EW(<5%M~zVoeA0~FrVM;i+kZ_N_@v8wm@x23 z+pd@}@Jaoj8Z+=ouh|$g@JVei88PrlCq6f1;FD(lFl6A9u9{}Zz$abK8#3@oHCGuj z@JV|K*Mm>m^1+CKPde_R5d)tzKEi~7PkMWsu%5p@>5+S;417}MLnaJ-(wE*Q41Cho zPeu%U(&u?b41ChIeKrhy(qGOt41ChO<(Pp_n&o86z$aD3gn>`$`NfWbPx{5cfq_pt zH5oDRNrP@XFz`u#>_-fIQlm--20rOJOT@q@Raxu6z$dLrv}53t9=&PLz$exHX2ZZI zUD1ay@JUnW*f8)(XCz<-KBe_X5f<^-Rr`@Csm0W!@wuao$JcLCmoVG zhJjD&xzCk>Px`=O30$pH$4r!3GR` zQZZ{r8wj6q|M5xx8xL7Of`Lz}zjy=#pH$2bGDk4*Np-vFGw?~#CIbdOshC$)7%=cj z#e99gAp@UO%%|l?GVn?D=NK{YNyVJ}dL#p%v~#Nx1D|x=ysiv48KI20m#o-yRHn(l*7O41ChwlQbCkq`6hy8Th2f^HqhzSN`LZZf=%m;FG>gm1E$O zn!hRImh}10nO|Z4M>seaEK%zO2j_+D+2qH8a~d1z%E4J<3S&obaK>4ob$<@dK@+oN zxefgL=*EM)h`k_2hCoX$qr|_{hHlV z0%yG`9aZAsOt%lSH90uPEvb2<1kQac&YmlQv)=Ob`*Lu;+l{lj9GtDT?)Fj+&QlBd zVNYS+i$LdsAUP=ZZ=3w5-uM3QW zc`vDNPH-^qWsvJF4(7egz(+Wk_mZ!BiGz7BIy9Gqc`rw6&T}yDC3yKs4(7eMWbNZ% z-pkqBn>m>Gl9tVA)$0nZbYTSN8N*Qts=&AWYb3RO?$9%V<=Am@7cR}=5`8QDPI703 z6}RWwWx8PGFYInNs2v}ebdTD3(00 ze@WABmg9wkvfA;g!OyAVito5BBwLd8k3%+Mc3Jt3evJQ$Q}#I)It){8UP)Qi+h~pTiRQ;dki_ zhf2Kc!2rq8K9_0Y-Cwxr?mNlc4?J};5@tCrN|wxZq0~3E5>Ih)l+3s+p^~>1xZm0I zb}awy7UiE*;G9#hi=OVdNl|b)wlA=ehn`UU-t;7&>V0fgXCCBc&g)4&{~w$mx1U?$+lxdj*0B}yReFT$ z_OB+XQB=3-^=3224bmX}wp=cP$C>?RWf81_ojD|d=T-J{mU#YM^XslpC9sCM!u4W? z*TM_;74wsu0o>y=JxPP`yb*_#xtTheWU%nO9pnro2|IfbZi0ct^>020yuY_&q&wbr zV1X)GDdZ2?-l*)vi5c>r*)oWO_wKrI&7;5h?|$5!gZFE{@c5xg;oOO!?xa9?-b}e3 zlJK?Nh~2g zP55PFk<^>q3DmHyvt470mHUu0XVPu%7~~g8`PBcxCN2fm2`*aXkM7VyF{{2bmq;IH z%04ZLa;CFHx`yP_dnLtxAE)8fCJC%T+o}%-&r3Z%N-qChbKGt#uIBF=nuP1cJmT+b z(Gl(|<^x+E*-D?+S9so#z`=IX=Upm1FIP9-PMXh^w>Kq_5Bl!JiFv}X;Qz@}nm=UA zY4~zthWxj$Na6PWeIKcC4dnlR!fejx?|Ww_Jiew@j0B#y*f_&(q%fcGrm#=qNA9z` zbEX^FB;8||PuulaBFqxkdm+(|^RP4WRUtEkeHAU&W@~g`g(YwicTi)B4XK#O9L+u!>({JX_g%!Pl~u;!bGt(ZN9^=J$0>0EcpHt_Fj zsnFYM3+rhW9*6yXUE75FTF2eDTfIt?s2F~+ng3|3-J(0{q=)dlXTz-QR?h2A#y518 z=n7d$gsdj146w~szht|)RfWV0`7ipLbAO?J4*HwFrBGKd|9!u@3Ac;c`EULPtJm0O z{C)2ng~t@L!7hp22`@DgEIjXosrON2aUEWDYE3)Vyd%uI46et|LMtVDt#8pQ`3CG# zHM$)STk;ScR;|Ul$4n&ExlfSs^lEJA7$#X%@*dqwufttN-!78uD?{16YVlSd4atgW z6(~cs0q@?RUpW41B?_q&W>D9UE(+~aj?5h!abDThc5LlkixxXI;&mr8Y*Gg_pxQ?b z_|}97njOYRkkxxDxn(*Ie9D>C*;#H=u=0hwzz;LTH=iZZ-@3G=~)`P)4#B)UbP(4N#9 zd}Z8T$;$zskyg)Ie5$dw9Zwzl93>b0#!T;#WKiaF6g;gOuZ`B>#O%7@DKh9*iygJi zxg`7u?PqnEoXYrvgmb>`;zy+bdzG+^}y`I0X4?jYqe zHTYI2YscNp?xC3UT49b@f9_EEd$h-}24DERR3hdzs&~+2|KE62?FPwA^}Fb@d=*v; zkVp#GzDGBQ|HjQ*M@Tj<_<&C7RpFW0izNE#)o3F5jlV{z7L7SlgYKQE!s|O;Dzde# zL2YlVao>rz+Hr+&9;0veZyfmTw9Wn99dVy0zwq#yWj1{mcf_$p)p&?}VmqFZ(**~s zRSRdc#@UGZ>gEnO-L3}L&pB80q+2aYTT+A98gG=WL7i}NVKsiZUR`qiW*wUKrW*g@ zOC?qlJL1bne&e`+6_SfX>yf4~k6rgkXATKx$PRt{3*S=jE)nzn(e=n8rwUI$`CcOC zfSVog`tHAQl)>>LLxuc* zIdYdrd_+48YVeJCBkoB^Df(1dgWdFm8G7R%pr1lN-&yGA#XMJ|1QiLr{aK;6SEFx) zT3U`Rh2CDwM?XJ8(mwyX(C43@^8_UbeZH&E=NHz$L(<;9r_kFYj}PdM(A%d9y?t-r zA4uBImk9lQ_3m;c?dN9;{e0GqZ-@vz{4Z$_FXHp-tC6&y?0&6yqI+r8<3OG&zB1QyqE)r{X%6zKi^a6=kL^iK^8(ke@N)(pFjPL&ItXy zwbK@xw%cEjw1-dKmt0i7`4@^4`u1L-#WoJG$n_rCAw zy3oVB2tB-*9qOvlO`)HkKe4PZt>Oog_VWvcem>^hd!!)r^Myh`uY30`nkMw~lZ1X= z%$K;w!kJdTaHY`Or>=U49EINANa*d`@sASZBlP*hg+4za>>er-`g~QP&mXYjHKIaq zKUL`Mf7!f3(%!zm(A!7cc!fNLe*U)5&zqd5R9fe^3Uyx0EkgyTL#XqgggUP@q0WoBJoz(~*7-iu zoQmR4y`$1Pe?zGAa=ji?X`O#5)cNA1$5dM93xql^X8RS_sI<;M5bC^q?KLW`^P7b_ zFJ{9>SE#hky9sqZcy}?C*7=)4op0LrkV@;kxlreazAB;8IOsMTq5rU^C)bp`IJ$HyMqSAWaU#RC| z&iZtZN^85;wh@wfk4mVtwl5KCyO>qKf1uJjKR;Tv$e?2xmDc&2LY)`$z^$cJTJw{I znlFi|q|!RyTd4CZHvgv5I{%}3Sv&4C@i&#${B?4PHV4i&P-&g_66(B|V^gZAw9e0q zI9F6SyNpWf{BEJnH$1DR(mMY`sPkezt@nvaYrb}IsYJ|^TYph${ZAF@f7XgmR9gRs z2=#xa@b_=h`u|+0|M^=!QFEc@&k}0>;fg9Mt@A}fo&VM8BbCwK? ze?+CV{a>NBXRm%prM3O8P}{}K72l^Hg*xvi)cMzO8eFl!MKbJ$O1I;B|nH#KA_5_3ve7s0z}#N{Q+keHtz?M6K>HsSk0 zOHt(#WqMh?5&!g9f>L9YXs3$}xZm>yXmwC$8vVTi_th z??ya&$uiXX{5NO8HQ<1|N$ADIuiT>jjkw3xW$4%OMy};*BhD#Y-i}9ouHedB8u87C z%h6@EM$VpV!r5I`prOw7T=t7*?B=-~g&gf7%y4MJ61C;1^20Z-LO6>duyQ%FYx>Of zyxfc}otC4sgWqvhtD11rt!2p5;srNrY!lwGFc}s0e#KoD&dl0eor3DJA9JCR%~*HY z5;V-=HJ5p+1xwy0AsvIK+|rB|oUtqsCHE@h#;Mmt?K#qo#y@VxzQ>c0#9VMOF1Fw&rU~shqN@hoQqYXI?q7(+ zyr)KkS{-V^+D~GHey11RkkO30HpHR)GaA%wWeb*D9f1Ptd(v?M%{V0~60IueP7_>P z@UhupNNbHM-LRk)D|d)!$G--4qvj)9@j|08B<3rAiuCgR7OXfm1c^E1XBXO(*NTng z!_kR{!X7JY#WT$3pjio>X`Xy5c3BtFjtg$obB_kM;;S>mkeHWT7WThd3wE!WgANtKjCFVV;v0W4HmK1|@cvTwgAk>6{=yqJZUzuix zG~$zY=OQtydG?@L;~Vk$p7VusTmCmcv+qhf6*S<*ALCGm+JCt_>l(2A>v*(DU7nU{ zH)4H_sCInRuazrpY{0ScF-Xk3nH*L9(umg_pN$4jYT`5=HR2IZBGAy$4cye9P54|= zILa9DoooBih`V}4qh;g1aMq1Yc&ztajn z$VOaB<596w6<00PkmmIZQK5Y)=j|qZ?l&Z&)YLL=z~n}}xiuNBIQ5)M7}1264^KpA z$3Er;+6jA4ZUJh#{e*ilsRg^tn~%y?zT|W?g#9)n7P&2W#pPwk!SxF_Lpaaq-tIZ*N8wj)vT`e4@Vq_0+53$vFKESQPDP?X`8V8Pi&nfNDi&qd zzu~$sYsKHb#G|(%J?YcLCOmn4JQ`=*h5Ec}#&u*Fk{h6hhFtiCb)4c*@o-(Ff2Rt6 ztcyZDI}Jb$KdW)P(tPyT#1vIFRbl__(WrN!Bht*Q!gF?oqYJvGDC=Q0Rw;}{-(QbJ zN`oq~+MqcoLU$M{^!tfRBLmTWXMzT7`iVCg2B91~8)SaI0!OXzLvL42M2@{HaqK-` zboJ~6G-JX~Y^vjqlyA;J2QF4&IW|E!Gk6kuP*sJUgJ+hP=u_Gnl}5K3DkoUxT|hNiM06r|sT zt;2;gn$`xOVx1 zzq~D+0XS6wMOZfBm3;%yHF3E>4U=5TCu*B8B!bTkF<2E@ZM5K zbnSTu3VXG~r5X#74)*HSZz>e_>hswC6!z+Zrz#Zo>hZ0L6!xnA)6Nw3>ZVq0 z3VSu8wikuHnq_B5VXv;5J%Yksy^~@?VXr#RG^VguhazhVdo^OIDTTcnnrKR4ugYZ& zps-g5PDK>fYC8 z6!z+aZ`Ks{YG|Apg}rL^#E`;XjV#uouvayThEv$9l>vPy>{ahudKC6*)RX}f_Ue)f zbqagc&{~DUUY$9hBZa*>Y<(REdo?TZ3kQ2O*ja(XUX|}t&%s`GGEkzhS5-bMQ`oCY zTdO(PtJ!Kb9PHH@i(YWBS1Svjaj;k44!p_1UR6%M#=&0A@PEOGu1@&Ihgy)Tk zMq;jRa7F8cp77ey7$jyN9}o2TL=)c6M<6ketoKH9wl?7_IUz{Qn}qA%9%{yt6?0M3 z)$z#lkI=`?n}af5xT1_hjd*O2aHO@}3pGCxzQaBYMh=(V(V2OTSi2$sIga;54xO8D zLa0C5Ry7G-7JBjvj#E&9_f#aGCak%{2L+l>L2J%7VX|pPJAPq44K4cGguls8K`&oT zL3gs7aah6(H0+iiGQTR+O#SKT&7eN7voFk@AvOJYd2&l+|StdYRCMwNH7XqYi%PywGzNukl9ZkG;{MNo|9Hte!56p6Xg$O%1hYr~(e%|ZW-RVKJ0 zbhj1%dulcslsf@+1R^n~ue3p_``hr8%IWC5H9>dp zwc*O;lTqw$D`a0StnJFQc6{iC9hxb8A5lCmyyqfrsIf%N%`G_e-87UfeAaFi`Xc)g z(~)7E9V+b7iU&G-qp4RdkjEe4yi~nllr5YyHKeW?@2{GLhNW90^#-9Iz8fOEK1;Mn z+P?)wAYD%zbV%r{f82~f3ab#}hBf0;3NdKZZH%f$HsRc(v)l2fW;--wqR>~|2ti_w z7~+WZh0oTc*hnO1&j&(paJUgiCCoxfX5-M*gho7g#dMVRouC+nCj9wRKsyc*&Zmz4 z)QG3|n~KD|sMQwje%OQu=gmMP&e@{_v?MFP( zDII5Y{iE<&rU`Xo{{+;zLo-&IJRa@5H4b&})`ZubpVW@a_Pe7RKcNn!d!Svu9%$*Y zM%;V$WK{UZ3w;oNi#uoRiH5}bqLcQ*?{8DQP{!6ts8i2o>=5jRsI?b5Jxurxd&m_j zrg@-H+=^GFyQ08SH+1|@Grs#=BV!z5ICs|DxkhM;pdx1zi|DtNfBKDrco6?uJC6MB19^mO}MfezB^@u z-Sjh`P}yr$95T*YB4*EwwbDeRdUU&LdIQP>h>R%Mr>@5*_D7TTWq}NoE6u-`xJQ z&NtiII9R8(;FZHV#hf^;j)Qe}R2xiTogHNV$r6wyAWJ}&fGh!70IWC_R;kR>2XK$d_k0a*gF1Y`-w5|AYzOF))@ECE>pvIJxa$P$nxAWJ}& zfGh!70IWC_R;kR>2XK$d_k0a*gF1Y`-w5|AYzOF))@ zECE>pvIJxa$P$nxAWJ}&fGh!70IWC_R;kR>2XK$d_k z0a*gF1Y`-w5|AYzOF))@ECE>pvIJxa$P$nxAWJ}&fGh!70IWC_R;kR>2XK$d_k0a*gF1Y`-w5|AYzOF))@ECE>pvIJxa$P$nxAWJ}&fGh!7 z0IWC_R;kR>2XK$d_k0a*gF1Y`-w5|AYzOF))@ECE>p zvIJxa$Py4G@GW5_ZawM2X4l6NG1opGiZA{=i}`JlWU@mZK3v&@ZR{A;jy+H^?yQgb z8mImw=<7UeSG$a*o5Z%`YYrCphg=3bkQqnB95g}~SBGTc50-JHRBs9HiZS0ms6S!( z7I3zxSYuoYBUZ(sIxe=uPm*oS1ScE{Imjo_WPT9LHf8d%NIm~Z?sn)K$5 z;l<5b{6=>hvTXe|wCr^~+wfy1In?72a_QfR|F*`T3|^vzC;h5mw@w6*l2?nkXSMUG z{Hhc(`|)}DY+Ml6TECQd-YB+BkL|%0h9#4cKSr_0Z<9z`SEY8m#xkEePxc^}UiKtn z9zJIWlYik(?t671V$Qs?gynCaM;^VH$c#F5W3SQ^$nVHxcH!DnHmWp?*_+%a z-HDiIKiX#=(9MUWM5z%mFS_h&m%p?-+Y*sXk`|vdH?&h{;{uaO-%K~;pZhO695sWK zu6%Ch*|if-R!t`t(vBcg#Zi3Zt17c8QHI3RO9^dymgQ^ zso0l=dhN?*H+#jk<64#N=H2|V*|T49M9fD%+oPSnv3SGAM6z3@6iqt44!^C7C%@)d znj3$N!CoH{$-a!0$hzbz4!S&-DCQXE4Oo8{yPSz2TeUtR{ZTx2x-yp(KUcu<+b`o5 zzX&p_+!!zO%f+@|=Mp;R{wVoZck#=t2og2I8LxQ14c~5#A)Rc!%(8~<#;yJ0$*4uK z`0M_3yevJM`1PBCS1w$MGe*RbvB5j>>rV;zSY9NN{?lI?unu?4T0p9Ah2gcSDY#>w zIP&^>K8`Dh!~R#oiB_krdG}7N#MfpmBqpWP@hqJMc*)p#3#P!Eyr!@)h$4G3jSD>SC-RKV-Zekw5aBo6}_$zuvwA zXD+t1+rQSHpO!nER6nRURniaTeJwuW4*oZ2$n!;f?lo7uv8jSJZ1&|Q)-%v6<%d8O_@rqg$&@IQx!QZc`2siB5? z^LZWpZS0m`Gn3!z&JT1s&2ppb&Y2&H=XLJvV25i}>GD)R{^n~%{^h|;boz@i-?@u5 zuU{56YVgD9{N~ji`Gut;&~)8l{Hnta{EO5MD*HQIJTsV|m+zZ5JW!1f?dijha|y>kcTVTykA7ofR_Z=8Z|Sv} z{8h(R_TRYf_8mO_iZQQrS(_KLdE|Dyuyh{3!?h8a&K{m8IiJj*JKhzG`LM?jlIY;b z7h9f2;omdyfql{Zyb>=g-IEL6F5*``o!O3O=M5oaED@jk3gZ)FvassMXx_z|-~oMx zka=q{AOG6)f3DwHxtK5E-P-Ys4+pT4cO36C`U{die!bgL{zLzYc5L7?m?*w-u`ny*d#f-Q~u6Zal^{FO^-`SOw>q`$5c-_kn~2bKquE_Jt9 z8>c{STe=b-uanGqPuKjW>Tt5~_zR|edILVSC!V-Ie@%UT&yEUET1qy5Yo+CbRfy7g zA5zhGH~o2W15PCIr1V-1sy@<-~_RTB_WYGVLmT^iR528umLY0wt$~G zY$zV3ItIVEw2)slQUSku8jk~=7x1kPGw^{S<8faa&s#MH<(=ykj@O4R;)i}8jy=rg z;H(n~JkfiBCwm~AZaRz49F&^3&S?@}o3@xgz25+*2f5;DvlsF$w`Q3oHjT$Q?-uhe z@sF_87%QxGGlah}UKjVD5rs#5P2hdHF|0my2G%hO=cWG^v~TwIK(%W|GH}k9{=bxJ|`c=A5Z(&F5_MS ze!nN0kF%&oyZaa6IU6JSp`9P*UKe;;QZzs0>Ki+2^`kgbZ61H)O1asHx}%uuIFCOw z>?WFb=QutvI+nMdKO9X931pKtE^5cxP9M+!E{55-FXYAi!7;SH6ylgQqX7#%& zD5r|>x&|Zof=(2jdgR6@hWF)Fmm{-YCov!PNS`m!KW2Bk_e5T$OHY1%)umC}G(36l zRlWE!x(00=<;S1OQsi6TT(i5l67i{j4ER|UhtCECV*ct-1AcG+Z1idi;cZtB=Nr4N zME`77=Wh}({@@R7{B&bCereDo{zyP=-u*$nc<*VReE09A==;DEY|_3Me)jx}W=(_h z*r|sx{Fz4)_{9D_EaPG%pE98mDbKq~cc>@xPgRBYNB=f$_D|sT#;r4dckm{?a&;+R z%NK0AWR{aufG^yO<~tm1vb$BGM(}2De%i(kX4X2Yq@~h_Upl!PR_m-rH0F5m z&8@3YpEytQxL42r*;g?Z9z;98H{ZIm8*+v2Md&cO$W1>@+fILKT+8 z$*YUQ3Gp<*x$E7CUS=;+@y|c#;$#hS(03y7PhFV5Dqn@Dm`^4~L3!xdioT@ggB#h^ zO95-9UBK6FL=wkVlYF_>t9a>nVNKf}Aal*F_{YQfWQOi4{Ilm-Y&bBSY(Jci-!M;H zwIG(XUVfB6<QR)I+8D`N3rU|3FPuv|GY0|DrC~S$t0(( z7HbbyCHg~+NU47xl6(6Vo?v4{bMXZiDJ&ug6Q5Kiv(K92JbII?qRB8jA5(D~oDMzCk`N#s(=1EDvY#Kv&5NPy{7 zob*YNS7H;%{b~AU&0UoFC-uIhTS5Vzm-~+Wbo3^FR(HkPUo`kHLp(_E5%NnBT zkh@Fxn%72H%;v}X(GPbP^Tr#-VKE!yUgpyblK6E?Ot6^uzC21V6wl|qN_MbkH7(S= zLjr#>HkJA8cx`qrES_I7Gn38NxJ$47vy^|SdEf5Z%oH=jsubR6Z=s$2fOs~$Z6R+u zH65v`9HFlEwtR}8A$i!>&1~do!e6)4Cx)71SV`wmyorM;$@)Rrg^L~dc^_QK!0ny* z#oMRw1=sswJFouy-3iCp$^#Z;PqY``V_Qdl^wl)1jp7S0O~4n2 zAI7)XLf+~3GJL`8xWuKK5pS^FmfyCiikmgnh#!1Im-#zb5;33KZ)-PvWCwow_8Da0 z%B@Ue_iWzz(QYi}14idj`2Z8%E7Xc~yPHRC-zD&Urmn$azPYUot=AgPzih?C=|VP3 z>JrW09ypSni`a?B`#xst*H0(A(z9^-_g8G?uBqfokG|x7^<1j=V?4R%q)pBkyW`YG zXA*N&ov*sJ*W z{4^H&DTsswf5gtyAE4u9vqmL4t!=)f0&Znb&v3wIa+*g;d`vbb$#M;Y9(9HHi{T}btMW4 zWB37q`FNSL9y!;`p8s@uP&+pNUVt5p-TBw@wb=dMa{TSj0^T!vBa)k*hL2cB^Y&~p z?h~*9=ln_F+e~z+n2)K?!C}se_>@IM?4~yt;=%dh{F3RxSTZY;OnH~g-YD*9$BRRa zaEx3MZ@l0xoehZLOrV z9?ws5+(SdU%_Q@zZnA(KSxn4a;yALS^bK3{pp=Pu$!lFQ;FS&8t+bP!w(Cn~$M+-} zla%@70By1E_RpiekS$(Em_Gn@n}I*?-p z)9l1Nahf*i{mFzJ)V{@j9B9CLzIX7#ldk-nfAk3V$B7hNTVN;Vy`wboXE_N6&l}~zi2P>4A;Q{q?_%VSJoKv(L7YsPhrtc0WJL_KKw{upr z1HLoJ$SWB*ddCCCEu2Z}`+UNh78meVk4a=m$Qzu#>KtD0H<@&t--IU^7GZzK@g!ki zZ!$K;g!EeR6d!-xlN{P=MrP_g!0Y;G6F1>A?UdzXY|zx7gfU~1GmYW#=lhe_?<`3F zD@XBK6L~WF)LUF8u_cfDcO`OjyO0Sd4Tv0miZ`w7i+ipNC22jb;R^pu{OMQ#akhJi zFJu;>`o*(}^V0{ooGD>4G@OjO(3kAa4`AQ)oQaG6KKy1=6u#a+lC(YBf|oQWVB04Q{hwamE?Um-ZzS*RB<@`=1uH=(T^xD55yy_uVJGlp`?7lP&^0~ z;M`GjiM`wfT>QEXmppYN8lOY4X3le*RU1g|KD~%9#rGfs(-Ap!T7@)^c}jPtdADQx zsSVi9!UC9kcHCA{z*LJhXwwFQrvK8PN z*>g$FQct`%>^P3Bo=aMGrsF5iQgL%;B$*LXjC*Q~#Z6zs$-qsu*eP{1zGO3<=-wYj zj(iJ4!*1IX%{NL!FW4KqE}BSKkPR8q-4>r6GJ+J{>q&l8D`0ke0(o)Aj_mO0j@^2V zAPXk-BGs>5>>iqWlHyof66`h^b$(??CZ;Qs)+d21Z-6JM-C;pguHK{fc8?-117G4R zL%Z>}95YCZxjo7?ywXleMgzM9yaG67@F+SVO%Pi8_{ww`|?RI?atB z`gU`0qGUPyeL9x7J0!4A<(>FvW4y_S-)dybhA1{~)OeEnK#`ao8_P@|dy#ze*~Bba zmDR`H!N%j}lH)=8?DfYB_|epG;#A*(eOUew8@419^D`~f;rlH+F^|klCd;dp*`A#z zBw}8@J(+~yRw@~{0Esz7Et0s{=`#7}=h^07bIJMYf$aF*LN?tsk}Mda#nx>-&%S=u zCNo|pv8#3Ve2V-Kvc`KYtB$wimmkn1B~zC&yv&6^yypaN()foRxgX3YrryIH$X&Ku z&6_`Iyb;^W?PpyJXY+Z-X0uD$ihP{qM1J$wm2BI^R`!nh@My*azG9F9KY#u^gC&2D4)dsAL874>p#D17&_v;V;CpWVb4RQ4j}GUUiftSe5T%eK6$*i14;Pl%=X>$BmSSA@ryZ|*!2Ce#A{*`zP)Dw zORMxH+k!0d(f)^-j$1U@7F~zC_-|y@eI}DNYU|m_s#4Zn$Dc@+56749XRV~CP< z6W%d2jU7()Axi6aGmnv`D1{`7m*KV+& zaWjbfrwV-ULk#mOn?{sOp5bMOW0?NQK%#$ZCb`+#m#ygZ3;&GwClkH;u#ds5cs`y* zYG3HHjqEjUd6`UX;;QL~_dZC>i@Po*rt!5@&tDFS`P#S$vNpLFv(qbPtz{u3dO|-o z$>uqGVW3RvkEF1g!S1{ytrhQmvWkUo^x>a11YpN~yVxA!(V)_xL9+&u5>hE+h-B6$ zGNn|=6s69!w^YVtC`BSOQHD&JfA2oU=d#xSyVn2S>qa*$%Q?r{*M2;sO&=S`BK&z{ zsnhhZ&SO%2FP9d7DW#9sc!K@=drU@gUq;5gsJq*8 z1t)$rkPzwJe8ToXsFrRZ5Ym;uWEBMa@70khzR~<9*&sM^>fra9f5p!jJwSkNj(YFAAMp9)#H>m@eb zI7yp+sEC6P=a3qV4x54UJ)4BJ)bM1;F#*jB!*OEhcQ z5=n$xpOYf>I~KAW{Rkr5D~y%6BYP9roT~%4@Ea$_AHI^nGBS0zo_!;@Zte*zZM6=U zIz@@Q%o3Ps=2g+LQN*O6DS^fAIWPKZAI_D}C}HT78zbMa@yWVw-iRW?_mmB4x*(kj;4&5O-mC$L-tjp8&n1#ast{4)nf89P`@^F8Ym znD4LDV%LUZPWx3nt606VxMf{0E+RaE`Pt3l`%7FApFNzw2AneF-wn^>s_Wue?RtIw z?X#g=rE&xtRFzBnMjoSiHM5v}&MN9ucb*s=_GAAXTTXA+8*%#ip{#uL6`D~q3PM-w zvN>M+{@VW5gV~XiAuQ?AAu8N?W2`x+6~XK}tEQi3D8MzvAX&A;H2D~I!f4t?Fr24X(Z|-pc zr#I93WAmB0*Gp1#ZazIT)q@2qZ=#>P%cz@_FDp)(!iqOuptd&e=%QD1*|g&Yv}jzS zShz*`PIxw#Kp+0>#5ND|WV;kL@GJ1MQ@H1t#;_6{SGqGkODxe9S zwM&RMlRMGG8^^yVA!QCMO6w@8TK?*<-KSNBIXoT-2W&2r(emG@R?K8Dt1cxLhV$92 zA)Cp|gw8}qDwJ^}80l81O@>`aVAsxdmYtM8(MmJ5L$1d0WEKYuuN5D_WI@}PPj++9?1fx z>d^Z(V<74NQ)(X z^HzG#+85q#RHPSIKc^~d<6-IcD&Al6zDT%}9Q`3+NEW}Q>^a?YKLK{x)`-6pZ4(K% z?!`dpw04MiU*t)e=C5tL$sJl29pFA4m4uOZ z-M~Hl0e?@U3j`I^kn&DW@bcO@axu#vQl{S~+kFkmMWbADWT6M-TVEn0JU5bVWnOre zTSeBExPaxLUqnIIhbRoUh3U6sV8yhxVzBf}jwoyDr^DNP~4rB7Vt;MIcbBN{c2-YjDkEln^PCWCT&mue@ ziY{182fm*?J5{~SRJczD+r!U$O6vVa|k7bno80QSTwFY55m&eA74@c+s8dIO@XDE!kY4<3wgOL>i3T zw{YcT2D3OZ0eVPYz+DnYp6PG*w>eMPO)Os zv?jusC{;4mW&ryj(;E~z4I!c}cI?Na(Qx^kHQX<7Be_lOJQN*O5_iIi!zO5Yw5opFE1nAiwqnFm~OU zSe?r!a~6lNr3*KZSrsRUR90o&7do?#a$T9!&-vt{oFCgHeT`a~cP9--Gg(;T2ikd* zH+lUrfJy$kK&|SH$->oUOxkJyyK{3p>DM@c>FX;o61|Bu4VPd~6{j(kRX50E*B0t= zViFT=VC3_bIBI`4fb|T2N3Kt{r4qFsOyN>3QFxoiE4l@-x`xlhPI4x{cat}RUDaex zzYAQC$vZ41L)E3>XV5e@ zLHZ!kpWK3HMh8Z)KXDlHXvQm&f<^ZWk zab&mS-V&2Fnc{H^Em`N=1E9x=NhI&hQ1(n_EF3tvoai_hF`3PSLBhPB_T1Ny@wFQTut2E+5FHl&_>sRSO|P{xGS1Ez3;hEupVlE$JN+OrG5L0lWJHpl-`x ze%W|SP`{E#HaN^8pZA7<-hMr}C_4i7HDr@6GHRgNb2PYDoFkWRdxGk0FD|PHPfp(I zLixDCP`$2%2zLp-Hosm?5+&eWK;bUEs|lY^jfWrOw-Vu&?N&o9T|W^yb4&0TA_cb| z^@A22b&y?hmUHmhP4p&2!pv^F$fF8%=+`hF)Gj|KFnJ6FpO%HGA9Bgd-7cV*UQcX{ zUy^QpY~V@BC30}e79z9W2Br<`3Vf$0q<+&xnC#j@cr|yTtf>d1f0}?}Y8f#dI2dkf z=z>zM5($vFM1muO!ExhBa>VEt5r3QlY+49;eyeJp%%xJY!@ zC=i8X!EoQ{BzZ8L(ojuLNKmaMvA#K!Il6(~s~j?BP6jmwf9MnSk%$v@m~T&&zqWCq zEF>pNvP>%j5bpO~Zj!UP`pj#tHVC)&4jGX1vu5wR?;(EM^2mvA9?Wl8DY@t{2Hvk9 z!1laLCkc2)ce2!;WzP>L`C?NTkvoW)6q}Iip((^JB9zVY+d{0*;`bWaiL5?tCt3DU zOk9*aS+{HXk5`VHCggz z1<-C%g_XxO__bH2fU{o`v8x%)E~gEK>;OIPX|os8_xee)L+6rmmrhLA#Tk?feQ0_5 zIF@cV7)(5_iw+JrOfw`Rz-aFkx@cnqHQ8zh&NbJ$OKS%46XwUm@yceJ%uV9w%=Cj^ z10A?dp&_LCWi&9)GP-C%CYcc91;0LTBT=g=NpX`GjEgPhr>74Dxz!WlGTtlQcK!>Y zN=A^iI*)_|nnB{WUeI~LP*B;`Ld-_?2C{b$xVLD5)#Vyu=w}bh%QlkeI7zUc=mmos zuaF)#=_EWg0M0(v6cx^SMs7Tf2Dy{woMLP!N%Zyy+3%6`-l9t+$2kmE=cm%JrNN|M zLIm7T-a{YxHF6I8Y?$npK_gZLl1-uUFm|jnx1;GIH(`G)Jl)=lv%79gq-|%yv$d~D zg6weO7VZTbIyIB9Kr3REp$-OO4u&?ZAt`hE!q+e>7&zOXG)am`#MK~J`mB^(SJol( z)x%-!kqn~Pe3iz=aj>VQ8)Q$eq(3fLK$PcCvi;6@=23T;eD&=Qlhzor#wi74!gY19 zl9g5Dp*aIRWB%~2fzV)y?omC&{w5}qy ze*H&c5?)SE7Fe)UUIGqp>`xQdg|KegrDR0CE}y6z#I`%%#b*U8NFg_YC8nr=a9esk zAnuOMR72kqT4T3!C*N?a|4xIy_J)fEWal|K_RQ4`+&*66mHSDus37;hwtI96IoR(K zjWzXwGLa?qnK@U?4~zY4_mhk#^(P#-4_S-gL69*ma=b&H4EFzPn|cM3ew*;llcX<% z)XwLh#w>bvl)3R}O^6Sw`T{qzIgf z5!7_PL4>>a770*G=?4ogYQQjjf1%$gIe+035$?0k2IBeCF7jikA$(17g1&`$;)$}o z{@SLSRG{zB*{1t@+rZ<_#&BVOI(@iB^RFE;8qZxltmw-uFSupIlPhdoarT2SI1PAlovZ@b68D^3xKg>Y=t^%B2noA~B66gufh~%CwFlqgGXbC(>((&%JwTBJ( zi5`;o>&wZ@RC_27xkREatsqG)zF=Pbg{b88B<{OC;r53d;$!lFY<=hqYGb=Y%%e!M zP0W&Kq}3y0 zpmvoaxiwsaRL__N2QRH7#in_G?Sa$+RCA6Hr$_i~N3b8HIpTA?6-42DXQ-1kfoppk zh)z*guvnrBbNxne^?jy7=XWYlb#*8?H)s;LUp!0-=B}f0M`ppdmS%DxFPYXjxI&*R zn~2r-P27jVC`hZ3g!B7!`L&}xAZKwmCC*>pKj{xcdkuwW z)9;ajElTk2hcQf1N#d&I8=rJ&W2zQfzUx=AD3LNKE zk#*gb!2QQqaPl}$9va4x!W+rtYiSrf1r0K@;u3MebLnEKYF_<77iu#n4nDmJqqj#% ziF<#KhDX7<)KI^I=AOf62l~IL^SdaTGu;^i?>(W$yOr2(uVHY0OfIc-{z4;5?f%-L zo}-w%Qz89liWZO}MYdFS4;_|j_Sfducro{&F0|y_XEHU&f=Tq>&$qlA0K#2SV#uy; zaplLH9|LQ%=d(nk?!^1@dNTI*43^zXovi<-6A1U<3J+%RMvqwEYb6CqL2P5cQu1}0 z1?joefh}5)OG<9P`D;5SS}~KBqeNjxR~Xs)n1=azfulH#u$vOhao8{@Ki3tmx#GQ> z6kU)RgrD1jZEl%E`o9T)#;`Q9RBJDB_~i$)ob&$LnTjjg6vPLTwdfuFu zdfpbGpO}PhdP5)EO29#Byi3~KjFsjNg;3Z;Cd3bC&x)Fe)Wkwk6lKpAN;i@@sZUAP zYYR4dh6)&7UrF}pOkmr~)WH2RPbQul%@RbGPRTdtWt)!a=xcM1d3hOy-7&RnYSZUGG4u!MJ6okUxf#z5nPm;6bO z5k!A)Je-+T%qxAcJ=pfRv@!#a}oNRJi!^@7;1 zsoc%gj;tjL4SrCvz1}-E{?xci4J5~U(j;kXl#FLHrlKQ zY@Ysx?%Oqrz0|uyf>&5For^8BboXG082g4!#xslbF+WM!vB~U66{UkMFdPEaT=>Q$jApi ziR_g2XC~9HkgO9~WOJ?uW1sGl?dlTb_5A=Q_4FpuO`bqp4FlLJQ#GjWWljz5Sg@|E z&EV4WZfsHbIVu@B5wce3F@v)=#|!r_%Lpi3s=}Y{nM*&FIwFmTlRCA#5xmY%?~!HoART#J&NO8_BlePEV0@vH3yLnnGcde6a%MCM zZahp5>&*q*=E-DR{%*2lNCd2SW6H;AzVgSmmL=Dh>jYp8Fq_&+BK9JX$<0PzFQHc=P}?gsE~g8;0e;pP7&e0`ZJeIo}<7n zbp;Ua=i1N7rtAr-9J zA(>j^>5hDSmeDed%y$?_w>CzjOBqA*##GVTk^pwCNtz_~$)!2{!rAv${LHi{qfMRz z|Jq5F3qbB&GHr;G<0cu3z$GV`_kTT<33uP0@lbO8C|AEGU)1xk0p5Z6hfLvzGvQ9& z?hC0)-Vxi45}eXdZ)n-qM5H71Nc5gK7?)qfefTLu%#QfL-LFrHy!8+gxhoE?p5Dz} zd(fM>Nch9TO;3rA)-!I{izU$e%4%-3T?41QI{^CKcuIyUN$}Zd_H657xCMP}O|2f! z1jimX$(#2{wBcYh46n1}E`AE53oPe?<+A;B;M?(3a#|$lT{=Kbhnx~USQ-iaE~e4F z^X_nAj`QJ3>jv8GOn0KYEDBttR?{+9S+aYECS-k`1VfE1dExdNs|e+{#=)jD<)qS7 z3EoJULJ!T;DZU}4`0HfeI>-gy~s|9SWaBRCOX|P748vZ z29jjgIc(&$6J%bTHp#yc#-bt)kx#0tiJ|vhs%qi|XD%!!1=7t_cghUV&GRI;K7XMh z32vaUzJY}M6%*|MM|fRbN9c@F5_`h|N;mH&3P$h9>~N>Q_WG*|(5Z%iesV7H$2*5{ zs#Bryo4;7Nj~jOdxwOepKmQ;RZg~xPa5R|?(b)mw|Fr|B;kk;W3j`Krkcx8rzs?zN znE%O1{9|1RclE6nXsx$`cMIxC%I+C}ScY7jTS#2ao51}WieT;bj$F9v1buvVk%-HU zB(KOHTBMxBzOk*uqS71seR3ARi@8gd4t0UN^gL?+tC6f}c7P+k$7tlC7v!ar6M*j_ zdTGI8B3JQ(bjEXSr~S)F_Wc*=cHLR0L_!yFbg^D)=g(N>2j78w4SpYK{hHzatophYQ^sIC-i4qd{G z9w^hwg;(fpS5H>kr0~$hAw@vlvObHPneWD~(|MS~Jq5l|;l^Q`mi<)%=n7fx@3NU3H0Tf4jsbK&v6SSQ%R)=KElfJu4>Su_ zlc;wSKz*M9e2O(E17nRr>d;4$bM7mNb2ovcbP@QDZlF>2QXnOLl)T*`!>nbkp!v>p z*mt^wE_S$1#`n2RWCH`(o!0|N_c2;xvCU$(VRbNxU#0~XHkRz&!x!9|N{b$OO)EtP0t4ISzVy>O-V+FLF47 zgG}e{;8W5?{FS-D=VQ{iJF}|F;V?A8ncg+ON2PZ9z_XP{=^gW_%wbh0c#(XFuCWPX zg_UbaT=+$rzi>92yXAp+!nZec=RP;)Te6Hy9led8I-(OZyXFZ;FRRl$AAD}T%mWGx zD)HZc9dQ|N4`#=|k!*_-B+SJI(pJg?_udj3PwyaA-N!+zs~#xJuOxPA%Fya;5BWnE zi03>L!H?5JVX)l}@v1#?F#DGUteKHP^LZl(tJ8(hh|SdhYcA<-T~8*RbYa?G-jO_= zd{TScj@?z5N^5OjQ73%uTrnL@8}EOjeJefKd8nq=QJ%~sb34uI+e-BlT$pFxQrdU& zSK4K|H`CiFTU;mqgLX-BV_t!_)ac!Je%0DuO!wt9<`Jkz5(5^o7C9SQc<2W;n>mw> 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 Date: Mon, 9 Jan 2023 17:05:10 +0800 Subject: [PATCH 094/164] Added animated raccoon material and deleted unused skinned vertex shader --- Assets/Materials/AnimatedRaccoon.shmat | 8 +++ Assets/Materials/AnimatedRaccoon.shmat.shmeta | 3 + Assets/Shaders/DefaultSkinned_VS.glsl | 65 ------------------- 3 files changed, 11 insertions(+), 65 deletions(-) create mode 100644 Assets/Materials/AnimatedRaccoon.shmat create mode 100644 Assets/Materials/AnimatedRaccoon.shmat.shmeta delete mode 100644 Assets/Shaders/DefaultSkinned_VS.glsl diff --git a/Assets/Materials/AnimatedRaccoon.shmat b/Assets/Materials/AnimatedRaccoon.shmat new file mode 100644 index 00000000..ae7c7163 --- /dev/null +++ b/Assets/Materials/AnimatedRaccoon.shmat @@ -0,0 +1,8 @@ +- VertexShader: 47911992 + FragmentShader: 46377769 + SubPass: G-Buffer Write + Properties: + data.color: {x: 1, y: 1, z: 1, w: 1} + data.textureIndex: 0 + data.alpha: 0 + data.beta: {x: 1, y: 1, z: 1} \ No newline at end of file diff --git a/Assets/Materials/AnimatedRaccoon.shmat.shmeta b/Assets/Materials/AnimatedRaccoon.shmat.shmeta new file mode 100644 index 00000000..39700e98 --- /dev/null +++ b/Assets/Materials/AnimatedRaccoon.shmat.shmeta @@ -0,0 +1,3 @@ +Name: AnimatedRaccoon +ID: 128805346 +Type: 7 diff --git a/Assets/Shaders/DefaultSkinned_VS.glsl b/Assets/Shaders/DefaultSkinned_VS.glsl deleted file mode 100644 index 22199ff5..00000000 --- a/Assets/Shaders/DefaultSkinned_VS.glsl +++ /dev/null @@ -1,65 +0,0 @@ -#version 450 -#extension GL_KHR_vulkan_glsl : enable - -//#include "ShaderDescriptorDefinitions.glsl" - - -layout(location = 0) in vec3 aVertexPos; -layout(location = 1) in vec2 aUV; -layout(location = 2) in vec3 aNormal; -layout(location = 3) in vec3 aTangent; -layout(location = 4) in mat4 worldTransform; -layout(location = 5) in ivec4 aBoneIndices; -layout(location = 6) in vec4 aBoneWeights; -layout(location = 8) in uvec2 integerData; - - -layout(location = 0) out struct -{ - vec4 vertPos; // location 0 - vec2 uv; // location = 1 - vec4 normal; // location = 2 - -} Out; - -// material stuff -layout(location = 3) out struct -{ - int materialIndex; - uint eid; - uint lightLayerIndex; - -} Out2; - -layout(set = 2, binding = 0) uniform CameraData -{ - vec4 position; - mat4 vpMat; - mat4 viewMat; - mat4 projMat; -} cameraData; - -void main() -{ - Out2.materialIndex = gl_InstanceIndex; - Out2.eid = integerData[0]; - Out2.lightLayerIndex = integerData[1]; - - // for transforming gBuffer position and normal data - mat4 modelViewMat = cameraData.viewMat * worldTransform; - - // gBuffer position will be in view space - Out.vertPos = modelViewMat * vec4(aVertexPos, 1.0f); - - // uvs for texturing in fragment shader - Out.uv = aUV; - - mat3 transposeInv = mat3 (transpose(inverse(modelViewMat))); - - // normals are also in view space - Out.normal.rgb = transposeInv * aNormal.rgb; - Out.normal.rgb = normalize (Out.normal.rgb); - - // clip space for rendering - gl_Position = cameraData.vpMat * worldTransform * vec4 (aVertexPos, 1.0f); -} \ No newline at end of file From a58c3e86a5ec4aa18f1657b4c73660c5faa9bf93 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Mon, 9 Jan 2023 17:10:13 +0800 Subject: [PATCH 095/164] Reinserted genMeta in compiling function --- SHADE_Engine/src/Assets/SHAssetManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index df982d28..44d62f66 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -437,7 +437,7 @@ namespace SHADE return; } - if (true) + if (genMeta) { GenerateNewMeta(newPath); } From 7bf0c2605217da318b8ce0707958559c761529f3 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 9 Jan 2023 17:51:54 +0800 Subject: [PATCH 096/164] Added support for loading SHRig and SHAnimationClip into SHResourceManager and modified SHAnimationClip to support proper keyframe data --- .../src/Animation/SHAnimationClip.cpp | 29 ++++++++++++++++++- SHADE_Engine/src/Animation/SHAnimationClip.h | 20 ++++++++----- SHADE_Engine/src/Resource/SHResourceManager.h | 6 +++- .../src/Resource/SHResourceManager.hpp | 10 ++++++- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp index 4ef91a39..a6841e0a 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -20,11 +20,38 @@ namespace SHADE /* Constructors */ /*-----------------------------------------------------------------------------------*/ SHAnimationClip::SHAnimationClip(const SHAnimAsset& asset) + : ticksPerSecond { asset.ticksPerSecond } + , totalTime { asset.duration } { + // Populate keyframes + for (const auto& channel : asset.nodeChannels) + { + // Create a channel + Channel newChannel; + newChannel.Name = channel.name; + newChannel.PositionKeyFrames.reserve(channel.positionKeys.size()); + newChannel.RotationKeyFrames.reserve(channel.rotationKeys.size()); + newChannel.ScaleKeyFrames.reserve(channel.scaleKeys.size()); + // Populate Keyframes + for (const auto& posKey : channel.positionKeys) + { + newChannel.PositionKeyFrames.emplace_back(SHAnimationKeyFrame{ static_cast(posKey.time), posKey.value}); + } + for (const auto& rotKey : channel.rotationKeys) + { + newChannel.RotationKeyFrames.emplace_back(SHAnimationKeyFrame{ static_cast(rotKey.time), rotKey.value}); + } + for (const auto& scaleKey : channel.scaleKeys) + { + newChannel.ScaleKeyFrames.emplace_back(SHAnimationKeyFrame{ static_cast(scaleKey.time), scaleKey.value}); + } + + // Insert the channel + channels.emplace_back(std::move(newChannel)); + } } - /*-----------------------------------------------------------------------------------*/ /* Usage Functions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h index d8415942..9e34256b 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.h +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -23,15 +23,13 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ ///

- /// Defines a single key frame in an animation. + /// Defines a single key frame in an animation for a specific type of data. /// + template struct SHAnimationKeyFrame { - float TimeStamp; - SHVec3 Position; - SHQuaternion Orientation; - SHVec3 Scale; - SHMatrix TransformationMatrix; + int FrameIndex; + T Data; }; /// @@ -50,18 +48,25 @@ namespace SHADE struct Channel { std::string Name; - std::vector KeyFrames; + std::vector> PositionKeyFrames; + std::vector> RotationKeyFrames; + std::vector> ScaleKeyFrames; }; /*---------------------------------------------------------------------------------*/ /* Constructors */ /*---------------------------------------------------------------------------------*/ + /// + /// Constructs an SHAnimation Clip from a specified SHAnimAsset. + /// + /// Animation asset to load. explicit SHAnimationClip(const SHAnimAsset& asset); /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ const std::vector& GetChannels() const noexcept { return channels; } + int GetTicksPerSecond() const noexcept { return ticksPerSecond; } float GetTotalTime() const noexcept { return totalTime; } private: @@ -69,6 +74,7 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ std::vector channels; + int ticksPerSecond; float totalTime; /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index efdd15be..086b8570 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -27,6 +27,8 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Assets/Asset Types/SHMaterialAsset.h" #include "Graphics/MiddleEnd/TextRendering/SHFont.h" +#include "Animation/SHAnimationClip.h" +#include "Animation/SHRig.h" namespace SHADE { @@ -43,12 +45,14 @@ namespace SHADE /// template struct SHResourceLoader { using AssetType = void; }; - template<> struct SHResourceLoader { using AssetType = SHMeshAsset; }; + template<> struct SHResourceLoader { using AssetType = SHMeshAsset; }; template<> struct SHResourceLoader { using AssetType = SHTextureAsset; }; template<> struct SHResourceLoader { using AssetType = SHShaderAsset; }; template<> struct SHResourceLoader { using AssetType = SHMaterialAsset; }; template<> struct SHResourceLoader { using AssetType = SHMaterialSpec; }; template<> struct SHResourceLoader { using AssetType = SHFontAsset; }; + template<> struct SHResourceLoader { using AssetType = SHAnimAsset; }; + template<> struct SHResourceLoader { using AssetType = SHRigAsset; }; /// /// Static class responsible for loading and caching runtime resources from their diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 51ee356a..44c38c50 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -40,7 +40,9 @@ namespace SHADE !std::is_same_v && !std::is_same_v && !std::is_same_v && - !std::is_same_v + !std::is_same_v && + !std::is_same_v && + !std::is_same_v ) { static_assert(true, "Unsupported Resource Type specified for SHResourceManager."); @@ -345,5 +347,11 @@ namespace SHADE return gfxSystem->AddFont(assetData); } + // Animation Clip and Rig + else + { + loadedAssetData.emplace_back(assetId); + return resourceHub.Create(assetData); + } } } From e9624977cd4d7ef2bf2e4ac452e7e5cb388a43ea Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 9 Jan 2023 23:32:20 +0800 Subject: [PATCH 097/164] Modified SHAnimationComponent to handle interpolation of separate position, rotation and scale keyframes --- .../src/Animation/SHAnimationClip.cpp | 6 +- SHADE_Engine/src/Animation/SHAnimationClip.h | 1 + .../src/Animation/SHAnimatorComponent.cpp | 83 ++++++++++++++----- .../src/Animation/SHAnimatorComponent.h | 7 ++ 4 files changed, 73 insertions(+), 24 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp index a6841e0a..666c548d 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -20,8 +20,8 @@ namespace SHADE /* Constructors */ /*-----------------------------------------------------------------------------------*/ SHAnimationClip::SHAnimationClip(const SHAnimAsset& asset) - : ticksPerSecond { asset.ticksPerSecond } - , totalTime { asset.duration } + : ticksPerSecond { static_cast(asset.ticksPerSecond) } + , totalTime { static_cast(asset.duration) } { // Populate keyframes for (const auto& channel : asset.nodeChannels) @@ -47,6 +47,8 @@ namespace SHADE newChannel.ScaleKeyFrames.emplace_back(SHAnimationKeyFrame{ static_cast(scaleKey.time), scaleKey.value}); } + newChannel.MaxFrames = std::max({ newChannel.PositionKeyFrames.size(), newChannel.RotationKeyFrames.size(), newChannel.ScaleKeyFrames.size() }); + // Insert the channel channels.emplace_back(std::move(newChannel)); } diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h index 9e34256b..fa8466b8 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.h +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -51,6 +51,7 @@ namespace SHADE std::vector> PositionKeyFrames; std::vector> RotationKeyFrames; std::vector> ScaleKeyFrames; + int MaxFrames; }; /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 629453a6..4b2ca166 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -83,6 +83,7 @@ namespace SHADE return; currClip = newClip; + secsPerTick = 1.0f / currClip->GetTicksPerSecond(); updatePoseWithClip(0.0f); } @@ -135,36 +136,74 @@ namespace SHADE bones.push(child); } - // Get interpolated frame - auto firstKeyFrame = channel.KeyFrames.end(); - auto nextKeyFrame = channel.KeyFrames.end(); - for (auto iter = channel.KeyFrames.begin(); iter != channel.KeyFrames.end(); ++iter) - { - const auto& KEYFRAME = *iter; - - if(KEYFRAME.TimeStamp <= poseTime) - { - firstKeyFrame = iter; - } - else if (KEYFRAME.TimeStamp > poseTime) - { - nextKeyFrame = iter; - break; - } - } - // Calculate the matrix + // Get closest frame index + const int CLOSEST_FRAME_IDX = static_cast(std::floorf(poseTime * currClip->GetTicksPerSecond())); + + // Calculate the matrix from interpolated values const int BONE_MTX_IDX = rig->GetNodeIndex(bone); - const float T = (poseTime - firstKeyFrame->TimeStamp) / (nextKeyFrame->TimeStamp - firstKeyFrame->TimeStamp); boneMatrices[BONE_MTX_IDX] = boneMatrices[BONE_MTX_IDX] * SHMatrix::Transform ( - SHVec3::Lerp(firstKeyFrame->Position, nextKeyFrame->Position, T), - SHQuaternion::Slerp(firstKeyFrame->Orientation, nextKeyFrame->Orientation, T), - SHVec3::Lerp(firstKeyFrame->Scale, nextKeyFrame->Scale, T) + getInterpolatedValue(channel.PositionKeyFrames, CLOSEST_FRAME_IDX, poseTime), + getInterpolatedValue(channel.RotationKeyFrames, CLOSEST_FRAME_IDX, poseTime), + getInterpolatedValue(channel.ScaleKeyFrames, CLOSEST_FRAME_IDX, poseTime) ); } } } + SHVec3 SHAnimatorComponent::getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime) + { + // Find the key frames that surround the current frame index + auto firstKeyFrame = keyframes.end(); + auto nextKeyFrame = keyframes.end(); + for (auto iter = keyframes.begin(); iter != keyframes.end(); ++iter) + { + const auto& KEYFRAME = *iter; + + if (KEYFRAME.FrameIndex <= closestFrameIndex) + { + firstKeyFrame = iter; + } + else if (KEYFRAME.FrameIndex > closestFrameIndex) + { + nextKeyFrame = iter; + break; + } + } + + // Get interpolated vector + const float PREV_FRAME_TIME = firstKeyFrame->FrameIndex * secsPerTick; + const float NEXT_FRAME_TIME = nextKeyFrame->FrameIndex * secsPerTick; + const float T = (poseTime - PREV_FRAME_TIME) / (NEXT_FRAME_TIME - PREV_FRAME_TIME); + return SHVec3::Lerp(firstKeyFrame->Data, nextKeyFrame->Data, T); + } + + SHQuaternion SHAnimatorComponent::getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime) + { + // Find the key frames that surround the current frame index + auto firstKeyFrame = keyframes.end(); + auto nextKeyFrame = keyframes.end(); + for (auto iter = keyframes.begin(); iter != keyframes.end(); ++iter) + { + const auto& KEYFRAME = *iter; + + if (KEYFRAME.FrameIndex <= closestFrameIndex) + { + firstKeyFrame = iter; + } + else if (KEYFRAME.FrameIndex > closestFrameIndex) + { + nextKeyFrame = iter; + break; + } + } + + // Get interpolated vector + const float PREV_FRAME_TIME = firstKeyFrame->FrameIndex * secsPerTick; + const float NEXT_FRAME_TIME = nextKeyFrame->FrameIndex * secsPerTick; + const float T = (poseTime - PREV_FRAME_TIME) / (NEXT_FRAME_TIME - PREV_FRAME_TIME); + return SHQuaternion::Slerp(firstKeyFrame->Data, nextKeyFrame->Data, T); + } } /*-------------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 056aa536..91edc9fc 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -19,6 +19,9 @@ of DigiPen Institute of Technology is prohibited. #include "ECS_Base/Components/SHComponent.h" #include "Resource/SHHandle.h" #include "Math/SHMatrix.h" +#include "Math/Vector/SHVec3.h" +#include "Math/SHQuaternion.h" +#include "SHAnimationClip.h" namespace SHADE { @@ -119,6 +122,8 @@ namespace SHADE // Playback Tracking float currPlaybackTime = 0.0f; bool isPlaying = false; + // Useful Cached Data + float secsPerTick = 0.0f; // Buffer std::vector boneMatrices; @@ -126,6 +131,8 @@ namespace SHADE /* Helper Functions */ /*---------------------------------------------------------------------------------*/ void updatePoseWithClip(float poseTime); + SHVec3 getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); + SHQuaternion getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); /*---------------------------------------------------------------------------------*/ /* RTTR */ From 5c14a0829af6fecdf2f206c9ccf6adaba4cce75c Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 10 Jan 2023 11:46:37 +0800 Subject: [PATCH 098/164] Added support for edge cases in transforms interpolation for animation clips --- .../src/Animation/SHAnimatorComponent.cpp | 59 +------------ .../src/Animation/SHAnimatorComponent.h | 11 ++- .../src/Animation/SHAnimatorComponent.hpp | 82 +++++++++++++++++++ 3 files changed, 92 insertions(+), 60 deletions(-) create mode 100644 SHADE_Engine/src/Animation/SHAnimatorComponent.hpp diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 4b2ca166..493833d0 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -3,7 +3,8 @@ \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Nov 20, 2022 -\brief Contains the definition of functions of the SHRenderable Component class. +\brief Contains the definition of functions of the SHAnimatorComponent Component + class. Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent @@ -145,65 +146,11 @@ namespace SHADE ( getInterpolatedValue(channel.PositionKeyFrames, CLOSEST_FRAME_IDX, poseTime), getInterpolatedValue(channel.RotationKeyFrames, CLOSEST_FRAME_IDX, poseTime), - getInterpolatedValue(channel.ScaleKeyFrames, CLOSEST_FRAME_IDX, poseTime) + getInterpolatedValue(channel.ScaleKeyFrames , CLOSEST_FRAME_IDX, poseTime) ); } } } - - SHVec3 SHAnimatorComponent::getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime) - { - // Find the key frames that surround the current frame index - auto firstKeyFrame = keyframes.end(); - auto nextKeyFrame = keyframes.end(); - for (auto iter = keyframes.begin(); iter != keyframes.end(); ++iter) - { - const auto& KEYFRAME = *iter; - - if (KEYFRAME.FrameIndex <= closestFrameIndex) - { - firstKeyFrame = iter; - } - else if (KEYFRAME.FrameIndex > closestFrameIndex) - { - nextKeyFrame = iter; - break; - } - } - - // Get interpolated vector - const float PREV_FRAME_TIME = firstKeyFrame->FrameIndex * secsPerTick; - const float NEXT_FRAME_TIME = nextKeyFrame->FrameIndex * secsPerTick; - const float T = (poseTime - PREV_FRAME_TIME) / (NEXT_FRAME_TIME - PREV_FRAME_TIME); - return SHVec3::Lerp(firstKeyFrame->Data, nextKeyFrame->Data, T); - } - - SHQuaternion SHAnimatorComponent::getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime) - { - // Find the key frames that surround the current frame index - auto firstKeyFrame = keyframes.end(); - auto nextKeyFrame = keyframes.end(); - for (auto iter = keyframes.begin(); iter != keyframes.end(); ++iter) - { - const auto& KEYFRAME = *iter; - - if (KEYFRAME.FrameIndex <= closestFrameIndex) - { - firstKeyFrame = iter; - } - else if (KEYFRAME.FrameIndex > closestFrameIndex) - { - nextKeyFrame = iter; - break; - } - } - - // Get interpolated vector - const float PREV_FRAME_TIME = firstKeyFrame->FrameIndex * secsPerTick; - const float NEXT_FRAME_TIME = nextKeyFrame->FrameIndex * secsPerTick; - const float T = (poseTime - PREV_FRAME_TIME) / (NEXT_FRAME_TIME - PREV_FRAME_TIME); - return SHQuaternion::Slerp(firstKeyFrame->Data, nextKeyFrame->Data, T); - } } /*-------------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 91edc9fc..d6adfa49 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -3,7 +3,8 @@ \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Nov 20, 2022 -\brief Contains the definition of the SHAnimationSystem class and related types. +\brief Contains the definition of the SHAnimatorComponent class and related + types. Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent @@ -131,12 +132,14 @@ namespace SHADE /* Helper Functions */ /*---------------------------------------------------------------------------------*/ void updatePoseWithClip(float poseTime); - SHVec3 getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); - SHQuaternion getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); + template + T getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); /*---------------------------------------------------------------------------------*/ /* RTTR */ /*---------------------------------------------------------------------------------*/ RTTR_ENABLE() }; -} \ No newline at end of file +} + +#include "SHAnimatorComponent.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp new file mode 100644 index 00000000..9c70d15e --- /dev/null +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp @@ -0,0 +1,82 @@ +/************************************************************************************//*! +\file SHAnimatorComponent.hpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Jan 10, 2023 +\brief Contains the definition of function templates of the SHAnimatorComponent + Component class. + +Copyright (C) 2023 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Primary Include +#include "SHAnimatorComponent.h" +// Project Includes +#include "SHRig.h" +#include "Math/SHMatrix.h" +#include "SHAnimationClip.h" +#include "Graphics/SHVkUtil.h" +#include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" +#include "ECS_Base/Managers/SHSystemManager.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------------*/ + template + T SHAnimatorComponent::getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime) + { + // Only allow SHVec3 and SHQuaternion + static_assert(std::is_same_v || std::is_same_v, "Only interpolation for SHVec3 and SHQuaternion is allowed."); + + // Find the key frames that surround the current frame index + auto firstKeyFrame = keyframes.end(); + auto nextKeyFrame = keyframes.end(); + for (auto iter = keyframes.begin(); iter != keyframes.end(); ++iter) + { + const auto& KEYFRAME = *iter; + + if (KEYFRAME.FrameIndex <= closestFrameIndex) + { + firstKeyFrame = iter; + } + else if (KEYFRAME.FrameIndex > closestFrameIndex) + { + nextKeyFrame = iter; + break; + } + } + + // Edge Cases + if (firstKeyFrame == keyframes.end()) + { + // No keyframes at all, means no changes + if (nextKeyFrame == keyframes.end()) + return T(); + // At the back, so no keyframes will follow + else + return firstKeyFrame->Data; + } + // At the front, so no prior key frames + else if (nextKeyFrame != keyframes.end()) + { + return nextKeyFrame->Data; + } + + // Get interpolated vector + const float PREV_FRAME_TIME = firstKeyFrame->FrameIndex * secsPerTick; + const float NEXT_FRAME_TIME = nextKeyFrame->FrameIndex * secsPerTick; + const float NORMALISED_TIME = (poseTime - PREV_FRAME_TIME) / (NEXT_FRAME_TIME - PREV_FRAME_TIME); + + if constexpr (std::is_same_v) + { + return SHQuaternion::Slerp(firstKeyFrame->Data, nextKeyFrame->Data, NORMALISED_TIME); + } + else if constexpr (std::is_same_v) + { + return SHVec3::Lerp(firstKeyFrame->Data, nextKeyFrame->Data, NORMALISED_TIME); + } + } +} From c0de2d4705f3da1523e5494a6982bc9caf87319c Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 10 Jan 2023 19:42:43 +0800 Subject: [PATCH 099/164] Fixed SHAnimationClip not copying name of channels correctly and added extra check for SHRig if an invalid rig is being constructed --- SHADE_Engine/src/Animation/SHAnimationClip.cpp | 2 +- SHADE_Engine/src/Animation/SHAnimationClip.h | 5 ++--- SHADE_Engine/src/Animation/SHRig.cpp | 3 +++ SHADE_Engine/src/Animation/SHRig.h | 3 ++- SHADE_Engine/src/Resource/SHResourceManager.h | 4 ++-- SHADE_Engine/src/Resource/SHResourceManager.hpp | 9 +++++++-- 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp index 666c548d..4dab967c 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -28,7 +28,7 @@ namespace SHADE { // Create a channel Channel newChannel; - newChannel.Name = channel.name; + newChannel.Name = std::string(channel.name); newChannel.PositionKeyFrames.reserve(channel.positionKeys.size()); newChannel.RotationKeyFrames.reserve(channel.rotationKeys.size()); newChannel.ScaleKeyFrames.reserve(channel.scaleKeys.size()); diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h index fa8466b8..8a10ce3a 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.h +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -11,9 +11,8 @@ of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #pragma once -// STL Includes - // Project Includes +#include "SH_API.h" #include "Math/SHMatrix.h" #include "Assets/Asset Types/Models/SHAnimationAsset.h" @@ -36,7 +35,7 @@ namespace SHADE /// Represents a animation clip of a 3D animation that is made for a specific model /// rig. /// - class SHAnimationClip + class SH_API SHAnimationClip { public: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 0d777c27..81c3e3de 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -27,7 +27,10 @@ namespace SHADE { // Don't bother if empty if (asset.root == nullptr) + { + SHLOG_ERROR("[SHRig] Attempted to load an invalid rig with no root."); return; + } // Do a recursive depth first traversal to populate the rig nodeCount = 0; diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index 66bb37ad..0dc9c829 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -17,6 +17,7 @@ of DigiPen Institute of Technology is prohibited. #include // Project Includes +#include "SH_API.h" #include "Math/SHMatrix.h" #include "Resource/SHHandle.h" #include "Resource/SHResourceLibrary.h" @@ -32,7 +33,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - class SHRig + class SH_API SHRig { public: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index 086b8570..3b9089a2 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -51,8 +51,8 @@ namespace SHADE template<> struct SHResourceLoader { using AssetType = SHMaterialAsset; }; template<> struct SHResourceLoader { using AssetType = SHMaterialSpec; }; template<> struct SHResourceLoader { using AssetType = SHFontAsset; }; - template<> struct SHResourceLoader { using AssetType = SHAnimAsset; }; - template<> struct SHResourceLoader { using AssetType = SHRigAsset; }; + template<> struct SHResourceLoader { using AssetType = SHModelAsset; }; + template<> struct SHResourceLoader { using AssetType = SHModelAsset; }; /// /// Static class responsible for loading and caching runtime resources from their diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 44c38c50..5aff2529 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -348,10 +348,15 @@ namespace SHADE return gfxSystem->AddFont(assetData); } // Animation Clip and Rig - else + else if constexpr (std::is_same_v) { loadedAssetData.emplace_back(assetId); - return resourceHub.Create(assetData); + return resourceHub.Create(assetData.rig); + } + else if constexpr (std::is_same_v) + { + loadedAssetData.emplace_back(assetId); + return resourceHub.Create(*assetData.anims[0]); } } } From 190665bbbfad0c17f5f8c373728732d977418d07 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Tue, 10 Jan 2023 20:52:34 +0800 Subject: [PATCH 100/164] Changed function signature to take in rig node pointer reference instead of r value ptr --- SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp | 4 +++- SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index bfb97fda..698e8d5d 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -153,7 +153,7 @@ namespace SHADE } } - void SHModelLoader::ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode* root) + void SHModelLoader::ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode*& root) { // Read All nodes into one contiguous data block struct NodeTemp @@ -193,6 +193,8 @@ namespace SHADE nodeQueue.emplace(depthPtr++, depthTempPtr++); } } + + delete[] dst; } void SHModelLoader::ReadMeshData(FileReference file, std::vector const& headers, diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h index 05e58b6c..93db8534 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h @@ -24,7 +24,7 @@ namespace SHADE void ReadRigHeader(FileReference file, SHRigDataHeader& header); void ReadRigData(FileReference file, SHRigDataHeader const& header, std::vector& data); - void ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode* root); + void ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode*& root); void ReadMeshData(FileReference file, std::vector const& headers, std::vector& meshes); void ReadAnimData(FileReference file, std::vector const& headers, std::vector& anims); From 3d731859268d00eda688242626011f6b31a98f26 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 11 Jan 2023 15:15:35 +0800 Subject: [PATCH 101/164] Fixed vector issues when constructing the SHRig --- SHADE_Engine/src/Animation/SHRig.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 81c3e3de..0a70dcd3 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -103,7 +103,8 @@ namespace SHADE continue; // Recursively create children - newNode->Children.emplace_back(recurseCreateNode(asset, child)); + auto childNode = recurseCreateNode(asset, child); // Not sure why this works but it is required for + newNode->Children.emplace_back(childNode); // the emplace_back operation to not crash } return newNode; From c0e8c032b9c2499c4f426da2eb718f302df71187 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 11 Jan 2023 16:17:32 +0800 Subject: [PATCH 102/164] Added inspector for animator component --- .../src/Application/SBApplication.cpp | 5 ++ .../src/Animation/SHAnimatorComponent.h | 6 ++ .../Inspector/SHEditorComponentView.hpp | 57 +++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index fcceacab..80bb28ff 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -47,6 +47,7 @@ #include "Tools/Logger/SHLogger.h" #include "Tools/SHDebugDraw.h" +#include "Resource/SHResourceManager.h" using namespace SHADE; @@ -170,6 +171,10 @@ namespace Sandbox // Link up SHDebugDraw SHDebugDraw::Init(SHSystemManager::GetSystem()); + + auto clip = SHResourceManager::LoadOrGet(77816045); + auto rig = SHResourceManager::LoadOrGet(77816045); + int i = 0; } void SBApplication::Update(void) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index d6adfa49..6c525be2 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -93,6 +93,12 @@ namespace SHADE /// Reference to a vector of the bone matrices. const std::vector& GetBoneMatrices() const noexcept { return boneMatrices; } /// + /// Retrieve the currently set model rig. + /// + /// Handle to the currently set rig. + Handle GetRig() const noexcept { return rig; } + /// + /// /// Retrieve the currently set animation clip. /// /// Handle to the currently set animation clip. diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 45964930..782f5636 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -22,6 +22,7 @@ #include "Serialization/SHSerializationHelper.hpp" #include "Tools/Utilities/SHClipboardUtilities.h" #include "SHInspectorCommands.h" +#include "Animation/SHAnimatorComponent.h" namespace SHADE { template @@ -574,4 +575,60 @@ namespace SHADE } ImGui::PopID(); } + + template<> + static void DrawComponent(SHAnimatorComponent* component) + { + if (!component) + return; + ImGui::PushID(SHFamilyID::GetID()); + const auto componentType = rttr::type::get(*component); + SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); + ImGui::SameLine(); + if (ImGui::CollapsingHeader(componentType.get_name().data())) + { + DrawContextMenu(component); + Handle const& rig = component->GetRig(); + const auto RIG_NAME = rig ? SHResourceManager::GetAssetName(rig).value_or("") : ""; + SHEditorWidgets::DragDropReadOnlyField("Rig", RIG_NAME, [component]() + { + Handle const& rig = component->GetRig(); + return SHResourceManager::GetAssetID(rig).value_or(0); + }, + [component](AssetID const& id) + { + if (SHAssetManager::GetType(id) != AssetType::MESH) + { + SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!") + return; + } + component->SetRig(SHResourceManager::LoadOrGet(id)); + SHResourceManager::FinaliseChanges(); + }, SHDragDrop::DRAG_RESOURCE); + + Handle const& clip = component->GetCurrentClip(); + const auto CLIP_NAME = rig ? SHResourceManager::GetAssetName(clip).value_or("") : ""; + SHEditorWidgets::DragDropReadOnlyField("Material", CLIP_NAME, + [component]() + { + Handle const& clip = component->GetCurrentClip(); + return SHResourceManager::GetAssetID(clip).value_or(0); + }, + [component](AssetID const& id) + { + if (SHAssetManager::GetType(id) != AssetType::MESH) + { + SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!") + return; + } + component->SetClip(SHResourceManager::LoadOrGet(id)); + }, SHDragDrop::DRAG_RESOURCE); + } + else + { + DrawContextMenu(component); + } + ImGui::PopID(); + } + } From 406759f8562e2e4e0b9022f639b0420581fa2791 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 11 Jan 2023 23:02:09 +0800 Subject: [PATCH 103/164] Added animator inspector to the editor --- .../Inspector/SHEditorInspector.cpp | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index 83647da7..57c8f12a 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -27,6 +27,13 @@ namespace SHADE { + template + void EnsureComponent(EntityID eid) + { + if(SHComponentManager::GetComponent_s(eid) == nullptr) + SHComponentManager::AddComponent(eid); + } + template, bool> = true> bool DrawAddComponentButton(EntityID const& eid) { @@ -48,9 +55,13 @@ namespace SHADE return selected; } - template , bool> = true, std::enable_if_t, bool> = true> + template bool DrawAddComponentWithEnforcedComponentButton(EntityID const& eid) { + // Only make sure components are passed here + static_assert(std::is_base_of_v, ""); + //(static_assert(std::is_base_of_v, ""), ...); + bool selected = false; if (!SHComponentManager::HasComponent(eid)) { @@ -58,9 +69,8 @@ namespace SHADE if(selected = ImGui::Selectable(std::format("Add {}", componentName).data()); selected) { - if(SHComponentManager::GetComponent_s(eid) == nullptr) - SHComponentManager::AddComponent(eid); - + // Ensure that all required components are present + (EnsureComponent(eid), ...); SHComponentManager::AddComponent(eid); } if(ImGui::IsItemHovered()) @@ -69,9 +79,8 @@ namespace SHADE ImGui::Text("Adds", componentName); ImGui::SameLine(); ImGui::TextColored(ImGuiColors::green, "%s", componentName); ImGui::SameLine(); ImGui::Text("to this entity", componentName); - ImGui::Text("Adds"); ImGui::SameLine(); - ImGui::TextColored(ImGuiColors::red, "%s", rttr::type::get().get_name().data()); ImGui::SameLine(); - ImGui::Text("if the entity does not already have it"); + ImGui::Text("Adds the following components if the entity does not already have it: "); + (ImGui::TextColored(ImGuiColors::red, "%s", rttr::type::get().get_name().data()), ...); ImGui::EndTooltip(); } } @@ -118,6 +127,10 @@ namespace SHADE { DrawComponent(renderableComponent); } + if (auto animatorComponent = SHComponentManager::GetComponent_s(eid)) + { + DrawComponent(animatorComponent); + } if(auto colliderComponent = SHComponentManager::GetComponent_s(eid)) { DrawComponent(colliderComponent); @@ -174,6 +187,7 @@ namespace SHADE DrawAddComponentWithEnforcedComponentButton(eid); DrawAddComponentWithEnforcedComponentButton(eid); DrawAddComponentWithEnforcedComponentButton(eid); + DrawAddComponentWithEnforcedComponentButton(eid); ImGui::EndMenu(); From d1ab5951268b16a9a19968876df6e2d61359da8e Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Thu, 12 Jan 2023 20:04:49 +0800 Subject: [PATCH 104/164] Fixed certain crashes when assigning rig and clip --- .../src/Animation/SHAnimatorComponent.cpp | 8 +++-- .../Inspector/SHEditorComponentView.hpp | 6 ++-- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 31 ++++++++++--------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 493833d0..50ce1975 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -85,7 +85,11 @@ namespace SHADE currClip = newClip; secsPerTick = 1.0f / currClip->GetTicksPerSecond(); - updatePoseWithClip(0.0f); + + if (!rig) + { + updatePoseWithClip(0.0f); + } } /*-----------------------------------------------------------------------------------*/ @@ -94,7 +98,7 @@ namespace SHADE void SHAnimatorComponent::Update(float dt) { // Nothing to animate - if (!currClip || !isPlaying) + if (!currClip || !isPlaying || !rig) return; // Update time on the playback diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 782f5636..d3329585 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -597,7 +597,7 @@ namespace SHADE }, [component](AssetID const& id) { - if (SHAssetManager::GetType(id) != AssetType::MESH) + if (SHAssetManager::GetType(id) != AssetType::MODEL) { SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!") return; @@ -608,7 +608,7 @@ namespace SHADE Handle const& clip = component->GetCurrentClip(); const auto CLIP_NAME = rig ? SHResourceManager::GetAssetName(clip).value_or("") : ""; - SHEditorWidgets::DragDropReadOnlyField("Material", CLIP_NAME, + SHEditorWidgets::DragDropReadOnlyField("Clip", CLIP_NAME, [component]() { Handle const& clip = component->GetCurrentClip(); @@ -616,7 +616,7 @@ namespace SHADE }, [component](AssetID const& id) { - if (SHAssetManager::GetType(id) != AssetType::MESH) + if (SHAssetManager::GetType(id) != AssetType::MODEL) { SHLOG_WARNING("Attempted to assign non mesh asset to Renderable Mesh property!") return; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 50fce74e..33a51cbb 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -648,15 +648,19 @@ namespace SHADE using PreDefDescLayoutType = SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes; static constexpr uint32_t MATERIAL_DESC_SET_INDEX = 0; - // Flags - bool descSetUpdateRequired = false; - /* Create Descriptor Sets if Needed */ + std::vector varDescCounts; PreDefDescLayoutType layoutTypes = {}; if (matPropsData) + { layoutTypes |= PreDefDescLayoutType::MATERIALS; + varDescCounts.push_back(0); + } if (!boneMatrixData.empty()) + { layoutTypes |= PreDefDescLayoutType::BONES; + varDescCounts.push_back(0); + } if (matPropsData || !boneMatrixData.empty()) { @@ -666,7 +670,7 @@ namespace SHADE instanceDataDescSet[frameIndex] = descPool->Allocate ( SHGraphicsPredefinedData::GetPredefinedDescSetLayouts(layoutTypes), - { 0 } + varDescCounts ); #ifdef _DEBUG const auto& DESC_SETS = instanceDataDescSet[frameIndex]->GetVkHandle(); @@ -699,7 +703,12 @@ namespace SHADE 0, static_cast(matPropsDataSize) ); - descSetUpdateRequired = true; + // Update the descriptor set buffer + instanceDataDescSet[frameIndex]->UpdateDescriptorSetBuffer + ( + MATERIAL_DESC_SET_INDEX, + SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA + ); } /* Animation Bone Data */ @@ -713,7 +722,7 @@ namespace SHADE BuffUsage::eVertexBuffer, "Batch Bone Indices Buffer" ); - const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(uint32_t)); + const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(SHMatrix)); SHVkUtil::EnsureBufferAndCopyHostVisibleData ( device, boneMatrixBuffer[frameIndex], boneMatrixData.data(), BONE_MTX_DATA_BYTES, @@ -728,21 +737,15 @@ namespace SHADE MATERIAL_DESC_SET_INDEX, SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA, bufferList, - static_cast(matPropsDataSize), + 0, static_cast(boneMatrixData.size() * sizeof(SHMatrix)) ); - descSetUpdateRequired = true; - } - - // Build and prepare the descriptor set if necessary - if (descSetUpdateRequired) - { // Update the descriptor set buffer instanceDataDescSet[frameIndex]->UpdateDescriptorSetBuffer ( MATERIAL_DESC_SET_INDEX, - SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA + SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA ); } } From 13d562505533707e921a7f900717ed49a163bef7 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 13 Jan 2023 15:18:35 +0800 Subject: [PATCH 105/164] Reverted changes to inertia tensors Created a new branch since I reverted to an older commit but kept some new updates --- Assets/Scenes/PhysicsSandbox.shade | 19 ++++---------- .../Broadphase/SHDynamicAABBTree.cpp | 2 +- .../Physics/Collision/Contacts/SHContact.h | 14 +++-------- .../Collision/Narrowphase/SHCollision.h | 1 + .../Narrowphase/SHConvexVsConvex.cpp | 25 ++++++++++--------- .../Narrowphase/SHSphereVsConvex.cpp | 14 ++++++++++- .../src/Physics/Dynamics/SHContactManager.cpp | 4 ++- .../src/Physics/Dynamics/SHContactSolver.cpp | 15 +++++------ SHADE_Engine/src/Physics/SHPhysicsConstants.h | 2 +- 9 files changed, 49 insertions(+), 47 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index d30c8f07..6d15850b 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,9 +45,9 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: -0.5, y: 10, z: -3} + Position: {x: 0, y: 0, z: 7} Pitch: 0 - Yaw: 180 + Yaw: 0 Roll: 0 Width: 1920 Height: 1080 @@ -71,7 +71,7 @@ Auto Mass: false Mass: 10 Drag: 0.00999999978 - Angular Drag: 0.00999999978 + Angular Drag: 0 Use Gravity: true Gravity Scale: 1 Interpolate: true @@ -95,15 +95,6 @@ Density: 1 Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} - - Is Trigger: true - Collision Tag: 1 - Type: Sphere - Radius: 0.5 - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0.75, y: 0.5, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true Scripts: - Type: PhysicsTestObj @@ -280,7 +271,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0.524352431, y: 13.5, z: 0.0463808179} + Translate: {x: 1, y: 2, z: 3} Rotate: {x: -0, y: 0.785398066, z: 0.785398185} Scale: {x: 0.999999821, y: 0.999999821, z: 0.999999881} IsActive: true @@ -321,7 +312,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -0.5, y: 10, z: 0} + Translate: {x: 0, y: 0, z: 3} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp index dc87d706..7177e517 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp @@ -337,7 +337,7 @@ namespace SHADE { SHASSERT(index >= 0 && index < capacity, "Trying to free an invalid AABB Tree node!") - nodes[index].next = NULL_NODE; + nodes[index].next = freeList; nodes[index].height = NULL_NODE; // Put it back on the free list diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h index 70e53794..0337eedc 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h @@ -31,22 +31,16 @@ namespace SHADE /* Type Definit */ /*---------------------------------------------------------------------------------*/ - enum class Type : uint8_t - { - VERTEX = 0 - , FACE = 1 - }; - /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ struct { - uint8_t indexA; - uint8_t indexB; - uint8_t typeA; - uint8_t typeB; + uint8_t inI; + uint8_t outI; + uint8_t inR; + uint8_t outR; }; uint32_t key = std::numeric_limits::max(); diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 980914c1..2a4503ce 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -82,6 +82,7 @@ namespace SHADE { int32_t halfEdgeA = -1; int32_t halfEdgeB = -1; + int32_t axis = -1; float bestDistance = std::numeric_limits::lowest(); }; diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index be43af8f..97e9dbd4 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -48,7 +48,8 @@ namespace SHADE bool SHCollision::ConvexVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - static constexpr float TOLERANCE = 0.1f + SHPHYSICS_LINEAR_SLOP; + static constexpr float ABSOLUTE_TOLERANCE = 0.01f; + static constexpr float RELATIVE_TOLERANCE = 0.95f; const SHConvexPolyhedron& POLY_A = dynamic_cast(A); const SHConvexPolyhedron& POLY_B = dynamic_cast(B); @@ -72,7 +73,7 @@ namespace SHADE const SHConvexPolyhedron* incidentPoly = nullptr; FaceQuery minFaceQuery; - if (FACE_QUERY_A.bestDistance + TOLERANCE > FACE_QUERY_B.bestDistance) + if (FACE_QUERY_A.bestDistance + ABSOLUTE_TOLERANCE > FACE_QUERY_B.bestDistance * RELATIVE_TOLERANCE) { minFaceQuery = FACE_QUERY_A; referencePoly = &POLY_A; @@ -91,7 +92,7 @@ namespace SHADE // If an edge pair contains the closest distance,vwe ignore any face queries and find the closest points on // each edge and use that as the contact point. - if (EDGE_QUERY.bestDistance > minFaceQuery.bestDistance + TOLERANCE) + if (EDGE_QUERY.bestDistance * RELATIVE_TOLERANCE > minFaceQuery.bestDistance + ABSOLUTE_TOLERANCE) { const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = POLY_A.GetHalfEdge(EDGE_QUERY.halfEdgeA); const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = POLY_B.GetHalfEdge(EDGE_QUERY.halfEdgeB); @@ -111,13 +112,9 @@ namespace SHADE // In this scenario, we only have one contact SHContact contact; - contact.featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); - contact.featurePair.indexA = HALF_EDGE_A.tailVertexIndex; - contact.featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); - contact.featurePair.indexB = HALF_EDGE_B.tailVertexIndex; - - contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); - contact.penetration = EDGE_QUERY.bestDistance; + contact.featurePair.key = EDGE_QUERY.axis; + contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); + contact.penetration = EDGE_QUERY.bestDistance; manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; @@ -188,6 +185,7 @@ namespace SHADE const int32_t EDGE_COUNT_A = A.GetHalfEdgeCount(); const int32_t EDGE_COUNT_B = B.GetHalfEdgeCount(); + int32_t axis = -1; for (int32_t i = 0; i < EDGE_COUNT_A; i += 2) { for (int32_t j = 0; j < EDGE_COUNT_B; j += 2) @@ -196,12 +194,15 @@ namespace SHADE if (!IS_MINKOWSKI_FACE) continue; + ++axis; + const float SEPARATION = distanceBetweenEdges(A, B, i, j); if (SEPARATION > edgeQuery.bestDistance) { edgeQuery.bestDistance = SEPARATION; edgeQuery.halfEdgeA = i; edgeQuery.halfEdgeB = j; + edgeQuery.axis = axis; } } } @@ -321,10 +322,10 @@ namespace SHADE * R = (c1a2 / a1 - c2) / ( b2 - a2b1/a1 ) */ - const float A2_OVER_A1 = VB_DOT_VB / VB_DOT_VA; + const float A2_OVER_A1 = VB_DOT_VA == 0.0f ? 0.0f : VB_DOT_VB / VB_DOT_VA; const float NUMERATOR = C_DOT_VA * A2_OVER_A1 - C_DOT_VB; const float DENOMINATOR = AV_DOT_VB - AV_DOT_VA * A2_OVER_A1; - const float R = NUMERATOR / DENOMINATOR; + const float R = DENOMINATOR == 0.0f ? NUMERATOR : NUMERATOR / DENOMINATOR; // Just take a point from A since it's A -> B return TAIL_A + R * VA; diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index e6a961e0..07466ab1 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -179,7 +179,13 @@ namespace SHADE bool SHCollision::ConvexVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - return SphereVsConvex(manifold, B, A); + if (SphereVsConvex(manifold, B, A)) + { + manifold.normal = -manifold.normal; + return true; + }; + + return false; } /*-----------------------------------------------------------------------------------*/ @@ -326,7 +332,13 @@ namespace SHADE // Face to vertex is in the opposite direction of any tangent. const float PROJECTION = SHVec3::Dot(FACE_TO_CENTER, tangent1); if (PROJECTION < 0) + { + const float DISTANCE_SQUARED = SHVec3::DistanceSquared(faceVertex, CENTER); + if (DISTANCE_SQUARED > RADIUS * RADIUS) + return 0; + return 3; + } // Belongs in region D by default return 4; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index 34e3dabf..ade7b482 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -238,6 +238,8 @@ namespace SHADE void SHContactManager::updateManifold(SHManifold& manifold, const SHManifold& oldManifold) noexcept { + static const float SQRT_ONE_THIRD = std::sqrtf(1.0f / 3.0f); + // Early out since exiting a collision does not require an update beyond updating the state if (manifold.state == SHCollisionState::EXIT) return; @@ -250,7 +252,7 @@ namespace SHADE const SHVec3& OLD_TANGENT_1 = oldManifold.tangents[1]; // Compute tangents - if (std::fabs(NORMAL.x) >= SHMath::EULER_CONSTANT) + if (std::fabs(NORMAL.x) >= SQRT_ONE_THIRD) tangent0 = SHVec3{ NORMAL.y, -NORMAL.x, 0.0f }; else tangent0 = SHVec3{ 0.0f, NORMAL.z, -NORMAL.y }; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp index fa6f1266..b5d7c2cc 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp @@ -187,14 +187,7 @@ namespace SHADE * restituion bias = restitution * (relative velocity /dot normal) */ - const SHVec3 RV_A = vA + SHVec3::Cross(wA, contact.rA); - const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB); - const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal); - const float ERROR_BIAS = SHPHYSICS_BAUMGARTE * INV_DT * std::min(0.0f, -contact.penetration + SHPHYSICS_LINEAR_SLOP); - const float RESTITUTION_BIAS = std::fabs(RV_N) > SHPHYSICS_RESTITUTION_THRESHOLD ? -constraint.restitution * RV_N : 0.0f; - - contact.bias = ERROR_BIAS + RESTITUTION_BIAS; // Warm starting // Compute impulses @@ -208,6 +201,14 @@ namespace SHADE vB += impulse * constraint.invMassB * LINEAR_LOCK_B; wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, impulse) * ANGULAR_LOCK_B; + + const SHVec3 RV_A = vA + SHVec3::Cross(wA, contact.rA); + const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB); + const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal); + + const float RESTITUTION_BIAS = std::fabs(RV_N) > SHPHYSICS_RESTITUTION_THRESHOLD ? -constraint.restitution * RV_N : 0.0f; + + contact.bias = ERROR_BIAS + RESTITUTION_BIAS; } velocityStateA.LinearVelocity = vA; diff --git a/SHADE_Engine/src/Physics/SHPhysicsConstants.h b/SHADE_Engine/src/Physics/SHPhysicsConstants.h index 0d8f6fc8..a6cbd608 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsConstants.h +++ b/SHADE_Engine/src/Physics/SHPhysicsConstants.h @@ -26,7 +26,7 @@ namespace SHADE * @brief * Linear Collision & Constraint tolerance. */ - static constexpr float SHPHYSICS_LINEAR_SLOP = 0.005f * SHPHYSICS_LENGTHS_PER_UNIT_METER; + static constexpr float SHPHYSICS_LINEAR_SLOP = 0.05f * SHPHYSICS_LENGTHS_PER_UNIT_METER; /** * @brief From ef5016351b2e10e37a51361b32426b2855499006 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Fri, 13 Jan 2023 15:18:35 +0800 Subject: [PATCH 106/164] Reverted changes to inertia tensors Created a new branch since I reverted to an older commit but kept some new updates. This will be the main branch moving forward. --- Assets/Scenes/PhysicsSandbox.shade | 19 ++++---------- .../Broadphase/SHDynamicAABBTree.cpp | 2 +- .../Physics/Collision/Contacts/SHContact.h | 14 +++-------- .../Collision/Narrowphase/SHCollision.h | 1 + .../Narrowphase/SHConvexVsConvex.cpp | 25 ++++++++++--------- .../Narrowphase/SHSphereVsConvex.cpp | 14 ++++++++++- .../src/Physics/Dynamics/SHContactManager.cpp | 4 ++- .../src/Physics/Dynamics/SHContactSolver.cpp | 15 +++++------ SHADE_Engine/src/Physics/SHPhysicsConstants.h | 2 +- 9 files changed, 49 insertions(+), 47 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index d30c8f07..6d15850b 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,9 +45,9 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: -0.5, y: 10, z: -3} + Position: {x: 0, y: 0, z: 7} Pitch: 0 - Yaw: 180 + Yaw: 0 Roll: 0 Width: 1920 Height: 1080 @@ -71,7 +71,7 @@ Auto Mass: false Mass: 10 Drag: 0.00999999978 - Angular Drag: 0.00999999978 + Angular Drag: 0 Use Gravity: true Gravity Scale: 1 Interpolate: true @@ -95,15 +95,6 @@ Density: 1 Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} - - Is Trigger: true - Collision Tag: 1 - Type: Sphere - Radius: 0.5 - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0.75, y: 0.5, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true Scripts: - Type: PhysicsTestObj @@ -280,7 +271,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0.524352431, y: 13.5, z: 0.0463808179} + Translate: {x: 1, y: 2, z: 3} Rotate: {x: -0, y: 0.785398066, z: 0.785398185} Scale: {x: 0.999999821, y: 0.999999821, z: 0.999999881} IsActive: true @@ -321,7 +312,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -0.5, y: 10, z: 0} + Translate: {x: 0, y: 0, z: 3} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp index dc87d706..7177e517 100644 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp +++ b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp @@ -337,7 +337,7 @@ namespace SHADE { SHASSERT(index >= 0 && index < capacity, "Trying to free an invalid AABB Tree node!") - nodes[index].next = NULL_NODE; + nodes[index].next = freeList; nodes[index].height = NULL_NODE; // Put it back on the free list diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h index 70e53794..0337eedc 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h @@ -31,22 +31,16 @@ namespace SHADE /* Type Definit */ /*---------------------------------------------------------------------------------*/ - enum class Type : uint8_t - { - VERTEX = 0 - , FACE = 1 - }; - /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ struct { - uint8_t indexA; - uint8_t indexB; - uint8_t typeA; - uint8_t typeB; + uint8_t inI; + uint8_t outI; + uint8_t inR; + uint8_t outR; }; uint32_t key = std::numeric_limits::max(); diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 980914c1..2a4503ce 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -82,6 +82,7 @@ namespace SHADE { int32_t halfEdgeA = -1; int32_t halfEdgeB = -1; + int32_t axis = -1; float bestDistance = std::numeric_limits::lowest(); }; diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index be43af8f..97e9dbd4 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -48,7 +48,8 @@ namespace SHADE bool SHCollision::ConvexVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - static constexpr float TOLERANCE = 0.1f + SHPHYSICS_LINEAR_SLOP; + static constexpr float ABSOLUTE_TOLERANCE = 0.01f; + static constexpr float RELATIVE_TOLERANCE = 0.95f; const SHConvexPolyhedron& POLY_A = dynamic_cast(A); const SHConvexPolyhedron& POLY_B = dynamic_cast(B); @@ -72,7 +73,7 @@ namespace SHADE const SHConvexPolyhedron* incidentPoly = nullptr; FaceQuery minFaceQuery; - if (FACE_QUERY_A.bestDistance + TOLERANCE > FACE_QUERY_B.bestDistance) + if (FACE_QUERY_A.bestDistance + ABSOLUTE_TOLERANCE > FACE_QUERY_B.bestDistance * RELATIVE_TOLERANCE) { minFaceQuery = FACE_QUERY_A; referencePoly = &POLY_A; @@ -91,7 +92,7 @@ namespace SHADE // If an edge pair contains the closest distance,vwe ignore any face queries and find the closest points on // each edge and use that as the contact point. - if (EDGE_QUERY.bestDistance > minFaceQuery.bestDistance + TOLERANCE) + if (EDGE_QUERY.bestDistance * RELATIVE_TOLERANCE > minFaceQuery.bestDistance + ABSOLUTE_TOLERANCE) { const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = POLY_A.GetHalfEdge(EDGE_QUERY.halfEdgeA); const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = POLY_B.GetHalfEdge(EDGE_QUERY.halfEdgeB); @@ -111,13 +112,9 @@ namespace SHADE // In this scenario, we only have one contact SHContact contact; - contact.featurePair.typeA = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); - contact.featurePair.indexA = HALF_EDGE_A.tailVertexIndex; - contact.featurePair.typeB = SHUtilities::ConvertEnum(SHContactFeatures::Type::VERTEX); - contact.featurePair.indexB = HALF_EDGE_B.tailVertexIndex; - - contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); - contact.penetration = EDGE_QUERY.bestDistance; + contact.featurePair.key = EDGE_QUERY.axis; + contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); + contact.penetration = EDGE_QUERY.bestDistance; manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; @@ -188,6 +185,7 @@ namespace SHADE const int32_t EDGE_COUNT_A = A.GetHalfEdgeCount(); const int32_t EDGE_COUNT_B = B.GetHalfEdgeCount(); + int32_t axis = -1; for (int32_t i = 0; i < EDGE_COUNT_A; i += 2) { for (int32_t j = 0; j < EDGE_COUNT_B; j += 2) @@ -196,12 +194,15 @@ namespace SHADE if (!IS_MINKOWSKI_FACE) continue; + ++axis; + const float SEPARATION = distanceBetweenEdges(A, B, i, j); if (SEPARATION > edgeQuery.bestDistance) { edgeQuery.bestDistance = SEPARATION; edgeQuery.halfEdgeA = i; edgeQuery.halfEdgeB = j; + edgeQuery.axis = axis; } } } @@ -321,10 +322,10 @@ namespace SHADE * R = (c1a2 / a1 - c2) / ( b2 - a2b1/a1 ) */ - const float A2_OVER_A1 = VB_DOT_VB / VB_DOT_VA; + const float A2_OVER_A1 = VB_DOT_VA == 0.0f ? 0.0f : VB_DOT_VB / VB_DOT_VA; const float NUMERATOR = C_DOT_VA * A2_OVER_A1 - C_DOT_VB; const float DENOMINATOR = AV_DOT_VB - AV_DOT_VA * A2_OVER_A1; - const float R = NUMERATOR / DENOMINATOR; + const float R = DENOMINATOR == 0.0f ? NUMERATOR : NUMERATOR / DENOMINATOR; // Just take a point from A since it's A -> B return TAIL_A + R * VA; diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index e6a961e0..07466ab1 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -179,7 +179,13 @@ namespace SHADE bool SHCollision::ConvexVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - return SphereVsConvex(manifold, B, A); + if (SphereVsConvex(manifold, B, A)) + { + manifold.normal = -manifold.normal; + return true; + }; + + return false; } /*-----------------------------------------------------------------------------------*/ @@ -326,7 +332,13 @@ namespace SHADE // Face to vertex is in the opposite direction of any tangent. const float PROJECTION = SHVec3::Dot(FACE_TO_CENTER, tangent1); if (PROJECTION < 0) + { + const float DISTANCE_SQUARED = SHVec3::DistanceSquared(faceVertex, CENTER); + if (DISTANCE_SQUARED > RADIUS * RADIUS) + return 0; + return 3; + } // Belongs in region D by default return 4; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index 34e3dabf..ade7b482 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -238,6 +238,8 @@ namespace SHADE void SHContactManager::updateManifold(SHManifold& manifold, const SHManifold& oldManifold) noexcept { + static const float SQRT_ONE_THIRD = std::sqrtf(1.0f / 3.0f); + // Early out since exiting a collision does not require an update beyond updating the state if (manifold.state == SHCollisionState::EXIT) return; @@ -250,7 +252,7 @@ namespace SHADE const SHVec3& OLD_TANGENT_1 = oldManifold.tangents[1]; // Compute tangents - if (std::fabs(NORMAL.x) >= SHMath::EULER_CONSTANT) + if (std::fabs(NORMAL.x) >= SQRT_ONE_THIRD) tangent0 = SHVec3{ NORMAL.y, -NORMAL.x, 0.0f }; else tangent0 = SHVec3{ 0.0f, NORMAL.z, -NORMAL.y }; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp index fa6f1266..b5d7c2cc 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp @@ -187,14 +187,7 @@ namespace SHADE * restituion bias = restitution * (relative velocity /dot normal) */ - const SHVec3 RV_A = vA + SHVec3::Cross(wA, contact.rA); - const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB); - const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal); - const float ERROR_BIAS = SHPHYSICS_BAUMGARTE * INV_DT * std::min(0.0f, -contact.penetration + SHPHYSICS_LINEAR_SLOP); - const float RESTITUTION_BIAS = std::fabs(RV_N) > SHPHYSICS_RESTITUTION_THRESHOLD ? -constraint.restitution * RV_N : 0.0f; - - contact.bias = ERROR_BIAS + RESTITUTION_BIAS; // Warm starting // Compute impulses @@ -208,6 +201,14 @@ namespace SHADE vB += impulse * constraint.invMassB * LINEAR_LOCK_B; wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, impulse) * ANGULAR_LOCK_B; + + const SHVec3 RV_A = vA + SHVec3::Cross(wA, contact.rA); + const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB); + const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal); + + const float RESTITUTION_BIAS = std::fabs(RV_N) > SHPHYSICS_RESTITUTION_THRESHOLD ? -constraint.restitution * RV_N : 0.0f; + + contact.bias = ERROR_BIAS + RESTITUTION_BIAS; } velocityStateA.LinearVelocity = vA; diff --git a/SHADE_Engine/src/Physics/SHPhysicsConstants.h b/SHADE_Engine/src/Physics/SHPhysicsConstants.h index 0d8f6fc8..a6cbd608 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsConstants.h +++ b/SHADE_Engine/src/Physics/SHPhysicsConstants.h @@ -26,7 +26,7 @@ namespace SHADE * @brief * Linear Collision & Constraint tolerance. */ - static constexpr float SHPHYSICS_LINEAR_SLOP = 0.005f * SHPHYSICS_LENGTHS_PER_UNIT_METER; + static constexpr float SHPHYSICS_LINEAR_SLOP = 0.05f * SHPHYSICS_LENGTHS_PER_UNIT_METER; /** * @brief From de6f4dd13847ada567fdca756d64812d416fee4f Mon Sep 17 00:00:00 2001 From: Glence Date: Fri, 13 Jan 2023 17:46:01 +0800 Subject: [PATCH 107/164] Added functions to attach and detach audio clips to objects --- SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp | 13 +++++++++++++ SHADE_Engine/src/AudioSystem/SHAudioSystem.h | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp index cd04f841..a31383fb 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp @@ -368,6 +368,19 @@ namespace SHADE sfxChannelGroup->addGroup(channelGroup); } + void SHAudioSystem::AttachAudioClipToObject(Handle handle, EntityID eid) + { + if (auto transform = SHComponentManager::GetComponent_s(eid)) + { + handle->transformRef = transform; + } + } + + void SHAudioSystem::DetachAudioClipToObject(Handle handle, EntityID eid) + { + handle->transformRef = nullptr; + } + //AudioClip* SHAudioSystem::CreateAudioClip(const char* path) //{ // AudioClipID newID{}; diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h index 08505ea9..153ec86a 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h @@ -26,6 +26,7 @@ namespace SHADE class AudioClip { public: + //expose to sxripting void Play(); //void Play(SHVec3 position); void Stop(bool fadeOut = true); @@ -38,7 +39,7 @@ namespace SHADE friend class SHAudioSystem; private: FMOD::Studio::EventInstance* instance = nullptr; - //SHTransformComponent* transformRef; + SHTransformComponent* transformRef = nullptr; }; class SH_API SHAudioSystem : public SHSystem @@ -68,10 +69,14 @@ namespace SHADE std::optional GetEventGUID(const char* path); //AudioClip* CreateAudioClip(const char* path); + //AUDIO CLIP Handle CreateAudioClip(const char* path); void AddAudioClipToBGMChannelGroup(Handle handle); void AddAudioClipToSFXChannelGroup(Handle handle); + void AttachAudioClipToObject(Handle handle, EntityID eid); + void DetachAudioClipToObject(Handle handle, EntityID eid); + /// float GetBgmVolume(); float GetSfxVolume(); float GetMasterVolume(); From 74d6e5cee7ae9c4b075fd742acfbade289fb8834 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 13 Jan 2023 18:14:40 +0800 Subject: [PATCH 108/164] SHPipelineLibrary now sets the pipeline layouts correctly --- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 15 ++++--- .../GlobalData/SHGraphicsPredefinedData.cpp | 44 +++++++++++-------- .../GlobalData/SHGraphicsPredefinedData.h | 15 ++++--- .../GlobalData/SHPredefinedDescriptorTypes.h | 2 +- .../MiddleEnd/Interface/SHGraphicsConstants.h | 2 +- .../MiddleEnd/Pipeline/SHPipelineLibrary.cpp | 4 +- .../MiddleEnd/Pipeline/SHPipelineLibrary.h | 4 +- .../RenderGraph/SHRenderGraphNode.cpp | 28 +++++++++++- 8 files changed, 75 insertions(+), 39 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 33a51cbb..824be2b7 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -581,7 +581,11 @@ namespace SHADE return; // Bind all required objects before drawing - static std::array dynamicOffset{ 0U, static_cast(boneMatrixData.size() * sizeof(SHMatrix)) }; + std::vector dynamicOffset{ 0 }; + if (!boneMatrixData.empty()) + { + dynamicOffset.emplace_back(0); + } cmdBuffer->BeginLabeledSegment("SHBatch for Pipeline #" + std::to_string(pipeline.GetId().Data.Index)); cmdBuffer->BindPipeline(pipeline); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0); @@ -649,17 +653,14 @@ namespace SHADE static constexpr uint32_t MATERIAL_DESC_SET_INDEX = 0; /* Create Descriptor Sets if Needed */ - std::vector varDescCounts; PreDefDescLayoutType layoutTypes = {}; if (matPropsData) { - layoutTypes |= PreDefDescLayoutType::MATERIALS; - varDescCounts.push_back(0); + layoutTypes = PreDefDescLayoutType::MATERIALS; } if (!boneMatrixData.empty()) { - layoutTypes |= PreDefDescLayoutType::BONES; - varDescCounts.push_back(0); + layoutTypes = PreDefDescLayoutType::MATERIAL_AND_BONES; } if (matPropsData || !boneMatrixData.empty()) @@ -670,7 +671,7 @@ namespace SHADE instanceDataDescSet[frameIndex] = descPool->Allocate ( SHGraphicsPredefinedData::GetPredefinedDescSetLayouts(layoutTypes), - varDescCounts + { 0 } ); #ifdef _DEBUG const auto& DESC_SETS = instanceDataDescSet[frameIndex]->GetVkHandle(); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp index bbd1a1b1..35e407e3 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp @@ -20,13 +20,20 @@ namespace SHADE //SHGraphicsPredefinedData::PerSystem SHGraphicsPredefinedData::renderGraphNodeComputeData; void SHGraphicsPredefinedData::InitDescMappings(void) noexcept - { + { perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].descMappings.AddMappings ({ {SHPredefinedDescriptorTypes::STATIC_DATA, 0}, {SHPredefinedDescriptorTypes::CAMERA, 1}, {SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH, 2}, - }); + }); + + perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING_ANIM)].descMappings.AddMappings + ({ + {SHPredefinedDescriptorTypes::STATIC_DATA, 0}, + {SHPredefinedDescriptorTypes::CAMERA, 1}, + {SHPredefinedDescriptorTypes::PER_INSTANCE_ANIM_BATCH, 2}, + }); perSystemData[SHUtilities::ConvertEnum(SystemType::TEXT_RENDERING)].descMappings.AddMappings ({ @@ -129,14 +136,7 @@ namespace SHADE .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PER_INST_MATERIAL_DATA, .DescriptorCount = 1, }; - SHVkDescriptorSetLayout::Binding boneDataBinding - { - .Type = vk::DescriptorType::eStorageBufferDynamic, - .Stage = vk::ShaderStageFlagBits::eVertex, - .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA, - .DescriptorCount = 1, - }; - Handle materialDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout({ materialDataBinding, boneDataBinding }); + Handle materialDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout({ materialDataBinding }); SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, materialDataPerInstanceLayout->GetVkHandle(), "[Descriptor Set Layout] Material Globals"); // font bitmap data (texture) @@ -160,17 +160,16 @@ namespace SHADE Handle fontDataDescSetLayout = logicalDevice->CreateDescriptorSetLayout({ fontBitmapBinding, fontMatrixBinding }); SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, fontDataDescSetLayout->GetVkHandle(), "[Descriptor Set Layout] Font Data"); - // Bone matrix data - SHVkDescriptorSetLayout::Binding boneMatrixBinding + // For per instance data (transforms, materials, etc.) + SHVkDescriptorSetLayout::Binding boneDataBinding { - .Type = vk::DescriptorType::eStorageBuffer, + .Type = vk::DescriptorType::eStorageBufferDynamic, .Stage = vk::ShaderStageFlagBits::eVertex, - .BindPoint = SHGraphicsConstants::DescriptorSetBindings::BONE_MATRIX_DATA, + .BindPoint = SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA, .DescriptorCount = 1, }; - - Handle boneMatricesDescSetLayout = logicalDevice->CreateDescriptorSetLayout({ boneMatrixBinding }); - SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, boneMatricesDescSetLayout->GetVkHandle(), "[Descriptor Set Layout] Bone Matrix Data"); + Handle materialBoneDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout({ materialDataBinding, boneDataBinding }); + SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, materialBoneDataPerInstanceLayout->GetVkHandle(), "[Descriptor Set Layout] Material and Bone Globals"); predefinedLayouts.push_back(staticGlobalLayout); predefinedLayouts.push_back(lightDataDescSetLayout); @@ -178,13 +177,20 @@ namespace SHADE predefinedLayouts.push_back(materialDataPerInstanceLayout); predefinedLayouts.push_back(fontDataDescSetLayout); predefinedLayouts.push_back({}); - predefinedLayouts.push_back(boneMatricesDescSetLayout); + predefinedLayouts.push_back(materialBoneDataPerInstanceLayout); perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING)].descSetLayouts = GetPredefinedDescSetLayouts ( SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::STATIC_DATA | SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::CAMERA | - SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIALS + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIALS + ); + + perSystemData[SHUtilities::ConvertEnum(SystemType::BATCHING_ANIM)].descSetLayouts = GetPredefinedDescSetLayouts + ( + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::STATIC_DATA | + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::CAMERA | + SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes::MATERIAL_AND_BONES ); perSystemData[SHUtilities::ConvertEnum(SystemType::TEXT_RENDERING)].descSetLayouts = GetPredefinedDescSetLayouts diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h index 8c304e4c..c346dc9b 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h @@ -23,18 +23,19 @@ namespace SHADE // This enum is mainly to initialize a bit field to retrieve bit fields from SHPRedefinedData enum class PredefinedDescSetLayoutTypes : uint64_t { - STATIC_DATA = 0b0000001, - LIGHTS = 0b0000010, - CAMERA = 0b0000100, - MATERIALS = 0b0001000, - FONT = 0b0010000, - SHADOW = 0b0100000, - BONES = 0b1000000, + STATIC_DATA = 0b00000001, + LIGHTS = 0b00000010, + CAMERA = 0b00000100, + MATERIALS = 0b00001000, + FONT = 0b00010000, + SHADOW = 0b00100000, + MATERIAL_AND_BONES = 0b01000000 }; enum class SystemType { BATCHING = 0, + BATCHING_ANIM, TEXT_RENDERING, RENDER_GRAPH_NODE_COMPUTE, NUM_TYPES diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h index 7a7bb826..ffb4685b 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHPredefinedDescriptorTypes.h @@ -12,7 +12,7 @@ namespace SHADE LIGHTS, CAMERA, PER_INSTANCE_BATCH, - BONES, + PER_INSTANCE_ANIM_BATCH, FONT, RENDER_GRAPH_RESOURCE, RENDER_GRAPH_NODE_COMPUTE_RESOURCE, diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index 3e8e379e..e967312f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -173,7 +173,7 @@ namespace SHADE */ /***************************************************************************/ - static constexpr uint32_t BONE_MATRIX_DATA = 0; + static constexpr uint32_t BONE_MATRIX_DATA = 1; }; struct VertexBufferBindings diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.cpp index baf09a2d..907a094e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.cpp @@ -8,12 +8,12 @@ namespace SHADE { - Handle SHPipelineLibrary::CreateGraphicsPipelines(std::pair, Handle> const& vsFsPair, Handle renderpass, Handle subpass) noexcept + Handle SHPipelineLibrary::CreateGraphicsPipelines(std::pair, Handle> const& vsFsPair, Handle renderpass, Handle subpass, SHGraphicsPredefinedData::SystemType systemType) noexcept { SHPipelineLayoutParams params { .shaderModules = {vsFsPair.first, vsFsPair.second}, - .predefinedDescSetLayouts = SHGraphicsPredefinedData::GetSystemData(SHGraphicsPredefinedData::SystemType::BATCHING).descSetLayouts + .predefinedDescSetLayouts = SHGraphicsPredefinedData::GetSystemData(systemType).descSetLayouts }; // Create the pipeline layout diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.h index 5085f21f..ff7d8485 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Pipeline/SHPipelineLibrary.h @@ -3,6 +3,7 @@ #include #include "Graphics/Shaders/SHVkShaderModule.h" #include "Graphics/Pipeline/SHVkPipeline.h" +#include "Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.h" namespace SHADE { @@ -32,7 +33,8 @@ namespace SHADE Handle CreateGraphicsPipelines ( std::pair, Handle> const& vsFsPair, Handle renderpass, - Handle subpass + Handle subpass, + SHGraphicsPredefinedData::SystemType systemType ) noexcept; Handle GetGraphicsPipeline (std::pair, Handle> const& vsFsPair) noexcept; bool CheckGraphicsPipelineExistence (std::pair, Handle> const& vsFsPair) noexcept; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp index 3c412645..1603563e 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp @@ -583,11 +583,37 @@ namespace SHADE Handle pipeline = pipelineLibrary.GetGraphicsPipeline(vsFsPair); if (!pipeline) { + // default to batching system type + SHGraphicsPredefinedData::SystemType systemType{ SHGraphicsPredefinedData::SystemType::BATCHING }; + auto const& REFLECTED_SETS = vsFsPair.first->GetReflectedData().GetDescriptorBindingInfo().GetReflectedSets(); + + // look for animation set binding in the shader (set 2 binding 1) + for (auto const& set : REFLECTED_SETS) + { + auto const mappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING_ANIM); + + // Look for set 2 + if (set->set == mappings.at(SHPredefinedDescriptorTypes::PER_INSTANCE_ANIM_BATCH)) + { + for (int i = 0; i < set->binding_count; i++) + { + // look for binding 1. if found use BATCHING_ANIM system type + if (set->bindings[i]->binding == SHGraphicsConstants::DescriptorSetBindings::BONE_MATRIX_DATA) + { + systemType = SHGraphicsPredefinedData::SystemType::BATCHING_ANIM; + break; + } + } + break; + } + } + pipeline = pipelineLibrary.CreateGraphicsPipelines ( vsFsPair, renderpass, - subpass + subpass, + systemType ); } From d1a41ea19482d0b156e04a58ad329531dd9ec746 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 13 Jan 2023 20:33:25 +0800 Subject: [PATCH 109/164] Fixed raccoon not rendering for animated material --- Assets/Materials/AnimatedRaccoon.shmat | 2 +- Assets/Shaders/Anim_VS.glsl | 2 +- Assets/Shaders/Anim_VS.shshaderb | Bin 5633 -> 5597 bytes .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 11 ----------- .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 1 - .../MiddleEnd/Interface/SHMeshLibrary.h | 14 +++++++------- 6 files changed, 9 insertions(+), 21 deletions(-) diff --git a/Assets/Materials/AnimatedRaccoon.shmat b/Assets/Materials/AnimatedRaccoon.shmat index ae7c7163..a255aa11 100644 --- a/Assets/Materials/AnimatedRaccoon.shmat +++ b/Assets/Materials/AnimatedRaccoon.shmat @@ -3,6 +3,6 @@ SubPass: G-Buffer Write Properties: data.color: {x: 1, y: 1, z: 1, w: 1} - data.textureIndex: 0 + data.textureIndex: 64651793 data.alpha: 0 data.beta: {x: 1, y: 1, z: 1} \ No newline at end of file diff --git a/Assets/Shaders/Anim_VS.glsl b/Assets/Shaders/Anim_VS.glsl index c9aa64aa..d12cb34b 100644 --- a/Assets/Shaders/Anim_VS.glsl +++ b/Assets/Shaders/Anim_VS.glsl @@ -71,5 +71,5 @@ void main() boneMatrix += BoneMatrices.data[aBoneIndices[3]] * aBoneWeights[3]; // clip space for rendering - gl_Position = cameraData.vpMat * worldTransform * boneMatrix * vec4 (aVertexPos, 1.0f); + gl_Position = cameraData.vpMat * worldTransform * vec4 (aVertexPos, 1.0f); } \ No newline at end of file diff --git a/Assets/Shaders/Anim_VS.shshaderb b/Assets/Shaders/Anim_VS.shshaderb index b1940e9865db2bbb8bfda8a10170d1e63280ac24..1560e232d160b78f166e493bb94fb1f6114fd935 100644 GIT binary patch delta 219 zcmZqFxvMS4ctw-}449de*%=rZ8MqjD8BR?UD`q^ou`8aL@#N-L%-KvpDva$ji08%i znXBFesN)b2n*nhk11p09kUk8=ATbbti5&s5L1K(ZVn?B3Oh7t-ft^7SNFM`YkbaPQ wkh}#DPXw9^k~2Z0!*#t|rH#{d*Q0p#ojVs0R|V_*jJK??r@t+D`O0A#u&VE_OC delta 255 zcmcbs-KZnR_(PNd449de*%=rZ8MqjD8O}@;D`q^su`8aL@$}|b%-KvpDva$ji08%i znX6t2sN)b2X8`de237_T|1c0A1Y(f5EdvXK36MSl#AZMo2vnm0q>lnINDKsEV#k1N zkQgJ9*m0;B6OayIU}sPS(kFlzq#vl>l>wx{0*EI94F*Y`1Y(c_K#ClJVs=3K6cFzP UVs0R|V_*jJLF)bjt+D`O03XUIVE_OC diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 824be2b7..48859040 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -602,10 +602,6 @@ namespace SHADE dynamicOffset ); } - if (boneMatrixBuffer[frameIndex] && boneFirstIndexBuffer[frameIndex]) - { - cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::BONE_INDICES, boneFirstIndexBuffer[frameIndex], 0); - } cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); cmdBuffer->EndLabeledSegment(); } @@ -716,13 +712,6 @@ namespace SHADE if (!boneMatrixData.empty()) { // Update GPU Buffers - const uint32_t BONE_IDX_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, boneFirstIndexBuffer[frameIndex], boneMatrixIndices.data(), BONE_IDX_DATA_BYTES, - BuffUsage::eVertexBuffer, - "Batch Bone Indices Buffer" - ); const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(SHMatrix)); SHVkUtil::EnsureBufferAndCopyHostVisibleData ( diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index 318e97d8..1bdd9a59 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -134,7 +134,6 @@ namespace SHADE TripleBuffer instancedIntegerBuffer; TripleBuffer matPropsBuffer; TripleBuffer boneMatrixBuffer; - TripleBuffer boneFirstIndexBuffer; TripleDescSet instanceDataDescSet; /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h index f61e4a20..5e42c526 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h @@ -154,13 +154,13 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ /* Getter Functions */ /*-----------------------------------------------------------------------------*/ - Handle GetVertexPositionsBuffer() const noexcept { return vertPosBuffer; } - Handle GetVertexTexCoordsBuffer() const noexcept { return vertTexCoordBuffer; } - Handle GetVertexTangentsBuffer() const noexcept { return vertTangentBuffer; } - Handle GetVertexNormalsBuffer() const noexcept { return vertNormalBuffer; } - Handle GetVertexBoneIndicesBuffer() const noexcept { return vertBoneIdxBuffer; } - Handle GetVertexBoneWeightsBuffer() const noexcept { return vertBoneWeightBuffer; } - Handle GetIndexBuffer() const noexcept { return indexBuffer; } + Handle GetVertexPositionsBuffer() const noexcept { return vertPosBuffer; } + Handle GetVertexTexCoordsBuffer() const noexcept { return vertTexCoordBuffer; } + Handle GetVertexTangentsBuffer() const noexcept { return vertTangentBuffer; } + Handle GetVertexNormalsBuffer() const noexcept { return vertNormalBuffer; } + Handle GetVertexBoneIndicesBuffer() const noexcept { return vertBoneIdxBuffer; } + Handle GetVertexBoneWeightsBuffer() const noexcept { return vertBoneWeightBuffer; } + Handle GetIndexBuffer() const noexcept { return indexBuffer; } private: /*-----------------------------------------------------------------------------*/ From 7ecb8b11ada0db7d81a5dcf933f1d10f169e67d2 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 13 Jan 2023 21:26:05 +0800 Subject: [PATCH 110/164] Added support for rendering bone-less models using the animated shader --- Assets/Shaders/Anim_VS.glsl | 2 +- Assets/Shaders/Anim_VS.shshaderb | Bin 5597 -> 4341 bytes .../src/Animation/SHAnimatorComponent.cpp | 2 +- .../MiddleEnd/Interface/SHMeshLibrary.cpp | 2 ++ .../src/Resource/SHResourceManager.hpp | 4 +++- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Assets/Shaders/Anim_VS.glsl b/Assets/Shaders/Anim_VS.glsl index d12cb34b..c9aa64aa 100644 --- a/Assets/Shaders/Anim_VS.glsl +++ b/Assets/Shaders/Anim_VS.glsl @@ -71,5 +71,5 @@ void main() boneMatrix += BoneMatrices.data[aBoneIndices[3]] * aBoneWeights[3]; // clip space for rendering - gl_Position = cameraData.vpMat * worldTransform * vec4 (aVertexPos, 1.0f); + gl_Position = cameraData.vpMat * worldTransform * boneMatrix * vec4 (aVertexPos, 1.0f); } \ No newline at end of file diff --git a/Assets/Shaders/Anim_VS.shshaderb b/Assets/Shaders/Anim_VS.shshaderb index 1560e232d160b78f166e493bb94fb1f6114fd935..c10bdc5a8ad53cb47aeb2f3d9b375b1e1728ae88 100644 GIT binary patch delta 1248 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 diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 50ce1975..5eff2c45 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -86,7 +86,7 @@ namespace SHADE currClip = newClip; secsPerTick = 1.0f / currClip->GetTicksPerSecond(); - if (!rig) + if (rig) { updatePoseWithClip(0.0f); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp index df21343e..b0ca7f14 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp @@ -185,7 +185,9 @@ namespace SHADE } else { + const auto OG_SIZE = vertBoneWeightStorage.size(); vertBoneWeightStorage.resize(vertBoneWeightStorage.size() + addJob.VertexCount); + std::fill_n(vertBoneWeightStorage.begin() + OG_SIZE, addJob.VertexCount, SHVec4(1.0f, 0.0f, 0.0f, 0.0f)); } indexStorage.insert ( diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 5aff2529..ea6b4ad9 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -211,7 +211,9 @@ namespace SHADE assetData.VertexTangents.data(), assetData.VertexNormals.data(), assetData.Indices.size(), - assetData.Indices.data() + assetData.Indices.data(), + assetData.VertexBoneIndices.empty() ? nullptr : assetData.VertexBoneIndices.data(), + assetData.VertexBoneWeights.empty() ? nullptr : assetData.VertexBoneWeights.data() ); } // Textures From dab109bc771b950548087178194c1180cf739671 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 16 Jan 2023 02:43:31 +0800 Subject: [PATCH 111/164] Fixed a fatal error with rigid body rotations. --- .../src/Physics/Dynamics/SHMotionState.cpp | 5 +- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 2 +- .../src/Physics/Dynamics/SHRigidBody.cpp | 13 +-- .../Routines/SHPhysicsPostUpdateRoutine.cpp | 82 ++++++++++--------- 4 files changed, 52 insertions(+), 50 deletions(-) diff --git a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp index cc014050..89c8ad32 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp @@ -109,10 +109,7 @@ namespace SHADE prevOrientation = orientation; - SHQuaternion qv{ velocity.x * dt, velocity.y * dt, velocity.z * dt, 0.0f }; - qv *= orientation; - - orientation += qv * 0.5f; + orientation += orientation * SHQuaternion{ velocity.x, velocity.y, velocity.z, 0.0f } * dt * 0.5f; orientation = SHQuaternion::Normalise(orientation); } diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp index 3595ec99..ed00e80e 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp @@ -142,7 +142,7 @@ namespace SHADE rigidBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * dt; // Integrate torque into angular velocity - rigidBody.angularVelocity += rigidBody.worldInvInertia * (rigidBody.accumulatedTorque * dt); + rigidBody.angularVelocity += rigidBody.worldInvInertia * rigidBody.accumulatedTorque * dt; // Apply drag (exponentially applied) rigidBody.linearVelocity *= 1.0f / (1.0f + dt * rigidBody.linearDrag); diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index d76b79b8..1de9e543 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -637,16 +637,17 @@ namespace SHADE void SHRigidBody::ComputeWorldData() noexcept { + const SHMatrix R = SHMatrix::Rotate(motionState.orientation); + const SHMatrix RT = SHMatrix::Transpose(R); + + // Compute world centroid + worldCentroid = (R * localCentroid) + motionState.position; + if (bodyType == Type::STATIC) return; - const SHMatrix ROTATION = SHMatrix::Rotate(motionState.orientation); - // Compute world inertia - worldInvInertia = SHMatrix::Transpose(ROTATION) * localInvInertia * ROTATION; - - // Compute world centroid - worldCentroid = (ROTATION * localCentroid) + motionState.position; + worldInvInertia = R * (localInvInertia * RT); } void SHRigidBody::ComputeMassData() noexcept diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp index 5b0e5563..a08b943b 100644 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp +++ b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp @@ -19,7 +19,6 @@ #include "Scene/SHSceneManager.h" #include "Scripting/SHScriptEngine.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -48,50 +47,55 @@ namespace SHADE // Interpolate transforms for rendering. // Only rigid bodies can move due to physics, so we run through the rigid body component dense set. - const auto& RIGIDBODY_DENSE = SHComponentManager::GetDense(); - for (auto& rigidBodyComponent : RIGIDBODY_DENSE) + if (physicsSystem->worldUpdated) { - const EntityID EID = rigidBodyComponent.GetEID(); - - // Skip missing transforms - auto* transformComponent = SHComponentManager::GetComponent_s(EID); - if (!transformComponent) - continue; - - // Skip invalid bodies (Should not occur) - if (!rigidBodyComponent.rigidBody) - continue; - - // Skip inactive bodies - const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(EID); - if (!IS_ACTIVE) - continue; - - const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); - - // Skip objects that have not moved - if (!MOTION_STATE) - continue; - - if (rigidBodyComponent.IsInterpolating()) + const auto& RIGIDBODY_DENSE = SHComponentManager::GetDense(); + for (auto& rigidBodyComponent : RIGIDBODY_DENSE) { - const SHVec3 RENDER_POSITION = MOTION_STATE.InterpolatePositions(FACTOR); - const SHQuaternion RENDER_ORIENTATION = MOTION_STATE.InterpolateOrientations(FACTOR); + const EntityID EID = rigidBodyComponent.GetEID(); - transformComponent->SetWorldPosition(RENDER_POSITION); - transformComponent->SetWorldOrientation(RENDER_ORIENTATION); - } - else - { - transformComponent->SetWorldPosition(MOTION_STATE.position); - transformComponent->SetWorldOrientation(MOTION_STATE.orientation); - } + // Skip missing transforms + auto* transformComponent = SHComponentManager::GetComponent_s(EID); + if (!transformComponent) + continue; - /* - * TODO: Test if the scene graph transforms abides by setting world position. Collisions will ignore the scene graph hierarchy. - */ + // Skip invalid bodies (Should not occur) + if (!rigidBodyComponent.rigidBody) + continue; + + // Skip inactive bodies + const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(EID); + if (!IS_ACTIVE) + continue; + + const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); + + // Skip objects that have not moved + if (!MOTION_STATE) + continue; + + if (rigidBodyComponent.IsInterpolating()) + { + const SHVec3 RENDER_POSITION = MOTION_STATE.InterpolatePositions(FACTOR); + const SHQuaternion RENDER_ORIENTATION = MOTION_STATE.InterpolateOrientations(FACTOR); + + transformComponent->SetWorldPosition(RENDER_POSITION); + transformComponent->SetWorldOrientation(RENDER_ORIENTATION); + } + else + { + transformComponent->SetWorldPosition(MOTION_STATE.position); + transformComponent->SetWorldOrientation(MOTION_STATE.orientation); + } + + /* + * TODO: Test if the scene graph transforms abides by setting world position. Collisions will ignore the scene graph hierarchy. + */ + } } + + // Collision & Trigger messages if (scriptingSystem != nullptr) scriptingSystem->ExecuteCollisionFunctions(); From 19bffc91247f92774083390d1e318179972cadb8 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 16 Jan 2023 02:44:27 +0800 Subject: [PATCH 112/164] First half of re-implementing face-face contact derivation --- Assets/Scenes/PhysicsSandbox.shade | 32 ++-- SHADE_Engine/src/Math/SHMathHelpers.h | 2 +- .../Physics/Collision/Contacts/SHContact.h | 12 +- .../Collision/Narrowphase/SHCollision.h | 40 ++--- .../Narrowphase/SHConvexVsConvex.cpp | 149 ++++++++++++++++-- .../src/Physics/Dynamics/SHContactSolver.cpp | 52 +++--- SHADE_Engine/src/Physics/SHPhysicsConstants.h | 2 +- 7 files changed, 202 insertions(+), 87 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 6d15850b..c9104ab3 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,7 +45,7 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 0, z: 7} + Position: {x: 0, y: 2, z: 7} Pitch: 0 Yaw: 0 Roll: 0 @@ -62,14 +62,14 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.45715916, y: 7.37748241, z: 0.227711335} - Rotate: {x: -0, y: 0, z: -0} + Translate: {x: -1.5, y: 7.5, z: 0} + Rotate: {x: -0, y: 0, z: 0.785398185} Scale: {x: 1, y: 1, z: 1} IsActive: true RigidBody Component: Type: Dynamic Auto Mass: false - Mass: 10 + Mass: 0.52359879 Drag: 0.00999999978 Angular Drag: 0 Use Gravity: true @@ -84,7 +84,7 @@ Freeze Rotation Z: false IsActive: true Collider Component: - DrawColliders: true + DrawColliders: false Colliders: - Is Trigger: false Collision Tag: 1 @@ -96,18 +96,14 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true - Scripts: - - Type: PhysicsTestObj - Enabled: true - forceAmount: 50 - torqueAmount: 500 + Scripts: ~ - EID: 2 Name: Default IsActive: true NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 4.09544182, z: 0} + Translate: {x: 0, y: 4, z: 0} Rotate: {x: -0, y: 0, z: -0.436332315} Scale: {x: 4.61071014, y: 0.999995887, z: 1} IsActive: true @@ -272,17 +268,17 @@ Components: Transform Component: Translate: {x: 1, y: 2, z: 3} - Rotate: {x: -0, y: 0.785398066, z: 0.785398185} - Scale: {x: 0.999999821, y: 0.999999821, z: 0.999999881} + Rotate: {x: 0, y: 0.785398185, z: 0.785397708} + Scale: {x: 0.999990404, y: 0.999994516, z: 0.999985456} IsActive: true RigidBody Component: Type: Dynamic Auto Mass: false Mass: 1 Drag: 0.00999999978 - Angular Drag: 0.00999999978 + Angular Drag: 0 Use Gravity: true - Gravity Scale: 0.5 + Gravity Scale: 1 Interpolate: true Sleeping Enabled: true Freeze Position X: false @@ -305,7 +301,11 @@ Position Offset: {x: 0, y: 0, z: 0} Rotation Offset: {x: 0, y: 0, z: 0} IsActive: true - Scripts: ~ + Scripts: + - Type: PhysicsTestObj + Enabled: true + forceAmount: 50 + torqueAmount: 5 - EID: 8 Name: Default IsActive: true diff --git a/SHADE_Engine/src/Math/SHMathHelpers.h b/SHADE_Engine/src/Math/SHMathHelpers.h index b053beff..1d65eb91 100644 --- a/SHADE_Engine/src/Math/SHMathHelpers.h +++ b/SHADE_Engine/src/Math/SHMathHelpers.h @@ -46,7 +46,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /** Standard Epsilon value for comparing Single-Precision Floating-Point values. */ - static constexpr float EPSILON = 0.001f; + static constexpr float EPSILON = 0.0001f; /** Single-Precision Floating-Point value of infinity */ static constexpr float INF = std::numeric_limits::infinity(); diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h index 0337eedc..bacf2255 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h @@ -27,20 +27,16 @@ namespace SHADE union SHContactFeatures { public: - /*---------------------------------------------------------------------------------*/ - /* Type Definit */ - /*---------------------------------------------------------------------------------*/ - /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ struct { - uint8_t inI; - uint8_t outI; - uint8_t inR; - uint8_t outR; + uint8_t inI; // Incoming Incident Edge + uint8_t outI; // Outgoing Incident Edge + uint8_t inR; // Incoming Reference Edge + uint8_t outR; // Outgoing Reference Edge }; uint32_t key = std::numeric_limits::max(); diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 2a4503ce..8012392b 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -11,6 +11,7 @@ #pragma once // Project Headers +#include "Math/Geometry/SHPlane.h" #include "Physics/Collision/Contacts/SHManifold.h" #include "Physics/Collision/Contacts/SHCollisionKey.h" #include "Physics/Collision/Shapes/SHHalfEdgeStructure.h" @@ -86,15 +87,21 @@ namespace SHADE float bestDistance = std::numeric_limits::lowest(); }; + struct ClipVertex + { + SHVec3 position; + SHContactFeatures featurePair; + }; + /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ // Sphere VS Convex - static FaceQuery findClosestFace (const SHSphere& sphere, const SHConvexPolyhedron& polyhedron) noexcept; - static int32_t findClosestPoint (const SHSphere& sphere, const SHConvexPolyhedron& polyhedron, int32_t faceIndex) noexcept; - static int32_t findVoronoiRegion (const SHSphere& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; + static FaceQuery findClosestFace (const SHSphere& sphere, const SHConvexPolyhedron& polyhedron) noexcept; + static int32_t findClosestPoint (const SHSphere& sphere, const SHConvexPolyhedron& polyhedron, int32_t faceIndex) noexcept; + static int32_t findVoronoiRegion (const SHSphere& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; // Capsule VS Convex @@ -102,29 +109,22 @@ namespace SHADE // Convex VS Convex - static FaceQuery queryFaceDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; - static EdgeQuery queryEdgeDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; - - static bool buildMinkowskiFace (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; - static float distanceBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static int32_t findIncidentFace (const SHConvexPolyhedron& poly, const SHVec3& normal) noexcept; - /* - * TODO: - * - * - * - * - * - * static uint32_t clip [sutherland-hodgemann clipping] - * * ! References * https://ia801303.us.archive.org/30/items/GDC2013Gregorius/GDC2013-Gregorius.pdf * https://github.com/RandyGaul/qu3e/blob/master/src/collision/q3Collide.cpp */ + static FaceQuery queryFaceDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + static EdgeQuery queryEdgeDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + + static bool buildMinkowskiFace (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; + static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; + static float distanceBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; + static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; + static int32_t findIncidentFace (const SHConvexPolyhedron& poly, const SHVec3& normal) noexcept; + static std::vector clipPolygonWithPlane (const std::vector& in, int32_t numIn, const SHPlane& plane, int32_t planeIdx) noexcept; + static std::vector reduceContacts (const std::vector& in) noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index 97e9dbd4..52cc2c81 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -54,6 +54,8 @@ namespace SHADE const SHConvexPolyhedron& POLY_A = dynamic_cast(A); const SHConvexPolyhedron& POLY_B = dynamic_cast(B); + // TODO: Check against cached separating axis. + const FaceQuery FACE_QUERY_A = queryFaceDirections(POLY_A, POLY_B); if (FACE_QUERY_A.bestDistance > 0.0f) return false; @@ -78,6 +80,7 @@ namespace SHADE minFaceQuery = FACE_QUERY_A; referencePoly = &POLY_A; incidentPoly = &POLY_B; + flipNormal = false; } else { @@ -114,7 +117,7 @@ namespace SHADE SHContact contact; contact.featurePair.key = EDGE_QUERY.axis; contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); - contact.penetration = EDGE_QUERY.bestDistance; + contact.penetration = -EDGE_QUERY.bestDistance; manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; @@ -125,23 +128,67 @@ namespace SHADE const SHVec3 REFERENCE_NORMAL = referencePoly->GetNormal(minFaceQuery.closestFace); const int32_t INCIDENT_FACE_IDX = findIncidentFace(*incidentPoly, REFERENCE_NORMAL); + const SHHalfEdgeStructure::Face& INCIDENT_FACE = incidentPoly->GetFace(INCIDENT_FACE_IDX); + const SHHalfEdgeStructure::Face& REFERENCE_FACE = referencePoly->GetFace(minFaceQuery.closestFace); - /* - * TODO: - * - * !! - * 4. From above, save the axis of minimum penetration (reference face) - * 5. Find the most anti-parallel face on other shape (incident face) - * 6. Clip incident face against side planes of reference face. (Sutherland-Hodgeman Clipping). - * Keep all vertices below reference face. - * 7. Reduce manifold to 4 contact points. We only need 4 contact points for a stable manifold. - * - * Remember to save IDs in queries. - * During generation of incident face, store IDs of face. - * - */ + const int32_t NUM_INCIDENT_VERTICES = static_cast(INCIDENT_FACE.vertexIndices.size()); + const int32_t NUM_REFERENCE_VERTICES = static_cast(REFERENCE_FACE.vertexIndices.size()); + // Build incoming vertices to clip + std::vector clipIn; + clipIn.resize(NUM_INCIDENT_VERTICES * 2, ClipVertex{}); + int32_t numClipIn = 0; + for (int32_t i = 0; i < NUM_INCIDENT_VERTICES; ++i) + { + const int32_t prevI = i - 1 < 0 ? NUM_INCIDENT_VERTICES - 1 : i - 1; + + const int32_t V_INDEX = INCIDENT_FACE.vertexIndices[i].index; + + // The incoming id is the previous edge + // The outgoing id is the current edge (where this vertex is the tail of) + + ClipVertex v; + v.position = incidentPoly->GetVertex(V_INDEX); + v.featurePair.inI = INCIDENT_FACE.vertexIndices[prevI].edgeIndex; + v.featurePair.outI = INCIDENT_FACE.vertexIndices[i].edgeIndex; + v.featurePair.inR = 0; + v.featurePair.outR = 0; + + clipIn[numClipIn++] = v; + } + + // Clip the vertices against the reference face side planes. + // Number of side planes == number of edges == number of vertices + for (int32_t i = 0; i < NUM_REFERENCE_VERTICES; ++i) + { + // Side plane can be built with the vertex on the edge and the plane's normal + // Plane normal = faceNormal X tangent (v2 - v1) + + const int32_t V1_INDEX = REFERENCE_FACE.vertexIndices[i].index; + const int32_t V2_INDEX = REFERENCE_FACE.vertexIndices[(i + 1) % NUM_REFERENCE_VERTICES].index; + + const SHVec3 V1 = referencePoly->GetVertex(V1_INDEX); + const SHVec3 V2 = referencePoly->GetVertex(V2_INDEX); + + const SHVec3 TANGENT = SHVec3::Normalise(V2 - V1); + const SHPlane CLIP_PLANE { V1, SHVec3::Cross(REFERENCE_NORMAL, TANGENT) }; + + std::vector clipOut = clipPolygonWithPlane(clipIn, numClipIn, CLIP_PLANE, REFERENCE_FACE.vertexIndices[i].edgeIndex); + if (clipOut.empty()) + return false; + + // Replace the clip container's contents with the clipped points for the next clipping pass + const int32_t NUM_CLIPPED = static_cast(clipOut.size()); + for (int32_t clippedIndex = 0; clippedIndex < NUM_CLIPPED; ++clippedIndex) + { + clipIn[clippedIndex].position = clipOut[clippedIndex].position; + clipIn[clippedIndex].featurePair.key = clipOut[clippedIndex].featurePair.key; + } + numClipIn = NUM_CLIPPED; + } + + // From the final set of clipped points, only keep the points that are below the reference plane. return false; } @@ -353,5 +400,77 @@ namespace SHADE return bestFace; } + std::vector SHCollision::clipPolygonWithPlane(const std::vector& in, int32_t numIn, const SHPlane& plane, int32_t planeIdx) noexcept + { + std::vector out; + + int32_t v1Index = numIn - 1; + int32_t v2Index = 0; + + float v1Distance = plane.SignedDistance(in[v1Index].position); + float v2Distance = 0.0f; + + for (int32_t i = 0; i < numIn; ++i) + { + v2Index = i; + + const SHVec3 v1Pos = in[v1Index].position; + const SHVec3 v2Pos = in[v2Index].position; + + v2Distance = plane.SignedDistance(v2Pos); + + // v1 in front, v2 behind + // keep the intersection point + if (v1Distance >= 0.0f && v2Distance < 0.0f) + { + ClipVertex intersection; + + // In case the edge is parallel, the intersection is just the start point + const bool IS_PARALLEL = SHMath::CompareFloat(SHVec3::Dot(v2Pos - v1Pos, plane.GetNormal()), 0.0f); + const float ALPHA = IS_PARALLEL ? 0.0f : v1Distance / SHVec3::Distance(v1Pos, v2Pos); + + intersection.position = SHVec3::ClampedLerp(v1Pos, v2Pos, ALPHA); + intersection.featurePair.inI = in[v1Index].featurePair.outI; + intersection.featurePair.outI = 0; + intersection.featurePair.inR = 0; + intersection.featurePair.outR = planeIdx; + + out.emplace_back(intersection); + } + + // v1 behind, v2 in front + // keep intersection point & v2 + if(v1Distance < 0.0f && v2Distance >= 0.0f) + { + ClipVertex intersection; + + // In case the edge is parallel, the intersection is just the start point + const bool IS_PARALLEL = SHMath::CompareFloat(SHVec3::Dot(v2Pos - v1Pos, plane.GetNormal()), 0.0f); + const float ALPHA = IS_PARALLEL ? 0.0f : -v1Distance / SHVec3::Distance(v1Pos, v2Pos); + + intersection.position = SHVec3::ClampedLerp(v1Pos, v2Pos, ALPHA); + intersection.featurePair.inI = 0; + intersection.featurePair.outI = in[v2Index].featurePair.inI; + intersection.featurePair.inR = planeIdx; + intersection.featurePair.outR = 0; + + out.emplace_back(intersection); + out.emplace_back(in[v2Index]); + } + + // both in front, keep v2 + if (v1Distance >= 0.0f && v2Distance >= 0.0f) + { + out.emplace_back(in[v2Index]); + } + + // v2 is the next v1 + v1Index = v2Index; + v1Distance = v2Distance; + } + + return out; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp index b5d7c2cc..926e1fed 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp @@ -187,7 +187,7 @@ namespace SHADE * restituion bias = restitution * (relative velocity /dot normal) */ - const float ERROR_BIAS = SHPHYSICS_BAUMGARTE * INV_DT * std::min(0.0f, -contact.penetration + SHPHYSICS_LINEAR_SLOP); + const float ERROR_BIAS = -SHPHYSICS_BAUMGARTE * INV_DT * std::max(0.0f, contact.penetration - SHPHYSICS_LINEAR_SLOP); // Warm starting // Compute impulses @@ -244,6 +244,31 @@ namespace SHADE SHVec3 velocityB = vB + SHVec3::Cross(wB, contact.rB); SHVec3 relativeVelocity = velocityB - velocityA; + // Get scalar of relative velocity along the normal + const float VN = SHVec3::Dot(relativeVelocity, constraint.normal); + + // Compute true normal impulse + const float OLD_NORMAL_IMPULSE = contact.normalImpulse; + + float newNormalImpulse = -(VN + contact.bias) * contact.normalMass; + contact.normalImpulse = std::max(OLD_NORMAL_IMPULSE + newNormalImpulse, 0.0f); + newNormalImpulse = contact.normalImpulse - OLD_NORMAL_IMPULSE; + + const SHVec3 NORMAL_IMPULSE = newNormalImpulse * constraint.normal; + + // Apply impulses + vA -= NORMAL_IMPULSE * constraint.invMassA * LINEAR_LOCK_A; + wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, NORMAL_IMPULSE) * ANGULAR_LOCK_A; + + vB += NORMAL_IMPULSE * constraint.invMassB * LINEAR_LOCK_B; + wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE) * ANGULAR_LOCK_B; + + // Solve normal impulse + // Re-compute relative velocity + velocityA = vA + SHVec3::Cross(wA, contact.rA); + velocityB = vB + SHVec3::Cross(wB, contact.rB); + relativeVelocity = velocityB - velocityA; + // Solve tangent impulse for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) { @@ -269,31 +294,6 @@ namespace SHADE vB += TANGENT_IMPULSE * constraint.invMassB * LINEAR_LOCK_B; wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, TANGENT_IMPULSE) * ANGULAR_LOCK_B; } - - // Solve normal impulse - // Re-compute relative velocity - velocityA = vA + SHVec3::Cross(wA, contact.rA); - velocityB = vB + SHVec3::Cross(wB, contact.rB); - relativeVelocity = velocityB - velocityA; - - // Get scalar of relative velocity along the normal - const float VN = SHVec3::Dot(relativeVelocity, constraint.normal); - - // Compute true normal impulse - const float OLD_NORMAL_IMPULSE = contact.normalImpulse; - - float newNormalImpulse = -(VN + contact.bias) * contact.normalMass; - contact.normalImpulse = std::max(OLD_NORMAL_IMPULSE + newNormalImpulse, 0.0f); - newNormalImpulse = contact.normalImpulse - OLD_NORMAL_IMPULSE; - - const SHVec3 NORMAL_IMPULSE = newNormalImpulse * constraint.normal; - - // Apply impulses - vA -= NORMAL_IMPULSE * constraint.invMassA * LINEAR_LOCK_A; - wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, NORMAL_IMPULSE) * ANGULAR_LOCK_A; - - vB += NORMAL_IMPULSE * constraint.invMassB * LINEAR_LOCK_B; - wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE) * ANGULAR_LOCK_B; } velocityStateA.LinearVelocity = vA; diff --git a/SHADE_Engine/src/Physics/SHPhysicsConstants.h b/SHADE_Engine/src/Physics/SHPhysicsConstants.h index a6cbd608..f252fce5 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsConstants.h +++ b/SHADE_Engine/src/Physics/SHPhysicsConstants.h @@ -26,7 +26,7 @@ namespace SHADE * @brief * Linear Collision & Constraint tolerance. */ - static constexpr float SHPHYSICS_LINEAR_SLOP = 0.05f * SHPHYSICS_LENGTHS_PER_UNIT_METER; + static constexpr float SHPHYSICS_LINEAR_SLOP = 0.01f * SHPHYSICS_LENGTHS_PER_UNIT_METER; /** * @brief From 85f0902c2d7282baae7e7e6935223c6cd626ea46 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 16 Jan 2023 03:48:08 +0800 Subject: [PATCH 113/164] Removed unused collision table and fixed bug with kinematic bodies exploding --- .../Narrowphase/SHCollisionDispatch.cpp | 21 ------------- .../Narrowphase/SHCollisionDispatch.h | 31 ------------------- .../src/Physics/Dynamics/SHRigidBody.cpp | 2 +- 3 files changed, 1 insertion(+), 53 deletions(-) diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp index e11902e0..075e113f 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp @@ -42,31 +42,10 @@ namespace SHADE , { SHCollision::CapsuleVsSphere, SHCollision::CapsuleVsConvex, SHCollision::CapsuleVsCapsule } // Capsule }; - const bool SHCollisionDispatcher::collisionTable[NUM_TYPES][NUM_TYPES] - { - /* S ST K KT D DT */ - /* S */ { false, false, false, true, true, true } - , /* ST */ { false, false, true, true, true, true } - , /* K */ { false, true, false, true, true, true } - , /* KT */ { true, true, true, true, true, true } - , /* D */ { true, true, true, true, true, true } - , /* DT */ { true, true, true, true, true, true } - }; - /*-----------------------------------------------------------------------------------*/ /* Public Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - bool SHCollisionDispatcher::ShouldCollide(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - // Filter through collision table - const int TYPE_A = SHUtilities::ConvertEnum(A.GetType()) + A.IsTrigger() ? TYPE_OFFSET : 0; - const int TYPE_B = SHUtilities::ConvertEnum(B.GetType()) + B.IsTrigger() ? TYPE_OFFSET : 0; - - if (!collisionTable[TYPE_A][TYPE_B]) - return false; - } - bool SHCollisionDispatcher::Collide(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { const int TYPE_A = SHUtilities::ConvertEnum(A.GetType()); diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h index 2dea7f1d..1df84da0 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h @@ -31,18 +31,6 @@ namespace SHADE /* Member Functions */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Filters the collision through the collision table and layer matching. - * @param A - * A Collision Shape. - * @param B - * A Collision Shape. - * @return - * True if both shapes should be tested for collision. - */ - static bool ShouldCollide (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - static bool Collide (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; static bool Collide (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; @@ -54,33 +42,14 @@ namespace SHADE using ManifoldCollide = bool(*)(SHManifold&, const SHCollisionShape& A, const SHCollisionShape& B); using TriggerCollide = bool(*)(const SHCollisionShape& A, const SHCollisionShape& B); - enum class Types - { - STATIC - , KINEMATIC - , DYNAMIC - , STATIC_TRIGGER - , KINEMATIC_TRIGGER - , DYNAMIC_TRIGGER - - , COUNT - }; - /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - // Read the Types enum class, then see where it's used and it'll make sense - static constexpr int TYPE_OFFSET = 3; - static constexpr int NUM_SHAPES = static_cast(SHCollisionShape::Type::COUNT); - static constexpr int NUM_TYPES = static_cast(Types::COUNT); static const ManifoldCollide manifoldCollide [NUM_SHAPES][NUM_SHAPES]; static const TriggerCollide triggerCollide [NUM_SHAPES][NUM_SHAPES]; - - static const bool collisionTable [NUM_TYPES][NUM_TYPES]; - }; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp index 1de9e543..bdaaf230 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp @@ -643,7 +643,7 @@ namespace SHADE // Compute world centroid worldCentroid = (R * localCentroid) + motionState.position; - if (bodyType == Type::STATIC) + if (bodyType != Type::DYNAMIC) return; // Compute world inertia From c5998c3b53b264bc2e159310278e4905f55092e3 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Mon, 16 Jan 2023 10:32:18 +0800 Subject: [PATCH 114/164] Laying out foundations for input editor UI --- .../InputBindings/SHInputBindingsPanel.cpp | 15 +++++++++++++++ .../InputBindings/SHInputBindingsPanel.h | 15 +++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp create mode 100644 SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.h diff --git a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp new file mode 100644 index 00000000..f05440dc --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp @@ -0,0 +1,15 @@ +#include "SHpch.h" +#include "SHInputBindingsPanel.h" +#include "Input/SHInputManager.h" +#include "Editor/SHEditorWidgets.hpp" + +namespace SHADE +{ + void SHInputBindingsPanel::Update() + { + if (Begin()) + { + + } + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.h b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.h new file mode 100644 index 00000000..c29a68c5 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Editor/EditorWindow/SHEditorWindow.h" +#include + +namespace SHADE +{ + class SH_API SHInputBindingsPanel final : public SHEditorWindow + { + public: + SHInputBindingsPanel() : SHEditorWindow("Input Bindings Panel", ImGuiWindowFlags_MenuBar) {} + + void Update() override; + }; +} \ No newline at end of file From 7f9991038a673379a08b41b50fa0668f094c2846 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Mon, 16 Jan 2023 14:34:59 +0800 Subject: [PATCH 115/164] Working commit --- Assets/Models/racoon.shmodel | Bin 603308 -> 679960 bytes .../Libraries/Loaders/SHModelLoader.cpp | 28 ++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/Assets/Models/racoon.shmodel b/Assets/Models/racoon.shmodel index 95cb9f3fd35b3b2b80eec3c4263239e6645144f2..b25e822367cd5769704b821a2c3e356b0adb9f59 100644 GIT binary patch delta 77307 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.VertexNormals.data()), vertexVec3Byte); file.read(reinterpret_cast(data.VertexTexCoords.data()), vertexVec2Byte); file.read(reinterpret_cast(data.Indices.data()), sizeof(uint32_t) * header.indexCount); + + std::vector boneInfos(header.boneCount); + std::vector bones(header.boneCount); + + file.read(reinterpret_cast(boneInfos.data()), sizeof(MeshBoneInfo) * header.boneCount); + + for (auto i{ 0 }; i < header.boneCount; ++i) + { + auto& bone = bones[i]; + auto const& info = boneInfos[i]; + + bone.name.resize(info.charCount); + file.read(bone.name.data(), info.charCount); + file.read(reinterpret_cast(&bone.offset), sizeof(SHMatrix)); + + uint32_t weightCount; + file.read(reinterpret_cast(&weightCount), sizeof(uint32_t)); + bone.weights.resize(weightCount); + file.read(reinterpret_cast(bone.weights.data()), sizeof(BoneWeight) * weightCount); + } + + data.VertexBoneIndices.resize(header.vertexCount); + data.VertexBoneWeights.resize(header.vertexCount); + + for (auto const& bone : bones) + { + + } meshes[i] = &data; } From c077575a738eeab8b4a5b2e2ab7f845af81e8adf Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 16 Jan 2023 15:01:14 +0800 Subject: [PATCH 116/164] Fixed convex-convex face detection Minor bugs with contact point detection. Will test more before pushing into main --- Assets/Scenes/PhysicsSandbox.shade | 6 +- .../Collision/Narrowphase/SHCollision.h | 2 +- .../Narrowphase/SHConvexVsConvex.cpp | 208 +++++++++++++++++- SHADE_Engine/src/Physics/SHPhysicsConstants.h | 12 +- 4 files changed, 220 insertions(+), 8 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index c9104ab3..0d2e4abe 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -267,9 +267,9 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 1, y: 2, z: 3} - Rotate: {x: 0, y: 0.785398185, z: 0.785397708} - Scale: {x: 0.999990404, y: 0.999994516, z: 0.999985456} + Translate: {x: 1.81218028, y: 2, z: 3} + Rotate: {x: 0.785398006, y: 0.785398483, z: 0.785398304} + Scale: {x: 0.999990404, y: 0.999994457, z: 0.999985337} IsActive: true RigidBody Component: Type: Dynamic diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 8012392b..1ef75974 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -124,7 +124,7 @@ namespace SHADE static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; static int32_t findIncidentFace (const SHConvexPolyhedron& poly, const SHVec3& normal) noexcept; static std::vector clipPolygonWithPlane (const std::vector& in, int32_t numIn, const SHPlane& plane, int32_t planeIdx) noexcept; - static std::vector reduceContacts (const std::vector& in) noexcept; + static std::vector reduceContacts (const std::vector& in, const SHVec3& faceNormal) noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index 52cc2c81..a59e8f28 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -189,8 +189,78 @@ namespace SHADE } // From the final set of clipped points, only keep the points that are below the reference plane. + const SHPlane REFERENCE_PLANE{ referencePoly->GetVertex(REFERENCE_FACE.vertexIndices.front().index), REFERENCE_NORMAL }; - return false; + std::vector contacts; + for (int32_t i = 0; i < numClipIn; ++i) + { + const SHVec3 POS = clipIn[i].position; + const float DIST = REFERENCE_PLANE.SignedDistance(POS); + if (DIST <= 0.0f) + { + SHContact contact; + contact.position = POS; + contact.penetration = -DIST; + + if (flipNormal) + { + contact.featurePair.inI = clipIn[i].featurePair.inR; + contact.featurePair.inR = clipIn[i].featurePair.inI; + contact.featurePair.outI = clipIn[i].featurePair.outR; + contact.featurePair.outR = clipIn[i].featurePair.outI; + } + else + { + contact.featurePair.key = clipIn[i].featurePair.key; + } + + contacts.emplace_back(contact); + ++numContacts; + } + } + + // Reduce contact manifold if more than 4 points + if (numContacts > 4) + { + const auto INDICES_TO_KEEP = reduceContacts(contacts, REFERENCE_NORMAL); + std::vector reducedContacts; + + const int32_t NUM_REDUCED = static_cast(INDICES_TO_KEEP.size()); + for (int32_t i = 0; i < NUM_REDUCED; ++i) + reducedContacts.emplace_back(contacts[INDICES_TO_KEEP[i]]); + + contacts.clear(); + // Copy contacts to main container + for (auto& contact : reducedContacts) + contacts.emplace_back(contact); + } + + // Remove potential duplicate contact points + // No way about this being an n^2 loop + static constexpr float THRESHOLD = SHPHYSICS_SAME_CONTACT_DISTANCE * SHPHYSICS_SAME_CONTACT_DISTANCE; + for (auto i = contacts.begin(); i != contacts.end(); ++i) + { + for (auto j = i + 1; j != contacts.end();) + { + const float D2 = SHVec3::DistanceSquared(i->position, j->position); + if (D2 < THRESHOLD) + j = contacts.erase(j); + else + ++j; + } + } + + // Copy final contacts into the manifold + numContacts = static_cast(contacts.size()); + for (int32_t i = 0; i < numContacts; ++i) + manifold.contacts[i] = contacts[i]; + + manifold.numContacts = numContacts; + manifold.normal = REFERENCE_NORMAL; + if (flipNormal) + manifold.normal = -manifold.normal; + + return true; } /*-----------------------------------------------------------------------------------*/ @@ -472,5 +542,141 @@ namespace SHADE return out; } + std::vector SHCollision::reduceContacts(const std::vector& in, const SHVec3& faceNormal) noexcept + { + std::vector indicesToKeep; + + // Use a map to temporarily store and track the contacts we want + std::unordered_map contactMap; + const int32_t NUM_CONTACTS = static_cast(in.size()); + for (int32_t i = 0; i < NUM_CONTACTS; ++i) + contactMap.emplace(i, &in[i]); + + // Find the furthest point in a given direction + int32_t indexToKeep = -1; + float bestDistance = std::numeric_limits::lowest(); + + for (const auto& [index, contact] : contactMap) + { + const float DIST = SHVec3::Dot(contact->position, SHVec3::One); + if (DIST > bestDistance) + { + bestDistance = DIST; + indexToKeep = index; + } + } + + indicesToKeep.emplace_back(indexToKeep); + contactMap.erase(indexToKeep); + + + indexToKeep = -1; + bestDistance = std::numeric_limits::lowest(); + + // Find point furthest away from the first index + const SHVec3& FIRST_POS = in[indicesToKeep.back()].position; + for (const auto& [index, contact] : contactMap) + { + const float DIST_SQUARED = SHVec3::DistanceSquared(FIRST_POS, contact->position); + if (DIST_SQUARED > bestDistance) + { + bestDistance = DIST_SQUARED; + indexToKeep = index; + } + } + + indicesToKeep.emplace_back(indexToKeep); + contactMap.erase(indexToKeep); + + indexToKeep = -1; + + // We compute the triangle with the largest area. + // The area can be positive or negative depending on the winding order. + + float maxArea = std::numeric_limits::lowest(); + float minArea = std::numeric_limits::max(); + + int32_t maxAreaIndex = -1; + int32_t minAreaIndex = -1; + + const SHVec3& SECOND_POS = in[indicesToKeep.back()].position; + for (const auto& [index, contact] : contactMap) + { + const SHVec3& POS = contact->position; + const SHVec3 TO_P1 = FIRST_POS - POS; + const SHVec3 TO_P2 = SECOND_POS - POS; + + const float AREA = SHVec3::Dot(SHVec3::Cross(TO_P1, TO_P2), faceNormal) * 0.5f; + + if (AREA > maxArea) + { + maxArea = AREA; + maxAreaIndex = index; + } + + if (AREA < minArea) + { + minArea = AREA; + minAreaIndex = index; + } + } + + // Compare which triangle creates the largest area + bool isAreaPositive = false; + if (maxArea > (-minArea)) + { + isAreaPositive = true; + indexToKeep = maxAreaIndex; + } + else + { + isAreaPositive = false; + indexToKeep = minAreaIndex; + } + + indicesToKeep.emplace_back(indexToKeep); + contactMap.erase(indexToKeep); + + indexToKeep = -1; + + // For the last point, we want the point which forms the largest area that is winded opposite to the first triangle + // The areas should be inverted: If area was -ve, we want a +ve. Otherwise, vice versa. + float bestArea = 0.0f; + + const SHVec3& THIRD_POS = in[indicesToKeep.back()].position; + const SHVec3 ABC[3] = { FIRST_POS, SECOND_POS, THIRD_POS }; + + for (const auto& [index, contact] : contactMap) + { + const SHVec3& Q = contact->position; + + for (int i = 0; i < 3; ++i) + { + const int P1 = i; + const int P2 = (i + 1) % 3; + + const SHVec3 TO_P1 = ABC[P1] - Q; + const SHVec3 TO_P2 = ABC[P2] - Q; + + const float AREA = SHVec3::Dot(SHVec3::Cross(TO_P1, TO_P2), faceNormal) * 0.5f; + + if (isAreaPositive && AREA < bestArea) + { + bestArea = AREA; + indexToKeep = index; + } + + if (!isAreaPositive && AREA > bestArea) + { + bestArea = AREA; + indexToKeep = index; + } + } + } + + indicesToKeep.emplace_back(indexToKeep); + return indicesToKeep; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsConstants.h b/SHADE_Engine/src/Physics/SHPhysicsConstants.h index f252fce5..b680515e 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsConstants.h +++ b/SHADE_Engine/src/Physics/SHPhysicsConstants.h @@ -34,11 +34,17 @@ namespace SHADE */ static constexpr float SHPHYSICS_RESTITUTION_THRESHOLD = 1.0f; - /** + /** * @brief * Scaling factor to control how fast overlaps are resolved.
* 1 is ideal for instant correction, but values close to 1 can lead to overshoot. - */ - static constexpr float SHPHYSICS_BAUMGARTE = 0.2f; + */ + static constexpr float SHPHYSICS_BAUMGARTE = 0.2f; + + /** + * @brief + * Distance threshold to consider two contacts as the same. + */ + static constexpr float SHPHYSICS_SAME_CONTACT_DISTANCE = 0.01; } From 50899574684db3e5085a29e4e1ef10b439fba08b Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Mon, 16 Jan 2023 16:12:43 +0800 Subject: [PATCH 117/164] Implemented bone weight reading into desired format. --- .../Assets/Asset Types/Models/SHMeshAsset.h | 21 ++++--- .../Libraries/Loaders/SHModelLoader.cpp | 59 ++++++++++++------- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h index c772f717..dee88cd0 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h @@ -18,6 +18,13 @@ namespace SHADE { + constexpr int BONE_INDEX_ALIGHTMENT = 4; + + struct IndexUInt4 + { + std::array indices; + }; + struct SHMeshDataHeader { uint32_t vertexCount; @@ -48,12 +55,12 @@ namespace SHADE struct SH_API SHMeshAsset : SHAssetData { std::string name; - std::vector VertexPositions; - std::vector VertexTangents; - std::vector VertexNormals; - std::vector VertexTexCoords; - std::vector Indices; - std::vector VertexBoneIndices; - std::vector VertexBoneWeights; + std::vector VertexPositions; + std::vector VertexTangents; + std::vector VertexNormals; + std::vector VertexTexCoords; + std::vector Indices; + std::vector VertexBoneIndices; + std::vector VertexBoneWeights; }; } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index b90205bc..649e6ffc 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -222,33 +222,50 @@ namespace SHADE file.read(reinterpret_cast(data.VertexNormals.data()), vertexVec3Byte); file.read(reinterpret_cast(data.VertexTexCoords.data()), vertexVec2Byte); file.read(reinterpret_cast(data.Indices.data()), sizeof(uint32_t) * header.indexCount); - - std::vector boneInfos(header.boneCount); - std::vector bones(header.boneCount); - file.read(reinterpret_cast(boneInfos.data()), sizeof(MeshBoneInfo) * header.boneCount); - - for (auto i{ 0 }; i < header.boneCount; ++i) + if (header.boneCount) { - auto& bone = bones[i]; - auto const& info = boneInfos[i]; + std::vector boneInfos(header.boneCount); + std::vector bones(header.boneCount); - bone.name.resize(info.charCount); - file.read(bone.name.data(), info.charCount); - file.read(reinterpret_cast(&bone.offset), sizeof(SHMatrix)); + file.read(reinterpret_cast(boneInfos.data()), sizeof(MeshBoneInfo) * header.boneCount); - uint32_t weightCount; - file.read(reinterpret_cast(&weightCount), sizeof(uint32_t)); - bone.weights.resize(weightCount); - file.read(reinterpret_cast(bone.weights.data()), sizeof(BoneWeight) * weightCount); - } + for (auto i{ 0 }; i < header.boneCount; ++i) + { + auto& bone = bones[i]; + auto const& info = boneInfos[i]; - data.VertexBoneIndices.resize(header.vertexCount); - data.VertexBoneWeights.resize(header.vertexCount); + bone.name.resize(info.charCount); + file.read(bone.name.data(), info.charCount); + file.read(reinterpret_cast(&bone.offset), sizeof(SHMatrix)); - for (auto const& bone : bones) - { - + uint32_t weightCount; + file.read(reinterpret_cast(&weightCount), sizeof(uint32_t)); + bone.weights.resize(weightCount); + file.read(reinterpret_cast(bone.weights.data()), sizeof(BoneWeight) * weightCount); + } + + data.VertexBoneIndices.resize(header.vertexCount); + data.VertexBoneWeights.resize(header.vertexCount); + + for (uint32_t boneIndex{0}; boneIndex < bones.size(); ++boneIndex) + { + auto const& bone = bones[boneIndex]; + for (auto const& weight : bone.weights) + { + auto& boneIndices = data.VertexBoneIndices[weight.index]; + auto& boneWeight = data.VertexBoneWeights[weight.index]; + + for (auto j{0}; j < BONE_INDEX_ALIGHTMENT; ++j) + { + if (boneWeight[j] == 0.f) + { + boneIndices.indices[j] = boneIndex; + boneWeight[j] = weight.weight; + } + } + } + } } meshes[i] = &data; From 36e01260ec16474ac956091eeb48704a508a2248 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 16 Jan 2023 16:23:48 +0800 Subject: [PATCH 118/164] Merge changes from main missing from previous commit --- Assets/Editor/Editor.SHConfig | 2 +- Assets/Scenes/PhysicsSandbox.shade | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Assets/Editor/Editor.SHConfig b/Assets/Editor/Editor.SHConfig index 51425027..b492a983 100644 --- a/Assets/Editor/Editor.SHConfig +++ b/Assets/Editor/Editor.SHConfig @@ -1,4 +1,4 @@ Start Maximized: true -Working Scene ID: 97161771 +Working Scene ID: 97402985 Window Size: {x: 1920, y: 1080} Style: 0 \ No newline at end of file diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 0d2e4abe..3144e959 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -62,7 +62,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.5, y: 7.5, z: 0} + Translate: {x: 3, y: 7.5, z: 0} Rotate: {x: -0, y: 0, z: 0.785398185} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -267,8 +267,8 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 1.81218028, y: 2, z: 3} - Rotate: {x: 0.785398006, y: 0.785398483, z: 0.785398304} + Translate: {x: -1, y: 7, z: 0} + Rotate: {x: 0.785398185, y: 0.785398185, z: 0.785398185} Scale: {x: 0.999990404, y: 0.999994457, z: 0.999985337} IsActive: true RigidBody Component: From 1fc2897150e74d0b2cc952edca73049564f7cd11 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 16 Jan 2023 16:30:15 +0800 Subject: [PATCH 119/164] Added SHVec4U and modified SHMesh to use SHVec4U instead of uint32_t --- .../Assets/Asset Types/Models/SHMeshAsset.h | 20 +++++++--------- .../Libraries/Loaders/SHModelLoader.cpp | 2 +- .../MiddleEnd/Interface/SHMeshLibrary.cpp | 4 ++-- .../MiddleEnd/Interface/SHMeshLibrary.h | 8 ++----- SHADE_Engine/src/Math/Vector/SHVec4U.h | 23 +++++++++++++++++++ 5 files changed, 36 insertions(+), 21 deletions(-) create mode 100644 SHADE_Engine/src/Math/Vector/SHVec4U.h diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h index dee88cd0..f73078a3 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h @@ -12,6 +12,7 @@ #include "Math/SHMath.h" #include "Assets/Asset Types/SHAssetData.h" +#include "Math/Vector/SHVec4U.h" #include #include @@ -20,11 +21,6 @@ namespace SHADE { constexpr int BONE_INDEX_ALIGHTMENT = 4; - struct IndexUInt4 - { - std::array indices; - }; - struct SHMeshDataHeader { uint32_t vertexCount; @@ -55,12 +51,12 @@ namespace SHADE struct SH_API SHMeshAsset : SHAssetData { std::string name; - std::vector VertexPositions; - std::vector VertexTangents; - std::vector VertexNormals; - std::vector VertexTexCoords; - std::vector Indices; - std::vector VertexBoneIndices; - std::vector VertexBoneWeights; + std::vector VertexPositions; + std::vector VertexTangents; + std::vector VertexNormals; + std::vector VertexTexCoords; + std::vector Indices; + std::vector VertexBoneIndices; + std::vector VertexBoneWeights; }; } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 649e6ffc..230df857 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -260,7 +260,7 @@ namespace SHADE { if (boneWeight[j] == 0.f) { - boneIndices.indices[j] = boneIndex; + boneIndices[j] = boneIndex; boneWeight[j] = weight.weight; } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp index b0ca7f14..658cc5fe 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp @@ -168,12 +168,12 @@ namespace SHADE vertBoneIdxStorage.insert ( vertBoneIdxStorage.end(), - addJob.VertexBoneIndices, addJob.VertexBoneIndices + addJob.VertexCount * SHMesh::BONE_INDICES_PER_VERTEX + addJob.VertexBoneIndices, addJob.VertexBoneIndices + addJob.VertexCount ); } else { - vertBoneIdxStorage.resize(vertBoneIdxStorage.size() + addJob.VertexCount * SHMesh::BONE_INDICES_PER_VERTEX); + vertBoneIdxStorage.resize(vertBoneIdxStorage.size() + addJob.VertexCount); } if (addJob.VertexBoneWeights) { diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h index 5e42c526..39b669b8 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h @@ -20,6 +20,7 @@ of DigiPen Institute of Technology is prohibited. #include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec3.h" #include "Math/Vector/SHVec4.h" +#include "Math/Vector/SHVec4U.h" namespace SHADE { @@ -50,14 +51,9 @@ namespace SHADE using VertexTexCoord = SHVec2; using VertexTangent = SHVec3; using VertexNormal = SHVec3; - using VertexBoneIndices = int; + using VertexBoneIndices = SHVec4U; using VertexWeights = SHVec4; - /*-----------------------------------------------------------------------------*/ - /* Constants */ - /*-----------------------------------------------------------------------------*/ - static constexpr size_t BONE_INDICES_PER_VERTEX = 4; - /*-----------------------------------------------------------------------------*/ /* Data Members */ /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/Vector/SHVec4U.h b/SHADE_Engine/src/Math/Vector/SHVec4U.h new file mode 100644 index 00000000..40eec94f --- /dev/null +++ b/SHADE_Engine/src/Math/Vector/SHVec4U.h @@ -0,0 +1,23 @@ +/************************************************************************************//*! +\file SHVec4U.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Jan 16, 2023 +\brief Contains type alias for SHVec4U. + +Copyright (C) 2023 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + /// + /// Simple type alias for a array of uint32_t of 4 uint32_ts. + /// + using SHVec4U = std::array; +} \ No newline at end of file From a668f38c87b4ba598a23197c650d8f15cd414bc7 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 16 Jan 2023 17:49:16 +0800 Subject: [PATCH 120/164] Fixed bone vertex data not correctly passed to the GPU --- .../MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp index 35e407e3..0cbd6c33 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp @@ -214,10 +214,10 @@ namespace SHADE defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_2D) }); // UVs at binding 1 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Normals at binding 2 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Tangents at binding 3 - defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Transform at binding 4 - 7 (4 slots) - defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::UINT32_2D) }); // Instanced integer data at index 8 - defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::UINT32_4D) }); // Instanced bone indices at index 9 - defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); // Instanced bone weights at index 10 + defaultVertexInputState.AddBinding(true , true , { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Transform at binding 4 - 7 (4 slots) + defaultVertexInputState.AddBinding(true , true , { SHVertexAttribute(SHAttribFormat::UINT32_2D) }); // Instanced integer data at index 8 + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::UINT32_4D) }); // Attribute bone indices at index 9 + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); // Attribute bone weights at index 10 } void SHGraphicsPredefinedData::Init(Handle logicalDevice) noexcept From bce7237e20a05b2067afcdc3463bf0edf2b5ff88 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 16 Jan 2023 21:02:37 +0800 Subject: [PATCH 121/164] Modified shader to support instanced bone first index and system to support runtime updating of animations --- Assets/Shaders/Anim_VS.glsl | 9 ++-- Assets/Shaders/Anim_VS.shshaderb | Bin 4341 -> 5837 bytes .../src/Application/SBApplication.cpp | 4 +- .../src/Animation/SHAnimationSystem.cpp | 22 +++++++- .../src/Animation/SHAnimationSystem.h | 17 +++++-- .../src/Animation/SHAnimatorComponent.h | 2 +- .../src/Animation/SHAnimatorComponent.hpp | 9 +++- .../Libraries/Loaders/SHModelLoader.cpp | 1 + .../Inspector/SHEditorComponentView.hpp | 2 +- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 47 ++++++++++++++++++ .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 2 + .../MiddleEnd/Batching/SHSuperBatch.cpp | 1 + .../GlobalData/SHGraphicsPredefinedData.cpp | 1 + .../MiddleEnd/Interface/SHGraphicsConstants.h | 7 +++ 14 files changed, 108 insertions(+), 16 deletions(-) diff --git a/Assets/Shaders/Anim_VS.glsl b/Assets/Shaders/Anim_VS.glsl index c9aa64aa..b553fdf1 100644 --- a/Assets/Shaders/Anim_VS.glsl +++ b/Assets/Shaders/Anim_VS.glsl @@ -12,6 +12,7 @@ layout(location = 4) in mat4 worldTransform; layout(location = 8) in uvec2 integerData; layout(location = 9) in uvec4 aBoneIndices; layout(location = 10) in vec4 aBoneWeights; +layout(location = 11) in uint firstBoneIndex; layout(location = 0) out struct { @@ -65,10 +66,10 @@ void main() Out.normal.rgb = normalize (Out.normal.rgb); // Compute bone matrix - mat4 boneMatrix = BoneMatrices.data[aBoneIndices[0]] * aBoneWeights[0]; - boneMatrix += BoneMatrices.data[aBoneIndices[1]] * aBoneWeights[1]; - boneMatrix += BoneMatrices.data[aBoneIndices[2]] * aBoneWeights[2]; - boneMatrix += BoneMatrices.data[aBoneIndices[3]] * aBoneWeights[3]; + mat4 boneMatrix = BoneMatrices.data[firstBoneIndex + aBoneIndices[0]] * aBoneWeights[0]; + boneMatrix += BoneMatrices.data[firstBoneIndex + aBoneIndices[1]] * aBoneWeights[1]; + boneMatrix += BoneMatrices.data[firstBoneIndex + aBoneIndices[2]] * aBoneWeights[2]; + boneMatrix += BoneMatrices.data[firstBoneIndex + aBoneIndices[3]] * aBoneWeights[3]; // clip space for rendering gl_Position = cameraData.vpMat * worldTransform * boneMatrix * vec4 (aVertexPos, 1.0f); diff --git a/Assets/Shaders/Anim_VS.shshaderb b/Assets/Shaders/Anim_VS.shshaderb index c10bdc5a8ad53cb47aeb2f3d9b375b1e1728ae88..0c71194940a697833a5595575c1fcae14556c0bf 100644 GIT binary patch literal 5837 zcmZ9O`I{V76^3iJ4kkbdgn$aMXMzrz7`G6kNT5k@hz`tPLQqkMV!ARZ?CBnQdL{`j z1os_+`;PmD`@SQ_xbOJC_>0fu`&QMN)RW3{a=-JP<(_-ay`2o5-jWpG$ndsxNivkI zPcBIQQ&P#MWEdt%HYAN=oti#4y>ofc-nr|V=jw7{GE#WzvoX22z@z-VnC4xT^~eRt zW@HEQd}KG$LJlH}$i2vM*2hUYN`|TFxv80%sp*^M=9iY{mIo&~*<2^@ zW^?VlpUn^Q)l9$hYJPclzO&MDUZEO;)>!TdgM zy)mWDzGBhoj|NJ^l`ACy6?er`+K799ai^K_a4{vR;5QifA_S1wzFJ^UbYu5h~ zR&Da;Q}@lt8L#i9h@bvjShYp{PX@c6dhGX9u=ykY3a~L-@NBXw)-qmU`+Yh31go67 zdu?Miul!a{2xQUq4x%uyg z`^vkT<9V!}Tbnu5ePeRw*u~ncbp5XZ4{F9<50WG&uh)Ms$Xw%y`VFl5 zT7&WGEksWHK34s;+sm6+-AhjUOIhuueC+QVkNCsj()aA#?u4@kS7Pz~Pas>tzF~Fq zzMfSdIrFOfj^)(7FTP{xdaIRW^D3)qUqt*RcwZS9W#Zeb`T|;j?u=3g=m_ zAm49Un*}cK&}p!_Rk z1u=*3-yG`tM2;)L=J0(+4u9k1BgfOg=I~9MLtP*3zN=>^pQ8Gy^;(67l7r|y~8g=d@HvR-(-CeBIg&}i@|cvC%9`X&RNaUp>fuiAja#j z-8XlNyxOiq+*=>d=Nr5p@m=*t4C_)a}Iq z#G1w04plvT8CaWVv9^~Za(*-XYhhoe5qZVWJi|!D+yXX6K4NYK%PW58#>~JOW1fha zEwFm|Ma(Q`@b~04#9GX)uQT<&%X@BpUV+HPOb>zm{jv9+<#t3L`4)NoJ-P$2m)?#?Yxh2mv%V9N^9$}> zU}OCrsQlgy)>m8jy$5XUP>p*p*jTxUeIHm~Z4vu^u(886?gL+}3Dy7TlNhtEgA@-fekf{l}p zd43EW^VA-5_&8WwtGa`q06Wjf-voaWk&k(P3T&KwA*jQ&5+?T+{%Ei842J5RW{JsJKowtJim$VJRhz3dZ1|W}HT?^(UjX zY83{?K8eh8_zFIOSAv&<>q~MlCoJ~fYwf+y**VFN^M6{?&aWwxt%^JE8{?R|nKWNU z(~C}HbnC)oU)n%85Dvc&@4K^}>B(<*9&LpE2Vp-6_Y@jWPw#kGnYo1O6dBzpPVPqg zVV>bq#-42qzt?|?!jkOgd0k&qmhB=Z`P98U^9VcCv$Y89A z+!)I-EF9Ky3<*~$arkgmVNDAC$XgRLkew6AA?9@xpUC6XFdsxcC(K7#Pq-?qtH91u z%i{mD%`0YcFq`8mo)S0&oZXbsPYd=?#4W)w_`G?8@;m~ruYM#nxXl~%L z>WKO9;D{Gx2ZAHaD1ImFYk#>ktD5(Ph(H1HLBXMaDEwBT6LW)(a7x>7dkiuspBo^i z?z5~bz|oiZsNj~y!}>%mT~W!lDBi)UF7>P|U;7!MAzV=zt@&f%ZTw9Bg78f>=+6am z;BC$e`Si9qd-BPHC+8xU@KV8&a7T@rvikxY%W|*e!!fVrUKbqmZ4_V58~N0ekIq*x hQs0*BTY&?R!=Nj&DgOXB#ec+`(4Az7f3?-J@DDSpg=hc( diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 80bb28ff..233ca4ed 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -34,6 +34,7 @@ #include "Physics/System/SHPhysicsDebugDrawSystem.h" #include "Scripting/SHScriptEngine.h" #include "UI/SHUISystem.h" +#include "Animation/SHAnimationSystem.h" // Components #include "Graphics/MiddleEnd/Interface/SHRenderable.h" @@ -96,6 +97,7 @@ namespace Sandbox // Link up SHDebugDraw SHSystemManager::CreateSystem(); SHDebugDraw::Init(SHSystemManager::GetSystem()); + SHSystemManager::CreateSystem(); #ifdef SHEDITOR SDL_Init(SDL_INIT_VIDEO); @@ -142,7 +144,7 @@ namespace Sandbox #ifdef SHEDITOR SHSystemManager::RegisterRoutine(); #endif - + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); diff --git a/SHADE_Engine/src/Animation/SHAnimationSystem.cpp b/SHADE_Engine/src/Animation/SHAnimationSystem.cpp index 8c514e7c..6f41e2aa 100644 --- a/SHADE_Engine/src/Animation/SHAnimationSystem.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationSystem.cpp @@ -16,6 +16,7 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "ECS_Base/Managers/SHComponentManager.h" #include "SHAnimatorComponent.h" +#include "ECS_Base/General/SHFamily.h" namespace SHADE { @@ -23,8 +24,11 @@ namespace SHADE /* System Routine Functions - UpdateRoutine */ /*-----------------------------------------------------------------------------------*/ SHAnimationSystem::UpdateRoutine::UpdateRoutine() - : SHSystemRoutine("Animation System Update", false) - {} + : SHSystemRoutine("Animation System Update", true) + { + SHFamilyID::GetID(); + } + void SHAnimationSystem::UpdateRoutine::Execute(double dt) noexcept { auto& animators = SHComponentManager::GetDense(); @@ -33,4 +37,18 @@ namespace SHADE animator.Update(dt); } } + + /*---------------------------------------------------------------------------------*/ + /* SHSystem Overrides */ + /*---------------------------------------------------------------------------------*/ + void SHAnimationSystem::Init(void) + { + + } + + void SHAnimationSystem::Exit(void) + { + + } + } diff --git a/SHADE_Engine/src/Animation/SHAnimationSystem.h b/SHADE_Engine/src/Animation/SHAnimationSystem.h index 96cd9a71..3d46edc2 100644 --- a/SHADE_Engine/src/Animation/SHAnimationSystem.h +++ b/SHADE_Engine/src/Animation/SHAnimationSystem.h @@ -18,10 +18,6 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -30,7 +26,7 @@ namespace SHADE ///
class SH_API SHAnimationSystem : public SHSystem { - public: + public: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ @@ -44,5 +40,16 @@ namespace SHADE UpdateRoutine(); void Execute(double dt) noexcept override final; }; + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + SHAnimationSystem() = default; + + /*---------------------------------------------------------------------------------*/ + /* SHSystem Overrides */ + /*---------------------------------------------------------------------------------*/ + virtual void Init(void) override final; + virtual void Exit(void) override final; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 6c525be2..43a9f044 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -128,7 +128,7 @@ namespace SHADE Handle currClip; // Playback Tracking float currPlaybackTime = 0.0f; - bool isPlaying = false; + bool isPlaying = true; // Useful Cached Data float secsPerTick = 0.0f; // Buffer diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp index 9c70d15e..d0775711 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp @@ -55,15 +55,20 @@ namespace SHADE // No keyframes at all, means no changes if (nextKeyFrame == keyframes.end()) return T(); - // At the back, so no keyframes will follow + // Out of range, clamp to the back else - return firstKeyFrame->Data; + return nextKeyFrame->Data; } // At the front, so no prior key frames else if (nextKeyFrame != keyframes.end()) { return nextKeyFrame->Data; } + // At the back, so no keyframes will follow + else + { + return firstKeyFrame->Data; + } // Get interpolated vector const float PREV_FRAME_TIME = firstKeyFrame->FrameIndex * secsPerTick; diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 230df857..6773fb28 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -79,6 +79,7 @@ namespace SHADE void SHModelLoader::ReadAnimNode(FileReference file, SHAnimNodeInfo const& info, SHAnimData& data) { + data.name.resize(info.charCount); file.read( data.name.data(), info.charCount diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index d3329585..8f1cbb6d 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -607,7 +607,7 @@ namespace SHADE }, SHDragDrop::DRAG_RESOURCE); Handle const& clip = component->GetCurrentClip(); - const auto CLIP_NAME = rig ? SHResourceManager::GetAssetName(clip).value_or("") : ""; + const auto CLIP_NAME = clip ? SHResourceManager::GetAssetName(clip).value_or("") : ""; SHEditorWidgets::DragDropReadOnlyField("Clip", CLIP_NAME, [component]() { diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 48859040..fdbbad24 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -380,6 +380,38 @@ namespace SHADE } + void SHBatch::UpdateAnimationBuffer(uint32_t frameIndex) + { + // Frame Index check + if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) + { + SHLOG_WARNING("[SHBatch] Attempted to update animation buffers with an invalid frame index."); + return; + } + + // Reset Animation Matrix Data + boneMatrixData.clear(); + + // Populate on the CPU + for (auto& subBatch : subBatches) + for (auto rendId : subBatch.Renderables) + { + auto animator = SHComponentManager::GetComponent_s(rendId); + if (animator) + { + const auto& MATRICES = animator->GetBoneMatrices(); + boneMatrixData.insert(boneMatrixData.end(), MATRICES.cbegin(), MATRICES.cend()); + } + } + + // Update GPU Buffers + const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(SHMatrix)); + if (boneMatrixBuffer[frameIndex]) + { + boneMatrixBuffer[frameIndex]->WriteToMemory(boneMatrixData.data(), BONE_MTX_DATA_BYTES, 0, 0); + } + } + void SHBatch::Build(Handle _device, Handle descPool, uint32_t frameIndex) { if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) @@ -557,6 +589,17 @@ namespace SHADE BuffUsage::eVertexBuffer, "Batch Instance Data Buffer" ); + // - Bone Matrix Indices + if (!boneMatrixIndices.empty()) + { + const uint32_t BMI_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneMatrixFirstIndexBuffer[frameIndex], boneMatrixIndices.data(), BMI_DATA_BYTES, + BuffUsage::eVertexBuffer, + "Batch Instance Bone Matrix First Index Buffer" + ); + } // - Material and bone buffers/descriptor sets rebuildDescriptorSetBuffers(frameIndex, descPool); } @@ -590,6 +633,10 @@ namespace SHADE cmdBuffer->BindPipeline(pipeline); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::INTEGER_DATA, instancedIntegerBuffer[frameIndex], 0); + if (boneMatrixFirstIndexBuffer[frameIndex]) + { + cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::BONE_MATRIX_FIRST_INDEX, boneMatrixFirstIndexBuffer[frameIndex], 0); + } auto const& descMappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); if (instanceDataDescSet[frameIndex]) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index 1bdd9a59..913a7a04 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -86,6 +86,7 @@ namespace SHADE void UpdateMaterialBuffer(uint32_t frameIndex, Handle descPool); void UpdateTransformBuffer(uint32_t frameIndex); void UpdateInstancedIntegerBuffer(uint32_t frameIndex); + void UpdateAnimationBuffer(uint32_t frameIndex); void Build(Handle device, Handle descPool, uint32_t frameIndex); void Draw(Handle cmdBuffer, uint32_t frameIndex); @@ -134,6 +135,7 @@ namespace SHADE TripleBuffer instancedIntegerBuffer; TripleBuffer matPropsBuffer; TripleBuffer boneMatrixBuffer; + TripleBuffer boneMatrixFirstIndexBuffer; // Instanced buffer, indicates where the first bone matrix is TripleDescSet instanceDataDescSet; /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp index 58993026..0129c0ba 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp @@ -94,6 +94,7 @@ namespace SHADE { batch.UpdateMaterialBuffer(frameIndex, descPool); batch.UpdateTransformBuffer(frameIndex); + batch.UpdateAnimationBuffer(frameIndex); batch.UpdateInstancedIntegerBuffer(frameIndex); } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp index 0cbd6c33..09f7d19e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp @@ -218,6 +218,7 @@ namespace SHADE defaultVertexInputState.AddBinding(true , true , { SHVertexAttribute(SHAttribFormat::UINT32_2D) }); // Instanced integer data at index 8 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::UINT32_4D) }); // Attribute bone indices at index 9 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); // Attribute bone weights at index 10 + defaultVertexInputState.AddBinding(true , true , { SHVertexAttribute(SHAttribFormat::UINT32_1D) }); // Instance bone matrix first index at index 11 } void SHGraphicsPredefinedData::Init(Handle logicalDevice) noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index e967312f..99694c11 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -234,6 +234,13 @@ namespace SHADE */ /***************************************************************************/ static constexpr uint32_t BONE_WEIGHTS = 7; + /***************************************************************************/ + /*! + \brief + Vertex buffer bindings for the bone matrix first index buffer. + */ + /***************************************************************************/ + static constexpr uint32_t BONE_MATRIX_FIRST_INDEX = 8; static constexpr uint32_t CALCULATED_GLYPH_POSITION = 0; static constexpr uint32_t GLYPH_INDEX = 1; From affa6f0dd8cc94340e36364dda3bbf3e6b167b22 Mon Sep 17 00:00:00 2001 From: Glence Date: Mon, 16 Jan 2023 21:05:21 +0800 Subject: [PATCH 122/164] added implantation for audio scripting --- Assets/Scripts/SC_SoundsBoard.cs | 13 ++- .../src/AudioSystem/SHAudioSystem.cpp | 4 +- SHADE_Engine/src/AudioSystem/SHAudioSystem.h | 4 +- SHADE_Managed/src/Audio/Audio.cxx | 31 +++++++ SHADE_Managed/src/Audio/Audio.hxx | 9 ++ SHADE_Managed/src/Audio/AudioClip.cxx | 89 +++++++++++++++++++ SHADE_Managed/src/Audio/AudioClip.hxx | 83 +++++++++++++++++ 7 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 SHADE_Managed/src/Audio/AudioClip.cxx create mode 100644 SHADE_Managed/src/Audio/AudioClip.hxx diff --git a/Assets/Scripts/SC_SoundsBoard.cs b/Assets/Scripts/SC_SoundsBoard.cs index 839450a9..fdfaa02b 100644 --- a/Assets/Scripts/SC_SoundsBoard.cs +++ b/Assets/Scripts/SC_SoundsBoard.cs @@ -4,6 +4,7 @@ using System; public class SoundsBoard : Script { + AudioClipHandler test; protected override void awake() { /* @@ -31,13 +32,21 @@ event:/Homeowner/homeowner_humming event:/Homeowner/homeowner_footsteps event:/Homeowner/homeowner_detect_raccoon */ + test = Audio.CreateAudioClip("event:/Music/player_undetected"); + Audio.AddAudioClipToSFXChannelGroup(test); + } + + protected override void start() + { + test.Play(); } protected override void update() { + if (Input.GetKeyDown(Input.KeyCode.Q)) - Audio.PlayBGMOnce2D("event:/UI/mouse_down_element"); + test.Play(); if (Input.GetKeyDown(Input.KeyCode.W)) - Audio.PlayBGMOnce2D("event:/UI/mouse_down_empty"); + test.Stop(true); if (Input.GetKeyDown(Input.KeyCode.E)) Audio.PlayBGMOnce2D("event:/UI/mouse_enter_element"); if (Input.GetKeyDown(Input.KeyCode.R)) diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp index a31383fb..543523e0 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp @@ -372,13 +372,13 @@ namespace SHADE { if (auto transform = SHComponentManager::GetComponent_s(eid)) { - handle->transformRef = transform; + //handle->transformRef = transform; } } void SHAudioSystem::DetachAudioClipToObject(Handle handle, EntityID eid) { - handle->transformRef = nullptr; + //handle->transformRef = nullptr; } //AudioClip* SHAudioSystem::CreateAudioClip(const char* path) diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h index 153ec86a..d1fc325a 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h @@ -23,7 +23,7 @@ namespace SHADE class SHAudioListenerComponent; - class AudioClip + class SH_API AudioClip { public: //expose to sxripting @@ -39,7 +39,7 @@ namespace SHADE friend class SHAudioSystem; private: FMOD::Studio::EventInstance* instance = nullptr; - SHTransformComponent* transformRef = nullptr; + EntityID transformRef = MAX_EID; }; class SH_API SHAudioSystem : public SHSystem diff --git a/SHADE_Managed/src/Audio/Audio.cxx b/SHADE_Managed/src/Audio/Audio.cxx index 52e29529..a4600df1 100644 --- a/SHADE_Managed/src/Audio/Audio.cxx +++ b/SHADE_Managed/src/Audio/Audio.cxx @@ -98,4 +98,35 @@ namespace SHADE auto audioSys = SHSystemManager::GetSystem(); audioSys->StopAllSounds(); } + + AudioClipHandler Audio::CreateAudioClip(System::String^ path) + { + auto audioSys = SHSystemManager::GetSystem(); + return AudioClipHandler(audioSys->CreateAudioClip(Convert::ToNative(path).data())); + } + + void Audio::AddAudioClipToBGMChannelGroup(AudioClipHandler handle) + { + auto audioSys = SHSystemManager::GetSystem(); + audioSys->AddAudioClipToBGMChannelGroup(handle.NativeObject); + } + + void Audio::AddAudioClipToSFXChannelGroup(AudioClipHandler handle) + { + auto audioSys = SHSystemManager::GetSystem(); + audioSys->AddAudioClipToSFXChannelGroup(handle.NativeObject); + } + + void Audio::AttachAudioClipToObject(AudioClipHandler handle, EntityID eid) + { + auto audioSys = SHSystemManager::GetSystem(); + audioSys->AttachAudioClipToObject(handle.NativeObject, eid); + } + + void Audio::DetachAudioClipToObject(AudioClipHandler handle, EntityID eid) + { + auto audioSys = SHSystemManager::GetSystem(); + audioSys->DetachAudioClipToObject(handle.NativeObject, eid); + } + } diff --git a/SHADE_Managed/src/Audio/Audio.hxx b/SHADE_Managed/src/Audio/Audio.hxx index d568dc90..1a5ced53 100644 --- a/SHADE_Managed/src/Audio/Audio.hxx +++ b/SHADE_Managed/src/Audio/Audio.hxx @@ -13,6 +13,7 @@ of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #pragma once #include "Engine/GameObject.hxx" +#include "Audio/AudioClip.hxx" namespace SHADE { @@ -99,5 +100,13 @@ namespace SHADE /// Stops playback of all sound effects and music. ///
static void StopAllSounds(); + + + //to comment ltr + static AudioClipHandler CreateAudioClip(System::String^ path); + static void AddAudioClipToBGMChannelGroup(AudioClipHandler handle); + static void AddAudioClipToSFXChannelGroup(AudioClipHandler handle); + static void AttachAudioClipToObject(AudioClipHandler handle, EntityID eid); + static void DetachAudioClipToObject(AudioClipHandler handle, EntityID eid); }; } diff --git a/SHADE_Managed/src/Audio/AudioClip.cxx b/SHADE_Managed/src/Audio/AudioClip.cxx new file mode 100644 index 00000000..021a3437 --- /dev/null +++ b/SHADE_Managed/src/Audio/AudioClip.cxx @@ -0,0 +1,89 @@ +/************************************************************************************//*! +\file AudioClip.cxx +\author Glence Low +\par email: glence.low\@digipen.edu +\date Jan 16, 2023 +\brief Contains the implementation of the functions in managed AudioClip + + Note: This file is written in C++17/CLI. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "AudioClip.hxx" +// Standard Library +#include +// Project Includes +#include "Utility/Convert.hxx" +#include "Resource/SHResourceManagerInterface.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Properties */ + /*---------------------------------------------------------------------------------*/ + Handle AudioClipHandler::NativeObject::get() + try + { + return Handle(Convert::ToNative(audioClipInstHandle)); + } + catch (const BadHandleCastException&) + { + return Handle(); + } + GenericHandle AudioClipHandler::NativeObjectHandle::get() + { + return audioClipInstHandle; + } + AssetID AudioClipHandler::NativeAssetID::get() + { + return SHResourceManagerInterface::GetAssetID(Convert::ToNative(audioClipInstHandle)).value_or(INVALID_ASSET_ID); + } + + /*---------------------------------------------------------------------------------*/ + /* Constructors/Destructor */ + /*---------------------------------------------------------------------------------*/ + AudioClipHandler::AudioClipHandler(Handle audioClip) + : audioClipInstHandle{ Handle(audioClip) } + {} + + /*---------------------------------------------------------------------------------*/ + /* AudioClip Properties Functions */ + /*---------------------------------------------------------------------------------*/ + void AudioClipHandler::Play() + { + NativeObject->Play(); + } + + void AudioClipHandler::Stop(bool fadeOut) + { + NativeObject->Stop(fadeOut); + } + + void AudioClipHandler::SetPause(bool pause) + { + NativeObject->SetPause(pause); + } + + bool AudioClipHandler::IsPaused() + { + return NativeObject->IsPaused(); + } + + void AudioClipHandler::SetParameter(System::String^ paramName, float value) + { + NativeObject->SetParameter(Convert::ToNative(paramName).data(), value); + } + + float AudioClipHandler::GetParameterValue(System::String^ paramName) + { + return NativeObject->GetParameterValue(Convert::ToNative(paramName).data()); + } + + + +} \ No newline at end of file diff --git a/SHADE_Managed/src/Audio/AudioClip.hxx b/SHADE_Managed/src/Audio/AudioClip.hxx new file mode 100644 index 00000000..09f6a2d1 --- /dev/null +++ b/SHADE_Managed/src/Audio/AudioClip.hxx @@ -0,0 +1,83 @@ +/************************************************************************************//*! +\file AudioClip.hxx +\author Glence Low +\par email: glence.low\@digipen.edu +\date Jan 16, 2023 +\brief Contains the definition of the managed Audio Clip class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// External Dependencies +#include "Resource/SHHandle.h" +#include "AudioSystem/SHAudioSystem.h" +#include "Assets/SHAssetMacros.h" +// Project Includes +#include "Engine/GenericHandle.hxx" + +namespace SHADE +{ + /// + /// Managed counterpart of the AudioSystem containing Audioclip + /// that can be fed to Audioscripting for creating clips. + /// + public value struct AudioClipHandler + { + internal: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Copy of the Handle to the native object. + /// + property Handle NativeObject + { + Handle get(); + } + /// + /// Generic handle for the native object + /// + property GenericHandle NativeObjectHandle + { + GenericHandle get(); + } + /// + /// The raw asset ID of the asset. + /// + property AssetID NativeAssetID + { + AssetID get(); + } + + /*-----------------------------------------------------------------------------*/ + /* Constructors/Destructor */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructor for the AudioClip + /// + /// Handle to the native material object. + AudioClipHandler(Handle audioclip); + + public: + + //to comment ltr + void Play(); + void Stop(bool fadeOut); + void SetPause(bool pause); + bool IsPaused(); + void SetParameter(System::String^ paramName, float value); + float GetParameterValue(System::String^ paramName); + + protected: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + GenericHandle audioClipInstHandle; + }; +} + From 1b4397c76d014d172010e2bf35a4856db44153b4 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Tue, 17 Jan 2023 14:38:50 +0800 Subject: [PATCH 123/164] Updated all compiled models Fixed UV mappings --- Assets/Models/ExteriorMeshs.shmodel | Bin 103436 -> 102666 bytes Assets/Models/HouseModular.shmodel | Bin 285698 -> 252771 bytes Assets/Models/KitchenAddOns1.shmodel | Bin 133022 -> 132411 bytes Assets/Models/KitchenCounterEmpty.shmodel | Bin 10165 -> 10165 bytes Assets/Models/KitchenCounterMeshs.shmodel | Bin 235098 -> 223299 bytes Assets/Models/KitchenShelves1.shmodel | Bin 14362 -> 13063 bytes Assets/Models/MD_BreakableObjects1.shmodel | Bin 175288 -> 173778 bytes Assets/Models/MD_FoodItems.shmodel | Bin 36626 -> 35947 bytes Assets/Models/MD_Homeowner-NoRig.shmodel | Bin 187027 -> 187027 bytes Assets/Models/MD_SkyDome01.shmodel | Bin 34174 -> 34174 bytes Assets/Models/Quad.shmodel | Bin 236 -> 236 bytes Assets/Models/racoon.shmodel | Bin 679960 -> 677824 bytes .../Libraries/Loaders/SHModelLoader.cpp | 8 +++----- 13 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Assets/Models/ExteriorMeshs.shmodel b/Assets/Models/ExteriorMeshs.shmodel index 90def7442a650455d53c4ab25f2e3660796a60e9..75d092b54c55d86257d0767dfb55f4eee5514345 100644 GIT binary patch delta 33111 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 diff --git a/Assets/Models/HouseModular.shmodel b/Assets/Models/HouseModular.shmodel index 4e280293c4e8504db956ece073112b0fcc8f0234..644f41abf56c1f489783c7a2e42f508b48b38320 100644 GIT binary patch literal 252771 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 literal 285698 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
)hCTu>Ee#+cm_l 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 I!q$NOKYOn3WB>pF diff --git a/Assets/Models/KitchenAddOns1.shmodel b/Assets/Models/KitchenAddOns1.shmodel index f43ea99370ec068b49b2b962dc70b6918f24c332..5b916be05b446f8a1dbeb605abe112260d98bbbe 100644 GIT binary patch delta 45535 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 literal 133022 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 Oi04tk>TRQ)A^!n~*15I- literal 10165 zcmeI2d$8426~~W;a4V5CUV^+bQIG=V5zq4zkVGB@3K)tA29*%;2q8n{cmu=&MWF~N zgwm9o3d$o2xmQxA8p>g^8Jwoc&>Ute5{-(nM*4pCZ>@9p`EkzW?`H0q`TG6twZCia zz1QC7dKow`SNeHD!&lDB<(gY~_}%mFoI8ENj1hC@Exbpux?BFcE_v_FnS4Wfsyye* zi>H#_v~gpxP6eGrYgN=y{iKqP>L-=-HtomuoK(M!2zSjNS-Yc$KPP3NB%jp9X(sl8~deGl{ANaTB##){s z?9=>3{r0#t&OT$U+#yk&qSpTNUguoedK>)ZbabwKL)vrkWVP>;c&uxF^Pl`|d;eMO zcR@VXYgVsKsumy56ECN!pRLI|^!l!;Sx#Hc+Nges_WI-=dvsmsNkjUS?%w^e*}AXzV~^2O2xj*n$7!4iv)gLOMVpJw&1I{M@uX^#>{> znmU(9^H~oS$W2)_`ds-t!p*)=N;;o^(Z&>p6w~S=ZrtiuCE=|DAbR0 zg=Gi#?bet)#r=b?bU)Q|*Wgo}PqB`zSD$<4%(CFQW?zfmW&D6FyJ>|p;sn&&(PW2U8ZxzX#7mZ@3V z{}Us(kZyG!&6yiGTT<%D<+cy5ws^cx{aK{?f)Mw@@!Ko6fOf>JKXPuC%_#t25tsk7w7dkE7DNFb;uqzW8qK zoF#kk82KfA@Z9q8;4MU+q};movC_TWHU6;f!#~+|)9;RE3pjrM^sCQBb=YHwGbgEW zslPrR=Cv>Usb~NA!+nVMI^3N1z??quSt@Dw`Cfew<@^nKyiSn|sPvQO*;^@iG~J)? zHM?%5xZk<^spekavya{5abKDFQcsz^;iM{;>%COJJ%=(P8yzkY~$)aYLCXXE$mi#K|m@MxwA~|t!r=;t^>0x|H%kE)*=##m~zP|5g$Zy+({AcEL2>HZW52lXq)8qKt z>-^8k-$VT%&UmKQr$4OsZqKgCu+trrV`Eeh;ujDsx)g_sw{Lzaq5BZ0tbq;wi>K^Ee@6+!w z-oM{(ynphz@3>zp?Ov$o`S5VNPwx{x&x4tVnfEyNfe&UK*!zax?+a!<`i|f43&!X7 z5wpKjzZdd(ubk8G$^FFrUg)#m%S+KcW$(l9!S99nxZm8npNBqjPt5=NhAH8mvds5_ z_sV(Tj#`KZ<_Ir<+`n&_@czxa(@tA#m-k-n2nDO&2owNJwH{fsi z$$w~;Onu};kzcN<3g?K=wS4BOFh51UQSZEyrw)B4?tc1DJs)%43unGn9P{(T-#u~G z_4D;n9(Cb`6Hey+TvhVw)rs09YV4vjslsH_@DyQ&Kn@->e5ZhSn(zhF@aKiE2>9@z;V%iZg}Vei zXqcR@3iE_nrr|llTw%Vj*fe~%Fw@4a;d_LI!XjaXY4~fx*KOjTw>Zi)%-U;&4Z=Fp@Hd3} zg@=T(rs35BIpl6P4L>P#77D`8gwDo45r0>BMEH(rc!V%UxL)|SY4}^hCxwmz{X@g+ zg?=`64G$A02{Q%W3>xM;^F84i;RmMS?+ZV&v1@pX@VGH!G`vl~|CI1!(=fI72-IW^ zH2kdayfI@myi>rlPuOo7CVz#nNcf>?c&jj4;OD{9rr{cabJLT(rr{mJa)GclL*d**9nA?DPr^(>uPe ToVj2czE>c&Rp9-g;ezl#2pxYg diff --git a/Assets/Models/KitchenCounterMeshs.shmodel b/Assets/Models/KitchenCounterMeshs.shmodel index 243f69b0bdc9aca05c62c00fb803f9beb2d6d42b..3fd279008e2c2abc691337333ff5565b78bc1736 100644 GIT binary patch literal 223299 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 F{2#E@$T$E1 literal 235098 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 diff --git a/Assets/Models/KitchenShelves1.shmodel b/Assets/Models/KitchenShelves1.shmodel index 8c31fff30c8432bcddb1f6319fa6366a2fd00d17..7ba536c9ca8bdf9ac00d46b5c17b4a8928516180 100644 GIT binary patch literal 13063 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 literal 14362 zcmeI3eT-d28OAT|wsf~zT3C=zcR?&&tagh9i(&6H`-hRF5kp%9W26XMi?m%zHq;nw zk6jaDV~l}N+9nWE6C3yggE1i_ZBK~ND5<8VG$?A-s6u|TWfAB?Q*SIf9Tfr+;DxV`D>$bT`wra7w69l?LbgKAv_$wy{9Q?%e zg2_km?J&=xR!c_ptlMEdJ7S8My;6TC%vy>m;_!K$aWgw`PVT^OPwz-itna(Ul1oVN zOZ|`B;@;X}pZ!|x%o*{<@IeOsq z@qyFhvfOMf7w1n-VU0W=%h@$@PF%=4!3WN!+$uPm@~PsC@4dKJasFj{RT1B`ZZT)= zPn=IN|FU(f;~USKwZgMcJhcC0d)qO_=Ug(^`kVK^B*w)tH!t2(fAspT$o$m!EtxkB67> zm|yGA&pOOwori56*NT_d{mAdzoXwLbpFK}x_kpLMeB~Km`KfFi4{z*$c_Ld^o^g2O z>4#6Ae)8P@+R6IX;{$%bbf3iST=Ji$ey+8T+6T=q_A~z~`^;QzHs1`4?=$;=*$2%o z3mE@iv&ClDn_Uzz{&KSsvmrACBp83883J*Z*#!aPFEK+;|(N%|0A3e$;Hx>|)oq zHej~QY-zyw*=BRhK4NxL!1zUGYbPHx)%(*+2GVDKxA)MxZPTs_-+kwzw6@@)v151d za^2vFC#}K}PudMO9v9_VijBubd3MARCwYpq2}hjdDb7l~Y{fuYd*PXbw?97Zdc1N#l7m#f1G)4D)E>Q@^;H=K9>iI!R-_S|WA$*?+(3)fuV}RVuY6UsSJB zqs&8%F)kH~8e<;5IB(bA-^k-wr(4y_JgGj`dAF%v?Ze2UcX*Xs+@Iwm59Ix*U9P!b zJs;xfXTJKSdi(5C5UXBPFv<}XiaG`B*b~)@%8gYoDi~#o>O&2J@hDcQQq(M1s#Yo* z#R6F=)&Qx1)o@m;U_AA$pe->X0P zuhaG8SH13kc=|5OjzhSn;5~idfG>>1Q}Bqt`I>|(GZS^7^-gj&D`ou%`Y@Yb!{a@T@yBsOa&%C@+77bM{BtrFno${i$e}RrUKJF(`I(yoZJEXt7#e%OET|kUcmI^ zHNs!C#w#ZGNo!0Cr>*Jx+Hr=wSYJQZEj%v!t1mQ)@sK&zvboJMzRwT(kSvcR!@fIh zj@O9tZO?i3_ug`k9rmgvo;?YBr#)$n$?mGPzxCcb#q_$Ly~SU#akqc1Iyw2zdjC^% z-Q=-X>Zi}lb>AM_=lkXVd-})qcdVcDt`|?vb646tuBZ0>t-kZ;bKSxnaMnt2F4__>Ckb#*_nuYGQ|)BL3` z9;!e7odNgiulCmImuDP4#>s6C{`8e*Maqfq8j~@Gp?z;}_5Id9se~ zoB8yAzHhf*U!HN+-~Y{*{rdF7FV8sFjFUh2&o!^PI#&d9uE=XBudq5l1oK+TYpl)* zc{K*>+-<}#XMvpGf$@vYZZhNCkuys$o^wV1y1m?tvrRCbvqD~tuQR(gU_7tKeP+XE zoFRhoG+vcC7v$^^jOX>7Gf~b3IVS|;d0po0RkN>0SA~cb)IhgVMj!z0$kVWzxIy3zp6I^Q3=S2OjA_>0bEJyVAeXyYj4q zz9m2Oo^&t#=uGKf-B0Y%ozlCSFTE=-y~{ZIQ+iikdRO}+-7CGz{lt2gIIhW)^`&=p z-_pDC(z}eazVxoV^e*GNf4u&bgN6l5*P`vvdFWcO^ewtrdKiozMmM7$(XD7$Fdlu0 zPL#d{Uu}ktlrBaGgQbVjz35!@E*OsvM(fgI-8*d8XkBz}tb5VHXkBz}tb5VHXnOQL z+8ivsj@Cuz4w*^!qJw9dq5IM7U_3e=-HXnRbuT)YoXM3n*fTu)hi89pzwNfQjqkK| zVt#WgXN!*B%LAH8xGIlpyvmf1cgsT@9|^uzG(Ju=K6W&H#Q93mh^74x4FCd` diff --git a/Assets/Models/MD_BreakableObjects1.shmodel b/Assets/Models/MD_BreakableObjects1.shmodel index 6d83f9fd823dfe2743dec9bb8b572236a5e8106c..3212f4500297c293cb35805adde94f2d87a2d7d4 100644 GIT binary patch delta 65224 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 delta 66467 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�rNy)r=8vEfO&_qU}1-;k)l6jF&8>B1M2;3bW0 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 Yx+|UK4}B4RhPVw^yozOmt}(&>KgDo6hX4Qo diff --git a/Assets/Models/MD_FoodItems.shmodel b/Assets/Models/MD_FoodItems.shmodel index 4466830ede4bb1b32f7adca2d78f344fd9f3a6b8..597ce28522752bb23243b74e5be5197d345e9d7f 100644 GIT binary patch delta 16870 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 literal 36626 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 OiB3)X$}b%;eEmN!vul|E diff --git a/Assets/Models/MD_Homeowner-NoRig.shmodel b/Assets/Models/MD_Homeowner-NoRig.shmodel index 639d49816c5b7378159ea6e24688410fd708f857..b359254de5f7bf337e4183912a8c170100a27720 100644 GIT binary patch delta 59261 zcmY(qc{G;a_y3QSDMJGh5}^#CkmtxwLU+dv(5O#AC=-8$D<613Yc{OJ*ZY1jIntu| zXRwqOcDlf=OEXb*-z?ntEQXsh$ZF#8&2zC>R)xrx^wZ6@L3prEgv?fJq~Ep&Z5lOcEcCu-J)4!@|r z_~cVPxw7IK)qL!a8%LMY^G)mNqTmQr-d;g$#74vTQw>ze7R`my4ZdUpKV1t90`UBi zJf@SMF1r#dxVPmTtsL^g-LqTByX;5Q!XXs>RUCr;;gG|;-xX#6-$ zoNU(jwrTvrqU633!M}B|+!xMTF%9F_IFb!so%B%TN(_%SAwSXwDBJrX2rX7zVOCAE z!wA_ac4wAu^;W^-@0(`9Xiq23BPUVq5Iy5;;~=e_m z-ny$a$J-JwPkur#zM7A7wfbnqnr3EcsFvVmFKIaEL#}69s=RvQG zeNO2Oqg0~M9 zknp^h+&g794e!r(A*~O;a~j1dIJ;;CEq`XtT{Tfey(SlaIYQ}BSt43Zcc*tY3aAQ6 z#(ma7WRIH^t~s>?Reuq(dgdSvo8yN4C(7xAWPKFdJQ@w%Md6T}C5FlMP-{y`^r-vU za`}flnywoSUcD3PZeoI4O3ksByh>>}Z!*Aa(@2 zqDq#7^x#AagJ1S^}PhOG_q?!1wv;l$BQVg!+G8(@84tNL8OLoRHzT zDBj-zyuXW$62Sew8vP_Fqz_d>iRrmp!UC5x__wA9^O&d%V>rog*uJQ9wCjf#%(ZDD zTlYOA4>CiM#b$HmYq!Ydj8Ie`CrNIfKTa%od<hJ|o-83ik7!&IuMLnY`D(ES(B( zcM-U9Y%+cFA`!kVSx@2)chZ_=E8$d~33uX_7Quc0RzZ)3Mo zld>M-FXn+eNA4lteon*;ow2k&(E;c3({(7rh=%h1CLS~JL=h#6v?k%cl|6LDl%;U# zV?F&He~mo4?~fL8r8NJ=Ix=Hp1kQ@6AaSE^kUyXF$ccdv{I|}m&0|Jn8iO={8m><` zM;Ei-y`VzSLQidaM32~oLiM)o=1*#G5!IYfjQS%%JA&)TZjTuBeIr5^jcgTk@U!*Z zPn{%snFoD1BmtFUNc!EEY_|o(c=Nn-Wef%I?q?Mow^OYY%Z55XCt^z2Tk1U1o_- zAH6&J5#6xT0e-mkP}QkRVg2WNGB+}a{LhL%{5*&ySxV05Gw4gFfQ-m<@`?9%fnq*w z3K$D1NmpsElO=yFXA*Ty0rqp6{JWr;1O^$hxP|w3nesyNllQk7@9!#p&clzg&f^#D0P$`h}rY$Z?TXOc)Y0j{{L2@f_llQ7=j7QDYjcU>oe+x_wJ zj8d}X{Qz@gbt>L@>OnR=`OYlo?frA>3OdnFf!vK5f%>OB@t~0PBgWN9_)}&f{jp9! z7V!SA;Qj6ECIx$VfB)qDJ>Ghd+~DoE?MOMDoUIR=){jQB#iH=`h%#iq8G%m4>gX&; z6^spbN4Y>*IMzFnbn*Un<^4VVY9%AZ+q>`J4(7p`y(Ci@fNs|*4X==e9J(0o*cKx? zni(XT{15CpTuwqPqPdqj-gtud_cQ-;F6yBps(qgLKYx?AyuV#}f9rb;lan4gxKmdd zPkac0ioq|8h09% zvmjVVE8OgWtyaD#>>JWT=e+<;`rg9*XZ6t8eI#@o9>=76cw=x_Gz{6PakASfHLbG& zc;d`$-8&uE-b#SZ;4)$0;C@1un06`(GmhVB z?!U-_$>22ZP^XljJ)DKQf;4VO@e?cPyfYP6G_L0!l+MNnnojVK=vjeRy((5!vGAjH zHn$;{3SL}YO%Ct+&b+>!jB__nf+rr8%=J(6@j}H(@_qOaBQF+oc!quai7A7 zt;N zD_mq(2o6k`2TIzbnfc4KvHAuQla&h0C*x=ocMXBa?2+8+*H5YH1{Qk1rEtfb23k5l zgh9MOf{1!urDniDuk<2@{o8MV7xxnguUySsR@KK(ZBj60rUvmEWWWPkcByyD@@g6-U7Mm$OsH(k$yb+Z(h9|oc% zd{dZmwKd5$TTGMFePNHxHD)*YOIIE)Aa{RzQcdqz@bdmehs}q` zq9JS8aOpLjVaIW2HwVMz{kq(W*f>~OaGYelS_J(o?s3~!j>lT#+w{^hHM({R8v@N0 zanLtR=}$HAziDG^X#CfQTFS z8TA+sYH} zlLA4hBPdDsr6&_j;LYs0WKhHuf3B5;cWs-=-yaK6DO(ksXOAIsR2)!rg)uzPnMnr5 zn`2c+I9NMg685NfP|X7@yw!DLtfDFfY}cMlcv7~Lizef!>?jq8&m6~19_5URgT9c^ zb>F7nSrp4&C&LL?b@p)v5rrJkF5`WoFQ83Cg2(%?|6N=xevKN=fn0P;ugvdkwCpziQE$dh@* zc&WKy%XtGx_utMO`60knpR8e{j3|@ZXoOV{S;);2WyVjOE7(yxKqNowF%QLq(UdNP z2_d@~=Wt~#7mow+-!)8Xdo3;h#ll6+DT2*UN84ROG}C z?z)*VmepB7tZx9Qg%vX~Y{)mN=eq$6s7R=hVwj?JGH)&5Xg>4H57@yTsqZ zSg8xs&lfSNNpsMB`8<9@IWn=rDJW$v57J5!jHb97x;#sS82<$BH{D4^tq+p}JyxVB zDH>xwi$K}QvE-zZH`YF9!6*tTn|V6;dITpR1eP1IdSD>9uyn4?IJNsec6f3XG> zNfV*LR0q6nHx?cbYY_W6i||smAv7F5#q79ggL$pfgtECs8LPXGSVyB5-Dg1No?uNJbPVqCcxgcG-9k(VA75=u|`eY$p;6 zofz!p4iKxEtUPHP6Nl`YJw!b!kd!=)!tv9KNupjPS@WvrVD`!|7+?6B+5PS& z_3qFkefi}i*DwwrSU=}F6%sLXM=5Ox&m$|BgcH^+B^Kkpv9RPsroc>kuT9XmH)Q9# ze4?c7iM z5QxehCb^&Dv0%**SM1Gb)ck;o=4Mmet>n!aHqOj^1akIu`Xg)3h(mx8kKjn`+J4vz@Z zek~RATUK(FRxC7hj1v^=?BUECS%`6)#dO@-D)0}EfhajS=H;a(YNTKb3-m{jTRB=d zK0%47ZKz{p>@sooNO_3&s%9EzFGY5_wm&5JKeIMZ{X?rZWWi2u6*qYQFVpjug*>i9 zuxE3XjY@VFJWUd3o{yKIVRMrJN~<`};v&*M#DdGig97{Nr`B>M88BdYg}Z%gGWqai zDRj1tXMV1b$Ey!muwNy^{Jk`_r7a;;c-;0BxlkO7T-kBXL!M=E^c7LzGOe==`~0rJ z=Xo|39+rZo6P^h|nmzEob|yrPv*oy1`qX_W6mE`6W9~zgK5g=hB_p3C;x3mk8n8SCJ%j&YIuich^N)kunGzosYtKsyQ!M^rPC)_5&q+jy zDM6&lDw~Ao#AC?UjkNZmFK%smL>E@e&|lT*cvfs8={6qbW(ms$XVkJWsQWhAt`dS{ zPpQzmUr!S6$XI;LE)U_VTi24@H7WSErD6Chb@i(5+7t#Tkek73oUHgn2^$ftauYq)b<71+t=a28@vxq_KW16h%j}Jy? zkwcT79*@timXm$LKDt>|$ml8rqh`b;QeTycCv9vPmy}X+`a}{cba&YJ zirpZ+#^D%z{So)Rt)0|3$TN>f1b!(1659M-FzAzkId9BK)YBNj*4_*jMRLWVOH&NXrNO=i=wx18*_$QhJ#sr-=2+%laF)f=Gri>{b{J9p(tD?^rO~s#^_kiwlZpu zEOE<)gH-qGOF>~m0yceS!B=*-FeBc`=FfT-KP9AaUP)4d=^A{@SCGTWrvv`dFu>cj z8W0tAi}+ab$M<4I8pyt2{SJBnB95QRnrHp=J5M=KANb^=s1S|9rvRwuXao_Qg_|424;*qFXHKdfUu-z6rzS z(_S#!f_i8j9Vc@=4?81;WV6WZbbY zib&ryhjzm1qen#?)nnfhmiqihSLwyrY|&DLkg<#L@@X5a`Q}F~erf+lcS@9Um#X^6 zoZMji_&^0UH$EkoPaFJ4zvxt@FR3wUGfYFlnZ?xfdNm_;CJil@dr*#L1>_~4)i#M~ z;A-2=WcoD0fAovvzo~TNM)JIDDdy%H;P1VB>E!KYs{4(_Uy*5ol%Z=a)jj&SmQjOa zz7NTUNji9~%nHtWr;yE^j@ZVDK+lI0#BOgoE?+Z>be_;4BMwi*jXUPU?CxoleLeFi z_wyDXHm^QHmm~y{#p`Cl`WSuOv@D-4R1O8jutT(<4Tzh9GdOJj@3N`z?1jlNlaPs@}mP6T(r>SH6D{|U;3AX)Zu}((RCT-5Jb#6p5 z&e5Ak2GTv~vTPUXn_foF9*#ln{W|zQnUD%+2E+IQg!r@$X4Q*2I$*dIr7hHnrkxC~ zJMe`5y6;Bvwg#doUkm;G$ein)?rSq$Jp)BD1{fo@U^3NdQpBiYIXLuH2@gNLONZu| zf<)1Hv~Z8WnB-05>b^3XnC-|%veIzRsgU;Vm`^j7OosVn4tD%@;QnT7!^Ly9c#dD1 zRI#%*(S4dsp?Ml!`~H*h-DpRT+>5}RC^d2ggtV#O4QEUWCxU~b7$^CVum3BZAg`Ha zSYD}*({Ak`_DaV1^|TW0+5C>g^aS8g1B>I1&gCQ&MT9#QH*s0Zd&$f1!5DSQlDE_o za;H@KKN|P5IHlK(>ksu1DA%yazI5V@=1dn%ZBIk@rz6Rsd{cVseF_$dN)XQkPnKr? zT0~`>w3%gV((zlt7*r0IAT#dsrT5I`C@IuqT28;GNpnMR^5`Pw>gmT+MIs9CC)O}~ z3tOqkwD~xg*vhc>Rm2oI3;Z!#0kUr2 zp_}^*uzBq+o8|Qk#(o~8_67+gVYe5Gv#;#XWSl#_8>NCzFNfp#=>2rx^mUY(Yz{B= zUUKy(X5vi|7G@kN<8D_njPr{{FiUO?HFuE2-O2*kE+T2O`0#YBP6~ltv*d;AdY{tT z$x$S`UxeHfO+n2}KInLrMNbt3qLFhSNekJ=tgH{l8;(~aUwyg==r=7x2 zo2)QI><7Jk?E`srTYz~zm$`4Yk`QO_@gMDHts~6HQiQyA7rbu#nLc{vMM`&i|3}Bi zZf7L6TR>vo47`7Z#cxAW!rO7$Od?(Ve@$Xx-0v6-v<3{jp z=P|DTxfN0q85)FZ$u-$zT;7p_pI64w_%V%~ZK6NQ%a_pgZ*S1I-YjO;1T)vynQ=Eg zB_UV0Mi9Eo6aUP0=j(qXCBXBQK24Ff!`;8cpg5zAUU=+*AEGV0BacV3{= z9g4c*^T_pme$4!r>1c8O6ql^hN4B{JqfhZFte-l9*nf|w9|RX^w0b{jGl%s2g&BMBB&gxgZEb5B2x==uqk{L_40Ycl7js~ zI0dcHUVH}8TBrXX?WAP|OV2$Oj;J-kvo-#>a6*`1Y|byL9>`(^&0x~XBp5fU42kt8 zTijxuv7XsT>d$^AV@sAH{b__NkDKHBRwH`oQ!AOKoX)6)@m5zlPO=m8m^&JA7&nU* zl4UkynYi9)%shC8{JOu6a9dXXNAGMuNt_2cGRwgiOZrR5?4O&N;HgPy6j4fg8g!UH z8A)gnSwKoG97yBBxc}&CId$~>wE&~{j$%f;Y$DY!gp5a4GXDRX%U9u&?j&GUd;xzoXPLTOP%-!_qlL~(n>5|}{c}|1$+Sz#f$T4!0IW6dKjr)(T z={wD6<=kS@4`<=Ob=$<5z&?{dB2@3NKwnpPawgvh6j)Pq)&50F`a6hB`#dZX_rV5f zarAm;!5lYrfp!-$*!8u9lqmT?!Q=))l-h_ysSNn0I6$j@8A-FB1&@}OlOAO)I8fyZ zW3P3S!syFnL+B8>DDVdIemT8RDF+F%hI~oxJ4;`uD}((uaoA(7jwcuMq(ZrugpV`A zjCxs^Bl4Fxywb(+5-~jxbXcPhRkYvg{57r;9fyGLvh!is!>3B7lO!26pQC@8rQ-yI?N@#Y1> zD7nzx)ftd*+Kw6X+l&mSv8eH3lc2RMlW}>O1u72C+^g50{-;sZ!I^pT`V%L)J`4LJ z>V*x-qv^NbEIQ|Rx4gQsttG9Kg|4{q{7g?1*rjK}Kh%?p3S=jf1rb^3)NRH5UMoez zcV)tiQXOvEC{4PPi-br6Lvl>@6zS5A$N2suRF28!tiNYK=GZG-bCVJkjbdSLOBXD+fg#HQYIRct@;#hL*pEf{u0s}^Ze_p$ zcIqhM!3CBysx}Q?G0W-d=z!*b>wS~5A)=~FQ1R*$^KV@}l7${AOJIm+cq82r}(85&~6mpif+4y4iQD!)`fxE4hjqJktmSlQ-2CnM# zWmI<`V}59-VcQLF@;q9Oybj33t|!Qp@Vyz^wk4u+-EOkNGL$GprD9fc9W!R88l1Q} z6K`iCyI=tS$66s8X1LXAT1k4PwXC(c)$mirR^ow>9yj?90SjVUE7V|mBMn%vY z>A=4lZ3AdmVKUiNlZ;dRlt@~62uaLJ!Jt3im`TYJ1ThOU6&AJ38p>2bpt+;jKV*`aAI>-`DMD(ZTPlZ&obNYRA|v?|-p?6A2&cCP0c(3Zo@ z%@4CI6Zd4I(Q_TzK45NXEy%zXRrAP`bHn8OhCr-jv-b+d!) z5Q_AEAro}xfjuUf}!+dCUX6rEt% z0ZtfsP8G(lT}=k0hq37ERJcS^u|mwXYVLDTC|7wq+jL^Y9>HQ#clF(;4DW?!eGbzBcynXJ{|iz8r)D9 zru}-!<><|ZQ&*7a3P;muE&|+2?vu)_k zvWh%D{pa#%xV~9P?!N)}bow><6wGn6w+DkowJsCM<5L|TpHBI?2pHKe=JM?E;Cmgv(+d%|5<7l0UCCwY)q0+8GqV$h6*j%vYejZx}8jsJAI~QExz_}i> zAwVB4uF%8e_7f!FMGV{0!Q)eJ9-sPtJw~Q4T1pr8ML@^GN^-B-h^nzMaN<%evB=e; zg&U(`??DMzdE*f`&ff##$BY0!5p}8%;0Xuz{3M&>l<3D+9|+V-hlXx##&V52(TdH9i9n2O42am)j3LaO+bCQ1U1?<3*7I9!-G$)g0L|i zWdHt5*nedQBRXvyiBePn*$4j2`qK*`zS0*Qb$&EQcZ$N2cgbKH=)aqZ4?wt#BKmXw#T1^4(x&d;{+JKur zA{bs=UI_1E_HtcImBCmp4i3ei;vUM@lEIz;NWUi(-2LYp`LoO#$nU3Iw##&otqg%_ zW9qr3O%Dj0{XG^6y;gJotf(Uw&-sGO=&#(hM?XpJo28KR>IK(ycQkZ8NrkKLWoVa- zE%D#D3_MHY`Hc1_`C{k=8XBi)Pw_RP*SLeZ$kWa3byo0~^#xV#2B#+Vms|)~0&+LT zz`2hl!nKo}A$Xb*egAMQ#AVC^Mr{Kt=*c#PC5%0MZrRLHNll24w*lJ+K{lQ~Mi4bI z2pYD%<6^eIBelaJuw>R&E=%ko$@v)!lV9a=EsnjUVUj5{mk)C{oz385VjPTL)yP z9*Tp2gOo5y+Eh<00&X@nlh0et=_Sz!ShUN5Oym-&#l}PsE7T*GZ+TGl1FN9Tw}!lV zFp>UMaU?y>Y=cn2bAHT`nW0QZ?Z(DM~^hTEvIwU|y-IL5lS)5$TLiIZ$B z75x?i7r(qA^RfzP_GnLNdZG^prVtuYVF})Xd{VnMnm+K1fm8CwN$3bSy7GPm41PRK zY(E!L*cc4^N2Ljl3--{1v58=wKg1kybf(*RD5>}TsNnoR>=$~nB@pXQohRk>Mp4sOzbTr<+a?8~tc1))fW^f=!vWjS6(RNfw?vu$kOdK14O1g~Gx@88Yf? zB(u?of3B=g&hb#vl!ubzW_ggW(?^oqa;bPtVwe-7?toZoxl>@IOMPhY`w`|wm-`UV}xDAR+-Q?X$! zLmSuib4H8!cy64cP<1eZUO$tHvpwUuY-<+ERaAtzO}m+}@EDNOl;_GSn~0&ZEvUte zpjTIE!Nyc2lAv&cW1Vd?;rM8I$nCA>daRa$V1h49_DUDz)QbUZ%7iv82_k8AnLEU0 zf!(Q*+@`zIBy>j-Oy5>37;Y`4<9B30K<;G*J0{a?|D}+>cM+%eKpws(hX`f!Ptl|e zv5>X%I1?z(qRLoNVL^Q}({$d5GjGTS`9o4HeD!-Fyxi;o^K>%7;n^JK>0Eu{Lsr5P zj|ZHw>l5;IsV=$t!$2Tenh8GUdLV1yDDWOX8`g!f@VYgZX_ZSB=x)pc&AWR9MMEs(WYm;i}jSq2{YtbjzpWI2c z*!kg@A7sl_2mhiEZGF)Euo}%t$)(?D06wbeAknt*IQ@+TeXYHl6eeF3j4@fx1Lcoo zDv#*Y3qD#ml&gVEkTw~eos0{8Vo7jQA9ZjPa()WIF!r!GJk=7(Wya~H?XUeivhQ=bZ7`A5K?EkMR{-9qUV8PNIBob)&(3Wr~3K;9a0 zNWQ8mbQ46@b!<Iz$ZQyB7va9&7-r(Hrhs?*u+5eotI3`@9x%p0pNpC%4Th_XAa})fMkLD`T81;o z;M=QAL-#5evr`eST(A`QH@QI9MJW=>XS>Z8C80WaBCOctE%2Dmzpi(8(J4QdV=z0? zfh_!ToBaI12_uZd!Jwvx^gleuiCzu{g@9c|cV&x^5l@0E+k%Np*i+%g)oCzuZ54SD zVk}Hx62MF-0RjH_eP_!(`540 z`4(wA-pnk18wSf>J!jaQ7$5AqEdqW6nb4V0$Cx}CLAu?|K<>w5L3@EYEaov=*Rn?8 zJTWR*?HUU1_KF%T*Vm6=uq=8-ZL$YGhQNfzDP|(ZUPK>^M7syv6!~5GY zL^6%9C*>HzGU=UkZJ8Q2oU#EGb#I|;*+}~PY8H!&E?={msAr014`)HSF@vF!H)*@8 zG<-ic38G^J^yLCuoVX^D%iSxDfybADn~E0Z8tta>ugu|Bnh1pW=z~w9ELO|#|8Q#o z4Yp5&?xTz8aD_{*5EC^MMs4n=(->Lo?bFAK>1rss`XN=;(!n3wt#Cng3O#n+70*n+P9tjX z&@rSJ44KQda`yN4c-};?UOP2$2~GB6oVsk&`okO(yYF*s%ir zmbZ~!*1?fnmso&}7D}*Z!3&%B_LFgX#eA64u19x>KPIx)!Duxm1kE1*U^!4DR-eU1IMCiUAJ7m>oXh3aI80wd|QfbXu#3Wn|cii|)Z}n>Or8OViHFSoi zSGSNLNn4CEeMrsQzLSOTPSVMpqtVN7F68rcEXI2tO>*`iF|jUmUTGN}T^R%H9vz&% zkI;F}44gNK#eoqQxWWr{WSc%;VGCEMhITUWz2XT=r9|E7#w~&HUNr;1UmM^gizbt* z5QQ$ z&^=^DGc?Pn=Dk=LnP5h%b7xR-xl~|lzL`RMQla3p=q$(xw}fNc-U%YZWqE_#+S;Z5s)t?7R zC{MVedBQdFl^y8*AK*&cAawMMot1VoWr--vIxIoq_SzNzU4ria41Xn!I5wppf+*QeF%!uC3`0(wR%~>HVe7a_bw8WdM;ITu084MG!?4>-Y!f%Z zn>Ss_x_&V}Gk8Oaw(ZQ^(+rbLYZuv#7#G7zs z5&m$O`ZHPA&vUDivtiHnSjaO;X9BO*lW{TjpyGX*OX$#mA1_?Uoku!cbW%F#T)R&W z?Ytn&=?aC)(eudaPJgcBc{)r!dWy?q`e?mFFhpmp!av<(sb37=J!f%|P7?G}BSU9U z;%~-yh_Y+xKX{G2u5`u)>km@=LJPxSy)2nBO6Xt!T zPacefmn*Gc_{s^=e^VJ%ms){u;CaD}H74+VP9O+pjbk)sfBhe%f`>e$GUp-HiF3+e z+g)u_7wrs9!h>X=)+j8W;0UH{k{HHB|(Qzo8&7pxVOurz-IDsvfXAg=b;t{ zYwU%jOMw%H^VO`@{b$J3p>;HV)5`zoi1w4zfrnIcc}QiEUqbqHQn)HUp|yxCC59Jt zIkk)=$mzaD3LVc--$YM{Ub9O$?(SsD-oKQIN9EMPi>FliJf*rV*huDB9pkDylR>6^ zEgkuWFqIZO+0?yFMtyhUUQY;zP2wHYZ=M)4@_)cO$dklr>}6u2(_qV7ahk304;jw# z!@9NoB&cBmIlVj(_8qTgs-w)o-pdr#-_(yL~oqkVOMk5|l?dBoXmBr(+>Hi>= zd%Y}{{2!z;|27llS{li*zDZz|B!P}Rq>B7!H={K=82Syi(B!q0+;IuTqVxBNgV9B9 z{l)|s%f5If^t3-nUT^S+hU}$+#Zte?gS$R(%;N~BktGV^dlWGIq8iv2%R;HnIQY7h zzpfML3Qff^AW~dP7%eXnQ|E$iJfvD=Y(SQpDZ|P!GUU<=N4Qm_g1>Wr60;+kz*%X* z&-+1wLQ#9T<(LljaR-@eJ@$kh%@+>l95kSe)&ZjaFoy5rIZfKaW-~_=17Y2wB&Mi# zn7@Q#1f}*S%s*$XVMW78e9c3up$DSaDFP7ReuF!0_WPqK;a{95MU+CJ$WVeQzfBsj|nvMTs{AVxWi(RO9>Z~ftGCLEA>DX2d>k<_%c zFvUDXvf&|;{TfSZ7@L7TV||$s%a3v1I%!zh=}mSlm7~RhnYeNYneWxnv}$oG_8cx| zmYN=*7nlV6N;^ed(0axpW1V=&5GzY!U5Om22e3JnKHG> zI5Sd-^p03SM~0`O!EyzX5+p$*&Sl|VN2ivnVk>AuSqir9{K52nkVn~*3()AuY6^i0 zsF-DsJJ%J^v~hoExriT{trK!XYT~G~zzZ|G2Dqh~U>zo;Rvs!?9;gNH;0IaF z8Z$8W@e#q}<%U@Ec?34{{U1wK&cnV5Lo{sS5Hqwf0G_HO25` z7DmAN#WvjRf7lx|&xS7?6xIl~$kb4!t;uM7#)=FY_|wu8QK-5pp4>1kqfdX?zz^wW zI`Z;Z9PYQq(!SA<>wJq|4W0(Ze;?7u{*&;7y%utt3}AihMS6Ln2h3vR=qiIzc(o@8 ztSzr_DwPBD`2ARX8OaixyXn+zvI}~$x);cI!ycOX$O7*z7zL_I{WRu^CmwxENrd@% z+IBS-K0m5!8FFZ#jdEE~@z{@1-K0Qs`&jt$zD>}$-&1%YpAXkwg>WLbVg)_#W8vas zaiZE%MO1dO;2iJB>^t#T(Df`EJziEadfM|i`Gwgiw@<;w^6CJ`8ki-6eAgYps11d5 ziCF?FURgwX_pYJLxB$GiwuxN#I!c2%YnW7ggQjcB;R7uL4Az|v58e0BME&_VWAA$s zbNfDxot6y;YLNNzdnfbi9~J|y#tB2#=31|Bih>*a+_>{ft<>Yg%!D~_8X_*fnVD)lSKq)mIs>^a$JonFU`d$gacw8?_Jp*Ku% z4#$0ckb;VZT}<|cFuKw<8k_pV$ok(q>DR?!5Ef-d|A96txz`mvs(%q}tx|evVK6?I z+D&e4ucI<&qM$FUoCKRilCUl|8z(gHWoB#%V6N@TfVhdPn8ycaQkS752s>CJe3e^F zt>pc1&8jxy*?ffV;5}`i| z4O>7FFfcH&Q7}*x6-CZo)9wW976q}p(YxOF{r>I`#~s(dp2az1h{)b&ueqN2ne=xj ztBg&E$2>J#l!=ccI{X+rT$ibZqsk=V>Dz(0%9|OjqugL*z;mj67m;r4|ru;iITy||3w5=RM9i4;Gyi^I)7no2v zS09`-M;)?TqNq!R1-?-r5S{TwBN#9QHIEdmWd7$0_pkhK30i z?{SCNEw#elrEa(=*BolAm8qP27|uVh2=lZugtZ?n(Am!w&NS` z^{K8qCrxJ?`r@)T1E6bxGLCQM z9pVy!{N>&-HMlQVtfp{TX1LQ`Y<_h2XqlDf&azz?RPgYC>v~lYP+vRtXp4_%b7j@b|+dt&yjvC=R z@y6WqgJ9XKWUeQ_GBL))g&uIGty0vJYo-mvuq$40Wo3u(QAZv}b$UVM@Xb^x48SUd z-(*YZ4l3DYk87_Dft5MSsBgFrs!SLNUw)-=BVPydsJ2xZY&Sd6W%oW)xf}8DVn&6q z$JG!Ynv8;D4>pKIEr$5%)(Dus_LlJD^k|%)r39P1Y^aM8+Z~JgLHLa)H1fw}boabY zq}x^q>BB9sYaa*k*HXBiyxLC&=Qjty&W9D$Tqy>R9yms}J>=`DLxL}s9MuJ@u|!n! zvlqVk7z3iDzCs%liB|@@!pX%t^o)f*Ug{J9|0|1XqaPl=s|rUNgDH`>!#rLYN}!7< zrG#L>sjK8@`2~9UtsLs~jsS^bJ3(~R6RCj;)bv?O&*hB3$7)(&@u-M?TGIzF^1VXf zFslJSm-57zdU>!)+D@0aOX9Cb zV>3Bebe4vf$>1q=6x-EM%=OVT!b<_Jpi)sp^Se1zMGbJ8S4)!%)$sd$Pna{Wg#32) z!m|dy$;{s;DNncZXg)23(^@f76utf^)i`g$ef%;V{qiz^-`8Kb^rw_Q3M(Ml)1;{0 z@!8mui=XjOG3%*#{+$DEqb-y|fK7Bs+-b35 zUIxAtml4^bZ6ejzJho2=6P>-^AsRI)3*P^jP2Alb1q(5cKbMQdCuDuO5yN@tXP+Xx zc8|6Duzog-4PQbooNwZ0?kfO&rJ>yHj{$Cxxq0}BH!&mDfv-e^X*f^Bjgn(>I9)>0OIox)N)E|+n? z<>kaq!8}|ktQ4eYZWm_1c)+<=<-^z|`3{>F`h(nCE$CJ|*gR-24@J+t1a8f^6JiRpm_uwb;D2vjvc#|uRccI z*94JE+++-B%7sb4TScvTF7!}~38&bW2VpikB-1eu3L1}z^SZ6*%--xAP}Wg+=xa@< zvH#aA&C0^SbAh5$uUWr$lvw<1c%56XUUT5r(_u|lLmwaN#r|9}%m>ld0pz`N1zoo< zojvd#=3?7}IB9`z6fSHlK>w4aZ3wyHWMiZDJIgmO>5>)bMZI-)V2*b5PaL`^U@E&d|KHC+A;gd!a13#7}fmhh) zpOc?3?M^Db+q{eDj+lj8CRlR)`);7xds1*-KQod3<^6QSrRg}B5Mhn#F`6HDiR$Z& z5*^A-LW}BF+U~PcxT-$|BdVs-H9Im;JW`p2-Mmfd+LwaXg^74qdb{YF3v1K8-$Jgi zZnzsK<_J|cpNp=(V7(&c7sb5(?DcM^9x_d`-yhL-t8}-!>M{7NW)1mS%{C1hfEqrM zXjXcc6!lNROU-qH;>~@uCp*TcVVm1(TKhwbAmTMUdZV# zat8fEO@VW;$K^FnpeOrLa5!rVJ9}^B>4H1a!kLdVkuCmTp8sebX=rK{)Y$${XQ?+< zwv>`Bo0PHRstu~iD2vCc349_xic?%a`w>Hp9%n z*kPw1VywGpg_;f~tQdkNTegc{&*I(TO`RQF6Xu8=N4S93s}&^v{8Fw|D;=K}d2)tw zJH*o4cnAzvfGMm;#J(U1RJT@;^d~98J=P~&I8c(RF0-dz-?E|ck|Vr#XcA=_4aLfg zP+Ie{f@(fU!8XNfw9kYc#OYEb7JduIu|Y~icBTwYU*L?*{Bk)u*31S*F(a+1#75NZ zbdf4qXM>}R0x5Ci$W8P++RtK0Reo@&A?ZxZ34_SF|*tS2pd2F*d?+m+jZ_wa) z$D(vB-&w>(45}nvS&@)+U!A)`WpG(u4D6AABDQ@hfw`@rz|FiVylqgxbkS?hV16!m zn`n^Wv=n@Nv6Pf&&mbe&r5y9_r+AW3#Q9$>z+Dx3@hH6FskTf9l_H2V3G$ z{(^C{sNXMIY?+S@*4ue*Zq+ZL-Zles*XnT!Z_DYx*LnD>YYz7&M}@i_&c~s4t(^C& zIw8Et14|>;i}t)V!h3$<*!R7qAiG%st8M);zD2Kj+Z1gaJWQD@UC(3H6-zGX!uKOr z=Vf7e-E8tmE)$%W^lNGeDj{e04M&IB63q!^rg$)sAAxryCky*O7~^<$#3__!B1VlT z3jICt`GTcFwvj%L(@jSm6K(oq`8=wY$HUVdg+kk;na$39c~D7MD!4BlCAdFGhsd1^ zsd&R#PBT3oIyPMt-j{!(dc)_6yNi|!&H+633fNA1k5EfIxRTGu1wH*z@v}*dy#+D81XTE9&&ca z2rG^ju>V(Im=L{I7%8CxsxpsB;-pF;(P1Kfy_QPyZdD7%zh}av`#dIhN^#vwA01JM z<)OVSTPPf?$_wM%Lm+vCyeZhUdH&wWvga0xA z-=bu!BOaJ?nv4JKfU>Ir&sm=5W;`2$^ShK#^Zi}!g7+9mQ&PoyGahh@w1Q!^i4Oes zdBO$O`oK*I8?g7U;u>0A!J@g7+}pjKuF(s{GuA9#OKmyLhzv(!wov?Mt0Xz~ARmIh z<#G!PWJ%eGeAvtD<{;t}?!)X5^0+7;jkEH(kp_P_{rY_HahN8oo&Jrht15tovXjE} zZN}X6qb?BP*(SUiY6Cez_Hbmp6kWH+5jKC=O)hS)6U8>B;;p4C$i&lgIn7b&INb3W zXFK7CFn4wyELKS2bUta*5LW8T?Bub3_F*wUa6^IU>{=d`*XW8?nkR5$+jwZ`Y!i=s zbxnMLh0naNj~5B!Tt%X10L~44IeRBFv>f6AO68KGAG?fEWmz#L`)aw_#TkEbeHlml z&G^LKZp_A>eB-+}cBT*Kh725pZj~-P>Vh&i*US#Tj5fxCsW*i$Yuur>OBX%#mIwp0 zd|=7J4ld$XK8`pQFLd0!Ay|CLhd=ul3;ECX3a3vLz^_^&)Tz`oe=p6&ArXWGDaLY_ zmh-s4c(d51ICEdEZ!WIM_aJu5(+S6@f!YV^aoG?14~>Nl zUaPr?$G_xjp^EG1b%nm<_a%pHiWB{qZvX*u~oP`>rOo(4?F}*!67ar^)WU*%GFv*`RxuQM9 zZAYYsICMS_&R3j;k)}hq-+!j#;Z4uEEgKKh*WPJ3+wCp4-**%3U7ZPIxBC^92he%Mc6b$$AfC+crk@I@(WayTe z=)BaHct@4dAFG1k<^21^rtut+otpvL;nT=imWUDknQeV#Pw{RQLl=nFva-syZC;|o zZF8He|L|zk8r0l4wtQGIDWP2%<3wS)hA@5J=xj75(b26r{R% z*z`^i4_y;1>dB|NdH7H<)6H_t`{s3aS?Ii0MR++Tizd~wn4fpiBE7l*t~7w%N~PHlh@Vd;~oWLQB`Ok`cgj<_^_{pZ77JTE!)0znA6j| z{YPrz8w=g3?}_>Gy&pFv>YZ`tNZeJ$e5V5174G4%UVoCgZ1h zfP%gP^gh!-4Cf_6#IvI$BR>BxuC}p;Rn=N4kl7-rZMgTv=`2qRz zY~#O+|HEq!C_(%OZ4l2?g9AroU}c*DSY)a|^13{{I9`@BT46xt*RrO=NFR~@nK({` zefHb#xG9?4Rw8U!$s#=5szs0QwFuMMeWg9%xu|Z)NTK^W57`~tMMlp z0<9AUft18_kaOiXkhh~#$bXCf*iqX7xUY&ZMX!r2+|9#cZ4(h^?!$FYWz9!%og&j+ zxk9pjKDO4cYtBq)a;OsF_Y`3cZNcD;{?BUMbR< zUm{X_&$Ap?39)9{7V$WCrAR9%iT1Pjx!sF+ysP9Zx-w_Fu&W^g0)m@p=BqPAI@}Y? zpqm}bvqlB?* z!(iN=31p$133OSSV%vm)*pm8?man%4;hQ1ct2sc{+;1SS*lQv37FZrzz`_X2Is0Bs z+;GP|^!^79^B(s@`6>t4;o<vX{#*UVvff^9+LR`RH1BwWFPm}KHeCZxMTp^_=)?RBMd%H!eUsYf(tA0I&xOe+uSIudVh`TTu22gqcVD=I*jC1WkapqI6B3%Tl_oG6PB;j z#95u=sE7MGGFEdM&c05#yX+V~md24`(`N{(%aTCuQX$=RbOSk)TukkC#^Iv)ODw$D z57w9{;>tCpgrD5c0qnf2uw-8nT_>Li4#~GDchZ~%h<)WwFH68TH(t}j!7Oz0dp~>~ zdV?H!I|_F9)yI!>s=4K*tz^T<5bTOMM@=^QP%4{^*`Ys#JJX3Z-Q6=~UoE5p-<;6A84c#gETVrf;YR+&!m&7v1aW zD{dlKx3tr+5I6BYH9K%#_=il`D2<&;4zT=-6TW)rI&9*(5n%Yj7}Ikkh38Wbl2n%S zbwd3FCn=dqzSgIs_TiyqNq9O`?<)}PU$UBfd%?$XtD?Qv*Pck+4hW2`9p(Ct>9Ru z13o)Go(^6zlpg2X$%#t|psULgr4w|axK0EED;=Siek-k8)elYQ=TK=q39?@|1LiE~ zjqy7k(?>}n{5B~Z^n8EN7ZN%2dV4kX($6KcJdz-9t2&liPof%Qys_+lD!8s&C4A?0 z(8a&MQohf)KjPah@mQ5V6Y4gsr}`3ygb(d{2+vE&tzW}%b6^3i3zXtUS3l-t@(*!o zF|#pqcR8sao(;o_I|OfiKQd;(7i#U-LJG!uqW)rY@kx)#s8e{C7;KmX=1L=lxR>`x z))rShAi7Vg-TR<=;&xg#ngieH{yfh3TtjA^Oh*4Z18Lu8Gg9z57sLA%2rF{r@b^P! z=>7aDeKfFyZatk0S!eQT(gq7KxjzuU__xwMdwW4c0*A#C&uF}w6z+=|4G%7`D5i-u zG{l|d0n|IdxhYI=?^37v&9&Unar&(P*AsvE%;R)y6XDf9ehCfAI7t6n{KqHbj?vnn zNcbEjkPz=RRKa>YB%~cCb6zIFp>5@~s-uz)3-yG1Tacz^NTSZyFZ7xy0jh5G7fYqa zK!C+DA^++hdQ7|0O)WiLC@$x*CtuRi!K=4Sh zFTB@)1%fKHV>)_GKNg~{PiH}flrT4V;#D%4QS zK7Y6gtC^p--x{x`hI8YmkH%Dab=)R1m_E$&$HNy`3W{tpery{@Up!h#QY1z~i-9i4 zMhBD8(Lbn6cL2;}O8u=7@O9}TqNg@w=n8tthv zF1^y99#aj#mgWJdZ|@~E-vWHOUK{SeC?~)2ZE<le*#>bniVEc3v8p=`^ zHwH0F{&A_mzvx|npI=D~IaBgmczU=1qttYUe2u**c=ht92}5%5R>CT7P_rkQa3=@i zD>!avuVUJ#CLQHGHAqyB9u1acqT<29u)c0TE&MnVDz?kv^H*hbN^gCN;rIMP4}Z=BW68i+}mSTTeRADhJ z=JU-vBm}LSOlMEX!bxd+xq7`6l4za|g^liXcHBH#T{sc?~CNZ3Gc$QJ!1VP{H&wCQnwu|G4Q zOZ06qp|qLSjhuk(-(ty!*m z|K%|uJZkn|B3RvACF+xs~% z`IMxHRm8uZ=fVh)7V%qfN^m>RcD{RyNkVqnU%ZA_D!`J-pM|^EJH#8W5j8v= z$8qL<-q>c;O%>XY(fc(Hs5Qn4hkhSV8$$ZvRcj0MyxJRQnzDR|9aeBM-2`VX-9#%j z6VP^|n4WnPEzD7d0XNX)Cp2W!%^=hH#;@l2g?h z%bLPQ{v_9prjtwTUskr@p2?vb4nkwEeC#!J(Kb$`OP{Z1=E~UH;(nS}gePUeD52U9 z+xHc@eecx6UGF?mySukAU{5Hetk9IKu~L})eK5&*kp^xi%ju6uJ6trv5-whxOW$-> z3-^BIz$Z5sns%m<6W?O7>_hn8+@%#x_|4p&Z9aAi_38zHDAqfmwID=Y%U;~dEYkpwIo#XF%^F9%faDi+$A zvGyRYQ@p>A8+0&ZZIHgBxKt;X>aKw;;6id8-T_?RZWmFd40| zi_#-QkCT&!Q($pvlrV1DK2pPEw5HBjVO^ODN$*a9cNP8U3b!p(fk|i4#lFJv9rwg{ znRIsY;Z`BgXe4LJRIbFyPsG{pIyu6EXiloc3*+{z;z`b@S*SYOh+1zhCOnhQ0_QYQ zz33g}nRpn)F1tvM8}tQ(cw;7rbZG8e=x;7V9+hhpFwf1Dthjge}utEf7m87F@ zEE;E=py}f6M2lw=AV(Y5;`!8c0_Yf2@Nz8wf7hi|8gZYN{4b$^oC^n|*a z#^BGpg~a=D1x?qALpQY+YQ07Vqh7QAFPqf|sXyz{bA4!znfx5GhDl^|t}!2UR zDj2;o74iK66C%$fvZ-^_aY#!Pk7!xMSj$T5bz-$d>1tW7t+@;_Dv9qrrJa9LBV}S4<-N;OmNGoAv23CXv~V zk;6eFE4imdCOAxRNAHHY+#}X@XC0u3MX44f?MoCk_LIVu5+j73Trt-OH?g461Gy~E z#L6Crq9baZmnMnKAv^bG0Ctoqlj%0$IBb9-zSH2F#s7EFES4BziiX+4QFDG0*A+7y zjTMcMQ_v^nwISGdn<5^oR43VBhLyts`!*~RKD~>^mqrpO@wT1|Vh-7)*TyI+?Gn|j z8ihuO3~=44;(r~o7jLET)a@kUULf0P-}*-HZHyMonM3xPf9=LRug&z^@&V|u#uD57 zA5ujokv%`Cgq_Ds#Dmku<9q%oo%BtfYBPzqb~|g9=52{?t&j1ON4(MvXFKu z)Xk|DnHG%2k!*W5>&tynfL%0BUig@Ho0rp`yzlN-7ai}dY+g(JhnMAhDLWE-@#*n>CBD-!q2#?9NkPs%3J?GT%%!%pbJafnrZU4|Y zJ8HNy<3iB%xib32(ZBfVT0^|HdL+gzwHE%z{J)DeOd^ZB;)VNHc5pA4L^iC`i#=v< zCQ?mCs1f~+DxBAb%YrT5Gc&~#7F8r7D4BIXHPH#>BB6L#C~B^g#c%VR$%PfyY2vCW zIJK!!+o)vHlPms{$neC&3KAmagNue8p?7_Hu}0NNxbEcxnsz8f zY;w1Y?roZYlO`*R?`<-|{3H%d?R<%@r9Q6fWJ7CyW)WHDklnwline@XFd3o2g2!`j zP%?&+PZQXR# z{cv12E0M19bwR~&HI%44N6z0>V9$4H+q&64h}THBlT;DI`hViGB?w)MQ4eM zK8L);^H=orqaXB0q%4lv8G-%#%oQ}4MAj6*I$bM@$POlv-Ju$2Ij@$~Gl}fZeNQx) zS3)Pdcwwl)ZyMEol2kBn?9f#cq5aErIF*_Kk!liL)y^`KKDvN>iIyUgCuT!Wc4Dqr z-&?=Ap}8DODqynM=)43@R&^slNW+uTczMGbW7?Yz5qM$=Df@n493e^fZfANVvV~E=B zEnMDi)))LQ^ZzaWalfim14SE7ml7kTSty;?hYPr~k&G_hCRSm-m)o>3(a4t_;(vTE zG4s8G+#R`pd@t2Xk+{FdXd(7N0SrH|S@?aiN%+U{ijf)0P5lti{IBE16fcDUGftCf zU0=U!;->DEMV*fGBFF9s$mO+5sqES~_@;fGTpTbSyoR0>rl`H7vv~Dm>OWHaQa+NlVJT+}d zF3SygULeiQE^{D#Tq?){rd?S!9p)n4!nnbUcXMj205yr_ztHn}>2TM#FX{P0htzfM z(6@Kog_b<%$tea?NmaTQ*%p`qJz0urS7j0d$(Tq2@=UwpcfH{Jv_im8^Dv;22*C-kSliu^f5?py}8KHksm4!%u;!yj```b`2( zBL+@YuOZvEwvxLV07Xpep1>@s`uY@53NRAJ)iuz6ng8FyFg^_$M5}4XH#TCiX+RL?3W||Q+8Q&1Tj~@XgqM>;HomglZ?f?pB zmr@z2W}~HYiEu>wy6~!R0VIx*LbGWL>HXC~U^`|LZCt9z(}HEGn3p$@Ty2~~ zCv8rLXEwv|y@mccgOfk7@JRS|^go0~s0JqYrFV;2> z1CyN-X;!K+_{mL!J5g`Mg%i#bi=5fOkItej;g}v|7S%A5^#Xh*#En^09-Ft&tM^aP zt8e%yczI$8JvQ+HjjW3V+gn$}_bX+<>Odmge{)i-cK8|bWE$1GlQTu{D|^A5(g)%Y z#XN{pwqp*xgD~_E4{@s(i;k)?ckro-aJpj|StuU`@0`DiLtaWlTvs$q9IhbxEY}+% zokC&p(05!!FGVO4y%r3aMdi!?qfre?0mDZNh{gPA#4SA!EVXp$*+=uZM_PH1XEd0m zY~ILCxKRLYXFEl0#z#1bFL8MIl!?fv>?awOpO4{N=L-Gjb#r@}MYV0cp785UIr+z; zy4W>GxSOLwwzTGBeCK-M{EQo1x#dU{d&vtTFoFx(;mA9M9uR$XRfJ7Uqngm7=T7Vw#y4-c zkc+X&rc~E?wUEjN$VgcopmPuQBHlEX1yiuhUxO~HH1E1w>T^rFFgOo;@~-d=oSsn*88CR=L$%b$%XV?&hx_2C7ISJJIRJJBJ)(hb1L+@OJRYAuoi;7DquS0?w9@Ij7XlRJo;zHKP>j|Z^$mcyBkHbeJUN-(*mQq)T_3^uo_ zf>x(kv^yjO+@#re7<}=BC^OL?T;5q?)A}T_#$gf6KHEiYy)V>u%toYTzp|D zMRoTpk!gx~c*Wg}<~B{^taxYKq~bx>9^x&a+RPq5_MJi9rC9pyu{!okRwz77OT}}$ zD`>^da?}cEvUaVdIR8d2_T)uly&-<-0O7vIV08ZA0)r~1bMg~x(b>Qdb`_4M zbG-;m95fIPgqTp%^P}MFw}--wi~_uOL*-SS;|}4>xh&jsQ zW4<_cW-gj-01|R}v9KVP={H}sMJZbrh+p{T!R_OgWY*M)!yK3`Wz)3L4WIN9%$Y5v zGwcA@#RX%nq#ak$uOBG;>p~p2m7ZjW4~o@Csr`oc?C74I3D1gyO@1B1&f{Om>W*2c zqoB`I2laVmBh#g#Cd$$*l``^`-6+0($P=34Ye@vtrB>WpD$ca4C00zAir*_1@^|@> zv&@#tHy%JATvHKF1uki*}&3LqBky5_~AGzFtJHO#ef!Jnw5LfiyS~yMdC5=R#*@ zB0SpHOt)+-p+{C_W01KNjZSbSTFi=ic7Bx*^QwjmGA}^Cgo8pV8%%g`fH&yxK2Pf| zePK~-9#H=H9i6M)PSY1;VU?i~9Y4m2WUG&c+1GE@X0udi^Cuq3 zM=x>fjYN^}fgFURTevNY2`OL3L#C`q)ce(BVSE5Hq;6{qmyQ>5KMV75oHs8PZ&e~G zQ+UV{$GIuXEf99HU%Do#TJh~~OSx@KT1p?Wi5sDDgai)H!RG<8qBk4IlGnRgOpB^i zBOUHCU-)P7e|ROcmef6r#a}}EkX8DTP&f7~^;sB4)2Fk({zbM#ZOnTzb$t|ki|nR1 zPa4rBOj??6#HzIpjbx(l6yP_VpyNk7(4KsXNlTtvhpjIsAOB`ny(^#0q5qV3l&a}x@9h_Lq-H80B()R?rizLvi!KHOTu)iY~J z&Zb(da=V3#xWuDU<_ht&jeWTk)?o1AQHu;V$xf{s@1wKY4o-IgJ~T*9t4$xO&? z^`+g_w)7j{WATAmOBJdw>GkEO=-7w^n0WOpRWYle<Y0rz{@)+^53T95J2YVnpN$(p!t2JK{dQDR4JO^c$sieE5A=;#dY+Cu^}9M_(+f#>49@pT%cpV z4MOE}?)u^e>gKzj3|z#=AF$o$xEs5*m^hZ$-bM_Y2yrLxrq-_kLT~nulb{Jz49Gm zofrZ4zpz1+LJG%QeeR_GEpcErc@!DZ7zJ++^V~0WDOB}~g^H(#1Ov}*y3#Bd7Jipt ziRQ`}VI&P^f3nEqwZT}BqXb5S!-)LqKx~b9O-2VUCSMX*>CT63(G6D+EjNxtub&EE zLd}TD93=fO+d_y{ANaZB8(F%~mYjuj9^!ZA5Z9fo%iaExASHYxmy(lU!IksG(xr^s z^nE7OyjV;ey)|f0UY6?*kN&8@`jbJ#YnD2$EAWNTGhXDCtq&>iSWb?7O~F$ZcJRT< z1-P9lLXm4Uyq)=ueC&!Q5oJ?p?kqjfp#&9n@L6 z`m-(fK_dqt^Lq>|dj6S=oiUi^YSq(Wqb9=M`lVF#P90_)7>tRlr9oLI0q$;4=R(ha zA#urCkjW{)sObKn)Zhl6V~ij_dm`~RH-wv`k)E4t3GXKxLT!^R{N%^)A>-z*=<+9@UT@(>&CiFxg6CYZ>Of)W&}lHBBW5 zyKX3bncGecF0&Mb{Db7NpC-&?XTK9qXR-t5!Ps{^LAfhW>3x>65+LCVQYATbk)8xS zs++-M<0lLF6!V!#ZBhaKzy!EBrIXWcX9EEVm4qKBo=OXH%) zCj{3UG*4!Ip;h}$;G@P-ZqtR~*pMs74l10n@YPeI-mru|R+x=5#KVRacqcb7Z%;C*QJZ z6lA{6hM%b=q$h8TKSoytMdFkwfriv1P?tq%*r8THmzGpSNhFEeX4QV`s{ zEUt+a2IS+{Cj)3zSDC0|ts9SA0<%|jt-)&O7drmI5tje&k1IP2X;+*Zw|qt(qzEkC zz{^m4;g171VR!+kiCLWJ`e-y}V;Z8L{-&2z2Z**RWYLh=EbPhW!p&g5Rg37< zBfzD>ec*)WcQ>jK0U>SGbbV?yc^=ItLWzAp@Y?9j2F|5YfOYi*BuItJtiKkZ2o9g6Ya^L z>NaxEo(>>YWAmVM#xK^P90r~Tb7|Y3o_|S8Wzr3LipzN6P#>cPppd4!*w0eWYmMn zI9Ico_T-PzJjm$<2=aOULQfvFAs)9lcaeMTHpIGjCOl=yh>s6m6E=SKg`r0uuvYy$ zG%el(9gKkTpK=c)J`1Hm;}L>HaX{+M?D{3TlSLfkwL>RZLqg3eVT>w9!)#T><-@?$DJnU1yZnAewZvtu5}9Sp z8r{6a#5vOfY+IawKgbNGQzh%@%S|@WTVxGOYl`W_KMoMVtfkM)S{mD003M2;P4qsV z5YF7ohs?P{Ob#hY(aI1D-1=%0`N5yhXB9rS=WlZidX=zx7z$sOK6{Mx||Kf4?J>YkI z5j{6q6TQT( zKvT3g3KaYFasP{D_Yq+ula}h4w3PIO4Rp`2!FWv@bofw3!xt%0mNtn^_B*NVgTr*7 zVm#_?tQ48rsl(HKqp^wa@xr2%I&QZMJI@_44PB-Gkh=8MWLDm2bg<9jYNUPf53`m! z?)wn#;a;lnIO+eFpBLxDSzkqZgreA}G!&k-N#ZlfL{`bp2BU^?RNbUS^naNDXECd4 zDt;MvjSN?R)%@mX0Z!Gd;MA^;6Pjw-gyJS2vLrI#FP59ix?{@Ig!0Hf+}103pgPph z`0QYPs{Uj)Yzr!_4+*2nkP-M7in;ZSxbMk>>>D`6^vPzLS3I% zbDw_Yz&lqLT5zUOpv+q8XVROyu)+!6GHEGc%TD2+Z{oK~nejj0#EWU!5WzBEel8hI zXS@r5^o;&^H*T*`xW^DmnY0uv5}F;XHOa&Md3ZQEkK?rpHK+x%mQuvgG^gKCc+IS( z)67~L*l0&On6;$Btffi|MY_2<4~LZ33$-Ug;ADk7h!kyT6Dff1?|Y4RPJAouRqRC^ znY46*NlTqh4dg|4f1)li16sFHPJ>BHo=jS5f3sLnWYSW<6l)w@+9Dpjo;|UQ+pK{$ zlM+E*av|N`_b{nPij=25l=3{BnE%>@}T357qaRI9tZAS8#-mE_pK2do1XFQzhm{o#5Sl zb)sGr0YwKK$lMwB@IK0%9G-9Y@B6e5L_s`zU;AxqaB@;78%{*R%tS4A4Mf7Vdrlv?n^K@N_N{T&PWCItx zW9=ZK+ME=z^786q(d6WIYY42@BL}`j!d?YkGM#-IIJ&yBiGM7%INpO;J!bP1*!%aK z9LwI%M-#~kCy*PfPfQL*K#Qp^aTy#5HK(=7cu^$OesU(ZqZ}aBUqn`37z(!E^-08v zFc2>dO4NxsPp+U;+!@yl+MpkQy zAY;}5@_gM`i1O1W)1OAL*178rs{^}H>ycmIEQ`tq`jEC4HPEgd~I{gpfIe5Rwo=5|R)?WlEAL{PyX4z3(4g zyJtW9JbRwjx`$DeeAx)$GtEg^B*Y%8QM8!5kAFYOf@C|!z`VtrYJy#$ZDUTl-Q22Y zUn^?;B1Fls(UfOrj`3Eb>C!GP!S&064qxY8vD}irP92U4Gb`GdY=zk6+}p=oYkX=q zC((Uxt~|HEl1BBh!^|%3Mr^(X3|-A==1D6&OED+YOLoW{ZAOA38`vhAlkq+WIAoiX z?4|4zjk8e{V?#|5_l}!kQlUu}1zEORBkV zjCFU-saKmh68QO!=BryEAC`1zkO@vySyJdN6Q~DAk*Ui73_KY@GkPZ?(Qzn^SB%B@ zW%^`vX*6DT&ZS2SP4KWjnuw=UUNyfbo!4kV_2cM%r9d_#4pAdfxY^=Tgco z8w_-crtAbOxL=Q<>k1<USP&ZnO&3@u*`XzS}p9GGrE8?A(RFG`wABVFBKnHEL=_z&J*9@_JRZ`^T$ z`LxMbp+j#j&2}V|?~SApM?LW3bSP=GhhklZE`9AA z0nfalbaM3^L^bNuaYb7^(3nfRKUzUyPbB3B8skz(DBT-AAKd<%ED%5Y z?0un6yPuE7xruY>{M@<7>~BCzo?7BCC*jW3an)eSd6c(+pf{2?`AZ>O6{G3XcRT1W zizYeev8Wiy9VopTi&3FbbodQnnj6PE-K_A&Hj*~)8-o+`=F-rcHfU&$qPE4x+|-+G z6m8N5;^& zLVH-ZaO=$(&WNszrafDMZT8W$^@%f<-i@N}%fK==mo_>AHtut%tB(-VS4L5_^k@{@ z&fzi+A{#tNjG}e?guZ@9G`V(LVePPJ3emU5yy0`{*GMyL%#Nb7BaM(B-Ur5qgedsg z!q#sNMbHHacqNa6#L8xN;fOiXK7VH|*M_0|iab=KJRv`10E`9&!uX^ds(;%cV9y{- zxL^c#c~#6?uZ8ycz2Pm2)kM}m12U93tnjYZjHSb z_NXrj$DZXLDE~AQB?U&@(S{M!`2N|n$3%n?TSuZ@(G)MzG+}gN45E2KlAFdrFHIjs zs@70%FoaRc7&Oitg}}{L+|A?&MAlhATA$0ZY|z1RKYi#7mxpDwK8jmZAlbs7pG%>g z^bxz?5MevVKron(uiZlty}$$~`woR5!VEHVY~gT2WP+Q##$nCoFcmk+!I4;YXfX1A zj6|%l4g&g`w*_jVL?imd=Wj{mBm{mJrpR?qX4s zJQ1SM&RiOG(cmy1c6wUSulIw`KV5{i`(Q`u5ESs!*gBr^F$YgfFkAzRKO<;**;5DZ9|Cchmwe%H!pH*} z$bE&L!J4LKjP1EPkY6zxo>z2{Bx{35n+C&ufibQWak+_g=9nPVMy1{Vnta5okt8`w z2O(>$;XFgM5`}UjXy+dt?x7LbU0?{W8Nj1FYd8#cgy6I>jHU>2YN#%xzJ{T|QU_t( z(eSF)f!Wz$eBuQ*+cg6pwTw8P6@+nbx!*EjUyMw#MBu^+u>Na;fWkoh>2kwDDFHrz z^+VBl8KgGaAW&Bs9-=LtD4pVikI@WfZ+tM4f3QN!742MHDD$@qo-E{mxv?u|4L3x{ zLswXK8Y2~Mn7Q8yxdEQ=3s?o0RtJn3y8=aWlTZ;Bh{8};2-ZwSh=UJaxh~}{>c*qE zISCCI3mv|B$?qSHrZ2M*@|7T3w`dl83;m(}C>UXJ{BN8ZvgMMXAu$|MjfC2UWVHRa14VNrP&UsRwK~ZN-MJIlZe48n zE?qn+Ovbj)+o2jR14ngtag~*<=45h1MkaKAd ziaSf0=GPghN?#0XPc`VbF2v6qGYB5Uhj>8*v<5;_QUA#clM=l8f@M#gI?ucB)k{BVhL`ZNFJ92$t`;zv;Q-@ z`;1Vqb~$i&7qSIEn15?7B0K*vi|f15+0n&LOvr)PA1NeC>_Yjo-tg?V2l;K1$eFem z$9Rh*p2T;%ij`%djfzKH*0vG5SmqMx%P zas?>i&o3t&K*gBLEQ)Q#r$R0|%EKy|0j%Tkb|lT3kN79sQ3Y96H*hx+9`0w`-t9x= zy}e9g;1Ku@`oX?~kD(zy+0W@lC|LNNt#`JE&5;Hc-ZcwV4lh_!#&~2OdB$XWjeVjBRA8JU&GDmeqtApmW*3 zu7x8fZ4+OLGnuhjGki_w;Em}v80BnWzePi{5ZgMBou0HAnOQC@uanE6yL7XXlM7*U zu8EZj79nJ6E%(fvidu`OY`O&Rj8Xhd@L?57-|OMmthLCTXoZ;M40N6c_IW==d1w^! zrVk_4E>*gm@*E*YB6|wz`z08<{@i2m&mau@_Z``B(;-@w@d3FBGf*p3Ltcp)%Z-wv zWUeORUBgX)Uyx@Lhy8@ZmOd=@NDN9hdozcdlOa)ZM!0oVI~rz(v1<+HXgzGmVz+i9 zdBq;#qS&D*eQv@`kMyF_^|OT9jCY~MYDT%b&}mC$_vK8GH-7~ac-V2P;`2mI^WS&~ zUidQi$8*rc&51nj`ya0nW2XJyqi}LD>sT3yWT`#Exq)pk6KrC;^8%1y{*8_1X5tE# zbh3}3lTr1rj&*fSLb1YPERCE*v&5A(m`5W5$iQ}G#ox3 zWYx~I;DAHSXhi^QCg(Bh-pL5uc#5@LeGCbyW;WTR4lc9gd%9r6k`A1h{}N`)dg1cz z=gYx3nzncJSh`1GWn=eWlLI?uZNh_bJ8#JI;@&VK4v~uS8 zNTwZ91F0L*(4C-9;u=|s; zFDjfgjD6z;lXzpmUVLok(^Com@EujsSJ}VbA4GgGyk`FVgEHwktguCbs+Q-nJC+@& zjgfe+vh_s|>)`oGZ$bM&rlg|D9(jQMoUI%0uqA9|5j1wR+e7ImV^uZityZ-aYa2YYg} z7d0(@$b1$5!2Mr6yM6czVqbQ%Lwn!jn0g=V;29^3uVQVck|acBcV*hABf@fSMHwdQ>y+UDMJ$Q|-M{SKB@Aa(e4ligDsyBQWuQpk2=x4Z#v96exotp z3tD%(!H;_I(C>tlE)b^FfwUuD*wpN&HEiGJ4UoF03aOnN&>FzE=qKq2`aVQ>);!wN*h0Gs;W82hmi(h2gNgeHN8mJ$v4!hZ&Xv)_VL62`HR`>Zi ztvDJ%yXBzsRUgUCgW5Vm(A>*rf$6 zunE4)QdApZH0v2lc-M(Y*E}|)v>jFXy*Yryt-ZbY#&)GOq4T;NEdKpM>6tDTY$Qp( zaZT*<$yRiB_Q5&v*!caPjoZpen#7-M>&A76J~;q(SJJp%YYl60Scgwmdg%LR9n{J! zdMZ*b;w2Q9y@#H%hQLMq{4dkMF3ens z?6MEcZu?{;Kdok#?_R-pxdxv3NK#;gEcWhdgK?h$c*|=jc-If_r~E_XA+D?L(1j!= zCGI4r1H}=l7&4^=YMld7saKCieXX7@cyU{jlB6^F?0U<@V?z8aF1R?XKDkzes-QQl zc10Qlv9>)w*mG?lh1#S-b%;Ld-BTd^qsEP6t$5N3;<&Ap?3M z#7#RiWfPKbykg=Hi%U3ZOa_V{-RpT4cg@yEo6!AUx+lH3OU&MEfsDuZp7-J|bKaGP z%=Q0EFYcQ5f$7lX7Y%WZ#Ra%sm+I~?{drmO1hxZlNH%FJuU z(JAp%v-~3tABv|&yEZ7_TufU+zCwHTVmkNbBcHbm>CK3b*gIwcv7GN*jbt%N=(VEs z)a^fyIduq=1L$|b`KV`$BPUO1*H4a0BaAh_HI7oL2F@{y&K+v&_( zN=}5($IfWzEr+Q}&6xU*3)$D$Ak48p-gnuca<4qh>bUB)(IQ%UX%u999j~V(v z9AA1)U8q^Ah17vQ7!|Drp|lUg8H(e}xjVY_1RpHw=BuH|)(7GbieKbC(Sq+AZv=_7 z_yL6P1-zu<_`pS4$hGjsG(RnbtoMevz~XrD`+td}H^c=NKR@Bm`t@Ge(=`~z@!k*@ zTpaKDXE166-njF354vk4(e{sDSol5TjoxjD3OFOH@cb^aWt!OPzjsh_!{y8^y$A)NFW8%Y*U;3znkhIJz+t+laI{;22+Dg?xFSU!K0R29 zi>vaGr*TdAN@Q64&0`3OC>MHW9YSlT1M}Z`3TDOztj@axO^W#}CFc~3`ki7= zeiy;$QzskIQHJbs-9zGSOco09KyQL-Q+4XPgPfQ#R|iG{pVU zuUO<-6Rg>Mi*OeoVbCYt~Lwg~5n-%JW(#U&Z0ncDL6m8VOI}>@Nj*v|0v( zQo;Q!Ii%G~A;4K4z6T7U^XMC^&>n+H@8uD+h~s|~{_78kIc6xf`oc=@nBZ&uK-6&f z?RKC5wKD&M-^Mf#?SbDe3-x>8w;h9Ydf>Ow%{sIyRe)o6&2g8*Z?)P(=s;0_7&re9 zep4>frgcA*dzN4^{3c~>NY@7P7A&?v4TsH5uzx+xqqnLMMK=J4 zniWx=G8#iF6p$@%!_ka^$mH-@ew8w6*PFw}Lm3VB|7$X(OS+^wRDkX@F?_ZX+cSpJ z4X!d=z@c`Vm;GQA3T(gogJ}+NglA1}$V?Zae6AvjzJRNFwN5ASU#A#3W<|Sf_rOHS($FJNhdNFi^&mrn{^- z%m>wD-m}scA6%L?01{m;Sf?z2v8gNmOqYdOjVr=7_C|w~8@}BB$yBHD>zw}}CL`sD z5%^K_M9X$x191TIWG^lA~S$r_>?KK`wa?$)DihmemwXoaW$Fqnv{XlQ?>%s^Ey*5&X=q_Ji#l zBE#?X-Wvq0k=q{7} zEQzYfr>wK2ofTXvV`mO!GePoI)^@*wbzZv64DLvycupm|^i~mpb04z#CaQ>(s%M=e zJ_yV8-7tcKQk8vekQb;;{|}VPb#cU@b!(YR*&>`fzJ>|7M6f>xrGyS6P{%7#?gfNWYs8pl%6z+*{C+7pa@Ygkulm7h>w5nM!QS9VyrZ#jw6WnNGjk)t!YeX#z+osLjot`qE*Sai#<3dS?lv(2s zJ*02>EVSYl9+GeE6jt2htf+++mClU9>Q^SzGVvEiscBH;Va{%Hx3t|IL7-;})Ezq= zbxH#$Z^;Y{a(}_{IHn}y)St{&_hBj9SBhBPPH*Np^AyXwct*Hzivsyh4rBMs{;;Ye zhV1YrV&SN#-N}0nO zUnX?k!1DGaGsCmqEF_q*jnxCl#;ce;e7li}v7|Xi6vtq0RdS;;<@4W@)26>JHlA@i<~p`|RGKWdH zajVdxujiS}^gPDp_n1u%2a$eiQ9*kX%e|(-rGDjmnl>(KAQ|QjCWl46ko{eq_$t;D z?_sY*w`2xWSc@tqj#ekZWYeB_-|oJ2EJlsI=8wXT4}(b%HvzXb22;{f2d?X=PQq-b zo^%EirAVaQpg|=kC-p?+R!NX~svMPS>9C)NaVvprTnG|EfAC*>_W5!K6%42*5jTVsFc##>la?gtmx6E-bU!K&wo7jZuiX`yw zU^4`Ld6Pe6$H&W3aaTQimZC&PPrI4UngP^3pbrFxr724=h?|q?M@7p9LuITSRXx@~ z)m%l&o2iAIBmF6VuNEruRjB)y1};erCi&iaSb15UN+0O;wD=TJs64%#(U-DB3^GzO zU~U}k6NMpaqcWvG3&!IZc}nG2&-&?dq{FeEuko_nus#relXnH@3I9ImPf3Tp z5Ll;3Hh&n@__(ME_r_gWWh#^fM&_tc&1Y}?DHuSXUNQXX9!OTMzHsQ{CKMHZ{LT^_ zPY8=kYgqE?Bf_xfs_2wHB5VyC0-eBf!dO|8o<5wy3o7nQ@t_lj;=v;h#bZVsis%0? zC282~P{+;;RRpQ4Bfz?n9YSjiSuD!wWQxmr0&IN8PepOhv+Y!mw>5>V-nKClU>6B=#&$&OvA6GmU=XZLGA zg*IzzSkviO!WtL8G9G^{tg5l-DY&>m;sT3XL>!7+OdN_^P^$Z#Fri4MCth4ZB@uri zZc1?iaZ`#z@drM*tHP|JiM@A_Me&M8ws^-iR^?I662ER|nTn6uC`h7V)eAOo;wM&o z>?t=r8qHbzGC9X6yCQaanJ_uWx~I>?1@KvX7n^(+kzvUrgf%Uon5Wgy z)#W}CuRlW8%{V$RpbFUnwnFU;9nccmhpnaW%SWDut##2n&$u&Q=+-FV4|MsMtk`_k_CHGlK$RB36 zw}XWohf%uMb~*Y6ptkdZ_Cg$l^Ln8&BwS7z+mAI{QWS?&RO+({|L z_?&oZxGsSPUwPQsNM+y=G^ z%s3>Ik|mnd1CcyP8UXnvoPOUl0GT#E5dT07oxD7u46{aWq)MqE?438n8H?jL^;dxe zhh!F8sGxYAH^c=I$BPRf4(m1!MEx9ZhzlZqzL_x)Au`@5;}A@Mjv_{L=;c_W9d3&1 z?joyA3$h$~sef*a-~sd;nQ8jO*E{)U&hVY zad|FD4zZ~29R*nqu>`pcMGA*lT)rAXfg>n2-1icTPLwG7QR+lhvg*Dzw)V$zWw!cJQ5Mq>0md`&-(&DWk_kX?@e;3gMEy^Tf5iKPBug$O#f*Kzadd8{0;jHEQiV}AT)44iQkgO(>!v8oY5*BwW2 z#R2GN6+;lP2eR_ZC}B%)e4UzyCsD;%e=C79V>oqt{vaTEqUSpjU+%DU*Ircor~xN! zA7k6LXVAXw53_4E*qF2lfzux2)6q+qG(+?bn*D6idHD&1?^eNhG51fmwH@J~U*d1s z1|%6>z(R*LNSb^UkseLZdvF>1-nU}l)x*f-8Q6V0h72BFc~_1RUK?;zZ~)f~wsX0@ zLf(zFxbylP?#H}GfaL*9m)?gj^K((xc>~E6*{Jl{f)de?EJT;r;K-;5YTL)pRTLV?kmW-(rN@2Mm0S)U<n$&ts?rer^uWZZ-TY@_gN3ogL_+|fm2-{b~cgP;J z=B|ZVQ8u20ze9V(W_;lV@og@`oY%f6S$+yn%KoC$FdtuBlaXz(53jFxz^-==$_-k% z{KvCwG{0Sg`%5>#_{}TaJd*~QQJ?t5b1PDn7a`+R0kU|8F|T*x=kt|l`LGisELJ1z z!&Yd#{s5=JoA91z82+metx@CA;CKQ#N&gT&nx~&MhLUT$*cmQYnmqeC(->(>`h!;E zCs#W(TN(!lL_%_TIR)oJgp_;39o$khHJ!Ia>0cX4Six_ziFV{-J_Hj_OryLuZkrNQ zDcR`ZjbuTlvq-`;05e2;rc z-eiq3GpK-*(Tjfb-x*KDCdsK(thJv-l+K`z*{KlS`8}hq*1-anIapM(Zu{nvv>^ZtlcQn&=GyhJSZ}FCmYl;p4ty=0yq9h zIwhyrj}LmenDypEOHk0TqcYb@xBD^>aahXhSOvXV)`Zg8)Rp{h90=1dD8IKwd4o)VIZ zSjTrq($?O9@&6GC-}b@<8)tr_8G%Ek&Xja>8s2?jB-roOnI^$8Qon2DAbn6`8)a#+{av`9R3J~j zp#0UBr^&CAQL|8vp5q5z2KT33vqWf9|A(x%oPH~N4+Snexb9sGr009XDOG|l1=piG zLW(@S>bSU08`3-1LwZ9qGPm47%13@i9KKG3WiAp_r+OD58hvOa&q(jrN7(Z)+Oij| zLAC%k!BD&itl4q4-<-b9Id zhb*Rw^{z-)no5CxgP@`ANl8Kd$Y<*UvgmXs8|_8pvC;!yR}P?{O-HcXc{-VAdE#*& zC0e=61B3h($ep*G@{Iq5?U_nF&(r5}3tQs!yak#R_6v6piKkw>IVHPXg+%h>j$`Jo zM0!_z7f~St>9A5JT^LO#;>kUFt#C-T9|eB-fz4M2(1Qs-uxWq_38j8uRJ$Tg8pnMd-Q;@H z8((183>6WjMXeUDnifWCH(p@Pf80vj=fORFD&D_Od{yn~`-$Aqj(7}|jS`TYrz*X=9ct( zzBSeLx1xMyuD@!?3-B-o74xe({1yTIIp@((ABjra8+hwG8-lzO*liz``fAFhy_U_mck>}iCe1*Ha9k^biQ$ThD= zyUkB78GRHI&8^ry>lzA*enCEWKPD%2!6|7cuBNuY|3!a_AJ7b+j5dslZxNvzQq=jW zURV}6g>L(DA%v_zN{!c`zPFZ<{Hhj=b`ntW`xe~gYRG{zHVX$on?nEmK0>&E0ImJu zL531G^sGadN{3rfyRI$`%C@0d+Il2dVL_*+Y0{FWskC7Uzftd)OzNH7)3ZS!{XSvP}0(rf`&~e>(jb4b?9``f2u~w{Jl=uFk1OH zh|cY_qUb|GblQUxQwye(mdH$xq8|8*RiP-M-Ivn1Lxnrx6R6;>CH0vQKxJM-DC(U* zt)8GonHm#l-y>BrT;fNvC+(@~g&*bdZn3yMfg)Q>=qI=OHnn~*efu|u@>SKy#CQQ! zFahl|52Krph#tKOqxZV~>0IAvvZ;5ZxBcdk$Z(q-2}kp-S4K$bpXbx*AEwme6G@Zq zjiSK;apaV&ODj_1$s}BtmPXH^Zhcj{F*b(cvj)<%gR`mQ&TzWLm)e6xMif~bNvi`! z)2Yl@vRPqGI)xE*Oj(oCyW(lrZ%x|&U=B6L4yAD)JV;$upF&j}xkaAG-0SWFcd98G zOrM83kbI;O9Y5hoKF5s6vB;4Y3M^@wjThzbu^{b0XL97+UrxCPl^@ih6EzN$lchz0 z=N;(9EG_ze-Gf>xRcTMDBW1m?p*DXf3j3@|8_)AI!vb4!cJ!oU`PS6v=S;HT+PyDC z`zBH9<4q`1SU?$L*E7Mh@${-Ii#-s9 z((+01J$uKuzZ)=3c{)9G%{?ha;$+HOTLI#g)5NM`y>tzT7#OCB`ANh9Pi)poy+ta zRxjRzc`LtT#M~VSdy$AMvkzb^Um5MpH{m5e1qYZP=l)B-;^Kl#e9!&JrRLI+8=eBI zx$6+|;2pj^UX6D-DM$<3jO}-lAbIkjX${fc@uJH{X(@f zx7eo~j;hCpk=_xH$`c3iHnszwgqz_bT+%bYGx$|HP3kBX@BIe>Kay>Ic>-omlC+%{ ztmFHNo&vuAlMeCoq>F3tgcme8{SeN}*ox43lUwW-oq%M#fTP*A&|kP0Uy|ApG;JMB zJkMiNgCtd`b#tz61vCVms6Vs{wxJg|#L}Cj<9=h?x@24)_KnMpbB8>ef56L$OUR~9 zhr;VL%xJiT7$qr+PvC-yZX)azEWwqCwRn2+6IV<~#r^%FC}b>Mi{;U6xZ;z7V?Hxr zm$M#YGOplsU>{n&Vkv?SZiGEox9IfBzyvNrt2TWDp0B@(eH>^W+;0gSwsK|LjnUAF z-2|DY=`a#zV&eBwr0U3!YGFJER_wsDYOYh}w-uR_eF zGGA?Cznx6Tp?(h5Njp$M^IGQmX%wZ;TnN{R&eRZ=%8m^+rpVGX#77uW;*wN&oYSX^ z3szu|oG#`4U4`t&qe()sL^x=-0qLc!$M(riRBF3eXqP*jfxAhT{@#vCjXfb7ZAYzKByZx?p;YiA z1}{errNDFZkbcva)Sg-6!ksbHx>FftD~6DQaVqk+TT|ZFI-%8PZTfPX<3DG#ttt7) z8{sprF{HCy75$uSDeXT!B%acx@^?vGxJZ|DJ}kq4<2Dqjq7Se0nsgvBke>Wdq0jjf zNw`l)Rz{P^Y=sT|kP75ju?e}<2ahyB$ z9FqKx)3fUSlzl;mmVWi2i)TecX-WPPT5L3wMm8>>;QK?!`@lSki&rPda~x!D)ucQP z7m`n!N`o|~QFr73>cxesx}S|D)$a>QM_rxts+_pXsR{J#P6)*fQKhVKClcQDApJ>^ zB+n0?idy5z`iKU}uJa|gUen0Ud>oyY*P(?|{76r9VgjvvwTzbZ(xG|N{YdJ#7Y!*% zpr)G|R3+&_DJNa2=5P#^d#lm#d!AI`J%iTx1<-71HEM~RO{<<+kZ=<>#FFgD%|^|l zJEzCc#(AN%#g@o;+dNvQWkn8gF=QucOUq?qD5Z_jsY7!}!OfiXvbk#RrLRJI;5Ub| zzuQnolRv3Gn?SNw9JBppLnbHuXyKA+r0hALf_yAVBHvKaupl2&&v_P6|uBxvG3H*_->5l-*$3pzK5E3TMzP&uFqXH=}uRbEu-+ zn)+OyNa4yFv}D^{n%ZGb^TTttH?bRdP#7)#$qgixY-1dYElihfmxlXjvt z4Yr<33w~PDJy}s8UvdXi>iG#2^HPJ%))`Pvr8#BX=lGt!7VWuZKt;yp^yXO*)%MpW z!O&O=KCex7W2|YjjU`pdSX0GT3p&8*mfz!tP?FVf5^zHX&$uFQu_GTl9>b|P*_@)r z@)pw6qHKPZI~&cfd|Vm1_?!mSi3X3NZRHl^Ae=+dyEQ4WO^b{z%xUua38dYsOHpEf z6^Ul_%RGRh*XWUIj4w@nqE9#0FQzZ2^l6}nI^};eBPUmN>as8+GbKM#4mY4?!D15d z&@EyB{q1c|>k@6~!vqV8;qHvL@gu$F7Jj|6HzQHxKpT2^kk{~Y965~Aqk-}kJYSOm-lS#Bln`~6|sDuw9EAGivJ=2VS>j#pe(-68h zH=N=P#?fU#B!#&+kZ|c(a^uE0KGe;o;;G&=DBYbTz3k}iU3V%< z4K1^Z;xopZ_K)%-|9gz8+_)k-KO0C$&7s;1Pg*s9F11yUr7g8ir1_j^eUS?Z1tL50 z9TY)x&%4o`1L5SM=1KFK-Rb3f7uu5=N-yjkY0kv)o#{gF zM$Ds4OP%Szx1iYAu@tap1|6C2Nb&2rweJLO|Lk%EnT~X#+dti@&c~GkmV|Q6RR{Vw zZ9Ekmb?cdJdLsVMFzb&ACKs`KUuru^1;dZDC67vBev=n22^URpJo-sq~4)!I(g-WRKPi%`-+xS_jUlaHNEIPG+tT;1cJ`Oy?G3 zxr!ADwx5Jqx-#3xIUVtbI!Bkn;maU8$hjOP4=(n=xtutQ-E1A_ahANQzPeSI_;$C3Fl~ZbZXgdPSNa{Dnk-!dy%(Nj;c9D)AwHy zE9DeTZjdVN;}lI-Mt@S{6wQaJKbS=1QSA1wVEZ^VQ*!b$%i`3`g{O)n8MztpX1b{6 zluYsP-qg)08PR3Ax2%zKGNXt0C2&s0DM6M@IVH1xOF2X01_*MLsgYAM`xeO42u{gl z{Qbo$I3=_3Obyr3-ie~AcUd{7Vs_+k(JfBJ)F0GC8>e9EDkUk3Q!vV@izq#=5}!{l zpmDeR(L~v$R2EPU1*16Xt=XHKhP@L@8BxD6A(3x7FTdf>wpa?C`~jZz3&^OX1{ERk zB&+%Yg~on#rM(6kx&EY3c?ZJbzGRf%iRgvCbk67<+@mJZ7w+-fJj;*X{T9&cYHr_U z<4K$i@TFsSWoYun0Fvk^#hRI(bY){TZXffYsLi6ED6r?I{}qbh+U!B;lKtq;f^nqR zy8<=6J*jNcTQq(1q|`Dck_zNR#F?X56y;4voG+tBWjyVhdK>?_d6TkfBQCr6(Cyhu zG-*o!HJm+;a?T%Y9&!~!tG&tT_#MWomj+o#RjKy*sJbMr$wzY#6CNbdSexw z&fC%N8xP>n&zV+NbYNdUdy4AUfsXxSNovSR{E;Urdt88oKF%b4Plj?i*f?dJ3=My0 zN8yjkV6@Yjj)dHRJ%{GXPQJ!QBWIEv_6A1B?CD>v3SBtrOul>epyG}*)z&D}vD=<} z{-+#-ZC^&(=gN5U*)L05)nHDpZp0rMjOpBtuOnXt&NF_5p zH^2Ekf4N+TbI$eoF4yOKzwX71N|{_TDlN#H$>neqXT34Gyh#`cDU-`4uR~%ulgosY z#wcZSSypgNEMak}QZPg!i;FyWnw-t$;a;s?24 zN5LCcH* z;OE38vajUh?67FZ04!qpurrN7>^BSG+MSN>(je%1M5Fxi0@x18#>Z-J+=@eB zdpryGhlRk&FA9wZ=cD2BLwbE<23ln8WHclZ7Hy4mb8-@1lxiV~FVWHYw@6`n5+;06 zL&eN^?3|+r=V$To-P1|(==cPTkv^pNN8<2pzed_?9fxy^wJ_Qx8ZFIgxN&hR^sX!5 z{CClC({H0k8L=o_)WXfAcJ)IA44QQVnlYeQh!tXMLd4D-%%|`iQhl zk}>Sz?^N(QnWsc+!S_f4PQB#JO5Fssmnh2dbbbOZj!~wkEE_2kG-({m#wb?}dd;%& zf=g{ivuxPk)S^a~jqtNNF zqKakXr*>8BVAYzt9q7`%z&a&}2NC*E98~5Ga zdSN43XC#aquh6g~Mp({&@5HciSU#9fuzzKv=)5cXwQ6G|%f`-zsW2P10JRD)NMltZ zbhbPtE7v$wt$9I8PDzMJZ>D`ozzolG)v{ONjkd8Iq?0@`%%SXdySjx;XMCEvb_s^sh72=->&>W7=40 zDUjlz4)47lxVyEBcKz&uvj-nh8;?gRl3$^6-pvmAy^RXvWf*i^83%THAeZm&nzb_c zPk&6A|BS=OJxx@xa6H~#YA5MOA2jk^PeI)qs$1_-u7ju!Yrib=3dph_dAxSki2bX%oBX{CAFcIKPYb=2+v>`Fm74hx=PU*MC62 zFLp%i-P82yoD{9*7b(!*72%of6tkQ;=2RQq*y@N?zbj)lyU==Gx`XQ+Vf4o}+LK`o z1J|20v(ORJsOL2POaP83zM>o7MQ{w^2E{1`<3h}7d$~Uf%sXiH!pT^+ zN*S7RJ3k!0rHpIZVJPIK-EAMpqk|t)o5mElkGM|*SjAQa-KE5&KqM?~pm9T|px#;& zb}PeJuP@Oys~}_!)Woa}ld$csDx9x};%2=nQoQ_dbCd!uCxl^9>OT~?_5Wa_vC#;R z88)gmIrqYbd}+C?S2jw{^GG$8jV4oj^oYj8l3?Bwlu*;@uX`n66Xlw9sT>OJD={Qem`?ay1A!C<|9+og4kwn3%gODcnaaFOozk5JlQ zO|`*l=wP_Gy6e60=gW7vmfV82vRtGc<$8V&3?A@PM>4}h_N;1BKcj>T1uAG^crf~o zbHo`QGW@D2)JGY+gZp4B!^6Z68mMA;IK`WkV1|dGEA=UY;i2*@Z`GeDqtjm%uNfXv z6+4AV^7ll}Tm!@Kke{YTPuU&X?@qr_>7% zIwO>!&+sriZx`7HC}Ge$CFCif#W0v-Dds42Un`E!bwGrj4n?dm!H%~}MOGtse6Cg^t5QpJ7;}l< zNo&-1n$T5$8z}5_pj>@x+|4A4>Tuw3GA`n?rv)m;aXwLtB}}^a$ex&)!6fB3S#+Km zSq>+j}Pn-XcsQz@42e-#4vGcy*W$A5Rsl}gg|S#C^}(_!fhYK z4GuoX{^y$P^-fbc9@!AHA1B&gXN!m8k*vAS7~Sf_srEImz<{f=>Gd|e)A=B48P2bN`XqZ0 zYzq^GH?kQ_E)Oq1lj+{Jh2z`+;h!Y8MZY_yV%-d00VO?AS1d*SKxZ+U69|&~j-(&g zN};e(MYNZ(bZ{G`SDwRhrQ1MMEwsj%0uPaRK*ArOFTy&k5F4&5=2r9X3d-W!V`d!d z<8})&YZN{?CX44T(k0LBDfTA`Vvih`P1SUOW85rZCQqM>t@>9fAHIFDy(kgL-M78g0$_CSFu!#;KpWjl>p3 zjP(gZPhS0l;z@rOOdV}Px1P*~-=j|?H|DcC&%9PV zTjzq0+f8XwQ5L3^f1>2Evmx<)N6B7J(2ua8Gc``wyuyxJJ7(kB>UX4&k%e@<=QPNF zI5zt_Q}wpts3~=&0{v`!OnOfK{pKL{!cCf{B*Bp7-t_LGBi_y#FYH_;=(s(Oe62>H z;&y;sj94`X-RGJ~aYHtwNAFW$i!IM6@}N6I?a;zyxbN+AFl_My+OoWbu3qxQ&Ens< zFvSNieYCM@o+k_x)p6>5PsFT?y#5C2`1x<_15dP^<{l9zyisAGXZy+xH9Hvj;fVN zvCzu{IU$x&g~-d{*tC&P#QVTM1~#zp=3P&fBTl{x zhivN(~{82iDJGdcpq>p z3TIN%v1XpYPSxRJ(#3vv?S zzc>L++aqu!ARj*b$X(GIhCSVrv8FT~-x#H0b5IDTUf~SZZ&G218K}Gv4QWIOTJPnc z=tVll?N3BKCoX;WS3a^Nu~1&YPeom`df)%;?5XhR=9OZk__JP~f+0g=VDe)Yl5VGB z{1gvpUYEjw2W?#8Q3Cro)LK3>6q*5Z!4(}aos$E#UC9{micsBUi^m&t@yonah6=t* z=`DUs%eN$9bEgm1Kj)s1L;vLJ=47-hj)U75Ywn23-HgMmaqYAk7CcLULZupt`4Q{D z=?Lf@w}R2R5A;|e3D0IsK=>saywvzeog0#nn=t`4ZzVv5B4S(<(UcjC&~RHsA67*0 zT|WN)3BjmHw}I7yNAzbiKcgL#;jMuMYRVr`Uhzz*Mha|SY!2hKqj1R06jwMXZ4hOO zeWP#F_QaW39DJKP%u^s6H5%QVlGEWHgD(DAwBQOA9i0iuU@fF9Nks0RDCDlNKv%~V z$|^{~u9KROdd!gH^}r}xyl0L<>s0XMeF83z4#(cBHu$`OHx{LdSk@heqq;Vzyfg;I z>#X5@_yrA1O-AOW7nE(rUvQEqzHr-KdHQUSR|vWXWTV|`8lnRup|JQH%)FP$&A5Xw za(Wbe8|Oi#G79CZbFgMyFornGb5P!qg1T}(x%@eIyCENnA`QV-A^6LW#{!gxqF6s2 z8g3D&T|N!dLi4feNh~7t=Aihu7=-(B)r|WbcqRnlv}yuA`sL!{#5_E+4nwh84m4TQ z9%tvGWlcB^?hk_$IoPv&GPE^$Am6Yo9GCw*8LM~9#)$wPepw%dWakX1ElGokLo|w{ z@lZLIj$io_I;xm~DcAFns2+(Hhdk_k8;LDPgV3@w6(_f)!0Yo=l*V)8kss2KUC+fu zn2MK;QAoX%j>CCr7&eAOdd~5DsMC6{Hj9ij?0c3E`TF1}1g7L6^kWnbcLbqUItzyn zrXYCOG`R2;5Ez$=GcU8?G&UN)^q+uqZZZD-V9xJU8-Y0|V)2ic4V;r*k@3j_rT3@9 z#MBaFizj23y*;vTdBd+|IQ}Dz#v~m(RK0P>h|dz7*CmW>mg4;0F*>k)We-WK2b8(K M>z9e26n!847yE8n*#H0l delta 59261 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 diff --git a/Assets/Models/MD_SkyDome01.shmodel b/Assets/Models/MD_SkyDome01.shmodel index a6b1f4874fcac240de7e78e46f48d7c2da6e0c07..dbfd004a496ad72ee8e3c4c22a1842a86a47e3dd 100644 GIT binary patch delta 10376 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 literal 236 zcmZQ%Km{y7E*lVoXb=c2O-un&`$0HUQOOuc+as|-;`TuC%}{X|8^|RMWGXHKn%w|0 cn}HZM#B_*y1RKPMh%o{MnIRHjnhD4T01(?1i~s-t diff --git a/Assets/Models/racoon.shmodel b/Assets/Models/racoon.shmodel index b25e822367cd5769704b821a2c3e356b0adb9f59..c3fa9a62440bd26f6a2b2dd9729dbbc37c1c36a7 100644 GIT binary patch delta 37002 zcmX_|d0dXm|NkpV5|X4WNs=TaA!%N(BqSu2N~Mx^NeG=RcamfeA$thf65@11I&pBU z$reJ=AqgRbGlK^P3}+f)*%H*{2^t|_FmA37b?ryt(yB0oY52q9@k+!PY{$YKlVG=A;Y;;AAGUux zfqPesV{)7gBj1hWLaks%q^w1yhZiHwJHY1r4D5He=FUBXS!CZrI224}n8haX)BPmY z|7*$A&?H>av!LptX>cp=#R<39p;&B2&hudmy6c6vE6jPxRE^hm`|$LeVYHhbM72$0t z{)qwmCiBTyTW%T}z^bw8T$d5Sm9quo?7^SrG||k14n^Ho4|Paa3IkHp-3l z>5l_A+*_SzzK#@){Wo7Q6aOea&S7_zU}_y&3$34Xn>xm4&F4Q-ZGsBru7{+YFILK1b^vAdkLCDuP zrK`RzYv*~uv_(4Xm-c2!jVHBAmcZR{5TcCCsk5~^)4ElOFICYrujz^Zm5-NRL9m|* z&ucG5^poDKcE2FlnLB>;LD~AhHlOw2f z+$Qq%{)F*|zeS|&T%-&s7Tfx+fqS?vY8Q0D)s^jG`>zu+pSvL}8d93RA_fMQ}WDQ4HLZi5eYcp=P-W>8p>41pj;(b~6;dmWNSt?1J#0avLtMBgLx) zXJP&Ih0rZ5!_~=ui>j!9VI0&R!tFguN3}&{pNFWg9ge%(zTify!RV*-0x_B~nDuu( ze4j1Et2X~Zw{#RmcdixX7}djs3xYo5{O*Cgefa^jjfSv$>t|>v zAI+P2g{Zncnq3b3gSAiH`K-+`bnNCz`{HbrMffP#B6B;UuedWrDGg^X`_rTKYOHMO z&bniN;QnNPuDu@zwUHB`kK z1&>Ev=W=oN?p&1pm@n3Ln}#}-|HQH8%i!3sM(C7nLyBo@v3T4{B;Pq9Z0mL*{b7^{ z+@6DMy_TZk%Wi1hIwit3Q5A^IMX;U7mDL<9NhCP5amVNr~44s z#hvxLGoUrZi=XuOL#MqTe_Y;(NvGV|Iw}Qv&XYNC%}V?+GJtCFiO?^1rLW>_ERI`y z(!Hx6T#9{J;yVN7eiK+e+YJj{T-k7J0LK02#*`otUL> zi3jN;=$P6T+iHgM#(+nnPMtj3zdiEOZCI3fO?1p3z>Mr2Vpn`mW~^8u5}N6=ppCak zzSEM5u+F1IWd9FP9~C0=*app;Uq#|jUtBY_fXnT+IH?_p%IVpnh`H;ewMH{n=)L;Q#;#ie!a&}P z8DhXTEgIgqWgDo~fcWXDLcQ=e3=h1tZ7_KNhvgf^F5gn9FPR|DT*!oDt4win+n-1@ zJS9?9=RoJ!Q=whF4iBbk;$G)2i1F`;)u-KH{bUqU+8z^|8ysP)=&p-v3ztDFql0+X zZXt3bo{Cvz`;p$T&Nef8BVvbM5vxZ0jewkMwp}X@!tqUs*tzLGvL`r;UZuH6+xA0z z$$pKL;`1WJ>oRh#8NhhJ7ZjwoL)RB~q0@Xk`nUdxoKZH&n*0>?W{Y6)rxJg7EJ1?h z3n=;>-h%deDzqDY2xHtT5afLphgPaEYv+CBcYcMzcgpd{bQKo1Z9t2c|G~OUjVrDz z)7PjSEp%QYy|yD&ddgMM(}+`Co}tp$gdaP6!^hE93_5ccks5<(y!a)WKOfD6zJJ4V z=}7(>dJ~6ACh!UpA5k_e`+%z%V-8 zUoL_U52X6odhzp$Im^Be5Qgr(Xl$*xB_`zUu$#8Dzj= z{Y#N}VI=nMSB67mEb>;GLf!AZxaHknF7KA8pAiPFxNxyqVKWV0%^QT~XSwgcyI@=2 zeg%^Mx-O0_%0a>YG~3$yJK@;prbu{o9r20UV#t?l7}mZL&%ZoF?2NS{?ED3+e%cDp z<3GS~`X_PU{wB^fSYdm=I@sg|KMna@#Xw-Q^{E7SC4Dii_o z`5MJ%Vnv7wFHgIGi!=U%_m*2tbD(-r6~aB`(!8k-F@01x&QYDQCo5pyQj3oVDs$;2 zeg0?g0#W_CG4ND9Myd9pd-KQm_^U53?0Aop!JvitbsX*|ZAHBjgzTHZr2A)J%&~k_ zT!_KXy%qGU%D}Jg6Y09`5bQn$a`u`{aC#%3x$z2IyB*9v{z-_naiLd(3qJLl%IMf= zSS%UGl^qRXIn{%UA9>)y>d~x;tP_{kjpdKCL-2aA9dkebDaw7x!`t;xJbDPdi8}Kg7_Bh>DJm~aqg_iwxkXN= zbSP(_RfX{&E2uO#*YPY zWa`5(>gmPef2HHqaPDf}f>DM;*?a3Kc6lb9m*nOwaURODjiack@6RsPnsSMq6HcY} z^YOp(a+%yX(p+(>vv;=fpjJvsN*k4xlp4!Nveb~Jn*21C|6krCo#U9 z#%$cO#O>7kB`-Bk-CujPja)39Hm=%bvBl+*s?({->2d0(9%fExzR@hpXk+||5gV_Z zn7B0JXk3ike;H@b$=4W}Hn#Vv!#ceG*a8 zdAQKB@WY2hUpg&If^VO(!s}EhqOMP-n#OX()Q%OkpM9})pf5damdRIhGE4K9L4A`C z&&*B0hK4CTH+KnUcAre!&P!oy?@dL;lKGfF)|dB;r5DrNi`i4+Fm#d^b@U^!qlG8S z-bBKE%Vbuz3x)F~51Q`u!>Cpstg#Qk`B+a@R{Fx&&x6_OKFIvx!MgF@*qJ|xML9n3 z-JXtn3+k|VoCo6*z2P`u4>t6yh2F_Yyw}zTQSo~e*l?pBYULiZtDX#}w)t z_F>++S}2a}!{fZ4m=k#v4rA-!JNYQIuGV46uA?x&`3+Z+jv+3v4puXdVQ^|4QqqrM zt?aMqkOfsQ`4;?p3ROZN*v$u))(7w-qZ+ekx-+}G4;JfA;?a9P2$gbCr4QW1B-ULIz%(gG zJ4}W8`AN(!nE@LqJM4-=rn3jlXD&c1Dd+xLfOG$PaOL);coyZ(sEbQspiuUt-H;?) zmI?Yqeo+#?TBe?J75)TT7m zKYP7YsPCMKy(gE6QrX_=>k@I*D+rJ8{wa*}BeB^0j5wO$2k*ImiF304Q|S+3r4k&zKLjEPxMagj@(|s*wo4p0oD^R;FS^FW&Mer_NWOAgxeB(nEQ>vo(>~m zD)pzI1)!wQRBR~>K!EvZG|!ug>i^Ev z{sww4!(nUwNc1_Ou%zzsH?XMm!i`DKg-eAE$F_TeOR_`akuFgEZxCH|^)Xb2k2}Uw|yXE(j(z#Y)!QX zGtp@tBOFXJV7$j|%vxhW(?!}?R??k0y)0pHvNJDT>J6Ox^t-!f9Y6?~)reaGQHI{29;+?%F-J30d zOJOV42PR_AgVyxx5Rdfymb4wW8ZH5CSfjKY_m8(_zsdx3-PW2BQm-1IMU`X8SaPo| zQ>0wfsU3@^Nn7(#JGlX^K%}lVpLJRekG<`fFY7NJZqKY)E3kQkLSFH@B+Ne1o(-}> z#YC5hYAayfTUTD$YB+uBz;h>-!Do!VjOQlfaZ7zF^-09Hq58Zh>(}~rvG0iG+CnDD*J zYE(>Kiye8^+z@BVz48YfP1Yc1*I*7+GG)DQESjw{<;sbxF)Dr?D(=~^{^dFp^s{D{ z(DkUfZ^Mu=YjJqZAZlVG%HI#>o7@ys>kg#f`b{u+Xu|=^w&8H5l{8GZAZKkq&JNy* zZBnl=3q6F<9#)Kdeh8Ny*s#l*Oia4bm%jFSNLgpa=vjFP9Mq4W?*4^*`NQ{T-@wt~ zgV-hOI?C?Y@Miy;Se4$NZCgCYWignuN}nS=a{%AiK10+k8xCJkg9Af{Q0K`9+`4AN zMZG@4G;<)M+<)Qu=OGHdyYma?hJzSiR*%XnHnfuVTDhY&y#tk*blHXzE-TaW`XFYy ztMG=WH6L8@phMLTTu)MA!J9$6nlzaYTz6uksR~VG`!=oI8F6<9UOo0;qUA2c9-Yji zJ9nW`Z_s@g7Rya-VO4sQ99yPHM{5;t+J)_ba)>8S9?Wd2xO^fLUU}X;+2;^!3Os2% zAqQV(`_TUHoTmDMsX2(P@}$?#b4{{Z@j2Ao^kM0|^BCH}i_Rbafqi#h=B&EhRBsab z4<=bl{h-@Tva#Y8HV68$N^$u%GRJr^%=Amq z!m9mb{<@>lq<6G$McZh1DgxR>iXZ8IOuw^0Wa#^G@kkBo{r05G+$^EB&xZlpCxn%3 z81~_qc;M|x!{jHzSjq)oOT`2iZUP=J>)uIhO0(i3R?YZh9cRZuD}dG#>fu)2jZcj7YkNdAz$h< z#}0%+-Xvb_2o&mgGIIj4SL!1(1b%o;qUoU#F!YiAhKxk2)Tb2MOAvji*_7CKMx_IJ2`&=0X zX+{s(ui(80Zhklc<4r2Ow^bS*`6@IhJB?8}%KTTcHw*c1zN4_`aoD7PhrV_e3bcN} z+&>HUHPVtv%Ys?4bV^LJP}%A`jISJnM#t}{G(Uz4b_GU(&8wL;Yrs>To7+dSZ+>>sYy-nt$_5>+l zy4#Pr8Xj;~k!iFI&X_XNpC!wK@%N3X%+B?~(LGaXmpKi(`=+vXs4IeELb-5$s zl2O=tCS#Jf;>cB1+v=n~*iab7?C2F3VBVT>-A%YHF^Z-8(-1u*l68G|;(&WND=lRp zQZa+sZPMX?csi4QZ^whH8cb5u^kVkEazw9pLQT5gX^(pIX`%+#9_YnSk?M?--qG5( znhdzpldYb#wGHTQ1{=1dPF_!DE4856(O$HdyQSTW9_;hg)7CM`2u7>a=)1BzZ@g*7 zPb+%xkbO&9WOn1LgmbphhT0fz(~Rfry6~-<5*0C4223(mXX{Q!5v}TH; zx!J-w1HheLe} zj{h|fE{=1B^VC9AKi6QW^n}aiYly`e#fV$foWJ&UKvB*@;d8bpYWD9J`n_7A=CZw* zzCxh}yPc`xp|TdVN^gjeh6Z8LV~R*vpC`iVQ$_iSzeW6&Afe`;C0zDg6oGek3zy7w zVsXx4p`<)TsC8T?;$zMVory6bdPuprG}K;%m8l{jZHox__)bI?%oUZ&n$UPrE#eQT zq9O9MNRtt=KP#KV#Y!8e6~XPGv$6-iO)*03l76ttgY3(8E+(tsDdzi3V;|zzG;Uamvz}t%hMA1Db+^sPb>hDIt z=ewSWxjhoj&!SqSLT`{h-j-O4)I)~YmOPENt(Ic-^+1-z#^ap*R2r^b3hl@Ms$Ysn z>-PSPwT?mWtf{>EM+{=R&l5G_lcAz<2elnFQLPb=9Y0Mm>#zbPsTweukc#Y%y)ecr6^=92(Q(ZI z7^d~ap}76QAN zmy9qTl~0E z2IHFNxqLUI! z7G{f=4l*=%=#aRk=prlDXNXn1lxWy;mq>a48!ibO#K@V-OgGvlVoH@5uxOnyj#6QI z{U-6_XLoLI*N4}BsxYPBF7ek3LsoR`!?YGsc3dNZTJ@k#gBgbpG2)IN19)B-GwId< zR+t)6t8M^C(1=(#kQX}|vRpBciohUarn(KJ_ZTDYHy^~Z<;K)*If$Z_A^Q#*#F6uj z_&`;#e@{c+=q7mTx-nOOv*ouqBhD%oJa^5Qkyiu*`x(l#ykOj7BOcxjDql6G*a9vX zYDmxX;Jh_P)D0fNS?hbT|D4xsK!MHlnke=T)90?dNKVmFbmE7%Q9NVViNjVz(5Pi6 z&fgp%Q)Ri85;TK7bYBQ!5L~zAhGpaVsU_+G|wLK!4G1Z(N(r|gN@HgkMt5NiG zGpE(0Ncwv8XS*T&sU@G^a+_%OZrz`&J>?ZK+)VSH`XpI&Z}Y}h=IEj4?y*Z8ii z7dSxV0OhI`sW$T z_@8T68hS+WXYn9jE9}n5jybx>xfNuvz+!@H@UqjfgxCi?dcA)S5K}7RVs>(O0{*<&G|LQ@X z6JZ=XLe7zsVbsbsWQ$R{{O9%%zR?e5@p(hK+zX?IQYY?Fl!URpqdsrU)u-(hvLGdt zW54M$>YctsaO~)t9?DF%>!7YYHHB1I-R{iyjzLLOD8Fk7xhvN~gKT^emh$oHw;*f=&Yh&io<#Bj$+>12q+# zFfCA6IJBl_-EWlLohRrVDgr*YqT=pI@xDNlRvA55kkE@A4aN(PY7G_@DKlmL9N`sf z%q?wtbJ)Y4tadWyzcHpP+-pMhwtaYGswH(y`Y^=Ss_AX&s6LSEwP#bMg)t46^QkJz2GT0DU`{@Mr&l+>@cq8k=R}*i_AJjB*^#mgx zm>W%Xx!%`?M04bHBRcGerjA)Bs-#D=`)qwWWkfUbtR9`Z_u;NycI@_G7E|Wwv+jTy zhkdo<;%l?$dO?qN*1Z{1L$;B3PvO;n1n2IW#gZes+?LUcC%!u{HaD7113PeRZ7*8B zuxHS;UR=I;I9opJ$2JmJte_o2)g=edMd01&TRD%2&e{DB%zxnd$ojr&z_or#jUKrF)VfC>?P*ZfB z%FJVjVc*f8WihAWeln2ho6kUbpFiuo^KomzG@eVkfQq;N)S7<<*RD*XcidG}sYKRIV=U-5qEDBhc*}U8TgWzr%PSL4`SHGpN?QIp@9! zr=D?hYD|l!N1iGz&d*@{GG*c3A&M*SwqRWA2)a0Ei$A?0*!+{^0dLP@&jl@5=QoRG z&#G;sAI_jfaR*VnL~cTPrV63?J(@17TZqB=x>Qw*VBNkMGFsJ7B+lr{qF*yu@hF1P z1BMI7M_P1x7{u=$(X_i_AO@9nqUXd=zPC5z{uvSMpr*@n-qSd=RW}-M4`#c$M(phn z#unc?vrT>=ow9p!TWko2|I>rRjHXfZa4#Ocpa`d9uRi>;Ih0o&`%qhDIu{-@P z*j+|F_dE&b!h`+TVP-JvZ<^DudIoP6TX2)Z3@RTAVa?%WF`!vLRt%ZVpC5y$bU#jv z?_o)mv``*$4x@U_264G-FzwbQi*XL2G+ePsw0a)M0=GHh{ro@%C^oMVWxr$tqja%W zKEvqJ9MKRF#JX;0Mb&gani@V9+Q$M}(&>rt?BL6V8fthH?$5N>&2ZPphh@I{DE~Qy z$=loG34J-Pz8g%BdNXC6F^K!*9(W+`$F-0 zvMMV^$uGENf_pM;GR{zyXZJvmYHmclNH-!rq8^hed2dm0~(is4!#cRNQxz25rhjG)Tj?pqUq3{#N42&aODRR*81Te#ret#uh?7 zu`=-&;vab{(6I9tY|7n`Ipr5>WWZ^4Y#l6vUC`xtJ*slYqvCD@)VsOB-sT4eZySr% zTYkd+moprP*Fj_JSQN^<`XK+Sk6O8R|3ZvP;BjeH0DfJw;C{D67K zCt;l89bDH>K+1-XaJV}jE)Qh8cTOlstA$YY&bbaaQ2Pms9H%nncz^s@ zEGO%d{%~}ZKdAN|iv~^Q>S0#Uk+Xd@2VvJ0cL}Gzh4tS7^isE0;PJz$ENNi_+rSX0 z?S6}VJ2|^s4Fi(ozybyO8P3F|$saJ-*^e&WY%rz2pENjZ5p^a8>Y23&4V}V*x*>2- zohcj z5UMX9f#8ur%zZ1cyfTb6Vi;nEgwt+!e~h0R%vk>+u$&*oc->LBJ0O}`4Flo3H*ANNW;c|V}LV8>%GkX~0T2v6zUUY(|O%Tia_mW$uUg;$)b_ zw=MhQ>o0!>S#?2z&QvCU)j^Vd5M%GFp<}AFe#ieOvYf*iaQw2kYBz&r?{12H8^WoT zcT8NkIh_`ta>c@LQa-d-g!G71Q0vagrX@4CQvkm#m1}+LU83#318B3wi)+0jnX>Sx zunFkL5wZSU+_@h;mQUu(!DjU9Ax}sIn(>~SH@kZGVAdFaPVHgL`My4kkL=8x75*$v z?ZUl!zC6^m17|c3;J;hj^6H-f9QJ4y?LPGsh2s^vJm}-gSAS|zYrHStJf2M#`(dK! zgdV+Oyr{XiJ(H(-vMOQ@3vT-ez3~S8zQ&6WY^4#>(Verm>(hA6L@pRUhcyeAi{E1n zIq|s65mXrS*JKy!F6qI<1>>mJb~a6USXl1s$$MYjn6Xj1@a`^DQ7D_ySJ#cLlgwG- zIF6?^qIq>#p*Xy~FEwtvvBtuJu`gU0q#s3#pz}g!hZ*PnJ&AEWdooUj-dk8kQSb0& zp>Zl&rs{5sRGm3=*>qgkCjY^dBQ{ zG3S)57&cyX37^M7YLa{^nahB+<3-S~IgEEQ5#APaS*=jz8Qr-oi0CdVb7srHb6c_N z>Kv-Nt8qfg94aZRii+{GslB`z&)lBFc8`>~aLY_S>-ih@^Jh`*zn>`nGM%|=zsk)i zjM}r_!(h7fx|KdFaBtOgE-tLXiE2r@n|wfVU=-`yzrr_*Idn3s!;j5#Xqj4#7B#b& z_vRTmHHNxRYms|n9*r)%!wvm;RLiKt?Dlhc%k4K7Z;fE7`#r2bAI^wdPw-=WD3wBs zQDQZn&w5`+b@%BsO8FbJmxnTGi{f9b9uvy?Sp_)k5<-h9l6q~N^`1vCu2lqshU6kS zKqe3#9f8xi8LSUJg4x4^>6ow&#hW8!GHNfv(&fr#vk$9h1~FzzI{y0DoK7S9vR)&I zZKMt8rW8n>!IFfzD|gmb{h5+Im2Jv)p_P>?BNavcSTn6TwUzqvr@U?V=Jw?q>HVIW z)rYMtzauqR1`PCn!TWPBZu6)@UTshE(+32$kXDSe_v|-GM!@w2wo0p6P4dI%{^-QT zxrNx4+MY8)fx7Bt!uT`lU%m!p$Y2X0-Oi~z}eEjCVt z%hY6X&t)#YD0iUJ;230eX-&(md!b~JBnr)gBvG%!-yug3KQ2im9t(uu=+>-Se^dd- zAs5B;ihYQFeMOX*q@rN$4Uzs|Je2hBh-Fj!pfvWb_%F>K*;{T3AB}-9jLR1XYC~8y z>M>HzhVYvH3v7QH#>%8J#MK4UVy}eKHwLjN%T{E~4PqZNb>6>f!k52hQO8)$1^9`{ z8+vi^ycyIStO(-NS*pBnXa&j^4Pfb$6 zC>%D3wcVuC`A--cTzj(a(R3V=@oy%2&B91WLuTXzAS|v6j~<(ksIblq@C?K5Kt1Lh zT?9LWj$Ak|8gmDCV6o2+iR4vuVLctKPs`(zDMsU%+s6Gip}6gzCfJ3S{+rhGuz(u=xvQnl&KF?GX+SQR9rQEfLT!-}YTn39_=4sjdE1C|w<4+jrzM*zWy_ z%P)S5vixR3_j;AEYq7!>y*>!T6}sZFX_<&`x6ig=?Nbq7ztT3U*ENxza@2O-m1{yt zHP!Z7p6*tV*^K*Zb6w{@Q* zoew1+UfBO0R_}gd(tp$G;p70FN3jNkhR|d zS>;n%{v?R^e4Q*)rqZn{VEQ*IcyoQP!E!|Bv& zGm{>DXHsXtbe?+WmJi z@Qk3=`yg6AjHH*3Jft{3fFlUl2FbW$C~HH56|~qI!bo{*N{)u|>G=?z zIV5Rl;}Cw9x2a2;P=0F>#v5M2R9_Otii08SH7K0w=R&F1Eu6*Mf?3uooa0Uf)A(dK zr+o-w@|!S5OF!|_@i3|#2%^)oFm{lxnWb4cM`j1nMh>hfFNiw+;k=P5-~T(m!sshq zuEjp#%!mu(X7zA-$&pseA1Ie=diL8ed0aDy-ZH{9G%ScAMPWSCBZ#x(!kFeA#KMDN zygVgHZaQJCl5=F{{xDwL8^pH$a#30(e`seIGq(q^>QWeYEeWFT%`keckQSC4Sd?N_ z7+WQW(Rp4t4eG+UyKOk#HNyB(JB)jaLaF&6jN6Wf@{&Oq&&7vuyNnlJ>n1m(Ct>vd z7|exz!)bFTL{2by`>zLcB;hZPJ7=I(2H6@|ER};rJ%~WUcv5(k2wk6M{S0m*?ODbEIV&&_W>~rIxJZzv& zQ+qG!1$trW)hrYrtiz74S-7-N`h;7L!^-YE7JfXAHdpF!uUa1MdEZxwu2=G)F{2)5 zzMX}4e`VUAb7Q);ClXBaQE2xIz17_q+u0Kv&bra-^CUb}bECVYU?v}SYocbfp1U$r zQZqkN-KZq-8kK#nylOoTqi(oSOJX=KhHk9*Itr#qZt|GU2sA8prK!YqjC;B%7$CWx zCD$gh(wRuuf!NJ;9 z9xm2E!LJESU9XPW%O)~*`4gd(;>>j2C&E=)&XqA)B0!91K)Vy-jOPT}NfN3`WkM4P zl}d#ZYl;?#xcV`Sm*i8#hzU&y)vfVPO$gPU8s{d|>aX)-nh-1JOB0%qtRL}Xn^3Hn z8xxw)t-scfZ9=y$Kb_FTcqN@0%PNU>l`FY4v0#sD$2PHG8GR()FPSkjY3et!Vr7a+ zE=_Qm$-Z$-a9NjZmnI19AD{6}@Y(J*6Psu?Egk13NNvcliA_{nM!0hm)u!yJLO(0P z(asZ_sJO&cD)iV(daQG9qT;r0RHmBD19X%JSC)yop7@bkZ=a*)^9UBj-9Y_RJF3~;gln@=Y~z#%>rTTNwcJ zj6-np8qR~;GSRtWG?Ralv#FPyi z@ZaO^G<=-~Rf%9md9Q~<+pcs~Y{qYyO}R8N1x6CpoE@Di$tqoTNnM8 z_S4RE9jrIBr-_DQgCrZ;G2#7M7)VGoWAS?2_t&Cj^*YRw5b3-@8=)_u(`Q@OBTz!8 zS=pPQFL4{!L8&m2aH`VMG?eXV$*Wf+?<3(=N8Rl*g3^-e+=GHbNxM3vLpQb+1E(Iv zhNEq`Fgg>8=WV%osx-ZqNGMein2lq{Oplz4*yhodCu`1P ztGOW~;;vvxad)c9SnzmLBbN657eRkTW1hWW;1c%zri$ zyC&JmJtPn(62a<`A!ysno<`lAP}Y1n_d1P*BFl9&6OVU6{R^^-fj*)>IM6^M(w3i> zq54y)u#$28-ja|`k6w$Cfse$kjw7i0x)|5?E=Qh3tYcbiz+8!9SG-z@a{CLY{hfx{ z64|b-TnWP+Cr~Cs^ou3heaU7O@+H!JEg}uEk^vvJYb8>qq$+S@SQ_djES|P@B`PH( zo}0P_EuRNK&1EHcF#rVhjUioyM)g*hHOT-gw*3*lhIYv5N1vr<*B(JVzyrr z%!0oOZ=ZEAx%a2YT%HK+zGp<_{#DSZTq->Dmg0=0;uq#6#JCBJJ--ZUIcgV}h!s;&X zPU_4re+l=Hu&zaa`Cv zMxGxZ&&oLoNPh0busKU$ad;e^td}Ve)7pt;UWwSzWw@xiwF=k0#<9!NBuw8tSo}y% zf!)3_RNA`?=3mB&ACaq}lVd0R+}9(=Xr8DYuokP0!h~1)M%0@QLUHChs9iqA)Liby?$T_9Rz_eLI;Om%!MFmrkdTBrY zzU>30$2pkZDjv0=2k_*G0C#I$ zkWzqQYImXX-zA)VdQmR9H!wu|G&H7ON8f^C9By$7Whc#{c1{|TOE06`{2{VDU7+bE zjn14L?08UubC=a|XL>jnY%9S@mj`GQ-4`3=e^7}1?t*8hrqX!Uoi z-TPU+bMMu?_p?@uUi9cu9NOFUqP?mO>WiBBSpWLuvX2IQQ{ z!_@SK=-iWMHWQArtyfdDbX4?T6^Z3cIGWLMJClvpiTT&q=ux;ygFMz!1rv|*Ot;e` zCLZPfSVWO$@2Tmmw2L%MJeqr}h-NYIsB-vT8hECH%;NV``nMZ&;LM<@Kx(qtwc zo#^r#^*i#0T%ylX>&G=z{pB(nD5E<0})6GBpRO{)w65lGX!oj|oTbQ~#jak)h(_(McG=#G@An%P5+O zN1;#8(M~2Fo%h&BLz#HAqH-_Q?`(i=XD*N%6OaBnagl16c%=RQ5>@c=%)+CWNxhYc zM}@<$u+cy*#q_vNsZ2Zy9(0J{{p( z7J~LOI;zor6VQB92kf~Yg5762z-pw3MXNf%=?d`TycZ096!Gp?K2m4R)#V>AoV^aj z)_US+&+dq0Hk{ua5%unSBIX}9I_%_$Yc0DYC%}_;LHQ}ifsLM@0=vh!VvsqrAfjF2 z{7(qpOmW5BD!_82x&;!N6FSanfwmRAYLx_;RnzJcyIDS zQ2AiYo9BZS>jz@s-41whWiV>hSJ~omLIQqy(E-wofq3QH3Q>I$5Js(#xP34@y;`B2 zG!Wejy`W*2I>(nY|1UZL(M4WZJ&xsrJaHm20gHQhBIB3Ah-lA8^Wg)r)4T=d7bc*C zMGF`_8ia!!+R#AQE#lMlKjZPo;^y!#8qDYa>zkv!I1q!@Izj6;6!}k_@POCsJuAB7 zNxwM!S>}YKJst7BFa-Sr2ck5w5ng!>f$QW(xIHo)E42|A%vNryzKyUisw;}C!f`)p zATByO!0#mKs9OsYc%zyrTOqlDXrhXMGYv+Wn*)3sbyuTeMg-P6GkM@(2;wV)Fxa=H{0um5E2e9r@^GtcZ?vUf-yP1Kk@mGczO0NxO(0XVmd9cA9xv=&5r<7;D?F^%ACJ1W!mV|kQB$uSq7ShF zNd6_?=+IZq(g+C_6 zd$7S-07|{RFz|sNKCyFw#*e$$JXbi~({Y=SwqBQnl*#fgb6k#8S} ztReh{^Jbzr5$Slnh6zW!$MB2^NcDH#B+O41Z{@4gadgH=(TulG#xVh@ulX&q5JF(k zTf}4b4OHMT3azh(!29<=e3OYs6K+vLWG`{w(+pU3?t${$V63?mh8ni}zLlZEyYZdy z^hXs2E%w3u&xt60<)OQqw()^hRYfR@t2^NcyKV{Ku|LGjo9pV1ozZSW51d}t5qmm> zAYobnPBQ_?YVi%~#>Aue&AbcB#G~{kH)zJT9=eb)mQ zzjz`&It(uw?gAcy>*1JK%IunxiO4?H0l&OdVIq6Tu$!WSdgZU} z5&JF?RO5}?*V*01&UU(*7k%f2$}oO*EA4>wLx*CQLo2+D8H$N>*x*1r6zk7*K+UY7 zSUS@O;d6(gXFx|ZJHmtoHBZ{sTcYb$=RjOh)R;AQmP<^*=zw3RN7?Ty%Z+h|CS1YF^F`l;nHNOLWt{cJ7$YVRc) zm)a9|Q?AkvuVQi3^*+Tosn99w5pCq&^-=#?ML+n2K_kAWUf-$kQ$a0FbLH1=_FS>x zN*F$|z7O^5j>JG-c(#Q?#ikgK>UW3J!Jazek>evnxJjYt{Ys@H9?|TcI^t2YJt`gX zD8;#_j(D`Pvr0!is&$Rh5sw@{h3j}n>RUA`9r5V=h%g=T$eAl46OUq7gzAV#0j*U! z;!*#YP#y8e*H#4!CLa05hw6w&=?zpm;?V}KgiJiTY^Z_*6OY1w3`HIjkHU?@b;P6n zncZQ=#G_}t#D_ESC?l)~HcuC1uSoo6%EY6ji$Mrx;!)iE0L*9NQBEU%_h;f!`ttyo zG4W`7TS5~i9yQ$C6$6-fRM9dJ6-+$(LkPfJ-W`*!5Gt8?lzv!*2@{XDhWo*riAPaWI^cd%^Blf5hL3#VPgEcwA*qbz5ij!@Sg3MCA@bM_$`q zHuXpDFg7}~9SDnD-t}4(i+|?C!#$`E4t4F0{H=K+@pjyG-bgyccSG=%sp5c}2{`g3 z9?cg9Blguo@zwDju$iAHX08fB`mDv`ntI_V==+^moF0z!#q-3A_qszJ>rx=T{ab}g z{}hWWCWk`P{5MgW#JgY4|A;A_!|{>r`!1Y~My9z5B}$Rt2}e%aUQ8@)LaiR~tJ`vW zGAfA1iO(%)eP`aJn(t08%z7ekKvQbXyIz-ey3@0oD0nY*rQk==7<15zUM%a0g7coV zZuMNY>A3oo+H)()oLZ6J^#$}b;u$jvqmVbvos5UgqN(=J=!x50%KPOxxoc+8@u$y8 zdpe&|DxTBs$e+lD-(uIr{Y;hbUy?9n7Hx}pMMaC}P>t~$+O}>MsYbt{x0~is`)`%B zKVlBK8NH)?VJ>ZcR!y(8>O2zKyraLm%^^oNyBmKhm*(GiN8y<{bf~zB;y2_{ddx?v zOwv-bH}7exT`na@e5PR>e-iYWjJnREd+)zc20OOtZ}*M1+?+*ae|@8THa6}QUrX(i zX48;>8p>)jo7S_Ru?Kc@IlYFG7tEveO9Wg}Tg|6MXTQ^C@lN7drHW zU92qoN^^7h4foUsy47GIS(v}4La?(3wd%-wM8AHbEv!T?xwN5AW)bkY z)`|=&HDs`;mYq{Xpm0YAx}Oxz+jkwv$X`n)A-rmCi(o?}Uy^5g5PQ+t{2`B=j~XJ& zS%qk3*OZ$^@C%MQfIgjAKo=(&;`W+ITrvx!fdeCvKTM>`mP=^#(t1c<5QVa40o1uD z3W7-x*}Psx^DDkl$E=C`Kw31k7q0%qR<3_W!HC1| zJ9us1974aniozM&P;z=5fid+XsP>51;`e_soV7RFx3|wab!W^kP;IWG+Z2)klJvTRhFX5yZA0 zQz(8-XPqgfeV?wl8ZnuM@V>9_7KtLB1tI=$Hr?JHj74*Pq?S#(VXAEoIhS?SSyC=; zCn9e`4vpidBfl>*DZ7V=SWPZ%{Jn|^PteaYAO6eNX0JbA~I`7Rv|i+6dV zPu2~p*Xj#-Fk4oW|C(K@jO2sGmsGPa1B*tN)3PyXD9gG@DmEtz?VZMwiKC z0H3J+^apJ;XUo}l*T{C@7^qKuJ4cO*#v=7mOUfL0pH>ter@}uoaJjZK`HQ!x{-?hv zt8FIwtZ}65rBCVm%?;^6@0+yvum|0mc9~ALvZVdO}U1( zB6Yt*HH#05m$qD^SNo5NuX^34i^;phf|@_*)wQ)^<=5+KT5@2Ucq{x4y>j|ld}e;0 z<|a%M!w0e(KI$&Mmr7~<%B5mq#!>2jaLY{8Fh;t&>(4O{M@sY^}cIO!{8i>o7AIeUX9i5$EXI zita?3)3iP4Fg@ne(ubpnUS!-LxArBpbzeIEwvQsuEq~L(H^*tdV=^kgKS(d6BzX2e zM_KI$BYpo3GN>O1lkB_XIEqzc2dt{zNwIdZKmUHztbq!rVvo zaJ(_U95A={t`oc7gAuMB49A!W#uzk*-)`<0p#ghIotqjC3uZFe)f!`IxQf{_p*qIX znTFvy#?$`EVLE11{$^fZm>IR@YIhxTYVp2c9iytJT2$*ORDTU*j}}a=^7Ug!5zKUq zx$dW9I%Y2JqGLE(4`%~4W}=tt@-DJ^;E9; z!OZPqf1zji;EK6l;dg^{+^;p$L{u<0%r~tI-ZN9u!Bg#rLrf~mZPQUlp`3Wf2mVZ* zJa()t9x-*Y`f4Y9v~Na#8g#-vrd%f9Z3h)oG*AEQgInLa(vn^NY`D^y%D&@`E2eTb z9MuV>tNYO7^Z=Awhg0SU;H!Ti^{NViOLh<1S_qt3(Sh=3^uUIq-~o*QPkFpx~%8!{=tRjq_QcEi#rXO5Q_7+ooF(zeCexQh_{PSG}wkNtmb!u zD@?t#Y=arh?Tl#c&;CQJ#2=%Bp^1DbniT}1=;;OV_Pq93!hFE?hka3zeL#HB&L0(H z{uI>?)K|w|_rLDXWP#)2bl%y@TCiLEdshI2&8NhVPvQ`^b}Xi{YmTI(!{YeELAZ2b zk7#YrUHPh1bp9~`xldEkD>n!Q-|rCl@EiG?*NZMOKxWBKaVS5Onx-Y<-ysCSa*Y`3 z*dJMD!`N1!A5uQ0sPXnyZ^RE}qhY65JZqbRwsw)Q>N*S=HzU!*eFWyXv*F1HeqoH} z!^8As6tlOJm>&{xwGWTsnW3mO?TWHW5AhGM23yv4LHg8Lq6NRXjJwSA zi&arL;W-W--6G*{JQ2o!`Jo_inr!XHZV+y7zb%u@lcAj{qoiE$u^b+gsV39pXFEPbXPH^tsly2t;89N5p1+9e1FnSxn83TVU>%zE3qL48oDqKG*`Itu2P zR$bAFDVPy^yP<|Dm@OarBAO|fZfalnF$ME>ac4L)^)fZGBib_cvhUZ9uwm-u4uj52 z!>y&C1`x(E>X+$wS*{^En%=xYAv&6#$wHuV`zdk7xd0{P{Af@12BG3E9;4%->{}u!89r>F_qqqaUUuq0nOy zSzRcn^}pt^!Ff6k=1-;pzbB#JiuL3(@CH%NB-*o`hmZ{w=DYB2dUZ0{&P<0>Iy*G( zbA!$;RPUhh#%HPT!wod(`58LywTKQKJ54`7(op8wQKba)}# z1UwpxZEuQ2{}+$xUhgB~)diK*J^YloKlm}7O57kuEqY4b`&5yQ`$UlS9?@Xt8%k(X zN!L>)qFUYra!bpktx-7*fhzdoY?X1JbmWM^Y3<52qQDVa^14CC~> z)WC5ftivBrK|i; z=f0+c&Qmak6^~bkIJ7oACYHCY;#aVJVuoQgWjq;)i&x?>qXEC96i$Kt;o&?f`>3H} ze(pAY!_CYeibbWpVA7jC74(Y1yW2^K92t#8Au0T_*b~V`X`m}Hi1$rHYir&*xjhPt zQ+pw!^BA~9#lpXKB(l5pMA~X5nO}>-q2mL(0l}5{1b+jQOxZt4}<#~cGo_D*Y(6CJSYuA{r$Y%wl5Uzj}1jZgYM824?`hW zz~I|Z^yTfB@7TLefcm#^w9XxdX?_v7x*-vcjiT^*!7zA^P@#2D629*gg+^VHFm102 z#-rH-$`am0;GT(^sX}F0GDhb{@fkrf+P{fFJKGWP_EzD};NfuE9f8l=lJWU-IGor< z%+xjlPgf5|clQVk-9H@PriWwF+a#!0ghyb;KS>y>iGb7ABwX(k0q^Q0R2oNMz}_T0 z;)>lnoCNdB;rMrH5@Px3X#ChDtiKwL(&1>8YxMQ;yDqxwkZkLmm~0HeiAHyiNL`% zN$?vTfg&w)xJE=^^jtPN;fiVa$jrVl0?FSqiO@d+`|VP3(StXv8m2%!OobLVQjnuj z;ks)IQg$@pIy*!>O42g9@J)rXr@d3PHKp=>WQgXqE%r* zKq{*8RJb@K6;0==U=^5(Yrm-wFf0{r>s81fk&0P2d21~z710}1IJ7Yp0S{FuT9t}R z>r@zVpKZu4sL-}76+>32u*ZN;OZd7L<=oPRT=A=n^R{gHsezcrV?Y1xO>vEH6{+WJ z9Rdf3U?iV?Al_PAO$&djpz23iSeTQ7b>9MUeZVX6a~Tr`A|Hzx2dc>I+#RZ(kOgV= z8_}-n9qk%_kGEB(VClmL)NY!Dt5)}E&BZC$Jbo-5+Yrk7KBOJ>c>(g9h{Jb#U}D}= zT7OQ`#h-oarN;E)rxX<=p`iT>a%(aLXIhTK*}wa;N9z|faIS>wS8mXdsBuUuD5rqa zV-a`zUvi!{7URPHrSrvOa5DS_*-jmc9zQc1ZvPnk^R$v~pBanRSKg3CJ9fEa@Rqz| z$FORDL1CwPZTRsP#T*`uHqFW@`l6am?n9$dusVfGx5S|E>`*#!H3kDp`_ao)%+*R7 zK;chgAsENgh_F5=Trq$S{c@0u+OyBdaeZ_Sit?uRLH(!%dcrEp zEFz1kA85tb-L%mC11(eSCY$>oD7WEmx_bTtm8nZ<(1^FxW=-A9kbu>Xrggl-h?>BA0!A$6w{)5fh_vKuA&F~e_;0ew#~CC znL^?8=WLp`E(Cj+{oZL2FOi14mD0L9CO!U+rquMn#@AD6>hB?VTkVdDktHOv;J-cy zMpmnqaM`+#CRupG<=YP0R;#6-y+TkGg-GI|+hjs6v@cn8fE{THmK(QNnQJF$?O|Iq{&FYKf@ z@13!0@E+><-V-~^Y|&(`+6%_(>>wDl!pcG$1o!a7jPo|I-Rgnw&RSznfisGdD{1No zZ+6XO4W9>I=<>-5|3tJvis?IgvaL1TovaZ(qBWi-TA_2G3rsG2qNm;3z-K=@G}_n} zCy!h430qrOgp<2B#I@ zsQo5ad~MCnN)Gc$+J&z)`JM~B8-5~ZA9evfxtf}~a(a3-shvyMWx^GHD!JwYpWoR0 z#@G!}HLqz)S8p_$Yp*N#QA{P(v~YvL)@mB&*cvOl*rAbIOU!>?P45=9!opFuu=&&s zc`;w<>cJ-1b=VyHdplxJu{o}qI$@QIKHWdhL?^drKRYaY)(q)QKd8yj+zD$cK2fI{ zcConr1ARY+$J+TbbsO0jQQxsejIv~L0paVXdex}D&j_B#YZobPL@%fw~3UeHpj9w30?=^B<0da*eUv~C3Va1-RmvAg9g6P(gI!i9Z!wVU9GUlMJRd&CSEo7G3c zFQ$0Vus(K$aRnl5G5oL@N++7@s-mdVYIZ|H0YoAEb$Lw}X4>)}z*NBW^dEhQ(vp>LHI$g{D) z%9USeBR}0<3llIX<0FNhw!|}EQ(T<>jokP)${NIU+Tpcyo~@_sLyg!PQowX~-X$+E z!2T(H^6Ry5n^(Tuv?vGgEoZ%%0wef z%rb!M1w+u(T6$K`7#q79q6rVfUq|a9)z%nOOKa)YOB*C*8=;KduN)s^fFbv-F=&So zo|juA&C&qBRq`UU#|YaEtqDw(wWB$Xw0=mbzj&dw`aNyC?uGNdpQvv;FE;CV zPwB1MS;azL88f7@>yB>)GIaKm|Qz{gGAwHWhHX zFQ2e%o9T(qtLx$Y2_`sCtq1=&W<#j?aOhbQ`{)1sjmmp@4 zRTjK);DV}X3-r9z7$>}*(yCdm>;j#?Y&6;lQL`VD94_+a4;DOajK2KMknf*2#(w_# z&F&=)@%HK?DqPkOM>c3|MZaeEIXQ;d$lj6u^)iOvVi%YYwxgCWuRQ!mBU$=+Yo3WM8geQ@m#QM{AB7J#YxQbI$@8_>slbl&kpxk zYwmq(hfeIWdB@26bmT@0gpOoCVvAg1e25(tnYv-3tsNf288K-O=#}V(n%1_Mo8BCA z&OIcHjFxDvu|;R~a%T*(e@NcrT`=a_1KQ*7jJ~fQ(%JqlZ0Glg9;|nUqrqeHneKvZ z#!u*ds57(UI9}j}cRTIyS04L});^`ox$Mu&#R1z+Ge>-v9g5$yK<;{bOg_U-%L*J| z^2!YvXP!|q`vdYz=BKFjO+h?A)N*NCrQvq`>9ucQd_#i&<>}QEfycy-3Yl(olpQzbv3yd51Sxv2qEpRoFpMpP`GqdSEy?1SeQ@71w zpJ|EgoNC(L$pRZMzokxP<`{DEE%kZn%?>6k@%>l}?AiNTy`iaE zZ}{`A`#i}Tjrl>Os+~8|<1JCjjJe?B=2)`C3(b5iaqf{PCf(ulpTsX_aJl-H64JeJ z`43Cn^zy{r5DRq4GlO>dd%Bxoj&U2SXp_nuMZA6H!wXMx)o1!4&;y?vnIYs?GX(ej zLPLTraQ;#)t=G7tdZ#f?v^Ga;FLvMds|T`J8>*grqOqqjRx@QU_M9m`b@xKLnrG*- zPTq)~XNn8FGg(oZ--}%pHujo1~^fVJ)`ln z!KnkUb?KF5uPN%JB{SXZuxp$xlH98)u)Q7H2Ue5GW^<%Gw87*ZHn`Qjnws&`a;EhM zTCmd=>S{h*UU1wL1?(}UL)Sg0>3J6Tw4gDv|1^i)76(*svBLV`N^*W!`Buidxa~sLaB_ky=+gA9PEZcFpW=rNzEHj(R;u9d1O4xK=A8#R zT$Zz4XraTO8|gziF5_}s4&`{E>ZvX+hjLubH*trhK7TbAAOmtib%30)ERdv?KTK3# zS1}ov)8$Z3mot*%azS+!k<;aP9jT}9$+b=S*rIiEs~tL1{^1i2tNl;biW z3sm2!M{;^yJm0~H}WNl)GBKUY0vC-Hha(t;>&bwQh{$ z1bK|=fI30BLh=Q2i{v4ak06FHcrE zlqa7oFURGI)Lk#9%N3I2a=KbBNKU8=<%(nt zupE~wR+lbcC#TE#$f4W=az0AF>iV>U2(jHqejrKA70v$P zbjw^(^BeCEoc&2W&jNM3&pBc&RFKXT(tbl8D)QL7Y+J0 zNB)#$;>rg6g`I*WqN93zbJ*x2LG=6#cy5NK7ZJ8F~*}g(odZhgEzFmvL7#t zt1?=k!sdvW*xUt`J(7%T@lq;*FBhrc}tAj=!pSkr^SWG-4Xlb zv^btQyDq`UMSnHBOIu$ePH*Fmq_Bm0)TxvL6?96Jp;>aA)dRTp2 znmALOxT-!XE)5pbDlDLZR_ry(9R5kUqNxEd&65+vwVTXQu(yX8^NihcFYywKcd#yS zY%2Df&kNR4Bk@Kw>zcV8#k{6gNK*G`D5`h^$;G0JXwZuFc9FlBwaOZrdNyJ#x5(!Y z8*%A&YZQEb9MmV%8o|*Ag8qEYEnM|5sDC>jN?&p0pxk}JYJYBMHSynoIPWu3-~Ti^2Yf7yd@F>{{-G;1b`_n6Zk zTp22UjB5lTkUzKhpfO?}Ko`1f{6YLy)(9>O8;E08HNwYx%f*t(4p5EUC2k(p5I&23 z5&zk21EIx8@xaCUC^&vUXj3+8#fr7MrCMGO{u?!{)-4pWa`>N7!)iqj%j)odLJjNm zKd)r}Q)*Z(>v>t5)u~@ut;$QQoc`ab;r~>>@={%=e(Tin|E(I25aki-9f)v#L8!>mO9pVV-j`jsp8pXyhx;D4&$I(00|{ihoKPxULO%h&yv8dfWM zSk_|yw`%x5)vr8hgx!uJgV$jQeIXX|w(-y`g&_iF=s z@7NAgW6q14$9KS=(By)-VF8(?(PsAM#ZMwpcR6=pQetfX&@(Fyw!r(~ml#FCF| zjkZ~rY~F2DVp=l1#AuX3$;AUkX~re~M~#w=OV*q)I%r)ow?WC*YPk-ZzsPlPtdZ;R%)prIaMR5Ano&t{1LH^aX05R{ z=AQ}wOiR{SoAv!=sY|h$k$Qhl=Ly0KV@<`u6Jyl|f>3t*mM)aPa3_BOuQ;dU1mPv) z2NZm<9&gB){I@Nq!vx`#u_oy^J)SgGkGtw&p)jZ21mU%@CY|G>`8T(LAjsuZ@mKZu zPc7`qX){5nG}c&gY2*1fp9TK&Z%0n63Bns=P3335f0zCTV5t0meU8@z;VoBSkG=wx zlW*%9&|JX_ay%voRmK`Q@1HsE|1*EJAZ*TQIYFrA7M3V2IH=E{uB?qYt`mfJ#+s>| zfyBRjrGjpXz&|;6B;mcWW)5RZ7+cCexrHqiygcV$N%+7OEYjy6d{Ey*<}%LZ3%7G_ zO2S8D%{T6nS#sulv7CVm|5yIIm2*Q9J{fDC*B69m{Cmki8RvhxzrS++mW0n-fHFjt zGxP;mEBK|HYm)GVdw}DU_%~Z=fc$YoIUnJA&J{`cYOLY^>i(*Y*k6@Vm!oy@t2vh? z;hV80n_H;n-!DoAb@GeHrAPV;eZJ_!ctz@b&L5Ic&qU+W<)y9wn@RevY^~tOb52Wwfr%zb)Z>@z z&+Fv+DtKAWNl7p?(fB;n=NtP$A13PhpM_54{4NPbCK{VPN(B_bVM+!ka*j!Yv5BT) zgT8?LQoUS91^=8=D+wmtfRFn0{Db;XRPc{E-z34*M3aA1pI$Uh-vE`q{en=F^FC3M197YpY_-KDtKkiJ4vwQ z9#9IdRC-t)stCN#sgi{HCYo45UqIz|`i!C!ygKKNBsAa(DtMh-jDkPQc`XT6eEmLs z!3A6NJ>{?9PjX&Lf;C_NM(KfFx=<~&Qv_b+JeLF;6HTVlg*Myug0TwzBIl_j*qUg{ zUg*m661%HrpR}vcX9LdxdP$xG+!JV{gC7}`HmHGlI$Lr;i72Hv~S`r#_4;@!3 zAP<#%{{<@o&f1ld;K(&pCP|%Oh=MoME|-KR+(R$)8PpY&rr>tkrIO&pbD&6%*U6uGxAb!Bkhlp;K5^9mw_^wdMS8-_NXLy@*Glb&sb$0=%V2M+QX9I z#SLho&)-F@tY~c%0n+{^3Eo_Qa-VDDG2$WWtl&Y~1Cr2+Ti~kCph%gF0SewpTOtXq z`FaOsh?Lu{t%Cb#_enw<6OCr8K3{ddvLJL)1pKsnC7~^kv68XP1bq)d!F{#AN!ZkGffuE0}$ z0hLMty%oHzcB>?GH4}At($_oYu8Ca7p{QPb4p8uo(eulJ4_P%cn*!#_gqq;zN@_z ze1LYSBy{Bl$~`2Ht-dCL5T^(vY6nY#KX;L`!WAiZ!9WEcq8%s+-ApvXTYbiYzAJ^n z3f@+k=3WvNV31Y)#3 zB_Ws#c&#rWbFV&Jq~OuoNJ$9cF0NZ@tM&L81@Eqnkc93$N$b?Df&Olorr^QaFiGga zc$RYi7d_TzFk2A_*Y=QvP_Ci>aoz1#sVJ2?bsee=k%Taw14?>bLDQA%r)t%b5YA)# zK%cKqmVPeGSMV(DbV-Qd2B?*m`TU?4SgZ)l(EcC^Djwr%{TK&#)rTt-e46%qNr>bO zrs%J8X`{!p6?~$0iX=qw82jqe>nifIf@f+cOF~Z*joL<8qw{~$*L0mCAZfpoglHZD z1+P@}`X&XRq@5rMy?CYTpf8|Oxg84?JVQHP5@L7`D08PyZkvLqYsX4LZxgj9^N_ya zBBdo+ioiJSXi13WXFjE6bwi<5@G;s^lF)}M=&K*1OiAApxeA`F9VrQYc?kXVxXpAu zK2yPyw5gKNPt9Yb6r8V&(G*1>O*=vo`g0d=(pT`(4t=;v!Be!uB_WRIfHHJ-YByEE zS7=*ELOeHM!(-hXDpKZ3j)E`KHkSl-0?$IF=9P+GU!VxA(KeNY0o=vU^<7-1boCqs zU!`p#2?M!{bt{=5WPY!2;Y0;rsBJ6>gBVvDSfni3{HU$_%hxuP)WTpEP-cH!SLG=J zOSJZqFoYkul$9`ln!bPx1z)7Km4u<(!XEkx)>UMzg3r-fOF|;&uM})^Oy7f(6+BPd zfNzswETG(#-GNx=|50q79UUbiR*6^?04! zdIdkE?IsDMxB^PoW-2S*1O?xt?J5Zw+yLeFt;=VFf~$+PT_j;NSKzh20H0!gO}8on z`?Z}VVGPf5rHAV7h5`jI)^?PHv8-&u^w-zj&fhEeHf;w<7{_>}(!l-t{CDbcwNR*S zC-IBF#z(me>Q=0UN(Q^MZ6qO+Td2HvxJ2qR+OFU`w5=pz0?z@ZYwL8vI0av?^^$~% z+yLd(F4k5*$K(wOLEzVy0~W_dbp{sU%5~qLNgSR0v6DUrD7x^Q2kxB$c8k^h$+ z$4bx?qDOCE|0aWf{K)uO8`b2B_3=$I`&qC2-|}90`d4}M^7=87=&j$&_sV^PIU)8&$WT+Mu8Vvh7G16 zZ|#uqa4w}M8pGZul$OZ`;rdAsG#ZjHBVbdgZ*Ac0+n}`LRi)SyY@Vw^Z~C(|MNfi zVp~qJ{jbr%xB95me96vD*G9Bje+-u@CY^H<)V%2d-T5Yi?upaU_&br(Wp2@z<-XLg zSQ%4=98mM@AZh5iz<2)$2wJL^(sYN}s9ZG@(epks#|nRJur)&TE?d~`NQ7iT4y7O6 zK#jZS(XAnRFw!xk&d@?K8a4`#_5ERfFd8OXJ*np0aT>qE41b2j;9RrI|D(FI>ZnSw zoO&Ome4%LHgZNd;!Fm2LYLze%;6uJ54EEYl`_$)bQHB(1s{0|{M-r*eQ>g815d}9# z&}jt`OxQOMHleF&%QQdehJK(81>rb-Sruh3LxH=d|Bp%;`@m#TDmAzTB6n30`Tqz- zmFP#3Xa4-@RT1~o4F;Bk&RPd#-4iUO%Mo6vZa+*SpB&IlZ^>_|HB8r-vjSCL*bh9? z$JbbivR!AraPjtGS}(PNddvIG41#O9Dth_e^2SI0`0-N(>-tOdwZBsrfVe?j)XVpl z=Rfj8((DgZ@y&wuw%>YAhm{Imn6gnKK}a}a1leEleGQXKmY|f&b+SEi2I+kbdOPTC zVEf*5rbhb>)apMO^wAq)$|+>4JAs~5>Eq1bc{J~e9`33{ zL$=I~`X!dpxOZka)2&F+dra^>O9`O`b0}$#1BA5xp{&|aG%lF|P6d5`%o!R;Y4u0R zQON`|{cWHvkx$Eh7Ex1|8-5G{oUJC4#_5A3ziku-p7F=mj%a*Mb^U+zQR)?%GV?f{ zl}jZx>murOKF89uGKv0*QDj^;#r2ad6CMtj310r;V{-2se^W_ z?QG8?4QPBg&t8_AA#3U#HYscxoWm4I^tn6gGKSExxpPtY%!wuq35Q3{ESeGQjVb5W z((|Wr&=yQzPE*&0VA7sKI`)1E#3yc{NZW9v_*Rq5pLlppJWLi+q44#-MQbO-;MLO8 zH0^u<6lz=ON@EB*qOQ_+El)6~_hhZ-g~)La=zNMD?r#4|1D88OPv|+l=rKdg`5p?Z zGsP>Jm-Kb;6f`vaB@g-W5X`n~r(en%_-Bz20v;=%O0R?3EM>9UUkERciQt252Q8cb zl@{*(OWI{Ew0Xj7QWm*GgE#e%^scj%$yzDy)NxuG_?cGK+T$C5;(jY>s%aGDTej1Z@k@|2CxdQX3WLdyU9?Ir2L3zB zDL!fm{(Rj{v?>gto61SxZ?F*epI;`G(QzQJ<8)wFFsh0#QL1-1vP_yNOkx366x7m{ zZhjh0y+`-_=Htz>b_#qrAK{A|XwU{1e9md5xer}1O6C*!oOFX#V>5l7X^l7bZ|QfP z4bE@*NrDg?OpbX$ub!G_0xA2tSmB@^f0v_@M94L3j0`seLbx4o72 z&HGGVb`NRp+CTK8>n2Sf^ODvz)zUi04^+RQk{Z;0lC4Y;eJ&6@A;a1G=w4eJb&GDH za_tUkDoQ2A)i>yiRx+6nZJ@%LepJ2uHpvQ^QR&)~^yAAc(x|E=F|!FYMe{UC&Qc|l zC;Lb-Q=fWv7f{XUk@QJvKb0Prpzh0?sc7PO8W@#D`B#S1r1cqO)cBi;C#O-~5<~J5 zoK7HfmC@v^;zKnR($wU&fD+Qos5RD!WUHsppW+GB5N<$aoNt`(M43+}lGRj4+Ph{T zW%+JWt5Pyrvx+?2(f4p4#$f zV&COYG}wJ2B;S1@aXnRZRedFeR!IT2touTN`#Wex-A78VucKn?w-lUukn~qQrqYTP zB(Hgq5;DxG-Lr&BA8L}xq3x6&Jc3?XucGFKDpVF2M1_Up=w{kXD$STl$yd}!YTjCd`Ywdzh-}uQDg?CyFHBZ+%ENs1W_IlMU`Vd8W19Kn zU^SqS9h|6vG_~XGG0lX0@e}r4!3g)~OVfa#?uhdpMmyzZ!|1Uk$@qk0>mMsRCRpNu zYs*&Bx&4drbnr4d1!&n2t}~_WpbM94vh(4sUgP`D$noI>FPPqANY%$q8v~%?+MK~VG4tj zLP&K6QYJnp!5bYz3{ez;pVv58JGIeGM|B*^5W(09Ls32A6$RzU;GYN)jQk-4i5D*^ z3!iAsbzxYYenJNFFQ`VLfkZTgkmYciyzQTof#q@fIP^QIM(0pq#a+^VyO}hSUQlPM zAEk^xPez}^==Zc6mn7`l71<+QHIzzjXA`=H!(3|(3yhcw zA(eCNZP$31Z+yWra$@PqW{ zEwouE5j{iJ(5>`f6fZBQJH|`#<=#%p@d!rBtLs$xU@@kCEv03n0`TnXQ*xLXh0wF- zXs4t%o+W>vyt4VYXLf@eY8@7SywVhZJ$Z62Er-AxXp7IP+$w2oy741zWTg4^{jh#WO z?p08EyWm5D7)Kv)J`psL8{K+%?uv>5AKIobM`9QvkXD zv8U*oZ!9_?noNJqrHo-}WVBR^k{-H}*p2zr75Rg8)i_Z1>8TW`A)w07Cz*-m3Oc+) zk-VZe()*1+D8XhC^mBx93jbhHvN95E1dA|z;;=qJZ$WQ`UcQ(1Hh4E9u&+V!9G|`h zy?k$ZFW+1LU%r>VRv6eP@1_6pz5Vp^y#tQsc1nQ+qnRf2(B)>AcQv;QI@(sY#tYo&QE=`Ez`nPXFFtNS<~k4Zg{Ngg^}-!k@d$0 z>$-f9%ys+h2chWxIRpPvtGX~;{PK~a)TY5}M-VAL)xoRxU#Q7;8j?$cNLtAk%G$5^ z5xXc1rqj*vFTLDijQV-AaeRbFsm1~cAt51Q{wx0fhxYqF`2bEg^NPLnA*W*hC;yj9 zalW^YUfSE=zy0?AKfbU3%`HhIy#GhSOaJZR-;w^E$-h1P%m0`DTfdk7JG$^Dfwgq+ z@c;hgB&mwc`d^8rrkLgbN;Z%AuVlXNeK7t&(AYjIq# zu1-eGHYgxsGuA2R=vazr{ODNzV;Qy562ut*n&uM-mHsOCCAindzBPoCW??@S6 z|0{9t{*PpfWj~Am?RZ4LeimZAuY2>f!w$Wp|8IHfn!_j892V|*Be-pY!^XbI8_yti z+?170nT?2CZD!sy2j7m&hu&U>v<4s09fr0MGg!|eD||@vg6CNic#WIQbcR?W>iRtV zPBcN>m)T78f)%z+^1?lT#?SwG*rdiFxy2Kjmk68wcthO+SU74P{9_3wW*!i%9%+Up zv%PTr8#k55x+6Pu2Bx{V6Wm)=!yz=D_9P2h53hBIGf;x z%a^R6;p2)%W{tvbSENK*V`sGs>gBBY_nuD*wW8bxa7BxcHLND=p{eisL9xsQ15B(D zmApqlC$|Yhyww$5Y1XhAw2xlA7R8ob`{>f1eh?hlN1N3}uqd*Kd>)CyYhDrk*ddA) zyNW1WNeow371M>AqA*-oOfUKUkd^#vI0+U+8X*LR(i%)|i0-oZlog z181(f@}ryZG|Cy)ssvSmup9i^OmLYCeD3pi>SoIli)UhmsvEqBztb*zHp_Abs(HE4 z1QVDWdNARuGco6(D_db>iHqXiedSMIr?P5AbL=f!$*g&On{O-F%{P{KboV4%(qN7x z-3sR7YXy(^Q_O*ve`@Y#pHEvr@O0=O=EDW9f!|r6u?@zijUu@UOKcgSM(q>kV8SbP zis0qPcAAlS$SgRmFr)awx!5ywCN1Xj6Hom}>clMM)cR4op)LAV&8L-IelIW?1lqW zQ)>(RHtyXNw!)(w$H;^G11sK?Q8_Pnwm3(fLRQE(J4Z_f+ClkzHJ#z|u2*;Hn~x>3 z^&4r<#@QGmdWWuXdE5JD)|0-8Q<_y*>q;aKAJe%hQv%VsQRWq zwXNJppWsJ+#H}6*rge3>6qg@K<|j9h`2B@+SaSw9t5i|rtcaNP zs;Kbu#LA{o7_!(2s%0Z^E7JwCx0N72c{rBeSHj`sd0617g6~Hh;C5^%vQIg}>GUuN zwAAcy$$kiyk5j>F=ONgb;ff2-2jSC8Tga73V}H^pOevQ}_*6&Krpn>f%h{lR2B736 zH?50hP}DgKF(DFIwsj0lLnWX-at>q`_J@SCIj(n!AyQJmkA@eSBl&_fOzzKw;!a+! zXo@p3;sRuzos1kAacua;@Xkyc<1hpEwF8iHlVQ*OfpGW=%(cV_me@ozy$Y> z4#JzY1eNUrah=P>{A3|GG#x7%2ceSl4Z{aR(Uw8>;b0``Oh=@G95%+9z;*9n?BV4X z4iCZDS<|s~lVAwcD@_o4YzS8H0ue0*R4;g;AyRd!H=ioQGX>Q zcQAaPrUV6Ep6R86ojxWIhYHG?xXVpe1(QD#s-~+W=cfr0nnyuF!xY;_s6&I7pIfPg zlC=!s!?mDhG6T;9E3~lY!8EkZ%cQ!DOxl#F4Sha2go60QGzO#*dHg2Jd4jD9I z<#sA6*~+b-9LjCoOR+&asop+=1UeyyDAp;55}zHS1jh_gUSCMAAqC`RRz)o>TPZrG zicGfbqfd8FQ5qlkUTi(L^|z36$#pXI%AgziH>jgJp9V=hqqwxq6w~~SYD)6xjmcBe z2*{urOS`Ci-A0ms{DA_)Gidpkk5qazm!i1G#7r?uK<{q~uMCh-G!Ht*4A3i&`Cxz~eyP=F==bp#Hwg4GP{ac*v-Key?1nK# zHzPmO1%GGXZBB8?#&MBk%_}^Pcq3ebxHkpsY zw4v^pC!qqfQC`rhQtm5%XRQJkBQ6h8?BkQ5I9Vg1eH14p!`<)!? zyiw!(qi-b=Q@+#X^m(ZG91X)E^C0fk(I<~y`i4xRogo;G7lB>uK+PSpPaILr4F`=$P5!leG&b%FIOqm;14~=$tNa#qp1G5NPNE!{XVf z{p*5j>t|y=uON4SHa@Alz#`QKww!m}WP_3iF7QsY!K+hcWMuiBCiC)?={6A7;R?l| z?__r01>cNpaDNE*?*;rMSKiLv+y*y39HYv6-PE|Pn3__$Nwwt!#pU$SRl(j88Z|%^ zwVFrig}x}1cXkmPCWa!z2>5tuA-v)Q3_UKrm(r1s;_OvL-6;DF+)JTYh zO~3)?FsutUfUIBv3V-Os_izN5&_vv?lfoB)YAKao4TY5S1c=v2qG3)EJxP;7TUjZ6 zij;)(lOoD|E6vrVavJb>kV)YZ7Z{^In%0(6sgMMU4wjM`zb%tlC5`*$W)kJ&2KBY# zuzGuj>feY$xwDcEne|8h(HeS{a>k@>i6>?liy}Sg3cVEzhnU56+R-_f zo!{t(Svo?9yL_Ae+4F;nyl&DvLs1wx+#`*pGg**-5DX=LQEBO83f=LE)P6Nl*%~1v zTzpCevJ08z$1vCif8(0YOZq4tYUI2JZx9 zI-!HiwVEh8^EnladrS>t9hCWn`|P$flE=*ks`q|PUURNe)0wCA{J=fR^M6N&UW}$y z(|F|S=yghv=%Ts5C(+U)(XgG*{W+!`6vEvtDegS@P;iH&`+cR~`-akS!)SP()+Dpp z5l|X4fHvM;gorf)adMAbfCs{|loS|@;6H!Zjq$Na*;vIE`7J=}vGXkbcnrjSOW1uL zA@jezn|+gtLQ?r*)}c zNImale)oOwC-MXv&fTyl+xt-`zfVpGf(Fx46*qkMR;OcW^Rai&brz%Kip4jZ*|)8Z zxT&_8IUaLBRQ_(3)M^Xk@oDVKAZJ7xxiXO>wvZnc%8q-@fqC2vrn=1@Z!b(>^)_>H zw@Zyxi`l{Rry?7@&K9=M0-*hw;hWtHlRKi0(7OK6WL}m71c#$TA>bOx-Pw}N>X8!` zOK&%sQtyNV56_!;9&y2DO=(tDHVwRTZbifxSGN-8}(H+CrS5Wa^Km2U3qdx<@Kvwrj z{g=QOGY8%v`?t;**vvz)_x!P=-(AuZ3c~y53$(pD6dr?5kQ9X?>f9Y#w=oPADVJ%u zMieft(53o^FX_?oa(eJ13WpW#DS6;aD%sXV8WjsrzDJU78onk=i}6H1ACl!odvb1k zKdqBrG1))UtDQQakqayV&8QBG6O~rHiG%x@sjJXc@HyFE& z+sH%K7YbL}sNh2Y_Pe%{-r*1^n7^icZ9jZG`;k5#4?*iI9(wz=HUj;3e5QJZAPjH* zKnHo?`BBnudN+Cjf~LJE%cGG{jqf47<57^2ZlRkW!|_(JgQPA*p+#_=rM-JaHO0Tk zSuYyPUZt@t_L|O2yUuh+w2*RWDw7}4My8!t*nyW1Df(bCYd+IL+F#GHT|6)`RZXyj zeTjcUVjoYk_6IlU+>QmTar7fn)GT22tFF?>NKbZV@dK)>No58V7wOK4pVa@RFJ}3g zGqs8L=zfw2-kFAAROvUG^VT26+QP_92}MZyTPjfVhOAaQNv`t3uZbdXjtzsva3KVB zh2e#{FfLvT$2Ebn2pSheVC^m;G*cw9-v6bFqYGfMP#8t0BN5ju#Ept5Xhex1(sTj3 zc|h`7^C7zSO9XnxkvR2S4Mq)zC|5!R?_Hx1IAAoIL=V%9sp_bc%A>PP17G~}s7+f9 zErarSL`WTJ65Hs)Fg2J6ZR4TWKn*mxZzB(DbyOVKLM^K`(EM%-u>op0uw@HbEK!H^ zx0R%?sfN&+m2~{N2HIs;(f34ksCuT+nQI!*j!7ebJvAh#r%_^(I*MviNVrY|t8S;z z(rIdNT$w_P*Q=v2B9_vN)lm61hOCn`a4|QQyU26Yq3se*Dd*L2=3Y3td1@g4VgxBa ziA3k+2ef9hI--N;lhVGCNM0033WeeL^yC43TB(Vq`GNFm@InL@KcLcDzFH@5QdU+t zF5b9F=f6ZDeb-IOtBypZ)eZWvE*xz>H^{6j3MJfV*;N$@%Ljt%^xgI=|dPgNKIKp37OeH<{;SVt8FlE<-?`-=!)J{CSCfRc5w&8x9w%* zwn2}z#Xh6pmul$TT}J1Wz}9_vL0vc15t>p)BlVnEx%~M^v`tG`R)@yPPcZ*3vXAtZ5ctmcW zL{Oj=j<_sQWLrc)f3O-Zjy*y%SF6HOH;;z<2vo7R-!_uGtBgA{bLrj3AKe;!ckRz_EG8m&rKLR`rvGFz(* zjq+>~Ayo)*n?KN26();UQlY0Ra_dv6BtjJ#>M3+vK?RjRQ%Gg93bvcCAc5hn5wNOD zqKr}%xF;u2cn{wsB5{J6Vw(Oy6WTm?$Rw3yGskI8>b5Oaqtd;o`}SWQQJN zZ*1wCdDpKhIuIX?E>=xnf}^o`>}fL3i$>Ft>m-uT4VV!($fh6~+G}o-&8TrmpD&=1 z_hZl^cY}T$(1HAzWt4O+23{*}lBv;HD2_}f8JV#VTalddV(j&6w{)2&j_i3GB7{rbQlb>A${Sl3z(whAwwkc3RHT|zpLGC}K zeB%T?n!K3IvoDdeSQ075A0V~bbow&(1bs zk->OTEECyAVqRXb(3pTd&D{OMqvSWn_+nSB0iLe&!V!T1L;`(LGu!}`-@TwSOdrXu zzUbVq2h}g$kQzG<;sTZV&^@FBvtho-ve1NcnLkR)H6Xmt7io9YaC2z@_L``n?X52o z%$0HNN&xQGjzEW)AC{L7>kHdDyAMN!q#veC8-m2$0f7UVt@@uqVTtRBju)Z!ug=^Bl7Ll$Fl%Wzo5Ut;gg)X=o4hz%82fSS%r zHf_L2v}oOBgX7gv`SdK4_&x$zgF2W^sU{4j*RvUyMnn7kD<*w-4Dv+;Wz1>}_w`v_ zV0BjG5dGmGTUM-tXx$bzYO)@Fs2^j?4(h>ubtCJ!p$plaS?tCg1Gqol!h{b6V{bmZbQ8=f4L7HhWTQ+ zxG(N{dZMmy9$J5SLsiKWkKu)PLhjHm@_=%)D+WC9M)cTuD0=G!l}1-|w|L_WclV_K z^hCF;D-I9wBEiHt-#OIM8$LWn*O(Seq36C+ZjKk8R=QyERtI{N@tt=5@W%WWSLmvG zQis$}Drs^ct@%GGFeH#_+34c^T{>p7x`s5kl5v4R3qw6e#`$**$77} zm*hw>kSk?w7!uoE#6^FaF z6)oVQR)zQGB(qBtmUFGheT^`5PFs-o8zHpsoK1}_f2hQjw|mn=xxcK)JmD7!8ijnJ zcHR}s%zR-Q?}n{WGik6u2&GbfSTNQVnn(O_=cFs5`lQO}Wn2nq2X z5^ZsJ0yk8JoZz)L0lx&?it$;p81`Fv_Jd&}d}KyKRx|+z{y0G9L>wBQkH+g~(TK|) zhX(GdXM;z>o}YdjXQ|>yax6}Dt6)>dVyqHV#H*vRkT6oj;VDZHY$pqM=cSO7QiQ1V zVia(vlgNPOsEv_9ZbdZKXYy6Ij)EZRfg4gv!(i6#iTp%vqknhBwWvUBpEVC|#(|JD zbVUR6$FdeTl!XRi*(7It`|XR@pWKlmKOa7|9#~}-h`lyG7?vFhYu5mj*oHw~Cj@oe zU^O=j#=cEqsJ05nxf>CvdLD{p-^1{fdqIOoM+)$Ce^}pPw%rigq!Rg&I21T8V@)DgZ2Vl!i*I9CgF(b5@< z;4ixne`j+e)@&@TYt!Jadm@DuqtP|kmc$3eVnk&)ChpTjsfGa+Y#xWEA|eriunw{g zOrn9Sbdld|ODCkFvAQw@hj)xe(**FJgiO< z9Da9}FSXS1XxtebjBNQuaOGF9N%}u9IxK-#98s-wD-?Ch;~-jfld20gwMSa{tfyT~avK8I2C^@xJ391(9#k_-?QWk2^%sy_KWaPVj(n zR}4n4l?CRlM&s7=ghN!_he^0w1Q1MC|GiFhjuztg{Y*$d!>eY#)qJ2MyHnT`%jbgE<4jVcRwu z>3$)Q9HIl8GYcRxR|l~(g1DEt=-W5Yb`yR?FosXXz3^2fuuZF;PFLedD-|;WGc3knjf#Bw)z}uySjqZ_U|P10cliuBbVp&Euy@| zd8GX=k<@N0xC9b%KV>L}IRgE!4?)eR8F+F*4lkVu_nihKLDLi!vj^e#Xfu>;9*m_u z=7=9T6jdCep;oViVcTsm{Ff@cTIOK9@i#Ks)LH*X)?>$(Ifmp+jE6(Pv82j{ zZvN?_wxOTs@$L?a%>O~V&c7v(wIAu>+BcMS;x9dueoi&7e~@$=kN?OE2~eW6)Pc8+G`M{!b7UPk23yXW5h(DQ6m~!D#M2)$|mhxP}#aFJe7T=|~ z7I~eSbqbb3WUMc8PYTh^hI6cJ%ThRx{6MP&Ty;_^XBqvLp`)jUeN$fs=S6`~?is>S z^6x3QzX)7jMxZcLfoyl2U`CnCAXF8E(!1g`$tD6u9{tH@{d^S8>|~#2hajP|i?u!u zgx{&>Y&nl5NVYXI+v0Et36#DvOCDEBX5|y0nca%6(WtE2nfw?Vz%2C zB1`)o^LZ8t*`Ru6G9|*nPz?2n_Oy*{Z5wMAz+M@3?o$-L;s>&*Bz~ zWiij;2t>5yHsYLlL^Y53yv=0M#{&8$(DidZlC3M4f4(p3+{@YU z)Bql4tz^~wWUSG@#o99ipq6`{$xjPJja&m0n;3}p;TPGLkRSn)r(9=|d}&O#)v_mN zgJEBGhYi)>N3mozQK_FM(s+jlzZjuc=#fEazEV`tES(FCCU`i!xsN%0e;smSD=HGw9 z;7aDjO)goD6RaRH5ca1}GIu`He4&%9m7nR!dn=j!Sl&?O6q^>xQ)ZW)Vilu#hq`B2 zu16rc&R4O^-h74Ton;+-kEAzNu?u@Sl{m}V*Ybgeoo0nQ_(Ymcv0W>8|2e0a`x^eh z15U9h!P-z1o;%HK7l%O>m2CH*F!&XpV=v@F(Uo|fN#75JL250#s2U2j?(1v^_ZOB5 zUtwL3xjepsEgR33%*9t(Olugr3U0HEQ@p*&HD>xW95ogLF0jLUcn5`-*v@Ty!1WiHeG6ZaHy4>A zzsH*#F0lN+VW{B^rSI_$qb|IRbzf*>YnqoK@qiyp zk;IH0-!s*B%aP2(GD1xztVN@R+D%1q$Luw83==HFtF0f|xK+zBC+Ha!T^E7pfL6M{ zR~REw+9;z*2wNj#kSy)PmKo z|AsDa7s7G1Sd{LPqJ+A)l$;=pep;Q>^HvD2k{?p;tdF$Iwuv$Xrd|AKd_o;1A1M4w zGpVV)r_mqZlJ?0D)MngCWd`r5Y*QEIa@R`BvX3OB{(;l z1%Ju$N);`=DvXNnr)ka}j>|gZ1b2>VNztyR-M+#YE9nIFX*Sq&+6g)w4f87C66PU1aJtaniFQ3*+P4j6Y4o!=TE95 zrf|59hNct8e{fvSifazsAJ6l&wm9N2$NwCxwue2(|8(&PxH|_5Jw0QOPL30@v~)x} z2NYF~ut&cs?l3oUgfRym)%>=@4h}eqUFm?q8v-H3vtD#LFv;<`Jpy`9SB~{*Ar_NgUL9A>q z+Vw%Qx|3)3L9ycM?fRfwr#R5G7rJ%%iCrJYYt`}DSj(YZtwQ#FSg=Q5c;vfE83p6) z`!HiVR&)BWVl4s}Zpm_Rnby8Jec&?XQu{s-+9l7qec-d*G7f!cHCcJvK9Jhf=?;CU zwt_I*K2)2qn+VKvR?;k6hdxwX+FBlHJe^Fg8*KYfaoaX?6y1`QG>m6U)htVa=; zrx22{sUo$UrIfg$iVVm3lhJ}h)U+#+4sI``?1g?Lzj`}aJzh$FlXIxhKY-$<@zkrO zDfIS47FDLilTz~rGWe55&sip^g$M*>^N%)yFHXiVhfJy*wu)+gbEos=3FuK+Pc4sD zP+^)DmW-YPYeg-1ESiY-#@w%0F$FIljY9OgNf7f=!BxkpFdw7>w;DtIwNirWgQ-yG zP|Yh>CUaz!0zOSPLjA{~*u8cNHgoJ}ON0@OHVwhMZow3eY#5Al=|)iHAkh-jskrAW zi`V^)5W_*F7C}>>)NAffhl;csz4e#^|0mt-N`nb>g1$2+MOT|t1(K`vnC=2B$If(E*>8dklRE}9Ol3yJttlK6JP(2c4!oIUQW)z@vusqY(F|j&eEN z`!ok0t=+`&;T(4~L$G%|{=eeR1uCcf|MySzbie9Cl2lU3EtI16=iN<`5UCiIM=D8Z zToQvPBrzoS+?(9X5ONQh!59q2B_RnRx#f}{BRZe`nC3q-`<%1RS!bPdTC266?|Xmu z?YsB>e!iF2=d%wmc=Y3;Ht^EAM#{ylaqRBTq<*<2JWLPJcLS}^iG4vkG+3f)Uq`yq z=BTh^?pazg#vHpNLn&`GH#&=pY2ONt3vpJ5(taowzFS*P+i#k~D5Vpn?0+hxeGDa& zk#B^KvvjFWSt$fO?m$=88IVjKN=oO47POX~#CPPXQH0<`iE+-<*|a~o-R(t+(dwi) zX+|B(?5RkrH#MC8DCmrGq@w5Ql!XCg-RhQ*b*&psX5a{ET@jLcQm|bzjMgx4T)}N5V-b^gaJTN$XBKBb~vcqf4DIpTQ%YfsPn< zu8JD2J0S7%EwWp0hb+6Br2feo&dn-m+?3e9eX&kP-fl9sh}|GXI%Y0ML9Vv7aW z-Jj_22vfw3z95)r{4NB{nJeVfl?pXx=LFZPr-H+YbiriYB_YM4lmnW`D6;od?i<_@ z9F%&8g@9&;bn@^xA?s$1;OxFda4oi=Nab)LeeqkNfq|oQC+%Tydake|ybV2=r4G&h ztI~hFcIIuY$FnBbq(j4&&9x1L`U>$=;r8CX(dn9mJxrlrjK-$*d z0*eJg;G#uT#Q;)uSaW!e_Q95+i|8l=NX{PSXuh2D4_Zsh*7#!K$2D{~L%_xm_Oe+A zd>FEUR!tJ%7qx-hN`0|KV}p`9Zw1aC$fJD^eUU#tkBmkOC_kG=R@VUMgd$R(Eui`O zBFc2~!>WEobiNEwsB!J(=!XcV;d(3-@TKQI8gLu9x^6$+;L(rw3-?p#O0F|*?x&!q zK)&N~;vPA+BpfH3+rF?dIZlh-0<~S0rzo;DVdLvk>UhW(-KLjP)GMg48Ot-nu%rJ9 zGRcWl;WE#@c;kfqY4+P5j*g>O&?%-Hb2^-+T?NBn8@+)Ru{Ye6zzuY~VmP!?*<*U! zFgRY^NHvoqQFLG=27abVQ^UWBLxqN zMC#lhDW;I|`@KI>r_^DXv46a*8!W0lMJ6;16*o`QkjoJmfBiIh77gdnJI78_XJHsZ zPMxO=`)E{kK2OzLXM3)kLN#IGh@Eww6#KfuIcgdW@fwC4ziaf;iP>$7Yg9R!-K<`W zq~Yx4mNNVr9SHV-Yu7k>7(5Jdmu^xIb~+T%t!0ssDrCzy(+lhG#gYvTNBYs5#aQ^9raM^sE zP-l8xFs>RV1Z}!6psO-iNI(8eSiwWCAHA*6rocn6ba*EiXL$)}J0A(>rwZJ(;)o`W zZ3V^f3ZV}JM^zJAqRp!gLb=~dK?IGiynZ4)I^qqt1z9A5NS$7{Ks7zZM(`P+dl=eWk z|7;@bd{1m~&Y&MF!*KBMe(G_!8$ug5P_GwZSigWa_?Wf2!?CG$s zi1x2#&xj26ic}i{HxA`h@dLM%8@5tNNjP?x=h6Doa2(#UkvR0n5V&aM(Wc*fpsw}+ z4a*vW>}4Ctr>_SNFFZuQ=XxS}=YG0WcTe z*%~j*xWEr{ffoWEb9{h9UU;x%AITFrH{>{!8en(~-%a@J^}^i`2Co&luDPtBkw^F_nRdGv$>P|1cL5?rqPqG(zceP-~; z^Ot6b?Au=$x^IVI)ZHI>)iY`1mEMqZaDj@XAT+mKM^9%5A>_tJ%Kjq;x%S1Xw`o`M zdU{(GfZXP5=|qR#SQNQ|O1k)Ch;scJ>h!HYCYG$HeO=fKQu}-Qg#n~#?q=x8;L(ka zW+>pP^N>kqm{s7u2n6kij59Vjg{&AC0RV*C%jMG?KpGK-MQ>cyNjz z+L=K(Sa^_T?Tdlua;9R`gYYuwFqy9iM4WJt#%IPLAbAZ{jbZ-}*JC9#F)tbp->sr# z8DH7JQnC&0jY^vm>Txmv*$a+PMNfb9$UaFkmHzOqK1L^A`lIuK{dBl(Z){90roloq z=3L!RUAEt)m-8)gn!%%)MV9!9!K1FjS|a0I0-1z))1_u(DQ!|T6`zQs^qW@n$06l3 zs#XXzAU>6fF0`iW&1O-lX%tPomPNS-h};(~AjRz{3VJi2vTn2?7o%KCJnKicKDp$U zV8^k+cG9^ZKMGx3Ksj&h>00V>vfLOz33jDCSKvS|_g|r)q``DQ?mC$Vdecd}hjeb$ z2`kPPZ13wFr&5fU24s=r zD2QD#5-(0AtE2r?;F0BH4Wv1Zr-3hnRNxUU=m!xzvfdY@0*~Ts`l-O9b=`wh;8CMp zunIh~{1~X>j+7N&f>hwq#ZkRg;E~OwAQgBNx|W@ZxwF~JB}fGx4H*!i0*~C91>rje zkK7{IFPy=nM8hBzc(gf{14-Dk)0##QQW-o7oE^aaAyk8DN| zhEJXXn;H-J%~wtZQoUho(V2_|FP!5^#O!wd2zV!uzBb__gGUidd%*jfmgLA@L+g3! z9l1W-*KbQ-{$LOJ{awiJgcl4F%_!(+e!s_9lWnIU9&~mhqdn}%T5m#;iT#j%k9~({ z_Qi*Jreqb{pK+g#6mc&UHOiq5be+Q?Mrn4V#qpsie{4Zrxo%G%+lc`P_EJ02iu5uE zBYn5PC=Q!hV=pT4@WUkrkBEokcJQ2W3BL^&89W+#ClWf*4%B*?4|?Q}!^@xiK@1+H ztoMc4z;W297KkzikBSom5ysrnr3ZdczGv|0&CMWe=wQvwjm;8E)2V0f3rA+CEM z)OZ-}xAP&`!r)O%YF|ue@W?8oKbr3xhW6FL$Z9zp^1KiXR2z!??gNm*;L&rPeyC*d z$c0C}7BP5K%ke>atc`@v;}FC*?x6>V^!e0AUk@3-L_><{5fC`+yjS=4+g zEZ0HN%7vshQyT?8%%`YeZM@mKn8wW3#=$-d>D5e40-GC>J%9?alRbWO*3g=g*rxXdeMD#$UWxM61h5( zIC@f9sv5T6nNNAm)sSN~pPmenp|fHEU46jbQ5_agNv}q#HeEzduFDX+bTO4&{X$oi zM%i@Gilf-H&Y@@C99S$hn||w|j*^eTi1X>W-_alPeG9PF+T) zYdKzA<_fC5@|l!X?Us<+wgx&}m_tLV|DX^43&_OwGp!oCfOfq9gEFEr>7{NX&7C=q z3i^MhBYGKRtivZfV-79ZAcN+PIsBTaqk?0p;|EzRcA!bTk>CT!@7Ul}}dy%7XGmk3z(OQ%iVS~%+#gp|V( z90Q8Sh@3RAosGyv29G}K^g;1L4!cm7PL1yD9kIF(${0L)s0c))?GVU4GdR%bCkiO& z!;VOURZshC29LJ=CyP$c=I}c*cwUqxqRx|q|eKhp2Ar|iiAh*2_l z@g`*~|5EFu+T{T|pSQiU*=z1|$2^09f$=(jx|s8Y_a)Eh+>K0Rxc7u9m*u zeULM;A9UDF)0JH@AJ>LJZr%@BYj`|qQy@RQeem{bUknxcz`h0lPV$8^&58}fe1B}Y z(;Gn_2(zXJWAQC-7wVRT8Vt? zQrh_%qGXaEeSb;rtuww8{H(bR80>}gr9D-ylm(SuDp$(1Q-tgUSLm+hw%-RHV!fk> zoE?4X$XIWcOXb5W>_N#a;J6ULfITW3SMz#h5NXWPfvjC9eShEB`v(pZC7m0x^FFw;iLf*^X<80lTrsRJ6y^|^)8lgu}3A+MneWj1mh@Wz887M6HVFn7X{ zC|!21a;D%24V*q}Mf+@JxcI$2J$&6vSo?<^PWox#u%8(rm^~?-wMhH!2m0{JfEs4| z3ytktLpxG~r)s|l$CLv;QPtvVp|0``1@(O;EV7&+v|ne5ggp!m4k{P)4!oz({1ZaW zlDDJ?+$`wTX9+d_W>9PYiOMhS5z=#BQOdQmJhAwKOhOBU6=TcEXWd1iH0=)MoGuW& zI$x(cl2Li{-{}et2$kReN>%%A3hV1{P@2aEL3#G`1qxLz6XGAYrJyh)tljYwmCo5I z{NC7|;_f$NJohT4d(IRFZM5XzVfvW1&4P@EnquYgcC^g51spQNgmK1zsLJCFnB3dntCeP$X-Gmp3*ES+mWRSaqa8SQcf1 zmsU#z{ZB2qX*^kozGj1J@)PErZj1bNtA$;`wit3|ituJpJJddWF7!Rv7A_MG2;VJl z4~<$)`mLoCei&g-wmNoLyZM6PI;kTZ?UxH)cU-ZrbB3^ZMrU~UiV&3dyJJYIuMqXN zD_WiDD(v-Dy2I;@t#D+1cWhqcEc7hqas6(6P`+8f=07ZjAu>07`Vs*3dI5>+`tZ9; z=sYn937df8@ZNBlK`1v3g!>t8M!5Dt9h=nY9BNtptsk^5-SReVa7FbvANDC1kP!CJ zdsUVz?)29Y?Dus>nekfhcFNMucz^elxBC5#@M>=$Y`x`-;T4|3)q^e=W6+)KqO5RZ ztsmWoH443t)49-M;n5hPg~1%SqVpj-Jou<^(=3?Abu`Da*FRB?*DIlJ+EL02Y7iWU zTp-&(GfF&oi#}ZLKw5L|Q{HeJ%AQtBJCc3rsTK1dIw##}$iRM0A$#>VgLH*NETvqRfC5&9g-tUAcMl^r9>xu=k!C1B16Q9+>P&%m#J~GN_F{T@q zaqH$uB2PNZ;MUN8fKy!uq5oT7q_r7{ojcgSrR5Oh%<@OO_Ja|qoa&D;ZbJ~s&F$uy zp(xlIfa{E0y5|R?-Tg?0i+myb5P_&~1JFNc7^ZN|x2-%9&$|HTPlw~T4FTxfkI_s0 z4mibZXCD_&3|KM}vqOAvg;7kMJTIJQ1haB+XSg#D*!j2{wlEJ^-N_SirU_7*(_j_7 z?pfvu!y}`)xZLr(VG?fc?ggI??A25gj&jZA!t`Hy;T;2r6W9|zaN8Idn+b4ukO-UE zJnFUoiqJpP8y6YXXc>`ilW>~p(D?RoNKE=Nq2bC;qGH$Ay2DRw3Sn3h#x#pJ{O8_^ujq) zExJC)9nKXxRHyBMcg%ABs_Biyn=gg>2|bV(`%>tm)dT7wlTki~P_zEBVDk;fk7@pk z;FszSVwBTfGXOeC$}2+kJ3piy-X+Wm142jT3fniiV?VQ;dfao#nS4Z8yFM6Izh(>V zdiF*7t`UOf)gD;HET=HMAFdP?3Pt?}F$0(_7#v#=20Qz zZZKrq&I)m(+z`tM=EvTyC}spxo4SLP)#GullN)9*f~odnS1e)#^Mg)zZahrFt%q(% zV+8Zeqi(Qa1T%-H&L%Q~IiO`{ z1hefUH{50f(_ZO@M~q-r7I(+Dj9$j~?TREuF9$bt#Ro<&cd2(rb4D+}A4>R@kxM0q zR}gW$G&>Hj&6wWeZ+umlp4KuR&SO}0?S($5VQ$#qL68y?-wuU$c>$UD$mq$%Sh|&F zj$R)MD75(r;iqT0ly~xra5i!d)wVK+Y2;c8T~{JFYiE&jK!dQO-3pqv#tiv;v*>X4 zQQ>IL6!LlWQ3zX`Pl;h>JaILdQu2=qYuQmD=hJ(k;ax1HJvk;=C9*G2q!}(OQ|_YZ zcUBnuXfuT^Y=xhkR`B$wHI_V1r=TKh#E*!fxcAmLp}T^aA7dO}#vRXJbA*D|#X_~N z0UiuIAv8av4Zpy%!qyQ6I2*HBxZ|!5zd>(Fb~0KZ<9z~maS_>}j`H$b3i0h;QTnJT zVVBMeQkb(C>>Tl4_U-FLI_?hMezPO~LH=NL(4~siYw0bB8{1r<9y%6t(V4 zS_2s>`!$g2D;4Z^JAhXBGJ^G6D8&{fKxcmdMYttk=+6VmwQ>x0jR~ft?#W2MN#xnc zl~v0yO4u+KK7~VQGFMI?42DrZ#|fx%7(fk=l8`tifu0;5k5c_)8XuVmpSH?a+EvV8 z(5CU^&}=NK-+f1pZp`bgP2kTd#3NalNTF{=p=x(JKY*P6Hj&mIkA=_h@g!?K3ZW|! zscrB`q^(aR2lkL3x_%-h$0(3uJ%$Vp#^6Kyan$)#GzX%aO6kSy(7-34{y7?lt>S1B zN7u1bn#Gd~dpejnj-umHHR++6{aZqcMUb|hT445K+* zGUacgsjf5@Lmr2dqf;z`ycE=_*(fMRjG%kNMhnk3rq7jvN|~m)yGEB(+wl> zVf-kHJ~$EvIteuSjRMNtN3k@EKMJtu%Sf`?slblIvE=-Y(W#nIG<3fLi4LRa!EwG7 z$KvSjY6VJrjG_tS*tzV-I66F&^^v0}nNQU1s>Nw!|LDP7gOB_KTEaD)a6GLro#o_tqXezT~JkM_! z?Y@<>|Z%C|7%8_IkX50h~bw5CKE4~5+~H26M4$JRq>cdrE0ISnVr z(9uW^A5QHjC-BDvq9~;y0atz=L3#ZWIG|Jvr6nbx=KV<8ufgQx=@E2d0q@6MLCY@3 zBWQCB)x6~c^oXI4$K!FOSq%NLj8F8Z81l}IN2oyz`7zL#JD#I$D<{Td$BSrM+Ka=g zR7BIIsCcZn98E@E@z`Y&O}$3&XBnnOQ_iS(R4&H;}*7f)NAk5 z4X=f%TAJ8B;Q{4k=nJbKRgh={FgkZ^nPoF+fVDr4Pw{6L+`y(;XX=g0UdH zgq~J#4Z30wteSCTT=u9x7OSPA>Ds4iaz}MA=5rUlN`w8wm4h^~+P#Pz^yIKGx+L0V?`^VxB*?G5vloJp?=6^{2xnzVNV~hy;!gqjS@XOso8%^k(|KVA}#p z)0#k5mwD`G`ZU_d^t64fI@X=QPse9n76Gn z`gq$ZVWRB{t=_gcR@nh1Y1Z)H&=t)STA|0ePDmSRiQTQcV~ceQWMAomLU#+?QMlpF zuVz?K$}!)Veh(kv4m+mb)w8?fMX)i-mUKnJvSx@l)EQ&h@8|K^4ru*rOSEj+9SF@2OX zhi0lG*J>xMGJHU{`?SG;8JegmaKYgV_bFtQ3p)Efr0!m}nAuVTPkVMi-tsEivZW(Z zr$3;vUpk_C(nE^aV287hWtbAt3EishlcjQaXXGUED^$i|GjwEl9?%Kt8uw|))(*%` zxknjKI&lbA{>(<#w)}A!8LV1$7O;^L@k@mPWm?NH!u*chO zYUsSx4s((;P;uOjy(HBz<$*1dr*O0cS6j%>Xrh&!9Tr~T2&*=S+F(#~9h|$t@jdL> z{YKLscVs%)?%|B6NliqwHb4e7{8^n8I~x0`iAEPEpglLE@ht3LEPCV6uDd* zp4Y`N`xeMcxl1FD>+nGH3st@{-73maSYpd{4&@VMfp_KiXppxBb_owCcep7k=H8-bL4t%!! zhs+v{aC-bbYSmT;xozv2e$vL^+Sl~^FWL+v){~v5HmVw4Qy*7$u|++tnyAGd)Ng2% zmNrvT@2T%0V;-P>K&GWUfL->6P~S|6+z-{15zp-V_xEXEuqobke@Hv{y-mrhru2() zn47$$rT5HX|Nb%6^x!`G#z&ORwP5128gl(l3%ndwLub4-;W?^?DiX91qftYq{DjNr zy`bEaT5#_5f@YLxV=b4|@^NaoH%S}keKk<> zM(ft`1H-M`8@*+?c3uMkzv!dms@HUB=4YC~uTk1J>_Rt?$Nat+;P%x2P@J0#T8DU) zJWn0%0%SPDAFY|;`iU&A8X@jP0~JnC$GL!yqfh5IWr7I`_I#mI zK@MxQA!x&NepQygp5EX0QlbYNRiuZM78f=8nGj-Hqv;nTr zGg5wdY6zWowe-t4W6Y|nRUPQ`Xl>PjLS@gWKF1h!m+EMaOcPUYXrQvf1RbB$(eGn5 z@VtTp6S|t-!-wplIMex3=zNF31ds;C@6En+IS5N?&O3a z5Ba%qal!}w$XUf!M^r3T%F!{#5f+vj=#;Gs^RY&dg*XB&HPK+HkK)x_yjkq41r6Lb zWjfNw2v0}qWBwx}tekHMt!c*SGuRM^_%@dJZpYIt+}&0)!Mq0?p!U2mPJc8(9_zW= z8>nC|<5;H~=>2JqmZA5VJi{H4rR2$>T7?r{s;hCI-5HB}s=-oWg)}{Vq%E++j0w*; z{Dv*85}r}NiWaa**2nt!T({q>qciNyINZws;_~2F9?^c<0yp`?G}E57z}7TF>{(@j z`6~^uBi90UT(bF7EinGf*ZtfOB! zX4t$Z7lslWuNmUYFKu8x{|~w|(EOnOZ>m$X4DKkaS{*{Jc;uI=6ql|LAv*r*kUzjgoKkSMWI6O=>xH|ADV?`5k(|R&%Y@ZR(b3 z3zrZhoVeA70m!=~U%?TQ=2y^r#zixwfN6Tr-xz>m}QAS!X?Qvn99%4Bj z&PsVbvGF@l9tEneZcdV?Vi|Gb9RmjdKGYoL!KF@fD z>BEu-qiizu@vtWcrd*(hzSq>1XkV;{RiWGp{-}%VjdHxVYmdo(&&e!BA2_awJ>B%s zd9@b0mFnVZt`-KpaE8yl8k#mv4^EES2$}AT{FP5A;-D@($8b|E!x=f3pVBjhGp^2i zPH#Io!!_(F9WHWW)BBuOt#ZP-O;72Wk2UQX3i1N|H+7>e8r#rxM z%R4&lqKjR78%eRZ9lLhbQ_wRu*H)kY1s-xeyY8>~S z9y;@tYuVy&Mdg2gM=ug%|;A=yqHE?9Km}Y3rrdZUo+UOvd-)+C( zJG6UzGhE>?`(B+*G3J3Lw6<}{xL4AYT_#vosR?UtEeEB^vGm7gP-gPv^3tF9!V*0gbt$LgT@6v%xs1%l7{D{(IyouX{4TjlwN6Ix?V^X7S)8xp z3eCB!i;BHv^lrNm_T1Bk&1)T`h2Nk{bB)o=fE%-6h8!_H7D=<^s-AU&&`At`N_xX@$6BF~j68BUJ|$)8hK3rKpQL64T;= z#HE-PburzvJ~UsUN{dTzeFqDLYCmFH+@F|MiU$$}@e0MIm~P6bk;OrXSZ_*KFwrLN zNYupxiAymp>SDTS{lRfx)8bMrYH@op-;%0mRmnB&kJl?z_ed1PqHkK!lumv5dT}W}tfJnu!=?j^x_DqQEuL7D-gF`2 ze#G@+35fgobHB>J1hMGF9XDkVA1*O1>f(lCT3m{0@c`mdyw~CZ#D`Se;g?wsSiO7# zcKnBZ2fj|q0=;?W%Ay*0XPvzIeRz5I;_71t-VLA8*GnAWzKb1(tBKXTJ+ zd?I?C;;@~0i3m)x!>Mrzm|@ozIcW(TQ^pP^wd}CY0qHHbb74|X&-{OdRKH##S9PnO!G$uT3hlWF=;i+^$iT)V=K7<2?OdX4x z+K%igI|lc!wnM~EV{s(H2^7q}yk1U>>n5XYxiiMd$EcRwkc^_$POu0`fPNY`a{F*B zh;{9_*ZVD6Bsd~w&3GlYhuTAEI~EH++o0-V93IPBW7mrbn9Ss>V{$T|gc+<9W3ls# z85+89(2#;=NVQ7F;@uqY!6g}ITuo7#G#3Bs5-WhR}zJ@QpM^6_@N9Q)3kT z&SU*PhH!Kki;_$OC~l3x;yiuC+*T%|_&4qvT}np$90Qnc9*6C=M!3Y224}dzx%uU{ z=+0)@VaIqF^Zd@Xr4ul5y#e0%@i1DI9!%0Ep#K~Cm**Ts$o5Lr9EpSY2vTAvvcM3k2Hbi8z0CwB+27*GB?COWpN6e-*r81$`G01Hm8yzDG!9}-_W#}tEB~~^Y!3gwm|@ZW zi#6H*FEgwZ8v>#=Ycjv$Ef7yg)c<>C`0wUdtg4&LZ<9IxUp2#D%`eaYG}+@O^DEZz z;(z_{11pjZa zqWCzSuN?`8sU7?Sy=tu%eWMWNXC2imu@miCP6kJ8mQ8O!DGc{rV=ICfACX*fkz!Ooxq z$LU1AEk5!A$=C{g<`EtK`Iyjm{c*BwG{etsARj&MjMob^1I_3sryjlNN?unQZQ?-< zcX{$y(};sZ@H8AB=tCQKn1){z-mOlcI`M!^KX^NzC5NT1n6FUNIHXXM_b`=5^M~dR ztum8$(U)K3@4X#5Vi88bApF_l$Kmu+}Tw@K0`;IqRu}D1jq%98+fA-%aAEPecBK|zMSDv6L-z@&LJ|vITl)n@=n^ht|`sZez7ysJq)wRDi z8(Q|)W+(2*qcr6=#64Kum%q@Gdx}4`Yvfw$^8Vsa!&>xwu2(R9TJzSq1Vm%E8OP-tpCR#WY3exSA{|7-ET_Mt#+ox#hM5#gGC@8s?$Iiu>2 ze4G?^?&qJ@W#5jEw%4=l+jldY0yg$3IrT15CEiN+@_ z(FzO7|E4KrGVzMT!=L_5{Hn=he@HaG;sEIt8%i{pthq!B54VwMg~|d)i71nGk!TU& z9ulpffW0UFb?dmOC0clRs6;C)7^>3v`v2>`%VZ-Z8Tmd*w1R?fC7Mh&U7{7Z%$8{U zZ2etRs)}EhzToeoN-Hc_EYZSmER|^dd`PrHm$ectJba@>lcg*3B_cmu5-t2jfkczZ z_DHk>m;DlrA54iROF#BiYbt;7AuK33Dajad<7bJ+PqjoVbh#+e!o#mfG+Fxfe`rea z!2C-5Llm{}8x;~wCc7`u3SAyaG=7^Tnk@Z=L@OwGt(1rnH|ixCzlsvAz~#$-M^o!9 zc_Vx@BpScQe`ADgve5JlbD=aXSXtGjci57mN**`U<0M-6$_cC~|5^NO*(4cdrIRFD#EmHut*~~gL}RNc(ZVfe z{Ff${zgW}-F0=ki{8N+Jrb{$7sS+*ReU3zv`OKAQ1;H5-joW|oB%(}_DbWg(=1a7Q zDOnPYO}RuXT)0r8g|GZx)c)E2ljSXvWaM^$L<>KVEzxA9OC(ys`5cMHZHT{VT>fJ5 z%c_?BP5kqg)Gn835%nu18aF^BTA|)*i56jzE74?1+cgrATQ3qV+1w#FVPAXZkA{fE4N5AS>D#aYhT-c+~oSZ zsJbN)2ewNzS?LamR(O7=MC0a~M3Yq&O0>dS)^|)f6eW#*JHv7Ji^qDG_C*rzBdz`JW^jw~Qs4tm?ExE37>u(IV>4 z{;B<0{;DgMos(oN)caMUg7Sx#4}vdCG;YsJ zG@0UeiB^zwRiZ^qxhB!LFCcCI6)wCX$r!P+Orpv1Zb~%nM@Y2r1Gglatn_x1_O<-Q z;x9aZr%4p+BUK&1oe+s8tGX-E3Ti7QT6q0EiN+n6e`)O6RG|0ZUn0MX77rzw%(hyh baqs8f+M!c>G#9AP58bVED0H`O%Fh1=1WNGs diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 6773fb28..8fe55dd0 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -239,11 +239,9 @@ namespace SHADE bone.name.resize(info.charCount); file.read(bone.name.data(), info.charCount); file.read(reinterpret_cast(&bone.offset), sizeof(SHMatrix)); - - uint32_t weightCount; - file.read(reinterpret_cast(&weightCount), sizeof(uint32_t)); - bone.weights.resize(weightCount); - file.read(reinterpret_cast(bone.weights.data()), sizeof(BoneWeight) * weightCount); + + bone.weights.resize(info.weightCount); + file.read(reinterpret_cast(bone.weights.data()), sizeof(BoneWeight) * info.weightCount); } data.VertexBoneIndices.resize(header.vertexCount); From 2d898851c523029cd0739a22eb50d1cc71274dec Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Tue, 17 Jan 2023 16:07:19 +0800 Subject: [PATCH 124/164] Fixed rig asset node delete --- .../Assets/Asset Types/Models/SHRigAsset.cpp | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp index 6e1ef2b3..f5186a0c 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.cpp @@ -7,25 +7,7 @@ namespace SHADE { SHRigAsset::~SHRigAsset() { - if (root == nullptr) - { - return; - } - - std::queue nodeQueue; - nodeQueue.push(root); - - while(!nodeQueue.empty()) - { - auto curr = nodeQueue.front(); - nodeQueue.pop(); - - for (auto child : curr->children) - { - nodeQueue.push(child); - } - - delete curr; - } + if (root != nullptr) + delete[] root; } } From af3e4a3cfd0c07055f72cb4a408de38977ddaba3 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 17 Jan 2023 18:26:58 +0800 Subject: [PATCH 125/164] Modified SHRig to use indices provided by SHRigAsset instead of auto generated indices --- SHADE_Engine/src/Animation/SHRig.cpp | 8 +++----- SHADE_Engine/src/Animation/SHRig.h | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 0a70dcd3..d86eee82 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -33,7 +33,6 @@ namespace SHADE } // Do a recursive depth first traversal to populate the rig - nodeCount = 0; rootNode = recurseCreateNode(asset, asset.root); } @@ -60,7 +59,7 @@ namespace SHADE int SHRig::GetNodeCount() const noexcept { - return nodeCount; + return static_cast(nodes.size()); } int SHRig::GetNodeIndex(Handle node) const noexcept @@ -80,7 +79,6 @@ namespace SHADE { // Construct the node auto newNode = nodeStore.Create(); - ++nodeCount; // Fill the node with data const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef); @@ -91,9 +89,9 @@ namespace SHADE { nodeNames.emplace(newNode, NODE_DATA.name); nodesByName.emplace(NODE_DATA.name, newNode); - nodeIndexMap.emplace(newNode, nodes.size()); - nodes.emplace_back(newNode); } + nodeIndexMap.emplace(newNode, sourceNode->idRef); + nodes.emplace_back(newNode); // Fill child nodes for (const auto& child : sourceNode->children) diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index 0dc9c829..53a201f4 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -94,7 +94,6 @@ namespace SHADE std::unordered_map> nodesByName; std::vector> nodes; std::unordered_map, int> nodeIndexMap; - int nodeCount = 0; SHResourceLibrary nodeStore; /*---------------------------------------------------------------------------------*/ From 20ffd67fcc81f2def90ee756424b25f3c563c681 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 17 Jan 2023 21:54:53 +0800 Subject: [PATCH 126/164] Fixed move constructor and assignment for SHBatch and added a check for if a batch is animated --- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 129 ++++++++++-------- .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 6 +- 2 files changed, 77 insertions(+), 58 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index fdbbad24..f281fdb0 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -37,40 +37,50 @@ namespace SHADE /* SHBatch - Constructors/Destructors */ /*-----------------------------------------------------------------------------------*/ SHBatch::SHBatch(Handle pipeline) - : pipeline{ pipeline } + : pipeline{ pipeline } { if (!pipeline) throw std::invalid_argument("Attempted to create a SHBatch with an invalid SHPipeline!"); + // Check the pipeline and flag it depending on whether or not it is animated + isAnimated = checkIfIsAnimatedPipeline(pipeline); + // Mark all as dirty setAllDirtyFlags(); } SHBatch::SHBatch(SHBatch&& rhs) - : device { rhs.device } - , pipeline { rhs.pipeline } - , referencedMatInstances { std::move(rhs.referencedMatInstances) } - , matBufferDirty { std::move(rhs.matBufferDirty) } - , subBatches { std::move(rhs.subBatches) } - , isDirty { std::move(rhs.isDirty) } - , drawData { std::move(rhs.drawData) } - , transformData { std::move(rhs.transformData) } - , instancedIntegerData { std::move(rhs.instancedIntegerData) } - , matPropsData { std::move(rhs.matPropsData) } - , matPropsDataSize { rhs.matPropsDataSize } - , singleMatPropAlignedSize { rhs.singleMatPropAlignedSize } - , singleMatPropSize { rhs.singleMatPropSize } - , isCPUBuffersDirty { rhs.isCPUBuffersDirty } - , drawDataBuffer { rhs.drawDataBuffer } - , transformDataBuffer { rhs.transformDataBuffer } - , instancedIntegerBuffer { rhs.instancedIntegerBuffer } - , matPropsBuffer { rhs.matPropsBuffer } - , instanceDataDescSet { rhs.instanceDataDescSet } + : device { rhs.device } + , isAnimated { rhs.isAnimated } + , pipeline { rhs.pipeline } + , referencedMatInstances { std::move(rhs.referencedMatInstances) } + , matBufferDirty { std::move(rhs.matBufferDirty) } + , subBatches { std::move(rhs.subBatches) } + , isDirty { std::move(rhs.isDirty) } + , drawData { std::move(rhs.drawData) } + , transformData { std::move(rhs.transformData) } + , instancedIntegerData { std::move(rhs.instancedIntegerData) } + , matPropsData { std::move(rhs.matPropsData) } + , matPropsDataSize { rhs.matPropsDataSize } + , singleMatPropAlignedSize { rhs.singleMatPropAlignedSize } + , singleMatPropSize { rhs.singleMatPropSize } + , boneMatrixData { std::move(rhs.boneMatrixData) } + , boneMatrixIndices { std::move(rhs.boneMatrixIndices) } + , isCPUBuffersDirty { rhs.isCPUBuffersDirty } + , drawDataBuffer { rhs.drawDataBuffer } + , transformDataBuffer { rhs.transformDataBuffer } + , instancedIntegerBuffer { rhs.instancedIntegerBuffer } + , matPropsBuffer { rhs.matPropsBuffer } + , boneMatrixBuffer { rhs.boneMatrixBuffer } + , boneMatrixFirstIndexBuffer { rhs.boneMatrixFirstIndexBuffer } + , instanceDataDescSet { rhs.instanceDataDescSet } { - rhs.drawDataBuffer = {}; - rhs.transformDataBuffer = {}; - rhs.instancedIntegerBuffer = {}; - rhs.matPropsBuffer = {}; + rhs.drawDataBuffer = {}; + rhs.transformDataBuffer = {}; + rhs.instancedIntegerBuffer = {}; + rhs.matPropsBuffer = {}; + rhs.boneMatrixBuffer = {}; + rhs.boneMatrixFirstIndexBuffer = {}; rhs.instanceDataDescSet = {}; } @@ -79,31 +89,38 @@ namespace SHADE if (this == &rhs) return *this; - device = rhs.device ; - pipeline = rhs.pipeline ; - referencedMatInstances = std::move(rhs.referencedMatInstances); - matBufferDirty = std::move(rhs.matBufferDirty) ; - subBatches = std::move(rhs.subBatches) ; - isDirty = std::move(rhs.isDirty) ; - drawData = std::move(rhs.drawData) ; - transformData = std::move(rhs.transformData) ; - instancedIntegerData = std::move(rhs.instancedIntegerData) ; - matPropsData = std::move(rhs.matPropsData) ; - matPropsDataSize = rhs.matPropsDataSize ; - singleMatPropAlignedSize = rhs.singleMatPropAlignedSize ; - singleMatPropSize = rhs.singleMatPropSize ; - isCPUBuffersDirty = rhs.isCPUBuffersDirty ; - drawDataBuffer = rhs.drawDataBuffer ; - transformDataBuffer = rhs.transformDataBuffer ; - instancedIntegerBuffer = rhs.instancedIntegerBuffer ; - matPropsBuffer = rhs.matPropsBuffer ; - instanceDataDescSet = rhs.instanceDataDescSet ; + device = rhs.device ; + isAnimated = rhs.isAnimated ; + pipeline = rhs.pipeline ; + referencedMatInstances = std::move(rhs.referencedMatInstances); + matBufferDirty = std::move(rhs.matBufferDirty) ; + subBatches = std::move(rhs.subBatches) ; + isDirty = std::move(rhs.isDirty) ; + drawData = std::move(rhs.drawData) ; + transformData = std::move(rhs.transformData) ; + instancedIntegerData = std::move(rhs.instancedIntegerData) ; + matPropsData = std::move(rhs.matPropsData) ; + matPropsDataSize = rhs.matPropsDataSize ; + singleMatPropAlignedSize = rhs.singleMatPropAlignedSize ; + singleMatPropSize = rhs.singleMatPropSize ; + boneMatrixData = std::move(rhs.boneMatrixData) ; + boneMatrixIndices = std::move(rhs.boneMatrixIndices) ; + isCPUBuffersDirty = rhs.isCPUBuffersDirty ; + drawDataBuffer = rhs.drawDataBuffer ; + transformDataBuffer = rhs.transformDataBuffer ; + instancedIntegerBuffer = rhs.instancedIntegerBuffer ; + matPropsBuffer = rhs.matPropsBuffer ; + boneMatrixBuffer = rhs.boneMatrixBuffer ; + boneMatrixFirstIndexBuffer = rhs.boneMatrixFirstIndexBuffer ; + instanceDataDescSet = rhs.instanceDataDescSet ; // Unset values - rhs.drawDataBuffer = {}; - rhs.transformDataBuffer = {}; - rhs.instancedIntegerBuffer = {}; - rhs.matPropsBuffer = {}; + rhs.drawDataBuffer = {}; + rhs.transformDataBuffer = {}; + rhs.instancedIntegerBuffer = {}; + rhs.matPropsBuffer = {}; + rhs.boneMatrixBuffer = {}; + rhs.boneMatrixFirstIndexBuffer = {}; rhs.instanceDataDescSet = {}; return *this; @@ -363,21 +380,19 @@ namespace SHADE // Populate on the CPU for (auto& subBatch : subBatches) - for (auto rendId : subBatch.Renderables) + for (auto rendId : subBatch.Renderables) + { + auto* renderable = SHComponentManager::GetComponent(rendId); + instancedIntegerData.emplace_back(SHInstancedIntegerData { - auto* renderable = SHComponentManager::GetComponent(rendId); - instancedIntegerData.emplace_back(SHInstancedIntegerData - { - rendId, - renderable->GetLightLayer() - } - ); - } + rendId, + renderable->GetLightLayer() + }); + } // Transfer to GPU if (instancedIntegerBuffer[frameIndex] && !drawData.empty()) instancedIntegerBuffer[frameIndex]->WriteToMemory(instancedIntegerData.data(), static_cast(instancedIntegerData.size() * sizeof(SHInstancedIntegerData)), 0, 0); - } void SHBatch::UpdateAnimationBuffer(uint32_t frameIndex) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index 913a7a04..86e17035 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -97,6 +97,7 @@ namespace SHADE bool IsEmpty() const noexcept { return subBatches.empty(); } Handle GetTransformBuffer(uint32_t frameIndex) const noexcept; Handle GetMDIBuffer(uint32_t frameIndex) const noexcept; + bool IsAnimated() const noexcept { return isAnimated; } private: /*---------------------------------------------------------------------------------*/ @@ -111,6 +112,8 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ // Resources Handle device; + // Config + bool isAnimated; // Whether the material supports animation // Batch Properties Handle pipeline; std::unordered_set> referencedMatInstances; @@ -126,7 +129,7 @@ namespace SHADE Byte matPropsDataSize = 0; Byte singleMatPropAlignedSize = 0; Byte singleMatPropSize = 0; - std::vector boneMatrixData; + std::vector boneMatrixData; // 0th element is always an identity matrix std::vector boneMatrixIndices; bool isCPUBuffersDirty = true; // GPU Buffers @@ -143,5 +146,6 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ void setAllDirtyFlags(); void rebuildDescriptorSetBuffers(uint32_t frameIndex, Handle descPool); + static bool checkIfIsAnimatedPipeline(Handle pipeline); }; } From b36145fa396e51b4779b03e27afccb773d48772b Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 17 Jan 2023 22:55:33 +0800 Subject: [PATCH 127/164] Added isAnimated check with base 0-index identity matrix for bones --- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 71 +++++++++++++++---- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index f281fdb0..9bd5437d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -397,6 +397,10 @@ namespace SHADE void SHBatch::UpdateAnimationBuffer(uint32_t frameIndex) { + // Ignore if not animated batch + if (!isAnimated) + return; + // Frame Index check if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) { @@ -407,6 +411,9 @@ namespace SHADE // Reset Animation Matrix Data boneMatrixData.clear(); + // Add the first identity matrix into the bone matrix data + boneMatrixData.emplace_back(SHMatrix::Identity); // This kills the GPU + // Populate on the CPU for (auto& subBatch : subBatches) for (auto rendId : subBatch.Renderables) @@ -417,6 +424,7 @@ namespace SHADE const auto& MATRICES = animator->GetBoneMatrices(); boneMatrixData.insert(boneMatrixData.end(), MATRICES.cbegin(), MATRICES.cend()); } + // We don't have to account for missing animators or reset indices as the renderable list are not updated at this point } // Update GPU Buffers @@ -461,10 +469,19 @@ namespace SHADE // - EID data instancedIntegerData.reserve(numTotalElements); instancedIntegerData.clear(); + // - Bone Data + if (isAnimated) + { + boneMatrixData.clear(); + boneMatrixIndices.clear(); + boneMatrixIndices.reserve(numTotalElements); - auto const& descMappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); + // Add the first identity matrix into the bone matrix data + boneMatrixData.emplace_back(SHMatrix::Identity); + } // - Material Properties Data + auto const& descMappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); const Handle SHADER_INFO = pipeline->GetPipelineLayout()->GetShaderBlockInterface ( descMappings.at(SHPredefinedDescriptorTypes::PER_INSTANCE_BATCH), @@ -484,10 +501,6 @@ namespace SHADE matPropsDataSize = matPropTotalBytes; } } - // - Bone Data - boneMatrixData.clear(); - boneMatrixIndices.clear(); - boneMatrixIndices.reserve(numTotalElements); // Build Sub Batches uint32_t nextInstanceIndex = 0; @@ -563,12 +576,20 @@ namespace SHADE } // Bone Data - auto animator = SHComponentManager::GetComponent_s(rendId); - if (animator) + if (isAnimated) { - boneMatrixIndices.emplace_back(static_cast(boneMatrixData.size())); - const auto& BONE_MATRICES = animator->GetBoneMatrices(); - boneMatrixData.insert(boneMatrixData.end(), BONE_MATRICES.cbegin(), BONE_MATRICES.cend()); + auto animator = SHComponentManager::GetComponent_s(rendId); + if (animator) + { + boneMatrixIndices.emplace_back(static_cast(boneMatrixData.size())); + const auto& BONE_MATRICES = animator->GetBoneMatrices(); + boneMatrixData.insert(boneMatrixData.end(), BONE_MATRICES.cbegin(), BONE_MATRICES.cend()); + } + else + { + // Take the first matrix which is always identity + boneMatrixIndices.emplace_back(static_cast(0)); + } } } } @@ -605,7 +626,7 @@ namespace SHADE "Batch Instance Data Buffer" ); // - Bone Matrix Indices - if (!boneMatrixIndices.empty()) + if (isAnimated && !boneMatrixIndices.empty()) { const uint32_t BMI_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); SHVkUtil::EnsureBufferAndCopyHostVisibleData @@ -640,7 +661,7 @@ namespace SHADE // Bind all required objects before drawing std::vector dynamicOffset{ 0 }; - if (!boneMatrixData.empty()) + if (isAnimated && !boneMatrixData.empty()) { dynamicOffset.emplace_back(0); } @@ -648,7 +669,7 @@ namespace SHADE cmdBuffer->BindPipeline(pipeline); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::INTEGER_DATA, instancedIntegerBuffer[frameIndex], 0); - if (boneMatrixFirstIndexBuffer[frameIndex]) + if (isAnimated && boneMatrixFirstIndexBuffer[frameIndex]) { cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::BONE_MATRIX_FIRST_INDEX, boneMatrixFirstIndexBuffer[frameIndex], 0); } @@ -721,7 +742,9 @@ namespace SHADE layoutTypes = PreDefDescLayoutType::MATERIAL_AND_BONES; } - if (matPropsData || !boneMatrixData.empty()) + const bool MUST_BUILD_BONE_DESC = isAnimated && !boneMatrixData.empty(); + + if (matPropsData || MUST_BUILD_BONE_DESC) { // Make sure that we have a descriptor set if we don't already have one if (!instanceDataDescSet[frameIndex]) @@ -771,7 +794,7 @@ namespace SHADE } /* Animation Bone Data */ - if (!boneMatrixData.empty()) + if (MUST_BUILD_BONE_DESC) { // Update GPU Buffers const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(SHMatrix)); @@ -801,4 +824,22 @@ namespace SHADE ); } } + + bool SHBatch::checkIfIsAnimatedPipeline(Handle pipeline) + { + if (!pipeline || !pipeline->GetPipelineLayout()) + return false; + + // Grab the pipeline descriptor set layouts + auto pipelineDescLayouts = pipeline->GetPipelineLayout()->GetDescriptorSetLayoutsPipeline(); + + // Check if they contain the material and bones layout, that indicates it is + using GfxPreDef = SHGraphicsPredefinedData; + using GfxPreDefType = GfxPreDef::PredefinedDescSetLayoutTypes; + const Handle BONE_DESC_SET_LAYOUT = GfxPreDef::GetPredefinedDescSetLayouts(GfxPreDefType::MATERIAL_AND_BONES)[0]; + return std::find_if(pipelineDescLayouts.begin(), pipelineDescLayouts.end(), [BONE_DESC_SET_LAYOUT](Handle layout) + { + return BONE_DESC_SET_LAYOUT == layout; + }) != pipelineDescLayouts.end(); + } } From 6872bae1513ea530107880bd78be05a47571a2b6 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Wed, 18 Jan 2023 04:00:48 +0800 Subject: [PATCH 128/164] Updated compiled shmodel file for racoon with fixed rig node order and build format Fixed bug when building histogram for vertex weights --- Assets/Materials/AnimatedBag.shmat | 8 ++++++++ Assets/Materials/AnimatedBag.shmat.shmeta | 3 +++ Assets/Models/racoon.shmodel | Bin 677824 -> 679720 bytes .../Libraries/Loaders/SHModelLoader.cpp | 10 ++++++++-- 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 Assets/Materials/AnimatedBag.shmat create mode 100644 Assets/Materials/AnimatedBag.shmat.shmeta diff --git a/Assets/Materials/AnimatedBag.shmat b/Assets/Materials/AnimatedBag.shmat new file mode 100644 index 00000000..3f0d9707 --- /dev/null +++ b/Assets/Materials/AnimatedBag.shmat @@ -0,0 +1,8 @@ +- VertexShader: 47911992 + FragmentShader: 46377769 + SubPass: G-Buffer Write + Properties: + data.color: {x: 1, y: 1, z: 1, w: 1} + data.textureIndex: 58303057 + data.alpha: 0 + data.beta: {x: 1, y: 1, z: 1} \ No newline at end of file diff --git a/Assets/Materials/AnimatedBag.shmat.shmeta b/Assets/Materials/AnimatedBag.shmat.shmeta new file mode 100644 index 00000000..6457c1c3 --- /dev/null +++ b/Assets/Materials/AnimatedBag.shmat.shmeta @@ -0,0 +1,3 @@ +Name: AnimatedBag +ID: 117923942 +Type: 7 diff --git a/Assets/Models/racoon.shmodel b/Assets/Models/racoon.shmodel index c3fa9a62440bd26f6a2b2dd9729dbbc37c1c36a7..e15051b6e9f11b309e006fb989fc1cc91b5b026d 100644 GIT binary patch delta 5266 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 diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 8fe55dd0..aea46add 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -176,8 +176,8 @@ namespace SHADE std::queue> nodeQueue; nodeQueue.emplace(std::make_pair(nodePool, dst)); - auto depthPtr = nodePool + 1; - auto depthTempPtr = dst + 1; + SHRigNode* depthPtr = nodePool + 1; + NodeTemp* depthTempPtr = dst + 1; while(!nodeQueue.empty()) { @@ -247,6 +247,11 @@ namespace SHADE data.VertexBoneIndices.resize(header.vertexCount); data.VertexBoneWeights.resize(header.vertexCount); + //for (auto& weight : data.VertexBoneWeights) + //{ + // weight = { -0.1f }; + //} + for (uint32_t boneIndex{0}; boneIndex < bones.size(); ++boneIndex) { auto const& bone = bones[boneIndex]; @@ -261,6 +266,7 @@ namespace SHADE { boneIndices[j] = boneIndex; boneWeight[j] = weight.weight; + break; } } } From 1b97cd72bd7bd9d8a17065a5555f40c0f6229123 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Wed, 18 Jan 2023 04:05:36 +0800 Subject: [PATCH 129/164] TEMP fix to get anim timing correct --- SHADE_Engine/src/Animation/SHAnimationClip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h index 8a10ce3a..58b5681a 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.h +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -67,7 +67,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ const std::vector& GetChannels() const noexcept { return channels; } int GetTicksPerSecond() const noexcept { return ticksPerSecond; } - float GetTotalTime() const noexcept { return totalTime; } + float GetTotalTime() const noexcept { return totalTime/(float)ticksPerSecond; } private: /*---------------------------------------------------------------------------------*/ From e013112b2c97890ecfc3c871e5ab6882c6750d10 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 18 Jan 2023 16:03:25 +0800 Subject: [PATCH 130/164] Fixed validation errors when rendering a non-animated model --- .../src/Graphics/MiddleEnd/Batching/SHBatch.cpp | 5 +++++ .../MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 9bd5437d..330c395c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -673,6 +673,11 @@ namespace SHADE { cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::BONE_MATRIX_FIRST_INDEX, boneMatrixFirstIndexBuffer[frameIndex], 0); } + else + { + // HACK: Bind the transform buffer instead since we won't use it anyways, but we must bind something + cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::BONE_MATRIX_FIRST_INDEX, transformDataBuffer[frameIndex], 0); + } auto const& descMappings = SHGraphicsPredefinedData::GetMappings(SHGraphicsPredefinedData::SystemType::BATCHING); if (instanceDataDescSet[frameIndex]) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp index 09f7d19e..758dc289 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsPredefinedData.cpp @@ -210,11 +210,11 @@ namespace SHADE void SHGraphicsPredefinedData::InitDefaultVertexInputState(void) noexcept { - defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // positions at binding 0 - defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_2D) }); // UVs at binding 1 - defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Normals at binding 2 - defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Tangents at binding 3 - defaultVertexInputState.AddBinding(true , true , { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Transform at binding 4 - 7 (4 slots) + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Attribute positions at binding 0 + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_2D) }); // Attribute UVs at binding 1 + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Attribute Normals at binding 2 + defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Attribute Tangents at binding 3 + defaultVertexInputState.AddBinding(true , true , { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Instanced Transform at binding 4 - 7 (4 slots) defaultVertexInputState.AddBinding(true , true , { SHVertexAttribute(SHAttribFormat::UINT32_2D) }); // Instanced integer data at index 8 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::UINT32_4D) }); // Attribute bone indices at index 9 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); // Attribute bone weights at index 10 From 35b7ac0178a95af008cf35bd52b59a5bffb2a682 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 18 Jan 2023 19:16:20 +0800 Subject: [PATCH 131/164] Fixed SHAnimationClip::GetTotalTime() --- SHADE_Engine/src/Animation/SHAnimationClip.cpp | 2 +- SHADE_Engine/src/Animation/SHAnimationClip.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp index 4dab967c..939275d3 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -21,7 +21,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHAnimationClip::SHAnimationClip(const SHAnimAsset& asset) : ticksPerSecond { static_cast(asset.ticksPerSecond) } - , totalTime { static_cast(asset.duration) } + , totalTime { static_cast(asset.duration) / static_cast(asset.ticksPerSecond) } { // Populate keyframes for (const auto& channel : asset.nodeChannels) diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.h b/SHADE_Engine/src/Animation/SHAnimationClip.h index 58b5681a..8a10ce3a 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.h +++ b/SHADE_Engine/src/Animation/SHAnimationClip.h @@ -67,7 +67,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ const std::vector& GetChannels() const noexcept { return channels; } int GetTicksPerSecond() const noexcept { return ticksPerSecond; } - float GetTotalTime() const noexcept { return totalTime/(float)ticksPerSecond; } + float GetTotalTime() const noexcept { return totalTime; } private: /*---------------------------------------------------------------------------------*/ From b9fcdc43d43954b9157b7d64ab69dea3affb6aa8 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 18 Jan 2023 19:16:45 +0800 Subject: [PATCH 132/164] Modified how SHAnimatorComponent computes the bone matrices --- .../src/Animation/SHAnimatorComponent.cpp | 76 ++++++++++++------- .../src/Animation/SHAnimatorComponent.h | 4 + SHADE_Engine/src/Animation/SHRig.cpp | 10 +-- SHADE_Engine/src/Animation/SHRig.h | 54 ++++++++----- .../Assets/Asset Types/Models/SHRigAsset.h | 6 +- .../Libraries/Loaders/SHModelLoader.cpp | 8 +- .../Assets/Libraries/Loaders/SHModelLoader.h | 2 +- 7 files changed, 99 insertions(+), 61 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 5eff2c45..6ef2d47e 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -83,10 +83,21 @@ namespace SHADE if (currClip == newClip) return; + // Set parameters currClip = newClip; secsPerTick = 1.0f / currClip->GetTicksPerSecond(); - if (rig) + // Build channel map + channelMap.clear(); + if (currClip) + { + for (const auto& channel : currClip->GetChannels()) + { + channelMap.emplace(channel.Name, &channel); + } + } + + if (rig && currClip) { updatePoseWithClip(0.0f); } @@ -123,36 +134,45 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ void SHAnimatorComponent::updatePoseWithClip(float poseTime) { - for (const auto& channel : currClip->GetChannels()) + // Get closest frame index + const int CLOSEST_FRAME_IDX = static_cast(std::floorf(poseTime * currClip->GetTicksPerSecond())); + updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, rig->GetRootNode(), SHMatrix::Identity); + } + + void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix) + { + // Check if there is a channel for this node + const std::string& BONE_NAME = rig->GetName(node); + SHMatrix transformMatrix = SHMatrix::Identity; + if (channelMap.contains(BONE_NAME)) { - // Get the bone - std::queue> bones; - bones.push(rig->GetNode(channel.Name)); - while (!bones.empty()) - { - // Select bone at front of the queue - auto bone = bones.front(); bones.pop(); - if (!bone) - continue; + const auto CHANNEL = channelMap[BONE_NAME]; + transformMatrix = SHMatrix::Transform + ( + getInterpolatedValue(CHANNEL->PositionKeyFrames, closestFrameIndex, poseTime), + getInterpolatedValue(CHANNEL->RotationKeyFrames, closestFrameIndex, poseTime), + getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime) + ); + } + else + { + transformMatrix = SHMatrix::Inverse(node->OffsetMatrix); // TODO: Use TransformMatrix for the bone + } - // Add any children to the queue - for (auto child : bone->Children) - { - bones.push(child); - } + // Apply parent's transformation + transformMatrix = parentMatrix * transformMatrix; - // Get closest frame index - const int CLOSEST_FRAME_IDX = static_cast(std::floorf(poseTime * currClip->GetTicksPerSecond())); - - // Calculate the matrix from interpolated values - const int BONE_MTX_IDX = rig->GetNodeIndex(bone); - boneMatrices[BONE_MTX_IDX] = boneMatrices[BONE_MTX_IDX] * SHMatrix::Transform - ( - getInterpolatedValue(channel.PositionKeyFrames, CLOSEST_FRAME_IDX, poseTime), - getInterpolatedValue(channel.RotationKeyFrames, CLOSEST_FRAME_IDX, poseTime), - getInterpolatedValue(channel.ScaleKeyFrames , CLOSEST_FRAME_IDX, poseTime) - ); - } + // Apply transformations to this node + const int BONE_MTX_IDX = rig->GetNodeIndex(node); + if (BONE_MTX_IDX >= 0) + { + boneMatrices[BONE_MTX_IDX] = boneMatrices[BONE_MTX_IDX] * transformMatrix * node->OffsetMatrix; + } + + // Apply pose to children + for (auto& child : node->Children) + { + updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix); } } } diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 43a9f044..b47106f8 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -30,6 +30,7 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ class SHRig; + struct SHRigNode; class SHAnimationClip; class SHVkBuffer; @@ -133,11 +134,14 @@ namespace SHADE float secsPerTick = 0.0f; // Buffer std::vector boneMatrices; + // Caches + std::unordered_map channelMap; /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ void updatePoseWithClip(float poseTime); + void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix); template T getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index d86eee82..6f9b2b85 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -39,7 +39,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Usage Functions */ /*-----------------------------------------------------------------------------------*/ - const std::string& SHRig::GetName(Handle node) const noexcept + const std::string& SHRig::GetName(Handle node) const noexcept { static const std::string EMPTY_STRING = ""; @@ -49,7 +49,7 @@ namespace SHADE return EMPTY_STRING; } - Handle SHRig::GetNode(const std::string& name) const noexcept + Handle SHRig::GetNode(const std::string& name) const noexcept { if (nodesByName.contains(name)) return nodesByName.at(name); @@ -62,7 +62,7 @@ namespace SHADE return static_cast(nodes.size()); } - int SHRig::GetNodeIndex(Handle node) const noexcept + int SHRig::GetNodeIndex(Handle node) const noexcept { if (nodeIndexMap.contains(node)) { @@ -75,14 +75,14 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ - Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNode* sourceNode) + Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode) { // Construct the node auto newNode = nodeStore.Create(); // Fill the node with data const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef); - newNode->Transform = NODE_DATA.transform; + newNode->OffsetMatrix = NODE_DATA.transform; // Populate maps if (!NODE_DATA.name.empty()) diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index 53a201f4..3d2b14c2 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -28,23 +28,32 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ struct SHRigAsset; - struct SHRigNode; + struct SHRigNodeAsset; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + ///

+ /// + /// + struct SHRigNode + { + /// + /// Matrix that performs a transformation from local space to bone (node) space. + /// + SHMatrix OffsetMatrix; + /// + /// Child nodes of this node. + /// + std::vector> Children; + }; + + /// + /// + /// class SH_API SHRig { public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - struct Node - { - SHMatrix Transform; - std::vector> Children; - }; - /*---------------------------------------------------------------------------------*/ /* Constructors */ /*---------------------------------------------------------------------------------*/ @@ -65,7 +74,12 @@ namespace SHADE /// Name of the node. If it does not have a name or is invalid, an empty string will /// be provided. /// - const std::string& GetName(Handle node) const noexcept; + const std::string& GetName(Handle node) const noexcept; + /// + /// Retrieves the root node of the rig. + /// + /// Handle to the root node of the rig. + Handle GetRootNode() const noexcept { return rootNode; } /// /// Retrieves a node via name. /// @@ -74,7 +88,7 @@ namespace SHADE /// Node with the specified name. If it does not have a name or is invalid, an empty /// handle will be provided. /// - Handle GetNode(const std::string& name) const noexcept; + Handle GetNode(const std::string& name) const noexcept; /// /// Returns the number of nodes in the rig. This matches the number of bone matrices /// needed. @@ -83,22 +97,22 @@ namespace SHADE /// /// Retrieves the index in the node storage. /// - int GetNodeIndex(Handle node) const noexcept; + int GetNodeIndex(Handle node) const noexcept; private: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - Handle rootNode; - std::unordered_map, std::string> nodeNames; - std::unordered_map> nodesByName; - std::vector> nodes; - std::unordered_map, int> nodeIndexMap; - SHResourceLibrary nodeStore; + Handle rootNode; + std::unordered_map, std::string> nodeNames; + std::unordered_map> nodesByName; + std::vector> nodes; + std::unordered_map, int> nodeIndexMap; + SHResourceLibrary nodeStore; /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - Handle recurseCreateNode(const SHRigAsset& asset, const SHRigNode* sourceNode); + Handle recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode); }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h index 86b497ae..424c5468 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h @@ -29,10 +29,10 @@ namespace SHADE SHMatrix transform; }; - struct SHRigNode + struct SHRigNodeAsset { uint32_t idRef; - std::vector children; + std::vector children; }; struct SH_API SHRigAsset : SHAssetData @@ -41,6 +41,6 @@ namespace SHADE SHRigDataHeader header; std::vector nodeDataCollection; - SHRigNode* root; + SHRigNodeAsset* root; }; } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index aea46add..0d3160bc 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -154,7 +154,7 @@ namespace SHADE } } - void SHModelLoader::ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode*& root) + void SHModelLoader::ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNodeAsset*& root) { // Read All nodes into one contiguous data block struct NodeTemp @@ -170,13 +170,13 @@ namespace SHADE ); // Build and populate tree - SHRigNode* nodePool = new SHRigNode[header.nodeCount]; + SHRigNodeAsset* nodePool = new SHRigNodeAsset[header.nodeCount]; root = nodePool; - std::queue> nodeQueue; + std::queue> nodeQueue; nodeQueue.emplace(std::make_pair(nodePool, dst)); - SHRigNode* depthPtr = nodePool + 1; + SHRigNodeAsset* depthPtr = nodePool + 1; NodeTemp* depthTempPtr = dst + 1; while(!nodeQueue.empty()) diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h index 93db8534..4320727f 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h @@ -24,7 +24,7 @@ namespace SHADE void ReadRigHeader(FileReference file, SHRigDataHeader& header); void ReadRigData(FileReference file, SHRigDataHeader const& header, std::vector& data); - void ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode*& root); + void ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNodeAsset*& root); void ReadMeshData(FileReference file, std::vector const& headers, std::vector& meshes); void ReadAnimData(FileReference file, std::vector const& headers, std::vector& anims); From 3e23f08aa86c4e6e49a0d53ec0480d93217b02eb Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 18 Jan 2023 19:25:27 +0800 Subject: [PATCH 133/164] Fixed bug where keyframes of animation clips are not interpolated --- SHADE_Engine/src/Animation/SHAnimatorComponent.hpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp index d0775711..b6055e53 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp @@ -59,13 +59,8 @@ namespace SHADE else return nextKeyFrame->Data; } - // At the front, so no prior key frames - else if (nextKeyFrame != keyframes.end()) - { - return nextKeyFrame->Data; - } // At the back, so no keyframes will follow - else + else if (nextKeyFrame == keyframes.end()) { return firstKeyFrame->Data; } From a543f6cc3a60ec5d46ecf873e2ce6cf23d3bd5d1 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 18 Jan 2023 19:52:26 +0800 Subject: [PATCH 134/164] Removed unecessary check in SHAnimatorComponent --- SHADE_Engine/src/Animation/SHAnimatorComponent.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp index b6055e53..9c6a5d3a 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.hpp @@ -42,7 +42,7 @@ namespace SHADE { firstKeyFrame = iter; } - else if (KEYFRAME.FrameIndex > closestFrameIndex) + else // KEYFRAME.FrameIndex > closestFrameIndex { nextKeyFrame = iter; break; From 62f104a535468012312925047b7f0e099a1af4fb Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 18 Jan 2023 20:11:48 +0800 Subject: [PATCH 135/164] Added Global Inverse Matrix for SHRig --- SHADE_Engine/src/Animation/SHAnimatorComponent.cpp | 2 +- SHADE_Engine/src/Animation/SHRig.cpp | 6 +++++- SHADE_Engine/src/Animation/SHRig.h | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 6ef2d47e..26f6b83e 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -166,7 +166,7 @@ namespace SHADE const int BONE_MTX_IDX = rig->GetNodeIndex(node); if (BONE_MTX_IDX >= 0) { - boneMatrices[BONE_MTX_IDX] = boneMatrices[BONE_MTX_IDX] * transformMatrix * node->OffsetMatrix; + boneMatrices[BONE_MTX_IDX] = rig->GetGlobalInverseMatrix() * transformMatrix * node->OffsetMatrix; } // Apply pose to children diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 6f9b2b85..84117f77 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -34,6 +34,10 @@ namespace SHADE // Do a recursive depth first traversal to populate the rig rootNode = recurseCreateNode(asset, asset.root); + if (rootNode) + { + globalInverseMatrix = rootNode->OffsetMatrix; + } } /*-----------------------------------------------------------------------------------*/ @@ -82,7 +86,7 @@ namespace SHADE // Fill the node with data const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef); - newNode->OffsetMatrix = NODE_DATA.transform; + newNode->OffsetMatrix = SHMatrix::Inverse(NODE_DATA.transform); // Populate maps if (!NODE_DATA.name.empty()) diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index 3d2b14c2..f28c628b 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -80,6 +80,7 @@ namespace SHADE /// /// Handle to the root node of the rig. Handle GetRootNode() const noexcept { return rootNode; } + const SHMatrix& GetGlobalInverseMatrix() const noexcept { return globalInverseMatrix; } /// /// Retrieves a node via name. /// @@ -108,6 +109,7 @@ namespace SHADE std::unordered_map> nodesByName; std::vector> nodes; std::unordered_map, int> nodeIndexMap; + SHMatrix globalInverseMatrix; SHResourceLibrary nodeStore; /*---------------------------------------------------------------------------------*/ From 71572381f71fbde71da73c3656a7174f23d329ff Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Fri, 20 Jan 2023 14:00:24 +0800 Subject: [PATCH 136/164] Progress on input bindings in editor --- .../InputBindings/SHInputBindingsPanel.cpp | 184 +++++++++++++++++- .../InputBindings/SHInputBindingsPanel.h | 2 + .../EditorWindow/SHEditorWindowIncludes.h | 3 +- SHADE_Engine/src/Editor/SHEditor.cpp | 1 + SHADE_Engine/src/Input/SHInputManager.h | 10 + 5 files changed, 198 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp index f05440dc..5dc9e17a 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp @@ -5,11 +5,193 @@ namespace SHADE { + //Internal Helper function + std::string labelConcat(char const* label, size_t entryNumber) + { + std::string concat = label; + concat += std::to_string(entryNumber); + return concat; + } + + void SHInputBindingsPanel::Init() + { + SHEditorWindow::Init(); + } + void SHInputBindingsPanel::Update() { - if (Begin()) + if (SHEditorWindow::Begin()) { + //Binding count + ImGui::Text("Binding Count: %d", SHInputManager::CountBindings()); + //Button to add new binding + if (ImGui::Button("Add New Binding")) + { + std::string newBindingName = "Binding" + std::to_string(SHInputManager::CountBindings()); + SHInputManager::AddBinding(newBindingName); + } + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Add a new binding to the list"); + ImGui::EndTooltip(); + } + + //Ensure unique label for entries + size_t entryNumber = 0; + + //Listing for each binding + for (auto& binding : SHInputManager::GetBindings()) + { + ImGui::Separator(); + std::string bindingName = binding.first; //Binding name to use as argument when modifying binding values + + //Non-modifiable binding name + ImGui::Text("Binding Name: %s", binding.first); + std::string bindingModifiedName; + //MAKE THE INPUTTEXT WORK + ImGui::InputText(labelConcat("##bindingModifyName", entryNumber).c_str(), &bindingModifiedName); + ImGui::SameLine(); + if (ImGui::Button(labelConcat("Rename##bindingRename", entryNumber).c_str())) + { + SHLOGV_CRITICAL("\"{0}\" > \"{1}\"", bindingName, bindingModifiedName); + SHInputManager::RenameBinding(bindingName, bindingModifiedName); + } + + //Modifiable binding name + /*size_t constexpr maxLen = 256; //Maximum binding name length + static char bindingNameCStr[maxLen]; + std::copy(bindingName.begin(), + bindingName.length() >= maxLen ? + bindingName.begin() + maxLen : + bindingName.end(), //Does not take into account null terminator + bindingNameCStr); + bindingNameCStr[bindingName.length()] = '\0'; //Null terminator + + //First character + char firstChar = bindingNameCStr[0]; + //If the input text is modified, modify the binding's name + if (ImGui::InputText("Binding Name", bindingNameCStr, maxLen)) + { + //Ensure name does not get shorter than 1 character long + if (std::strlen(bindingNameCStr) < 1) + bindingNameCStr[0] = firstChar; + //Rename binding in the input manager + SHInputManager::RenameBinding(bindingName, std::string{ bindingNameCStr }); + bindingName = std::string{ bindingNameCStr }; + } + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("The name of this binding"); + ImGui::EndTooltip(); + }*/ + + //Binding value test + //TODO + ImGui::Text("Value"); + + + //Binding Type Combo Box + int bindingType = static_cast(SHInputManager::GetBindingType(bindingName)); + if (ImGui::Combo(labelConcat("Input Type##", entryNumber).c_str(), &bindingType, "Keyboard / Mouse Buttons / Controller\0Mouse Horizontal\0Mouse Vertical\0Mouse Scroll Wheel")) + SHInputManager::SetBindingType(bindingName, static_cast(bindingType)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Which of the four types the binding uses"); + ImGui::Text("Keyboard / Mouse Buttons / Controller = Keys, mouse buttons and ALL controller inputs"); + ImGui::Text("Mouse Horizontal = Horizontal movement of the mouse"); + ImGui::Text("Mouse Vertical = Vertical movement of the mouse"); + ImGui::Text("Mouse Scroll Wheel = The scroll wheel found at the middle of most mouses"); + ImGui::EndTooltip(); + } + + //Inversion + bool bindingInvert = SHInputManager::GetBindingInverted(bindingName); + if (ImGui::Checkbox(labelConcat("Inverted##", entryNumber).c_str(), &bindingInvert)) + SHInputManager::SetBindingInverted(bindingName, bindingInvert); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("If inverted:"); + ImGui::Text("Positive inputs mean negative value of the binding"); + ImGui::Text("Negative inputs mean positive value of the binding"); + ImGui::Text("Mouse moving up / right means negative value of the binding"); + ImGui::Text("Scrolling the mouse wheel up means negative value of the binding"); + ImGui::EndTooltip(); + } + + //Sensitivity + double bindingSensitivity = SHInputManager::GetBindingSensitivity(bindingName); + if (ImGui::InputDouble(labelConcat("Sensitivity##", entryNumber).c_str(), &bindingSensitivity)) + SHInputManager::SetBindingSensitivity(bindingName, bindingSensitivity); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Value multiplier for mouse movement and scrolling"); + ImGui::Text("For other digital inputs, serves as a rate of how fast axis value goes to maximum positive/negative"); + ImGui::Text("For other analog inputs, serves as a multiplier, but axis value magnitude will still be capped at 1"); + ImGui::EndTooltip(); + } + + //Below this section is only for KB/M type bindings + //Not relevant for mouse movement and scrolling + if (SHInputManager::GetBindingType(bindingName) == SHInputManager::SH_BINDINGTYPE::KB_MB_CONTROLLER) + { + //Dead + float bindingDead = static_cast(SHInputManager::GetBindingDead(bindingName)); + if (ImGui::SliderFloat(labelConcat("Deadzone##", entryNumber).c_str(), &bindingDead, 0.0f, 1.0f)) + SHInputManager::SetBindingDead(bindingName, static_cast(bindingDead)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Any positive or negative analog input with magnitude less than this will be registered as neutral"); + ImGui::EndTooltip(); + } + + //Gravity + double bindingGravity = SHInputManager::GetBindingGravity(bindingName); + if (ImGui::InputDouble(labelConcat("Gravity##", entryNumber).c_str(), &bindingGravity)) + SHInputManager::SetBindingGravity(bindingName, static_cast(bindingGravity)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("The rate at which the value moves to neutral if no input in the direction is read"); + ImGui::TextColored(ImVec4{ 1.0f, 0.5f, 0.5f, 1.0f }, "Should be non-negative"); + ImGui::EndTooltip(); + } + + //Snap + bool bindingSnap = SHInputManager::GetBindingSnap(bindingName); + if (ImGui::Checkbox(labelConcat("Snap##", entryNumber).c_str(), &bindingSnap)) + SHInputManager::SetBindingSnap(bindingName, bindingSnap); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("If no other input on the axis is present and a input is made in the opposite direction of the current value,"); + ImGui::Text("the binding's value will jump to neutral 0 before resuming in the input direction"); + ImGui::EndTooltip(); + } + + //Positive key codes + + //Negative key codes + + //Positive controller codes + + //Negative controller codes + } + + ++entryNumber; //Next entry + } } + ImGui::End(); + } + + void SHInputBindingsPanel::Exit() + { + SHEditorWindow::Exit(); } } \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.h b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.h index c29a68c5..db2ec0c8 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.h @@ -10,6 +10,8 @@ namespace SHADE public: SHInputBindingsPanel() : SHEditorWindow("Input Bindings Panel", ImGuiWindowFlags_MenuBar) {} + void Init() override; void Update() override; + void Exit() override; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h index 9aad6ede..290ed622 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h @@ -6,4 +6,5 @@ #include "ViewportWindow/SHEditorViewport.h" //Editor Viewport #include "AssetBrowser/SHAssetBrowser.h" //Asset Browser #include "MaterialInspector/SHMaterialInspector.h" //Material Inspector -#include "ColliderTagPanel/SHColliderTagPanel.h" //Collider Tag Panel \ No newline at end of file +#include "ColliderTagPanel/SHColliderTagPanel.h" //Collider Tag Panel +#include "InputBindings/SHInputBindingsPanel.h" //Input Bindings \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 2b8ddbb5..b99d9f32 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -104,6 +104,7 @@ namespace SHADE SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); diff --git a/SHADE_Engine/src/Input/SHInputManager.h b/SHADE_Engine/src/Input/SHInputManager.h index 680035c3..5698f4c1 100644 --- a/SHADE_Engine/src/Input/SHInputManager.h +++ b/SHADE_Engine/src/Input/SHInputManager.h @@ -738,6 +738,16 @@ namespace SHADE return bindings.erase(bindingName); } + //Rename a binding + static inline void RenameBinding(std::string const& oldBindingName, std::string const& newBindingName) noexcept + { + //https://stackoverflow.com/a/44883472 + //https://en.cppreference.com/w/cpp/container/map/extract + auto nodeHandler = bindings.extract(oldBindingName); + nodeHandler.key() = newBindingName; + bindings.insert(std::move(nodeHandler)); + } + //Clears all bindings from the list static inline void ClearBindings() noexcept { From 80fb8f7c7323e19ca300d60ae799ca6e986df02a Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sun, 22 Jan 2023 17:00:55 +0800 Subject: [PATCH 137/164] Saved and loaded bone offsets --- Assets/Models/racoon.shmodel | Bin 679720 -> 681640 bytes .../Assets/Asset Types/Models/SHRigAsset.h | 1 + .../Libraries/Loaders/SHModelLoader.cpp | 5 +++++ 3 files changed, 6 insertions(+) diff --git a/Assets/Models/racoon.shmodel b/Assets/Models/racoon.shmodel index e15051b6e9f11b309e006fb989fc1cc91b5b026d..3fa9d9bd9e5f0adff8ab6f8cbb8a6fade7883247 100644 GIT binary patch delta 1330 zcmYk)TSydP6u|Lg?$$bjFNM(}2(?iXWfoY3)d+d3AYasUHH3QTLb_?CR@%;KYpvAU zru@2S-cq8=Ze`Jp>_vPKM9HU9IaE*yDQJ)nU7$1N!{_u5ir04$`AKRC0_I((G1eIcYKO(_l z0fdbNM+8_{PjFPgDE1Om2^hydf@%Si_?)0dz$d&#a7@5gj1U|b@Eh+CoDeXLc?7ir zW-ynaPCzVr2~N&}D(!MP=3yp*UyL|0oxl*_#_a?sAQ7twP6=3m6$AkRNmxs8TEHr- zCO9Kt3GOBc3RsV21oZ+o;y!}20@mOHf^!1aVIskK0o&0{&>$ckodk^nG7$)x1b8u) z;DUf$OeMG|AP-XrLIU<<8o?z2<+zN%6tD*u6I>QhgDVNH2&lv51kC~}anu4Opx&@7 zxGJF0APcSuK%?J+>jHwtumv{+v>1aHvXua8tltf5EVL_*W*M_p7YZmN3nKPK$>w22n8}qZ%G+GnXTeribR_AM3YI3PVS5^61 zrkU^8)hiAamCO`fz%{&F}uUM>i6X8I9F5*!!#XO=%bhV?H zUw46{*_JVm_jas!psU3t95oz!OZdqFj{6*{EY6u=CO`D3?C#C>HMF zw8kv;l1s8lG);1;X_BVtHUFBVX)>|VlR_aNR0M@k5Q-0)iXtffncJCT_u+o`@VjGn zKRYwu-}g6r!`F5WFYZhvZ%b}SmLyA)R&ryqY${Tk+1lWo$w8atu6Ojl{@@0Hs zu27)N*+#7a}8|cG+Ugv2zo8IMr%%d9Q1oy zYhG(BUh{V64T4jayIFNkxdGd*Y`;OPZBC4!&vH|mx8L7q+1~+WJc8OKQHLY}T;9f8ZSigO)3O z0>pfO2|K7P8T$PfHxZn++*o_QR<1el)!u$(qXzbG+(__+<$|6gsa^7(S2k{_c?C-d zhWHHn8u1wndE>0IYX+^B@qU6ctW~Gf91ZrjazWXe&f>nhiuVx=hfMk%-b*mTMpFS2 zp+h;R4_h^C_$oe1FlxEckE9S0Q3N=pEEvT55i1DJGV2S@y8F!tJ6&kPkaq}o5R5Ua z;+2O?I;(8fzz$+H!8yx~`wZvx4~gXMQ?|-b^B6u!FwTcVh0lHx7`$8AXlrrdN3e?E zJm+l|tz?i%-tj`e2D41yHG(W3EPqe~Ku?hOva&J5hA-h&f(vX?)mes-`b9w!~VA5 zb%JTu%J(sVXeR6>Wy=j(wd1!0Gpv=bFF?OvyRb{jW({mB-Xyqax#C3tIh?}IE4yx} zxd(qDnB{@3!%s95wo}vLxW_wkk?Ri4F4mTw_IU(aQZJBKX2s~ zWox|Retr{wC%6w6zz&=x$b|wA<21oH8p!`uvAo^&cH~6$ zWaMJ?(!>1R=EHpN h-ADM2&LjM5`zyR;@=N8beDz#@dF&`>oIh&+@;~uV$o>ET diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h index 424c5468..59c9b51e 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h @@ -27,6 +27,7 @@ namespace SHADE { std::string name; SHMatrix transform; + SHMatrix offset; }; struct SHRigNodeAsset diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 0d3160bc..9e4abc67 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -151,6 +151,11 @@ namespace SHADE reinterpret_cast(&data[i].transform), sizeof(SHMatrix) ); + + file.read( + reinterpret_cast(&data[i].offset), + sizeof(SHMatrix) + ); } } From 1dc16fdcda0ec5becc71822d45ced957460b4fc9 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 22 Jan 2023 17:38:51 +0800 Subject: [PATCH 138/164] Fixed typos --- Assets/Scenes/PhysicsSandbox.shade | 24 +++++++++---------- .../src/Physics/Dynamics/SHPhysicsWorld.h | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 3144e959..ad77de0a 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -62,7 +62,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 3, y: 7.5, z: 0} + Translate: {x: -1, y: 7.5, z: 0} Rotate: {x: -0, y: 0, z: 0.785398185} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -77,11 +77,11 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: true + Freeze Position Y: false Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false + Freeze Rotation X: true + Freeze Rotation Y: true + Freeze Rotation Z: true IsActive: true Collider Component: DrawColliders: false @@ -90,7 +90,7 @@ Collision Tag: 1 Type: Sphere Radius: 1 - Friction: 0.400000006 + Friction: 1 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0, z: 0} @@ -131,7 +131,7 @@ Collision Tag: 2 Type: Box Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 + Friction: 1 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0, z: 0} @@ -144,9 +144,9 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: -1.74209237, z: 0} + Translate: {x: 0, y: -3, z: 0} Rotate: {x: -0, y: 0, z: -0} - Scale: {x: 10, y: 0.5, z: 10} + Scale: {x: 10, y: 3, z: 10} IsActive: true RigidBody Component: Type: Static @@ -267,8 +267,8 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1, y: 7, z: 0} - Rotate: {x: 0.785398185, y: 0.785398185, z: 0.785398185} + Translate: {x: 2, y: 2, z: 3} + Rotate: {x: 0, y: 0, z: 0} Scale: {x: 0.999990404, y: 0.999994457, z: 0.999985337} IsActive: true RigidBody Component: @@ -278,7 +278,7 @@ Drag: 0.00999999978 Angular Drag: 0 Use Gravity: true - Gravity Scale: 1 + Gravity Scale: 5 Interpolate: true Sleeping Enabled: true Freeze Position X: false diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index f63bac40..b171f206 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -108,7 +108,7 @@ namespace SHADE * Performs a single simulation step.
* Detect Collisions -> Integrate Forces -> Resolve Contacts -> Integrate Velocities * @param dt - * A discrete time step for the simulation. This should be consistent for deteministic + * A discrete time step for the simulation. This should be consistent for deterministic * behaviour. */ void Step (float dt); @@ -118,7 +118,7 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - // EntityIDs are used to map resolved contraints back to bodies + // EntityIDs are used to map resolved constraints back to bodies using RigidBodies = std::unordered_map; /*---------------------------------------------------------------------------------*/ From dddb556553737387ac9ef4538fba8759766595e8 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Sun, 22 Jan 2023 17:59:36 +0800 Subject: [PATCH 139/164] Progress --- .../InputBindings/SHInputBindingsPanel.cpp | 60 +++--- SHADE_Engine/src/Input/SHInputManager.cpp | 181 ++++++++++++++++++ SHADE_Engine/src/Input/SHInputManager.h | 3 + 3 files changed, 216 insertions(+), 28 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp index 5dc9e17a..ca9e263f 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp @@ -5,6 +5,9 @@ namespace SHADE { + static std::vector bindingRenames; + static std::vector positiveKeyListening; + //Internal Helper function std::string labelConcat(char const* label, size_t entryNumber) { @@ -30,6 +33,9 @@ namespace SHADE { std::string newBindingName = "Binding" + std::to_string(SHInputManager::CountBindings()); SHInputManager::AddBinding(newBindingName); + bindingRenames.resize(SHInputManager::CountBindings()); + for (auto& s : bindingRenames) + s.clear(); } if (ImGui::IsItemHovered()) { @@ -45,29 +51,28 @@ namespace SHADE for (auto& binding : SHInputManager::GetBindings()) { ImGui::Separator(); - std::string bindingName = binding.first; //Binding name to use as argument when modifying binding values + + ImGui::Text("Entry Number %d", entryNumber); //Non-modifiable binding name ImGui::Text("Binding Name: %s", binding.first); - std::string bindingModifiedName; - //MAKE THE INPUTTEXT WORK - ImGui::InputText(labelConcat("##bindingModifyName", entryNumber).c_str(), &bindingModifiedName); + ImGui::InputText(labelConcat("##bindingModifyName", entryNumber).c_str(), &bindingRenames[entryNumber]); ImGui::SameLine(); if (ImGui::Button(labelConcat("Rename##bindingRename", entryNumber).c_str())) { - SHLOGV_CRITICAL("\"{0}\" > \"{1}\"", bindingName, bindingModifiedName); - SHInputManager::RenameBinding(bindingName, bindingModifiedName); + SHInputManager::RenameBinding(binding.first, bindingRenames[entryNumber]); + bindingRenames[entryNumber].clear(); } //Modifiable binding name /*size_t constexpr maxLen = 256; //Maximum binding name length static char bindingNameCStr[maxLen]; - std::copy(bindingName.begin(), - bindingName.length() >= maxLen ? - bindingName.begin() + maxLen : - bindingName.end(), //Does not take into account null terminator + std::copy(binding.first.begin(), + binding.first.length() >= maxLen ? + binding.first.begin() + maxLen : + binding.first.end(), //Does not take into account null terminator bindingNameCStr); - bindingNameCStr[bindingName.length()] = '\0'; //Null terminator + bindingNameCStr[binding.first.length()] = '\0'; //Null terminator //First character char firstChar = bindingNameCStr[0]; @@ -78,8 +83,8 @@ namespace SHADE if (std::strlen(bindingNameCStr) < 1) bindingNameCStr[0] = firstChar; //Rename binding in the input manager - SHInputManager::RenameBinding(bindingName, std::string{ bindingNameCStr }); - bindingName = std::string{ bindingNameCStr }; + SHInputManager::RenameBinding(binding.first, std::string{ bindingNameCStr }); + binding.first = std::string{ bindingNameCStr }; } if (ImGui::IsItemHovered()) { @@ -92,11 +97,10 @@ namespace SHADE //TODO ImGui::Text("Value"); - //Binding Type Combo Box - int bindingType = static_cast(SHInputManager::GetBindingType(bindingName)); + int bindingType = static_cast(SHInputManager::GetBindingType(binding.first)); if (ImGui::Combo(labelConcat("Input Type##", entryNumber).c_str(), &bindingType, "Keyboard / Mouse Buttons / Controller\0Mouse Horizontal\0Mouse Vertical\0Mouse Scroll Wheel")) - SHInputManager::SetBindingType(bindingName, static_cast(bindingType)); + SHInputManager::SetBindingType(binding.first, static_cast(bindingType)); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -109,9 +113,9 @@ namespace SHADE } //Inversion - bool bindingInvert = SHInputManager::GetBindingInverted(bindingName); + bool bindingInvert = SHInputManager::GetBindingInverted(binding.first); if (ImGui::Checkbox(labelConcat("Inverted##", entryNumber).c_str(), &bindingInvert)) - SHInputManager::SetBindingInverted(bindingName, bindingInvert); + SHInputManager::SetBindingInverted(binding.first, bindingInvert); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -124,9 +128,9 @@ namespace SHADE } //Sensitivity - double bindingSensitivity = SHInputManager::GetBindingSensitivity(bindingName); + double bindingSensitivity = SHInputManager::GetBindingSensitivity(binding.first); if (ImGui::InputDouble(labelConcat("Sensitivity##", entryNumber).c_str(), &bindingSensitivity)) - SHInputManager::SetBindingSensitivity(bindingName, bindingSensitivity); + SHInputManager::SetBindingSensitivity(binding.first, bindingSensitivity); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -138,12 +142,12 @@ namespace SHADE //Below this section is only for KB/M type bindings //Not relevant for mouse movement and scrolling - if (SHInputManager::GetBindingType(bindingName) == SHInputManager::SH_BINDINGTYPE::KB_MB_CONTROLLER) + if (SHInputManager::GetBindingType(binding.first) == SHInputManager::SH_BINDINGTYPE::KB_MB_CONTROLLER) { //Dead - float bindingDead = static_cast(SHInputManager::GetBindingDead(bindingName)); + float bindingDead = static_cast(SHInputManager::GetBindingDead(binding.first)); if (ImGui::SliderFloat(labelConcat("Deadzone##", entryNumber).c_str(), &bindingDead, 0.0f, 1.0f)) - SHInputManager::SetBindingDead(bindingName, static_cast(bindingDead)); + SHInputManager::SetBindingDead(binding.first, static_cast(bindingDead)); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -152,9 +156,9 @@ namespace SHADE } //Gravity - double bindingGravity = SHInputManager::GetBindingGravity(bindingName); + double bindingGravity = SHInputManager::GetBindingGravity(binding.first); if (ImGui::InputDouble(labelConcat("Gravity##", entryNumber).c_str(), &bindingGravity)) - SHInputManager::SetBindingGravity(bindingName, static_cast(bindingGravity)); + SHInputManager::SetBindingGravity(binding.first, static_cast(bindingGravity)); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -164,9 +168,9 @@ namespace SHADE } //Snap - bool bindingSnap = SHInputManager::GetBindingSnap(bindingName); + bool bindingSnap = SHInputManager::GetBindingSnap(binding.first); if (ImGui::Checkbox(labelConcat("Snap##", entryNumber).c_str(), &bindingSnap)) - SHInputManager::SetBindingSnap(bindingName, bindingSnap); + SHInputManager::SetBindingSnap(binding.first, bindingSnap); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -174,7 +178,7 @@ namespace SHADE ImGui::Text("the binding's value will jump to neutral 0 before resuming in the input direction"); ImGui::EndTooltip(); } - + //Positive key codes //Negative key codes diff --git a/SHADE_Engine/src/Input/SHInputManager.cpp b/SHADE_Engine/src/Input/SHInputManager.cpp index b8f329b9..c33b36b6 100644 --- a/SHADE_Engine/src/Input/SHInputManager.cpp +++ b/SHADE_Engine/src/Input/SHInputManager.cpp @@ -100,6 +100,187 @@ namespace SHADE } } + std::string SHInputManager::getKeycodeName(SH_KEYCODE k) noexcept + { + int kInt = static_cast(k); + //Numbers + if (kInt >= static_cast(SH_KEYCODE::NUMBER_0) && kInt <= static_cast(SH_KEYCODE::NUMBER_9)) + { + return std::to_string(kInt - 48); + } + //Letters + if (kInt >= static_cast(SH_KEYCODE::A) && kInt <= static_cast(SH_KEYCODE::Z)) + { + return std::string(1, static_cast(kInt)); + } + //Numpads + if (kInt >= static_cast(SH_KEYCODE::NUMPAD_0) && kInt <= static_cast(SH_KEYCODE::NUMPAD_9)) + { + return "Keypad " + std::to_string(kInt - 96); + } + + //Other keys + switch (k) + { + case SH_KEYCODE::LMB: + return "Left Mouse Button"; + break; + case SH_KEYCODE::RMB: + return "Right Mouse Button"; + break; + case SH_KEYCODE::CANCEL: + return "Cancel"; + break; + case SH_KEYCODE::MMB: + return "Middle Mouse Button"; + break; + case SH_KEYCODE::XMB1: + return "X1 Mouse Button"; + break; + case SH_KEYCODE::XMB2: + return "X2 Mouse Button"; + break; + case SH_KEYCODE::BACKSPACE: + return "Backspace"; + break; + case SH_KEYCODE::TAB: + return "Tab"; + break; + case SH_KEYCODE::CLEAR: + return "Clear"; + break; + case SH_KEYCODE::ENTER: + return "Enter"; + break; + case SH_KEYCODE::SHIFT: + return "Shift"; + break; + case SH_KEYCODE::CTRL: + return "Ctrl"; + break; + case SH_KEYCODE::ALT: + return "Alt"; + break; + case SH_KEYCODE::PAUSE: + return "Pause"; + break; + case SH_KEYCODE::CAPS_LOCK: + return "Caps Lock"; + break; + case SH_KEYCODE::IME_KANA: + return "IME Kana / Hangul Mode"; + break; + case SH_KEYCODE::IME_ON: + return "IME On"; + break; + case SH_KEYCODE::IME_JUNJA: + return "IME Junja Mode"; + break; + case SH_KEYCODE::IME_FINAL: + return "IME Final Mode"; + break; + case SH_KEYCODE::IME_HANJA: + return "IME Kanji / Hanja Mode"; + break; + case SH_KEYCODE::IME_OFF: + return "IME Off"; + break; + case SH_KEYCODE::ESCAPE: + return "Esc"; + break; + case SH_KEYCODE::IME_CONVERT: + return "IME Convert"; + break; + case SH_KEYCODE::IME_NONCONVERT: + return "IME Nonconvert"; + break; + case SH_KEYCODE::IME_ACCEPT: + return "IME Accept"; + break; + case SH_KEYCODE::IME_MODECHANGE: + return "IME Mode Change Request"; + break; + case SH_KEYCODE::SPACE: + return "Spacebar"; + break; + case SH_KEYCODE::PAGE_UP: + return "Page Up"; + break; + case SH_KEYCODE::PAGE_DOWN: + return "Page Down"; + break; + case SH_KEYCODE::END: + return "End"; + break; + case SH_KEYCODE::HOME: + return "Home"; + break; + case SH_KEYCODE::LEFT_ARROW: + return "Left Arrow"; + break; + case SH_KEYCODE::UP_ARROW: + return "Up Arrow"; + break; + case SH_KEYCODE::RIGHT_ARROW: + return "Right Arrow"; + break; + case SH_KEYCODE::DOWN_ARROW: + return "Down Arrow"; + break; + case SH_KEYCODE::SELECT: + return "Select"; + break; + case SH_KEYCODE::PRINT: + return "Print"; + break; + case SH_KEYCODE::EXECUTE: + return "Execute"; + break; + case SH_KEYCODE::PRINT_SCREEN: + return "Print Screen"; + break; + case SH_KEYCODE::INSERT: + return "Insert"; + break; + case SH_KEYCODE::DEL: + return "Delete"; + break; + case SH_KEYCODE::HELP: + return "Help"; + break; + case SH_KEYCODE::NUMBER_0: + return "0"; + break; + case SH_KEYCODE::NUMBER_1: + return "1"; + break; + case SH_KEYCODE::NUMBER_2: + return "2"; + break; + case SH_KEYCODE::NUMBER_3: + return "3"; + break; + case SH_KEYCODE::NUMBER_4: + return "4"; + break; + case SH_KEYCODE::NUMBER_5: + return "5"; + break; + case SH_KEYCODE::NUMBER_6: + return "6"; + break; + case SH_KEYCODE::NUMBER_7: + return "7"; + break; + case SH_KEYCODE::NUMBER_8: + return "8"; + break; + case SH_KEYCODE::NUMBER_9: + return "9"; + break; + } + } + //The Binding File format presently goes as such: /* * Binding count diff --git a/SHADE_Engine/src/Input/SHInputManager.h b/SHADE_Engine/src/Input/SHInputManager.h index 5698f4c1..2a9b9dcd 100644 --- a/SHADE_Engine/src/Input/SHInputManager.h +++ b/SHADE_Engine/src/Input/SHInputManager.h @@ -415,6 +415,9 @@ namespace SHADE return controllerInUse; } + //Get the name of keycode + static std::string getKeycodeName(SH_KEYCODE const keycode) noexcept; + //For testing purposes //static void PrintCurrentState() noexcept; From 0c3106f15b9d770be1a2391655ecf63bcf404e36 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Sun, 22 Jan 2023 19:20:03 +0800 Subject: [PATCH 140/164] Abstracted contact derivation as setup for cached SAT --- Assets/Scenes/PhysicsSandbox.shade | 14 +- .../Collision/Narrowphase/SHCollision.h | 105 ++++++- .../Narrowphase/SHConvexVsConvex.cpp | 277 +++++++++--------- .../src/Physics/Dynamics/SHPhysicsWorld.h | 4 - 4 files changed, 243 insertions(+), 157 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index ad77de0a..8f439326 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -79,9 +79,9 @@ Freeze Position X: false Freeze Position Y: false Freeze Position Z: false - Freeze Rotation X: true - Freeze Rotation Y: true - Freeze Rotation Z: true + Freeze Rotation X: false + Freeze Rotation Y: false + Freeze Rotation Z: false IsActive: true Collider Component: DrawColliders: false @@ -90,7 +90,7 @@ Collision Tag: 1 Type: Sphere Radius: 1 - Friction: 1 + Friction: 0.400000006 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0, z: 0} @@ -131,7 +131,7 @@ Collision Tag: 2 Type: Box Half Extents: {x: 1, y: 1, z: 1} - Friction: 1 + Friction: 0.400000006 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0, z: 0} @@ -268,7 +268,7 @@ Components: Transform Component: Translate: {x: 2, y: 2, z: 3} - Rotate: {x: 0, y: 0, z: 0} + Rotate: {x: 0.785398185, y: 0.785398185, z: 0.785398185} Scale: {x: 0.999990404, y: 0.999994457, z: 0.999985337} IsActive: true RigidBody Component: @@ -278,7 +278,7 @@ Drag: 0.00999999978 Angular Drag: 0 Use Gravity: true - Gravity Scale: 5 + Gravity Scale: 1 Interpolate: true Sleeping Enabled: true Freeze Position X: false diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 1ef75974..e78b33fa 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -99,9 +99,27 @@ namespace SHADE // Sphere VS Convex - static FaceQuery findClosestFace (const SHSphere& sphere, const SHConvexPolyhedron& polyhedron) noexcept; - static int32_t findClosestPoint (const SHSphere& sphere, const SHConvexPolyhedron& polyhedron, int32_t faceIndex) noexcept; - static int32_t findVoronoiRegion (const SHSphere& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; + static FaceQuery findClosestFace + ( + const SHSphere& sphere + , const SHConvexPolyhedron& polyhedron + ) noexcept; + + static int32_t findClosestPoint + ( + const SHSphere& sphere + , const SHConvexPolyhedron& polyhedron + , int32_t faceIndex + ) noexcept; + + static int32_t findVoronoiRegion + ( + const SHSphere& sphere + , const SHVec3& faceVertex + , const SHVec3& faceNormal + , const SHVec3& tangent1 + , const SHVec3& tangent2 + ) noexcept; // Capsule VS Convex @@ -115,16 +133,79 @@ namespace SHADE * https://github.com/RandyGaul/qu3e/blob/master/src/collision/q3Collide.cpp */ - static FaceQuery queryFaceDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; - static EdgeQuery queryEdgeDirections (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept; + static FaceQuery queryFaceDirections + ( + const SHConvexPolyhedron& A + , const SHConvexPolyhedron& B + ) noexcept; - static bool buildMinkowskiFace (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static bool isMinkowskiFace (const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept; - static float distanceBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static SHVec3 findClosestPointBetweenEdges (const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept; - static int32_t findIncidentFace (const SHConvexPolyhedron& poly, const SHVec3& normal) noexcept; - static std::vector clipPolygonWithPlane (const std::vector& in, int32_t numIn, const SHPlane& plane, int32_t planeIdx) noexcept; - static std::vector reduceContacts (const std::vector& in, const SHVec3& faceNormal) noexcept; + static EdgeQuery queryEdgeDirections + ( + const SHConvexPolyhedron& A + , const SHConvexPolyhedron& B + ) noexcept; + + static bool buildMinkowskiFace + ( + const SHConvexPolyhedron& A + , const SHConvexPolyhedron& B + , int32_t edgeA + , int32_t edgeB + ) noexcept; + + static bool isMinkowskiFace + ( + const SHVec3& a + , const SHVec3& b + , const SHVec3& c + , const SHVec3& d + ) noexcept; + + static float distanceBetweenEdges + ( + const SHConvexPolyhedron& A + , const SHConvexPolyhedron& B + , int32_t edgeA + , int32_t edgeB + ) noexcept; + + static SHVec3 findClosestPointBetweenEdges + ( + const SHConvexPolyhedron& A + , const SHConvexPolyhedron& B + , int32_t edgeA + , int32_t edgeB + ) noexcept; + + static int32_t findIncidentFace + ( + const SHConvexPolyhedron& poly + , const SHVec3& normal + ) noexcept; + + static bool findFaceContacts + ( + SHManifold& manifold + , const SHConvexPolyhedron& incPoly + , int32_t incFace + , const SHConvexPolyhedron& refPoly + , int32_t refFace + , bool flip + ) noexcept; + + static std::vector clipPolygonWithPlane + ( + const std::vector& in + , int32_t numIn + , const SHPlane& plane + , int32_t planeIdx + ) noexcept; + + static std::vector reduceContacts + ( + const std::vector& in + , const SHVec3& faceNormal + ) noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index a59e8f28..71539949 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -128,139 +128,7 @@ namespace SHADE const SHVec3 REFERENCE_NORMAL = referencePoly->GetNormal(minFaceQuery.closestFace); const int32_t INCIDENT_FACE_IDX = findIncidentFace(*incidentPoly, REFERENCE_NORMAL); - const SHHalfEdgeStructure::Face& INCIDENT_FACE = incidentPoly->GetFace(INCIDENT_FACE_IDX); - const SHHalfEdgeStructure::Face& REFERENCE_FACE = referencePoly->GetFace(minFaceQuery.closestFace); - - const int32_t NUM_INCIDENT_VERTICES = static_cast(INCIDENT_FACE.vertexIndices.size()); - const int32_t NUM_REFERENCE_VERTICES = static_cast(REFERENCE_FACE.vertexIndices.size()); - - // Build incoming vertices to clip - std::vector clipIn; - clipIn.resize(NUM_INCIDENT_VERTICES * 2, ClipVertex{}); - - int32_t numClipIn = 0; - for (int32_t i = 0; i < NUM_INCIDENT_VERTICES; ++i) - { - const int32_t prevI = i - 1 < 0 ? NUM_INCIDENT_VERTICES - 1 : i - 1; - - const int32_t V_INDEX = INCIDENT_FACE.vertexIndices[i].index; - - // The incoming id is the previous edge - // The outgoing id is the current edge (where this vertex is the tail of) - - ClipVertex v; - v.position = incidentPoly->GetVertex(V_INDEX); - v.featurePair.inI = INCIDENT_FACE.vertexIndices[prevI].edgeIndex; - v.featurePair.outI = INCIDENT_FACE.vertexIndices[i].edgeIndex; - v.featurePair.inR = 0; - v.featurePair.outR = 0; - - clipIn[numClipIn++] = v; - } - - // Clip the vertices against the reference face side planes. - // Number of side planes == number of edges == number of vertices - for (int32_t i = 0; i < NUM_REFERENCE_VERTICES; ++i) - { - // Side plane can be built with the vertex on the edge and the plane's normal - // Plane normal = faceNormal X tangent (v2 - v1) - - const int32_t V1_INDEX = REFERENCE_FACE.vertexIndices[i].index; - const int32_t V2_INDEX = REFERENCE_FACE.vertexIndices[(i + 1) % NUM_REFERENCE_VERTICES].index; - - const SHVec3 V1 = referencePoly->GetVertex(V1_INDEX); - const SHVec3 V2 = referencePoly->GetVertex(V2_INDEX); - - const SHVec3 TANGENT = SHVec3::Normalise(V2 - V1); - const SHPlane CLIP_PLANE { V1, SHVec3::Cross(REFERENCE_NORMAL, TANGENT) }; - - std::vector clipOut = clipPolygonWithPlane(clipIn, numClipIn, CLIP_PLANE, REFERENCE_FACE.vertexIndices[i].edgeIndex); - if (clipOut.empty()) - return false; - - // Replace the clip container's contents with the clipped points for the next clipping pass - const int32_t NUM_CLIPPED = static_cast(clipOut.size()); - for (int32_t clippedIndex = 0; clippedIndex < NUM_CLIPPED; ++clippedIndex) - { - clipIn[clippedIndex].position = clipOut[clippedIndex].position; - clipIn[clippedIndex].featurePair.key = clipOut[clippedIndex].featurePair.key; - } - numClipIn = NUM_CLIPPED; - } - - // From the final set of clipped points, only keep the points that are below the reference plane. - const SHPlane REFERENCE_PLANE{ referencePoly->GetVertex(REFERENCE_FACE.vertexIndices.front().index), REFERENCE_NORMAL }; - - std::vector contacts; - for (int32_t i = 0; i < numClipIn; ++i) - { - const SHVec3 POS = clipIn[i].position; - const float DIST = REFERENCE_PLANE.SignedDistance(POS); - if (DIST <= 0.0f) - { - SHContact contact; - contact.position = POS; - contact.penetration = -DIST; - - if (flipNormal) - { - contact.featurePair.inI = clipIn[i].featurePair.inR; - contact.featurePair.inR = clipIn[i].featurePair.inI; - contact.featurePair.outI = clipIn[i].featurePair.outR; - contact.featurePair.outR = clipIn[i].featurePair.outI; - } - else - { - contact.featurePair.key = clipIn[i].featurePair.key; - } - - contacts.emplace_back(contact); - ++numContacts; - } - } - - // Reduce contact manifold if more than 4 points - if (numContacts > 4) - { - const auto INDICES_TO_KEEP = reduceContacts(contacts, REFERENCE_NORMAL); - std::vector reducedContacts; - - const int32_t NUM_REDUCED = static_cast(INDICES_TO_KEEP.size()); - for (int32_t i = 0; i < NUM_REDUCED; ++i) - reducedContacts.emplace_back(contacts[INDICES_TO_KEEP[i]]); - - contacts.clear(); - // Copy contacts to main container - for (auto& contact : reducedContacts) - contacts.emplace_back(contact); - } - - // Remove potential duplicate contact points - // No way about this being an n^2 loop - static constexpr float THRESHOLD = SHPHYSICS_SAME_CONTACT_DISTANCE * SHPHYSICS_SAME_CONTACT_DISTANCE; - for (auto i = contacts.begin(); i != contacts.end(); ++i) - { - for (auto j = i + 1; j != contacts.end();) - { - const float D2 = SHVec3::DistanceSquared(i->position, j->position); - if (D2 < THRESHOLD) - j = contacts.erase(j); - else - ++j; - } - } - - // Copy final contacts into the manifold - numContacts = static_cast(contacts.size()); - for (int32_t i = 0; i < numContacts; ++i) - manifold.contacts[i] = contacts[i]; - - manifold.numContacts = numContacts; - manifold.normal = REFERENCE_NORMAL; - if (flipNormal) - manifold.normal = -manifold.normal; - - return true; + return findFaceContacts(manifold, *incidentPoly, INCIDENT_FACE_IDX, *referencePoly, minFaceQuery.closestFace, flipNormal); } /*-----------------------------------------------------------------------------------*/ @@ -442,7 +310,7 @@ namespace SHADE const float A2_OVER_A1 = VB_DOT_VA == 0.0f ? 0.0f : VB_DOT_VB / VB_DOT_VA; const float NUMERATOR = C_DOT_VA * A2_OVER_A1 - C_DOT_VB; const float DENOMINATOR = AV_DOT_VB - AV_DOT_VA * A2_OVER_A1; - const float R = DENOMINATOR == 0.0f ? NUMERATOR : NUMERATOR / DENOMINATOR; + const float R = std::clamp(NUMERATOR / DENOMINATOR, 0.0f, 1.0f); // Just take a point from A since it's A -> B return TAIL_A + R * VA; @@ -470,6 +338,147 @@ namespace SHADE return bestFace; } + bool SHCollision::findFaceContacts(SHManifold& manifold, const SHConvexPolyhedron& incPoly, int32_t incFace, const SHConvexPolyhedron& refPoly, int32_t refFace, bool flip) noexcept + { + const SHHalfEdgeStructure::Face& INCIDENT_FACE = incPoly.GetFace(incFace); + const SHHalfEdgeStructure::Face& REFERENCE_FACE = refPoly.GetFace(refFace); + + const int32_t NUM_INCIDENT_VERTICES = static_cast(INCIDENT_FACE.vertexIndices.size()); + const int32_t NUM_REFERENCE_VERTICES = static_cast(REFERENCE_FACE.vertexIndices.size()); + + // Build incoming vertices to clip + std::vector clipIn; + clipIn.resize(NUM_INCIDENT_VERTICES * 2, ClipVertex{}); + + int32_t numClipIn = 0; + for (int32_t i = 0; i < NUM_INCIDENT_VERTICES; ++i) + { + const int32_t prevI = i - 1 < 0 ? NUM_INCIDENT_VERTICES - 1 : i - 1; + + const int32_t V_INDEX = INCIDENT_FACE.vertexIndices[i].index; + + // The incoming id is the previous edge + // The outgoing id is the current edge (where this vertex is the tail of) + + ClipVertex v; + v.position = incPoly.GetVertex(V_INDEX); + v.featurePair.inI = INCIDENT_FACE.vertexIndices[prevI].edgeIndex; + v.featurePair.outI = INCIDENT_FACE.vertexIndices[i].edgeIndex; + v.featurePair.inR = 0; + v.featurePair.outR = 0; + + clipIn[numClipIn++] = v; + } + + // Clip the vertices against the reference face side planes. + // Number of side planes == number of edges == number of vertices + const SHVec3 REF_NORMAL = refPoly.GetNormal(refFace); + for (int32_t i = 0; i < NUM_REFERENCE_VERTICES; ++i) + { + // Side plane can be built with the vertex on the edge and the plane's normal + // Plane normal = faceNormal X tangent (v2 - v1) + + const int32_t V1_INDEX = REFERENCE_FACE.vertexIndices[i].index; + const int32_t V2_INDEX = REFERENCE_FACE.vertexIndices[(i + 1) % NUM_REFERENCE_VERTICES].index; + + const SHVec3 V1 = refPoly.GetVertex(V1_INDEX); + const SHVec3 V2 = refPoly.GetVertex(V2_INDEX); + + const SHVec3 TANGENT = SHVec3::Normalise(V2 - V1); + const SHPlane CLIP_PLANE { V1, SHVec3::Cross(REF_NORMAL, TANGENT) }; + + std::vector clipOut = clipPolygonWithPlane(clipIn, numClipIn, CLIP_PLANE, REFERENCE_FACE.vertexIndices[i].edgeIndex); + if (clipOut.empty()) + return false; + + // Replace the clip container's contents with the clipped points for the next clipping pass + const int32_t NUM_CLIPPED = static_cast(clipOut.size()); + for (int32_t clippedIndex = 0; clippedIndex < NUM_CLIPPED; ++clippedIndex) + { + clipIn[clippedIndex].position = clipOut[clippedIndex].position; + clipIn[clippedIndex].featurePair.key = clipOut[clippedIndex].featurePair.key; + } + numClipIn = NUM_CLIPPED; + } + + // From the final set of clipped points, only keep the points that are below the reference plane. + const SHPlane REFERENCE_PLANE{ refPoly.GetVertex(REFERENCE_FACE.vertexIndices.front().index), REF_NORMAL }; + + uint32_t numContacts = 0; + + std::vector contacts; + for (int32_t i = 0; i < numClipIn; ++i) + { + const SHVec3 POS = clipIn[i].position; + const float DIST = REFERENCE_PLANE.SignedDistance(POS); + if (DIST <= 0.0f) + { + SHContact contact; + contact.position = POS; + contact.penetration = -DIST; + + if (flip) + { + contact.featurePair.inI = clipIn[i].featurePair.inR; + contact.featurePair.inR = clipIn[i].featurePair.inI; + contact.featurePair.outI = clipIn[i].featurePair.outR; + contact.featurePair.outR = clipIn[i].featurePair.outI; + } + else + { + contact.featurePair.key = clipIn[i].featurePair.key; + } + + contacts.emplace_back(contact); + ++numContacts; + } + } + + // Reduce contact manifold if more than 4 points + if (numContacts > 4) + { + const auto INDICES_TO_KEEP = reduceContacts(contacts, REF_NORMAL); + std::vector reducedContacts; + + const int32_t NUM_REDUCED = static_cast(INDICES_TO_KEEP.size()); + for (int32_t i = 0; i < NUM_REDUCED; ++i) + reducedContacts.emplace_back(contacts[INDICES_TO_KEEP[i]]); + + contacts.clear(); + // Copy contacts to main container + for (auto& contact : reducedContacts) + contacts.emplace_back(contact); + } + + // Remove potential duplicate contact points + // No way about this being an n^2 loop + static constexpr float THRESHOLD = SHPHYSICS_SAME_CONTACT_DISTANCE * SHPHYSICS_SAME_CONTACT_DISTANCE; + for (auto i = contacts.begin(); i != contacts.end(); ++i) + { + for (auto j = i + 1; j != contacts.end();) + { + const float D2 = SHVec3::DistanceSquared(i->position, j->position); + if (D2 < THRESHOLD) + j = contacts.erase(j); + else + ++j; + } + } + + // Copy final contacts into the manifold + numContacts = static_cast(contacts.size()); + for (int32_t i = 0; i < numContacts; ++i) + manifold.contacts[i] = contacts[i]; + + manifold.numContacts = numContacts; + manifold.normal = REF_NORMAL; + if (flip) + manifold.normal = -manifold.normal; + + return true; + } + + std::vector SHCollision::clipPolygonWithPlane(const std::vector& in, int32_t numIn, const SHPlane& plane, int32_t planeIdx) noexcept { std::vector out; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h index b171f206..9ad525e8 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h @@ -139,10 +139,6 @@ namespace SHADE // TODO: Move to island when islands are set up void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept; void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept; - - - - }; From 354d9434f6d3d0604f3b3d7d0746b8506b31400b Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 22 Jan 2023 21:43:37 +0800 Subject: [PATCH 141/164] SHAnimatorComponent and SHRig now use the proper transform and offset matrices --- SHADE_Engine/src/Animation/SHAnimatorComponent.cpp | 10 +++------- SHADE_Engine/src/Animation/SHRig.cpp | 3 ++- SHADE_Engine/src/Animation/SHRig.h | 4 ++++ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 26f6b83e..bd6228bd 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -143,7 +143,7 @@ namespace SHADE { // Check if there is a channel for this node const std::string& BONE_NAME = rig->GetName(node); - SHMatrix transformMatrix = SHMatrix::Identity; + SHMatrix transformMatrix = node->TransformMatrix; if (channelMap.contains(BONE_NAME)) { const auto CHANNEL = channelMap[BONE_NAME]; @@ -154,19 +154,15 @@ namespace SHADE getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime) ); } - else - { - transformMatrix = SHMatrix::Inverse(node->OffsetMatrix); // TODO: Use TransformMatrix for the bone - } // Apply parent's transformation - transformMatrix = parentMatrix * transformMatrix; + transformMatrix = transformMatrix * parentMatrix; // Apply transformations to this node const int BONE_MTX_IDX = rig->GetNodeIndex(node); if (BONE_MTX_IDX >= 0) { - boneMatrices[BONE_MTX_IDX] = rig->GetGlobalInverseMatrix() * transformMatrix * node->OffsetMatrix; + boneMatrices[BONE_MTX_IDX] = node->OffsetMatrix * transformMatrix * rig->GetGlobalInverseMatrix(); } // Apply pose to children diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 84117f77..306c9a00 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -86,7 +86,8 @@ namespace SHADE // Fill the node with data const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef); - newNode->OffsetMatrix = SHMatrix::Inverse(NODE_DATA.transform); + newNode->OffsetMatrix = SHMatrix::Transpose(NODE_DATA.offset); + newNode->TransformMatrix = SHMatrix::Transpose(NODE_DATA.transform); // Populate maps if (!NODE_DATA.name.empty()) diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index f28c628b..0329dc6e 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -43,6 +43,10 @@ namespace SHADE /// SHMatrix OffsetMatrix; /// + /// Matrix that performs a transformation from bone (node) space to local space. + /// + SHMatrix TransformMatrix; + /// /// Child nodes of this node. /// std::vector> Children; From a0f6cd3ae7c6a4aadb5916e8042515e0dd7573f7 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 23 Jan 2023 00:37:22 +0800 Subject: [PATCH 142/164] Cached SAT for improved stability The effects of baumgarte stabilisation can be rather obvious especially when polyhedrons are thrown around at angles. Regardless, the system is relatively stable bar the added energy from the solving method, which may make for a more "bombastic" physics playground --- Assets/Scenes/PhysicsSandbox.shade | 10 +- .../Collision/Contacts/SHCollisionEvents.h | 4 +- .../Physics/Collision/Contacts/SHManifold.h | 3 + .../Physics/Collision/Contacts/SHManifold.hpp | 60 +++---- .../Collision/Narrowphase/SHCollision.h | 11 ++ .../Narrowphase/SHConvexVsConvex.cpp | 149 ++++++++++++++++-- .../Physics/Collision/Narrowphase/SHSATInfo.h | 87 ++++++++++ .../Collision/Narrowphase/SHSATInfo.hpp | 71 +++++++++ 8 files changed, 351 insertions(+), 44 deletions(-) create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.h create mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.hpp diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 8f439326..44e5e03f 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,7 +45,7 @@ NumberOfChildren: 0 Components: Camera Component: - Position: {x: 0, y: 2, z: 7} + Position: {x: 0, y: 2, z: 10} Pitch: 0 Yaw: 0 Roll: 0 @@ -62,7 +62,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1, y: 7.5, z: 0} + Translate: {x: -2, y: 7.5, z: 0} Rotate: {x: -0, y: 0, z: 0.785398185} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -77,7 +77,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: false + Freeze Position Y: true Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -267,8 +267,8 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 2, y: 2, z: 3} - Rotate: {x: 0.785398185, y: 0.785398185, z: 0.785398185} + Translate: {x: 0, y: 7, z: 0} + Rotate: {x: 0, y: 0.785398185, z: 0} Scale: {x: 0.999990404, y: 0.999994457, z: 0.999985337} IsActive: true RigidBody Component: diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h index 3dbb16d0..15142303 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h @@ -38,7 +38,7 @@ namespace SHADE { public: SHCollisionKey info; - SHCollisionState state; + SHCollisionState state = SHCollisionState::INVALID; }; /** @@ -52,7 +52,7 @@ namespace SHADE static constexpr int MAX_NUM_CONTACTS = 4; SHCollisionKey info; - SHCollisionState state; + SHCollisionState state = SHCollisionState::INVALID; SHVec3 normal; SHVec3 contactPoints[MAX_NUM_CONTACTS]; }; diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h index 3b591875..e60e5329 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h @@ -12,6 +12,7 @@ // Primary Header #include "Physics/Dynamics/SHRigidBody.h" +#include "Physics/Collision/Narrowphase/SHSATInfo.h" #include "Physics/Collision/Shapes/SHCollisionShape.h" #include "SHContact.h" #include "SHCollisionEvents.h" @@ -58,6 +59,8 @@ namespace SHADE uint32_t numContacts = 0; SHCollisionState state = SHCollisionState::INVALID; + SHSATInfo cachedSATInfo; + SHVec3 normal; SHVec3 tangents[SHContact::NUM_TANGENTS]; SHContact contacts[MAX_NUM_CONTACTS]; diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp index 23be8c79..f1b93a43 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp +++ b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp @@ -28,13 +28,14 @@ namespace SHADE } inline SHManifold::SHManifold(const SHManifold& rhs) noexcept - : shapeA { rhs.shapeA } - , shapeB { rhs.shapeB } - , bodyA { rhs.bodyA } - , bodyB { rhs.bodyB } - , numContacts { rhs.numContacts } - , state { rhs.state } - , normal { rhs.normal } + : shapeA { rhs.shapeA } + , shapeB { rhs.shapeB } + , bodyA { rhs.bodyA } + , bodyB { rhs.bodyB } + , numContacts { rhs.numContacts } + , state { rhs.state } + , cachedSATInfo { rhs.cachedSATInfo } + , normal { rhs.normal } { static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); @@ -44,13 +45,14 @@ namespace SHADE } inline SHManifold::SHManifold(SHManifold&& rhs) noexcept - : shapeA { rhs.shapeA } - , shapeB { rhs.shapeB } - , bodyA { rhs.bodyA } - , bodyB { rhs.bodyB } - , numContacts { rhs.numContacts } - , state { rhs.state } - , normal { rhs.normal } + : shapeA { rhs.shapeA } + , shapeB { rhs.shapeB } + , bodyA { rhs.bodyA } + , bodyB { rhs.bodyB } + , numContacts { rhs.numContacts } + , state { rhs.state } + , cachedSATInfo { rhs.cachedSATInfo } + , normal { rhs.normal } { static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); @@ -68,13 +70,14 @@ namespace SHADE if (this == &rhs) return *this; - shapeA = rhs.shapeA; - shapeB = rhs.shapeB; - bodyA = rhs.bodyA; - bodyB = rhs.bodyB; - numContacts = rhs.numContacts; - state = rhs.state; - normal = rhs.normal; + shapeA = rhs.shapeA; + shapeB = rhs.shapeB; + bodyA = rhs.bodyA; + bodyB = rhs.bodyB; + numContacts = rhs.numContacts; + state = rhs.state; + cachedSATInfo = rhs.cachedSATInfo; + normal = rhs.normal; static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); @@ -87,13 +90,14 @@ namespace SHADE inline SHManifold& SHManifold::operator=(SHManifold&& rhs) noexcept { - shapeA = rhs.shapeA; - shapeB = rhs.shapeB; - bodyA = rhs.bodyA; - bodyB = rhs.bodyB; - numContacts = rhs.numContacts; - state = rhs.state; - normal = rhs.normal; + shapeA = rhs.shapeA; + shapeB = rhs.shapeB; + bodyA = rhs.bodyA; + bodyB = rhs.bodyB; + numContacts = rhs.numContacts; + state = rhs.state; + cachedSATInfo = rhs.cachedSATInfo; + normal = rhs.normal; static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index e78b33fa..060d42cc 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -139,6 +139,8 @@ namespace SHADE , const SHConvexPolyhedron& B ) noexcept; + + static EdgeQuery queryEdgeDirections ( const SHConvexPolyhedron& A @@ -207,5 +209,14 @@ namespace SHADE , const SHVec3& faceNormal ) noexcept; + // Cached Convex VS Convex + + static bool cachedConvexVSConvex + ( + SHManifold& manifold + , const SHSATInfo& cachedInfo + , const SHConvexPolyhedron& A + , const SHConvexPolyhedron& B + ) noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index 71539949..f2d6fcb3 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -48,25 +48,56 @@ namespace SHADE bool SHCollision::ConvexVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept { - static constexpr float ABSOLUTE_TOLERANCE = 0.01f; - static constexpr float RELATIVE_TOLERANCE = 0.95f; + static constexpr float ABSOLUTE_TOLERANCE = 0.01f; // 0.0005 + static constexpr float RELATIVE_TOLERANCE = 0.95f; // 1.0002 const SHConvexPolyhedron& POLY_A = dynamic_cast(A); const SHConvexPolyhedron& POLY_B = dynamic_cast(B); - // TODO: Check against cached separating axis. + if (manifold.cachedSATInfo.type != SHSATInfo::Type::INVALID) + return cachedConvexVSConvex(manifold, manifold.cachedSATInfo, POLY_A, POLY_B); + + SHSATInfo cachedInfo; const FaceQuery FACE_QUERY_A = queryFaceDirections(POLY_A, POLY_B); if (FACE_QUERY_A.bestDistance > 0.0f) + { + // cache the info + cachedInfo.type = SHSATInfo::Type::FACE; + cachedInfo.info.refPoly = SHSATInfo::ShapeID::A; + cachedInfo.info.refFace = FACE_QUERY_A.closestFace; + + manifold.cachedSATInfo = cachedInfo; + return false; + } + const FaceQuery FACE_QUERY_B = queryFaceDirections(POLY_B, POLY_A); if (FACE_QUERY_B.bestDistance > 0.0f) + { + // cache the info + cachedInfo.type = SHSATInfo::Type::FACE; + cachedInfo.info.refPoly = SHSATInfo::ShapeID::B; + cachedInfo.info.refFace = FACE_QUERY_B.closestFace; + + manifold.cachedSATInfo = cachedInfo; + return false; + } const EdgeQuery EDGE_QUERY = queryEdgeDirections(POLY_A, POLY_B); if (EDGE_QUERY.bestDistance > 0.0f) + { + // cache the info + cachedInfo.type = SHSATInfo::Type::EDGE; + cachedInfo.info.edgeA = EDGE_QUERY.halfEdgeA; + cachedInfo.info.edgeB = EDGE_QUERY.halfEdgeB; + + manifold.cachedSATInfo = cachedInfo; + return false; + } // Apply weight to improve frame coherence of normal directions. // We want a normal in the direction from A -> B, so we flip the normal if needed. @@ -81,6 +112,10 @@ namespace SHADE referencePoly = &POLY_A; incidentPoly = &POLY_B; flipNormal = false; + + cachedInfo.type = SHSATInfo::Type::FACE; + cachedInfo.info.refPoly = SHSATInfo::ShapeID::A; + cachedInfo.info.refFace = minFaceQuery.closestFace; } else { @@ -88,9 +123,13 @@ namespace SHADE referencePoly = &POLY_B; incidentPoly = &POLY_A; flipNormal = true; + + cachedInfo.type = SHSATInfo::Type::FACE; + cachedInfo.info.refPoly = SHSATInfo::ShapeID::B; + cachedInfo.info.refFace = minFaceQuery.closestFace; } - uint32_t numContacts = 0; + // If an edge pair contains the closest distance,vwe ignore any face queries and find the closest points on @@ -114,6 +153,8 @@ namespace SHADE manifold.normal = -manifold.normal; // In this scenario, we only have one contact + uint32_t numContacts = 0; + SHContact contact; contact.featurePair.key = EDGE_QUERY.axis; contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); @@ -122,12 +163,20 @@ namespace SHADE manifold.contacts[numContacts++] = contact; manifold.numContacts = numContacts; + // Cache the info + cachedInfo.type = SHSATInfo::Type::EDGE; + cachedInfo.info.edgeA = EDGE_QUERY.halfEdgeA; + cachedInfo.info.edgeB = EDGE_QUERY.halfEdgeB; + + manifold.cachedSATInfo = cachedInfo; + return true; } const SHVec3 REFERENCE_NORMAL = referencePoly->GetNormal(minFaceQuery.closestFace); const int32_t INCIDENT_FACE_IDX = findIncidentFace(*incidentPoly, REFERENCE_NORMAL); + manifold.cachedSATInfo = cachedInfo; return findFaceContacts(manifold, *incidentPoly, INCIDENT_FACE_IDX, *referencePoly, minFaceQuery.closestFace, flipNormal); } @@ -268,6 +317,7 @@ namespace SHADE * (LB(s) - LA(r)) /dot VB = 0 * * Where LB(s) - LA(r) is the same vector as VB X VA. + * */ const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); @@ -291,15 +341,17 @@ namespace SHADE * c = C' /dot U * * U is either VA or VB + * + * TODO: Check if segments degenerate into a point */ const SHVec3 C = TAIL_B - TAIL_A; - const float VB_DOT_VA = SHVec3::Dot(VB, VA); // a1 - const float VB_DOT_VB = SHVec3::Dot(VB, VB); // a2 - const float AV_DOT_VA = SHVec3::Dot(-VA, VA); // b1 - const float AV_DOT_VB = SHVec3::Dot(-VA, VB); // b2 - const float C_DOT_VA = SHVec3::Dot(C, VA); // c1 + const float VB_DOT_VA = SHVec3::Dot(VB, VA); // a1 + const float VB_DOT_VB = SHVec3::Dot(VB, VB); // a2 + const float AV_DOT_VA = SHVec3::Dot(-VA, VA); // b1 + const float AV_DOT_VB = SHVec3::Dot(-VA, VB); // b2 + const float C_DOT_VA = SHVec3::Dot(C, VA); // c1 const float C_DOT_VB = SHVec3::Dot(C, VB); // c2 /* @@ -310,6 +362,8 @@ namespace SHADE const float A2_OVER_A1 = VB_DOT_VA == 0.0f ? 0.0f : VB_DOT_VB / VB_DOT_VA; const float NUMERATOR = C_DOT_VA * A2_OVER_A1 - C_DOT_VB; const float DENOMINATOR = AV_DOT_VB - AV_DOT_VA * A2_OVER_A1; + + // We clamp it because the factor cannot be beyond [0,1] as it would be beyond the segment if it was so. const float R = std::clamp(NUMERATOR / DENOMINATOR, 0.0f, 1.0f); // Just take a point from A since it's A -> B @@ -687,5 +741,82 @@ namespace SHADE return indicesToKeep; } + bool SHCollision::cachedConvexVSConvex(SHManifold& manifold, const SHSATInfo& cachedInfo, const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept + { + if (cachedInfo.type == SHSATInfo::Type::FACE) + { + SHASSERT(cachedInfo.info.refPoly != SHSATInfo::ShapeID::INVALID, "Attempted to perform cached SAT with an invalid cached collision!") + + // Assume the reference poly was A + const SHConvexPolyhedron* incPoly = &B; + const SHConvexPolyhedron* refPoly = &A; + bool flip = false; + + // Swap if it was B + if (cachedInfo.info.refPoly == SHSATInfo::ShapeID::B) + { + refPoly = &B; + incPoly = &A; + flip = true; + } + + const SHHalfEdgeStructure::Face& REF_FACE = refPoly->GetFace(cachedInfo.info.refFace); + const SHVec3 REF_NORMAL = refPoly->GetNormal(cachedInfo.info.refFace); + + const SHVec3 SUPPORT_POINT = incPoly->FindSupportPoint(-REF_NORMAL); + + const SHPlane REF_FACE_PLANE { refPoly->GetVertex(REF_FACE.vertexIndices[0].index), REF_NORMAL }; + const float DISTANCE = REF_FACE_PLANE.SignedDistance(SUPPORT_POINT); + + if (DISTANCE > 0.0f) + return false; + + // Find the incident face + const int32_t INCIDENT_FACE_INDEX = findIncidentFace(*incPoly, REF_NORMAL); + + return findFaceContacts(manifold, *incPoly, INCIDENT_FACE_INDEX, *refPoly, cachedInfo.info.refFace, flip); + } + + if (cachedInfo.type == SHSATInfo::Type::EDGE) + { + const float DISTANCE = distanceBetweenEdges(A, B, cachedInfo.info.edgeA, cachedInfo.info.edgeB); + if (DISTANCE > 0.0f) + return false; + + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(cachedInfo.info.edgeA); + const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(cachedInfo.info.edgeB); + + const SHVec3 HEAD_A = A.GetVertex(HALF_EDGE_A.headVertexIndex); + const SHVec3 TAIL_A = A.GetVertex(HALF_EDGE_A.tailVertexIndex); + const SHVec3 HEAD_B = B.GetVertex(HALF_EDGE_B.headVertexIndex); + const SHVec3 TAIL_B = B.GetVertex(HALF_EDGE_B.tailVertexIndex); + + const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); + const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); + + manifold.normal = SHVec3::Cross(VB, VA); + // Flip normal if need to ( A -> B) + if (SHVec3::Dot(manifold.normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) + manifold.normal = -manifold.normal; + + // In this scenario, we only have one contact + uint32_t numContacts = 0; + + SHContact contact; + // Take feature pair key from previous manifold + contact.featurePair.key = manifold.contacts[0].featurePair.key; + contact.position = findClosestPointBetweenEdges(A, B, cachedInfo.info.edgeA, cachedInfo.info.edgeB); + contact.penetration = -DISTANCE; + + manifold.contacts[numContacts++] = contact; + manifold.numContacts = numContacts; + + return true; + } + + // Should never reach this. + return false; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.h new file mode 100644 index 00000000..25606084 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.h @@ -0,0 +1,87 @@ +/**************************************************************************************** + * \file SHSATInfo.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for storing information of collision detection between two + * convex shapes using SAT. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include // int32_t + +namespace SHADE +{ + /** + * @brief + * Primarily used to cached collision information between two convex polyhedrons for + * temporal coherence. + */ + struct SHSATInfo + { + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + enum class ShapeID : int32_t + { + A = 0 + , B = 1 + + , INVALID = -1 + }; + + enum class Type + { + EDGE + , FACE + + , INVALID = -1 + }; + + union ContactInfo + { + struct // FaceContact + { + ShapeID refPoly; + int32_t refFace; + }; + + struct // Edge Contact + { + int32_t edgeA; + int32_t edgeB; + }; + }; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + Type type; + ContactInfo info; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHSATInfo () noexcept; + SHSATInfo (const SHSATInfo& rhs) noexcept; + SHSATInfo (SHSATInfo&& rhs) noexcept; + ~SHSATInfo () noexcept = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHSATInfo& operator= (const SHSATInfo& rhs) noexcept; + SHSATInfo& operator= (SHSATInfo&& rhs) noexcept; + }; + +} // namespace SHADE + +#include "SHSATInfo.hpp" diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.hpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.hpp new file mode 100644 index 00000000..8e51d54b --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.hpp @@ -0,0 +1,71 @@ +/**************************************************************************************** + * \file SHSATInfo.hpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation of inlined methods for cached SAT Info. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +// Primary Header +#include "SHSATInfo.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-----------------------------------------------------------------------------------*/ + + inline SHSATInfo::SHSATInfo() noexcept + : type { Type::INVALID } + , info {} + { + info.refPoly = ShapeID::INVALID; + info.refFace = -1; + } + + inline SHSATInfo::SHSATInfo(const SHSATInfo& rhs) noexcept + : type { rhs.type } + , info {} + { + info.refPoly = rhs.info.refPoly; + info.refFace = rhs.info.refFace; + + } + + inline SHSATInfo::SHSATInfo(SHSATInfo&& rhs) noexcept + : type { rhs.type } + , info {} + { + info.refPoly = rhs.info.refPoly; + info.refFace = rhs.info.refFace; + } + + /*---------------------------------------------------------------------------------- */ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------- */ + + inline SHSATInfo& SHSATInfo::operator=(const SHSATInfo& rhs) noexcept + { + if (this == &rhs) + return *this; + + type = rhs.type; + info.refPoly = rhs.info.refPoly; + info.refFace = rhs.info.refFace; + + return *this; + } + + inline SHSATInfo& SHSATInfo::operator=(SHSATInfo&& rhs) noexcept + { + type = rhs.type; + info.refPoly = rhs.info.refPoly; + info.refFace = rhs.info.refFace; + + return *this; + } +} From 5730381302bcc23fd0a7fffc6139ced1ce7bef7e Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 23 Jan 2023 00:56:46 +0800 Subject: [PATCH 143/164] Physics objects can be implicity static if only a collider was added. Removed the need to create an extra body. Math is great. --- Assets/Scenes/PhysicsSandbox.shade | 108 +----------------- .../Dynamics/Constraints/SHVelocityState.h | 35 +++--- .../src/Physics/Dynamics/SHContactManager.cpp | 14 ++- .../src/Physics/Dynamics/SHContactSolver.cpp | 37 +++++- 4 files changed, 65 insertions(+), 129 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 44e5e03f..3b940bc1 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -8,23 +8,6 @@ Rotate: {x: 0, y: 0, z: 0.436332315} Scale: {x: 4.61070776, y: 0.99999392, z: 0.999996722} IsActive: true - RigidBody Component: - Type: Static - Auto Mass: false - Mass: .inf - Drag: 0.00999999978 - Angular Drag: 0.00999999978 - Use Gravity: true - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: true - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true Collider Component: DrawColliders: false Colliders: @@ -62,7 +45,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -2, y: 7.5, z: 0} + Translate: {x: 0, y: 7.5, z: 0} Rotate: {x: -0, y: 0, z: 0.785398185} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -77,7 +60,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: true + Freeze Position Y: false Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -107,23 +90,6 @@ Rotate: {x: -0, y: 0, z: -0.436332315} Scale: {x: 4.61071014, y: 0.999995887, z: 1} IsActive: true - RigidBody Component: - Type: Static - Auto Mass: false - Mass: .inf - Drag: 0.00999999978 - Angular Drag: 0.00999999978 - Use Gravity: true - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: true - Freeze Position Y: true - Freeze Position Z: true - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true Collider Component: DrawColliders: false Colliders: @@ -148,23 +114,6 @@ Rotate: {x: -0, y: 0, z: -0} Scale: {x: 10, y: 3, z: 10} IsActive: true - RigidBody Component: - Type: Static - Auto Mass: false - Mass: .inf - Drag: 0.00999999978 - Angular Drag: 0.00999999978 - Use Gravity: true - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: true - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true Collider Component: DrawColliders: false Colliders: @@ -189,23 +138,6 @@ Rotate: {x: -0, y: 0, z: 1.57079601} Scale: {x: 9.99975109, y: 0.499992192, z: 10} IsActive: true - RigidBody Component: - Type: Static - Auto Mass: false - Mass: .inf - Drag: 0.00999999978 - Angular Drag: 0.00999999978 - Use Gravity: true - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: true - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true Collider Component: DrawColliders: false Colliders: @@ -230,23 +162,6 @@ Rotate: {x: -0, y: 0, z: 1.57079601} Scale: {x: 9.99975109, y: 0.499992192, z: 10} IsActive: true - RigidBody Component: - Type: Static - Auto Mass: false - Mass: .inf - Drag: 0.00999999978 - Angular Drag: 0.00999999978 - Use Gravity: true - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: true - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true Collider Component: DrawColliders: false Colliders: @@ -267,7 +182,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 7, z: 0} + Translate: {x: 0, y: 2, z: 3} Rotate: {x: 0, y: 0.785398185, z: 0} Scale: {x: 0.999990404, y: 0.999994457, z: 0.999985337} IsActive: true @@ -316,23 +231,6 @@ Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true - RigidBody Component: - Type: Static - Auto Mass: false - Mass: .inf - Drag: 0.00999999978 - Angular Drag: 0.00999999978 - Use Gravity: true - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: true - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true Collider Component: DrawColliders: false Colliders: diff --git a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h index 6cbb6efb..7c9b6a10 100644 --- a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h +++ b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h @@ -33,23 +33,30 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ SHVelocityState (const SHRigidBody* rigidBody) noexcept + : LinearVelocity { SHVec3::Zero } + , AngularVelocity { SHVec3::Zero } + , LinearLockFactor { SHVec3::Zero } + , AngularLockFactor { SHVec3::Zero } { - LinearVelocity = rigidBody->GetLinearVelocity(); - AngularVelocity = rigidBody->GetAngularVelocity(); - - LinearLockFactor = SHVec3 + if (rigidBody) { - rigidBody->GetFreezePositionX() ? 0.0f : 1.0f - , rigidBody->GetFreezePositionY() ? 0.0f : 1.0f - , rigidBody->GetFreezePositionZ() ? 0.0f : 1.0f - }; + LinearVelocity = rigidBody->GetLinearVelocity(); + AngularVelocity = rigidBody->GetAngularVelocity(); - AngularLockFactor = SHVec3 - { - rigidBody->GetFreezeRotationX() ? 0.0f : 1.0f - , rigidBody->GetFreezeRotationY() ? 0.0f : 1.0f - , rigidBody->GetFreezeRotationZ() ? 0.0f : 1.0f - }; + LinearLockFactor = SHVec3 + { + rigidBody->GetFreezePositionX() ? 0.0f : 1.0f + , rigidBody->GetFreezePositionY() ? 0.0f : 1.0f + , rigidBody->GetFreezePositionZ() ? 0.0f : 1.0f + }; + + AngularLockFactor = SHVec3 + { + rigidBody->GetFreezeRotationX() ? 0.0f : 1.0f + , rigidBody->GetFreezeRotationY() ? 0.0f : 1.0f + , rigidBody->GetFreezeRotationZ() ? 0.0f : 1.0f + }; + } } }; diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp index ade7b482..7434953e 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp @@ -200,11 +200,17 @@ namespace SHADE const auto& STATE_A = VELOCITY_STATES.find(key.GetEntityA())->second; const auto& STATE_B = VELOCITY_STATES.find(key.GetEntityB())->second; - bodyA->SetLinearVelocity(STATE_A.LinearVelocity); - bodyB->SetLinearVelocity(STATE_B.LinearVelocity); + if (bodyA) + { + bodyA->SetLinearVelocity(STATE_A.LinearVelocity); + bodyA->SetAngularVelocity(STATE_A.AngularVelocity); + } - bodyA->SetAngularVelocity(STATE_A.AngularVelocity); - bodyB->SetAngularVelocity(STATE_B.AngularVelocity); + if (bodyB) + { + bodyB->SetLinearVelocity(STATE_B.LinearVelocity); + bodyB->SetAngularVelocity(STATE_B.AngularVelocity); + } } contactSolver.Reset(); diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp index 926e1fed..f16d7f1e 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp +++ b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp @@ -63,13 +63,38 @@ namespace SHADE // Mass data - newConstraint.invMassA = BODY_A->invMass; - newConstraint.invInertiaA = BODY_A->worldInvInertia; - newConstraint.centerOfMassA = BODY_A->worldCentroid; + // Account for implicity-static bodies + if (BODY_A) + { + newConstraint.invMassA = BODY_A->invMass; + newConstraint.centerOfMassA = BODY_A->worldCentroid; + newConstraint.invInertiaA = BODY_A->worldInvInertia; + } + else + { + newConstraint.invMassA = 0.0f; + newConstraint.centerOfMassA = SHVec3::Zero; - newConstraint.invMassB = BODY_B->invMass; - newConstraint.invInertiaB = BODY_B->worldInvInertia; - newConstraint.centerOfMassB = BODY_B->worldCentroid; + // Matrix needs to maintain its homogenous structure + newConstraint.invInertiaA = SHMatrix::Zero; + newConstraint.invInertiaA.m[3][3] = 1.0f; + } + + if (BODY_B) + { + newConstraint.invMassB = BODY_B->invMass; + newConstraint.centerOfMassB = BODY_B->worldCentroid; + newConstraint.invInertiaB = BODY_B->worldInvInertia; + } + else + { + newConstraint.invMassB = 0.0f; + newConstraint.centerOfMassB = SHVec3::Zero; + + // Matrix needs to maintain its homogenous structure + newConstraint.invInertiaB = SHMatrix::Zero; + newConstraint.invInertiaB.m[3][3] = 1.0f; + } // Collision data From 4ed7aa3aed2ccfcf0eb6742bd5dc5b041765aced Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 23 Jan 2023 03:03:40 +0800 Subject: [PATCH 144/164] Reverted an attempt to stabilise edge detection between polyhedrons --- Assets/Scenes/PhysicsSandbox.shade | 8 ++++---- .../Physics/Collision/Narrowphase/SHConvexVsConvex.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 3b940bc1..56f272e0 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -45,7 +45,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 7.5, z: 0} + Translate: {x: 2, y: 7.5, z: 0} Rotate: {x: -0, y: 0, z: 0.785398185} Scale: {x: 1, y: 1, z: 1} IsActive: true @@ -60,7 +60,7 @@ Interpolate: true Sleeping Enabled: true Freeze Position X: false - Freeze Position Y: false + Freeze Position Y: true Freeze Position Z: false Freeze Rotation X: false Freeze Rotation Y: false @@ -182,8 +182,8 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 0, y: 2, z: 3} - Rotate: {x: 0, y: 0.785398185, z: 0} + Translate: {x: 0, y: 7, z: 0} + Rotate: {x: 0, y: 0, z: 0.785398185} Scale: {x: 0.999990404, y: 0.999994457, z: 0.999985337} IsActive: true RigidBody Component: diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp index f2d6fcb3..e38a66a7 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp @@ -364,7 +364,7 @@ namespace SHADE const float DENOMINATOR = AV_DOT_VB - AV_DOT_VA * A2_OVER_A1; // We clamp it because the factor cannot be beyond [0,1] as it would be beyond the segment if it was so. - const float R = std::clamp(NUMERATOR / DENOMINATOR, 0.0f, 1.0f); + const float R = DENOMINATOR == 0.0f ? NUMERATOR : NUMERATOR / DENOMINATOR; // Just take a point from A since it's A -> B return TAIL_A + R * VA; From e70354df74a8ce51b4da456f422c46ce915f4cf5 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Tue, 24 Jan 2023 19:17:27 +0800 Subject: [PATCH 145/164] Get input code name functions --- SHADE_Engine/src/Input/SHInputManager.cpp | 324 ++++++++++++++++++++-- SHADE_Engine/src/Input/SHInputManager.h | 7 +- 2 files changed, 308 insertions(+), 23 deletions(-) diff --git a/SHADE_Engine/src/Input/SHInputManager.cpp b/SHADE_Engine/src/Input/SHInputManager.cpp index c33b36b6..87ea3aa5 100644 --- a/SHADE_Engine/src/Input/SHInputManager.cpp +++ b/SHADE_Engine/src/Input/SHInputManager.cpp @@ -100,7 +100,7 @@ namespace SHADE } } - std::string SHInputManager::getKeycodeName(SH_KEYCODE k) noexcept + std::string SHInputManager::getKeyCodeName(SH_KEYCODE k) noexcept { int kInt = static_cast(k); //Numbers @@ -118,6 +118,11 @@ namespace SHADE { return "Keypad " + std::to_string(kInt - 96); } + //Function keys + if (kInt >= static_cast(SH_KEYCODE::F1) && kInt <= static_cast(SH_KEYCODE::F24)) + { + return "F" + std::to_string(kInt - 111); + } //Other keys switch (k) @@ -248,35 +253,312 @@ namespace SHADE case SH_KEYCODE::HELP: return "Help"; break; - case SH_KEYCODE::NUMBER_0: - return "0"; + case SH_KEYCODE::LEFT_WINDOWS: + return "Left Windows"; break; - case SH_KEYCODE::NUMBER_1: - return "1"; + case SH_KEYCODE::RIGHT_WINDOWS: + return "Right Windows"; break; - case SH_KEYCODE::NUMBER_2: - return "2"; + case SH_KEYCODE::APPS: + return "Applications"; break; - case SH_KEYCODE::NUMBER_3: - return "3"; + case SH_KEYCODE::SLEEP: + return "Sleep"; break; - case SH_KEYCODE::NUMBER_4: - return "4"; + case SH_KEYCODE::MULTIPLY: + return "Multiply"; break; - case SH_KEYCODE::NUMBER_5: - return "5"; + case SH_KEYCODE::ADD: + return "Add"; break; - case SH_KEYCODE::NUMBER_6: - return "6"; + case SH_KEYCODE::SEPARATOR: + return "Separator"; break; - case SH_KEYCODE::NUMBER_7: - return "7"; + case SH_KEYCODE::SUBTRACT: + return "Subtract"; break; - case SH_KEYCODE::NUMBER_8: - return "8"; + case SH_KEYCODE::DECIMAL: + return "Decimal"; break; - case SH_KEYCODE::NUMBER_9: - return "9"; + case SH_KEYCODE::DIVIDE: + return "Divide"; + break; + case SH_KEYCODE::NUM_LOCK: + return "Num Lock"; + break; + case SH_KEYCODE::SCROLL_LOCK: + return "Scroll Lock"; + break; + case SH_KEYCODE::OEM_PC98_NUMPAD_EQUAL: + return "OEM PC98 Numpad Equal / Fujitsu Dictionary"; + break; + case SH_KEYCODE::OEM_FUJITSU_UNREGISTER: + return "OEM Fujitsu Unregister"; + break; + case SH_KEYCODE::OEM_FUJITSU_REGISTER: + return "OEM Fujitsu Register"; + break; + case SH_KEYCODE::OEM_FUJITSU_LEFT_THUMB: + return "OEM Fujitsu Left Thumb"; + break; + case SH_KEYCODE::OEM_FUJITSU_RIGHT_THUMB: + return "OEM Fujitsu Right Thumb"; + break; + case SH_KEYCODE::LEFT_SHIFT: + return "Left Shift"; + break; + case SH_KEYCODE::RIGHT_SHIFT: + return "Right Shift"; + break; + case SH_KEYCODE::LEFT_CTRL: + return "Left Ctrl"; + break; + case SH_KEYCODE::RIGHT_CTRL: + return "Right Ctrl"; + break; + case SH_KEYCODE::LEFT_ALT: + return "Left Alt"; + break; + case SH_KEYCODE::RIGHT_ALT: + return "Right Alt"; + break; + case SH_KEYCODE::BROWSER_BACK: + return "Browser Back"; + break; + case SH_KEYCODE::BROWSER_FORWARD: + return "Browser Forward"; + break; + case SH_KEYCODE::BROWSER_REFRESH: + return "Browser Refresh"; + break; + case SH_KEYCODE::BROWSER_STOP: + return "Browser Stop"; + break; + case SH_KEYCODE::BROWSER_SEARCH: + return "Browser Search"; + break; + case SH_KEYCODE::BROWSER_FAVOURITES: + return "Browser Favourites"; + break; + case SH_KEYCODE::BROWSER_HOME: + return "Browser Start and Home"; + break; + case SH_KEYCODE::VOLUME_MUTE: + return "Volume Mute"; + break; + case SH_KEYCODE::VOLUME_DOWN: + return "Volume Down"; + break; + case SH_KEYCODE::VOLUME_UP: + return "Volume Up"; + break; + case SH_KEYCODE::MEDIA_NEXT_TRACK: + return "Media Next Track"; + break; + case SH_KEYCODE::MEDIA_PREVIOUS_TRACK: + return "Media Previous Track"; + break; + case SH_KEYCODE::MEDIA_STOP: + return "Media Stop"; + break; + case SH_KEYCODE::MEDIA_PLAY_PAUSE: + return "Media Play/Pause"; + break; + case SH_KEYCODE::LAUNCH_MAIL: + return "Launch Mail"; + break; + case SH_KEYCODE::LAUNCH_MEDIA_SELECT: + return "Select Media"; + break; + case SH_KEYCODE::LAUNCH_APP_1: + return "Launch App 1"; + break; + case SH_KEYCODE::LAUNCH_APP_2: + return "Launch App 2"; + break; + case SH_KEYCODE::OEM_1: + return "; or :"; + break; + case SH_KEYCODE::OEM_PLUS: + return "+"; + break; + case SH_KEYCODE::OEM_COMMA: + return ","; + break; + case SH_KEYCODE::OEM_2: + return "/ or ?"; + break; + case SH_KEYCODE::OEM_3: + return "` or ~"; + break; + case SH_KEYCODE::OEM_4: + return "[ or {"; + break; + case SH_KEYCODE::OEM_5: + return "\\ or |"; + break; + case SH_KEYCODE::OEM_6: + return "] or }"; + break; + case SH_KEYCODE::OEM_7: + return "\' or \""; + break; + case SH_KEYCODE::OEM_8: + return "OEM 8"; + break; + case SH_KEYCODE::OEM_AX: + return "OEM AX"; + break; + case SH_KEYCODE::OEM_102: + return "< or > or \\ or |"; + break; + case SH_KEYCODE::OEM_ICO_HELP: + return "ICO Help"; + break; + case SH_KEYCODE::OEM_ICO_00: + return "ICO 00"; + break; + case SH_KEYCODE::IME_PROCESS: + return "IME Process"; + break; + case SH_KEYCODE::OEM_ICO_CLEAR: + return "ICO Clear"; + break; + case SH_KEYCODE::PACKET: + return "Packet"; + break; + case SH_KEYCODE::OEM_RESET: + return "OEM Reset"; + break; + case SH_KEYCODE::OEM_JUMP: + return "OEM Jump"; + break; + case SH_KEYCODE::OEM_PA1: + return "OEM PA1"; + break; + case SH_KEYCODE::OEM_PA2: + return "OEM PA2"; + break; + case SH_KEYCODE::OEM_PA3: + return "OEM PA3"; + break; + case SH_KEYCODE::OEM_WSCTRL: + return "OEM WSCTRL"; + break; + case SH_KEYCODE::OEM_CUSEL: + return "OEM CuSel"; + break; + case SH_KEYCODE::OEM_ATTN: + return "OEM Attn"; + break; + case SH_KEYCODE::OEM_FINISH: + return "OEM Finish"; + break; + case SH_KEYCODE::OEM_COPY: + return "OEM Copy"; + break; + case SH_KEYCODE::OEM_AUTO: + return "OEM Auto"; + break; + case SH_KEYCODE::OEM_ENLW: + return "OEM ENLW"; + break; + case SH_KEYCODE::OEM_BACKTAB: + return "OEM Backtab"; + break; + case SH_KEYCODE::ATTN: + return "Attn"; + break; + case SH_KEYCODE::CRSEL: + return "CrSel"; + break; + case SH_KEYCODE::EXSEL: + return "ExSel"; + break; + case SH_KEYCODE::EREOF: + return "Erase EOF"; + break; + case SH_KEYCODE::PLAY: + return "Play"; + break; + case SH_KEYCODE::ZOOM: + return "Zoom"; + break; + case SH_KEYCODE::NONAME: + return "No Name Key"; + break; + case SH_KEYCODE::PA_1: + return "PA1"; + break; + case SH_KEYCODE::OEM_CLEAR: + return "OEM Clear"; + break; + } + } + + std::string SHInputManager::getControllerCodeName(SH_CONTROLLERCODE c) noexcept + { + switch (c) + { + case SH_CONTROLLERCODE::DPAD_UP: + return "D-Pad Up"; + break; + case SH_CONTROLLERCODE::DPAD_DOWN: + return "D-Pad Down"; + break; + case SH_CONTROLLERCODE::DPAD_LEFT: + return "D-Pad Left"; + break; + case SH_CONTROLLERCODE::DPAD_RIGHT: + return "D-Pad Right"; + break; + case SH_CONTROLLERCODE::START: + return "Start Button"; + break; + case SH_CONTROLLERCODE::BACK: + return "Back Button"; + break; + case SH_CONTROLLERCODE::LEFT_THUMBSTICK: + return "Left Thumbstick Button"; + break; + case SH_CONTROLLERCODE::RIGHT_THUMBSTICK: + return "Right Thumbstick Button"; + break; + case SH_CONTROLLERCODE::LEFT_SHOULDER: + return "Left Shoulder Button"; + break; + case SH_CONTROLLERCODE::RIGHT_SHOULDER: + return "Right Shoulder Button"; + break; + case SH_CONTROLLERCODE::A: + return "A Button"; + break; + case SH_CONTROLLERCODE::B: + return "B Button"; + break; + case SH_CONTROLLERCODE::X: + return "X Button"; + break; + case SH_CONTROLLERCODE::Y: + return "Y Button"; + break; + case SH_CONTROLLERCODE::LEFT_TRIGGER: + return "Left Trigger"; + break; + case SH_CONTROLLERCODE::RIGHT_TRIGGER: + return "Right Trigger"; + break; + case SH_CONTROLLERCODE::LEFT_THUMBSTICK_X: + return "Left Thumbstick Horizontal"; + break; + case SH_CONTROLLERCODE::LEFT_THUMBSTICK_Y: + return "Left Thumbstick Vertical"; + break; + case SH_CONTROLLERCODE::RIGHT_THUMBSTICK_X: + return "Right Thumbstick Horizontal"; + break; + case SH_CONTROLLERCODE::RIGHT_THUMBSTICK_Y: + return "Right Thumbstick Vertical"; break; } } diff --git a/SHADE_Engine/src/Input/SHInputManager.h b/SHADE_Engine/src/Input/SHInputManager.h index 2a9b9dcd..52322f0c 100644 --- a/SHADE_Engine/src/Input/SHInputManager.h +++ b/SHADE_Engine/src/Input/SHInputManager.h @@ -415,8 +415,11 @@ namespace SHADE return controllerInUse; } - //Get the name of keycode - static std::string getKeycodeName(SH_KEYCODE const keycode) noexcept; + //Get the name of key code + static std::string getKeyCodeName(SH_KEYCODE const keyCode) noexcept; + + //Get the name of controller code + static std::string getControllerCodeName(SH_CONTROLLERCODE const controllerCode) noexcept; //For testing purposes //static void PrintCurrentState() noexcept; From 80a7fe701b4961f65ddf5446784893aab732c950 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 24 Jan 2023 23:31:00 +0800 Subject: [PATCH 146/164] Added debug draw for animation system --- Assets/Models/BoneIKTest4.gltf | 533 ++++++++++++++++++ Assets/Models/BoneIKTest4.shmodel | Bin 0 -> 29138 bytes Assets/Models/BoneIKTest4.shmodel.shmeta | 7 + Assets/Models/racoon.shmodel | Bin 681640 -> 681640 bytes .../src/Animation/SHAnimatorComponent.cpp | 14 +- .../src/Animation/SHAnimatorComponent.h | 2 +- SHADE_Engine/src/Animation/SHRig.cpp | 2 +- 7 files changed, 552 insertions(+), 6 deletions(-) create mode 100644 Assets/Models/BoneIKTest4.gltf create mode 100644 Assets/Models/BoneIKTest4.shmodel create mode 100644 Assets/Models/BoneIKTest4.shmodel.shmeta diff --git a/Assets/Models/BoneIKTest4.gltf b/Assets/Models/BoneIKTest4.gltf new file mode 100644 index 00000000..776e5c7f --- /dev/null +++ b/Assets/Models/BoneIKTest4.gltf @@ -0,0 +1,533 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v3.3.27", + "version" : "2.0" + }, + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 5 + ] + } + ], + "nodes" : [ + { + "name" : "Toe", + "rotation" : [ + 0.3402905762195587, + 2.2418998923967592e-07, + 1.3251393227164954e-07, + 0.9403203725814819 + ], + "translation" : [ + 4.526706700452854e-15, + 1.4782648086547852, + 2.9802322387695312e-08 + ] + }, + { + "children" : [ + 0 + ], + "name" : "Feet", + "rotation" : [ + 0.5031473636627197, + 2.7469864605222938e-08, + 5.997971896931631e-08, + 0.8642006516456604 + ], + "translation" : [ + -1.3515230601335304e-14, + 2.8003838062286377, + 8.940696716308594e-08 + ] + }, + { + "children" : [ + 1 + ], + "name" : "Knee", + "rotation" : [ + -0.18873976171016693, + 1.170667758287891e-07, + 5.999392094224731e-09, + 0.9820272326469421 + ], + "translation" : [ + 2.0332639651815532e-14, + 3.3708431720733643, + 5.960464477539063e-08 + ] + }, + { + "children" : [ + 2 + ], + "name" : "Thigh", + "rotation" : [ + 7.534745805060084e-08, + 0.06310665607452393, + 0.9980068206787109, + 4.764464822670789e-09 + ], + "scale" : [ + 1, + 1.000000238418579, + 1 + ], + "translation" : [ + 0, + 7.316200256347656, + -0.27762365341186523 + ] + }, + { + "mesh" : 0, + "name" : "Cube", + "skin" : 0 + }, + { + "children" : [ + 4, + 3 + ], + "name" : "Armature" + } + ], + "animations" : [ + { + "channels" : [ + { + "sampler" : 0, + "target" : { + "node" : 3, + "path" : "translation" + } + }, + { + "sampler" : 1, + "target" : { + "node" : 3, + "path" : "rotation" + } + }, + { + "sampler" : 2, + "target" : { + "node" : 3, + "path" : "scale" + } + }, + { + "sampler" : 3, + "target" : { + "node" : 2, + "path" : "translation" + } + }, + { + "sampler" : 4, + "target" : { + "node" : 2, + "path" : "rotation" + } + }, + { + "sampler" : 5, + "target" : { + "node" : 2, + "path" : "scale" + } + }, + { + "sampler" : 6, + "target" : { + "node" : 1, + "path" : "translation" + } + }, + { + "sampler" : 7, + "target" : { + "node" : 1, + "path" : "rotation" + } + }, + { + "sampler" : 8, + "target" : { + "node" : 1, + "path" : "scale" + } + }, + { + "sampler" : 9, + "target" : { + "node" : 0, + "path" : "translation" + } + }, + { + "sampler" : 10, + "target" : { + "node" : 0, + "path" : "rotation" + } + }, + { + "sampler" : 11, + "target" : { + "node" : 0, + "path" : "scale" + } + } + ], + "name" : "ArmatureAction", + "samplers" : [ + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 8 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 9 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 10 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 11 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 12 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 13 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 14 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 15 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 16 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 17 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 18 + }, + { + "input" : 7, + "interpolation" : "LINEAR", + "output" : 19 + } + ] + } + ], + "meshes" : [ + { + "name" : "Cube.001", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2, + "JOINTS_0" : 3, + "WEIGHTS_0" : 4 + }, + "indices" : 5 + } + ] + } + ], + "skins" : [ + { + "inverseBindMatrices" : 6, + "joints" : [ + 3, + 2, + 1, + 0 + ], + "name" : "Armature" + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 243, + "max" : [ + 0.5055701732635498, + 7.3792195320129395, + 1.4862254858016968 + ], + "min" : [ + -0.5055701732635498, + -8.90140370302106e-07, + -0.999980628490448 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 243, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 243, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5121, + "count" : 243, + "type" : "VEC4" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 243, + "type" : "VEC4" + }, + { + "bufferView" : 5, + "componentType" : 5123, + "count" : 252, + "type" : "SCALAR" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 4, + "type" : "MAT4" + }, + { + "bufferView" : 7, + "componentType" : 5126, + "count" : 60, + "max" : [ + 2 + ], + "min" : [ + 0.03333333333333333 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 60, + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 60, + "type" : "VEC4" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 60, + "type" : "VEC3" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 60, + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 60, + "type" : "VEC4" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 60, + "type" : "VEC3" + }, + { + "bufferView" : 14, + "componentType" : 5126, + "count" : 60, + "type" : "VEC3" + }, + { + "bufferView" : 15, + "componentType" : 5126, + "count" : 60, + "type" : "VEC4" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 60, + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 60, + "type" : "VEC3" + }, + { + "bufferView" : 18, + "componentType" : 5126, + "count" : 60, + "type" : "VEC4" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 60, + "type" : "VEC3" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 2916, + "byteOffset" : 0, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 2916, + "byteOffset" : 2916, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 1944, + "byteOffset" : 5832, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 972, + "byteOffset" : 7776, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 3888, + "byteOffset" : 8748, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 504, + "byteOffset" : 12636, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 256, + "byteOffset" : 13140 + }, + { + "buffer" : 0, + "byteLength" : 240, + "byteOffset" : 13396 + }, + { + "buffer" : 0, + "byteLength" : 720, + "byteOffset" : 13636 + }, + { + "buffer" : 0, + "byteLength" : 960, + "byteOffset" : 14356 + }, + { + "buffer" : 0, + "byteLength" : 720, + "byteOffset" : 15316 + }, + { + "buffer" : 0, + "byteLength" : 720, + "byteOffset" : 16036 + }, + { + "buffer" : 0, + "byteLength" : 960, + "byteOffset" : 16756 + }, + { + "buffer" : 0, + "byteLength" : 720, + "byteOffset" : 17716 + }, + { + "buffer" : 0, + "byteLength" : 720, + "byteOffset" : 18436 + }, + { + "buffer" : 0, + "byteLength" : 960, + "byteOffset" : 19156 + }, + { + "buffer" : 0, + "byteLength" : 720, + "byteOffset" : 20116 + }, + { + "buffer" : 0, + "byteLength" : 720, + "byteOffset" : 20836 + }, + { + "buffer" : 0, + "byteLength" : 960, + "byteOffset" : 21556 + }, + { + "buffer" : 0, + "byteLength" : 720, + "byteOffset" : 22516 + } + ], + "buffers" : [ + { + "byteLength" : 23236, + "uri" : "data:application/octet-stream;base64,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AACAvwAAAAAAAACAAAAAADKrzjwl638/AAAAAKwPAD5+/X0/AAAAAK0PAD5+/X0/AACAv7e8zrBxjSy0AACAvwAAAAAOxDszAACAv+c9WjQGr9KzhOvOM95M8L1QO34/4JjPsnaqzjwk638/AAAAADKrzjwl638/AACAvwL6UbTt+tEzAACAvwAAAADKdLKxAACAvwAAAAAAAACAAAAAAK0PAL5+/X2/AAAAADSszrwk63+/AAAAAJyrzrwk63+/AACAvwAAAACEt9kzAACAvwAAAAAOxDszAACAv+c9WjQGr9KzAAAAAANN8D1PO36/AAAAAEUiXD7AA3q/AAAAAEoiXD7AA3q/AACAvwL6UbTt+tEzAACAv7e8zrBxjSy0AACAv+c9WjQGr9KzAAAAAJyrzrwk63+/AAAAANhM8D1PO36/AAAAAANN8D1PO36/AAAAAK0PAL5+/X2/AAAAAKoPAL5+/X2/AAAAADSszrwk63+/AACAP8bI0rSVjayzAACAP8G8zjDV+tEzAACAP8tm5zKy3aYzAAAAANhM8D1PO36/AAAAAANN8D1PO36/AAAAAEoiXD7AA3q/AACAPwAAAAAsj+azAACAPwAAAAABJtczAACAPxmuUjTODESzAAAAADSszrwk63+/AAAAAJyrzrwk63+/AAAAANhM8D1PO36/AACAP8bI0rSVjayzAACAP9L58TEZr9KzAACAPxmuUjTODESz4JjPsnaqzjwk638/AAAAADKrzjwl638/AAAAAK0PAD5+/X0/AACAPwAAAADKdLKxAACAP8G8zjDV+tEzAACAP8tm5zKy3aYzdJqEsk4iXL6/A3o/AAAAAE0iXL7AA3o/AAAAAIVM8L1RO34/AACAPwAAAAABJtczAACAP9L58TEZr9KzAACAPxmuUjTODESzAAAAAIVM8L1RO34/hOvOM95M8L1QO34/4JjPsnaqzjwk638/AACAP8bI0rSVjayzAACAP8G8zjDV+tEzAACAP9L58TEZr9KzAACAvwAAAAAAAACAAACAvwAAAACEt9kzAACAvwAAAADrhT2zDtJaszYiXD7AA3q/AAAAAFYiXD6/A3q/AAAAAEUiXD7AA3q/AACAvwAAAAAAAACAAACAvwAAAADrhT2zAACAvwAAAAAA6YQzVhwFs0UiXL7AA3o/dJqEsk4iXL6/A3o/AAAAAE0iXL7AA3o/VhwFs0UiXL7AA3o/1F6AM0EiXL7AA3o/AAAAAE0iXL7AA3o/AACAPwAAAAAsj+azAACAPwAAAAABJtczAACAPwAAAAAqmJgyDtJaszYiXD7AA3q/AAAAAEUiXD7AA3q/AAAAAEoiXD7AA3q/AACAPwAAAAAsj+azAACAPwAAAAAAAACAAACAPwAAAAAqmJgy1F6AM0EiXL7AA3o/oUL3sTAiXL7BA3o/AAAAADEiXL7AA3o/AACAPwAAAAC5+ccyAACAPwAAAAAAAACAAACAPwAAAAAqmJgyAACAvwAAAAAd1SWzAACAvwAAAAD66IQzAACAvwAAAAAA6YQzVhwFs0UiXL7AA3o/1F6AM0EiXL7AA3o/oUL3sTAiXL7BA3o/AACAvwAAAAAd1SWzAACAvwAAAADrhT2zAACAvwAAAAAA6YQzAAAAAFYiXD6/A3q/AAAAAFYiXD6/A3q/DtJaszYiXD7AA3q/AAAAAFYiXD6/A3q/AAAAAFYiXD6/A3q/AACAPwAAAAAAAACAAACAPwAAAAC5+ccyAACAPwAAAAAAAACAAADAPgAAgD8AAMA+AACAPwAAwD4AAIA/AAAAPgAAgD4AAAA+AACAPgAAAD4AAIA+AAAgPwAAgD8AACA/AACAPwAAID8AAIA/AABgPwAAgD4AAGA/AACAPgAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAAD4AAAA/AADAPgAAQD8AAMA+AABAPwAAID8AAEA/AAAgPwAAQD8AACA/AABAPwAAID8AAEA/AAAgPwAAQD8AACA/AABAPwAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AACA/AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AADAPgAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAYD8AAMA+AABgPwAAwD4AAGA/AAAAPgAAwD4AAAA+AADAPgAAAD4AAMA+AAAgPwAAYD8AACA/AABgPwAAID8AAGA/AAAgPwAAYD8AAGA/AADAPgAAYD8AAMA+AABgPwAAwD4AAMA+AADAPgAAwD4AAMA+AADAPgAAwD4AAMA+AADAPgAAwD4AAMA+AADAPgAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AADAPgAAwD4AAIA/AADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAID8AAAAAAABgPwAAgD4AAGA/AACAPgAAID8AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAKA+AADAPgAAoD4AAMA+AACgPgAAwD4AAKA+AADAPgAAoD4AACA/AABwPwAAID8AAHA/AAAgPwAAcD8AAGA/AACgPgAAYD8AAKA+AAAgPwAAoD4AACA/AACgPgAAID8AAKA+AAAgPwAAoD4AACA/AACgPgAAID8AAKA+AADAPgAAcD8AAMA+AABwPwAAwD4AAHA/AAAAPgAAoD4AAAA+AACgPgAAAD4AAKA+AAAgPwAAYD8AACA/AABgPwAAYD8AAMA+AABgPwAAwD4AACA/AABAPwAAID8AAEA/AAAgPwAAQD8AAGA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AABgPwAAID8AAGA/AAAgPwAAYD8AAGA/AADAPgAAYD8AAMA+AABgPwAAwD4AACA/AABgPwAAID8AAGA/AAAgPwAAYD8AAGA/AADAPgAAYD8AAMA+AABgPwAAwD4AACA/iQNfPwAAID+JA18/AAAgP4kDXz8AAGA/AADAPgAAYD8AAMA+AABgPwAAwD4AACA/AABAPwAAID8AAEA/AAAgPwAAQD8BACA/AABAPwEAID8AAEA/AQAgPwAAQD8AACA/AABAPwAAID8AAEA/AAAgPwAAQD8AACA/AABAPwAAID8AAEA/AAAgPwAAQD8AACA/AABAPwAAID8AAEA/AAAgPwAAQD8AACA/AABAPwAAID8AAEA/AAAgPwAAQD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/ePzAPgAAID94/MA+AAAgP3j8wD4AACA/ePzAPgAAID94/MA+AAAgP3j8wD4BACA/AABAPwEAID8AAEA/AQAgPwAAQD8BACA/AABAPwEAID8AAEA/AQAgPwAAQD8AACA/AABgPwAAID8AAGA/AAAgPwAAYD8AAGA/AADAPgAAYD8AAMA+AABgPwAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4BACA//v//PgEAID/+//8+AQAgP/7//z4BACA//v//PgEAID/+//8+AQAgP/7//z4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AADAPgAAID8AAMA+AAAgPwAAwD4AACA/AABgPwAAID8AAGA/AAAgPwAAYD8AAGA/AADAPgAAYD8AAMA+AABgPwAAwD4AACA/AABAPwAAID8AAEA/AAAgPwAAQD8AACA/AABAPwAAID8AAEA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AwIAAAMCAAADAgAAAwIAAAMCAAADAgAAAwIAAAMCAAADAgAAAwIAAAMCAAADAgAAAgEAAAIBAAACAQAAAgEAAAIBAAACAQAAAgEAAAIBAAACAQAAAgEAAAIBAAADAgAAAwIAAAMCAAADAgAAAwIAAAMCAAADAgAAAwIAAAMCAAADAgAAAgEAAAIBAAACAQAAAgEAAAIBAAACAQAAAgEAAAIBAAACAQAAAgMBAAIDAQACAwEAAgMBAAIDAQACAwEAAgEAAAIBAAACAQAAAgEAAAIBAAACAQAAAgEAAAIDAQACAwEAAgMBAAIDAQACAwEAAgMBAAIBAAACAQAAAgEAAAIBAAACAQAAAgEAAAIBAAADAgAAAwIAAAMCAAADAgAAAwIAAAMCAAADAgAAAwIAAAMCAAADAgAAAwIAAAMCAAADAgAA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ZmZmP+/ubj93d3c/AACAP0REhD+JiIg/zcyMPxERkT9VVZU/mpmZP97dnT8iIqI/ZmamP6uqqj/v7q4/MzOzP3d3tz+8u7s/AADAP0RExD+JiMg/zczMPxER0T9VVdU/mpnZP97d3T8iIuI/ZmbmP6uq6j/v7u4/MzPzP3d39z+8u/s/AAAAQGJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvmJk5idQHupAriSOvlPXw7IV8GU9qZh/P1U6hDKgSMayjYeTPb9Vfz8miJwyFELNssvQ1z0dk34/EFvQMvOh17LjExE+8Gp9P7BLBTNyx+OyyrMzPvUGfD8opyEz4xbwsomUUT6ylHo/Fo07M9sw+7KXkGk+d0B5PwjnUTPJ/QGzoux6PvMxeD+lymMzAMkEs7Wtgj5yhXc/c4FwM/qiBbPkYYQ+eUt3PzJddzMASgSz7fyCPvx6dz+GS3gzY60As2J1fj5a+Hc/SrRzM1gB9rLB5XQ+MpJ4P/sdazM/s+eydL1sPoQQeT/XHWEzLFvYskxyaT49Qnk/qvlYM+x11bJCLHg+Ml54P9AZVTPqNO2yWRqRPhCBdT+c8lIzSXsPs1v1sT77CXA/JAJRM8zcM7N5pds+Ej9nP5MvTTPGll6zeFEEPxYnWz/JIkUzpHeDs2W8GD/6cU0/o+g2M7pVkrMZJCg/WQpBP/2xITORB5qzM7UxP3lFOD9QOAYzpteasykXNj8d8TM/L8jLMmQflrPLXzY/fqczP3aMhDIsZ42zF38zP4+HNj+AS+UxGRmCM3oxLr/imDu/X4ocMeVuazP2Rye/O8lBvyCLPDLMM1UzZk4gvyiYR7/Gcp0yx7pOMxXhHL+PTEq/B1jFMjoWWTNmTiC/KJhHv6j4qDKGsG8z9kcnvzvJQb9cZFMyTXeEM3oxLr/imDu/MmB3McsIkLMWfzM/kIc2P3/8tzHvA5mzy182P36nMz/MZ3IyP/adsykXNj8d8TM/31vAMkhNnbMztTE/eUU4PxFtADO2pZWzGCQoP1sKQT84wxszIbCGs2W8GD/6cU0/4sswM2afZLN6UQQ/FSdbP/7cPjOWYjmzeqXbPg8/Zz+92EYz9YYUs1v1sT77CXA/Q7hKM5CW9rJPGpE+EYF1P0/JTDNkdN6yQix4PjJeeD/gFE8zLFvYskxyaT49Qnk/qvlYMzMX57IrVmw+phZ5P9ulYDMz6fSywqZzPsmleD/WEWozRBsAs3GafD6yFng/hItyM2L6A7MrKoI+xpZ3P1LcdzPd2QWzWj6EPjpQdz9um3gzSr4FswfBgz7yYHc/QCl0M3HRA7MoVYA+9NN3P8yRajPnVwCzp6VzPtqleD9KIlwzUUn3smSyYD7Ewnk/63xJMy0/7LI8M0g+Sw97P9lGMzPhjeCyMNgqPnJpfD91RhozNmXVsv+4CT6QrH0/MeX+MiUbzLJ58c090bN+P1zIyDLX+MWyqsKPPVdefz80tZkyU9fDshXwZT2pmH8/VTqEMgAAgD8EAIA/rwGAPwAAgD8DAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8FAIA/rwGAPwAAgD8FAIA/rwGAPwAAgD8EAIA/sAGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/sAGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8FAIA/sAGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8FAIA/sAGAPwAAgD8FAIA/sAGAPwAAgD8GAIA/sQGAPwAAgD8EAIA/rwGAPwAAgD8DAIA/rgGAPwAAgD8EAIA/sAGAPwAAgD8EAIA/sAGAPwAAgD8FAIA/sAGAPwAAgD8EAIA/sAGAPwAAgD8EAIA/sAGAPwAAgD8DAIA/rgGAPwAAgD8EAIA/rgGAPwAAgD8GAIA/sQGAPwAAgD8FAIA/sAGAPwAAgD8FAIA/sAGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/sAGAPwAAgD8EAIA/sAGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8FAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8FAIA/sAGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAPwAAgD8EAIA/rwGAP1qApynmu1dATDSeM9jpHCnpu1dAzrFZNFpjnSnou1dAQBMHMeJUyynlu1dAiEB4NI23Cirlu1dAa8kcNMlZwCnmu1dARQwDNKScPynnu1dAFboDtEb/lSnqu1dAIkttNKkKnynpu1dA4HkMM+St8yjpu1dAYDMkM1yRjyjpu1dAVpgxNBrOLSnnu1dAYRs5tLDJwCnqu1dAe16RNHiLRyjlu1dA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AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA///9/PwAAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA//v9/PwAAgD8AAIA///9/PwAAgD8BAIA/AACAPwAAgD8CAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAP+QADirHN70/974HNBccCyrHN70/EbrtM6lL5inIN70/qOG5MysGJirIN70/rp1HNDH2HCnHN70/Dd3fMx9OMyrIN70/KFArNH4l0ibJN70/Y98aNDKyJ6nGN70/IDx7MuKyQCrFN70/Usjms0VD7SnFN70/hDFMNFH+XynHN70/j9WGNLv6HynHN70/ya6LtBqVYyrHN70/dceENDVADirFN70/bosxNS0nFCrEN70/vENutOIfsyrGN70/stTZNPjZKynHN70/nfEaNBnKWSrHN70/11oCNC8OLirJN70/vhHns0QBeKnJN70/M4GlNNmsLarKN70/E5w7NG1HHyvFN70/SzwjNFnbIyrHN70/3Do9tIb9yCrHN70/kRA3tKJjCCrLN70/Xx5hNXEI0SrHN70/B6pHs5ZXairHN70/5i9mtF8cCCvCN70/+B1ENO09nyrHN70/JbZkNO1ZeqrIN70/cUWktBctnynGN70/zS/2tL04kCrCN70/+B1ENCyv1CnHN70/5i9mtHh8bqrHN70/LlCbNETHkCnLN70/Xx5hNcOeFCvHN70/khA3tKFAkCnJN70/MtTjMwhaHirFN70/xJEktCZTEirKN70/Epw7NPcpFyrHN70/JhWqM+QJFirMN70/niGXNBW5hCrHN70/5YcgtAZfpyrKN70/TR7pNDtPjynHN70/Gr2psi0nFCrEN70/vENutPPjKCrIN70/2YKutD2wbCrHN70/fAI1tB4CTirFN70/PrrjtPe55SrGN70/lssSNFXHaSjIN70/6emRM74KD6rIN70/B0X0M6D/VyrHN70/ldWGNKMYFSrIN70/xf7/MvQZDyrHN70/B56BM3uNUCrHN70/uaQ3NAZs8CnGN70/WkYnNLnK7SnIN70/U5UhNLGj9SnHN70/IZdKNKfMtynJN70/aNIlM+QADirHN70/974HNJE6rj7duHA0JkkONNa4cD+ROq4+4LhwNChJDjTWuHA/kTquPuC4cDQnSQ401rhwP5E6rj7euHA0J0kONNa4cD+ROq4+4LhwNChJDjTWuHA/kTquPuG4cDQlSQ401rhwP5E6rj7guHA0J0kONNa4cD+ROq4+4bhwNCdJDjTWuHA/kTquPt64cDQnSQ401rhwP5E6rj7duHA0J0kONNa4cD+SOq4+4LhwNCdJDjTVuHA/kTquPt24cDQoSQ401rhwP5E6rj7euHA0J0kONNa4cD+ROq4+3bhwNChJDjTWuHA/kTquPuG4cDQoSQ401rhwP5E6rj7duHA0J0kONNa4cD+ROq4+3rhwNChJDjTWuHA/kTquPty4cDQnSQ401rhwP5E6rj7cuHA0JkkONNa4cD+ROq4+3rhwNCdJDjTWuHA/kTquPt64cDQoSQ401rhwP5E6rj7huHA0LEkONNa4cD+ROq4+3bhwNCdJDjTWuHA/kTquPtu4cDQoSQ401rhwP5E6rj7guHA0KEkONNa4cD+ROq4+4bhwNClJDjTWuHA/kTquPuG4cDQoSQ401rhwP5E6rj7guHA0J0kONNa4cD+ROq4+4bhwNCVJDjTWuHA/kjquPuG4cDQmSQ401rhwP5E6rj7duHA0J0kONNa4cD+ROq4+3bhwNChJDjTWuHA/kTquPt64cDQnSQ401rhwP5E6rj7duHA0KEkONNa4cD+ROq4+4bhwNChJDjTWuHA/kTquPt64cDQnSQ401rhwP5I6rj7cuHA0KUkONNa4cD+ROq4+4LhwNCdJDjTWuHA/kTquPty4cDQpSQ401rhwP5E6rj7huHA0J0kONNa4cD+ROq4+3rhwNCdJDjTWuHA/kjquPuG4cDQpSQ401bhwP5E6rj7cuHA0J0kONNa4cD+ROq4+3LhwNCdJDjTWuHA/kTquPuG4cDQoSQ401rhwP5E6rj7cuHA0J0kONNa4cD+ROq4+4bhwNClJDjTWuHA/kjquPuC4cDQoSQ401bhwP5E6rj7buHA0J0kONNa4cD+ROq4+3bhwNCZJDjTWuHA/kjquPtu4cDQmSQ401bhwP5E6rj7auHA0JkkONNa4cD+ROq4+4rhwNCdJDjTWuHA/kjquPty4cDQpSQ401rhwP5E6rj7cuHA0KEkONNa4cD+ROq4+4LhwNCdJDjTWuHA/kTquPt24cDQoSQ401rhwP5E6rj7euHA0JkkONNa4cD+SOq4+3rhwNCdJDjTVuHA/kTquPt24cDQmSQ401rhwPwAAgD8BAIA/AACAPwAAgD8AAIA///9/PwAAgD8CAIA/AACAPwAAgD8CAIA/AACAPwAAgD8BAIA//v9/PwAAgD8BAIA///9/PwAAgD8BAIA///9/PwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA///9/PwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8AAIA//v9/PwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AQCAPwAAgD8BAIA/AACAPwAAgD8BAIA//f9/PwAAgD8BAIA///9/PwAAgD8BAIA///9/PwAAgD8BAIA///9/PwAAgD8BAIA/AACAPwAAgD8CAIA/AACAPwAAgD8BAIA///9/PwAAgD8CAIA///9/PwAAgD8BAIA//v9/PwAAgD8BAIA///9/PwAAgD8CAIA/AACAPwAAgD8AAIA//v9/PwAAgD8BAIA//v9/PwAAgD8CAIA///9/PwAAgD8CAIA/AQCAPwAAgD8CAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA///9/PwAAgD8BAIA//v9/PwAAgD8BAIA///9/PwAAgD8BAIA///9/PwAAgD8BAIA/AACAPwAAgD8AAIA///9/PwAAgD8BAIA/AACAPwAAgD8AAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8BAIA/AACAPwAAgD8CAIA/AACAPwAAgD8BAIA/AACAPwAAgD8CAIA/AACAPwAAgD8CAIA/AACAPwAAgD8CAIA/AACAPwAAgD8BAIA/AACAPwAAgD8CAIA/AQCAPwAAgD8CAIA/AACAPwAAgD8BAIA///9/PwAAgD8BAIA//v9/PwAAgD8BAIA/AACAPwAAgD8CAIA/AACAPwAAgD8BAIA/AACAPw==" + } + ] +} diff --git a/Assets/Models/BoneIKTest4.shmodel b/Assets/Models/BoneIKTest4.shmodel new file mode 100644 index 0000000000000000000000000000000000000000..9443ea3d6e1268b24a05dfe256d13465b5bdfa65 GIT binary patch literal 29138 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 bQCUSDIpCuvWc8a@_|}!6Y`l!Ug4h26wCcl_ literal 0 HcmV?d00001 diff --git a/Assets/Models/BoneIKTest4.shmodel.shmeta b/Assets/Models/BoneIKTest4.shmodel.shmeta new file mode 100644 index 00000000..e1603435 --- /dev/null +++ b/Assets/Models/BoneIKTest4.shmodel.shmeta @@ -0,0 +1,7 @@ +Name: BoneIKTest4 +ID: 81814706 +Type: 4 +Sub Assets: +Name: Cube +ID: 137599708 +Type: 8 diff --git a/Assets/Models/racoon.shmodel b/Assets/Models/racoon.shmodel index 3fa9d9bd9e5f0adff8ab6f8cbb8a6fade7883247..816f6057de53868a6b64da641068c4486cf08da7 100644 GIT binary patch delta 4494 zcmZwKdx#rV6vy$iwzb=pA|eqHkswm+AXt$gBC^rCAd83;??pspMMO*y5eXI%XSCK` z*JsuEe#H7(?^Ud>>sqVs_@u6`@A3UuUw;_z525+TKg4q3gFE@Su&L^ZGauEu3oM6sf z2@|;n=A!FiI=A{Q&f}Nl=6=l0yW3zU+YZzD0rSy=Fk>EtNfPe>>g0l%%)vxnfVpTE zrV|q;$?lHz@(=Mlw}3*My#v!(4fD}QFk?Q2N%CdE;&5YK5(NxqvL9d~zrcL-2TWHJ zOp?D~&ix$&ZPteAQiQqa-~X|pG5zxkg-Kmq0n=sNwQl}-3boZJlw=LeG!BA!w-zSp z!7!5@QnWaXd+X35s_6y-v!%mfJ~{#>)gxiL`M_MQJQl{k7*}#M3T@*UnC|m1Nson@ zEQ0ygaXstgAL7?tuZQ{Y1v8}+U@kflCe;c|cTAYCojhQ%-Z(jB0QJU=Q(?N@!X!Nn zX0wej-x`DIRz0>?$G;H2DUGAhM`yvLdNxe=d6=thf=P1jKFjkDQQL;yhavx9dPu+| zJs)PW3t+BwAxzJgL5oLi>Eb~g_0c6Tsa^`x!wKeVn_-e%4l|7_)Z$|Nda|KV(yL%L ztHXTjYM7piFjKmAzlHom)GoSiKXy&Z2~3Yvn5oHO8@~~z{ogjAOxy(XwMm$* zPr>vkhxzGgm}%S+tl0la^HvmU9{@9Rx5Fg81LhX*glP}aXPSSAUzYFc!|sr@?uOa+ zJuq!RV1DF2m@VB8leqiccQU{s>GvADB!$mRr95d+XiRnkY2&$6=D} zfVt@>V7BojOqx&iTc`iima8B0A+dAMz$Bf4`Nd~p+OEN5*~5G*ehz5b&%?CWgSnBN zFjIOFCgqo4+9!6bmw%Xqw(>Fxr8)=m>W z-y>jV?pv6o-}S7Me~8~Ne&55eALV<1zJ$PJ`A3**{RA`ZpJDm}GhmxE>XBauP?uuq YH<*-vhw0l7Y;wcw@cf3^HCtx?1EA<&SO5S3 delta 4486 zcmb`FJ4{qT6o&t+uA(#+R5T=7D1|0ksD+ZyP*H3{VJNi`BPghV3cGVq7ZntIfDcsg zL5LQTYz#pYZHNU4fk<_liJJIGlmrWH)Wo~Xvb%Th%$?cCO(u8dzvq14oa|1_RZq-S zPevWb=@Cv$IEl5!U2EO7&RrpQt#{XkDdChwMkD{+XN*2Gj)kHq(KAjT7|m9^h?Vj+ zeP^^-0j+qGG{iWb;Mw_&(3dpGI8nd_b^cizVYG(&!qdw~mmJssKK0JJN+RBzI=_no ziJx*{VSJ@Nl_#>qcAjF4Hva{6{#3jEHABQW=?CgNlwUG_rhdk$qJe3Pq+Z5p6EEXW zH|jQv6lb)XfVysf{*B%+A|0gzF3~ijGe2J7{GaFv<4k@)|D2iRE4^Tx_3#4ynNH2_ zFFj+N^8huKx;V%GlNuOZ#Q?pc-xOwa2k>(I-fqRIoEqIfO#o2Q6BI>2b&P1~fFY`7 z2y32s{z|H6kTo!9*yOh=YG?GU^2~lCu!33{=U0Koa&LjkUrk+%3k3qb_~dCPbuccf zc`CmG;f=P^Ax2CM^i~>AEB<}d%;+r%FljtMM;MoEc$V|;rV7So8=!IGWWiplVq9@~ zmVP6(o+=rA`Cm}$ucZ*9KYyU^rga3yRUc35*KyK$Bb76*`GD!~cAG4JnD#IRiUnr0 z4YZ3fXy9e}Go6}U6YXbQHvl!gsJdM7|sUjDm8hozgI|!+eH9#JpF5tu{} z6Y~=HN(RJ;n3Wi@D1J%Yw*>}HoBTK=mL(pfcou#m@wxaT@vyK9D*dD4i^OPQ zK-F!ZijNXwN}kfMVtBGw;)lc|CD2o6KGdz0ycNR|j|&G5FC5%d8LNqxP1VFVHpTw~ Dc5wQ* diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index bd6228bd..7a397a4c 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -23,6 +23,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/SHVkUtil.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" #include "ECS_Base/Managers/SHSystemManager.h" +#include "Tools/SHDebugDraw.h" namespace SHADE { @@ -113,7 +114,7 @@ namespace SHADE return; // Update time on the playback - currPlaybackTime += dt; + currPlaybackTime += dt * 0.01f; if (currPlaybackTime > currClip->GetTotalTime()) { currPlaybackTime = currPlaybackTime - currClip->GetTotalTime(); @@ -139,7 +140,7 @@ namespace SHADE updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, rig->GetRootNode(), SHMatrix::Identity); } - void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix) + void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix, std::optional worldPos) { // Check if there is a channel for this node const std::string& BONE_NAME = rig->GetName(node); @@ -160,15 +161,20 @@ namespace SHADE // Apply transformations to this node const int BONE_MTX_IDX = rig->GetNodeIndex(node); + std::optional position; if (BONE_MTX_IDX >= 0) { - boneMatrices[BONE_MTX_IDX] = node->OffsetMatrix * transformMatrix * rig->GetGlobalInverseMatrix(); + boneMatrices[BONE_MTX_IDX] = node->OffsetMatrix * transformMatrix; + position = SHVec3(boneMatrices[BONE_MTX_IDX]._41, boneMatrices[BONE_MTX_IDX]._42, boneMatrices[BONE_MTX_IDX]._43); + SHDebugDraw::Cube(position.value(), SHQuaternion{}, SHVec3(0.01f, 0.01f, 0.01f), SHColour::ORANGE); + if (worldPos.has_value()) + SHDebugDraw::Line(position.value(), worldPos.value(), SHColour::ORANGE); } // Apply pose to children for (auto& child : node->Children) { - updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix); + updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix, position); } } } diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index b47106f8..95123e8d 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -141,7 +141,7 @@ namespace SHADE /* Helper Functions */ /*---------------------------------------------------------------------------------*/ void updatePoseWithClip(float poseTime); - void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix); + void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix, std::optional worldPos = {}); template T getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 306c9a00..87ae3bff 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -36,7 +36,7 @@ namespace SHADE rootNode = recurseCreateNode(asset, asset.root); if (rootNode) { - globalInverseMatrix = rootNode->OffsetMatrix; + globalInverseMatrix = SHMatrix::Inverse(rootNode->TransformMatrix); } } From bde191aeca68eae05e6bae9e6374a8facab30b51 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Wed, 25 Jan 2023 23:22:15 +0800 Subject: [PATCH 147/164] Input Bindings Panel Done --- Assets/Bindings.SHConfig | 95 +++ .../InputBindings/SHInputBindingsPanel.cpp | 541 ++++++++++++++---- SHADE_Engine/src/Input/SHInputManager.cpp | 50 +- SHADE_Engine/src/Input/SHInputManager.h | 11 +- 4 files changed, 550 insertions(+), 147 deletions(-) diff --git a/Assets/Bindings.SHConfig b/Assets/Bindings.SHConfig index 573541ac..bdc254b5 100644 --- a/Assets/Bindings.SHConfig +++ b/Assets/Bindings.SHConfig @@ -1 +1,96 @@ +7 +Controller Look Horizontal 0 +0 +5 +0.2 +5 +0 +0 +0 +1 +18 +0 +Controller Look Vertical +0 +0 +5 +0.2 +5 +0 +0 +0 +1 +19 +0 +Horizontal +0 +0 +5 +0.2 +5 +1 +2 +39 +68 +2 +37 +65 +2 +3 +16 +1 +2 +Jump +0 +0 +1000 +0.2 +1000 +0 +1 +32 +0 +1 +10 +0 +Mouse Look Horizontal +1 +0 +1 +0.2 +1 +0 +0 +0 +0 +0 +Mouse Look Vertical +2 +0 +1 +0.2 +1 +0 +0 +0 +0 +0 +Vertical +0 +0 +5 +0.2 +5 +1 +2 +38 +87 +2 +40 +83 +2 +0 +17 +1 +1 diff --git a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp index ca9e263f..c304c1b0 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp @@ -5,8 +5,29 @@ namespace SHADE { + //Vectors containing data for elements for different bindings static std::vector bindingRenames; - static std::vector positiveKeyListening; + + //Flags to prevent unwanted editing of bindings + static size_t positiveKeyListeningFor; + static bool positiveKeyListening; + + static size_t negativeKeyListeningFor; + static bool negativeKeyListening; + + static size_t positiveControllerListeningFor; + static bool positiveControllerListening; + + static size_t negativeControllerListeningFor; + static bool negativeControllerListening; + + //Internal Helper function + void resizeVectors(size_t newSize) + { + bindingRenames.resize(newSize); + for (auto& s : bindingRenames) + s.clear(); + } //Internal Helper function std::string labelConcat(char const* label, size_t entryNumber) @@ -25,17 +46,63 @@ namespace SHADE { if (SHEditorWindow::Begin()) { + //ImGui::ShowDemoWindow(); + //Binding count ImGui::Text("Binding Count: %d", SHInputManager::CountBindings()); + //Binding file name + static std::string bindingFileName; + ImGui::InputText("Binding File Path", &bindingFileName); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text(".SHConfig will be appeneded to file name"); + ImGui::Text("If no name is provided, saves to \"Assets/Bindings.SHConfig\""); + ImGui::EndTooltip(); + } + + //Save bindings to... + if (ImGui::Button("Save Bindings")) + { + if (bindingFileName.empty()) + { + SHInputManager::SaveBindings(); + } + else + { + std::string filePath = std::string(ASSET_ROOT); + filePath += "/"; + filePath += bindingFileName; + filePath += ".SHConfig"; + SHInputManager::SaveBindings(filePath); + } + } + + //Load bindings from... + if (ImGui::Button("Load Bindings")) + { + if (bindingFileName.empty()) + { + SHInputManager::LoadBindings(); + } + else + { + std::string filePath = std::string(ASSET_ROOT); + filePath += "/"; + filePath += bindingFileName; + filePath += ".SHConfig"; + SHInputManager::LoadBindings(filePath); + } + resizeVectors(SHInputManager::CountBindings()); + } + //Button to add new binding if (ImGui::Button("Add New Binding")) { std::string newBindingName = "Binding" + std::to_string(SHInputManager::CountBindings()); SHInputManager::AddBinding(newBindingName); - bindingRenames.resize(SHInputManager::CountBindings()); - for (auto& s : bindingRenames) - s.clear(); + resizeVectors(SHInputManager::CountBindings()); } if (ImGui::IsItemHovered()) { @@ -50,144 +117,378 @@ namespace SHADE //Listing for each binding for (auto& binding : SHInputManager::GetBindings()) { - ImGui::Separator(); - - ImGui::Text("Entry Number %d", entryNumber); - - //Non-modifiable binding name - ImGui::Text("Binding Name: %s", binding.first); - ImGui::InputText(labelConcat("##bindingModifyName", entryNumber).c_str(), &bindingRenames[entryNumber]); - ImGui::SameLine(); - if (ImGui::Button(labelConcat("Rename##bindingRename", entryNumber).c_str())) + if (ImGui::CollapsingHeader(binding.first.c_str())) { - SHInputManager::RenameBinding(binding.first, bindingRenames[entryNumber]); - bindingRenames[entryNumber].clear(); - } + //Modifiable binding name + ImGui::Text("Binding Name: %s", binding.first.c_str()); + ImGui::InputText(labelConcat("##bindingModifyName", entryNumber).c_str(), &bindingRenames[entryNumber]); + ImGui::SameLine(); + if (ImGui::Button(labelConcat("Rename##bindingRename", entryNumber).c_str())) + { + SHInputManager::RenameBinding(binding.first, bindingRenames[entryNumber]); + bindingRenames[entryNumber].clear(); + } - //Modifiable binding name - /*size_t constexpr maxLen = 256; //Maximum binding name length - static char bindingNameCStr[maxLen]; - std::copy(binding.first.begin(), - binding.first.length() >= maxLen ? - binding.first.begin() + maxLen : - binding.first.end(), //Does not take into account null terminator - bindingNameCStr); - bindingNameCStr[binding.first.length()] = '\0'; //Null terminator - - //First character - char firstChar = bindingNameCStr[0]; - //If the input text is modified, modify the binding's name - if (ImGui::InputText("Binding Name", bindingNameCStr, maxLen)) - { - //Ensure name does not get shorter than 1 character long - if (std::strlen(bindingNameCStr) < 1) - bindingNameCStr[0] = firstChar; - //Rename binding in the input manager - SHInputManager::RenameBinding(binding.first, std::string{ bindingNameCStr }); - binding.first = std::string{ bindingNameCStr }; - } - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::Text("The name of this binding"); - ImGui::EndTooltip(); - }*/ - - //Binding value test - //TODO - ImGui::Text("Value"); - - //Binding Type Combo Box - int bindingType = static_cast(SHInputManager::GetBindingType(binding.first)); - if (ImGui::Combo(labelConcat("Input Type##", entryNumber).c_str(), &bindingType, "Keyboard / Mouse Buttons / Controller\0Mouse Horizontal\0Mouse Vertical\0Mouse Scroll Wheel")) - SHInputManager::SetBindingType(binding.first, static_cast(bindingType)); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::Text("Which of the four types the binding uses"); - ImGui::Text("Keyboard / Mouse Buttons / Controller = Keys, mouse buttons and ALL controller inputs"); - ImGui::Text("Mouse Horizontal = Horizontal movement of the mouse"); - ImGui::Text("Mouse Vertical = Vertical movement of the mouse"); - ImGui::Text("Mouse Scroll Wheel = The scroll wheel found at the middle of most mouses"); - ImGui::EndTooltip(); - } - - //Inversion - bool bindingInvert = SHInputManager::GetBindingInverted(binding.first); - if (ImGui::Checkbox(labelConcat("Inverted##", entryNumber).c_str(), &bindingInvert)) - SHInputManager::SetBindingInverted(binding.first, bindingInvert); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::Text("If inverted:"); - ImGui::Text("Positive inputs mean negative value of the binding"); - ImGui::Text("Negative inputs mean positive value of the binding"); - ImGui::Text("Mouse moving up / right means negative value of the binding"); - ImGui::Text("Scrolling the mouse wheel up means negative value of the binding"); - ImGui::EndTooltip(); - } - - //Sensitivity - double bindingSensitivity = SHInputManager::GetBindingSensitivity(binding.first); - if (ImGui::InputDouble(labelConcat("Sensitivity##", entryNumber).c_str(), &bindingSensitivity)) - SHInputManager::SetBindingSensitivity(binding.first, bindingSensitivity); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::Text("Value multiplier for mouse movement and scrolling"); - ImGui::Text("For other digital inputs, serves as a rate of how fast axis value goes to maximum positive/negative"); - ImGui::Text("For other analog inputs, serves as a multiplier, but axis value magnitude will still be capped at 1"); - ImGui::EndTooltip(); - } - - //Below this section is only for KB/M type bindings - //Not relevant for mouse movement and scrolling - if (SHInputManager::GetBindingType(binding.first) == SHInputManager::SH_BINDINGTYPE::KB_MB_CONTROLLER) - { - //Dead - float bindingDead = static_cast(SHInputManager::GetBindingDead(binding.first)); - if (ImGui::SliderFloat(labelConcat("Deadzone##", entryNumber).c_str(), &bindingDead, 0.0f, 1.0f)) - SHInputManager::SetBindingDead(binding.first, static_cast(bindingDead)); + if (ImGui::Button(labelConcat("Delete Binding##", entryNumber).c_str())) + { + SHInputManager::RemoveBinding(binding.first); + resizeVectors(SHInputManager::CountBindings()); + ImGui::End(); + return; + } if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text("Any positive or negative analog input with magnitude less than this will be registered as neutral"); + ImGui::Text("Delete this binding from the list"); ImGui::EndTooltip(); } - //Gravity - double bindingGravity = SHInputManager::GetBindingGravity(binding.first); - if (ImGui::InputDouble(labelConcat("Gravity##", entryNumber).c_str(), &bindingGravity)) - SHInputManager::SetBindingGravity(binding.first, static_cast(bindingGravity)); + //Binding value test + ImGui::BeginDisabled(); + float val = SHInputManager::GetBindingAxis(binding.first); + ImGui::SliderFloat(labelConcat("Value##", entryNumber).c_str(), &val, -1.0f, 1.0f); + ImGui::EndDisabled(); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text("The rate at which the value moves to neutral if no input in the direction is read"); - ImGui::TextColored(ImVec4{ 1.0f, 0.5f, 0.5f, 1.0f }, "Should be non-negative"); + ImGui::Text("Test the current value of the binding"); + ImGui::Text("For mouse movement/wheel inputs, will be multiplied by Sensitivity, with 0 being neutral (no input detected)"); + ImGui::Text("Between -1 and 1 for other inputs, with 0 still being neutral (no input detected)"); ImGui::EndTooltip(); } - - //Snap - bool bindingSnap = SHInputManager::GetBindingSnap(binding.first); - if (ImGui::Checkbox(labelConcat("Snap##", entryNumber).c_str(), &bindingSnap)) - SHInputManager::SetBindingSnap(binding.first, bindingSnap); + ImGui::BeginDisabled(); + float rawVal = SHInputManager::GetBindingAxisRaw(binding.first); + ImGui::SliderFloat(labelConcat("Raw Value##", entryNumber).c_str(), &rawVal, -1.0f, 1.0f); + ImGui::EndDisabled(); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text("If no other input on the axis is present and a input is made in the opposite direction of the current value,"); - ImGui::Text("the binding's value will jump to neutral 0 before resuming in the input direction"); + ImGui::Text("Test the current value of the binding"); + ImGui::Text("Raw value means it will be fixed among -1, 0 and 1 for non-mouse movement/wheel inputs"); + ImGui::Text("No difference between this and Value for mouse movement/wheel inputs"); + ImGui::Text("But for other inputs, does not consider smoothing options such as gravity and sensitivity"); + ImGui::Text("If both positive and negative input is detected, returns neutral 0"); ImGui::EndTooltip(); } - - //Positive key codes - //Negative key codes + //Binding Type Combo Box + int bindingType = static_cast(SHInputManager::GetBindingType(binding.first)); + if (ImGui::Combo(labelConcat("Input Type##", entryNumber).c_str(), &bindingType, "Keyboard / Mouse Buttons / Controller\0Mouse Horizontal\0Mouse Vertical\0Mouse Scroll Wheel")) + SHInputManager::SetBindingType(binding.first, static_cast(bindingType)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Which of the four types the binding uses"); + ImGui::Text("Keyboard / Mouse Buttons / Controller = Keys, mouse buttons and ALL controller inputs"); + ImGui::Text("Mouse Horizontal = Horizontal movement of the mouse"); + ImGui::Text("Mouse Vertical = Vertical movement of the mouse"); + ImGui::Text("Mouse Scroll Wheel = The scroll wheel found at the middle of most mouses"); + ImGui::EndTooltip(); + } - //Positive controller codes + //Inversion + bool bindingInvert = SHInputManager::GetBindingInverted(binding.first); + if (ImGui::Checkbox(labelConcat("Inverted##", entryNumber).c_str(), &bindingInvert)) + SHInputManager::SetBindingInverted(binding.first, bindingInvert); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("If inverted:"); + ImGui::Text("Positive inputs mean negative value of the binding"); + ImGui::Text("Negative inputs mean positive value of the binding"); + ImGui::Text("Mouse moving up / right means negative value of the binding"); + ImGui::Text("Scrolling the mouse wheel up means negative value of the binding"); + ImGui::EndTooltip(); + } - //Negative controller codes + //Sensitivity + double bindingSensitivity = SHInputManager::GetBindingSensitivity(binding.first); + if (ImGui::InputDouble(labelConcat("Sensitivity##", entryNumber).c_str(), &bindingSensitivity)) + SHInputManager::SetBindingSensitivity(binding.first, bindingSensitivity); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Value multiplier for mouse movement and scrolling"); + ImGui::Text("For other digital inputs, serves as a rate of how fast axis value goes to maximum positive/negative"); + //ImGui::Text("For other analog inputs, serves as a multiplier, but axis value magnitude will still be capped at 1"); + ImGui::Text("Irrelevant for other analog inputs"); + ImGui::EndTooltip(); + } + + //Below this section is only for KB/M type bindings + //Not relevant for mouse movement and scrolling + if (SHInputManager::GetBindingType(binding.first) == SHInputManager::SH_BINDINGTYPE::KB_MB_CONTROLLER) + { + //Dead + float bindingDead = static_cast(SHInputManager::GetBindingDead(binding.first)); + if (ImGui::SliderFloat(labelConcat("Deadzone##", entryNumber).c_str(), &bindingDead, 0.0f, 1.0f)) + SHInputManager::SetBindingDead(binding.first, static_cast(bindingDead)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Any positive or negative analog input with magnitude less than this will be registered as neutral"); + ImGui::EndTooltip(); + } + + //Gravity + double bindingGravity = SHInputManager::GetBindingGravity(binding.first); + if (ImGui::InputDouble(labelConcat("Gravity##", entryNumber).c_str(), &bindingGravity)) + SHInputManager::SetBindingGravity(binding.first, static_cast(bindingGravity)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("The rate at which the value moves to neutral if no input in the direction is read"); + ImGui::TextColored(ImVec4{ 1.0f, 0.5f, 0.5f, 1.0f }, "Should be non-negative"); + ImGui::EndTooltip(); + } + + //Snap + bool bindingSnap = SHInputManager::GetBindingSnap(binding.first); + if (ImGui::Checkbox(labelConcat("Snap##", entryNumber).c_str(), &bindingSnap)) + SHInputManager::SetBindingSnap(binding.first, bindingSnap); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("If no other input on the axis is present and a input is made in the opposite direction of the current value,"); + ImGui::Text("the binding's value will jump to neutral 0 before resuming in the input direction"); + ImGui::EndTooltip(); + } + + size_t keycodeIndex = 0; + //Positive key codes + ImGui::Separator(); + ImGui::Text("Positive Key Codes:"); + + ImGui::SameLine(); + //Button to ask for inputs + if (!positiveKeyListening) + { + if (ImGui::Button(labelConcat("New##positiveKeyCode", entryNumber).c_str())) + { + positiveKeyListening = true; + positiveKeyListeningFor = entryNumber; + } + } + else + { + if (positiveKeyListeningFor == entryNumber) + { + //Listening for inputs + ImGui::Button(labelConcat("Press##positiveKeyCode", entryNumber).c_str()); + + SHInputManager::SH_KEYCODE k; + if (SHInputManager::AnyKey(&k)) + { + positiveKeyListening = false; + SHInputManager::AddBindingPositiveKeyCode(binding.first, k); + } + } + else + { + //Not listening + ImGui::BeginDisabled(); + ImGui::Button(labelConcat("New##positiveKeyCode", entryNumber).c_str()); + ImGui::EndDisabled(); + } + } + + //List and remove bindings + ImGui::Indent(); + keycodeIndex = 0; + for (auto& k : binding.second.positiveKeyCodes) + { + //ImGui::Text("%d", static_cast(k)); + ImGui::Text(SHInputManager::GetKeyCodeName(k).c_str()); + ImGui::SameLine(); + std::string labelString = "X##KeyPositive"; + labelString += binding.first; + + //Delete button + if (ImGui::SmallButton(labelConcat(labelString.c_str(), keycodeIndex).c_str())) + { + SHInputManager::RemoveBindingPositiveKeyCode(binding.first, k); + break; + } + ++keycodeIndex; + } + ImGui::Unindent(); + + + //Negative key codes + ImGui::Separator(); + ImGui::Text("Negative Key Codes:"); + + ImGui::SameLine(); + //Button to ask for inputs + if (!negativeKeyListening) + { + if (ImGui::Button(labelConcat("New##negativeKeyCode", entryNumber).c_str())) + { + negativeKeyListening = true; + negativeKeyListeningFor = entryNumber; + } + } + else + { + if (negativeKeyListeningFor == entryNumber) + { + //Listening for inputs + ImGui::Button(labelConcat("Press##negativeKeyCode", entryNumber).c_str()); + + SHInputManager::SH_KEYCODE k; + if (SHInputManager::AnyKey(&k)) + { + negativeKeyListening = false; + SHInputManager::AddBindingNegativeKeyCode(binding.first, k); + } + } + else + { + //Not listening + ImGui::BeginDisabled(); + ImGui::Button(labelConcat("New##negativeKeyCode", entryNumber).c_str()); + ImGui::EndDisabled(); + } + } + + //List and remove bindings + ImGui::Indent(); + keycodeIndex = 0; + for (auto& k : binding.second.negativeKeyCodes) + { + //ImGui::Text("%d", static_cast(k)); + ImGui::Text(SHInputManager::GetKeyCodeName(k).c_str()); + ImGui::SameLine(); + std::string labelString = "X##KeyNegative"; + labelString += binding.first; + + //Delete button + if (ImGui::SmallButton(labelConcat(labelString.c_str(), keycodeIndex).c_str())) + { + SHInputManager::RemoveBindingNegativeKeyCode(binding.first, k); + break; + } + ++keycodeIndex; + } + ImGui::Unindent(); + + //Positive controller codes + ImGui::Separator(); + ImGui::Text("Positive Controller Codes:"); + + ImGui::SameLine(); + //Button to ask for inputs + if (!positiveControllerListening) + { + if (ImGui::Button(labelConcat("New##positiveControllerCode", entryNumber).c_str())) + { + positiveControllerListening = true; + positiveControllerListeningFor = entryNumber; + } + } + else + { + if (positiveControllerListeningFor == entryNumber) + { + //Listening for inputs + ImGui::Button(labelConcat("Press##positiveControllerCode", entryNumber).c_str()); + + SHInputManager::SH_CONTROLLERCODE c; + if (SHInputManager::AnyControllerInput(&c)) + { + positiveControllerListening = false; + SHInputManager::AddBindingPositiveControllerCode(binding.first, c); + } + } + else + { + //Not listening + ImGui::BeginDisabled(); + ImGui::Button(labelConcat("New##positiveControllerCode", entryNumber).c_str()); + ImGui::EndDisabled(); + } + } + + //List and remove bindings + ImGui::Indent(); + keycodeIndex = 0; + for (auto& c : binding.second.positiveControllerCodes) + { + //ImGui::Text("%d", static_cast(k)); + ImGui::Text(SHInputManager::GetControllerCodeName(c).c_str()); + ImGui::SameLine(); + std::string labelString = "X##ControllerPositive"; + labelString += binding.first; + + //Delete button + if (ImGui::SmallButton(labelConcat(labelString.c_str(), keycodeIndex).c_str())) + { + SHInputManager::RemoveBindingPositiveControllerCode(binding.first, c); + break; + } + ++keycodeIndex; + } + ImGui::Unindent(); + + //Negative controller codes + ImGui::Separator(); + ImGui::Text("Negative Controller Codes:"); + + ImGui::SameLine(); + //Button to ask for inputs + if (!negativeControllerListening) + { + if (ImGui::Button(labelConcat("New##negativeControllerCode", entryNumber).c_str())) + { + negativeControllerListening = true; + negativeControllerListeningFor = entryNumber; + } + } + else + { + if (negativeControllerListeningFor == entryNumber) + { + //Listening for inputs + ImGui::Button(labelConcat("Press##negativeControllerCode", entryNumber).c_str()); + + SHInputManager::SH_CONTROLLERCODE c; + if (SHInputManager::AnyControllerInput(&c)) + { + negativeControllerListening = false; + SHInputManager::AddBindingNegativeControllerCode(binding.first, c); + } + } + else + { + //Not listening + ImGui::BeginDisabled(); + ImGui::Button(labelConcat("New##negativeControllerCode", entryNumber).c_str()); + ImGui::EndDisabled(); + } + } + + //List and remove bindings + ImGui::Indent(); + keycodeIndex = 0; + for (auto& c : binding.second.negativeControllerCodes) + { + //ImGui::Text("%d", static_cast(k)); + ImGui::Text(SHInputManager::GetControllerCodeName(c).c_str()); + ImGui::SameLine(); + std::string labelString = "X##ControllerNegative"; + labelString += binding.first; + + //Delete button + if (ImGui::SmallButton(labelConcat(labelString.c_str(), keycodeIndex).c_str())) + { + SHInputManager::RemoveBindingNegativeControllerCode(binding.first, c); + break; + } + ++keycodeIndex; + } + ImGui::Unindent(); + } } - ++entryNumber; //Next entry } } diff --git a/SHADE_Engine/src/Input/SHInputManager.cpp b/SHADE_Engine/src/Input/SHInputManager.cpp index 87ea3aa5..2f0ab6d6 100644 --- a/SHADE_Engine/src/Input/SHInputManager.cpp +++ b/SHADE_Engine/src/Input/SHInputManager.cpp @@ -100,7 +100,7 @@ namespace SHADE } } - std::string SHInputManager::getKeyCodeName(SH_KEYCODE k) noexcept + std::string SHInputManager::GetKeyCodeName(SH_KEYCODE k) noexcept { int kInt = static_cast(k); //Numbers @@ -116,7 +116,7 @@ namespace SHADE //Numpads if (kInt >= static_cast(SH_KEYCODE::NUMPAD_0) && kInt <= static_cast(SH_KEYCODE::NUMPAD_9)) { - return "Keypad " + std::to_string(kInt - 96); + return "Numpad " + std::to_string(kInt - 96); } //Function keys if (kInt >= static_cast(SH_KEYCODE::F1) && kInt <= static_cast(SH_KEYCODE::F24)) @@ -266,22 +266,22 @@ namespace SHADE return "Sleep"; break; case SH_KEYCODE::MULTIPLY: - return "Multiply"; + return "Numpad *"; break; case SH_KEYCODE::ADD: - return "Add"; + return "Numpad +"; break; case SH_KEYCODE::SEPARATOR: return "Separator"; break; case SH_KEYCODE::SUBTRACT: - return "Subtract"; + return "Numpad -"; break; case SH_KEYCODE::DECIMAL: - return "Decimal"; + return "Numpad ."; break; case SH_KEYCODE::DIVIDE: - return "Divide"; + return "Numpad /"; break; case SH_KEYCODE::NUM_LOCK: return "Num Lock"; @@ -380,10 +380,16 @@ namespace SHADE return "; or :"; break; case SH_KEYCODE::OEM_PLUS: - return "+"; + return "= or +"; break; case SH_KEYCODE::OEM_COMMA: - return ","; + return ", or <"; + break; + case SH_KEYCODE::OEM_MINUS: + return "- or _"; + break; + case SH_KEYCODE::OEM_PERIOD: + return ". or >"; break; case SH_KEYCODE::OEM_2: return "/ or ?"; @@ -496,7 +502,7 @@ namespace SHADE } } - std::string SHInputManager::getControllerCodeName(SH_CONTROLLERCODE c) noexcept + std::string SHInputManager::GetControllerCodeName(SH_CONTROLLERCODE c) noexcept { switch (c) { @@ -1143,7 +1149,7 @@ namespace SHADE digitalInput = true; } if (std::abs(newValue) > std::abs(largestMagnitude)) - largestMagnitude = newValue * data.sensitivity * (data.inverted ? -1.0 : 1.0); + largestMagnitude = newValue * (data.inverted ? -1.0 : 1.0); } } @@ -1159,7 +1165,7 @@ namespace SHADE digitalInput = true; } if (std::abs(newValue) > std::abs(largestMagnitude)) - largestMagnitude = -newValue * data.sensitivity * (data.inverted ? -1.0 : 1.0); + largestMagnitude = -newValue * (data.inverted ? -1.0 : 1.0); } } @@ -1203,21 +1209,23 @@ namespace SHADE if (data.value > 1.0) data.value = 1.0; else if (data.value < -1.0) data.value = -1.0; } - //If analog input was in, - //sensitivity is instead used as a multiplier + //If analog input was in, raw value taken else { - data.value = largestMagnitude * data.sensitivity; + data.value = largestMagnitude; if (data.value > 1.0) data.value = 1.0; else if (data.value < -1.0) data.value = -1.0; } if (data.snap) //Snapping { - if (data.value > 0.0 && data.negativeInputHeld) - data.value = 0.0; - if (data.value < 0.0 && data.positiveInputHeld) - data.value = 0.0; + if (digitalInput) //Only for digital inputs + { + if (data.value > 0.0 && data.negativeInputHeld) + data.value = 0.0; + if (data.value < 0.0 && data.positiveInputHeld) + data.value = 0.0; + } } } } @@ -1399,11 +1407,11 @@ namespace SHADE { return 0.0; } - else if (bindings[bindingName].positiveInputHeld) + else if (GetBindingAxis(bindingName, cNum) > 0.0) { return 1.0; } - else if (bindings[bindingName].negativeInputHeld) + else if (GetBindingAxis(bindingName, cNum) < 0.0) { return -1.0; } diff --git a/SHADE_Engine/src/Input/SHInputManager.h b/SHADE_Engine/src/Input/SHInputManager.h index 52322f0c..1bcafa7d 100644 --- a/SHADE_Engine/src/Input/SHInputManager.h +++ b/SHADE_Engine/src/Input/SHInputManager.h @@ -363,6 +363,7 @@ namespace SHADE //Speed in units per second that the axis will move toward target value for digital //For mouse movement / scrolling, serves as multiplier + //Irrelevant for other analog inputs double sensitivity = 1.0; //If enabled, axis value will reset to zero when pressing a button @@ -416,10 +417,10 @@ namespace SHADE } //Get the name of key code - static std::string getKeyCodeName(SH_KEYCODE const keyCode) noexcept; + static std::string GetKeyCodeName(SH_KEYCODE const keyCode) noexcept; //Get the name of controller code - static std::string getControllerCodeName(SH_CONTROLLERCODE const controllerCode) noexcept; + static std::string GetControllerCodeName(SH_CONTROLLERCODE const controllerCode) noexcept; //For testing purposes //static void PrintCurrentState() noexcept; @@ -825,7 +826,7 @@ namespace SHADE //Get the sensitivity of the binding //Serves as a multiplier for mouse movement/scrolling //For other digital inputs, serves as a rate of how fast axis value goes to maximum positive/negative - //For other analog inputs, serves as a multiplier, but axis value magnitude will still be capped at 1 + //Irrelevant for other analog inputs static inline double GetBindingSensitivity(std::string const& bindingName) { return bindings[bindingName].sensitivity; @@ -834,7 +835,7 @@ namespace SHADE //Set the sensitivity of the binding //Serves as a multiplier for mouse movement/scrolling //For other digital inputs, serves as a rate of how fast axis value goes to maximum positive/negative - //For other analog inputs, serves as a multiplier, but axis value magnitude will still be capped at 1 + //Irrelevant for other analog inputs static inline void SetBindingSensitivity(std::string const& bindingName, double const newValue) { bindings[bindingName].sensitivity = newValue; @@ -1000,8 +1001,6 @@ namespace SHADE //Get the axis value of binding, between -1 and 1 for non-mouse movement/wheel //For mouse movement/wheel, it won't be between -1 and 1. It will also be multiplied by sensitivity - //To avoid interference between mouse movement/wheel and keyboard/mouse/controller input, - //Set mouseXBound, mouseYBound and mouseScrollBound to false //controllerNumber is not used static double GetBindingAxis(std::string const& bindingName, size_t controllerNumber = 0) noexcept; From ac10b95b0175ff95e567fea18a2621ecd8e89871 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Wed, 25 Jan 2023 23:54:04 +0800 Subject: [PATCH 148/164] Tooltips --- .../InputBindings/SHInputBindingsPanel.cpp | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp index c304c1b0..d3fa33fa 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/InputBindings/SHInputBindingsPanel.cpp @@ -58,7 +58,7 @@ namespace SHADE { ImGui::BeginTooltip(); ImGui::Text(".SHConfig will be appeneded to file name"); - ImGui::Text("If no name is provided, saves to \"Assets/Bindings.SHConfig\""); + ImGui::Text("If no name is provided, saves to or loads from \"Assets/Bindings.SHConfig\""); ImGui::EndTooltip(); } @@ -258,6 +258,12 @@ namespace SHADE //Positive key codes ImGui::Separator(); ImGui::Text("Positive Key Codes:"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("When this keyboard or mouse button is held, causes the value to go positive, or negative when inverted"); + ImGui::EndTooltip(); + } ImGui::SameLine(); //Button to ask for inputs @@ -274,7 +280,12 @@ namespace SHADE if (positiveKeyListeningFor == entryNumber) { //Listening for inputs - ImGui::Button(labelConcat("Press##positiveKeyCode", entryNumber).c_str()); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.4f, 0.4f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.6f, 0.3f, 0.3f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::Button(labelConcat("PRESS##positiveKeyCode", entryNumber).c_str()); + ImGui::PopStyleColor(4); SHInputManager::SH_KEYCODE k; if (SHInputManager::AnyKey(&k)) @@ -317,6 +328,12 @@ namespace SHADE //Negative key codes ImGui::Separator(); ImGui::Text("Negative Key Codes:"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("When this keyboard or mouse button is held, causes the value to go negative, or positive when inverted"); + ImGui::EndTooltip(); + } ImGui::SameLine(); //Button to ask for inputs @@ -333,7 +350,12 @@ namespace SHADE if (negativeKeyListeningFor == entryNumber) { //Listening for inputs - ImGui::Button(labelConcat("Press##negativeKeyCode", entryNumber).c_str()); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.4f, 0.4f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.6f, 0.3f, 0.3f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::Button(labelConcat("PRESS##negativeKeyCode", entryNumber).c_str()); + ImGui::PopStyleColor(4); SHInputManager::SH_KEYCODE k; if (SHInputManager::AnyKey(&k)) @@ -375,6 +397,12 @@ namespace SHADE //Positive controller codes ImGui::Separator(); ImGui::Text("Positive Controller Codes:"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("When this controller button is held, causes the value to go positive, or negative when inverted"); + ImGui::EndTooltip(); + } ImGui::SameLine(); //Button to ask for inputs @@ -391,7 +419,12 @@ namespace SHADE if (positiveControllerListeningFor == entryNumber) { //Listening for inputs - ImGui::Button(labelConcat("Press##positiveControllerCode", entryNumber).c_str()); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.4f, 0.4f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.6f, 0.3f, 0.3f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::Button(labelConcat("PRESS##positiveControllerCode", entryNumber).c_str()); + ImGui::PopStyleColor(4); SHInputManager::SH_CONTROLLERCODE c; if (SHInputManager::AnyControllerInput(&c)) @@ -433,6 +466,12 @@ namespace SHADE //Negative controller codes ImGui::Separator(); ImGui::Text("Negative Controller Codes:"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("When this controller button is pressed, causes the value to go negative, or positive when inverted"); + ImGui::EndTooltip(); + } ImGui::SameLine(); //Button to ask for inputs @@ -449,7 +488,12 @@ namespace SHADE if (negativeControllerListeningFor == entryNumber) { //Listening for inputs - ImGui::Button(labelConcat("Press##negativeControllerCode", entryNumber).c_str()); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.4f, 0.4f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.6f, 0.3f, 0.3f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::Button(labelConcat("PRESS##negativeControllerCode", entryNumber).c_str()); + ImGui::PopStyleColor(4); SHInputManager::SH_CONTROLLERCODE c; if (SHInputManager::AnyControllerInput(&c)) From 1472823dc0e25cc889d0c648e19f064071004278 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 29 Jan 2023 14:45:46 +0800 Subject: [PATCH 149/164] Removed buggy animation debug draw --- SHADE_Engine/src/Animation/SHAnimatorComponent.cpp | 10 +++------- SHADE_Engine/src/Animation/SHAnimatorComponent.h | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 7a397a4c..932a857a 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -114,7 +114,7 @@ namespace SHADE return; // Update time on the playback - currPlaybackTime += dt * 0.01f; + currPlaybackTime += dt; if (currPlaybackTime > currClip->GetTotalTime()) { currPlaybackTime = currPlaybackTime - currClip->GetTotalTime(); @@ -140,7 +140,7 @@ namespace SHADE updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, rig->GetRootNode(), SHMatrix::Identity); } - void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix, std::optional worldPos) + void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix) { // Check if there is a channel for this node const std::string& BONE_NAME = rig->GetName(node); @@ -165,16 +165,12 @@ namespace SHADE if (BONE_MTX_IDX >= 0) { boneMatrices[BONE_MTX_IDX] = node->OffsetMatrix * transformMatrix; - position = SHVec3(boneMatrices[BONE_MTX_IDX]._41, boneMatrices[BONE_MTX_IDX]._42, boneMatrices[BONE_MTX_IDX]._43); - SHDebugDraw::Cube(position.value(), SHQuaternion{}, SHVec3(0.01f, 0.01f, 0.01f), SHColour::ORANGE); - if (worldPos.has_value()) - SHDebugDraw::Line(position.value(), worldPos.value(), SHColour::ORANGE); } // Apply pose to children for (auto& child : node->Children) { - updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix, position); + updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix); } } } diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 95123e8d..b47106f8 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -141,7 +141,7 @@ namespace SHADE /* Helper Functions */ /*---------------------------------------------------------------------------------*/ void updatePoseWithClip(float poseTime); - void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix, std::optional worldPos = {}); + void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix); template T getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); From 3f1a25c95b72d11d852b10dc5b1c9b44389f466f Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 29 Jan 2023 15:39:48 +0800 Subject: [PATCH 150/164] Fixed crash caused by loading more than one SHRig --- SHADE_Engine/src/Animation/SHRig.cpp | 42 +++++++++++++++++-- SHADE_Engine/src/Animation/SHRig.h | 37 ++++++++++++---- .../src/Resource/SHResourceManager.cpp | 1 + SHADE_Engine/src/Resource/SHResourceManager.h | 3 ++ .../src/Resource/SHResourceManager.hpp | 2 +- 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index 87ae3bff..d96d6153 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -23,7 +23,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Constructors */ /*-----------------------------------------------------------------------------------*/ - SHRig::SHRig(const SHRigAsset& asset) + SHRig::SHRig(const SHRigAsset& asset, SHResourceLibrary& nodeStore) { // Don't bother if empty if (asset.root == nullptr) @@ -33,13 +33,47 @@ namespace SHADE } // Do a recursive depth first traversal to populate the rig - rootNode = recurseCreateNode(asset, asset.root); + rootNode = recurseCreateNode(asset, asset.root, nodeStore); if (rootNode) { globalInverseMatrix = SHMatrix::Inverse(rootNode->TransformMatrix); } } + SHRig::SHRig(SHRig&& rhs) + : rootNode { rhs.rootNode } + , nodeNames { std::move(rhs.nodeNames) } + , nodesByName { std::move(rhs.nodesByName) } + , nodes { std::move(rhs.nodes) } + , nodeIndexMap { std::move(rhs.nodeIndexMap) } + , globalInverseMatrix { std::move(rhs.globalInverseMatrix) } + { + rhs.rootNode = {}; + } + SHRig::~SHRig() + { + // Unload all nodes + for (auto node : nodes) + { + if (node) + node.Free(); + } + nodes.clear(); + } + + SHRig& SHRig::operator=(SHRig&& rhs) + { + rootNode = rhs.rootNode; + nodeNames = std::move(rhs.nodeNames); + nodesByName = std::move(rhs.nodesByName); + nodes = std::move(rhs.nodes); + nodeIndexMap = std::move(rhs.nodeIndexMap); + globalInverseMatrix = std::move(rhs.globalInverseMatrix); + + rhs.rootNode = {}; + + return *this; + } /*-----------------------------------------------------------------------------------*/ /* Usage Functions */ /*-----------------------------------------------------------------------------------*/ @@ -79,7 +113,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ - Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode) + Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode, SHResourceLibrary& nodeStore) { // Construct the node auto newNode = nodeStore.Create(); @@ -106,7 +140,7 @@ namespace SHADE continue; // Recursively create children - auto childNode = recurseCreateNode(asset, child); // Not sure why this works but it is required for + auto childNode = recurseCreateNode(asset, child, nodeStore); // Not sure why this works but it is required for newNode->Children.emplace_back(childNode); // the emplace_back operation to not crash } diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index 0329dc6e..ae198317 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -53,19 +53,43 @@ namespace SHADE }; /// - /// + /// Represents an animation skeletal rig for a model. /// class SH_API SHRig { public: /*---------------------------------------------------------------------------------*/ - /* Constructors */ + /* Constructors/Destructors */ /*---------------------------------------------------------------------------------*/ /// - /// + /// Constructs a rig from a SHRigAsset. /// - /// - explicit SHRig(const SHRigAsset& asset); + /// + /// SHRigAsset to load. + /// + /// + /// Reference to a ResourceLibrary to use to create the rig's nodes. + /// + explicit SHRig(const SHRigAsset& asset, SHResourceLibrary& nodeStore); + /// + /// Move Constructor + /// + /// SHRig to move from. + /// Reference to this object. + SHRig& operator=(SHRig&& rhs); /*---------------------------------------------------------------------------------*/ /* Getter Functions */ @@ -114,11 +138,10 @@ namespace SHADE std::vector> nodes; std::unordered_map, int> nodeIndexMap; SHMatrix globalInverseMatrix; - SHResourceLibrary nodeStore; /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - Handle recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode); + Handle recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode, SHResourceLibrary& nodeStore); }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Resource/SHResourceManager.cpp b/SHADE_Engine/src/Resource/SHResourceManager.cpp index 5cb93bb0..5a99cad1 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.cpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.cpp @@ -20,6 +20,7 @@ namespace SHADE /* Static Data Member Definitions */ /*-----------------------------------------------------------------------------------*/ SHResourceHub SHResourceManager::resourceHub; + SHResourceLibrary SHResourceManager::rigNodeStore; std::unordered_map>> SHResourceManager::handlesMap; std::unordered_map SHResourceManager::assetIdMap; std::unordered_map> SHResourceManager::typedFreeFuncMap; diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index 3b9089a2..837c0195 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -13,6 +13,7 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include + // Project Includes #include "SH_API.h" #include "SHResourceLibrary.h" @@ -36,6 +37,7 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ class SHMaterial; + struct SHRigNode; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -176,6 +178,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ // Handles static SHResourceHub resourceHub; + static SHResourceLibrary rigNodeStore; static std::unordered_map handlesMap; static std::unordered_map assetIdMap; static std::unordered_map> typedFreeFuncMap; diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index ea6b4ad9..ea0d75a4 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -353,7 +353,7 @@ namespace SHADE else if constexpr (std::is_same_v) { loadedAssetData.emplace_back(assetId); - return resourceHub.Create(assetData.rig); + return resourceHub.Create(assetData.rig, rigNodeStore); } else if constexpr (std::is_same_v) { From eab2f2d54ac686f31e9b9aab4264dfe52463e8ec Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 29 Jan 2023 16:03:08 +0800 Subject: [PATCH 151/164] Fixed bug where replacing an animator's rig causes the GPU to be lost --- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 66 ++++++++++--------- .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 7 ++ 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 330c395c..685511fe 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -428,11 +428,7 @@ namespace SHADE } // Update GPU Buffers - const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(SHMatrix)); - if (boneMatrixBuffer[frameIndex]) - { - boneMatrixBuffer[frameIndex]->WriteToMemory(boneMatrixData.data(), BONE_MTX_DATA_BYTES, 0, 0); - } + rebuildBoneMatrixDescSetBuffer(frameIndex); } void SHBatch::Build(Handle _device, Handle descPool, uint32_t frameIndex) @@ -734,7 +730,7 @@ namespace SHADE // Using Declarations and constants using BuffUsage = vk::BufferUsageFlagBits; using PreDefDescLayoutType = SHGraphicsPredefinedData::PredefinedDescSetLayoutTypes; - static constexpr uint32_t MATERIAL_DESC_SET_INDEX = 0; + /* Create Descriptor Sets if Needed */ PreDefDescLayoutType layoutTypes = {}; @@ -801,35 +797,41 @@ namespace SHADE /* Animation Bone Data */ if (MUST_BUILD_BONE_DESC) { - // Update GPU Buffers - const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(SHMatrix)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, boneMatrixBuffer[frameIndex], boneMatrixData.data(), BONE_MTX_DATA_BYTES, - BuffUsage::eStorageBuffer, - "Batch Bone Matrix Buffer" - ); - - // Update descriptor set buffer - std::array, 1> bufferList = { boneMatrixBuffer[frameIndex] }; - instanceDataDescSet[frameIndex]->ModifyWriteDescBuffer - ( - MATERIAL_DESC_SET_INDEX, - SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA, - bufferList, - 0, - static_cast(boneMatrixData.size() * sizeof(SHMatrix)) - ); - - // Update the descriptor set buffer - instanceDataDescSet[frameIndex]->UpdateDescriptorSetBuffer - ( - MATERIAL_DESC_SET_INDEX, - SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA - ); + rebuildBoneMatrixDescSetBuffer(frameIndex); } } + void SHBatch::rebuildBoneMatrixDescSetBuffer(uint32_t frameIndex) + { + using BuffUsage = vk::BufferUsageFlagBits; + // Update GPU Buffers + const uint32_t BONE_MTX_DATA_BYTES = static_cast(boneMatrixData.size() * sizeof(SHMatrix)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneMatrixBuffer[frameIndex], boneMatrixData.data(), BONE_MTX_DATA_BYTES, + BuffUsage::eStorageBuffer, + "Batch Bone Matrix Buffer" + ); + + // Update descriptor set buffer + std::array, 1> bufferList = { boneMatrixBuffer[frameIndex] }; + instanceDataDescSet[frameIndex]->ModifyWriteDescBuffer + ( + MATERIAL_DESC_SET_INDEX, + SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA, + bufferList, + 0, + static_cast(boneMatrixData.size() * sizeof(SHMatrix)) + ); + + // Update the descriptor set buffer + instanceDataDescSet[frameIndex]->UpdateDescriptorSetBuffer + ( + MATERIAL_DESC_SET_INDEX, + SHGraphicsConstants::DescriptorSetBindings::PER_INST_BONE_DATA + ); + } + bool SHBatch::checkIfIsAnimatedPipeline(Handle pipeline) { if (!pipeline || !pipeline->GetPipelineLayout()) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index 86e17035..b9192663 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -106,6 +106,11 @@ namespace SHADE using TripleBool = std::array; using TripleBuffer = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; using TripleDescSet = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; + + /*---------------------------------------------------------------------------------*/ + /* Constants */ + /*---------------------------------------------------------------------------------*/ + static constexpr uint32_t MATERIAL_DESC_SET_INDEX = 0; /*---------------------------------------------------------------------------------*/ /* Data Members */ @@ -146,6 +151,8 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ void setAllDirtyFlags(); void rebuildDescriptorSetBuffers(uint32_t frameIndex, Handle descPool); + void rebuildBoneMatrixDescSetBuffer(uint32_t frameIndex); static bool checkIfIsAnimatedPipeline(Handle pipeline); + }; } From 83a33b3851a8e2b74113580055369519cacf2d18 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sun, 29 Jan 2023 18:38:12 +0800 Subject: [PATCH 152/164] Bone count data member in mesh asset --- SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h | 2 ++ SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h index f73078a3..d8afc217 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h @@ -58,5 +58,7 @@ namespace SHADE std::vector Indices; std::vector VertexBoneIndices; std::vector VertexBoneWeights; + + uint32_t boneCount; }; } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 9e4abc67..ce6d0f07 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -221,6 +221,7 @@ namespace SHADE data.VertexNormals.resize(header.vertexCount); data.VertexTexCoords.resize(header.vertexCount); data.Indices.resize(header.indexCount); + data.boneCount = header.boneCount; file.read(data.name.data(), header.charCount); file.read(reinterpret_cast(data.VertexPositions.data()), vertexVec3Byte); @@ -252,11 +253,6 @@ namespace SHADE data.VertexBoneIndices.resize(header.vertexCount); data.VertexBoneWeights.resize(header.vertexCount); - //for (auto& weight : data.VertexBoneWeights) - //{ - // weight = { -0.1f }; - //} - for (uint32_t boneIndex{0}; boneIndex < bones.size(); ++boneIndex) { auto const& bone = bones[boneIndex]; From 6af4933b5279fc4371e4d30886b3d020214d27d5 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 29 Jan 2023 18:51:31 +0800 Subject: [PATCH 153/164] Fixed on-close crashes coming from SHResourceManager --- .../src/Application/SBApplication.cpp | 6 ++++++ .../src/Graphics/MiddleEnd/Batching/SHBatch.cpp | 4 ++++ SHADE_Engine/src/Resource/SHResourceLibrary.h | 6 ++++-- SHADE_Engine/src/Resource/SHResourceManager.cpp | 11 ++++++++++- SHADE_Engine/src/Resource/SHResourceManager.h | 9 ++++++++- SHADE_Engine/src/Resource/SHResourceManager.hpp | 17 +++++++++++------ 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 233ca4ed..e63754a7 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -239,7 +239,13 @@ namespace Sandbox SDL_Quit(); #endif + // Unload scenes SHSceneManager::Exit(); + + // Free all remaining resources + SHResourceManager::UnloadAll(); + + // Shut down engine SHSystemManager::Exit(); SHAssetManager::Exit(); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 685511fe..8332f170 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -424,6 +424,10 @@ namespace SHADE const auto& MATRICES = animator->GetBoneMatrices(); boneMatrixData.insert(boneMatrixData.end(), MATRICES.cbegin(), MATRICES.cend()); } + else + { + // TODO: Populate with empty matrices + } // We don't have to account for missing animators or reset indices as the renderable list are not updated at this point } diff --git a/SHADE_Engine/src/Resource/SHResourceLibrary.h b/SHADE_Engine/src/Resource/SHResourceLibrary.h index a6f772fe..1ff037bc 100644 --- a/SHADE_Engine/src/Resource/SHResourceLibrary.h +++ b/SHADE_Engine/src/Resource/SHResourceLibrary.h @@ -176,12 +176,14 @@ namespace SHADE /* Type Definition */ /*-----------------------------------------------------------------------------*/ using LibraryDeleter = std::function; + using LibraryClearer = std::function; /*-----------------------------------------------------------------------------*/ /* Data Members */ /*-----------------------------------------------------------------------------*/ - std::unordered_map resourceLibs; // TODO: Replace with compile time map - std::vector deleters; + std::unordered_map resourceLibs; // TODO: Replace with compile time map + std::vector deleters; + std::unordered_map clearers; /*-----------------------------------------------------------------------------*/ /* Helper Functions */ diff --git a/SHADE_Engine/src/Resource/SHResourceManager.cpp b/SHADE_Engine/src/Resource/SHResourceManager.cpp index 5a99cad1..9ddb8814 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.cpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.cpp @@ -23,7 +23,8 @@ namespace SHADE SHResourceLibrary SHResourceManager::rigNodeStore; std::unordered_map>> SHResourceManager::handlesMap; std::unordered_map SHResourceManager::assetIdMap; - std::unordered_map> SHResourceManager::typedFreeFuncMap; + std::unordered_map SHResourceManager::typedFreeFuncMap; + std::unordered_map SHResourceManager::typedFreeAllFuncMap; std::vector SHResourceManager::loadedAssetData; bool SHResourceManager::textureChanged = false; bool SHResourceManager::meshChanged = false; @@ -92,6 +93,14 @@ namespace SHADE loadedAssetData.clear(); } + void SHResourceManager::UnloadAll() + { + for (auto func : typedFreeAllFuncMap) + { + func.second(); + } + } + /*-----------------------------------------------------------------------------------*/ /* Query Functions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index 837c0195..4188bde9 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -111,6 +111,10 @@ namespace SHADE /// Needs to be called to finalise all changes to loads, unless at runtime. /// static void FinaliseChanges(); + /// + /// Unloads all loaded resources. + /// + static void UnloadAll(); /*---------------------------------------------------------------------------------*/ /* Query Functions */ @@ -172,6 +176,8 @@ namespace SHADE using HandleAssetMap = std::unordered_map, AssetID>; using AssetHandleMapRef = std::reference_wrapper; using HandleAssetMapRef = std::reference_wrapper; + using AssetFreeFunc = std::function; + using LibraryClearer = std::function; /*---------------------------------------------------------------------------------*/ /* Data Members */ @@ -181,7 +187,8 @@ namespace SHADE static SHResourceLibrary rigNodeStore; static std::unordered_map handlesMap; static std::unordered_map assetIdMap; - static std::unordered_map> typedFreeFuncMap; + static std::unordered_map typedFreeFuncMap; + static std::unordered_map typedFreeAllFuncMap; // Pointers to temp CPU resources static std::vector loadedAssetData; // Dirty Flags diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index ea0d75a4..baf915e0 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -177,14 +177,19 @@ namespace SHADE { handlesMap.emplace(TYPE, AssetHandleMap{}); assetIdMap.emplace(TYPE, HandleAssetMap{}); - typedFreeFuncMap.emplace - ( - TYPE, - [TYPE](AssetID assetId) + typedFreeFuncMap[TYPE] = [TYPE](AssetID assetId) + { + static_cast>(SHResourceManager::handlesMap[TYPE][assetId]).Free(); + }; + typedFreeAllFuncMap[TYPE] = [TYPE]() + { + auto& handlesMap = SHResourceManager::handlesMap[TYPE]; + for (auto handle : handlesMap) { - static_cast>(SHResourceManager::handlesMap[TYPE][assetId]).Free(); + static_cast>(handle.second).Free(); } - ); + handlesMap.clear(); + }; } return std::make_pair(std::ref(handlesMap[TYPE]), std::ref(assetIdMap[TYPE])); } From 2d1987e14bf481b44a6f3e005d3b6f25e5ac162c Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 29 Jan 2023 19:27:58 +0800 Subject: [PATCH 154/164] Added handling for rendering objects using an animated shader but without an animator component or an attached rig --- .../Assets/Asset Types/Models/SHMeshAsset.h | 3 +-- .../Libraries/Loaders/SHModelLoader.cpp | 2 +- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 21 ++++++++++++++++--- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 4 ++-- .../MiddleEnd/Interface/SHGraphicsSystem.h | 2 +- .../MiddleEnd/Interface/SHMeshLibrary.cpp | 5 ++++- .../MiddleEnd/Interface/SHMeshLibrary.h | 5 ++++- .../MiddleEnd/Meshes/SHPrimitiveGenerator.cpp | 3 ++- .../src/Resource/SHResourceManager.hpp | 3 ++- 9 files changed, 35 insertions(+), 13 deletions(-) diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h index d8afc217..18272d02 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h @@ -58,7 +58,6 @@ namespace SHADE std::vector Indices; std::vector VertexBoneIndices; std::vector VertexBoneWeights; - - uint32_t boneCount; + uint32_t BoneCount; }; } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index ce6d0f07..52a3a925 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -221,7 +221,7 @@ namespace SHADE data.VertexNormals.resize(header.vertexCount); data.VertexTexCoords.resize(header.vertexCount); data.Indices.resize(header.indexCount); - data.boneCount = header.boneCount; + data.BoneCount = header.boneCount; file.read(data.name.data(), header.charCount); file.read(reinterpret_cast(data.VertexPositions.data()), vertexVec3Byte); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 8332f170..b30933be 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -418,16 +418,31 @@ namespace SHADE for (auto& subBatch : subBatches) for (auto rendId : subBatch.Renderables) { + // Get resources auto animator = SHComponentManager::GetComponent_s(rendId); + auto renderable = SHComponentManager::GetComponent(rendId); + auto mesh = renderable->GetMesh(); + + // Add matrices + const int BONE_COUNT = static_cast(mesh->BoneCount); + int extraMatricesToAdd = BONE_COUNT; if (animator) { + // Add matrices const auto& MATRICES = animator->GetBoneMatrices(); - boneMatrixData.insert(boneMatrixData.end(), MATRICES.cbegin(), MATRICES.cend()); + if (MATRICES.size() <= BONE_COUNT) + { + boneMatrixData.insert(boneMatrixData.end(), MATRICES.cbegin(), MATRICES.cend()); + extraMatricesToAdd = std::max({0, BONE_COUNT - static_cast(MATRICES.size())}); + } } - else + + // If we need to patch up with more matrices, add it + if (extraMatricesToAdd > 0) { - // TODO: Populate with empty matrices + boneMatrixData.insert(boneMatrixData.end(), extraMatricesToAdd, SHMatrix::Identity); } + // We don't have to account for missing animators or reset indices as the renderable list are not updated at this point } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index a2be7405..7075024e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -797,9 +797,9 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Mesh Registration Functions */ /*---------------------------------------------------------------------------------*/ - SHADE::Handle SHGraphicsSystem::AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices, const SHMesh::VertexBoneIndices* const boneIndices, const SHMesh::VertexWeights* const boneWeights) + SHADE::Handle SHGraphicsSystem::AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices, const SHMesh::VertexBoneIndices* const boneIndices, const SHMesh::VertexWeights* const boneWeights, uint32_t boneCount) { - return meshLibrary.AddMesh(vertexCount, positions, texCoords, tangents, normals, boneIndices, boneWeights, indexCount, indices); + return meshLibrary.AddMesh(vertexCount, positions, texCoords, tangents, normals, boneIndices, boneWeights, indexCount, indices, boneCount); } void SHGraphicsSystem::RemoveMesh(Handle mesh) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index c2151b21..2c548172 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -229,7 +229,7 @@ namespace SHADE */ /*******************************************************************************/ - Handle AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices, const SHMesh::VertexBoneIndices* const boneIndices = nullptr, const SHMesh::VertexWeights* const boneWeights = nullptr); + Handle AddMesh(uint32_t vertexCount, const SHMesh::VertexPosition* const positions, const SHMesh::VertexTexCoord* const texCoords, const SHMesh::VertexTangent* const tangents, const SHMesh::VertexNormal* const normals, uint32_t indexCount, const SHMesh::Index* const indices, const SHMesh::VertexBoneIndices* const boneIndices = nullptr, const SHMesh::VertexWeights* const boneWeights = nullptr, uint32_t boneCount = 0); /*******************************************************************************/ /*! diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp index 658cc5fe..2aedad61 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp @@ -28,7 +28,8 @@ namespace SHADE const SHMesh::VertexNormal* const normals, const SHMesh::VertexBoneIndices* const boneIndices, const SHMesh::VertexWeights* const boneWeights, - uint32_t indexCount, const SHMesh::Index* const indices) + uint32_t indexCount, const SHMesh::Index* const indices, + uint32_t boneCount) { isDirty = true; @@ -44,6 +45,7 @@ namespace SHADE boneWeights, indexCount, indices, + boneCount, handle }); return handle; @@ -140,6 +142,7 @@ namespace SHADE .VertexCount = static_cast(addJob.VertexCount), .FirstIndex = static_cast(indexStorage.size()), .IndexCount = static_cast(addJob.IndexCount), + .BoneCount = addJob.BoneCount }; // Copy into storage diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h index 39b669b8..c953c9e2 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.h @@ -61,6 +61,7 @@ namespace SHADE uint32_t VertexCount; uint32_t FirstIndex; uint32_t IndexCount; + uint32_t BoneCount; }; /***********************************************************************************/ /*! @@ -115,7 +116,8 @@ namespace SHADE const SHMesh::VertexNormal* const normals, const SHMesh::VertexBoneIndices* const boneIndices, const SHMesh::VertexWeights* const boneWeights, - uint32_t indexCount, const SHMesh::Index* const indices); + uint32_t indexCount, const SHMesh::Index* const indices, + uint32_t boneCount); /*******************************************************************************/ /*! @@ -173,6 +175,7 @@ namespace SHADE const SHMesh::VertexWeights* VertexBoneWeights = nullptr; uint32_t IndexCount = 0; const SHMesh::Index* Indices = nullptr; + uint32_t BoneCount = 0; Handle Handle; }; /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp index a9d83560..7e5d8e28 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp @@ -407,7 +407,8 @@ namespace SHADE nullptr, nullptr, static_cast(meshData.Indices.size()), - meshData.Indices.data() + meshData.Indices.data(), + 0 ); } diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index baf915e0..8b0af04f 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -218,7 +218,8 @@ namespace SHADE assetData.Indices.size(), assetData.Indices.data(), assetData.VertexBoneIndices.empty() ? nullptr : assetData.VertexBoneIndices.data(), - assetData.VertexBoneWeights.empty() ? nullptr : assetData.VertexBoneWeights.data() + assetData.VertexBoneWeights.empty() ? nullptr : assetData.VertexBoneWeights.data(), + assetData.BoneCount ); } // Textures From 4a00312f57ba71851b9a5e0a112957aa59f6858c Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 29 Jan 2023 20:35:18 +0800 Subject: [PATCH 155/164] Added serialization of AnimatorComponent --- .../src/Application/SBApplication.cpp | 1 + .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 2 +- .../src/Serialization/SHSerialization.cpp | 3 ++ .../src/Serialization/SHYAMLConverters.h | 32 +++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index e63754a7..4466c7a8 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -152,6 +152,7 @@ namespace Sandbox SHComponentManager::CreateComponentSparseSet(); SHComponentManager::CreateComponentSparseSet(); SHComponentManager::CreateComponentSparseSet(); + SHComponentManager::CreateComponentSparseSet(); //SHComponentManager::CreateComponentSparseSet(); SHAssetManager::Load(); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index b30933be..297a12a6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -443,7 +443,7 @@ namespace SHADE boneMatrixData.insert(boneMatrixData.end(), extraMatricesToAdd, SHMatrix::Identity); } - // We don't have to account for missing animators or reset indices as the renderable list are not updated at this point + // TODO: Recompute boneindexdata if a new animator was added } // Update GPU Buffers diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index a0b961a3..2d8b272b 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -210,6 +210,7 @@ namespace SHADE AddComponentToComponentNode(components, eid); AddComponentToComponentNode(components, eid); + AddComponentToComponentNode(components, eid); node[ComponentsNode] = components; @@ -266,6 +267,7 @@ namespace SHADE AddComponentID(componentIDList, componentsNode); AddComponentID(componentIDList, componentsNode); AddComponentID(componentIDList, componentsNode); + AddComponentID(componentIDList, componentsNode); return componentIDList; } @@ -348,5 +350,6 @@ namespace SHADE SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); + SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); } } diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index 58268b57..a30a54d3 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -15,6 +15,7 @@ #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHFont.h" #include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Animation/SHAnimatorComponent.h" namespace YAML { @@ -29,6 +30,8 @@ namespace YAML struct HasYAMLConv : std::true_type {}; template<> struct HasYAMLConv : std::true_type {}; + template<> + struct HasYAMLConv : std::true_type {}; template<> struct convert @@ -386,4 +389,33 @@ namespace YAML return true; } }; + + + template<> + struct convert + { + static constexpr std::string_view RIG_YAML_TAG = "Rig"; + static constexpr std::string_view CLIP_YAML_TAG = "Clip"; + + static YAML::Node encode(SHAnimatorComponent const& rhs) + { + YAML::Node node; + node[RIG_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetRig()).value_or(0); + node[CLIP_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetCurrentClip()).value_or(0); + return node; + } + static bool decode(YAML::Node const& node, SHAnimatorComponent& rhs) + { + if (node[RIG_YAML_TAG.data()].IsDefined()) + { + rhs.SetRig(SHResourceManager::LoadOrGet(node[RIG_YAML_TAG.data()].as())); + } + if (node[CLIP_YAML_TAG.data()].IsDefined()) + { + rhs.SetClip(SHResourceManager::LoadOrGet(node[CLIP_YAML_TAG.data()].as())); + } + return true; + } + }; + } From 8c3703ce04cf14b72e6a44ed113be5e4b1d5ad1e Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sun, 29 Jan 2023 20:52:04 +0800 Subject: [PATCH 156/164] Fixed models using wrong animation bone matrices --- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 297a12a6..c35223aa 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -410,6 +410,7 @@ namespace SHADE // Reset Animation Matrix Data boneMatrixData.clear(); + boneMatrixIndices.clear(); // Add the first identity matrix into the bone matrix data boneMatrixData.emplace_back(SHMatrix::Identity); // This kills the GPU @@ -423,6 +424,9 @@ namespace SHADE auto renderable = SHComponentManager::GetComponent(rendId); auto mesh = renderable->GetMesh(); + // Mark start + boneMatrixIndices.emplace_back(static_cast(boneMatrixData.size())); + // Add matrices const int BONE_COUNT = static_cast(mesh->BoneCount); int extraMatricesToAdd = BONE_COUNT; @@ -442,11 +446,19 @@ namespace SHADE { boneMatrixData.insert(boneMatrixData.end(), extraMatricesToAdd, SHMatrix::Identity); } - - // TODO: Recompute boneindexdata if a new animator was added } // Update GPU Buffers + if (!boneMatrixIndices.empty()) + { + const uint32_t BMI_DATA_BYTES = static_cast(boneMatrixIndices.size() * sizeof(uint32_t)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, boneMatrixFirstIndexBuffer[frameIndex], boneMatrixIndices.data(), BMI_DATA_BYTES, + vk::BufferUsageFlagBits::eVertexBuffer, + "Batch Instance Bone Matrix First Index Buffer" + ); + } rebuildBoneMatrixDescSetBuffer(frameIndex); } @@ -586,24 +598,34 @@ namespace SHADE { SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!"); } - //propsCurrPtr += singleMatPropAlignedSize; propsCurrPtr += singleMatPropSize; } // Bone Data if (isAnimated) { + // Mark start + boneMatrixIndices.emplace_back(static_cast(boneMatrixData.size())); + auto animator = SHComponentManager::GetComponent_s(rendId); + auto mesh = renderable->GetMesh(); + const int BONE_COUNT = static_cast(mesh->BoneCount); + int extraMatricesToAdd = BONE_COUNT; if (animator) { - boneMatrixIndices.emplace_back(static_cast(boneMatrixData.size())); - const auto& BONE_MATRICES = animator->GetBoneMatrices(); - boneMatrixData.insert(boneMatrixData.end(), BONE_MATRICES.cbegin(), BONE_MATRICES.cend()); + // Add matrices + const auto& MATRICES = animator->GetBoneMatrices(); + if (MATRICES.size() <= BONE_COUNT) + { + boneMatrixData.insert(boneMatrixData.end(), MATRICES.cbegin(), MATRICES.cend()); + extraMatricesToAdd = std::max({0, BONE_COUNT - static_cast(MATRICES.size())}); + } } - else + + // If we need to patch up with more matrices, add it + if (extraMatricesToAdd > 0) { - // Take the first matrix which is always identity - boneMatrixIndices.emplace_back(static_cast(0)); + boneMatrixData.insert(boneMatrixData.end(), extraMatricesToAdd, SHMatrix::Identity); } } } From 3ff2e6bb92dd2b8baa2ebe7b98b1285c218fc2a1 Mon Sep 17 00:00:00 2001 From: XiaoQiDigipen <72735604+XiaoQiDigipen@users.noreply.github.com> Date: Mon, 30 Jan 2023 14:40:55 +0800 Subject: [PATCH 157/164] Revert "Implemented a custom physics engine" --- Assets/Application.SHConfig | 2 +- Assets/CollisionTags.SHConfig | 32 +- Assets/Editor/Editor.SHConfig | 2 +- Assets/Scenes/PhysicsSandbox.shade | 247 ------ Assets/Scenes/PhysicsSandbox.shade.shmeta | 3 - .../Implemented/LeafNodes/LeafSearch.cs | 2 +- .../Gameplay/Player/SC_PickAndThrow.cs | 11 +- Assets/Scripts/Tests/PhysicsTestObj.cs | 119 --- Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta | 3 - .../src/Application/SBApplication.cpp | 34 +- SHADE_Application/src/Scenes/SBMainScene.cpp | 17 + SHADE_Application/src/Scenes/SBTestScene.cpp | 28 +- .../src/Camera/SHCameraArmComponent.h | 2 +- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 30 +- .../src/ECS_Base/Managers/SHEntityManager.cpp | 2 +- .../ColliderTagPanel/SHColliderTagPanel.cpp | 10 +- .../Inspector/SHEditorComponentView.hpp | 100 +-- .../EditorWindow/MenuBar/SHEditorMenuBar.cpp | 30 - .../EditorWindow/MenuBar/SHEditorMenuBar.h | 1 - SHADE_Engine/src/Events/SHEventDefines.h | 2 - SHADE_Engine/src/Math/Geometry/SHAABB.h | 173 ---- .../Math/Geometry/{SHAABB.cpp => SHBox.cpp} | 121 ++- SHADE_Engine/src/Math/Geometry/SHBox.h | 105 +++ SHADE_Engine/src/Math/Geometry/SHPlane.cpp | 141 --- SHADE_Engine/src/Math/Geometry/SHPlane.h | 121 --- .../Geometry/SHShape.cpp} | 26 +- .../Geometry/SHShape.h} | 87 +- SHADE_Engine/src/Math/Geometry/SHSphere.cpp | 208 +++++ .../Shapes => Math/Geometry}/SHSphere.h | 87 +- SHADE_Engine/src/Math/SHColour.cpp | 8 - SHADE_Engine/src/Math/SHMathHelpers.h | 12 +- SHADE_Engine/src/Math/SHMatrix.cpp | 8 - SHADE_Engine/src/Math/SHMatrix.h | 1 - SHADE_Engine/src/Math/SHQuaternion.cpp | 24 +- SHADE_Engine/src/Math/SHQuaternion.h | 15 +- SHADE_Engine/src/Math/SHRay.cpp | 12 + SHADE_Engine/src/Math/SHRay.h | 6 + .../src/Math/Transform/SHTransformSystem.cpp | 2 + SHADE_Engine/src/Math/Vector/SHVec2.cpp | 17 +- SHADE_Engine/src/Math/Vector/SHVec2.h | 6 + SHADE_Engine/src/Math/Vector/SHVec3.cpp | 50 +- SHADE_Engine/src/Math/Vector/SHVec3.h | 48 +- SHADE_Engine/src/Math/Vector/SHVec4.cpp | 8 - .../Broadphase/SHDynamicAABBTree.cpp | 644 -------------- .../Collision/Broadphase/SHDynamicAABBTree.h | 156 ---- .../Collision/Contacts/SHCollisionEvents.h | 60 -- .../Collision/Contacts/SHCollisionKey.cpp | 166 ---- .../Physics/Collision/Contacts/SHContact.h | 72 -- .../Physics/Collision/Contacts/SHManifold.h | 73 -- .../Physics/Collision/Contacts/SHManifold.hpp | 110 --- .../Narrowphase/SHCapsuleVsConvex.cpp | 49 -- .../Collision/Narrowphase/SHCollision.h | 222 ----- .../Narrowphase/SHCollisionDispatch.cpp | 65 -- .../Narrowphase/SHConvexVsConvex.cpp | 822 ------------------ .../Physics/Collision/Narrowphase/SHSATInfo.h | 87 -- .../Collision/Narrowphase/SHSATInfo.hpp | 71 -- .../Narrowphase/SHSphereVsCapsule.cpp | 48 - .../Narrowphase/SHSphereVsConvex.cpp | 348 -------- .../Narrowphase/SHSphereVsSphere.cpp | 97 --- .../src/Physics/Collision/SHCollider.cpp | 393 --------- .../src/Physics/Collision/SHCollider.h | 178 ---- .../src/Physics/Collision/SHCollisionInfo.cpp | 93 ++ ...SHCollisionShapeID.h => SHCollisionInfo.h} | 103 ++- .../Physics/Collision/SHCollisionListener.cpp | 254 ++++++ ...onShapeLibrary.h => SHCollisionListener.h} | 98 +-- .../Physics/Collision/SHCollisionSpace.cpp | 308 ------- .../src/Physics/Collision/SHCollisionSpace.h | 196 ----- .../SHCollisionTagMatrix.cpp | 35 +- .../SHCollisionTagMatrix.h | 0 .../{CollisionTags => }/SHCollisionTags.cpp | 0 .../{CollisionTags => }/SHCollisionTags.h | 0 .../Physics/Collision/SHCompositeCollider.cpp | 176 ---- .../Physics/Collision/SHCompositeCollider.h | 80 -- .../Physics/Collision/SHPhysicsRaycaster.cpp | 350 ++++++++ .../Physics/Collision/SHPhysicsRaycaster.h | 134 +++ .../src/Physics/Collision/Shapes/SHBox.cpp | 319 ------- .../src/Physics/Collision/Shapes/SHBox.h | 129 --- .../Collision/Shapes/SHCollisionShape.cpp | 188 ---- .../Collision/Shapes/SHCollisionShape.h | 210 ----- .../Collision/Shapes/SHCollisionShapeID.hpp | 80 -- .../Shapes/SHCollisionShapeLibrary.cpp | 174 ---- .../Collision/Shapes/SHConvexPolyhedron.cpp | 96 -- .../Collision/Shapes/SHConvexPolyhedron.h | 93 -- .../Collision/Shapes/SHHalfEdgeStructure.cpp | 182 ---- .../Collision/Shapes/SHHalfEdgeStructure.h | 143 --- .../src/Physics/Collision/Shapes/SHSphere.cpp | 240 ----- .../Constraints/SHContactConstraint.h | 56 -- .../Dynamics/Constraints/SHVelocityState.h | 63 -- .../src/Physics/Dynamics/SHContactManager.cpp | 297 ------- .../src/Physics/Dynamics/SHContactManager.h | 140 --- .../src/Physics/Dynamics/SHContactManager.hpp | 61 -- .../src/Physics/Dynamics/SHContactSolver.cpp | 332 ------- .../src/Physics/Dynamics/SHMotionState.cpp | 128 --- .../src/Physics/Dynamics/SHMotionState.h | 127 --- .../src/Physics/Dynamics/SHPhysicsWorld.cpp | 190 ---- .../src/Physics/Dynamics/SHPhysicsWorld.h | 145 --- .../src/Physics/Dynamics/SHRigidBody.cpp | 765 ---------------- .../src/Physics/Dynamics/SHRigidBody.h | 268 ------ .../PhysicsObject/SHPhysicsObject.cpp | 163 ---- .../Interface/PhysicsObject/SHPhysicsObject.h | 109 --- .../PhysicsObject/SHPhysicsObjectManager.cpp | 178 ---- .../PhysicsObject/SHPhysicsObjectManager.h | 116 --- .../Physics/Interface/SHColliderComponent.cpp | 189 +++- .../Physics/Interface/SHColliderComponent.h | 49 +- .../Physics/Interface/SHCollisionShape.cpp | 368 ++++++++ .../src/Physics/Interface/SHCollisionShape.h | 134 +++ .../SHPhysicsMaterial.cpp | 0 .../SHPhysicsMaterial.h | 4 - .../Interface/SHRigidBodyComponent.cpp | 527 +++++++---- .../Physics/Interface/SHRigidBodyComponent.h | 52 +- .../Physics/PhysicsObject/SHPhysicsObject.cpp | 431 +++++++++ .../SHPhysicsObject.h} | 116 ++- .../PhysicsObject/SHPhysicsObjectManager.cpp | 309 +++++++ .../PhysicsObject/SHPhysicsObjectManager.h | 181 ++++ SHADE_Engine/src/Physics/SHPhysicsConstants.h | 50 -- SHADE_Engine/src/Physics/SHPhysicsEvents.h | 12 +- SHADE_Engine/src/Physics/SHPhysicsWorld.cpp | 71 ++ ...SHCollisionDispatch.h => SHPhysicsWorld.h} | 60 +- .../Routines/SHPhysicsDebugDrawRoutine.cpp | 114 --- .../Routines/SHPhysicsPostUpdateRoutine.cpp | 104 --- .../Routines/SHPhysicsPreUpdateRoutine.cpp | 94 -- .../Routines/SHPhysicsUpdateRoutine.cpp | 66 -- .../System/SHPhysicsDebugDrawSystem.cpp | 329 +++++-- .../Physics/System/SHPhysicsDebugDrawSystem.h | 139 ++- .../src/Physics/System/SHPhysicsSystem.cpp | 589 +++++++------ .../src/Physics/System/SHPhysicsSystem.h | 370 +++++--- .../System/SHPhysicsSystemInterface.cpp | 110 ++- .../Physics/System/SHPhysicsSystemInterface.h | 25 +- .../System/SHPhysicsSystemRoutines.cpp | 412 +++++++++ SHADE_Engine/src/Scene/SHSceneGraph.cpp | 46 +- SHADE_Engine/src/Scene/SHSceneGraph.h | 81 +- SHADE_Engine/src/Scene/SHSceneManager.cpp | 2 +- SHADE_Engine/src/Scene/SHSceneNode.h | 13 +- .../src/Serialization/SHYAMLConverters.h | 49 +- SHADE_Managed/src/Components/Collider.cxx | 55 +- SHADE_Managed/src/Components/Collider.h++ | 6 +- SHADE_Managed/src/Components/Collider.hxx | 25 +- SHADE_Managed/src/Components/RigidBody.cxx | 8 +- SHADE_Managed/src/Components/RigidBody.hxx | 1 + SHADE_Managed/src/Physics/Physics.cxx | 254 +----- SHADE_Managed/src/Physics/Physics.hxx | 114 +-- SHADE_Managed/src/Scripts/ScriptStore.cxx | 25 +- SHADE_Managed/src/Utility/Convert.cxx | 1 - SHADE_Managed/src/Utility/Convert.hxx | 6 +- 144 files changed, 5403 insertions(+), 12631 deletions(-) delete mode 100644 Assets/Scenes/PhysicsSandbox.shade delete mode 100644 Assets/Scenes/PhysicsSandbox.shade.shmeta delete mode 100644 Assets/Scripts/Tests/PhysicsTestObj.cs delete mode 100644 Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta delete mode 100644 SHADE_Engine/src/Math/Geometry/SHAABB.h rename SHADE_Engine/src/Math/Geometry/{SHAABB.cpp => SHBox.cpp} (60%) create mode 100644 SHADE_Engine/src/Math/Geometry/SHBox.h delete mode 100644 SHADE_Engine/src/Math/Geometry/SHPlane.cpp delete mode 100644 SHADE_Engine/src/Math/Geometry/SHPlane.h rename SHADE_Engine/src/{Physics/Collision/Narrowphase/SHCapsuleVsCapsule.cpp => Math/Geometry/SHShape.cpp} (57%) rename SHADE_Engine/src/{Physics/Dynamics/SHContactSolver.h => Math/Geometry/SHShape.h} (57%) create mode 100644 SHADE_Engine/src/Math/Geometry/SHSphere.cpp rename SHADE_Engine/src/{Physics/Collision/Shapes => Math/Geometry}/SHSphere.h (56%) delete mode 100644 SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.hpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCollider.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCollider.h create mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp rename SHADE_Engine/src/Physics/Collision/{Shapes/SHCollisionShapeID.h => SHCollisionInfo.h} (55%) create mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionListener.cpp rename SHADE_Engine/src/Physics/Collision/{Shapes/SHCollisionShapeLibrary.h => SHCollisionListener.h} (50%) delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h rename SHADE_Engine/src/Physics/Collision/{CollisionTags => }/SHCollisionTagMatrix.cpp (86%) rename SHADE_Engine/src/Physics/Collision/{CollisionTags => }/SHCollisionTagMatrix.h (100%) rename SHADE_Engine/src/Physics/Collision/{CollisionTags => }/SHCollisionTags.cpp (100%) rename SHADE_Engine/src/Physics/Collision/{CollisionTags => }/SHCollisionTags.h (100%) delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h create mode 100644 SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp create mode 100644 SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.hpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.cpp delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.h delete mode 100644 SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.cpp delete mode 100644 SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h delete mode 100644 SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactManager.h delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHMotionState.h delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp delete mode 100644 SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h delete mode 100644 SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp delete mode 100644 SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h delete mode 100644 SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp delete mode 100644 SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h create mode 100644 SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp create mode 100644 SHADE_Engine/src/Physics/Interface/SHCollisionShape.h rename SHADE_Engine/src/Physics/{Collision => Interface}/SHPhysicsMaterial.cpp (100%) rename SHADE_Engine/src/Physics/{Collision => Interface}/SHPhysicsMaterial.h (98%) create mode 100644 SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp rename SHADE_Engine/src/Physics/{Collision/Contacts/SHCollisionKey.h => PhysicsObject/SHPhysicsObject.h} (51%) create mode 100644 SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp create mode 100644 SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h delete mode 100644 SHADE_Engine/src/Physics/SHPhysicsConstants.h create mode 100644 SHADE_Engine/src/Physics/SHPhysicsWorld.cpp rename SHADE_Engine/src/Physics/{Collision/Narrowphase/SHCollisionDispatch.h => SHPhysicsWorld.h} (51%) delete mode 100644 SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp delete mode 100644 SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp delete mode 100644 SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp delete mode 100644 SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp create mode 100644 SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp diff --git a/Assets/Application.SHConfig b/Assets/Application.SHConfig index d0fa83df..ee5e42a8 100644 --- a/Assets/Application.SHConfig +++ b/Assets/Application.SHConfig @@ -1,4 +1,4 @@ Start in Fullscreen: false -Starting Scene ID: 97402985 +Starting Scene ID: 87244611 Window Size: {x: 1920, y: 1080} Window Title: SHADE Engine \ No newline at end of file diff --git a/Assets/CollisionTags.SHConfig b/Assets/CollisionTags.SHConfig index 9488270c..d3ebe7e2 100644 --- a/Assets/CollisionTags.SHConfig +++ b/Assets/CollisionTags.SHConfig @@ -1,16 +1,16 @@ -0 1 3 -1 2 3 -2 3 65535 -3 4 65535 -4 5 65535 -5 6 65535 -6 7 65535 -7 8 65535 -8 9 65535 -9 10 65535 -10 11 65535 -11 12 65535 -12 13 65535 -13 14 65535 -14 15 65535 -15 16 65535 +0 1 +1 2 +2 3 +3 4 +4 5 +5 6 +6 7 +7 8 +8 9 +9 10 +10 11 +11 12 +12 13 +13 14 +14 15 +15 16 diff --git a/Assets/Editor/Editor.SHConfig b/Assets/Editor/Editor.SHConfig index b492a983..51425027 100644 --- a/Assets/Editor/Editor.SHConfig +++ b/Assets/Editor/Editor.SHConfig @@ -1,4 +1,4 @@ Start Maximized: true -Working Scene ID: 97402985 +Working Scene ID: 97161771 Window Size: {x: 1920, y: 1080} Style: 0 \ No newline at end of file diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade deleted file mode 100644 index 56f272e0..00000000 --- a/Assets/Scenes/PhysicsSandbox.shade +++ /dev/null @@ -1,247 +0,0 @@ -- EID: 0 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 2.72256827, y: 0.501797795, z: -0.0273017883} - Rotate: {x: 0, y: 0, z: 0.436332315} - Scale: {x: 4.61070776, y: 0.99999392, z: 0.999996722} - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 1 - Type: Box - Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ -- EID: 1 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Camera Component: - Position: {x: 0, y: 2, z: 10} - Pitch: 0 - Yaw: 0 - Roll: 0 - Width: 1920 - Height: 1080 - Near: 0.00999999978 - Far: 10000 - Perspective: true - IsActive: true - Scripts: ~ -- EID: 3 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 2, y: 7.5, z: 0} - Rotate: {x: -0, y: 0, z: 0.785398185} - Scale: {x: 1, y: 1, z: 1} - IsActive: true - RigidBody Component: - Type: Dynamic - Auto Mass: false - Mass: 0.52359879 - Drag: 0.00999999978 - Angular Drag: 0 - Use Gravity: true - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: true - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 1 - Type: Sphere - Radius: 1 - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ -- EID: 2 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 0, y: 4, z: 0} - Rotate: {x: -0, y: 0, z: -0.436332315} - Scale: {x: 4.61071014, y: 0.999995887, z: 1} - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 2 - Type: Box - Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ -- EID: 4 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 0, y: -3, z: 0} - Rotate: {x: -0, y: 0, z: -0} - Scale: {x: 10, y: 3, z: 10} - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 1 - Type: Box - Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ -- EID: 5 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: -4.80025721, y: 3, z: 0} - Rotate: {x: -0, y: 0, z: 1.57079601} - Scale: {x: 9.99975109, y: 0.499992192, z: 10} - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 1 - Type: Box - Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ -- EID: 6 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 4.80000019, y: 3, z: 0} - Rotate: {x: -0, y: 0, z: 1.57079601} - Scale: {x: 9.99975109, y: 0.499992192, z: 10} - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 1 - Type: Box - Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ -- EID: 7 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 0, y: 7, z: 0} - Rotate: {x: 0, y: 0, z: 0.785398185} - Scale: {x: 0.999990404, y: 0.999994457, z: 0.999985337} - IsActive: true - RigidBody Component: - Type: Dynamic - Auto Mass: false - Mass: 1 - Drag: 0.00999999978 - Angular Drag: 0 - Use Gravity: true - Gravity Scale: 1 - Interpolate: true - Sleeping Enabled: true - Freeze Position X: false - Freeze Position Y: false - Freeze Position Z: false - Freeze Rotation X: false - Freeze Rotation Y: false - Freeze Rotation Z: false - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 1 - Type: Box - Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: - - Type: PhysicsTestObj - Enabled: true - forceAmount: 50 - torqueAmount: 5 -- EID: 8 - Name: Default - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 0, y: 0, z: 3} - Rotate: {x: -0, y: 0, z: -0} - Scale: {x: 1, y: 1, z: 1} - IsActive: true - Collider Component: - DrawColliders: false - Colliders: - - Is Trigger: false - Collision Tag: 1 - Type: Box - Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0, z: 0} - Rotation Offset: {x: 0, y: 0, z: 0} - IsActive: true - Scripts: ~ \ No newline at end of file diff --git a/Assets/Scenes/PhysicsSandbox.shade.shmeta b/Assets/Scenes/PhysicsSandbox.shade.shmeta deleted file mode 100644 index 97c00ca6..00000000 --- a/Assets/Scenes/PhysicsSandbox.shade.shmeta +++ /dev/null @@ -1,3 +0,0 @@ -Name: PhysicsSandbox -ID: 97402985 -Type: 5 diff --git a/Assets/Scripts/Gameplay/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs b/Assets/Scripts/Gameplay/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs index b5b03629..7c68712c 100644 --- a/Assets/Scripts/Gameplay/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs +++ b/Assets/Scripts/Gameplay/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs @@ -159,7 +159,7 @@ public partial class LeafSearch : BehaviourTreeNode //Since transform position is often the raccoon's base and the ray needs to hit somewhere higher to be more reliable Vector3 rayDestination = plrT.GlobalPosition + plrT.GlobalScale * playerCollider.PositionOffset; Ray sightRay = new Ray(eyePosition, rayDestination - eyePosition); - RaycastHit sightRayHit = Physics.Raycast(sightRay, false)[0]; + RaycastHit sightRayHit = Physics.Raycast(sightRay); //As of November 2022, RaycastHit contains only the FIRST object hit by //the ray in the Other GameObject data member //Diren may likely add ALL objects hit by the ray over December diff --git a/Assets/Scripts/Gameplay/Player/SC_PickAndThrow.cs b/Assets/Scripts/Gameplay/Player/SC_PickAndThrow.cs index 8323dba2..9c879314 100644 --- a/Assets/Scripts/Gameplay/Player/SC_PickAndThrow.cs +++ b/Assets/Scripts/Gameplay/Player/SC_PickAndThrow.cs @@ -1,7 +1,6 @@ using SHADE; using SHADE_Scripting; using System; -using System.Collections.Generic; using static PlayerController; using static Item; @@ -204,13 +203,9 @@ public class PickAndThrow : Script Vector3 playerRayPos = pc.tranform.GlobalPosition; playerRayPos.y += 0.05f; dirNor.Normalise(); - List rayList1 = Physics.Raycast(new Ray(playerRayPos, Vector3.RotateY(dirNor, SHADE.Math.DegreesToRadians(22.5f))), rayDistance, false); - List rayList2 = Physics.Raycast(new Ray(playerRayPos, Vector3.RotateY(dirNor, SHADE.Math.DegreesToRadians(-22.5f))), rayDistance, false); - List rayList3 = Physics.Raycast(new Ray(playerRayPos, dirNor), rayDistance * 0.75f, false); - - RaycastHit ray1 = rayList1[0]; - RaycastHit ray2 = rayList2[0]; - RaycastHit ray3 = rayList3[0]; + RaycastHit ray1 = Physics.Raycast(new Ray(playerRayPos, Vector3.RotateY(dirNor, SHADE.Math.DegreesToRadians(22.5f))), rayDistance); + RaycastHit ray2 = Physics.Raycast(new Ray(playerRayPos, Vector3.RotateY(dirNor, SHADE.Math.DegreesToRadians(-22.5f))), rayDistance); + RaycastHit ray3 = Physics.Raycast(new Ray(playerRayPos, dirNor), rayDistance * 0.75f); inRange = CheckForItem(ray1) || CheckForItem(ray2) || CheckForItem(ray3); } } diff --git a/Assets/Scripts/Tests/PhysicsTestObj.cs b/Assets/Scripts/Tests/PhysicsTestObj.cs deleted file mode 100644 index 2348894f..00000000 --- a/Assets/Scripts/Tests/PhysicsTestObj.cs +++ /dev/null @@ -1,119 +0,0 @@ -using SHADE; -using System; -using System.Collections.Generic; -using static Item; - - -public class PhysicsTestObj : Script -{ - public RigidBody body { get; set; } - public Collider collider { get; set; } - - // Movement input booleans - public enum Direction - { - UP, - DOWN, - FORWARD, - BACK, - LEFT, - RIGHT - }; - - internal bool[] move = new bool[6]; - internal bool[] rotate = new bool[6]; - - internal Vector3[] moveVec = new Vector3[6] - { - Vector3.Up, - Vector3.Down, - Vector3.Back, - Vector3.Forward, - Vector3.Left, - Vector3.Right - }; - - internal Vector3[] rotateVec = new Vector3[6] - { - Vector3.Right, - Vector3.Left, - Vector3.Forward, - Vector3.Down, - Vector3.Up, - Vector3.Down - }; - - internal Input.KeyCode[] moveInputKeys = new Input.KeyCode[6] - { - Input.KeyCode.Space, - Input.KeyCode.LeftControl, - Input.KeyCode.W, - Input.KeyCode.S, - Input.KeyCode.A, - Input.KeyCode.D - }; - - internal Input.KeyCode[] rotateInputKeys = new Input.KeyCode[6] - { - Input.KeyCode.I, - Input.KeyCode.K, - Input.KeyCode.U, - Input.KeyCode.O, - Input.KeyCode.J, - Input.KeyCode.L - }; - - public float forceAmount = 50.0f; - public float torqueAmount = 500.0f; - - protected override void awake() - { - body = GetComponent(); - collider = GetComponent(); - - for (int i = 0; i < 6; ++i) - { - move[i] = false; - rotate[i] = false; - } - } - - protected override void update() - { - Ray colliderRay = new Ray(); - colliderRay.Direction = Vector3.Right; - Physics.ColliderRaycast(collider.Owner, colliderRay, false); - - for (int i = 0; i < 6; ++i) - { - if (Input.GetKeyDown(moveInputKeys[i])) - move[i] = true; - - if (Input.GetKeyDown(rotateInputKeys[i])) - rotate[i] = true; - } - } - - protected override void fixedUpdate() - { - for (int i = 0; i < 6; ++i) - { - bool shouldMove = move[i]; - bool shouldRotate = rotate[i]; - - if (shouldMove) - { - //Vector3 offset = new Vector3(0.25f, 0.0f, 0.0f); - //rb.AddForceAtLocalPos(moveVec[i] * forceAmount, offset); - body.AddForce(moveVec[i] * forceAmount); - move[i] = false; - } - - if (shouldRotate) - { - body.AddTorque(rotateVec[i] * torqueAmount); - rotate[i] = false; - } - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta b/Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta deleted file mode 100644 index 8c096651..00000000 --- a/Assets/Scripts/Tests/PhysicsTestObj.cs.shmeta +++ /dev/null @@ -1,3 +0,0 @@ -Name: PhysicsTestObj -ID: 159293012 -Type: 9 diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 5f451dea..fcceacab 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -77,6 +77,9 @@ namespace Sandbox SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); +#ifndef _PUBLISH + SHSystemManager::CreateSystem(); +#endif SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); @@ -87,6 +90,7 @@ namespace Sandbox SHSystemManager::CreateSystem(); SHGraphicsSystem* graphicsSystem = static_cast(SHSystemManager::GetSystem()); + SHPhysicsSystem* physicsSystem = SHSystemManager::GetSystem(); // Link up SHDebugDraw SHSystemManager::CreateSystem(); @@ -101,8 +105,6 @@ namespace Sandbox editor->SetSDLWindow(sdlWindow); editor->SetSHWindow(&window); } - - SHSystemManager::CreateSystem(); #endif // Create Routines @@ -114,11 +116,11 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); - SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); -#ifdef SHEDITOR - SHSystemManager::RegisterRoutine(); +#ifndef _PUBLISH + SHSystemManager::RegisterRoutine(); #endif SHSystemManager::RegisterRoutine(); @@ -191,12 +193,32 @@ namespace Sandbox #endif SHSceneManager::SceneUpdate(0.016f); #ifdef SHEDITOR - SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, SHFrameRateController::GetRawDeltaTime()); editor->PollPicking(); #else SHSystemManager::RunRoutines(false, SHFrameRateController::GetRawDeltaTime()); #endif + // TODO: Move into an Editor menu + static bool drawContacts = false; + if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::F9)) + { + drawContacts = !drawContacts; + SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACT_POINTS, drawContacts); + SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACT_NORMALS, drawContacts); + } + static bool drawColliders = false; + if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::F10)) + { + drawColliders = !drawColliders; + SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDER, drawColliders); + } + static bool drawRays = false; + if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::F11)) + { + drawRays = !drawRays; + SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS, drawRays); + } + } // Finish all graphics jobs first graphicsSystem->AwaitGraphicsExecution(); diff --git a/SHADE_Application/src/Scenes/SBMainScene.cpp b/SHADE_Application/src/Scenes/SBMainScene.cpp index dd713980..73926115 100644 --- a/SHADE_Application/src/Scenes/SBMainScene.cpp +++ b/SHADE_Application/src/Scenes/SBMainScene.cpp @@ -44,6 +44,23 @@ namespace Sandbox { sceneName = SHSerialization::DeserializeSceneFromFile(sceneAssetID); + auto* physicsSystem = SHSystemManager::GetSystem(); + if (!physicsSystem) + { + SHLOGV_CRITICAL("Failed to get the physics system for building the scene!") + return; + } + + #ifdef SHEDITOR + + physicsSystem->ForceBuild(SHSceneManager::GetCurrentSceneGraph()); + + #else + + physicsSystem->BuildScene(SHSceneManager::GetCurrentSceneGraph()); + + #endif + /*-----------------------------------------------------------------------*/ /* TESTING CODE */ /*-----------------------------------------------------------------------*/ diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index 0603b22b..a5edd124 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -92,7 +92,7 @@ namespace Sandbox floorRigidBody.SetType(SHRigidBodyComponent::Type::STATIC); - //floorCollider.AddBoundingBox(); + floorCollider.AddBoundingBox(); // Create blank entity with a script //testObj = SHADE::SHEntityManager::CreateEntity(); @@ -113,9 +113,9 @@ namespace Sandbox racoonTransform.SetWorldScale({ 2.0f, 2.0f, 2.0f }); racoonTransform.SetWorldPosition({ -3.0f, -2.0f, -5.0f }); - //racoonCollider.AddBoundingBox(); - //racoonCollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f,0.5f,0.0f)); - //racoonCollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + racoonCollider.AddBoundingBox(); + racoonCollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f,0.5f,0.0f)); + racoonCollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); auto racoonItemLocation = SHEntityManager::CreateEntity(); auto& racoonItemLocationTransform = *SHComponentManager::GetComponent_s(racoonItemLocation); @@ -138,15 +138,15 @@ namespace Sandbox itemTransform.SetWorldScale({ 2.0f, 2.0f, 2.0f }); itemTransform.SetWorldPosition({ 0.0f, -2.0f, -5.0f }); - //itemCollider.AddBoundingBox(); - //itemCollider.AddBoundingBox(SHVec3(2.0f,2.0f,2.0f)); - //itemCollider.GetCollisionShape(1).SetIsTrigger(true); + itemCollider.AddBoundingBox(); + itemCollider.AddBoundingBox(SHVec3(2.0f,2.0f,2.0f)); + itemCollider.GetCollisionShape(1).SetIsTrigger(true); - //itemCollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - //itemCollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + itemCollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + itemCollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); - //itemCollider.GetCollisionShape(1).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - //itemCollider.GetCollisionShape(1).SetBoundingBox(SHVec3(1.0f, 1.0f, 1.0f)); + itemCollider.GetCollisionShape(1).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + itemCollider.GetCollisionShape(1).SetBoundingBox(SHVec3(1.0f, 1.0f, 1.0f)); itemRigidBody.SetInterpolate(false); itemRigidBody.SetFreezeRotationX(true); @@ -167,9 +167,9 @@ namespace Sandbox AITransform.SetWorldScale({ 2.0f, 2.0f, 2.0f }); AITransform.SetWorldPosition({ -8.0f, -2.0f, 2.5f }); - //AICollider.AddBoundingBox(); - //AICollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - //AICollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + AICollider.AddBoundingBox(); + AICollider.GetCollisionShape(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + AICollider.GetCollisionShape(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); AIRigidBody.SetInterpolate(false); AIRigidBody.SetFreezeRotationX(true); diff --git a/SHADE_Engine/src/Camera/SHCameraArmComponent.h b/SHADE_Engine/src/Camera/SHCameraArmComponent.h index dfae1cd5..9d8ec853 100644 --- a/SHADE_Engine/src/Camera/SHCameraArmComponent.h +++ b/SHADE_Engine/src/Camera/SHCameraArmComponent.h @@ -10,7 +10,7 @@ namespace SHADE { - class SHAABB; + class SHBox; class SHRay; class SH_API SHCameraArmComponent final: public SHComponent diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 4fb1d5cc..6feece48 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -10,7 +10,7 @@ #include "Scene/SHSceneManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.h" -#include "Math/Geometry/SHAABB.h" +#include "Math/Geometry/SHBox.h" #include "Math/SHRay.h" #include "Physics/System/SHPhysicsSystem.h" @@ -186,20 +186,20 @@ namespace SHADE //SHLOG_INFO("Ray position: {},{},{} direction:{},{},{}",pivot.ray.position.x, pivot.ray.position.y, pivot.ray.position.z,pivot.ray.direction.x, pivot.ray.direction.y, pivot.ray.direction.z) - //auto result = physicsSystem->Raycast(pivot.ray ); - //if (result && result.distance < pivot.GetArmLength()) - //{ - // - // SHVec3 newOffset = SHVec3{ 0.0f,0.0f, result.distance * 0.8f }; - // newOffset = SHVec3::RotateX(newOffset, -(SHMath::DegreesToRadians(pivot.GetPitch()))); - // newOffset = SHVec3::RotateY(newOffset, (SHMath::DegreesToRadians(pivot.GetYaw()))); - // pivot.offset = newOffset; - // //SHLOG_INFO("CAMERA COLLISION HIT, {}", result.distance); - //} - //else - //{ - // //SHLOG_INFO("CAMERA COLLISION CANT HIT CAMERA"); - //} + auto result = physicsSystem->Raycast(pivot.ray ); + if (result && result.distance < pivot.GetArmLength()) + { + + SHVec3 newOffset = SHVec3{ 0.0f,0.0f, result.distance * 0.8f }; + newOffset = SHVec3::RotateX(newOffset, -(SHMath::DegreesToRadians(pivot.GetPitch()))); + newOffset = SHVec3::RotateY(newOffset, (SHMath::DegreesToRadians(pivot.GetYaw()))); + pivot.offset = newOffset; + //SHLOG_INFO("CAMERA COLLISION HIT, {}", result.distance); + } + else + { + //SHLOG_INFO("CAMERA COLLISION CANT HIT CAMERA"); + } diff --git a/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp b/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp index f5f08674..1c603c57 100644 --- a/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp +++ b/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp @@ -162,7 +162,7 @@ namespace SHADE //SHSceneNode* parentNode = entityVec[eIndex]->GetSceneNode()->GetParent(); - //SHSceneGraph::removeChild(parentNode,entityVec[eIndex].get()); + //SHSceneGraph::RemoveChild(parentNode,entityVec[eIndex].get()); //TODO remove from parent and recursively delete child. diff --git a/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp index f28e29c5..8169aa5c 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ColliderTagPanel/SHColliderTagPanel.cpp @@ -1,7 +1,7 @@ #include "SHpch.h" #include "SHColliderTagPanel.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" +#include "Physics/Collision/SHCollisionTagMatrix.h" #include "Editor/SHEditorWidgets.hpp" namespace SHADE @@ -15,7 +15,7 @@ namespace SHADE ImGui::TableNextRow(); ImGui::PushID("CollisionTagNames"); - for (int i = SHCollisionTag::NUM_LAYERS; i >= 1; --i) + for (int i = SHCollisionTag::NUM_LAYERS; i >= 0; --i) { ImGui::TableNextColumn(); if(i == SHCollisionTag::NUM_LAYERS) continue; @@ -29,7 +29,7 @@ namespace SHADE ImGui::PopID(); } ImGui::PopID(); - for (int i = 0; i < SHCollisionTag::NUM_LAYERS - 1; ++i) + for (int i = 0; i < SHCollisionTag::NUM_LAYERS; ++i) { std::string tagName = SHCollisionTagMatrix::GetTagName(i); auto tag = SHCollisionTagMatrix::GetTag(i); @@ -53,8 +53,8 @@ namespace SHADE tagName2 = std::to_string(idx); ImGui::TableNextColumn(); - if(i == idx) - continue; + //if(i == idx) + // continue; std::string label = std::format("##{} vs {}", tagName, tagName2); SHEditorWidgets::CheckBox(label, [tag, &idx]{return tag->GetLayerState(idx);}, [tag, i, idx](bool const& value){tag->SetLayerState(idx, value); SHCollisionTagMatrix::GetTag(idx)->SetLayerState(i, value);}, label.substr(2)); } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 17dfdd6a..3e1fdc44 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -18,11 +18,10 @@ #include "Physics/Interface/SHColliderComponent.h" #include "Reflection/SHReflectionMetadata.h" #include "Resource/SHResourceManager.h" +#include "Physics/Collision/SHCollisionTagMatrix.h" #include "Serialization/SHSerializationHelper.hpp" #include "Tools/Utilities/SHClipboardUtilities.h" #include "SHInspectorCommands.h" -#include "Physics/Collision/SHCompositeCollider.h" -#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" namespace SHADE { template @@ -256,34 +255,31 @@ namespace SHADE if(rbType == SHRigidBodyComponent::Type::DYNAMIC) //Dynamic only fields { - SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetIsGravityEnabled(value);}, "Whether Gravity is enabled for this body"); - SHEditorWidgets::DragFloat("Gravity Scale", [component] { return component->GetGravityScale(); }, [component](float const& value) { component->SetGravityScale(value); }, "Per-object Gravity Scale", 0.1f, 0.0f); - - SHEditorWidgets::CheckBox("Auto Mass", [component]{return component->GetAutoMass();}, [component](bool const& value){component->SetAutoMass(value);}, "If mass should be automatically computed. Setting mass will turn this off."); - SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); + SHEditorWidgets::CheckBox("Use Gravity", [component]{return component->IsGravityEnabled();}, [component](bool const& value){component->SetGravityEnabled(value);}, "Gravity"); + //SHEditorWidgets::DragFloat("Mass", [component] {return component->GetMass(); }, [component](float const& value) {component->SetMass(value); }, "Mass"); } if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { - SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag", 0.1f, 0.0f); - SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag", 0.1f, 0.0f); + SHEditorWidgets::DragFloat("Drag", [component] {return component->GetDrag(); }, [component](float const& value) {component->SetDrag(value); }, "Drag"); + SHEditorWidgets::DragFloat("Angular Drag", [component] {return component->GetAngularDrag(); }, [component](float const& value) {component->SetAngularDrag(value); }, "Angular Drag"); - SHEditorWidgets::CheckBox("Interpolate", [component] {return component->IsInterpolating(); }, [component](bool const& value) {component->SetInterpolate(value); }, "If the position between frames should be interpolated."); + SHEditorWidgets::CheckBox("Interpolate", [component] {return component->IsInterpolating(); }, [component](bool const& value) {component->SetInterpolate(value); }, "Interpolate"); SHEditorWidgets::BeginPanel(std::format("{} Constraints", ICON_FA_LOCK).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); SHEditorWidgets::TextLabel("Freeze Position"); ImGui::PushID("FreezePos"); - SHEditorWidgets::CheckBox("X", [component] {return component->GetFreezePositionX(); }, [component](bool const& value) {component->SetFreezePositionX(value); }, "Stops any displacement along the X-axis."); ImGui::SameLine(); - SHEditorWidgets::CheckBox("Y", [component] {return component->GetFreezePositionY(); }, [component](bool const& value) {component->SetFreezePositionY(value); }, "Stops any displacement along the Y-axis."); ImGui::SameLine(); - SHEditorWidgets::CheckBox("Z", [component] {return component->GetFreezePositionZ(); }, [component](bool const& value) {component->SetFreezePositionZ(value); }, "Stops any displacement along the Z-axis."); + SHEditorWidgets::CheckBox("X", [component] {return component->GetFreezePositionX(); }, [component](bool const& value) {component->SetFreezePositionX(value); }, "Freeze Position - X"); ImGui::SameLine(); + SHEditorWidgets::CheckBox("Y", [component] {return component->GetFreezePositionY(); }, [component](bool const& value) {component->SetFreezePositionY(value); }, "Freeze Position - Y"); ImGui::SameLine(); + SHEditorWidgets::CheckBox("Z", [component] {return component->GetFreezePositionZ(); }, [component](bool const& value) {component->SetFreezePositionZ(value); }, "Freeze Position - Z"); ImGui::PopID(); SHEditorWidgets::TextLabel("Freeze Rotation"); ImGui::PushID("FreezeRot"); - SHEditorWidgets::CheckBox("X", [component] {return component->GetFreezeRotationX(); }, [component](bool const& value) {component->SetFreezeRotationX(value); }, "Stops any rotation about the X-axis."); ImGui::SameLine(); - SHEditorWidgets::CheckBox("Y", [component] {return component->GetFreezeRotationY(); }, [component](bool const& value) {component->SetFreezeRotationY(value); }, "Stops any rotation about the Y-axis."); ImGui::SameLine(); - SHEditorWidgets::CheckBox("Z", [component] {return component->GetFreezeRotationZ(); }, [component](bool const& value) {component->SetFreezeRotationZ(value); }, "Stops any rotation about the Z-axis."); + SHEditorWidgets::CheckBox("X", [component] {return component->GetFreezeRotationX(); }, [component](bool const& value) {component->SetFreezeRotationX(value); }, "Freeze Rotation - X"); ImGui::SameLine(); + SHEditorWidgets::CheckBox("Y", [component] {return component->GetFreezeRotationY(); }, [component](bool const& value) {component->SetFreezeRotationY(value); }, "Freeze Rotation - Y"); ImGui::SameLine(); + SHEditorWidgets::CheckBox("Z", [component] {return component->GetFreezeRotationZ(); }, [component](bool const& value) {component->SetFreezeRotationZ(value); }, "Freeze Rotation - Z"); ImGui::PopID(); SHEditorWidgets::EndPanel(); @@ -292,15 +288,9 @@ namespace SHADE //Debug Info (Read-Only) if(ImGui::CollapsingHeader("Debug Information", ImGuiTreeNodeFlags_DefaultOpen))//Dynamic or Kinematic only fields { + SHEditorWidgets::DragFloat("Mass", [component] { return component->GetMass(); }, [](float value){}, "Mass", 0.1f, 0.0f, std::numeric_limits::infinity(), "%.3f", ImGuiSliderFlags_ReadOnly); SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component] {return component->GetPosition(); }, [](SHVec3 const& value) {}, false, "Position", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); - SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] - { - // Convert it to degrees... - auto rot = component->GetRotation(); - for (size_t i = 0; i < SHVec3::SIZE; ++i) - rot[i] = SHMath::RadiansToDegrees(rot[i]); - return rot; - }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); + SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, [component] {return component->GetRotation(); }, [](SHVec3 const& value) {}, false, "Rotation", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); if (rbType == SHRigidBodyComponent::Type::DYNAMIC || rbType == SHRigidBodyComponent::Type::KINEMATIC) //Dynamic or Kinematic only fields { SHEditorWidgets::DragVec3("Velocity", { "X", "Y", "Z" }, [component] {return component->GetLinearVelocity(); }, [](SHVec3 const& value) {}, false, "Linear Velocity", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); @@ -310,7 +300,7 @@ namespace SHADE { SHEditorWidgets::DragVec3("Force", { "X", "Y", "Z" }, [component] {return component->GetForce(); }, [](SHVec3 const& value) {}, false, "Force", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); SHEditorWidgets::DragVec3("Torque", { "X", "Y", "Z" }, [component] {return component->GetTorque(); }, [](SHVec3 const& value) {}, false, "Torque", 0.1f, "%.3f", 0.0f, 0.0f, ImGuiSliderFlags_ReadOnly); - SHEditorWidgets::CheckBox("Is Asleep", [component] {return component->IsSleeping(); }, [](bool value) {}, "If the Rigid Body is asleep"); + SHEditorWidgets::CheckBox("Is Asleep", [component] {return component->GetIsSleeping(); }, [](bool value) {}, "If the Rigid Body is asleep"); } } @@ -342,67 +332,64 @@ namespace SHADE { DrawContextMenu(component); - SHEditorWidgets::CheckBox("Draw Colliders", [component] { return component->GetDebugDrawState(); }, [component](bool value) { component->SetDebugDrawState(value); }); - - auto* collisionShapes = component->GetCollisionShapes(); - int const size = collisionShapes ? static_cast(collisionShapes->size()) : 0; - ImGui::BeginChild("Collision Shapes", { 0.0f, collisionShapes->empty() ? 1.0f : 250.0f }, true); + auto& colliders = component->GetCollisionShapes(); + int const size = static_cast(colliders.size()); + ImGui::BeginChild("Collision Shapes", { 0.0f, colliders.empty() ? 1.0f : 250.0f }, true); std::optional colliderToDelete{ std::nullopt }; for (int i{}; i < size; ++i) { ImGui::PushID(i); - SHCollisionShape* shape = component->GetCollisionShape(i); + SHCollisionShape* collider = &component->GetCollisionShape(i); auto cursorPos = ImGui::GetCursorPos(); - //collider->IsTrigger - if (shape->GetType() == SHCollisionShape::Type::BOX) + + if (collider->GetType() == SHCollisionShape::Type::BOX) { SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - auto* box = reinterpret_cast(shape); + const auto* BOX = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragVec3 ( "Half Extents", { "X", "Y", "Z" }, - [box] { return box->GetRelativeExtents(); }, - [box](SHVec3 const& vec) { box->SetRelativeExtents(vec); }); + [BOX] { return BOX->GetRelativeExtents(); }, + [collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); }); } - else if (shape->GetType() == SHCollisionShape::Type::SPHERE) + else if (collider->GetType() == SHCollisionShape::Type::SPHERE) { SHEditorWidgets::BeginPanel(std::format("{} Sphere #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - auto* sphere = reinterpret_cast(shape); + const auto* SPHERE = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragFloat ( "Radius", - [sphere] { return sphere->GetRelativeRadius(); }, - [sphere](float const& value) { sphere->SetRelativeRadius(value); }); + [SPHERE] { return SPHERE->GetRelativeRadius(); }, + [collider](float const& value) { collider->SetBoundingSphere(value); }); } - else if (shape->GetType() == SHCollisionShape::Type::CAPSULE) + else if (collider->GetType() == SHCollisionShape::Type::CAPSULE) { } { - SHEditorWidgets::CheckBox("Is Trigger", [shape] { return shape->IsTrigger(); }, [shape](bool value) { shape->SetIsTrigger(value); }); - SHEditorWidgets::ComboBox("Tag", collisionTagNames, [shape]{return SHCollisionTagMatrix::GetTagIndex(shape->GetCollisionTag().GetName());}, [shape](int const& value){shape->SetCollisionTag(SHCollisionTagMatrix::GetTag(value));}); - + SHEditorWidgets::CheckBox("Is Trigger", [collider] { return collider->IsTrigger(); }, [collider](bool value) { collider->SetIsTrigger(value); }); + SHEditorWidgets::ComboBox("Tag", collisionTagNames, [collider]{return SHCollisionTagMatrix::GetTagIndex(collider->GetCollisionTag().GetName());}, [collider](int const& value){collider->SetCollisionTag(SHCollisionTagMatrix::GetTag(value));}); if(ImGui::CollapsingHeader("Physics Material")) { - SHEditorWidgets::DragFloat("Friction", [shape] { return shape->GetFriction(); }, [shape](float value) { shape->SetFriction(value); }, "Friction", 0.05f, 0.0f, 1.0f); - SHEditorWidgets::DragFloat("Bounciness", [shape] { return shape->GetBounciness(); }, [shape](float value) { shape->SetBounciness(value); }, "Bounciness", 0.05f, 0.0f, 1.0f); - SHEditorWidgets::DragFloat("Mass Density", [shape] { return shape->GetDensity(); }, [shape](float value) { shape->SetDensity(value); }, "Mass Density", 0.1f, 0.0f); + SHEditorWidgets::DragFloat("Friction", [collider] { return collider->GetFriction(); }, [collider](float value) { collider->SetFriction(value); }, "Friction", 0.05f, 0.0f, 1.0f); + SHEditorWidgets::DragFloat("Bounciness", [collider] { return collider->GetBounciness(); }, [collider](float value) { collider->SetBounciness(value); }, "Bounciness", 0.05f, 0.0f, 1.0f); + SHEditorWidgets::DragFloat("Mass Density", [collider] { return collider->GetDensity(); }, [collider](float value) { collider->SetDensity(value); }, "Mass Density", 0.1f, 0.0f); } SHEditorWidgets::BeginPanel("Offsets",{ ImGui::GetContentRegionAvail().x, 30.0f }); - SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [&shape] {return shape->GetPositionOffset(); }, [&shape](SHVec3 const& vec) {shape->SetPositionOffset(vec); }); + SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [&collider] {return collider->GetPositionOffset(); }, [&collider](SHVec3 const& vec) {collider->SetPositionOffset(vec); }); SHEditorWidgets::DragVec3("Rotation", { "X", "Y", "Z" }, - [&shape] + [&collider] { - auto offset = shape->GetRotationOffset(); + auto offset = collider->GetRotationOffset(); return offset; }, - [&shape](SHVec3 const& vec) + [&collider](SHVec3 const& vec) { - shape->SetRotationOffset(vec); + collider->SetRotationOffset(vec); }, true); SHEditorWidgets::EndPanel(); } @@ -417,24 +404,21 @@ namespace SHADE } if (colliderToDelete.has_value()) { - component->GetCollider()->RemoveCollisionShape(colliderToDelete.value()); + component->RemoveCollider(colliderToDelete.value()); } ImGui::EndChild(); - // TODO: Handle differences between composite & hull collider if (ImGui::BeginMenu("Add Collider")) { int newColl = -1; if (ImGui::Selectable("Box Collider")) { - auto* compositeCollider = dynamic_cast(component->GetCollider()); - compositeCollider->AddBoxCollisionShape(SHVec3::One); + newColl = component->AddBoundingBox(); } if (ImGui::Selectable("Sphere Collider")) { - auto* compositeCollider = dynamic_cast(component->GetCollider()); - compositeCollider->AddSphereCollisionShape(1.0f); + newColl = component->AddBoundingSphere(); } //No idea why this doesn't work diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index c52d05e4..3fe9ceb5 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -25,7 +25,6 @@ #include "Serialization/SHSerialization.h" #include "Serialization/Configurations/SHConfigurationManager.h" #include "Editor/EditorWindow/SHEditorWindowManager.h" -#include "Physics/System/SHPhysicsDebugDrawSystem.h" const std::string LAYOUT_FOLDER_PATH{ std::string(ASSET_ROOT) + "/Editor/Layouts" }; @@ -89,7 +88,6 @@ namespace SHADE DrawThemeMenu(); DrawLayoutMenu(); DrawApplicationConfig(); - DrawPhysicsSettings(); std::string const sceneName{std::format("Current Scene: {}",SHSceneManager::GetSceneName().data())}; auto const size = ImGui::CalcTextSize(sceneName.data()); @@ -306,32 +304,4 @@ namespace SHADE ImGui::EndMenu(); } } - - void SHEditorMenuBar::DrawPhysicsSettings() noexcept - { - if (ImGui::BeginMenu("Physics Settings")) - { - if (auto* physicsDebugDraw = SHSystemManager::GetSystem()) - { - bool drawColliders = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDERS); - if (ImGui::Checkbox("Draw Colliders", &drawColliders)) - physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDERS, drawColliders); - - bool drawContactPoints = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS); - if (ImGui::Checkbox("Draw Contact Points", &drawContactPoints)) - physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::CONTACTS, drawContactPoints); - - bool drawRays = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS); - if (ImGui::Checkbox("Draw Rays", &drawRays)) - physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS, drawRays); - - bool drawBroadphase = physicsDebugDraw->GetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE); - if (ImGui::Checkbox("Draw Broadphase", &drawBroadphase)) - physicsDebugDraw->SetFlagState(SHPhysicsDebugDrawSystem::DebugDrawFlags::BROADPHASE, drawBroadphase); - } - - ImGui::EndMenu(); - } - } - }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h index 4891dc5b..77ebcf55 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h @@ -25,7 +25,6 @@ namespace SHADE void DrawThemeMenu() noexcept; void DrawLayoutMenu() noexcept; void DrawApplicationConfig() noexcept; - void DrawPhysicsSettings() noexcept; float menuBarHeight = 20.0f; std::vector layoutPaths; diff --git a/SHADE_Engine/src/Events/SHEventDefines.h b/SHADE_Engine/src/Events/SHEventDefines.h index ca29d416..c090cd69 100644 --- a/SHADE_Engine/src/Events/SHEventDefines.h +++ b/SHADE_Engine/src/Events/SHEventDefines.h @@ -24,6 +24,4 @@ constexpr SHEventIdentifier SH_SCENE_EXIT_PRE { 15 }; constexpr SHEventIdentifier SH_SCENE_EXIT_POST { 16 }; constexpr SHEventIdentifier SH_GRAPHICS_LIGHT_ENABLE_SHADOW_EVENT { 17 }; constexpr SHEventIdentifier SH_BUTTON_CLICK_EVENT { 18 }; -constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_DRAW_EVENT { 19 }; - diff --git a/SHADE_Engine/src/Math/Geometry/SHAABB.h b/SHADE_Engine/src/Math/Geometry/SHAABB.h deleted file mode 100644 index 9b62c85b..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHAABB.h +++ /dev/null @@ -1,173 +0,0 @@ -/**************************************************************************************** - * \file SHAABB.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a 3-Dimensional Axis Aligned Bounding Box - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "Math/SHRay.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a 3D Axis-Aligned Bounding Box. - */ - class SH_API SHAABB : private DirectX::BoundingBox - { - public: - /*---------------------------------------------------------------------------------*/ - /* Static Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr size_t NUM_VERTICES = 8; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - ~SHAABB () noexcept = default; - - SHAABB () noexcept; - SHAABB (const SHVec3& center, const SHVec3& halfExtents) noexcept; - SHAABB (const SHAABB& rhs) noexcept; - SHAABB (SHAABB&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHAABB& operator= (const SHAABB& rhs) noexcept; - SHAABB& operator= (SHAABB&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] SHVec3 GetExtents () const noexcept; - [[nodiscard]] SHVec3 GetMin () const noexcept; - [[nodiscard]] SHVec3 GetMax () const noexcept; - [[nodiscard]] std::vector GetVertices () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetCenter (const SHVec3& newCenter) noexcept; - void SetExtents (const SHVec3& newHalfExtents) noexcept; - void SetMin (const SHVec3& min) noexcept; - void SetMax (const SHVec3& max) noexcept; - void SetMinMax (const SHVec3& min, const SHVec3& max) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Checks if a point is inside the aabb. - * @param point - * The point to check. - * @return - * True if the point is inside the aabb. - */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept; - - /** - * @brief - * Casts a ray against the aabb. - * @param ray - * The ray to cast. - * @return - * The result of the raycast.
- * See the corresponding header for the contents of the raycast result object. - */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept; - - /** - * @brief - * Checks if an entire other aabb is contained by this aabb. - * @param rhs - * The aabb to check. - * @return - * True if the other sphere is completely contained by this aabb. - */ - [[nodiscard]] bool Contains (const SHAABB& rhs) const noexcept; - - /** - * @brief - * Calculates the volume of the aabb. - */ - [[nodiscard]] float Volume () const noexcept; - - /** - * @brief - * Calculates the surface area of the aabb. - */ - [[nodiscard]] float SurfaceArea () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Static Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Combines two aabbs to form a larger aabb. - * If one aabb is completely contained by the other, the result is the larger aabb. - * @return - * The combined aabb. - */ - [[nodiscard]] static SHAABB Combine (const SHAABB& lhs, const SHAABB& rhs) noexcept; - - /** - * @brief - * Checks if two aabbs are intersecting. - * @return - * True if they are intersecting. - */ - [[nodiscard]] static bool Intersect (const SHAABB& lhs, const SHAABB& rhs) noexcept; - - /** - * @brief - * Builds a single aabb from multiple aabbs. - * @param spheres - * The set of aabbs to build from. - * @param numSpheres - * The number of aabbs in the set to build from. - * @return - * An aabb that contains all the spheres in the set. - */ - [[nodiscard]] static SHAABB BuildFromBoxes (const SHAABB* boxes, size_t numBoxes) noexcept; - - /** - * @brief - * Builds a aabb from a set of vertices. - * @param vertices - * The vertices to build a aabb from. - * @param numVertices - * The number of vertices in the set to build from. - * @param stride - * The stride between each vertex, in the instance there is data in between each - * vertex that does not define the geometry of the object. - * @return - * An aabb that contains all the vertices in the set. - */ - [[nodiscard]] static SHAABB BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; - }; - - -} // namespace SHADE - diff --git a/SHADE_Engine/src/Math/Geometry/SHAABB.cpp b/SHADE_Engine/src/Math/Geometry/SHBox.cpp similarity index 60% rename from SHADE_Engine/src/Math/Geometry/SHAABB.cpp rename to SHADE_Engine/src/Math/Geometry/SHBox.cpp index f5d9fd60..7261749b 100644 --- a/SHADE_Engine/src/Math/Geometry/SHAABB.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHBox.cpp @@ -1,5 +1,5 @@ /**************************************************************************************** - * \file SHAABB.cpp + * \file SHBox.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 * \brief Implementation for a 3-Dimensional Axis Aligned Bounding Box * @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHAABB.h" +#include "SHBox.h" // Project Headers #include "Math/SHMathHelpers.h" #include "Math/SHRay.h" @@ -24,52 +24,75 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHAABB::SHAABB() noexcept + SHBox::SHBox() noexcept + : RelativeExtents { SHVec3::One } { - Extents = SHVec3::One * 0.5f; + type = Type::BOX; } - SHAABB::SHAABB(const SHVec3& c, const SHVec3& hE) noexcept + SHBox::SHBox(const SHVec3& c, const SHVec3& hE) noexcept + : RelativeExtents { SHVec3::One } { - Center = c; + type = Type::BOX; + + Center = c; Extents = hE; } - SHAABB::SHAABB(const SHAABB& rhs) noexcept + SHBox::SHBox(const SHBox& rhs) noexcept { if (this == &rhs) return; - Center = rhs.Center; - Extents = rhs.Extents; + type = Type::BOX; + + Center = rhs.Center; + Extents = rhs.Extents; + RelativeExtents = rhs.RelativeExtents; } - SHAABB::SHAABB(SHAABB&& rhs) noexcept + SHBox::SHBox(SHBox&& rhs) noexcept { - Center = rhs.Center; - Extents = rhs.Extents; + type = Type::BOX; + + Center = rhs.Center; + Extents = rhs.Extents; + RelativeExtents = rhs.RelativeExtents; } /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHAABB& SHAABB::operator=(const SHAABB& rhs) noexcept + SHBox& SHBox::operator=(const SHBox& rhs) noexcept { - if (this != &rhs) + if (rhs.type != Type::BOX) { - Center = rhs.Center; - Extents = rhs.Extents; + SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") + } + else if (this != &rhs) + { + Center = rhs.Center; + Extents = rhs.Extents; + RelativeExtents = rhs.RelativeExtents; } return *this; } - SHAABB& SHAABB::operator=(SHAABB&& rhs) noexcept + SHBox& SHBox::operator=(SHBox&& rhs) noexcept { - Center = rhs.Center; - Extents = rhs.Extents; + if (rhs.type != Type::BOX) + { + SHLOG_WARNING("Cannot assign a non-bounding box to a bounding box!") + } + else + { + Center = rhs.Center; + Extents = rhs.Extents; + RelativeExtents = rhs.RelativeExtents; + } return *this; } @@ -78,22 +101,27 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHVec3 SHAABB::GetCenter() const noexcept + SHVec3 SHBox::GetCenter() const noexcept { return Center; } - SHVec3 SHAABB::GetExtents() const noexcept + SHVec3 SHBox::GetWorldExtents() const noexcept { return Extents; } - SHVec3 SHAABB::GetMin() const noexcept + const SHVec3& SHBox::GetRelativeExtents() const noexcept + { + return RelativeExtents; + } + + SHVec3 SHBox::GetMin() const noexcept { return SHVec3{ Center.x - Extents.x, Center.y - Extents.y, Center.z - Extents.z }; } - SHVec3 SHAABB::GetMax() const noexcept + SHVec3 SHBox::GetMax() const noexcept { return SHVec3{ Center.x + Extents.x, Center.y + Extents.y, Center.z + Extents.z }; } @@ -102,17 +130,22 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHAABB::SetCenter(const SHVec3& newCenter) noexcept + void SHBox::SetCenter(const SHVec3& newCenter) noexcept { Center = newCenter; } - void SHAABB::SetExtents(const SHVec3& newHalfExtents) noexcept + void SHBox::SetWorldExtents(const SHVec3& newWorldExtents) noexcept { - Extents = newHalfExtents; + Extents = newWorldExtents; } - void SHAABB::SetMin(const SHVec3& min) noexcept + void SHBox::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept + { + RelativeExtents = newRelativeExtents; + } + + void SHBox::SetMin(const SHVec3& min) noexcept { const SHVec3 MAX = GetMax(); @@ -120,7 +153,7 @@ namespace SHADE Extents = SHVec3::Abs((MAX - min) * 0.5f); } - void SHAABB::SetMax(const SHVec3& max) noexcept + void SHBox::SetMax(const SHVec3& max) noexcept { const SHVec3 MIN = GetMin(); @@ -128,13 +161,13 @@ namespace SHADE Extents = SHVec3::Abs((max - MIN) * 0.5f); } - void SHAABB::SetMinMax(const SHVec3& min, const SHVec3& max) noexcept + void SHBox::SetMinMax(const SHVec3& min, const SHVec3& max) noexcept { Center = SHVec3::Lerp(min, max, 0.5f); Extents = SHVec3::Abs((max - min) * 0.5f); } - std::vector SHAABB::GetVertices() const noexcept + std::vector SHBox::GetVertices() const noexcept { std::vector vertices{ 8 }; GetCorners(vertices.data()); @@ -145,12 +178,12 @@ namespace SHADE /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - bool SHAABB::TestPoint(const SHVec3& point) const noexcept + bool SHBox::TestPoint(const SHVec3& point) const noexcept { return BoundingBox::Contains(point); } - SHRaycastResult SHAABB::Raycast(const SHRay& ray) const noexcept + SHRaycastResult SHBox::Raycast(const SHRay& ray) const noexcept { SHRaycastResult result; @@ -159,24 +192,22 @@ namespace SHADE { result.position = ray.position + ray.direction * result.distance; result.angle = SHVec3::Angle(ray.position, result.position); - - // TODO: Compute normal } return result; } - bool SHAABB::Contains(const SHAABB& rhs) const noexcept + bool SHBox::Contains(const SHBox& rhs) const noexcept { - return BoundingBox::Contains(rhs) == CONTAINS; + return BoundingBox::Contains(rhs); } - float SHAABB::Volume() const noexcept + float SHBox::Volume() const noexcept { return 8.0f * (Extents.x * Extents.y * Extents.z); } - float SHAABB::SurfaceArea() const noexcept + float SHBox::SurfaceArea() const noexcept { return 8.0f * ((Extents.x * Extents.y) + (Extents.x * Extents.z) @@ -187,21 +218,21 @@ namespace SHADE /* Static Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHAABB SHAABB::Combine(const SHAABB& lhs, const SHAABB& rhs) noexcept + SHBox SHBox::Combine(const SHBox& lhs, const SHBox& rhs) noexcept { - SHAABB result; + SHBox result; CreateMerged(result, lhs, rhs); return result; } - bool SHAABB::Intersect(const SHAABB& lhs, const SHAABB& rhs) noexcept + bool SHBox::Intersect(const SHBox& lhs, const SHBox& rhs) noexcept { return lhs.Intersects(rhs); } - SHAABB SHAABB::BuildFromBoxes(const SHAABB* boxes, size_t numBoxes) noexcept + SHBox SHBox::BuildFromBoxes(const SHBox* boxes, size_t numBoxes) noexcept { - SHAABB result; + SHBox result; for (size_t i = 1; i < numBoxes; ++i) CreateMerged(result, boxes[i - 1], boxes[i]); @@ -209,9 +240,9 @@ namespace SHADE return result; } - SHAABB SHAABB::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept + SHBox SHBox::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept { - SHAABB result; + SHBox result; CreateFromPoints(result, numVertices, vertices, stride); return result; } diff --git a/SHADE_Engine/src/Math/Geometry/SHBox.h b/SHADE_Engine/src/Math/Geometry/SHBox.h new file mode 100644 index 00000000..a0ca9458 --- /dev/null +++ b/SHADE_Engine/src/Math/Geometry/SHBox.h @@ -0,0 +1,105 @@ +/**************************************************************************************** + * \file SHBox.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a 3-Dimensional Axis Aligned Bounding Box + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "SHShape.h" +#include "SH_API.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + class SH_API SHBox : public SHShape, + private DirectX::BoundingBox + { + public: + /*---------------------------------------------------------------------------------*/ + /* Static Data Members */ + /*---------------------------------------------------------------------------------*/ + + static constexpr size_t NUM_VERTICES = 8; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + ~SHBox () override = default; + + SHBox () noexcept; + SHBox (const SHVec3& center, const SHVec3& halfExtents) noexcept; + SHBox (const SHBox& rhs) noexcept; + SHBox (SHBox&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHBox& operator= (const SHBox& rhs) noexcept; + SHBox& operator= (SHBox&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; + [[nodiscard]] const SHVec3& GetRelativeExtents () const noexcept; + [[nodiscard]] SHVec3 GetMin () const noexcept; + [[nodiscard]] SHVec3 GetMax () const noexcept; + [[nodiscard]] std::vector GetVertices () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetCenter (const SHVec3& newCenter) noexcept; + void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; + void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; + void SetMin (const SHVec3& min) noexcept; + void SetMax (const SHVec3& max) noexcept; + void SetMinMax (const SHVec3& min, const SHVec3& max) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; + [[nodiscard]] SHRaycastResult Raycast(const SHRay& ray) const noexcept override; + + [[nodiscard]] bool Contains (const SHBox& rhs) const noexcept; + [[nodiscard]] float Volume () const noexcept; + [[nodiscard]] float SurfaceArea () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Static Function Members */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] static SHBox Combine (const SHBox& lhs, const SHBox& rhs) noexcept; + [[nodiscard]] static bool Intersect (const SHBox& lhs, const SHBox& rhs) noexcept; + [[nodiscard]] static SHBox BuildFromBoxes (const SHBox* boxes, size_t numBoxes) noexcept; + [[nodiscard]] static SHBox BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + SHVec3 RelativeExtents; + }; + + +} // namespace SHADE + diff --git a/SHADE_Engine/src/Math/Geometry/SHPlane.cpp b/SHADE_Engine/src/Math/Geometry/SHPlane.cpp deleted file mode 100644 index 1ffbfaba..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHPlane.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/**************************************************************************************** - * \file SHPlane.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a plane. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -#include - -// Primary Header -#include "SHPlane.h" - -#include "Input/SHInputManager.h" -#include "Math/SHMathHelpers.h" - -using namespace DirectX; - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPlane::SHPlane() noexcept - : planeEquation { SHVec3::One } - { - planeEquation.w = 0.0f; - } - - SHPlane::SHPlane(const SHVec3& point, const SHVec3& normal) noexcept - { - XMStoreFloat4(&planeEquation, XMPlaneFromPointNormal(point, normal)); - } - - SHPlane::SHPlane(float a, float b, float c, float d) noexcept - : planeEquation { a, b, c, d } - {} - - SHPlane::SHPlane(const SHVec3& normal, float distance) noexcept - : planeEquation { normal } - { - planeEquation.w = distance; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHPlane::operator==(const SHPlane& rhs) const noexcept - { - return XMPlaneEqual(planeEquation, rhs.planeEquation); - } - - bool SHPlane::operator!=(const SHPlane& rhs) const noexcept - { - return XMPlaneNotEqual(planeEquation, rhs.planeEquation); - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHVec3 SHPlane::GetNormal() const noexcept - { - return SHVec3{ planeEquation.x, planeEquation.y, planeEquation.z }; - } - - float SHPlane::GetDistance() const noexcept - { - return planeEquation.w; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPlane::SetNormal(const SHVec3& normal) noexcept - { - planeEquation.x = normal.x; - planeEquation.y = normal.y; - planeEquation.z = normal.z; - } - - void SHPlane::SetDistance(float distance) noexcept - { - planeEquation.w = distance; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHPlane::TestPoint(const SHVec3& point) const noexcept - { - const float DISTANCE = SignedDistance(point); - return SHMath::CompareFloat(DISTANCE, 0.0f); - } - - SHRaycastResult SHPlane::Raycast(const SHRay& ray) const noexcept - { - SHRaycastResult result; - - const SHVec3 N = GetNormal(); - - // Check if ray is parallel to plane - const float N_DOT_D = N.Dot(ray.direction); - if (SHMath::CompareFloat(N_DOT_D, 0.0f)) - { - result.hit = false; - return result; - } - - const float DIST = (planeEquation.w - N.Dot(ray.position)) / N_DOT_D; - if (DIST < 0.0f || !SHMath::CompareFloat(DIST, 0.0f)) - { - result.hit = false; - return result; - } - - result.hit = true; - result.distance = DIST; - result.position = ray.position + ray.direction * DIST; - result.angle = SHVec3::Angle(ray.position, result.position); - - // The normal is either the plane's normal or the reverse if the ray came from below - result.normal = N_DOT_D < 0.0f ? -N : N; - - return result; - } - - float SHPlane::SignedDistance(const SHVec3& point) const noexcept - { - return XMVectorGetX(XMPlaneDotCoord(planeEquation, point)); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHPlane.h b/SHADE_Engine/src/Math/Geometry/SHPlane.h deleted file mode 100644 index 6593c627..00000000 --- a/SHADE_Engine/src/Math/Geometry/SHPlane.h +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************************** - * \file SHPlane.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a plane. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "Math/SHRay.h" -#include "Math/Vector/SHVec4.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a 3D plane in point-normal form. - */ - class SH_API SHPlane - { - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - ~SHPlane () noexcept = default; - SHPlane (const SHPlane& rhs) noexcept = default; - SHPlane (SHPlane&& rhs) noexcept = default; - - SHPlane () noexcept; - SHPlane (const SHVec3& point, const SHVec3& normal) noexcept; - SHPlane (float a, float b, float c, float d) noexcept; - SHPlane (const SHVec3& normal, float distance) noexcept; - - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHPlane& operator= (const SHPlane& rhs) noexcept = default; - SHPlane& operator= (SHPlane&& rhs) noexcept = default; - - bool operator== (const SHPlane& rhs) const noexcept; - bool operator!= (const SHPlane& rhs) const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] SHVec3 GetNormal () const noexcept; - [[nodiscard]] float GetDistance () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetNormal (const SHVec3& normal) noexcept; - void SetDistance (float distance) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Checks if a point is on the plane. - * @param point - * The point to check. - * @return - * True if the point is on the plane. - */ - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept; - - /** - * @brief - * Casts a ray against the plane. - * @param ray - * The ray to cast. - * @return - * The result of the raycast.
- * See the corresponding header for the contents of the raycast result object. - */ - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept; - - /** - * @brief - * Gets the signed distance from a point to the plane. - * @param point - * The point to check. - * @return - * The signed distance of the point to a plane.
- * If the signed distance is negative, the point is behind the plane.
- * If the signed distance is zero, the point is on the plane.
- * If the signed distance is positive, the point is in front of the plane.
- */ - [[nodiscard]] float SignedDistance (const SHVec3& point) const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Static Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /* - * TODO: - * Transform plane - * Intersection Tests - */ - - private: - - SHVec4 planeEquation; - }; - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsCapsule.cpp b/SHADE_Engine/src/Math/Geometry/SHShape.cpp similarity index 57% rename from SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsCapsule.cpp rename to SHADE_Engine/src/Math/Geometry/SHShape.cpp index c75437cb..2f869029 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsCapsule.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHShape.cpp @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCapsuleVsCapsule.cpp + * \file SHShape.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Detecting Collisions between two capsules + * \brief Implementation for a shape. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -11,25 +11,25 @@ #include // Primary Header -#include "SHCollision.h" - -// Project Headers -#include "Math/SHMathHelpers.h" +#include "SHShape.h" namespace SHADE { /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ + /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - bool SHCollision::CapsuleVsCapsule(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return false; - } + SHShape::SHShape() + : type { Type::NONE } + {} - bool SHCollision::CapsuleVsCapsule(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHShape::Type SHShape::GetType() const noexcept { - return false; + return type; } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h b/SHADE_Engine/src/Math/Geometry/SHShape.h similarity index 57% rename from SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h rename to SHADE_Engine/src/Math/Geometry/SHShape.h index 3146a743..812cb169 100644 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.h +++ b/SHADE_Engine/src/Math/Geometry/SHShape.h @@ -1,8 +1,7 @@ /**************************************************************************************** - * \file SHContactSolver.h + * \file SHShape.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Contact Solver that builds contacct constraints and solves - * them. + * \brief Interface for a shape. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -12,8 +11,9 @@ #pragma once // Project Headers -#include "Constraints/SHContactConstraint.h" -#include "Constraints/SHVelocityState.h" +#include "SH_API.h" +#include "Math/SHRay.h" + namespace SHADE { @@ -21,73 +21,62 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates an object that builds contact constraints and solves them. - */ - class SH_API SHContactSolver + class SH_API SHShape { public: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using VelocityStates = std::unordered_map; - using ContactConstraints = std::unordered_map; + enum class Type + { + BOX + , SPHERE + , CAPSULE + , CONVEX_HULL + + , COUNT + , NONE = -1 + }; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + bool isIntersecting; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHContactSolver () noexcept = default; - ~SHContactSolver () noexcept = default; + virtual ~SHShape () = default; + + SHShape (const SHShape&) = default; + SHShape (SHShape&&) = default; + + SHShape& operator=(const SHShape&) = default; + SHShape& operator=(SHShape&&) = default; + + SHShape(); /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] const VelocityStates& GetVelocities () const noexcept; - [[nodiscard]] const ContactConstraints& GetContantConstraints () const noexcept; + [[nodiscard]] Type GetType () const noexcept; /*---------------------------------------------------------------------------------*/ - /* Member Functions */ + /* Function Members */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Build a contact constraint from a new manifold. - * @param manifold - * A manifold to build a contact constraint from. - */ - void AddContact (const SHCollisionKey& key, const SHManifold& manifold) noexcept; + [[nodiscard]] virtual bool TestPoint (const SHVec3& point) const noexcept = 0; + [[nodiscard]] virtual SHRaycastResult Raycast (const SHRay& ray) const noexcept = 0; - void Reset () noexcept; - - /** - * @brief - * Solves all the contact constraints. - * @param numIterations - * The number of times to iterate over constraints when solving them. - * @param dt - * The delta time of the simulation step. - */ - void SolveContacts (int numIterations, float dt) noexcept; - - private: + protected: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - VelocityStates velocityStates; - ContactConstraints contactConstraints; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - void preSolve (float dt) noexcept; - void solve () noexcept; - + Type type; }; - -} // namespace SHADE +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Geometry/SHSphere.cpp b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp new file mode 100644 index 00000000..54935251 --- /dev/null +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.cpp @@ -0,0 +1,208 @@ +/**************************************************************************************** + * \file SHBoundingSphere.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Bounding Sphere + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHSphere.h" +// Project Headers +#include "Math/SHMathHelpers.h" +#include "Math/SHRay.h" + +using namespace DirectX; + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHSphere::SHSphere() noexcept + : RelativeRadius { 1.0f } + { + type = Type::SPHERE; + } + + SHSphere::SHSphere(const SHVec3& center, float radius) noexcept + : RelativeRadius { 1.0f } + { + type = Type::SPHERE; + + Center = center; + Radius = radius; + } + + SHSphere::SHSphere(const SHSphere& rhs) noexcept + { + if (this == &rhs) + return; + + type = Type::SPHERE; + + Center = rhs.Center; + Radius = rhs.Radius; + RelativeRadius = rhs.RelativeRadius; + } + + SHSphere::SHSphere(SHSphere&& rhs) noexcept + { + type = Type::SPHERE; + + Center = rhs.Center; + Radius = rhs.Radius; + RelativeRadius = rhs.RelativeRadius; + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHSphere& SHSphere::operator=(const SHSphere& rhs) noexcept + { + if (rhs.type != Type::SPHERE) + { + SHLOG_WARNING("Cannot assign a non-sphere to a sphere!") + } + else if (this != &rhs) + { + Center = rhs.Center; + Radius = rhs.Radius; + RelativeRadius = rhs.RelativeRadius; + } + + return *this; + } + + SHSphere& SHSphere::operator=(SHSphere&& rhs) noexcept + { + if (rhs.type != Type::SPHERE) + { + SHLOG_WARNING("Cannot assign a non-sphere to a sphere!") + } + else + { + Center = rhs.Center; + Radius = rhs.Radius; + RelativeRadius = rhs.RelativeRadius; + } + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHVec3 SHSphere::GetCenter() const noexcept + { + return Center; + } + + float SHSphere::GetWorldRadius() const noexcept + { + return Radius; + } + + float SHSphere::GetRelativeRadius() const noexcept + { + return RelativeRadius; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHSphere::SetCenter(const SHVec3& center) noexcept + { + Center = center; + } + + void SHSphere::SetWorldRadius(float newWorldRadius) noexcept + { + Radius = newWorldRadius; + } + + void SHSphere::SetRelativeRadius(float newRelativeRadius) noexcept + { + RelativeRadius = newRelativeRadius; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHSphere::TestPoint(const SHVec3& point) const noexcept + { + return BoundingSphere::Contains(point); + } + + SHRaycastResult SHSphere::Raycast(const SHRay& ray) const noexcept + { + SHRaycastResult result; + + result.hit = Intersects(ray.position, ray.direction, result.distance); + if (result.hit) + { + result.position = ray.position + ray.direction * result.distance; + result.angle = SHVec3::Angle(ray.position, result.position); + } + + return result; + } + + bool SHSphere::Contains(const SHSphere& rhs) const noexcept + { + return BoundingSphere::Contains(rhs); + } + + float SHSphere::Volume() const noexcept + { + return (4.0f / 3.0f) * SHMath::PI * (Radius * Radius * Radius); + } + + float SHSphere::SurfaceArea() const noexcept + { + return 4.0f * SHMath::PI * (Radius * Radius); + } + + /*-----------------------------------------------------------------------------------*/ + /* Static Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHSphere SHSphere::Combine(const SHSphere& lhs, const SHSphere& rhs) noexcept + { + SHSphere result; + CreateMerged(result, lhs, rhs); + return result; + } + + bool SHSphere::Intersect(const SHSphere& lhs, const SHSphere& rhs) noexcept + { + return lhs.Intersects(rhs); + } + + SHSphere SHSphere::BuildFromSpheres(const SHSphere* spheres, size_t numSpheres) noexcept + { + SHSphere result; + + for (size_t i = 1; i < numSpheres; ++i) + CreateMerged(result, spheres[i - 1], spheres[i]); + + return result; + } + + SHSphere SHSphere::BuildFromVertices(const SHVec3* vertices, size_t numVertices, size_t stride) noexcept + { + SHSphere result; + CreateFromPoints(result, numVertices, vertices, stride); + return result; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.h b/SHADE_Engine/src/Math/Geometry/SHSphere.h similarity index 56% rename from SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.h rename to SHADE_Engine/src/Math/Geometry/SHSphere.h index b874d2fe..e056f21a 100644 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.h +++ b/SHADE_Engine/src/Math/Geometry/SHSphere.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHSphereCollisionShape.h + * \file SHBoundingSphere.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Sphere Collision Shape. + * \brief Interface for a Bounding Sphere. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -13,7 +13,8 @@ #include // Project Headers -#include "SHCollisionShape.h" +#include "SHShape.h" +#include "SH_API.h" namespace SHADE { @@ -21,44 +22,18 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates the information to create a sphere. - */ - struct SHSphereCreateInfo + class SH_API SHSphere : public SHShape, + private DirectX::BoundingSphere { - public: - SHVec3 Center = SHVec3::Zero; - float Radius = 1.0f; - float RelativeRadius = 1.0f; - float Scale = 1.0f; - }; - - /** - * @brief - * Encapsulate a Sphere Shape used for Physics Simulations. - */ - class SH_API SHSphere final : public SHCollisionShape - , private DirectX::BoundingSphere - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHCollider; - friend class SHCompositeCollider; - friend class SHCollision; - friend class SHCollisionShapeLibrary; - public: /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHSphere (SHCollisionShapeID id) noexcept; - SHSphere (const SHSphere& rhs) noexcept; - SHSphere (SHSphere&& rhs) noexcept; + SHSphere () noexcept; + SHSphere (const SHVec3& center, float radius) noexcept; + SHSphere (const SHSphere& rhs) noexcept; + SHSphere (SHSphere&& rhs) noexcept; ~SHSphere () override = default; @@ -73,43 +48,45 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] float GetWorldRadius () const noexcept; - [[nodiscard]] float GetRelativeRadius () const noexcept; - - [[nodiscard]] SHVec3 GetWorldCentroid () const noexcept override; - [[nodiscard]] SHVec3 GetRelativeCentroid () const noexcept override; - [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override; - [[nodiscard]] SHQuaternion GetWorldOrientation () const noexcept override; - [[nodiscard]] SHQuaternion GetRelativeOrientation () const noexcept override; - [[nodiscard]] float GetVolume () const noexcept override; - [[nodiscard]] float GetSurfaceArea () const noexcept override; - [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] float GetWorldRadius () const noexcept; + [[nodiscard]] float GetRelativeRadius () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ + void SetCenter (const SHVec3& center) noexcept; void SetWorldRadius (float newWorldRadius) noexcept; void SetRelativeRadius (float newRelativeRadius) noexcept; - void SetScale (float maxScale) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ - void Update () noexcept override; [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; - [[nodiscard]] SHMatrix GetTRS () const noexcept override; - [[nodiscard]] SHAABB ComputeAABB () const noexcept override; + [[nodiscard]] SHRaycastResult Raycast(const SHRay& ray) const noexcept override; + + [[nodiscard]] bool Contains (const SHSphere& rhs) const noexcept; + [[nodiscard]] float Volume () const noexcept; + [[nodiscard]] float SurfaceArea () const noexcept; + + + /*---------------------------------------------------------------------------------*/ + /* Static Function Members */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] static SHSphere Combine (const SHSphere& lhs, const SHSphere& rhs) noexcept; + [[nodiscard]] static bool Intersect (const SHSphere& lhs, const SHSphere& rhs) noexcept; + [[nodiscard]] static SHSphere BuildFromSpheres (const SHSphere* spheres, size_t numSpheres) noexcept; + [[nodiscard]] static SHSphere BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; private: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - float relativeRadius; - float scale; - }; + float RelativeRadius; -} // namespace SHADE \ No newline at end of file + }; +} // namespace SHADE diff --git a/SHADE_Engine/src/Math/SHColour.cpp b/SHADE_Engine/src/Math/SHColour.cpp index 7c372376..fc2f2a08 100644 --- a/SHADE_Engine/src/Math/SHColour.cpp +++ b/SHADE_Engine/src/Math/SHColour.cpp @@ -260,8 +260,6 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; - // This will never hit - default: return x; } } @@ -276,8 +274,6 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; - // This will never hit - default: return x; } } @@ -292,8 +288,6 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; - // This will never hit - default: return x; } } @@ -308,8 +302,6 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; - // This will never hit - default: return x; } } diff --git a/SHADE_Engine/src/Math/SHMathHelpers.h b/SHADE_Engine/src/Math/SHMathHelpers.h index 1d65eb91..427011a6 100644 --- a/SHADE_Engine/src/Math/SHMathHelpers.h +++ b/SHADE_Engine/src/Math/SHMathHelpers.h @@ -46,16 +46,14 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /** Standard Epsilon value for comparing Single-Precision Floating-Point values. */ - static constexpr float EPSILON = 0.0001f; + static constexpr float EPSILON = 0.001f; /** Single-Precision Floating-Point value of infinity */ - static constexpr float INF = std::numeric_limits::infinity(); + static constexpr float INF = std::numeric_limits::infinity(); - static constexpr float PI = std::numbers::pi_v; - static constexpr float HALF_PI = PI * 0.5f; - static constexpr float TWO_PI = 2.0f * PI; - - static constexpr float EULER_CONSTANT = std::numbers::egamma_v; + static constexpr float PI = std::numbers::pi_v; + static constexpr float HALF_PI = PI * 0.5f; + static constexpr float TWO_PI = 2.0f * PI; /*---------------------------------------------------------------------------------*/ /* Static Function Members */ diff --git a/SHADE_Engine/src/Math/SHMatrix.cpp b/SHADE_Engine/src/Math/SHMatrix.cpp index 2cbd5ef6..3d450a88 100644 --- a/SHADE_Engine/src/Math/SHMatrix.cpp +++ b/SHADE_Engine/src/Math/SHMatrix.cpp @@ -34,14 +34,6 @@ namespace SHADE 0.0f, 0.0f, 0.0f, 1.0f }; - const SHMatrix SHMatrix::Zero - { - SHVec4::Zero - , SHVec4::Zero - , SHVec4::Zero - , SHVec4::Zero - }; - /*-----------------------------------------------------------------------------------*/ /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/SHMatrix.h b/SHADE_Engine/src/Math/SHMatrix.h index ffd8ce89..6af8fdc9 100644 --- a/SHADE_Engine/src/Math/SHMatrix.h +++ b/SHADE_Engine/src/Math/SHMatrix.h @@ -45,7 +45,6 @@ namespace SHADE static constexpr size_t NUM_COLS = 4U; static const SHMatrix Identity; - static const SHMatrix Zero; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ diff --git a/SHADE_Engine/src/Math/SHQuaternion.cpp b/SHADE_Engine/src/Math/SHQuaternion.cpp index 1bf51b28..1fa4e246 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.cpp +++ b/SHADE_Engine/src/Math/SHQuaternion.cpp @@ -40,14 +40,20 @@ namespace SHADE : XMFLOAT4( vec4.x, vec4.y, vec4.z, vec4.w ) {} - SHQuaternion::SHQuaternion(const XMFLOAT4& xmfloat4) noexcept - : XMFLOAT4( xmfloat4 ) - {} - SHQuaternion::SHQuaternion(float _x, float _y, float _z, float _w) noexcept : XMFLOAT4( _x, _y, _z, _w ) {} + SHQuaternion::SHQuaternion(const reactphysics3d::Vector3& rp3dEuler) noexcept + : XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) + { + XMStoreFloat4(this, XMQuaternionRotationRollPitchYawFromVector(SHVec3 { rp3dEuler })); + } + + SHQuaternion::SHQuaternion(const reactphysics3d::Quaternion& rp3dQuat) noexcept + : XMFLOAT4( rp3dQuat.x, rp3dQuat.y, rp3dQuat.z, rp3dQuat.w ) + {} + /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -135,6 +141,16 @@ namespace SHADE return XMQuaternionNotEqual(*this, rhs); } + SHQuaternion::operator reactphysics3d::Quaternion() const noexcept + { + return reactphysics3d::Quaternion{ x, y, z, w }; + } + + SHQuaternion::operator reactphysics3d::Vector3() const noexcept + { + return reactphysics3d::Vector3{ ToEuler() }; + } + SHQuaternion::operator XMVECTOR() const noexcept { return XMLoadFloat4(this); diff --git a/SHADE_Engine/src/Math/SHQuaternion.h b/SHADE_Engine/src/Math/SHQuaternion.h index 827f8929..29f6df7e 100644 --- a/SHADE_Engine/src/Math/SHQuaternion.h +++ b/SHADE_Engine/src/Math/SHQuaternion.h @@ -11,6 +11,7 @@ #pragma once #include +#include #include @@ -47,10 +48,14 @@ namespace SHADE SHQuaternion (const SHQuaternion& rhs) = default; SHQuaternion (SHQuaternion&& rhs) = default; - SHQuaternion () noexcept; - SHQuaternion (const SHVec4& vec4) noexcept; - SHQuaternion (const XMFLOAT4& xmfloat4) noexcept; - SHQuaternion (float x, float y, float z, float w) noexcept; + SHQuaternion () noexcept; + SHQuaternion (const SHVec4& vec4) noexcept; + SHQuaternion (float x, float y, float z, float w) noexcept; + + // Conversion from other math types + + SHQuaternion (const reactphysics3d::Vector3& rp3dEuler) noexcept; + SHQuaternion (const reactphysics3d::Quaternion& rp3dQuat) noexcept; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ @@ -77,6 +82,8 @@ namespace SHADE // Conversion to other math types used by SHADE + operator reactphysics3d::Quaternion () const noexcept; + operator reactphysics3d::Vector3 () const noexcept; operator DirectX::XMVECTOR () const noexcept; /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/SHRay.cpp b/SHADE_Engine/src/Math/SHRay.cpp index 622087c6..c4931aba 100644 --- a/SHADE_Engine/src/Math/SHRay.cpp +++ b/SHADE_Engine/src/Math/SHRay.cpp @@ -30,6 +30,12 @@ namespace SHADE , direction { dir } {} + SHRay::SHRay(const reactphysics3d::Ray rp3dRay) noexcept + : position { rp3dRay.point1 } + , direction { SHVec3::Normalise(rp3dRay.point2 - rp3dRay.point1) } + {} + + /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -56,6 +62,12 @@ namespace SHADE return XMVector3NotEqual(LHS_POS, RHS_POS) || XMVector3NotEqual(LHS_DIR, RHS_DIR); } + SHRay::operator reactphysics3d::Ray() const noexcept + { + // We use 2km. Temp solution. + return reactphysics3d::Ray{ position, position + (direction * MAX_RAYCAST_DIST) }; + } + SHRaycastResult::operator bool() const noexcept { return hit; diff --git a/SHADE_Engine/src/Math/SHRay.h b/SHADE_Engine/src/Math/SHRay.h index 56599018..18efc224 100644 --- a/SHADE_Engine/src/Math/SHRay.h +++ b/SHADE_Engine/src/Math/SHRay.h @@ -10,7 +10,10 @@ #pragma once +#include + // Project Headers +#include "SH_API.h" #include "Vector/SHVec3.h" namespace SHADE @@ -37,6 +40,7 @@ namespace SHADE SHRay () noexcept; SHRay (const SHVec3& pos, const SHVec3& dir) noexcept; + SHRay (const reactphysics3d::Ray rp3dRay) noexcept; SHRay (const SHRay&) noexcept = default; SHRay (SHRay&& ) noexcept = default; @@ -51,6 +55,8 @@ namespace SHADE [[nodiscard]] bool operator==(const SHRay& rhs) const noexcept; [[nodiscard]] bool operator!=(const SHRay& rhs) const noexcept; + + operator reactphysics3d::Ray() const noexcept; }; struct SH_API SHRaycastResult diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index 0a0ec092..03de360d 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -246,6 +246,8 @@ namespace SHADE tf.world.position = SHVec3::Transform(tf.local.position, localToWorld); tf.world.scale = tf.local.scale * (parent ? parent->GetLocalScale() : SHVec3::One); + + if (convertRotation) { tf.worldRotation = tf.localRotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero); diff --git a/SHADE_Engine/src/Math/Vector/SHVec2.cpp b/SHADE_Engine/src/Math/Vector/SHVec2.cpp index da0215f4..9573be01 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec2.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec2.cpp @@ -50,6 +50,10 @@ namespace SHADE : XMFLOAT2( _x, _y ) {} + SHVec2::SHVec2(const reactphysics3d::Vector2& rp3dVec2) noexcept + : XMFLOAT2( rp3dVec2.x, rp3dVec2.y ) + {} + /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -161,8 +165,6 @@ namespace SHADE { case 0: return x; case 1: return y; - // This will never hit - default: return x; } } @@ -175,8 +177,6 @@ namespace SHADE { case 0: return x; case 1: return y; - // This will never hit - default: return x; } } @@ -189,8 +189,6 @@ namespace SHADE { case 0: return x; case 1: return y; - // This will never hit - default: return x; } } @@ -203,11 +201,14 @@ namespace SHADE { case 0: return x; case 1: return y; - // This will never hit - default: return x; } } + SHVec2::operator reactphysics3d::Vector2() const noexcept + { + return reactphysics3d::Vector2{ x, y }; + } + SHVec2 operator* (float lhs, const SHVec2& rhs) noexcept { SHVec2 result; diff --git a/SHADE_Engine/src/Math/Vector/SHVec2.h b/SHADE_Engine/src/Math/Vector/SHVec2.h index 78c1e6e8..e780d3ac 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec2.h +++ b/SHADE_Engine/src/Math/Vector/SHVec2.h @@ -11,6 +11,7 @@ #pragma once #include +#include #include #include @@ -58,6 +59,10 @@ namespace SHADE SHVec2 (float n) noexcept; SHVec2 (float x, float y) noexcept; + // Conversion from other math types to SHADE + + SHVec2 (const reactphysics3d::Vector2& rp3dVec2) noexcept; + /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ @@ -68,6 +73,7 @@ namespace SHADE // Conversion to other math types used by SHADE operator DirectX::XMVECTOR () const noexcept; + operator reactphysics3d::Vector2 () const noexcept; SHVec2& operator+= (const SHVec2& rhs) noexcept; SHVec2& operator-= (const SHVec2& rhs) noexcept; diff --git a/SHADE_Engine/src/Math/Vector/SHVec3.cpp b/SHADE_Engine/src/Math/Vector/SHVec3.cpp index 72d2b0a2..4b77636a 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec3.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec3.cpp @@ -57,6 +57,14 @@ namespace SHADE : XMFLOAT3( _x, _y, _z ) {} + SHVec3::SHVec3(const reactphysics3d::Vector3& rp3dVec3) noexcept + : XMFLOAT3( rp3dVec3.x, rp3dVec3.y, rp3dVec3.z ) + {} + + SHVec3::SHVec3(const reactphysics3d::Quaternion& rp3dVec3) noexcept + : XMFLOAT3( SHQuaternion{rp3dVec3}.ToEuler() ) + {} + /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -171,8 +179,6 @@ namespace SHADE case 0: return x; case 1: return y; case 2: return z; - // This will never hit - default: return x; } } @@ -186,8 +192,6 @@ namespace SHADE case 0: return x; case 1: return y; case 2: return z; - // This will never hit - default: return x; } } @@ -201,8 +205,6 @@ namespace SHADE case 0: return x; case 1: return y; case 2: return z; - // This will never hit - default: return x; } } @@ -216,11 +218,19 @@ namespace SHADE case 0: return x; case 1: return y; case 2: return z; - // This will never hit - default: return x; } } + SHVec3::operator reactphysics3d::Vector3() const noexcept + { + return reactphysics3d::Vector3{ x, y , z }; + } + + SHVec3::operator reactphysics3d::Quaternion() const noexcept + { + return reactphysics3d::Quaternion::fromEulerAngles(x, y, z); + } + SHVec3 operator* (float lhs, const SHVec3& rhs) noexcept { SHVec3 result; @@ -372,30 +382,6 @@ namespace SHADE return lhs.Cross(rhs); } - SHMatrix SHVec3::OuterProduct(const SHVec3& lhs, const SHVec3& rhs) noexcept - { - /* - * Outer product is a matrix multiplication u * vT - * 3x1 * 1x3 = 3x3 - * - * | u1 | | v1 v2 v3 | | u1v1 u1v2 u1v3 | - * | u2 | = | u2v1 u2v2 u2v3 | - * | u3 | | u3v1 u3v2 u3v3 | - */ - - SHMatrix u = SHMatrix::Zero; - SHMatrix vT = SHMatrix::Zero; - - for (int i = 0; i < SIZE; ++i) - { - u.m[0][i] = lhs[i]; - vT.m[i][0] = rhs[i]; - } - - return u * vT; - } - - SHVec3 SHVec3::Project(const SHVec3& v, const SHVec3& u) noexcept { SHVec3 result; diff --git a/SHADE_Engine/src/Math/Vector/SHVec3.h b/SHADE_Engine/src/Math/Vector/SHVec3.h index 4b0112c5..de37d6a5 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec3.h +++ b/SHADE_Engine/src/Math/Vector/SHVec3.h @@ -11,6 +11,8 @@ #pragma once #include +#include +#include #include #include @@ -67,6 +69,9 @@ namespace SHADE // Conversion from other math types to SHADE + SHVec3 (const reactphysics3d::Vector3& rp3dVec3) noexcept; + SHVec3 (const reactphysics3d::Quaternion& rp3dVec3) noexcept; + /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ @@ -76,6 +81,8 @@ namespace SHADE // Conversion to other math types used by SHADE + operator reactphysics3d::Vector3 () const noexcept; + operator reactphysics3d::Quaternion () const noexcept; operator DirectX::XMVECTOR () const noexcept; SHVec3& operator+= (const SHVec3& rhs) noexcept; @@ -115,28 +122,27 @@ namespace SHADE /* Static Function Members */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static SHVec3 Normalise (const SHVec3& v) noexcept; - [[nodiscard]] static SHVec3 Abs (const SHVec3& v) noexcept; - [[nodiscard]] static SHVec3 Min (const std::initializer_list& vs) noexcept; - [[nodiscard]] static SHVec3 Max (const std::initializer_list& vs) noexcept; - [[nodiscard]] static SHVec3 Clamp (const SHVec3& v, const SHVec3& vMin, const SHVec3& vMax) noexcept; - [[nodiscard]] static SHVec3 Lerp (const SHVec3& a, const SHVec3& b, float t) noexcept; - [[nodiscard]] static SHVec3 ClampedLerp (const SHVec3& a, const SHVec3& b, float t, float tMin = 0.0f, float tMax = 1.0f) noexcept; + [[nodiscard]] static SHVec3 Normalise (const SHVec3& v) noexcept; + [[nodiscard]] static SHVec3 Abs (const SHVec3& v) noexcept; + [[nodiscard]] static SHVec3 Min (const std::initializer_list& vs) noexcept; + [[nodiscard]] static SHVec3 Max (const std::initializer_list& vs) noexcept; + [[nodiscard]] static SHVec3 Clamp (const SHVec3& v, const SHVec3& vMin, const SHVec3& vMax) noexcept; + [[nodiscard]] static SHVec3 Lerp (const SHVec3& a, const SHVec3& b, float t) noexcept; + [[nodiscard]] static SHVec3 ClampedLerp (const SHVec3& a, const SHVec3& b, float t, float tMin = 0.0f, float tMax = 1.0f) noexcept; - [[nodiscard]] static float Distance (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static float DistanceSquared (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static float Angle (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static float Dot (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static SHVec3 Cross (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static SHMatrix OuterProduct (const SHVec3& lhs, const SHVec3& rhs) noexcept; - [[nodiscard]] static SHVec3 Project (const SHVec3& v, const SHVec3& u) noexcept; - [[nodiscard]] static SHVec3 Reflect (const SHVec3& v, const SHVec3& normal) noexcept; - [[nodiscard]] static SHVec3 Rotate (const SHVec3& v, const SHVec3& axis, float angleInRad) noexcept; - [[nodiscard]] static SHVec3 Rotate (const SHVec3& v, const SHQuaternion& q) noexcept; - [[nodiscard]] static SHVec3 RotateX (const SHVec3& v, float angleInRad) noexcept; - [[nodiscard]] static SHVec3 RotateY (const SHVec3& v, float angleInRad) noexcept; - [[nodiscard]] static SHVec3 RotateZ (const SHVec3& v, float angleInRad) noexcept; - [[nodiscard]] static SHVec3 Transform (const SHVec3& v, const SHMatrix& transformMtx) noexcept; + [[nodiscard]] static float Distance (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static float DistanceSquared (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static float Angle (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static float Dot (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static SHVec3 Cross (const SHVec3& lhs, const SHVec3& rhs) noexcept; + [[nodiscard]] static SHVec3 Project (const SHVec3& v, const SHVec3& u) noexcept; + [[nodiscard]] static SHVec3 Reflect (const SHVec3& v, const SHVec3& normal) noexcept; + [[nodiscard]] static SHVec3 Rotate (const SHVec3& v, const SHVec3& axis, float angleInRad) noexcept; + [[nodiscard]] static SHVec3 Rotate (const SHVec3& v, const SHQuaternion& q) noexcept; + [[nodiscard]] static SHVec3 RotateX (const SHVec3& v, float angleInRad) noexcept; + [[nodiscard]] static SHVec3 RotateY (const SHVec3& v, float angleInRad) noexcept; + [[nodiscard]] static SHVec3 RotateZ (const SHVec3& v, float angleInRad) noexcept; + [[nodiscard]] static SHVec3 Transform (const SHVec3& v, const SHMatrix& transformMtx) noexcept; }; SHVec3 operator* (float lhs, const SHVec3& rhs) noexcept; diff --git a/SHADE_Engine/src/Math/Vector/SHVec4.cpp b/SHADE_Engine/src/Math/Vector/SHVec4.cpp index c164e7f4..c6f01d9e 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec4.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec4.cpp @@ -164,8 +164,6 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; - // This will never hit - default: return x; } } @@ -180,8 +178,6 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; - // This will never hit - default: return x; } } @@ -196,8 +192,6 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; - // This will never hit - default: return x; } } @@ -212,8 +206,6 @@ namespace SHADE case 1: return y; case 2: return z; case 3: return w; - // This will never hit - default: return x; } } diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp deleted file mode 100644 index 7177e517..00000000 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.cpp +++ /dev/null @@ -1,644 +0,0 @@ -/**************************************************************************************** - * \file SHDynamicAABBTree.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Dynamic AABB Tree for broadphase collision detection. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -#include - -// Primary Header -#include "SHDynamicAABBTree.h" - -// Project Headers -#include "Math/SHMathHelpers.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHAABBTree::SHAABBTree() noexcept - : root { NULL_NODE } - , nodes { nullptr } - , nodeCount { 0 } - , capacity { 1024 } - , freeList { NULL_NODE } - { - // Build initial tree - nodes = new Node[1024]; - - addToFreeList(0); - } - - SHAABBTree::~SHAABBTree() noexcept - { - delete[] nodes; - } - - SHAABBTree::Node::Node() noexcept - : id { MAX_EID, std::numeric_limits::max() } - , parent { NULL_NODE } - , left { NULL_NODE } - , right { NULL_NODE } - , height { NULL_NODE } - {} - - SHAABBTree::Node::Node(const Node& rhs) noexcept - : AABB { rhs.AABB } - , id { rhs.id } - , next { rhs.next } - , left { rhs.left } - , right { rhs.right } - , height { rhs.height } - {} - - SHAABBTree::Node::Node(Node&& rhs) noexcept - : AABB { rhs.AABB } - , id { rhs.id } - , next { rhs.next } - , left { rhs.left } - , right { rhs.right } - , height { rhs.height } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHAABBTree::Node& SHAABBTree::Node::operator=(const Node& rhs) noexcept - { - if (this == &rhs) - return *this; - - AABB = rhs.AABB; - id = rhs.id; - parent = rhs.parent; - next = rhs.next; - left = rhs.left; - right = rhs.right; - height = rhs.height; - - return *this; - } - - SHAABBTree::Node& SHAABBTree::Node::operator=(Node&& rhs) noexcept - { - AABB = std::move(rhs.AABB); - id = std::move(rhs.id); - parent = rhs.parent; - next = rhs.next; - left = rhs.left; - right = rhs.right; - height = rhs.height; - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const std::vector& SHAABBTree::GetAABBs() const noexcept - { - static AABBs aabbs; - static std::stack nodeIndices; - - aabbs.clear(); - - nodeIndices.push(root); - while (!nodeIndices.empty()) - { - // Pop the top node - const int INDEX = nodeIndices.top(); - nodeIndices.pop(); - - // Skip null nodes - if (INDEX == NULL_NODE) - continue; - - const Node& CURRENT_NODE = nodes[INDEX]; - - aabbs.emplace_back(CURRENT_NODE.AABB); - - if (!isLeaf(INDEX)) - { - nodeIndices.push(CURRENT_NODE.left); - nodeIndices.push(CURRENT_NODE.right); - } - } - - return aabbs; - } - - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHAABBTree::Insert(SHCollisionShapeID id, const SHAABB& AABB) - { - const int32_t NEW_INDEX = allocateNode(); - - if (!nodeMap.emplace(id, NEW_INDEX).second) - { - // Attempted to add a duplicate node - freeNode(NEW_INDEX); - return; - } - - Node& newNode = nodes[NEW_INDEX]; - newNode.AABB = AABB; - newNode.id = id; - newNode.height = 0; - - // Fatten the AABB - const SHVec3 EXTENSION{ AABB_EXTENSION }; - - const SHVec3 newMin = newNode.AABB.GetMin() - EXTENSION; - const SHVec3 newMax = newNode.AABB.GetMax() + EXTENSION; - - newNode.AABB.SetMin(newMin); - newNode.AABB.SetMax(newMax); - - insertLeaf(NEW_INDEX); - } - - void SHAABBTree::Update(SHCollisionShapeID id, const SHAABB& AABB) - { - // Get node index - const int32_t INDEX_TO_UPDATE = nodeMap[id]; - - Node& nodeToUpdate = nodes[INDEX_TO_UPDATE]; - - // If new AABB has not moved enough, skip. - if (nodeToUpdate.AABB.Contains(AABB)) - return; - - removeLeaf(INDEX_TO_UPDATE); - - nodeToUpdate.AABB = AABB; - - // Fatten the AABB - const SHVec3 EXTENSION{ AABB_EXTENSION }; - - const SHVec3 newMin = nodeToUpdate.AABB.GetMin() - EXTENSION; - const SHVec3 newMax = nodeToUpdate.AABB.GetMax() + EXTENSION; - - nodeToUpdate.AABB.SetMin(newMin); - nodeToUpdate.AABB.SetMax(newMax); - - insertLeaf(INDEX_TO_UPDATE); - } - - void SHAABBTree::Remove(SHCollisionShapeID id) noexcept - { - // Get node index - const int32_t INDEX_TO_REMOVE = nodeMap[id]; - - removeLeaf(INDEX_TO_REMOVE); - freeNode(INDEX_TO_REMOVE); - - nodeMap.erase(id); - } - - const std::vector& SHAABBTree::Query(SHCollisionShapeID id, const SHAABB& AABB) const noexcept - { - static std::vector potentialCollisions; - static std::stack nodeIndices; - - potentialCollisions.clear(); - - // We use this to ignore shapes on the same entity - const EntityID EID = id.GetEntityID(); - - nodeIndices.push(root); - while (!nodeIndices.empty()) - { - const int32_t INDEX = nodeIndices.top(); - nodeIndices.pop(); - - if (INDEX == NULL_NODE) - continue; - - const Node& NODE = nodes[INDEX]; - if (!SHAABB::Intersect(AABB, NODE.AABB)) - continue; - - // Avoid checking against shapes of the same composite collider (and itself) - if (isLeaf(INDEX) && NODE.id.GetEntityID() != EID) - { - potentialCollisions.emplace_back(NODE.id); - } - else - { - nodeIndices.push(NODE.left); - nodeIndices.push(NODE.right); - } - } - - return potentialCollisions; - } - - const std::vector& SHAABBTree::Query(const SHRay& ray, float distance) const noexcept - { - static std::vector potentialHits; - static std::stack nodeIndices; - - potentialHits.clear(); - - nodeIndices.push(root); - while (!nodeIndices.empty()) - { - const int32_t INDEX = nodeIndices.top(); - nodeIndices.pop(); - - if (INDEX == NULL_NODE) - continue; - - const Node& NODE = nodes[INDEX]; - - const auto& RESULT = NODE.AABB.Raycast(ray); - if (!RESULT || RESULT.distance > distance) - continue; - - if (isLeaf(INDEX)) - { - potentialHits.emplace_back(NODE.id); - } - else - { - // Non-leaf nodes need to be traversed further - nodeIndices.push(NODE.left); - nodeIndices.push(NODE.right); - } - } - - return potentialHits; - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHAABBTree::isLeaf(int32_t index) const noexcept - { - const Node& NODE = nodes[index]; - return NODE.left == NULL_NODE && NODE.right == NULL_NODE; - } - - int32_t SHAABBTree::allocateNode() - { - if (freeList == NULL_NODE) - { - // No more free nodes available, so we need to resize the tree for more nodes - capacity *= 2; - - Node* newNodes = new Node[capacity]; - - // Copy all the nodes manually. I do this instead of memcpy to guarantee it copies properly. - for (int32_t i = 0; i < nodeCount; ++i) - { - newNodes[i].AABB = nodes[i].AABB; - newNodes[i].id = nodes[i].id; - newNodes[i].parent = nodes[i].parent; - newNodes[i].left = nodes[i].left; - newNodes[i].right = nodes[i].right; - newNodes[i].height = nodes[i].height; - } - - delete[] nodes; - nodes = newNodes; - - addToFreeList(nodeCount); - } - - const int32_t FREE_NODE = freeList; - freeList = nodes[FREE_NODE].next; - - // Set node to default - Node& newNode = nodes[FREE_NODE]; - newNode.parent = NULL_NODE; - newNode.left = NULL_NODE; - newNode.right = NULL_NODE; - newNode.height = NULL_NODE; - - ++nodeCount; - return FREE_NODE; - } - - void SHAABBTree::freeNode(int32_t index) noexcept - { - SHASSERT(index >= 0 && index < capacity, "Trying to free an invalid AABB Tree node!") - - nodes[index].next = freeList; - nodes[index].height = NULL_NODE; - - // Put it back on the free list - freeList = index; - - --nodeCount; - } - - void SHAABBTree::insertLeaf(int32_t index) - { - // If there is no root, the first insert must make the root - if (root == NULL_NODE) - { - root = index; - nodes[root].parent = NULL_NODE; - return; - } - - // Find best sibling for new leaf - // Utilise Surface Area Heuristic - const SHAABB& LEAF_AABB = nodes[index].AABB; - - uint32_t searchIndex = root; - while (!isLeaf(searchIndex)) - { - const SHAABB COMBINED_AABB = SHAABB::Combine(LEAF_AABB, nodes[searchIndex].AABB); - const float COMBINED_AREA = COMBINED_AABB.SurfaceArea(); - - const float INHERITED_COST = 2.0f * (COMBINED_AREA - nodes[searchIndex].AABB.SurfaceArea()); - - const int32_t LEFT_INDEX = nodes[searchIndex].left; - const int32_t RIGHT_INDEX = nodes[searchIndex].right; - - float leftCost = 0.0f; - float rightCost = 0.0f; - - const float LEFT_COMBINED_AREA = SHAABB::Combine(LEAF_AABB, nodes[LEFT_INDEX].AABB).SurfaceArea(); - const float RIGHT_COMBINED_AREA = SHAABB::Combine(LEAF_AABB, nodes[RIGHT_INDEX].AABB).SurfaceArea(); - - // Compute cost for descending into the left - if (isLeaf(LEFT_INDEX)) - leftCost = LEFT_COMBINED_AREA + INHERITED_COST; - else - leftCost = LEFT_COMBINED_AREA - nodes[LEFT_INDEX].AABB.SurfaceArea() + INHERITED_COST; - - // Compute cost for descending into the right - if (isLeaf(RIGHT_INDEX)) - rightCost = RIGHT_COMBINED_AREA + INHERITED_COST; - else - rightCost = RIGHT_COMBINED_AREA - nodes[RIGHT_INDEX].AABB.SurfaceArea() + INHERITED_COST; - - // Early out - const float BRANCH_COST = 2.0f * COMBINED_AREA; - if (BRANCH_COST < leftCost && BRANCH_COST < rightCost) - break; - - // Traverse - searchIndex = leftCost < rightCost ? LEFT_INDEX : RIGHT_INDEX; - } - - const int32_t BEST_SIBLING = searchIndex; - - // Create a new parent for the leaf - const int32_t OLD_PARENT = nodes[BEST_SIBLING].parent; - const int32_t NEW_PARENT = allocateNode(); - - Node& newParent = nodes[NEW_PARENT]; - newParent.parent = OLD_PARENT; - newParent.id = SHCollisionShapeID{ MAX_EID, std::numeric_limits::max() }; - newParent.AABB = SHAABB::Combine(LEAF_AABB, nodes[BEST_SIBLING].AABB); - newParent.height = nodes[BEST_SIBLING].height + 1; - - newParent.left = BEST_SIBLING; - newParent.right = index; - - nodes[BEST_SIBLING].parent = NEW_PARENT; - nodes[index].parent = NEW_PARENT; - - // If sibling was the root - if (OLD_PARENT == NULL_NODE) - root = NEW_PARENT; - else - (nodes[OLD_PARENT].left == BEST_SIBLING ? nodes[OLD_PARENT].left : nodes[OLD_PARENT].right) = NEW_PARENT; - - syncHierarchy(NEW_PARENT); - } - - void SHAABBTree::removeLeaf(int32_t index) - { - if (index == root) - { - root = NULL_NODE; - return; - } - - const int32_t PARENT = nodes[index].parent; - - if (PARENT == NULL_NODE) - { - freeNode(index); - return; - } - - const int32_t GRANDPARENT = nodes[PARENT].parent; - const int32_t SIBLING = nodes[PARENT].left == index ? nodes[PARENT].right : nodes[PARENT].left; - - if (GRANDPARENT != NULL_NODE) - { - // Replace parent with sibling - (nodes[GRANDPARENT].left == PARENT ? nodes[GRANDPARENT].left : nodes[GRANDPARENT].right) = SIBLING; - nodes[SIBLING].parent = GRANDPARENT; - } - else - { - // Parent was root - root = SIBLING; - nodes[SIBLING].parent = NULL_NODE; - } - - freeNode(PARENT); - syncHierarchy(GRANDPARENT); - } - - void SHAABBTree::syncHierarchy(int32_t index) - { - while (index != NULL_NODE) - { - index = balance(index); - - const int32_t LEFT_INDEX = nodes[index].left; - const Node& LEFT_NODE = nodes[LEFT_INDEX]; - - const int32_t RIGHT_INDEX = nodes[index].right; - const Node& RIGHT_NODE = nodes[RIGHT_INDEX]; - - nodes[index].height = 1 + SHMath::Max(LEFT_NODE.height, RIGHT_NODE.height); - nodes[index].AABB = SHAABB::Combine(LEFT_NODE.AABB, RIGHT_NODE.AABB); - - // Sync up to the root - index = nodes[index].parent; - } - } - - int32_t SHAABBTree::balance(int32_t index) - { - if (isLeaf(index) || nodes[index].height == 1) - return index; - - Node& nodeA = nodes[index]; - - const int32_t LEFT = nodeA.left; - const int32_t RIGHT = nodeA.right; - - const int32_t DIFF = nodes[RIGHT].height - nodes[LEFT].height; - - if (DIFF > 1) - return rotateLeft(index); - - if (DIFF < -1) - return rotateRight(index); - - return index; - } - - int32_t SHAABBTree::rotateLeft(int32_t index) - { - /**************************** - A C - / \ / \ - B C --> A F/G - / \ / \ - F G B G/F - ****************************/ - - // Promote C - - Node& nodeA = nodes[index]; - - const int32_t B = nodeA.left; - const int32_t C = nodeA.right; - - Node& nodeB = nodes[B]; - Node& nodeC = nodes[C]; - - const int32_t F = nodeC.left; - const int32_t G = nodeC.right; - - Node& nodeF = nodes[F]; - Node& nodeG = nodes[G]; - - if (nodeA.parent != NULL_NODE) - (nodes[nodeA.parent].left == index ? nodes[nodeA.parent].left : nodes[nodeA.parent].right) = C; - else - root = C; - - nodeC.left = index; - nodeC.parent = nodeA.parent; - nodeA.parent = C; - - if (nodeF.height > nodeG.height) - { - nodeC.right = F; - nodeA.right = G; - nodeG.parent = index; - - nodeA.AABB = SHAABB::Combine(nodeB.AABB, nodeG.AABB); - nodeC.AABB = SHAABB::Combine(nodeA.AABB, nodeF.AABB); - - nodeA.height = 1 + SHMath::Max(nodeB.height, nodeG.height); - nodeC.height = 1 + SHMath::Max(nodeA.height, nodeF.height); - } - else - { - nodeC.right = G; - nodeA.right = F; - nodeF.parent = index; - - nodeA.AABB = SHAABB::Combine(nodeB.AABB, nodeF.AABB); - nodeC.AABB = SHAABB::Combine(nodeA.AABB, nodeG.AABB); - - nodeA.height = 1 + SHMath::Max(nodeB.height, nodeF.height); - nodeC.height = 1 + SHMath::Max(nodeA.height, nodeG.height); - } - - return C; - } - - int32_t SHAABBTree::rotateRight(int32_t index) - { - /************************* - A B - / \ / \ - B C --> D/E A - / \ / \ - D E E/D C - *************************/ - - // Promote B - - Node& nodeA = nodes[index]; - - const int32_t B = nodeA.left; - const int32_t C = nodeA.right; - - Node& nodeB = nodes[B]; - Node& nodeC = nodes[C]; - - const int32_t D = nodeB.left; - const int32_t E = nodeB.right; - - Node& nodeD = nodes[D]; - Node& nodeE = nodes[E]; - - if (nodeA.parent != NULL_NODE) - (nodes[nodeA.parent].left == index ? nodes[nodeA.parent].left : nodes[nodeA.parent].right) = B; - else - root = B; - - nodeB.right = index; - nodeB.parent = nodeA.parent; - nodeA.parent = B; - - if (nodeD.height > nodeE.height) - { - nodeB.left = D; - nodeA.left = E; - nodeE.parent = index; - - nodeA.AABB = SHAABB::Combine(nodeC.AABB, nodeE.AABB); - nodeB.AABB = SHAABB::Combine(nodeA.AABB, nodeD.AABB); - - nodeA.height = 1 + SHMath::Max(nodeC.height, nodeE.height); - nodeB.height = 1 + SHMath::Max(nodeA.height, nodeD.height); - } - else - { - nodeB.left = E; - nodeA.left = D; - nodeD.parent = index; - - nodeA.AABB = SHAABB::Combine(nodeC.AABB, nodeD.AABB); - nodeB.AABB = SHAABB::Combine(nodeA.AABB, nodeE.AABB); - - nodeA.height = 1 + SHMath::Max(nodeC.height, nodeD.height); - nodeB.height = 1 + SHMath::Max(nodeA.height, nodeE.height); - } - - return B; - } - - void SHAABBTree::addToFreeList(int32_t index) noexcept - { - for (int32_t i = index; i < capacity; ++i) - { - nodes[i].next = i + 1; - nodes[i].height = NULL_NODE; - } - - nodes[capacity - 1].next = NULL_NODE; - nodes[capacity - 1].height = NULL_NODE; - - freeList = index; - } -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h b/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h deleted file mode 100644 index 292c3528..00000000 --- a/SHADE_Engine/src/Physics/Collision/Broadphase/SHDynamicAABBTree.h +++ /dev/null @@ -1,156 +0,0 @@ -/**************************************************************************************** - * \file SHDynamicAABBTree.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Dynamic AABB Tree for broadphase collision detection. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "Physics/Collision/Shapes/SHCollisionShape.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a dynamic AABB Tree for collision detection. - */ - class SH_API SHAABBTree - { - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - using AABBs = std::vector; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr int NULL_NODE = -1; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHAABBTree () noexcept; - ~SHAABBTree () noexcept; - - SHAABBTree(const SHAABBTree& other) = delete; - SHAABBTree(SHAABBTree&& other) noexcept = delete; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHAABBTree& operator=(const SHAABBTree& other) = delete; - SHAABBTree& operator=(SHAABBTree&& other) noexcept = delete; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] const AABBs& GetAABBs () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - void Insert (SHCollisionShapeID id, const SHAABB& AABB); - void Update (SHCollisionShapeID id, const SHAABB& AABB); - void Remove (SHCollisionShapeID id) noexcept; - - [[nodiscard]] const std::vector& Query(SHCollisionShapeID id, const SHAABB& AABB) const noexcept; - [[nodiscard]] const std::vector& Query(const SHRay& ray, float distance) const noexcept; - - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - struct Node - { - public: - /*-------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-------------------------------------------------------------------------------*/ - - Node () noexcept; - Node (const Node& rhs) noexcept; - Node (Node&& rhs) noexcept; - - ~Node () noexcept = default; - - /*-------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*-------------------------------------------------------------------------------*/ - - Node& operator=(const Node& rhs) noexcept; - Node& operator=(Node&& rhs) noexcept; - - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - SHAABB AABB; - SHCollisionShapeID id; // Used to lookup the collision shape & entity for culling against itself - - union - { - int32_t parent; - int32_t next; - }; - - - int32_t left; - int32_t right; - int32_t height; // Leaves have a height of 0. Free nodes have a height of -1 - }; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr float AABB_EXTENSION = 0.2f; - - // For quick access - std::unordered_map nodeMap; - - int32_t root; - Node* nodes; // Dynamically allocated array of nodes. I use dynamic allocation as in the past, using a vector causes weird issues. - int32_t nodeCount; - int32_t capacity; // Used for resizing the tree. - int32_t freeList; // Stores the next available node on the free list. - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - bool isLeaf (int32_t index) const noexcept; - - int32_t allocateNode (); - void freeNode (int32_t index) noexcept; - - void insertLeaf (int32_t index); - void removeLeaf (int32_t index); - void syncHierarchy (int32_t index); - int32_t balance (int32_t index); - int32_t rotateLeft (int32_t index); - int32_t rotateRight (int32_t index); - - void addToFreeList (int32_t index) noexcept; - }; - - -} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h deleted file mode 100644 index 15142303..00000000 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionEvents.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionKey.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for Collision Information for Collision & Triggers. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "SHCollisionKey.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - enum class SHCollisionState - { - ENTER - , STAY - , EXIT - - , TOTAL - , INVALID = -1 - }; - - /** - * @brief - * Encapsulates the event for an intersection between two collision shapes that do not - * have physical resolution. - */ - struct SH_API SHTriggerEvent - { - public: - SHCollisionKey info; - SHCollisionState state = SHCollisionState::INVALID; - }; - - /** - * @brief - * Encapsulates the event for an intersection between two collision shapes that do - * have physical resolution. - */ - struct SH_API SHCollisionEvent - { - public: - static constexpr int MAX_NUM_CONTACTS = 4; - - SHCollisionKey info; - SHCollisionState state = SHCollisionState::INVALID; - SHVec3 normal; - SHVec3 contactPoints[MAX_NUM_CONTACTS]; - }; - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp deleted file mode 100644 index 4bb22697..00000000 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionInfo.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for Collision Info. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollisionKey.h" - -// Project Headers -#include "Physics/Collision/SHCollider.h" -#include "Physics/Interface/SHColliderComponent.h" - - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollisionKey::SHCollisionKey() noexcept - { - ids[ENTITY_A] = MAX_EID; - ids[ENTITY_B] = MAX_EID; - ids[SHAPE_INDEX_A] = std::numeric_limits::max(); - ids[SHAPE_INDEX_B] = std::numeric_limits::max(); - } - - SHCollisionKey::SHCollisionKey(const SHCollisionKey& rhs) noexcept - { - value[0] = rhs.value[0]; - value[1] = rhs.value[1]; - } - - SHCollisionKey::SHCollisionKey(SHCollisionKey&& rhs) noexcept - { - value[0] = rhs.value[0]; - value[1] = rhs.value[1]; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollisionKey& SHCollisionKey::operator=(const SHCollisionKey& rhs) noexcept - { - if (this == &rhs) - return *this; - - value[0] = rhs.value[0]; - value[1] = rhs.value[1]; - - return *this; - } - - SHCollisionKey& SHCollisionKey::operator=(SHCollisionKey&& rhs) noexcept - { - value[0] = rhs.value[0]; - value[1] = rhs.value[1]; - - return *this; - } - - bool SHCollisionKey::operator==(const SHCollisionKey& rhs) const - { - // When checking for equal, check both ways. - // Exact Match (A, idxA, B, idxB) - const bool EXACT_MATCH = value[0] == rhs.value[0] && value[1] == rhs.value[1]; - - // Flipped Match: (B, idxB, A, idxA) - const bool FLIPPED_MATCH = value[0] == rhs.value[1] && value[1] == rhs.value[0]; - - return EXACT_MATCH || FLIPPED_MATCH; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - EntityID SHCollisionKey::GetEntityA() const noexcept - { - return ids[ENTITY_A]; - } - - EntityID SHCollisionKey::GetEntityB() const noexcept - { - return ids[ENTITY_B]; - } - - uint32_t SHCollisionKey::GetShapeIndexA() const noexcept - { - return ids[SHAPE_INDEX_A]; - } - - uint32_t SHCollisionKey::GetShapeIndexB() const noexcept - { - return ids[SHAPE_INDEX_B]; - } - - const SHRigidBodyComponent* SHCollisionKey::GetRigidBodyA() const noexcept - { - return SHComponentManager::GetComponent_s(ids[ENTITY_A]); - } - - const SHRigidBodyComponent* SHCollisionKey::GetRigidBodyB() const noexcept - { - return SHComponentManager::GetComponent_s(ids[ENTITY_B]); - } - - const SHCollisionShape* SHCollisionKey::GetCollisionShapeA() const noexcept - { - const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_A]); - return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[SHAPE_INDEX_A]); - } - - const SHCollisionShape* SHCollisionKey::GetCollisionShapeB() const noexcept - { - const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent(ids[ENTITY_B]); - return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[SHAPE_INDEX_B]); - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionKey::SetEntityA(EntityID entityID) noexcept - { - ids[ENTITY_A] = entityID; - } - - void SHCollisionKey::SetEntityB(EntityID entityID) noexcept - { - ids[ENTITY_B] = entityID; - } - - void SHCollisionKey::SetCollisionShapeA(uint32_t shapeIndexA) noexcept - { - ids[SHAPE_INDEX_A] = shapeIndexA; - } - - void SHCollisionKey::SetCollisionShapeB(uint32_t shapeIndexB) noexcept - { - ids[SHAPE_INDEX_B] = shapeIndexB; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - std::size_t SHCollisionKeyHash::operator()(const SHCollisionKey& id) const - { - static constexpr int NUM_IDS = ARRAYSIZE(id.ids); - - // Hashable is not a word. Sue me. - auto hashablePtr = reinterpret_cast::const_pointer>(id.ids); - return std::hash{}(std::u32string_view(hashablePtr, NUM_IDS)); - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h deleted file mode 100644 index bacf2255..00000000 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHContact.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************************** - * \file SHManifold.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collision Manifold - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Primary Header -#include "Physics/Dynamics/SHRigidBody.h" -#include "Physics/Collision/Shapes/SHCollisionShape.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a value that represents the touching features of a contact. - */ - union SHContactFeatures - { - public: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - struct - { - uint8_t inI; // Incoming Incident Edge - uint8_t outI; // Outgoing Incident Edge - uint8_t inR; // Incoming Reference Edge - uint8_t outR; // Outgoing Reference Edge - }; - - uint32_t key = std::numeric_limits::max(); - }; - - /** - * @brief - * Encapsulates a physical collision contact. - */ - struct SH_API SHContact - { - public: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr int NUM_TANGENTS = 2; - - float penetration = 0.0f; - float bias = 0.0f; // Restitution + Baumguarte factor - float normalImpulse = 0.0f; // Accumulated normal impulse - float normalMass = 0.0f; // Effective mass along the normal - float tangentImpulse[NUM_TANGENTS] = { 0.0f }; // Accumulated tangent impulses - float tangentMass[NUM_TANGENTS] = { 0.0f }; // Effective masses along the tangents - - SHVec3 position; - SHVec3 rA; // Vector from COM of A to the contact - SHVec3 rB; // Vector from COM of B to the contact - SHContactFeatures featurePair; - }; -} - -#pragma once diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h deleted file mode 100644 index e60e5329..00000000 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************************** - * \file SHManifold.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collision Manifold - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Primary Header -#include "Physics/Dynamics/SHRigidBody.h" -#include "Physics/Collision/Narrowphase/SHSATInfo.h" -#include "Physics/Collision/Shapes/SHCollisionShape.h" -#include "SHContact.h" -#include "SHCollisionEvents.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - struct SH_API SHManifold - { - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHManifold (SHCollisionShape* a, SHCollisionShape* b) noexcept; - SHManifold (const SHManifold& rhs) noexcept; - SHManifold (SHManifold&& rhs) noexcept; - - ~SHManifold () noexcept = default; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHManifold& operator=(const SHManifold& rhs) noexcept; - SHManifold& operator=(SHManifold&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - // We only need 4 contact points to build a stable manifold. - static constexpr int MAX_NUM_CONTACTS = 4; - - SHCollisionShape* shapeA; - SHCollisionShape* shapeB; - - SHRigidBody* bodyA = nullptr; - SHRigidBody* bodyB = nullptr; - - uint32_t numContacts = 0; - SHCollisionState state = SHCollisionState::INVALID; - - SHSATInfo cachedSATInfo; - - SHVec3 normal; - SHVec3 tangents[SHContact::NUM_TANGENTS]; - SHContact contacts[MAX_NUM_CONTACTS]; - - }; - -} // namespace SHADE - -#include "SHManifold.hpp" - diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp b/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp deleted file mode 100644 index f1b93a43..00000000 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHManifold.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************************** - * \file SHManifold.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collision Manifold - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Primary Header -#include "SHManifold.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - inline SHManifold::SHManifold(SHCollisionShape* a, SHCollisionShape* b) noexcept - : shapeA { a } - , shapeB { b } - { - bodyA = shapeA->collider->rigidBody; - bodyB = shapeB->collider->rigidBody; - } - - inline SHManifold::SHManifold(const SHManifold& rhs) noexcept - : shapeA { rhs.shapeA } - , shapeB { rhs.shapeB } - , bodyA { rhs.bodyA } - , bodyB { rhs.bodyB } - , numContacts { rhs.numContacts } - , state { rhs.state } - , cachedSATInfo { rhs.cachedSATInfo } - , normal { rhs.normal } - { - static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); - memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); - - for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) - tangents[i] = rhs.tangents[i]; - } - - inline SHManifold::SHManifold(SHManifold&& rhs) noexcept - : shapeA { rhs.shapeA } - , shapeB { rhs.shapeB } - , bodyA { rhs.bodyA } - , bodyB { rhs.bodyB } - , numContacts { rhs.numContacts } - , state { rhs.state } - , cachedSATInfo { rhs.cachedSATInfo } - , normal { rhs.normal } - { - static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); - memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); - - for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) - tangents[i] = rhs.tangents[i]; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - inline SHManifold& SHManifold::operator=(const SHManifold& rhs) noexcept - { - if (this == &rhs) - return *this; - - shapeA = rhs.shapeA; - shapeB = rhs.shapeB; - bodyA = rhs.bodyA; - bodyB = rhs.bodyB; - numContacts = rhs.numContacts; - state = rhs.state; - cachedSATInfo = rhs.cachedSATInfo; - normal = rhs.normal; - - static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); - memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); - - for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) - tangents[i] = rhs.tangents[i]; - - return *this; - } - - inline SHManifold& SHManifold::operator=(SHManifold&& rhs) noexcept - { - shapeA = rhs.shapeA; - shapeB = rhs.shapeB; - bodyA = rhs.bodyA; - bodyB = rhs.bodyB; - numContacts = rhs.numContacts; - state = rhs.state; - cachedSATInfo = rhs.cachedSATInfo; - normal = rhs.normal; - - static constexpr size_t SIZE_OF_CONTACTS = sizeof(SHContact) * static_cast(MAX_NUM_CONTACTS); - memcpy_s(contacts, SIZE_OF_CONTACTS, rhs.contacts, SIZE_OF_CONTACTS); - - for (int i = 0; i < SHContact::NUM_TANGENTS; ++i) - tangents[i] = rhs.tangents[i]; - - return *this; - } -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp deleted file mode 100644 index 6c82c3a5..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCapsuleVsConvex.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************************** - * \file SHCapsuleVsConvex.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Detecting Collisions between a capsule and a convex - * polyhedron. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollision.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Physics/Collision/Shapes/SHConvexPolyhedron.h" - -// TODO - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHCollision::CapsuleVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return false; - } - - bool SHCollision::ConvexVsCapsule(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return CapsuleVsConvex(B, A); - } - - bool SHCollision::CapsuleVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return false; - } - - bool SHCollision::ConvexVsCapsule(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return CapsuleVsConvex(manifold, B, A); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h deleted file mode 100644 index 060d42cc..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ /dev/null @@ -1,222 +0,0 @@ -/**************************************************************************************** - * \file SHCollision.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for the Detecting Collisions between two shapes - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "Math/Geometry/SHPlane.h" -#include "Physics/Collision/Contacts/SHManifold.h" -#include "Physics/Collision/Contacts/SHCollisionKey.h" -#include "Physics/Collision/Shapes/SHHalfEdgeStructure.h" - - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates static methods for testing for collision between two shapes. - */ - class SH_API SHCollision - { - public: - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /* Spheres VS X */ - - [[nodiscard]] static bool SphereVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool SphereVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - [[nodiscard]] static bool SphereVsCapsule (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool SphereVsCapsule (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - [[nodiscard]] static bool SphereVsConvex (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool SphereVsConvex (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - /* Capsule VS X */ - - [[nodiscard]] static bool CapsuleVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool CapsuleVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - [[nodiscard]] static bool CapsuleVsCapsule (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool CapsuleVsCapsule (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - [[nodiscard]] static bool CapsuleVsConvex (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool CapsuleVsConvex (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - /* Polygon VS X */ - - [[nodiscard]] static bool ConvexVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool ConvexVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - [[nodiscard]] static bool ConvexVsCapsule (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool ConvexVsCapsule (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - [[nodiscard]] static bool ConvexVsConvex (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - [[nodiscard]] static bool ConvexVsConvex (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - struct FaceQuery - { - bool colliding = false; // Allows for early out - int32_t closestFace = -1; - float bestDistance = std::numeric_limits::lowest(); - }; - - struct EdgeQuery - { - int32_t halfEdgeA = -1; - int32_t halfEdgeB = -1; - int32_t axis = -1; - float bestDistance = std::numeric_limits::lowest(); - }; - - struct ClipVertex - { - SHVec3 position; - SHContactFeatures featurePair; - }; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - // Sphere VS Convex - - static FaceQuery findClosestFace - ( - const SHSphere& sphere - , const SHConvexPolyhedron& polyhedron - ) noexcept; - - static int32_t findClosestPoint - ( - const SHSphere& sphere - , const SHConvexPolyhedron& polyhedron - , int32_t faceIndex - ) noexcept; - - static int32_t findVoronoiRegion - ( - const SHSphere& sphere - , const SHVec3& faceVertex - , const SHVec3& faceNormal - , const SHVec3& tangent1 - , const SHVec3& tangent2 - ) noexcept; - - // Capsule VS Convex - - // TODO: Capsule VS Convex uses the same gauss map optimisation as convex vs convex - - // Convex VS Convex - - /* - * ! References - * https://ia801303.us.archive.org/30/items/GDC2013Gregorius/GDC2013-Gregorius.pdf - * https://github.com/RandyGaul/qu3e/blob/master/src/collision/q3Collide.cpp - */ - - static FaceQuery queryFaceDirections - ( - const SHConvexPolyhedron& A - , const SHConvexPolyhedron& B - ) noexcept; - - - - static EdgeQuery queryEdgeDirections - ( - const SHConvexPolyhedron& A - , const SHConvexPolyhedron& B - ) noexcept; - - static bool buildMinkowskiFace - ( - const SHConvexPolyhedron& A - , const SHConvexPolyhedron& B - , int32_t edgeA - , int32_t edgeB - ) noexcept; - - static bool isMinkowskiFace - ( - const SHVec3& a - , const SHVec3& b - , const SHVec3& c - , const SHVec3& d - ) noexcept; - - static float distanceBetweenEdges - ( - const SHConvexPolyhedron& A - , const SHConvexPolyhedron& B - , int32_t edgeA - , int32_t edgeB - ) noexcept; - - static SHVec3 findClosestPointBetweenEdges - ( - const SHConvexPolyhedron& A - , const SHConvexPolyhedron& B - , int32_t edgeA - , int32_t edgeB - ) noexcept; - - static int32_t findIncidentFace - ( - const SHConvexPolyhedron& poly - , const SHVec3& normal - ) noexcept; - - static bool findFaceContacts - ( - SHManifold& manifold - , const SHConvexPolyhedron& incPoly - , int32_t incFace - , const SHConvexPolyhedron& refPoly - , int32_t refFace - , bool flip - ) noexcept; - - static std::vector clipPolygonWithPlane - ( - const std::vector& in - , int32_t numIn - , const SHPlane& plane - , int32_t planeIdx - ) noexcept; - - static std::vector reduceContacts - ( - const std::vector& in - , const SHVec3& faceNormal - ) noexcept; - - // Cached Convex VS Convex - - static bool cachedConvexVSConvex - ( - SHManifold& manifold - , const SHSATInfo& cachedInfo - , const SHConvexPolyhedron& A - , const SHConvexPolyhedron& B - ) noexcept; - }; -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp deleted file mode 100644 index 075e113f..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionDispatch.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the static Collision Dispatcher - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollisionDispatch.h" - -// Project Header -#include "SHCollision.h" -#include "Tools/Utilities/SHUtilities.h" - - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Static Data Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHCollisionDispatcher::ManifoldCollide SHCollisionDispatcher::manifoldCollide[NUM_SHAPES][NUM_SHAPES] - { - // vs Sphere / Box / Capsule - - { SHCollision::SphereVsSphere, SHCollision::SphereVsConvex, SHCollision::SphereVsCapsule } // Sphere - , { SHCollision::ConvexVsSphere, SHCollision::ConvexVsConvex, SHCollision::ConvexVsCapsule } // Box - , { SHCollision::CapsuleVsSphere, SHCollision::CapsuleVsConvex, SHCollision::CapsuleVsCapsule } // Capsule - }; - - const SHCollisionDispatcher::TriggerCollide SHCollisionDispatcher::triggerCollide[NUM_SHAPES][NUM_SHAPES] - { - // vs Sphere / Box / Capsule - - { SHCollision::SphereVsSphere, SHCollision::SphereVsConvex, SHCollision::SphereVsCapsule } // Sphere - , { SHCollision::ConvexVsSphere, SHCollision::ConvexVsConvex, SHCollision::ConvexVsCapsule } // Box - , { SHCollision::CapsuleVsSphere, SHCollision::CapsuleVsConvex, SHCollision::CapsuleVsCapsule } // Capsule - }; - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHCollisionDispatcher::Collide(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - const int TYPE_A = SHUtilities::ConvertEnum(A.GetType()); - const int TYPE_B = SHUtilities::ConvertEnum(B.GetType()); - - return manifoldCollide[TYPE_A][TYPE_B](manifold, A, B); - } - - bool SHCollisionDispatcher::Collide(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - const int TYPE_A = SHUtilities::ConvertEnum(A.GetType()); - const int TYPE_B = SHUtilities::ConvertEnum(B.GetType()); - - return triggerCollide[TYPE_A][TYPE_B](A, B); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp deleted file mode 100644 index e38a66a7..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHConvexVsConvex.cpp +++ /dev/null @@ -1,822 +0,0 @@ -/**************************************************************************************** - * \file SHConvexVsConvex.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Detecting Collisions between two convex polyhedrons. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollision.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Math/Geometry/SHPlane.h" -#include "Physics/Collision/Shapes/SHConvexPolyhedron.h" -#include "Physics/SHPhysicsConstants.h" -#include "Tools/Utilities/SHUtilities.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHCollision::ConvexVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - const SHConvexPolyhedron& POLY_A = dynamic_cast(A); - const SHConvexPolyhedron& POLY_B = dynamic_cast(B); - - const FaceQuery FACE_QUERY_A = queryFaceDirections(POLY_A, POLY_B); - if (FACE_QUERY_A.bestDistance > 0.0f) - return false; - - const FaceQuery FACE_QUERY_B = queryFaceDirections(POLY_B, POLY_A); - if (FACE_QUERY_B.bestDistance > 0.0f) - return false; - - const EdgeQuery EDGE_QUERY = queryEdgeDirections(POLY_A, POLY_B); - if (EDGE_QUERY.bestDistance > 0.0f) - return false; - - return true; - } - - bool SHCollision::ConvexVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - static constexpr float ABSOLUTE_TOLERANCE = 0.01f; // 0.0005 - static constexpr float RELATIVE_TOLERANCE = 0.95f; // 1.0002 - - const SHConvexPolyhedron& POLY_A = dynamic_cast(A); - const SHConvexPolyhedron& POLY_B = dynamic_cast(B); - - if (manifold.cachedSATInfo.type != SHSATInfo::Type::INVALID) - return cachedConvexVSConvex(manifold, manifold.cachedSATInfo, POLY_A, POLY_B); - - SHSATInfo cachedInfo; - - const FaceQuery FACE_QUERY_A = queryFaceDirections(POLY_A, POLY_B); - if (FACE_QUERY_A.bestDistance > 0.0f) - { - // cache the info - cachedInfo.type = SHSATInfo::Type::FACE; - cachedInfo.info.refPoly = SHSATInfo::ShapeID::A; - cachedInfo.info.refFace = FACE_QUERY_A.closestFace; - - manifold.cachedSATInfo = cachedInfo; - - return false; - } - - - const FaceQuery FACE_QUERY_B = queryFaceDirections(POLY_B, POLY_A); - if (FACE_QUERY_B.bestDistance > 0.0f) - { - // cache the info - cachedInfo.type = SHSATInfo::Type::FACE; - cachedInfo.info.refPoly = SHSATInfo::ShapeID::B; - cachedInfo.info.refFace = FACE_QUERY_B.closestFace; - - manifold.cachedSATInfo = cachedInfo; - - return false; - } - - const EdgeQuery EDGE_QUERY = queryEdgeDirections(POLY_A, POLY_B); - if (EDGE_QUERY.bestDistance > 0.0f) - { - // cache the info - cachedInfo.type = SHSATInfo::Type::EDGE; - cachedInfo.info.edgeA = EDGE_QUERY.halfEdgeA; - cachedInfo.info.edgeB = EDGE_QUERY.halfEdgeB; - - manifold.cachedSATInfo = cachedInfo; - - return false; - } - - // Apply weight to improve frame coherence of normal directions. - // We want a normal in the direction from A -> B, so we flip the normal if needed. - bool flipNormal = false; - const SHConvexPolyhedron* referencePoly = nullptr; - const SHConvexPolyhedron* incidentPoly = nullptr; - - FaceQuery minFaceQuery; - if (FACE_QUERY_A.bestDistance + ABSOLUTE_TOLERANCE > FACE_QUERY_B.bestDistance * RELATIVE_TOLERANCE) - { - minFaceQuery = FACE_QUERY_A; - referencePoly = &POLY_A; - incidentPoly = &POLY_B; - flipNormal = false; - - cachedInfo.type = SHSATInfo::Type::FACE; - cachedInfo.info.refPoly = SHSATInfo::ShapeID::A; - cachedInfo.info.refFace = minFaceQuery.closestFace; - } - else - { - minFaceQuery = FACE_QUERY_B; - referencePoly = &POLY_B; - incidentPoly = &POLY_A; - flipNormal = true; - - cachedInfo.type = SHSATInfo::Type::FACE; - cachedInfo.info.refPoly = SHSATInfo::ShapeID::B; - cachedInfo.info.refFace = minFaceQuery.closestFace; - } - - - - - // If an edge pair contains the closest distance,vwe ignore any face queries and find the closest points on - // each edge and use that as the contact point. - if (EDGE_QUERY.bestDistance * RELATIVE_TOLERANCE > minFaceQuery.bestDistance + ABSOLUTE_TOLERANCE) - { - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = POLY_A.GetHalfEdge(EDGE_QUERY.halfEdgeA); - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = POLY_B.GetHalfEdge(EDGE_QUERY.halfEdgeB); - - const SHVec3 HEAD_A = POLY_A.GetVertex(HALF_EDGE_A.headVertexIndex); - const SHVec3 TAIL_A = POLY_A.GetVertex(HALF_EDGE_A.tailVertexIndex); - const SHVec3 HEAD_B = POLY_B.GetVertex(HALF_EDGE_B.headVertexIndex); - const SHVec3 TAIL_B = POLY_B.GetVertex(HALF_EDGE_B.tailVertexIndex); - - const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); - const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); - - manifold.normal = SHVec3::Cross(VB, VA); - // Flip normal if need to ( A -> B) - if (SHVec3::Dot(manifold.normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) - manifold.normal = -manifold.normal; - - // In this scenario, we only have one contact - uint32_t numContacts = 0; - - SHContact contact; - contact.featurePair.key = EDGE_QUERY.axis; - contact.position = findClosestPointBetweenEdges(POLY_A, POLY_B, EDGE_QUERY.halfEdgeA, EDGE_QUERY.halfEdgeB); - contact.penetration = -EDGE_QUERY.bestDistance; - - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - // Cache the info - cachedInfo.type = SHSATInfo::Type::EDGE; - cachedInfo.info.edgeA = EDGE_QUERY.halfEdgeA; - cachedInfo.info.edgeB = EDGE_QUERY.halfEdgeB; - - manifold.cachedSATInfo = cachedInfo; - - return true; - } - - const SHVec3 REFERENCE_NORMAL = referencePoly->GetNormal(minFaceQuery.closestFace); - const int32_t INCIDENT_FACE_IDX = findIncidentFace(*incidentPoly, REFERENCE_NORMAL); - - manifold.cachedSATInfo = cachedInfo; - return findFaceContacts(manifold, *incidentPoly, INCIDENT_FACE_IDX, *referencePoly, minFaceQuery.closestFace, flipNormal); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollision::FaceQuery SHCollision::queryFaceDirections(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept - { - FaceQuery faceQuery; - - const int32_t NUM_FACES = A.GetFaceCount(); - for (const int32_t i : std::views::iota(0, NUM_FACES)) - { - const SHHalfEdgeStructure::Face& FACE_A = A.GetFace(i); - const SHVec3 NORMAL_A = A.GetNormal(i); - - // Smallest penetration is point closest to face normal - const SHVec3 SUPPORT_POINT = B.FindSupportPoint(-NORMAL_A); - - const SHVec3 VERTEX_A = A.GetVertex(FACE_A.vertexIndices[0].index); - - const SHPlane FACE_PLANE { VERTEX_A, NORMAL_A }; - const float DISTANCE = FACE_PLANE.SignedDistance(SUPPORT_POINT); - - if (DISTANCE > faceQuery.bestDistance) - { - faceQuery.bestDistance = DISTANCE; - faceQuery.closestFace = i; - } - } - - return faceQuery; - } - - SHCollision::EdgeQuery SHCollision::queryEdgeDirections(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept - { - EdgeQuery edgeQuery; - - const int32_t EDGE_COUNT_A = A.GetHalfEdgeCount(); - const int32_t EDGE_COUNT_B = B.GetHalfEdgeCount(); - - int32_t axis = -1; - for (int32_t i = 0; i < EDGE_COUNT_A; i += 2) - { - for (int32_t j = 0; j < EDGE_COUNT_B; j += 2) - { - const bool IS_MINKOWSKI_FACE = buildMinkowskiFace(A, B, i, j); - if (!IS_MINKOWSKI_FACE) - continue; - - ++axis; - - const float SEPARATION = distanceBetweenEdges(A, B, i, j); - if (SEPARATION > edgeQuery.bestDistance) - { - edgeQuery.bestDistance = SEPARATION; - edgeQuery.halfEdgeA = i; - edgeQuery.halfEdgeB = j; - edgeQuery.axis = axis; - } - } - } - - return edgeQuery; - } - - bool SHCollision::buildMinkowskiFace(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept - { - // Get Half Edge from both polygons - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); - const SHHalfEdgeStructure::HalfEdge& TWIN_EDGE_A = A.GetHalfEdge(HALF_EDGE_A.twinEdgeIndex); - - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(edgeB); - const SHHalfEdgeStructure::HalfEdge& TWIN_EDGE_B = B.GetHalfEdge(HALF_EDGE_B.twinEdgeIndex); - - - - // Get normals from face and twin edge face - const SHVec3 NA = A.GetNormal(HALF_EDGE_A.faceIndex); - const SHVec3 NB = A.GetNormal(TWIN_EDGE_A.faceIndex); - const SHVec3 NC = B.GetNormal(HALF_EDGE_B.faceIndex); - const SHVec3 ND = B.GetNormal(TWIN_EDGE_B.faceIndex); - - return isMinkowskiFace(NA, NB, -NC, -ND); - } - - bool SHCollision::isMinkowskiFace(const SHVec3& a, const SHVec3& b, const SHVec3& c, const SHVec3& d) noexcept - { - const SHVec3 BXA = SHVec3::Cross(b, a); - const SHVec3 DXC = SHVec3::Cross(d, c); - - const float CBA = SHVec3::Dot(c, BXA); - const float DBA = SHVec3::Dot(d, BXA); - const float ADC = SHVec3::Dot(a, DXC); - const float BDC = SHVec3::Dot(b, DXC); - - return CBA * DBA < 0.0f && ADC * BDC < 0.0f && CBA * BDC > 0.0f; - } - - float SHCollision::distanceBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept - { - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(edgeB); - - const SHVec3 HEAD_A = A.GetVertex(HALF_EDGE_A.headVertexIndex); - const SHVec3 TAIL_A = A.GetVertex(HALF_EDGE_A.tailVertexIndex); - const SHVec3 HEAD_B = B.GetVertex(HALF_EDGE_B.headVertexIndex); - const SHVec3 TAIL_B = B.GetVertex(HALF_EDGE_B.tailVertexIndex); - - const SHVec3 DIR_A = SHVec3::Normalise(HEAD_A - TAIL_A); - const SHVec3 DIR_B = SHVec3::Normalise(HEAD_B - TAIL_B); - - // Check if the edges are parallel (abs dot product is 1) - const float DOT_BETWEEN_EDGES = std::fabs(SHVec3::Dot(DIR_A, DIR_B)); - if (SHMath::CompareFloat(DOT_BETWEEN_EDGES, 1.0f)) - return std::numeric_limits::lowest(); - - SHVec3 normal = SHVec3::Cross(DIR_A, DIR_B); - // Flip normal if need to ( A -> B) - if (SHVec3::Dot(normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) - normal = -normal; - - return SHVec3::Dot(normal, HEAD_B - HEAD_A); - } - - SHVec3 SHCollision::findClosestPointBetweenEdges(const SHConvexPolyhedron& A, const SHConvexPolyhedron& B, int32_t edgeA, int32_t edgeB) noexcept - { - /* - * The two edges can be parameterised in the form p + tv - * - * LA(r) = A + rVA - * LB(s) = B + sVB - * - * The vector between the closest points is the cross product of VA and VB. - * Since the cross product is orthogonal to VA and VB, we can generalise this as: - * (LB(s) - LA(r)) /dot VA = 0 - * (LB(s) - LA(r)) /dot VB = 0 - * - * Where LB(s) - LA(r) is the same vector as VB X VA. - * - */ - - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(edgeA); - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(edgeB); - - const SHVec3 HEAD_A = A.GetVertex(HALF_EDGE_A.headVertexIndex); - const SHVec3 TAIL_A = A.GetVertex(HALF_EDGE_A.tailVertexIndex); - const SHVec3 HEAD_B = B.GetVertex(HALF_EDGE_B.headVertexIndex); - const SHVec3 TAIL_B = B.GetVertex(HALF_EDGE_B.tailVertexIndex); - - const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); - const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); - - /* - * Find a1, a2, b1, b2, c1, c2 to solve the simultaneous equation - * - * C' = TAIL_B - TAIL_A - * - * a = VB /dot U - * b = -VA /dot U - * c = C' /dot U - * - * U is either VA or VB - * - * TODO: Check if segments degenerate into a point - */ - - const SHVec3 C = TAIL_B - TAIL_A; - - const float VB_DOT_VA = SHVec3::Dot(VB, VA); // a1 - const float VB_DOT_VB = SHVec3::Dot(VB, VB); // a2 - const float AV_DOT_VA = SHVec3::Dot(-VA, VA); // b1 - const float AV_DOT_VB = SHVec3::Dot(-VA, VB); // b2 - const float C_DOT_VA = SHVec3::Dot(C, VA); // c1 - const float C_DOT_VB = SHVec3::Dot(C, VB); // c2 - - /* - * We only need to solve for R - * R = (c1a2 / a1 - c2) / ( b2 - a2b1/a1 ) - */ - - const float A2_OVER_A1 = VB_DOT_VA == 0.0f ? 0.0f : VB_DOT_VB / VB_DOT_VA; - const float NUMERATOR = C_DOT_VA * A2_OVER_A1 - C_DOT_VB; - const float DENOMINATOR = AV_DOT_VB - AV_DOT_VA * A2_OVER_A1; - - // We clamp it because the factor cannot be beyond [0,1] as it would be beyond the segment if it was so. - const float R = DENOMINATOR == 0.0f ? NUMERATOR : NUMERATOR / DENOMINATOR; - - // Just take a point from A since it's A -> B - return TAIL_A + R * VA; - } - - int32_t SHCollision::findIncidentFace(const SHConvexPolyhedron& poly, const SHVec3& normal) noexcept - { - // Get the most anti-parallel face to the normal - int32_t bestFace = 0; - float bestProjection = std::numeric_limits::max(); - - const int32_t NUM_FACES = poly.GetFaceCount(); - for (const int32_t i : std::views::iota(0, NUM_FACES)) - { - const SHVec3 INC_NORMAL = poly.GetNormal(i); - const float PROJECTION = SHVec3::Dot(INC_NORMAL, normal); - - if (PROJECTION < bestProjection) - { - bestProjection = PROJECTION; - bestFace = i; - } - } - - return bestFace; - } - - bool SHCollision::findFaceContacts(SHManifold& manifold, const SHConvexPolyhedron& incPoly, int32_t incFace, const SHConvexPolyhedron& refPoly, int32_t refFace, bool flip) noexcept - { - const SHHalfEdgeStructure::Face& INCIDENT_FACE = incPoly.GetFace(incFace); - const SHHalfEdgeStructure::Face& REFERENCE_FACE = refPoly.GetFace(refFace); - - const int32_t NUM_INCIDENT_VERTICES = static_cast(INCIDENT_FACE.vertexIndices.size()); - const int32_t NUM_REFERENCE_VERTICES = static_cast(REFERENCE_FACE.vertexIndices.size()); - - // Build incoming vertices to clip - std::vector clipIn; - clipIn.resize(NUM_INCIDENT_VERTICES * 2, ClipVertex{}); - - int32_t numClipIn = 0; - for (int32_t i = 0; i < NUM_INCIDENT_VERTICES; ++i) - { - const int32_t prevI = i - 1 < 0 ? NUM_INCIDENT_VERTICES - 1 : i - 1; - - const int32_t V_INDEX = INCIDENT_FACE.vertexIndices[i].index; - - // The incoming id is the previous edge - // The outgoing id is the current edge (where this vertex is the tail of) - - ClipVertex v; - v.position = incPoly.GetVertex(V_INDEX); - v.featurePair.inI = INCIDENT_FACE.vertexIndices[prevI].edgeIndex; - v.featurePair.outI = INCIDENT_FACE.vertexIndices[i].edgeIndex; - v.featurePair.inR = 0; - v.featurePair.outR = 0; - - clipIn[numClipIn++] = v; - } - - // Clip the vertices against the reference face side planes. - // Number of side planes == number of edges == number of vertices - const SHVec3 REF_NORMAL = refPoly.GetNormal(refFace); - for (int32_t i = 0; i < NUM_REFERENCE_VERTICES; ++i) - { - // Side plane can be built with the vertex on the edge and the plane's normal - // Plane normal = faceNormal X tangent (v2 - v1) - - const int32_t V1_INDEX = REFERENCE_FACE.vertexIndices[i].index; - const int32_t V2_INDEX = REFERENCE_FACE.vertexIndices[(i + 1) % NUM_REFERENCE_VERTICES].index; - - const SHVec3 V1 = refPoly.GetVertex(V1_INDEX); - const SHVec3 V2 = refPoly.GetVertex(V2_INDEX); - - const SHVec3 TANGENT = SHVec3::Normalise(V2 - V1); - const SHPlane CLIP_PLANE { V1, SHVec3::Cross(REF_NORMAL, TANGENT) }; - - std::vector clipOut = clipPolygonWithPlane(clipIn, numClipIn, CLIP_PLANE, REFERENCE_FACE.vertexIndices[i].edgeIndex); - if (clipOut.empty()) - return false; - - // Replace the clip container's contents with the clipped points for the next clipping pass - const int32_t NUM_CLIPPED = static_cast(clipOut.size()); - for (int32_t clippedIndex = 0; clippedIndex < NUM_CLIPPED; ++clippedIndex) - { - clipIn[clippedIndex].position = clipOut[clippedIndex].position; - clipIn[clippedIndex].featurePair.key = clipOut[clippedIndex].featurePair.key; - } - numClipIn = NUM_CLIPPED; - } - - // From the final set of clipped points, only keep the points that are below the reference plane. - const SHPlane REFERENCE_PLANE{ refPoly.GetVertex(REFERENCE_FACE.vertexIndices.front().index), REF_NORMAL }; - - uint32_t numContacts = 0; - - std::vector contacts; - for (int32_t i = 0; i < numClipIn; ++i) - { - const SHVec3 POS = clipIn[i].position; - const float DIST = REFERENCE_PLANE.SignedDistance(POS); - if (DIST <= 0.0f) - { - SHContact contact; - contact.position = POS; - contact.penetration = -DIST; - - if (flip) - { - contact.featurePair.inI = clipIn[i].featurePair.inR; - contact.featurePair.inR = clipIn[i].featurePair.inI; - contact.featurePair.outI = clipIn[i].featurePair.outR; - contact.featurePair.outR = clipIn[i].featurePair.outI; - } - else - { - contact.featurePair.key = clipIn[i].featurePair.key; - } - - contacts.emplace_back(contact); - ++numContacts; - } - } - - // Reduce contact manifold if more than 4 points - if (numContacts > 4) - { - const auto INDICES_TO_KEEP = reduceContacts(contacts, REF_NORMAL); - std::vector reducedContacts; - - const int32_t NUM_REDUCED = static_cast(INDICES_TO_KEEP.size()); - for (int32_t i = 0; i < NUM_REDUCED; ++i) - reducedContacts.emplace_back(contacts[INDICES_TO_KEEP[i]]); - - contacts.clear(); - // Copy contacts to main container - for (auto& contact : reducedContacts) - contacts.emplace_back(contact); - } - - // Remove potential duplicate contact points - // No way about this being an n^2 loop - static constexpr float THRESHOLD = SHPHYSICS_SAME_CONTACT_DISTANCE * SHPHYSICS_SAME_CONTACT_DISTANCE; - for (auto i = contacts.begin(); i != contacts.end(); ++i) - { - for (auto j = i + 1; j != contacts.end();) - { - const float D2 = SHVec3::DistanceSquared(i->position, j->position); - if (D2 < THRESHOLD) - j = contacts.erase(j); - else - ++j; - } - } - - // Copy final contacts into the manifold - numContacts = static_cast(contacts.size()); - for (int32_t i = 0; i < numContacts; ++i) - manifold.contacts[i] = contacts[i]; - - manifold.numContacts = numContacts; - manifold.normal = REF_NORMAL; - if (flip) - manifold.normal = -manifold.normal; - - return true; - } - - - std::vector SHCollision::clipPolygonWithPlane(const std::vector& in, int32_t numIn, const SHPlane& plane, int32_t planeIdx) noexcept - { - std::vector out; - - int32_t v1Index = numIn - 1; - int32_t v2Index = 0; - - float v1Distance = plane.SignedDistance(in[v1Index].position); - float v2Distance = 0.0f; - - for (int32_t i = 0; i < numIn; ++i) - { - v2Index = i; - - const SHVec3 v1Pos = in[v1Index].position; - const SHVec3 v2Pos = in[v2Index].position; - - v2Distance = plane.SignedDistance(v2Pos); - - // v1 in front, v2 behind - // keep the intersection point - if (v1Distance >= 0.0f && v2Distance < 0.0f) - { - ClipVertex intersection; - - // In case the edge is parallel, the intersection is just the start point - const bool IS_PARALLEL = SHMath::CompareFloat(SHVec3::Dot(v2Pos - v1Pos, plane.GetNormal()), 0.0f); - const float ALPHA = IS_PARALLEL ? 0.0f : v1Distance / SHVec3::Distance(v1Pos, v2Pos); - - intersection.position = SHVec3::ClampedLerp(v1Pos, v2Pos, ALPHA); - intersection.featurePair.inI = in[v1Index].featurePair.outI; - intersection.featurePair.outI = 0; - intersection.featurePair.inR = 0; - intersection.featurePair.outR = planeIdx; - - out.emplace_back(intersection); - } - - // v1 behind, v2 in front - // keep intersection point & v2 - if(v1Distance < 0.0f && v2Distance >= 0.0f) - { - ClipVertex intersection; - - // In case the edge is parallel, the intersection is just the start point - const bool IS_PARALLEL = SHMath::CompareFloat(SHVec3::Dot(v2Pos - v1Pos, plane.GetNormal()), 0.0f); - const float ALPHA = IS_PARALLEL ? 0.0f : -v1Distance / SHVec3::Distance(v1Pos, v2Pos); - - intersection.position = SHVec3::ClampedLerp(v1Pos, v2Pos, ALPHA); - intersection.featurePair.inI = 0; - intersection.featurePair.outI = in[v2Index].featurePair.inI; - intersection.featurePair.inR = planeIdx; - intersection.featurePair.outR = 0; - - out.emplace_back(intersection); - out.emplace_back(in[v2Index]); - } - - // both in front, keep v2 - if (v1Distance >= 0.0f && v2Distance >= 0.0f) - { - out.emplace_back(in[v2Index]); - } - - // v2 is the next v1 - v1Index = v2Index; - v1Distance = v2Distance; - } - - return out; - } - - std::vector SHCollision::reduceContacts(const std::vector& in, const SHVec3& faceNormal) noexcept - { - std::vector indicesToKeep; - - // Use a map to temporarily store and track the contacts we want - std::unordered_map contactMap; - const int32_t NUM_CONTACTS = static_cast(in.size()); - for (int32_t i = 0; i < NUM_CONTACTS; ++i) - contactMap.emplace(i, &in[i]); - - // Find the furthest point in a given direction - int32_t indexToKeep = -1; - float bestDistance = std::numeric_limits::lowest(); - - for (const auto& [index, contact] : contactMap) - { - const float DIST = SHVec3::Dot(contact->position, SHVec3::One); - if (DIST > bestDistance) - { - bestDistance = DIST; - indexToKeep = index; - } - } - - indicesToKeep.emplace_back(indexToKeep); - contactMap.erase(indexToKeep); - - - indexToKeep = -1; - bestDistance = std::numeric_limits::lowest(); - - // Find point furthest away from the first index - const SHVec3& FIRST_POS = in[indicesToKeep.back()].position; - for (const auto& [index, contact] : contactMap) - { - const float DIST_SQUARED = SHVec3::DistanceSquared(FIRST_POS, contact->position); - if (DIST_SQUARED > bestDistance) - { - bestDistance = DIST_SQUARED; - indexToKeep = index; - } - } - - indicesToKeep.emplace_back(indexToKeep); - contactMap.erase(indexToKeep); - - indexToKeep = -1; - - // We compute the triangle with the largest area. - // The area can be positive or negative depending on the winding order. - - float maxArea = std::numeric_limits::lowest(); - float minArea = std::numeric_limits::max(); - - int32_t maxAreaIndex = -1; - int32_t minAreaIndex = -1; - - const SHVec3& SECOND_POS = in[indicesToKeep.back()].position; - for (const auto& [index, contact] : contactMap) - { - const SHVec3& POS = contact->position; - const SHVec3 TO_P1 = FIRST_POS - POS; - const SHVec3 TO_P2 = SECOND_POS - POS; - - const float AREA = SHVec3::Dot(SHVec3::Cross(TO_P1, TO_P2), faceNormal) * 0.5f; - - if (AREA > maxArea) - { - maxArea = AREA; - maxAreaIndex = index; - } - - if (AREA < minArea) - { - minArea = AREA; - minAreaIndex = index; - } - } - - // Compare which triangle creates the largest area - bool isAreaPositive = false; - if (maxArea > (-minArea)) - { - isAreaPositive = true; - indexToKeep = maxAreaIndex; - } - else - { - isAreaPositive = false; - indexToKeep = minAreaIndex; - } - - indicesToKeep.emplace_back(indexToKeep); - contactMap.erase(indexToKeep); - - indexToKeep = -1; - - // For the last point, we want the point which forms the largest area that is winded opposite to the first triangle - // The areas should be inverted: If area was -ve, we want a +ve. Otherwise, vice versa. - float bestArea = 0.0f; - - const SHVec3& THIRD_POS = in[indicesToKeep.back()].position; - const SHVec3 ABC[3] = { FIRST_POS, SECOND_POS, THIRD_POS }; - - for (const auto& [index, contact] : contactMap) - { - const SHVec3& Q = contact->position; - - for (int i = 0; i < 3; ++i) - { - const int P1 = i; - const int P2 = (i + 1) % 3; - - const SHVec3 TO_P1 = ABC[P1] - Q; - const SHVec3 TO_P2 = ABC[P2] - Q; - - const float AREA = SHVec3::Dot(SHVec3::Cross(TO_P1, TO_P2), faceNormal) * 0.5f; - - if (isAreaPositive && AREA < bestArea) - { - bestArea = AREA; - indexToKeep = index; - } - - if (!isAreaPositive && AREA > bestArea) - { - bestArea = AREA; - indexToKeep = index; - } - } - } - - indicesToKeep.emplace_back(indexToKeep); - return indicesToKeep; - } - - bool SHCollision::cachedConvexVSConvex(SHManifold& manifold, const SHSATInfo& cachedInfo, const SHConvexPolyhedron& A, const SHConvexPolyhedron& B) noexcept - { - if (cachedInfo.type == SHSATInfo::Type::FACE) - { - SHASSERT(cachedInfo.info.refPoly != SHSATInfo::ShapeID::INVALID, "Attempted to perform cached SAT with an invalid cached collision!") - - // Assume the reference poly was A - const SHConvexPolyhedron* incPoly = &B; - const SHConvexPolyhedron* refPoly = &A; - bool flip = false; - - // Swap if it was B - if (cachedInfo.info.refPoly == SHSATInfo::ShapeID::B) - { - refPoly = &B; - incPoly = &A; - flip = true; - } - - const SHHalfEdgeStructure::Face& REF_FACE = refPoly->GetFace(cachedInfo.info.refFace); - const SHVec3 REF_NORMAL = refPoly->GetNormal(cachedInfo.info.refFace); - - const SHVec3 SUPPORT_POINT = incPoly->FindSupportPoint(-REF_NORMAL); - - const SHPlane REF_FACE_PLANE { refPoly->GetVertex(REF_FACE.vertexIndices[0].index), REF_NORMAL }; - const float DISTANCE = REF_FACE_PLANE.SignedDistance(SUPPORT_POINT); - - if (DISTANCE > 0.0f) - return false; - - // Find the incident face - const int32_t INCIDENT_FACE_INDEX = findIncidentFace(*incPoly, REF_NORMAL); - - return findFaceContacts(manifold, *incPoly, INCIDENT_FACE_INDEX, *refPoly, cachedInfo.info.refFace, flip); - } - - if (cachedInfo.type == SHSATInfo::Type::EDGE) - { - const float DISTANCE = distanceBetweenEdges(A, B, cachedInfo.info.edgeA, cachedInfo.info.edgeB); - if (DISTANCE > 0.0f) - return false; - - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_A = A.GetHalfEdge(cachedInfo.info.edgeA); - const SHHalfEdgeStructure::HalfEdge& HALF_EDGE_B = B.GetHalfEdge(cachedInfo.info.edgeB); - - const SHVec3 HEAD_A = A.GetVertex(HALF_EDGE_A.headVertexIndex); - const SHVec3 TAIL_A = A.GetVertex(HALF_EDGE_A.tailVertexIndex); - const SHVec3 HEAD_B = B.GetVertex(HALF_EDGE_B.headVertexIndex); - const SHVec3 TAIL_B = B.GetVertex(HALF_EDGE_B.tailVertexIndex); - - const SHVec3 VA = SHVec3::Normalise(HEAD_A - TAIL_A); - const SHVec3 VB = SHVec3::Normalise(HEAD_B - TAIL_B); - - manifold.normal = SHVec3::Cross(VB, VA); - // Flip normal if need to ( A -> B) - if (SHVec3::Dot(manifold.normal, HEAD_A - A.GetWorldCentroid()) < 0.0f) - manifold.normal = -manifold.normal; - - // In this scenario, we only have one contact - uint32_t numContacts = 0; - - SHContact contact; - // Take feature pair key from previous manifold - contact.featurePair.key = manifold.contacts[0].featurePair.key; - contact.position = findClosestPointBetweenEdges(A, B, cachedInfo.info.edgeA, cachedInfo.info.edgeB); - contact.penetration = -DISTANCE; - - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - return true; - } - - // Should never reach this. - return false; - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.h deleted file mode 100644 index 25606084..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************************** - * \file SHSATInfo.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for storing information of collision detection between two - * convex shapes using SAT. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include // int32_t - -namespace SHADE -{ - /** - * @brief - * Primarily used to cached collision information between two convex polyhedrons for - * temporal coherence. - */ - struct SHSATInfo - { - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - enum class ShapeID : int32_t - { - A = 0 - , B = 1 - - , INVALID = -1 - }; - - enum class Type - { - EDGE - , FACE - - , INVALID = -1 - }; - - union ContactInfo - { - struct // FaceContact - { - ShapeID refPoly; - int32_t refFace; - }; - - struct // Edge Contact - { - int32_t edgeA; - int32_t edgeB; - }; - }; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - Type type; - ContactInfo info; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHSATInfo () noexcept; - SHSATInfo (const SHSATInfo& rhs) noexcept; - SHSATInfo (SHSATInfo&& rhs) noexcept; - ~SHSATInfo () noexcept = default; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHSATInfo& operator= (const SHSATInfo& rhs) noexcept; - SHSATInfo& operator= (SHSATInfo&& rhs) noexcept; - }; - -} // namespace SHADE - -#include "SHSATInfo.hpp" diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.hpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.hpp deleted file mode 100644 index 8e51d54b..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSATInfo.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************************** - * \file SHSATInfo.hpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation of inlined methods for cached SAT Info. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Primary Header -#include "SHSATInfo.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-----------------------------------------------------------------------------------*/ - - inline SHSATInfo::SHSATInfo() noexcept - : type { Type::INVALID } - , info {} - { - info.refPoly = ShapeID::INVALID; - info.refFace = -1; - } - - inline SHSATInfo::SHSATInfo(const SHSATInfo& rhs) noexcept - : type { rhs.type } - , info {} - { - info.refPoly = rhs.info.refPoly; - info.refFace = rhs.info.refFace; - - } - - inline SHSATInfo::SHSATInfo(SHSATInfo&& rhs) noexcept - : type { rhs.type } - , info {} - { - info.refPoly = rhs.info.refPoly; - info.refFace = rhs.info.refFace; - } - - /*---------------------------------------------------------------------------------- */ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------- */ - - inline SHSATInfo& SHSATInfo::operator=(const SHSATInfo& rhs) noexcept - { - if (this == &rhs) - return *this; - - type = rhs.type; - info.refPoly = rhs.info.refPoly; - info.refFace = rhs.info.refFace; - - return *this; - } - - inline SHSATInfo& SHSATInfo::operator=(SHSATInfo&& rhs) noexcept - { - type = rhs.type; - info.refPoly = rhs.info.refPoly; - info.refFace = rhs.info.refFace; - - return *this; - } -} diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp deleted file mode 100644 index 25515fd7..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsCapsule.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************************** - * \file SHSphereVsCapsule.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Detecting Collisions between a sphere and a capsule. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollision.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Physics/Collision/Shapes/SHSphere.h" - -// TODO - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHCollision::SphereVsCapsule(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return false; - } - - bool SHCollision::CapsuleVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return SphereVsCapsule(B, A); - } - - bool SHCollision::SphereVsCapsule(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return false; - } - - bool SHCollision::CapsuleVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return SphereVsCapsule(manifold, B, A); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp deleted file mode 100644 index 07466ab1..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/**************************************************************************************** - * \file SHSphereVsConvex.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Detecting Collisions between a sphere and a convex - * polyhedron. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollision.h" - -// Project Headers -#include "Math/Geometry/SHPlane.h" -#include "Math/SHMathHelpers.h" -#include "Physics/Collision/Shapes/SHSphere.h" -#include "Physics/Collision/Shapes/SHConvexPolyhedron.h" -#include "Physics/SHPhysicsConstants.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHCollision::SphereVsConvex(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - const SHSphere& SPHERE = dynamic_cast(A); - const SHConvexPolyhedron& POLYHEDRON = dynamic_cast(B); - - const SHVec3 CENTER = SPHERE.Center; - const float RADIUS = SPHERE.GetWorldRadius(); - - // Find closest face - const FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON); - if (!FACE_QUERY.colliding) - return false; - - // If center of sphere is inside the polyhedron (below the face) - if (FACE_QUERY.bestDistance < SHMath::EPSILON) - return true; - - // Find closest face of polygon to circle - const int32_t CLOSEST_POINT = findClosestPoint(SPHERE, POLYHEDRON, FACE_QUERY.closestFace); - - const auto& FACE = POLYHEDRON.GetFace(FACE_QUERY.closestFace); - const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace); - const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); - - // Get points and build tangents - const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; - const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1; - - const SHVec3 P1 = POLYHEDRON.GetVertex(FACE.vertexIndices[CLOSEST_POINT].index); - const SHVec3 P2 = POLYHEDRON.GetVertex(FACE.vertexIndices[P2_INDEX].index); - const SHVec3 P3 = POLYHEDRON.GetVertex(FACE.vertexIndices[P3_INDEX].index); - - const SHVec3 TANGENT_1 = SHVec3::Normalise(P2 - P1); - const SHVec3 TANGENT_2 = SHVec3::Normalise(P3 - P1); - - // Get the voronoi region it belongs in - const int32_t REGION = findVoronoiRegion(SPHERE, P1, FACE_NORMAL, TANGENT_1, TANGENT_2); - - return REGION > 0; - } - - bool SHCollision::ConvexVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - return SphereVsConvex(B, A); - } - - bool SHCollision::SphereVsConvex(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - // Convert to underlying types - // For the convex, we only need the convex polyhedron shape since the get vertex is pure virtual. - const SHSphere& SPHERE = dynamic_cast(A); - const SHConvexPolyhedron& POLYHEDRON = dynamic_cast(B); - - const SHVec3 CENTER = SPHERE.Center; - const float RADIUS = SPHERE.GetWorldRadius(); - - const FaceQuery FACE_QUERY = findClosestFace(SPHERE, POLYHEDRON); - if (!FACE_QUERY.colliding) - return false; - - uint32_t numContacts = 0; - const float PENETRATION = RADIUS - FACE_QUERY.bestDistance; - - SHContact contact; - contact.featurePair.key = 0; - - // Check if center is inside polyhedron (below the face) - if (FACE_QUERY.bestDistance < SHMath::EPSILON) - { - manifold.normal = -POLYHEDRON.GetNormal(FACE_QUERY.closestFace); - - contact.penetration = PENETRATION; - contact.position = CENTER; - - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - return true; - } - - // Find closest face of polygon to circle - const int32_t CLOSEST_POINT = findClosestPoint(SPHERE, POLYHEDRON, FACE_QUERY.closestFace); - - const auto& FACE = POLYHEDRON.GetFace(FACE_QUERY.closestFace); - const SHVec3& FACE_NORMAL = POLYHEDRON.GetNormal(FACE_QUERY.closestFace); - const int32_t NUM_VERTICES = static_cast(FACE.vertexIndices.size()); - - // Get points and build tangents - const int32_t P2_INDEX = (CLOSEST_POINT + 1) % NUM_VERTICES; - const int32_t P3_INDEX = CLOSEST_POINT == 0 ? NUM_VERTICES - 1 : CLOSEST_POINT - 1; - - const SHVec3 P1 = POLYHEDRON.GetVertex(FACE.vertexIndices[CLOSEST_POINT].index); - const SHVec3 P2 = POLYHEDRON.GetVertex(FACE.vertexIndices[P2_INDEX].index); - const SHVec3 P3 = POLYHEDRON.GetVertex(FACE.vertexIndices[P3_INDEX].index); - - const SHVec3 TANGENT_1 = SHVec3::Normalise(P2 - P1); - const SHVec3 TANGENT_2 = SHVec3::Normalise(P3 - P1); - - // Get the voronoi region it belongs in - const int32_t REGION = findVoronoiRegion(SPHERE, P1, FACE_NORMAL, TANGENT_1, TANGENT_2); - if (REGION == 0) - return false; - - // Create contact information based on region - - const SHVec3 P1_TO_CENTER = CENTER - P1; - switch (REGION) - { - case 1: // Region A - case 2: // Region B - { - // Find closest point - const SHVec3& TANGENT = REGION == 1 ? TANGENT_1 : TANGENT_2; - const SHVec3 CP = P1 + TANGENT * SHVec3::Dot(P1_TO_CENTER, TANGENT); - const SHVec3 CP_TO_CENTER = CENTER - CP; - - manifold.normal = -SHVec3::Normalise(CP_TO_CENTER); - - contact.penetration = RADIUS - SHVec3::Dot(CP_TO_CENTER, -manifold.normal); - contact.position = CP; - - break; - } - case 3: // Region C - { - manifold.normal = -SHVec3::Normalise(P1_TO_CENTER); - - contact.penetration = RADIUS - P1_TO_CENTER.Length(); - contact.position = P1; - - break; - } - case 4: // Region D - { - manifold.normal = -FACE_NORMAL; - - contact.penetration = PENETRATION; - contact.position = CENTER - FACE_NORMAL * RADIUS; - - break; - } - default: return false; // Should never happen - } - - manifold.contacts[numContacts++] = contact; - manifold.numContacts = numContacts; - - return true; - } - - bool SHCollision::ConvexVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - if (SphereVsConvex(manifold, B, A)) - { - manifold.normal = -manifold.normal; - return true; - }; - - return false; - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollision::FaceQuery SHCollision::findClosestFace - ( - const SHSphere& sphere - , const SHConvexPolyhedron& polyhedron - ) noexcept - { - FaceQuery faceQuery; - - const SHVec3 CENTER = sphere.Center; - const float RADIUS = sphere.GetWorldRadius(); - - /* - * Test against each face. - * - * 1. For each face, build a plane in point-normal form. - * 2. Find the signed distance from plane to center of sphere. - * 3. Save best distance and face. - */ - for (int32_t i = 0; i < polyhedron.GetFaceCount(); ++i) - { - const SHHalfEdgeStructure::Face& FACE = polyhedron.GetFace(i); - - // Build plane equation - // Use first vertex to build the plane - const SHPlane FACE_PLANE { polyhedron.GetVertex(FACE.vertexIndices[0].index), polyhedron.GetNormal(i) }; - - // Find signed distance of center to plane - const float SIGNED_DIST = FACE_PLANE.SignedDistance(CENTER); - - // Early out: - // If face is facing away from center, signed dist is negative. - // Therefore signed distance is only positive when sphere is in front of the face. - if (SIGNED_DIST > RADIUS) - return faceQuery; - - if (SIGNED_DIST > faceQuery.bestDistance) - { - faceQuery.bestDistance = SIGNED_DIST; - faceQuery.closestFace = i; - } - } - - faceQuery.colliding = true; - return faceQuery; - } - - int32_t SHCollision::findClosestPoint - ( - const SHSphere& sphere - , const SHConvexPolyhedron& polyhedron - , int32_t faceIndex - ) noexcept - { - // Find closest point on face - int32_t closestPointIndex = -1; - - const SHVec3 CENTER = sphere.Center; - - const SHHalfEdgeStructure::Face& FACE = polyhedron.GetFace(faceIndex); - const int32_t NUM_VERITICES = static_cast(FACE.vertexIndices.size()); - - float smallestDist = std::numeric_limits::max(); - for (int32_t i = 0; i < NUM_VERITICES; ++i) - { - const SHVec3 POINT = polyhedron.GetVertex(FACE.vertexIndices[i].index); - const float DIST = SHVec3::DistanceSquared(CENTER, POINT); - - if (DIST < smallestDist) - { - smallestDist = DIST; - closestPointIndex = i; - } - } - - return closestPointIndex; - } - - int32_t SHCollision::findVoronoiRegion - ( - const SHSphere& sphere - , const SHVec3& faceVertex - , const SHVec3& faceNormal - , const SHVec3& tangent1 - , const SHVec3& tangent2 - ) noexcept - { - static constexpr int NUM_TANGENTS = 2; - - // Check against voronoi regions of the face to determine the type of the intersection test - // We have 3 voronoi regions to check: 1 -> 2, 1 -> 3 and 1 -> center - // If none of these are true, the sphere is above the face but not separating - - /* - * | 2 - * _ _ _ _ _ _ | _ _ _ - * / / - * | / regionD | / regionA - * |/ _ _ _ _ _|/ _ _ _ - * 3/ regionB /1 - * / / regionC - * - */ - - const SHVec3 CENTER = sphere.Center; - const float RADIUS = sphere.GetWorldRadius(); - - const SHVec3 TANGENTS [NUM_TANGENTS] { tangent1, tangent2 }; - const SHVec3 ADJACENT_NORMALS [NUM_TANGENTS] { SHVec3::Cross(tangent1, faceNormal), SHVec3::Cross(faceNormal, tangent2) }; - - const SHVec3 FACE_TO_CENTER = CENTER - faceVertex; - - // To be inside either region A or B, 2 conditions must be satisfied - // 1. Same side as tangent - // 2. Same side as adjacent normal - - // Check Region A & B - for (int i = 0; i < NUM_TANGENTS; ++i) - { - float projection = SHVec3::Dot(FACE_TO_CENTER, TANGENTS[i]); - if (projection >= 0.0f) - { - // Find closest point - const SHVec3 CLOSEST_POINT = faceVertex + projection * TANGENTS[i]; - - projection = SHVec3::Dot(FACE_TO_CENTER, ADJACENT_NORMALS[i]); - if (projection >= 0.0f) - { - if (projection > RADIUS) - return 0; - - // Region 1 or 2 ( A or B) - return i + 1; - } - } - } - - // Check Region C - // Face to vertex is in the opposite direction of any tangent. - const float PROJECTION = SHVec3::Dot(FACE_TO_CENTER, tangent1); - if (PROJECTION < 0) - { - const float DISTANCE_SQUARED = SHVec3::DistanceSquared(faceVertex, CENTER); - if (DISTANCE_SQUARED > RADIUS * RADIUS) - return 0; - - return 3; - } - - // Belongs in region D by default - return 4; - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp deleted file mode 100644 index 1a76924e..00000000 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsSphere.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************************** - * \file SHSphereVsSphere.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Detecting Collisions between two spheres - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollision.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Physics/Collision/Shapes/SHSphere.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHCollision::SphereVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - const SHSphere& SPHERE_A = dynamic_cast(A); - const SHSphere& SPHERE_B = dynamic_cast(B); - - const SHVec3 CENTER_A = SPHERE_A.Center; - const float RADIUS_A = SPHERE_A.Radius; - const SHVec3 CENTER_B = SPHERE_B.Center; - const float RADIUS_B = SPHERE_B.Radius; - - - const SHVec3 A_TO_B = CENTER_B - CENTER_A; - const float DISTANCE_BETWEEN_CENTERS_SQUARED = A_TO_B.LengthSquared(); - - const float COMBINED_RADIUS = RADIUS_B + RADIUS_A; - const float COMBINED_RADIUS_SQUARED = COMBINED_RADIUS * COMBINED_RADIUS; - - if (DISTANCE_BETWEEN_CENTERS_SQUARED > COMBINED_RADIUS_SQUARED) - return false; - } - - bool SHCollision::SphereVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept - { - // Convert to underlying types - const SHSphere& SPHERE_A = dynamic_cast(A); - const SHSphere& SPHERE_B = dynamic_cast(B); - - const SHVec3 CENTER_A = SPHERE_A.Center; - const float RADIUS_A = SPHERE_A.Radius; - const SHVec3 CENTER_B = SPHERE_B.Center; - const float RADIUS_B = SPHERE_B.Radius; - - - const SHVec3 A_TO_B = CENTER_B - CENTER_A; - const float DISTANCE_BETWEEN_CENTERS_SQUARED = A_TO_B.LengthSquared(); - - const float COMBINED_RADIUS = RADIUS_B + RADIUS_A; - const float COMBINED_RADIUS_SQUARED = COMBINED_RADIUS * COMBINED_RADIUS; - - if (DISTANCE_BETWEEN_CENTERS_SQUARED > COMBINED_RADIUS_SQUARED) - return false; - - // Only populate the manifold if there is a collision - - uint32_t numContacts = 0; - - SHContact contact; - contact.featurePair.key = 0; - - if (SHMath::CompareFloat(DISTANCE_BETWEEN_CENTERS_SQUARED, 0.0f)) - { - manifold.normal = SHVec3::UnitY; - contact.position = CENTER_A; - contact.penetration = RADIUS_B; - - manifold.contacts[numContacts++] = contact; - } - else - { - manifold.normal = SHVec3::Normalise(A_TO_B); - contact.position = CENTER_B - manifold.normal * RADIUS_B; - contact.penetration = COMBINED_RADIUS - A_TO_B.Length(); - - manifold.contacts[numContacts++] = contact; - } - - manifold.numContacts = numContacts; - - return true; - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCollider.cpp deleted file mode 100644 index 2e7cc3c6..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/**************************************************************************************** - * \file SHCollider.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Base Collider Class. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollider.h" - -// Project Headers -#include "Broadphase/SHDynamicAABBTree.h" -#include "Events/SHEvent.h" -#include "Math/SHMathHelpers.h" -#include "Physics/SHPhysicsEvents.h" -#include "Physics/Dynamics/SHRigidBody.h" - - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept - : entityID { eid } - , flags { 0 } - , rigidBody { nullptr } - , shapeLibrary { nullptr } - , broadphase { nullptr } - , transform { worldTransform } - { - flags |= ACTIVE_FLAG; - flags |= MOVED_FLAG; - } - - SHCollider::SHCollider(const SHCollider& rhs) noexcept - : entityID { rhs.entityID } - , flags { rhs.flags } - , rigidBody { rhs.rigidBody } - , shapeLibrary { rhs.shapeLibrary } - , broadphase { rhs.broadphase } - , transform { rhs.transform } - {} - - SHCollider::SHCollider(SHCollider&& rhs) noexcept - : entityID { rhs.entityID } - , flags { rhs.flags } - , rigidBody { rhs.rigidBody } - , shapeLibrary { rhs.shapeLibrary } - , broadphase { rhs.broadphase } - , transform { rhs.transform } - {} - - SHCollider::~SHCollider() noexcept - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Composite Collider {}. Unable to add destroy collider!", entityID) - return; - } - - for (auto* shape : shapes) - shapeLibrary->DestroyShape(shape); - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollider& SHCollider::operator=(const SHCollider& rhs) noexcept - { - if (this == &rhs) - return *this; - - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) - return *this; - } - - entityID = rhs.entityID; - flags = rhs.flags; - rigidBody = rhs.rigidBody; - shapeLibrary = rhs.shapeLibrary; - broadphase = rhs.broadphase; - transform = rhs.transform; - - copyShapes(rhs); - - return *this; - } - - SHCollider& SHCollider::operator=(SHCollider&& rhs) noexcept - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add copy shapes!", entityID) - return *this; - } - - entityID = rhs.entityID; - flags = rhs.flags; - rigidBody = rhs.rigidBody; - shapeLibrary = rhs.shapeLibrary; - broadphase = rhs.broadphase; - transform = rhs.transform; - - copyShapes(rhs); - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - EntityID SHCollider::GetEntityID() const noexcept - { - return entityID; - } - - SHCollider::Type SHCollider::GetType() const noexcept - { - if (flags & COMPOSITE_FLAG) - return Type::COMPOSITE; - - if (flags & HULL_FLAG) - return Type::HULL; - - return Type::INVALID; - } - - bool SHCollider::IsActive() const noexcept - { - return flags & ACTIVE_FLAG; - } - - bool SHCollider::GetDebugDrawState() const noexcept - { - return flags & DRAW_FLAG; - } - - const SHTransform& SHCollider::GetTransform() const noexcept - { - return transform; - } - - const SHVec3& SHCollider::GetPosition() const noexcept - { - return transform.position; - } - - const SHQuaternion& SHCollider::GetOrientation() const noexcept - { - return transform.orientation; - } - - const SHVec3& SHCollider::GetScale() const noexcept - { - return transform.scale; - } - - const SHCollider::CollisionShapes& SHCollider::GetCollisionShapes() const noexcept - { - return shapes; - } - - SHCollisionShape* SHCollider::GetCollisionShape(int index) const - { - const int NUM_SHAPES = static_cast(shapes.size()); - - if (index < 0 || index >= NUM_SHAPES) - throw std::invalid_argument("Out-of-range index!"); - - return shapes[index]; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollider::SetType(Type type) noexcept - { - if (type == Type::COMPOSITE) - flags |= COMPOSITE_FLAG; - - if (type == Type::HULL) - flags |= HULL_FLAG; - } - - void SHCollider::SetIsActive(bool state) noexcept - { - const bool PREV_STATE = flags & ACTIVE_FLAG; - state ? flags |= ACTIVE_FLAG : flags &= ~(ACTIVE_FLAG); - - if (!broadphase) - return; - - for (auto* shape : shapes) - { - if (PREV_STATE) // Previously inactive - broadphase->Insert(shape->id, shape->ComputeAABB()); - else // Previously active - broadphase->Remove(shape->id); - } - } - - void SHCollider::SetDebugDrawState(bool state) noexcept - { - state ? flags |= DRAW_FLAG : flags &= ~(DRAW_FLAG); - - #ifdef SHEDITOR - - // Broadcast event for the Debug Draw system to catch - const SHColliderOnDebugDrawEvent EVENT_DATA - { - .entityID = entityID - , .debugDrawState = state - }; - - SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_DRAW_EVENT); - - #endif - } - - void SHCollider::SetRigidBody(SHRigidBody* rb) noexcept - { - rigidBody = rb; - } - - void SHCollider::SetTransform(const SHTransform& newTransform) noexcept - { - flags |= MOVED_FLAG; - transform = newTransform; - } - - void SHCollider::SetPosition(const SHVec3& newPosition) noexcept - { - flags |= MOVED_FLAG; - transform.position = newPosition; - } - - void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept - { - flags |= MOVED_FLAG; - transform.orientation = newOrientation; - } - - void SHCollider::SetScale(const SHVec3& newScale) noexcept - { - flags |= MOVED_FLAG; - transform.scale = newScale; - } - - void SHCollider::SetLibrary(SHCollisionShapeLibrary* factory) noexcept - { - shapeLibrary = factory; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHMatrix& SHCollider::ComputeTRS() noexcept - { - return transform.ComputeTRS(); - } - - void SHCollider::RemoveCollisionShape(int index) - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add remove shape!", entityID) - return; - } - - const int NUM_SHAPES = static_cast(shapes.size()); - - if (index < 0 || index >= NUM_SHAPES) - throw std::invalid_argument("Out-of-range index!"); - - auto shape = shapes.begin(); - for (int i = 0; i < NUM_SHAPES; ++i, ++shape) - { - if (i == index) - break; - } - - const SHPhysicsColliderRemovedEvent EVENT_DATA - { - .entityID = entityID - , .colliderType = (*shape)->GetType() - , .colliderIndex = index - }; - - // Remove from broadphase - if (broadphase) - broadphase->Remove((*shape)->id); - - shapeLibrary->DestroyShape(*shape); - *shape = nullptr; - - // Remove the shape from the container to prevent accessing a nullptr - shape = shapes.erase(shape); - - - - // Broadcast Event for removing a shape - SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT); - - if (rigidBody) - rigidBody->ComputeMassData(); - - SHLOG_INFO_D("Removing Collision Shape {} from Entity {}", index, entityID) - } - - void SHCollider::Update() noexcept - { - for (auto* shape : shapes) - shape->Update(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollider::copyShapes(const SHCollider& rhsCollider) - { - for (const auto* shape : rhsCollider.shapes) - { - switch (shape->GetType()) - { - case SHCollisionShape::Type::BOX: - { - const SHBox* RHS_BOX = dynamic_cast(shape); - - const SHBoxCreateInfo BOX_CREATE_INFO - { - .Center = RHS_BOX->Center - , .Extents = RHS_BOX->Extents - , .RelativeExtents = RHS_BOX->relativeExtents - , .Orientation = RHS_BOX->Orientation - , .Scale = RHS_BOX->scale - }; - - const uint32_t NEW_INDEX = static_cast(shapes.size()); - const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - - SHBox* box = shapeLibrary->CreateBox(NEW_SHAPE_ID, BOX_CREATE_INFO); - *box = *RHS_BOX; - - shapes.emplace_back(box); - - break; - } - case SHCollisionShape::Type::SPHERE: - { - const SHSphere* RHS_SPHERE = dynamic_cast(shape); - - const SHSphereCreateInfo SPHERE_CREATE_INFO - { - .Center = RHS_SPHERE->Center - , .Radius = RHS_SPHERE->Radius - , .RelativeRadius = RHS_SPHERE->relativeRadius - , .Scale = RHS_SPHERE->scale - }; - - const uint32_t NEW_INDEX = static_cast(shapes.size()); - const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - - SHSphere* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); - *sphere = *RHS_SPHERE; - - shapes.emplace_back(sphere); - - break; - } - case SHCollisionShape::Type::CAPSULE: - { - break; - } - default: break; - } - } - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollider.h b/SHADE_Engine/src/Physics/Collision/SHCollider.h deleted file mode 100644 index a326215f..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCollider.h +++ /dev/null @@ -1,178 +0,0 @@ -/**************************************************************************************** - * \file SHCollider.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Base Collider Class. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "ECS_Base/Entity/SHEntity.h" -#include "Math/Transform/SHTransform.h" -#include "Physics/Collision/Shapes/SHCollisionShapeLibrary.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - class SHRigidBody; - class SHAABBTree; - - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Base class for a collider. - * There are only two collider types supported by SHADE Engine: Composite & Hull - */ - class SH_API SHCollider - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHCollisionSpace; - friend struct SHManifold; - - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - enum class Type - { - COMPOSITE - , HULL - - , TOTAL - , INVALID = -1 - }; - - using CollisionShapes = std::vector; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Constructor for a collider. - * @param eid - * The entity this collider belongs to. - * @param worldTransform - * The world transform for the collider. Defaults to the identity transform. - * This is particularly important for composite colliders for offsets & relative sizes. - * @return - */ - SHCollider (EntityID eid, const SHTransform& worldTransform = SHTransform::Identity) noexcept; - SHCollider (const SHCollider& rhs) noexcept; - SHCollider (SHCollider&& rhs) noexcept; - virtual ~SHCollider () noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHCollider& operator=(const SHCollider& rhs) noexcept; - SHCollider& operator=(SHCollider&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] EntityID GetEntityID () const noexcept; - - [[nodiscard]] Type GetType () const noexcept; - [[nodiscard]] bool IsActive () const noexcept; - [[nodiscard]] bool GetDebugDrawState () const noexcept; - - [[nodiscard]] const SHTransform& GetTransform () const noexcept; - [[nodiscard]] const SHVec3& GetPosition () const noexcept; - [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; - [[nodiscard]] const SHVec3& GetScale () const noexcept; - - [[nodiscard]] const CollisionShapes& GetCollisionShapes () const noexcept; - [[nodiscard]] SHCollisionShape* GetCollisionShape (int index) const; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetType (Type type) noexcept; - void SetIsActive (bool state) noexcept; - void SetDebugDrawState (bool state) noexcept; - - void SetRigidBody (SHRigidBody* rb) noexcept; - - void SetTransform (const SHTransform& newTransform) noexcept; - void SetPosition (const SHVec3& newPosition) noexcept; - void SetOrientation (const SHQuaternion& newOrientation) noexcept; - void SetScale (const SHVec3& newScale) noexcept; - - void SetLibrary (SHCollisionShapeLibrary* factory) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Computes the TRS for the collider's transform - * @return - * The computed TRS. - */ - const SHMatrix& ComputeTRS() noexcept; - - /** - * @brief - * Removes a shape from the container. Removal reduces the size of the container. - * If removing all, perform removal from back to front. - * @param index - * The index of the shape to remove. - * @throws - * Invalid argument for out-of-range indices. - */ - void RemoveCollisionShape (int index); - - /** - * @brief - * Recomputes the transforms for all shapes in this composite collider. - */ - void Update () noexcept; - - protected: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr uint8_t COMPOSITE_FLAG = 1U << static_cast(Type::COMPOSITE); - static constexpr uint8_t HULL_FLAG = 1U << static_cast(Type::HULL); - static constexpr uint8_t ACTIVE_FLAG = 1U << 2; - static constexpr uint8_t DRAW_FLAG = 1U << 3; - static constexpr uint8_t MOVED_FLAG = 1U << 4; - - EntityID entityID; - uint8_t flags; // 0 0 0 hasMoved debugDraw active hull composite - SHRigidBody* rigidBody; - SHCollisionShapeLibrary* shapeLibrary; - SHAABBTree* broadphase; - SHTransform transform; - CollisionShapes shapes; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - void copyShapes (const SHCollider& rhsCollider); - }; - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp new file mode 100644 index 00000000..43ad05ca --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.cpp @@ -0,0 +1,93 @@ +/**************************************************************************************** + * \file SHCollisionInfo.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for Collision Info. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollisionInfo.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionInfo::SHCollisionInfo() noexcept + : collisionState { State::INVALID } + { + ids[ENTITY_A] = MAX_EID; + ids[ENTITY_B] = MAX_EID; + ids[COLLIDER_A] = std::numeric_limits::max(); + ids[COLLIDER_B] = std::numeric_limits::max(); + } + + SHCollisionInfo::SHCollisionInfo(EntityID entityA, EntityID entityB) noexcept + : collisionState { State::INVALID } + { + ids[ENTITY_A] = entityA; + ids[ENTITY_B] = entityB; + ids[COLLIDER_A] = std::numeric_limits::max(); + ids[COLLIDER_B] = std::numeric_limits::max(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollisionInfo::operator==(const SHCollisionInfo& rhs) const noexcept + { + return value[0] == rhs.value[0] && value[1] == rhs.value[1]; + } + + bool SHCollisionInfo::operator!=(const SHCollisionInfo& rhs) const noexcept + { + return value[0] != rhs.value[0] || value[1] != rhs.value[1]; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + EntityID SHCollisionInfo::GetEntityA() const noexcept + { + return ids[ENTITY_A]; + } + + EntityID SHCollisionInfo::GetEntityB() const noexcept + { + return ids[ENTITY_B]; + } + + const SHRigidBodyComponent* SHCollisionInfo::GetRigidBodyA() const noexcept + { + return SHComponentManager::GetComponent_s(ids[ENTITY_A]); + } + + const SHRigidBodyComponent* SHCollisionInfo::GetRigidBodyB() const noexcept + { + return SHComponentManager::GetComponent_s(ids[ENTITY_B]); + } + + const SHCollisionShape* SHCollisionInfo::GetColliderA() const noexcept + { + return &SHComponentManager::GetComponent(ids[ENTITY_A])->GetCollisionShape(ids[COLLIDER_A]); + } + + const SHCollisionShape* SHCollisionInfo::GetColliderB() const noexcept + { + return &SHComponentManager::GetComponent(ids[ENTITY_B])->GetCollisionShape(ids[COLLIDER_B]); + } + + SHCollisionInfo::State SHCollisionInfo::GetCollisionState() const noexcept + { + return collisionState; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.h b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h similarity index 55% rename from SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.h rename to SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h index 09f712d1..d2dad647 100644 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionInfo.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCollisionShapeKey.h + * \file SHCollisionInfo.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collison Shape ID Class and it's hashing function + * \brief Interface for Collision Information for Collision & Triggers. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -11,95 +11,92 @@ #pragma once // Project Headers -#include "ECS_Base/Entity/SHEntity.h" +#include "Physics/Interface/SHColliderComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" + namespace SHADE { - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - struct SHCollisionShapeIDHash; - /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates an identifier for a collision shape. - */ - union SHCollisionShapeID + class SH_API SHCollisionInfo { private: /*---------------------------------------------------------------------------------*/ /* Friends */ /*---------------------------------------------------------------------------------*/ - friend struct SHCollisionShapeIDHash; + friend class SHCollisionListener; public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + enum class State + { + ENTER + , STAY + , EXIT + + , TOTAL + , INVALID = -1 + }; + /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHCollisionShapeID (EntityID eid, uint32_t shapeID) noexcept; - SHCollisionShapeID (const SHCollisionShapeID& rhs) noexcept; - SHCollisionShapeID (SHCollisionShapeID&& rhs) noexcept; + SHCollisionInfo () noexcept; + SHCollisionInfo (EntityID entityA, EntityID entityB) noexcept; - ~SHCollisionShapeID () noexcept = default; + + SHCollisionInfo (const SHCollisionInfo& rhs) = default; + SHCollisionInfo (SHCollisionInfo&& rhs) = default; + ~SHCollisionInfo () = default; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHCollisionShapeID& operator=(const SHCollisionShapeID& rhs) noexcept; - SHCollisionShapeID& operator=(SHCollisionShapeID&& rhs) noexcept; + bool operator== (const SHCollisionInfo& rhs) const noexcept; + bool operator!= (const SHCollisionInfo& rhs) const noexcept; - bool operator==(const SHCollisionShapeID& rhs) const; + SHCollisionInfo& operator= (const SHCollisionInfo& rhs) = default; + SHCollisionInfo& operator= (SHCollisionInfo&& rhs) = default; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] EntityID GetEntityID () const noexcept; - [[nodiscard]] uint32_t GetShapeIndex () const noexcept; + [[nodiscard]] EntityID GetEntityA () const noexcept; + [[nodiscard]] EntityID GetEntityB () const noexcept; + [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyA () const noexcept; + [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyB () const noexcept; + [[nodiscard]] const SHCollisionShape* GetColliderA () const noexcept; + [[nodiscard]] const SHCollisionShape* GetColliderB () const noexcept; + [[nodiscard]] State GetCollisionState () const noexcept; private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - struct IDs - { - public: - EntityID entityID = MAX_EID; - uint32_t shapeIndex = std::numeric_limits::max(); - }; + static constexpr uint32_t ENTITY_A = 0; + static constexpr uint32_t ENTITY_B = 1; + static constexpr uint32_t COLLIDER_A = 2; + static constexpr uint32_t COLLIDER_B = 3; /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - uint64_t value; - IDs ids; + union + { + uint64_t value[2]; // EntityValue, ColliderIndexValue + uint32_t ids [4]; // EntityA, EntityB, ColliderIndexA, ColliderIndexB + }; + + State collisionState; }; - /** - * @brief - * Encapsulates a functor to hash a CollisionShapeID - */ - struct SHCollisionShapeIDHash - { - public: - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - std::size_t operator()(const SHCollisionShapeID& id) const; - }; - -} // namespace SHADE - -#include "SHCollisionShapeID.hpp" +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionListener.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionListener.cpp new file mode 100644 index 00000000..3e485153 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionListener.cpp @@ -0,0 +1,254 @@ +/**************************************************************************************** + * \file SHCollisionListener.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Collision Listener. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollisionListener.h" + +// Project Headers +#include "ECS_Base/Managers/SHEntityManager.h" +#include "Physics/PhysicsObject/SHPhysicsObject.h" +#include "Physics/System/SHPhysicsSystem.h" +#include "Scene/SHSceneManager.h" + +/*-------------------------------------------------------------------------------------*/ +/* Local Helper Functions */ +/*-------------------------------------------------------------------------------------*/ + +uint32_t matchColliders(const SHADE::SHPhysicsObject&physicsObject, const rp3d::Entity colliderID) +{ + for (uint32_t i = 0; i < physicsObject.GetCollisionBody()->getNbColliders(); ++i) + { + const auto* collider = physicsObject.GetCollisionBody()->getCollider(i); + if (collider->getEntity() == colliderID) + return i; + } + + return std::numeric_limits::max(); +} + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionListener::SHCollisionListener() noexcept + : system { nullptr } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const std::vector& SHCollisionListener::GetCollisionInfoContainer() const noexcept + { + return collisionInfoContainer; + } + + const std::vector& SHCollisionListener::GetTriggerInfoContainer() const noexcept + { + return triggerInfoContainer; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionListener::BindToSystem(SHPhysicsSystem* physicsSystem) noexcept + { + system = physicsSystem; + } + + void SHCollisionListener::BindToWorld(rp3d::PhysicsWorld* world) noexcept + { + if (!world) + return; + + world->setEventListener(this); + } + + void SHCollisionListener::CleanContainers() noexcept + { + static const auto CLEAR = [](std::vector& container) + { + for (auto eventIter = container.begin(); eventIter != container.end();) + { + const SHCollisionInfo& C_INFO = *eventIter; + + const bool INVALID_ENTITY = !SHEntityManager::IsValidEID(C_INFO.GetEntityA()) || !SHEntityManager::IsValidEID(C_INFO.GetEntityB()); + if (INVALID_ENTITY) + { + eventIter = container.erase(eventIter); + continue; + } + else + { + const bool CLEAR_EVENT = C_INFO.GetCollisionState() == SHCollisionInfo::State::EXIT || C_INFO.GetCollisionState() == SHCollisionInfo::State::INVALID; + + const bool INACTIVE_OBJECT = !SHSceneManager::CheckNodeAndComponentsActive(C_INFO.GetEntityA()) + || !SHSceneManager::CheckNodeAndComponentsActive(C_INFO.GetEntityB()); + + if (CLEAR_EVENT || INACTIVE_OBJECT) + { + eventIter = container.erase(eventIter); + continue; + } + } + + ++eventIter; + } + }; + + CLEAR(collisionInfoContainer); + CLEAR(triggerInfoContainer); + } + + void SHCollisionListener::ClearContainers() noexcept + { + collisionInfoContainer.clear(); + triggerInfoContainer.clear(); + } + + void SHCollisionListener::onContact(const rp3d::CollisionCallback::CallbackData& callbackData) + { + for (uint32_t i = 0; i < callbackData.getNbContactPairs(); ++i) + { + const auto CONTACT_PAIR = callbackData.getContactPair(i); + const SHCollisionInfo NEW_INFO = generateCollisionInfo(CONTACT_PAIR); + + updateInfoContainers(NEW_INFO, collisionInfoContainer); + } + } + + void SHCollisionListener::onTrigger(const rp3d::OverlapCallback::CallbackData& callbackData) + { + for (uint32_t i = 0; i < callbackData.getNbOverlappingPairs(); ++i) + { + const auto OVERLAP_PAIR = callbackData.getOverlappingPair(i); + const SHCollisionInfo NEW_INFO = generateTriggerInfo(OVERLAP_PAIR); + + updateInfoContainers(NEW_INFO, triggerInfoContainer); + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionListener::updateInfoContainers(const SHCollisionInfo& collisionEvent, std::vector& container) noexcept + { + const auto IT = std::ranges::find_if(container.begin(), container.end(), [&](const SHCollisionInfo& info) + { + const bool ENTITY_MATCH = (info.ids[0] == collisionEvent.ids[0] && info.ids[1] == collisionEvent.ids[1]) + || (info.ids[0] == collisionEvent.ids[1] && info.ids[1] == collisionEvent.ids[0]); + const bool COLLIDERS_MATCH = (info.ids[2] == collisionEvent.ids[2] && info.ids[3] == collisionEvent.ids[3]) + || (info.ids[2] == collisionEvent.ids[3] && info.ids[3] == collisionEvent.ids[2]); + return ENTITY_MATCH && COLLIDERS_MATCH; + }); + + if (IT == container.end()) + container.emplace_back(collisionEvent); + else + IT->collisionState = collisionEvent.collisionState; + } + + SHCollisionInfo SHCollisionListener::generateCollisionInfo(const rp3d::CollisionCallback::ContactPair& cp) const noexcept + { + SHCollisionInfo cInfo; + + // Update collision state + cInfo.collisionState = static_cast(cp.getEventType()); + + // Match body and collider for collision event + const rp3d::Entity body1 = cp.getBody1()->getEntity(); + const rp3d::Entity body2 = cp.getBody2()->getEntity(); + const rp3d::Entity collider1 = cp.getCollider1()->getEntity(); + const rp3d::Entity collider2 = cp.getCollider2()->getEntity(); + + // Find and match both ids + bool matched[2] = { false, false }; + + + for (auto& [entityID, physicsObject] : system->GetPhysicsObjects()) + { + // Match body 1 + if (matched[SHCollisionInfo::ENTITY_A] == false && physicsObject.GetCollisionBody()->getEntity() == body1) + { + cInfo.ids[SHCollisionInfo::ENTITY_A] = entityID; + cInfo.ids[SHCollisionInfo::COLLIDER_A] = matchColliders(physicsObject, collider1); + + matched[SHCollisionInfo::ENTITY_A] = true; + } + + // Match body 2 + if (matched[SHCollisionInfo::ENTITY_B] == false && physicsObject.GetCollisionBody()->getEntity() == body2) + { + cInfo.ids[SHCollisionInfo::ENTITY_B] = entityID; + cInfo.ids[SHCollisionInfo::COLLIDER_B] = matchColliders(physicsObject, collider2); + + matched[SHCollisionInfo::ENTITY_B] = true; + } + + if (matched[SHCollisionInfo::ENTITY_A] == true && matched[SHCollisionInfo::ENTITY_B] == true) + return cInfo; + } + + return cInfo; + } + + SHCollisionInfo SHCollisionListener::generateTriggerInfo(const rp3d::OverlapCallback::OverlapPair& cp) const noexcept + { + SHCollisionInfo cInfo; + + // Update collision state + cInfo.collisionState = static_cast(cp.getEventType()); + + // Match body and collider for collision event + const rp3d::Entity body1 = cp.getBody1()->getEntity(); + const rp3d::Entity body2 = cp.getBody2()->getEntity(); + const rp3d::Entity collider1 = cp.getCollider1()->getEntity(); + const rp3d::Entity collider2 = cp.getCollider2()->getEntity(); + + // Find and match both ids + bool matched[2] = { false, false }; + + + for (auto& [entityID, physicsObject] : system->GetPhysicsObjects()) + { + // Match body 1 + if (matched[SHCollisionInfo::ENTITY_A] == false && physicsObject.GetCollisionBody()->getEntity() == body1) + { + cInfo.ids[SHCollisionInfo::ENTITY_A] = entityID; + cInfo.ids[SHCollisionInfo::COLLIDER_A] = matchColliders(physicsObject, collider1); + + matched[SHCollisionInfo::ENTITY_A] = true; + } + + // Match body 2 + if (matched[SHCollisionInfo::ENTITY_B] == false && physicsObject.GetCollisionBody()->getEntity() == body2) + { + cInfo.ids[SHCollisionInfo::ENTITY_B] = entityID; + cInfo.ids[SHCollisionInfo::COLLIDER_B] = matchColliders(physicsObject, collider2); + + matched[SHCollisionInfo::ENTITY_B] = true; + } + + if (matched[SHCollisionInfo::ENTITY_A] == true && matched[SHCollisionInfo::ENTITY_B] == true) + return cInfo; + } + + return cInfo; + } + + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.h b/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h similarity index 50% rename from SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.h rename to SHADE_Engine/src/Physics/Collision/SHCollisionListener.h index 462b8dd8..62882556 100644 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCollisionShapeLibrary.h + * \file SHCollisionListener.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collison Shape Library. + * \brief Interface for a Collision Listener. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -10,99 +10,71 @@ #pragma once -#include +// External Dependencies +#include -// Project Header -#include "SHSphere.h" -#include "SHBox.h" +// Project Headers +#include "SH_API.h" +#include "SHCollisionInfo.h" namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + class SHPhysicsSystem; + /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates a class for Creating, Storing and Destroying Collision Shapes.
- * All memory for collision shapes are stored in this factory class.
- */ - class SH_API SHCollisionShapeLibrary final + class SH_API SHCollisionListener final : public rp3d::EventListener { public: /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHCollisionShapeLibrary () noexcept; - ~SHCollisionShapeLibrary () noexcept; + SHCollisionListener() noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] const std::vector& GetCollisionInfoContainer () const noexcept; + [[nodiscard]] const std::vector& GetTriggerInfoContainer () const noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Creates a sphere collision shape. - * @param id - * The ID of the shape. - * @param createInfo - * The info to create the sphere with. - * @return - * A new sphere collision shape. - */ - SHSphere* CreateSphere (SHCollisionShapeID id, const SHSphereCreateInfo& createInfo); + void BindToSystem (SHPhysicsSystem* physicsSystem) noexcept; + void BindToWorld (rp3d::PhysicsWorld* world) noexcept; + void CleanContainers () noexcept; + void ClearContainers () noexcept; - /** - * @brief - * Creates a box collision shape. - * @param id - * The ID of the shape. - * @param createInfo - * The info to create the box with. - * @return - * A new box collision shape. - */ - SHBox* CreateBox (SHCollisionShapeID id, const SHBoxCreateInfo& createInfo); - - /** - * @brief - * Destroys a collision shape. - * * @param eid - * The entity the shape belongs to. - * @param shapeID - * The ID of the shape. - * @param shape - * The shape to destroy. - */ - void DestroyShape (SHCollisionShape* shape); + void onContact (const rp3d::CollisionCallback::CallbackData& callbackData) override; + void onTrigger (const rp3d::OverlapCallback::CallbackData& callbackData) override; private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - // We use unordered maps for fast lookup when deleting. - // Since we are not instancing shapes (yet?), I'd rather not iterate through an entire vector to find the shape. - - using Spheres = std::unordered_map; - using Boxes = std::unordered_map; /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - - SHHalfEdgeStructure boxHalfEdgeStructure; - Spheres spheres; - Boxes boxes; - // TODO: Add capsules and hulls + SHPhysicsSystem* system; + std::vector collisionInfoContainer; + std::vector triggerInfoContainer; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ - void createBoxPolyhedron() noexcept; + static void updateInfoContainers (const SHCollisionInfo& collisionEvent, std::vector& container) noexcept; + + SHCollisionInfo generateCollisionInfo (const rp3d::CollisionCallback::ContactPair& cp) const noexcept; + SHCollisionInfo generateTriggerInfo (const rp3d::OverlapCallback::OverlapPair& cp) const noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp deleted file mode 100644 index c643e05e..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionSpace.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Collision Space that handles collision detetction. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - - -#include - -// Primary Header -#include "SHCollisionSpace.h" - -#include "Narrowphase/SHCollisionDispatch.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHAABBTree::AABBs& SHCollisionSpace::GetBroadphaseAABBs() const noexcept - { - return broadphase.GetAABBs(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionSpace::SetContactManager(SHContactManager* _contactManager) noexcept - { - contactManager = _contactManager; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionSpace::AddCollider(SHCollider* collider) noexcept - { - const bool INSERTED = colliders.emplace(collider->entityID, collider).second; - if (!INSERTED) - { - SHLOG_WARNING_D("Attempting to add duplicate collider {} to the Physics World!", collider->entityID) - return; - } - - collider->broadphase = &broadphase; - - // Add all existing shapes to the broadphase - for (const auto* shape : collider->shapes) - broadphase.Insert(shape->id, shape->ComputeAABB()); - } - - void SHCollisionSpace::RemoveCollider(SHCollider* collider) noexcept - { - colliders.erase(collider->entityID); - - const uint32_t NUM_SHAPES = static_cast(collider->shapes.size()); - if (NUM_SHAPES == 0) - return; - - for (uint32_t i = 0; i < NUM_SHAPES; ++i) - broadphase.Remove(collider->shapes[i]->id); - - if (contactManager) - { - contactManager->RemoveInvalidatedTrigger(collider->entityID); - contactManager->RemoveInvalidatedManifold(collider->entityID); - } - - /* - * TODO: - * Get collider's rigid body - * Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep - */ - } - - void SHCollisionSpace::UpdateBroadphase() noexcept - { - // Update any colliders that have moved - for (auto& collider : colliders | std::views::values) - { - const bool IS_ACTIVE = collider->IsActive(); - const bool HAS_MOVED = collider->flags & SHCollider::MOVED_FLAG; - - if (!IS_ACTIVE || !HAS_MOVED) - continue; - - // Clear hasMoved flag here - collider->flags &= ~SHCollider::MOVED_FLAG; - - // Update moved shapes in broadphase - for (auto* shape : collider->shapes) - broadphase.Update(shape->id, shape->ComputeAABB()); - } - } - - void SHCollisionSpace::DetectCollisions() noexcept - { - // TODO: Profile broad-phase and narrow-phase - - /* - * Broad-phase - */ - - // Broadphase Queries: Kinematic Triggers, Awake Dynamic Bodies & Dynamic Triggers - for (auto& collider : colliders | std::views::values) - { - // Colliders without bodies are considered to be static bodies - // This is specific to this engine because of Unity's stupid convention. - const bool IS_IMPLICIT_STATIC = !collider->rigidBody; - const bool IS_ACTIVE = collider->IsActive(); - - // Skip inactive colliders - if (!IS_ACTIVE || IS_IMPLICIT_STATIC) - continue; - - const bool IS_EXPLICIT_STATIC = collider->rigidBody->GetType() == SHRigidBody::Type::STATIC; - if (IS_EXPLICIT_STATIC) - continue; - - // All remaining are kinematic or dynamic - // Iterate through shapes: if kinematic / dynamic trigger, else if dynamic & awake - // Results are loaded into the narrowphase batch - broadphaseQuery(collider->rigidBody->GetType(), collider); - } - - /* - * Narrow-phase - */ - - // If no potential collisions, we can skip the entire narrow phase. No further updates necessary. - // All contact / trigger states persist in this step. - if (narrowphaseBatch.empty()) - return; - - // All narrowphase IDs are unique, there should be no duplicate collision checks. - // This applies both ways: A -> B and B -> A. - for (auto& [key, narrowphasePair] : narrowphaseBatch) - { - // Filter through tags before attempting narrow-phase - const uint16_t TAG_A = narrowphasePair.A->GetCollisionTag().GetMask(); - const uint16_t TAG_B = narrowphasePair.B->GetCollisionTag().GetMask(); - - const bool MATCH_TAG = TAG_A & TAG_B; - if (!MATCH_TAG) - continue; - - const bool IS_A_TRIGGER = narrowphasePair.A->IsTrigger(); - const bool IS_B_TRIGGER = narrowphasePair.B->IsTrigger(); - - if (IS_A_TRIGGER || IS_B_TRIGGER) - collideTriggers(key, narrowphasePair); - else - collideManifolds(key, narrowphasePair); - } - - // Clear every frame - narrowphaseBatch.clear(); - - // Test all collisions - if (contactManager) - contactManager->Update(); - } - - const SHCollisionSpace::RaycastResults& SHCollisionSpace::Raycast(const RaycastInfo& info) noexcept - { - static RaycastResults results; - results.clear(); - - const bool FILTER_COLLIDER = info.colliderEntityID.has_value(); - - // Cast ray into the broadphase scene - const auto& POTENTIAL_HITS = broadphase.Query(info.ray, info.distance); - - if (POTENTIAL_HITS.empty()) - return results; - - // Iterate through all potential hits - const int NUM_HITS = static_cast(POTENTIAL_HITS.size()); - for (const int i : std::ranges::views::iota(0, NUM_HITS)) - { - const auto HIT_ID = POTENTIAL_HITS[i]; - - const EntityID EID = HIT_ID.GetEntityID(); - - if (FILTER_COLLIDER && EID == info.colliderEntityID.value()) - continue; - - // Get shape - const uint32_t IDX = HIT_ID.GetShapeIndex(); - const auto* SHAPE = colliders.find(EID)->second->GetCollisionShape(IDX); - - // Filter the layers - const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & info.layers; - if (!LAYER_MATCH) - continue; - - // We cast to the underlying shape. THis is done because a convex hull will not have an inherited raycast method. - // Kinda awkward oversight...oops - - SHRaycastResult baseResult; - switch (SHAPE->GetType()) - { - case SHCollisionShape::Type::SPHERE: - { - baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); - break; - } - case SHCollisionShape::Type::BOX: - { - baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); - break; - } - case SHCollisionShape::Type::CAPSULE: - { - // TODO - break; - } - default: continue; // Redundant case - } - - if (!baseResult || baseResult.distance > info.distance) - continue; - - // Copy to a physics raycast result - SHPhysicsRaycastResult result; - memcpy_s(&result, sizeof(SHRaycastResult), &baseResult, sizeof(SHRaycastResult)); - - result.entityHit = EID; - result.shapeIndex = IDX; - - results.emplace_back(result); - } - - return results; - } - - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionSpace::broadphaseQuery(SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept - { - for (auto* shape : collider->shapes) - { - // For kinematic shapes, we only query triggers against everything else - if (rigidBodyType == SHRigidBody::Type::KINEMATIC && !shape->IsTrigger()) - continue; - - auto& potentialCollisions = broadphase.Query(shape->id, shape->ComputeAABB()); - - // Build narrow-phase pairs - auto* shapeA = shape; - - const EntityID ID_A = shape->id.GetEntityID(); - const uint32_t INDEX_A = shape->id.GetShapeIndex(); - - for (auto& id : potentialCollisions) - { - // Get corresponding shape - const EntityID ID_B = id.GetEntityID(); - const uint32_t INDEX_B = id.GetShapeIndex(); - - auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B); - - // Build collision ID - SHCollisionKey collisionKey; - collisionKey.SetEntityA(ID_A); - collisionKey.SetEntityB(ID_B); - collisionKey.SetCollisionShapeA(INDEX_A); - collisionKey.SetCollisionShapeB(INDEX_B); - - // Check if it already exists. If it doesn't, put into batch. - // The overloaded equality operator ensures no duplicate collision tests are performed. - auto narrowphasePair = narrowphaseBatch.find(collisionKey); - if (narrowphasePair == narrowphaseBatch.end()) - narrowphaseBatch.emplace(collisionKey, NarrowphasePair{ shapeA, shapeB }); - } - } - } - - void SHCollisionSpace::collideTriggers(const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept - { - auto* A = narrowphasePair.A; - auto* B = narrowphasePair.B; - - // Send to contact manager - if (contactManager) - contactManager->AddTrigger(key, A, B); - } - - void SHCollisionSpace::collideManifolds(const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept - { - auto* A = narrowphasePair.A; - auto* B = narrowphasePair.B; - - // Send to contact manager - if (contactManager) - contactManager->AddManifold(key, A, B); - } -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h deleted file mode 100644 index 8607cf45..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h +++ /dev/null @@ -1,196 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionSpace.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Collision Space that handles collision detetction. - * This is to separate the logic between dynamics and collision detection, - * but the collision space does send information to the contact manager - * for dynamic resolution and collision state reporting. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "Broadphase/SHDynamicAABBTree.h" -#include "Physics/Dynamics/SHContactManager.h" -#include "SHCollider.h" -#include "SHPhysicsRaycastResult.h" -#include "CollisionTags/SHCollisionTags.h" -#include "CollisionTags/SHCollisionTags.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Allows collision detection to be performed with the use of colliders & collision shapes. - * The space will generate manifold data for resolution when needed. - */ - class SH_API SHCollisionSpace - { - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Contains information to cast a ray into the collision space. - * The collider entityID and shape index is optional. - */ - struct RaycastInfo - { - private: - /*-------------------------------------------------------------------------------*/ - /* Friends */ - /*-------------------------------------------------------------------------------*/ - - friend class SHCollisionSpace; - - public: - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - bool continuous = false; - uint16_t layers = static_cast(SHCollisionTag::Layer::ALL); - float distance = std::numeric_limits::infinity(); - SHRay ray; - - /*-------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*-------------------------------------------------------------------------------*/ - - /** - * @brief - * Sets the collider ID for the raycast. Setting this specifies that the ray - * should ignore this collider. - * @param eid - * The entity ID of the collider. - */ - void SetColliderID(EntityID eid) noexcept { colliderEntityID = eid; } - - private: - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - std::optional colliderEntityID; - }; - - using RaycastResults = std::vector; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHCollisionSpace () noexcept = default; - ~SHCollisionSpace () noexcept = default; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - const SHAABBTree::AABBs& GetBroadphaseAABBs () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetContactManager(SHContactManager* contactManager) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Adds a collider to the collision space for it to be tested for collision with - * other colliders. - * @param collider - * A collider to add. Duplicates will be ignored. - */ - void AddCollider (SHCollider* collider) noexcept; - - /** - * @brief - * Removes a collider from the collision space. This will prevent any collisions - * being detected between it and other colliders unless manually tested. - * @param collider - * A collider to remove. If a reference to it doesn't exist, it will be ignored. - */ - void RemoveCollider (SHCollider* collider) noexcept; - - /** - * @brief - * Invoke this method to update the broadphase of colliders that have been moved since - * the last frame. - */ - void UpdateBroadphase () noexcept; - - /** - * @brief - * Detects collisions between all colliders. Results are sent to the attached contact - * manager for resolution. - */ - void DetectCollisions () noexcept; - - /** - * @brief - * Casts a ray into the collision space. - * @param info - * Contains the information for the raycast. - * @return - * A container of the objects hit by the ray. If nothing was hit, the container - * will be empty. - */ - [[nodiscard]] const RaycastResults& Raycast(const RaycastInfo& info) noexcept; - - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - struct NarrowphasePair - { - SHCollisionShape* A = nullptr; - SHCollisionShape* B = nullptr; - }; - - using Colliders = std::unordered_map; - using NarrowphaseBatch = std::unordered_map; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - SHContactManager* contactManager = nullptr; - - Colliders colliders; - NarrowphaseBatch narrowphaseBatch; - - SHAABBTree broadphase; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - // Broadphase helpers - - void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; - - // Narrowphase helpers - - void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; - void collideManifolds (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; - }; - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.cpp similarity index 86% rename from SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp rename to SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.cpp index 4d3980b0..b687c6ca 100644 --- a/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.cpp @@ -14,9 +14,6 @@ // Primary Header #include "SHCollisionTagMatrix.h" -// Project Headers -#include "Tools/Utilities/SHUtilities.h" - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -148,9 +145,8 @@ namespace SHADE /** * I HATE FILE IO * - * Each line in the file should be "indextag namemask". - * If the line fails to follow this format, use the default tag name (index + 1) and default mask. - * If no mask was read, use a default mask. + * Each line in the file should be "indextag name". + * If the line fails to follow this format, use the default tag name (index + 1) */ // Populate tag names with default @@ -191,40 +187,18 @@ namespace SHADE { SHLOG_ERROR ( - "Collision tag file line {} does not match the required format of 'indextag namemask'. Default tag used for index {}" + "Collision tag file line {} does not match the required format of 'indextag name'. Default tag used for index {}" , linesRead + 1 , tagIndex ) // Use default collisionTags[tagIndex].SetName(std::to_string(tagIndex + 1)); - collisionTags[tagIndex].SetMask(SHUtilities::ConvertEnum(SHCollisionTag::Layer::ALL)); continue; } collisionTags[tagIndex].SetName(tagName); - // Next element is the mask value - std::string maskString; - ss >> maskString; - - uint16_t mask = std::numeric_limits::max(); - if (maskString.empty()) - { - SHLOG_ERROR - ( - "Collision tag file line {} does not match the required format of 'indextag namemask'. Default mask used for index {}" - , linesRead + 1 - , tagIndex - ) - } - else - { - mask = static_cast(std::stoi(maskString)); - } - - collisionTags[tagIndex].SetMask(mask); - ss.clear(); } @@ -241,9 +215,8 @@ namespace SHADE return; } - // Index Name Mask for (int i = 0; i < SHCollisionTag::NUM_LAYERS; ++i) - collisionTagNamesFile << i << " " << collisionTags[i].GetName() << " " << collisionTags[i].GetMask() << std::endl; + collisionTagNamesFile << i << " " << collisionTags[i].GetName() << std::endl; collisionTagNamesFile.close(); } diff --git a/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.h b/SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.h similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTagMatrix.h rename to SHADE_Engine/src/Physics/Collision/SHCollisionTagMatrix.h diff --git a/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTags.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionTags.cpp similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTags.cpp rename to SHADE_Engine/src/Physics/Collision/SHCollisionTags.cpp diff --git a/SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTags.h b/SHADE_Engine/src/Physics/Collision/SHCollisionTags.h similarity index 100% rename from SHADE_Engine/src/Physics/Collision/CollisionTags/SHCollisionTags.h rename to SHADE_Engine/src/Physics/Collision/SHCollisionTags.h diff --git a/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp deleted file mode 100644 index 28b6aafe..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/**************************************************************************************** - * \file SHCompositeCollider.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Composite Collider Class. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCompositeCollider.h" - -// Project Headers -#include "Broadphase/SHDynamicAABBTree.h" -#include "Math/SHMathHelpers.h" -#include "Physics/SHPhysicsEvents.h" -#include "Physics/Dynamics/SHRigidBody.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCompositeCollider::SHCompositeCollider(EntityID eid, const SHTransform& worldTransform) noexcept - : SHCollider( eid, worldTransform ) - { - flags |= COMPOSITE_FLAG; - } - - SHCompositeCollider::SHCompositeCollider(const SHCompositeCollider& rhs) noexcept - : SHCollider( rhs ) - {} - - SHCompositeCollider::SHCompositeCollider(SHCompositeCollider&& rhs) noexcept - : SHCollider( rhs ) - {} - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCompositeCollider& SHCompositeCollider::operator=(const SHCompositeCollider& rhs) noexcept - { - if (this == &rhs) - return *this; - - SHCollider::operator=(rhs); - - return *this; - } - - SHCompositeCollider& SHCompositeCollider::operator=(SHCompositeCollider&& rhs) noexcept - { - SHCollider::operator=(rhs); - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - int SHCompositeCollider::AddSphereCollisionShape(float relativeRadius, const SHVec3& posOffset, const SHVec3& rotOffset) - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add new shape!", entityID) - return -1; - } - - // Compute world radius - const float SPHERE_SCALE = std::fabs(SHMath::Max({ transform.scale.x, transform.scale.y, transform.scale.z })); - - // Compute center - const SHQuaternion FINAL_ROT = transform.orientation * SHQuaternion::FromEuler(rotOffset); - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(transform.position); - - // Create Sphere - const SHSphereCreateInfo SPHERE_CREATE_INFO - { - .Center = SHVec3::Transform(posOffset, TRS) - , .Radius = relativeRadius * SPHERE_SCALE * 0.5f - , .RelativeRadius = relativeRadius - , .Scale = SPHERE_SCALE - }; - - const uint32_t NEW_INDEX = static_cast(shapes.size()); - const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - - SHSphere* sphere = shapeLibrary->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO); - - // Set offsets - sphere->collider = this; - sphere->SetPositionOffset(posOffset); - sphere->SetRotationOffset(rotOffset); - - shapes.emplace_back(sphere); - - if (broadphase) - broadphase->Insert(NEW_SHAPE_ID, sphere->ComputeAABB()); - - // Broadcast Event for adding a shape - const SHPhysicsColliderAddedEvent EVENT_DATA - { - .entityID = entityID - , .colliderType = SHCollisionShape::Type::SPHERE - , .colliderIndex = static_cast(NEW_INDEX) - }; - - SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT); - - if (rigidBody) - rigidBody->ComputeMassData(); - - return static_cast(NEW_INDEX); - } - - int SHCompositeCollider::AddBoxCollisionShape(const SHVec3& relativeExtents, const SHVec3& posOffset, const SHVec3& rotOffset) - { - if (!shapeLibrary) - { - SHLOGV_ERROR("Shape factory is unlinked with Collider {}. Unable to add new shape!", entityID) - return -1; - } - - // Compute center - const SHQuaternion FINAL_ROT = transform.orientation * SHQuaternion::FromEuler(rotOffset); - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(transform.position); - - // Create Sphere - const SHBoxCreateInfo BOX_CREATE_INFO - { - .Center = SHVec3::Transform(posOffset, TRS) - , .Extents = relativeExtents * SHVec3::Abs(transform.scale) * 0.5f - , .RelativeExtents = relativeExtents - , .Orientation = FINAL_ROT - , .Scale = SHVec3::Abs(transform.scale) - }; - - const uint32_t NEW_INDEX = static_cast(shapes.size()); - const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX }; - - SHBox* box = shapeLibrary->CreateBox(NEW_SHAPE_ID, BOX_CREATE_INFO); - - // Set offsets - box->collider = this; - box->SetPositionOffset(posOffset); - box->SetRotationOffset(rotOffset); - - shapes.emplace_back(box); - - if (broadphase) - broadphase->Insert(NEW_SHAPE_ID, box->ComputeAABB()); - - // Broadcast Event for adding a shape - const SHPhysicsColliderAddedEvent EVENT_DATA - { - .entityID = entityID - , .colliderType = SHCollisionShape::Type::BOX - , .colliderIndex = static_cast(NEW_INDEX) - }; - - SHEventManager::BroadcastEvent(EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT); - - if (rigidBody) - rigidBody->ComputeMassData(); - - return static_cast(NEW_INDEX); - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h b/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h deleted file mode 100644 index 8ab71186..00000000 --- a/SHADE_Engine/src/Physics/Collision/SHCompositeCollider.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************************** - * \file SHCompositeCollider.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Composite Collider Class. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "SHCollider.h" - -namespace SHADE -{ - /** - * @brief - * Encapsulates the behaviour of a collider with composited shapes.
- * Contains no data members but methods to add multiple shapes. - */ - class SH_API SHCompositeCollider : public SHCollider - { - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHCompositeCollider(EntityID eid, const SHTransform& worldTransform = SHTransform::Identity) noexcept; - SHCompositeCollider(const SHCompositeCollider& rhs) noexcept; - SHCompositeCollider(SHCompositeCollider&& rhs) noexcept; - ~SHCompositeCollider () noexcept override = default; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHCompositeCollider& operator=(const SHCompositeCollider& rhs) noexcept; - SHCompositeCollider& operator=(SHCompositeCollider&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Adds a sphere collision shape. - * @param relativeRadius - * The relative radius is constructed with respect to the world scale.
- * Radius = max(scale.x, scale.y, scale.z) * 0.5 * relativeRadius - * @param posOffset - * The position offset of the sphere from the center of the collider. Defaults to a Zero Vector. - * @param rotOffset - * The rotation offset of the sphere from the rotation of the collider. Defaults to a Zero Vector. - * @return - * The index of the newly added shape. - */ - int AddSphereCollisionShape (float relativeRadius, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero); - - /** - * @brief - * Adds a box collision shape. - * @param relativeExtents - * The relative extents are constructed with respect to the world scale.
- * Extents = scale * 0.5 * relativeExtents - * @param posOffset - * The position offset of the box from the center of the collider. Defaults to a Zero Vector. - * @param rotOffset - * The rotation offset of the box from the rotation of the collider. Defaults to a Zero Vector. - * @return - * The index of the newly added shape. - */ - int AddBoxCollisionShape (const SHVec3& relativeExtents, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero); - - // TODO: Add Capsule - }; - - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp new file mode 100644 index 00000000..cab5c93b --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp @@ -0,0 +1,350 @@ +/**************************************************************************************** + * \file SHPhysicsRaycaster.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Physics Raycaster. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsRaycaster.h" + +/* + * TODO(DIREN): + * Once the physics engine has been rebuilt, this whole implementation should change + * and just call PhysicsWorld.Raycast etc. + * + * SHRaycastResult can be converted to a bool when necessary. + */ + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsRaycaster::SHPhysicsRaycaster() noexcept + : world { nullptr } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHPhysicsRaycaster::RaycastPairs& SHPhysicsRaycaster::GetRaycasts() const noexcept + { + return raycasts; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsRaycaster::SetObjectManager(SHPhysicsObjectManager* physicsObjectManager) noexcept + { + objectManager = physicsObjectManager; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsRaycaster::BindToWorld(rp3d::PhysicsWorld* physicsWorld) noexcept + { + world = physicsWorld; + } + + void SHPhysicsRaycaster::ClearFrame() noexcept + { + raycasts.clear(); + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::Raycast(const SHRay& ray, float distance, const SHCollisionTag& collisionTag) noexcept + { + // Reset temp + temp = SHPhysicsRaycastResult{}; + temp.distance = distance; + + if (!world) + { + SHLOG_ERROR("Physics world missing for raycasting!") + return temp; + } + + // If distance in infinity, cast to the default max distance of 2 km. + if (distance == std::numeric_limits::infinity()) + { + world->raycast(ray, this, collisionTag); + } + else + { + const SHVec3 END_POINT = ray.position + ray.direction * distance; + const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; + world->raycast(RP3D_RAY, this, collisionTag); + } + + // If a hit was found, populate temp info for return. + if (temp.hit) + { + temp.distance = SHVec3::Distance(ray.position, temp.position); + temp.angle = SHVec3::Angle(ray.position, temp.position); + } + + raycasts.emplace_back(ray, temp); + return temp; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::Linecast(const SHVec3& start, const SHVec3& end, const SHCollisionTag& collisionTag) noexcept + { + temp = SHPhysicsRaycastResult{}; + temp.distance = SHVec3::Distance(start, end); + + if (!world) + { + SHLOG_ERROR("Physics world missing for raycasting!") + return temp; + } + + const rp3d::Ray RP3D_RAY{ start, end }; + world->raycast(RP3D_RAY, this, collisionTag); + + if (temp.hit) + { + temp.distance = SHVec3::Distance(start, temp.position); + temp.angle = SHVec3::Angle(start, temp.position); + } + + raycasts.emplace_back(RP3D_RAY, temp); + return temp; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderRaycast(EntityID eid, const SHRay& ray, float distance) noexcept + { + SHPhysicsRaycastResult result; + result.distance = distance; + + // Get a valid physics object with at least 1 collider. + const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); + if (!PHYSICS_OBJECT) + return result; + + auto* rp3dBody = PHYSICS_OBJECT->GetCollisionBody(); + + // Data to populate + rp3d::RaycastInfo rp3dRaycastInfo; + bool hit = false; + + if (distance == std::numeric_limits::infinity()) + { + hit = rp3dBody->raycast(ray, rp3dRaycastInfo); + } + else + { + const SHVec3 END_POINT = ray.position + ray.direction * distance; + const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; + hit = rp3dBody->raycast(RP3D_RAY, rp3dRaycastInfo); + } + + if (hit) + { + result.hit = true; + result.position = rp3dRaycastInfo.worldPoint; + result.normal = rp3dRaycastInfo.worldPoint; + result.distance = SHVec3::Distance(ray.position, result.position); + result.angle = SHVec3::Angle(ray.position, result.position); + result.entityHit = eid; + result.shapeIndex = findColliderIndex(rp3dBody, rp3dRaycastInfo.collider->getEntity()); + } + + raycasts.emplace_back(ray, result); + return result; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderRaycast(EntityID eid, int shapeIndex, const SHRay& ray, float distance) noexcept + { + SHPhysicsRaycastResult result; + result.distance = distance; + + // Get a valid physics object with at least 1 collider. + const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); + if (!PHYSICS_OBJECT) + return result; + + // Boundary check for shape index + if (shapeIndex < 0 || shapeIndex >= static_cast(PHYSICS_OBJECT->GetCollisionBody()->getNbColliders())) + { + SHLOGV_WARNING("Invalid collision shape index passed in") + return result; + } + + auto* rp3dCollider = PHYSICS_OBJECT->GetCollisionBody()->getCollider(shapeIndex); + + rp3d::RaycastInfo rp3dRaycastInfo; + bool hit = false; + if (distance == std::numeric_limits::infinity()) + { + hit = rp3dCollider->raycast(ray, rp3dRaycastInfo); + } + else + { + const SHVec3 END_POINT = ray.position + ray.direction * distance; + const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; + hit = rp3dCollider->raycast(RP3D_RAY, rp3dRaycastInfo); + } + + if (hit) + { + result.hit = true; + result.position = rp3dRaycastInfo.worldPoint; + result.normal = rp3dRaycastInfo.worldPoint; + result.distance = SHVec3::Distance(ray.position, result.position); + result.angle = SHVec3::Angle(ray.position, result.position); + result.entityHit = eid; + result.shapeIndex = shapeIndex; + } + + raycasts.emplace_back(ray, result); + return result; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderLinecast(EntityID eid, const SHVec3& start, const SHVec3& end) noexcept + { + SHPhysicsRaycastResult result; + result.distance = SHVec3::Distance(start, end); + + const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); + if (!PHYSICS_OBJECT) + return result; + + auto* rp3dBody = PHYSICS_OBJECT->GetCollisionBody(); + + rp3d::RaycastInfo rp3dRaycastInfo; + + const rp3d::Ray RP3D_RAY{ start, end }; + if (rp3dBody->raycast(RP3D_RAY, rp3dRaycastInfo)) + { + result.hit = true; + result.position = rp3dRaycastInfo.worldPoint; + result.normal = rp3dRaycastInfo.worldPoint; + result.distance = SHVec3::Distance(start, result.position); + result.angle = SHVec3::Angle(end, result.position); + result.entityHit = eid; + result.shapeIndex = findColliderIndex(rp3dBody, rp3dRaycastInfo.collider->getEntity()); + } + + raycasts.emplace_back(RP3D_RAY, result); + return result; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderLinecast(EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept + { + SHPhysicsRaycastResult result; + result.distance = SHVec3::Distance(start, end); + + const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); + if (!PHYSICS_OBJECT) + return result; + + if (shapeIndex < 0 || shapeIndex >= static_cast(PHYSICS_OBJECT->GetCollisionBody()->getNbColliders())) + { + SHLOGV_WARNING("Invalid collision shape index passed in") + return result; + } + + auto* rp3dCollider = PHYSICS_OBJECT->GetCollisionBody()->getCollider(shapeIndex); + + rp3d::RaycastInfo rp3dRaycastInfo; + + const rp3d::Ray RP3D_RAY{ start, end }; + if (rp3dCollider->raycast(RP3D_RAY, rp3dRaycastInfo)) + { + result.hit = true; + result.position = rp3dRaycastInfo.worldPoint; + result.normal = rp3dRaycastInfo.worldPoint; + result.distance = SHVec3::Distance(start, result.position); + result.angle = SHVec3::Angle(end, result.position); + result.entityHit = eid; + result.shapeIndex = shapeIndex; + } + + raycasts.emplace_back(RP3D_RAY, result); + return result; + } + + rp3d::decimal SHPhysicsRaycaster::notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) + { + temp.hit = true; + temp.position = raycastInfo.worldPoint; + temp.normal = raycastInfo.worldNormal; + + if (!objectManager) + { + SHLOGV_ERROR("No physics object manager linked with raycaster to match bodies") + return 0.0f; + } + + // Compare body IDs to find the matching physics object + const auto HIT_BODY_EID = raycastInfo.body->getEntity(); + + for (const auto& [entityID, physicsObject] : objectManager->GetPhysicsObjects()) + { + const auto RP3D_BODY = physicsObject.GetCollisionBody(); + + // Match rp3d bodies + if (RP3D_BODY->getEntity() != HIT_BODY_EID) + continue; + + temp.entityHit = entityID; + + // Find collider index + if (const int INDEX = findColliderIndex(RP3D_BODY, raycastInfo.collider->getEntity()); INDEX > -1) + { + temp.shapeIndex = INDEX; + break; + } + } + + return 0.0f; + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject* SHPhysicsRaycaster::validateColliderRaycast(EntityID eid) noexcept + { + if (!objectManager) + { + SHLOGV_ERROR("No physics object manager linked with raycaster to match bodies") + return nullptr; + } + + auto* physicsObject = objectManager->GetPhysicsObject(eid); + if (!physicsObject || physicsObject->GetCollisionBody()->getNbColliders() == 0) + { + SHLOGV_WARNING("Cannot cast ray at an entity without colliders!") + return nullptr; + } + + return physicsObject; + } + + int SHPhysicsRaycaster::findColliderIndex(const rp3d::CollisionBody* rp3dBody, rp3d::Entity rp3dColliderEID) noexcept + { + const int NUM_COLLISION_SHAPES = static_cast(rp3dBody->getNbColliders()); + for (int i = 0; i < NUM_COLLISION_SHAPES; ++i) + { + const auto COLLIDER_EID = rp3dBody->getCollider(i)->getEntity(); + if (COLLIDER_EID == rp3dColliderEID) + return i; + } + + return -1; + } + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h new file mode 100644 index 00000000..447207c7 --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h @@ -0,0 +1,134 @@ +/**************************************************************************************** + * \file SHPhysicsRaycaster.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Physics Raycaster. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +#include + +// Project Headers +#include "Math/SHRay.h" +#include "Physics/PhysicsObject/SHPhysicsObjectManager.h" +#include "Physics/SHPhysicsWorld.h" +#include "SH_API.h" +#include "SHCollisionTags.h" +#include "SHPhysicsRaycastResult.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + class SH_API SHPhysicsRaycaster : public reactphysics3d::RaycastCallback + { + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using RaycastPair = std::pair; + using RaycastPairs = std::vector; + + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsRaycaster() noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] const RaycastPairs& GetRaycasts() const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetObjectManager(SHPhysicsObjectManager* physicsObjectManager) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + void BindToWorld (rp3d::PhysicsWorld* physicsWorld) noexcept; + void ClearFrame () noexcept; + + // TODO(Diren): Filtering, return all shades ray hits + + SHPhysicsRaycastResult Raycast + ( + const SHRay& ray + , float distance = std::numeric_limits::infinity() + , const SHCollisionTag& collisionTag = SHCollisionTag{} + ) noexcept; + + SHPhysicsRaycastResult Linecast + ( + const SHVec3& start + , const SHVec3& end + , const SHCollisionTag& collisionTag = SHCollisionTag{} + ) noexcept; + + SHPhysicsRaycastResult ColliderRaycast + ( + EntityID eid + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + SHPhysicsRaycastResult ColliderRaycast + ( + EntityID eid + , int shapeIndex + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + SHPhysicsRaycastResult ColliderLinecast + ( + EntityID eid + , const SHVec3& start + , const SHVec3& end + ) noexcept; + + SHPhysicsRaycastResult ColliderLinecast + ( + EntityID eid + , int shapeIndex + , const SHVec3& start + , const SHVec3& end + ) noexcept; + + rp3d::decimal notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) override; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + rp3d::PhysicsWorld* world; + SHPhysicsObjectManager* objectManager; // For + SHPhysicsRaycastResult temp; // Holds the temporary result after casting into the world + RaycastPairs raycasts; // Used for debug drawing + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsObject* validateColliderRaycast (EntityID eid) noexcept; + static int findColliderIndex (const rp3d::CollisionBody* rp3dBody, rp3d::Entity rp3dColliderEID) noexcept; + }; + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp deleted file mode 100644 index 8022e487..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/**************************************************************************************** - * \file SHBoxCollisionShape.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Box Collision Shape. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHBox.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Math/SHMatrix.h" -#include "Physics/Collision/SHCollider.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHBox::SHBox(SHCollisionShapeID id) noexcept - : SHConvexPolyhedron (id, Type::BOX) - , relativeExtents { SHVec3::One } - , scale { SHVec3::One } - { - Extents = SHVec3::One * 0.5f; - } - - SHBox::SHBox(const SHBox& rhs) noexcept - : SHConvexPolyhedron ( rhs ) - , relativeExtents { rhs.relativeExtents } - , scale { rhs.scale } - { - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - } - - SHBox::SHBox(SHBox&& rhs) noexcept - : SHConvexPolyhedron ( rhs ) - , relativeExtents { rhs.relativeExtents } - , scale { rhs.scale } - { - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHBox& SHBox::operator=(const SHBox& rhs) noexcept - { - if (this == &rhs) - return *this; - - // Collision Shape Properties - - SHConvexPolyhedron::operator=(rhs); - - // Box Properties - - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - - // Local Properties - - relativeExtents = rhs.relativeExtents; - scale = rhs.scale; - - return *this; - } - - SHBox& SHBox::operator=(SHBox&& rhs) noexcept - { - // Collision Shape Properties - - SHConvexPolyhedron::operator=(rhs); - - // Box Properties - - Center = rhs.Center; - Extents = rhs.Extents; - Orientation = rhs.Orientation; - - // Local Properties - - relativeExtents = rhs.relativeExtents; - scale = rhs.scale; - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHVec3 SHBox::GetWorldExtents() const noexcept - { - return Extents; - } - - SHVec3 SHBox::GetRelativeExtents() const noexcept - { - return relativeExtents; - } - - SHVec3 SHBox::GetVertex(int index) const - { - if (index < 0 || index >= NUM_VERTICES) - throw std::invalid_argument("Index out-of-range!"); - - SHVec3 vertices[NUM_VERTICES]; - GetCorners(vertices); - - return vertices[index]; - } - - SHVec3 SHBox::GetNormal(int faceIndex) const - { - // Get local normal - const SHVec3& LOCAL_NORMAL = halfEdgeStructure->GetFace(faceIndex).normal; - - // Rotate normal into world space - return SHVec3::Rotate(LOCAL_NORMAL, Orientation); - } - - SHVec3 SHBox::GetWorldCentroid() const noexcept - { - return Center; - } - - SHVec3 SHBox::GetRelativeCentroid() const noexcept - { - if (collider) - return SHVec3{ Center } - collider->GetPosition(); - - return Center; - } - - SHVec3 SHBox::GetLocalCentroid() const noexcept - { - return SHVec3::Zero; - } - - SHQuaternion SHBox::GetWorldOrientation() const noexcept - { - return Orientation; - } - - SHQuaternion SHBox::GetRelativeOrientation() const noexcept - { - return transform.orientation; - } - - float SHBox::GetVolume() const noexcept - { - return 8.0f * (Extents.x * Extents.y * Extents.z); - } - - float SHBox::GetSurfaceArea() const noexcept - { - return 8.0f * (Extents.x * Extents.y - + Extents.x * Extents.z - + Extents.y * Extents.z); - } - - SHMatrix SHBox::GetInertiaTensor(float mass) const noexcept - { - static constexpr float ONE_OVER_TWELVE = (1.0f / 12.0f); - - const float WIDTH = 2.0f * Extents.x; - const float HEIGHT = 2.0f * Extents.y; - const float DEPTH = 2.0f * Extents.z; - - const float WIDTH_SQUARED = WIDTH * WIDTH; - const float HEIGHT_SQUARED = HEIGHT * HEIGHT; - const float DEPTH_SQUARED = DEPTH * DEPTH; - - const float H2_PLUS_D2 = HEIGHT_SQUARED + DEPTH_SQUARED; - const float W2_PLUS_H2 = WIDTH_SQUARED + HEIGHT_SQUARED; - const float W2_PLUS_D2 = WIDTH_SQUARED + DEPTH_SQUARED; - - SHMatrix result; - result.m[0][0] = ONE_OVER_TWELVE * mass * H2_PLUS_D2; - result.m[1][1] = ONE_OVER_TWELVE * mass * W2_PLUS_H2; - result.m[2][2] = ONE_OVER_TWELVE * mass * W2_PLUS_D2; - return result; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHBox::SetWorldExtents(const SHVec3& newWorldExtents) noexcept - { - Extents = newWorldExtents; - - // Recompute Relative radius - relativeExtents = 2.0f * Extents / scale; - } - - void SHBox::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept - { - relativeExtents = newRelativeExtents; - - // Recompute world radius - Extents = relativeExtents * scale * 0.5f; - } - - void SHBox::SetScale(const SHVec3& newScale) noexcept - { - scale = SHVec3::Abs(newScale); - - // Recompute world radius - Extents = relativeExtents * scale * 0.5f; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHBox::Update() noexcept - { - const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); - - SetScale(PARENT_TRANSFORM.scale); - - // Recompute center - const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); - - Orientation = FINAL_ROT; - Center = SHVec3::Transform(transform.position, TRS); - } - - bool SHBox::TestPoint(const SHVec3& point) const noexcept - { - return Contains(point); - } - - SHRaycastResult SHBox::Raycast(const SHRay& ray) const noexcept - { - SHRaycastResult result; - - result.hit = Intersects(ray.position, ray.direction, result.distance); - if (result.hit) - { - result.position = ray.position + ray.direction * result.distance; - result.angle = SHVec3::Angle(ray.position, result.position); - - // TODO: Compute Normal: Test which face the position belongs in. The normal is that face's normal. - } - - return result; - } - - SHMatrix SHBox::GetTRS() const noexcept - { - const SHQuaternion ROTATION = collider ? collider->GetTransform().orientation * transform.orientation : Orientation; - const SHVec3 SCALE = SHVec3{ Extents } *2.0f; - - return SHMatrix::Transform(Center, ROTATION, SCALE); - } - - SHAABB SHBox::ComputeAABB() const noexcept - { - SHVec3 min{ std::numeric_limits::max() }; - SHVec3 max{ std::numeric_limits::lowest() }; - - SHVec3 vertices[NUM_VERTICES]; - GetCorners(vertices); - - for (auto& vertex : vertices) - { - min = SHVec3::Min({ vertex, min }); - max = SHVec3::Max({ vertex, max }); - } - - const SHVec3 HALF_EXTENTS = (max - min) * 0.5f; - const SHVec3 CENTROID = min + HALF_EXTENTS; - - return SHAABB{ CENTROID, HALF_EXTENTS }; - } - - SHVec3 SHBox::FindSupportPoint(const SHVec3& direction) const noexcept - { - float bestDistance = std::numeric_limits::lowest(); - - - SHVec3 vertices[NUM_VERTICES]; - GetCorners(vertices); - - // No reason to put the center really.. - SHVec3 bestPoint = Center; - for (auto& vertex : vertices) - { - const float PROJECTION = SHVec3::Dot(vertex, direction); - - if (PROJECTION > bestDistance) - { - bestDistance = PROJECTION; - bestPoint = vertex; - } - } - - return bestPoint; - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h deleted file mode 100644 index 480b2501..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHBox.h +++ /dev/null @@ -1,129 +0,0 @@ -/**************************************************************************************** - * \file SHBox.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Box Collision Shape. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "SHConvexPolyhedron.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates the information to create a box. - */ - struct SHBoxCreateInfo - { - public: - SHVec3 Center = SHVec3::Zero; - SHVec3 Extents = SHVec3::One * 0.5f; - SHVec3 RelativeExtents = SHVec3::One; - SHQuaternion Orientation = SHQuaternion::Identity; - SHVec3 Scale = SHVec3::One; - }; - - /** - * @brief - * Encapsulate a Box Shape used for Physics Simulations. - */ - class SH_API SHBox final : public SHConvexPolyhedron - , private DirectX::BoundingOrientedBox - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHCollider; - friend class SHCompositeCollider; - friend class SHCollision; - friend class SHCollisionShapeLibrary; - - public: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr int NUM_VERTICES = 8; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHBox (SHCollisionShapeID id) noexcept; - SHBox (const SHBox& rhs) noexcept; - SHBox (SHBox&& rhs) noexcept; - - ~SHBox () override = default; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHBox& operator= (const SHBox& rhs) noexcept; - SHBox& operator= (SHBox&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] SHVec3 GetWorldExtents () const noexcept; - [[nodiscard]] SHVec3 GetRelativeExtents () const noexcept; - - // Overriden Methods - - [[nodiscard]] SHVec3 GetVertex (int index) const override; - [[nodiscard]] SHVec3 GetNormal (int faceIndex) const override; - - [[nodiscard]] SHVec3 GetWorldCentroid () const noexcept override; - [[nodiscard]] SHVec3 GetRelativeCentroid () const noexcept override; - [[nodiscard]] SHVec3 GetLocalCentroid () const noexcept override; - [[nodiscard]] SHQuaternion GetWorldOrientation () const noexcept override; - [[nodiscard]] SHQuaternion GetRelativeOrientation () const noexcept override; - [[nodiscard]] float GetVolume () const noexcept override; - [[nodiscard]] float GetSurfaceArea () const noexcept override; - [[nodiscard]] SHMatrix GetInertiaTensor (float mass) const noexcept override; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetWorldExtents (const SHVec3& newWorldExtents) noexcept; - void SetRelativeExtents (const SHVec3& newRelativeExtents) noexcept; - void SetScale (const SHVec3& newScale) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - void Update () noexcept override; - [[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override; - [[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override; - [[nodiscard]] SHMatrix GetTRS () const noexcept override; - [[nodiscard]] SHAABB ComputeAABB () const noexcept override; - [[nodiscard]] SHVec3 FindSupportPoint (const SHVec3& direction) const noexcept override; - - private: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - SHVec3 relativeExtents; - SHVec3 scale; - }; - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.cpp deleted file mode 100644 index dfbed047..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/**************************************************************************************** - * \file SHCollider.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Collider. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollisionShape.h" - -// Project Headers -#include "Physics/Collision/SHCollider.h" -#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" -#include "Reflection/SHReflectionMetadata.h" -#include "Tools/Utilities/SHUtilities.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollisionShape::SHCollisionShape(SHCollisionShapeID id, Type colliderType) - : id { id } - , flags { 0 } - , collider { nullptr } - , collisionTag { SHCollisionTagMatrix::GetTag(0) } - { - flags |= 1U << SHUtilities::ConvertEnum(colliderType); - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - EntityID SHCollisionShape::GetEntityID() const noexcept - { - return id.GetEntityID(); - } - - uint32_t SHCollisionShape::GetIndex() const noexcept - { - return id.GetShapeIndex(); - } - - float SHCollisionShape::GetFriction() const noexcept - { - return material.GetFriction(); - } - - float SHCollisionShape::GetBounciness() const noexcept - { - return material.GetBounciness(); - } - - float SHCollisionShape::GetDensity() const noexcept - { - return material.GetDensity(); - } - - const SHPhysicsMaterial& SHCollisionShape::GetMaterial() const noexcept - { - return material; - } - - const SHVec3& SHCollisionShape::GetPositionOffset() const noexcept - { - return transform.position; - } - - const SHVec3& SHCollisionShape::GetRotationOffset() const noexcept - { - return rotationOffset; - } - - SHCollisionShape::Type SHCollisionShape::GetType() const noexcept - { - for (int i = 0; i < SHUtilities::ConvertEnum(Type::COUNT); ++i) - { - const uint8_t FLAG_VALUE = 1U << SHUtilities::ConvertEnum(static_cast(i)); - - if (flags & FLAG_VALUE) - return static_cast(i); - } - - return Type::INVALID; - } - - bool SHCollisionShape::IsTrigger() const noexcept - { - static constexpr int FLAG_POS = 3; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - return flags & FLAG_VALUE; - } - - bool SHCollisionShape::IsColliding() const noexcept - { - static constexpr int FLAG_POS = 4; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - return flags & FLAG_VALUE; - } - - const SHCollisionTag& SHCollisionShape::GetCollisionTag() const noexcept - { - return *collisionTag; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionShape::SetCollisionTag(SHCollisionTag* newCollisionTag) noexcept - { - collisionTag = newCollisionTag; - } - - void SHCollisionShape::SetFriction(float friction) noexcept - { - material.SetFriction(friction); - } - - void SHCollisionShape::SetBounciness(float bounciness) noexcept - { - material.SetBounciness(bounciness); - } - - void SHCollisionShape::SetDensity(float density) noexcept - { - material.SetDensity(density); - } - - void SHCollisionShape::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept - { - material = newMaterial; - } - - void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept - { - transform.position = posOffset; - - Update(); - } - - void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept - { - rotationOffset = rotOffset; - transform.orientation = SHQuaternion::FromEuler(rotationOffset); - - Update(); - } - - void SHCollisionShape::SetIsTrigger(bool isTrigger) noexcept - { - static constexpr int FLAG_POS = 3; - static constexpr uint8_t FLAG_VALUE = 1U << FLAG_POS; - - isTrigger ? flags |= FLAG_VALUE : flags &= ~FLAG_VALUE; - } - -} // namespace SHADE - -RTTR_REGISTRATION -{ - using namespace SHADE; - using namespace rttr; - - registration::enumeration("Collider Type") - ( - value("Box", SHCollisionShape::Type::BOX), - value("Sphere", SHCollisionShape::Type::SPHERE) - // TODO(Diren): Add More Shapes - ); - - registration::class_("Collider") - .property("IsTrigger" , &SHCollisionShape::IsTrigger , &SHCollisionShape::SetIsTrigger ) - .property("Friction" , &SHCollisionShape::GetFriction , &SHCollisionShape::SetFriction ) - .property("Bounciness" , &SHCollisionShape::GetBounciness , &SHCollisionShape::SetBounciness ) - .property("Density" , &SHCollisionShape::GetDensity , &SHCollisionShape::SetDensity ) - .property("Position Offset" , &SHCollisionShape::GetPositionOffset, &SHCollisionShape::SetPositionOffset) - .property("Rotation Offset" , &SHCollisionShape::GetRotationOffset, &SHCollisionShape::SetRotationOffset) (metadata(META::angleInRad, true)); -} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.h deleted file mode 100644 index 3e57b74a..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShape.h +++ /dev/null @@ -1,210 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionShape.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Base CollisionShape Class. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "ECS_Base/Entity/SHEntity.h" -#include "Physics/Collision/CollisionTags/SHCollisionTags.h" -#include "Physics/Collision/SHPhysicsMaterial.h" -#include "SHCollisionShapeID.h" -#include "Math/Geometry/SHAABB.h" -#include "Math/Transform/SHTransform.h" -#include "Physics/Collision/SHPhysicsRaycastResult.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - class SHRigidBody; - - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - class SH_API SHCollisionShape - { - private: - - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHCollider; - friend class SHCompositeCollider; - friend class SHColliderComponent; - friend class SHCollisionShapeLibrary; - friend class SHCollisionSpace; - friend struct SHManifold; - - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - enum class Type - { - SPHERE - , BOX - , CAPSULE - , CONVEX_HULL - - , COUNT - , INVALID = -1 - }; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHCollisionShape (SHCollisionShapeID id, Type colliderType = Type::SPHERE); - - SHCollisionShape (const SHCollisionShape& rhs) noexcept = default; - SHCollisionShape (SHCollisionShape&& rhs) noexcept = default; - virtual ~SHCollisionShape () noexcept = default; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHCollisionShape& operator=(const SHCollisionShape& rhs) noexcept = default; - SHCollisionShape& operator=(SHCollisionShape&& rhs) noexcept = default; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] EntityID GetEntityID () const noexcept; - [[nodiscard]] uint32_t GetIndex () const noexcept; - - // Material Properties - // TODO: Remove individual setters once instanced materials are supported - - [[nodiscard]] float GetFriction () const noexcept; - [[nodiscard]] float GetBounciness () const noexcept; - [[nodiscard]] float GetDensity () const noexcept; - [[nodiscard]] const SHPhysicsMaterial& GetMaterial () const noexcept; - - // Offsets - - [[nodiscard]] const SHVec3& GetPositionOffset () const noexcept; - [[nodiscard]] const SHVec3& GetRotationOffset () const noexcept; - - // Flags - - [[nodiscard]] Type GetType () const noexcept; - [[nodiscard]] bool IsTrigger () const noexcept; - [[nodiscard]] bool IsColliding () const noexcept; - - [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; - - // Virtual methods - - [[nodiscard]] virtual SHVec3 GetWorldCentroid () const noexcept = 0; - [[nodiscard]] virtual SHVec3 GetRelativeCentroid () const noexcept = 0; - [[nodiscard]] virtual SHVec3 GetLocalCentroid () const noexcept = 0; - [[nodiscard]] virtual SHQuaternion GetWorldOrientation () const noexcept = 0; - [[nodiscard]] virtual SHQuaternion GetRelativeOrientation () const noexcept = 0; - [[nodiscard]] virtual float GetVolume () const noexcept = 0; - [[nodiscard]] virtual float GetSurfaceArea () const noexcept = 0; - [[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; - - void SetFriction (float friction) noexcept; - void SetBounciness (float bounciness) noexcept; - void SetDensity (float density) noexcept; - void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; - - void SetPositionOffset (const SHVec3& posOffset) noexcept; - void SetRotationOffset (const SHVec3& rotOffset) noexcept; - - // Flags - - // Forces rigidbody to recompute mass if one exists - void SetIsTrigger (bool isTrigger) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Computes the transform of the shape. - */ - virtual void Update () noexcept = 0; - - /** - * @brief - * Tests if a point is inside this shape. - * @param point - * The point to test against the shape. - * @return - * True if the point is inside the shape. False otherwise. - */ - [[nodiscard]] virtual bool TestPoint (const SHVec3& point) const noexcept = 0; - - /** - * @brief - * Casts a ray at this shape. - * @param ray - * The ray to cast at the shape. - * @return - * The result of the ray cast. See the corresponding struct for it's contents. - */ - [[nodiscard]] virtual SHRaycastResult Raycast (const SHRay& ray) const noexcept = 0; - - /** - * @brief - * Computes the TRS matrix for rendering the shape. - * @return - * The model-to-world matrix for rendering the shape. - */ - [[nodiscard]] virtual SHMatrix GetTRS () const noexcept = 0; - - /** - * @brief - * Computes a tight-fitting AABB around this shape. - * @return - * An AABB. - */ - [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0; - - protected: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - SHCollisionShapeID id; - - SHCollider* collider; // The collider it belongs to. - SHCollisionTag* collisionTag; - SHPhysicsMaterial material; // TODO: Change to pointer once instancing is supported - - SHTransform transform; // Stores the local position and rotation. - - // Needed for conversion to euler angles - SHVec3 rotationOffset; - - uint8_t flags; // 0 0 0 trigger convexHull capsule sphere box - - - RTTR_ENABLE() - }; - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.hpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.hpp deleted file mode 100644 index bb9ccb1f..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeID.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionShapeKey.hpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Inlined Implementations for a Collison Shape ID Class and - * it's hashing function. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Primary Header -#include "SHCollisionShapeID.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - inline SHCollisionShapeID::SHCollisionShapeID(EntityID eid, uint32_t shapeID) noexcept - : ids { eid, shapeID } - {} - - inline SHCollisionShapeID::SHCollisionShapeID(const SHCollisionShapeID& rhs) noexcept - : ids { rhs.ids.entityID, rhs.ids.shapeIndex } - {} - - inline SHCollisionShapeID::SHCollisionShapeID(SHCollisionShapeID&& rhs) noexcept - : ids { rhs.ids.entityID, rhs.ids.shapeIndex } - {} - - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - inline SHCollisionShapeID& SHCollisionShapeID::operator=(const SHCollisionShapeID& rhs) noexcept - { - if (this == &rhs) - return *this; - - value = rhs.value; - - return *this; - } - - inline SHCollisionShapeID& SHCollisionShapeID::operator=(SHCollisionShapeID&& rhs) noexcept - { - value = rhs.value; - - return *this; - } - - inline bool SHCollisionShapeID::operator==(const SHCollisionShapeID& rhs) const - { - return value == rhs.value; - } - - inline std::size_t SHCollisionShapeIDHash::operator()(const SHCollisionShapeID& id) const - { - return std::hash{}(id.value); - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - inline EntityID SHCollisionShapeID::GetEntityID() const noexcept - { - return ids.entityID; - } - - inline uint32_t SHCollisionShapeID::GetShapeIndex() const noexcept - { - return ids.shapeIndex; - } -} diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp deleted file mode 100644 index c70728f3..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHCollisionShapeLibrary.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/**************************************************************************************** - * \file SHCollisionShapeLibrary.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Collison Shape Factory Class. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHCollisionShapeLibrary.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHCollisionShapeLibrary::SHCollisionShapeLibrary() noexcept - { - createBoxPolyhedron(); - } - - SHCollisionShapeLibrary::~SHCollisionShapeLibrary() noexcept - { - // Free all shapes in each container - for (auto* sphereCollisionShape : spheres | std::views::values) - DestroyShape(sphereCollisionShape); - - // Free all shapes in each container - for (auto* boxCollisionShape : boxes | std::views::values) - DestroyShape(boxCollisionShape); - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHSphere* SHCollisionShapeLibrary::CreateSphere(SHCollisionShapeID id, const SHSphereCreateInfo& createInfo) - { - const auto RESULT = spheres.emplace(id, new SHSphere{ id }); - if (RESULT.second) - { - SHSphere* sphere = RESULT.first->second; - - sphere->Center = createInfo.Center; - sphere->Radius = createInfo.Radius; - sphere->relativeRadius = createInfo.RelativeRadius; - sphere->scale = createInfo.Scale; - - return sphere; - } - - return spheres.find(id)->second; - } - - SHBox* SHCollisionShapeLibrary::CreateBox(SHCollisionShapeID id, const SHBoxCreateInfo& createInfo) - { - const auto RESULT = boxes.emplace(id, new SHBox{ id }); - if (RESULT.second) - { - SHBox* box = RESULT.first->second; - - box->Center = createInfo.Center; - box->Extents = createInfo.Extents; - box->relativeExtents = createInfo.RelativeExtents; - box->Orientation = createInfo.Orientation; - box->scale = createInfo.Scale; - - // Set halfEdge data structure for the box - box->halfEdgeStructure = &boxHalfEdgeStructure; - - return box; - } - - return boxes.find(id)->second; - } - - - void SHCollisionShapeLibrary::DestroyShape(SHCollisionShape* shape) - { - switch (shape->GetType()) - { - case SHCollisionShape::Type::BOX: - { - break; - } - case SHCollisionShape::Type::SPHERE: - { - SHSphere* sphere = spheres.find(shape->id)->second; - spheres.erase(shape->id); - - delete sphere; - sphere = nullptr; - - break; - } - case SHCollisionShape::Type::CAPSULE: - { - break; - } - default: break; - } - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHCollisionShapeLibrary::createBoxPolyhedron() noexcept - { - static constexpr int NUM_VERTICES_PER_FACE = 4; - static constexpr int NUM_FACES = 6; - - /* - * Vertices (Front/Back Face): - * - * 3/7 ---------- 2/6 - * | | - * | | - * | | - * 0/4 ---------- 1/5 - * - * Faces: - * - * Front: 0 (0,1,2,3) Normal: -Z - * Right: 1 (1,5,6,2) Normal: X - * Back: 2 (5,4,7,6) Normal: Z - * Left: 3 (4,0,3,7) Normal: -X - * Top: 4 (3,2,6,7) Normal: Y - * Bottom: 5 (4,5,1,0) Normal: -Y - * - */ - - // Create face data - - const SHVec3 FACE_NORMALS[NUM_FACES] - { - -SHVec3::UnitZ - , SHVec3::UnitX - , SHVec3::UnitZ - , -SHVec3::UnitX - , SHVec3::UnitY - , -SHVec3::UnitY - }; - - const int32_t FACE_VERTICES[NUM_FACES][NUM_VERTICES_PER_FACE] - { - { 0, 1, 2, 3 } - , { 5, 6, 2, 1 } - , { 5, 4, 7, 6 } - , { 0, 3, 7, 4 } - , { 2, 6, 7, 3 } - , { 5, 1, 0, 4 } - }; - - for (int i = 0; i < NUM_FACES; ++i) - { - SHHalfEdgeStructure::Face newFace; - newFace.normal = FACE_NORMALS[i]; - - for (int j = 0; j < NUM_VERTICES_PER_FACE; ++j) - newFace.vertexIndices.emplace_back(FACE_VERTICES[i][j]); - - boxHalfEdgeStructure.AddFace(newFace); - } - - boxHalfEdgeStructure.Build(); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.cpp deleted file mode 100644 index 8cccd837..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************************** - * \file SHConvexPolyhedronCollisionShape.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a convex polyhedron collision shape. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHConvexPolyhedron.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHConvexPolyhedron::SHConvexPolyhedron(SHCollisionShapeID id,Type polyhedronType) noexcept - : SHCollisionShape (id, polyhedronType) - , halfEdgeStructure { nullptr } - {} - - SHConvexPolyhedron::SHConvexPolyhedron(const SHConvexPolyhedron& rhs) noexcept - : SHCollisionShape ( rhs ) - , halfEdgeStructure { rhs.halfEdgeStructure } - {} - - SHConvexPolyhedron::SHConvexPolyhedron(SHConvexPolyhedron&& rhs) noexcept - : SHCollisionShape ( rhs ) - , halfEdgeStructure { rhs.halfEdgeStructure } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHConvexPolyhedron& SHConvexPolyhedron::operator=(const SHConvexPolyhedron& rhs) noexcept - { - if (this == &rhs) - return *this; - - SHCollisionShape::operator=(rhs); - - // Local Properties - halfEdgeStructure = rhs.halfEdgeStructure; - - return *this; - } - - SHConvexPolyhedron& SHConvexPolyhedron::operator=(SHConvexPolyhedron&& rhs) noexcept - { - SHCollisionShape::operator=(rhs); - - // Local Properties - halfEdgeStructure = rhs.halfEdgeStructure; - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHHalfEdgeStructure* SHConvexPolyhedron::GetHalfEdgeStructure() const noexcept - { - return halfEdgeStructure; - } - - int32_t SHConvexPolyhedron::GetFaceCount() const noexcept - { - return halfEdgeStructure->GetFaceCount(); - } - - int32_t SHConvexPolyhedron::GetHalfEdgeCount() const noexcept - { - return halfEdgeStructure->GetHalfEdgeCount(); - } - - const SHHalfEdgeStructure::Face& SHConvexPolyhedron::GetFace(int index) const - { - // Assume it has already been initialised - return halfEdgeStructure->GetFace(index); - } - - const SHHalfEdgeStructure::HalfEdge& SHConvexPolyhedron::GetHalfEdge(int index) const - { - // Assume it has already been initialised - return halfEdgeStructure->GetHalfEdge(index); - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h deleted file mode 100644 index 3944c556..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHConvexPolyhedron.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************************** - * \file SHConvexPolyhedronCollisionShape.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a convex polyhedron collision shape. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "SHCollisionShape.h" -#include "SHHalfEdgeStructure.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a convex polyhedron shape used for Physics Simulations.. - */ - class SH_API SHConvexPolyhedron : public SHCollisionShape - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHCollider; - friend class SHCollision; - friend class SHCollisionShapeLibrary; - - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHConvexPolyhedron (SHCollisionShapeID id, Type polyhedronType) noexcept; - SHConvexPolyhedron (const SHConvexPolyhedron& rhs) noexcept; - SHConvexPolyhedron (SHConvexPolyhedron&& rhs) noexcept; - - ~SHConvexPolyhedron () override = default; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHConvexPolyhedron& operator=(const SHConvexPolyhedron& rhs) noexcept; - SHConvexPolyhedron& operator=(SHConvexPolyhedron&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] const SHHalfEdgeStructure* GetHalfEdgeStructure () const noexcept; - [[nodiscard]] int32_t GetFaceCount () const noexcept; - [[nodiscard]] const SHHalfEdgeStructure::Face& GetFace (int index) const; - [[nodiscard]] int32_t GetHalfEdgeCount () const noexcept; - [[nodiscard]] const SHHalfEdgeStructure::HalfEdge& GetHalfEdge (int index) const; - - // Virtual Methods - - [[nodiscard]] virtual SHVec3 GetVertex (int index) const = 0; - [[nodiscard]] virtual SHVec3 GetNormal (int faceIndex) const = 0; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Finds the most extreme point on the polygon in a given direction. - * @param direction - * The direction to find the support point in. - * @return - * The most extreme vertex in the given direction. - */ - [[nodiscard]] virtual SHVec3 FindSupportPoint (const SHVec3& direction) const noexcept = 0; - - protected: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - SHHalfEdgeStructure* halfEdgeStructure; // Defines the polyhedron by it's half edges. - }; - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.cpp deleted file mode 100644 index a25b5b8b..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/**************************************************************************************** - * \file SHHalfEdgeStructure.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a half-edge data structure to represent polyhedra. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHHalfEdgeStructure.h" - -// Helper Macros - -#define BUILD_UINT64_FROM_UINT32S(a, b) (uint64_t)a << 32 | b - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHHalfEdgeStructure::Face::Face(const Face& rhs) noexcept - : normal { rhs.normal } - { - std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); - } - - SHHalfEdgeStructure::Face::Face(Face&& rhs) noexcept - : normal { rhs.normal } - { - std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHHalfEdgeStructure::Face& SHHalfEdgeStructure::Face::operator=(const Face& rhs) noexcept - { - if (this == &rhs) - return *this; - - normal = rhs.normal; - - vertexIndices.clear(); - std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); - - return *this; - } - - SHHalfEdgeStructure::Face& SHHalfEdgeStructure::Face::operator=(Face&& rhs) noexcept - { - normal = rhs.normal; - - vertexIndices.clear(); - std::ranges::copy(rhs.vertexIndices.begin(), rhs.vertexIndices.end(), std::back_inserter(vertexIndices)); - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - int32_t SHHalfEdgeStructure::GetFaceCount() const noexcept - { - return static_cast(faces.size()); - } - - int32_t SHHalfEdgeStructure::GetHalfEdgeCount() const noexcept - { - return static_cast(halfEdges.size()); - } - - const SHHalfEdgeStructure::Face& SHHalfEdgeStructure::GetFace(int32_t index) const - { - if (index < 0 || index >= static_cast(faces.size())) - throw std::invalid_argument("Index out-of-range!"); - - return faces[index]; - } - - const SHHalfEdgeStructure::HalfEdge& SHHalfEdgeStructure::GetHalfEdge(int32_t index) const - { - if (index < 0 || index >= static_cast(halfEdges.size())) - throw std::invalid_argument("Index out-of-range!"); - - return halfEdges[index]; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHHalfEdgeStructure::AddFace(const Face& face) - { - faces.emplace_back(face); - } - - void SHHalfEdgeStructure::Build() noexcept - { - // We use the pair of vertex IDs on a half-edge to prevent duplicates - std::unordered_map edgeMap; - edgeMap.clear(); - - if (faces.empty()) - { - SHLOGV_CRITICAL("Unable to build convex polyhedron, no faces have been added!") - return; - } - - // For each face, build half edges - for (size_t i = 0; i < faces.size(); ++i) - { - Face& face = faces[i]; - - if (face.vertexIndices.empty()) - { - SHLOGV_CRITICAL("Unable to build convex polyhedron, no vertices have been added to face {}!", i) - return; - } - - // Iterate through vertices and build half-edges - for (size_t j = 0; j < face.vertexIndices.size(); ++j) - { - const int32_t TAIL = face.vertexIndices[j].index; - const int32_t HEAD = face.vertexIndices[(j + 1) % face.vertexIndices.size()].index; - - const uint64_t NEW_EDGE_ID = BUILD_UINT64_FROM_UINT32S(TAIL, HEAD); - const uint64_t TWIN_EDGE_ID = BUILD_UINT64_FROM_UINT32S(HEAD, TAIL); - - // Check if the half-edge has already been inserted - auto newEdgeIter = edgeMap.find(NEW_EDGE_ID); - if (newEdgeIter == edgeMap.end()) - { - // Reuse the iterator for mapping with the twin - newEdgeIter = edgeMap.emplace(NEW_EDGE_ID, HalfEdge{}).first; - - HalfEdge& newHalfEdge = newEdgeIter->second; - newHalfEdge.tailVertexIndex = TAIL; - newHalfEdge.headVertexIndex = HEAD; - newHalfEdge.faceIndex = static_cast(i); - - // Set edge index of the newly inserted edge as the size of the map - 1 - // Since it is an unordered map, it will just be at the back - newHalfEdge.edgeIndex = static_cast(edgeMap.size()) - 1; - - // Map vertex to this edge index - face.vertexIndices[j].edgeIndex = newHalfEdge.edgeIndex; - } - - // Find twin edge if one exists - auto twinEdgeIter = edgeMap.find(TWIN_EDGE_ID); - if (twinEdgeIter != edgeMap.end()) - { - // Set the twin index of both the edges - HalfEdge& newHalfEdge = newEdgeIter->second; - HalfEdge& twinHalfEdge = twinEdgeIter->second; - - newHalfEdge.twinEdgeIndex = twinHalfEdge.edgeIndex; - twinHalfEdge.twinEdgeIndex = newHalfEdge.edgeIndex; - } - } - } - - // Copy all half edges into the vector - // At this point, no duplicates should be in the map and all edges should be linked. - for (auto& halfEdge : edgeMap | std::views::values) - halfEdges.emplace_back(halfEdge); - - // Sort based on edge indices - std::ranges::sort(halfEdges.begin(), halfEdges.end(), [](const HalfEdge& lhs, const HalfEdge& rhs) - { - return lhs.edgeIndex < rhs.edgeIndex; - }); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.h b/SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.h deleted file mode 100644 index 8982ae23..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHHalfEdgeStructure.h +++ /dev/null @@ -1,143 +0,0 @@ -/**************************************************************************************** - * \file SHHalfEdgeStructure.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a half-edge data structure to represent polyhedra. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "Math/Vector/SHVec3.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates data for a convex polyhedron's geometry represented as faces & half edges. - */ - class SH_API SHHalfEdgeStructure - { - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates the data half edge of a face on a polyhedron. - */ - struct HalfEdge - { - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - //Head and tail forms the edge. - //Head <----- Tail - int32_t tailVertexIndex = -1; - - // Head is also tail of the next edge. - int32_t headVertexIndex = -1; - - int32_t edgeIndex = -1; - // Other half of the edge on a different face. - // Important for extrapolating face normals. - int32_t twinEdgeIndex = -1; - - // Adjacent face of this edge. - int32_t faceIndex = -1; - }; - - struct Vertex - { - public: - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - int32_t index = -1; - int32_t edgeIndex = -1; // the half-edge that this vertex is a tail of. - }; - - /** - * @brief - * Encapsulates the data of a face on a polyhedron. - */ - struct Face - { - public: - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - SHVec3 normal; - std::vector vertexIndices; // Must be in CCW order - - /*-------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-------------------------------------------------------------------------------*/ - - Face () noexcept = default; - Face (const Face& rhs) noexcept; - Face (Face&& rhs) noexcept; - ~Face () noexcept = default; - - /*-------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*-------------------------------------------------------------------------------*/ - - Face& operator= (const Face& rhs) noexcept; - Face& operator= (Face&& rhs) noexcept; - }; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] int32_t GetFaceCount () const noexcept; - [[nodiscard]] int32_t GetHalfEdgeCount () const noexcept; - - [[nodiscard]] const Face& GetFace (int32_t index) const; - [[nodiscard]] const HalfEdge& GetHalfEdge (int32_t index) const; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Adds a face to the polyhedron. The face must be constructed outside the polyhedron. - * @param face - * The face to insert. - */ - void AddFace (const Face& face); - - /** - * @brief - * Builds the half-edges of the polyhedron using the faces.
- * Before this method is invoked, there must be some faces. - * @return - */ - void Build() noexcept; - - private: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - std::vector faces; - std::vector halfEdges; - - }; - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.cpp b/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.cpp deleted file mode 100644 index 5708bf91..00000000 --- a/SHADE_Engine/src/Physics/Collision/Shapes/SHSphere.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/**************************************************************************************** - * \file SHSphereCollisionShape.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Sphere Collision Shape. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHSphere.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Math/SHMatrix.h" -#include "Physics/Collision/SHCollider.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHSphere::SHSphere(SHCollisionShapeID id) noexcept - : SHCollisionShape (id, Type::SPHERE) - , relativeRadius { 1.0f } - , scale { 1.0f } - { - Radius = 0.5f; - } - - SHSphere::SHSphere(const SHSphere& rhs) noexcept - : SHCollisionShape ( rhs ) - , relativeRadius { rhs.relativeRadius } - , scale { rhs.scale } - { - Center = rhs.Center; - Radius = rhs.Radius; - } - - SHSphere::SHSphere(SHSphere&& rhs) noexcept - : SHCollisionShape ( rhs ) - , relativeRadius { rhs.relativeRadius } - , scale { rhs.scale } - { - Center = rhs.Center; - Radius = rhs.Radius; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHSphere& SHSphere::operator=(const SHSphere& rhs) noexcept - { - if (this == &rhs) - return *this; - - SHCollisionShape::operator=(rhs); - - // Sphere Properties - - Center = rhs.Center; - Radius = rhs.Radius; - - // Local Properties - - relativeRadius = rhs.relativeRadius; - scale = rhs.scale; - - return *this; - } - - SHSphere& SHSphere::operator=(SHSphere&& rhs) noexcept - { - SHCollisionShape::operator=(rhs); - - // Sphere Properties - - Center = rhs.Center; - Radius = rhs.Radius; - - // Local Properties - - relativeRadius = rhs.relativeRadius; - scale = rhs.scale; - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - float SHSphere::GetWorldRadius() const noexcept - { - return Radius; - } - - float SHSphere::GetRelativeRadius() const noexcept - { - return relativeRadius; - } - - SHVec3 SHSphere::GetWorldCentroid() const noexcept - { - return Center; - } - - SHVec3 SHSphere::GetRelativeCentroid() const noexcept - { - if (collider) - return SHVec3{ Center } - collider->GetPosition(); - - return Center; - } - - SHVec3 SHSphere::GetLocalCentroid() const noexcept - { - return SHVec3::Zero; - } - - SHQuaternion SHSphere::GetWorldOrientation() const noexcept - { - if (collider) - return collider->GetOrientation() * transform.orientation; - - return transform.orientation; - } - - SHQuaternion SHSphere::GetRelativeOrientation() const noexcept - { - return transform.orientation; - } - - float SHSphere::GetVolume() const noexcept - { - return (4.0f / 3.0f) * SHMath::PI * (Radius * Radius * Radius); - } - - float SHSphere::GetSurfaceArea() const noexcept - { - return 4.0f * SHMath::PI * (Radius * Radius); - } - - SHMatrix SHSphere::GetInertiaTensor(float mass) const noexcept - { - static constexpr float TWO_OVER_FIVE = 2.0f / 5.0f; - - const float DIAGONAL = TWO_OVER_FIVE * mass * (Radius * Radius); - - SHMatrix result; - result.m[0][0] = result.m[1][1] = result.m[2][2] = DIAGONAL; - return result; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHSphere::SetWorldRadius(float newWorldRadius) noexcept - { - Radius = newWorldRadius; - - // Recompute Relative radius - relativeRadius = 2.0f * Radius / scale; - } - - void SHSphere::SetRelativeRadius(float newRelativeRadius) noexcept - { - relativeRadius = newRelativeRadius; - - // Recompute world radius - Radius = relativeRadius * scale * 0.5f; - } - - void SHSphere::SetScale(float maxScale) noexcept - { - scale = std::fabs(maxScale); - - // Recompute world radius - Radius = relativeRadius * scale * 0.5f; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHSphere::Update() noexcept - { - const SHTransform& PARENT_TRANSFORM = collider->GetTransform(); - - const float SPHERE_SCALE = std::fabs(SHMath::Max({ PARENT_TRANSFORM.scale.x, PARENT_TRANSFORM.scale.y, PARENT_TRANSFORM.scale.z })); - SetScale(SPHERE_SCALE); - - // Recompute center - const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation; - const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position); - - Center = SHVec3::Transform(transform.position, TRS); - } - - bool SHSphere::TestPoint(const SHVec3& point) const noexcept - { - return Contains(point); - } - - SHRaycastResult SHSphere::Raycast(const SHRay& ray) const noexcept - { - SHRaycastResult result; - - result.hit = Intersects(ray.position, ray.direction, result.distance); - if (result.hit) - { - result.position = ray.position + ray.direction * result.distance; - result.angle = SHVec3::Angle(ray.position, result.position); - result.normal = SHVec3::Normalise(result.position - Center); - } - - return result; - } - - SHMatrix SHSphere::GetTRS() const noexcept - { - const SHQuaternion ROTATION = collider ? collider->GetTransform().orientation * transform.orientation : transform.orientation; - const SHVec3 SCALE { Radius }; - - return SHMatrix::Transform(Center, ROTATION, SCALE); - } - - SHAABB SHSphere::ComputeAABB() const noexcept - { - return SHAABB{ Center, SHVec3{ Radius } }; - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h deleted file mode 100644 index 04be00b4..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHContactConstraint.h +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************************** - * \file SHContactConstraint.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Contact Constraint. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "Physics/Collision/Contacts/SHManifold.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - struct SH_API SHContactConstraint - { - public: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - // Use the entity IDs to map resolved constraints back to the bodies - - SHCollisionKey key; - - // Material Data - - float friction = 0.0f; - float restitution = 0.0f; - - // Mass Data - - float invMassA = 0.0f; - float invMassB = 0.0f; - SHMatrix invInertiaA; - SHMatrix invInertiaB; - SHVec3 centerOfMassA; - SHVec3 centerOfMassB; - - // Collision Data - - SHVec3 normal; - SHVec3 tangents[SHContact::NUM_TANGENTS]; - SHContact contacts[SHManifold::MAX_NUM_CONTACTS]; - - uint32_t numContacts = 0; - }; -} // namespace SHADE - diff --git a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h b/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h deleted file mode 100644 index 7c9b6a10..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/Constraints/SHVelocityState.h +++ /dev/null @@ -1,63 +0,0 @@ -/**************************************************************************************** - * \file SHVelocityState.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Velocity State for constraint solving. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "Physics/Dynamics/SHRigidBody.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - struct SH_API SHVelocityState - { - public: - SHVec3 LinearVelocity; - SHVec3 AngularVelocity; - - SHVec3 LinearLockFactor; - SHVec3 AngularLockFactor; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHVelocityState (const SHRigidBody* rigidBody) noexcept - : LinearVelocity { SHVec3::Zero } - , AngularVelocity { SHVec3::Zero } - , LinearLockFactor { SHVec3::Zero } - , AngularLockFactor { SHVec3::Zero } - { - if (rigidBody) - { - LinearVelocity = rigidBody->GetLinearVelocity(); - AngularVelocity = rigidBody->GetAngularVelocity(); - - LinearLockFactor = SHVec3 - { - rigidBody->GetFreezePositionX() ? 0.0f : 1.0f - , rigidBody->GetFreezePositionY() ? 0.0f : 1.0f - , rigidBody->GetFreezePositionZ() ? 0.0f : 1.0f - }; - - AngularLockFactor = SHVec3 - { - rigidBody->GetFreezeRotationX() ? 0.0f : 1.0f - , rigidBody->GetFreezeRotationY() ? 0.0f : 1.0f - , rigidBody->GetFreezeRotationZ() ? 0.0f : 1.0f - }; - } - } - }; - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp deleted file mode 100644 index 7434953e..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/**************************************************************************************** - * \file SHContactManager.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Contact Manager that stores collision information and - * resolves contact constraints. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHContactManager.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Physics/Collision/Narrowphase/SHCollisionDispatch.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHContactManager::TriggerEvents& SHContactManager::GetTriggerEvents() const noexcept - { - static TriggerEvents triggerEvents; - - triggerEvents.clear(); - - for (auto& [id, trigger] : triggers) - triggerEvents.emplace_back(SHTriggerEvent{ id, trigger.state }); - - return triggerEvents; - } - - const SHContactManager::CollisionEvents& SHContactManager::GetCollisionEvents() const noexcept - { - static CollisionEvents collisionEvents; - - collisionEvents.clear(); - - for (auto& [id, manifold] : manifolds) - { - SHCollisionEvent collisionEvent - { - .info = id - , .state = manifold.state - , .normal = manifold.normal - }; - - for (uint32_t i = 0; i < manifold.numContacts; ++i) - collisionEvent.contactPoints[i] = manifold.contacts[i].position; - - collisionEvents.emplace_back(collisionEvent); - } - - return collisionEvents; - } - - const SHContactManager::ContactPoints& SHContactManager::GetContactPoints() const noexcept - { - static ContactPoints contactPoints; - contactPoints.clear(); - - for (auto& manifold : manifolds | std::views::values) - { - // Skip exit state manifolds - if (manifold.state == SHCollisionState::EXIT) - continue; - - for (uint32_t i = 0; i < manifold.numContacts; ++i) - { - const ContactInfo INFO - { - .position = manifold.contacts[i].position - , .normal = manifold.normal - }; - - contactPoints.emplace_back(INFO); - } - } - - return contactPoints; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHContactManager::AddTrigger(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept - { - // If id not found, emplace new trigger. - auto trigger = triggers.find(key); - if (trigger == triggers.end()) - triggers.emplace(key, Trigger{ A, B, SHCollisionState::INVALID }); - } - - void SHContactManager::AddManifold(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept - { - // If id not found, emplace new manifold - auto manifold = manifolds.find(key); - if (manifold == manifolds.end()) - manifolds.emplace(key, SHManifold{ A, B }); - } - - void SHContactManager::RemoveInvalidatedTrigger(EntityID eid) noexcept - { - removeInvalidObject(triggers, eid); - } - - void SHContactManager::RemoveInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) noexcept - { - removeInvalidObject(triggers, eid, shapeIndex); - } - - void SHContactManager::RemoveInvalidatedManifold(EntityID eid) noexcept - { - removeInvalidObject(manifolds, eid); - } - - void SHContactManager::RemoveInvalidatedManifold(EntityID eid, uint32_t shapeIndex) noexcept - { - removeInvalidObject(manifolds, eid, shapeIndex); - } - - void SHContactManager::Update() noexcept - { - // Clear expired or invalid collisions. If not, test collision. - for (auto manifoldPair = manifolds.begin(); manifoldPair != manifolds.end();) - { - // Test collision of every manifold. - SHManifold& manifold = manifoldPair->second; - SHManifold oldManifold = manifold; - - const bool IS_COLLIDING = SHCollisionDispatcher::Collide(manifold, *manifold.shapeA, *manifold.shapeB); - - auto& collisionState = manifold.state; - updateCollisionState(IS_COLLIDING, collisionState); - - const bool IS_INVALID = collisionState == SHCollisionState::INVALID; - if (IS_INVALID) - { - manifoldPair = manifolds.erase(manifoldPair); - continue; - } - - updateManifold(manifold, oldManifold); - ++manifoldPair; - } - - // Clear expired or invalid triggers, If not, test collision. - for (auto triggerPair = triggers.begin(); triggerPair != triggers.end();) - { - // Test collision of every trigger. - Trigger& oldTrigger = triggerPair->second; - Trigger newTrigger = oldTrigger; - - const bool IS_COLLIDING = SHCollisionDispatcher::Collide(*newTrigger.A, *newTrigger.B); - - auto& collisionState = newTrigger.state; - updateCollisionState(IS_COLLIDING, collisionState); - - const bool IS_INVALID = collisionState == SHCollisionState::INVALID; - if (IS_INVALID) - triggerPair = triggers.erase(triggerPair); - else - ++triggerPair; - } - } - - void SHContactManager::SolveCollisions(int numIterations, float dt) noexcept - { - static constexpr int SIZE_CONTACTS = sizeof(SHContact) * SHManifold::MAX_NUM_CONTACTS; - - // Build constraints - for (auto& [key, manifold] : manifolds) - contactSolver.AddContact(key, manifold); - - // Solve contacts - contactSolver.SolveContacts(numIterations, dt); - - // Map impulses back to manifolds - const auto& CONTACT_CONSTRAINTS = contactSolver.GetContantConstraints(); - const auto& VELOCITY_STATES = contactSolver.GetVelocities(); - - for (auto& [key, contactConstraint] : CONTACT_CONSTRAINTS) - { - SHManifold& manifold = manifolds.find(key)->second; - - for (uint32_t i = 0; i < contactConstraint.numContacts; ++i) - manifold.contacts[i] = contactConstraint.contacts[i]; - - // Assign velocities back to the bodies - SHRigidBody* bodyA = manifold.bodyA; - SHRigidBody* bodyB = manifold.bodyB; - - const auto& STATE_A = VELOCITY_STATES.find(key.GetEntityA())->second; - const auto& STATE_B = VELOCITY_STATES.find(key.GetEntityB())->second; - - if (bodyA) - { - bodyA->SetLinearVelocity(STATE_A.LinearVelocity); - bodyA->SetAngularVelocity(STATE_A.AngularVelocity); - } - - if (bodyB) - { - bodyB->SetLinearVelocity(STATE_B.LinearVelocity); - bodyB->SetAngularVelocity(STATE_B.AngularVelocity); - } - } - - contactSolver.Reset(); - } - - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHContactManager::updateCollisionState(bool isColliding, SHCollisionState& state) noexcept - { - if (isColliding) - { - // New states start at invalid. In the first frame of collision, move to enter. - // If it already in enter, move to stay - state = state == SHCollisionState::INVALID ? SHCollisionState::ENTER : SHCollisionState::STAY; - } - else - { - // If already exited and still not colliding, the collision has expired. - // Invalid states are removed in the next frame - if (state == SHCollisionState::EXIT) - state = SHCollisionState::INVALID; - - // If previously colliding, move to exit. - if (state == SHCollisionState::ENTER || state == SHCollisionState::STAY) - state = SHCollisionState::EXIT; - } - } - - void SHContactManager::updateManifold(SHManifold& manifold, const SHManifold& oldManifold) noexcept - { - static const float SQRT_ONE_THIRD = std::sqrtf(1.0f / 3.0f); - - // Early out since exiting a collision does not require an update beyond updating the state - if (manifold.state == SHCollisionState::EXIT) - return; - - const SHVec3& NORMAL = manifold.normal; - SHVec3& tangent0 = manifold.tangents[0]; - SHVec3& tangent1 = manifold.tangents[1]; - - const SHVec3& OLD_TANGENT_0 = oldManifold.tangents[0]; - const SHVec3& OLD_TANGENT_1 = oldManifold.tangents[1]; - - // Compute tangents - if (std::fabs(NORMAL.x) >= SQRT_ONE_THIRD) - tangent0 = SHVec3{ NORMAL.y, -NORMAL.x, 0.0f }; - else - tangent0 = SHVec3{ 0.0f, NORMAL.z, -NORMAL.y }; - - tangent0 = SHVec3::Normalise(tangent0); - tangent1 = SHVec3::Cross(NORMAL, tangent0); - - // Accumulate impulses - for (uint32_t i = 0; i < manifold.numContacts; ++i) - { - SHContact& contact = manifold.contacts[i]; - - // Reset impulses - contact.normalImpulse = 0.0f; - contact.tangentImpulse[0] = contact.tangentImpulse[1] = 0.0f; - - for (uint32_t j = 0; j < oldManifold.numContacts; ++j) - { - const SHContact& OLD_CONTACT = oldManifold.contacts[j]; - - if (OLD_CONTACT.featurePair.key == contact.featurePair.key) - { - // If contact features persists, re-project old solution - contact.normalImpulse = OLD_CONTACT.normalImpulse; - - const SHVec3 FRICTION_FORCE = OLD_TANGENT_0 * OLD_CONTACT.tangentImpulse[0] + OLD_TANGENT_1 * OLD_CONTACT.tangentImpulse[1]; - contact.tangentImpulse[0] = SHVec3::Dot(FRICTION_FORCE, tangent0); - contact.tangentImpulse[1] = SHVec3::Dot(FRICTION_FORCE, tangent1); - - break; - } - } - } - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h deleted file mode 100644 index 81f37146..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.h +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************************** - * \file SHContactManager.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Contact Manager that stores collision information. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "Physics/Collision/Contacts/SHCollisionEvents.h" -#include "Physics/Collision/Contacts/SHCollisionKey.h" -#include "Physics/Collision/Contacts/SHManifold.h" -#include "SHContactSolver.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a class that stores collision information. - */ - class SH_API SHContactManager - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHPhysicsWorld; - - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - struct ContactInfo - { - SHVec3 position; - SHVec3 normal; - }; - - using TriggerEvents = std::vector; - using CollisionEvents = std::vector; - using ContactPoints = std::vector; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHContactManager () noexcept = default; - ~SHContactManager () noexcept = default; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - const TriggerEvents& GetTriggerEvents () const noexcept; - const CollisionEvents& GetCollisionEvents () const noexcept; - const ContactPoints& GetContactPoints () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - void AddTrigger (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept; - void AddManifold (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept; - - void RemoveInvalidatedTrigger (EntityID eid) noexcept; - void RemoveInvalidatedTrigger (EntityID eid, uint32_t shapeIndex) noexcept; - void RemoveInvalidatedManifold (EntityID eid) noexcept; - void RemoveInvalidatedManifold (EntityID eid, uint32_t shapeIndex) noexcept; - - /** - * @brief - * Removes any invalidated contacts and triggers, then performs narrowphase collision - * detection on existing triggers and manifolds. - */ - void Update () noexcept; - - /** - * @brief - * Builds contact constraints and solves them. Results are stored in the corresponding - * manifolds abiding by the sequential impulse method. - */ - void SolveCollisions (int numIterations, float dt) noexcept; - - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - struct Trigger - { - SHCollisionShape* A = nullptr; - SHCollisionShape* B = nullptr; - - SHCollisionState state = SHCollisionState::INVALID; - }; - - using Manifolds = std::unordered_map; - using Triggers = std::unordered_map; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - Manifolds manifolds; - Triggers triggers; - - SHContactSolver contactSolver; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - static void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept; - static void updateManifold (SHManifold& manifold, const SHManifold& oldManifold) noexcept; - - // Removal Helpers - - template - static void removeInvalidObject (std::unordered_map& container, EntityID eid) noexcept; - template - static void removeInvalidObject (std::unordered_map& container, EntityID eid, uint32_t shapeIndex) noexcept; - - }; - - -} // namespace SHADE - -#include "SHContactManager.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp b/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp deleted file mode 100644 index 04a4b234..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactManager.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************************** - * \file SHContactManager.hpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for templated methods of the Contact Manager. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Primary Header -#include "SHContactManager.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Private Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - template - void SHContactManager::removeInvalidObject(std::unordered_map& container, EntityID eid) noexcept - { - if (container.empty()) - return; - - for (auto invalidated = container.begin(); invalidated != container.end();) - { - const auto& ID = invalidated->first; - - const bool MATCHES_A = ID.GetEntityA() == eid; - const bool MATCHES_B = ID.GetEntityB() == eid; - - if (MATCHES_A || MATCHES_B) - invalidated = container.erase(invalidated); - else - ++invalidated; - } - } - - template - void SHContactManager::removeInvalidObject(std::unordered_map& container, EntityID eid, uint32_t shapeIndex) noexcept - { - if (container.empty()) - return; - - for (auto invalidated = container.begin(); invalidated != container.end();) - { - const auto& ID = invalidated->first; - - const bool MATCHES_A = ID.GetEntityA() == eid && ID.GetShapeIndexA() == shapeIndex; - const bool MATCHES_B = ID.GetEntityB() == eid && ID.GetShapeIndexB() == shapeIndex; - - if (MATCHES_A || MATCHES_B) - invalidated = container.erase(invalidated); - else - ++invalidated; - } - } -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp b/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp deleted file mode 100644 index f16d7f1e..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHContactSolver.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/**************************************************************************************** - * \file SHContactSolver.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Contact Solver. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -***************************************************************************************/ - -#include - -// Primary Header -#include "SHContactSolver.h" - -// Project Headers -#include "Math/SHMathHelpers.h" -#include "Physics/SHPhysicsConstants.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHContactSolver::VelocityStates& SHContactSolver::GetVelocities() const noexcept - { - return velocityStates; - } - - const SHContactSolver::ContactConstraints& SHContactSolver::GetContantConstraints() const noexcept - { - return contactConstraints; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHContactSolver::AddContact(const SHCollisionKey& key, const SHManifold& manifold) noexcept - { - SHContactConstraint& newConstraint = contactConstraints.emplace(key, SHContactConstraint{}).first->second; - - const auto* SHAPE_A = manifold.shapeA; - const auto* SHAPE_B = manifold.shapeB; - - const auto* BODY_A = manifold.bodyA; - const auto* BODY_B = manifold.bodyB; - - // Add velocities if it doesn't already exist - velocityStates.emplace(key.GetEntityA(), SHVelocityState{ BODY_A }); - velocityStates.emplace(key.GetEntityB(), SHVelocityState{ BODY_B }); - - // Mix friction & restitution - const float FRICTION_A = SHAPE_A->GetFriction(); - const float RESTITUTION_A = SHAPE_A->GetBounciness(); - - const float FRICTION_B = SHAPE_B->GetFriction(); - const float RESTITUTION_B = SHAPE_B->GetBounciness(); - - newConstraint.friction = std::sqrtf(FRICTION_A * FRICTION_B); - newConstraint.restitution = std::max(RESTITUTION_A, RESTITUTION_B); - - // Mass data - - // Account for implicity-static bodies - if (BODY_A) - { - newConstraint.invMassA = BODY_A->invMass; - newConstraint.centerOfMassA = BODY_A->worldCentroid; - newConstraint.invInertiaA = BODY_A->worldInvInertia; - } - else - { - newConstraint.invMassA = 0.0f; - newConstraint.centerOfMassA = SHVec3::Zero; - - // Matrix needs to maintain its homogenous structure - newConstraint.invInertiaA = SHMatrix::Zero; - newConstraint.invInertiaA.m[3][3] = 1.0f; - } - - if (BODY_B) - { - newConstraint.invMassB = BODY_B->invMass; - newConstraint.centerOfMassB = BODY_B->worldCentroid; - newConstraint.invInertiaB = BODY_B->worldInvInertia; - } - else - { - newConstraint.invMassB = 0.0f; - newConstraint.centerOfMassB = SHVec3::Zero; - - // Matrix needs to maintain its homogenous structure - newConstraint.invInertiaB = SHMatrix::Zero; - newConstraint.invInertiaB.m[3][3] = 1.0f; - } - - // Collision data - - newConstraint.numContacts = manifold.numContacts; - - newConstraint.normal = manifold.normal; - - static constexpr size_t TANGENTS_SIZE = sizeof(SHVec3) * SHContact::NUM_TANGENTS; - static constexpr size_t CONTACTS_SIZE = sizeof(SHContact) * SHManifold::MAX_NUM_CONTACTS; - - memcpy_s(newConstraint.tangents, TANGENTS_SIZE, manifold.tangents, TANGENTS_SIZE); - memcpy_s(newConstraint.contacts, CONTACTS_SIZE, manifold.contacts, CONTACTS_SIZE); - - // Compute rA & rB for contacts - for (uint32_t i = 0; i < newConstraint.numContacts; ++i) - { - newConstraint.contacts[i].rA = newConstraint.contacts[i].position - newConstraint.centerOfMassA; - newConstraint.contacts[i].rB = newConstraint.contacts[i].position - newConstraint.centerOfMassB; - } - } - - void SHContactSolver::Reset() noexcept - { - velocityStates.clear(); - contactConstraints.clear(); - } - - void SHContactSolver::SolveContacts(int numIterations, float dt) noexcept - { - preSolve(dt); - - for (int i = 0; i < numIterations; ++i) - solve(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHContactSolver::preSolve(float dt) noexcept - { - const float INV_DT = 1.0f / dt; - - for (auto& [key, constraint] : contactConstraints) - { - const float INV_MASS_SUM = constraint.invMassA + constraint.invMassB; - - SHVelocityState& velocityStateA = velocityStates.find(key.GetEntityA())->second; - SHVelocityState& velocityStateB = velocityStates.find(key.GetEntityB())->second; - - SHVec3 vA = velocityStateA.LinearVelocity; - SHVec3 wA = velocityStateA.AngularVelocity; - SHVec3 vB = velocityStateB.LinearVelocity; - SHVec3 wB = velocityStateB.AngularVelocity; - - const SHVec3& LINEAR_LOCK_A = velocityStateA.LinearLockFactor; - const SHVec3& ANGULAR_LOCK_A = velocityStateA.AngularLockFactor; - const SHVec3& LINEAR_LOCK_B = velocityStateB.LinearLockFactor; - const SHVec3& ANGULAR_LOCK_B = velocityStateB.AngularLockFactor; - - for (uint32_t i = 0; i < constraint.numContacts; ++i) - { - SHContact& contact = constraint.contacts[i]; - - // Calculate JM-1JT (Effective mass) - /* - * rXnT * I-1 * rXn: - * - * 1. 3x3 * 3x1 = 3x1 - * 2. 1x3 * 3x1 = 1x1 - * - * First do I-1 * rXn - * | ix 0 0 || x | | ix * x | - * | 0 iy 0 || y | = | iy * y | - * | 0 0 iz || z | | iz * z | - * - * Then dot product the result with rXnT - * | ix * x |[ u v w ] - * | iy * y | = [ ix * x * w + iy * y * v + iz * z * w ] - * | iz * z | - * - * Simplified: - * - * rXnT /dot (I-1 * rXn) - */ - - // Effective mass along Normal - const SHVec3 RA_CROSS_N = SHVec3::Cross(contact.rA, constraint.normal); - const SHVec3 RB_CROSS_N = SHVec3::Cross(contact.rB, constraint.normal); - - contact.normalMass = INV_MASS_SUM; - contact.normalMass += SHVec3::Dot(RA_CROSS_N, constraint.invInertiaA * RA_CROSS_N); - contact.normalMass += SHVec3::Dot(RB_CROSS_N, constraint.invInertiaB * RB_CROSS_N); - - // Invert the normal mass (we want the actual mass, not the inverse mass) - contact.normalMass = 1.0f / contact.normalMass; - - // Effective mass along tangents (same steps as above) - - for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) - { - const SHVec3 RA_CROSS_T = SHVec3::Cross(contact.rA, constraint.tangents[j]); - const SHVec3 RB_CROSS_T = SHVec3::Cross(contact.rB, constraint.tangents[j]); - - contact.tangentMass[j] = INV_MASS_SUM; - contact.tangentMass[j] += SHVec3::Dot(RA_CROSS_T, constraint.invInertiaA * RA_CROSS_T); - contact.tangentMass[j] += SHVec3::Dot(RB_CROSS_T, constraint.invInertiaB * RB_CROSS_T); - - contact.tangentMass[j] = 1.0f / contact.tangentMass[j]; - } - - // Calculate bias per contact - /* - * error bias = baumgarte factor / dt * penetration - * restituion bias = restitution * (relative velocity /dot normal) - */ - - const float ERROR_BIAS = -SHPHYSICS_BAUMGARTE * INV_DT * std::max(0.0f, contact.penetration - SHPHYSICS_LINEAR_SLOP); - - // Warm starting - // Compute impulses - SHVec3 impulse = constraint.normal * contact.normalImpulse; - for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) - impulse += constraint.tangents[j] * contact.tangentImpulse[j]; - - // Apply impulses onto velocities - vA -= impulse * constraint.invMassA * LINEAR_LOCK_A; - wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, impulse) * ANGULAR_LOCK_A; - - vB += impulse * constraint.invMassB * LINEAR_LOCK_B; - wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, impulse) * ANGULAR_LOCK_B; - - const SHVec3 RV_A = vA + SHVec3::Cross(wA, contact.rA); - const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB); - const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal); - - const float RESTITUTION_BIAS = std::fabs(RV_N) > SHPHYSICS_RESTITUTION_THRESHOLD ? -constraint.restitution * RV_N : 0.0f; - - contact.bias = ERROR_BIAS + RESTITUTION_BIAS; - } - - velocityStateA.LinearVelocity = vA; - velocityStateA.AngularVelocity = wA; - velocityStateB.LinearVelocity = vB; - velocityStateB.AngularVelocity = wB; - } - } - - void SHContactSolver::solve() noexcept - { - for (auto& [key, constraint] : contactConstraints) - { - SHVelocityState& velocityStateA = velocityStates.find(key.GetEntityA())->second; - SHVelocityState& velocityStateB = velocityStates.find(key.GetEntityB())->second; - - SHVec3 vA = velocityStateA.LinearVelocity; - SHVec3 wA = velocityStateA.AngularVelocity; - SHVec3 vB = velocityStateB.LinearVelocity; - SHVec3 wB = velocityStateB.AngularVelocity; - - const SHVec3& LINEAR_LOCK_A = velocityStateA.LinearLockFactor; - const SHVec3& ANGULAR_LOCK_A = velocityStateA.AngularLockFactor; - const SHVec3& LINEAR_LOCK_B = velocityStateB.LinearLockFactor; - const SHVec3& ANGULAR_LOCK_B = velocityStateB.AngularLockFactor; - - for (uint32_t i = 0; i < constraint.numContacts; ++i) - { - SHContact& contact = constraint.contacts[i]; - - // Compute relative velocity - SHVec3 velocityA = vA + SHVec3::Cross(wA, contact.rA); - SHVec3 velocityB = vB + SHVec3::Cross(wB, contact.rB); - SHVec3 relativeVelocity = velocityB - velocityA; - - // Get scalar of relative velocity along the normal - const float VN = SHVec3::Dot(relativeVelocity, constraint.normal); - - // Compute true normal impulse - const float OLD_NORMAL_IMPULSE = contact.normalImpulse; - - float newNormalImpulse = -(VN + contact.bias) * contact.normalMass; - contact.normalImpulse = std::max(OLD_NORMAL_IMPULSE + newNormalImpulse, 0.0f); - newNormalImpulse = contact.normalImpulse - OLD_NORMAL_IMPULSE; - - const SHVec3 NORMAL_IMPULSE = newNormalImpulse * constraint.normal; - - // Apply impulses - vA -= NORMAL_IMPULSE * constraint.invMassA * LINEAR_LOCK_A; - wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, NORMAL_IMPULSE) * ANGULAR_LOCK_A; - - vB += NORMAL_IMPULSE * constraint.invMassB * LINEAR_LOCK_B; - wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE) * ANGULAR_LOCK_B; - - // Solve normal impulse - // Re-compute relative velocity - velocityA = vA + SHVec3::Cross(wA, contact.rA); - velocityB = vB + SHVec3::Cross(wB, contact.rB); - relativeVelocity = velocityB - velocityA; - - // Solve tangent impulse - for (int j = 0; j < SHContact::NUM_TANGENTS; ++j) - { - // Get scalar of relative velocity along tangent - const float VT = SHVec3::Dot(relativeVelocity, constraint.tangents[j]); - - // Compute true tangent impulse - const float MAX_TANGENT_IMPULSE = constraint.friction * contact.normalImpulse; - const float OLD_TANGENT_IMPULSE = contact.tangentImpulse[j]; - - // We cannot exceed the maximum frictional force (coulumb's law) - // Compute true tangent impulse - float newTangentImpulse = -VT * contact.tangentMass[j]; - contact.tangentImpulse[j] = std::clamp(OLD_TANGENT_IMPULSE + newTangentImpulse, -MAX_TANGENT_IMPULSE, MAX_TANGENT_IMPULSE); - newTangentImpulse = contact.tangentImpulse[j] - OLD_TANGENT_IMPULSE; - - const SHVec3 TANGENT_IMPULSE = newTangentImpulse * constraint.tangents[j]; - - // Apply impulses - vA -= TANGENT_IMPULSE * constraint.invMassA * LINEAR_LOCK_A; - wA -= constraint.invInertiaA * SHVec3::Cross(contact.rA, TANGENT_IMPULSE) * ANGULAR_LOCK_A; - - vB += TANGENT_IMPULSE * constraint.invMassB * LINEAR_LOCK_B; - wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, TANGENT_IMPULSE) * ANGULAR_LOCK_B; - } - } - - velocityStateA.LinearVelocity = vA; - velocityStateA.AngularVelocity = wA; - velocityStateB.LinearVelocity = vB; - velocityStateB.AngularVelocity = wB; - } - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp deleted file mode 100644 index 89c8ad32..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************************** - * \file SHMotionState.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Motion State. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHMotionState.h" - -// Project Headers -#include "Math/SHMathHelpers.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHMotionState::SHMotionState() noexcept - : hasMoved { false } - {} - - SHMotionState::SHMotionState(const SHMotionState& rhs) noexcept - : hasMoved { rhs.hasMoved } - , position { rhs.position } - , prevPosition { rhs.prevPosition } - {} - - SHMotionState::SHMotionState(SHMotionState&& rhs) noexcept - : hasMoved { rhs.hasMoved } - , position { rhs.position } - , prevPosition { rhs.prevPosition } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHMotionState& SHMotionState::operator=(const SHMotionState& rhs) noexcept - { - if (this == &rhs) - return *this; - - hasMoved = rhs.hasMoved; - position = rhs.position; - prevPosition = rhs.prevPosition; - - return *this; - } - - SHMotionState& SHMotionState::operator=(SHMotionState&& rhs) noexcept - { - hasMoved = rhs.hasMoved; - position = rhs.position; - prevPosition = rhs.prevPosition; - - return *this; - } - - SHMotionState::operator bool() const noexcept - { - return hasMoved; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definition */ - /*-----------------------------------------------------------------------------------*/ - - void SHMotionState::ForcePosition(const SHVec3& newPosition) noexcept - { - hasMoved = true; - - prevPosition = newPosition; - position = newPosition; - } - - void SHMotionState::ForceOrientation(const SHQuaternion& newOrientation) noexcept - { - hasMoved = true; - - prevOrientation = newOrientation; - orientation = newOrientation; - } - - - void SHMotionState::IntegratePosition(const SHVec3& velocity, float dt) noexcept - { - // Velocities are 0 when objects are static or sleeping. We do not want to integrate them here. - // This call should never reach here. - - hasMoved = true; - - prevPosition = position; - position += velocity * dt; - } - - void SHMotionState::IntegrateOrientation(const SHVec3& velocity, float dt) noexcept - { - // Velocities are 0 when objects are static or sleeping. We do not want to integrate them here. - // This call should never reach here. - - hasMoved = true; - - prevOrientation = orientation; - - orientation += orientation * SHQuaternion{ velocity.x, velocity.y, velocity.z, 0.0f } * dt * 0.5f; - orientation = SHQuaternion::Normalise(orientation); - } - - - SHVec3 SHMotionState::InterpolatePositions(float factor) const noexcept - { - return SHVec3::ClampedLerp(prevPosition, position, factor); - } - - SHQuaternion SHMotionState::InterpolateOrientations(float factor) const noexcept - { - return SHQuaternion::ClampedSlerp(prevOrientation, orientation, factor); - } - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h b/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h deleted file mode 100644 index 778e6a8b..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHMotionState.h +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************************************** - * \file SHMotionState.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Motion State. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "Math/SHQuaternion.h" -#include "Math/Vector/SHVec3.h" - - -namespace SHADE -{ - /*-------------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-------------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates the motion state of a rigid body in physics. - */ - struct SH_API SHMotionState - { - public: - /*-----------------------------------------------------------------------------------*/ - /* Data Members */ - /*-----------------------------------------------------------------------------------*/ - - bool hasMoved; - - SHVec3 position; - SHVec3 prevPosition; - - SHQuaternion orientation; - SHQuaternion prevOrientation; - - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-----------------------------------------------------------------------------------*/ - - SHMotionState () noexcept; - SHMotionState (const SHMotionState& rhs) noexcept; - SHMotionState (SHMotionState&& rhs) noexcept; - - ~SHMotionState() = default; - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*-----------------------------------------------------------------------------------*/ - - SHMotionState& operator= (const SHMotionState& rhs) noexcept; - SHMotionState& operator= (SHMotionState&& rhs) noexcept; - - operator bool () const noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Function Members */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Forcefully sets the position. Meant to be used when transform overrides the rigid body - * positions. - * @param newPosition - * The new position to set. - */ - void ForcePosition (const SHVec3& newPosition) noexcept; - - /** - * @brief - * Forcefully sets the orientation. Meant to be used when transform overrides the rigid body - * orientations - * @param newOrientation - * The new orientation to set. - */ - void ForceOrientation (const SHQuaternion& newOrientation) noexcept; - - /** - * @brief - * Integrates the positions using linear velocity with respect to time. - * @param velocity - * The linear velocity to integrate. - * @param dt - * The delta time to integrate with respect to. - */ - void IntegratePosition (const SHVec3& velocity, float dt) noexcept; - - /** - * @brief - * Integrates the orientation using angular velocity with respect to time. - * @param velocity - * The angular velocity to integrate. - * @param dt - * The delta time to integrate with respect to. - */ - void IntegrateOrientation (const SHVec3& velocity, float dt) noexcept; - - /** - * @brief - * Interpolates the position between the previous and the last using a given factor. - * @param factor - * The factor to interpolate by. Should be between 0 & 1. - * @returns - * The interpolated position meant for rendering. - */ - SHVec3 InterpolatePositions (float factor) const noexcept; - - /** - * @brief - * Interpolates the orientation between the previous and the last using a given factor. - * @param factor - * The factor to interpolate by. Should be between 0 & 1. - * @returns - * The interpolated orientation meant for rendering. - */ - SHQuaternion InterpolateOrientations (float factor) const noexcept; - - }; - - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp deleted file mode 100644 index ed00e80e..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsWorld.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Physics World. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHPhysicsWorld.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsWorld::SHPhysicsWorld(const WorldSettings& worldSettings) noexcept - : settings { worldSettings } - , collisionSpace { nullptr } - { - SHLOG_INFO_D("Creating Physics World") - } - - SHPhysicsWorld::~SHPhysicsWorld() noexcept - { - collisionSpace = nullptr; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - const SHContactManager::ContactPoints& SHPhysicsWorld::GetContactPoints() const noexcept - { - return contactManager.GetContactPoints(); - } - - const SHContactManager::TriggerEvents& SHPhysicsWorld::GetTriggerEvents() const noexcept - { - return contactManager.GetTriggerEvents(); - } - - const SHContactManager::CollisionEvents& SHPhysicsWorld::GetCollisionEvents() const noexcept - { - return contactManager.GetCollisionEvents(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsWorld::SetCollisionSpace(SHCollisionSpace* _collisionSpace) noexcept - { - collisionSpace = _collisionSpace; - _collisionSpace->SetContactManager(&contactManager); - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsWorld::AddRigidBody(SHRigidBody* rigidBody) noexcept - { - const bool INSERTED = rigidBodies.emplace(rigidBody->entityID, rigidBody).second; - if (!INSERTED) - { - SHLOG_WARNING_D("Attempting to add duplicate rigid body {} to the Physics World!", rigidBody->entityID) - } - } - - void SHPhysicsWorld::RemoveRigidBody(SHRigidBody* rigidBody) noexcept - { - rigidBodies.erase(rigidBody->entityID); - - // Contact manager to remove invalidated contacts - contactManager.RemoveInvalidatedManifold(rigidBody->entityID); - - // TODO: Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep - } - - - void SHPhysicsWorld::Step(float dt) - { - /* - * Detect Collisions - */ - - if (collisionSpace) - collisionSpace->DetectCollisions(); - - /* - * Integrate Forces - */ - - for (auto* rigidBody : rigidBodies | std::views::values) - { - if (!rigidBody->IsActive()) - continue; - - rigidBody->ComputeWorldData(); - integrateForces(*rigidBody, dt); - } - - - /* - * Resolve Contacts - */ - - contactManager.SolveCollisions(settings.numVelocitySolverIterations, dt); - - /* - * Integrate Velocities - */ - - for (auto* rigidBody : rigidBodies | std::views::values) - { - if (!rigidBody->IsActive()) - continue; - - integrateVelocities(*rigidBody, dt); - } - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsWorld::integrateForces(SHRigidBody& rigidBody, float dt) const noexcept - { - if (rigidBody.bodyType != SHRigidBody::Type::DYNAMIC) - return; - - // Integrate forces and gravity into linear velocity - const SHVec3 LINEAR_ACCELERATION = rigidBody.accumulatedForce * rigidBody.invMass; - const SHVec3 GRAVITATIONAL_ACCELERATION = rigidBody.IsGravityEnabled() ? settings.gravity * rigidBody.gravityScale : SHVec3::Zero; - - rigidBody.linearVelocity += (LINEAR_ACCELERATION + GRAVITATIONAL_ACCELERATION) * dt; - - // Integrate torque into angular velocity - rigidBody.angularVelocity += rigidBody.worldInvInertia * rigidBody.accumulatedTorque * dt; - - // Apply drag (exponentially applied) - rigidBody.linearVelocity *= 1.0f / (1.0f + dt * rigidBody.linearDrag); - rigidBody.angularVelocity *= 1.0f / (1.0f + dt * rigidBody.angularDrag); - - rigidBody.constrainLinearVelocities(); - rigidBody.constrainAngularVelocities(); - } - - void SHPhysicsWorld::integrateVelocities(SHRigidBody& rigidBody, float dt) const noexcept - { - // Always reset movement flag - rigidBody.motionState.hasMoved = false; - - // Set all velocities of static bodies to 0 - if (rigidBody.bodyType == SHRigidBody::Type::STATIC) - { - rigidBody.linearVelocity = SHVec3::Zero; - } - // Dynamic & Kinematic bodies - // Both dynamic and kinematic can sleep when their velocities are under the thresholds. - else if (!rigidBody.IsSleeping()) - { - rigidBody.constrainLinearVelocities(); - rigidBody.constrainAngularVelocities(); - - rigidBody.motionState.IntegratePosition(rigidBody.linearVelocity, dt); - rigidBody.motionState.IntegrateOrientation(rigidBody.angularVelocity, dt); - - // Sync with collider transforms if a collider is present - if (rigidBody.collider) - { - rigidBody.collider->SetPosition(rigidBody.motionState.position); - rigidBody.collider->SetOrientation(rigidBody.motionState.orientation); - - rigidBody.collider->Update(); - } - } - - // Clear all forces - // We clear forces for static bodies as well for redundancy - rigidBody.ClearForces(); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h b/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h deleted file mode 100644 index 9ad525e8..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHPhysicsWorld.h +++ /dev/null @@ -1,145 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsWorld.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Physics World. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "Physics/Collision/SHCollisionSpace.h" -#include "SHContactManager.h" -#include "SHContactSolver.h" -#include "SHRigidBody.h" - - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates the overall simulation of physics. The bulk of dynamics are handled here, - * with the collision detection handled by an attached collision space.
- * A collision space must be created separately and attached with the world. - */ - class SH_API SHPhysicsWorld - { - public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - struct WorldSettings - { - public: - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ - - SHVec3 gravity = SHVec3{ 0.0f, -9.81f, 0.0f }; - uint16_t numVelocitySolverIterations = 10; - uint16_t numPositionSolverIterations = 5; // Unused until PGS is implemented - bool sleepingEnabled = true; - }; - - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsWorld (const WorldSettings& worldSettings = WorldSettings{}) noexcept; - ~SHPhysicsWorld () noexcept; - - SHPhysicsWorld (const SHPhysicsWorld&) = delete; - SHPhysicsWorld (SHPhysicsWorld&&) = delete; - - /*---------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*---------------------------------------------------------------------------------*/ - - SHPhysicsWorld& operator=(const SHPhysicsWorld&) = delete; - SHPhysicsWorld& operator=(SHPhysicsWorld&&) = delete; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - const SHContactManager::ContactPoints& GetContactPoints () const noexcept; - const SHContactManager::TriggerEvents& GetTriggerEvents () const noexcept; - const SHContactManager::CollisionEvents& GetCollisionEvents () const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetCollisionSpace(SHCollisionSpace* collisionSpace) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * Adds a rigid body to the world for it to be simulated using motion dynamics. - * @param rigidBody - * A rigid body to add. Duplicates will be ignored. - */ - void AddRigidBody (SHRigidBody* rigidBody) noexcept; - - /** - * @brief - * Removes a rigid body from the world. It's motion will not be affected unless - * explicitly modified. - * @param rigidBody - * A rigid body to add. Duplicates will be ignored. - */ - void RemoveRigidBody (SHRigidBody* rigidBody) noexcept; - - /** - * @brief - * Performs a single simulation step.
- * Detect Collisions -> Integrate Forces -> Resolve Contacts -> Integrate Velocities - * @param dt - * A discrete time step for the simulation. This should be consistent for deterministic - * behaviour. - */ - void Step (float dt); - - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - // EntityIDs are used to map resolved constraints back to bodies - using RigidBodies = std::unordered_map; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - WorldSettings settings; - SHCollisionSpace* collisionSpace; - - RigidBodies rigidBodies; - SHContactManager contactManager; - SHContactSolver contactSolver; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - // TODO: Move to island when islands are set up - void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept; - void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept; - }; - - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp deleted file mode 100644 index bdaaf230..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.cpp +++ /dev/null @@ -1,765 +0,0 @@ -/**************************************************************************************** - * \file SHRigidBody.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Rigid Body. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHRigidBody.h" - -// Project Headers -#include -#include - -#include "Physics/Collision/SHCollider.h" -#include "Tools/Logger/SHLogger.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHRigidBody::SHRigidBody(EntityID eid, Type type) noexcept - : entityID { eid } - , collider { nullptr } - , bodyType { type } - , gravityScale { 1.0f } - , invMass { type == Type::DYNAMIC ? 1.0f : 0.0f } - , linearDrag { 0.01f } - , angularDrag { 0.01f } - , flags { 0U } - { - // Set default flags - flags |= 1U << 0; // Body is active - flags |= 1U << 2; // Sleeping is enabled - flags |= 1U << 3; // Gravity is enabled - - // TODO: Compute inertia if body is dynamic - } - - SHRigidBody::SHRigidBody(const SHRigidBody& rhs) noexcept - : entityID { rhs.entityID } - , collider { nullptr } - , bodyType { rhs.bodyType } - , gravityScale { rhs.gravityScale } - , invMass { rhs.invMass } - , linearDrag { rhs.linearDrag } - , angularDrag { rhs.angularDrag } - , localInvInertia { rhs.localInvInertia } - , worldInvInertia { rhs.worldInvInertia } - , localCentroid { rhs.localCentroid } - , worldCentroid { rhs.worldCentroid } - , flags { rhs.flags } - , motionState { rhs.motionState } - { - // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. - } - - SHRigidBody::SHRigidBody(SHRigidBody&& rhs) noexcept - : entityID { rhs.entityID } - , collider { nullptr } - , bodyType { rhs.bodyType } - , gravityScale { rhs.gravityScale } - , invMass { rhs.invMass } - , linearDrag { rhs.linearDrag } - , angularDrag { rhs.angularDrag } - , localInvInertia { rhs.localInvInertia } - , worldInvInertia { rhs.worldInvInertia } - , localCentroid { rhs.localCentroid } - , worldCentroid { rhs.worldCentroid } - , flags { rhs.flags } - , motionState { std::move(rhs.motionState) } - { - // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHRigidBody& SHRigidBody::operator=(const SHRigidBody& rhs) noexcept - { - // Handle self assignment - if (this == &rhs) - return *this; - - entityID = rhs.entityID; - - // Deep copy the collider - *collider = *rhs.collider; - bodyType = rhs.bodyType; - gravityScale = rhs.gravityScale; - invMass = rhs.invMass; - linearDrag = rhs.linearDrag; - angularDrag = rhs.angularDrag; - localInvInertia = rhs.localInvInertia; - worldInvInertia = rhs.worldInvInertia; - localCentroid = rhs.localCentroid; - worldCentroid = rhs.worldCentroid; - flags = rhs.flags; - motionState = rhs.motionState; - - // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. - accumulatedForce = SHVec3::Zero; - accumulatedTorque = SHVec3::Zero; - linearVelocity = SHVec3::Zero; - angularVelocity = SHVec3::Zero; - - return *this; - } - - SHRigidBody& SHRigidBody::operator=(SHRigidBody&& rhs) noexcept - { - entityID = rhs.entityID; - - // Deep copy the collider - *collider = *rhs.collider; - bodyType = rhs.bodyType; - gravityScale = rhs.gravityScale; - invMass = rhs.invMass; - linearDrag = rhs.linearDrag; - angularDrag = rhs.angularDrag; - localInvInertia = rhs.localInvInertia; - worldInvInertia = rhs.worldInvInertia; - localCentroid = rhs.localCentroid; - worldCentroid = rhs.worldCentroid; - flags = rhs.flags; - motionState = std::move(rhs.motionState); - - // All other properties are defaulted to 0 to not carry over any potential errors / invalid values. - accumulatedForce = SHVec3::Zero; - accumulatedTorque = SHVec3::Zero; - linearVelocity = SHVec3::Zero; - angularVelocity = SHVec3::Zero; - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHRigidBody::Type SHRigidBody::GetType() const noexcept - { - return bodyType; - } - - float SHRigidBody::GetGravityScale() const noexcept - { - return gravityScale; - } - - float SHRigidBody::GetMass() const noexcept - { - return 1.0f/ invMass; - } - - float SHRigidBody::GetLinearDrag() const noexcept - { - return linearDrag; - } - - float SHRigidBody::GetAngularDrag() const noexcept - { - return angularDrag; - } - - const SHMatrix& SHRigidBody::GetLocalInvInertia() const noexcept - { - return localInvInertia; - } - - const SHMatrix& SHRigidBody::GetWorldInvInertia() const noexcept - { - return worldInvInertia; - } - - const SHVec3& SHRigidBody::GetLocalCentroid() const noexcept - { - return localCentroid; - } - - const SHVec3& SHRigidBody::GetWorldCentroid() const noexcept - { - return worldCentroid; - } - - const SHVec3& SHRigidBody::GetForce() const noexcept - { - return accumulatedForce; - } - - const SHVec3& SHRigidBody::GetTorque() const noexcept - { - return accumulatedTorque; - } - - const SHVec3& SHRigidBody::GetLinearVelocity() const noexcept - { - // Check if linear velocity needs to be constrained - - return linearVelocity; - } - - const SHVec3& SHRigidBody::GetAngularVelocity() const noexcept - { - return angularVelocity; - } - - // Flags - - bool SHRigidBody::IsActive() const noexcept - { - static constexpr unsigned int FLAG_POS = 0; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::IsSleeping() const noexcept - { - static constexpr unsigned int FLAG_POS = 1; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::IsSleepingEnabled() const noexcept - { - static constexpr unsigned int FLAG_POS = 2; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::IsGravityEnabled() const noexcept - { - static constexpr unsigned int FLAG_POS = 3; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::IsAutoMassEnabled() const noexcept - { - static constexpr unsigned int FLAG_POS = 4; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::IsTriggerInMassData() const noexcept - { - static constexpr unsigned int FLAG_POS = 6; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::GetFreezePositionX() const noexcept - { - static constexpr unsigned int FLAG_POS = 10; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::GetFreezePositionY() const noexcept - { - static constexpr unsigned int FLAG_POS = 11; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::GetFreezePositionZ() const noexcept - { - static constexpr unsigned int FLAG_POS = 12; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::GetFreezeRotationX() const noexcept - { - static constexpr unsigned int FLAG_POS = 13; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::GetFreezeRotationY() const noexcept - { - static constexpr unsigned int FLAG_POS = 14; - return flags & (1U << FLAG_POS); - } - - bool SHRigidBody::GetFreezeRotationZ() const noexcept - { - static constexpr unsigned int FLAG_POS = 15; - return flags & (1U << FLAG_POS); - } - - SHMotionState& SHRigidBody::GetMotionState() noexcept - { - return motionState; - } - - /*-----------------------------------------------------------------------------------*/ - /* Setter Functions Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHRigidBody::SetCollider(SHCollider* c) noexcept - { - collider = c; - } - - void SHRigidBody::SetType(Type newType) noexcept - { - if (newType == bodyType) - return; - - bodyType = newType; - - if (bodyType != Type::DYNAMIC) - { - invMass = 0.0f; - localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = 0.0f; - worldInvInertia.m[0][0] = worldInvInertia.m[1][1] = worldInvInertia.m[2][2] = 0.0f; - } - else - { - invMass = 1.0f; - localInvInertia = SHMatrix::Identity; - } - } - - void SHRigidBody::SetGravityScale(float newGravityScale) noexcept - { - gravityScale = newGravityScale; - } - - void SHRigidBody::SetMass(float newMass) noexcept - { - if (bodyType != Type::DYNAMIC) - { - invMass = 0.0f; - SHLOG_WARNING("Cannot set mass of a non-Dynamic Body {}", entityID) - return; - } - - if (newMass < 0.0f) - { - SHLOG_WARNING("Cannot set mass below 0. Object {}'s mass will remain unchanged.", entityID) - return; - } - - invMass = 1.0f / newMass; - - // Turn off automass - static constexpr unsigned int AUTO_MASS_FLAG = 4; - static constexpr uint16_t VALUE = 1U << AUTO_MASS_FLAG; - - flags &= ~(VALUE); - ComputeMassData(); - } - - void SHRigidBody::SetLinearDrag(float newLinearDrag) noexcept - { - if (bodyType == Type::STATIC) - { - SHLOG_WARNING("Cannot set linear drag of a Static Body {}", entityID) - return; - } - - if (newLinearDrag < 0.0f) - { - SHLOG_WARNING("Cannot set drag below 0. Object {}'s linear drag will remain unchanged.", entityID) - return; - } - - linearDrag = newLinearDrag; - } - - void SHRigidBody::SetAngularDrag(float newAngularDrag) noexcept - { - if (bodyType == Type::STATIC) - { - SHLOG_WARNING("Cannot set angular drag of a Static Body {}", entityID) - return; - } - - if (newAngularDrag < 0.0f) - { - SHLOG_WARNING("Cannot set drag below 0. Object {}'s angular drag will remain unchanged.", entityID) - return; - } - - angularDrag = newAngularDrag; - } - - void SHRigidBody::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept - { - if (bodyType == Type::STATIC) - { - SHLOG_WARNING("Cannot set linear velocity of a Static Body {}", entityID) - return; - } - - linearVelocity = newLinearVelocity; - constrainLinearVelocities(); - } - - void SHRigidBody::SetAngularVelocity(const SHVec3& newAngularVelocity) noexcept - { - if (bodyType == Type::STATIC) - { - SHLOG_WARNING("Cannot set angular velocity of a Static Body {}", entityID) - return; - } - - angularVelocity = newAngularVelocity; - constrainAngularVelocities(); - } - - void SHRigidBody::SetIsActive(bool isActive) noexcept - { - static constexpr unsigned int FLAG_POS = 0; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - isActive ? flags |= VALUE : flags &= ~VALUE; - } - - void SHRigidBody::SetIsSleeping(bool isSleeping) noexcept - { - static constexpr unsigned int FLAG_POS = 1; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (isSleeping) - { - flags |= VALUE; - - ClearForces(); - linearVelocity = SHVec3::Zero; - angularVelocity = SHVec3::Zero; - } - else - { - flags &= ~VALUE; - } - } - - void SHRigidBody::SetSleepingEnabled(bool enableSleeping) noexcept - { - static constexpr unsigned int FLAG_POS = 2; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (enableSleeping) - { - flags |= VALUE; - } - else - { - flags &= ~VALUE; - // Wake the body - SetIsSleeping(false); - } - } - - void SHRigidBody::SetGravityEnabled(bool enableGravity) noexcept - { - static constexpr unsigned int FLAG_POS = 3; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - enableGravity ? flags |= VALUE : flags &= ~VALUE; - } - - void SHRigidBody::SetAutoMassEnabled(bool enableAutoMass) noexcept - { - static constexpr unsigned int FLAG_POS = 4; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (enableAutoMass) - { - flags |= VALUE; - } - else - { - flags &= ~VALUE; - // Use default mass of 1 - invMass = 1.0f; - } - - ComputeMassData(); - } - - void SHRigidBody::SetTriggerInMassData(bool triggerInMassData) noexcept - { - static constexpr unsigned int FLAG_POS = 6; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - triggerInMassData ? flags |= VALUE : flags &= ~VALUE; - } - - void SHRigidBody::SetFreezePositionX(bool freezePositionX) noexcept - { - static constexpr unsigned int FLAG_POS = 10; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezePositionX) - { - flags |= VALUE; - // Reset linear velocity along X-axis - linearVelocity.x = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - void SHRigidBody::SetFreezePositionY(bool freezePositionY) noexcept - { - static constexpr unsigned int FLAG_POS = 11; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezePositionY) - { - flags |= VALUE; - // Reset linear velocity along Y-axis - linearVelocity.y = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - void SHRigidBody::SetFreezePositionZ(bool freezePositionZ) noexcept - { - static constexpr unsigned int FLAG_POS = 12; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezePositionZ) - { - flags |= VALUE; - // Reset linear velocity along Z-axis - linearVelocity.z = 0.0f; - } - else - { - flags &= ~VALUE; - } - } - - void SHRigidBody::SetFreezeRotationX(bool freezeRotationX) noexcept - { - static constexpr unsigned int FLAG_POS = 13; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezeRotationX) - { - flags |= VALUE; - // Reset angular velocity along X-axis - angularVelocity.x = 0.0f; - // Set inertia tensor on the x-axis to 0 - localInvInertia.m[0][0] = 0.0f; - } - else - { - flags &= ~VALUE; - ComputeMassData(); - } - } - - void SHRigidBody::SetFreezeRotationY(bool freezeRotationY) noexcept - { - static constexpr unsigned int FLAG_POS = 14; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezeRotationY) - { - flags |= VALUE; - // Reset angular velocity along Y-axis - angularVelocity.y = 0.0f; - // Set inertia tensor on the y-axis to 0 - localInvInertia.m[1][1] = 0.0f; - } - else - { - flags &= ~VALUE; - ComputeMassData(); - } - } - - void SHRigidBody::SetFreezeRotationZ(bool freezeRotationZ) noexcept - { - static constexpr unsigned int FLAG_POS = 15; - static constexpr uint16_t VALUE = 1U << FLAG_POS; - - if (freezeRotationZ) - { - flags |= VALUE; - // Reset angular velocity along Z-axis - angularVelocity.z = 0.0f; - // Set inertia tensor on the z-axis to 0 - localInvInertia.m[2][2] = 0.0f; - } - else - { - flags &= ~VALUE; - ComputeMassData(); - } - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definition */ - /*-----------------------------------------------------------------------------------*/ - - void SHRigidBody::AddForce(const SHVec3& force, const SHVec3& pos) noexcept - { - if (bodyType != Type::DYNAMIC) - return; - - accumulatedForce += force; - accumulatedTorque += SHVec3::Cross(pos, force); - } - - void SHRigidBody::AddImpulse(const SHVec3& impulse, const SHVec3& pos) noexcept - { - if (bodyType != Type::DYNAMIC) - return; - - linearVelocity += impulse * invMass; - angularVelocity += worldInvInertia * SHVec3::Cross(pos, impulse); - } - - void SHRigidBody::AddTorque(const SHVec3& torque) noexcept - { - if (bodyType != Type::DYNAMIC) - return; - - accumulatedTorque += torque; - } - - void SHRigidBody::ClearForces() noexcept - { - accumulatedForce = SHVec3::Zero; - accumulatedTorque = SHVec3::Zero; - } - - void SHRigidBody::ComputeWorldData() noexcept - { - const SHMatrix R = SHMatrix::Rotate(motionState.orientation); - const SHMatrix RT = SHMatrix::Transpose(R); - - // Compute world centroid - worldCentroid = (R * localCentroid) + motionState.position; - - if (bodyType != Type::DYNAMIC) - return; - - // Compute world inertia - worldInvInertia = R * (localInvInertia * RT); - } - - void SHRigidBody::ComputeMassData() noexcept - { - // Reset centroid - localCentroid = SHVec3::Zero; - localInvInertia = SHMatrix::Identity; - - // In the instance the body is a particle - if (!collider || collider->GetCollisionShapes().empty()) - { - localInvInertia.m[0][0] = localInvInertia.m[1][1] = localInvInertia.m[2][2] = invMass; - return; - } - - const float CUSTOM_MASS = 1.0f / invMass; - const bool AUTO_MASS = IsAutoMassEnabled(); - const bool INCLUDE_TRIGGERS = IsTriggerInMassData(); - - - // Compute Total mass and store individual masses if custom mass is being used. - // Compute local centroid at the same time - - // Zero matrix; - SHMatrix J = SHMatrix::Zero; - - float totalMass = 0.0f; - std::vector trueMass; // We store the true masses here for calculating the ratio with custom masses. - - const auto& SHAPES = collider->GetCollisionShapes(); - for (auto* shape : SHAPES) - { - // We skip triggers by default - if (shape->IsTrigger() && !INCLUDE_TRIGGERS) - continue; - - // p = m/v, therefore m = pv. This is the true mass of the shape. - const float MASS = shape->GetDensity() * shape->GetVolume(); - totalMass += MASS; - - trueMass.emplace_back(MASS); - - // Weighted sum of masses contribute to the centroid's location using the collider's local position. - localCentroid += MASS * shape->GetRelativeCentroid(); - } - - if (totalMass > 0.0f) - { - localCentroid /= totalMass; - - if (AUTO_MASS) - invMass = 1.0f / totalMass; - } - - const SHMatrix R = SHMatrix::Rotate(motionState.orientation); - const SHMatrix RT = SHMatrix::Transpose(R); - - // We need the world centroid to compute the offset of the collider from the body's centroid - worldCentroid = (R * localCentroid) + motionState.position; - - for (size_t i = 0; i < SHAPES.size(); ++i) - { - const auto* SHAPE = SHAPES[i]; - - // We skip triggers by default - if (SHAPE->IsTrigger() && !INCLUDE_TRIGGERS) - continue; - - // If using custom mass, take the ratio of the mass - float actualMass = trueMass[i]; - if (!AUTO_MASS) - actualMass *= CUSTOM_MASS / totalMass; - - // Convert inertia tensor into local-space of the body - // R * I * RT = R * (I * RT) - SHMatrix I = SHAPE->GetInertiaTensor( actualMass ) * RT; - I = R * I; - - // Parallel Axis Theorem - // https://en.wikipedia.org/wiki/Parallel_axis_theorem - // J = I + m((R /dot R)E_3 - R /outerProduct R) - const SHVec3 R = SHAPE->GetWorldCentroid() - worldCentroid; - const float R_MAG2 = R.LengthSquared(); - const SHMatrix R_OX_R = SHVec3::OuterProduct(R, R); - - J += I + actualMass * (SHMatrix::Identity * R_MAG2 - R_OX_R); - } - - // Set diagonals then invert - localInvInertia.m[0][0] = J.m[0][0]; - localInvInertia.m[1][1] = J.m[1][1]; - localInvInertia.m[2][2] = J.m[2][2]; - - localInvInertia = SHMatrix::Inverse(localInvInertia); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Function Definition */ - /*-----------------------------------------------------------------------------------*/ - - void SHRigidBody::constrainLinearVelocities() noexcept - { - linearVelocity.x = GetFreezePositionX() ? 0.0f : linearVelocity.x; - linearVelocity.y = GetFreezePositionY() ? 0.0f : linearVelocity.y; - linearVelocity.z = GetFreezePositionZ() ? 0.0f : linearVelocity.z; - } - - void SHRigidBody::constrainAngularVelocities() noexcept - { - angularVelocity.x = GetFreezeRotationX() ? 0.0f : angularVelocity.x; - angularVelocity.y = GetFreezeRotationY() ? 0.0f : angularVelocity.y; - angularVelocity.z = GetFreezeRotationZ() ? 0.0f : angularVelocity.z; - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h b/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h deleted file mode 100644 index 3c869ad2..00000000 --- a/SHADE_Engine/src/Physics/Dynamics/SHRigidBody.h +++ /dev/null @@ -1,268 +0,0 @@ -/**************************************************************************************** - * \file SHRigidBody.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Rigid Body. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "ECS_Base/Entity/SHEntity.h" -#include "Math/SHMatrix.h" -#include "Math/Vector/SHVec3.h" -#include "SHMotionState.h" - -namespace SHADE -{ - /*-------------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-------------------------------------------------------------------------------------*/ - - class SHCollider; - - /*-------------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-------------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a Rigid Body used in Physics Simulations - */ - class SH_API SHRigidBody - { - private: - /*-----------------------------------------------------------------------------------*/ - /* Friends */ - /*-----------------------------------------------------------------------------------*/ - - friend class SHPhysicsWorld; - friend class SHContactSolver; - - public: - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - enum class Type - { - STATIC // Immovable body with infinite mass - , KINEMATIC // Only movable by setting velocity, unaffected by forces. Has infinite mass. - , DYNAMIC // Affected by forces. - }; - - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-----------------------------------------------------------------------------------*/ - - SHRigidBody (EntityID eid, Type type) noexcept; - SHRigidBody (const SHRigidBody& rhs) noexcept; - SHRigidBody (SHRigidBody&& rhs) noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*-----------------------------------------------------------------------------------*/ - - SHRigidBody& operator= (const SHRigidBody& rhs) noexcept; - SHRigidBody& operator= (SHRigidBody&& rhs) noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*-----------------------------------------------------------------------------------*/ - - [[nodiscard]] Type GetType () const noexcept; - - [[nodiscard]] float GetGravityScale () const noexcept; - - [[nodiscard]] float GetMass () const noexcept; - [[nodiscard]] float GetLinearDrag () const noexcept; - [[nodiscard]] float GetAngularDrag () const noexcept; - - [[nodiscard]] const SHMatrix& GetLocalInvInertia () const noexcept; - [[nodiscard]] const SHMatrix& GetWorldInvInertia () const noexcept; - - [[nodiscard]] const SHVec3& GetLocalCentroid () const noexcept; - [[nodiscard]] const SHVec3& GetWorldCentroid () const noexcept; - - [[nodiscard]] const SHVec3& GetForce () const noexcept; - [[nodiscard]] const SHVec3& GetTorque () const noexcept; - [[nodiscard]] const SHVec3& GetLinearVelocity () const noexcept; - [[nodiscard]] const SHVec3& GetAngularVelocity () const noexcept; - - // Flags - - [[nodiscard]] bool IsActive () const noexcept; - [[nodiscard]] bool IsSleeping () const noexcept; - [[nodiscard]] bool IsSleepingEnabled () const noexcept; - [[nodiscard]] bool IsGravityEnabled () const noexcept; - [[nodiscard]] bool IsAutoMassEnabled () const noexcept; - [[nodiscard]] bool IsTriggerInMassData () const noexcept; - [[nodiscard]] bool GetFreezePositionX () const noexcept; - [[nodiscard]] bool GetFreezePositionY () const noexcept; - [[nodiscard]] bool GetFreezePositionZ () const noexcept; - [[nodiscard]] bool GetFreezeRotationX () const noexcept; - [[nodiscard]] bool GetFreezeRotationY () const noexcept; - [[nodiscard]] bool GetFreezeRotationZ () const noexcept; - - [[nodiscard]] SHMotionState& GetMotionState () noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*-----------------------------------------------------------------------------------*/ - - void SetCollider (SHCollider* c) noexcept; - - /** - * @brief - * Changing the type from non-Dynamic to Dynamic will set the default - * mass and drag values. - */ - void SetType (Type newType) noexcept; - - void SetGravityScale (float newGravityScale) noexcept; - - /** - * @brief - * Mass is only modifiable for Dynamic bodies. - * @param newMass - * The new mass to set. Values below 0 will be ignored. - */ - void SetMass (float newMass) noexcept; - - /** - * @brief - * Drag is only modifiable for non-Static bodies. - * @param newLinearDrag - * The new drag to set. Values below 0 will be ignored. - */ - void SetLinearDrag (float newLinearDrag) noexcept; - - /** - * @brief - * Drag is only modifiable for non-Static bodies. - * @param newAngularDrag - * The new drag to set. Values below 0 will be ignored. - */ - void SetAngularDrag (float newAngularDrag) noexcept; - - void SetLinearVelocity (const SHVec3& newLinearVelocity) noexcept; - void SetAngularVelocity (const SHVec3& newAngularVelocity)noexcept; - - // Flags - - void SetIsActive (bool isActive) noexcept; - void SetIsSleeping (bool isSleeping) noexcept; - void SetSleepingEnabled (bool enableSleeping) noexcept; - void SetGravityEnabled (bool enableGravity) noexcept; - void SetAutoMassEnabled (bool enableAutoMass) noexcept; - void SetTriggerInMassData(bool triggerInMassData) noexcept; - void SetFreezePositionX (bool freezePositionX) noexcept; - void SetFreezePositionY (bool freezePositionY) noexcept; - void SetFreezePositionZ (bool freezePositionZ) noexcept; - void SetFreezeRotationX (bool freezeRotationX) noexcept; - void SetFreezeRotationY (bool freezeRotationY) noexcept; - void SetFreezeRotationZ (bool freezeRotationZ) noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Member Functions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Adds a force to the body with an offset from it's center of mass.
- * Non-dynamic bodies will be ignored. - * @param force - * The force to add to the body. - * @param pos - * The position from the center of mass to offset the force. Defaults to zero. - */ - void AddForce (const SHVec3& force, const SHVec3& pos = SHVec3::Zero) noexcept; - - /** - * @brief - * Adds an impulse to the body with an offset from it's center of mass.
- * Non-dynamic bodies will be ignored. - * @param impulse - * The impulse to add to the body. - * @param pos - * The position from the center of mass to offset the impulse. Defaults to zero. - */ - void AddImpulse (const SHVec3& impulse, const SHVec3& pos = SHVec3::Zero) noexcept; - - /** - * @brief - * Adds torque to rotate the body about it's centroid.
- * Non-dynamic bodies will be ignored. - * @param torque - * The torque to add to the body. - */ - void AddTorque (const SHVec3& torque) noexcept; - - /** - * @brief - * Removes all the forces from the body. - */ - void ClearForces () noexcept; - - /** - * @brief - * Computes the centroid and invInertia in world space. - */ - void ComputeWorldData () noexcept; - - /** - * @brief - * Computes the centroid and inertia of the object.
- * If auto-mass is enabled, computes the mass.
- * If auto-mass is disabled, the inertia is computed based on the ratio each shape's volume over the total volume. - * - */ - void ComputeMassData () noexcept; - - private: - /*-----------------------------------------------------------------------------------*/ - /* Data Members */ - /*-----------------------------------------------------------------------------------*/ - - // The entityID here is only meant for linking with the actual component in the engine. - EntityID entityID; - SHCollider* collider; - - Type bodyType; - - float gravityScale; - - float invMass; - float linearDrag; - float angularDrag; - - SHMatrix localInvInertia; - SHMatrix worldInvInertia; - - SHVec3 localCentroid; - SHVec3 worldCentroid; - - SHVec3 accumulatedForce; - SHVec3 accumulatedTorque; - - SHVec3 linearVelocity; - SHVec3 angularVelocity; - - // aZ aY aX pZ pY pX 0 0 0 addTriggersToMassData inIsland autoMass enableGravity enableSleeping sleeping active - uint16_t flags; - - SHMotionState motionState; - - /*-----------------------------------------------------------------------------------*/ - /* Member Functions */ - /*-----------------------------------------------------------------------------------*/ - - void constrainLinearVelocities () noexcept; - void constrainAngularVelocities () noexcept; - - }; - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp deleted file mode 100644 index 2353e3f3..00000000 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsObject.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Physics Object. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHPhysicsObject.h" - -#include "Physics/Collision/SHCollider.h" -#include "Physics/Collision/SHCompositeCollider.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructor & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject::SHPhysicsObject(EntityID eid) noexcept - : entityID { eid } - {} - - SHPhysicsObject::SHPhysicsObject(const SHPhysicsObject& rhs) noexcept - : entityID { rhs.entityID } - { - deepCopyComponents(rhs.rigidBody, rhs.collider); - } - - SHPhysicsObject::SHPhysicsObject(SHPhysicsObject&& rhs) noexcept - : entityID { rhs.entityID } - { - deepCopyComponents(rhs.rigidBody, rhs.collider); - } - - SHPhysicsObject::~SHPhysicsObject() noexcept - { - entityID = MAX_EID; - - delete rigidBody; - rigidBody = nullptr; - - delete collider; - collider = nullptr; - } - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overload Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject& SHPhysicsObject::operator=(const SHPhysicsObject& rhs) noexcept - { - if (this == &rhs) - return *this; - - entityID = rhs.entityID; - - deepCopyComponents(rhs.rigidBody, rhs.collider); - - return *this; - } - - SHPhysicsObject& SHPhysicsObject::operator=(SHPhysicsObject&& rhs) noexcept - { - entityID = rhs.entityID; - - deepCopyComponents(rhs.rigidBody, rhs.collider); - - return *this; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - bool SHPhysicsObject::IsEmpty() const noexcept - { - return rigidBody == nullptr && collider == nullptr; - } - - SHRigidBody* SHPhysicsObject::CreateRigidBody(SHRigidBody::Type bodyType) - { - if (rigidBody) - { - SHLOG_INFO_D("Rigid body for Entity {} has already been created!", entityID) - return rigidBody; - } - - rigidBody = new SHRigidBody{ entityID, bodyType }; - - // Link with collider if it exists - if (collider) - { - collider->SetRigidBody(rigidBody); - rigidBody->SetCollider(collider); - } - - rigidBody->ComputeMassData(); - return rigidBody; - } - - void SHPhysicsObject::DestroyRigidBody() noexcept - { - delete rigidBody; - rigidBody = nullptr; - - // Unlink with collider - if (collider) - collider->SetRigidBody(nullptr); - } - - SHCollider* SHPhysicsObject::CreateCompositeCollider(const SHTransform& transform) - { - if (collider) - { - SHLOG_INFO_D("Collider for Entity {} has already been created!", entityID) - return collider; - } - - collider = new SHCompositeCollider{ entityID, transform }; - - // Link with rigidBody if it exists - if (rigidBody) - { - rigidBody->SetCollider(collider); - collider->SetRigidBody(rigidBody); - } - - return collider; - } - - void SHPhysicsObject::DestroyCollider() noexcept - { - delete collider; - collider = nullptr; - - // Unlink with rigid body - if (rigidBody) - { - rigidBody->SetCollider(nullptr); - rigidBody->ComputeMassData(); - } - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsObject::deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCollider* rhsCollider) - { - if (rhsRigidBody) - rigidBody = new SHRigidBody{ *rhsRigidBody }; - - if (rhsCollider) - collider = new SHCollider { *rhsCollider }; - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h deleted file mode 100644 index 9736f8d0..00000000 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObject.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsObject.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Physics Object. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Headers -#include "Physics/Collision/SHCollider.h" -#include "Physics/Dynamics/SHRigidBody.h" - -namespace SHADE -{ - /*-------------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-------------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a rigid body and a collider tied to an Entity. - */ - struct SH_API SHPhysicsObject - { - public: - /*-----------------------------------------------------------------------------------*/ - /* Data Members */ - /*-----------------------------------------------------------------------------------*/ - - EntityID entityID = MAX_EID; - SHRigidBody* rigidBody = nullptr; - SHCollider* collider = nullptr; - - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject (EntityID eid) noexcept; - SHPhysicsObject (const SHPhysicsObject& rhs) noexcept; - SHPhysicsObject (SHPhysicsObject&& rhs) noexcept; - ~SHPhysicsObject () noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Operator Overloads */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject& operator=(const SHPhysicsObject& rhs) noexcept; - SHPhysicsObject& operator=(SHPhysicsObject&& rhs) noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Member Functions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Checks if the physics object has a rigid body or a collider. - * @return - * True if the physics object has neither a rigid body nor a collider. - */ - [[nodiscard]] bool IsEmpty () const noexcept; - - /** - * @brief - * Creates a rigid body for this physics object. - * @param bodyType - * The rigid body's type. Can be modified after creation. - * @return - * Pointer to the rigid body that was created. The memory of this rigid body is managed - * by the physics object itself. - */ - SHRigidBody* CreateRigidBody (SHRigidBody::Type bodyType); - - /** - * @brief - * Destroys the rigid body of this physics object and frees the memory. - */ - void DestroyRigidBody () noexcept; - - /** - * @brief - * Creates a collider for this physics object. - * @param colliderType - * The collider's type. Should not be modified after creation. - * @param transform. - * The world transform of the collider. Defaults to the identity transform. - * @return - * Pointer to the collider that was created. The memory of this collider is managed - * by the physics object itself. - */ - SHCollider* CreateCompositeCollider (const SHTransform& transform = SHTransform::Identity); - - /** - * @brief - * Destroys the collider of this physics object and frees the memory. - */ - void DestroyCollider () noexcept; - - private: - /*-----------------------------------------------------------------------------------*/ - /* Member Functions */ - /*-----------------------------------------------------------------------------------*/ - - void deepCopyComponents(const SHRigidBody* rhsRigidBody, const SHCollider* rhsCollider); - }; -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp deleted file mode 100644 index 956f8078..00000000 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsObjectManager.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Physics Object Manager. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "SHPhysicsObjectManager.h" - -// Project Headers -#include "Math/Transform/SHTransformComponent.h" -#include "Physics/Interface/SHColliderComponent.h" -#include "Physics/Interface/SHRigidBodyComponent.h" -#include "Tools/Utilities/SHUtilities.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObjectManager::~SHPhysicsObjectManager() noexcept - { - RemoveAllObjects(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Getter Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObjectManager::EntityObjectMap& SHPhysicsObjectManager::GetPhysicsObjects() noexcept - { - return physicsObjects; - } - - const SHPhysicsObject* SHPhysicsObjectManager::GetPhysicsObject(EntityID entityID) noexcept - { - const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); - if (PHYSICS_OBJECT_ITERATOR == physicsObjects.end()) - { - SHLOG_ERROR("Cannot find physics object for entity {}!", entityID) - return nullptr; - } - - return &PHYSICS_OBJECT_ITERATOR->second; - } - - /*-----------------------------------------------------------------------------------*/ - /* Public Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsObjectManager::AddRigidBody(EntityID entityID) noexcept - { - SHPhysicsObject* physicsObject = ensurePhysicsObject(entityID); - - // Get the component - auto* rigidBodyComponent = SHComponentManager::GetComponent(entityID); - - // Create a new rigidbody in the physics object - const auto RIGID_BODY_TYPE = static_cast(rigidBodyComponent->GetType()); - auto* rigidBody = physicsObject->CreateRigidBody(RIGID_BODY_TYPE); - - SHVec3 worldPos = SHVec3::Zero; - SHQuaternion worldRot = SHQuaternion::Identity; - - if (const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); TRANSFORM_COMPONENT) - { - worldPos = TRANSFORM_COMPONENT->GetWorldPosition(); - worldRot = TRANSFORM_COMPONENT->GetWorldOrientation(); - } - - SHMotionState& motionState = rigidBody->GetMotionState(); - motionState.ForcePosition(worldPos); - motionState.ForceOrientation(worldRot); - - // Link with the component - rigidBodyComponent->SetRigidBody(rigidBody); - } - - void SHPhysicsObjectManager::RemoveRigidBody(EntityID entityID) noexcept - { - const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); - if (PHYSICS_OBJECT_ITERATOR == physicsObjects.end()) - return; - - SHPhysicsObject* physicsObject = &PHYSICS_OBJECT_ITERATOR->second; - - physicsObject->DestroyRigidBody(); - - // Destroy empty physics objects - if (physicsObject->IsEmpty()) - destroyPhysicsObject(entityID); - } - - void SHPhysicsObjectManager::AddCollider(EntityID entityID, SHCollider::Type type) noexcept - { - SHPhysicsObject* physicsObject = ensurePhysicsObject(entityID); - - // Get the component - auto* colliderComponent = SHComponentManager::GetComponent(entityID); - - SHTransform worldTransform = SHTransform::Identity; - if (const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); TRANSFORM_COMPONENT) - { - worldTransform.position = TRANSFORM_COMPONENT->GetWorldPosition(); - worldTransform.orientation = TRANSFORM_COMPONENT->GetWorldOrientation(); - worldTransform.scale = TRANSFORM_COMPONENT->GetWorldScale(); - - worldTransform.ComputeTRS(); - } - - // Create a new composite collider in the physics object - if (type == SHCollider::Type::COMPOSITE) - physicsObject->CreateCompositeCollider(worldTransform); - - // TODO: Hull collider - - physicsObject->collider->SetLibrary(&shapeLibrary); - - // Link with the component - colliderComponent->SetCollider(dynamic_cast(physicsObject->collider)); - } - - void SHPhysicsObjectManager::RemoveCollider(EntityID entityID) noexcept - { - const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); - if (PHYSICS_OBJECT_ITERATOR != physicsObjects.end()) - { - SHPhysicsObject* physicsObject = &PHYSICS_OBJECT_ITERATOR->second; - - physicsObject->DestroyCollider(); - - // Destroy empty physics objects - if (physicsObject->IsEmpty()) - destroyPhysicsObject(entityID); - } - } - - void SHPhysicsObjectManager::RemoveAllObjects() noexcept - { - // Physics objects itself will delete the object - physicsObjects.clear(); - } - - /*-----------------------------------------------------------------------------------*/ - /* Private Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject* SHPhysicsObjectManager::createPhysicsObject(EntityID entityID) - { - const auto& NEW_OBJECT = physicsObjects.emplace(entityID, SHPhysicsObject{entityID}).first; - return &NEW_OBJECT->second; - } - - SHPhysicsObject* SHPhysicsObjectManager::ensurePhysicsObject(EntityID entityID) - { - const auto PHYSICS_OBJECT_ITERATOR = physicsObjects.find(entityID); - - SHPhysicsObject* physicsObject = PHYSICS_OBJECT_ITERATOR == physicsObjects.end() - ? createPhysicsObject(entityID) - : &PHYSICS_OBJECT_ITERATOR->second; - - return physicsObject; - } - - - void SHPhysicsObjectManager::destroyPhysicsObject(EntityID entityID) - { - physicsObjects.erase(entityID); - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h deleted file mode 100644 index 8b4e79c1..00000000 --- a/SHADE_Engine/src/Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsObjectManager.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Physics Object Manager. - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -#include - -// Project Headers -#include "SHPhysicsObject.h" -#include "Physics/Collision/Shapes/SHCollisionShapeLibrary.h" - -namespace SHADE -{ - /*-------------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-------------------------------------------------------------------------------------*/ - - /** - * @brief - * Encapsulates a manager for physics objects that links raw physics components with the - * engine's components. - */ - class SH_API SHPhysicsObjectManager - { - private: - /*-----------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------------*/ - - using EntityObjectMap = std::unordered_map; - - public: - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObjectManager () noexcept = default; - ~SHPhysicsObjectManager () noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*-----------------------------------------------------------------------------------*/ - - [[nodiscard]] EntityObjectMap& GetPhysicsObjects () noexcept; - [[nodiscard]] const SHPhysicsObject* GetPhysicsObject (EntityID entityID) noexcept; - - /*-----------------------------------------------------------------------------------*/ - /* Member Functions */ - /*-----------------------------------------------------------------------------------*/ - - /** - * @brief - * Creates a rigid body and links it with the rigid body component. - * @param entityID - * The entity to link the new rigid body to. - */ - void AddRigidBody (EntityID entityID) noexcept; - - /** - * @brief - * Destroys a rigid body and removes the link with the rigid body component. - * @param entityID - * The entity to destroy the rigid body of. - */ - void RemoveRigidBody (EntityID entityID) noexcept; - - /** - * @brief - * Creates a composite collider and links it with the collider component. - * @param entityID - * The entity to link the new collider to. - */ - void AddCollider (EntityID entityID, SHCollider::Type type) noexcept; - - /** - * @brief - * Destroys a composite collider and removes the link with the collider component. - * @param entityID - * The entity to destroy the collider of. - */ - void RemoveCollider (EntityID entityID) noexcept; - - /** - * @brief - * Removes all physics object in the manager. This is only meant to be called when - * the world is being destroyed or the scene is being changed. - */ - void RemoveAllObjects () noexcept; - - private: - /*-----------------------------------------------------------------------------------*/ - /* Data Members */ - /*-----------------------------------------------------------------------------------*/ - - EntityObjectMap physicsObjects; - SHCollisionShapeLibrary shapeLibrary; - - /*-----------------------------------------------------------------------------------*/ - /* Member Functions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsObject* createPhysicsObject (EntityID entityID); - SHPhysicsObject* ensurePhysicsObject (EntityID entityID); - void destroyPhysicsObject (EntityID entityID); - - - }; - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp index a36fb730..d7db2c64 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.cpp @@ -13,6 +13,11 @@ // Primary Header #include "SHColliderComponent.h" +// Project Headers +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Math/SHMathHelpers.h" +#include "Physics/System/SHPhysicsSystem.h" + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -20,58 +25,185 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHColliderComponent::SHColliderComponent() noexcept - : collider { nullptr } + : system { nullptr } {} /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCompositeCollider* const SHColliderComponent::GetCollider() const noexcept + + const SHVec3& SHColliderComponent::GetPosition() const noexcept { - return collider; + return position; } - const SHCollider::CollisionShapes* const SHColliderComponent::GetCollisionShapes() const noexcept + const SHQuaternion& SHColliderComponent::GetOrientation() const noexcept { - if (!collider) - return nullptr; - - return &collider->GetCollisionShapes(); + return orientation; } - - SHCollisionShape* const SHColliderComponent::GetCollisionShape(int index) const + SHVec3 SHColliderComponent::GetRotation() const noexcept { - if (!collider) - return nullptr; - - return collider->GetCollisionShape(index); + return orientation.ToEuler(); } - bool SHColliderComponent::GetDebugDrawState() const noexcept + const SHVec3& SHColliderComponent::GetScale() const noexcept { - if (!collider) - return false; - - return collider->GetDebugDrawState(); + return scale; } - + + const SHColliderComponent::CollisionShapes& SHColliderComponent::GetCollisionShapes() const noexcept + { + return collisionShapes; + } + + SHCollisionShape& SHColliderComponent::GetCollisionShape(int index) + { + if (index < 0 || static_cast(index) >= collisionShapes.size()) + throw std::invalid_argument("Out-of-range access!"); + + return collisionShapes[index]; + } + /*-----------------------------------------------------------------------------------*/ - /* Setter Function Definitions */ + /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHColliderComponent::SetCollider(SHCompositeCollider* c) noexcept + void SHColliderComponent::OnCreate() { - collider = c; + auto* physicsSystem = SHSystemManager::GetSystem(); + if (!physicsSystem) + { + SHLOG_ERROR("Physics System does not exist to link with Physics Components!") + return; + } + + system = physicsSystem; + + // Sync with transform if one already exists + if (auto* transformComponent = SHComponentManager::GetComponent_s(GetEID()); transformComponent) + { + position = transformComponent->GetWorldPosition(); + orientation = transformComponent->GetWorldOrientation(); + scale = transformComponent->GetWorldScale(); + } } - void SHColliderComponent::SetDebugDrawState(bool state) noexcept + void SHColliderComponent::OnDestroy() { - if (collider) - collider->SetDebugDrawState(state); + } + void SHColliderComponent::RecomputeCollisionShapes() noexcept + { + for (auto& collisionShape : collisionShapes) + { + switch (collisionShape.GetType()) + { + case SHCollisionShape::Type::BOX: + { + auto* box = reinterpret_cast(collisionShape.shape); + const SHVec3& RELATIVE_EXTENTS = box->GetRelativeExtents(); + + // Recompute world extents based on new scale and fixed relative extents + + const SHVec3 WORLD_EXTENTS = RELATIVE_EXTENTS * (scale * 0.5f); + box->SetWorldExtents(WORLD_EXTENTS); + + continue; + } + case SHCollisionShape::Type::SPHERE: + { + auto* sphere = reinterpret_cast(collisionShape.shape); + const float RELATIVE_RADIUS = sphere->GetRelativeRadius(); + + // Recompute world radius based on new scale and fixed radius + + const float MAX_SCALE = SHMath::Max({ scale.x, scale.y, scale.z }); + const float WORLD_RADIUS = RELATIVE_RADIUS * MAX_SCALE * 0.5f; + sphere->SetWorldRadius(WORLD_RADIUS); + + continue; + } + default: continue; + } + } + } + + int SHColliderComponent::AddBoundingBox(const SHVec3& halfExtents, const SHVec3& posOffset, const SHVec3& rotOffset) noexcept + { + if (!system) + { + SHLOG_ERROR("Physics system does not exist, unable to add Box Collider!") + return -1; + } + + static constexpr auto TYPE = SHCollisionShape::Type::BOX; + + auto& collider = collisionShapes.emplace_back(SHCollisionShape{ GetEID(), TYPE }); + + collider.entityID = GetEID(); + collider.SetPositionOffset(posOffset); + collider.SetRotationOffset(rotOffset); + collider.SetBoundingBox(halfExtents); + + // Notify Physics System + const int NEW_SHAPE_INDEX = static_cast(collisionShapes.size()) - 1; + + system->AddCollisionShape(GetEID(), NEW_SHAPE_INDEX); + return NEW_SHAPE_INDEX; + } + + int SHColliderComponent::AddBoundingSphere(float radius, const SHVec3& posOffset) noexcept + { + if (!system) + { + SHLOG_ERROR("Physics system does not exist, unable to add Sphere Collider!") + return -1; + } + + static constexpr auto TYPE = SHCollisionShape::Type::SPHERE; + + auto& collider = collisionShapes.emplace_back(SHCollisionShape{ GetEID(), TYPE }); + + collider.entityID = GetEID(); + collider.SetPositionOffset(posOffset); + collider.SetBoundingSphere(radius); + + // Notify Physics System + const int NEW_SHAPE_INDEX = static_cast(collisionShapes.size()) - 1; + + system->AddCollisionShape(GetEID(), NEW_SHAPE_INDEX); + return NEW_SHAPE_INDEX; + } + + void SHColliderComponent::RemoveCollider(int index) + { + if (index < 0 || static_cast(index) >= collisionShapes.size()) + throw std::invalid_argument("Out-of-range access!"); + + int idx = 0; + auto it = collisionShapes.begin(); + for (; it != collisionShapes.end(); ++it) + { + if (idx == index) + break; + + ++idx; + } + + it = collisionShapes.erase(it); + + // Notify Physics System + if (!system) + { + SHLOG_ERROR("Physics system does not exist, unable to remove Collider!") + return; + } + + system->RemoveCollisionShape(GetEID(), index); + } } // namespace SHADE @@ -80,6 +212,5 @@ RTTR_REGISTRATION using namespace rttr; using namespace SHADE; - registration::class_("Collider Component") - .property("Is Debug Drawing", &SHColliderComponent::GetDebugDrawState, &SHColliderComponent::SetDebugDrawState); -} \ No newline at end of file + registration::class_("Collider Component"); +} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h index 39552949..0781f3cf 100644 --- a/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHColliderComponent.h @@ -14,7 +14,14 @@ // Project Headers #include "ECS_Base/Components/SHComponent.h" -#include "Physics/Collision/SHCompositeCollider.h" +#include "Math/Geometry/SHBox.h" +#include "Math/Geometry/SHSphere.h" +#include "SHCollisionShape.h" + +//namespace SHADE +//{ +// class SHPhysicsSystem; +//} namespace SHADE { @@ -30,7 +37,13 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ friend class SHPhysicsSystem; - friend struct SHPhysicsObject; + friend class SHPhysicsObject; + + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using CollisionShapes = std::vector; public: @@ -54,30 +67,42 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHCompositeCollider* const GetCollider () const noexcept; - [[nodiscard]] const SHCollider::CollisionShapes* const GetCollisionShapes() const noexcept; - [[nodiscard]] SHCollisionShape* const GetCollisionShape (int index) const; + [[nodiscard]] bool HasChanged () const noexcept; - // Required for serialisation + [[nodiscard]] const SHVec3& GetPosition () const noexcept; + [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; + [[nodiscard]] SHVec3 GetRotation () const noexcept; + [[nodiscard]] const SHVec3& GetScale () const noexcept; - [[nodiscard]] bool GetDebugDrawState () const noexcept; + [[nodiscard]] const CollisionShapes& GetCollisionShapes() const noexcept; + [[nodiscard]] SHCollisionShape& GetCollisionShape (int index); /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ + /* Function Members */ /*---------------------------------------------------------------------------------*/ - void SetCollider (SHCompositeCollider* c) noexcept; + void OnCreate () override; + void OnDestroy () override; - // Required for serialisation + void RecomputeCollisionShapes () noexcept; - void SetDebugDrawState (bool state) noexcept; + void RemoveCollider (int index); + + int AddBoundingBox (const SHVec3& halfExtents = SHVec3::One, const SHVec3& posOffset = SHVec3::Zero, const SHVec3& rotOffset = SHVec3::Zero) noexcept; + int AddBoundingSphere (float radius = 1.0f, const SHVec3& posOffset = SHVec3::Zero) noexcept; private: + /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - SHCompositeCollider* collider; + SHPhysicsSystem* system; + + SHVec3 position; + SHQuaternion orientation; + SHVec3 scale; + CollisionShapes collisionShapes; RTTR_ENABLE() }; diff --git a/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp new file mode 100644 index 00000000..f597077f --- /dev/null +++ b/SHADE_Engine/src/Physics/Interface/SHCollisionShape.cpp @@ -0,0 +1,368 @@ +/**************************************************************************************** + * \file SHCollider.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Collider. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHCollisionShape.h" +// Project Headers +#include "Math/Geometry/SHBox.h" +#include "Math/Geometry/SHSphere.h" +#include "Math/SHMathHelpers.h" +#include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Reflection/SHReflectionMetadata.h" +#include "SHColliderComponent.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionShape::SHCollisionShape(EntityID eid, Type colliderType, const SHPhysicsMaterial& physicsMaterial) + : type { colliderType } + , entityID { eid } + , isTrigger { false } + , dirty { true } + , shape { nullptr } + , material { physicsMaterial } + , collisionTag { SHCollisionTagMatrix::GetTag(0) } + { + switch (type) + { + case Type::BOX: + { + shape = new SHBox{ SHVec3::Zero, SHVec3::One }; + break; + } + case Type::SPHERE: + { + shape = new SHSphere{ SHVec3::Zero, 0.5f }; + break; + } + default: break; + } + } + + SHCollisionShape::SHCollisionShape(const SHCollisionShape& rhs) noexcept + : type { rhs.type} + , entityID { rhs.entityID } + , isTrigger { rhs.isTrigger } + , dirty { true } + , shape { nullptr } + , material { rhs.material } + , positionOffset { rhs.positionOffset } + , rotationOffset { rhs.rotationOffset } + , collisionTag { rhs.collisionTag } + { + CopyShape(rhs.shape); + } + + SHCollisionShape::SHCollisionShape(SHCollisionShape&& rhs) noexcept + : type { rhs.type} + , entityID { rhs.entityID } + , isTrigger { rhs.isTrigger } + , dirty { true } + , shape { nullptr } + , material { rhs.material } + , positionOffset { rhs.positionOffset } + , rotationOffset { rhs.rotationOffset } + , collisionTag { rhs.collisionTag } + { + CopyShape(rhs.shape); + } + + SHCollisionShape::~SHCollisionShape() noexcept + { + shape = nullptr; + } + + /*-----------------------------------------------------------------------------------*/ + /* Operator Overload Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionShape& SHCollisionShape::operator=(const SHCollisionShape& rhs) noexcept + { + if (this == &rhs) + return *this; + + type = rhs.type; + entityID = rhs.entityID; + isTrigger = rhs.isTrigger; + dirty = true; + material = rhs.material; + positionOffset = rhs.positionOffset; + rotationOffset = rhs.rotationOffset; + collisionTag = rhs.collisionTag; + + delete shape; + CopyShape(rhs.shape); + + return *this; + } + + SHCollisionShape& SHCollisionShape::operator=(SHCollisionShape&& rhs) noexcept + { + type = rhs.type; + entityID = rhs.entityID; + isTrigger = rhs.isTrigger; + dirty = true; + material = rhs.material; + positionOffset = rhs.positionOffset; + rotationOffset = rhs.rotationOffset; + collisionTag = rhs.collisionTag; + + delete shape; + CopyShape(rhs.shape); + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHCollisionShape::HasChanged() const noexcept + { + return dirty; + } + + bool SHCollisionShape::IsTrigger() const noexcept + { + return isTrigger; + } + + SHCollisionShape::Type SHCollisionShape::GetType() const noexcept + { + return type; + } + + const SHCollisionTag& SHCollisionShape::GetCollisionTag() const noexcept + { + return *collisionTag; + } + + float SHCollisionShape::GetFriction() const noexcept + { + return material.GetFriction(); + } + + float SHCollisionShape::GetBounciness() const noexcept + { + return material.GetBounciness(); + } + + float SHCollisionShape::GetDensity() const noexcept + { + return material.GetDensity(); + } + + const SHPhysicsMaterial& SHCollisionShape::GetMaterial() const noexcept + { + return material; + } + + const SHVec3& SHCollisionShape::GetPositionOffset() const noexcept + { + return positionOffset; + } + + const SHVec3& SHCollisionShape::GetRotationOffset() const noexcept + { + return rotationOffset; + } + + const SHShape* SHCollisionShape::GetShape() const noexcept + { + return shape; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionShape::SetBoundingBox(const SHVec3& halfExtents) + { + dirty = true; + + const auto* COLLIDER = SHComponentManager::GetComponent(entityID); + auto* box = reinterpret_cast(shape); + + SHVec3 correctedHalfExtents = halfExtents; + + // Get current relative halfExtents for error checking. 0 is ignored + const SHVec3& CURRENT_RELATIVE_EXTENTS = box->GetRelativeExtents(); + for (size_t i = 0; i < SHVec3::SIZE; ++i) + { + if (SHMath::CompareFloat(halfExtents[i], 0.0f)) + correctedHalfExtents[i] = CURRENT_RELATIVE_EXTENTS[i]; + } + + // Set the half extents relative to world scale + const SHVec3 WORLD_EXTENTS = correctedHalfExtents * COLLIDER->GetScale() * 0.5f; + + if (type != Type::BOX) + { + type = Type::BOX; + + delete shape; + shape = new SHBox{ positionOffset, WORLD_EXTENTS }; + } + + box->SetWorldExtents(WORLD_EXTENTS); + box->SetRelativeExtents(correctedHalfExtents); + } + + void SHCollisionShape::SetBoundingSphere(float radius) + { + dirty = true; + + auto* sphere = reinterpret_cast(shape); + const auto* COLLIDER = SHComponentManager::GetComponent(entityID); + + // Get current relative halfExtents for error checking. 0 is ignored + const float CURRENT_RELATIVE_RADIUS = sphere->GetRelativeRadius(); + if (SHMath::CompareFloat(radius, 0.0f)) + radius = CURRENT_RELATIVE_RADIUS; + + // Set the radius relative to world scale + const SHVec3 WORLD_SCALE = COLLIDER->GetScale(); + const float MAX_SCALE = SHMath::Max({ WORLD_SCALE.x, WORLD_SCALE.y, WORLD_SCALE.z }); + const float WORLD_RADIUS = radius * MAX_SCALE * 0.5f; + + if (type != Type::SPHERE) + { + type = Type::SPHERE; + + delete shape; + shape = new SHSphere{ positionOffset, WORLD_RADIUS }; + } + + sphere->SetWorldRadius(WORLD_RADIUS); + sphere->SetRelativeRadius(radius); + } + + void SHCollisionShape::SetIsTrigger(bool trigger) noexcept + { + dirty = true; + isTrigger = trigger; + } + + void SHCollisionShape::SetCollisionTag(SHCollisionTag* newCollisionTag) noexcept + { + dirty = true; + collisionTag = newCollisionTag; + } + + void SHCollisionShape::SetFriction(float friction) noexcept + { + dirty = true; + material.SetFriction(friction); + } + + void SHCollisionShape::SetBounciness(float bounciness) noexcept + { + dirty = true; + material.SetBounciness(bounciness); + } + + void SHCollisionShape::SetDensity(float density) noexcept + { + dirty = true; + material.SetDensity(density); + } + + void SHCollisionShape::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept + { + dirty = true; + material = newMaterial; + } + + void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept + { + dirty = true; + positionOffset = posOffset; + + switch (type) + { + case Type::BOX: + { + reinterpret_cast(shape)->SetCenter(positionOffset); + break; + } + case Type::SPHERE: + { + reinterpret_cast(shape)->SetCenter(positionOffset); + break; + } + default: break; + } + } + + void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept + { + dirty = true; + rotationOffset = rotOffset; + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHCollisionShape::CopyShape(const SHShape* rhs) + { + switch (type) + { + case Type::BOX: + { + const auto* RHS_BOX = reinterpret_cast(rhs); + + shape = new SHBox{ positionOffset, RHS_BOX->GetWorldExtents() }; + auto* lhsBox = reinterpret_cast(shape); + lhsBox->SetRelativeExtents(RHS_BOX->GetRelativeExtents()); + + break; + } + case Type::SPHERE: + { + const auto* RHS_SPHERE = reinterpret_cast(rhs); + + shape = new SHSphere{ positionOffset, RHS_SPHERE->GetWorldRadius() }; + auto* lhsSphere = reinterpret_cast(shape); + lhsSphere->SetRelativeRadius(RHS_SPHERE->GetRelativeRadius()); + + break; + } + default: break; + } + } + +} // namespace SHADE + +RTTR_REGISTRATION +{ + using namespace SHADE; + using namespace rttr; + + registration::enumeration("Collider Type") + ( + value("Box", SHCollisionShape::Type::BOX), + value("Sphere", SHCollisionShape::Type::SPHERE) + // TODO(Diren): Add More Shapes + ); + + registration::class_("Collider") + .property("IsTrigger" , &SHCollisionShape::IsTrigger , &SHCollisionShape::SetIsTrigger ) + .property("Friction" , &SHCollisionShape::GetFriction , &SHCollisionShape::SetFriction ) + .property("Bounciness" , &SHCollisionShape::GetBounciness , &SHCollisionShape::SetBounciness ) + .property("Density" , &SHCollisionShape::GetDensity , &SHCollisionShape::SetDensity ) + .property("Position Offset" , &SHCollisionShape::GetPositionOffset, &SHCollisionShape::SetPositionOffset) + .property("Rotation Offset" , &SHCollisionShape::GetRotationOffset, &SHCollisionShape::SetRotationOffset) (metadata(META::angleInRad, true)); +} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHCollisionShape.h b/SHADE_Engine/src/Physics/Interface/SHCollisionShape.h new file mode 100644 index 00000000..597814a6 --- /dev/null +++ b/SHADE_Engine/src/Physics/Interface/SHCollisionShape.h @@ -0,0 +1,134 @@ +/**************************************************************************************** + * \file SHCollider.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Collider. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include + +// Project Headers +#include "ECS_Base/Entity/SHEntity.h" +#include "Math/Geometry/SHShape.h" +#include "Math/SHQuaternion.h" +#include "SHPhysicsMaterial.h" +#include "Physics/Collision/SHCollisionTags.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + class SH_API SHCollisionShape + { + private: + + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHColliderComponent; + friend class SHPhysicsObject; + + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + enum class Type + { + BOX + , SPHERE + , CAPSULE + }; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionShape (EntityID eid, Type colliderType = Type::BOX, const SHPhysicsMaterial& physicsMaterial = SHPhysicsMaterial::DEFAULT); + + SHCollisionShape (const SHCollisionShape& rhs) noexcept; + SHCollisionShape (SHCollisionShape&& rhs) noexcept; + ~SHCollisionShape () noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionShape& operator=(const SHCollisionShape& rhs) noexcept; + SHCollisionShape& operator=(SHCollisionShape&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] bool HasChanged () const noexcept; + + [[nodiscard]] bool IsTrigger () const noexcept; + + [[nodiscard]] Type GetType () const noexcept; + + [[nodiscard]] const SHCollisionTag& GetCollisionTag () const noexcept; + + [[nodiscard]] float GetFriction () const noexcept; + [[nodiscard]] float GetBounciness () const noexcept; + [[nodiscard]] float GetDensity () const noexcept; + [[nodiscard]] const SHPhysicsMaterial& GetMaterial () const noexcept; + + [[nodiscard]] const SHVec3& GetPositionOffset () const noexcept; + [[nodiscard]] const SHVec3& GetRotationOffset () const noexcept; + + [[nodiscard]] const SHShape* GetShape () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetBoundingBox (const SHVec3& halfExtents); + void SetBoundingSphere (float radius); + + void SetIsTrigger (bool isTrigger) noexcept; + void SetCollisionTag (SHCollisionTag* newCollisionTag) noexcept; + void SetFriction (float friction) noexcept; + void SetBounciness (float bounciness) noexcept; + void SetDensity (float density) noexcept; + void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; + + void SetPositionOffset (const SHVec3& posOffset) noexcept; + void SetRotationOffset (const SHVec3& rotOffset) noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + Type type; + EntityID entityID; // The entity this collider belongs to + bool isTrigger; + bool dirty; + + SHShape* shape; + SHPhysicsMaterial material; + + SHVec3 positionOffset; + SHVec3 rotationOffset; + + SHCollisionTag* collisionTag; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + void CopyShape(const SHShape* rhs); + + RTTR_ENABLE() + }; + +} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.cpp b/SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.cpp similarity index 100% rename from SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.cpp rename to SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.cpp diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h b/SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.h similarity index 98% rename from SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h rename to SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.h index 773ac4c1..b3db1655 100644 --- a/SHADE_Engine/src/Physics/Collision/SHPhysicsMaterial.h +++ b/SHADE_Engine/src/Physics/Interface/SHPhysicsMaterial.h @@ -19,10 +19,6 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates the data of a physics material for physics simulations. - */ class SH_API SHPhysicsMaterial { public: diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp index 04bb5b6b..330c3abe 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.cpp @@ -10,9 +10,17 @@ #include +// External Dependencies +#include + // Primary Header #include "SHRigidBodyComponent.h" +// Project Headers +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Math/SHMathHelpers.h" +#include "Physics/System/SHPhysicsSystem.h" + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -21,318 +29,382 @@ namespace SHADE SHRigidBodyComponent::SHRigidBodyComponent() noexcept : type { Type::DYNAMIC } - , interpolate { true } - , rigidBody { nullptr } - {} + , flags { 0 } + , dirtyFlags { std::numeric_limits::max() } + , mass { 1.0f } + , drag { 0.01f } + , angularDrag { 0.01f } + , system { nullptr } + { + // Initialise default flags + flags |= 1U << 0; // Gravity set to true + flags |= 1U << 1; // Sleeping allowed + flags |= 1U << 8; // Interpolate by default + } /*-----------------------------------------------------------------------------------*/ - /* Getter Functions Definitions */ + /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ + bool SHRigidBodyComponent::IsGravityEnabled() const noexcept + { + static constexpr int FLAG_POS = 0; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBodyComponent::IsAllowedToSleep() const noexcept + { + static constexpr int FLAG_POS = 1; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBodyComponent::IsInterpolating() const noexcept + { + static constexpr int FLAG_POS = 8; + return flags & (1U << FLAG_POS); + } + + bool SHRigidBodyComponent::GetIsSleeping() const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + return physicsObject->GetRigidBody()->isSleeping(); + + return false; + } + SHRigidBodyComponent::Type SHRigidBodyComponent::GetType() const noexcept { return type; } - bool SHRigidBodyComponent::IsGravityEnabled() const noexcept - { - return rigidBody ? rigidBody->IsGravityEnabled() : false; - } - - bool SHRigidBodyComponent::IsAllowedToSleep() const noexcept - { - return rigidBody ? rigidBody->IsSleepingEnabled() : false; - } - - bool SHRigidBodyComponent::IsInterpolating() const noexcept - { - return interpolate; - } - - bool SHRigidBodyComponent::IsSleeping() const noexcept - { - return rigidBody ? rigidBody->IsSleeping() : false; - } - - bool SHRigidBodyComponent::GetAutoMass() const noexcept - { - return rigidBody ? rigidBody->IsAutoMassEnabled() : false; - } - bool SHRigidBodyComponent::GetFreezePositionX() const noexcept { - return rigidBody ? rigidBody->GetFreezePositionX() : false; + static constexpr int FLAG_POS = 2; + return flags & (1U << FLAG_POS); } bool SHRigidBodyComponent::GetFreezePositionY() const noexcept { - return rigidBody ? rigidBody->GetFreezePositionY() : false; + static constexpr int FLAG_POS = 3; + return flags & (1U << FLAG_POS); } bool SHRigidBodyComponent::GetFreezePositionZ() const noexcept { - return rigidBody ? rigidBody->GetFreezePositionZ() : false; + static constexpr int FLAG_POS = 4; + return flags & (1U << FLAG_POS); } bool SHRigidBodyComponent::GetFreezeRotationX() const noexcept { - return rigidBody ? rigidBody->GetFreezeRotationX() : false; + static constexpr int FLAG_POS = 5; + return flags & (1U << FLAG_POS); } bool SHRigidBodyComponent::GetFreezeRotationY() const noexcept { - return rigidBody ? rigidBody->GetFreezeRotationY() : false; + static constexpr int FLAG_POS = 6; + return flags & (1U << FLAG_POS); } bool SHRigidBodyComponent::GetFreezeRotationZ() const noexcept { - return rigidBody ? rigidBody->GetFreezeRotationZ() : false; + static constexpr int FLAG_POS = 7; + return flags & (1U << FLAG_POS); } - float SHRigidBodyComponent::GetGravityScale() const noexcept - { - return rigidBody ? rigidBody->GetGravityScale() : 0.0f; - } + //bool SHRigidBodyComponent::GetAutoMass() const noexcept + //{ + // static constexpr int FLAG_POS = 9; + // return flags & (1U << FLAG_POS); + //} - float SHRigidBodyComponent::GetMass() const noexcept + float SHRigidBodyComponent::GetMass() const noexcept { - return rigidBody ? rigidBody->GetMass() : -1.0f; + return mass; } float SHRigidBodyComponent::GetDrag() const noexcept { - return rigidBody ? rigidBody->GetLinearDrag() : -1.0f; + return drag; } float SHRigidBodyComponent::GetAngularDrag() const noexcept { - return rigidBody ? rigidBody->GetAngularDrag() : -1.0f; + return angularDrag; } SHVec3 SHRigidBodyComponent::GetForce() const noexcept { - return rigidBody ? rigidBody->GetForce() : SHVec3::Zero; + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + return physicsObject->GetRigidBody()->getForce(); + + return SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetTorque() const noexcept { - return rigidBody ? rigidBody->GetTorque() : SHVec3::Zero; + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + return physicsObject->GetRigidBody()->getTorque(); + + return SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetLinearVelocity() const noexcept { - return rigidBody ? rigidBody->GetLinearVelocity() : SHVec3::Zero; + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + return physicsObject->GetRigidBody()->getLinearVelocity(); + + return SHVec3::Zero; } SHVec3 SHRigidBodyComponent::GetAngularVelocity() const noexcept { - return rigidBody ? rigidBody->GetAngularVelocity() : SHVec3::Zero; + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + return physicsObject->GetRigidBody()->getAngularVelocity(); + + return SHVec3::Zero; } - SHVec3 SHRigidBodyComponent::GetPosition() const noexcept + const SHVec3& SHRigidBodyComponent::GetPosition() const noexcept { - return rigidBody ? rigidBody->GetMotionState().position : SHVec3::Zero; + return position; + } + + const SHQuaternion& SHRigidBodyComponent::GetOrientation() const noexcept + { + return orientation; } SHVec3 SHRigidBodyComponent::GetRotation() const noexcept { - return rigidBody ? rigidBody->GetMotionState().orientation.ToEuler() : SHVec3::Zero; + return orientation.ToEuler(); } /*-----------------------------------------------------------------------------------*/ - /* Setter Functions Definitions */ + /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ void SHRigidBodyComponent::SetType(Type newType) noexcept { - if (newType == type) + static constexpr int FLAG_POS = 8; + + if (type == newType) return; type = newType; - - if (rigidBody) - rigidBody->SetType(static_cast(newType)); + dirtyFlags |= 1U << FLAG_POS; } - void SHRigidBodyComponent::SetRigidBody(SHRigidBody* rb) noexcept + void SHRigidBodyComponent::SetGravityEnabled(bool enableGravity) noexcept { - rigidBody = rb; - } + static constexpr int FLAG_POS = 0; - void SHRigidBodyComponent::SetIsGravityEnabled(bool enableGravity) noexcept - { - if (rigidBody) - rigidBody->SetGravityEnabled(enableGravity); + if (type != Type::DYNAMIC) + { + SHLOG_WARNING("Cannot enable gravity of a non-dynamic object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + enableGravity ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } void SHRigidBodyComponent::SetIsAllowedToSleep(bool isAllowedToSleep) noexcept { - if (rigidBody) - rigidBody->SetSleepingEnabled(isAllowedToSleep); - } + static constexpr int FLAG_POS = 1; - void SHRigidBodyComponent::SetAutoMass(bool autoMass) noexcept - { - if (rigidBody) - rigidBody->SetAutoMassEnabled(autoMass); + if (type != Type::DYNAMIC) + { + SHLOG_WARNING("Cannot enable sleeping of a non-dynamic object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + isAllowedToSleep ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } void SHRigidBodyComponent::SetFreezePositionX(bool freezePositionX) noexcept { - if (rigidBody) - rigidBody->SetFreezePositionX(freezePositionX); + static constexpr int FLAG_POS = 2; + + if (type == Type::STATIC) + { + SHLOG_WARNING("Cannot set linear constraints of a static object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + freezePositionX ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } void SHRigidBodyComponent::SetFreezePositionY(bool freezePositionY) noexcept { - if (rigidBody) - rigidBody->SetFreezePositionY(freezePositionY); + static constexpr int FLAG_POS = 3; + + if (type == Type::STATIC) + { + SHLOG_WARNING("Cannot set linear constraints of a static object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + freezePositionY ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } void SHRigidBodyComponent::SetFreezePositionZ(bool freezePositionZ) noexcept { - if (rigidBody) - rigidBody->SetFreezePositionZ(freezePositionZ); + static constexpr int FLAG_POS = 4; + + if (type == Type::STATIC) + { + SHLOG_WARNING("Cannot set linear constraints of a static object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + freezePositionZ ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } void SHRigidBodyComponent::SetFreezeRotationX(bool freezeRotationX) noexcept { - if (rigidBody) - rigidBody->SetFreezeRotationX(freezeRotationX); + static constexpr int FLAG_POS = 5; + + if (type == Type::STATIC) + { + SHLOG_WARNING("Cannot set angular constraints of a static object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + freezeRotationX ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } void SHRigidBodyComponent::SetFreezeRotationY(bool freezeRotationY) noexcept { - if (rigidBody) - rigidBody->SetFreezeRotationY(freezeRotationY); + static constexpr int FLAG_POS = 6; + + if (type == Type::STATIC) + { + SHLOG_WARNING("Cannot set angular constraints of a static object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + freezeRotationY ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } void SHRigidBodyComponent::SetFreezeRotationZ(bool freezeRotationZ) noexcept { - if (rigidBody) - rigidBody->SetFreezeRotationZ(freezeRotationZ); + static constexpr int FLAG_POS = 7; + + if (type == Type::STATIC) + { + SHLOG_WARNING("Cannot set angular constraints of a static object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + freezeRotationZ ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } void SHRigidBodyComponent::SetInterpolate(bool allowInterpolation) noexcept { - interpolate = allowInterpolation; + static constexpr int FLAG_POS = 8; + allowInterpolation ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); } - void SHRigidBodyComponent::SetGravityScale(float gravityScale) noexcept - { - if (rigidBody) - rigidBody->SetGravityScale(gravityScale); - } + //void SHRigidBodyComponent::SetAutoMass(bool autoMass) noexcept + //{ + // static constexpr int FLAG_POS = 9; + // autoMass ? flags |= 1U << FLAG_POS : flags &= ~(1U << FLAG_POS); - void SHRigidBodyComponent::SetMass(float newMass) noexcept - { - if (rigidBody) - rigidBody->SetMass(newMass); - } + // dirtyFlags |= 1U << FLAG_POS; + //} + + //void SHRigidBodyComponent::SetMass(float newMass) noexcept + //{ + // static constexpr int FLAG_POS = 9; + + // if (newMass < 0.0f) + // return; + + // if (type != Type::DYNAMIC) + // { + // SHLOG_WARNING("Cannot set mass of a non-dynamic object {}", GetEID()) + // return; + // } + + // dirtyFlags |= 1U << FLAG_POS; + // mass = newMass; + + // // Turn off automass + // flags &= ~(1U << FLAG_POS); + //} void SHRigidBodyComponent::SetDrag(float newDrag) noexcept { - if (rigidBody) - rigidBody->SetLinearDrag(newDrag); + static constexpr int FLAG_POS = 10; + + if (type != Type::DYNAMIC) + { + SHLOG_WARNING("Cannot set drag of a non-dynamic object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + drag = newDrag; } void SHRigidBodyComponent::SetAngularDrag(float newAngularDrag) noexcept { - if (rigidBody) - rigidBody->SetAngularDrag(newAngularDrag); + static constexpr int FLAG_POS = 11; + + if (type != Type::DYNAMIC) + { + SHLOG_WARNING("Cannot set angular drag of a non-dynamic object {}", GetEID()) + return; + } + + dirtyFlags |= 1U << FLAG_POS; + angularDrag = newAngularDrag; } void SHRigidBodyComponent::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept { - if (rigidBody) - rigidBody->SetLinearVelocity(newLinearVelocity); + + if (type == Type::STATIC) + { + SHLOG_WARNING("Cannot set linear velocity of a static object {}", GetEID()) + return; + } + + auto* physicsObject = system->GetPhysicsObject(GetEID()); + if (!physicsObject) + { + SHLOGV_ERROR("Unable to retrieve physics object of Entity {}", GetEID()) + return; + } + physicsObject->GetRigidBody()->setLinearVelocity(newLinearVelocity); } void SHRigidBodyComponent::SetAngularVelocity(const SHVec3& newAngularVelocity) noexcept { - if (rigidBody) - rigidBody->SetAngularVelocity(newAngularVelocity); - } + static constexpr int FLAG_POS = 13; - /*-----------------------------------------------------------------------------------*/ - /* Member Function Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHRigidBodyComponent::AddForce(const SHVec3& force) const noexcept - { - if (rigidBody) - rigidBody->AddForce(force); - } - - void SHRigidBodyComponent::AddForceAtLocalPos(const SHVec3& force, const SHVec3& localPos) const noexcept - { - if (rigidBody) - rigidBody->AddForce(force, localPos); - } - - void SHRigidBodyComponent::AddForceAtWorldPos(const SHVec3& force, const SHVec3& worldPos) const noexcept - { - if (rigidBody) + if (type == Type::STATIC) { - // Convert world pos into local space of the body - const SHVec3 LOCAL_POS = worldPos - rigidBody->GetWorldCentroid(); - rigidBody->AddForce(force, LOCAL_POS); + SHLOG_WARNING("Cannot set angular velocity of a static object {}", GetEID()) + return; } - } - void SHRigidBodyComponent::AddRelativeForce(const SHVec3& relativeForce) const noexcept - { - if (rigidBody) + auto* physicsObject = system->GetPhysicsObject(GetEID()); + if (!physicsObject) { - // Rotate force into world space - const SHVec3 FORCE = SHVec3::Rotate(relativeForce, rigidBody->GetMotionState().orientation); - rigidBody->AddForce(FORCE); + SHLOGV_ERROR("Unable to retrieve physics object of Entity {}", GetEID()) + return; } - } - - void SHRigidBodyComponent::AddRelativeForceAtLocalPos(const SHVec3& relativeForce, const SHVec3& localPos) const noexcept - { - if (rigidBody) - { - // Rotate force into world space - const SHVec3 FORCE = SHVec3::Rotate(relativeForce, rigidBody->GetMotionState().orientation); - rigidBody->AddForce(FORCE, localPos); - } - } - - void SHRigidBodyComponent::AddRelativeForceAtWorldPos(const SHVec3& relativeForce, const SHVec3& worldPos) const noexcept - { - if (rigidBody) - { - // Rotate force into world space - const SHVec3 FORCE = SHVec3::Rotate(relativeForce, rigidBody->GetMotionState().orientation); - // Convert world pos into local space of the body - const SHVec3 LOCAL_POS = worldPos - rigidBody->GetWorldCentroid(); - - rigidBody->AddForce(FORCE, LOCAL_POS); - } - } - - void SHRigidBodyComponent::AddTorque(const SHVec3& torque) const noexcept - { - if (rigidBody) - rigidBody->AddTorque(torque); - } - - void SHRigidBodyComponent::AddRelativeTorque(const SHVec3& relativeTorque) const noexcept - { - if (rigidBody) - { - // Rotate force into world space - const SHVec3 TORQUE = SHVec3::Rotate(relativeTorque, rigidBody->GetMotionState().orientation); - rigidBody->AddTorque(TORQUE); - } - } - - void SHRigidBodyComponent::ClearForces() const noexcept - { - if (rigidBody) - rigidBody->ClearForces(); + physicsObject->GetRigidBody()->setAngularVelocity(newAngularVelocity); } /*-----------------------------------------------------------------------------------*/ @@ -341,7 +413,81 @@ namespace SHADE void SHRigidBodyComponent::OnCreate() { - + auto* physicsSystem = SHSystemManager::GetSystem(); + if (!physicsSystem) + { + SHLOG_ERROR("Physics System does not exist to link with Physics Components!") + return; + } + + system = physicsSystem; + + // Sync with transform if one already exists + if (auto* transformComponent = SHComponentManager::GetComponent_s(GetEID()); transformComponent) + { + position = transformComponent->GetWorldPosition(); + orientation = transformComponent->GetWorldOrientation(); + } + } + + void SHRigidBodyComponent::AddForce(const SHVec3& force) const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->applyWorldForceAtCenterOfMass(force); + } + + void SHRigidBodyComponent::AddForceAtLocalPos(const SHVec3& force, const SHVec3& localPos) const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->applyWorldForceAtLocalPosition(force, localPos); + } + + void SHRigidBodyComponent::AddForceAtWorldPos(const SHVec3& force, const SHVec3& worldPos) const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->applyWorldForceAtWorldPosition(force, worldPos); + } + + void SHRigidBodyComponent::AddRelativeForce(const SHVec3& relativeForce) const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->applyLocalForceAtCenterOfMass(relativeForce); + } + + void SHRigidBodyComponent::AddRelativeForceAtLocalPos(const SHVec3& relativeForce, const SHVec3& localPos) const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->applyLocalForceAtLocalPosition(relativeForce, localPos); + } + + void SHRigidBodyComponent::AddRelativeForceAtWorldPos(const SHVec3& relativeForce, const SHVec3& worldPos) const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->applyLocalForceAtWorldPosition(relativeForce, worldPos); + } + + void SHRigidBodyComponent::AddTorque(const SHVec3& torque) const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->applyWorldTorque(torque); + } + + void SHRigidBodyComponent::AddRelativeTorque(const SHVec3& relativeTorque) const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->applyLocalTorque(relativeTorque); + } + + void SHRigidBodyComponent::ClearForces() const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->resetForce(); + } + + void SHRigidBodyComponent::ClearTorque() const noexcept + { + if (auto* physicsObject = system->GetPhysicsObject(GetEID()); physicsObject) + physicsObject->GetRigidBody()->resetTorque(); } } // namespace SHADE @@ -359,19 +505,18 @@ RTTR_REGISTRATION ); registration::class_("RigidBody Component") - .property("Type" , &SHRigidBodyComponent::GetType , &SHRigidBodyComponent::SetType ) - .property("Auto Mass" , &SHRigidBodyComponent::GetAutoMass , &SHRigidBodyComponent::SetAutoMass ) - .property("Mass" , &SHRigidBodyComponent::GetMass , &SHRigidBodyComponent::SetMass ) - .property("Drag" , &SHRigidBodyComponent::GetDrag , &SHRigidBodyComponent::SetDrag ) - .property("Angular Drag" , &SHRigidBodyComponent::GetAngularDrag , &SHRigidBodyComponent::SetAngularDrag ) - .property("Use Gravity" , &SHRigidBodyComponent::IsGravityEnabled , &SHRigidBodyComponent::SetIsGravityEnabled ) - .property("Gravity Scale" , &SHRigidBodyComponent::GetGravityScale , &SHRigidBodyComponent::SetGravityScale ) - .property("Interpolate" , &SHRigidBodyComponent::IsInterpolating , &SHRigidBodyComponent::SetInterpolate ) - .property("Sleeping Enabled" , &SHRigidBodyComponent::IsAllowedToSleep , &SHRigidBodyComponent::SetIsAllowedToSleep ) - .property("Freeze Position X" , &SHRigidBodyComponent::GetFreezePositionX , &SHRigidBodyComponent::SetFreezePositionX ) - .property("Freeze Position Y" , &SHRigidBodyComponent::GetFreezePositionY , &SHRigidBodyComponent::SetFreezePositionY ) - .property("Freeze Position Z" , &SHRigidBodyComponent::GetFreezePositionZ , &SHRigidBodyComponent::SetFreezePositionZ ) - .property("Freeze Rotation X" , &SHRigidBodyComponent::GetFreezeRotationX , &SHRigidBodyComponent::SetFreezeRotationX ) - .property("Freeze Rotation Y" , &SHRigidBodyComponent::GetFreezeRotationY , &SHRigidBodyComponent::SetFreezeRotationY ) - .property("Freeze Rotation Z" , &SHRigidBodyComponent::GetFreezeRotationZ , &SHRigidBodyComponent::SetFreezeRotationZ ); + .property("Type" , &SHRigidBodyComponent::GetType , &SHRigidBodyComponent::SetType ) + //.property("Mass" , &SHRigidBodyComponent::GetMass , &SHRigidBodyComponent::SetMass ) + .property("Drag" , &SHRigidBodyComponent::GetDrag , &SHRigidBodyComponent::SetDrag ) + .property("Angular Drag" , &SHRigidBodyComponent::GetAngularDrag , &SHRigidBodyComponent::SetAngularDrag ) + .property("Use Gravity" , &SHRigidBodyComponent::IsGravityEnabled , &SHRigidBodyComponent::SetGravityEnabled ) + .property("Interpolate" , &SHRigidBodyComponent::IsInterpolating , &SHRigidBodyComponent::SetInterpolate ) + .property("Sleeping Enabled" , &SHRigidBodyComponent::IsAllowedToSleep , &SHRigidBodyComponent::SetIsAllowedToSleep) + //.property("Auto Mass" , &SHRigidBodyComponent::GetAutoMass , &SHRigidBodyComponent::SetAutoMass ) + .property("Freeze Position X" , &SHRigidBodyComponent::GetFreezePositionX , &SHRigidBodyComponent::SetFreezePositionX ) + .property("Freeze Position Y" , &SHRigidBodyComponent::GetFreezePositionY , &SHRigidBodyComponent::SetFreezePositionY ) + .property("Freeze Position Z" , &SHRigidBodyComponent::GetFreezePositionZ , &SHRigidBodyComponent::SetFreezePositionZ ) + .property("Freeze Rotation X" , &SHRigidBodyComponent::GetFreezeRotationX , &SHRigidBodyComponent::SetFreezeRotationX ) + .property("Freeze Rotation Y" , &SHRigidBodyComponent::GetFreezeRotationY , &SHRigidBodyComponent::SetFreezeRotationY ) + .property("Freeze Rotation Z" , &SHRigidBodyComponent::GetFreezeRotationZ , &SHRigidBodyComponent::SetFreezeRotationZ ); } \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h index 1173c1d5..532b3312 100644 --- a/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Interface/SHRigidBodyComponent.h @@ -15,7 +15,7 @@ // Project Headers #include "ECS_Base/Components/SHComponent.h" #include "Math/Vector/SHVec3.h" -#include "Physics/Dynamics/SHRigidBody.h" +#include "Math/SHQuaternion.h" namespace SHADE { @@ -31,7 +31,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ friend class SHPhysicsSystem; - friend struct SHPhysicsObject; + friend class SHPhysicsObject; public: /*---------------------------------------------------------------------------------*/ @@ -67,13 +67,13 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] Type GetType () const noexcept; - [[nodiscard]] bool IsGravityEnabled () const noexcept; [[nodiscard]] bool IsAllowedToSleep () const noexcept; [[nodiscard]] bool IsInterpolating () const noexcept; - [[nodiscard]] bool IsSleeping () const noexcept; - [[nodiscard]] bool GetAutoMass () const noexcept; + + [[nodiscard]] bool GetIsSleeping () const noexcept; + + [[nodiscard]] Type GetType () const noexcept; [[nodiscard]] bool GetFreezePositionX () const noexcept; [[nodiscard]] bool GetFreezePositionY () const noexcept; @@ -82,7 +82,8 @@ namespace SHADE [[nodiscard]] bool GetFreezeRotationY () const noexcept; [[nodiscard]] bool GetFreezeRotationZ () const noexcept; - [[nodiscard]] float GetGravityScale () const noexcept; + //[[nodiscard]] bool GetAutoMass () const noexcept; + [[nodiscard]] float GetMass () const noexcept; [[nodiscard]] float GetDrag () const noexcept; [[nodiscard]] float GetAngularDrag () const noexcept; @@ -92,7 +93,8 @@ namespace SHADE [[nodiscard]] SHVec3 GetLinearVelocity () const noexcept; [[nodiscard]] SHVec3 GetAngularVelocity () const noexcept; - [[nodiscard]] SHVec3 GetPosition () const noexcept; + [[nodiscard]] const SHVec3& GetPosition () const noexcept; + [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; [[nodiscard]] SHVec3 GetRotation () const noexcept; /*---------------------------------------------------------------------------------*/ @@ -101,12 +103,8 @@ namespace SHADE void SetType (Type newType) noexcept; - void SetRigidBody (SHRigidBody* rb) noexcept; - - void SetIsGravityEnabled (bool enableGravity) noexcept; + void SetGravityEnabled (bool enableGravity) noexcept; void SetIsAllowedToSleep (bool isAllowedToSleep) noexcept; - void SetAutoMass (bool autoMass) noexcept; - void SetFreezePositionX (bool freezePositionX) noexcept; void SetFreezePositionY (bool freezePositionY) noexcept; void SetFreezePositionZ (bool freezePositionZ) noexcept; @@ -114,9 +112,9 @@ namespace SHADE void SetFreezeRotationY (bool freezeRotationY) noexcept; void SetFreezeRotationZ (bool freezeRotationZ) noexcept; void SetInterpolate (bool allowInterpolation) noexcept; + //void SetAutoMass (bool autoMass) noexcept; - void SetGravityScale (float gravityScale) noexcept; - void SetMass (float newMass) noexcept; + //void SetMass (float newMass) noexcept; void SetDrag (float newDrag) noexcept; void SetAngularDrag (float newAngularDrag) noexcept; @@ -139,17 +137,33 @@ namespace SHADE void AddRelativeTorque (const SHVec3& relativeTorque) const noexcept; void ClearForces () const noexcept; + void ClearTorque () const noexcept; private: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - Type type; - bool interpolate; - SHRigidBody* rigidBody; + static constexpr size_t NUM_FLAGS = 8; + static constexpr size_t NUM_DIRTY_FLAGS = 12; + + Type type; + + uint16_t flags; // 0 0 0 0 0 0 am ip aZ aY aX lZ lY lX slp g + uint16_t dirtyFlags; // 0 0 0 0 aD d m t aZ aY aX lZ lY lX slp g + + float mass; + float drag; + float angularDrag; + + SHVec3 linearVelocity; + SHVec3 angularVelocity; + + SHPhysicsSystem* system; + + SHVec3 position; + SHQuaternion orientation; RTTR_ENABLE() }; - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp new file mode 100644 index 00000000..71b08831 --- /dev/null +++ b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp @@ -0,0 +1,431 @@ +/**************************************************************************************** + * \file SHPhysicsObject.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Physics Object. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsObject.h" + +// Project Headers +#include "ECS_Base/Managers/SHSystemManager.h" +#include "ECS_Base/Managers/SHComponentManager.h" +#include "Scene/SHSceneManager.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject::SHPhysicsObject(EntityID eid, rp3d::PhysicsCommon* physicsFactory, rp3d::PhysicsWorld* physicsWorld) noexcept + : entityID { eid } + , collidersActive { true } + , factory { physicsFactory } + , world { physicsWorld } + , rp3dBody { nullptr } + { + // Implicitly create a static body. + + const auto* TRANSFORM = SHComponentManager::GetComponent(eid); + + const rp3d::Transform RP3D_TRANSFORM { TRANSFORM->GetWorldPosition(), TRANSFORM->GetWorldOrientation() }; + + rp3dBody = world->createRigidBody(RP3D_TRANSFORM); + rp3dBody->setType(rp3d::BodyType::STATIC); + } + + SHPhysicsObject::~SHPhysicsObject() noexcept + { + factory = nullptr; + world = nullptr; + rp3dBody = nullptr; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHVec3 SHPhysicsObject::GetPosition() const noexcept + { + return rp3dBody->getTransform().getPosition(); + } + + SHQuaternion SHPhysicsObject::GetOrientation() const noexcept + { + return rp3dBody->getTransform().getOrientation(); + } + + SHVec3 SHPhysicsObject::GetRotation() const noexcept + { + return SHQuaternion{ rp3dBody->getTransform().getOrientation() }.ToEuler(); + } + + rp3d::CollisionBody* SHPhysicsObject::GetCollisionBody() const noexcept + { + return rp3dBody; + } + + rp3d::RigidBody* SHPhysicsObject::GetRigidBody() const noexcept + { + return rp3dBody; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsObject::SetStaticBody() const noexcept + { + if (!rp3dBody) + return; + + rp3dBody->setType(rp3d::BodyType::STATIC); + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + int SHPhysicsObject::AddCollisionShape(int index) + { + // Get collider component + auto* colliderComponent = SHComponentManager::GetComponent_s(entityID); + if (!colliderComponent) + { + SHLOGV_ERROR("Unable to add Collision Shape to Entity {} due to Missing Collider Component!", entityID) + return -1; + } + + auto& collisionShape = colliderComponent->GetCollisionShape(index); + switch (collisionShape.GetType()) + { + // TODO(Diren): Add more collider shapes + + case SHCollisionShape::Type::BOX: + { + addBoxShape(collisionShape); + break; + } + + case SHCollisionShape::Type::SPHERE: + { + addSphereShape(collisionShape); + break; + } + default: break; + } + + auto* rp3dCollider = rp3dBody->getCollider(rp3dBody->getNbColliders() - 1); + syncColliderProperties(collisionShape, rp3dCollider); + + if (rp3dBody->getType() == rp3d::BodyType::DYNAMIC) + { + rp3dBody->updateMassPropertiesFromColliders(); + + if (auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); rigidBodyComponent) + rigidBodyComponent->mass = rp3dBody->getMass(); + } + + return index; + } + + void SHPhysicsObject::RemoveCollisionShape(int index) + { + const int NUM_COLLIDERS = static_cast(rp3dBody->getNbColliders()); + if (NUM_COLLIDERS == 0) + return; + + if (index < 0 || index >= NUM_COLLIDERS) + throw std::invalid_argument("Index out of range!"); + + auto* collider = rp3dBody->getCollider(index); + rp3dBody->removeCollider(collider); + + if (rp3dBody->getType() == rp3d::BodyType::DYNAMIC) + { + rp3dBody->updateMassPropertiesFromColliders(); + + auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); + if (rigidBodyComponent) + rigidBodyComponent->mass = rp3dBody->getMass(); + } + } + + void SHPhysicsObject::RemoveAllCollisionShapes() const noexcept + { + int numColliders = static_cast(rp3dBody->getNbColliders()); + if (numColliders == 0) + return; + + while (numColliders - 1 >= 0) + { + auto* collider = rp3dBody->getCollider(numColliders - 1); + rp3dBody->removeCollider(collider); + + --numColliders; + } + } + + void SHPhysicsObject::SyncRigidBody(SHRigidBodyComponent& component) const noexcept + { + // This state is synced in the pre-update routine + if (!rp3dBody->isActive()) + return; + + if (component.dirtyFlags == 0) + return; + + for (size_t i = 0; i < SHRigidBodyComponent::NUM_DIRTY_FLAGS; ++i) + { + if (const bool IS_DIRTY = component.dirtyFlags & (1U << i); IS_DIRTY) + { + switch (i) + { + case 0: // Gravity + { + const bool IS_ENABLED = component.flags & (1U << i); + rp3dBody->enableGravity(IS_ENABLED); + + break; + } + case 1: // Sleeping + { + const bool IS_ENABLED = component.flags & (1U << i); + rp3dBody->setIsAllowedToSleep(IS_ENABLED); + + break; + } + case 2: // Lock Position X + { + const bool IS_ENABLED = component.flags & (1U << i); + + auto positionLock = rp3dBody->getLinearLockAxisFactor(); + positionLock.x = IS_ENABLED ? 0.0f : 1.0f; + rp3dBody->setLinearLockAxisFactor(positionLock); + + break; + } + case 3: // Lock Position Y + { + const bool IS_ENABLED = component.flags & (1U << i); + + auto positionLock = rp3dBody->getLinearLockAxisFactor(); + positionLock.y = IS_ENABLED ? 0.0f : 1.0f; + rp3dBody->setLinearLockAxisFactor(positionLock); + + break; + } + case 4: // Lock Position Z + { + const bool IS_ENABLED = component.flags & (1U << i); + + auto positionLock = rp3dBody->getLinearLockAxisFactor(); + positionLock.z = IS_ENABLED ? 0.0f : 1.0f; + rp3dBody->setLinearLockAxisFactor(positionLock); + + break; + } + case 5: // Lock Rotation X + { + const bool IS_ENABLED = component.flags & (1U << i); + + auto rotationLock = rp3dBody->getAngularLockAxisFactor(); + rotationLock.x = IS_ENABLED ? 0.0f : 1.0f; + rp3dBody->setAngularLockAxisFactor(rotationLock); + + break; + } + case 6: // Lock Rotation Y + { + const bool IS_ENABLED = component.flags & (1U << i); + + auto rotationLock = rp3dBody->getAngularLockAxisFactor(); + rotationLock.y = IS_ENABLED ? 0.0f : 1.0f; + rp3dBody->setAngularLockAxisFactor(rotationLock); + + break; + } + case 7: // Lock Rotation Z + { + const bool IS_ENABLED = component.flags & (1U << i); + + auto rotationLock = rp3dBody->getAngularLockAxisFactor(); + rotationLock.z = IS_ENABLED ? 0.0f : 1.0f; + rp3dBody->setAngularLockAxisFactor(rotationLock); + + break; + } + case 8: // Type + { + rp3dBody->setType(static_cast(component.type)); + + break; + } + case 9: // Mass + { + //rp3dBody->setMass(component.mass); + + //if (component.GetAutoMass()) + //{ + // rp3dBody->updateMassPropertiesFromColliders(); + // component.mass = rp3dBody->getMass(); + //} + //else + //{ + // rp3dBody->setMass(component.mass); + // rp3dBody->updateLocalCenterOfMassFromColliders(); + // rp3dBody->updateLocalInertiaTensorFromColliders(); + //} + + break; + } + case 10: // Drag + { + rp3dBody->setLinearDamping(component.drag); + + break; + } + case 11: // Angular Drag + { + rp3dBody->setAngularDamping(component.angularDrag); + + break; + } + default: break; + } + } + } + + component.dirtyFlags = 0; + } + + void SHPhysicsObject::SyncColliders(SHColliderComponent& component) const noexcept + { + // This state is synced in the pre-update routine + if (!rp3dBody->isActive()) + return; + + const int NUM_SHAPES = static_cast(rp3dBody->getNbColliders()); + for (int i = 0; i < NUM_SHAPES; ++i) + { + auto& collisionShape = component.collisionShapes[i]; + + if (!collisionShape.dirty) + continue; + + switch (collisionShape.GetType()) + { + case SHCollisionShape::Type::BOX: syncBoxShape(i, collisionShape); break; + case SHCollisionShape::Type::SPHERE: syncSphereShape(i, collisionShape); break; + default: break; + } + + auto* rp3dCollider = rp3dBody->getCollider(i); + syncColliderProperties(collisionShape, rp3dCollider); + + collisionShape.dirty = false; + } + + // Set rigidbody mass if dynamic + auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); + if (rigidBodyComponent) + { + // This is generally expensive, will be optimised in the future with my own engine. + rp3dBody->updateMassPropertiesFromColliders(); + rigidBodyComponent->mass = rp3dBody->getMass(); + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsObject::syncColliderProperties(const SHCollisionShape& collisionShape, rp3d::Collider* rp3dCollider) const noexcept + { + rp3dCollider->setIsTrigger(collisionShape.IsTrigger()); + + auto& rp3dMaterial = rp3dCollider->getMaterial(); + + rp3dMaterial.setFrictionCoefficient(collisionShape.GetFriction()); + rp3dMaterial.setBounciness(collisionShape.GetBounciness()); + rp3dMaterial.setMassDensity(collisionShape.GetDensity()); + + const unsigned short MASK_BITS = collisionShape.GetCollisionTag(); + rp3dCollider->setCollisionCategoryBits(MASK_BITS); + rp3dCollider->setCollideWithMaskBits(MASK_BITS); + } + + void SHPhysicsObject::addBoxShape(const SHCollisionShape& boxShape) const noexcept + { + const rp3d::Transform OFFSETS + { + boxShape.GetPositionOffset() + , boxShape.GetRotationOffset() + }; + + const auto* BOX = reinterpret_cast(boxShape.GetShape()); + rp3d::BoxShape* newBox = factory->createBoxShape(BOX->GetWorldExtents()); + + rp3dBody->addCollider(newBox, OFFSETS); + } + + void SHPhysicsObject::syncBoxShape(int index, const SHCollisionShape& boxShape) const noexcept + { + const auto* BOX = reinterpret_cast(boxShape.GetShape()); + + auto* rp3dCollider = rp3dBody->getCollider(index); + auto* rp3dBox = reinterpret_cast(rp3dCollider->getCollisionShape()); + + const rp3d::Transform OFFSETS + { + boxShape.GetPositionOffset() + , boxShape.GetRotationOffset() + }; + + rp3dCollider->setIsTrigger(boxShape.IsTrigger()); + rp3dCollider->setLocalToBodyTransform(OFFSETS); + + rp3dBox->setHalfExtents(BOX->GetWorldExtents()); + } + + void SHPhysicsObject::addSphereShape(const SHCollisionShape& sphereShape) const noexcept + { + const rp3d::Transform OFFSETS + { + sphereShape.GetPositionOffset() + , sphereShape.GetRotationOffset() + }; + + const auto* SPHERE = reinterpret_cast(sphereShape.GetShape()); + rp3d::SphereShape* newSphere = factory->createSphereShape(SPHERE->GetWorldRadius()); + + rp3dBody->addCollider(newSphere, OFFSETS); + } + + void SHPhysicsObject::syncSphereShape(int index, const SHCollisionShape& sphereShape) const noexcept + { + const auto* SPHERE = reinterpret_cast(sphereShape.GetShape()); + + auto* rp3dCollider = rp3dBody->getCollider(index); + auto* rp3dSphere = reinterpret_cast(rp3dCollider->getCollisionShape()); + + const rp3d::Transform OFFSETS + { + sphereShape.GetPositionOffset() + , sphereShape.GetRotationOffset() + }; + + rp3dCollider->setIsTrigger(sphereShape.IsTrigger()); + rp3dCollider->setLocalToBodyTransform(OFFSETS); + + rp3dSphere->setRadius(SPHERE->GetWorldRadius()); + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.h b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.h similarity index 51% rename from SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.h rename to SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.h index 811f8375..c572ca2e 100644 --- a/SHADE_Engine/src/Physics/Collision/Contacts/SHCollisionKey.h +++ b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCollisionID.h + * \file SHPhysicsObject.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for Collision Information for Collision & Triggers. + * \brief Interface for a Physics Object. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -10,108 +10,102 @@ #pragma once -// Project Headers -#include "Physics/Interface/SHColliderComponent.h" -#include "Physics/Interface/SHRigidBodyComponent.h" +#include +// Project Headers +#include "Math/Transform/SHTransformComponent.h" +#include "Physics/Interface/SHRigidBodyComponent.h" +#include "Physics/Interface/SHColliderComponent.h" namespace SHADE { - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - struct SHCollisionKeyHash; - /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates the information when two collision shapes intersect. - */ - class SH_API SHCollisionKey + class SH_API SHPhysicsObject { private: /*---------------------------------------------------------------------------------*/ /* Friends */ /*---------------------------------------------------------------------------------*/ - friend struct SHCollisionKeyHash; + friend class SHPhysicsSystem; + friend class SHPhysicsObjectManager; public: /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHCollisionKey () noexcept; - SHCollisionKey (const SHCollisionKey& rhs) noexcept; - SHCollisionKey (SHCollisionKey&& rhs) noexcept; - - ~SHCollisionKey () noexcept = default; + SHPhysicsObject (EntityID eid, rp3d::PhysicsCommon* physicsFactory, rp3d::PhysicsWorld* physicsWorld) noexcept; + SHPhysicsObject (const SHPhysicsObject& rhs) noexcept = default; + SHPhysicsObject (SHPhysicsObject&& rhs) noexcept = default; + virtual ~SHPhysicsObject () noexcept; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - - SHCollisionKey& operator= (const SHCollisionKey& rhs) noexcept; - SHCollisionKey& operator= (SHCollisionKey&& rhs) noexcept; - - bool operator==(const SHCollisionKey& rhs) const; + + SHPhysicsObject& operator=(const SHPhysicsObject& rhs) noexcept = default; + SHPhysicsObject& operator=(SHPhysicsObject&& rhs) noexcept = default; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] EntityID GetEntityA () const noexcept; - [[nodiscard]] EntityID GetEntityB () const noexcept; - [[nodiscard]] uint32_t GetShapeIndexA () const noexcept; - [[nodiscard]] uint32_t GetShapeIndexB () const noexcept; - [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyA () const noexcept; - [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyB () const noexcept; - [[nodiscard]] const SHCollisionShape* GetCollisionShapeA () const noexcept; - [[nodiscard]] const SHCollisionShape* GetCollisionShapeB () const noexcept; + [[nodiscard]] SHVec3 GetPosition () const noexcept; + [[nodiscard]] SHQuaternion GetOrientation () const noexcept; + [[nodiscard]] SHVec3 GetRotation () const noexcept; + + [[nodiscard]] rp3d::CollisionBody* GetCollisionBody () const noexcept; + [[nodiscard]] rp3d::RigidBody* GetRigidBody () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetEntityA (EntityID entityID) noexcept; - void SetEntityB (EntityID entityID) noexcept; - void SetCollisionShapeA (uint32_t shapeIndexA) noexcept; - void SetCollisionShapeB (uint32_t shapeIndexB) noexcept; + void SetStaticBody () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + int AddCollisionShape (int index); + void RemoveCollisionShape (int index); + void RemoveAllCollisionShapes () const noexcept; + + void SyncRigidBody (SHRigidBodyComponent& component) const noexcept; + void SyncColliders (SHColliderComponent& component) const noexcept; private: - - static constexpr uint32_t ENTITY_A = 0; - static constexpr uint32_t SHAPE_INDEX_A = 1; - static constexpr uint32_t ENTITY_B = 2; - static constexpr uint32_t SHAPE_INDEX_B = 3; - /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - union - { - uint64_t value[2]; // EntityValue, ShapeIndexValue - uint32_t ids [4]; // EntityA, EntityB, ShapeIndexA, ShapeIndexB - }; - }; + EntityID entityID; + bool collidersActive; // Only used to sync with SHADE components + + rp3d::PhysicsCommon* factory; + rp3d::PhysicsWorld* world; + + rp3d::RigidBody* rp3dBody; + rp3d::Transform prevTransform; // Cached transform for interpolation - /** - * @brief - * Encapsulates a functor to hash a CollisionKey - */ - struct SHCollisionKeyHash - { - public: /*---------------------------------------------------------------------------------*/ - /* Member Functions */ + /* Function Members */ /*---------------------------------------------------------------------------------*/ - std::size_t operator()(const SHCollisionKey& id) const; - }; + void syncColliderProperties (const SHCollisionShape& collisionShape, rp3d::Collider* rp3dCollider) const noexcept; + // Box Shapes + + void addBoxShape (const SHCollisionShape& boxShape) const noexcept; + void syncBoxShape (int index, const SHCollisionShape& boxShape) const noexcept; + + // Sphere Shapes + + void addSphereShape (const SHCollisionShape& sphereShape) const noexcept; + void syncSphereShape (int index, const SHCollisionShape& sphereShape) const noexcept; + }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp new file mode 100644 index 00000000..7c111a2d --- /dev/null +++ b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp @@ -0,0 +1,309 @@ +/**************************************************************************************** + * \file SHPhysicsObjectManager.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Physics Object Manager. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsObjectManager.h" + +// Project Headers +#include "ECS_Base/Managers/SHEntityManager.h" +#include "Tools/Utilities/SHUtilities.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Static Data Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObjectManager::CommandFunctionPtr SHPhysicsObjectManager::componentFunc[3][2] + { + addRigidBody , addCollider , addCollisionShape + , removeRigidBody , removeCollider , removeCollisionShape + }; + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsObjectManager::SetFactory(rp3d::PhysicsCommon& physicsFactory) noexcept + { + factory = &physicsFactory; + } + + void SHPhysicsObjectManager::SetWorld(rp3d::PhysicsWorld* physicsWorld) noexcept + { + world = physicsWorld; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject* SHPhysicsObjectManager::GetPhysicsObject(EntityID eid) noexcept + { + const auto it = physicsObjects.find(eid); + if (it == physicsObjects.end()) + return nullptr; + + return &it->second; + } + + const SHPhysicsObjectManager::PhysicsObjectEntityMap SHPhysicsObjectManager::GetPhysicsObjects() const noexcept + { + return physicsObjects; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsObjectManager::AddRigidBody(EntityID eid) noexcept + { + const QueueCommand NEW_QUEUE_COMMAND + { + .eid = eid + , .command = QueueCommand::Command::ADD + , .component = PhysicsComponents::RIGID_BODY + }; + + commandQueue.push(NEW_QUEUE_COMMAND); + } + + void SHPhysicsObjectManager::AddCollider(EntityID eid) noexcept + { + const QueueCommand NEW_QUEUE_COMMAND + { + .eid = eid + , .command = QueueCommand::Command::ADD + , .component = PhysicsComponents::COLLIDER + }; + + commandQueue.push(NEW_QUEUE_COMMAND); + } + + void SHPhysicsObjectManager::AddCollisionShape(EntityID eid, int shapeIndex) noexcept + { + const QueueCommand NEW_QUEUE_COMMAND + { + .eid = eid + , .command = QueueCommand::Command::ADD + , .component = PhysicsComponents::COLLISION_SHAPE + , .shapeIndex = shapeIndex + }; + + commandQueue.push(NEW_QUEUE_COMMAND); + } + + void SHPhysicsObjectManager::RemoveRigidBody(EntityID eid) noexcept + { + const QueueCommand NEW_QUEUE_COMMAND + { + .eid = eid + , .command = QueueCommand::Command::REMOVE + , .component = PhysicsComponents::RIGID_BODY + }; + + commandQueue.push(NEW_QUEUE_COMMAND); + } + + void SHPhysicsObjectManager::RemoveCollider(EntityID eid) noexcept + { + const QueueCommand NEW_QUEUE_COMMAND + { + .eid = eid + , .command = QueueCommand::Command::REMOVE + , .component = PhysicsComponents::COLLIDER + }; + + commandQueue.push(NEW_QUEUE_COMMAND); + } + + void SHPhysicsObjectManager::RemoveCollisionShape(EntityID eid, int shapeIndex) noexcept + { + const QueueCommand NEW_QUEUE_COMMAND + { + .eid = eid + , .command = QueueCommand::Command::REMOVE + , .component = PhysicsComponents::COLLISION_SHAPE + , .shapeIndex = shapeIndex + }; + + commandQueue.push(NEW_QUEUE_COMMAND); + } + + void SHPhysicsObjectManager::UpdateCommands() + { + if (commandQueue.empty()) + return; + + while (!commandQueue.empty()) + { + const QueueCommand COMMAND = commandQueue.front(); + commandQueue.pop(); + + // Check validity of command + if (COMMAND.command == QueueCommand::Command::INVALID || COMMAND.component == PhysicsComponents::INVALID) + continue; + + // Get physics components + const PhysicsComponentGroup COMPONENT_GROUP + { + .eid = COMMAND.eid + , .rigidBodyComponent = SHComponentManager::GetComponent_s(COMMAND.eid) + , .colliderComponent = SHComponentManager::GetComponent_s(COMMAND.eid) + }; + + // Delete any object that is missing both components + // We infer that a remove command has been pushed for these, but we will ignore those if both components have already been removed. + if (!COMPONENT_GROUP.rigidBodyComponent && !COMPONENT_GROUP.colliderComponent) + { + destroyPhysicsObject(COMMAND.eid); + wakeAllObjects(); + continue; + } + + // Find the physics Object. If none found and attempting to add, create an object. + SHPhysicsObject* physicsObject = GetPhysicsObject(COMMAND.eid); + if (!physicsObject && COMMAND.command == QueueCommand::Command::ADD) + physicsObject = createPhysicsObject(COMMAND.eid); + + componentFunc[SHUtilities::ConvertEnum(COMMAND.command)][SHUtilities::ConvertEnum(COMMAND.component)](COMMAND, physicsObject, COMPONENT_GROUP); + + // If any removal was done, wake all objects + if (COMMAND.command == QueueCommand::Command::REMOVE) + wakeAllObjects(); + } + } + + void SHPhysicsObjectManager::RemoveAllObjects() + { + // Destroy all objects and clear + for (auto& physicsObject : physicsObjects | std::views::values) + { + world->destroyRigidBody(physicsObject.GetRigidBody()); + physicsObject.rp3dBody = nullptr; + } + + physicsObjects.clear(); + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject* SHPhysicsObjectManager::createPhysicsObject(EntityID eid) noexcept + { + // Force transforms to sync + SHVec3 worldPos = SHVec3::Zero; + SHQuaternion worldRot = SHQuaternion::Identity; + + const SHTransformComponent* TRANSFORM = nullptr; + if (SHEntityManager::IsValidEID(eid)) + TRANSFORM = SHComponentManager::GetComponent_s(eid); + + if (TRANSFORM) + { + worldPos = TRANSFORM->GetWorldPosition(); + worldRot = TRANSFORM->GetWorldOrientation(); + } + + const rp3d::Transform RP3D_TRANSFORM{ worldPos, worldRot }; + + auto& newPhysicsObject = physicsObjects.emplace(eid, SHPhysicsObject{ eid, factory, world }).first->second; + newPhysicsObject.GetRigidBody()->setTransform(RP3D_TRANSFORM); + newPhysicsObject.prevTransform = RP3D_TRANSFORM; + + return &newPhysicsObject; + } + + void SHPhysicsObjectManager::destroyPhysicsObject(EntityID eid) noexcept + { + const auto ITER = physicsObjects.find(eid); + if (ITER == physicsObjects.end()) + { + // Assume the object has already been successfully destroyed + return; + } + + world->destroyRigidBody(ITER->second.GetRigidBody()); + ITER->second.rp3dBody = nullptr; + + physicsObjects.erase(eid); + } + + void SHPhysicsObjectManager::addRigidBody(const QueueCommand&, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup) + { + SHASSERT(physicsObject != nullptr, "Valid physics object required to add body!") + + if (!componentGroup.rigidBodyComponent) + { + SHLOG_ERROR("Entity {} is missing a Rigidbody Component. Unable to update physics object!", componentGroup.eid) + return; + } + + // A static rigid body is implicitly created on creation of a physics object. + // Nothing is needed here. + } + + void SHPhysicsObjectManager::addCollider(const QueueCommand&, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup) + { + SHASSERT(physicsObject != nullptr, "Valid physics object required to add collider!") + + if (!componentGroup.colliderComponent) + { + SHLOG_ERROR("Entity {} is missing a Rigidbody Component. Unable to update physics object!", componentGroup.eid) + return; + } + + //const int NUM_SHAPES = static_cast(componentGroup.colliderComponent->GetCollisionShapes().size()); + //for (int i = 0; i < NUM_SHAPES; ++i) + // physicsObject->AddCollisionShape(i); + + physicsObject->SyncColliders(*componentGroup.colliderComponent); + } + + void SHPhysicsObjectManager::removeRigidBody(const QueueCommand&, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup) + { + SHASSERT(physicsObject != nullptr, "Valid physics object required to remove body!") + + if (componentGroup.colliderComponent) + physicsObject->SetStaticBody(); + } + + void SHPhysicsObjectManager::removeCollider(const QueueCommand&, SHPhysicsObject* physicsObject, const PhysicsComponentGroup&) + { + SHASSERT(physicsObject != nullptr, "Valid physics object required to remove collider!") + + physicsObject->RemoveAllCollisionShapes(); + } + + void SHPhysicsObjectManager::addCollisionShape(const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup&) + { + SHASSERT(physicsObject != nullptr, "Valid physics object required to add collision shape!") + + physicsObject->AddCollisionShape(command.shapeIndex); + } + + void SHPhysicsObjectManager::removeCollisionShape(const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup&) + { + SHASSERT(physicsObject != nullptr, "Valid physics object required to remove collision shape!") + + physicsObject->RemoveCollisionShape(command.shapeIndex); + } + + void SHPhysicsObjectManager::wakeAllObjects() noexcept + { + for (auto& physicsObject : physicsObjects | std::views::values) + physicsObject.GetRigidBody()->setIsSleeping(false); + } + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h new file mode 100644 index 00000000..f41c62ad --- /dev/null +++ b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h @@ -0,0 +1,181 @@ +/**************************************************************************************** + * \file SHPhysicsObjectManager.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Physics Object Manager. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#pragma once + +#include +#include + +#include + +// Project Headers +#include "SHPhysicsObject.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + class SH_API SHPhysicsObjectManager + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHPhysicsSystem; + + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using PhysicsObjectEntityMap = std::unordered_map; + + public: + + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + enum class PhysicsComponents + { + RIGID_BODY + , COLLIDER + , COLLISION_SHAPE + + , TOTAL + , INVALID = -1 + }; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsObjectManager () = default; + ~SHPhysicsObjectManager () = default; + + SHPhysicsObjectManager (const SHPhysicsObjectManager&) = delete; + SHPhysicsObjectManager (SHPhysicsObjectManager&&) = delete; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsObjectManager& operator=(const SHPhysicsObjectManager&) = delete; + SHPhysicsObjectManager& operator=(SHPhysicsObjectManager&&) = delete; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] SHPhysicsObject* GetPhysicsObject (EntityID eid) noexcept; + [[nodiscard]] const PhysicsObjectEntityMap GetPhysicsObjects () const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetFactory (rp3d::PhysicsCommon& physicsFactory) noexcept; + void SetWorld (rp3d::PhysicsWorld* physicsWorld) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + void AddRigidBody (EntityID eid) noexcept; + void AddCollider (EntityID eid) noexcept; + void AddCollisionShape (EntityID eid, int shapeIndex) noexcept; + + void RemoveRigidBody (EntityID eid) noexcept; + void RemoveCollider (EntityID eid) noexcept; + void RemoveCollisionShape (EntityID eid, int shapeIndex) noexcept; + + void UpdateCommands (); + void RemoveAllObjects (); + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + struct QueueCommand + { + /*-------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-------------------------------------------------------------------------------*/ + + enum class Command + { + ADD + , REMOVE + + , INVALID = -1 + }; + + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + EntityID eid = MAX_EID; + Command command = Command::INVALID; + PhysicsComponents component = PhysicsComponents::INVALID; + int shapeIndex = -1; // Only used when adding & removing collision shapes + }; + + struct PhysicsComponentGroup + { + public: + + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + + EntityID eid = MAX_EID; + SHRigidBodyComponent* rigidBodyComponent = nullptr; + SHColliderComponent* colliderComponent = nullptr; + }; + + using CommandFunctionPtr = void(*)(const QueueCommand&, SHPhysicsObject*, const PhysicsComponentGroup&); + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + static CommandFunctionPtr componentFunc[3][2]; // 3 components, 2 commands + + rp3d::PhysicsCommon* factory = nullptr; + rp3d::PhysicsWorld* world = nullptr; + + PhysicsObjectEntityMap physicsObjects; + std::queue commandQueue; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsObject* createPhysicsObject (EntityID eid) noexcept; + void destroyPhysicsObject (EntityID eid) noexcept; + + void wakeAllObjects () noexcept; + + static void addRigidBody (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); + static void addCollider (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); + static void removeRigidBody (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); + static void removeCollider (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); + + static void addCollisionShape (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); + static void removeCollisionShape (const QueueCommand& command, SHPhysicsObject* physicsObject, const PhysicsComponentGroup& componentGroup); + + + }; + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsConstants.h b/SHADE_Engine/src/Physics/SHPhysicsConstants.h deleted file mode 100644 index b680515e..00000000 --- a/SHADE_Engine/src/Physics/SHPhysicsConstants.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsConstants.h - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Definitions for constants used in physics simulations - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#pragma once - -// Project Includes -#include "Math/SHMathHelpers.h" - -namespace SHADE -{ - /** - * @brief - * The number of simulations length for every real world unit meter.
- * Modify this to change the global scale of the simulation. - */ - static constexpr float SHPHYSICS_LENGTHS_PER_UNIT_METER = 1.0f; - - /** - * @brief - * Linear Collision & Constraint tolerance. - */ - static constexpr float SHPHYSICS_LINEAR_SLOP = 0.01f * SHPHYSICS_LENGTHS_PER_UNIT_METER; - - /** - * @brief - * Velocity threshold for restitution to be applied. - */ - static constexpr float SHPHYSICS_RESTITUTION_THRESHOLD = 1.0f; - - /** - * @brief - * Scaling factor to control how fast overlaps are resolved.
- * 1 is ideal for instant correction, but values close to 1 can lead to overshoot. - */ - static constexpr float SHPHYSICS_BAUMGARTE = 0.2f; - - /** - * @brief - * Distance threshold to consider two contacts as the same. - */ - static constexpr float SHPHYSICS_SAME_CONTACT_DISTANCE = 0.01; - -} diff --git a/SHADE_Engine/src/Physics/SHPhysicsEvents.h b/SHADE_Engine/src/Physics/SHPhysicsEvents.h index a192ec3f..ae48a75b 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsEvents.h +++ b/SHADE_Engine/src/Physics/SHPhysicsEvents.h @@ -11,7 +11,8 @@ #pragma once // Project Headers -#include "Collision/Shapes/SHCollisionShape.h" +#include "Interface/SHCollisionShape.h" + namespace SHADE { @@ -27,16 +28,9 @@ namespace SHADE }; struct SHPhysicsColliderRemovedEvent - { - EntityID entityID; - SHCollisionShape::Type colliderType; - int colliderIndex; - }; - - struct SHColliderOnDebugDrawEvent { EntityID entityID; - bool debugDrawState; + int colliderIndex; }; diff --git a/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp b/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp new file mode 100644 index 00000000..85e76702 --- /dev/null +++ b/SHADE_Engine/src/Physics/SHPhysicsWorld.cpp @@ -0,0 +1,71 @@ +/**************************************************************************************** + * \file SHPhysicsWorld.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Physics World. + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsWorld.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsWorldState::SHPhysicsWorldState() noexcept + : world { nullptr } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Members Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsWorldState::CreateWorld(rp3d::PhysicsCommon& factory) + { + rp3d::PhysicsWorld::WorldSettings rp3dWorldSettings; + rp3dWorldSettings.gravity = settings.gravity; + rp3dWorldSettings.defaultVelocitySolverNbIterations = settings.numVelocitySolverIterations; + rp3dWorldSettings.defaultPositionSolverNbIterations = settings.numPositionSolverIterations; + rp3dWorldSettings.isSleepingEnabled = settings.sleepingEnabled; + + // These are my preferred default values. QoL for modifying these. + rp3dWorldSettings.defaultBounciness = 0.0f; + rp3dWorldSettings.defaultFrictionCoefficient = 0.4f; + + world = factory.createPhysicsWorld(rp3dWorldSettings); + world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::SPLIT_IMPULSES); + } + + void SHPhysicsWorldState::DestroyWorld(rp3d::PhysicsCommon& factory) + { + if (!world) + return; + + factory.destroyPhysicsWorld(world); + world = nullptr; + } + + void SHPhysicsWorldState::UpdateSettings() const noexcept + { + if (!world) + { + SHLOGV_ERROR("Unable to update Physics World settings without creating a world!") + return; + } + + world->setGravity(settings.gravity); + world->setNbIterationsVelocitySolver(settings.numVelocitySolverIterations); + world->setNbIterationsPositionSolver(settings.numPositionSolverIterations); + world->enableSleeping(settings.sleepingEnabled); + } + + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h b/SHADE_Engine/src/Physics/SHPhysicsWorld.h similarity index 51% rename from SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h rename to SHADE_Engine/src/Physics/SHPhysicsWorld.h index 1df84da0..c5152c44 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollisionDispatch.h +++ b/SHADE_Engine/src/Physics/SHPhysicsWorld.h @@ -1,7 +1,7 @@ /**************************************************************************************** - * \file SHCollisionDispatch.h + * \file SHPhysicsWorld.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for the static Collision Dispatcher + * \brief Interface for a Physics World. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -10,9 +10,11 @@ #pragma once +#include + // Project Headers -#include "Physics/Collision/Contacts/SHManifold.h" -#include "Physics/Collision/Contacts/SHCollisionKey.h" +#include "Math/SHMath.h" +#include "SH_API.h" namespace SHADE { @@ -20,36 +22,52 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates static methods for running narrow-phase collision detection. - */ - class SH_API SHCollisionDispatcher + struct SH_API SHPhysicsWorldState { public: - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - static bool Collide (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - static bool Collide (const SHCollisionShape& A, const SHCollisionShape& B) noexcept; - - private: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using ManifoldCollide = bool(*)(SHManifold&, const SHCollisionShape& A, const SHCollisionShape& B); - using TriggerCollide = bool(*)(const SHCollisionShape& A, const SHCollisionShape& B); + struct WorldSettings + { + public: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + SHVec3 gravity = SHVec3{ 0.0f, -9.81f, 0.0f }; + uint16_t numVelocitySolverIterations = 10; + uint16_t numPositionSolverIterations = 5; + bool sleepingEnabled = true; + }; /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - static constexpr int NUM_SHAPES = static_cast(SHCollisionShape::Type::COUNT); + rp3d::PhysicsWorld* world; + WorldSettings settings; - static const ManifoldCollide manifoldCollide [NUM_SHAPES][NUM_SHAPES]; - static const TriggerCollide triggerCollide [NUM_SHAPES][NUM_SHAPES]; + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsWorldState() noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + void CreateWorld (rp3d::PhysicsCommon& factory); + void DestroyWorld (rp3d::PhysicsCommon& factory); + + /** + * @brief Applies the current settings to the physics world. The world must be created + * before this is called. + */ + void UpdateSettings () const noexcept; }; diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp deleted file mode 100644 index 69317e59..00000000 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsDebugDrawRoutine.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsDebugDrawRutine.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Physics Debut Draw Routine - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "Physics/System/SHPhysicsDebugDrawSystem.h" - -// Project Headers -#include "ECS_Base/Managers/SHSystemManager.h" -#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" -#include "Math/Transform/SHTransformComponent.h" -#include "Physics/System/SHPhysicsSystem.h" -#include "Tools/Utilities/SHUtilities.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsDebugDrawSystem::PhysicsDebugDraw::PhysicsDebugDraw() - : SHSystemRoutine { "Physics Debug-Draw", true } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsDebugDrawSystem::PhysicsDebugDraw::Execute(double) noexcept - { - auto* physicsDebugDrawSystem = reinterpret_cast(GetSystem()); - - if (!physicsDebugDrawSystem->IsDebugDrawActive()) - return; - - auto* debugDrawSystem = SHSystemManager::GetSystem(); - - const bool DRAW_COLLIDERS = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::COLLIDERS); - const bool DRAW_CONTACTS = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::CONTACTS); - const bool DRAW_RAYCASTS = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::RAYCASTS); - const bool DRAW_BROADPHASE = physicsDebugDrawSystem->GetFlagState(DebugDrawFlags::BROADPHASE); - - // If draw all colliders is active, get all colliders from the dense set and draw. - // Else we check if any colliders have been flagged for drawing. - if (DRAW_COLLIDERS) - { - const auto& COLLIDER_COMPONENT_DENSE = SHComponentManager::GetDense(); - for (const auto& COLLIDER_COMPONENT : COLLIDER_COMPONENT_DENSE) - { - const auto* COLLIDER = COLLIDER_COMPONENT.GetCollider(); - drawCollider(debugDrawSystem, *COLLIDER); - } - } - else if (!physicsDebugDrawSystem->collidersToDraw.empty()) - { - for (const auto EID : physicsDebugDrawSystem->collidersToDraw) - { - const auto* COLLIDER = SHComponentManager::GetComponent(EID)->GetCollider(); - drawCollider(debugDrawSystem, *COLLIDER); - } - } - - auto* physicsSystem = SHSystemManager::GetSystem(); - if (!physicsSystem) - return; - - if (DRAW_CONTACTS) - { - const SHColour& CONTACT_COLOUR = physicsDebugDrawSystem->DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::CONTACT)]; - - const auto& CONTACT_POINTS = physicsSystem->physicsWorld->GetContactPoints(); - for (auto& contactPoint : CONTACT_POINTS) - { - const SHMatrix TRS = SHMatrix::Transform(contactPoint.position, SHQuaternion::Identity, SHVec3{ 0.1f }); - debugDrawSystem->DrawCube(TRS, CONTACT_COLOUR); - debugDrawSystem->DrawLine(contactPoint.position, contactPoint.position + contactPoint.normal * 0.5f, CONTACT_COLOUR, true); - } - } - - if (DRAW_RAYCASTS) - { - const SHColour& RAY_COLOUR = physicsDebugDrawSystem->DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::RAYCAST)]; - - const auto& RAYS = physicsSystem->raycastHits; - for (const auto& hit : RAYS) - debugDrawSystem->DrawLine(hit.start, hit.end, RAY_COLOUR, true); - - // Clear rays for the physics system - physicsSystem->raycastHits.clear(); - } - - if (DRAW_BROADPHASE) - { - const SHColour& AABB_COLOUR = physicsDebugDrawSystem->DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::BROADPHASE)]; - - const auto& BROADPHASE_AABBS = physicsSystem->collisionSpace->GetBroadphaseAABBs(); - for (auto& aabb : BROADPHASE_AABBS) - { - // Compute AABB Transform - const SHMatrix TRS = SHMatrix::Transform(aabb.GetCenter(), SHQuaternion::Identity, aabb.GetExtents() * 2.0f); - debugDrawSystem->DrawWireCube(TRS, AABB_COLOUR); - } - } - } - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp deleted file mode 100644 index a08b943b..00000000 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPostUpdateRoutine.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsPostUpdateRoutine.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Physics Post-Update Routine - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "Physics/System/SHPhysicsSystem.h" - -// Project Headers -#include "ECS_Base/Managers/SHSystemManager.h" -#include "Math/Transform/SHTransformComponent.h" -#include "Scene/SHSceneManager.h" -#include "Scripting/SHScriptEngine.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsSystem::PhysicsPostUpdate::PhysicsPostUpdate() - : SHSystemRoutine { "Physics Post-Update", false } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsSystem::PhysicsPostUpdate::Execute(double) noexcept - { - auto* physicsSystem = reinterpret_cast(GetSystem()); - auto* scriptingSystem = SHSystemManager::GetSystem(); - - if (scriptingSystem == nullptr) - { - SHLOGV_ERROR("Unable to invoke collision and trigger script events due to missing SHScriptEngine!"); - } - - const float FACTOR = static_cast(physicsSystem->interpolationFactor); - - // Interpolate transforms for rendering. - // Only rigid bodies can move due to physics, so we run through the rigid body component dense set. - if (physicsSystem->worldUpdated) - { - const auto& RIGIDBODY_DENSE = SHComponentManager::GetDense(); - for (auto& rigidBodyComponent : RIGIDBODY_DENSE) - { - const EntityID EID = rigidBodyComponent.GetEID(); - - // Skip missing transforms - auto* transformComponent = SHComponentManager::GetComponent_s(EID); - if (!transformComponent) - continue; - - // Skip invalid bodies (Should not occur) - if (!rigidBodyComponent.rigidBody) - continue; - - // Skip inactive bodies - const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(EID); - if (!IS_ACTIVE) - continue; - - const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); - - // Skip objects that have not moved - if (!MOTION_STATE) - continue; - - if (rigidBodyComponent.IsInterpolating()) - { - const SHVec3 RENDER_POSITION = MOTION_STATE.InterpolatePositions(FACTOR); - const SHQuaternion RENDER_ORIENTATION = MOTION_STATE.InterpolateOrientations(FACTOR); - - transformComponent->SetWorldPosition(RENDER_POSITION); - transformComponent->SetWorldOrientation(RENDER_ORIENTATION); - } - else - { - transformComponent->SetWorldPosition(MOTION_STATE.position); - transformComponent->SetWorldOrientation(MOTION_STATE.orientation); - } - - /* - * TODO: Test if the scene graph transforms abides by setting world position. Collisions will ignore the scene graph hierarchy. - */ - } - } - - - - // Collision & Trigger messages - if (scriptingSystem != nullptr) - scriptingSystem->ExecuteCollisionFunctions(); - } - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp deleted file mode 100644 index 3f2de93f..00000000 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsPreUpdateRoutine.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsPreUpdateRoutine.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Physics Pre-Update Routine - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "Physics/System/SHPhysicsSystem.h" - -// Project Headers -#include "ECS_Base/Managers/SHComponentManager.h" -#include "Math/Transform/SHTransformComponent.h" -#include "Scene/SHSceneManager.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsSystem::PhysicsPreUpdate::PhysicsPreUpdate() - : SHSystemRoutine { "Physics Pre-Update", true } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsSystem::PhysicsPreUpdate::Execute(double) noexcept - { - auto* physicsSystem = reinterpret_cast(GetSystem()); - - // Get all physics objects & sync transforms - auto& physicsObjects = physicsSystem->physicsObjectManager.GetPhysicsObjects(); - for (auto& [entityID, physicsObject] : physicsObjects) - { - const auto* TRANSFORM_COMPONENT = SHComponentManager::GetComponent_s(entityID); - // Assume transform is always active - const bool UPDATE_TRANSFORM = TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged(); - - // We assume that all engine components and physics object components have been successfully linked - - if (physicsObject.rigidBody) - { - const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(entityID); - const bool RIGIDBODY_ACTIVE = physicsObject.rigidBody->IsActive(); - - if (IS_ACTIVE != RIGIDBODY_ACTIVE) - physicsObject.rigidBody->SetIsActive(IS_ACTIVE); - - if (UPDATE_TRANSFORM) - { - const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); - const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation(); - - SHMotionState& motionState = physicsObject.rigidBody->GetMotionState(); - motionState.ForcePosition(WORLD_POS); - motionState.ForceOrientation(WORLD_ROT); - } - } - - if (physicsObject.collider) - { - const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(entityID); - const bool COLLIDER_ACTIVE = physicsObject.collider->IsActive(); - - if (IS_ACTIVE != COLLIDER_ACTIVE) - physicsObject.collider->SetIsActive(IS_ACTIVE); - - if (UPDATE_TRANSFORM) - { - const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition(); - const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation(); - const SHVec3& WORLD_SCL = TRANSFORM_COMPONENT->GetWorldScale(); - - physicsObject.collider->SetPosition(WORLD_POS); - physicsObject.collider->SetOrientation(WORLD_ROT); - physicsObject.collider->SetScale(WORLD_SCL); - - physicsObject.collider->Update(); - } - } - } - - physicsSystem->collisionSpace->UpdateBroadphase(); - } - -} // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp b/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp deleted file mode 100644 index 6ae53b31..00000000 --- a/SHADE_Engine/src/Physics/System/Routines/SHPhysicsUpdateRoutine.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************************** - * \file SHPhysicsUpdateRoutine.cpp - * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for the Physics Update Routine - * - * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or - * disclosure of this file or its contents without the prior written consent - * of DigiPen Institute of Technology is prohibited. -****************************************************************************************/ - -#include - -// Primary Header -#include "Physics/System/SHPhysicsSystem.h" - -// Project Headers -#include "ECS_Base/Managers/SHSystemManager.h" -#include "Scripting/SHScriptEngine.h" - -namespace SHADE -{ - /*-----------------------------------------------------------------------------------*/ - /* Constructors & Destructor Definitions */ - /*-----------------------------------------------------------------------------------*/ - - SHPhysicsSystem::PhysicsUpdate::PhysicsUpdate() - : SHFixedSystemRoutine { DEFAULT_FIXED_STEP, "Physics Update", false } - {} - - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHPhysicsSystem::PhysicsUpdate::Execute(double dt) noexcept - { - auto* physicsSystem = reinterpret_cast(GetSystem()); - - auto* scriptEngine = SHSystemManager::GetSystem(); - if (!scriptEngine) - { - SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing ScriptEngine!") - } - - const double FIXED_DT = physicsSystem->fixedDT; - accumulatedTime += dt; - - int count = 0; - while (accumulatedTime > FIXED_DT) - { - if (scriptEngine) - scriptEngine->ExecuteFixedUpdates(); - - if (physicsSystem->physicsWorld) - physicsSystem->physicsWorld->Step(static_cast(FIXED_DT)); - - accumulatedTime -= FIXED_DT; - ++count; - } - - stats.numSteps = count; - physicsSystem->worldUpdated = count > 0; - - physicsSystem->interpolationFactor = physicsSystem->worldUpdated ? accumulatedTime / FIXED_DT : 0.0; - } - -} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index ae25216f..96af5e39 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -13,60 +13,79 @@ // Primary Header #include "SHPhysicsDebugDrawSystem.h" -// Project Header +// Project Headers #include "ECS_Base/Managers/SHSystemManager.h" -#include "Math/Transform/SHTransformComponent.h" -#include "Physics/SHPhysicsEvents.h" -#include "Tools/Utilities/SHUtilities.h" +#include "Editor/SHEditor.h" +#include "Scene/SHSceneManager.h" namespace SHADE { - const SHColour SHPhysicsDebugDrawSystem::DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(Colours::COUNT)] + /*-----------------------------------------------------------------------------------*/ + /* Static Data Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHPhysicsDebugDrawSystem::DebugDrawFunction SHPhysicsDebugDrawSystem::drawFunctions[NUM_FLAGS] = { - SHColour::GREEN // Colliders - , SHColour::PURPLE // Triggers - , SHColour::RED // Contacts - , SHColour::ORANGE // Raycasts - , SHColour::CYAN // Broadphase + drawColliders + , drawColliderAABBs + , drawBroadPhaseAABBs + , drawContactPoints + , drawContactNormals + , drawRaycasts }; + SHVec3 SHPhysicsDebugDrawSystem::boxVertices[NUM_BOX_VERTICES]; + /*-----------------------------------------------------------------------------------*/ /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ SHPhysicsDebugDrawSystem::SHPhysicsDebugDrawSystem() noexcept - : flags { 0 } + : debugDrawFlags { 0 } + , physicsSystem { nullptr } { - collidersToDraw.clear(); + debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER)] = SHColour::GREEN; + debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER_AABB)] = SHColour::YELLOW; + debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::BROAD_PHASE_AABB)] = SHColour::CYAN; + debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_POINTS)] = SHColour::RED; + debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_NORMALS)] = SHColour::RED; + debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::RAYCASTS)] = SHColour::ORANGE; } + SHPhysicsDebugDrawSystem::PhysicsDebugDrawRoutine::PhysicsDebugDrawRoutine() + : SHSystemRoutine { "Physics Debug Draw", true } + {} + /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - bool SHPhysicsDebugDrawSystem::IsDebugDrawActive() const noexcept + bool SHPhysicsDebugDrawSystem::GetDebugDrawFlag(DebugDrawFlags flag) const noexcept { - return flags & ACTIVE_FLAG; - } + const auto INT_FLAG = SHUtilities::ConvertEnum(flag); + if (INT_FLAG < 0 || INT_FLAG >= NUM_FLAGS) + { + SHLOG_ERROR("Invalid Debug Draw Flag Passed {} in. Unable to get debug draw state!", INT_FLAG) + return false; + } - bool SHPhysicsDebugDrawSystem::GetFlagState(DebugDrawFlags flag) const noexcept - { - const uint8_t ENUM_VALUE = SHUtilities::ConvertEnum(flag); - return flags & ENUM_VALUE; + return debugDrawFlags & 1U << SHUtilities::ConvertEnum(flag); } /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsDebugDrawSystem::SetFlagState(DebugDrawFlags flag, bool state) noexcept + void SHPhysicsDebugDrawSystem::SetDebugDrawFlag(DebugDrawFlags flag, bool value) noexcept { - const uint8_t ENUM_VALUE = SHUtilities::ConvertEnum(flag); - state ? flags |= ENUM_VALUE : flags &= ~ENUM_VALUE; + const auto INT_FLAG = SHUtilities::ConvertEnum(flag); + if (INT_FLAG < 0 || INT_FLAG >= NUM_FLAGS) + { + SHLOG_ERROR("Invalid Debug Draw Flag Passed {} in. Unable to set debug draw state!", INT_FLAG) + return; + } - // If no other debug drawing state is active, turn active off. Otherwise, we maintain - // the active state. - flags == ACTIVE_FLAG ? flags = 0 : flags |= ACTIVE_FLAG; + value ? (debugDrawFlags |= 1U << INT_FLAG) : (debugDrawFlags &= ~(1U << INT_FLAG)); } /*-----------------------------------------------------------------------------------*/ @@ -77,76 +96,240 @@ namespace SHADE { SystemFamily::GetID(); - // Register collider draw event - const std::shared_ptr EVENT_RECEIVER = std::make_shared>(this, &SHPhysicsDebugDrawSystem::onColliderDraw); - const ReceiverPtr EVENT_RECEIVER_PTR = std::dynamic_pointer_cast(EVENT_RECEIVER); + SHASSERT(physicsSystem == nullptr, "Non-existent physics system attached to the physics debug draw system!") + physicsSystem = SHSystemManager::GetSystem(); - SHEventManager::SubscribeTo(SH_PHYSICS_COLLIDER_DRAW_EVENT, EVENT_RECEIVER_PTR); + // Generate shapes + generateBox(); } void SHPhysicsDebugDrawSystem::Exit() { - + physicsSystem = nullptr; } - void SHPhysicsDebugDrawSystem::AddRaycast(const SHRay& ray, const SHPhysicsRaycastResult& result) noexcept + void SHPhysicsDebugDrawSystem::PhysicsDebugDrawRoutine::Execute(double) noexcept { - + auto* system = reinterpret_cast(GetSystem()); + + auto* debugDrawSystem = SHSystemManager::GetSystem(); + if (debugDrawSystem == nullptr) + { + SHLOG_ERROR("Unable to get a debug draw system for Physics Debug Drawing!") + return; + } + + rp3d::DebugRenderer* rp3dRenderer = nullptr; + if (system->physicsSystem->worldState.world) + rp3dRenderer = &system->physicsSystem->worldState.world->getDebugRenderer(); + + for (int i = 0; i < SHUtilities::ConvertEnum(DebugDrawFlags::NUM_FLAGS); ++i) + { + const bool DRAW = (system->debugDrawFlags & (1U << i)) > 0; + if (DRAW) + { + drawFunctions[i](debugDrawSystem, rp3dRenderer); + } + else + { + if (rp3dRenderer && (i == 3 || i == 4)) + { + rp3dRenderer->setIsDebugItemDisplayed(reactphysics3d::DebugRenderer::DebugItem::CONTACT_POINT, false); + rp3dRenderer->setIsDebugItemDisplayed(reactphysics3d::DebugRenderer::DebugItem::CONTACT_NORMAL, false); + } + } + + } + + // Automatically clear the container of raycasts despite debug drawing state + // TODO(Diren): Move this somewhere else + system->physicsSystem->raycaster.ClearFrame(); } /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHEventHandle SHPhysicsDebugDrawSystem::onColliderDraw(SHEventPtr onColliderDrawEvent) + void SHPhysicsDebugDrawSystem::drawColliders(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept { - const auto& EVENT_DATA = reinterpret_cast*>(onColliderDrawEvent.get())->data; - - // Add to the container to draw all colliders - if (EVENT_DATA->debugDrawState) + const auto& COLLIDER_SET = SHComponentManager::GetDense(); + for (const auto& COLLIDER : COLLIDER_SET) { - if (collidersToDraw.empty()) - flags |= ACTIVE_FLAG; + // Skip inactive colliders + if (!SHSceneManager::CheckNodeAndComponentsActive(COLLIDER.GetEID())) + continue; - collidersToDraw.emplace(EVENT_DATA->entityID); - } - else - { - collidersToDraw.erase(EVENT_DATA->entityID); - - // if no colliders queued for drawing and no other debug drawing is enabled, disable debug drawing - if (collidersToDraw.empty() && flags == ACTIVE_FLAG) - flags = 0; - } - - return onColliderDrawEvent.get()->handle; - } - - void SHPhysicsDebugDrawSystem::drawCollider(SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept - { - for (const auto* SHAPE : collider.GetCollisionShapes()) - { - const SHColour& DRAW_COLOUR = DEBUG_DRAW_COLOURS[SHUtilities::ConvertEnum(SHAPE->IsTrigger() ? Colours::TRIGGER : Colours::COLLIDER)]; - - switch (SHAPE->GetType()) + for (auto& collisionShape : COLLIDER.GetCollisionShapes()) { - case SHCollisionShape::Type::SPHERE: + switch (collisionShape.GetType()) { - debugDrawSystem->DrawWireSphere(SHAPE->GetTRS(), DRAW_COLOUR, true); - break; + case SHCollisionShape::Type::BOX: debugDrawBox(debugRenderer, COLLIDER, collisionShape); break; + case SHCollisionShape::Type::SPHERE: debugDrawSphere(debugRenderer, COLLIDER, collisionShape); break; + default: break; } - case SHCollisionShape::Type::BOX: - { - debugDrawSystem->DrawWireCube(SHAPE->GetTRS(), DRAW_COLOUR, true); - break; - } - case SHCollisionShape::Type::CAPSULE: - { - break; - } - default: break; } } } + void SHPhysicsDebugDrawSystem::drawColliderAABBs(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept + { + + } + + void SHPhysicsDebugDrawSystem::drawBroadPhaseAABBs(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept + { + + } + + void SHPhysicsDebugDrawSystem::drawContactPoints(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept + { + #ifdef SHEDITOR + + const auto* EDITOR = SHSystemManager::GetSystem(); + if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) + { + rp3dRenderer->setIsDebugItemDisplayed(rp3d::DebugRenderer::DebugItem::CONTACT_POINT, true); + const int NUM_TRIS = static_cast(rp3dRenderer->getNbTriangles()); + if (NUM_TRIS == 0) + return; + + const auto& TRI_ARRAY = rp3dRenderer->getTrianglesArray(); + for (int i = 0; i < NUM_TRIS; ++i) + debugRenderer->DrawTri(TRI_ARRAY[i].point1, TRI_ARRAY[i].point2, TRI_ARRAY[i].point3, SHColour::RED); + } + + #else + + rp3dRenderer->setIsDebugItemDisplayed(rp3d::DebugRenderer::DebugItem::CONTACT_POINT, true); + const int NUM_TRIS = static_cast(rp3dRenderer->getNbTriangles()); + if (NUM_TRIS == 0) + return; + + const auto& TRI_ARRAY = rp3dRenderer->getTrianglesArray(); + for (int i = 0; i < NUM_TRIS; ++i) + debugRenderer->DrawTri(TRI_ARRAY[i].point1, TRI_ARRAY[i].point2, TRI_ARRAY[i].point3, SHColour::RED); + + #endif + } + + void SHPhysicsDebugDrawSystem::drawContactNormals(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept + { + #ifdef SHEDITOR + const auto* EDITOR = SHSystemManager::GetSystem(); + if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) + { + rp3dRenderer->setIsDebugItemDisplayed(rp3d::DebugRenderer::DebugItem::CONTACT_NORMAL, true); + const int NUM_LINES = static_cast(rp3dRenderer->getNbLines()); + if (NUM_LINES == 0) + return; + + const auto& LINE_ARRAY = rp3dRenderer->getLinesArray(); + for (int i = 0; i < NUM_LINES; ++i) + debugRenderer->DrawLine(LINE_ARRAY[i].point1, LINE_ARRAY[i].point2, SHColour::RED); + } + + #else + + rp3dRenderer->setIsDebugItemDisplayed(rp3d::DebugRenderer::DebugItem::CONTACT_NORMAL, true); + const int NUM_LINES = static_cast(rp3dRenderer->getNbLines()); + if (NUM_LINES == 0) + return; + + const auto& LINE_ARRAY = rp3dRenderer->getLinesArray(); + for (int i = 0; i < NUM_LINES; ++i) + debugRenderer->DrawLine(LINE_ARRAY[i].point1, LINE_ARRAY[i].point2, SHColour::RED); + + #endif + } + + void SHPhysicsDebugDrawSystem::drawRaycasts(SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept + { + auto* physicsSystem = SHSystemManager::GetSystem(); + if (!physicsSystem) + { + SHLOG_ERROR("Unable to retrieve physics system for debug drawing raycasts!") + return; + } + + const SHColour& RAY_COLOUR = SHColour::ORANGE; + + // Draw all raycast pairs + for (const auto& [ray, raycastResult] : physicsSystem->raycaster.GetRaycasts()) + { + // If infinity, it is an infinite raycast. If not, render the distance in raycastResult. + // Ignore the hit variable as it will always correspond to the length of the raycast, hit or miss. + const float RENDER_DIST = raycastResult.distance == std::numeric_limits::infinity() ? SHRay::MAX_RAYCAST_DIST : raycastResult.distance; + const SHVec3 END_POS = ray.position + (ray.direction * RENDER_DIST); + + debugRenderer->DrawLine(ray.position, END_POS, RAY_COLOUR); + } + } + + + void SHPhysicsDebugDrawSystem::generateBox() noexcept + { + boxVertices[0] = { 0.5f, 0.5f, -0.5f }; // TOP_RIGHT_BACK + boxVertices[1] = { -0.5f, 0.5f, -0.5f }; // TOP_LEFT_BACK + boxVertices[2] = { 0.5f, -0.5f, -0.5f }; // BTM_RIGHT_BACK + boxVertices[3] = { -0.5f, -0.5f, -0.5f }; // BTM_LEFT_BACK + boxVertices[4] = { 0.5f, 0.5f, 0.5f }; // TOP_RIGHT_FRONT + boxVertices[5] = { -0.5f, 0.5f, 0.5f }; // TOP_LEFT_FRONT + boxVertices[6] = { 0.5f, -0.5f, 0.5f }; // BTM_RIGHT_FRONT + boxVertices[7] = { -0.5f, -0.5f, 0.5f }; // BTM_LEFT_FRONT + } + + void SHPhysicsDebugDrawSystem::debugDrawBox(SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept + { + auto* BOX = reinterpret_cast(collisionShape.GetShape()); + + // Calculate final position & orientation + const SHVec3 COLLIDER_POS = colliderComponent.GetPosition(); + const SHVec3 BOX_POS = collisionShape.GetPositionOffset(); + const SHQuaternion COLLIDER_ROT = colliderComponent.GetOrientation(); + const SHQuaternion BOX_ROT = SHQuaternion::FromEuler(collisionShape.GetRotationOffset()); + + + const SHMatrix COLLIDER_TR = SHMatrix::Rotate(COLLIDER_ROT) * SHMatrix::Translate(COLLIDER_POS); + const SHMatrix BOX_TRS = SHMatrix::Scale(BOX->GetWorldExtents() * 2.0f) * SHMatrix::Rotate(BOX_ROT) * SHMatrix::Translate(BOX_POS); + + const SHMatrix FINAL_TRS = BOX_TRS * COLLIDER_TR; + + const SHColour COLLIDER_COLOUR = collisionShape.IsTrigger() ? SHColour::PURPLE : SHColour::GREEN; + + std::array transformedVertices; + for (uint32_t i = 0; i < NUM_BOX_VERTICES / 2; ++i) + { + const uint32_t IDX1 = i; + const uint32_t IDX2 = i + NUM_BOX_VERTICES / 2; + + transformedVertices[IDX1] = SHVec3::Transform(boxVertices[IDX1], FINAL_TRS); + transformedVertices[IDX2] = SHVec3::Transform(boxVertices[IDX2], FINAL_TRS); + + // Draw 4 line to connect the quads + debugRenderer->DrawLine(transformedVertices[IDX1], transformedVertices[IDX2], COLLIDER_COLOUR); + } + + // A, B, C, D + std::array backQuad { transformedVertices[0], transformedVertices[1], transformedVertices[3], transformedVertices[2] }; + debugRenderer->DrawLineLoop(backQuad.begin(), backQuad.end(), COLLIDER_COLOUR); + + // E, F, G, H + std::array frontQuad { transformedVertices[4], transformedVertices[5], transformedVertices[7], transformedVertices[6] }; + debugRenderer->DrawLineLoop(frontQuad.begin(), frontQuad.end(), COLLIDER_COLOUR); + } + + void SHPhysicsDebugDrawSystem::debugDrawSphere(SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept + { + auto* SPHERE = reinterpret_cast(collisionShape.GetShape()); + + const SHColour COLLIDER_COLOUR = collisionShape.IsTrigger() ? SHColour::PURPLE : SHColour::GREEN; + + // Calculate final position & orientation + const SHQuaternion FINAL_ROT = colliderComponent.GetOrientation() * SHQuaternion::FromEuler(collisionShape.GetRotationOffset()); + const SHMatrix TR = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(colliderComponent.GetPosition()); + + /* #KWFix */ + //debugRenderer->DrawSphere(COLLIDER_COLOUR, SHVec3::Transform(collisionShape.GetPositionOffset(), TR), SPHERE->GetWorldRadius()); + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index 336dff0c..dc703092 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -10,18 +10,14 @@ #pragma once -#include +#include // Project Headers -#include "ECS_Base/Entity/SHEntity.h" -#include "ECS_Base/System/SHSystem.h" #include "ECS_Base/System/SHSystemRoutine.h" -#include "Events/SHEvent.h" #include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" -#include "Physics/Collision/SHCollider.h" -#include "Physics/Collision/SHPhysicsRaycastResult.h" -#include "Physics/Collision/Shapes/SHSphere.h" - +#include "Math/SHColour.h" +#include "SHPhysicsSystem.h" +#include "Tools/Utilities/SHUtilities.h" namespace SHADE { @@ -32,55 +28,63 @@ namespace SHADE class SH_API SHPhysicsDebugDrawSystem final : public SHSystem { public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ - enum class DebugDrawFlags : uint8_t + enum class DebugDrawFlags { - COLLIDERS = 0x02 - , CONTACTS = 0x04 - , RAYCASTS = 0x08 - , BROADPHASE = 0x10 + COLLIDER + , COLLIDER_AABB + , BROAD_PHASE_AABB + , CONTACT_POINTS + , CONTACT_NORMALS + , RAYCASTS + + , NUM_FLAGS }; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHPhysicsDebugDrawSystem () noexcept; - ~SHPhysicsDebugDrawSystem() noexcept = default; + SHPhysicsDebugDrawSystem() noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] bool IsDebugDrawActive () const noexcept; - [[nodiscard]] bool GetFlagState (DebugDrawFlags flag) const noexcept; + bool GetDebugDrawFlag(DebugDrawFlags flag) const noexcept; + /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetFlagState (DebugDrawFlags flag, bool state) noexcept; + void SetDebugDrawFlag(DebugDrawFlags flag, bool value) noexcept; /*---------------------------------------------------------------------------------*/ - /* Member Functions */ + /* Function Members */ /*---------------------------------------------------------------------------------*/ - void Init () override; - void Exit () override; - - void AddRaycast (const SHRay& ray, const SHPhysicsRaycastResult& result) noexcept; + void Init() override; + void Exit() override; /*---------------------------------------------------------------------------------*/ /* System Routines */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * If the editor is enabled, this routine invokes debug drawing for colliders and collision information. - */ - class SH_API PhysicsDebugDraw final : public SHSystemRoutine + class SH_API PhysicsDebugDrawRoutine final : public SHSystemRoutine { public: - PhysicsDebugDraw(); + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + PhysicsDebugDrawRoutine(); + + /*-------------------------------------------------------------------------------*/ + /* Function Members */ + /*-------------------------------------------------------------------------------*/ void Execute(double dt) noexcept override; }; @@ -89,59 +93,46 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - union DebugDrawInfo - { - struct Contact - { - SHVec3 worldPos; - SHVec3 normal; - } contact; - - struct Raycast - { - SHVec3 start; - SHVec3 end; - } raycast; - }; - - using Colliders = std::unordered_set; - using Raycasts = std::vector; - using Contacts = std::vector; - - enum class Colours - { - COLLIDER - , TRIGGER - , CONTACT - , RAYCAST - , BROADPHASE - - , COUNT - }; + using DebugDrawFunction = void(*)(SHDebugDrawSystem*, rp3d::DebugRenderer*) noexcept; /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - static constexpr uint8_t ACTIVE_FLAG = 0x01; + static constexpr int NUM_FLAGS = SHUtilities::ConvertEnum(DebugDrawFlags::NUM_FLAGS); + static const DebugDrawFunction drawFunctions[NUM_FLAGS]; - static const SHColour DEBUG_DRAW_COLOURS[static_cast(Colours::COUNT)]; + // SHAPES INFO - // 0 0 0 drawBroadphase drawRaycasts drawContacts drawAllColliders debugDrawActive - uint8_t flags; - - Colliders collidersToDraw; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - SHEventHandle onColliderDraw(SHEventPtr onColliderDrawEvent); - - static void drawCollider (SHDebugDrawSystem* debugDrawSystem, const SHCollider& collider) noexcept; - static void drawRaycast (SHDebugDrawSystem* debugDrawSystem, const DebugDrawInfo::Raycast& raycastInfo) noexcept; + static constexpr size_t NUM_BOX_VERTICES = 8; + static SHVec3 boxVertices[NUM_BOX_VERTICES]; + + uint8_t debugDrawFlags; + SHPhysicsSystem* physicsSystem; + SHColour debugColours[NUM_FLAGS]; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + // Generic Draw Functions + + static void drawColliders (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; + static void drawColliderAABBs (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; + static void drawBroadPhaseAABBs (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; + static void drawContactPoints (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; + static void drawContactNormals (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; + static void drawRaycasts (SHDebugDrawSystem* debugRenderer, rp3d::DebugRenderer* rp3dRenderer) noexcept; + + // Shape Generation Functions + + static void generateBox () noexcept; + + // Shape Draw Functions + + static void debugDrawBox (SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept; + static void debugDrawSphere (SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept; }; - } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 97b555af..768c9b77 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -19,9 +19,9 @@ #include "ECS_Base/Managers/SHEntityManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.h" -#include "Physics/Collision/SHCollisionSpace.h" -#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" -#include "Physics/Interface/SHColliderComponent.h" +#include "Physics/Collision/SHCollisionTagMatrix.h" +#include "Physics/SHPhysicsEvents.h" +#include "Scene/SHSceneManager.h" #include "Scripting/SHScriptEngine.h" namespace SHADE @@ -30,26 +30,11 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHPhysicsSystem::SHPhysicsSystem() noexcept + SHPhysicsSystem::SHPhysicsSystem() : worldUpdated { false } , interpolationFactor { 0.0 } , fixedDT { DEFAULT_FIXED_STEP } - , physicsWorld { nullptr } - , collisionSpace { nullptr } - { - // Add more events here to register them - - eventFunctions[0] = { &SHPhysicsSystem::onComponentAdded , SH_COMPONENT_ADDED_EVENT }; - eventFunctions[1] = { &SHPhysicsSystem::onComponentRemoved, SH_COMPONENT_REMOVED_EVENT }; - eventFunctions[2] = { &SHPhysicsSystem::onSceneInit , SH_SCENE_INIT_POST }; - eventFunctions[3] = { &SHPhysicsSystem::onSceneExit , SH_SCENE_EXIT_POST }; - } - - SHPhysicsSystem::~SHPhysicsSystem() noexcept - { - delete collisionSpace; - delete physicsWorld; - } + {} /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ @@ -60,58 +45,45 @@ namespace SHADE return 1.0 / fixedDT; } - double SHPhysicsSystem::GetFixedDT() const noexcept + const SHPhysicsWorldState::WorldSettings& SHPhysicsSystem::GetWorldSettings() const noexcept { - return fixedDT; + return worldState.settings; } - const std::vector& SHPhysicsSystem::GetTriggerInfo() const noexcept + const std::vector& SHPhysicsSystem::GetAllCollisionInfo() const noexcept { - return physicsWorld->GetTriggerEvents(); + return collisionListener.GetCollisionInfoContainer(); } - const std::vector& SHPhysicsSystem::GetCollisionInfo() const noexcept + const std::vector& SHPhysicsSystem::GetAllTriggerInfo() const noexcept { - return physicsWorld->GetCollisionEvents(); + return collisionListener.GetTriggerInfoContainer(); + } + + const SHPhysicsObject* SHPhysicsSystem::GetPhysicsObject(EntityID eid) noexcept + { + return objectManager.GetPhysicsObject(eid); + } + + + const SHPhysicsObjectManager::PhysicsObjectEntityMap& SHPhysicsSystem::GetPhysicsObjects() const noexcept + { + return objectManager.physicsObjects; } /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsSystem::SetFixedUpdateRate(double fixedUpdateRate) noexcept + void SHPhysicsSystem::SetFixedUpdateRate(double fixedUpdateRate) noexcept { - // Handle invalid input - if (fixedUpdateRate <= 0.0) - { - SHLOGV_WARNING("Invalid value for setting fixed update rate! Fixed update rate unchanged.") - return; - } - fixedDT = 1.0 / fixedUpdateRate; - - // Handle potential incorrect / unintended input - if (fixedDT > 1.0) - { - SHLOGV_WARNING("Fixed Update Rate Time is set below 1. This may result in undesirable behaviour.") - } } - void SHPhysicsSystem::SetFixedDT(double fixedDt) noexcept + void SHPhysicsSystem::SetWorldSettings(const SHPhysicsWorldState::WorldSettings& settings) noexcept { - if (fixedDt <= 0.0) - { - SHLOGV_WARNING("Invalid value for setting fixed delta time! Fixed delta time unchanged.") - return; - } - - fixedDT = fixedDt; - - // Handle potential incorrect / unintended input - if (fixedDT > 1.0) - { - SHLOGV_WARNING("Fixed Delta Time is set above 1. This may result in undesirable behaviour.") - } + worldState.settings = settings; + worldState.UpdateSettings(); } /*-----------------------------------------------------------------------------------*/ @@ -120,271 +92,390 @@ namespace SHADE void SHPhysicsSystem::Init() { - // TODO(Diren): Consider using a non-static collision tag matrix. // Initialise collision tags std::filesystem::path defaultCollisionTagNameFilePath { ASSET_ROOT }; defaultCollisionTagNameFilePath.append("CollisionTags.SHConfig"); SHCollisionTagMatrix::Init(defaultCollisionTagNameFilePath); - // Register Events - for (int i = 0; i < NUM_EVENT_FUNCTIONS; ++i) - { - const std::shared_ptr EVENT_RECEIVER = std::make_shared>(this, eventFunctions[i].first); - const ReceiverPtr EVENT_RECEIVER_PTR = std::dynamic_pointer_cast(EVENT_RECEIVER); + // Link Physics Object Manager with System & Raycaster + objectManager.SetFactory(factory); + raycaster.SetObjectManager(&objectManager); - SHEventManager::SubscribeTo(eventFunctions[i].second, EVENT_RECEIVER_PTR); - } + // Link Collision Listener with System + collisionListener.BindToSystem(this); + + // Subscribe to component events + const std::shared_ptr ADD_COMPONENT_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::addPhysicsComponent) }; + const ReceiverPtr ADD_COMPONENT_RECEIVER_PTR = std::dynamic_pointer_cast(ADD_COMPONENT_RECEIVER); + SHEventManager::SubscribeTo(SH_COMPONENT_ADDED_EVENT, ADD_COMPONENT_RECEIVER_PTR); + + const std::shared_ptr REMOVE_COMPONENT_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::removePhysicsComponent) }; + const ReceiverPtr REMOVE_COMPONENT_RECEIVER_PTR = std::dynamic_pointer_cast(REMOVE_COMPONENT_RECEIVER); + SHEventManager::SubscribeTo(SH_COMPONENT_REMOVED_EVENT, REMOVE_COMPONENT_RECEIVER_PTR); + + #ifdef SHEDITOR + + // Subscribe to Editor State Change Events + const std::shared_ptr ON_PLAY_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::onPlay) }; + const ReceiverPtr ON_PLAY_RECEIVER_PTR = std::dynamic_pointer_cast(ON_PLAY_RECEIVER); + SHEventManager::SubscribeTo(SH_EDITOR_ON_PLAY_EVENT, ON_PLAY_RECEIVER_PTR); + + const std::shared_ptr ON_STOP_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::onStop) }; + const ReceiverPtr ON_STOP_RECEIVER_PTR = std::dynamic_pointer_cast(ON_STOP_RECEIVER); + SHEventManager::SubscribeTo(SH_EDITOR_ON_STOP_EVENT, ON_STOP_RECEIVER_PTR); + + #endif + } void SHPhysicsSystem::Exit() { + worldState.DestroyWorld(factory); + // Write collision tag names to file std::filesystem::path defaultCollisionTagNameFilePath { ASSET_ROOT }; defaultCollisionTagNameFilePath.append("CollisionTags.SHConfig"); SHCollisionTagMatrix::Exit(defaultCollisionTagNameFilePath); } - void SHPhysicsSystem::ForceUpdate() + void SHPhysicsSystem::BuildScene(SHSceneGraph& sceneGraph) { - if (!physicsWorld) - return; - - auto* scriptingSystem = SHSystemManager::GetSystem(); - - if (scriptingSystem == nullptr) + static const auto BUILD_NEW_SCENE_PHYSICS_OBJECT = [&](SHSceneNode* node) { - SHLOGV_ERROR("Unable to invoke collision and trigger script events due to missing SHScriptEngine!"); + const EntityID EID = node->GetEntityID(); + + if (SHComponentManager::HasComponent(EID)) + objectManager.AddRigidBody(EID); + + if (SHComponentManager::HasComponent(EID)) + { + objectManager.AddCollider(EID); + + auto* COLLIDER = SHComponentManager::GetComponent(EID); + for (size_t i = 0; i < COLLIDER->GetCollisionShapes().size(); ++i) + objectManager.AddCollisionShape(EID, i); + } + + }; + + //////////////////////////////// + + // Destroy an existing world + if (worldState.world != nullptr) + { + objectManager.RemoveAllObjects(); + objectManager.SetWorld(nullptr); + + collisionListener.ClearContainers(); + raycaster.ClearFrame(); + + worldState.DestroyWorld(factory); } - scriptingSystem->ExecuteFixedUpdates(); - physicsWorld->Step(static_cast(fixedDT)); + worldState.CreateWorld(factory); +#ifdef _PUBLISH + worldState.world->setIsDebugRenderingEnabled(false); +#else + worldState.world->setIsDebugRenderingEnabled(true); +#endif - const auto& RIGIDBODY_DENSE = SHComponentManager::GetDense(); - for (auto& rigidBodyComponent : RIGIDBODY_DENSE) + // Link Collision Listener & Raycaster + collisionListener.BindToWorld(worldState.world); + raycaster.BindToWorld(worldState.world); + + // Link with object manager & create all physics objects + objectManager.SetWorld(worldState.world); + + // When building a scene, clear the object manager command queue and build scene objects again. + // This is done to avoid duplicate adds. + while (!objectManager.commandQueue.empty()) + objectManager.commandQueue.pop(); + + sceneGraph.Traverse(BUILD_NEW_SCENE_PHYSICS_OBJECT); + objectManager.UpdateCommands(); + } + + void SHPhysicsSystem::ForceBuild(SHSceneGraph& sceneGraph) + { + // HACK: Band-aid fix. To be removed. + objectManager.UpdateCommands(); + } + + + void SHPhysicsSystem::ForceUpdate() + { + if (!worldState.world) { - const EntityID EID = rigidBodyComponent.GetEID(); + SHLOGV_ERROR("Unable to force update without a Physics world!") + return; + } - // Skip missing transforms - auto* transformComponent = SHComponentManager::GetComponent_s(EID); - if (!transformComponent) - continue; + auto* scriptingSystem = SHSystemManager::GetSystem(); + if (scriptingSystem == nullptr) + { + SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!"); + } - // Skip invalid bodies (Should not occur) - if (!rigidBodyComponent.rigidBody) - continue; + // Force the physics world to update once + if (scriptingSystem != nullptr) + scriptingSystem->ExecuteFixedUpdates(); - // Skip inactive bodies - const bool IS_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive(EID); - if (!IS_ACTIVE) - continue; + worldState.world->update(static_cast(fixedDT)); - const SHMotionState& MOTION_STATE = rigidBodyComponent.rigidBody->GetMotionState(); + // Sync transforms. No interpolation applied here + for (auto& [entityID, physicsObject] : objectManager.physicsObjects) + { + auto* transformComponent = SHComponentManager::GetComponent_s(entityID); + auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); + auto* colliderComponent = SHComponentManager::GetComponent_s(entityID); - // Skip objects that have not moved - if (!MOTION_STATE) - continue; + const auto& CURRENT_TF = physicsObject.GetRigidBody()->getTransform(); + const auto& RENDER_POS = CURRENT_TF.getPosition(); + const auto& RENDER_ROT = CURRENT_TF.getOrientation(); - // We ignore interpolations here because we are only stepping once - transformComponent->SetWorldPosition(MOTION_STATE.position); - transformComponent->SetWorldOrientation(MOTION_STATE.orientation); + // Cache transform + physicsObject.prevTransform = CURRENT_TF; + + // Sync with physics components + if (rigidBodyComponent && SHSceneManager::CheckNodeAndComponentsActive(entityID)) + { + rigidBodyComponent->position = RENDER_POS; + rigidBodyComponent->orientation = RENDER_ROT; + } + + if (colliderComponent && SHSceneManager::CheckNodeAndComponentsActive(entityID)) + { + colliderComponent->position = RENDER_POS; + colliderComponent->orientation = RENDER_ROT; + } + + // Set transform for rendering + if (transformComponent) + { + transformComponent->SetWorldPosition(RENDER_POS); + transformComponent->SetWorldOrientation(RENDER_ROT); + } } } - const std::vector& SHPhysicsSystem::Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept + SHPhysicsRaycastResult SHPhysicsSystem::Raycast(const SHRay& ray, float distance, const SHCollisionTag& collisionTag) noexcept { - auto& results = collisionSpace->Raycast(info); + return raycaster.Raycast(ray, distance, collisionTag); + } - // Load start and end points into the container for debug drawing - #ifdef SHEDITOR + SHPhysicsRaycastResult SHPhysicsSystem::Linecast(const SHVec3& start, const SHVec3& end, const SHCollisionTag& collisionTag) noexcept + { + return raycaster.Linecast(start, end, collisionTag); + } - SHVec3 endPos = info.ray.position + info.ray.direction * SHRay::MAX_RAYCAST_DIST; + SHPhysicsRaycastResult SHPhysicsSystem::ColliderRaycast(EntityID eid, const SHRay& ray, float distance) noexcept + { + return raycaster.ColliderRaycast(eid, ray, distance); + } - if (!results.empty()) - endPos = results.back().position; - - raycastHits.emplace_back(info.ray.position, endPos); - - #endif + SHPhysicsRaycastResult SHPhysicsSystem::ColliderRaycast(EntityID eid, int shapeIndex, const SHRay& ray, float distance) noexcept + { + return raycaster.ColliderRaycast(eid, shapeIndex, ray, distance); + } - return results; + SHPhysicsRaycastResult SHPhysicsSystem::ColliderLinecast(EntityID eid, const SHVec3& start, const SHVec3& end) noexcept + { + return raycaster.ColliderLinecast(eid, start, end); + } + + SHPhysicsRaycastResult SHPhysicsSystem::ColliderLinecast(EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept + { + return raycaster.ColliderLinecast(eid, shapeIndex, start, end); + } + + void SHPhysicsSystem::AddCollisionShape(EntityID eid, int shapeIndex) + { + static const auto ADD_SHAPE = [&](EntityID entityID, int index) + { + objectManager.AddCollisionShape(entityID, index); + + auto* colliderComponent = SHComponentManager::GetComponent(entityID); + auto& collisionShape = colliderComponent->GetCollisionShape(index); + + const SHPhysicsColliderAddedEvent COLLIDER_ADDED_EVENT_DATA + { + .entityID = entityID + , .colliderType = collisionShape.GetType() + , .colliderIndex = index + }; + + SHEventManager::BroadcastEvent(COLLIDER_ADDED_EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT); + }; + + #ifdef SHEDITOR + + const auto* EDITOR = SHSystemManager::GetSystem(); + if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) + ADD_SHAPE(eid, shapeIndex); + + #else + + ADD_SHAPE(eid, shapeIndex); + + #endif + } + + void SHPhysicsSystem::RemoveCollisionShape(EntityID eid, int shapeIndex) + { + static const auto REMOVE_SHAPE = [&](EntityID entityID, int index) + { + objectManager.RemoveCollisionShape(entityID, index); + + const SHPhysicsColliderRemovedEvent COLLIDER_REMOVED_EVENT_DATA + { + .entityID = entityID + , .colliderIndex = index + }; + + SHEventManager::BroadcastEvent(COLLIDER_REMOVED_EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT); + }; + + #ifdef SHEDITOR + + const auto* EDITOR = SHSystemManager::GetSystem(); + if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) + REMOVE_SHAPE(eid, shapeIndex); + + #else + + REMOVE_SHAPE(eid, shapeIndex); + + #endif } /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHEventHandle SHPhysicsSystem::onSceneInit(SHEventPtr onSceneInitEvent) + SHEventHandle SHPhysicsSystem::addPhysicsComponent(SHEventPtr addComponentEvent) noexcept { - /* - * If a world already exists, destroy it - * Recreate the world. - * - * If there is an editor and editor mode is already playing, link all entities with the world immediately. - */ + const auto& EVENT_DATA = reinterpret_cast*>(addComponentEvent.get()); - // Destroy an existing world if there is one. - //! This should almost never happen. - if (physicsWorld) + static const auto RIGID_BODY_ID = ComponentFamily::GetID(); + static const auto COLLIDER_ID = ComponentFamily::GetID(); + + const auto ADDED_ID = EVENT_DATA->data->addedComponentType; + const bool IS_PHYSICS_COMPONENT = ADDED_ID == RIGID_BODY_ID || ADDED_ID == COLLIDER_ID; + if (IS_PHYSICS_COMPONENT) { - // Remove all references of physics objects from the world - for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) - { - if (PHYSICS_OBJECT.rigidBody) - physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); - } + const EntityID EID = EVENT_DATA->data->eid; - delete physicsWorld; - physicsWorld = nullptr; + // We only add tell the physics object manager to add a component if the scene is played + + #ifdef SHEDITOR + + const auto* EDITOR = SHSystemManager::GetSystem(); + + if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) + ADDED_ID == RIGID_BODY_ID ? objectManager.AddRigidBody(EID) : objectManager.AddCollider(EID); + + #else + + ADDED_ID == RIGID_BODY_ID ? objectManager.AddRigidBody(EID) : objectManager.AddCollider(EID); + + #endif } - if (collisionSpace) - { - for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) - { - if (PHYSICS_OBJECT.collider) - collisionSpace->RemoveCollider(PHYSICS_OBJECT.collider); - } - - delete collisionSpace; - collisionSpace = nullptr; - } - - // Create the physics world & collision space - physicsWorld = new SHPhysicsWorld; - collisionSpace = new SHCollisionSpace; - - physicsWorld->SetCollisionSpace(collisionSpace); - - // Immediately add all existing bodies and colliders to the world. - // Since we recreated the scene and the world, the initial data has been reset and determinism is guaranteed. - // Only if the current scene data changes, then so would the results of the simulation. - - for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) - { - if (PHYSICS_OBJECT.rigidBody) - physicsWorld->AddRigidBody(PHYSICS_OBJECT.rigidBody); - - if (PHYSICS_OBJECT.collider) - collisionSpace->AddCollider(PHYSICS_OBJECT.collider); - } - - return onSceneInitEvent.get()->handle; + return EVENT_DATA->handle; } - SHEventHandle SHPhysicsSystem::onSceneExit(SHEventPtr onSceneExitEvent) + SHEventHandle SHPhysicsSystem::removePhysicsComponent(SHEventPtr removeComponentEvent) noexcept { - /* - * Destroy the physics world. - * Destroy all physics objects. - */ + const auto& EVENT_DATA = reinterpret_cast*>(removeComponentEvent.get()); - for (const auto& PHYSICS_OBJECT : physicsObjectManager.GetPhysicsObjects() | std::views::values) + static const auto RIGID_BODY_ID = ComponentFamily::GetID(); + static const auto COLLIDER_ID = ComponentFamily::GetID(); + + const auto REMOVED_ID = EVENT_DATA->data->removedComponentType; + const bool IS_PHYSICS_COMPONENT = REMOVED_ID == RIGID_BODY_ID || REMOVED_ID == COLLIDER_ID; + if (IS_PHYSICS_COMPONENT) { - if (PHYSICS_OBJECT.rigidBody) - physicsWorld->RemoveRigidBody(PHYSICS_OBJECT.rigidBody); + const EntityID EID = EVENT_DATA->data->eid; - if (PHYSICS_OBJECT.collider) - collisionSpace->RemoveCollider(PHYSICS_OBJECT.collider); + // We only add tell the physics object manager to remove a component if the scene is played + + #ifdef SHEDITOR + + const auto* EDITOR = SHSystemManager::GetSystem(); + + if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) + REMOVED_ID == RIGID_BODY_ID ? objectManager.RemoveRigidBody(EID) : objectManager.RemoveCollider(EID); + + #else + + REMOVED_ID == RIGID_BODY_ID ? objectManager.RemoveRigidBody(EID) : objectManager.RemoveCollider(EID); + + #endif } - delete collisionSpace; - collisionSpace = nullptr; - - delete physicsWorld; - physicsWorld = nullptr; - - return onSceneExitEvent.get()->handle; + return EVENT_DATA->handle; } - SHEventHandle SHPhysicsSystem::onComponentAdded(SHEventPtr onComponentAddedEvent) + SHEventHandle SHPhysicsSystem::onPlay(SHEventPtr onPlayEvent) { - static const auto RIGID_BODY_COMPONENT_ID = ComponentFamily::GetID(); - static const auto COLLIDER_COMPONENT_ID = ComponentFamily::GetID(); - - const auto& EVENT_DATA = reinterpret_cast*>(onComponentAddedEvent.get())->data; - - const auto ADDED_ID = EVENT_DATA->addedComponentType; - - const bool IS_RIGID_BODY = ADDED_ID == RIGID_BODY_COMPONENT_ID; - const bool IS_COLLIDER = ADDED_ID == COLLIDER_COMPONENT_ID; - - // TODO: Hull Collider - - // Check if its a physics component - const bool IS_PHYSICS_COMPONENT = IS_RIGID_BODY || IS_COLLIDER; - if (!IS_PHYSICS_COMPONENT) - return onComponentAddedEvent.get()->handle; - - const EntityID EID = EVENT_DATA->eid; - - // Link engine components with physics object component - if (IS_RIGID_BODY) + static const auto BUILD_PHYSICS_OBJECT = [&](SHSceneNode* node) { - physicsObjectManager.AddRigidBody(EID); + const EntityID EID = node->GetEntityID(); - if (physicsWorld) + if (SHComponentManager::HasComponent(EID)) + objectManager.AddRigidBody(EID); + + if (SHComponentManager::HasComponent(EID)) { - auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody; - physicsWorld->AddRigidBody(rigidBody); + objectManager.AddCollider(EID); + + auto* COLLIDER = SHComponentManager::GetComponent(EID); + for (size_t i = 0; i < COLLIDER->GetCollisionShapes().size(); ++i) + objectManager.AddCollisionShape(EID, i); } - } + }; - if (IS_COLLIDER) - { - physicsObjectManager.AddCollider(EID, SHCollider::Type::COMPOSITE); + //////////////////////////////// - if (collisionSpace) - { - auto* collider = physicsObjectManager.GetPhysicsObject(EID)->collider; - collisionSpace->AddCollider(collider); - } - } + // Create physics world + if (worldState.world != nullptr) + return onPlayEvent->handle; - return onComponentAddedEvent.get()->handle; + worldState.CreateWorld(factory); +#ifdef _PUBLISH + worldState.world->setIsDebugRenderingEnabled(false); +#else + worldState.world->setIsDebugRenderingEnabled(true); +#endif + + // Link Collision Listener & Raycaster + collisionListener.BindToWorld(worldState.world); + raycaster.BindToWorld(worldState.world); + + // Link with object manager & create all physics objects + objectManager.SetWorld(worldState.world); + + // Build scene + SHSceneManager::GetCurrentSceneGraph().Traverse(BUILD_PHYSICS_OBJECT); + objectManager.UpdateCommands(); + + return onPlayEvent->handle; } - SHEventHandle SHPhysicsSystem::onComponentRemoved(SHEventPtr onComponentRemovedEvent) + SHEventHandle SHPhysicsSystem::onStop(SHEventPtr onStopEvent) { - static const auto RIGID_BODY_COMPONENT_ID = ComponentFamily::GetID(); - static const auto COLLIDER_COMPONENT_ID = ComponentFamily::GetID(); + // Remove all physics objects + objectManager.RemoveAllObjects(); + objectManager.SetWorld(nullptr); - const auto& EVENT_DATA = reinterpret_cast*>(onComponentRemovedEvent.get())->data; + // Clear all collision info + // Collision listener is automatically unbound when world is destroyed + collisionListener.ClearContainers(); + raycaster.ClearFrame(); - const auto REMOVED_DATA = EVENT_DATA->removedComponentType; + // Destroy the world + worldState.DestroyWorld(factory); - const bool IS_RIGID_BODY = REMOVED_DATA == RIGID_BODY_COMPONENT_ID; - const bool IS_COLLIDER = REMOVED_DATA == COLLIDER_COMPONENT_ID; - - // Check if its a physics component - const bool IS_PHYSICS_COMPONENT = IS_RIGID_BODY || IS_COLLIDER; - if (!IS_PHYSICS_COMPONENT) - return onComponentRemovedEvent.get()->handle; - - const EntityID EID = EVENT_DATA->eid; - - if (IS_RIGID_BODY) - { - if (physicsWorld) - { - auto* rigidBody = physicsObjectManager.GetPhysicsObject(EID)->rigidBody; - physicsWorld->RemoveRigidBody(rigidBody); - } - - physicsObjectManager.RemoveRigidBody(EID); - } - - if (IS_COLLIDER) - { - if (collisionSpace) - { - auto* collider = physicsObjectManager.GetPhysicsObject(EID)->collider; - collisionSpace->RemoveCollider(collider); - } - - physicsObjectManager.RemoveCollider(EID); - } - - return onComponentRemovedEvent.get()->handle; + return onStopEvent->handle; } - -} // namespace SHADE +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index 712727ec..99db493e 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -10,16 +10,24 @@ #pragma once +#include +#include + +// External Dependencies +#include + // Project Headers -#include "ECS_Base/System/SHSystem.h" #include "ECS_Base/System/SHSystemRoutine.h" #include "ECS_Base/System/SHFixedSystemRoutine.h" -#include "Events/SHEvent.h" -#include "Physics/Dynamics/SHPhysicsWorld.h" -#include "Physics/Interface/PhysicsObject/SHPhysicsObjectManager.h" +#include "Math/Transform/SHTransformComponent.h" +#include "Physics/Collision/SHCollisionListener.h" +#include "Physics/Collision/SHPhysicsRaycaster.h" #include "Physics/Interface/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" +#include "Physics/PhysicsObject/SHPhysicsObjectManager.h" +#include "Physics/SHPhysicsWorld.h" +#include "Scene/SHSceneGraph.h" namespace SHADE { @@ -27,10 +35,6 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates a system for running and managing the physics simulation of the engine. - */ class SH_API SHPhysicsSystem final : public SHSystem { private: @@ -45,146 +49,248 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHPhysicsSystem () noexcept; - ~SHPhysicsSystem() noexcept; + SHPhysicsSystem(); /*---------------------------------------------------------------------------------*/ /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] double GetFixedUpdateRate() const noexcept; - [[nodiscard]] double GetFixedDT () const noexcept; + [[nodiscard]] double GetFixedUpdateRate () const noexcept; + [[nodiscard]] const SHPhysicsWorldState::WorldSettings& GetWorldSettings () const noexcept; - [[nodiscard]] const std::vector& GetTriggerInfo () const noexcept; - [[nodiscard]] const std::vector& GetCollisionInfo () const noexcept; + [[nodiscard]] const std::vector& GetAllCollisionInfo () const noexcept; + [[nodiscard]] const std::vector& GetAllTriggerInfo () const noexcept; + [[nodiscard]] const SHPhysicsObject* GetPhysicsObject (EntityID eid) noexcept; + [[nodiscard]] const SHPhysicsObjectManager::PhysicsObjectEntityMap& GetPhysicsObjects () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetFixedUpdateRate(double fixedUpdateRate) noexcept; - void SetFixedDT(double fixedDt) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Member Functions */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * - Initialises the static collision tag matrix. - * - Registers the system to catch specific events. - */ - void Init() override; - void Exit() override; - - /** - * @brief - * Forces the world to take a single step. Interpolation of bodies cannot be done when - * forcing an update. - */ - void ForceUpdate(); - - /** - * @brief - * Casts a ray into the collision space. - * @param info - * Contains the information for the raycast. - * @return - * A container of the objects hit by the ray. If nothing was hit, the container - * will be empty. - */ - [[nodiscard]] const std::vector& Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* System Routines */ - /*---------------------------------------------------------------------------------*/ - - /** - * @brief - * The physics update routine that runs before the simulation is updated. - * This is always running, regardless of the editor state. - *
- * This update game logic is applied before the simulation runs. - */ - class SH_API PhysicsPreUpdate final : public SHSystemRoutine - { - public: - PhysicsPreUpdate(); - void Execute(double dt) noexcept override; - }; - - /** - * @brief - * The physics update routine that runs at a fixed rate. This is where the main - * simulation runs. If delta time is large enough, this may run more than once per - * frame. If delta time is small enough, this may not run at all. - */ - class SH_API PhysicsUpdate final : public SHFixedSystemRoutine - { - public: - PhysicsUpdate(); - void Execute(double dt) noexcept override; - }; - - /** - * @brief - * The physics update that runs after the simulation. This sets the rendering - * transforms and sends messages to scripting system for collision & trigger events.
- * - */ - class SH_API PhysicsPostUpdate final : public SHSystemRoutine - { - public: - PhysicsPostUpdate(); - void Execute(double dt) noexcept override; - }; - - private: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - - using EventFunctionPair = std::pair; - - struct RaycastHit - { - SHVec3 start; - SHVec3 end; - }; - - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - static constexpr int NUM_EVENT_FUNCTIONS = 4; - - // Event function container for cleanly registering to events - EventFunctionPair eventFunctions[NUM_EVENT_FUNCTIONS]; - - // System data - bool worldUpdated; - double interpolationFactor; - double fixedDT; - - // Sub-systems / managers - - SHPhysicsWorld* physicsWorld; - SHCollisionSpace* collisionSpace; - SHPhysicsObjectManager physicsObjectManager; - - // For the debug drawer to draw rays - #ifdef SHEDITOR - std::vector raycastHits; - #endif + void SetFixedUpdateRate (double fixedUpdateRate) noexcept; + void SetWorldSettings (const SHPhysicsWorldState::WorldSettings& settings) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ - SHEventHandle onSceneInit (SHEventPtr onSceneInitEvent); - SHEventHandle onSceneExit (SHEventPtr onSceneExitEvent); + void Init () override; + void Exit () override; - SHEventHandle onComponentAdded (SHEventPtr onComponentAddedEvent); - SHEventHandle onComponentRemoved (SHEventPtr onComponentRemovedEvent); + void BuildScene (SHSceneGraph& sceneGraph); + void ForceBuild (SHSceneGraph& sceneGraph); + void ForceUpdate (); + + /** + * @brief Casts a ray into the world. + * @param ray The ray to cast. + * @param distance The distance to cast the ray. Defaults to infinity. + * @param collisionTag The collision tag to use for filtering the raycast. + * @return The result of the raycast. + */ + SHPhysicsRaycastResult Raycast + ( + const SHRay& ray + , float distance = std::numeric_limits::infinity() + , const SHCollisionTag& collisionTag = SHCollisionTag{} + ) noexcept; + + /** + * @brief Casts a bounded ray into the world. + * @param start The starting point of the ray. + * @param end The end point of the ray. + * @param collisionTag The collision tag to use for filtering the bounded raycast. + * @return The result of the raycast. + */ + SHPhysicsRaycastResult Linecast + ( + const SHVec3& start + , const SHVec3& end + , const SHCollisionTag& collisionTag = SHCollisionTag{} + ) noexcept; + + /** + * @brief Casts a ray at a body with colliders. + * @param eid The entity to cast to. + * @param ray The ray to cast. + * @param distance The distance to cast the ray. Defaults to infinity. + * @return The result of the raycast. + */ + SHPhysicsRaycastResult ColliderRaycast + ( + EntityID eid + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + /** + * @brief Casts a ray at a collider. + * @param eid The entity to cast to. + * @param shapeIndex The index of the collision shape. + * @param ray The ray to cast. + * @param distance The distance to cast the ray. Defaults to infinity. + * @return The result of the raycast. + */ + SHPhysicsRaycastResult ColliderRaycast + ( + EntityID eid + , int shapeIndex + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + /** + * @brief Casts a bounded ray at a body with colliders. + * @param eid + * @param start + * @param end + * @return The result of the raycast. + */ + SHPhysicsRaycastResult ColliderLinecast + ( + EntityID eid + , const SHVec3& start + , const SHVec3& end + ) noexcept; + + /** + * @brief + * @param eid + * @param shapeIndex + * @param start + * @param end + * @return The result of the raycast. + */ + SHPhysicsRaycastResult ColliderLinecast + ( + EntityID eid + , int shapeIndex + , const SHVec3& start + , const SHVec3& end + ) noexcept; + + // Specific Handling for Collision Shapes as they are not under the Component System. + // This is done as events need to be sent out. + // TODO(Diren): Consider using a static method through the ColliderComponent. + + void AddCollisionShape (EntityID eid, int shapeIndex); + void RemoveCollisionShape (EntityID eid, int shapeIndex); + + /*---------------------------------------------------------------------------------*/ + /* System Routines */ + /*---------------------------------------------------------------------------------*/ + + class SH_API PhysicsPreUpdate final : public SHSystemRoutine + { + public: + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + PhysicsPreUpdate(); + + /*-------------------------------------------------------------------------------*/ + /* Function Members */ + /*-------------------------------------------------------------------------------*/ + + void Execute(double dt) noexcept override; + + private: + /*-------------------------------------------------------------------------------*/ + /* Function Members */ + /*-------------------------------------------------------------------------------*/ + + void syncRigidBodyActive (EntityID eid, SHPhysicsObject& physicsObject) const noexcept; + void syncColliderActive (EntityID eid, SHPhysicsObject& physicsObject) const noexcept; + static void syncOnPlay (EntityID eid, SHPhysicsObject& physicsObject) noexcept; + + static void preUpdateSyncTransform + ( + SHPhysicsObject& physicsObject + , SHTransformComponent* transformComponent + , SHRigidBodyComponent* rigidBodyComponent + , SHColliderComponent* colliderComponent + ) noexcept; + }; + + class SH_API PhysicsFixedUpdate final : public SHFixedSystemRoutine + { + public: + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + PhysicsFixedUpdate(); + + /*-------------------------------------------------------------------------------*/ + /* Function Members */ + /*-------------------------------------------------------------------------------*/ + + void Execute (double dt) noexcept override; + }; + + class SH_API PhysicsPostUpdate final : public SHSystemRoutine + { + public: + /*-------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*-------------------------------------------------------------------------------*/ + + PhysicsPostUpdate(); + + /*-------------------------------------------------------------------------------*/ + /* Function Members */ + /*-------------------------------------------------------------------------------*/ + + void Execute(double dt) noexcept override; + + private: + + /*-------------------------------------------------------------------------------*/ + /* Function Members */ + /*-------------------------------------------------------------------------------*/ + + static void postUpdateSyncTransforms + ( + SHPhysicsObject& physicsObject + , SHTransformComponent* transformComponent + , SHRigidBodyComponent* rigidBodyComponent + , SHColliderComponent* colliderComponent + , double interpolationFactor + ) noexcept; + }; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + // System data + + bool worldUpdated; + double interpolationFactor; + double fixedDT; + + // rp3d + + rp3d::PhysicsCommon factory; + + // Interface objects + + SHPhysicsWorldState worldState; + SHPhysicsObjectManager objectManager; + SHCollisionListener collisionListener; + SHPhysicsRaycaster raycaster; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + SHEventHandle addPhysicsComponent (SHEventPtr addComponentEvent) noexcept; + SHEventHandle removePhysicsComponent (SHEventPtr removeComponentEvent) noexcept; + + SHEventHandle onPlay (SHEventPtr onPlayEvent); + SHEventHandle onStop (SHEventPtr onStopEvent); + SHEventHandle buildScene (SHEventPtr onSceneChangeEvent); }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp index b6ed9d56..1fb11274 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp @@ -1,6 +1,8 @@ /************************************************************************************//*! \file SHPhysicsSystemInterface.cpp -\author Diren D Bharwani, diren.dbharwani, 390002520 +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 31, 2022 \brief Contains the definitions of the functions of the static SHPhysicsSystemInterface class. @@ -8,13 +10,10 @@ Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ - - +// Precompiled Headers #include "SHpch.h" - // Primary Header #include "SHPhysicsSystemInterface.h" - // Project Includes #include "ECS_Base/Managers/SHSystemManager.h" #include "Physics/System/SHPhysicsSystem.h" @@ -24,24 +23,28 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Static Usage Functions */ /*-----------------------------------------------------------------------------------*/ - const std::vector& SHPhysicsSystemInterface::GetCollisionInfo() noexcept + const std::vector& SHPhysicsSystemInterface::GetCollisionInfo() noexcept { - static std::vector emptyVec; + static std::vector emptyVec; - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - return physicsSystem->GetCollisionInfo(); + auto phySystem = SHSystemManager::GetSystem(); + if (phySystem) + { + return phySystem->GetAllCollisionInfo(); + } SHLOGV_WARNING("Failed to get collision events. Empty vector returned instead."); return emptyVec; } - const std::vector& SHPhysicsSystemInterface::GetTriggerInfo() noexcept + const std::vector& SHPhysicsSystemInterface::GetTriggerInfo() noexcept { - static std::vector emptyVec; + static std::vector emptyVec; - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - return physicsSystem->GetTriggerInfo(); + auto phySystem = SHSystemManager::GetSystem(); + if (phySystem) + { + return phySystem->GetAllTriggerInfo(); + } SHLOGV_WARNING("Failed to get trigger events. Empty vector returned instead."); return emptyVec; @@ -49,34 +52,85 @@ namespace SHADE double SHPhysicsSystemInterface::GetFixedDT() noexcept { - auto* physicsSystem = SHSystemManager::GetSystem(); - if (physicsSystem) - return physicsSystem->GetFixedDT(); + auto phySystem = SHSystemManager::GetSystem(); + if (phySystem) + { + return 1.0 / phySystem->GetFixedUpdateRate(); + } SHLOGV_WARNING("Failed to get fixed delta time. 0.0 returned instead."); return 0.0; } - int SHPhysicsSystemInterface::GetFixedUpdateRate() noexcept + SHPhysicsRaycastResult SHPhysicsSystemInterface::Raycast(const SHRay& ray, float distance) noexcept { auto* physicsSystem = SHSystemManager::GetSystem(); if (physicsSystem) - return physicsSystem->GetFixedUpdateRate(); + { + return physicsSystem->Raycast(ray, distance); + } - SHLOGV_WARNING("Failed to get fixed update rate. 0.0 returned instead."); - return 0.0; + SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); + return SHPhysicsRaycastResult{}; } - const std::vector& SHPhysicsSystemInterface::Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept + SHPhysicsRaycastResult SHPhysicsSystemInterface::Linecast(const SHVec3& start, const SHVec3& end) noexcept { - static std::vector emptyVec; - auto* physicsSystem = SHSystemManager::GetSystem(); if (physicsSystem) - return physicsSystem->Raycast(info); + { + return physicsSystem->Linecast(start, end); + } - SHLOGV_WARNING("Failed to get fixed update rate. 0.0 returned instead."); - return emptyVec; + SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); + return SHPhysicsRaycastResult{}; } + SHPhysicsRaycastResult SHPhysicsSystemInterface::ColliderRaycast(EntityID eid, const SHRay& ray, float distance) noexcept + { + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + { + return physicsSystem->ColliderRaycast(eid, ray, distance); + } + + SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); + return SHPhysicsRaycastResult{}; + } + + SHPhysicsRaycastResult SHPhysicsSystemInterface::ColliderRaycast(EntityID eid, int shapeIndex, const SHRay& ray, float distance) noexcept + { + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + { + return physicsSystem->ColliderRaycast(eid, shapeIndex, ray, distance); + } + + SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); + return SHPhysicsRaycastResult{}; + } + + SHPhysicsRaycastResult SHPhysicsSystemInterface::ColliderLinecast(EntityID eid, const SHVec3& start, const SHVec3& end) noexcept + { + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + { + return physicsSystem->ColliderLinecast(eid, start, end); + } + + SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); + return SHPhysicsRaycastResult{}; + } + + SHPhysicsRaycastResult SHPhysicsSystemInterface::ColliderLinecast(EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept + { + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + { + return physicsSystem->ColliderLinecast(eid, shapeIndex, start, end); + } + + SHLOGV_WARNING("Failed to get the physics system. No ray was casted."); + return SHPhysicsRaycastResult{}; + } } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h index 8a1f6be8..0065aee3 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h @@ -1,21 +1,21 @@ /************************************************************************************//*! \file SHPhysicsSystemInterface.h -\author Diren D Bharwani, diren.dbharwani, 390002520 +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 31, 2022 \brief Contains the definition of the SHGraphicsSystemInterface static class. Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ - #pragma once +// STL Includes #include // Project Headers #include "ECS_Base/Entity/SHEntity.h" -#include "Physics/Collision/SHCollisionSpace.h" -#include "Physics/Collision/Contacts/SHCollisionEvents.h" namespace SHADE @@ -24,10 +24,10 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ + class SHCollisionInfo; class SHVec3; struct SHRay; struct SHPhysicsRaycastResult; - struct SHCollisionSpace::RaycastInfo; /*-----------------------------------------------------------------------------------*/ @@ -49,12 +49,15 @@ namespace SHADE /* Static Usage Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static const std::vector& GetCollisionInfo () noexcept; - [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; - [[nodiscard]] static double GetFixedDT () noexcept; - [[nodiscard]] static int GetFixedUpdateRate () noexcept; - [[nodiscard]] static const std::vector& Raycast (const SHCollisionSpace::RaycastInfo& info) noexcept; - + [[nodiscard]] static const std::vector& GetCollisionInfo() noexcept; + [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; + [[nodiscard]] static double GetFixedDT () noexcept; + [[nodiscard]] static SHPhysicsRaycastResult Raycast (const SHRay& ray, float distance = std::numeric_limits::infinity()) noexcept; + [[nodiscard]] static SHPhysicsRaycastResult Linecast (const SHVec3& start, const SHVec3& end) noexcept; + [[nodiscard]] static SHPhysicsRaycastResult ColliderRaycast (EntityID eid, const SHRay& ray, float distance = std::numeric_limits::infinity()) noexcept; + [[nodiscard]] static SHPhysicsRaycastResult ColliderRaycast (EntityID eid, int shapeIndex, const SHRay& ray, float distance = std::numeric_limits::infinity()) noexcept; + [[nodiscard]] static SHPhysicsRaycastResult ColliderLinecast (EntityID eid, const SHVec3& start, const SHVec3& end) noexcept; + [[nodiscard]] static SHPhysicsRaycastResult ColliderLinecast (EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept; }; } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp new file mode 100644 index 00000000..a5ca957a --- /dev/null +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemRoutines.cpp @@ -0,0 +1,412 @@ +/**************************************************************************************** + * \file SHPhysicsSystemRoutines.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for the Physics System Routines + * + * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or + * disclosure of this file or its contents without the prior written consent + * of DigiPen Institute of Technology is prohibited. +****************************************************************************************/ + +#include + +// Primary Header +#include "SHPhysicsSystem.h" +// Project Headers +#include "ECS_Base/Managers/SHEntityManager.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Editor/SHEditor.h" +#include "Scene/SHSceneManager.h" +#include "Scripting/SHScriptEngine.h" + +#include "Input/SHInputManager.h" +#include "Physics/Collision/SHCollisionTagMatrix.h" + +/*-------------------------------------------------------------------------------------*/ +/* Local Functions */ +/*-------------------------------------------------------------------------------------*/ + +void testFunction(); + +///////////////////////////////////////////////////////////////////////////////////////// + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsSystem::PhysicsPreUpdate::PhysicsPreUpdate() + : SHSystemRoutine { "Physics PreUpdate", true } + {} + + SHPhysicsSystem::PhysicsFixedUpdate::PhysicsFixedUpdate() + : SHFixedSystemRoutine { DEFAULT_FIXED_STEP, "Physics FixedUpdate", false } + {} + + SHPhysicsSystem::PhysicsPostUpdate::PhysicsPostUpdate() + : SHSystemRoutine { "Physics PostUpdate", false } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsSystem::PhysicsPreUpdate::Execute(double) noexcept + { + auto* physicsSystem = reinterpret_cast(GetSystem()); + + #ifdef SHEDITOR + + // Only Sync on Play. + // Otherwise, Components are only holding data until the world is built on play. + const auto* EDITOR = SHSystemManager::GetSystem(); + if (EDITOR && EDITOR->editorState != SHEditor::State::STOP) + { + physicsSystem->objectManager.UpdateCommands(); + + for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects) + { + // Ensure a valid physics Object + if (physicsObject.rp3dBody == nullptr) + continue; + + // Sync active states between SHADE & RP3D + syncRigidBodyActive(entityID, physicsObject); + syncColliderActive(entityID, physicsObject); + + syncOnPlay(entityID, physicsObject); + } + } + else + { + auto& rigidBodyDense = SHComponentManager::GetDense(); + auto& colliderDense = SHComponentManager::GetDense(); + + for (auto& rigidBodyComponent : rigidBodyDense) + { + const auto* TRANSFORM = SHComponentManager::GetComponent_s(rigidBodyComponent.GetEID()); + + if (TRANSFORM && TRANSFORM->HasChanged()) + { + rigidBodyComponent.position = TRANSFORM->GetWorldPosition(); + rigidBodyComponent.orientation = TRANSFORM->GetWorldOrientation(); + } + } + + for (auto& colliderComponent : colliderDense) + { + const auto* TRANSFORM = SHComponentManager::GetComponent_s(colliderComponent.GetEID()); + + if (TRANSFORM && TRANSFORM->HasChanged()) + { + colliderComponent.position = TRANSFORM->GetWorldPosition(); + colliderComponent.orientation = TRANSFORM->GetWorldOrientation(); + colliderComponent.scale = TRANSFORM->GetWorldScale(); + + colliderComponent.RecomputeCollisionShapes(); + } + } + } + + #else + + // Always sync Rigid Body & Collider Components with Physics Objects + // Do not check for an editor here + + physicsSystem->objectManager.UpdateCommands(); + + for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects) + { + // Ensure a valid physics Object + if (physicsObject.rp3dBody == nullptr) + continue; + + syncRigidBodyActive(entityID, physicsObject); + syncColliderActive(entityID, physicsObject); + + syncOnPlay(entityID, physicsObject); + } + + #endif + } + + void SHPhysicsSystem::PhysicsFixedUpdate::Execute(double dt) noexcept + { + auto* physicsSystem = reinterpret_cast(GetSystem()); + auto* scriptingSystem = SHSystemManager::GetSystem(); + if (scriptingSystem == nullptr) + { + SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!"); + } + + const double FIXED_DT = physicsSystem->fixedDT; + // HACK: Clamp DT here to prevent a ridiculous amount of updates. This limits updates from large dt to 2. + // HACK: This should be done by the FRC and not here for predictable behaviour. + + accumulatedTime += dt; + + //testFunction(); + + int count = 0; + while (accumulatedTime > FIXED_DT) + { + if (scriptingSystem != nullptr) + scriptingSystem->ExecuteFixedUpdates(); + + physicsSystem->worldState.world->update(static_cast(FIXED_DT)); + + accumulatedTime -= FIXED_DT; + ++count; + } + + stats.numSteps = count; + physicsSystem->worldUpdated = count > 0; + + physicsSystem->interpolationFactor = accumulatedTime / fixedTimeStep; + } + + void SHPhysicsSystem::PhysicsPostUpdate::Execute(double) noexcept + { + auto* physicsSystem = reinterpret_cast(GetSystem()); + auto* scriptingSystem = SHSystemManager::GetSystem(); + + if (scriptingSystem == nullptr) + { + SHLOGV_ERROR("Unable to invoke collision and trigger script events due to missing SHScriptEngine!"); + } + + // Interpolate transforms for rendering + if (physicsSystem->worldUpdated) + { + for (auto& [entityID, physicsObject] : physicsSystem->objectManager.physicsObjects) + { + auto* transformComponent = SHComponentManager::GetComponent_s(entityID); + auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); + auto* colliderComponent = SHComponentManager::GetComponent_s(entityID); + + postUpdateSyncTransforms + ( + physicsObject + , transformComponent + , rigidBodyComponent + , colliderComponent + , physicsSystem->interpolationFactor + ); + } + + // Collision & Trigger messages + if (scriptingSystem != nullptr) + scriptingSystem->ExecuteCollisionFunctions(); + + // Since this function never runs when editor in not in play, execute the function anyway + physicsSystem->collisionListener.CleanContainers(); + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsSystem::PhysicsPreUpdate::syncRigidBodyActive(EntityID eid, SHPhysicsObject& physicsObject) const noexcept + { + if (!SHComponentManager::HasComponent(eid)) + return; + + const bool IS_ACTIVE_IN_SCENE = SHSceneManager::CheckNodeAndComponentsActive(eid); + const bool IS_RP3D_BODY_ACTIVE = physicsObject.GetRigidBody()->isActive(); + + if (IS_ACTIVE_IN_SCENE != IS_RP3D_BODY_ACTIVE) + physicsObject.GetRigidBody()->setIsActive(IS_ACTIVE_IN_SCENE); + } + + void SHPhysicsSystem::PhysicsPreUpdate::syncColliderActive(EntityID eid, SHPhysicsObject& physicsObject) const noexcept + { + const auto* COLLIDER = SHComponentManager::GetComponent_s(eid); + if (!COLLIDER) + return; + + const bool IS_ACTIVE_IN_SCENE = SHSceneManager::CheckNodeAndComponentsActive(eid); + const bool IS_RP3D_COLLIDER_ACTIVE = physicsObject.collidersActive; + + if (IS_ACTIVE_IN_SCENE != IS_RP3D_COLLIDER_ACTIVE) + { + // HACK: If active state turned off, remove all collision shapes. If turned on, add them back. + auto* physicsSystem = reinterpret_cast(GetSystem()); + + const int NUM_SHAPES = static_cast(COLLIDER->GetCollisionShapes().size()); + if (IS_ACTIVE_IN_SCENE) + { + for (int i = 0; i < NUM_SHAPES; ++i) + physicsSystem->objectManager.AddCollisionShape(eid, i); + } + else + { + for (int i = NUM_SHAPES - 1; i >= 0; --i) + physicsSystem->objectManager.RemoveCollisionShape(eid, i); + } + + physicsObject.collidersActive = IS_ACTIVE_IN_SCENE; + } + } + + void SHPhysicsSystem::PhysicsPreUpdate::syncOnPlay(EntityID eid, SHPhysicsObject& physicsObject) noexcept + { + auto* transformComponent = SHComponentManager::GetComponent_s(eid); + auto* rigidBodyComponent = SHComponentManager::GetComponent_s(eid); + auto* colliderComponent = SHComponentManager::GetComponent_s(eid); + + // Sync transforms & physics components transforms + if (transformComponent && transformComponent->HasChanged()) + { + physicsObject.GetRigidBody()->setIsSleeping(false); + + preUpdateSyncTransform + ( + physicsObject + , transformComponent + , rigidBodyComponent + , colliderComponent + ); + } + + // Sync Rigid Bodies + if (rigidBodyComponent) + physicsObject.SyncRigidBody(*rigidBodyComponent); + + // Sync Colliders + if (colliderComponent) + physicsObject.SyncColliders(*colliderComponent); + } + + void SHPhysicsSystem::PhysicsPreUpdate::preUpdateSyncTransform + ( + SHPhysicsObject& physicsObject + , SHTransformComponent* transformComponent + , SHRigidBodyComponent* rigidBodyComponent + , SHColliderComponent* colliderComponent + ) noexcept + { + if (!transformComponent) + return; + + const SHVec3& WORLD_POS = transformComponent->GetWorldPosition(); + const SHQuaternion& WORLD_ROT = transformComponent->GetWorldOrientation(); + const SHVec3& WORLD_SCL = transformComponent->GetWorldScale(); + + const rp3d::Transform RP3D_TRANSFORM { WORLD_POS, WORLD_ROT }; + physicsObject.GetRigidBody()->setTransform(RP3D_TRANSFORM); + physicsObject.prevTransform = RP3D_TRANSFORM; + + if (rigidBodyComponent && SHSceneManager::CheckNodeAndComponentsActive(physicsObject.entityID)) + { + rigidBodyComponent->position = WORLD_POS; + rigidBodyComponent->orientation = WORLD_ROT; + } + + if (colliderComponent && SHSceneManager::CheckNodeAndComponentsActive(physicsObject.entityID)) + { + colliderComponent->position = WORLD_POS; + colliderComponent->orientation = WORLD_ROT; + colliderComponent->scale = WORLD_SCL; + + colliderComponent->RecomputeCollisionShapes(); + } + } + + void SHPhysicsSystem::PhysicsPostUpdate::postUpdateSyncTransforms + ( + SHPhysicsObject& physicsObject + , SHTransformComponent* transformComponent + , SHRigidBodyComponent* rigidBodyComponent + , SHColliderComponent* colliderComponent + , double interpolationFactor + ) noexcept + { + const rp3d::Transform& CURRENT_TF = physicsObject.GetRigidBody()->getTransform(); + auto renderPos = CURRENT_TF.getPosition(); + auto renderRot = CURRENT_TF.getOrientation(); + + // Cache transforms + if (physicsObject.GetRigidBody()->isActive()) + physicsObject.prevTransform = CURRENT_TF; + + // Sync with rigid bodies + if (rigidBodyComponent && SHSceneManager::CheckNodeAndComponentsActive(physicsObject.entityID)) + { + // Skip static bodies + if (rigidBodyComponent->GetType() == SHRigidBodyComponent::Type::STATIC) + return; + + // Check if transform should be interpolated + if (rigidBodyComponent->IsInterpolating()) + { + // Interpolate transforms between current and predicted next transform + + const rp3d::Transform PREV_TF = physicsObject.prevTransform; + const rp3d::Transform INTERPOLATED_TF = rp3d::Transform::interpolateTransforms(PREV_TF, CURRENT_TF, static_cast(interpolationFactor)); + + renderPos = INTERPOLATED_TF.getPosition(); + renderRot = INTERPOLATED_TF.getOrientation(); + } + + rigidBodyComponent->position = CURRENT_TF.getPosition(); + rigidBodyComponent->orientation = CURRENT_TF.getOrientation(); + + // Sync with colliders + if (colliderComponent && SHSceneManager::CheckNodeAndComponentsActive(physicsObject.entityID)) + { + // Skip colliders without rigidbody components. If any transform was updated, it was done in pre-update. + + colliderComponent->position = CURRENT_TF.getPosition(); + colliderComponent->orientation = CURRENT_TF.getOrientation(); + } + + // Set transform for rendering + if (transformComponent) + { + transformComponent->SetWorldPosition(renderPos); + transformComponent->SetWorldOrientation(renderRot); + } + } + } +} // namespace SHADE + +///////////////////////////////////////////////////////////////////////////////////////// + +void testFunction() +{ + using namespace SHADE; + + // Test movement + const float forceModifier = 25.0f; + EntityID eid = 65538; + + if (SHEntityManager::IsValidEID(eid)) + { + auto* rb = SHComponentManager::GetComponent_s(eid); + if (rb) + { + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::W)) + rb->AddForce(SHVec3::UnitZ * forceModifier); + + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::A)) + rb->AddForce(-SHVec3::UnitX * forceModifier); + + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::S)) + rb->AddForce(-SHVec3::UnitZ * forceModifier); + + if (SHInputManager::GetKey(SHInputManager::SH_KEYCODE::D)) + rb->AddForce(SHVec3::UnitX * forceModifier); + } + } + + // Cast rays + auto* tag = SHCollisionTagMatrix::GetTag(1); + tag->SetLayerState(SHCollisionTag::Layer::_1, false); + tag->SetLayerState(SHCollisionTag::Layer::_2, true); + + SHRay ray { SHVec3{3.0f, 3.5f, 0.0f}, -SHVec3::UnitX }; + auto* physicsSystem = SHSystemManager::GetSystem(); + physicsSystem->Raycast(ray, std::numeric_limits::infinity(), *tag); +} diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index 7dc8d84d..b876f5b0 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -27,7 +27,7 @@ namespace SHADE : root { nullptr } { // The root is set to the maximum entity. It should not be interfaced with. - root = allocateNode(MAX_EID); + root = AllocateNode(MAX_EID); } SHSceneGraph::~SHSceneGraph() noexcept @@ -260,7 +260,7 @@ namespace SHADE if (newParent == nullptr) newParent = root; - changeParent(NODE_ITER->second, newParent); + ChangeParent(NODE_ITER->second, newParent); SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } @@ -305,7 +305,7 @@ namespace SHADE }; SHSceneNode* currentNode = NODE_ITER->second; - changeParent(currentNode, PARENT_ITER->second); + ChangeParent(currentNode, PARENT_ITER->second); SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } @@ -352,7 +352,7 @@ namespace SHADE return NODE_ITER->second; } - SHSceneNode* newNode = allocateNode(entityID); + SHSceneNode* newNode = AllocateNode(entityID); if (parent == nullptr) { @@ -362,7 +362,7 @@ namespace SHADE } else { - changeParent(newNode, parent); + ChangeParent(newNode, parent); } return newNode; @@ -389,9 +389,9 @@ namespace SHADE // Remove reference of current node from parent SHSceneNode* currentNode = NODE_ITER->second; if (currentNode->parent != nullptr) - removeChild(currentNode->parent, currentNode); + RemoveChild(currentNode->parent, currentNode); - releaseNode(currentNode); + ReleaseNode(currentNode); return true; } @@ -399,16 +399,16 @@ namespace SHADE { // Remove reference of current node from parent if (nodeToRemove->parent != nullptr) - removeChild(nodeToRemove->parent, nodeToRemove); + RemoveChild(nodeToRemove->parent, nodeToRemove); - releaseNode(nodeToRemove); + ReleaseNode(nodeToRemove); return true; } void SHSceneGraph::Reset() noexcept { for (auto* node : entityNodeMap | std::views::values) - releaseNode(node); + ReleaseNode(node); } bool SHSceneGraph::IsChildOf(EntityID entityID, SHSceneNode* targetNode) noexcept @@ -498,39 +498,39 @@ namespace SHADE void SHSceneGraph::Traverse (const UnaryFunction& function) const { - traverseAndInvokeFunction(root, function); + TraverseAndInvokeFunction(root, function); } /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHSceneNode* SHSceneGraph::allocateNode(EntityID entityID) + SHSceneNode* SHSceneGraph::AllocateNode(EntityID entityID) { SHSceneNode* newNode = new SHSceneNode{entityID}; entityNodeMap.emplace(entityID, newNode); return newNode; } - void SHSceneGraph::releaseNode(SHSceneNode* node) noexcept + void SHSceneGraph::ReleaseNode(SHSceneNode* node) noexcept { SHASSERT(node != nullptr, "Attempting to release Invalid Node!") // Remove parent's reference to this node if there is a parent if (node->parent != nullptr) - removeChild(node->parent, node); + RemoveChild(node->parent, node); // Remove child's references to this node. Children end up as floating nodes. for (auto* child : node->GetChildren()) { - changeParent(child, nullptr); + ChangeParent(child, nullptr); } entityNodeMap.erase(node->GetEntityID()); delete node; } - void SHSceneGraph::changeParent(SHSceneNode* node, SHSceneNode* newParent) + void SHSceneGraph::ChangeParent(SHSceneNode* node, SHSceneNode* newParent) { // Handle self assignment if (node->parent != nullptr && newParent != nullptr && node->parent->entityID == newParent->entityID) @@ -538,7 +538,7 @@ namespace SHADE // Remove child if (node->parent) - removeChild(node->parent, node); + RemoveChild(node->parent, node); if (newParent == nullptr) { @@ -548,16 +548,16 @@ namespace SHADE node->parent = newParent; // Update parent's children - addChild(newParent, node); + AddChild(newParent, node); } - void SHSceneGraph::addChild(SHSceneNode* node, SHSceneNode* newChild) + void SHSceneGraph::AddChild(SHSceneNode* node, SHSceneNode* newChild) { SHASSERT(node != nullptr, "Attempting to modify a non-existent scene node!") SHASSERT(newChild != nullptr, "Attempting to add a non-existent child to a SceneNode!") if (newChild->parent) - removeChild(newChild->parent, newChild); + RemoveChild(newChild->parent, newChild); newChild->parent = node; node->children.emplace_back(newChild); @@ -571,7 +571,7 @@ namespace SHADE SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_ADD_CHILD_EVENT); } - void SHSceneGraph::removeChild(SHSceneNode* node, SHSceneNode* childToRemove) + void SHSceneGraph::RemoveChild(SHSceneNode* node, SHSceneNode* childToRemove) { SHASSERT(node != nullptr, "Attempting to modify a non-existent scene node!") SHASSERT(childToRemove != nullptr, "Attempting to remove a non-existent child from a SceneNode!") @@ -592,12 +592,12 @@ namespace SHADE SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_REMOVE_CHILD_EVENT); } - void SHSceneGraph::traverseAndInvokeFunction(const SHSceneNode* node, const UnaryFunction& function) + void SHSceneGraph::TraverseAndInvokeFunction(const SHSceneNode* node, const UnaryFunction& function) { for (auto* child : node->children) { function(child); - traverseAndInvokeFunction(child, function); + TraverseAndInvokeFunction(child, function); } } diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index ce51db5e..3285fc6f 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -24,10 +24,6 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates a hierarchical tree for objects in the scene. - */ class SH_API SHSceneGraph { public: @@ -69,28 +65,7 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Changing the parent of a node will broadcast three events in the following order: - *
- * 1. SHSceneGraphChangeParentEvent - * 2. SHSceneGraphRemoveChildEvent - * 3. SHSceneGraphAddChildEvent - *

- * See the corresponding header file for the contents of the event struct. - */ void SetParent (EntityID entityID, SHSceneNode* newParent) noexcept; - - /** - * @brief - * Changing the parent of a node will broadcast three events in the following order: - *
- * 1. SHSceneGraphChangeParentEvent - * 2. SHSceneGraphRemoveChildEvent - * 3. SHSceneGraphAddChildEvent - *

- * See the corresponding header file for the contents of the event struct. - */ void SetParent (EntityID entityID, EntityID newParent) noexcept; void SetActive (EntityID entityID, bool isActive) noexcept; @@ -99,47 +74,15 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - /** - * @brief - * Adding a node will broadcast two events in the following order: - *
- * 1. SHSceneGraphChangeParentEvent - * 2. SHSceneGraphAddChildEvent - *

- * See the corresponding header file for the contents of the event struct. - */ SHSceneNode* AddNode (EntityID entityID, SHSceneNode* parent = nullptr); + bool RemoveNode (EntityID entityID) noexcept; + bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; + void Reset () noexcept; - /** - * @brief - * Removing a node will broadcast the SHSceneGraphRemoveChildEvent. - * See the corresponding header file for the contents of the event struct. - */ - bool RemoveNode (EntityID entityID) noexcept; + bool IsChildOf (EntityID entityID, SHSceneNode* targetNode) noexcept; + bool IsChildOf (EntityID entityID, EntityID targetID) noexcept; - /** - * @brief - * Removing a node will broadcast the SHSceneGraphRemoveChildEvent. - * See the corresponding header file for the contents of the event struct. - */ - bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; - - /** - * @brief - * Clears all the scene nodes in the scene graph. - */ - void Reset () noexcept; - - bool IsChildOf (EntityID entityID, SHSceneNode* targetNode) noexcept; - bool IsChildOf (EntityID entityID, EntityID targetID) noexcept; - - /** - * @brief - * Traverses the graph and a depth-first manner and applies the the function onto each node. - * @param function - * A unary function that takes in a SHSceneNode pointer. - */ - void Traverse (const UnaryFunction& function) const; + void Traverse (const UnaryFunction& function) const; private: /*---------------------------------------------------------------------------------*/ @@ -153,14 +96,14 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - SHSceneNode* allocateNode (EntityID entityID); - void releaseNode (SHSceneNode* node) noexcept; + SHSceneNode* AllocateNode (EntityID entityID); + void ReleaseNode (SHSceneNode* node) noexcept; - void changeParent (SHSceneNode* node, SHSceneNode* newParent); - void addChild (SHSceneNode* node, SHSceneNode* newChild); - void removeChild (SHSceneNode* node, SHSceneNode* childToRemove); + void ChangeParent (SHSceneNode* node, SHSceneNode* newParent); + void AddChild (SHSceneNode* node, SHSceneNode* newChild); + void RemoveChild (SHSceneNode* node, SHSceneNode* childToRemove); - static void traverseAndInvokeFunction (const SHSceneNode* node, const UnaryFunction& function); + static void TraverseAndInvokeFunction (const SHSceneNode* node, const UnaryFunction& function); }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Scene/SHSceneManager.cpp b/SHADE_Engine/src/Scene/SHSceneManager.cpp index 79e8818a..3614fa52 100644 --- a/SHADE_Engine/src/Scene/SHSceneManager.cpp +++ b/SHADE_Engine/src/Scene/SHSceneManager.cpp @@ -19,7 +19,6 @@ #include "Events/SHEventManager.hpp" //#include "FRC/SHFrameRateController.h" //#include "ECS_Base/System/SHApplication.h" -#include "SHSceneEvents.h" #include @@ -67,6 +66,7 @@ namespace SHADE SHEventManager::BroadcastEvent(exitEvent, SH_SCENE_EXIT_POST); delete currentScene; + } if (!prevSceneReload) { diff --git a/SHADE_Engine/src/Scene/SHSceneNode.h b/SHADE_Engine/src/Scene/SHSceneNode.h index 45d81c1d..62979850 100644 --- a/SHADE_Engine/src/Scene/SHSceneNode.h +++ b/SHADE_Engine/src/Scene/SHSceneNode.h @@ -15,17 +15,20 @@ // Project Headers #include "ECS_Base/Entity/SHEntity.h" #include "SH_API.h" +#include "SHSceneGraph.h" namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + class SHSceneGraph; + /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - /** - * @brief - * Encapsulates an object in the scene that is part of the scene's hierarchy. - */ class SH_API SHSceneNode { private: @@ -64,7 +67,7 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetActive (bool newActiveState) noexcept; + void SetActive (bool newActiveState) noexcept; private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index 1b485633..2e2d45f4 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -3,6 +3,9 @@ #include "Physics/Interface/SHColliderComponent.h" #include "Graphics/MiddleEnd/TextRendering/SHTextRenderableComponent.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" +#include "Math/Geometry/SHBox.h" +#include "Math/Geometry/SHSphere.h" +#include "Physics/Interface/SHCollisionShape.h" #include "Resource/SHResourceManager.h" #include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec3.h" @@ -11,8 +14,7 @@ #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" #include "SHSerializationTools.h" #include "Graphics/MiddleEnd/TextRendering/SHFont.h" -#include "Physics/Collision/SHCompositeCollider.h" -#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" +#include "Physics/Collision/SHCollisionTagMatrix.h" namespace YAML { @@ -144,14 +146,14 @@ namespace YAML { case SHCollisionShape::Type::BOX: { - const auto& BOX = dynamic_cast(rhs); - node[HalfExtents] = BOX.GetRelativeExtents(); + const auto* BOX = reinterpret_cast(rhs.GetShape()); + node[HalfExtents] = BOX->GetRelativeExtents(); } break; case SHCollisionShape::Type::SPHERE: { - const auto& SPHERE = dynamic_cast(rhs); - node[Radius] = SPHERE.GetRelativeRadius(); + const auto* SPHERE = reinterpret_cast(rhs.GetShape()); + node[Radius] = SPHERE->GetRelativeRadius(); } break; case SHCollisionShape::Type::CAPSULE: break; @@ -187,19 +189,13 @@ namespace YAML case SHCollisionShape::Type::BOX: { if (node[HalfExtents].IsDefined()) - { - auto* box = dynamic_cast(&rhs); - box->SetRelativeExtents(node[HalfExtents].as()); - } + rhs.SetBoundingBox(node[HalfExtents].as()); } break; case SHCollisionShape::Type::SPHERE: { if (node[Radius].IsDefined()) - { - auto* sphere = dynamic_cast(&rhs); - sphere->SetRelativeRadius(node[Radius].as()); - } + rhs.SetBoundingSphere(node[Radius].as()); } break; case SHCollisionShape::Type::CAPSULE: break; @@ -223,19 +219,15 @@ namespace YAML template<> struct convert { - static constexpr const char* DrawColliders = "DrawColliders"; static constexpr const char* Colliders = "Colliders"; - static Node encode(SHColliderComponent& rhs) { Node node, collidersNode; - - node[DrawColliders] = rhs.GetDebugDrawState(); - - int const numColliders = static_cast(rhs.GetCollisionShapes()->size()); + auto const& colliders = rhs.GetCollisionShapes(); + int const numColliders = static_cast(colliders.size()); for (int i = 0; i < numColliders; ++i) { - auto& collider = *rhs.GetCollisionShape(i); + auto& collider = rhs.GetCollisionShape(i); Node colliderNode = convert::encode(collider); if (colliderNode.IsDefined()) collidersNode[i] = colliderNode; @@ -245,9 +237,6 @@ namespace YAML } static bool decode(Node const& node, SHColliderComponent& rhs) { - if (node[DrawColliders].IsDefined()) - rhs.SetDebugDrawState(node[DrawColliders].as()); - if (node[Colliders].IsDefined()) { int numColliders{}; @@ -260,16 +249,14 @@ namespace YAML if (!ok) return false; - auto* collider = rhs.GetCollider(); switch (colliderType) { - case SHCollisionShape::Type::SPHERE: collider->AddSphereCollisionShape(1.0f); break; - case SHCollisionShape::Type::BOX: collider->AddBoxCollisionShape(SHVec3::One); break; - case SHCollisionShape::Type::CAPSULE: break; - default:; + case SHCollisionShape::Type::BOX: rhs.AddBoundingBox(); break; + case SHCollisionShape::Type::SPHERE: rhs.AddBoundingSphere(); break; + case SHCollisionShape::Type::CAPSULE: break; + default:; } - - YAML::convert::decode(colliderNode, *collider->GetCollisionShape(numColliders++)); + YAML::convert::decode(colliderNode, rhs.GetCollisionShape(numColliders++)); } } return true; diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index f4b4f09d..b5f1444f 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -19,7 +19,7 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { - /*---------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ /* CollisionShape - Constructors */ /*---------------------------------------------------------------------------------*/ CollisionShape::CollisionShape(int arrayIdx, Entity attachedEntity) @@ -107,7 +107,7 @@ namespace SHADE try { - auto& shape = *collider->GetCollisionShape(arrayIndex); + auto& shape = collider->GetCollisionShape(arrayIndex); return shape; } catch (std::invalid_argument&) @@ -128,19 +128,35 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 BoxCollider::Center::get() { - return Convert::ToCLI(getNativeCollisionShape().GetWorldCentroid()); + return Convert::ToCLI(getNativeCollisionShape().GetCenter()); + } + void BoxCollider::Center::set(Vector3 value) + { + getNativeCollisionShape().SetCenter(Convert::ToNative(value)); } Vector3 BoxCollider::HalfExtents::get() { - return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); + return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); } void BoxCollider::HalfExtents::set(Vector3 value) { - getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); + getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); } - Quaternion BoxCollider::Orientation::get() + Vector3 BoxCollider::Min::get() { - return Convert::ToCLI(getNativeCollisionShape().GetWorldOrientation()); + return Convert::ToCLI(getNativeCollisionShape().GetMin()); + } + void BoxCollider::Min::set(Vector3 value) + { + getNativeCollisionShape().SetMin(Convert::ToNative(value)); + } + Vector3 BoxCollider::Max::get() + { + return Convert::ToCLI(getNativeCollisionShape().GetMax()); + } + void BoxCollider::Max::set(Vector3 value) + { + getNativeCollisionShape().SetMax(Convert::ToNative(value)); } /*---------------------------------------------------------------------------------*/ @@ -148,13 +164,11 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ bool BoxCollider::TestPoint(Vector3 point) { - //return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); - return false; + return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); } bool BoxCollider::Raycast(Ray ray, float maxDistance) { - //return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); - return false; + return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); } /*---------------------------------------------------------------------------------*/ @@ -162,7 +176,11 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 SphereCollider::Center::get() { - return Convert::ToCLI(getNativeCollisionShape().GetWorldCentroid()); + return Convert::ToCLI(getNativeCollisionShape().GetCenter()); + } + void SphereCollider::Center::set(Vector3 value) + { + getNativeCollisionShape().SetCenter(Convert::ToNative(value)); } float SphereCollider::Radius::get() { @@ -178,11 +196,11 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ bool SphereCollider::TestPoint(Vector3 point) { - return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); + return getNativeCollisionShape().TestPoint(Convert::ToNative(point)); } bool SphereCollider::Raycast(Ray ray, float maxDistance) { - return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); + return getNativeCollisionShape().Raycast(Convert::ToNative(ray)); } /*---------------------------------------------------------------------------------*/ @@ -213,10 +231,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ int Collider::CollisionShapeCount::get() { - if (const auto* shapes = GetNativeComponent()->GetCollisionShapes()) - return static_cast(shapes->size()); - - return -1; + return static_cast(GetNativeComponent()->GetCollisionShapes().size()); } /*---------------------------------------------------------------------------------*/ @@ -296,10 +311,10 @@ namespace SHADE // Populate the list int i = 0; - for (const auto* collider : *GetNativeComponent()->GetCollisionShapes()) + for (const auto& collider : GetNativeComponent()->GetCollisionShapes()) { CollisionShape^ bound = nullptr; - switch (collider->GetType()) + switch (collider.GetType()) { case SHCollisionShape::Type::BOX: bound = gcnew BoxCollider(i, Owner.GetEntity()); diff --git a/SHADE_Managed/src/Components/Collider.h++ b/SHADE_Managed/src/Components/Collider.h++ index c2e732f4..8ea648aa 100644 --- a/SHADE_Managed/src/Components/Collider.h++ +++ b/SHADE_Managed/src/Components/Collider.h++ @@ -27,11 +27,11 @@ namespace SHADE try { - auto* shape = collider->GetCollisionShape(arrayIndex); - if (!shape || shape->GetType() == SHCollisionShape::Type::INVALID) + auto& shape = collider->GetCollisionShape(arrayIndex); + if (shape.GetType() != SHCollisionShape::Type::BOX) throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); - return dynamic_cast(*shape); + return reinterpret_cast(shape); } catch (std::invalid_argument&) { diff --git a/SHADE_Managed/src/Components/Collider.hxx b/SHADE_Managed/src/Components/Collider.hxx index 19235571..a649483f 100644 --- a/SHADE_Managed/src/Components/Collider.hxx +++ b/SHADE_Managed/src/Components/Collider.hxx @@ -142,14 +142,15 @@ namespace SHADE /* Properties */ /*-----------------------------------------------------------------------------*/ /// - /// Center of the box collider. + /// Center of the Bounding Box formed by this bound. /// property Vector3 Center { Vector3 get(); + void set(Vector3 value); } /// - /// Half of the scale of the box collider. + /// Half of the scale of the Bounding Box formed by this bound. /// property Vector3 HalfExtents { @@ -157,11 +158,22 @@ namespace SHADE void set(Vector3 value); } /// - /// The orientation of the box. + /// Position of the bottom left back corner of the Bounding Box formed by this + /// bound. /// - property Quaternion Orientation + property Vector3 Min { - Quaternion get(); + Vector3 get(); + void set(Vector3 value); + } + /// + /// Position of the top right front corner of the Bounding Box formed by this + /// bound. + /// + property Vector3 Max + { + Vector3 get(); + void set(Vector3 value); } /*-----------------------------------------------------------------------------*/ @@ -189,11 +201,12 @@ namespace SHADE /* Properties */ /*-----------------------------------------------------------------------------*/ /// - /// Center of the sphere. + /// Center of the Bounding Sphere formed by this bound. /// property Vector3 Center { Vector3 get(); + void set(Vector3 value); } /// /// Radius of the Bounding Sphere formed by this bound. diff --git a/SHADE_Managed/src/Components/RigidBody.cxx b/SHADE_Managed/src/Components/RigidBody.cxx index 6237f3a9..a564402f 100644 --- a/SHADE_Managed/src/Components/RigidBody.cxx +++ b/SHADE_Managed/src/Components/RigidBody.cxx @@ -34,7 +34,7 @@ namespace SHADE } void RigidBody::IsGravityEnabled::set(bool value) { - return GetNativeComponent()->SetIsGravityEnabled(value); + return GetNativeComponent()->SetGravityEnabled(value); } bool RigidBody::IsAllowedToSleep::get() { @@ -217,4 +217,10 @@ namespace SHADE { return Convert::ToCLI(GetNativeComponent()->GetTorque()); } + + void RigidBody::ClearTorque() + { + GetNativeComponent()->ClearTorque(); + } + } \ No newline at end of file diff --git a/SHADE_Managed/src/Components/RigidBody.hxx b/SHADE_Managed/src/Components/RigidBody.hxx index 8293cca4..8bfe34aa 100644 --- a/SHADE_Managed/src/Components/RigidBody.hxx +++ b/SHADE_Managed/src/Components/RigidBody.hxx @@ -155,6 +155,7 @@ namespace SHADE void AddRelativeTorque(Vector3 relativeForce); Vector3 GetTorque(); + void ClearTorque(); }; } \ No newline at end of file diff --git a/SHADE_Managed/src/Physics/Physics.cxx b/SHADE_Managed/src/Physics/Physics.cxx index 60aba58d..9e2c1413 100644 --- a/SHADE_Managed/src/Physics/Physics.cxx +++ b/SHADE_Managed/src/Physics/Physics.cxx @@ -15,15 +15,10 @@ // External Dependencies #include "Physics/System/SHPhysicsSystemInterface.h" // Project Header -#include "Components/Collider.hxx" -#include "Components/Transform.hxx" #include "Engine/GameObject.hxx" -#include "Physics/Collision/SHCollisionSpace.h" #include "Utility/Convert.hxx" #include "Utility/Debug.hxx" -using namespace System::Collections::Generic; - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -46,262 +41,49 @@ namespace SHADE /* Raycast Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - List^ Physics::Raycast(Ray ray, bool continuous) + RaycastHit Physics::Raycast(Ray ray) { - List^ results = gcnew List(); - - // Cast natively - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(ray); - raycastInfo.continuous = continuous; - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::Raycast(Convert::ToNative(ray))); } - List^ Physics::Raycast(Ray ray, float distance, bool continuous) + RaycastHit Physics::Raycast(Ray ray, float distance) { - List^ results = gcnew List(); - - // Cast natively - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(ray); - raycastInfo.distance = distance; - raycastInfo.continuous = continuous; - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::Raycast(Convert::ToNative(ray), distance)); } - List^ Physics::Linecast(Vector3 start, Vector3 end, bool continuous) + RaycastHit Physics::Linecast(Vector3 start, Vector3 end) { - List^ results = gcnew List(); - - // Cast natively - Vector3 direction = end - start; - direction.Normalise(); - const Ray CLI_RAY( start, direction ); - - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(CLI_RAY); - raycastInfo.distance = (end - start).GetMagnitude(); - raycastInfo.continuous = continuous; - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::Linecast(Convert::ToNative(start), Convert::ToNative(end))); } - List^ Physics::ColliderRaycast(GameObject object, Ray ray, bool continuous) + RaycastHit Physics::ColliderRaycast(GameObject object, Ray ray) { - List^ results = gcnew List(); - - // Get the collider's position (same as the transform) - Transform^ managedTransform = object.GetComponent(); - if (!managedTransform) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); - - const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; - ray.Position += COLLIDER_POS; - - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(ray); - raycastInfo.continuous = continuous; - raycastInfo.SetColliderID(object.EntityId); - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::ColliderRaycast(object.EntityId, Convert::ToNative(ray))); } - List^ Physics::ColliderRaycast(GameObject object, Ray ray, float distance, bool continuous) + RaycastHit Physics::ColliderRaycast(GameObject object, Ray ray, float distance) { - List^ results = gcnew List(); - - // Get the collider's position (same as the transform) - Transform^ managedTransform = object.GetComponent(); - if (!managedTransform) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); - - const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; - ray.Position += COLLIDER_POS; - - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(ray); - raycastInfo.distance = distance; - raycastInfo.continuous = continuous; - raycastInfo.SetColliderID(object.EntityId); - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::ColliderRaycast(object.EntityId, Convert::ToNative(ray), distance)); } - List^ Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, bool continuous) + RaycastHit Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray) { - List^ results = gcnew List(); - - // Get the collider's position - Vector3 shapePos = Vector3::Zero; - Collider^ managedCollider = object.GetComponent(); - if (!managedCollider) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); - - CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); - if (!managedShape) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); - - const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); - shapePos = Convert::ToCLI(NATIVE_SHAPE.GetWorldCentroid()); - - ray.Position += shapePos; - - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(ray); - raycastInfo.continuous = continuous; - raycastInfo.SetColliderID(object.EntityId); - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::ColliderRaycast(object.EntityId, shapeIndex, Convert::ToNative(ray))); } - List^ Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, float distance, bool continuous) + RaycastHit Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, float distance) { - List^ results = gcnew List(); - - // Get the collider's position - Vector3 shapePos = Vector3::Zero; - Collider^ managedCollider = object.GetComponent(); - if (!managedCollider) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); - - CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); - if (!managedShape) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); - - const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); - shapePos = Convert::ToCLI(NATIVE_SHAPE.GetWorldCentroid()); - - ray.Position += shapePos; - - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(ray); - raycastInfo.continuous = continuous; - raycastInfo.distance = distance; - raycastInfo.SetColliderID(object.EntityId); - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::ColliderRaycast(object.EntityId, shapeIndex, Convert::ToNative(ray), distance)); } - List^ Physics::ColliderLineCast(GameObject object, Vector3 start, Vector3 end, bool continuous) + RaycastHit Physics::ColliderLineCast(GameObject object, Vector3 start, Vector3 end) { - List^ results = gcnew List(); - - // Get the collider's position (same as the transform) - Transform^ managedTransform = object.GetComponent(); - if (!managedTransform) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); - - const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; - start += COLLIDER_POS; - - Vector3 direction = end - start; - direction.Normalise(); - const Ray CLI_RAY( start, direction ); - - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(CLI_RAY); - raycastInfo.distance = (end - start).GetMagnitude(); - raycastInfo.continuous = continuous; - raycastInfo.SetColliderID(object.EntityId); - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::ColliderLinecast(object.EntityId, Convert::ToNative(start), Convert::ToNative(end))); } - List^ Physics::ColliderLineCast(GameObject object, int shapeIndex, Vector3 start, Vector3 end, bool continuous) + RaycastHit Physics::ColliderLineCast(GameObject object, int shapeIndex, Vector3 start, Vector3 end) { - List^ results = gcnew List(); - - // Get the collider's position - Vector3 shapePos = Vector3::Zero; - Collider^ managedCollider = object.GetComponent(); - if (!managedCollider) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); - - CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); - if (!managedShape) - throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); - - const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); - shapePos = Convert::ToCLI(NATIVE_SHAPE.GetWorldCentroid()); - - start += shapePos; - - Vector3 direction = end - start; - direction.Normalise(); - const Ray CLI_RAY( start, direction ); - - SHCollisionSpace::RaycastInfo raycastInfo; - raycastInfo.ray = Convert::ToNative(CLI_RAY); - raycastInfo.continuous = continuous; - raycastInfo.distance = (end - start).GetMagnitude(); - raycastInfo.SetColliderID(object.EntityId); - - const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); - if (!NATIVE_RESULTS.empty()) - { - for (const auto& nativeResult : NATIVE_RESULTS) - results->Add(Convert::ToCLI(nativeResult)); - } - - return results; + return Convert::ToCLI(SHPhysicsSystemInterface::ColliderLinecast(object.EntityId, shapeIndex, Convert::ToNative(start), Convert::ToNative(end))); } /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Physics/Physics.hxx b/SHADE_Managed/src/Physics/Physics.hxx index abbd1891..f13e5952 100644 --- a/SHADE_Managed/src/Physics/Physics.hxx +++ b/SHADE_Managed/src/Physics/Physics.hxx @@ -10,7 +10,6 @@ #pragma once - // Project Includes #include "Math/Ray.hxx" #include "RaycastHit.hxx" @@ -40,146 +39,83 @@ namespace SHADE /* Raycast Function Members */ /*---------------------------------------------------------------------------------*/ - // TODO(Diren): Add layers for raycasting - /// - /// Casts an infinite ray into the world.
- /// This raycast will stop at the first object hit. + /// Casts an infinite ray into the world. ///
/// The ray to cast. - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned. - /// - static System::Collections::Generic::List^ Raycast (Ray ray, bool continuous); + /// The result of the raycast. + static RaycastHit Raycast (Ray ray); /// /// Casts a ray for a given distance into the world. /// /// The ray to cast. /// The distance to cast the ray. - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned. - /// - static System::Collections::Generic::List^ Raycast (Ray ray, float distance, bool continuous); + /// The result of the raycast. + static RaycastHit Raycast (Ray ray, float distance); /// /// Casts a bounded ray into the world. /// /// The start of the bounded ray. /// The end of the bounded ray. - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned. - /// - static System::Collections::Generic::List^ Linecast (Vector3 start, Vector3 end, bool continuous); + /// The result of the raycast. + static RaycastHit Linecast (Vector3 start, Vector3 end); /// /// Casts an infinite ray w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// - /// The ray to cast.
- /// The position of the ray is offset from the collider's position. - /// - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned. - /// - static System::Collections::Generic::List^ ColliderRaycast (GameObject object, Ray ray, bool continuous); + /// The ray to cast. + /// The result of the raycast. + static RaycastHit ColliderRaycast (GameObject object, Ray ray); /// /// Casts a ray for a given distance w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// - /// The ray to cast.
- /// The position of the ray is offset from the collider's position. - /// + /// The ray to cast. /// The distance to cast the ray. - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned. - /// - static System::Collections::Generic::List^ ColliderRaycast (GameObject object, Ray ray, float distance, bool continuous); + /// The result of the raycast. + static RaycastHit ColliderRaycast (GameObject object, Ray ray, float distance); /// /// Casts an infinite ray w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// - /// The ray to cast.
- /// The position of the ray is offset from the collider's position. - /// - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned. - /// - static System::Collections::Generic::List^ ColliderRaycast (GameObject object, int shapeIndex, Ray ray, bool continuous); + /// The ray to cast. + /// The result of the raycast. + static RaycastHit ColliderRaycast (GameObject object, int shapeIndex, Ray ray); /// /// Casts a ray for a given distance w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// - /// The ray to cast.
- /// The position of the ray is offset from the collider's position. - /// + /// The ray to cast. /// The distance to cast the ray. - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned. - /// - static System::Collections::Generic::List^ ColliderRaycast (GameObject object, int shapeIndex, Ray ray, float distance, bool continuous); + /// The result of the raycast. + static RaycastHit ColliderRaycast (GameObject object, int shapeIndex, Ray ray, float distance); /// /// Casts a bounded ray w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// - /// The start of the bounded ray.
- /// The start of the ray is offset from the collider's position. + /// The start of the bounded ray. /// - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned. - /// - static System::Collections::Generic::List^ ColliderLineCast (GameObject object, Vector3 start, Vector3 end, bool continuous); + /// The result of the raycast. + static RaycastHit ColliderLineCast (GameObject object, Vector3 start, Vector3 end); /// /// Casts a bounded ray w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// - /// The start of the bounded ray.
- /// The start of the ray is offset from the collider's position. + /// The start of the bounded ray. /// The end of the bounded ray. - /// - /// Whether or not the raycast should stop at the first object hit. - /// - /// - /// The results of the raycast. If nothing was hit, an empty list is returned./// - static System::Collections::Generic::List^ ColliderLineCast (GameObject object, int shapeIndex, Vector3 start, Vector3 end, bool continuous); + /// The result of the raycast. + static RaycastHit ColliderLineCast (GameObject object, int shapeIndex, Vector3 start, Vector3 end); private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index 1393fe33..a5a0ebc7 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -30,6 +30,7 @@ of DigiPen Institute of Technology is prohibited. #include "Engine/Application.hxx" #include "Physics/System/SHPhysicsSystemInterface.h" #include "Physics/SHPhysicsEvents.h" +#include "Physics/Collision/SHCollisionInfo.h" namespace SHADE { @@ -604,8 +605,8 @@ namespace SHADE { auto entities = { - std::make_pair(collisionInfo.info.GetEntityA(), collisionInfo.info.GetEntityB()), - std::make_pair(collisionInfo.info.GetEntityB(), collisionInfo.info.GetEntityA()) + std::make_pair(collisionInfo.GetEntityA(), collisionInfo.GetEntityB()), + std::make_pair(collisionInfo.GetEntityB(), collisionInfo.GetEntityA()) }; for (auto entity : entities) { @@ -624,15 +625,15 @@ namespace SHADE for (int i = 0; i < entityScripts->Count; ++i) { Script^ script = entityScripts[i]; - switch (collisionInfo.state) + switch (collisionInfo.GetCollisionState()) { - case SHCollisionState::ENTER: + case SHCollisionInfo::State::ENTER: script->OnCollisionEnter(info); break; - case SHCollisionState::STAY: + case SHCollisionInfo::State::STAY: script->OnCollisionStay(info); break; - case SHCollisionState::EXIT: + case SHCollisionInfo::State::EXIT: script->OnCollisionExit(info); break; } @@ -646,8 +647,8 @@ namespace SHADE { auto entities = { - std::make_pair(triggerInfo.info.GetEntityA(), triggerInfo.info.GetEntityB()), - std::make_pair(triggerInfo.info.GetEntityB(), triggerInfo.info.GetEntityA()) + std::make_pair(triggerInfo.GetEntityA(), triggerInfo.GetEntityB()), + std::make_pair(triggerInfo.GetEntityB(), triggerInfo.GetEntityA()) }; for (auto entity : entities) { @@ -666,15 +667,15 @@ namespace SHADE for (int i = 0; i < entityScripts->Count; ++i) { Script^ script = entityScripts[i]; - switch (triggerInfo.state) + switch (triggerInfo.GetCollisionState()) { - case SHCollisionState::ENTER: + case SHCollisionInfo::State::ENTER: script->OnTriggerEnter(info); break; - case SHCollisionState::STAY: + case SHCollisionInfo::State::STAY: script->OnTriggerStay(info); break; - case SHCollisionState::EXIT: + case SHCollisionInfo::State::EXIT: script->OnTriggerExit(info); break; } diff --git a/SHADE_Managed/src/Utility/Convert.cxx b/SHADE_Managed/src/Utility/Convert.cxx index e3a53661..590a3cf0 100644 --- a/SHADE_Managed/src/Utility/Convert.cxx +++ b/SHADE_Managed/src/Utility/Convert.cxx @@ -135,7 +135,6 @@ namespace SHADE return cli; } - /*---------------------------------------------------------------------------------*/ /* Handle Conversions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Utility/Convert.hxx b/SHADE_Managed/src/Utility/Convert.hxx index f48eb66d..fb373c51 100644 --- a/SHADE_Managed/src/Utility/Convert.hxx +++ b/SHADE_Managed/src/Utility/Convert.hxx @@ -144,14 +144,14 @@ namespace SHADE /// /// Converts from a managed RaycastHit to a native SHPhysicsRaycastResult /// - /// The managed RaycastHit to convert from. + /// The managed RaycastHit to convert from. /// Native copy of a managed RaycastHit. static SHPhysicsRaycastResult ToNative(RaycastHit cli); /// - /// Converts from a native SHPhysicsRaycastResult to a managed RaycastHit + /// Converts from native SHPhysicsRaycastResult to a managed RaycastHit. /// - /// The native SHPhysicsRaycastResult to convert from. + /// The native SHPhysicsRaycastResult to convert from. /// Managed copy of a native SHPhysicsRaycastResult. static RaycastHit ToCLI(const SHPhysicsRaycastResult& native); From b3f79e31dbca9385e510b43a029401022e645ed0 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 30 Jan 2023 14:58:52 +0800 Subject: [PATCH 158/164] Forgot to commit shader binary --- Assets/Shaders/Anim_VS.shshaderb | Bin 5837 -> 6053 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Assets/Shaders/Anim_VS.shshaderb b/Assets/Shaders/Anim_VS.shshaderb index 0c71194940a697833a5595575c1fcae14556c0bf..6318555888e0c1f04b668c241c14b5ab73baa8b8 100644 GIT binary patch literal 6053 zcmZ9O`FmVd8OLv$WQr6h1zJ#`&ZKCotp&BzDpDF+48_m{TZ${;lDQ2RCo^F(NoieZ za9^Rg3kbOHJMJRl0x6*)(sf zoQj-|oP%sZo`-Blu0dXnEFwpdCFBJ1Z)A{vE0`Z6=@1#l#;3+6CdS6Enwp-Qo0{(( zX=PKbyq!%o^KLfX%NH~KPO13i?djG+v+6L6N42hD);NVV9JOTSGM-I)SvOBxd)v+I z_QGanPbEpRyt(XaeXV@vmfm=JBG?ddGiVjC{b zByab!nXJ1f?WHiau!CA)wO%5MxXpudR=f0*@bv!aUWw3p}Pl%dkC(&2RZj;4c=e(5NoKrhp54OYz_8P zfA6pQQ%ZfQ)cwx6p7+n8zau#F$vFq-nsU|>oLH`Zmj;V`QJZ(qwe>lhisc(<>f*IT zc|LW-b=2Lrx??@{c?b28@7sooy_iGWWyH8Q_anEt%o+Qsqn`$Ee)H(B?wyy@zM9!s z?Ge8Qto>ZfJ5<^e=q ze|6uBoONByTu035x|c9F5P4_CJkMoz@7m3y?mLq+&o<_Isq23!cmR>tU)^^o=ULU6 zcOXe}>b_$+?c>bGYxjKjF}t>$_6cUsS3c_T-ADWs zxbOR9FPm_l%Y|6{CU}PH!Mnf6ZJkaKFEPH_Roct#2PnehW56yX&Z*C^>ce z=XYTM+0OfRf9}Cy+<#&GKI*%H`69%9s(beSjR;-4zX{YY$L{+*1MzK-l4UjXnTX#8 zzXv;6L0up1o~b{b&q3BA<}lZJh`c_&)u$om@S9)`b$ueogYPM7yi!&%c*Z;egWd!xsLcc z^9vCPFKQhH zySJ$IN^rH--RN?TsA~^cu3GC=C0DKWC2+>;uf1C97`kig6SZCqw$@#U=c=xswO!2o zQsf#$|8eGP5&h$SsOuAZyAEt0PUD7-Fux43X0f&dWxw}>wYk4W!6%8FV}ieS_V9Y- zKq*W`9`_nC2f@aeKVohG%PS6T)tHx~8zUDnuK>#{j)<9L5BtzxiCBxd_4S;5oAT~k zpI0Gr(bLy}{Z@KU+{=xKKJugFonpQT@hs0re3Q}Z6s*6)wQgdTi@2M?5$8LMxM{F) z)^D7CauJt-Bd!UKxIqORN|^#HsN-Ul|;`xo5%!N$r( z><7U5Y74&)f{pbK1ot7Zv2qdnVX(g1!tWzsV+SkTN5RI*MeMy`eYN@b#XjDLxEE_O zw?5HNZPBay!PS0#4BdWukHhByuzd9ML9lW1(a(p#(a#6L(T9(NwT+f{@Dt!AB=Xn6 zpG4%NpPvF7Cm(q}4X*a{Gw5=TIIquwjdjG?d=9Lyw($Er*jRfP+!w&c%0=uK!TM?o zzb}D}wRgdN8EmXv#C`>=ueR{}D%jX+Kfi`+9n_cQXGAIf1M~JO_3C{>Mr?kpBby10FX3 literal 5837 zcmZ9O`I{V76^3iJ4kkbdgn$aMXMzrz7`G6kNT5k@hz`tPLQqkMV!ARZ?CBnQdL{`j z1os_+`;PmD`@SQ_xbOJC_>0fu`&QMN)RW3{a=-JP<(_-ay`2o5-jWpG$ndsxNivkI zPcBIQQ&P#MWEdt%HYAN=oti#4y>ofc-nr|V=jw7{GE#WzvoX22z@z-VnC4xT^~eRt zW@HEQd}KG$LJlH}$i2vM*2hUYN`|TFxv80%sp*^M=9iY{mIo&~*<2^@ zW^?VlpUn^Q)l9$hYJPclzO&MDUZEO;)>!TdgM zy)mWDzGBhoj|NJ^l`ACy6?er`+K799ai^K_a4{vR;5QifA_S1wzFJ^UbYu5h~ zR&Da;Q}@lt8L#i9h@bvjShYp{PX@c6dhGX9u=ykY3a~L-@NBXw)-qmU`+Yh31go67 zdu?Miul!a{2xQUq4x%uyg z`^vkT<9V!}Tbnu5ePeRw*u~ncbp5XZ4{F9<50WG&uh)Ms$Xw%y`VFl5 zT7&WGEksWHK34s;+sm6+-AhjUOIhuueC+QVkNCsj()aA#?u4@kS7Pz~Pas>tzF~Fq zzMfSdIrFOfj^)(7FTP{xdaIRW^D3)qUqt*RcwZS9W#Zeb`T|;j?u=3g=m_ zAm49Un*}cK&}p!_Rk z1u=*3-yG`tM2;)L=J0(+4u9k1BgfOg=I~9MLtP*3zN=>^pQ8Gy^;(67l7r|y~8g=d@HvR-(-CeBIg&}i@|cvC%9`X&RNaUp>fuiAja#j z-8XlNyxOiq+*=>d=Nr5p@m=*t4C_)a}Iq z#G1w04plvT8CaWVv9^~Za(*-XYhhoe5qZVWJi|!D+yXX6K4NYK%PW58#>~JOW1fha zEwFm|Ma(Q`@b~04#9GX)uQT<&%X@BpUV+HPOb>zm{jv9+<#t3L`4)NoJ-P$2m)?#?Yxh2mv%V9N^9$}> zU}OCrsQlgy)>m8jy$5XUP>p*p*jTxUeIHm~Z4vu^u(886?gL+}3Dy7TlNhtEgA@-fekf{l}p zd43EW^VA-5_&8WwtGa`q06Wjf-voaWk&k(P3T&KwA*jQ&5+?T+{%Ei842J5RW{JsJKowtJim$VJRhz3d Date: Mon, 30 Jan 2023 16:47:03 +0800 Subject: [PATCH 159/164] Spatial setup --- .../src/AudioSystem/SHAudioSystem.cpp | 25 +++++++++++++++---- SHADE_Engine/src/AudioSystem/SHAudioSystem.h | 3 +-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp index 543523e0..b4330979 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp @@ -125,14 +125,29 @@ namespace SHADE SHTransformComponent* listenerTransform = SHComponentManager::GetComponent_s(listener.GetEID()); if (listenerTransform) { - listener.SetPos(listenerTransform->GetLocalPosition()); - listener.SetForward({ (listenerTransform->GetLocalScale()[0] > 0.f) ? 1.f : -1.f, 0.f, 0.f }); + listener.SetPos(listenerTransform->GetWorldPosition()); // TODO: Clean up listener + listener.SetForward({ (listenerTransform->GetLocalScale()[0] > 0.f) ? 1.f : -1.f, 0.f, 0.f }); //TODO: USE CORRECT FORWARD FMOD_VECTOR pos = { listener.pos[0] ,listener.pos[1] ,0.f }; FMOD_VECTOR forward = { listener.forward[0] ,listener.forward[1] ,listener.forward[2] }; FMOD_VECTOR up = { listener.up[0] ,listener.up[1] ,listener.up[2] }; fmodSystem->set3DListenerAttributes(0, &pos, nullptr, &forward, &up); } } + + auto [begin, end] = audioClipLibrary.GetDenseAccess(); + for(auto it = begin; it != end; ++it) + { + if(it->instance && (it->transformRef != MAX_EID)) + { + if(SHTransformComponent* transformComponent = SHComponentManager::GetComponent_s(it->transformRef)) + { + FMOD_3D_ATTRIBUTES attribs{}; //TODO: Set other attribs + auto pos = transformComponent->GetWorldPosition(); + attribs.position = {pos.x, pos.y, pos.z}; + it->instance->set3DAttributes(&attribs); + } + } + } } SHAudioSystem::AudioRoutine::AudioRoutine() @@ -372,13 +387,13 @@ namespace SHADE { if (auto transform = SHComponentManager::GetComponent_s(eid)) { - //handle->transformRef = transform; + handle->transformRef = eid; } } - void SHAudioSystem::DetachAudioClipToObject(Handle handle, EntityID eid) + void SHAudioSystem::DetachAudioClipFromObject(Handle handle) { - //handle->transformRef = nullptr; + handle->transformRef = MAX_EID; } //AudioClip* SHAudioSystem::CreateAudioClip(const char* path) diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h index d1fc325a..0c12cef4 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.h +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.h @@ -26,7 +26,6 @@ namespace SHADE class SH_API AudioClip { public: - //expose to sxripting void Play(); //void Play(SHVec3 position); void Stop(bool fadeOut = true); @@ -75,7 +74,7 @@ namespace SHADE void AddAudioClipToSFXChannelGroup(Handle handle); void AttachAudioClipToObject(Handle handle, EntityID eid); - void DetachAudioClipToObject(Handle handle, EntityID eid); + void DetachAudioClipFromObject(Handle handle); /// float GetBgmVolume(); float GetSfxVolume(); From 526c6dc883cf26021861b87d3d479431790b17db Mon Sep 17 00:00:00 2001 From: SHAM-DP Date: Mon, 30 Jan 2023 16:47:38 +0800 Subject: [PATCH 160/164] Spatial scripting inteface changes (remove eid requirement) --- SHADE_Managed/src/Audio/Audio.cxx | 4 ++-- SHADE_Managed/src/Audio/Audio.hxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SHADE_Managed/src/Audio/Audio.cxx b/SHADE_Managed/src/Audio/Audio.cxx index a4600df1..9432886f 100644 --- a/SHADE_Managed/src/Audio/Audio.cxx +++ b/SHADE_Managed/src/Audio/Audio.cxx @@ -123,10 +123,10 @@ namespace SHADE audioSys->AttachAudioClipToObject(handle.NativeObject, eid); } - void Audio::DetachAudioClipToObject(AudioClipHandler handle, EntityID eid) + void Audio::DetachAudioClipFromObject(AudioClipHandler handle) { auto audioSys = SHSystemManager::GetSystem(); - audioSys->DetachAudioClipToObject(handle.NativeObject, eid); + audioSys->DetachAudioClipFromObject(handle.NativeObject); } } diff --git a/SHADE_Managed/src/Audio/Audio.hxx b/SHADE_Managed/src/Audio/Audio.hxx index 1a5ced53..a8c94156 100644 --- a/SHADE_Managed/src/Audio/Audio.hxx +++ b/SHADE_Managed/src/Audio/Audio.hxx @@ -107,6 +107,6 @@ namespace SHADE static void AddAudioClipToBGMChannelGroup(AudioClipHandler handle); static void AddAudioClipToSFXChannelGroup(AudioClipHandler handle); static void AttachAudioClipToObject(AudioClipHandler handle, EntityID eid); - static void DetachAudioClipToObject(AudioClipHandler handle, EntityID eid); + static void DetachAudioClipFromObject(AudioClipHandler handle); }; } From cf999b0d3bcf3d282f873ee28640c51ca631e80a Mon Sep 17 00:00:00 2001 From: SHAM-DP Date: Mon, 30 Jan 2023 17:44:06 +0800 Subject: [PATCH 161/164] Added entity filter --- .../HierarchyPanel/SHHierarchyPanel.cpp | 128 ++++++++++++------ .../HierarchyPanel/SHHierarchyPanel.h | 3 +- SHADE_Engine/src/Editor/SHEditor.cpp | 2 +- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 16 +-- 4 files changed, 99 insertions(+), 50 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index ef4ad35e..a7e85ad4 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -57,9 +57,13 @@ namespace SHADE skipFrame = false; return; } - DrawMenuBar(); auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - + DrawMenuBar(); + if(wasFilterChanged && !filter.empty()) + { + filterHighlightedEntities.clear(); + filterOpenEntities.clear(); + } if (const auto root = sceneGraph.GetRoot()) { auto const& children = root->GetChildren(); @@ -67,7 +71,13 @@ namespace SHADE for (const auto child : children) { if (child) + { + if(wasFilterChanged && !filter.empty()) + { + EntityFilterCheck(child); + } RecursivelyDrawEntityNode(child); + } if (skipFrame) { ImGui::End(); @@ -177,9 +187,10 @@ namespace SHADE void SHHierarchyPanel::DrawHierarchyPanelFilter() noexcept { + wasFilterChanged = false; if(ImGui::InputTextWithHint("##hierarchyPanelFilter", "Filter", &filter)) { - + wasFilterChanged = true; } ImGui::SameLine(); if(ImGui::Button("x")) @@ -199,29 +210,70 @@ namespace SHADE bool result = false; result |= SHStringUtilities::StringFindInsensitive(entity->name, filter) != std::string::npos; - + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) { result |= SHComponentManager::HasComponent(eid); } - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; - //result |= SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos; + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } + if(SHStringUtilities::StringFindInsensitive(rttr::type::get().get_name().data(), filter) != std::string::npos) + { + result |= SHComponentManager::HasComponent(eid); + } - //std::vector const& children = entityNode->GetChildren(); - - //for (auto const& child : children) - //{ - // result |= EntityFilterCheck(child); - //} + if(result) + { + filterHighlightedEntities.push_back(eid); + } + std::vector const& children = entityNode->GetChildren(); + + bool open = false; + for (auto const& child : children) + { + open |= EntityFilterCheck(child); + } + result |= open; + + if(open) + { + filterOpenEntities.push_back(eid); + } return result; } @@ -245,31 +297,27 @@ namespace SHADE const bool isSelected = (std::ranges::find(editor->selectedEntities, eid) != editor->selectedEntities.end()); - bool highlighted = false; - //if(!filter.empty()) - //{ - // highlighted = EntityFilterCheck(currentNode); - // if (highlighted) - // { - // ImGui::PushStyleColor(ImGuiCol_Text, highlightedColor); - // - // ImGui::SetNextItemOpen(true); - // - // - // } - // else - // { - // - // } - //} + bool highlighted = false, open = false; + if(!filter.empty()) + { + highlighted = (std::ranges::find(filterHighlightedEntities, eid) != filterHighlightedEntities.end()); + if(open = std::ranges::find(filterOpenEntities, eid) != filterOpenEntities.end()) + { + ImGui::SetNextItemOpen(true); + } + + if(!open && !highlighted) + { + return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + } + ImGui::PushStyleColor(ImGuiCol_Text, highlighted ? highlightedColor : ImVec4(0.5f, 0.5f, 0.5f, 1.f )); + } const ImGuiTreeNodeFlags nodeFlags = ((isSelected) ? ImGuiTreeNodeFlags_Selected : 0) | ((children.empty()) ? ImGuiTreeNodeFlags_Leaf : ImGuiTreeNodeFlags_OpenOnArrow); - - //Draw Node bool isNodeOpen = ImGui::TreeNodeEx(reinterpret_cast(eid), nodeFlags, "%u: %s", SHEntityManager::GetEntityIndex(eid), entity->name.c_str()); - if (highlighted) + if(!filter.empty()) { ImGui::PopStyleColor(); } diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h index c6670948..d217a307 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h @@ -44,7 +44,8 @@ namespace SHADE std::string filter; bool isAnyNodeSelected = false; EntityID scrollTo = MAX_EID; - std::vector draggingEntities; + std::vector draggingEntities, filterOpenEntities, filterHighlightedEntities; + bool wasFilterChanged = false; };//class SHHierarchyPanel diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 2276164f..4d180635 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -106,8 +106,8 @@ namespace SHADE SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); - SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index c4990153..3eb2cec5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -568,14 +568,14 @@ namespace SHADE #endif } - if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::B)) - { - auto& lightComps = SHComponentManager::GetDense(); - for (auto& comp : lightComps) - { - comp.SetEnableShadow(true); - } - } + //if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::B)) + //{ + // auto& lightComps = SHComponentManager::GetDense(); + // for (auto& comp : lightComps) + // { + // comp.SetEnableShadow(true); + // } + //} renderGraph->Begin(frameIndex); auto cmdBuffer = renderGraph->GetCommandBuffer(frameIndex); From 559cda92f1660678eef5a98f8729dd9df4697032 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Mon, 30 Jan 2023 18:27:29 +0800 Subject: [PATCH 162/164] Delete Editor.SHConfig --- Assets/Editor/Editor.SHConfig | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 Assets/Editor/Editor.SHConfig diff --git a/Assets/Editor/Editor.SHConfig b/Assets/Editor/Editor.SHConfig deleted file mode 100644 index 51425027..00000000 --- a/Assets/Editor/Editor.SHConfig +++ /dev/null @@ -1,4 +0,0 @@ -Start Maximized: true -Working Scene ID: 97161771 -Window Size: {x: 1920, y: 1080} -Style: 0 \ No newline at end of file From f2b8e526086ed690da7086e00421a44d548ce925 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Mon, 30 Jan 2023 18:28:49 +0800 Subject: [PATCH 163/164] Commented unused shader build lines --- .../src/Assets/Asset Types/Models/SHMeshAsset.h | 2 +- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h index 18272d02..14744c4d 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHMeshAsset.h @@ -58,6 +58,6 @@ namespace SHADE std::vector Indices; std::vector VertexBoneIndices; std::vector VertexBoneWeights; - uint32_t BoneCount; + uint32_t BoneCount; }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index b10ca123..ec26837e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -127,12 +127,12 @@ namespace SHADE SHFreetypeInstance::Init(); - SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl", false); - SHAssetManager::CompileAsset("../../Assets/Shaders/SSAO_CS.glsl", false); - SHAssetManager::CompileAsset("../../Assets/Shaders/SSAOBlur_CS.glsl", false); - SHAssetManager::CompileAsset("../../Assets/Shaders/PureCopy_CS.glsl", false); - SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_VS.glsl", false); - SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_FS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/SSAO_CS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/SSAOBlur_CS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/PureCopy_CS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_VS.glsl", false); + //SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_FS.glsl", false); // Load Built In Shaders static constexpr AssetID VS_DEFAULT = 39210065; defaultVertShader = SHResourceManager::LoadOrGet(VS_DEFAULT); From c13f06e4a60c0ef38d4dae6a12e252d12b5718e0 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Mon, 30 Jan 2023 19:55:42 +0800 Subject: [PATCH 164/164] Added warning line to check for existing path --- SHADE_Engine/src/Filesystem/SHFileSystem.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SHADE_Engine/src/Filesystem/SHFileSystem.cpp b/SHADE_Engine/src/Filesystem/SHFileSystem.cpp index a28f70ca..cbcbd45b 100644 --- a/SHADE_Engine/src/Filesystem/SHFileSystem.cpp +++ b/SHADE_Engine/src/Filesystem/SHFileSystem.cpp @@ -105,6 +105,12 @@ namespace SHADE std::vector assets; // Get all subfolders/files in this current folder + if (!std::filesystem::exists(std::filesystem::path(folder->path))) + { + SHLOG_WARNING("[Asset Manager] Path to build directory does not exist!: {}", folder->path); + continue; + } + for (auto& dirEntry : std::filesystem::directory_iterator(folder->path)) { auto path = dirEntry.path();