From 632df80d068e7cd1049ae3423a254cc1c3f4413e Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 26 Oct 2022 16:05:18 +0800 Subject: [PATCH 001/113] Refactored SHPrimitiveGenerator and added Sphere generation support --- .../MiddleEnd/Meshes/SHPrimitiveGenerator.cpp | 165 ++++++++++++++---- .../MiddleEnd/Meshes/SHPrimitiveGenerator.h | 65 +++++-- 2 files changed, 182 insertions(+), 48 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp index be181beb..6c708e58 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.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. @@ -18,50 +18,59 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Static Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + SHMeshData SHPrimitiveGenerator::cubeMesh; + SHMeshData SHPrimitiveGenerator::sphereMesh; + + /*-----------------------------------------------------------------------------------*/ + /* Primitive Generation Functions */ + /*-----------------------------------------------------------------------------------*/ SHMeshData SHPrimitiveGenerator::Cube() noexcept { SHMeshData mesh; - mesh.VertexPositions = + mesh.VertexPositions = { // front - SHVec3(-0.5f, -0.5f, 0.5f), - SHVec3( 0.5f, -0.5f, 0.5f), - SHVec3( 0.5f, 0.5f, 0.5f), - SHVec3(-0.5f, 0.5f, 0.5f), + SHVec3(-0.5f, -0.5f, 0.5f), + SHVec3(0.5f, -0.5f, 0.5f), + SHVec3(0.5f, 0.5f, 0.5f), + SHVec3(-0.5f, 0.5f, 0.5f), // back - SHVec3(-0.5f, -0.5f, -0.5f), - SHVec3(-0.5f, 0.5f, -0.5f), - SHVec3( 0.5f, 0.5f, -0.5f), - SHVec3( 0.5f, -0.5f, -0.5f), + SHVec3(-0.5f, -0.5f, -0.5f), + SHVec3(-0.5f, 0.5f, -0.5f), + SHVec3(0.5f, 0.5f, -0.5f), + SHVec3(0.5f, -0.5f, -0.5f), - // top + // top SHVec3(-0.5f, 0.5f, -0.5f), SHVec3(-0.5f, 0.5f, 0.5f), - SHVec3( 0.5f, 0.5f, 0.5f), - SHVec3( 0.5f, 0.5f, -0.5f), - + SHVec3(0.5f, 0.5f, 0.5f), + SHVec3(0.5f, 0.5f, -0.5f), + // bottom - SHVec3(-0.5f, -0.5f, -0.5f), - SHVec3( 0.5f, -0.5f, -0.5f), - SHVec3( 0.5f, -0.5f, 0.5f), - SHVec3(-0.5f, -0.5f, 0.5f), - + SHVec3(-0.5f, -0.5f, -0.5f), + SHVec3(0.5f, -0.5f, -0.5f), + SHVec3(0.5f, -0.5f, 0.5f), + SHVec3(-0.5f, -0.5f, 0.5f), + // right - SHVec3(0.5f, -0.5f, -0.5f), - SHVec3(0.5f, 0.5f, -0.5f), - SHVec3(0.5f, 0.5f, 0.5f), - SHVec3(0.5f, -0.5f, 0.5f), - + SHVec3(0.5f, -0.5f, -0.5f), + SHVec3(0.5f, 0.5f, -0.5f), + SHVec3(0.5f, 0.5f, 0.5f), + SHVec3(0.5f, -0.5f, 0.5f), + // left - SHVec3(-0.5f, -0.5f, -0.5f), - SHVec3(-0.5f, -0.5f, 0.5f), - SHVec3(-0.5f, 0.5f, 0.5f), - SHVec3(-0.5f, 0.5f, -0.5f) + SHVec3(-0.5f, -0.5f, -0.5f), + SHVec3(-0.5f, -0.5f, 0.5f), + SHVec3(-0.5f, 0.5f, 0.5f), + SHVec3(-0.5f, 0.5f, -0.5f) }; - mesh.VertexTexCoords = + mesh.VertexTexCoords = { SHVec2(0.0f, 1.0f), SHVec2(1.0f, 1.0f), @@ -99,7 +108,7 @@ namespace SHADE SHVec2(0.0f, 0.0f) }; - mesh.VertexTangents = + mesh.VertexTangents = { // front SHVec3(1.0f, 0.0f, 0.0f), @@ -118,7 +127,7 @@ namespace SHADE SHVec3(1.0f, 0.0f, 0.0f), SHVec3(1.0f, 0.0f, 0.0f), SHVec3(1.0f, 0.0f, 0.0f), - + // bottom SHVec3(1.0f, 0.0f, 0.0f), SHVec3(1.0f, 0.0f, 0.0f), @@ -193,10 +202,93 @@ namespace SHADE Handle SHPrimitiveGenerator::Cube(SHMeshLibrary& meshLibrary) noexcept { - static SHMeshData meshData = Cube(); + if (cubeMesh.VertexPositions.empty()) + cubeMesh = Cube(); + return addMeshDataTo(cubeMesh, meshLibrary); + } + + Handle SHPrimitiveGenerator::Cube(SHGraphicsSystem& gfxSystem) noexcept + { + if (cubeMesh.VertexPositions.empty()) + cubeMesh = Cube(); + return addMeshDataTo(cubeMesh, gfxSystem); + } + + SHADE::SHMeshData SHPrimitiveGenerator::Sphere() noexcept + { + SHMeshData meshData; + + const int LAT_SLICES = 8; + const int LONG_SLICES = 8; + float radius = 1; + for (int latNum = 0; latNum <= LAT_SLICES; ++latNum) + { + float theta = static_cast(latNum * std::numbers::pi / LAT_SLICES); + float sinTheta = sin(theta); + float cosTheta = cos(theta); + + for (int longNum = 0; longNum <= LONG_SLICES; ++longNum) + { + float phi = static_cast(longNum * 2 * std::numbers::pi / LONG_SLICES); + float sinPhi = sin(phi); + float cosPhi = cos(phi); + + const SHVec3 NORMAL = SHVec3(cosPhi * sinTheta, cosTheta, sinPhi * sinTheta); + meshData.VertexNormals.emplace_back(NORMAL); + meshData.VertexTangents.emplace_back(/* TODO */); + meshData.VertexTexCoords.emplace_back + ( + 1.0f - (longNum / static_cast(LONG_SLICES)), + 1.0f - (latNum / static_cast(LAT_SLICES)) + ); + meshData.VertexPositions.emplace_back(radius * NORMAL.x, radius * NORMAL.y, radius * NORMAL.z); + } + } + + for (int latNumber{}; latNumber < LAT_SLICES; latNumber++) + { + for (int longNumber{}; longNumber < LONG_SLICES; longNumber++) + { + const auto FIRST = (latNumber * (LONG_SLICES + 1)) + longNumber; + const auto SECOND = (FIRST + LONG_SLICES + 1); + + meshData.Indices.emplace_back(FIRST); + meshData.Indices.emplace_back(SECOND); + meshData.Indices.emplace_back(FIRST + 1); + + meshData.Indices.emplace_back(SECOND); + meshData.Indices.emplace_back(SECOND + 1); + meshData.Indices.emplace_back(FIRST + 1); + } + } + + return meshData; + } + + SHADE::Handle SHPrimitiveGenerator::Sphere(SHMeshLibrary& meshLibrary) noexcept + { + if (sphereMesh.VertexPositions.empty()) + sphereMesh = Sphere(); + + return addMeshDataTo(sphereMesh, meshLibrary); + } + + SHADE::Handle SHPrimitiveGenerator::Sphere(SHGraphicsSystem& gfxSystem) noexcept + { + if (sphereMesh.VertexPositions.empty()) + sphereMesh = Sphere(); + + return addMeshDataTo(sphereMesh, gfxSystem); + } + + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------------*/ + SHADE::Handle SHPrimitiveGenerator::addMeshDataTo(const SHMeshData& meshData, SHMeshLibrary& meshLibrary) noexcept + { return meshLibrary.AddMesh ( - static_cast(meshData.VertexPositions.size()), + static_cast(meshData.VertexPositions.size()), meshData.VertexPositions.data(), meshData.VertexTexCoords.data(), meshData.VertexTangents.data(), @@ -206,17 +298,16 @@ namespace SHADE ); } - Handle SHPrimitiveGenerator::Cube(SHGraphicsSystem& gfxSystem) noexcept + SHADE::Handle SHPrimitiveGenerator::addMeshDataTo(const SHMeshData& meshData, SHGraphicsSystem& gfxSystem) noexcept { - static SHMeshData meshData = Cube(); 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() ); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h index dd059a29..ef4e26a6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h @@ -4,9 +4,9 @@ \par email: kahwei.tng\@digipen.edu \date Sep 18, 2022 \brief Contains the static class definition of SHPrimitiveGenerator. - + Copyright (C) 2022 DigiPen Institute of Technology. -Reproduction or disclosure of this file or its contents without the prior written consent +Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #pragma once @@ -29,12 +29,12 @@ namespace SHADE /*************************************************************************************/ /*! \brief - Static class that contains functions for generating 3D primitives. + Static class that contains functions for generating 3D primitives. */ /*************************************************************************************/ class SH_API SHPrimitiveGenerator { - public: + public: /*---------------------------------------------------------------------------------*/ /* Constructors */ /*---------------------------------------------------------------------------------*/ @@ -42,7 +42,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Primitive Generation Functions */ - /*---------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ /***********************************************************************************/ /*! \brief @@ -52,20 +52,20 @@ namespace SHADE SHMeshData object containing vertex data for the cube. */ /***********************************************************************************/ - [[nodiscard]] static SHMeshData Cube() noexcept; + [[nodiscard]] static SHMeshData Cube() noexcept; /***********************************************************************************/ /*! \brief Produces a cube and constructs a SHMesh using the SHMeshLibrary provided. \param meshLibrary - Reference to the SHMeshLibrary to procude and store a cube mesh in. + Reference to the SHMeshLibrary to produce and store a cube mesh in. \return SHMesh object that points to the generated cube mesh in the SHMeshLibrary. */ /***********************************************************************************/ - [[nodiscard]] static Handle Cube(SHMeshLibrary& meshLibrary) noexcept; + [[nodiscard]] static Handle Cube(SHMeshLibrary& meshLibrary) noexcept; /***********************************************************************************/ /*! \brief @@ -78,12 +78,55 @@ namespace SHADE SHMesh object that points to the generated cube mesh in the SHGraphicsSystem. */ /***********************************************************************************/ - [[nodiscard]] static Handle Cube(SHGraphicsSystem& gfxSystem) noexcept; + [[nodiscard]] static Handle Cube(SHGraphicsSystem& gfxSystem) noexcept; + /***********************************************************************************/ + /*! + \brief + Produces a sphere and stores the data in a SHMeshData object. - private: + \return + SHMeshData object containing vertex data for the sphere. + */ + /***********************************************************************************/ + [[nodiscard]] static SHMeshData Sphere() noexcept; + /***********************************************************************************/ + /*! + \brief + Produces a sphere and constructs a SHMesh using the SHMeshLibrary provided. + + \param meshLibrary + Reference to the SHMeshLibrary to produce and store a sphere mesh in. + + \return + SHMesh object that points to the generated sphere mesh in the SHMeshLibrary. + */ + /***********************************************************************************/ + [[nodiscard]] static Handle Sphere(SHMeshLibrary& meshLibrary) noexcept; + /***********************************************************************************/ + /*! + \brief + Produces a sphere and constructs a SHMesh using the SHGraphicsSystem provided. + + \param gfxSystem + Reference to the SHGraphicsSystem to produce and store a sphere mesh in. + + \return + SHMesh object that points to the generated sphere mesh in the SHGraphicsSystem. + */ + /***********************************************************************************/ + [[nodiscard]] static Handle Sphere(SHGraphicsSystem& gfxSystem) noexcept; + + private: /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static SHMeshData genCubeData() noexcept; + static Handle addMeshDataTo(const SHMeshData& meshData, SHMeshLibrary& meshLibrary) noexcept; + static Handle addMeshDataTo(const SHMeshData& meshData, SHGraphicsSystem& gfxSystem) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + static SHMeshData cubeMesh; + static SHMeshData sphereMesh; }; } \ No newline at end of file From 550b8d85f0eee7f157da51803b35e9910d0f4b87 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 26 Oct 2022 16:05:50 +0800 Subject: [PATCH 002/113] Added SHDebugDrawSystem class --- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 155 ++++++++++++++++++ .../MiddleEnd/Interface/SHDebugDrawSystem.h | 93 +++++++++++ .../MiddleEnd/Interface/SHDebugDrawSystem.hpp | 41 +++++ .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 7 + .../MiddleEnd/Interface/SHGraphicsSystem.h | 10 +- TempShaderFolder/DebugDrawFs.glsl | 17 ++ TempShaderFolder/DebugDrawFs.spv | Bin 0 -> 664 bytes TempShaderFolder/DebugDrawVs.glsl | 24 +++ TempShaderFolder/DebugDrawVs.spv | Bin 0 -> 1488 bytes 9 files changed, 340 insertions(+), 7 deletions(-) create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp create mode 100644 TempShaderFolder/DebugDrawFs.glsl create mode 100644 TempShaderFolder/DebugDrawFs.spv create mode 100644 TempShaderFolder/DebugDrawVs.glsl create mode 100644 TempShaderFolder/DebugDrawVs.spv diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp new file mode 100644 index 00000000..cd6dd7ab --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -0,0 +1,155 @@ +/************************************************************************************//*! +\file SHDebugDrawSystem.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 16, 2022 +\brief Contains the definition of functions of the SHDebugDrawSystem 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. +*//*************************************************************************************/ +#include "SHpch.h" +#include "SHDebugDrawSystem.h" +#include "../Meshes/SHMeshData.h" +#include "../Meshes/SHPrimitiveGenerator.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "SHGraphicsSystem.h" +#include "Graphics/Devices/SHVkLogicalDevice.h" +#include "../../SHVkUtil.h" +#include "Graphics/MiddleEnd/Interface/SHViewport.h" +#include "Graphics/MiddleEnd/Interface/SHRenderer.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* DrawRoutine */ + /*---------------------------------------------------------------------------------*/ + SHDebugDrawSystem::DrawRoutine::DrawRoutine() + : SHSystemRoutine("Debug Draw") + {} + + void SHDebugDrawSystem::DrawRoutine::Execute(double dt) noexcept + { + auto gfxSys = SHSystemManager::GetSystem(); + if (gfxSys) + { + SHLOG_WARNING("[DebugDraw] Attempted to do debug draw without a graphics system."); + return; + } + + // Create the buffer if it doesn't exist or just update it + SHDebugDrawSystem* system = static_cast(GetSystem()); + const uint32_t DATA_SIZE = sizeof(PointVertex) * system->points.size(); + SHVkUtil::EnsureBufferAndCopyHostVisibleData(gfxSys->GetDevice(), system->vertexBuffer, system->points.data(), DATA_SIZE, vk::BufferUsageFlagBits::eVertexBuffer); + + // Reset for next frame + system->points.clear(); + } + + /*---------------------------------------------------------------------------------*/ + /* SHSystem overrides */ + /*---------------------------------------------------------------------------------*/ + void SHDebugDrawSystem::Init() + { + // Register function for subpass + const auto* GFX_SYSTEM = SHSystemManager::GetSystem(); + auto const& RENDERERS = GFX_SYSTEM->GetDefaultViewport()->GetRenderers(); + auto renderGraph = RENDERERS[SHGraphicsConstants::RenderGraphIndices::WORLD]->GetRenderGraph(); + auto subPass = renderGraph->GetNode("Debug Draw")->GetSubpass("Debug Draw"); + subPass->AddExteriorDrawCalls([&](Handle& cmdBuffer) + { + cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); + cmdBuffer->BindVertexBuffer(0, vertexBuffer, 0); + cmdBuffer->DrawArrays(static_cast(points.size()), 1, 0, 0); + }); + } + + void SHDebugDrawSystem::Exit() + { + + } + + /*---------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*---------------------------------------------------------------------------------*/ + void SHDebugDrawSystem::DrawLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) + { + points.emplace_back(PointVertex{ startPt, color }); + points.emplace_back(PointVertex{ endPt, color }); + } + + void SHDebugDrawSystem::DrawTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3) + { + DrawPoly(color, { pt1, pt2, pt3 }); + } + + void SHDebugDrawSystem::DrawQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4) + { + DrawPoly(color, { pt1, pt2, pt3, pt4 }); + } + + void SHDebugDrawSystem::DrawPoly(const SHVec4& color, std::initializer_list pointList) + { + DrawPoly(color, pointList.begin(), pointList.end()); + } + + void SHDebugDrawSystem::DrawCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) + { + static const SHVec3 EXTENTS = SHVec3 { 0.5f, 0.5f, 0.5f }; + static const SHVec3 UNIT_BOT_LEFT_BACK = SHVec3 { pos - EXTENTS }; + static const SHVec3 UNIT_BOT_RIGHT_BACK = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, 0.0f } }; + static const SHVec3 UNIT_BOT_LEFT_FRONT = SHVec3 { pos + SHVec3 { 0.0f , -EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_BOT_RIGHT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_LEFT_BACK = SHVec3 { pos + SHVec3 { EXTENTS.x, EXTENTS.y, 0.0f } }; + static const SHVec3 UNIT_TOP_RIGHT_BACK = SHVec3 { pos + SHVec3 { 0.0f , EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_LEFT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_RIGHT_FRONT = SHVec3 { pos + EXTENTS }; + + const SHVec3 BOT_LEFT_BACK = UNIT_BOT_LEFT_BACK * size; + const SHVec3 BOT_RIGHT_BACK = UNIT_BOT_RIGHT_BACK * size; + const SHVec3 BOT_LEFT_FRONT = UNIT_BOT_LEFT_FRONT * size; + const SHVec3 BOT_RIGHT_FRONT = UNIT_BOT_RIGHT_FRONT * size; + const SHVec3 TOP_LEFT_BACK = UNIT_TOP_LEFT_BACK * size; + const SHVec3 TOP_RIGHT_BACK = UNIT_TOP_RIGHT_BACK * size; + const SHVec3 TOP_LEFT_FRONT = UNIT_TOP_LEFT_FRONT * size; + const SHVec3 TOP_RIGHT_FRONT = UNIT_TOP_RIGHT_FRONT * size; + + DrawPoly + ( + color, + { + // Bottom Square + BOT_LEFT_BACK , BOT_RIGHT_BACK, + BOT_RIGHT_BACK , BOT_RIGHT_FRONT, + BOT_RIGHT_FRONT, BOT_LEFT_FRONT, + BOT_LEFT_FRONT , BOT_LEFT_BACK, + // Middle Lines + TOP_LEFT_BACK , BOT_LEFT_BACK, + TOP_RIGHT_BACK , BOT_RIGHT_BACK, + TOP_RIGHT_FRONT, BOT_RIGHT_FRONT, + TOP_LEFT_FRONT , BOT_LEFT_FRONT, + // Top Square + TOP_LEFT_BACK , TOP_RIGHT_BACK, + TOP_RIGHT_BACK , TOP_RIGHT_FRONT, + TOP_RIGHT_FRONT, TOP_LEFT_FRONT, + TOP_LEFT_FRONT , TOP_LEFT_BACK + + } + ); + } + + void SHDebugDrawSystem::DrawSphere(const SHVec4& color, const SHVec3& pos, double radius) + { + if (spherePoints.empty()) + { + // Generate + const SHMeshData SPHERE = SHPrimitiveGenerator::Sphere(); + for (const auto& idx : SPHERE.Indices) + { + spherePoints.emplace_back(SPHERE.VertexPositions[idx]); + } + } + DrawPoly(color, spherePoints.begin(), spherePoints.end()); + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h new file mode 100644 index 00000000..9344482a --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -0,0 +1,93 @@ +/************************************************************************************//*! +\file SHDebugDrawSystem.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 16, 2022 +\brief Contains the definition of the SHDebugDrawSystem 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 Includes +#include "SH_API.h" +#include "Math/Vector/SHVec2.h" +#include "Math/Vector/SHVec3.h" +#include "Math/Vector/SHVec4.h" +#include "ECS_Base/System/SHSystem.h" +#include "ECS_Base/System/SHSystemRoutine.h" +#include "Resource/SHHandle.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*---------------------------------------------------------------------------------*/ + class SHVkBuffer; + + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + /***********************************************************************************/ + /*! + \brief + Manages the Debug Draw system. + */ + /***********************************************************************************/ + class SH_API SHDebugDrawSystem : public SHSystem + { + public: + /*-------------------------------------------------------------------------------*/ + /* System Routines */ + /*-------------------------------------------------------------------------------*/ + class SH_API DrawRoutine final : public SHSystemRoutine + { + public: + DrawRoutine(); + virtual void Execute(double dt) noexcept override final; + }; + + /*-------------------------------------------------------------------------------*/ + /* SHSystem overrides */ + /*-------------------------------------------------------------------------------*/ + virtual void Init() override final; + virtual void Exit() override final; + + /*-------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*-------------------------------------------------------------------------------*/ + void DrawLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + void DrawTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3); + void DrawQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4); + void DrawPoly(const SHVec4& color, std::initializer_list pointList); + template + void DrawPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd); + void DrawCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size); + void DrawSphere(const SHVec4& color, const SHVec3& pos, double radius); + + private: + /*-------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-------------------------------------------------------------------------------*/ + struct PointVertex + { + SHVec3 Position; + SHVec4 Color; + }; + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + // CPU Buffers + std::vector points; + // GPU Buffers + Handle vertexBuffer; + // Cached Points for polygon drawing + std::vector spherePoints; + }; +} + +#include "SHDebugDrawSystem.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp new file mode 100644 index 00000000..90c645e3 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp @@ -0,0 +1,41 @@ +/************************************************************************************//*! +\file SHDebugDrawSystem.hpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 16, 2022 +\brief Contains the definition of template functions the SHDebugDrawSystem + 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 +#include "SHDebugDrawSystem.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Template Functions */ + /*---------------------------------------------------------------------------------*/ + template + void SHADE::SHDebugDrawSystem::DrawPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd) + { + // Ensure dereferenced type is SHVec3 + static_assert(std::is_same_v>, "Parameters to DrawPoly must be SHVec3."); + + const size_t POINTS_COUNT = pointListEnd - pointListBegin; + // Invalid polygon + if (POINTS_COUNT < 2) + { + SHLOG_WARNING("[SHDebugDraw] Invalid polygon provided to DrawPoly()."); + return; + } + + for (auto pointIter = pointListBegin + 1; pointIter != pointListEnd; ++pointIter) + { + points.emplace_back(PointVertex { *(pointIter - 1), color }); + points.emplace_back(PointVertex { *pointIter , color }); + } + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 9b984a4b..418e8260 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -127,15 +127,22 @@ namespace SHADE shaderSourceLibrary.LoadShader(2, "KirschCs.glsl", SH_SHADER_TYPE::COMPUTE, true); shaderSourceLibrary.LoadShader(3, "PureCopyCs.glsl", SH_SHADER_TYPE::COMPUTE, true); + shaderSourceLibrary.LoadShader(4, "DebugDrawVs.glsl", SH_SHADER_TYPE::VERTEX, true); + shaderSourceLibrary.LoadShader(5, "DebugDrawFs.glsl", SH_SHADER_TYPE::FRAGMENT, true); + shaderModuleLibrary.ImportFromSourceLibrary(device, shaderSourceLibrary); auto cubeVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl"); auto cubeFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl"); auto greyscale = shaderModuleLibrary.GetShaderModule("KirschCs.glsl"); auto pureCopy = shaderModuleLibrary.GetShaderModule("PureCopyCs.glsl"); + auto debugDrawVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl"); + auto debugDrawFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl"); cubeVS->Reflect(); cubeFS->Reflect(); greyscale->Reflect(); pureCopy->Reflect(); + debugDrawVS->Reflect(); + debugDrawFS->Reflect(); } void SHGraphicsSystem::InitSceneRenderGraph(void) noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index ae93bd78..0ede74c6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -287,9 +287,8 @@ namespace SHADE #endif Handle GetMousePickSystem(void) const noexcept {return mousePickSystem;}; Handle GetPostOffscreenRenderSystem(void) const noexcept {return postOffscreenRender;}; - //SHRenderGraph const& GetRenderGraph(void) const noexcept; + Handle GetDebugDrawPipeline(void) const noexcept { return debugDrawPipeline; } - //Handle GetRenderPass() const { return renderPass; } private: @@ -329,10 +328,6 @@ namespace SHADE Handle worldViewport; // Whole screen std::vector> viewports; // Additional viewports - // Debug Renderers - Handle debugWorldRenderer; - Handle debugScreenRenderer; - // Temp renderers Handle worldRenderer; @@ -344,8 +339,9 @@ namespace SHADE SHShaderSourceLibrary shaderSourceLibrary; SHShaderModuleLibrary shaderModuleLibrary; - // Temp Materials + // Built-In Materials Handle defaultMaterial; + Handle debugDrawPipeline; Handle worldRenderGraph; diff --git a/TempShaderFolder/DebugDrawFs.glsl b/TempShaderFolder/DebugDrawFs.glsl new file mode 100644 index 00000000..266f8ad4 --- /dev/null +++ b/TempShaderFolder/DebugDrawFs.glsl @@ -0,0 +1,17 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +#extension GL_EXT_nonuniform_qualifier : require + +layout(location = 0) in struct +{ + vec4 vertColor; +} In; + + +layout(location = 0) out vec4 outColor; + +void main() +{ + outColor = In.vertColor; +} \ No newline at end of file diff --git a/TempShaderFolder/DebugDrawFs.spv b/TempShaderFolder/DebugDrawFs.spv new file mode 100644 index 0000000000000000000000000000000000000000..dd21ce44da7b58c2713eba6b923289359d901fb1 GIT binary patch literal 664 zcmY*W+e*Vg5FMM|Z0$`^-%79eFgs$wF0;Pb3wgupKqJzfrLc-N;R}ieKEBj#Tjcs;5c(UxSeG7s_+} zMd~iJ8lDb9clw4pRn{3lJt{x&Q{T*kXsO;)*E4gYBgAWr+mngyjMX#@Rh)cyT6xCT zYGxvIGAqqqS)E*G{Hd4DHU%XpHs4p0Quf9mO>P4(i0~{h7P|eeGUm2EuXOY$qK0$1 z!<1}P{WRF4Ef6(vj@2@cHW6G zYU}X$ij1R{nElD^12^D%IX=m86PSznEqH2K?;Nk^U3m{K_WJ@;f*)b0whd!_*0_R_ Sc^w$}Yh>`A#NViT0s8^rdQj8= literal 0 HcmV?d00001 diff --git a/TempShaderFolder/DebugDrawVs.glsl b/TempShaderFolder/DebugDrawVs.glsl new file mode 100644 index 00000000..ae11658d --- /dev/null +++ b/TempShaderFolder/DebugDrawVs.glsl @@ -0,0 +1,24 @@ +#version 450 +#extension GL_KHR_vulkan_glsl : enable + +layout(location = 0) in vec3 aVertexPos; +layout(location = 1) in vec4 aVertColor; + + +layout(location = 0) out struct +{ + vec4 vertColor; // location 0 + +} Out; + +layout(set = 2, binding = 0) uniform CameraData +{ + vec4 position; + mat4 vpMat; +} cameraData; + +void main() +{ + gl_Position = cameraData.vpMat * vec4 (aVertexPos, 1.0f); + Out.vertColor = aVertColor; +} \ No newline at end of file diff --git a/TempShaderFolder/DebugDrawVs.spv b/TempShaderFolder/DebugDrawVs.spv new file mode 100644 index 0000000000000000000000000000000000000000..73e83f692e5dbd2a53de8fcea413846f1461c009 GIT binary patch literal 1488 zcmZ9LZ)+1l5XRS}m)6#{*4jU6jY*^RPoeliMGzH9<@zF$mV$4O<8sKt^%9axP+$2K z{7QZ*-v~ax+uIPjVQyxgd3JVYc2lo)mW{b#R?WIOHg0X1nh;~wOrz9c@3MC?DF!F6 zUcZE~Vd^Cj&ZfCp+DrOg#Wu%Tm)w^eNuEjS`qiZWXv30D3VTu5?}xq5Q8F4ulj28~ zMw!jiXkf=_QrKAvZ)JgJ^CX)N772kEi)RL^bxmh{%cApi{52gH>36w89gaQzx^>#i&`aZe~ZPc2dN7lFnmHfY>z4=CMG1(mN~AZ%euxU!~*t zG%jLS3m9wU3z6NZxYRNmeTfTc_HRok3;V&CYwX%nFYxN-u`m|BdV8h`bcfk+EF2DZ z(VrHwgYr(w92MbA7_(z}?J;TsLwk(5fH4D)VS{lep(5Dui2I&9@>yqMfsr#PeaFFv zbNd>m2U=p}WgmG%pBee@$fqB0^5Zi*FrV+PZ0d%;Q^m+j-}}<^K|RFpNy7yvA3n1N zBOlxYi7~%E!atP7UTSK};*DYxdm>E?j9mCf6-NB2Z1Tb12^!Hv&*OSBX>EVHd`Y*JH+8_W< u@)B!H_9QI{^)Z)@guCWGKd1wIc+|$8=Mwt${1>vBkLTmV`KQX>O8x`b2XP(% literal 0 HcmV?d00001 From 57027da80b966344746e7835219975a4e8128570 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 26 Oct 2022 16:27:58 +0800 Subject: [PATCH 003/113] Integrated into SBApplication and accounted for case where number of points to draw is 0. --- .../src/Application/SBApplication.cpp | 3 +++ .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 19 +++++++++++--- .../MiddleEnd/Interface/SHDebugDrawSystem.h | 15 +++++------ .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 25 +++++++++++++++++-- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 54dc0ccf..9b230296 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -31,6 +31,7 @@ #include "FRC/SHFramerateController.h" #include "AudioSystem/SHAudioSystem.h" #include "Camera/SHCameraSystem.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" // Components #include "Graphics/MiddleEnd/Interface/SHRenderable.h" @@ -69,6 +70,7 @@ namespace Sandbox SHGraphicsSystem* graphicsSystem = static_cast(SHSystemManager::GetSystem()); SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); + SHSystemManager::CreateSystem(); #ifdef SHEDITOR SDL_Init(SDL_INIT_VIDEO); @@ -90,6 +92,7 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index cd6dd7ab..7e82fb21 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -25,11 +25,11 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* DrawRoutine */ /*---------------------------------------------------------------------------------*/ - SHDebugDrawSystem::DrawRoutine::DrawRoutine() + SHDebugDrawSystem::ProcessPointsRoutine::ProcessPointsRoutine() : SHSystemRoutine("Debug Draw") {} - void SHDebugDrawSystem::DrawRoutine::Execute(double dt) noexcept + void SHDebugDrawSystem::ProcessPointsRoutine::Execute(double dt) noexcept { auto gfxSys = SHSystemManager::GetSystem(); if (gfxSys) @@ -40,8 +40,12 @@ namespace SHADE // Create the buffer if it doesn't exist or just update it SHDebugDrawSystem* system = static_cast(GetSystem()); + system->numPoints = system->points.size(); const uint32_t DATA_SIZE = sizeof(PointVertex) * system->points.size(); - SHVkUtil::EnsureBufferAndCopyHostVisibleData(gfxSys->GetDevice(), system->vertexBuffer, system->points.data(), DATA_SIZE, vk::BufferUsageFlagBits::eVertexBuffer); + if (DATA_SIZE > 0) + { + SHVkUtil::EnsureBufferAndCopyHostVisibleData(gfxSys->GetDevice(), system->vertexBuffer, system->points.data(), DATA_SIZE, vk::BufferUsageFlagBits::eVertexBuffer); + } // Reset for next frame system->points.clear(); @@ -59,6 +63,10 @@ namespace SHADE auto subPass = renderGraph->GetNode("Debug Draw")->GetSubpass("Debug Draw"); subPass->AddExteriorDrawCalls([&](Handle& cmdBuffer) { + // Don't draw if no points + if (numPoints <= 0) + return; + cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); cmdBuffer->BindVertexBuffer(0, vertexBuffer, 0); cmdBuffer->DrawArrays(static_cast(points.size()), 1, 0, 0); @@ -67,7 +75,10 @@ namespace SHADE void SHDebugDrawSystem::Exit() { - + if (vertexBuffer) + { + vertexBuffer.Free(); + } } /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index 9344482a..cc3e728a 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -21,14 +21,10 @@ of DigiPen Institute of Technology is prohibited. #include "ECS_Base/System/SHSystem.h" #include "ECS_Base/System/SHSystemRoutine.h" #include "Resource/SHHandle.h" +#include "Graphics/Buffers/SHVkBuffer.h" namespace SHADE { - /*---------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*---------------------------------------------------------------------------------*/ - class SHVkBuffer; - /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ @@ -44,10 +40,10 @@ namespace SHADE /*-------------------------------------------------------------------------------*/ /* System Routines */ /*-------------------------------------------------------------------------------*/ - class SH_API DrawRoutine final : public SHSystemRoutine - { + class SH_API ProcessPointsRoutine final : public SHSystemRoutine + { public: - DrawRoutine(); + ProcessPointsRoutine(); virtual void Execute(double dt) noexcept override final; }; @@ -73,7 +69,7 @@ namespace SHADE /*-------------------------------------------------------------------------------*/ /* Type Definitions */ /*-------------------------------------------------------------------------------*/ - struct PointVertex + struct SH_API PointVertex { SHVec3 Position; SHVec4 Color; @@ -85,6 +81,7 @@ namespace SHADE std::vector points; // GPU Buffers Handle vertexBuffer; + uint32_t numPoints = 0; // Cached Points for polygon drawing std::vector spherePoints; }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 418e8260..6bf52736 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -204,12 +204,15 @@ namespace SHADE auto pureCopyShader = shaderModuleLibrary.GetShaderModule("PureCopyCs.glsl"); gBufferNode->AddNodeCompute(pureCopyShader, { "Scene Pre-Process", "Scene" }); + // Set up Debug Draw Pass + auto debugDrawNode = worldRenderGraph->AddNode("Debug Draw", { "Scene" }, { "G-Buffer" }); + auto debugDrawSubpass = debugDrawNode->AddSubpass("Debug Draw"); + debugDrawSubpass->AddColorOutput("Scene"); - auto dummyNode = worldRenderGraph->AddNode("Dummy Pass", { "Scene" }, {"G-Buffer"}); // no predecessors + auto dummyNode = worldRenderGraph->AddNode("Dummy Pass", { "Scene" }, {"Debug Draw"}); // no predecessors auto dummySubpass = dummyNode->AddSubpass("Dummy Subpass"); dummySubpass->AddInput("Scene"); - // Generate world render graph worldRenderGraph->Generate(); @@ -224,6 +227,24 @@ namespace SHADE defaultMaterial = AddMaterial(cubeVS, cubeFS, gBufferSubpass); + // Create debug draw pipeline + auto debugDrawVS = shaderModuleLibrary.GetShaderModule("DebugDrawVs.glsl"); + auto debugDrawFS = shaderModuleLibrary.GetShaderModule("DebugDrawFs.glsl"); + + auto debugDrawPipelineLayout = resourceManager.Create + ( + device, SHPipelineLayoutParams + { + .shaderModules = { debugDrawVS, debugDrawFS }, + .globalDescSetLayouts = SHGraphicsGlobalData::GetDescSetLayouts() + } + ); + debugDrawPipeline = resourceManager.Create(device, debugDrawPipelineLayout); + debugDrawPipeline->GetPipelineState().SetRasterizationState(SHRasterizationState + { + .polygonMode = vk::PolygonMode::eLine, + .cull_mode = vk::CullModeFlagBits::eNone + }); } void SHGraphicsSystem::InitMiddleEnd(void) noexcept From 2108d9e1f6a2e1758b6c0b36b73ede9709731d6d Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Thu, 27 Oct 2022 10:12:30 +0800 Subject: [PATCH 004/113] Added triple buffering to SHDebugDrawSystem --- .../src/Application/SBApplication.cpp | 4 ++ SHADE_Application/src/Scenes/SBTestScene.cpp | 1 + .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 58 ++++++++++++++---- .../MiddleEnd/Interface/SHDebugDrawSystem.h | 29 ++++++--- .../MiddleEnd/Interface/SHDebugDrawSystem.hpp | 7 +++ .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 32 +++++++++- .../MiddleEnd/Interface/SHGraphicsSystem.h | 2 +- .../src/Graphics/Pipeline/SHVkPipeline.h | 3 +- TempShaderFolder/DebugDrawVs.glsl | 4 +- TempShaderFolder/DebugDrawVs.spv | Bin 1488 -> 1504 bytes 10 files changed, 114 insertions(+), 26 deletions(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 9b230296..29189d63 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -149,6 +149,10 @@ namespace Sandbox { SHFrameRateController::UpdateFRC(); SHInputManager::UpdateInput(SHFrameRateController::GetRawDeltaTime()); + + auto debugDraw = SHSystemManager::GetSystem(); + //debugDraw->DrawLine(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 0.0f, 0.0f), SHVec3(5.0f, 0.0f, 0.0f)); + debugDraw->DrawCube(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, SHVec3(1.0f, 1.0f, 1.0f)); SHSceneManager::UpdateSceneManager(); #ifdef SHEDITOR if(editor->editorState == SHEditor::State::PLAY) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index a06e68c2..4172dffd 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -17,6 +17,7 @@ #include "Assets/SHAssetManager.h" #include "Camera/SHCameraComponent.h" #include "Resource/SHResourceManager.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" using namespace SHADE; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 7e82fb21..444e9b57 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -11,6 +11,9 @@ of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #include "SHpch.h" #include "SHDebugDrawSystem.h" +// STL Includes +#include +// Project Includes #include "../Meshes/SHMeshData.h" #include "../Meshes/SHPrimitiveGenerator.h" #include "ECS_Base/Managers/SHSystemManager.h" @@ -26,25 +29,30 @@ namespace SHADE /* DrawRoutine */ /*---------------------------------------------------------------------------------*/ SHDebugDrawSystem::ProcessPointsRoutine::ProcessPointsRoutine() - : SHSystemRoutine("Debug Draw") - {} + : SHSystemRoutine("Debug Draw", true) + { + SystemFamily::GetID(); + } void SHDebugDrawSystem::ProcessPointsRoutine::Execute(double dt) noexcept { auto gfxSys = SHSystemManager::GetSystem(); - if (gfxSys) + if (!gfxSys) { SHLOG_WARNING("[DebugDraw] Attempted to do debug draw without a graphics system."); return; } + // Get current frame index + const uint32_t FRAME_IDX = gfxSys->GetCurrentFrameIndex(); + // Create the buffer if it doesn't exist or just update it SHDebugDrawSystem* system = static_cast(GetSystem()); - system->numPoints = system->points.size(); + system->numPoints[FRAME_IDX] = system->points.size(); const uint32_t DATA_SIZE = sizeof(PointVertex) * system->points.size(); if (DATA_SIZE > 0) { - SHVkUtil::EnsureBufferAndCopyHostVisibleData(gfxSys->GetDevice(), system->vertexBuffer, system->points.data(), DATA_SIZE, vk::BufferUsageFlagBits::eVertexBuffer); + system->vertexBuffers[FRAME_IDX]->WriteToMemory(system->points.data(), DATA_SIZE, 0, 0); } // Reset for next frame @@ -61,23 +69,45 @@ namespace SHADE auto const& RENDERERS = GFX_SYSTEM->GetDefaultViewport()->GetRenderers(); auto renderGraph = RENDERERS[SHGraphicsConstants::RenderGraphIndices::WORLD]->GetRenderGraph(); auto subPass = renderGraph->GetNode("Debug Draw")->GetSubpass("Debug Draw"); - subPass->AddExteriorDrawCalls([&](Handle& cmdBuffer) + subPass->AddExteriorDrawCalls([this, GFX_SYSTEM](Handle& cmdBuffer) { + // Get Current frame index + const uint32_t FRAME_IDX = GFX_SYSTEM->GetCurrentFrameIndex(); + // Don't draw if no points - if (numPoints <= 0) + if (numPoints[FRAME_IDX] <= 0) return; cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); - cmdBuffer->BindVertexBuffer(0, vertexBuffer, 0); - cmdBuffer->DrawArrays(static_cast(points.size()), 1, 0, 0); + cmdBuffer->BindVertexBuffer(0, vertexBuffers[FRAME_IDX], 0); + cmdBuffer->DrawArrays(numPoints[FRAME_IDX], 1, 0, 0); }); + + // Reset trackers + std::fill_n(numPoints.begin(), numPoints.size(), 0); + + // Allocate buffers + static constexpr uint32_t BUFFER_SIZE = MAX_POINTS * sizeof(PointVertex); + for (Handle& bufHandle : vertexBuffers) + { + bufHandle = GFX_SYSTEM->GetDevice()->CreateBuffer + ( + BUFFER_SIZE, + nullptr, + 0, + vk::BufferUsageFlagBits::eVertexBuffer, + VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, + VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT + ); + } } void SHDebugDrawSystem::Exit() { - if (vertexBuffer) + for (auto vertexBuffer : vertexBuffers) { - vertexBuffer.Free(); + if (vertexBuffer) + vertexBuffer.Free(); } } @@ -86,6 +116,12 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ void SHDebugDrawSystem::DrawLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) { + if (points.size() > MAX_POINTS) + { + SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); + return; + } + points.emplace_back(PointVertex{ startPt, color }); points.emplace_back(PointVertex{ endPt, color }); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index cc3e728a..2c6a6600 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -22,6 +22,7 @@ of DigiPen Institute of Technology is prohibited. #include "ECS_Base/System/SHSystemRoutine.h" #include "Resource/SHHandle.h" #include "Graphics/Buffers/SHVkBuffer.h" +#include "SHGraphicsConstants.h" namespace SHADE { @@ -34,7 +35,7 @@ namespace SHADE Manages the Debug Draw system. */ /***********************************************************************************/ - class SH_API SHDebugDrawSystem : public SHSystem + class SH_API SHDebugDrawSystem final : public SHSystem { public: /*-------------------------------------------------------------------------------*/ @@ -66,22 +67,30 @@ namespace SHADE void DrawSphere(const SHVec4& color, const SHVec3& pos, double radius); private: - /*-------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ struct SH_API PointVertex { - SHVec3 Position; + SHVec4 Position; SHVec4 Color; }; - /*-------------------------------------------------------------------------------*/ - /* Data Members */ - /*-------------------------------------------------------------------------------*/ + using TripleBuffer = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; + using TripleUInt = std::array; + + /*---------------------------------------------------------------------------------*/ + /* Constants */ + /*---------------------------------------------------------------------------------*/ + static constexpr uint32_t MAX_POINTS = 100'000; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ // CPU Buffers std::vector points; // GPU Buffers - Handle vertexBuffer; - uint32_t numPoints = 0; + TripleBuffer vertexBuffers; + TripleUInt numPoints; // Cached Points for polygon drawing std::vector spherePoints; }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp index 90c645e3..a913ecfd 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp @@ -24,6 +24,13 @@ namespace SHADE // Ensure dereferenced type is SHVec3 static_assert(std::is_same_v>, "Parameters to DrawPoly must be SHVec3."); + // Check if points exceeded max + if (points.size() > MAX_POINTS) + { + SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); + return; + } + const size_t POINTS_COUNT = pointListEnd - pointListBegin; // Invalid polygon if (POINTS_COUNT < 2) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 6bf52736..e6c1cd27 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -37,6 +37,7 @@ of DigiPen Institute of Technology is prohibited. #include "Assets/Asset Types/SHTextureAsset.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" #include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h" +#include "Graphics/SHVkUtil.h" namespace SHADE { @@ -239,12 +240,41 @@ namespace SHADE .globalDescSetLayouts = SHGraphicsGlobalData::GetDescSetLayouts() } ); - debugDrawPipeline = resourceManager.Create(device, debugDrawPipelineLayout); + debugDrawPipeline = resourceManager.Create(device, debugDrawPipelineLayout, nullptr, debugDrawNode->GetRenderpass(), debugDrawSubpass); debugDrawPipeline->GetPipelineState().SetRasterizationState(SHRasterizationState { .polygonMode = vk::PolygonMode::eLine, .cull_mode = vk::CullModeFlagBits::eNone }); + + SHVertexInputState debugDrawVertexInputState; + debugDrawVertexInputState.AddBinding(false, true, { SHVertexAttribute(SHAttribFormat::FLOAT_4D), SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); + debugDrawPipeline->GetPipelineState().SetVertexInputState(debugDrawVertexInputState); + SHColorBlendState colorBlendState{}; + colorBlendState.logic_op_enable = VK_FALSE; + colorBlendState.logic_op = vk::LogicOp::eCopy; + + auto const& subpassColorReferences = debugDrawSubpass->GetColorAttachmentReferences(); + colorBlendState.attachments.reserve(subpassColorReferences.size()); + + for (auto& att : subpassColorReferences) + { + colorBlendState.attachments.push_back(vk::PipelineColorBlendAttachmentState + { + .blendEnable = SHVkUtil::IsBlendCompatible(debugDrawSubpass->GetFormatFromAttachmentReference(att.attachment)), + .srcColorBlendFactor = vk::BlendFactor::eSrcAlpha, + .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, + .colorBlendOp = vk::BlendOp::eAdd, + .srcAlphaBlendFactor = vk::BlendFactor::eOne, + .dstAlphaBlendFactor = vk::BlendFactor::eZero, + .alphaBlendOp = vk::BlendOp::eAdd, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + } + ); + } + + debugDrawPipeline->GetPipelineState().SetColorBlenState(colorBlendState); + debugDrawPipeline->ConstructPipeline(); } void SHGraphicsSystem::InitMiddleEnd(void) noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 0ede74c6..58f7cb6e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -288,7 +288,7 @@ namespace SHADE Handle GetMousePickSystem(void) const noexcept {return mousePickSystem;}; Handle GetPostOffscreenRenderSystem(void) const noexcept {return postOffscreenRender;}; Handle GetDebugDrawPipeline(void) const noexcept { return debugDrawPipeline; } - + uint32_t GetCurrentFrameIndex(void) const noexcept { return renderContext.GetCurrentFrame(); } private: diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h index 7378cc48..7e313ed6 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h +++ b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h @@ -47,12 +47,13 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* CTORS AND DTORS */ /*-----------------------------------------------------------------------*/ + //! For constructing a graphics pipeline SHVkPipeline (Handle const& inLogicalDeviceHdl, Handle const& inPipelineLayout, SHVkPipelineState const* const state, Handle const& renderpassHdl, Handle subpass) noexcept; - + //! For constructing a compute pipeline SHVkPipeline(Handle const& inLogicalDeviceHdl, Handle const& inPipelineLayout) noexcept; diff --git a/TempShaderFolder/DebugDrawVs.glsl b/TempShaderFolder/DebugDrawVs.glsl index ae11658d..cb0886d1 100644 --- a/TempShaderFolder/DebugDrawVs.glsl +++ b/TempShaderFolder/DebugDrawVs.glsl @@ -1,7 +1,7 @@ #version 450 #extension GL_KHR_vulkan_glsl : enable -layout(location = 0) in vec3 aVertexPos; +layout(location = 0) in vec4 aVertexPos; layout(location = 1) in vec4 aVertColor; @@ -19,6 +19,6 @@ layout(set = 2, binding = 0) uniform CameraData void main() { - gl_Position = cameraData.vpMat * vec4 (aVertexPos, 1.0f); + gl_Position = cameraData.vpMat * vec4 (aVertexPos.xyz, 1.0f); Out.vertColor = aVertColor; } \ No newline at end of file diff --git a/TempShaderFolder/DebugDrawVs.spv b/TempShaderFolder/DebugDrawVs.spv index 73e83f692e5dbd2a53de8fcea413846f1461c009..46a663cfdfb38bf67aa8e976cfc3262fe113b3e9 100644 GIT binary patch delta 446 zcmXw!J5Iw;5Jl&iBp^i*LKIH;1oH{zQ_>(IKva|g(a^96RuGl|@=7c=0z06kprS_N z9Ls*z(VMw*$8Y9i_%(ce>`x-1te+lInpAaKAI-=;1HM`HnetRt-|2Tv-?XVj+(s|T z_n+)6EegUha)M>JXmjmqIr#?n0G8mC+;|%zUasxmC$eV$l*kc`tsQb(Q}f^EWvh{A zF|O*~e4%|4e0Cly&68WLrtWc^4DO@fjNmqUJe{^ftfsfqidM7tj=_9u7wp$xL_9b9 syzyg;dys>s8#MPjfDx?16&soByJRPOj^PM8{|Rvko!?x~AETYaKNkcX=Kufz delta 430 zcmX|+O-=$)5QOU?;DQh$E{1J7xkTc|jc36lXyybSfp8JHfm;`@T$}jS z_;4nb?y9ct*Zoo1RbHP;ElKI-`7wl6wh5=zA>3b?f0*q<64LB5e4p6Y%PZ2oib}U% zg{!bmNE^9bIwB6>O=1AnZz6ZQwHdrj>;UOyVgEUqdyhen%quu|?ULx4HP13nqei{1 z5yXStV8bYTFD}=9x()p7N~QeCZqx|>nlG}?6Ar$9)C$B0=KifBx!-=*z5W|>T{{B_ p7=Sx8&ROXKQUx_|%LV2(&RNk-6EFs`K~r+yb?}?p^TTS_;1^fg9BTjo From 94f579e8e3345622c0936997b6eb0c356408373c Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Sun, 30 Oct 2022 13:25:15 +0800 Subject: [PATCH 005/113] CompileAsset function from Asset manager is working --- .../src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 4983bd48..bc485b40 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -37,6 +37,7 @@ of DigiPen Institute of Technology is prohibited. #include "Assets/Asset Types/SHTextureAsset.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" #include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h" +#include "Assets/SHAssetManager.h" namespace SHADE { @@ -109,6 +110,7 @@ namespace SHADE transferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); graphicsTexCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl"); shaderModuleLibrary.ImportAllShaderSource(device); shaderModuleLibrary.ReflectAllShaderModules(); From fad9d37cd4ca683d27be892b26334046c62f64b5 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Sun, 30 Oct 2022 14:17:36 +0800 Subject: [PATCH 006/113] Lighting data is now copied to CPU buffer and GPU buffer every frame Since lighting will be done in view space, the camera's constant movement will make it so that the light data is often changing. Keeping track of these changes for optimization might prove to be counter productive. Copying data every frame might just be more ideal. --- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 2 + .../MiddleEnd/Interface/SHGraphicsSystem.h | 6 +- .../MiddleEnd/Lights/SHLightComponent.cpp | 82 +++++++++---------- .../MiddleEnd/Lights/SHLightComponent.h | 22 ++--- .../MiddleEnd/Lights/SHLightingSubSystem.cpp | 46 ++++++----- .../MiddleEnd/Lights/SHLightingSubSystem.h | 8 +- 6 files changed, 86 insertions(+), 80 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index bc485b40..5deb83a9 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -110,6 +110,8 @@ namespace SHADE transferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); graphicsTexCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_VS.glsl"); + SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_FS.glsl"); SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl"); shaderModuleLibrary.ImportAllShaderSource(device); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 0f9d602a..072a1d99 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -358,9 +358,9 @@ namespace SHADE Handle postOffscreenRender; Handle lightingSubSystem; - uint32_t resizeWidth; - uint32_t resizeHeight; - bool restoredFromMinimize = false; + uint32_t resizeWidth = 1; + uint32_t resizeHeight = 1; + bool restoredFromMinimize = false; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.cpp index fb8795fa..2ea6bc8b 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.cpp @@ -9,9 +9,9 @@ namespace SHADE { lightData.Reset(); SetType(SH_LIGHT_TYPE::DIRECTIONAL); - indexInBuffer = std::numeric_limits::max(); + //indexInBuffer = std::numeric_limits::max(); isActive = true; - Unbind(); + //Unbind(); } @@ -23,28 +23,28 @@ namespace SHADE void SHLightComponent::SetPosition(SHVec3 const& position) noexcept { lightData.position = position; - MakeDirty(); + //MakeDirty(); } void SHLightComponent::SetType(SH_LIGHT_TYPE type) noexcept { lightData.type = type; - MakeDirty(); + //MakeDirty(); } void SHLightComponent::SetDirection(SHVec3 const& direction) noexcept { lightData.direction = direction; - MakeDirty(); + //MakeDirty(); } void SHLightComponent::SetColor(SHVec4 const& color) noexcept { lightData.color = color; - MakeDirty(); + //MakeDirty(); } void SHLightComponent::ModifyCullingMask(uint8_t layerIndex, bool value) noexcept @@ -54,7 +54,7 @@ namespace SHADE else lightData.cullingMask &= ~(1u << layerIndex); - MakeDirty(); + //MakeDirty(); } void SHLightComponent::SetCullingMask(uint32_t const& value) noexcept @@ -65,43 +65,43 @@ namespace SHADE void SHLightComponent::SetAllLayers(void) noexcept { lightData.cullingMask = std::numeric_limits::max(); - MakeDirty(); + //MakeDirty(); } void SHLightComponent::ClearAllLayers(void) noexcept { lightData.cullingMask = 0; - MakeDirty(); + //MakeDirty(); } - void SHLightComponent::MakeDirty(void) noexcept - { - dirty = true; - } + //void SHLightComponent::MakeDirty(void) noexcept + //{ + // dirty = true; + //} - void SHLightComponent::ClearDirtyFlag(void) noexcept - { - dirty = false; - } + //void SHLightComponent::ClearDirtyFlag(void) noexcept + //{ + // dirty = false; + //} - void SHLightComponent::Unbind(void) noexcept - { - bound = false; - MakeDirty(); - } - - void SHLightComponent::SetBound(uint32_t inIndexInBuffer) noexcept -{ - bound = true; - indexInBuffer = inIndexInBuffer; - } +// void SHLightComponent::Unbind(void) noexcept +// { +// bound = false; +// MakeDirty(); +// } +// +// void SHLightComponent::SetBound(uint32_t inIndexInBuffer) noexcept +//{ +// bound = true; +// indexInBuffer = inIndexInBuffer; +// } void SHLightComponent::SetStrength(float value) noexcept { lightData.strength = value; - MakeDirty(); + //MakeDirty(); } SHLightData const& SHLightComponent::GetLightData(void) const noexcept @@ -135,20 +135,20 @@ namespace SHADE } - bool SHLightComponent::IsDirty(void) const noexcept - { - return dirty; - } + //bool SHLightComponent::IsDirty(void) const noexcept + //{ + // return dirty; + //} - bool SHLightComponent::GetBound(void) const noexcept - { - return bound; - } + //bool SHLightComponent::GetBound(void) const noexcept + //{ + // return bound; + //} - uint32_t SHLightComponent::GetIndexInBuffer(void) const noexcept - { - return indexInBuffer; - } + //uint32_t SHLightComponent::GetIndexInBuffer(void) const noexcept + //{ + // return indexInBuffer; + //} float SHLightComponent::GetStrength(void) const noexcept { diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.h b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.h index 81eb80f5..6b35559c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.h @@ -17,13 +17,13 @@ namespace SHADE //! Since the lighting system is gonna be self contained and light weight, we store this //! so that we only write this to the CPU buffer when this light component change, we don't //! rewrite everything. However we still write to the GPU buffer when everything changes. - uint32_t indexInBuffer; + //uint32_t indexInBuffer; - //! If the light component changed some value we mark this true. - bool dirty; + ////! If the light component changed some value we mark this true. + //bool dirty; - //! If the light's data is already in the buffers, this will be set to true. - bool bound; + ////! If the light's data is already in the buffers, this will be set to true. + //bool bound; public: @@ -44,10 +44,10 @@ namespace SHADE void SetCullingMask (uint32_t const& value) noexcept; void SetAllLayers (void) noexcept; // serialized void ClearAllLayers (void) noexcept; // serialized - void MakeDirty (void) noexcept; - void ClearDirtyFlag (void) noexcept; - void Unbind (void) noexcept; - void SetBound (uint32_t inIndexInBuffer) noexcept; + //void MakeDirty (void) noexcept; + //void ClearDirtyFlag (void) noexcept; + //void Unbind (void) noexcept; + //void SetBound (uint32_t inIndexInBuffer) noexcept; void SetStrength (float value) noexcept; // serialized @@ -57,8 +57,8 @@ namespace SHADE SHVec3 const& GetDirection (void) const noexcept; // serialized SHVec4 const& GetColor (void) const noexcept; // serialized uint32_t const& GetCullingMask (void) const noexcept; // serialized - bool IsDirty (void) const noexcept; - bool GetBound (void) const noexcept; + //bool IsDirty (void) const noexcept; + //bool GetBound (void) const noexcept; uint32_t GetIndexInBuffer (void) const noexcept; float GetStrength (void) const noexcept; RTTR_ENABLE() diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp index 5ca879c4..c4ba9fa1 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp @@ -240,7 +240,7 @@ namespace SHADE */ /***************************************************************************/ - void SHLightingSubSystem::PerTypeData::AddLight(Handle logicalDevice, SHLightComponent* unboundLight, bool expanded) noexcept + void SHLightingSubSystem::PerTypeData::AddLight(Handle logicalDevice, SHLightComponent* unboundLight, bool& expanded) noexcept { if (unboundLight) { @@ -262,7 +262,7 @@ namespace SHADE WriteLightToAddress(writeLocation, unboundLight); // Set the light component to be bound to that location - unboundLight->SetBound(numLights); + //unboundLight->SetBound(numLights); // Increase light count ++numLights; @@ -280,11 +280,11 @@ namespace SHADE */ /***************************************************************************/ - void SHLightingSubSystem::PerTypeData::ModifyLight(SHLightComponent* lightComp) noexcept - { - void* writeLocation = reinterpret_cast(intermediateData.get()) + (lightDataAlignedSize * lightComp->GetIndexInBuffer()); - WriteLightToAddress(writeLocation, lightComp); - } + //void SHLightingSubSystem::PerTypeData::ModifyLight(SHLightComponent* lightComp) noexcept + //{ + // void* writeLocation = reinterpret_cast(intermediateData.get()) + (lightDataAlignedSize * lightComp->GetIndexInBuffer()); + // WriteLightToAddress(writeLocation, lightComp); + //} void SHLightingSubSystem::PerTypeData::WriteToGPU(uint32_t frameIndex) noexcept { @@ -406,7 +406,7 @@ namespace SHADE dynamicOffsets[i].resize(NUM_LIGHT_TYPES + 1); // +1 for the count } - numLightComponents = 0; + //numLightComponents = 0; } /***************************************************************************/ @@ -419,21 +419,25 @@ namespace SHADE */ /***************************************************************************/ - void SHLightingSubSystem::Run(Handle cmdBuffer, uint32_t frameIndex) noexcept + void SHLightingSubSystem::Run(Handle cmdBuffer, /*SHMatrix const& viewMat,*/ uint32_t frameIndex) noexcept { static uint32_t constexpr NUM_LIGHT_TYPES = SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES); auto& lightComps = SHComponentManager::GetDense(); bool expanded = false; - bool rewrite = false; - if (numLightComponents > lightComps.size()) - { - rewrite = true; - ResetNumLights(); - } + // First we reset the number of lights. We do this every frame so that we can count how many lights we have + ResetNumLights(); - numLightComponents = lightComps.size(); + //bool rewrite = false; + + //if (numLightComponents > lightComps.size()) + //{ + // rewrite = true; + // ResetNumLights(); + //} + + //numLightComponents = lightComps.size(); for (auto& light : lightComps) { @@ -441,22 +445,22 @@ namespace SHADE // First we want to make sure the light is already bound to the system. if it // isn't, we write it to the correct buffer. - if (!light.GetBound() || rewrite) + //if (!light.GetBound() || rewrite) { perTypeData[enumValue].AddLight(logicalDevice, &light, expanded); - //// add to light count + // add to light count //++lightCountsData[enumValue]; } // if there was modification to the light data - if (light.IsDirty()) + //if (light.IsDirty()) { // Write the data to the CPU - perTypeData[enumValue].ModifyLight(&light); + //perTypeData[enumValue].ModifyLight(&light); // Light is now updated in the container - light.ClearDirtyFlag(); + //light.ClearDirtyFlag(); } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h index fb7aa2de..4a667ec5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h @@ -102,8 +102,8 @@ namespace SHADE /*-----------------------------------------------------------------------*/ void InitializeData (Handle logicalDevice, SH_LIGHT_TYPE type) noexcept; void Expand (Handle logicalDevice) noexcept; - void AddLight (Handle logicalDevice, SHLightComponent* unboundLight, bool expanded) noexcept; - void ModifyLight (SHLightComponent* lightComp) noexcept; + void AddLight (Handle logicalDevice, SHLightComponent* unboundLight, bool& expanded) noexcept; + //void ModifyLight (SHLightComponent* lightComp) noexcept; void WriteToGPU (uint32_t frameIndex) noexcept; void ResetNumLights (void) noexcept; @@ -144,7 +144,7 @@ namespace SHADE //! Number of SHLightComponents recorded. If at the beginning of the run function the size returned by the dense //! set is less than the size recorded, rewrite all light components into the its respective buffers. If its more, //! don't do anything. - uint32_t numLightComponents; + //uint32_t numLightComponents; /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER FUNCTIONS */ @@ -159,7 +159,7 @@ namespace SHADE /* PUBLIC MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ void Init (Handle device, Handle descPool) noexcept; - void Run (Handle cmdBuffer, uint32_t frameIndex) noexcept; + void Run (Handle cmdBuffer, /*SHMatrix const& viewMat,*/ uint32_t frameIndex) noexcept; void Exit (void) noexcept; }; From 4711a131eb875fcb43706834c42960189e6e4fcf Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Sun, 30 Oct 2022 16:35:55 +0800 Subject: [PATCH 007/113] Shifted the lighting system run outside the viewport loop. - Since lighting is only calculated in the world render graph for now, this will do just fine - Renderer takes in a view and projection matrix and does the transpose of the multiplication in the renderer --- Assets/Shaders/TestCube_VS.glsl | 1 + Assets/Shaders/TestCube_VS.shshaderb | Bin 3305 -> 3385 bytes .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 16 ++++++++++--- .../MiddleEnd/Interface/SHRenderer.cpp | 17 ++++++++++---- .../Graphics/MiddleEnd/Interface/SHRenderer.h | 8 ++++--- .../MiddleEnd/Lights/SHLightingSubSystem.cpp | 22 ++++++++++++------ .../MiddleEnd/Lights/SHLightingSubSystem.h | 8 ++++--- 7 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Assets/Shaders/TestCube_VS.glsl b/Assets/Shaders/TestCube_VS.glsl index 49f107dd..44c6e956 100644 --- a/Assets/Shaders/TestCube_VS.glsl +++ b/Assets/Shaders/TestCube_VS.glsl @@ -33,6 +33,7 @@ layout(set = 2, binding = 0) uniform CameraData { vec4 position; mat4 vpMat; + mat4 viewMat; } cameraData; void main() diff --git a/Assets/Shaders/TestCube_VS.shshaderb b/Assets/Shaders/TestCube_VS.shshaderb index 03e23af37451b6c4c0791110541c7b6e31d07bbe..2544abd58b576513f2e8185a0b84ef070e6a42fe 100644 GIT binary patch delta 105 zcmaDUxl@Xn!GL!o^CKo(); + { +#ifdef SHEDITOR + auto editorSystem = SHSystemManager::GetSystem(); + if (editorSystem->editorState != SHEditor::State::PLAY) + lightingSubSystem->Run(cameraSystem->GetEditorCamera()->GetViewMatrix(), frameIndex); + else + lightingSubSystem->Run(worldRenderer->GetCameraDirector()->GetViewMatrix(), frameIndex); +#else + lightingSubSystem->Run(worldRenderer->GetCameraDirector()->GetViewMatrix(), frameIndex); +#endif + } // For every viewport for (int vpIndex = 0; vpIndex < static_cast(viewports.size()); ++vpIndex) @@ -394,8 +405,7 @@ namespace SHADE currentCmdBuffer->BindIndexBuffer(buffer, 0); } - // Bind the descriptor set for lights - lightingSubSystem->Run(currentCmdBuffer, frameIndex); + lightingSubSystem->BindDescSet(currentCmdBuffer, frameIndex); // Bind textures auto textureDescSet = texLibrary.GetTextureDescriptorSetGroup(); @@ -419,7 +429,7 @@ namespace SHADE { auto editorSystem = SHSystemManager::GetSystem(); if (editorSystem->editorState != SHEditor::State::PLAY) - worldRenderer->UpdateDataAndBind(currentCmdBuffer, frameIndex, SHMatrix::Transpose(cameraSystem->GetEditorCamera()->GetProjMatrix() * cameraSystem->GetEditorCamera()->GetViewMatrix())); + worldRenderer->UpdateDataAndBind(currentCmdBuffer, frameIndex, cameraSystem->GetEditorCamera()->GetViewMatrix(), cameraSystem->GetEditorCamera()->GetProjMatrix()); else renderers[renIndex]->UpdateDataAndBind(currentCmdBuffer, frameIndex); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp index 962130be..a44e0661 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp @@ -83,13 +83,14 @@ namespace SHADE { if (camera && cameraDirector) { - UpdateDataAndBind(cmdBuffer, frameIndex, SHMatrix::Transpose(cameraDirector->GetVPMatrix())); + //UpdateDataAndBind(cmdBuffer, frameIndex, SHMatrix::Transpose(cameraDirector->GetVPMatrix())); + UpdateDataAndBind(cmdBuffer, frameIndex, cameraDirector->GetViewMatrix(), cameraDirector->GetProjMatrix()); } } - void SHRenderer::UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix exteriorMatrix) noexcept + void SHRenderer::UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix viewMatrix, SHMatrix projMatrix) noexcept { - SetViewProjectionMatrix(exteriorMatrix); + SetViewProjectionMatrix(viewMatrix, projMatrix); //cpuCameraData.viewProjectionMatrix = camera->GetViewProjectionMatrix(); cameraBuffer->WriteToMemory(&cpuCameraData, sizeof(SHShaderCameraData), 0, cameraDataAlignedSize * frameIndex); @@ -103,10 +104,11 @@ namespace SHADE { } - void SHRenderer::SetViewProjectionMatrix(SHMatrix const& vpMatrix) noexcept + void SHRenderer::SetViewProjectionMatrix(SHMatrix viewMatrix, SHMatrix projMatrix) noexcept { //cpuCameraData.viewProjectionMatrix = camera->GetViewMatrix() * camera->GetProjectionMatrix(); - cpuCameraData.viewProjectionMatrix = vpMatrix; + cpuCameraData.viewProjectionMatrix = SHMatrix::Transpose(projMatrix * viewMatrix); + cpuCameraData.viewMatrix = viewMatrix; } Handle SHRenderer::GetRenderGraph(void) const noexcept @@ -119,4 +121,9 @@ namespace SHADE return commandBuffers[frameIndex]; } + Handle SHRenderer::GetCameraDirector(void) const noexcept + { + return cameraDirector; + } + } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h index 87cf8ee9..b7c6718c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h @@ -46,6 +46,7 @@ namespace SHADE { SHVec4 cameraPosition; SHMatrix viewProjectionMatrix; + SHMatrix viewMatrix; }; /*---------------------------------------------------------------------------------*/ @@ -79,15 +80,16 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ void Draw(uint32_t frameIndex, Handle descPool) noexcept; void UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex) noexcept; - void UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix exteriorMatrix) noexcept; + void UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix viewMatrix, SHMatrix projMatrix) noexcept; void UpdateCameraDataToBuffer (void) noexcept; - void SetViewProjectionMatrix (SHMatrix const& vpMatrix) noexcept; + void SetViewProjectionMatrix (SHMatrix viewMatrix, SHMatrix projMatrix) noexcept; /*-----------------------------------------------------------------------------*/ /* Setters and Getters */ /*-----------------------------------------------------------------------------*/ - Handle GetRenderGraph (void) const noexcept; + Handle GetRenderGraph (void) const noexcept; Handle GetCommandBuffer(uint32_t frameIndex) const noexcept; + Handle GetCameraDirector (void) const noexcept; private: /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp index c4ba9fa1..ac7212bf 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp @@ -8,6 +8,7 @@ #include "SHLightComponent.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "SHLightComponent.h" +#include "Math/Vector/SHVec4.h" namespace SHADE { @@ -31,7 +32,7 @@ namespace SHADE */ /***************************************************************************/ - void SHLightingSubSystem::PerTypeData::WriteLightToAddress(void* address, SHLightComponent* lightComp) noexcept + void SHLightingSubSystem::PerTypeData::WriteLightToAddress(void* address, SHMatrix const& viewMat, SHLightComponent* lightComp) noexcept { auto const& lightData = lightComp->GetLightData(); switch (lightData.type) @@ -41,6 +42,7 @@ namespace SHADE SHDirectionalLightData* lightPtr = reinterpret_cast(address); lightPtr->cullingMask = lightData.cullingMask; + //lightPtr->direction = viewMat * SHVec4 (lightData.direction[0], lightData.direction[1], lightData.direction[2], 0.0f); lightPtr->direction = lightData.direction; lightPtr->diffuseColor = lightData.color; lightPtr->active = lightComp->isActive; @@ -240,7 +242,7 @@ namespace SHADE */ /***************************************************************************/ - void SHLightingSubSystem::PerTypeData::AddLight(Handle logicalDevice, SHLightComponent* unboundLight, bool& expanded) noexcept + void SHLightingSubSystem::PerTypeData::AddLight(Handle logicalDevice, SHLightComponent* unboundLight, SHMatrix const& viewMat, bool& expanded) noexcept { if (unboundLight) { @@ -259,7 +261,7 @@ namespace SHADE void* writeLocation = reinterpret_cast(intermediateData.get()) + (lightDataAlignedSize * numLights); // Write the light data to address - WriteLightToAddress(writeLocation, unboundLight); + WriteLightToAddress(writeLocation, viewMat, unboundLight); // Set the light component to be bound to that location //unboundLight->SetBound(numLights); @@ -419,7 +421,7 @@ namespace SHADE */ /***************************************************************************/ - void SHLightingSubSystem::Run(Handle cmdBuffer, /*SHMatrix const& viewMat,*/ uint32_t frameIndex) noexcept + void SHLightingSubSystem::Run(SHMatrix const& viewMat, uint32_t frameIndex) noexcept { static uint32_t constexpr NUM_LIGHT_TYPES = SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES); @@ -447,7 +449,7 @@ namespace SHADE // isn't, we write it to the correct buffer. //if (!light.GetBound() || rewrite) { - perTypeData[enumValue].AddLight(logicalDevice, &light, expanded); + perTypeData[enumValue].AddLight(logicalDevice, &light, viewMat, expanded); // add to light count //++lightCountsData[enumValue]; @@ -492,8 +494,6 @@ namespace SHADE // so we do it anyway. #NoteToSelf: if at any point it affects performance, do a check before computing. ComputeDynamicOffsets(); - // Bind descriptor set (We bind at an offset because the buffer holds NUM_FRAME_BUFFERS sets of data). - cmdBuffer->BindDescriptorSet(lightingDataDescSet, SH_PIPELINE_TYPE::COMPUTE, SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, {dynamicOffsets[frameIndex]}); } @@ -509,4 +509,12 @@ namespace SHADE { } + + void SHLightingSubSystem::BindDescSet(Handle cmdBuffer, uint32_t frameIndex) noexcept + { + //Bind descriptor set(We bind at an offset because the buffer holds NUM_FRAME_BUFFERS sets of data). + cmdBuffer->BindDescriptorSet(lightingDataDescSet, SH_PIPELINE_TYPE::COMPUTE, SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, { dynamicOffsets[frameIndex] }); + + } + } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h index 4a667ec5..ae6caead 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h @@ -95,14 +95,14 @@ namespace SHADE //! to the GPU that stores NUM_FRAME_BUFFERS copies. std::unique_ptr intermediateData; - void WriteLightToAddress (void* address, SHLightComponent* lightComp) noexcept; + void WriteLightToAddress (void* address, SHMatrix const& viewMat, SHLightComponent* lightComp) noexcept; public: /*-----------------------------------------------------------------------*/ /* PUBLIC MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ void InitializeData (Handle logicalDevice, SH_LIGHT_TYPE type) noexcept; void Expand (Handle logicalDevice) noexcept; - void AddLight (Handle logicalDevice, SHLightComponent* unboundLight, bool& expanded) noexcept; + void AddLight (Handle logicalDevice, SHLightComponent* unboundLight, SHMatrix const& viewMat, bool& expanded) noexcept; //void ModifyLight (SHLightComponent* lightComp) noexcept; void WriteToGPU (uint32_t frameIndex) noexcept; void ResetNumLights (void) noexcept; @@ -159,8 +159,10 @@ namespace SHADE /* PUBLIC MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ void Init (Handle device, Handle descPool) noexcept; - void Run (Handle cmdBuffer, /*SHMatrix const& viewMat,*/ uint32_t frameIndex) noexcept; + void Run (SHMatrix const& viewMat, uint32_t frameIndex) noexcept; void Exit (void) noexcept; + void BindDescSet (Handle cmdBuffer, uint32_t frameIndex) noexcept; + }; } From 39b5ac0774081d9fb9336d6512a1a90b02eea1e7 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Sun, 30 Oct 2022 16:52:40 +0800 Subject: [PATCH 008/113] LIGHTING NOW DONE IN VIEW SPACE --- Assets/Shaders/TestCube_VS.glsl | 15 ++++++++++++--- Assets/Shaders/TestCube_VS.shshaderb | Bin 3385 -> 3525 bytes .../MiddleEnd/Interface/SHRenderer.cpp | 2 +- .../MiddleEnd/Lights/SHLightingSubSystem.cpp | 7 +++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Assets/Shaders/TestCube_VS.glsl b/Assets/Shaders/TestCube_VS.glsl index 44c6e956..867b945f 100644 --- a/Assets/Shaders/TestCube_VS.glsl +++ b/Assets/Shaders/TestCube_VS.glsl @@ -38,14 +38,23 @@ layout(set = 2, binding = 0) uniform CameraData void main() { - Out2.materialIndex = gl_InstanceIndex; Out2.eid = integerData[0]; Out2.lightLayerIndex = integerData[1]; - Out.vertPos = worldTransform * vec4(aVertexPos, 1.0f); + // 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; - Out.normal.rgb = mat3(transpose(inverse(worldTransform))) * aNormal.rgb; + + // normals are also in view space + Out.normal.rgb = mat3(transpose(inverse(modelViewMat))) * 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/Assets/Shaders/TestCube_VS.shshaderb b/Assets/Shaders/TestCube_VS.shshaderb index 2544abd58b576513f2e8185a0b84ef070e6a42fe..b7e1294bb1b2fcbe7f31212c0662330feff59f48 100644 GIT binary patch literal 3525 zcmZ9MYj+c65XYb922!q4E-GTuqE)N)28beHY%#3{5^Sk=!lQZX;`OkG{=860JgiBAkv~1uZ zkpq6@C4L-u;bt`OpQJh>(+TJNU~phdePJ-%OWNL3KhCVl!8dkh37_Vwip%#7!`@~z zO}wb@TUhC;X&-_p@rQoA?j@d(n!6(V(Rkp8JAwcBo<}=U+gEk3c_TmeQd3#h92jRZ zo(4%UPJ3LFH8}O|PqF}pQS*Ktmi)_-`-Pa~XD*M&aX8qHy=b~Sjz{D#yL79LWlWr{xChI{q8%4|f*kW|EFg(!F z1NM_c{S3z2_M)L5r88>fv%f4He`IDqFIvgv1>NY`rwzrh`7KH2Y{@spyA{A8*U@ftg=Rm68vPdfA7?Lvlkh2R*w%{k$V&=84Qf<}DXx=84Q4 z&{GF8`KX6EaOe@oFzms&4Z{bF-WmoEmeo^EiK7d+L&p3nxBtut#Puz|iwAi7$HdKO-Lf z1?ivHO>Sz&{;Y0v;KV~_ufXuf_MC`={dwK!to|3olMmVQy(FIa=6^{%KIdgw*9}oi zBj@S&Mahf0!H~(dq?@?l^pD*_j|^e+n(mSa9rYtG=NS6ebKdM%B+rSEu}5YP!KmpS z-E|Q$$;p3HLj=xE%=dKj&FF}M%-w-+X#~ti% ziz8M;g#4*)Y^ec%{oG#w@z(5Oe)Rgg^G^*~LE<7u_upxv}B<*v&g4_TA=y zj2*SHgP)0-DOJOSyuXf!T8NFU&Fz7B z-Xi^_-mgU1f;Z*M+uRn>FZPFBruMq-ha&7b>o>{McXf`&vAH)QBH;VCMI& zc=O|b!2G@wk00NSA9i522j7b~KfbRa`3DiW_36hP1Ls(um^I)3Q;sobY^|oAc#B=^ z!6V`LaHt8pMV_Z-L`%HMLf94 zzltxu^-wZ4;I4xZk$nOR# literal 3385 zcmZ9NdwUaA5XMiNOOXPC$VEj=S}$m|Rs}?nOYJ2sh9=lj@5Dp0gw@R^PqGc*Uw#Gs zNPa5+@p*WEyE|ncPoH_t%scPQnKL^lbt}hRx=OQ)bI#4UqwcsHW{^AOW`#I6@2ctE zXzn(bCrNMl@|6`RCtN9I!g*PLtg>X_!x;Ujg!HdU-jQ6Ad?@)`vMG5WfmqVttoDDD zsH~_)v(sp`8qJMPcQooul4nuSiNZMO^ulq_O~Ppa??{0c#@*Kgesz9^kKvH8aHlBL{czVS0R_?dNV7B+K_%{*fB28w>Z12#3|=WSq9@0Mz!e=N>q z83rGW-7sRnsMRoZu_Tw6jt zgDvGbt^YOI$ApugJn*rZ3o!ir8WM}&;!jD(e@^~av~xB!!#}MZ zA2|82nJX~j;hvGO;Gflw&+30oI_F{Ad2dK3zr|mWPRv;`YuXWNspmTLfxVF1#L}-t z?Qp;|ZrQypo%8TfFE(=zc3$?o+ToIudf(GNBLRnx&AR~O%n!8JB*epC(vA-tJ~rde(JrcowLB~yiMuG-_CtC?bgp7 z>GbG=;(yX!OLcmd{Z7_%3}5CiyI-Vp25*{kupi_Yz2+?tN1qUC{Z%{X;3EdxOTh3y z%ysK6z2cqBNUka;JiY^qv;P&s8@(a4s{Mq7*EvLJ$$a?LLw) zBllE8P5Z|ZFqUB}(!uDBVV~p}eQN7Ab?y8^CmufjWyxR4i|;cDXTzaZX77rG`L~{7 z!=ttZ?bjsCAADx-O9_1I4>laT+jZ&O?U=q0cl}C2&E&$jm-pa?bbQ=}+HOg}SS|gU z(U;p2aD+u3?rJfs(uo1Ln6IUSBP@Kym^InNfLYACbZ~@aF>T#}_q8FpEg?5t`)&>C zn-cmUtRXM5^(F&ZjOPotWSJv?!T8~8`|MoP4}f+?qU!2 zg%iV~CU{^L_nmZ$EB4@f;dT~r@W3qYheBMj2S4U<)&qE8^x&cPuEa~2ZJXy_j+-6G zz9jiuG0gWP2^?^ni@x;Ydwwh%jAh>=lnzD@4f`#}sL$T>@6w5fkH7eyBiWn}$G+o0 SI={>F5_*FT??0V%P4XNphWKOv diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp index a44e0661..655451ec 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp @@ -108,7 +108,7 @@ namespace SHADE { //cpuCameraData.viewProjectionMatrix = camera->GetViewMatrix() * camera->GetProjectionMatrix(); cpuCameraData.viewProjectionMatrix = SHMatrix::Transpose(projMatrix * viewMatrix); - cpuCameraData.viewMatrix = viewMatrix; + cpuCameraData.viewMatrix = SHMatrix::Transpose(viewMatrix); } Handle SHRenderer::GetRenderGraph(void) const noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp index ac7212bf..35bcc72f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp @@ -9,6 +9,7 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "SHLightComponent.h" #include "Math/Vector/SHVec4.h" +#include "Math/SHMatrix.h" namespace SHADE { @@ -41,9 +42,11 @@ namespace SHADE { SHDirectionalLightData* lightPtr = reinterpret_cast(address); + SHVec4 transformedDir = viewMat * SHVec4(lightData.direction[0], lightData.direction[1], lightData.direction[2], 0.0f); + lightPtr->cullingMask = lightData.cullingMask; - //lightPtr->direction = viewMat * SHVec4 (lightData.direction[0], lightData.direction[1], lightData.direction[2], 0.0f); - lightPtr->direction = lightData.direction; + lightPtr->direction = SHVec3 (transformedDir.x, transformedDir.y, transformedDir.z); + //lightPtr->direction = lightData.direction; lightPtr->diffuseColor = lightData.color; lightPtr->active = lightComp->isActive; break; From 647182241efad86d80d5ec63df6a83ea4e0036f3 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Sun, 30 Oct 2022 19:21:02 +0800 Subject: [PATCH 009/113] Fixed a buy with view space lighting calculations --- Assets/Shaders/DeferredComposite_CS.glsl | 6 +++--- Assets/Shaders/DeferredComposite_CS.shshaderb | Bin 4665 -> 4665 bytes .../MiddleEnd/Interface/SHGraphicsConstants.h | 12 +++++++++++- .../MiddleEnd/Lights/SHLightingSubSystem.cpp | 3 ++- .../RenderGraph/SHRenderGraphNodeCompute.cpp | 14 +++++--------- .../RenderGraph/SHRenderGraphNodeCompute.h | 10 +++++++++- 6 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Assets/Shaders/DeferredComposite_CS.glsl b/Assets/Shaders/DeferredComposite_CS.glsl index c1caf0aa..5f586b9b 100644 --- a/Assets/Shaders/DeferredComposite_CS.glsl +++ b/Assets/Shaders/DeferredComposite_CS.glsl @@ -51,10 +51,10 @@ void main() vec3 pixelDiffuse = imageLoad (albedo, globalThread).rgb; // Get position of fragment in world space - vec3 positionWorld = imageLoad (positions, globalThread).rgb; + vec3 positionView = imageLoad (positions, globalThread).rgb; // normal of fragment - vec3 normalWorld = imageLoad(normals, globalThread).rgb; + vec3 normalView = imageLoad(normals, globalThread).rgb; vec3 fragColor = vec3 (0.0f); @@ -64,7 +64,7 @@ void main() vec3 dLightNormalized = normalize (DirLightData.dLightData[i].direction); // Get diffuse strength - float diffuseStrength = max (0, dot (dLightNormalized, normalWorld)); + float diffuseStrength = max (0, dot (dLightNormalized, normalView)); // Calculate the fragment color fragColor += DirLightData.dLightData[i].diffuseColor.rgb * diffuseStrength.rrr * pixelDiffuse; diff --git a/Assets/Shaders/DeferredComposite_CS.shshaderb b/Assets/Shaders/DeferredComposite_CS.shshaderb index 889de5fb344289c281264b054407d97c317a7fff..aaaa5207fc09134d01b8cdfec1bc483a63024116 100644 GIT binary patch delta 28 fcmdm~vQuS)FC%MMW@2y1f<<6Z#(hieG| delta 28 fcmdm~vQuS)FC%Mseo;=!2y1f<<6Z#(krN5q diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index a39ec10e..0257bf3b 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -70,7 +70,17 @@ namespace SHADE */ /***************************************************************************/ static constexpr uint32_t RENDERGRAPH_RESOURCE = 4; - + /***************************************************************************/ + /*! + \brief + DescriptorSet Index for render graph node compute resources. For data + that we wish to pass to compute shaders in the render graph, this is + the set to use. Unlike the sets from 1 to 3, this set index does not have + hard coded bindings and is NOT part of the layouts included in the global + data. + */ + /***************************************************************************/ + static constexpr uint32_t RENDERGRAPH_NODE_COMPUTE_RESOURCE = 5; }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp index 35bcc72f..485f859f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp @@ -42,7 +42,8 @@ namespace SHADE { SHDirectionalLightData* lightPtr = reinterpret_cast(address); - SHVec4 transformedDir = viewMat * SHVec4(lightData.direction[0], lightData.direction[1], lightData.direction[2], 0.0f); + // #NoteToSelf: NEED TO TRANSPOSE HERE BECAUSE MULTIPLICATION IS ROW MAJOR. + SHVec4 transformedDir = SHMatrix::Transpose(viewMat) * SHVec4(lightData.direction[0], lightData.direction[1], lightData.direction[2], 0.0f); lightPtr->cullingMask = lightData.cullingMask; lightPtr->direction = SHVec3 (transformedDir.x, transformedDir.y, transformedDir.z); diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp index 5323d706..6a9b1c31 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp @@ -42,16 +42,12 @@ namespace SHADE //Get the descriptor set layouts required to allocate. We only want the ones for allocate because //global descriptors are already bound in the main system. - auto const& layouts = computePipeline->GetPipelineLayout()->GetDescriptorSetLayoutsAllocate(); - - //Variable counts for the descriptor sets (all should be 1). - std::vector variableCounts{ static_cast(layouts.size()) }; - std::fill(variableCounts.begin(), variableCounts.end(), 0); + auto const& layouts = computePipeline->GetPipelineLayout()->GetDescriptorSetLayoutsPipeline()[SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE]; // Allocate descriptor sets to hold the images for reading (STORAGE_IMAGE) for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) { - descSetGroups[i] = graphStorage->descriptorPool->Allocate(layouts, variableCounts); + graphResourceDescSets[i] = graphStorage->descriptorPool->Allocate({layouts}, { 1 }); } HandleResize(); @@ -63,7 +59,7 @@ namespace SHADE cmdBuffer->BindPipeline(computePipeline); // bind descriptor sets - cmdBuffer->BindDescriptorSet(descSetGroups[frameIndex], SH_PIPELINE_TYPE::COMPUTE, SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE, {}); + cmdBuffer->BindDescriptorSet(graphResourceDescSets[frameIndex], SH_PIPELINE_TYPE::COMPUTE, SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE, {}); // dispatch compute cmdBuffer->ComputeDispatch(groupSizeX, groupSizeY, 1); @@ -89,8 +85,8 @@ namespace SHADE uint32_t imageIndex = (resources[i]->resourceTypeFlags & static_cast(SH_ATT_DESC_TYPE_FLAGS::COLOR_PRESENT)) ? frameIndex : 0; SHVkDescriptorSetGroup::viewSamplerLayout vsl = std::make_tuple(resources[i]->GetImageView(imageIndex), Handle{}, vk::ImageLayout::eGeneral); - descSetGroups[frameIndex]->ModifyWriteDescImage(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE, binding.BindPoint, { &vsl, 1 }); - descSetGroups[frameIndex]->UpdateDescriptorSetImages(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE, binding.BindPoint); + graphResourceDescSets[frameIndex]->ModifyWriteDescImage(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE, binding.BindPoint, { &vsl, 1 }); + graphResourceDescSets[frameIndex]->UpdateDescriptorSetImages(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE, binding.BindPoint); ++i; } } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h index 2cd3c948..d7c29f49 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h @@ -19,12 +19,15 @@ namespace SHADE class SHVkShaderModule; class SHVkCommandBuffer; + class SHRenderGraphNodeCompute { private: static constexpr uint32_t workGroupSizeX = 16; static constexpr uint32_t workGroupSizeY = 16; + using ExternalDescSetBindCommands = std::function)>; + //! To run the dispatch command Handle computePipeline; @@ -32,7 +35,12 @@ namespace SHADE Handle pipelineLayout; //! Descriptor set group to hold the images for reading (STORAGE_IMAGE) - std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS> descSetGroups; + std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS> graphResourceDescSets; + + //! Sometimes we want to bind descriptor sets for a compute resource (buffers, images, etc). This container + //! helps store functions that call those bind functions. The reason why we dont just have a handle to descriptor + //! sets here is because we dont know how we want to bind the sets (different set each frame, dynamic offsets, etc). + std::vector externalDescriptorSetBindCommands; //! vector of resources needed by the subpass compute std::vector> resources; From 997ce3011a836053440156696817f67c6c95c402 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Sun, 30 Oct 2022 23:15:52 +0800 Subject: [PATCH 010/113] Screw you SPIRV-Reflect and screw you GLSL - Pipeline layouts now have a parameter to decide if a binding with a buffer descriptor stores a dynamic type instead. - SHRenderGraphNodeCompute has catered for this as well through its ctor params --- .../Descriptors/SHVkDescriptorSetLayout.cpp | 12 ++++++ .../Descriptors/SHVkDescriptorSetLayout.h | 1 + .../MiddleEnd/PostProcessing/SHSSAO.cpp | 7 ++++ .../MiddleEnd/PostProcessing/SHSSAO.h | 15 +++++++ .../Pipeline/SHPipelineLayoutParams.h | 8 ++++ .../Graphics/Pipeline/SHVkPipelineLayout.cpp | 23 +++++++++++ .../Graphics/Pipeline/SHVkPipelineLayout.h | 3 ++ .../RenderGraph/SHRenderGraphNode.cpp | 4 +- .../Graphics/RenderGraph/SHRenderGraphNode.h | 2 +- .../RenderGraph/SHRenderGraphNodeCompute.cpp | 39 +++++++++++++++++-- .../RenderGraph/SHRenderGraphNodeCompute.h | 22 ++++++++--- 11 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.cpp b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.cpp index 0943c489..4be8cc9e 100644 --- a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.cpp +++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.cpp @@ -119,6 +119,18 @@ namespace SHADE return setIndex; } + uint32_t SHVkDescriptorSetLayout::GetNumDynamicOffsetsRequired(void) const noexcept + { + uint32_t numDynamicBindings = 0; + for (auto& binding : layoutDesc) + { + if (binding.Type == vk::DescriptorType::eUniformBufferDynamic || binding.Type == vk::DescriptorType::eStorageBufferDynamic) + ++numDynamicBindings; + } + + return numDynamicBindings; + } + SHVkDescriptorSetLayout& SHVkDescriptorSetLayout::operator=(SHVkDescriptorSetLayout&& rhs) noexcept { if (&rhs == this) diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h index 1639901d..caa3c057 100644 --- a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h +++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h @@ -99,6 +99,7 @@ namespace SHADE inline const vk::DescriptorSetLayout& GetVkHandle() const { return setLayout; } std::vector const& GetBindings (void) const noexcept; SetIndex GetSetIndex (void) const noexcept; + uint32_t GetNumDynamicOffsetsRequired (void) const noexcept; private: /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp new file mode 100644 index 00000000..fb5ae824 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp @@ -0,0 +1,7 @@ +#include "SHpch.h" +#include "SHSSAO.h" + +namespace SHADE +{ + +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h new file mode 100644 index 00000000..17df7200 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Resource/SHHandle.h" +#include "Graphics/SHVulkanIncludes.h" + +namespace SHADE +{ + + class SHSSAO + { + private: + + + }; +} diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHPipelineLayoutParams.h b/SHADE_Engine/src/Graphics/Pipeline/SHPipelineLayoutParams.h index d696e4a3..010bed0e 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHPipelineLayoutParams.h +++ b/SHADE_Engine/src/Graphics/Pipeline/SHPipelineLayoutParams.h @@ -2,8 +2,10 @@ #define SH_PIPELINE_LAYOUT_PARAMS_H #include "Graphics/SHVulkanIncludes.h" +#include "Graphics/SHVulkanDefines.h" #include "Resource/SHHandle.h" #include "Graphics/Descriptors/SHVkDescriptorSetLayout.h" +#include namespace SHADE { @@ -24,6 +26,12 @@ namespace SHADE //! want to use the layout to initialize the pipeline layout but we do not //! want to use it for allocating descriptor sets. std::vector> const& globalDescSetLayouts = {}; + + //! Since both SPIRV-Reflect and GLSL don't provide ways to describe UBOs or + //! SSBOs as dynamic, we need to do it ourselves. This will store bindings + //! which we will use when parsing for descriptor set layouts. If a parsed + //! binding is in this container, we make that binding's descriptor dynamic. + std::unordered_set dynamicBufferBindings; }; struct SHPipelineLayoutParamsDummy diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipelineLayout.cpp b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipelineLayout.cpp index 37d00795..47b2e010 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipelineLayout.cpp +++ b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipelineLayout.cpp @@ -165,6 +165,28 @@ namespace SHADE newBinding.Stage = shaderModule->GetShaderStageFlagBits(); newBinding.Type = descBindingInfo.ConvertFromReflectDescType(reflectedBinding->descriptor_type); + // Here we want to check if a binding is supposed to be dynamic. If it is, make it dynamic. + if (newBinding.Type == vk::DescriptorType::eUniformBuffer || newBinding.Type == vk::DescriptorType::eStorageBuffer) + { + for (auto& bsHash : dynamicBufferBindings) + { + uint32_t set = static_cast(bsHash >> 32); + uint32_t binding = static_cast(bsHash & 0xFFFFFFFF); + if (set == CURRENT_SET && binding == newBinding.BindPoint) + { + switch (newBinding.Type) + { + case vk::DescriptorType::eUniformBuffer: + newBinding.Type = vk::DescriptorType::eUniformBufferDynamic; + break; + case vk::DescriptorType::eStorageBuffer: + newBinding.Type = vk::DescriptorType::eStorageBufferDynamic; + break; + } + } + } + } + // In reality, the check for variable descriptor sets do not exists in spirv-reflect. Fortunately, when a shader // defines a boundless descriptor binding in the shader, the information reflected makes the array dimensions // contain a 1 element of value 1. Knowing that having an array [1] doesn't make sense, we can use this to @@ -300,6 +322,7 @@ namespace SHADE , vkDescriptorSetLayoutsAllocate{} , descriptorSetLayoutsPipeline{} , vkDescriptorSetLayoutsPipeline{} + , dynamicBufferBindings{std::move (pipelineLayoutParams.dynamicBufferBindings)} { for (auto& mod : shaderModules) { diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipelineLayout.h b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipelineLayout.h index b4298e00..e2af02a9 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipelineLayout.h +++ b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipelineLayout.h @@ -29,6 +29,9 @@ namespace SHADE //! Push constant interface SHPushConstantInterface pushConstantInterface; + //! See SHPipelineLayoutParams for details + std::unordered_set dynamicBufferBindings; + //! Push constant ranges std::vector vkPcRanges; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp index aa9c7944..12f0e246 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp @@ -263,7 +263,7 @@ namespace SHADE return subpass; } - Handle SHRenderGraphNode::AddNodeCompute(Handle computeShaderModule, std::initializer_list resources, float numWorkGroupScale/* = 1.0f*/) noexcept + Handle SHRenderGraphNode::AddNodeCompute(Handle computeShaderModule, std::initializer_list resources, std::unordered_set&& dynamicBufferBindings, float numWorkGroupScale/* = 1.0f*/) noexcept { // Look for the required resources in the graph std::vector> nodeComputeResources{}; @@ -276,7 +276,7 @@ namespace SHADE } // Create the subpass compute with the resources - auto nodeCompute = graphStorage->resourceManager->Create(graphStorage, computeShaderModule, std::move(nodeComputeResources), nodeComputes.empty()); + auto nodeCompute = graphStorage->resourceManager->Create(graphStorage, computeShaderModule, std::move(nodeComputeResources), std::move (dynamicBufferBindings), nodeComputes.empty()); nodeComputes.push_back(nodeCompute); return nodeCompute; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h index 16f3f914..695d1c31 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h @@ -100,7 +100,7 @@ namespace SHADE /* PUBLIC MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ Handle AddSubpass(std::string subpassName) noexcept; - Handle AddNodeCompute(Handle computeShaderModule, std::initializer_list resources, float numWorkGroupScale = 1.0f) noexcept; + Handle AddNodeCompute(Handle computeShaderModule, std::initializer_list resources, std::unordered_set&& dynamicBufferBindings = {}, float numWorkGroupScale = 1.0f) noexcept; void AddDummySubpassIfNeeded (void) noexcept; // TODO: RemoveSubpass() diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp index 6a9b1c31..b5f6f7ab 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp @@ -13,7 +13,7 @@ namespace SHADE { - SHRenderGraphNodeCompute::SHRenderGraphNodeCompute(Handle graphStorage, Handle computeShaderModule, std::vector>&& subpassComputeResources, bool followingEndRP, float inNumWorkGroupScale/* = 1.0f*/) noexcept + SHRenderGraphNodeCompute::SHRenderGraphNodeCompute(Handle graphStorage, Handle computeShaderModule, std::vector>&& subpassComputeResources, std::unordered_set&& dynamicBufferBindings, bool followingEndRP, float inNumWorkGroupScale/* = 1.0f*/) noexcept : computePipeline{} , pipelineLayout{} , resources{} @@ -21,11 +21,13 @@ namespace SHADE , groupSizeY{0} , followingEndRenderpass {followingEndRP} , numWorkGroupScale {std::clamp(inNumWorkGroupScale, 0.0f, 1.0f)} + , computeResource{} { SHPipelineLayoutParams pipelineLayoutParams { .shaderModules = {computeShaderModule}, - .globalDescSetLayouts = SHGraphicsGlobalData::GetDescSetLayouts() + .globalDescSetLayouts = SHGraphicsGlobalData::GetDescSetLayouts(), + .dynamicBufferBindings = std::move(dynamicBufferBindings), }; // Create pipeline layout from parameters @@ -42,12 +44,31 @@ namespace SHADE //Get the descriptor set layouts required to allocate. We only want the ones for allocate because //global descriptors are already bound in the main system. - auto const& layouts = computePipeline->GetPipelineLayout()->GetDescriptorSetLayoutsPipeline()[SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE]; + auto const& graphResourceLayout = computePipeline->GetPipelineLayout()->GetDescriptorSetLayoutsPipeline()[SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE]; // Allocate descriptor sets to hold the images for reading (STORAGE_IMAGE) for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) { - graphResourceDescSets[i] = graphStorage->descriptorPool->Allocate({layouts}, { 1 }); + graphResourceDescSets[i] = graphStorage->descriptorPool->Allocate({graphResourceLayout}, { 1 }); + } + + + auto const& layouts = computePipeline->GetPipelineLayout()->GetDescriptorSetLayoutsPipeline(); + + if (layouts.size() == SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE + 1) + { + // create compute resources + computeResource = graphStorage->resourceManager->Create(); + auto computeResourceLayout = layouts[SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE]; + computeResource->descSet = graphStorage->descriptorPool->Allocate({ computeResourceLayout }, { 1 }); + + // Allocate for descriptor offsets + for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) + computeResource->dynamicOffsets[i].resize(computeResourceLayout->GetNumDynamicOffsetsRequired()); + + // 1st set all start at 0 + for (auto& index : computeResource->dynamicOffsets[0]) + index = 0; } HandleResize(); @@ -133,4 +154,14 @@ namespace SHADE } } + void SHRenderGraphNodeCompute::SetDynamicOffsets(std::span perFrameSizes) noexcept + { + for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) + { + auto dynamicOffsets = computeResource->dynamicOffsets[i]; + for (uint32_t j = 0; j < perFrameSizes.size(); ++j) + dynamicOffsets[j] = perFrameSizes[j] * i; + } + } + } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h index d7c29f49..1101e276 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h @@ -22,11 +22,22 @@ namespace SHADE class SHRenderGraphNodeCompute { + private: + // Binding of set SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE + struct ComputeResource + { + //! Descriptor set (initialized by parent class) + Handle descSet {}; + + //! Dynamic offsets into these resources (initialized from the outside). + std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS> dynamicOffsets {}; + }; + private: static constexpr uint32_t workGroupSizeX = 16; static constexpr uint32_t workGroupSizeY = 16; - using ExternalDescSetBindCommands = std::function)>; + using ExternalDescSetBindCommands = std::function, Handle)>; //! To run the dispatch command Handle computePipeline; @@ -37,10 +48,7 @@ namespace SHADE //! Descriptor set group to hold the images for reading (STORAGE_IMAGE) std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS> graphResourceDescSets; - //! Sometimes we want to bind descriptor sets for a compute resource (buffers, images, etc). This container - //! helps store functions that call those bind functions. The reason why we dont just have a handle to descriptor - //! sets here is because we dont know how we want to bind the sets (different set each frame, dynamic offsets, etc). - std::vector externalDescriptorSetBindCommands; + Handle computeResource; //! vector of resources needed by the subpass compute std::vector> resources; @@ -58,11 +66,13 @@ namespace SHADE std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS> memoryBarriers; public: - SHRenderGraphNodeCompute(Handle graphStorage, Handle computeShaderModule, std::vector>&& subpassComputeResources, bool followingEndRP, float inNumWorkGroupScale = 1.0f) noexcept; + SHRenderGraphNodeCompute(Handle graphStorage, Handle computeShaderModule, std::vector>&& subpassComputeResources, std::unordered_set&& dynamicBufferBindings, bool followingEndRP, float inNumWorkGroupScale = 1.0f) noexcept; void Execute (Handle cmdBuffer, uint32_t frameIndex) noexcept; void HandleResize (void) noexcept; + void SetDynamicOffsets (std::span perFrameSizes) noexcept; + friend class SHRenderGraph; friend class SHRenderGraphNode; }; From 7a6474eaf80e749caea7a892b2e572a1e97178f2 Mon Sep 17 00:00:00 2001 From: Glence Date: Sun, 30 Oct 2022 23:59:35 +0800 Subject: [PATCH 011/113] base class for playercontroller --- TempScriptsFolder/PlayerController.cs | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 TempScriptsFolder/PlayerController.cs diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs new file mode 100644 index 00000000..2e0123eb --- /dev/null +++ b/TempScriptsFolder/PlayerController.cs @@ -0,0 +1,52 @@ +using SHADE; +using System; + + +public class PlayerController : Script +{ + private RigidBody rb; + public float xAxisMove; + public float zAxisMove; + public float force = 800.0f; + + public PlayerController(GameObject gameObj) : base(gameObj) { } + + protected override void awake() + { + rb = GetComponent(); + if (rb == null) + { + Debug.LogError("RigidBody is NULL!"); + } + } + + protected override void update() + { + Move(); + + //float x = xAxisMove * force * (float)Time.DeltaTime; + //Debug.Log(x.ToString()); + } + + private void Move() + { + + if (Input.GetKey(Input.KeyCode.A)) + xAxisMove = -1; + else if (Input.GetKey(Input.KeyCode.D)) + xAxisMove = 1; + else + xAxisMove = 0; + + if (Input.GetKey(Input.KeyCode.W)) + zAxisMove = -1; + else if (Input.GetKey(Input.KeyCode.S)) + zAxisMove = 1; + else + zAxisMove = 0; + + if (rb != null) + rb.AddForce(new Vector3(xAxisMove * force * (float)Time.DeltaTime, 0.0f, zAxisMove * force * (float)Time.DeltaTime)); + } +} + From 94a57219da3f1d8ccc53b9dd463758cb029aaa49 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Mon, 31 Oct 2022 10:32:32 +0800 Subject: [PATCH 012/113] Created CPU and GPU data for SSAO Renderpass for SSAO not yet done (that's next) --- Assets/Shaders/SSAO_CS.glsl | 24 +++++++++ .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 12 +++++ .../MiddleEnd/Interface/SHGraphicsSystem.h | 3 ++ .../MiddleEnd/PostProcessing/SHSSAO.cpp | 53 +++++++++++++++++++ .../MiddleEnd/PostProcessing/SHSSAO.h | 22 ++++++++ .../src/Graphics/Queues/SHVkQueue.cpp | 5 ++ SHADE_Engine/src/Graphics/Queues/SHVkQueue.h | 2 + .../RenderGraph/SHRenderGraphNodeCompute.cpp | 5 ++ .../RenderGraph/SHRenderGraphNodeCompute.h | 6 ++- 9 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 Assets/Shaders/SSAO_CS.glsl diff --git a/Assets/Shaders/SSAO_CS.glsl b/Assets/Shaders/SSAO_CS.glsl new file mode 100644 index 00000000..3759c4b4 --- /dev/null +++ b/Assets/Shaders/SSAO_CS.glsl @@ -0,0 +1,24 @@ +#version 450 + +uint const NUM_SAMPLES = 64; +uint const NUM_ROTATIONS = 16; + +layout(local_size_x = 16, local_size_y = 16) in; +layout(set = 4, binding = 0, rgba32f) uniform image2D positions; +layout(set = 4, binding = 1, rgba32f) uniform image2D normals; +layout(set = 4, binding = 2, rgba32f) uniform image2D outputImage; + + +// SSAO data +layout(std430, set = 5, binding = 0) buffer SSAOData +{ + vec3 samples[NUM_SAMPLES]; + vec3 rotations[NUM_ROTATIONS]; + +} ssaoData; + +void main() +{ + // store result into result image + imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor, 1.0f)); +} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index edf0be23..d062b4d6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -253,6 +253,18 @@ namespace SHADE lightingSubSystem = resourceManager.Create(); lightingSubSystem->Init(device, descPool); + + ssaoStorage = resourceManager.Create(); + + ssaoTransferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + ssaoTransferCmdBuffer->BeginRecording(); + + ssaoStorage->Init(device, ssaoTransferCmdBuffer); + + ssaoTransferCmdBuffer->EndRecording(); + graphicsQueue->SubmitCommandBuffer({ssaoTransferCmdBuffer}); + + graphicsQueue->WaitIdle(); } #ifdef SHEDITOR diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 072a1d99..9b95ebe4 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -32,6 +32,7 @@ of DigiPen Institute of Technology is prohibited. #include "../Textures/SHVkSamplerCache.h" #include "Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.h" #include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h" +#include "Graphics/MiddleEnd/PostProcessing/SHSSAO.h" namespace SHADE { @@ -313,6 +314,7 @@ namespace SHADE Handle descPool; Handle graphicsCmdPool; Handle transferCmdBuffer; + Handle ssaoTransferCmdBuffer; Handle graphicsTexCmdBuffer; SHRenderContext renderContext; std::array, 2> graphSemaphores; @@ -357,6 +359,7 @@ namespace SHADE Handle mousePickSystem; Handle postOffscreenRender; Handle lightingSubSystem; + Handle ssaoStorage; uint32_t resizeWidth = 1; uint32_t resizeHeight = 1; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp index fb5ae824..f2894786 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp @@ -1,7 +1,60 @@ #include "SHpch.h" #include "SHSSAO.h" +#include "Graphics/Devices/SHVkLogicalDevice.h" +#include "Graphics/Buffers/SHVkBuffer.h" +#include namespace SHADE { + void SHSSAO::Init(Handle logicalDevice, Handle cmdBuffer) noexcept + { + // create memory on CPU side + ssaoData = std::make_unique(); + + // Initialize a distribution to get values from 0 to 1 + std::uniform_real_distribution distrib{0.0f, 1.0f}; + + // generator for random number + std::default_random_engine generator; + + // generate samples + auto& samples = ssaoData->samples; + for (uint32_t i = 0; i < NUM_SAMPLES; ++i) + { + samples[i] = SHVec4 + { + distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + distrib(generator), // 0.0f - 1.0f so that sample space is a hemisphere + 0.0f + }; + + // This makes sure that most points are closer to fragment's position + float scale = 1.0f / static_cast(NUM_SAMPLES); + scale = std::lerp(0.1f, 1.0f, scale * scale); + samples[i] *= scale; + } + + // generate rotation vector + auto& rotationVectors = ssaoData->rotatationVectors; + for (uint32_t i = 0; i < NUM_ROTATION_VECTORS; ++i) + { + samples[i] = SHVec4 + { + distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + 0.0f, // we want to rotate about the z axis in tangent space + 0.0f + }; + } + + // Get aligned size for buffer + uint32_t alignedSize = logicalDevice->PadSSBOSize(sizeof (SamplesAndKernel)); + + // Create buffer + ssaoDataBuffer = logicalDevice->CreateBuffer(alignedSize, ssaoData.get(), alignedSize, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst, VMA_MEMORY_USAGE_AUTO, {}); + + ssaoDataBuffer->TransferToDeviceResource(cmdBuffer); + } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h index 17df7200..32e8eb1a 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h @@ -2,14 +2,36 @@ #include "Resource/SHHandle.h" #include "Graphics/SHVulkanIncludes.h" +#include "Math/Vector/SHVec4.h" namespace SHADE { + class SHVkBuffer; + class SHVkLogicalDevice; + class SHVkCommandBuffer; class SHSSAO { private: + static constexpr uint32_t NUM_SAMPLES = 64; + static constexpr uint32_t NUM_ROTATION_VECTORS = 16; + struct SamplesAndKernel + { + //! distances from a pixel we want to sample + std::array samples; + std::array rotatationVectors; + }; + + private: + //! Samples and kernel on CPU side + std::unique_ptr ssaoData; + + //! For passing SSAO samples and kernel to GPU + Handle ssaoDataBuffer; + + public: + void Init (Handle logicalDevice, Handle cmdBuffer) noexcept; }; } diff --git a/SHADE_Engine/src/Graphics/Queues/SHVkQueue.cpp b/SHADE_Engine/src/Graphics/Queues/SHVkQueue.cpp index 828f2974..dcb3ff6a 100644 --- a/SHADE_Engine/src/Graphics/Queues/SHVkQueue.cpp +++ b/SHADE_Engine/src/Graphics/Queues/SHVkQueue.cpp @@ -129,4 +129,9 @@ namespace SHADE return vkQueue; } + void SHVkQueue::WaitIdle(void) const noexcept + { + vkQueue.waitIdle(); + } + } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/Queues/SHVkQueue.h b/SHADE_Engine/src/Graphics/Queues/SHVkQueue.h index a647cb72..f27f3b0b 100644 --- a/SHADE_Engine/src/Graphics/Queues/SHVkQueue.h +++ b/SHADE_Engine/src/Graphics/Queues/SHVkQueue.h @@ -50,6 +50,8 @@ namespace SHADE void SubmitCommandBuffer (std::initializer_list> cmdBuffers, std::initializer_list> signalSems = {}, std::initializer_list> waitSems = {}, vk::PipelineStageFlags waitDstStageMask = {}, Handle const& fence = {}) noexcept; vk::Result Present (Handle const& swapchain, std::initializer_list> waitSems, uint32_t frameIndex) noexcept; vk::Queue GetVkQueue() noexcept; + + void WaitIdle (void) const noexcept; }; } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp index b5f6f7ab..60d76c49 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp @@ -164,4 +164,9 @@ namespace SHADE } } + void SHRenderGraphNodeCompute::ModifyWriteDescBufferComputeResource(uint32_t set, uint32_t binding, std::span> const& buffers, uint32_t offset, uint32_t range) noexcept + { + computeResource->descSet->ModifyWriteDescBuffer(set, binding, buffers, offset, range); + } + } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h index 1101e276..d61eda33 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h @@ -18,6 +18,7 @@ namespace SHADE class SHRenderGraphResource; class SHVkShaderModule; class SHVkCommandBuffer; + class SHVkBuffer; class SHRenderGraphNodeCompute @@ -37,8 +38,6 @@ namespace SHADE static constexpr uint32_t workGroupSizeX = 16; static constexpr uint32_t workGroupSizeY = 16; - using ExternalDescSetBindCommands = std::function, Handle)>; - //! To run the dispatch command Handle computePipeline; @@ -48,6 +47,7 @@ namespace SHADE //! Descriptor set group to hold the images for reading (STORAGE_IMAGE) std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS> graphResourceDescSets; + //! Compute resources Handle computeResource; //! vector of resources needed by the subpass compute @@ -73,6 +73,8 @@ namespace SHADE void SetDynamicOffsets (std::span perFrameSizes) noexcept; + void ModifyWriteDescBufferComputeResource (uint32_t set, uint32_t binding, std::span> const& buffers, uint32_t offset, uint32_t range) noexcept; + friend class SHRenderGraph; friend class SHRenderGraphNode; }; From 0ffd596734f4aa1960ee899fb0b21787c39e1d46 Mon Sep 17 00:00:00 2001 From: Glence Date: Mon, 31 Oct 2022 16:45:47 +0800 Subject: [PATCH 013/113] update player controller --- TempScriptsFolder/PlayerController.cs | 109 ++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 2e0123eb..0eb94d13 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -1,13 +1,37 @@ using SHADE; using System; +//in air controls? public class PlayerController : Script { + public enum RaccoonStates + { + IDILE, + WALKING, + RUNNING, + INAIR, + FALLING, + HOLDING, + CAUGHT, + TOTAL + } + private RigidBody rb; - public float xAxisMove; - public float zAxisMove; - public float force = 800.0f; + private float xAxisMove; + private float zAxisMove; + private RaccoonStates currentState = RaccoonStates.IDILE; + + public float maxMoveVel = 2.0f; + public float moveForce = 200.0f; + public float sprintMultiplier = 1.5f; + + public float jumpForce = 500.0f; + public float initialJumpForce = 100.0f; + public float maxJumpForce = 500.0f; + public float maxJumpTime = 500.0f; + private bool isJumping = false; + private bool isGrounded = false; public PlayerController(GameObject gameObj) : base(gameObj) { } @@ -18,14 +42,22 @@ public class PlayerController : Script { Debug.LogError("RigidBody is NULL!"); } + + RaccoonStates currentState = RaccoonStates.IDILE; } protected override void update() { - Move(); + currentState = RaccoonStates.IDILE; - //float x = xAxisMove * force * (float)Time.DeltaTime; - //Debug.Log(x.ToString()); + Move(); + Sprint(); + Jump(); + + if (rb != null && currentState == RaccoonStates.IDILE) + rb.LinearVelocity = new Vector3(0.0f, 0.0f, 0.0f); + + Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); } private void Move() @@ -45,8 +77,69 @@ public class PlayerController : Script else zAxisMove = 0; - if (rb != null) - rb.AddForce(new Vector3(xAxisMove * force * (float)Time.DeltaTime, 0.0f, zAxisMove * force * (float)Time.DeltaTime)); + if((Input.GetKey(Input.KeyCode.A) || Input.GetKey(Input.KeyCode.D) || Input.GetKey(Input.KeyCode.W) || Input.GetKey(Input.KeyCode.S)) && currentState != RaccoonStates.RUNNING) + currentState = RaccoonStates.WALKING; + + if (rb != null && currentState == RaccoonStates.WALKING) + { + if (rb.LinearVelocity.x <= maxMoveVel) + rb.AddForce(new Vector3(moveForce * xAxisMove * (float)Time.DeltaTime, 0.0f, 0.0f)); + else + { + Vector3 v = rb.LinearVelocity; + v.x = maxMoveVel; + rb.LinearVelocity = v; + } + + if (rb.LinearVelocity.z <= maxMoveVel) + rb.AddForce(new Vector3(0.0f, 0.0f, moveForce * zAxisMove * (float)Time.DeltaTime)); + else + { + Vector3 v = rb.LinearVelocity; + v.z = maxMoveVel; + rb.LinearVelocity = v; + } + } } + + private void Sprint() + { + //left shift not working for now and chang eto getkey down + if (Input.GetKey(Input.KeyCode.RightShift)) + { + if (currentState == RaccoonStates.WALKING && rb != null) + { + currentState = RaccoonStates.RUNNING; + rb.LinearVelocity *= sprintMultiplier; + } + } + + if (Input.GetKeyUp(Input.KeyCode.RightShift)) + { + if (currentState == RaccoonStates.RUNNING && rb != null) + { + currentState = RaccoonStates.WALKING; + rb.LinearVelocity /= sprintMultiplier; + } + } + } + + //press and hold jump + private void Jump() + { + if (currentState == RaccoonStates.WALKING || currentState == RaccoonStates.RUNNING || currentState == RaccoonStates.IDILE) + { + if (Input.GetKeyDown(Input.KeyCode.Space) && rb != null) + { + currentState = RaccoonStates.INAIR; + rb.AddForce(new Vector3(0.0f, jumpForce, 0.0f)); + } + } + + //collision check when grounded + + } + + } From 48f01e41642ad8852ff2a6cecfc48aef73212f47 Mon Sep 17 00:00:00 2001 From: Glence Date: Mon, 31 Oct 2022 18:05:36 +0800 Subject: [PATCH 014/113] camp vel --- TempScriptsFolder/PlayerController.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 0eb94d13..29ac9007 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -82,21 +82,28 @@ public class PlayerController : Script if (rb != null && currentState == RaccoonStates.WALKING) { - if (rb.LinearVelocity.x <= maxMoveVel) - rb.AddForce(new Vector3(moveForce * xAxisMove * (float)Time.DeltaTime, 0.0f, 0.0f)); + if (rb.LinearVelocity.x <= maxMoveVel || rb.LinearVelocity.x >= -maxMoveVel) + rb.AddForce(new Vector3(moveForce * xAxisMove, 0.0f, 0.0f)); else { Vector3 v = rb.LinearVelocity; - v.x = maxMoveVel; + if(v.x >= 0) + v.x = maxMoveVel; + else + v.x = -maxMoveVel; + rb.LinearVelocity = v; } - if (rb.LinearVelocity.z <= maxMoveVel) - rb.AddForce(new Vector3(0.0f, 0.0f, moveForce * zAxisMove * (float)Time.DeltaTime)); + if (rb.LinearVelocity.z <= maxMoveVel || rb.LinearVelocity.z >= -maxMoveVel) + rb.AddForce(new Vector3(0.0f, 0.0f, moveForce * zAxisMove )); else { Vector3 v = rb.LinearVelocity; - v.z = maxMoveVel; + if (v.z >= 0) + v.z = maxMoveVel; + else + v.z = -maxMoveVel; rb.LinearVelocity = v; } } From 2bd3b45ba0a105815aba0c589fc556d4ae66e21b Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Mon, 31 Oct 2022 20:49:28 +0800 Subject: [PATCH 015/113] SSAO sort of working --- Assets/Shaders/DeferredComposite_CS.glsl | 6 +- Assets/Shaders/DeferredComposite_CS.shshaderb | Bin 4665 -> 4801 bytes Assets/Shaders/SSAO_CS.glsl | 78 ++++++++++++++++-- Assets/Shaders/SSAO_CS.shshaderb | Bin 0 -> 6425 bytes Assets/Shaders/TestCube_VS.glsl | 5 +- Assets/Shaders/TestCube_VS.shshaderb | Bin 3525 -> 3689 bytes .../src/Graphics/Buffers/SHVkBuffer.cpp | 5 ++ .../src/Graphics/Buffers/SHVkBuffer.h | 3 +- .../GlobalData/SHGraphicsGlobalData.cpp | 2 +- .../MiddleEnd/Interface/SHGraphicsConstants.h | 1 + .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 73 +++++++++++----- .../MiddleEnd/Interface/SHRenderer.cpp | 6 +- .../Graphics/MiddleEnd/Interface/SHRenderer.h | 5 +- .../MiddleEnd/PostProcessing/SHSSAO.cpp | 7 +- .../MiddleEnd/PostProcessing/SHSSAO.h | 4 + .../RenderGraph/SHRenderGraphNodeCompute.cpp | 6 ++ .../Graphics/Shaders/SHShaderReflected.cpp | 10 +++ 17 files changed, 174 insertions(+), 37 deletions(-) create mode 100644 Assets/Shaders/SSAO_CS.shshaderb diff --git a/Assets/Shaders/DeferredComposite_CS.glsl b/Assets/Shaders/DeferredComposite_CS.glsl index 5f586b9b..4607363b 100644 --- a/Assets/Shaders/DeferredComposite_CS.glsl +++ b/Assets/Shaders/DeferredComposite_CS.glsl @@ -21,7 +21,8 @@ layout(set = 4, binding = 0, rgba32f) uniform image2D positions; layout(set = 4, binding = 1, rgba32f) uniform image2D normals; layout(set = 4, binding = 2, rgba8) uniform image2D albedo; layout(set = 4, binding = 3, r32ui) uniform uimage2D lightLayerData; -layout(set = 4, binding = 4, rgba8) uniform image2D targetImage; +layout(set = 4, binding = 4, rgba32f) uniform image2D ssaoImage; +layout(set = 4, binding = 5, rgba8) uniform image2D targetImage; layout(set = 1, binding = 0) uniform LightCounts { @@ -78,6 +79,7 @@ void main() } // store result into result image - imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor, 1.0f)); + //imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor, 1.0f)); + imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(imageLoad(ssaoImage, globalThread).rgb, 1.0f)); } \ No newline at end of file diff --git a/Assets/Shaders/DeferredComposite_CS.shshaderb b/Assets/Shaders/DeferredComposite_CS.shshaderb index aaaa5207fc09134d01b8cdfec1bc483a63024116..d691c168b25040b5ef44acb3432711e65747ca18 100644 GIT binary patch delta 458 zcmYk2y-EX75Jo4P{V^+oh*?Qt5u;U7OHf2og;dd6ETV{&TI2z=u~4H?qbBQH$TqPN zd@#ZDExWJ-hna6??wz^!d-HcIa^@?RTG?p?W+e-4$v(fLy1VAtJ(b~baMimQoSzYU z=3AWXxJORULw3zgTQ-Q@g02_z2k|1*vU-d!_Q%PO-z*>Ra)HvCC~Klu@#9q8>`RBl zZ?FuxekU$Li_J7ff^Rs0huFJxN8fkVz{`Daj%EYH0VE2rKp*Rj_uslx}1*T4zr0v%9YKXJ-( NxA;3y{f}$f@DCT_Ft7js delta 321 zcmX|+y9&ZU5Jhh`F~Nu+qJb0^8Z9IumV$+)m9N^;zYq%>3*V0z|3gz*`b+kLcS7RA z;m+Kdo!NaiUUf%xiinS{JPXiZBai#TsT#V8Fd7*%)il^JfPpJDN8BV2sWp<;!{}rB z`p~CQqP-))sxP_C=u8SE%`c2K(t;U}mGROeNXGNHs8CNt?n}SaR&#)OMayWBWVBt diff --git a/Assets/Shaders/SSAO_CS.glsl b/Assets/Shaders/SSAO_CS.glsl index 3759c4b4..ac84cb81 100644 --- a/Assets/Shaders/SSAO_CS.glsl +++ b/Assets/Shaders/SSAO_CS.glsl @@ -1,7 +1,13 @@ #version 450 -uint const NUM_SAMPLES = 64; -uint const NUM_ROTATIONS = 16; +const uint NUM_SAMPLES = 64; +const uint NUM_ROTATIONS = 16; +const int ROTATION_KERNEL_W = 4; +const int ROTATION_KERNEL_H = 4; + +// can perhaps pass in as push constant. +const float RADIUS = 0.5f; +const float BIAS = 0.025f; layout(local_size_x = 16, local_size_y = 16) in; layout(set = 4, binding = 0, rgba32f) uniform image2D positions; @@ -12,13 +18,75 @@ layout(set = 4, binding = 2, rgba32f) uniform image2D outputImage; // SSAO data layout(std430, set = 5, binding = 0) buffer SSAOData { - vec3 samples[NUM_SAMPLES]; - vec3 rotations[NUM_ROTATIONS]; + vec4 samples[NUM_SAMPLES]; + vec4 rotations[NUM_ROTATIONS]; } ssaoData; +layout(set = 2, binding = 0) uniform CameraData +{ + vec4 position; + mat4 vpMat; + mat4 viewMat; + mat4 projMat; + +} cameraData; + +shared vec4 sharedRotations[NUM_ROTATIONS]; + void main() { + ivec2 size = imageSize (outputImage); + + // load rotations into shared memory + uint localThreadIndex = gl_LocalInvocationIndex; + if (localThreadIndex < NUM_ROTATIONS) + sharedRotations[localThreadIndex] = ssaoData.rotations[localThreadIndex]; + + barrier(); + + ivec2 globalThread = ivec2 (gl_GlobalInvocationID.xy); + + // load all the necessary variables + vec3 viewSpacePos = imageLoad (positions, globalThread).rgb; + vec3 viewSpaceNormal = normalize (imageLoad (normals, globalThread).rgb); + + // Get random vector + uint randomVecXIndex = globalThread.x % ROTATION_KERNEL_W; + uint randomVecYIndex = globalThread.y % ROTATION_KERNEL_H; + vec3 randomVec = (sharedRotations[randomVecYIndex * ROTATION_KERNEL_W + randomVecXIndex].rgb) * 2.0f - 1.0f; + + // Gram schmidt + vec3 tangent = normalize (randomVec - (viewSpaceNormal * dot(viewSpaceNormal, randomVec))); + vec3 bitangent = cross (tangent, viewSpaceNormal); + mat3 TBN = mat3 (tangent, bitangent, viewSpaceNormal); + + float occlusion = 0.0f; + for (int i = 0; i < NUM_SAMPLES; ++i) + { + // We want to get a position at an offset from the view space position. Offset scaled by radius. + vec3 samplePos = TBN * ssaoData.samples[i].rgb; + samplePos = viewSpacePos + samplePos * RADIUS; + + // Now we take that offset position and bring it to clip space + vec4 offsetPos = vec4 (samplePos, 1.0f); + offsetPos = cameraData.projMat * offsetPos; + + // then we do perspective division + offsetPos.xyz /= offsetPos.w; + + // and bring it from [-1, 1] to screen coordinates + offsetPos.xyz = ((offsetPos.xyz * 0.5f) + 0.5f); + offsetPos.xy *= vec2(size.xy); + + float sampleDepth = imageLoad (positions, ivec2 (offsetPos.xy)).z; + + float rangeCheck = smoothstep (0.0f, 1.0f, RADIUS / abs (viewSpacePos.z - sampleDepth)); + occlusion += (sampleDepth >= samplePos.z + BIAS ? 1.0f : 0.0f) * rangeCheck; + } + + occlusion = (occlusion / float(NUM_SAMPLES)); + // store result into result image - imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor, 1.0f)); + imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), vec4(occlusion.rrr, 0.0f)); } \ No newline at end of file diff --git a/Assets/Shaders/SSAO_CS.shshaderb b/Assets/Shaders/SSAO_CS.shshaderb new file mode 100644 index 0000000000000000000000000000000000000000..78fd5087a76621f421742ffd5f2b9fa21528e843 GIT binary patch literal 6425 zcmZ9P33Oa#6~`ZKlCD6Jttet!YAaF;$Rf5tleU;vLlbD(Tu7&rX)=(^#LP4-qO{T$ zq>4pxK}At&0Tso4M?}S~h%4?2?hEb;#dAEz-|x+L+jsJv^FR0A|6RWO-S@pW$)s6F zW%-&s>DVb*c0e{gJ1~2)MP-L%lVGx3J{LJ7>&Wfyp0z!T8_j{mr<{7Cp$BD?6}j)= zY)aOS-UdcW)tdZa;AAk1eECCiUAvVtnfyuU_p@0W9d!5fclY*o_nh5d9v$s(G_M@4 z^bc2SmHvV1Sf$*oj#rGET8OLG%EOxmiUI9J6noH~#vYC8Rb*%M*v?6_tiGu^x~aK( zq%>H8nUXyPZn$194X+y-tCR*-*9IzAwBo1c=NKIB@6o-wHjZdk>$O%$J7;?afsLWk zSY_b6dNcPm@_pvRuU*^KyRy_QS*rt9&ebT5j1E^Ca2+}4yvO!sxB6UQT%%NveD`wj zgTwU=(I_TWr{;5}$L`!!XmVxl@5q)Q$E%gg*N&FTmA-nTmDipvMH#I(s-8&FJvx#5 zV%OBiMoPo*ZCMw5EjK-1N7ju!R;mruM=q$8FWNtk-Xec-@a@_8MIPOnWn{Bd8?4lt z*zMUc_J(RO#!Sw};MOf)ll!-!*ULQh1`VU@-;S*2dV97L*_v(Zkk-5vuD)?&qte{Z z?SflT8mWwxTGRD8$hl%3>OM1hpU?Q{Ii+TS^X@t)*@*Lv7#*u$D(Bw2vCD;h-uhoZ4WMO-*0<3I@#W*V$a&e_^t_lZT$xT``Lwb z?eDQ|-~8<%VvIM&vlcl%W{GR%Ijdrxwt0OZ^5iDwdH!Oawt3#K$dija=VtHLaxOmi zc@u2U>C(R!>D>GbA4-;abE#oX^}~trSN`a>u9qBhmFAM%S~Ep2NFh zZ(KL@w*3Ce8Ef5RlFxf>U2VUy#z)<`=$*j)dAV%eBW<5k)H?=yKDIIP)_*3_ImlaI z+j}l&eP_H7cn;%_L+Z1p_9A4f_x=4WPBGEn3D{F9;CW9%cLqLN^ZiZ5mGkbe?Rz9= z{3%G|-BbH?r1OgW=cD@^?!Ow{e9sx@?LjZjw-4KX;yf1? z{PuDQV0G%(UaysHUN#$h6wThE-9D^kC8qMz%~T@imv%UovsJJ5~y zdAQ&0Nw@!FIMuFX%LmMRFSaw)@0_%ECocBA13UV@Gx@Bc-)C}n^7~vKK)2_(H|mdq z2lwnbBl>;}JIgjNUqoNtgT26eE}Zdcd}YkHcb9wbg=t6HqwlzXzj$74-$DOY30wb( z1>3vfZ+-Z+I}5h?-4kqouZ!{iP8aR+1bet(`)vJvj`*$U+X}XKZF|ACpPdEUXX)>A zG2h?EqU~>^_L-dDZ`DD-@6-^s{K1Jki|IKlZDW02hXcP!`kb3~_>MrAyN7AdMji#^ zd^S%I|1_lQztqzAR3PW`QI9%v(5(~pvy$D#Gd~Xf*tg>!$mamR zb8~?GY8&$;`^CP>N67bkBTfL;^KP9A{EqZ86~7^;B~ISu`+POA&g^+Wzx#N9w5{WO z&j3r4ZS0xAm<{x4-)8}1#xK%Mn3AF zjV>=-QGX^gcK;V7&N$(|ok)4->pgrSaGzDcUOJF30`fbE(LN_}eh;11EaaNRTeCaa zQS)4EYxaStc^z9Qcn6KuZympf`XaA^UaZr^HYWU=&^y7o>0Fnio8t=Z3UoR5 z3+~FqO)79#pswEL6nQP!49qjed5$6F)wdzXlfE5!9q@a2J<{*6@^S9h4agn9rT?`^ z{YvNVyZt&~jd-tbOk8~bUym&}7x>NBehsj;wbZR;-!}pKjM{HNw|0E<--x~d*snh0 zjz-=L^u_!4ro_p+oV#@Y0R7g8p1z4b@$7rxjCFZ-W8@;| zLG+1d{}!A%adu~jY4+hA$3eLubfihX}Kaq=$f)Y1&Thps>V#``|H_2lDD{QzA) z=J*i0T=e`y^d-RA+mr9xkAQsGKSnpt-)j9o0mjJtTP^ofU~g?ezwem*&w%$#|4jbw zl>0d__9QS4)&t|5>BC6pAqv`Mb6Q>2HzJ{0`(F!FJ%^ zg4%yd+(M-J`s9kgn>S#`{(nYykC^jc(B&QjvA>-0Gmw7;2PWIO{|%fB;{T4nCr;kw z^NIgE{(-JP_Wmcjd&f8BU+8jyxO@MG)8`8AKj?DS5AN~Ac}M$rUwsdr0OlL7-{({2 PES|%D^#6}4r-T0kYfO5x literal 0 HcmV?d00001 diff --git a/Assets/Shaders/TestCube_VS.glsl b/Assets/Shaders/TestCube_VS.glsl index 867b945f..0e055395 100644 --- a/Assets/Shaders/TestCube_VS.glsl +++ b/Assets/Shaders/TestCube_VS.glsl @@ -34,6 +34,7 @@ layout(set = 2, binding = 0) uniform CameraData vec4 position; mat4 vpMat; mat4 viewMat; + mat4 projMat; } cameraData; void main() @@ -51,8 +52,10 @@ void main() // uvs for texturing in fragment shader Out.uv = aUV; + mat3 transposeInv = mat3 (transpose(inverse(modelViewMat))); + // normals are also in view space - Out.normal.rgb = mat3(transpose(inverse(modelViewMat))) * aNormal.rgb; + Out.normal.rgb = transposeInv * aNormal.rgb; Out.normal.rgb = normalize (Out.normal.rgb); // clip space for rendering diff --git a/Assets/Shaders/TestCube_VS.shshaderb b/Assets/Shaders/TestCube_VS.shshaderb index b7e1294bb1b2fcbe7f31212c0662330feff59f48..fb2965a83900e01b2591230c1de83828ad72c658 100644 GIT binary patch delta 1479 zcmYk6%T7~K6o&Wd$j3fl1pa#lC6j5odwH8ZHZHpywa1x(D zJTr&Bfnn^(IC0<;I5KhI6F4&Q|4z>)?B>r}>tFxcd+oK`e#`zD9x|mPCS37Ye!v*V zq|ITovpZB7To(OG=zV@ybB?+uX-+GO3$3=d8T@uWt-Ge6u-h&-J1wtMool{PC}F}- zT%PyZTjhpg*VGl)8XLuGd$ro`R(H_35sy#K>(sk-uPJuG)PwJ_>@-!ees-!+Z{4nU zy5(l2+E<9D8C6bgHyZo7q*&T!CH3hA=i*;4&Z;Ndeo~oPWo(>R{Qe*p{EENzxxt9Q z`KalImFb&(&WSjz)lq|v{w#+Nmk@Bd)H>f@ z_*?1$&5{5|%#>FIrn#%0i0=#1%!G+aZOnKd2v>xxa6;e$IoXE-1uPCj2WRWnf@bEV z|40lf+C{Y{3ujf2<=fqPIJ^53`P6Ym>d?VW>huZOr-H5HI*fE9;_b*v#`qygf@NpM zTXElV#4($aV4Hd--)?^J$`$d{uxJV$oUMB<-_{MwUo?TAas8@XdlY5J;B46o`L^uf z3~EuGodG&HW^h)vA)FD83Cv(jc1wV_ag!Nb5b;4p2s{Ci68qnSmi!xn9sXd})Q=ec%uILU2+1`yeo delta 1294 zcmYk5OKVe66ot>tqls-Ih|nMjl7~g1;=mUbF<7*wK4=r$##c3sF~(?`#PllCNx**) z@7$jtNM@oV@o#kQ%%PKl-@3U+a>8Qoz4l&b@3SxYo%xlXH{X-SKjBazW{hKIP13v@ z%{Pvep&KSk9Xg*EBSu_(tvA@McTCjGn3u$@POaP=RGS00`GF7?-a81Tt|;pS z6}@)X-DvMOClxVY8Jgs7blUx!?LD{NZ8Xu6^Uj6SN%hu=@^igv_@`Hi^oN?@B#?HM zjC$zIP0W8zeZ|SzI%&j(r#SiIBA`S@_o=P~<6_lIHOFD$$Je7hCl)meP`UB9nSwK1 z#l09*T}5>XUsI}cmUtt`WwDFIY}q^n>XISWO0vGMZj+tn0#3O}K4MzafU18Pk{t?=CQYcW|FVmCU?j$0p%)eQ+;m-9LB7hxc|000k52n z?$P{Ka~EIthz~U0-`RzzxR95hE3A~6LYrC#y6qlV1J{^B+p7R=ZNwCc?*r9}Uq-7f zZh8la)Nl(=S)C$}-^7ZuRaN}+APq7=GiI?5fOMKsI@O7@x<~j{r|)BRkMX6`d(jn* z3Pmn+aS{6jSfQSiBi;^pyTGRbx9xpNq)t2|(ir*-%GeQV{sVVlRH36mYuFuNtLpf+ z-}D2Y!`qvsQ=K@gYfR~;ANV5B1rHn=6^hgY&tTi&G&lzIz&!Rf5O3o*_-DZ}4BB{h1@ diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp index da4bc292..08481483 100644 --- a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp @@ -64,6 +64,11 @@ namespace SHADE return bufferUsageFlags; } + uint32_t SHVkBuffer::GetSizeStored(void) const noexcept + { + return sizeStored; + } + /***************************************************************************/ /*! diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h index aeda7d18..eb24d161 100644 --- a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h +++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h @@ -102,8 +102,9 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* SETTERS AND GETTERS */ /*-----------------------------------------------------------------------*/ - vk::Buffer GetVkBuffer (void) const noexcept; + vk::Buffer GetVkBuffer (void) const noexcept; vk::BufferUsageFlags GetUsageBits(void) const noexcept; + uint32_t GetSizeStored (void) const noexcept; template T GetDataFromMappedPointer(uint32_t index) const noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp index d5fb81bd..9717889d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp @@ -79,7 +79,7 @@ namespace SHADE SHVkDescriptorSetLayout::Binding cameraDataBinding { .Type = vk::DescriptorType::eUniformBufferDynamic, - .Stage = vk::ShaderStageFlagBits::eVertex, + .Stage = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eCompute, .BindPoint = SHGraphicsConstants::DescriptorSetBindings::CAMERA_DATA, .DescriptorCount = 1, }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index 0257bf3b..0a67cd9f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -129,6 +129,7 @@ namespace SHADE */ /***************************************************************************/ static constexpr uint32_t BATCHED_PER_INST_DATA = 0; + }; struct VertexBufferBindings diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index d062b4d6..2a59a5d1 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -37,6 +37,7 @@ of DigiPen Institute of Technology is prohibited. #include "Assets/Asset Types/SHTextureAsset.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" #include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h" +#include "Graphics/RenderGraph/SHRenderGraphNodeCompute.h" #include "Assets/SHAssetManager.h" namespace SHADE @@ -113,6 +114,7 @@ namespace SHADE SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_VS.glsl"); SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_FS.glsl"); SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl"); + SHAssetManager::CompileAsset("../../Assets/Shaders/SSAO_CS.glsl"); shaderModuleLibrary.ImportAllShaderSource(device); shaderModuleLibrary.ReflectAllShaderModules(); @@ -154,57 +156,95 @@ namespace SHADE renderContextCmdPools[i] = renderContext.GetFrameData(i).cmdPoolHdls[0]; } + /*-----------------------------------------------------------------------*/ + /* SCENE RENDER GRAPH RESOURCES */ + /*-----------------------------------------------------------------------*/ // Initialize world render graph worldRenderGraph->Init(device, swapchain); worldRenderGraph->AddResource("Position", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Sfloat); worldRenderGraph->AddResource("Normals", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Sfloat); + //worldRenderGraph->AddResource("Tangents", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Sfloat); worldRenderGraph->AddResource("Albedo", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); worldRenderGraph->AddResource("Depth Buffer", { SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL }, windowDims.first, windowDims.second, vk::Format::eD32SfloatS8Uint); worldRenderGraph->AddResource("Entity ID", { SH_ATT_DESC_TYPE_FLAGS::COLOR }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc); worldRenderGraph->AddResource("Light Layer Indices", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc); worldRenderGraph->AddResource("Scene", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); + worldRenderGraph->AddResource("SSAO", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Sfloat); + /*-----------------------------------------------------------------------*/ + /* MAIN NODE */ + /*-----------------------------------------------------------------------*/ auto gBufferNode = worldRenderGraph->AddNode("G-Buffer", { "Position", "Entity ID", "Light Layer Indices", "Normals", + //"Tangents", "Albedo", "Depth Buffer", - "Scene" + "Scene", + "SSAO" }, {}); // no predecessors + /*-----------------------------------------------------------------------*/ + /* G-BUFFER SUBPASS INIT */ + /*-----------------------------------------------------------------------*/ auto gBufferSubpass = gBufferNode->AddSubpass("G-Buffer Write"); gBufferSubpass->AddColorOutput("Position"); gBufferSubpass->AddColorOutput("Entity ID"); gBufferSubpass->AddColorOutput("Light Layer Indices"); gBufferSubpass->AddColorOutput("Normals"); + //gBufferSubpass->AddColorOutput("Tangents"); gBufferSubpass->AddColorOutput("Albedo"); gBufferSubpass->AddDepthOutput("Depth Buffer", SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL); - //// kirsch - //auto kirschShader = shaderModuleLibrary.GetShaderModule("KirschCs.glsl"); - //gBufferNode->AddNodeCompute(kirschShader, { "Position", "Scene" }); + /*-----------------------------------------------------------------------*/ + /* SSAO PASS AND DATA INIT */ + /*-----------------------------------------------------------------------*/ + ssaoStorage = resourceManager.Create(); - //// copy - //auto pureCopyShader = shaderModuleLibrary.GetShaderModule("PureCopyCs.glsl"); - //gBufferNode->AddNodeCompute(pureCopyShader, { "Position", "Scene" }); + ssaoTransferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + ssaoTransferCmdBuffer->BeginRecording(); - // deferred composite + ssaoStorage->Init(device, ssaoTransferCmdBuffer); + + ssaoTransferCmdBuffer->EndRecording(); + graphicsQueue->SubmitCommandBuffer({ ssaoTransferCmdBuffer }); + + graphicsQueue->WaitIdle(); + + auto ssaoShader = shaderModuleLibrary.GetBuiltInShaderModule("SSAO_CS"); + Handle ssaoPass = gBufferNode->AddNodeCompute(ssaoShader, {"Position", "Normals", "SSAO"}); + auto ssaoDataBuffer = ssaoStorage->GetBuffer(); + ssaoPass->ModifyWriteDescBufferComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_BINDING, {&ssaoDataBuffer, 1}, 0, ssaoStorage->GetBuffer()->GetSizeStored()); + + + /*-----------------------------------------------------------------------*/ + /* DEFERRED COMPOSITE SUBPASS INIT */ + /*-----------------------------------------------------------------------*/ auto deferredCompositeShader = shaderModuleLibrary.GetBuiltInShaderModule("DeferredComposite_CS"); - gBufferNode->AddNodeCompute(deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "Scene" }); - + gBufferNode->AddNodeCompute(deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "SSAO", "Scene" }); + /*-----------------------------------------------------------------------*/ + /* DUMMY SUBPASS TO TRANSITION SCENE FOR PRESENT USAGE */ + /*-----------------------------------------------------------------------*/ + // Dummy to transition scene to be used for shader read auto dummyNode = worldRenderGraph->AddNode("Dummy Pass", { "Scene" }, {"G-Buffer"}); // no predecessors auto dummySubpass = dummyNode->AddSubpass("Dummy Subpass"); dummySubpass->AddInput("Scene"); - + /*-----------------------------------------------------------------------*/ + /* GENERATE RENDER GRAPH */ + /*-----------------------------------------------------------------------*/ // Generate world render graph worldRenderGraph->Generate(); + + /*-----------------------------------------------------------------------*/ + /* BIND RENDER GRAPH TO RENDERER */ + /*-----------------------------------------------------------------------*/ // Add world renderer to default viewport worldRenderer = worldViewport->AddRenderer(resourceManager, swapchain->GetNumImages(), renderContextCmdPools, descPool, SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::HIGH_FREQUENCY_GLOBALS], worldRenderGraph); worldRenderer->SetCamera(worldCamera); @@ -254,17 +294,6 @@ namespace SHADE lightingSubSystem = resourceManager.Create(); lightingSubSystem->Init(device, descPool); - ssaoStorage = resourceManager.Create(); - - ssaoTransferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); - ssaoTransferCmdBuffer->BeginRecording(); - - ssaoStorage->Init(device, ssaoTransferCmdBuffer); - - ssaoTransferCmdBuffer->EndRecording(); - graphicsQueue->SubmitCommandBuffer({ssaoTransferCmdBuffer}); - - graphicsQueue->WaitIdle(); } #ifdef SHEDITOR diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp index 655451ec..d0deb30c 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp @@ -88,7 +88,7 @@ namespace SHADE } } - void SHRenderer::UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix viewMatrix, SHMatrix projMatrix) noexcept + void SHRenderer::UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix const& viewMatrix, SHMatrix const& projMatrix) noexcept { SetViewProjectionMatrix(viewMatrix, projMatrix); @@ -98,17 +98,19 @@ namespace SHADE std::array dynamicOffsets{ frameIndex * cameraDataAlignedSize }; cmdBuffer->BindDescriptorSet(cameraDescriptorSet, SH_PIPELINE_TYPE::GRAPHICS, SHGraphicsConstants::DescriptorSetIndex::HIGH_FREQUENCY_GLOBALS, std::span{ dynamicOffsets.data(), 1 }); + cmdBuffer->BindDescriptorSet(cameraDescriptorSet, SH_PIPELINE_TYPE::COMPUTE, SHGraphicsConstants::DescriptorSetIndex::HIGH_FREQUENCY_GLOBALS, std::span{ dynamicOffsets.data(), 1 }); } void SHRenderer::UpdateCameraDataToBuffer(void) noexcept { } - void SHRenderer::SetViewProjectionMatrix(SHMatrix viewMatrix, SHMatrix projMatrix) noexcept + void SHRenderer::SetViewProjectionMatrix(SHMatrix const& viewMatrix, SHMatrix const& projMatrix) noexcept { //cpuCameraData.viewProjectionMatrix = camera->GetViewMatrix() * camera->GetProjectionMatrix(); cpuCameraData.viewProjectionMatrix = SHMatrix::Transpose(projMatrix * viewMatrix); cpuCameraData.viewMatrix = SHMatrix::Transpose(viewMatrix); + cpuCameraData.projectionMatrix = SHMatrix::Transpose(projMatrix); } Handle SHRenderer::GetRenderGraph(void) const noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h index b7c6718c..140cf53b 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.h @@ -47,6 +47,7 @@ namespace SHADE SHVec4 cameraPosition; SHMatrix viewProjectionMatrix; SHMatrix viewMatrix; + SHMatrix projectionMatrix; }; /*---------------------------------------------------------------------------------*/ @@ -80,9 +81,9 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ void Draw(uint32_t frameIndex, Handle descPool) noexcept; void UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex) noexcept; - void UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix viewMatrix, SHMatrix projMatrix) noexcept; + void UpdateDataAndBind(Handle cmdBuffer, uint32_t frameIndex, SHMatrix const& viewMatrix, SHMatrix const& projMatrix) noexcept; void UpdateCameraDataToBuffer (void) noexcept; - void SetViewProjectionMatrix (SHMatrix viewMatrix, SHMatrix projMatrix) noexcept; + void SetViewProjectionMatrix (SHMatrix const& viewMatrix, SHMatrix const& projMatrix) noexcept; /*-----------------------------------------------------------------------------*/ /* Setters and Getters */ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp index f2894786..c6aa1978 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp @@ -39,7 +39,7 @@ namespace SHADE auto& rotationVectors = ssaoData->rotatationVectors; for (uint32_t i = 0; i < NUM_ROTATION_VECTORS; ++i) { - samples[i] = SHVec4 + rotationVectors[i] = SHVec4 { distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f @@ -57,4 +57,9 @@ namespace SHADE ssaoDataBuffer->TransferToDeviceResource(cmdBuffer); } + Handle SHSSAO::GetBuffer(void) const noexcept + { + return ssaoDataBuffer; + } + } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h index 32e8eb1a..040b4695 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h @@ -12,6 +12,9 @@ namespace SHADE class SHSSAO { + public: + static constexpr uint32_t DESC_SET_BINDING = 0; + private: static constexpr uint32_t NUM_SAMPLES = 64; static constexpr uint32_t NUM_ROTATION_VECTORS = 16; @@ -33,5 +36,6 @@ namespace SHADE public: void Init (Handle logicalDevice, Handle cmdBuffer) noexcept; + Handle GetBuffer (void) const noexcept; }; } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp index 60d76c49..2c471c83 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp @@ -82,6 +82,11 @@ namespace SHADE // bind descriptor sets cmdBuffer->BindDescriptorSet(graphResourceDescSets[frameIndex], SH_PIPELINE_TYPE::COMPUTE, SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_RESOURCE, {}); + if (computeResource) + { + cmdBuffer->BindDescriptorSet(computeResource->descSet, SH_PIPELINE_TYPE::COMPUTE, SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, computeResource->dynamicOffsets[frameIndex]); + } + // dispatch compute cmdBuffer->ComputeDispatch(groupSizeX, groupSizeY, 1); @@ -167,6 +172,7 @@ namespace SHADE void SHRenderGraphNodeCompute::ModifyWriteDescBufferComputeResource(uint32_t set, uint32_t binding, std::span> const& buffers, uint32_t offset, uint32_t range) noexcept { computeResource->descSet->ModifyWriteDescBuffer(set, binding, buffers, offset, range); + computeResource->descSet->UpdateDescriptorSetBuffer(set, binding); } } diff --git a/SHADE_Engine/src/Graphics/Shaders/SHShaderReflected.cpp b/SHADE_Engine/src/Graphics/Shaders/SHShaderReflected.cpp index f28561c5..96fa77ab 100644 --- a/SHADE_Engine/src/Graphics/Shaders/SHShaderReflected.cpp +++ b/SHADE_Engine/src/Graphics/Shaders/SHShaderReflected.cpp @@ -142,6 +142,16 @@ namespace SHADE case SpvOp::SpvOpTypeRuntimeArray: recurseForInfo(&member, interfaceHdl, member.offset, biggestAlignment, parentVarName + std::string(member.name) + "."); break; + case SpvOp::SpvOpTypeArray: + interfaceHdl->AddVariable(parentVarName + std::string (member.name), + SHShaderBlockInterface::Variable + ( + parentOffset + member.offset, + SHShaderBlockInterface::Variable::Type::OTHER + ) + ); + biggestAlignment = std::max(biggestAlignment, member.size); + break; } } }; From 441207260b9cfd0b50c3cd87cb89bd9aae2cefe7 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Mon, 31 Oct 2022 20:49:49 +0800 Subject: [PATCH 016/113] shmeta --- Assets/Shaders/SSAO_CS.shshaderb.shmeta | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Assets/Shaders/SSAO_CS.shshaderb.shmeta diff --git a/Assets/Shaders/SSAO_CS.shshaderb.shmeta b/Assets/Shaders/SSAO_CS.shshaderb.shmeta new file mode 100644 index 00000000..a5b5b739 --- /dev/null +++ b/Assets/Shaders/SSAO_CS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: SSAO_CS +ID: 48941934 +Type: 2 From 53b9c8f746ad72b98d954d9f57ff41ffbcc7f62e Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Mon, 31 Oct 2022 23:28:46 +0800 Subject: [PATCH 017/113] SSAO WIP --- Assets/Shaders/SSAO_CS.glsl | 24 +++--- Assets/Shaders/SSAO_CS.shshaderb | Bin 6425 -> 6313 bytes .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 7 +- .../MiddleEnd/PostProcessing/SHSSAO.cpp | 70 ++++++++++++++++-- .../MiddleEnd/PostProcessing/SHSSAO.h | 32 +++++--- .../RenderGraph/SHRenderGraphNodeCompute.cpp | 7 ++ .../RenderGraph/SHRenderGraphNodeCompute.h | 4 +- 7 files changed, 109 insertions(+), 35 deletions(-) diff --git a/Assets/Shaders/SSAO_CS.glsl b/Assets/Shaders/SSAO_CS.glsl index ac84cb81..9cc2de7b 100644 --- a/Assets/Shaders/SSAO_CS.glsl +++ b/Assets/Shaders/SSAO_CS.glsl @@ -19,10 +19,11 @@ layout(set = 4, binding = 2, rgba32f) uniform image2D outputImage; layout(std430, set = 5, binding = 0) buffer SSAOData { vec4 samples[NUM_SAMPLES]; - vec4 rotations[NUM_ROTATIONS]; } ssaoData; +layout (set = 5, binding = 1) uniform sampler2D noiseTexture; + layout(set = 2, binding = 0) uniform CameraData { vec4 position; @@ -32,33 +33,28 @@ layout(set = 2, binding = 0) uniform CameraData } cameraData; -shared vec4 sharedRotations[NUM_ROTATIONS]; void main() { ivec2 size = imageSize (outputImage); - // load rotations into shared memory - uint localThreadIndex = gl_LocalInvocationIndex; - if (localThreadIndex < NUM_ROTATIONS) - sharedRotations[localThreadIndex] = ssaoData.rotations[localThreadIndex]; - - barrier(); - ivec2 globalThread = ivec2 (gl_GlobalInvocationID.xy); // load all the necessary variables vec3 viewSpacePos = imageLoad (positions, globalThread).rgb; vec3 viewSpaceNormal = normalize (imageLoad (normals, globalThread).rgb); - // Get random vector - uint randomVecXIndex = globalThread.x % ROTATION_KERNEL_W; - uint randomVecYIndex = globalThread.y % ROTATION_KERNEL_H; - vec3 randomVec = (sharedRotations[randomVecYIndex * ROTATION_KERNEL_W + randomVecXIndex].rgb) * 2.0f - 1.0f; + ivec2 noiseDim = textureSize(noiseTexture, 0); + vec2 threadUV = globalThread / size; + vec2 noiseUV = vec2 (float(size.x)/float(noiseDim.x), float(size.y) / float(noiseDim.y)); + noiseUV *= threadUV; + vec3 randomVec = texture(noiseTexture, noiseUV).rgb * 2.0f - 1.0f; // Gram schmidt vec3 tangent = normalize (randomVec - (viewSpaceNormal * dot(viewSpaceNormal, randomVec))); vec3 bitangent = cross (tangent, viewSpaceNormal); + + // matrix for tangent to view mat3 TBN = mat3 (tangent, bitangent, viewSpaceNormal); float occlusion = 0.0f; @@ -85,7 +81,7 @@ void main() occlusion += (sampleDepth >= samplePos.z + BIAS ? 1.0f : 0.0f) * rangeCheck; } - occlusion = (occlusion / float(NUM_SAMPLES)); + occlusion = 1.0f - (occlusion / float(NUM_SAMPLES)); // store result into result image imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), vec4(occlusion.rrr, 0.0f)); diff --git a/Assets/Shaders/SSAO_CS.shshaderb b/Assets/Shaders/SSAO_CS.shshaderb index 78fd5087a76621f421742ffd5f2b9fa21528e843..9ba80c93e4334ef6e464462f7f9c384d0a450093 100644 GIT binary patch literal 6313 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 PES|%D^#6}4r-T0kYfO5x diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 2a59a5d1..6a3bd916 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -215,10 +215,15 @@ namespace SHADE graphicsQueue->WaitIdle(); + ssaoStorage->PrepareRotationVectorsVkData(device); + auto ssaoShader = shaderModuleLibrary.GetBuiltInShaderModule("SSAO_CS"); Handle ssaoPass = gBufferNode->AddNodeCompute(ssaoShader, {"Position", "Normals", "SSAO"}); auto ssaoDataBuffer = ssaoStorage->GetBuffer(); - ssaoPass->ModifyWriteDescBufferComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_BINDING, {&ssaoDataBuffer, 1}, 0, ssaoStorage->GetBuffer()->GetSizeStored()); + ssaoPass->ModifyWriteDescBufferComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_BUFFER_BINDING, { &ssaoDataBuffer, 1 }, 0, ssaoStorage->GetBuffer()->GetSizeStored()); + + auto viewSamplerLayout = ssaoStorage->GetViewSamplerLayout(); + ssaoPass->ModifyWriteDescImageComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_IMAGE_BINDING, {&viewSamplerLayout, 1}); /*-----------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp index c6aa1978..b145902f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp @@ -2,15 +2,14 @@ #include "SHSSAO.h" #include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/Buffers/SHVkBuffer.h" +#include "Graphics/Images/SHVkImage.h" +#include "Graphics/Images/SHVkSampler.h" #include namespace SHADE { void SHSSAO::Init(Handle logicalDevice, Handle cmdBuffer) noexcept { - // create memory on CPU side - ssaoData = std::make_unique(); - // Initialize a distribution to get values from 0 to 1 std::uniform_real_distribution distrib{0.0f, 1.0f}; @@ -18,7 +17,6 @@ namespace SHADE std::default_random_engine generator; // generate samples - auto& samples = ssaoData->samples; for (uint32_t i = 0; i < NUM_SAMPLES; ++i) { samples[i] = SHVec4 @@ -36,7 +34,6 @@ namespace SHADE } // generate rotation vector - auto& rotationVectors = ssaoData->rotatationVectors; for (uint32_t i = 0; i < NUM_ROTATION_VECTORS; ++i) { rotationVectors[i] = SHVec4 @@ -48,15 +45,74 @@ namespace SHADE }; } + SHImageCreateParams imageDetails = + { + .imageType = vk::ImageType::e2D, + .width = NUM_ROTATION_VECTORS_W, + .height = NUM_ROTATION_VECTORS_H, + .depth = 1, + .levels = 1, + .arrayLayers = 1, + .imageFormat = vk::Format::eR32G32B32A32Sfloat, + .usageFlags = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst, + .createFlags = {} + }; + + uint32_t mipOffset = 0; + rotationVectorsImage = logicalDevice->CreateImage(imageDetails, reinterpret_cast( rotationVectors.data()), static_cast(sizeof(rotationVectors)), {&mipOffset, 1}, VMA_MEMORY_USAGE_AUTO, {}); + + vk::ImageMemoryBarrier transferBarrier{}; + rotationVectorsImage->PrepareImageTransitionInfo(vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, transferBarrier); + + cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, {transferBarrier}); + + rotationVectorsImage->TransferToDeviceResource(cmdBuffer); + + vk::ImageMemoryBarrier shaderReadBarrier{}; + rotationVectorsImage->PrepareImageTransitionInfo(vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, shaderReadBarrier); + cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eComputeShader, {}, {}, {}, { shaderReadBarrier }); + + // Get aligned size for buffer - uint32_t alignedSize = logicalDevice->PadSSBOSize(sizeof (SamplesAndKernel)); + uint32_t alignedSize = logicalDevice->PadSSBOSize(sizeof (samples)); // Create buffer - ssaoDataBuffer = logicalDevice->CreateBuffer(alignedSize, ssaoData.get(), alignedSize, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst, VMA_MEMORY_USAGE_AUTO, {}); + ssaoDataBuffer = logicalDevice->CreateBuffer(alignedSize, samples.data(), alignedSize, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst, VMA_MEMORY_USAGE_AUTO, {}); ssaoDataBuffer->TransferToDeviceResource(cmdBuffer); } + void SHSSAO::PrepareRotationVectorsVkData(Handle logicalDevice) noexcept + { + SHImageViewDetails DETAILS = + { + .viewType = vk::ImageViewType::e2D, + .format = vk::Format::eR32G32B32A32Sfloat, + .imageAspectFlags = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .mipLevelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + }; + rotationVectorsImageView = rotationVectorsImage->CreateImageView(logicalDevice, rotationVectorsImage, DETAILS); + + SHVkSamplerParams samplerParams + { + .minFilter = vk::Filter::eNearest, + .magFilter = vk::Filter::eNearest, + .addressMode = vk::SamplerAddressMode::eRepeat, + .mipmapMode = vk::SamplerMipmapMode::eNearest, + .maxLod = 1u + }; + + rotationVectorsSampler = logicalDevice->CreateSampler(samplerParams); + } + + SHVkDescriptorSetGroup::viewSamplerLayout SHSSAO::GetViewSamplerLayout(void) const noexcept + { + return std::make_tuple(rotationVectorsImageView, rotationVectorsSampler, vk::ImageLayout::eShaderReadOnlyOptimal); + } + Handle SHSSAO::GetBuffer(void) const noexcept { return ssaoDataBuffer; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h index 040b4695..c7c133c5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.h @@ -3,39 +3,47 @@ #include "Resource/SHHandle.h" #include "Graphics/SHVulkanIncludes.h" #include "Math/Vector/SHVec4.h" +#include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" namespace SHADE { class SHVkBuffer; class SHVkLogicalDevice; class SHVkCommandBuffer; + class SHVkImage; + class SHVkImageView; + class SHVkSampler; class SHSSAO { public: - static constexpr uint32_t DESC_SET_BINDING = 0; + static constexpr uint32_t DESC_SET_BUFFER_BINDING = 0; + static constexpr uint32_t DESC_SET_IMAGE_BINDING = 1; private: static constexpr uint32_t NUM_SAMPLES = 64; - static constexpr uint32_t NUM_ROTATION_VECTORS = 16; - - struct SamplesAndKernel - { - //! distances from a pixel we want to sample - std::array samples; - - std::array rotatationVectors; - }; + static constexpr uint32_t NUM_ROTATION_VECTORS_W = 4; + static constexpr uint32_t NUM_ROTATION_VECTORS_H = 4; + static constexpr uint32_t NUM_ROTATION_VECTORS = NUM_ROTATION_VECTORS_W * NUM_ROTATION_VECTORS_H; private: - //! Samples and kernel on CPU side - std::unique_ptr ssaoData; + //! distances from a pixel we want to sample + std::array samples; //! For passing SSAO samples and kernel to GPU Handle ssaoDataBuffer; + std::array rotationVectors; + + Handle rotationVectorsImage; + Handle rotationVectorsImageView; + Handle rotationVectorsSampler; + public: void Init (Handle logicalDevice, Handle cmdBuffer) noexcept; + void PrepareRotationVectorsVkData (Handle logicalDevice) noexcept; + SHVkDescriptorSetGroup::viewSamplerLayout GetViewSamplerLayout (void) const noexcept; + Handle GetBuffer (void) const noexcept; }; } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp index 2c471c83..a86acbc7 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp @@ -175,4 +175,11 @@ namespace SHADE computeResource->descSet->UpdateDescriptorSetBuffer(set, binding); } + void SHRenderGraphNodeCompute::ModifyWriteDescImageComputeResource(uint32_t set, uint32_t binding, std::span const& viewSamplerLayouts) noexcept + { + computeResource->descSet->ModifyWriteDescImage(set, binding, viewSamplerLayouts); + computeResource->descSet->UpdateDescriptorSetImages(set, binding); + + } + } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h index d61eda33..81157dc2 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h @@ -2,6 +2,7 @@ #include "Resource/SHHandle.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" +#include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" #include "Graphics/SHVulkanIncludes.h" #include #include @@ -10,7 +11,6 @@ namespace SHADE { class SHVkPipeline; - class SHVkDescriptorSetGroup; class SHVkDescriptorPool; class SHVkLogicalDevice; class SHVkPipelineLayout; @@ -74,6 +74,8 @@ namespace SHADE void SetDynamicOffsets (std::span perFrameSizes) noexcept; void ModifyWriteDescBufferComputeResource (uint32_t set, uint32_t binding, std::span> const& buffers, uint32_t offset, uint32_t range) noexcept; + void ModifyWriteDescImageComputeResource(uint32_t set, uint32_t binding, std::span const& viewSamplerLayouts) noexcept; + friend class SHRenderGraph; friend class SHRenderGraphNode; From dc7d5a7ec30a2379f2af47316aaa0f731b4f8123 Mon Sep 17 00:00:00 2001 From: Glence Date: Tue, 1 Nov 2022 01:31:13 +0800 Subject: [PATCH 018/113] updates to the player controller --- TempScriptsFolder/CameraFix.cs | 24 ++++++ TempScriptsFolder/PlayerController.cs | 117 +++++++++++++++----------- 2 files changed, 92 insertions(+), 49 deletions(-) create mode 100644 TempScriptsFolder/CameraFix.cs diff --git a/TempScriptsFolder/CameraFix.cs b/TempScriptsFolder/CameraFix.cs new file mode 100644 index 00000000..5347a72f --- /dev/null +++ b/TempScriptsFolder/CameraFix.cs @@ -0,0 +1,24 @@ +using SHADE; +using System; + +public class CameraFix : Script +{ + public CameraFix(GameObject gameObj) : base(gameObj) { } + + private Transform tranform; + public Vector3 pos = Vector3.Zero; + public Vector3 rot = Vector3.Zero; + protected override void awake() + { + tranform = GetComponent(); + if (tranform == null) + Debug.LogError("tranform is NULL!"); + else + { + tranform.LocalPosition = pos; + tranform.LocalEulerAngles = rot; + } + + + } +} diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 29ac9007..4e28213d 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -18,20 +18,23 @@ public class PlayerController : Script } private RigidBody rb; - private float xAxisMove; - private float zAxisMove; - private RaccoonStates currentState = RaccoonStates.IDILE; + private Transform tranform; + public float xAxisMove; + public float zAxisMove; + private bool isMoveKeyPress = false; + public RaccoonStates currentState = RaccoonStates.IDILE; public float maxMoveVel = 2.0f; public float moveForce = 200.0f; public float sprintMultiplier = 1.5f; + public float rotationFactorPerFrame = 1.0f; public float jumpForce = 500.0f; - public float initialJumpForce = 100.0f; +/* public float initialJumpForce = 100.0f; public float maxJumpForce = 500.0f; public float maxJumpTime = 500.0f; private bool isJumping = false; - private bool isGrounded = false; + private bool isGrounded = false;*/ public PlayerController(GameObject gameObj) : base(gameObj) { } @@ -39,30 +42,38 @@ public class PlayerController : Script { rb = GetComponent(); if (rb == null) - { Debug.LogError("RigidBody is NULL!"); - } + + tranform = GetComponent(); + if(tranform == null) + Debug.LogError("tranform is NULL!"); + + tranform.LocalPosition = new Vector3(-3.0f, -2.0f, -5.0f); + tranform.LocalRotation = Quaternion.Euler(0.0f,0.0f,0.0f); RaccoonStates currentState = RaccoonStates.IDILE; } protected override void update() { - currentState = RaccoonStates.IDILE; + if (Input.GetKey(Input.KeyCode.G)) + { + tranform.LocalRotation = Quaternion.Euler(0.0f, 0.0f, 0.0f); + tranform.LocalPosition = new Vector3(-3.0f, -2.0f, -5.0f); + } + MoveKey(); + //Rotation(); Move(); Sprint(); - Jump(); + //Jump(); - if (rb != null && currentState == RaccoonStates.IDILE) - rb.LinearVelocity = new Vector3(0.0f, 0.0f, 0.0f); - Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); + Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); } - private void Move() + private void MoveKey() { - if (Input.GetKey(Input.KeyCode.A)) xAxisMove = -1; else if (Input.GetKey(Input.KeyCode.D)) @@ -77,57 +88,47 @@ public class PlayerController : Script else zAxisMove = 0; - if((Input.GetKey(Input.KeyCode.A) || Input.GetKey(Input.KeyCode.D) || Input.GetKey(Input.KeyCode.W) || Input.GetKey(Input.KeyCode.S)) && currentState != RaccoonStates.RUNNING) + isMoveKeyPress = xAxisMove != 0 || zAxisMove != 0; + + if(isMoveKeyPress && currentState != RaccoonStates.RUNNING) currentState = RaccoonStates.WALKING; - if (rb != null && currentState == RaccoonStates.WALKING) + if (!isMoveKeyPress) + currentState = RaccoonStates.IDILE; + } + + private void Move() + { + if (isMoveKeyPress && rb != null) { - if (rb.LinearVelocity.x <= maxMoveVel || rb.LinearVelocity.x >= -maxMoveVel) - rb.AddForce(new Vector3(moveForce * xAxisMove, 0.0f, 0.0f)); - else + rb.AddForce(new Vector3(moveForce * xAxisMove, 0.0f, moveForce * zAxisMove)); + + if (rb.LinearVelocity.x > maxMoveVel || rb.LinearVelocity.x < -maxMoveVel) { Vector3 v = rb.LinearVelocity; - if(v.x >= 0) - v.x = maxMoveVel; - else - v.x = -maxMoveVel; - + v.x = System.Math.Clamp(v.x, -maxMoveVel, maxMoveVel); + rb.LinearVelocity = v; + } + if (rb.LinearVelocity.z > maxMoveVel || rb.LinearVelocity.z < -maxMoveVel) + { + Vector3 v = rb.LinearVelocity; + v.z = System.Math.Clamp(v.z, -maxMoveVel, maxMoveVel); rb.LinearVelocity = v; } - if (rb.LinearVelocity.z <= maxMoveVel || rb.LinearVelocity.z >= -maxMoveVel) - rb.AddForce(new Vector3(0.0f, 0.0f, moveForce * zAxisMove )); - else - { - Vector3 v = rb.LinearVelocity; - if (v.z >= 0) - v.z = maxMoveVel; - else - v.z = -maxMoveVel; - rb.LinearVelocity = v; - } + } } private void Sprint() { - //left shift not working for now and chang eto getkey down - if (Input.GetKey(Input.KeyCode.RightShift)) + if (Input.GetKeyDown(Input.KeyCode.LeftShift) && currentState == RaccoonStates.WALKING && isMoveKeyPress) { - if (currentState == RaccoonStates.WALKING && rb != null) - { - currentState = RaccoonStates.RUNNING; - rb.LinearVelocity *= sprintMultiplier; - } + currentState = RaccoonStates.RUNNING; } - - if (Input.GetKeyUp(Input.KeyCode.RightShift)) + else if (Input.GetKeyUp(Input.KeyCode.LeftShift) && currentState == RaccoonStates.RUNNING && isMoveKeyPress) { - if (currentState == RaccoonStates.RUNNING && rb != null) - { - currentState = RaccoonStates.WALKING; - rb.LinearVelocity /= sprintMultiplier; - } + currentState = RaccoonStates.WALKING; } } @@ -147,6 +148,24 @@ public class PlayerController : Script } + private void Rotation() + { + Vector3 poitionToLookAt; + poitionToLookAt.x = xAxisMove; + poitionToLookAt.y = 0.0f; + poitionToLookAt.z = zAxisMove; + + if (tranform != null) + { + Quaternion currentRotation = tranform.LocalRotation; + if (currentState == RaccoonStates.WALKING || currentState == RaccoonStates.RUNNING) + { + Quaternion targetRotation = Quaternion.LookRotation(poitionToLookAt, new Vector3(0.0f, 1.0f, 0.0f)); + tranform.LocalRotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactorPerFrame * (float)Time.DeltaTime); + } + } + } + } From 54a36e147600c5847f66af73768cf2e9ae85abaa Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Tue, 1 Nov 2022 02:32:14 +0800 Subject: [PATCH 019/113] SSAO WIP --- Assets/Shaders/DeferredComposite_CS.glsl | 3 ++- Assets/Shaders/DeferredComposite_CS.shshaderb | Bin 4801 -> 4817 bytes Assets/Shaders/SSAO_CS.glsl | 16 +++++++++------- Assets/Shaders/SSAO_CS.shshaderb | Bin 6313 -> 6433 bytes 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Assets/Shaders/DeferredComposite_CS.glsl b/Assets/Shaders/DeferredComposite_CS.glsl index 4607363b..0d29d8e4 100644 --- a/Assets/Shaders/DeferredComposite_CS.glsl +++ b/Assets/Shaders/DeferredComposite_CS.glsl @@ -55,7 +55,7 @@ void main() vec3 positionView = imageLoad (positions, globalThread).rgb; // normal of fragment - vec3 normalView = imageLoad(normals, globalThread).rgb; + vec3 normalView = -imageLoad(normals, globalThread).rgb; vec3 fragColor = vec3 (0.0f); @@ -79,6 +79,7 @@ void main() } // store result into result image + //imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(normalView, 1.0f)); //imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor, 1.0f)); imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(imageLoad(ssaoImage, globalThread).rgb, 1.0f)); diff --git a/Assets/Shaders/DeferredComposite_CS.shshaderb b/Assets/Shaders/DeferredComposite_CS.shshaderb index d691c168b25040b5ef44acb3432711e65747ca18..cdf5baea792c275a0cb6a28f6bcb400ef788782f 100644 GIT binary patch literal 4817 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 G0RIKoSZT`u literal 6313 zcmZvf36zy(8OJ{cW@J-CQ89?MO)*qLQ#4s^ppY3^jh69p=Q3QKxp$g-K?O=k2{R)t zt!Q&XTWGbiz07P6Y@vOzY_n`%O*^M^`u)E9KA!K)={@H;&;R*9>-&E1_uZMc;VD_Z zMz_s5Cd)=;?=|3QR{n zQ(u35bE$vb)}czNw-q}!pTDoar!#l0tZlECOU-J%wsLu1t0S9=-d?R-wRW&nu5{NM zt-SVZ8ep*As2VJ3kJ-i8)%Br)Qa}8dYz}-aHyhaP*=f05ZB&+52Y`9*GlE-Jxw^S+ z=<&TQfN$m#uiwy$9h03Ou~63Q!X7Hsdg}ulDrK-eH=&!QT3@Bs%wyy?SA#KTbXJ91 zw{&&x-wCdlIqe1;0_)#}HpU+N(2df-V1GIUxf|iuu3gf#ywofK^V=ckYQ?tpdy{sf zRF61w55U#8Y-v=QtwWU?hFew|s0@`_ksVZ&bH!PUy@kVQ@9*}(OG?cG=Y8{e$wr(v zcyOqGg`9m3VwVej2)SeU)WfE9EP*tdn!~Zuy+OgJI7C+gIE7 zVFKd)&{h}YwT*Y__ihyZ+WOs_e(#0;_JUpXzY@L!@ts>iRGhzcT%9~eInTg)K8s>K z?U={>VQg3AuOMd_+j*QJVziBkzQbTSJ~WAI<+WiK^R&(DMxvJ7$UNsQ=4qREGZJ}n zk>@^#v90Ak529y}uw$S4AI0Z$Iu$vQEc0ej!P(UUM6k2VM}P09%lH{+%ohW$xOW$J&icuQO{D}bgJEmmb1pI(C$ypGtu^) zj6Ls9*!-CBHZW_E`ELX`lYVEuJz<~ObWUP8+43Rgy&c;g!-##g4}8u^c4`8|pFe}mn}8BF6H_nkFH z@wuMM7s7dsaXIUwFzsmfQe!;e$zs1FR zn?~5>g6(~(7Hsd6zr&I5ecn~Dt-r5e`|J-CZ1WF~u#YDD*__7&@NtOWn623MIX-b0 zaeAJWwy~b~$%x;RE+YMwoRT@(Px@}C7Z zM!rsu`RMZydFy!>wU-d@UAYi3e<5Pcj$F5R^Zia;l(@)Qgza3#oWhyGQFXBGA(U}N^C{MBGN<04e9Y4emJ7QAcD{+6qw{Y; ztl^z8R=;)1Xnm1)1-Mw}N^E1o-w(F#rDO_+qfE^&A9 z9QJ}=k+|#8{~<>EWr(${CAOAx-+(x0)P6PC+DquPAN?BSCd7I588-ucGomlvzgrR~ z@ABL|=UdV{^;)pKjPnfkq2;XSoqQcKwP4>0HYVPW*MsG5L-wSaZvdO8FKWs~%{PIq z5qILvU^!QCZvo2{@5x&e*SaUIZ_nG{%r{>DM#P>6(E9w=-idw(auB%#agN*3??mL| z?A`@7&$IC?-;Ed}A2sEyxdZ(k#P~yqHO8agi^xaK_koRx=kb29oZ^b-5%>55aMlR% zyMHS72N84iNB_IP)?SFjxqS$1tjn2=k&B!UgUwlnM9xRR<~WNv#>hp^N5SUYjYQ7J zz~=ZY%rQnT_Wg0NIkE3gfaMfd>^tt!C*h3o9>rOFD%o<%o^%$U1{<$GX8a7;e#Nu+ zEVi*OXEa7Ga_#|}Q#^~$VVe_YVT@el+zU3Rcov_>HYd*FUa*{JaSZxCWE|pe`h4^k z5*KInMQm&Occ%U?A##e#S>m_)%V7Q1h?%|u9=Z3g!Wrvw@5abQ&ey;r_x^P_b7Jqt z$T{Z(^burQvVA}9M~ZWQBXROB>(tU2d=soc{^tD_*n0ADr@jrAk8?Z>mW!Fc1D=X_ z_Ri${_FY6i?C*ii^S4_6_Yq^{{jHY!0pe_95dHDD)ephmGyN0!JKWeGA;zAKtU)#+ z#(Abk(fXtJkHPkid4B@lhZN`iDYpEcG~ds_at|Q)!0ko<9I=+ZLuuv*!SeQZj_COd zuyc4nocEW=gNS^@{0h7iiJrd(%NgrF^vU_Vwj1qlk=pzY^ly+|$ZmRQ|2AOW^u Date: Tue, 1 Nov 2022 11:57:08 +0800 Subject: [PATCH 020/113] SSAO WIP --- Assets/Shaders/DeferredComposite_CS.glsl | 2 +- Assets/Shaders/DeferredComposite_CS.shshaderb | Bin 4817 -> 4789 bytes Assets/Shaders/SSAO_CS.glsl | 18 +++++++++--------- Assets/Shaders/SSAO_CS.shshaderb | Bin 6433 -> 6189 bytes .../MiddleEnd/PostProcessing/SHSSAO.cpp | 17 +++++++++++++++++ 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/Assets/Shaders/DeferredComposite_CS.glsl b/Assets/Shaders/DeferredComposite_CS.glsl index 0d29d8e4..da13b1b4 100644 --- a/Assets/Shaders/DeferredComposite_CS.glsl +++ b/Assets/Shaders/DeferredComposite_CS.glsl @@ -81,6 +81,6 @@ void main() // store result into result image //imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(normalView, 1.0f)); //imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor, 1.0f)); - imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(imageLoad(ssaoImage, globalThread).rgb, 1.0f)); + imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(imageLoad(ssaoImage, globalThread).rgba/*, 1.0f*/)); } \ No newline at end of file diff --git a/Assets/Shaders/DeferredComposite_CS.shshaderb b/Assets/Shaders/DeferredComposite_CS.shshaderb index cdf5baea792c275a0cb6a28f6bcb400ef788782f..525f3c4ced3b824db7ef716d0d4f39927b2b018e 100644 GIT binary patch delta 300 zcmYj~u@1pt6ot?Kx7DafL}=2%K+}mGF&J&aGk6P&L1Hi{imGR@(0CB9VwX6#X%jd3 za{qJgz2|=iufWy!IMzQvPI n3h$76`p-M&p20qYjTU=EBvUfvMs^nbdc(dYGXFwZ7%=_^8D=Al delta 328 zcmXw!yAA=)44pM4@oMuFf0C#ye=eieBQJjh*Dn z%$b@0Wd3LEUGvOW4q;ek)d38+NaObKnzrEweS2<}&m5;nk#u3hXRZ)C(!nR*v3hyR z4FP7II)^T+Bgv3RpI95Dfp};sYwZZlib&$JpSab+l}d3TZMsSO!QEXu(*Y7FP_D`9 z4^wDtGLI00j@c6JD5BnuwR$=t>h1qMy&b|qiTObEUi+HsV`HAN0J2z8XyHqn7Wo0{ C%O&Cf diff --git a/Assets/Shaders/SSAO_CS.glsl b/Assets/Shaders/SSAO_CS.glsl index a0fd0bc8..c035adcc 100644 --- a/Assets/Shaders/SSAO_CS.glsl +++ b/Assets/Shaders/SSAO_CS.glsl @@ -7,7 +7,7 @@ const int ROTATION_KERNEL_H = 4; // can perhaps pass in as push constant. const float RADIUS = 0.5f; -const float BIAS = 0.005f; +const float BIAS = 0.025f; layout(local_size_x = 16, local_size_y = 16) in; layout(set = 4, binding = 0, rgba32f) uniform image2D positions; @@ -43,17 +43,17 @@ void main() // load all the necessary variables vec3 viewSpacePos = imageLoad (positions, globalThread).rgb; vec3 viewSpaceNormal = normalize (imageLoad (normals, globalThread).rgb); - viewSpaceNormal = -viewSpaceNormal; + //viewSpaceNormal = -viewSpaceNormal; - ivec2 noiseDim = textureSize(noiseTexture, 0); + vec2 noiseDim = vec2 (textureSize(noiseTexture, 0)); vec2 threadUV = (vec2(globalThread) + vec2 (0.5f)) / vec2(size); - vec2 noiseUVMult = vec2 (float(size.x)/float(noiseDim.x), float(size.y) / float(noiseDim.y)); + vec2 noiseUVMult = vec2 ((vec2(size) / noiseDim); noiseUVMult *= threadUV; - vec3 randomVec = texture(noiseTexture, noiseUVMult).rgb * 2.0f - 1.0f; + vec3 randomVec = texture(noiseTexture, noiseUVMult).rgb; // Gram schmidt vec3 tangent = normalize (randomVec - (viewSpaceNormal * dot(viewSpaceNormal, randomVec))); - vec3 bitangent = cross (viewSpaceNormal, tangent); + vec3 bitangent = normalize (cross (tangent, viewSpaceNormal)); // matrix for tangent to view mat3 TBN = mat3(tangent, bitangent, viewSpaceNormal); @@ -75,16 +75,16 @@ void main() // and bring it from [-1, 1] to screen coordinates offsetPos.xyz = ((offsetPos.xyz * 0.5f) + 0.5f); offsetPos.xy *= vec2(size.xy); - //offsetPos.y = size.y - offsetPos.y; float sampleDepth = imageLoad (positions, ivec2 (offsetPos.xy)).z; float rangeCheck = smoothstep (0.0f, 1.0f, RADIUS / abs (viewSpacePos.z - sampleDepth)); - occlusion += (sampleDepth >= samplePos.z + BIAS ? 1.0f : 0.0f) * rangeCheck; + occlusion += (sampleDepth <= samplePos.z + BIAS ? 1.0f : 0.0f) * rangeCheck; + //occlusion += (sampleDepth <= samplePos.z + BIAS ? 1.0f : 0.0f); } occlusion = 1.0f - (occlusion / float(NUM_SAMPLES)); // store result into result image - imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy), vec4(occlusion.rrr, 0.0f)); + imageStore(outputImage, globalThread, vec4(occlusion.rrr, 1.0f)); } \ No newline at end of file diff --git a/Assets/Shaders/SSAO_CS.shshaderb b/Assets/Shaders/SSAO_CS.shshaderb index ba9a3defde1149ae2025d4b22048dda2dd16a008..e0103cd5dc849c1cf72aaf7c901a6e9fd6af7c0c 100644 GIT binary patch literal 6189 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 G0RIKoSZT`u diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp index b145902f..2bf32fd8 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PostProcessing/SHSSAO.cpp @@ -19,6 +19,23 @@ namespace SHADE // generate samples for (uint32_t i = 0; i < NUM_SAMPLES; ++i) { + //SHVec3 temp + //{ + // distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + // distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f + // distrib(generator), // 0.0f - 1.0f so that sample space is a hemisphere + //}; + + //temp = SHVec3::Normalise(temp); + //temp *= distrib(generator); + + //// This makes sure that most points are closer to fragment's position + //float scale = 1.0f / static_cast(NUM_SAMPLES); + //scale = std::lerp(0.1f, 1.0f, scale * scale); + //temp *= scale; + //samples[i] = SHVec4 (temp.x, temp.y, temp.z, 0.0f); + + samples[i] = SHVec4 { distrib(generator) * 2.0f - 1.0f, // -1.0f - 1.0f From b0054d62c6b7450860e5b453539f968aeb1ed45c Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Tue, 1 Nov 2022 12:09:50 +0800 Subject: [PATCH 021/113] Create AIPrototype.cs --- TempScriptsFolder/AIPrototype.cs | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 TempScriptsFolder/AIPrototype.cs diff --git a/TempScriptsFolder/AIPrototype.cs b/TempScriptsFolder/AIPrototype.cs new file mode 100644 index 00000000..c7240806 --- /dev/null +++ b/TempScriptsFolder/AIPrototype.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using SHADE; + +public class AIPrototype : Script +{ + //This object's relevant components + private Transform transform; + + [SerializeField] + [Tooltip("The list of waypoints that the object will move around on")] + private Vector3[] waypoints; + + [SerializeField] + [Tooltip("How fast the object moves about waypoints")] + private float moveSpeed; + + //To cycle depending on the length of waypoints + private int currentTargetWaypointIndex; + + public AIPrototype(GameObject gameObj) : base(gameObj) { } + + protected override void awake() + { + transform = GetComponent(); + if (transform == null) + { + Debug.LogError("Transform is NULL!"); + } + + currentTargetWaypointIndex = 0; + } + + protected override void update() + { + //Head towards the next target + transform.GlobalPosition += (waypoints[currentTargetWaypointIndex] - transform.GlobalPosition) * moveSpeed * (float)Time.DeltaTime; + + //Cycle to next waypoint if near enough + if ((waypoints[currentTargetWaypointIndex] - transform.GlobalPosition).GetSqrMagnitude() < 1.0f) + { + ++currentTargetWaypointIndex; + if (currentTargetWaypointIndex == waypoints.Length) + { + currentTargetWaypointIndex = 0; //Recycle + } + } + } +} \ No newline at end of file From 53fc1fce953f58766116d2ffcb52fdda01aa32dc Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Tue, 1 Nov 2022 12:10:15 +0800 Subject: [PATCH 022/113] WIP --- Assets/Shaders/SSAO_CS.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Shaders/SSAO_CS.glsl b/Assets/Shaders/SSAO_CS.glsl index c035adcc..2f44763c 100644 --- a/Assets/Shaders/SSAO_CS.glsl +++ b/Assets/Shaders/SSAO_CS.glsl @@ -47,7 +47,7 @@ void main() vec2 noiseDim = vec2 (textureSize(noiseTexture, 0)); vec2 threadUV = (vec2(globalThread) + vec2 (0.5f)) / vec2(size); - vec2 noiseUVMult = vec2 ((vec2(size) / noiseDim); + vec2 noiseUVMult = vec2 (vec2(size) / noiseDim); noiseUVMult *= threadUV; vec3 randomVec = texture(noiseTexture, noiseUVMult).rgb; From 78545889da8ee22fd71924b002e927448de81bfa Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Tue, 1 Nov 2022 12:47:50 +0800 Subject: [PATCH 023/113] SHSHSHAO --- Assets/Shaders/SSAO_CS.glsl | 5 ++++- Assets/Shaders/SSAO_CS.shshaderb | Bin 6189 -> 6237 bytes 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Assets/Shaders/SSAO_CS.glsl b/Assets/Shaders/SSAO_CS.glsl index 2f44763c..a359a3e8 100644 --- a/Assets/Shaders/SSAO_CS.glsl +++ b/Assets/Shaders/SSAO_CS.glsl @@ -78,8 +78,11 @@ void main() float sampleDepth = imageLoad (positions, ivec2 (offsetPos.xy)).z; + if (sampleDepth == 0.0f) + continue; + float rangeCheck = smoothstep (0.0f, 1.0f, RADIUS / abs (viewSpacePos.z - sampleDepth)); - occlusion += (sampleDepth <= samplePos.z + BIAS ? 1.0f : 0.0f) * rangeCheck; + occlusion += (sampleDepth <= samplePos.z - BIAS ? 1.0f : 0.0f) * rangeCheck; //occlusion += (sampleDepth <= samplePos.z + BIAS ? 1.0f : 0.0f); } diff --git a/Assets/Shaders/SSAO_CS.shshaderb b/Assets/Shaders/SSAO_CS.shshaderb index e0103cd5dc849c1cf72aaf7c901a6e9fd6af7c0c..a419165f53608005e3b2fbbadff33153e4be7f19 100644 GIT binary patch literal 6237 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 literal 6189 zcmZ9Od30S>6^Acrl8y|OsoGjP-~g?KB4ClBBc_$MN$nuw5MGnloJah_rLN^#e&4-kv+l}SYp?U$zdfFP&%N)pojNDW z*Oa!!Q?u--Y({o;_RoUKW@l|MS-zjeJ~Qjg^{$OuH!f?o`j?%3&Y8L%lTC5UZO7u% z0d7ZzE48}(3CMBqagdb@JdhWdD;uhObD>KoSOxjM7C?BliSRa-|Yebt^u zvxw`+<^e_;&6>`V)>u^b-P{-*t_;DqXN%$Mx!OeU$d=@Ktyx`H8wTdFX9TycdUb1T z^zpNt1K-L!-oB&o?Z_?)-|ag#jSaQ(oR`3jR_gtY;T_dJuywbvw<`64YQ6Be41H%U z7=5N>6}W9{Hs?87;6@)4Z*nNG_Hp)Fjytjm_GV>xWGD^7v%CUs>(nbf%@kjs%r=Caew6L%+C7_g?}m2Yz0k|{eej)#@7;R5V*ciFb@3eK zrsTLi^I4ShsYgG?$GjtcJw3#%?IC>ZPoJn8K74EvSH!iUm*dop>p>!y+~hd>Eyt-F zcOw#UauMe_?ZI5m$!EN1k{;*O{unl&(-V>7i85{>Im{_O5g+He0P(CRqgzW(`vUZ7 z>~hLUM9#>0b5755s~cNh#>yE#D;ezL{G@xoT-NuSxu*hU&uUK*|0J-z%SV0hr%V4+*!B0WhkY^FrTsK^?Jj?^=TH?3d?Sp__X}p|4!ZZL#ioiIX=*pUsGC4tg)U=ZzU$ z4z_3c@UMciZ0|~Y^82pcJnCafw-^1L?{&y3WG(C+~sRnqSTM}BAa5OU=3;R~YPBk19;{ZS;#_O6M1e@FA168`@Hd(Net z(|i6HqK{(jb9wI8)5qmm{te@|!!!GiPDh+y=;jaIcfjBIvc9OKpAKF!N%wa-{I&a= z9J=`~nWS%-q+eFjF9%mjy7$fBv&iRt^LIFO^G}p??}opnVK@HJB>hOzFJvBmYmPzu z&J3a(cWmM=VtUR>U0>&Y65=9yOl~_S>Rv&H0Ev zJ9$#YJaQ{!_t=lWzW}Am#i|Mb}3@@;?nMuedym z`Dbz0tpD`HUCgepb-f$%*7ZAeCUOSSkM4Kuti%m)F8g?9;+C^F@bMdUcH+(F-BORa zoC~(^@O>6oZi0NqJ{yrc4~dxb!9KT$xd1G8AjMn=HcsA}-e0+hUkNV9uR@pWN%5<} zE0RPXYtUCA+RS5bYZ1>Ab=QIQlixxv-*;yz?+WfYU^(X>HM_w2YO`nGF}bL<9$cKG zsJ$Wg%lMWSb9yeioPH56XS~mCBjR(5e4D`5xRj^nee6c`k#9r%4T*cxljFh%`1rlC zj_0D z9JSk@-y-?w=Y?Ro&|d_$zgf(|S}#V-;q%c~d*pu!IP&Xfe!p$K8l#jIDMRFgZQ|rz&fPiRleL{2$#Hrd~a z=zlk2jv4IlLgXXoyTSUz^LP(fPH}m@;ylIg<9pG~5#o2>nZ6G(R(sTcKiJ#{k(kj3 z!1}uESs%HG`5@Ssdyt6v5ZD-dF-9M`h`A4J%)Lm&d>Cwuy&I#CT%7wOU}NIk9|g-P zt~htxqmRMqvjT~C`r}ELQzp_ZJ^|KWd-V88u=UEb_!PRnE_>8RE@D0nHl{p_&!8I< zv(QH_Vm=EtraX)L(T#~&=p*MWrm}wynU2JNiO(l4X7vShbNYYqH1-D&ImKl!@f-X_ z@B@fBqNfMJlh6JoIDK87T_3rK`7(I&*}np3jP;DsN6tQHvVRqsm-JKEzlN0i{(9o% zUFNB$8GHk*J^q`16Kp>DxKoG0@-fFlV7ch|TVTIM&fcD8uzwqo5B)n}K+668 z7`@<&nm<98dkDFoKK8Kx6fu{!gQ@4ACEoh>5jB4fwh!-z{r&>cS3Z1x32q@1$>&#K zIek5cHaUOS{EhRs$ldr(_J@&iWH0gRzeXNL9zl%PCRhI5+>IXfe*?Bg%=x!qxkr(B zPQL?tHf_<9oH@LEzekQv`c(EmAjc)$bN>;sMtnQ|lsI`M?%1Ef+FZf?1uW-t4(_js e^Dg%AUV7jEh8VBEcKhw4KYOzu?f)ao3grLsf Date: Tue, 1 Nov 2022 13:24:14 +0800 Subject: [PATCH 024/113] sprinting works now --- TempScriptsFolder/PlayerController.cs | 53 +++++++++++++++++++++------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 4e28213d..468ae162 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -19,22 +19,26 @@ public class PlayerController : Script private RigidBody rb; private Transform tranform; - public float xAxisMove; - public float zAxisMove; + private float xAxisMove; + private float zAxisMove; + public float drag = 4.0f; private bool isMoveKeyPress = false; public RaccoonStates currentState = RaccoonStates.IDILE; public float maxMoveVel = 2.0f; public float moveForce = 200.0f; public float sprintMultiplier = 1.5f; + private float oldForce; + private float maxOldVel; + private bool sprintIncreaseOnce = false; public float rotationFactorPerFrame = 1.0f; public float jumpForce = 500.0f; /* public float initialJumpForce = 100.0f; public float maxJumpForce = 500.0f; - public float maxJumpTime = 500.0f; + public float maxJumpTime = 500.0f;*/ private bool isJumping = false; - private bool isGrounded = false;*/ + private bool isGrounded = false; public PlayerController(GameObject gameObj) : base(gameObj) { } @@ -43,6 +47,8 @@ public class PlayerController : Script rb = GetComponent(); if (rb == null) Debug.LogError("RigidBody is NULL!"); + else + rb.Drag = drag; tranform = GetComponent(); if(tranform == null) @@ -50,8 +56,6 @@ public class PlayerController : Script tranform.LocalPosition = new Vector3(-3.0f, -2.0f, -5.0f); tranform.LocalRotation = Quaternion.Euler(0.0f,0.0f,0.0f); - - RaccoonStates currentState = RaccoonStates.IDILE; } protected override void update() @@ -66,12 +70,17 @@ public class PlayerController : Script //Rotation(); Move(); Sprint(); - //Jump(); + Jump(); - Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); + //Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); } + protected override void fixedUpdate() + { + } + + private void MoveKey() { if (Input.GetKey(Input.KeyCode.A)) @@ -122,13 +131,27 @@ public class PlayerController : Script private void Sprint() { - if (Input.GetKeyDown(Input.KeyCode.LeftShift) && currentState == RaccoonStates.WALKING && isMoveKeyPress) + if (Input.GetKey(Input.KeyCode.LeftShift) && isMoveKeyPress) { currentState = RaccoonStates.RUNNING; + if(!sprintIncreaseOnce) + { + sprintIncreaseOnce = true; + oldForce = moveForce; + moveForce = moveForce * sprintMultiplier; + + maxOldVel = maxMoveVel; + maxMoveVel = maxMoveVel * sprintMultiplier; + } } - else if (Input.GetKeyUp(Input.KeyCode.LeftShift) && currentState == RaccoonStates.RUNNING && isMoveKeyPress) + + if (Input.GetKeyUp(Input.KeyCode.LeftShift) && (currentState == RaccoonStates.RUNNING || currentState == RaccoonStates.IDILE)) { - currentState = RaccoonStates.WALKING; + if(isMoveKeyPress) + currentState = RaccoonStates.WALKING; + sprintIncreaseOnce = false; + moveForce = oldForce; + maxMoveVel = maxOldVel; } } @@ -140,6 +163,7 @@ public class PlayerController : Script if (Input.GetKeyDown(Input.KeyCode.Space) && rb != null) { currentState = RaccoonStates.INAIR; + isJumping = true; rb.AddForce(new Vector3(0.0f, jumpForce, 0.0f)); } } @@ -166,6 +190,11 @@ public class PlayerController : Script } } - + protected override void onCollisionEnter(CollisionInfo info) + { + Debug.Log($"Collision Enter: {info.GameObject.Name}"); + } + + } From e397d180dd3f065d98b1575ad012b40045de3065 Mon Sep 17 00:00:00 2001 From: Glence Date: Tue, 1 Nov 2022 17:49:01 +0800 Subject: [PATCH 025/113] jumping almost there --- TempScriptsFolder/PlayerController.cs | 113 ++++++++++++++++++-------- 1 file changed, 77 insertions(+), 36 deletions(-) diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 468ae162..98c3d3b5 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -19,47 +19,58 @@ public class PlayerController : Script private RigidBody rb; private Transform tranform; - private float xAxisMove; - private float zAxisMove; - public float drag = 4.0f; + private int xAxisMove; + private int zAxisMove; + public float drag = 2.0f; private bool isMoveKeyPress = false; public RaccoonStates currentState = RaccoonStates.IDILE; public float maxMoveVel = 2.0f; - public float moveForce = 200.0f; - public float sprintMultiplier = 1.5f; + public float moveForce = 50.0f; + public float sprintMultiplier = 2.0f; private float oldForce; private float maxOldVel; private bool sprintIncreaseOnce = false; public float rotationFactorPerFrame = 1.0f; - public float jumpForce = 500.0f; -/* public float initialJumpForce = 100.0f; - public float maxJumpForce = 500.0f; - public float maxJumpTime = 500.0f;*/ + + public float initialJumpVel; + public float maxJumpHeight = 1.0f; + public float maxJumpTime = 0.5f; private bool isJumping = false; - private bool isGrounded = false; + public bool isGrounded = true; + private float gravity = -9.8f; + private float groundGravity = -0.5f; public PlayerController(GameObject gameObj) : base(gameObj) { } protected override void awake() { + //rigidbody check rb = GetComponent(); if (rb == null) Debug.LogError("RigidBody is NULL!"); else rb.Drag = drag; + //Transform check tranform = GetComponent(); if(tranform == null) Debug.LogError("tranform is NULL!"); + //Jump setup + float timeToApex = maxJumpTime / 2; + gravity = (-2 * maxJumpHeight) / MathF.Pow(timeToApex, 2); + initialJumpVel = (2 * maxJumpHeight) / timeToApex; + + //toRemove tranform.LocalPosition = new Vector3(-3.0f, -2.0f, -5.0f); - tranform.LocalRotation = Quaternion.Euler(0.0f,0.0f,0.0f); + tranform.LocalRotation = Quaternion.Euler(0.0f, 0.0f, 0.0f); } protected override void update() { + //toRemove if (Input.GetKey(Input.KeyCode.G)) { tranform.LocalRotation = Quaternion.Euler(0.0f, 0.0f, 0.0f); @@ -67,17 +78,18 @@ public class PlayerController : Script } MoveKey(); - //Rotation(); - Move(); - Sprint(); - Jump(); - //Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); + Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); } protected override void fixedUpdate() { + //Rotation(); + Move(); + Sprint(); + Jump(); + Gravity(); } @@ -108,24 +120,25 @@ public class PlayerController : Script private void Move() { - if (isMoveKeyPress && rb != null) + if (rb != null) { rb.AddForce(new Vector3(moveForce * xAxisMove, 0.0f, moveForce * zAxisMove)); - if (rb.LinearVelocity.x > maxMoveVel || rb.LinearVelocity.x < -maxMoveVel) + if (isMoveKeyPress) { - Vector3 v = rb.LinearVelocity; - v.x = System.Math.Clamp(v.x, -maxMoveVel, maxMoveVel); - rb.LinearVelocity = v; + if (rb.LinearVelocity.x > maxMoveVel || rb.LinearVelocity.x < -maxMoveVel) + { + Vector3 v = rb.LinearVelocity; + v.x = System.Math.Clamp(v.x, -maxMoveVel, maxMoveVel); + rb.LinearVelocity = v; + } + if (rb.LinearVelocity.z > maxMoveVel || rb.LinearVelocity.z < -maxMoveVel) + { + Vector3 v = rb.LinearVelocity; + v.z = System.Math.Clamp(v.z, -maxMoveVel, maxMoveVel); + rb.LinearVelocity = v; + } } - if (rb.LinearVelocity.z > maxMoveVel || rb.LinearVelocity.z < -maxMoveVel) - { - Vector3 v = rb.LinearVelocity; - v.z = System.Math.Clamp(v.z, -maxMoveVel, maxMoveVel); - rb.LinearVelocity = v; - } - - } } @@ -138,10 +151,10 @@ public class PlayerController : Script { sprintIncreaseOnce = true; oldForce = moveForce; - moveForce = moveForce * sprintMultiplier; + moveForce *= sprintMultiplier; maxOldVel = maxMoveVel; - maxMoveVel = maxMoveVel * sprintMultiplier; + maxMoveVel *= sprintMultiplier; } } @@ -160,16 +173,23 @@ public class PlayerController : Script { if (currentState == RaccoonStates.WALKING || currentState == RaccoonStates.RUNNING || currentState == RaccoonStates.IDILE) { - if (Input.GetKeyDown(Input.KeyCode.Space) && rb != null) + if (Input.GetKeyDown(Input.KeyCode.Space)&& isGrounded && rb != null) { - currentState = RaccoonStates.INAIR; isJumping = true; - rb.AddForce(new Vector3(0.0f, jumpForce, 0.0f)); + Vector3 v = rb.LinearVelocity; + v.y = initialJumpVel; + rb.LinearVelocity = v; } } - //collision check when grounded + if (rb != null) + { + if (rb.LinearVelocity.y > 0 && isJumping) + currentState = RaccoonStates.INAIR; + else if(rb.LinearVelocity.y < 0 && isJumping) + currentState = RaccoonStates.FALLING; + } } private void Rotation() @@ -190,9 +210,30 @@ public class PlayerController : Script } } + private void Gravity() + { + if (rb != null) + { + //check player vel.y if its close to zero its on the ground + if (SHADE.Math.CompareFloat(rb.LinearVelocity.y, 0.0f, 0.1f)) + isGrounded = true; + else + isGrounded = false; + + Vector3 v = rb.LinearVelocity; + + if (isGrounded) + v.y = groundGravity; + else + v.y += gravity * (float)Time.DeltaTime; + + rb.LinearVelocity = v; + + } + } + protected override void onCollisionEnter(CollisionInfo info) { - Debug.Log($"Collision Enter: {info.GameObject.Name}"); } From 26591e8c245dd8bbd74471d6ca8d9f028fb6a2cd Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Tue, 1 Nov 2022 19:03:08 +0800 Subject: [PATCH 026/113] material inspector --- .../MaterialInspector/SHMaterialInspector.cpp | 81 +++++++++++++++++++ .../MaterialInspector/SHMaterialInspector.h | 28 +++++++ 2 files changed, 109 insertions(+) create mode 100644 SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp create mode 100644 SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h diff --git a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp new file mode 100644 index 00000000..c4904533 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp @@ -0,0 +1,81 @@ +#include "SHpch.h" +#include "SHMaterialInspector.h" +#include "Editor/SHImGuiHelpers.hpp" +#include + +//#include "Assets/SHAssetManager.h" +//#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" +//#include "Editor/SHEditorWidgets.hpp" +//#include "Resource/SHResourceManager.h" + +namespace SHADE +{ + SHMaterialInspector::SHMaterialInspector() + :SHEditorWindow("Material Inspector", ImGuiWindowFlags_MenuBar)//,isNewMaterial(false), currentViewedMaterial(0) + { + } + + void SHMaterialInspector::OpenMaterial(AssetID const& assetId) noexcept + { + //Get mat data + //isNewMaterial = false; + } + + void SHMaterialInspector::Init() + { + SHEditorWindow::Init(); + } + + void SHMaterialInspector::Update() + { + SHEditorWindow::Update(); + + //if(Begin()) + //{ + // DrawMenuBar(); + + // if(SHEditorWidgets::DragDropReadOnlyField("Vertex Shader", std::to_string(currentMatSpec.vertexShader), [&](){return currentMatSpec.vertexShader;}, [&](AssetID const& id){currentMatSpec.vertexShader = id;}, SHDragDrop::DRAG_RESOURCE)) + // { + // vertShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec.vertexShader); + // } + // if(SHEditorWidgets::DragDropReadOnlyField("Fragment Shader", std::to_string(currentMatSpec.fragShader), [&](){return currentMatSpec.fragShader;}, [&](AssetID const& id){currentMatSpec.fragShader = id;}, SHDragDrop::DRAG_RESOURCE)) + // { + // fragShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec.fragShader); + // } + + // auto vertInterface = vertShaderHandle->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA); + + // int const varCount = static_cast(vertInterface->GetVariableCount()); + // + // for(int i = 0; i < varCount; ++i) + // { + // auto variable = vertInterface->GetVariable(i); + // } + + //} + //ImGui::End(); + } + + void SHMaterialInspector::Exit() + { + SHEditorWindow::Exit(); + } + + void SHMaterialInspector::CreateNewMaterial() + { + //isNewMaterial = true; + ////prompt for a name + //currentViewedMaterial = SHAssetManager::CreateNewAsset(AssetType::MATERIAL, "NewMaterial"); + //currentMatSpec = {}; + //vertShaderHandle = {}; + //fragShaderHandle = {}; + } + + void SHMaterialInspector::DrawMenuBar() + { + if(ImGui::BeginMenuBar()) + { + ImGui::EndMenuBar(); + } + } +} diff --git a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h new file mode 100644 index 00000000..7256a0b6 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h @@ -0,0 +1,28 @@ +#pragma once +#include "Assets/SHAssetMacros.h" +#include "Editor/EditorWindow/SHEditorWindow.h" + + +namespace SHADE +{ + class SHMaterialInspector final : public SHEditorWindow + { + public: + SHMaterialInspector(); + ~SHMaterialInspector() = default; + + void Init() override; + void Update() override; + void Exit() override; + + void CreateNewMaterial(); + void OpenMaterial(AssetID const& assetId) noexcept; + private: + void DrawMenuBar(); + + //bool isNewMaterial; + //AssetID currentViewedMaterial; + //SHMaterialSpec currentMatSpec; + //Handle vertShaderHandle, fragShaderHandle; + }; +} From b0d08d27fd1f5a7a3e43d17d2269c8766082408b Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Tue, 1 Nov 2022 20:10:59 +0800 Subject: [PATCH 027/113] SSAO is fully implemented --- Assets/Shaders/DeferredComposite_CS.glsl | 12 ++-- Assets/Shaders/DeferredComposite_CS.shshaderb | Bin 4789 -> 4997 bytes Assets/Shaders/SSAOBlur_CS.glsl | 58 ++++++++++++++++++ Assets/Shaders/SSAOBlur_CS.shshaderb | Bin 0 -> 4669 bytes Assets/Shaders/SSAO_CS.glsl | 35 +++++++---- Assets/Shaders/SSAO_CS.shshaderb | Bin 6237 -> 6125 bytes .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 12 +++- 7 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 Assets/Shaders/SSAOBlur_CS.glsl create mode 100644 Assets/Shaders/SSAOBlur_CS.shshaderb diff --git a/Assets/Shaders/DeferredComposite_CS.glsl b/Assets/Shaders/DeferredComposite_CS.glsl index da13b1b4..833180e7 100644 --- a/Assets/Shaders/DeferredComposite_CS.glsl +++ b/Assets/Shaders/DeferredComposite_CS.glsl @@ -21,7 +21,7 @@ layout(set = 4, binding = 0, rgba32f) uniform image2D positions; layout(set = 4, binding = 1, rgba32f) uniform image2D normals; layout(set = 4, binding = 2, rgba8) uniform image2D albedo; layout(set = 4, binding = 3, r32ui) uniform uimage2D lightLayerData; -layout(set = 4, binding = 4, rgba32f) uniform image2D ssaoImage; +layout(set = 4, binding = 4, r8) uniform image2D ssaoBlurredImage; layout(set = 4, binding = 5, rgba8) uniform image2D targetImage; layout(set = 1, binding = 0) uniform LightCounts @@ -55,7 +55,7 @@ void main() vec3 positionView = imageLoad (positions, globalThread).rgb; // normal of fragment - vec3 normalView = -imageLoad(normals, globalThread).rgb; + vec3 normalView = imageLoad(normals, globalThread).rgb; vec3 fragColor = vec3 (0.0f); @@ -78,9 +78,11 @@ void main() fragColor += pixelDiffuse.rgb * AmbLightData.aLightData[i].ambientColor.rgb * vec3 (AmbLightData.aLightData[i].strength); } + float ssaoVal = imageLoad (ssaoBlurredImage, globalThread).r; + fragColor *= ssaoVal; + // store result into result image - //imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(normalView, 1.0f)); - //imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor, 1.0f)); - imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(imageLoad(ssaoImage, globalThread).rgba/*, 1.0f*/)); + imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor.rgb, 1.0f)); + //imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(ssaoVal.rrr, 1.0f)); } \ No newline at end of file diff --git a/Assets/Shaders/DeferredComposite_CS.shshaderb b/Assets/Shaders/DeferredComposite_CS.shshaderb index 525f3c4ced3b824db7ef716d0d4f39927b2b018e..8d7a83271b3d684965b4a639eba7e74329c9469d 100644 GIT binary patch literal 4997 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 diff --git a/Assets/Shaders/SSAOBlur_CS.glsl b/Assets/Shaders/SSAOBlur_CS.glsl new file mode 100644 index 00000000..64066525 --- /dev/null +++ b/Assets/Shaders/SSAOBlur_CS.glsl @@ -0,0 +1,58 @@ +#version 450 + +#define BLUR_WIDTH 5 +#define BLUR_HALF_WIDTH BLUR_WIDTH / 2 +#define SHM_WIDTH BLUR_WIDTH + 16 - 1 + +layout(local_size_x = 16, local_size_y = 16) in; +layout(set = 4, binding = 0, r8) uniform image2D ssaoImage; +layout(set = 4, binding = 1, r8) uniform image2D ssaoBlurImage; + + +float GetSSAOValue(ivec2 uv, ivec2 imageSize) +{ + if (uv.x >= 0 && uv.y >= 0 && uv.x < imageSize.x && uv.y < imageSize.y) + { + return imageLoad (ssaoImage, uv).r; + } + + return 0.0f; +} + +shared float sharedPixels[16 + BLUR_WIDTH - 1][16 + BLUR_WIDTH - 1]; + +void main() +{ + ivec2 globalThread = ivec2 (gl_GlobalInvocationID.xy); + ivec2 localThread = ivec2 (gl_LocalInvocationID.xy); + ivec2 inputImageSize = imageSize(ssaoImage); + + // Load color into shared memory + ivec2 start = ivec2 (gl_WorkGroupID) * ivec2 (gl_WorkGroupSize) - (BLUR_HALF_WIDTH); + for (int i = localThread.x; i < SHM_WIDTH; i += int (gl_WorkGroupSize.x)) + { + for (int j = localThread.y; j < SHM_WIDTH; j += int (gl_WorkGroupSize.y)) + { + float value = GetSSAOValue (start + ivec2 (i, j), inputImageSize); + sharedPixels[i][j] = value; + } + } + + // wait for all shared memory to load + barrier(); + + ivec2 shmStart = ivec2 (localThread + (BLUR_HALF_WIDTH)); + + float sum = 0; + for (int i = -BLUR_HALF_WIDTH; i <= BLUR_HALF_WIDTH; ++i) + { + for (int j = -BLUR_HALF_WIDTH; j <= BLUR_HALF_WIDTH; ++j) + { + float sharedVal = sharedPixels[shmStart.x + i][shmStart.y + j]; + sum += sharedVal; + } + } + + sum /= (BLUR_WIDTH * BLUR_WIDTH); + imageStore(ssaoBlurImage, globalThread, vec4(sum.rrr, 1.0f)); +} \ No newline at end of file diff --git a/Assets/Shaders/SSAOBlur_CS.shshaderb b/Assets/Shaders/SSAOBlur_CS.shshaderb new file mode 100644 index 0000000000000000000000000000000000000000..680cb8d5f8bd787a251d73bf38d4d814202e13e0 GIT binary patch literal 4669 zcmZ9O2X~ZJ5QaYj8={B^0*a!7y&@v09BUL^qtQfzy~HJ1VRcj7Y+_f$hF!7ug1uu$ z#jeMn;h(Ucqxd{u?sz_P&w0F!;)NroR+obe7UoybJ0k%f6>xqi=DE4Hd>W@K$y34IC}sMH4Kj|V4%Gr$hy z&^I&Jwa4d8CPxeUcPu=GYRa9x<*u%B=gGZ&!^6EJ&1>t`-g<4Y+S^}iRQsB>v8r)x zg}B;aUwyQ{7*HajSVxKY>8w$%HhX&7yUwlDN2~jf)s`GB;7imn4LLfdUdpyZuMJdI zS9@yLv0TpChmn!WP=|cZwPwd4SJ#JDRq89(G^&+;Ok&&eeXp+fmUCCf;Mh=KrCA#q z>{yZeOPR6`8Pl9&pP17rJz)wtTeA-QwZY-hW?sN}VeYdNexzAxG?B*HYx0~o)VQ+T z7#baRQTKPD^YfU=ne|_h>r=8}WS{=0kF$1G&BdJ=T93eSG~=wlSY)K8^+5J#3bv*C*CT8`1AFd-V+V z1XDoR3(+0g-Z#&2ezNVU_O8jcH`==-+jWZhw9V%mFyD_zdS#c|FKBxwm;@O z4(WRG=F|3!e%g<7fSib!^Uz%*>^}6_ zz#4~;&T}vN&2=5nKOg&gq&_+A8<29^&c6|9Kjf`h+czSoe_hgx`PaimJ+5~bXk(L6 z-^1AdY~8vw><_SU=YI2i0)W^ho_{kY#I+kNy3CHQuw<{A|4E za>6@v1iiFA_uw$sA221PYkF7x_Sv_vS4^-wCfI(bBHp`u;RL%{usxIEg6(>KmtwxQ z-=(7M_a|)U`|Sza^)^hf{RV~K_>IXvmi>8O+ktnri>MQjCjvR)@H}oH)*L4R{m0QM z`=T9rPC@rhN1igeoNyHLoQkdAJoZ!DJd2U1fhAx~TJ!Y88Edb#W6d+seK*$Eh3p1$ zXK{kQpR*E&_>Vg~an{qF-cnEQ&oX_tCGK2wdEZ&YosaGt6YEmk1?a}=vqsNft{wj@ zSCIOZk!uyYIX1E03}io$>jQt0 zL;F%-F6XL8ZI`24n{OfVtwxu(Ht(voYp+2%UukXru3ZTn`u$DQuZ&#&wwfbq^Uso8 z)TUhn=5nsObEhJ&0`@X;HPGE>*#01!JI~PDSf^( z?d6=%Hppwibl@C)#`sp{qlTN&i#4sqwkG}NxCJ<;ot*2Cw*tSJ&RYx2DQBELw4e4t z8Ef2zZcg{)-MJmetpv{3Cs#Zp`>A~y(tOIucL%yPN4`7J-7Xh;z6XJ>E#1!+j}EU%bux(d8Y%J%BDZ2}HgJ(T&yTe(y%gsXv5Vp7agK zhkg2+>7r>`%z#n=c-$S-`808ndDQt z?sLfJfx~rQKsr|$>%NF?j#&34bh%hp`&nQv=c>p1eHq<;6u&L6V9Q7TSJB;9ocC+! z#>hvW*U`;055&AT(B;E^6Wux^&s*rm$jAMD8{PZu8v320b2fD50tn))`xeq{`mz?wCyzH&Ecf@?k$oCO?v40<9 z%SAqYa?X!?=N*ate2VUV)~Ee^hA#IBi2cYJzZm&B*qm(N*B9WJWZRD~f%&@m8f-y+ zl{k6fTQcq&pwAK9x9D=dui(B*Tnnep##+j#bsuzVv3}#N!&>)4I;_>0eSw^{sz)9B zqfhMF0dVpWb0E647W;Y-wtUojFuL>Ptu@wCMy-dSTZ{D@ZynZpDAHlA#w-GI)~X)w qAa9wU;Bk)Bghe^~S>h literal 0 HcmV?d00001 diff --git a/Assets/Shaders/SSAO_CS.glsl b/Assets/Shaders/SSAO_CS.glsl index a359a3e8..1a572521 100644 --- a/Assets/Shaders/SSAO_CS.glsl +++ b/Assets/Shaders/SSAO_CS.glsl @@ -36,37 +36,45 @@ layout(set = 2, binding = 0) uniform CameraData void main() { - ivec2 size = imageSize (outputImage); + // image size of the SSAO image + ivec2 ssaoSize = imageSize (outputImage); + // global thread ivec2 globalThread = ivec2 (gl_GlobalInvocationID.xy); // load all the necessary variables vec3 viewSpacePos = imageLoad (positions, globalThread).rgb; vec3 viewSpaceNormal = normalize (imageLoad (normals, globalThread).rgb); - //viewSpaceNormal = -viewSpaceNormal; + // Get the noise dimension. This should be 4x4 vec2 noiseDim = vec2 (textureSize(noiseTexture, 0)); - vec2 threadUV = (vec2(globalThread) + vec2 (0.5f)) / vec2(size); - vec2 noiseUVMult = vec2 (vec2(size) / noiseDim); + + // Get normlized thread UV coordinates + vec2 threadUV = (vec2(globalThread)) / vec2(ssaoSize); + vec2 noiseUVMult = vec2 (vec2(ssaoSize) / noiseDim); noiseUVMult *= threadUV; + + // sample from the noise vec3 randomVec = texture(noiseTexture, noiseUVMult).rgb; // Gram schmidt vec3 tangent = normalize (randomVec - (viewSpaceNormal * dot(viewSpaceNormal, randomVec))); vec3 bitangent = normalize (cross (tangent, viewSpaceNormal)); - // matrix for tangent to view + // matrix for tangent space to view space mat3 TBN = mat3(tangent, bitangent, viewSpaceNormal); float occlusion = 0.0f; for (int i = 0; i < NUM_SAMPLES; ++i) { // We want to get a position at an offset from the view space position. Offset scaled by radius. - vec3 samplePos = TBN * ssaoData.samples[i].rgb; - samplePos = viewSpacePos + samplePos * RADIUS; + vec3 displacementVector = TBN * ssaoData.samples[i].rgb; + + // Why are we adding positions? + displacementVector = viewSpacePos + displacementVector * RADIUS; // Now we take that offset position and bring it to clip space - vec4 offsetPos = vec4 (samplePos, 1.0f); + vec4 offsetPos = vec4 (displacementVector, 1.0f); offsetPos = cameraData.projMat * offsetPos; // then we do perspective division @@ -74,20 +82,23 @@ void main() // and bring it from [-1, 1] to screen coordinates offsetPos.xyz = ((offsetPos.xyz * 0.5f) + 0.5f); - offsetPos.xy *= vec2(size.xy); + offsetPos.xy *= vec2(ssaoSize.xy); + // Now we attempt to get a position at that point. float sampleDepth = imageLoad (positions, ivec2 (offsetPos.xy)).z; + // skip checks if (sampleDepth == 0.0f) continue; + // if sampled fragment is in front of current fragment, just occlude float rangeCheck = smoothstep (0.0f, 1.0f, RADIUS / abs (viewSpacePos.z - sampleDepth)); - occlusion += (sampleDepth <= samplePos.z - BIAS ? 1.0f : 0.0f) * rangeCheck; - //occlusion += (sampleDepth <= samplePos.z + BIAS ? 1.0f : 0.0f); + occlusion += (sampleDepth <= displacementVector.z - BIAS ? 1.0f : 0.0f) * rangeCheck; } occlusion = 1.0f - (occlusion / float(NUM_SAMPLES)); // store result into result image - imageStore(outputImage, globalThread, vec4(occlusion.rrr, 1.0f)); + imageStore(outputImage, globalThread, occlusion.rrrr); + } \ No newline at end of file diff --git a/Assets/Shaders/SSAO_CS.shshaderb b/Assets/Shaders/SSAO_CS.shshaderb index a419165f53608005e3b2fbbadff33153e4be7f19..6bb4abcd8018cac584ecc8f8b59b86984eb528d7 100644 GIT binary patch literal 6125 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 G$o~MNH$(FP literal 6237 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 diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 6a3bd916..af3b46e1 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -115,6 +115,7 @@ namespace SHADE SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_FS.glsl"); SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl"); SHAssetManager::CompileAsset("../../Assets/Shaders/SSAO_CS.glsl"); + SHAssetManager::CompileAsset("../../Assets/Shaders/SSAOBlur_CS.glsl"); shaderModuleLibrary.ImportAllShaderSource(device); shaderModuleLibrary.ReflectAllShaderModules(); @@ -169,7 +170,8 @@ namespace SHADE worldRenderGraph->AddResource("Entity ID", { SH_ATT_DESC_TYPE_FLAGS::COLOR }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc); worldRenderGraph->AddResource("Light Layer Indices", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc); worldRenderGraph->AddResource("Scene", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); - worldRenderGraph->AddResource("SSAO", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Sfloat); + worldRenderGraph->AddResource("SSAO", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR8Unorm); + worldRenderGraph->AddResource("SSAO Blur", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR8Unorm); /*-----------------------------------------------------------------------*/ /* MAIN NODE */ @@ -184,7 +186,8 @@ namespace SHADE "Albedo", "Depth Buffer", "Scene", - "SSAO" + "SSAO", + "SSAO Blur" }, {}); // no predecessors @@ -225,12 +228,15 @@ namespace SHADE auto viewSamplerLayout = ssaoStorage->GetViewSamplerLayout(); ssaoPass->ModifyWriteDescImageComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_IMAGE_BINDING, {&viewSamplerLayout, 1}); + auto ssaoBlurShader = shaderModuleLibrary.GetBuiltInShaderModule("SSAOBlur_CS"); + Handle ssaoBlurPass = gBufferNode->AddNodeCompute(ssaoBlurShader, { "SSAO", "SSAO Blur"}); + /*-----------------------------------------------------------------------*/ /* DEFERRED COMPOSITE SUBPASS INIT */ /*-----------------------------------------------------------------------*/ auto deferredCompositeShader = shaderModuleLibrary.GetBuiltInShaderModule("DeferredComposite_CS"); - gBufferNode->AddNodeCompute(deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "SSAO", "Scene" }); + gBufferNode->AddNodeCompute(deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "SSAO Blur", "Scene" }); /*-----------------------------------------------------------------------*/ /* DUMMY SUBPASS TO TRANSITION SCENE FOR PRESENT USAGE */ From 674b4d5155e70751f54dbba888649de5a4473e01 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 1 Nov 2022 20:29:03 +0800 Subject: [PATCH 028/113] Fixed extra # appearing on all names of script variables --- SHADE_Engine/src/Editor/SHEditorUI.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SHADE_Engine/src/Editor/SHEditorUI.cpp b/SHADE_Engine/src/Editor/SHEditorUI.cpp index f3760b4d..cc63c565 100644 --- a/SHADE_Engine/src/Editor/SHEditorUI.cpp +++ b/SHADE_Engine/src/Editor/SHEditorUI.cpp @@ -157,7 +157,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::Checkbox("#", &value); + return ImGui::Checkbox("##", &value); } bool SHEditorUI::InputInt(const std::string& label, int& value, bool* isHovered) { @@ -165,7 +165,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::InputInt("#", &value, + return ImGui::InputInt("##", &value, 1, 10, ImGuiInputTextFlags_EnterReturnsTrue); } @@ -176,7 +176,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - const bool CHANGED = InputInt("#", signedVal); + const bool CHANGED = InputInt("##", signedVal); if (CHANGED) { signedVal = std::clamp(signedVal, 0, std::numeric_limits::max()); @@ -190,7 +190,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::InputFloat("#", &value, + return ImGui::InputFloat("##", &value, 0.1f, 1.0f, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue); } @@ -200,7 +200,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::InputDouble("#", &value, + return ImGui::InputDouble("##", &value, 0.1, 1.0, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue); } @@ -210,7 +210,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::InputDouble("#", &value, + return ImGui::InputDouble("##", &value, 1.0, 45.0, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue); } @@ -280,7 +280,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - const bool CHANGED = ImGui::InputText("#", &buffer[0], TEXT_FIELD_MAX_LENGTH); + const bool CHANGED = ImGui::InputText("##", &buffer[0], TEXT_FIELD_MAX_LENGTH); if (CHANGED) { value = std::string(buffer.data(), buffer.data() + TEXT_FIELD_MAX_LENGTH); @@ -327,7 +327,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - if (ImGui::BeginCombo("#", INITIAL_NAME.c_str(), ImGuiComboFlags_None)) + if (ImGui::BeginCombo("##", INITIAL_NAME.c_str(), ImGuiComboFlags_None)) { for (int i = 0; i < enumNames.size(); ++i) { From 0096bc49757719b223dcc55b45852dde40d173db Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 1 Nov 2022 20:32:35 +0800 Subject: [PATCH 029/113] Debug Draw fixes --- .../src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h | 5 +++++ .../Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index 2c6a6600..0f95e661 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -26,6 +26,11 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /*---------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*---------------------------------------------------------------------------------*/ + class SHVkBuffer; + /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index e6c1cd27..0353c2ec 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -131,6 +131,9 @@ namespace SHADE shaderSourceLibrary.LoadShader(4, "DebugDrawVs.glsl", SH_SHADER_TYPE::VERTEX, true); shaderSourceLibrary.LoadShader(5, "DebugDrawFs.glsl", SH_SHADER_TYPE::FRAGMENT, true); + shaderSourceLibrary.LoadShader(3, "DebugDrawVs.glsl", SH_SHADER_TYPE::VERTEX, true); + shaderSourceLibrary.LoadShader(4, "DebugDrawFs.glsl", SH_SHADER_TYPE::FRAGMENT, true); + shaderModuleLibrary.ImportFromSourceLibrary(device, shaderSourceLibrary); auto cubeVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl"); auto cubeFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl"); @@ -214,6 +217,11 @@ namespace SHADE auto dummySubpass = dummyNode->AddSubpass("Dummy Subpass"); dummySubpass->AddInput("Scene"); + // Set up Debug Draw Pass + auto debugDrawNode = worldRenderGraph->AddNode("Debug Draw", { "Scene" }, {"G-Buffer"}); + auto debugDrawSubpass = debugDrawNode->AddSubpass("Debug Draw"); + debugDrawSubpass->AddColorOutput("Scene"); + // Generate world render graph worldRenderGraph->Generate(); @@ -223,6 +231,7 @@ namespace SHADE worldRenderer->SetCameraDirector(cameraSystem->CreateDirector()); + // Create default material auto cubeVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl"); auto cubeFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl"); From e2778da955286eb97ba4dbd24d6c492caf895abe Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Tue, 1 Nov 2022 20:54:51 +0800 Subject: [PATCH 030/113] WIP Rewrite flow from compiling to loading to recognising assets --- Dependencies.bat | 15 +++++----- Dependencies.lua | 2 +- SHADE_Engine/premake5.lua | 20 +++++++------- SHADE_Engine/src/Assets/SHAssetMacros.h | 3 ++ SHADE_Engine/src/Assets/SHAssetManager.cpp | 32 +++++++++++----------- SHADE_Engine/src/Assets/SHAssetManager.h | 1 + premake5.lua | 1 + 7 files changed, 40 insertions(+), 34 deletions(-) diff --git a/Dependencies.bat b/Dependencies.bat index 1d4a3473..dcd4750e 100644 --- a/Dependencies.bat +++ b/Dependencies.bat @@ -7,7 +7,7 @@ echo "SHADE DEPENDENCIES (Default - All in 10 Seconds)" echo "A - All" echo "B - VMA" echo "C - msdf" -echo "D - assimp" +echo "D - ModelCompiler" echo "E - spdlog" echo "F - reactphysics3d" echo "G - imgui" @@ -29,7 +29,7 @@ set _e=%ERRORLEVEL% if %_e%==1 goto VMA if %_e%==2 goto VMA if %_e%==3 goto MSDF -if %_e%==4 goto assimp +if %_e%==4 goto ModelCompiler if %_e%==5 goto spdlog if %_e%==6 goto reactphysics3d if %_e%==7 goto imgui @@ -53,12 +53,13 @@ if %_e%==2 (goto :done) else (goto :MSDF) echo -----------------------MSDF---------------------------- rmdir "Dependencies/msdf" /S /Q git clone --recurse-submodules https://github.com/SHADE-DP/msdf-atlas-gen.git "Dependencies/msdf" -if %_e%==3 (goto :done) else (goto :assimp) +if %_e%==3 (goto :done) else (goto :ModelCompiler) -:assimp -echo -----------------------assimp---------------------------- -rmdir "Dependencies/assimp" /S /Q -git clone https://github.com/SHADE-DP/assimp.git "Dependencies/assimp" +:ModelCompiler +echo -----------------------ModelCompiler---------------------------- +rmdir "Dependencies/ModelCompiler" /S /Q +git clone https://github.com/SHADE-DP/ModelCompiler.git "Dependencies/ModelCompiler" +Dependencies/ModelCompiler/Dependencies.bat if %_e%==4 (goto :done) else (goto :spdlog) @REM :ktx diff --git a/Dependencies.lua b/Dependencies.lua index 6dd7a711..2e24222b 100644 --- a/Dependencies.lua +++ b/Dependencies.lua @@ -1,5 +1,5 @@ IncludeDir = {} -IncludeDir["assimp"] = "%{wks.location}\\Dependencies\\assimp" +IncludeDir["ModelCompiler"] = "%{wks.location}\\Dependencies\\ModelCompiler" IncludeDir["imgui"] = "%{wks.location}\\Dependencies\\imgui" IncludeDir["imguizmo"] = "%{wks.location}\\Dependencies\\imguizmo" IncludeDir["imnodes"] = "%{wks.location}\\Dependencies\\imnodes" diff --git a/SHADE_Engine/premake5.lua b/SHADE_Engine/premake5.lua index bc1f6a03..06fd7009 100644 --- a/SHADE_Engine/premake5.lua +++ b/SHADE_Engine/premake5.lua @@ -26,7 +26,6 @@ project "SHADE_Engine" externalincludedirs { - "%{IncludeDir.assimp}\\include", "%{IncludeDir.imgui}", "%{IncludeDir.imguizmo}", "%{IncludeDir.imnodes}", @@ -52,8 +51,6 @@ project "SHADE_Engine" { "%{prj.location}/libs", "%{IncludeDir.VULKAN}/Lib", - "%{IncludeDir.assimp}/lib/Debug", - "%{IncludeDir.assimp}/lib/Release", "%{IncludeDir.RTTR}/lib", "%{IncludeDir.SDL}/lib", "%{IncludeDir.spdlog}/lib", @@ -119,7 +116,7 @@ project "SHADE_Engine" filter "configurations:Debug" postbuildcommands { - "xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Debug\\assimp-vc142-mtd.dll\" \"$(OutDir)\"", + --"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Debug\\assimp-vc142-mtd.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodL.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudioL.dll\" \"$(OutDir)\"" } @@ -127,7 +124,7 @@ project "SHADE_Engine" filter "configurations:Release" postbuildcommands { - "xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"", + --"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmod.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudio.dll\" \"$(OutDir)\"" } @@ -135,32 +132,35 @@ project "SHADE_Engine" filter "configurations:Publish" postbuildcommands { - "xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"", + --"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmod.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudio.dll\" \"$(OutDir)\"" } filter "configurations:Publish" - postbuildcommands {"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\""} + postbuildcommands + { + --"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"" + } warnings 'Extra' filter "configurations:Debug" symbols "On" defines {"_DEBUG", "SHEDITOR"} - links{"assimp-vc142-mtd.lib", "librttr_core_d.lib", "spdlogd.lib"} + links{"librttr_core_d.lib", "spdlogd.lib"} links{"fmodstudioL_vc.lib", "fmodL_vc.lib"} filter "configurations:Release" optimize "On" defines{"_RELEASE", "SHEDITOR"} - links{"assimp-vc142-mt.lib", "librttr_core.lib", "spdlog.lib"} + links{"librttr_core.lib", "spdlog.lib"} links{"fmodstudio_vc.lib", "fmod_vc.lib"} filter "configurations:Publish" optimize "On" defines{"_RELEASE", "_PUBLISH"} - links{"assimp-vc142-mt.lib", "librttr_core.lib", "spdlog.lib"} + links{"librttr_core.lib", "spdlog.lib"} excludes { -- "%{prj.location}/src/Editor/**.cpp", diff --git a/SHADE_Engine/src/Assets/SHAssetMacros.h b/SHADE_Engine/src/Assets/SHAssetMacros.h index a21a9840..52e5cb80 100644 --- a/SHADE_Engine/src/Assets/SHAssetMacros.h +++ b/SHADE_Engine/src/Assets/SHAssetMacros.h @@ -63,6 +63,9 @@ constexpr std::string_view ASSET_ROOT {"../../Assets"}; constexpr std::string_view BUILT_IN_ASSET_ROOT{ "../../Built_In" }; #endif +// COMPILER PATHS +constexpr std::string_view MODEL_COMPILER_EXE{ "ModelCompiler.exe" }; + // INTERNAL ASSET PATHS constexpr std::string_view SCENE_FOLDER{ "/Scenes/" }; constexpr std::string_view PREFAB_FOLDER{ "/Prefabs/" }; diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index 682eb9ec..29e00075 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -11,6 +11,8 @@ #include #include #include +#include + #include "SHAssetManager.h" #include "SHAssetMetaHandler.h" @@ -19,7 +21,7 @@ #include "Libraries/Loaders/SHShaderSourceLoader.h" #include "Libraries/Loaders/SHTextBasedLoader.h" -#include "Libraries/Compilers/SHMeshCompiler.h" +//#include "Libraries/Compilers/SHMeshCompiler.h" #include "Libraries/Compilers/SHTextureCompiler.h" #include "Libraries/Compilers/SHShaderSourceCompiler.h" @@ -413,22 +415,15 @@ namespace SHADE } else if (ext == GLTF_EXTENSION.data() || ext == FBX_EXTENSION.data()) { - std::vector meshes; - std::vector anims; - SHMeshCompiler::LoadFromFile(path, meshes, anims); + std::string command = MODEL_COMPILER_EXE.data(); + command += " " + path.string(); + std::system(command.c_str()); - for (auto const& mesh : meshes) - { - SHAsset meshAsset{ - .name = mesh->header.name - }; - meshAsset.path = SHMeshCompiler::CompileMeshBinary(*mesh, path).value(); - meshAsset.id = GenerateAssetID(AssetType::MESH); - meshAsset.type = AssetType::MESH; - assetCollection.insert({ meshAsset.id, meshAsset }); - SHAssetMetaHandler::WriteMetaData(meshAsset); - } - continue; + std::string newPath = path.string().substr(0, path.string().find_last_of('.')); + newPath += MESH_EXTENSION; + newAsset.path = newPath; + newAsset.id = GenerateAssetID(AssetType::MESH); + newAsset.type = AssetType::MESH; } assetCollection.insert({ newAsset.id, newAsset }); @@ -492,6 +487,11 @@ namespace SHADE return data; } + void SHAssetManager::LoadDataFromScratch(AssetPath path) noexcept + { + + } + void SHAssetManager::BuildAssetCollection() noexcept { SHFileSystem::BuildDirectory(ASSET_ROOT.data(), folderRoot, assetCollection); diff --git a/SHADE_Engine/src/Assets/SHAssetManager.h b/SHADE_Engine/src/Assets/SHAssetManager.h index 64527e01..4daeb536 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.h +++ b/SHADE_Engine/src/Assets/SHAssetManager.h @@ -96,6 +96,7 @@ namespace SHADE static void InitLoaders() noexcept; static void LoadAllData() noexcept; static SHAssetData* LoadData(SHAsset const& asset) noexcept; + static void LoadDataFromScratch(AssetPath path) noexcept; inline static void BuildAssetCollection() noexcept; static bool IsRecognised(char const*) noexcept; diff --git a/premake5.lua b/premake5.lua index 35bff9d2..2164d649 100644 --- a/premake5.lua +++ b/premake5.lua @@ -30,4 +30,5 @@ workspace "SHADE" --include "Dependencies/tracy" include "Dependencies/yamlcpp" include "Dependencies/reactphysics3d" + include "Dependencies/ModelCompiler" group "" From 129f92e4b66f18dbc8e17e92e014ed8bdf5aa1de Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Tue, 1 Nov 2022 21:49:42 +0800 Subject: [PATCH 031/113] mat inspector properties handling (WIP) --- .../MaterialInspector/SHMaterialInspector.cpp | 106 ++++++++++++------ .../MaterialInspector/SHMaterialInspector.h | 12 +- .../MiddleEnd/Materials/SHMaterialSpec.h | 1 + 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp index c4904533..775754d7 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp @@ -3,22 +3,22 @@ #include "Editor/SHImGuiHelpers.hpp" #include -//#include "Assets/SHAssetManager.h" -//#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" -//#include "Editor/SHEditorWidgets.hpp" -//#include "Resource/SHResourceManager.h" +#include "Assets/SHAssetManager.h" +#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" +#include "Editor/SHEditorWidgets.hpp" +#include "Resource/SHResourceManager.h" namespace SHADE { SHMaterialInspector::SHMaterialInspector() - :SHEditorWindow("Material Inspector", ImGuiWindowFlags_MenuBar)//,isNewMaterial(false), currentViewedMaterial(0) + :SHEditorWindow("Material Inspector", ImGuiWindowFlags_MenuBar), isNewMaterial(false), currentViewedMaterial(0) { } void SHMaterialInspector::OpenMaterial(AssetID const& assetId) noexcept { //Get mat data - //isNewMaterial = false; + isNewMaterial = false; } void SHMaterialInspector::Init() @@ -30,30 +30,20 @@ namespace SHADE { SHEditorWindow::Update(); - //if(Begin()) - //{ - // DrawMenuBar(); + if(Begin()) + { + DrawMenuBar(); - // if(SHEditorWidgets::DragDropReadOnlyField("Vertex Shader", std::to_string(currentMatSpec.vertexShader), [&](){return currentMatSpec.vertexShader;}, [&](AssetID const& id){currentMatSpec.vertexShader = id;}, SHDragDrop::DRAG_RESOURCE)) - // { - // vertShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec.vertexShader); - // } - // if(SHEditorWidgets::DragDropReadOnlyField("Fragment Shader", std::to_string(currentMatSpec.fragShader), [&](){return currentMatSpec.fragShader;}, [&](AssetID const& id){currentMatSpec.fragShader = id;}, SHDragDrop::DRAG_RESOURCE)) - // { - // fragShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec.fragShader); - // } - - // auto vertInterface = vertShaderHandle->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA); - - // int const varCount = static_cast(vertInterface->GetVariableCount()); - // - // for(int i = 0; i < varCount; ++i) - // { - // auto variable = vertInterface->GetVariable(i); - // } - - //} - //ImGui::End(); + if(SHEditorWidgets::DragDropReadOnlyField("Vertex Shader", std::to_string(currentMatSpec.vertexShader), [&](){return currentMatSpec.vertexShader;}, [&](AssetID const& id){currentMatSpec.vertexShader = id;}, SHDragDrop::DRAG_RESOURCE)) + { + vertShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec.vertexShader); + } + if(SHEditorWidgets::DragDropReadOnlyField("Fragment Shader", std::to_string(currentMatSpec.fragShader), [&](){return currentMatSpec.fragShader;}, [&](AssetID const& id){currentMatSpec.fragShader = id;}, SHDragDrop::DRAG_RESOURCE)) + { + fragShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec.fragShader); + } + } + ImGui::End(); } void SHMaterialInspector::Exit() @@ -63,12 +53,12 @@ namespace SHADE void SHMaterialInspector::CreateNewMaterial() { - //isNewMaterial = true; - ////prompt for a name - //currentViewedMaterial = SHAssetManager::CreateNewAsset(AssetType::MATERIAL, "NewMaterial"); - //currentMatSpec = {}; - //vertShaderHandle = {}; - //fragShaderHandle = {}; + isNewMaterial = true; + //prompt for a name + currentViewedMaterial = SHAssetManager::CreateNewAsset(AssetType::MATERIAL, "NewMaterial"); + currentMatSpec = {}; + vertShaderHandle = {}; + fragShaderHandle = {}; } void SHMaterialInspector::DrawMenuBar() @@ -78,4 +68,50 @@ namespace SHADE ImGui::EndMenuBar(); } } + + void SHMaterialInspector::DrawShaderProperties(Handle shaderModule) + { + auto interface = shaderModule->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA); + + int const varCount = static_cast(interface->GetVariableCount()); + + for(int i = 0; i < varCount; ++i) + { + auto variable = interface->GetVariable(i); + const std::string& VAR_NAME = interface->GetVariableName(i); + switch (variable->type) + { + case SHShaderBlockInterface::Variable::Type::FLOAT: + SHEditorWidgets::DragFloat(VAR_NAME, [&]() + { + if(currentMatSpec.properties[VAR_NAME].IsDefined()) + return currentMatSpec.properties[VAR_NAME].as(); + else + return 0.0f; + }, + [&](float const& value) + { + currentMatSpec.properties[VAR_NAME] = value; + } + ); + break; + case SHShaderBlockInterface::Variable::Type::INT: + + break; + case SHShaderBlockInterface::Variable::Type::VECTOR2: + + break; + case SHShaderBlockInterface::Variable::Type::VECTOR3: + + break; + case SHShaderBlockInterface::Variable::Type::VECTOR4: + + break; + case SHShaderBlockInterface::Variable::Type::OTHER: + default: + continue; + break; + } + } + } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h index 7256a0b6..20e165a7 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h +++ b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h @@ -1,6 +1,9 @@ #pragma once #include "Assets/SHAssetMacros.h" #include "Editor/EditorWindow/SHEditorWindow.h" +#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" +#include "Graphics/Shaders/SHVkShaderModule.h" +#include "Resource/SHHandle.h" namespace SHADE @@ -19,10 +22,11 @@ namespace SHADE void OpenMaterial(AssetID const& assetId) noexcept; private: void DrawMenuBar(); + void DrawShaderProperties(Handle shaderModule); - //bool isNewMaterial; - //AssetID currentViewedMaterial; - //SHMaterialSpec currentMatSpec; - //Handle vertShaderHandle, fragShaderHandle; + bool isNewMaterial; + AssetID currentViewedMaterial; + SHMaterialSpec currentMatSpec; + Handle vertShaderHandle, fragShaderHandle; }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h index 338c34b2..e41436ce 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h @@ -16,6 +16,7 @@ of DigiPen Institute of Technology is prohibited. #include // Project Includes #include "Assets/SHAssetMacros.h" +#include namespace SHADE { From dc8ed48be329690d2bb126cc43d9a6ad94095095 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 1 Nov 2022 21:49:57 +0800 Subject: [PATCH 032/113] Fixed debug draw system cube rendering --- .../src/Application/SBApplication.cpp | 5 +-- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 31 +++++++++---------- .../MiddleEnd/Interface/SHDebugDrawSystem.hpp | 6 ++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 54dcd094..4f03efc0 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -139,9 +139,10 @@ namespace Sandbox SHInputManager::UpdateInput(SHFrameRateController::GetRawDeltaTime()); auto debugDraw = SHSystemManager::GetSystem(); - debugDraw->DrawLine(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 0.0f, 0.0f), SHVec3(5.0f, 0.0f, 0.0f)); - debugDraw->DrawLine(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 1.0f, 0.0f), SHVec3(5.0f, 1.0f, 0.0f)); + /*debugDraw->DrawLine(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 0.0f, 0.0f), SHVec3(5.0f, 0.0f, 0.0f)); + debugDraw->DrawLine(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 1.0f, 0.0f), SHVec3(5.0f, 1.0f, 0.0f));*/ //debugDraw->DrawCube(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, SHVec3(1.0f, 1.0f, 1.0f)); + debugDraw->DrawSphere(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, 3.0f); SHSceneManager::UpdateSceneManager(); #ifdef SHEDITOR if(editor->editorState == SHEditor::State::PLAY) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index d54a8dad..08019665 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -145,14 +145,14 @@ namespace SHADE void SHDebugDrawSystem::DrawCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) { static const SHVec3 EXTENTS = SHVec3 { 0.5f, 0.5f, 0.5f }; - static const SHVec3 UNIT_BOT_LEFT_BACK = SHVec3 { pos - EXTENTS }; - static const SHVec3 UNIT_BOT_RIGHT_BACK = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, 0.0f } }; - static const SHVec3 UNIT_BOT_LEFT_FRONT = SHVec3 { pos + SHVec3 { 0.0f , -EXTENTS.y, EXTENTS.z } }; - static const SHVec3 UNIT_BOT_RIGHT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; - static const SHVec3 UNIT_TOP_LEFT_BACK = SHVec3 { pos + SHVec3 { EXTENTS.x, EXTENTS.y, 0.0f } }; - static const SHVec3 UNIT_TOP_RIGHT_BACK = SHVec3 { pos + SHVec3 { 0.0f , EXTENTS.y, EXTENTS.z } }; - static const SHVec3 UNIT_TOP_LEFT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, EXTENTS.y, EXTENTS.z } }; - static const SHVec3 UNIT_TOP_RIGHT_FRONT = SHVec3 { pos + EXTENTS }; + static const SHVec3 UNIT_BOT_LEFT_FRONT = SHVec3 { pos - EXTENTS }; + static const SHVec3 UNIT_BOT_RIGHT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_BOT_RIGHT_BACK = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_BOT_LEFT_BACK = SHVec3 { pos + SHVec3 { -EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_LEFT_BACK = SHVec3 { pos + SHVec3 { -EXTENTS.x, EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_RIGHT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_TOP_LEFT_FRONT = SHVec3 { pos + SHVec3 { -EXTENTS.x, EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_TOP_RIGHT_BACK = SHVec3 { pos + EXTENTS }; const SHVec3 BOT_LEFT_BACK = UNIT_BOT_LEFT_BACK * size; const SHVec3 BOT_RIGHT_BACK = UNIT_BOT_RIGHT_BACK * size; @@ -172,17 +172,16 @@ namespace SHADE BOT_RIGHT_BACK , BOT_RIGHT_FRONT, BOT_RIGHT_FRONT, BOT_LEFT_FRONT, BOT_LEFT_FRONT , BOT_LEFT_BACK, - // Middle Lines - TOP_LEFT_BACK , BOT_LEFT_BACK, - TOP_RIGHT_BACK , BOT_RIGHT_BACK, - TOP_RIGHT_FRONT, BOT_RIGHT_FRONT, - TOP_LEFT_FRONT , BOT_LEFT_FRONT, // Top Square TOP_LEFT_BACK , TOP_RIGHT_BACK, TOP_RIGHT_BACK , TOP_RIGHT_FRONT, TOP_RIGHT_FRONT, TOP_LEFT_FRONT, - TOP_LEFT_FRONT , TOP_LEFT_BACK - + TOP_LEFT_FRONT , TOP_LEFT_BACK, + // Middle Lines + TOP_LEFT_BACK , BOT_LEFT_BACK, + TOP_RIGHT_BACK , BOT_RIGHT_BACK, + TOP_RIGHT_FRONT, BOT_RIGHT_FRONT, + TOP_LEFT_FRONT , BOT_LEFT_FRONT } ); } @@ -192,7 +191,7 @@ namespace SHADE if (spherePoints.empty()) { // Generate - const SHMeshData SPHERE = SHPrimitiveGenerator::Sphere(); + static const SHMeshData SPHERE = SHPrimitiveGenerator::Sphere(); for (const auto& idx : SPHERE.Indices) { spherePoints.emplace_back(SPHERE.VertexPositions[idx]); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp index a913ecfd..1fd90f38 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp @@ -39,10 +39,10 @@ namespace SHADE return; } - for (auto pointIter = pointListBegin + 1; pointIter != pointListEnd; ++pointIter) + const size_t POINTS_ROUNDED_COUNT = POINTS_COUNT / 2 * 2; + for (auto pointIter = pointListBegin; pointIter != (pointListBegin + POINTS_ROUNDED_COUNT); ++pointIter) { - points.emplace_back(PointVertex { *(pointIter - 1), color }); - points.emplace_back(PointVertex { *pointIter , color }); + points.emplace_back(PointVertex { *pointIter, color }); } } } \ No newline at end of file From 97a39d62c6ab55d5655ef4a0c4d06721b204c66b Mon Sep 17 00:00:00 2001 From: Glence Date: Tue, 1 Nov 2022 23:28:31 +0800 Subject: [PATCH 033/113] player controller done for now --- TempScriptsFolder/PlayerController.cs | 85 ++++++++++++++++++--------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 98c3d3b5..0d374bae 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -10,7 +10,7 @@ public class PlayerController : Script IDILE, WALKING, RUNNING, - INAIR, + JUMP, FALLING, HOLDING, CAUGHT, @@ -19,26 +19,47 @@ public class PlayerController : Script private RigidBody rb; private Transform tranform; - private int xAxisMove; - private int zAxisMove; + + //to be remove public float drag = 2.0f; - private bool isMoveKeyPress = false; + [SerializeField] + [Tooltip("The current state fo the raccoon")] public RaccoonStates currentState = RaccoonStates.IDILE; + //Movement variables============================================================ + [SerializeField] + [Tooltip("Max vel for walking")] public float maxMoveVel = 2.0f; + [SerializeField] + [Tooltip("how much force is apply for walking")] public float moveForce = 50.0f; + [SerializeField] + [Tooltip("increase the moveForce and maxMoveVel by its amt")] public float sprintMultiplier = 2.0f; + private float oldForce; private float maxOldVel; private bool sprintIncreaseOnce = false; + private int xAxisMove; + private int zAxisMove; + private bool isMoveKeyPress = false; + + [SerializeField] + [Tooltip("curr not working")] public float rotationFactorPerFrame = 1.0f; - - public float initialJumpVel; - public float maxJumpHeight = 1.0f; - public float maxJumpTime = 0.5f; - private bool isJumping = false; - public bool isGrounded = true; + //Jumping vars================================================================== + [SerializeField] + [Tooltip("max height of the jump")] + public float maxJumpHeight = 4.0f; + [SerializeField] + [Tooltip("max amt of time it will take for the jump")] + public float maxJumpTime = 0.75f; + [SerializeField] + [Tooltip("increase gravity when falling")] + public float fallMultipler = 2.0f; + private float initialJumpVel; + private bool isGrounded = true; private float gravity = -9.8f; private float groundGravity = -0.5f; @@ -80,7 +101,7 @@ public class PlayerController : Script MoveKey(); - Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); + //Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); } protected override void fixedUpdate() @@ -111,10 +132,10 @@ public class PlayerController : Script isMoveKeyPress = xAxisMove != 0 || zAxisMove != 0; - if(isMoveKeyPress && currentState != RaccoonStates.RUNNING) + if(isMoveKeyPress && currentState != RaccoonStates.RUNNING && isGrounded) currentState = RaccoonStates.WALKING; - if (!isMoveKeyPress) + if (!isMoveKeyPress && isGrounded) currentState = RaccoonStates.IDILE; } @@ -144,7 +165,7 @@ public class PlayerController : Script private void Sprint() { - if (Input.GetKey(Input.KeyCode.LeftShift) && isMoveKeyPress) + if (Input.GetKey(Input.KeyCode.LeftShift) && isMoveKeyPress && isGrounded) { currentState = RaccoonStates.RUNNING; if(!sprintIncreaseOnce) @@ -158,7 +179,7 @@ public class PlayerController : Script } } - if (Input.GetKeyUp(Input.KeyCode.LeftShift) && (currentState == RaccoonStates.RUNNING || currentState == RaccoonStates.IDILE)) + if (Input.GetKeyUp(Input.KeyCode.LeftShift)) { if(isMoveKeyPress) currentState = RaccoonStates.WALKING; @@ -173,23 +194,17 @@ public class PlayerController : Script { if (currentState == RaccoonStates.WALKING || currentState == RaccoonStates.RUNNING || currentState == RaccoonStates.IDILE) { - if (Input.GetKeyDown(Input.KeyCode.Space)&& isGrounded && rb != null) + if (Input.GetKeyDown(Input.KeyCode.Space) && isGrounded && rb != null) { - isJumping = true; + currentState = RaccoonStates.JUMP; Vector3 v = rb.LinearVelocity; - v.y = initialJumpVel; + v.y = initialJumpVel * 0.5f; rb.LinearVelocity = v; } } - if (rb != null) - { - if (rb.LinearVelocity.y > 0 && isJumping) - currentState = RaccoonStates.INAIR; - else if(rb.LinearVelocity.y < 0 && isJumping) - currentState = RaccoonStates.FALLING; - - } + if(rb != null && !isGrounded && (rb.LinearVelocity.y < 0.0f || Input.GetKeyUp(Input.KeyCode.Space))) + currentState = RaccoonStates.FALLING; } private void Rotation() @@ -215,7 +230,7 @@ public class PlayerController : Script if (rb != null) { //check player vel.y if its close to zero its on the ground - if (SHADE.Math.CompareFloat(rb.LinearVelocity.y, 0.0f, 0.1f)) + if (SHADE.Math.CompareFloat(rb.LinearVelocity.y, 0.0f)) isGrounded = true; else isGrounded = false; @@ -224,8 +239,20 @@ public class PlayerController : Script if (isGrounded) v.y = groundGravity; - else - v.y += gravity * (float)Time.DeltaTime; + else if (currentState == RaccoonStates.FALLING) + { + float prevYVel = v.y; + float newYVel = v.y + (gravity * fallMultipler * (float)Time.DeltaTime); + float nextYVel = (prevYVel + newYVel) * 0.5f; + v.y = nextYVel; + } + else + { + float prevYVel = v.y; + float newYVel = v.y + (gravity * (float)Time.DeltaTime); + float nextYVel = (prevYVel + newYVel) * 0.5f; + v.y = nextYVel; + } rb.LinearVelocity = v; From 4eef6143dcfcb71ea6f38a7527e29bf139d99049 Mon Sep 17 00:00:00 2001 From: Glence Date: Wed, 2 Nov 2022 00:25:43 +0800 Subject: [PATCH 034/113] base for pick and throw --- TempScriptsFolder/PickAndThrow.cs | 30 +++++++++++++++++++++++++++ TempScriptsFolder/PlayerController.cs | 1 + 2 files changed, 31 insertions(+) create mode 100644 TempScriptsFolder/PickAndThrow.cs diff --git a/TempScriptsFolder/PickAndThrow.cs b/TempScriptsFolder/PickAndThrow.cs new file mode 100644 index 00000000..834a508c --- /dev/null +++ b/TempScriptsFolder/PickAndThrow.cs @@ -0,0 +1,30 @@ +using SHADE; +using System; +using static PlayerController; + +public class PickAndThrow : Script +{ + private PlayerController pc; + public uint itemEID; + Transform itemHoldLocation; + public PickAndThrow(GameObject gameObj) : base(gameObj) { } + protected override void awake() + { + pc = GetScript(); + itemHoldLocation = GetComponentInChildren(); + } + protected override void update() + { + if (pc != null && pc.holdItem) + { + } + } + protected override void onCollisionEnter(CollisionInfo info) + { + if (info.GameObject.Name == "item" && Input.GetKey(Input.KeyCode.E)) + { + pc.holdItem = true; + itemEID = info.GameObject.EntityId; + } + } +} \ No newline at end of file diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 0d374bae..36e19860 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -22,6 +22,7 @@ public class PlayerController : Script //to be remove public float drag = 2.0f; + public bool holdItem = false; [SerializeField] [Tooltip("The current state fo the raccoon")] public RaccoonStates currentState = RaccoonStates.IDILE; From 5f7b28e8a0c9a137bbe2de82186d56e64490a8f9 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 01:11:28 +0800 Subject: [PATCH 035/113] Added SHDebugDraw convenience static class for debug drawing --- .../src/Application/SBApplication.cpp | 10 +- SHADE_Application/src/Scenes/SBTestScene.cpp | 3 + .../MiddleEnd/Interface/SHDebugDrawSystem.h | 98 ++++++++++++----- .../MiddleEnd/Interface/SHDebugDrawSystem.hpp | 20 ++-- .../MiddleEnd/Meshes/SHPrimitiveGenerator.cpp | 4 +- SHADE_Engine/src/Tools/SHDebugDraw.cpp | 73 +++++++++++++ SHADE_Engine/src/Tools/SHDebugDraw.h | 101 ++++++++++++++++++ 7 files changed, 267 insertions(+), 42 deletions(-) create mode 100644 SHADE_Engine/src/Tools/SHDebugDraw.cpp create mode 100644 SHADE_Engine/src/Tools/SHDebugDraw.h diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 4f03efc0..b865f028 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -43,6 +43,7 @@ #include "Assets/SHAssetManager.h" #include "Tools/SHLogger.h" +#include "Tools/SHDebugDraw.h" using namespace SHADE; @@ -126,6 +127,9 @@ namespace Sandbox SHSceneManager::InitSceneManager("TestScene"); SHFrameRateController::UpdateFRC(); + + // Link up SHDebugDraw + SHDebugDraw::Init(SHSystemManager::GetSystem()); } void SBApplication::Update(void) @@ -137,12 +141,6 @@ namespace Sandbox { SHFrameRateController::UpdateFRC(); SHInputManager::UpdateInput(SHFrameRateController::GetRawDeltaTime()); - - auto debugDraw = SHSystemManager::GetSystem(); - /*debugDraw->DrawLine(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 0.0f, 0.0f), SHVec3(5.0f, 0.0f, 0.0f)); - debugDraw->DrawLine(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 1.0f, 0.0f), SHVec3(5.0f, 1.0f, 0.0f));*/ - //debugDraw->DrawCube(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, SHVec3(1.0f, 1.0f, 1.0f)); - debugDraw->DrawSphere(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, 3.0f); SHSceneManager::UpdateSceneManager(); #ifdef SHEDITOR if(editor->editorState == SHEditor::State::PLAY) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index 5b53eef9..a5b3b546 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -19,6 +19,7 @@ #include "Math/SHColour.h" #include "Resource/SHResourceManager.h" #include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" +#include "Tools/SHDebugDraw.h" using namespace SHADE; @@ -188,6 +189,8 @@ namespace Sandbox SHADE::SHScriptEngine* scriptEngine = static_cast(SHADE::SHSystemManager::GetSystem()); scriptEngine->RemoveAllScripts(testObj); } + + SHDebugDraw::Cube(SHColour::CRIMSON, SHVec3(1.0f, 0.0f, 0.0f), SHVec3(1.0f, 1.0f, 1.0f)); } void SBTestScene::Render() diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index 1bbf3ab8..4b83958d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -23,29 +23,27 @@ of DigiPen Institute of Technology is prohibited. #include "Resource/SHHandle.h" #include "Graphics/Buffers/SHVkBuffer.h" #include "SHGraphicsConstants.h" +#include "Math/SHColour.h" namespace SHADE { - /*---------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*---------------------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ class SHVkBuffer; - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - /***********************************************************************************/ - /*! - \brief - Manages the Debug Draw system. - */ - /***********************************************************************************/ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + /// + /// Manages the Debug Draw system. + /// class SH_API SHDebugDrawSystem final : public SHSystem { public: - /*-------------------------------------------------------------------------------*/ - /* System Routines */ - /*-------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + /* System Routines */ + /*---------------------------------------------------------------------------------*/ class SH_API ProcessPointsRoutine final : public SHSystemRoutine { public: @@ -53,27 +51,79 @@ namespace SHADE virtual void Execute(double dt) noexcept override final; }; - /*-------------------------------------------------------------------------------*/ - /* SHSystem overrides */ - /*-------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + /* SHSystem overrides */ + /*---------------------------------------------------------------------------------*/ virtual void Init() override final; virtual void Exit() override final; - /*-------------------------------------------------------------------------------*/ - /* Configuration Functions */ - /*-------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + /* Configuration Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Configures the line width used to draw all lines in the Debug Draw system. + /// float LineWidth = 1.0f; - /*-------------------------------------------------------------------------------*/ - /* Draw Functions */ - /*-------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Renders a line between two points in world space. + /// + /// Colour of the line. + /// First point of the line. + /// Second point of the line. void DrawLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + /// + /// Renders a triangle indicated by three points in world space. + /// + /// Colour of the triangle. + /// First point of the triangle. + /// Second point of the triangle. + /// Third point of the triangle. void DrawTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3); + /// + /// Renders a quadrilateral indicated by four points in world space. + /// + /// Colour of the quadrilateral. + /// First point of the triangle. + /// Second point of the quadrilateral. + /// Third point of the quadrilateral. + /// Third point of the quadrilateral. void DrawQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4); + /// + /// Renders a polygon indicated by the specified set of points in world space. + /// + /// Colour of the polygon. + /// List of points for the polygon. void DrawPoly(const SHVec4& color, std::initializer_list pointList); + /// + /// Renders a polygon indicated by the specified set of points in world space. + /// + /// Iterator for a STL-like container. + /// Colour of the polygon. + /// + /// Iterator to the first point of the point container. + /// + /// + /// One past last iterator of the point container. + /// template void DrawPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd); + /// + /// Renders a wireframe cube centered around the position specified in world space. + /// + /// Colour of the cube. + /// Position where the cube wil be centered at. + /// Size of the rendered cube. void DrawCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size); + /// + /// Renders a wireframe sphere centered around the position specified in world space. + /// + /// Colour of the sphere. + /// Position where the sphere wil be centered at. + /// Size of the rendered sphere. void DrawSphere(const SHVec4& color, const SHVec3& pos, double radius); private: diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp index 1fd90f38..14fbb42f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp @@ -3,7 +3,7 @@ \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Oct 16, 2022 -\brief Contains the definition of template functions the SHDebugDrawSystem +\brief Contains the definition of template functions the SHDebugDrawSystem class. Copyright (C) 2022 DigiPen Institute of Technology. @@ -15,11 +15,11 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { - /*---------------------------------------------------------------------------------*/ - /* Template Functions */ - /*---------------------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*-----------------------------------------------------------------------------------*/ template - void SHADE::SHDebugDrawSystem::DrawPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd) + void SHDebugDrawSystem::DrawPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd) { // Ensure dereferenced type is SHVec3 static_assert(std::is_same_v>, "Parameters to DrawPoly must be SHVec3."); @@ -27,22 +27,22 @@ namespace SHADE // Check if points exceeded max if (points.size() > MAX_POINTS) { - SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); - return; + SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); + return; } const size_t POINTS_COUNT = pointListEnd - pointListBegin; // Invalid polygon if (POINTS_COUNT < 2) { - SHLOG_WARNING("[SHDebugDraw] Invalid polygon provided to DrawPoly()."); - return; + SHLOG_WARNING("[SHDebugDraw] Invalid polygon provided to DrawPoly()."); + return; } const size_t POINTS_ROUNDED_COUNT = POINTS_COUNT / 2 * 2; for (auto pointIter = pointListBegin; pointIter != (pointListBegin + POINTS_ROUNDED_COUNT); ++pointIter) { - points.emplace_back(PointVertex { *pointIter, color }); + points.emplace_back(PointVertex{ *pointIter, color }); } } } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp index 6c708e58..c9a3f6c5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp @@ -218,8 +218,8 @@ namespace SHADE { SHMeshData meshData; - const int LAT_SLICES = 8; - const int LONG_SLICES = 8; + const int LAT_SLICES = 12; + const int LONG_SLICES = 12; float radius = 1; for (int latNum = 0; latNum <= LAT_SLICES; ++latNum) { diff --git a/SHADE_Engine/src/Tools/SHDebugDraw.cpp b/SHADE_Engine/src/Tools/SHDebugDraw.cpp new file mode 100644 index 00000000..a5b86c42 --- /dev/null +++ b/SHADE_Engine/src/Tools/SHDebugDraw.cpp @@ -0,0 +1,73 @@ +/************************************************************************************//*! +\file SHDebugDrawSystem.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 2, 2022 +\brief Contains the definition of functions of the SHDebugDrawSystem 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. +*//*************************************************************************************/ +// Precompiled Header +#include "SHpch.h" +// Primary Include +#include "SHDebugDraw.h" +// Project Includes +#include "Math/Vector/SHVec4.h" +#include "Math/SHColour.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" +#include "ECS_Base/Managers/SHSystemManager.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Static Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + SHDebugDrawSystem* SHDebugDraw::dbgDrawSys = nullptr; + + /*-----------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*-----------------------------------------------------------------------------------*/ + void SHDebugDraw::Init(SHDebugDrawSystem* sys) + { + dbgDrawSys = sys; + if (dbgDrawSys == nullptr) + { + SHLOG_ERROR("[SHDebugDraw] Invalid SHDebugDrawSystem provided for initialization!"); + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*-----------------------------------------------------------------------------------*/ + void SHDebugDraw::Line(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) + { + dbgDrawSys->DrawLine(color, startPt, endPt); + } + + void SHDebugDraw::Tri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3) + { + dbgDrawSys->DrawTri(color, pt1, pt2, pt3); + } + + void SHDebugDraw::Quad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4) + { + dbgDrawSys->DrawQuad(color, pt1, pt2, pt3, pt4); + } + + void SHDebugDraw::Poly(const SHVec4& color, std::initializer_list pointList) + { + dbgDrawSys->DrawPoly(color, pointList); + } + + void SHDebugDraw::Cube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) + { + dbgDrawSys->DrawCube(color, pos, size); + } + + void SHDebugDraw::Sphere(const SHVec4& color, const SHVec3& pos, double radius) + { + dbgDrawSys->DrawSphere(color, pos, radius); + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Tools/SHDebugDraw.h b/SHADE_Engine/src/Tools/SHDebugDraw.h new file mode 100644 index 00000000..7ce44ec2 --- /dev/null +++ b/SHADE_Engine/src/Tools/SHDebugDraw.h @@ -0,0 +1,101 @@ +/************************************************************************************//*! +\file SHDebugDraw.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 2, 2022 +\brief Contains the definition of the SHDebugDraw 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 + +// Project Includes +#include "SH_API.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + class SHDebugDrawSystem; + class SHVec4; + class SHVec3; + class SHColour; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + /// + /// Static helper class to make it easier to do debug drawing and enable support for + /// managed code to execute it withot dependency hell due to graphics system + /// dependencies. + /// + class SH_API SHDebugDraw final + { + public: + /*---------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Sets up the link with the SHDebugDrawSystem. Must be called after the + /// SHDebugDrawSystem is spawned. + /// + static void Init(SHDebugDrawSystem* sys); + + /*---------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Renders a line between two points in world space. + /// + /// Colour of the line. + /// First point of the line. + /// Second point of the line. + static void Line(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + /// + /// Renders a triangle indicated by three points in world space. + /// + /// Colour of the triangle. + /// First point of the triangle. + /// Second point of the triangle. + /// Third point of the triangle. + static void Tri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3); + /// + /// Renders a quadrilateral indicated by four points in world space. + /// + /// Colour of the quadrilateral. + /// First point of the triangle. + /// Second point of the quadrilateral. + /// Third point of the quadrilateral. + /// Third point of the quadrilateral. + static void Quad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4); + /// + /// Renders a polygon indicated by the specified set of points in world space. + /// + /// Colour of the polygon. + /// List of points for the polygon. + static void Poly(const SHVec4& color, std::initializer_list pointList); + /// + /// Renders a wireframe cube centered around the position specified in world space. + /// + /// Colour of the cube. + /// Position where the cube wil be centered at. + /// Size of the rendered cube. + static void Cube(const SHVec4& color, const SHVec3& pos, const SHVec3& size); + /// + /// Renders a wireframe sphere centered around the position specified in world space. + /// + /// Colour of the sphere. + /// Position where the sphere wil be centered at. + /// Size of the rendered sphere. + static void Sphere(const SHVec4& color, const SHVec3& pos, double radius); + + private: + /*---------------------------------------------------------------------------------*/ + /* Static Data Members */ + /*---------------------------------------------------------------------------------*/ + static SHDebugDrawSystem* dbgDrawSys; + }; +} \ No newline at end of file From 1371302a40602daba1c94f10a4e819521b6fd192 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 09:34:36 +0800 Subject: [PATCH 036/113] Generalised Draw functions in SHDebugDrawSystem for potentially any number of draw queues --- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 89 ++++++++++++------- .../MiddleEnd/Interface/SHDebugDrawSystem.h | 11 +++ .../MiddleEnd/Interface/SHDebugDrawSystem.hpp | 13 ++- 3 files changed, 77 insertions(+), 36 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 08019665..715b7d69 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -117,54 +117,75 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ void SHDebugDrawSystem::DrawLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) { - if (points.size() > MAX_POINTS) + drawLine(points, color, startPt, endPt); + } + + void SHDebugDrawSystem::DrawTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3) + { + drawPoly(points, color, { pt1, pt2, pt3 }); + } + + void SHDebugDrawSystem::DrawQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4) + { + drawPoly(points, color, { pt1, pt2, pt3, pt4 }); + } + + void SHDebugDrawSystem::DrawPoly(const SHVec4& color, std::initializer_list pointList) + { + drawPoly(points, color, pointList.begin(), pointList.end()); + } + + void SHDebugDrawSystem::DrawCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) + { + drawCube(points, color, pos, size); + } + + void SHDebugDrawSystem::DrawSphere(const SHVec4& color, const SHVec3& pos, double radius) + { + drawSphere(points, color, pos, radius); + } + + void SHDebugDrawSystem::drawLine(std::vector& storage, const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) + { + if (storage.size() > MAX_POINTS) { SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); return; } - points.emplace_back(PointVertex{ startPt, color }); - points.emplace_back(PointVertex{ endPt, color }); + storage.emplace_back(PointVertex{ startPt, color }); + storage.emplace_back(PointVertex{ endPt, color }); } - void SHDebugDrawSystem::DrawTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3) + void SHDebugDrawSystem::drawPoly(std::vector& storage, const SHVec4& color, std::initializer_list pointList) { - DrawPoly(color, { pt1, pt2, pt3 }); + drawPoly(storage, color, pointList.begin(), pointList.end()); } - void SHDebugDrawSystem::DrawQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4) + void SHDebugDrawSystem::drawCube(std::vector& storage, const SHVec4& color, const SHVec3& pos, const SHVec3& size) { - DrawPoly(color, { pt1, pt2, pt3, pt4 }); - } + static const SHVec3 EXTENTS = SHVec3{ 0.5f, 0.5f, 0.5f }; + static const SHVec3 UNIT_BOT_LEFT_FRONT = SHVec3{ pos - EXTENTS }; + static const SHVec3 UNIT_BOT_RIGHT_FRONT = SHVec3{ pos + SHVec3 { EXTENTS.x, -EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_BOT_RIGHT_BACK = SHVec3{ pos + SHVec3 { EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_BOT_LEFT_BACK = SHVec3{ pos + SHVec3 { -EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_LEFT_BACK = SHVec3{ pos + SHVec3 { -EXTENTS.x, EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_RIGHT_FRONT = SHVec3{ pos + SHVec3 { EXTENTS.x, EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_TOP_LEFT_FRONT = SHVec3{ pos + SHVec3 { -EXTENTS.x, EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_TOP_RIGHT_BACK = SHVec3{ pos + EXTENTS }; - void SHDebugDrawSystem::DrawPoly(const SHVec4& color, std::initializer_list pointList) - { - DrawPoly(color, pointList.begin(), pointList.end()); - } - - void SHDebugDrawSystem::DrawCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) - { - static const SHVec3 EXTENTS = SHVec3 { 0.5f, 0.5f, 0.5f }; - static const SHVec3 UNIT_BOT_LEFT_FRONT = SHVec3 { pos - EXTENTS }; - static const SHVec3 UNIT_BOT_RIGHT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, -EXTENTS.z } }; - static const SHVec3 UNIT_BOT_RIGHT_BACK = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; - static const SHVec3 UNIT_BOT_LEFT_BACK = SHVec3 { pos + SHVec3 { -EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; - static const SHVec3 UNIT_TOP_LEFT_BACK = SHVec3 { pos + SHVec3 { -EXTENTS.x, EXTENTS.y, EXTENTS.z } }; - static const SHVec3 UNIT_TOP_RIGHT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, EXTENTS.y, -EXTENTS.z } }; - static const SHVec3 UNIT_TOP_LEFT_FRONT = SHVec3 { pos + SHVec3 { -EXTENTS.x, EXTENTS.y, -EXTENTS.z } }; - static const SHVec3 UNIT_TOP_RIGHT_BACK = SHVec3 { pos + EXTENTS }; - - const SHVec3 BOT_LEFT_BACK = UNIT_BOT_LEFT_BACK * size; - const SHVec3 BOT_RIGHT_BACK = UNIT_BOT_RIGHT_BACK * size; - const SHVec3 BOT_LEFT_FRONT = UNIT_BOT_LEFT_FRONT * size; + const SHVec3 BOT_LEFT_BACK = UNIT_BOT_LEFT_BACK * size; + const SHVec3 BOT_RIGHT_BACK = UNIT_BOT_RIGHT_BACK * size; + const SHVec3 BOT_LEFT_FRONT = UNIT_BOT_LEFT_FRONT * size; const SHVec3 BOT_RIGHT_FRONT = UNIT_BOT_RIGHT_FRONT * size; - const SHVec3 TOP_LEFT_BACK = UNIT_TOP_LEFT_BACK * size; - const SHVec3 TOP_RIGHT_BACK = UNIT_TOP_RIGHT_BACK * size; - const SHVec3 TOP_LEFT_FRONT = UNIT_TOP_LEFT_FRONT * size; + const SHVec3 TOP_LEFT_BACK = UNIT_TOP_LEFT_BACK * size; + const SHVec3 TOP_RIGHT_BACK = UNIT_TOP_RIGHT_BACK * size; + const SHVec3 TOP_LEFT_FRONT = UNIT_TOP_LEFT_FRONT * size; const SHVec3 TOP_RIGHT_FRONT = UNIT_TOP_RIGHT_FRONT * size; - DrawPoly + drawPoly ( + storage, color, { // Bottom Square @@ -186,7 +207,7 @@ namespace SHADE ); } - void SHDebugDrawSystem::DrawSphere(const SHVec4& color, const SHVec3& pos, double radius) + void SHDebugDrawSystem::drawSphere(std::vector& storage, const SHVec4& color, const SHVec3& pos, double radius) { if (spherePoints.empty()) { @@ -197,6 +218,6 @@ namespace SHADE spherePoints.emplace_back(SPHERE.VertexPositions[idx]); } } - DrawPoly(color, spherePoints.begin(), spherePoints.end()); + drawPoly(storage, color, spherePoints.begin(), spherePoints.end()); } } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index 4b83958d..41a0dd77 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -148,11 +148,22 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ // CPU Buffers std::vector points; + std::vector persistentPoints; // GPU Buffers TripleBuffer vertexBuffers; TripleUInt numPoints; // Cached Points for polygon drawing std::vector spherePoints; + + /*---------------------------------------------------------------------------------*/ + /* Helper Draw Functions */ + /*---------------------------------------------------------------------------------*/ + void drawLine(std::vector& storage, const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + void drawPoly(std::vector& storage, const SHVec4& color, std::initializer_list pointList); + 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); }; } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp index 14fbb42f..953b1cb3 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp @@ -20,12 +20,21 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ template void SHDebugDrawSystem::DrawPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd) + { + drawPoly(points, color, pointListBegin, pointListEnd); + } + + /*-----------------------------------------------------------------------------------*/ + /* Helper Draw Functions */ + /*-----------------------------------------------------------------------------------*/ + template + void SHDebugDrawSystem::drawPoly(std::vector& storage, const SHVec4& color, IterType pointListBegin, IterType pointListEnd) { // Ensure dereferenced type is SHVec3 static_assert(std::is_same_v>, "Parameters to DrawPoly must be SHVec3."); // Check if points exceeded max - if (points.size() > MAX_POINTS) + if (storage.size() > MAX_POINTS) { SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); return; @@ -42,7 +51,7 @@ namespace SHADE const size_t POINTS_ROUNDED_COUNT = POINTS_COUNT / 2 * 2; for (auto pointIter = pointListBegin; pointIter != (pointListBegin + POINTS_ROUNDED_COUNT); ++pointIter) { - points.emplace_back(PointVertex{ *pointIter, color }); + storage.emplace_back(PointVertex{ *pointIter, color }); } } } \ No newline at end of file From 41e1f01f29174fbfaa8cc700bbef9e79b07e5173 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 10:06:39 +0800 Subject: [PATCH 037/113] Added support for persistent debug draw --- .../src/Application/SBApplication.cpp | 4 + .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 126 +++++++++++++++--- .../MiddleEnd/Interface/SHDebugDrawSystem.h | 85 +++++++++++- SHADE_Engine/src/Tools/SHDebugDraw.cpp | 38 +++++- SHADE_Engine/src/Tools/SHDebugDraw.h | 58 ++++++++ 5 files changed, 291 insertions(+), 20 deletions(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index b865f028..8b437584 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -146,6 +146,10 @@ namespace Sandbox if(editor->editorState == SHEditor::State::PLAY) SHSceneManager::SceneUpdate(0.016f); #endif + SHDebugDraw::Line(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 0.0f, 0.0f), SHVec3(5.0f, 0.0f, 0.0f)); + SHDebugDraw::Line(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 1.0f, 0.0f), SHVec3(5.0f, 1.0f, 0.0f)); + SHDebugDraw::Cube(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, SHVec3(1.0f, 1.0f, 1.0f)); + SHDebugDraw::Sphere(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, 3.0f); SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, 0.016f); editor->PollPicking(); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 715b7d69..16069564 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -43,20 +43,41 @@ namespace SHADE return; } + // Get the system + SHDebugDrawSystem* system = static_cast(GetSystem()); + // Get current frame index const uint32_t FRAME_IDX = gfxSys->GetCurrentFrameIndex(); - // Create the buffer if it doesn't exist or just update it - SHDebugDrawSystem* system = static_cast(GetSystem()); + /* Non-Persistent Buffer */ + // Update the buffer system->numPoints[FRAME_IDX] = system->points.size(); const uint32_t DATA_SIZE = sizeof(PointVertex) * system->points.size(); if (DATA_SIZE > 0) { - system->vertexBuffers[FRAME_IDX]->WriteToMemory(system->points.data(), DATA_SIZE, 0, 0); + system->vertexBuffers[FRAME_IDX]->WriteToMemory(system->points.data(), DATA_SIZE, 0, 0); } // Reset for next frame system->points.clear(); + + /* Persistent Buffer */ + // Check if there are changes + if (system->persistentBuffersCleared[FRAME_IDX] + || + system->numPersistentPoints[FRAME_IDX] != system->persistentPoints.size()) + { + // Update Buffer + system->numPersistentPoints[FRAME_IDX] = system->persistentPoints.size(); + const uint32_t DATA_SIZE = sizeof(PointVertex) * system->persistentPoints.size(); + if (DATA_SIZE > 0) + { + system->persistentVertexBuffers[FRAME_IDX]->WriteToMemory(system->persistentPoints.data(), DATA_SIZE, 0, 0); + } + + // Reset Flag + system->persistentBuffersCleared[FRAME_IDX] = false; + } } /*---------------------------------------------------------------------------------*/ @@ -74,32 +95,56 @@ namespace SHADE // Get Current frame index const uint32_t FRAME_IDX = GFX_SYSTEM->GetCurrentFrameIndex(); - // Don't draw if no points - if (numPoints[FRAME_IDX] <= 0) - return; - + // Set Pipeline cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); cmdBuffer->SetLineWidth(LineWidth); - cmdBuffer->BindVertexBuffer(0, vertexBuffers[FRAME_IDX], 0); - cmdBuffer->DrawArrays(numPoints[FRAME_IDX], 1, 0, 0); + + // Don't draw if no points + if (numPoints[FRAME_IDX] > 0) + { + cmdBuffer->BindVertexBuffer(0, vertexBuffers[FRAME_IDX], 0); + cmdBuffer->DrawArrays(numPoints[FRAME_IDX], 1, 0, 0); + } + if (numPersistentPoints[FRAME_IDX] > 0) + { + cmdBuffer->BindVertexBuffer(0, persistentVertexBuffers[FRAME_IDX], 0); + cmdBuffer->DrawArrays(numPersistentPoints[FRAME_IDX], 1, 0, 0); + } }); // Reset trackers std::fill_n(numPoints.begin(), numPoints.size(), 0); + std::fill_n(numPersistentPoints.begin(), numPersistentPoints.size(), 0); + for (bool& cleared : persistentBuffersCleared) + cleared = true; // Allocate buffers + // - Non-Persistent Draws static constexpr uint32_t BUFFER_SIZE = MAX_POINTS * sizeof(PointVertex); for (Handle& bufHandle : vertexBuffers) { - bufHandle = GFX_SYSTEM->GetDevice()->CreateBuffer - ( - BUFFER_SIZE, - nullptr, - 0, - vk::BufferUsageFlagBits::eVertexBuffer, - VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, - VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT - ); + bufHandle = GFX_SYSTEM->GetDevice()->CreateBuffer + ( + BUFFER_SIZE, + nullptr, + 0, + vk::BufferUsageFlagBits::eVertexBuffer, + VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, + VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT + ); + } + // - Persistent Draws + for (Handle& bufHandle : persistentVertexBuffers) + { + bufHandle = GFX_SYSTEM->GetDevice()->CreateBuffer + ( + BUFFER_SIZE, + nullptr, + 0, + vk::BufferUsageFlagBits::eVertexBuffer, + VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, + VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT + ); } } @@ -110,6 +155,11 @@ namespace SHADE if (vertexBuffer) vertexBuffer.Free(); } + for (auto vertexBuffer : persistentVertexBuffers) + { + if (vertexBuffer) + vertexBuffer.Free(); + } } /*---------------------------------------------------------------------------------*/ @@ -145,6 +195,46 @@ namespace SHADE drawSphere(points, color, pos, radius); } + /*---------------------------------------------------------------------------------*/ + /* Persistent Draw Functions */ + /*---------------------------------------------------------------------------------*/ + void SHDebugDrawSystem::DrawPersistentLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) + { + drawLine(persistentPoints, color, startPt, endPt); + } + + void SHDebugDrawSystem::DrawPersistentTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3) + { + drawPoly(persistentPoints, color, { pt1, pt2, pt3 }); + } + + void SHDebugDrawSystem::DrawPersistentQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4) + { + drawPoly(persistentPoints, color, { pt1, pt2, pt3, pt4 }); + } + + void SHDebugDrawSystem::DrawPersistentPoly(const SHVec4& color, std::initializer_list pointList) + { + drawPoly(persistentPoints, color, pointList.begin(), pointList.end()); + } + + void SHDebugDrawSystem::DrawPersistentCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) + { + drawCube(persistentPoints, color, pos, size); + } + + void SHDebugDrawSystem::DrawPersistentSphere(const SHVec4& color, const SHVec3& pos, double radius) + { + drawSphere(persistentPoints, color, pos, radius); + } + + void SHDebugDrawSystem::ClearPersistentDraws() + { + persistentPoints.clear(); + for (bool& cleared : persistentBuffersCleared) + cleared = true; + } + void SHDebugDrawSystem::drawLine(std::vector& storage, const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) { if (storage.size() > MAX_POINTS) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index 41a0dd77..7c9e7c81 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -126,6 +126,78 @@ namespace SHADE /// Size of the rendered sphere. void DrawSphere(const SHVec4& color, const SHVec3& pos, double radius); + /*---------------------------------------------------------------------------------*/ + /* Persistent Draw Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Renders a line between two points in world space that will persist until + /// ClearPersistentDraws() is called. + /// + /// Colour of the line. + /// First point of the line. + /// Second point of the line. + void DrawPersistentLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + /// + /// Renders a triangle indicated by three points in world space that will persist + /// until ClearPersistentDraws() is called. + /// + /// Colour of the triangle. + /// First point of the triangle. + /// Second point of the triangle. + /// Third point of the triangle. + void DrawPersistentTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3); + /// + /// Renders a quadrilateral indicated by four points in world space that will persist + /// until ClearPersistentDraws() is called. + /// + /// Colour of the quadrilateral. + /// First point of the triangle. + /// Second point of the quadrilateral. + /// Third point of the quadrilateral. + /// Third point of the quadrilateral. + void DrawPersistentQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4); + /// + /// Renders a polygon indicated by the specified set of points in world space that + /// will persist until ClearPersistentDraws() is called. + /// + /// Colour of the polygon. + /// List of points for the polygon. + void DrawPersistentPoly(const SHVec4& color, std::initializer_list pointList); + /// + /// Renders a polygon indicated by the specified set of points in world space that + /// will persist until ClearPersistentDraws() is called. + /// + /// Iterator for a STL-like container. + /// Colour of the polygon. + /// + /// Iterator to the first point of the point container. + /// + /// + /// One past last iterator of the point container. + /// + template + void DrawPersistentPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd); + /// + /// Renders a wireframe cube centered around the position specified in world space + /// that will persist until ClearPersistentDraws() is called. + /// + /// Colour of the cube. + /// Position where the cube wil be centered at. + /// Size of the rendered cube. + void DrawPersistentCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size); + /// + /// Renders a wireframe sphere centered around the position specified in world space + /// that will persist until ClearPersistentDraws() is called. + /// + /// Colour of the sphere. + /// Position where the sphere wil be centered at. + /// Size of the rendered sphere. + void DrawPersistentSphere(const SHVec4& color, const SHVec3& pos, double radius); + /// + /// Clears any persistent drawn debug primitives. + /// + void ClearPersistentDraws(); + private: /*---------------------------------------------------------------------------------*/ /* Type Definitions */ @@ -136,7 +208,8 @@ namespace SHADE SHVec4 Color; }; using TripleBuffer = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; - using TripleUInt = std::array; + using TripleUInt = std::array; + using TripleBool = std::array; /*---------------------------------------------------------------------------------*/ /* Constants */ @@ -152,6 +225,9 @@ namespace SHADE // GPU Buffers TripleBuffer vertexBuffers; TripleUInt numPoints; + TripleBuffer persistentVertexBuffers; + TripleUInt numPersistentPoints; + TripleBool persistentBuffersCleared; // Cached Points for polygon drawing std::vector spherePoints; @@ -165,6 +241,13 @@ namespace SHADE 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); }; + + template + void SHADE::SHDebugDrawSystem::DrawPersistentPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd) + { + + } + } #include "SHDebugDrawSystem.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Tools/SHDebugDraw.cpp b/SHADE_Engine/src/Tools/SHDebugDraw.cpp index a5b86c42..b8aa8b0e 100644 --- a/SHADE_Engine/src/Tools/SHDebugDraw.cpp +++ b/SHADE_Engine/src/Tools/SHDebugDraw.cpp @@ -25,7 +25,7 @@ namespace SHADE /* Static Member Definitions */ /*-----------------------------------------------------------------------------------*/ SHDebugDrawSystem* SHDebugDraw::dbgDrawSys = nullptr; - + /*-----------------------------------------------------------------------------------*/ /* Lifecycle Functions */ /*-----------------------------------------------------------------------------------*/ @@ -70,4 +70,40 @@ namespace SHADE { dbgDrawSys->DrawSphere(color, pos, radius); } + + void SHDebugDraw::PersistentLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) + { + dbgDrawSys->DrawPersistentLine(color, startPt, endPt); + } + + void SHDebugDraw::PersistentTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3) + { + dbgDrawSys->DrawPersistentTri(color, pt1, pt2, pt3); + } + + void SHDebugDraw::PersistentQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4) + { + dbgDrawSys->DrawPersistentQuad(color, pt1, pt2, pt3, pt4); + } + + void SHDebugDraw::PersistentPoly(const SHVec4& color, std::initializer_list pointList) + { + dbgDrawSys->DrawPersistentPoly(color, pointList); + } + + void SHDebugDraw::PersistentCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) + { + dbgDrawSys->DrawPersistentCube(color, pos, size); + } + + void SHDebugDraw::PersistentSphere(const SHVec4& color, const SHVec3& pos, double radius) + { + dbgDrawSys->DrawPersistentSphere(color, pos, radius); + } + + void SHDebugDraw::ClearPersistentDraws() + { + dbgDrawSys->ClearPersistentDraws(); + } + } \ No newline at end of file diff --git a/SHADE_Engine/src/Tools/SHDebugDraw.h b/SHADE_Engine/src/Tools/SHDebugDraw.h index 7ce44ec2..04504c3a 100644 --- a/SHADE_Engine/src/Tools/SHDebugDraw.h +++ b/SHADE_Engine/src/Tools/SHDebugDraw.h @@ -92,6 +92,64 @@ namespace SHADE /// Size of the rendered sphere. static void Sphere(const SHVec4& color, const SHVec3& pos, double radius); + /*---------------------------------------------------------------------------------*/ + /* Persistent Draw Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Renders a line between two points in world space that will persist until + /// ClearPersistentDraws() is called. + /// + /// Colour of the line. + /// First point of the line. + /// Second point of the line. + static void PersistentLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + /// + /// Renders a triangle indicated by three points in world space that will persist + /// until ClearPersistentDraws() is called. + /// + /// Colour of the triangle. + /// First point of the triangle. + /// Second point of the triangle. + /// Third point of the triangle. + static void PersistentTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3); + /// + /// Renders a quadrilateral indicated by four points in world space that will persist + /// until ClearPersistentDraws() is called. + /// + /// Colour of the quadrilateral. + /// First point of the triangle. + /// Second point of the quadrilateral. + /// Third point of the quadrilateral. + /// Third point of the quadrilateral. + static void PersistentQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4); + /// + /// Renders a polygon indicated by the specified set of points in world space that + /// will persist until ClearPersistentDraws() is called. + /// + /// Colour of the polygon. + /// List of points for the polygon. + static void PersistentPoly(const SHVec4& color, std::initializer_list pointList); + /// + /// Renders a wireframe cube centered around the position specified in world space + /// that will persist until ClearPersistentDraws() is called. + /// + /// Colour of the cube. + /// Position where the cube wil be centered at. + /// Size of the rendered cube. + static void PersistentCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size); + /// + /// Renders a wireframe sphere centered around the position specified in world space + /// that will persist until ClearPersistentDraws() is called. + /// + /// Colour of the sphere. + /// Position where the sphere wil be centered at. + /// Size of the rendered sphere. + static void PersistentSphere(const SHVec4& color, const SHVec3& pos, double radius); + /// + /// Clears any persistent drawn debug primitives. + /// + static void ClearPersistentDraws(); + private: /*---------------------------------------------------------------------------------*/ /* Static Data Members */ From 82e0e4df5ce8ec81619bd1463e3ec94dce216c71 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 10:33:17 +0800 Subject: [PATCH 038/113] Added editor gridlines and cardinal axes --- .../src/Application/SBApplication.cpp | 10 +- SHADE_Engine/src/Editor/SHEditor.cpp | 104 +++++++++++++++++- SHADE_Engine/src/Editor/SHEditor.h | 9 +- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 2 +- 4 files changed, 114 insertions(+), 11 deletions(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 8b437584..10cc09b3 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -72,6 +72,9 @@ namespace Sandbox SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); + + // Link up SHDebugDraw + SHDebugDraw::Init(SHSystemManager::GetSystem()); #ifdef SHEDITOR SDL_Init(SDL_INIT_VIDEO); @@ -127,9 +130,6 @@ namespace Sandbox SHSceneManager::InitSceneManager("TestScene"); SHFrameRateController::UpdateFRC(); - - // Link up SHDebugDraw - SHDebugDraw::Init(SHSystemManager::GetSystem()); } void SBApplication::Update(void) @@ -146,10 +146,6 @@ namespace Sandbox if(editor->editorState == SHEditor::State::PLAY) SHSceneManager::SceneUpdate(0.016f); #endif - SHDebugDraw::Line(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 0.0f, 0.0f), SHVec3(5.0f, 0.0f, 0.0f)); - SHDebugDraw::Line(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), SHVec3(-5.0f, 1.0f, 0.0f), SHVec3(5.0f, 1.0f, 0.0f)); - SHDebugDraw::Cube(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, SHVec3(1.0f, 1.0f, 1.0f)); - SHDebugDraw::Sphere(SHVec4(1.0f, 1.0f, 1.0f, 1.0f), {}, 3.0f); SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, 0.016f); editor->PollPicking(); } diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index cf5056a5..6259a7c6 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -46,6 +46,7 @@ #include #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" +#include "Tools/SHDebugDraw.h" RTTR_REGISTRATION { @@ -77,7 +78,6 @@ namespace SHADE //#==============================================================# void SHEditor::Init() { - IMGUI_CHECKVERSION(); if(auto context = ImGui::CreateContext()) { @@ -120,6 +120,17 @@ namespace SHADE window->Init(); } + /* Editor View Gridlines */ + SetUpGridLines(true, true); + // Handle state changes so that we only show in edit mode + std::shared_ptr> stateChangeEventReceiver + { + std::make_shared>(this, &SHEditor::onEditorStateChanged) + }; + SHEventManager::SubscribeTo(SH_EDITOR_ON_PLAY_EVENT, std::dynamic_pointer_cast(stateChangeEventReceiver)); + SHEventManager::SubscribeTo(SH_EDITOR_ON_PAUSE_EVENT, std::dynamic_pointer_cast(stateChangeEventReceiver)); + SHEventManager::SubscribeTo(SH_EDITOR_ON_STOP_EVENT, std::dynamic_pointer_cast(stateChangeEventReceiver)); + SHLOG_INFO("Successfully initialised SHADE Engine Editor") } @@ -180,6 +191,97 @@ namespace SHADE io->Fonts->Build(); } + void SHEditor::SetUpGridLines(bool drawGrid, bool drawAxes) + { + // Clear existing lines + SHDebugDraw::ClearPersistentDraws(); + + static constexpr float DELTA = 1.0f; + static constexpr int EXTENT_COUNT = static_cast(500.0f /* TODO: Remove hard code */ / DELTA); + static constexpr float LINE_HALF_LENGTH = (DELTA * static_cast(EXTENT_COUNT)) * 0.5f; + + // Render Grid + static const SHColour GRID_COL = { 0.2f, 0.2f, 0.2f, 1.0f }; + if (drawGrid) + { + for (int i = 1; i < EXTENT_COUNT; ++i) + { + // X-Axis Lines + SHDebugDraw::PersistentLine + ( + GRID_COL, + SHVec3 { -LINE_HALF_LENGTH, 0.0f, i * DELTA }, + SHVec3 { LINE_HALF_LENGTH, 0.0f, i * DELTA } + ); + SHDebugDraw::PersistentLine + ( + GRID_COL, + SHVec3 { -LINE_HALF_LENGTH, 0.0f, i * -DELTA }, + SHVec3 { LINE_HALF_LENGTH, 0.0f, i * -DELTA } + ); + // Y-Axis Lines + SHDebugDraw::PersistentLine + ( + GRID_COL, + SHVec3 { i * DELTA, 0.0f, -LINE_HALF_LENGTH }, + SHVec3 { i * DELTA, 0.0f, LINE_HALF_LENGTH } + ); + SHDebugDraw::PersistentLine + ( + GRID_COL, + SHVec3 { -i * DELTA, 0.0f, -LINE_HALF_LENGTH }, + SHVec3 { -i * DELTA, 0.0f, LINE_HALF_LENGTH } + ); + } + } + + // Render World Axes + if (drawAxes || drawGrid) + { + const SHColour X_AXIS_COL = drawAxes ? SHColour::RED : GRID_COL; + const SHColour Y_AXIS_COL = drawAxes ? SHColour::GREEN : GRID_COL; + const SHColour Z_AXIS_COL = drawAxes ? SHColour::BLUE : GRID_COL; + // X + SHDebugDraw::PersistentLine + ( + X_AXIS_COL, + SHVec3 { -LINE_HALF_LENGTH, 0.0f, 0.0f }, + SHVec3 { LINE_HALF_LENGTH, 0.0f, 0.0f } + ); + // Y + SHDebugDraw::PersistentLine + ( + Y_AXIS_COL, + SHVec3 { 0.0f, -LINE_HALF_LENGTH, 0.0f }, + SHVec3 { 0.0f, LINE_HALF_LENGTH, 0.0f } + ); + // Z + SHDebugDraw::PersistentLine + ( + Z_AXIS_COL, + SHVec3 { 0.0f, 0.0f, -LINE_HALF_LENGTH }, + SHVec3 { 0.0f, 0.0f, LINE_HALF_LENGTH } + ); + } + } + + SHEventHandle SHEditor::onEditorStateChanged(SHEventPtr eventPtr) + { + auto eventData = reinterpret_cast*>(eventPtr.get()); + switch (editorState) + { + case State::PAUSE: + case State::STOP: + SetUpGridLines(true, true); + break; + case State::PLAY: + default: + SHDebugDraw::ClearPersistentDraws(); + break; + } + return eventData->handle; + } + void SHEditor::Exit() { for (const auto& window : SHEditorWindowManager::editorWindows | std::views::values) diff --git a/SHADE_Engine/src/Editor/SHEditor.h b/SHADE_Engine/src/Editor/SHEditor.h index 624069db..6e0ef5ae 100644 --- a/SHADE_Engine/src/Editor/SHEditor.h +++ b/SHADE_Engine/src/Editor/SHEditor.h @@ -16,8 +16,9 @@ #include "Resource/SHHandle.h" #include "EditorWindow/SHEditorWindow.h" #include "Tools/SHLog.h" -#include "Gizmos/SHTransformGizmo.h" - +#include "Gizmos/SHTransformGizmo.h"` +#include "Events/SHEventDefines.h" +#include "Events/SHEvent.h" //#==============================================================# //|| Library Includes || @@ -194,6 +195,10 @@ namespace SHADE void InitFonts() noexcept; + void SetUpGridLines(bool drawGrid, bool drawAxes); + + SHEventHandle onEditorStateChanged(SHEventPtr eventPtr); + // Handle to command pool used for ImGui Vulkan Backend Handle imguiCommandPool; // Handle to command buffer used for ImGui Vulkan Backend diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 16069564..90fe51d5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -95,7 +95,7 @@ namespace SHADE // Get Current frame index const uint32_t FRAME_IDX = GFX_SYSTEM->GetCurrentFrameIndex(); - // Set Pipeline + // Set Pipelin cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); cmdBuffer->SetLineWidth(LineWidth); From 4eb7879bf28a150c65533bf3a66892ba6ba8e1fd Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Wed, 2 Nov 2022 11:37:03 +0800 Subject: [PATCH 039/113] Revert "Create AIPrototype.cs" This reverts commit b0054d62c6b7450860e5b453539f968aeb1ed45c. --- TempScriptsFolder/AIPrototype.cs | 53 -------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 TempScriptsFolder/AIPrototype.cs diff --git a/TempScriptsFolder/AIPrototype.cs b/TempScriptsFolder/AIPrototype.cs deleted file mode 100644 index c7240806..00000000 --- a/TempScriptsFolder/AIPrototype.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; -using SHADE; - -public class AIPrototype : Script -{ - //This object's relevant components - private Transform transform; - - [SerializeField] - [Tooltip("The list of waypoints that the object will move around on")] - private Vector3[] waypoints; - - [SerializeField] - [Tooltip("How fast the object moves about waypoints")] - private float moveSpeed; - - //To cycle depending on the length of waypoints - private int currentTargetWaypointIndex; - - public AIPrototype(GameObject gameObj) : base(gameObj) { } - - protected override void awake() - { - transform = GetComponent(); - if (transform == null) - { - Debug.LogError("Transform is NULL!"); - } - - currentTargetWaypointIndex = 0; - } - - protected override void update() - { - //Head towards the next target - transform.GlobalPosition += (waypoints[currentTargetWaypointIndex] - transform.GlobalPosition) * moveSpeed * (float)Time.DeltaTime; - - //Cycle to next waypoint if near enough - if ((waypoints[currentTargetWaypointIndex] - transform.GlobalPosition).GetSqrMagnitude() < 1.0f) - { - ++currentTargetWaypointIndex; - if (currentTargetWaypointIndex == waypoints.Length) - { - currentTargetWaypointIndex = 0; //Recycle - } - } - } -} \ No newline at end of file From f513603479999309b4d8a2ab0e2e612a94093c69 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Wed, 2 Nov 2022 13:14:17 +0800 Subject: [PATCH 040/113] AI Prototype implemented --- TempScriptsFolder/AIPrototype.cs | 174 +++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 TempScriptsFolder/AIPrototype.cs diff --git a/TempScriptsFolder/AIPrototype.cs b/TempScriptsFolder/AIPrototype.cs new file mode 100644 index 00000000..88a319e3 --- /dev/null +++ b/TempScriptsFolder/AIPrototype.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using SHADE; + +public class AIPrototype : Script +{ + //This object's relevant components + private Transform transform; + private RigidBody rb; + + /*[SerializeField] + [Tooltip("The list of waypoints that the object will move around on")] + private Vector3[] waypoints;*/ + + private Vector3[] waypoints = { new Vector3(2.0f, -2.0f, -2.8f), new Vector3(-2.0f, -2.0f, -2.8f), new Vector3(-2.0f, -2.0f, -7.0f), new Vector3(2.0f, -2.0f, -7.0f) }; + + [SerializeField] + [Tooltip("How much force is applied in movement")] + private float movementForceMultiplier = 100.0f; + + [SerializeField] + [Tooltip("How fast the object moves about waypoints")] + private float patrolSpeed = 1.0f; + + [SerializeField] + [Tooltip("How fast the object moves while chasing")] + private float chaseSpeed = 1.5f; + + [SerializeField] + [Tooltip("How near the player must be for the chase to begin. Should be less than distanceToEndChase")] + private float distanceToStartChase = 1.5f; + + [SerializeField] + [Tooltip("How far the player must be for the chase to end. Should be greater than distanceToStartChase")] + private float distanceToEndChase = 2.5f; + + //Whether the AI is chasing or not + private bool chaseMode; + + //To cycle depending on the length of waypoints + private int currentTargetWaypointIndex; + + private GameObject? player; + + public AIPrototype(GameObject gameObj) : base(gameObj) { } + + protected override void awake() + { + transform = GetComponent(); + if (transform == null) + { + Debug.LogError("Transform is NULL!"); + } + + rb = GetComponent(); + if (rb == null) + { + Debug.LogError("Rigidbody is NULL!"); + } + + currentTargetWaypointIndex = 0; + + player = GameObject.Find("Player"); + if (player == null) + { + Debug.LogError("Player is NULL!"); + } + + chaseMode = false; + } + + protected override void fixedUpdate() + { + //Patrolling + if (!chaseMode) + { + //Head towards the next target + Vector3 normalisedDifference = waypoints[currentTargetWaypointIndex] - transform.GlobalPosition; + normalisedDifference /= normalisedDifference.GetMagnitude(); + + //transform.GlobalPosition += normalisedDifference * moveSpeed * (float)Time.DeltaTime; + //rb.LinearVelocity = normalisedDifference * patrolSpeed; + + //ORIGINAL INTENDED CODE + /*rb.AddForce(new Vector3(normalisedDifference.x, 0.0f, normalisedDifference.z) * movementForceMultiplier); + float currentSpeed = MathF.Sqrt(rb.LinearVelocity.x * rb.LinearVelocity.x + rb.LinearVelocity.z * rb.LinearVelocity.z); + if (currentSpeed > patrolSpeed) + { + float adjustmentFactor = patrolSpeed / currentSpeed; + Vector3 adjustedVelocity = rb.LinearVelocity; + //adjustedVelocity *= adjustmentFactor; + adjustedVelocity.x = patrolSpeed; + adjustedVelocity.z = patrolSpeed; + rb.LinearVelocity = adjustedVelocity; + }*/ + + //TODO delete this when original intended code above works with velocity being limited correctly + rb.LinearVelocity = normalisedDifference * patrolSpeed; + + //transform.GlobalRotation.SetLookRotation(waypoints[currentTargetWaypointIndex], Vector3.Up); + + //Cycle to next waypoint if near enough current waypoint + if ((waypoints[currentTargetWaypointIndex] - transform.GlobalPosition).GetSqrMagnitude() <= 0.5f) + { + ++currentTargetWaypointIndex; + if (currentTargetWaypointIndex >= waypoints.Length) + { + currentTargetWaypointIndex = 0; //Recycle + } + } + + //Go chase if near enough to player + if (player != null) + { + Transform pTransform = player.GetValueOrDefault().GetComponent(); + if ((pTransform.GlobalPosition - transform.GlobalPosition).GetMagnitude() <= distanceToStartChase) + { + //Start the chase + chaseMode = true; + } + } + } + else //Chasing + { + if (player != null) + { + Transform pTransform = player.GetValueOrDefault().GetComponent(); + + //Chase the player + Vector3 normalisedDifference = pTransform.GlobalPosition - transform.GlobalPosition; + normalisedDifference /= normalisedDifference.GetMagnitude(); + + //transform.GlobalPosition += normalisedDifference * moveSpeed * (float)Time.DeltaTime; + + //ORIGINAL INTENDED CODE + /*rb.AddForce(new Vector3(normalisedDifference.x, 0.0f, normalisedDifference.z) * movementForceMultiplier); + float currentSpeed = MathF.Sqrt(rb.LinearVelocity.x * rb.LinearVelocity.x + rb.LinearVelocity.z * rb.LinearVelocity.z); + if (currentSpeed > chaseSpeed) + { + float adjustmentFactor = chaseSpeed / currentSpeed; + Vector3 adjustedVelocity = rb.LinearVelocity; + adjustedVelocity *= adjustmentFactor; + rb.LinearVelocity = adjustedVelocity; + }*/ + + //TODO delete this when original intended code above works with velocity being limited correctly + rb.LinearVelocity = normalisedDifference * chaseSpeed; + + //End chase if too far + if ((pTransform.GlobalPosition - transform.GlobalPosition).GetMagnitude() >= distanceToEndChase) + { + //Stop the chase + chaseMode = false; + + //Find the nearest waypoint to go instead + float nearestWaypointDistance = 99999999999999.9f; + for (int i = 0; i < waypoints.Length; ++i) + { + if ((waypoints[i] - transform.GlobalPosition).GetSqrMagnitude() < nearestWaypointDistance) + { + nearestWaypointDistance = waypoints[i].GetSqrMagnitude(); + currentTargetWaypointIndex = i; + } + } + } + } + } + } +} \ No newline at end of file From 7bda8b39986d3807f30bf0ebdc173a551b106402 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 13:48:53 +0800 Subject: [PATCH 041/113] Added IsInterpolating to RigidBody in C# --- SHADE_Managed/src/Components/RigidBody.cxx | 8 ++++++++ SHADE_Managed/src/Components/RigidBody.hxx | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/SHADE_Managed/src/Components/RigidBody.cxx b/SHADE_Managed/src/Components/RigidBody.cxx index 172b928b..12861600 100644 --- a/SHADE_Managed/src/Components/RigidBody.cxx +++ b/SHADE_Managed/src/Components/RigidBody.cxx @@ -148,6 +148,14 @@ namespace SHADE { return Convert::ToCLI(GetNativeComponent()->GetTorque()); } + bool RigidBody::Interpolating::get() + { + return GetNativeComponent()->IsInterpolating(); + } + void RigidBody::Interpolating::set(bool value) + { + GetNativeComponent()->SetInterpolate(value); + } /*---------------------------------------------------------------------------------*/ /* Force Functions */ diff --git a/SHADE_Managed/src/Components/RigidBody.hxx b/SHADE_Managed/src/Components/RigidBody.hxx index b3e031ba..d3a30612 100644 --- a/SHADE_Managed/src/Components/RigidBody.hxx +++ b/SHADE_Managed/src/Components/RigidBody.hxx @@ -129,6 +129,11 @@ namespace SHADE { Vector3 get(); } + property bool Interpolating + { + bool get(); + void set(bool value); + } /*-----------------------------------------------------------------------------*/ /* Force Functions */ From eb569a4263a267140aabdbdc8e73f411e27156b8 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Wed, 2 Nov 2022 14:21:27 +0800 Subject: [PATCH 042/113] Shader updates --- Assets/Shaders/DeferredComposite_CS.glsl | 27 ++++++++++++------ Assets/Shaders/DeferredComposite_CS.shshaderb | Bin 4997 -> 5501 bytes 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Assets/Shaders/DeferredComposite_CS.glsl b/Assets/Shaders/DeferredComposite_CS.glsl index 833180e7..5af585ba 100644 --- a/Assets/Shaders/DeferredComposite_CS.glsl +++ b/Assets/Shaders/DeferredComposite_CS.glsl @@ -57,25 +57,34 @@ void main() // normal of fragment vec3 normalView = imageLoad(normals, globalThread).rgb; + // light layer index + uint lightLayer = imageLoad (lightLayerData, globalThread).r; + vec3 fragColor = vec3 (0.0f); for (int i = 0; i < lightCounts.directionalLights; ++i) { - // get normalized direction of light - vec3 dLightNormalized = normalize (DirLightData.dLightData[i].direction); + if ((lightLayer & DirLightData.dLightData[i].cullingMask) != 0) + { + // get normalized direction of light + vec3 dLightNormalized = normalize (DirLightData.dLightData[i].direction); - // Get diffuse strength - float diffuseStrength = max (0, dot (dLightNormalized, normalView)); + // Get diffuse strength + float diffuseStrength = max (0, dot (dLightNormalized, normalView)); - // Calculate the fragment color - fragColor += DirLightData.dLightData[i].diffuseColor.rgb * diffuseStrength.rrr * pixelDiffuse; + // Calculate the fragment color + fragColor += DirLightData.dLightData[i].diffuseColor.rgb * diffuseStrength.rrr * pixelDiffuse; + } } for (int i = 0; i < lightCounts.ambientLights; ++i) { - // Just do some add - //fragColor += pixelDiffuse.rgb * AmbLightData.aLightData[i].ambientColor.rgb * vec3 (0.5f); - fragColor += pixelDiffuse.rgb * AmbLightData.aLightData[i].ambientColor.rgb * vec3 (AmbLightData.aLightData[i].strength); + if ((lightLayer & AmbLightData.aLightData[i].cullingMask) != 0) + { + // Just do some add + //fragColor += pixelDiffuse.rgb * AmbLightData.aLightData[i].ambientColor.rgb * vec3 (0.5f); + fragColor += pixelDiffuse.rgb * AmbLightData.aLightData[i].ambientColor.rgb * vec3 (AmbLightData.aLightData[i].strength); + } } float ssaoVal = imageLoad (ssaoBlurredImage, globalThread).r; diff --git a/Assets/Shaders/DeferredComposite_CS.shshaderb b/Assets/Shaders/DeferredComposite_CS.shshaderb index 8d7a83271b3d684965b4a639eba7e74329c9469d..6e0ff2a40f9907e1126db4f89f94de2523310b2a 100644 GIT binary patch literal 5501 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 H+t-dYB8QE`3^aG z`1y0n-KiXvxHcZf$0zIMuq0J=cBHjc;;}+%8mykG6~iQ|RR;%Bdt0zld^$Q)jt8QN ziJ6A7v#&LHP&|ys%cUB6TacGNU28;a9*xSUr3%jl-5%a>*n075tv(gTnJ4i!%wmmn zUyt;OdRXbN#kG36uO-+l9;I?F*eN~~RVI`E+DtWRsDh3Z6CAj!s#U`{bs=w?{;`LC zPS>Jp;w*;KgT{2tV0pKv{&Y5+8js4eJF;`_5*~Z`bMo&OR!ZX@NlzD@E6?6D_c@TY zaJH^vNqwf6WVK;u&DJiN+Q6wfYV^78-^Ho9I1_6;Du=_y$+TwtskxN3%dSs%+7TQO zKDUN}FbM^xxv;acGh`Nedr+A6r6)6g@L_zjJUU9bT1h6ArzI!}&-&CJOiS+b?(goI zGn>qIzq7S+ZuW8Rh6R>vIOm>wANX@_|32smv*YKt`iX^&6?!M*Txtn>JT#m6%fcIt zur?aTI%j)u)#>}lk8k`C%?tSEf>#`+by;4pxn7DU@#mvl5c(z>6Dk*68`991S ztrns4w$_PSJ-tgZ=f>VG?%UC^v#|4)d^>S?;_r;_nx46ZMMNXERLAEb0Se zAO6g;UmrU4k)Qp{5zF=)X7yPu_*s3+B%7brw@MiK-Vvw1ya*qgIg3wyU{)Wq=huf$ zeKrfs9Cly#?bqkn(Xr=5)bootv-W_~?}+>H(DA39r8*&J?2?9#yj(HtJWbM9O4lFG z6vLjE&a8kz_|Hpm{ilUL`N4>ZKRqWF_)e$uCM^CU$>hX;h5qNh^CDs`7e}{PDo9PLxcm;W`(_{avPHyND~-3v-TK77=Y?@*?Bqx1ZGvIn?PT^X z6WS+Eya91)v3T6Y8r?SU4%=GE2gK2dQxGR67BwIg6 zC9{{jW*?W_B%(gvq4no(jVf_$@#@FzQ239ESb8lc|SZUlB(ppTCH~ zT(ajQXTX=3=vN&^&D8ao)3GyOyh|4IbweBafe_>b&59e zB@Ppt#qM_)_ckIw>RT$p7u#;#1m^}L58sW4M0_*Q@n0>%XI%T4&$S|az=w5n%>Tn8 zaEL`d?wk1BBX;6-i9aIZ`(g1PmF&lV%=v&@{KqAOLo7djUOIM*|AgcXPN)8jB0v5n z>G*(K{3j)YLo7c&ea6mNdc=F3On3b{x0A{Y~BVotNplSt9?}ROCqZs+X)fA%;(D@?AF^?90ty^-r`RUuZqYw;?DY- z!@!{{s-N?{E+Q6oJL4OYnQ!xXQ!;+w<})Ulxw+)xyd@b7Uz;f~=7gD{Z*cZAC!vV5 zU(|l|qQl0;*^dp(pF{eOjc*El#a0$4Hk=u)h);@G*duZ5aLfI+WMbHio|Ft`Ga5@~ zM(zHY(J2wQ&1h9J`@oq|^a&C9$%kxrRg=tpTK*ZyLT!XN8duKg)McvgJE3`GUyuVdKv6Wp3UTVYj(?&tc$@<#}H+xv;aBS-L3l zXX28>@U@u$qc8M`dxBe?A4sN7n~4u4gV{`A1M~aNeY5|H_{XBFPHz|gMARv=Z~Ui{ z@d4-W3+Me@1kR$y%i>^mA74lYvol_k%o(q_x&Jbig8)A}BN+ax#J>_ Date: Wed, 2 Nov 2022 15:05:34 +0800 Subject: [PATCH 043/113] new SHMETA --- Assets/Shaders/SSAOBlur_CS.shshaderb.shmeta | 3 +++ Assets/Shaders/SSAO_CS.shshaderb.shmeta | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 Assets/Shaders/SSAOBlur_CS.shshaderb.shmeta diff --git a/Assets/Shaders/SSAOBlur_CS.shshaderb.shmeta b/Assets/Shaders/SSAOBlur_CS.shshaderb.shmeta new file mode 100644 index 00000000..57bdb72b --- /dev/null +++ b/Assets/Shaders/SSAOBlur_CS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: SSAOBlur_CS +ID: 39760835 +Type: 2 diff --git a/Assets/Shaders/SSAO_CS.shshaderb.shmeta b/Assets/Shaders/SSAO_CS.shshaderb.shmeta index a5b5b739..9a477b24 100644 --- a/Assets/Shaders/SSAO_CS.shshaderb.shmeta +++ b/Assets/Shaders/SSAO_CS.shshaderb.shmeta @@ -1,3 +1,3 @@ Name: SSAO_CS -ID: 48941934 +ID: 38430899 Type: 2 From f0ef9fc0cf411b494ec1e44c380fc3a48c89e138 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 15:13:12 +0800 Subject: [PATCH 044/113] Fixed validation errors due to debug draw --- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 16 ++- .../MiddleEnd/Interface/SHDebugDrawSystem.h | 25 ++-- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 113 ++++++++++-------- .../MiddleEnd/Interface/SHGraphicsSystem.h | 6 + 4 files changed, 92 insertions(+), 68 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 90fe51d5..74effb6d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -95,18 +95,26 @@ namespace SHADE // Get Current frame index const uint32_t FRAME_IDX = GFX_SYSTEM->GetCurrentFrameIndex(); - // Set Pipelin - cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); - cmdBuffer->SetLineWidth(LineWidth); - // Don't draw if no points if (numPoints[FRAME_IDX] > 0) { + cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); + cmdBuffer->SetLineWidth(LineWidth); cmdBuffer->BindVertexBuffer(0, vertexBuffers[FRAME_IDX], 0); cmdBuffer->DrawArrays(numPoints[FRAME_IDX], 1, 0, 0); } + }); + auto subPassWithDepth = renderGraph->GetNode("Debug Draw with Depth")->GetSubpass("Debug Draw with Depth"); + subPassWithDepth->AddExteriorDrawCalls([this, GFX_SYSTEM](Handle& cmdBuffer) + { + // Get Current frame index + const uint32_t FRAME_IDX = GFX_SYSTEM->GetCurrentFrameIndex(); + + // Don't draw if no points if (numPersistentPoints[FRAME_IDX] > 0) { + cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawDepthPipeline()); + cmdBuffer->SetLineWidth(LineWidth); cmdBuffer->BindVertexBuffer(0, persistentVertexBuffers[FRAME_IDX], 0); cmdBuffer->DrawArrays(numPersistentPoints[FRAME_IDX], 1, 0, 0); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index 7c9e7c81..2a9709ed 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -131,7 +131,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /// /// Renders a line between two points in world space that will persist until - /// ClearPersistentDraws() is called. + /// ClearPersistentDraws() is called. These lines are depth tested. /// /// Colour of the line. /// First point of the line. @@ -139,7 +139,7 @@ namespace SHADE void DrawPersistentLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); /// /// Renders a triangle indicated by three points in world space that will persist - /// until ClearPersistentDraws() is called. + /// until ClearPersistentDraws() is called. These lines are depth tested. /// /// Colour of the triangle. /// First point of the triangle. @@ -148,7 +148,7 @@ namespace SHADE void DrawPersistentTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3); /// /// Renders a quadrilateral indicated by four points in world space that will persist - /// until ClearPersistentDraws() is called. + /// until ClearPersistentDraws() is called. These lines are depth tested. /// /// Colour of the quadrilateral. /// First point of the triangle. @@ -158,14 +158,16 @@ namespace SHADE void DrawPersistentQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4); /// /// Renders a polygon indicated by the specified set of points in world space that - /// will persist until ClearPersistentDraws() is called. + /// will persist until ClearPersistentDraws() is called. These lines are depth + /// tested. /// /// Colour of the polygon. /// List of points for the polygon. void DrawPersistentPoly(const SHVec4& color, std::initializer_list pointList); /// /// Renders a polygon indicated by the specified set of points in world space that - /// will persist until ClearPersistentDraws() is called. + /// will persist until ClearPersistentDraws() is called. These lines are depth + /// tested. /// /// Iterator for a STL-like container. /// Colour of the polygon. @@ -179,7 +181,8 @@ namespace SHADE void DrawPersistentPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd); /// /// Renders a wireframe cube centered around the position specified in world space - /// that will persist until ClearPersistentDraws() is called. + /// that will persist until ClearPersistentDraws() is called. These lines are depth + /// tested. /// /// Colour of the cube. /// Position where the cube wil be centered at. @@ -187,7 +190,8 @@ namespace SHADE void DrawPersistentCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size); /// /// Renders a wireframe sphere centered around the position specified in world space - /// that will persist until ClearPersistentDraws() is called. + /// that will persist until ClearPersistentDraws() is called. These lines are depth + /// tested. /// /// Colour of the sphere. /// Position where the sphere wil be centered at. @@ -241,13 +245,6 @@ namespace SHADE 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); }; - - template - void SHADE::SHDebugDrawSystem::DrawPersistentPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd) - { - - } - } #include "SHDebugDrawSystem.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 2bc2b7de..4d551cee 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -171,7 +171,7 @@ namespace SHADE "Normals", "Albedo", "Depth Buffer", - "Scene" + "Scene" }, {}); // no predecessors @@ -186,8 +186,14 @@ namespace SHADE // deferred composite gBufferNode->AddNodeCompute(deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "Scene" }); - // Set up Debug Draw Pass - auto debugDrawNode = worldRenderGraph->AddNode("Debug Draw", { "Scene" }, {"G-Buffer"}); + // Set up Debug Draw Passes + // - Depth Tested + auto debugDrawNodeDepth = worldRenderGraph->AddNode("Debug Draw with Depth", { "Scene", "Depth Buffer" }, {"G-Buffer"}); + auto debugDrawDepthSubpass = debugDrawNodeDepth->AddSubpass("Debug Draw with Depth"); + debugDrawDepthSubpass->AddColorOutput("Scene"); + debugDrawDepthSubpass->AddDepthOutput("Depth Buffer"); + // - No Depth Test + auto debugDrawNode = worldRenderGraph->AddNode("Debug Draw", { "Scene" }, { "Debug Draw with Depth" }); auto debugDrawSubpass = debugDrawNode->AddSubpass("Debug Draw"); debugDrawSubpass->AddColorOutput("Scene"); @@ -209,53 +215,8 @@ namespace SHADE defaultMaterial = AddMaterial(defaultVertShader, defaultFragShader, gBufferSubpass); // Create debug draw pipeline - auto debugDrawPipelineLayout = resourceManager.Create - ( - device, SHPipelineLayoutParams - { - .shaderModules = { debugVertShader, debugFragShader }, - .globalDescSetLayouts = SHGraphicsGlobalData::GetDescSetLayouts() - } - ); - debugDrawPipeline = resourceManager.Create(device, debugDrawPipelineLayout, nullptr, debugDrawNode->GetRenderpass(), debugDrawSubpass); - debugDrawPipeline->GetPipelineState().SetRasterizationState(SHRasterizationState - { - .polygonMode = vk::PolygonMode::eLine, - .cull_mode = vk::CullModeFlagBits::eNone - }); - debugDrawPipeline->GetPipelineState().SetInputAssemblyState(SHInputAssemblyState - { - .topology = vk::PrimitiveTopology::eLineList - }); - - SHVertexInputState debugDrawVertexInputState; - debugDrawVertexInputState.AddBinding(false, true, { SHVertexAttribute(SHAttribFormat::FLOAT_4D), SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); - debugDrawPipeline->GetPipelineState().SetVertexInputState(debugDrawVertexInputState); - SHColorBlendState colorBlendState{}; - colorBlendState.logic_op_enable = VK_FALSE; - colorBlendState.logic_op = vk::LogicOp::eCopy; - - auto const& subpassColorReferences = debugDrawSubpass->GetColorAttachmentReferences(); - colorBlendState.attachments.reserve(subpassColorReferences.size()); - - for (auto& att : subpassColorReferences) - { - colorBlendState.attachments.push_back(vk::PipelineColorBlendAttachmentState - { - .blendEnable = SHVkUtil::IsBlendCompatible(debugDrawSubpass->GetFormatFromAttachmentReference(att.attachment)), - .srcColorBlendFactor = vk::BlendFactor::eSrcAlpha, - .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, - .colorBlendOp = vk::BlendOp::eAdd, - .srcAlphaBlendFactor = vk::BlendFactor::eOne, - .dstAlphaBlendFactor = vk::BlendFactor::eZero, - .alphaBlendOp = vk::BlendOp::eAdd, - .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, - } - ); - } - - debugDrawPipeline->GetPipelineState().SetColorBlenState(colorBlendState); - debugDrawPipeline->ConstructPipeline(); + debugDrawPipeline = createDebugDrawPipeline(debugDrawNode->GetRenderpass(), debugDrawSubpass); + debugDrawDepthPipeline = createDebugDrawPipeline(debugDrawNodeDepth->GetRenderpass(), debugDrawDepthSubpass); } void SHGraphicsSystem::InitMiddleEnd(void) noexcept @@ -867,5 +828,57 @@ namespace SHADE return worldRenderGraph->GetNode(G_BUFFER_RENDER_GRAPH_NODE_NAME.data()); } + Handle SHGraphicsSystem::createDebugDrawPipeline(Handle renderPass, Handle subpass) + { + auto pipelineLayout = resourceManager.Create + ( + device, SHPipelineLayoutParams + { + .shaderModules = { debugVertShader, debugFragShader }, + .globalDescSetLayouts = SHGraphicsGlobalData::GetDescSetLayouts() + } + ); + auto pipeline = resourceManager.Create(device, pipelineLayout, nullptr, renderPass, subpass); + pipeline->GetPipelineState().SetRasterizationState(SHRasterizationState + { + .polygonMode = vk::PolygonMode::eLine, + .cull_mode = vk::CullModeFlagBits::eNone + }); + pipeline->GetPipelineState().SetInputAssemblyState(SHInputAssemblyState + { + .topology = vk::PrimitiveTopology::eLineList + }); + + SHVertexInputState debugDrawVertexInputState; + debugDrawVertexInputState.AddBinding(false, true, { SHVertexAttribute(SHAttribFormat::FLOAT_4D), SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); + pipeline->GetPipelineState().SetVertexInputState(debugDrawVertexInputState); + SHColorBlendState colorBlendState{}; + colorBlendState.logic_op_enable = VK_FALSE; + colorBlendState.logic_op = vk::LogicOp::eCopy; + + auto const& subpassColorReferences = subpass->GetColorAttachmentReferences(); + colorBlendState.attachments.reserve(subpassColorReferences.size()); + + for (auto& att : subpassColorReferences) + { + colorBlendState.attachments.push_back(vk::PipelineColorBlendAttachmentState + { + .blendEnable = SHVkUtil::IsBlendCompatible(subpass->GetFormatFromAttachmentReference(att.attachment)), + .srcColorBlendFactor = vk::BlendFactor::eSrcAlpha, + .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, + .colorBlendOp = vk::BlendOp::eAdd, + .srcAlphaBlendFactor = vk::BlendFactor::eOne, + .dstAlphaBlendFactor = vk::BlendFactor::eZero, + .alphaBlendOp = vk::BlendOp::eAdd, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + }); + } + + pipeline->GetPipelineState().SetColorBlenState(colorBlendState); + pipeline->ConstructPipeline(); + + return pipeline; + } + #pragma endregion MISC } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index b307dc92..07e5fbab 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -289,6 +289,7 @@ namespace SHADE Handle GetPostOffscreenRenderSystem(void) const noexcept {return postOffscreenRender;}; Handle GetPrimaryRenderpass() const noexcept; Handle GetDebugDrawPipeline(void) const noexcept { return debugDrawPipeline; } + Handle GetDebugDrawDepthPipeline(void) const noexcept { return debugDrawDepthPipeline; } uint32_t GetCurrentFrameIndex(void) const noexcept { return renderContext.GetCurrentFrame(); } /*-----------------------------------------------------------------------------*/ @@ -355,6 +356,7 @@ namespace SHADE // Built-In Materials Handle defaultMaterial; Handle debugDrawPipeline; + Handle debugDrawDepthPipeline; Handle worldRenderGraph; @@ -367,5 +369,9 @@ namespace SHADE uint32_t resizeHeight; bool restoredFromMinimize = false; + /*---------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*---------------------------------------------------------------------------------*/ + Handle createDebugDrawPipeline(Handle renderPass, Handle subpass); }; } \ No newline at end of file From d818adf2a20a87ec374ba2f81bba58953e53987b Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 15:34:35 +0800 Subject: [PATCH 045/113] Fixed DrawPoly() --- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 9 +++-- .../MiddleEnd/Interface/SHDebugDrawSystem.h | 3 ++ .../MiddleEnd/Interface/SHDebugDrawSystem.hpp | 34 ++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 74effb6d..37d15808 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -255,6 +255,11 @@ namespace SHADE storage.emplace_back(PointVertex{ endPt, color }); } + void SHDebugDrawSystem::drawLineSet(std::vector& storage, const SHVec4& color, std::initializer_list pointList) + { + drawLineSet(storage, color, pointList.begin(), pointList.end()); + } + void SHDebugDrawSystem::drawPoly(std::vector& storage, const SHVec4& color, std::initializer_list pointList) { drawPoly(storage, color, pointList.begin(), pointList.end()); @@ -281,7 +286,7 @@ namespace SHADE const SHVec3 TOP_LEFT_FRONT = UNIT_TOP_LEFT_FRONT * size; const SHVec3 TOP_RIGHT_FRONT = UNIT_TOP_RIGHT_FRONT * size; - drawPoly + drawLineSet ( storage, color, @@ -316,6 +321,6 @@ namespace SHADE spherePoints.emplace_back(SPHERE.VertexPositions[idx]); } } - drawPoly(storage, color, spherePoints.begin(), spherePoints.end()); + drawLineSet(storage, color, spherePoints.begin(), spherePoints.end()); } } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h index 2a9709ed..20ddcd42 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -239,6 +239,9 @@ namespace SHADE /* Helper Draw Functions */ /*---------------------------------------------------------------------------------*/ void drawLine(std::vector& storage, const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + void drawLineSet(std::vector& storage, const SHVec4& color, std::initializer_list pointList); + template + void drawLineSet(std::vector& storage, const SHVec4& color, IterType pointListBegin, IterType pointListEnd); void drawPoly(std::vector& storage, const SHVec4& color, std::initializer_list pointList); template void drawPoly(std::vector& storage, const SHVec4& color, IterType pointListBegin, IterType pointListEnd); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp index 953b1cb3..2a171e73 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp @@ -28,7 +28,7 @@ namespace SHADE /* Helper Draw Functions */ /*-----------------------------------------------------------------------------------*/ template - void SHDebugDrawSystem::drawPoly(std::vector& storage, const SHVec4& color, IterType pointListBegin, IterType pointListEnd) + void SHDebugDrawSystem::drawLineSet(std::vector& storage, const SHVec4& color, IterType pointListBegin, IterType pointListEnd) { // Ensure dereferenced type is SHVec3 static_assert(std::is_same_v>, "Parameters to DrawPoly must be SHVec3."); @@ -54,4 +54,36 @@ namespace SHADE storage.emplace_back(PointVertex{ *pointIter, color }); } } + template + void SHDebugDrawSystem::drawPoly(std::vector& storage, const SHVec4& color, IterType pointListBegin, IterType pointListEnd) + { + // Ensure dereferenced type is SHVec3 + static_assert(std::is_same_v>, "Parameters to DrawPoly must be SHVec3."); + + // Check if points exceeded max + if (storage.size() > MAX_POINTS) + { + SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); + return; + } + + const size_t POINTS_COUNT = pointListEnd - pointListBegin; + // Invalid polygon + if (POINTS_COUNT < 2) + { + SHLOG_WARNING("[SHDebugDraw] Invalid polygon provided to DrawPoly()."); + return; + } + + // Trace the polygon + for (auto pointIter = pointListBegin + 1; pointIter != pointListEnd; ++pointIter) + { + storage.emplace_back(PointVertex{ *(pointIter - 1), color }); + storage.emplace_back(PointVertex{ *pointIter , color }); + } + + // Close the line loop + storage.emplace_back(PointVertex{ *(pointListEnd - 1), color }); + storage.emplace_back(PointVertex{ *pointListBegin , color }); + } } \ No newline at end of file From 02c79b4cc48f5d606c9ed6fcaaf75be1fc5718da Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 2 Nov 2022 15:44:24 +0800 Subject: [PATCH 046/113] Added Debug Drawing for Colliders (wonk) and Trigger checkbox to Colldiers Press space to toggle debug draw. Does not work until you press play, then it can be toggled on and off. --- .../src/Application/SBApplication.cpp | 8 + .../Inspector/SHEditorComponentView.hpp | 4 + SHADE_Engine/src/Math/SHColour.cpp | 11 ++ SHADE_Engine/src/Math/SHColour.h | 7 + SHADE_Engine/src/Physics/SHPhysicsSystem.cpp | 162 +++++++++++++++++- SHADE_Engine/src/Physics/SHPhysicsSystem.h | 52 +++--- SHADE_Engine/src/Tools/SHUtilities.h | 2 +- 7 files changed, 220 insertions(+), 26 deletions(-) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index b865f028..81926359 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -91,6 +91,7 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); @@ -148,6 +149,13 @@ namespace Sandbox #endif SHSystemManager::RunRoutines(editor->editorState != SHEditor::State::PLAY, 0.016f); editor->PollPicking(); + + static bool drawColliders = false; + if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::SPACE)) + { + drawColliders = !drawColliders; + SHSystemManager::GetSystem()->SetDrawColliders(drawColliders); + } } // Finish all graphics jobs first graphicsSystem->AwaitGraphicsExecution(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 4777fc6a..6f03a55e 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -273,6 +273,7 @@ namespace SHADE { } + { 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); }); @@ -297,6 +298,9 @@ namespace SHADE }); SHEditorWidgets::EndPanel(); } + + SHEditorWidgets::CheckBox("Is Trigger", [collider] { return collider->IsTrigger(); }, [collider](bool value) { collider->SetIsTrigger(value); }); + if (ImGui::Button(std::format("{} Remove Collider #{}", ICON_MD_REMOVE, i).data())) { colliderToDelete = i; diff --git a/SHADE_Engine/src/Math/SHColour.cpp b/SHADE_Engine/src/Math/SHColour.cpp index 944c37cb..fc2f2a08 100644 --- a/SHADE_Engine/src/Math/SHColour.cpp +++ b/SHADE_Engine/src/Math/SHColour.cpp @@ -118,6 +118,17 @@ namespace SHADE } {} + SHColour::SHColour(uint32_t rgba) noexcept + : XMFLOAT4 { 0.0f, 0.0f, 0.0f, 1.0f } + { + const SHColour32 TMP { ._32bitValue = rgba }; + + x = static_cast(TMP._8bitValues[0]) / 255.0f; + y = static_cast(TMP._8bitValues[1]) / 255.0f; + z = static_cast(TMP._8bitValues[2]) / 255.0f; + w = static_cast(TMP._8bitValues[3]) / 255.0f; + } + SHColour::SHColour(const SHVec3& rgb) noexcept : XMFLOAT4 { rgb.x, rgb.y, rgb.z, 1.0f } {} diff --git a/SHADE_Engine/src/Math/SHColour.h b/SHADE_Engine/src/Math/SHColour.h index a6adf7bb..5dac0edd 100644 --- a/SHADE_Engine/src/Math/SHColour.h +++ b/SHADE_Engine/src/Math/SHColour.h @@ -34,6 +34,12 @@ namespace SHADE float v = 0.0f; }; + union SH_API SHColour32 + { + uint32_t _32bitValue; + uint8_t _8bitValues[4]; + }; + class SH_API SHColour : public DirectX::XMFLOAT4 { public: @@ -46,6 +52,7 @@ namespace SHADE SHColour (float r, float g, float b, float a) noexcept; SHColour (uint8_t r, uint8_t g, uint8_t b) noexcept; SHColour (uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept; + SHColour (uint32_t rgba) noexcept; SHColour (const SHVec3& rgb) noexcept; SHColour (const SHVec4& rgba) noexcept; diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp index e1acccd9..bdee8ba1 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp @@ -17,10 +17,12 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHEntityManager.h" #include "ECS_Base/Managers/SHSystemManager.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" #include "Math/SHMathHelpers.h" #include "Math/Transform/SHTransformComponent.h" #include "Scene/SHSceneManager.h" #include "Scripting/SHScriptEngine.h" +#include "Tools/SHUtilities.h" namespace SHADE { @@ -30,6 +32,7 @@ namespace SHADE SHPhysicsSystem::SHPhysicsSystem() : worldUpdated { false } + , debugDrawFlags { 0 } , interpolationFactor { 0.0 } , fixedDT { 60.0 } , world { nullptr } @@ -47,6 +50,11 @@ namespace SHADE : SHSystemRoutine { "Physics PostUpdate", false } {} + SHPhysicsSystem::PhysicsDebugDraw::PhysicsDebugDraw() + : SHSystemRoutine { "Physics DebugDraw", true } + {} + + /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -99,6 +107,31 @@ namespace SHADE return 0; } + bool SHPhysicsSystem::GetDrawColliders() const noexcept + { + return debugDrawFlags & SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER); + } + + bool SHPhysicsSystem::GetDrawColliderAABBs() const noexcept + { + return debugDrawFlags & SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER_AABB); + } + + bool SHPhysicsSystem::GetDrawBroadPhase() const noexcept + { + return debugDrawFlags & SHUtilities::ConvertEnum(DebugDrawFlags::BROAD_PHASE_AABB); + } + + bool SHPhysicsSystem::GetDrawContactPoints() const noexcept + { + return debugDrawFlags & SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_POINTS); + } + + bool SHPhysicsSystem::GetDrawContactNormals() const noexcept + { + return debugDrawFlags & SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_NORMALS); + } + const SHPhysicsSystem::CollisionEvents& SHPhysicsSystem::GetCollisionInfo() const noexcept { return collisionInfo; @@ -181,6 +214,96 @@ namespace SHADE } } + void SHPhysicsSystem::SetDrawColliders(bool shouldDraw) noexcept + { + static constexpr auto FLAG_VALUE = SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER); + shouldDraw ? debugDrawFlags |= FLAG_VALUE : debugDrawFlags &= ~(FLAG_VALUE); + + if (world == nullptr) + { + SHLOGV_WARNING("No physics world has been initialised!") + return; + } + + world->getDebugRenderer().setIsDebugItemDisplayed + ( + rp3d::DebugRenderer::DebugItem::COLLISION_SHAPE, + shouldDraw + ); + } + + void SHPhysicsSystem::SetDrawColliderAABBs(bool shouldDraw) noexcept + { + static constexpr auto FLAG_VALUE = SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER_AABB); + shouldDraw ? debugDrawFlags |= FLAG_VALUE : debugDrawFlags &= ~(FLAG_VALUE); + + if (world == nullptr) + { + SHLOGV_WARNING("No physics world has been initialised!") + return; + } + + world->getDebugRenderer().setIsDebugItemDisplayed + ( + rp3d::DebugRenderer::DebugItem::COLLIDER_AABB, + shouldDraw + ); + } + + void SHPhysicsSystem::SetDrawBroadPhase(bool shouldDraw) noexcept + { + static constexpr auto FLAG_VALUE = SHUtilities::ConvertEnum(DebugDrawFlags::BROAD_PHASE_AABB); + shouldDraw ? debugDrawFlags |= FLAG_VALUE : debugDrawFlags &= ~(FLAG_VALUE); + + if (world == nullptr) + { + SHLOGV_WARNING("No physics world has been initialised!") + return; + } + + world->getDebugRenderer().setIsDebugItemDisplayed + ( + rp3d::DebugRenderer::DebugItem::COLLIDER_BROADPHASE_AABB, + shouldDraw + ); + } + + void SHPhysicsSystem::SetDrawContactPoints(bool shouldDraw) noexcept + { + static constexpr auto FLAG_VALUE = SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_POINTS); + shouldDraw ? debugDrawFlags |= FLAG_VALUE : debugDrawFlags &= ~(FLAG_VALUE); + + if (world == nullptr) + { + SHLOGV_WARNING("No physics world has been initialised!") + return; + } + + world->getDebugRenderer().setIsDebugItemDisplayed + ( + rp3d::DebugRenderer::DebugItem::CONTACT_POINT, + shouldDraw + ); + } + + void SHPhysicsSystem::SetDrawContactNormals(bool shouldDraw) noexcept + { + static constexpr auto FLAG_VALUE = SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_NORMALS); + shouldDraw ? debugDrawFlags |= FLAG_VALUE : debugDrawFlags &= ~(FLAG_VALUE); + + if (world == nullptr) + { + SHLOGV_WARNING("No physics world has been initialised!") + return; + } + + world->getDebugRenderer().setIsDebugItemDisplayed + ( + rp3d::DebugRenderer::DebugItem::CONTACT_NORMAL, + shouldDraw + ); + } + /*-----------------------------------------------------------------------------------*/ /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -198,6 +321,7 @@ namespace SHADE world = factory.createPhysicsWorld(settings); world->setEventListener(this); + world->setIsDebugRenderingEnabled(true); // Set up solvers world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::SPLIT_IMPULSES); @@ -323,7 +447,7 @@ namespace SHADE auto* scriptingSystem = SHSystemManager::GetSystem(); if (scriptingSystem == nullptr) { - SHLOGV_WARNING("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!"); + SHLOGV_ERROR("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!"); } fixedTimeStep = 1.0 / physicsSystem->fixedDT; @@ -353,7 +477,7 @@ namespace SHADE auto* scriptingSystem = SHSystemManager::GetSystem(); if (scriptingSystem == nullptr) { - SHLOGV_WARNING("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 @@ -369,6 +493,40 @@ namespace SHADE } } + void SHPhysicsSystem::PhysicsDebugDraw::Execute(double) noexcept + { + const auto* PHYSICS_SYSTEM = reinterpret_cast(GetSystem()); + if (PHYSICS_SYSTEM->debugDrawFlags == 0) + return; + + auto* debugDrawSystem = SHSystemManager::GetSystem(); + if (debugDrawSystem == nullptr) + { + SHLOGV_ERROR("Unable to debug draw physics objects due to missing SHDebugDrawSystem!"); + return; + } + + const auto& RP3D_DEBUG_RENDERER = PHYSICS_SYSTEM->world->getDebugRenderer(); + + const auto& LINES = RP3D_DEBUG_RENDERER.getLines(); + const auto& TRIANGLES = RP3D_DEBUG_RENDERER.getTriangles(); + + // Draw all lines + for (uint32_t i = 0; i < RP3D_DEBUG_RENDERER.getNbLines(); ++i) + { + const auto& LINE = LINES[i]; + debugDrawSystem->DrawLine(SHColour{ LINE.color1 }, LINE.point1, LINE.point2); + } + + for (uint32_t i = 0; i < RP3D_DEBUG_RENDERER.getNbTriangles(); ++i) + { + const auto& TRIANGLE = TRIANGLES[i]; + SHColour triColour{ TRIANGLE.color1 }; + triColour.a() = 1.0f; + debugDrawSystem->DrawTri(triColour, TRIANGLE.point1, TRIANGLE.point2, TRIANGLE.point3); + } + } + void SHPhysicsSystem::onContact(const CallbackData& callbackData) { for (uint32_t i = 0; i < callbackData.getNbContactPairs(); ++i) diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/SHPhysicsSystem.h index 1d773618..05e6e57e 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.h @@ -51,6 +51,15 @@ namespace SHADE using CollisionEvents = std::vector; + enum class DebugDrawFlags : uint8_t + { + COLLIDER = 1 + , COLLIDER_AABB = 2 + , BROAD_PHASE_AABB = 4 + , CONTACT_POINTS = 8 + , CONTACT_NORMALS = 16 + }; + /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ @@ -69,6 +78,12 @@ namespace SHADE [[nodiscard]] uint16_t GetNumberVelocityIterations () const noexcept; [[nodiscard]] uint16_t GetNumberPositionIterations () const noexcept; + [[nodiscard]] bool GetDrawColliders () const noexcept; + [[nodiscard]] bool GetDrawColliderAABBs () const noexcept; + [[nodiscard]] bool GetDrawBroadPhase () const noexcept; + [[nodiscard]] bool GetDrawContactPoints () const noexcept; + [[nodiscard]] bool GetDrawContactNormals () const noexcept; + [[nodiscard]] const CollisionEvents& GetCollisionInfo () const noexcept; [[nodiscard]] const CollisionEvents& GetTriggerInfo () const noexcept; @@ -85,6 +100,13 @@ namespace SHADE void SetWorldSettings (const WorldSettings& settings) const noexcept; + // TODO(Diren): Can the debug draw flags be done through an enum? + void SetDrawColliders (bool shouldDraw) noexcept; + void SetDrawColliderAABBs (bool shouldDraw) noexcept; + void SetDrawBroadPhase (bool shouldDraw) noexcept; + void SetDrawContactPoints (bool shouldDraw) noexcept; + void SetDrawContactNormals (bool shouldDraw) noexcept; + /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ @@ -105,48 +127,31 @@ namespace SHADE class SH_API PhysicsPreUpdate final : public SHSystemRoutine { public: - /*-------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*-------------------------------------------------------------------------------*/ PhysicsPreUpdate(); - - /*-------------------------------------------------------------------------------*/ - /* Function Members */ - /*-------------------------------------------------------------------------------*/ - void Execute(double dt) noexcept override; }; 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(); + void Execute(double dt) noexcept override; + }; - /*-------------------------------------------------------------------------------*/ - /* Function Members */ - /*-------------------------------------------------------------------------------*/ - + class SH_API PhysicsDebugDraw final : public SHSystemRoutine + { + public: + PhysicsDebugDraw(); void Execute(double dt) noexcept override; }; @@ -162,6 +167,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ bool worldUpdated; + uint8_t debugDrawFlags; double interpolationFactor; double fixedDT; diff --git a/SHADE_Engine/src/Tools/SHUtilities.h b/SHADE_Engine/src/Tools/SHUtilities.h index b3d840e7..287a827e 100644 --- a/SHADE_Engine/src/Tools/SHUtilities.h +++ b/SHADE_Engine/src/Tools/SHUtilities.h @@ -39,7 +39,7 @@ namespace SHADE * @param[in] enumClassMember A member of the specified enum class. * @returns The value of the enum class member in the output type. */ - template + template > static constexpr OutputType ConvertEnum(InputType enumClassMember) noexcept; /** From 27526dfd92dfa820f2eb72d7afac3f0fb6496e2f Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Wed, 2 Nov 2022 16:12:47 +0800 Subject: [PATCH 047/113] Fixed relative size for sphere colliders --- .../Editor/EditorWindow/Inspector/SHEditorComponentView.hpp | 4 ++-- SHADE_Engine/src/Physics/SHCollider.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 6f03a55e..20615fdc 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -265,9 +265,9 @@ namespace SHADE { const SHVec3& TF_WORLD_SCALE = transformComponent->GetWorldScale(); const float MAX_SCALE = SHMath::Max({ TF_WORLD_SCALE.x, TF_WORLD_SCALE.y, TF_WORLD_SCALE.z }); - return sphere->GetRadius() / MAX_SCALE; + return (sphere->GetRadius() / MAX_SCALE) * 2.0f; }, - [collider](float const& value) { collider->SetBoundingSphere(value); }); + [collider](float const& value) { collider->SetBoundingSphere(value); }); } else if (collider->GetType() == SHCollider::Type::CAPSULE) { diff --git a/SHADE_Engine/src/Physics/SHCollider.cpp b/SHADE_Engine/src/Physics/SHCollider.cpp index 6cea3dc1..6d455d67 100644 --- a/SHADE_Engine/src/Physics/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/SHCollider.cpp @@ -212,7 +212,7 @@ namespace SHADE const SHVec3 TF_WORLD_SCALE = transformComponent->GetWorldScale(); const float MAX_SCALE = SHMath::Max({ TF_WORLD_SCALE.x, TF_WORLD_SCALE.y, TF_WORLD_SCALE.z }); - worldRadius *= MAX_SCALE; + worldRadius *= MAX_SCALE * 0.5f; } if (type == Type::SPHERE) From 6ab6cc15a921aedc01ff2c761608dd7ee6fd3186 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Wed, 2 Nov 2022 16:14:26 +0800 Subject: [PATCH 048/113] Zero out arrays for input manager --- SHADE_Engine/src/Input/SHInputManager.cpp | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/SHADE_Engine/src/Input/SHInputManager.cpp b/SHADE_Engine/src/Input/SHInputManager.cpp index c665a9c9..4849a772 100644 --- a/SHADE_Engine/src/Input/SHInputManager.cpp +++ b/SHADE_Engine/src/Input/SHInputManager.cpp @@ -25,16 +25,16 @@ namespace SHADE std::map SHInputManager::bindings; unsigned SHInputManager::keyCount = 0; - bool SHInputManager::keys[MAX_KEYS]; - bool SHInputManager::keysLast[MAX_KEYS]; - double SHInputManager::keysHeldTime[MAX_KEYS]; - double SHInputManager::keysReleasedTime[MAX_KEYS]; + bool SHInputManager::keys[MAX_KEYS] = {}; + bool SHInputManager::keysLast[MAX_KEYS] = {}; + double SHInputManager::keysHeldTime[MAX_KEYS] = {}; + double SHInputManager::keysReleasedTime[MAX_KEYS] = {}; unsigned SHInputManager::keyToggleCount = 0; - bool SHInputManager::keysToggle[MAX_KEYS]; - bool SHInputManager::keysToggleLast[MAX_KEYS]; - double SHInputManager::keysToggleOnTime[MAX_KEYS]; - double SHInputManager::keysToggleOffTime[MAX_KEYS]; + bool SHInputManager::keysToggle[MAX_KEYS] = {}; + bool SHInputManager::keysToggleLast[MAX_KEYS] = {}; + double SHInputManager::keysToggleOnTime[MAX_KEYS] = {}; + double SHInputManager::keysToggleOffTime[MAX_KEYS] = {}; int SHInputManager::mouseScreenX = 0; int SHInputManager::mouseScreenY = 0; @@ -46,11 +46,11 @@ namespace SHADE int SHInputManager::mouseWheelVerticalDeltaPoll = 0; unsigned char SHInputManager::controllersConnectedCount = 0; - unsigned SHInputManager::controllersInputCount[XUSER_MAX_COUNT]; - unsigned SHInputManager::controllersButtonCount[XUSER_MAX_COUNT]; - short SHInputManager::controllers[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; - short SHInputManager::controllersLast[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; - double SHInputManager::controllersHeldTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + unsigned SHInputManager::controllersInputCount[XUSER_MAX_COUNT] = {}; + unsigned SHInputManager::controllersButtonCount[XUSER_MAX_COUNT] = {}; + short SHInputManager::controllers[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT] = {}; + short SHInputManager::controllersLast[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT] = {}; + double SHInputManager::controllersHeldTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT] = {}; double SHInputManager::controllersReleasedTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; //Internal helper function for splitting between inputs @@ -106,9 +106,11 @@ namespace SHADE memcpy(keysLast, keys, sizeof(keys)); //Poll - unsigned char keyboardState[MAX_KEYS]; + unsigned char keyboardState[MAX_KEYS] = {}; + SecureZeroMemory(keyboardState, sizeof(keyboardState)); //if (GetKeyboardState(keyboardState) == false) return; - SHASSERT(GetKeyboardState(keyboardState), "SHInputManager:GetKeyboardState() failed ({})", GetLastError()); + bool keyboardStateGot = GetKeyboardState(keyboardState); + SHASSERT(keyboardStateGot, "SHInputManager:GetKeyboardState() failed ({})", GetLastError()); keyCount = 0; keyToggleCount = 0; for (size_t i = 0; i < MAX_KEYS; ++i) From d207042fec502fd43567a5f4d2a9fecf64cec185 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 16:56:38 +0800 Subject: [PATCH 049/113] Reworked SHMaterialSpec and SHMaterial loading system --- .../MiddleEnd/Materials/SHMaterialSpec.cpp | 74 ++++++ .../MiddleEnd/Materials/SHMaterialSpec.h | 14 ++ SHADE_Engine/src/Resource/SHResourceManager.h | 9 +- .../src/Resource/SHResourceManager.hpp | 70 ++++-- .../Serialization/SHSerializationHelper.hpp | 231 +++++++----------- 5 files changed, 238 insertions(+), 160 deletions(-) create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.cpp diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.cpp new file mode 100644 index 00000000..0652b2bf --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.cpp @@ -0,0 +1,74 @@ +/************************************************************************************//*! +\file SHMaterialSpec.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 2, 2022 +\brief Contains the function definitions of SHMaterialSpec. + +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 "SHpch.h" +#include "SHMaterialSpec.h" +#include "Graphics/Shaders/SHVkShaderModule.h" +#include "Resource/SHResourceManager.h" +#include "Graphics/MiddleEnd/Interface/SHMaterial.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------------*/ + SHMaterialSpec::SHMaterialSpec(const SHMaterial& material) + { + // Get Shader Handles + const auto& SHADERS = material.GetPipeline()->GetPipelineLayout()->GetShaderModules(); + Handle vShaderMod, fShaderMod; + for (const auto& shader : SHADERS) + { + const auto FLAG_BITS = shader->GetShaderStageFlagBits(); + if (FLAG_BITS & vk::ShaderStageFlagBits::eVertex) + vShaderMod = shader; + else if (FLAG_BITS & vk::ShaderStageFlagBits::eFragment) + fShaderMod = shader; + } + vertexShader = SHResourceManager::GetAssetID(vShaderMod).value_or(0); + fragShader = SHResourceManager::GetAssetID(vShaderMod).value_or(0); + subpassName = material.GetPipeline()->GetPipelineState().GetSubpass()->GetName(); + + // Write Properties + Handle pipelineProperties = material.GetShaderBlockInterface(); + for (int i = 0; i < static_cast(pipelineProperties->GetVariableCount()); ++i) + { + const SHShaderBlockInterface::Variable* VARIABLE = pipelineProperties->GetVariable(i); + if (!VARIABLE) + break; + const std::string& VAR_NAME = pipelineProperties->GetVariableName(i); + YAML::Node propNode; + switch (VARIABLE->type) + { + case SHADE::SHShaderBlockInterface::Variable::Type::FLOAT: + propNode = material.GetProperty(VARIABLE->offset); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::INT: + propNode = material.GetProperty(VARIABLE->offset); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR2: + propNode = material.GetProperty(VARIABLE->offset); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR3: + propNode = material.GetProperty(VARIABLE->offset); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR4: + propNode = material.GetProperty(VARIABLE->offset); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::OTHER: + default: + continue; + break; + } + properties[VAR_NAME.data()] = propNode; + } + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h index e41436ce..03928cfa 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h @@ -20,6 +20,14 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Forward Declaration */ + /*-----------------------------------------------------------------------------------*/ + class SHMaterial; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ /*************************************************************************************/ /*! \brief @@ -33,5 +41,11 @@ namespace SHADE AssetID fragShader; std::string subpassName; YAML::Node properties; + + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + SHMaterialSpec() = default; + SHMaterialSpec(const SHMaterial& material); }; } diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index 61689420..bf26c16c 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -13,6 +13,8 @@ of DigiPen Institute of Technology is prohibited. // STL Includes #include + +namespace SHADE { class SHMaterial; } // Project Includes #include "SH_API.h" #include "SHResourceLibrary.h" @@ -24,6 +26,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/MiddleEnd/Textures/SHTextureLibrary.h" #include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h" #include "Graphics/MiddleEnd/Interface/SHMaterial.h" +#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Assets/Asset Types/SHMaterialAsset.h" namespace SHADE @@ -50,9 +53,11 @@ namespace SHADE using AssetType = SHShaderAsset; }; template<> + struct SHResourceLoader { using AssetType = SHMaterialAsset; }; + template<> struct SHResourceLoader { - using AssetType = SHMaterialAsset; + using AssetType = SHMaterialSpec; }; @@ -79,6 +84,8 @@ namespace SHADE /// Handle to a loaded runtime asset. template static Handle LoadOrGet(AssetID assetId); + template<> + static inline Handle LoadOrGet(AssetID assetId); /// /// Unloads an existing loaded asset. Attempting to unload an invalid Handle will /// simply do nothing except emit a warning. diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 15834cdf..aa0a0af9 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -24,6 +24,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/Shaders/SHVkShaderModule.h" #include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" +#include "Serialization/SHSerializationHelper.hpp" namespace SHADE { @@ -34,7 +35,12 @@ namespace SHADE Handle SHResourceManager::LoadOrGet(AssetID assetId) { // Check if it is an unsupported type - if (!std::is_same_v && !std::is_same_v) + if (!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."); } @@ -54,14 +60,41 @@ namespace SHADE } auto handle = load(assetId, *assetData); - Handle genericHandle = Handle(); + Handle genericHandle = Handle(handle); + typedHandleMap.get().emplace(assetId, genericHandle); + typedAssetIdMap.get().emplace(genericHandle, assetId); + return handle; + } + + template<> + Handle SHResourceManager::LoadOrGet(AssetID assetId) + { + /* Attempt to get existing loaded asset */ + auto [typedHandleMap, typedAssetIdMap] = getAssetHandleMap(); + if (typedHandleMap.get().contains(assetId)) + return Handle(typedHandleMap.get()[assetId]); + + /* Otherwise, we need to load it! */ + // Get system + SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem == nullptr) + throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); + + // Get SHMaterialSpec + Handle matSpec = LoadOrGet(assetId); + if (!matSpec) + return {}; + + // Create the material + auto handle = load(assetId, *matSpec); + Handle genericHandle = Handle(handle); typedHandleMap.get().emplace(assetId, genericHandle); typedAssetIdMap.get().emplace(genericHandle, assetId); return handle; } template - void SHResourceManager::Unload(Handle assetId) + void SHResourceManager::Unload(Handle asset) { // Check if it is an unsupported type if (!std::is_same_v && !std::is_same_v) @@ -71,14 +104,18 @@ namespace SHADE /* Attempt to get existing loaded asset */ auto [typedHandleMap, typedAssetIdMap] = getAssetHandleMap(); - if (typedHandleMap.get().contains(assetId)) + if (typedHandleMap.get().contains(asset)) { + // Remove from ResourceHub if SHMaterialSpec + if (std::is_same_v) + resourceHub.Free(asset); + // Dispose - Handle handle = typedHandleMap.get()[assetId]; - Handle typedHandle = static_cast>(handle); + Handle handle = typedHandleMap.get()[asset]; + auto typedHandle = static_cast>(handle); typedHandle.Free(); typedAssetIdMap.get().erase(handle); - typedHandleMap.get().erase(assetId); + typedHandleMap.get().erase(asset); } else { @@ -179,15 +216,20 @@ namespace SHADE shader->Reflect(); return shader; } + // Material Spec + else if constexpr (std::is_same_v) + { + // Get the data we need to construct + auto matSpec = resourceHub.Create(); + *matSpec = YAML::Node(assetData.data).as(); + return matSpec; + } // Materials else if constexpr (std::is_same_v) { - // Get the data we need to construct - SHMaterialSpec matSpec = YAML::Node(assetData.data).as(); - // Load shaders - auto vertexShader = SHResourceManager::LoadOrGet(matSpec.vertexShader); - auto fragShader = SHResourceManager::LoadOrGet(matSpec.fragShader); + auto vertexShader = SHResourceManager::LoadOrGet(assetData.vertexShader); + auto fragShader = SHResourceManager::LoadOrGet(assetData.fragShader); // Ensure that both shaders are present if (!(vertexShader && fragShader)) @@ -203,7 +245,7 @@ namespace SHADE SHLOG_ERROR("[SHResourceManager] Failed to load material as RenderPass could not be found."); return {}; } - auto subPass = renderPass->GetSubpass(matSpec.subpassName); + auto subPass = renderPass->GetSubpass(assetData.subpassName); if (!subPass) { SHLOG_ERROR("[SHResourceManager] Failed to load material as SubPass could not be found."); @@ -218,7 +260,7 @@ namespace SHADE for (int i = 0; i < static_cast(pipelineProperties->GetVariableCount()); ++i) { const std::string& PROP_NAME = pipelineProperties->GetVariableName(i); - const auto& PROP_NODE = matSpec.properties; + const auto& PROP_NODE = assetData.properties; if (PROP_NODE) { const std::string& VAR_NAME = pipelineProperties->GetVariableName(i); diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index 2179d1cf..f2c4572a 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -20,9 +20,9 @@ namespace YAML { - using namespace SHADE; + using namespace SHADE; - template<> + template<> struct convert { static constexpr const char* x = "x"; @@ -172,17 +172,17 @@ namespace YAML switch (colliderType) { case SHCollider::Type::BOX: - { - if(node[HalfExtents].IsDefined()) - rhs.SetBoundingBox(node[HalfExtents].as()); - } - break; + { + if (node[HalfExtents].IsDefined()) + rhs.SetBoundingBox(node[HalfExtents].as()); + } + break; case SHCollider::Type::SPHERE: - { - if(node[Radius].IsDefined()) - rhs.SetBoundingSphere(node[Radius].as()); - } - break; + { + if (node[Radius].IsDefined()) + rhs.SetBoundingSphere(node[Radius].as()); + } + break; case SHCollider::Type::CAPSULE: break; default:; } @@ -194,7 +194,7 @@ namespace YAML rhs.SetDensity(node[Density].as()); if (node[PositionOffset].IsDefined()) rhs.SetPositionOffset(node[PositionOffset].as()); - + return true; } }; @@ -231,7 +231,7 @@ namespace YAML const SHCollider::Type colliderType = enumAlign.name_to_value(colliderNode[convert::Type].as()).convert(&ok); if (!ok) return false; - + switch (colliderType) { case SHCollider::Type::BOX: rhs.AddBoundingBox(); break; @@ -246,143 +246,84 @@ namespace YAML } }; - template<> - struct convert + template<> + struct convert + { + static constexpr std::string_view VERT_SHADER_YAML_TAG = "VertexShader"; + static constexpr std::string_view FRAG_SHADER_YAML_TAG = "FragmentShader"; + static constexpr std::string_view SUBPASS_YAML_TAG = "SubPass"; + static constexpr std::string_view PROPS_YAML_TAG = "Properties"; + + static YAML::Node encode(SHMaterialSpec const& rhs) { - static constexpr std::string_view VERT_SHADER_YAML_TAG = "VertexShader"; - static constexpr std::string_view FRAG_SHADER_YAML_TAG = "FragmentShader"; - static constexpr std::string_view SUBPASS_YAML_TAG = "SubPass"; - static constexpr std::string_view PROPS_YAML_TAG = "Properties"; + YAML::Node node; + node[VERT_SHADER_YAML_TAG.data()] = rhs.vertexShader; + node[FRAG_SHADER_YAML_TAG.data()] = rhs.fragShader; + node[SUBPASS_YAML_TAG.data()] = rhs.subpassName; + node[PROPS_YAML_TAG.data()] = rhs.properties; + return node; + } - static YAML::Node encode(SHMaterial const& rhs) - { - // Write Properties - YAML::Node propertiesNode; - Handle pipelineProperties = rhs.GetShaderBlockInterface(); - for (int i = 0; i < static_cast(pipelineProperties->GetVariableCount()); ++i) - { - const SHShaderBlockInterface::Variable* VARIABLE = pipelineProperties->GetVariable(i); - if (!VARIABLE) - break; - const std::string& VAR_NAME = pipelineProperties->GetVariableName(i); - YAML::Node propNode; - switch (VARIABLE->type) - { - case SHADE::SHShaderBlockInterface::Variable::Type::FLOAT: - propNode = rhs.GetProperty(VARIABLE->offset); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::INT: - propNode = rhs.GetProperty(VARIABLE->offset); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR2: - propNode = rhs.GetProperty(VARIABLE->offset); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR3: - propNode = rhs.GetProperty(VARIABLE->offset); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR4: - propNode = rhs.GetProperty(VARIABLE->offset); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::OTHER: - default: - continue; - break; - } - propertiesNode[VAR_NAME.data()] = propNode; - } - - // Get Shader Handles - const auto& SHADERS = rhs.GetPipeline()->GetPipelineLayout()->GetShaderModules(); - Handle vertexShader, fragShader; - for (const auto& shader : SHADERS) - { - const auto FLAG_BITS = shader->GetShaderStageFlagBits(); - if (FLAG_BITS & vk::ShaderStageFlagBits::eVertex) - vertexShader = shader; - else if (FLAG_BITS & vk::ShaderStageFlagBits::eFragment) - fragShader = shader; - } - - // Write Material - YAML::Node node; - - node[VERT_SHADER_YAML_TAG.data()] = SHResourceManager::GetAssetID(vertexShader).value_or(0); - node[FRAG_SHADER_YAML_TAG.data()] = SHResourceManager::GetAssetID(fragShader).value_or(0); - node[SUBPASS_YAML_TAG.data()] = rhs.GetPipeline()->GetPipelineState().GetSubpass()->GetName(); - node[PROPS_YAML_TAG.data()] = propertiesNode; - - return node; - } - }; - - template<> - struct convert + static bool decode(YAML::Node const& node, SHMaterialSpec& rhs) { - static constexpr std::string_view VERT_SHADER_YAML_TAG = "VertexShader"; - static constexpr std::string_view FRAG_SHADER_YAML_TAG = "FragmentShader"; - static constexpr std::string_view SUBPASS_YAML_TAG = "SubPass"; - static constexpr std::string_view PROPS_YAML_TAG = "Properties"; + // Retrieve Shader Asset IDs + if (!node[VERT_SHADER_YAML_TAG.data()]) + return false; + rhs.vertexShader = node[VERT_SHADER_YAML_TAG.data()].as(); + if (!node[FRAG_SHADER_YAML_TAG.data()]) + return false; + rhs.fragShader = node[FRAG_SHADER_YAML_TAG.data()].as(); - static bool decode(YAML::Node const& node, SHMaterialSpec& rhs) + // Retrieve Subpass + if (!node[SUBPASS_YAML_TAG.data()]) + return false; + rhs.subpassName = node[SUBPASS_YAML_TAG.data()].as(); + + // Retrieve + if (!node[PROPS_YAML_TAG.data()]) + return false; + rhs.properties = node[PROPS_YAML_TAG.data()]; + + return true; + } + }; + + template<> + struct convert + { + static constexpr std::string_view MESH_YAML_TAG = "Mesh"; + static constexpr std::string_view MAT_YAML_TAG = "Material"; + + static YAML::Node encode(SHRenderable const& rhs) + { + YAML::Node node; + node[MESH_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMesh()).value_or(0); + node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMaterial()->GetBaseMaterial()).value_or(0); + return node; + } + static bool decode(YAML::Node const& node, SHRenderable& rhs) + { + if (node[MESH_YAML_TAG.data()].IsDefined()) { - // Retrieve Shader Asset IDs - if (!node[VERT_SHADER_YAML_TAG.data()]) - return false; - rhs.vertexShader = node[VERT_SHADER_YAML_TAG.data()].as(); - if (!node[FRAG_SHADER_YAML_TAG.data()]) - return false; - rhs.fragShader = node[FRAG_SHADER_YAML_TAG.data()].as(); - - // Retrieve Subpass - if (!node[SUBPASS_YAML_TAG.data()]) - return false; - rhs.subpassName = node[SUBPASS_YAML_TAG.data()].as(); - - // Retrieve - if (!node[PROPS_YAML_TAG.data()]) - return false; - rhs.properties = node[PROPS_YAML_TAG.data()]; - - return true; + rhs.SetMesh(SHResourceManager::LoadOrGet(node[MESH_YAML_TAG.data()].as())); } - }; - - template<> - struct convert - { - static constexpr std::string_view MESH_YAML_TAG = "Mesh"; - static constexpr std::string_view MAT_YAML_TAG = "Material"; - - static YAML::Node encode(SHRenderable const& rhs) + if (node[MAT_YAML_TAG.data()].IsDefined()) + { + // Temporarily, use default material + auto gfxSystem = SHSystemManager::GetSystem(); + if (!gfxSystem) + return false; + Handle baseMat = SHResourceManager::LoadOrGet(node[MAT_YAML_TAG.data()].as()); + if (!baseMat) { - YAML::Node node; - node[MESH_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMesh()).value_or(0); - node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMaterial()->GetBaseMaterial()).value_or(0); - return node; + baseMat = gfxSystem->GetDefaultMaterial(); + SHLog::Warning("[SHSerializationHelper] Unable to load specified material. Falling back to default material."); } - static bool decode(YAML::Node const& node, SHRenderable& rhs) - { - if (node[MESH_YAML_TAG.data()].IsDefined()) - { - rhs.SetMesh(SHResourceManager::LoadOrGet(node[MESH_YAML_TAG.data()].as())); - } - if (node[MAT_YAML_TAG.data()].IsDefined()) - { - // Temporarily, use default material - auto gfxSystem = SHSystemManager::GetSystem(); - if (!gfxSystem) - return false; - Handle baseMat = SHResourceManager::LoadOrGet(node[MAT_YAML_TAG.data()].as()); - if (!baseMat) - { - baseMat = gfxSystem->GetDefaultMaterial(); - SHLog::Warning("[SHSerializationHelper] Unable to load specified material. Falling back to default material."); - } - rhs.SetMaterial(gfxSystem->AddOrGetBaseMaterialInstance(baseMat)); - } - return true; - } - }; + rhs.SetMaterial(gfxSystem->AddOrGetBaseMaterialInstance(baseMat)); + } + return true; + } + }; } namespace SHADE @@ -550,7 +491,7 @@ namespace SHADE auto properties = propType.get_properties(); for (auto const& property : properties) { - if(propertyNode[property.get_name().data()].IsDefined()) + if (propertyNode[property.get_name().data()].IsDefined()) InitializeProperty(component, property, propertyNode[property.get_name().data()]); } } @@ -564,7 +505,7 @@ namespace SHADE return; auto rttrType = rttr::type::get(); auto componentNode = componentsNode[rttrType.get_name().data()]; - if(!componentNode.IsDefined()) + if (!componentNode.IsDefined()) return; auto properties = rttrType.get_properties(); for (auto const& prop : properties) From ebc94225e76f1c0ff29b042f90c2342a52ac32c9 Mon Sep 17 00:00:00 2001 From: Brandon Mak Date: Wed, 2 Nov 2022 17:04:31 +0800 Subject: [PATCH 050/113] Merge remote-tracking branch 'origin/main' into SP3-1-Rendering --- Assets/Shaders/DebugDraw_FS.glsl | 17 + Assets/Shaders/DebugDraw_FS.shshaderb | Bin 0 -> 673 bytes Assets/Shaders/DebugDraw_FS.shshaderb.shmeta | 3 + Assets/Shaders/DebugDraw_VS.glsl | 24 + Assets/Shaders/DebugDraw_VS.shshaderb | Bin 0 -> 1513 bytes Assets/Shaders/DebugDraw_VS.shshaderb.shmeta | 3 + Assets/Shaders/DeferredComposite_CS.shshaderb | Bin 5501 -> 5501 bytes .../DeferredComposite_CS.shshaderb.shmeta | 2 +- Assets/Shaders/Kirsch_CS.shshaderb | Bin 5909 -> 5909 bytes Assets/Shaders/PureCopy_CS.shshaderb | Bin 1273 -> 1273 bytes Assets/Shaders/SSAOBlur_CS.shshaderb | Bin 4669 -> 4669 bytes Assets/Shaders/SSAO_CS.shshaderb | Bin 6125 -> 6125 bytes Assets/Shaders/TestCube_FS.shshaderb | Bin 2545 -> 2545 bytes Assets/Shaders/TestCube_FS.shshaderb.shmeta | 2 +- Assets/Shaders/TestCube_VS.shshaderb | Bin 3689 -> 3689 bytes Assets/Shaders/TestCube_VS.shshaderb.shmeta | 2 +- .../src/Application/SBApplication.cpp | 16 +- SHADE_Application/src/Scenes/SBTestScene.cpp | 8 +- SHADE_Engine/premake5.lua | 2 +- .../src/Assets/Asset Types/SHShaderAsset.h | 11 +- .../Compilers/SHShaderSourceCompiler.cpp | 201 +- .../Libraries/Loaders/SHTextBasedLoader.h | 4 + SHADE_Engine/src/Assets/SHAssetMacros.h | 9 +- SHADE_Engine/src/Assets/SHAssetManager.cpp | 6 + .../src/Camera/SHCameraArmComponent.cpp | 68 + .../src/Camera/SHCameraArmComponent.h | 44 + SHADE_Engine/src/Camera/SHCameraComponent.cpp | 2 +- SHADE_Engine/src/Camera/SHCameraComponent.h | 4 +- SHADE_Engine/src/Camera/SHCameraDirector.cpp | 2 + SHADE_Engine/src/Camera/SHCameraDirector.h | 3 +- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 168 +- SHADE_Engine/src/Camera/SHCameraSystem.h | 20 +- .../ECS_Base/Managers/SHComponentManager.cpp | 13 + .../src/ECS_Base/Managers/SHEntityManager.cpp | 11 +- .../src/ECS_Base/Managers/SHEntityManager.h | 2 + .../src/ECS_Base/Managers/SHSystemManager.cpp | 2 +- .../src/Editor/DragDrop/SHDragDrop.hpp | 6 +- .../AssetBrowser/SHAssetBrowser.cpp | 160 +- .../AssetBrowser/SHAssetBrowser.h | 13 +- .../HierarchyPanel/SHHierarchyPanel.cpp | 190 +- .../HierarchyPanel/SHHierarchyPanel.h | 6 +- .../Inspector/SHEditorComponentView.h | 12 + .../Inspector/SHEditorComponentView.hpp | 159 +- .../Inspector/SHEditorInspector.cpp | 39 +- .../EditorWindow/MenuBar/SHEditorMenuBar.cpp | 20 +- .../EditorWindow/MenuBar/SHEditorMenuBar.h | 6 + .../ViewportWindow/SHEditorViewport.cpp | 89 +- .../ViewportWindow/SHEditorViewport.h | 3 +- .../src/Editor/Gizmos/SHTransformGizmo.cpp | 5 +- SHADE_Engine/src/Editor/SHEditor.cpp | 6 +- .../src/Editor/{SHEditor.hpp => SHEditor.h} | 4 +- SHADE_Engine/src/Editor/SHEditorUI.cpp | 46 +- SHADE_Engine/src/Editor/SHEditorUI.h | 8 + SHADE_Engine/src/Editor/SHEditorWidgets.hpp | 122 +- SHADE_Engine/src/Events/SHEventDefines.h | 3 + .../Graphics/Commands/SHVkCommandBuffer.cpp | 5 + .../src/Graphics/Commands/SHVkCommandBuffer.h | 1 + .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 11 +- .../MiddleEnd/Batching/SHSuperBatch.cpp | 6 +- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 202 + .../MiddleEnd/Interface/SHDebugDrawSystem.h | 159 + .../MiddleEnd/Interface/SHDebugDrawSystem.hpp | 48 + .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 111 +- .../MiddleEnd/Interface/SHGraphicsSystem.h | 32 +- .../Interface/SHGraphicsSystemInterface.cpp | 77 + .../Interface/SHGraphicsSystemInterface.h | 52 + .../MiddleEnd/Materials/SHMaterialSpec.h | 36 + .../MiddleEnd/Meshes/SHPrimitiveGenerator.cpp | 165 +- .../MiddleEnd/Meshes/SHPrimitiveGenerator.h | 65 +- .../Shaders/SHShaderModuleLibrary.cpp | 139 - .../MiddleEnd/Shaders/SHShaderModuleLibrary.h | 44 - .../src/Graphics/Pipeline/SHVkPipeline.h | 3 +- .../src/Graphics/Windowing/SHWindow.cpp | 41 +- .../src/Graphics/Windowing/SHWindow.h | 9 +- SHADE_Engine/src/Math/SHColour.cpp | 172 +- SHADE_Engine/src/Math/SHColour.h | 50 +- SHADE_Engine/src/Math/SHMath.h | 2 + .../Math/Transform/SHTransformComponent.cpp | 7 +- .../src/Math/Transform/SHTransformSystem.cpp | 6 +- SHADE_Engine/src/Math/Vector/SHVec4.cpp | 1 + SHADE_Engine/src/Math/Vector/SHVec4.h | 2 + .../Components/SHColliderComponent.cpp | 10 +- .../Physics/Components/SHColliderComponent.h | 1 + .../Components/SHRigidBodyComponent.cpp | 317 +- .../Physics/Components/SHRigidBodyComponent.h | 31 +- SHADE_Engine/src/Physics/SHCollider.cpp | 15 +- SHADE_Engine/src/Physics/SHCollider.h | 5 +- SHADE_Engine/src/Physics/SHPhysicsObject.cpp | 106 +- SHADE_Engine/src/Physics/SHPhysicsObject.h | 1 - SHADE_Engine/src/Physics/SHPhysicsSystem.cpp | 276 +- SHADE_Engine/src/Physics/SHPhysicsSystem.h | 91 +- SHADE_Engine/src/Physics/SHPhysicsSystem.hpp | 84 + .../src/Physics/SHPhysicsSystemInterface.cpp | 65 + .../src/Physics/SHPhysicsSystemInterface.h | 46 + SHADE_Engine/src/Physics/SHPhysicsUtils.cpp | 93 + SHADE_Engine/src/Physics/SHPhysicsUtils.h | 116 + .../src/Reflection/SHReflectionMetadata.h | 1 + .../src/Resource/SHResourceManager.cpp | 17 +- SHADE_Engine/src/Resource/SHResourceManager.h | 31 +- .../src/Resource/SHResourceManager.hpp | 206 +- SHADE_Engine/src/Scripting/SHScriptEngine.cpp | 11 +- SHADE_Engine/src/Scripting/SHScriptEngine.h | 6 + .../src/Scripting/SHScriptEngineRoutines.cpp | 2 +- .../Serialization/Prefab/SHPrefabManager.cpp | 56 + .../Serialization/Prefab/SHPrefabManager.h | 28 + .../src/Serialization/SHSerialization.cpp | 107 +- .../Serialization/SHSerializationHelper.hpp | 432 +- SHADE_Engine/src/Tools/Dialog/SHWinDialog.cpp | 71 + SHADE_Engine/src/Tools/Dialog/SHWinDialog.h | 36 + SHADE_Engine/src/Tools/SHDebugDraw.cpp | 73 + SHADE_Engine/src/Tools/SHDebugDraw.h | 101 + SHADE_Engine/src/Tools/SHLog.h | 2 +- SHADE_Managed/premake5.lua | 10 +- SHADE_Managed/src/Components/Camera.cxx | 127 + SHADE_Managed/src/Components/Camera.hxx | 70 + SHADE_Managed/src/Components/CameraArm.cxx | 51 + SHADE_Managed/src/Components/CameraArm.hxx | 40 + SHADE_Managed/src/Components/Component.cxx | 5 + SHADE_Managed/src/Components/Component.hxx | 9 + SHADE_Managed/src/Components/RigidBody.cxx | 8 + SHADE_Managed/src/Components/RigidBody.hxx | 5 + SHADE_Managed/src/Components/Transform.cxx | 26 - SHADE_Managed/src/Components/Transform.hxx | 24 - SHADE_Managed/src/Editor/Editor.cxx | 15 +- SHADE_Managed/src/Engine/Application.cxx | 71 + SHADE_Managed/src/Engine/Application.hxx | 77 + SHADE_Managed/src/Engine/ECS.cxx | 4 + SHADE_Managed/src/Engine/GameObject.cxx | 50 +- SHADE_Managed/src/Engine/GameObject.hxx | 19 +- SHADE_Managed/src/Engine/Time.cxx | 13 + SHADE_Managed/src/Engine/Time.hxx | 23 +- SHADE_Managed/src/Input/Input.cxx | 7 + SHADE_Managed/src/Input/Input.hxx | 154 +- SHADE_Managed/src/Math/Vector2.cxx | 16 + SHADE_Managed/src/Math/Vector2.hxx | 16 + SHADE_Managed/src/Math/Vector3.cxx | 18 + SHADE_Managed/src/Math/Vector3.hxx | 16 + SHADE_Managed/src/Physics/CollisionInfo.cxx | 36 + SHADE_Managed/src/Physics/CollisionInfo.hxx | 66 + SHADE_Managed/src/Scripts/Script.cxx | 81 +- SHADE_Managed/src/Scripts/Script.hxx | 92 +- SHADE_Managed/src/Scripts/ScriptStore.cxx | 132 +- SHADE_Managed/src/Scripts/ScriptStore.hxx | 4 + .../src/Serialisation/ReflectionUtilities.cxx | 10 + TempScriptsFolder/CameraControl.cs | 22 + TempScriptsFolder/PhysicsTest.cs | 31 +- TempScriptsFolder/PrintWhenActive.cs | 11 + TempScriptsFolder/ThirdPersonCamera.cs | 55 + TempShaderFolder/DeferredCompositeCs.glsl | 83 - TempShaderFolder/DeferredCompositeCs.spv | Bin 4656 -> 0 bytes TempShaderFolder/TestCubeFs.spv | Bin 2536 -> 0 bytes TempShaderFolder/TestCubeVs.spv | Bin 3296 -> 0 bytes bin/Debug/SHADE_CSharp.xml | 1029 +++ bin/Debug/SHADE_Managed.xml | 6250 +++++++++++++++++ bin/Release/SHADE_CSharp.xml | 1029 +++ bin/Release/SHADE_Managed.xml | 6250 +++++++++++++++++ 156 files changed, 20008 insertions(+), 1476 deletions(-) create mode 100644 Assets/Shaders/DebugDraw_FS.glsl create mode 100644 Assets/Shaders/DebugDraw_FS.shshaderb create mode 100644 Assets/Shaders/DebugDraw_FS.shshaderb.shmeta create mode 100644 Assets/Shaders/DebugDraw_VS.glsl create mode 100644 Assets/Shaders/DebugDraw_VS.shshaderb create mode 100644 Assets/Shaders/DebugDraw_VS.shshaderb.shmeta create mode 100644 SHADE_Engine/src/Camera/SHCameraArmComponent.cpp create mode 100644 SHADE_Engine/src/Camera/SHCameraArmComponent.h create mode 100644 SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.h rename SHADE_Engine/src/Editor/{SHEditor.hpp => SHEditor.h} (98%) create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.cpp create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.h create mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h delete mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.cpp delete mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.h create mode 100644 SHADE_Engine/src/Physics/SHPhysicsSystem.hpp create mode 100644 SHADE_Engine/src/Physics/SHPhysicsSystemInterface.cpp create mode 100644 SHADE_Engine/src/Physics/SHPhysicsSystemInterface.h create mode 100644 SHADE_Engine/src/Physics/SHPhysicsUtils.cpp create mode 100644 SHADE_Engine/src/Physics/SHPhysicsUtils.h create mode 100644 SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.cpp create mode 100644 SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.h create mode 100644 SHADE_Engine/src/Tools/Dialog/SHWinDialog.cpp create mode 100644 SHADE_Engine/src/Tools/Dialog/SHWinDialog.h create mode 100644 SHADE_Engine/src/Tools/SHDebugDraw.cpp create mode 100644 SHADE_Engine/src/Tools/SHDebugDraw.h create mode 100644 SHADE_Managed/src/Components/Camera.cxx create mode 100644 SHADE_Managed/src/Components/Camera.hxx create mode 100644 SHADE_Managed/src/Components/CameraArm.cxx create mode 100644 SHADE_Managed/src/Components/CameraArm.hxx create mode 100644 SHADE_Managed/src/Engine/Application.cxx create mode 100644 SHADE_Managed/src/Engine/Application.hxx create mode 100644 SHADE_Managed/src/Physics/CollisionInfo.cxx create mode 100644 SHADE_Managed/src/Physics/CollisionInfo.hxx create mode 100644 TempScriptsFolder/CameraControl.cs create mode 100644 TempScriptsFolder/PrintWhenActive.cs create mode 100644 TempScriptsFolder/ThirdPersonCamera.cs delete mode 100644 TempShaderFolder/DeferredCompositeCs.glsl delete mode 100644 TempShaderFolder/DeferredCompositeCs.spv delete mode 100644 TempShaderFolder/TestCubeFs.spv delete mode 100644 TempShaderFolder/TestCubeVs.spv create mode 100644 bin/Debug/SHADE_CSharp.xml create mode 100644 bin/Debug/SHADE_Managed.xml create mode 100644 bin/Release/SHADE_CSharp.xml create mode 100644 bin/Release/SHADE_Managed.xml diff --git a/Assets/Shaders/DebugDraw_FS.glsl b/Assets/Shaders/DebugDraw_FS.glsl new file mode 100644 index 00000000..266f8ad4 --- /dev/null +++ b/Assets/Shaders/DebugDraw_FS.glsl @@ -0,0 +1,17 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +#extension GL_EXT_nonuniform_qualifier : require + +layout(location = 0) in struct +{ + vec4 vertColor; +} In; + + +layout(location = 0) out vec4 outColor; + +void main() +{ + outColor = In.vertColor; +} \ No newline at end of file diff --git a/Assets/Shaders/DebugDraw_FS.shshaderb b/Assets/Shaders/DebugDraw_FS.shshaderb new file mode 100644 index 0000000000000000000000000000000000000000..7b59688597046295970cf20f6afdc8f52cb72e81 GIT binary patch literal 673 zcmY*W%Sr=55Nz`{F)u~Eg@{KX1c@SuLX@xw3jy`uZCH~@9Np}$`v~|aeve<}MX+kK zkvP!PQ`OboGd-=(oTMwCJ1&cCNmc5yyP~8mIWQtMDW%@BAM9QfPkV#2J|zvwr%Gy? zQkDWdZQ&S?fo<3htOXr;G{7m3sv=)VDnMb`YIuL6A{{uP6KfT{I8z-e|M^u<;^@By zA>+-J>v;3TnQJvT?FY`}4RtE4GkSVdp6?}|nfc*Dy(f-qW=4mIR~fg)W6K_?Nf4+g z{&2N&ji=Srgy>|Jn!VCGna+3TBS*XZ4b+Q~{#@2Wb)8f32(#c7BKESyIt_wSao^vJU-MZK-@cbF_ml&r1ts&^F!I;P;5~_dQ1t@#3!m>%egFUf literal 0 HcmV?d00001 diff --git a/Assets/Shaders/DebugDraw_FS.shshaderb.shmeta b/Assets/Shaders/DebugDraw_FS.shshaderb.shmeta new file mode 100644 index 00000000..58f62b44 --- /dev/null +++ b/Assets/Shaders/DebugDraw_FS.shshaderb.shmeta @@ -0,0 +1,3 @@ +Name: DebugDraw_FS +ID: 36671027 +Type: 2 diff --git a/Assets/Shaders/DebugDraw_VS.glsl b/Assets/Shaders/DebugDraw_VS.glsl new file mode 100644 index 00000000..cb0886d1 --- /dev/null +++ b/Assets/Shaders/DebugDraw_VS.glsl @@ -0,0 +1,24 @@ +#version 450 +#extension GL_KHR_vulkan_glsl : enable + +layout(location = 0) in vec4 aVertexPos; +layout(location = 1) in vec4 aVertColor; + + +layout(location = 0) out struct +{ + vec4 vertColor; // location 0 + +} Out; + +layout(set = 2, binding = 0) uniform CameraData +{ + vec4 position; + mat4 vpMat; +} cameraData; + +void main() +{ + gl_Position = cameraData.vpMat * vec4 (aVertexPos.xyz, 1.0f); + Out.vertColor = aVertColor; +} \ No newline at end of file diff --git a/Assets/Shaders/DebugDraw_VS.shshaderb b/Assets/Shaders/DebugDraw_VS.shshaderb new file mode 100644 index 0000000000000000000000000000000000000000..2d8cd0edd9ff60919d7c1931f06ee0ea6390bff0 GIT binary patch literal 1513 zcmZ9L-)a*<6vn4XH?6I0t+jvD8k0t?S}0zq2%;jXY%d~dDR{dqn?(k;n~-dRdgUwl zN(;o82j)6VA>#-}&bJo5{kTz_?Yfb(V}-Fe_%w9F>sSG&Lc{teS?~!`?;j zcv1|GU%q+)W!=;r6V8UY;q*m)FJqfytV!-l4kb?|b^U7Ef9t@aGKIY;?DxaorzjbX zqDk>ROQX!@X*96oG%4&Xg||G%vw4zD2lIqLjQN~_YF$;1uUT}Kj=!YiBK;;)Xo;ia z8%}Ithq=%t$EOE}+q}52Ka`m>rH`8B>t=RzVkbqMC+U@s3E-P%*_AI)pY+ZO^xM?l zjW5%2d=eLNSqm6z zhGXGyxQqU@kRG@@aWN{wsW4{8^1H{V35?xi%ms`YcnlqkI|=1Mhlk(y+>y;X6$^}* zfwM~vHk@16Fg;L*5tnns4Hbj=99*;8vRU|%IU diff --git a/Assets/Shaders/SSAO_CS.shshaderb b/Assets/Shaders/SSAO_CS.shshaderb index 6bb4abcd8018cac584ecc8f8b59b86984eb528d7..69f7a44f1a9f7144e912c70a533a67027a7e737b 100644 GIT binary patch delta 10 RcmaE>|5l$-VI$)kaR3|5l%oX(QtsaR3-<1MvU= diff --git a/Assets/Shaders/TestCube_FS.shshaderb b/Assets/Shaders/TestCube_FS.shshaderb index b0113dc7799a94fb1c40720187c05a9b26a2617c..95b4f62ae89c1bd3dc23f684757a7298c60833a3 100644 GIT binary patch delta 10 Rcmew;{85-uU?bxPP5>7&1H}LU delta 10 Rcmew;{85;ZaU6?1GWGF diff --git a/Assets/Shaders/TestCube_FS.shshaderb.shmeta b/Assets/Shaders/TestCube_FS.shshaderb.shmeta index 42f270af..fbe098b1 100644 --- a/Assets/Shaders/TestCube_FS.shshaderb.shmeta +++ b/Assets/Shaders/TestCube_FS.shshaderb.shmeta @@ -1,3 +1,3 @@ Name: TestCube_FS -ID: 37450402 +ID: 46377769 Type: 2 diff --git a/Assets/Shaders/TestCube_VS.shshaderb b/Assets/Shaders/TestCube_VS.shshaderb index fb2965a83900e01b2591230c1de83828ad72c658..fb282b4df95c458e637710753ab4b5beb88c23e2 100644 GIT binary patch delta 10 RcmaDU^HPS9aU){}9{?5H12q5u delta 10 RcmaDU^HPS9VIyM(9{?5C12g~t diff --git a/Assets/Shaders/TestCube_VS.shshaderb.shmeta b/Assets/Shaders/TestCube_VS.shshaderb.shmeta index b133437b..5c9f895f 100644 --- a/Assets/Shaders/TestCube_VS.shshaderb.shmeta +++ b/Assets/Shaders/TestCube_VS.shshaderb.shmeta @@ -1,3 +1,3 @@ Name: TestCube_VS -ID: 41688429 +ID: 39210065 Type: 2 diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index de548060..b865f028 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -4,7 +4,7 @@ //#define SHEDITOR #ifdef SHEDITOR -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" //#include "Scenes/SBEditorScene.h" #endif // SHEDITOR @@ -31,6 +31,7 @@ #include "FRC/SHFramerateController.h" #include "AudioSystem/SHAudioSystem.h" #include "Camera/SHCameraSystem.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" // Components #include "Graphics/MiddleEnd/Interface/SHRenderable.h" @@ -42,6 +43,7 @@ #include "Assets/SHAssetManager.h" #include "Tools/SHLogger.h" +#include "Tools/SHDebugDraw.h" using namespace SHADE; @@ -69,6 +71,7 @@ namespace Sandbox SHGraphicsSystem* graphicsSystem = static_cast(SHSystemManager::GetSystem()); SHSystemManager::CreateSystem(); SHSystemManager::CreateSystem(); + SHSystemManager::CreateSystem(); #ifdef SHEDITOR SDL_Init(SDL_INIT_VIDEO); @@ -90,11 +93,12 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); - SHSystemManager::RegisterRoutine(); + //SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); #ifdef SHEDITOR @@ -108,13 +112,10 @@ namespace Sandbox SHComponentManager::CreateComponentSparseSet(); SHComponentManager::CreateComponentSparseSet(); SHComponentManager::CreateComponentSparseSet(); - SHComponentManager::CreateComponentSparseSet(); + //SHComponentManager::CreateComponentSparseSet(); SHAssetManager::Load(); - auto id = SHFamilyID::GetID(); - auto id2 = SHFamilyID::GetID(); - auto id3 = SHFamilyID::GetID(); SHSystemManager::RegisterRoutine(); @@ -126,6 +127,9 @@ namespace Sandbox SHSceneManager::InitSceneManager("TestScene"); SHFrameRateController::UpdateFRC(); + + // Link up SHDebugDraw + SHDebugDraw::Init(SHSystemManager::GetSystem()); } void SBApplication::Update(void) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index 23b57dfc..a5b3b546 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -16,7 +16,10 @@ #include "Assets/SHAssetManager.h" #include "Camera/SHCameraComponent.h" +#include "Math/SHColour.h" #include "Resource/SHResourceManager.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" +#include "Tools/SHDebugDraw.h" using namespace SHADE; @@ -128,7 +131,6 @@ namespace Sandbox floorRigidBody.SetType(SHRigidBodyComponent::Type::STATIC); auto* floorBox = floorCollider.AddBoundingBox(); - floorBox->SetHalfExtents(floorTransform.GetWorldScale() * 0.5f); // Create blank entity with a script //testObj = SHADE::SHEntityManager::CreateEntity(); @@ -160,7 +162,7 @@ namespace Sandbox SHComponentManager::RemoveComponent (0); auto ambientLight = SHEntityManager::CreateEntity(); - SHComponentManager::GetComponent(ambientLight)->SetColor(SHVec4(1.0f, 1.0f, 1.0f, 1.0f)); + SHComponentManager::GetComponent(ambientLight)->SetColor(SHColour::WHITE); SHComponentManager::GetComponent(ambientLight)->SetStrength(0.25f); SHComponentManager::GetComponent(ambientLight)->SetType(SH_LIGHT_TYPE::AMBIENT); } @@ -187,6 +189,8 @@ namespace Sandbox SHADE::SHScriptEngine* scriptEngine = static_cast(SHADE::SHSystemManager::GetSystem()); scriptEngine->RemoveAllScripts(testObj); } + + SHDebugDraw::Cube(SHColour::CRIMSON, SHVec3(1.0f, 0.0f, 0.0f), SHVec3(1.0f, 1.0f, 1.0f)); } void SBTestScene::Render() diff --git a/SHADE_Engine/premake5.lua b/SHADE_Engine/premake5.lua index 18920194..bc1f6a03 100644 --- a/SHADE_Engine/premake5.lua +++ b/SHADE_Engine/premake5.lua @@ -8,7 +8,7 @@ project "SHADE_Engine" pchheader "SHpch.h" pchsource "%{prj.location}/src/SHpch.cpp" staticruntime "off" - + buildoptions{"/bigobj"} files { "%{prj.location}/src/**.h", diff --git a/SHADE_Engine/src/Assets/Asset Types/SHShaderAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHShaderAsset.h index adcd215a..d71c1bb3 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHShaderAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/SHShaderAsset.h @@ -10,6 +10,7 @@ *****************************************************************************/ #pragma once +#include #include "SHAssetData.h" #include "SH_API.h" #include @@ -17,12 +18,14 @@ namespace SHADE { + //! Tighter control over types of shaders. Maps directly to their + //! equivalent vk::ShaderStageFlagBits. enum class SH_SHADER_TYPE : uint8_t { - VERTEX, - FRAGMENT, - COMPUTE, - INAVLID_TYPE + VERTEX = static_cast(vk::ShaderStageFlagBits::eVertex), + FRAGMENT = static_cast(vk::ShaderStageFlagBits::eFragment), + COMPUTE = static_cast(vk::ShaderStageFlagBits::eCompute), + INAVLID_TYPE = std::numeric_limits::max() }; struct SH_API SHShaderAsset : SHAssetData diff --git a/SHADE_Engine/src/Assets/Libraries/Compilers/SHShaderSourceCompiler.cpp b/SHADE_Engine/src/Assets/Libraries/Compilers/SHShaderSourceCompiler.cpp index 329e34e9..fd1f6d8a 100644 --- a/SHADE_Engine/src/Assets/Libraries/Compilers/SHShaderSourceCompiler.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Compilers/SHShaderSourceCompiler.cpp @@ -19,144 +19,145 @@ namespace SHADE { - std::string SHShaderSourceCompiler::CompileShaderSourceToBinary(AssetPath path, SHShaderAsset const& data) noexcept - { - std::string newPath{ path.string() }; + std::string SHShaderSourceCompiler::CompileShaderSourceToBinary(AssetPath path, SHShaderAsset const& data) noexcept + { + std::string newPath{ path.string() }; newPath = newPath.substr(0, newPath.find_last_of('.')); newPath += SHADER_BUILT_IN_EXTENSION.data(); - std::ofstream file{ newPath, std::ios::binary | std::ios::out | std::ios::trunc }; + std::ofstream file{ newPath, std::ios::binary | std::ios::out | std::ios::trunc }; - file.write( - reinterpret_cast(& data.shaderType), sizeof(uint8_t) - ); + file.write( + reinterpret_cast(& data.shaderType), sizeof(uint8_t) + ); - size_t const byteCount = sizeof(uint32_t) * data.spirvBinary.size(); + size_t const byteCount = sizeof(uint32_t) * data.spirvBinary.size(); - file.write( - reinterpret_cast(&byteCount), sizeof(size_t) - ); + file.write( + reinterpret_cast(&byteCount), sizeof(size_t) + ); - file.write( - reinterpret_cast(data.spirvBinary.data()), byteCount - ); + file.write( + reinterpret_cast(data.spirvBinary.data()), byteCount + ); - file.close(); + file.close(); - return newPath; - } + return newPath; + } - SHShaderAsset const* SHShaderSourceCompiler::CompileShaderSourceToMemory(std::string const& data, std::string const& name, SH_SHADER_TYPE type) noexcept - { - // shaderc compiler - shaderc::Compiler compiler; - shaderc::CompileOptions options; + SHShaderAsset const* SHShaderSourceCompiler::CompileShaderSourceToMemory(std::string const& data, std::string const& name, SH_SHADER_TYPE type) noexcept + { + // shaderc compiler + shaderc::Compiler compiler; + shaderc::CompileOptions options; - options.AddMacroDefinition("MY_DEFINE", "1"); + options.AddMacroDefinition("MY_DEFINE", "1"); - //TODO: Check if we need optimisation levels when compiling into spirv - // Set optimization levels - //if (opLevel != shaderc_optimization_level_zero) - // options.SetOptimizationLevel(opLevel); + //TODO: Check if we need optimisation levels when compiling into spirv + // Set optimization levels + //if (opLevel != shaderc_optimization_level_zero) + // options.SetOptimizationLevel(opLevel); - // Attempt to get the shaderc equivalent shader stage - shaderc_shader_kind shaderKind; - switch (type) - { - case SH_SHADER_TYPE::VERTEX: - shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader; - break; - case SH_SHADER_TYPE::FRAGMENT: - shaderKind = shaderc_shader_kind::shaderc_glsl_fragment_shader; - break; - case SH_SHADER_TYPE::COMPUTE: - shaderKind = shaderc_shader_kind::shaderc_glsl_compute_shader; - break; - default: - shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader; - break; - } + // Attempt to get the shaderc equivalent shader stage + shaderc_shader_kind shaderKind; + switch (type) + { + case SH_SHADER_TYPE::VERTEX: + shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader; + break; + case SH_SHADER_TYPE::FRAGMENT: + shaderKind = shaderc_shader_kind::shaderc_glsl_fragment_shader; + break; + case SH_SHADER_TYPE::COMPUTE: + shaderKind = shaderc_shader_kind::shaderc_glsl_compute_shader; + break; + default: + shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader; + break; + } - // Compile the shader and get the result - shaderc::SpvCompilationResult compileResult = compiler.CompileGlslToSpv(data, shaderKind, name.c_str(), options); + // Compile the shader and get the result + shaderc::SpvCompilationResult compileResult = compiler.CompileGlslToSpv(data, shaderKind, name.c_str(), options); - if (compileResult.GetCompilationStatus() != shaderc_compilation_status_success) - { - SHLOG_ERROR("Shaderc failed to compile GLSL shader to binary | " + compileResult.GetErrorMessage()); - return nullptr; - } + if (compileResult.GetCompilationStatus() != shaderc_compilation_status_success) + { + SHLOG_ERROR("Shaderc failed to compile GLSL shader to binary | " + compileResult.GetErrorMessage()); + return nullptr; + } - auto result = new SHShaderAsset(); - result->spirvBinary.resize(compileResult.end() - compileResult.begin()); + auto result = new SHShaderAsset(); + result->spirvBinary.resize(compileResult.end() - compileResult.begin()); - std::ranges::copy(compileResult.begin(), compileResult.end(), result->spirvBinary.data()); + std::ranges::copy(compileResult.begin(), compileResult.end(), result->spirvBinary.data()); result->name = name; result->shaderType = type; - return result; - } + return result; + } - SH_SHADER_TYPE SHShaderSourceCompiler::GetShaderTypeFromFilename(std::string name) noexcept - { - for (auto i { 0}; i < SHADER_TYPE_MAX_COUNT; ++i) - { - if (name.find(SHADER_IDENTIFIERS[i].data()) != std::string::npos) - { - return static_cast(i); - } - } + SH_SHADER_TYPE SHShaderSourceCompiler::GetShaderTypeFromFilename(std::string name) noexcept + { + for (auto i { 0 }; i < SHADER_TYPE_MAX_COUNT; ++i) + { + const auto& [SHADER_SUFFIX, SHADER_TYPE] = SHADER_IDENTIFIERS[i]; + if (name.find(SHADER_SUFFIX.data()) != std::string::npos) + { + return SHADER_TYPE; + } + } - return SH_SHADER_TYPE::INAVLID_TYPE; - } + return SH_SHADER_TYPE::INAVLID_TYPE; + } std::optional SHShaderSourceCompiler::LoadAndCompileShader(AssetPath path) noexcept - { - auto type = GetShaderTypeFromFilename(path.filename().string()); + { + auto type = GetShaderTypeFromFilename(path.filename().string()); - if (type == SH_SHADER_TYPE::INAVLID_TYPE) - { - SHLOG_ERROR("Invalid filename for shaders, follow suffix in SHAssetMacros.h: {}", path.string()); - return {}; - } + if (type == SH_SHADER_TYPE::INAVLID_TYPE) + { + SHLOG_ERROR("Invalid filename for shaders, follow suffix in SHAssetMacros.h: {}", path.string()); + return {}; + } path.make_preferred(); - std::ifstream file{ path.string(), std::ios::in }; + std::ifstream file{ path.string(), std::ios::in }; - if (file.is_open()) - { - std::stringstream stream; + if (file.is_open()) + { + std::stringstream stream; - stream << file.rdbuf(); + stream << file.rdbuf(); - std::string const content = stream.str(); + std::string const content = stream.str(); - auto data = CompileShaderSourceToMemory(content, path.filename().string(), type); + auto data = CompileShaderSourceToMemory(content, path.filename().string(), type); - if (data == nullptr) - { - return{}; - } + if (data == nullptr) + { + return{}; + } - return CompileShaderSourceToBinary(path, *data); - } + return CompileShaderSourceToBinary(path, *data); + } - SHLOG_ERROR("Unable to open shader file: {}", path.string()); + SHLOG_ERROR("Unable to open shader file: {}", path.string()); - return {}; - } + return {}; + } - std::optional SHShaderSourceCompiler::CompileShaderFromString - (std::string const& string, AssetPath path, SH_SHADER_TYPE type) noexcept - { - auto const data = CompileShaderSourceToMemory(string, path.filename().string(), type); + std::optional SHShaderSourceCompiler::CompileShaderFromString + (std::string const& string, AssetPath path, SH_SHADER_TYPE type) noexcept + { + auto const data = CompileShaderSourceToMemory(string, path.filename().string(), type); - if (data == nullptr) - { - return{}; - } + if (data == nullptr) + { + return{}; + } - return CompileShaderSourceToBinary(path, *data); - } + return CompileShaderSourceToBinary(path, *data); + } } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.h index 80771058..b74c6c94 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHTextBasedLoader.h @@ -11,6 +11,10 @@ #pragma once #include "SHAssetLoader.h" +#include "Assets/Asset Types/SHPrefabAsset.h" +#include "Assets/Asset Types/SHSceneAsset.h" +#include "Assets/Asset Types/SHMaterialAsset.h" + namespace SHADE { struct SHTextBasedLoader : SHAssetLoader diff --git a/SHADE_Engine/src/Assets/SHAssetMacros.h b/SHADE_Engine/src/Assets/SHAssetMacros.h index a21a9840..3376a2d5 100644 --- a/SHADE_Engine/src/Assets/SHAssetMacros.h +++ b/SHADE_Engine/src/Assets/SHAssetMacros.h @@ -13,6 +13,7 @@ #include #include #include +#include "Asset Types/SHShaderAsset.h" // FMOD Fwd Declare namespace FMOD @@ -113,10 +114,10 @@ constexpr std::string_view VERTEX_SHADER{ "_VS" }; constexpr std::string_view FRAGMENT_SHADER{ "_FS" }; constexpr std::string_view COMPUTER_SHADER{ "_CS" }; -constexpr std::string_view SHADER_IDENTIFIERS[] = { - VERTEX_SHADER, - FRAGMENT_SHADER, - COMPUTER_SHADER +constexpr std::pair SHADER_IDENTIFIERS[] = { + std::make_pair(VERTEX_SHADER, SHADE::SH_SHADER_TYPE::VERTEX), + std::make_pair(FRAGMENT_SHADER, SHADE::SH_SHADER_TYPE::FRAGMENT), + std::make_pair(COMPUTER_SHADER, SHADE::SH_SHADER_TYPE::COMPUTE) }; constexpr size_t SHADER_TYPE_MAX_COUNT{ 3 }; diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index 682eb9ec..4897fc42 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -151,19 +151,23 @@ namespace SHADE ****************************************************************************/ AssetID SHAssetManager::CreateNewAsset(AssetType type, AssetName name) noexcept { + SHAssetData* data = nullptr; std::string newPath{ ASSET_ROOT }; switch (type) { case AssetType::PREFAB: newPath += PREFAB_FOLDER; + data = new SHPrefabAsset(); break; case AssetType::SCENE: newPath += SCENE_FOLDER; + data = new SHSceneAsset(); break; case AssetType::MATERIAL: newPath += MATERIAL_FOLDER; + data = new SHMaterialAsset(); break; default: @@ -189,6 +193,8 @@ namespace SHADE ) }); + assetData.emplace(id, data); + return id; } diff --git a/SHADE_Engine/src/Camera/SHCameraArmComponent.cpp b/SHADE_Engine/src/Camera/SHCameraArmComponent.cpp new file mode 100644 index 00000000..9cb221ff --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraArmComponent.cpp @@ -0,0 +1,68 @@ +#include "SHpch.h" +#include "SHCameraArmComponent.h" + + + +namespace SHADE +{ + + SHCameraArmComponent::SHCameraArmComponent() + :pitch(0.0f), yaw(0.0f), armLength(1.0f),offset(), dirty(true), lookAtCameraOrigin(true) + { + + } + + + SHVec3 const& SHCameraArmComponent::GetOffset() const noexcept + { + return offset; + } + + float SHCameraArmComponent::GetPitch() const noexcept + { + return pitch; + } + + float SHCameraArmComponent::GetYaw() const noexcept + { + return yaw; + } + + float SHCameraArmComponent::GetArmLength() const noexcept + { + return armLength; + } + + void SHCameraArmComponent::SetPitch(float pitch) noexcept + { + this->pitch = pitch; + dirty = true; + } + + void SHCameraArmComponent::SetYaw(float yaw) noexcept + { + this->yaw = yaw; + dirty = true; + } + + void SHCameraArmComponent::SetArmLength(float length) noexcept + { + this->armLength = length; + dirty = true; + } + +}//namespace SHADE + + +RTTR_REGISTRATION +{ + using namespace SHADE; + using namespace rttr; + + registration::class_("Camera Arm Component") + .property("Arm Pitch", &SHCameraArmComponent::GetPitch, &SHCameraArmComponent::SetPitch) + .property("Arm Yaw", &SHCameraArmComponent::GetYaw, &SHCameraArmComponent::SetYaw) + .property("Arm Length", &SHCameraArmComponent::GetArmLength, &SHCameraArmComponent::SetArmLength) + .property("Look At Camera Origin", &SHCameraArmComponent::lookAtCameraOrigin); + +} \ No newline at end of file diff --git a/SHADE_Engine/src/Camera/SHCameraArmComponent.h b/SHADE_Engine/src/Camera/SHCameraArmComponent.h new file mode 100644 index 00000000..2b81a808 --- /dev/null +++ b/SHADE_Engine/src/Camera/SHCameraArmComponent.h @@ -0,0 +1,44 @@ +#pragma once + + +#include +#include "ECS_Base/Components/SHComponent.h" +#include "Math/SHMatrix.h" +#include "SH_API.h" + +namespace SHADE +{ + class SH_API SHCameraArmComponent final: public SHComponent + { + private: + float pitch; + float yaw; + float armLength; + + bool dirty; + SHVec3 offset; + + public: + friend class SHCameraSystem; + SHCameraArmComponent(); + virtual ~SHCameraArmComponent() = default; + + bool lookAtCameraOrigin; + //Getters + //SHMatrix const& GetMatrix() const noexcept; + SHVec3 const& GetOffset() const noexcept; + float GetPitch() const noexcept; + float GetYaw() const noexcept; + float GetArmLength() const noexcept; + + //Setters + void SetPitch(float pitch) noexcept; + void SetYaw(float yaw) noexcept; + void SetArmLength(float length) noexcept; + + protected: + + + }; + +}//namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.cpp b/SHADE_Engine/src/Camera/SHCameraComponent.cpp index 31afe2ac..ac451df5 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.cpp +++ b/SHADE_Engine/src/Camera/SHCameraComponent.cpp @@ -13,7 +13,7 @@ namespace SHADE , width(1920.0f), height(1080.0f), zNear(0.01f), zFar(10000.0f), fov(90.0f), movementSpeed(1.0f), turnSpeed(0.5f) , perspProj(true), dirtyView(true), dirtyProj(true) , viewMatrix(), projMatrix() - , position() + , position(), offset() { ComponentFamily::GetID(); } diff --git a/SHADE_Engine/src/Camera/SHCameraComponent.h b/SHADE_Engine/src/Camera/SHCameraComponent.h index f5e08af4..b778b8fa 100644 --- a/SHADE_Engine/src/Camera/SHCameraComponent.h +++ b/SHADE_Engine/src/Camera/SHCameraComponent.h @@ -33,7 +33,7 @@ namespace SHADE SHVec3 position; bool perspProj; - + SHVec3 offset; @@ -41,7 +41,7 @@ namespace SHADE friend class SHCameraSystem; SHCameraComponent(); - ~SHCameraComponent(); + virtual ~SHCameraComponent(); //Getters and setters. diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.cpp b/SHADE_Engine/src/Camera/SHCameraDirector.cpp index 559897c0..98341098 100644 --- a/SHADE_Engine/src/Camera/SHCameraDirector.cpp +++ b/SHADE_Engine/src/Camera/SHCameraDirector.cpp @@ -1,6 +1,7 @@ #include "SHpch.h" #include "SHCameraDirector.h" #include "SHCameraComponent.h" +#include "SHCameraArmComponent.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/SHECSMacros.h" #include "ECS_Base/Managers/SHEntityManager.h" @@ -48,6 +49,7 @@ namespace SHADE viewMatrix = camComponent->GetViewMatrix(); projMatrix = camComponent->GetProjMatrix(); } + } void SHCameraDirector::SetMainCamera(SHCameraComponent& camera) noexcept diff --git a/SHADE_Engine/src/Camera/SHCameraDirector.h b/SHADE_Engine/src/Camera/SHCameraDirector.h index 5d09788b..6d5404c5 100644 --- a/SHADE_Engine/src/Camera/SHCameraDirector.h +++ b/SHADE_Engine/src/Camera/SHCameraDirector.h @@ -21,6 +21,7 @@ namespace SHADE EntityID mainCameraEID; EntityID transitionCameraEID; + SHMatrix GetViewMatrix() const noexcept; SHMatrix GetProjMatrix() const noexcept; @@ -35,7 +36,7 @@ namespace SHADE protected: SHMatrix viewMatrix; SHMatrix projMatrix; - + }; typedef Handle DirectorHandle; diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 609805f8..d5bd414d 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -1,10 +1,12 @@ #include "SHpch.h" #include "SHCameraSystem.h" +#include "SHCameraArmComponent.h" #include "Math/SHMathHelpers.h" #include "Input/SHInputManager.h" #include "Math/Vector/SHVec2.h" #include "ECS_Base/Managers/SHComponentManager.h" #include "Math/Transform/SHTransformComponent.h" +#include namespace SHADE @@ -59,6 +61,7 @@ namespace SHADE } UpdateCameraComponent(editorCamera); + } void SHCameraSystem::EditorCameraUpdate::Execute(double dt) noexcept { @@ -112,6 +115,8 @@ namespace SHADE //std::cout << "Camera position: " << camera.position.x << " " << camera.position.y << std::endl; system->UpdateCameraComponent(system->editorCamera); + + system->DecomposeViewMatrix(camera.viewMatrix, camera.pitch, camera.yaw, camera.roll, camera.position); } void SHCameraSystem::Init(void) @@ -121,6 +126,9 @@ namespace SHADE editorCamera.SetYaw(0.0f); editorCamera.SetRoll(0.0f); editorCamera.movementSpeed = 2.0f; + + SHComponentManager::CreateComponentSparseSet(); + SHComponentManager::CreateComponentSparseSet(); } @@ -134,6 +142,26 @@ namespace SHADE return &editorCamera; } + void SHCameraSystem::UpdatePivotArmComponent(SHCameraArmComponent& pivot) noexcept + { + if (pivot.dirty) + { + + SHVec3 offset{ 0.0f,0.0f, pivot.GetArmLength() }; + offset = SHVec3::RotateX(offset, -(SHMath::DegreesToRadians(pivot.GetPitch()))); + offset = SHVec3::RotateY(offset, (SHMath::DegreesToRadians(pivot.GetYaw()))); + + + //pivot.rtMatrix = SHMatrix::RotateX(SHMath::DegreesToRadians(pivot.GetPitch())) + // * SHMatrix::RotateY(SHMath::DegreesToRadians(pivot.GetYaw())) + // * SHMatrix::Translate(SHVec3(0.0f , 0.0f, pivot.GetArmLength())); + + pivot.offset = offset; + // pivot.rtMatrix = SHMatrix::Inverse(pivot.rtMatrix); + } + } + + void SHCameraSystem::UpdateCameraComponent(SHCameraComponent& camera) noexcept { if (SHComponentManager::HasComponent(camera.GetEID()) == true && &camera != &editorCamera) @@ -151,6 +179,15 @@ namespace SHADE if (camera.dirtyView) { + camera.offset = SHVec3{ 0.0f }; + if (SHComponentManager::HasComponent(camera.GetEID())) + { + auto arm = SHComponentManager::GetComponent(camera.GetEID()); + camera.offset = arm->GetOffset(); + if(arm->lookAtCameraOrigin) + CameraLookAt(camera, camera.position); + } + SHVec3 view, right, UP; @@ -171,9 +208,12 @@ namespace SHADE camera.viewMatrix(2, 1) = view[1]; camera.viewMatrix(2, 2) = view[2]; - camera.viewMatrix(0, 3) = -right.Dot(camera.position); - camera.viewMatrix(1, 3) = -UP.Dot(camera.position); - camera.viewMatrix(2, 3) = -view.Dot(camera.position); + camera.viewMatrix(0, 3) = -right.Dot(camera.position + camera.offset); + camera.viewMatrix(1, 3) = -UP.Dot(camera.position + camera.offset); + camera.viewMatrix(2, 3) = -view.Dot(camera.position + camera.offset); + + + camera.dirtyView = false; } @@ -221,6 +261,8 @@ namespace SHADE SHVec3 up = { 0.0f,1.0f,0.0f }; + + target = SHVec3::RotateX(target, SHMath::DegreesToRadians(camera.pitch)); target = SHVec3::RotateY(target, SHMath::DegreesToRadians(camera.yaw)); target += camera.position; @@ -241,6 +283,13 @@ namespace SHADE { SHCameraSystem* system = static_cast(GetSystem()); auto& dense = SHComponentManager::GetDense(); + auto& pivotDense = SHComponentManager::GetDense(); + + for (auto& pivot : pivotDense) + { + system->UpdatePivotArmComponent(pivot); + } + for (auto& cam : dense) { system->UpdateCameraComponent(cam); @@ -274,18 +323,115 @@ namespace SHADE } void SHCameraSystem::ClampCameraRotation(SHCameraComponent& camera) noexcept { + constexpr float clampVal = 85.0f; + if (camera.pitch > clampVal) + camera.SetPitch(clampVal); + if (camera.pitch < -clampVal) + camera.SetPitch(-clampVal); + if (camera.roll > clampVal) + camera.SetRoll(clampVal); + if (camera.roll < -clampVal) + camera.SetRoll(-clampVal); - if (camera.pitch > 85) - camera.SetPitch(85); - if (camera.pitch < -85) - camera.SetPitch(-85); - if (camera.roll > 85) - camera.SetRoll(85); - if (camera.roll < -85) - camera.SetRoll(-85); + while (camera.yaw > 360) + camera.yaw -= 360; + while (camera.yaw < -360) + camera.yaw += 360; } + void SHCameraSystem::SetMainCamera(EntityID eid, size_t directorIndex) noexcept + { + if (SHComponentManager::HasComponent(eid) && directorIndex < directorHandleList.size()) + directorHandleList[directorIndex]->SetMainCamera(*SHComponentManager::GetComponent(eid)); + else + { + SHLOG_WARNING("Set Main Camera warning: Entity does not have camera component or director does not exist.") + } + } + + void SHCameraSystem::DecomposeViewMatrix(SHMatrix const& viewMatrix, float& pitch, float& yaw, float& roll, SHVec3& pos) noexcept + { + + float initPitch = pitch; + SHVec3 initPos = pos; + SHVec3 translate3, scale; + SHQuaternion quat; + + //SHMatrix viewInverse = viewMatrix; + + viewMatrix.Decompose(translate3, quat, scale); + yaw = 180+ SHMath::RadiansToDegrees(quat.ToEuler().y); + pitch = -SHMath::RadiansToDegrees(quat.ToEuler().x); + + SHVec4 dotPos{ -viewMatrix(0,3),-viewMatrix(1,3), -viewMatrix(2,3), 1.0f }; + SHMatrix mtx = viewMatrix; + mtx(0, 3) = 0.0f; + mtx(1, 3) = 0.0f; + mtx(2, 3) = 0.0f; + mtx.Transpose(); + mtx = SHMatrix::Inverse(mtx); + SHVec4 translate = mtx* dotPos; + + pos.x = translate.x; + pos.y = translate.y; + pos.z = translate.z; + + } + void SHCameraSystem::SetCameraViewMatrix(SHCameraComponent& camera, SHMatrix const& viewMatrix) noexcept + { + DecomposeViewMatrix(viewMatrix, camera.pitch, camera.yaw, camera.roll, camera.position); + camera.dirtyView = true; + } + + void SHCameraSystem::CameraLookAt(SHCameraComponent& camera, SHVec3 target) noexcept + { + + if (camera.position == target) + { + //lets off set it abit so the view is nt fked + target.z -= 0.0001f; + } + SHVec3 forward, right, upVec; + + SHVec3 up = { 0.0f,1.0f,0.0f }; + + + ////SHVec3::RotateZ(target, SHMath::DegreesToRadians(camera.roll)); + + //target = SHVec3::Normalise(target); + + SHVec3::RotateZ(up, camera.roll); + up = SHVec3::Normalise(up); + + + forward = target - (camera.position + camera.offset); forward = SHVec3::Normalise(forward); + right = SHVec3::Cross(forward, up); right = SHVec3::Normalise(right); + upVec = SHVec3::Cross(forward, right); + + + SHMatrix viewMtx; + viewMtx = SHMatrix::Identity; + viewMtx(0, 0) = right[0]; + viewMtx(0, 1) = right[1]; + viewMtx(0, 2) = right[2]; + + viewMtx(1, 0) = upVec[0]; + viewMtx(1, 1) = upVec[1]; + viewMtx(1, 2) = upVec[2]; + + viewMtx(2, 0) = forward[0]; + viewMtx(2, 1) = forward[1]; + viewMtx(2, 2) = forward[2]; + + viewMtx(0, 3) = -right.Dot(camera.position + camera.offset); + viewMtx(1, 3) = -upVec.Dot(camera.position + camera.offset); + viewMtx(2, 3) = -forward.Dot(camera.position + camera.offset); + + + SetCameraViewMatrix(camera, viewMtx); + } + } diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.h b/SHADE_Engine/src/Camera/SHCameraSystem.h index 68071160..98fd442f 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.h +++ b/SHADE_Engine/src/Camera/SHCameraSystem.h @@ -9,6 +9,9 @@ namespace SHADE { + + class SHCameraArmComponent; + class SH_API SHCameraSystem final : public SHSystem { private: @@ -19,6 +22,11 @@ namespace SHADE SHResourceLibrary directorLibrary; std::vector directorHandleList; + + void UpdateCameraComponent(SHCameraComponent& camera) noexcept; + void UpdatePivotArmComponent(SHCameraArmComponent& pivot) noexcept; + + public: SHCameraSystem(void) = default; virtual ~SHCameraSystem(void) = default; @@ -39,7 +47,7 @@ namespace SHADE class SH_API CameraSystemUpdate final: public SHSystemRoutine { public: - CameraSystemUpdate() : SHSystemRoutine("Camera System Update", false) {}; + CameraSystemUpdate() : SHSystemRoutine("Camera System Update", true) {}; virtual void Execute(double dt)noexcept override final; }; friend class CameraSystemUpdate; @@ -51,12 +59,10 @@ namespace SHADE DirectorHandle GetDirector(size_t index) noexcept; void ClampCameraRotation(SHCameraComponent& camera) noexcept; void UpdateEditorCamera(double dt) noexcept; - protected: - - void UpdateCameraComponent(SHCameraComponent& camera) noexcept; - - - + void SetMainCamera(EntityID eid, size_t directorIndex) noexcept; + void DecomposeViewMatrix(SHMatrix const& matrix, float& pitch, float& yaw, float& roll, SHVec3& pos) noexcept; + void SetCameraViewMatrix(SHCameraComponent& camera, SHMatrix const& viewMatrix) noexcept; + void CameraLookAt(SHCameraComponent& camera, SHVec3 target) noexcept; }; diff --git a/SHADE_Engine/src/ECS_Base/Managers/SHComponentManager.cpp b/SHADE_Engine/src/ECS_Base/Managers/SHComponentManager.cpp index be78a146..8a715a49 100644 --- a/SHADE_Engine/src/ECS_Base/Managers/SHComponentManager.cpp +++ b/SHADE_Engine/src/ECS_Base/Managers/SHComponentManager.cpp @@ -33,12 +33,18 @@ namespace SHADE return; } + std::vector eventVec; + for (uint32_t i = 0; i < componentSet.Size(); ++i) { SHComponent* comp = (SHComponent*) componentSet.GetElement(i, EntityHandleGenerator::GetIndex(entityID)); if (comp) { comp->OnDestroy(); + SHComponentRemovedEvent eventData; + eventData.eid = entityID; + eventData.removedComponentType = i; + eventVec.push_back(eventData); } } @@ -51,8 +57,15 @@ namespace SHADE componentSet.RemoveElements(EntityHandleGenerator::GetIndex(entityID)); + for (auto& eventData : eventVec) + { + SHEventManager::BroadcastEvent(eventData, SH_COMPONENT_REMOVED_EVENT); + } + + //entityHandle.RemoveHandle(entityID); + } diff --git a/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp b/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp index bbf8d400..6ce4f277 100644 --- a/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp +++ b/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.cpp @@ -213,5 +213,14 @@ namespace SHADE return SHSerialization::DeserializeEntityToSceneFromString(data); }*/ - + EntityID SHEntityManager::GetEntityByName(std::string const& name) noexcept + { + EntityID result = MAX_EID; + for (auto& entity : entityVec) + { + if (entity->name == name) + result = entity->GetEID(); + } + return result; + } } diff --git a/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.h b/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.h index f32ab2c4..911748bd 100644 --- a/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.h +++ b/SHADE_Engine/src/ECS_Base/Managers/SHEntityManager.h @@ -209,6 +209,8 @@ namespace SHADE //static EntityID DuplicateEntity(EntityID eid) noexcept; + static EntityID GetEntityByName(std::string const& name) noexcept; + protected: diff --git a/SHADE_Engine/src/ECS_Base/Managers/SHSystemManager.cpp b/SHADE_Engine/src/ECS_Base/Managers/SHSystemManager.cpp index 551233db..057568d6 100644 --- a/SHADE_Engine/src/ECS_Base/Managers/SHSystemManager.cpp +++ b/SHADE_Engine/src/ECS_Base/Managers/SHSystemManager.cpp @@ -29,7 +29,7 @@ namespace SHADE { system.second->Init(); #ifdef _DEBUG - std::cout << system.first << " Init" << std::endl; + SHLOG_INFO("Initialising System {}...", system.first) #endif } } diff --git a/SHADE_Engine/src/Editor/DragDrop/SHDragDrop.hpp b/SHADE_Engine/src/Editor/DragDrop/SHDragDrop.hpp index f9849d78..ce0615e1 100644 --- a/SHADE_Engine/src/Editor/DragDrop/SHDragDrop.hpp +++ b/SHADE_Engine/src/Editor/DragDrop/SHDragDrop.hpp @@ -6,12 +6,12 @@ namespace SHADE { //TODO: Convert to RTTR? - constexpr auto DRAG_EID = "DragEID"; - constexpr auto DRAG_RESOURCE = "DragResource"; - struct SHDragDrop { + using DragDropTag = std::string_view; + static constexpr DragDropTag DRAG_EID = "DragEID"; + static constexpr DragDropTag DRAG_RESOURCE = "DragResource"; static bool BeginSource(ImGuiDragDropFlags const flags = 0); /** * \brief Ends the DragDrop Source. ONLY CALL IF BeginSource returns true diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp index caad9b10..8c71eb8f 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp @@ -4,14 +4,16 @@ #include "Editor/IconsMaterialDesign.h" #include "Editor/SHImGuiHelpers.hpp" #include +#include #include "Assets/SHAssetManager.h" +#include "Editor/IconsFontAwesome6.h" #include "Editor/DragDrop/SHDragDrop.hpp" namespace SHADE { SHAssetBrowser::SHAssetBrowser() - :SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar) + :SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar), rootFolder(SHAssetManager::GetRootFolder()), prevFolder(rootFolder), currentFolder(rootFolder) { } @@ -23,62 +25,140 @@ namespace SHADE void SHAssetBrowser::Update() { SHEditorWindow::Update(); - if(Begin()) + if (Begin()) { + RecursivelyDrawTree(rootFolder); DrawMenuBar(); - auto const& assets = SHAssetManager::GetAllAssets(); - if(ImGui::BeginTable("AssetBrowserTable", 3)) - { - ImGui::TableNextColumn(); - ImGui::TableHeader("Asset ID"); - ImGui::TableNextColumn(); - ImGui::TableHeader("Name"); - ImGui::TableNextColumn(); - ImGui::TableHeader("Type"); - for(SHAsset const& asset : assets) - { - DrawAsset(asset); - } - ImGui::EndTable(); - } + DrawCurrentFolder(); } ImGui::End(); } void SHAssetBrowser::DrawMenuBar() { - if(ImGui::BeginMenuBar()) + if (ImGui::BeginMenuBar()) { ImGui::EndMenuBar(); } } - void SHAssetBrowser::DrawAsset(SHAsset const& asset) + ImRect SHAssetBrowser::RecursivelyDrawTree(FolderPointer folder) { - ImGui::PushID(asset.id); - ImGui::BeginGroup(); - - ImGui::TableNextColumn(); - ImGui::Selectable(std::format("{}", asset.id).data(), false, ImGuiSelectableFlags_SpanAllColumns); - if(SHDragDrop::BeginSource()) + auto const& subFolders = folder->subFolders; + auto const& files = folder->files; + const bool isSelected = std::ranges::find(selectedFolders, folder) != selectedFolders.end(); + ImGuiTreeNodeFlags flags = (subFolders.empty() && files.empty()) ? ImGuiTreeNodeFlags_Leaf : ImGuiTreeNodeFlags_OpenOnArrow; + if (isSelected) + flags |= ImGuiTreeNodeFlags_Selected; + if (folder == rootFolder) + flags |= ImGuiTreeNodeFlags_DefaultOpen; + + bool isOpen = ImGui::TreeNodeEx(folder, flags, "%s %s", ICON_MD_FOLDER, folder->name.data()); + const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + if(ImGui::IsItemClicked()) { - auto id = asset.id; - ImGui::Text("Moving Asset: %zu", id); - SHDragDrop::SetPayload(DRAG_RESOURCE, &id); - SHDragDrop::EndSource(); + selectedFolders.clear(); + selectedFolders.push_back(folder); + } + if (isOpen) + { + const ImColor treeLineColor = ImGui::GetColorU32(ImGuiCol_CheckMark); + const float horizontalOffset = 0.0f; + ImDrawList* drawList = ImGui::GetWindowDrawList(); + ImVec2 vertLineStart = ImGui::GetCursorScreenPos(); + vertLineStart.x += horizontalOffset; + ImVec2 vertLineEnd = vertLineStart; + for (auto const& subFolder : subFolders) + { + const float horizontalLineSize = 8.0f; + const ImRect childRect = RecursivelyDrawTree(subFolder); + const float midPoint = (childRect.Min.y + childRect.Max.y) * 0.5f; + drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1); + vertLineEnd.y = midPoint; + } + for (auto const& file : files) + { + const float horizontalLineSize = 25.0f; + const ImRect childRect = DrawFile(file); + const float midPoint = (childRect.Min.y + childRect.Max.y) * 0.5f; + drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1); + vertLineEnd.y = midPoint; + } + drawList->AddLine(vertLineStart, vertLineEnd, treeLineColor, 1); + ImGui::TreePop(); + } + return nodeRect; + } + + void SHAssetBrowser::DrawCurrentFolder() + { + //auto const& subFolders = currentFolder->subFolders; + //ImVec2 initialCursorPos = ImGui::GetCursorPos(); + //ImVec2 initialRegionAvail = ImGui::GetContentRegionAvail(); + //int maxTiles = initialRegionAvail.x / tileWidth; + //float maxX = (maxTiles - 1)*tileWidth; + //ImVec2 tilePos = initialCursorPos; + //for (auto const& subFolder : subFolders) + //{ + // ImGui::SetCursorPos(tilePos); + // ImGui::BeginGroup(); + // ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, {0.0f, 0.0f}); + // ImGui::Button(ICON_MD_FOLDER, {tileWidth}); + // ImGui::Text(subFolder->name.data()); + // ImGui::PopStyleVar(); + // ImGui::EndGroup(); + // if(tilePos.x >= maxX) + // { + // tilePos.x = initialCursorPos.x; + // } + // else + // { + // ImGui::SameLine(); + // tilePos.x += tileWidth; + // } + //} + } + + ImRect SHAssetBrowser::DrawFile(SHFile const& file) noexcept + { + if (file.assetMeta == nullptr) + return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + const bool isSelected = std::ranges::find(selectedAssets, file.assetMeta->id) != selectedAssets.end(); + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf; + if (isSelected) + flags |= ImGuiTreeNodeFlags_Selected; + std::string icon{}; + + switch(file.assetMeta->type) + { + case AssetType::INVALID: break; + case AssetType::SHADER: icon = ICON_FA_FILE_CODE; break; + case AssetType::SHADER_BUILT_IN: icon = ICON_FA_FILE_CODE; break; + case AssetType::TEXTURE: icon = ICON_FA_IMAGES; break; + case AssetType::MESH: icon = ICON_FA_CUBES; break; + case AssetType::SCENE: icon = ICON_MD_IMAGE; break; + case AssetType::PREFAB: icon = ICON_FA_BOX_OPEN; break; + case AssetType::MATERIAL: break; + case AssetType::MAX_COUNT: break; + default: ; } - ImGui::TableNextColumn(); - ImGui::Text("%s", asset.name.c_str()); - - ImGui::TableNextColumn(); - ImGui::Text("%s", "Type"); - - ImGui::EndGroup(); - ImGui::PopID(); - - - + ImGui::TreeNodeEx(file.assetMeta, flags, "%s %s", icon.data(), file.assetMeta->name.data()); + const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + if(SHDragDrop::BeginSource()) + { + auto id = file.assetMeta->id; + ImGui::Text("Moving Asset: %s [%zu]", file.name.data(), file.assetMeta->id); + SHDragDrop::SetPayload(SHDragDrop::DRAG_RESOURCE, &id); + SHDragDrop::EndSource(); + } + if(ImGui::IsItemClicked()) + { + selectedAssets.clear(); + selectedAssets.push_back(file.assetMeta->id); + } + ImGui::TreePop(); + return nodeRect; } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h index 0e3053bc..d56fc029 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h @@ -1,7 +1,9 @@ #pragma once +#include "imgui_internal.h" #include "Assets/SHAsset.h" #include "Editor/EditorWindow/SHEditorWindow.h" +#include "Filesystem/SHFolder.h" namespace SHADE { @@ -16,9 +18,14 @@ namespace SHADE void Refresh(); private: void DrawMenuBar(); - void DrawAsset(SHAsset const& asset); + ImRect RecursivelyDrawTree(FolderPointer folder); + void DrawCurrentFolder(); + ImRect DrawFile(SHFile const& file) noexcept; - float idColumnWidth, nameColumnWidth, typeColumnWidth; + FolderPointer rootFolder, prevFolder, currentFolder; + std::vector selectedFolders; + std::vector selectedAssets; + static constexpr float tileWidth = 50.0f; }; -} \ No newline at end of file +} diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index f2f8d927..1b289a90 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -6,7 +6,7 @@ //#==============================================================# //|| SHADE Includes || //#==============================================================# -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "Editor/SHImGuiHelpers.hpp" #include "Editor/SHEditorWidgets.hpp" #include "SHHierarchyPanel.h" @@ -48,15 +48,28 @@ namespace SHADE if (Begin()) { + if (skipFrame) + { + ImGui::End(); + skipFrame = false; + return; + } DrawMenuBar(); auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - if(const auto root = sceneGraph.GetRoot()) + + if (const auto root = sceneGraph.GetRoot()) { auto const& children = root->GetChildren(); - + for (const auto child : children) { - RecursivelyDrawEntityNode(child); + if (child) + RecursivelyDrawEntityNode(child); + if (skipFrame) + { + ImGui::End(); + return; + } } } else @@ -64,12 +77,36 @@ namespace SHADE SHLOG_WARNING("Scene Graph root is null! Unable to render hierarchy.") } - if(ImGui::IsWindowHovered() && !SHDragDrop::hasDragDrop && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) + if (ImGui::IsWindowHovered() && !SHDragDrop::hasDragDrop && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { - if(auto editor = SHSystemManager::GetSystem()) + if (auto editor = SHSystemManager::GetSystem()) editor->selectedEntities.clear(); } ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal); + if (ImGui::IsWindowFocused()) + { + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_A)) + { + SelectAllEntities(); + } + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_C)) + { + CopySelectedEntities(); + } + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && !ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyReleased(ImGuiKey_V)) + { + PasteEntities(); + } + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyReleased(ImGuiKey_V)) + { + const auto editor = SHSystemManager::GetSystem(); + if (editor->selectedEntities.size() == 1) + { + PasteEntities(editor->selectedEntities.back()); + } + } + } + } ImGui::End(); } @@ -81,7 +118,7 @@ namespace SHADE void SHHierarchyPanel::SetScrollTo(EntityID eid) { - if(eid == MAX_EID) + if (eid == MAX_EID) return; scrollTo = eid; } @@ -93,8 +130,10 @@ namespace SHADE { if (ImGui::BeginMenuBar()) { - ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x * 0.75f); - if(ImGui::SmallButton(ICON_MD_DESELECT)) + auto size = ImGui::GetWindowSize(); + auto g = ImGui::GetCurrentContext(); + ImGui::SetCursorPosX(size.x - g->Style.FramePadding.x * 15.0f); + if (ImGui::SmallButton(ICON_MD_CLEAR_ALL)) { auto editor = SHSystemManager::GetSystem(); editor->selectedEntities.clear(); @@ -119,15 +158,17 @@ namespace SHADE } } - ImRect SHHierarchyPanel::RecursivelyDrawEntityNode(SHSceneNode* currentNode) + ImRect SHHierarchyPanel::RecursivelyDrawEntityNode(SHSceneNode* const currentNode) { + if (currentNode == nullptr) + return {}; auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); //Get node data (Children, eid, selected) auto& children = currentNode->GetChildren(); EntityID eid = currentNode->GetEntityID(); - if(scrollTo != MAX_EID && eid == scrollTo) + if (scrollTo != MAX_EID && eid == scrollTo) { ImGui::SetScrollHereY(); scrollTo = MAX_EID; @@ -154,23 +195,27 @@ namespace SHADE if (SHDragDrop::BeginSource()) { std::string moveLabel = "Moving EID: "; - if(!isSelected) - editor->selectedEntities.push_back(eid); - for(int i = 0; i < static_cast(editor->selectedEntities.size()); ++i) + static std::vector draggingEntities = editor->selectedEntities; + if (!isSelected) { - moveLabel.append(std::to_string(editor->selectedEntities[i])); - if(i + 1 < static_cast(editor->selectedEntities.size())) + draggingEntities.clear(); + draggingEntities.push_back(eid); + } + for (int i = 0; i < static_cast(draggingEntities.size()); ++i) + { + moveLabel.append(std::to_string(draggingEntities[i])); + if (i + 1 < static_cast(draggingEntities.size())) { moveLabel.append(", "); } } ImGui::Text(moveLabel.c_str()); - SHDragDrop::SetPayload>(DRAG_EID, &editor->selectedEntities); + SHDragDrop::SetPayload>(SHDragDrop::DRAG_EID, &draggingEntities); SHDragDrop::EndSource(); } else if (SHDragDrop::BeginTarget()) //If Received DragDrop { - if (const std::vector* eidPayload = SHDragDrop::AcceptPayload>(DRAG_EID)) //If payload is valid + if (const std::vector* eidPayload = SHDragDrop::AcceptPayload>(SHDragDrop::DRAG_EID)) //If payload is valid { ParentSelectedEntities(eid); SHDragDrop::EndTarget(); @@ -178,37 +223,43 @@ namespace SHADE } //Context menu - if(ImGui::BeginPopupContextItem(std::to_string(eid).c_str())) + if (ImGui::BeginPopupContextItem(std::to_string(eid).c_str())) { - if(!isSelected) + if (!isSelected) { editor->selectedEntities.clear(); editor->selectedEntities.push_back(eid); } - if(ImGui::Selectable("Copy")) + if (ImGui::Selectable("Copy")) { - SHClipboardUtilities::WriteToClipboard(SHSerialization::SerializeEntitiesToString(editor->selectedEntities)); + CopySelectedEntities(); } - if(ImGui::Selectable("Paste")) + if (ImGui::Selectable("Paste")) { - SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard())); + PasteEntities(); + skipFrame = true; + ImGui::EndPopup(); + if (isNodeOpen) + ImGui::TreePop(); + return nodeRect; } - if(ImGui::Selectable("Paste as Child")) + if (ImGui::Selectable("Paste as Child")) { - SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard(), eid)); + PasteEntities(eid); + skipFrame = true; } - if(ImGui::Selectable(std::format("{} Delete", ICON_MD_DELETE).data())) + if (ImGui::Selectable(std::format("{} Delete", ICON_MD_DELETE).data())) { SHEntityManager::DestroyEntity(eid); } - - if((currentNode->GetParent() != sceneGraph.GetRoot()) && ImGui::Selectable(std::format("{} Unparent Selected", ICON_MD_NORTH_WEST).data())) + + if ((currentNode->GetParent() != sceneGraph.GetRoot()) && ImGui::Selectable(std::format("{} Unparent Selected", ICON_MD_NORTH_WEST).data())) { ParentSelectedEntities(MAX_EID); } ImGui::EndPopup(); } - + //Handle node selection if (ImGui::IsItemHovered()) { @@ -216,11 +267,11 @@ namespace SHADE { if (!isSelected) { - if(ImGui::IsKeyDown(ImGuiKey_LeftShift)) + if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) { - if(editor->selectedEntities.size() >= 1) + if (editor->selectedEntities.size() >= 1) { - SelectRangeOfEntities(editor->selectedEntities[0], eid); + SelectRangeOfEntities(editor->selectedEntities[0], eid); } else editor->selectedEntities.clear(); } @@ -278,12 +329,12 @@ namespace SHADE auto const editor = SHSystemManager::GetSystem(); SHEntityParentCommand::EntityParentData entityParentData; std::vector parentedEIDS; - for(auto const& eid : editor->selectedEntities) + for (auto const& eid : editor->selectedEntities) { - if(sceneGraph.GetChild(eid, parentEID) == nullptr) + if (sceneGraph.GetChild(eid, parentEID) == nullptr) { parentedEIDS.push_back(eid); - if(auto parent = sceneGraph.GetParent(eid)) + if (auto parent = sceneGraph.GetParent(eid)) entityParentData[eid].oldParentEID = parent->GetEntityID(); entityParentData[eid].newParentEID = parentEID; } @@ -298,34 +349,57 @@ namespace SHADE editor->selectedEntities.clear(); auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); sceneGraph.Traverse([&](SHSceneNode* nodePtr) - { - auto eid = nodePtr->GetEntityID(); - if(!startSelecting) { - if(eid == beginEID || eid == endEID) + auto eid = nodePtr->GetEntityID(); + if (!startSelecting) { - startSelecting = true; - editor->selectedEntities.push_back(eid); - } - } - else - { - if(!endSelecting) - { - editor->selectedEntities.push_back(eid); - if(eid == endEID || eid == beginEID) + if (eid == beginEID || eid == endEID) { - endSelecting = true; + startSelecting = true; + editor->selectedEntities.push_back(eid); } } - } - }); + else + { + if (!endSelecting) + { + editor->selectedEntities.push_back(eid); + if (eid == endEID || eid == beginEID) + { + endSelecting = true; + } + } + } + }); + } + + void SHHierarchyPanel::SelectAllEntities() + { + const auto editor = SHSystemManager::GetSystem(); + editor->selectedEntities.clear(); + auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + sceneGraph.Traverse([&](SHSceneNode* nodePtr) + { + auto eid = nodePtr->GetEntityID(); + editor->selectedEntities.push_back(eid); + }); + } + + void SHHierarchyPanel::CopySelectedEntities() + { + const auto editor = SHSystemManager::GetSystem(); + SHClipboardUtilities::WriteToClipboard(SHSerialization::SerializeEntitiesToString(editor->selectedEntities)); + } + + void SHHierarchyPanel::PasteEntities(EntityID parentEID) + { + SetScrollTo(SHSerialization::DeserializeEntitiesFromString(SHClipboardUtilities::GetDataFromClipboard(), parentEID)); } void SHCreateEntityCommand::Execute() { EntityID newEID = SHEntityManager::CreateEntity(eid); - if(eid == MAX_EID) + if (eid == MAX_EID) eid = newEID; } @@ -337,9 +411,9 @@ namespace SHADE void SHEntityParentCommand::Execute() { auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - for(auto const& eid : entities) + for (auto const& eid : entities) { - if(entityParentData[eid].newParentEID == MAX_EID) + if (entityParentData[eid].newParentEID == MAX_EID) sceneGraph.SetParent(eid, nullptr); else sceneGraph.SetParent(eid, entityParentData[eid].newParentEID); @@ -349,9 +423,9 @@ namespace SHADE void SHEntityParentCommand::Undo() { auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - for(auto const& eid : entities) + for (auto const& eid : entities) { - if(entityParentData[eid].oldParentEID == MAX_EID) + if (entityParentData[eid].oldParentEID == MAX_EID) sceneGraph.SetParent(eid, nullptr); else sceneGraph.SetParent(eid, entityParentData[eid].oldParentEID); diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h index 0cfe6474..9b26e9d6 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h @@ -26,10 +26,14 @@ namespace SHADE void SetScrollTo(EntityID eid); private: void DrawMenuBar() const noexcept; - ImRect RecursivelyDrawEntityNode(SHSceneNode*); + ImRect RecursivelyDrawEntityNode(SHSceneNode* const); void CreateChildEntity(EntityID parentEID) const noexcept; void ParentSelectedEntities(EntityID parentEID) const noexcept; void SelectRangeOfEntities(EntityID beginEID, EntityID EndEID); + void SelectAllEntities(); + void CopySelectedEntities(); + void PasteEntities(EntityID parentEID = MAX_EID); + bool skipFrame = false; std::string filter; bool isAnyNodeSelected = false; EntityID scrollTo = MAX_EID; diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.h b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.h new file mode 100644 index 00000000..69f4c145 --- /dev/null +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.h @@ -0,0 +1,12 @@ +#pragma once +#include "ECS_Base/Components/SHComponent.h" + +namespace SHADE +{ + template::value, bool> = true> + static void DrawContextMenu(T* component); + template, bool> = true> + static void DrawComponent(T* component); +} + +#include "SHEditorComponentView.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 9899d4a4..4777fc6a 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -13,25 +13,28 @@ #include "Editor/IconsFontAwesome6.h" #include "ECS_Base/Components/SHComponent.h" #include "Editor/SHEditorWidgets.hpp" +#include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Graphics/MiddleEnd/Lights/SHLightComponent.h" #include "Physics/Components/SHColliderComponent.h" #include "Reflection/SHReflectionMetadata.h" +#include "Resource/SHResourceManager.h" + namespace SHADE { template std::vector GetRTTREnumNames() { auto const rttrType = rttr::type::get(); - if(!rttrType.is_enumeration()) + if (!rttrType.is_enumeration()) return {}; auto const enumAlign = rttrType.get_enumeration(); auto const names = enumAlign.get_names(); std::vector result; - std::transform(names.begin(), names.end(), std::back_inserter(result), [](rttr::string_view const& name){return name.data();}); + std::transform(names.begin(), names.end(), std::back_inserter(result), [](rttr::string_view const& name) {return name.data(); }); return result; } - template::value, bool> = true> + template::value, bool>> static void DrawContextMenu(T* component) { if (!component) @@ -60,13 +63,15 @@ namespace SHADE ImGui::EndPopup(); } } - template, bool> = true> + template, bool>> static void DrawComponent(T* component) { if (!component) return; - const auto componentType = rttr::type::get(*component); + const auto componentType = rttr::type::get(); + ImGui::PushID(SHFamilyID::GetID()); SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }, "Is Component Active"); + ImGui::PopID(); ImGui::SameLine(); if (ImGui::CollapsingHeader(componentType.get_name().data())) { @@ -75,7 +80,8 @@ namespace SHADE for (auto const& property : properties) { auto const& type = property.get_type(); - + auto tooltip = property.get_metadata(META::tooltip); + bool const& isAngleInRad = property.get_metadata(META::angleInRad).is_valid() ? property.get_metadata(META::angleInRad).template get_value() : false; if (type.is_enumeration()) { auto enumAlign = type.get_enumeration(); @@ -89,29 +95,25 @@ namespace SHADE auto values = enumAlign.get_values(); auto it = std::next(values.begin(), idx); property.set_value(component, *it); - }); + }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else if (type.is_arithmetic()) { if (type == rttr::type::get()) { - SHEditorWidgets::CheckBox(property.get_name().data(), [component, property] {return property.get_value(component).to_bool(); }, [component, property](bool const& result) {property.set_value(component, result); }); + SHEditorWidgets::CheckBox(property.get_name().data(), [component, property] {return property.get_value(component).to_bool(); }, [component, property](bool const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } - //else if (type == rttr::type::get()) - //{ - // - //} else if (type == rttr::type::get() || type == rttr::type::get() || type == rttr::type::get() || type == rttr::type::get()) { auto metaMin = property.get_metadata(META::min); auto metaMax = property.get_metadata(META::max); if (metaMin && metaMax) { - SHEditorWidgets::SliderInt(property.get_name().data(), metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }); + SHEditorWidgets::SliderInt(property.get_name().data(), metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else { - SHEditorWidgets::DragInt(property.get_name().data(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }); + SHEditorWidgets::DragInt(property.get_name().data(), [component, property] {return property.get_value(component).to_int(); }, [component, property](int const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -120,11 +122,11 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U8, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint8(); }, [component, property](uint8_t const& result) {property.set_value(component, result); }, "%zu"); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U8, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint8(); }, [component, property](uint8_t const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string(), "%zu"); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U8, [component, property] {return property.get_value(component).to_uint8(); }, [component, property](uint8_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu"); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U8, [component, property] {return property.get_value(component).to_uint8(); }, [component, property](uint8_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -133,11 +135,11 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U16, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, "%zu"); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U16, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string(), "%zu"); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U16, [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu"); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U16, [component, property] {return property.get_value(component).to_uint16(); }, [component, property](uint16_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -146,11 +148,11 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U32, metaMin.template get_value(), metaMax.template get_value(), [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, "%zu"); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U32, metaMin.template get_value(), metaMax.template get_value(), [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string(), "%zu"); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U32, [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu"); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U32, [component, property] { return property.get_value(component).to_uint32(); }, [component, property](uint32_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -159,11 +161,11 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U64, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, "%zu"); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_U64, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string(), "%zu"); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U64, [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu"); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_U64, [component, property] {return property.get_value(component).to_uint64(); }, [component, property](uint64_t const& result) {property.set_value(component, result); }, 0.1f, 0, 0, "%zu", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -171,17 +173,17 @@ namespace SHADE auto metaMin = property.get_metadata(META::min); auto metaMax = property.get_metadata(META::max); float min{}, max{}; - if(metaMin.is_valid()) + if (metaMin.is_valid()) min = std::max(metaMin.template get_value(), -FLT_MAX * 0.5f); - if(metaMax.is_valid()) + if (metaMax.is_valid()) max = std::min(metaMax.template get_value(), FLT_MAX * 0.5f); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderFloat(property.get_name().data(), min, max, [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }); + SHEditorWidgets::SliderFloat(property.get_name().data(), min, max, [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else { - SHEditorWidgets::DragFloat(property.get_name().data(), [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }, "Test"); + SHEditorWidgets::DragFloat(property.get_name().data(), [component, property] {return property.get_value(component).to_float(); }, [component, property](float const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } else if (type == rttr::type::get()) @@ -190,25 +192,25 @@ namespace SHADE auto metaMax = property.get_metadata(META::max); if (metaMin.is_valid() && metaMax.is_valid()) { - SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_Double, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }); + SHEditorWidgets::SliderScalar(property.get_name().data(), ImGuiDataType_Double, metaMin.template get_value(), metaMax.template get_value(), [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else { - SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_Double, [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }, 0.1f); + SHEditorWidgets::DragScalar(property.get_name().data(), ImGuiDataType_Double, [component, property] {return property.get_value(component).to_double(); }, [component, property](double const& result) {property.set_value(component, result); }, 0.1f, {}, {}, "%.3f", tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } } else if (type == rttr::type::get()) { - SHEditorWidgets::DragVec4(property.get_name().data(), { "X", "Y", "Z", "W" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec4 vec) {return property.set_value(component, vec); }); + SHEditorWidgets::DragVec4(property.get_name().data(), { "X", "Y", "Z", "W" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec4 vec) {return property.set_value(component, vec); }, isAngleInRad, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else if (type == rttr::type::get()) { - SHEditorWidgets::DragVec3(property.get_name().data(), { "X", "Y", "Z" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec3 vec) {return property.set_value(component, vec); }); + SHEditorWidgets::DragVec3(property.get_name().data(), { "X", "Y", "Z" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec3 vec) {return property.set_value(component, vec); }, isAngleInRad, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } else if (type == rttr::type::get()) { - SHEditorWidgets::DragVec2(property.get_name().data(), { "X", "Y" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec2 vec) {return property.set_value(component, vec); }); + SHEditorWidgets::DragVec2(property.get_name().data(), { "X", "Y" }, [component, property]() {return property.get_value(component).template convert(); }, [component, property](SHVec2 vec) {return property.set_value(component, vec); }, isAngleInRad, tooltip.is_valid() ? tooltip.template get_value() : std::string()); } } @@ -223,10 +225,10 @@ namespace SHADE return; // Get transform component for extrapolating relative sizes - auto* transformComponent = SHComponentManager::GetComponent(component->GetEID()); + auto* transformComponent = SHComponentManager::GetComponent_s(component->GetEID()); const auto componentType = rttr::type::get(*component); - SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }); + 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())) { @@ -234,8 +236,8 @@ namespace SHADE auto& colliders = component->GetColliders(); int const size = static_cast(colliders.size()); - ImGui::BeginChild("Colliders", {0.0f, colliders.empty() ? 1.0f : 250.0f}, true); - std::optional colliderToDelete{std::nullopt}; + ImGui::BeginChild("Colliders", { 0.0f, colliders.empty() ? 1.0f : 250.0f }, true); + std::optional colliderToDelete{ std::nullopt }; for (int i{}; i < size; ++i) { ImGui::PushID(i); @@ -244,12 +246,12 @@ namespace SHADE if (collider->GetType() == SHCollider::Type::BOX) { - SHEditorWidgets::BeginPanel( std::format("{} Box Collider #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); + SHEditorWidgets::BeginPanel(std::format("{} Box Collider #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); auto box = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragVec3 ( - "Half Extents", { "X", "Y", "Z" }, - [box, transformComponent] { return (transformComponent->GetWorldScale() * 2.0f) * box->GetHalfExtents(); }, + "Half Extents", { "X", "Y", "Z" }, + [box, transformComponent] { return (box->GetHalfExtents() * 2.0f) / transformComponent->GetWorldScale(); }, [collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); }); } else if (collider->GetType() == SHCollider::Type::SPHERE) @@ -258,32 +260,51 @@ namespace SHADE auto sphere = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragFloat ( - "Radius", + "Radius", [sphere, transformComponent] { const SHVec3& TF_WORLD_SCALE = transformComponent->GetWorldScale(); const float MAX_SCALE = SHMath::Max({ TF_WORLD_SCALE.x, TF_WORLD_SCALE.y, TF_WORLD_SCALE.z }); return sphere->GetRadius() / MAX_SCALE; - }, - [collider](float const& value) { collider->SetBoundingSphere(value);}); + }, + [collider](float const& value) { collider->SetBoundingSphere(value); }); } else if (collider->GetType() == SHCollider::Type::CAPSULE) { } { - SHEditorWidgets::BeginPanel("Offset", { ImGui::GetContentRegionAvail().x, 30.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("Rotation", { "X", "Y", "Z" }, + [&collider] + { + auto offset = collider->GetRotationOffset(); + offset.x = SHMath::RadiansToDegrees(offset.x); + offset.y = SHMath::RadiansToDegrees(offset.y); + offset.z = SHMath::RadiansToDegrees(offset.z); + return offset; + }, + [&collider](SHVec3 const& vec) + { + const SHVec3 vecInRad + { + SHMath::DegreesToRadians(vec.x) + , SHMath::DegreesToRadians(vec.y) + , SHMath::DegreesToRadians(vec.z) + }; + collider->SetRotationOffset(vecInRad); + }); SHEditorWidgets::EndPanel(); } - if(ImGui::Button(std::format("{} Remove Collider #{}", ICON_MD_REMOVE, i).data())) + if (ImGui::Button(std::format("{} Remove Collider #{}", ICON_MD_REMOVE, i).data())) { colliderToDelete = i; } SHEditorWidgets::EndPanel(); ImGui::PopID(); } - if(colliderToDelete.has_value()) + if (colliderToDelete.has_value()) { component->RemoveCollider(colliderToDelete.value()); } @@ -291,11 +312,11 @@ namespace SHADE if (ImGui::BeginMenu("Add Collider")) { - if(ImGui::Selectable("Box Collider")) + if (ImGui::Selectable("Box Collider")) { component->AddBoundingBox(); } - if(ImGui::Selectable("Sphere Collider")) + if (ImGui::Selectable("Sphere Collider")) { component->AddBoundingSphere(); } @@ -308,10 +329,10 @@ namespace SHADE template<> static void DrawComponent(SHLightComponent* component) { - if (!component) + if (!component) return; const auto componentType = rttr::type::get(*component); - SHEditorWidgets::CheckBox("##IsActive", [component]() {return component->isActive; }, [component](bool const& active) {component->isActive = active; }); + 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())) { @@ -319,19 +340,49 @@ namespace SHADE static auto const enumAlign = rttr::type::get().get_enumeration(); static std::vector list(GetRTTREnumNames()); - + SHEditorWidgets::ComboBox("Type", list, [component] {return static_cast(component->GetType()); }, [component](int const& idx) { component->SetType(static_cast(idx)); }); - SHEditorWidgets::DragVec3("Position", {"X", "Y", "Z"}, [component](){return component->GetPosition();}, [component](SHVec3 const& vec){component->SetPosition(vec);}); - SHEditorWidgets::DragVec3("Direction", {"X", "Y", "Z"}, [component](){return component->GetDirection();}, [component](SHVec3 const& vec){component->SetDirection(vec);}); - SHEditorWidgets::ColorPicker("Color", [component](){return component->GetColor();}, [component](SHVec4 const& rgba){component->SetColor(rgba);}); - SHEditorWidgets::DragFloat("Strength", [component](){return component->GetStrength();}, [component](float const& value){component->SetStrength(value);}); + SHEditorWidgets::DragVec3("Position", { "X", "Y", "Z" }, [component]() {return component->GetPosition(); }, [component](SHVec3 const& vec) {component->SetPosition(vec); }); + SHEditorWidgets::DragVec3("Direction", { "X", "Y", "Z" }, [component]() {return component->GetDirection(); }, [component](SHVec3 const& vec) {component->SetDirection(vec); }); + SHEditorWidgets::ColorPicker("Color", [component]() {return component->GetColor(); }, [component](SHVec4 const& rgba) {component->SetColor(rgba); }); + SHEditorWidgets::DragFloat("Strength", [component]() {return component->GetStrength(); }, [component](float const& value) {component->SetStrength(value); }); } else { DrawContextMenu(component); } } -} \ No newline at end of file + + template<> + static void DrawComponent(SHRenderable* component) + { + if (!component) + return; + 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& mesh = component->GetMesh(); + + SHEditorWidgets::DragDropReadOnlyField("Mesh", std::to_string(SHResourceManager::GetAssetID(mesh).value_or(0)).data(), [component]() + { + Handle const& mesh = component->GetMesh(); + return SHResourceManager::GetAssetID(mesh).value_or(0); + }, + [component](AssetID const& id) + { + component->SetMesh(SHResourceManager::LoadOrGet(id)); + SHResourceManager::FinaliseChanges(); + }, SHDragDrop::DRAG_RESOURCE); + } + else + { + DrawContextMenu(component); + } + } +} diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp index ebb241ef..2fecae25 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorInspector.cpp @@ -1,6 +1,6 @@ #include "SHpch.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "SHEditorInspector.h" #include "ECS_Base/SHECSMacros.h" @@ -10,17 +10,15 @@ #include "Editor/SHImGuiHelpers.hpp" #include "Editor/SHEditorWidgets.hpp" -#include "SHEditorComponentView.hpp" -#include "ECS_Base/UnitTesting/SHTestComponents.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Scripting/SHScriptEngine.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "ECS_Base/Managers/SHSystemManager.h" -#include "AudioSystem/SHAudioSystem.h" #include "Physics/Components/SHRigidBodyComponent.h" #include "Physics/Components/SHColliderComponent.h" #include "Camera/SHCameraComponent.h" +#include "Camera/SHCameraArmComponent.h" +#include "SHEditorComponentView.h" namespace SHADE { @@ -30,8 +28,17 @@ namespace SHADE bool selected = false; if(!SHComponentManager::HasComponent(eid)) { - if(selected = ImGui::Selectable(std::format("Add {}", rttr::type::get().get_name().data()).data()); selected) + const char* componentName = rttr::type::get().get_name().data(); + if(selected = ImGui::Selectable(std::format("Add {}", componentName).data()); selected) SHComponentManager::AddComponent(eid); + if(ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Adds", componentName); ImGui::SameLine(); + ImGui::TextColored(ImGuiColors::green, "%s", componentName); ImGui::SameLine(); + ImGui::Text("to this entity", componentName); + ImGui::EndTooltip(); + } } return selected; } @@ -42,13 +49,26 @@ namespace SHADE bool selected = false; if (!SHComponentManager::HasComponent(eid)) { - if(selected = ImGui::Selectable(std::format("Add {}", rttr::type::get().get_name().data()).data()); selected) + const char* componentName = rttr::type::get().get_name().data(); + + if(selected = ImGui::Selectable(std::format("Add {}", componentName).data()); selected) { if(SHComponentManager::GetComponent_s(eid) == nullptr) SHComponentManager::AddComponent(eid); SHComponentManager::AddComponent(eid); } + if(ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + 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::EndTooltip(); + } } return selected; } @@ -107,6 +127,9 @@ namespace SHADE if (auto cameraComponent = SHComponentManager::GetComponent_s(eid)) { DrawComponent(cameraComponent); + }if (auto cameraArmComponent = SHComponentManager::GetComponent_s(eid)) + { + DrawComponent(cameraArmComponent); } ImGui::Separator(); // Render Scripts @@ -117,6 +140,8 @@ namespace SHADE { DrawAddComponentButton(eid); DrawAddComponentButton(eid); + DrawAddComponentButton(eid); + DrawAddComponentButton(eid); // Components that require Transforms diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index c33f4fb6..06c0c2ae 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -3,7 +3,7 @@ //#==============================================================# //|| SHADE Includes || //#==============================================================# -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "SHEditorMenuBar.h" #include "Editor/IconsMaterialDesign.h" #include "Editor/Command/SHCommandManager.h" @@ -175,19 +175,37 @@ namespace SHADE ImGui::BeginDisabled(editor->editorState == SHEditor::State::PLAY); if(ImGui::SmallButton(ICON_MD_PLAY_ARROW)) { + const SHEditorStateChangeEvent STATE_CHANGE_EVENT + { + .previousState = editor->editorState + }; editor->editorState = SHEditor::State::PLAY; + + SHEventManager::BroadcastEvent(STATE_CHANGE_EVENT, SH_EDITOR_ON_PLAY_EVENT); } ImGui::EndDisabled(); ImGui::BeginDisabled(editor->editorState == SHEditor::State::PAUSE); if(ImGui::SmallButton(ICON_MD_PAUSE)) { + const SHEditorStateChangeEvent STATE_CHANGE_EVENT + { + .previousState = editor->editorState + }; editor->editorState = SHEditor::State::PAUSE; + + SHEventManager::BroadcastEvent(STATE_CHANGE_EVENT, SH_EDITOR_ON_PAUSE_EVENT); } ImGui::EndDisabled(); ImGui::BeginDisabled(editor->editorState == SHEditor::State::STOP); if(ImGui::SmallButton(ICON_MD_STOP)) { + const SHEditorStateChangeEvent STATE_CHANGE_EVENT + { + .previousState = editor->editorState + }; editor->editorState = SHEditor::State::STOP; + + SHEventManager::BroadcastEvent(STATE_CHANGE_EVENT, SH_EDITOR_ON_STOP_EVENT); } ImGui::EndDisabled(); ImGui::EndMenuBar(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h index 7cbcd696..e4f1d20b 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.h @@ -20,4 +20,10 @@ namespace SHADE float menuBarHeight = 20.0f; std::vector layoutPaths; };//class SHEditorMenuBar + + struct SHEditorStateChangeEvent + { + SHEditor::State previousState; + }; + }//namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp index f5170999..d6ef8d19 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.cpp @@ -5,13 +5,16 @@ #include "ImGuizmo.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/IconsMaterialDesign.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" #include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" #include +#include "Camera/SHCameraSystem.h" +#include "FRC/SHFramerateController.h" + constexpr std::string_view windowName = "\xef\x80\x95 Viewport"; namespace SHADE @@ -30,8 +33,15 @@ namespace SHADE void SHEditorViewport::Update() { SHEditorWindow::Update(); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f,0.0f)); - if(Begin()) + if (shouldUpdateCamera) + { + auto camSystem = SHSystemManager::GetSystem(); + camSystem->UpdateEditorCamera(SHFrameRateController::GetRawDeltaTime()); + shouldUpdateCamera = false; + } + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + + if (Begin()) { ImGuizmo::SetDrawlist(); DrawMenuBar(); @@ -39,21 +49,38 @@ namespace SHADE auto const& descriptorSet = gfxSystem->GetPostOffscreenRenderSystem()->GetDescriptorSetGroup()->GetVkHandle()[0]; auto mousePos = ImGui::GetMousePos(); beginCursorPos = ImGui::GetCursorScreenPos(); - viewportMousePos = {mousePos.x - beginCursorPos.x, mousePos.y - beginCursorPos.y}; - gfxSystem->GetMousePickSystem ()->SetViewportMousePos (viewportMousePos); + viewportMousePos = { mousePos.x - beginCursorPos.x, mousePos.y - beginCursorPos.y }; + gfxSystem->GetMousePickSystem()->SetViewportMousePos(viewportMousePos); - ImGui::Image((ImTextureID)descriptorSet, {beginContentRegionAvailable.x, beginContentRegionAvailable.y}); + ImGui::Image((ImTextureID)descriptorSet, { beginContentRegionAvailable.x, beginContentRegionAvailable.y }); - if(ImGui::IsWindowHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Right)) + if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Right)) { ImGui::SetMouseCursor(ImGuiMouseCursor_None); ImGui::SetCursorScreenPos(ImGui::GetMousePos()); ImGui::PushStyleColor(ImGuiCol_Text, ImGuiColors::green); ImGui::Text(ICON_FA_EYE); ImGui::PopStyleColor(); + + shouldUpdateCamera = true; + } + if (ImGui::IsWindowFocused() && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) + { + if (ImGui::IsKeyReleased(ImGuiKey_Q)) + { + transformGizmo.operation = SHTransformGizmo::Operation::TRANSLATE; + } + if (ImGui::IsKeyReleased(ImGuiKey_W)) + { + transformGizmo.operation = SHTransformGizmo::Operation::ROTATE; + } + if (ImGui::IsKeyReleased(ImGuiKey_E)) + { + transformGizmo.operation = SHTransformGizmo::Operation::SCALE; + } } } - ImGuizmo::SetRect(beginCursorPos.x , beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); + ImGuizmo::SetRect(beginCursorPos.x, beginCursorPos.y, beginContentRegionAvailable.x, beginContentRegionAvailable.y); transformGizmo.Draw(); ImGui::End(); ImGui::PopStyleVar(); @@ -72,11 +99,12 @@ namespace SHADE //auto pos = ImGui::GetCursorPos(); //windowCursorPos = {} - if(beginContentRegionAvailable.x == 0 || beginContentRegionAvailable.y == 0) + if (beginContentRegionAvailable.x == 0 || beginContentRegionAvailable.y == 0) { - beginContentRegionAvailable = windowSize; + beginContentRegionAvailable = windowSize; } gfxSystem->PrepareResize(static_cast(beginContentRegionAvailable.x), static_cast(beginContentRegionAvailable.y)); + shouldUpdateCamera = true; } void SHEditorViewport::OnPosChange() @@ -86,44 +114,63 @@ namespace SHADE void SHEditorViewport::DrawMenuBar() noexcept { - if(ImGui::BeginMenuBar()) + if (ImGui::BeginMenuBar()) { + ImGui::BeginDisabled(ImGui::IsWindowFocused() && ImGui::IsMouseDown(ImGuiMouseButton_Right)); bool const isTranslate = transformGizmo.operation == SHTransformGizmo::Operation::TRANSLATE; ImGui::BeginDisabled(isTranslate); - if(isTranslate) + if (isTranslate) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::Button(ICON_MD_OPEN_WITH)) + if (ImGui::Button(ICON_MD_OPEN_WITH)) { transformGizmo.operation = SHTransformGizmo::Operation::TRANSLATE; } ImGui::EndDisabled(); - if(isTranslate) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + ImGui::BeginTooltip(); + ImGui::Text("Translate [Q]"); + ImGui::EndTooltip(); + } + if (isTranslate) ImGui::PopStyleColor(); bool const isRotate = transformGizmo.operation == SHTransformGizmo::Operation::ROTATE; ImGui::BeginDisabled(isRotate); - if(isRotate) + if (isRotate) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::Button(ICON_MD_AUTORENEW)) + if (ImGui::Button(ICON_MD_AUTORENEW)) { transformGizmo.operation = SHTransformGizmo::Operation::ROTATE; } ImGui::EndDisabled(); - if(isRotate) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + ImGui::BeginTooltip(); + ImGui::Text("Rotate [W]"); + ImGui::EndTooltip(); + } + if (isRotate) ImGui::PopStyleColor(); bool const isScale = transformGizmo.operation == SHTransformGizmo::Operation::SCALE; ImGui::BeginDisabled(isScale); - if(isScale) + if (isScale) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_CheckMark]); - if(ImGui::Button(ICON_MD_EXPAND)) + if (ImGui::Button(ICON_MD_EXPAND)) { transformGizmo.operation = SHTransformGizmo::Operation::SCALE; } ImGui::EndDisabled(); - if(isScale) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + ImGui::BeginTooltip(); + ImGui::Text("Scale [E]"); + ImGui::EndTooltip(); + } + if (isScale) ImGui::PopStyleColor(); - + ImGui::EndDisabled(); ImGui::EndMenuBar(); } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h index 80b13285..0fae4317 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h +++ b/SHADE_Engine/src/Editor/EditorWindow/ViewportWindow/SHEditorViewport.h @@ -14,7 +14,7 @@ namespace SHADE { - class SHEditorViewport final : public SHEditorWindow + class SHEditorViewport final : public SHEditorWindow { public: SHEditorViewport(); @@ -28,5 +28,6 @@ namespace SHADE private: void DrawMenuBar() noexcept; SHVec2 beginCursorPos; + bool shouldUpdateCamera = false; };//class SHEditorViewport }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp index 3c984051..e3bbc809 100644 --- a/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp +++ b/SHADE_Engine/src/Editor/Gizmos/SHTransformGizmo.cpp @@ -3,7 +3,7 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "Editor/SHImGuiHelpers.hpp" #include #include @@ -63,6 +63,9 @@ namespace SHADE if (selectedEntityTransformComponent == nullptr) return; + if(!selectedEntityTransformComponent->isActive) + return; + SHMatrix mat = selectedEntityTransformComponent->GetTRS(); useSnap = ImGui::IsKeyDown(ImGuiKey_LeftCtrl); if(useSnap) diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 06fadfee..cf5056a5 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -21,7 +21,7 @@ #include "Graphics/MiddleEnd/Interface/SHViewport.h" #include "Graphics/MiddleEnd/Interface/SHRenderer.h" -#include "SHEditor.hpp" +#include "SHEditor.h" #include "SHEditorWidgets.hpp" #include "Math/Transform/SHTransformSystem.h" @@ -175,7 +175,7 @@ namespace SHADE ImFontConfig icons_config{}; icons_config.MergeMode = true; icons_config.GlyphOffset.y = 5.f; constexpr ImWchar icon_ranges_fa[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFont* UIFontFA = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/fa-solid-900.ttf", 20.f, &icons_config, icon_ranges_fa); //TODO: Change to config based assets path - constexpr ImWchar icon_ranges_md[] = { ICON_MIN_MD, ICON_MAX_MD, 0 }; + constexpr ImWchar icon_ranges_md[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 }; ImFont* UIFontMD = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges_md); //TODO: Change to config based assets path io->Fonts->Build(); } @@ -293,6 +293,7 @@ namespace SHADE //#==============================================================# void SHEditor::InitBackend() { +#ifdef SHEDITOR if(ImGui_ImplSDL2_InitForVulkan(sdlWindow) == false) { SHLOG_CRITICAL("Editor backend initialisation; Failed to perform SDL initialisation for Vulkan") @@ -339,6 +340,7 @@ namespace SHADE renderGraph->GetNode("ImGui Node")->GetSubpass("ImGui Draw")->AddExteriorDrawCalls([](Handle& cmd) { ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd->GetVkCommandBuffer()); }); +#endif } void SHEditor::PollPicking() diff --git a/SHADE_Engine/src/Editor/SHEditor.hpp b/SHADE_Engine/src/Editor/SHEditor.h similarity index 98% rename from SHADE_Engine/src/Editor/SHEditor.hpp rename to SHADE_Engine/src/Editor/SHEditor.h index 34405390..624069db 100644 --- a/SHADE_Engine/src/Editor/SHEditor.hpp +++ b/SHADE_Engine/src/Editor/SHEditor.h @@ -15,7 +15,7 @@ #include "ECS_Base/System/SHSystemRoutine.h" #include "Resource/SHHandle.h" #include "EditorWindow/SHEditorWindow.h" -#include "Tools/SHLogger.h" +#include "Tools/SHLog.h" #include "Gizmos/SHTransformGizmo.h" @@ -76,7 +76,7 @@ namespace SHADE } else { - SHLOG_WARNING("Attempt to create duplicate of Editor window type") + SHLog::Warning("Attempt to create duplicate of Editor window type"); } } diff --git a/SHADE_Engine/src/Editor/SHEditorUI.cpp b/SHADE_Engine/src/Editor/SHEditorUI.cpp index 0137b951..cc63c565 100644 --- a/SHADE_Engine/src/Editor/SHEditorUI.cpp +++ b/SHADE_Engine/src/Editor/SHEditorUI.cpp @@ -16,6 +16,7 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include #include "SHEditorWidgets.hpp" +#include "ECS_Base/Managers/SHEntityManager.h" namespace SHADE { @@ -156,7 +157,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::Checkbox("#", &value); + return ImGui::Checkbox("##", &value); } bool SHEditorUI::InputInt(const std::string& label, int& value, bool* isHovered) { @@ -164,7 +165,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::InputInt("#", &value, + return ImGui::InputInt("##", &value, 1, 10, ImGuiInputTextFlags_EnterReturnsTrue); } @@ -175,7 +176,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - const bool CHANGED = InputInt("#", signedVal); + const bool CHANGED = InputInt("##", signedVal); if (CHANGED) { signedVal = std::clamp(signedVal, 0, std::numeric_limits::max()); @@ -189,7 +190,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::InputFloat("#", &value, + return ImGui::InputFloat("##", &value, 0.1f, 1.0f, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue); } @@ -199,7 +200,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::InputDouble("#", &value, + return ImGui::InputDouble("##", &value, 0.1, 1.0, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue); } @@ -209,7 +210,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - return ImGui::InputDouble("#", &value, + return ImGui::InputDouble("##", &value, 1.0, 45.0, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue); } @@ -279,7 +280,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - const bool CHANGED = ImGui::InputText("#", &buffer[0], TEXT_FIELD_MAX_LENGTH); + const bool CHANGED = ImGui::InputText("##", &buffer[0], TEXT_FIELD_MAX_LENGTH); if (CHANGED) { value = std::string(buffer.data(), buffer.data() + TEXT_FIELD_MAX_LENGTH); @@ -287,6 +288,35 @@ namespace SHADE return CHANGED; } + bool SHEditorUI::InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered) + { + ImGui::Text(label.c_str()); + if (isHovered) + *isHovered = ImGui::IsItemHovered(); + ImGui::SameLine(); + SHEntity* entity = SHEntityManager::GetEntityByID(value); + std::ostringstream oss; + if (entity) + { + oss << value << ": " << entity->name; + } + std::string entityName = oss.str(); + bool changed = ImGui::InputText("##", &entityName, ImGuiInputTextFlags_ReadOnly); + if (SHDragDrop::BeginTarget()) + { + if (const std::vector* payload = SHDragDrop::AcceptPayload>(SHDragDrop::DRAG_EID)) + { + if (!payload->empty()) + { + value = payload->at(0); + changed = true; + } + SHDragDrop::EndTarget(); + } + } + return changed; + } + bool SHEditorUI::InputEnumCombo(const std::string& label, int& v, const std::vector& enumNames, bool* isHovered) { // Clamp input value @@ -297,7 +327,7 @@ namespace SHADE if (isHovered) *isHovered = ImGui::IsItemHovered(); ImGui::SameLine(); - if (ImGui::BeginCombo("#", INITIAL_NAME.c_str(), ImGuiComboFlags_None)) + if (ImGui::BeginCombo("##", INITIAL_NAME.c_str(), ImGuiComboFlags_None)) { for (int i = 0; i < enumNames.size(); ++i) { diff --git a/SHADE_Engine/src/Editor/SHEditorUI.h b/SHADE_Engine/src/Editor/SHEditorUI.h index f3051aa1..d4104639 100644 --- a/SHADE_Engine/src/Editor/SHEditorUI.h +++ b/SHADE_Engine/src/Editor/SHEditorUI.h @@ -308,6 +308,14 @@ namespace SHADE /// True if the value was changed. static bool InputTextField(const std::string& label, std::string& value, bool* isHovered = nullptr); /// + /// Creates a drag field widget for int input. + /// + /// Label used to identify this widget. + /// Reference to the variable to store the result. + /// The type of enum to input. diff --git a/SHADE_Engine/src/Editor/SHEditorWidgets.hpp b/SHADE_Engine/src/Editor/SHEditorWidgets.hpp index 46538827..053348d7 100644 --- a/SHADE_Engine/src/Editor/SHEditorWidgets.hpp +++ b/SHADE_Engine/src/Editor/SHEditorWidgets.hpp @@ -22,6 +22,8 @@ #include #include +#include "DragDrop/SHDragDrop.hpp" + namespace SHADE { class SH_API SHEditorWidgets @@ -41,7 +43,7 @@ namespace SHADE ImGui::BeginGroup(); auto itemSpacing = ImGui::GetStyle().ItemSpacing; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.2f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); auto frameHeight = ImGui::GetFrameHeight(); @@ -86,7 +88,7 @@ namespace SHADE auto itemSpacing = ImGui::GetStyle().ItemSpacing; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.2f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); auto frameHeight = ImGui::GetFrameHeight(); @@ -127,7 +129,7 @@ namespace SHADE ImGui::GetWindowDrawList()->AddRect( frameRect.Min, frameRect.Max, - ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Button)), + ImColor(ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)), halfFrame.x); ImGui::PopClipRect(); @@ -174,15 +176,19 @@ namespace SHADE ImGui::SetColumnWidth(-1, 80.0f); ImGui::Text(label.c_str()); if (isHovered) - *isHovered = ImGui::IsItemHovered(); + *isHovered |= ImGui::IsItemHovered(); ImGui::NextColumn(); for (std::size_t i = 0; i < N; ++i) { ImGui::PushID(static_cast(i)); - ImGui::TextUnformatted(componentLabels[i].c_str(), ImGui::FindRenderedTextEnd(componentLabels[i].c_str())); ImGui::SameLine(); + ImGui::TextUnformatted(componentLabels[i].c_str(), ImGui::FindRenderedTextEnd(componentLabels[i].c_str())); + if (isHovered) + *isHovered |= ImGui::IsItemHovered(); + ImGui::SameLine(); ImGui::SetNextItemWidth(80.0f); valueChanged |= ImGui::DragFloat("##v", values[i], speed, valueMin, valueMax, displayFormat, flags); - + if (isHovered) + *isHovered |= ImGui::IsItemHovered(); const ImVec2 min = ImGui::GetItemRectMin(); const ImVec2 max = ImGui::GetItemRectMax(); const float spacing = g.Style.FrameRounding; @@ -203,23 +209,31 @@ namespace SHADE } static bool DragVec2(const std::string& label, std::vectorconst& componentLabels, std::function get, - std::function set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f, + std::function set, bool const& isAnAngleInRad = false, std::string_view const& tooltip = {}, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, ImGuiSliderFlags flags = 0) { SHVec2 values = get(); + if(isAnAngleInRad) + { + values = {SHMath::RadiansToDegrees(values.x), SHMath::RadiansToDegrees(values.y)}; + } bool const changed = DragN(label, componentLabels, { &values.x, &values.y }, speed, displayFormat, valueMin, valueMax, flags); static bool startRecording = false; if (changed) { + if(isAnAngleInRad) + { + values = {SHMath::DegreesToRadians(values.x), SHMath::DegreesToRadians(values.y)}; + } SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), values, set)), startRecording); if (!startRecording) startRecording = true; } if (startRecording && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) startRecording = false; - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); @@ -230,17 +244,24 @@ namespace SHADE } static bool DragVec3(const std::string& label, std::vectorconst& componentLabels, std::function get, - std::function set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f, + std::function set, bool const& isAnAngleInRad = false, std::string_view const& tooltip = {}, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, ImGuiSliderFlags flags = 0) { SHVec3 values = get(); - bool const changed = DragN(label, componentLabels, { &values.x, &values.y, &values.z }, speed, displayFormat, valueMin, valueMax, flags); - - + if(isAnAngleInRad) + { + values = {SHMath::RadiansToDegrees(values.x), SHMath::RadiansToDegrees(values.y), SHMath::RadiansToDegrees(values.z)}; + } + bool isHovered = false; + bool const changed = DragN(label, componentLabels, { &values.x, &values.y, &values.z }, speed, displayFormat, valueMin, valueMax, flags, &isHovered); static bool startRecording = false; if (changed) { SHVec3 old = get(); + if(isAnAngleInRad) + { + values = {SHMath::DegreesToRadians(values.x), SHMath::DegreesToRadians(values.y), SHMath::DegreesToRadians(values.z)}; + } SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(old, values, set)), startRecording); if (!startRecording) startRecording = true; @@ -249,9 +270,9 @@ namespace SHADE { startRecording = false; } - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (isHovered) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); @@ -262,14 +283,22 @@ namespace SHADE } static bool DragVec4(const std::string& label, std::vectorconst& componentLabels, std::function get, - std::function set, float speed = 0.1f, const char* displayFormat = "%.3f", std::string_view const& tooltip = {}, float valueMin = 0.0f, float valueMax = 0.0f, + std::function set, bool const& isAnAngleInRad = false, std::string_view const& tooltip = {}, float speed = 0.1f, const char* displayFormat = "%.3f", float valueMin = 0.0f, float valueMax = 0.0f, ImGuiSliderFlags flags = 0) { SHVec4 values = get(); + if(isAnAngleInRad) + { + values = {SHMath::RadiansToDegrees(values.x), SHMath::RadiansToDegrees(values.y), SHMath::RadiansToDegrees(values.z), SHMath::RadiansToDegrees(values.w)}; + } bool const changed = DragN(label, componentLabels, { &values.x, &values.y, &values.z, &values.w }, speed, displayFormat, valueMin, valueMax, flags); static bool startRecording = false; if (changed) { + if(isAnAngleInRad) + { + values = {SHMath::DegreesToRadians(values.x), SHMath::DegreesToRadians(values.y), SHMath::DegreesToRadians(values.z), SHMath::DegreesToRadians(values.w)}; + } SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), values, set)), startRecording); if (!startRecording) startRecording = true; @@ -278,9 +307,9 @@ namespace SHADE { startRecording = false; } - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); @@ -297,7 +326,7 @@ namespace SHADE static void TextLabel(std::string_view const& text, bool sameLine = true) { const ImVec2 textSize = ImGui::CalcTextSize(text.data(), NULL, true); - if(textSize.x > 0.0f) + if (textSize.x > 0.0f) { ImGui::Text(text.data()); ImGui::SameLine(); @@ -310,32 +339,32 @@ namespace SHADE ImGui::BeginGroup(); ImGui::PushID(label.data()); TextLabel(label); - if (ImGui::Checkbox("##", &value)) + bool const changed = ImGui::Checkbox("##", &value); + if (changed) { SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), value, set)), false); - return true; } ImGui::PopID(); ImGui::EndGroup(); - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); ImGui::EndTooltip(); } } - return false; + return changed; } template - static bool RadioButton(std::vector const& label, std::vector const& listTypes, std::function get, std::function set ,std::string_view const& tooltip = {}) + static bool RadioButton(std::vector const& label, std::vector const& listTypes, std::function get, std::function set, std::string_view const& tooltip = {}) { T type = get(); ImGui::BeginGroup(); ImGui::PushID(label.data()); - TextLabel(label); + //TextLabel(label); for (size_t i = 0; i < listTypes.size(); i++) { if (ImGui::RadioButton(label[i].c_str(), type == listTypes[i])) @@ -366,12 +395,11 @@ namespace SHADE ImGui::BeginGroup(); ImGui::PushID(label.data()); TextLabel(label); - if (ImGui::InputText("##", &text, flag, callback, userData)) + bool const changed = ImGui::InputText("##", &text, flag, callback, userData); + if (changed) { if (ImGui::IsItemDeactivatedAfterEdit()) SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), text, set)), false); - - return true; } ImGui::PopID(); ImGui::EndGroup(); @@ -384,7 +412,37 @@ namespace SHADE ImGui::EndTooltip(); } } - return false; + return changed; + } + + template + static bool DragDropReadOnlyField(std::string const& label, std::string_view const& fieldVTextValue, std::function const& get, std::function const& set, SHDragDrop::DragDropTag const& dragDropTag, std::string_view const& tooltip = {}) + { + std::string text = fieldVTextValue.data(); + ImGui::BeginGroup(); + ImGui::PushID(label.data()); + TextLabel(label); + bool const changed = ImGui::InputText("##", &text, ImGuiInputTextFlags_ReadOnly, nullptr, nullptr); + if(SHDragDrop::BeginTarget()) + { + if(T* payload = SHDragDrop::AcceptPayload(dragDropTag)) + { + SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), *payload, set)), false); + SHDragDrop::EndTarget(); + } + } + ImGui::PopID(); + ImGui::EndGroup(); + if (!tooltip.empty()) + { + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text(tooltip.data()); + ImGui::EndTooltip(); + } + } + return changed; } template @@ -442,9 +500,9 @@ namespace SHADE } ImGui::PopID(); ImGui::EndGroup(); - if(!tooltip.empty()) + if (!tooltip.empty()) { - if(ImGui::IsItemHovered()) + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Text(tooltip.data()); diff --git a/SHADE_Engine/src/Events/SHEventDefines.h b/SHADE_Engine/src/Events/SHEventDefines.h index 804fbfec..d649fabf 100644 --- a/SHADE_Engine/src/Events/SHEventDefines.h +++ b/SHADE_Engine/src/Events/SHEventDefines.h @@ -13,4 +13,7 @@ constexpr SHEventIdentifier SH_COMPONENT_REMOVED_EVENT { 4 }; constexpr SHEventIdentifier SH_SCENEGRAPH_CHANGE_PARENT_EVENT { 5 }; constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_ADDED_EVENT { 6 }; constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_REMOVED_EVENT { 7 }; +constexpr SHEventIdentifier SH_EDITOR_ON_PLAY_EVENT { 8 }; +constexpr SHEventIdentifier SH_EDITOR_ON_PAUSE_EVENT { 9 }; +constexpr SHEventIdentifier SH_EDITOR_ON_STOP_EVENT { 10 }; diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp index a744c795..fb64bcc2 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp @@ -282,6 +282,11 @@ namespace SHADE } + void SHVkCommandBuffer::SetLineWidth(float lineWidth) noexcept + { + vkCommandBuffer.setLineWidth(lineWidth); + } + /***************************************************************************/ /*! diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h index 4c978c34..bc0d4b04 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h @@ -115,6 +115,7 @@ namespace SHADE // Dynamic State void SetViewportScissor (float vpWidth, float vpHeight, uint32_t sWidth, uint32_t sHeight, float vpX = 0.0f, float vpY = 0.0f, int32_t sX = 0.0f, int32_t sY = 0.0f, float vpMinDepth = 0.0f, float vpMaxDepth = 1.0f) noexcept; + void SetLineWidth (float lineWidth) noexcept; // Binding Commands void BindPipeline (Handle const& pipelineHdl) noexcept; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 1bda7c90..22d1875b 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -74,11 +74,12 @@ namespace SHADE void SHBatch::Remove(const SHRenderable* renderable) { - // Check if we have a SubBatch with the same mesh yet + // Check if we have a SubBatch with the existing mesh yet (if changed, we use the old mesh) + Handle prevSubBatchMesh = renderable->HasMeshChanged() ? renderable->GetPrevMesh() : renderable->GetMesh(); auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch) - { - return batch.Mesh == renderable->GetMesh(); - }); + { + return batch.Mesh == prevSubBatchMesh; + }); // Attempt to remove if it exists if (subBatch == subBatches.end()) @@ -88,9 +89,7 @@ namespace SHADE // Check if other renderables in subBatches contain the same material instance bool matUnused = true; - Handle matToCheck = renderable->HasMaterialChanged() ? renderable->GetPrevMaterial() : renderable->GetMaterial(); - for (const auto& sb : subBatches) { // Check material usage diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp index 434e7163..df389879 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp @@ -37,9 +37,9 @@ namespace SHADE // Check if we have a batch with the same pipeline first auto batch = std::find_if(batches.begin(), batches.end(), [&](const SHBatch& batch) - { - return batch.GetPipeline() == PIPELINE; - }); + { + return batch.GetPipeline() == PIPELINE; + }); // Create one if not found diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp new file mode 100644 index 00000000..08019665 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -0,0 +1,202 @@ +/************************************************************************************//*! +\file SHDebugDrawSystem.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 16, 2022 +\brief Contains the definition of functions of the SHDebugDrawSystem 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. +*//*************************************************************************************/ +#include "SHpch.h" +#include "SHDebugDrawSystem.h" +// STL Includes +#include +// Project Includes +#include "../Meshes/SHMeshData.h" +#include "../Meshes/SHPrimitiveGenerator.h" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "SHGraphicsSystem.h" +#include "Graphics/Devices/SHVkLogicalDevice.h" +#include "../../SHVkUtil.h" +#include "Graphics/MiddleEnd/Interface/SHViewport.h" +#include "Graphics/MiddleEnd/Interface/SHRenderer.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* DrawRoutine */ + /*---------------------------------------------------------------------------------*/ + SHDebugDrawSystem::ProcessPointsRoutine::ProcessPointsRoutine() + : SHSystemRoutine("Debug Draw", true) + { + SystemFamily::GetID(); + } + + void SHDebugDrawSystem::ProcessPointsRoutine::Execute(double dt) noexcept + { + auto gfxSys = SHSystemManager::GetSystem(); + if (!gfxSys) + { + SHLOG_WARNING("[DebugDraw] Attempted to do debug draw without a graphics system."); + return; + } + + // Get current frame index + const uint32_t FRAME_IDX = gfxSys->GetCurrentFrameIndex(); + + // Create the buffer if it doesn't exist or just update it + SHDebugDrawSystem* system = static_cast(GetSystem()); + system->numPoints[FRAME_IDX] = system->points.size(); + const uint32_t DATA_SIZE = sizeof(PointVertex) * system->points.size(); + if (DATA_SIZE > 0) + { + system->vertexBuffers[FRAME_IDX]->WriteToMemory(system->points.data(), DATA_SIZE, 0, 0); + } + + // Reset for next frame + system->points.clear(); + } + + /*---------------------------------------------------------------------------------*/ + /* SHSystem overrides */ + /*---------------------------------------------------------------------------------*/ + void SHDebugDrawSystem::Init() + { + // Register function for subpass + const auto* GFX_SYSTEM = SHSystemManager::GetSystem(); + auto const& RENDERERS = GFX_SYSTEM->GetDefaultViewport()->GetRenderers(); + auto renderGraph = RENDERERS[SHGraphicsConstants::RenderGraphIndices::WORLD]->GetRenderGraph(); + auto subPass = renderGraph->GetNode("Debug Draw")->GetSubpass("Debug Draw"); + subPass->AddExteriorDrawCalls([this, GFX_SYSTEM](Handle& cmdBuffer) + { + // Get Current frame index + const uint32_t FRAME_IDX = GFX_SYSTEM->GetCurrentFrameIndex(); + + // Don't draw if no points + if (numPoints[FRAME_IDX] <= 0) + return; + + cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); + cmdBuffer->SetLineWidth(LineWidth); + cmdBuffer->BindVertexBuffer(0, vertexBuffers[FRAME_IDX], 0); + cmdBuffer->DrawArrays(numPoints[FRAME_IDX], 1, 0, 0); + }); + + // Reset trackers + std::fill_n(numPoints.begin(), numPoints.size(), 0); + + // Allocate buffers + static constexpr uint32_t BUFFER_SIZE = MAX_POINTS * sizeof(PointVertex); + for (Handle& bufHandle : vertexBuffers) + { + bufHandle = GFX_SYSTEM->GetDevice()->CreateBuffer + ( + BUFFER_SIZE, + nullptr, + 0, + vk::BufferUsageFlagBits::eVertexBuffer, + VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, + VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT + ); + } + } + + void SHDebugDrawSystem::Exit() + { + for (auto vertexBuffer : vertexBuffers) + { + if (vertexBuffer) + vertexBuffer.Free(); + } + } + + /*---------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*---------------------------------------------------------------------------------*/ + void SHDebugDrawSystem::DrawLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) + { + if (points.size() > MAX_POINTS) + { + SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); + return; + } + + points.emplace_back(PointVertex{ startPt, color }); + points.emplace_back(PointVertex{ endPt, color }); + } + + void SHDebugDrawSystem::DrawTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3) + { + DrawPoly(color, { pt1, pt2, pt3 }); + } + + void SHDebugDrawSystem::DrawQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4) + { + DrawPoly(color, { pt1, pt2, pt3, pt4 }); + } + + void SHDebugDrawSystem::DrawPoly(const SHVec4& color, std::initializer_list pointList) + { + DrawPoly(color, pointList.begin(), pointList.end()); + } + + void SHDebugDrawSystem::DrawCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) + { + static const SHVec3 EXTENTS = SHVec3 { 0.5f, 0.5f, 0.5f }; + static const SHVec3 UNIT_BOT_LEFT_FRONT = SHVec3 { pos - EXTENTS }; + static const SHVec3 UNIT_BOT_RIGHT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_BOT_RIGHT_BACK = SHVec3 { pos + SHVec3 { EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_BOT_LEFT_BACK = SHVec3 { pos + SHVec3 { -EXTENTS.x, -EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_LEFT_BACK = SHVec3 { pos + SHVec3 { -EXTENTS.x, EXTENTS.y, EXTENTS.z } }; + static const SHVec3 UNIT_TOP_RIGHT_FRONT = SHVec3 { pos + SHVec3 { EXTENTS.x, EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_TOP_LEFT_FRONT = SHVec3 { pos + SHVec3 { -EXTENTS.x, EXTENTS.y, -EXTENTS.z } }; + static const SHVec3 UNIT_TOP_RIGHT_BACK = SHVec3 { pos + EXTENTS }; + + const SHVec3 BOT_LEFT_BACK = UNIT_BOT_LEFT_BACK * size; + const SHVec3 BOT_RIGHT_BACK = UNIT_BOT_RIGHT_BACK * size; + const SHVec3 BOT_LEFT_FRONT = UNIT_BOT_LEFT_FRONT * size; + const SHVec3 BOT_RIGHT_FRONT = UNIT_BOT_RIGHT_FRONT * size; + const SHVec3 TOP_LEFT_BACK = UNIT_TOP_LEFT_BACK * size; + const SHVec3 TOP_RIGHT_BACK = UNIT_TOP_RIGHT_BACK * size; + const SHVec3 TOP_LEFT_FRONT = UNIT_TOP_LEFT_FRONT * size; + const SHVec3 TOP_RIGHT_FRONT = UNIT_TOP_RIGHT_FRONT * size; + + DrawPoly + ( + color, + { + // Bottom Square + BOT_LEFT_BACK , BOT_RIGHT_BACK, + BOT_RIGHT_BACK , BOT_RIGHT_FRONT, + BOT_RIGHT_FRONT, BOT_LEFT_FRONT, + BOT_LEFT_FRONT , BOT_LEFT_BACK, + // Top Square + TOP_LEFT_BACK , TOP_RIGHT_BACK, + TOP_RIGHT_BACK , TOP_RIGHT_FRONT, + TOP_RIGHT_FRONT, TOP_LEFT_FRONT, + TOP_LEFT_FRONT , TOP_LEFT_BACK, + // Middle Lines + TOP_LEFT_BACK , BOT_LEFT_BACK, + TOP_RIGHT_BACK , BOT_RIGHT_BACK, + TOP_RIGHT_FRONT, BOT_RIGHT_FRONT, + TOP_LEFT_FRONT , BOT_LEFT_FRONT + } + ); + } + + void SHDebugDrawSystem::DrawSphere(const SHVec4& color, const SHVec3& pos, double radius) + { + if (spherePoints.empty()) + { + // Generate + static const SHMeshData SPHERE = SHPrimitiveGenerator::Sphere(); + for (const auto& idx : SPHERE.Indices) + { + spherePoints.emplace_back(SPHERE.VertexPositions[idx]); + } + } + DrawPoly(color, spherePoints.begin(), spherePoints.end()); + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h new file mode 100644 index 00000000..4b83958d --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h @@ -0,0 +1,159 @@ +/************************************************************************************//*! +\file SHDebugDrawSystem.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 16, 2022 +\brief Contains the definition of the SHDebugDrawSystem 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 Includes +#include "SH_API.h" +#include "Math/Vector/SHVec2.h" +#include "Math/Vector/SHVec3.h" +#include "Math/Vector/SHVec4.h" +#include "ECS_Base/System/SHSystem.h" +#include "ECS_Base/System/SHSystemRoutine.h" +#include "Resource/SHHandle.h" +#include "Graphics/Buffers/SHVkBuffer.h" +#include "SHGraphicsConstants.h" +#include "Math/SHColour.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + class SHVkBuffer; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + /// + /// Manages the Debug Draw system. + /// + class SH_API SHDebugDrawSystem final : public SHSystem + { + public: + /*---------------------------------------------------------------------------------*/ + /* System Routines */ + /*---------------------------------------------------------------------------------*/ + class SH_API ProcessPointsRoutine final : public SHSystemRoutine + { + public: + ProcessPointsRoutine(); + virtual void Execute(double dt) noexcept override final; + }; + + /*---------------------------------------------------------------------------------*/ + /* SHSystem overrides */ + /*---------------------------------------------------------------------------------*/ + virtual void Init() override final; + virtual void Exit() override final; + + /*---------------------------------------------------------------------------------*/ + /* Configuration Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Configures the line width used to draw all lines in the Debug Draw system. + /// + float LineWidth = 1.0f; + + /*---------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Renders a line between two points in world space. + /// + /// Colour of the line. + /// First point of the line. + /// Second point of the line. + void DrawLine(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + /// + /// Renders a triangle indicated by three points in world space. + /// + /// Colour of the triangle. + /// First point of the triangle. + /// Second point of the triangle. + /// Third point of the triangle. + void DrawTri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3); + /// + /// Renders a quadrilateral indicated by four points in world space. + /// + /// Colour of the quadrilateral. + /// First point of the triangle. + /// Second point of the quadrilateral. + /// Third point of the quadrilateral. + /// Third point of the quadrilateral. + void DrawQuad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4); + /// + /// Renders a polygon indicated by the specified set of points in world space. + /// + /// Colour of the polygon. + /// List of points for the polygon. + void DrawPoly(const SHVec4& color, std::initializer_list pointList); + /// + /// Renders a polygon indicated by the specified set of points in world space. + /// + /// Iterator for a STL-like container. + /// Colour of the polygon. + /// + /// Iterator to the first point of the point container. + /// + /// + /// One past last iterator of the point container. + /// + template + void DrawPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd); + /// + /// Renders a wireframe cube centered around the position specified in world space. + /// + /// Colour of the cube. + /// Position where the cube wil be centered at. + /// Size of the rendered cube. + void DrawCube(const SHVec4& color, const SHVec3& pos, const SHVec3& size); + /// + /// Renders a wireframe sphere centered around the position specified in world space. + /// + /// Colour of the sphere. + /// Position where the sphere wil be centered at. + /// Size of the rendered sphere. + void DrawSphere(const SHVec4& color, const SHVec3& pos, double radius); + + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + struct SH_API PointVertex + { + SHVec4 Position; + SHVec4 Color; + }; + using TripleBuffer = std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS>; + using TripleUInt = std::array; + + /*---------------------------------------------------------------------------------*/ + /* Constants */ + /*---------------------------------------------------------------------------------*/ + static constexpr uint32_t MAX_POINTS = 100'000; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + // CPU Buffers + std::vector points; + // GPU Buffers + TripleBuffer vertexBuffers; + TripleUInt numPoints; + // Cached Points for polygon drawing + std::vector spherePoints; + }; +} + +#include "SHDebugDrawSystem.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp new file mode 100644 index 00000000..14fbb42f --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.hpp @@ -0,0 +1,48 @@ +/************************************************************************************//*! +\file SHDebugDrawSystem.hpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 16, 2022 +\brief Contains the definition of template functions the SHDebugDrawSystem + 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 +#include "SHDebugDrawSystem.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*-----------------------------------------------------------------------------------*/ + template + void SHDebugDrawSystem::DrawPoly(const SHVec4& color, IterType pointListBegin, IterType pointListEnd) + { + // Ensure dereferenced type is SHVec3 + static_assert(std::is_same_v>, "Parameters to DrawPoly must be SHVec3."); + + // Check if points exceeded max + if (points.size() > MAX_POINTS) + { + SHLOG_WARNING("[DebugDraw] Exceeded maximum size of drawable debug elements."); + return; + } + + const size_t POINTS_COUNT = pointListEnd - pointListBegin; + // Invalid polygon + if (POINTS_COUNT < 2) + { + SHLOG_WARNING("[SHDebugDraw] Invalid polygon provided to DrawPoly()."); + return; + } + + const size_t POINTS_ROUNDED_COUNT = POINTS_COUNT / 2 * 2; + for (auto pointIter = pointListBegin; pointIter != (pointListBegin + POINTS_ROUNDED_COUNT); ++pointIter) + { + points.emplace_back(PointVertex{ *pointIter, color }); + } + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index af3b46e1..eb9269e6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -17,7 +17,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/Windowing/Surface/SHVkSurface.h" #include "Graphics/Swapchain/SHVkSwapchain.h" #include "Camera/SHCameraSystem.h" -#include "Editor/SHEditor.hpp" +#include "Editor/SHEditor.h" #include "ECS_Base/Managers/SHSystemManager.h" //#include "SHRenderer.h" #include "Graphics/Windowing/SHWindow.h" @@ -37,8 +37,10 @@ of DigiPen Institute of Technology is prohibited. #include "Assets/Asset Types/SHTextureAsset.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" #include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h" -#include "Graphics/RenderGraph/SHRenderGraphNodeCompute.h" #include "Assets/SHAssetManager.h" +#include "Resource/SHResourceManager.h" +#include "Graphics/SHVkUtil.h" +#include "Graphics/RenderGraph/SHRenderGraphNodeCompute.h" namespace SHADE { @@ -106,19 +108,16 @@ namespace SHADE descPool = device->CreateDescriptorPools(); // Create generic command buffer - //transferCmdPool = device->CreateCommandPool(SH_QUEUE_FAMILY_ARRAY_INDEX::GRAPHICS, SH_CMD_POOL_RESET::POOL_BASED, true); graphicsCmdPool = device->CreateCommandPool(SH_QUEUE_FAMILY_ARRAY_INDEX::GRAPHICS, SH_CMD_POOL_RESET::POOL_BASED, true); - transferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); - graphicsTexCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); - SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_VS.glsl"); - SHAssetManager::CompileAsset("../../Assets/Shaders/TestCube_FS.glsl"); - SHAssetManager::CompileAsset("../../Assets/Shaders/DeferredComposite_CS.glsl"); - SHAssetManager::CompileAsset("../../Assets/Shaders/SSAO_CS.glsl"); - SHAssetManager::CompileAsset("../../Assets/Shaders/SSAOBlur_CS.glsl"); - - shaderModuleLibrary.ImportAllShaderSource(device); - shaderModuleLibrary.ReflectAllShaderModules(); + // Load Built In Shaders + static constexpr AssetID VS_DEFAULT = 39210065; defaultVertShader = SHResourceManager::LoadOrGet(VS_DEFAULT); + 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); + static constexpr AssetID CS_COMPOSITE = 45072428; deferredCompositeShader = SHResourceManager::LoadOrGet(CS_COMPOSITE); + static constexpr AssetID SSAO = 38430899; ssaoShader = SHResourceManager::LoadOrGet(SSAO); + static constexpr AssetID SSAO_BLUR = 39760835; ssaoBlurShader = SHResourceManager::LoadOrGet(SSAO_BLUR); } void SHGraphicsSystem::InitSceneRenderGraph(void) noexcept @@ -220,7 +219,6 @@ namespace SHADE ssaoStorage->PrepareRotationVectorsVkData(device); - auto ssaoShader = shaderModuleLibrary.GetBuiltInShaderModule("SSAO_CS"); Handle ssaoPass = gBufferNode->AddNodeCompute(ssaoShader, {"Position", "Normals", "SSAO"}); auto ssaoDataBuffer = ssaoStorage->GetBuffer(); ssaoPass->ModifyWriteDescBufferComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_BUFFER_BINDING, { &ssaoDataBuffer, 1 }, 0, ssaoStorage->GetBuffer()->GetSizeStored()); @@ -228,21 +226,21 @@ namespace SHADE auto viewSamplerLayout = ssaoStorage->GetViewSamplerLayout(); ssaoPass->ModifyWriteDescImageComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_IMAGE_BINDING, {&viewSamplerLayout, 1}); - auto ssaoBlurShader = shaderModuleLibrary.GetBuiltInShaderModule("SSAOBlur_CS"); Handle ssaoBlurPass = gBufferNode->AddNodeCompute(ssaoBlurShader, { "SSAO", "SSAO Blur"}); /*-----------------------------------------------------------------------*/ /* DEFERRED COMPOSITE SUBPASS INIT */ /*-----------------------------------------------------------------------*/ - auto deferredCompositeShader = shaderModuleLibrary.GetBuiltInShaderModule("DeferredComposite_CS"); gBufferNode->AddNodeCompute(deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "SSAO Blur", "Scene" }); - /*-----------------------------------------------------------------------*/ - /* DUMMY SUBPASS TO TRANSITION SCENE FOR PRESENT USAGE */ - /*-----------------------------------------------------------------------*/ - // Dummy to transition scene to be used for shader read - auto dummyNode = worldRenderGraph->AddNode("Dummy Pass", { "Scene" }, {"G-Buffer"}); // no predecessors + // Set up Debug Draw Pass + auto debugDrawNode = worldRenderGraph->AddNode("Debug Draw", { "Scene" }, {"G-Buffer"}); + auto debugDrawSubpass = debugDrawNode->AddSubpass("Debug Draw"); + debugDrawSubpass->AddColorOutput("Scene"); + + // Dummy Node + auto dummyNode = worldRenderGraph->AddNode("Dummy Pass", { "Scene" }, { "Debug Draw" }); // no predecessors auto dummySubpass = dummyNode->AddSubpass("Dummy Subpass"); dummySubpass->AddInput("Scene"); @@ -262,10 +260,57 @@ namespace SHADE worldRenderer->SetCameraDirector(cameraSystem->CreateDirector()); - auto cubeVS = shaderModuleLibrary.GetBuiltInShaderModule("TestCube_VS"); - auto cubeFS = shaderModuleLibrary.GetBuiltInShaderModule("TestCube_FS"); + // Create default materials + defaultMaterial = AddMaterial(defaultVertShader, defaultFragShader, gBufferSubpass); - defaultMaterial = AddMaterial(cubeVS, cubeFS, gBufferSubpass); + // Create debug draw pipeline + auto debugDrawPipelineLayout = resourceManager.Create + ( + device, SHPipelineLayoutParams + { + .shaderModules = { debugVertShader, debugFragShader }, + .globalDescSetLayouts = SHGraphicsGlobalData::GetDescSetLayouts() + } + ); + debugDrawPipeline = resourceManager.Create(device, debugDrawPipelineLayout, nullptr, debugDrawNode->GetRenderpass(), debugDrawSubpass); + debugDrawPipeline->GetPipelineState().SetRasterizationState(SHRasterizationState + { + .polygonMode = vk::PolygonMode::eLine, + .cull_mode = vk::CullModeFlagBits::eNone + }); + debugDrawPipeline->GetPipelineState().SetInputAssemblyState(SHInputAssemblyState + { + .topology = vk::PrimitiveTopology::eLineList + }); + + SHVertexInputState debugDrawVertexInputState; + debugDrawVertexInputState.AddBinding(false, true, { SHVertexAttribute(SHAttribFormat::FLOAT_4D), SHVertexAttribute(SHAttribFormat::FLOAT_4D) }); + debugDrawPipeline->GetPipelineState().SetVertexInputState(debugDrawVertexInputState); + SHColorBlendState colorBlendState{}; + colorBlendState.logic_op_enable = VK_FALSE; + colorBlendState.logic_op = vk::LogicOp::eCopy; + + auto const& subpassColorReferences = debugDrawSubpass->GetColorAttachmentReferences(); + colorBlendState.attachments.reserve(subpassColorReferences.size()); + + for (auto& att : subpassColorReferences) + { + colorBlendState.attachments.push_back(vk::PipelineColorBlendAttachmentState + { + .blendEnable = SHVkUtil::IsBlendCompatible(debugDrawSubpass->GetFormatFromAttachmentReference(att.attachment)), + .srcColorBlendFactor = vk::BlendFactor::eSrcAlpha, + .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, + .colorBlendOp = vk::BlendOp::eAdd, + .srcAlphaBlendFactor = vk::BlendFactor::eOne, + .dstAlphaBlendFactor = vk::BlendFactor::eZero, + .alphaBlendOp = vk::BlendOp::eAdd, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + } + ); + } + + debugDrawPipeline->GetPipelineState().SetColorBlenState(colorBlendState); + debugDrawPipeline->ConstructPipeline(); } void SHGraphicsSystem::InitMiddleEnd(void) noexcept @@ -696,10 +741,14 @@ namespace SHADE void SHGraphicsSystem::BuildMeshBuffers() { + transferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + device->WaitIdle(); transferCmdBuffer->BeginRecording(); meshLibrary.BuildBuffers(device, transferCmdBuffer); transferCmdBuffer->EndRecording(); graphicsQueue->SubmitCommandBuffer({ transferCmdBuffer }); + device->WaitIdle(); + transferCmdBuffer.Free(); transferCmdBuffer = {}; } /*---------------------------------------------------------------------------------*/ @@ -724,10 +773,14 @@ namespace SHADE void SHGraphicsSystem::BuildTextures() { + graphicsTexCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + device->WaitIdle(); texLibrary.BuildTextures ( device, graphicsTexCmdBuffer, graphicsQueue, descPool ); + device->WaitIdle(); + graphicsTexCmdBuffer.Free(); graphicsTexCmdBuffer = {}; } #pragma endregion ADD_REMOVE @@ -767,6 +820,7 @@ namespace SHADE void SHGraphicsSystem::EndRoutine::Execute(double) noexcept { reinterpret_cast(system)->EndRender(); + SHResourceManager::FinaliseChanges(); } /*-----------------------------------------------------------------------------------*/ @@ -784,8 +838,13 @@ namespace SHADE if (!renderable.HasChanged()) continue; - // Remove from old material's SuperBatch - Handle prevMaterial = renderable.GetPrevMaterial(); + if (!renderable.GetMesh()) + { + SHLOG_CRITICAL("NULL Mesh provided!"); + } + + // Remove from the SuperBatch it is previously in (prevMat if mat has changed) + Handle prevMaterial = renderable.HasMaterialChanged() ? renderable.GetPrevMaterial() : renderable.GetMaterial(); if (prevMaterial) { Handle oldSuperBatch = prevMaterial->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch(); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 9b95ebe4..d8b4650e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -25,7 +25,6 @@ of DigiPen Institute of Technology is prohibited. #include "ECS_Base/System/SHSystemRoutine.h" #include "Graphics/Descriptors/SHVkDescriptorPool.h" #include "Graphics/RenderGraph/SHRenderGraph.h" -#include "Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.h" #include "SHMeshLibrary.h" #include "Graphics/MiddleEnd/Materials/SHMaterialInstanceCache.h" #include "../Textures/SHTextureLibrary.h" @@ -290,10 +289,13 @@ namespace SHADE Handle GetMousePickSystem(void) const noexcept {return mousePickSystem;}; Handle GetPostOffscreenRenderSystem(void) const noexcept {return postOffscreenRender;}; Handle GetPrimaryRenderpass() const noexcept; - //SHRenderGraph const& GetRenderGraph(void) const noexcept; - - //Handle GetRenderPass() const { return renderPass; } + Handle GetDebugDrawPipeline(void) const noexcept { return debugDrawPipeline; } + uint32_t GetCurrentFrameIndex(void) const noexcept { return renderContext.GetCurrentFrame(); } + /*-----------------------------------------------------------------------------*/ + /* Getters */ + /*-----------------------------------------------------------------------------*/ + SHWindow* GetWindow() noexcept { return window; } private: /*-----------------------------------------------------------------------------*/ @@ -322,11 +324,12 @@ namespace SHADE SHWindow* window = nullptr; // Middle End Resources - SHResourceHub resourceManager; + SHResourceHub resourceManager; SHMeshLibrary meshLibrary; SHTextureLibrary texLibrary; SHSamplerCache samplerCache; SHMaterialInstanceCache materialInstanceCache; + // Viewports #ifdef SHEDITOR Handle editorViewport; @@ -337,21 +340,26 @@ namespace SHADE Handle worldViewport; // Whole screen std::vector> viewports; // Additional viewports - // Debug Renderers - Handle debugWorldRenderer; - Handle debugScreenRenderer; - // Temp renderers Handle worldRenderer; // Temp Cameras Handle worldCamera; Handle screenCamera; - - SHShaderModuleLibrary shaderModuleLibrary; - // Temp Materials + // Built-In Shaders + Handle defaultVertShader; + Handle defaultFragShader; + Handle debugVertShader; + Handle debugFragShader; + Handle deferredCompositeShader; + Handle ssaoShader; + Handle ssaoBlurShader; + + + // Built-In Materials Handle defaultMaterial; + Handle debugDrawPipeline; Handle worldRenderGraph; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.cpp new file mode 100644 index 00000000..1ad46e04 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.cpp @@ -0,0 +1,77 @@ +/************************************************************************************//*! +\file SHGraphicsSystemInterface.cpp +\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 + SHGraphicsSystemInterface 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "SHGraphicsSystemInterface.h" +// Project Includes +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" +#include "Graphics/Windowing/SHWindow.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Static Usage Functions */ + /*-----------------------------------------------------------------------------------*/ + uint32_t SHGraphicsSystemInterface::GetWindowWidth() + { + auto gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem) + { + const auto WND = gfxSystem->GetWindow(); + return WND->GetWindowSize().first; + } + + SHLOG_WARNING("[SHGraphicsSystemInterface] Failed to get window width. Value of 0 returned instead."); + return 0; + } + + uint32_t SHGraphicsSystemInterface::GetWindowHeight() + { + auto gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem) + { + const auto WND = gfxSystem->GetWindow(); + return WND->GetWindowSize().second; + } + + SHLOG_WARNING("[SHGraphicsSystemInterface] Failed to get window height. Value of 0 returned instead."); + return 0; + } + + bool SHGraphicsSystemInterface::IsFullscreen() + { + auto gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem) + { + const auto WND = gfxSystem->GetWindow(); + return WND->GetWindowData().isFullscreen; + } + + SHLOG_WARNING("[SHGraphicsSystemInterface] Failed to get window fullscreen status. Value of false returned instead."); + return false; + } + + void SHGraphicsSystemInterface::CloseWindow() + { + auto gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem) + { + auto WND = gfxSystem->GetWindow(); + return WND->Close(); + } + + SHLOG_WARNING("[SHGraphicsSystemInterface] Failed to close window."); + } +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.h new file mode 100644 index 00000000..5bc77ed9 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.h @@ -0,0 +1,52 @@ +/************************************************************************************//*! +\file SHGraphicsSystemInterface.h +\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 + +namespace SHADE +{ + /// + /// Static class that wraps up certain functions in the SHGraphicsSystem so that + /// accessing it from SHADE_Managed would not cause issues due to C++20 features. + /// + class SH_API SHGraphicsSystemInterface final + { + public: + /*---------------------------------------------------------------------------------*/ + /* Constructor */ + /*---------------------------------------------------------------------------------*/ + SHGraphicsSystemInterface() = delete; + + /*---------------------------------------------------------------------------------*/ + /* Static Usage Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Retrieves the current window width. + /// + /// The current window width. + static uint32_t GetWindowWidth(); + /// + /// Retrieves the current window height. + /// + /// The current window height. + static uint32_t GetWindowHeight(); + /// + /// Retrieves the current window fullscreen status. + /// + /// The current window fullscreen status.. + static bool IsFullscreen(); + /// + /// Closes the current window, and depending on the implementation, should also + /// close the application. + /// + static void CloseWindow(); + }; +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h new file mode 100644 index 00000000..338c34b2 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h @@ -0,0 +1,36 @@ +/************************************************************************************//*! +\file SHMaterialSpec.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 31, 2022 +\brief Contains the struct definition of SHMaterialSpec. + +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 +// Standard Library +#include +#include +#include +// Project Includes +#include "Assets/SHAssetMacros.h" + +namespace SHADE +{ + /*************************************************************************************/ + /*! + \brief + Describes a Material's serialized properties. A representation of a material that is + independent of GPU resources. + */ + /*************************************************************************************/ + struct SHMaterialSpec + { + AssetID vertexShader; + AssetID fragShader; + std::string subpassName; + YAML::Node properties; + }; +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp index be181beb..c9a3f6c5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.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. @@ -18,50 +18,59 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Static Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + SHMeshData SHPrimitiveGenerator::cubeMesh; + SHMeshData SHPrimitiveGenerator::sphereMesh; + + /*-----------------------------------------------------------------------------------*/ + /* Primitive Generation Functions */ + /*-----------------------------------------------------------------------------------*/ SHMeshData SHPrimitiveGenerator::Cube() noexcept { SHMeshData mesh; - mesh.VertexPositions = + mesh.VertexPositions = { // front - SHVec3(-0.5f, -0.5f, 0.5f), - SHVec3( 0.5f, -0.5f, 0.5f), - SHVec3( 0.5f, 0.5f, 0.5f), - SHVec3(-0.5f, 0.5f, 0.5f), + SHVec3(-0.5f, -0.5f, 0.5f), + SHVec3(0.5f, -0.5f, 0.5f), + SHVec3(0.5f, 0.5f, 0.5f), + SHVec3(-0.5f, 0.5f, 0.5f), // back - SHVec3(-0.5f, -0.5f, -0.5f), - SHVec3(-0.5f, 0.5f, -0.5f), - SHVec3( 0.5f, 0.5f, -0.5f), - SHVec3( 0.5f, -0.5f, -0.5f), + SHVec3(-0.5f, -0.5f, -0.5f), + SHVec3(-0.5f, 0.5f, -0.5f), + SHVec3(0.5f, 0.5f, -0.5f), + SHVec3(0.5f, -0.5f, -0.5f), - // top + // top SHVec3(-0.5f, 0.5f, -0.5f), SHVec3(-0.5f, 0.5f, 0.5f), - SHVec3( 0.5f, 0.5f, 0.5f), - SHVec3( 0.5f, 0.5f, -0.5f), - + SHVec3(0.5f, 0.5f, 0.5f), + SHVec3(0.5f, 0.5f, -0.5f), + // bottom - SHVec3(-0.5f, -0.5f, -0.5f), - SHVec3( 0.5f, -0.5f, -0.5f), - SHVec3( 0.5f, -0.5f, 0.5f), - SHVec3(-0.5f, -0.5f, 0.5f), - + SHVec3(-0.5f, -0.5f, -0.5f), + SHVec3(0.5f, -0.5f, -0.5f), + SHVec3(0.5f, -0.5f, 0.5f), + SHVec3(-0.5f, -0.5f, 0.5f), + // right - SHVec3(0.5f, -0.5f, -0.5f), - SHVec3(0.5f, 0.5f, -0.5f), - SHVec3(0.5f, 0.5f, 0.5f), - SHVec3(0.5f, -0.5f, 0.5f), - + SHVec3(0.5f, -0.5f, -0.5f), + SHVec3(0.5f, 0.5f, -0.5f), + SHVec3(0.5f, 0.5f, 0.5f), + SHVec3(0.5f, -0.5f, 0.5f), + // left - SHVec3(-0.5f, -0.5f, -0.5f), - SHVec3(-0.5f, -0.5f, 0.5f), - SHVec3(-0.5f, 0.5f, 0.5f), - SHVec3(-0.5f, 0.5f, -0.5f) + SHVec3(-0.5f, -0.5f, -0.5f), + SHVec3(-0.5f, -0.5f, 0.5f), + SHVec3(-0.5f, 0.5f, 0.5f), + SHVec3(-0.5f, 0.5f, -0.5f) }; - mesh.VertexTexCoords = + mesh.VertexTexCoords = { SHVec2(0.0f, 1.0f), SHVec2(1.0f, 1.0f), @@ -99,7 +108,7 @@ namespace SHADE SHVec2(0.0f, 0.0f) }; - mesh.VertexTangents = + mesh.VertexTangents = { // front SHVec3(1.0f, 0.0f, 0.0f), @@ -118,7 +127,7 @@ namespace SHADE SHVec3(1.0f, 0.0f, 0.0f), SHVec3(1.0f, 0.0f, 0.0f), SHVec3(1.0f, 0.0f, 0.0f), - + // bottom SHVec3(1.0f, 0.0f, 0.0f), SHVec3(1.0f, 0.0f, 0.0f), @@ -193,10 +202,93 @@ namespace SHADE Handle SHPrimitiveGenerator::Cube(SHMeshLibrary& meshLibrary) noexcept { - static SHMeshData meshData = Cube(); + if (cubeMesh.VertexPositions.empty()) + cubeMesh = Cube(); + return addMeshDataTo(cubeMesh, meshLibrary); + } + + Handle SHPrimitiveGenerator::Cube(SHGraphicsSystem& gfxSystem) noexcept + { + if (cubeMesh.VertexPositions.empty()) + cubeMesh = Cube(); + return addMeshDataTo(cubeMesh, gfxSystem); + } + + SHADE::SHMeshData SHPrimitiveGenerator::Sphere() noexcept + { + SHMeshData meshData; + + const int LAT_SLICES = 12; + const int LONG_SLICES = 12; + float radius = 1; + for (int latNum = 0; latNum <= LAT_SLICES; ++latNum) + { + float theta = static_cast(latNum * std::numbers::pi / LAT_SLICES); + float sinTheta = sin(theta); + float cosTheta = cos(theta); + + for (int longNum = 0; longNum <= LONG_SLICES; ++longNum) + { + float phi = static_cast(longNum * 2 * std::numbers::pi / LONG_SLICES); + float sinPhi = sin(phi); + float cosPhi = cos(phi); + + const SHVec3 NORMAL = SHVec3(cosPhi * sinTheta, cosTheta, sinPhi * sinTheta); + meshData.VertexNormals.emplace_back(NORMAL); + meshData.VertexTangents.emplace_back(/* TODO */); + meshData.VertexTexCoords.emplace_back + ( + 1.0f - (longNum / static_cast(LONG_SLICES)), + 1.0f - (latNum / static_cast(LAT_SLICES)) + ); + meshData.VertexPositions.emplace_back(radius * NORMAL.x, radius * NORMAL.y, radius * NORMAL.z); + } + } + + for (int latNumber{}; latNumber < LAT_SLICES; latNumber++) + { + for (int longNumber{}; longNumber < LONG_SLICES; longNumber++) + { + const auto FIRST = (latNumber * (LONG_SLICES + 1)) + longNumber; + const auto SECOND = (FIRST + LONG_SLICES + 1); + + meshData.Indices.emplace_back(FIRST); + meshData.Indices.emplace_back(SECOND); + meshData.Indices.emplace_back(FIRST + 1); + + meshData.Indices.emplace_back(SECOND); + meshData.Indices.emplace_back(SECOND + 1); + meshData.Indices.emplace_back(FIRST + 1); + } + } + + return meshData; + } + + SHADE::Handle SHPrimitiveGenerator::Sphere(SHMeshLibrary& meshLibrary) noexcept + { + if (sphereMesh.VertexPositions.empty()) + sphereMesh = Sphere(); + + return addMeshDataTo(sphereMesh, meshLibrary); + } + + SHADE::Handle SHPrimitiveGenerator::Sphere(SHGraphicsSystem& gfxSystem) noexcept + { + if (sphereMesh.VertexPositions.empty()) + sphereMesh = Sphere(); + + return addMeshDataTo(sphereMesh, gfxSystem); + } + + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions */ + /*-----------------------------------------------------------------------------------*/ + SHADE::Handle SHPrimitiveGenerator::addMeshDataTo(const SHMeshData& meshData, SHMeshLibrary& meshLibrary) noexcept + { return meshLibrary.AddMesh ( - static_cast(meshData.VertexPositions.size()), + static_cast(meshData.VertexPositions.size()), meshData.VertexPositions.data(), meshData.VertexTexCoords.data(), meshData.VertexTangents.data(), @@ -206,17 +298,16 @@ namespace SHADE ); } - Handle SHPrimitiveGenerator::Cube(SHGraphicsSystem& gfxSystem) noexcept + SHADE::Handle SHPrimitiveGenerator::addMeshDataTo(const SHMeshData& meshData, SHGraphicsSystem& gfxSystem) noexcept { - static SHMeshData meshData = Cube(); 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() ); } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h index dd059a29..ef4e26a6 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h @@ -4,9 +4,9 @@ \par email: kahwei.tng\@digipen.edu \date Sep 18, 2022 \brief Contains the static class definition of SHPrimitiveGenerator. - + Copyright (C) 2022 DigiPen Institute of Technology. -Reproduction or disclosure of this file or its contents without the prior written consent +Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #pragma once @@ -29,12 +29,12 @@ namespace SHADE /*************************************************************************************/ /*! \brief - Static class that contains functions for generating 3D primitives. + Static class that contains functions for generating 3D primitives. */ /*************************************************************************************/ class SH_API SHPrimitiveGenerator { - public: + public: /*---------------------------------------------------------------------------------*/ /* Constructors */ /*---------------------------------------------------------------------------------*/ @@ -42,7 +42,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Primitive Generation Functions */ - /*---------------------------------------------------------------------------------*/ + /*---------------------------------------------------------------------------------*/ /***********************************************************************************/ /*! \brief @@ -52,20 +52,20 @@ namespace SHADE SHMeshData object containing vertex data for the cube. */ /***********************************************************************************/ - [[nodiscard]] static SHMeshData Cube() noexcept; + [[nodiscard]] static SHMeshData Cube() noexcept; /***********************************************************************************/ /*! \brief Produces a cube and constructs a SHMesh using the SHMeshLibrary provided. \param meshLibrary - Reference to the SHMeshLibrary to procude and store a cube mesh in. + Reference to the SHMeshLibrary to produce and store a cube mesh in. \return SHMesh object that points to the generated cube mesh in the SHMeshLibrary. */ /***********************************************************************************/ - [[nodiscard]] static Handle Cube(SHMeshLibrary& meshLibrary) noexcept; + [[nodiscard]] static Handle Cube(SHMeshLibrary& meshLibrary) noexcept; /***********************************************************************************/ /*! \brief @@ -78,12 +78,55 @@ namespace SHADE SHMesh object that points to the generated cube mesh in the SHGraphicsSystem. */ /***********************************************************************************/ - [[nodiscard]] static Handle Cube(SHGraphicsSystem& gfxSystem) noexcept; + [[nodiscard]] static Handle Cube(SHGraphicsSystem& gfxSystem) noexcept; + /***********************************************************************************/ + /*! + \brief + Produces a sphere and stores the data in a SHMeshData object. - private: + \return + SHMeshData object containing vertex data for the sphere. + */ + /***********************************************************************************/ + [[nodiscard]] static SHMeshData Sphere() noexcept; + /***********************************************************************************/ + /*! + \brief + Produces a sphere and constructs a SHMesh using the SHMeshLibrary provided. + + \param meshLibrary + Reference to the SHMeshLibrary to produce and store a sphere mesh in. + + \return + SHMesh object that points to the generated sphere mesh in the SHMeshLibrary. + */ + /***********************************************************************************/ + [[nodiscard]] static Handle Sphere(SHMeshLibrary& meshLibrary) noexcept; + /***********************************************************************************/ + /*! + \brief + Produces a sphere and constructs a SHMesh using the SHGraphicsSystem provided. + + \param gfxSystem + Reference to the SHGraphicsSystem to produce and store a sphere mesh in. + + \return + SHMesh object that points to the generated sphere mesh in the SHGraphicsSystem. + */ + /***********************************************************************************/ + [[nodiscard]] static Handle Sphere(SHGraphicsSystem& gfxSystem) noexcept; + + private: /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static SHMeshData genCubeData() noexcept; + static Handle addMeshDataTo(const SHMeshData& meshData, SHMeshLibrary& meshLibrary) noexcept; + static Handle addMeshDataTo(const SHMeshData& meshData, SHGraphicsSystem& gfxSystem) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + static SHMeshData cubeMesh; + static SHMeshData sphereMesh; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.cpp deleted file mode 100644 index d0b160df..00000000 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "SHPch.h" -#include "SHShaderModuleLibrary.h" -#include "Graphics/Devices/SHVkLogicalDevice.h" -#include "Assets/SHAssetManager.h" - -namespace SHADE -{ - /***************************************************************************/ - /*! - - \brief - Imports all shader binaries from the source library. - - \param logicalDeviceHdl - For creating shader modules. - - \param sourceLib - The source library class that stores the container of shader binary data. - - */ - /***************************************************************************/ - //void SHShaderModuleLibrary::ImportFromSourceLibrary(Handle& logicalDeviceHdl, SHShaderSourceLibrary const& sourceLib) noexcept - //{ - // auto const& sources = sourceLib.GetSourceLibrary(); - // for (auto const& source : sources) - // { - // vk::ShaderStageFlagBits shaderType{}; - // switch (source.shaderType) - // { - // case SH_SHADER_TYPE::VERTEX: - // shaderType = vk::ShaderStageFlagBits::eVertex; - // break; - // case SH_SHADER_TYPE::FRAGMENT: - // shaderType = vk::ShaderStageFlagBits::eFragment; - // break; - // case SH_SHADER_TYPE::COMPUTE: - // shaderType = vk::ShaderStageFlagBits::eCompute; - // break; - // default: - // shaderType = vk::ShaderStageFlagBits::eVertex; - // break; - // } - - // Handle newShaderModule = logicalDeviceHdl->CreateShaderModule(source.spirvBinary, "main", shaderType, source.name); - // shaderModules.emplace(source.id, newShaderModule); - // stringToID.emplace(source.name, source.id); - // } - //} - - /***************************************************************************/ - /*! - - \brief - Gets the shader module based on module name. - - \param shaderName - - \return - - */ - /***************************************************************************/ - //Handle SHShaderModuleLibrary::GetShaderModule(std::string shaderName) const noexcept - //{ - // if (stringToID.contains(shaderName)) - // return shaderModules.at(stringToID.at(shaderName)); - // else - // return {}; - //} - - vk::ShaderStageFlagBits SHShaderModuleLibrary::GetVkShaderFlag(SH_SHADER_TYPE type) noexcept - { - vk::ShaderStageFlagBits shaderType{}; - switch (type) - { - case SH_SHADER_TYPE::VERTEX: - shaderType = vk::ShaderStageFlagBits::eVertex; - break; - case SH_SHADER_TYPE::FRAGMENT: - shaderType = vk::ShaderStageFlagBits::eFragment; - break; - case SH_SHADER_TYPE::COMPUTE: - shaderType = vk::ShaderStageFlagBits::eCompute; - break; - default: - shaderType = vk::ShaderStageFlagBits::eVertex; - break; - } - - return shaderType; - } - - Handle SHShaderModuleLibrary::GetBuiltInShaderModule(std::string shaderName) const noexcept - { - if (builtInShaderModules.contains(shaderName)) - return builtInShaderModules.at(shaderName); - else - return {}; - } - - void SHShaderModuleLibrary::ImportAllShaderSource(Handle& logicalDeviceHdl) noexcept - { - uint32_t idCounter{ 0 }; - - auto const data = SHAssetManager::GetAllDataOfType(AssetType::SHADER); - for (auto const& dataPtr : data) - { - auto const shader = dynamic_cast(dataPtr); - - Handle newShaderModule = - logicalDeviceHdl->CreateShaderModule(shader->spirvBinary, "main", GetVkShaderFlag(shader->shaderType), shader->name); - - shaderModules.emplace(idCounter++, newShaderModule); - } - - auto const builtIn = SHAssetManager::GetAllDataOfType(AssetType::SHADER_BUILT_IN); - for (auto const& dataPtr : builtIn) - { - auto const shader = dynamic_cast(dataPtr); - - Handle newShaderModule = - logicalDeviceHdl->CreateShaderModule(shader->spirvBinary, "main", GetVkShaderFlag(shader->shaderType), shader->name); - - builtInShaderModules.emplace(shader->name, newShaderModule); - } - } - - void SHShaderModuleLibrary::ReflectAllShaderModules() noexcept - { - for (auto& module : shaderModules) - { - module.second->Reflect(); - } - - for (auto& module : builtInShaderModules) - { - module.second->Reflect(); - } - } -} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.h deleted file mode 100644 index 9daae816..00000000 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Shaders/SHShaderModuleLibrary.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef SH_SHADER_MODULE_LIBRARY_H -#define SH_SHADER_MODULE_LIBRARY_H - -#include "Graphics/Shaders/SHVkShaderModule.h" -#include "Assets/Asset Types/SHShaderAsset.h" - -namespace SHADE -{ - class SHVkLogicalDevice; - - /* - * The purpose of this shader module library is to be separate from the source library. The source library contains - * pure shader binary data that contains no vulkan related objects. Every time we load on unload a scene/level, - * this class and the source library class is cleared of its modules and recreated. - */ - class SHShaderModuleLibrary - { - private: - /*-----------------------------------------------------------------------*/ - /* PRIVATE MEMBER VARIABLES */ - /*-----------------------------------------------------------------------*/ - //! Stored shader modules - std::unordered_map> shaderModules; - std::unordered_map> builtInShaderModules; - - inline vk::ShaderStageFlagBits GetVkShaderFlag(SH_SHADER_TYPE type) noexcept; - - public: - /*-----------------------------------------------------------------------*/ - /* PUBLIC MEMBER FUNCTIONS */ - /*-----------------------------------------------------------------------*/ - //void ImportFromSourceLibrary(Handle& logicalDeviceHdl, SHShaderSourceLibrary const& sourceLib) noexcept; - - /*-----------------------------------------------------------------------*/ - /* SETTERS AND GETTERS */ - /*-----------------------------------------------------------------------*/ - Handle GetBuiltInShaderModule(std::string shaderName) const noexcept; - - void ImportAllShaderSource(Handle& logicalDeviceHdl) noexcept; - void ReflectAllShaderModules() noexcept; - }; -} - -#endif \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h index 7378cc48..7e313ed6 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h +++ b/SHADE_Engine/src/Graphics/Pipeline/SHVkPipeline.h @@ -47,12 +47,13 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* CTORS AND DTORS */ /*-----------------------------------------------------------------------*/ + //! For constructing a graphics pipeline SHVkPipeline (Handle const& inLogicalDeviceHdl, Handle const& inPipelineLayout, SHVkPipelineState const* const state, Handle const& renderpassHdl, Handle subpass) noexcept; - + //! For constructing a compute pipeline SHVkPipeline(Handle const& inLogicalDeviceHdl, Handle const& inPipelineLayout) noexcept; diff --git a/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp b/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp index 44a1cb0e..e0dbc1a4 100644 --- a/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp +++ b/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp @@ -54,13 +54,14 @@ namespace SHADE if (wndData.isFullscreen) { - dwExStyle = WS_EX_APPWINDOW | WS_EX_ACCEPTFILES; + dwExStyle = WS_EX_APPWINDOW; dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + dwExStyle |= WS_EX_ACCEPTFILES; } else { - dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES; - + dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + dwExStyle |= WS_EX_ACCEPTFILES; if (wndData.frameEnabled) { dwStyle = WNDSTYLE::SHWS_WINDOWED; @@ -87,7 +88,7 @@ namespace SHADE } } - //DPI_AWARENESS_CONTEXT prevDPIContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + DPI_AWARENESS_CONTEXT prevDPIContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); RECT windowRect; windowRect.left = wndData.x; //or CW_USEDEFAULT ? @@ -97,13 +98,16 @@ namespace SHADE AdjustWindowRectEx(&windowRect, dwStyle, false, dwExStyle); //Create window - wndHWND = CreateWindowEx(0, (LPWSTR) wndData.name.c_str(), (LPWSTR)wndData.title.c_str(), dwStyle, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, parent, NULL, hInstance, NULL); - + wndHWND = CreateWindowEx(dwExStyle, (LPWSTR) wndData.name.c_str(), (LPWSTR)wndData.title.c_str(), dwStyle, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, parent, NULL, hInstance, NULL); + if (!wndHWND) { //DWORD err = GetLastError(); return false; } + BOOL help = ChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD); + help &= ChangeWindowMessageFilter (WM_COPYDATA, MSGFLT_ADD); + help &= ChangeWindowMessageFilter (0x0049, MSGFLT_ADD); if (wndData.isVisible) { @@ -256,7 +260,7 @@ namespace SHADE return wndHWND; } - const WindowData SHWindow::GetWindowData() + const WindowData SHWindow::GetWindowData() const { return wndData; } @@ -318,13 +322,13 @@ namespace SHADE case WM_CREATE: OnCreate(hwnd, reinterpret_cast(wparam)); break; + case WM_QUIT: case WM_CLOSE: - case WM_DESTROY: OnDestroy(); return 0; case WM_DROPFILES: - //OnFileDrop(reinterpret_cast(wparam)); + OnFileDrop(reinterpret_cast(wparam)); break; case WM_ENTERSIZEMOVE: case WM_EXITSIZEMOVE: @@ -386,12 +390,25 @@ namespace SHADE void SHWindow::OnDestroy() { OnClose(); + DragAcceptFiles(wndHWND, false); this->Destroy(); } - //void SHWindow::OnFileDrop(HDROP drop) - //{ - //} + void SHWindow::OnFileDrop(HDROP drop) + { + + int const numFiles = static_cast(DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0)); + for(int i = 0; i < numFiles; ++i) + { + //char fileNameBuffer[MAX_PATH]; + std::wstring fileNameBuffer; + fileNameBuffer.reserve(MAX_PATH); + DragQueryFile(drop, static_cast(i), fileNameBuffer.data(), MAX_PATH); + std::string name(fileNameBuffer.begin(), fileNameBuffer.end()); + SHLOG_INFO("Dropped: {}", name) + } + DragFinish(drop); + } void SHWindow::OnSize([[maybe_unused]] UINT msg,[[maybe_unused]] UINT type, SIZE size) { diff --git a/SHADE_Engine/src/Graphics/Windowing/SHWindow.h b/SHADE_Engine/src/Graphics/Windowing/SHWindow.h index 0a180285..11308d90 100644 --- a/SHADE_Engine/src/Graphics/Windowing/SHWindow.h +++ b/SHADE_Engine/src/Graphics/Windowing/SHWindow.h @@ -2,6 +2,7 @@ #define SH_WINDOW_H #include +#include #include #include #include "SHWindowMap.h" @@ -10,7 +11,7 @@ namespace SHADE { constexpr uint16_t MAX_BUFFER = 1024; - + enum WNDSTYLE : DWORD { SHWS_WINDOWED = WS_OVERLAPPEDWINDOW, @@ -47,7 +48,7 @@ namespace SHADE //bool canFullscreen = true; - unsigned bgColor = WHITE_BRUSH; + unsigned bgColor = DKGRAY_BRUSH; //bool transparent = false; @@ -122,7 +123,7 @@ namespace SHADE HWND GetHWND(); - const WindowData GetWindowData(); + const WindowData GetWindowData() const; CALLBACKID RegisterWindowSizeCallback(WindowResizeCallbackFn); void UnregisterWindowSizeCallback(CALLBACKID const& callbackid); @@ -168,7 +169,7 @@ namespace SHADE void OnCreate(HWND hwnd, LPCREATESTRUCT create_struct); void OnClose(); void OnDestroy(); - //void OnFileDrop(HDROP drop); + void OnFileDrop(HDROP drop); void OnSize(UINT msg, UINT type, SIZE size); void OnPosChange(LPWINDOWPOS pos); void OnPaint(HDC hdc, LPPAINTSTRUCT paint); diff --git a/SHADE_Engine/src/Math/SHColour.cpp b/SHADE_Engine/src/Math/SHColour.cpp index 8aae2cb3..944c37cb 100644 --- a/SHADE_Engine/src/Math/SHColour.cpp +++ b/SHADE_Engine/src/Math/SHColour.cpp @@ -87,19 +87,19 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHColour::SHColour() noexcept - : SHVec4 { 0.0f, 0.0f, 0.0f, 1.0f } + : XMFLOAT4 { 0.0f, 0.0f, 0.0f, 1.0f } {} SHColour::SHColour(float r, float g, float b) noexcept - : SHVec4 { r, g, b, 1.0f } + : XMFLOAT4 { r, g, b, 1.0f } {} SHColour::SHColour(float r, float g, float b, float a) noexcept - : SHVec4 { r, g, b, a } + : XMFLOAT4 { r, g, b, a } {} SHColour::SHColour(uint8_t r, uint8_t g, uint8_t b) noexcept - : SHVec4 + : XMFLOAT4 { static_cast(r) / 255.0f, static_cast(g) / 255.0f, @@ -109,7 +109,7 @@ namespace SHADE {} SHColour::SHColour(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept - : SHVec4 + : XMFLOAT4 { static_cast(r) / 255.0f, static_cast(g) / 255.0f, @@ -118,12 +118,24 @@ namespace SHADE } {} - SHColour::SHColour(const DirectX::XMFLOAT3& colour) noexcept - : SHVec4 { colour.x, colour.y, colour.z, 1.0f } + SHColour::SHColour(const SHVec3& rgb) noexcept + : XMFLOAT4 { rgb.x, rgb.y, rgb.z, 1.0f } + {} + + SHColour::SHColour(const SHVec4& rgba) noexcept + : XMFLOAT4 { rgba.x, rgba.y, rgba.z, rgba.w } + {} + + SHColour::SHColour(const DirectX::XMFLOAT3& rgb) noexcept + : XMFLOAT4 { rgb.x, rgb.y, rgb.z, 1.0f } + {} + + SHColour::SHColour(const DirectX::XMFLOAT4& rgba) noexcept + : XMFLOAT4 { rgba.x, rgba.y, rgba.z, rgba.w } {} SHColour::SHColour(const DirectX::XMVECTORF32& colour) noexcept - : SHVec4 + : XMFLOAT4 { XMVectorGetX(colour), XMVectorGetY(colour), @@ -136,6 +148,86 @@ namespace SHADE /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ + SHColour::operator XMVECTOR() const noexcept + { + return XMLoadFloat4(this); + } + + SHColour::operator SHVec4() const noexcept + { + return SHVec4{ *this }; + } + + SHColour& SHColour::operator+=(const SHColour& rhs) noexcept + { + return *this = *this + rhs; + } + + SHColour& SHColour::operator-=(const SHColour& rhs) noexcept + { + return *this = *this - rhs; + } + + SHColour& SHColour::operator*=(const SHColour& rhs) noexcept + { + return *this = *this * rhs; + } + + SHColour& SHColour::operator*=(float rhs) noexcept + { + return *this = *this * rhs; + } + + SHColour& SHColour::operator/=(const SHColour& rhs) noexcept + { + return *this = *this / rhs; + } + + SHColour SHColour::operator+(const SHColour& rhs) const noexcept + { + SHColour result; + + XMStoreFloat4(&result, XMVectorAdd(*this, rhs)); + return result; + } + + SHColour SHColour::operator-(const SHColour& rhs) const noexcept + { + SHColour result; + + XMStoreFloat4(&result, XMVectorSubtract(*this, rhs)); + return result; + } + + SHColour SHColour::operator-() const noexcept + { + return SHColour{ -x, -y, -z, -w }; + } + + SHColour SHColour::operator*(const SHColour& rhs) const noexcept + { + SHColour result; + + XMStoreFloat4(&result, XMVectorMultiply(*this, rhs)); + return result; + } + + SHColour SHColour::operator*(float rhs) const noexcept + { + SHColour result; + + XMStoreFloat4(&result, XMVectorScale(*this, rhs)); + return result; + } + + SHColour SHColour::operator/(const SHColour& rhs) const noexcept + { + SHColour result; + + XMStoreFloat4(&result, XMVectorDivide(*this, rhs)); + return result; + } + bool SHColour::operator==(const SHColour& rhs) const noexcept { return XMColorEqual(*this, rhs); @@ -146,6 +238,70 @@ namespace SHADE return XMColorNotEqual(*this, rhs); } + float& SHColour::operator[](int index) + { + if (index >= SIZE || index < 0) + throw std::invalid_argument("Index out of range!"); + + switch (index) + { + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + } + + float& SHColour::operator[](size_t index) + { + if (index >= SIZE || index < 0) + throw std::invalid_argument("Index out of range!"); + + switch (index) + { + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + } + + float SHColour::operator[](int index) const + { + if (index >= SIZE || index < 0) + throw std::invalid_argument("Index out of range!"); + + switch (index) + { + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + } + + float SHColour::operator[](size_t index) const + { + if (index >= SIZE || index < 0) + throw std::invalid_argument("Index out of range!"); + + switch (index) + { + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + } + + SHColour operator* (float lhs, const SHColour& rhs) noexcept + { + SHColour result; + + XMStoreFloat4(&result, XMVectorScale(rhs, lhs)); + return result; + } + /*-----------------------------------------------------------------------------------*/ /* Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/SHColour.h b/SHADE_Engine/src/Math/SHColour.h index bd2bc9e7..a6adf7bb 100644 --- a/SHADE_Engine/src/Math/SHColour.h +++ b/SHADE_Engine/src/Math/SHColour.h @@ -19,12 +19,6 @@ namespace SHADE { - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - class SHColour; - /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -40,7 +34,7 @@ namespace SHADE float v = 0.0f; }; - class SH_API SHColour : private SHVec4 + class SH_API SHColour : public DirectX::XMFLOAT4 { public: /*---------------------------------------------------------------------------------*/ @@ -53,9 +47,11 @@ namespace SHADE SHColour (uint8_t r, uint8_t g, uint8_t b) noexcept; SHColour (uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept; - SHColour (const SHVec3& colour) noexcept; - SHColour (const DirectX::XMFLOAT3& colour) noexcept; - SHColour (const DirectX::XMVECTORF32& colour) noexcept; + SHColour (const SHVec3& rgb) noexcept; + SHColour (const SHVec4& rgba) noexcept; + SHColour (const DirectX::XMFLOAT3& rgb) noexcept; + SHColour (const DirectX::XMFLOAT4& rgba) noexcept; + SHColour (const DirectX::XMVECTORF32& rgba) noexcept; SHColour (const SHColour&) = default; SHColour (SHColour&&) = default; @@ -66,11 +62,32 @@ namespace SHADE /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHColour& operator= (const SHColour&) = default; - SHColour& operator= (SHColour&&) = default; + SHColour& operator= (const SHColour&) = default; + SHColour& operator= (SHColour&&) = default; - bool operator== (const SHColour& rhs) const noexcept; - bool operator!= (const SHColour& rhs) const noexcept; + operator DirectX::XMVECTOR () const noexcept; + operator SHVec4 () const noexcept; + + SHColour& operator+= (const SHColour& rhs) noexcept; + SHColour& operator-= (const SHColour& rhs) noexcept; + SHColour& operator*= (const SHColour& rhs) noexcept; + SHColour& operator*= (float rhs) noexcept; + SHColour& operator/= (const SHColour& rhs) noexcept; + + [[nodiscard]] SHColour operator+ (const SHColour& rhs) const noexcept; + [[nodiscard]] SHColour operator- (const SHColour& rhs) const noexcept; + [[nodiscard]] SHColour operator- () const noexcept; + [[nodiscard]] SHColour operator* (const SHColour& rhs) const noexcept; + [[nodiscard]] SHColour operator* (float rhs) const noexcept; + [[nodiscard]] SHColour operator/ (const SHColour& rhs) const noexcept; + + [[nodiscard]] bool operator== (const SHColour& rhs) const noexcept; + [[nodiscard]] bool operator!= (const SHColour& rhs) const noexcept; + + [[nodiscard]] float& operator[] (int index); + [[nodiscard]] float& operator[] (size_t index); + [[nodiscard]] float operator[] (int index) const; + [[nodiscard]] float operator[] (size_t index) const; /*---------------------------------------------------------------------------------*/ /* Properties */ @@ -105,6 +122,8 @@ namespace SHADE /* Static Data Members */ /*---------------------------------------------------------------------------------*/ + static constexpr size_t SIZE = 4U; + static const SHColour BEIGE ; static const SHColour BLACK ; static const SHColour BLUE ; @@ -159,8 +178,9 @@ namespace SHADE static const SHColour TURQUOISE ; static const SHColour VIOLET ; static const SHColour WHITE ; - static const SHColour YELLOW; + static const SHColour YELLOW ; }; + SHColour operator* (float lhs, const SHColour& rhs) noexcept; } // namespace SHADE diff --git a/SHADE_Engine/src/Math/SHMath.h b/SHADE_Engine/src/Math/SHMath.h index 5fcea9fc..3a24d6ef 100644 --- a/SHADE_Engine/src/Math/SHMath.h +++ b/SHADE_Engine/src/Math/SHMath.h @@ -9,4 +9,6 @@ #include "SHQuaternion.h" #include "SHMatrix.h" +#include "SHColour.h" + #include "Transform/SHTransform.h" \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp index e56cbc8d..fa0befa3 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformComponent.cpp @@ -14,6 +14,7 @@ #include "SHTransformComponent.h" // Project Headers #include "Math/SHMathHelpers.h" +#include "Reflection/SHReflectionMetadata.h" namespace SHADE { @@ -184,7 +185,7 @@ RTTR_REGISTRATION using namespace rttr; registration::class_("Transform Component") - .property("Translate" , &SHTransformComponent::GetLocalPosition , &SHTransformComponent::SetLocalPosition ) - .property("Rotate" , &SHTransformComponent::GetLocalRotation , select_overload(&SHTransformComponent::SetLocalRotation) ) - .property("Scale" , &SHTransformComponent::GetLocalScale , &SHTransformComponent::SetLocalScale ); + .property("Translate" ,&SHTransformComponent::GetLocalPosition ,&SHTransformComponent::SetLocalPosition ) (metadata(META::tooltip, "Translate")) + .property("Rotate" ,&SHTransformComponent::GetLocalRotation ,select_overload(&SHTransformComponent::SetLocalRotation)) (metadata(META::tooltip, "Rotate"), metadata(META::angleInRad, true)) + .property("Scale" ,&SHTransformComponent::GetLocalScale ,&SHTransformComponent::SetLocalScale ) (metadata(META::tooltip, "Scale")); } \ No newline at end of file diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index c90e4431..f000aa5b 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -47,16 +47,14 @@ namespace SHADE { // Get the current scene graph to traverse and update const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); - - // TODO(Diren): Consider how to clear dirty in pause / stop mode and update physics, but do not clear in play mode. - UpdateEntity(SCENE_GRAPH.GetRoot(), false); + UpdateEntity(SCENE_GRAPH.GetRoot(), !IsRunInEditorPause); } void SHTransformSystem::TransformPostPhysicsUpdate::Execute(double) noexcept { // Get the current scene graph to traverse and update const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); - UpdateEntity(SCENE_GRAPH.GetRoot(), true); + UpdateEntity(SCENE_GRAPH.GetRoot(), IsRunInEditorPause); } void SHTransformSystem::Init() diff --git a/SHADE_Engine/src/Math/Vector/SHVec4.cpp b/SHADE_Engine/src/Math/Vector/SHVec4.cpp index 88e7f5c9..943d540e 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec4.cpp +++ b/SHADE_Engine/src/Math/Vector/SHVec4.cpp @@ -14,6 +14,7 @@ #include "SHVec4.h" // Project Headers #include "Math/SHMatrix.h" +#include "Math/SHColour.h" #include "Tools/SHLogger.h" using namespace DirectX; diff --git a/SHADE_Engine/src/Math/Vector/SHVec4.h b/SHADE_Engine/src/Math/Vector/SHVec4.h index db8ef860..3c509039 100644 --- a/SHADE_Engine/src/Math/Vector/SHVec4.h +++ b/SHADE_Engine/src/Math/Vector/SHVec4.h @@ -23,7 +23,9 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ + class SHMatrix; + class SHColour; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ diff --git a/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp index fb999847..c7e327fa 100644 --- a/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp @@ -146,4 +146,12 @@ namespace SHADE system->RemoveCollisionShape(GetEID(), index); } -} // namespace SHADE \ No newline at end of file +} // namespace SHADE + +RTTR_REGISTRATION +{ + using namespace rttr; + using namespace SHADE; + + registration::class_("Collider Component"); +} \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Components/SHColliderComponent.h b/SHADE_Engine/src/Physics/Components/SHColliderComponent.h index af726b51..7ce272a9 100644 --- a/SHADE_Engine/src/Physics/Components/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Components/SHColliderComponent.h @@ -100,5 +100,6 @@ namespace SHADE SHQuaternion orientation; Colliders colliders; + RTTR_ENABLE() }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Components/SHRigidBodyComponent.cpp b/SHADE_Engine/src/Physics/Components/SHRigidBodyComponent.cpp index c1969557..369d26a5 100644 --- a/SHADE_Engine/src/Physics/Components/SHRigidBodyComponent.cpp +++ b/SHADE_Engine/src/Physics/Components/SHRigidBodyComponent.cpp @@ -18,6 +18,7 @@ // Project Headers #include "ECS_Base/Managers/SHSystemManager.h" +#include "Math/SHMathHelpers.h" #include "Physics/SHPhysicsSystem.h" namespace SHADE @@ -28,21 +29,9 @@ namespace SHADE SHRigidBodyComponent::SHRigidBodyComponent() noexcept : type { Type::DYNAMIC } - , flags { 0 } - , dirtyFlags { 0 } , interpolate { true } , rp3dBody { nullptr } - , mass { 1.0f } - , drag { 0.01f } - , angularDrag { 0.01f } - { - // Set default flags: Gravity & Sleeping enabled - flags |= 1U << 0; - flags |= 1U << 1; - - // Set all dirty flags to true - dirtyFlags = 1023; - } + {} /*-----------------------------------------------------------------------------------*/ /* Getter Function Definitions */ @@ -50,12 +39,24 @@ namespace SHADE bool SHRigidBodyComponent::IsGravityEnabled() const noexcept { - return flags & (1U << 0); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + return rp3dBody->isGravityEnabled(); } bool SHRigidBodyComponent::IsAllowedToSleep() const noexcept { - return flags & (1U << 1); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + return rp3dBody->isAllowedToSleep(); } bool SHRigidBodyComponent::IsInterpolating() const noexcept @@ -70,67 +71,151 @@ namespace SHADE float SHRigidBodyComponent::GetMass() const noexcept { - return mass; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return 0.0f; + } + + return rp3dBody->getMass(); } float SHRigidBodyComponent::GetDrag() const noexcept { - return drag; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return 0.0f; + } + + return rp3dBody->getLinearDamping(); } float SHRigidBodyComponent::GetAngularDrag() const noexcept { - return angularDrag; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return 0.0f; + } + + return rp3dBody->getAngularDamping(); } bool SHRigidBodyComponent::GetFreezePositionX() const noexcept { - return flags & (1U << 2); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + const auto& LINEAR_CONSTRAINTS = rp3dBody->getLinearLockAxisFactor(); + return SHMath::CompareFloat(LINEAR_CONSTRAINTS.x, 0.0f); } bool SHRigidBodyComponent::GetFreezePositionY() const noexcept { - return flags & (1U << 3); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + const auto& LINEAR_CONSTRAINTS = rp3dBody->getLinearLockAxisFactor(); + return SHMath::CompareFloat(LINEAR_CONSTRAINTS.y, 0.0f); } bool SHRigidBodyComponent::GetFreezePositionZ() const noexcept { - return flags & (1U << 4); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + const auto& LINEAR_CONSTRAINTS = rp3dBody->getLinearLockAxisFactor(); + return SHMath::CompareFloat(LINEAR_CONSTRAINTS.z, 0.0f); } bool SHRigidBodyComponent::GetFreezeRotationX() const noexcept { - return flags & (1U << 5); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + const auto& ANGULAR_CONSTRAINTS = rp3dBody->getAngularLockAxisFactor(); + return SHMath::CompareFloat(ANGULAR_CONSTRAINTS.x, 0.0f); } bool SHRigidBodyComponent::GetFreezeRotationY() const noexcept { - return flags & (1U << 6); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + const auto& ANGULAR_CONSTRAINTS = rp3dBody->getAngularLockAxisFactor(); + return SHMath::CompareFloat(ANGULAR_CONSTRAINTS.y, 0.0f); } bool SHRigidBodyComponent::GetFreezeRotationZ() const noexcept { - return flags & (1U << 7); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + const auto& ANGULAR_CONSTRAINTS = rp3dBody->getAngularLockAxisFactor(); + return SHMath::CompareFloat(ANGULAR_CONSTRAINTS.z, 0.0f); } - const SHVec3& SHRigidBodyComponent::GetForce() const noexcept + SHVec3 SHRigidBodyComponent::GetForce() const noexcept { - return force; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return false; + } + + return rp3dBody->getForce(); } - const SHVec3& SHRigidBodyComponent::GetTorque() const noexcept + SHVec3 SHRigidBodyComponent::GetTorque() const noexcept { - return torque; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return SHVec3::Zero; + } + + return rp3dBody->getTorque(); } - const SHVec3& SHRigidBodyComponent::GetLinearVelocity() const noexcept + SHVec3 SHRigidBodyComponent::GetLinearVelocity() const noexcept { - return linearVelocity; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return SHVec3::Zero; + } + + return rp3dBody->getLinearVelocity(); } - const SHVec3& SHRigidBodyComponent::GetAngularVelocity() const noexcept + SHVec3 SHRigidBodyComponent::GetAngularVelocity() const noexcept { - return angularVelocity; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return SHVec3::Zero; + } + + return rp3dBody->getAngularVelocity(); } const SHVec3& SHRigidBodyComponent::GetPosition() const noexcept @@ -157,8 +242,15 @@ namespace SHADE if (type == newType) return; - dirtyFlags |= 1U << 4; type = newType; + + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + rp3dBody->setType(static_cast(type)); } void SHRigidBodyComponent::SetGravityEnabled(bool enableGravity) noexcept @@ -171,8 +263,13 @@ namespace SHADE return; } - dirtyFlags |= 1U << FLAG_POS; - enableGravity ? flags |= (1U << FLAG_POS) : flags &= ~(1U << FLAG_POS); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + rp3dBody->enableGravity(enableGravity); } void SHRigidBodyComponent::SetIsAllowedToSleep(bool isAllowedToSleep) noexcept @@ -185,92 +282,127 @@ namespace SHADE return; } - dirtyFlags |= 1U << 1; - isAllowedToSleep ? flags |= (1U << FLAG_POS) : flags &= ~(1U << FLAG_POS); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + rp3dBody->setIsAllowedToSleep(isAllowedToSleep); } 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 << 2; - freezePositionX ? flags |= (1U << FLAG_POS) : flags &= ~(1U << FLAG_POS); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + auto linearConstraints = rp3dBody->getLinearLockAxisFactor(); + linearConstraints.x = freezePositionX ? 0.0f : 1.0f; + rp3dBody->setLinearLockAxisFactor(linearConstraints); } 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 << 2; - freezePositionY ? flags |= (1U << FLAG_POS) : flags &= ~(1U << FLAG_POS); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + auto linearConstraints = rp3dBody->getLinearLockAxisFactor(); + linearConstraints.y = freezePositionY ? 0.0f : 1.0f; + rp3dBody->setLinearLockAxisFactor(linearConstraints); } 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 << 2; - freezePositionZ ? flags |= (1U << FLAG_POS) : flags &= ~(1U << FLAG_POS); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + auto linearConstraints = rp3dBody->getLinearLockAxisFactor(); + linearConstraints.z = freezePositionZ ? 0.0f : 1.0f; + rp3dBody->setLinearLockAxisFactor(linearConstraints); } 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 << 3; - freezeRotationX ? flags |= (1U << FLAG_POS) : flags &= ~(1U << FLAG_POS); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + auto angularConstraints = rp3dBody->getAngularLockAxisFactor(); + angularConstraints.x = freezeRotationX ? 0.0f : 1.0f; + rp3dBody->setAngularLockAxisFactor(angularConstraints); } 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 << 3; - freezeRotationY ? flags |= (1U << FLAG_POS) : flags &= ~(1U << FLAG_POS); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + auto angularConstraints = rp3dBody->getAngularLockAxisFactor(); + angularConstraints.y = freezeRotationY ? 0.0f : 1.0f; + rp3dBody->setAngularLockAxisFactor(angularConstraints); } 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 << 3; - freezeRotationZ ? flags |= (1U << FLAG_POS) : flags &= ~(1U << FLAG_POS); + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + auto angularConstraints = rp3dBody->getAngularLockAxisFactor(); + angularConstraints.z = freezeRotationZ ? 0.0f : 1.0f; + rp3dBody->setAngularLockAxisFactor(angularConstraints); } void SHRigidBodyComponent::SetInterpolate(bool allowInterpolation) noexcept @@ -283,11 +415,16 @@ namespace SHADE if (type != Type::DYNAMIC) { SHLOG_WARNING("Cannot set mass of a non-dynamic object {}", GetEID()) - return; + return; } - dirtyFlags |= 1U << 5; - mass = newMass; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + rp3dBody->setMass(newMass); } void SHRigidBodyComponent::SetDrag(float newDrag) noexcept @@ -298,8 +435,13 @@ namespace SHADE return; } - dirtyFlags |= 1U << 6; - drag = newDrag; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + rp3dBody->setLinearDamping(newDrag); } void SHRigidBodyComponent::SetAngularDrag(float newAngularDrag) noexcept @@ -310,8 +452,13 @@ namespace SHADE return; } - dirtyFlags |= 1U << 7; - angularDrag = newAngularDrag; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + rp3dBody->setLinearDamping(newAngularDrag); } void SHRigidBodyComponent::SetLinearVelocity(const SHVec3& newLinearVelocity) noexcept @@ -322,8 +469,13 @@ namespace SHADE return; } - dirtyFlags |= 1U << 8; - linearVelocity = newLinearVelocity; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + rp3dBody->setLinearVelocity(newLinearVelocity); } void SHRigidBodyComponent::SetAngularVelocity(const SHVec3& newAngularVelocity) noexcept @@ -334,8 +486,13 @@ namespace SHADE return; } - dirtyFlags |= 1U << 9; - angularVelocity = newAngularVelocity; + if (rp3dBody == nullptr) + { + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) + return; + } + + rp3dBody->setAngularVelocity(newAngularVelocity); } /*-----------------------------------------------------------------------------------*/ @@ -346,7 +503,7 @@ namespace SHADE { if (rp3dBody == nullptr) { - SHLOGV_ERROR("Entity {} is missing an rp3dBody!", GetEID()) + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) return; } @@ -357,7 +514,7 @@ namespace SHADE { if (rp3dBody == nullptr) { - SHLOGV_ERROR("Entity {} is missing an rp3dBody!", GetEID()) + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) return; } @@ -368,7 +525,7 @@ namespace SHADE { if (rp3dBody == nullptr) { - SHLOGV_ERROR("Entity {} is missing an rp3dBody!", GetEID()) + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) return; } @@ -379,7 +536,7 @@ namespace SHADE { if (rp3dBody == nullptr) { - SHLOGV_ERROR("Entity {} is missing an rp3dBody!", GetEID()) + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) return; } @@ -390,7 +547,7 @@ namespace SHADE { if (rp3dBody == nullptr) { - SHLOGV_ERROR("Entity {} is missing an rp3dBody!", GetEID()) + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) return; } @@ -401,7 +558,7 @@ namespace SHADE { if (rp3dBody == nullptr) { - SHLOGV_ERROR("Entity {} is missing an rp3dBody!", GetEID()) + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) return; } @@ -412,7 +569,7 @@ namespace SHADE { if (rp3dBody == nullptr) { - SHLOGV_ERROR("Entity {} is missing an rp3dBody!", GetEID()) + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) return; } @@ -423,7 +580,7 @@ namespace SHADE { if (rp3dBody == nullptr) { - SHLOGV_ERROR("Entity {} is missing an rp3dBody!", GetEID()) + SHLOG_ERROR("Missing rp3dBody from Entity {}", GetEID()) return; } diff --git a/SHADE_Engine/src/Physics/Components/SHRigidBodyComponent.h b/SHADE_Engine/src/Physics/Components/SHRigidBodyComponent.h index 3c5dd4f9..ba7d2dd9 100644 --- a/SHADE_Engine/src/Physics/Components/SHRigidBodyComponent.h +++ b/SHADE_Engine/src/Physics/Components/SHRigidBodyComponent.h @@ -94,10 +94,10 @@ namespace SHADE [[nodiscard]] bool GetFreezeRotationY () const noexcept; [[nodiscard]] bool GetFreezeRotationZ () 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; + [[nodiscard]] SHVec3 GetForce () const noexcept; + [[nodiscard]] SHVec3 GetTorque () const noexcept; + [[nodiscard]] SHVec3 GetLinearVelocity () const noexcept; + [[nodiscard]] SHVec3 GetAngularVelocity () const noexcept; [[nodiscard]] const SHVec3& GetPosition () const noexcept; [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; @@ -149,28 +149,13 @@ namespace SHADE static constexpr size_t NUM_FLAGS = 8; static constexpr size_t NUM_DIRTY_FLAGS = 16; - Type type; - - // rX rY rZ pX pY pZ slp g - uint8_t flags; - // 0 0 0 0 0 0 aV lV aD d m t ag lc slp g - uint16_t dirtyFlags; - bool interpolate; + Type type; + bool interpolate; reactphysics3d::RigidBody* rp3dBody; - float mass; - float drag; - float angularDrag; - - SHVec3 force; - SHVec3 linearVelocity; - - SHVec3 torque; - SHVec3 angularVelocity; - - SHVec3 position; - SHQuaternion orientation; + SHVec3 position; + SHQuaternion orientation; RTTR_ENABLE() }; diff --git a/SHADE_Engine/src/Physics/SHCollider.cpp b/SHADE_Engine/src/Physics/SHCollider.cpp index 9488042d..6cea3dc1 100644 --- a/SHADE_Engine/src/Physics/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/SHCollider.cpp @@ -17,6 +17,7 @@ #include "Math/Geometry/SHBoundingSphere.h" #include "Math/Transform/SHTransformComponent.h" #include "Math/SHMathHelpers.h" +#include "Reflection/SHReflectionMetadata.h" namespace SHADE { @@ -158,6 +159,11 @@ namespace SHADE return positionOffset; } + const SHVec3& SHCollider::GetRotationOffset() const noexcept + { + return rotationOffset; + } + SHShape* SHCollider::GetShape() noexcept { dirty = true; @@ -275,6 +281,12 @@ namespace SHADE } } + void SHCollider::SetRotationOffset(const SHVec3& rotOffset) noexcept + { + dirty = true; + rotationOffset = rotOffset; + } + /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -316,5 +328,6 @@ RTTR_REGISTRATION ); registration::class_("Collider") - .property("Position Offset", &SHCollider::GetPositionOffset, &SHCollider::SetPositionOffset); + .property("Position Offset", &SHCollider::GetPositionOffset, &SHCollider::SetPositionOffset) + .property("Rotation Offset", &SHCollider::GetRotationOffset, &SHCollider::SetRotationOffset) (metadata(META::angleInRad, true)); } \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHCollider.h b/SHADE_Engine/src/Physics/SHCollider.h index f760ffd0..8cc233c4 100644 --- a/SHADE_Engine/src/Physics/SHCollider.h +++ b/SHADE_Engine/src/Physics/SHCollider.h @@ -80,6 +80,7 @@ namespace SHADE [[nodiscard]] const SHPhysicsMaterial& GetMaterial () const noexcept; [[nodiscard]] const SHVec3& GetPositionOffset () const noexcept; + [[nodiscard]] const SHVec3& GetRotationOffset () const noexcept; [[nodiscard]] SHShape* GetShape () noexcept; @@ -96,7 +97,8 @@ namespace SHADE void SetDensity (float density) noexcept; void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept; - void SetPositionOffset (const SHVec3& positionOffset) noexcept; + void SetPositionOffset (const SHVec3& posOffset) noexcept; + void SetRotationOffset (const SHVec3& rotOffset) noexcept; private: /*---------------------------------------------------------------------------------*/ @@ -110,6 +112,7 @@ namespace SHADE SHShape* shape; SHPhysicsMaterial material; SHVec3 positionOffset; + SHVec3 rotationOffset; /*---------------------------------------------------------------------------------*/ /* Function Members */ diff --git a/SHADE_Engine/src/Physics/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/SHPhysicsObject.cpp index 4d4d8cd7..37c1269e 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsObject.cpp @@ -130,6 +130,8 @@ namespace SHADE int SHPhysicsObject::AddCollider(SHCollider* collider) { + const rp3d::Transform OFFSETS{ collider->GetPositionOffset(), collider->GetRotationOffset() }; + switch (collider->GetType()) { case SHCollider::Type::BOX: @@ -137,7 +139,7 @@ namespace SHADE const auto* box = reinterpret_cast(collider->GetShape()); rp3d::BoxShape* newBox = factory->createBoxShape(box->GetHalfExtents()); - rp3dBody->addCollider(newBox, rp3d::Transform{ collider->GetPositionOffset(), SHQuaternion::Identity }); + rp3dBody->addCollider(newBox, OFFSETS); break; } case SHCollider::Type::SPHERE: @@ -145,7 +147,7 @@ namespace SHADE const auto* sphere = reinterpret_cast(collider->GetShape()); rp3d::SphereShape* newSphere = factory->createSphereShape(sphere->GetRadius()); - rp3dBody->addCollider(newSphere, rp3d::Transform{ collider->GetPositionOffset(), SHQuaternion::Identity }); + rp3dBody->addCollider(newSphere, OFFSETS); break; } // TODO(Diren): Add more collider shapes @@ -168,96 +170,6 @@ namespace SHADE rp3dBody->removeCollider(collider); } - void SHPhysicsObject::SyncRigidBody(SHRigidBodyComponent* rb) const noexcept - { - SHASSERT(rp3dBody != nullptr, "ReactPhysics body does not exist!") - - if (rb->dirtyFlags == 0) - return; - - auto* rigidBody = reinterpret_cast(rp3dBody); - - const uint16_t RB_FLAGS = rb->dirtyFlags; - for (size_t i = 0; i < SHRigidBodyComponent::NUM_DIRTY_FLAGS; ++i) - { - // Check if current dirty flag has been set to true - if (RB_FLAGS & 1U << i) - { - switch (i) - { - case 0: // Gravity - { - rigidBody->enableGravity(rb->IsGravityEnabled()); - break; - } - case 1: // Sleeping - { - rigidBody->setIsAllowedToSleep(rb->IsAllowedToSleep()); - break; - } - case 2: // Linear Constraints - { - const rp3d::Vector3 CONSTRAINTS - { - rb->flags & 1U << 2 ? 0.0f : 1.0f, - rb->flags & 1U << 3 ? 0.0f : 1.0f, - rb->flags & 1U << 4 ? 0.0f : 1.0f - }; - - - rigidBody->setLinearLockAxisFactor(CONSTRAINTS); - break; - } - case 3: // Angular Constraints - { - const rp3d::Vector3 CONSTRAINTS - { - rb->flags & 1U << 5 ? 0.0f : 1.0f, - rb->flags & 1U << 6 ? 0.0f : 1.0f, - rb->flags & 1U << 7 ? 0.0f : 1.0f - }; - - rigidBody->setAngularLockAxisFactor(CONSTRAINTS); - break; - } - case 4: // Type - { - rigidBody->setType(static_cast(rb->GetType())); - break; - } - case 5: // Mass - { - rigidBody->setMass(rb->GetMass()); - break; - } - case 6: // Drag - { - rigidBody->setLinearDamping(rb->GetDrag()); - break; - } - case 7: // Angular Drag - { - rigidBody->setAngularDamping(rb->GetAngularDrag()); - break; - } - case 8: // Linear Velocity - { - rigidBody->setLinearVelocity(rb->GetLinearVelocity()); - break; - } - case 9: // Angular Velocity - { - rigidBody->setAngularVelocity(rb->GetAngularVelocity()); - break; - } - default: break; - } - } - } - - rb->dirtyFlags = 0; - } - void SHPhysicsObject::SyncColliders(SHColliderComponent* c) const noexcept { int index = 0; @@ -266,9 +178,13 @@ namespace SHADE if (!collider.dirty) continue; - // Update offsets auto* rp3dCollider = rp3dBody->getCollider(index); - rp3dCollider->setLocalToBodyTransform(rp3d::Transform(collider.GetPositionOffset(), SHQuaternion::Identity)); + + // Update trigger flag + rp3dCollider->setIsTrigger(collider.IsTrigger()); + + // Update offsets + rp3dCollider->setLocalToBodyTransform(rp3d::Transform(collider.GetPositionOffset(), collider.GetRotationOffset())); switch (collider.GetType()) { @@ -293,6 +209,8 @@ namespace SHADE default: break; } + // TODO(Diren): Update Material + collider.dirty = false; ++index; } diff --git a/SHADE_Engine/src/Physics/SHPhysicsObject.h b/SHADE_Engine/src/Physics/SHPhysicsObject.h index 67e5ec64..64caacdb 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsObject.h +++ b/SHADE_Engine/src/Physics/SHPhysicsObject.h @@ -72,7 +72,6 @@ namespace SHADE int AddCollider (SHCollider* collider); void RemoveCollider (int index); - void SyncRigidBody (SHRigidBodyComponent* rb) const noexcept; void SyncColliders (SHColliderComponent* c) const noexcept; private: diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp index 44142aaf..e1acccd9 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp @@ -17,10 +17,10 @@ #include "ECS_Base/Managers/SHComponentManager.h" #include "ECS_Base/Managers/SHEntityManager.h" #include "ECS_Base/Managers/SHSystemManager.h" -#include "Editor/SHEditor.hpp" #include "Math/SHMathHelpers.h" -#include "Scene/SHSceneManager.h" #include "Math/Transform/SHTransformComponent.h" +#include "Scene/SHSceneManager.h" +#include "Scripting/SHScriptEngine.h" namespace SHADE { @@ -99,6 +99,16 @@ namespace SHADE return 0; } + const SHPhysicsSystem::CollisionEvents& SHPhysicsSystem::GetCollisionInfo() const noexcept + { + return collisionInfo; + } + + const SHPhysicsSystem::CollisionEvents& SHPhysicsSystem::GetTriggerInfo() const noexcept + { + return triggerInfo; + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -187,6 +197,7 @@ namespace SHADE settings.defaultBounciness = 0.0f; world = factory.createPhysicsWorld(settings); + world->setEventListener(this); // Set up solvers world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::SPLIT_IMPULSES); @@ -200,6 +211,12 @@ namespace SHADE 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 + const std::shared_ptr EDITOR_STOP_RECEIVER { std::make_shared>(this, &SHPhysicsSystem::ResetWorld) }; + const ReceiverPtr EDITOR_STOP_RECEIVER_PTR = std::dynamic_pointer_cast(EDITOR_STOP_RECEIVER); + SHEventManager::SubscribeTo(SH_EDITOR_ON_STOP_EVENT, EDITOR_STOP_RECEIVER_PTR); + #endif } void SHPhysicsSystem::Exit() @@ -246,7 +263,10 @@ namespace SHADE if (physicsObject.rp3dBody == nullptr) continue; - const auto* transformComponent = SHComponentManager::GetComponent_s(entityID); + const auto* transformComponent = SHComponentManager::GetComponent_s(entityID); + auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); + auto* colliderComponent = SHComponentManager::GetComponent_s(entityID); + if (transformComponent && transformComponent->HasChanged()) { const auto WORLD_POS = transformComponent->GetWorldPosition(); @@ -255,68 +275,119 @@ namespace SHADE physicsObject.SetPosition(WORLD_POS); physicsObject.SetOrientation(WORLD_ROT); - auto* rigidBodyComponent = SHComponentManager::GetComponent_s(entityID); + // Sync physics component transforms + if (rigidBodyComponent) { rigidBodyComponent->position = WORLD_POS; rigidBodyComponent->orientation = WORLD_ROT; - - // Clear all forces and velocities if editor is stopped - if (SHSystemManager::GetSystem()->editorState == SHEditor::State::STOP) - { - auto* rp3dRigidBody = reinterpret_cast(physicsObject.rp3dBody); - rp3dRigidBody->resetForce(); - rp3dRigidBody->resetTorque(); - rp3dRigidBody->setLinearVelocity(SHVec3::Zero); - rp3dRigidBody->setAngularVelocity(SHVec3::Zero); - } } - auto* colliderComponent = SHComponentManager::GetComponent_s(entityID); if (colliderComponent) { colliderComponent->position = WORLD_POS; colliderComponent->orientation = WORLD_ROT; } } - } - // Update bodies and colliders if component is dirty - system->SyncRigidBodyComponents(SHComponentManager::GetDense()); - system->SyncColliderComponents(SHComponentManager::GetDense()); + // Sync rigid bodies + + if (rigidBodyComponent) + { + // Sync active states + const bool COMPONENT_ACTIVE = rigidBodyComponent->isActive; + SyncActiveStates(physicsObject, COMPONENT_ACTIVE); + + if (!COMPONENT_ACTIVE) + continue; + } + + // Sync colliders + + if (colliderComponent) + { + const bool COMPONENT_ACTIVE = colliderComponent->isActive; + SyncActiveStates(physicsObject, colliderComponent->isActive); + + if (!COMPONENT_ACTIVE) + continue; + + physicsObject.SyncColliders(colliderComponent); + } + } } void SHPhysicsSystem::PhysicsFixedUpdate::Execute(double dt) noexcept { - auto* system = reinterpret_cast(GetSystem()); - fixedTimeStep = 1.0 / system->fixedDT; + auto* physicsSystem = reinterpret_cast(GetSystem()); + auto* scriptingSystem = SHSystemManager::GetSystem(); + if (scriptingSystem == nullptr) + { + SHLOGV_WARNING("Unable to invoke FixedUpdate() on scripts due to missing SHScriptEngine!"); + } + + fixedTimeStep = 1.0 / physicsSystem->fixedDT; accumulatedTime += dt; int count = 0; while (accumulatedTime > fixedTimeStep) { - system->world->update(static_cast(fixedTimeStep)); + if (scriptingSystem != nullptr) + scriptingSystem->ExecuteFixedUpdates(); + + physicsSystem->world->update(static_cast(fixedTimeStep)); accumulatedTime -= fixedTimeStep; ++count; } stats.numSteps = count; - system->worldUpdated = count > 0; + physicsSystem->worldUpdated = count > 0; - system->interpolationFactor = accumulatedTime / fixedTimeStep; + physicsSystem->interpolationFactor = accumulatedTime / fixedTimeStep; } void SHPhysicsSystem::PhysicsPostUpdate::Execute(double) noexcept { - auto* system = reinterpret_cast(GetSystem()); + auto* physicsSystem = reinterpret_cast(GetSystem()); + auto* scriptingSystem = SHSystemManager::GetSystem(); + if (scriptingSystem == nullptr) + { + SHLOGV_WARNING("Unable to invoke collision and trigger script events due to missing SHScriptEngine!"); + } // Interpolate transforms for rendering - if (system->worldUpdated) + if (physicsSystem->worldUpdated) { - system->SyncTransforms(); + physicsSystem->SyncTransforms(); - // TODO(Diren): Handle trigger messages for scripting + // Collision & Trigger messages + if (scriptingSystem != nullptr) + scriptingSystem->ExecuteCollisionFunctions(); + + physicsSystem->ClearInvalidCollisions(); + } + } + + void SHPhysicsSystem::onContact(const CallbackData& callbackData) + { + for (uint32_t i = 0; i < callbackData.getNbContactPairs(); ++i) + { + const auto CONTACT_PAIR = callbackData.getContactPair(i); + const SHCollisionEvent NEW_EVENT = GenerateCollisionEvent(CONTACT_PAIR); + + UpdateEventContainers(NEW_EVENT, collisionInfo); + } + } + + void SHPhysicsSystem::onTrigger(const rp3d::OverlapCallback::CallbackData& callbackData) + { + for (uint32_t i = 0; i < callbackData.getNbOverlappingPairs(); ++i) + { + const auto& OVERLAP_PAIR = callbackData.getOverlappingPair(i); + const SHCollisionEvent NEW_EVENT = GenerateCollisionEvent(OVERLAP_PAIR); + + UpdateEventContainers(NEW_EVENT, triggerInfo); } } @@ -353,58 +424,11 @@ namespace SHADE map.erase(entityID); } - void SHPhysicsSystem::SyncActiveStates(SHPhysicsObject* physicsObject, bool componentActive) noexcept + void SHPhysicsSystem::SyncActiveStates(SHPhysicsObject& physicsObject, bool componentActive) noexcept { - const bool RP3D_ACTIVE = physicsObject->rp3dBody->isActive(); + const bool RP3D_ACTIVE = physicsObject.rp3dBody->isActive(); if (RP3D_ACTIVE != componentActive) - physicsObject->rp3dBody->setIsActive(componentActive); - } - - - void SHPhysicsSystem::SyncRigidBodyComponents(std::vector& denseArray) noexcept - { - if (denseArray.empty()) - return; - - for (auto& comp : denseArray) - { - const EntityID ENTITY_ID = comp.GetEID(); - - // Get physicsObject - auto* physicsObject = GetPhysicsObject(ENTITY_ID); - - // TODO(Diren): Check if active in hierarchy - const bool COMPONENT_ACTIVE = comp.isActive; - SyncActiveStates(physicsObject, COMPONENT_ACTIVE); - - if (!COMPONENT_ACTIVE) - continue; - - physicsObject->SyncRigidBody(&comp); - } - } - - void SHPhysicsSystem::SyncColliderComponents(std::vector& denseArray) noexcept - { - if (denseArray.empty()) - return; - - for (auto& comp : denseArray) - { - const EntityID ENTITY_ID = comp.GetEID(); - - // Get physicsObject - auto* physicsObject = GetPhysicsObject(ENTITY_ID); - - // TODO(Diren): Check if active in hierarchy - const bool COMPONENT_ACTIVE = comp.isActive; - SyncActiveStates(physicsObject, COMPONENT_ACTIVE); - - if (!COMPONENT_ACTIVE) - continue; - - physicsObject->SyncColliders(&comp); - } + physicsObject.rp3dBody->setIsActive(componentActive); } void SHPhysicsSystem::SyncTransforms() noexcept @@ -459,15 +483,54 @@ namespace SHADE } // Convert RP3D Transform to SHADE - auto* transformComponent = SHComponentManager::GetComponent(entityID); - transformComponent->SetWorldPosition(rp3dPos); - transformComponent->SetWorldOrientation(rp3dRot); + auto* transformComponent = SHComponentManager::GetComponent_s(entityID); + + if (transformComponent != nullptr) + { + transformComponent->SetWorldPosition(rp3dPos); + transformComponent->SetWorldOrientation(rp3dRot); + } // Cache transforms physicsObject.prevTransform = CURRENT_TF; } } + void SHPhysicsSystem::UpdateEventContainers(const SHCollisionEvent& collisionEvent, CollisionEvents& container) noexcept + { + const auto IT = std::ranges::find_if(container.begin(), container.end(), [&](const SHCollisionEvent& e) + { + const bool ENTITY_MATCH = e.value[0] == collisionEvent.value[0]; + const bool COLLIDERS_MATCH = e.value[1] == collisionEvent.value[1]; + return ENTITY_MATCH && COLLIDERS_MATCH; + }); + + if (IT == container.end()) + container.emplace_back(collisionEvent); + else + IT->collisionState = collisionEvent.collisionState; + } + + void SHPhysicsSystem::ClearInvalidCollisions() noexcept + { + static const auto CLEAR = [](CollisionEvents& container) + { + for (auto eventIter = container.begin(); eventIter != container.end();) + { + const bool CLEAR_EVENT = eventIter->GetCollisionState() == SHCollisionEvent::State::EXIT + || eventIter->GetCollisionState() == SHCollisionEvent::State::INVALID; + + if (CLEAR_EVENT) + eventIter = container.erase(eventIter); + else + ++eventIter; + } + }; + + CLEAR(collisionInfo); + CLEAR(triggerInfo); + } + SHEventHandle SHPhysicsSystem::AddPhysicsComponent(SHEventPtr addComponentEvent) { const auto& EVENT_DATA = reinterpret_cast*>(addComponentEvent.get()); @@ -556,14 +619,21 @@ namespace SHADE const EntityID ENTITY_ID = EVENT_DATA->data->eid; auto* physicsObject = GetPhysicsObject(ENTITY_ID); - SHASSERT(physicsObject != nullptr, "Physics object has been lost from the world!") + auto* rigidBodyComponent = SHComponentManager::GetComponent_s(ENTITY_ID); + auto* colliderComponent = SHComponentManager::GetComponent_s(ENTITY_ID); - if (REMOVED_ID == RIGID_BODY_ID) + // Wake up all physics objects + for (auto& [entityID, object] : map) + { + if (SHComponentManager::HasComponent(entityID)) + reinterpret_cast(object.rp3dBody)->setIsSleeping(false); + } + + if (REMOVED_ID == RIGID_BODY_ID && physicsObject != nullptr) { world->destroyRigidBody(reinterpret_cast(physicsObject->rp3dBody)); - physicsObject->rp3dBody = nullptr; + physicsObject->rp3dBody = nullptr; - auto* colliderComponent = SHComponentManager::GetComponent_s(ENTITY_ID); if (colliderComponent != nullptr) { // Preserve colliders as a collision body @@ -575,16 +645,9 @@ namespace SHADE for (auto& collider : colliderComponent->colliders) physicsObject->AddCollider(&collider); } - - // Wake up all physics objects - for (auto& [entityID, object] : map) - { - if (SHComponentManager::HasComponent(entityID)) - reinterpret_cast(object.rp3dBody)->setIsSleeping(false); - } } - if (REMOVED_ID == COLLIDER_ID) + if (REMOVED_ID == COLLIDER_ID && physicsObject != nullptr) { // Remove all colliders const int NUM_COLLIDERS = static_cast(physicsObject->rp3dBody->getNbColliders()); @@ -594,13 +657,36 @@ namespace SHADE auto* collider = physicsObject->rp3dBody->getCollider(i); physicsObject->rp3dBody->removeCollider(collider); } + + // Check for a rigidbody component + if (rigidBodyComponent == nullptr) + physicsObject->rp3dBody = nullptr; } - if (physicsObject->rp3dBody == nullptr) - DestroyPhysicsObject(ENTITY_ID); + if (physicsObject != nullptr && physicsObject->rp3dBody == nullptr) + DestroyPhysicsObject(ENTITY_ID); } return EVENT_DATA->handle; } + SHEventHandle SHPhysicsSystem::ResetWorld(SHEventPtr editorStopEvent) + { + // TODO(Diren): Rebuild world based on how scene reloading is done + + for (auto& [entityID, physicsObject] : map) + { + if (SHComponentManager::HasComponent(entityID)) + { + auto* rp3dRigidBody = reinterpret_cast(physicsObject.rp3dBody); + rp3dRigidBody->resetForce(); + rp3dRigidBody->resetTorque(); + rp3dRigidBody->setLinearVelocity(SHVec3::Zero); + rp3dRigidBody->setAngularVelocity(SHVec3::Zero); + } + } + + return editorStopEvent->handle; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/SHPhysicsSystem.h index a3c3bea1..1d773618 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.h @@ -23,16 +23,18 @@ #include "Math/Transform/SHTransformComponent.h" #include "Scene/SHSceneGraph.h" #include "SHPhysicsObject.h" - +#include "SHPhysicsUtils.h" namespace SHADE { + /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - class SH_API SHPhysicsSystem final : public SHSystem + class SH_API SHPhysicsSystem final : public SHSystem + , public rp3d::EventListener { public: /*---------------------------------------------------------------------------------*/ @@ -47,6 +49,8 @@ namespace SHADE bool sleepingEnabled; }; + using CollisionEvents = std::vector; + /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ @@ -57,13 +61,16 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] double GetFixedDT () const noexcept; + [[nodiscard]] double GetFixedDT () const noexcept; - [[nodiscard]] bool IsSleepingEnabled () const noexcept; + [[nodiscard]] bool IsSleepingEnabled () const noexcept; - [[nodiscard]] SHVec3 GetWorldGravity () const noexcept; - [[nodiscard]] uint16_t GetNumberVelocityIterations () const noexcept; - [[nodiscard]] uint16_t GetNumberPositionIterations () const noexcept; + [[nodiscard]] SHVec3 GetWorldGravity () const noexcept; + [[nodiscard]] uint16_t GetNumberVelocityIterations () const noexcept; + [[nodiscard]] uint16_t GetNumberPositionIterations () const noexcept; + + [[nodiscard]] const CollisionEvents& GetCollisionInfo () const noexcept; + [[nodiscard]] const CollisionEvents& GetTriggerInfo () const noexcept; /*---------------------------------------------------------------------------------*/ @@ -82,16 +89,14 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - void Init () override; - void Exit () override; + void Init () override; + void Exit () override; - //void AddRigidBody (EntityID entityID) noexcept; - //void AddCollider (EntityID entityID) noexcept; - //void RemoveRigidBody (EntityID entityID) noexcept; - //void RemoveCollider (EntityID entityID) noexcept; + void AddCollisionShape (EntityID entityID, SHCollider* collider); + void RemoveCollisionShape (EntityID entityID, int index); - void AddCollisionShape (EntityID entityID, SHCollider* collider); - void RemoveCollisionShape (EntityID entityID, int index); + void onContact (const rp3d::CollisionCallback::CallbackData& callbackData) override; + void onTrigger (const rp3d::OverlapCallback::CallbackData& callbackData) override; /*---------------------------------------------------------------------------------*/ /* System Routines */ @@ -156,49 +161,41 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - bool worldUpdated; + bool worldUpdated; - double interpolationFactor; - double fixedDT; - - rp3d::PhysicsWorld* world; - rp3d::PhysicsCommon factory; - - EntityObjectMap map; + double interpolationFactor; + double fixedDT; + rp3d::PhysicsWorld* world; + rp3d::PhysicsCommon factory; + EntityObjectMap map; + CollisionEvents collisionInfo; + CollisionEvents triggerInfo; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ - SHPhysicsObject* EnsurePhysicsObject (EntityID entityID) noexcept; - SHPhysicsObject* GetPhysicsObject (EntityID entityID) noexcept; - void DestroyPhysicsObject (EntityID entityID) noexcept; - void SyncActiveStates (SHPhysicsObject* physicsObject, bool componentActive) noexcept; - void SyncRigidBodyComponents (std::vector& denseArray) noexcept; - void SyncColliderComponents (std::vector& denseArray) noexcept; + SHPhysicsObject* EnsurePhysicsObject (EntityID entityID) noexcept; + SHPhysicsObject* GetPhysicsObject (EntityID entityID) noexcept; + void DestroyPhysicsObject (EntityID entityID) noexcept; + + static void SyncActiveStates (SHPhysicsObject& physicsObject, bool componentActive) noexcept; void SyncTransforms () noexcept; + static void UpdateEventContainers (const SHCollisionEvent& collisionEvent, CollisionEvents& container) noexcept; + void ClearInvalidCollisions () noexcept; + SHEventHandle AddPhysicsComponent (SHEventPtr addComponentEvent); SHEventHandle RemovePhysicsComponent (SHEventPtr removeComponentEvent); + SHEventHandle ResetWorld (SHEventPtr editorStopEvent); + + template + || std::is_same_v>> + SHCollisionEvent GenerateCollisionEvent (const RP3DCollisionPair& cp) noexcept; }; +} // namespace SHADE - /*-----------------------------------------------------------------------------------*/ - /* Event Data Definitions */ - /*-----------------------------------------------------------------------------------*/ - - struct SHPhysicsColliderAddedEvent - { - EntityID entityID; - SHCollider::Type colliderType; - int colliderIndex; - }; - - struct SHPhysicsColliderRemovedEvent - { - EntityID entityID; - int colliderIndex; - }; - -} // namespace SHADE \ No newline at end of file +#include "SHPhysicsSystem.hpp" \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.hpp b/SHADE_Engine/src/Physics/SHPhysicsSystem.hpp new file mode 100644 index 00000000..957fb3aa --- /dev/null +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.hpp @@ -0,0 +1,84 @@ +/**************************************************************************************** + * \file SHPhysicsSystem.hpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for templated functions the Physics 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 + +// Primary Header +#include "SHPhysicsSystem.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + template + SHCollisionEvent SHPhysicsSystem::GenerateCollisionEvent(const RP3DCollisionPair& cp) noexcept + { + static const auto MATCH_COLLIDER = [] + ( + const SHPhysicsObject& physicsObject + , const rp3d::Entity colliderID + )->uint32_t + { + for (uint32_t i = 0; i < physicsObject.rp3dBody->getNbColliders(); ++i) + { + const auto* collider = physicsObject.rp3dBody->getCollider(i); + if (collider->getEntity() == colliderID) + return i; + } + + return std::numeric_limits::max(); + }; + + SHCollisionEvent 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] : map) + { + // Match body 1 + if (matched[SHCollisionEvent::ENTITY_A] == false && physicsObject.rp3dBody->getEntity() == body1) + { + cInfo.ids[SHCollisionEvent::ENTITY_A] = entityID; + cInfo.ids[SHCollisionEvent::COLLIDER_A] = MATCH_COLLIDER(physicsObject, collider1); + + matched[SHCollisionEvent::ENTITY_A] = true; + } + + // Match body 2 + if (matched[SHCollisionEvent::ENTITY_B] == false && physicsObject.rp3dBody->getEntity() == body2) + { + cInfo.ids[SHCollisionEvent::ENTITY_B] = entityID; + cInfo.ids[SHCollisionEvent::COLLIDER_B] = MATCH_COLLIDER(physicsObject, collider2); + + matched[SHCollisionEvent::ENTITY_B] = true; + } + + if (matched[SHCollisionEvent::ENTITY_A] == true && matched[SHCollisionEvent::ENTITY_B] == true) + return cInfo; + } + + return cInfo; + } +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystemInterface.cpp b/SHADE_Engine/src/Physics/SHPhysicsSystemInterface.cpp new file mode 100644 index 00000000..4b292340 --- /dev/null +++ b/SHADE_Engine/src/Physics/SHPhysicsSystemInterface.cpp @@ -0,0 +1,65 @@ +/************************************************************************************//*! +\file SHPhysicsSystemInterface.cpp +\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. + +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/SHPhysicsSystem.h" +#include "Physics/SHPhysicsUtils.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Static Usage Functions */ + /*-----------------------------------------------------------------------------------*/ + const std::vector& SHPhysicsSystemInterface::GetCollisionInfo() noexcept + { + static std::vector emptyVec; + + auto phySystem = SHSystemManager::GetSystem(); + if (phySystem) + { + return phySystem->GetCollisionInfo(); + } + + SHLOG_WARNING("[SHPhysicsSystemInterface] 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->GetTriggerInfo(); + } + + SHLOG_WARNING("[SHPhysicsSystemInterface] Failed to get trigger events. Empty vector returned instead."); + return emptyVec; + } + + double SHPhysicsSystemInterface::GetFixedDT() noexcept + { + auto phySystem = SHSystemManager::GetSystem(); + if (phySystem) + { + return phySystem->GetFixedDT(); + } + + SHLOG_WARNING("[SHPhysicsSystemInterface] Failed to get fixed delta time. 0.0 returned instead."); + return 0.0; + } +} diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystemInterface.h b/SHADE_Engine/src/Physics/SHPhysicsSystemInterface.h new file mode 100644 index 00000000..da6a0433 --- /dev/null +++ b/SHADE_Engine/src/Physics/SHPhysicsSystemInterface.h @@ -0,0 +1,46 @@ +/************************************************************************************//*! +\file SHPhysicsSystemInterface.h +\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 + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + class SHCollisionEvent; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + /// + /// Static class that wraps up certain functions in the SHPhysicsSystem so that + /// accessing it from SHADE_Managed would not cause issues due to C++20 features. + /// + class SH_API SHPhysicsSystemInterface final + { + public: + /*---------------------------------------------------------------------------------*/ + /* Constructor */ + /*---------------------------------------------------------------------------------*/ + SHPhysicsSystemInterface() = delete; + + /*---------------------------------------------------------------------------------*/ + /* Static Usage Functions */ + /*---------------------------------------------------------------------------------*/ + [[nodiscard]] static const std::vector& GetCollisionInfo() noexcept; + [[nodiscard]] static const std::vector& GetTriggerInfo() noexcept; + [[nodiscard]] static double GetFixedDT() noexcept; + }; +} diff --git a/SHADE_Engine/src/Physics/SHPhysicsUtils.cpp b/SHADE_Engine/src/Physics/SHPhysicsUtils.cpp new file mode 100644 index 00000000..8d5bc956 --- /dev/null +++ b/SHADE_Engine/src/Physics/SHPhysicsUtils.cpp @@ -0,0 +1,93 @@ +/**************************************************************************************** + * \file SHPhysicsUtils.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for some Physics Utilities + * + * \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 "SHPhysicsUtils.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHCollisionEvent::SHCollisionEvent() 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(); + } + + SHCollisionEvent::SHCollisionEvent(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 SHCollisionEvent::operator==(const SHCollisionEvent& rhs) const noexcept + { + return value[0] == rhs.value[0] && value[1] == rhs.value[1]; + } + + bool SHCollisionEvent::operator!=(const SHCollisionEvent& rhs) const noexcept + { + return value[0] != rhs.value[0] || value[1] != rhs.value[1]; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + EntityID SHCollisionEvent::GetEntityA() const noexcept + { + return ids[ENTITY_A]; + } + + EntityID SHCollisionEvent::GetEntityB() const noexcept + { + return ids[ENTITY_B]; + } + + const SHRigidBodyComponent* SHCollisionEvent::GetRigidBodyA() const noexcept + { + return SHComponentManager::GetComponent_s(ids[ENTITY_A]); + } + + const SHRigidBodyComponent* SHCollisionEvent::GetRigidBodyB() const noexcept + { + return SHComponentManager::GetComponent_s(ids[ENTITY_B]); + } + + const SHCollider* SHCollisionEvent::GetColliderA() const noexcept + { + return &SHComponentManager::GetComponent(ids[ENTITY_A])->GetCollider(ids[COLLIDER_A]); + } + + const SHCollider* SHCollisionEvent::GetColliderB() const noexcept + { + return &SHComponentManager::GetComponent(ids[ENTITY_B])->GetCollider(ids[COLLIDER_B]); + } + + SHCollisionEvent::State SHCollisionEvent::GetCollisionState() const noexcept + { + return collisionState; + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/SHPhysicsUtils.h b/SHADE_Engine/src/Physics/SHPhysicsUtils.h new file mode 100644 index 00000000..57f9c6fc --- /dev/null +++ b/SHADE_Engine/src/Physics/SHPhysicsUtils.h @@ -0,0 +1,116 @@ +/**************************************************************************************** + * \file SHPhysicsUtils.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for some Physics Utilities + * + * \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 "Components/SHColliderComponent.h" +#include "Components/SHRigidBodyComponent.h" + + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + struct SHPhysicsColliderAddedEvent + { + EntityID entityID; + SHCollider::Type colliderType; + int colliderIndex; + }; + + struct SHPhysicsColliderRemovedEvent + { + EntityID entityID; + int colliderIndex; + }; + + class SH_API SHCollisionEvent + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHPhysicsSystem; + + public: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + enum class State + { + ENTER + , STAY + , EXIT + + , TOTAL + , INVALID = -1 + }; + + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHCollisionEvent () noexcept; + SHCollisionEvent (EntityID entityA, EntityID entityB) noexcept; + + + SHCollisionEvent (const SHCollisionEvent& rhs) = default; + SHCollisionEvent (SHCollisionEvent&& rhs) = default; + ~SHCollisionEvent () = default; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + bool operator== (const SHCollisionEvent& rhs) const noexcept; + bool operator!= (const SHCollisionEvent& rhs) const noexcept; + + SHCollisionEvent& operator= (const SHCollisionEvent& rhs) = default; + SHCollisionEvent& operator= (SHCollisionEvent&& 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 SHCollider* GetColliderA () const noexcept; + [[nodiscard]] const SHCollider* 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/Reflection/SHReflectionMetadata.h b/SHADE_Engine/src/Reflection/SHReflectionMetadata.h index 0cc6d8a5..b4dc009c 100644 --- a/SHADE_Engine/src/Reflection/SHReflectionMetadata.h +++ b/SHADE_Engine/src/Reflection/SHReflectionMetadata.h @@ -7,5 +7,6 @@ namespace SHADE constexpr const char* min = "MIN"; constexpr const char* max = "MAX"; constexpr const char* tooltip = "tooltip"; + constexpr const char* angleInRad = "angleInRad"; } } diff --git a/SHADE_Engine/src/Resource/SHResourceManager.cpp b/SHADE_Engine/src/Resource/SHResourceManager.cpp index 156c31c7..dad9fd9f 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.cpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.cpp @@ -21,9 +21,11 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHResourceHub SHResourceManager::resourceHub; std::unordered_map>> SHResourceManager::handlesMap; - std::unordered_map SHResourceManager::assetIdMap; + std::unordered_map SHResourceManager::assetIdMap; std::unordered_map> SHResourceManager::typedFreeFuncMap; std::vector SHResourceManager::loadedAssetData; + bool SHResourceManager::textureChanged = false; + bool SHResourceManager::meshChanged = false; /*-----------------------------------------------------------------------------------*/ /* Function Definitions */ @@ -63,8 +65,17 @@ namespace SHADE SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); if (gfxSystem == nullptr) throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); - gfxSystem->BuildMeshBuffers(); - gfxSystem->BuildTextures(); + + if (meshChanged) + { + gfxSystem->BuildMeshBuffers(); + meshChanged = false; + } + if (textureChanged) + { + gfxSystem->BuildTextures(); + textureChanged = false; + } // Free CPU Resources for (auto assetId : loadedAssetData) diff --git a/SHADE_Engine/src/Resource/SHResourceManager.h b/SHADE_Engine/src/Resource/SHResourceManager.h index 84f93b10..e5e303e1 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.h +++ b/SHADE_Engine/src/Resource/SHResourceManager.h @@ -17,9 +17,27 @@ of DigiPen Institute of Technology is prohibited. #include "SH_API.h" #include "SHResourceLibrary.h" #include "Assets/SHAssetMacros.h" +#include "Assets/Asset Types/SHMeshAsset.h" +#include "Assets/Asset Types/SHTextureAsset.h" +#include "Assets/Asset Types/SHShaderAsset.h" +#include "Graphics/Shaders/SHVkShaderModule.h" +#include "Graphics/MiddleEnd/Textures/SHTextureLibrary.h" +#include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h" +#include "Graphics/MiddleEnd/Interface/SHMaterial.h" +#include "Assets/Asset Types/SHMaterialAsset.h" namespace SHADE { + /// + /// Template structs that maps a resource to their loaded asset representation type. + /// + template + struct SHResourceLoader { using AssetType = void; }; + template<> struct SHResourceLoader { using AssetType = SHMeshAsset; }; + template<> struct SHResourceLoader { using AssetType = SHTextureAsset; }; + template<> struct SHResourceLoader { using AssetType = SHShaderAsset; }; + template<> struct SHResourceLoader { using AssetType = SHMaterialAsset; }; + /// /// Static class responsible for loading and caching runtime resources from their /// serialised Asset IDs. @@ -61,7 +79,7 @@ namespace SHADE /// Handle to the resource to unload. static void Unload(AssetID assetId); /// - /// Needs to be called to finalise all changes to loads. + /// Needs to be called to finalise all changes to loads, unless at runtime. /// static void FinaliseChanges(); @@ -111,6 +129,9 @@ namespace SHADE static std::unordered_map> typedFreeFuncMap; // Pointers to temp CPU resources static std::vector loadedAssetData; + // Dirty Flags + static bool meshChanged; + static bool textureChanged; /*---------------------------------------------------------------------------------*/ /* Helper Functions */ @@ -124,6 +145,14 @@ namespace SHADE /// Reference to the AssetHandleMap of the specified type. template static std::pair getAssetHandleMap(); + /// + /// + /// + /// + /// + /// + template + static Handle load(AssetID assetId, const typename SHResourceLoader::AssetType& assetData); }; } diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 072adaa2..1623d70a 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -3,7 +3,7 @@ \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Oct 21, 2022 -\brief Contains the definition of the function templates of the +\brief Contains the definition of the function templates of the SHResourceManager static class. Copyright (C) 2022 DigiPen Institute of Technology. @@ -13,12 +13,17 @@ of DigiPen Institute of Technology is prohibited. #pragma once // Primary Include #include "SHResourceManager.h" +// External Dependencies +#include // Project Includes #include "Assets/SHAssetManager.h" #include "Assets/Asset Types/SHAssetIncludes.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Tools/SHLog.h" +#include "Graphics/Shaders/SHVkShaderModule.h" +#include "Graphics/Devices/SHVkLogicalDevice.h" +#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" namespace SHADE { @@ -40,67 +45,19 @@ namespace SHADE return Handle(typedHandleMap.get()[assetId]); /* Otherwise, we need to load it! */ - // Meshes - if constexpr (std::is_same_v) + // Load Asset Data + const auto* assetData = SHAssetManager::GetData::AssetType>(assetId); + if (assetData == nullptr) { - // Get system - SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); - if (gfxSystem == nullptr) - throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); - - // Load - const SHMeshAsset* assetData = SHAssetManager::GetData(assetId); - if (assetData == nullptr) - { - SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); - return {}; - } - loadedAssetData.emplace_back(assetId); - - Handle meshHandle = gfxSystem->AddMesh - ( - assetData->vertexPosition.size(), - assetData->vertexPosition.data(), - assetData->texCoords.data(), - assetData->vertexTangent.data(), - assetData->vertexNormal.data(), - assetData->indices.size(), - assetData->indices.data() - ); - Handle genericHandle = Handle(meshHandle); - typedHandleMap.get().emplace(assetId, genericHandle); - typedAssetIdMap.get().emplace(genericHandle, assetId); - return meshHandle; + SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); + return {}; } - // Textures - else if constexpr (std::is_same_v) - { - // Get system - SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); - if (gfxSystem == nullptr) - throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); - // Load - const SHTextureAsset* assetData = SHAssetManager::GetData(assetId); - if (assetData == nullptr) - { - SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); - return {}; - } - loadedAssetData.emplace_back(assetId); - - Handle texHandle = gfxSystem->AddTexture - ( - assetData->numBytes, - assetData->pixelData, - assetData->width, - assetData->height, - assetData->format, - assetData->mipOffsets - ); - typedHandleMap.get().emplace(assetId, Handle(texHandle)); - return texHandle; - } + auto handle = load(assetId, *assetData); + Handle genericHandle = Handle(handle); + typedHandleMap.get().emplace(assetId, genericHandle); + typedAssetIdMap.get().emplace(genericHandle, assetId); + return handle; } template @@ -152,7 +109,7 @@ namespace SHADE template std::pair SHResourceManager::getAssetHandleMap() { - const std::type_index TYPE = typeid(ResourceType); + const std::type_index TYPE = typeid(ResourceType); if (!handlesMap.contains(TYPE)) { @@ -160,7 +117,7 @@ namespace SHADE assetIdMap.emplace(TYPE, HandleAssetMap{}); typedFreeFuncMap.emplace ( - TYPE, + TYPE, [TYPE](AssetID assetId) { static_cast>(SHResourceManager::handlesMap[TYPE][assetId]).Free(); @@ -169,4 +126,131 @@ namespace SHADE } return std::make_pair(std::ref(handlesMap[TYPE]), std::ref(assetIdMap[TYPE])); } + + template + Handle SHResourceManager::load(AssetID assetId, const typename SHResourceLoader::AssetType& assetData) + { + // Get system + SHGraphicsSystem* gfxSystem = SHSystemManager::GetSystem(); + if (gfxSystem == nullptr) + throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); + + // Meshes + if constexpr (std::is_same_v) + { + loadedAssetData.emplace_back(assetId); + meshChanged = true; + + return gfxSystem->AddMesh + ( + assetData.vertexPosition.size(), + assetData.vertexPosition.data(), + assetData.texCoords.data(), + assetData.vertexTangent.data(), + assetData.vertexNormal.data(), + assetData.indices.size(), + assetData.indices.data() + ); + } + // Textures + else if constexpr (std::is_same_v) + { + loadedAssetData.emplace_back(assetId); + textureChanged = true; + + return gfxSystem->AddTexture + ( + assetData.numBytes, + assetData.pixelData, + assetData.width, + assetData.height, + assetData.format, + assetData.mipOffsets + ); + } + // Shaders + else if constexpr (std::is_same_v) + { + auto shader = gfxSystem->GetDevice()->CreateShaderModule + ( + assetData.spirvBinary, + "main", + static_cast(assetData.shaderType), + assetData.name + ); + shader->Reflect(); + return shader; + } + // Materials + else if constexpr (std::is_same_v) + { + // Get the data we need to construct + SHMaterialSpec matSpec = YAML::Node(assetData.data).as(); + + // Load shaders + auto vertexShader = SHResourceManager::LoadOrGet(matSpec.vertexShader); + auto fragShader = SHResourceManager::LoadOrGet(matSpec.fragShader); + + // Ensure that both shaders are present + if (!(vertexShader && fragShader)) + { + SHLOG_ERROR("[SHResourceManager] Failed to load material as shaders failed to be loaded."); + return {}; + } + + // Grab subpass from worldRenderer + auto renderPass = gfxSystem->GetPrimaryRenderpass(); + if (!renderPass) + { + SHLOG_ERROR("[SHResourceManager] Failed to load material as RenderPass could not be found."); + return {}; + } + auto subPass = renderPass->GetSubpass(matSpec.subpassName); + if (!subPass) + { + SHLOG_ERROR("[SHResourceManager] Failed to load material as SubPass could not be found."); + return {}; + } + + // Create material + auto matHandle = gfxSystem->AddMaterial(vertexShader, fragShader, subPass); + + // Set properties for the material + Handle pipelineProperties = matHandle->GetShaderBlockInterface(); + for (int i = 0; i < static_cast(pipelineProperties->GetVariableCount()); ++i) + { + const std::string& PROP_NAME = pipelineProperties->GetVariableName(i); + const auto& PROP_NODE = matSpec.properties; + if (PROP_NODE) + { + const std::string& VAR_NAME = pipelineProperties->GetVariableName(i); + const SHShaderBlockInterface::Variable* VARIABLE = pipelineProperties->GetVariable(i); + switch (VARIABLE->type) + { + case SHADE::SHShaderBlockInterface::Variable::Type::FLOAT: + matHandle->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::INT: + matHandle->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR2: + matHandle->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR3: + matHandle->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR4: + matHandle->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::OTHER: + default: + continue; + break; + } + } + } + + return matHandle; + } + } } diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index 21ce7b82..4a73342e 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -80,7 +80,10 @@ namespace SHADE { csScriptsExecuteFixedUpdate(); } - + void SHScriptEngine::ExecuteCollisionFunctions() + { + csScriptsExecutePhysicsEvents(); + } void SHScriptEngine::Exit() { // Do not allow deinitialization if not initialised @@ -377,6 +380,12 @@ namespace SHADE DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", "ExecuteLateUpdate" ); + csScriptsExecutePhysicsEvents = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "ExecuteCollisionFunctions" + ); csScriptsFrameCleanUp = dotNet.GetFunctionPtr ( DEFAULT_CSHARP_LIB_NAME, diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.h b/SHADE_Engine/src/Scripting/SHScriptEngine.h index c38e3618..9ddd617a 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.h +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.h @@ -98,6 +98,11 @@ namespace SHADE /// void ExecuteFixedUpdates(); /// + /// Executes the OnCollision*()s and OnTrigger*()s of the Scripts that are attached + /// to Entities. + /// + void ExecuteCollisionFunctions(); + /// /// Shuts down the DotNetRuntime. /// void Exit() override; @@ -245,6 +250,7 @@ namespace SHADE CsFuncPtr csScriptsExecuteFixedUpdate = nullptr; CsFuncPtr csScriptsExecuteUpdate = nullptr; CsFuncPtr csScriptsExecuteLateUpdate = nullptr; + CsFuncPtr csScriptsExecutePhysicsEvents = nullptr; CsFuncPtr csScriptsFrameCleanUp = nullptr; CsScriptManipFuncPtr csScriptsAdd = nullptr; CsScriptBasicFuncPtr csScriptsRemoveAll = nullptr; diff --git a/SHADE_Engine/src/Scripting/SHScriptEngineRoutines.cpp b/SHADE_Engine/src/Scripting/SHScriptEngineRoutines.cpp index 5467fc56..a2981c06 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngineRoutines.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngineRoutines.cpp @@ -54,7 +54,7 @@ namespace SHADE /* System Routine Functions - FrameCleanUpRoutine */ /*-----------------------------------------------------------------------------------*/ SHScriptEngine::FrameCleanUpRoutine::FrameCleanUpRoutine() - : SHSystemRoutine("Script Engine Frame Clean Up", false) + : SHSystemRoutine("Script Engine Frame Clean Up", true) {} void SHScriptEngine::FrameCleanUpRoutine::Execute(double) noexcept { diff --git a/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.cpp b/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.cpp new file mode 100644 index 00000000..8ab098b8 --- /dev/null +++ b/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.cpp @@ -0,0 +1,56 @@ +#include "SHpch.h" +#include "SHPrefabManager.h" + +namespace SHADE +{ + SHPrefabManager::PrefabMap SHPrefabManager::prefabMap{}; + + void SHPrefabManager::AddPrefab(AssetID const& prefabAssetID) noexcept + { + prefabMap.insert({ prefabAssetID, {} }); + } + + void SHPrefabManager::RemovePrefab(AssetID const& prefabAssetID) noexcept + { + prefabMap.erase(prefabAssetID); + } + + void SHPrefabManager::ClearPrefab(AssetID const& prefabAssetID) noexcept + { + if (prefabMap.contains(prefabAssetID)) + { + prefabMap[prefabAssetID].clear(); + } + } + + void SHPrefabManager::UpdateAllPrefabEntities(AssetID const& prefabAssetID) noexcept + { + //Loop through all entities and deserialize new data + } + + void SHPrefabManager::AddEntity(AssetID const& prefabAssetID, EntityID const& eid) noexcept + { + if (prefabMap.contains(prefabAssetID)) + { + prefabMap[prefabAssetID].insert(eid); + } + } + + void SHPrefabManager::RemoveEntity(AssetID const& prefabAssetID, EntityID const& eid) noexcept + { + if (prefabMap.contains(prefabAssetID)) + { + prefabMap[prefabAssetID].erase(eid); + } + } + + void SHPrefabManager::Clear() noexcept + { + prefabMap.clear(); + } + + bool SHPrefabManager::Empty() noexcept + { + return prefabMap.empty(); + } +} diff --git a/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.h b/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.h new file mode 100644 index 00000000..37c317ed --- /dev/null +++ b/SHADE_Engine/src/Serialization/Prefab/SHPrefabManager.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Assets/SHAssetMacros.h" +#include "ECS_Base/SHECSMacros.h" +#include +#include + + +namespace SHADE +{ + class SHPrefabManager + { + public: + using PrefabMap = std::unordered_map>; + + static void AddPrefab(AssetID const& prefabAssetID) noexcept; + static void RemovePrefab(AssetID const& prefabAssetID) noexcept; + static void ClearPrefab(AssetID const& prefabAssetID) noexcept; + static void UpdateAllPrefabEntities(AssetID const& prefabAssetID) noexcept; + static void AddEntity(AssetID const& prefabAssetID, EntityID const& eid) noexcept; + static void RemoveEntity(AssetID const& prefabAssetID, EntityID const& eid) noexcept; + static void Clear() noexcept; + static bool Empty() noexcept; + + private: + static PrefabMap prefabMap; + }; +} diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index f8913d8d..03498951 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -10,6 +10,7 @@ #include "Assets/SHAssetManager.h" #include +#include "Camera/SHCameraComponent.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Math/Transform/SHTransformComponent.h" #include "Physics/Components/SHRigidBodyComponent.h" @@ -58,7 +59,7 @@ namespace SHADE static EntityID DeserializeEntity(YAML::iterator& it, YAML::Node const& node, std::vector& createdEntities, EntityID parentEID = MAX_EID) { EntityID eid = MAX_EID; - if(!node) + if (!node) return eid; if (node[EIDNode]) eid = node[EIDNode].as(); @@ -71,13 +72,13 @@ namespace SHADE createdEntities.push_back(eid); if (node[NumberOfChildrenNode]) { - if(const int numOfChildren = node[NumberOfChildrenNode].as(); numOfChildren > 0) + if (const int numOfChildren = node[NumberOfChildrenNode].as(); numOfChildren > 0) { ++it; for (int i = 0; i < numOfChildren; ++i) { DeserializeEntity(it, (*it), createdEntities, eid); - if((i + 1) < numOfChildren) + if ((i + 1) < numOfChildren) ++it; } } @@ -85,7 +86,9 @@ namespace SHADE // Deserialise scripts if (node[ScriptsNode]) - SHSystemManager::GetSystem()->DeserialiseScripts(eid, node[ScriptsNode]); + SHSystemManager::GetSystem()->DeserialiseScripts(eid, node[ScriptsNode]); + + return eid; } void SHSerialization::DeserializeSceneFromFile(std::filesystem::path const& path) @@ -120,10 +123,10 @@ namespace SHADE { DeserializeEntity(it, (*it), createdEntities); } - if(createdEntities.empty()) + if (createdEntities.empty()) { SHLOG_ERROR("Failed to create entities from deserializaiton") - return; + return; } //Initialize Entity auto entityVecIt = createdEntities.begin(); @@ -137,7 +140,7 @@ namespace SHADE { out << SerializeEntityToNode(entityNode); auto const& children = entityNode->GetChildren(); - for(auto const& child : children) + for (auto const& child : children) { EmitEntity(child, out); } @@ -162,17 +165,36 @@ namespace SHADE { } + template, bool> = true> + static void AddComponentToComponentNode(YAML::Node& componentsNode, EntityID const& eid) + { + if (const ComponentType* component = SHComponentManager::GetComponent_s(eid)) + { + componentsNode[rttr::type::get().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(component); + } + } + + template, bool> = true> + static void AddConvComponentToComponentNode(YAML::Node& componentsNode, EntityID const& eid) + { + if (ComponentType* component = SHComponentManager::GetComponent_s(eid)) + { + componentsNode[rttr::type::get().get_name().data()] = YAML::convert::encode(*component); + } + } + + YAML::Node SHSerialization::SerializeEntityToNode(SHSceneNode* sceneNode) { YAML::Node node; auto eid = sceneNode->GetEntityID(); auto entity = SHEntityManager::GetEntityByID(eid); - if (!sceneNode || !entity) + if (!sceneNode && !entity) { node = YAML::Null; return node; } - node.SetStyle(YAML::EmitterStyle::Block); + node.SetStyle(YAML::EmitterStyle::Block); node[EIDNode] = eid; node[EntityNameNode] = entity->name; node[IsActiveNode] = sceneNode->IsActive(); @@ -181,22 +203,13 @@ namespace SHADE YAML::Node components; - if (const auto transform = SHComponentManager::GetComponent_s(eid)) - { - components[rttr::type::get().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(transform); - } - if (const auto renderable = SHComponentManager::GetComponent_s(eid)) - { - components[rttr::type::get().get_name().data()] = *renderable; - } - if (const auto rigidbody = SHComponentManager::GetComponent_s(eid)) - { - components[rttr::type::get().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(rigidbody); - } - if(const auto light = SHComponentManager::GetComponent_s(eid)) - { - components[rttr::type::get().get_name().data()] = SHSerializationHelper::SerializeComponentToNode(light); - } + AddComponentToComponentNode(components, eid); + AddComponentToComponentNode(components, eid); + AddConvComponentToComponentNode(components, eid); + AddComponentToComponentNode(components, eid); + AddComponentToComponentNode(components, eid); + AddConvComponentToComponentNode(components, eid); + node[ComponentsNode] = components; YAML::Node scripts; @@ -208,22 +221,22 @@ namespace SHADE EntityID SHSerialization::DeserializeEntitiesFromString(std::string const& data, EntityID const& parentEID) noexcept { - if(data.empty()) + if (data.empty()) return MAX_EID; YAML::Node entities = YAML::Load(data.c_str()); - EntityID eid{MAX_EID}; + EntityID eid{ MAX_EID }; std::vector createdEntities; - for(auto it = entities.begin(); it != entities.end(); ++it) + for (auto it = entities.begin(); it != entities.end(); ++it) { eid = DeserializeEntity(it, *it, createdEntities, parentEID); } - if(createdEntities.empty()) + if (createdEntities.empty()) { SHLOG_ERROR("Failed to create entities from deserializaiton") - return MAX_EID; + return MAX_EID; } auto entityVecIt = createdEntities.begin(); - for(auto it = entities.begin(); it != entities.end(); ++it) + for (auto it = entities.begin(); it != entities.end(); ++it) { InitializeEntity(*it, *entityVecIt++); } @@ -231,33 +244,22 @@ namespace SHADE } template, bool> = true> - std::optional GetComponentID(YAML::Node const& componentNode) + static void AddComponentID(std::vector& idList, YAML::Node const& componentNode) { if (componentNode[rttr::type::get().get_name().data()]) - return { SHFamilyID::GetID() }; - else - return std::nullopt; + idList.push_back(SHFamilyID::GetID()); } std::vector SHSerialization::GetComponentIDList(YAML::Node const& componentsNode) { std::vector componentIDList; - - auto id = GetComponentID(componentsNode); - if (id.has_value()) - componentIDList.push_back(id.value()); - - id = GetComponentID(componentsNode); - if (id.has_value()) - componentIDList.push_back(id.value()); - - id = GetComponentID(componentsNode); - if (id.has_value()) - componentIDList.push_back(id.value()); - - id = GetComponentID(componentsNode); - if(id.has_value()) - componentIDList.push_back(id.value()); + + AddComponentID(componentIDList, componentsNode); + AddComponentID(componentIDList, componentsNode); + AddComponentID(componentIDList, componentsNode); + AddComponentID(componentIDList, componentsNode); + AddComponentID(componentIDList, componentsNode); + AddComponentID(componentIDList, componentsNode); return componentIDList; } @@ -268,7 +270,10 @@ namespace SHADE if (!componentsNode) return; SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); + SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); + SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); SHSerializationHelper::ConvertNodeToComponent(componentsNode, eid); + SHSerializationHelper::ConvertNodeToComponent(componentsNode, eid); SHSerializationHelper::InitializeComponentFromNode(componentsNode, eid); } } diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index 047232a0..2179d1cf 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -12,11 +12,240 @@ #include "Resource/SHResourceManager.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Graphics/MiddleEnd/Interface/SHMaterial.h" +#include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" #include "SHSerializationTools.h" +#include "Physics/Components/SHColliderComponent.h" +#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" +#include "Tools/SHLog.h" namespace YAML { using namespace SHADE; + + template<> + struct convert + { + static constexpr const char* x = "x"; + static constexpr const char* y = "y"; + static constexpr const char* z = "z"; + static constexpr const char* w = "w"; + + static Node encode(SHVec4 const& rhs) + { + Node node; + node.SetStyle(EmitterStyle::Flow); + node[x] = rhs.x; + node[y] = rhs.y; + node[z] = rhs.z; + node[w] = rhs.w; + return node; + } + static bool decode(Node const& node, SHVec4& rhs) + { + if (node[x].IsDefined()) + rhs.x = node[x].as(); + if (node[y].IsDefined()) + rhs.y = node[y].as(); + if (node[z].IsDefined()) + rhs.z = node[z].as(); + if (node[w].IsDefined()) + rhs.w = node[w].as(); + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* x = "x"; + static constexpr const char* y = "y"; + static constexpr const char* z = "z"; + + static Node encode(SHVec3 const& rhs) + { + Node node; + node.SetStyle(EmitterStyle::Flow); + node[x] = rhs.x; + node[y] = rhs.y; + node[z] = rhs.z; + return node; + } + static bool decode(Node const& node, SHVec3& rhs) + { + if (node[x].IsDefined()) + rhs.x = node[x].as(); + if (node[y].IsDefined()) + rhs.y = node[y].as(); + if (node[z].IsDefined()) + rhs.z = node[z].as(); + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* x = "x"; + static constexpr const char* y = "y"; + + static Node encode(SHVec2 const& rhs) + { + Node node; + node.SetStyle(EmitterStyle::Flow); + node[x] = rhs.x; + node[y] = rhs.y; + return node; + } + static bool decode(Node const& node, SHVec2& rhs) + { + if (node[x].IsDefined()) + rhs.x = node[x].as(); + if (node[y].IsDefined()) + rhs.y = node[y].as(); + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* IsTrigger = "Is Trigger"; + + static constexpr const char* Type = "Type"; + static constexpr const char* HalfExtents = "Half Extents"; + static constexpr const char* Radius = "Radius"; + + static constexpr const char* Friction = "Friction"; + static constexpr const char* Bounciness = "Bounciness"; + static constexpr const char* Density = "Density"; + static constexpr const char* PositionOffset = "Position Offset"; + + static Node encode(SHCollider& rhs) + { + Node node; + + node[IsTrigger] = rhs.IsTrigger(); + + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + SHCollider::Type colliderType = rhs.GetType(); + + node[Type] = enumAlign.value_to_name(colliderType).data(); + + switch (colliderType) + { + case SHCollider::Type::BOX: + { + auto const bb = reinterpret_cast(rhs.GetShape()); + node[HalfExtents] = bb->GetHalfExtents(); + } + break; + case SHCollider::Type::SPHERE: + { + auto const bs = reinterpret_cast(rhs.GetShape()); + node[Radius] = bs->GetRadius(); + } + break; + case SHCollider::Type::CAPSULE: break; + default:; + } + + node[Friction] = rhs.GetFriction(); + node[Bounciness] = rhs.GetBounciness(); + node[Density] = rhs.GetDensity(); + node[PositionOffset] = rhs.GetPositionOffset(); + + return node; + } + static bool decode(Node const& node, SHCollider& rhs) + { + if (node[IsTrigger].IsDefined()) + rhs.SetIsTrigger(node[IsTrigger].as()); + if (!node[Type].IsDefined()) + return false; + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + bool ok; + const SHCollider::Type colliderType = enumAlign.name_to_value(node[Type].as()).convert(&ok); + if (!ok) + return false; + switch (colliderType) + { + case SHCollider::Type::BOX: + { + if(node[HalfExtents].IsDefined()) + rhs.SetBoundingBox(node[HalfExtents].as()); + } + break; + case SHCollider::Type::SPHERE: + { + if(node[Radius].IsDefined()) + rhs.SetBoundingSphere(node[Radius].as()); + } + break; + case SHCollider::Type::CAPSULE: break; + default:; + } + if (node[Friction].IsDefined()) + rhs.SetFriction(node[Friction].as()); + if (node[Bounciness].IsDefined()) + rhs.SetBounciness(rhs.GetBounciness()); + if (node[Density].IsDefined()) + rhs.SetDensity(node[Density].as()); + if (node[PositionOffset].IsDefined()) + rhs.SetPositionOffset(node[PositionOffset].as()); + + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* Colliders = "Colliders"; + static Node encode(SHColliderComponent& rhs) + { + Node node, collidersNode; + auto const& colliders = rhs.GetColliders(); + int const numColliders = static_cast(colliders.size()); + for (int i = 0; i < numColliders; ++i) + { + auto& collider = rhs.GetCollider(i); + Node colliderNode = convert::encode(collider); + if (colliderNode.IsDefined()) + collidersNode[i] = colliderNode; + } + node[Colliders] = collidersNode; + return node; + } + static bool decode(Node const& node, SHColliderComponent& rhs) + { + if (node[Colliders].IsDefined()) + { + int numColliders{}; + for (auto const& colliderNode : node[Colliders]) + { + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + bool ok = false; + const SHCollider::Type colliderType = enumAlign.name_to_value(colliderNode[convert::Type].as()).convert(&ok); + if (!ok) + return false; + + switch (colliderType) + { + case SHCollider::Type::BOX: rhs.AddBoundingBox(); break; + case SHCollider::Type::SPHERE: rhs.AddBoundingSphere(); break; + case SHCollider::Type::CAPSULE: break; + default:; + } + YAML::convert::decode(colliderNode, rhs.GetCollider(numColliders++)); + } + } + return true; + } + }; + template<> struct convert { @@ -46,13 +275,13 @@ namespace YAML propNode = rhs.GetProperty(VARIABLE->offset); break; case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR2: - propNode = SHSerializationTools::ValToYAML(rhs.GetProperty(VARIABLE->offset)); + propNode = rhs.GetProperty(VARIABLE->offset); break; case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR3: - propNode = SHSerializationTools::ValToYAML(rhs.GetProperty(VARIABLE->offset)); + propNode = rhs.GetProperty(VARIABLE->offset); break; case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR4: - propNode = SHSerializationTools::ValToYAML(rhs.GetProperty(VARIABLE->offset)); + propNode = rhs.GetProperty(VARIABLE->offset); break; case SHADE::SHShaderBlockInterface::Variable::Type::OTHER: default: @@ -77,104 +306,45 @@ namespace YAML // Write Material YAML::Node node; - node[VERT_SHADER_YAML_TAG.data()] = 0; // SHResourceManager::GetAssetID(vertexShader).value_or(0); - node[FRAG_SHADER_YAML_TAG.data()] = 0; // SHResourceManager::GetAssetID(fragShader).value_or(0); - node[SUBPASS_YAML_TAG.data()] = rhs.GetPipeline()->GetPipelineState().GetSubpass()->GetName(); + node[VERT_SHADER_YAML_TAG.data()] = SHResourceManager::GetAssetID(vertexShader).value_or(0); + node[FRAG_SHADER_YAML_TAG.data()] = SHResourceManager::GetAssetID(fragShader).value_or(0); + node[SUBPASS_YAML_TAG.data()] = rhs.GetPipeline()->GetPipelineState().GetSubpass()->GetName(); node[PROPS_YAML_TAG.data()] = propertiesNode; return node; } - static bool decode(YAML::Node const& node, SHMaterial& rhs) - { - /* - // Retrieve Shader Asset IDs - AssetID vertShaderId = 0; - AssetID fragShaderId = 0; - if (node[VERT_SHADER_YAML_TAG.data()]) - vertShaderId = node[VERT_SHADER_YAML_TAG.data()].as(); - if (node[FRAG_SHADER_YAML_TAG.data()]) - fragShaderId = node[FRAG_SHADER_YAML_TAG.data()].as(); + }; - // Ensure that both shaders are present - if (vertShaderId == 0 || fragShaderId == 0) - return false; // No pipeline + template<> + struct convert + { + static constexpr std::string_view VERT_SHADER_YAML_TAG = "VertexShader"; + static constexpr std::string_view FRAG_SHADER_YAML_TAG = "FragmentShader"; + static constexpr std::string_view SUBPASS_YAML_TAG = "SubPass"; + static constexpr std::string_view PROPS_YAML_TAG = "Properties"; - // Get Shader Modules - Handle vertexShader, fragShader; - vertexShader = SHResourceManager::LoadOrGet(vertShaderId); - fragShader = SHResourceManager::LoadOrGet(fragShaderId); + static bool decode(YAML::Node const& node, SHMaterialSpec& rhs) + { + // Retrieve Shader Asset IDs + if (!node[VERT_SHADER_YAML_TAG.data()]) + return false; + rhs.vertexShader = node[VERT_SHADER_YAML_TAG.data()].as(); + if (!node[FRAG_SHADER_YAML_TAG.data()]) + return false; + rhs.fragShader = node[FRAG_SHADER_YAML_TAG.data()].as(); - // Get Pipeline Library - if (node[SUBPASS_YAML_TAG.data()]) - { - auto gfxSystem = SHSystemManager::GetSystem(); - if (!gfxSystem) - return false; + // Retrieve Subpass + if (!node[SUBPASS_YAML_TAG.data()]) + return false; + rhs.subpassName = node[SUBPASS_YAML_TAG.data()].as(); - // Grab subpass from worldRenderer - auto renderPass = gfxSystem->GetPrimaryRenderpass(); - if (!renderPass) - return false; - auto subPass = renderPass->GetSubpass(node[SUBPASS_YAML_TAG.data()].as()); - if (!subPass) - return false; + // Retrieve + if (!node[PROPS_YAML_TAG.data()]) + return false; + rhs.properties = node[PROPS_YAML_TAG.data()]; - // Set Pipeline - rhs.SetPipeline(renderPass->GetOrCreatePipeline - ( - std::make_pair(vertexShader, fragShader), - subPass - )); - } - */ - - // TODO: Load Proper Material! - // Set default material - auto gfxSystem = SHSystemManager::GetSystem(); - if (!gfxSystem) - return false; - rhs.SetPipeline(gfxSystem->GetDefaultMaterial()->GetPipeline()); - - if (node[PROPS_YAML_TAG.data()]) - { - // Loop through all properties - Handle pipelineProperties = rhs.GetShaderBlockInterface(); - const YAML::Node& PROPS_NODE = node[PROPS_YAML_TAG.data()]; - for (int i = 0; i < static_cast(pipelineProperties->GetVariableCount()); ++i) - { - const std::string& PROP_NAME = pipelineProperties->GetVariableName(i); - const auto& PROP_NODE = PROPS_NODE[PROP_NAME.data()]; - if (PROP_NODE) - { - const std::string& VAR_NAME = pipelineProperties->GetVariableName(i); - const SHShaderBlockInterface::Variable* VARIABLE = pipelineProperties->GetVariable(i); - switch (VARIABLE->type) - { - case SHADE::SHShaderBlockInterface::Variable::Type::FLOAT: - rhs.SetProperty(VARIABLE->offset, PROP_NODE.as()); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::INT: - rhs.SetProperty(VARIABLE->offset, PROP_NODE.as()); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR2: - rhs.SetProperty(VARIABLE->offset, SHSerializationTools::YAMLToVec2(PROP_NODE)); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR3: - rhs.SetProperty(VARIABLE->offset, SHSerializationTools::YAMLToVec3(PROP_NODE)); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR4: - rhs.SetProperty(VARIABLE->offset, SHSerializationTools::YAMLToVec4(PROP_NODE)); - break; - case SHADE::SHShaderBlockInterface::Variable::Type::OTHER: - default: - continue; - break; - } - } - } - } - return true; - } + return true; + } }; template<> @@ -187,69 +357,62 @@ namespace YAML { YAML::Node node; node[MESH_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMesh()).value_or(0); - node[MAT_YAML_TAG.data()] = 0; // TODO: Asset ID + node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMaterial()->GetBaseMaterial()).value_or(0); return node; } static bool decode(YAML::Node const& node, SHRenderable& rhs) { - if (node[MESH_YAML_TAG.data()]) + if (node[MESH_YAML_TAG.data()].IsDefined()) { rhs.SetMesh(SHResourceManager::LoadOrGet(node[MESH_YAML_TAG.data()].as())); } - if (node[MAT_YAML_TAG.data()]) + if (node[MAT_YAML_TAG.data()].IsDefined()) { - // TODO: Convert Asset ID To Material HAndle // Temporarily, use default material auto gfxSystem = SHSystemManager::GetSystem(); if (!gfxSystem) return false; - rhs.SetMaterial(gfxSystem->AddOrGetBaseMaterialInstance(gfxSystem->GetDefaultMaterial())); + Handle baseMat = SHResourceManager::LoadOrGet(node[MAT_YAML_TAG.data()].as()); + if (!baseMat) + { + baseMat = gfxSystem->GetDefaultMaterial(); + SHLog::Warning("[SHSerializationHelper] Unable to load specified material. Falling back to default material."); + } + rhs.SetMaterial(gfxSystem->AddOrGetBaseMaterialInstance(baseMat)); } return true; } }; } - namespace SHADE { struct SHSerializationHelper { - template , bool> = true> - static std::string SerializeComponentToString(ComponentType* component) - { - return std::string(); - } - template , bool> = true> - static void SerializeComponentToFile(ComponentType* component, std::filesystem::path const& path) - { - } static YAML::Node RTTRToNode(const rttr::variant& var) { YAML::Node node; auto varType = var.get_type(); + if (varType.is_sequential_container()) + { + for (auto const& elem : var.create_sequential_view()) + { + node.push_back(RTTRToNode(elem)); + } + } if (varType == rttr::type::get()) { - node.SetStyle(YAML::EmitterStyle::Flow); - node["X"] = var.convert().x; - node["Y"] = var.convert().y; - node["Z"] = var.convert().z; - node["W"] = var.convert().w; + node = YAML::convert::encode(var.convert()); } else if (varType == rttr::type::get()) { - node.SetStyle(YAML::EmitterStyle::Flow); - node["X"] = var.convert().x; - node["Y"] = var.convert().y; - node["Z"] = var.convert().z; + node = YAML::convert::encode(var.convert()); } else if (varType == rttr::type::get()) { - node.SetStyle(YAML::EmitterStyle::Flow); - node["X"] = var.convert().x; - node["Y"] = var.convert().y; + node = YAML::convert::encode(var.convert()); } else if (varType.is_arithmetic()) { @@ -300,7 +463,7 @@ namespace SHADE else { auto properties = var.get_type().get_properties(); - for (auto property : properties) + for (auto const& property : properties) { node[property.get_name().data()] = RTTRToNode(property.get_value(var)); } @@ -308,6 +471,17 @@ namespace SHADE return node; } + template , bool> = true> + static std::string SerializeComponentToString(ComponentType* component) + { + return std::string(); + } + + template , bool> = true> + static void SerializeComponentToFile(ComponentType* component, std::filesystem::path const& path) + { + } + template , bool> = true> static YAML::Node SerializeComponentToNode(ComponentType* component) { @@ -327,15 +501,18 @@ namespace SHADE auto propType = prop.get_type(); if (propType == rttr::type::get()) { - prop.set_value(component, SHSerializationTools::YAMLToVec4(propertyNode)); + SHVec4 vec = propertyNode.as(); + prop.set_value(component, vec); } else if (propType == rttr::type::get()) { - prop.set_value(component, SHSerializationTools::YAMLToVec3(propertyNode)); + SHVec3 vec = propertyNode.as(); + prop.set_value(component, vec); } else if (propType == rttr::type::get()) { - prop.set_value(component, SHSerializationTools::YAMLToVec2(propertyNode)); + SHVec2 vec = propertyNode.as(); + prop.set_value(component, vec); } else if (propType.is_arithmetic()) { @@ -371,9 +548,10 @@ namespace SHADE else { auto properties = propType.get_properties(); - for (auto property : properties) + for (auto const& property : properties) { - InitializeProperty(component, property, propertyNode[property.get_name().data()]); + if(propertyNode[property.get_name().data()].IsDefined()) + InitializeProperty(component, property, propertyNode[property.get_name().data()]); } } } @@ -381,7 +559,7 @@ namespace SHADE template , bool> = true> static void InitializeComponentFromNode(YAML::Node const& componentsNode, EntityID const& eid) { - auto component = SHComponentManager::GetComponent_s(eid); + ComponentType* component = SHComponentManager::GetComponent_s(eid); if (componentsNode.IsNull() && !component) return; auto rttrType = rttr::type::get(); @@ -406,7 +584,7 @@ namespace SHADE return; auto rttrType = rttr::type::get(); auto componentNode = componentsNode[rttrType.get_name().data()]; - if (componentsNode.IsNull()) + if (!componentNode.IsDefined()) return; YAML::convert::decode(componentNode, *component); } diff --git a/SHADE_Engine/src/Tools/Dialog/SHWinDialog.cpp b/SHADE_Engine/src/Tools/Dialog/SHWinDialog.cpp new file mode 100644 index 00000000..bd13801a --- /dev/null +++ b/SHADE_Engine/src/Tools/Dialog/SHWinDialog.cpp @@ -0,0 +1,71 @@ +#include "SHpch.h" +#include "SHWinDialog.h" +#include +#include + +namespace SHADE +{ + void SHWinDialog::DisplayMessageBox(MessageBoxType const& messageBoxType, std::string const& title, std::string const& text) + { + if(messageBoxType == MessageBoxType::MB_MAX) + return; + + UINT flags = MB_APPLMODAL | MB_SETFOREGROUND | MB_OK; + flags |= static_cast(messageBoxType); + + const std::wstring wTitle(title.begin(), title.end()); + const std::wstring wText(text.begin(), text.end()); + + MessageBox(GetDesktopWindow(), wText.data(), wTitle.data(), flags); + } + + std::vector SHWinDialog::DisplayOpenDialog(OpenSaveConfig const& openSaveConfig) + { + const HWND hwnd = GetDesktopWindow(); + HRESULT hResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if(SUCCEEDED(hResult)) + { + IFileOpenDialog* pFileOpen; + + //Create Dialog object + hResult = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, IID_IFileOpenDialog, reinterpret_cast(pFileOpen)); + + if(SUCCEEDED(hResult)) + { + //Show the open dialog box + hResult = pFileOpen->Show(hwnd); + + //Get file name from the dialoh box + if(SUCCEEDED(hResult)) + { + if(openSaveConfig.openMultiple) + { + IShellItemArray* pItemArray; + hResult = pFileOpen->GetResults(&pItemArray); + if(SUCCEEDED(hResult)) + { + + } + } + else + { + IShellItem* pItem; + hResult = pFileOpen->GetResult(&pItem); + if(SUCCEEDED(hResult)) + { + PWSTR pszFilePath; + hResult = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); + } + } + + } + } + } + return {}; + } + + std::vector SHWinDialog::DisplaySaveAsDialog(OpenSaveConfig const& openSaveConfig) + { + return{}; + } +} diff --git a/SHADE_Engine/src/Tools/Dialog/SHWinDialog.h b/SHADE_Engine/src/Tools/Dialog/SHWinDialog.h new file mode 100644 index 00000000..02fe07b9 --- /dev/null +++ b/SHADE_Engine/src/Tools/Dialog/SHWinDialog.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +namespace SHADE +{ + //https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox + enum class MessageBoxType + { + MB_ERROR = 0x00000010L, + MB_QUESTION = 0x00000020L, + MB_WARNING = 0x00000030L, + MB_INFO = 0x00000040L, + MB_MAX = 0 + }; + + struct OpenSaveConfig + { + using Extension = std::string; + using Extensions = std::vector; + using FileTypeDesc = std::pair; + using FilterList = std::vector; + + std::string title = "Open"; + bool openFolders = false; + bool openMultiple = false; + FilterList filterList{}; + }; + + struct SHWinDialog + { + static void DisplayMessageBox(MessageBoxType const& messageBoxType, std::string const& title, std::string const& text); + static std::vector DisplayOpenDialog(OpenSaveConfig const& openSaveConfig); + static std::vector DisplaySaveAsDialog(OpenSaveConfig const& openSaveConfig); + }; +} diff --git a/SHADE_Engine/src/Tools/SHDebugDraw.cpp b/SHADE_Engine/src/Tools/SHDebugDraw.cpp new file mode 100644 index 00000000..a5b86c42 --- /dev/null +++ b/SHADE_Engine/src/Tools/SHDebugDraw.cpp @@ -0,0 +1,73 @@ +/************************************************************************************//*! +\file SHDebugDrawSystem.cpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 2, 2022 +\brief Contains the definition of functions of the SHDebugDrawSystem 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. +*//*************************************************************************************/ +// Precompiled Header +#include "SHpch.h" +// Primary Include +#include "SHDebugDraw.h" +// Project Includes +#include "Math/Vector/SHVec4.h" +#include "Math/SHColour.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" +#include "ECS_Base/Managers/SHSystemManager.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Static Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + SHDebugDrawSystem* SHDebugDraw::dbgDrawSys = nullptr; + + /*-----------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*-----------------------------------------------------------------------------------*/ + void SHDebugDraw::Init(SHDebugDrawSystem* sys) + { + dbgDrawSys = sys; + if (dbgDrawSys == nullptr) + { + SHLOG_ERROR("[SHDebugDraw] Invalid SHDebugDrawSystem provided for initialization!"); + } + } + + /*-----------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*-----------------------------------------------------------------------------------*/ + void SHDebugDraw::Line(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt) + { + dbgDrawSys->DrawLine(color, startPt, endPt); + } + + void SHDebugDraw::Tri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3) + { + dbgDrawSys->DrawTri(color, pt1, pt2, pt3); + } + + void SHDebugDraw::Quad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4) + { + dbgDrawSys->DrawQuad(color, pt1, pt2, pt3, pt4); + } + + void SHDebugDraw::Poly(const SHVec4& color, std::initializer_list pointList) + { + dbgDrawSys->DrawPoly(color, pointList); + } + + void SHDebugDraw::Cube(const SHVec4& color, const SHVec3& pos, const SHVec3& size) + { + dbgDrawSys->DrawCube(color, pos, size); + } + + void SHDebugDraw::Sphere(const SHVec4& color, const SHVec3& pos, double radius) + { + dbgDrawSys->DrawSphere(color, pos, radius); + } +} \ No newline at end of file diff --git a/SHADE_Engine/src/Tools/SHDebugDraw.h b/SHADE_Engine/src/Tools/SHDebugDraw.h new file mode 100644 index 00000000..7ce44ec2 --- /dev/null +++ b/SHADE_Engine/src/Tools/SHDebugDraw.h @@ -0,0 +1,101 @@ +/************************************************************************************//*! +\file SHDebugDraw.h +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 2, 2022 +\brief Contains the definition of the SHDebugDraw 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 + +// Project Includes +#include "SH_API.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + class SHDebugDrawSystem; + class SHVec4; + class SHVec3; + class SHColour; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + /// + /// Static helper class to make it easier to do debug drawing and enable support for + /// managed code to execute it withot dependency hell due to graphics system + /// dependencies. + /// + class SH_API SHDebugDraw final + { + public: + /*---------------------------------------------------------------------------------*/ + /* Lifecycle Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Sets up the link with the SHDebugDrawSystem. Must be called after the + /// SHDebugDrawSystem is spawned. + /// + static void Init(SHDebugDrawSystem* sys); + + /*---------------------------------------------------------------------------------*/ + /* Draw Functions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Renders a line between two points in world space. + /// + /// Colour of the line. + /// First point of the line. + /// Second point of the line. + static void Line(const SHVec4& color, const SHVec3& startPt, const SHVec3& endPt); + /// + /// Renders a triangle indicated by three points in world space. + /// + /// Colour of the triangle. + /// First point of the triangle. + /// Second point of the triangle. + /// Third point of the triangle. + static void Tri(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3); + /// + /// Renders a quadrilateral indicated by four points in world space. + /// + /// Colour of the quadrilateral. + /// First point of the triangle. + /// Second point of the quadrilateral. + /// Third point of the quadrilateral. + /// Third point of the quadrilateral. + static void Quad(const SHVec4& color, const SHVec3& pt1, const SHVec3& pt2, const SHVec3& pt3, const SHVec3& pt4); + /// + /// Renders a polygon indicated by the specified set of points in world space. + /// + /// Colour of the polygon. + /// List of points for the polygon. + static void Poly(const SHVec4& color, std::initializer_list pointList); + /// + /// Renders a wireframe cube centered around the position specified in world space. + /// + /// Colour of the cube. + /// Position where the cube wil be centered at. + /// Size of the rendered cube. + static void Cube(const SHVec4& color, const SHVec3& pos, const SHVec3& size); + /// + /// Renders a wireframe sphere centered around the position specified in world space. + /// + /// Colour of the sphere. + /// Position where the sphere wil be centered at. + /// Size of the rendered sphere. + static void Sphere(const SHVec4& color, const SHVec3& pos, double radius); + + private: + /*---------------------------------------------------------------------------------*/ + /* Static Data Members */ + /*---------------------------------------------------------------------------------*/ + static SHDebugDrawSystem* dbgDrawSys; + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Tools/SHLog.h b/SHADE_Engine/src/Tools/SHLog.h index 89dd9206..91117da6 100644 --- a/SHADE_Engine/src/Tools/SHLog.h +++ b/SHADE_Engine/src/Tools/SHLog.h @@ -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. *//*************************************************************************************/ - +#pragma once // Standard Library #include // Project Headers diff --git a/SHADE_Managed/premake5.lua b/SHADE_Managed/premake5.lua index 906511c1..b383f002 100644 --- a/SHADE_Managed/premake5.lua +++ b/SHADE_Managed/premake5.lua @@ -25,11 +25,16 @@ project "SHADE_Managed" includedirs { "%{prj.location}/src", + } + + externalincludedirs + { "%{IncludeDir.spdlog}/include", "%{IncludeDir.imgui}", "%{IncludeDir.imguizmo}", "%{IncludeDir.imnodes}", "%{IncludeDir.yamlcpp}", + "%{IncludeDir.SDL}\\include", "%{IncludeDir.RTTR}/include", "%{IncludeDir.dotnet}\\include", "%{IncludeDir.reactphysics3d}\\include", @@ -38,13 +43,16 @@ project "SHADE_Managed" libdirs { - "%{IncludeDir.RTTR}/lib" + "%{IncludeDir.RTTR}/lib", + "%{IncludeDir.SDL}/lib" } links { "yaml-cpp", "imgui", + "SDL2.lib", + "SDL2main.lib", "SHADE_Engine", "SHADE_CSharp" } diff --git a/SHADE_Managed/src/Components/Camera.cxx b/SHADE_Managed/src/Components/Camera.cxx new file mode 100644 index 00000000..5e570cc1 --- /dev/null +++ b/SHADE_Managed/src/Components/Camera.cxx @@ -0,0 +1,127 @@ +#include "SHpch.h" + +#include "Camera.hxx" +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Camera/SHCameraSystem.h" + + +namespace SHADE +{ + Camera::Camera(Entity entity) + :Component(entity) + { + + } + + + float Camera::Pitch::get() + { + return (GetNativeComponent()->GetPitch()); + } + + void Camera::Pitch::set(float val) + { + GetNativeComponent()->SetPitch(val); + } + float Camera::Yaw::get() + { + return (GetNativeComponent()->GetYaw()); + } + + void Camera::Yaw::set(float val) + { + GetNativeComponent()->SetYaw(val); + } + float Camera::Roll::get() + { + return (GetNativeComponent()->GetRoll()); + } + + void Camera::Roll::set(float val) + { + GetNativeComponent()->SetRoll(val); + } + float Camera::Width::get() + { + return (GetNativeComponent()->GetWidth()); + } + + void Camera::Width::set(float val) + { + GetNativeComponent()->SetWidth(val); + } + float Camera::Height::get() + { + return (GetNativeComponent()->GetHeight()); + } + + void Camera::Height::set(float val) + { + GetNativeComponent()->SetHeight(val); + } + float Camera::Near::get() + { + return (GetNativeComponent()->GetNear()); + } + + void Camera::Near::set(float val) + { + GetNativeComponent()->SetNear(val); + } + float Camera::Far::get() + { + return (GetNativeComponent()->GetFar()); + } + + void Camera::Far::set(float val) + { + GetNativeComponent()->SetFar(val); + } + float Camera::FOV::get() + { + return (GetNativeComponent()->GetFOV()); + } + + void Camera::FOV::set(float val) + { + GetNativeComponent()->SetFOV(val); + } + + Vector3 Camera::Position::get() + { + return Convert::ToCLI(GetNativeComponent()->GetPosition()); + } + + void Camera::Position::set(Vector3 val) + { + GetNativeComponent()->SetPosition(Convert::ToNative(val)); + } + + void Camera::SetMainCamera(size_t directorIndex) + { + auto system = SHSystemManager::GetSystem(); + system->SetMainCamera(GetNativeComponent()->GetEID(), directorIndex); + } + + void Camera::SetMainCamera() + { + SetMainCamera(0); + } + + void Camera::LookAt(Vector3 targetPosition) + { + auto system = SHSystemManager::GetSystem(); + system->CameraLookAt(*GetNativeComponent(), Convert::ToNative(targetPosition)); + } + + Vector3 Camera::GetForward() + { + auto system = SHSystemManager::GetSystem(); + SHVec3 forward, up, right; + system->GetCameraAxis(*GetNativeComponent(), forward, right, up); + return Convert::ToCLI(forward); + + } + + +} \ No newline at end of file diff --git a/SHADE_Managed/src/Components/Camera.hxx b/SHADE_Managed/src/Components/Camera.hxx new file mode 100644 index 00000000..257bff11 --- /dev/null +++ b/SHADE_Managed/src/Components/Camera.hxx @@ -0,0 +1,70 @@ +#pragma once + +// Project Includes +#include "Components/Component.hxx" +#include "Math/Vector3.hxx" +#include "Math/Quaternion.hxx" +// External Dependencies +#include "Camera/SHCameraComponent.h" + +namespace SHADE +{ + public ref class Camera : public Component + { + internal: + Camera(Entity entity); + + public: + property float Pitch + { + float get(); + void set(float val); + } + property float Yaw + { + float get(); + void set(float val); + } + property float Roll + { + float get(); + void set(float val); + } + property float Width + { + float get(); + void set(float val); + } + property float Height + { + float get(); + void set(float val); + } + property float Near + { + float get(); + void set(float val); + } + property float Far + { + float get(); + void set(float val); + } + property float FOV + { + float get(); + void set(float val); + } + property Vector3 Position + { + Vector3 get(); + void set(Vector3 val); + } + + + void SetMainCamera(size_t directorIndex); + void SetMainCamera(); + void LookAt(Vector3 targetPosition); + Vector3 GetForward(); + }; +} \ No newline at end of file diff --git a/SHADE_Managed/src/Components/CameraArm.cxx b/SHADE_Managed/src/Components/CameraArm.cxx new file mode 100644 index 00000000..67fcf6cf --- /dev/null +++ b/SHADE_Managed/src/Components/CameraArm.cxx @@ -0,0 +1,51 @@ +#include "SHpch.h" +#include "CameraArm.hxx" + + +namespace SHADE +{ + CameraArm::CameraArm(Entity entity) + :Component(entity) + { + + } + + float CameraArm::Pitch::get() + { + return (GetNativeComponent()->GetPitch()); + } + + void CameraArm::Pitch::set(float val) + { + GetNativeComponent()->SetPitch(val); + } + float CameraArm::Yaw::get() + { + return (GetNativeComponent()->GetYaw()); + } + + void CameraArm::Yaw::set(float val) + { + GetNativeComponent()->SetYaw(val); + } + + float CameraArm::ArmLength::get() + { + return (GetNativeComponent()->GetArmLength()); + } + + void CameraArm::ArmLength::set(float val) + { + GetNativeComponent()->SetArmLength(val); + } + + bool CameraArm::LookAtCameraOrigin::get() + { + return GetNativeComponent()->lookAtCameraOrigin; + } + + void CameraArm::LookAtCameraOrigin::set(bool val) + { + GetNativeComponent()->lookAtCameraOrigin = val; + } +} \ No newline at end of file diff --git a/SHADE_Managed/src/Components/CameraArm.hxx b/SHADE_Managed/src/Components/CameraArm.hxx new file mode 100644 index 00000000..771771cf --- /dev/null +++ b/SHADE_Managed/src/Components/CameraArm.hxx @@ -0,0 +1,40 @@ +#pragma once + +// Project Includes +#include "Components/Component.hxx" +#include "Math/Vector3.hxx" + +// External Dependencies +#include "Camera/SHCameraArmComponent.h" + + +namespace SHADE +{ + public ref class CameraArm : public Component + { + internal: + CameraArm(Entity entity); + public: + property float Pitch + { + float get(); + void set(float val); + } + property float Yaw + { + float get(); + void set(float val); + } + property float ArmLength + { + float get(); + void set(float val); + } + property bool LookAtCameraOrigin + { + bool get(); + void set(bool val); + } + + }; +} \ No newline at end of file diff --git a/SHADE_Managed/src/Components/Component.cxx b/SHADE_Managed/src/Components/Component.cxx index a6afc5cc..7f56fad3 100644 --- a/SHADE_Managed/src/Components/Component.cxx +++ b/SHADE_Managed/src/Components/Component.cxx @@ -67,6 +67,11 @@ namespace SHADE ScriptStore::RemoveScript(owner.GetEntity()); } + BaseComponent::operator bool(BaseComponent^ c) + { + return c != nullptr; + } + /*---------------------------------------------------------------------------------*/ /* Constructors */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Components/Component.hxx b/SHADE_Managed/src/Components/Component.hxx index 670e4e21..5ffa3952 100644 --- a/SHADE_Managed/src/Components/Component.hxx +++ b/SHADE_Managed/src/Components/Component.hxx @@ -101,6 +101,15 @@ namespace SHADE generic where T : ref class, Script void RemoveScript(); + /*-----------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------*/ + /// + /// Implicit conversion operator to enable checking if a component is null. + /// + /// Component to check. + static operator bool(BaseComponent^ c); + protected: /*-----------------------------------------------------------------------------*/ /* Constructors */ diff --git a/SHADE_Managed/src/Components/RigidBody.cxx b/SHADE_Managed/src/Components/RigidBody.cxx index 172b928b..12861600 100644 --- a/SHADE_Managed/src/Components/RigidBody.cxx +++ b/SHADE_Managed/src/Components/RigidBody.cxx @@ -148,6 +148,14 @@ namespace SHADE { return Convert::ToCLI(GetNativeComponent()->GetTorque()); } + bool RigidBody::Interpolating::get() + { + return GetNativeComponent()->IsInterpolating(); + } + void RigidBody::Interpolating::set(bool value) + { + GetNativeComponent()->SetInterpolate(value); + } /*---------------------------------------------------------------------------------*/ /* Force Functions */ diff --git a/SHADE_Managed/src/Components/RigidBody.hxx b/SHADE_Managed/src/Components/RigidBody.hxx index b3e031ba..d3a30612 100644 --- a/SHADE_Managed/src/Components/RigidBody.hxx +++ b/SHADE_Managed/src/Components/RigidBody.hxx @@ -129,6 +129,11 @@ namespace SHADE { Vector3 get(); } + property bool Interpolating + { + bool get(); + void set(bool value); + } /*-----------------------------------------------------------------------------*/ /* Force Functions */ diff --git a/SHADE_Managed/src/Components/Transform.cxx b/SHADE_Managed/src/Components/Transform.cxx index 98f0da4f..927ce87f 100644 --- a/SHADE_Managed/src/Components/Transform.cxx +++ b/SHADE_Managed/src/Components/Transform.cxx @@ -87,15 +87,6 @@ namespace SHADE { GetNativeComponent()->SetWorldScale(Convert::ToNative(val)); } - Transform^ Transform::Parent::get() - { - auto node = SHSceneManager::GetCurrentSceneGraph().GetNode(owner.GetEntity()); - if (!node) - throw gcnew System::InvalidOperationException("[Transform] Unable to retrieve SceneGraphNode for an Entity."); - - const auto PARENT = node->GetParent(); - return PARENT ? gcnew Transform(PARENT->GetEntityID()) : nullptr; - } /*---------------------------------------------------------------------------------*/ /* Constructors */ @@ -103,21 +94,4 @@ namespace SHADE Transform::Transform(Entity entity) : Component(entity) {} - - /*---------------------------------------------------------------------------------*/ - /* Usage Functions */ - /*---------------------------------------------------------------------------------*/ - - void Transform::SetParent(Transform^ parent, bool worldPositionStays) - { - auto& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - auto node = sceneGraph.GetNode(owner.GetEntity()); - if (!node) - throw gcnew System::InvalidOperationException("[Transform] Unable to retrieve SceneGraphNode for an Entity."); - - if (parent) - node->SetParent(sceneGraph.GetNode(parent->owner.GetEntity())); - else - sceneGraph.SetParent(parent->owner.GetEntity(), nullptr); - } } diff --git a/SHADE_Managed/src/Components/Transform.hxx b/SHADE_Managed/src/Components/Transform.hxx index bbe9fd19..942c6224 100644 --- a/SHADE_Managed/src/Components/Transform.hxx +++ b/SHADE_Managed/src/Components/Transform.hxx @@ -107,30 +107,6 @@ namespace SHADE Vector3 get(); void set(Vector3 val); } - /// - /// Parent Transform that affects this Transform. - /// - property Transform^ Parent - { - Transform^ get(); - } - - /*-----------------------------------------------------------------------------*/ - /* Usage Functions */ - /*-----------------------------------------------------------------------------*/ - /// - /// Sets the parent of this Transform component. - /// - /// - /// Entity that contains the Transform component that this Transform will be - /// parented to. If null, unparenting will occur. - /// - /// - /// If true, the transform values of this Transform component will retain their - /// pre-parent-change global transforms. The local transform values will be - /// modified to ensure that the global transforms do not change. - /// - void SetParent(Transform^ parent, bool worldPositionStays); }; } diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx index 1097e203..55fe91da 100644 --- a/SHADE_Managed/src/Editor/Editor.cxx +++ b/SHADE_Managed/src/Editor/Editor.cxx @@ -260,7 +260,7 @@ namespace SHADE int val = safe_cast(field->GetValue(object)); int oldVal = val; - if (SHEditorUI::InputEnumCombo(Convert::ToNative(field->Name), val, nativeEnumNames)) + if (SHEditorUI::InputEnumCombo(Convert::ToNative(field->Name), val, nativeEnumNames, &isHovered)) { field->SetValue(object, val); registerUndoAction(object, field, val, oldVal); @@ -280,12 +280,23 @@ namespace SHADE // Actual Field std::string val = Convert::ToNative(stringVal); std::string oldVal = val; - if (SHEditorUI::InputTextField(Convert::ToNative(field->Name), val)) + if (SHEditorUI::InputTextField(Convert::ToNative(field->Name), val, &isHovered)) { field->SetValue(object, Convert::ToCLI(val)); registerUndoAction(object, field, Convert::ToCLI(val), Convert::ToCLI(oldVal)); } } + else if (field->FieldType == GameObject::typeid) + { + GameObject gameObj = safe_cast(field->GetValue(object)); + uint32_t entityId = gameObj.GetEntity(); + if (SHEditorUI::InputGameObjectField(Convert::ToNative(field->Name), entityId, &isHovered)) + { + GameObject newVal = GameObject(entityId); + field->SetValue(object, newVal); + registerUndoAction(object, field, newVal, gameObj); + } + } else { array^ interfaces = field->FieldType->GetInterfaces(); diff --git a/SHADE_Managed/src/Engine/Application.cxx b/SHADE_Managed/src/Engine/Application.cxx new file mode 100644 index 00000000..c19bafa6 --- /dev/null +++ b/SHADE_Managed/src/Engine/Application.cxx @@ -0,0 +1,71 @@ +/************************************************************************************//*! +\file Application.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 31, 2022 +\brief Contains the definitions of the functions in the static managed + Application 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Application.hxx" +// External Dependencies +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Editor/SHEditor.h" +#include "Graphics/MiddleEnd/Interface/SHGraphicsSystemInterface.h" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Properties */ + /*---------------------------------------------------------------------------------*/ + bool Application::IsPlaying::get() + { + auto editor = SHSystemManager::GetSystem(); + if (editor) + return editor->editorState == SHEditor::State::PLAY + || + editor->editorState == SHEditor::State::PAUSE; + + return true; + } + bool Application::IsPaused::get() + { + auto editor = SHSystemManager::GetSystem(); + if (editor) + return editor->editorState == SHEditor::State::PAUSE; + + return false; + } + int Application::WindowWidth::get() + { + return SHGraphicsSystemInterface::GetWindowWidth(); + } + int Application::WindowHeight::get() + { + return SHGraphicsSystemInterface::GetWindowWidth(); + } + bool Application::IsFullscreen::get() + { + return SHGraphicsSystemInterface::IsFullscreen(); + } + /*void Application::IsFullscreen::set(bool value) + { + return SHGraphicsSystemInterface::SetFullscreen(value); + }*/ + + /*---------------------------------------------------------------------------------*/ + /* Usage Functions */ + /*---------------------------------------------------------------------------------*/ + void Application::Quit() + { + SHGraphicsSystemInterface::CloseWindow(); + } +} diff --git a/SHADE_Managed/src/Engine/Application.hxx b/SHADE_Managed/src/Engine/Application.hxx new file mode 100644 index 00000000..8629cf75 --- /dev/null +++ b/SHADE_Managed/src/Engine/Application.hxx @@ -0,0 +1,77 @@ +/************************************************************************************//*! +\file Application.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 31, 2022 +\brief Contains the definitions of a managed static Application 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 + +namespace SHADE +{ + /// + /// Static class that contains useful properties for querying the state of the + /// engine. + /// + public ref class Application abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Whether or not the engine is playing. This will always be true on Publish. + /// On Debug/Release builds, this is true when the editor is in Play Mode. It + /// will also be true even if the editor is in Play Mode but is paused. + /// + static property bool IsPlaying + { + bool get(); + } + /// + /// Whether or not the engine is in a paused state where script updates and + /// physics are not in play. + /// + static property bool IsPaused + { + bool get(); + } + /// + /// Retrieves the designated width of the current window. + /// + static property int WindowWidth + { + int get(); + } + /// + /// Retrieves the designated height of the current window. + /// + static property int WindowHeight + { + int get(); + } + /// + /// Whether or not the application is currently in fullscreen mode or not. + /// + static property bool IsFullscreen + { + bool get(); + // TODO: once implemented on SHADE_Engine + //void set(bool value); + } + + /*-----------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Marks the application to stop at the end of the current frame. + /// + static void Quit(); + }; +} diff --git a/SHADE_Managed/src/Engine/ECS.cxx b/SHADE_Managed/src/Engine/ECS.cxx index df67c788..3da39394 100644 --- a/SHADE_Managed/src/Engine/ECS.cxx +++ b/SHADE_Managed/src/Engine/ECS.cxx @@ -33,6 +33,8 @@ of DigiPen Institute of Technology is prohibited. #include "Components/Transform.hxx" #include "Components\RigidBody.hxx" #include "Components\Collider.hxx" +#include "Components/Camera.hxx" +#include "Components/CameraArm.hxx" namespace SHADE { @@ -248,6 +250,8 @@ namespace SHADE componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); + componentMap.Add(createComponentSet()); + componentMap.Add(createComponentSet()); } /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Engine/GameObject.cxx b/SHADE_Managed/src/Engine/GameObject.cxx index 8c78e399..7ceabfed 100644 --- a/SHADE_Managed/src/Engine/GameObject.cxx +++ b/SHADE_Managed/src/Engine/GameObject.cxx @@ -17,9 +17,12 @@ of DigiPen Institute of Technology is prohibited. #include "GameObject.hxx" // External Dependencies #include "ECS_Base/Managers/SHEntityManager.h" +#include "Scene/SHSceneGraph.h" // Project Headers #include "ECS.hxx" +#include "Utility/Convert.hxx" #include "Scripts/ScriptStore.hxx" +#include "Utility/Debug.hxx" namespace SHADE { @@ -39,7 +42,13 @@ namespace SHADE System::Nullable GameObject::Find(System::String ^ name) { // Search the GameObjectLibrary for an Entity with the specified name - throw gcnew System::NotImplementedException(); + const auto ENTITY_ID = SHEntityManager::GetEntityByName(Convert::ToNative(name)); + if (ENTITY_ID == MAX_EID) + { + return {}; + } + + return GameObject(ENTITY_ID); } /*---------------------------------------------------------------------------------*/ @@ -56,7 +65,38 @@ namespace SHADE } bool GameObject::IsActiveInHierarchy::get() { - return true; // TODO: Update once we have an equivalent on the Entity object + auto node = SHSceneManager::GetCurrentSceneGraph().GetNode(GetEntity()); + if (!node) + { + Debug::LogWarning("Attempting to access a GameObject's ActiveInHierarchy state which does not exist. Assuming inactive."); + return false; + } + return node->IsActive(); + } + Entity GameObject::EntityId::get() + { + return entity; + } + GameObject^ GameObject::Parent::get() + { + const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); + const auto* ROOT = SCENE_GRAPH.GetRoot(); + + const auto* NODE = SCENE_GRAPH.GetNode(entity); + if (NODE == nullptr) + throw gcnew System::InvalidOperationException("Unable to retrieve SceneGraphNode for Entity " + entity.ToString()); + + const auto* PARENT = NODE->GetParent(); + return PARENT != ROOT ? gcnew GameObject(PARENT->GetEntityID()) : nullptr; + } + void GameObject::Parent::set(GameObject^ newParent) + { + const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); + + if (newParent == nullptr) + SCENE_GRAPH.SetParent(entity, nullptr); + else + SCENE_GRAPH.SetParent(entity, newParent->EntityId); } /*---------------------------------------------------------------------------------*/ @@ -141,11 +181,13 @@ namespace SHADE /* Constructors */ /*---------------------------------------------------------------------------------*/ GameObject::GameObject(const SHEntity& entity) - : entity { entity.GetEID() } + : entity { entity.GetEID() } + , children{ gcnew System::Collections::ArrayList } {} GameObject::GameObject(Entity entity) - : entity { entity } + : entity { entity } + , children{ gcnew System::Collections::ArrayList } {} /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Engine/GameObject.hxx b/SHADE_Managed/src/Engine/GameObject.hxx index 723d9cec..99296a91 100644 --- a/SHADE_Managed/src/Engine/GameObject.hxx +++ b/SHADE_Managed/src/Engine/GameObject.hxx @@ -86,6 +86,21 @@ namespace SHADE { bool get(); } + /// + /// Native Entity ID value for this GameObject. + /// + property Entity EntityId + { + Entity get(); + } + /// + /// The parent entity for this GameObject. + /// + property GameObject^ Parent + { + GameObject^ get(); + void set(GameObject^); + } /*-----------------------------------------------------------------------------*/ /* GameObject Property Functions */ @@ -105,6 +120,7 @@ namespace SHADE /// Whether to activate or deactivate this GameObject. /// void SetActive(bool active); + /*-----------------------------------------------------------------------------*/ /* Component Access Functions */ @@ -235,7 +251,8 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ /* Data Members */ /*-----------------------------------------------------------------------------*/ - Entity entity; + Entity entity; + System::Collections::ArrayList^ children; public: /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Engine/Time.cxx b/SHADE_Managed/src/Engine/Time.cxx index ff0628e7..36032e00 100644 --- a/SHADE_Managed/src/Engine/Time.cxx +++ b/SHADE_Managed/src/Engine/Time.cxx @@ -14,6 +14,9 @@ of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ // Precompiled Headers #include "SHpch.h" +// External Dependencies +#include "FRC/SHFramerateController.h" +#include "Physics/SHPhysicsSystemInterface.h" // Primary Header #include "Time.hxx" @@ -26,4 +29,14 @@ namespace SHADE { return SHFrameRateController::GetRawDeltaTime(); } + + + float Time::DeltaTimeF::get() + { + return static_cast(SHFrameRateController::GetRawDeltaTime()); + } + double Time::FixedDeltaTime::get() + { + return SHPhysicsSystemInterface::GetFixedDT(); + } } \ No newline at end of file diff --git a/SHADE_Managed/src/Engine/Time.hxx b/SHADE_Managed/src/Engine/Time.hxx index 969eea03..c0f0ed62 100644 --- a/SHADE_Managed/src/Engine/Time.hxx +++ b/SHADE_Managed/src/Engine/Time.hxx @@ -14,8 +14,6 @@ of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #pragma once -#include "FRC/SHFramerateController.h" - namespace SHADE { /// @@ -29,13 +27,28 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ /// /// Time taken to process the previous frame. - /// Note, is affected by TimeScale. Use UnscaledDeltaTime if you wish to retrieve - /// real world time. This is also affected by MaxDeltaTime clamping that - /// UnscaledDeltaTime is subject to. /// static property double DeltaTime { double get(); } + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Time taken to process the previous frame. + /// + static property float DeltaTimeF + { + float get(); + } + /// + /// Time taken for Physics simulations. You should use this for operations + /// within Script.FixedUpdate() + /// + static property double FixedDeltaTime + { + double get(); + } }; } \ No newline at end of file diff --git a/SHADE_Managed/src/Input/Input.cxx b/SHADE_Managed/src/Input/Input.cxx index 0a386f3a..f0ea0edc 100644 --- a/SHADE_Managed/src/Input/Input.cxx +++ b/SHADE_Managed/src/Input/Input.cxx @@ -99,4 +99,11 @@ namespace SHADE return SHInputManager::GetKeyReleasedTime(static_cast(key)); } + Vector2 Input::GetMouseVelocity() + { + double velX, velY; + SHInputManager::GetMouseVelocity(&velX, &velY); + + return Convert::ToCLI(SHVec2{ (float)velX,(float)velY }); + } } \ No newline at end of file diff --git a/SHADE_Managed/src/Input/Input.hxx b/SHADE_Managed/src/Input/Input.hxx index f281e4c8..875054cc 100644 --- a/SHADE_Managed/src/Input/Input.hxx +++ b/SHADE_Managed/src/Input/Input.hxx @@ -31,10 +31,159 @@ namespace SHADE /// /// Represents the available supported keycodes that can be passed into the /// key-based Input functions. + /// + /// Attempting to follow https://docs.unity3d.com/ScriptReference/KeyCode.html + /// Win32 keycodes are shift-insensitive, i.e. 'A' and 'a' are the same keycode and '1' and '!' are the same keycode /// enum class KeyCode : int { + Backspace = static_cast(SHInputManager::SH_KEYCODE::BACKSPACE), + Delete = static_cast(SHInputManager::SH_KEYCODE::DEL), + Tab = static_cast(SHInputManager::SH_KEYCODE::TAB), + Clear = static_cast(SHInputManager::SH_KEYCODE::CLEAR), + Return = static_cast(SHInputManager::SH_KEYCODE::ENTER), + Pause = static_cast(SHInputManager::SH_KEYCODE::PAUSE), + Escape = static_cast(SHInputManager::SH_KEYCODE::ESCAPE), Space = static_cast(SHInputManager::SH_KEYCODE::SPACE), + Keypad0 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_0), + Keypad1 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_1), + Keypad2 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_2), + Keypad3 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_3), + Keypad4 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_4), + Keypad5 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_5), + Keypad6 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_6), + Keypad7 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_7), + Keypad8 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_8), + Keypad9 = static_cast(SHInputManager::SH_KEYCODE::NUMPAD_9), + KeypadPeriod = static_cast(SHInputManager::SH_KEYCODE::DECIMAL), + KeypadDivide = static_cast(SHInputManager::SH_KEYCODE::DIVIDE), + KeypadMultiply = static_cast(SHInputManager::SH_KEYCODE::MULTIPLY), + KeypadMinus = static_cast(SHInputManager::SH_KEYCODE::SUBTRACT), + KeypadPlus = static_cast(SHInputManager::SH_KEYCODE::ADD), + KeypadEnter = static_cast(SHInputManager::SH_KEYCODE::ENTER), + //KeypadEquals + UpArrow = static_cast(SHInputManager::SH_KEYCODE::UP_ARROW), + DownArrow = static_cast(SHInputManager::SH_KEYCODE::DOWN_ARROW), + RightArrow = static_cast(SHInputManager::SH_KEYCODE::RIGHT_ARROW), + LeftArrow = static_cast(SHInputManager::SH_KEYCODE::LEFT_ARROW), + Insert = static_cast(SHInputManager::SH_KEYCODE::INSERT), + Home = static_cast(SHInputManager::SH_KEYCODE::HOME), + End = static_cast(SHInputManager::SH_KEYCODE::END), + PageUp = static_cast(SHInputManager::SH_KEYCODE::PAGE_UP), + PageDown = static_cast(SHInputManager::SH_KEYCODE::PAGE_DOWN), + F1 = static_cast(SHInputManager::SH_KEYCODE::F1), + F2 = static_cast(SHInputManager::SH_KEYCODE::F2), + F3 = static_cast(SHInputManager::SH_KEYCODE::F3), + F4 = static_cast(SHInputManager::SH_KEYCODE::F4), + F5 = static_cast(SHInputManager::SH_KEYCODE::F5), + F6 = static_cast(SHInputManager::SH_KEYCODE::F6), + F7 = static_cast(SHInputManager::SH_KEYCODE::F7), + F8 = static_cast(SHInputManager::SH_KEYCODE::F8), + F9 = static_cast(SHInputManager::SH_KEYCODE::F9), + F10 = static_cast(SHInputManager::SH_KEYCODE::F10), + F11 = static_cast(SHInputManager::SH_KEYCODE::F11), + F12 = static_cast(SHInputManager::SH_KEYCODE::F12), + F13 = static_cast(SHInputManager::SH_KEYCODE::F13), + F14 = static_cast(SHInputManager::SH_KEYCODE::F14), + F15 = static_cast(SHInputManager::SH_KEYCODE::F15), + F16 = static_cast(SHInputManager::SH_KEYCODE::F16), + F17 = static_cast(SHInputManager::SH_KEYCODE::F17), + F18 = static_cast(SHInputManager::SH_KEYCODE::F18), + F19 = static_cast(SHInputManager::SH_KEYCODE::F19), + F20 = static_cast(SHInputManager::SH_KEYCODE::F20), + F21 = static_cast(SHInputManager::SH_KEYCODE::F21), + F22 = static_cast(SHInputManager::SH_KEYCODE::F22), + F23 = static_cast(SHInputManager::SH_KEYCODE::F23), + F24 = static_cast(SHInputManager::SH_KEYCODE::F24), + Alpha0 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_0), + Alpha1 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_1), + Alpha2 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_2), + Alpha3 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_3), + Alpha4 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_4), + Alpha5 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_5), + Alpha6 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_6), + Alpha7 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_7), + Alpha8 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_8), + Alpha9 = static_cast(SHInputManager::SH_KEYCODE::NUMBER_9), + //Exclaim + //DoubleQuote + //Hash + //Dollar + //Percent + //Ampersand + Quote = static_cast(SHInputManager::SH_KEYCODE::OEM_7), + //LeftParen + //RightParen + //Asterisk + //Plus + Comma = static_cast(SHInputManager::SH_KEYCODE::OEM_COMMA), + Minus = static_cast(SHInputManager::SH_KEYCODE::OEM_MINUS), + Period = static_cast(SHInputManager::SH_KEYCODE::OEM_PERIOD), + Slash = static_cast(SHInputManager::SH_KEYCODE::OEM_2), + //Colon + Semicolon = static_cast(SHInputManager::SH_KEYCODE::OEM_1), + //Less + Equals = static_cast(SHInputManager::SH_KEYCODE::OEM_PLUS), + //Greater + //Question + //At + LeftBracket = static_cast(SHInputManager::SH_KEYCODE::OEM_4), + Backslash = static_cast(SHInputManager::SH_KEYCODE::OEM_5), + RightBracket = static_cast(SHInputManager::SH_KEYCODE::OEM_6), + //Caret + //Underscore + BackQuote = static_cast(SHInputManager::SH_KEYCODE::OEM_3), + A = static_cast(SHInputManager::SH_KEYCODE::A), + B = static_cast(SHInputManager::SH_KEYCODE::B), + C = static_cast(SHInputManager::SH_KEYCODE::C), + D = static_cast(SHInputManager::SH_KEYCODE::D), + E = static_cast(SHInputManager::SH_KEYCODE::E), + F = static_cast(SHInputManager::SH_KEYCODE::F), + G = static_cast(SHInputManager::SH_KEYCODE::G), + H = static_cast(SHInputManager::SH_KEYCODE::H), + I = static_cast(SHInputManager::SH_KEYCODE::I), + J = static_cast(SHInputManager::SH_KEYCODE::J), + K = static_cast(SHInputManager::SH_KEYCODE::K), + L = static_cast(SHInputManager::SH_KEYCODE::L), + M = static_cast(SHInputManager::SH_KEYCODE::M), + N = static_cast(SHInputManager::SH_KEYCODE::N), + O = static_cast(SHInputManager::SH_KEYCODE::O), + P = static_cast(SHInputManager::SH_KEYCODE::P), + Q = static_cast(SHInputManager::SH_KEYCODE::Q), + R = static_cast(SHInputManager::SH_KEYCODE::R), + S = static_cast(SHInputManager::SH_KEYCODE::S), + T = static_cast(SHInputManager::SH_KEYCODE::T), + U = static_cast(SHInputManager::SH_KEYCODE::U), + V = static_cast(SHInputManager::SH_KEYCODE::V), + W = static_cast(SHInputManager::SH_KEYCODE::W), + X = static_cast(SHInputManager::SH_KEYCODE::X), + Y = static_cast(SHInputManager::SH_KEYCODE::Y), + Z = static_cast(SHInputManager::SH_KEYCODE::Z), + //LeftCurlyBracket + //Pipe + //RightCurlyBracket + //Tilde + NumLock = static_cast(SHInputManager::SH_KEYCODE::NUM_LOCK), + CapsLock = static_cast(SHInputManager::SH_KEYCODE::CAPS_LOCK), + ScrollLock = static_cast(SHInputManager::SH_KEYCODE::SCROLL_LOCK), + RightShift = static_cast(SHInputManager::SH_KEYCODE::RIGHT_SHIFT), + LeftShift = static_cast(SHInputManager::SH_KEYCODE::LEFT_SHIFT), + RightControl = static_cast(SHInputManager::SH_KEYCODE::RIGHT_CTRL), + LeftControl = static_cast(SHInputManager::SH_KEYCODE::LEFT_CTRL), + RightAlt = static_cast(SHInputManager::SH_KEYCODE::RIGHT_ALT), + LeftAlt = static_cast(SHInputManager::SH_KEYCODE::LEFT_ALT), + LeftWindows = static_cast(SHInputManager::SH_KEYCODE::LEFT_WINDOWS), + RightWindows = static_cast(SHInputManager::SH_KEYCODE::RIGHT_WINDOWS), + //AltGr + Help = static_cast(SHInputManager::SH_KEYCODE::HELP), + Print = static_cast(SHInputManager::SH_KEYCODE::PRINT), + SysReq = static_cast(SHInputManager::SH_KEYCODE::PRINT_SCREEN), + //Break + //Menu + //Mouse buttons use mouse codes, which are enums declared later + //TODO Controller input +#if 0 + Space = static_cast(SHInputManager::SH_KEYCODE::SPACE), //Apostrophe = static_cast(SHInputManager::SH_KEYCODE::APOSTROPHE), Comma = static_cast(SHInputManager::SH_KEYCODE::OEM_COMMA), Minus = static_cast(SHInputManager::SH_KEYCODE::OEM_MINUS), @@ -190,7 +339,8 @@ namespace SHADE JoystickButton6 = JoystickView, JoystickButton7 = JoystickMenu, JoystickButton8 = JoystickLeftStick, - JoystickButton9 = JoystickRightStick + JoystickButton9 = JoystickRightStick +#endif }; /// @@ -321,5 +471,7 @@ namespace SHADE /// The key to check. /// Time in seconds that the key was held. static double GetMouseReleasedTime(MouseCode mouseButton); + + static Vector2 GetMouseVelocity(); }; } \ No newline at end of file diff --git a/SHADE_Managed/src/Math/Vector2.cxx b/SHADE_Managed/src/Math/Vector2.cxx index b110d4f8..42080d60 100644 --- a/SHADE_Managed/src/Math/Vector2.cxx +++ b/SHADE_Managed/src/Math/Vector2.cxx @@ -236,6 +236,22 @@ namespace SHADE lhs.y * rhs.y ); } + Vector2 Vector2::operator*(Vector2 lhs, double rhs) + { + return Vector2 + ( + lhs.x * static_cast(rhs), + lhs.y * static_cast(rhs) + ); + } + Vector2 Vector2::operator/(Vector2 lhs, double rhs) + { + return Vector2 + ( + lhs.x / static_cast(rhs), + lhs.y / static_cast(rhs) + ); + } Vector2 Vector2::operator*(Vector2 lhs, float rhs) { return Vector2 diff --git a/SHADE_Managed/src/Math/Vector2.hxx b/SHADE_Managed/src/Math/Vector2.hxx index 94b1989f..4877696b 100644 --- a/SHADE_Managed/src/Math/Vector2.hxx +++ b/SHADE_Managed/src/Math/Vector2.hxx @@ -361,6 +361,22 @@ namespace SHADE /// Vector2 to multiply with. /// Scalar to multiply with. /// The result of the scalar multiplication. + static Vector2 operator*(Vector2 lhs, double rhs); + /// + /// Calculates the division of a Vector2 with a scalar value and returns + /// the result. + /// + /// Scalar to divide with. + /// Vector2 to divide with. + /// The result of the scalar division. + static Vector2 operator/(Vector2 lhs, double rhs); + /// + /// Calculates the multiplication of a Vector2 with a scalar value and returns + /// the result. + /// + /// Vector2 to multiply with. + /// Scalar to multiply with. + /// The result of the scalar multiplication. static Vector2 operator*(Vector2 lhs, float rhs); /// /// Calculates the division of a Vector2 with a scalar value and returns diff --git a/SHADE_Managed/src/Math/Vector3.cxx b/SHADE_Managed/src/Math/Vector3.cxx index adbb4d3a..83adbb38 100644 --- a/SHADE_Managed/src/Math/Vector3.cxx +++ b/SHADE_Managed/src/Math/Vector3.cxx @@ -237,6 +237,24 @@ namespace SHADE lhs.z * rhs.z ); } + Vector3 Vector3::operator*(Vector3 lhs, double rhs) + { + return Vector3 + ( + lhs.x * static_cast(rhs), + lhs.y * static_cast(rhs), + lhs.z * static_cast(rhs) + ); + } + Vector3 Vector3::operator/(Vector3 lhs, double rhs) + { + return Vector3 + ( + lhs.x / static_cast(rhs), + lhs.y / static_cast(rhs), + lhs.z / static_cast(rhs) + ); + } Vector3 Vector3::operator*(Vector3 lhs, float rhs) { return Vector3 diff --git a/SHADE_Managed/src/Math/Vector3.hxx b/SHADE_Managed/src/Math/Vector3.hxx index 70cff88f..4cdf653e 100644 --- a/SHADE_Managed/src/Math/Vector3.hxx +++ b/SHADE_Managed/src/Math/Vector3.hxx @@ -375,6 +375,22 @@ namespace SHADE /// Vector3 to multiply with. /// Scalar to multiply with. /// The result of the scalar multiplication. + static Vector3 operator*(Vector3 lhs, double rhs); + /// + /// Calculates the division of a Vector3 with a scalar value and returns + /// the result. + /// + /// Scalar to divide with. + /// Vector3 to divide with. + /// The result of the scalar division. + static Vector3 operator/(Vector3 lhs, double rhs); + /// + /// Calculates the multiplication of a Vector3 with a scalar value and returns + /// the result. + /// + /// Vector3 to multiply with. + /// Scalar to multiply with. + /// The result of the scalar multiplication. static Vector3 operator*(Vector3 lhs, float rhs); /// /// Calculates the division of a Vector3 with a scalar value and returns diff --git a/SHADE_Managed/src/Physics/CollisionInfo.cxx b/SHADE_Managed/src/Physics/CollisionInfo.cxx new file mode 100644 index 00000000..135760db --- /dev/null +++ b/SHADE_Managed/src/Physics/CollisionInfo.cxx @@ -0,0 +1,36 @@ +/************************************************************************************//*! +\file CollisionInfo.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 31, 2022 +\brief Contains the definition of the functions of the managed CollisionInfo + struct. + + 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. +*//*************************************************************************************/ +#include "SHpch.h" +#include "CollisionInfo.hxx" +#include "Components/RigidBody.hxx" +#include "Components/Collider.hxx" + +namespace SHADE +{ + Collider^ CollisionInfo::Collider::get() + { + return GameObject.GetComponent(); + } + + CollisionShape^ CollisionInfo::CollisionShape::get() + { + throw gcnew System::NotImplementedException(); + } + + RigidBody^ CollisionInfo::RigidBody::get() + { + return GameObject.GetComponent(); + } +} diff --git a/SHADE_Managed/src/Physics/CollisionInfo.hxx b/SHADE_Managed/src/Physics/CollisionInfo.hxx new file mode 100644 index 00000000..40cb9ccc --- /dev/null +++ b/SHADE_Managed/src/Physics/CollisionInfo.hxx @@ -0,0 +1,66 @@ +/************************************************************************************//*! +\file CollisionInfo.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Oct 31, 2022 +\brief Contains the definition of the managed CollisionInfo struct with the + definition of its properties and declaration of functions. + + 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 +// Project Includes +#include "Engine/GameObject.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*---------------------------------------------------------------------------------*/ + ref class RigidBody; + ref class Collider; + ref class CollisionShape; + + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + /// + /// Struct that describes a collision + /// + public value struct CollisionInfo + { + public: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// The GameObject whose collider you are colliding with. + /// + property GameObject GameObject; + /// + /// The Collider that you are colliding with. + /// + property Collider^ Collider + { + SHADE::Collider^ get(); + } + /// + /// The CollisionShape of the Collider that you are colliding with. + /// + property CollisionShape^ CollisionShape + { + SHADE::CollisionShape^ get(); + } + /// + /// The RigidBody that you are colliding with. + /// + property RigidBody^ RigidBody + { + SHADE::RigidBody^ get(); + } + }; +} diff --git a/SHADE_Managed/src/Scripts/Script.cxx b/SHADE_Managed/src/Scripts/Script.cxx index ecd27325..e476d69d 100644 --- a/SHADE_Managed/src/Scripts/Script.cxx +++ b/SHADE_Managed/src/Scripts/Script.cxx @@ -18,6 +18,8 @@ of DigiPen Institute of Technology is prohibited. #include "Script.hxx" // Project Headers #include "Utility/Debug.hxx" +#include "ScriptStore.hxx" +#include "Engine/ECS.hxx" namespace SHADE { @@ -49,8 +51,7 @@ namespace SHADE generic void Script::RemoveComponent() { - throw gcnew System::NotImplementedException; - //ECS::RemoveComponent(owner.GetNativeEntity()); + owner.RemoveComponent(); } /*---------------------------------------------------------------------------------*/ @@ -59,37 +60,37 @@ namespace SHADE generic T Script::AddScript() { - throw gcnew System::NotImplementedException; - //return ScriptStore::AddScript(owner.GetEntity()); + return ScriptStore::AddScript(owner.GetEntity()); } generic T Script::GetScript() { - throw gcnew System::NotImplementedException; - //return ScriptStore::GetScript(owner.GetEntity()); + return ScriptStore::GetScript(owner.GetEntity()); } generic T Script::GetScriptInChildren() { - throw gcnew System::NotImplementedException; - //return ScriptStore::GetScriptInChildren(owner.GetEntity()); + return ScriptStore::GetScriptInChildren(owner.GetEntity()); } generic System::Collections::Generic::IEnumerable^ Script::GetScripts() { - throw gcnew System::NotImplementedException; - //return ScriptStore::GetScripts(owner.GetEntity()); + return ScriptStore::GetScripts(owner.GetEntity()); } generic void Script::RemoveScript() { - throw gcnew System::NotImplementedException; - //ScriptStore::RemoveScript(owner.GetEntity()); + ScriptStore::RemoveScript(owner.GetEntity()); } - + + Script::operator bool(Script^ s) + { + return s != nullptr; + } + /*---------------------------------------------------------------------------------*/ /* "All-time" Lifecycle Functions */ /*---------------------------------------------------------------------------------*/ @@ -146,6 +147,48 @@ namespace SHADE SAFE_NATIVE_CALL_END(this) } + void Script::OnCollisionEnter(CollisionInfo collision) + { + SAFE_NATIVE_CALL_BEGIN + onCollisionEnter(collision); + SAFE_NATIVE_CALL_END(this) + } + + void Script::OnCollisionStay(CollisionInfo collision) + { + SAFE_NATIVE_CALL_BEGIN + onCollisionStay(collision); + SAFE_NATIVE_CALL_END(this) + } + + void Script::OnCollisionExit(CollisionInfo collision) + { + SAFE_NATIVE_CALL_BEGIN + onCollisionExit(collision); + SAFE_NATIVE_CALL_END(this) + } + + void Script::OnTriggerEnter(CollisionInfo collision) + { + SAFE_NATIVE_CALL_BEGIN + onTriggerEnter(collision); + SAFE_NATIVE_CALL_END(this) + } + + void Script::OnTriggerStay(CollisionInfo collision) + { + SAFE_NATIVE_CALL_BEGIN + onTriggerStay(collision); + SAFE_NATIVE_CALL_END(this) + } + + void Script::OnTriggerExit(CollisionInfo collision) + { + SAFE_NATIVE_CALL_BEGIN + onTriggerExit(collision); + SAFE_NATIVE_CALL_END(this) + } + /*---------------------------------------------------------------------------------*/ /* Constructors */ /*---------------------------------------------------------------------------------*/ @@ -168,4 +211,14 @@ namespace SHADE void Script::update() {} void Script::lateUpdate() {} void Script::onDestroy() {} -}// namespace PlushieAPI + + /*---------------------------------------------------------------------------------*/ + /* Virtual Event Functions */ + /*---------------------------------------------------------------------------------*/ + void Script::onTriggerEnter(CollisionInfo) {} + void Script::onTriggerStay(CollisionInfo) {} + void Script::onTriggerExit(CollisionInfo) {} + void Script::onCollisionEnter(CollisionInfo) {} + void Script::onCollisionStay(CollisionInfo) {} + void Script::onCollisionExit(CollisionInfo) {} +} diff --git a/SHADE_Managed/src/Scripts/Script.hxx b/SHADE_Managed/src/Scripts/Script.hxx index cef9f4cd..afeaa8a0 100644 --- a/SHADE_Managed/src/Scripts/Script.hxx +++ b/SHADE_Managed/src/Scripts/Script.hxx @@ -15,6 +15,7 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "Engine/GameObject.hxx" +#include "Physics/CollisionInfo.hxx" namespace SHADE { @@ -153,6 +154,15 @@ namespace SHADE generic where T : ref class, Script void RemoveScript(); + /*-----------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------*/ + /// + /// Implicit conversion operator to enable checking if a component is null. + /// + /// Component to check. + static operator bool(Script^ s); + internal: /*-----------------------------------------------------------------------------*/ /* "All-Time" Lifecycle Functions */ @@ -204,6 +214,46 @@ namespace SHADE /// void OnDestroy(); + /*-----------------------------------------------------------------------------*/ + /* Event Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Used to call onCollisionEnter(). This should be called when a collision is + /// detected between the attached GameObject and another GameObject. + /// + /// Information on the collision event. + void OnCollisionEnter(CollisionInfo collision); + /// + /// Used to call onCollisionStay(). This should be called when a collision is + /// persistent between the attached GameObject and another GameObject. + /// + /// Information on the collision event. + void OnCollisionStay(CollisionInfo collision); + /// + /// Used to call onCollisionExit(). This should be called when a collision ends + /// between the attached GameObject and another GameObject. + /// + /// Information on the collision event. + void OnCollisionExit(CollisionInfo collision); + /// + /// Used to call onTriggerEnter(). This should be called when a trigger-type + /// collision is detected between the attached GameObject and another GameObject. + /// + /// Information on the collision event. + void OnTriggerEnter(CollisionInfo collision); + /// + /// Used to call onTriggerStay(). This should be called when a trigger-type + /// collision is detected between the attached GameObject and another GameObject. + /// + /// Information on the collision event. + void OnTriggerStay(CollisionInfo collision); + /// + /// Used to call onTriggerExit(). This should be called when a trigger-type + /// collision is detected between the attached GameObject and another GameObject. + /// + /// Information on the collision event. + void OnTriggerExit(CollisionInfo collision); + protected: /*-----------------------------------------------------------------------------*/ /* Constructors */ @@ -264,6 +314,46 @@ namespace SHADE /// virtual void onDestroy(); + /*-----------------------------------------------------------------------------*/ + /* Virtual Event Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Called when the attached GameObject has a trigger Collider and collides with + /// another GameObject with a Collider in the first frame of collision. + /// + /// Information on the collision event. + virtual void onTriggerEnter(CollisionInfo info); + /// + /// Called when the attached GameObject has a trigger Collider and collides with + /// another GameObject with a Collider in subsequent frames of collision. + /// + /// Information on the collision event. + virtual void onTriggerStay(CollisionInfo info); + /// + /// Called when the attached GameObject has a trigger Collider and leaves a + /// collision with another GameObject with a Collider2D. + /// + /// Information on the collision event. + virtual void onTriggerExit(CollisionInfo info); + /// + /// Called when the attached GameObject has a Collider and collides with + /// another GameObject with a Collider in the first frame of collision. + /// + /// Information on the collision event. + virtual void onCollisionEnter(CollisionInfo info); + /// + /// Called when the attached GameObject has a Collider and collides with + /// another GameObject with a Collider in subsequent frames of collision. + /// + /// Information on the collision event. + virtual void onCollisionStay(CollisionInfo info); + /// + /// Called when the attached GameObject has a Collider and leaves a + /// collision with another GameObject with a Collider2D. + /// + /// Information on the collision event. + virtual void onCollisionExit(CollisionInfo info); + private: /*-----------------------------------------------------------------------------*/ /* Data Members */ @@ -271,4 +361,4 @@ namespace SHADE GameObject owner; }; -} // namespace PlushieAPI +} diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index b6bc1815..407d0fa8 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -27,6 +27,9 @@ of DigiPen Institute of Technology is prohibited. #include "Script.hxx" #include "Engine/Entity.hxx" #include "Serialisation/ReflectionUtilities.hxx" +#include "Engine/Application.hxx" +#include "Physics/SHPhysicsSystemInterface.h" +#include "Physics/SHPhysicsUtils.h" namespace SHADE { @@ -70,7 +73,7 @@ namespace SHADE SAFE_NATIVE_CALL_BEGIN Script^ script; return AddScriptViaNameWithRef(entity, scriptName, script); - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") return false; } @@ -300,7 +303,7 @@ namespace SHADE removeScript(script); } scriptList->Clear(); - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } void ScriptStore::RemoveAllScriptsImmediately(Entity entity, bool callOnDestroy) { @@ -325,7 +328,7 @@ namespace SHADE startList.Remove(script); } scriptList->Clear(); - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } /*---------------------------------------------------------------------------------*/ @@ -364,7 +367,7 @@ namespace SHADE startList.AddRange(%inactiveStartList); inactiveStartList.Clear(); - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } void ScriptStore::FrameCleanUp() { @@ -372,8 +375,11 @@ namespace SHADE // Clear the queue while (disposalQueue.Count > 0) { - Script^ script = disposalQueue.Dequeue(); - script->OnDestroy(); + Script^ script = disposalQueue.Dequeue(); + if (Application::IsPlaying) + { + script->OnDestroy(); + } auto entity = script->Owner.GetEntity(); auto scriptList = scripts[script->Owner.GetEntity()]; scriptList->Remove(script); @@ -382,13 +388,13 @@ namespace SHADE scripts.Remove(entity); } } - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } void ScriptStore::Exit() { SAFE_NATIVE_CALL_BEGIN // Run the deinit all scripts if needed - //if (Application::IsPlaying) + if (Application::IsPlaying) { Debug::Log("Running OnDestroy() for scripts."); for each (System::Collections::Generic::KeyValuePair entity in scripts) @@ -406,7 +412,7 @@ namespace SHADE startList.Clear(); disposalQueue.Clear(); scriptTypeList = nullptr; - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } /*---------------------------------------------------------------------------------*/ @@ -435,7 +441,7 @@ namespace SHADE script->FixedUpdate(); } } - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } void ScriptStore::ExecuteUpdate() { @@ -452,7 +458,7 @@ namespace SHADE script->Update(); } } - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } void ScriptStore::ExecuteLateUpdate() { @@ -469,7 +475,95 @@ namespace SHADE script->LateUpdate(); } } - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") + } + + void ScriptStore::ExecuteCollisionFunctions() + { + SAFE_NATIVE_CALL_BEGIN + /* Collisions */ + const auto& collisions = SHPhysicsSystemInterface::GetCollisionInfo(); + for (const auto& collisionInfo : collisions) + { + auto entities = + { + std::make_pair(collisionInfo.GetEntityA(), collisionInfo.GetEntityB()), + std::make_pair(collisionInfo.GetEntityB(), collisionInfo.GetEntityA()) + }; + for (auto entity : entities) + { + // Don't bother if this object has no scripts or is inactive + if (!isEntityActive(entity.first) || !scripts.ContainsKey(entity.first)) + continue; + + // Construct the collision state object + CollisionInfo info; + info.GameObject = GameObject(entity.second); + + // Call all of the script's functions + auto entityScripts = scripts[entity.first]; + if (entityScripts->Count > 0) + { + for each (Script ^ script in entityScripts) + { + switch (collisionInfo.GetCollisionState()) + { + case SHCollisionEvent::State::ENTER: + script->OnCollisionEnter(info); + break; + case SHCollisionEvent::State::STAY: + script->OnCollisionStay(info); + break; + case SHCollisionEvent::State::EXIT: + script->OnCollisionExit(info); + break; + } + } + } + } + } + /* Triggers */ + const auto& triggers = SHPhysicsSystemInterface::GetTriggerInfo(); + for (const auto& triggerInfo : triggers) + { + auto entities = + { + std::make_pair(triggerInfo.GetEntityA(), triggerInfo.GetEntityB()), + std::make_pair(triggerInfo.GetEntityB(), triggerInfo.GetEntityA()) + }; + for (auto entity : entities) + { + // Don't bother if this object has no scripts or is inactive + if (!isEntityActive(entity.first) || !scripts.ContainsKey(entity.first)) + continue; + + // Construct the collision state object + CollisionInfo info; + info.GameObject = GameObject(entity.second); + + // Call all of the script's functions + auto entityScripts = scripts[entity.first]; + if (entityScripts->Count > 0) + { + for each (Script ^ script in entityScripts) + { + switch (triggerInfo.GetCollisionState()) + { + case SHCollisionEvent::State::ENTER: + script->OnTriggerEnter(info); + break; + case SHCollisionEvent::State::STAY: + script->OnTriggerStay(info); + break; + case SHCollisionEvent::State::EXIT: + script->OnTriggerExit(info); + break; + } + } + } + } + } + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } bool ScriptStore::SerialiseScripts(Entity entity, System::IntPtr yamlNodePtr) @@ -505,7 +599,7 @@ namespace SHADE } return true; - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") return false; } @@ -555,7 +649,7 @@ namespace SHADE } return true; - SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore") + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") return false; } @@ -663,14 +757,10 @@ namespace SHADE bool ScriptStore::isEntityActive(Entity entity) { - // Get native Entity - SHEntity* nativeEntity = SHEntityManager::GetEntityByID(entity); - - // Entity Validity Check - if (nativeEntity == nullptr) + // Invalid entity + if (!EntityUtils::IsValid(entity)) return false; - // Check active state - return nativeEntity->GetActive(); + return GameObject(entity).IsActiveInHierarchy; } } diff --git a/SHADE_Managed/src/Scripts/ScriptStore.hxx b/SHADE_Managed/src/Scripts/ScriptStore.hxx index 9aa66fcd..a4c6e824 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.hxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.hxx @@ -233,6 +233,10 @@ namespace SHADE /// Executes LateUpdate() for all scripts. /// static void ExecuteLateUpdate(); + /// + /// Executes OnCollision*() and OnTrigger*() for all scripts. + /// + static void ExecuteCollisionFunctions(); /*-----------------------------------------------------------------------------*/ /* Serialisation Functions */ diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx index f114d01b..4b4e44fc 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx @@ -22,6 +22,7 @@ of DigiPen Institute of Technology is prohibited. #include "Math/Vector2.hxx" #include "Math/Vector3.hxx" #include "Utility/Debug.hxx" +#include "Engine/GameObject.hxx" /*-------------------------------------------------------------------------------------*/ /* Macro Functions */ @@ -167,6 +168,11 @@ namespace SHADE fieldNode.push_back(vec.y); fieldNode.push_back(vec.z); } + else if (fieldInfo->FieldType == GameObject::typeid) + { + GameObject gameObj = safe_cast(fieldInfo->GetValue(object)); + fieldNode = gameObj.GetEntity(); + } else // Not any of the supported types { Debug::LogWarning(Convert::ToNative(System::String::Format @@ -242,6 +248,10 @@ namespace SHADE ); } } + else if (fieldInfo->FieldType == GameObject::typeid) + { + fieldInfo->SetValue(object, GameObject(node.as())); + } else // Not any of the supported types { Debug::LogWarning(Convert::ToNative(System::String::Format diff --git a/TempScriptsFolder/CameraControl.cs b/TempScriptsFolder/CameraControl.cs new file mode 100644 index 00000000..fc900f46 --- /dev/null +++ b/TempScriptsFolder/CameraControl.cs @@ -0,0 +1,22 @@ +using System; +using SHADE; + +namespace SHADE_Scripting +{ + public class CameraControl :Script + { + public float turnSpeed = 0.5f; + + public CameraControl(GameObject go) : base(go) { } + protected override void update() + { + //Camera + Camera cam = GetComponent(); + Vector2 mouseVel = Input.GetMouseVelocity(); + + cam.Pitch -= mouseVel.y * turnSpeed * (float)Time.DeltaTime; + cam.Yaw += mouseVel.x * turnSpeed * (float)Time.DeltaTime; + + } + } +} diff --git a/TempScriptsFolder/PhysicsTest.cs b/TempScriptsFolder/PhysicsTest.cs index add5971d..cc01615d 100644 --- a/TempScriptsFolder/PhysicsTest.cs +++ b/TempScriptsFolder/PhysicsTest.cs @@ -38,6 +38,35 @@ public class PhysicsTest : Script RigidBody.AddForce(Force); Debug.Log($"Jump!"); } - Debug.Log($"{Transform.LocalPosition.y}"); + } + + protected override void fixedUpdate() + { + Debug.Log("Fixed Update"); + } + + protected override void onCollisionEnter(CollisionInfo info) + { + Debug.Log($"Collision Enter: {info.GameObject.Name}"); + } + protected override void onCollisionStay(CollisionInfo info) + { + Debug.Log($"Collision Stay: {info.GameObject.Name}"); + } + protected override void onCollisionExit(CollisionInfo info) + { + Debug.Log($"Collision Exit: {info.GameObject.Name}"); + } + protected override void onTriggerEnter(CollisionInfo info) + { + Debug.Log($"Trigger Enter: {info.GameObject.Name}"); + } + protected override void onTriggerStay(CollisionInfo info) + { + Debug.Log($"Trigger Stay: {info.GameObject.Name}"); + } + protected override void onTriggerExit(CollisionInfo info) + { + Debug.Log($"Trigger Exit: {info.GameObject.Name}"); } } \ No newline at end of file diff --git a/TempScriptsFolder/PrintWhenActive.cs b/TempScriptsFolder/PrintWhenActive.cs new file mode 100644 index 00000000..41afdd58 --- /dev/null +++ b/TempScriptsFolder/PrintWhenActive.cs @@ -0,0 +1,11 @@ +using SHADE; + +public class PrintWhenActive : Script +{ + public PrintWhenActive(GameObject gameObj) : base(gameObj) { } + + protected override void update() + { + Debug.Log("Active!"); + } +} \ No newline at end of file diff --git a/TempScriptsFolder/ThirdPersonCamera.cs b/TempScriptsFolder/ThirdPersonCamera.cs new file mode 100644 index 00000000..e3b043bd --- /dev/null +++ b/TempScriptsFolder/ThirdPersonCamera.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SHADE; + + +namespace SHADE_Scripting +{ + public class ThirdPersonCamera: Script + { + + public float armLength = 4.0f; + public float turnSpeedPitch = 0.3f; + public float turnSpeedYaw = 0.5f; + public float pitchClamp = 45.0f; + public ThirdPersonCamera(GameObject go) : base(go) { } + + protected override void awake() + { + if(!GetComponent()) + { + AddComponent(); + } + if (!GetComponent()) + { + AddComponent(); + } + GetComponent().ArmLength = armLength; + } + + protected override void update() + { + CameraArm arm = GetComponent(); + if(arm) + { + Vector2 vel = Input.GetMouseVelocity(); + arm.Pitch -= vel.y * turnSpeedPitch * Time.DeltaTimeF; + arm.Yaw += vel.x * turnSpeedYaw * Time.DeltaTimeF; + + if(arm.Pitch > pitchClamp) + { + arm.Pitch = pitchClamp; + } + else if(arm.Pitch < -pitchClamp) + { + arm.Pitch = -pitchClamp; + } + + } + } + + } +} diff --git a/TempShaderFolder/DeferredCompositeCs.glsl b/TempShaderFolder/DeferredCompositeCs.glsl deleted file mode 100644 index c1caf0aa..00000000 --- a/TempShaderFolder/DeferredCompositeCs.glsl +++ /dev/null @@ -1,83 +0,0 @@ -#version 450 - -struct DirectionalLightStruct -{ - vec3 direction; - uint isActive; - uint cullingMask; - vec4 diffuseColor; -}; - -struct AmbientLightStruct -{ - vec4 ambientColor; - float strength; - uint isActive; - uint cullingMask; -}; - -layout(local_size_x = 16, local_size_y = 16) in; -layout(set = 4, binding = 0, rgba32f) uniform image2D positions; -layout(set = 4, binding = 1, rgba32f) uniform image2D normals; -layout(set = 4, binding = 2, rgba8) uniform image2D albedo; -layout(set = 4, binding = 3, r32ui) uniform uimage2D lightLayerData; -layout(set = 4, binding = 4, rgba8) uniform image2D targetImage; - -layout(set = 1, binding = 0) uniform LightCounts -{ - uint directionalLights; - uint pointLights; - uint spotLights; - uint ambientLights; - -} lightCounts; - -layout(std430, set = 1, binding = 1) buffer DirectionalLightData -{ - DirectionalLightStruct dLightData[]; -} DirLightData; - -layout(std430, set = 1, binding = 4) buffer AmbientLightData -{ - AmbientLightStruct aLightData[]; -} AmbLightData; - -void main() -{ - // convenient variables - ivec2 globalThread = ivec2(gl_GlobalInvocationID); - - // Get the diffuse color of the pixel - vec3 pixelDiffuse = imageLoad (albedo, globalThread).rgb; - - // Get position of fragment in world space - vec3 positionWorld = imageLoad (positions, globalThread).rgb; - - // normal of fragment - vec3 normalWorld = imageLoad(normals, globalThread).rgb; - - vec3 fragColor = vec3 (0.0f); - - for (int i = 0; i < lightCounts.directionalLights; ++i) - { - // get normalized direction of light - vec3 dLightNormalized = normalize (DirLightData.dLightData[i].direction); - - // Get diffuse strength - float diffuseStrength = max (0, dot (dLightNormalized, normalWorld)); - - // Calculate the fragment color - fragColor += DirLightData.dLightData[i].diffuseColor.rgb * diffuseStrength.rrr * pixelDiffuse; - } - - for (int i = 0; i < lightCounts.ambientLights; ++i) - { - // Just do some add - //fragColor += pixelDiffuse.rgb * AmbLightData.aLightData[i].ambientColor.rgb * vec3 (0.5f); - fragColor += pixelDiffuse.rgb * AmbLightData.aLightData[i].ambientColor.rgb * vec3 (AmbLightData.aLightData[i].strength); - } - - // store result into result image - imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(fragColor, 1.0f)); - -} \ No newline at end of file diff --git a/TempShaderFolder/DeferredCompositeCs.spv b/TempShaderFolder/DeferredCompositeCs.spv deleted file mode 100644 index 03ef7ac5b615ad4dd42a57f4cad2daf5876ceb6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4656 zcmZve33F6M5XT2Ln}i$W^Z+-+0|XJdM3EyVuml$pgRRMAbh0?@EGW8jzf0*+)^cT{ zJT_LY9G|Gp&Q5gFYe{_~iJSF_THLNz)A&jqzy824ZdQ}ITIf)eN0`5;+)I?Bk+deG z)-RX76?0QyI3dNw@dm|J8BHKl2$w0 zS8#j8<4i8PL*kXVF`W*#=9+0o74&DAVCP%aYDP)sLf&5eu!o+{w&G^$ErxRko!OSb z^6tz0x!GuDGOl-b1joi4s!@AjE~j^tO^t?OLco~x#LZP;1e z+9gvPI5o$eA%FW1acZv4B^r;$>8Nu#s~LZ4uI1Nd?Nd+t-3j3fYZ!^rNN|=5J1aj! zZsBfE3e&#qWabY(hHsunN2xa(>9q0`Tupd>PknAy@=(yfhiA@hGFyMUwQ_Fuaqf-< zmTWlZUf2)(Id|9(?u6M1^IQGI!p4e%lW{Ix33~!Gzw>$FY1D4i)6tozQP){}-2-6> zb5Mz{)!TXWvgVmLzfAOm2%Yz`Rn!yc1Clul_CfK`j*gv$o%a#i(XrE`ld6Q-X0eZD z7}NJuw%;(T&uYQX>RTt-{H(rB!pQfLIQ5l8 z_}Cm-eCh+U`k1G%K6L7{S(x|sP!H`t%g!u4Dk48KM?CXK$De#_bpp;XAPpUPy*#bn zMbbA)*B`kI!(NikEPz4yFU@fM(!!toV8q0qJ0%wQA+Iy@7Jrpwa^k;1zv*X5M6C7V z=oV{(-d4@}u*{Smf9&P7M5sIpFpGiwQ=|t>P?viHSZ4 zpk+Gmj¥*5B%csRCr(+Mm|)~V=M7n&Q(h(}{%6EF59hb@ zT##({c~LTZ$!qpW$%{nP$2+t8d01m1dso<~kN3knBOiaii3dh~=&8rB-|#Z^U}qNQ zMJ#6Ft~hy!VfS`VvU9hNqGS6}v|42PPm-yZdu7IXgZMz$n5kdH*^6Je$TJ|Kw)@_I zFSVgR@Yn%;JMd*jS@iEuAw_X~d6P>-^ctOa!vABTURt1UTcGoQl=(Azk6YFA+c#J-c%a3~2itxpDL?`E*VC3OV zKQ7`uqvOxtQG6z~pV`?W!Uudzonkhh6oEr5^3h-7(>LtI<8Gc3@wP4g(~`sZ&v+kj zi~p=-aEKMgFG2t-Rso9Llnl}DIFhhi~qc2aEKMg=U%aM7VdPnm$}cqqQ7*~ zed5dy{12V;fcQ%y7WRYU*x}U4eZ4Fq2KUJQPE)$IoN~GF%kH0@);6; zO~k%qqQB*XzF$Os@*!KkQOV>R(yg8ne_h14hWOYl5BFISVPCFF(NBx$3EwSj^n&>X zCq6#oA~4g>N~U-BRqHzOH$?b=FINp<=S0NCkMA}<#O5tvv)a!~w%RXBz9h2Rv0V`1 z%Y42m!fto_mdC(ZcDMLb!`mYAjr+5{<1ujPj_T)p?}~_p-Ol))Wais^-j|FYxcN*- zW^Qi#I3Gv`!`EgCj5%RuxHmZanUhGw*>7n-devi-;_Sx;7S18}kBx5#_lm79PHZ?c z+7O=>v9QPD*x{D@L&?Oj8NDnS%w{x^%#2$9nb8>$xXox&GW)=pQS>Ph`N@ZDy=qCO zpO$}4GWnTV?){1=6>%nPaD2Ja>~oLNb`m z1U9hn-sv~{H;La7&3k>B_?My;qA~UPuJ~6T1831YW}r{}YY}!k%Quoa3x9{`%eNvh pmSNvX1|yDP-+OGa__!i-hd+qek3aT%qN?&zFL|;5qbLVO{{oeIr1Jm( diff --git a/TempShaderFolder/TestCubeFs.spv b/TempShaderFolder/TestCubeFs.spv deleted file mode 100644 index 2381b8347c412e9f822330db717bd1f1e41c24b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2536 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 diff --git a/bin/Debug/SHADE_CSharp.xml b/bin/Debug/SHADE_CSharp.xml new file mode 100644 index 00000000..daeaa3c5 --- /dev/null +++ b/bin/Debug/SHADE_CSharp.xml @@ -0,0 +1,1029 @@ + + + + SHADE_CSharp + + + + + Interface for a CallbackAction that all variants inherit from. + + + + + Whether or not this CallbackAction is runtime assigned. If it is, then the + TargetMethodName and TargetObject properties are invalid. + + + + + Name of the method that this CallbackAction is using. + + + + + Object which the specified target method is called on. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 1 parameter. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 2 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 3 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 4 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 5 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 6 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 7 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 8 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 9 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 10 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Interface for a CallbackEvent that all variants inherit from. + + + + + Registers an empty ICallbackAction. + + + + + Registers an ICallbackAction with the event such that it will be called in + future + + ICallbackAction to register with. + + + + Deregisters an ICallbackAction that was previously added. This should + only emit a warning if an action that was not previous added was + provided. + + ICallbackAction to remove. + + + + Iterable set of ICallbackActions that were registered to this event. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + diff --git a/bin/Debug/SHADE_Managed.xml b/bin/Debug/SHADE_Managed.xml new file mode 100644 index 00000000..7b653116 --- /dev/null +++ b/bin/Debug/SHADE_Managed.xml @@ -0,0 +1,6250 @@ + + + + "SHADE_Managed" + + + + +Retrieves the duration that the specified key has not been held or was last +not been held for. + + The key to check. + Time in seconds that the key was held. + + + +Retrieves the duration that the specified key has been held or was last held +for. + + The key to check. + Time in seconds that the key was held. + + + +Retrieves the duration that the specified key has not been held or was last +not been held for. + + The key to check. + Time in seconds that the key was held. + + + +Retrieves the duration that the specified key has been held or was last held +for. + + The key to check. + Time in seconds that the key was held. + + + +Sets the position of the mouse cursor relative to the top left corner of the +window. + + +Position of the mouse in window pixel coordinates to set. + + + + +Checks if a specified mouse button is no longer pressed and was pressed +before. + + MouseCode of the mouse button to check. + +True during the frame the user releases the given mouse button. + + + + +Checks if a specified mouse button is pressed and was not pressed before. + + MouseCode of the mouse button to check. + +True during the frame the user pressed the given mouse button. + + + + +Checks if a specified mouse button is being held down. +This will also be true if GetMouseButtonDown() is true. + + MouseCode of the mouse button to check. + True while the user holds down the mouse button specified. + + + +Checks if a specified key is no longer pressed pressed and was pressed +before. + + KeyCode of the key to check. + +True during the frame the user releases the key identified by name. + + + + +Checks if a specified key is pressed and was not pressed before. + + KeyCode of the key to check. + +True during the frame the user starts pressing down the key specified. + + + + +Checks if a specified key is being held down. +This will also be true if GetKeyDown() is true. + + KeyCode of the key to check. + True while the user holds down the key specified. + + + +Amnount of vertical mouse scroll in this frame. + + + + +Mouse position in screen coordinates relative to the top left of the window. +This value is a Vector3 for compatibility with functions that have Vector3 +arguments. The z component of the Vector3 is always 0 + + + + +Represents the available supported mouse keycodes that can be passed into the +mouse-button-based Input functions. + + + + +Represents the available supported keycodes that can be passed into the +key-based Input functions. + +Attempting to follow https://docs.unity3d.com/ScriptReference/KeyCode.html +Win32 keycodes are shift-insensitive, i.e. 'A' and 'a' are the same keycode and '1' and '!' are the same keycode + + + + +Static class responsible for providing access to Input-related functionality. + + + + +Simple attribute to mark that a field in a Script should be serialised. + + + + +Cleans up all required components for managed code. + + + + +Reloads the managed script assembly. +Take note that this will clear all existing scripts, ensure that the scene +is saved before doing so. +Equivalent to calling UnloadScriptAssembly() and then LoadScriptAssembly(). + + + + +Loads the managed script assembly. Ensure this is only called after +UnloadScriptAssembly() has been called. + + + + +Unloads the managed script assembly. +Take note that this will clear all existing scripts, ensure that the scene +is saved before doing so. + + + + +Initialises all required components for managed code. + + + + +Name of the Managed Library that contains the C# scripts written externally. + + + + +Static class that contains the functions for interfacing with the core +PlushieEngine written in C++ for managing the lifecycle of managed code. + + + + +Default Constructor + + + + +Custom AssemblyLoadContext marked as collectible so that it can be unloaded. + + + + +Time taken for Physics simulations. You should use this for operations +within Script.FixedUpdate() + + + + +Time taken to process the previous frame. + + + + +Static class that contains the functions for working with time. + + + + +Static class that wraps up certain functions in the SHPhysicsSystem so that +accessing it from SHADE_Managed would not cause issues due to C++20 features. + + + + +Constructor for a Tooltip attribute that fills in the description. + + Text to be shown when a field is hovered. + + + +Description that is to be shown in the Tooltip. + + + + +Simple attribute to provide a field in a script with a tooltip. + + + + +Checks if a specified file exists. + + File path to the file to check. + True if the file exists + + + +Deletes the folder and all files in it as specified by the file path. + + File path to the file to delete. + + + +Deletes the file as specified by the file path. + + File path to the file to delete. + + + +Reads the file via the specified path that represents a build log of error +and warning messages. + + +File path to the build log of script builds done by BuildScriptAssembly() to +dump and process. + + + + +Registers events for the scripting system + + + + +Loads all the function pointers to CLR code that we need to execute. + + + + +Generates a .csproj file for editing and compiling the C# scripts. + + File path to the generated file. + + + +Utilises execution of a external batch file for invoking the dotnet build +tool to compile C# scripts in the Assets folder into the SHADE_Scripting +C# assembly DLL. + + +Whether or not a debug build will be built. Only debug built C# assemblies +can be debugged. + + +Whether or not we are reloading the assembly, if so, unload and then reload it. + + Whether or not the build succeeded. + + + +Performs a redo for script inspector changes if it exists. + + + + +Performs an undo for script inspector changes if it exists. + + + + +Renders the set of attached Scripts for the specified Entity into the +inspector. +
+This function is meant for consumption from native code in the inspector +rendering code. +
+ The Entity to render the Scripts of. +
+ + +Creates scripts and sets fields for the specified Entity based on the specified +YAML node. + + The Entity to deserialise a Script on to. + +YAML Node that contains the serialised script data. + + True if successfully deserialised. + + + +Performs serialization of all scripts for the specified entity into the +YAML::Node specified. This node will contain all serialised scripts after +calling this function. + + The Entity to Serialise. + +YAML Node that will store the serialised scripts. + + True if successfully serialised. + + + +Removes all Scripts attached to the specified Entity. Unlike +RemoveAllScripts(), this removes all the scripts immediately. +Does not do anything if the specified Entity is invalid or does not have any +Scripts attached. + + The entity to remove the scripts from. + +Whether or not to call OnDestroy on the scripts. This is ignored if not in +play mode. + + + + +Removes all Scripts attached to the specified Entity. Does not do anything +if the specified Entity is invalid or does not have any Scripts +attached. + + The entity to remove the scripts from. + + + +Adds a Script to a specified Entity. Note that while you can call this +multiple times on a specified Entity, it will work for all intents and +purposes but GetScript<T>() (C# only) currently only +gives you the first Script added of the specified type. + + The entity to add a script to. + Type name of the script to add. + +True if successfully added. False otherwise with the error logged to the +console. + + + + +Shuts down the DotNetRuntime. + + + + +Executes the OnCollision*()s and OnTrigger*()s of the Scripts that are attached +to Entities. + + + + +Executes the FixedUpdate()s of the Scripts that are attached to +Entities. + + + + +Reloads the managed script assembly. +Take note that this will clear all existing scripts, ensure that the scene +is saved before doing so. + + + + +Unloads the managed script assembly. +Take note that this will clear all existing scripts, ensure that the scene +is saved before doing so. + + + + +Loads the managed script assembly. Ensure this is only called after +UnloadScriptAssembly() has been called. + + + + +Initialises the DotNetRuntime and retrieves function pointers to all +functions on the CLR used to interface with the engine. + + + + +Manages initialisation of the DotNetRuntime and interfacing with CLR code written +and executed on .NET. + + + + +Deserialises a YAML node that contains a map of Scripts and copies the +deserialised data into the specified object if there are matching fields. + + +The JSON string that contains the data to copy into this Script object. + + The object to copy deserialised data into. + + + +Creates a JSON node that represents the specified object and its associated +serialisable fields. Public fields and fields marked with the SerialiseField +attribute will be serialised. + + The object to serialise. + + + +Checks if a specified field is a candidate for serialisation. This means that +the field is public or private with the [SerialiseField] attribute. + + The field to check. + +True if the specified field is a candidate for serialisation. + + + + +Retrieves a set of all non-static (instance) fields from a specified object. + + The object to get non-static fields from. + Immutable list of non-static fields. + + + +Contains useful static functions for working with Reflection. + + + +Converts the node to a YAML string. + + +Emits the node to the given output stream. + + +Emits the node to the given {@link Emitter}. If there is an error in writing, +{@link Emitter#good} will return false. + + + + Loads the input file as a list of YAML documents. + + @throws {@link ParserException} if it is malformed. + @throws {@link BadFile} if the file cannot be loaded. + + + + Loads the input stream as a list of YAML documents. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input string as a list of YAML documents. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input string as a list of YAML documents. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input file as a single YAML document. + + @throws {@link ParserException} if it is malformed. + @throws {@link BadFile} if the file cannot be loaded. + + + + Loads the input stream as a single YAML document. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input string as a single YAML document. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input string as a single YAML document. + + @throws {@link ParserException} if it is malformed. + + + +Handles a "TAG" directive, which should be of the form 'handle prefix', +where 'handle' is converted to 'prefix' in the file. + + + +Handles a "YAML" directive, which should be of the form 'major.minor' (like +a version number). + + + +Reads any directives that are next in the queue, setting the internal +{@code m_pDirectives} state. + + + + Handles the next document by calling events on the {@code eventHandler}. + + @throw a ParserException on error. + @return false if there are no more documents + + + +Resets the parser with the given input stream. Any existing state is +erased. + + + +Evaluates to true if the parser has some valid input to be read. + + +Constructs a parser from the given input stream. The input stream must +live as long as the parser. + + + +Constructs an empty parser (with no input. + + +A parser turns a stream of bytes into one stream of "events" per YAML +document in the input stream. + + + + +Renders a context menu when right clicked for the scripts + + The Entity to render the Scripts of. + The Script to render the inspector for. + + + +Renders a field specified into the inspector. + + The field to render. + +The object that contains the data of the field to render. + + + + +Renders a single specified Script's inspector. + + The Entity to render the Scripts of. + The Script to render the inspector for. + +Indices used internally to differentiate each rendered Script +inspector. This is required to open and close each Script's inspector +independently from each other. + + + + +Redoes the last script inspector change if there is any. + + + + +Undoes the last script inspector change if there is any. + + + + +Renders a dropdown button that allows for the addition of PlushieScripts +onto the specified Entity. + + The Entity to add PlushieScripts to. + + + +Renders the set of attached Scripts for the specified Entity into the +inspector. +
+This function is meant for consumption from native code in the inspector +rendering code. +
+ The Entity to render the Scripts of. +
+ + +Static class for Editor-related functions + + + + +Processes a YAML node that contains a list of multiple scripts to be loaded +into the specified Entity. +

+This function should only be called from native unmanaged code. +
+ +The Entity to attach the deserialised Scripts to. + + +Pointer to the YAML::Node that contains serialized script data. + + +
+ + +Populates a YAML node with the scripts for a specified Entity. +

+This function should only be called from native unmanaged code. +
+ The Entity to Serialise. + +Pointer to a YAML::Node that will be populated with all of the serialised +scripts and their associated fields. + + +True if serialisation is successful. False if the buffer is too small for +the serialised output. + +
+ + +Executes OnCollision*() and OnTrigger*() for all scripts. + + + + +Executes LateUpdate() for all scripts. + + + + +Executes Update() for all scripts. + + + + +Executes FixedUpdate() for all scripts. + + + + +Retrieves a immutable list of available scripts that can be added. + + Immutable list of available scripts that can be added. + + + +Cleans up data stored in the ScriptStore to free up memory for garbage +collection. + + + + +Cleans up scripts that were marked for deletion. This calls the OnDestroy() +for these Scripts. + + + + +Sets up scripts that were marked for initialization. This calls the Awake() +and Start() for Scripts that have yet to have done so. + + + + +Initializes the ScriptStore to allocate and pre-populate reflection data. + + + + +Removes all Scripts attached to the specified Entity. Unlike +RemoveAllScripts(), this removes all the scripts immediately. +Does not do anything if the specified Entity is invalid or does not have any +Scripts attached. + + The entity to remove the scripts from. + +Whether or not to call OnDestroy on the scripts.This is ignored if not in +play mode. + + + + +Removes all Scripts attached to the specified Entity. Does not do anything +if the specified Entity is invalid or does not have any Scripts +attached. + + The entity to remove the scripts from. + + + +Removes a specific script from the + + The entity to remove the script from. + The script to remove. + True if successfully removed. False otherwise. + + + +Removes all Scripts of the specified type from the specified Entity. + + +Type of script to remove. +This needs to be a default constructable Script. + + The entity to remove the script from. + +If the specified Entity is invalid. + + + + +Retrieves an immutable list of all scripts attached to a specified Entity. + + +The entity which the scripts to retrieve are attached. + + +Immutable list of references to scripts attached to the specified Entity. +This can also be null if there are no scripts at all or an invalid Entity +was specified. + + + + +Retrieves a immutable list of scripts from the specified Entity that +matches the specified type. +
+Note that this function allocates. It should be used sparingly. +
+ +Type of scripts to get. +This needs to be a default constructable Script. + + +The entity which the scripts to retrieve are attached. + + +Immutable list of references to scripts of the specified type. + +
+ + +Retrieves the first Script from the specified Entity's children that matches +the specified type. + + +Type of script to get. +This needs to be a default constructable Script. + + +The entity which the script to retrieve is attached. + + +Reference to the script. This can be null if no script of the specified +type is attached. + + +If the specified Entity is invalid. + + + + +Retrieves the first Script from the specified Entity that matches the +specified type. + + +Type of script to get. +This needs to be a default constructable Script. + + +The entity which the script to retrieve is attached. + + +Reference to the script. This can be null if no script of the specified +type is attached. + + +If the specified Entity is invalid. + + + + +Adds a Script to a specified Entity. +
+This function is meant for consumption from native code or for serialisation +purposes. If you are writing in C# or C++/CLI and not doing serialisation, +use AddScript<T>() instead as it is faster. +
+ The entity to add a script to. + The entity to add a script to. + +Out parameter handle to the Script that was created. + + +True if successfully added. False otherwise with the error logged to the +console. + +
+ + +Adds a Script to a specified Entity. +
+This function is meant for consumption from native code. If you are writing +in C# or C++/CLI, use AddScript<T>() instead as it is faster. +
+ The entity to add a script to. + The entity to add a script to. + +True if successfully added. False otherwise with the error logged to the +console. + +
+ + +Adds a Script to a specified Entity. + + +Type of script to add. +This needs to be a default constructable PlushieScript. + + The entity to add a script to. + Reference to the script added. + +If the specified Entity is invalid. + + + + +Responsible for managing all scripts attached to Entities as well as executing +all lifecycle functions of scripts. + + + + +Checks if two Colors are not approximately equal. + + Color to compare. + Another Color to compare. + +True if all components are not approximately equal within the default +tolerance value. + + + + +Checks if two Colors are approximately equal. + + Color to compare. + Another Color to compare. + +True if all components are approximately equal within the default +tolerance value. + + + + +Calculates the division of a Color with a scalar value and returns +the result. + + Scalar to divide with. + Color to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Color with a scalar value and returns +the result. + + Color to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the component-wise multiplication of two Colors and returns the +result. + + Color to multiply with. + Another Color to multiply with. + The result of rhs subtracted from lhs. + + + +Subtracts a Color from another Color and returns the result. + + Color to subtract from. + Another Color to subtract. + The result of rhs subtracted from lhs. + + + +Adds two Colors together and returns the result. + + Color to add. + Another Color to add. + The result of lhs added to rhs + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. +Unlike Lerp(), t is not clamped to a range at all. + + The start Color, returned when t = 0.0. + The end Color, returned when t = 1.0. + Value used to interpolate between a and b. + The interpolated Color. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. + + The start Color, returned when t = 0.0. + The end Color, returned when t = 1.0. + +Value used to interpolate between a and b which is clamped to +the range[0, 1]. + + The interpolated Vector3. + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Alpha component of the colour. Ranges from 0.0f to 1.0f. + + + + +Blue component of the colour. Ranges from 0.0f to 1.0f. + + + + +Green component of the colour. Ranges from 0.0f to 1.0f. + + + + +Red component of the colour. Ranges from 0.0f to 1.0f. + + + + +Constructor to construct a Color with the specified components. + + Red component to set. + Green component to set. + Blue component to set. + Alpha component to set. + + + +Constructor to construct a Color with the specified components with the +alpha component set to 1.0f. + + Red component to set. + Green component to set. + Blue component to set. + + + +Constructor to construct a Color with the specified components with the +blue and alpha component set to 1.0f. + + Red component to set. + Green component to set. + + + +Constructor to construct a Color with the specified components with the +green, blue and alpha component set to 1.0f. + + Red component to set. + + + +Pure yellow, mix of pure red and green. + + + + +Pure magenta, mix of pure red and blue. + + + + +Pure cyan, mix of pure green and blue. + + + + +Pure blue. + + + + +Pure green. + + + + +Pure red. + + + + +Pure white. + + + + +Dark Gray, darker than gray. + + + + +Gray, halfway between black and white. + + + + +Light Gray, lighter than gray. + + + + +Pure black. + + + + +A static class that contains a set of default Colors. + + + + +CLR version of the the SHADE Engine's Color struct which describes a Color +encoded using floating point numbers that range from 0.0f to 1.0f. + + + + +Creates a inline button widget. +
+Wraps up ImGui::Button(). +
+ Text to display. + True if button was pressed. +
+ + +Creates a small inline button widget. +
+Wraps up ImGui::SmallButton(). +
+ Text to display. + True if button was pressed. +
+ + +Creates a visual text widget. +
+Wraps up ImGui::Text(). +
+ Text to display. +
+ + +Creates a menu item in the list of items for a mini popup. +
+Wraps up ImGui::MenuItem(). +
+ Label used to identify this widget. + Whether or not the menu item was selected. +
+ + +Opens the popup that was defined with the specified label. +
+Wraps up ImGui::OpenPopup(). +
+
+ + +Marks the end of a definition of a mini pop up that can show options. +
+Wraps up ImGui::EndPopup(). +
+
+ + +Marks the start of a definition of a mini pop up that can show options. +
+Wraps up ImGui::BeginPopup(). +
+ Label used to identify this widget. + Whether or not the pop up is open. +
+ + +Creates a collapsing title header. +
+Wraps up ImGui::CollapsingHeader(). +
+ Label for the header. + True if the header is open, false otherwise. +
+ + +Unindents the widgets rendered after this call. +
+Wraps up ImGui::Unindent(). +
+
+ + +Indents the widgets rendered after this call. +
+Wraps up ImGui::Indent(). +
+
+ + +Marks the end of a stack of ImGui widgets from the last PushID() call. +
+Wraps up ImGui::PopID(). +
+
+ + +Marks the start of a stack of ImGui widgets with the specified id. +
+Wraps up ImGui::PushID(). +
+ Integer-based ID. +
+ + +Marks the start of a stack of ImGui widgets with the specified id. +
+Wraps up ImGui::PushID(). +
+ String-based ID. +
+ + +Maximum length of a string supported by InputTextField() + + + + +Static class that contains useful functions for Editor UI using ImGui. + + + + +Redoes the last undo-ed command if it exists. + + + + +Undos the last added command if it exists. + + + + +Adds a command onto the stack. + + + + + +True if there is a redoable action in the stack. + + + + +True if there is an undoable action in the stack. + + + + +Command for the stack that represents a data modification. + + + + +Class that is able to store a stack of actions that can be done and redone. + + + + +To be called from native code when a Collision Shape has been changed. + + +The entity which has it's collision shape changed. + + + + +To be called from native code when a collision shape has been removed. + + The entity which has it's collision shape removed. + + + +Retrieves a ColliderBound at the specified index in the ColliderBound list +and casts it to the appropriate type. + + Type of the ColliderBound to cast to. + Index to retrieve a ColliderBound from. + ColliderBound for the specified index. + + + +Retrieves a ColliderBound at the specified index in the ColliderBound list. + + Index to retrieve a ColliderBound from. + ColliderBound for the specified index. + + + +Total number of ColliderShapes in the Collider component. + + + + +Constructs a Collider Component that represents a native SHColliderComponent +component tied to the specified Entity. + + Entity that this Component will be tied to. + + + +CLR version of the the SHADE Engine's SHColliderComponent. +A single Collider component can contain one or multiple Collider Bounds. + + + + + + + + + + +Radius of the Bounding Sphere formed by this bound. + + + + +Center of the Bounding Sphere formed by this bound. + + + + +Sphere-shaped Collider Bound. + + + + + + + + + + +Position of the top right front corner of the Bounding Box formed by this +bound. + + + + +Position of the bottom left back corner of the Bounding Box formed by this +bound. + + + + +Half of the scale of the Bounding Box formed by this bound. + + + + +Center of the Bounding Box formed by this bound. + + + + +Box-shaped Collider Bound. + + + + +Computes a Raycast and checks if there is a collision with any object. + + The ray to cast. + Maximum distance for the raycast check. + True if the ray intersects with an object in the scene. + + + +Checks if the specified point is within this shape's bounds. + + Point to test with. + True if the point is in the shape's bounds. + + + +Base interface for all Collider Shapes. + + + +@brief The density of the collider that determines the mass of the collision shape + if it is automatically computed. Must be a positive number. + + + +@brief The bounciness factor of the physics object., clamped between [0,1].
+ 0 means the object will never bounce. + 1 means the object never loses energy on a bounce. + +
+ +@brief The friction coefficient of the physics object., clamped between [0,1].
+ 0 means the object will never experience friction. + 1 means the friction force against the object is equal to the applied force. + +
+ +@brief Sets the mass density of the physics material. +@param newDensity The density value to set. Always made positive. + + + +@brief Sets the bounciness factor of the physics material. +@param newBounciness The bounciness value to set. Clamped between [0,1]. + + + +@brief Sets the friction coefficient of the physics material. +@param newFriction The friction value to set. Clamped between [0,1]. + + + +@brief Default constructor for a physics material. +@param friction The friction of the material. Clamped between [0,1]. Defaults to 0.4. +@param bounciness The bounciness of the material. Clamped between [0,1]. +@param density The mass density of the material. Always made positive. + + + + +Closes the current window, and depending on the implementation, should also +close the application. + + + + +Retrieves the current window fullscreen status. + + The current window fullscreen status.. + + + +Retrieves the current window height. + + The current window height. + + + +Retrieves the current window width. + + The current window width. + + + +Static class that wraps up certain functions in the SHGraphicsSystem so that +accessing it from SHADE_Managed would not cause issues due to C++20 features. + + + + @brief Perform ImGui and ImGui Backend Render + + + + + @brief Start new frame for editor + + + + + @brief Initialise Backend for ImGui (SDL and Vulkan backend) + + @param sdlWindow Pointer to SDL_Window + + + + @brief Set the Style for the editor + + @param style Desired style + + + + @brief Safely shutdown the editor + + + + + @brief Update the editor and add to ImGui DrawList + + @param dt Delta-time of the frame + + + + @brief Initialise the editor + + @param sdlWindow pointer to SDL_Window object created in application + + + + @brief Style options + + + + + @brief SHEditor static class contains editor variables and implementation of editor functions. + + + + + Get the YUV conversion mode, returning the correct mode for the resolution + when the current conversion mode is SDL_YUV_CONVERSION_AUTOMATIC + + \since This function is available since SDL 2.0.8. + + + + Get the YUV conversion mode + + \since This function is available since SDL 2.0.8. + + + + Set the YUV conversion mode + + \since This function is available since SDL 2.0.8. + + + + Perform low-level surface scaled blitting only. + + This is a semi-private function and it performs low-level surface blitting, + assuming the input rectangles have already been clipped. + + \param src the SDL_Surface structure to be copied from + \param srcrect the SDL_Rect structure representing the rectangle to be + copied + \param dst the SDL_Surface structure that is the blit target + \param dstrect the SDL_Rect structure representing the rectangle that is + copied into + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitScaled + + + + Perform a scaled surface copy to a destination surface. + + SDL_UpperBlitScaled() has been replaced by SDL_BlitScaled(), which is + merely a macro for this function with a less confusing name. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitScaled + + + + Perform bilinear scaling between two surfaces of the same format, 32BPP. + + \since This function is available since SDL 2.0.16. + + + + Perform a fast, low quality, stretch blit between two surfaces of the same + format. + + Please use SDL_BlitScaled() instead. + + \since This function is available since SDL 2.0.0. + + + + Perform low-level surface blitting only. + + This is a semi-private blit function and it performs low-level surface + blitting, assuming the input rectangles have already been clipped. + + Unless you know what you're doing, you should be using SDL_BlitSurface() + instead. + + \param src the SDL_Surface structure to be copied from + \param srcrect the SDL_Rect structure representing the rectangle to be + copied, or NULL to copy the entire surface + \param dst the SDL_Surface structure that is the blit target + \param dstrect the SDL_Rect structure representing the rectangle that is + copied into + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + + + + * Performs a fast blit from the source surface to the destination surface. + * + * This assumes that the source and destination rectangles are + * the same size. If either \c srcrect or \c dstrect are NULL, the entire + * surface (\c src or \c dst) is copied. The final blit rectangles are saved + * in \c srcrect and \c dstrect after all clipping is performed. + * + * \returns 0 if the blit is successful, otherwise it returns -1. + * + * The blit function should not be called on a locked surface. + * + * The blit semantics for surfaces with and without blending and colorkey + * are defined as follows: + * \verbatim + RGBA->RGB: + Source surface blend mode set to SDL_BLENDMODE_BLEND: + alpha-blend (using the source alpha-channel and per-surface alpha) + SDL_SRCCOLORKEY ignored. + Source surface blend mode set to SDL_BLENDMODE_NONE: + copy RGB. + if SDL_SRCCOLORKEY set, only copy the pixels matching the + RGB values of the source color key, ignoring alpha in the + comparison. + + RGB->RGBA: + Source surface blend mode set to SDL_BLENDMODE_BLEND: + alpha-blend (using the source per-surface alpha) + Source surface blend mode set to SDL_BLENDMODE_NONE: + copy RGB, set destination alpha to source per-surface alpha value. + both: + if SDL_SRCCOLORKEY set, only copy the pixels matching the + source color key. + + RGBA->RGBA: + Source surface blend mode set to SDL_BLENDMODE_BLEND: + alpha-blend (using the source alpha-channel and per-surface alpha) + SDL_SRCCOLORKEY ignored. + Source surface blend mode set to SDL_BLENDMODE_NONE: + copy all of RGBA to the destination. + if SDL_SRCCOLORKEY set, only copy the pixels matching the + RGB values of the source color key, ignoring alpha in the + comparison. + + RGB->RGB: + Source surface blend mode set to SDL_BLENDMODE_BLEND: + alpha-blend (using the source per-surface alpha) + Source surface blend mode set to SDL_BLENDMODE_NONE: + copy RGB. + both: + if SDL_SRCCOLORKEY set, only copy the pixels matching the + source color key. + \endverbatim + * + * You should call SDL_BlitSurface() unless you know exactly how SDL + * blitting works internally and how to use the other blit functions. + + Perform a fast blit from the source surface to the destination surface. + + SDL_UpperBlit() has been replaced by SDL_BlitSurface(), which is merely a + macro for this function with a less confusing name. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + + + + Perform a fast fill of a set of rectangles with a specific color. + + `color` should be a pixel of the format used by the surface, and can be + generated by SDL_MapRGB() or SDL_MapRGBA(). If the color value contains an + alpha component then the destination is simply filled with that alpha + information, no blending takes place. + + If there is a clip rectangle set on the destination (set via + SDL_SetClipRect()), then this function will fill based on the intersection + of the clip rectangle and `rect`. + + \param dst the SDL_Surface structure that is the drawing target + \param rects an array of SDL_Rects representing the rectangles to fill. + \param count the number of rectangles in the array + \param color the color to fill with + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FillRect + + + + Perform a fast fill of a rectangle with a specific color. + + `color` should be a pixel of the format used by the surface, and can be + generated by SDL_MapRGB() or SDL_MapRGBA(). If the color value contains an + alpha component then the destination is simply filled with that alpha + information, no blending takes place. + + If there is a clip rectangle set on the destination (set via + SDL_SetClipRect()), then this function will fill based on the intersection + of the clip rectangle and `rect`. + + \param dst the SDL_Surface structure that is the drawing target + \param rect the SDL_Rect structure representing the rectangle to fill, or + NULL to fill the entire surface + \param color the color to fill with + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FillRects + + + + Premultiply the alpha on a block of pixels. + + This is safe to use with src == dst, but not for other overlapping areas. + + This function is currently only implemented for SDL_PIXELFORMAT_ARGB8888. + + \param width the width of the block to convert, in pixels + \param height the height of the block to convert, in pixels + \param src_format an SDL_PixelFormatEnum value of the `src` pixels format + \param src a pointer to the source pixels + \param src_pitch the pitch of the source pixels, in bytes + \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format + \param dst a pointer to be filled in with premultiplied pixel data + \param dst_pitch the pitch of the destination pixels, in bytes + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.18. + + + + Copy a block of pixels of one format to another format. + + \param width the width of the block to copy, in pixels + \param height the height of the block to copy, in pixels + \param src_format an SDL_PixelFormatEnum value of the `src` pixels format + \param src a pointer to the source pixels + \param src_pitch the pitch of the source pixels, in bytes + \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format + \param dst a pointer to be filled in with new pixel data + \param dst_pitch the pitch of the destination pixels, in bytes + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + + + Copy an existing surface to a new surface of the specified format enum. + + This function operates just like SDL_ConvertSurface(), but accepts an + SDL_PixelFormatEnum value instead of an SDL_PixelFormat structure. As such, + it might be easier to call but it doesn't have access to palette + information for the destination surface, in case that would be important. + + \param src the existing SDL_Surface structure to convert + \param pixel_format the SDL_PixelFormatEnum that the new surface is + optimized for + \param flags the flags are unused and should be set to 0; this is a + leftover from SDL 1.2's API + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocFormat + \sa SDL_ConvertSurface + \sa SDL_CreateRGBSurface + + + + Copy an existing surface to a new surface of the specified format. + + This function is used to optimize images for faster *repeat* blitting. This + is accomplished by converting the original and storing the result as a new + surface. The new, optimized surface can then be used as the source for + future blits, making them faster. + + \param src the existing SDL_Surface structure to convert + \param fmt the SDL_PixelFormat structure that the new surface is optimized + for + \param flags the flags are unused and should be set to 0; this is a + leftover from SDL 1.2's API + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocFormat + \sa SDL_ConvertSurfaceFormat + \sa SDL_CreateRGBSurface + + + + Get the clipping rectangle for a surface. + + When `surface` is the destination of a blit, only the area within the clip + rectangle is drawn into. + + \param surface the SDL_Surface structure representing the surface to be + clipped + \param rect an SDL_Rect structure filled in with the clipping rectangle for + the surface + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_SetClipRect + + + + Set the clipping rectangle for a surface. + + When `surface` is the destination of a blit, only the area within the clip + rectangle is drawn into. + + Note that blits are automatically clipped to the edges of the source and + destination surfaces. + + \param surface the SDL_Surface structure to be clipped + \param rect the SDL_Rect structure representing the clipping rectangle, or + NULL to disable clipping + \returns SDL_TRUE if the rectangle intersects the surface, otherwise + SDL_FALSE and blits will be completely clipped. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_GetClipRect + + + + Get the blend mode used for blit operations. + + \param surface the SDL_Surface structure to query + \param blendMode a pointer filled in with the current SDL_BlendMode + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_SetSurfaceBlendMode + + + + Set the blend mode used for blit operations. + + To copy a surface to another surface (or texture) without blending with the + existing data, the blendmode of the SOURCE surface should be set to + `SDL_BLENDMODE_NONE`. + + \param surface the SDL_Surface structure to update + \param blendMode the SDL_BlendMode to use for blit blending + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceBlendMode + + + + Get the additional alpha value used in blit operations. + + \param surface the SDL_Surface structure to query + \param alpha a pointer filled in with the current alpha value + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceColorMod + \sa SDL_SetSurfaceAlphaMod + + + + Set an additional alpha value used in blit operations. + + When this surface is blitted, during the blit operation the source alpha + value is modulated by this alpha value according to the following formula: + + `srcA = srcA * (alpha / 255)` + + \param surface the SDL_Surface structure to update + \param alpha the alpha value multiplied into blit operations + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceAlphaMod + \sa SDL_SetSurfaceColorMod + + + + Get the additional color value multiplied into blit operations. + + \param surface the SDL_Surface structure to query + \param r a pointer filled in with the current red color value + \param g a pointer filled in with the current green color value + \param b a pointer filled in with the current blue color value + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceAlphaMod + \sa SDL_SetSurfaceColorMod + + + + Set an additional color value multiplied into blit operations. + + When this surface is blitted, during the blit operation each source color + channel is modulated by the appropriate color value according to the + following formula: + + `srcC = srcC * (color / 255)` + + \param surface the SDL_Surface structure to update + \param r the red color value multiplied into blit operations + \param g the green color value multiplied into blit operations + \param b the blue color value multiplied into blit operations + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceColorMod + \sa SDL_SetSurfaceAlphaMod + + + + Get the color key (transparent pixel) for a surface. + + The color key is a pixel of the format used by the surface, as generated by + SDL_MapRGB(). + + If the surface doesn't have color key enabled this function returns -1. + + \param surface the SDL_Surface structure to query + \param key a pointer filled in with the transparent pixel + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_SetColorKey + + + + Returns whether the surface has a color key + + It is safe to pass a NULL `surface` here; it will return SDL_FALSE. + + \param surface the SDL_Surface structure to query + \return SDL_TRUE if the surface has a color key, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.9. + + \sa SDL_SetColorKey + \sa SDL_GetColorKey + + + + Set the color key (transparent pixel) in a surface. + + The color key defines a pixel value that will be treated as transparent in + a blit. For example, one can use this to specify that cyan pixels should be + considered transparent, and therefore not rendered. + + It is a pixel of the format used by the surface, as generated by + SDL_MapRGB(). + + RLE acceleration can substantially speed up blitting of images with large + horizontal runs of transparent pixels. See SDL_SetSurfaceRLE() for details. + + \param surface the SDL_Surface structure to update + \param flag SDL_TRUE to enable color key, SDL_FALSE to disable color key + \param key the transparent pixel + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_GetColorKey + + + + Returns whether the surface is RLE enabled + + It is safe to pass a NULL `surface` here; it will return SDL_FALSE. + + \param surface the SDL_Surface structure to query + \returns SDL_TRUE if the surface is RLE enabled, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.14. + + \sa SDL_SetSurfaceRLE + + + + Save a surface to a file. + + Convenience macro. + + Set the RLE acceleration hint for a surface. + + If RLE is enabled, color key and alpha blending blits are much faster, but + the surface must be locked before directly accessing the pixels. + + \param surface the SDL_Surface structure to optimize + \param flag 0 to disable, non-zero to enable RLE acceleration + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_LockSurface + \sa SDL_UnlockSurface + + + + Load a surface from a file. + + Convenience macro. + + Save a surface to a seekable SDL data stream in BMP format. + + Surfaces with a 24-bit, 32-bit and paletted 8-bit format get saved in the + BMP directly. Other RGB formats with 8-bit or higher get converted to a + 24-bit surface or, if they have an alpha mask or a colorkey, to a 32-bit + surface before they are saved. YUV and paletted 1-bit and 4-bit formats are + not supported. + + \param surface the SDL_Surface structure containing the image to be saved + \param dst a data stream to save to + \param freedst non-zero to close the stream after being written + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_LoadBMP_RW + \sa SDL_SaveBMP + + + + Load a BMP image from a seekable SDL data stream. + + The new surface should be freed with SDL_FreeSurface(). Not doing so will + result in a memory leak. + + src is an open SDL_RWops buffer, typically loaded with SDL_RWFromFile. + Alternitavely, you might also use the macro SDL_LoadBMP to load a bitmap + from a file, convert it to an SDL_Surface and then close the file. + + \param src the data stream for the surface + \param freesrc non-zero to close the stream after being read + \returns a pointer to a new SDL_Surface structure or NULL if there was an + error; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FreeSurface + \sa SDL_RWFromFile + \sa SDL_LoadBMP + \sa SDL_SaveBMP_RW + + + + Release a surface after directly accessing the pixels. + + \param surface the SDL_Surface structure to be unlocked + + \since This function is available since SDL 2.0.0. + + \sa SDL_LockSurface + + + + Set up a surface for directly accessing the pixels. + + Between calls to SDL_LockSurface() / SDL_UnlockSurface(), you can write to + and read from `surface->pixels`, using the pixel format stored in + `surface->format`. Once you are done accessing the surface, you should use + SDL_UnlockSurface() to release it. + + Not all surfaces require locking. If `SDL_MUSTLOCK(surface)` evaluates to + 0, then you can read and write to the surface at any time, and the pixel + format of the surface will not change. + + \param surface the SDL_Surface structure to be locked + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_MUSTLOCK + \sa SDL_UnlockSurface + + + + Set the palette used by a surface. + + A single palette can be shared with many surfaces. + + \param surface the SDL_Surface structure to update + \param palette the SDL_Palette structure to use + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + + + Free an RGB surface. + + It is safe to pass NULL to this function. + + \param surface the SDL_Surface to free. + + \since This function is available since SDL 2.0.0. + + \sa SDL_CreateRGBSurface + \sa SDL_CreateRGBSurfaceFrom + \sa SDL_LoadBMP + \sa SDL_LoadBMP_RW + + + + Allocate a new RGB surface with with a specific pixel format and existing + pixel data. + + This function operates mostly like SDL_CreateRGBSurfaceFrom(), except + instead of providing pixel color masks, you provide it with a predefined + format from SDL_PixelFormatEnum. + + No copy is made of the pixel data. Pixel data is not managed automatically; + you must free the surface before you free the pixel data. + + \param pixels a pointer to existing pixel data + \param width the width of the surface + \param height the height of the surface + \param depth the depth of the surface in bits + \param pitch the pitch of the surface in bytes + \param format the SDL_PixelFormatEnum for the new surface's pixel format. + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.5. + + \sa SDL_CreateRGBSurfaceFrom + \sa SDL_CreateRGBSurfaceWithFormat + \sa SDL_FreeSurface + + + + Allocate a new RGB surface with existing pixel data. + + This function operates mostly like SDL_CreateRGBSurface(), except it does + not allocate memory for the pixel data, instead the caller provides an + existing buffer of data for the surface to use. + + No copy is made of the pixel data. Pixel data is not managed automatically; + you must free the surface before you free the pixel data. + + \param pixels a pointer to existing pixel data + \param width the width of the surface + \param height the height of the surface + \param depth the depth of the surface in bits + \param pitch the pitch of the surface in bytes + \param Rmask the red mask for the pixels + \param Gmask the green mask for the pixels + \param Bmask the blue mask for the pixels + \param Amask the alpha mask for the pixels + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_CreateRGBSurface + \sa SDL_CreateRGBSurfaceWithFormat + \sa SDL_FreeSurface + + + + Allocate a new RGB surface with a specific pixel format. + + This function operates mostly like SDL_CreateRGBSurface(), except instead + of providing pixel color masks, you provide it with a predefined format + from SDL_PixelFormatEnum. + + \param flags the flags are unused and should be set to 0 + \param width the width of the surface + \param height the height of the surface + \param depth the depth of the surface in bits + \param format the SDL_PixelFormatEnum for the new surface's pixel format. + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.5. + + \sa SDL_CreateRGBSurface + \sa SDL_CreateRGBSurfaceFrom + \sa SDL_FreeSurface + + + + Allocate a new RGB surface. + + If `depth` is 4 or 8 bits, an empty palette is allocated for the surface. + If `depth` is greater than 8 bits, the pixel format is set using the + [RGBA]mask parameters. + + The [RGBA]mask parameters are the bitmasks used to extract that color from + a pixel. For instance, `Rmask` being 0xFF000000 means the red data is + stored in the most significant byte. Using zeros for the RGB masks sets a + default value, based on the depth. For example: + + ```c++ + SDL_CreateRGBSurface(0,w,h,32,0,0,0,0); + ``` + + However, using zero for the Amask results in an Amask of 0. + + By default surfaces with an alpha mask are set up for blending as with: + + ```c++ + SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND) + ``` + + You can change this by calling SDL_SetSurfaceBlendMode() and selecting a + different `blendMode`. + + \param flags the flags are unused and should be set to 0 + \param width the width of the surface + \param height the height of the surface + \param depth the depth of the surface in bits + \param Rmask the red mask for the pixels + \param Gmask the green mask for the pixels + \param Bmask the blue mask for the pixels + \param Amask the alpha mask for the pixels + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_CreateRGBSurfaceFrom + \sa SDL_CreateRGBSurfaceWithFormat + \sa SDL_FreeSurface + + + +Reference count -- used when freeing surface + + +info for fast blit mapping to other surfaces + + +clipping information + + +list of BlitMap that hold a reference to this surface + + +information needed for surfaces requiring locks + + +Application data associated with the surface + + + \brief A collection of pixels used in software blitting. + + \note This structure should be treated as read-only, except for \c pixels, + which, if not NULL, contains the raw pixel data for the surface. + + +\brief The type of function used for surface blitting functions. + + + + Compose a custom blend mode for renderers. + + The functions SDL_SetRenderDrawBlendMode and SDL_SetTextureBlendMode accept + the SDL_BlendMode returned by this function if the renderer supports it. + + A blend mode controls how the pixels from a drawing operation (source) get + combined with the pixels from the render target (destination). First, the + components of the source and destination pixels get multiplied with their + blend factors. Then, the blend operation takes the two products and + calculates the result that will get stored in the render target. + + Expressed in pseudocode, it would look like this: + + ```c + dstRGB = colorOperation(srcRGB * srcColorFactor, dstRGB * dstColorFactor); + dstA = alphaOperation(srcA * srcAlphaFactor, dstA * dstAlphaFactor); + ``` + + Where the functions `colorOperation(src, dst)` and `alphaOperation(src, + dst)` can return one of the following: + + - `src + dst` + - `src - dst` + - `dst - src` + - `min(src, dst)` + - `max(src, dst)` + + The red, green, and blue components are always multiplied with the first, + second, and third components of the SDL_BlendFactor, respectively. The + fourth component is not used. + + The alpha component is always multiplied with the fourth component of the + SDL_BlendFactor. The other components are not used in the alpha + calculation. + + Support for these blend modes varies for each renderer. To check if a + specific SDL_BlendMode is supported, create a renderer and pass it to + either SDL_SetRenderDrawBlendMode or SDL_SetTextureBlendMode. They will + return with an error if the blend mode is not supported. + + This list describes the support of custom blend modes for each renderer in + SDL 2.0.6. All renderers support the four blend modes listed in the + SDL_BlendMode enumeration. + + - **direct3d**: Supports all operations with all factors. However, some + factors produce unexpected results with `SDL_BLENDOPERATION_MINIMUM` and + `SDL_BLENDOPERATION_MAXIMUM`. + - **direct3d11**: Same as Direct3D 9. + - **opengl**: Supports the `SDL_BLENDOPERATION_ADD` operation with all + factors. OpenGL versions 1.1, 1.2, and 1.3 do not work correctly with SDL + 2.0.6. + - **opengles**: Supports the `SDL_BLENDOPERATION_ADD` operation with all + factors. Color and alpha factors need to be the same. OpenGL ES 1 + implementation specific: May also support `SDL_BLENDOPERATION_SUBTRACT` + and `SDL_BLENDOPERATION_REV_SUBTRACT`. May support color and alpha + operations being different from each other. May support color and alpha + factors being different from each other. + - **opengles2**: Supports the `SDL_BLENDOPERATION_ADD`, + `SDL_BLENDOPERATION_SUBTRACT`, `SDL_BLENDOPERATION_REV_SUBTRACT` + operations with all factors. + - **psp**: No custom blend mode support. + - **software**: No custom blend mode support. + + Some renderers do not provide an alpha component for the default render + target. The `SDL_BLENDFACTOR_DST_ALPHA` and + `SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA` factors do not have an effect in this + case. + + \param srcColorFactor the SDL_BlendFactor applied to the red, green, and + blue components of the source pixels + \param dstColorFactor the SDL_BlendFactor applied to the red, green, and + blue components of the destination pixels + \param colorOperation the SDL_BlendOperation used to combine the red, + green, and blue components of the source and + destination pixels + \param srcAlphaFactor the SDL_BlendFactor applied to the alpha component of + the source pixels + \param dstAlphaFactor the SDL_BlendFactor applied to the alpha component of + the destination pixels + \param alphaOperation the SDL_BlendOperation used to combine the alpha + component of the source and destination pixels + \returns an SDL_BlendMode that represents the chosen factors and + operations. + + \since This function is available since SDL 2.0.6. + + \sa SDL_SetRenderDrawBlendMode + \sa SDL_GetRenderDrawBlendMode + \sa SDL_SetTextureBlendMode + \sa SDL_GetTextureBlendMode + + + + Calculate the intersection of a rectangle and line segment with float + precision. + + This function is used to clip a line segment to a rectangle. A line segment + contained entirely within the rectangle or that does not intersect will + remain unchanged. A line segment that crosses the rectangle at either or + both ends will be clipped to the boundary of the rectangle and the new + coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary. + + \param rect an SDL_FRect structure representing the rectangle to intersect + \param X1 a pointer to the starting X-coordinate of the line + \param Y1 a pointer to the starting Y-coordinate of the line + \param X2 a pointer to the ending X-coordinate of the line + \param Y2 a pointer to the ending Y-coordinate of the line + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.22. + + + + Calculate a minimal rectangle enclosing a set of points with float + precision. + + If `clip` is not NULL then only points inside of the clipping rectangle are + considered. + + \param points an array of SDL_FPoint structures representing points to be + enclosed + \param count the number of structures in the `points` array + \param clip an SDL_FRect used for clipping or NULL to enclose all points + \param result an SDL_FRect structure filled in with the minimal enclosing + rectangle + \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the + points were outside of the clipping rectangle. + + \since This function is available since SDL 2.0.22. + + + + Calculate the union of two rectangles with float precision. + + \param A an SDL_FRect structure representing the first rectangle + \param B an SDL_FRect structure representing the second rectangle + \param result an SDL_FRect structure filled in with the union of rectangles + `A` and `B` + + \since This function is available since SDL 2.0.22. + + + + Calculate the intersection of two rectangles with float precision. + + If `result` is NULL then this function will return SDL_FALSE. + + \param A an SDL_FRect structure representing the first rectangle + \param B an SDL_FRect structure representing the second rectangle + \param result an SDL_FRect structure filled in with the intersection of + rectangles `A` and `B` + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.22. + + \sa SDL_HasIntersectionF + + + + Determine whether two rectangles intersect with float precision. + + If either pointer is NULL the function will return SDL_FALSE. + + \param A an SDL_FRect structure representing the first rectangle + \param B an SDL_FRect structure representing the second rectangle + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.22. + + \sa SDL_IntersectRect + + + + Returns true if the two rectangles are equal, using a default epsilon. + + \since This function is available since SDL 2.0.22. + + + + Returns true if the two rectangles are equal, within some given epsilon. + + \since This function is available since SDL 2.0.22. + + + +Returns true if the rectangle has no area. + + + +Returns true if point resides inside a rectangle. + + + + Calculate the intersection of a rectangle and line segment. + + This function is used to clip a line segment to a rectangle. A line segment + contained entirely within the rectangle or that does not intersect will + remain unchanged. A line segment that crosses the rectangle at either or + both ends will be clipped to the boundary of the rectangle and the new + coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary. + + \param rect an SDL_Rect structure representing the rectangle to intersect + \param X1 a pointer to the starting X-coordinate of the line + \param Y1 a pointer to the starting Y-coordinate of the line + \param X2 a pointer to the ending X-coordinate of the line + \param Y2 a pointer to the ending Y-coordinate of the line + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.0. + + + + Calculate a minimal rectangle enclosing a set of points. + + If `clip` is not NULL then only points inside of the clipping rectangle are + considered. + + \param points an array of SDL_Point structures representing points to be + enclosed + \param count the number of structures in the `points` array + \param clip an SDL_Rect used for clipping or NULL to enclose all points + \param result an SDL_Rect structure filled in with the minimal enclosing + rectangle + \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the + points were outside of the clipping rectangle. + + \since This function is available since SDL 2.0.0. + + + + Calculate the union of two rectangles. + + \param A an SDL_Rect structure representing the first rectangle + \param B an SDL_Rect structure representing the second rectangle + \param result an SDL_Rect structure filled in with the union of rectangles + `A` and `B` + + \since This function is available since SDL 2.0.0. + + + + Calculate the intersection of two rectangles. + + If `result` is NULL then this function will return SDL_FALSE. + + \param A an SDL_Rect structure representing the first rectangle + \param B an SDL_Rect structure representing the second rectangle + \param result an SDL_Rect structure filled in with the intersection of + rectangles `A` and `B` + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.0. + + \sa SDL_HasIntersection + + + + Determine whether two rectangles intersect. + + If either pointer is NULL the function will return SDL_FALSE. + + \param A an SDL_Rect structure representing the first rectangle + \param B an SDL_Rect structure representing the second rectangle + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.0. + + \sa SDL_IntersectRect + + + +Returns true if the two rectangles are equal. + + + +Returns true if the rectangle has no area. + + + +Returns true if point resides inside a rectangle. + + + + A rectangle, with the origin at the upper left (floating point). + + \sa SDL_FRectEmpty + \sa SDL_FRectEquals + \sa SDL_FRectEqualsEpsilon + \sa SDL_HasIntersectionF + \sa SDL_IntersectFRect + \sa SDL_IntersectFRectAndLine + \sa SDL_UnionFRect + \sa SDL_EncloseFPoints + \sa SDL_PointInFRect + + + + A rectangle, with the origin at the upper left (integer). + + \sa SDL_RectEmpty + \sa SDL_RectEquals + \sa SDL_HasIntersection + \sa SDL_IntersectRect + \sa SDL_IntersectRectAndLine + \sa SDL_UnionRect + \sa SDL_EnclosePoints + + + + The structure that defines a point (floating point) + + \sa SDL_EncloseFPoints + \sa SDL_PointInFRect + + + + Use this function to write 64 bits in native format to a SDL_RWops as + big-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in big-endian format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteLE64 + + + + Use this function to write 64 bits in native format to a SDL_RWops as + little-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in little-endian + format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteBE64 + + + + Use this function to write 32 bits in native format to a SDL_RWops as + big-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in big-endian format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteLE32 + + + + Use this function to write 32 bits in native format to a SDL_RWops as + little-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in little-endian + format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteBE32 + + + + Use this function to write 16 bits in native format to a SDL_RWops as + big-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in big-endian format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteLE16 + + + + Use this function to write 16 bits in native format to a SDL_RWops as + little-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in little-endian + format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteBE16 + + + + \name Write endian functions + + Write an item of native format to the specified endianness. + + Use this function to write a byte to an SDL_RWops. + + \param dst the SDL_RWops to write to + \param value the byte value to write + \returns 1 on success or 0 on failure; call SDL_GetError() for more + information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadU8 + + + + Use this function to read 64 bits of big-endian data from an SDL_RWops and + return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 64 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadLE64 + + + + Use this function to read 64 bits of little-endian data from an SDL_RWops + and return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 64 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadBE64 + + + + Use this function to read 32 bits of big-endian data from an SDL_RWops and + return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 32 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadLE32 + + + + Use this function to read 32 bits of little-endian data from an SDL_RWops + and return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 32 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadBE32 + + + + Use this function to read 16 bits of big-endian data from an SDL_RWops and + return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 16 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadLE16 + + + + Use this function to read 16 bits of little-endian data from an SDL_RWops + and return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 16 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadBE16 + + + + \name Read endian functions + + Read an item of the specified endianness and return in native format. + + Use this function to read a byte from an SDL_RWops. + + \param src the SDL_RWops to read from + \returns the read byte on success or 0 on failure; call SDL_GetError() for + more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteU8 + + + + Load all the data from a file path. + + The data is allocated with a zero byte at the end (null terminated) for + convenience. This extra byte is not included in the value reported via + `datasize`. + + The data should be freed with SDL_free(). + + Prior to SDL 2.0.10, this function was a macro wrapping around + SDL_LoadFile_RW. + + \param file the path to read all available data from + \param datasize if not NULL, will store the number of bytes read + \returns the data, or NULL if there was an error. + + \since This function is available since SDL 2.0.10. + + + + Load all the data from an SDL data stream. + + The data is allocated with a zero byte at the end (null terminated) for + convenience. This extra byte is not included in the value reported via + `datasize`. + + The data should be freed with SDL_free(). + + \param src the SDL_RWops to read all available data from + \param datasize if not NULL, will store the number of bytes read + \param freesrc if non-zero, calls SDL_RWclose() on `src` before returning + \returns the data, or NULL if there was an error. + + \since This function is available since SDL 2.0.6. + + + + Close and free an allocated SDL_RWops structure. + + SDL_RWclose() closes and cleans up the SDL_RWops stream. It releases any + resources used by the stream and frees the SDL_RWops itself with + SDL_FreeRW(). This returns 0 on success, or -1 if the stream failed to + flush to its output (e.g. to disk). + + Note that if this fails to flush the stream to disk, this function reports + an error, but the SDL_RWops is still invalid once this function returns. + + Prior to SDL 2.0.10, this function was a macro. + + \param context SDL_RWops structure to close + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWwrite + + + + Write to an SDL_RWops data stream. + + This function writes exactly `num` objects each of size `size` from the + area pointed at by `ptr` to the stream. If this fails for any reason, it'll + return less than `num` to demonstrate how far the write progressed. On + success, it returns `num`. + + SDL_RWwrite is actually a function wrapper that calls the SDL_RWops's + `write` method appropriately, to simplify application development. + + Prior to SDL 2.0.10, this function was a macro. + + \param context a pointer to an SDL_RWops structure + \param ptr a pointer to a buffer containing data to write + \param size the size of an object to write, in bytes + \param num the number of objects to write + \returns the number of objects written, which will be less than **num** on + error; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + + + + Read from a data source. + + This function reads up to `maxnum` objects each of size `size` from the + data source to the area pointed at by `ptr`. This function may read less + objects than requested. It will return zero when there has been an error or + the data stream is completely read. + + SDL_RWread() is actually a function wrapper that calls the SDL_RWops's + `read` method appropriately, to simplify application development. + + Prior to SDL 2.0.10, this function was a macro. + + \param context a pointer to an SDL_RWops structure + \param ptr a pointer to a buffer to read data into + \param size the size of each object to read, in bytes + \param maxnum the maximum number of objects to be read + \returns the number of objects read, or 0 at error or end of file; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWseek + \sa SDL_RWwrite + + + + Determine the current read/write offset in an SDL_RWops data stream. + + SDL_RWtell is actually a wrapper function that calls the SDL_RWops's `seek` + method, with an offset of 0 bytes from `RW_SEEK_CUR`, to simplify + application development. + + Prior to SDL 2.0.10, this function was a macro. + + \param context a SDL_RWops data stream object from which to get the current + offset + \returns the current offset in the stream, or -1 if the information can not + be determined. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWwrite + + + + Seek within an SDL_RWops data stream. + + This function seeks to byte `offset`, relative to `whence`. + + `whence` may be any of the following values: + + - `RW_SEEK_SET`: seek from the beginning of data + - `RW_SEEK_CUR`: seek relative to current read point + - `RW_SEEK_END`: seek relative to the end of data + + If this stream can not seek, it will return -1. + + SDL_RWseek() is actually a wrapper function that calls the SDL_RWops's + `seek` method appropriately, to simplify application development. + + Prior to SDL 2.0.10, this function was a macro. + + \param context a pointer to an SDL_RWops structure + \param offset an offset in bytes, relative to **whence** location; can be + negative + \param whence any of `RW_SEEK_SET`, `RW_SEEK_CUR`, `RW_SEEK_END` + \returns the final offset in the data stream after the seek or -1 on error. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWtell + \sa SDL_RWwrite + + + + Use this function to get the size of the data stream in an SDL_RWops. + + Prior to SDL 2.0.10, this function was a macro. + + \param context the SDL_RWops to get the size of the data stream from + \returns the size of the data stream in the SDL_RWops on success, -1 if + unknown or a negative error code on failure; call SDL_GetError() + for more information. + + \since This function is available since SDL 2.0.10. + + + + Use this function to free an SDL_RWops structure allocated by + SDL_AllocRW(). + + Applications do not need to use this function unless they are providing + their own SDL_RWops implementation. If you just need a SDL_RWops to + read/write a common data source, you should use the built-in + implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc, and + call the **close** method on those SDL_RWops pointers when you are done + with them. + + Only use SDL_FreeRW() on pointers returned by SDL_AllocRW(). The pointer is + invalid as soon as this function returns. Any extra memory allocated during + creation of the SDL_RWops is not freed by SDL_FreeRW(); the programmer must + be responsible for managing that memory in their **close** method. + + \param area the SDL_RWops structure to be freed + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocRW + + + + Use this function to allocate an empty, unpopulated SDL_RWops structure. + + Applications do not need to use this function unless they are providing + their own SDL_RWops implementation. If you just need a SDL_RWops to + read/write a common data source, you should use the built-in + implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc. + + You must free the returned pointer with SDL_FreeRW(). Depending on your + operating system and compiler, there may be a difference between the + malloc() and free() your program uses and the versions SDL calls + internally. Trying to mix the two can cause crashing such as segmentation + faults. Since all SDL_RWops must free themselves when their **close** + method is called, all SDL_RWops must be allocated through this function, so + they can all be freed correctly with SDL_FreeRW(). + + \returns a pointer to the allocated memory on success, or NULL on failure; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FreeRW + + + + Use this function to prepare a read-only memory buffer for use with RWops. + + This function sets up an SDL_RWops struct based on a memory area of a + certain size. It assumes the memory area is not writable. + + Attempting to write to this RWops stream will report an error without + writing to the memory buffer. + + This memory buffer is not copied by the RWops; the pointer you provide must + remain valid until you close the stream. Closing the stream will not free + the original buffer. + + If you need to write to a memory buffer, you should use SDL_RWFromMem() + with a writable buffer of memory instead. + + \param mem a pointer to a read-only buffer to feed an SDL_RWops stream + \param size the buffer size, in bytes + \returns a pointer to a new SDL_RWops structure, or NULL if it fails; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWtell + + + + Use this function to prepare a read-write memory buffer for use with + SDL_RWops. + + This function sets up an SDL_RWops struct based on a memory area of a + certain size, for both read and write access. + + This memory buffer is not copied by the RWops; the pointer you provide must + remain valid until you close the stream. Closing the stream will not free + the original buffer. + + If you need to make sure the RWops never writes to the memory buffer, you + should use SDL_RWFromConstMem() with a read-only buffer of memory instead. + + \param mem a pointer to a buffer to feed an SDL_RWops stream + \param size the buffer size, in bytes + \returns a pointer to a new SDL_RWops structure, or NULL if it fails; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWtell + \sa SDL_RWwrite + + + + Use this function to create an SDL_RWops structure from a standard I/O file + pointer (stdio.h's `FILE*`). + + This function is not available on Windows, since files opened in an + application on that platform cannot be used by a dynamically linked + library. + + On some platforms, the first parameter is a `void*`, on others, it's a + `FILE*`, depending on what system headers are available to SDL. It is + always intended to be the `FILE*` type from the C runtime's stdio.h. + + \param fp the `FILE*` that feeds the SDL_RWops stream + \param autoclose SDL_TRUE to close the `FILE*` when closing the SDL_RWops, + SDL_FALSE to leave the `FILE*` open when the RWops is + closed + \returns a pointer to the SDL_RWops structure that is created, or NULL on + failure; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWtell + \sa SDL_RWwrite + + + + \name RWFrom functions + + Functions to create SDL_RWops structures from various data streams. + + Use this function to create a new SDL_RWops structure for reading from + and/or writing to a named file. + + The `mode` string is treated roughly the same as in a call to the C + library's fopen(), even if SDL doesn't happen to use fopen() behind the + scenes. + + Available `mode` strings: + + - "r": Open a file for reading. The file must exist. + - "w": Create an empty file for writing. If a file with the same name + already exists its content is erased and the file is treated as a new + empty file. + - "a": Append to a file. Writing operations append data at the end of the + file. The file is created if it does not exist. + - "r+": Open a file for update both reading and writing. The file must + exist. + - "w+": Create an empty file for both reading and writing. If a file with + the same name already exists its content is erased and the file is + treated as a new empty file. + - "a+": Open a file for reading and appending. All writing operations are + performed at the end of the file, protecting the previous content to be + overwritten. You can reposition (fseek, rewind) the internal pointer to + anywhere in the file for reading, but writing operations will move it + back to the end of file. The file is created if it does not exist. + + **NOTE**: In order to open a file as a binary file, a "b" character has to + be included in the `mode` string. This additional "b" character can either + be appended at the end of the string (thus making the following compound + modes: "rb", "wb", "ab", "r+b", "w+b", "a+b") or be inserted between the + letter and the "+" sign for the mixed modes ("rb+", "wb+", "ab+"). + Additional characters may follow the sequence, although they should have no + effect. For example, "t" is sometimes appended to make explicit the file is + a text file. + + This function supports Unicode filenames, but they must be encoded in UTF-8 + format, regardless of the underlying operating system. + + As a fallback, SDL_RWFromFile() will transparently open a matching filename + in an Android app's `assets`. + + Closing the SDL_RWops will close the file handle SDL is holding internally. + + \param file a UTF-8 string representing the filename to open + \param mode an ASCII string representing the mode to be used for opening + the file. + \returns a pointer to the SDL_RWops structure that is created, or NULL on + failure; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWtell + \sa SDL_RWwrite + + + +Return the size of the file in this rwops, or -1 if unknown + + + Seek to \c offset relative to \c whence, one of stdio's whence values: + RW_SEEK_SET, RW_SEEK_CUR, RW_SEEK_END + + \return the final offset in the data stream, or -1 on error. + + + Read up to \c maxnum objects each of size \c size from the data + stream to the area pointed at by \c ptr. + + \return the number of objects read, or 0 at error or end of file. + + + Write exactly \c num objects each of size \c size from the area + pointed at by \c ptr to data stream. + + \return the number of objects written, or 0 at error or end of file. + + + Close and free an allocated SDL_RWops structure. + + \return 0 if successful or -1 on write error when flushing data. + + + + Clear any previous error message for this thread. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetError + \sa SDL_SetError + + + + Get the last error message that was set for the current thread. + + This allows the caller to copy the error string into a provided buffer, but + otherwise operates exactly the same as SDL_GetError(). + + \param errstr A buffer to fill with the last error message that was set for + the current thread + \param maxlen The size of the buffer pointed to by the errstr parameter + \returns the pointer passed in as the `errstr` parameter. + + \since This function is available since SDL 2.0.14. + + \sa SDL_GetError + + + + Retrieve a message about the last error that occurred on the current + thread. + + It is possible for multiple errors to occur before calling SDL_GetError(). + Only the last error is returned. + + The message is only applicable when an SDL function has signaled an error. + You must check the return values of SDL function calls to determine when to + appropriately call SDL_GetError(). You should *not* use the results of + SDL_GetError() to decide if an error has occurred! Sometimes SDL will set + an error string even when reporting success. + + SDL will *not* clear the error string for successful API calls. You *must* + check return values for failure cases before you can assume the error + string applies. + + Error strings are set per-thread, so an error set in a different thread + will not interfere with the current thread's operation. + + The returned string is internally allocated and must not be freed by the + application. + + \returns a message with information about the specific error that occurred, + or an empty string if there hasn't been an error message set since + the last call to SDL_ClearError(). The message is only applicable + when an SDL function has signaled an error. You must check the + return values of SDL function calls to determine when to + appropriately call SDL_GetError(). + + \since This function is available since SDL 2.0.0. + + \sa SDL_ClearError + \sa SDL_SetError + + + + Calculate a 256 entry gamma ramp for a gamma value. + + \param gamma a gamma value where 0.0 is black and 1.0 is identity + \param ramp an array of 256 values filled in with the gamma ramp + + \since This function is available since SDL 2.0.0. + + \sa SDL_SetWindowGammaRamp + + + + Get RGBA values from a pixel in the specified format. + + This function uses the entire 8-bit [0..255] range when converting color + components from pixel formats with less than 8-bits per RGB component + (e.g., a completely white pixel in 16-bit RGB565 format would return [0xff, + 0xff, 0xff] not [0xf8, 0xfc, 0xf8]). + + If the surface has no alpha component, the alpha will be returned as 0xff + (100% opaque). + + \param pixel a pixel value + \param format an SDL_PixelFormat structure describing the format of the + pixel + \param r a pointer filled in with the red component + \param g a pointer filled in with the green component + \param b a pointer filled in with the blue component + \param a a pointer filled in with the alpha component + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetRGB + \sa SDL_MapRGB + \sa SDL_MapRGBA + + + + Get RGB values from a pixel in the specified format. + + This function uses the entire 8-bit [0..255] range when converting color + components from pixel formats with less than 8-bits per RGB component + (e.g., a completely white pixel in 16-bit RGB565 format would return [0xff, + 0xff, 0xff] not [0xf8, 0xfc, 0xf8]). + + \param pixel a pixel value + \param format an SDL_PixelFormat structure describing the format of the + pixel + \param r a pointer filled in with the red component + \param g a pointer filled in with the green component + \param b a pointer filled in with the blue component + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetRGBA + \sa SDL_MapRGB + \sa SDL_MapRGBA + + + + Map an RGBA quadruple to a pixel value for a given pixel format. + + This function maps the RGBA color value to the specified pixel format and + returns the pixel value best approximating the given RGBA color value for + the given pixel format. + + If the specified pixel format has no alpha component the alpha value will + be ignored (as it will be in formats with a palette). + + If the format has a palette (8-bit) the index of the closest matching color + in the palette will be returned. + + If the pixel format bpp (color depth) is less than 32-bpp then the unused + upper bits of the return value can safely be ignored (e.g., with a 16-bpp + format the return value can be assigned to a Uint16, and similarly a Uint8 + for an 8-bpp format). + + \param format an SDL_PixelFormat structure describing the format of the + pixel + \param r the red component of the pixel in the range 0-255 + \param g the green component of the pixel in the range 0-255 + \param b the blue component of the pixel in the range 0-255 + \param a the alpha component of the pixel in the range 0-255 + \returns a pixel value + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetRGB + \sa SDL_GetRGBA + \sa SDL_MapRGB + + + + Map an RGB triple to an opaque pixel value for a given pixel format. + + This function maps the RGB color value to the specified pixel format and + returns the pixel value best approximating the given RGB color value for + the given pixel format. + + If the format has a palette (8-bit) the index of the closest matching color + in the palette will be returned. + + If the specified pixel format has an alpha component it will be returned as + all 1 bits (fully opaque). + + If the pixel format bpp (color depth) is less than 32-bpp then the unused + upper bits of the return value can safely be ignored (e.g., with a 16-bpp + format the return value can be assigned to a Uint16, and similarly a Uint8 + for an 8-bpp format). + + \param format an SDL_PixelFormat structure describing the pixel format + \param r the red component of the pixel in the range 0-255 + \param g the green component of the pixel in the range 0-255 + \param b the blue component of the pixel in the range 0-255 + \returns a pixel value + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetRGB + \sa SDL_GetRGBA + \sa SDL_MapRGBA + + + + Free a palette created with SDL_AllocPalette(). + + \param palette the SDL_Palette structure to be freed + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocPalette + + + + Set a range of colors in a palette. + + \param palette the SDL_Palette structure to modify + \param colors an array of SDL_Color structures to copy into the palette + \param firstcolor the index of the first palette entry to modify + \param ncolors the number of entries to modify + \returns 0 on success or a negative error code if not all of the colors + could be set; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocPalette + \sa SDL_CreateRGBSurface + + + + Set the palette for a pixel format structure. + + \param format the SDL_PixelFormat structure that will use the palette + \param palette the SDL_Palette structure that will be used + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocPalette + \sa SDL_FreePalette + + + + Create a palette structure with the specified number of color entries. + + The palette entries are initialized to white. + + \param ncolors represents the number of color entries in the color palette + \returns a new SDL_Palette structure on success or NULL on failure (e.g. if + there wasn't enough memory); call SDL_GetError() for more + information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FreePalette + + + + Free an SDL_PixelFormat structure allocated by SDL_AllocFormat(). + + \param format the SDL_PixelFormat structure to free + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocFormat + + + + Create an SDL_PixelFormat structure corresponding to a pixel format. + + Returned structure may come from a shared global cache (i.e. not newly + allocated), and hence should not be modified, especially the palette. Weird + errors such as `Blit combination not supported` may occur. + + \param pixel_format one of the SDL_PixelFormatEnum values + \returns the new SDL_PixelFormat structure or NULL on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FreeFormat + + + + Convert a bpp value and RGBA masks to an enumerated pixel format. + + This will return `SDL_PIXELFORMAT_UNKNOWN` if the conversion wasn't + possible. + + \param bpp a bits per pixel value; usually 15, 16, or 32 + \param Rmask the red mask for the format + \param Gmask the green mask for the format + \param Bmask the blue mask for the format + \param Amask the alpha mask for the format + \returns one of the SDL_PixelFormatEnum values + + \since This function is available since SDL 2.0.0. + + \sa SDL_PixelFormatEnumToMasks + + + + Convert one of the enumerated pixel formats to a bpp value and RGBA masks. + + \param format one of the SDL_PixelFormatEnum values + \param bpp a bits per pixel value; usually 15, 16, or 32 + \param Rmask a pointer filled in with the red mask for the format + \param Gmask a pointer filled in with the green mask for the format + \param Bmask a pointer filled in with the blue mask for the format + \param Amask a pointer filled in with the alpha mask for the format + \returns SDL_TRUE on success or SDL_FALSE if the conversion wasn't + possible; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_MasksToPixelFormatEnum + + + + Get the human readable name of a pixel format. + + \param format the pixel format to query + \returns the human readable name of the specified pixel format or + `SDL_PIXELFORMAT_UNKNOWN` if the format isn't recognized. + + \since This function is available since SDL 2.0.0. + + + +\note Everything in the pixel format structure is read-only. + + + +The bits of this structure can be directly reinterpreted as an integer-packed +color which uses the SDL_PIXELFORMAT_RGBA32 format (SDL_PIXELFORMAT_ABGR8888 +on little-endian systems and SDL_PIXELFORMAT_RGBA8888 on big-endian systems). + + + + If a + b would overflow, return -1. Otherwise store a + b via ret + and return 0. + + \since This function is available since SDL 2.24.0. + + + + If a * b would overflow, return -1. Otherwise store a * b via ret + and return 0. + + \since This function is available since SDL 2.24.0. + + + + This function converts a string between encodings in one pass, returning a + string that must be freed with SDL_free() or NULL on error. + + \since This function is available since SDL 2.0.0. + + + + Get the number of outstanding (unfreed) allocations + + \since This function is available since SDL 2.0.7. + + + + Replace SDL's memory allocation functions with a custom set + + \since This function is available since SDL 2.0.7. + + + + Get the current set of SDL memory functions + + \since This function is available since SDL 2.0.7. + + + + Get the original set of SDL memory functions + + \since This function is available since SDL 2.24.0. + + + +\endcond + \file begin_code.h + + This file sets things up for C dynamic library function definitions, + static inlined functions, and structures aligned at 4-byte alignment. + If you don't like ugly C preprocessor code, don't look at this file. :) + + + +\name Floating-point constants + +\cond + + +\brief An unsigned 64-bit integer type. + + + +\brief A signed 64-bit integer type. + + + +\brief An unsigned 32-bit integer type. + + + +\brief A signed 32-bit integer type. + + + +\brief An unsigned 16-bit integer type. + + + +\brief A signed 16-bit integer type. + + + +\brief An unsigned 8-bit integer type. + + + +\brief A signed 8-bit integer type. + + + + \file close_code.h + + This file reverses the effects of begin_code.h and should be included + after you finish any function and structure declarations in your headers + + + + \file SDL_stdinc.h + + This is a general header that includes C language support. + + \file SDL_platform.h + + Try to get a standard set of platform defines. + + \file begin_code.h + + This file sets things up for C dynamic library function definitions, + static inlined functions, and structures aligned at 4-byte alignment. + If you don't like ugly C preprocessor code, don't look at this file. :) + + Get the name of the platform. + + Here are the names returned for some (but not all) supported platforms: + + - "Windows" + - "Mac OS X" + - "Linux" + - "iOS" + - "Android" + + \returns the name of the platform. If the correct platform name is not + available, returns a string beginning with the text "Unknown". + + \since This function is available since SDL 2.0.0. + + + +*!************************************************************************* + + + +Marks the application to stop at the end of the current frame. + + + + +Whether or not the application is currently in fullscreen mode or not. + + + + +Retrieves the designated height of the current window. + + + + +Retrieves the designated width of the current window. + + + + +Whether or not the engine is in a paused state where script updates and +physics are not in play. + + + + +Whether or not the engine is playing. This will always be true on Publish. +On Debug/Release builds, this is true when the editor is in Play Mode. It +will also be true even if the editor is in Play Mode but is paused. + + + + +Static class that contains useful properties for querying the state of the +engine. + + + + +Sets the parent of this Transform component. + + +Entity that contains the Transform component that this Transform will be +parented to. If null, unparenting will occur. + + +If true, the transform values of this Transform component will retain their +pre-parent-change global transforms. The local transform values will be +modified to ensure that the global transforms do not change. + + + + +Parent Transform that affects this Transform. + + + + +Global scale stored by this Transform. + + + + +Global euler angle rotations stored by this Transform. + + + + +Global rotation quaternion stored by this Transform. + + + + +Global position stored by this Transform. + + + + +Local scale stored by this Transform. + + + + +Local euler angle rotations stored by this Transform. + + + + +Local rotation quaternion stored by this Transform. + + + + +Local position stored by this Transform. + + + + +Constructs a Transform Component that represents a native Transform component +tied to the specified Entity. + + Entity that this Component will be tied to. + + + +CLR version of the SHADE Engine's TransformComponent. + + + + +Compares if two float values are close enough to be the same with the +specified tolerance value. + + One of the values to compare. + The other value to compare. + Tolerance for floating point comparison. + True if a and b are practically the same. + + + +Compares if two float values are close enough to be the same with a tolerance +of Epsilon. + + One of the values to compare. + The other value to compare. + True if a and b are practically the same. + + + +Calculates the linear parameter t that produces the interpolant value within +the range [a, b]. + + Start value. + End value. + Value between start and end. + Percentage of value between start and end. + + + +Linearly interpolates between a and b by t. +The parameter t is not clamped and a value based on a and b is supported. +If t is less than zero, or greater than one, then LerpUnclamped will result +in a return value outside the range a to b. + + The start value. + The end value. + The interpolation value between the two float. + The interpolated float result between the two float values. + + + +Linearly interpolates between a and b by t. +The parameter t is clamped to the range [0, 1]. + + The start value. + The end value. + The interpolation value between the two float. + The interpolated float result between the two float values. + + + +Converts an angle from radian representation to degree representation. + + Radian-based angle to convert. + The specified angle in degrees. + + + +Converts an angle from degree representation to radian representation. + + Degree-based angle to convert. + The specified angle in radians. + + + +Wraps a value if they get to low or too high. + + Value to wrap. + Minimum value to wrap at. + Maximum value to wrap at. + Wrapped value. + + + +Small value used for single precision floating point comparisons. + + + + +Radians-to-degrees conversion constant + + + + +Degrees-to-radians conversion constant + + + + +Contains utility Math functions. + + + + +Logs a native exception that is formatted nicely to the output. + + Native exception to log. + Name of the one responsible for the exception. + + + +Logs an exception that is formatted nicely to the output. + + Name of the one responsible for the exception. + Exception to log. + + + +Logs a native exception that is formatted nicely to the output. +Equivalent to calling +LogException(exception, Convert::ToNative(thrower->GetType()->Name)); + + Native exception to log. + +Object that threw the exception to label the exception message. +The name of the object will be used. + + + + +Logs an exception that is formatted nicely to the output. + + Exception to log. + +Object that threw the exception to label the exception message. +The name of the object will be used. + + + + +Logs an exception that is formatted nicely to the output. + + Exception to log. + + + +Logs a error message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that threw the error to label the error message. +The name of the object will be used. + + + + +Logs a error message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that threw the error to label the error message. +The name of the object will be used. + + + + +Logs a error message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Object that threw the error to label the error message. +The name of the object will be used. + + + + +Logs a error message to the output. + + The string to output. + + + +Logs a error message to the output. + + The string to output. + + + +Logs a warning message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that threw the warning to label the warning message. +The name of the object will be used. + + + + +Logs a warning message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that threw the warning to label the warning message. +The name of the object will be used. + + + + +Logs a warning message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Object that threw the warning to label the warning message. +The name of the object will be used. + + + + +Logs a warning message to the output. + + The string to output. + + + +Logs a warning message to the output. + + The string to output. + + + +Logs a message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that sent the message to label the message. +The name of the object will be used. + + + + +Logs a message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that sent the message to label the message. +The name of the object will be used. + + + + +Logs a message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Object that sent the message to label the message. +The name of the object will be used. + + + + +Logs a message to the output. + + The string to output. + + + +Logs a message to the output. + + The string to output. + + + +Static class that contains the functions for working with time. + + + + +Material used to render this Renderable. + + + + +Material used to render this Renderable. + + + + +Mesh used to render this Renderable. + + + + +Constructs a Renderable Component that represents a native Renderable +component tied to the specified Entity. + + Entity that this Component will be tied to. + + + +CLR version of the SHADE Engine's SHRenderableComponent. + + + + +Retrieves the value of a specified property on the material. + + Type of property to get. + Name of the property to get. + Value of that property on the material. + +If this Material object is invalid. + + +If the name or type was specified that does not match the material's shader's +defined properties. + + + + +Set the value of a specific property. + + Type of property to set. + Name of the property to set. + Value to set te property to. + +If this Material object is invalid. + + +If the name or type was specified that does not match the material's shader's +defined properties. + + + + +Constructor for the Material + + Handle to the native material object. + + + +Managed counterpart of the native MaterialInstance object containing material +data that can be fed to Renderables for rendering. + + + + +Constructor for the Mesh + + Handle to the mesh object. + + + +Managed counterpart of the native Mesh object containing vertex data that can +be fed to Renderables for rendering. + + + +@brief Decomposes a transformation matrix into translation, orientation and scale. +@param[out] scale The scaling factor of the matrix. +@param[out] orientation The orientation of the matrix. +@param[out] translation The translation of the matrix. +@return True if decomposition was successful. + + + +@brief Decomposes a transformation matrix into translation, euler angles and scale. +@param[out] scale The scaling factor of the matrix. +@param[out] rotation The euler angles of the matrix. +@param[out] translation The translation of the matrix. +@return True if decomposition was successful. + + + +@brief Interface for a Column-Major Row Vector 4x4 Matrix. + + + + +Constructs a RigidBody Component that represents a native +SHRigidBodyComponent component tied to the specified Entity. + + Entity that this Component will be tied to. + + + +CLR version of the the SHADE Engine's SHRigidBodyComponent. + + + + +Creates an instance of the Managed representation of a Component with a +native Entity. + + Type of Component to create. + Native Entity that this Component is tied to. + The created Managed representation of the Component. + + + +Static constructor to initialize static data + + + + +Pointer to a function for Component manipulation operations. + + +Contains a set of Component related data used for resolving operations for +each Component. + + + + +Removes a Component from the specified Entity. + + Type of the Component to remove. + +Entity object that should have the specified Component removed from/ + + + + +Checks if the specified Entity has the specified Component. + + Type of the Component to check for. + Entity object to check for the Component. + +True if the specified Entity has the specified Component. False otherwise. + + + + +Ensures a Component on the specified Entity. + + Type of the Component to ensure. + Entity object to ensure the Component on. + Reference to the Component. + + + +Retrieves the first Component from the specified GameObjectt's children that +matches the specified type. + + Type of the Component to get. + Entity object to get the Component from. + +Reference to the Component or null if the Entity does not have the +specified Component. + + + + +Gets a Component from the specified Entity. + + Type of the Component to get. + Entity object to get the Component from. + +Reference to the Component or null if the Entity does not have the +specified Component. + + + + +Adds a Component to the specified Entity. + + Type of the Component to add. + +Entity object that should have the specified Component added to. + + Reference to the Component that was added. + + + +Static class which contains functions that map Pls::ECS's Component manipulation +functions to managed generic functions. + + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Entity that this Component belongs to. + + + + +Constructor for BaseComponent to tie it to a specific Entity. +Constructors of derived Components should call this Constructor. + + Entity that this Component will be tied to. + + + +Implicit conversion operator to enable checking if a component is null. + + Component to check. + + + +Removes all Scripts of the specified type from this GameObject. + + Type of PLushieScripts to remove. + + + +Retrieves a immutable list of Scripts of the specified type from this +GameObject. + + Type of Scripts to Get. + Immutable list of Scripts of the specified type. + + + +Retrieves a Script of the specified type from this GameObject. +If multiple Scripts of the same specified type are added on the same +GameObject, this will retrieve the first one added. + + Type of Script to add. + Reference to the Script to retrieve. + + + +Adds a Script of the specified type to this GameObject. + + Type of Script to add. + Reference to the created Script. + + + +Removes a Component from this GameObject. If no Component exists to begin +with, nothing happens. + + Type of the Component to get. + + + +Gets a Component from this GameObject. + + Type of the Component to get. + +Reference to the Component or null if this GameObject does not have the +specified Component. + + + + +Adds a Component to this GameObject. + + Type of the Component to add. + Reference to the Component that was added. + + + +Retrieves the GameObject that this Component belongs to. + + + + +Class that serves as the base for a wrapper class to Components in native code. + + + + +Called when the attached GameObject has a Collider and leaves a +collision with another GameObject with a Collider2D. + + Information on the collision event. + + + +Called when the attached GameObject has a Collider and collides with +another GameObject with a Collider in subsequent frames of collision. + + Information on the collision event. + + + +Called when the attached GameObject has a Collider and collides with +another GameObject with a Collider in the first frame of collision. + + Information on the collision event. + + + +Called when the attached GameObject has a trigger Collider and leaves a +collision with another GameObject with a Collider2D. + + Information on the collision event. + + + +Called when the attached GameObject has a trigger Collider and collides with +another GameObject with a Collider in subsequent frames of collision. + + Information on the collision event. + + + +Called when the attached GameObject has a trigger Collider and collides with +another GameObject with a Collider in the first frame of collision. + + Information on the collision event. + + + +Called just before the end of the frame where the attached GameObject or +this script is destroyed directly or indirectly due to destruction of the +owner. + + + + +Called every frame after physics and collision resolution but before +rendering. + + + + +Called every frame before physics and collision resolution. + + + + +Called every frame in sync with Physics update steps and thus in most cases +will execute more than update() will. This will be called immediately before +a Physics update step. + + + + +Called on the first frame that the attached GameObject is active but always +after Awake(). + + + + +Called on the first frame that the attached GameObject is active if they are +a part of the scene. + + + + +Called immediately once this script is detached from a GameObject. + + + + +Called immediately once this script is attached to a GameObject. + + + + +Constructor for Script to tie it to a specific GameObject. +Constructors of derived Scripts should call this Constructor. + + +GameObject that this Script will be tied to. + + + + +Used to call onTriggerExit(). This should be called when a trigger-type +collision is detected between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onTriggerStay(). This should be called when a trigger-type +collision is detected between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onTriggerEnter(). This should be called when a trigger-type +collision is detected between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onCollisionExit(). This should be called when a collision ends +between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onCollisionStay(). This should be called when a collision is +persistent between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onCollisionEnter(). This should be called when a collision is +detected between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onDestroy(). This should be called at the end of the frame +where the attached GameObject or this script is destroyed directly or +indirectly due to destruction of the owner. + + + + +Used to call lateUpdate(). This should be called every frame after physics +and collision resolution but before rendering. + + + + +Used to call update(). This should be called every frame before physics and +collision resolution. + + + + +Used to call fixedUpdate(). This should be called in sync with Physics +update steps and thus in most cases will execute more than Update() will. +This will be called immediately before a Physics update step. + + + + +Used to call start(). This should be called on the first frame that the +attached GameObject is active but always after Awake(). + + + + +Used to call awake(). This should be called on the first frame that the +attached GameObject is active if they are a part of the scene. + + + + +Used to call onDetached(). This is called immediately when this script is +detached from a GameObject. + + + + +Used to call onAttached(). This is called immediately when this script is +attached to a GameObject. + + + + +Implicit conversion operator to enable checking if a component is null. + + Component to check. + + + +Removes all Scripts of the specified type from this GameObject. + + +Type of script to remove. +This needs to be a default constructable Script. + + + + +Retrieves a immutable list of scripts from the specified Entity that +matches the specified type. +
+Note that this function allocates. It should be used sparingly. +
+ +Type of scripts to get. +This needs to be a default constructable Script. + + +Immutable list of references to scripts of the specified type. + +
+ + +Retrieves the first Script from this GameObject's children that matches the +specified type. + + +Type of script to get. +This needs to be a default constructable Script. + + Reference to the script added + + + +Retrieves the first Script from this GameObject that matches the specified +type. + + +Type of script to get. +This needs to be a default constructable Script. + + Reference to the script added + + + +Adds a Script to this GameObject. + + +Type of script to add. +This needs to be a default constructable Script. + + Reference to the script added + + + +Removes a Component from the GameObject that this Script belongs to. + + +Type of the Component to remove. Must be derived from BaseComponent. + + + + +Ensures a Component on the GameObject that this Script belongs to. + + +Type of the Component to ensure. Must be derived from BaseComponent. + + Reference to the Component. + + + +Retrieves the first Component from this GameObject's children that matches +the specified type. + + +Type of the Component to get. Must be derived from BaseComponent. + + Reference to the Component that was retrieved. + + + +Gets a Component from the GameObject that this Script belongs to. + + +Type of the Component to get. Must be derived from BaseComponent. + + Reference to the Component that was retrieved. + + + +Adds a Component to the GameObject that this Script belongs to. + + +Type of the Component to add. Must be derived from BaseComponent. + + Reference to the Component that was added. + + + +GameObject that this Script belongs to. + + + + +Class that forms the basis of all "script"-objects that can be attached to +Entities to update each Entity's Components via C# code. + + + + +The RigidBody that you are colliding with. + + + + +The CollisionShape of the Collider that you are colliding with. + + + + +The Collider that you are colliding with. + + + + +The GameObject whose collider you are colliding with. + + + + +Struct that describes a collision + + + + +Checks if two GameObject references are different. + + GameObject to check. + Another GameObject to check with. + True if both Components are different. + + + +Checks if two GameObject references are the same. + + GameObject to check. + Another GameObject to check with. + True if both Components are the same. + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Retrieves the native Entity object that this GameObject represents. + + Native Entity object that this GameObject represents. + + + +Retrieves the CLR Entity object that this GameObject represents. + + Entity object that this GameObject represents. + + + +Constructor for the GameObject. + + +Managed numerical representation of the ECS Entity that this GameObject +should represent. + + + + +Constructor for the GameObject. + + +The ECS Entity that this GameObject should represent. + + + + +Removes all Scripts of the specified type from this GameObject. + + Type of PLushieScripts to remove. + + + +Retrieves a immutable list of Scripts of the specified type from this +GameObject. + + Type of Scripts to retrieve. + Immutable list of Scripts of the specified type. + + + +Retrieves a Script of the specified type from child GameObjects. +If multiple Scripts of the same specified type are added on the same +child GameObject, this will retrieve the first one added. + + Type of Script to retrieve. + Reference to the Script to retrieve. + + + +Retrieves a Script of the specified type from this GameObject. +If multiple Scripts of the same specified type are added on the same +GameObject, this will retrieve the first one added. + + Type of Script to retrieve. + Reference to the Script to retrieve. + + + +Adds a Script of the specified type to this GameObject. + + Type of Script to add. + Reference to the created Script. + + + +Removes a Component from this GameObject. If no Component exists to begin +with, nothing happens. + + Type of the Component to get. + + + +Ensures a Component on this GameObject. + + Type of the Component to ensure. + +Reference to the Component. + + + + +Retrieves the first Component from this GameObject's children that matches +the specified type. + + Type of the Component to get. + +Reference to the Component or null if neither of this GameObject's children +does not have the specified Component. + + + + +Gets a Component from this GameObject. + + Type of the Component to get. + +Reference to the Component or null if this GameObject does not have the +specified Component. + + + + +Adds a Component to this GameObject. + + Type of the Component to add. + Reference to the Component that was added. + + + +Sets the active state of this GameObject. +
+The actual "activeness" of this GameObject is still dependent on the parents' +active states. +
+ +Whether to activate or deactivate this GameObject. + +
+ + +Sets the name of this GameObject. + + The name to set. + + + +Native Entity ID value for this GameObject. + + + + +Whether or not this Entity is active in the Scene hierarchy. + + + + +Whether or not this Entity alone, is active. This does not mean that this +object is active in the scene. For example, if this Entity's parent is not +active, then this Entity would also be not active. + + + + +Name of the object that this Entity represents. + + + + +Retrieves a GameObject with the specified name. If there are multiple +GameObjects with the same name, the first found GameObject will be retrieved. +There is no guaranteed order of which GameObject is considered "first". + + Name of the GameObject to find. + GameObject that has the specified name. Null if not found. + + + +Destroys the specified GameObject. Note that the specified GameObject will no +longer be a valid GameObject after this function is called. + + The GameObject to be destroyed. + + + +Creates a new GameObject in the current Scene. If multiple Scenes are loaded, +and you would like to create an object in a specific Scene, call the Scene's +CreateGameObject(). + + GameObject that represents the newly created GameObject. + + + +Lightweight object for an PlushieEngine Entity that allows for easy access +to Component and Script operations. + + + + +Constructor for a Tooltip attribute that fills in the description. + + Text to be shown when a field is hovered. + + + +Maximum value for the Ranged field. + + + + +Minimum value for the Ranged field. + + + + +Simple attribute to constrain the range of values for a field on the editor. + + + + +Converts from a native std::Stringto a managed String. + + The native std::string to convert from. + Managed copy of a native std::string. + + + +Converts from a managed String to a native std::string. + + The managed String to convert from. + Native copy of a managed String. + + + +Converts from a native Vector2 to a managed Vector2. + + The native Vector2 to convert from. + Managed copy of a native Vector2. + + + +Converts from a native Quaternion to a managed Quaternion. + + The native Quaternion to convert from. + Managed copy of a native Quaternion. + + + +Converts from a managed Quaternion to a native Quaternion. + + The managed Quaternion to convert from. + Native copy of a managed Quaternion. + + + +Converts from a native Vector2 to a managed Vector2. + + The native Vector2 to convert from. + Managed copy of a native Vector2. + + + +Converts from a managed Vector2 to a native Vector2. + + The managed Vector2 to convert from. + Native copy of a managed Vector2. + + + +Converts from a native Vector3 to a managed Vector3. + + The native Vector3 to convert from. + Managed copy of a native Vector3. + + + +Converts from a managed Vector3 to a native Vector3. + + The managed Vector3 to convert from. + Native copy of a managed Vector3. + + + +Converts from a native Entity to a managed Entity (UInt32). + + Native Entity to convert from. + Managed representation of the specified Entity. + + + +Provides functions easy and consistent syntax for converting between custom +managed and native types that are aligned. + + + + +Converts to true if this is a valid Handle. + + + + +The library that the handle was issued by. + + + + +The internal ID of the handle. + + + + +Creates a ray starting at origin along direction. + + Source of the ray. + Direction the ray travels in. + + + +The direction that a ray travels in. + + + + +The start point of the ray. + + + + +CLR version of the the SHADE Engine's Ray class that represents a ray in +3-Dimensional space. + + + + +Are two quaternions equal to each other? + + Left-hand side quaternion. + Right-hand side quaternion. + + + +Combines rotations lhs and rhs. + + Left-hand side quaternion. + Right-hand side quaternion. + + + +Spherically interpolates between a and b by t. The parameter t is not clamped. + + + + +Spherically interpolates between quaternions a and b by ratio t. The parameter t is clamped to the range [0, 1]. + + Start value, returned when t = 0. + End value, returned when t = 1. + Interpolation ratio. + A quaternion spherically interpolated between quaternions a and b. + + + +Rotates a rotation from towards to.
+The from quaternion is rotated towards to by an angular step of maxDegreesDelta (but note that the rotation will not overshoot). +Negative values of maxDegreesDelta will move away from to until the rotation is exactly the opposite direction. +
+
+ + +Converts this quaternion to one with the same orientation but with a magnitude of 1. + + + + +Creates a rotation with the specified forward and upwards directions.
+Z axis will be aligned with forward, X axis aligned with cross product between forward and upwards, and Y axis aligned with cross product between Z and X. +
+
+ + +Interpolates between a and b by t and normalizes the result afterwards. The parameter t is not clamped. + + + + +Interpolates between a and b by t and normalizes the result afterwards. The parameter t is clamped to the range [0, 1]. + + Start value, returned when t = 0. + End value, returned when t = 1. + Interpolation ratio. + A quaternion interpolated between quaternions a and b. + + + +Returns the Inverse of rotation. + + + + +Creates a rotation which rotates from fromDirection to toDirection. + + + + +Returns a rotation that rotates y degrees around the y axis, x degrees around the x axis, and z degrees around the z axis; applied in that order. + + + + +The dot product between two rotations. + + + + +Creates a rotation which rotates angle degrees around axis. + + + + +Returns the angle in degrees between two rotations a and b.
+ The angle in degrees between the two vectors. +
+ + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Converts a rotation to angle-axis representation (angles in degrees). + + + + +Creates a rotation with the specified forward and upwards directions.
+The result is applied to this quaternion. +If used to orient a Transform, the Z axis will be aligned with forward and the Y axis with upwards, assuming these vectors are orthogonal. +Logs an error if the forward direction is zero. +
+ The direction to look in. + The vector that defines in which direction up is. +
+ + +Creates a rotation which rotates from fromDirection to toDirection.
+Use this to create a rotation which starts at the first Vector (fromDirection) and rotates to the second Vector (toDirection). +These Vectors must be set up in a script. +
+
+ + +Constructor to construct a Quaternion with the specified components. + + X-coordinate to set. + Y-coordinate to set. + Z-coordinate to set. + W-coordinate to set. + + + +W-component of the Quaternion. Do not directly modify quaternions. + + + + +Z-component of the Quaternion. +Don't modify this directly unless you know quaternions inside out. + + + + +Y-component of the Quaternion. +Don't modify this directly unless you know quaternions inside out. + + + + +X-component of the Quaternion. +Don't modify this directly unless you know quaternions inside out. + + + + +Shorthand for writing Quaternion(0, 0, 0, 1). + + + + +CLR version of SHADE's Quaternion class that represents an orientation. +Designed to closely match Unity's Quaternion struct. + + + + +Explicit conversion operator to enable explicit casting from a Vector2 to a +Vector3. + + Vector2 to convert from. + + + +Explicit conversion operator to enable explicit casting from a Vector3 to a +Vector2. + + Vector3 to convert from. + + + +Checks if two Vector3s are not approximately equal. This is equivalent to +calling !Vector3.IsNear() with default tolerance values. + + Vector3 to compare. + Another Vector3 to compare. + +True if all components are not approximately equal within the default +tolerance value. + + + + +Checks if two Vector3s are approximately equal. This is equivalent to +calling Vector3.IsNear() with default tolerance values. + + Vector3 to compare. + Another Vector3 to compare. + +True if all components are approximately equal within the default +tolerance value. + + + + +Calculates the division of a Vector3 with a scalar value and returns +the result. + + Scalar to divide with. + Vector3 to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Vector3 with a scalar value and returns +the result. + + Vector3 to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the division of a Vector3 with a scalar value and returns +the result. + + Scalar to divide with. + Vector3 to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Vector3 with a scalar value and returns +the result. + + Vector3 to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the component-wise multiplication of two Vector3s and returns the +result. + + Vector3 to multiply with. + Another Vector3 to multiply with. + The result of rhs subtracted from lhs. + + + +Subtracts a Vector3 from another Vector3 and returns the result. + + Vector3 to subtract from. + Another Vector3 to subtract. + The result of rhs subtracted from lhs. + + + +Adds two Vector3s together and returns the result. + + Vector3 to add. + Another Vector3 to add. + The result of lhs added to rhs + + + +Moves a point current towards target. +Similar to Lerp(), however, the function will ensure that the distance never +exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the +vector away from target + + The current position of the point. + The target position to move to. + Maximum distance moved per call. + Vector representing the moved point. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. +Unlike Lerp(), t is not clamped to a range at all. + + The start Vector3, returned when t = 0.0f. + The end Vector3, returned when t = 1.0f. + Value used to interpolate between a and b. + The interpolated Vector3. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. + + The start Vector3, returned when t = 0.0f. + The end Vector3, returned when t = 1.0f. + +Value used to interpolate between a and b which is clamped to +the range[0, 1]. + + The interpolated Vector3. + + + +Computes and returns a Vector3 that is made from the largest components of +the two specified Vector3s. + + Vector3 to calculate maximum Vector3 with. + Another Vector3 to calculate maximum Vector3 with. + +The Vector3 that contains the largest components of the two specified +Vector3s. + + + + +Computes and returns a Vector3 that is made from the smallest components of +the two specified Vector3s. + + Vector3 to calculate minimum Vector3 with. + Another Vector3 to calculate minimum Vector3 with. + +The Vector3 that contains the smallest components of the two specified +Vector3s. + + + + +Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise +direction. + + A Vector3 to rotate. + +Angle to rotate the vector by in an anti-clockwise direction in degrees. + + The Vector3 that represents the rotated vector. + + + +Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise +direction. + + A Vector3 to rotate. + +Angle to rotate the vector by in an anti-clockwise direction in radians. + + The Vector3 that represents the rotated vector. + + + +Reflects a Vector3 across another Vector3. + + A Vector3 to reflect. + A normal to reflect the Vector3 across. + The Vector3 that represents vec reflected across normal. + + + +Computes and returns a Vector3 projection. + + Vector3 to project. + Vector3 to project onto. + The Vector3 that represents the projected vec onto direction. + + + +Computes and returns the cross product of 2 specified Vector3s. + + Vector3 to calculate cross product with. + Another Vector3 to calculate cross product with. + The cross product of the two Vector3s. + + + +Computes and returns the dot product of 2 specified Vector3s. + + Vector3 to calculate dot product with. + Another Vector3 to calculate dot product with. + Scalar value representing the dot product of the two Vector3s. + + + +Checks if two specified Vector3s are near in value. + + Vector3 to check if is near in value. + Another Vector3 to check if is near in value. + Amount of tolerance to do the comparison with. + +True if the two Vector3s are within the tolerance value specified + + + + +Checks if two specified Vector3s are near in value. + + Vector3 to check if is near in value. + Another Vector3 to check if is near in value. + +True if the two Vector3s are within the tolerance value specified + + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Checks if a specified point is near this Vector3 that represents a point. + + The other point to check if we are near. + +The amount of tolerance before we consider these points as "near". + + +True if this Vector3 representing a point and the specified point are within +the range of the specified tolerance. False otherwise. + + + + +Checks if a specified point is near this Vector3 that represents a point with +a tolerance value of PLS_EPSILON. + + The other point to check if we are near. + +True if this Vector3 representing a point and the specified point are within +the range of the specified tolerance. False otherwise. + + + + +Calculates and returns the angle of this vector from the right vector. This +function returns values between -180.0f and 180.0f. + + Returns the angle of this vector from the right vector in degrees. + + + +Calculates and returns the angle of this vector from the right vector. This +function returns values between -Math.PI and Math.PI. + + Returns the angle of this vector from the right vector in radians. + + + +Calculates and returns the squared magnitude of this Vector3. + + Returns the squared length of this Vector3. + + + +Calculates and returns the magnitude of this Vector3. Note that this function +incurs a performance cost from the square root calculation. If you do not +need the precise magnitude, consider using GetSqrMagnitude() instead. + + Returns the length of this Vector3. + + + +Creates a copy of this Vector3 and returns a normalized version. + + +Returns a normalised copy of this Vector3. +If this Vector3 is a zero vector, a zero vector will be returned. + + + + +Normalises this current Vector3. This changes the data of this Vector3. +If you would like to get a copy, use GetNormalised() instead. +This function does nothing to a zero vector. + + + + +Conversion constructor to construct a Vector3 using a Vector2. + + + + + +Constructor to construct a Vector3 with the specified components. + + X-coordinate to set. + Y-coordinate to set. + Z-coordinate to set. + + + +Constructor to construct a Vector3 with the specified components with the +Z-component set to 0.0f. + + X-coordinate to set. + Y-coordinate to set. + + + +Constructor to construct a Vector3 with the specified components with the +Y and Z-component set to 0.0f. + + X-coordinate to set. + + + +Z-component of the Vector3. + + + + +Y-component of the Vector3. + + + + +X-component of the Vector3. + + + + +Shorthand for writing Vector3(0, 0, 0). + + + + +Shorthand for writing Vector3(0, 1, 0). + + + + +Shorthand for writing Vector3(1, 0, 0). + + + + +Shorthand for writing Vector3(float.PositiveInfinity, +float.PositiveInfinity, float.PositiveInfinity). + + + + +Shorthand for writing Vector3(1, 1, 1). + + + + +Shorthand for writing Vector3(float.NegativeInfinity, +float.NegativeInfinity, float.NegativeInfinity). + + + + +Shorthand for writing Vector3(-1, 0, 0). + + + + +Shorthand for writing Vector3(0, 0, 1). + + + + +Shorthand for writing Vector3(0, -1, 0). + + + + +Shorthand for writing Vector3(0, 0, -1). + + + + +CLR version of SHADE Engine's Vector3 class that represents a 3-Dimensional Vector. +Designed to closely match Unity's Vector3 struct. + + + + +Checks if two Vector2s are not approximately equal. This is equivalent to +calling !Vector2.IsNear() with default tolerance values. + + Vector2 to compare. + Another Vector2 to compare. + +True if all components are not approximately equal within the default +tolerance value. + + + + +Checks if two Vector2s are approximately equal. This is equivalent to +calling Vector2.IsNear() with default tolerance values. + + Vector2 to compare. + Another Vector2 to compare. + +True if all components are approximately equal within the default +tolerance value. + + + + +Calculates the division of a Vector2 with a scalar value and returns +the result. + + Scalar to divide with. + Vector2 to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Vector2 with a scalar value and returns +the result. + + Vector2 to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the division of a Vector2 with a scalar value and returns +the result. + + Scalar to divide with. + Vector2 to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Vector2 with a scalar value and returns +the result. + + Vector2 to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the component-wise multiplication of two Vector2s and returns the +result. + + Vector2 to multiply with. + Another Vector2 to multiply with. + The result of rhs subtracted from lhs. + + + +Subtracts a Vector2 from another Vector2 and returns the result. + + Vector2 to subtract from. + Another Vector2 to subtract. + The result of rhs subtracted from lhs. + + + +Adds two Vector2s together and returns the result. + + Vector2 to add. + Another Vector2 to add. + The result of lhs added to rhs + + + +Moves a point current towards target. +Similar to Lerp(), however, the function will ensure that the distance never +exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the +vector away from target + + The current position of the point. + The target position to move to. + Maximum distance moved per call. + Vector representing the moved point. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. +Unlike Lerp(), t is not clamped to a range at all. + + The start Vector2, returned when t = 0.0f. + The end Vector2, returned when t = 1.0f. + Value used to interpolate between a and b. + The interpolated Vector2. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. + + The start Vector2, returned when t = 0.0f. + The end Vector2, returned when t = 1.0f. + +Value used to interpolate between a and b which is clamped to +the range[0, 1]. + + The interpolated Vector2. + + + +Computes and returns a Vector2 that is made from the largest components of +the two specified Vector2s. + + Vector2 to calculate maximum Vector2 with. + Another Vector2 to calculate maximum Vector2 with. + +The Vector2 that contains the largest components of the two specified +Vector2s. + + + + +Computes and returns a Vector2 that is made from the smallest components of +the two specified Vector2s. + + Vector2 to calculate minimum Vector2 with. + Another Vector2 to calculate minimum Vector2 with. + +The Vector2 that contains the smallest components of the two specified +Vector2s. + + + + +Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise +direction. + + A Vector2 to rotate. + +Angle to rotate the vector by in an anti-clockwise direction in degrees. + + The Vector2 that represents the rotated vector. + + + +Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise +direction. + + A Vector2 to rotate. + +Angle to rotate the vector by in an anti-clockwise direction in radians. + + The Vector2 that represents the rotated vector. + + + +Reflects a Vector2 across another Vector2. + + A Vector2 to reflect. + A normal to reflect the Vector2 across. + The Vector2 that represents vec reflected across normal. + + + +Computes and returns a Vector2 projection. + + Vector2 to project. + Vector2 to project onto. + The Vector2 that represents the projected vec onto direction. + + + +Computes a perpendicular Vector2 to the specified Vector2. + + Vector2 to find a perpendicular of. + +Whether the inward perpendicular Vector is retrieved. If true, the +resultant vector is rotated 90-degrees in a counter-clockwise. + + The perpendicular Vector2 relative to the specified Vector2. + + + + +Computes the inward perpendicular Vector2 to the specified Vector2. +Equivalent to calling Perpendicular(lhs, true). This means, the +resultant Vector2 is rotated 90-degrees in a counter-clockwise. + + Vector2 to find a perpendicular of. + +The perpendicular Vector2 relative to the specified Vector2. + + + + +Computes and returns the dot product of 2 specified Vector2s. + + Vector2 to calculate dot product with. + Another Vector2 to calculate dot product with. + +Scalar value representing the dot product of the two Vector2s. + + + + +Checks if two specified Vector2s are near in value. + + Vector2 to check if is near in value. + Another Vector2 to check if is near in value. + +Amount of tolerance to do the comparison with. + + +True if the two Vector2s are within the tolerance value specified + + + + +Checks if two specified Vector2s are near in value. + + Vector2 to check if is near in value. + Another Vector2 to check if is near in value. + +True if the two Vector2s are within the tolerance value specified + + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Checks if a specified point is near this Vector2 that represents a point. + + The other point to check if we are near. + +The amount of tolerance before we consider these points as "near". + + +True if this Vector2 representing a point and the specified point are within +the range of the specified tolerance. False otherwise. + + + + +Checks if a specified point is near this Vector2 that represents a point with +a tolerance value of PLS_EPSILON. + + The other point to check if we are near. + +True if this Vector2 representing a point and the specified point are within +the range of the specified tolerance. False otherwise. + + + + +Calculates and returns the angle of this vector from the right vector. This +function returns values between -180.0f and 180.0f. + + Returns the angle of this vector from the right vector in degrees. + + + +Calculates and returns the angle of this vector from the right vector. This +function returns values between -Math.PI and Math.PI. + + Returns the angle of this vector from the right vector in radians. + + + +Calculates and returns the squared magnitude of this Vector2. + + Returns the squared length of this Vector2. + + + +Calculates and returns the magnitude of this Vector2. Note that this function +incurs a performance cost from the square root calculation. If you do not +need the precise magnitude, consider using GetSqrMagnitude() instead. + + Returns the length of this Vector2. + + + +Creates a copy of this Vector2 and returns a normalized version. + + +Returns a normalised copy of this Vector2. +If this Vector2 is a zero vector, a zero vector will be returned. + + + + +Normalises this current Vector2. This changes the data of this Vector2. +If you would like to get a copy, use GetNormalised() instead. +This function does nothing to a zero vector. + + + + +Constructor to construct a Vector2 with the specified components.. + + X-coordinate to set. + Y-coordinate to set. + + + +Constructor to construct a Vector2 with the specified components with the +Y-component set to 0.0f. + + X-coordinate to set. + + + +Y-component of the Vector2. + + + + +X-component of the Vector2. + + + + +Shorthand for writing Vector2(0, 0). + + + + +Shorthand for writing Vector2(0, 1). + + + + +Shorthand for writing Vector2(1, 0). + + + + +Shorthand for writing Vector2(float.PositiveInfinity, +float.PositiveInfinity). + + + + +Shorthand for writing Vector2(1, 1). + + + + +Shorthand for writing Vector2(float.NegativeInfinity, +float.NegativeInfinity). + + + + +Shorthand for writing Vector2(-1, 0). + + + + +Shorthand for writing Vector2(0, -1). + + + + +CLR version of SHADE Engine's Vector2 class that represents a 2-Dimensional Vector. +Designed to closely match Unity's Vector2 struct. + + + + +Checks if the specified entity is valid. This is done by checking if it +matches Pls::Entity::INVALID. + + The Entity to check. + True if the specified Entity is valid. + + + +Static class that contains useful utility functions for working with Entity. + + + + +Manages all resources in multiple ResourceLibraries. + + + + +Base class for SHResourceLibrary that holds information about the library type. + + + + +Template Specialization for Handle that represents a type-less Handle. + + + + +Converts to true if this is a valid Handle. + + + + +Native ID type of a handle + + + + +Base implementation of the Handle that is not templated to allow for holding +generic non-type-specific Handles. + + + + +Exception thrown when a generic Handle is being casted to the wrong type. + + + + +Exception thrown when an invalid Handle was dereferenced. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/bin/Release/SHADE_CSharp.xml b/bin/Release/SHADE_CSharp.xml new file mode 100644 index 00000000..daeaa3c5 --- /dev/null +++ b/bin/Release/SHADE_CSharp.xml @@ -0,0 +1,1029 @@ + + + + SHADE_CSharp + + + + + Interface for a CallbackAction that all variants inherit from. + + + + + Whether or not this CallbackAction is runtime assigned. If it is, then the + TargetMethodName and TargetObject properties are invalid. + + + + + Name of the method that this CallbackAction is using. + + + + + Object which the specified target method is called on. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 1 parameter. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 2 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 3 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 4 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 5 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 6 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 7 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 8 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 9 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Represents a function call that can be serialised and put togetheer with scripts. + This variant accepts functions with 10 parameters. + + + + + + + + + + + + + + Constructs an empty Callback action. + + + + + Constructs a CallbackAction that represents a call to the specified static + method. + + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a CallbackAction that represents a call to a specified member + method on the specified target. + + Object to call the method on. + Method to call. + + Thrown if a method that is not compatible with the target is specified. The method's + source type must match the target's type. + + + + + Constructs a Callback action based on an action. + + Action that wraps a function to be called. + + + + Invokes the CallbackAction's stored method/action with the specified parameters. + + + + + Interface for a CallbackEvent that all variants inherit from. + + + + + Registers an empty ICallbackAction. + + + + + Registers an ICallbackAction with the event such that it will be called in + future + + ICallbackAction to register with. + + + + Deregisters an ICallbackAction that was previously added. This should + only emit a warning if an action that was not previous added was + provided. + + ICallbackAction to remove. + + + + Iterable set of ICallbackActions that were registered to this event. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + + A container of CallbackActions that is correlated to a specific scenario as + specified by the user of this class. + This variant accepts CallbackEvents with 1 generic parameter. + + + + + + + + + + + + + + Adds a CallbackAction into the event. + + CallbackAction to add. + + + + Constructs and adds a CallbackACtion into the event. + + System.Action to add as a CallbackAction. + + + + Constructs and adds a CallbackACtion into the event. + + Object to call the method on. + Method to call. + + + + + + + Invokes all stored CallbackActions with the specified parameters. + + + + diff --git a/bin/Release/SHADE_Managed.xml b/bin/Release/SHADE_Managed.xml new file mode 100644 index 00000000..7b653116 --- /dev/null +++ b/bin/Release/SHADE_Managed.xml @@ -0,0 +1,6250 @@ + + + + "SHADE_Managed" + + + + +Retrieves the duration that the specified key has not been held or was last +not been held for. + + The key to check. + Time in seconds that the key was held. + + + +Retrieves the duration that the specified key has been held or was last held +for. + + The key to check. + Time in seconds that the key was held. + + + +Retrieves the duration that the specified key has not been held or was last +not been held for. + + The key to check. + Time in seconds that the key was held. + + + +Retrieves the duration that the specified key has been held or was last held +for. + + The key to check. + Time in seconds that the key was held. + + + +Sets the position of the mouse cursor relative to the top left corner of the +window. + + +Position of the mouse in window pixel coordinates to set. + + + + +Checks if a specified mouse button is no longer pressed and was pressed +before. + + MouseCode of the mouse button to check. + +True during the frame the user releases the given mouse button. + + + + +Checks if a specified mouse button is pressed and was not pressed before. + + MouseCode of the mouse button to check. + +True during the frame the user pressed the given mouse button. + + + + +Checks if a specified mouse button is being held down. +This will also be true if GetMouseButtonDown() is true. + + MouseCode of the mouse button to check. + True while the user holds down the mouse button specified. + + + +Checks if a specified key is no longer pressed pressed and was pressed +before. + + KeyCode of the key to check. + +True during the frame the user releases the key identified by name. + + + + +Checks if a specified key is pressed and was not pressed before. + + KeyCode of the key to check. + +True during the frame the user starts pressing down the key specified. + + + + +Checks if a specified key is being held down. +This will also be true if GetKeyDown() is true. + + KeyCode of the key to check. + True while the user holds down the key specified. + + + +Amnount of vertical mouse scroll in this frame. + + + + +Mouse position in screen coordinates relative to the top left of the window. +This value is a Vector3 for compatibility with functions that have Vector3 +arguments. The z component of the Vector3 is always 0 + + + + +Represents the available supported mouse keycodes that can be passed into the +mouse-button-based Input functions. + + + + +Represents the available supported keycodes that can be passed into the +key-based Input functions. + +Attempting to follow https://docs.unity3d.com/ScriptReference/KeyCode.html +Win32 keycodes are shift-insensitive, i.e. 'A' and 'a' are the same keycode and '1' and '!' are the same keycode + + + + +Static class responsible for providing access to Input-related functionality. + + + + +Simple attribute to mark that a field in a Script should be serialised. + + + + +Cleans up all required components for managed code. + + + + +Reloads the managed script assembly. +Take note that this will clear all existing scripts, ensure that the scene +is saved before doing so. +Equivalent to calling UnloadScriptAssembly() and then LoadScriptAssembly(). + + + + +Loads the managed script assembly. Ensure this is only called after +UnloadScriptAssembly() has been called. + + + + +Unloads the managed script assembly. +Take note that this will clear all existing scripts, ensure that the scene +is saved before doing so. + + + + +Initialises all required components for managed code. + + + + +Name of the Managed Library that contains the C# scripts written externally. + + + + +Static class that contains the functions for interfacing with the core +PlushieEngine written in C++ for managing the lifecycle of managed code. + + + + +Default Constructor + + + + +Custom AssemblyLoadContext marked as collectible so that it can be unloaded. + + + + +Time taken for Physics simulations. You should use this for operations +within Script.FixedUpdate() + + + + +Time taken to process the previous frame. + + + + +Static class that contains the functions for working with time. + + + + +Static class that wraps up certain functions in the SHPhysicsSystem so that +accessing it from SHADE_Managed would not cause issues due to C++20 features. + + + + +Constructor for a Tooltip attribute that fills in the description. + + Text to be shown when a field is hovered. + + + +Description that is to be shown in the Tooltip. + + + + +Simple attribute to provide a field in a script with a tooltip. + + + + +Checks if a specified file exists. + + File path to the file to check. + True if the file exists + + + +Deletes the folder and all files in it as specified by the file path. + + File path to the file to delete. + + + +Deletes the file as specified by the file path. + + File path to the file to delete. + + + +Reads the file via the specified path that represents a build log of error +and warning messages. + + +File path to the build log of script builds done by BuildScriptAssembly() to +dump and process. + + + + +Registers events for the scripting system + + + + +Loads all the function pointers to CLR code that we need to execute. + + + + +Generates a .csproj file for editing and compiling the C# scripts. + + File path to the generated file. + + + +Utilises execution of a external batch file for invoking the dotnet build +tool to compile C# scripts in the Assets folder into the SHADE_Scripting +C# assembly DLL. + + +Whether or not a debug build will be built. Only debug built C# assemblies +can be debugged. + + +Whether or not we are reloading the assembly, if so, unload and then reload it. + + Whether or not the build succeeded. + + + +Performs a redo for script inspector changes if it exists. + + + + +Performs an undo for script inspector changes if it exists. + + + + +Renders the set of attached Scripts for the specified Entity into the +inspector. +
+This function is meant for consumption from native code in the inspector +rendering code. +
+ The Entity to render the Scripts of. +
+ + +Creates scripts and sets fields for the specified Entity based on the specified +YAML node. + + The Entity to deserialise a Script on to. + +YAML Node that contains the serialised script data. + + True if successfully deserialised. + + + +Performs serialization of all scripts for the specified entity into the +YAML::Node specified. This node will contain all serialised scripts after +calling this function. + + The Entity to Serialise. + +YAML Node that will store the serialised scripts. + + True if successfully serialised. + + + +Removes all Scripts attached to the specified Entity. Unlike +RemoveAllScripts(), this removes all the scripts immediately. +Does not do anything if the specified Entity is invalid or does not have any +Scripts attached. + + The entity to remove the scripts from. + +Whether or not to call OnDestroy on the scripts. This is ignored if not in +play mode. + + + + +Removes all Scripts attached to the specified Entity. Does not do anything +if the specified Entity is invalid or does not have any Scripts +attached. + + The entity to remove the scripts from. + + + +Adds a Script to a specified Entity. Note that while you can call this +multiple times on a specified Entity, it will work for all intents and +purposes but GetScript<T>() (C# only) currently only +gives you the first Script added of the specified type. + + The entity to add a script to. + Type name of the script to add. + +True if successfully added. False otherwise with the error logged to the +console. + + + + +Shuts down the DotNetRuntime. + + + + +Executes the OnCollision*()s and OnTrigger*()s of the Scripts that are attached +to Entities. + + + + +Executes the FixedUpdate()s of the Scripts that are attached to +Entities. + + + + +Reloads the managed script assembly. +Take note that this will clear all existing scripts, ensure that the scene +is saved before doing so. + + + + +Unloads the managed script assembly. +Take note that this will clear all existing scripts, ensure that the scene +is saved before doing so. + + + + +Loads the managed script assembly. Ensure this is only called after +UnloadScriptAssembly() has been called. + + + + +Initialises the DotNetRuntime and retrieves function pointers to all +functions on the CLR used to interface with the engine. + + + + +Manages initialisation of the DotNetRuntime and interfacing with CLR code written +and executed on .NET. + + + + +Deserialises a YAML node that contains a map of Scripts and copies the +deserialised data into the specified object if there are matching fields. + + +The JSON string that contains the data to copy into this Script object. + + The object to copy deserialised data into. + + + +Creates a JSON node that represents the specified object and its associated +serialisable fields. Public fields and fields marked with the SerialiseField +attribute will be serialised. + + The object to serialise. + + + +Checks if a specified field is a candidate for serialisation. This means that +the field is public or private with the [SerialiseField] attribute. + + The field to check. + +True if the specified field is a candidate for serialisation. + + + + +Retrieves a set of all non-static (instance) fields from a specified object. + + The object to get non-static fields from. + Immutable list of non-static fields. + + + +Contains useful static functions for working with Reflection. + + + +Converts the node to a YAML string. + + +Emits the node to the given output stream. + + +Emits the node to the given {@link Emitter}. If there is an error in writing, +{@link Emitter#good} will return false. + + + + Loads the input file as a list of YAML documents. + + @throws {@link ParserException} if it is malformed. + @throws {@link BadFile} if the file cannot be loaded. + + + + Loads the input stream as a list of YAML documents. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input string as a list of YAML documents. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input string as a list of YAML documents. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input file as a single YAML document. + + @throws {@link ParserException} if it is malformed. + @throws {@link BadFile} if the file cannot be loaded. + + + + Loads the input stream as a single YAML document. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input string as a single YAML document. + + @throws {@link ParserException} if it is malformed. + + + + Loads the input string as a single YAML document. + + @throws {@link ParserException} if it is malformed. + + + +Handles a "TAG" directive, which should be of the form 'handle prefix', +where 'handle' is converted to 'prefix' in the file. + + + +Handles a "YAML" directive, which should be of the form 'major.minor' (like +a version number). + + + +Reads any directives that are next in the queue, setting the internal +{@code m_pDirectives} state. + + + + Handles the next document by calling events on the {@code eventHandler}. + + @throw a ParserException on error. + @return false if there are no more documents + + + +Resets the parser with the given input stream. Any existing state is +erased. + + + +Evaluates to true if the parser has some valid input to be read. + + +Constructs a parser from the given input stream. The input stream must +live as long as the parser. + + + +Constructs an empty parser (with no input. + + +A parser turns a stream of bytes into one stream of "events" per YAML +document in the input stream. + + + + +Renders a context menu when right clicked for the scripts + + The Entity to render the Scripts of. + The Script to render the inspector for. + + + +Renders a field specified into the inspector. + + The field to render. + +The object that contains the data of the field to render. + + + + +Renders a single specified Script's inspector. + + The Entity to render the Scripts of. + The Script to render the inspector for. + +Indices used internally to differentiate each rendered Script +inspector. This is required to open and close each Script's inspector +independently from each other. + + + + +Redoes the last script inspector change if there is any. + + + + +Undoes the last script inspector change if there is any. + + + + +Renders a dropdown button that allows for the addition of PlushieScripts +onto the specified Entity. + + The Entity to add PlushieScripts to. + + + +Renders the set of attached Scripts for the specified Entity into the +inspector. +
+This function is meant for consumption from native code in the inspector +rendering code. +
+ The Entity to render the Scripts of. +
+ + +Static class for Editor-related functions + + + + +Processes a YAML node that contains a list of multiple scripts to be loaded +into the specified Entity. +

+This function should only be called from native unmanaged code. +
+ +The Entity to attach the deserialised Scripts to. + + +Pointer to the YAML::Node that contains serialized script data. + + +
+ + +Populates a YAML node with the scripts for a specified Entity. +

+This function should only be called from native unmanaged code. +
+ The Entity to Serialise. + +Pointer to a YAML::Node that will be populated with all of the serialised +scripts and their associated fields. + + +True if serialisation is successful. False if the buffer is too small for +the serialised output. + +
+ + +Executes OnCollision*() and OnTrigger*() for all scripts. + + + + +Executes LateUpdate() for all scripts. + + + + +Executes Update() for all scripts. + + + + +Executes FixedUpdate() for all scripts. + + + + +Retrieves a immutable list of available scripts that can be added. + + Immutable list of available scripts that can be added. + + + +Cleans up data stored in the ScriptStore to free up memory for garbage +collection. + + + + +Cleans up scripts that were marked for deletion. This calls the OnDestroy() +for these Scripts. + + + + +Sets up scripts that were marked for initialization. This calls the Awake() +and Start() for Scripts that have yet to have done so. + + + + +Initializes the ScriptStore to allocate and pre-populate reflection data. + + + + +Removes all Scripts attached to the specified Entity. Unlike +RemoveAllScripts(), this removes all the scripts immediately. +Does not do anything if the specified Entity is invalid or does not have any +Scripts attached. + + The entity to remove the scripts from. + +Whether or not to call OnDestroy on the scripts.This is ignored if not in +play mode. + + + + +Removes all Scripts attached to the specified Entity. Does not do anything +if the specified Entity is invalid or does not have any Scripts +attached. + + The entity to remove the scripts from. + + + +Removes a specific script from the + + The entity to remove the script from. + The script to remove. + True if successfully removed. False otherwise. + + + +Removes all Scripts of the specified type from the specified Entity. + + +Type of script to remove. +This needs to be a default constructable Script. + + The entity to remove the script from. + +If the specified Entity is invalid. + + + + +Retrieves an immutable list of all scripts attached to a specified Entity. + + +The entity which the scripts to retrieve are attached. + + +Immutable list of references to scripts attached to the specified Entity. +This can also be null if there are no scripts at all or an invalid Entity +was specified. + + + + +Retrieves a immutable list of scripts from the specified Entity that +matches the specified type. +
+Note that this function allocates. It should be used sparingly. +
+ +Type of scripts to get. +This needs to be a default constructable Script. + + +The entity which the scripts to retrieve are attached. + + +Immutable list of references to scripts of the specified type. + +
+ + +Retrieves the first Script from the specified Entity's children that matches +the specified type. + + +Type of script to get. +This needs to be a default constructable Script. + + +The entity which the script to retrieve is attached. + + +Reference to the script. This can be null if no script of the specified +type is attached. + + +If the specified Entity is invalid. + + + + +Retrieves the first Script from the specified Entity that matches the +specified type. + + +Type of script to get. +This needs to be a default constructable Script. + + +The entity which the script to retrieve is attached. + + +Reference to the script. This can be null if no script of the specified +type is attached. + + +If the specified Entity is invalid. + + + + +Adds a Script to a specified Entity. +
+This function is meant for consumption from native code or for serialisation +purposes. If you are writing in C# or C++/CLI and not doing serialisation, +use AddScript<T>() instead as it is faster. +
+ The entity to add a script to. + The entity to add a script to. + +Out parameter handle to the Script that was created. + + +True if successfully added. False otherwise with the error logged to the +console. + +
+ + +Adds a Script to a specified Entity. +
+This function is meant for consumption from native code. If you are writing +in C# or C++/CLI, use AddScript<T>() instead as it is faster. +
+ The entity to add a script to. + The entity to add a script to. + +True if successfully added. False otherwise with the error logged to the +console. + +
+ + +Adds a Script to a specified Entity. + + +Type of script to add. +This needs to be a default constructable PlushieScript. + + The entity to add a script to. + Reference to the script added. + +If the specified Entity is invalid. + + + + +Responsible for managing all scripts attached to Entities as well as executing +all lifecycle functions of scripts. + + + + +Checks if two Colors are not approximately equal. + + Color to compare. + Another Color to compare. + +True if all components are not approximately equal within the default +tolerance value. + + + + +Checks if two Colors are approximately equal. + + Color to compare. + Another Color to compare. + +True if all components are approximately equal within the default +tolerance value. + + + + +Calculates the division of a Color with a scalar value and returns +the result. + + Scalar to divide with. + Color to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Color with a scalar value and returns +the result. + + Color to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the component-wise multiplication of two Colors and returns the +result. + + Color to multiply with. + Another Color to multiply with. + The result of rhs subtracted from lhs. + + + +Subtracts a Color from another Color and returns the result. + + Color to subtract from. + Another Color to subtract. + The result of rhs subtracted from lhs. + + + +Adds two Colors together and returns the result. + + Color to add. + Another Color to add. + The result of lhs added to rhs + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. +Unlike Lerp(), t is not clamped to a range at all. + + The start Color, returned when t = 0.0. + The end Color, returned when t = 1.0. + Value used to interpolate between a and b. + The interpolated Color. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. + + The start Color, returned when t = 0.0. + The end Color, returned when t = 1.0. + +Value used to interpolate between a and b which is clamped to +the range[0, 1]. + + The interpolated Vector3. + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Alpha component of the colour. Ranges from 0.0f to 1.0f. + + + + +Blue component of the colour. Ranges from 0.0f to 1.0f. + + + + +Green component of the colour. Ranges from 0.0f to 1.0f. + + + + +Red component of the colour. Ranges from 0.0f to 1.0f. + + + + +Constructor to construct a Color with the specified components. + + Red component to set. + Green component to set. + Blue component to set. + Alpha component to set. + + + +Constructor to construct a Color with the specified components with the +alpha component set to 1.0f. + + Red component to set. + Green component to set. + Blue component to set. + + + +Constructor to construct a Color with the specified components with the +blue and alpha component set to 1.0f. + + Red component to set. + Green component to set. + + + +Constructor to construct a Color with the specified components with the +green, blue and alpha component set to 1.0f. + + Red component to set. + + + +Pure yellow, mix of pure red and green. + + + + +Pure magenta, mix of pure red and blue. + + + + +Pure cyan, mix of pure green and blue. + + + + +Pure blue. + + + + +Pure green. + + + + +Pure red. + + + + +Pure white. + + + + +Dark Gray, darker than gray. + + + + +Gray, halfway between black and white. + + + + +Light Gray, lighter than gray. + + + + +Pure black. + + + + +A static class that contains a set of default Colors. + + + + +CLR version of the the SHADE Engine's Color struct which describes a Color +encoded using floating point numbers that range from 0.0f to 1.0f. + + + + +Creates a inline button widget. +
+Wraps up ImGui::Button(). +
+ Text to display. + True if button was pressed. +
+ + +Creates a small inline button widget. +
+Wraps up ImGui::SmallButton(). +
+ Text to display. + True if button was pressed. +
+ + +Creates a visual text widget. +
+Wraps up ImGui::Text(). +
+ Text to display. +
+ + +Creates a menu item in the list of items for a mini popup. +
+Wraps up ImGui::MenuItem(). +
+ Label used to identify this widget. + Whether or not the menu item was selected. +
+ + +Opens the popup that was defined with the specified label. +
+Wraps up ImGui::OpenPopup(). +
+
+ + +Marks the end of a definition of a mini pop up that can show options. +
+Wraps up ImGui::EndPopup(). +
+
+ + +Marks the start of a definition of a mini pop up that can show options. +
+Wraps up ImGui::BeginPopup(). +
+ Label used to identify this widget. + Whether or not the pop up is open. +
+ + +Creates a collapsing title header. +
+Wraps up ImGui::CollapsingHeader(). +
+ Label for the header. + True if the header is open, false otherwise. +
+ + +Unindents the widgets rendered after this call. +
+Wraps up ImGui::Unindent(). +
+
+ + +Indents the widgets rendered after this call. +
+Wraps up ImGui::Indent(). +
+
+ + +Marks the end of a stack of ImGui widgets from the last PushID() call. +
+Wraps up ImGui::PopID(). +
+
+ + +Marks the start of a stack of ImGui widgets with the specified id. +
+Wraps up ImGui::PushID(). +
+ Integer-based ID. +
+ + +Marks the start of a stack of ImGui widgets with the specified id. +
+Wraps up ImGui::PushID(). +
+ String-based ID. +
+ + +Maximum length of a string supported by InputTextField() + + + + +Static class that contains useful functions for Editor UI using ImGui. + + + + +Redoes the last undo-ed command if it exists. + + + + +Undos the last added command if it exists. + + + + +Adds a command onto the stack. + + + + + +True if there is a redoable action in the stack. + + + + +True if there is an undoable action in the stack. + + + + +Command for the stack that represents a data modification. + + + + +Class that is able to store a stack of actions that can be done and redone. + + + + +To be called from native code when a Collision Shape has been changed. + + +The entity which has it's collision shape changed. + + + + +To be called from native code when a collision shape has been removed. + + The entity which has it's collision shape removed. + + + +Retrieves a ColliderBound at the specified index in the ColliderBound list +and casts it to the appropriate type. + + Type of the ColliderBound to cast to. + Index to retrieve a ColliderBound from. + ColliderBound for the specified index. + + + +Retrieves a ColliderBound at the specified index in the ColliderBound list. + + Index to retrieve a ColliderBound from. + ColliderBound for the specified index. + + + +Total number of ColliderShapes in the Collider component. + + + + +Constructs a Collider Component that represents a native SHColliderComponent +component tied to the specified Entity. + + Entity that this Component will be tied to. + + + +CLR version of the the SHADE Engine's SHColliderComponent. +A single Collider component can contain one or multiple Collider Bounds. + + + + + + + + + + +Radius of the Bounding Sphere formed by this bound. + + + + +Center of the Bounding Sphere formed by this bound. + + + + +Sphere-shaped Collider Bound. + + + + + + + + + + +Position of the top right front corner of the Bounding Box formed by this +bound. + + + + +Position of the bottom left back corner of the Bounding Box formed by this +bound. + + + + +Half of the scale of the Bounding Box formed by this bound. + + + + +Center of the Bounding Box formed by this bound. + + + + +Box-shaped Collider Bound. + + + + +Computes a Raycast and checks if there is a collision with any object. + + The ray to cast. + Maximum distance for the raycast check. + True if the ray intersects with an object in the scene. + + + +Checks if the specified point is within this shape's bounds. + + Point to test with. + True if the point is in the shape's bounds. + + + +Base interface for all Collider Shapes. + + + +@brief The density of the collider that determines the mass of the collision shape + if it is automatically computed. Must be a positive number. + + + +@brief The bounciness factor of the physics object., clamped between [0,1].
+ 0 means the object will never bounce. + 1 means the object never loses energy on a bounce. + +
+ +@brief The friction coefficient of the physics object., clamped between [0,1].
+ 0 means the object will never experience friction. + 1 means the friction force against the object is equal to the applied force. + +
+ +@brief Sets the mass density of the physics material. +@param newDensity The density value to set. Always made positive. + + + +@brief Sets the bounciness factor of the physics material. +@param newBounciness The bounciness value to set. Clamped between [0,1]. + + + +@brief Sets the friction coefficient of the physics material. +@param newFriction The friction value to set. Clamped between [0,1]. + + + +@brief Default constructor for a physics material. +@param friction The friction of the material. Clamped between [0,1]. Defaults to 0.4. +@param bounciness The bounciness of the material. Clamped between [0,1]. +@param density The mass density of the material. Always made positive. + + + + +Closes the current window, and depending on the implementation, should also +close the application. + + + + +Retrieves the current window fullscreen status. + + The current window fullscreen status.. + + + +Retrieves the current window height. + + The current window height. + + + +Retrieves the current window width. + + The current window width. + + + +Static class that wraps up certain functions in the SHGraphicsSystem so that +accessing it from SHADE_Managed would not cause issues due to C++20 features. + + + + @brief Perform ImGui and ImGui Backend Render + + + + + @brief Start new frame for editor + + + + + @brief Initialise Backend for ImGui (SDL and Vulkan backend) + + @param sdlWindow Pointer to SDL_Window + + + + @brief Set the Style for the editor + + @param style Desired style + + + + @brief Safely shutdown the editor + + + + + @brief Update the editor and add to ImGui DrawList + + @param dt Delta-time of the frame + + + + @brief Initialise the editor + + @param sdlWindow pointer to SDL_Window object created in application + + + + @brief Style options + + + + + @brief SHEditor static class contains editor variables and implementation of editor functions. + + + + + Get the YUV conversion mode, returning the correct mode for the resolution + when the current conversion mode is SDL_YUV_CONVERSION_AUTOMATIC + + \since This function is available since SDL 2.0.8. + + + + Get the YUV conversion mode + + \since This function is available since SDL 2.0.8. + + + + Set the YUV conversion mode + + \since This function is available since SDL 2.0.8. + + + + Perform low-level surface scaled blitting only. + + This is a semi-private function and it performs low-level surface blitting, + assuming the input rectangles have already been clipped. + + \param src the SDL_Surface structure to be copied from + \param srcrect the SDL_Rect structure representing the rectangle to be + copied + \param dst the SDL_Surface structure that is the blit target + \param dstrect the SDL_Rect structure representing the rectangle that is + copied into + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitScaled + + + + Perform a scaled surface copy to a destination surface. + + SDL_UpperBlitScaled() has been replaced by SDL_BlitScaled(), which is + merely a macro for this function with a less confusing name. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitScaled + + + + Perform bilinear scaling between two surfaces of the same format, 32BPP. + + \since This function is available since SDL 2.0.16. + + + + Perform a fast, low quality, stretch blit between two surfaces of the same + format. + + Please use SDL_BlitScaled() instead. + + \since This function is available since SDL 2.0.0. + + + + Perform low-level surface blitting only. + + This is a semi-private blit function and it performs low-level surface + blitting, assuming the input rectangles have already been clipped. + + Unless you know what you're doing, you should be using SDL_BlitSurface() + instead. + + \param src the SDL_Surface structure to be copied from + \param srcrect the SDL_Rect structure representing the rectangle to be + copied, or NULL to copy the entire surface + \param dst the SDL_Surface structure that is the blit target + \param dstrect the SDL_Rect structure representing the rectangle that is + copied into + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + + + + * Performs a fast blit from the source surface to the destination surface. + * + * This assumes that the source and destination rectangles are + * the same size. If either \c srcrect or \c dstrect are NULL, the entire + * surface (\c src or \c dst) is copied. The final blit rectangles are saved + * in \c srcrect and \c dstrect after all clipping is performed. + * + * \returns 0 if the blit is successful, otherwise it returns -1. + * + * The blit function should not be called on a locked surface. + * + * The blit semantics for surfaces with and without blending and colorkey + * are defined as follows: + * \verbatim + RGBA->RGB: + Source surface blend mode set to SDL_BLENDMODE_BLEND: + alpha-blend (using the source alpha-channel and per-surface alpha) + SDL_SRCCOLORKEY ignored. + Source surface blend mode set to SDL_BLENDMODE_NONE: + copy RGB. + if SDL_SRCCOLORKEY set, only copy the pixels matching the + RGB values of the source color key, ignoring alpha in the + comparison. + + RGB->RGBA: + Source surface blend mode set to SDL_BLENDMODE_BLEND: + alpha-blend (using the source per-surface alpha) + Source surface blend mode set to SDL_BLENDMODE_NONE: + copy RGB, set destination alpha to source per-surface alpha value. + both: + if SDL_SRCCOLORKEY set, only copy the pixels matching the + source color key. + + RGBA->RGBA: + Source surface blend mode set to SDL_BLENDMODE_BLEND: + alpha-blend (using the source alpha-channel and per-surface alpha) + SDL_SRCCOLORKEY ignored. + Source surface blend mode set to SDL_BLENDMODE_NONE: + copy all of RGBA to the destination. + if SDL_SRCCOLORKEY set, only copy the pixels matching the + RGB values of the source color key, ignoring alpha in the + comparison. + + RGB->RGB: + Source surface blend mode set to SDL_BLENDMODE_BLEND: + alpha-blend (using the source per-surface alpha) + Source surface blend mode set to SDL_BLENDMODE_NONE: + copy RGB. + both: + if SDL_SRCCOLORKEY set, only copy the pixels matching the + source color key. + \endverbatim + * + * You should call SDL_BlitSurface() unless you know exactly how SDL + * blitting works internally and how to use the other blit functions. + + Perform a fast blit from the source surface to the destination surface. + + SDL_UpperBlit() has been replaced by SDL_BlitSurface(), which is merely a + macro for this function with a less confusing name. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + + + + Perform a fast fill of a set of rectangles with a specific color. + + `color` should be a pixel of the format used by the surface, and can be + generated by SDL_MapRGB() or SDL_MapRGBA(). If the color value contains an + alpha component then the destination is simply filled with that alpha + information, no blending takes place. + + If there is a clip rectangle set on the destination (set via + SDL_SetClipRect()), then this function will fill based on the intersection + of the clip rectangle and `rect`. + + \param dst the SDL_Surface structure that is the drawing target + \param rects an array of SDL_Rects representing the rectangles to fill. + \param count the number of rectangles in the array + \param color the color to fill with + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FillRect + + + + Perform a fast fill of a rectangle with a specific color. + + `color` should be a pixel of the format used by the surface, and can be + generated by SDL_MapRGB() or SDL_MapRGBA(). If the color value contains an + alpha component then the destination is simply filled with that alpha + information, no blending takes place. + + If there is a clip rectangle set on the destination (set via + SDL_SetClipRect()), then this function will fill based on the intersection + of the clip rectangle and `rect`. + + \param dst the SDL_Surface structure that is the drawing target + \param rect the SDL_Rect structure representing the rectangle to fill, or + NULL to fill the entire surface + \param color the color to fill with + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FillRects + + + + Premultiply the alpha on a block of pixels. + + This is safe to use with src == dst, but not for other overlapping areas. + + This function is currently only implemented for SDL_PIXELFORMAT_ARGB8888. + + \param width the width of the block to convert, in pixels + \param height the height of the block to convert, in pixels + \param src_format an SDL_PixelFormatEnum value of the `src` pixels format + \param src a pointer to the source pixels + \param src_pitch the pitch of the source pixels, in bytes + \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format + \param dst a pointer to be filled in with premultiplied pixel data + \param dst_pitch the pitch of the destination pixels, in bytes + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.18. + + + + Copy a block of pixels of one format to another format. + + \param width the width of the block to copy, in pixels + \param height the height of the block to copy, in pixels + \param src_format an SDL_PixelFormatEnum value of the `src` pixels format + \param src a pointer to the source pixels + \param src_pitch the pitch of the source pixels, in bytes + \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format + \param dst a pointer to be filled in with new pixel data + \param dst_pitch the pitch of the destination pixels, in bytes + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + + + Copy an existing surface to a new surface of the specified format enum. + + This function operates just like SDL_ConvertSurface(), but accepts an + SDL_PixelFormatEnum value instead of an SDL_PixelFormat structure. As such, + it might be easier to call but it doesn't have access to palette + information for the destination surface, in case that would be important. + + \param src the existing SDL_Surface structure to convert + \param pixel_format the SDL_PixelFormatEnum that the new surface is + optimized for + \param flags the flags are unused and should be set to 0; this is a + leftover from SDL 1.2's API + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocFormat + \sa SDL_ConvertSurface + \sa SDL_CreateRGBSurface + + + + Copy an existing surface to a new surface of the specified format. + + This function is used to optimize images for faster *repeat* blitting. This + is accomplished by converting the original and storing the result as a new + surface. The new, optimized surface can then be used as the source for + future blits, making them faster. + + \param src the existing SDL_Surface structure to convert + \param fmt the SDL_PixelFormat structure that the new surface is optimized + for + \param flags the flags are unused and should be set to 0; this is a + leftover from SDL 1.2's API + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocFormat + \sa SDL_ConvertSurfaceFormat + \sa SDL_CreateRGBSurface + + + + Get the clipping rectangle for a surface. + + When `surface` is the destination of a blit, only the area within the clip + rectangle is drawn into. + + \param surface the SDL_Surface structure representing the surface to be + clipped + \param rect an SDL_Rect structure filled in with the clipping rectangle for + the surface + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_SetClipRect + + + + Set the clipping rectangle for a surface. + + When `surface` is the destination of a blit, only the area within the clip + rectangle is drawn into. + + Note that blits are automatically clipped to the edges of the source and + destination surfaces. + + \param surface the SDL_Surface structure to be clipped + \param rect the SDL_Rect structure representing the clipping rectangle, or + NULL to disable clipping + \returns SDL_TRUE if the rectangle intersects the surface, otherwise + SDL_FALSE and blits will be completely clipped. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_GetClipRect + + + + Get the blend mode used for blit operations. + + \param surface the SDL_Surface structure to query + \param blendMode a pointer filled in with the current SDL_BlendMode + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_SetSurfaceBlendMode + + + + Set the blend mode used for blit operations. + + To copy a surface to another surface (or texture) without blending with the + existing data, the blendmode of the SOURCE surface should be set to + `SDL_BLENDMODE_NONE`. + + \param surface the SDL_Surface structure to update + \param blendMode the SDL_BlendMode to use for blit blending + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceBlendMode + + + + Get the additional alpha value used in blit operations. + + \param surface the SDL_Surface structure to query + \param alpha a pointer filled in with the current alpha value + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceColorMod + \sa SDL_SetSurfaceAlphaMod + + + + Set an additional alpha value used in blit operations. + + When this surface is blitted, during the blit operation the source alpha + value is modulated by this alpha value according to the following formula: + + `srcA = srcA * (alpha / 255)` + + \param surface the SDL_Surface structure to update + \param alpha the alpha value multiplied into blit operations + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceAlphaMod + \sa SDL_SetSurfaceColorMod + + + + Get the additional color value multiplied into blit operations. + + \param surface the SDL_Surface structure to query + \param r a pointer filled in with the current red color value + \param g a pointer filled in with the current green color value + \param b a pointer filled in with the current blue color value + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceAlphaMod + \sa SDL_SetSurfaceColorMod + + + + Set an additional color value multiplied into blit operations. + + When this surface is blitted, during the blit operation each source color + channel is modulated by the appropriate color value according to the + following formula: + + `srcC = srcC * (color / 255)` + + \param surface the SDL_Surface structure to update + \param r the red color value multiplied into blit operations + \param g the green color value multiplied into blit operations + \param b the blue color value multiplied into blit operations + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetSurfaceColorMod + \sa SDL_SetSurfaceAlphaMod + + + + Get the color key (transparent pixel) for a surface. + + The color key is a pixel of the format used by the surface, as generated by + SDL_MapRGB(). + + If the surface doesn't have color key enabled this function returns -1. + + \param surface the SDL_Surface structure to query + \param key a pointer filled in with the transparent pixel + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_SetColorKey + + + + Returns whether the surface has a color key + + It is safe to pass a NULL `surface` here; it will return SDL_FALSE. + + \param surface the SDL_Surface structure to query + \return SDL_TRUE if the surface has a color key, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.9. + + \sa SDL_SetColorKey + \sa SDL_GetColorKey + + + + Set the color key (transparent pixel) in a surface. + + The color key defines a pixel value that will be treated as transparent in + a blit. For example, one can use this to specify that cyan pixels should be + considered transparent, and therefore not rendered. + + It is a pixel of the format used by the surface, as generated by + SDL_MapRGB(). + + RLE acceleration can substantially speed up blitting of images with large + horizontal runs of transparent pixels. See SDL_SetSurfaceRLE() for details. + + \param surface the SDL_Surface structure to update + \param flag SDL_TRUE to enable color key, SDL_FALSE to disable color key + \param key the transparent pixel + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_GetColorKey + + + + Returns whether the surface is RLE enabled + + It is safe to pass a NULL `surface` here; it will return SDL_FALSE. + + \param surface the SDL_Surface structure to query + \returns SDL_TRUE if the surface is RLE enabled, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.14. + + \sa SDL_SetSurfaceRLE + + + + Save a surface to a file. + + Convenience macro. + + Set the RLE acceleration hint for a surface. + + If RLE is enabled, color key and alpha blending blits are much faster, but + the surface must be locked before directly accessing the pixels. + + \param surface the SDL_Surface structure to optimize + \param flag 0 to disable, non-zero to enable RLE acceleration + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_BlitSurface + \sa SDL_LockSurface + \sa SDL_UnlockSurface + + + + Load a surface from a file. + + Convenience macro. + + Save a surface to a seekable SDL data stream in BMP format. + + Surfaces with a 24-bit, 32-bit and paletted 8-bit format get saved in the + BMP directly. Other RGB formats with 8-bit or higher get converted to a + 24-bit surface or, if they have an alpha mask or a colorkey, to a 32-bit + surface before they are saved. YUV and paletted 1-bit and 4-bit formats are + not supported. + + \param surface the SDL_Surface structure containing the image to be saved + \param dst a data stream to save to + \param freedst non-zero to close the stream after being written + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_LoadBMP_RW + \sa SDL_SaveBMP + + + + Load a BMP image from a seekable SDL data stream. + + The new surface should be freed with SDL_FreeSurface(). Not doing so will + result in a memory leak. + + src is an open SDL_RWops buffer, typically loaded with SDL_RWFromFile. + Alternitavely, you might also use the macro SDL_LoadBMP to load a bitmap + from a file, convert it to an SDL_Surface and then close the file. + + \param src the data stream for the surface + \param freesrc non-zero to close the stream after being read + \returns a pointer to a new SDL_Surface structure or NULL if there was an + error; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FreeSurface + \sa SDL_RWFromFile + \sa SDL_LoadBMP + \sa SDL_SaveBMP_RW + + + + Release a surface after directly accessing the pixels. + + \param surface the SDL_Surface structure to be unlocked + + \since This function is available since SDL 2.0.0. + + \sa SDL_LockSurface + + + + Set up a surface for directly accessing the pixels. + + Between calls to SDL_LockSurface() / SDL_UnlockSurface(), you can write to + and read from `surface->pixels`, using the pixel format stored in + `surface->format`. Once you are done accessing the surface, you should use + SDL_UnlockSurface() to release it. + + Not all surfaces require locking. If `SDL_MUSTLOCK(surface)` evaluates to + 0, then you can read and write to the surface at any time, and the pixel + format of the surface will not change. + + \param surface the SDL_Surface structure to be locked + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_MUSTLOCK + \sa SDL_UnlockSurface + + + + Set the palette used by a surface. + + A single palette can be shared with many surfaces. + + \param surface the SDL_Surface structure to update + \param palette the SDL_Palette structure to use + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + + + Free an RGB surface. + + It is safe to pass NULL to this function. + + \param surface the SDL_Surface to free. + + \since This function is available since SDL 2.0.0. + + \sa SDL_CreateRGBSurface + \sa SDL_CreateRGBSurfaceFrom + \sa SDL_LoadBMP + \sa SDL_LoadBMP_RW + + + + Allocate a new RGB surface with with a specific pixel format and existing + pixel data. + + This function operates mostly like SDL_CreateRGBSurfaceFrom(), except + instead of providing pixel color masks, you provide it with a predefined + format from SDL_PixelFormatEnum. + + No copy is made of the pixel data. Pixel data is not managed automatically; + you must free the surface before you free the pixel data. + + \param pixels a pointer to existing pixel data + \param width the width of the surface + \param height the height of the surface + \param depth the depth of the surface in bits + \param pitch the pitch of the surface in bytes + \param format the SDL_PixelFormatEnum for the new surface's pixel format. + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.5. + + \sa SDL_CreateRGBSurfaceFrom + \sa SDL_CreateRGBSurfaceWithFormat + \sa SDL_FreeSurface + + + + Allocate a new RGB surface with existing pixel data. + + This function operates mostly like SDL_CreateRGBSurface(), except it does + not allocate memory for the pixel data, instead the caller provides an + existing buffer of data for the surface to use. + + No copy is made of the pixel data. Pixel data is not managed automatically; + you must free the surface before you free the pixel data. + + \param pixels a pointer to existing pixel data + \param width the width of the surface + \param height the height of the surface + \param depth the depth of the surface in bits + \param pitch the pitch of the surface in bytes + \param Rmask the red mask for the pixels + \param Gmask the green mask for the pixels + \param Bmask the blue mask for the pixels + \param Amask the alpha mask for the pixels + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_CreateRGBSurface + \sa SDL_CreateRGBSurfaceWithFormat + \sa SDL_FreeSurface + + + + Allocate a new RGB surface with a specific pixel format. + + This function operates mostly like SDL_CreateRGBSurface(), except instead + of providing pixel color masks, you provide it with a predefined format + from SDL_PixelFormatEnum. + + \param flags the flags are unused and should be set to 0 + \param width the width of the surface + \param height the height of the surface + \param depth the depth of the surface in bits + \param format the SDL_PixelFormatEnum for the new surface's pixel format. + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.5. + + \sa SDL_CreateRGBSurface + \sa SDL_CreateRGBSurfaceFrom + \sa SDL_FreeSurface + + + + Allocate a new RGB surface. + + If `depth` is 4 or 8 bits, an empty palette is allocated for the surface. + If `depth` is greater than 8 bits, the pixel format is set using the + [RGBA]mask parameters. + + The [RGBA]mask parameters are the bitmasks used to extract that color from + a pixel. For instance, `Rmask` being 0xFF000000 means the red data is + stored in the most significant byte. Using zeros for the RGB masks sets a + default value, based on the depth. For example: + + ```c++ + SDL_CreateRGBSurface(0,w,h,32,0,0,0,0); + ``` + + However, using zero for the Amask results in an Amask of 0. + + By default surfaces with an alpha mask are set up for blending as with: + + ```c++ + SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND) + ``` + + You can change this by calling SDL_SetSurfaceBlendMode() and selecting a + different `blendMode`. + + \param flags the flags are unused and should be set to 0 + \param width the width of the surface + \param height the height of the surface + \param depth the depth of the surface in bits + \param Rmask the red mask for the pixels + \param Gmask the green mask for the pixels + \param Bmask the blue mask for the pixels + \param Amask the alpha mask for the pixels + \returns the new SDL_Surface structure that is created or NULL if it fails; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_CreateRGBSurfaceFrom + \sa SDL_CreateRGBSurfaceWithFormat + \sa SDL_FreeSurface + + + +Reference count -- used when freeing surface + + +info for fast blit mapping to other surfaces + + +clipping information + + +list of BlitMap that hold a reference to this surface + + +information needed for surfaces requiring locks + + +Application data associated with the surface + + + \brief A collection of pixels used in software blitting. + + \note This structure should be treated as read-only, except for \c pixels, + which, if not NULL, contains the raw pixel data for the surface. + + +\brief The type of function used for surface blitting functions. + + + + Compose a custom blend mode for renderers. + + The functions SDL_SetRenderDrawBlendMode and SDL_SetTextureBlendMode accept + the SDL_BlendMode returned by this function if the renderer supports it. + + A blend mode controls how the pixels from a drawing operation (source) get + combined with the pixels from the render target (destination). First, the + components of the source and destination pixels get multiplied with their + blend factors. Then, the blend operation takes the two products and + calculates the result that will get stored in the render target. + + Expressed in pseudocode, it would look like this: + + ```c + dstRGB = colorOperation(srcRGB * srcColorFactor, dstRGB * dstColorFactor); + dstA = alphaOperation(srcA * srcAlphaFactor, dstA * dstAlphaFactor); + ``` + + Where the functions `colorOperation(src, dst)` and `alphaOperation(src, + dst)` can return one of the following: + + - `src + dst` + - `src - dst` + - `dst - src` + - `min(src, dst)` + - `max(src, dst)` + + The red, green, and blue components are always multiplied with the first, + second, and third components of the SDL_BlendFactor, respectively. The + fourth component is not used. + + The alpha component is always multiplied with the fourth component of the + SDL_BlendFactor. The other components are not used in the alpha + calculation. + + Support for these blend modes varies for each renderer. To check if a + specific SDL_BlendMode is supported, create a renderer and pass it to + either SDL_SetRenderDrawBlendMode or SDL_SetTextureBlendMode. They will + return with an error if the blend mode is not supported. + + This list describes the support of custom blend modes for each renderer in + SDL 2.0.6. All renderers support the four blend modes listed in the + SDL_BlendMode enumeration. + + - **direct3d**: Supports all operations with all factors. However, some + factors produce unexpected results with `SDL_BLENDOPERATION_MINIMUM` and + `SDL_BLENDOPERATION_MAXIMUM`. + - **direct3d11**: Same as Direct3D 9. + - **opengl**: Supports the `SDL_BLENDOPERATION_ADD` operation with all + factors. OpenGL versions 1.1, 1.2, and 1.3 do not work correctly with SDL + 2.0.6. + - **opengles**: Supports the `SDL_BLENDOPERATION_ADD` operation with all + factors. Color and alpha factors need to be the same. OpenGL ES 1 + implementation specific: May also support `SDL_BLENDOPERATION_SUBTRACT` + and `SDL_BLENDOPERATION_REV_SUBTRACT`. May support color and alpha + operations being different from each other. May support color and alpha + factors being different from each other. + - **opengles2**: Supports the `SDL_BLENDOPERATION_ADD`, + `SDL_BLENDOPERATION_SUBTRACT`, `SDL_BLENDOPERATION_REV_SUBTRACT` + operations with all factors. + - **psp**: No custom blend mode support. + - **software**: No custom blend mode support. + + Some renderers do not provide an alpha component for the default render + target. The `SDL_BLENDFACTOR_DST_ALPHA` and + `SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA` factors do not have an effect in this + case. + + \param srcColorFactor the SDL_BlendFactor applied to the red, green, and + blue components of the source pixels + \param dstColorFactor the SDL_BlendFactor applied to the red, green, and + blue components of the destination pixels + \param colorOperation the SDL_BlendOperation used to combine the red, + green, and blue components of the source and + destination pixels + \param srcAlphaFactor the SDL_BlendFactor applied to the alpha component of + the source pixels + \param dstAlphaFactor the SDL_BlendFactor applied to the alpha component of + the destination pixels + \param alphaOperation the SDL_BlendOperation used to combine the alpha + component of the source and destination pixels + \returns an SDL_BlendMode that represents the chosen factors and + operations. + + \since This function is available since SDL 2.0.6. + + \sa SDL_SetRenderDrawBlendMode + \sa SDL_GetRenderDrawBlendMode + \sa SDL_SetTextureBlendMode + \sa SDL_GetTextureBlendMode + + + + Calculate the intersection of a rectangle and line segment with float + precision. + + This function is used to clip a line segment to a rectangle. A line segment + contained entirely within the rectangle or that does not intersect will + remain unchanged. A line segment that crosses the rectangle at either or + both ends will be clipped to the boundary of the rectangle and the new + coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary. + + \param rect an SDL_FRect structure representing the rectangle to intersect + \param X1 a pointer to the starting X-coordinate of the line + \param Y1 a pointer to the starting Y-coordinate of the line + \param X2 a pointer to the ending X-coordinate of the line + \param Y2 a pointer to the ending Y-coordinate of the line + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.22. + + + + Calculate a minimal rectangle enclosing a set of points with float + precision. + + If `clip` is not NULL then only points inside of the clipping rectangle are + considered. + + \param points an array of SDL_FPoint structures representing points to be + enclosed + \param count the number of structures in the `points` array + \param clip an SDL_FRect used for clipping or NULL to enclose all points + \param result an SDL_FRect structure filled in with the minimal enclosing + rectangle + \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the + points were outside of the clipping rectangle. + + \since This function is available since SDL 2.0.22. + + + + Calculate the union of two rectangles with float precision. + + \param A an SDL_FRect structure representing the first rectangle + \param B an SDL_FRect structure representing the second rectangle + \param result an SDL_FRect structure filled in with the union of rectangles + `A` and `B` + + \since This function is available since SDL 2.0.22. + + + + Calculate the intersection of two rectangles with float precision. + + If `result` is NULL then this function will return SDL_FALSE. + + \param A an SDL_FRect structure representing the first rectangle + \param B an SDL_FRect structure representing the second rectangle + \param result an SDL_FRect structure filled in with the intersection of + rectangles `A` and `B` + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.22. + + \sa SDL_HasIntersectionF + + + + Determine whether two rectangles intersect with float precision. + + If either pointer is NULL the function will return SDL_FALSE. + + \param A an SDL_FRect structure representing the first rectangle + \param B an SDL_FRect structure representing the second rectangle + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.22. + + \sa SDL_IntersectRect + + + + Returns true if the two rectangles are equal, using a default epsilon. + + \since This function is available since SDL 2.0.22. + + + + Returns true if the two rectangles are equal, within some given epsilon. + + \since This function is available since SDL 2.0.22. + + + +Returns true if the rectangle has no area. + + + +Returns true if point resides inside a rectangle. + + + + Calculate the intersection of a rectangle and line segment. + + This function is used to clip a line segment to a rectangle. A line segment + contained entirely within the rectangle or that does not intersect will + remain unchanged. A line segment that crosses the rectangle at either or + both ends will be clipped to the boundary of the rectangle and the new + coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary. + + \param rect an SDL_Rect structure representing the rectangle to intersect + \param X1 a pointer to the starting X-coordinate of the line + \param Y1 a pointer to the starting Y-coordinate of the line + \param X2 a pointer to the ending X-coordinate of the line + \param Y2 a pointer to the ending Y-coordinate of the line + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.0. + + + + Calculate a minimal rectangle enclosing a set of points. + + If `clip` is not NULL then only points inside of the clipping rectangle are + considered. + + \param points an array of SDL_Point structures representing points to be + enclosed + \param count the number of structures in the `points` array + \param clip an SDL_Rect used for clipping or NULL to enclose all points + \param result an SDL_Rect structure filled in with the minimal enclosing + rectangle + \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the + points were outside of the clipping rectangle. + + \since This function is available since SDL 2.0.0. + + + + Calculate the union of two rectangles. + + \param A an SDL_Rect structure representing the first rectangle + \param B an SDL_Rect structure representing the second rectangle + \param result an SDL_Rect structure filled in with the union of rectangles + `A` and `B` + + \since This function is available since SDL 2.0.0. + + + + Calculate the intersection of two rectangles. + + If `result` is NULL then this function will return SDL_FALSE. + + \param A an SDL_Rect structure representing the first rectangle + \param B an SDL_Rect structure representing the second rectangle + \param result an SDL_Rect structure filled in with the intersection of + rectangles `A` and `B` + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.0. + + \sa SDL_HasIntersection + + + + Determine whether two rectangles intersect. + + If either pointer is NULL the function will return SDL_FALSE. + + \param A an SDL_Rect structure representing the first rectangle + \param B an SDL_Rect structure representing the second rectangle + \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. + + \since This function is available since SDL 2.0.0. + + \sa SDL_IntersectRect + + + +Returns true if the two rectangles are equal. + + + +Returns true if the rectangle has no area. + + + +Returns true if point resides inside a rectangle. + + + + A rectangle, with the origin at the upper left (floating point). + + \sa SDL_FRectEmpty + \sa SDL_FRectEquals + \sa SDL_FRectEqualsEpsilon + \sa SDL_HasIntersectionF + \sa SDL_IntersectFRect + \sa SDL_IntersectFRectAndLine + \sa SDL_UnionFRect + \sa SDL_EncloseFPoints + \sa SDL_PointInFRect + + + + A rectangle, with the origin at the upper left (integer). + + \sa SDL_RectEmpty + \sa SDL_RectEquals + \sa SDL_HasIntersection + \sa SDL_IntersectRect + \sa SDL_IntersectRectAndLine + \sa SDL_UnionRect + \sa SDL_EnclosePoints + + + + The structure that defines a point (floating point) + + \sa SDL_EncloseFPoints + \sa SDL_PointInFRect + + + + Use this function to write 64 bits in native format to a SDL_RWops as + big-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in big-endian format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteLE64 + + + + Use this function to write 64 bits in native format to a SDL_RWops as + little-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in little-endian + format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteBE64 + + + + Use this function to write 32 bits in native format to a SDL_RWops as + big-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in big-endian format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteLE32 + + + + Use this function to write 32 bits in native format to a SDL_RWops as + little-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in little-endian + format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteBE32 + + + + Use this function to write 16 bits in native format to a SDL_RWops as + big-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in big-endian format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteLE16 + + + + Use this function to write 16 bits in native format to a SDL_RWops as + little-endian data. + + SDL byteswaps the data only if necessary, so the application always + specifies native format, and the data written will be in little-endian + format. + + \param dst the stream to which data will be written + \param value the data to be written, in native format + \returns 1 on successful write, 0 on error. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteBE16 + + + + \name Write endian functions + + Write an item of native format to the specified endianness. + + Use this function to write a byte to an SDL_RWops. + + \param dst the SDL_RWops to write to + \param value the byte value to write + \returns 1 on success or 0 on failure; call SDL_GetError() for more + information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadU8 + + + + Use this function to read 64 bits of big-endian data from an SDL_RWops and + return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 64 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadLE64 + + + + Use this function to read 64 bits of little-endian data from an SDL_RWops + and return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 64 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadBE64 + + + + Use this function to read 32 bits of big-endian data from an SDL_RWops and + return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 32 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadLE32 + + + + Use this function to read 32 bits of little-endian data from an SDL_RWops + and return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 32 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadBE32 + + + + Use this function to read 16 bits of big-endian data from an SDL_RWops and + return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 16 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadLE16 + + + + Use this function to read 16 bits of little-endian data from an SDL_RWops + and return in native format. + + SDL byteswaps the data only if necessary, so the data returned will be in + the native byte order. + + \param src the stream from which to read data + \returns 16 bits of data in the native byte order of the platform. + + \since This function is available since SDL 2.0.0. + + \sa SDL_ReadBE16 + + + + \name Read endian functions + + Read an item of the specified endianness and return in native format. + + Use this function to read a byte from an SDL_RWops. + + \param src the SDL_RWops to read from + \returns the read byte on success or 0 on failure; call SDL_GetError() for + more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_WriteU8 + + + + Load all the data from a file path. + + The data is allocated with a zero byte at the end (null terminated) for + convenience. This extra byte is not included in the value reported via + `datasize`. + + The data should be freed with SDL_free(). + + Prior to SDL 2.0.10, this function was a macro wrapping around + SDL_LoadFile_RW. + + \param file the path to read all available data from + \param datasize if not NULL, will store the number of bytes read + \returns the data, or NULL if there was an error. + + \since This function is available since SDL 2.0.10. + + + + Load all the data from an SDL data stream. + + The data is allocated with a zero byte at the end (null terminated) for + convenience. This extra byte is not included in the value reported via + `datasize`. + + The data should be freed with SDL_free(). + + \param src the SDL_RWops to read all available data from + \param datasize if not NULL, will store the number of bytes read + \param freesrc if non-zero, calls SDL_RWclose() on `src` before returning + \returns the data, or NULL if there was an error. + + \since This function is available since SDL 2.0.6. + + + + Close and free an allocated SDL_RWops structure. + + SDL_RWclose() closes and cleans up the SDL_RWops stream. It releases any + resources used by the stream and frees the SDL_RWops itself with + SDL_FreeRW(). This returns 0 on success, or -1 if the stream failed to + flush to its output (e.g. to disk). + + Note that if this fails to flush the stream to disk, this function reports + an error, but the SDL_RWops is still invalid once this function returns. + + Prior to SDL 2.0.10, this function was a macro. + + \param context SDL_RWops structure to close + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWwrite + + + + Write to an SDL_RWops data stream. + + This function writes exactly `num` objects each of size `size` from the + area pointed at by `ptr` to the stream. If this fails for any reason, it'll + return less than `num` to demonstrate how far the write progressed. On + success, it returns `num`. + + SDL_RWwrite is actually a function wrapper that calls the SDL_RWops's + `write` method appropriately, to simplify application development. + + Prior to SDL 2.0.10, this function was a macro. + + \param context a pointer to an SDL_RWops structure + \param ptr a pointer to a buffer containing data to write + \param size the size of an object to write, in bytes + \param num the number of objects to write + \returns the number of objects written, which will be less than **num** on + error; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + + + + Read from a data source. + + This function reads up to `maxnum` objects each of size `size` from the + data source to the area pointed at by `ptr`. This function may read less + objects than requested. It will return zero when there has been an error or + the data stream is completely read. + + SDL_RWread() is actually a function wrapper that calls the SDL_RWops's + `read` method appropriately, to simplify application development. + + Prior to SDL 2.0.10, this function was a macro. + + \param context a pointer to an SDL_RWops structure + \param ptr a pointer to a buffer to read data into + \param size the size of each object to read, in bytes + \param maxnum the maximum number of objects to be read + \returns the number of objects read, or 0 at error or end of file; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWseek + \sa SDL_RWwrite + + + + Determine the current read/write offset in an SDL_RWops data stream. + + SDL_RWtell is actually a wrapper function that calls the SDL_RWops's `seek` + method, with an offset of 0 bytes from `RW_SEEK_CUR`, to simplify + application development. + + Prior to SDL 2.0.10, this function was a macro. + + \param context a SDL_RWops data stream object from which to get the current + offset + \returns the current offset in the stream, or -1 if the information can not + be determined. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWwrite + + + + Seek within an SDL_RWops data stream. + + This function seeks to byte `offset`, relative to `whence`. + + `whence` may be any of the following values: + + - `RW_SEEK_SET`: seek from the beginning of data + - `RW_SEEK_CUR`: seek relative to current read point + - `RW_SEEK_END`: seek relative to the end of data + + If this stream can not seek, it will return -1. + + SDL_RWseek() is actually a wrapper function that calls the SDL_RWops's + `seek` method appropriately, to simplify application development. + + Prior to SDL 2.0.10, this function was a macro. + + \param context a pointer to an SDL_RWops structure + \param offset an offset in bytes, relative to **whence** location; can be + negative + \param whence any of `RW_SEEK_SET`, `RW_SEEK_CUR`, `RW_SEEK_END` + \returns the final offset in the data stream after the seek or -1 on error. + + \since This function is available since SDL 2.0.10. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWtell + \sa SDL_RWwrite + + + + Use this function to get the size of the data stream in an SDL_RWops. + + Prior to SDL 2.0.10, this function was a macro. + + \param context the SDL_RWops to get the size of the data stream from + \returns the size of the data stream in the SDL_RWops on success, -1 if + unknown or a negative error code on failure; call SDL_GetError() + for more information. + + \since This function is available since SDL 2.0.10. + + + + Use this function to free an SDL_RWops structure allocated by + SDL_AllocRW(). + + Applications do not need to use this function unless they are providing + their own SDL_RWops implementation. If you just need a SDL_RWops to + read/write a common data source, you should use the built-in + implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc, and + call the **close** method on those SDL_RWops pointers when you are done + with them. + + Only use SDL_FreeRW() on pointers returned by SDL_AllocRW(). The pointer is + invalid as soon as this function returns. Any extra memory allocated during + creation of the SDL_RWops is not freed by SDL_FreeRW(); the programmer must + be responsible for managing that memory in their **close** method. + + \param area the SDL_RWops structure to be freed + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocRW + + + + Use this function to allocate an empty, unpopulated SDL_RWops structure. + + Applications do not need to use this function unless they are providing + their own SDL_RWops implementation. If you just need a SDL_RWops to + read/write a common data source, you should use the built-in + implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc. + + You must free the returned pointer with SDL_FreeRW(). Depending on your + operating system and compiler, there may be a difference between the + malloc() and free() your program uses and the versions SDL calls + internally. Trying to mix the two can cause crashing such as segmentation + faults. Since all SDL_RWops must free themselves when their **close** + method is called, all SDL_RWops must be allocated through this function, so + they can all be freed correctly with SDL_FreeRW(). + + \returns a pointer to the allocated memory on success, or NULL on failure; + call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FreeRW + + + + Use this function to prepare a read-only memory buffer for use with RWops. + + This function sets up an SDL_RWops struct based on a memory area of a + certain size. It assumes the memory area is not writable. + + Attempting to write to this RWops stream will report an error without + writing to the memory buffer. + + This memory buffer is not copied by the RWops; the pointer you provide must + remain valid until you close the stream. Closing the stream will not free + the original buffer. + + If you need to write to a memory buffer, you should use SDL_RWFromMem() + with a writable buffer of memory instead. + + \param mem a pointer to a read-only buffer to feed an SDL_RWops stream + \param size the buffer size, in bytes + \returns a pointer to a new SDL_RWops structure, or NULL if it fails; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWtell + + + + Use this function to prepare a read-write memory buffer for use with + SDL_RWops. + + This function sets up an SDL_RWops struct based on a memory area of a + certain size, for both read and write access. + + This memory buffer is not copied by the RWops; the pointer you provide must + remain valid until you close the stream. Closing the stream will not free + the original buffer. + + If you need to make sure the RWops never writes to the memory buffer, you + should use SDL_RWFromConstMem() with a read-only buffer of memory instead. + + \param mem a pointer to a buffer to feed an SDL_RWops stream + \param size the buffer size, in bytes + \returns a pointer to a new SDL_RWops structure, or NULL if it fails; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWtell + \sa SDL_RWwrite + + + + Use this function to create an SDL_RWops structure from a standard I/O file + pointer (stdio.h's `FILE*`). + + This function is not available on Windows, since files opened in an + application on that platform cannot be used by a dynamically linked + library. + + On some platforms, the first parameter is a `void*`, on others, it's a + `FILE*`, depending on what system headers are available to SDL. It is + always intended to be the `FILE*` type from the C runtime's stdio.h. + + \param fp the `FILE*` that feeds the SDL_RWops stream + \param autoclose SDL_TRUE to close the `FILE*` when closing the SDL_RWops, + SDL_FALSE to leave the `FILE*` open when the RWops is + closed + \returns a pointer to the SDL_RWops structure that is created, or NULL on + failure; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFile + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWtell + \sa SDL_RWwrite + + + + \name RWFrom functions + + Functions to create SDL_RWops structures from various data streams. + + Use this function to create a new SDL_RWops structure for reading from + and/or writing to a named file. + + The `mode` string is treated roughly the same as in a call to the C + library's fopen(), even if SDL doesn't happen to use fopen() behind the + scenes. + + Available `mode` strings: + + - "r": Open a file for reading. The file must exist. + - "w": Create an empty file for writing. If a file with the same name + already exists its content is erased and the file is treated as a new + empty file. + - "a": Append to a file. Writing operations append data at the end of the + file. The file is created if it does not exist. + - "r+": Open a file for update both reading and writing. The file must + exist. + - "w+": Create an empty file for both reading and writing. If a file with + the same name already exists its content is erased and the file is + treated as a new empty file. + - "a+": Open a file for reading and appending. All writing operations are + performed at the end of the file, protecting the previous content to be + overwritten. You can reposition (fseek, rewind) the internal pointer to + anywhere in the file for reading, but writing operations will move it + back to the end of file. The file is created if it does not exist. + + **NOTE**: In order to open a file as a binary file, a "b" character has to + be included in the `mode` string. This additional "b" character can either + be appended at the end of the string (thus making the following compound + modes: "rb", "wb", "ab", "r+b", "w+b", "a+b") or be inserted between the + letter and the "+" sign for the mixed modes ("rb+", "wb+", "ab+"). + Additional characters may follow the sequence, although they should have no + effect. For example, "t" is sometimes appended to make explicit the file is + a text file. + + This function supports Unicode filenames, but they must be encoded in UTF-8 + format, regardless of the underlying operating system. + + As a fallback, SDL_RWFromFile() will transparently open a matching filename + in an Android app's `assets`. + + Closing the SDL_RWops will close the file handle SDL is holding internally. + + \param file a UTF-8 string representing the filename to open + \param mode an ASCII string representing the mode to be used for opening + the file. + \returns a pointer to the SDL_RWops structure that is created, or NULL on + failure; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_RWclose + \sa SDL_RWFromConstMem + \sa SDL_RWFromFP + \sa SDL_RWFromMem + \sa SDL_RWread + \sa SDL_RWseek + \sa SDL_RWtell + \sa SDL_RWwrite + + + +Return the size of the file in this rwops, or -1 if unknown + + + Seek to \c offset relative to \c whence, one of stdio's whence values: + RW_SEEK_SET, RW_SEEK_CUR, RW_SEEK_END + + \return the final offset in the data stream, or -1 on error. + + + Read up to \c maxnum objects each of size \c size from the data + stream to the area pointed at by \c ptr. + + \return the number of objects read, or 0 at error or end of file. + + + Write exactly \c num objects each of size \c size from the area + pointed at by \c ptr to data stream. + + \return the number of objects written, or 0 at error or end of file. + + + Close and free an allocated SDL_RWops structure. + + \return 0 if successful or -1 on write error when flushing data. + + + + Clear any previous error message for this thread. + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetError + \sa SDL_SetError + + + + Get the last error message that was set for the current thread. + + This allows the caller to copy the error string into a provided buffer, but + otherwise operates exactly the same as SDL_GetError(). + + \param errstr A buffer to fill with the last error message that was set for + the current thread + \param maxlen The size of the buffer pointed to by the errstr parameter + \returns the pointer passed in as the `errstr` parameter. + + \since This function is available since SDL 2.0.14. + + \sa SDL_GetError + + + + Retrieve a message about the last error that occurred on the current + thread. + + It is possible for multiple errors to occur before calling SDL_GetError(). + Only the last error is returned. + + The message is only applicable when an SDL function has signaled an error. + You must check the return values of SDL function calls to determine when to + appropriately call SDL_GetError(). You should *not* use the results of + SDL_GetError() to decide if an error has occurred! Sometimes SDL will set + an error string even when reporting success. + + SDL will *not* clear the error string for successful API calls. You *must* + check return values for failure cases before you can assume the error + string applies. + + Error strings are set per-thread, so an error set in a different thread + will not interfere with the current thread's operation. + + The returned string is internally allocated and must not be freed by the + application. + + \returns a message with information about the specific error that occurred, + or an empty string if there hasn't been an error message set since + the last call to SDL_ClearError(). The message is only applicable + when an SDL function has signaled an error. You must check the + return values of SDL function calls to determine when to + appropriately call SDL_GetError(). + + \since This function is available since SDL 2.0.0. + + \sa SDL_ClearError + \sa SDL_SetError + + + + Calculate a 256 entry gamma ramp for a gamma value. + + \param gamma a gamma value where 0.0 is black and 1.0 is identity + \param ramp an array of 256 values filled in with the gamma ramp + + \since This function is available since SDL 2.0.0. + + \sa SDL_SetWindowGammaRamp + + + + Get RGBA values from a pixel in the specified format. + + This function uses the entire 8-bit [0..255] range when converting color + components from pixel formats with less than 8-bits per RGB component + (e.g., a completely white pixel in 16-bit RGB565 format would return [0xff, + 0xff, 0xff] not [0xf8, 0xfc, 0xf8]). + + If the surface has no alpha component, the alpha will be returned as 0xff + (100% opaque). + + \param pixel a pixel value + \param format an SDL_PixelFormat structure describing the format of the + pixel + \param r a pointer filled in with the red component + \param g a pointer filled in with the green component + \param b a pointer filled in with the blue component + \param a a pointer filled in with the alpha component + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetRGB + \sa SDL_MapRGB + \sa SDL_MapRGBA + + + + Get RGB values from a pixel in the specified format. + + This function uses the entire 8-bit [0..255] range when converting color + components from pixel formats with less than 8-bits per RGB component + (e.g., a completely white pixel in 16-bit RGB565 format would return [0xff, + 0xff, 0xff] not [0xf8, 0xfc, 0xf8]). + + \param pixel a pixel value + \param format an SDL_PixelFormat structure describing the format of the + pixel + \param r a pointer filled in with the red component + \param g a pointer filled in with the green component + \param b a pointer filled in with the blue component + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetRGBA + \sa SDL_MapRGB + \sa SDL_MapRGBA + + + + Map an RGBA quadruple to a pixel value for a given pixel format. + + This function maps the RGBA color value to the specified pixel format and + returns the pixel value best approximating the given RGBA color value for + the given pixel format. + + If the specified pixel format has no alpha component the alpha value will + be ignored (as it will be in formats with a palette). + + If the format has a palette (8-bit) the index of the closest matching color + in the palette will be returned. + + If the pixel format bpp (color depth) is less than 32-bpp then the unused + upper bits of the return value can safely be ignored (e.g., with a 16-bpp + format the return value can be assigned to a Uint16, and similarly a Uint8 + for an 8-bpp format). + + \param format an SDL_PixelFormat structure describing the format of the + pixel + \param r the red component of the pixel in the range 0-255 + \param g the green component of the pixel in the range 0-255 + \param b the blue component of the pixel in the range 0-255 + \param a the alpha component of the pixel in the range 0-255 + \returns a pixel value + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetRGB + \sa SDL_GetRGBA + \sa SDL_MapRGB + + + + Map an RGB triple to an opaque pixel value for a given pixel format. + + This function maps the RGB color value to the specified pixel format and + returns the pixel value best approximating the given RGB color value for + the given pixel format. + + If the format has a palette (8-bit) the index of the closest matching color + in the palette will be returned. + + If the specified pixel format has an alpha component it will be returned as + all 1 bits (fully opaque). + + If the pixel format bpp (color depth) is less than 32-bpp then the unused + upper bits of the return value can safely be ignored (e.g., with a 16-bpp + format the return value can be assigned to a Uint16, and similarly a Uint8 + for an 8-bpp format). + + \param format an SDL_PixelFormat structure describing the pixel format + \param r the red component of the pixel in the range 0-255 + \param g the green component of the pixel in the range 0-255 + \param b the blue component of the pixel in the range 0-255 + \returns a pixel value + + \since This function is available since SDL 2.0.0. + + \sa SDL_GetRGB + \sa SDL_GetRGBA + \sa SDL_MapRGBA + + + + Free a palette created with SDL_AllocPalette(). + + \param palette the SDL_Palette structure to be freed + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocPalette + + + + Set a range of colors in a palette. + + \param palette the SDL_Palette structure to modify + \param colors an array of SDL_Color structures to copy into the palette + \param firstcolor the index of the first palette entry to modify + \param ncolors the number of entries to modify + \returns 0 on success or a negative error code if not all of the colors + could be set; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocPalette + \sa SDL_CreateRGBSurface + + + + Set the palette for a pixel format structure. + + \param format the SDL_PixelFormat structure that will use the palette + \param palette the SDL_Palette structure that will be used + \returns 0 on success or a negative error code on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocPalette + \sa SDL_FreePalette + + + + Create a palette structure with the specified number of color entries. + + The palette entries are initialized to white. + + \param ncolors represents the number of color entries in the color palette + \returns a new SDL_Palette structure on success or NULL on failure (e.g. if + there wasn't enough memory); call SDL_GetError() for more + information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FreePalette + + + + Free an SDL_PixelFormat structure allocated by SDL_AllocFormat(). + + \param format the SDL_PixelFormat structure to free + + \since This function is available since SDL 2.0.0. + + \sa SDL_AllocFormat + + + + Create an SDL_PixelFormat structure corresponding to a pixel format. + + Returned structure may come from a shared global cache (i.e. not newly + allocated), and hence should not be modified, especially the palette. Weird + errors such as `Blit combination not supported` may occur. + + \param pixel_format one of the SDL_PixelFormatEnum values + \returns the new SDL_PixelFormat structure or NULL on failure; call + SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_FreeFormat + + + + Convert a bpp value and RGBA masks to an enumerated pixel format. + + This will return `SDL_PIXELFORMAT_UNKNOWN` if the conversion wasn't + possible. + + \param bpp a bits per pixel value; usually 15, 16, or 32 + \param Rmask the red mask for the format + \param Gmask the green mask for the format + \param Bmask the blue mask for the format + \param Amask the alpha mask for the format + \returns one of the SDL_PixelFormatEnum values + + \since This function is available since SDL 2.0.0. + + \sa SDL_PixelFormatEnumToMasks + + + + Convert one of the enumerated pixel formats to a bpp value and RGBA masks. + + \param format one of the SDL_PixelFormatEnum values + \param bpp a bits per pixel value; usually 15, 16, or 32 + \param Rmask a pointer filled in with the red mask for the format + \param Gmask a pointer filled in with the green mask for the format + \param Bmask a pointer filled in with the blue mask for the format + \param Amask a pointer filled in with the alpha mask for the format + \returns SDL_TRUE on success or SDL_FALSE if the conversion wasn't + possible; call SDL_GetError() for more information. + + \since This function is available since SDL 2.0.0. + + \sa SDL_MasksToPixelFormatEnum + + + + Get the human readable name of a pixel format. + + \param format the pixel format to query + \returns the human readable name of the specified pixel format or + `SDL_PIXELFORMAT_UNKNOWN` if the format isn't recognized. + + \since This function is available since SDL 2.0.0. + + + +\note Everything in the pixel format structure is read-only. + + + +The bits of this structure can be directly reinterpreted as an integer-packed +color which uses the SDL_PIXELFORMAT_RGBA32 format (SDL_PIXELFORMAT_ABGR8888 +on little-endian systems and SDL_PIXELFORMAT_RGBA8888 on big-endian systems). + + + + If a + b would overflow, return -1. Otherwise store a + b via ret + and return 0. + + \since This function is available since SDL 2.24.0. + + + + If a * b would overflow, return -1. Otherwise store a * b via ret + and return 0. + + \since This function is available since SDL 2.24.0. + + + + This function converts a string between encodings in one pass, returning a + string that must be freed with SDL_free() or NULL on error. + + \since This function is available since SDL 2.0.0. + + + + Get the number of outstanding (unfreed) allocations + + \since This function is available since SDL 2.0.7. + + + + Replace SDL's memory allocation functions with a custom set + + \since This function is available since SDL 2.0.7. + + + + Get the current set of SDL memory functions + + \since This function is available since SDL 2.0.7. + + + + Get the original set of SDL memory functions + + \since This function is available since SDL 2.24.0. + + + +\endcond + \file begin_code.h + + This file sets things up for C dynamic library function definitions, + static inlined functions, and structures aligned at 4-byte alignment. + If you don't like ugly C preprocessor code, don't look at this file. :) + + + +\name Floating-point constants + +\cond + + +\brief An unsigned 64-bit integer type. + + + +\brief A signed 64-bit integer type. + + + +\brief An unsigned 32-bit integer type. + + + +\brief A signed 32-bit integer type. + + + +\brief An unsigned 16-bit integer type. + + + +\brief A signed 16-bit integer type. + + + +\brief An unsigned 8-bit integer type. + + + +\brief A signed 8-bit integer type. + + + + \file close_code.h + + This file reverses the effects of begin_code.h and should be included + after you finish any function and structure declarations in your headers + + + + \file SDL_stdinc.h + + This is a general header that includes C language support. + + \file SDL_platform.h + + Try to get a standard set of platform defines. + + \file begin_code.h + + This file sets things up for C dynamic library function definitions, + static inlined functions, and structures aligned at 4-byte alignment. + If you don't like ugly C preprocessor code, don't look at this file. :) + + Get the name of the platform. + + Here are the names returned for some (but not all) supported platforms: + + - "Windows" + - "Mac OS X" + - "Linux" + - "iOS" + - "Android" + + \returns the name of the platform. If the correct platform name is not + available, returns a string beginning with the text "Unknown". + + \since This function is available since SDL 2.0.0. + + + +*!************************************************************************* + + + +Marks the application to stop at the end of the current frame. + + + + +Whether or not the application is currently in fullscreen mode or not. + + + + +Retrieves the designated height of the current window. + + + + +Retrieves the designated width of the current window. + + + + +Whether or not the engine is in a paused state where script updates and +physics are not in play. + + + + +Whether or not the engine is playing. This will always be true on Publish. +On Debug/Release builds, this is true when the editor is in Play Mode. It +will also be true even if the editor is in Play Mode but is paused. + + + + +Static class that contains useful properties for querying the state of the +engine. + + + + +Sets the parent of this Transform component. + + +Entity that contains the Transform component that this Transform will be +parented to. If null, unparenting will occur. + + +If true, the transform values of this Transform component will retain their +pre-parent-change global transforms. The local transform values will be +modified to ensure that the global transforms do not change. + + + + +Parent Transform that affects this Transform. + + + + +Global scale stored by this Transform. + + + + +Global euler angle rotations stored by this Transform. + + + + +Global rotation quaternion stored by this Transform. + + + + +Global position stored by this Transform. + + + + +Local scale stored by this Transform. + + + + +Local euler angle rotations stored by this Transform. + + + + +Local rotation quaternion stored by this Transform. + + + + +Local position stored by this Transform. + + + + +Constructs a Transform Component that represents a native Transform component +tied to the specified Entity. + + Entity that this Component will be tied to. + + + +CLR version of the SHADE Engine's TransformComponent. + + + + +Compares if two float values are close enough to be the same with the +specified tolerance value. + + One of the values to compare. + The other value to compare. + Tolerance for floating point comparison. + True if a and b are practically the same. + + + +Compares if two float values are close enough to be the same with a tolerance +of Epsilon. + + One of the values to compare. + The other value to compare. + True if a and b are practically the same. + + + +Calculates the linear parameter t that produces the interpolant value within +the range [a, b]. + + Start value. + End value. + Value between start and end. + Percentage of value between start and end. + + + +Linearly interpolates between a and b by t. +The parameter t is not clamped and a value based on a and b is supported. +If t is less than zero, or greater than one, then LerpUnclamped will result +in a return value outside the range a to b. + + The start value. + The end value. + The interpolation value between the two float. + The interpolated float result between the two float values. + + + +Linearly interpolates between a and b by t. +The parameter t is clamped to the range [0, 1]. + + The start value. + The end value. + The interpolation value between the two float. + The interpolated float result between the two float values. + + + +Converts an angle from radian representation to degree representation. + + Radian-based angle to convert. + The specified angle in degrees. + + + +Converts an angle from degree representation to radian representation. + + Degree-based angle to convert. + The specified angle in radians. + + + +Wraps a value if they get to low or too high. + + Value to wrap. + Minimum value to wrap at. + Maximum value to wrap at. + Wrapped value. + + + +Small value used for single precision floating point comparisons. + + + + +Radians-to-degrees conversion constant + + + + +Degrees-to-radians conversion constant + + + + +Contains utility Math functions. + + + + +Logs a native exception that is formatted nicely to the output. + + Native exception to log. + Name of the one responsible for the exception. + + + +Logs an exception that is formatted nicely to the output. + + Name of the one responsible for the exception. + Exception to log. + + + +Logs a native exception that is formatted nicely to the output. +Equivalent to calling +LogException(exception, Convert::ToNative(thrower->GetType()->Name)); + + Native exception to log. + +Object that threw the exception to label the exception message. +The name of the object will be used. + + + + +Logs an exception that is formatted nicely to the output. + + Exception to log. + +Object that threw the exception to label the exception message. +The name of the object will be used. + + + + +Logs an exception that is formatted nicely to the output. + + Exception to log. + + + +Logs a error message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that threw the error to label the error message. +The name of the object will be used. + + + + +Logs a error message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that threw the error to label the error message. +The name of the object will be used. + + + + +Logs a error message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Object that threw the error to label the error message. +The name of the object will be used. + + + + +Logs a error message to the output. + + The string to output. + + + +Logs a error message to the output. + + The string to output. + + + +Logs a warning message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that threw the warning to label the warning message. +The name of the object will be used. + + + + +Logs a warning message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that threw the warning to label the warning message. +The name of the object will be used. + + + + +Logs a warning message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Object that threw the warning to label the warning message. +The name of the object will be used. + + + + +Logs a warning message to the output. + + The string to output. + + + +Logs a warning message to the output. + + The string to output. + + + +Logs a message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that sent the message to label the message. +The name of the object will be used. + + + + +Logs a message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Name of the object that sent the message to label the message. +The name of the object will be used. + + + + +Logs a message to the output with a label such that it looks like this: +"[Label] Message" + + The string to output. + +Object that sent the message to label the message. +The name of the object will be used. + + + + +Logs a message to the output. + + The string to output. + + + +Logs a message to the output. + + The string to output. + + + +Static class that contains the functions for working with time. + + + + +Material used to render this Renderable. + + + + +Material used to render this Renderable. + + + + +Mesh used to render this Renderable. + + + + +Constructs a Renderable Component that represents a native Renderable +component tied to the specified Entity. + + Entity that this Component will be tied to. + + + +CLR version of the SHADE Engine's SHRenderableComponent. + + + + +Retrieves the value of a specified property on the material. + + Type of property to get. + Name of the property to get. + Value of that property on the material. + +If this Material object is invalid. + + +If the name or type was specified that does not match the material's shader's +defined properties. + + + + +Set the value of a specific property. + + Type of property to set. + Name of the property to set. + Value to set te property to. + +If this Material object is invalid. + + +If the name or type was specified that does not match the material's shader's +defined properties. + + + + +Constructor for the Material + + Handle to the native material object. + + + +Managed counterpart of the native MaterialInstance object containing material +data that can be fed to Renderables for rendering. + + + + +Constructor for the Mesh + + Handle to the mesh object. + + + +Managed counterpart of the native Mesh object containing vertex data that can +be fed to Renderables for rendering. + + + +@brief Decomposes a transformation matrix into translation, orientation and scale. +@param[out] scale The scaling factor of the matrix. +@param[out] orientation The orientation of the matrix. +@param[out] translation The translation of the matrix. +@return True if decomposition was successful. + + + +@brief Decomposes a transformation matrix into translation, euler angles and scale. +@param[out] scale The scaling factor of the matrix. +@param[out] rotation The euler angles of the matrix. +@param[out] translation The translation of the matrix. +@return True if decomposition was successful. + + + +@brief Interface for a Column-Major Row Vector 4x4 Matrix. + + + + +Constructs a RigidBody Component that represents a native +SHRigidBodyComponent component tied to the specified Entity. + + Entity that this Component will be tied to. + + + +CLR version of the the SHADE Engine's SHRigidBodyComponent. + + + + +Creates an instance of the Managed representation of a Component with a +native Entity. + + Type of Component to create. + Native Entity that this Component is tied to. + The created Managed representation of the Component. + + + +Static constructor to initialize static data + + + + +Pointer to a function for Component manipulation operations. + + +Contains a set of Component related data used for resolving operations for +each Component. + + + + +Removes a Component from the specified Entity. + + Type of the Component to remove. + +Entity object that should have the specified Component removed from/ + + + + +Checks if the specified Entity has the specified Component. + + Type of the Component to check for. + Entity object to check for the Component. + +True if the specified Entity has the specified Component. False otherwise. + + + + +Ensures a Component on the specified Entity. + + Type of the Component to ensure. + Entity object to ensure the Component on. + Reference to the Component. + + + +Retrieves the first Component from the specified GameObjectt's children that +matches the specified type. + + Type of the Component to get. + Entity object to get the Component from. + +Reference to the Component or null if the Entity does not have the +specified Component. + + + + +Gets a Component from the specified Entity. + + Type of the Component to get. + Entity object to get the Component from. + +Reference to the Component or null if the Entity does not have the +specified Component. + + + + +Adds a Component to the specified Entity. + + Type of the Component to add. + +Entity object that should have the specified Component added to. + + Reference to the Component that was added. + + + +Static class which contains functions that map Pls::ECS's Component manipulation +functions to managed generic functions. + + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Entity that this Component belongs to. + + + + +Constructor for BaseComponent to tie it to a specific Entity. +Constructors of derived Components should call this Constructor. + + Entity that this Component will be tied to. + + + +Implicit conversion operator to enable checking if a component is null. + + Component to check. + + + +Removes all Scripts of the specified type from this GameObject. + + Type of PLushieScripts to remove. + + + +Retrieves a immutable list of Scripts of the specified type from this +GameObject. + + Type of Scripts to Get. + Immutable list of Scripts of the specified type. + + + +Retrieves a Script of the specified type from this GameObject. +If multiple Scripts of the same specified type are added on the same +GameObject, this will retrieve the first one added. + + Type of Script to add. + Reference to the Script to retrieve. + + + +Adds a Script of the specified type to this GameObject. + + Type of Script to add. + Reference to the created Script. + + + +Removes a Component from this GameObject. If no Component exists to begin +with, nothing happens. + + Type of the Component to get. + + + +Gets a Component from this GameObject. + + Type of the Component to get. + +Reference to the Component or null if this GameObject does not have the +specified Component. + + + + +Adds a Component to this GameObject. + + Type of the Component to add. + Reference to the Component that was added. + + + +Retrieves the GameObject that this Component belongs to. + + + + +Class that serves as the base for a wrapper class to Components in native code. + + + + +Called when the attached GameObject has a Collider and leaves a +collision with another GameObject with a Collider2D. + + Information on the collision event. + + + +Called when the attached GameObject has a Collider and collides with +another GameObject with a Collider in subsequent frames of collision. + + Information on the collision event. + + + +Called when the attached GameObject has a Collider and collides with +another GameObject with a Collider in the first frame of collision. + + Information on the collision event. + + + +Called when the attached GameObject has a trigger Collider and leaves a +collision with another GameObject with a Collider2D. + + Information on the collision event. + + + +Called when the attached GameObject has a trigger Collider and collides with +another GameObject with a Collider in subsequent frames of collision. + + Information on the collision event. + + + +Called when the attached GameObject has a trigger Collider and collides with +another GameObject with a Collider in the first frame of collision. + + Information on the collision event. + + + +Called just before the end of the frame where the attached GameObject or +this script is destroyed directly or indirectly due to destruction of the +owner. + + + + +Called every frame after physics and collision resolution but before +rendering. + + + + +Called every frame before physics and collision resolution. + + + + +Called every frame in sync with Physics update steps and thus in most cases +will execute more than update() will. This will be called immediately before +a Physics update step. + + + + +Called on the first frame that the attached GameObject is active but always +after Awake(). + + + + +Called on the first frame that the attached GameObject is active if they are +a part of the scene. + + + + +Called immediately once this script is detached from a GameObject. + + + + +Called immediately once this script is attached to a GameObject. + + + + +Constructor for Script to tie it to a specific GameObject. +Constructors of derived Scripts should call this Constructor. + + +GameObject that this Script will be tied to. + + + + +Used to call onTriggerExit(). This should be called when a trigger-type +collision is detected between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onTriggerStay(). This should be called when a trigger-type +collision is detected between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onTriggerEnter(). This should be called when a trigger-type +collision is detected between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onCollisionExit(). This should be called when a collision ends +between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onCollisionStay(). This should be called when a collision is +persistent between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onCollisionEnter(). This should be called when a collision is +detected between the attached GameObject and another GameObject. + + Information on the collision event. + + + +Used to call onDestroy(). This should be called at the end of the frame +where the attached GameObject or this script is destroyed directly or +indirectly due to destruction of the owner. + + + + +Used to call lateUpdate(). This should be called every frame after physics +and collision resolution but before rendering. + + + + +Used to call update(). This should be called every frame before physics and +collision resolution. + + + + +Used to call fixedUpdate(). This should be called in sync with Physics +update steps and thus in most cases will execute more than Update() will. +This will be called immediately before a Physics update step. + + + + +Used to call start(). This should be called on the first frame that the +attached GameObject is active but always after Awake(). + + + + +Used to call awake(). This should be called on the first frame that the +attached GameObject is active if they are a part of the scene. + + + + +Used to call onDetached(). This is called immediately when this script is +detached from a GameObject. + + + + +Used to call onAttached(). This is called immediately when this script is +attached to a GameObject. + + + + +Implicit conversion operator to enable checking if a component is null. + + Component to check. + + + +Removes all Scripts of the specified type from this GameObject. + + +Type of script to remove. +This needs to be a default constructable Script. + + + + +Retrieves a immutable list of scripts from the specified Entity that +matches the specified type. +
+Note that this function allocates. It should be used sparingly. +
+ +Type of scripts to get. +This needs to be a default constructable Script. + + +Immutable list of references to scripts of the specified type. + +
+ + +Retrieves the first Script from this GameObject's children that matches the +specified type. + + +Type of script to get. +This needs to be a default constructable Script. + + Reference to the script added + + + +Retrieves the first Script from this GameObject that matches the specified +type. + + +Type of script to get. +This needs to be a default constructable Script. + + Reference to the script added + + + +Adds a Script to this GameObject. + + +Type of script to add. +This needs to be a default constructable Script. + + Reference to the script added + + + +Removes a Component from the GameObject that this Script belongs to. + + +Type of the Component to remove. Must be derived from BaseComponent. + + + + +Ensures a Component on the GameObject that this Script belongs to. + + +Type of the Component to ensure. Must be derived from BaseComponent. + + Reference to the Component. + + + +Retrieves the first Component from this GameObject's children that matches +the specified type. + + +Type of the Component to get. Must be derived from BaseComponent. + + Reference to the Component that was retrieved. + + + +Gets a Component from the GameObject that this Script belongs to. + + +Type of the Component to get. Must be derived from BaseComponent. + + Reference to the Component that was retrieved. + + + +Adds a Component to the GameObject that this Script belongs to. + + +Type of the Component to add. Must be derived from BaseComponent. + + Reference to the Component that was added. + + + +GameObject that this Script belongs to. + + + + +Class that forms the basis of all "script"-objects that can be attached to +Entities to update each Entity's Components via C# code. + + + + +The RigidBody that you are colliding with. + + + + +The CollisionShape of the Collider that you are colliding with. + + + + +The Collider that you are colliding with. + + + + +The GameObject whose collider you are colliding with. + + + + +Struct that describes a collision + + + + +Checks if two GameObject references are different. + + GameObject to check. + Another GameObject to check with. + True if both Components are different. + + + +Checks if two GameObject references are the same. + + GameObject to check. + Another GameObject to check with. + True if both Components are the same. + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Retrieves the native Entity object that this GameObject represents. + + Native Entity object that this GameObject represents. + + + +Retrieves the CLR Entity object that this GameObject represents. + + Entity object that this GameObject represents. + + + +Constructor for the GameObject. + + +Managed numerical representation of the ECS Entity that this GameObject +should represent. + + + + +Constructor for the GameObject. + + +The ECS Entity that this GameObject should represent. + + + + +Removes all Scripts of the specified type from this GameObject. + + Type of PLushieScripts to remove. + + + +Retrieves a immutable list of Scripts of the specified type from this +GameObject. + + Type of Scripts to retrieve. + Immutable list of Scripts of the specified type. + + + +Retrieves a Script of the specified type from child GameObjects. +If multiple Scripts of the same specified type are added on the same +child GameObject, this will retrieve the first one added. + + Type of Script to retrieve. + Reference to the Script to retrieve. + + + +Retrieves a Script of the specified type from this GameObject. +If multiple Scripts of the same specified type are added on the same +GameObject, this will retrieve the first one added. + + Type of Script to retrieve. + Reference to the Script to retrieve. + + + +Adds a Script of the specified type to this GameObject. + + Type of Script to add. + Reference to the created Script. + + + +Removes a Component from this GameObject. If no Component exists to begin +with, nothing happens. + + Type of the Component to get. + + + +Ensures a Component on this GameObject. + + Type of the Component to ensure. + +Reference to the Component. + + + + +Retrieves the first Component from this GameObject's children that matches +the specified type. + + Type of the Component to get. + +Reference to the Component or null if neither of this GameObject's children +does not have the specified Component. + + + + +Gets a Component from this GameObject. + + Type of the Component to get. + +Reference to the Component or null if this GameObject does not have the +specified Component. + + + + +Adds a Component to this GameObject. + + Type of the Component to add. + Reference to the Component that was added. + + + +Sets the active state of this GameObject. +
+The actual "activeness" of this GameObject is still dependent on the parents' +active states. +
+ +Whether to activate or deactivate this GameObject. + +
+ + +Sets the name of this GameObject. + + The name to set. + + + +Native Entity ID value for this GameObject. + + + + +Whether or not this Entity is active in the Scene hierarchy. + + + + +Whether or not this Entity alone, is active. This does not mean that this +object is active in the scene. For example, if this Entity's parent is not +active, then this Entity would also be not active. + + + + +Name of the object that this Entity represents. + + + + +Retrieves a GameObject with the specified name. If there are multiple +GameObjects with the same name, the first found GameObject will be retrieved. +There is no guaranteed order of which GameObject is considered "first". + + Name of the GameObject to find. + GameObject that has the specified name. Null if not found. + + + +Destroys the specified GameObject. Note that the specified GameObject will no +longer be a valid GameObject after this function is called. + + The GameObject to be destroyed. + + + +Creates a new GameObject in the current Scene. If multiple Scenes are loaded, +and you would like to create an object in a specific Scene, call the Scene's +CreateGameObject(). + + GameObject that represents the newly created GameObject. + + + +Lightweight object for an PlushieEngine Entity that allows for easy access +to Component and Script operations. + + + + +Constructor for a Tooltip attribute that fills in the description. + + Text to be shown when a field is hovered. + + + +Maximum value for the Ranged field. + + + + +Minimum value for the Ranged field. + + + + +Simple attribute to constrain the range of values for a field on the editor. + + + + +Converts from a native std::Stringto a managed String. + + The native std::string to convert from. + Managed copy of a native std::string. + + + +Converts from a managed String to a native std::string. + + The managed String to convert from. + Native copy of a managed String. + + + +Converts from a native Vector2 to a managed Vector2. + + The native Vector2 to convert from. + Managed copy of a native Vector2. + + + +Converts from a native Quaternion to a managed Quaternion. + + The native Quaternion to convert from. + Managed copy of a native Quaternion. + + + +Converts from a managed Quaternion to a native Quaternion. + + The managed Quaternion to convert from. + Native copy of a managed Quaternion. + + + +Converts from a native Vector2 to a managed Vector2. + + The native Vector2 to convert from. + Managed copy of a native Vector2. + + + +Converts from a managed Vector2 to a native Vector2. + + The managed Vector2 to convert from. + Native copy of a managed Vector2. + + + +Converts from a native Vector3 to a managed Vector3. + + The native Vector3 to convert from. + Managed copy of a native Vector3. + + + +Converts from a managed Vector3 to a native Vector3. + + The managed Vector3 to convert from. + Native copy of a managed Vector3. + + + +Converts from a native Entity to a managed Entity (UInt32). + + Native Entity to convert from. + Managed representation of the specified Entity. + + + +Provides functions easy and consistent syntax for converting between custom +managed and native types that are aligned. + + + + +Converts to true if this is a valid Handle. + + + + +The library that the handle was issued by. + + + + +The internal ID of the handle. + + + + +Creates a ray starting at origin along direction. + + Source of the ray. + Direction the ray travels in. + + + +The direction that a ray travels in. + + + + +The start point of the ray. + + + + +CLR version of the the SHADE Engine's Ray class that represents a ray in +3-Dimensional space. + + + + +Are two quaternions equal to each other? + + Left-hand side quaternion. + Right-hand side quaternion. + + + +Combines rotations lhs and rhs. + + Left-hand side quaternion. + Right-hand side quaternion. + + + +Spherically interpolates between a and b by t. The parameter t is not clamped. + + + + +Spherically interpolates between quaternions a and b by ratio t. The parameter t is clamped to the range [0, 1]. + + Start value, returned when t = 0. + End value, returned when t = 1. + Interpolation ratio. + A quaternion spherically interpolated between quaternions a and b. + + + +Rotates a rotation from towards to.
+The from quaternion is rotated towards to by an angular step of maxDegreesDelta (but note that the rotation will not overshoot). +Negative values of maxDegreesDelta will move away from to until the rotation is exactly the opposite direction. +
+
+ + +Converts this quaternion to one with the same orientation but with a magnitude of 1. + + + + +Creates a rotation with the specified forward and upwards directions.
+Z axis will be aligned with forward, X axis aligned with cross product between forward and upwards, and Y axis aligned with cross product between Z and X. +
+
+ + +Interpolates between a and b by t and normalizes the result afterwards. The parameter t is not clamped. + + + + +Interpolates between a and b by t and normalizes the result afterwards. The parameter t is clamped to the range [0, 1]. + + Start value, returned when t = 0. + End value, returned when t = 1. + Interpolation ratio. + A quaternion interpolated between quaternions a and b. + + + +Returns the Inverse of rotation. + + + + +Creates a rotation which rotates from fromDirection to toDirection. + + + + +Returns a rotation that rotates y degrees around the y axis, x degrees around the x axis, and z degrees around the z axis; applied in that order. + + + + +The dot product between two rotations. + + + + +Creates a rotation which rotates angle degrees around axis. + + + + +Returns the angle in degrees between two rotations a and b.
+ The angle in degrees between the two vectors. +
+ + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Converts a rotation to angle-axis representation (angles in degrees). + + + + +Creates a rotation with the specified forward and upwards directions.
+The result is applied to this quaternion. +If used to orient a Transform, the Z axis will be aligned with forward and the Y axis with upwards, assuming these vectors are orthogonal. +Logs an error if the forward direction is zero. +
+ The direction to look in. + The vector that defines in which direction up is. +
+ + +Creates a rotation which rotates from fromDirection to toDirection.
+Use this to create a rotation which starts at the first Vector (fromDirection) and rotates to the second Vector (toDirection). +These Vectors must be set up in a script. +
+
+ + +Constructor to construct a Quaternion with the specified components. + + X-coordinate to set. + Y-coordinate to set. + Z-coordinate to set. + W-coordinate to set. + + + +W-component of the Quaternion. Do not directly modify quaternions. + + + + +Z-component of the Quaternion. +Don't modify this directly unless you know quaternions inside out. + + + + +Y-component of the Quaternion. +Don't modify this directly unless you know quaternions inside out. + + + + +X-component of the Quaternion. +Don't modify this directly unless you know quaternions inside out. + + + + +Shorthand for writing Quaternion(0, 0, 0, 1). + + + + +CLR version of SHADE's Quaternion class that represents an orientation. +Designed to closely match Unity's Quaternion struct. + + + + +Explicit conversion operator to enable explicit casting from a Vector2 to a +Vector3. + + Vector2 to convert from. + + + +Explicit conversion operator to enable explicit casting from a Vector3 to a +Vector2. + + Vector3 to convert from. + + + +Checks if two Vector3s are not approximately equal. This is equivalent to +calling !Vector3.IsNear() with default tolerance values. + + Vector3 to compare. + Another Vector3 to compare. + +True if all components are not approximately equal within the default +tolerance value. + + + + +Checks if two Vector3s are approximately equal. This is equivalent to +calling Vector3.IsNear() with default tolerance values. + + Vector3 to compare. + Another Vector3 to compare. + +True if all components are approximately equal within the default +tolerance value. + + + + +Calculates the division of a Vector3 with a scalar value and returns +the result. + + Scalar to divide with. + Vector3 to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Vector3 with a scalar value and returns +the result. + + Vector3 to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the division of a Vector3 with a scalar value and returns +the result. + + Scalar to divide with. + Vector3 to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Vector3 with a scalar value and returns +the result. + + Vector3 to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the component-wise multiplication of two Vector3s and returns the +result. + + Vector3 to multiply with. + Another Vector3 to multiply with. + The result of rhs subtracted from lhs. + + + +Subtracts a Vector3 from another Vector3 and returns the result. + + Vector3 to subtract from. + Another Vector3 to subtract. + The result of rhs subtracted from lhs. + + + +Adds two Vector3s together and returns the result. + + Vector3 to add. + Another Vector3 to add. + The result of lhs added to rhs + + + +Moves a point current towards target. +Similar to Lerp(), however, the function will ensure that the distance never +exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the +vector away from target + + The current position of the point. + The target position to move to. + Maximum distance moved per call. + Vector representing the moved point. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. +Unlike Lerp(), t is not clamped to a range at all. + + The start Vector3, returned when t = 0.0f. + The end Vector3, returned when t = 1.0f. + Value used to interpolate between a and b. + The interpolated Vector3. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. + + The start Vector3, returned when t = 0.0f. + The end Vector3, returned when t = 1.0f. + +Value used to interpolate between a and b which is clamped to +the range[0, 1]. + + The interpolated Vector3. + + + +Computes and returns a Vector3 that is made from the largest components of +the two specified Vector3s. + + Vector3 to calculate maximum Vector3 with. + Another Vector3 to calculate maximum Vector3 with. + +The Vector3 that contains the largest components of the two specified +Vector3s. + + + + +Computes and returns a Vector3 that is made from the smallest components of +the two specified Vector3s. + + Vector3 to calculate minimum Vector3 with. + Another Vector3 to calculate minimum Vector3 with. + +The Vector3 that contains the smallest components of the two specified +Vector3s. + + + + +Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise +direction. + + A Vector3 to rotate. + +Angle to rotate the vector by in an anti-clockwise direction in degrees. + + The Vector3 that represents the rotated vector. + + + +Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise +direction. + + A Vector3 to rotate. + +Angle to rotate the vector by in an anti-clockwise direction in radians. + + The Vector3 that represents the rotated vector. + + + +Reflects a Vector3 across another Vector3. + + A Vector3 to reflect. + A normal to reflect the Vector3 across. + The Vector3 that represents vec reflected across normal. + + + +Computes and returns a Vector3 projection. + + Vector3 to project. + Vector3 to project onto. + The Vector3 that represents the projected vec onto direction. + + + +Computes and returns the cross product of 2 specified Vector3s. + + Vector3 to calculate cross product with. + Another Vector3 to calculate cross product with. + The cross product of the two Vector3s. + + + +Computes and returns the dot product of 2 specified Vector3s. + + Vector3 to calculate dot product with. + Another Vector3 to calculate dot product with. + Scalar value representing the dot product of the two Vector3s. + + + +Checks if two specified Vector3s are near in value. + + Vector3 to check if is near in value. + Another Vector3 to check if is near in value. + Amount of tolerance to do the comparison with. + +True if the two Vector3s are within the tolerance value specified + + + + +Checks if two specified Vector3s are near in value. + + Vector3 to check if is near in value. + Another Vector3 to check if is near in value. + +True if the two Vector3s are within the tolerance value specified + + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Checks if a specified point is near this Vector3 that represents a point. + + The other point to check if we are near. + +The amount of tolerance before we consider these points as "near". + + +True if this Vector3 representing a point and the specified point are within +the range of the specified tolerance. False otherwise. + + + + +Checks if a specified point is near this Vector3 that represents a point with +a tolerance value of PLS_EPSILON. + + The other point to check if we are near. + +True if this Vector3 representing a point and the specified point are within +the range of the specified tolerance. False otherwise. + + + + +Calculates and returns the angle of this vector from the right vector. This +function returns values between -180.0f and 180.0f. + + Returns the angle of this vector from the right vector in degrees. + + + +Calculates and returns the angle of this vector from the right vector. This +function returns values between -Math.PI and Math.PI. + + Returns the angle of this vector from the right vector in radians. + + + +Calculates and returns the squared magnitude of this Vector3. + + Returns the squared length of this Vector3. + + + +Calculates and returns the magnitude of this Vector3. Note that this function +incurs a performance cost from the square root calculation. If you do not +need the precise magnitude, consider using GetSqrMagnitude() instead. + + Returns the length of this Vector3. + + + +Creates a copy of this Vector3 and returns a normalized version. + + +Returns a normalised copy of this Vector3. +If this Vector3 is a zero vector, a zero vector will be returned. + + + + +Normalises this current Vector3. This changes the data of this Vector3. +If you would like to get a copy, use GetNormalised() instead. +This function does nothing to a zero vector. + + + + +Conversion constructor to construct a Vector3 using a Vector2. + + + + + +Constructor to construct a Vector3 with the specified components. + + X-coordinate to set. + Y-coordinate to set. + Z-coordinate to set. + + + +Constructor to construct a Vector3 with the specified components with the +Z-component set to 0.0f. + + X-coordinate to set. + Y-coordinate to set. + + + +Constructor to construct a Vector3 with the specified components with the +Y and Z-component set to 0.0f. + + X-coordinate to set. + + + +Z-component of the Vector3. + + + + +Y-component of the Vector3. + + + + +X-component of the Vector3. + + + + +Shorthand for writing Vector3(0, 0, 0). + + + + +Shorthand for writing Vector3(0, 1, 0). + + + + +Shorthand for writing Vector3(1, 0, 0). + + + + +Shorthand for writing Vector3(float.PositiveInfinity, +float.PositiveInfinity, float.PositiveInfinity). + + + + +Shorthand for writing Vector3(1, 1, 1). + + + + +Shorthand for writing Vector3(float.NegativeInfinity, +float.NegativeInfinity, float.NegativeInfinity). + + + + +Shorthand for writing Vector3(-1, 0, 0). + + + + +Shorthand for writing Vector3(0, 0, 1). + + + + +Shorthand for writing Vector3(0, -1, 0). + + + + +Shorthand for writing Vector3(0, 0, -1). + + + + +CLR version of SHADE Engine's Vector3 class that represents a 3-Dimensional Vector. +Designed to closely match Unity's Vector3 struct. + + + + +Checks if two Vector2s are not approximately equal. This is equivalent to +calling !Vector2.IsNear() with default tolerance values. + + Vector2 to compare. + Another Vector2 to compare. + +True if all components are not approximately equal within the default +tolerance value. + + + + +Checks if two Vector2s are approximately equal. This is equivalent to +calling Vector2.IsNear() with default tolerance values. + + Vector2 to compare. + Another Vector2 to compare. + +True if all components are approximately equal within the default +tolerance value. + + + + +Calculates the division of a Vector2 with a scalar value and returns +the result. + + Scalar to divide with. + Vector2 to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Vector2 with a scalar value and returns +the result. + + Vector2 to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the division of a Vector2 with a scalar value and returns +the result. + + Scalar to divide with. + Vector2 to divide with. + The result of the scalar division. + + + +Calculates the multiplication of a Vector2 with a scalar value and returns +the result. + + Vector2 to multiply with. + Scalar to multiply with. + The result of the scalar multiplication. + + + +Calculates the component-wise multiplication of two Vector2s and returns the +result. + + Vector2 to multiply with. + Another Vector2 to multiply with. + The result of rhs subtracted from lhs. + + + +Subtracts a Vector2 from another Vector2 and returns the result. + + Vector2 to subtract from. + Another Vector2 to subtract. + The result of rhs subtracted from lhs. + + + +Adds two Vector2s together and returns the result. + + Vector2 to add. + Another Vector2 to add. + The result of lhs added to rhs + + + +Moves a point current towards target. +Similar to Lerp(), however, the function will ensure that the distance never +exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the +vector away from target + + The current position of the point. + The target position to move to. + Maximum distance moved per call. + Vector representing the moved point. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. +Unlike Lerp(), t is not clamped to a range at all. + + The start Vector2, returned when t = 0.0f. + The end Vector2, returned when t = 1.0f. + Value used to interpolate between a and b. + The interpolated Vector2. + + + +Linearly interpolates between two specified points. +This is most commonly used to find a point some fraction of the way along a +line between two endpoints. + + The start Vector2, returned when t = 0.0f. + The end Vector2, returned when t = 1.0f. + +Value used to interpolate between a and b which is clamped to +the range[0, 1]. + + The interpolated Vector2. + + + +Computes and returns a Vector2 that is made from the largest components of +the two specified Vector2s. + + Vector2 to calculate maximum Vector2 with. + Another Vector2 to calculate maximum Vector2 with. + +The Vector2 that contains the largest components of the two specified +Vector2s. + + + + +Computes and returns a Vector2 that is made from the smallest components of +the two specified Vector2s. + + Vector2 to calculate minimum Vector2 with. + Another Vector2 to calculate minimum Vector2 with. + +The Vector2 that contains the smallest components of the two specified +Vector2s. + + + + +Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise +direction. + + A Vector2 to rotate. + +Angle to rotate the vector by in an anti-clockwise direction in degrees. + + The Vector2 that represents the rotated vector. + + + +Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise +direction. + + A Vector2 to rotate. + +Angle to rotate the vector by in an anti-clockwise direction in radians. + + The Vector2 that represents the rotated vector. + + + +Reflects a Vector2 across another Vector2. + + A Vector2 to reflect. + A normal to reflect the Vector2 across. + The Vector2 that represents vec reflected across normal. + + + +Computes and returns a Vector2 projection. + + Vector2 to project. + Vector2 to project onto. + The Vector2 that represents the projected vec onto direction. + + + +Computes a perpendicular Vector2 to the specified Vector2. + + Vector2 to find a perpendicular of. + +Whether the inward perpendicular Vector is retrieved. If true, the +resultant vector is rotated 90-degrees in a counter-clockwise. + + The perpendicular Vector2 relative to the specified Vector2. + + + + +Computes the inward perpendicular Vector2 to the specified Vector2. +Equivalent to calling Perpendicular(lhs, true). This means, the +resultant Vector2 is rotated 90-degrees in a counter-clockwise. + + Vector2 to find a perpendicular of. + +The perpendicular Vector2 relative to the specified Vector2. + + + + +Computes and returns the dot product of 2 specified Vector2s. + + Vector2 to calculate dot product with. + Another Vector2 to calculate dot product with. + +Scalar value representing the dot product of the two Vector2s. + + + + +Checks if two specified Vector2s are near in value. + + Vector2 to check if is near in value. + Another Vector2 to check if is near in value. + +Amount of tolerance to do the comparison with. + + +True if the two Vector2s are within the tolerance value specified + + + + +Checks if two specified Vector2s are near in value. + + Vector2 to check if is near in value. + Another Vector2 to check if is near in value. + +True if the two Vector2s are within the tolerance value specified + + + + +Gets a unique hash for this object. + + Unique hash for this object. + + + +Compares equality with another unboxed object. + + The unboxed object to compare with. + True if both objects are the same. + + + +Compares equality with an object of the same type. + + The object to compare with. + True if both objects are the same. + + + +Checks if a specified point is near this Vector2 that represents a point. + + The other point to check if we are near. + +The amount of tolerance before we consider these points as "near". + + +True if this Vector2 representing a point and the specified point are within +the range of the specified tolerance. False otherwise. + + + + +Checks if a specified point is near this Vector2 that represents a point with +a tolerance value of PLS_EPSILON. + + The other point to check if we are near. + +True if this Vector2 representing a point and the specified point are within +the range of the specified tolerance. False otherwise. + + + + +Calculates and returns the angle of this vector from the right vector. This +function returns values between -180.0f and 180.0f. + + Returns the angle of this vector from the right vector in degrees. + + + +Calculates and returns the angle of this vector from the right vector. This +function returns values between -Math.PI and Math.PI. + + Returns the angle of this vector from the right vector in radians. + + + +Calculates and returns the squared magnitude of this Vector2. + + Returns the squared length of this Vector2. + + + +Calculates and returns the magnitude of this Vector2. Note that this function +incurs a performance cost from the square root calculation. If you do not +need the precise magnitude, consider using GetSqrMagnitude() instead. + + Returns the length of this Vector2. + + + +Creates a copy of this Vector2 and returns a normalized version. + + +Returns a normalised copy of this Vector2. +If this Vector2 is a zero vector, a zero vector will be returned. + + + + +Normalises this current Vector2. This changes the data of this Vector2. +If you would like to get a copy, use GetNormalised() instead. +This function does nothing to a zero vector. + + + + +Constructor to construct a Vector2 with the specified components.. + + X-coordinate to set. + Y-coordinate to set. + + + +Constructor to construct a Vector2 with the specified components with the +Y-component set to 0.0f. + + X-coordinate to set. + + + +Y-component of the Vector2. + + + + +X-component of the Vector2. + + + + +Shorthand for writing Vector2(0, 0). + + + + +Shorthand for writing Vector2(0, 1). + + + + +Shorthand for writing Vector2(1, 0). + + + + +Shorthand for writing Vector2(float.PositiveInfinity, +float.PositiveInfinity). + + + + +Shorthand for writing Vector2(1, 1). + + + + +Shorthand for writing Vector2(float.NegativeInfinity, +float.NegativeInfinity). + + + + +Shorthand for writing Vector2(-1, 0). + + + + +Shorthand for writing Vector2(0, -1). + + + + +CLR version of SHADE Engine's Vector2 class that represents a 2-Dimensional Vector. +Designed to closely match Unity's Vector2 struct. + + + + +Checks if the specified entity is valid. This is done by checking if it +matches Pls::Entity::INVALID. + + The Entity to check. + True if the specified Entity is valid. + + + +Static class that contains useful utility functions for working with Entity. + + + + +Manages all resources in multiple ResourceLibraries. + + + + +Base class for SHResourceLibrary that holds information about the library type. + + + + +Template Specialization for Handle that represents a type-less Handle. + + + + +Converts to true if this is a valid Handle. + + + + +Native ID type of a handle + + + + +Base implementation of the Handle that is not templated to allow for holding +generic non-type-specific Handles. + + + + +Exception thrown when a generic Handle is being casted to the wrong type. + + + + +Exception thrown when an invalid Handle was dereferenced. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file From 976c2201459b5815dfbf61d73d03c54ff8d3af6f Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 17:19:18 +0800 Subject: [PATCH 051/113] Deserialization of SHMaterialSpec will return an empty SHMaterialSpec on failure now instead --- .../Serialization/SHSerializationHelper.hpp | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index f2c4572a..8f1b972c 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -267,22 +267,18 @@ namespace YAML static bool decode(YAML::Node const& node, SHMaterialSpec& rhs) { // Retrieve Shader Asset IDs - if (!node[VERT_SHADER_YAML_TAG.data()]) - return false; - rhs.vertexShader = node[VERT_SHADER_YAML_TAG.data()].as(); - if (!node[FRAG_SHADER_YAML_TAG.data()]) - return false; - rhs.fragShader = node[FRAG_SHADER_YAML_TAG.data()].as(); + if (node[VERT_SHADER_YAML_TAG.data()]) + rhs.vertexShader = node[VERT_SHADER_YAML_TAG.data()].as(); + if (node[FRAG_SHADER_YAML_TAG.data()]) + rhs.fragShader = node[FRAG_SHADER_YAML_TAG.data()].as(); // Retrieve Subpass - if (!node[SUBPASS_YAML_TAG.data()]) - return false; - rhs.subpassName = node[SUBPASS_YAML_TAG.data()].as(); + if (node[SUBPASS_YAML_TAG.data()]) + rhs.subpassName = node[SUBPASS_YAML_TAG.data()].as(); // Retrieve - if (!node[PROPS_YAML_TAG.data()]) - return false; - rhs.properties = node[PROPS_YAML_TAG.data()]; + if (node[PROPS_YAML_TAG.data()]) + rhs.properties = node[PROPS_YAML_TAG.data()]; return true; } From fc569736458292542cb8fe74d5ca812436442568 Mon Sep 17 00:00:00 2001 From: Glence Date: Wed, 2 Nov 2022 17:31:57 +0800 Subject: [PATCH 052/113] added getright in camera.cxx/.hxx PickandThrow is done adjusted thirdPersonCamera and PlayerController to work with each other --- .../Inspector/SHEditorComponentView.hpp | 4 + SHADE_Managed/src/Components/Camera.cxx | 9 ++ SHADE_Managed/src/Components/Camera.hxx | 2 + TempScriptsFolder/PickAndThrow.cs | 82 +++++++++++++++-- TempScriptsFolder/PlayerController.cs | 88 +++++++++++++++---- TempScriptsFolder/ThirdPersonCamera.cs | 40 +++++---- 6 files changed, 182 insertions(+), 43 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index 4777fc6a..12893de6 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -244,9 +244,12 @@ namespace SHADE SHCollider* collider = &component->GetCollider(i); auto cursorPos = ImGui::GetCursorPos(); + //collider->IsTrigger + if (collider->GetType() == SHCollider::Type::BOX) { SHEditorWidgets::BeginPanel(std::format("{} Box Collider #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); + SHEditorWidgets::CheckBox("Is Trigger", [collider]() {return collider->IsTrigger(); }, [collider](bool const& value) {collider->SetIsTrigger(value); }, "Is Trigger"); auto box = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragVec3 ( @@ -257,6 +260,7 @@ namespace SHADE else if (collider->GetType() == SHCollider::Type::SPHERE) { SHEditorWidgets::BeginPanel(std::format("{} Sphere Collider #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); + SHEditorWidgets::CheckBox("Is Trigger", [collider]() {return collider->IsTrigger(); }, [collider](bool const& value) {collider->SetIsTrigger(value); }, "Is Trigger"); auto sphere = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragFloat ( diff --git a/SHADE_Managed/src/Components/Camera.cxx b/SHADE_Managed/src/Components/Camera.cxx index 5e570cc1..0d0dbced 100644 --- a/SHADE_Managed/src/Components/Camera.cxx +++ b/SHADE_Managed/src/Components/Camera.cxx @@ -123,5 +123,14 @@ namespace SHADE } + Vector3 Camera::GetRight() + { + auto system = SHSystemManager::GetSystem(); + SHVec3 forward, up, right; + system->GetCameraAxis(*GetNativeComponent(), forward, right, up); + return Convert::ToCLI(right); + + } + } \ No newline at end of file diff --git a/SHADE_Managed/src/Components/Camera.hxx b/SHADE_Managed/src/Components/Camera.hxx index 257bff11..c6afeb6d 100644 --- a/SHADE_Managed/src/Components/Camera.hxx +++ b/SHADE_Managed/src/Components/Camera.hxx @@ -66,5 +66,7 @@ namespace SHADE void SetMainCamera(); void LookAt(Vector3 targetPosition); Vector3 GetForward(); + Vector3 GetRight(); + }; } \ No newline at end of file diff --git a/TempScriptsFolder/PickAndThrow.cs b/TempScriptsFolder/PickAndThrow.cs index 834a508c..09252247 100644 --- a/TempScriptsFolder/PickAndThrow.cs +++ b/TempScriptsFolder/PickAndThrow.cs @@ -5,26 +5,94 @@ using static PlayerController; public class PickAndThrow : Script { private PlayerController pc; - public uint itemEID; - Transform itemHoldLocation; + public GameObject item; + public Vector3 throwForce = new Vector3(200.0f, 300.0f, 200.0f); + private Transform itemTransform; + private RigidBody itemRidibody; + private Transform raccoonHoldLocation; + private float lastXDir; + private float lastZDir; + private bool inRange = false; public PickAndThrow(GameObject gameObj) : base(gameObj) { } + protected override void awake() { pc = GetScript(); - itemHoldLocation = GetComponentInChildren(); + raccoonHoldLocation = GetComponentInChildren(); + if (raccoonHoldLocation == null) + Debug.Log("CHILD EMPTY"); + itemTransform = item.GetComponent(); + if (itemTransform == null) + Debug.Log("Item transform EMPTY"); + itemRidibody = item.GetComponent(); + if (itemRidibody == null) + Debug.Log("Item rb EMPTY"); } protected override void update() { - if (pc != null && pc.holdItem) + if (!pc.isMoveKeyPress) { + if (pc.xAxisMove != 0) + { + lastXDir = pc.xAxisMove; + lastZDir = 0.0f; + } + if (pc.zAxisMove != 0) + { + lastXDir = 0.0f; + lastZDir = pc.zAxisMove; + } } + else + { + lastXDir = pc.xAxisMove; + lastZDir = pc.zAxisMove; + } + + if (pc != null && inRange && !pc.holdItem && Input.GetKey(Input.KeyCode.E)) + pc.holdItem = true; + + if (pc != null && itemRidibody != null && itemTransform!= null && pc.holdItem) + { + itemTransform.LocalPosition = raccoonHoldLocation.GlobalPosition; + itemRidibody.IsGravityEnabled = false; + itemRidibody.LinearVelocity = Vector3.Zero; + itemRidibody.AngularVelocity = Vector3.Zero; + + if (Input.GetMouseButtonDown(Input.MouseCode.LeftButton)) + { + pc.holdItem = false; + itemRidibody.IsGravityEnabled = true; + itemRidibody.AddForce(new Vector3(throwForce.x * lastXDir, throwForce.y, throwForce.z * lastZDir)); + itemRidibody.LinearVelocity += pc.rb.LinearVelocity; + Debug.Log($"x: {itemRidibody.LinearVelocity.x} z: {itemRidibody.LinearVelocity.z}"); + } + } + else if(!pc.holdItem) + itemRidibody.IsGravityEnabled = true; } protected override void onCollisionEnter(CollisionInfo info) { - if (info.GameObject.Name == "item" && Input.GetKey(Input.KeyCode.E)) + } + protected override void onTriggerEnter(CollisionInfo info) + { + Debug.Log("ENTER"); + if (info.GameObject == item && !pc.holdItem) { - pc.holdItem = true; - itemEID = info.GameObject.EntityId; + inRange = true; } } + protected override void onTriggerStay(CollisionInfo info) + { + Debug.Log("STAY"); + } + protected override void onTriggerExit(CollisionInfo info) + { + Debug.Log("EXIT"); + if (info.GameObject == item && !pc.holdItem) + { + inRange = false; + } + } + } \ No newline at end of file diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 36e19860..5566c411 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -12,17 +12,17 @@ public class PlayerController : Script RUNNING, JUMP, FALLING, - HOLDING, CAUGHT, TOTAL } - private RigidBody rb; + public RigidBody rb; private Transform tranform; + private Camera cam; //to be remove public float drag = 2.0f; - public bool holdItem = false; + public bool holdItem { get; set; } [SerializeField] [Tooltip("The current state fo the raccoon")] public RaccoonStates currentState = RaccoonStates.IDILE; @@ -41,9 +41,11 @@ public class PlayerController : Script private float oldForce; private float maxOldVel; private bool sprintIncreaseOnce = false; - private int xAxisMove; - private int zAxisMove; - private bool isMoveKeyPress = false; + + public float xAxisMove { get; set; } + public float zAxisMove { get; set; } + + public bool isMoveKeyPress { get; set; } [SerializeField] [Tooltip("curr not working")] @@ -68,12 +70,21 @@ public class PlayerController : Script protected override void awake() { + + isMoveKeyPress = false; + holdItem = false; //rigidbody check rb = GetComponent(); if (rb == null) Debug.LogError("RigidBody is NULL!"); else + { + rb.IsGravityEnabled = false; + rb.FreezeRotationX = true; + rb.FreezeRotationY = true; + rb.FreezeRotationZ = true; rb.Drag = drag; + } //Transform check tranform = GetComponent(); @@ -92,6 +103,9 @@ public class PlayerController : Script protected override void update() { + if (cam == null) + cam = GetComponentInChildren(); + //toRemove if (Input.GetKey(Input.KeyCode.G)) { @@ -117,20 +131,55 @@ public class PlayerController : Script private void MoveKey() { - if (Input.GetKey(Input.KeyCode.A)) - xAxisMove = -1; - else if (Input.GetKey(Input.KeyCode.D)) - xAxisMove = 1; - else - xAxisMove = 0; + /* if (Input.GetKey(Input.KeyCode.A)) + xAxisMove = -1; + else if (Input.GetKey(Input.KeyCode.D)) + xAxisMove = 1; + else + xAxisMove = 0; + if (Input.GetKey(Input.KeyCode.W)) + zAxisMove = -1; + else if (Input.GetKey(Input.KeyCode.S)) + zAxisMove = 1; + else + zAxisMove = 0;*/ + + + xAxisMove = 0; + zAxisMove = 0; if (Input.GetKey(Input.KeyCode.W)) - zAxisMove = -1; - else if (Input.GetKey(Input.KeyCode.S)) - zAxisMove = 1; - else - zAxisMove = 0; - + { + Vector3 camerAixs = cam.GetForward(); + camerAixs.y = 0; + camerAixs.Normalise(); + xAxisMove = camerAixs.x; + zAxisMove = camerAixs.z; + } + if (Input.GetKey(Input.KeyCode.S)) + { + Vector3 camerAixs = cam.GetForward(); + camerAixs.y = 0; + camerAixs.Normalise(); + xAxisMove = -camerAixs.x; + zAxisMove = -camerAixs.z; + } + if (Input.GetKey(Input.KeyCode.A)) + { + Vector3 camerAixs = cam.GetRight(); + camerAixs.y = 0; + camerAixs.Normalise(); + xAxisMove = -camerAixs.x; + zAxisMove = -camerAixs.z; + } + if (Input.GetKey(Input.KeyCode.D)) + { + Vector3 camerAixs = cam.GetRight(); + camerAixs.y = 0; + camerAixs.Normalise(); + xAxisMove = camerAixs.x; + zAxisMove = camerAixs.z; + } isMoveKeyPress = xAxisMove != 0 || zAxisMove != 0; if(isMoveKeyPress && currentState != RaccoonStates.RUNNING && isGrounded) @@ -169,7 +218,8 @@ public class PlayerController : Script if (Input.GetKey(Input.KeyCode.LeftShift) && isMoveKeyPress && isGrounded) { currentState = RaccoonStates.RUNNING; - if(!sprintIncreaseOnce) + holdItem = false; + if (!sprintIncreaseOnce) { sprintIncreaseOnce = true; oldForce = moveForce; diff --git a/TempScriptsFolder/ThirdPersonCamera.cs b/TempScriptsFolder/ThirdPersonCamera.cs index e3b043bd..618c562f 100644 --- a/TempScriptsFolder/ThirdPersonCamera.cs +++ b/TempScriptsFolder/ThirdPersonCamera.cs @@ -19,10 +19,13 @@ namespace SHADE_Scripting protected override void awake() { + AddComponent(); + if(!GetComponent()) { AddComponent(); } + GetComponent().SetMainCamera(); if (!GetComponent()) { AddComponent(); @@ -30,26 +33,29 @@ namespace SHADE_Scripting GetComponent().ArmLength = armLength; } - protected override void update() - { - CameraArm arm = GetComponent(); - if(arm) + protected override void update() + { + if (Input.GetMouseButton(Input.MouseCode.RightButton)) + { + CameraArm arm = GetComponent(); + if (arm) + { + Vector2 vel = Input.GetMouseVelocity(); + arm.Pitch -= vel.y * turnSpeedPitch * Time.DeltaTimeF; + arm.Yaw += vel.x * turnSpeedYaw * Time.DeltaTimeF; + + if (arm.Pitch > pitchClamp) { - Vector2 vel = Input.GetMouseVelocity(); - arm.Pitch -= vel.y * turnSpeedPitch * Time.DeltaTimeF; - arm.Yaw += vel.x * turnSpeedYaw * Time.DeltaTimeF; - - if(arm.Pitch > pitchClamp) - { - arm.Pitch = pitchClamp; - } - else if(arm.Pitch < -pitchClamp) - { - arm.Pitch = -pitchClamp; - } - + arm.Pitch = pitchClamp; } + else if (arm.Pitch < 0) + { + arm.Pitch = 0; + } + + } } + } } } From 4721a133e133b37edc0d2bee64867ab514361a38 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 17:43:28 +0800 Subject: [PATCH 053/113] Added a way to retrieve texture handles from texture indices --- .../MiddleEnd/Textures/SHTextureLibrary.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.h b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.h index 9357b0e0..5b7e4914 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.h @@ -129,6 +129,21 @@ namespace SHADE /* Getter Functions */ /*-----------------------------------------------------------------------------*/ Handle GetTextureDescriptorSetGroup() const noexcept { return texDescriptors; } + /***************************************************************************/ + /*! + * + \brief + Retrieves the texture handle at the specified texture index. + + \param textureId + Index of the texture to look for. + + \returns + Handle to the texture + + */ + /***************************************************************************/ + Handle GetTextureHandle(SHTexture::Index textureId) const { return texOrder[textureId]; }; private: /*-----------------------------------------------------------------------------*/ From c68c5adc0d3753ebbad5e8b35dc0b80ed66dba3e Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 2 Nov 2022 17:51:07 +0800 Subject: [PATCH 054/113] Added GetTextureHandle() to SHGraphicsSystem --- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 5 +++++ .../MiddleEnd/Interface/SHGraphicsSystem.h | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 97380fa3..bccece68 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -655,6 +655,11 @@ namespace SHADE ); } + Handle SHGraphicsSystem::GetTextureHandle(SHTexture::Index textureId) const + { + return texLibrary.GetTextureHandle(textureId); + } + #pragma endregion ADD_REMOVE #pragma region ROUTINES diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 3f899446..d5ac64dc 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -262,6 +262,21 @@ namespace SHADE */ /***************************************************************************/ void BuildTextures(); + /***************************************************************************/ + /*! + * + \brief + Retrieves the texture handle at the specified texture index. + + \param textureId + Index of the texture to look for. + + \returns + Handle to the texture + + */ + /***************************************************************************/ + Handle GetTextureHandle(SHTexture::Index textureId) const; void PrepareResize(uint32_t newWidth, uint32_t newHeight) noexcept; void HandleResize(void) noexcept; From 2beae24924f9f116f83704a88f828e9ec92fe0e1 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Wed, 2 Nov 2022 20:50:51 +0800 Subject: [PATCH 055/113] Removed Original mesh compiler, rewriting mesh loader to reflect ModelCompiler format --- .../src/Assets/Asset Types/SHMeshAsset.h | 42 ++-- .../Libraries/Compilers/SHMeshCompiler.cpp | 201 ------------------ .../Libraries/Compilers/SHMeshCompiler.h | 40 ---- .../Assets/Libraries/Loaders/SHMeshLoader.cpp | 123 ++++------- .../Assets/Libraries/Loaders/SHMeshLoader.h | 7 +- 5 files changed, 72 insertions(+), 341 deletions(-) delete mode 100644 SHADE_Engine/src/Assets/Libraries/Compilers/SHMeshCompiler.cpp delete mode 100644 SHADE_Engine/src/Assets/Libraries/Compilers/SHMeshCompiler.h diff --git a/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h index 20b442ba..0ef5267b 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h @@ -18,24 +18,32 @@ namespace SHADE { - struct SH_API SHMeshAssetHeader - { - uint32_t vertexCount; - uint32_t indexCount; - std::string name; - }; + struct SHMeshDataHeader + { + uint32_t vertexCount; + uint32_t indexCount; + uint32_t charCount; + }; - struct SH_API SHMeshAsset : SHAssetData - { - bool compiled; - bool changed; + struct SHMeshData + { + std::string name; + std::vector vertexPosition; + std::vector vertexTangent; + std::vector vertexNormal; + std::vector texCoords; + std::vector indices; + }; - SHMeshAssetHeader header; + struct SHMeshAssetHeader + { + size_t meshCount; + }; - std::vector vertexPosition; - std::vector vertexTangent; - std::vector vertexNormal; - std::vector texCoords; - std::vector indices; - }; + struct SH_API SHMeshAsset : SHAssetData + { + SHMeshAssetHeader header; + std::vector headers; + std::vector meshes; + }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/Compilers/SHMeshCompiler.cpp b/SHADE_Engine/src/Assets/Libraries/Compilers/SHMeshCompiler.cpp deleted file mode 100644 index 046ba767..00000000 --- a/SHADE_Engine/src/Assets/Libraries/Compilers/SHMeshCompiler.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/*************************************************************************//** - * \file SHMeshCompiler.cpp - * \author Loh Xiao Qi - * \date 30 September 2022 - * \brief Library to write data in SHMeshAsset into binary file for faster - * loading in the future - * - * - * 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 "SHpch.h" -#include "SHMeshCompiler.h" -#include "Graphics/MiddleEnd/Meshes/SHMeshData.h" -#include - -#include - -namespace SHADE -{ - - Assimp::Importer SHMeshCompiler::aiImporter; - - void SHMeshCompiler::ProcessNode(aiNode const& node, aiScene const& scene, MeshVectorRef meshes) noexcept - { - for (size_t i{ 0 }; i < node.mNumMeshes; ++i) - { - aiMesh* mesh = scene.mMeshes[node.mMeshes[i]]; - meshes.push_back(ProcessMesh(*mesh)); - } - - for (size_t i{ 0 }; i < node.mNumChildren; ++i) - { - ProcessNode(*node.mChildren[i], scene, meshes); - } - } - - void SHMeshCompiler::ExtractAnimations(aiScene const& scene, AnimVectorRef anims) noexcept - { - if (scene.HasAnimations()) - { - std::vector anims(scene.mNumAnimations); - for (auto i{ 0 }; i < scene.mNumAnimations; ++i) - { - auto const& anim{ *scene.mAnimations[i] }; - - anims[i].name = anim.mName.C_Str(); - - anims[i].duration = anim.mDuration; - anims[i].ticksPerSecond = anim.mTicksPerSecond; - - std::copy_n(anim.mChannels, anim.mNumChannels, anims[i].nodeChannels.data()); - std::copy_n(anim.mMeshChannels, anim.mNumMeshChannels, anims[i].meshChannels.data()); - std::copy_n(anim.mMorphMeshChannels, anim.mNumMorphMeshChannels, anims[i].morphMeshChannels.data()); - } - } - } - - SHMeshAsset* SHMeshCompiler::ProcessMesh(aiMesh const& mesh) noexcept - { - SHMeshAsset* result = new SHMeshAsset(); - result->compiled = false; - result->changed = false; - - for (size_t i{ 0 }; i < mesh.mNumVertices; ++i) - { - // Vertex position - SHVec3 vertex; - vertex.x = mesh.mVertices[i].x; - vertex.y = mesh.mVertices[i].y; - vertex.z = mesh.mVertices[i].z; - result->vertexPosition.push_back(vertex); - - // Tex coords - SHVec2 texCoord{ 0.f, 0.f }; - if (mesh.mTextureCoords[0]) - { - texCoord.x = mesh.mTextureCoords[0][i].x; - texCoord.y = mesh.mTextureCoords[0][i].y; - } - result->texCoords.push_back(texCoord); - - // Normals - SHVec3 normal{ 0.f, 0.f, 0.f }; - if (mesh.mNormals) - { - normal.x = mesh.mNormals[i].x; - normal.y = mesh.mNormals[i].y; - normal.z = mesh.mNormals[i].z; - } - result->vertexNormal.push_back(normal); - - // Tangent - SHVec3 tangent{ 0.f, 0.f, 0.f }; - if (mesh.mTangents) - { - tangent.x = mesh.mTangents[i].x; - tangent.y = mesh.mTangents[i].y; - tangent.z = mesh.mTangents[i].z; - } - result->vertexTangent.push_back(tangent); - } - - for (size_t i{ 0 }; i < mesh.mNumFaces; ++i) - { - aiFace face = mesh.mFaces[i]; - for (size_t j{ 0 }; j < face.mNumIndices; ++j) - { - result->indices.push_back(face.mIndices[j]); - } - } - - result->header.vertexCount = static_cast(result->vertexPosition.size()); - result->header.indexCount = static_cast(result->indices.size()); - result->header.name = mesh.mName.C_Str(); - - return result; - } - - void SHMeshCompiler::LoadFromFile(AssetPath path, MeshVectorRef meshes, AnimVectorRef anims) noexcept - { - const aiScene* scene = aiImporter.ReadFile(path.string().c_str(), - aiProcess_Triangulate // Make sure we get triangles rather than nvert polygons - | aiProcess_GenUVCoords // Convert any type of mapping to uv mapping - | aiProcess_TransformUVCoords // preprocess UV transformations (scaling, translation ...) - | aiProcess_FindInstances // search for instanced meshes and remove them by references to one master - | aiProcess_CalcTangentSpace // calculate tangents and bitangents if possible - | aiProcess_JoinIdenticalVertices // join identical vertices/ optimize indexing - | aiProcess_RemoveRedundantMaterials // remove redundant materials - | aiProcess_FindInvalidData // detect invalid model data, such as invalid normal vectors - | aiProcess_FlipUVs // flip the V to match the Vulkans way of doing UVs - ); - - if (!scene || !scene->HasMeshes()) - { - SHLOG_ERROR("ERROR in GLTF::ASSIMP: {}\nFile: {}", aiImporter.GetErrorString(), path.string()); - return; - } - - //ExtractAnimations(*scene, anims); - - ProcessNode(*scene->mRootNode, *scene, meshes); - - aiImporter.FreeScene(); - } - - std::optional SHMeshCompiler::CompileMeshBinary(SHMeshAsset const& asset, AssetPath path) noexcept - { - std::string newPath{ path.parent_path().string() + '/' }; - newPath += asset.header.name + MESH_EXTENSION.data(); - - std::ofstream file{ newPath, std::ios::out | std::ios::binary | std::ios::trunc }; - if (!file.is_open()) - { - SHLOG_ERROR("Unable to open file for writing mesh file: {}", path.string()); - } - - file.write( - reinterpret_cast(&(asset.header.vertexCount)), - sizeof(uint32_t) - ); - - file.write( - reinterpret_cast(&(asset.header.indexCount)), - sizeof(uint32_t) - ); - - auto const vertexVec3Byte{ sizeof(SHVec3) * asset.header.vertexCount }; - auto const vertexVec2Byte{ sizeof(SHVec2) * asset.header.vertexCount }; - - file.write( - reinterpret_cast(asset.vertexPosition.data()), - vertexVec3Byte - ); - - file.write( - reinterpret_cast(asset.vertexTangent.data()), - vertexVec3Byte - ); - - file.write( - reinterpret_cast(asset.vertexNormal.data()), - vertexVec3Byte - ); - - file.write( - reinterpret_cast(asset.texCoords.data()), - vertexVec2Byte - ); - - file.write( - reinterpret_cast(asset.indices.data()), - sizeof(uint32_t) * asset.header.indexCount - ); - - file.close(); - - return newPath; - } -} diff --git a/SHADE_Engine/src/Assets/Libraries/Compilers/SHMeshCompiler.h b/SHADE_Engine/src/Assets/Libraries/Compilers/SHMeshCompiler.h deleted file mode 100644 index 1af1298b..00000000 --- a/SHADE_Engine/src/Assets/Libraries/Compilers/SHMeshCompiler.h +++ /dev/null @@ -1,40 +0,0 @@ -/*************************************************************************//** - * \file SHMeshCompiler.h - * \author Loh Xiao Qi - * \date 30 September 2022 - * \brief Library to write data in SHMeshAsset into binary file for faster - * loading in the future - * - * - * 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 - -#include "Assets/Asset Types/SHAnimationAsset.h" -#include "Assets/Asset Types/SHMeshAsset.h" -#include "Assets/SHAssetMacros.h" - -namespace SHADE -{ - class SHMeshCompiler - { - - using MeshVectorRef = std::vector&; - using AnimVectorRef = std::vector&; - - static Assimp::Importer aiImporter; - static void ProcessNode(aiNode const& node, aiScene const& scene, MeshVectorRef meshes) noexcept; - static void ExtractAnimations(aiScene const& scene, AnimVectorRef anims) noexcept; - static SHMeshAsset* ProcessMesh(aiMesh const& mesh) noexcept; - - public: - static void LoadFromFile(AssetPath path, MeshVectorRef meshes, AnimVectorRef anims) noexcept; - static std::optional CompileMeshBinary(SHMeshAsset const& asset, AssetPath path) noexcept; - }; -} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.cpp index 52134440..7520bb84 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.cpp @@ -16,6 +16,36 @@ namespace SHADE { + void SHMeshLoader::ReadHeader(std::ifstream& file, SHMeshDataHeader& header) noexcept + { + file.read( + reinterpret_cast(&header), + sizeof(SHMeshDataHeader) + ); + } + + void SHMeshLoader::ReadData(std::ifstream& file, SHMeshDataHeader const& header, SHMeshData& data) noexcept + { + + auto const vertexVec3Byte{ sizeof(SHVec3) * header.vertexCount }; + auto const vertexVec2Byte{ sizeof(SHVec2) * header.indexCount }; + + data.vertexPosition.resize(header.vertexCount); + data.vertexTangent.resize(header.vertexCount); + data.vertexNormal.resize(header.vertexCount); + data.texCoords.resize(header.vertexCount); + data.indices.resize(header.indexCount); + + file.read(data.name.data(), header.charCount); + file.read(reinterpret_cast(data.vertexPosition.data()), vertexVec3Byte); + file.read(reinterpret_cast(data.vertexTangent.data()), vertexVec3Byte); + file.read(reinterpret_cast(data.vertexNormal.data()), vertexVec3Byte); + file.read(reinterpret_cast(data.texCoords.data()), vertexVec2Byte); + file.read(reinterpret_cast(data.indices.data()), sizeof(uint32_t) * header.indexCount); + + + } + void SHMeshLoader::LoadSHMesh(AssetPath path, SHMeshAsset& mesh) noexcept { std::ifstream file{ path.string(), std::ios::in | std::ios::binary }; @@ -25,46 +55,21 @@ namespace SHADE return; } - const std::string name{ path.stem().string() }; - file.seekg(0); - uint32_t vertCount, indexCount; - std::vector vertPos, vertTan, vertNorm; - std::vector texCoord; - std::vector indices; + file.read( + reinterpret_cast(&mesh.header), + sizeof(SHMeshAssetHeader) + ); - file.read(reinterpret_cast(&vertCount), sizeof(uint32_t)); - file.read(reinterpret_cast(&indexCount), sizeof(uint32_t)); - - auto const vertexVec3Byte{ sizeof(SHVec3) * vertCount }; - auto const vertexVec2Byte{ sizeof(SHVec2) * vertCount }; - - vertPos.resize(vertCount); - vertTan.resize(vertCount); - vertNorm.resize(vertCount); - texCoord.resize(vertCount); - indices.resize(indexCount); - - file.read(reinterpret_cast(vertPos.data()), vertexVec3Byte); - file.read(reinterpret_cast(vertTan.data()), vertexVec3Byte); - file.read(reinterpret_cast(vertNorm.data()), vertexVec3Byte); - file.read(reinterpret_cast(texCoord.data()), vertexVec2Byte); - file.read(reinterpret_cast(indices.data()), sizeof(uint32_t) * indexCount); - - mesh.compiled = true; - mesh.changed = false; - - mesh.header.indexCount = indexCount; - mesh.header.vertexCount = vertCount; - mesh.header.name = name; - - mesh.vertexPosition = std::move(vertPos); - mesh.vertexTangent = std::move(vertTan); - mesh.vertexNormal = std::move(vertNorm); - mesh.texCoords = std::move(texCoord); - mesh.indices = std::move(indices); + mesh.headers.resize(mesh.header.meshCount); + mesh.meshes.resize(mesh.header.meshCount); + for (auto i{ 0 }; i < mesh.header.meshCount; ++i) + { + ReadHeader(file, mesh.headers[i]); + ReadData(file, mesh.headers[i], mesh.meshes[i]); + } file.close(); } @@ -79,52 +84,6 @@ namespace SHADE void SHMeshLoader::Write(SHAssetData const* data, AssetPath path) { - std::ofstream file{ path, std::ios::out | std::ios::binary | std::ios::trunc }; - if (!file.is_open()) - { - SHLOG_ERROR("Unable to open file for writing mesh file: {}", path.string()); - } - auto asset = *dynamic_cast(data); - - file.write( - reinterpret_cast(&(asset.header.vertexCount)), - sizeof(uint32_t) - ); - - file.write( - reinterpret_cast(&(asset.header.indexCount)), - sizeof(uint32_t) - ); - - auto const vertexVec3Byte{ sizeof(SHVec3) * asset.header.vertexCount }; - auto const vertexVec2Byte{ sizeof(SHVec2) * asset.header.vertexCount }; - - file.write( - reinterpret_cast(asset.vertexPosition.data()), - vertexVec3Byte - ); - - file.write( - reinterpret_cast(asset.vertexTangent.data()), - vertexVec3Byte - ); - - file.write( - reinterpret_cast(asset.vertexNormal.data()), - vertexVec3Byte - ); - - file.write( - reinterpret_cast(asset.texCoords.data()), - vertexVec2Byte - ); - - file.write( - reinterpret_cast(asset.indices.data()), - sizeof(uint32_t) * asset.header.indexCount - ); - - file.close(); } } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.h index 03a111ce..e45ada90 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.h @@ -12,11 +12,16 @@ #pragma once #include "Assets/Asset Types/SHMeshAsset.h" #include "SHAssetLoader.h" +#include namespace SHADE { - struct SHMeshLoader : SHAssetLoader + class SHMeshLoader : public SHAssetLoader { + void ReadHeader(std::ifstream& file, SHMeshDataHeader& header) noexcept; + void ReadData(std::ifstream& file, SHMeshDataHeader const& header, SHMeshData& data) noexcept; + + public: void LoadSHMesh(AssetPath path, SHMeshAsset& meshes) noexcept; SHAssetData* Load(AssetPath path) override; void Write(SHAssetData const* data, AssetPath path) override; From d1f624b2eb843c0630c4544b3b6e3a011f3fffa3 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Wed, 2 Nov 2022 21:09:53 +0800 Subject: [PATCH 056/113] Removed SHMeshData declaration/definition in graphics middle end Renamed some data members in SHMeshData Replaced calls and references to mesh data in Primitive Generator --- .../src/Assets/Asset Types/SHAssetIncludes.h | 2 +- .../{SHMeshAsset.h => SHModelAsset.h} | 14 +++--- .../{SHMeshLoader.cpp => SHModelLoader.cpp} | 46 +++++++++---------- .../{SHMeshLoader.h => SHModelLoader.h} | 8 ++-- .../Graphics/MiddleEnd/Meshes/SHMeshData.h | 36 --------------- .../MiddleEnd/Meshes/SHPrimitiveGenerator.cpp | 38 +++++++-------- .../MiddleEnd/Meshes/SHPrimitiveGenerator.h | 3 +- .../src/Resource/SHResourceManager.hpp | 2 +- 8 files changed, 57 insertions(+), 92 deletions(-) rename SHADE_Engine/src/Assets/Asset Types/{SHMeshAsset.h => SHModelAsset.h} (78%) rename SHADE_Engine/src/Assets/Libraries/Loaders/{SHMeshLoader.cpp => SHModelLoader.cpp} (51%) rename SHADE_Engine/src/Assets/Libraries/Loaders/{SHMeshLoader.h => SHModelLoader.h} (82%) delete mode 100644 SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHMeshData.h diff --git a/SHADE_Engine/src/Assets/Asset Types/SHAssetIncludes.h b/SHADE_Engine/src/Assets/Asset Types/SHAssetIncludes.h index 1c1ed44a..f4fb49f0 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 "SHMeshAsset.h" +#include "SHModelAsset.h" #include "SHTextureAsset.h" \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h similarity index 78% rename from SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h rename to SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h index 0ef5267b..45b06198 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h @@ -28,21 +28,21 @@ namespace SHADE struct SHMeshData { std::string name; - std::vector vertexPosition; - std::vector vertexTangent; - std::vector vertexNormal; - std::vector texCoords; + std::vector vertexPositions; + std::vector vertexTangents; + std::vector vertexNormals; + std::vector vertexTexCoords; std::vector indices; }; - struct SHMeshAssetHeader + struct SHModelAssetHeader { size_t meshCount; }; - struct SH_API SHMeshAsset : SHAssetData + struct SH_API SHModelAsset : SHAssetData { - SHMeshAssetHeader header; + SHModelAssetHeader header; std::vector headers; std::vector meshes; }; diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp similarity index 51% rename from SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.cpp rename to SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index 7520bb84..57d92b57 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -1,5 +1,5 @@ /*************************************************************************//** - * \file SHMeshLoader.cpp + * \file SHModelLoader.cpp * \author Loh Xiao Qi * \date 30 September 2022 * \brief Implementation for Mesh loader. Accounts for custom binary format @@ -11,12 +11,12 @@ * of DigiPen Institute of Technology is prohibited. *****************************************************************************/ #include "SHpch.h" -#include "SHMeshLoader.h" +#include "SHModelLoader.h" #include namespace SHADE { - void SHMeshLoader::ReadHeader(std::ifstream& file, SHMeshDataHeader& header) noexcept + void SHModelLoader::ReadHeader(std::ifstream& file, SHMeshDataHeader& header) noexcept { file.read( reinterpret_cast(&header), @@ -24,29 +24,29 @@ namespace SHADE ); } - void SHMeshLoader::ReadData(std::ifstream& file, SHMeshDataHeader const& header, SHMeshData& data) noexcept + void SHModelLoader::ReadData(std::ifstream& file, SHMeshDataHeader const& header, SHMeshData& data) noexcept { auto const vertexVec3Byte{ sizeof(SHVec3) * header.vertexCount }; auto const vertexVec2Byte{ sizeof(SHVec2) * header.indexCount }; - data.vertexPosition.resize(header.vertexCount); - data.vertexTangent.resize(header.vertexCount); - data.vertexNormal.resize(header.vertexCount); - data.texCoords.resize(header.vertexCount); + 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.vertexPosition.data()), vertexVec3Byte); - file.read(reinterpret_cast(data.vertexTangent.data()), vertexVec3Byte); - file.read(reinterpret_cast(data.vertexNormal.data()), vertexVec3Byte); - file.read(reinterpret_cast(data.texCoords.data()), vertexVec2Byte); + 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); } - void SHMeshLoader::LoadSHMesh(AssetPath path, SHMeshAsset& mesh) noexcept + void SHModelLoader::LoadSHMesh(AssetPath path, SHModelAsset& model) noexcept { std::ifstream file{ path.string(), std::ios::in | std::ios::binary }; if (!file.is_open()) @@ -58,31 +58,31 @@ namespace SHADE file.seekg(0); file.read( - reinterpret_cast(&mesh.header), - sizeof(SHMeshAssetHeader) + reinterpret_cast(&model.header), + sizeof(SHModelAssetHeader) ); - mesh.headers.resize(mesh.header.meshCount); - mesh.meshes.resize(mesh.header.meshCount); + model.headers.resize(model.header.meshCount); + model.meshes.resize(model.header.meshCount); - for (auto i{ 0 }; i < mesh.header.meshCount; ++i) + for (auto i{ 0 }; i < model.header.meshCount; ++i) { - ReadHeader(file, mesh.headers[i]); - ReadData(file, mesh.headers[i], mesh.meshes[i]); + ReadHeader(file, model.headers[i]); + ReadData(file, model.headers[i], model.meshes[i]); } file.close(); } - SHAssetData* SHMeshLoader::Load(AssetPath path) + SHAssetData* SHModelLoader::Load(AssetPath path) { - auto result = new SHMeshAsset(); + auto result = new SHModelAsset(); LoadSHMesh(path, *result); return result; } - void SHMeshLoader::Write(SHAssetData const* data, AssetPath path) + void SHModelLoader::Write(SHAssetData const* data, AssetPath path) { } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h similarity index 82% rename from SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.h rename to SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h index e45ada90..688e628e 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHMeshLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h @@ -1,5 +1,5 @@ /*************************************************************************//** - * \file SHMeshLoader.h + * \file SHModelLoader.h * \author Loh Xiao Qi * \date 30 September 2022 * \brief Library to load gltf mesh files and custom binary format @@ -10,19 +10,19 @@ * of DigiPen Institute of Technology is prohibited. *****************************************************************************/ #pragma once -#include "Assets/Asset Types/SHMeshAsset.h" +#include "Assets/Asset Types/SHModelAsset.h" #include "SHAssetLoader.h" #include namespace SHADE { - class SHMeshLoader : public SHAssetLoader + class SHModelLoader : public SHAssetLoader { void ReadHeader(std::ifstream& file, SHMeshDataHeader& header) noexcept; void ReadData(std::ifstream& file, SHMeshDataHeader const& header, SHMeshData& data) noexcept; public: - void LoadSHMesh(AssetPath path, SHMeshAsset& meshes) noexcept; + void LoadSHMesh(AssetPath path, SHModelAsset& model) noexcept; SHAssetData* Load(AssetPath path) override; void Write(SHAssetData const* data, AssetPath path) override; }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHMeshData.h b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHMeshData.h deleted file mode 100644 index 2f7d320b..00000000 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHMeshData.h +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************//*! -\file SHPrimitiveGenerator.h -\author Tng Kah Wei, kahwei.tng, 390009620 -\par email: kahwei.tng\@digipen.edu -\date Sep 18, 2022 -\brief Contains the static class definition of SHPrimitiveGenerator. - -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 Includes -#include "Math/SHMath.h" -#include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h" - -namespace SHADE -{ - /*************************************************************************************/ - /*! - \brief - Static class that contains functions for generating 3D primitives. - */ - /*************************************************************************************/ - struct SHMeshData - { - std::vector VertexPositions; - std::vector VertexTexCoords; - std::vector VertexTangents; - std::vector VertexNormals; - std::vector Indices; - }; -} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp index be181beb..e76d940f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.cpp @@ -22,7 +22,7 @@ namespace SHADE { SHMeshData mesh; - mesh.VertexPositions = + mesh.vertexPositions = { // front SHVec3(-0.5f, -0.5f, 0.5f), @@ -61,7 +61,7 @@ namespace SHADE SHVec3(-0.5f, 0.5f, -0.5f) }; - mesh.VertexTexCoords = + mesh.vertexTexCoords = { SHVec2(0.0f, 1.0f), SHVec2(1.0f, 1.0f), @@ -99,7 +99,7 @@ namespace SHADE SHVec2(0.0f, 0.0f) }; - mesh.VertexTangents = + mesh.vertexTangents = { // front SHVec3(1.0f, 0.0f, 0.0f), @@ -139,7 +139,7 @@ namespace SHADE }; - mesh.VertexNormals = + mesh.vertexNormals = { // front SHVec3(0.0f, 0.0f, 1.0f), @@ -178,7 +178,7 @@ namespace SHADE SHVec3(-1.0f, 0.0f, 0.0f) }; - mesh.Indices = + mesh.indices = { 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, @@ -196,13 +196,13 @@ namespace SHADE static SHMeshData meshData = Cube(); return meshLibrary.AddMesh ( - static_cast(meshData.VertexPositions.size()), - meshData.VertexPositions.data(), - meshData.VertexTexCoords.data(), - meshData.VertexTangents.data(), - meshData.VertexNormals.data(), - static_cast(meshData.Indices.size()), - meshData.Indices.data() + static_cast(meshData.vertexPositions.size()), + meshData.vertexPositions.data(), + meshData.vertexTexCoords.data(), + meshData.vertexTangents.data(), + meshData.vertexNormals.data(), + static_cast(meshData.indices.size()), + meshData.indices.data() ); } @@ -211,13 +211,13 @@ namespace SHADE static SHMeshData meshData = Cube(); return gfxSystem.AddMesh ( - static_cast(meshData.VertexPositions.size()), - meshData.VertexPositions.data(), - meshData.VertexTexCoords.data(), - meshData.VertexTangents.data(), - meshData.VertexNormals.data(), - static_cast(meshData.Indices.size()), - meshData.Indices.data() + static_cast(meshData.vertexPositions.size()), + meshData.vertexPositions.data(), + meshData.vertexTexCoords.data(), + meshData.vertexTangents.data(), + meshData.vertexNormals.data(), + static_cast(meshData.indices.size()), + meshData.indices.data() ); } } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h index dd059a29..852374fa 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h @@ -13,7 +13,8 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "Math/SHMath.h" -#include "SHMeshData.h" +#include "Assets/Asset Types/SHModelAsset.h" +#include "Graphics/MiddleEnd/Interface/SHMeshLibrary.h" #include "SH_API.h" namespace SHADE diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index 072adaa2..d078ce48 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -49,7 +49,7 @@ namespace SHADE throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); // Load - const SHMeshAsset* assetData = SHAssetManager::GetData(assetId); + const SHModelAsset* assetData = SHAssetManager::GetData(assetId); if (assetData == nullptr) { SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); From e609b5634ad2672cf767c5edbdba713ced8d9ac0 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Wed, 2 Nov 2022 21:31:27 +0800 Subject: [PATCH 057/113] Material Inspector Can set material --- Assets/Editor/Layouts/Default.ini | 26 +- Assets/Materials/TestMat.shmat | 8 + Assets/Materials/TestMat.shmat.shmeta | 3 + Assets/Shaders/TestCube_FS.glsl | 2 +- Assets/Shaders/TestCube_FS.shshaderb | Bin 2545 -> 2285 bytes SHADE_Engine/src/Assets/SHAssetManager.cpp | 6 + .../AssetBrowser/SHAssetBrowser.cpp | 80 +++++- .../AssetBrowser/SHAssetBrowser.h | 4 +- .../Inspector/SHEditorComponentView.hpp | 14 + .../MaterialInspector/SHMaterialInspector.cpp | 262 ++++++++++++++---- .../MaterialInspector/SHMaterialInspector.h | 9 +- .../Editor/EditorWindow/SHEditorWindow.cpp | 7 +- .../src/Editor/EditorWindow/SHEditorWindow.h | 1 + .../EditorWindow/SHEditorWindowIncludes.h | 13 +- SHADE_Engine/src/Editor/SHEditor.cpp | 2 + SHADE_Engine/src/Editor/SHEditorWidgets.hpp | 3 +- .../MiddleEnd/Materials/SHMaterialSpec.cpp | 2 +- .../MiddleEnd/Materials/SHMaterialSpec.h | 2 +- .../src/Graphics/RenderGraph/SHSubpass.cpp | 3 +- .../BlockInterface/SHShaderBlockInterface.cpp | 24 +- .../BlockInterface/SHShaderBlockInterface.h | 9 +- .../src/Resource/SHResourceManager.hpp | 26 +- .../Serialization/SHSerializationHelper.hpp | 8 +- 23 files changed, 380 insertions(+), 134 deletions(-) create mode 100644 Assets/Materials/TestMat.shmat create mode 100644 Assets/Materials/TestMat.shmat.shmeta diff --git a/Assets/Editor/Layouts/Default.ini b/Assets/Editor/Layouts/Default.ini index d7a7bf69..1099b5ac 100644 --- a/Assets/Editor/Layouts/Default.ini +++ b/Assets/Editor/Layouts/Default.ini @@ -1,16 +1,16 @@ [Window][MainStatusBar] -Pos=0,1060 +Pos=0,1007 Size=1920,20 Collapsed=0 [Window][SHEditorMenuBar] Pos=0,48 -Size=1920,1012 +Size=1920,959 Collapsed=0 [Window][Hierarchy Panel] -Pos=0,197 -Size=308,863 +Pos=0,189 +Size=308,818 Collapsed=0 DockId=0x00000004,0 @@ -21,13 +21,13 @@ Collapsed=0 [Window][Inspector] Pos=1528,48 -Size=392,1012 +Size=392,959 Collapsed=0 DockId=0x00000006,0 [Window][Profiler] Pos=0,48 -Size=308,147 +Size=308,139 Collapsed=0 DockId=0x00000003,0 @@ -76,7 +76,7 @@ DockId=0x0000000B,0 [Window][ Viewport] Pos=310,48 -Size=1216,715 +Size=1216,662 Collapsed=0 DockId=0x0000000B,0 @@ -93,13 +93,19 @@ Collapsed=0 DockId=0x0000000A,0 [Window][ Asset Browser] -Pos=310,765 +Pos=310,712 Size=1216,295 Collapsed=0 DockId=0x0000000C,0 +[Window][Material Inspector] +Pos=1528,48 +Size=392,959 +Collapsed=0 +DockId=0x00000006,1 + [Docking][Data] -DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X +DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=0,71 Size=1920,959 Split=X DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1526,1036 Split=X DockNode ID=0x00000001 Parent=0x00000005 SizeRef=308,1036 Split=Y Selected=0x1E6EB881 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,147 Selected=0x1E6EB881 @@ -111,5 +117,5 @@ DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Spli DockNode ID=0x0000000C Parent=0x00000009 SizeRef=1501,295 Selected=0xB128252A DockNode ID=0x0000000A Parent=0x00000007 SizeRef=1501,310 Selected=0xD446F7B6 DockNode ID=0x00000008 Parent=0x00000002 SizeRef=1501,338 Selected=0xD9F31532 - DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=392,1036 Selected=0xE7039252 + DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=392,1036 Selected=0xD3697FB6 diff --git a/Assets/Materials/TestMat.shmat b/Assets/Materials/TestMat.shmat new file mode 100644 index 00000000..089576f3 --- /dev/null +++ b/Assets/Materials/TestMat.shmat @@ -0,0 +1,8 @@ +- VertexShader: 39210065 + FragmentShader: 46377769 + SubPass: G-Buffer Write + Properties: + data.color: {x: 1, y: 0.200000003, z: 0.100000001, w: 1} + data.textureIndex: 64651793 + data.alpha: 0 + data.beta: {x: 1, y: 1, z: 1} \ No newline at end of file diff --git a/Assets/Materials/TestMat.shmat.shmeta b/Assets/Materials/TestMat.shmat.shmeta new file mode 100644 index 00000000..1612ef22 --- /dev/null +++ b/Assets/Materials/TestMat.shmat.shmeta @@ -0,0 +1,3 @@ +Name: TestMat +ID: 126974645 +Type: 7 diff --git a/Assets/Shaders/TestCube_FS.glsl b/Assets/Shaders/TestCube_FS.glsl index c60da6ce..093cc9c6 100644 --- a/Assets/Shaders/TestCube_FS.glsl +++ b/Assets/Shaders/TestCube_FS.glsl @@ -43,7 +43,7 @@ void main() { position = In.vertPos; normals = In.normal; - albedo = texture(textures[nonuniformEXT(MatProp.data[In2.materialIndex].textureIndex)], In.uv) + MatProp.data[In2.materialIndex].color / MatProp.data[In2.materialIndex].alpha; + albedo = texture(textures[nonuniformEXT(MatProp.data[In2.materialIndex].textureIndex)], In.uv); outEntityID = In2.eid; lightLayerIndices = In2.lightLayerIndex; diff --git a/Assets/Shaders/TestCube_FS.shshaderb b/Assets/Shaders/TestCube_FS.shshaderb index 95b4f62ae89c1bd3dc23f684757a7298c60833a3..fcb72b6e912e98ddd75e1e9eb888bad2282d0f97 100644 GIT binary patch delta 266 zcmew;{8ms*;0Xr<7%(#_vokO-GH@~QGT2QNo6Kqs6t>>Du9-;`#L6!%am_2qEUEN# zVPIuoXRzMv#vIKk2$GTmVn!f#XJ7#eZ(<2%GBpEonSodth^>LbAU;SbNK669w*b;0 zEy(iLo1@t68B6Sdio}6ZraP3JUrrs9F28n~zTZ6?J82$nsVFAPd#=;vt delta 528 zcmZ9I&q_j35Qpd9t6d7pq|#kPf`R+WG&S?zwKhSt(I;qAo7Sf>;rtcnceo=cL7ljKm6E3X9=A&V zjVH5O+zKXb+8+=)f->Zw!IKT{W02mau|_z7eNc{KK38MaKyhEMj{U#hDVlWE(-)d% zliz|0ZiAZZU#(?=U6HH?XHJvud0FgE1(yRT=gDd3*oUB4lT8%4rD;IzqYq^pnN={< gRLdE-fF5Xp^8JG$wtnP?RQ?4>SAOFBA68w3IcNPTo&W#< diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index 4897fc42..cf58d520 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -157,16 +157,22 @@ namespace SHADE { case AssetType::PREFAB: newPath += PREFAB_FOLDER; + newPath += name; + newPath += PREFAB_EXTENSION; data = new SHPrefabAsset(); break; case AssetType::SCENE: newPath += SCENE_FOLDER; + newPath += name; + newPath += SCENE_EXTENSION; data = new SHSceneAsset(); break; case AssetType::MATERIAL: newPath += MATERIAL_FOLDER; + newPath += name; + newPath += MATERIAL_EXTENSION; data = new SHMaterialAsset(); break; diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp index 8c71eb8f..a18cd70f 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp @@ -5,15 +5,19 @@ #include "Editor/SHImGuiHelpers.hpp" #include #include +#include #include "Assets/SHAssetManager.h" +#include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/IconsFontAwesome6.h" +#include "Editor/SHEditor.h" #include "Editor/DragDrop/SHDragDrop.hpp" +#include "Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h" namespace SHADE { SHAssetBrowser::SHAssetBrowser() - :SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar), rootFolder(SHAssetManager::GetRootFolder()), prevFolder(rootFolder), currentFolder(rootFolder) + :SHEditorWindow("\xee\x8b\x87 Asset Browser", ImGuiWindowFlags_MenuBar), rootFolder(SHAssetManager::GetRootFolder()), prevFolder(rootFolder), currentFolder(rootFolder), assetBeingCreated(std::nullopt) { } @@ -53,14 +57,33 @@ namespace SHADE flags |= ImGuiTreeNodeFlags_Selected; if (folder == rootFolder) flags |= ImGuiTreeNodeFlags_DefaultOpen; - + bool isOpen = ImGui::TreeNodeEx(folder, flags, "%s %s", ICON_MD_FOLDER, folder->name.data()); + ImGuiID folderID = ImGui::GetItemID(); const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); - if(ImGui::IsItemClicked()) + + if (ImGui::BeginPopupContextItem()) + { + if (ImGui::BeginMenu("Create Asset")) + { + //TODO: Change to rttr type enum align + if (ImGui::Selectable("Material")) + { + assetBeingCreated = { folder, AssetType::MATERIAL, "New Material" }; + ImGui::TreeNodeSetOpen(folderID, true); + isOpen = true; + } + ImGui::EndMenu(); + } + ImGui::EndPopup(); + } + + if (ImGui::IsItemClicked()) { selectedFolders.clear(); selectedFolders.push_back(folder); } + if (isOpen) { const ImColor treeLineColor = ImGui::GetColorU32(ImGuiCol_CheckMark); @@ -86,6 +109,9 @@ namespace SHADE vertLineEnd.y = midPoint; } drawList->AddLine(vertLineStart, vertLineEnd, treeLineColor, 1); + if(assetBeingCreated.has_value() && std::get<0>(assetBeingCreated.value()) == folder) + DrawAssetBeingCreated(); + ImGui::TreePop(); } return nodeRect; @@ -130,7 +156,7 @@ namespace SHADE flags |= ImGuiTreeNodeFlags_Selected; std::string icon{}; - switch(file.assetMeta->type) + switch (file.assetMeta->type) { case AssetType::INVALID: break; case AssetType::SHADER: icon = ICON_FA_FILE_CODE; break; @@ -141,24 +167,64 @@ namespace SHADE case AssetType::PREFAB: icon = ICON_FA_BOX_OPEN; break; case AssetType::MATERIAL: break; case AssetType::MAX_COUNT: break; - default: ; + default:; } ImGui::TreeNodeEx(file.assetMeta, flags, "%s %s", icon.data(), file.assetMeta->name.data()); const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); - if(SHDragDrop::BeginSource()) + if (SHDragDrop::BeginSource()) { auto id = file.assetMeta->id; ImGui::Text("Moving Asset: %s [%zu]", file.name.data(), file.assetMeta->id); SHDragDrop::SetPayload(SHDragDrop::DRAG_RESOURCE, &id); SHDragDrop::EndSource(); } - if(ImGui::IsItemClicked()) + if (ImGui::IsItemClicked()) { selectedAssets.clear(); selectedAssets.push_back(file.assetMeta->id); } + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + { + switch (file.assetMeta->type) + { + case AssetType::INVALID: break; + case AssetType::SHADER: icon = ICON_FA_FILE_CODE; break; + case AssetType::SHADER_BUILT_IN: icon = ICON_FA_FILE_CODE; break; + case AssetType::TEXTURE: icon = ICON_FA_IMAGES; break; + case AssetType::MESH: icon = ICON_FA_CUBES; break; + case AssetType::SCENE: icon = ICON_MD_IMAGE; break; + case AssetType::PREFAB: icon = ICON_FA_BOX_OPEN; break; + case AssetType::MATERIAL: + if (auto matInspector = SHEditorWindowManager::GetEditorWindow()) + { + matInspector->OpenMaterial(file.assetMeta->id); + } + break; + case AssetType::MAX_COUNT: break; + default:; + } + + } ImGui::TreePop(); return nodeRect; } + + void SHAssetBrowser::DrawAssetBeingCreated() noexcept + { + if (!assetBeingCreated.has_value()) + return; + auto& path = std::get<0>(assetBeingCreated.value()); + auto& type = std::get<1>(assetBeingCreated.value()); + auto& assetName = std::get<2>(assetBeingCreated.value()); + if (ImGui::InputText("##newAssetname", &assetName, ImGuiInputTextFlags_EnterReturnsTrue)) + { + AssetID assetId = SHAssetManager::CreateNewAsset(type, assetName); + if (auto matInspector = SHEditorWindowManager::GetEditorWindow()) + { + matInspector->OpenMaterial(assetId, true); + } + assetBeingCreated.reset(); + } + } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h index d56fc029..eec40262 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h @@ -10,6 +10,7 @@ namespace SHADE class SHAssetBrowser final : public SHEditorWindow { public: + using AssetEntry = std::tuple; SHAssetBrowser(); void Init(); @@ -21,11 +22,12 @@ namespace SHADE ImRect RecursivelyDrawTree(FolderPointer folder); void DrawCurrentFolder(); ImRect DrawFile(SHFile const& file) noexcept; + void DrawAssetBeingCreated() noexcept; FolderPointer rootFolder, prevFolder, currentFolder; + std::optional assetBeingCreated; std::vector selectedFolders; std::vector selectedAssets; static constexpr float tileWidth = 50.0f; - }; } diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index e3f93713..5b16a47d 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -349,6 +349,7 @@ namespace SHADE { DrawContextMenu(component); Handle const& mesh = component->GetMesh(); + Handle const& mat = component->GetMaterial(); SHEditorWidgets::DragDropReadOnlyField("Mesh", std::to_string(SHResourceManager::GetAssetID(mesh).value_or(0)).data(), [component]() { @@ -359,6 +360,19 @@ namespace SHADE { component->SetMesh(SHResourceManager::LoadOrGet(id)); }, SHDragDrop::DRAG_RESOURCE); + + SHEditorWidgets::DragDropReadOnlyField("Material", mat ? std::to_string(SHResourceManager::GetAssetID(mat->GetBaseMaterial()).value_or(0)).data() : "", [component]() + { + Handle const& mat = component->GetMaterial(); + if(!mat) + return static_cast(0); + return SHResourceManager::GetAssetID(mat->GetBaseMaterial()).value_or(0); + }, + [component](AssetID const& id) + { + auto gfxSystem = SHSystemManager::GetSystem(); + component->SetMaterial(gfxSystem->AddOrGetBaseMaterialInstance(SHResourceManager::LoadOrGet(id))); + }, SHDragDrop::DRAG_RESOURCE); } else { diff --git a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp index 775754d7..3eb8564f 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp @@ -1,9 +1,11 @@ #include "SHpch.h" +#include "Serialization/SHSerializationHelper.hpp" #include "SHMaterialInspector.h" #include "Editor/SHImGuiHelpers.hpp" #include #include "Assets/SHAssetManager.h" +#include "Editor/IconsMaterialDesign.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Editor/SHEditorWidgets.hpp" #include "Resource/SHResourceManager.h" @@ -11,14 +13,24 @@ namespace SHADE { SHMaterialInspector::SHMaterialInspector() - :SHEditorWindow("Material Inspector", ImGuiWindowFlags_MenuBar), isNewMaterial(false), currentViewedMaterial(0) + :SHEditorWindow("Material Inspector", ImGuiWindowFlags_MenuBar), isDirty(false), isNewMaterial(false), currentViewedMaterial(0) { } - void SHMaterialInspector::OpenMaterial(AssetID const& assetId) noexcept + void SHMaterialInspector::OpenMaterial(AssetID const& assetId, bool isNew) noexcept { //Get mat data - isNewMaterial = false; + if(isDirty) + return; + isDirty = isNew; + isOpen = true; + SetFocusToWindow(); + currentViewedMaterial = assetId; + + //currentMatSpec = //Get mat spec + + currentMatSpec = SHResourceManager::LoadOrGet(assetId); + currentMaterial = SHResourceManager::LoadOrGet(assetId); } void SHMaterialInspector::Init() @@ -30,18 +42,26 @@ namespace SHADE { SHEditorWindow::Update(); - if(Begin()) + if (Begin()) { - DrawMenuBar(); + if(currentViewedMaterial) + { + DrawMenuBar(); - if(SHEditorWidgets::DragDropReadOnlyField("Vertex Shader", std::to_string(currentMatSpec.vertexShader), [&](){return currentMatSpec.vertexShader;}, [&](AssetID const& id){currentMatSpec.vertexShader = id;}, SHDragDrop::DRAG_RESOURCE)) - { - vertShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec.vertexShader); - } - if(SHEditorWidgets::DragDropReadOnlyField("Fragment Shader", std::to_string(currentMatSpec.fragShader), [&](){return currentMatSpec.fragShader;}, [&](AssetID const& id){currentMatSpec.fragShader = id;}, SHDragDrop::DRAG_RESOURCE)) - { - fragShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec.fragShader); + //if (SHEditorWidgets::DragDropReadOnlyField("Vertex Shader", std::to_string(currentMatSpec->vertexShader), [&]() {return currentMatSpec->vertexShader; }, [&](AssetID const& id) {currentMatSpec->vertexShader = id; }, SHDragDrop::DRAG_RESOURCE)) + //{ + // isDirty = true; + // vertShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec->vertexShader); + //} + //if (SHEditorWidgets::DragDropReadOnlyField("Fragment Shader", std::to_string(currentMatSpec->fragShader), [&]() {return currentMatSpec->fragShader; }, [&](AssetID const& id) {currentMatSpec->fragShader = id; }, SHDragDrop::DRAG_RESOURCE)) + //{ + // isDirty = true; + // fragShaderHandle = SHResourceManager::LoadOrGet(currentMatSpec->fragShader); + //} + + DrawShaderProperties(/*fragShaderHandle*/); } + } ImGui::End(); } @@ -51,67 +71,187 @@ namespace SHADE SHEditorWindow::Exit(); } - void SHMaterialInspector::CreateNewMaterial() - { - isNewMaterial = true; - //prompt for a name - currentViewedMaterial = SHAssetManager::CreateNewAsset(AssetType::MATERIAL, "NewMaterial"); - currentMatSpec = {}; - vertShaderHandle = {}; - fragShaderHandle = {}; - } - void SHMaterialInspector::DrawMenuBar() { - if(ImGui::BeginMenuBar()) + if (ImGui::BeginMenuBar()) { + ImGui::BeginDisabled(!isDirty); + if(ImGui::Button(std::format("{} Save", ICON_MD_SAVE).data())) + { + //save + if(auto matAsset = SHAssetManager::GetData(currentViewedMaterial)) + { + YAML::Emitter out; + out << YAML::BeginSeq; + out << YAML::convert::encode(*currentMatSpec); + out << YAML::EndSeq; + matAsset->data = out.c_str(); + + Handle pipelineProperties = currentMaterial->GetShaderBlockInterface(); + for (int i = 0; i < static_cast(pipelineProperties->GetVariableCount()); ++i) + { + const std::string& PROP_NAME = pipelineProperties->GetVariableName(i); + const YAML::Node& PROP_NODE = currentMatSpec->properties[PROP_NAME.data()]; + if (PROP_NODE.IsDefined()) + { + const std::string& VAR_NAME = pipelineProperties->GetVariableName(i); + const SHShaderBlockInterface::Variable* VARIABLE = pipelineProperties->GetVariable(i); + switch (VARIABLE->type) + { + case SHADE::SHShaderBlockInterface::Variable::Type::FLOAT: + currentMaterial->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::INT: + currentMaterial->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR2: + currentMaterial->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR3: + currentMaterial->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR4: + currentMaterial->SetProperty(VARIABLE->offset, PROP_NODE.as()); + break; + case SHADE::SHShaderBlockInterface::Variable::Type::OTHER: + default: + continue; + break; + } + } + } + + if(SHAssetManager::SaveAsset(currentViewedMaterial)) + { + isDirty = false; + } + } + } + ImGui::EndDisabled(); ImGui::EndMenuBar(); } } - void SHMaterialInspector::DrawShaderProperties(Handle shaderModule) + void SHMaterialInspector::DrawShaderProperties(/*Handle shaderModule*/) { - auto interface = shaderModule->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA); + /*if(!shaderModule) + return;*/ + auto gfxSystem = SHSystemManager::GetSystem(); + auto interface = gfxSystem->GetDefaultMaterialInstance()->GetBaseMaterial()->GetShaderBlockInterface(); + //auto interface = shaderModule->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA); - int const varCount = static_cast(interface->GetVariableCount()); - - for(int i = 0; i < varCount; ++i) + int const varCount = static_cast(interface->GetVariableCount()); + + for (int i = 0; i < varCount; ++i) + { + auto variable = interface->GetVariable(i); + const std::string& VAR_NAME = interface->GetVariableName(i); + if(VAR_NAME.empty()) + continue; + switch (variable->type) { - auto variable = interface->GetVariable(i); - const std::string& VAR_NAME = interface->GetVariableName(i); - switch (variable->type) + case SHShaderBlockInterface::Variable::Type::FLOAT: + isDirty |= SHEditorWidgets::DragFloat(VAR_NAME, + [&]() + { + if (currentMatSpec->properties[VAR_NAME].IsDefined()) + return currentMatSpec->properties[VAR_NAME].as(); + else + return 0.0f; + }, + [&](float const& value) + { + currentMatSpec->properties[VAR_NAME] = value; + } + ); + break; + case SHShaderBlockInterface::Variable::Type::INT: + isDirty |= SHEditorWidgets::DragInt(VAR_NAME, + [&]() + { + if (currentMatSpec->properties[VAR_NAME].IsDefined()) + return currentMatSpec->properties[VAR_NAME].as(); + else + return 0; + }, + [&](int const& value) + { + currentMatSpec->properties[VAR_NAME] = value; + } + ); + if(SHDragDrop::BeginTarget()) { - case SHShaderBlockInterface::Variable::Type::FLOAT: - SHEditorWidgets::DragFloat(VAR_NAME, [&]() - { - if(currentMatSpec.properties[VAR_NAME].IsDefined()) - return currentMatSpec.properties[VAR_NAME].as(); - else - return 0.0f; - }, - [&](float const& value) - { - currentMatSpec.properties[VAR_NAME] = value; - } - ); - break; - case SHShaderBlockInterface::Variable::Type::INT: - - break; - case SHShaderBlockInterface::Variable::Type::VECTOR2: - - break; - case SHShaderBlockInterface::Variable::Type::VECTOR3: - - break; - case SHShaderBlockInterface::Variable::Type::VECTOR4: - - break; - case SHShaderBlockInterface::Variable::Type::OTHER: - default: - continue; - break; + if(AssetID* payload = SHDragDrop::AcceptPayload(SHDragDrop::DRAG_RESOURCE)) + { + currentMatSpec->properties[VAR_NAME] = *payload; + isDirty = true; + SHDragDrop::EndTarget(); + } } + break; + case SHShaderBlockInterface::Variable::Type::VECTOR2: + isDirty |= SHEditorWidgets::DragVec2(VAR_NAME, { "X", "Y" }, + [&]() + { + if (currentMatSpec->properties[VAR_NAME].IsDefined()) + return currentMatSpec->properties[VAR_NAME].as(); + else + return SHVec2::Zero; + }, + [&](SHVec2 const& value) + { + currentMatSpec->properties[VAR_NAME] = value; + } + ); + break; + case SHShaderBlockInterface::Variable::Type::VECTOR3: + isDirty |= SHEditorWidgets::DragVec3(VAR_NAME, { "X", "Y", "Z" }, + [&]() + { + if (currentMatSpec->properties[VAR_NAME].IsDefined()) + return currentMatSpec->properties[VAR_NAME].as(); + else + return SHVec3::Zero; + }, + [&](SHVec3 const& value) + { + currentMatSpec->properties[VAR_NAME] = value; + } + ); + break; + case SHShaderBlockInterface::Variable::Type::VECTOR4: + isDirty |= SHEditorWidgets::DragVec4(VAR_NAME, { "X", "Y", "Z", "W" }, + [&]() + { + if (currentMatSpec->properties[VAR_NAME].IsDefined()) + return currentMatSpec->properties[VAR_NAME].as(); + else + return SHVec4::Zero; + }, + [&](SHVec4 const& value) + { + currentMatSpec->properties[VAR_NAME] = value; + } + ); + break; + case SHShaderBlockInterface::Variable::Type::OTHER: + isDirty |= SHEditorWidgets::InputText(VAR_NAME, + [&]() + { + if (currentMatSpec->properties[VAR_NAME].IsDefined()) + return currentMatSpec->properties[VAR_NAME].as(); + else + return std::string(); + }, + [&](std::string const& value) + { + currentMatSpec->properties[VAR_NAME] = value; + } + ); + default: + continue; + break; } + } } } diff --git a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h index 20e165a7..79885399 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h +++ b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.h @@ -18,15 +18,16 @@ namespace SHADE void Update() override; void Exit() override; - void CreateNewMaterial(); - void OpenMaterial(AssetID const& assetId) noexcept; + void OpenMaterial(AssetID const& assetId, bool isNew = false) noexcept; private: void DrawMenuBar(); - void DrawShaderProperties(Handle shaderModule); + void DrawShaderProperties(/*Handle shaderModule*/); + bool isDirty; bool isNewMaterial; AssetID currentViewedMaterial; - SHMaterialSpec currentMatSpec; + Handle currentMatSpec; + Handle currentMaterial; Handle vertShaderHandle, fragShaderHandle; }; } diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp index b5a691d8..5f00cc37 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.cpp @@ -19,7 +19,7 @@ namespace SHADE //|| Public Member Functions || //#==============================================================# SHEditorWindow::SHEditorWindow(std::string_view const& name, ImGuiWindowFlags const& inFlags) - : windowName(name), windowFlags(inFlags), io(ImGui::GetIO()) + :isOpen(true), isWindowHovered(false), windowName(name), windowFlags(inFlags), io(ImGui::GetIO()) { } @@ -68,5 +68,10 @@ namespace SHADE void SHEditorWindow::OnPosChange() { } + + void SHEditorWindow::SetFocusToWindow() + { + ImGui::SetWindowFocus(windowName.data()); + } }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h index 239d8223..faacd8f2 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindow.h @@ -34,6 +34,7 @@ namespace SHADE virtual bool Begin(); virtual void OnResize(); virtual void OnPosChange(); + virtual void SetFocusToWindow(); ImGuiWindowFlags windowFlags = 0; ImGuiIO& io; diff --git a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h index 5208c6d9..2fcde2b2 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h +++ b/SHADE_Engine/src/Editor/EditorWindow/SHEditorWindowIncludes.h @@ -1,7 +1,8 @@ #pragma once -#include "MenuBar/SHEditorMenuBar.h" //Menu Bar -#include "HierarchyPanel/SHHierarchyPanel.h" //Hierarchy Panel -#include "Inspector/SHEditorInspector.h" //Inspector -#include "Profiling/SHEditorProfiler.h" //Profiler -#include "ViewportWindow/SHEditorViewport.h" //Editor Viewport -#include "AssetBrowser/SHAssetBrowser.h" //Asset Browser \ No newline at end of file +#include "MenuBar/SHEditorMenuBar.h" //Menu Bar +#include "HierarchyPanel/SHHierarchyPanel.h" //Hierarchy Panel +#include "Inspector/SHEditorInspector.h" //Inspector +#include "Profiling/SHEditorProfiler.h" //Profiler +#include "ViewportWindow/SHEditorViewport.h" //Editor Viewport +#include "AssetBrowser/SHAssetBrowser.h" //Asset Browser +#include "MaterialInspector/SHMaterialInspector.h" //Material Inspector \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index cf5056a5..8bf4c17b 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -93,6 +93,8 @@ namespace SHADE SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); + SHEditorWindowManager::CreateEditorWindow(); io = &ImGui::GetIO(); diff --git a/SHADE_Engine/src/Editor/SHEditorWidgets.hpp b/SHADE_Engine/src/Editor/SHEditorWidgets.hpp index 053348d7..0855d68d 100644 --- a/SHADE_Engine/src/Editor/SHEditorWidgets.hpp +++ b/SHADE_Engine/src/Editor/SHEditorWidgets.hpp @@ -422,12 +422,13 @@ namespace SHADE ImGui::BeginGroup(); ImGui::PushID(label.data()); TextLabel(label); - bool const changed = ImGui::InputText("##", &text, ImGuiInputTextFlags_ReadOnly, nullptr, nullptr); + bool changed = ImGui::InputText("##", &text, ImGuiInputTextFlags_ReadOnly, nullptr, nullptr); if(SHDragDrop::BeginTarget()) { if(T* payload = SHDragDrop::AcceptPayload(dragDropTag)) { SHCommandManager::PerformCommand(std::reinterpret_pointer_cast(std::make_shared>(get(), *payload, set)), false); + changed = true; SHDragDrop::EndTarget(); } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.cpp index 0652b2bf..0dabc4e8 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.cpp @@ -34,7 +34,7 @@ namespace SHADE fShaderMod = shader; } vertexShader = SHResourceManager::GetAssetID(vShaderMod).value_or(0); - fragShader = SHResourceManager::GetAssetID(vShaderMod).value_or(0); + fragShader = SHResourceManager::GetAssetID(fShaderMod).value_or(0); subpassName = material.GetPipeline()->GetPipelineState().GetSubpass()->GetName(); // Write Properties diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h index 03928cfa..9c2177c3 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Materials/SHMaterialSpec.h @@ -46,6 +46,6 @@ namespace SHADE /* Constructors */ /*---------------------------------------------------------------------------------*/ SHMaterialSpec() = default; - SHMaterialSpec(const SHMaterial& material); + explicit SHMaterialSpec(const SHMaterial& material); }; } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp index d8f4f8c2..6e78eb9f 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp @@ -70,7 +70,7 @@ namespace SHADE , inputImageDescriptors{ std::move(rhs.inputImageDescriptors) } , inputDescriptorLayout{ rhs.inputDescriptorLayout } , inputSamplers{ rhs.inputSamplers } - + , name { rhs.name } { } @@ -105,6 +105,7 @@ namespace SHADE inputImageDescriptors = std::move(rhs.inputImageDescriptors); inputDescriptorLayout = rhs.inputDescriptorLayout; inputSamplers = rhs.inputSamplers; + name = std::move(rhs.name); return *this; } diff --git a/SHADE_Engine/src/Graphics/Shaders/BlockInterface/SHShaderBlockInterface.cpp b/SHADE_Engine/src/Graphics/Shaders/BlockInterface/SHShaderBlockInterface.cpp index f214c094..67c83266 100644 --- a/SHADE_Engine/src/Graphics/Shaders/BlockInterface/SHShaderBlockInterface.cpp +++ b/SHADE_Engine/src/Graphics/Shaders/BlockInterface/SHShaderBlockInterface.cpp @@ -54,17 +54,7 @@ namespace SHADE { return variables.size(); } - - SHShaderBlockInterface::SHShaderBlockInterface(void) noexcept - : bytesRequired{ 0 } - {} - - SHShaderBlockInterface::SHShaderBlockInterface(SHShaderBlockInterface&& rhs) noexcept - : variables { std::move(rhs.variables) } - , variableIndexing { std::move(rhs.variableIndexing) } - , bytesRequired { rhs.bytesRequired } - {} - + void SHShaderBlockInterface::SetBytesRequired(uint32_t bytes) noexcept { bytesRequired = bytes; @@ -75,16 +65,4 @@ namespace SHADE { return bytesRequired; } - - SHADE::SHShaderBlockInterface& SHShaderBlockInterface::operator=(SHShaderBlockInterface&& rhs) noexcept - { - if (&rhs == this) - return *this; - - variables = std::move(rhs.variables); - variableIndexing = std::move(rhs.variableIndexing); - bytesRequired = rhs.bytesRequired; - - return *this; - } } \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/Shaders/BlockInterface/SHShaderBlockInterface.h b/SHADE_Engine/src/Graphics/Shaders/BlockInterface/SHShaderBlockInterface.h index ae75e2c8..8b7ccb97 100644 --- a/SHADE_Engine/src/Graphics/Shaders/BlockInterface/SHShaderBlockInterface.h +++ b/SHADE_Engine/src/Graphics/Shaders/BlockInterface/SHShaderBlockInterface.h @@ -33,7 +33,7 @@ namespace SHADE std::unordered_map variableIndexing; //! bytes required by the block (includes padding). This variable is required - uint32_t bytesRequired; + uint32_t bytesRequired = 0; public: void AddVariable (std::string name, Variable&& newVariable) noexcept; @@ -43,13 +43,6 @@ namespace SHADE const std::string& GetVariableName(uint32_t index) const noexcept; size_t GetVariableCount() const noexcept; - /*-----------------------------------------------------------------------*/ - /* CTORS AND DTORS */ - /*-----------------------------------------------------------------------*/ - SHShaderBlockInterface(void) noexcept; - SHShaderBlockInterface(SHShaderBlockInterface&& rhs) noexcept; - SHShaderBlockInterface& operator=(SHShaderBlockInterface&& rhs) noexcept; - /*-----------------------------------------------------------------------*/ /* SETTERS AND GETTERS */ /*-----------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index aa0a0af9..4f2acd45 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -221,7 +221,14 @@ namespace SHADE { // Get the data we need to construct auto matSpec = resourceHub.Create(); - *matSpec = YAML::Node(assetData.data).as(); + YAML::convert::decode(*YAML::Load(assetData.data).begin(), *matSpec); + // Failed to load + if (matSpec->subpassName == "") + { + // Use default material + *matSpec = SHMaterialSpec(*gfxSystem->GetDefaultMaterial()); + } + return matSpec; } // Materials @@ -260,8 +267,8 @@ namespace SHADE for (int i = 0; i < static_cast(pipelineProperties->GetVariableCount()); ++i) { const std::string& PROP_NAME = pipelineProperties->GetVariableName(i); - const auto& PROP_NODE = assetData.properties; - if (PROP_NODE) + const YAML::Node& PROP_NODE = assetData.properties[PROP_NAME.data()]; + if (PROP_NODE.IsDefined()) { const std::string& VAR_NAME = pipelineProperties->GetVariableName(i); const SHShaderBlockInterface::Variable* VARIABLE = pipelineProperties->GetVariable(i); @@ -271,7 +278,18 @@ namespace SHADE matHandle->SetProperty(VARIABLE->offset, PROP_NODE.as()); break; case SHADE::SHShaderBlockInterface::Variable::Type::INT: - matHandle->SetProperty(VARIABLE->offset, PROP_NODE.as()); + { + Handle texture = LoadOrGet(PROP_NODE.as()); + if (texture) + { + matHandle->SetProperty(VARIABLE->offset, texture->TextureArrayIndex); + } + else + { + SHLOG_WARNING("[] Attempted to load invalid texture! Setting to 0."); + matHandle->SetProperty(VARIABLE->offset, 0); + } + } break; case SHADE::SHShaderBlockInterface::Variable::Type::VECTOR2: matHandle->SetProperty(VARIABLE->offset, PROP_NODE.as()); diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index 8f1b972c..ed4f118a 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -267,17 +267,17 @@ namespace YAML static bool decode(YAML::Node const& node, SHMaterialSpec& rhs) { // Retrieve Shader Asset IDs - if (node[VERT_SHADER_YAML_TAG.data()]) + if (node[VERT_SHADER_YAML_TAG.data()].IsDefined()) rhs.vertexShader = node[VERT_SHADER_YAML_TAG.data()].as(); - if (node[FRAG_SHADER_YAML_TAG.data()]) + if (node[FRAG_SHADER_YAML_TAG.data()].IsDefined()) rhs.fragShader = node[FRAG_SHADER_YAML_TAG.data()].as(); // Retrieve Subpass - if (node[SUBPASS_YAML_TAG.data()]) + if (node[SUBPASS_YAML_TAG.data()].IsDefined()) rhs.subpassName = node[SUBPASS_YAML_TAG.data()].as(); // Retrieve - if (node[PROPS_YAML_TAG.data()]) + if (node[PROPS_YAML_TAG.data()].IsDefined()) rhs.properties = node[PROPS_YAML_TAG.data()]; return true; From c96eeb7c5b8dc0409f969c3d2fe6b35b56fe6299 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Wed, 2 Nov 2022 22:36:44 +0800 Subject: [PATCH 058/113] Removed xml --- bin/Debug/SHADE_CSharp.xml | 1029 ------ bin/Debug/SHADE_Managed.xml | 6250 --------------------------------- bin/Release/SHADE_CSharp.xml | 1029 ------ bin/Release/SHADE_Managed.xml | 6250 --------------------------------- 4 files changed, 14558 deletions(-) delete mode 100644 bin/Debug/SHADE_CSharp.xml delete mode 100644 bin/Debug/SHADE_Managed.xml delete mode 100644 bin/Release/SHADE_CSharp.xml delete mode 100644 bin/Release/SHADE_Managed.xml diff --git a/bin/Debug/SHADE_CSharp.xml b/bin/Debug/SHADE_CSharp.xml deleted file mode 100644 index daeaa3c5..00000000 --- a/bin/Debug/SHADE_CSharp.xml +++ /dev/null @@ -1,1029 +0,0 @@ - - - - SHADE_CSharp - - - - - Interface for a CallbackAction that all variants inherit from. - - - - - Whether or not this CallbackAction is runtime assigned. If it is, then the - TargetMethodName and TargetObject properties are invalid. - - - - - Name of the method that this CallbackAction is using. - - - - - Object which the specified target method is called on. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 1 parameter. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 2 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 3 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 4 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 5 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 6 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 7 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 8 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 9 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 10 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Interface for a CallbackEvent that all variants inherit from. - - - - - Registers an empty ICallbackAction. - - - - - Registers an ICallbackAction with the event such that it will be called in - future - - ICallbackAction to register with. - - - - Deregisters an ICallbackAction that was previously added. This should - only emit a warning if an action that was not previous added was - provided. - - ICallbackAction to remove. - - - - Iterable set of ICallbackActions that were registered to this event. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - diff --git a/bin/Debug/SHADE_Managed.xml b/bin/Debug/SHADE_Managed.xml deleted file mode 100644 index 7b653116..00000000 --- a/bin/Debug/SHADE_Managed.xml +++ /dev/null @@ -1,6250 +0,0 @@ - - - - "SHADE_Managed" - - - - -Retrieves the duration that the specified key has not been held or was last -not been held for. - - The key to check. - Time in seconds that the key was held. - - - -Retrieves the duration that the specified key has been held or was last held -for. - - The key to check. - Time in seconds that the key was held. - - - -Retrieves the duration that the specified key has not been held or was last -not been held for. - - The key to check. - Time in seconds that the key was held. - - - -Retrieves the duration that the specified key has been held or was last held -for. - - The key to check. - Time in seconds that the key was held. - - - -Sets the position of the mouse cursor relative to the top left corner of the -window. - - -Position of the mouse in window pixel coordinates to set. - - - - -Checks if a specified mouse button is no longer pressed and was pressed -before. - - MouseCode of the mouse button to check. - -True during the frame the user releases the given mouse button. - - - - -Checks if a specified mouse button is pressed and was not pressed before. - - MouseCode of the mouse button to check. - -True during the frame the user pressed the given mouse button. - - - - -Checks if a specified mouse button is being held down. -This will also be true if GetMouseButtonDown() is true. - - MouseCode of the mouse button to check. - True while the user holds down the mouse button specified. - - - -Checks if a specified key is no longer pressed pressed and was pressed -before. - - KeyCode of the key to check. - -True during the frame the user releases the key identified by name. - - - - -Checks if a specified key is pressed and was not pressed before. - - KeyCode of the key to check. - -True during the frame the user starts pressing down the key specified. - - - - -Checks if a specified key is being held down. -This will also be true if GetKeyDown() is true. - - KeyCode of the key to check. - True while the user holds down the key specified. - - - -Amnount of vertical mouse scroll in this frame. - - - - -Mouse position in screen coordinates relative to the top left of the window. -This value is a Vector3 for compatibility with functions that have Vector3 -arguments. The z component of the Vector3 is always 0 - - - - -Represents the available supported mouse keycodes that can be passed into the -mouse-button-based Input functions. - - - - -Represents the available supported keycodes that can be passed into the -key-based Input functions. - -Attempting to follow https://docs.unity3d.com/ScriptReference/KeyCode.html -Win32 keycodes are shift-insensitive, i.e. 'A' and 'a' are the same keycode and '1' and '!' are the same keycode - - - - -Static class responsible for providing access to Input-related functionality. - - - - -Simple attribute to mark that a field in a Script should be serialised. - - - - -Cleans up all required components for managed code. - - - - -Reloads the managed script assembly. -Take note that this will clear all existing scripts, ensure that the scene -is saved before doing so. -Equivalent to calling UnloadScriptAssembly() and then LoadScriptAssembly(). - - - - -Loads the managed script assembly. Ensure this is only called after -UnloadScriptAssembly() has been called. - - - - -Unloads the managed script assembly. -Take note that this will clear all existing scripts, ensure that the scene -is saved before doing so. - - - - -Initialises all required components for managed code. - - - - -Name of the Managed Library that contains the C# scripts written externally. - - - - -Static class that contains the functions for interfacing with the core -PlushieEngine written in C++ for managing the lifecycle of managed code. - - - - -Default Constructor - - - - -Custom AssemblyLoadContext marked as collectible so that it can be unloaded. - - - - -Time taken for Physics simulations. You should use this for operations -within Script.FixedUpdate() - - - - -Time taken to process the previous frame. - - - - -Static class that contains the functions for working with time. - - - - -Static class that wraps up certain functions in the SHPhysicsSystem so that -accessing it from SHADE_Managed would not cause issues due to C++20 features. - - - - -Constructor for a Tooltip attribute that fills in the description. - - Text to be shown when a field is hovered. - - - -Description that is to be shown in the Tooltip. - - - - -Simple attribute to provide a field in a script with a tooltip. - - - - -Checks if a specified file exists. - - File path to the file to check. - True if the file exists - - - -Deletes the folder and all files in it as specified by the file path. - - File path to the file to delete. - - - -Deletes the file as specified by the file path. - - File path to the file to delete. - - - -Reads the file via the specified path that represents a build log of error -and warning messages. - - -File path to the build log of script builds done by BuildScriptAssembly() to -dump and process. - - - - -Registers events for the scripting system - - - - -Loads all the function pointers to CLR code that we need to execute. - - - - -Generates a .csproj file for editing and compiling the C# scripts. - - File path to the generated file. - - - -Utilises execution of a external batch file for invoking the dotnet build -tool to compile C# scripts in the Assets folder into the SHADE_Scripting -C# assembly DLL. - - -Whether or not a debug build will be built. Only debug built C# assemblies -can be debugged. - - -Whether or not we are reloading the assembly, if so, unload and then reload it. - - Whether or not the build succeeded. - - - -Performs a redo for script inspector changes if it exists. - - - - -Performs an undo for script inspector changes if it exists. - - - - -Renders the set of attached Scripts for the specified Entity into the -inspector. -
-This function is meant for consumption from native code in the inspector -rendering code. -
- The Entity to render the Scripts of. -
- - -Creates scripts and sets fields for the specified Entity based on the specified -YAML node. - - The Entity to deserialise a Script on to. - -YAML Node that contains the serialised script data. - - True if successfully deserialised. - - - -Performs serialization of all scripts for the specified entity into the -YAML::Node specified. This node will contain all serialised scripts after -calling this function. - - The Entity to Serialise. - -YAML Node that will store the serialised scripts. - - True if successfully serialised. - - - -Removes all Scripts attached to the specified Entity. Unlike -RemoveAllScripts(), this removes all the scripts immediately. -Does not do anything if the specified Entity is invalid or does not have any -Scripts attached. - - The entity to remove the scripts from. - -Whether or not to call OnDestroy on the scripts. This is ignored if not in -play mode. - - - - -Removes all Scripts attached to the specified Entity. Does not do anything -if the specified Entity is invalid or does not have any Scripts -attached. - - The entity to remove the scripts from. - - - -Adds a Script to a specified Entity. Note that while you can call this -multiple times on a specified Entity, it will work for all intents and -purposes but GetScript<T>() (C# only) currently only -gives you the first Script added of the specified type. - - The entity to add a script to. - Type name of the script to add. - -True if successfully added. False otherwise with the error logged to the -console. - - - - -Shuts down the DotNetRuntime. - - - - -Executes the OnCollision*()s and OnTrigger*()s of the Scripts that are attached -to Entities. - - - - -Executes the FixedUpdate()s of the Scripts that are attached to -Entities. - - - - -Reloads the managed script assembly. -Take note that this will clear all existing scripts, ensure that the scene -is saved before doing so. - - - - -Unloads the managed script assembly. -Take note that this will clear all existing scripts, ensure that the scene -is saved before doing so. - - - - -Loads the managed script assembly. Ensure this is only called after -UnloadScriptAssembly() has been called. - - - - -Initialises the DotNetRuntime and retrieves function pointers to all -functions on the CLR used to interface with the engine. - - - - -Manages initialisation of the DotNetRuntime and interfacing with CLR code written -and executed on .NET. - - - - -Deserialises a YAML node that contains a map of Scripts and copies the -deserialised data into the specified object if there are matching fields. - - -The JSON string that contains the data to copy into this Script object. - - The object to copy deserialised data into. - - - -Creates a JSON node that represents the specified object and its associated -serialisable fields. Public fields and fields marked with the SerialiseField -attribute will be serialised. - - The object to serialise. - - - -Checks if a specified field is a candidate for serialisation. This means that -the field is public or private with the [SerialiseField] attribute. - - The field to check. - -True if the specified field is a candidate for serialisation. - - - - -Retrieves a set of all non-static (instance) fields from a specified object. - - The object to get non-static fields from. - Immutable list of non-static fields. - - - -Contains useful static functions for working with Reflection. - - - -Converts the node to a YAML string. - - -Emits the node to the given output stream. - - -Emits the node to the given {@link Emitter}. If there is an error in writing, -{@link Emitter#good} will return false. - - - - Loads the input file as a list of YAML documents. - - @throws {@link ParserException} if it is malformed. - @throws {@link BadFile} if the file cannot be loaded. - - - - Loads the input stream as a list of YAML documents. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input string as a list of YAML documents. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input string as a list of YAML documents. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input file as a single YAML document. - - @throws {@link ParserException} if it is malformed. - @throws {@link BadFile} if the file cannot be loaded. - - - - Loads the input stream as a single YAML document. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input string as a single YAML document. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input string as a single YAML document. - - @throws {@link ParserException} if it is malformed. - - - -Handles a "TAG" directive, which should be of the form 'handle prefix', -where 'handle' is converted to 'prefix' in the file. - - - -Handles a "YAML" directive, which should be of the form 'major.minor' (like -a version number). - - - -Reads any directives that are next in the queue, setting the internal -{@code m_pDirectives} state. - - - - Handles the next document by calling events on the {@code eventHandler}. - - @throw a ParserException on error. - @return false if there are no more documents - - - -Resets the parser with the given input stream. Any existing state is -erased. - - - -Evaluates to true if the parser has some valid input to be read. - - -Constructs a parser from the given input stream. The input stream must -live as long as the parser. - - - -Constructs an empty parser (with no input. - - -A parser turns a stream of bytes into one stream of "events" per YAML -document in the input stream. - - - - -Renders a context menu when right clicked for the scripts - - The Entity to render the Scripts of. - The Script to render the inspector for. - - - -Renders a field specified into the inspector. - - The field to render. - -The object that contains the data of the field to render. - - - - -Renders a single specified Script's inspector. - - The Entity to render the Scripts of. - The Script to render the inspector for. - -Indices used internally to differentiate each rendered Script -inspector. This is required to open and close each Script's inspector -independently from each other. - - - - -Redoes the last script inspector change if there is any. - - - - -Undoes the last script inspector change if there is any. - - - - -Renders a dropdown button that allows for the addition of PlushieScripts -onto the specified Entity. - - The Entity to add PlushieScripts to. - - - -Renders the set of attached Scripts for the specified Entity into the -inspector. -
-This function is meant for consumption from native code in the inspector -rendering code. -
- The Entity to render the Scripts of. -
- - -Static class for Editor-related functions - - - - -Processes a YAML node that contains a list of multiple scripts to be loaded -into the specified Entity. -

-This function should only be called from native unmanaged code. -
- -The Entity to attach the deserialised Scripts to. - - -Pointer to the YAML::Node that contains serialized script data. - - -
- - -Populates a YAML node with the scripts for a specified Entity. -

-This function should only be called from native unmanaged code. -
- The Entity to Serialise. - -Pointer to a YAML::Node that will be populated with all of the serialised -scripts and their associated fields. - - -True if serialisation is successful. False if the buffer is too small for -the serialised output. - -
- - -Executes OnCollision*() and OnTrigger*() for all scripts. - - - - -Executes LateUpdate() for all scripts. - - - - -Executes Update() for all scripts. - - - - -Executes FixedUpdate() for all scripts. - - - - -Retrieves a immutable list of available scripts that can be added. - - Immutable list of available scripts that can be added. - - - -Cleans up data stored in the ScriptStore to free up memory for garbage -collection. - - - - -Cleans up scripts that were marked for deletion. This calls the OnDestroy() -for these Scripts. - - - - -Sets up scripts that were marked for initialization. This calls the Awake() -and Start() for Scripts that have yet to have done so. - - - - -Initializes the ScriptStore to allocate and pre-populate reflection data. - - - - -Removes all Scripts attached to the specified Entity. Unlike -RemoveAllScripts(), this removes all the scripts immediately. -Does not do anything if the specified Entity is invalid or does not have any -Scripts attached. - - The entity to remove the scripts from. - -Whether or not to call OnDestroy on the scripts.This is ignored if not in -play mode. - - - - -Removes all Scripts attached to the specified Entity. Does not do anything -if the specified Entity is invalid or does not have any Scripts -attached. - - The entity to remove the scripts from. - - - -Removes a specific script from the - - The entity to remove the script from. - The script to remove. - True if successfully removed. False otherwise. - - - -Removes all Scripts of the specified type from the specified Entity. - - -Type of script to remove. -This needs to be a default constructable Script. - - The entity to remove the script from. - -If the specified Entity is invalid. - - - - -Retrieves an immutable list of all scripts attached to a specified Entity. - - -The entity which the scripts to retrieve are attached. - - -Immutable list of references to scripts attached to the specified Entity. -This can also be null if there are no scripts at all or an invalid Entity -was specified. - - - - -Retrieves a immutable list of scripts from the specified Entity that -matches the specified type. -
-Note that this function allocates. It should be used sparingly. -
- -Type of scripts to get. -This needs to be a default constructable Script. - - -The entity which the scripts to retrieve are attached. - - -Immutable list of references to scripts of the specified type. - -
- - -Retrieves the first Script from the specified Entity's children that matches -the specified type. - - -Type of script to get. -This needs to be a default constructable Script. - - -The entity which the script to retrieve is attached. - - -Reference to the script. This can be null if no script of the specified -type is attached. - - -If the specified Entity is invalid. - - - - -Retrieves the first Script from the specified Entity that matches the -specified type. - - -Type of script to get. -This needs to be a default constructable Script. - - -The entity which the script to retrieve is attached. - - -Reference to the script. This can be null if no script of the specified -type is attached. - - -If the specified Entity is invalid. - - - - -Adds a Script to a specified Entity. -
-This function is meant for consumption from native code or for serialisation -purposes. If you are writing in C# or C++/CLI and not doing serialisation, -use AddScript<T>() instead as it is faster. -
- The entity to add a script to. - The entity to add a script to. - -Out parameter handle to the Script that was created. - - -True if successfully added. False otherwise with the error logged to the -console. - -
- - -Adds a Script to a specified Entity. -
-This function is meant for consumption from native code. If you are writing -in C# or C++/CLI, use AddScript<T>() instead as it is faster. -
- The entity to add a script to. - The entity to add a script to. - -True if successfully added. False otherwise with the error logged to the -console. - -
- - -Adds a Script to a specified Entity. - - -Type of script to add. -This needs to be a default constructable PlushieScript. - - The entity to add a script to. - Reference to the script added. - -If the specified Entity is invalid. - - - - -Responsible for managing all scripts attached to Entities as well as executing -all lifecycle functions of scripts. - - - - -Checks if two Colors are not approximately equal. - - Color to compare. - Another Color to compare. - -True if all components are not approximately equal within the default -tolerance value. - - - - -Checks if two Colors are approximately equal. - - Color to compare. - Another Color to compare. - -True if all components are approximately equal within the default -tolerance value. - - - - -Calculates the division of a Color with a scalar value and returns -the result. - - Scalar to divide with. - Color to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Color with a scalar value and returns -the result. - - Color to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the component-wise multiplication of two Colors and returns the -result. - - Color to multiply with. - Another Color to multiply with. - The result of rhs subtracted from lhs. - - - -Subtracts a Color from another Color and returns the result. - - Color to subtract from. - Another Color to subtract. - The result of rhs subtracted from lhs. - - - -Adds two Colors together and returns the result. - - Color to add. - Another Color to add. - The result of lhs added to rhs - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. -Unlike Lerp(), t is not clamped to a range at all. - - The start Color, returned when t = 0.0. - The end Color, returned when t = 1.0. - Value used to interpolate between a and b. - The interpolated Color. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. - - The start Color, returned when t = 0.0. - The end Color, returned when t = 1.0. - -Value used to interpolate between a and b which is clamped to -the range[0, 1]. - - The interpolated Vector3. - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Alpha component of the colour. Ranges from 0.0f to 1.0f. - - - - -Blue component of the colour. Ranges from 0.0f to 1.0f. - - - - -Green component of the colour. Ranges from 0.0f to 1.0f. - - - - -Red component of the colour. Ranges from 0.0f to 1.0f. - - - - -Constructor to construct a Color with the specified components. - - Red component to set. - Green component to set. - Blue component to set. - Alpha component to set. - - - -Constructor to construct a Color with the specified components with the -alpha component set to 1.0f. - - Red component to set. - Green component to set. - Blue component to set. - - - -Constructor to construct a Color with the specified components with the -blue and alpha component set to 1.0f. - - Red component to set. - Green component to set. - - - -Constructor to construct a Color with the specified components with the -green, blue and alpha component set to 1.0f. - - Red component to set. - - - -Pure yellow, mix of pure red and green. - - - - -Pure magenta, mix of pure red and blue. - - - - -Pure cyan, mix of pure green and blue. - - - - -Pure blue. - - - - -Pure green. - - - - -Pure red. - - - - -Pure white. - - - - -Dark Gray, darker than gray. - - - - -Gray, halfway between black and white. - - - - -Light Gray, lighter than gray. - - - - -Pure black. - - - - -A static class that contains a set of default Colors. - - - - -CLR version of the the SHADE Engine's Color struct which describes a Color -encoded using floating point numbers that range from 0.0f to 1.0f. - - - - -Creates a inline button widget. -
-Wraps up ImGui::Button(). -
- Text to display. - True if button was pressed. -
- - -Creates a small inline button widget. -
-Wraps up ImGui::SmallButton(). -
- Text to display. - True if button was pressed. -
- - -Creates a visual text widget. -
-Wraps up ImGui::Text(). -
- Text to display. -
- - -Creates a menu item in the list of items for a mini popup. -
-Wraps up ImGui::MenuItem(). -
- Label used to identify this widget. - Whether or not the menu item was selected. -
- - -Opens the popup that was defined with the specified label. -
-Wraps up ImGui::OpenPopup(). -
-
- - -Marks the end of a definition of a mini pop up that can show options. -
-Wraps up ImGui::EndPopup(). -
-
- - -Marks the start of a definition of a mini pop up that can show options. -
-Wraps up ImGui::BeginPopup(). -
- Label used to identify this widget. - Whether or not the pop up is open. -
- - -Creates a collapsing title header. -
-Wraps up ImGui::CollapsingHeader(). -
- Label for the header. - True if the header is open, false otherwise. -
- - -Unindents the widgets rendered after this call. -
-Wraps up ImGui::Unindent(). -
-
- - -Indents the widgets rendered after this call. -
-Wraps up ImGui::Indent(). -
-
- - -Marks the end of a stack of ImGui widgets from the last PushID() call. -
-Wraps up ImGui::PopID(). -
-
- - -Marks the start of a stack of ImGui widgets with the specified id. -
-Wraps up ImGui::PushID(). -
- Integer-based ID. -
- - -Marks the start of a stack of ImGui widgets with the specified id. -
-Wraps up ImGui::PushID(). -
- String-based ID. -
- - -Maximum length of a string supported by InputTextField() - - - - -Static class that contains useful functions for Editor UI using ImGui. - - - - -Redoes the last undo-ed command if it exists. - - - - -Undos the last added command if it exists. - - - - -Adds a command onto the stack. - - - - - -True if there is a redoable action in the stack. - - - - -True if there is an undoable action in the stack. - - - - -Command for the stack that represents a data modification. - - - - -Class that is able to store a stack of actions that can be done and redone. - - - - -To be called from native code when a Collision Shape has been changed. - - -The entity which has it's collision shape changed. - - - - -To be called from native code when a collision shape has been removed. - - The entity which has it's collision shape removed. - - - -Retrieves a ColliderBound at the specified index in the ColliderBound list -and casts it to the appropriate type. - - Type of the ColliderBound to cast to. - Index to retrieve a ColliderBound from. - ColliderBound for the specified index. - - - -Retrieves a ColliderBound at the specified index in the ColliderBound list. - - Index to retrieve a ColliderBound from. - ColliderBound for the specified index. - - - -Total number of ColliderShapes in the Collider component. - - - - -Constructs a Collider Component that represents a native SHColliderComponent -component tied to the specified Entity. - - Entity that this Component will be tied to. - - - -CLR version of the the SHADE Engine's SHColliderComponent. -A single Collider component can contain one or multiple Collider Bounds. - - - - - - - - - - -Radius of the Bounding Sphere formed by this bound. - - - - -Center of the Bounding Sphere formed by this bound. - - - - -Sphere-shaped Collider Bound. - - - - - - - - - - -Position of the top right front corner of the Bounding Box formed by this -bound. - - - - -Position of the bottom left back corner of the Bounding Box formed by this -bound. - - - - -Half of the scale of the Bounding Box formed by this bound. - - - - -Center of the Bounding Box formed by this bound. - - - - -Box-shaped Collider Bound. - - - - -Computes a Raycast and checks if there is a collision with any object. - - The ray to cast. - Maximum distance for the raycast check. - True if the ray intersects with an object in the scene. - - - -Checks if the specified point is within this shape's bounds. - - Point to test with. - True if the point is in the shape's bounds. - - - -Base interface for all Collider Shapes. - - - -@brief The density of the collider that determines the mass of the collision shape - if it is automatically computed. Must be a positive number. - - - -@brief The bounciness factor of the physics object., clamped between [0,1].
- 0 means the object will never bounce. - 1 means the object never loses energy on a bounce. - -
- -@brief The friction coefficient of the physics object., clamped between [0,1].
- 0 means the object will never experience friction. - 1 means the friction force against the object is equal to the applied force. - -
- -@brief Sets the mass density of the physics material. -@param newDensity The density value to set. Always made positive. - - - -@brief Sets the bounciness factor of the physics material. -@param newBounciness The bounciness value to set. Clamped between [0,1]. - - - -@brief Sets the friction coefficient of the physics material. -@param newFriction The friction value to set. Clamped between [0,1]. - - - -@brief Default constructor for a physics material. -@param friction The friction of the material. Clamped between [0,1]. Defaults to 0.4. -@param bounciness The bounciness of the material. Clamped between [0,1]. -@param density The mass density of the material. Always made positive. - - - - -Closes the current window, and depending on the implementation, should also -close the application. - - - - -Retrieves the current window fullscreen status. - - The current window fullscreen status.. - - - -Retrieves the current window height. - - The current window height. - - - -Retrieves the current window width. - - The current window width. - - - -Static class that wraps up certain functions in the SHGraphicsSystem so that -accessing it from SHADE_Managed would not cause issues due to C++20 features. - - - - @brief Perform ImGui and ImGui Backend Render - - - - - @brief Start new frame for editor - - - - - @brief Initialise Backend for ImGui (SDL and Vulkan backend) - - @param sdlWindow Pointer to SDL_Window - - - - @brief Set the Style for the editor - - @param style Desired style - - - - @brief Safely shutdown the editor - - - - - @brief Update the editor and add to ImGui DrawList - - @param dt Delta-time of the frame - - - - @brief Initialise the editor - - @param sdlWindow pointer to SDL_Window object created in application - - - - @brief Style options - - - - - @brief SHEditor static class contains editor variables and implementation of editor functions. - - - - - Get the YUV conversion mode, returning the correct mode for the resolution - when the current conversion mode is SDL_YUV_CONVERSION_AUTOMATIC - - \since This function is available since SDL 2.0.8. - - - - Get the YUV conversion mode - - \since This function is available since SDL 2.0.8. - - - - Set the YUV conversion mode - - \since This function is available since SDL 2.0.8. - - - - Perform low-level surface scaled blitting only. - - This is a semi-private function and it performs low-level surface blitting, - assuming the input rectangles have already been clipped. - - \param src the SDL_Surface structure to be copied from - \param srcrect the SDL_Rect structure representing the rectangle to be - copied - \param dst the SDL_Surface structure that is the blit target - \param dstrect the SDL_Rect structure representing the rectangle that is - copied into - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitScaled - - - - Perform a scaled surface copy to a destination surface. - - SDL_UpperBlitScaled() has been replaced by SDL_BlitScaled(), which is - merely a macro for this function with a less confusing name. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitScaled - - - - Perform bilinear scaling between two surfaces of the same format, 32BPP. - - \since This function is available since SDL 2.0.16. - - - - Perform a fast, low quality, stretch blit between two surfaces of the same - format. - - Please use SDL_BlitScaled() instead. - - \since This function is available since SDL 2.0.0. - - - - Perform low-level surface blitting only. - - This is a semi-private blit function and it performs low-level surface - blitting, assuming the input rectangles have already been clipped. - - Unless you know what you're doing, you should be using SDL_BlitSurface() - instead. - - \param src the SDL_Surface structure to be copied from - \param srcrect the SDL_Rect structure representing the rectangle to be - copied, or NULL to copy the entire surface - \param dst the SDL_Surface structure that is the blit target - \param dstrect the SDL_Rect structure representing the rectangle that is - copied into - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - - - - * Performs a fast blit from the source surface to the destination surface. - * - * This assumes that the source and destination rectangles are - * the same size. If either \c srcrect or \c dstrect are NULL, the entire - * surface (\c src or \c dst) is copied. The final blit rectangles are saved - * in \c srcrect and \c dstrect after all clipping is performed. - * - * \returns 0 if the blit is successful, otherwise it returns -1. - * - * The blit function should not be called on a locked surface. - * - * The blit semantics for surfaces with and without blending and colorkey - * are defined as follows: - * \verbatim - RGBA->RGB: - Source surface blend mode set to SDL_BLENDMODE_BLEND: - alpha-blend (using the source alpha-channel and per-surface alpha) - SDL_SRCCOLORKEY ignored. - Source surface blend mode set to SDL_BLENDMODE_NONE: - copy RGB. - if SDL_SRCCOLORKEY set, only copy the pixels matching the - RGB values of the source color key, ignoring alpha in the - comparison. - - RGB->RGBA: - Source surface blend mode set to SDL_BLENDMODE_BLEND: - alpha-blend (using the source per-surface alpha) - Source surface blend mode set to SDL_BLENDMODE_NONE: - copy RGB, set destination alpha to source per-surface alpha value. - both: - if SDL_SRCCOLORKEY set, only copy the pixels matching the - source color key. - - RGBA->RGBA: - Source surface blend mode set to SDL_BLENDMODE_BLEND: - alpha-blend (using the source alpha-channel and per-surface alpha) - SDL_SRCCOLORKEY ignored. - Source surface blend mode set to SDL_BLENDMODE_NONE: - copy all of RGBA to the destination. - if SDL_SRCCOLORKEY set, only copy the pixels matching the - RGB values of the source color key, ignoring alpha in the - comparison. - - RGB->RGB: - Source surface blend mode set to SDL_BLENDMODE_BLEND: - alpha-blend (using the source per-surface alpha) - Source surface blend mode set to SDL_BLENDMODE_NONE: - copy RGB. - both: - if SDL_SRCCOLORKEY set, only copy the pixels matching the - source color key. - \endverbatim - * - * You should call SDL_BlitSurface() unless you know exactly how SDL - * blitting works internally and how to use the other blit functions. - - Perform a fast blit from the source surface to the destination surface. - - SDL_UpperBlit() has been replaced by SDL_BlitSurface(), which is merely a - macro for this function with a less confusing name. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - - - - Perform a fast fill of a set of rectangles with a specific color. - - `color` should be a pixel of the format used by the surface, and can be - generated by SDL_MapRGB() or SDL_MapRGBA(). If the color value contains an - alpha component then the destination is simply filled with that alpha - information, no blending takes place. - - If there is a clip rectangle set on the destination (set via - SDL_SetClipRect()), then this function will fill based on the intersection - of the clip rectangle and `rect`. - - \param dst the SDL_Surface structure that is the drawing target - \param rects an array of SDL_Rects representing the rectangles to fill. - \param count the number of rectangles in the array - \param color the color to fill with - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FillRect - - - - Perform a fast fill of a rectangle with a specific color. - - `color` should be a pixel of the format used by the surface, and can be - generated by SDL_MapRGB() or SDL_MapRGBA(). If the color value contains an - alpha component then the destination is simply filled with that alpha - information, no blending takes place. - - If there is a clip rectangle set on the destination (set via - SDL_SetClipRect()), then this function will fill based on the intersection - of the clip rectangle and `rect`. - - \param dst the SDL_Surface structure that is the drawing target - \param rect the SDL_Rect structure representing the rectangle to fill, or - NULL to fill the entire surface - \param color the color to fill with - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FillRects - - - - Premultiply the alpha on a block of pixels. - - This is safe to use with src == dst, but not for other overlapping areas. - - This function is currently only implemented for SDL_PIXELFORMAT_ARGB8888. - - \param width the width of the block to convert, in pixels - \param height the height of the block to convert, in pixels - \param src_format an SDL_PixelFormatEnum value of the `src` pixels format - \param src a pointer to the source pixels - \param src_pitch the pitch of the source pixels, in bytes - \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format - \param dst a pointer to be filled in with premultiplied pixel data - \param dst_pitch the pitch of the destination pixels, in bytes - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.18. - - - - Copy a block of pixels of one format to another format. - - \param width the width of the block to copy, in pixels - \param height the height of the block to copy, in pixels - \param src_format an SDL_PixelFormatEnum value of the `src` pixels format - \param src a pointer to the source pixels - \param src_pitch the pitch of the source pixels, in bytes - \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format - \param dst a pointer to be filled in with new pixel data - \param dst_pitch the pitch of the destination pixels, in bytes - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - - - Copy an existing surface to a new surface of the specified format enum. - - This function operates just like SDL_ConvertSurface(), but accepts an - SDL_PixelFormatEnum value instead of an SDL_PixelFormat structure. As such, - it might be easier to call but it doesn't have access to palette - information for the destination surface, in case that would be important. - - \param src the existing SDL_Surface structure to convert - \param pixel_format the SDL_PixelFormatEnum that the new surface is - optimized for - \param flags the flags are unused and should be set to 0; this is a - leftover from SDL 1.2's API - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocFormat - \sa SDL_ConvertSurface - \sa SDL_CreateRGBSurface - - - - Copy an existing surface to a new surface of the specified format. - - This function is used to optimize images for faster *repeat* blitting. This - is accomplished by converting the original and storing the result as a new - surface. The new, optimized surface can then be used as the source for - future blits, making them faster. - - \param src the existing SDL_Surface structure to convert - \param fmt the SDL_PixelFormat structure that the new surface is optimized - for - \param flags the flags are unused and should be set to 0; this is a - leftover from SDL 1.2's API - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocFormat - \sa SDL_ConvertSurfaceFormat - \sa SDL_CreateRGBSurface - - - - Get the clipping rectangle for a surface. - - When `surface` is the destination of a blit, only the area within the clip - rectangle is drawn into. - - \param surface the SDL_Surface structure representing the surface to be - clipped - \param rect an SDL_Rect structure filled in with the clipping rectangle for - the surface - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_SetClipRect - - - - Set the clipping rectangle for a surface. - - When `surface` is the destination of a blit, only the area within the clip - rectangle is drawn into. - - Note that blits are automatically clipped to the edges of the source and - destination surfaces. - - \param surface the SDL_Surface structure to be clipped - \param rect the SDL_Rect structure representing the clipping rectangle, or - NULL to disable clipping - \returns SDL_TRUE if the rectangle intersects the surface, otherwise - SDL_FALSE and blits will be completely clipped. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_GetClipRect - - - - Get the blend mode used for blit operations. - - \param surface the SDL_Surface structure to query - \param blendMode a pointer filled in with the current SDL_BlendMode - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_SetSurfaceBlendMode - - - - Set the blend mode used for blit operations. - - To copy a surface to another surface (or texture) without blending with the - existing data, the blendmode of the SOURCE surface should be set to - `SDL_BLENDMODE_NONE`. - - \param surface the SDL_Surface structure to update - \param blendMode the SDL_BlendMode to use for blit blending - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceBlendMode - - - - Get the additional alpha value used in blit operations. - - \param surface the SDL_Surface structure to query - \param alpha a pointer filled in with the current alpha value - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceColorMod - \sa SDL_SetSurfaceAlphaMod - - - - Set an additional alpha value used in blit operations. - - When this surface is blitted, during the blit operation the source alpha - value is modulated by this alpha value according to the following formula: - - `srcA = srcA * (alpha / 255)` - - \param surface the SDL_Surface structure to update - \param alpha the alpha value multiplied into blit operations - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceAlphaMod - \sa SDL_SetSurfaceColorMod - - - - Get the additional color value multiplied into blit operations. - - \param surface the SDL_Surface structure to query - \param r a pointer filled in with the current red color value - \param g a pointer filled in with the current green color value - \param b a pointer filled in with the current blue color value - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceAlphaMod - \sa SDL_SetSurfaceColorMod - - - - Set an additional color value multiplied into blit operations. - - When this surface is blitted, during the blit operation each source color - channel is modulated by the appropriate color value according to the - following formula: - - `srcC = srcC * (color / 255)` - - \param surface the SDL_Surface structure to update - \param r the red color value multiplied into blit operations - \param g the green color value multiplied into blit operations - \param b the blue color value multiplied into blit operations - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceColorMod - \sa SDL_SetSurfaceAlphaMod - - - - Get the color key (transparent pixel) for a surface. - - The color key is a pixel of the format used by the surface, as generated by - SDL_MapRGB(). - - If the surface doesn't have color key enabled this function returns -1. - - \param surface the SDL_Surface structure to query - \param key a pointer filled in with the transparent pixel - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_SetColorKey - - - - Returns whether the surface has a color key - - It is safe to pass a NULL `surface` here; it will return SDL_FALSE. - - \param surface the SDL_Surface structure to query - \return SDL_TRUE if the surface has a color key, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.9. - - \sa SDL_SetColorKey - \sa SDL_GetColorKey - - - - Set the color key (transparent pixel) in a surface. - - The color key defines a pixel value that will be treated as transparent in - a blit. For example, one can use this to specify that cyan pixels should be - considered transparent, and therefore not rendered. - - It is a pixel of the format used by the surface, as generated by - SDL_MapRGB(). - - RLE acceleration can substantially speed up blitting of images with large - horizontal runs of transparent pixels. See SDL_SetSurfaceRLE() for details. - - \param surface the SDL_Surface structure to update - \param flag SDL_TRUE to enable color key, SDL_FALSE to disable color key - \param key the transparent pixel - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_GetColorKey - - - - Returns whether the surface is RLE enabled - - It is safe to pass a NULL `surface` here; it will return SDL_FALSE. - - \param surface the SDL_Surface structure to query - \returns SDL_TRUE if the surface is RLE enabled, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.14. - - \sa SDL_SetSurfaceRLE - - - - Save a surface to a file. - - Convenience macro. - - Set the RLE acceleration hint for a surface. - - If RLE is enabled, color key and alpha blending blits are much faster, but - the surface must be locked before directly accessing the pixels. - - \param surface the SDL_Surface structure to optimize - \param flag 0 to disable, non-zero to enable RLE acceleration - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_LockSurface - \sa SDL_UnlockSurface - - - - Load a surface from a file. - - Convenience macro. - - Save a surface to a seekable SDL data stream in BMP format. - - Surfaces with a 24-bit, 32-bit and paletted 8-bit format get saved in the - BMP directly. Other RGB formats with 8-bit or higher get converted to a - 24-bit surface or, if they have an alpha mask or a colorkey, to a 32-bit - surface before they are saved. YUV and paletted 1-bit and 4-bit formats are - not supported. - - \param surface the SDL_Surface structure containing the image to be saved - \param dst a data stream to save to - \param freedst non-zero to close the stream after being written - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_LoadBMP_RW - \sa SDL_SaveBMP - - - - Load a BMP image from a seekable SDL data stream. - - The new surface should be freed with SDL_FreeSurface(). Not doing so will - result in a memory leak. - - src is an open SDL_RWops buffer, typically loaded with SDL_RWFromFile. - Alternitavely, you might also use the macro SDL_LoadBMP to load a bitmap - from a file, convert it to an SDL_Surface and then close the file. - - \param src the data stream for the surface - \param freesrc non-zero to close the stream after being read - \returns a pointer to a new SDL_Surface structure or NULL if there was an - error; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FreeSurface - \sa SDL_RWFromFile - \sa SDL_LoadBMP - \sa SDL_SaveBMP_RW - - - - Release a surface after directly accessing the pixels. - - \param surface the SDL_Surface structure to be unlocked - - \since This function is available since SDL 2.0.0. - - \sa SDL_LockSurface - - - - Set up a surface for directly accessing the pixels. - - Between calls to SDL_LockSurface() / SDL_UnlockSurface(), you can write to - and read from `surface->pixels`, using the pixel format stored in - `surface->format`. Once you are done accessing the surface, you should use - SDL_UnlockSurface() to release it. - - Not all surfaces require locking. If `SDL_MUSTLOCK(surface)` evaluates to - 0, then you can read and write to the surface at any time, and the pixel - format of the surface will not change. - - \param surface the SDL_Surface structure to be locked - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_MUSTLOCK - \sa SDL_UnlockSurface - - - - Set the palette used by a surface. - - A single palette can be shared with many surfaces. - - \param surface the SDL_Surface structure to update - \param palette the SDL_Palette structure to use - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - - - Free an RGB surface. - - It is safe to pass NULL to this function. - - \param surface the SDL_Surface to free. - - \since This function is available since SDL 2.0.0. - - \sa SDL_CreateRGBSurface - \sa SDL_CreateRGBSurfaceFrom - \sa SDL_LoadBMP - \sa SDL_LoadBMP_RW - - - - Allocate a new RGB surface with with a specific pixel format and existing - pixel data. - - This function operates mostly like SDL_CreateRGBSurfaceFrom(), except - instead of providing pixel color masks, you provide it with a predefined - format from SDL_PixelFormatEnum. - - No copy is made of the pixel data. Pixel data is not managed automatically; - you must free the surface before you free the pixel data. - - \param pixels a pointer to existing pixel data - \param width the width of the surface - \param height the height of the surface - \param depth the depth of the surface in bits - \param pitch the pitch of the surface in bytes - \param format the SDL_PixelFormatEnum for the new surface's pixel format. - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.5. - - \sa SDL_CreateRGBSurfaceFrom - \sa SDL_CreateRGBSurfaceWithFormat - \sa SDL_FreeSurface - - - - Allocate a new RGB surface with existing pixel data. - - This function operates mostly like SDL_CreateRGBSurface(), except it does - not allocate memory for the pixel data, instead the caller provides an - existing buffer of data for the surface to use. - - No copy is made of the pixel data. Pixel data is not managed automatically; - you must free the surface before you free the pixel data. - - \param pixels a pointer to existing pixel data - \param width the width of the surface - \param height the height of the surface - \param depth the depth of the surface in bits - \param pitch the pitch of the surface in bytes - \param Rmask the red mask for the pixels - \param Gmask the green mask for the pixels - \param Bmask the blue mask for the pixels - \param Amask the alpha mask for the pixels - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_CreateRGBSurface - \sa SDL_CreateRGBSurfaceWithFormat - \sa SDL_FreeSurface - - - - Allocate a new RGB surface with a specific pixel format. - - This function operates mostly like SDL_CreateRGBSurface(), except instead - of providing pixel color masks, you provide it with a predefined format - from SDL_PixelFormatEnum. - - \param flags the flags are unused and should be set to 0 - \param width the width of the surface - \param height the height of the surface - \param depth the depth of the surface in bits - \param format the SDL_PixelFormatEnum for the new surface's pixel format. - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.5. - - \sa SDL_CreateRGBSurface - \sa SDL_CreateRGBSurfaceFrom - \sa SDL_FreeSurface - - - - Allocate a new RGB surface. - - If `depth` is 4 or 8 bits, an empty palette is allocated for the surface. - If `depth` is greater than 8 bits, the pixel format is set using the - [RGBA]mask parameters. - - The [RGBA]mask parameters are the bitmasks used to extract that color from - a pixel. For instance, `Rmask` being 0xFF000000 means the red data is - stored in the most significant byte. Using zeros for the RGB masks sets a - default value, based on the depth. For example: - - ```c++ - SDL_CreateRGBSurface(0,w,h,32,0,0,0,0); - ``` - - However, using zero for the Amask results in an Amask of 0. - - By default surfaces with an alpha mask are set up for blending as with: - - ```c++ - SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND) - ``` - - You can change this by calling SDL_SetSurfaceBlendMode() and selecting a - different `blendMode`. - - \param flags the flags are unused and should be set to 0 - \param width the width of the surface - \param height the height of the surface - \param depth the depth of the surface in bits - \param Rmask the red mask for the pixels - \param Gmask the green mask for the pixels - \param Bmask the blue mask for the pixels - \param Amask the alpha mask for the pixels - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_CreateRGBSurfaceFrom - \sa SDL_CreateRGBSurfaceWithFormat - \sa SDL_FreeSurface - - - -Reference count -- used when freeing surface - - -info for fast blit mapping to other surfaces - - -clipping information - - -list of BlitMap that hold a reference to this surface - - -information needed for surfaces requiring locks - - -Application data associated with the surface - - - \brief A collection of pixels used in software blitting. - - \note This structure should be treated as read-only, except for \c pixels, - which, if not NULL, contains the raw pixel data for the surface. - - -\brief The type of function used for surface blitting functions. - - - - Compose a custom blend mode for renderers. - - The functions SDL_SetRenderDrawBlendMode and SDL_SetTextureBlendMode accept - the SDL_BlendMode returned by this function if the renderer supports it. - - A blend mode controls how the pixels from a drawing operation (source) get - combined with the pixels from the render target (destination). First, the - components of the source and destination pixels get multiplied with their - blend factors. Then, the blend operation takes the two products and - calculates the result that will get stored in the render target. - - Expressed in pseudocode, it would look like this: - - ```c - dstRGB = colorOperation(srcRGB * srcColorFactor, dstRGB * dstColorFactor); - dstA = alphaOperation(srcA * srcAlphaFactor, dstA * dstAlphaFactor); - ``` - - Where the functions `colorOperation(src, dst)` and `alphaOperation(src, - dst)` can return one of the following: - - - `src + dst` - - `src - dst` - - `dst - src` - - `min(src, dst)` - - `max(src, dst)` - - The red, green, and blue components are always multiplied with the first, - second, and third components of the SDL_BlendFactor, respectively. The - fourth component is not used. - - The alpha component is always multiplied with the fourth component of the - SDL_BlendFactor. The other components are not used in the alpha - calculation. - - Support for these blend modes varies for each renderer. To check if a - specific SDL_BlendMode is supported, create a renderer and pass it to - either SDL_SetRenderDrawBlendMode or SDL_SetTextureBlendMode. They will - return with an error if the blend mode is not supported. - - This list describes the support of custom blend modes for each renderer in - SDL 2.0.6. All renderers support the four blend modes listed in the - SDL_BlendMode enumeration. - - - **direct3d**: Supports all operations with all factors. However, some - factors produce unexpected results with `SDL_BLENDOPERATION_MINIMUM` and - `SDL_BLENDOPERATION_MAXIMUM`. - - **direct3d11**: Same as Direct3D 9. - - **opengl**: Supports the `SDL_BLENDOPERATION_ADD` operation with all - factors. OpenGL versions 1.1, 1.2, and 1.3 do not work correctly with SDL - 2.0.6. - - **opengles**: Supports the `SDL_BLENDOPERATION_ADD` operation with all - factors. Color and alpha factors need to be the same. OpenGL ES 1 - implementation specific: May also support `SDL_BLENDOPERATION_SUBTRACT` - and `SDL_BLENDOPERATION_REV_SUBTRACT`. May support color and alpha - operations being different from each other. May support color and alpha - factors being different from each other. - - **opengles2**: Supports the `SDL_BLENDOPERATION_ADD`, - `SDL_BLENDOPERATION_SUBTRACT`, `SDL_BLENDOPERATION_REV_SUBTRACT` - operations with all factors. - - **psp**: No custom blend mode support. - - **software**: No custom blend mode support. - - Some renderers do not provide an alpha component for the default render - target. The `SDL_BLENDFACTOR_DST_ALPHA` and - `SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA` factors do not have an effect in this - case. - - \param srcColorFactor the SDL_BlendFactor applied to the red, green, and - blue components of the source pixels - \param dstColorFactor the SDL_BlendFactor applied to the red, green, and - blue components of the destination pixels - \param colorOperation the SDL_BlendOperation used to combine the red, - green, and blue components of the source and - destination pixels - \param srcAlphaFactor the SDL_BlendFactor applied to the alpha component of - the source pixels - \param dstAlphaFactor the SDL_BlendFactor applied to the alpha component of - the destination pixels - \param alphaOperation the SDL_BlendOperation used to combine the alpha - component of the source and destination pixels - \returns an SDL_BlendMode that represents the chosen factors and - operations. - - \since This function is available since SDL 2.0.6. - - \sa SDL_SetRenderDrawBlendMode - \sa SDL_GetRenderDrawBlendMode - \sa SDL_SetTextureBlendMode - \sa SDL_GetTextureBlendMode - - - - Calculate the intersection of a rectangle and line segment with float - precision. - - This function is used to clip a line segment to a rectangle. A line segment - contained entirely within the rectangle or that does not intersect will - remain unchanged. A line segment that crosses the rectangle at either or - both ends will be clipped to the boundary of the rectangle and the new - coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary. - - \param rect an SDL_FRect structure representing the rectangle to intersect - \param X1 a pointer to the starting X-coordinate of the line - \param Y1 a pointer to the starting Y-coordinate of the line - \param X2 a pointer to the ending X-coordinate of the line - \param Y2 a pointer to the ending Y-coordinate of the line - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.22. - - - - Calculate a minimal rectangle enclosing a set of points with float - precision. - - If `clip` is not NULL then only points inside of the clipping rectangle are - considered. - - \param points an array of SDL_FPoint structures representing points to be - enclosed - \param count the number of structures in the `points` array - \param clip an SDL_FRect used for clipping or NULL to enclose all points - \param result an SDL_FRect structure filled in with the minimal enclosing - rectangle - \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the - points were outside of the clipping rectangle. - - \since This function is available since SDL 2.0.22. - - - - Calculate the union of two rectangles with float precision. - - \param A an SDL_FRect structure representing the first rectangle - \param B an SDL_FRect structure representing the second rectangle - \param result an SDL_FRect structure filled in with the union of rectangles - `A` and `B` - - \since This function is available since SDL 2.0.22. - - - - Calculate the intersection of two rectangles with float precision. - - If `result` is NULL then this function will return SDL_FALSE. - - \param A an SDL_FRect structure representing the first rectangle - \param B an SDL_FRect structure representing the second rectangle - \param result an SDL_FRect structure filled in with the intersection of - rectangles `A` and `B` - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.22. - - \sa SDL_HasIntersectionF - - - - Determine whether two rectangles intersect with float precision. - - If either pointer is NULL the function will return SDL_FALSE. - - \param A an SDL_FRect structure representing the first rectangle - \param B an SDL_FRect structure representing the second rectangle - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.22. - - \sa SDL_IntersectRect - - - - Returns true if the two rectangles are equal, using a default epsilon. - - \since This function is available since SDL 2.0.22. - - - - Returns true if the two rectangles are equal, within some given epsilon. - - \since This function is available since SDL 2.0.22. - - - -Returns true if the rectangle has no area. - - - -Returns true if point resides inside a rectangle. - - - - Calculate the intersection of a rectangle and line segment. - - This function is used to clip a line segment to a rectangle. A line segment - contained entirely within the rectangle or that does not intersect will - remain unchanged. A line segment that crosses the rectangle at either or - both ends will be clipped to the boundary of the rectangle and the new - coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary. - - \param rect an SDL_Rect structure representing the rectangle to intersect - \param X1 a pointer to the starting X-coordinate of the line - \param Y1 a pointer to the starting Y-coordinate of the line - \param X2 a pointer to the ending X-coordinate of the line - \param Y2 a pointer to the ending Y-coordinate of the line - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.0. - - - - Calculate a minimal rectangle enclosing a set of points. - - If `clip` is not NULL then only points inside of the clipping rectangle are - considered. - - \param points an array of SDL_Point structures representing points to be - enclosed - \param count the number of structures in the `points` array - \param clip an SDL_Rect used for clipping or NULL to enclose all points - \param result an SDL_Rect structure filled in with the minimal enclosing - rectangle - \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the - points were outside of the clipping rectangle. - - \since This function is available since SDL 2.0.0. - - - - Calculate the union of two rectangles. - - \param A an SDL_Rect structure representing the first rectangle - \param B an SDL_Rect structure representing the second rectangle - \param result an SDL_Rect structure filled in with the union of rectangles - `A` and `B` - - \since This function is available since SDL 2.0.0. - - - - Calculate the intersection of two rectangles. - - If `result` is NULL then this function will return SDL_FALSE. - - \param A an SDL_Rect structure representing the first rectangle - \param B an SDL_Rect structure representing the second rectangle - \param result an SDL_Rect structure filled in with the intersection of - rectangles `A` and `B` - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.0. - - \sa SDL_HasIntersection - - - - Determine whether two rectangles intersect. - - If either pointer is NULL the function will return SDL_FALSE. - - \param A an SDL_Rect structure representing the first rectangle - \param B an SDL_Rect structure representing the second rectangle - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.0. - - \sa SDL_IntersectRect - - - -Returns true if the two rectangles are equal. - - - -Returns true if the rectangle has no area. - - - -Returns true if point resides inside a rectangle. - - - - A rectangle, with the origin at the upper left (floating point). - - \sa SDL_FRectEmpty - \sa SDL_FRectEquals - \sa SDL_FRectEqualsEpsilon - \sa SDL_HasIntersectionF - \sa SDL_IntersectFRect - \sa SDL_IntersectFRectAndLine - \sa SDL_UnionFRect - \sa SDL_EncloseFPoints - \sa SDL_PointInFRect - - - - A rectangle, with the origin at the upper left (integer). - - \sa SDL_RectEmpty - \sa SDL_RectEquals - \sa SDL_HasIntersection - \sa SDL_IntersectRect - \sa SDL_IntersectRectAndLine - \sa SDL_UnionRect - \sa SDL_EnclosePoints - - - - The structure that defines a point (floating point) - - \sa SDL_EncloseFPoints - \sa SDL_PointInFRect - - - - Use this function to write 64 bits in native format to a SDL_RWops as - big-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in big-endian format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteLE64 - - - - Use this function to write 64 bits in native format to a SDL_RWops as - little-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in little-endian - format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteBE64 - - - - Use this function to write 32 bits in native format to a SDL_RWops as - big-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in big-endian format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteLE32 - - - - Use this function to write 32 bits in native format to a SDL_RWops as - little-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in little-endian - format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteBE32 - - - - Use this function to write 16 bits in native format to a SDL_RWops as - big-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in big-endian format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteLE16 - - - - Use this function to write 16 bits in native format to a SDL_RWops as - little-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in little-endian - format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteBE16 - - - - \name Write endian functions - - Write an item of native format to the specified endianness. - - Use this function to write a byte to an SDL_RWops. - - \param dst the SDL_RWops to write to - \param value the byte value to write - \returns 1 on success or 0 on failure; call SDL_GetError() for more - information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadU8 - - - - Use this function to read 64 bits of big-endian data from an SDL_RWops and - return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 64 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadLE64 - - - - Use this function to read 64 bits of little-endian data from an SDL_RWops - and return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 64 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadBE64 - - - - Use this function to read 32 bits of big-endian data from an SDL_RWops and - return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 32 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadLE32 - - - - Use this function to read 32 bits of little-endian data from an SDL_RWops - and return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 32 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadBE32 - - - - Use this function to read 16 bits of big-endian data from an SDL_RWops and - return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 16 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadLE16 - - - - Use this function to read 16 bits of little-endian data from an SDL_RWops - and return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 16 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadBE16 - - - - \name Read endian functions - - Read an item of the specified endianness and return in native format. - - Use this function to read a byte from an SDL_RWops. - - \param src the SDL_RWops to read from - \returns the read byte on success or 0 on failure; call SDL_GetError() for - more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteU8 - - - - Load all the data from a file path. - - The data is allocated with a zero byte at the end (null terminated) for - convenience. This extra byte is not included in the value reported via - `datasize`. - - The data should be freed with SDL_free(). - - Prior to SDL 2.0.10, this function was a macro wrapping around - SDL_LoadFile_RW. - - \param file the path to read all available data from - \param datasize if not NULL, will store the number of bytes read - \returns the data, or NULL if there was an error. - - \since This function is available since SDL 2.0.10. - - - - Load all the data from an SDL data stream. - - The data is allocated with a zero byte at the end (null terminated) for - convenience. This extra byte is not included in the value reported via - `datasize`. - - The data should be freed with SDL_free(). - - \param src the SDL_RWops to read all available data from - \param datasize if not NULL, will store the number of bytes read - \param freesrc if non-zero, calls SDL_RWclose() on `src` before returning - \returns the data, or NULL if there was an error. - - \since This function is available since SDL 2.0.6. - - - - Close and free an allocated SDL_RWops structure. - - SDL_RWclose() closes and cleans up the SDL_RWops stream. It releases any - resources used by the stream and frees the SDL_RWops itself with - SDL_FreeRW(). This returns 0 on success, or -1 if the stream failed to - flush to its output (e.g. to disk). - - Note that if this fails to flush the stream to disk, this function reports - an error, but the SDL_RWops is still invalid once this function returns. - - Prior to SDL 2.0.10, this function was a macro. - - \param context SDL_RWops structure to close - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWwrite - - - - Write to an SDL_RWops data stream. - - This function writes exactly `num` objects each of size `size` from the - area pointed at by `ptr` to the stream. If this fails for any reason, it'll - return less than `num` to demonstrate how far the write progressed. On - success, it returns `num`. - - SDL_RWwrite is actually a function wrapper that calls the SDL_RWops's - `write` method appropriately, to simplify application development. - - Prior to SDL 2.0.10, this function was a macro. - - \param context a pointer to an SDL_RWops structure - \param ptr a pointer to a buffer containing data to write - \param size the size of an object to write, in bytes - \param num the number of objects to write - \returns the number of objects written, which will be less than **num** on - error; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - - - - Read from a data source. - - This function reads up to `maxnum` objects each of size `size` from the - data source to the area pointed at by `ptr`. This function may read less - objects than requested. It will return zero when there has been an error or - the data stream is completely read. - - SDL_RWread() is actually a function wrapper that calls the SDL_RWops's - `read` method appropriately, to simplify application development. - - Prior to SDL 2.0.10, this function was a macro. - - \param context a pointer to an SDL_RWops structure - \param ptr a pointer to a buffer to read data into - \param size the size of each object to read, in bytes - \param maxnum the maximum number of objects to be read - \returns the number of objects read, or 0 at error or end of file; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWseek - \sa SDL_RWwrite - - - - Determine the current read/write offset in an SDL_RWops data stream. - - SDL_RWtell is actually a wrapper function that calls the SDL_RWops's `seek` - method, with an offset of 0 bytes from `RW_SEEK_CUR`, to simplify - application development. - - Prior to SDL 2.0.10, this function was a macro. - - \param context a SDL_RWops data stream object from which to get the current - offset - \returns the current offset in the stream, or -1 if the information can not - be determined. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWwrite - - - - Seek within an SDL_RWops data stream. - - This function seeks to byte `offset`, relative to `whence`. - - `whence` may be any of the following values: - - - `RW_SEEK_SET`: seek from the beginning of data - - `RW_SEEK_CUR`: seek relative to current read point - - `RW_SEEK_END`: seek relative to the end of data - - If this stream can not seek, it will return -1. - - SDL_RWseek() is actually a wrapper function that calls the SDL_RWops's - `seek` method appropriately, to simplify application development. - - Prior to SDL 2.0.10, this function was a macro. - - \param context a pointer to an SDL_RWops structure - \param offset an offset in bytes, relative to **whence** location; can be - negative - \param whence any of `RW_SEEK_SET`, `RW_SEEK_CUR`, `RW_SEEK_END` - \returns the final offset in the data stream after the seek or -1 on error. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWtell - \sa SDL_RWwrite - - - - Use this function to get the size of the data stream in an SDL_RWops. - - Prior to SDL 2.0.10, this function was a macro. - - \param context the SDL_RWops to get the size of the data stream from - \returns the size of the data stream in the SDL_RWops on success, -1 if - unknown or a negative error code on failure; call SDL_GetError() - for more information. - - \since This function is available since SDL 2.0.10. - - - - Use this function to free an SDL_RWops structure allocated by - SDL_AllocRW(). - - Applications do not need to use this function unless they are providing - their own SDL_RWops implementation. If you just need a SDL_RWops to - read/write a common data source, you should use the built-in - implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc, and - call the **close** method on those SDL_RWops pointers when you are done - with them. - - Only use SDL_FreeRW() on pointers returned by SDL_AllocRW(). The pointer is - invalid as soon as this function returns. Any extra memory allocated during - creation of the SDL_RWops is not freed by SDL_FreeRW(); the programmer must - be responsible for managing that memory in their **close** method. - - \param area the SDL_RWops structure to be freed - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocRW - - - - Use this function to allocate an empty, unpopulated SDL_RWops structure. - - Applications do not need to use this function unless they are providing - their own SDL_RWops implementation. If you just need a SDL_RWops to - read/write a common data source, you should use the built-in - implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc. - - You must free the returned pointer with SDL_FreeRW(). Depending on your - operating system and compiler, there may be a difference between the - malloc() and free() your program uses and the versions SDL calls - internally. Trying to mix the two can cause crashing such as segmentation - faults. Since all SDL_RWops must free themselves when their **close** - method is called, all SDL_RWops must be allocated through this function, so - they can all be freed correctly with SDL_FreeRW(). - - \returns a pointer to the allocated memory on success, or NULL on failure; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FreeRW - - - - Use this function to prepare a read-only memory buffer for use with RWops. - - This function sets up an SDL_RWops struct based on a memory area of a - certain size. It assumes the memory area is not writable. - - Attempting to write to this RWops stream will report an error without - writing to the memory buffer. - - This memory buffer is not copied by the RWops; the pointer you provide must - remain valid until you close the stream. Closing the stream will not free - the original buffer. - - If you need to write to a memory buffer, you should use SDL_RWFromMem() - with a writable buffer of memory instead. - - \param mem a pointer to a read-only buffer to feed an SDL_RWops stream - \param size the buffer size, in bytes - \returns a pointer to a new SDL_RWops structure, or NULL if it fails; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWtell - - - - Use this function to prepare a read-write memory buffer for use with - SDL_RWops. - - This function sets up an SDL_RWops struct based on a memory area of a - certain size, for both read and write access. - - This memory buffer is not copied by the RWops; the pointer you provide must - remain valid until you close the stream. Closing the stream will not free - the original buffer. - - If you need to make sure the RWops never writes to the memory buffer, you - should use SDL_RWFromConstMem() with a read-only buffer of memory instead. - - \param mem a pointer to a buffer to feed an SDL_RWops stream - \param size the buffer size, in bytes - \returns a pointer to a new SDL_RWops structure, or NULL if it fails; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWtell - \sa SDL_RWwrite - - - - Use this function to create an SDL_RWops structure from a standard I/O file - pointer (stdio.h's `FILE*`). - - This function is not available on Windows, since files opened in an - application on that platform cannot be used by a dynamically linked - library. - - On some platforms, the first parameter is a `void*`, on others, it's a - `FILE*`, depending on what system headers are available to SDL. It is - always intended to be the `FILE*` type from the C runtime's stdio.h. - - \param fp the `FILE*` that feeds the SDL_RWops stream - \param autoclose SDL_TRUE to close the `FILE*` when closing the SDL_RWops, - SDL_FALSE to leave the `FILE*` open when the RWops is - closed - \returns a pointer to the SDL_RWops structure that is created, or NULL on - failure; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWtell - \sa SDL_RWwrite - - - - \name RWFrom functions - - Functions to create SDL_RWops structures from various data streams. - - Use this function to create a new SDL_RWops structure for reading from - and/or writing to a named file. - - The `mode` string is treated roughly the same as in a call to the C - library's fopen(), even if SDL doesn't happen to use fopen() behind the - scenes. - - Available `mode` strings: - - - "r": Open a file for reading. The file must exist. - - "w": Create an empty file for writing. If a file with the same name - already exists its content is erased and the file is treated as a new - empty file. - - "a": Append to a file. Writing operations append data at the end of the - file. The file is created if it does not exist. - - "r+": Open a file for update both reading and writing. The file must - exist. - - "w+": Create an empty file for both reading and writing. If a file with - the same name already exists its content is erased and the file is - treated as a new empty file. - - "a+": Open a file for reading and appending. All writing operations are - performed at the end of the file, protecting the previous content to be - overwritten. You can reposition (fseek, rewind) the internal pointer to - anywhere in the file for reading, but writing operations will move it - back to the end of file. The file is created if it does not exist. - - **NOTE**: In order to open a file as a binary file, a "b" character has to - be included in the `mode` string. This additional "b" character can either - be appended at the end of the string (thus making the following compound - modes: "rb", "wb", "ab", "r+b", "w+b", "a+b") or be inserted between the - letter and the "+" sign for the mixed modes ("rb+", "wb+", "ab+"). - Additional characters may follow the sequence, although they should have no - effect. For example, "t" is sometimes appended to make explicit the file is - a text file. - - This function supports Unicode filenames, but they must be encoded in UTF-8 - format, regardless of the underlying operating system. - - As a fallback, SDL_RWFromFile() will transparently open a matching filename - in an Android app's `assets`. - - Closing the SDL_RWops will close the file handle SDL is holding internally. - - \param file a UTF-8 string representing the filename to open - \param mode an ASCII string representing the mode to be used for opening - the file. - \returns a pointer to the SDL_RWops structure that is created, or NULL on - failure; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWtell - \sa SDL_RWwrite - - - -Return the size of the file in this rwops, or -1 if unknown - - - Seek to \c offset relative to \c whence, one of stdio's whence values: - RW_SEEK_SET, RW_SEEK_CUR, RW_SEEK_END - - \return the final offset in the data stream, or -1 on error. - - - Read up to \c maxnum objects each of size \c size from the data - stream to the area pointed at by \c ptr. - - \return the number of objects read, or 0 at error or end of file. - - - Write exactly \c num objects each of size \c size from the area - pointed at by \c ptr to data stream. - - \return the number of objects written, or 0 at error or end of file. - - - Close and free an allocated SDL_RWops structure. - - \return 0 if successful or -1 on write error when flushing data. - - - - Clear any previous error message for this thread. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetError - \sa SDL_SetError - - - - Get the last error message that was set for the current thread. - - This allows the caller to copy the error string into a provided buffer, but - otherwise operates exactly the same as SDL_GetError(). - - \param errstr A buffer to fill with the last error message that was set for - the current thread - \param maxlen The size of the buffer pointed to by the errstr parameter - \returns the pointer passed in as the `errstr` parameter. - - \since This function is available since SDL 2.0.14. - - \sa SDL_GetError - - - - Retrieve a message about the last error that occurred on the current - thread. - - It is possible for multiple errors to occur before calling SDL_GetError(). - Only the last error is returned. - - The message is only applicable when an SDL function has signaled an error. - You must check the return values of SDL function calls to determine when to - appropriately call SDL_GetError(). You should *not* use the results of - SDL_GetError() to decide if an error has occurred! Sometimes SDL will set - an error string even when reporting success. - - SDL will *not* clear the error string for successful API calls. You *must* - check return values for failure cases before you can assume the error - string applies. - - Error strings are set per-thread, so an error set in a different thread - will not interfere with the current thread's operation. - - The returned string is internally allocated and must not be freed by the - application. - - \returns a message with information about the specific error that occurred, - or an empty string if there hasn't been an error message set since - the last call to SDL_ClearError(). The message is only applicable - when an SDL function has signaled an error. You must check the - return values of SDL function calls to determine when to - appropriately call SDL_GetError(). - - \since This function is available since SDL 2.0.0. - - \sa SDL_ClearError - \sa SDL_SetError - - - - Calculate a 256 entry gamma ramp for a gamma value. - - \param gamma a gamma value where 0.0 is black and 1.0 is identity - \param ramp an array of 256 values filled in with the gamma ramp - - \since This function is available since SDL 2.0.0. - - \sa SDL_SetWindowGammaRamp - - - - Get RGBA values from a pixel in the specified format. - - This function uses the entire 8-bit [0..255] range when converting color - components from pixel formats with less than 8-bits per RGB component - (e.g., a completely white pixel in 16-bit RGB565 format would return [0xff, - 0xff, 0xff] not [0xf8, 0xfc, 0xf8]). - - If the surface has no alpha component, the alpha will be returned as 0xff - (100% opaque). - - \param pixel a pixel value - \param format an SDL_PixelFormat structure describing the format of the - pixel - \param r a pointer filled in with the red component - \param g a pointer filled in with the green component - \param b a pointer filled in with the blue component - \param a a pointer filled in with the alpha component - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetRGB - \sa SDL_MapRGB - \sa SDL_MapRGBA - - - - Get RGB values from a pixel in the specified format. - - This function uses the entire 8-bit [0..255] range when converting color - components from pixel formats with less than 8-bits per RGB component - (e.g., a completely white pixel in 16-bit RGB565 format would return [0xff, - 0xff, 0xff] not [0xf8, 0xfc, 0xf8]). - - \param pixel a pixel value - \param format an SDL_PixelFormat structure describing the format of the - pixel - \param r a pointer filled in with the red component - \param g a pointer filled in with the green component - \param b a pointer filled in with the blue component - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetRGBA - \sa SDL_MapRGB - \sa SDL_MapRGBA - - - - Map an RGBA quadruple to a pixel value for a given pixel format. - - This function maps the RGBA color value to the specified pixel format and - returns the pixel value best approximating the given RGBA color value for - the given pixel format. - - If the specified pixel format has no alpha component the alpha value will - be ignored (as it will be in formats with a palette). - - If the format has a palette (8-bit) the index of the closest matching color - in the palette will be returned. - - If the pixel format bpp (color depth) is less than 32-bpp then the unused - upper bits of the return value can safely be ignored (e.g., with a 16-bpp - format the return value can be assigned to a Uint16, and similarly a Uint8 - for an 8-bpp format). - - \param format an SDL_PixelFormat structure describing the format of the - pixel - \param r the red component of the pixel in the range 0-255 - \param g the green component of the pixel in the range 0-255 - \param b the blue component of the pixel in the range 0-255 - \param a the alpha component of the pixel in the range 0-255 - \returns a pixel value - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetRGB - \sa SDL_GetRGBA - \sa SDL_MapRGB - - - - Map an RGB triple to an opaque pixel value for a given pixel format. - - This function maps the RGB color value to the specified pixel format and - returns the pixel value best approximating the given RGB color value for - the given pixel format. - - If the format has a palette (8-bit) the index of the closest matching color - in the palette will be returned. - - If the specified pixel format has an alpha component it will be returned as - all 1 bits (fully opaque). - - If the pixel format bpp (color depth) is less than 32-bpp then the unused - upper bits of the return value can safely be ignored (e.g., with a 16-bpp - format the return value can be assigned to a Uint16, and similarly a Uint8 - for an 8-bpp format). - - \param format an SDL_PixelFormat structure describing the pixel format - \param r the red component of the pixel in the range 0-255 - \param g the green component of the pixel in the range 0-255 - \param b the blue component of the pixel in the range 0-255 - \returns a pixel value - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetRGB - \sa SDL_GetRGBA - \sa SDL_MapRGBA - - - - Free a palette created with SDL_AllocPalette(). - - \param palette the SDL_Palette structure to be freed - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocPalette - - - - Set a range of colors in a palette. - - \param palette the SDL_Palette structure to modify - \param colors an array of SDL_Color structures to copy into the palette - \param firstcolor the index of the first palette entry to modify - \param ncolors the number of entries to modify - \returns 0 on success or a negative error code if not all of the colors - could be set; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocPalette - \sa SDL_CreateRGBSurface - - - - Set the palette for a pixel format structure. - - \param format the SDL_PixelFormat structure that will use the palette - \param palette the SDL_Palette structure that will be used - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocPalette - \sa SDL_FreePalette - - - - Create a palette structure with the specified number of color entries. - - The palette entries are initialized to white. - - \param ncolors represents the number of color entries in the color palette - \returns a new SDL_Palette structure on success or NULL on failure (e.g. if - there wasn't enough memory); call SDL_GetError() for more - information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FreePalette - - - - Free an SDL_PixelFormat structure allocated by SDL_AllocFormat(). - - \param format the SDL_PixelFormat structure to free - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocFormat - - - - Create an SDL_PixelFormat structure corresponding to a pixel format. - - Returned structure may come from a shared global cache (i.e. not newly - allocated), and hence should not be modified, especially the palette. Weird - errors such as `Blit combination not supported` may occur. - - \param pixel_format one of the SDL_PixelFormatEnum values - \returns the new SDL_PixelFormat structure or NULL on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FreeFormat - - - - Convert a bpp value and RGBA masks to an enumerated pixel format. - - This will return `SDL_PIXELFORMAT_UNKNOWN` if the conversion wasn't - possible. - - \param bpp a bits per pixel value; usually 15, 16, or 32 - \param Rmask the red mask for the format - \param Gmask the green mask for the format - \param Bmask the blue mask for the format - \param Amask the alpha mask for the format - \returns one of the SDL_PixelFormatEnum values - - \since This function is available since SDL 2.0.0. - - \sa SDL_PixelFormatEnumToMasks - - - - Convert one of the enumerated pixel formats to a bpp value and RGBA masks. - - \param format one of the SDL_PixelFormatEnum values - \param bpp a bits per pixel value; usually 15, 16, or 32 - \param Rmask a pointer filled in with the red mask for the format - \param Gmask a pointer filled in with the green mask for the format - \param Bmask a pointer filled in with the blue mask for the format - \param Amask a pointer filled in with the alpha mask for the format - \returns SDL_TRUE on success or SDL_FALSE if the conversion wasn't - possible; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_MasksToPixelFormatEnum - - - - Get the human readable name of a pixel format. - - \param format the pixel format to query - \returns the human readable name of the specified pixel format or - `SDL_PIXELFORMAT_UNKNOWN` if the format isn't recognized. - - \since This function is available since SDL 2.0.0. - - - -\note Everything in the pixel format structure is read-only. - - - -The bits of this structure can be directly reinterpreted as an integer-packed -color which uses the SDL_PIXELFORMAT_RGBA32 format (SDL_PIXELFORMAT_ABGR8888 -on little-endian systems and SDL_PIXELFORMAT_RGBA8888 on big-endian systems). - - - - If a + b would overflow, return -1. Otherwise store a + b via ret - and return 0. - - \since This function is available since SDL 2.24.0. - - - - If a * b would overflow, return -1. Otherwise store a * b via ret - and return 0. - - \since This function is available since SDL 2.24.0. - - - - This function converts a string between encodings in one pass, returning a - string that must be freed with SDL_free() or NULL on error. - - \since This function is available since SDL 2.0.0. - - - - Get the number of outstanding (unfreed) allocations - - \since This function is available since SDL 2.0.7. - - - - Replace SDL's memory allocation functions with a custom set - - \since This function is available since SDL 2.0.7. - - - - Get the current set of SDL memory functions - - \since This function is available since SDL 2.0.7. - - - - Get the original set of SDL memory functions - - \since This function is available since SDL 2.24.0. - - - -\endcond - \file begin_code.h - - This file sets things up for C dynamic library function definitions, - static inlined functions, and structures aligned at 4-byte alignment. - If you don't like ugly C preprocessor code, don't look at this file. :) - - - -\name Floating-point constants - -\cond - - -\brief An unsigned 64-bit integer type. - - - -\brief A signed 64-bit integer type. - - - -\brief An unsigned 32-bit integer type. - - - -\brief A signed 32-bit integer type. - - - -\brief An unsigned 16-bit integer type. - - - -\brief A signed 16-bit integer type. - - - -\brief An unsigned 8-bit integer type. - - - -\brief A signed 8-bit integer type. - - - - \file close_code.h - - This file reverses the effects of begin_code.h and should be included - after you finish any function and structure declarations in your headers - - - - \file SDL_stdinc.h - - This is a general header that includes C language support. - - \file SDL_platform.h - - Try to get a standard set of platform defines. - - \file begin_code.h - - This file sets things up for C dynamic library function definitions, - static inlined functions, and structures aligned at 4-byte alignment. - If you don't like ugly C preprocessor code, don't look at this file. :) - - Get the name of the platform. - - Here are the names returned for some (but not all) supported platforms: - - - "Windows" - - "Mac OS X" - - "Linux" - - "iOS" - - "Android" - - \returns the name of the platform. If the correct platform name is not - available, returns a string beginning with the text "Unknown". - - \since This function is available since SDL 2.0.0. - - - -*!************************************************************************* - - - -Marks the application to stop at the end of the current frame. - - - - -Whether or not the application is currently in fullscreen mode or not. - - - - -Retrieves the designated height of the current window. - - - - -Retrieves the designated width of the current window. - - - - -Whether or not the engine is in a paused state where script updates and -physics are not in play. - - - - -Whether or not the engine is playing. This will always be true on Publish. -On Debug/Release builds, this is true when the editor is in Play Mode. It -will also be true even if the editor is in Play Mode but is paused. - - - - -Static class that contains useful properties for querying the state of the -engine. - - - - -Sets the parent of this Transform component. - - -Entity that contains the Transform component that this Transform will be -parented to. If null, unparenting will occur. - - -If true, the transform values of this Transform component will retain their -pre-parent-change global transforms. The local transform values will be -modified to ensure that the global transforms do not change. - - - - -Parent Transform that affects this Transform. - - - - -Global scale stored by this Transform. - - - - -Global euler angle rotations stored by this Transform. - - - - -Global rotation quaternion stored by this Transform. - - - - -Global position stored by this Transform. - - - - -Local scale stored by this Transform. - - - - -Local euler angle rotations stored by this Transform. - - - - -Local rotation quaternion stored by this Transform. - - - - -Local position stored by this Transform. - - - - -Constructs a Transform Component that represents a native Transform component -tied to the specified Entity. - - Entity that this Component will be tied to. - - - -CLR version of the SHADE Engine's TransformComponent. - - - - -Compares if two float values are close enough to be the same with the -specified tolerance value. - - One of the values to compare. - The other value to compare. - Tolerance for floating point comparison. - True if a and b are practically the same. - - - -Compares if two float values are close enough to be the same with a tolerance -of Epsilon. - - One of the values to compare. - The other value to compare. - True if a and b are practically the same. - - - -Calculates the linear parameter t that produces the interpolant value within -the range [a, b]. - - Start value. - End value. - Value between start and end. - Percentage of value between start and end. - - - -Linearly interpolates between a and b by t. -The parameter t is not clamped and a value based on a and b is supported. -If t is less than zero, or greater than one, then LerpUnclamped will result -in a return value outside the range a to b. - - The start value. - The end value. - The interpolation value between the two float. - The interpolated float result between the two float values. - - - -Linearly interpolates between a and b by t. -The parameter t is clamped to the range [0, 1]. - - The start value. - The end value. - The interpolation value between the two float. - The interpolated float result between the two float values. - - - -Converts an angle from radian representation to degree representation. - - Radian-based angle to convert. - The specified angle in degrees. - - - -Converts an angle from degree representation to radian representation. - - Degree-based angle to convert. - The specified angle in radians. - - - -Wraps a value if they get to low or too high. - - Value to wrap. - Minimum value to wrap at. - Maximum value to wrap at. - Wrapped value. - - - -Small value used for single precision floating point comparisons. - - - - -Radians-to-degrees conversion constant - - - - -Degrees-to-radians conversion constant - - - - -Contains utility Math functions. - - - - -Logs a native exception that is formatted nicely to the output. - - Native exception to log. - Name of the one responsible for the exception. - - - -Logs an exception that is formatted nicely to the output. - - Name of the one responsible for the exception. - Exception to log. - - - -Logs a native exception that is formatted nicely to the output. -Equivalent to calling -LogException(exception, Convert::ToNative(thrower->GetType()->Name)); - - Native exception to log. - -Object that threw the exception to label the exception message. -The name of the object will be used. - - - - -Logs an exception that is formatted nicely to the output. - - Exception to log. - -Object that threw the exception to label the exception message. -The name of the object will be used. - - - - -Logs an exception that is formatted nicely to the output. - - Exception to log. - - - -Logs a error message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that threw the error to label the error message. -The name of the object will be used. - - - - -Logs a error message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that threw the error to label the error message. -The name of the object will be used. - - - - -Logs a error message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Object that threw the error to label the error message. -The name of the object will be used. - - - - -Logs a error message to the output. - - The string to output. - - - -Logs a error message to the output. - - The string to output. - - - -Logs a warning message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that threw the warning to label the warning message. -The name of the object will be used. - - - - -Logs a warning message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that threw the warning to label the warning message. -The name of the object will be used. - - - - -Logs a warning message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Object that threw the warning to label the warning message. -The name of the object will be used. - - - - -Logs a warning message to the output. - - The string to output. - - - -Logs a warning message to the output. - - The string to output. - - - -Logs a message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that sent the message to label the message. -The name of the object will be used. - - - - -Logs a message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that sent the message to label the message. -The name of the object will be used. - - - - -Logs a message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Object that sent the message to label the message. -The name of the object will be used. - - - - -Logs a message to the output. - - The string to output. - - - -Logs a message to the output. - - The string to output. - - - -Static class that contains the functions for working with time. - - - - -Material used to render this Renderable. - - - - -Material used to render this Renderable. - - - - -Mesh used to render this Renderable. - - - - -Constructs a Renderable Component that represents a native Renderable -component tied to the specified Entity. - - Entity that this Component will be tied to. - - - -CLR version of the SHADE Engine's SHRenderableComponent. - - - - -Retrieves the value of a specified property on the material. - - Type of property to get. - Name of the property to get. - Value of that property on the material. - -If this Material object is invalid. - - -If the name or type was specified that does not match the material's shader's -defined properties. - - - - -Set the value of a specific property. - - Type of property to set. - Name of the property to set. - Value to set te property to. - -If this Material object is invalid. - - -If the name or type was specified that does not match the material's shader's -defined properties. - - - - -Constructor for the Material - - Handle to the native material object. - - - -Managed counterpart of the native MaterialInstance object containing material -data that can be fed to Renderables for rendering. - - - - -Constructor for the Mesh - - Handle to the mesh object. - - - -Managed counterpart of the native Mesh object containing vertex data that can -be fed to Renderables for rendering. - - - -@brief Decomposes a transformation matrix into translation, orientation and scale. -@param[out] scale The scaling factor of the matrix. -@param[out] orientation The orientation of the matrix. -@param[out] translation The translation of the matrix. -@return True if decomposition was successful. - - - -@brief Decomposes a transformation matrix into translation, euler angles and scale. -@param[out] scale The scaling factor of the matrix. -@param[out] rotation The euler angles of the matrix. -@param[out] translation The translation of the matrix. -@return True if decomposition was successful. - - - -@brief Interface for a Column-Major Row Vector 4x4 Matrix. - - - - -Constructs a RigidBody Component that represents a native -SHRigidBodyComponent component tied to the specified Entity. - - Entity that this Component will be tied to. - - - -CLR version of the the SHADE Engine's SHRigidBodyComponent. - - - - -Creates an instance of the Managed representation of a Component with a -native Entity. - - Type of Component to create. - Native Entity that this Component is tied to. - The created Managed representation of the Component. - - - -Static constructor to initialize static data - - - - -Pointer to a function for Component manipulation operations. - - -Contains a set of Component related data used for resolving operations for -each Component. - - - - -Removes a Component from the specified Entity. - - Type of the Component to remove. - -Entity object that should have the specified Component removed from/ - - - - -Checks if the specified Entity has the specified Component. - - Type of the Component to check for. - Entity object to check for the Component. - -True if the specified Entity has the specified Component. False otherwise. - - - - -Ensures a Component on the specified Entity. - - Type of the Component to ensure. - Entity object to ensure the Component on. - Reference to the Component. - - - -Retrieves the first Component from the specified GameObjectt's children that -matches the specified type. - - Type of the Component to get. - Entity object to get the Component from. - -Reference to the Component or null if the Entity does not have the -specified Component. - - - - -Gets a Component from the specified Entity. - - Type of the Component to get. - Entity object to get the Component from. - -Reference to the Component or null if the Entity does not have the -specified Component. - - - - -Adds a Component to the specified Entity. - - Type of the Component to add. - -Entity object that should have the specified Component added to. - - Reference to the Component that was added. - - - -Static class which contains functions that map Pls::ECS's Component manipulation -functions to managed generic functions. - - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Entity that this Component belongs to. - - - - -Constructor for BaseComponent to tie it to a specific Entity. -Constructors of derived Components should call this Constructor. - - Entity that this Component will be tied to. - - - -Implicit conversion operator to enable checking if a component is null. - - Component to check. - - - -Removes all Scripts of the specified type from this GameObject. - - Type of PLushieScripts to remove. - - - -Retrieves a immutable list of Scripts of the specified type from this -GameObject. - - Type of Scripts to Get. - Immutable list of Scripts of the specified type. - - - -Retrieves a Script of the specified type from this GameObject. -If multiple Scripts of the same specified type are added on the same -GameObject, this will retrieve the first one added. - - Type of Script to add. - Reference to the Script to retrieve. - - - -Adds a Script of the specified type to this GameObject. - - Type of Script to add. - Reference to the created Script. - - - -Removes a Component from this GameObject. If no Component exists to begin -with, nothing happens. - - Type of the Component to get. - - - -Gets a Component from this GameObject. - - Type of the Component to get. - -Reference to the Component or null if this GameObject does not have the -specified Component. - - - - -Adds a Component to this GameObject. - - Type of the Component to add. - Reference to the Component that was added. - - - -Retrieves the GameObject that this Component belongs to. - - - - -Class that serves as the base for a wrapper class to Components in native code. - - - - -Called when the attached GameObject has a Collider and leaves a -collision with another GameObject with a Collider2D. - - Information on the collision event. - - - -Called when the attached GameObject has a Collider and collides with -another GameObject with a Collider in subsequent frames of collision. - - Information on the collision event. - - - -Called when the attached GameObject has a Collider and collides with -another GameObject with a Collider in the first frame of collision. - - Information on the collision event. - - - -Called when the attached GameObject has a trigger Collider and leaves a -collision with another GameObject with a Collider2D. - - Information on the collision event. - - - -Called when the attached GameObject has a trigger Collider and collides with -another GameObject with a Collider in subsequent frames of collision. - - Information on the collision event. - - - -Called when the attached GameObject has a trigger Collider and collides with -another GameObject with a Collider in the first frame of collision. - - Information on the collision event. - - - -Called just before the end of the frame where the attached GameObject or -this script is destroyed directly or indirectly due to destruction of the -owner. - - - - -Called every frame after physics and collision resolution but before -rendering. - - - - -Called every frame before physics and collision resolution. - - - - -Called every frame in sync with Physics update steps and thus in most cases -will execute more than update() will. This will be called immediately before -a Physics update step. - - - - -Called on the first frame that the attached GameObject is active but always -after Awake(). - - - - -Called on the first frame that the attached GameObject is active if they are -a part of the scene. - - - - -Called immediately once this script is detached from a GameObject. - - - - -Called immediately once this script is attached to a GameObject. - - - - -Constructor for Script to tie it to a specific GameObject. -Constructors of derived Scripts should call this Constructor. - - -GameObject that this Script will be tied to. - - - - -Used to call onTriggerExit(). This should be called when a trigger-type -collision is detected between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onTriggerStay(). This should be called when a trigger-type -collision is detected between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onTriggerEnter(). This should be called when a trigger-type -collision is detected between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onCollisionExit(). This should be called when a collision ends -between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onCollisionStay(). This should be called when a collision is -persistent between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onCollisionEnter(). This should be called when a collision is -detected between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onDestroy(). This should be called at the end of the frame -where the attached GameObject or this script is destroyed directly or -indirectly due to destruction of the owner. - - - - -Used to call lateUpdate(). This should be called every frame after physics -and collision resolution but before rendering. - - - - -Used to call update(). This should be called every frame before physics and -collision resolution. - - - - -Used to call fixedUpdate(). This should be called in sync with Physics -update steps and thus in most cases will execute more than Update() will. -This will be called immediately before a Physics update step. - - - - -Used to call start(). This should be called on the first frame that the -attached GameObject is active but always after Awake(). - - - - -Used to call awake(). This should be called on the first frame that the -attached GameObject is active if they are a part of the scene. - - - - -Used to call onDetached(). This is called immediately when this script is -detached from a GameObject. - - - - -Used to call onAttached(). This is called immediately when this script is -attached to a GameObject. - - - - -Implicit conversion operator to enable checking if a component is null. - - Component to check. - - - -Removes all Scripts of the specified type from this GameObject. - - -Type of script to remove. -This needs to be a default constructable Script. - - - - -Retrieves a immutable list of scripts from the specified Entity that -matches the specified type. -
-Note that this function allocates. It should be used sparingly. -
- -Type of scripts to get. -This needs to be a default constructable Script. - - -Immutable list of references to scripts of the specified type. - -
- - -Retrieves the first Script from this GameObject's children that matches the -specified type. - - -Type of script to get. -This needs to be a default constructable Script. - - Reference to the script added - - - -Retrieves the first Script from this GameObject that matches the specified -type. - - -Type of script to get. -This needs to be a default constructable Script. - - Reference to the script added - - - -Adds a Script to this GameObject. - - -Type of script to add. -This needs to be a default constructable Script. - - Reference to the script added - - - -Removes a Component from the GameObject that this Script belongs to. - - -Type of the Component to remove. Must be derived from BaseComponent. - - - - -Ensures a Component on the GameObject that this Script belongs to. - - -Type of the Component to ensure. Must be derived from BaseComponent. - - Reference to the Component. - - - -Retrieves the first Component from this GameObject's children that matches -the specified type. - - -Type of the Component to get. Must be derived from BaseComponent. - - Reference to the Component that was retrieved. - - - -Gets a Component from the GameObject that this Script belongs to. - - -Type of the Component to get. Must be derived from BaseComponent. - - Reference to the Component that was retrieved. - - - -Adds a Component to the GameObject that this Script belongs to. - - -Type of the Component to add. Must be derived from BaseComponent. - - Reference to the Component that was added. - - - -GameObject that this Script belongs to. - - - - -Class that forms the basis of all "script"-objects that can be attached to -Entities to update each Entity's Components via C# code. - - - - -The RigidBody that you are colliding with. - - - - -The CollisionShape of the Collider that you are colliding with. - - - - -The Collider that you are colliding with. - - - - -The GameObject whose collider you are colliding with. - - - - -Struct that describes a collision - - - - -Checks if two GameObject references are different. - - GameObject to check. - Another GameObject to check with. - True if both Components are different. - - - -Checks if two GameObject references are the same. - - GameObject to check. - Another GameObject to check with. - True if both Components are the same. - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Retrieves the native Entity object that this GameObject represents. - - Native Entity object that this GameObject represents. - - - -Retrieves the CLR Entity object that this GameObject represents. - - Entity object that this GameObject represents. - - - -Constructor for the GameObject. - - -Managed numerical representation of the ECS Entity that this GameObject -should represent. - - - - -Constructor for the GameObject. - - -The ECS Entity that this GameObject should represent. - - - - -Removes all Scripts of the specified type from this GameObject. - - Type of PLushieScripts to remove. - - - -Retrieves a immutable list of Scripts of the specified type from this -GameObject. - - Type of Scripts to retrieve. - Immutable list of Scripts of the specified type. - - - -Retrieves a Script of the specified type from child GameObjects. -If multiple Scripts of the same specified type are added on the same -child GameObject, this will retrieve the first one added. - - Type of Script to retrieve. - Reference to the Script to retrieve. - - - -Retrieves a Script of the specified type from this GameObject. -If multiple Scripts of the same specified type are added on the same -GameObject, this will retrieve the first one added. - - Type of Script to retrieve. - Reference to the Script to retrieve. - - - -Adds a Script of the specified type to this GameObject. - - Type of Script to add. - Reference to the created Script. - - - -Removes a Component from this GameObject. If no Component exists to begin -with, nothing happens. - - Type of the Component to get. - - - -Ensures a Component on this GameObject. - - Type of the Component to ensure. - -Reference to the Component. - - - - -Retrieves the first Component from this GameObject's children that matches -the specified type. - - Type of the Component to get. - -Reference to the Component or null if neither of this GameObject's children -does not have the specified Component. - - - - -Gets a Component from this GameObject. - - Type of the Component to get. - -Reference to the Component or null if this GameObject does not have the -specified Component. - - - - -Adds a Component to this GameObject. - - Type of the Component to add. - Reference to the Component that was added. - - - -Sets the active state of this GameObject. -
-The actual "activeness" of this GameObject is still dependent on the parents' -active states. -
- -Whether to activate or deactivate this GameObject. - -
- - -Sets the name of this GameObject. - - The name to set. - - - -Native Entity ID value for this GameObject. - - - - -Whether or not this Entity is active in the Scene hierarchy. - - - - -Whether or not this Entity alone, is active. This does not mean that this -object is active in the scene. For example, if this Entity's parent is not -active, then this Entity would also be not active. - - - - -Name of the object that this Entity represents. - - - - -Retrieves a GameObject with the specified name. If there are multiple -GameObjects with the same name, the first found GameObject will be retrieved. -There is no guaranteed order of which GameObject is considered "first". - - Name of the GameObject to find. - GameObject that has the specified name. Null if not found. - - - -Destroys the specified GameObject. Note that the specified GameObject will no -longer be a valid GameObject after this function is called. - - The GameObject to be destroyed. - - - -Creates a new GameObject in the current Scene. If multiple Scenes are loaded, -and you would like to create an object in a specific Scene, call the Scene's -CreateGameObject(). - - GameObject that represents the newly created GameObject. - - - -Lightweight object for an PlushieEngine Entity that allows for easy access -to Component and Script operations. - - - - -Constructor for a Tooltip attribute that fills in the description. - - Text to be shown when a field is hovered. - - - -Maximum value for the Ranged field. - - - - -Minimum value for the Ranged field. - - - - -Simple attribute to constrain the range of values for a field on the editor. - - - - -Converts from a native std::Stringto a managed String. - - The native std::string to convert from. - Managed copy of a native std::string. - - - -Converts from a managed String to a native std::string. - - The managed String to convert from. - Native copy of a managed String. - - - -Converts from a native Vector2 to a managed Vector2. - - The native Vector2 to convert from. - Managed copy of a native Vector2. - - - -Converts from a native Quaternion to a managed Quaternion. - - The native Quaternion to convert from. - Managed copy of a native Quaternion. - - - -Converts from a managed Quaternion to a native Quaternion. - - The managed Quaternion to convert from. - Native copy of a managed Quaternion. - - - -Converts from a native Vector2 to a managed Vector2. - - The native Vector2 to convert from. - Managed copy of a native Vector2. - - - -Converts from a managed Vector2 to a native Vector2. - - The managed Vector2 to convert from. - Native copy of a managed Vector2. - - - -Converts from a native Vector3 to a managed Vector3. - - The native Vector3 to convert from. - Managed copy of a native Vector3. - - - -Converts from a managed Vector3 to a native Vector3. - - The managed Vector3 to convert from. - Native copy of a managed Vector3. - - - -Converts from a native Entity to a managed Entity (UInt32). - - Native Entity to convert from. - Managed representation of the specified Entity. - - - -Provides functions easy and consistent syntax for converting between custom -managed and native types that are aligned. - - - - -Converts to true if this is a valid Handle. - - - - -The library that the handle was issued by. - - - - -The internal ID of the handle. - - - - -Creates a ray starting at origin along direction. - - Source of the ray. - Direction the ray travels in. - - - -The direction that a ray travels in. - - - - -The start point of the ray. - - - - -CLR version of the the SHADE Engine's Ray class that represents a ray in -3-Dimensional space. - - - - -Are two quaternions equal to each other? - - Left-hand side quaternion. - Right-hand side quaternion. - - - -Combines rotations lhs and rhs. - - Left-hand side quaternion. - Right-hand side quaternion. - - - -Spherically interpolates between a and b by t. The parameter t is not clamped. - - - - -Spherically interpolates between quaternions a and b by ratio t. The parameter t is clamped to the range [0, 1]. - - Start value, returned when t = 0. - End value, returned when t = 1. - Interpolation ratio. - A quaternion spherically interpolated between quaternions a and b. - - - -Rotates a rotation from towards to.
-The from quaternion is rotated towards to by an angular step of maxDegreesDelta (but note that the rotation will not overshoot). -Negative values of maxDegreesDelta will move away from to until the rotation is exactly the opposite direction. -
-
- - -Converts this quaternion to one with the same orientation but with a magnitude of 1. - - - - -Creates a rotation with the specified forward and upwards directions.
-Z axis will be aligned with forward, X axis aligned with cross product between forward and upwards, and Y axis aligned with cross product between Z and X. -
-
- - -Interpolates between a and b by t and normalizes the result afterwards. The parameter t is not clamped. - - - - -Interpolates between a and b by t and normalizes the result afterwards. The parameter t is clamped to the range [0, 1]. - - Start value, returned when t = 0. - End value, returned when t = 1. - Interpolation ratio. - A quaternion interpolated between quaternions a and b. - - - -Returns the Inverse of rotation. - - - - -Creates a rotation which rotates from fromDirection to toDirection. - - - - -Returns a rotation that rotates y degrees around the y axis, x degrees around the x axis, and z degrees around the z axis; applied in that order. - - - - -The dot product between two rotations. - - - - -Creates a rotation which rotates angle degrees around axis. - - - - -Returns the angle in degrees between two rotations a and b.
- The angle in degrees between the two vectors. -
- - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Converts a rotation to angle-axis representation (angles in degrees). - - - - -Creates a rotation with the specified forward and upwards directions.
-The result is applied to this quaternion. -If used to orient a Transform, the Z axis will be aligned with forward and the Y axis with upwards, assuming these vectors are orthogonal. -Logs an error if the forward direction is zero. -
- The direction to look in. - The vector that defines in which direction up is. -
- - -Creates a rotation which rotates from fromDirection to toDirection.
-Use this to create a rotation which starts at the first Vector (fromDirection) and rotates to the second Vector (toDirection). -These Vectors must be set up in a script. -
-
- - -Constructor to construct a Quaternion with the specified components. - - X-coordinate to set. - Y-coordinate to set. - Z-coordinate to set. - W-coordinate to set. - - - -W-component of the Quaternion. Do not directly modify quaternions. - - - - -Z-component of the Quaternion. -Don't modify this directly unless you know quaternions inside out. - - - - -Y-component of the Quaternion. -Don't modify this directly unless you know quaternions inside out. - - - - -X-component of the Quaternion. -Don't modify this directly unless you know quaternions inside out. - - - - -Shorthand for writing Quaternion(0, 0, 0, 1). - - - - -CLR version of SHADE's Quaternion class that represents an orientation. -Designed to closely match Unity's Quaternion struct. - - - - -Explicit conversion operator to enable explicit casting from a Vector2 to a -Vector3. - - Vector2 to convert from. - - - -Explicit conversion operator to enable explicit casting from a Vector3 to a -Vector2. - - Vector3 to convert from. - - - -Checks if two Vector3s are not approximately equal. This is equivalent to -calling !Vector3.IsNear() with default tolerance values. - - Vector3 to compare. - Another Vector3 to compare. - -True if all components are not approximately equal within the default -tolerance value. - - - - -Checks if two Vector3s are approximately equal. This is equivalent to -calling Vector3.IsNear() with default tolerance values. - - Vector3 to compare. - Another Vector3 to compare. - -True if all components are approximately equal within the default -tolerance value. - - - - -Calculates the division of a Vector3 with a scalar value and returns -the result. - - Scalar to divide with. - Vector3 to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Vector3 with a scalar value and returns -the result. - - Vector3 to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the division of a Vector3 with a scalar value and returns -the result. - - Scalar to divide with. - Vector3 to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Vector3 with a scalar value and returns -the result. - - Vector3 to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the component-wise multiplication of two Vector3s and returns the -result. - - Vector3 to multiply with. - Another Vector3 to multiply with. - The result of rhs subtracted from lhs. - - - -Subtracts a Vector3 from another Vector3 and returns the result. - - Vector3 to subtract from. - Another Vector3 to subtract. - The result of rhs subtracted from lhs. - - - -Adds two Vector3s together and returns the result. - - Vector3 to add. - Another Vector3 to add. - The result of lhs added to rhs - - - -Moves a point current towards target. -Similar to Lerp(), however, the function will ensure that the distance never -exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the -vector away from target - - The current position of the point. - The target position to move to. - Maximum distance moved per call. - Vector representing the moved point. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. -Unlike Lerp(), t is not clamped to a range at all. - - The start Vector3, returned when t = 0.0f. - The end Vector3, returned when t = 1.0f. - Value used to interpolate between a and b. - The interpolated Vector3. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. - - The start Vector3, returned when t = 0.0f. - The end Vector3, returned when t = 1.0f. - -Value used to interpolate between a and b which is clamped to -the range[0, 1]. - - The interpolated Vector3. - - - -Computes and returns a Vector3 that is made from the largest components of -the two specified Vector3s. - - Vector3 to calculate maximum Vector3 with. - Another Vector3 to calculate maximum Vector3 with. - -The Vector3 that contains the largest components of the two specified -Vector3s. - - - - -Computes and returns a Vector3 that is made from the smallest components of -the two specified Vector3s. - - Vector3 to calculate minimum Vector3 with. - Another Vector3 to calculate minimum Vector3 with. - -The Vector3 that contains the smallest components of the two specified -Vector3s. - - - - -Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise -direction. - - A Vector3 to rotate. - -Angle to rotate the vector by in an anti-clockwise direction in degrees. - - The Vector3 that represents the rotated vector. - - - -Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise -direction. - - A Vector3 to rotate. - -Angle to rotate the vector by in an anti-clockwise direction in radians. - - The Vector3 that represents the rotated vector. - - - -Reflects a Vector3 across another Vector3. - - A Vector3 to reflect. - A normal to reflect the Vector3 across. - The Vector3 that represents vec reflected across normal. - - - -Computes and returns a Vector3 projection. - - Vector3 to project. - Vector3 to project onto. - The Vector3 that represents the projected vec onto direction. - - - -Computes and returns the cross product of 2 specified Vector3s. - - Vector3 to calculate cross product with. - Another Vector3 to calculate cross product with. - The cross product of the two Vector3s. - - - -Computes and returns the dot product of 2 specified Vector3s. - - Vector3 to calculate dot product with. - Another Vector3 to calculate dot product with. - Scalar value representing the dot product of the two Vector3s. - - - -Checks if two specified Vector3s are near in value. - - Vector3 to check if is near in value. - Another Vector3 to check if is near in value. - Amount of tolerance to do the comparison with. - -True if the two Vector3s are within the tolerance value specified - - - - -Checks if two specified Vector3s are near in value. - - Vector3 to check if is near in value. - Another Vector3 to check if is near in value. - -True if the two Vector3s are within the tolerance value specified - - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Checks if a specified point is near this Vector3 that represents a point. - - The other point to check if we are near. - -The amount of tolerance before we consider these points as "near". - - -True if this Vector3 representing a point and the specified point are within -the range of the specified tolerance. False otherwise. - - - - -Checks if a specified point is near this Vector3 that represents a point with -a tolerance value of PLS_EPSILON. - - The other point to check if we are near. - -True if this Vector3 representing a point and the specified point are within -the range of the specified tolerance. False otherwise. - - - - -Calculates and returns the angle of this vector from the right vector. This -function returns values between -180.0f and 180.0f. - - Returns the angle of this vector from the right vector in degrees. - - - -Calculates and returns the angle of this vector from the right vector. This -function returns values between -Math.PI and Math.PI. - - Returns the angle of this vector from the right vector in radians. - - - -Calculates and returns the squared magnitude of this Vector3. - - Returns the squared length of this Vector3. - - - -Calculates and returns the magnitude of this Vector3. Note that this function -incurs a performance cost from the square root calculation. If you do not -need the precise magnitude, consider using GetSqrMagnitude() instead. - - Returns the length of this Vector3. - - - -Creates a copy of this Vector3 and returns a normalized version. - - -Returns a normalised copy of this Vector3. -If this Vector3 is a zero vector, a zero vector will be returned. - - - - -Normalises this current Vector3. This changes the data of this Vector3. -If you would like to get a copy, use GetNormalised() instead. -This function does nothing to a zero vector. - - - - -Conversion constructor to construct a Vector3 using a Vector2. - - - - - -Constructor to construct a Vector3 with the specified components. - - X-coordinate to set. - Y-coordinate to set. - Z-coordinate to set. - - - -Constructor to construct a Vector3 with the specified components with the -Z-component set to 0.0f. - - X-coordinate to set. - Y-coordinate to set. - - - -Constructor to construct a Vector3 with the specified components with the -Y and Z-component set to 0.0f. - - X-coordinate to set. - - - -Z-component of the Vector3. - - - - -Y-component of the Vector3. - - - - -X-component of the Vector3. - - - - -Shorthand for writing Vector3(0, 0, 0). - - - - -Shorthand for writing Vector3(0, 1, 0). - - - - -Shorthand for writing Vector3(1, 0, 0). - - - - -Shorthand for writing Vector3(float.PositiveInfinity, -float.PositiveInfinity, float.PositiveInfinity). - - - - -Shorthand for writing Vector3(1, 1, 1). - - - - -Shorthand for writing Vector3(float.NegativeInfinity, -float.NegativeInfinity, float.NegativeInfinity). - - - - -Shorthand for writing Vector3(-1, 0, 0). - - - - -Shorthand for writing Vector3(0, 0, 1). - - - - -Shorthand for writing Vector3(0, -1, 0). - - - - -Shorthand for writing Vector3(0, 0, -1). - - - - -CLR version of SHADE Engine's Vector3 class that represents a 3-Dimensional Vector. -Designed to closely match Unity's Vector3 struct. - - - - -Checks if two Vector2s are not approximately equal. This is equivalent to -calling !Vector2.IsNear() with default tolerance values. - - Vector2 to compare. - Another Vector2 to compare. - -True if all components are not approximately equal within the default -tolerance value. - - - - -Checks if two Vector2s are approximately equal. This is equivalent to -calling Vector2.IsNear() with default tolerance values. - - Vector2 to compare. - Another Vector2 to compare. - -True if all components are approximately equal within the default -tolerance value. - - - - -Calculates the division of a Vector2 with a scalar value and returns -the result. - - Scalar to divide with. - Vector2 to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Vector2 with a scalar value and returns -the result. - - Vector2 to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the division of a Vector2 with a scalar value and returns -the result. - - Scalar to divide with. - Vector2 to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Vector2 with a scalar value and returns -the result. - - Vector2 to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the component-wise multiplication of two Vector2s and returns the -result. - - Vector2 to multiply with. - Another Vector2 to multiply with. - The result of rhs subtracted from lhs. - - - -Subtracts a Vector2 from another Vector2 and returns the result. - - Vector2 to subtract from. - Another Vector2 to subtract. - The result of rhs subtracted from lhs. - - - -Adds two Vector2s together and returns the result. - - Vector2 to add. - Another Vector2 to add. - The result of lhs added to rhs - - - -Moves a point current towards target. -Similar to Lerp(), however, the function will ensure that the distance never -exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the -vector away from target - - The current position of the point. - The target position to move to. - Maximum distance moved per call. - Vector representing the moved point. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. -Unlike Lerp(), t is not clamped to a range at all. - - The start Vector2, returned when t = 0.0f. - The end Vector2, returned when t = 1.0f. - Value used to interpolate between a and b. - The interpolated Vector2. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. - - The start Vector2, returned when t = 0.0f. - The end Vector2, returned when t = 1.0f. - -Value used to interpolate between a and b which is clamped to -the range[0, 1]. - - The interpolated Vector2. - - - -Computes and returns a Vector2 that is made from the largest components of -the two specified Vector2s. - - Vector2 to calculate maximum Vector2 with. - Another Vector2 to calculate maximum Vector2 with. - -The Vector2 that contains the largest components of the two specified -Vector2s. - - - - -Computes and returns a Vector2 that is made from the smallest components of -the two specified Vector2s. - - Vector2 to calculate minimum Vector2 with. - Another Vector2 to calculate minimum Vector2 with. - -The Vector2 that contains the smallest components of the two specified -Vector2s. - - - - -Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise -direction. - - A Vector2 to rotate. - -Angle to rotate the vector by in an anti-clockwise direction in degrees. - - The Vector2 that represents the rotated vector. - - - -Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise -direction. - - A Vector2 to rotate. - -Angle to rotate the vector by in an anti-clockwise direction in radians. - - The Vector2 that represents the rotated vector. - - - -Reflects a Vector2 across another Vector2. - - A Vector2 to reflect. - A normal to reflect the Vector2 across. - The Vector2 that represents vec reflected across normal. - - - -Computes and returns a Vector2 projection. - - Vector2 to project. - Vector2 to project onto. - The Vector2 that represents the projected vec onto direction. - - - -Computes a perpendicular Vector2 to the specified Vector2. - - Vector2 to find a perpendicular of. - -Whether the inward perpendicular Vector is retrieved. If true, the -resultant vector is rotated 90-degrees in a counter-clockwise. - - The perpendicular Vector2 relative to the specified Vector2. - - - - -Computes the inward perpendicular Vector2 to the specified Vector2. -Equivalent to calling Perpendicular(lhs, true). This means, the -resultant Vector2 is rotated 90-degrees in a counter-clockwise. - - Vector2 to find a perpendicular of. - -The perpendicular Vector2 relative to the specified Vector2. - - - - -Computes and returns the dot product of 2 specified Vector2s. - - Vector2 to calculate dot product with. - Another Vector2 to calculate dot product with. - -Scalar value representing the dot product of the two Vector2s. - - - - -Checks if two specified Vector2s are near in value. - - Vector2 to check if is near in value. - Another Vector2 to check if is near in value. - -Amount of tolerance to do the comparison with. - - -True if the two Vector2s are within the tolerance value specified - - - - -Checks if two specified Vector2s are near in value. - - Vector2 to check if is near in value. - Another Vector2 to check if is near in value. - -True if the two Vector2s are within the tolerance value specified - - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Checks if a specified point is near this Vector2 that represents a point. - - The other point to check if we are near. - -The amount of tolerance before we consider these points as "near". - - -True if this Vector2 representing a point and the specified point are within -the range of the specified tolerance. False otherwise. - - - - -Checks if a specified point is near this Vector2 that represents a point with -a tolerance value of PLS_EPSILON. - - The other point to check if we are near. - -True if this Vector2 representing a point and the specified point are within -the range of the specified tolerance. False otherwise. - - - - -Calculates and returns the angle of this vector from the right vector. This -function returns values between -180.0f and 180.0f. - - Returns the angle of this vector from the right vector in degrees. - - - -Calculates and returns the angle of this vector from the right vector. This -function returns values between -Math.PI and Math.PI. - - Returns the angle of this vector from the right vector in radians. - - - -Calculates and returns the squared magnitude of this Vector2. - - Returns the squared length of this Vector2. - - - -Calculates and returns the magnitude of this Vector2. Note that this function -incurs a performance cost from the square root calculation. If you do not -need the precise magnitude, consider using GetSqrMagnitude() instead. - - Returns the length of this Vector2. - - - -Creates a copy of this Vector2 and returns a normalized version. - - -Returns a normalised copy of this Vector2. -If this Vector2 is a zero vector, a zero vector will be returned. - - - - -Normalises this current Vector2. This changes the data of this Vector2. -If you would like to get a copy, use GetNormalised() instead. -This function does nothing to a zero vector. - - - - -Constructor to construct a Vector2 with the specified components.. - - X-coordinate to set. - Y-coordinate to set. - - - -Constructor to construct a Vector2 with the specified components with the -Y-component set to 0.0f. - - X-coordinate to set. - - - -Y-component of the Vector2. - - - - -X-component of the Vector2. - - - - -Shorthand for writing Vector2(0, 0). - - - - -Shorthand for writing Vector2(0, 1). - - - - -Shorthand for writing Vector2(1, 0). - - - - -Shorthand for writing Vector2(float.PositiveInfinity, -float.PositiveInfinity). - - - - -Shorthand for writing Vector2(1, 1). - - - - -Shorthand for writing Vector2(float.NegativeInfinity, -float.NegativeInfinity). - - - - -Shorthand for writing Vector2(-1, 0). - - - - -Shorthand for writing Vector2(0, -1). - - - - -CLR version of SHADE Engine's Vector2 class that represents a 2-Dimensional Vector. -Designed to closely match Unity's Vector2 struct. - - - - -Checks if the specified entity is valid. This is done by checking if it -matches Pls::Entity::INVALID. - - The Entity to check. - True if the specified Entity is valid. - - - -Static class that contains useful utility functions for working with Entity. - - - - -Manages all resources in multiple ResourceLibraries. - - - - -Base class for SHResourceLibrary that holds information about the library type. - - - - -Template Specialization for Handle that represents a type-less Handle. - - - - -Converts to true if this is a valid Handle. - - - - -Native ID type of a handle - - - - -Base implementation of the Handle that is not templated to allow for holding -generic non-type-specific Handles. - - - - -Exception thrown when a generic Handle is being casted to the wrong type. - - - - -Exception thrown when an invalid Handle was dereferenced. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
\ No newline at end of file diff --git a/bin/Release/SHADE_CSharp.xml b/bin/Release/SHADE_CSharp.xml deleted file mode 100644 index daeaa3c5..00000000 --- a/bin/Release/SHADE_CSharp.xml +++ /dev/null @@ -1,1029 +0,0 @@ - - - - SHADE_CSharp - - - - - Interface for a CallbackAction that all variants inherit from. - - - - - Whether or not this CallbackAction is runtime assigned. If it is, then the - TargetMethodName and TargetObject properties are invalid. - - - - - Name of the method that this CallbackAction is using. - - - - - Object which the specified target method is called on. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 1 parameter. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 2 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 3 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 4 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 5 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 6 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 7 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 8 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 9 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Represents a function call that can be serialised and put togetheer with scripts. - This variant accepts functions with 10 parameters. - - - - - - - - - - - - - - Constructs an empty Callback action. - - - - - Constructs a CallbackAction that represents a call to the specified static - method. - - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a CallbackAction that represents a call to a specified member - method on the specified target. - - Object to call the method on. - Method to call. - - Thrown if a method that is not compatible with the target is specified. The method's - source type must match the target's type. - - - - - Constructs a Callback action based on an action. - - Action that wraps a function to be called. - - - - Invokes the CallbackAction's stored method/action with the specified parameters. - - - - - Interface for a CallbackEvent that all variants inherit from. - - - - - Registers an empty ICallbackAction. - - - - - Registers an ICallbackAction with the event such that it will be called in - future - - ICallbackAction to register with. - - - - Deregisters an ICallbackAction that was previously added. This should - only emit a warning if an action that was not previous added was - provided. - - ICallbackAction to remove. - - - - Iterable set of ICallbackActions that were registered to this event. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - - A container of CallbackActions that is correlated to a specific scenario as - specified by the user of this class. - This variant accepts CallbackEvents with 1 generic parameter. - - - - - - - - - - - - - - Adds a CallbackAction into the event. - - CallbackAction to add. - - - - Constructs and adds a CallbackACtion into the event. - - System.Action to add as a CallbackAction. - - - - Constructs and adds a CallbackACtion into the event. - - Object to call the method on. - Method to call. - - - - - - - Invokes all stored CallbackActions with the specified parameters. - - - - diff --git a/bin/Release/SHADE_Managed.xml b/bin/Release/SHADE_Managed.xml deleted file mode 100644 index 7b653116..00000000 --- a/bin/Release/SHADE_Managed.xml +++ /dev/null @@ -1,6250 +0,0 @@ - - - - "SHADE_Managed" - - - - -Retrieves the duration that the specified key has not been held or was last -not been held for. - - The key to check. - Time in seconds that the key was held. - - - -Retrieves the duration that the specified key has been held or was last held -for. - - The key to check. - Time in seconds that the key was held. - - - -Retrieves the duration that the specified key has not been held or was last -not been held for. - - The key to check. - Time in seconds that the key was held. - - - -Retrieves the duration that the specified key has been held or was last held -for. - - The key to check. - Time in seconds that the key was held. - - - -Sets the position of the mouse cursor relative to the top left corner of the -window. - - -Position of the mouse in window pixel coordinates to set. - - - - -Checks if a specified mouse button is no longer pressed and was pressed -before. - - MouseCode of the mouse button to check. - -True during the frame the user releases the given mouse button. - - - - -Checks if a specified mouse button is pressed and was not pressed before. - - MouseCode of the mouse button to check. - -True during the frame the user pressed the given mouse button. - - - - -Checks if a specified mouse button is being held down. -This will also be true if GetMouseButtonDown() is true. - - MouseCode of the mouse button to check. - True while the user holds down the mouse button specified. - - - -Checks if a specified key is no longer pressed pressed and was pressed -before. - - KeyCode of the key to check. - -True during the frame the user releases the key identified by name. - - - - -Checks if a specified key is pressed and was not pressed before. - - KeyCode of the key to check. - -True during the frame the user starts pressing down the key specified. - - - - -Checks if a specified key is being held down. -This will also be true if GetKeyDown() is true. - - KeyCode of the key to check. - True while the user holds down the key specified. - - - -Amnount of vertical mouse scroll in this frame. - - - - -Mouse position in screen coordinates relative to the top left of the window. -This value is a Vector3 for compatibility with functions that have Vector3 -arguments. The z component of the Vector3 is always 0 - - - - -Represents the available supported mouse keycodes that can be passed into the -mouse-button-based Input functions. - - - - -Represents the available supported keycodes that can be passed into the -key-based Input functions. - -Attempting to follow https://docs.unity3d.com/ScriptReference/KeyCode.html -Win32 keycodes are shift-insensitive, i.e. 'A' and 'a' are the same keycode and '1' and '!' are the same keycode - - - - -Static class responsible for providing access to Input-related functionality. - - - - -Simple attribute to mark that a field in a Script should be serialised. - - - - -Cleans up all required components for managed code. - - - - -Reloads the managed script assembly. -Take note that this will clear all existing scripts, ensure that the scene -is saved before doing so. -Equivalent to calling UnloadScriptAssembly() and then LoadScriptAssembly(). - - - - -Loads the managed script assembly. Ensure this is only called after -UnloadScriptAssembly() has been called. - - - - -Unloads the managed script assembly. -Take note that this will clear all existing scripts, ensure that the scene -is saved before doing so. - - - - -Initialises all required components for managed code. - - - - -Name of the Managed Library that contains the C# scripts written externally. - - - - -Static class that contains the functions for interfacing with the core -PlushieEngine written in C++ for managing the lifecycle of managed code. - - - - -Default Constructor - - - - -Custom AssemblyLoadContext marked as collectible so that it can be unloaded. - - - - -Time taken for Physics simulations. You should use this for operations -within Script.FixedUpdate() - - - - -Time taken to process the previous frame. - - - - -Static class that contains the functions for working with time. - - - - -Static class that wraps up certain functions in the SHPhysicsSystem so that -accessing it from SHADE_Managed would not cause issues due to C++20 features. - - - - -Constructor for a Tooltip attribute that fills in the description. - - Text to be shown when a field is hovered. - - - -Description that is to be shown in the Tooltip. - - - - -Simple attribute to provide a field in a script with a tooltip. - - - - -Checks if a specified file exists. - - File path to the file to check. - True if the file exists - - - -Deletes the folder and all files in it as specified by the file path. - - File path to the file to delete. - - - -Deletes the file as specified by the file path. - - File path to the file to delete. - - - -Reads the file via the specified path that represents a build log of error -and warning messages. - - -File path to the build log of script builds done by BuildScriptAssembly() to -dump and process. - - - - -Registers events for the scripting system - - - - -Loads all the function pointers to CLR code that we need to execute. - - - - -Generates a .csproj file for editing and compiling the C# scripts. - - File path to the generated file. - - - -Utilises execution of a external batch file for invoking the dotnet build -tool to compile C# scripts in the Assets folder into the SHADE_Scripting -C# assembly DLL. - - -Whether or not a debug build will be built. Only debug built C# assemblies -can be debugged. - - -Whether or not we are reloading the assembly, if so, unload and then reload it. - - Whether or not the build succeeded. - - - -Performs a redo for script inspector changes if it exists. - - - - -Performs an undo for script inspector changes if it exists. - - - - -Renders the set of attached Scripts for the specified Entity into the -inspector. -
-This function is meant for consumption from native code in the inspector -rendering code. -
- The Entity to render the Scripts of. -
- - -Creates scripts and sets fields for the specified Entity based on the specified -YAML node. - - The Entity to deserialise a Script on to. - -YAML Node that contains the serialised script data. - - True if successfully deserialised. - - - -Performs serialization of all scripts for the specified entity into the -YAML::Node specified. This node will contain all serialised scripts after -calling this function. - - The Entity to Serialise. - -YAML Node that will store the serialised scripts. - - True if successfully serialised. - - - -Removes all Scripts attached to the specified Entity. Unlike -RemoveAllScripts(), this removes all the scripts immediately. -Does not do anything if the specified Entity is invalid or does not have any -Scripts attached. - - The entity to remove the scripts from. - -Whether or not to call OnDestroy on the scripts. This is ignored if not in -play mode. - - - - -Removes all Scripts attached to the specified Entity. Does not do anything -if the specified Entity is invalid or does not have any Scripts -attached. - - The entity to remove the scripts from. - - - -Adds a Script to a specified Entity. Note that while you can call this -multiple times on a specified Entity, it will work for all intents and -purposes but GetScript<T>() (C# only) currently only -gives you the first Script added of the specified type. - - The entity to add a script to. - Type name of the script to add. - -True if successfully added. False otherwise with the error logged to the -console. - - - - -Shuts down the DotNetRuntime. - - - - -Executes the OnCollision*()s and OnTrigger*()s of the Scripts that are attached -to Entities. - - - - -Executes the FixedUpdate()s of the Scripts that are attached to -Entities. - - - - -Reloads the managed script assembly. -Take note that this will clear all existing scripts, ensure that the scene -is saved before doing so. - - - - -Unloads the managed script assembly. -Take note that this will clear all existing scripts, ensure that the scene -is saved before doing so. - - - - -Loads the managed script assembly. Ensure this is only called after -UnloadScriptAssembly() has been called. - - - - -Initialises the DotNetRuntime and retrieves function pointers to all -functions on the CLR used to interface with the engine. - - - - -Manages initialisation of the DotNetRuntime and interfacing with CLR code written -and executed on .NET. - - - - -Deserialises a YAML node that contains a map of Scripts and copies the -deserialised data into the specified object if there are matching fields. - - -The JSON string that contains the data to copy into this Script object. - - The object to copy deserialised data into. - - - -Creates a JSON node that represents the specified object and its associated -serialisable fields. Public fields and fields marked with the SerialiseField -attribute will be serialised. - - The object to serialise. - - - -Checks if a specified field is a candidate for serialisation. This means that -the field is public or private with the [SerialiseField] attribute. - - The field to check. - -True if the specified field is a candidate for serialisation. - - - - -Retrieves a set of all non-static (instance) fields from a specified object. - - The object to get non-static fields from. - Immutable list of non-static fields. - - - -Contains useful static functions for working with Reflection. - - - -Converts the node to a YAML string. - - -Emits the node to the given output stream. - - -Emits the node to the given {@link Emitter}. If there is an error in writing, -{@link Emitter#good} will return false. - - - - Loads the input file as a list of YAML documents. - - @throws {@link ParserException} if it is malformed. - @throws {@link BadFile} if the file cannot be loaded. - - - - Loads the input stream as a list of YAML documents. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input string as a list of YAML documents. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input string as a list of YAML documents. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input file as a single YAML document. - - @throws {@link ParserException} if it is malformed. - @throws {@link BadFile} if the file cannot be loaded. - - - - Loads the input stream as a single YAML document. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input string as a single YAML document. - - @throws {@link ParserException} if it is malformed. - - - - Loads the input string as a single YAML document. - - @throws {@link ParserException} if it is malformed. - - - -Handles a "TAG" directive, which should be of the form 'handle prefix', -where 'handle' is converted to 'prefix' in the file. - - - -Handles a "YAML" directive, which should be of the form 'major.minor' (like -a version number). - - - -Reads any directives that are next in the queue, setting the internal -{@code m_pDirectives} state. - - - - Handles the next document by calling events on the {@code eventHandler}. - - @throw a ParserException on error. - @return false if there are no more documents - - - -Resets the parser with the given input stream. Any existing state is -erased. - - - -Evaluates to true if the parser has some valid input to be read. - - -Constructs a parser from the given input stream. The input stream must -live as long as the parser. - - - -Constructs an empty parser (with no input. - - -A parser turns a stream of bytes into one stream of "events" per YAML -document in the input stream. - - - - -Renders a context menu when right clicked for the scripts - - The Entity to render the Scripts of. - The Script to render the inspector for. - - - -Renders a field specified into the inspector. - - The field to render. - -The object that contains the data of the field to render. - - - - -Renders a single specified Script's inspector. - - The Entity to render the Scripts of. - The Script to render the inspector for. - -Indices used internally to differentiate each rendered Script -inspector. This is required to open and close each Script's inspector -independently from each other. - - - - -Redoes the last script inspector change if there is any. - - - - -Undoes the last script inspector change if there is any. - - - - -Renders a dropdown button that allows for the addition of PlushieScripts -onto the specified Entity. - - The Entity to add PlushieScripts to. - - - -Renders the set of attached Scripts for the specified Entity into the -inspector. -
-This function is meant for consumption from native code in the inspector -rendering code. -
- The Entity to render the Scripts of. -
- - -Static class for Editor-related functions - - - - -Processes a YAML node that contains a list of multiple scripts to be loaded -into the specified Entity. -

-This function should only be called from native unmanaged code. -
- -The Entity to attach the deserialised Scripts to. - - -Pointer to the YAML::Node that contains serialized script data. - - -
- - -Populates a YAML node with the scripts for a specified Entity. -

-This function should only be called from native unmanaged code. -
- The Entity to Serialise. - -Pointer to a YAML::Node that will be populated with all of the serialised -scripts and their associated fields. - - -True if serialisation is successful. False if the buffer is too small for -the serialised output. - -
- - -Executes OnCollision*() and OnTrigger*() for all scripts. - - - - -Executes LateUpdate() for all scripts. - - - - -Executes Update() for all scripts. - - - - -Executes FixedUpdate() for all scripts. - - - - -Retrieves a immutable list of available scripts that can be added. - - Immutable list of available scripts that can be added. - - - -Cleans up data stored in the ScriptStore to free up memory for garbage -collection. - - - - -Cleans up scripts that were marked for deletion. This calls the OnDestroy() -for these Scripts. - - - - -Sets up scripts that were marked for initialization. This calls the Awake() -and Start() for Scripts that have yet to have done so. - - - - -Initializes the ScriptStore to allocate and pre-populate reflection data. - - - - -Removes all Scripts attached to the specified Entity. Unlike -RemoveAllScripts(), this removes all the scripts immediately. -Does not do anything if the specified Entity is invalid or does not have any -Scripts attached. - - The entity to remove the scripts from. - -Whether or not to call OnDestroy on the scripts.This is ignored if not in -play mode. - - - - -Removes all Scripts attached to the specified Entity. Does not do anything -if the specified Entity is invalid or does not have any Scripts -attached. - - The entity to remove the scripts from. - - - -Removes a specific script from the - - The entity to remove the script from. - The script to remove. - True if successfully removed. False otherwise. - - - -Removes all Scripts of the specified type from the specified Entity. - - -Type of script to remove. -This needs to be a default constructable Script. - - The entity to remove the script from. - -If the specified Entity is invalid. - - - - -Retrieves an immutable list of all scripts attached to a specified Entity. - - -The entity which the scripts to retrieve are attached. - - -Immutable list of references to scripts attached to the specified Entity. -This can also be null if there are no scripts at all or an invalid Entity -was specified. - - - - -Retrieves a immutable list of scripts from the specified Entity that -matches the specified type. -
-Note that this function allocates. It should be used sparingly. -
- -Type of scripts to get. -This needs to be a default constructable Script. - - -The entity which the scripts to retrieve are attached. - - -Immutable list of references to scripts of the specified type. - -
- - -Retrieves the first Script from the specified Entity's children that matches -the specified type. - - -Type of script to get. -This needs to be a default constructable Script. - - -The entity which the script to retrieve is attached. - - -Reference to the script. This can be null if no script of the specified -type is attached. - - -If the specified Entity is invalid. - - - - -Retrieves the first Script from the specified Entity that matches the -specified type. - - -Type of script to get. -This needs to be a default constructable Script. - - -The entity which the script to retrieve is attached. - - -Reference to the script. This can be null if no script of the specified -type is attached. - - -If the specified Entity is invalid. - - - - -Adds a Script to a specified Entity. -
-This function is meant for consumption from native code or for serialisation -purposes. If you are writing in C# or C++/CLI and not doing serialisation, -use AddScript<T>() instead as it is faster. -
- The entity to add a script to. - The entity to add a script to. - -Out parameter handle to the Script that was created. - - -True if successfully added. False otherwise with the error logged to the -console. - -
- - -Adds a Script to a specified Entity. -
-This function is meant for consumption from native code. If you are writing -in C# or C++/CLI, use AddScript<T>() instead as it is faster. -
- The entity to add a script to. - The entity to add a script to. - -True if successfully added. False otherwise with the error logged to the -console. - -
- - -Adds a Script to a specified Entity. - - -Type of script to add. -This needs to be a default constructable PlushieScript. - - The entity to add a script to. - Reference to the script added. - -If the specified Entity is invalid. - - - - -Responsible for managing all scripts attached to Entities as well as executing -all lifecycle functions of scripts. - - - - -Checks if two Colors are not approximately equal. - - Color to compare. - Another Color to compare. - -True if all components are not approximately equal within the default -tolerance value. - - - - -Checks if two Colors are approximately equal. - - Color to compare. - Another Color to compare. - -True if all components are approximately equal within the default -tolerance value. - - - - -Calculates the division of a Color with a scalar value and returns -the result. - - Scalar to divide with. - Color to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Color with a scalar value and returns -the result. - - Color to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the component-wise multiplication of two Colors and returns the -result. - - Color to multiply with. - Another Color to multiply with. - The result of rhs subtracted from lhs. - - - -Subtracts a Color from another Color and returns the result. - - Color to subtract from. - Another Color to subtract. - The result of rhs subtracted from lhs. - - - -Adds two Colors together and returns the result. - - Color to add. - Another Color to add. - The result of lhs added to rhs - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. -Unlike Lerp(), t is not clamped to a range at all. - - The start Color, returned when t = 0.0. - The end Color, returned when t = 1.0. - Value used to interpolate between a and b. - The interpolated Color. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. - - The start Color, returned when t = 0.0. - The end Color, returned when t = 1.0. - -Value used to interpolate between a and b which is clamped to -the range[0, 1]. - - The interpolated Vector3. - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Alpha component of the colour. Ranges from 0.0f to 1.0f. - - - - -Blue component of the colour. Ranges from 0.0f to 1.0f. - - - - -Green component of the colour. Ranges from 0.0f to 1.0f. - - - - -Red component of the colour. Ranges from 0.0f to 1.0f. - - - - -Constructor to construct a Color with the specified components. - - Red component to set. - Green component to set. - Blue component to set. - Alpha component to set. - - - -Constructor to construct a Color with the specified components with the -alpha component set to 1.0f. - - Red component to set. - Green component to set. - Blue component to set. - - - -Constructor to construct a Color with the specified components with the -blue and alpha component set to 1.0f. - - Red component to set. - Green component to set. - - - -Constructor to construct a Color with the specified components with the -green, blue and alpha component set to 1.0f. - - Red component to set. - - - -Pure yellow, mix of pure red and green. - - - - -Pure magenta, mix of pure red and blue. - - - - -Pure cyan, mix of pure green and blue. - - - - -Pure blue. - - - - -Pure green. - - - - -Pure red. - - - - -Pure white. - - - - -Dark Gray, darker than gray. - - - - -Gray, halfway between black and white. - - - - -Light Gray, lighter than gray. - - - - -Pure black. - - - - -A static class that contains a set of default Colors. - - - - -CLR version of the the SHADE Engine's Color struct which describes a Color -encoded using floating point numbers that range from 0.0f to 1.0f. - - - - -Creates a inline button widget. -
-Wraps up ImGui::Button(). -
- Text to display. - True if button was pressed. -
- - -Creates a small inline button widget. -
-Wraps up ImGui::SmallButton(). -
- Text to display. - True if button was pressed. -
- - -Creates a visual text widget. -
-Wraps up ImGui::Text(). -
- Text to display. -
- - -Creates a menu item in the list of items for a mini popup. -
-Wraps up ImGui::MenuItem(). -
- Label used to identify this widget. - Whether or not the menu item was selected. -
- - -Opens the popup that was defined with the specified label. -
-Wraps up ImGui::OpenPopup(). -
-
- - -Marks the end of a definition of a mini pop up that can show options. -
-Wraps up ImGui::EndPopup(). -
-
- - -Marks the start of a definition of a mini pop up that can show options. -
-Wraps up ImGui::BeginPopup(). -
- Label used to identify this widget. - Whether or not the pop up is open. -
- - -Creates a collapsing title header. -
-Wraps up ImGui::CollapsingHeader(). -
- Label for the header. - True if the header is open, false otherwise. -
- - -Unindents the widgets rendered after this call. -
-Wraps up ImGui::Unindent(). -
-
- - -Indents the widgets rendered after this call. -
-Wraps up ImGui::Indent(). -
-
- - -Marks the end of a stack of ImGui widgets from the last PushID() call. -
-Wraps up ImGui::PopID(). -
-
- - -Marks the start of a stack of ImGui widgets with the specified id. -
-Wraps up ImGui::PushID(). -
- Integer-based ID. -
- - -Marks the start of a stack of ImGui widgets with the specified id. -
-Wraps up ImGui::PushID(). -
- String-based ID. -
- - -Maximum length of a string supported by InputTextField() - - - - -Static class that contains useful functions for Editor UI using ImGui. - - - - -Redoes the last undo-ed command if it exists. - - - - -Undos the last added command if it exists. - - - - -Adds a command onto the stack. - - - - - -True if there is a redoable action in the stack. - - - - -True if there is an undoable action in the stack. - - - - -Command for the stack that represents a data modification. - - - - -Class that is able to store a stack of actions that can be done and redone. - - - - -To be called from native code when a Collision Shape has been changed. - - -The entity which has it's collision shape changed. - - - - -To be called from native code when a collision shape has been removed. - - The entity which has it's collision shape removed. - - - -Retrieves a ColliderBound at the specified index in the ColliderBound list -and casts it to the appropriate type. - - Type of the ColliderBound to cast to. - Index to retrieve a ColliderBound from. - ColliderBound for the specified index. - - - -Retrieves a ColliderBound at the specified index in the ColliderBound list. - - Index to retrieve a ColliderBound from. - ColliderBound for the specified index. - - - -Total number of ColliderShapes in the Collider component. - - - - -Constructs a Collider Component that represents a native SHColliderComponent -component tied to the specified Entity. - - Entity that this Component will be tied to. - - - -CLR version of the the SHADE Engine's SHColliderComponent. -A single Collider component can contain one or multiple Collider Bounds. - - - - - - - - - - -Radius of the Bounding Sphere formed by this bound. - - - - -Center of the Bounding Sphere formed by this bound. - - - - -Sphere-shaped Collider Bound. - - - - - - - - - - -Position of the top right front corner of the Bounding Box formed by this -bound. - - - - -Position of the bottom left back corner of the Bounding Box formed by this -bound. - - - - -Half of the scale of the Bounding Box formed by this bound. - - - - -Center of the Bounding Box formed by this bound. - - - - -Box-shaped Collider Bound. - - - - -Computes a Raycast and checks if there is a collision with any object. - - The ray to cast. - Maximum distance for the raycast check. - True if the ray intersects with an object in the scene. - - - -Checks if the specified point is within this shape's bounds. - - Point to test with. - True if the point is in the shape's bounds. - - - -Base interface for all Collider Shapes. - - - -@brief The density of the collider that determines the mass of the collision shape - if it is automatically computed. Must be a positive number. - - - -@brief The bounciness factor of the physics object., clamped between [0,1].
- 0 means the object will never bounce. - 1 means the object never loses energy on a bounce. - -
- -@brief The friction coefficient of the physics object., clamped between [0,1].
- 0 means the object will never experience friction. - 1 means the friction force against the object is equal to the applied force. - -
- -@brief Sets the mass density of the physics material. -@param newDensity The density value to set. Always made positive. - - - -@brief Sets the bounciness factor of the physics material. -@param newBounciness The bounciness value to set. Clamped between [0,1]. - - - -@brief Sets the friction coefficient of the physics material. -@param newFriction The friction value to set. Clamped between [0,1]. - - - -@brief Default constructor for a physics material. -@param friction The friction of the material. Clamped between [0,1]. Defaults to 0.4. -@param bounciness The bounciness of the material. Clamped between [0,1]. -@param density The mass density of the material. Always made positive. - - - - -Closes the current window, and depending on the implementation, should also -close the application. - - - - -Retrieves the current window fullscreen status. - - The current window fullscreen status.. - - - -Retrieves the current window height. - - The current window height. - - - -Retrieves the current window width. - - The current window width. - - - -Static class that wraps up certain functions in the SHGraphicsSystem so that -accessing it from SHADE_Managed would not cause issues due to C++20 features. - - - - @brief Perform ImGui and ImGui Backend Render - - - - - @brief Start new frame for editor - - - - - @brief Initialise Backend for ImGui (SDL and Vulkan backend) - - @param sdlWindow Pointer to SDL_Window - - - - @brief Set the Style for the editor - - @param style Desired style - - - - @brief Safely shutdown the editor - - - - - @brief Update the editor and add to ImGui DrawList - - @param dt Delta-time of the frame - - - - @brief Initialise the editor - - @param sdlWindow pointer to SDL_Window object created in application - - - - @brief Style options - - - - - @brief SHEditor static class contains editor variables and implementation of editor functions. - - - - - Get the YUV conversion mode, returning the correct mode for the resolution - when the current conversion mode is SDL_YUV_CONVERSION_AUTOMATIC - - \since This function is available since SDL 2.0.8. - - - - Get the YUV conversion mode - - \since This function is available since SDL 2.0.8. - - - - Set the YUV conversion mode - - \since This function is available since SDL 2.0.8. - - - - Perform low-level surface scaled blitting only. - - This is a semi-private function and it performs low-level surface blitting, - assuming the input rectangles have already been clipped. - - \param src the SDL_Surface structure to be copied from - \param srcrect the SDL_Rect structure representing the rectangle to be - copied - \param dst the SDL_Surface structure that is the blit target - \param dstrect the SDL_Rect structure representing the rectangle that is - copied into - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitScaled - - - - Perform a scaled surface copy to a destination surface. - - SDL_UpperBlitScaled() has been replaced by SDL_BlitScaled(), which is - merely a macro for this function with a less confusing name. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitScaled - - - - Perform bilinear scaling between two surfaces of the same format, 32BPP. - - \since This function is available since SDL 2.0.16. - - - - Perform a fast, low quality, stretch blit between two surfaces of the same - format. - - Please use SDL_BlitScaled() instead. - - \since This function is available since SDL 2.0.0. - - - - Perform low-level surface blitting only. - - This is a semi-private blit function and it performs low-level surface - blitting, assuming the input rectangles have already been clipped. - - Unless you know what you're doing, you should be using SDL_BlitSurface() - instead. - - \param src the SDL_Surface structure to be copied from - \param srcrect the SDL_Rect structure representing the rectangle to be - copied, or NULL to copy the entire surface - \param dst the SDL_Surface structure that is the blit target - \param dstrect the SDL_Rect structure representing the rectangle that is - copied into - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - - - - * Performs a fast blit from the source surface to the destination surface. - * - * This assumes that the source and destination rectangles are - * the same size. If either \c srcrect or \c dstrect are NULL, the entire - * surface (\c src or \c dst) is copied. The final blit rectangles are saved - * in \c srcrect and \c dstrect after all clipping is performed. - * - * \returns 0 if the blit is successful, otherwise it returns -1. - * - * The blit function should not be called on a locked surface. - * - * The blit semantics for surfaces with and without blending and colorkey - * are defined as follows: - * \verbatim - RGBA->RGB: - Source surface blend mode set to SDL_BLENDMODE_BLEND: - alpha-blend (using the source alpha-channel and per-surface alpha) - SDL_SRCCOLORKEY ignored. - Source surface blend mode set to SDL_BLENDMODE_NONE: - copy RGB. - if SDL_SRCCOLORKEY set, only copy the pixels matching the - RGB values of the source color key, ignoring alpha in the - comparison. - - RGB->RGBA: - Source surface blend mode set to SDL_BLENDMODE_BLEND: - alpha-blend (using the source per-surface alpha) - Source surface blend mode set to SDL_BLENDMODE_NONE: - copy RGB, set destination alpha to source per-surface alpha value. - both: - if SDL_SRCCOLORKEY set, only copy the pixels matching the - source color key. - - RGBA->RGBA: - Source surface blend mode set to SDL_BLENDMODE_BLEND: - alpha-blend (using the source alpha-channel and per-surface alpha) - SDL_SRCCOLORKEY ignored. - Source surface blend mode set to SDL_BLENDMODE_NONE: - copy all of RGBA to the destination. - if SDL_SRCCOLORKEY set, only copy the pixels matching the - RGB values of the source color key, ignoring alpha in the - comparison. - - RGB->RGB: - Source surface blend mode set to SDL_BLENDMODE_BLEND: - alpha-blend (using the source per-surface alpha) - Source surface blend mode set to SDL_BLENDMODE_NONE: - copy RGB. - both: - if SDL_SRCCOLORKEY set, only copy the pixels matching the - source color key. - \endverbatim - * - * You should call SDL_BlitSurface() unless you know exactly how SDL - * blitting works internally and how to use the other blit functions. - - Perform a fast blit from the source surface to the destination surface. - - SDL_UpperBlit() has been replaced by SDL_BlitSurface(), which is merely a - macro for this function with a less confusing name. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - - - - Perform a fast fill of a set of rectangles with a specific color. - - `color` should be a pixel of the format used by the surface, and can be - generated by SDL_MapRGB() or SDL_MapRGBA(). If the color value contains an - alpha component then the destination is simply filled with that alpha - information, no blending takes place. - - If there is a clip rectangle set on the destination (set via - SDL_SetClipRect()), then this function will fill based on the intersection - of the clip rectangle and `rect`. - - \param dst the SDL_Surface structure that is the drawing target - \param rects an array of SDL_Rects representing the rectangles to fill. - \param count the number of rectangles in the array - \param color the color to fill with - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FillRect - - - - Perform a fast fill of a rectangle with a specific color. - - `color` should be a pixel of the format used by the surface, and can be - generated by SDL_MapRGB() or SDL_MapRGBA(). If the color value contains an - alpha component then the destination is simply filled with that alpha - information, no blending takes place. - - If there is a clip rectangle set on the destination (set via - SDL_SetClipRect()), then this function will fill based on the intersection - of the clip rectangle and `rect`. - - \param dst the SDL_Surface structure that is the drawing target - \param rect the SDL_Rect structure representing the rectangle to fill, or - NULL to fill the entire surface - \param color the color to fill with - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FillRects - - - - Premultiply the alpha on a block of pixels. - - This is safe to use with src == dst, but not for other overlapping areas. - - This function is currently only implemented for SDL_PIXELFORMAT_ARGB8888. - - \param width the width of the block to convert, in pixels - \param height the height of the block to convert, in pixels - \param src_format an SDL_PixelFormatEnum value of the `src` pixels format - \param src a pointer to the source pixels - \param src_pitch the pitch of the source pixels, in bytes - \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format - \param dst a pointer to be filled in with premultiplied pixel data - \param dst_pitch the pitch of the destination pixels, in bytes - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.18. - - - - Copy a block of pixels of one format to another format. - - \param width the width of the block to copy, in pixels - \param height the height of the block to copy, in pixels - \param src_format an SDL_PixelFormatEnum value of the `src` pixels format - \param src a pointer to the source pixels - \param src_pitch the pitch of the source pixels, in bytes - \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format - \param dst a pointer to be filled in with new pixel data - \param dst_pitch the pitch of the destination pixels, in bytes - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - - - Copy an existing surface to a new surface of the specified format enum. - - This function operates just like SDL_ConvertSurface(), but accepts an - SDL_PixelFormatEnum value instead of an SDL_PixelFormat structure. As such, - it might be easier to call but it doesn't have access to palette - information for the destination surface, in case that would be important. - - \param src the existing SDL_Surface structure to convert - \param pixel_format the SDL_PixelFormatEnum that the new surface is - optimized for - \param flags the flags are unused and should be set to 0; this is a - leftover from SDL 1.2's API - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocFormat - \sa SDL_ConvertSurface - \sa SDL_CreateRGBSurface - - - - Copy an existing surface to a new surface of the specified format. - - This function is used to optimize images for faster *repeat* blitting. This - is accomplished by converting the original and storing the result as a new - surface. The new, optimized surface can then be used as the source for - future blits, making them faster. - - \param src the existing SDL_Surface structure to convert - \param fmt the SDL_PixelFormat structure that the new surface is optimized - for - \param flags the flags are unused and should be set to 0; this is a - leftover from SDL 1.2's API - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocFormat - \sa SDL_ConvertSurfaceFormat - \sa SDL_CreateRGBSurface - - - - Get the clipping rectangle for a surface. - - When `surface` is the destination of a blit, only the area within the clip - rectangle is drawn into. - - \param surface the SDL_Surface structure representing the surface to be - clipped - \param rect an SDL_Rect structure filled in with the clipping rectangle for - the surface - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_SetClipRect - - - - Set the clipping rectangle for a surface. - - When `surface` is the destination of a blit, only the area within the clip - rectangle is drawn into. - - Note that blits are automatically clipped to the edges of the source and - destination surfaces. - - \param surface the SDL_Surface structure to be clipped - \param rect the SDL_Rect structure representing the clipping rectangle, or - NULL to disable clipping - \returns SDL_TRUE if the rectangle intersects the surface, otherwise - SDL_FALSE and blits will be completely clipped. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_GetClipRect - - - - Get the blend mode used for blit operations. - - \param surface the SDL_Surface structure to query - \param blendMode a pointer filled in with the current SDL_BlendMode - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_SetSurfaceBlendMode - - - - Set the blend mode used for blit operations. - - To copy a surface to another surface (or texture) without blending with the - existing data, the blendmode of the SOURCE surface should be set to - `SDL_BLENDMODE_NONE`. - - \param surface the SDL_Surface structure to update - \param blendMode the SDL_BlendMode to use for blit blending - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceBlendMode - - - - Get the additional alpha value used in blit operations. - - \param surface the SDL_Surface structure to query - \param alpha a pointer filled in with the current alpha value - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceColorMod - \sa SDL_SetSurfaceAlphaMod - - - - Set an additional alpha value used in blit operations. - - When this surface is blitted, during the blit operation the source alpha - value is modulated by this alpha value according to the following formula: - - `srcA = srcA * (alpha / 255)` - - \param surface the SDL_Surface structure to update - \param alpha the alpha value multiplied into blit operations - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceAlphaMod - \sa SDL_SetSurfaceColorMod - - - - Get the additional color value multiplied into blit operations. - - \param surface the SDL_Surface structure to query - \param r a pointer filled in with the current red color value - \param g a pointer filled in with the current green color value - \param b a pointer filled in with the current blue color value - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceAlphaMod - \sa SDL_SetSurfaceColorMod - - - - Set an additional color value multiplied into blit operations. - - When this surface is blitted, during the blit operation each source color - channel is modulated by the appropriate color value according to the - following formula: - - `srcC = srcC * (color / 255)` - - \param surface the SDL_Surface structure to update - \param r the red color value multiplied into blit operations - \param g the green color value multiplied into blit operations - \param b the blue color value multiplied into blit operations - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetSurfaceColorMod - \sa SDL_SetSurfaceAlphaMod - - - - Get the color key (transparent pixel) for a surface. - - The color key is a pixel of the format used by the surface, as generated by - SDL_MapRGB(). - - If the surface doesn't have color key enabled this function returns -1. - - \param surface the SDL_Surface structure to query - \param key a pointer filled in with the transparent pixel - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_SetColorKey - - - - Returns whether the surface has a color key - - It is safe to pass a NULL `surface` here; it will return SDL_FALSE. - - \param surface the SDL_Surface structure to query - \return SDL_TRUE if the surface has a color key, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.9. - - \sa SDL_SetColorKey - \sa SDL_GetColorKey - - - - Set the color key (transparent pixel) in a surface. - - The color key defines a pixel value that will be treated as transparent in - a blit. For example, one can use this to specify that cyan pixels should be - considered transparent, and therefore not rendered. - - It is a pixel of the format used by the surface, as generated by - SDL_MapRGB(). - - RLE acceleration can substantially speed up blitting of images with large - horizontal runs of transparent pixels. See SDL_SetSurfaceRLE() for details. - - \param surface the SDL_Surface structure to update - \param flag SDL_TRUE to enable color key, SDL_FALSE to disable color key - \param key the transparent pixel - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_GetColorKey - - - - Returns whether the surface is RLE enabled - - It is safe to pass a NULL `surface` here; it will return SDL_FALSE. - - \param surface the SDL_Surface structure to query - \returns SDL_TRUE if the surface is RLE enabled, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.14. - - \sa SDL_SetSurfaceRLE - - - - Save a surface to a file. - - Convenience macro. - - Set the RLE acceleration hint for a surface. - - If RLE is enabled, color key and alpha blending blits are much faster, but - the surface must be locked before directly accessing the pixels. - - \param surface the SDL_Surface structure to optimize - \param flag 0 to disable, non-zero to enable RLE acceleration - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_BlitSurface - \sa SDL_LockSurface - \sa SDL_UnlockSurface - - - - Load a surface from a file. - - Convenience macro. - - Save a surface to a seekable SDL data stream in BMP format. - - Surfaces with a 24-bit, 32-bit and paletted 8-bit format get saved in the - BMP directly. Other RGB formats with 8-bit or higher get converted to a - 24-bit surface or, if they have an alpha mask or a colorkey, to a 32-bit - surface before they are saved. YUV and paletted 1-bit and 4-bit formats are - not supported. - - \param surface the SDL_Surface structure containing the image to be saved - \param dst a data stream to save to - \param freedst non-zero to close the stream after being written - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_LoadBMP_RW - \sa SDL_SaveBMP - - - - Load a BMP image from a seekable SDL data stream. - - The new surface should be freed with SDL_FreeSurface(). Not doing so will - result in a memory leak. - - src is an open SDL_RWops buffer, typically loaded with SDL_RWFromFile. - Alternitavely, you might also use the macro SDL_LoadBMP to load a bitmap - from a file, convert it to an SDL_Surface and then close the file. - - \param src the data stream for the surface - \param freesrc non-zero to close the stream after being read - \returns a pointer to a new SDL_Surface structure or NULL if there was an - error; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FreeSurface - \sa SDL_RWFromFile - \sa SDL_LoadBMP - \sa SDL_SaveBMP_RW - - - - Release a surface after directly accessing the pixels. - - \param surface the SDL_Surface structure to be unlocked - - \since This function is available since SDL 2.0.0. - - \sa SDL_LockSurface - - - - Set up a surface for directly accessing the pixels. - - Between calls to SDL_LockSurface() / SDL_UnlockSurface(), you can write to - and read from `surface->pixels`, using the pixel format stored in - `surface->format`. Once you are done accessing the surface, you should use - SDL_UnlockSurface() to release it. - - Not all surfaces require locking. If `SDL_MUSTLOCK(surface)` evaluates to - 0, then you can read and write to the surface at any time, and the pixel - format of the surface will not change. - - \param surface the SDL_Surface structure to be locked - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_MUSTLOCK - \sa SDL_UnlockSurface - - - - Set the palette used by a surface. - - A single palette can be shared with many surfaces. - - \param surface the SDL_Surface structure to update - \param palette the SDL_Palette structure to use - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - - - Free an RGB surface. - - It is safe to pass NULL to this function. - - \param surface the SDL_Surface to free. - - \since This function is available since SDL 2.0.0. - - \sa SDL_CreateRGBSurface - \sa SDL_CreateRGBSurfaceFrom - \sa SDL_LoadBMP - \sa SDL_LoadBMP_RW - - - - Allocate a new RGB surface with with a specific pixel format and existing - pixel data. - - This function operates mostly like SDL_CreateRGBSurfaceFrom(), except - instead of providing pixel color masks, you provide it with a predefined - format from SDL_PixelFormatEnum. - - No copy is made of the pixel data. Pixel data is not managed automatically; - you must free the surface before you free the pixel data. - - \param pixels a pointer to existing pixel data - \param width the width of the surface - \param height the height of the surface - \param depth the depth of the surface in bits - \param pitch the pitch of the surface in bytes - \param format the SDL_PixelFormatEnum for the new surface's pixel format. - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.5. - - \sa SDL_CreateRGBSurfaceFrom - \sa SDL_CreateRGBSurfaceWithFormat - \sa SDL_FreeSurface - - - - Allocate a new RGB surface with existing pixel data. - - This function operates mostly like SDL_CreateRGBSurface(), except it does - not allocate memory for the pixel data, instead the caller provides an - existing buffer of data for the surface to use. - - No copy is made of the pixel data. Pixel data is not managed automatically; - you must free the surface before you free the pixel data. - - \param pixels a pointer to existing pixel data - \param width the width of the surface - \param height the height of the surface - \param depth the depth of the surface in bits - \param pitch the pitch of the surface in bytes - \param Rmask the red mask for the pixels - \param Gmask the green mask for the pixels - \param Bmask the blue mask for the pixels - \param Amask the alpha mask for the pixels - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_CreateRGBSurface - \sa SDL_CreateRGBSurfaceWithFormat - \sa SDL_FreeSurface - - - - Allocate a new RGB surface with a specific pixel format. - - This function operates mostly like SDL_CreateRGBSurface(), except instead - of providing pixel color masks, you provide it with a predefined format - from SDL_PixelFormatEnum. - - \param flags the flags are unused and should be set to 0 - \param width the width of the surface - \param height the height of the surface - \param depth the depth of the surface in bits - \param format the SDL_PixelFormatEnum for the new surface's pixel format. - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.5. - - \sa SDL_CreateRGBSurface - \sa SDL_CreateRGBSurfaceFrom - \sa SDL_FreeSurface - - - - Allocate a new RGB surface. - - If `depth` is 4 or 8 bits, an empty palette is allocated for the surface. - If `depth` is greater than 8 bits, the pixel format is set using the - [RGBA]mask parameters. - - The [RGBA]mask parameters are the bitmasks used to extract that color from - a pixel. For instance, `Rmask` being 0xFF000000 means the red data is - stored in the most significant byte. Using zeros for the RGB masks sets a - default value, based on the depth. For example: - - ```c++ - SDL_CreateRGBSurface(0,w,h,32,0,0,0,0); - ``` - - However, using zero for the Amask results in an Amask of 0. - - By default surfaces with an alpha mask are set up for blending as with: - - ```c++ - SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND) - ``` - - You can change this by calling SDL_SetSurfaceBlendMode() and selecting a - different `blendMode`. - - \param flags the flags are unused and should be set to 0 - \param width the width of the surface - \param height the height of the surface - \param depth the depth of the surface in bits - \param Rmask the red mask for the pixels - \param Gmask the green mask for the pixels - \param Bmask the blue mask for the pixels - \param Amask the alpha mask for the pixels - \returns the new SDL_Surface structure that is created or NULL if it fails; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_CreateRGBSurfaceFrom - \sa SDL_CreateRGBSurfaceWithFormat - \sa SDL_FreeSurface - - - -Reference count -- used when freeing surface - - -info for fast blit mapping to other surfaces - - -clipping information - - -list of BlitMap that hold a reference to this surface - - -information needed for surfaces requiring locks - - -Application data associated with the surface - - - \brief A collection of pixels used in software blitting. - - \note This structure should be treated as read-only, except for \c pixels, - which, if not NULL, contains the raw pixel data for the surface. - - -\brief The type of function used for surface blitting functions. - - - - Compose a custom blend mode for renderers. - - The functions SDL_SetRenderDrawBlendMode and SDL_SetTextureBlendMode accept - the SDL_BlendMode returned by this function if the renderer supports it. - - A blend mode controls how the pixels from a drawing operation (source) get - combined with the pixels from the render target (destination). First, the - components of the source and destination pixels get multiplied with their - blend factors. Then, the blend operation takes the two products and - calculates the result that will get stored in the render target. - - Expressed in pseudocode, it would look like this: - - ```c - dstRGB = colorOperation(srcRGB * srcColorFactor, dstRGB * dstColorFactor); - dstA = alphaOperation(srcA * srcAlphaFactor, dstA * dstAlphaFactor); - ``` - - Where the functions `colorOperation(src, dst)` and `alphaOperation(src, - dst)` can return one of the following: - - - `src + dst` - - `src - dst` - - `dst - src` - - `min(src, dst)` - - `max(src, dst)` - - The red, green, and blue components are always multiplied with the first, - second, and third components of the SDL_BlendFactor, respectively. The - fourth component is not used. - - The alpha component is always multiplied with the fourth component of the - SDL_BlendFactor. The other components are not used in the alpha - calculation. - - Support for these blend modes varies for each renderer. To check if a - specific SDL_BlendMode is supported, create a renderer and pass it to - either SDL_SetRenderDrawBlendMode or SDL_SetTextureBlendMode. They will - return with an error if the blend mode is not supported. - - This list describes the support of custom blend modes for each renderer in - SDL 2.0.6. All renderers support the four blend modes listed in the - SDL_BlendMode enumeration. - - - **direct3d**: Supports all operations with all factors. However, some - factors produce unexpected results with `SDL_BLENDOPERATION_MINIMUM` and - `SDL_BLENDOPERATION_MAXIMUM`. - - **direct3d11**: Same as Direct3D 9. - - **opengl**: Supports the `SDL_BLENDOPERATION_ADD` operation with all - factors. OpenGL versions 1.1, 1.2, and 1.3 do not work correctly with SDL - 2.0.6. - - **opengles**: Supports the `SDL_BLENDOPERATION_ADD` operation with all - factors. Color and alpha factors need to be the same. OpenGL ES 1 - implementation specific: May also support `SDL_BLENDOPERATION_SUBTRACT` - and `SDL_BLENDOPERATION_REV_SUBTRACT`. May support color and alpha - operations being different from each other. May support color and alpha - factors being different from each other. - - **opengles2**: Supports the `SDL_BLENDOPERATION_ADD`, - `SDL_BLENDOPERATION_SUBTRACT`, `SDL_BLENDOPERATION_REV_SUBTRACT` - operations with all factors. - - **psp**: No custom blend mode support. - - **software**: No custom blend mode support. - - Some renderers do not provide an alpha component for the default render - target. The `SDL_BLENDFACTOR_DST_ALPHA` and - `SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA` factors do not have an effect in this - case. - - \param srcColorFactor the SDL_BlendFactor applied to the red, green, and - blue components of the source pixels - \param dstColorFactor the SDL_BlendFactor applied to the red, green, and - blue components of the destination pixels - \param colorOperation the SDL_BlendOperation used to combine the red, - green, and blue components of the source and - destination pixels - \param srcAlphaFactor the SDL_BlendFactor applied to the alpha component of - the source pixels - \param dstAlphaFactor the SDL_BlendFactor applied to the alpha component of - the destination pixels - \param alphaOperation the SDL_BlendOperation used to combine the alpha - component of the source and destination pixels - \returns an SDL_BlendMode that represents the chosen factors and - operations. - - \since This function is available since SDL 2.0.6. - - \sa SDL_SetRenderDrawBlendMode - \sa SDL_GetRenderDrawBlendMode - \sa SDL_SetTextureBlendMode - \sa SDL_GetTextureBlendMode - - - - Calculate the intersection of a rectangle and line segment with float - precision. - - This function is used to clip a line segment to a rectangle. A line segment - contained entirely within the rectangle or that does not intersect will - remain unchanged. A line segment that crosses the rectangle at either or - both ends will be clipped to the boundary of the rectangle and the new - coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary. - - \param rect an SDL_FRect structure representing the rectangle to intersect - \param X1 a pointer to the starting X-coordinate of the line - \param Y1 a pointer to the starting Y-coordinate of the line - \param X2 a pointer to the ending X-coordinate of the line - \param Y2 a pointer to the ending Y-coordinate of the line - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.22. - - - - Calculate a minimal rectangle enclosing a set of points with float - precision. - - If `clip` is not NULL then only points inside of the clipping rectangle are - considered. - - \param points an array of SDL_FPoint structures representing points to be - enclosed - \param count the number of structures in the `points` array - \param clip an SDL_FRect used for clipping or NULL to enclose all points - \param result an SDL_FRect structure filled in with the minimal enclosing - rectangle - \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the - points were outside of the clipping rectangle. - - \since This function is available since SDL 2.0.22. - - - - Calculate the union of two rectangles with float precision. - - \param A an SDL_FRect structure representing the first rectangle - \param B an SDL_FRect structure representing the second rectangle - \param result an SDL_FRect structure filled in with the union of rectangles - `A` and `B` - - \since This function is available since SDL 2.0.22. - - - - Calculate the intersection of two rectangles with float precision. - - If `result` is NULL then this function will return SDL_FALSE. - - \param A an SDL_FRect structure representing the first rectangle - \param B an SDL_FRect structure representing the second rectangle - \param result an SDL_FRect structure filled in with the intersection of - rectangles `A` and `B` - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.22. - - \sa SDL_HasIntersectionF - - - - Determine whether two rectangles intersect with float precision. - - If either pointer is NULL the function will return SDL_FALSE. - - \param A an SDL_FRect structure representing the first rectangle - \param B an SDL_FRect structure representing the second rectangle - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.22. - - \sa SDL_IntersectRect - - - - Returns true if the two rectangles are equal, using a default epsilon. - - \since This function is available since SDL 2.0.22. - - - - Returns true if the two rectangles are equal, within some given epsilon. - - \since This function is available since SDL 2.0.22. - - - -Returns true if the rectangle has no area. - - - -Returns true if point resides inside a rectangle. - - - - Calculate the intersection of a rectangle and line segment. - - This function is used to clip a line segment to a rectangle. A line segment - contained entirely within the rectangle or that does not intersect will - remain unchanged. A line segment that crosses the rectangle at either or - both ends will be clipped to the boundary of the rectangle and the new - coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary. - - \param rect an SDL_Rect structure representing the rectangle to intersect - \param X1 a pointer to the starting X-coordinate of the line - \param Y1 a pointer to the starting Y-coordinate of the line - \param X2 a pointer to the ending X-coordinate of the line - \param Y2 a pointer to the ending Y-coordinate of the line - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.0. - - - - Calculate a minimal rectangle enclosing a set of points. - - If `clip` is not NULL then only points inside of the clipping rectangle are - considered. - - \param points an array of SDL_Point structures representing points to be - enclosed - \param count the number of structures in the `points` array - \param clip an SDL_Rect used for clipping or NULL to enclose all points - \param result an SDL_Rect structure filled in with the minimal enclosing - rectangle - \returns SDL_TRUE if any points were enclosed or SDL_FALSE if all the - points were outside of the clipping rectangle. - - \since This function is available since SDL 2.0.0. - - - - Calculate the union of two rectangles. - - \param A an SDL_Rect structure representing the first rectangle - \param B an SDL_Rect structure representing the second rectangle - \param result an SDL_Rect structure filled in with the union of rectangles - `A` and `B` - - \since This function is available since SDL 2.0.0. - - - - Calculate the intersection of two rectangles. - - If `result` is NULL then this function will return SDL_FALSE. - - \param A an SDL_Rect structure representing the first rectangle - \param B an SDL_Rect structure representing the second rectangle - \param result an SDL_Rect structure filled in with the intersection of - rectangles `A` and `B` - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.0. - - \sa SDL_HasIntersection - - - - Determine whether two rectangles intersect. - - If either pointer is NULL the function will return SDL_FALSE. - - \param A an SDL_Rect structure representing the first rectangle - \param B an SDL_Rect structure representing the second rectangle - \returns SDL_TRUE if there is an intersection, SDL_FALSE otherwise. - - \since This function is available since SDL 2.0.0. - - \sa SDL_IntersectRect - - - -Returns true if the two rectangles are equal. - - - -Returns true if the rectangle has no area. - - - -Returns true if point resides inside a rectangle. - - - - A rectangle, with the origin at the upper left (floating point). - - \sa SDL_FRectEmpty - \sa SDL_FRectEquals - \sa SDL_FRectEqualsEpsilon - \sa SDL_HasIntersectionF - \sa SDL_IntersectFRect - \sa SDL_IntersectFRectAndLine - \sa SDL_UnionFRect - \sa SDL_EncloseFPoints - \sa SDL_PointInFRect - - - - A rectangle, with the origin at the upper left (integer). - - \sa SDL_RectEmpty - \sa SDL_RectEquals - \sa SDL_HasIntersection - \sa SDL_IntersectRect - \sa SDL_IntersectRectAndLine - \sa SDL_UnionRect - \sa SDL_EnclosePoints - - - - The structure that defines a point (floating point) - - \sa SDL_EncloseFPoints - \sa SDL_PointInFRect - - - - Use this function to write 64 bits in native format to a SDL_RWops as - big-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in big-endian format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteLE64 - - - - Use this function to write 64 bits in native format to a SDL_RWops as - little-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in little-endian - format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteBE64 - - - - Use this function to write 32 bits in native format to a SDL_RWops as - big-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in big-endian format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteLE32 - - - - Use this function to write 32 bits in native format to a SDL_RWops as - little-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in little-endian - format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteBE32 - - - - Use this function to write 16 bits in native format to a SDL_RWops as - big-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in big-endian format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteLE16 - - - - Use this function to write 16 bits in native format to a SDL_RWops as - little-endian data. - - SDL byteswaps the data only if necessary, so the application always - specifies native format, and the data written will be in little-endian - format. - - \param dst the stream to which data will be written - \param value the data to be written, in native format - \returns 1 on successful write, 0 on error. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteBE16 - - - - \name Write endian functions - - Write an item of native format to the specified endianness. - - Use this function to write a byte to an SDL_RWops. - - \param dst the SDL_RWops to write to - \param value the byte value to write - \returns 1 on success or 0 on failure; call SDL_GetError() for more - information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadU8 - - - - Use this function to read 64 bits of big-endian data from an SDL_RWops and - return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 64 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadLE64 - - - - Use this function to read 64 bits of little-endian data from an SDL_RWops - and return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 64 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadBE64 - - - - Use this function to read 32 bits of big-endian data from an SDL_RWops and - return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 32 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadLE32 - - - - Use this function to read 32 bits of little-endian data from an SDL_RWops - and return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 32 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadBE32 - - - - Use this function to read 16 bits of big-endian data from an SDL_RWops and - return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 16 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadLE16 - - - - Use this function to read 16 bits of little-endian data from an SDL_RWops - and return in native format. - - SDL byteswaps the data only if necessary, so the data returned will be in - the native byte order. - - \param src the stream from which to read data - \returns 16 bits of data in the native byte order of the platform. - - \since This function is available since SDL 2.0.0. - - \sa SDL_ReadBE16 - - - - \name Read endian functions - - Read an item of the specified endianness and return in native format. - - Use this function to read a byte from an SDL_RWops. - - \param src the SDL_RWops to read from - \returns the read byte on success or 0 on failure; call SDL_GetError() for - more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_WriteU8 - - - - Load all the data from a file path. - - The data is allocated with a zero byte at the end (null terminated) for - convenience. This extra byte is not included in the value reported via - `datasize`. - - The data should be freed with SDL_free(). - - Prior to SDL 2.0.10, this function was a macro wrapping around - SDL_LoadFile_RW. - - \param file the path to read all available data from - \param datasize if not NULL, will store the number of bytes read - \returns the data, or NULL if there was an error. - - \since This function is available since SDL 2.0.10. - - - - Load all the data from an SDL data stream. - - The data is allocated with a zero byte at the end (null terminated) for - convenience. This extra byte is not included in the value reported via - `datasize`. - - The data should be freed with SDL_free(). - - \param src the SDL_RWops to read all available data from - \param datasize if not NULL, will store the number of bytes read - \param freesrc if non-zero, calls SDL_RWclose() on `src` before returning - \returns the data, or NULL if there was an error. - - \since This function is available since SDL 2.0.6. - - - - Close and free an allocated SDL_RWops structure. - - SDL_RWclose() closes and cleans up the SDL_RWops stream. It releases any - resources used by the stream and frees the SDL_RWops itself with - SDL_FreeRW(). This returns 0 on success, or -1 if the stream failed to - flush to its output (e.g. to disk). - - Note that if this fails to flush the stream to disk, this function reports - an error, but the SDL_RWops is still invalid once this function returns. - - Prior to SDL 2.0.10, this function was a macro. - - \param context SDL_RWops structure to close - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWwrite - - - - Write to an SDL_RWops data stream. - - This function writes exactly `num` objects each of size `size` from the - area pointed at by `ptr` to the stream. If this fails for any reason, it'll - return less than `num` to demonstrate how far the write progressed. On - success, it returns `num`. - - SDL_RWwrite is actually a function wrapper that calls the SDL_RWops's - `write` method appropriately, to simplify application development. - - Prior to SDL 2.0.10, this function was a macro. - - \param context a pointer to an SDL_RWops structure - \param ptr a pointer to a buffer containing data to write - \param size the size of an object to write, in bytes - \param num the number of objects to write - \returns the number of objects written, which will be less than **num** on - error; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - - - - Read from a data source. - - This function reads up to `maxnum` objects each of size `size` from the - data source to the area pointed at by `ptr`. This function may read less - objects than requested. It will return zero when there has been an error or - the data stream is completely read. - - SDL_RWread() is actually a function wrapper that calls the SDL_RWops's - `read` method appropriately, to simplify application development. - - Prior to SDL 2.0.10, this function was a macro. - - \param context a pointer to an SDL_RWops structure - \param ptr a pointer to a buffer to read data into - \param size the size of each object to read, in bytes - \param maxnum the maximum number of objects to be read - \returns the number of objects read, or 0 at error or end of file; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWseek - \sa SDL_RWwrite - - - - Determine the current read/write offset in an SDL_RWops data stream. - - SDL_RWtell is actually a wrapper function that calls the SDL_RWops's `seek` - method, with an offset of 0 bytes from `RW_SEEK_CUR`, to simplify - application development. - - Prior to SDL 2.0.10, this function was a macro. - - \param context a SDL_RWops data stream object from which to get the current - offset - \returns the current offset in the stream, or -1 if the information can not - be determined. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWwrite - - - - Seek within an SDL_RWops data stream. - - This function seeks to byte `offset`, relative to `whence`. - - `whence` may be any of the following values: - - - `RW_SEEK_SET`: seek from the beginning of data - - `RW_SEEK_CUR`: seek relative to current read point - - `RW_SEEK_END`: seek relative to the end of data - - If this stream can not seek, it will return -1. - - SDL_RWseek() is actually a wrapper function that calls the SDL_RWops's - `seek` method appropriately, to simplify application development. - - Prior to SDL 2.0.10, this function was a macro. - - \param context a pointer to an SDL_RWops structure - \param offset an offset in bytes, relative to **whence** location; can be - negative - \param whence any of `RW_SEEK_SET`, `RW_SEEK_CUR`, `RW_SEEK_END` - \returns the final offset in the data stream after the seek or -1 on error. - - \since This function is available since SDL 2.0.10. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWtell - \sa SDL_RWwrite - - - - Use this function to get the size of the data stream in an SDL_RWops. - - Prior to SDL 2.0.10, this function was a macro. - - \param context the SDL_RWops to get the size of the data stream from - \returns the size of the data stream in the SDL_RWops on success, -1 if - unknown or a negative error code on failure; call SDL_GetError() - for more information. - - \since This function is available since SDL 2.0.10. - - - - Use this function to free an SDL_RWops structure allocated by - SDL_AllocRW(). - - Applications do not need to use this function unless they are providing - their own SDL_RWops implementation. If you just need a SDL_RWops to - read/write a common data source, you should use the built-in - implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc, and - call the **close** method on those SDL_RWops pointers when you are done - with them. - - Only use SDL_FreeRW() on pointers returned by SDL_AllocRW(). The pointer is - invalid as soon as this function returns. Any extra memory allocated during - creation of the SDL_RWops is not freed by SDL_FreeRW(); the programmer must - be responsible for managing that memory in their **close** method. - - \param area the SDL_RWops structure to be freed - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocRW - - - - Use this function to allocate an empty, unpopulated SDL_RWops structure. - - Applications do not need to use this function unless they are providing - their own SDL_RWops implementation. If you just need a SDL_RWops to - read/write a common data source, you should use the built-in - implementations in SDL, like SDL_RWFromFile() or SDL_RWFromMem(), etc. - - You must free the returned pointer with SDL_FreeRW(). Depending on your - operating system and compiler, there may be a difference between the - malloc() and free() your program uses and the versions SDL calls - internally. Trying to mix the two can cause crashing such as segmentation - faults. Since all SDL_RWops must free themselves when their **close** - method is called, all SDL_RWops must be allocated through this function, so - they can all be freed correctly with SDL_FreeRW(). - - \returns a pointer to the allocated memory on success, or NULL on failure; - call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FreeRW - - - - Use this function to prepare a read-only memory buffer for use with RWops. - - This function sets up an SDL_RWops struct based on a memory area of a - certain size. It assumes the memory area is not writable. - - Attempting to write to this RWops stream will report an error without - writing to the memory buffer. - - This memory buffer is not copied by the RWops; the pointer you provide must - remain valid until you close the stream. Closing the stream will not free - the original buffer. - - If you need to write to a memory buffer, you should use SDL_RWFromMem() - with a writable buffer of memory instead. - - \param mem a pointer to a read-only buffer to feed an SDL_RWops stream - \param size the buffer size, in bytes - \returns a pointer to a new SDL_RWops structure, or NULL if it fails; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWtell - - - - Use this function to prepare a read-write memory buffer for use with - SDL_RWops. - - This function sets up an SDL_RWops struct based on a memory area of a - certain size, for both read and write access. - - This memory buffer is not copied by the RWops; the pointer you provide must - remain valid until you close the stream. Closing the stream will not free - the original buffer. - - If you need to make sure the RWops never writes to the memory buffer, you - should use SDL_RWFromConstMem() with a read-only buffer of memory instead. - - \param mem a pointer to a buffer to feed an SDL_RWops stream - \param size the buffer size, in bytes - \returns a pointer to a new SDL_RWops structure, or NULL if it fails; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWtell - \sa SDL_RWwrite - - - - Use this function to create an SDL_RWops structure from a standard I/O file - pointer (stdio.h's `FILE*`). - - This function is not available on Windows, since files opened in an - application on that platform cannot be used by a dynamically linked - library. - - On some platforms, the first parameter is a `void*`, on others, it's a - `FILE*`, depending on what system headers are available to SDL. It is - always intended to be the `FILE*` type from the C runtime's stdio.h. - - \param fp the `FILE*` that feeds the SDL_RWops stream - \param autoclose SDL_TRUE to close the `FILE*` when closing the SDL_RWops, - SDL_FALSE to leave the `FILE*` open when the RWops is - closed - \returns a pointer to the SDL_RWops structure that is created, or NULL on - failure; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFile - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWtell - \sa SDL_RWwrite - - - - \name RWFrom functions - - Functions to create SDL_RWops structures from various data streams. - - Use this function to create a new SDL_RWops structure for reading from - and/or writing to a named file. - - The `mode` string is treated roughly the same as in a call to the C - library's fopen(), even if SDL doesn't happen to use fopen() behind the - scenes. - - Available `mode` strings: - - - "r": Open a file for reading. The file must exist. - - "w": Create an empty file for writing. If a file with the same name - already exists its content is erased and the file is treated as a new - empty file. - - "a": Append to a file. Writing operations append data at the end of the - file. The file is created if it does not exist. - - "r+": Open a file for update both reading and writing. The file must - exist. - - "w+": Create an empty file for both reading and writing. If a file with - the same name already exists its content is erased and the file is - treated as a new empty file. - - "a+": Open a file for reading and appending. All writing operations are - performed at the end of the file, protecting the previous content to be - overwritten. You can reposition (fseek, rewind) the internal pointer to - anywhere in the file for reading, but writing operations will move it - back to the end of file. The file is created if it does not exist. - - **NOTE**: In order to open a file as a binary file, a "b" character has to - be included in the `mode` string. This additional "b" character can either - be appended at the end of the string (thus making the following compound - modes: "rb", "wb", "ab", "r+b", "w+b", "a+b") or be inserted between the - letter and the "+" sign for the mixed modes ("rb+", "wb+", "ab+"). - Additional characters may follow the sequence, although they should have no - effect. For example, "t" is sometimes appended to make explicit the file is - a text file. - - This function supports Unicode filenames, but they must be encoded in UTF-8 - format, regardless of the underlying operating system. - - As a fallback, SDL_RWFromFile() will transparently open a matching filename - in an Android app's `assets`. - - Closing the SDL_RWops will close the file handle SDL is holding internally. - - \param file a UTF-8 string representing the filename to open - \param mode an ASCII string representing the mode to be used for opening - the file. - \returns a pointer to the SDL_RWops structure that is created, or NULL on - failure; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_RWclose - \sa SDL_RWFromConstMem - \sa SDL_RWFromFP - \sa SDL_RWFromMem - \sa SDL_RWread - \sa SDL_RWseek - \sa SDL_RWtell - \sa SDL_RWwrite - - - -Return the size of the file in this rwops, or -1 if unknown - - - Seek to \c offset relative to \c whence, one of stdio's whence values: - RW_SEEK_SET, RW_SEEK_CUR, RW_SEEK_END - - \return the final offset in the data stream, or -1 on error. - - - Read up to \c maxnum objects each of size \c size from the data - stream to the area pointed at by \c ptr. - - \return the number of objects read, or 0 at error or end of file. - - - Write exactly \c num objects each of size \c size from the area - pointed at by \c ptr to data stream. - - \return the number of objects written, or 0 at error or end of file. - - - Close and free an allocated SDL_RWops structure. - - \return 0 if successful or -1 on write error when flushing data. - - - - Clear any previous error message for this thread. - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetError - \sa SDL_SetError - - - - Get the last error message that was set for the current thread. - - This allows the caller to copy the error string into a provided buffer, but - otherwise operates exactly the same as SDL_GetError(). - - \param errstr A buffer to fill with the last error message that was set for - the current thread - \param maxlen The size of the buffer pointed to by the errstr parameter - \returns the pointer passed in as the `errstr` parameter. - - \since This function is available since SDL 2.0.14. - - \sa SDL_GetError - - - - Retrieve a message about the last error that occurred on the current - thread. - - It is possible for multiple errors to occur before calling SDL_GetError(). - Only the last error is returned. - - The message is only applicable when an SDL function has signaled an error. - You must check the return values of SDL function calls to determine when to - appropriately call SDL_GetError(). You should *not* use the results of - SDL_GetError() to decide if an error has occurred! Sometimes SDL will set - an error string even when reporting success. - - SDL will *not* clear the error string for successful API calls. You *must* - check return values for failure cases before you can assume the error - string applies. - - Error strings are set per-thread, so an error set in a different thread - will not interfere with the current thread's operation. - - The returned string is internally allocated and must not be freed by the - application. - - \returns a message with information about the specific error that occurred, - or an empty string if there hasn't been an error message set since - the last call to SDL_ClearError(). The message is only applicable - when an SDL function has signaled an error. You must check the - return values of SDL function calls to determine when to - appropriately call SDL_GetError(). - - \since This function is available since SDL 2.0.0. - - \sa SDL_ClearError - \sa SDL_SetError - - - - Calculate a 256 entry gamma ramp for a gamma value. - - \param gamma a gamma value where 0.0 is black and 1.0 is identity - \param ramp an array of 256 values filled in with the gamma ramp - - \since This function is available since SDL 2.0.0. - - \sa SDL_SetWindowGammaRamp - - - - Get RGBA values from a pixel in the specified format. - - This function uses the entire 8-bit [0..255] range when converting color - components from pixel formats with less than 8-bits per RGB component - (e.g., a completely white pixel in 16-bit RGB565 format would return [0xff, - 0xff, 0xff] not [0xf8, 0xfc, 0xf8]). - - If the surface has no alpha component, the alpha will be returned as 0xff - (100% opaque). - - \param pixel a pixel value - \param format an SDL_PixelFormat structure describing the format of the - pixel - \param r a pointer filled in with the red component - \param g a pointer filled in with the green component - \param b a pointer filled in with the blue component - \param a a pointer filled in with the alpha component - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetRGB - \sa SDL_MapRGB - \sa SDL_MapRGBA - - - - Get RGB values from a pixel in the specified format. - - This function uses the entire 8-bit [0..255] range when converting color - components from pixel formats with less than 8-bits per RGB component - (e.g., a completely white pixel in 16-bit RGB565 format would return [0xff, - 0xff, 0xff] not [0xf8, 0xfc, 0xf8]). - - \param pixel a pixel value - \param format an SDL_PixelFormat structure describing the format of the - pixel - \param r a pointer filled in with the red component - \param g a pointer filled in with the green component - \param b a pointer filled in with the blue component - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetRGBA - \sa SDL_MapRGB - \sa SDL_MapRGBA - - - - Map an RGBA quadruple to a pixel value for a given pixel format. - - This function maps the RGBA color value to the specified pixel format and - returns the pixel value best approximating the given RGBA color value for - the given pixel format. - - If the specified pixel format has no alpha component the alpha value will - be ignored (as it will be in formats with a palette). - - If the format has a palette (8-bit) the index of the closest matching color - in the palette will be returned. - - If the pixel format bpp (color depth) is less than 32-bpp then the unused - upper bits of the return value can safely be ignored (e.g., with a 16-bpp - format the return value can be assigned to a Uint16, and similarly a Uint8 - for an 8-bpp format). - - \param format an SDL_PixelFormat structure describing the format of the - pixel - \param r the red component of the pixel in the range 0-255 - \param g the green component of the pixel in the range 0-255 - \param b the blue component of the pixel in the range 0-255 - \param a the alpha component of the pixel in the range 0-255 - \returns a pixel value - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetRGB - \sa SDL_GetRGBA - \sa SDL_MapRGB - - - - Map an RGB triple to an opaque pixel value for a given pixel format. - - This function maps the RGB color value to the specified pixel format and - returns the pixel value best approximating the given RGB color value for - the given pixel format. - - If the format has a palette (8-bit) the index of the closest matching color - in the palette will be returned. - - If the specified pixel format has an alpha component it will be returned as - all 1 bits (fully opaque). - - If the pixel format bpp (color depth) is less than 32-bpp then the unused - upper bits of the return value can safely be ignored (e.g., with a 16-bpp - format the return value can be assigned to a Uint16, and similarly a Uint8 - for an 8-bpp format). - - \param format an SDL_PixelFormat structure describing the pixel format - \param r the red component of the pixel in the range 0-255 - \param g the green component of the pixel in the range 0-255 - \param b the blue component of the pixel in the range 0-255 - \returns a pixel value - - \since This function is available since SDL 2.0.0. - - \sa SDL_GetRGB - \sa SDL_GetRGBA - \sa SDL_MapRGBA - - - - Free a palette created with SDL_AllocPalette(). - - \param palette the SDL_Palette structure to be freed - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocPalette - - - - Set a range of colors in a palette. - - \param palette the SDL_Palette structure to modify - \param colors an array of SDL_Color structures to copy into the palette - \param firstcolor the index of the first palette entry to modify - \param ncolors the number of entries to modify - \returns 0 on success or a negative error code if not all of the colors - could be set; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocPalette - \sa SDL_CreateRGBSurface - - - - Set the palette for a pixel format structure. - - \param format the SDL_PixelFormat structure that will use the palette - \param palette the SDL_Palette structure that will be used - \returns 0 on success or a negative error code on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocPalette - \sa SDL_FreePalette - - - - Create a palette structure with the specified number of color entries. - - The palette entries are initialized to white. - - \param ncolors represents the number of color entries in the color palette - \returns a new SDL_Palette structure on success or NULL on failure (e.g. if - there wasn't enough memory); call SDL_GetError() for more - information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FreePalette - - - - Free an SDL_PixelFormat structure allocated by SDL_AllocFormat(). - - \param format the SDL_PixelFormat structure to free - - \since This function is available since SDL 2.0.0. - - \sa SDL_AllocFormat - - - - Create an SDL_PixelFormat structure corresponding to a pixel format. - - Returned structure may come from a shared global cache (i.e. not newly - allocated), and hence should not be modified, especially the palette. Weird - errors such as `Blit combination not supported` may occur. - - \param pixel_format one of the SDL_PixelFormatEnum values - \returns the new SDL_PixelFormat structure or NULL on failure; call - SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_FreeFormat - - - - Convert a bpp value and RGBA masks to an enumerated pixel format. - - This will return `SDL_PIXELFORMAT_UNKNOWN` if the conversion wasn't - possible. - - \param bpp a bits per pixel value; usually 15, 16, or 32 - \param Rmask the red mask for the format - \param Gmask the green mask for the format - \param Bmask the blue mask for the format - \param Amask the alpha mask for the format - \returns one of the SDL_PixelFormatEnum values - - \since This function is available since SDL 2.0.0. - - \sa SDL_PixelFormatEnumToMasks - - - - Convert one of the enumerated pixel formats to a bpp value and RGBA masks. - - \param format one of the SDL_PixelFormatEnum values - \param bpp a bits per pixel value; usually 15, 16, or 32 - \param Rmask a pointer filled in with the red mask for the format - \param Gmask a pointer filled in with the green mask for the format - \param Bmask a pointer filled in with the blue mask for the format - \param Amask a pointer filled in with the alpha mask for the format - \returns SDL_TRUE on success or SDL_FALSE if the conversion wasn't - possible; call SDL_GetError() for more information. - - \since This function is available since SDL 2.0.0. - - \sa SDL_MasksToPixelFormatEnum - - - - Get the human readable name of a pixel format. - - \param format the pixel format to query - \returns the human readable name of the specified pixel format or - `SDL_PIXELFORMAT_UNKNOWN` if the format isn't recognized. - - \since This function is available since SDL 2.0.0. - - - -\note Everything in the pixel format structure is read-only. - - - -The bits of this structure can be directly reinterpreted as an integer-packed -color which uses the SDL_PIXELFORMAT_RGBA32 format (SDL_PIXELFORMAT_ABGR8888 -on little-endian systems and SDL_PIXELFORMAT_RGBA8888 on big-endian systems). - - - - If a + b would overflow, return -1. Otherwise store a + b via ret - and return 0. - - \since This function is available since SDL 2.24.0. - - - - If a * b would overflow, return -1. Otherwise store a * b via ret - and return 0. - - \since This function is available since SDL 2.24.0. - - - - This function converts a string between encodings in one pass, returning a - string that must be freed with SDL_free() or NULL on error. - - \since This function is available since SDL 2.0.0. - - - - Get the number of outstanding (unfreed) allocations - - \since This function is available since SDL 2.0.7. - - - - Replace SDL's memory allocation functions with a custom set - - \since This function is available since SDL 2.0.7. - - - - Get the current set of SDL memory functions - - \since This function is available since SDL 2.0.7. - - - - Get the original set of SDL memory functions - - \since This function is available since SDL 2.24.0. - - - -\endcond - \file begin_code.h - - This file sets things up for C dynamic library function definitions, - static inlined functions, and structures aligned at 4-byte alignment. - If you don't like ugly C preprocessor code, don't look at this file. :) - - - -\name Floating-point constants - -\cond - - -\brief An unsigned 64-bit integer type. - - - -\brief A signed 64-bit integer type. - - - -\brief An unsigned 32-bit integer type. - - - -\brief A signed 32-bit integer type. - - - -\brief An unsigned 16-bit integer type. - - - -\brief A signed 16-bit integer type. - - - -\brief An unsigned 8-bit integer type. - - - -\brief A signed 8-bit integer type. - - - - \file close_code.h - - This file reverses the effects of begin_code.h and should be included - after you finish any function and structure declarations in your headers - - - - \file SDL_stdinc.h - - This is a general header that includes C language support. - - \file SDL_platform.h - - Try to get a standard set of platform defines. - - \file begin_code.h - - This file sets things up for C dynamic library function definitions, - static inlined functions, and structures aligned at 4-byte alignment. - If you don't like ugly C preprocessor code, don't look at this file. :) - - Get the name of the platform. - - Here are the names returned for some (but not all) supported platforms: - - - "Windows" - - "Mac OS X" - - "Linux" - - "iOS" - - "Android" - - \returns the name of the platform. If the correct platform name is not - available, returns a string beginning with the text "Unknown". - - \since This function is available since SDL 2.0.0. - - - -*!************************************************************************* - - - -Marks the application to stop at the end of the current frame. - - - - -Whether or not the application is currently in fullscreen mode or not. - - - - -Retrieves the designated height of the current window. - - - - -Retrieves the designated width of the current window. - - - - -Whether or not the engine is in a paused state where script updates and -physics are not in play. - - - - -Whether or not the engine is playing. This will always be true on Publish. -On Debug/Release builds, this is true when the editor is in Play Mode. It -will also be true even if the editor is in Play Mode but is paused. - - - - -Static class that contains useful properties for querying the state of the -engine. - - - - -Sets the parent of this Transform component. - - -Entity that contains the Transform component that this Transform will be -parented to. If null, unparenting will occur. - - -If true, the transform values of this Transform component will retain their -pre-parent-change global transforms. The local transform values will be -modified to ensure that the global transforms do not change. - - - - -Parent Transform that affects this Transform. - - - - -Global scale stored by this Transform. - - - - -Global euler angle rotations stored by this Transform. - - - - -Global rotation quaternion stored by this Transform. - - - - -Global position stored by this Transform. - - - - -Local scale stored by this Transform. - - - - -Local euler angle rotations stored by this Transform. - - - - -Local rotation quaternion stored by this Transform. - - - - -Local position stored by this Transform. - - - - -Constructs a Transform Component that represents a native Transform component -tied to the specified Entity. - - Entity that this Component will be tied to. - - - -CLR version of the SHADE Engine's TransformComponent. - - - - -Compares if two float values are close enough to be the same with the -specified tolerance value. - - One of the values to compare. - The other value to compare. - Tolerance for floating point comparison. - True if a and b are practically the same. - - - -Compares if two float values are close enough to be the same with a tolerance -of Epsilon. - - One of the values to compare. - The other value to compare. - True if a and b are practically the same. - - - -Calculates the linear parameter t that produces the interpolant value within -the range [a, b]. - - Start value. - End value. - Value between start and end. - Percentage of value between start and end. - - - -Linearly interpolates between a and b by t. -The parameter t is not clamped and a value based on a and b is supported. -If t is less than zero, or greater than one, then LerpUnclamped will result -in a return value outside the range a to b. - - The start value. - The end value. - The interpolation value between the two float. - The interpolated float result between the two float values. - - - -Linearly interpolates between a and b by t. -The parameter t is clamped to the range [0, 1]. - - The start value. - The end value. - The interpolation value between the two float. - The interpolated float result between the two float values. - - - -Converts an angle from radian representation to degree representation. - - Radian-based angle to convert. - The specified angle in degrees. - - - -Converts an angle from degree representation to radian representation. - - Degree-based angle to convert. - The specified angle in radians. - - - -Wraps a value if they get to low or too high. - - Value to wrap. - Minimum value to wrap at. - Maximum value to wrap at. - Wrapped value. - - - -Small value used for single precision floating point comparisons. - - - - -Radians-to-degrees conversion constant - - - - -Degrees-to-radians conversion constant - - - - -Contains utility Math functions. - - - - -Logs a native exception that is formatted nicely to the output. - - Native exception to log. - Name of the one responsible for the exception. - - - -Logs an exception that is formatted nicely to the output. - - Name of the one responsible for the exception. - Exception to log. - - - -Logs a native exception that is formatted nicely to the output. -Equivalent to calling -LogException(exception, Convert::ToNative(thrower->GetType()->Name)); - - Native exception to log. - -Object that threw the exception to label the exception message. -The name of the object will be used. - - - - -Logs an exception that is formatted nicely to the output. - - Exception to log. - -Object that threw the exception to label the exception message. -The name of the object will be used. - - - - -Logs an exception that is formatted nicely to the output. - - Exception to log. - - - -Logs a error message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that threw the error to label the error message. -The name of the object will be used. - - - - -Logs a error message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that threw the error to label the error message. -The name of the object will be used. - - - - -Logs a error message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Object that threw the error to label the error message. -The name of the object will be used. - - - - -Logs a error message to the output. - - The string to output. - - - -Logs a error message to the output. - - The string to output. - - - -Logs a warning message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that threw the warning to label the warning message. -The name of the object will be used. - - - - -Logs a warning message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that threw the warning to label the warning message. -The name of the object will be used. - - - - -Logs a warning message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Object that threw the warning to label the warning message. -The name of the object will be used. - - - - -Logs a warning message to the output. - - The string to output. - - - -Logs a warning message to the output. - - The string to output. - - - -Logs a message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that sent the message to label the message. -The name of the object will be used. - - - - -Logs a message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Name of the object that sent the message to label the message. -The name of the object will be used. - - - - -Logs a message to the output with a label such that it looks like this: -"[Label] Message" - - The string to output. - -Object that sent the message to label the message. -The name of the object will be used. - - - - -Logs a message to the output. - - The string to output. - - - -Logs a message to the output. - - The string to output. - - - -Static class that contains the functions for working with time. - - - - -Material used to render this Renderable. - - - - -Material used to render this Renderable. - - - - -Mesh used to render this Renderable. - - - - -Constructs a Renderable Component that represents a native Renderable -component tied to the specified Entity. - - Entity that this Component will be tied to. - - - -CLR version of the SHADE Engine's SHRenderableComponent. - - - - -Retrieves the value of a specified property on the material. - - Type of property to get. - Name of the property to get. - Value of that property on the material. - -If this Material object is invalid. - - -If the name or type was specified that does not match the material's shader's -defined properties. - - - - -Set the value of a specific property. - - Type of property to set. - Name of the property to set. - Value to set te property to. - -If this Material object is invalid. - - -If the name or type was specified that does not match the material's shader's -defined properties. - - - - -Constructor for the Material - - Handle to the native material object. - - - -Managed counterpart of the native MaterialInstance object containing material -data that can be fed to Renderables for rendering. - - - - -Constructor for the Mesh - - Handle to the mesh object. - - - -Managed counterpart of the native Mesh object containing vertex data that can -be fed to Renderables for rendering. - - - -@brief Decomposes a transformation matrix into translation, orientation and scale. -@param[out] scale The scaling factor of the matrix. -@param[out] orientation The orientation of the matrix. -@param[out] translation The translation of the matrix. -@return True if decomposition was successful. - - - -@brief Decomposes a transformation matrix into translation, euler angles and scale. -@param[out] scale The scaling factor of the matrix. -@param[out] rotation The euler angles of the matrix. -@param[out] translation The translation of the matrix. -@return True if decomposition was successful. - - - -@brief Interface for a Column-Major Row Vector 4x4 Matrix. - - - - -Constructs a RigidBody Component that represents a native -SHRigidBodyComponent component tied to the specified Entity. - - Entity that this Component will be tied to. - - - -CLR version of the the SHADE Engine's SHRigidBodyComponent. - - - - -Creates an instance of the Managed representation of a Component with a -native Entity. - - Type of Component to create. - Native Entity that this Component is tied to. - The created Managed representation of the Component. - - - -Static constructor to initialize static data - - - - -Pointer to a function for Component manipulation operations. - - -Contains a set of Component related data used for resolving operations for -each Component. - - - - -Removes a Component from the specified Entity. - - Type of the Component to remove. - -Entity object that should have the specified Component removed from/ - - - - -Checks if the specified Entity has the specified Component. - - Type of the Component to check for. - Entity object to check for the Component. - -True if the specified Entity has the specified Component. False otherwise. - - - - -Ensures a Component on the specified Entity. - - Type of the Component to ensure. - Entity object to ensure the Component on. - Reference to the Component. - - - -Retrieves the first Component from the specified GameObjectt's children that -matches the specified type. - - Type of the Component to get. - Entity object to get the Component from. - -Reference to the Component or null if the Entity does not have the -specified Component. - - - - -Gets a Component from the specified Entity. - - Type of the Component to get. - Entity object to get the Component from. - -Reference to the Component or null if the Entity does not have the -specified Component. - - - - -Adds a Component to the specified Entity. - - Type of the Component to add. - -Entity object that should have the specified Component added to. - - Reference to the Component that was added. - - - -Static class which contains functions that map Pls::ECS's Component manipulation -functions to managed generic functions. - - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Entity that this Component belongs to. - - - - -Constructor for BaseComponent to tie it to a specific Entity. -Constructors of derived Components should call this Constructor. - - Entity that this Component will be tied to. - - - -Implicit conversion operator to enable checking if a component is null. - - Component to check. - - - -Removes all Scripts of the specified type from this GameObject. - - Type of PLushieScripts to remove. - - - -Retrieves a immutable list of Scripts of the specified type from this -GameObject. - - Type of Scripts to Get. - Immutable list of Scripts of the specified type. - - - -Retrieves a Script of the specified type from this GameObject. -If multiple Scripts of the same specified type are added on the same -GameObject, this will retrieve the first one added. - - Type of Script to add. - Reference to the Script to retrieve. - - - -Adds a Script of the specified type to this GameObject. - - Type of Script to add. - Reference to the created Script. - - - -Removes a Component from this GameObject. If no Component exists to begin -with, nothing happens. - - Type of the Component to get. - - - -Gets a Component from this GameObject. - - Type of the Component to get. - -Reference to the Component or null if this GameObject does not have the -specified Component. - - - - -Adds a Component to this GameObject. - - Type of the Component to add. - Reference to the Component that was added. - - - -Retrieves the GameObject that this Component belongs to. - - - - -Class that serves as the base for a wrapper class to Components in native code. - - - - -Called when the attached GameObject has a Collider and leaves a -collision with another GameObject with a Collider2D. - - Information on the collision event. - - - -Called when the attached GameObject has a Collider and collides with -another GameObject with a Collider in subsequent frames of collision. - - Information on the collision event. - - - -Called when the attached GameObject has a Collider and collides with -another GameObject with a Collider in the first frame of collision. - - Information on the collision event. - - - -Called when the attached GameObject has a trigger Collider and leaves a -collision with another GameObject with a Collider2D. - - Information on the collision event. - - - -Called when the attached GameObject has a trigger Collider and collides with -another GameObject with a Collider in subsequent frames of collision. - - Information on the collision event. - - - -Called when the attached GameObject has a trigger Collider and collides with -another GameObject with a Collider in the first frame of collision. - - Information on the collision event. - - - -Called just before the end of the frame where the attached GameObject or -this script is destroyed directly or indirectly due to destruction of the -owner. - - - - -Called every frame after physics and collision resolution but before -rendering. - - - - -Called every frame before physics and collision resolution. - - - - -Called every frame in sync with Physics update steps and thus in most cases -will execute more than update() will. This will be called immediately before -a Physics update step. - - - - -Called on the first frame that the attached GameObject is active but always -after Awake(). - - - - -Called on the first frame that the attached GameObject is active if they are -a part of the scene. - - - - -Called immediately once this script is detached from a GameObject. - - - - -Called immediately once this script is attached to a GameObject. - - - - -Constructor for Script to tie it to a specific GameObject. -Constructors of derived Scripts should call this Constructor. - - -GameObject that this Script will be tied to. - - - - -Used to call onTriggerExit(). This should be called when a trigger-type -collision is detected between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onTriggerStay(). This should be called when a trigger-type -collision is detected between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onTriggerEnter(). This should be called when a trigger-type -collision is detected between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onCollisionExit(). This should be called when a collision ends -between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onCollisionStay(). This should be called when a collision is -persistent between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onCollisionEnter(). This should be called when a collision is -detected between the attached GameObject and another GameObject. - - Information on the collision event. - - - -Used to call onDestroy(). This should be called at the end of the frame -where the attached GameObject or this script is destroyed directly or -indirectly due to destruction of the owner. - - - - -Used to call lateUpdate(). This should be called every frame after physics -and collision resolution but before rendering. - - - - -Used to call update(). This should be called every frame before physics and -collision resolution. - - - - -Used to call fixedUpdate(). This should be called in sync with Physics -update steps and thus in most cases will execute more than Update() will. -This will be called immediately before a Physics update step. - - - - -Used to call start(). This should be called on the first frame that the -attached GameObject is active but always after Awake(). - - - - -Used to call awake(). This should be called on the first frame that the -attached GameObject is active if they are a part of the scene. - - - - -Used to call onDetached(). This is called immediately when this script is -detached from a GameObject. - - - - -Used to call onAttached(). This is called immediately when this script is -attached to a GameObject. - - - - -Implicit conversion operator to enable checking if a component is null. - - Component to check. - - - -Removes all Scripts of the specified type from this GameObject. - - -Type of script to remove. -This needs to be a default constructable Script. - - - - -Retrieves a immutable list of scripts from the specified Entity that -matches the specified type. -
-Note that this function allocates. It should be used sparingly. -
- -Type of scripts to get. -This needs to be a default constructable Script. - - -Immutable list of references to scripts of the specified type. - -
- - -Retrieves the first Script from this GameObject's children that matches the -specified type. - - -Type of script to get. -This needs to be a default constructable Script. - - Reference to the script added - - - -Retrieves the first Script from this GameObject that matches the specified -type. - - -Type of script to get. -This needs to be a default constructable Script. - - Reference to the script added - - - -Adds a Script to this GameObject. - - -Type of script to add. -This needs to be a default constructable Script. - - Reference to the script added - - - -Removes a Component from the GameObject that this Script belongs to. - - -Type of the Component to remove. Must be derived from BaseComponent. - - - - -Ensures a Component on the GameObject that this Script belongs to. - - -Type of the Component to ensure. Must be derived from BaseComponent. - - Reference to the Component. - - - -Retrieves the first Component from this GameObject's children that matches -the specified type. - - -Type of the Component to get. Must be derived from BaseComponent. - - Reference to the Component that was retrieved. - - - -Gets a Component from the GameObject that this Script belongs to. - - -Type of the Component to get. Must be derived from BaseComponent. - - Reference to the Component that was retrieved. - - - -Adds a Component to the GameObject that this Script belongs to. - - -Type of the Component to add. Must be derived from BaseComponent. - - Reference to the Component that was added. - - - -GameObject that this Script belongs to. - - - - -Class that forms the basis of all "script"-objects that can be attached to -Entities to update each Entity's Components via C# code. - - - - -The RigidBody that you are colliding with. - - - - -The CollisionShape of the Collider that you are colliding with. - - - - -The Collider that you are colliding with. - - - - -The GameObject whose collider you are colliding with. - - - - -Struct that describes a collision - - - - -Checks if two GameObject references are different. - - GameObject to check. - Another GameObject to check with. - True if both Components are different. - - - -Checks if two GameObject references are the same. - - GameObject to check. - Another GameObject to check with. - True if both Components are the same. - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Retrieves the native Entity object that this GameObject represents. - - Native Entity object that this GameObject represents. - - - -Retrieves the CLR Entity object that this GameObject represents. - - Entity object that this GameObject represents. - - - -Constructor for the GameObject. - - -Managed numerical representation of the ECS Entity that this GameObject -should represent. - - - - -Constructor for the GameObject. - - -The ECS Entity that this GameObject should represent. - - - - -Removes all Scripts of the specified type from this GameObject. - - Type of PLushieScripts to remove. - - - -Retrieves a immutable list of Scripts of the specified type from this -GameObject. - - Type of Scripts to retrieve. - Immutable list of Scripts of the specified type. - - - -Retrieves a Script of the specified type from child GameObjects. -If multiple Scripts of the same specified type are added on the same -child GameObject, this will retrieve the first one added. - - Type of Script to retrieve. - Reference to the Script to retrieve. - - - -Retrieves a Script of the specified type from this GameObject. -If multiple Scripts of the same specified type are added on the same -GameObject, this will retrieve the first one added. - - Type of Script to retrieve. - Reference to the Script to retrieve. - - - -Adds a Script of the specified type to this GameObject. - - Type of Script to add. - Reference to the created Script. - - - -Removes a Component from this GameObject. If no Component exists to begin -with, nothing happens. - - Type of the Component to get. - - - -Ensures a Component on this GameObject. - - Type of the Component to ensure. - -Reference to the Component. - - - - -Retrieves the first Component from this GameObject's children that matches -the specified type. - - Type of the Component to get. - -Reference to the Component or null if neither of this GameObject's children -does not have the specified Component. - - - - -Gets a Component from this GameObject. - - Type of the Component to get. - -Reference to the Component or null if this GameObject does not have the -specified Component. - - - - -Adds a Component to this GameObject. - - Type of the Component to add. - Reference to the Component that was added. - - - -Sets the active state of this GameObject. -
-The actual "activeness" of this GameObject is still dependent on the parents' -active states. -
- -Whether to activate or deactivate this GameObject. - -
- - -Sets the name of this GameObject. - - The name to set. - - - -Native Entity ID value for this GameObject. - - - - -Whether or not this Entity is active in the Scene hierarchy. - - - - -Whether or not this Entity alone, is active. This does not mean that this -object is active in the scene. For example, if this Entity's parent is not -active, then this Entity would also be not active. - - - - -Name of the object that this Entity represents. - - - - -Retrieves a GameObject with the specified name. If there are multiple -GameObjects with the same name, the first found GameObject will be retrieved. -There is no guaranteed order of which GameObject is considered "first". - - Name of the GameObject to find. - GameObject that has the specified name. Null if not found. - - - -Destroys the specified GameObject. Note that the specified GameObject will no -longer be a valid GameObject after this function is called. - - The GameObject to be destroyed. - - - -Creates a new GameObject in the current Scene. If multiple Scenes are loaded, -and you would like to create an object in a specific Scene, call the Scene's -CreateGameObject(). - - GameObject that represents the newly created GameObject. - - - -Lightweight object for an PlushieEngine Entity that allows for easy access -to Component and Script operations. - - - - -Constructor for a Tooltip attribute that fills in the description. - - Text to be shown when a field is hovered. - - - -Maximum value for the Ranged field. - - - - -Minimum value for the Ranged field. - - - - -Simple attribute to constrain the range of values for a field on the editor. - - - - -Converts from a native std::Stringto a managed String. - - The native std::string to convert from. - Managed copy of a native std::string. - - - -Converts from a managed String to a native std::string. - - The managed String to convert from. - Native copy of a managed String. - - - -Converts from a native Vector2 to a managed Vector2. - - The native Vector2 to convert from. - Managed copy of a native Vector2. - - - -Converts from a native Quaternion to a managed Quaternion. - - The native Quaternion to convert from. - Managed copy of a native Quaternion. - - - -Converts from a managed Quaternion to a native Quaternion. - - The managed Quaternion to convert from. - Native copy of a managed Quaternion. - - - -Converts from a native Vector2 to a managed Vector2. - - The native Vector2 to convert from. - Managed copy of a native Vector2. - - - -Converts from a managed Vector2 to a native Vector2. - - The managed Vector2 to convert from. - Native copy of a managed Vector2. - - - -Converts from a native Vector3 to a managed Vector3. - - The native Vector3 to convert from. - Managed copy of a native Vector3. - - - -Converts from a managed Vector3 to a native Vector3. - - The managed Vector3 to convert from. - Native copy of a managed Vector3. - - - -Converts from a native Entity to a managed Entity (UInt32). - - Native Entity to convert from. - Managed representation of the specified Entity. - - - -Provides functions easy and consistent syntax for converting between custom -managed and native types that are aligned. - - - - -Converts to true if this is a valid Handle. - - - - -The library that the handle was issued by. - - - - -The internal ID of the handle. - - - - -Creates a ray starting at origin along direction. - - Source of the ray. - Direction the ray travels in. - - - -The direction that a ray travels in. - - - - -The start point of the ray. - - - - -CLR version of the the SHADE Engine's Ray class that represents a ray in -3-Dimensional space. - - - - -Are two quaternions equal to each other? - - Left-hand side quaternion. - Right-hand side quaternion. - - - -Combines rotations lhs and rhs. - - Left-hand side quaternion. - Right-hand side quaternion. - - - -Spherically interpolates between a and b by t. The parameter t is not clamped. - - - - -Spherically interpolates between quaternions a and b by ratio t. The parameter t is clamped to the range [0, 1]. - - Start value, returned when t = 0. - End value, returned when t = 1. - Interpolation ratio. - A quaternion spherically interpolated between quaternions a and b. - - - -Rotates a rotation from towards to.
-The from quaternion is rotated towards to by an angular step of maxDegreesDelta (but note that the rotation will not overshoot). -Negative values of maxDegreesDelta will move away from to until the rotation is exactly the opposite direction. -
-
- - -Converts this quaternion to one with the same orientation but with a magnitude of 1. - - - - -Creates a rotation with the specified forward and upwards directions.
-Z axis will be aligned with forward, X axis aligned with cross product between forward and upwards, and Y axis aligned with cross product between Z and X. -
-
- - -Interpolates between a and b by t and normalizes the result afterwards. The parameter t is not clamped. - - - - -Interpolates between a and b by t and normalizes the result afterwards. The parameter t is clamped to the range [0, 1]. - - Start value, returned when t = 0. - End value, returned when t = 1. - Interpolation ratio. - A quaternion interpolated between quaternions a and b. - - - -Returns the Inverse of rotation. - - - - -Creates a rotation which rotates from fromDirection to toDirection. - - - - -Returns a rotation that rotates y degrees around the y axis, x degrees around the x axis, and z degrees around the z axis; applied in that order. - - - - -The dot product between two rotations. - - - - -Creates a rotation which rotates angle degrees around axis. - - - - -Returns the angle in degrees between two rotations a and b.
- The angle in degrees between the two vectors. -
- - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Converts a rotation to angle-axis representation (angles in degrees). - - - - -Creates a rotation with the specified forward and upwards directions.
-The result is applied to this quaternion. -If used to orient a Transform, the Z axis will be aligned with forward and the Y axis with upwards, assuming these vectors are orthogonal. -Logs an error if the forward direction is zero. -
- The direction to look in. - The vector that defines in which direction up is. -
- - -Creates a rotation which rotates from fromDirection to toDirection.
-Use this to create a rotation which starts at the first Vector (fromDirection) and rotates to the second Vector (toDirection). -These Vectors must be set up in a script. -
-
- - -Constructor to construct a Quaternion with the specified components. - - X-coordinate to set. - Y-coordinate to set. - Z-coordinate to set. - W-coordinate to set. - - - -W-component of the Quaternion. Do not directly modify quaternions. - - - - -Z-component of the Quaternion. -Don't modify this directly unless you know quaternions inside out. - - - - -Y-component of the Quaternion. -Don't modify this directly unless you know quaternions inside out. - - - - -X-component of the Quaternion. -Don't modify this directly unless you know quaternions inside out. - - - - -Shorthand for writing Quaternion(0, 0, 0, 1). - - - - -CLR version of SHADE's Quaternion class that represents an orientation. -Designed to closely match Unity's Quaternion struct. - - - - -Explicit conversion operator to enable explicit casting from a Vector2 to a -Vector3. - - Vector2 to convert from. - - - -Explicit conversion operator to enable explicit casting from a Vector3 to a -Vector2. - - Vector3 to convert from. - - - -Checks if two Vector3s are not approximately equal. This is equivalent to -calling !Vector3.IsNear() with default tolerance values. - - Vector3 to compare. - Another Vector3 to compare. - -True if all components are not approximately equal within the default -tolerance value. - - - - -Checks if two Vector3s are approximately equal. This is equivalent to -calling Vector3.IsNear() with default tolerance values. - - Vector3 to compare. - Another Vector3 to compare. - -True if all components are approximately equal within the default -tolerance value. - - - - -Calculates the division of a Vector3 with a scalar value and returns -the result. - - Scalar to divide with. - Vector3 to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Vector3 with a scalar value and returns -the result. - - Vector3 to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the division of a Vector3 with a scalar value and returns -the result. - - Scalar to divide with. - Vector3 to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Vector3 with a scalar value and returns -the result. - - Vector3 to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the component-wise multiplication of two Vector3s and returns the -result. - - Vector3 to multiply with. - Another Vector3 to multiply with. - The result of rhs subtracted from lhs. - - - -Subtracts a Vector3 from another Vector3 and returns the result. - - Vector3 to subtract from. - Another Vector3 to subtract. - The result of rhs subtracted from lhs. - - - -Adds two Vector3s together and returns the result. - - Vector3 to add. - Another Vector3 to add. - The result of lhs added to rhs - - - -Moves a point current towards target. -Similar to Lerp(), however, the function will ensure that the distance never -exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the -vector away from target - - The current position of the point. - The target position to move to. - Maximum distance moved per call. - Vector representing the moved point. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. -Unlike Lerp(), t is not clamped to a range at all. - - The start Vector3, returned when t = 0.0f. - The end Vector3, returned when t = 1.0f. - Value used to interpolate between a and b. - The interpolated Vector3. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. - - The start Vector3, returned when t = 0.0f. - The end Vector3, returned when t = 1.0f. - -Value used to interpolate between a and b which is clamped to -the range[0, 1]. - - The interpolated Vector3. - - - -Computes and returns a Vector3 that is made from the largest components of -the two specified Vector3s. - - Vector3 to calculate maximum Vector3 with. - Another Vector3 to calculate maximum Vector3 with. - -The Vector3 that contains the largest components of the two specified -Vector3s. - - - - -Computes and returns a Vector3 that is made from the smallest components of -the two specified Vector3s. - - Vector3 to calculate minimum Vector3 with. - Another Vector3 to calculate minimum Vector3 with. - -The Vector3 that contains the smallest components of the two specified -Vector3s. - - - - -Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise -direction. - - A Vector3 to rotate. - -Angle to rotate the vector by in an anti-clockwise direction in degrees. - - The Vector3 that represents the rotated vector. - - - -Rotates a Vector3 on the Z-axis by a specified angle in an anti-clockwise -direction. - - A Vector3 to rotate. - -Angle to rotate the vector by in an anti-clockwise direction in radians. - - The Vector3 that represents the rotated vector. - - - -Reflects a Vector3 across another Vector3. - - A Vector3 to reflect. - A normal to reflect the Vector3 across. - The Vector3 that represents vec reflected across normal. - - - -Computes and returns a Vector3 projection. - - Vector3 to project. - Vector3 to project onto. - The Vector3 that represents the projected vec onto direction. - - - -Computes and returns the cross product of 2 specified Vector3s. - - Vector3 to calculate cross product with. - Another Vector3 to calculate cross product with. - The cross product of the two Vector3s. - - - -Computes and returns the dot product of 2 specified Vector3s. - - Vector3 to calculate dot product with. - Another Vector3 to calculate dot product with. - Scalar value representing the dot product of the two Vector3s. - - - -Checks if two specified Vector3s are near in value. - - Vector3 to check if is near in value. - Another Vector3 to check if is near in value. - Amount of tolerance to do the comparison with. - -True if the two Vector3s are within the tolerance value specified - - - - -Checks if two specified Vector3s are near in value. - - Vector3 to check if is near in value. - Another Vector3 to check if is near in value. - -True if the two Vector3s are within the tolerance value specified - - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Checks if a specified point is near this Vector3 that represents a point. - - The other point to check if we are near. - -The amount of tolerance before we consider these points as "near". - - -True if this Vector3 representing a point and the specified point are within -the range of the specified tolerance. False otherwise. - - - - -Checks if a specified point is near this Vector3 that represents a point with -a tolerance value of PLS_EPSILON. - - The other point to check if we are near. - -True if this Vector3 representing a point and the specified point are within -the range of the specified tolerance. False otherwise. - - - - -Calculates and returns the angle of this vector from the right vector. This -function returns values between -180.0f and 180.0f. - - Returns the angle of this vector from the right vector in degrees. - - - -Calculates and returns the angle of this vector from the right vector. This -function returns values between -Math.PI and Math.PI. - - Returns the angle of this vector from the right vector in radians. - - - -Calculates and returns the squared magnitude of this Vector3. - - Returns the squared length of this Vector3. - - - -Calculates and returns the magnitude of this Vector3. Note that this function -incurs a performance cost from the square root calculation. If you do not -need the precise magnitude, consider using GetSqrMagnitude() instead. - - Returns the length of this Vector3. - - - -Creates a copy of this Vector3 and returns a normalized version. - - -Returns a normalised copy of this Vector3. -If this Vector3 is a zero vector, a zero vector will be returned. - - - - -Normalises this current Vector3. This changes the data of this Vector3. -If you would like to get a copy, use GetNormalised() instead. -This function does nothing to a zero vector. - - - - -Conversion constructor to construct a Vector3 using a Vector2. - - - - - -Constructor to construct a Vector3 with the specified components. - - X-coordinate to set. - Y-coordinate to set. - Z-coordinate to set. - - - -Constructor to construct a Vector3 with the specified components with the -Z-component set to 0.0f. - - X-coordinate to set. - Y-coordinate to set. - - - -Constructor to construct a Vector3 with the specified components with the -Y and Z-component set to 0.0f. - - X-coordinate to set. - - - -Z-component of the Vector3. - - - - -Y-component of the Vector3. - - - - -X-component of the Vector3. - - - - -Shorthand for writing Vector3(0, 0, 0). - - - - -Shorthand for writing Vector3(0, 1, 0). - - - - -Shorthand for writing Vector3(1, 0, 0). - - - - -Shorthand for writing Vector3(float.PositiveInfinity, -float.PositiveInfinity, float.PositiveInfinity). - - - - -Shorthand for writing Vector3(1, 1, 1). - - - - -Shorthand for writing Vector3(float.NegativeInfinity, -float.NegativeInfinity, float.NegativeInfinity). - - - - -Shorthand for writing Vector3(-1, 0, 0). - - - - -Shorthand for writing Vector3(0, 0, 1). - - - - -Shorthand for writing Vector3(0, -1, 0). - - - - -Shorthand for writing Vector3(0, 0, -1). - - - - -CLR version of SHADE Engine's Vector3 class that represents a 3-Dimensional Vector. -Designed to closely match Unity's Vector3 struct. - - - - -Checks if two Vector2s are not approximately equal. This is equivalent to -calling !Vector2.IsNear() with default tolerance values. - - Vector2 to compare. - Another Vector2 to compare. - -True if all components are not approximately equal within the default -tolerance value. - - - - -Checks if two Vector2s are approximately equal. This is equivalent to -calling Vector2.IsNear() with default tolerance values. - - Vector2 to compare. - Another Vector2 to compare. - -True if all components are approximately equal within the default -tolerance value. - - - - -Calculates the division of a Vector2 with a scalar value and returns -the result. - - Scalar to divide with. - Vector2 to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Vector2 with a scalar value and returns -the result. - - Vector2 to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the division of a Vector2 with a scalar value and returns -the result. - - Scalar to divide with. - Vector2 to divide with. - The result of the scalar division. - - - -Calculates the multiplication of a Vector2 with a scalar value and returns -the result. - - Vector2 to multiply with. - Scalar to multiply with. - The result of the scalar multiplication. - - - -Calculates the component-wise multiplication of two Vector2s and returns the -result. - - Vector2 to multiply with. - Another Vector2 to multiply with. - The result of rhs subtracted from lhs. - - - -Subtracts a Vector2 from another Vector2 and returns the result. - - Vector2 to subtract from. - Another Vector2 to subtract. - The result of rhs subtracted from lhs. - - - -Adds two Vector2s together and returns the result. - - Vector2 to add. - Another Vector2 to add. - The result of lhs added to rhs - - - -Moves a point current towards target. -Similar to Lerp(), however, the function will ensure that the distance never -exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the -vector away from target - - The current position of the point. - The target position to move to. - Maximum distance moved per call. - Vector representing the moved point. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. -Unlike Lerp(), t is not clamped to a range at all. - - The start Vector2, returned when t = 0.0f. - The end Vector2, returned when t = 1.0f. - Value used to interpolate between a and b. - The interpolated Vector2. - - - -Linearly interpolates between two specified points. -This is most commonly used to find a point some fraction of the way along a -line between two endpoints. - - The start Vector2, returned when t = 0.0f. - The end Vector2, returned when t = 1.0f. - -Value used to interpolate between a and b which is clamped to -the range[0, 1]. - - The interpolated Vector2. - - - -Computes and returns a Vector2 that is made from the largest components of -the two specified Vector2s. - - Vector2 to calculate maximum Vector2 with. - Another Vector2 to calculate maximum Vector2 with. - -The Vector2 that contains the largest components of the two specified -Vector2s. - - - - -Computes and returns a Vector2 that is made from the smallest components of -the two specified Vector2s. - - Vector2 to calculate minimum Vector2 with. - Another Vector2 to calculate minimum Vector2 with. - -The Vector2 that contains the smallest components of the two specified -Vector2s. - - - - -Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise -direction. - - A Vector2 to rotate. - -Angle to rotate the vector by in an anti-clockwise direction in degrees. - - The Vector2 that represents the rotated vector. - - - -Rotates a Vector2 on the Z-axis by a specified angle in an anti-clockwise -direction. - - A Vector2 to rotate. - -Angle to rotate the vector by in an anti-clockwise direction in radians. - - The Vector2 that represents the rotated vector. - - - -Reflects a Vector2 across another Vector2. - - A Vector2 to reflect. - A normal to reflect the Vector2 across. - The Vector2 that represents vec reflected across normal. - - - -Computes and returns a Vector2 projection. - - Vector2 to project. - Vector2 to project onto. - The Vector2 that represents the projected vec onto direction. - - - -Computes a perpendicular Vector2 to the specified Vector2. - - Vector2 to find a perpendicular of. - -Whether the inward perpendicular Vector is retrieved. If true, the -resultant vector is rotated 90-degrees in a counter-clockwise. - - The perpendicular Vector2 relative to the specified Vector2. - - - - -Computes the inward perpendicular Vector2 to the specified Vector2. -Equivalent to calling Perpendicular(lhs, true). This means, the -resultant Vector2 is rotated 90-degrees in a counter-clockwise. - - Vector2 to find a perpendicular of. - -The perpendicular Vector2 relative to the specified Vector2. - - - - -Computes and returns the dot product of 2 specified Vector2s. - - Vector2 to calculate dot product with. - Another Vector2 to calculate dot product with. - -Scalar value representing the dot product of the two Vector2s. - - - - -Checks if two specified Vector2s are near in value. - - Vector2 to check if is near in value. - Another Vector2 to check if is near in value. - -Amount of tolerance to do the comparison with. - - -True if the two Vector2s are within the tolerance value specified - - - - -Checks if two specified Vector2s are near in value. - - Vector2 to check if is near in value. - Another Vector2 to check if is near in value. - -True if the two Vector2s are within the tolerance value specified - - - - -Gets a unique hash for this object. - - Unique hash for this object. - - - -Compares equality with another unboxed object. - - The unboxed object to compare with. - True if both objects are the same. - - - -Compares equality with an object of the same type. - - The object to compare with. - True if both objects are the same. - - - -Checks if a specified point is near this Vector2 that represents a point. - - The other point to check if we are near. - -The amount of tolerance before we consider these points as "near". - - -True if this Vector2 representing a point and the specified point are within -the range of the specified tolerance. False otherwise. - - - - -Checks if a specified point is near this Vector2 that represents a point with -a tolerance value of PLS_EPSILON. - - The other point to check if we are near. - -True if this Vector2 representing a point and the specified point are within -the range of the specified tolerance. False otherwise. - - - - -Calculates and returns the angle of this vector from the right vector. This -function returns values between -180.0f and 180.0f. - - Returns the angle of this vector from the right vector in degrees. - - - -Calculates and returns the angle of this vector from the right vector. This -function returns values between -Math.PI and Math.PI. - - Returns the angle of this vector from the right vector in radians. - - - -Calculates and returns the squared magnitude of this Vector2. - - Returns the squared length of this Vector2. - - - -Calculates and returns the magnitude of this Vector2. Note that this function -incurs a performance cost from the square root calculation. If you do not -need the precise magnitude, consider using GetSqrMagnitude() instead. - - Returns the length of this Vector2. - - - -Creates a copy of this Vector2 and returns a normalized version. - - -Returns a normalised copy of this Vector2. -If this Vector2 is a zero vector, a zero vector will be returned. - - - - -Normalises this current Vector2. This changes the data of this Vector2. -If you would like to get a copy, use GetNormalised() instead. -This function does nothing to a zero vector. - - - - -Constructor to construct a Vector2 with the specified components.. - - X-coordinate to set. - Y-coordinate to set. - - - -Constructor to construct a Vector2 with the specified components with the -Y-component set to 0.0f. - - X-coordinate to set. - - - -Y-component of the Vector2. - - - - -X-component of the Vector2. - - - - -Shorthand for writing Vector2(0, 0). - - - - -Shorthand for writing Vector2(0, 1). - - - - -Shorthand for writing Vector2(1, 0). - - - - -Shorthand for writing Vector2(float.PositiveInfinity, -float.PositiveInfinity). - - - - -Shorthand for writing Vector2(1, 1). - - - - -Shorthand for writing Vector2(float.NegativeInfinity, -float.NegativeInfinity). - - - - -Shorthand for writing Vector2(-1, 0). - - - - -Shorthand for writing Vector2(0, -1). - - - - -CLR version of SHADE Engine's Vector2 class that represents a 2-Dimensional Vector. -Designed to closely match Unity's Vector2 struct. - - - - -Checks if the specified entity is valid. This is done by checking if it -matches Pls::Entity::INVALID. - - The Entity to check. - True if the specified Entity is valid. - - - -Static class that contains useful utility functions for working with Entity. - - - - -Manages all resources in multiple ResourceLibraries. - - - - -Base class for SHResourceLibrary that holds information about the library type. - - - - -Template Specialization for Handle that represents a type-less Handle. - - - - -Converts to true if this is a valid Handle. - - - - -Native ID type of a handle - - - - -Base implementation of the Handle that is not templated to allow for holding -generic non-type-specific Handles. - - - - -Exception thrown when a generic Handle is being casted to the wrong type. - - - - -Exception thrown when an invalid Handle was dereferenced. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
\ No newline at end of file From 0979ee93e67e53319eaa6ad60cf0ed7a13224417 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Wed, 2 Nov 2022 22:44:27 +0800 Subject: [PATCH 059/113] TEMPORARY Push with all relative paths changed for building exe --- SHADE_Engine/src/Assets/SHAssetMacros.h | 2 +- SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp | 6 +++--- .../Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp | 6 +++--- SHADE_Engine/src/Editor/SHEditor.cpp | 12 ++++++++---- SHADE_Engine/src/Scripting/SHScriptEngine.cpp | 7 ++++++- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/SHADE_Engine/src/Assets/SHAssetMacros.h b/SHADE_Engine/src/Assets/SHAssetMacros.h index 3376a2d5..336db566 100644 --- a/SHADE_Engine/src/Assets/SHAssetMacros.h +++ b/SHADE_Engine/src/Assets/SHAssetMacros.h @@ -56,7 +56,7 @@ enum class AssetType : AssetTypeMeta constexpr size_t TYPE_COUNT{ static_cast(AssetType::MAX_COUNT) }; //Directory -#ifdef _PUBLISH +#ifndef _PUBLISH constexpr std::string_view ASSET_ROOT{ "Assets" }; constexpr std::string_view BUILT_IN_ASSET_ROOT {"Built_In"}; #else diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp index c3c7ef03..59d3f632 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp @@ -79,10 +79,10 @@ namespace SHADE //SHResourceManager::LoadAllAudio(system, soundList); - LoadBank("../../Assets/Audio/Master.bank"); - LoadBank("../../Assets/Audio/Master.strings.bank"); + LoadBank("Assets/Audio/Master.bank"); + LoadBank("Assets/Audio/Master.strings.bank"); //LoadBank("../../Assets/Audio/Music.bank"); - LoadBank("../../Assets/Audio/footsteps.bank"); + LoadBank("Assets/Audio/footsteps.bank"); //auto clip = CreateAudioClip("event:/Characters/sfx_footsteps_human"); //clip->Play(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index 06c0c2ae..1298f168 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -38,7 +38,7 @@ namespace SHADE void SHEditorMenuBar::Init() { SHEditorWindow::Init(); - constexpr std::string_view path = "../../Assets/Editor/Layouts"; + constexpr std::string_view path = "Assets/Editor/Layouts"; for(auto const& entry : std::filesystem::directory_iterator(path)) { layoutPaths.push_back(entry.path()); @@ -77,11 +77,11 @@ namespace SHADE { if(ImGui::Selectable("Save")) { - SHSerialization::SerializeSceneToFile("../../Assets/Scenes/Test.SHADE"); + SHSerialization::SerializeSceneToFile("Assets/Scenes/Test.SHADE"); } if(ImGui::Selectable("Load")) { - SHSerialization::DeserializeSceneFromFile("../../Assets/Scenes/Test.SHADE"); + SHSerialization::DeserializeSceneFromFile("Assets/Scenes/Test.SHADE"); } ImGui::EndMenu(); } diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 6974d213..9972066c 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -102,7 +102,11 @@ namespace SHADE io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //Enable for Multi-Viewports io->ConfigFlags |= ImGuiConfigFlags_DockingEnable; //Enable docking +#ifndef _PUBLISH + io->IniFilename = "Assets/Editor/Layouts/UserLayout.ini"; +#else io->IniFilename = "../../Assets/Editor/Layouts/UserLayout.ini"; +#endif io->ConfigWindowsMoveFromTitleBarOnly = true; InitLayout(); @@ -176,20 +180,20 @@ namespace SHADE { if(!std::filesystem::exists(io->IniFilename)) { - std::filesystem::copy_file("../../Assets/Editor/Layouts/Default.ini", io->IniFilename); + std::filesystem::copy_file("Assets/Editor/Layouts/Default.ini", io->IniFilename); } //eventually load preferred layout here } void SHEditor::InitFonts() noexcept { - ImFont* mainFont = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/Segoe UI.ttf", 20.f);//TODO: Change to config based assets path + ImFont* mainFont = io->Fonts->AddFontFromFileTTF("Assets/Editor/Fonts/Segoe UI.ttf", 20.f);//TODO: Change to config based assets path ImFontConfig icons_config{}; icons_config.MergeMode = true; icons_config.GlyphOffset.y = 5.f; constexpr ImWchar icon_ranges_fa[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; - ImFont* UIFontFA = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/fa-solid-900.ttf", 20.f, &icons_config, icon_ranges_fa); //TODO: Change to config based assets path + ImFont* UIFontFA = io->Fonts->AddFontFromFileTTF("Assets/Editor/Fonts/fa-solid-900.ttf", 20.f, &icons_config, icon_ranges_fa); //TODO: Change to config based assets path constexpr ImWchar icon_ranges_md[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 }; - ImFont* UIFontMD = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges_md); //TODO: Change to config based assets path + ImFont* UIFontMD = io->Fonts->AddFontFromFileTTF("Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges_md); //TODO: Change to config based assets path io->Fonts->Build(); } diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index 4a73342e..5a09b646 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -32,7 +32,11 @@ namespace SHADE /* Static Definitions */ /*----------------------------------------------------------------------------------*/ const std::string SHScriptEngine::DEFAULT_CSHARP_NAMESPACE = std::string("SHADE"); - const std::string SHScriptEngine::CSPROJ_DIR = "..\\..\\TempScriptsFolder"; + #ifndef _PUBLISH + const std::string SHScriptEngine::CSPROJ_DIR = "TempScriptsFolder"; + #else + const std::string SHScriptEngine::CSPROJ_DIR = "..\\..\\TempScriptsFolder"; + #endif const std::string SHScriptEngine::CSPROJ_PATH = std::string(CSPROJ_DIR) + "\\SHADE_Scripting.csproj"; /*-----------------------------------------------------------------------------------*/ @@ -264,6 +268,7 @@ namespace SHADE \n\ ..\\bin\\Debug\\SHADE_Managed.dll\n\ ..\\bin\\Release\\SHADE_Managed.dll\n\ + SHADE_Managed.dll\n\ \n\ \n\ ..\\bin\\Debug\\SHADE_CSharp.dll\n\ From f24346a6fdec8c8c17246a04db4564072b458a9e Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Wed, 2 Nov 2022 22:44:27 +0800 Subject: [PATCH 060/113] Revert "TEMPORARY Push with all relative paths changed for building exe" This reverts commit 0979ee93e67e53319eaa6ad60cf0ed7a13224417. --- SHADE_Engine/src/Assets/SHAssetMacros.h | 2 +- SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp | 6 +++--- .../Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp | 6 +++--- SHADE_Engine/src/Editor/SHEditor.cpp | 12 ++++-------- SHADE_Engine/src/Scripting/SHScriptEngine.cpp | 7 +------ 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/SHADE_Engine/src/Assets/SHAssetMacros.h b/SHADE_Engine/src/Assets/SHAssetMacros.h index 336db566..3376a2d5 100644 --- a/SHADE_Engine/src/Assets/SHAssetMacros.h +++ b/SHADE_Engine/src/Assets/SHAssetMacros.h @@ -56,7 +56,7 @@ enum class AssetType : AssetTypeMeta constexpr size_t TYPE_COUNT{ static_cast(AssetType::MAX_COUNT) }; //Directory -#ifndef _PUBLISH +#ifdef _PUBLISH constexpr std::string_view ASSET_ROOT{ "Assets" }; constexpr std::string_view BUILT_IN_ASSET_ROOT {"Built_In"}; #else diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp index 59d3f632..c3c7ef03 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp @@ -79,10 +79,10 @@ namespace SHADE //SHResourceManager::LoadAllAudio(system, soundList); - LoadBank("Assets/Audio/Master.bank"); - LoadBank("Assets/Audio/Master.strings.bank"); + LoadBank("../../Assets/Audio/Master.bank"); + LoadBank("../../Assets/Audio/Master.strings.bank"); //LoadBank("../../Assets/Audio/Music.bank"); - LoadBank("Assets/Audio/footsteps.bank"); + LoadBank("../../Assets/Audio/footsteps.bank"); //auto clip = CreateAudioClip("event:/Characters/sfx_footsteps_human"); //clip->Play(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index 1298f168..06c0c2ae 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -38,7 +38,7 @@ namespace SHADE void SHEditorMenuBar::Init() { SHEditorWindow::Init(); - constexpr std::string_view path = "Assets/Editor/Layouts"; + constexpr std::string_view path = "../../Assets/Editor/Layouts"; for(auto const& entry : std::filesystem::directory_iterator(path)) { layoutPaths.push_back(entry.path()); @@ -77,11 +77,11 @@ namespace SHADE { if(ImGui::Selectable("Save")) { - SHSerialization::SerializeSceneToFile("Assets/Scenes/Test.SHADE"); + SHSerialization::SerializeSceneToFile("../../Assets/Scenes/Test.SHADE"); } if(ImGui::Selectable("Load")) { - SHSerialization::DeserializeSceneFromFile("Assets/Scenes/Test.SHADE"); + SHSerialization::DeserializeSceneFromFile("../../Assets/Scenes/Test.SHADE"); } ImGui::EndMenu(); } diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 9972066c..6974d213 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -102,11 +102,7 @@ namespace SHADE io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //Enable for Multi-Viewports io->ConfigFlags |= ImGuiConfigFlags_DockingEnable; //Enable docking -#ifndef _PUBLISH - io->IniFilename = "Assets/Editor/Layouts/UserLayout.ini"; -#else io->IniFilename = "../../Assets/Editor/Layouts/UserLayout.ini"; -#endif io->ConfigWindowsMoveFromTitleBarOnly = true; InitLayout(); @@ -180,20 +176,20 @@ namespace SHADE { if(!std::filesystem::exists(io->IniFilename)) { - std::filesystem::copy_file("Assets/Editor/Layouts/Default.ini", io->IniFilename); + std::filesystem::copy_file("../../Assets/Editor/Layouts/Default.ini", io->IniFilename); } //eventually load preferred layout here } void SHEditor::InitFonts() noexcept { - ImFont* mainFont = io->Fonts->AddFontFromFileTTF("Assets/Editor/Fonts/Segoe UI.ttf", 20.f);//TODO: Change to config based assets path + ImFont* mainFont = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/Segoe UI.ttf", 20.f);//TODO: Change to config based assets path ImFontConfig icons_config{}; icons_config.MergeMode = true; icons_config.GlyphOffset.y = 5.f; constexpr ImWchar icon_ranges_fa[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; - ImFont* UIFontFA = io->Fonts->AddFontFromFileTTF("Assets/Editor/Fonts/fa-solid-900.ttf", 20.f, &icons_config, icon_ranges_fa); //TODO: Change to config based assets path + ImFont* UIFontFA = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/fa-solid-900.ttf", 20.f, &icons_config, icon_ranges_fa); //TODO: Change to config based assets path constexpr ImWchar icon_ranges_md[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 }; - ImFont* UIFontMD = io->Fonts->AddFontFromFileTTF("Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges_md); //TODO: Change to config based assets path + ImFont* UIFontMD = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges_md); //TODO: Change to config based assets path io->Fonts->Build(); } diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index 5a09b646..4a73342e 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -32,11 +32,7 @@ namespace SHADE /* Static Definitions */ /*----------------------------------------------------------------------------------*/ const std::string SHScriptEngine::DEFAULT_CSHARP_NAMESPACE = std::string("SHADE"); - #ifndef _PUBLISH - const std::string SHScriptEngine::CSPROJ_DIR = "TempScriptsFolder"; - #else - const std::string SHScriptEngine::CSPROJ_DIR = "..\\..\\TempScriptsFolder"; - #endif + const std::string SHScriptEngine::CSPROJ_DIR = "..\\..\\TempScriptsFolder"; const std::string SHScriptEngine::CSPROJ_PATH = std::string(CSPROJ_DIR) + "\\SHADE_Scripting.csproj"; /*-----------------------------------------------------------------------------------*/ @@ -268,7 +264,6 @@ namespace SHADE \n\ ..\\bin\\Debug\\SHADE_Managed.dll\n\ ..\\bin\\Release\\SHADE_Managed.dll\n\ - SHADE_Managed.dll\n\ \n\ \n\ ..\\bin\\Debug\\SHADE_CSharp.dll\n\ From 5fe10ce8d1d16cdc0c14982c4ac22df7c660658e Mon Sep 17 00:00:00 2001 From: Glence Date: Thu, 3 Nov 2022 01:50:02 +0800 Subject: [PATCH 061/113] Changes to main for presentation Change TestCube_FS.glsl Change debugdraw Key input to F10 TestScene changes AI waypoint changed playercontroller camera axis change --- Assets/Shaders/TestCube_FS.glsl | 2 +- Assets/Shaders/TestCube_FS.shshaderb | Bin 2285 -> 2401 bytes .../src/Application/SBApplication.cpp | 2 +- SHADE_Application/src/Scenes/SBTestScene.cpp | 145 +++++++++++------- TempScriptsFolder/AIPrototype.cs | 2 +- TempScriptsFolder/PickAndThrow.cs | 4 + TempScriptsFolder/PlayerController.cs | 11 +- TempScriptsFolder/ThirdPersonCamera.cs | 2 +- 8 files changed, 111 insertions(+), 57 deletions(-) diff --git a/Assets/Shaders/TestCube_FS.glsl b/Assets/Shaders/TestCube_FS.glsl index 093cc9c6..1d2b156d 100644 --- a/Assets/Shaders/TestCube_FS.glsl +++ b/Assets/Shaders/TestCube_FS.glsl @@ -43,7 +43,7 @@ void main() { position = In.vertPos; normals = In.normal; - albedo = texture(textures[nonuniformEXT(MatProp.data[In2.materialIndex].textureIndex)], In.uv); + albedo = texture(textures[nonuniformEXT(MatProp.data[In2.materialIndex].textureIndex)], In.uv) + MatProp.data[In2.materialIndex].color; outEntityID = In2.eid; lightLayerIndices = In2.lightLayerIndex; diff --git a/Assets/Shaders/TestCube_FS.shshaderb b/Assets/Shaders/TestCube_FS.shshaderb index fcb72b6e912e98ddd75e1e9eb888bad2282d0f97..01bf616394dc1f3cad2a9274e76a1966e373ca4b 100644 GIT binary patch delta 383 zcmYk1O-{mK5Qe8Mq|pQfg193@5>4DIr2_Ti&#-2L@dR#kQx>j_;RJ+xkYG>b0XTvQ z&r4`xlP5Ft&dfJ&_toE8qp~=&;}`ht(9E-QE7^G6*cAK7Zns_em&!ht>Eu0qOFw60 zD_UqSn+C65Rjnp?@N5O-pTgXqM3g@8qP38F!~ojxc&fE3P5l8;PyFw@EDjUz%W>|O z)&tzY1@u`d6FVT?aY!e-a1EMKFQ1i-7}Wnh+LsNp`;$Jv$Q8LX-IJXJxc6drBswT> ePoRbtsa_O3PMAd)%X#@uWq9CjQ delta 266 zcmaDT^j1(z;0Xr<7%(#_vokO-GH@~QGT2QNo6Kqs6t>>Du9-;`#L6!%am_2qEUEN# zVPIuoXRzMv#vIKk2$GTmVn!f#XJ7#eZ(<2%GBpEonSodth^>LbAU;SbNK669w*b;0 zEy(iLo4wfW8B6Sdio}6ZraP3JUrrs9F28n~zTZ6?J82$nsVFAPdt%(~P diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index 63276d04..c43ca45d 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -154,7 +154,7 @@ namespace Sandbox editor->PollPicking(); static bool drawColliders = false; - if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::SPACE)) + if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::F10)) { drawColliders = !drawColliders; SHSystemManager::GetSystem()->SetDrawColliders(drawColliders); diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index a5b3b546..b840bed3 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -65,58 +65,20 @@ namespace Sandbox SHResourceManager::FinaliseChanges(); // Create Materials - auto matInst = graphicsSystem->AddOrGetBaseMaterialInstance(); - auto customMat = graphicsSystem->AddMaterialInstanceCopy(matInst); - customMat->SetProperty("data.color", SHVec4(0.0f, 1.0f, 1.0f, 1.0f)); - customMat->SetProperty("data.textureIndex", 0); - customMat->SetProperty("data.alpha", 0.1f); + auto baseRaccoonMat = graphicsSystem->AddOrGetBaseMaterialInstance(); + auto baseRaccoonMatInstant = graphicsSystem->AddMaterialInstanceCopy(baseRaccoonMat); + baseRaccoonMatInstant->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 1.0f)); + baseRaccoonMatInstant->SetProperty("data.textureIndex", 0); + baseRaccoonMatInstant->SetProperty("data.alpha", 0.1f); - // Create Stress Test Objects - static const SHVec3 TEST_OBJ_SCALE = SHVec3::One; - constexpr int NUM_ROWS = 3; - constexpr int NUM_COLS = 1; - static const SHVec3 TEST_OBJ_SPACING = { 0.1f, 0.1f, 0.1f }; - static const SHVec3 TEST_OBJ_START_POS = { -(NUM_COLS / 2 * TEST_OBJ_SPACING.x) + 1.0f, -2.0f, -1.0f }; + auto baseFloorMatInstant = graphicsSystem->AddMaterialInstanceCopy(baseRaccoonMat); + baseFloorMatInstant->SetProperty("data.color", SHVec4(1.0f, 1.0f, 1.0f, 1.0f)); + baseFloorMatInstant->SetProperty("data.textureIndex", 0); + baseFloorMatInstant->SetProperty("data.alpha", 0.1f); - for (int y = 0; y < NUM_ROWS; ++y) - for (int x = 0; x < NUM_COLS; ++x) - { - auto entity = SHEntityManager::CreateEntity(); - auto& renderable = *SHComponentManager::GetComponent_s(entity); - auto& transform = *SHComponentManager::GetComponent_s(entity); - auto& collider = *SHComponentManager::GetComponent_s(entity); + auto dummy = SHEntityManager::CreateEntity<>(); - //renderable.Mesh = handles.front(); - renderable.SetMesh(CUBE_MESH); - renderable.SetMaterial(customMat); - - if (y == 50) - renderable.GetModifiableMaterial()->SetProperty("data.color", SHVec4(1.0f, 0.0f, 0.0f, 1.0f)); - - //Set initial positions - transform.SetWorldPosition(TEST_OBJ_START_POS + SHVec3{ x * TEST_OBJ_SPACING.x, y * TEST_OBJ_SPACING.y, SHMath::GenerateRandomNumber(-3.5f, -5.0f) }); - //transform.SetWorldPosition({-1.0f, -1.0f, -1.0f}); - transform.SetWorldRotation(SHMath::GenerateRandomNumber(0.0f, 360.0f), SHMath::GenerateRandomNumber(0.0f, 360.0f), SHMath::GenerateRandomNumber(0.0f, 360.0f)); - transform.SetWorldScale(TEST_OBJ_SCALE); - - collider.AddBoundingBox(SHVec3::One, SHVec3::Zero); - stressTestObjects.emplace_back(entity); - } - - auto raccoonSpin = SHEntityManager::CreateEntity(); - auto& renderable = *SHComponentManager::GetComponent_s(raccoonSpin); - auto& transform = *SHComponentManager::GetComponent_s(raccoonSpin); - - renderable.SetMesh(handles.front()); - renderable.SetMaterial(customMat); - renderable.GetModifiableMaterial()->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 0.0f)); - renderable.GetModifiableMaterial()->SetProperty("data.alpha", 1.0f); - renderable.GetModifiableMaterial()->SetProperty("data.textureIndex", 0); - - transform.SetWorldPosition({ -3.0f, -1.0f, -1.0f }); - transform.SetLocalScale({ 5.0f, 5.0f, 5.0f }); - - auto floor = SHEntityManager::CreateEntity(); + auto floor = SHEntityManager::CreateEntity(); auto& floorRenderable = *SHComponentManager::GetComponent_s(floor); auto& floorTransform = *SHComponentManager::GetComponent_s(floor); auto& floorRigidBody = *SHComponentManager::GetComponent_s(floor); @@ -125,7 +87,7 @@ namespace Sandbox floorRenderable.SetMesh(CUBE_MESH); floorRenderable.SetMaterial(graphicsSystem->GetDefaultMaterialInstance()); - floorTransform.SetWorldScale({ 7.5f, 0.5f, 7.5 }); + floorTransform.SetWorldScale({ 17.5f, 0.5f, 17.5f }); floorTransform.SetWorldPosition({ 0.0f, -3.0f, -5.0f }); floorRigidBody.SetType(SHRigidBodyComponent::Type::STATIC); @@ -138,16 +100,95 @@ namespace Sandbox //testObjRenderable.Mesh = CUBE_MESH; //testObjRenderable.SetMaterial(matInst); + //raccoon ======================================================================================================================= + auto racoon = SHEntityManager::CreateEntity(MAX_EID, "Player"); + auto& racoonRenderable = *SHComponentManager::GetComponent_s(racoon); + auto& racoonTransform = *SHComponentManager::GetComponent_s(racoon); + auto& racoonRigidBody = *SHComponentManager::GetComponent_s(racoon); + auto& racoonCollider = *SHComponentManager::GetComponent_s(racoon); + + racoonRenderable.SetMesh(handles.front()); + racoonRenderable.SetMaterial(baseRaccoonMatInstant); + + racoonTransform.SetWorldScale({ 2.0f, 2.0f, 2.0f }); + racoonTransform.SetWorldPosition({ -3.0f, -2.0f, -5.0f }); + + racoonCollider.AddBoundingBox(); + racoonCollider.GetCollider(0).SetPositionOffset(SHVec3(0.0f,0.5f,0.0f)); + racoonCollider.GetCollider(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + + auto racoonItemLocation = SHEntityManager::CreateEntity(); + auto& racoonItemLocationTransform = *SHComponentManager::GetComponent_s(racoonItemLocation); + SHSceneManager::GetCurrentSceneGraph().SetParent(racoonItemLocation, racoon); + + auto racoonCamera = SHEntityManager::CreateEntity(); + SHSceneManager::GetCurrentSceneGraph().SetParent(racoonCamera, racoon); + //================================================================================================================================ + + //item =========================================================================================================================== + auto item = SHEntityManager::CreateEntity(MAX_EID, "item"); + auto& itemRenderable = *SHComponentManager::GetComponent_s(item); + auto& itemTransform = *SHComponentManager::GetComponent_s(item); + auto& itemRigidBody = *SHComponentManager::GetComponent_s(item); + auto& itemCollider = *SHComponentManager::GetComponent_s(item); + + itemRenderable.SetMesh(handles.front()); + itemRenderable.SetMaterial(baseRaccoonMatInstant); + + 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.GetCollider(1).SetIsTrigger(true); + + itemCollider.GetCollider(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + itemCollider.GetCollider(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + + itemCollider.GetCollider(1).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + itemCollider.GetCollider(1).SetBoundingBox(SHVec3(1.0f, 1.0f, 1.0f)); + + itemRigidBody.SetInterpolate(false); + itemRigidBody.SetFreezeRotationX(true); + itemRigidBody.SetFreezeRotationY(true); + itemRigidBody.SetFreezeRotationZ(true); + //================================================================================================================================ + + //AI ============================================================================================================================= + auto AI = SHEntityManager::CreateEntity(MAX_EID, "AI"); + auto& AIRenderable = *SHComponentManager::GetComponent_s(AI); + auto& AITransform = *SHComponentManager::GetComponent_s(AI); + auto& AIRigidBody = *SHComponentManager::GetComponent_s(AI); + auto& AICollider = *SHComponentManager::GetComponent_s(AI); + + AIRenderable.SetMesh(handles.front()); + AIRenderable.SetMaterial(baseRaccoonMatInstant); + + AITransform.SetWorldScale({ 2.0f, 2.0f, 2.0f }); + AITransform.SetWorldPosition({ -8.0f, -2.0f, 2.5f }); + + AICollider.AddBoundingBox(); + AICollider.GetCollider(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); + AICollider.GetCollider(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + + AIRigidBody.SetInterpolate(false); + AIRigidBody.SetFreezeRotationX(true); + AIRigidBody.SetFreezeRotationY(true); + AIRigidBody.SetFreezeRotationZ(true); + //================================================================================================================================ SHADE::SHScriptEngine* scriptEngine = static_cast(SHADE::SHSystemManager::GetSystem()); - scriptEngine->AddScript(raccoonSpin, "RaccoonSpin"); + scriptEngine->AddScript(racoon, "PlayerController"); + scriptEngine->AddScript(racoon, "PickAndThrow"); + scriptEngine->AddScript(racoonCamera, "ThirdPersonCamera"); + scriptEngine->AddScript(AI, "AIPrototype"); auto raccoonShowcase = SHEntityManager::CreateEntity(); auto& renderableShowcase = *SHComponentManager::GetComponent_s(raccoonShowcase); auto& transformShowcase = *SHComponentManager::GetComponent_s(raccoonShowcase); renderableShowcase.SetMesh(handles.front()); - renderableShowcase.SetMaterial(customMat); + renderableShowcase.SetMaterial(baseRaccoonMatInstant); renderableShowcase.GetModifiableMaterial()->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 0.0f)); renderableShowcase.GetModifiableMaterial()->SetProperty("data.alpha", 1.0f); renderableShowcase.GetModifiableMaterial()->SetProperty("data.textureIndex", 0); diff --git a/TempScriptsFolder/AIPrototype.cs b/TempScriptsFolder/AIPrototype.cs index 88a319e3..80fa643e 100644 --- a/TempScriptsFolder/AIPrototype.cs +++ b/TempScriptsFolder/AIPrototype.cs @@ -17,7 +17,7 @@ public class AIPrototype : Script [Tooltip("The list of waypoints that the object will move around on")] private Vector3[] waypoints;*/ - private Vector3[] waypoints = { new Vector3(2.0f, -2.0f, -2.8f), new Vector3(-2.0f, -2.0f, -2.8f), new Vector3(-2.0f, -2.0f, -7.0f), new Vector3(2.0f, -2.0f, -7.0f) }; + private Vector3[] waypoints = { new Vector3(-8.0f, -2.0f, 3.5f), new Vector3(-8.0f, -2.0f, -13.0f), new Vector3(8.0f, -2.0f, -13.0f), new Vector3(8.0f, -2.0f, 3.5f) }; [SerializeField] [Tooltip("How much force is applied in movement")] diff --git a/TempScriptsFolder/PickAndThrow.cs b/TempScriptsFolder/PickAndThrow.cs index 09252247..80fcd61a 100644 --- a/TempScriptsFolder/PickAndThrow.cs +++ b/TempScriptsFolder/PickAndThrow.cs @@ -21,9 +21,13 @@ public class PickAndThrow : Script raccoonHoldLocation = GetComponentInChildren(); if (raccoonHoldLocation == null) Debug.Log("CHILD EMPTY"); + else + raccoonHoldLocation.LocalPosition = new Vector3(0.0f, 1.0f, 0.0f); + itemTransform = item.GetComponent(); if (itemTransform == null) Debug.Log("Item transform EMPTY"); + itemRidibody = item.GetComponent(); if (itemRidibody == null) Debug.Log("Item rb EMPTY"); diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 5566c411..77a4b9d3 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -44,6 +44,8 @@ public class PlayerController : Script public float xAxisMove { get; set; } public float zAxisMove { get; set; } + + public Vector2 axisMove { get; set; } public bool isMoveKeyPress { get; set; } @@ -84,6 +86,7 @@ public class PlayerController : Script rb.FreezeRotationY = true; rb.FreezeRotationZ = true; rb.Drag = drag; + rb.Interpolating = false; } //Transform check @@ -148,6 +151,7 @@ public class PlayerController : Script xAxisMove = 0; zAxisMove = 0; + axisMove = Vector2.Zero; if (Input.GetKey(Input.KeyCode.W)) { Vector3 camerAixs = cam.GetForward(); @@ -155,6 +159,7 @@ public class PlayerController : Script camerAixs.Normalise(); xAxisMove = camerAixs.x; zAxisMove = camerAixs.z; + axisMove += new Vector2(camerAixs.x, camerAixs.z); } if (Input.GetKey(Input.KeyCode.S)) { @@ -163,6 +168,7 @@ public class PlayerController : Script camerAixs.Normalise(); xAxisMove = -camerAixs.x; zAxisMove = -camerAixs.z; + axisMove -= new Vector2(camerAixs.x, camerAixs.z); } if (Input.GetKey(Input.KeyCode.A)) { @@ -171,6 +177,7 @@ public class PlayerController : Script camerAixs.Normalise(); xAxisMove = -camerAixs.x; zAxisMove = -camerAixs.z; + axisMove -= new Vector2(camerAixs.x, camerAixs.z); } if (Input.GetKey(Input.KeyCode.D)) { @@ -179,7 +186,9 @@ public class PlayerController : Script camerAixs.Normalise(); xAxisMove = camerAixs.x; zAxisMove = camerAixs.z; + axisMove += new Vector2(camerAixs.x, camerAixs.z); } + axisMove.Normalise(); isMoveKeyPress = xAxisMove != 0 || zAxisMove != 0; if(isMoveKeyPress && currentState != RaccoonStates.RUNNING && isGrounded) @@ -193,7 +202,7 @@ public class PlayerController : Script { if (rb != null) { - rb.AddForce(new Vector3(moveForce * xAxisMove, 0.0f, moveForce * zAxisMove)); + rb.AddForce(new Vector3(moveForce * axisMove.x, 0.0f, moveForce * axisMove.y)); if (isMoveKeyPress) { diff --git a/TempScriptsFolder/ThirdPersonCamera.cs b/TempScriptsFolder/ThirdPersonCamera.cs index 618c562f..141865e8 100644 --- a/TempScriptsFolder/ThirdPersonCamera.cs +++ b/TempScriptsFolder/ThirdPersonCamera.cs @@ -11,7 +11,7 @@ namespace SHADE_Scripting public class ThirdPersonCamera: Script { - public float armLength = 4.0f; + public float armLength = 2.0f; public float turnSpeedPitch = 0.3f; public float turnSpeedYaw = 0.5f; public float pitchClamp = 45.0f; From 544716547e630a46e0eb60e476cb2bd99684e0e8 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Thu, 3 Nov 2022 02:37:38 +0800 Subject: [PATCH 062/113] Added preprocessor check for debug to turn off renderdoc --- .../src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 6234941c..be67f1b3 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -52,7 +52,11 @@ namespace SHADE /* BACKEND BOILERPLATE */ /*-----------------------------------------------------------------------*/ // Set Up Instance + #ifdef _DEBUG SHVkInstance::Init(true, true, true); + #else + SHVkInstance::Init(true, false, true); + #endif // Get Physical Device and Construct Logical Device physicalDevice = SHVkInstance::CreatePhysicalDevice(SH_PHYSICAL_DEVICE_TYPE::BEST); From ecd1efee5642c42b29317107b77ad4a54d4ceb04 Mon Sep 17 00:00:00 2001 From: mushgunAX Date: Thu, 3 Nov 2022 21:25:43 +0800 Subject: [PATCH 063/113] AI Capture Mechanic --- TempScriptsFolder/AIPrototype.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/TempScriptsFolder/AIPrototype.cs b/TempScriptsFolder/AIPrototype.cs index 80fa643e..d678de78 100644 --- a/TempScriptsFolder/AIPrototype.cs +++ b/TempScriptsFolder/AIPrototype.cs @@ -25,15 +25,19 @@ public class AIPrototype : Script [SerializeField] [Tooltip("How fast the object moves about waypoints")] - private float patrolSpeed = 1.0f; + private float patrolSpeed = 0.4f; [SerializeField] [Tooltip("How fast the object moves while chasing")] - private float chaseSpeed = 1.5f; + private float chaseSpeed = 0.8f; + + [SerializeField] + [Tooltip("How near the player must be to the AI for capture")] + private float distanceToCapture = 1.2f; [SerializeField] [Tooltip("How near the player must be for the chase to begin. Should be less than distanceToEndChase")] - private float distanceToStartChase = 1.5f; + private float distanceToStartChase = 2.0f; [SerializeField] [Tooltip("How far the player must be for the chase to end. Should be greater than distanceToStartChase")] @@ -151,6 +155,12 @@ public class AIPrototype : Script //TODO delete this when original intended code above works with velocity being limited correctly rb.LinearVelocity = normalisedDifference * chaseSpeed; + //Capture player if near enough + if ((pTransform.GlobalPosition - transform.GlobalPosition).GetMagnitude() <= distanceToCapture) + { + player.GetValueOrDefault().GetScript().currentState = PlayerController.RaccoonStates.CAUGHT; + } + //End chase if too far if ((pTransform.GlobalPosition - transform.GlobalPosition).GetMagnitude() >= distanceToEndChase) { From eea35600d3092bcb3564f53fd95846172fd837e8 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 4 Nov 2022 14:04:59 +0800 Subject: [PATCH 064/113] Switched shaders to use multiply colour instead of additive --- Assets/Shaders/TestCube_FS.glsl | 2 +- Assets/Shaders/TestCube_FS.shshaderb | Bin 2401 -> 2401 bytes SHADE_Application/src/Scenes/SBTestScene.cpp | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Assets/Shaders/TestCube_FS.glsl b/Assets/Shaders/TestCube_FS.glsl index 1d2b156d..d6f88687 100644 --- a/Assets/Shaders/TestCube_FS.glsl +++ b/Assets/Shaders/TestCube_FS.glsl @@ -43,7 +43,7 @@ void main() { position = In.vertPos; normals = In.normal; - albedo = texture(textures[nonuniformEXT(MatProp.data[In2.materialIndex].textureIndex)], In.uv) + MatProp.data[In2.materialIndex].color; + albedo = texture(textures[nonuniformEXT(MatProp.data[In2.materialIndex].textureIndex)], In.uv) * MatProp.data[In2.materialIndex].color; outEntityID = In2.eid; lightLayerIndices = In2.lightLayerIndex; diff --git a/Assets/Shaders/TestCube_FS.shshaderb b/Assets/Shaders/TestCube_FS.shshaderb index 01bf616394dc1f3cad2a9274e76a1966e373ca4b..bcf5bf5ec1c14753c2ee4942289ea98261360a16 100644 GIT binary patch delta 13 UcmaDT^iXKSO%BG^$+tM#0W0wZ`Tzg` delta 13 UcmaDT^iXKSO%BG!$+tM#0V~}F_5c6? diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index b840bed3..fa5d7868 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -67,7 +67,7 @@ namespace Sandbox // Create Materials auto baseRaccoonMat = graphicsSystem->AddOrGetBaseMaterialInstance(); auto baseRaccoonMatInstant = graphicsSystem->AddMaterialInstanceCopy(baseRaccoonMat); - baseRaccoonMatInstant->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 1.0f)); + baseRaccoonMatInstant->SetProperty("data.color", SHVec4(1.0f, 1.0f, 1.0f, 1.0f)); baseRaccoonMatInstant->SetProperty("data.textureIndex", 0); baseRaccoonMatInstant->SetProperty("data.alpha", 0.1f); @@ -189,7 +189,7 @@ namespace Sandbox renderableShowcase.SetMesh(handles.front()); renderableShowcase.SetMaterial(baseRaccoonMatInstant); - renderableShowcase.GetModifiableMaterial()->SetProperty("data.color", SHVec4(0.0f, 0.0f, 0.0f, 0.0f)); + renderableShowcase.GetModifiableMaterial()->SetProperty("data.color", SHVec4(1.0f, 1.0f, 1.0f, 1.0f)); renderableShowcase.GetModifiableMaterial()->SetProperty("data.alpha", 1.0f); renderableShowcase.GetModifiableMaterial()->SetProperty("data.textureIndex", 0); From 4ee02949776f48fc3e983a874b928d3b36c5e616 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Fri, 4 Nov 2022 14:44:18 +0800 Subject: [PATCH 065/113] Scene loads correctly now --- Assets/Application.SHConfig | 4 + Assets/Materials/TestMat.shmat | 2 +- Assets/Materials/WhiteMat.shmat | 8 + Assets/Materials/WhiteMat.shmat.shmeta | 3 + Assets/Scenes/M2Scene.shade | 216 ++++++++++++++++++ Assets/Scenes/M2Scene.shade.shmeta | 3 + .../src/Application/SBApplication.cpp | 15 +- SHADE_Application/src/Scenes/SBMainScene.cpp | 62 +++++ SHADE_Application/src/Scenes/SBMainScene.h | 30 +++ .../EditorWindow/MenuBar/SHEditorMenuBar.cpp | 56 ++++- SHADE_Engine/src/Editor/SHEditor.cpp | 122 +++++++++- SHADE_Engine/src/Editor/SHEditor.h | 26 ++- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 10 +- .../src/Graphics/Windowing/SHWindow.cpp | 24 +- .../src/Graphics/Windowing/SHWindow.h | 15 +- .../src/Resource/SHResourceManager.hpp | 3 + SHADE_Engine/src/Scene/SHScene.h | 2 + SHADE_Engine/src/Scene/SHSceneManager.cpp | 28 ++- SHADE_Engine/src/Scene/SHSceneManager.h | 23 +- .../Configurations/SHConfigurationManager.cpp | 89 ++++++++ .../Configurations/SHConfigurationManager.h | 44 ++++ .../src/Serialization/SHSerialization.cpp | 54 ++--- .../src/Serialization/SHSerialization.h | 8 +- .../Serialization/SHSerializationHelper.hpp | 42 ++-- SHADE_Engine/src/Tools/FileIO/SHFieIO.cpp | 31 +++ SHADE_Engine/src/Tools/FileIO/SHFileIO.h | 14 ++ SHADE_Managed/premake5.lua | 1 + 27 files changed, 834 insertions(+), 101 deletions(-) create mode 100644 Assets/Application.SHConfig create mode 100644 Assets/Materials/WhiteMat.shmat create mode 100644 Assets/Materials/WhiteMat.shmat.shmeta create mode 100644 Assets/Scenes/M2Scene.shade create mode 100644 Assets/Scenes/M2Scene.shade.shmeta create mode 100644 SHADE_Application/src/Scenes/SBMainScene.cpp create mode 100644 SHADE_Application/src/Scenes/SBMainScene.h create mode 100644 SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.cpp create mode 100644 SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.h create mode 100644 SHADE_Engine/src/Tools/FileIO/SHFieIO.cpp create mode 100644 SHADE_Engine/src/Tools/FileIO/SHFileIO.h diff --git a/Assets/Application.SHConfig b/Assets/Application.SHConfig new file mode 100644 index 00000000..3f7a4ac0 --- /dev/null +++ b/Assets/Application.SHConfig @@ -0,0 +1,4 @@ +Start in Fullscreen: false +Starting Scene ID: 94283040 +Window Size: {x: 1920, y: 1080} +Window Title: SHADE Engine \ No newline at end of file diff --git a/Assets/Materials/TestMat.shmat b/Assets/Materials/TestMat.shmat index 089576f3..acfa2dc3 100644 --- a/Assets/Materials/TestMat.shmat +++ b/Assets/Materials/TestMat.shmat @@ -2,7 +2,7 @@ FragmentShader: 46377769 SubPass: G-Buffer Write Properties: - data.color: {x: 1, y: 0.200000003, z: 0.100000001, w: 1} + data.color: {x: 0, y: 0, z: 0, w: 1} data.textureIndex: 64651793 data.alpha: 0 data.beta: {x: 1, y: 1, z: 1} \ No newline at end of file diff --git a/Assets/Materials/WhiteMat.shmat b/Assets/Materials/WhiteMat.shmat new file mode 100644 index 00000000..5a1cb199 --- /dev/null +++ b/Assets/Materials/WhiteMat.shmat @@ -0,0 +1,8 @@ +- VertexShader: 39210065 + 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/WhiteMat.shmat.shmeta b/Assets/Materials/WhiteMat.shmat.shmeta new file mode 100644 index 00000000..588afba4 --- /dev/null +++ b/Assets/Materials/WhiteMat.shmat.shmeta @@ -0,0 +1,3 @@ +Name: WhiteMat +ID: 124370424 +Type: 7 diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade new file mode 100644 index 00000000..39caeeaa --- /dev/null +++ b/Assets/Scenes/M2Scene.shade @@ -0,0 +1,216 @@ +- EID: 0 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Camera Component: + Position: {x: 0, y: 0, z: 0} + Pitch: 0 + Yaw: 0 + Roll: 0 + Width: 1920 + Height: 1080 + Near: 0.00999999978 + Far: 10000 + Perspective: true + Light Component: + Position: {x: 0, y: 0, z: 0} + Type: Directional + Direction: {x: 1.79999995, y: 0, z: 1} + Color: {x: 0.951541841, y: 0.921719015, z: 0.553319454, w: 1} + Layer: 4294967295 + Strength: 0 + Scripts: ~ +- EID: 1 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: -1.440328, y: -4.41369677, z: -5} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 49.4798889, y: 0.5, z: 17.5} + Renderable Component: + Mesh: 80365422 + Material: 126974645 + RigidBody Component: + Type: Static + Mass: 1 + Drag: 0 + Angular Drag: 0 + Use Gravity: true + Interpolate: 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 + Collider Component: + Colliders: + - Is Trigger: false + Type: Box + Half Extents: {x: 0.0170898438, y: 0.00048828125, z: 0.0170898438} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} + Scripts: ~ +- EID: 2 + Name: Player + IsActive: true + NumberOfChildren: 2 + Components: + Transform Component: + Translate: {x: -3.06177855, y: -2, z: -5} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 2, y: 2, z: 2} + Renderable Component: + Mesh: 80365422 + Material: 126974645 + RigidBody Component: + Type: Dynamic + Mass: 1 + Drag: 0 + Angular Drag: 0 + Use Gravity: true + Interpolate: 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 + Collider Component: + Colliders: + - Is Trigger: false + Type: Box + Half Extents: {x: 0.0009765625, y: 0.0009765625, z: 0.0009765625} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0.5, z: 0} + Scripts: ~ +- EID: 3 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: -0.0094268322, y: 0, z: 0} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 1, y: 1, z: 1} + Scripts: ~ +- EID: 4 + 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} + Scripts: ~ +- EID: 5 + Name: item + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: -2, z: -5} + Rotate: {x: 0, y: 0, z: 0} + Scale: {x: 2, y: 2, z: 2} + Renderable Component: + Mesh: 80365422 + Material: 124370424 + RigidBody Component: + Type: Dynamic + Mass: 1 + Drag: 0 + Angular Drag: 0 + Use Gravity: true + Interpolate: false + Freeze Position X: false + Freeze Position Y: false + Freeze Position Z: false + Freeze Rotation X: true + Freeze Rotation Y: true + Freeze Rotation Z: true + Collider Component: + Colliders: + - Is Trigger: false + Type: Box + Half Extents: {x: 0.0009765625, y: 0.0009765625, z: 0.0009765625} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0.5, z: 0} + - Is Trigger: true + Type: Box + Half Extents: {x: 0.001953125, y: 0.001953125, z: 0.001953125} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0.5, z: 0} + Scripts: ~ +- EID: 6 + Name: AI + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: -8, y: -2, z: 2.5} + Rotate: {x: 0, y: 0, z: 0} + Scale: {x: 2, y: 2, z: 2} + Renderable Component: + Mesh: 80365422 + Material: 126974645 + RigidBody Component: + Type: Dynamic + Mass: 1 + Drag: 0 + Angular Drag: 0 + Use Gravity: true + Interpolate: false + Freeze Position X: false + Freeze Position Y: false + Freeze Position Z: false + Freeze Rotation X: true + Freeze Rotation Y: true + Freeze Rotation Z: true + Collider Component: + Colliders: + - Is Trigger: false + Type: Box + Half Extents: {x: 0.0009765625, y: 0.0009765625, z: 0.0009765625} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0.5, z: 0} + Scripts: ~ +- EID: 7 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 3, y: -1, z: -1} + Rotate: {x: 0, y: 0, z: 0} + Scale: {x: 5, y: 5, z: 5} + Renderable Component: + Mesh: 80365422 + Material: 126974645 + Scripts: ~ +- EID: 8 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Light Component: + Position: {x: 0, y: 0, z: 0} + Type: Ambient + Direction: {x: 0, y: 0, z: 1} + Color: {x: 1, y: 1, z: 1, w: 1} + Layer: 4294967295 + Strength: 0.25 + Scripts: ~ \ No newline at end of file diff --git a/Assets/Scenes/M2Scene.shade.shmeta b/Assets/Scenes/M2Scene.shade.shmeta new file mode 100644 index 00000000..9289949c --- /dev/null +++ b/Assets/Scenes/M2Scene.shade.shmeta @@ -0,0 +1,3 @@ +Name: M2Scene +ID: 94283040 +Type: 5 diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index c43ca45d..f4102067 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -41,6 +41,8 @@ #include "Assets/SHAssetManager.h" +#include "Scenes/SBMainScene.h" +#include "Serialization/Configurations/SHConfigurationManager.h" #include "Tools/SHLogger.h" #include "Tools/SHDebugDraw.h" @@ -60,8 +62,9 @@ namespace Sandbox { // Set working directory SHFileUtilities::SetWorkDirToExecDir(); - - window.Create(hInstance, hPrevInstance, lpCmdLine, nCmdShow); + WindowData wndData{}; + auto& appConfig = SHConfigurationManager::LoadApplicationConfig(&wndData); + window.Create(hInstance, hPrevInstance, lpCmdLine, nCmdShow, wndData); // Create Systems SHSystemManager::CreateSystem(); @@ -80,7 +83,11 @@ namespace Sandbox SDL_Init(SDL_INIT_VIDEO); sdlWindow = SDL_CreateWindowFrom(window.GetHWND()); SHSystemManager::CreateSystem(); - SHSystemManager::GetSystem()->SetSDLWindow(sdlWindow); + if(auto editor = SHSystemManager::GetSystem()) + { + editor->SetSDLWindow(sdlWindow); + editor->SetSHWindow(&window); + } #endif // Create Routines @@ -128,7 +135,7 @@ namespace Sandbox SHSystemManager::Init(); - SHSceneManager::InitSceneManager("TestScene"); + SHSceneManager::InitSceneManager(appConfig.startingSceneID); SHFrameRateController::UpdateFRC(); diff --git a/SHADE_Application/src/Scenes/SBMainScene.cpp b/SHADE_Application/src/Scenes/SBMainScene.cpp new file mode 100644 index 00000000..34190915 --- /dev/null +++ b/SHADE_Application/src/Scenes/SBMainScene.cpp @@ -0,0 +1,62 @@ + #include "SBpch.h" +#include "SBMainScene.h" + +#include "ECS_Base/Managers/SHSystemManager.h" +#include "Graphics/MiddleEnd/Meshes/SHPrimitiveGenerator.h" +#include "ECS_Base/Managers/SHEntityManager.h" +#include "Graphics/MiddleEnd/Interface/SHRenderable.h" +#include "Scene/SHSceneManager.h" +#include "Graphics/MiddleEnd/Interface/SHGraphicsSystem.h" +#include "Scripting/SHScriptEngine.h" +#include "Math/Transform/SHTransformComponent.h" +#include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" +#include "Physics/Components/SHRigidBodyComponent.h" +#include "Physics/Components/SHColliderComponent.h" +#include "Graphics/MiddleEnd/Lights/SHLightComponent.h" + +#include "Assets/SHAssetManager.h" +#include "Camera/SHCameraComponent.h" +#include "Resource/SHResourceManager.h" +#include "Serialization/SHSerialization.h" + + using namespace SHADE; + +namespace Sandbox +{ + + void SBMainScene::WindowFocusFunc([[maybe_unused]] void* window, int focused) + { + if (focused) + { + } + else + { + } + } + + void SBMainScene::Load() + { + } + + void SBMainScene::Init() + { + sceneName = SHSerialization::DeserializeSceneFromFile(sceneAssetID); + } + + void SBMainScene::Update(float dt) + { + } + + void SBMainScene::Render() + { + } + + void SBMainScene::Unload() + { + } + + void SBMainScene::Free() + { + //SHSerialization::SerializeScene("resources/scenes/Scene01.SHADE"); + } +} diff --git a/SHADE_Application/src/Scenes/SBMainScene.h b/SHADE_Application/src/Scenes/SBMainScene.h new file mode 100644 index 00000000..7bd10118 --- /dev/null +++ b/SHADE_Application/src/Scenes/SBMainScene.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Scene/SHScene.h" +#include "Scene/SHSceneManager.h" + +namespace Sandbox +{ + class SBMainScene : public SHADE::SHScene + { + private: + EntityID camera; + EntityID testObj; + std::vector stressTestObjects; + + public: + virtual void Load(); + virtual void Init(); + virtual void Update(float dt); + virtual void Render(); + virtual void Free(); + virtual void Unload(); + + //TODO: Change to new window DO IT IN CPP TOO + void WindowFocusFunc(void* window, int focused); + + SBMainScene(void) = default; + }; + +} + diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index 06c0c2ae..af2d5517 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -3,6 +3,7 @@ //#==============================================================# //|| SHADE Includes || //#==============================================================# +#include "Editor/SHEditorWidgets.hpp" #include "Editor/SHEditor.h" #include "SHEditorMenuBar.h" #include "Editor/IconsMaterialDesign.h" @@ -17,7 +18,11 @@ #include #include +#include "Assets/SHAssetManager.h" +#include "Assets/Asset Types/SHSceneAsset.h" +#include "Scene/SHSceneManager.h" #include "Serialization/SHSerialization.h" +#include "Serialization/Configurations/SHConfigurationManager.h" namespace SHADE { @@ -75,13 +80,17 @@ namespace SHADE { if (ImGui::BeginMenu("File")) { + if(ImGui::Selectable("New Scene")) + { + SHSystemManager::GetSystem()->NewScene(); + } if(ImGui::Selectable("Save")) { - SHSerialization::SerializeSceneToFile("../../Assets/Scenes/Test.SHADE"); + SHSystemManager::GetSystem()->SaveScene(); } if(ImGui::Selectable("Load")) { - SHSerialization::DeserializeSceneFromFile("../../Assets/Scenes/Test.SHADE"); + //SHSystemManager::GetSystem()->LoadScene() } ImGui::EndMenu(); } @@ -155,6 +164,35 @@ namespace SHADE } ImGui::EndMenu(); } + + if (ImGui::BeginMenu("Application Config")) + { + auto& appConfig = SHConfigurationManager::applicationConfig; + ImGui::InputText("Window Title", &appConfig.windowTitle); + ImGui::Checkbox("Start in Fullscreen", &appConfig.startInFullScreen); + SHEditorWidgets::DragN("Window Size", { "Width", "Height" }, { &appConfig.windowSize.x, &appConfig.windowSize.y }); + //ImGui::InputScalar("Starting Scene", ImGuiDataType_U32, &appConfig.startingSceneID); + auto sceneAsset = SHAssetManager::GetData(appConfig.startingSceneID); + + if(ImGui::BeginCombo("Starting Scne", sceneAsset ? sceneAsset->name.data() : "")) + { + auto scenes = SHAssetManager::GetAllRecordOfType(AssetType::SCENE); + for(auto const& scene : scenes) + { + if(ImGui::Selectable(scene.name.data())) + { + appConfig.startingSceneID = scene.id; + } + } + ImGui::EndCombo(); + } + if (ImGui::Button("Save")) + { + SHConfigurationManager::SaveApplicationConfig(); + } + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); } @@ -175,13 +213,16 @@ namespace SHADE ImGui::BeginDisabled(editor->editorState == SHEditor::State::PLAY); if(ImGui::SmallButton(ICON_MD_PLAY_ARROW)) { - const SHEditorStateChangeEvent STATE_CHANGE_EVENT + if(editor->SaveScene()) { - .previousState = editor->editorState - }; - editor->editorState = SHEditor::State::PLAY; + const SHEditorStateChangeEvent STATE_CHANGE_EVENT + { + .previousState = editor->editorState + }; + editor->editorState = SHEditor::State::PLAY; - SHEventManager::BroadcastEvent(STATE_CHANGE_EVENT, SH_EDITOR_ON_PLAY_EVENT); + SHEventManager::BroadcastEvent(STATE_CHANGE_EVENT, SH_EDITOR_ON_PLAY_EVENT); + } } ImGui::EndDisabled(); ImGui::BeginDisabled(editor->editorState == SHEditor::State::PAUSE); @@ -206,6 +247,7 @@ namespace SHADE editor->editorState = SHEditor::State::STOP; SHEventManager::BroadcastEvent(STATE_CHANGE_EVENT, SH_EDITOR_ON_STOP_EVENT); + editor->LoadScene(SHSceneManager::GetCurrentSceneAssetID()); } ImGui::EndDisabled(); ImGui::EndMenuBar(); diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 6974d213..de8ac9d2 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -45,7 +45,11 @@ #include #include +#include "Assets/SHAssetManager.h" +#include "Assets/Asset Types/SHSceneAsset.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" +#include "Scene/SHSceneManager.h" +#include "Serialization/SHSerialization.h" #include "Tools/SHDebugDraw.h" RTTR_REGISTRATION @@ -147,7 +151,9 @@ namespace SHADE window->Update(); } } - + + RenderSceneNamePrompt(); + RenderUnsavedChangesPrompt(); //PollPicking(); if(ImGui::IsKeyDown(ImGuiKey_LeftShift) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyReleased(ImGuiKey_Z)) @@ -172,6 +178,60 @@ namespace SHADE } } + void SHEditor::RenderSceneNamePrompt() noexcept + { + if(isSceneNamePromptOpen) + { + ImGui::OpenPopup(sceneNamePromptName.data()); + } + + if(ImGui::BeginPopupModal(sceneNamePromptName.data(), &isSceneNamePromptOpen)) + { + static std::string newSceneName{}; + ImGui::Text("Enter new scene name"); + ImGui::InputText("##name", &newSceneName); + ImGui::BeginDisabled(newSceneName.empty()); + if(ImGui::Button("Save")) + { + SaveScene(newSceneName); + newSceneName.clear(); + isSceneNamePromptOpen = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndDisabled(); + ImGui::SameLine(); + if(ImGui::Button("Cancel")) + { + isSceneNamePromptOpen = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + } + + void SHEditor::RenderUnsavedChangesPrompt() noexcept + { + if(isUnsavedChangesPromptOpen) + { + ImGui::OpenPopup(unsavedChangesPromptName.data()); + } + + if(ImGui::BeginPopupModal(unsavedChangesPromptName.data(), &isUnsavedChangesPromptOpen)) + { + ImGui::Text("You have unsaved changes!"); + if(ImGui::Button("Save")) + { + isSceneNamePromptOpen = true; + } + ImGui::SameLine(); + if(ImGui::Button("Cancel")) + { + isUnsavedChangesPromptOpen = false; + ImGui::CloseCurrentPopup(); + } + } + } + void SHEditor::InitLayout() noexcept { if(!std::filesystem::exists(io->IniFilename)) @@ -470,6 +530,66 @@ namespace SHADE } } + void SHEditor::NewScene() + { + if(shWindow->IsUnsavedChanges()) + { + //Unsaved changes prompt + sceneToLoad = 0; + isUnsavedChangesPromptOpen = true; + } + else + { + SHSceneManager::RestartScene(0); + shWindow->ToggleUnsavedChanges(); + } + } + + bool SHEditor::SaveScene(std::string const& newSceneName) + { + auto const data = SHAssetManager::GetData(SHSceneManager::GetCurrentSceneAssetID()); + if (!data) + { + if (newSceneName.empty()) + { + //Prompt for scene name + isSceneNamePromptOpen = true; + return false; + } + //Else We have a new name + + SHSceneManager::SetCurrentSceneName(newSceneName); + SHSceneManager::SetCurrentSceneAssetID(SHAssetManager::CreateNewAsset(AssetType::SCENE, newSceneName)); + } + //Get data, if data is null, asset doesn't exist, prompt for a name and create a new asset with the name + + //serialize the scene + if(SHSerialization::SerializeSceneToFile(SHSceneManager::GetCurrentSceneAssetID())) + { + if(shWindow->IsUnsavedChanges()) + shWindow->ToggleUnsavedChanges(); + + return true; + } + return false; + } + + void SHEditor::LoadScene(AssetID const& assetID) noexcept + { + if(shWindow->IsUnsavedChanges()) + { + //Unsaved changes prompt + isUnsavedChangesPromptOpen = true; + sceneToLoad = assetID; + } + else + { + //Load the scene + sceneToLoad = 0; + SHSceneManager::RestartScene(assetID); + } + } + void SHEditor::NewFrame() { SDL_Event event; diff --git a/SHADE_Engine/src/Editor/SHEditor.h b/SHADE_Engine/src/Editor/SHEditor.h index 6e0ef5ae..0f5a3aaa 100644 --- a/SHADE_Engine/src/Editor/SHEditor.h +++ b/SHADE_Engine/src/Editor/SHEditor.h @@ -16,15 +16,18 @@ #include "Resource/SHHandle.h" #include "EditorWindow/SHEditorWindow.h" #include "Tools/SHLog.h" -#include "Gizmos/SHTransformGizmo.h"` +#include "Gizmos/SHTransformGizmo.h" #include "Events/SHEventDefines.h" #include "Events/SHEvent.h" +#include "Graphics/Windowing/SHWindow.h" //#==============================================================# //|| Library Includes || //#==============================================================# #include +#include "Assets/SHAssetMacros.h" + namespace SHADE { //#==============================================================# @@ -171,9 +174,16 @@ namespace SHADE void InitBackend(); void SetSDLWindow(SDL_Window* inSDLWindow){sdlWindow = inSDLWindow;}; + void SetSHWindow(SHWindow* inWindow){shWindow = inWindow;} void PollPicking(); + void NewScene(); + + bool SaveScene(std::string const& newSceneName = {}); + + void LoadScene(AssetID const& assetID) noexcept; + // List of selected entities std::vector selectedEntities; @@ -191,6 +201,10 @@ namespace SHADE */ void Render(); + void RenderSceneNamePrompt() noexcept; + + void RenderUnsavedChangesPrompt() noexcept; + void InitLayout() noexcept; void InitFonts() noexcept; @@ -199,12 +213,22 @@ namespace SHADE SHEventHandle onEditorStateChanged(SHEventPtr eventPtr); + bool isSceneNamePromptOpen = false; + + bool isUnsavedChangesPromptOpen = false; + + static constexpr std::string_view sceneNamePromptName = "Save scene as..."; + static constexpr std::string_view unsavedChangesPromptName = "Unsaved Changes"; + + AssetID sceneToLoad = 0; + // Handle to command pool used for ImGui Vulkan Backend Handle imguiCommandPool; // Handle to command buffer used for ImGui Vulkan Backend Handle imguiCommandBuffer; SDL_Window* sdlWindow {nullptr}; + SHWindow* shWindow {nullptr}; ImGuiIO* io{nullptr}; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index be67f1b3..32a03e33 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -764,6 +764,7 @@ namespace SHADE void SHGraphicsSystem::BeginRoutine::Execute(double) noexcept { + SHResourceManager::FinaliseChanges(); reinterpret_cast(system)->BeginRender(); } @@ -789,7 +790,6 @@ namespace SHADE void SHGraphicsSystem::EndRoutine::Execute(double) noexcept { reinterpret_cast(system)->EndRender(); - SHResourceManager::FinaliseChanges(); } /*-----------------------------------------------------------------------------------*/ @@ -806,11 +806,6 @@ namespace SHADE { if (!renderable.HasChanged()) continue; - - if (!renderable.GetMesh()) - { - SHLOG_CRITICAL("NULL Mesh provided!"); - } // Remove from the SuperBatch it is previously in (prevMat if mat has changed) Handle prevMaterial = renderable.HasMaterialChanged() ? renderable.GetPrevMaterial() : renderable.GetMaterial(); @@ -821,8 +816,9 @@ namespace SHADE } // Add to new SuperBatch if there is a material + // Add to new SuperBatch if there is a material and a mesh to render Handle newMatInstance = renderable.GetMaterial(); - if (newMatInstance) + if (newMatInstance && renderable.GetMesh()) { Handle newSuperBatch = newMatInstance->GetBaseMaterial()->GetPipeline()->GetPipelineState().GetSubpass()->GetSuperBatch(); newSuperBatch->Add(&renderable); diff --git a/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp b/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp index e0dbc1a4..22ca5eba 100644 --- a/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp +++ b/SHADE_Engine/src/Graphics/Windowing/SHWindow.cpp @@ -4,7 +4,6 @@ #include "ECS_Base/Managers/SHSystemManager.h" #include "Input/SHInputManager.h" - namespace SHADE { SHWindow::SHWindow() @@ -151,11 +150,11 @@ namespace SHADE SetWindowText(wndHWND, LPCWSTR(wndData.title.c_str())); } - SHWindow::SHVec2 SHWindow::GetPosition() const + SHWindow::WindowSize SHWindow::GetPosition() const { RECT rect; GetWindowRect(wndHWND, &rect); - return SHVec2(static_cast(rect.left), static_cast(rect.top)); + return WindowSize(static_cast(rect.left), static_cast(rect.top)); } void SHWindow::SetPosition(unsigned x, unsigned y) @@ -165,18 +164,18 @@ namespace SHADE wndData.y = y; } - SHWindow::SHVec2 SHWindow::GetWindowSize() const + SHWindow::WindowSize SHWindow::GetWindowSize() const { RECT rect; GetClientRect(wndHWND, &rect); - return SHVec2(static_cast(rect.right - rect.left), static_cast(rect.bottom - rect.top)); + return WindowSize(static_cast(rect.right - rect.left), static_cast(rect.bottom - rect.top)); } - SHWindow::SHVec2 SHWindow::GetCurrentDisplaySize() const + SHWindow::WindowSize SHWindow::GetCurrentDisplaySize() const { unsigned screenWidth = GetSystemMetrics(SM_CXSCREEN); unsigned screenHeight = GetSystemMetrics(SM_CYSCREEN); - return SHVec2(screenWidth, screenHeight); + return WindowSize(screenWidth, screenHeight); } void SHWindow::SetMouseVisible(bool show) @@ -260,7 +259,7 @@ namespace SHADE return wndHWND; } - const WindowData SHWindow::GetWindowData() const + const WindowData SHWindow::GetWindowData() const noexcept { return wndData; } @@ -287,6 +286,15 @@ namespace SHADE windowCloseCallbacks.erase(callbackid); } + void SHWindow::ToggleUnsavedChanges() noexcept + { + unsavedChanges = !unsavedChanges; + std::wstring title = wndData.title; + if(unsavedChanges) + title.append(L"*"); + SetWindowText(wndHWND, title.data()); + } + LRESULT SHWindow::WndProcStatic(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { auto window = windowMap.GetWindow(hwnd); diff --git a/SHADE_Engine/src/Graphics/Windowing/SHWindow.h b/SHADE_Engine/src/Graphics/Windowing/SHWindow.h index 11308d90..06df9036 100644 --- a/SHADE_Engine/src/Graphics/Windowing/SHWindow.h +++ b/SHADE_Engine/src/Graphics/Windowing/SHWindow.h @@ -74,7 +74,7 @@ namespace SHADE class SH_API SHWindow { public: - using SHVec2 = std::pair; + using WindowSize = std::pair; typedef std::function WindowResizeCallbackFn; typedef std::function WindowCloseCallbackFn; typedef uint16_t CALLBACKID; @@ -92,15 +92,15 @@ namespace SHADE void SetTitle(std::wstring title); - SHVec2 GetPosition() const; + WindowSize GetPosition() const; void SetPosition(unsigned x, unsigned y); //void SetPosition(SHMathVec2U); - SHVec2 GetWindowSize() const; + WindowSize GetWindowSize() const; //Get size of display the window is in (whichever window contains the window origin) - SHVec2 GetCurrentDisplaySize() const; + WindowSize GetCurrentDisplaySize() const; void SetMouseVisible(bool show); @@ -123,7 +123,7 @@ namespace SHADE HWND GetHWND(); - const WindowData GetWindowData() const; + const WindowData GetWindowData() const noexcept; CALLBACKID RegisterWindowSizeCallback(WindowResizeCallbackFn); void UnregisterWindowSizeCallback(CALLBACKID const& callbackid); @@ -131,6 +131,8 @@ namespace SHADE void UnregisterWindowCloseCallback(CALLBACKID const& callbackid); bool IsMinimized() const { return wndData.isMinimised; } + void ToggleUnsavedChanges() noexcept; + bool IsUnsavedChanges() const noexcept{return unsavedChanges;} protected: static LRESULT CALLBACK WndProcStatic(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); @@ -164,7 +166,8 @@ namespace SHADE std::unordered_map windowCloseCallbacks; CALLBACKID windowResizeCallbackCount{}; CALLBACKID windowCloseCallbackCount{}; - //TODO: Shift to events abstraction + + bool unsavedChanges = false; void OnCreate(HWND hwnd, LPCREATESTRUCT create_struct); void OnClose(); diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index fc8b2c57..ee1a76ba 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -282,6 +282,9 @@ namespace SHADE case SHADE::SHShaderBlockInterface::Variable::Type::INT: { Handle texture = LoadOrGet(PROP_NODE.as()); + // HACK: Need to split this out to a separate pass before loading the materials and subsequently, the scenes + gfxSystem->BuildTextures(); + if (texture) { matHandle->SetProperty(VARIABLE->offset, texture->TextureArrayIndex); diff --git a/SHADE_Engine/src/Scene/SHScene.h b/SHADE_Engine/src/Scene/SHScene.h index a81c70ef..1f06824e 100644 --- a/SHADE_Engine/src/Scene/SHScene.h +++ b/SHADE_Engine/src/Scene/SHScene.h @@ -13,6 +13,7 @@ #include #include "SHSceneGraph.h" +#include "Assets/SHAssetMacros.h" #include "ECS_Base/General/SHFamily.h" namespace SHADE @@ -32,6 +33,7 @@ namespace SHADE virtual ~SHScene() = default; std::string sceneName; + AssetID sceneAssetID; SHSceneGraph& GetSceneGraph() noexcept { return sceneGraph; } diff --git a/SHADE_Engine/src/Scene/SHSceneManager.cpp b/SHADE_Engine/src/Scene/SHSceneManager.cpp index 2baf0b9c..be9c7755 100644 --- a/SHADE_Engine/src/Scene/SHSceneManager.cpp +++ b/SHADE_Engine/src/Scene/SHSceneManager.cpp @@ -29,7 +29,7 @@ namespace SHADE std::string SHSceneManager::newSceneName{}; uint32_t SHSceneManager::currentSceneID = UINT32_MAX; uint32_t SHSceneManager::nextSceneID = UINT32_MAX; - + AssetID SHSceneManager::currentSceneAssetID{}; SHScene* SHSceneManager::currentScene = nullptr; //SHScene* SHSceneManager::nextScene = nullptr; @@ -107,16 +107,18 @@ namespace SHADE } } - void SHSceneManager::RestartScene(std::string const& sceneName) noexcept + void SHSceneManager::RestartScene(AssetID const& assetID ) noexcept { - if (currentScene->sceneName != sceneName) + if (currentScene->sceneAssetID != assetID) { - cleanReload = true; - newSceneName = sceneName; + //cleanReload = true; + cleanReload = false; + //newSceneName = sceneName; } else cleanReload = false; + currentScene->sceneAssetID = assetID; nextSceneID = currentSceneID; sceneChanged = true; } @@ -151,4 +153,20 @@ namespace SHADE { return currentScene->sceneName; } + + void SHSceneManager::SetCurrentSceneName(std::string const& sceneName) noexcept + { + currentScene->sceneName = sceneName; + } + + AssetID SHSceneManager::GetCurrentSceneAssetID() noexcept + { + return currentScene->sceneAssetID; + } + + void SHSceneManager::SetCurrentSceneAssetID(AssetID const& newAssetID) + { + currentScene->sceneAssetID = newAssetID; + currentSceneAssetID = newAssetID; + } } diff --git a/SHADE_Engine/src/Scene/SHSceneManager.h b/SHADE_Engine/src/Scene/SHSceneManager.h index 2e4b1717..23d13261 100644 --- a/SHADE_Engine/src/Scene/SHSceneManager.h +++ b/SHADE_Engine/src/Scene/SHSceneManager.h @@ -20,6 +20,7 @@ //Project Headers #include "SH_API.h" #include "ECS_Base/General/SHFamily.h" +#include "Assets/SHAssetMacros.h" namespace SHADE { @@ -47,6 +48,9 @@ namespace SHADE //pointer to the current scene static SHScene* currentScene; + //Scene AssetID of the current scene + static AssetID currentSceneAssetID; + //Used in reloading scene. static std::string newSceneName; @@ -80,10 +84,10 @@ namespace SHADE * None. ***************************************************************************/ template - static std::enable_if_t, void> InitSceneManager(std::string const& sceneName) noexcept + static std::enable_if_t, void> InitSceneManager(AssetID const& sceneAssetID) noexcept { //prevSceneCreate = newScene; - newScene = [sceneName]() { currentScene = new T(); currentScene->sceneName = sceneName; }; + newScene = [sceneAssetID]() { currentScene = new T(); currentScene->sceneAssetID = sceneAssetID; }; //nextSceneID = SHFamilyID::GetID(); nextSceneID = 0; @@ -99,7 +103,7 @@ namespace SHADE * None. ***************************************************************************/ template - static std::enable_if_t, void> ChangeScene(std::string const& sceneName) noexcept + static std::enable_if_t, void> ChangeScene(AssetID const& sceneAssetID) noexcept { //check if this new Scene is current Scene (Use RestartScene instead) if (currentSceneID == SHFamilyID::GetID()) @@ -107,7 +111,7 @@ namespace SHADE return; } //prevSceneCreate = newScene; - newScene = [sceneName]() { currentScene = new T(); currentScene->sceneName; }; + newScene = [sceneAssetID]() { currentScene = new T(); currentScene->sceneAssetID = sceneAssetID; }; nextSceneID = SHFamilyID::GetID(); sceneChanged = true; } @@ -137,11 +141,11 @@ namespace SHADE * Restarts current scene. Only Scene::Init() and Scene::Free() * Scene::Load() and Scene::Unload() will not be called. * Edit: allows for RestartScene to restart the scene with a different - * scene name. - * If a sceneName is different from the current one, Load and Unload will + * scene asset ID. + * If a scene asset id is different from the current one, Load and Unload will * run. ***************************************************************************/ - static void RestartScene(std::string const& sceneName ) noexcept; + static void RestartScene(AssetID const& assetID ) noexcept; /*!************************************************************************* * \brief @@ -164,7 +168,10 @@ namespace SHADE static void Exit() noexcept; static std::string GetSceneName() noexcept; - + static void SetCurrentSceneName(std::string const& sceneName) noexcept; + static AssetID GetCurrentSceneAssetID() noexcept; + //Only if scene doesn't exist, and scene asset id needs to be updated to the new one + static void SetCurrentSceneAssetID(AssetID const& newAssetID); }; diff --git a/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.cpp b/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.cpp new file mode 100644 index 00000000..6fa4e9bf --- /dev/null +++ b/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.cpp @@ -0,0 +1,89 @@ +#include "SHpch.h" +#include "SHConfigurationManager.h" +#include "Tools/FileIO/SHFileIO.h" +#include "Serialization/SHSerializationHelper.hpp" + +namespace SHADE +{ + SHApplicationConfig SHConfigurationManager::applicationConfig; +#ifdef SHEDITOR + SHEditorConfig SHConfigurationManager::editorConfig; +#endif + + void SHConfigurationManager::SaveApplicationConfig() + { + YAML::Emitter out; + out << SHSerializationHelper::RTTRToNode(applicationConfig); + SHFileIO::WriteStringToFile(applicationConfigPath, out.c_str()); + } + + SHApplicationConfig& SHConfigurationManager::LoadApplicationConfig(WindowData* wndData) + { + if(!std::filesystem::exists(applicationConfigPath)) + { + SaveApplicationConfig(); + return applicationConfig; + } + + auto const node = YAML::Load(SHFileIO::GetStringFromFile(applicationConfigPath)); + auto properties = rttr::type::get().get_properties(); + for(auto const& property : properties) + { + if(node[property.get_name().data()].IsDefined()) + SHSerializationHelper::InitializeProperty(&applicationConfig, property, node[property.get_name().data()]); + } + + if(wndData != nullptr) + { + wndData->isFullscreen = applicationConfig.startInFullScreen; + wndData->title = std::wstring(applicationConfig.windowTitle.begin(), applicationConfig.windowTitle.end()); + wndData->width = static_cast(applicationConfig.windowSize.x); + wndData->height = static_cast(applicationConfig.windowSize.y); + } + + return applicationConfig; + } + +#ifdef SHEDITOR + void SHConfigurationManager::SaveEditorConfig() + { + YAML::Emitter out; + out << SHSerializationHelper::RTTRToNode(editorConfig); + SHFileIO::WriteStringToFile(editorConfigPath, out.c_str()); + } + + SHEditorConfig& SHConfigurationManager::LoadEditorConfig() + { + auto const node = YAML::Load(SHFileIO::GetStringFromFile(editorConfigPath)); + auto properties = rttr::type::get().get_properties(); + for(auto const& property : properties) + { + if(node[property.get_name().data()].IsDefined()) + SHSerializationHelper::InitializeProperty(&editorConfig, property, node[property.get_name().data()]); + } + return editorConfig; + } + + void SHConfigurationManager::FetchEditorCameraData() + { + + } + + void SHConfigurationManager::SetEditorCameraData() + { + + } +#endif +} + +RTTR_REGISTRATION +{ + using namespace rttr; + using namespace SHADE; + + registration::class_("Application Config") + .property("Start in Fullscreen", &SHApplicationConfig::startInFullScreen) + .property("Starting Scene ID", &SHApplicationConfig::startingSceneID) + .property("Window Size", &SHApplicationConfig::windowSize) + .property("Window Title", &SHApplicationConfig::windowTitle); +} diff --git a/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.h b/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.h new file mode 100644 index 00000000..abf679ca --- /dev/null +++ b/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include "Assets/SHAssetMacros.h" +#include "Graphics/Windowing/SHWindow.h" +#include "SH_API.h" +#include "Math/Vector/SHVec2.h" + +namespace SHADE +{ + + struct SHApplicationConfig + { + bool startInFullScreen{ false }; + AssetID startingSceneID{}; + SHVec2 windowSize {1920, 1080}; + std::string windowTitle {"SHADE Engine"}; + RTTR_ENABLE() + }; + + struct SHEditorConfig + { + + }; + + class SH_API SHConfigurationManager + { + public: + static constexpr std::string_view applicationConfigPath{"../../Assets/Application.SHConfig"}; + static constexpr std::string_view editorConfigPath{"../../Assets/Editor/Editor.SHConfig"}; + + static void SaveApplicationConfig(); + static SHApplicationConfig& LoadApplicationConfig(WindowData* wndData = nullptr); + static SHApplicationConfig applicationConfig; +#ifdef SHEDITOR + static void SaveEditorConfig(); + static SHEditorConfig& LoadEditorConfig(); + static SHEditorConfig editorConfig; + private: + static void FetchEditorCameraData(); + static void SetEditorCameraData(); +#endif + + }; +} diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index 03498951..dc79ac72 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -10,6 +10,7 @@ #include "Assets/SHAssetManager.h" #include +#include "Assets/Asset Types/SHSceneAsset.h" #include "Camera/SHCameraComponent.h" #include "Graphics/MiddleEnd/Interface/SHRenderable.h" #include "Math/Transform/SHTransformComponent.h" @@ -17,19 +18,23 @@ #include "ECS_Base/Managers/SHSystemManager.h" #include "Graphics/MiddleEnd/Lights/SHLightComponent.h" #include "Scripting/SHScriptEngine.h" +#include "Tools/FileIO/SHFileIO.h" namespace SHADE { - void SHSerialization::SerializeSceneToFile(std::filesystem::path const& path) + bool SHSerialization::SerializeSceneToFile(AssetID const& sceneAssetID) { + auto assetData = SHAssetManager::GetData(sceneAssetID); + if(!assetData) + { + SHLOG_ERROR("Asset does not exist: {}", sceneAssetID); + return false; + } YAML::Emitter out; SerializeSceneToEmitter(out); - std::ofstream file(path.c_str()); - if (file.good()) - { - file << out.c_str(); - file.close(); - } + assetData->data = out.c_str(); + + return SHAssetManager::SaveAsset(sceneAssetID); } std::string SHSerialization::SerializeSceneToString() @@ -91,31 +96,16 @@ namespace SHADE return eid; } - void SHSerialization::DeserializeSceneFromFile(std::filesystem::path const& path) + std::string SHSerialization::DeserializeSceneFromFile(AssetID const& sceneAssetID) noexcept { - //TODO:Shift to using XQ's FileIO - std::ifstream iFile; - iFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - std::string fileContent = ""; - - try + auto assetData = SHAssetManager::GetData(sceneAssetID); + if(!assetData) { - // Open file - // Read file's buffer contents into streams - iFile.open(path); - std::stringstream fileStream; - fileStream << iFile.rdbuf(); - - fileContent = fileStream.str(); - - // Close file handler - iFile.close(); + SHLOG_ERROR("Attempted to load scene that doesn't exist {}", sceneAssetID) + SHSceneManager::SetCurrentSceneAssetID(0); + return NewSceneName.data(); } - catch (std::ifstream::failure e) - { - SHLOG_ERROR("Could not read file"); - } - YAML::Node entities = YAML::Load(fileContent); + YAML::Node entities = YAML::Load(assetData->data); std::vector createdEntities{}; //Create Entities @@ -126,7 +116,7 @@ namespace SHADE if (createdEntities.empty()) { SHLOG_ERROR("Failed to create entities from deserializaiton") - return; + return NewSceneName.data(); } //Initialize Entity auto entityVecIt = createdEntities.begin(); @@ -134,7 +124,9 @@ namespace SHADE { InitializeEntity(*it, *entityVecIt++); } - } + + return assetData->name; + } void SHSerialization::EmitEntity(SHSceneNode* entityNode, YAML::Emitter& out) { diff --git a/SHADE_Engine/src/Serialization/SHSerialization.h b/SHADE_Engine/src/Serialization/SHSerialization.h index 018bf0c3..865d53a3 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.h +++ b/SHADE_Engine/src/Serialization/SHSerialization.h @@ -5,6 +5,7 @@ #include #include +#include namespace YAML { @@ -25,12 +26,11 @@ namespace SHADE struct SH_API SHSerialization { - //TODO: change paths to resource ID - static void SerializeSceneToFile(std::filesystem::path const& path); + static bool SerializeSceneToFile(AssetID const& sceneAssetID); static std::string SerializeSceneToString(); static void SerializeSceneToEmitter(YAML::Emitter& out); - static void DeserializeSceneFromFile(std::filesystem::path const& path); + static std::string DeserializeSceneFromFile(AssetID const& sceneAssetID) noexcept; static void EmitEntity(SHSceneNode* entityNode, YAML::Emitter& out); @@ -44,5 +44,7 @@ namespace SHADE static std::vector GetComponentIDList(YAML::Node const& componentsNode); private: static void InitializeEntity(YAML::Node const& entityNode, EntityID const& eid); + + static constexpr std::string_view NewSceneName = "New Scene"; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index ed4f118a..46e591dc 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -397,6 +397,8 @@ namespace SHADE node = YAML::Null; } } + else if (varType == rttr::type::get()) + node = var.to_string(); else { auto properties = var.get_type().get_properties(); @@ -432,63 +434,65 @@ namespace SHADE return node; } - template , bool> = true> - static void InitializeProperty(ComponentType* component, rttr::property const& prop, YAML::Node const& propertyNode) + template + static void InitializeProperty(Type* object, rttr::property const& prop, YAML::Node const& propertyNode) { auto propType = prop.get_type(); if (propType == rttr::type::get()) { SHVec4 vec = propertyNode.as(); - prop.set_value(component, vec); + prop.set_value(object, vec); } else if (propType == rttr::type::get()) { SHVec3 vec = propertyNode.as(); - prop.set_value(component, vec); + prop.set_value(object, vec); } else if (propType == rttr::type::get()) { SHVec2 vec = propertyNode.as(); - prop.set_value(component, vec); + prop.set_value(object, vec); } else if (propType.is_arithmetic()) { bool ok = false; if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); else if (propType == rttr::type::get()) - prop.set_value(component, propertyNode.as()); + prop.set_value(object, propertyNode.as()); } else if (propType.is_enumeration()) { auto enumAlign = prop.get_enumeration(); - prop.set_value(component, enumAlign.name_to_value(propertyNode.as())); + prop.set_value(object, enumAlign.name_to_value(propertyNode.as())); } + else if (propType == rttr::type::get()) + prop.set_value(object, propertyNode.as()); else { auto properties = propType.get_properties(); for (auto const& property : properties) { - if (propertyNode[property.get_name().data()].IsDefined()) - InitializeProperty(component, property, propertyNode[property.get_name().data()]); + if(propertyNode[property.get_name().data()].IsDefined()) + InitializeProperty(object, property, propertyNode[property.get_name().data()]); } } } diff --git a/SHADE_Engine/src/Tools/FileIO/SHFieIO.cpp b/SHADE_Engine/src/Tools/FileIO/SHFieIO.cpp new file mode 100644 index 00000000..c4fec120 --- /dev/null +++ b/SHADE_Engine/src/Tools/FileIO/SHFieIO.cpp @@ -0,0 +1,31 @@ +#include "SHpch.h" +#include "SHFileIO.h" +#include + +namespace SHADE +{ + int SHFileIO::WriteStringToFile(std::filesystem::path const& filePath, std::string_view const& strView) + { + std::ofstream file(filePath); + if(file.good()) + { + file << strView; + file.close(); + return 1; + } + return 0; + } + + std::string SHFileIO::GetStringFromFile(std::filesystem::path const& filePath) + { + std::ifstream iFile; + iFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + std::string fileData{}; + iFile.open(filePath, std::iostream::binary); + std::stringstream ss; + ss << iFile.rdbuf(); + fileData = ss.str(); + iFile.close(); + return fileData; + } +} diff --git a/SHADE_Engine/src/Tools/FileIO/SHFileIO.h b/SHADE_Engine/src/Tools/FileIO/SHFileIO.h new file mode 100644 index 00000000..adc95d5b --- /dev/null +++ b/SHADE_Engine/src/Tools/FileIO/SHFileIO.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include +#include + +namespace SHADE +{ + struct SH_API SHFileIO + { + static int WriteStringToFile(std::filesystem::path const& filePath, std::string_view const& strView); + static std::string GetStringFromFile(std::filesystem::path const& file); + }; +} \ No newline at end of file diff --git a/SHADE_Managed/premake5.lua b/SHADE_Managed/premake5.lua index b383f002..88021071 100644 --- a/SHADE_Managed/premake5.lua +++ b/SHADE_Managed/premake5.lua @@ -38,6 +38,7 @@ project "SHADE_Managed" "%{IncludeDir.RTTR}/include", "%{IncludeDir.dotnet}\\include", "%{IncludeDir.reactphysics3d}\\include", + "%{IncludeDir.VULKAN}\\include", "%{wks.location}/SHADE_Engine/src" } From 2b34e8c13ba27783a5707077031f64f8d30450e7 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Fri, 4 Nov 2022 16:09:15 +0800 Subject: [PATCH 066/113] Fix drag-drop parenting issue Users can now drop onto empty space in hierarchy panel to unparent --- .../HierarchyPanel/SHHierarchyPanel.cpp | 23 +++++++++++++------ .../HierarchyPanel/SHHierarchyPanel.h | 4 +++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index 1b289a90..e2c39ad8 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -45,7 +45,7 @@ namespace SHADE SHEditorWindow::Update(); isAnyNodeSelected = false; - + if (Begin()) { if (skipFrame) @@ -108,6 +108,12 @@ namespace SHADE } } + if(ImGui::IsWindowHovered() && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) + { + ParentSelectedEntities(MAX_EID, draggingEntities); + draggingEntities.clear(); + ImGui::ClearDragDrop(); + } ImGui::End(); } @@ -195,7 +201,7 @@ namespace SHADE if (SHDragDrop::BeginSource()) { std::string moveLabel = "Moving EID: "; - static std::vector draggingEntities = editor->selectedEntities; + draggingEntities = editor->selectedEntities; if (!isSelected) { draggingEntities.clear(); @@ -217,7 +223,8 @@ namespace SHADE { if (const std::vector* eidPayload = SHDragDrop::AcceptPayload>(SHDragDrop::DRAG_EID)) //If payload is valid { - ParentSelectedEntities(eid); + ParentSelectedEntities(eid, draggingEntities); + draggingEntities.clear(); SHDragDrop::EndTarget(); } } @@ -255,7 +262,7 @@ namespace SHADE if ((currentNode->GetParent() != sceneGraph.GetRoot()) && ImGui::Selectable(std::format("{} Unparent Selected", ICON_MD_NORTH_WEST).data())) { - ParentSelectedEntities(MAX_EID); + ParentSelectedEntities(MAX_EID, editor->selectedEntities); } ImGui::EndPopup(); } @@ -323,14 +330,16 @@ namespace SHADE SHEntityManager::CreateEntity(MAX_EID, "DefaultChild", parentEID); } - void SHHierarchyPanel::ParentSelectedEntities(EntityID parentEID) const noexcept + void SHHierarchyPanel::ParentSelectedEntities(EntityID parentEID, std::vector const& entities) const noexcept { auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); - auto const editor = SHSystemManager::GetSystem(); + //auto const editor = SHSystemManager::GetSystem(); SHEntityParentCommand::EntityParentData entityParentData; std::vector parentedEIDS; - for (auto const& eid : editor->selectedEntities) + for (auto const& eid : entities) { + if(eid == parentEID) + continue; if (sceneGraph.GetChild(eid, parentEID) == nullptr) { parentedEIDS.push_back(eid); diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h index 9b26e9d6..64f841d6 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.h @@ -28,7 +28,7 @@ namespace SHADE void DrawMenuBar() const noexcept; ImRect RecursivelyDrawEntityNode(SHSceneNode* const); void CreateChildEntity(EntityID parentEID) const noexcept; - void ParentSelectedEntities(EntityID parentEID) const noexcept; + void ParentSelectedEntities(EntityID parentEID, std::vector const& entities) const noexcept; void SelectRangeOfEntities(EntityID beginEID, EntityID EndEID); void SelectAllEntities(); void CopySelectedEntities(); @@ -37,6 +37,8 @@ namespace SHADE std::string filter; bool isAnyNodeSelected = false; EntityID scrollTo = MAX_EID; + std::vector draggingEntities; + };//class SHHierarchyPanel //Might move to a different file From 4eecc0c08d1bf8c28ec6abac169620b54c6de38f Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Fri, 4 Nov 2022 16:23:13 +0800 Subject: [PATCH 067/113] Removed old mesh and meta files --- Assets/Cube.003.shmesh | Bin 31656 -> 0 bytes Assets/Cube.003.shmesh.shmeta | 3 --- Assets/Cube.012.shmesh.shmeta | 3 --- 3 files changed, 6 deletions(-) delete mode 100644 Assets/Cube.003.shmesh delete mode 100644 Assets/Cube.003.shmesh.shmeta delete mode 100644 Assets/Cube.012.shmesh.shmeta diff --git a/Assets/Cube.003.shmesh b/Assets/Cube.003.shmesh deleted file mode 100644 index 54cfb867570cf8bdca307ccb4b2763a4bbd29892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31656 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 Date: Fri, 4 Nov 2022 17:17:24 +0800 Subject: [PATCH 068/113] Shifted assets and generated new assets --- Assets/Models/racoon.fbx | Bin 0 -> 703020 bytes Assets/Models/racoon.gltf | 4993 +++++++++++++++++ .../racoon.shmodel} | Bin 223192 -> 254874 bytes Assets/Models/racoon.shmodel.shmeta | 10 + 4 files changed, 5003 insertions(+) create mode 100644 Assets/Models/racoon.fbx create mode 100644 Assets/Models/racoon.gltf rename Assets/{Cube.012.shmesh => Models/racoon.shmodel} (87%) create mode 100644 Assets/Models/racoon.shmodel.shmeta diff --git a/Assets/Models/racoon.fbx b/Assets/Models/racoon.fbx new file mode 100644 index 0000000000000000000000000000000000000000..4d823d9d32fd1d077dc4e87503058d5318c79759 GIT binary patch literal 703020 zcmb??2|U!#|GyF{p;94NC!r{Evm1((qmnkYOJZ}x9#}UU5)!)Qjt)gpQ8uh2A<0d0 zFLLKzH|sxdwm$k4_4|DP^XT2a-!t=i&TC%tn%BJMHRGtUlc}+d@fLN}(_2(6Fvd1c zTedK4X6WQ&V8~`+_>tCw^22CjQ?$(?M>{me)&h$`g2H9sVFJ(EAH}E8Hc+8v4#VltgI|-=L?IMgVKfR9lhor zkjXOFQLK&LF7Tmhg~kARZ`oyQXMElYJvTy{x*}WlY>^U|65k7c_lrx(h)X4d4nm;9 z;8~?-g2tdVF&D6U8^NcB752Qb74*rl?4OdAphVvmZFA7r&X^xi0tOh^z_S7pzTU!8 z8G0!+#6QGr3O<;DsK2L`+Gsmt2+MS_+Aa$Um`+;QS)uhH;@b9 z6fmn0?972gAcDLF`74j2ogA<>rnYk>l!YZH(DoLP4$hTKEGS`7!&+OTG4qW;fIm{~ z0##pg%m#}?+t^v4ZDsdf03C&TFAtt&$Mjgi&q1sS0YcHYvC@Ocj_TRjSYXWbAdx_M z;D=$ZqQnI(5WkK1h4YRt=rBs448X8MRcuWD3sMU}S^>y+%p*agrE6nh24=3++|Yq8 z;;;ma={y^tltBqphq|Bz9?&+VuF!vZ!Q2TFE7vK)E;E+l)L#=C?o>&XscO{ATyxH;TUFL@uqEE%92L*ts zfa*b#Uh~s}3XKw^)qD2t1OK+{qw6-%@V!QKqW`=&HLL;P^u#{D9OnRcjBzWVfNG0?2h0zkK@9ph6-Nu(IooRi3_E0HG%+3(1e{8q( z*LKygV6|JiUC;&tJLn$N*#+(XL;1&cwdvIR00Iydv`&3j^*>2^7NqNRa_M<;Xw~=^ z`5!s>Oox!0hu{D|`WOp4JriRqw7Rj09ay5EaW};hz$BS_)gf4)rw{i*Py*5bd8tQBAgs3pVP+X)R-J*e6FZ1BQB z=jeAY1#}+i77)^qgXjy!1S>l=uwdF4FJeVDfO^0~F(sG)-|!ePB{<+V0mCy*huaCt zL6;$(phXR`EPBwQg2oW6|5?y9L2U+Q&=ZJVDewRihy#d34`+-4UbcU{o;FErz+i z%=ys&yj4RIqoza{yB}%`v_UAl_#)RI~|}0T0htM-Ivq?iLE|?zJRttV@%MtKUM8i z{O6MusH_E;QNZ1{H3hWa<@&>*n}WJ73kv80#{o$f(6gbguAZ07+ztd{;pe3y{Z{~} zp`X$wU@D+Y$mlFYgT@Hzyow!g)6au_+bVj+1;a1%6B7790xu2%gNPS`L9x^z-VYu3`Y5>6-4);KFjogp)#e%?_VZELOyd5ZM(llHU#V5pu7c!2gm zeE!+agoZ$=3x4pIm;JySfZz_!!$GgAs#r&`iJdbNr-1pu+DILon`!eObNUEXpG&{K zTv-Is*zvz2<^V*fpAz5!U53W+n2m)s6pc6q_HySftSs!D=FHf6ESA2-f|#3|xKngu z(RpHM%+!F}Y`aCz+}ITBV7nNdG#CJ=(cSZOK|i2<3yAv)cXDg^0t8>sj_{iH&hfCM2>x?zTV!{Y>?e>NB>HUk_a z8yt2{A6I~qf2|A-%^3{7+03m0tBcK>yt^Xb4x=7FKH`VM@| z!XEpdaRq@9ff!zZ3%Lxy&-~x`wWs51&hrb62p#u-V)qq*g~msG9u~3$CvA)|wqQ(v zi0AG8@6;C5V_*dyz`w?30qy@VtxcB({_nFp5&%QJT`20Cz!n*j547}w z84m64ztaK#&jhWGwb8P$wy@L1SpC-Ug8B^JbPUKJpj+)97!7(rt z{{@i2-vDs|Pao>lQXHN68z6l;(BAp3LGumTdugFDf2Q7XARbUdB=ijf{&y23 z02CbRq{w_HAv?Z+*F|Tv{*q*iPUmY&k}W!ybCw`m!t6{gNwz4H`#>akF_9QMF3O}C zoov^wGeuuVm)0>Vpo4T?W4^W3;D=84Kd6`{2$L=X z`ga;O06z#A0cpVt5r9JCTp*W!I9*i1UN1$p=+N~nMYZV2Z3UsfML1kSyAVrJEy`iQ zQdEm__zh4&19Kcaz!ZVS|NCys!eoKR@L!$J!(eL<4sg z?RjjlCHV)Nt_Obuy+EOUs8|PphQ^J)R6{;9Q2vA981#fO#ti*eE36oZS}%d|SBool zX^g*GSCf8&VeI&S>&w{E7=P6lMZsU?v4D@i>We2GL;jy3r;iIb5kzCm?9BgaXbS!a z^H)7uyJfydi*xf=okIKuW?nG=r%pxw5$3Nt)%Hi2zv`6e)_K{;GW>9HfIkKWw@g(q zW>$YUg|L4B`Kt~k{{iH$y3-9npmmnM)Ihs)EiCq;irx2c)0~g`?*QYrZSz$0B^a_$ z`nY*FbV<77fDSSppi#&uLk;VhV;z3CeOd;}g6Y-e=BtC1ia`f$2KKe~Xb`Oi*AwhO zSO(lPAmDIV5MqYleiPGa_!$Lka=r)0f5h=L!R-jg#gA{G^9c6s^Q?m(h;=CJs&0cu zJI_bN|J5ugWPtxlprv~dM9Y51Cqxhi2o@|UPz*`S#A?gJ#p-u?5UCy{EtP}NE0lhH z8Kh&={cFf)g|L6DRKH|EFn>fnv}2xQbx;||1oGb?)bDH5zrTOM{M`!W4KN`Up@*9w zFKt2C?q8q3LHt6eKLpAEPhdB=fPM?zy)*`a=%tG0aH0R^f`ZV?UnI4iphZv*ijyx4 z(X9aAU~`3*p_fCwqetkLgV&#uKfMmLFfYVd3BEyi9BTtkbKHN$x%d+Z_*k}Qo|oU@ zXhXp}3uCLiUvVIQs35cgbp!I4ehoT98VG{UdIXybU}t~K9E&mQ00iIDPmx1iaApB@ zb{}}aP(V5cg;7BuZ7yQD$e|QOF^7AZPA)u84h_9FIMK4uGIpZJB7QULDghBRiB#u_ zp#C4Swy?DYCSyME4Z;6&ruIh_-vC;NNa*Sa*~jmA3yu&MoE!dxH~=>R^bkKP;GxUl zr}MtwvNIodFZdpSU+@w`XNN9QsC#s{zv5=_S8fi1;0nQZDdGHb3h?7xU@urTpbjkH zh7R{v+;{bF1w;)2i0Mf+iFW3SU zU}Fz8D+V4gWDtkCAdCf_8Zd+R#mD+2AcShG&l5s+KnKiNV=HZA+}}9tzcbtq=$Lf< zfozE?!TLYLeWmwJYMw=CPC;-BagbRPBQfj|I_9wJ)^_rGu|2UG*%ls+gR%JiEC{+?f7 z00-&K0-L1=PT~nx#x_gXti_GQ2ReHTY}R+Y1xzij$Be+BL*3g09xxD)9_yKdlUXPR zyW|wue?Xm1Dm_mMsmBk}LykBs2J+dKnnn8$+yfAhG#~{5t}hMh19@(u{rPa zH$nViIw3HikdRoxkBSw}+<5-7!SARbXF*nczPSbQ1bTB@jOVDKvHpWev7uwo)eRaR zu%ohumU^6((>xQ9-TT*S4~Z+8PD&qHXr`+XY@pSH&c)nC)5Y$sKnR0$1PSmCx(yi} zIs!1-IN+)*6^RrBRFJTNenG_0tNtl)Cv6eW22uml`D-~wbV{LlN@$89Y>aUbr~1E> zH7IAewK&bG#c1BsX@L6=aR`kD;LriAN57FM)X3(&^Rw^pJOQKz3pXGu!S{bSZm_1K z9-2pmP=0JZ{v%{29g=P)AlZK3cl-t7?{*#nU`|0W$LE=Z{9n2jedqOmI;VA|!ylW6 zhh{n*p3e9Gg#HSkL;AOC9vvDtFxkJm`iqTrJDqBQ8bVYSILKpJL_+}b-=vkmBtR1k zaxoU#LWs#z7N%(IA-c(fG8S0EDX?@vbZp@Hb^8GgEwF@h0DaIRLr*{1zzl%AKo-y| z!ot+feDMuFgfc>>0v^)$sd5-?VP-->}!RTiq zNJc6q;BJ)dVx|fr3Z)a#?Ic7rceQTrg4@zeR?(txJ;*CpwUpkqByfNr@h#-16S zj=o-ULhUW2qsta@34VJA8RRp>(lHhY_Wy9lk%6I^PQO4IpnLv*p$t&#f06n?Y!sTp zQ12E_eRl8--9VhXm@;<_QIl>YAW_n707TeK2UrK*f3*Q?KrXNWKkhPCg4*BHPj;XH zphL)XUkI{^;F7$Fjm6xbhy64|A$q+Z|FHl>h!89>kXAr5O$%!R{wn6LZn7SvQ-h`V zyW-QSm$=3X>dvJrbYh^F5HTdOA6F;;uK9dA0+{mOReAx!U$8L8$DjX;R{;2++LEI@ z7psu~@mr|ozeYKd4)DiOo&&i7YJX2ZjdB7&hmh%05wa|ZdAkk0F1E`tIM6_++X`v{ zNkf)G8(isJZ04>3hapfi3##jZ8{3OjSNyd)P=bq#Rlf?VLo0^LJTp*3f8L-j9+=6X zw=Ogf2ie{wJz-GJunY(oY8O}sNFLCp_PgH$)+Pe*$LHqlQdUug4nem}8vz95+o1oJ z{_@W(Z~*!rY^yrqjKTI3>WaP=h&n>gKQmSfWdC1M00=TbD#-<&g(hk}`2Jy{z(&9x z4c(0#0DM8UA@R`7R1A2%LIaZ56x*PupOp9lD1bB&x-bre4>h*f zpJ~y_|MXKwXKu;qImcKrz2<`6wL=Unng+U|QJ7CG^Ovn{b(QCDyo2o&1KUq`q_rOF zUWCjT!`z=vsDOAFv=3%j_EV*~d7(qFvNBPc7d6zvDroPeZe?sXf9)13Rt3#`Pd^E; z5YYX9oIuQ=9Y`K@Uke%{NkPOvQYEMbaRK#}<2%Op zKZ14znK9(~3U7&k2~c2X0o+v6{%!NQ&p@u@GSdh`_{o&&JTDi##M=4>L*`#f#}&YU%Udw6e6tur_8VNpXYESi8JbW{S&%Sd?o=NJnHRSxV969rrC|R*ntV z_VG_y`-&a9ViI-@5vm=PRa0|fhTklT%Ogpw>`JtN^jTZNq8vUDPh|zMAB@!Jf2|2A08;-7jvwyV0FUBj7ymjwZ{8h zBa64d9$Yw!Fq^%=VrFvXQc`{r4t2v#q$@0ekxTIEb(jTp?-=amI8)czDvZaZ*HIJH zQ|I~}vsdpZq^53Ux^HIoWm9Xnl>2oG+ZB&^vjV>o<=SHWzH=J}*I8t1e{9y~uV!Ld zAG|8tU^?@RVnX`}b9Gw9CI6gj)RD&Dl$1Y=Kl z_B@acl_TDr-AAHm`c@Y)2`aa2CCNv!Bc(-G2)>lke&QmSIJI%+zE15{)R|v`Ns}7Vml}pYXZg zJkn~{%3{N5)NrP=t+e3^Wey}lJ&3iK)9N{El+kkhoBMc<$vX1;-qP6peNSnZB8yn5 zb^hbZ=$x^LLVs=0aGcQ@yhsnOe;Fr^+33qw&T*j>=p?+`7Zrv>RJI;hE~l zlS1NsS_fu3^@-&K7nEA~tbb;XJg#nP+u(z+%{fGSg-^thXzU8I`0WkhBVEjxs7h=0 znHg?wiLh;ME65)Nx$4(wF%@@fS#(jqH5fi~INP@Vv&&uq`H?*&a~o#lD}m?Xk-EIv zQ|fiv>!{tMPBb3KEs-j6u~^E&Yc@2p7KFsfW-(W>5B<<)y0^{SL6gOQh;YO`{PMVP1_ zHgb|B2{;P&9Bt$Abt=@k&_HoL=^vBKA&ixOM-Fl*SR+8KE{wY$<jl( zIcmqTqA_!hAXL)P}RxNcr|X zlZPJl9vY6WV;r~-7qeS8e8eG*v3P2CUvpy#krp!gs4oSvIk9-NvRdW~^NrEL*0apB zQMxI}R&Qaw8KX-{`}AJIdpdKnYdjB&T^md7o#yBZIzPZW?%%dXsng_wdc>}9jJhdK z9xd1q+a4e+&`}`S!C6`N(IF?oP~3G{!uE)V?!JFwWu=&8=TK3?HJ5TJym0)V*`u(wiowQ?^a2Rg*jJ~g3U5l&#BYoumNNLyX#8$ zn2Bk&A|oX?Hc>>|E>eTOIkL`(+-vk=A0E$ zqmwws|ISsa!+&j*B>OaRkPqwIvF@{i{B&E}HvR^Jw))vBveAZbOyn={H&Ly-jm$T; zZhVDqUkQ_R3h53^I-Qnna82GdVJWMP4UU|qGWnx@>Wwx>nUBZA~kTfQ1@E@&0EL*tNNS}G}TiqCXT1igL%|B$vP zyY00=X<@Zn^Y-eZu$D#%)MUz6IbHJr=JqV-A!i?2zFDM$$ZQ~(lp|tiB@kO?>u#U zFUR)79*c5uXUAtDPw6M>ipd-kTN|VKJ>vReYc|Rqg`t&ZL zx2yJ|aOHsr4({d3X~$N;z8w*waw)n>dIq&$&1)??kE;`AA7zoA;_f~{Jn;0LZ3_+E zN;@=Z8p6fX?;JC>_FGEJMPuA8F+oQhpLN`_C&P!I9(y$y*1Vlr=;0)=V-Z2Uw2iQK zXT0UMG@b#t)FPa%)Qd~MR zadQ8P;p@*84zwn}T-ND70)ppNsz}Pr`pWw+Tw{_H*4_k4{Elea>=AHJ5 zs9Ty%FuwW>U5&x%Gn=M1pMA}b>?N!Brrx|WnvW2TnT^SNh_#QJuFf>~rqvKxdX8%I zaUYMG*iIUE*+}Bs``W)E?1EXgLC8l+Oqq_7VBCA*STK3_J=!bR%BG;GeDNhOa-;9Y z?5K!wmU@^uV=?@_S5Ue^*eJ`!FsjdI&g~e#I9qXC%2f*X?w%D~dNIv!bA^=sI##$0 zDfiBLAw0r3o?c;144Je$g-c6bGf0`4dA}-EqEhoky`#eCEjvFyAn*07^b}bXU_1@E8J&k3Ru5fVgN-xZy zB2M0YbNi|w<2<=yRmo?zk2>+n2OW3zpW;W>?_vRQADzCL>>lks z9+D}V# zaeTK0!r^r(WnkIJbjw&?Km8)a@j=p;e66l;IT1S89#U7P^GqDjwc5?d4ScowjtXg# z%!4~nSqApC9NN#^a_Xn7sqz$8?IWEv9!wOg_Mmms6V-{7>4B3teHkx1+K}hA+1F7~#Uj!8^;5cWBS<*ENn+6oNsA=>rt%jj%YEZl2>))_e;-#SPwu(~dmD z`FT(LoSs~uX~I}PQn?zZdSee$Qip3+(zaD^#hsmAE~h)A$JLoAn45aedlIv+ams7( zi@WmPS^fS#N_WZ=#)!zBdzO(yd6r|QU@FA!+k-E3?tCCuIZEESzq>uNyVm3L@x%KD zW+yrtnaDMF1(30utF=jE0b~yHmnlil#^gR`a=j}r(tU=LW^bJ}D_7T^PCkSov5;wj)E70_=77i$a;dLZn4O=2H&Qb_O*RVG3k$43U zQu%||;H{kQAO~bd0%vx$J5)Zq9_|_B z>d5E(`Y|8r`D=2tOQ!N?)_Tn3i(JPMisMU8hb-++yS_%n2;}7oxN#60$9GZ{I_lJm zHE#Q{B}`iRb((CtRL88C)fQ&(rDP=0tCZvXM`j%CoC;%y$!MPnyi+MTv+IW2;B_aLRAez|PWhqGZ&j)5+3OqLAkbv=8{?{Q-^{3M>xhIKoP1FJTeaxW^x$3l|Z%r>0;Yzq-}4nC=}W)DO0(4G5o7u&4*rys5Qbn7wNm&nDgoF*$kvy>>3 zH5}W4N~zTyc&Q(DATibb+w!QeRUhEIMPm3j$8__nB3mCOs`cpXd|p|CkFXXV6?@sW z!}U|Op&Ng2{8dWo8^4E+G+J#w-mvX6HzJPgk!c&TW*TLa9ra$1a5+-AQ8KIRV6J6! z!q+>rY}>$9+P)=BGDjN90{vs9MQDR+KB)?I6JmpPtRj)!+CK6ap~h^bmt)0QKGKov zyXC@c*^z8IOl0#{o5nWUV+Ie8rSc`SIJ=;2pTyVM-Wf>6$TB+DB(5H*?$lDN@A;O! zuQPX`yOXQNc?U`B#Y$wy9-o<`flp+=bm^zPvuo(!f6;&Mbwn4<_C9;Alk4z~*w!BL zQU%^d#&E&VHALF(-S5fbb(K2}U&1FFPGW*aj77h0i8||4mKig;bv)iMBx{7cbHi-t zOU;O&F67Gk$be-;muU6|vp6#bF6ppn;zYZAM1o?4ChBpAQ-i zCFhjL&Y_&hh}jj&M`uJi#sjS=>9!j>L)b3%l5}pfBGVscsy(S_Zr~4Y(o-5$WeyFN zU;W0@U437tb#C&8lF`fM___dEv6xvA>qbKq&5#AAq+&T#vL_mWE5K2<)kf^h4fu>Qhf%&Q(^hLa;`ukiE))n3ytKG*DrYKeRviY8#G$H64B`lAOk|JEV?-CK@dTkt?@^U*8#r4*EDpVDtq~UXd9XY`n!jbk9=7`{D+uDN+cp^rJH}fIc z{8kkU3ayaky!!z4D(^~hpPQC-bPsKC?NoZ4)BTsNmiFt*u`b?I%cY#c6fndYM&}C0 zfrALHGL}ocH+aJzeefHUcKB?EPaK9}OR=ewBGj;)xtUaX*@L792aU5*^M`s`}0d3 zY@G4S@-(VlVVpD@mQkL40cGh&cwua?r|$1aAD-&&?Th=msdFMiv9fWW%Zom(p7f;f zNVMGH-j7Ldg9qX)t8B)<*q`mnkdd)*8CZ8HxHznG1PvkI}O9<3i(yWf)=3?O-2IHkUGfwN9((ri96jA$_CbPb(Bp%tJ_)G)q5>{ zV4usy^Rzm@2dY`Z)|WVpyj|V0t!uA?EAGjK+Bf+|f0Wlb-gCjH~v9-Sym~y%~*d14B(}}Kq;9K#GxLQ0& z*V9m1sl0)#dnJ#v>^r((vN=$%Qv!(4B#pjwT(o)SNNJg#I# z$AgO9-W-ax8^%QR-!jV89m(8&n;bKObIx#)BYm*jJhj!OXCQ3;Ce7BP7j4j ztY;RFv)U?{#N3nXu5fJgq<*GaqNLK6nWscN(bOe#o4br0?DO_IX~~y^)26{UOpY}k zSw;ScBwdP+Kg-da11n}U6Fc$74(XGyo!sc((@l*Lrz#t&B}(-knzpaaFQ()NtCXZ& z#J!{)w#_#{3~@+XpiSiD&x1-vM`9*n~OT+A<_K8d8mR<==;$w-H6`svi>d@O1P5cs` z{*@@t=PZLctQjkNfwilNi-R)(_Cjkr;-mh>z2dJLTso`5&Y}kT$jYv^=F?wN4!(M3o>)X>AJ*;{I5N0q z<#vG-OYK;*Iv(n?59_+4-V@^G6HLDLUraBj@j0#Ut_iNfJyM8T={#(_A^`CysEXfA zLegW{zbjf+cXhT8$CH(U@}}I>Zu8YRLYj!2;^_^}Rr}>zP7brS(9Y>*Ja$ZVi(@2) zFo*YVUM_g*5ySHb@OaOj;5JGQ@1x#sYD*{cwb*niZfD*-{G@b;jo~l9oDQ-=Z^orL zujpcgb$2XR4iCZ%ya>RzRi7@cd;UIm#3;*)<|>GH*?s72Z1C*x)7rD7v3?2BY{tP( z)cc}#c8Ut|Wf)1B-=DfnkaLW3pd)88_DP4Ur!#LD=}8h-&u;FZ=|ECwH3tYJPN(4= zBfrX?s>Z`q+^&al9H($nWd{#S1Xb>Edx7${==Cad7a8#=4nbM>5By=Dw~ z(i2wUHg&Z;e`W%BR%Xa$@IF{x!lO~@Yu+R5`jqdE~y>px= znq$0EzS8jXP)9w*VmNC*RHmDOY2FjUe7fwaEo{!Wq*|ElUazYz~Jm_ zf0Ct?+$?dX>ggq#ntZpQ||Hvaxn1?$h`n~osL8{UEaq`(xc;~IwAn`uu+2K{4 zRnKiHLQgn~qhMUVEYrQFwX$}N6xTsSH!U4sfjI4>{G zb5l)w5A&GO7`u0(jErfS4sd#xLW8XviA@tFod~bJ?0zbr=n>>xpK-JGRV~*iS$vX` z>!-Zg52G9JlK z`t;W`iO2FKw3CiB_}1QO#&C8{p;(uyX%~ZST-59Zu$qHtRooX}H~> z87md|xJT$JyZCVR&ZuM_UPWRUM#TZ~YFl!Yzxh-)`kNs|p&Dt4y@3x1FWv-Oo-h=A zDX5!Q2Ubzu?EbJ0`Pltl731KCgZM0ihZ6()2q5r~FR=pi?!>$Nbh0KMBf-^@u)V!( zSorAMm9Q()42Z3^ltkLcD;<1Q@)D$La4S#TptpePY)^dnsez12t09GkuDup{ay|JBrS>g{_EGnJMhh0Km;H_1M5 zcTp|L8R8b(twgrI!{j{C&PsjLT)jHb|Jjr?PvTdmo6#uy{3LNoE=Mbj`>O&WPav?+ zxeM0>oLN1k76xkjOvR}@7v&GVSKpV`ZGCk1l0+gi*^2Bj9WS9erFE(9?%@4uA>%!< z9kP`Z;oGwW=WYAES>E?r`PiD2)wxHn`@ZrYyH9EB= zhw86TUe%ggR(qk6qh^yt9;XG$No@*G6egpjMKW54>`>*IQ#{TYPZZkMO+DXA>ehA6 zfJ?M{KT)CCR0wnyJ^Lhmt%LKM*@T+$d#q4;wlhZ{m%m1@WPgV-|B8GR&oc58Rbp@{ z(^Hhv8;?xxaGt5_-G`9E0{$JGgU>9i9Z>iX68~wo65iAU==V9q2csXAJZSRDF>w`1 z9h_z26%R$Jt{q>F1d5fdMjlL9hLH-F=p>0GJSD#Rl-pquGIkQ5-&=l?o2xAO1FpHM zD%d`kj|*PmybdwUW{c$GyYq4IevtEFSWMlglYN95X{P7?6C8s`V|U8|GlL+O8FWC* zKrrW&sjV|UpjJ$xS~Xm?Y8%p6Va05={2Kem{UDipe8_WuCCr8Qp29(GO72FhY zbh9v}ZqICxeDCPo*vv-yX0$CcJ}Fo`%b*Ka4mb3p>BsEu<(Sw*%6JP4uJHG&$}X~J z!br|sor&0))G#<16jac0vg`J{ioy*+Sv6ORAHu8R?^c8;B}c`#mw;g9x8o6w*MdCV zF7X$_tE%*GDx;7E7{mLcOmasf73KLPTG1Q>$L63wqA(tHSQgBvDG}fMpku)BD>kY$<6#^h%^|Zw@Dbj$!=mY!uxPa%Y};OO zdyD(1s>usBN^;_&hOV2NZotuu9HX;rwTL3#D;_o;Sf1LtF@q(zzbS?|u3nzhZ`6eAU2kS|f*ARD#9U7T zc9lThv1dg0WUbk!gu7p49RrE?tE(-DdwVu(*bIJ&;m_7a*Rt5r`bn+ZdM=hn#kyTd zS#NL+EI*>s6P+jT>=RGt&&76>!*Wuari#0~Xs?#_n%Go)I}sRW<}o_@z)fEuI9Yu5 zk^SJNWMa2lCIUZs4j#s<@k0FlYp#&XK29_f^TgVdda0>=`pWf=kNVbK42>VQNz1CD zn&0B*^5R^d=o0&UpnKFU{Ar#Tq49Jp67}qv^rn%qeVa%pBAVbQ93NSgI>p2%d9r0& z&n~aTVp&D<-_}Sq-5GL6jNijF8z&^tx?g^{^QO+f;GI~2mej{8LiY|z>WnoLjH29f zw<2gQc4F5W*O7_6IZ9D(ulIj+>WNNq5Yx3G(4K509gbq8#a%o)Ue|@rGAOVy@s8DV zI(3;Ral@FUxa}j?F$JXK&ZwInEd^EUkwym6d3Y%-_oPZu=;W=z?(T`(ev~>cF@%|X z@TD-jx&iX_(<0zBe0$RwKgqW1tKP-OKgHh%u7us+A5W~x-B}*hm@+t&z^8~zm_m?MuUS)S?k#DV&1N ztDVbIHYJD!_Ob*^*{oR~nHZ?g4wEd`#0|dQ*b+SD`~WpHjB3ew#esJ|p1Q7fr!!1A zvRh;PLlWPN1^b33E2W4UnDxVoi<&cu{lUwEZ8Innmv*LlPGrrJR}{w~a!7>(`2Br) zf|ufX-rgBDZVKWxOIJm6XXf6Sq(<+1S7l-y<8;SWgw&a|$yqs}#WA*(a1l#*gwf8- ze|^{IZR$kqc9L@@m-FlB_0;z2@w)n+7X9U)1mDPCycb`nZIO*G{#;skYho?OY{y#C zQ|>@V)3R(|4VdB@jPNye_!sTL?2!ldbrDgb!(6o&gOzy#CYyyQld)N2umbTi((bO; zIpkus)$fdiU9aI!x`|!#4=4Ktubqhqe4fFGap~K|HsN=kuOW*!ZS1*y2iyda-N2H0QVgd|I#EV0bZc$8O$Kroio3;zqgLwU?cT? z;vtWqw`ZS6RD_xPWm%{6e1W&+%TUrSU_*i#o_TFeNBAQQWn-EqdNimGEK|+Lg!2!j z7TyyUr5SuWJ27-F{ZL_fLSCYOCpr>Pjq z&=zEqt3lbc9GN@~RD*(UH4)6wBZgfZZWRiZx|~7hiLLxP4^VM!2ay_; zPAuXj?eE;O1yV1I!el-Z#yi)uX~KK>T&eLFqx%RM=jBN>X_4BULiv{) zHzY6=f824Y@q+Ri<-)qv^`l~!?4OEE9M0U$($y+`DM+B4$$5$uzb=?Y%%6UK=73NR(G+&W|VEEGvNT;36fgy;Cc_e`EX82r54b zC3!8czie|o@v=4>W?PSkxPW~EEA`ol9p!L^%Y*9;T+Y$1u1>@gzrF^s;mnK&PRax8 zNOFmIR81c&KrOk1^6l_;%j!h$b%`x3*=_yAtpfN=W$I)8nWno6ZXuISZZ@A3*9nW{ z+P>K|(lJoE%c;a?pp3PIG83b>bEwQH+i5IxgGK?26nKQ_m%uA2fRC?A?3|RwUW#8P z5?zhx9Q$H94QojXQY{&>yAs(uyRm+{Dk8z*t&kk?EnhwD#b)OudoGkiqtcKF@3?YR z%yx=G4PQ%_VYbh6Uagr5lNBV|IKQ2J@0cTMO$0-+Gd8B@b&qbfNKJript>qN9!Zt= zb=b^-LF6%VDaPs%6F8Oy$F8p!7uvj95Jmu9g>kjUTe9UV?b8?b3@dq^g${rAbvOr0 zv8=tm3`6UbT|TVwdMbjd$9kXy*7s<}q(C2ccigNq_raUeFoHvtb><)?U03asnbhU> zJGGLY<0N7>p**%Utc}U9q~u6X3k-#@D_o6{72FYZ&?lNpa>d}i!&IW-?v&+b`ct06 z9X!!oYCfLvUqhb2JJb(_+c*g0q>^_Ob?h0=%6%;JHB$suRF}$CW=Okei+dX_R8$vI zcE5v%*R$t+W^@xeMt*;P`zTY&q4<^=g{b&Q ziY0ngT%Eg1HkcKBsk?8D$V9U0FQ&vW#PNMei*cW7G7gnU@^M znY;Y3ZoEXx_SNO2+iRu;k!5npMCl0DVx6kXjWD?*gR;ngYUx+>z$l(Lnnn8HIPz4`@-c(#NW6)lOi_Q9kwXA8 zW_!g`Dy3uAROG-eVM61wdz(HA2=S>Y1>j4J`ikn(JSno#+010 z;O$lQItt4KdCD1_`ENyZ+BA_ zK4+gyC~m(Q6q;pTwk-98gpXB4-nq~oid9kzq&a2#qFdgh00Da$y0K}Zf>yL?!s$nq-w>vIwJ8KJF3{fGI6%M2|U z$l*~}^4toY#*^IA%#o@Y=Vl7elJ{{~hvGU7dP1YcVe%c4y}FvS%!sHihl2B5%GEvj zRZqxx;nv{?NSU|KT?7kBo5P6@$E#wx;Bw;eFs5MmhMegQ4zUkq1h_P1Zt6Z%-(dFk z%^f~7vsZn%N1|}{9#zwL)W#7GiThjf&O1_WKU$) z9iA0BAvJJ8+7DaLLPpvMMzxyWT|r6xsL_dTKiL}|iAr2v3`5>`)cG3gG`e8#qiOT=c`-VHb(uCQiK`zy9GN=n-l?6@Kp9eUBc4B{blH$?)%Mv^ z$#M5fDR+JIX<_T5^0mK~-^0Gi5H42*tKd-z_J>IV>!K(d)(sEqQ zhcoEJnx>|qYe`{SiX-H+^sl;5hSXw?l;$V0Bxo2dOYys_l4t{u9rs^zzrVM?->dPI z)?t_}=dFy&6gZ>*$*GtMl?yfaMVbzB~qIxe2=9dBN98z~M-j>^8KN#Ndy4WxM*k6FI$ z>1OQ6cIw;4=^dXbP}zvzNDFl>e|9pQB-ixeP_h_{1z1H*by7Je|S8GRd z(oBip#;DHIqBzAN=1G-1C@(jyTrGCNnx3_j1~Z|r9}agE8LA|aSEXWhjb|R-|m_a7UEk8?h_9s%Nf_=f=hQ`pmw(I?a-NB`BR&(0S02m2@P_g0{c7 zzILLs%_}SSVQz9szJFagzU=;P5|1SBOx`wcDh_G?P>K5uns=xmq@!9<^IPMi} z-&IU~luZap|AL($)C!KLa#wP2u?DFs4WBKac{GWwWbv6 zXD&N#JJWxjcBV~EEIqVus;eZsX>>xe9Di$QeWV~s)MxO7bnLr)84hp5l2kTg#jxT~ z6Um2K;cvoMboW(su|~thS^9&d)3KwI-Rpo8`|$8;LFq_$ z;S%20xvtms&fX|_%U7Dj*5;z1+;gA=Zn@V@{54+SylZdXLysQu&0+w>fRXpd?s}EK0qO=>V?raNGoB@A9U}R;pbJliKq}0j7 z?w{1`J!pYBG9~Y9uQ~)<}8z z_RC}LBNfNT<9CQ;S5;Oz(n_5gRTHnCVo4E1cqHXh9mFChquBZ;pPAP0dnfj7_%10y zJlx^IV7Dk~FP-)DHugjkC) zC*;4*fn89zVuA{5a66?y*50sMu-~Wr5emiC_pMlnf=Lv_~HVFy-Da^sxZm;;{aOwjS37f>i)t z!%?dyszWis)@;j65jfvzi+)Slt$nnU@ZnX$yNbcuoreWewxSS$oy{Z>#Z|OPDK~*K z{a0f)NMx|DUc*PVDc!qoC~-5|me}s>=kdE&9?&=sof*r zmODQ3wYin&&|0bUt|~q`U+!wN{NBWq0nuM%4?eg368!9mubY~65BTp?!dre=b((QT zh%w(3uh<#(aJN3KVI}M4xKinYH=J#Zej|mt`!Qd;AKUK3Z5*F9h|IZ4kM<9U_uN{oo;$?3yBhb%{Ct9 zB_^a;taJ2}AX(ZT+pJ$iz5TUfW(R3vL@uC*Prk}AvB%L&li0g~YSp!~vnVo>l(1W# z1E~?9T-4=sC7U8zXK*dW>1Kdy9^pWmUBsvT96}vFhM8T&li z@9gvV{eJ&+I_JDD&)4(ydY!jz@4fd$w>Mixd-r9|snuP?OD0MbWtDB{QPim>NU-oq zfr8|XbI{H!bJ{gkTi?hOFFw7NC96{(=WA=!GUxkVwu{k6>cXw{6b`0jUi$lictXrH z^3U_iE=^;J+JE2mFN7J5=1LIQep{I5(NBqy-(_hnI$0LW7i^?zYs=-}rbl&x8;a9E#1c8L#o%i)1z~TCEV8~h-0ATIcQq!y)$MX|uGJ=tm~P{% zL#J}03+u$PT#16*B%BH%G3sF)5q`f$OL)c|yL36-*B$(6Yj!IELMz$8o3PD;A4yts zb+74Ze;{V!&#y_C{IYC_&!WH(B%zbz{-Pz~^jT_g$>RS#Qb=vsu7rI_P z=Re^=A17Eomlv$C68nmAx#a%-O@i{D2CN#u9rB+bdXpC?Cf&@fe2(k;F#c)(II4Mjhq1tDR26vz4~cNTWrTK>d#5TABBw1CBw#k zEgufysu%v|rV>B@BRtF43_oVP8DdIs+uCBD;4=j)m7O!|?BA{G*pV31CeS=0a1U%X z)Pd<~;=%)SFrVta2f(wkEjjWPmx%XKQL~wy8}v^Ub`vOX?8!)m3DPjv$be=d51)~p zwH@_WB)o;r*Hf+Jm0HU5>pQSR<$r&^xGHqiZOm;vx-lx|;`sdht`&6THb$Jx{8>)3 z>m~I0J%On2bw>#)a=tl*!x64Io>4wAnW{S)rbn)HF_zlLzg?7?a0)|C*4pOjY${%% zN_dvf2@f~5>(5GJ=j92-Rkv4h^ByN}s$Fo6jhxWatUJAwRD-PB&FAmEc%*)i?AH z;Tq;XIm>clhmn>I9La6t9Lr3p`n+jBvxYs2cWSzf(kT?{t#*cLzIi_KNC^uv&(QN|{6#vXgjQ&SuH z#8Z=!1S7P4F5*29sZcS`WcPFU(Jvc4QzK$%>bCDHj5#}rD-!U3R-0;T<+gQNEpB$S zUpwy~?Yp5kWcB7$v8Rqj4xc)3xd)%PQAc1(-hhwm68z4u5U1Xfv4g>^Woo@w_wm@v zE?)TAU73+j>o(|Xw)P`TjjaD?+K#s|O-E1g{JV zr|*qD^KomfiTQFHMYG`+-|HCo`cvwqK3rh&bKCj8cvqWhDTwjRndQ?>-7eXh;11{` z?6#QlVI#=>(Q_o}=CfHV^@#Pa{ONZJDrzxFU(q?9pXLN!w!95~B%Mbt7mis9*ry(4 z+5`_b!0w+%ta==0!)Uewm2bQ8SGb?ER(A^mLX{B%*SmsUZ)_l|dqW*V#`%BGMoA6Dwt#avWIQkKrRgLW^Bp(r6P+=~Nk)dbG!bkjFWo!C&mRa=~+6uhg z&-s7vW{B^z!!iq7Wip95LwyI_pdsNoXd3-$ty3**jBz%D*5QJx`2u(`bykKvcObFqGLv4u5M{d(>VT z-ckEha7SIzjFi|*7~Y-xPTx1*jMU>|A$;B1l0!XmQBBo8el2j|<08L^@PI2F!u$zFSLucw443dQ@yZrqo+mHIsDU#wts|=Cm-^N zwinJ=wYH`(%wxiuL)5Uk_YGTa^_5K7m+T?@8_e4s_H%fg^W$>Q@Qy#4zV!Ynn9ojw zRGyALb9n4g;bL4vM`f&{O2sWqn(d3U@ri0fFN~=L_iJaQ65Ny%8}@9vE3>h-;TC1< ztmlpNwH;1fJB0+|t7RTL=AkgBs?jy7kt)s2n5f8*k170Ru4Gi3Si4!kGjl>@mxE8L zO`cBtGkmi4Gs_);-UaKTiaFgmxZt0AAG3S0s&vR*b^c~<{8)B!aFKLxRL2x_o8N`f zlU<(PR*bc?2N1!WgG2XWeG;CviLt}hIXj+FNt}v}72R`&S^kiLR%G;%owkF=SBS^o z*G*2y78&>dPo9l=*3*MD_SI^tq(c!WrlSH|*eQ)+oiA5sdV-0t?jOh#kFclu$MvVu z{iTaA9Hw%y4etskpOirM+5#yJT2cO6>pF%T`^}#9R-CJv40u=?o4DS2w{bNb5&gkW zrhqD;8B;i8($F!nY|>CD5jJB5+h*G`@N&Vc=KOY@I{7?BDZdc77}aS$CNt#s%r;xk z{1D$ABX6fa;FlzsDC-DjK~%(EIAh~@0ODMv>RmJc-NG00@GDAB)*X`S#!qsS20L9f z(A{n9OrD%3jd6BNwUB#x7WM+3(!@13cI(NB}E*Hujl zcs+V6(9pFA3XVSHr*rN196!8h@Lfl^GK)I+N;ZNVNWpmy=i9h$pxk8^PE!@Eb1zffqo`u>&sC!S(%Xbk4V#9#`bg@oE$rgdUUcAM z{UBa!V&9JS7S%fosUwmDkx#4%*2XABOS&%S4Oer*3oOW zntCWU(TyD+>9#%*uyBD5HoRw19q7&{b;0tqALzQxu;DF}+zvyf2-O?G{H2ac)y(R@ z5hl~d#GeCuZ*~Je3A4P)ZW#gN!b?+uI4_F&`eVa=Z)Gt4NwwEHOE-p@+bqU#9}BbO z6!kVQb4k7@h}3gDL7-D#IsFqllG^mAFc&FY!b$y1>hPVP_*)m~bC)C&BxGdUyXO{P zfUT2S?$n%^Nfjz9uPs-EbdI5QWUTc5_i$1W7#{Q+tD-ENIrHw~+-Ac?7|Wt4;T&qr z@B*g-CuvkGR8}i@lf{yr(Ru1ohYn!Hc~r>YLPfW*J#_H}_9D@0vW}7QufhC>GZ{Ua z{%csCyP1*AYHZdxGRYJK=Ksm>-K!upwCA~>TIZ4~u4VV65rvV34%Aj-G|kvC>Cl_l z;CUFVO@1+fO^Tw1FIOG$rtqoZIE$Xnexeno5V^t#|MrhR?z<$qKbM$$;3#PpXH$y6~ut2`3BYqNj{; zHczH#TOecXAh+ccZ(((T%XiCK%Y^Hl#8TH5aAh9~RfRym?FJN&*gw!PiCgd7_|iD= zJRK%^b$fPfSe+sItE=CTEswhW#;gS^bgVLN#m%RR$n7*KEB)*)SSLC49o1+iql?v` zsEv~}7}f3=E@?TVj-fmqJM;&}r&`TFwfu_akkaH(hlXe{^0mi%^4B<|@-KpYSO|~m z{jKY$Glj5stn$sjOOfmAq~$4n>-b*nF+=S8rGPK@h3uTqPNyk!9yRV;Va2|`Rds<@ z;qO~VRD?5>p0?eB)=-zvmWVCfN@30Q;5Rb~!mJysGd^f4-re=u5A9fL*|#0xC1V^? z+_c59g^D`Ho&G0vH!UckIz^~g$oS<4d@8B6*xr(EEaRBGr;AJaxhE67I2%czE4zfh zZ~GrY7+(F`wk(u_7T|S`Z(l|S3jF)p_PPoye`<`}Wu^)#cLtWdmKdXZ|2k`ijh-mf z&BS6NsVZ&htV}`}k9w5$tN(;cYbf^H&O zgzWA$%KvV=)-k@Azd7lpz{>XQP@-t1u18Tlq)HPPp;RpyITvnYi9}|WO`mBZ>6U;& z=!d2+vByNul9(HucJ9h7D0zYm9t$6y-vi6@T1f`1)eVyb;&!^4r zQk*wJdq>2y!A_ke&7gnq+w}HsF2){`tgv=-8%fxh8=rbf$}ds1g|XR6NdWCV^fxSLf>!pQgphcvB_BbCET6!{w*FXld`&=MsY_&!ze$kJrcC*EYsBB z51adDCz%gM3j{y3*9GkL@NUK{fPaMsb)?3N|5(7e43E^zlx!6u5Z1pes-H ziOh!7?BrEKp|#1_=R?zNR`>z`%!$a?U}3MpozIg)7z}o-MsH9g=^Su^2^{{r$@T8y zuJjZdMbS{ed&jSJ+~B*Cbv-4Uih1Yxb4sIQqUf-HX4Q=u^jFql!*@;(?y zM!>w`yrioOYQRf}FX#vBJZ8$k6A(!JIqCr~u)%ed`C1Vwd0%=8UnaSjKHMN%a4-&E^3x(>K{fB|5(wcCo6NT7IQMc5^y z{-4x2K3EeMQT3m?5bwN;(Id(K&v_4U{!yqQ!iNF(YKVMn1R+Ls;Q%+8b!Gmd>hmu3 z7A}Kg+wr3W2j+}vdv6?bI?ofP5t9nmF_~_Yrn8_OQm+*7dqH(PKKdB+s!QGnUTibS zM8E5HQMvVN>5ia5AP&>a_+F51+)WA5vYzL$Ik0-j=^VEPgb`h(Bm#lz-ZO6t(nWuG zW*tzQboXr}s2uCh`CVXE_?YD-1cwafw?@6_3~*?*Sy%!1LKlCErlGX|{Fy$&!a$53 zy~YG6IvpceMN&iu%qr%_xah=c{tGfoa@Us-MNe0m5}jTw*G(X6j?}sEo1B#%Mw{+j zfO{IMUx0FYQw&!yrlkU&hb5;PnwV&DJI*>eDK_-^f z@E_m7D=DW_>S~zZH;N9TG}f7NP*qr8;s#(E`rW$+$l=*rfo&{R6C4=e{zsAK@G2;d zvqHdppA5|KODEmr_CfD%4l3wL!v-6#?>#jrsrOpGNR zkdr>0M?wPds8PjPz$5$Xi!LW1W*ns;%3t1LDN$<-zN-g&gBL}F9W9e*gF+m(YXnOo z#zxmhk`4av|6Rz#QYMV)yb4CjLl9(Pbswo+)Cf=!yP~r&DF$Yb7G1F^ zs5MU+tr#F)njD(|0;HYdHvoI);wvC+_~VbOFQwD8tc|u@d!%N5E>TSWaaUUYeuoQa{rh7yM(5fzj*&%j zE+v^-+SvHr`;$b@OI=mKTS2N_T`;BtrjPpbQjsng7wLrW;)Pat?OoL1xsDS`xEh^W zBeR8{w4Sz({*NZNoKY+ne;>#VjLU~3Vx6>}YK*=;>{}IcrBdmGXYiamGRzrehgkv2 z?#toMC~g!p8t8COjfh;`_XG}VmekggfYhAPBVmCK|C68*2&CuiSs4CW#q@D8=9y-b z^Fn%OOfVuZuzw6WpEc!O!j{5-z!$&1W4uXr%bOG;bd}x<8@Lr}W4n(27iyEEt zJ(<&_J7>V=TG#*D#X*k$-B#t+rjYYU6+1{rK&Y>LbJsip#{9_TJCQ3@%=|@vku4ja zxB4p@#a4LMjk6+fr`M%L8FvWZ)pRF#&S6MlR~#U;R`}ik#P#RqHqoa3U)!?OdjJmU z>1$6gF9|TL3kA0FmDne|04|uxPLY^NjnRYw$ig$nU%-aG>*+jxIGF0{Bt}mK&#h*L zesaC(j0$A#Z~l#*?-}d3IGNR-$NGu?%bo_6`tttXI1q;MSrRBTfAsKqBDf`X4pw-X zBvokTug$;AfB{Nf7p)nFU^FdGrh?3eA|ujZcyRn_=CO_xT0uQEVzD3rq3EC7CQ#Fh z={C$!5LQ1Wbj}czQxnr|oi8G1jow1HH34>19U6G;v>sYH@c}~KsofLVgFVEF|RSqP;!4Bd3oP*^xxpQg}DFPNrOOKvqaSco#_q(WI6wE z{;x`H6yqa-J;VE3%1;6AW+?A?px}D*^qaQ=l~#Dca{YWyn^w5@nbIoeW#1=&GO|il z7w2(*p8nb`IJu;XsT1rg>H_wajjeJ0d5%Pl{lFWZc>6!rE2j6oS2+miE;x3<7(gr@ z)0(#cVRgc7ClmYg6i$KX6IC(q33r@P08#od)Cjd0f=;b>lpJW1iULjUuRMPx8RRZ~_Z3_vjX zhc;G#S>?$M099zxZ96QO*|K!O9$l+1hYgCe0tWrCOq%a0Joagq1vsweKkW%}1yUn_ zo~(f0>gSPW`Jz_Lm-t#d0~|kXD+AA!np=IfY4#YXn%1I5E+|sWt$RIH+5LH;l901_ zkV(fG$yQzJRzDS0mc2>?u*{eWN6`U8RZ|BeaiGV7 zKiR$Z2kS(GJsdkz+u%9#GM(Dr9Bn4haKdcmduSJ{7OAll_bCii|7y^0yTUI=w^VBd z_~~?r>sPP2^W7Q#;V?w`+D;@+j%FG7ar4ASPH(;Wa5+<%ZxelXg7x%+@GvLzN1$y@ z-1;r>Ci$h+vm=kKUbpZv)M@Z3qi;jfUkW?=bH{zo%FnKTHU8zQ>~vhKvD@ss8T3_q zBWG-S|CoF*dw)}SpmZH{Ryz0TIb4V-VHo?6uHdHB-%nm>+h1o=a%9weqIJ&*N}_hS zcP~mf2V+M0_OtL3Iu^{$`r31J%)iz3m>61$b-j5*EweVi)5!?oSh~My5Vm_vs_(w5 zTHRb`=7e(96JApy>(u$NqSJ(7iWr+q-&e7fa{AX+9joGZtHP?(Dv9Fxx-B{YETNUf82JG{OXo*N(fM)Ieep&GDBn&lGB7%^_v;ZPWb6PUv-mjSOgWCj zA5N$hBSY;?(>VFR#uSeBt8%=j--Sv<*qdfz$uV3kQ+T93J9pNV@8I5;=I#2(CC^E! z_S0<%RN!fCg7qsYa&YB#=1iDUEA{XyUI~T|i>Gt$N|LiHy7|%E%@YJS$APD$)jS1+ z>$f$lu+Hnh@%8*(sUyO-=1H5fDFHhu(SP<$Hbw=?A*UW`jW3o@GWkaSTJ4gH_?P28 zSI3daGl$ zd?(*x%G>g8`W}WCp!E`2H{44<}ihc;7%hD^sIo`0sj~ zW*7G*7q*?cGJJ9TTN7hEWXxvPm*$=Eld99SZNfIiT8275JS!Bx#M-^gw%*zDJs(BZ z8*9%vr>T=?X6ok!Mfo3n%e5Sjj=bDRF7&ReIHd6~(@|Cs}YyVMJ5OQPo{Hu5Nxn~h>(;7x3VfI*CxZ|vg)l&nl5rhxvOD|2*Z}g8;t*!0q zmEdxh{sx$$Oz{lV0{fYqX)RU(yP6GIMW{`@Zrw5TX^Vn4_`RJ}oAbhQ za2s^1eZ+TK9PP8RZI@tXt`AZfZ*vU#h;5x3CJP@hjbB!mIX0&>F&o>`o$5BGoQST4 zgKkV%9>0f_Z0yEhz4sFQc(SYhO|8FNeQPeVbiH;alLi&N&ZK_)qcxk2mYTd}YILUC zyt89pY`t}&UYN`E9v6S$#by0Qh#UIC`7UW#Yo!uW zs>CKdE1z_pg8LbX71!ackj?_b4SQ~Ov%|S`e zSeJvAwxsTkp4T!@3{T!Pk9J9^iiy1NLgTcI@CN$oI43=k0JZF7B>a^=77$K+pHf*o2ct6JuOUXf!Jd!>&{nyI`4bfrl%(!|MrSvjv8`gd_b)a^+A@Ahi{6R^_Hl9M zy&J(b(8t;XMhN1h@vLv2_+A;Urr~j$Jvkxk_q}S8W+{1l_WFMlnlN)~Zbx1lf7+k@ z-8aRJw`A1+{?E->i9-EFRpq92YH(9<6O9S~S~Z-2dUCbpVTVTJW zR9%YLLsM&@eT2cEg-iwyd6cGw`L7r)gpfLR9Wcd<(@Tjls~hm*_z1z3vB{g+;i0P$ zkuZ*JHalzTejDvi^~CSvqhTr$`eg^3VfZ-z@4gMJ{VtAU(8CGTByvASrs6z_wl-{; ziX8bRbpILb7R=?RWQ9dnLLV+#3&&!(Qm+~MUX%dh!((PGIVNxD4_|6_xl&Spr|dPS zTgYW!PgH4}0Qk$IqGEWL9Le8kM(1>cr{Y~m|54o@yABZU%yb!ymN;W~8t zu|27guTx`Zvo>@_L9TNW#~-mTf0UGXF2l;$x%HUWnNwri`U4b);<%RK&I+VSG*4Gh zx5r-ILiHg)p9e)(gzK#C%K?;aB(U{F~8P9}xa7I_w} zaOZv9|0Z^_AF(yiO`!K8lH)!ju2|N&yHW6`=_iCIOQA<*OcpIPhKh69>D;vBdGjo` z!(5W)a!n!OaoXQ1x3E4*-wrLrVw^{G&uQE#*il{Xwi(4X-uDL;o5O?w#ABy&Rhz|G zjDYkx`k6}bI-dAu<*w;1)P;b?w03AaMW9HY7TnSj{s~j4v|dclds~pp5@XPivZGDw zr`uT;BUN$K6s~M#MPe_H#CzDs*O-MaYEMxw3Kw6pfbc29_>Gc4)7#>MXLJe8moV;~%YQ z!dSTWraH&5qB6x`n?EK^F(n-Fbl!oi~rM>We?~=-b~> z)7W?4^c0=&55ygX4Wwb&cPlXpW~8WZeZ}9pz*=tlIWW0?UaJho`n}?7nkR5Gy~S~X z%tL3=4{^sDgH;&6zp3c-L_7zY^R&a#U<$dH!+s3RsueC%khr6E1+XSh(_3!cTM~ap zexpox5_DLgzJlv-71{M@~2bC$&a$`hK#VYzqS zL`pVPLSE@$RVRB-jA@sI8aHdQlG6O8n`#=PJmFYHFWW_9^)t0(f=uo{rv(sqBb#!FBPH_I}X4ZkrDkF|}OfQ7Yd3^~a$fTJB zK#*X-2R$7yZ;$&WXMHY3BD>&-0n5zp{{Bo~L*0GTKrxd4TNGPgvbacpVl7V{K%LTv zHArFS%oZDqJk1EyMU;Q$%g=!jP583Yzj;?Lq{)CXO5{FYY&!vybFP*V`LlhnqmzI2 z!A}7QtqH5V1x%Rw{-PLk-9eVkO3>!qY<5okHCIu-u1w$d0(o*9@_Zj6O7!28;E-}7 zdx3u^w%u)92U*c$+)e}_uk>6$Edaia24n%}ZvLHO0M=S1>=GdII{$`<$bw_rfw%Ua zc9CnC!$Sjx8L}`sC;D#mVL_|eyv0nMnnkn>5TRtKh_d*8;j+ljjpkS<5H>YMlGQHL zbWC}J2!Ctn5owT9nWwvfpqp|NaQ2ybTf~9M>Q)d&w?lC(h^cbwJ}{xip}t!Ka?$%b z3IPsu;FIDu-c^cSYdSY4UgKo}h-`*;^_eY@Tw#iEqox^*pAnC_IUfta9YCPED&BVi zYk|uKB6@{xhns-48J4T4O4=7b$$?t?^Nn~8NJf(1h-R}W&m*P`vlA^XA{H;qLElvX z>?HcRWuSj))l`%T{BIdhKxxBVdjZ%}y=2Bd4wE}%{}4DzM{NW#oS4)5uVRVML(a{* zwtzxNlo2I6;JT71P}Q29fa&*b1>nfY`SW}Dojx#QvJMeJ9DJ*s#m)I;lq{m30vQz5 z0jzhQXssk%G4+T|>e@^7gKBB=h4zPchW`Q%bj(y-cQjO`3FJ152? zkOj5T-?l*X;~~$l(KDd=nBIWi_oQ&qzfVUuil{T&1t)`YBjVKvcR*4aFNqRR_j%hQ zfJHcwIst-oy_GI%lIO_hjRActr6Ouo*vLopYsu?H9QW%}hwtNV zQPnIQYTa0fm%-4Q0yT@$^!X5uH*|Fi917hBA^7K!C*e(Vh^acQ3H<27GMioI?l2XY zM$}-g#nPQ57klyNmF)f>BlZ?xr~b`Bj`C5u5Sks#?2_la=HTo34;`ChqRv1iWFS)MHIeLvo$=p zkC1&cYdxvOr!u}%F5>6MUv_=P(#06wtqyY=_vI+nDrlOq>Vo_C{0mqgw?6XB|CGuK ziiw+8)3oyQ%H&V31v^_F2;YWHc8m+!>eR&)C@tYGaeu*y@5z$rmzp;1TyqKJjV5vp zxO(zPLTca#9bb4nDJpAqBxr9OrDgC+% z0~YdCs#54?Pt(rN$Zd$G$Smhvd*?@3u0f_J_aX36qTcTWuCZDD=E>2!fO3B}_^S&U zatIo<12`-7J&Cs(PvIJ19)P!wxCX->3f$KKxaM}QAdfNCS;+6|b(^TBodI|4TjSUUnWcFMR{;CWr=CMozoW98iC4-< zw}iWAG&7+iPv6NJ&$3iFm7iYw!<*&C{ib9&)sMa%dMlZ&6Pk+cEmJ>iQxWMauAlp! zNG)P0f(Io@lJYA5$RV$gX8wfsQ?34IdX!BreLCT#7(Ary}84x?;P7s^d!9_SZ{(;k|)&<|CR| z6GK8rmA;W&VD&KR3Q>ML#nwq$XGjrYKf&N>Elog*2Ac7(K!jrfVx zE?%E+UEJcR{VA&PUt(9G2T6WAir)^tiR^#+*@~s3K~fUJ?#+<(18wQrsDfrjOxwbk z88r1uoXa!p^%Dpp-+LbK%FQA2VyC=3GlWhb_le+2mZ|HndW=0=N)*kUuBnZ7Wj_xZqO?DciA8#(0TQOgL z=NRY>PhibFDzsGBl32XmMF>WJ5m#^vjjiu!x*tTuIXA3_BvoPTcj=&Um4=av_|@h> zw?xCT*hu}2ln&bHfk++sJxB7?Bu!{&C%8cfPmT`d{&p=Ft1xyd??0EG2#dZ?s=jUz6VxgE0>S*H`*~zuGpK?T( zKt$;faW^wyh(2C?FRDGxC%RTaj3p8Jur05@itwwFQ2YIo0Vj0x!9=JF>6P&kdeVa7 z*pf=#K`G%`$5rpuEaAB?xD&7K^)FdV7{NSi4(;Z0Dn{Iv-51t^t5W`=+zw1b199iE zs7txzhFo@`VrfJFhLz+HcHkYX-4oZ1q2LRt9H6#kNX?Nb+Cr=?^nhFhb!%Yt>&#mT(W z8f9;&EKDvfyXllwy8bQkp-Z&WGc0~Yac7I_>u#gjPOmY?ii}XdAtPa)_a}yQH>f2|1@imCIK$u3j1Is z$V1gBjWv5FS5QF|@bdw1_Os#Rqweh4KLiiMdMEq#tOiNBFhwB)e@T4B%pu zez+ILkrqDr*H7D`rMFNQsYed|usdzNK_M_ey)I%2eoP}!}|9-kapRUb*;C= zEZw5diND<0TrWZK??z6HFcV!CMWKuD)EqF?LAmDdYDyi!B!AC*YX<6jrcH;wZ-V1` z7=7xl#7Yp}W^p&6?jpPGhjV>!4*WIi9}IK3`WH1|wzM&q?{$Cehta`-)W!LCPY~F@^PY2=D5&i30Of9|<0zJ-ekD-y%{eseR+j;Fw#Nv%| zaYvjKqB_WEu7cUZk{e#|?!gePS$Raqb@qeQovzeTnc7OHl7nF?iT=$s8+7u^K0^q9 zu7p>G)@jro@!YfIpFqoSsPC_$@7wHWb&We)Tan=CWq%amn!ITB^1f}PMM71KmHEQG z)N96nsC6Am1ivNO6}e%TxKZVAv!P_)earX6KW5_v4_WUX=!;Y2_evLQ6x~l;&-e&2 z$Eu!=e*57=_-dY@i$es4bBHDL7D zHyLNtuTJ;96lRxBi&s=uKg_)Tl1Rwhw!G@X;Ud@^FJ=-qJmTW4ZRnFxf8G|jOY%=n zXFgtXQOQ~(s-P%rk@B%*d~IZTFDZJs>XBA}bJRyD0bZTj%>JcYDW;DL+0W;gjQa?Z zvtnqmbUU7w!@b9Wp=vsw25(hj8_Y8#Rx*{=(O-DEp~B{UhyaHMv>1SKx1BT?4k;~Qng5C zjcE~yc3CMZ^}$4}?@mwC)!d>U$Yg`y@bTiiZJ_X>`m!XnBJJ?-VI|?33qsh}pl_yJ z8rPiRa~uNqvm82sa87D2`aP^jE{Fa5!Lc5jzrv>bOY6MSIcr*_ne60TcaQ*g%=3<) z%xuq%)KEPeX`i~5dnE|giv7UrC@q6{UvH&^TGv24$Qhe4 z<=(K%7OY&Jg5_ac-~-tF-?s6539MYoSLoqRouSm- z-TcFE;;_(+Q_ImWQ676Z&YrfwT9xg)<)xzw7L`D?91`#Nj(=ETI8|vx4*u)||1dFr zE%*AjC%-JR^8Q=*(cOSzNGw?Vo{fI#Y@&d|9RtR0_B}0*c)gYz1i+`lx7L;&DuEmV z=(XtE6Nwr?z$i)?g?YA?8+m9b^+kD$8J^o6{W5;hBH_FKC72oz2%ir}1cUG|01JZf z?B<_WhEh)gyv8RXg#U5viWkkEnXxu5SiAvl;U|)@N#=kbcdi}sm^Y;pAl^5Uda^u3 zy>6iFKgTQO9;c;SG)7Zbjtqd0C_;4W8GA-j17a8dM{HBItx5UTk{-P?=<`|R8HdT@=4ju4cH&k@C_~f!9e+LTZ!sLAaIpKo@?in+sJmTkn{cp--7%6`EylHviRKV z18s6>cVI2jLvPO2x+Wz+J3Y4Axh!SH!CNKo{#E0T%D_-@m#hzoz8jxARp*r*Ljx=K zTx@2iC23x~3uL>xo5jC#_}0C^weF|W1njFYl|!F#Ki9h#fX2Tmo72V7!0>Ks;rr!N z#vn+!{jR0I&xi@NqrC zx<$G0AeB66vL?R+h(Z)37fTPiSbW-Hva@BxllGCQ5nIRwu%co7(Q~8sXdi1FKBE0K zT`UGoJmOmp0ZDFC!IKaqPQ%3_AuFoB{~fTBU2ROjqSOl#c@!e;<0__}A7ikDCfm{O z#|#$&V@AQgQ|dSm+DGp42U6#NoqpAM{{Udio29|@k#@51n3t+mO$Wg)UgK}7&{#IQ ztUqV5TmU54Q^`ABdC$XC@=|qUM$0dQB&DakC!TS#VAE%XD)9v1{Y_>xbOsH!R&ZTH zTAGBHiiaERqy5|of%OZp?Zn&MoNgN zzb7#({v8{0NXw-!v7RDyHs9E&P^kl6_1ZhkZ6xqYj}iQ`wsGS9#p@jBU7chCc4bq> zwo+C(vZ&CUzWVyrXVc^NR?H9%GrqIt6@GFB_k0?6mC~+dNk3@_?#}F@9U4p^2Dv@k zww&mqA>akZ`?syex|l{KPx$jg9wyCB4PBA*{1=Ys4G4vguMHcHyKQUIci;T5FlRNM z^lp9PM~~nf-RFx~DEa-u*VO}a)sf=GYrXupz1KFiWE&hxedW;Sq#M(;7nxG* zLi#JatTocZLU;9qRR42GX-#pml!7c;S*BR;uDHG<<01E7Q-en{CSHG3x>%-PQXi5b zrhwF~_x(|OLMtH3iMWX{} zJZa>@LU_<#0*t?nQ2b`o!o7>S`3BjovN+=%sAVMhp%1(?!JaGmc-@G^2Zpb+=E29y z*U>%g!;(69Ju%D^{54aIQEYWfV-s~Va;nF2yw28sHh5-byv|am2dd^b@XrkU{*v~D zdoEI`V?%|zf9egjlhH7>VkiE?W&dG^N2C8f$E+_dLAtmFA&=U|e0qa57axvhc!$p7)Q5HGbPHpV?tfl+r0n$afhcd)*??UX*O%jh zW`O)wX;-gGPZ2bu4R>LZ6vVLSdiQwA*-GD$qNyD=v$Zy{6w5j129Q z;wYB&0ferb{$kr{{#Ljv-iDK@(lFh5q_OaZ27jcwuTvj&Eo-+&LsM7Pc-039OrUqx zuaQ*m8kKb=_!M}G%uPpar;wft1X)~)h2Jt=hGJ7Y^g=l^$1b4`(>%&_xCq_vSEDfFbFxjZYXRn7@rS( zOY(Sgu1kt9Mp!+K;~v?DVg*_w>}HH63Qu;t3vE(avD<;$iF>JxGT|td{}i;mw59bT zZ7v8LT+XpW-$x_S%8h5eNSt-Co~5$-IFuI%P&2RH{B;wYizzie{uJ0 zB%8DaVYdt(l6YVDox$LZ*utsM28Uvwmn0UoVO7lsV)$Y2oPuzNU&OVDxA%e@77CO| z=Air#Qtlpt$ z`v+L%i}fnE@#y`@PIeDRE&NdOi40wnP#>N0%&e%aA@=TM&v1_&E39eT7+N+na?mj( zHpyIQrp$VcweMZqU##6(_JKC~%#==x-mLFHepHxu8l7l8))l6+wv}-FNE%TY9^c$Q z`pHYb7OFsKd*kzQcN*)0z^^N_bD;~nIM)&@jwu9hhaA{ZADpggCKig(cvxLDS>Inx z+Ac%E9+mLJhVOuXA3Ldr7>n4m=xALi?sqTm;VAThWbJEZTcpz5opbcjGh1qHExExH zGl!>+?DH%L$>i^5*7=`k)VEA}l%ze_FjA>Xe{4{PK+C3lyRL+=0F9bM%n9 zmPOKfq|Me!nPD6q59kk`rC?jfIkrP|2i@LXMASwL*W6u9jn&W~fyGwkjwIhBoiT|9 z3W1REk1#ZZqIam6uY*`6ijUWInNu(wsK<+iKdZ;zBXzP0k6OON>}lSOb9@@KjQnMP zgP%AN`JjG;4Ojmem3c3>-kCqph*>>n{HZmz!J{C&vA3dS)j8ip4!ysL=_je|9G3I6+Y^v%5M`a`SjM~wvA^5n?W7hVU{pmadK}*U9 z(x12$+-17O;DXi&6;iZRS!**%(esV3nWnQ5QM&q7*!CB(%(!Md*ftcpavoQl32 zvTyHg;+yyGNE7yt;6~ZZ+udir&RJy92#I}u;l^EoeT?JD)EhevN2gcRFDyFkC9Tz8 z;bpz)lcl_j+Bi9AokQg6E*Dv%b+xh3sDdpM@pqNr?cqxyv#?wH50!^{-NW_8Hj3x>jvNhvM6@yTpdB!no@CX`S1!Oh`p+lJa!(xRJh>aw`$Ky*a#< z+cm7tjFy|xAiVyQ&eUAHbLLL~MpB~I^h)q8htS(`na?5`tP3bz`z*PJdi^UgU8$E- z)=}G=^hdxIsmLrrX7tJWOs7QSF74r5BIUgo%mutt>}Wx27*M@D$uKR?eEZ^|fuETJ zq955c{I@FLs`!+8TdD^Cl(9GOi_?bM$c6oLwsHl_%MRMi(uiU;jyY~fT4#62IF;At z@J?UfRB>EwnO)23tbgr8*JFjet|E!NWOQJd~PmGn> z{EzW@_{AO|TFm>~aKIGj*zx)0c%)0PH%D3uWr`m<0h8VVcGI4sHJ60#h1SHg$}KYv z5ha_MSW=y@nW_1+$MO{V!5LVvx>>91f3OnPk;r$P0Q@I%rT>7D9aW19d{m*7Eq4e;a-OFC+ z-!l%3m0+eZK-T0u>k6jXR_6fv)#LhegEd+f(ie$K;r%b?5>}r9Z>q2atdt(!rz8QZ zu7{uBft#$HWPGME?+CkykuDCN3X_%NjL#@EWMV1Y;rg%m$n~1f@)cqL{TS@+%tk$Y z6-u^&UGq7;CT?{UJ=0&R;;r&)#(@|Ri9@))?I>I;$^z^-7++ME#yJL87e$!?K9wv) zaoZg>Dc8(&Tn+W@BEBFMiVGXU>>s$4oAtO;R>B?Xsy#m>PPu0)D9WbfJKS#n`Tdia z!vCS^-Q$wj-tTeCTiz=xOH)d_o0(FYq0rrTH?7mfJeg=JSejTCs0gH{c9raMlvJ=h zX`MPkQkfy3QfUdI#}Y^d%@Pd+5&;E)@AP^7e*ZChoN>?0-p{k1wbtxmTs8bR8Qb$p zY_0S7xkK68Wg_-(&N(`jr7;}KW?w8;gir4%R>-WA8C8&AWfd-(iw= zECmO=jp)$vZQQOsDwjIFJ#rR0K;56|(f@JhmQNpEQh#Xw@;34(Sz6>HLuzBgTvYe`@PWU+ zUFCM9*PSh`89wk834R>aGhKn|@_Mv#yyGeAj3=sL(Ic%mTp~+)7_tC!(m%c7j`rk% zvwud71!HF*a_q6u z-TLi~=@*H~lzpGqT+YDWc_aMKF}v}Bp7YqefKu-I(!chbZdu)fV+)OGk}BL{^VIrN z%KJc$`*H^N#QZ5-??SHmm7#WeOLdW5HbmcF3yPHOsl=~TwB2LxhGBrnv>tr*>*Fs` zU9!4v&Dp>F&v?==IQ|GJedE#O9#flkQ+9Ue`M(KM5t+r4DgJ*z?_TSx4q4l2rT1F> zniVmA=xG=r@UX&l*n-|K|7d274otfauJV0V{w%$4ayXf=)A13yr{R%A@L=HYErMiF zUdH;R{z|BTW zX1~tn*9Gc4ScZFl1#U@<+w=W7oMrERogviVDG|fF#@Fr8-Dg2H!~4gpFlQXL`=jf8 zOdp;N{_Ly1$A@J#e{uX;tMMhZwF*m4N!6`tNn! zrb6l~%W2>n64QoL3&LLlU6gL^HnK`>r>D5L=Cz>~&q>SZV%_z_xSjWgE_i47Q51_B zbz+{>`@X5zWw@djZ%zk4p0D3+x?Q5c7&<;fiRdF0yh+@V+R(lFRTobUgoI3vyaPVq zWkk+xyIG}NJ5gd=dgSY};-}lxv=rvN;e()%i1F8Jw^AaXIWYB=Eb3Fx8DJH6t4Vak zY|Z|8UkOK&1m2*^A@?`b`-KycqJ`)C?~Upt1KrBT6t8VmR+ZBCt)J#b%5l&y+?Q9I zT-H}_`A=MxbG*q}ab3D6r}S0mSnuCe!`;b-V59r_ME+!Z+h6lPLz6aLM6_ZTNbO#g zV~0E|`qp1sYYzDg{kDD_{={Cr)^zeS`R%@w*CB?WH#sYpe7iAxrbW{xeR2xh#i|Iw z=GR3{o=fk3XfXyp?6vNTMLOe&{Xo=z*uP~B3&P>RM-ZNeOMiZyo!$d^>tFhhuW_c{ z^>1??8S;uUva|7zA^nKsEhKi+)=2F+mkqDG)GLWK@A)?-j=m9t zB4b|M(yr|<_ur|M_RO5I@!w7u+dY1UyyTAcv<&b62jfzWG2=zKxMQUK@646nYBg*1 zPR`+&KPMmFSlZ9B1wCo+PJ{23)cOus2Ct^Buu5~+j4my%b2PFq25e0`yTz^i?6C_a z`3l^e?Cv|0ulxQI=NhoJ&6nQ3oH889dh(6deCpi9VOra_5~APNmdlu_lF1OSVHP*e zuwm-9Y*5jyUA}HYeR@KYKU<)Qt(aH*k5HC>?E4yfx8oZwYz!BZyo&!hz6dad8;^dE z(XO#z#zy6Tot{eB^3TUxV{gkJX;PyN!eg&i7iKo$`ah>mF~KO1F?{4om2}*7nqcfw zYY)48^?{;5%pXia0`q1hft|mNL1SKqzf05GWVt>CS{(v>SytP`-#B*%vqZ% zZTmYl23Ath9Y6QP#3V$=I-fgCZ>+L%&8L0Z*u%@QN4es0=Ly0K1mEHUm|GRnT5kV}=)UpsJ2j6X`oiv~U>b)^Z((Pu8YqW{{>f+b-icOxYIxU9*w z&oS^>=Zbim$m)O7ch^L(d}&>`)U;$|kFY;&;iO$^9bwVA%Rqhf&qGbRMzy&tB|d|} ze|U+O)Ih~T6wmhzr0}&LUFPH#i?*8XP}BkT1LhX+kO`}B_t|% z1DGA)IHqAk@~Zy(BF{_CiVvIUuu7aig4kZuswcODdj`c^R>{Fa!yr$7@QT5@oS197 zalCJdhru+Jn|x6ork?L^4H;nO?P>J~^K2f@qp6Q=+9VX;ZkIFe7Cn4O3!@9V<_bfW z-g0~$>>X%Od=KvG02&XEt`v@?t#`K|A|5cW={X6l!~|_FZ#-ffZs;2KV{(paMGeU) zme&T zI#(mk_;;{?I}Nn@Z&P|3lmTwb>5EjSYe>wI7w|mz_1LS%z@})hFowPKOOZwqRaHo9 zG~tfA#D3g#vfyXyGY+9S$1A9TVe_RBcDimS$cCEqKtz3yw^R?epKaA^+QIx54-#dU zc72l_&>Q!r`;8&>>u7j~i37)`j*MvA$%PF=Jo&LaOdw|0@malu!q6l0#P9oEMMqR= z!0@eC;YY;n`#5@BJ2+{OPqOv)wFA8?7x_99K3+r}w}LJZ$Hq_j)`;9j?WX4RfDzV1 z30!JPm`-2aPBsj;jARX>%Ch-k&IEpFE-*VV!0d>$DTmpZn?GYWKynJR>`5K^9-x(P zLpcq#GQR_weP_UGw~Nk36ct9SrwJJB%Ym&U2=*gK6>7*ou%~$hO$Z+w^yR{C8(z{( zfp4$$n-`|yK{@xI>SESSp=v~w(by?a4U+puvw(!Id-%8NV2vj|@dZ1Voj<=#3~o5& zJ%yekknDiLhj-0!NyWQBmtR1U+bi>2!FQMG%o9!*|EM7u`e=}lC2wtH<14}i@yLDm zyoF@w!g?x(;(n@t$5ZT#8hpt$S7pjZ+ZK1QVM4o+>Bwk5DayPuoqEG~xOSPW$f6i$fl_8ps{^IKrY%l5z7r^E6O#@w=R`D?HxyhsJcfn&|T{Z8vqQG4-U}m&31$9I7SNOXVHM4ljIZ zjjp8@LvsfX?)cKRN?2)%e(6>B{&}vP;3%W)#E0rOlZsr8gxAh^uONDZO&w>G`aJY< zN|EkY6h}isbE8@`t%9Vw%N_yaW&TATg{bBj`{1On0r1(uc9OEjIcc3Q6HH$H>t`Ie z*6I6eJk`_bH&p(ClH5Mv0Jc+uv*db+TN?jm0@NkVmF1_Fp2R58r?bVKsaj8xXP&e< zn_ib}lPY-*e&CF;liUiiel4*4sAUu>KUU0P$Zmaqnod>W%7)`G#@nHG_-mrzOhMtUvd^a*NKH$mc3Yi+v*Zv(jMF7j+UnUvUi`w=yBQ3 ziIb)zC)1>pv2-aM>Fk1c^2!aSgBN&xs36K6bHf99a>qhR+=DDdQs&W2*vNj>V&Hma zsmh1K!G@NbR%ouFb-0z7q|J4$5B4Btb6hzL<*dj#K}x&G_~SeA5!$@kKwf{JPTk7) zgbc*PT)6U!`8Y{=R-JFC-%KLHO^7|$ma|39?2Qbka&HzrECdiQU##T|kmrt6B=A$TMzO`X9+K`8~-Fyz%1k9g)^p z#aMeN(WpdbW&{}HrJ>(b)WIL-i5&NZt zgsY8leRXA=#JurmGykst9IbS*zMx(A+?XwwaLNhovFtgbtPl`iNn)-RoP9^{+X8QgzqHx$<=hpm4=?X z#?{ zw)*H8?HF&v524aI$lsb@rCb7eJcQ#ifuKl||M~00Iots+zCkLEJ+Bp`Ac$A-%6}RB z!-q3n)Z5bSrskUB(gUV^XWU8_0<)2|3TlcUDJ@xe1^69%X|S5>)86*0$&~Uv#L!kI z*>&8)vAplU}6Gwr+ zu^#-KeRV_CP1-4@(naF0*ziztI9pgL7QAwlD+2YFj|&FIsLLcUMH5miu#LlH!;1{@ zpf<&^?}nCs!BMo*@Lorh%QUOisP73ih0lH!h`GeOHfWv8#O8f=@C5PI_0}^>abWDz zIEJ8vdVYgY!E3+6X;2}^d#xn#Z-e(cnmb-*cnmN(ZYzMGjuo!{HcS#3N{wMOF|10_ zyq9#Xr15!VUMLcd`pfau4JPtVwT#vXU8wh9m7e))w$ZxQkpNQLr=|sOersP2yZoQG zZsd%2hB6|8xS>_c{qLb9u9k6NG-``w&w3xdHGX50fu4RGG1A`I;xY`{q3xG}wrl&5 zNa%GgzgZ7JyGT_6x1yvnF9QG`#>H5ATeqFC&JZ)YMb6EiuA=qF@a6Yu=Ehb zlEe+N4sX7QIyaSNiNF3-*RLI%nA$hmGurp`>g(nR%&{)So4_9VnMphOWXg3oFsbkd z1L#W8&nN4?F2FM3QGu%Iht$HEt#_PXpY*A}HA#6dxS1LzfAv7rXWxf&p57d1nc8qX zQ5MIfl;cf!D?u)W*!kxP%CfB~gI}kvl;GwF-j|E8pgKMfk&gpW*l<3o`BoX4Tgq|b z1jp&KgW`cGY;O=JFO`dR6i<2rP3b4epD0Z~GUCwQc4D?c9Hizp&>)RV{@{*O2r!80*nno<%&~N(QUR>u=y8O}dMl z`^jsuAJo}w*2-O==4>$5-29P)i8I+@PX8RhLXV|~k0iYTA3?RrUMXK;wu9p^=(mra zc4Gvd2lE;4Bgj?^S$=la+Vsd8NS1d+;n)(jL%LqYER)9bwRN-zcK+zv0zh>8@qAw}WxKJf zoF?X_7zvpw1v)s6KCir zVM|wDsr0!mv1})+8%bHQO6KBgHk^@ccH94UNGf%mN)Ou?m|C5Im?#f#K~$V=+(qM#`xM)zS8BxMxJ<=aq!*{ub-Gy47_14t#mCR`bNe*5RF+yo_-rFbCU6z z+Bk81QWmjj55XS4?&qw?giqvOP0-b!ZoO>*9>MCN)S+5%BnM2&){zM-GHukz%tim- zBjE4xjK`oys?gn-V7FIAm8j~5ya8Ug`p`*jC+4>ax$L%cFPFblBEQ3Zq!%TJ(GDTY_*Uy}v(s0j0p*@O4AeSHkD*V`x z6HT<|sI_>3$#tX$D1`W30PMrN{Nso!)OG&52@4?O7!|Q)L^OEh zfB{|KW?Er#%wC}-hureYt5kG8dajB*K&N!)(|QK7?k$0~Z8BIVU8z)TsB%C|kzjjVmwC6S3V>$?h!fXRHE0g&Ls%g^;-*6lu<;O*2)Sf%53%&`GWb&??zQ zrV~~RL+9m`Kh;&R1zJq6`5Q0EcnU~@cJqy9aqK>=u32H04VcVKc7qnvgNf6T_g+oo zL|}ru{k9yI&j+JgKbGw+skqW6SS|1|tR?BZ)iN5?x<12a%R&==5;Ljfl%LA4yx@Lu z$!p|a?#5Y=o=1}{+W&wGxw68}XwwJoxQs8TBpv=M4(qZ90ZWm~EAqiMFu>7j2vJ6Q|qXQv^K$Uvz&vHja_4%;legJ7>X| zs7UK_rDNS?e`cE^3;a z<|Wq2XKh*8Q#^failt%|`M3Cy$PHjn+B|Uv-m|Gga0D`9Ht3-G#CB?REKxe%w3B@- zlh1T&$i?Bc{S65ZL>ZF5#*8$uOB#O@UA-vgJK1c;%p=p4>-QdC#LI=WYQusdoeW>% z_lWPOZik7Qe8n>aY@^4678W>T%>?(r8*if`6Vf*g6|L26JCY!AkhmVq9T+oIeWWq` z52H5Op=*^lK5#lsPv8LF_g~Iu^cC7vy`Vvi+cvpt-!c~&ss~Fs4A2rRy`^0$y=VJy zsD=6s*QtQVJ8wt$#NiGeb#Th(Y4F8OHrNQIwEd!xf6zeouxBqavD*rcG^4rZ+`C1D z$}Csy+rs4{S;nVpU1lw`VQBQ2Rxznt#&%geFBLgguA8d>iV7p1!xeX^*3{rvxC3aV zdySo>bX2kp^{7->E^B*aqDd@Cx3+&AH5hN>m+rHZjTeXG#>!=@ABky_eA2;fx^A5z zz_{X`O>bZ2&Fuez1(CZ4;@P&F%JXKR=iPgho<>%e(NW071!r#UQHG6QEI_s*&|5k5 zGV-rqeH=Pa$8BxR0E)-2EvNztC)JY8d6^(~$3td95&JWj_Y`M2Wa6hVB>z@r0B=!M zjmjG+;f5=5R)+zfno#oGK!YlcIj0Apf%bBHp z6{Jqk=9{sU!SPLnI8>QviAA}8IJ9kqrsOOHg1hEf*S^nlUq924Uje^MbZ`7pbwHTs z=v}&PpKM5tQl@;K)l1#Zs;P`)fOa4E=rv$G%=kYXfFr`Xt+mQf#rT&ifpeRZn=8B|qy{#cz7=W@cWRJpjYT`ghB#l_BBOA76ilt-BjlK@pQwVJn z>(l`lo^UiUGgzEbXQ7Ob4M!vm8V`W^B1y+&D*WhdQ^lFK7_GA3RQVPBK8zSlSO1M= z1m1CJK|qV8E`i4AyvoxmHyWwZI~`Sn)IL1_qLk;*5?-bai<)b?leHLEV9$wYTt!G5 zDIyMqC2SyYHW}Q1*#ra zs3NgowdZw~daoa+mB8Y+gqcPn%0@W0h%#d_Z*MM;t zy^BzATB-1UdpWQkQw!&@g0Psq zrlOONNI5XkJs=e)C=JwksT%~yPa`4m63L~w43Umafgl>=VGYKaNpJ4Ep|vLniw7>>Z5h#*KiMP~{Jd2X)-v39*XUk(#ki;j-XVus8qUvT;w5sn zha!)9<7aiWywd4#-{6(W887x`xfwbb)MGxE4-4LRz`W?W*>>fX$iVS37*{g|4mNt; zv(_S0^@PK#PAH=Z#g|;9JWW)WrQAp=C&5;$rDrj}pe)d8Ti`StS-}40EP^5#;qQ+n zj6Bgv3r$M{W|=!LZN?J36#N#~R>U)*A!7oo^1UYS{~9+OS!VbdUxr(HMtBbdZ2kp3>S5xV*9Z2u}08g_QCWNd?zKHtcY^Srj56F~+Z z?_|>{9pd$VXZhl>4VG`4@y=NKnWdeb+}xa-M^p*Ggsr%e z0nXT?wS3Mm2vsMjhNqfFcTK7Xc`N{d;4dx!5QvLsF3;i7>f6{1oJ*aADaOT5u1rlk zKIh<|>{iy{qgIW<0$sHsI?$RXdVaatqGPrsn<-KJ+i zZOPwgTs%@fRLhA%q~$#WkC&mYJS(KO_FKR@Ha#osZQWc(4lpdPdkFn)|DmY$uQC6|4ZRUZRR>+t8S;KT=;w{;ADI6&W%9MFGdtZmat`(R+s zowQ=lWv$JKm-AwGiBYxvOL;}i1~E#F8TxDJ>(IsHIXZjEu2*-Wjsi=7vH8}48eJJ_RByZ2d zM%odtYc{RbBm}Bz7NEo^{pqEHpALYr#-V^ANuoCY*~alQT|rv`s6%cUmGCt{=@dB! zMix1c-)$3LB%y=VQh1i6)Ca|2@<-h$_ zyz+*arE4=h(*0t>#BNN%u<=)x3?Wg%AsBT<>J~}GQw|eEQ1zSu4_RDRV#-g%TDGEAm2;p)Gd|ceJ2P-$>yFo z-FPE8s2#UkNM!hJ#pf_Cgm2B*qP*8oeTIgk5My##e5&sq9Q3{F%#`?xIZS}pL)v?W zdCtuRLg~(|t|OC1i3_7a=VU1QNUu7T9-<8qk#IJp1(gc4Z6EKZwmC`2=uz%|9B+ia z1l_+fj@X8H94D2ITC8(s?9b=!YmNxnyw1K447qx}86N0j?N-PJ#0Y`oH3??GS#;TIQ~Ht}&R4N}#wzGiSCSujRUw`+o5>%8dJ%R)S;M1UDz==bxz_D@f?~CDcS9Vb{I=-a3HGd& z`KZ2MZlcaJPxa#KU4!PTZ0gO2i@I&rslyCsjE)N#2 zPCl_k$Tqef%B+kq-fCl08=Wif;*yu=4#nQ%?dFXVLQrD z^6Ol_x-f!$i?uBy%tL#H%tqExx#scVB`=GZ18ME4{?C{c?gR;)qoyf;QE`YHLU+_Trxg_}7CT4>O<^;=^H=?$uA^5msHCXyV`LinxIxoXd zny1=H{;UA?0l(IOjHiE4U3g;ybOO{v0EljsUko^Br~cWPAdmO671Ych4oi4lh(Cw$ zOxyqq=W{c(vVei|e-ZHDCMHo7e0jW#nh1x@(B;7|P;<(U`E({0D=dwBy#idS`S-R| z-hg<0O@3->FQ7kYyHS8B(kSl>)M%aSQN&ibCL_Y|;;q&=W7rg*cZcPx@Lp7GP-(=3xu@JIz~+6+mJ&4@0NK z$=*05{Ch&XLccp`RbZR-yNedwwK&09Qa7B zng>YPEy+RhoP}BZBqa}Osn0bPg;&r+&{aQ zzujT*xGJZHf$0X*!~;7%&yQUye!gVpc!h4b$%0?NThwxEDCq#dspvtoS}2OP5561~ zIKFdqhWwGg;}R&b9R{izkg!PYr86`gtQaQ})f zAS~gyy_o-!?zXVN1xWP^i(tsFAwYab0;Mzg3Omtjhr)pazoPqP-x~hAdc5pXWWFw% z1O45@m)3X+&bg^V z9iYPxXHR*nG2e$XP4j!WWCN@$>UQNtRZX-*e(XLd9AK6V?C)0aL_{h@Wy zdta8b#MBV{Dba6*VX4@%w78BUfD8p5px2Lrg(HIM3tJ7-SeW5DjYOe59sHEZFT?FKFB$16IhU(BcAMC^us(l_hDAmP zarlAHeAG@frH5`JTd1QbLm(iI3wXSp1xne7bA)yNDbz8L+}@VWB!0CtuM!UxW+>}| z{waXw0k^iH!3K@2b^g-{k_5u6j3J5^eA%t!IF$kAu>hG(Y(n|Q@w5I#D8?CTD4^vB zgrPPBC%#pu{0*1P=tl)xu?h|T-xNpxeC8rP18&dQe;GA@W_C}pabr?CJ|x+l`s9-Hn|mtXYq4j)0!baq3&@s(Z>>(BAocux=_XIeXG!kcVjQ>|bO1 zz@DK_A&EAx=`yA3<7 zSl&puH5TSoSlCQf{|VRyFu}8#vx%F+M<&v101-5JizV2pUK&erzqKPjj{07$fENxt z-hr}We|(uhh@0enSVo~R&nE-!C`J!4{I{%B>n0DO+dYBE@2!4BR{gEoOKOPE;EQjh zHQyzn=RdS+A|C-Tb+n5XPyGQr3?G2=`c*+Cin#`*W;zC!3M=IEvB3+Ic1z=xm-y{A z97TO~co&}rfdzBlwwRdj*bWEtjfRhbO$4_R1`u`+_WY_R5biWoFL}|Rj7hA9DtpNCz6&1mAbVc$aE_F=AC+-4y-8M)q9hPU-IYm2d!w$wJ}K* z6<$2Q6@ZGq-YEy*Z2NzAImI*dR+Gvu0dVLSaQA*SMI5Pp!&1M5sAn99 zM7A*Ez>puOQLo+Z7mmM`+}hXDi5pb|5ZQ-LSk@FhZ)#aj9rAh$d?S0;$VAld94i3r z2|x5uNbrn~7Y-c0=(zCm0M1q9N@Jk9&);)HW4!BgXbfeSZcVUUQPl2L6#2e00zK6x z6n(TYzYDzuSiP12%8WFRM{k7zIHzN_`O}Ow;Ez%R{wRN&a^z(~!q2c$FV?#m&)-)Z zr3^73RrS-BDiDA-|KI>$lZrK zEC=S!*u_0b1FzlZmFge;8?LPF-VDGM<|t~*Lhx=CoHk(S#{CbijGhjhu>fCE-4}M8 z>3T1$b$U(lqHLJbjdm>Qnie7*wK<5Knn8Fzz1hXv5KA;<4DslXz%*u2-T*9*uw|-t zw4?TPsgrrl#B<^^esPn(!pq^c5~qeXnrC&kTtq3YD$35Z$+25j_e5;CxKw+Io}l%t zC?a_@nirw z23g9ll`&;wb#t}?$#6Q`3m{Hey|#mo4$x1JW=wrly(bxd=X!z`I9if|9h!fDeka)_ zW?hp~N&w89EEuZwF$rxl#0;tbUN3Ij*vDfz_7qbh@m_y%`U3jio7UC{MoE?FSwOPp z4_@cmmz*ooK*Ra+%f123a75q#krb3x{zgi1s>5^LW)-(r81+7CdwF~yc$7SB|!<)>aw3B(adxTQv^YGgvq{j&wsk}aW=qa6&F#^H&k<$yOO z73maH6zws+g)A^!>dMfvf5@Ji6&*9U6H37e2&g%ha0);av2d5HPCTxO6h?vX1S*L8 z>6SH~${V`sc|FA<`!{wyEYW{kP_<{$6Gj}KEcvrlJo6s#ex?-Ht(F@03Qyf=U)19F z>tFAabEPLaBNsdnkVNUJH-0@_W8aHl+0~I|&ob@!46$z*@^Fbul$KVH2CFZSxlaRu%Uh%Gvjk z|8(f1`|BY@PVN-2QU{_XF6vbPe*1VGU-5&97$c#kT0=U zHU`o7Uy7$YX#A5f7v^j%WfE2WkS3-pANA67q5()U;64lny~?YcnEiQaFIFs_^q%no zoQt!Y${IJ;BUCF)rbOhtpPiEORe(H|-8wwDGLtT*;nz+CJh*^&G$duJS#nKBN#!be zI31oFtmMbC=Zns~!!Ix#%T$*NWl=s5Cm|b9lQWDZ7XW6^#Oi^Wc<|PIfIImnuOpI5&qqH9aU5^tQik(MOgb$%Sp;hUIE9 z4G*b5Vy9ITYy*{+GNUNB(q4rGY|wzOHbTgag=Ge3L?tH08w1~x*V-xzbh~OdJ=Y=a z(4U$OL%8m>K+<|aKVlEHAb3@NqnU7M^uFez!Tj#EHZmhAw{mqnQ{>i8@fi(MFI>kp z`DhiaU8eqRN(HbKxEPQjr{kGYtdDtS%csyhd86+9B!c+fl#&y9&kgz^w3xMhIt~jh ztZ+c%Z?GI*W1;n-^2m3Ja76JIls0T)*f>{AfghDlm-T6+i;l(D=FFeYuw#N`n?8M* z_LC1(?pP?24ODI?)?+@w9zLgu)v@ZGwCU)J&W*u3J+B`^*d7e1{|vZK$87kVAik6N z!^~>!lA#-!VE*4l03I90O_)CYTLM@S_K#wIQ$GMFZhm2Swq`RHgWgbW81fzR1sm~# zeg!z$L4MDp>K0AhmU88IGa(Rn^Lo3IYDVY-xPP<|0>x8Q{w&`^icWTv#Q&M_dvk{p0kVO5H@rLmG3BgtF-&Gc5NtZXnR_K?=0^G+AhJ5B^10j+qrHp{^Z;P6>E7xLnkha} z>LwI?o7XfQNi!Wzlp?oyfU$|S@o+aV)-IEoUMmDlK=N?m z*sJU?WE=(>MF+)6qM)`EsV==(B`$KMDK|q#BrPEv>1nT`46ue+jRMG0CSTGz0+{v^ z(!FY;V=pe~bQ4oxi@s|D8i4OmsyRg!G~lVt{%t>mfpD`?3VxWzx}=gSaszmH-;b<0 zVy386?f@R2QVRm*H@=OR;aW_45ii=ETlL@C+qPJlxyH4*!RDpCrMuA1&G-RL#+QC7 zTmN5sCsZqjE1!zIgc*wOr_xC(bdvo#vLOA$$asi3Nwr}L{9SBN!(EhZw~g6Df(7il>5M~6c>c`W*SCyG zTE&bW1H!Tw>FJRK=uZLrx_o9yEeY=L+hR_doYBkp!2l=qEDV${&j!`KfxoE6Cf+>yEU4N%R?hz<^*qHb%9%s49BdtUBWeyxf@<&pYdGhCZ9$?(f zxBn;IECJRK%m54=yHyTD<$)51v_jOiMY|-OHkX`6yqyBw?=(HAGLdX9m)^770bzr@6a>9wQ!ncB82dgo}o5Zkj^*e3HzWzAQ7 zlrIu35N2q3^(|q5e*o&Aj|J$Fpd|{9+*7pcgv2ci|A!pjs@v&$nyMszXwlC~4sy5b z^IR-CkvZ3tlnC|eb=ia7!`WFzU8XMionDa(Lhnsy|Nri~q9Sn7?w|#|#Pp)r{P2Kq zZ*^3h(WAZd*$dDO&@G$yvF26ob7XB*m~IQ9;!S&OUfb~MJ-sgE%Yd~%Fq9OswjM}o z_7evvLM*%X5t)|ZUhjjY)cRy78}K&uJx*E{f7ghZU|gSA>kwKL5?mjlItjctV--md zP_+$>zf);UwvvU(!qR3N^mXALx0Oh^`C!R(_S-&S@t?CaMx^&MFPtQy{P03yqqV4{ zArN*;ekRjG%q1)RVf=>O+Evdb#i2*+ytw_jT*+k(WjSQwU=#^bGCk)6+d-6NFXr{T zMvF$-&gC6GW!U_p=HG9cJRS-z6MLvzXku)9?F#wKQqOIV<1j_uzOxvRtN2~-<1p}D z+7&Y^P0!k|H;Yri*!S_u;i2|_TJ=8d;DXaiqscvN7KG>xnFtsOF8NXPTjyKH5x1dy zp3RXFefZx2Zf^DMtgI7e&w4fBfj4=PO7k_aWst@=Wwq`@ljCz;tnlM3855=jkKFDvZ@-c@(-iI!c3wpp)oh4!Rln;HR%QJd|D}XkA(jf?- zD#{~#==;^5pPm9Zm7k|O0JCMiN~DsOk%r)9`k{6hVG`QA<~t>r9T=A2e`FHQaj`=? z?_gU~TnM?O5>}S~XUoS$?b`mFoF#ua30f&Ht_1 z;x}V!(hY7>CCw~M+yDrw(~Ffh!SCU_pT;v3KaQrK8lkk@$Pb?gFkfflbQkQk)gq&3 z=vAeg@eP`*NK(E8+kEV_8>I;Q*oBD{BA+ky(T)5{+eq~xq?xu#*g*;Y<*OeOz%zab zZ{TI9ImV$$)G$v`WhJ*Qk@5P?9uRf#AXf21x~pVhCv0BDvU&pbm)fd{ct#2+F4~(! zfO}c2TtbJa@Oa`+!v1${Z=95XqquP)4&<}st(`#vc`-cPzV|f7U%f}pyW?$!tR)}3 z1~|9nxTQGzi%y*%i(bp70S|yNvkrG#E>>ic84ys41>*%xEZsa8@A{f?H$}ZQ2MVR` z9E}tjE`TO9s!LtvjsEX0=_Qbm!Urn-smDr!+mDTbr2yrm(Oh~ZQ&!1ZOtD)w8w@38 zueH9!T7tzis^Y(2I)r&#FfS2LYU=^*MAtRzhu1jG*rwL$B;iTlnoJACgbdBHQ~{wQ z34IO!r`Gltekn*|UUZEl_@S9ninKH^c5W}BI8+TWUi^+4HU{5~dQ-Y&u#qO(tsRSp z=;-Er+#Z16caVdv%gwX--zucz6-6ECoe_waMQYQ@!w>{x@DCcpwi|jvnE)^$Ti|(2 z#`@6=Z8pZ`AzfZM!5Ia>Vv_)8gTFGi!H<}cs|NNovpHj=CEeADRozEGV&1GTO9-)b*)pb(Ao$kfJx(9ty&pw~w%O!nHWsl^C*2Fs!+V!IYj(?Nv+ zkXDmjO=YXxrC!Gp1wgoB0WadPmW5}(r&l(c{S1T7cDk?+T>dZEuuKW zD;iyb8cxn1FWSFx;QkIqygIN&(eZ7+tsyFWRb@lWmy@rbMM#5jwo+Q*|jo1g`G30�__Y> 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 literal 0 HcmV?d00001 diff --git a/Assets/Models/racoon.gltf b/Assets/Models/racoon.gltf new file mode 100644 index 00000000..459a542d --- /dev/null +++ b/Assets/Models/racoon.gltf @@ -0,0 +1,4993 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v3.3.27", + "version" : "2.0" + }, + "extensionsUsed" : [ + "KHR_materials_specular", + "KHR_materials_ior" + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 55 + ] + } + ], + "nodes" : [ + { + "name" : "L_Toe_end", + "rotation" : [ + -1.304514398725587e-07, + -4.8278069232242024e-14, + -3.113858042524953e-07, + 1 + ], + "translation" : [ + 2.9270432744255004e-09, + 0.02392714098095894, + 1.3476908478082805e-10 + ] + }, + { + "children" : [ + 0 + ], + "name" : "L_Toe", + "rotation" : [ + 0.32702386379241943, + 1.1310142156162328e-07, + 1.641405731334089e-07, + 0.945016086101532 + ], + "scale" : [ + 1, + 0.9999999403953552, + 0.9999999403953552 + ], + "translation" : [ + -8.650776095464607e-09, + 0.03380582109093666, + -2.448857117087755e-09 + ] + }, + { + "children" : [ + 1 + ], + "name" : "L_Feet", + "rotation" : [ + 0.516292929649353, + -0.020581310614943504, + -0.05452270060777664, + 0.854426920413971 + ], + "translation" : [ + 1.2865877252465907e-09, + 0.06353945285081863, + 2.6193447411060333e-10 + ] + }, + { + "children" : [ + 2 + ], + "name" : "L_Shin", + "rotation" : [ + -0.054226718842983246, + 0.00034972387948073447, + -0.0027083493769168854, + 0.9985249042510986 + ], + "scale" : [ + 0.9999998807907104, + 0.9999999403953552, + 0.9999998807907104 + ], + "translation" : [ + -8.217813984856548e-09, + 0.012935775332152843, + -1.1059455573558807e-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.9945576786994934, + 0.06613556295633316 + ], + "scale" : [ + 1.0000009536743164, + 1.0000001192092896, + 1.0000014305114746 + ], + "translation" : [ + 0.06634333729743958, + 0.021777987480163574, + -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, + 0, + 1 + ], + "scale" : [ + 1, + 0.9999999403953552, + 1 + ], + "translation" : [ + 0, + 0.022377878427505493, + 0 + ] + }, + { + "children" : [ + 7 + ], + "name" : "Neck", + "translation" : [ + 0, + 0.10304805636405945, + 0 + ] + }, + { + "name" : "L_Hand_end", + "rotation" : [ + 1.3239958462918366e-08, + -2.4324227076988336e-09, + 1.4901161193847656e-08, + 1 + ], + "translation" : [ + 2.2351740014414645e-08, + 0.016836093738675117, + -5.329070518200751e-15 + ] + }, + { + "children" : [ + 9 + ], + "name" : "L_Hand", + "rotation" : [ + -0.10859407484531403, + -0.0013414795976132154, + -0.012280543334782124, + 0.9940094351768494 + ], + "scale" : [ + 1, + 1, + 0.9999999403953552 + ], + "translation" : [ + -5.215407838932151e-08, + 0.030574528500437737, + 4.579678858362968e-09 + ] + }, + { + "children" : [ + 10 + ], + "name" : "L_Forearm", + "rotation" : [ + 0.03182216361165047, + -0.010124370455741882, + -0.05386859551072121, + 0.9979895353317261 + ], + "scale" : [ + 1, + 0.9999999403953552, + 0.9999999403953552 + ], + "translation" : [ + -1.4001724224499412e-08, + 0.011892830953001976, + -4.656612873077393e-10 + ] + }, + { + "children" : [ + 11 + ], + "name" : "L_Elbow", + "rotation" : [ + 0.13403145968914032, + 0.0004466302052605897, + 0.0229647234082222, + 0.9907108545303345 + ], + "translation" : [ + 9.490547014934236e-09, + 0.07338026165962219, + 1.862645149230957e-09 + ] + }, + { + "children" : [ + 12 + ], + "name" : "L_Shoulder", + "rotation" : [ + -0.05528340861201286, + 0.01580565795302391, + -0.27442947030067444, + 0.9598866701126099 + ], + "translation" : [ + 1.1175854908174188e-08, + 0.034574370831251144, + -3.3306690738754696e-15 + ] + }, + { + "children" : [ + 13 + ], + "name" : "L_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" : "R_Hand_end", + "rotation" : [ + 1.3239958462918366e-08, + 2.4324227076988336e-09, + -1.4901161193847656e-08, + 1 + ], + "translation" : [ + -2.2351740014414645e-08, + 0.016836093738675117, + -5.329070518200751e-15 + ] + }, + { + "children" : [ + 15 + ], + "name" : "R_Hand", + "rotation" : [ + -0.10859407484531403, + 0.0013414795976132154, + 0.012280543334782124, + 0.9940094351768494 + ], + "scale" : [ + 1, + 1, + 0.9999999403953552 + ], + "translation" : [ + 5.215407838932151e-08, + 0.030574528500437737, + 4.579678858362968e-09 + ] + }, + { + "children" : [ + 16 + ], + "name" : "R_Forearm", + "rotation" : [ + 0.03182216361165047, + 0.010124370455741882, + 0.05386859551072121, + 0.9979895353317261 + ], + "scale" : [ + 1, + 0.9999999403953552, + 0.9999999403953552 + ], + "translation" : [ + 1.4001724224499412e-08, + 0.011892830953001976, + -4.656612873077393e-10 + ] + }, + { + "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 + ] + }, + { + "name" : "Tail_end", + "translation" : [ + 0, + 0.07595176249742508, + -1.3838050705317073e-09 + ] + }, + { + "children" : [ + 29 + ], + "name" : "Tail", + "rotation" : [ + -0.7071068286895752, + 0, + 0, + 0.7071068286895752 + ], + "translation" : [ + -5.8597615213960615e-18, + 0.03983837366104126, + -0.09847982972860336 + ] + }, + { + "name" : "L_Hip_end", + "translation" : [ + 0, + 0.032987553626298904, + -1.5967565047958487e-09 + ] + }, + { + "children" : [ + 31 + ], + "name" : "L_Hip", + "translation" : [ + 0.06953180581331253, + 0.04957667365670204, + 0.061330340802669525 + ] + }, + { + "name" : "L_Butt_end", + "translation" : [ + 0, + 0.03298754245042801, + 1.3750955929481279e-09 + ] + }, + { + "children" : [ + 33 + ], + "name" : "L_Butt", + "translation" : [ + 0.06953180581331253, + -0.0007792188553139567, + -0.04653617739677429 + ] + }, + { + "name" : "R_Toe_end", + "rotation" : [ + -1.304514398725587e-07, + 4.8278069232242024e-14, + 3.113858042524953e-07, + 1 + ], + "translation" : [ + -2.9270432744255004e-09, + 0.02392714098095894, + 1.3476908478082805e-10 + ] + }, + { + "children" : [ + 35 + ], + "name" : "R_Toe", + "rotation" : [ + 0.32702386379241943, + -1.1310142156162328e-07, + -1.641405731334089e-07, + 0.945016086101532 + ], + "scale" : [ + 1, + 0.9999999403953552, + 0.9999999403953552 + ], + "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, + 0.06353945285081863, + 2.6193447411060333e-10 + ] + }, + { + "children" : [ + 37 + ], + "name" : "R_Shin", + "rotation" : [ + -0.054226718842983246, + -0.00034972387948073447, + 0.0027083493769168854, + 0.9985249042510986 + ], + "scale" : [ + 0.9999998807907104, + 0.9999999403953552, + 0.9999998807907104 + ], + "translation" : [ + 8.217813984856548e-09, + 0.012935775332152843, + -1.1059455573558807e-09 + ] + }, + { + "children" : [ + 38 + ], + "name" : "R_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" : [ + 39 + ], + "name" : "R_Thigh", + "rotation" : [ + 0.005340703763067722, + 0.08032803982496262, + 0.9945576786994934, + 0.06613556295633316 + ], + "scale" : [ + 1.0000009536743164, + 1.0000001192092896, + 1.0000014305114746 + ], + "translation" : [ + -0.06634333729743958, + 0.021777987480163574, + -0.000205356627702713 + ] + }, + { + "name" : "R_Hip_end", + "translation" : [ + 0, + 0.032987553626298904, + -1.5967565047958487e-09 + ] + }, + { + "children" : [ + 41 + ], + "name" : "R_Hip", + "translation" : [ + -0.06953180581331253, + 0.04957667365670204, + 0.061330340802669525 + ] + }, + { + "name" : "R_Butt_end", + "translation" : [ + 0, + 0.03298754245042801, + 1.3750955929481279e-09 + ] + }, + { + "children" : [ + 43 + ], + "name" : "R_Butt", + "translation" : [ + -0.06953180581331253, + -0.0007792188553139567, + -0.04653617739677429 + ] + }, + { + "children" : [ + 5, + 28, + 30, + 32, + 34, + 40, + 42, + 44 + ], + "name" : "Pelvis", + "translation" : [ + 0, + 0.15915730595588684, + 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", + "skin" : 0 + }, + { + "mesh" : 1, + "name" : "Raccoon", + "skin" : 0 + }, + { + "children" : [ + 53, + 54, + 52 + ], + "name" : "Armature" + } + ], + "animations" : [ + { + "channels" : [ + { + "sampler" : 0, + "target" : { + "node" : 52, + "path" : "translation" + } + }, + { + "sampler" : 1, + "target" : { + "node" : 52, + "path" : "rotation" + } + }, + { + "sampler" : 2, + "target" : { + "node" : 52, + "path" : "scale" + } + }, + { + "sampler" : 3, + "target" : { + "node" : 45, + "path" : "translation" + } + }, + { + "sampler" : 4, + "target" : { + "node" : 45, + "path" : "rotation" + } + }, + { + "sampler" : 5, + "target" : { + "node" : 45, + "path" : "scale" + } + }, + { + "sampler" : 6, + "target" : { + "node" : 5, + "path" : "translation" + } + }, + { + "sampler" : 7, + "target" : { + "node" : 5, + "path" : "rotation" + } + }, + { + "sampler" : 8, + "target" : { + "node" : 5, + "path" : "scale" + } + }, + { + "sampler" : 9, + "target" : { + "node" : 4, + "path" : "translation" + } + }, + { + "sampler" : 10, + "target" : { + "node" : 4, + "path" : "rotation" + } + }, + { + "sampler" : 11, + "target" : { + "node" : 4, + "path" : "scale" + } + }, + { + "sampler" : 12, + "target" : { + "node" : 3, + "path" : "translation" + } + }, + { + "sampler" : 13, + "target" : { + "node" : 3, + "path" : "rotation" + } + }, + { + "sampler" : 14, + "target" : { + "node" : 3, + "path" : "scale" + } + }, + { + "sampler" : 15, + "target" : { + "node" : 2, + "path" : "translation" + } + }, + { + "sampler" : 16, + "target" : { + "node" : 2, + "path" : "rotation" + } + }, + { + "sampler" : 17, + "target" : { + "node" : 2, + "path" : "scale" + } + }, + { + "sampler" : 18, + "target" : { + "node" : 1, + "path" : "translation" + } + }, + { + "sampler" : 19, + "target" : { + "node" : 1, + "path" : "rotation" + } + }, + { + "sampler" : 20, + "target" : { + "node" : 1, + "path" : "scale" + } + }, + { + "sampler" : 21, + "target" : { + "node" : 0, + "path" : "translation" + } + }, + { + "sampler" : 22, + "target" : { + "node" : 0, + "path" : "rotation" + } + }, + { + "sampler" : 23, + "target" : { + "node" : 0, + "path" : "scale" + } + }, + { + "sampler" : 24, + "target" : { + "node" : 28, + "path" : "translation" + } + }, + { + "sampler" : 25, + "target" : { + "node" : 28, + "path" : "rotation" + } + }, + { + "sampler" : 26, + "target" : { + "node" : 28, + "path" : "scale" + } + }, + { + "sampler" : 27, + "target" : { + "node" : 27, + "path" : "translation" + } + }, + { + "sampler" : 28, + "target" : { + "node" : 27, + "path" : "rotation" + } + }, + { + "sampler" : 29, + "target" : { + "node" : 27, + "path" : "scale" + } + }, + { + "sampler" : 30, + "target" : { + "node" : 8, + "path" : "translation" + } + }, + { + "sampler" : 31, + "target" : { + "node" : 8, + "path" : "rotation" + } + }, + { + "sampler" : 32, + "target" : { + "node" : 8, + "path" : "scale" + } + }, + { + "sampler" : 33, + "target" : { + "node" : 7, + "path" : "translation" + } + }, + { + "sampler" : 34, + "target" : { + "node" : 7, + "path" : "rotation" + } + }, + { + "sampler" : 35, + "target" : { + "node" : 7, + "path" : "scale" + } + }, + { + "sampler" : 36, + "target" : { + "node" : 6, + "path" : "translation" + } + }, + { + "sampler" : 37, + "target" : { + "node" : 6, + "path" : "rotation" + } + }, + { + "sampler" : 38, + "target" : { + "node" : 6, + "path" : "scale" + } + }, + { + "sampler" : 39, + "target" : { + "node" : 14, + "path" : "translation" + } + }, + { + "sampler" : 40, + "target" : { + "node" : 14, + "path" : "rotation" + } + }, + { + "sampler" : 41, + "target" : { + "node" : 14, + "path" : "scale" + } + }, + { + "sampler" : 42, + "target" : { + "node" : 13, + "path" : "translation" + } + }, + { + "sampler" : 43, + "target" : { + "node" : 13, + "path" : "rotation" + } + }, + { + "sampler" : 44, + "target" : { + "node" : 13, + "path" : "scale" + } + }, + { + "sampler" : 45, + "target" : { + "node" : 12, + "path" : "translation" + } + }, + { + "sampler" : 46, + "target" : { + "node" : 12, + "path" : "rotation" + } + }, + { + "sampler" : 47, + "target" : { + "node" : 12, + "path" : "scale" + } + }, + { + "sampler" : 48, + "target" : { + "node" : 11, + "path" : "translation" + } + }, + { + "sampler" : 49, + "target" : { + "node" : 11, + "path" : "rotation" + } + }, + { + "sampler" : 50, + "target" : { + "node" : 11, + "path" : "scale" + } + }, + { + "sampler" : 51, + "target" : { + "node" : 10, + "path" : "translation" + } + }, + { + "sampler" : 52, + "target" : { + "node" : 10, + "path" : "rotation" + } + }, + { + "sampler" : 53, + "target" : { + "node" : 10, + "path" : "scale" + } + }, + { + "sampler" : 54, + "target" : { + "node" : 9, + "path" : "translation" + } + }, + { + "sampler" : 55, + "target" : { + "node" : 9, + "path" : "rotation" + } + }, + { + "sampler" : 56, + "target" : { + "node" : 9, + "path" : "scale" + } + }, + { + "sampler" : 57, + "target" : { + "node" : 20, + "path" : "translation" + } + }, + { + "sampler" : 58, + "target" : { + "node" : 20, + "path" : "rotation" + } + }, + { + "sampler" : 59, + "target" : { + "node" : 20, + "path" : "scale" + } + }, + { + "sampler" : 60, + "target" : { + "node" : 19, + "path" : "translation" + } + }, + { + "sampler" : 61, + "target" : { + "node" : 19, + "path" : "rotation" + } + }, + { + "sampler" : 62, + "target" : { + "node" : 19, + "path" : "scale" + } + }, + { + "sampler" : 63, + "target" : { + "node" : 18, + "path" : "translation" + } + }, + { + "sampler" : 64, + "target" : { + "node" : 18, + "path" : "rotation" + } + }, + { + "sampler" : 65, + "target" : { + "node" : 18, + "path" : "scale" + } + }, + { + "sampler" : 66, + "target" : { + "node" : 17, + "path" : "translation" + } + }, + { + "sampler" : 67, + "target" : { + "node" : 17, + "path" : "rotation" + } + }, + { + "sampler" : 68, + "target" : { + "node" : 17, + "path" : "scale" + } + }, + { + "sampler" : 69, + "target" : { + "node" : 16, + "path" : "translation" + } + }, + { + "sampler" : 70, + "target" : { + "node" : 16, + "path" : "rotation" + } + }, + { + "sampler" : 71, + "target" : { + "node" : 16, + "path" : "scale" + } + }, + { + "sampler" : 72, + "target" : { + "node" : 15, + "path" : "translation" + } + }, + { + "sampler" : 73, + "target" : { + "node" : 15, + "path" : "rotation" + } + }, + { + "sampler" : 74, + "target" : { + "node" : 15, + "path" : "scale" + } + }, + { + "sampler" : 75, + "target" : { + "node" : 23, + "path" : "translation" + } + }, + { + "sampler" : 76, + "target" : { + "node" : 23, + "path" : "rotation" + } + }, + { + "sampler" : 77, + "target" : { + "node" : 23, + "path" : "scale" + } + }, + { + "sampler" : 78, + "target" : { + "node" : 22, + "path" : "translation" + } + }, + { + "sampler" : 79, + "target" : { + "node" : 22, + "path" : "rotation" + } + }, + { + "sampler" : 80, + "target" : { + "node" : 22, + "path" : "scale" + } + }, + { + "sampler" : 81, + "target" : { + "node" : 21, + "path" : "translation" + } + }, + { + "sampler" : 82, + "target" : { + "node" : 21, + "path" : "rotation" + } + }, + { + "sampler" : 83, + "target" : { + "node" : 21, + "path" : "scale" + } + }, + { + "sampler" : 84, + "target" : { + "node" : 26, + "path" : "translation" + } + }, + { + "sampler" : 85, + "target" : { + "node" : 26, + "path" : "rotation" + } + }, + { + "sampler" : 86, + "target" : { + "node" : 26, + "path" : "scale" + } + }, + { + "sampler" : 87, + "target" : { + "node" : 25, + "path" : "translation" + } + }, + { + "sampler" : 88, + "target" : { + "node" : 25, + "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, + "path" : "scale" + } + } + ], + "name" : "Armature|Armature|ArmatureAction", + "samplers" : [ + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 15 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 16 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 17 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 18 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 19 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 20 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 21 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 22 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 23 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 24 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 25 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 26 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 27 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 28 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 29 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 30 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 31 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 32 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 33 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 34 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 35 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 36 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 37 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 38 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 39 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 40 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 41 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 42 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 43 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 44 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 45 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 46 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 47 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 48 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 49 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 50 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 51 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 52 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 53 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 54 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 55 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 56 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 57 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 58 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 59 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 60 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 61 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 62 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 63 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 64 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 65 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 66 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 67 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 68 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 69 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 70 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 71 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 72 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 73 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 74 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 75 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 76 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 77 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 78 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 79 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 80 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 81 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 82 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 83 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 84 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 85 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 86 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 87 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 88 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 89 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 90 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 91 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 92 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 93 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 94 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 95 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 96 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 97 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 98 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 99 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 100 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 101 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 102 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 103 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 104 + }, + { + "input" : 14, + "interpolation" : "LINEAR", + "output" : 105 + }, + { + "input" : 14, + "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 + } + ] + } + ], + "materials" : [ + { + "doubleSided" : true, + "name" : "BagMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.5 + } + }, + { + "alphaMode" : "BLEND", + "doubleSided" : true, + "extensions" : { + "KHR_materials_specular" : { + "specularColorFactor" : [ + 0, + 0, + 0 + ] + }, + "KHR_materials_ior" : { + "ior" : 1.4500000476837158 + } + }, + "name" : "BodyMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1 + ], + "metallicFactor" : 0 + } + } + ], + "meshes" : [ + { + "name" : "Cube.003", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2, + "JOINTS_0" : 3, + "WEIGHTS_0" : 4 + }, + "indices" : 5, + "material" : 0 + } + ] + }, + { + "name" : "Cube.012", + "primitives" : [ + { + "attributes" : { + "POSITION" : 7, + "NORMAL" : 8, + "TEXCOORD_0" : 9, + "COLOR_0" : 10, + "JOINTS_0" : 11, + "WEIGHTS_0" : 12 + }, + "indices" : 13, + "material" : 1 + } + ] + } + ], + "skins" : [ + { + "inverseBindMatrices" : 6, + "joints" : [ + 52, + 45, + 5, + 4, + 3, + 2, + 1, + 0, + 28, + 27, + 8, + 7, + 6, + 14, + 13, + 12, + 11, + 10, + 9, + 20, + 19, + 18, + 17, + 16, + 15, + 23, + 22, + 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 + ], + "name" : "Armature" + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 506, + "max" : [ + 0.1090814545750618, + 0.40452075004577637, + 0.0857388824224472 + ], + "min" : [ + -0.09462108463048935, + 0.2630254030227661, + -0.11617939174175262 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 506, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 506, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5121, + "count" : 506, + "type" : "VEC4" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 506, + "type" : "VEC4" + }, + { + "bufferView" : 5, + "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" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 3484, + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 3484, + "type" : "VEC2" + }, + { + "bufferView" : 10, + "componentType" : 5123, + "count" : 3484, + "normalized" : true, + "type" : "VEC4" + }, + { + "bufferView" : 11, + "componentType" : 5121, + "count" : 3484, + "type" : "VEC4" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 3484, + "type" : "VEC4" + }, + { + "bufferView" : 13, + "componentType" : 5123, + "count" : 17472, + "type" : "SCALAR" + }, + { + "bufferView" : 14, + "componentType" : 5126, + "count" : 51, + "max" : [ + 2.125 + ], + "min" : [ + 0.041666666666666664 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 15, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 18, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 21, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 24, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 27, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 30, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 33, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 34, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 35, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 36, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 37, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 38, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 39, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 40, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 41, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 42, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 43, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 44, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 45, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 46, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 47, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 48, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 49, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 50, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 51, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 52, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 53, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 54, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 55, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 56, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 57, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 58, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 59, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 60, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 61, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 62, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 63, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 64, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 65, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 66, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 67, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 68, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 69, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 70, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 71, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 72, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 73, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 74, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 75, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 76, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 77, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 78, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 79, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 80, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 81, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 82, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 83, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 84, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 85, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 86, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 87, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 88, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 89, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 90, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 91, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 92, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 93, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 94, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 95, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 96, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 97, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 98, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 99, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 100, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 101, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 102, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 103, + "componentType" : 5126, + "count" : 51, + "type" : "VEC4" + }, + { + "bufferView" : 104, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "bufferView" : 105, + "componentType" : 5126, + "count" : 51, + "type" : "VEC3" + }, + { + "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, + "type" : "VEC3" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 6072, + "byteOffset" : 0, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 6072, + "byteOffset" : 6072, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 4048, + "byteOffset" : 12144, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 2024, + "byteOffset" : 16192, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 8096, + "byteOffset" : 18216, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 4692, + "byteOffset" : 26312, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 3392, + "byteOffset" : 31004 + }, + { + "buffer" : 0, + "byteLength" : 41808, + "byteOffset" : 34396, + "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, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 55744, + "byteOffset" : 187692, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 34944, + "byteOffset" : 243436, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 204, + "byteOffset" : 278380 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 278584 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 279196 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 280012 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 280624 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 281236 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 282052 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 282664 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 283276 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 284092 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 284704 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 285316 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 286132 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 286744 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 287356 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 288172 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 288784 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 289396 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 290212 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 290824 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 291436 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 292252 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 292864 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 293476 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 294292 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 294904 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 295516 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 296332 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 296944 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 297556 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 298372 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 298984 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 299596 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 300412 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 301024 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 301636 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 302452 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 303064 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 303676 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 304492 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 305104 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 305716 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 306532 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 307144 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 307756 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 308572 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 309184 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 309796 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 310612 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 311224 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 311836 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 312652 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 313264 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 313876 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 314692 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 315304 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 315916 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 316732 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 317344 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 317956 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 318772 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 319384 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 319996 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 320812 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 321424 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 322036 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 322852 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 323464 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 324076 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 324892 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 325504 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 326116 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 326932 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 327544 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 328156 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 328972 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 329584 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 330196 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 331012 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 331624 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 332236 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 333052 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 333664 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 334276 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 335092 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 335704 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 336316 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 337132 + }, + { + "buffer" : 0, + "byteLength" : 612, + "byteOffset" : 337744 + }, + { + "buffer" : 0, + "byteLength" : 816, + "byteOffset" : 338356 + }, + { + "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 + } + ], + "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+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8LDQoOCw0KDgsNCg4LDQoOCw0KDgsKDQ4LCg0JCwoNCQsKDQkLCg0JCwoNCQsKDQkLCg0JCwoNCQsKDQ4LCg0JCwoNEwsKDQ4LCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoTDQsKDRMLChMNCwoNEwsKEw0LCg0TCwoTDQsKDRMLCg0TCwoTDQsKEw0LCg0TCwoTDQsKDRMLChMNCwoNEwsKEw0LCg0TCwoTDQsKDRMLChMNCwoNCQsKEw0LCg0JCwoNCQsKEw0LChMNCwoNCQsKDQkKCxMNCgsNCQoLCRMKCw0JCQ0KCw0JCgsJEw0KCQ0TCAkTDQgNCQ4ICQgNEwkNCA4JCBMNCAkNDggJDQ4ICRMNCAkBDQgJAQ4ICQEhAQgJIQgBCSEIAQkhAQgCKwEhCAIBCCErAQIhJQECJQgBAiEAAQIlIQECISsBAiUAAQIlIQIBISUBAiUhAQIlIQIBJSEBAiUhAQIjJQECIyUBAiMAAQIjAAECJQABAiUAAQIfJQECIx8BAiUfAQIjJQECIy0BAiUfAQIfIwEfAiUBAiMfAR8CJQECHyMBHwIlAR8CJQEfAiUBHwIlAR8CJQEfAiUBHwIjAR8CIx8BAiUfAQIlHwECJQEfAiMfAQIAHwECJR8BAiUfAQIjHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwEjAB8BAAAfAQgJHwEICR8BCAkBHwgJAR8ICQEfCAkBHwgJHwEICR8BCAkBHwgJAR8ICQEIHwkBCB8JAQgJHwEIHwkIAQkfAQgJAggBCR8ICQENCAkBHwgJAQ0ICQ0OCAkNDggJDRMJCA0OCQgNEwkIDQ4JDRMIDQkOCAkNEwgJDQoLCQ0TCg0JCgsKCw0JCgsNCQoLCRMLCg0JCwoTDQsKDQkLCg0JCwoNCQsKDQkLCg0JCwoTDQsKEw0LCg0JCwoNCQsKEw0LCg0JCwoTDQsKDQkLCg0TCwoTDQsKDQ4LCg0TCwoNAAsKEw0LCg0ACwoNEwsKDQALCg0ACw0KAAsKAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALCgAACwoAAAsKAAALCgAACwoAAAsKAAALCgAACwoAAAsKAAALCgAACwoAAAsKAAALCgAACwoAAAsKDQALCg0ACwoNAAsKAAALCg0TCwoNEwsKDQALCg0TCwoNEwsKDQALCg0ACwoNEwsKDRMLCg0TCwoNEwsKEw0LCg0TCwoTDQsKDRMLCg0TCwoTDQsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDRMLCg0TCwoNEwsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDRMLCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNDgsKDQ4LCg0ACwoNDgsKDQ4LCg0OCwoODQsKDQ4LDgoNCw4NCgsODQoLDg0KCw4NCgsODQoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoLDQ4KCw0OCgsNDgoNCw4KCw0OCg0LDgoNCw4KDQ4LAA0OCwoODQsADg0LAA4NCwAODQsADg0LAA4NCwAODQsADg0LAA4NCwAODQsADg0AAA4NAAAODQAADg0AAA4PAAAODwAADg8AAA4PAAAODwAADw4QAA4PEAAODxAADg8AAA8OAAAODxANDg8QAA4NDwkODxANDg0PCA4PEAAODxANDg8QDQ4PDQgODw0IDw4QAA8OEAAODxANDg8NCQ8OEAAODxANDg0PCQ8OEAAODxAADw4QAA4PDQAODxAADw4QAA4PAAAODQAADg0LAA4NAAAODQsADg0JIQ0OCQsODQkIDQ4JCA4JDQgNDgkLDQ4JIQ0OCwoNDgsKDQ4LCg0LDgoNCwoODQsOCgsNCg4LDQ4KCw0OCgsNDgoNCw4KCw0OCgsNDgoNDgsKDQsOCg0OCwAODQsADg0LAAsNDgoLDQoOCw0KDgsKDQ4LCg0OCwoNDg0KCwkNCQoLDQkOCAkNDggJCA0OCQ0IDggJDQ4ICQINAQgJIQEICQIBIQgCAQIhCCECAQACASEAAiEBAAIhAQACIQEAAiEBAAIhAwECAwQhAiEBAwIhAwACAwQAAgMhAAIhASMCAwQAAgMjBAIDIwQCASMhAgEjIQMCBAADAgQAAiMDBAIjAQADAgQAAiMBAwIjAQAEAgMAAgMBIwIjAQAEAgMjAiMEAQIEAyMCASMEAgQDIwIBIwMCASMDAgMEAAIDBAACAQMhAgMEAQIDIQQCAwQAAgMEAAIDBCECAwQAAgMEAAIDBAADAgQAAwIEAAQDAgADBAIAAwIEAAQDAgAEAwIAAwQCAAQAAAAEBQAABAMAAAQAAAAEBQAABAAAAAQFAAAEAwIABAMCAAQDAgAEBQAABAUAAAQDAgAEAAAABAIDAAQAAAAEAgMABAAAAAQCAwAEAAAABAIDAAQAAAAEAgMABAIDAAQAAAAEAAAABAMCAAQAAAADAgQAAwIEAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAEBQAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQAAAAUGAAAFAAAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABgAAAAYFAAAGBQAABgAAAAYFAAAGBQAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABQYEAAUGBAAFBgQABQYEAAUGBAAFBAYABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQCAAQFAgAEBQIABAUCAAQFAgAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAQFAAAFBAIABQQCAAQFAAAEBQAABQQAAAUEAAAFBAAABAUAAAUEAAAFBAAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABQQAAAUGAAAFBgAABQYAAAUGAAAFBgAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAFBgAABQYAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABQQCAAQFAAAEBQAABAUAAAQFAAAEBQAABAUAAAUEAAAFBAAABQQAAAUEAAAFBAAABQQAAAUEAAAFBgAABQYAAAUGAAAFBgAABQYAAAUGAAAFBgAABQYAAAUEAAAFBAAABAUAAAQFAAAFBgAABQYAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABgUAAAYFAAAGBQAABQYAAAUGAAAGBQAABgUAAAYFAAAGBQAABgAAAAYFAAAGBQAABgUAAAYFAAAGBQAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGBQAABgUAAAUGAAADBAIAAwQCAAMCBAAjAgEfAQIjHyMCAQABIwIAASMCAAEjAh8BIwgCAQgjAgEjAgABCAkCAQgJAggJDSMICQIfCAkNDggJDQ4JDQ4IDQ4JCA0JDggNDgkIDQkLCg0OCwkNCwoJDQsKDgsKDQkLDQoOCwoNCQsKDQkLDQoOCw0KDgsKDQkLDQoOCw0KDgsNCg4LDQoOCw0KDgsKDQ4LCg4NCw0KAAsNCg4LDQAACw0AAAsAAAALDQAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsKAAALAAAACwAAAAsAAAALAAAACw0AAAsNAAALDQoACw0KAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCgAACwoAAAsAAAALAAAACwAAAAsAAAALCgAACwoAAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALDQAACw0AAAsNAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACw0AAAsNAAALDQAACw0AAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACwoNAAsKAAALCgAACwoAAAsAAAALAAAACwAAAAsAAAALCgAACwoNAAsKDQALCg0ACwoNAAsKDQALCg0ACw4KDQsKDQALDg0ACw0AAAsNAAALDg0ACw0OAAsODQALDg0ACw0OAAsNDgALDQ4ACw4NCgsODQALDQ4ACw0OAAsNAAALDQAACwAAAAsNAAALDQAACwAAAAsAAAALDQAACwAAAAsNAAALAAAACw0AAAsAAAALDQAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALDQAACwAAAAsNAAALDQAACw0OAAsNAAALDQ4KCw4KDQsNCg4LDgoNCw0OCgsODQoLDQ4KCw0OCgsNCg4LDQ4KCw0OCgsNDgoLDQoOCw0KDgsNCg4LDQoOCw0OCg0LCg4NCw4KDQ4LCg0OCwoNDgkLDQ4JCw4NCQgODQkIDg0JCA4NCAkODQgPDg0IDwgJDQ4ICQ0CCAkNAggJAgEICQIBCAkCAQgBCQIIAQkCCAEJAgECIwgBAiMhAQIjIQIBIyECASMhAgEjIQIjAQACIwEhAiMBIQIhAQACASEAAQIhCAgBAiEICQIhCAkODQ4JDQ8LAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAAQgJHwEfCAkBAiMfAR8CCAECIx8BHwIjAR8CIwEfAiMBHwIjAR8IAgEfCAIfAQIIAR8ICQEfCAkfAQgJHwEjAB8BIwAfASMtHwEjAB8BAAAfAQAAHwEAAB8BAAAfAAAAHwAAAB8AAAAfAAAAHwEAAB8tIwEfAQIjDw4QAA8OEAAQDw4AEBEAABAPDhEQDwAAEA8AABARAAAQEQAAEA8AABARAAAPEA4AEA8AAA8QDgAQEQAAEA8OERARDwAPEA4AEA8OERARAAAPEA4AEA8OERAPDhEQEQAAEBEAABAPDhEQEQAAEA8OERARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEAAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEQAAAREAAAERAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAABEAAAAQEQAAEBEAABARAAAQEQAAERAAABEQAAAREAAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAAEBEAABARAAAQEQAADw4QAA8OEAAPDhAADw4QAA8QDgAPDhAADxAOAA8OEAAPDhAADw4QAA8OEAAODxAACwoNAAsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTAAsKEwALChMNCwoTAAsKEwALChMACwoAAAsKEwALCgAACwoAAAsKAAALCgAACwoAAAsKAAALCgAACwoAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsKEwALEwoACwoTAAsKEwALChMNCwoTAAsKEw0LChMUCwoTCQsKEwkLChMJCwoTCQsKEwkLChMJCwoTCQsKEwkLChMJCwoTCQoLEwkKCxMJCRMKCxMJCgsJEw0IEwkUCAkIExQJCBMUCAkTFAgJExQICQETCAkBEwgBCR8BCAklAQgfCQEICR8BHwgJAQgfCQEfCAkBHwgJAR8ICQEfCAkfAQgJHwEICR8BAAAfAS0AHwEAAB8BAAAfAQAAHwEAAB8BAAAfAQAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAQAAHwEAAB8BAAAfAQAAHwEAAB8BAAAfASUAHwElLR8BJQIBHyUtAR8lAgEfJQIBHyUtAR8lLQEfJQIBJR8tASUfLQElLR8BJS0CASUtIwElHwIBJS0fASUtAgElLQIBJS0AASUtAAElAislAQIrASUCKyUBKwIBJQIrASUrIQElKwIBJSsAAQglIQErCCUBCAkrCAEJKwgJARMICQEUCAkTFAgJExQJCBMNCRMIFAkTDQgTCRQICRMKCxMJCgsKCxMJCgsTCQsKEwkLChMJCwoTCQsKEwkLChMJCwoTCQsKEwkLChMJCwoTDQsKEwkLChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKExQLChMJCwoTFAsKEwkLChMUCwoTCQsKEwkLEwoUCxMKFAsKEwkLEwoUEwoLCRMLChQTCQoLExQLChMJFAgTFAkrCRMUCBMUCQgJCBMUCRMIFAgJExQICSUTAQgJKwEICSUBKwglASUrCCslAQAlASsAJSsBACUrAQAlKwEAJSsBACUrJgElJicrJSsBJiUrJgAlJicAJSYrACUrAS0lJicAJSYtJyUmLSclAS0rJQEtKyYlJwAmJScAJS0mJyUtAQAmJScAJS0BJiUtAQAnJSYAJSYBLSUtAQAnJSYtJS0nASUnJi0lAS0nJScmLSUBLSYlAS0mJSYnACUmJwAlASYrJSYnASUmKyclJicAJSYnACUmJyslJicAJSYnACUmJwAmJScAJiUnACcmJQAmJyUAJiUnACcmJQAnJiUAJiclACcAAAAnKAAAJyYAACcAAAAnKAAAJwAAACcoAAAnJiUAJyYlACcmJQAnKAAAJygAACcmJQAnAAAAJyUmACcAAAAnJSYAJwAAACclJgAnAAAAJyUmACcAAAAnJSYAJyUmACcAAAAnAAAAJyYlACcAAAAmJScAJiUnACcoAAAnKAAAJygAACcoAAAnKAAAJygAACcoAAAnKAAAKCcAACgnAAAoJwAAKCcAACgnAAAoJwAAKAAAACgpAAAoAAAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKQAAACkoAAApKAAAKQAAACkoAAApKAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKCknACgpJwAoKScAKCknACgpJwAoJykAKCcAACgnAAAoJwAAKCcAACgnAAAoJwAAKCcAACgnAAAoJwAAKCcAACgnAAAoJwAAKCclACcoJQAnKCUAJyglACcoJQAnKAAAJygAACcoAAAnKAAAJygAACcoAAAnKAAAJygAACcoAAAnKAAAJygAACcoAAAoJyUAKCclACcoAAAnKAAAKCcAACgnAAAoJwAAJygAACgnAAAoJwAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKCcAACgpAAAoKQAAKCkAACgpAAAoKQAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAAoKQAAKCkAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKCclACcoAAAnKAAAJygAACcoAAAnKAAAJygAACgnAAAoJwAAKCcAACgnAAAoJwAAKCcAACgnAAAoKQAAKCkAACgpAAAoKQAAKCkAACgpAAAoKQAAKCkAACgnAAAoJwAAJygAACcoAAAoKQAAKCkAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKSgAACkoAAApKAAAKCkAACgpAAApKAAAKSgAACkoAAApKAAAKQAAACkoAAApKAAAKSgAACkoAAApKAAAKQAAACkAAAApAAAAKQAAACkAAAApAAAAKQAAACkAAAApKAAAKSgAACgpAAAmJyUAJiclACYlJwAtJQEfASUtHy0lAQABLSUAAS0lAAEtJR8BLQglAQgtJQEtJQABCAklAQgJJQgJEy0ICSUfCAkTFAgJExQJExQIExQJCBMJFAgTFAkIEwkLChMUCwkTCwoJEwsKFAsKEwkLEwoUCwoTCQsKEwkLEwoUCxMKFAsKEwkLEwoUCxMKFAsTChQLEwoUCxMKFAsKExQLChQTCxMKAAsTChQLEwAACxMAAAsAAAALEwAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsKAAALAAAACwAAAAsAAAALAAAACxMAAAsTAAALEwoACxMKAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALCgAACwoAAAsAAAALAAAACwAAAAsAAAALCgAACwoAAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALEwAACxMAAAsTAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACxMAAAsTAAALEwAACxMAAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKAAALCgAACwoAAAsAAAALAAAACwAAAAsAAAALCgAACwoTAAsKEwALChMACwoTAAsKEwALEwoACwoTAAsKEwALEwoUCwoTFAsKEwALChMUCwoTFAsKExQLChMUCxQKEwsUEwoLFBMKCxQTCgsUEwoLFBMKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKCxMUCgsTFAoLExQKEwsUCgsTFAoTCxQKEwsUChMUCwATFAsKFBMLABQTCwAUEwsAFBMLABQTCwAUEwsAFBMLABQTCwAUEwsAFBMLABQTAAAUEwAAFBMAABQTAAAUFQAAFBUAABQVAAAUFQAAFBUAABUUFgAUFRYAFBUWABQVAAAVFAAAFBUWExQVFgAUExUJFBUWExQTFQgUFRYAFBUWExQVFhMUFRMIFBUTCBUUFgAVFBYAFBUWExQVEwkVFBYAFBUWExQTFQkVFBYAFBUWABUUFgAUFRMAFBUWABUUFgAUFQAAFBMAABQTCwAUEwAAFBMLABQTCSsTFAkLFBMJCBQJEwgTFAkLExQLChMUCwoTCxQKEwsUCgsTChQLExQKCxMKFAsTChQLExQKCxMUCgsTChQLChMUCxMUCgsTChQLChMUCwoTFAsTFAoLExQKEwsUChMUCwoTCxQKExQLABQTCwAUEwsAFAkTFQgJFBMUEwgVFBMIFQgJEyUICRMlFBMICQgJExQUEwkIFBMJCBQTCQgTFAkLExQJCxMUCwoTFAsKEwsKFBMLFAoLEwoUCxMUCgsTChQLEwoUCxMUCgsTFAoLEwoUCxMKFAsTFAoLExQKCxMUCgsTFAoLFBMKCxQTCgsUEwALExQACxMUAAsUChMLExQACxQKEwsTChQLExQKCxMAAAsTAAALEwAACxMAAAsTAAALAAAACxMAAAsAAAALEwAACxMAAAsTFAALExQACxMUAAsTFAALFBMACxQTAAsUEwALFBMACxQKEwsTAAALEwAACxMAAAsTCgALEwAACwAAAAsTAAALAAAACxMAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACAklAQgJJQEICSUBCAEJJQgBCSUIAQklASUtCAElLSsBJS0rJQEtKyUBLSslAS0rJS0BACUtASslLQErJSsBACUBKwABJSsICAElKwgJJSsVFBYAFRQWABYVFAAWFwAAFhUUFxYVAAAWFQAAFhcAABYXAAAWFQAAFhcAABUWFAAWFQAAFRYUABYXAAAWFRQXFhcVABUWFAAWFRQXFhcAABUWFAAWFRQXFhUUFxYXAAAWFwAAFhUUFxYXAAAWFRQXFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFwAAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFxYAABcWAAAXFgAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABYXAAAWFwAAFhcAABYXAAAXFgAAFxYAABcWAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAWFwAAFhcAABYXAAAVFBYAFRQWABUUFgAVFBYAFRYUABUUFgAVFhQAFRQWABUUFgAVFBYAFRQWABQVFgALChMUCwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMACwoTAAsKEwALChMNCwoTAAsKEwALChMNCwoTDQsKEw0LChMNCwoTDQsKEw0LChMNCwoTDQsKEwALChMACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAEICR8BHwgJASUtHwEfJQgBJS0fAR8lLQEfJS0BHyUtAR8lLQEfCCUBHwglHwElCAEfCAkBHwgJHwEICR8BLQAfAS0AHwEjLR8BLQAfAQAAHwEAAB8BAAAfAQAAHwAAAB8AAAAfAAAAHwAAAB8BAAAfIy0BHwElLQsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAACIQEDAiEDAAIhAyMCASMDAgEjAwIjAQMCIwEDAgEDIwIBIwQCASMfAgEjAwIBIwMCAQMlAiEBAyUrASYlKyYAJSsmLSUBLSYlAS0mJS0BJiUtASYlASYtJQEtJyUBLR8lAS0mJQEtJiUBJgIlKwEmZszmPnKYgD5B3mQ+HbCYPWbM5j5ymIA+Qd5kPh2wmD0/E88+EDaUPt+jZz4Dk6M91YPjPmaNlz7quB8+O0nUPdWD4z5mjZc+6rgfPjtJ1D2aHgE/kNVePjZTVD6guZA9TuDoPkOTkD6wglU+z1hePU7g6D5Dk5A+sIJVPs9YXj0PsOg+PLOfPjy+Kz5c9oY9D7DoPjyznz48vis+XPaGPSZ80T4yk5k+yMpsPh1adD3+DQA/KzOHPufKPz42W0Y9G+T8PkprlD7jOh8+X5l4PedKFj8zGGw+S1gWPpaPET21KRw/q7RKPtl0Az5SX4I9az0SP41mgT5MRwI+qddHPf2XNz+IJik+jlDTPTIUbTwh9zw/CZYOPgO5yj2mh8E8I1cvP3HJPD6bXMU94q4MPdwNSj+WMAM+/XuOPZefVTw/IEU/QVoOPoSchT3/s9I8cOFOP60X7j1s75U97KwdOzFXVT/FPPU9ROEZPavIGDyT2VI/wIL/Pd/hEz25/X08wPpYP4+/5z33/RE9Am9tO80mVz98DwI+ASbFPDwJCzxsBlo/Jmz2PSR8wTzfFpA74fZUPyUDBj54k8s8i+5KPDseVz92ugM+HEm5PE03Cjw7Hlc/droDPhxJuTxNNwo84m5VPxbhBz7r2LA8c4REPOJuVT8W4Qc+69iwPHOERDy9ylk/FNv6PcuWtDxylZo7vcpZPxTb+j3LlrQ8cpWaO0LhVj8j5gY+fYarPGxAAjzHZ1U/qboKPjw3pDws9TE8xvRYP1HcAT7sqak8GmujOxndUT919Cw+GMD9OwpKajsrWVM/zlInPo9/BDx0IkA7zMdQP1YUMT6mZew7NyqNOyRtUT9y3DQ+mbhkO3UQ7jrtLlI/28UxPv2/djvTt9E6HcxQP4dZNz4EpVQ71dsIO+yeVD9RYio+lYjIOqV3yDoUiFA/yH04Po+BPzu19xg7u2RQP8IFOT7B7Sw7OeUsO1EhUD+lizM+5ofXO9tapjvE308/YJI0Pivrvju95r47iZNUP57TDD71xJY831lgPF5OVD9yiQ0+8/SEPMHzhDxHUFQ/Y7QJPtwSqzyvgno8R1BUP2O0CT7cEqs8r4J6PEYOVD8qcQo+iFeVPFtWlTxGDlQ/KnEKPohXlTxbVpU8QyVUP7FhCD71gbU8LMiCPH7bUz/RGAk+e+WdPErknTxq4FE/3osCPrh6AT1Rnqw8L0JRP16oAz4hPN08+DrdPOOuQj/l4xI+sIBvPYwBGj25lkA/SiUVPvD/UD1t/1A9I9UrP3OjRj6DmKQ9++5ePdq4Kj/nDkw+vQ2JPaENiT28MQ8/op+LPoxw2D0cBn8923EOPzeEjz4xMKc9GDCnPUyo+j7ijaU+dmfzPca/iz0q/fk+b9uqPtpOtj3CTrY9TfHkPlmMtD4/jwA+6OqYPU3x5D5ZjLQ+P48APujqmD37/uQ+76i7PjWwvj0esL49+/7kPu+ouz41sL49HrC+PUf0yz5+C8Q+UMoJPkpsrD1H18w+CuWvPiAkOz58xpY93YLNPs74zD62CMs9oAjLPZNO2T6leIY+U/czPj56DD5cPuY+/UeJPtAWID553AA+KZHBPp4NhT7GH4A+xgXlPZhTzj5sRps+Md4wPojb9z2aLP4+g+l+PmNGEj7R7eQ92qzlPlZuPj5Sbj4+osk3PqwcBj82w5Y+UG3UPXygnz0w5xE/iZExPoeRMT5YgKo9khcGP1f5oD4gLrY9+S+VPbXf7j4ngoQ+XhJIPthToj0wuMg+NgmLPsBpTz5zEwk+wZH0Pm8IiT7SZQI+0GUCPomY5D7ivco+dEbcPc6/TD0oetQ+pPOmPoUPBT7lFAQ+3wfiPiBx1D4ADpM9/g2TPb5tAD+CKpI+b7YEPiZ7qj3YqRk/SeR8PhQjyj0/i109BPMNPxnVnz4NrAc+NLBdOtMZ5T5MXMw+Gm0YPibVlDtL69U+N0/QPmljMj72yJM6xurbPnav1D6Mlxc+ZH/mO9IqTj81wAY+b0ZaPT8uIDx860g/hfSoPbWrkT2+B3w9gKxCP+DZdD5xQGg5cUBoOV8eSz9u+fI99FVHPUbRID0za1o/nHiSPZx4kj2OpnY71DkWPxBmgT6PTCQ+AAAAADePRj9LqSw+FVELPYwssjzi8N0+t0+1PigvWT7/TZ85FH5QP7EHvj2xB749AAAAAH5YPz8NeVs+FK8RPTROLjs3mRI/szqBPnNcLT5vKbk7hMtOP0uPxD1Jj8Q9V0iFOduwQz8xUTw+DlhDPYqsgju3mQ4/TkTVPnri0TwDOVQ6w/pAP8Es9j3BLPY9Oga9O4aZTj8+jz8+MJ6VO87erjqGmU4/Po8/PjCelTvO3q46dF0CP3mW7D7z6eo8AAAAAHRdAj95luw+8+nqPAAAAADnFFg/ZKyfPWSsnz0AAAAA5xRYP2Ssnz1krJ89AAAAAA9rID/WX50+8q0vPeJEvTzIEfQ+PpLfPlhdmT1jlEA8mq8ZP34lIT5+JSE+Pu2tPTMbFj9gbpU+fJGlPd22Jz1ISso+4/rIPqsdWT5R/a85Y175PnrxOD568Tg+SWAbPsjGJj9Sbyc+bYzyPbJeiD2fVCo/xQkQPr2jxj29o8Y9T2APP+SReD7Nye897g+kPThDLj8FND0+j4jxPa3WhzybVyU/NeMmPh+UIj4WqQQ9RB0sPxSuUT69uXs9vbl7PekxGT9gU58+pZaqPSrJ6DvpMRk/YFOfPqWWqj0qyeg7WQsHPytUwT6SVEI9klRCPVkLBz8rVME+klRCPZJUQj1ZCwc/K1TBPpJUQj2SVEI9qtEbP0gbgj57dAk+zJNDO6rRGz9IG4I+e3QJPsyTQzuy7Qc/+nvXPlOtPD3zewk79yorPzVKjz7Z/tI82f7SPPcqKz81So8+2f7SPNn+0jx+f+8+DoXgPnp+vz1mr145+LJ3P/Ju8zy9jzE7AAAAADEjfj9vctg7OKmvOTiprzkxI34/b3LYOziprzk4qa85MxdLPzAkTD7mG+k725FYOTU4fT+w8jE8AAAAAAAAAACcAH8/OGR/OwAAAAAAAAAAKlF6P8zatTwAAAAAAAAAACpRej/M2rU8AAAAAAAAAACWDFs/p80TPgAAAAAAAAAACmhWP9pfJj4AAAAAAAAAAApoVj/aXyY+AAAAAAAAAACVi3o/XI2uPAAAAAAAAAAAY6B2P9X5FT0AAAAAAAAAAFCsfz84Xqc6AAAAAAAAAAC+jHE/JTRnPQAAAAAAAAAAvoxxPyU0Zz0AAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAA8d/P5fyYzoAAAAAAAAAANj1fz/qeSI5AAAAAAAAAADGh38/wHPwOgAAAAAAAAAArr98P20UUDwAAAAAAAAAAJQXez+QDZ08AAAAAAAAAACzzn4/TKaYOwAAAAAAAAAAysNyP2bDUz0AAAAAAAAAAGVkdz+suQk9AAAAAAAAAAA8z20/HoaRPQAAAAAAAAAAbFNaP1KyFj4AAAAAAAAAAHd4Tz9AmT8+rDkhOwAAAACKqWE/r7PyPQAAAAAAAAAA6BojP10vlj7Q7Ys9cl6fOndHGz/kUbQ+dBAjPQggvTr3dCc/XJOJPuIrnD1efG86lebiPqw9xz76NA8+LhTkPJXm4j6sPcc++jQPPi4U5Dw4vAM/hg2xPjK23D33YwI9OLwDP4YNsT4yttw992MCPZc+3D5ziNQ+/2H6Pa4DBT2XPtw+c4jUPv9h+j2uAwU9U7ABP0XAfT6VFCE+uNO0PedK8D4AdJA+OjQcPvGbxD18eBk/YEIvPoTEID5VLpQ9pcP0PmZphT4t0As+dav/PXJeFz8VHII+DInGPSEmbD3/id8+s6V+PgplMz5H4Q4+EOTPPgYKtD6txjY+T7qCPXwQ7T7DJMs+QCoLPpBojTudLso+nYKlPkqWOT6BDs49mbUSPwrWeT49uCc+1tqcPLCmEz8MRIE+JmAtPm+Bvjp/xxI/aAOXPsGY1T2PduA8ZwoTPwwzlD76SrU9SitDParfED8lzoI++if6PU5EZz2W9Bc/402XPtUjYz3VI2M9SCgJP5dwdj7Phh8+9c6KPZz0DD8AJH4+lQnOPZUJzj0qR+Y+W+NiPizaWz5LaOk9rikEP3u+kT74otE9pBXGPQ638D7/yrE++ZW9PdlhuD31dBA/g5YrPoOWKz5R/s09oPHMPtOslz7Geyk+T0cNPkIr6j6LaT4+i2k+Pl/WLj7iWPI+Wh6CPpLmED73KgY+2/vFPrl3kj5ebjY+eKoYPgoCrz5gy48+rpGCPqWD+j3lltM+7B2VPvStKj5z6AM+ZezVPr08sz4R9ww+VG3BPSSi2D62F70+S4zUPUeM1D3M0tI+79GiPldXPz5uvqo9OFjtPjhHpT7uywQ+W+qrPThY7T44R6U+7ssEPlvqqz1yVeo+0sCWPmkvMT4gSJk9clXqPtLAlj5pLzE+IEiZPYqS7z7taaw+EwfIPQ4HyD2Kku8+7WmsPhMHyD0OB8g9AXQAP9Fmlz4q3QA+ZAqdPTHi/T6Dtok+zdcqPprtiz1swAE/QMWcPtFzvz3Mc789dusSP1Jwez7Y+O8928qBPYjFEz9wGIE+/LiuPfa4rj3VjhE/X6NhPmfdHj6ND2U98jw3P0fdHD6f37c9h/woPRGkNj/rdh8+0viFPc34hT2ZWTk/UwwOPuHo9D25xpA80VxbP7+Npz0VrFM9jq0dPAk0Xz+qtJ09GlZRPQAAAADuV1o/SfeoPVNJBD08SQQ9STpwP7iHRD0JT188AAAAADkxcD/e0E49RW64O0VuuDujem8/2t4iPcDtyjwAAAAAWMl9P23fCzyHR+U4AAAAAPP8fT/D5os7zD9rOwAAAABQIH0/Bew3PAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAHfB9P9z4AzwAAAAAAAAAAIi5fT8PnhE8AAAAAAAAAAAG/30/lj4APAAAAAAAAAAA8IB3Pw7xBz0AAAAAAAAAAHTydj/L2BA9AAAAAAAAAADWvXc/rSIEPQAAAAAAAAAAfQdzPzWITz0AAAAAAAAAAJMNcj/FJl89AAAAAAAAAABZ23M/b0pCPQAAAAAAAAAAO1txP1JMaj0AAAAAAAAAAGCycT8K2mQ9AAAAAAAAAAC5FnE/eJRuPQAAAAAAAAAARztuP8sljj0AAAAAAAAAAI6lbj+M04o9AAAAAAAAAABNA24/hkaPPScRnzkAAAAAy+dpP+lQrj2eL5w6AAAAAJrbaD8pQrE9/iB8OwAAAAAus2o/kGaqPQAAAAAAAAAA1TZlPzSDzD3arpc7CVsWOUyWZT/lFcs9gHwDO4B8AzsxbWQ/i37NPQR+8TsAAAAAtfljP+h80z3sj6w7KDd2OrX5Yz/ofNM97I+sOyg3djrDa2M/7EDUPZIHAzwAAAAAw2tjP+xA1D2SBwM8AAAAAAUvZD/uxNI9wi48O8IuPDsFL2Q/7sTSPcIuPDvCLjw75+phP5/d3z1g+sU7JHANO/hkYT+8ROE9ibkNPNAnbjqaAmI/SFnfPaCRhDu4jIQ79xRaP/QDEj5aoGY7pWoDOwwyWj/awxE+5oIuO/Z5LjubrFk/AAETPtUKlTtuH9I6DHZXP/2yHj5uoQM75CWzOnqTVz8EWB4+S43WOhZ81jrYQlc/yS8fPifEIjvB4Zw6yW5WP93IIT7ESVY7dWyROtdsWD8mCRY+c0nWO+GXyDrUxVU/U4sjPpZihTtdI5k6toJXP+x6GD4zKPU7g33oOjqeVD9YRig+MUd9O7jQpTpReFY/rK4bPvtEBDz37wo7jP9bPyOg9D0QG508nJYDO2piXj81Je49sA5jPO9pmTout14/m87jPaLfmTwAAAAALrdeP5vO4z2i35k8AAAAAL9kXz99mNw9FgahPAAAAAA13WA/+cLaPfeacjwAAAAANd1gP/nC2j33mnI8AAAAANOmYT/BltQ9Q5VxPAAAAABsTmI/m2zVPUcAQTwAAAAAbE5iP5ts1T1HAEE8AAAAAFsvYz8Olc89g4A3PAAAAAA9+l8/gZnmPSIbRjxIMtE5+iRmP3xpuj2idSM8AAAAAN0eZD+g17893ot5PAAAAAB2xW0/rPODPfEJ3jsAAAAAaWtwP2ocZD2jZ6k7AAAAADc0aD8K5po9+uCNPAAAAAD0Ilw/FAa9PaHEQz0AAAAAGq5rP9zOjD15Ai48AAAAALS9Vj/N5L89+GlvPYrEEzyu6mo/0xSCPXjSZzx7t5k78GRwPyl4YD00xsk7AAAAADN7UT91cas9TtOKPWCG9zwj7GE/QGxrPehlDj1u18484ipTPyuUlD3leGA9trBDPZ9qbz9OxxI9UluGPP+DTTzG3WA/dXUwPePYLz1M1RE9Q+tQP4ojkT1653s9Ph1TPVJPbz8WQ/A8Eh6cPIG0iTwCnjw/x5AOPiCkvD2PlAI9Tq5AP63pAD5AaOc9zI8KPE6uQD+t6QA+QGjnPcyPCjxlzjM/vQgCPuoe3j38uH49HxYBPwTqhz5+lE4+AfjpPL0t+z4tI44+mZkxPl4Sbz3g2gQ/H0eDPi8pVz5M0W084NoEPx9Hgz4vKVc+TNFtPF3q0D7uI6U+lrZ6PoFmyTzNcdA+OQyqPlRGTz5g9m49Lsi8PlKnrz6FrIg+iD+uPC7IvD5Sp68+hayIPog/rjy3cNM+5/SgPucahj6n7y88t3DTPuf0oD7nGoY+p+8vPDY+uj4sraw+z0SVPsrz8zs2Pro+LK2sPs9ElT7K8/M7Nj66PiytrD7PRJU+yvPzOzY+uj4sraw+z0SVPsrz8zsOWtg+crSjPixecT6dJrQ8rXXePhyEpD5a6UI+ZoxcPboFBT+BXog+w5hGPoaapDx4osA+IE2xPlj3gj4JkbE8eKLAPiBNsT5Y94I+CZGxPLagyT7JlrI+BJRNPgj0Zz22oMk+yZayPgSUTT4I9Gc9K3y4PmCqsz7u+4k+ididPCqdvD6/Tbs+jRtXPn86ZD3uhLM+9VWqPgbQnz5LRZU77oSzPvVVqj4G0J8+S0WVO6D60D5DFME+OuJbPgAAAABZgOM+80eOPraUez6/agM9guXSPmbJyT4xokY+AAAAAILl0j5myck+MaJGPgAAAAA3rwg/11HIPuc+mT0AAAAAQmf1PuTU3T5mD7M9AAAAAOEjDj9k18A+YoOLPQAAAADhIw4/ZNfAPmKDiz0AAAAA36ZVP33aJj7zgiI7AAAAACoUSj+QWFQ+m7JVOwAAAADr0Fc/k9wfPtu/XzoAAAAA69BXP5PcHz7bv186AAAAALVtcz+1JEk9AAAAAAAAAAA54G8/Of6APQAAAAAAAAAAfz10PxIoPD0AAAAAAAAAAH89dD8SKDw9AAAAAAAAAACmDXs/TkuePAAAAAAAAAAAkkZ9P3RbLjwAAAAAAAAAAI9YeD8T7vQ8AAAAAAAAAACPWHg/E+70PAAAAAAAAAAA4QJYP3/0Hz4AAAAAAAAAABeZQT+cpmA+NKjHPAAAAADf2Es/jXVKPtzexDsAAAAA39hLP411Sj7c3sQ7AAAAADeJXD8m2w0+AAAAAAAAAAAx908/PSNAPgAAAAAAAAAAb3U+P2wrLz6gZTU90JUmPRSHDD9Jy7M+O5rMPQAAAABNWT8/mjb3Pa45mT2Ximk9zdw0P/0xQT65EZM9y0cHPa4nNz8xnd89Q2jYPRq9jj2nvAo/bx2qPoXSAD4AAAAA+isnPwSTaz6KcN495UwIPPorJz8Ek2s+inDePeVMCDzmPzc/GMT+PU0AvD1sPIs95j83PxjE/j1NALw9bDyLPelJ+D4YCJA+/FtvPgAAAADpSfg+GAiQPvxbbz4AAAAAa2EmP/q2YT6xp/899PCdO5rTPz9cS+w9LqSRPaRzgz1o/eE+94qWPqN3hz4AAAAA+C0wPzFYRj5oAeE91vMGPMiGSD9iCb09joutPalpIj28wuA+gwyYPsIwhz4AAAAAbUw/P4tPOD6L/ZQ9AAAAAKBeBD/tAIU+qYNkPgAAAADoZG8/XrFdPaAAMDwAAAAAHAtHP9rGMj7kMkQ9AAAAAC6+GT/h6mM+axw1PgAAAAA7oXY/TuwVPQAAAAAAAAAAZGtxP8FJaT0AAAAAAAAAAH6QUT+zujg+SquBOgAAAACecW4/FHOMPQAAAAAAAAAAbApEP/Fwbz6wv8o5AAAAAEMDOj9fjl4+NZw6PY/YKzzxuRo/A3+9PupXojwE6Lk7Da8WP7FCkD7rOcE9y4UQPZj41j6fA4g+kWNhPgRIwT3jaiU/h2QSPnTIDz7yTpA9KEwnP64RiT7b6449NWETPAGBKj+M5EA+T6j8PV8atjx1OCs/vgksPpSW1D2UJHM9De4tP0f89T0/q+U9D+i0PYybIj9j5XU+VtHHPQYe3jz94PE+cxiIPiRPCz7/vQA+8W7mPoVtiD6GK0M+HTe+PZDv8T6qH4I+DSBBPgSDrT3BTsw+u2itPlGEGT5uGeY9dG3EPtAkuT40ABo+jLbVPdxs2z56xKU+ykQNPhSx4D3cbNs+esSlPspEDT4UseA96Yy/Prjlqz7EQHM+7mdXPT08wD5NNrM+xGtePq+8aj09PMA+TTazPsRrXj6vvGo9w8TbPnhtsD4g4VA+TNO1PI4EuT4lxao+SV6TPleAjTx56M8+Yy/MPkrQRz4AAAAADocKP/CBxz7Zv409AAAAAECN/D5Aed0+/eWXPQAAAAC34fE+Hb6RPpp/BD6Fgeg9gfEXPzqfLj4LqPo9gI3oPV7K/j7nsXk+YokkPvZfyD0THxw/TkckPkHtEj5JnrA9hfw8P7KQ+j2hlqU9HulvPSIvOz+668s9IMOxPRvYqD16Ias+qHefPveiiz6aD6c96lEdPyylFz7gSPs9et3qPV2oIj9soEg+uUcDPqDZJT0Eeq4+OYSkPjfAVj5PQwM+lW2/PpdglD6OQVQ+GSIEPl7zuj7VUXU+BdFkPm32Lz6HUyU/raCFPopdcT2TZAw9fVUwPz92iT4Yvfo8tF5GPB1d3T5tr8o+ciIjPspHTDzDjsE+OZW+PlTqTD6yNks9b47tPnHcrT5MAys+nTfxPE3HHj9uCHA+P9LwPQOK4zyz77w+SU2oPgXDmj4AAAAALz8BP3L4gj5eEnU+AAAAABpM+j6xF6Q+aThDPgAAAACqGBI/y9HMPgXO7zwAAAAAmD0qP5QjmD7cCRs9AAAAAKGNOD/xPI4+d82nOgAAAACrAkE/sFRZPlONCD0Pn/o57YpRPyJyJT7gXXY8LIqfOyBbKj9zvmQ+68mePV3ACT1r6FM/sDoQPpCOAD0AAAAA4eVJP7Q+Jz4gp0Q9AAAAAApnRT+ILxw+oWicPQAAAAA4Ylg/dTUEPlS5gDwKqCI82HIcP4BimD5E37o9AAAAACiCVz+Qfs89qdsLPX0Jujwoglc/kH7PPanbCz19Cbo8iNJHPwMlBD6zbLA9t1CLO4jSRz8DJQQ+s2ywPbdQizuY2BM/Q5RvPl8JQT4AAAAAmNgTP0OUbz5fCUE+AAAAANQ2TT9YVPk9/RJdPSeuuTyfYho/ed2PPiN17T0AAAAAthXmPqcmsz5Dh00+AAAAAONJRT/OMd097x3bPUgJazzu3+k+bK+hPkzhaD4AAAAA94XEPucnwD5ApHY+AAAAAP8TFT+1vIY+ut0EPhjHyjwo7dw+BfqvPqgxZj4AAAAApq/HPliqpj6wCpE+BFKbOpgSKT804CA+4jHRPfl4pD24eck+ZnexPj6URz4SE4U9/vQ0P6TKLj6qMu494wHJOxXH7D6uTKw+ThwQPrXwdj2ZkkM/tCs1PijtFz0gdbQ8mZJDP7QrNT4o7Rc9IHW0PCQiDz9Tx28+HbBTPgAAAAAkIg8/U8dvPh2wUz4AAAAAAj8TPyrjqT6Rxro99C3tOjTuST97UCY+7qzgPLcIrzwK2jw/dvRhPhZL3TwVoG88ueskP9qEmT6mHWU9AAAAABOZDT9J5rQ+R56/PQAAAACxJhA/dQnCPqbDZD1TWgg7Var4PmC7zj4yaeI9AAAAAOFOBj8nVdA+XzSMPQAAAACWMxk/2Y2aPvMrzD0AAAAA00YQPxdufz6cdj8+AAAAABneBz+rqIU+QTZVPgAAAABpFPA+EoXgPhaavT0AAAAANKjwPnIB0T5mWfk9AAAAAKN2Cz9ikYs+sgI7PgAAAADNV0I/kahLPujgKz0AAAAAZrg9P+baUD4PDmE9AAAAAHE5Ez/RVJE+nXAQPgAAAAAAAIA/AAAAAAAAAAAAAAAAQdp+P3zfkjsAAAAAAAAAAB1Pez9gHJY8AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAA3p16P0BErDwAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAABSEXs/w9WdPAAAAAAAAAAA8XInP1n2hT4Rj6w9AAAAAOqpcz89/UE9cgRZOgAAAADqqXM/Pf1BPXIEWToAAAAADzB+P5r45zsAAAAAAAAAAA8wfj+a+Oc7AAAAAAAAAACmlmQ/6vRmPbmgTz0AAAAAAACAPwAAAAAAAAAAAAAAAK7LXz82WqU9sJA4PQAAAAAAAIA/AAAAAAAAAAAAAAAA/YBdP0sV9D1zFn88AAAAAAAAgD8AAAAAAAAAAAAAAAAcZVw/xToJPnEZpjsAAAAAAACAPwAAAAAAAAAAAAAAAG7nXz9IpPI9FATiOwAAAAAAAIA/AAAAAAAAAAAAAAAA0FVgP0XzjD2AvGA9AAAAANBVYD9F84w9gLxgPQAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAIRudz8dhgE9xDPyOgAAAAAAAIA/AAAAAAAAAAAAAAAAXEYQP6W6cj7pK0w+AAAAAOBAAj/kYrM+tDYQPgAAAAAROmI/fC/uPQAAAAAAAAAABN9/P+nvAzoAAAAAAAAAAHoMXT8Yzgs+AAAAAAAAAAB+hyg/BPGuPgAAAAAAAAAAF3RcP6IvDj4AAAAAAAAAAEnLVz/d0iA+AAAAAAAAAAB39So/ExWqPgAAAAAAAAAAc3QlPxsXtT4AAAAAAAAAABPuXz+1RwA+AAAAAAAAAAAT7l8/tUcAPgAAAAAAAAAApRZeP2ylBz4AAAAAAAAAAKUWXj9spQc+AAAAAAAAAAA3jlc/JMchPgAAAAAAAAAAN45XPyTHIT4AAAAAAAAAAAAAgD8AAAAAAAAAAAAAAABHZn8/ebgZOwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAADMXcz/LjE49AAAAAAAAAABDtXA/2Kt0PQAAAAAAAAAAuEhvP0G6hT0AAAAAAAAAALPbYz9kIuE9AAAAAAAAAACedV4/iCkGPgAAAAAAAAAALudfP0pjAD4AAAAAAAAAAAAAgD8AAAAAAAAAAAAAAACp3HM/dzVCPQAAAAAAAAAAlllvP1IzhT0AAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAzkXc/1+wGPQAAAAAAAAAAH/t9P2M4ATwAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAB8KX4/8EHrOwAAAAAAAAAA7Xl/P1ATBjsAAAAAAAAAAA/RVj/DuyQ+AAAAAAAAAAAhnFA/fI89PgAAAAAAAAAAXdlOP4iaRD4AAAAAAAAAADvZGD+ITc4+AAAAAAAAAABl3hY/NUPSPgAAAAAAAAAA7poWPyPK0j4AAAAAAAAAACzHAT+qcfw+AAAAAAAAAACUagc/1yrxPgAAAAAAAAAACY0GP+3l8j4AAAAAAAAAAGeTXD9isg0+AAAAAAAAAABSS1w/udIOPgAAAAAAAAAAa9VbP1aqED4AAAAAAAAAAGI9Zz/xFMY9AAAAAAAAAACim2g/9CK7PQAAAAAAAAAAPeJnPx3uwD0AAAAAAAAAAO2ybj+6wUs90h6SPAAAAAD08HA/xFYgPdozoTwAAAAA9PBwP8RWID3aM6E8AAAAALSScD+Ozx09awqyPAAAAAC0knA/js8dPWsKsjwAAAAALq5uPx5JiT2puiI6AAAAABsfbj8oB489AAAAAAAAAAAbH24/KAePPQAAAAAAAAAAiGduP7vDjD0AAAAAAAAAAIhnbj+7w4w9AAAAAAAAAAA9g2g/HOa7PQAAAAAAAAAAPYNoPxzmuz0AAAAAAAAAAIiwaD/He7o9AAAAAAAAAACIsGg/x3u6PQAAAAAAAAAAkh9oP3IDvz0AAAAAAAAAAJIfaD9yA789AAAAAAAAAAA/01M/BbMwPgAAAAAAAAAAPXBVPw0/Kj4AAAAAAAAAADX2VD8m/Sk+WIIKOwAAAADwJwQ/s4X3PuKvqTkAAAAA8CcEP7OF9z7ir6k5AAAAAKFQAT/+Wfk+XJgAPAAAAAChUAE//ln5PlyYADwAAAAAx2ICP3E6+z4AAAAAAAAAAMdiAj9xOvs+AAAAAAAAAAB/O14/BRIHPgAAAAAAAAAAdDlcPzEaDz4AAAAAAAAAAJLQWj+7vRQ+AAAAAAAAAACtJXA/L6V9PQAAAAAAAAAAfJZuPyNMiz0AAAAAAAAAAC33bz+XRoA9AAAAAAAAAAAV9m0/WE+QPQAAAAAAAAAAeW9pPzaEtD0AAAAAAAAAAHlvaT82hLQ9AAAAAAAAAAAg3lc/gIcgPgAAAAAAAAAAgbIHP1ty7j71KIo7AAAAAIGyBz9bcu4+9SiKOwAAAAAERks/8udSPgAAAAAAAAAABEZLP/LnUj4AAAAAAAAAAO84WT9FHBs+AAAAAAAAAADvOFk/RRwbPgAAAAAAAAAA7zhZP0UcGz4AAAAAAAAAAHdoNz8SL5E+AAAAAAAAAADeTlY/iMQmPgAAAAAAAAAA3k5WP4jEJj4AAAAAAAAAAMWKez9Lp448AAAAAAAAAAAgc2s//WakPQAAAAAAAAAAt39yP4gEWD0AAAAAAAAAAGKrXz97UgE+AAAAAAAAAADQbGM/hJnkPQAAAAAAAAAAHLc2P8mRkj4AAAAAAAAAAJBObz+Fi4U9AAAAAAAAAABgkEM/g75xPgAAAAAAAAAAYJBDP4O+cT4AAAAAAAAAANp0Bj9LFvM+AAAAAAAAAADadAY/SxbzPgAAAAAAAAAAcRcaPx/Ryz4AAAAAAAAAAO0EDT8o9uU+AAAAAAAAAADCyUU/+NhoPgAAAAAAAAAArb84P6eAjj4AAAAAAAAAAK2/OD+ngI4+AAAAAAAAAAC8yHs/j+iGPAAAAAAAAAAAMfh/P73l+TgAAAAAAAAAALojeT+8iNs8AAAAAAAAAAC6I3k/vIjbPAAAAAAAAAAAvNB9P+rQCzwAAAAAAAAAALzQfT/q0As8AAAAAAAAAABJ93Y/dIsQPQAAAAAAAAAAHF17P4BclDwAAAAAAAAAACMKbD/orp89AAAAAAAAAADO9hE/YhLcPgAAAAAAAAAAl3Q+P9EWgz4AAAAAAAAAAD+iVT8Fdyk+AAAAAAAAAAD1eWw/XTCcPQAAAAAAAAAAz/9yPxIDUD0AAAAAAAAAAI5lfD+enGY8AAAAAAAAAACAmXI//2dWPQAAAAAAAAAAHcd6P1YcpzwAAAAAAAAAAEAEfz9NwHs7AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAD3bXD9wQww+gzKfOQAAAACC4Go/6fuoPQAAAAAAAAAAfDNkPyRk3j0AAAAAAAAAAHwzZD8kZN49AAAAAAAAAADzFVQ/M6gvPgAAAAAAAAAA7b9EP0sAbT4AAAAAAAAAAO2/RD9LAG0+AAAAAAAAAAAVaQg/1i3vPgAAAAAAAAAAFWkIP9Yt7z4AAAAAAAAAAMnaUz/dlDA+AAAAAAAAAADJ2lM/3ZQwPgAAAAAAAAAAydpTP92UMD4AAAAAAAAAAGIDXT948gs+AAAAAAAAAADWVW8/UVGFPQAAAAAAAAAAp2FdP2V5Cj4AAAAAAAAAAJbHND/TcJY+AAAAAAAAAACMeDs/6Q6JPgAAAAAAAAAAjHg7P+kOiT4AAAAAAAAAAB0hcT837m09AAAAAAAAAADwbl0/QEQKPgAAAAAAAAAAJX95P2sb0DwAAAAAAAAAAD+xaT8DdrI9AAAAAAAAAACgJlA/gGU/PgAAAAAAAAAAoCZQP4BlPz4AAAAAAAAAAAH+MD/8A54+AAAAAAAAAACft1s/hyERPgAAAAAAAAAAf+9UPwdCLD4AAAAAAAAAAPtMPj8JZoM+AAAAAAAAAAAOXmg/kw+9PQAAAAAAAAAAMe1vP3iWgD0AAAAAAAAAABa9aD9QF7o9AAAAAAAAAADXSXA/kWJ7PQAAAAAAAAAAFfJyP6reUD0AAAAAAAAAAFV8dz+uOgg9AAAAAAAAAAA0JXY/wqwdPQAAAAAAAAAANCV2P8KsHT0AAAAAAAAAALvFDz+KdOA+AAAAAAAAAABPHjY/YcOTPgAAAAAAAAAATx42P2HDkz4AAAAAAAAAAEPuAz96I/g+AAAAAAAAAABD7gM/eiP4PgAAAAAAAAAAAC9DPwFEcz4AAAAAAAAAANTRDT9ZXOQ+AAAAAAAAAAA+kXk/LdjNPAAAAAAAAAAAdNl9PwGjCTwAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAACk8Xo/dMuhPAAAAAAAAAAApPF6P3TLoTwAAAAAAAAAAOepfj+TDKs7AAAAAAAAAADnqX4/kwyrOwAAAAAAAAAAWbl7P9zUiDwAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAHkl4PzXc9jwAAAAAAAAAAFiudz9/GgU9AAAAAAAAAADm0RY/NFzSPgAAAAAAAAAAiXDnPvMP1j4J/wQ+AAAAAKmyCD/ws3g+cIFkPgAAAADLA+4+6lrMPptCCz4AAAAAPgq5Pl1ioD6FoZY++x3/PNem1D7xFq8+LaF2PkSe8TqIuc8+iHazPuCfeT4AAAAASeX0PrWWtz4CCCc+AAAAAD7s3z6j2JE+IDuOPgAAAAAELhw/381qPu3IIz5QJjE6nXgPP4tRWj6Pqhg+50KePdblIj+CVDg+riL2PZ8Fgj2toig/nDo7PrA6Ij4AAAAAsXkJP+ouqz6/e+Y9QOyDPKlBDD/vjIg+Ex0NPqoJQz2xhzk/QNuEPgs5YzxLlPs6swQ4PwmFdT5JPsI8IgORPEiH7T4HqXY+Iw0hPkY7DT67/6A+ah6gPgDrXz632B0+uTy0PgVajz7KdEU+tl0zPrnDrz5jm6k+OgxTPhhr9D1UMRQ/hUhiPukpBz5+kIs9LrMhP3ZtRz5syAk+m/UfPdt+Fj/pmyI+H64HPhp19z25XSY/2+AAPnKuAD6m88k9NbCrPv9UlD7715A+PYu8Pfw64D6JdJE+e448Pv8kwD0Ts9U+U4WLPjq7dz7vp4s9WcvSPvJmnD4S0lA+sZKhPSy56j7IZYI+jZ1lPhhJgD0sueo+yGWCPo2dZT4YSYA9BXLoPsDGjj6L70M+2D2bPQVy6D7Axo4+i+9DPtg9mz3c+fw+1XFtPla1XT5plGs9n1v5PlUShj6tATQ+0USaPWuFDz9G90g+1zhAPtnoYj3S8A0/A0xmPh7VFz4qN5Q9XOIyP4jvET4AbO09FEQvPaE0NT/V4Ao+ipnAPZz/fz0pQ2E/H9GIPdEgFz23FIY8XodmP6fsUD1nlRA9ZCBYPLlXcD8atQs9l57dPAAAAAA+FHI/KLoPPTN5VTzZHM074BZ+P0iQ9DsAAAAAAAAAAEdCfT8Mbi88AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAbpZ/PxMj0zoAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/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/AACAP7Fmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PbFmjr3nEEs9hjV7PfQMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/9/P/QMFziFB4o7u+wwumf/fz/0DBc4hQeKO7vsMLpn/38/9AwXOIUHiju77DC6Z/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/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==" + } + ] +} diff --git a/Assets/Cube.012.shmesh b/Assets/Models/racoon.shmodel similarity index 87% rename from Assets/Cube.012.shmesh rename to Assets/Models/racoon.shmodel index bc26a4e833280158bf40aeb73938f1f82caa6edb..9c5d866ac663720e0c1ab614522d71438f2510e5 100644 GIT binary patch delta 31959 zcmZ^~X*`wR_y3OuM3j&*Av7qWBF&qLgMOLz-wfp-E&cWFDh36%9ChZ&Ojy z^lp|0jZ$gW-2Z&O!|(5XgfaYu~*(IIUSxC?)aVe?KH8B*w~0 zNc0mAZG2ZNpU|Tdf*9^H~wpWW_c5QURh78 zswMxQoqzF3+5fU5{Ewlk{~7!ji*NW}L;sooUqc^mCekTGYasm03i^JbH|>1i46k3! zqE0>ubfN@>GX=+Kfxjnx@aPh}BpK9mWhR+DGYXFOPo*vg8_2g&DR4V8ivDiTBC{sN zfV%TF`lVl!kbEAN26JB4(^knlg6_6>SW@_vnEilj)h(e2D!VN6XEx3eum&KvQlmEqc6;WF;5Eq`&*<<12QenjIpL zD)6V*osQ6Z!L=~uLrfnQ#!#u1yJ>N2J>)F5r7zZa(|*;hAT@mJzt|=tf|@Tm4=>t| z(n#q@IxL_Hx`G<1nr1pV)jtO|EWAZax1A-Xe-hzKc^e(knnP|5iGo@GRQ`*%FTYIw zcpQP4w|VrxSolAKo>Qku-r6{LG$e`!jeVW^{1dh?*k1a4RF~-cnsOoB z)?G_?_&pSzzgPt}HV*koM&nt3C=OzMO@?=6u?hkI#;!6Tg&!ih4fn%dr#UMb924fjqfL`|_Sm z@nqip1h5irkDwXf%B&Ae&4;9}2wIY_Lkr$E!p|^HE8PTKy*7;qb{oTFD zW$Qv{NO?fFNGB0v$rPwv@SNUonM;KljvN+{3p8`rL6QDC5p1u#L@i(3v-V6YhHj15 z{|djpq1gBOzT7uN`*PoZq7MuI<*|Lex9`*U<-Q^Je{tW4_`mqSeMtDBY1i$uOr0->YiGk};Sa;_R{rqqtG1Djn8~=7{_%vTo z<9G_pdvDNq;hK2C+V~1M_wY82f0Rblo@T?x@i*zU$?CM`Srg=W)Y6EK6ykm^3lhxl zP?5$+I_PpEM7S`jel3#zd|m|uRU7EHFkNc(r3v`>IaKPBHl1JG0+vaoG^uzG?KC?H zKIWV0z3HBGUR(`4F0`XNO9s69@aqUs~GU#Jd;{nZX#o&li)#+3GF}i4q37> z8oo$h5Un%2Nt8PiV2N>+NPX2K5;7?ocHPmjo*8qWycYT-z>($Wtm;?YAt8sNA#B4F z!Ee`_WXp>LSkv#KfNo%9OuvJm8tP2yTk6S)%}KE4+7$9oHk~MuI9Pvt4@q5LM3SBy zhQ`G8x{(LP zHie|a@2s`X!V_@(pLAjp9V9ZaErz8*sieF6OsyI#hqYZvL@}*fCC4e;2^l^z;8mR?ah0~^bPR6|I6=`}8Bh zh6S*vE}!;N1tOf9m<0vnkI|s-Bgm?ssqjdxjE=c!N&f85hBC8!dSjRa+5h1P{J2p_ zmpxlTq-qbrABCedd{iLmKQtYdcc;=hS0c%Yp~-;zlIfN$v1GeK0yM5qqSAsi;(s;; zZZ6qPb*lS`vX$u5DT-Nep$_q?jSQ9qd{e0sK8bI9=W?K0WN8H2*y3TMHYyJv7oPZOYq>; zWwP8T2F_^f5lP$2WP3s))P@Zq9wv3<*VK3ze%OX6jVUEB6l3A%z+mz|^)yL|I|O5b z7m$r(bBRS_A`Fe+PGrlXh+J$uv}nZ>$Jr?)$u9*|b9RwhuU+I=^kFbN5Kqo}FCgEa zBtgNHEOJ9A5k!KHrGd$pIMRB{mb920fiv&Z$@UA%q<%muoV6$10*KZb4639*FkjkuOspSx+01 z3#Oe#;yO;Odt_&?L`esPJv?XK_caKK}0^EgymlQ$&}kFl=MFZ z`@6zPP)&zu-1u`aHDd>&%Yh`v-;GX>ItOk0eM#Bg2}1fXioxMtZ{pzX zL)}g=@LujrnlA394TM4S0vlp%wTk9nYl0_P)+8%zpE&PtgfA~Akdp&4X{D$E6vmGr z2SWBy7=IBG>NH68yL9S%v>C1qmmv}4B&~P20AC!R3;sB#(ibZ(!?@8M0%B7_|8!h} zn;<+YIONHxy81U?T*@d_NDI8pHPWF@`kei?>I#91l#aN4eO5%$_?h-6I) zXhV(onj3>er#_U>n3tEJ+$2dPC_G6!(=NcYo|~e&h2jqjybMj*7e%d4(#ZDsc%G zN1t$W(IS&F=pS~3j(qp9_HttpZ1v8kMY}C(WHQUZ=Rq3Pnr13s`Gqj}$x*t|rd^=d znFF1JkI@KMV=`rG20YF!rdLj^CZf(12;7uSUppTnO)Cxoe-}-UUOG*Z*ChzyS=9`SyVW0VBFo+&ix$X+tP<_H|PzMsm6WRN#s<6ze6 zU35V4QPM+`q37fZs`!l($>f8;&REmoT6JXDyhG3%Vnh#1-XI}!W8eg<5q)mFLd=^J zq2ufcp(wQK9@#W12CkXvSts7VN!~dp!sMMbRw*twi1fM`ki2tKK;B&;KQ4-wWcfP5 zhV~i~ly(rd#LggOAtiMa4}sjD@kHf9DpAjfgP`?0iE~^5arH_Db$?&d+#5)!XCl}N z6UbZ5J)~g45wOJwa$)3TQr=IP417a6DTk}6-er?hc`78N zCkNz@Cy}ua?FAWb+3?vmm#D`S2!0JZ3ct=ACeqKITAfSFgKvIW#IB^-`m^x~NPBgN zEFQZ|^!#fP1UDs+-4~A3UI{D*tP!dVL3004X6yI0Duwz29(Z`Iw z?|&Ks#;hc#uF2AIk80rLtfl1ps&zC^w-)9!*^(ov_VjaTJ&cqFvU+nYb(>lTYo;5J zvj;X&hYJ_r+_aHo@#lOR`LYp~g!~pTjG<{-mtdvOcfrxPDw?F)4D;@02%KLWp>ba> zgK+n*!vbeb5nY(k0;P|mtX{s&rlDi5K$be?*wz)Iw6NnavpShB?Y>=mWW`C4I~q#^%YRv)a?OM6h61`(X}!SVY%vTU zlujQTofJsLW`ehS5q(+Pk3?%1K(yG|!)nCI&fiq<8!9ZKRXrBuQd%}xnP$+8ZHvfP z$-{7ZVK!Z)wvNQCOov~diL~-_H0j=w0Kc>kQvbCnWXOb6__%kwc&RNTlZs=ZF5Z{6 z2~Lyfpu@1X(4H1vY$5AfV_>C}A~h*&CK~IKz@bipo-4US6vfVNvDQGeiTp!03D+h< zUAL}ioYY+sE0)EY?yM8IZT*L+1SCR4rnBH(a0@x|GzPXUQzw({n#rf-N#Ju@mYDk< zC+)jq;YQsGa-i!Z`8oD5TsS+M=r!&mf`{>Ndq))6GW0NMzK{ZXb2gIB`}0Y=${{%A zl|s66{Yi6A8q|>}BJ3HUOtvVe!bS5eVl#RYxl@`2i*F{7J6AIWL6Vtp_IVCjF!`0B zz%L)Ri@iFf@s&0Gk^{4z<&a-%=Lo)^DTaOT4-+$`+S-7Ag|OmwDlw|IwVE`m6t@3P zAcrhZi0H5raOwF0LhGlBY!yzyo4Au`r12GMP)K zPB;f|x4DrKE@SC@69(^II+MqnB53D53U_x+CVG>6#lPiEa9P5Hq?aC{^T#(pVw)1_ zeHlr~;TD+FphyNQmQwrV^N>3IhG1B25?z_o3NGQ71z(<4(}oExu;APTL6nIwi~6>= zfphaHfx@O58b7H8o^Oy7^=4<$_pNO(w)s!(qS>YN=A-k_zq3KK>V6WfE^CEFA?HL6 z*OO_9S_9Y`452#LcG0=nEfBJ95N$skO4r0tXxE=gpTs!R`&XJE_MSOae=wbXTz?KG zFJD5{W7X)as~m0#S9sFOxw7=}&r>k}N(9~B{!nD3S_5DI*+N@a9~VsvDTT0M2{dJ; zzi5bi6}0#4r^?S-Y6tHx0=K9PD)DOBS@or5utWY3owitHxnp!WbnMti`m*|;_X2@V zK4_%GlD^zrxxPmDPO1Wi%Vmi5*^dIL9R(or$Rp>oeMM4Fk3zuv1OMWSOSTJ&SER$A z-ZavetFtzUmR%@PVKq`ckZXrc^|UG1x5_W zC|{+HOPff$TpFB7sH0VlK_npa7)VXMPEXmGi}vrXgVtzHcUhU!mHtKGlX{P`%A11O zd*UHsW;eBvill;-=OIn0kPiBBmZUurCc>K`muW-TdLpe51BTL1=;mT|Dv{X&YWGX% z1OHMoJ2VF*ZnaXsibg@kh|@4D=`uYN8A)p=RsoT3pz|dsi8h6#fvL?aI(znR`u9W? zd^%M}oz%3cK5YT_hbL&AY#H&IlLM0bTj|=IKr&MNSsP;>QyHO2eWlAO9n@2?=j(3k zqMNiP*9q-qv2F&ZQ9N%do^Mz`6X(-Epg+GAwB#Ld{oRkEHBKY=Rh5M}Y?m}fCO#4Q zi|=)p*2LkVyopkM-hv=h8oH%=}hX@ZrQHNAdh9@%(@6+`0QvAQT(I zHnAbKUD7-@@rhv4Pbu)KTY*vw9>9i|t8r6dD{L98!i3WEpi?;vos=U8K3k3wZktHs z)R#;*<~7V~lEaQ9b1bnNiWNUq@rdd$p0j@eYMIHS;KN=rF&ZJj+z#t5YT)3#E3tQb z8SK9i&!XKY32{j1Z?H0#;_+r{Q0qhlGuVEDv5imR`0|JFJjEVkbTZ-O043}kGy|_% zwtzhL!?2PWXmnf)%Z$h3y5;R`Vafz-QW=WvrEl1?%bKKXs4H4T4Z*%#SY(8bdne<= zYkOexsJR$ELcHWF2!F9SrA_FTtOf4I+rX{40!j*;@HPJlEr#!Dy!{T8t12fujR(Wq zD?L!_w*=+5Do)!?Fh%Jo93DOfPp@AJdbcgGDs~Zu`@V;j7iO}vnY#u17kz|ln|=Al zug+{=nk9A=j={1^N8sM`C7A!90qj?6;u_)1+hF(J7(d0?qI}&eFw%d?{B{T6NXauW zr#O|iIV{CV7K1~SGcGwDM?|WV;r;w=+*Hzlp{WU5 zdUGpcl{TIT)u6Ba9r*DoD?ISU5KGUw!WGRfNIT<=e_}U);Pq92vCA=GSuV0|U@<<*0{@j%rtu+Pwe z*iFiq6=}jkpE{!StBojiNQx|+c!0D+i~01}4p^tL1pR0EgXg(l@Xf*l+hkWj zfDho-SA}q0ct!<3EeXJ&-o5O>uU$+ccp6iPu}3&Q91EweVvf#>(CBX$Jo>Q%O>g(o zSjU0%t@Ch@8=;E{E;I0wzb`Fu0+cNti&J;FV~4C9zi_~p2Ae73B$YX+HP{Pe7S+S2 zGGCneHy*;fSKzz){j9O;Ec4p&OkDp*`QU)kgWx=2B&Y~0j1%v#ynLN zlD-Akl!i0MiKzm`bUWTQZ6wx=%p*h7?NH2|jm#5Lqwt0NY{o#Ge93?qhA+f383Vbc zIDrgX`jC~}8_4!(ufU-Zv-tZ;1^zcG5%P4^@$oB740RuXC)a%j#}Th#uj(|sJj{dJ z+5Uy}TrVgn8^f;ks&oHg_S~^al4X7H<%8Q^g2pi~Zl~P}mqwQ`3lB&1j};EXkR!J2 zk^di3TeXR~hi%5@Gw!_Qz9hcAI~%S^2MFvpjeyjr+xaN_1w8lAK)n9R1YW1Q@?Y75 zP<@6rgvxmHn#lfmlq-j557wLC5Bpd4kg4Mqa>;)NV#d&=Wa%R- zZXNxEiJAwXkFeW=%WW)#dQm&8=uKt=8<)b>VF7rs=QNA93q-Yo$%4qH3vmC+7=E`| zpI=@&55Gm>{n2H(DvmK3jH6!-=Q75s{Pw=lm>=;08Y`Vq z#iIg>;0vR<9W1euV4Pn+oK_Nywr94%1xsI+{rwFa9q5QZG%tc-u|FDLEQh^a!ZAz~ z_mRb@+Mwi`=b*aT54VT4LjT5VwAhic+%PA6haUl)z0sz*A8wP8qb8e@nPmJ-T;w|h z<*Qxr$Z{oI5#3Ag_RnX@{$?oLF$#xgyI`({3ZAQ9N^Tx%VPnr};fI^zZ#r27Dehn4 z+EGWeNtfU&g=eI&G|vnlIQHWjI>F%Q?uwy*_4wGv%kb;MX!JPm%2(vMLPgyG{ISlC ze_Ij{L+ghiarfsM{e`fw^DyKcna2l@+yq083_xDGlWXag3;vF3rAFEweCb+$s5STr zI=_wh^;a@nX_gE>@41+-Et?L9=DY)8+_ZDlTxKEf*EWFb5AflQCNsb}v>Tkh8H3lc zm3()<>rB3AEzes2lk~PWKt+HCxN5KA-6a=U-LxRy@Lq?kJ6;Yif)(V$=XMMh5WVrxT!T;M)0k(4QTNtd-V7xn%yoLz}s#* z<7z#j3Xg2^fwXP*m?|2<2UP5a@reYdzZ$_kdKy76-UQG6n80N-d!hHM9C~qm-nmG; zI&K_+hpvy~-i>-#Ikpib0|xW0XO*#hpgNkC4dWZDW~0a2Igo$w3iDK+gwNYlaK)*5 zc0tP@qc-KzX|LsZnz1oj32lBum_f2gXtfeW^-tM{><{eE#)&AiSq=kie!!WF`bcgL z=jKObcyRp$%+C7@_dE8G{B6Ogk$)5vibCL%!8)wZ$%R$&6Y==!ZaAH$z=JwmT18rmn)+XOR&4u8Ixf7nxAf#R;#h?1G5lwQy>-8+z@$3!kq9 zVRl?H>k%(ms}g1TKxjPENBD85?jpM{1?1nEzV7=2mNn~Y+=+L_Ql&C?>D@L z)0Ou@J4Kv`q)DUv!Bu!=-9xb07$UMAkjtjMbi#9mQYdQK2k)+0qFi|@e zSK=Z=VGk@5r(dv6iCZkW4MvA1;pRV6`S6Q#VB?h07`}B1|8SrTXtx%oB{=cX5BVQCREls^Mx-q!?spgeB9#0M;sUhbJo3sow1?(^Jp9Bp75UN3g`0+ ztAgP6x&C-sCV(&7@Jdkgpcd?eZGL>G&01y|UBd3Dc=MCD-C)##kFZ&KGY>J3rO{va z!-wq^?6)Lxhku50kJ&yvH(CGzSGr+s{bqi};5!XGv;pLw-XPg7%f&+9b2i+?m#_IW z8JOP__+lKyGvAl8VQe2+kvsvSR<7a-er>GkZy>LJzmEv#zAOXt)2n%;(QU@PGHCSz zMcAj~&n0}$u{|J;@0^bU6|)LRQCPu+%bv6QR%x{I=XCgcJ%Hc7nZat72J(*bIP1o( zr$O_FFYo#LhCMh?NG+l*A?HT`7glDmiCTf&aP=?ifO}`4cJpHX>De##O|Fs_mpBMv zz{eo&8yCaQr3dniS#hElgQ_4jXg)6pQQ+ikFa4uF9d5IY-0JH}7U{l%KfDIa=FJV5 zXzR!e0@e72Q$OhEtCvaZ@ojvEi8iZ}oy)7WLfNKxX?zegi?_;*~{y|ZPuWhd&f8UIxJa_|tJSHFPCTQ@2?6c(4P)B?+ z-HFFcIL5{)Nn*gj{`@}MC3rmB8-Hxo;30YWP$+GUuSZ$&vb~!j;_xU;7&(TUy zbq44-OqXvveg`5gjWA@rE_d1@gD=+%!JZqYe3Y<40X-jggNvsg?|(o71NW+8v)OBA z^>7+yJ^BDIT6KBD<0+VZ`3S7Zm*)HD>0^e1B3?KZ&bI3M;HRJW;cUM@EHZpCzD!CY z-+Y=`fV(AH%M8Qlx-m?r#}5T}D14@#%%vd+qsQ!LFQ>?H|FvU~Y4u0p#3R+#W|x9- zi%Btj|6&YEkLKf_xIz43uao#XzifD!tcRnDJo&rOsjw6! za9r|A9@o`FMy|ULZgV&C<}=}<6K7HZ7i{76)(JGW|4z`8+`>!LX0z11iSSS1Iv)J^ z0z1_;o;sAT=D)LVF~`JXGou1=-QhgNMzU z!2(sJFg|z!zjW*h_&aK$o2wc>T&<1QNA$<2*$^7NOal+aQr( z!1f&Q#}Pp%p)NAhI-)KZU#%&I7it+y+tUZeu2|0xO;X`<7hgfQpD~`EYfqjjN>a%L zAuq8R&EpmnLD#+U_};M;{??eVf_wga?R6cFDvw~(Zv!0qNE&tb9AY{hj{Km`C@viF zWF7-x>&-TR_1b7mzRs~0vl;4u59OHzis{b>J{smqmC9oI=KVZsEoxc?(SIgj~uU_ zbVDF#ug4GVx(&|j4TU&LG!}R5=w?DCRcv#c$lKR80Q;te|9BKLVbSk78lTpsfMT3TG| z@N@X;C>9%j4nZG_XUu2oM6NkmlOH<#1gf`S z+H?)al#N|XN}TIUC@b-nJI~{YFW=sE&e6y5_pX>!9U=JjoOO5Y=E+;r*k+D ziMkF#cX6_zFzo|uwa8-XJ6v&cjT+zM)&a@tK6LE&;LhDc zdDPyIu<7AAobV=*DyP{JKRF@)Bs+=^nNSZtW5?lUHG~i6Z_q6x!uZb^L#}zX6xv>i z3za)>VO&ojbI$hS@q>nQ%W=KHOARoLDPe==arT}Ik!Nn3z-P$Rz@jugl-oK1haae6 z5}|tBf9hDC^rjl5RgExPVitxyxyD+?d|}If>v7-f*WjNhBQ(72f|gT%v49OZ?55{P z{?Ew{c=f>mHv}#~%XV45SK$ZkkRnS)IBB;h?@k3+1X3MT6IGV@Ilusz(J=Xjg& z2-Az8t)Pv<-x2QxKJi5&ncG6HJaHU<^Xw|vhv=Z$%43jY?arnruH%6PBlxt}opA2D z0ot1PL*pS8Y}tAT{EuRbz~iK3eGK`HGEHH{{w! zl=#w;r=U956kE59!_?@P>`#*_kKeAzPZr;Tbg^?queQQp$$y!{w)gDQRW06bd<7zP zCg9Q0E=YgK@d%|w%x9n$fAHrryfrpKr(gEy6E=|Vlq`m4vEuySf7x9qUOExKq)IUR zwK>Fm!Vc8$)!;_gZo{oHV_8&!~3h{dUnw(Ah| zL6>)GT?XS{V{k>OBR7;(LxIXyGH8x0*V`0&dhhu|yW zAX7}y5$APQFJa+QQ?Bq$gKzTaf*%S-=Il)%p_u2j0}U#_k`vz9Bw*Wiayd-MgSYG!J+8cNZU15)>$tj^PxV}md)Z?8Qq%>V zR+1<$vSNl3o?QNMBo!W(83Xd4wqdLLcY$t}A1}IiOohzp+a9eDPlb5x__Fr;H#qZqj^&wauby|)Bcwa@!liNnxA}VvW_=&c^&^b= zPeC=b(LZGBUVnW5<_D;|xbo3KaZGOV3N*Gn4|RSU*>x{FPQU+R&0QD&#j}LHdR)m_ z16}5Qg;{<0W7r5jOICvpxo*#Ymr3)wOP;toWD>pVWrr8UhR%x(^=12BJyaCinJ>1} zm&JDQ!2j5hD3`|n*zptFX;ic4KXy!F!lH5NIJ2djDt0KM`pJ2C{;4Pbd1V2}YW!e< zA-kBh+X|eXSI2UM`@z!HoloEP85&NTqJpm;vH4_)zdm+D+q{vS{v65MeHWtU8Y>um z_cvQH>pS&xy~olkR`M2MFT0p(!%Q2z_|uATQn=LzRRurUKt&ZHME1I3Sk53m_3s^K zdQ*?TSZ#nG;1E-=wdA3@b+L5bb}&A08suNC!cfgR2>W^fBAzcp!!{vro2W+G-YpRH zWsRDFJW|dQWE+S$o?-Zq|Hw=v*arOzShmM_n`s>J&8vA8gdsx*b?7duH`u1)HPPO9J7 z_Q{hm^2=T7f8L4To;n=Qh2%iDeClxLWaGat%&zdldzXnMjd2^IlD>%h;_mAWKtBjDmE`_s_#~^C6 zFT(jW2yRXR=Qpcy-TonXKEoNkewadEPF*YZgAHytt9CL7`wk>!toZg>V>x+Z!uqn& z&f{$1+qL}F+r_Lezx{NPPRVj)o|&PjGUdHMDmsoW&f0`!@n@-N>_PbWaU&lF((t}} zIE;A_##cO^%fIbZ#F@%N;eS~8UxP~R&9F*uD&DC#;6c9LsJgWu|6pQ9`*Ka#c)I6X z0Q-1yD+VmME?PBm8y_-qChN=QG4*g>X*HhZ^Pw+4Z81a1{$_YIW<2!eOJ9X@s1P>~ zXWC7IOJaTJf7vf)6_-%F$Hvhn+L!NKm*eKGE`0enSJszj&b}ZTzB7#J7;HtE_yM%B zGK6V{Z^gr%N7xx*Fs3|LWbf9VBmF-wz?;Vh@yZD|;QKr+WWvSc`G)!GX#Dyo46M`T z$LAg=^D5o&O}jiFJ?)TqLrUyGE~9we)*{I79)%IVO}VLq98NtnALU%fK)_dVf0zAI z>j%dI`SagdZ2OGsu(Q&e|4vdT#LyERZ^`gcK`jE0eyd^e{7`P4F_o8nX@H(rO8>>e z{~auzc?{+qzX=CFdSYK*_WT*FA2j*WI$-keR%O?A@*S*o~qL3A0E#@iE?EB7u&@jgzz2KXg6Dj|6iODEyYfZ?SXH< zmf-)z>a%3U;k^JG@(06zv#{^rzu9hM3R8bMiVc$s=0BRh!}eLOI3_iLy@=Ogm1cX{ z<0~Otx={iLm(RoHwg;J=-d@&ZvXrT21@VNOpHR49J_;Wuv7SHL{Q30r?M(YJ?!3>o&;A9%JUtt}_lzZ)H*okpOocB>@F%9@J@|$fvIxcEGa%yE^WNBk zPxgNae;xNj*r$b9>Gl`G<=g1DHH&!rXj@o3WeX2IYR>v{s=PD0{2hwZ2ablmthGOn z1phqDp1ldd>)zjmEVj9kb@msR($}hCM2ab@Egi`}FKl9>v@T{BH;3<3KM7i2tnfyg zDrbXtv#a9=^1@~pe#r78tbINXxAs%wi?7(zSXl*Lp5ewX87SkqnVKk>Aji9myGhUT zv3zBVfIEgL;;`3-_^V!ui+BW>M*(;HDb(hgpGKjvcu)85jqmLDr^^h(3jB?CU}~yc|21^9xRF`YuFSjSHM9JoOs~6H86tB#mfujaoPd^&2f*RaQSrpCiy$eb|cs$lu<;pF?B%pYyy4x zDO}fV44w;C$J`xSINY&6KFZufs>c{|Gie)qK1c>VzmGEC9>U!R%}3J@ zlIWptg!=Ck(P>B^8})J^Pc)y038h_-b9(|BODLiDz7+QD#b@^Hm@5tr?1EXhj8Som zG75E5U$FzBkD1>-J9JVx3&tzQqkGCITpXy)U$(R|I%5JB%k2lDmjzau8K9Dk5uedk z!ICPBasIWPkoVRK?>^VV#Q~$a?#R<@{KT<1&t*GkYFeY@dp&e3G3HB?%h@6yBdq9+ zfW^lqW7yEq*!Fi44>QjevKvSA@OXv+NY&58*ORp|HqDW*T(*;~J~$Yw0(Hr%3ybl< zr$3;xGk{NRPNTsC&cNPn184{h!4F00@cO5a+g+5jZdaTnXbe9{WtR(4_;+4y;=}d) z%&1hVwW6AtLO&|`d?S9cIm8xUvgc!r*D(LImotF=_HZ@T*2V~jBroScQ(vo(0CvMKMfEoH$gNAV+`u`o<5qR4g6V#37e!X%X>>*`JUYhn53eCT?tA&Z0=3a z|Go!4X)WP~azKRZH8i=!JV%~cq=;I!hFIX;j~BcfiZf^Vz`OR@e5vv@c&;>=TZB#E zi#*ivpF`q)kr$F229Cw^Wl9iu+nUQdordh@Y5Z*OVBXo^5<49Si$$xutR>S2uN1YD zwDVt?Rr+sG8#s^8d6>e?$IrvtBV}-r+zKIU_uPm(YP2A3SR}K)ITS~Xn8*F@S5r;m zj(WO-u=l5k9*f+DsuuuO%U0A%Rx9J;?aush>LYU6XaRak$zyc#DYBv}7?&4J0js+Q zVaa+aJbK@rS9VoF=U02Qa2SlW^6_9RaKT@7YLK{iD8^Fp!4-9(HBTLkCc)=d$HtT@Bz6noWF$GKYb8mDMmlWGDRmy%PPR#<0>+^8DD}d8mKxGyM8HR=hGQ z;RoTIP4MD-Gz{GlfElA>*;c2&EM&@jtdV~KO9D(Vq(%wNwCus+dmLQZ6NFFPD%gh$ zuh>_+`4}Aj2!tJD(L7iKA9PDo_e-&0c4`xDP8iJ3*xzHW2Q4seXA&HGY=*hHM!2VO zKGTO7a0m~=5t4>{Ti`h+TwXQ~^It~7sp(U(<&{28*tD80H%^8bZR>GXoFVTluVP2_ zjd4&$C~SRVjlFa9@WaJ0HnuYZc5YjTtCYs^vd)ui;_A^j-XR>`mssLR6$8{?yr0E< zNrwc@wP;yCfnRkN%Pk3p=zMuS#JX8xz-lA(X<(beC9%yb)KSu* zh=|s>VUyQj+&JhPb5r~aOVup#g2f`fsKbz@F6#xaP6@%|fYmtR#(lW^&oJIU%><8D zbV7;6HeRsmoalMc4Dh-aLUY2m;~Z0MahtXdubXI(boM1{^NwxY%{@cJX9=gUgMgZNLGjKnK6WCo|=T=yq*2kT*$vY)nKX;Qv3;W>*8N)v0JB=t;G{Fm|bUgLPC zN-?v{8q2M04#J#56S1gl%!3clWMfvAihDuV@qk>h#PYqAmBd(bN!c{$H_r-JG--2{ zo_ad$WD#sT7Q~gs_2ig|7uYJp>0G#Z1cS&WCfGk@B!BTIn*2JI3)5{k@RDw6Pz;HK zSIa{9mBlfj_Gk}0f9=oRmkvPx;X;`HcP{t5qlJS>aFmxY zopt=lhj7tKv|QH7jFz{9!#M|>5i^jxtyjg|o!Xczsm*UppN@B*tcO5TO)kuufjb*~ z*%?ns&f2wc>Ol?eSonaAe*F@Dx6b0$`HX29pM~+(-n>d!%-oer;OQ%0Zu}yKx&5vH zRbA(V(mHmACf?ueh~Bl+MZQXk z{I-J|H+Y#@V=(L$>>j@Y4N`Rk=f=EYbM%+wth z`nl3GTPzbxnOZBxi1CO-e!Hf=4&_s^t)*bA)LeJy`CyMqiF z*bbNPuEZOax9FUp7c8Y=8DG-%lJwgqg$~8ucyduVy*gJM6%V{PS$mnRo+yJmCVAp^ zk5D@3l^oCTb?0{$+_mOA<#D`(JH~YT)edX^%M{cW@vS@V(VsU4V(BAKTr)UcNV4{| zu%WGMctn{Z^B47i>5 zK2V+p?$XAgCmrx&!V^J=hCWY}oylFc8_`tZ8Y6sTF&!H_+{l~Zs$9>_g-bT3iozX6 z;*D{$kS4p50Y*dlv3v7*c{GaJA1Nbh&c(gj66EI}C7!ls9yctLqaAmZ@M+*Y?B{Gq z?rAFWDK>80KdaVS_lpARd%9u#)@h=(n-%#kDL4LYy{bG}wVFtu|ax`!{sC{iOB9i+m8ue$K2kI{n5 z`l|SkqYECGk|MHRtv5})_DrX{zTad%vTOLSo5EzWv;7+ENL_;qMxUe2T9?_Cy=!=5tt4nI zc?-oszUVAyp_!K7*!}T7eDS>wq7*q0uh=id$Hz9%k6kjnvciK0%SDp?L=N9&y5mFf zDY*Dr8U9t=+O^i&pSF*c!{rt3D7{IZK>G`}`ll~XpC`>8$V%WYq1YQYd_GUiu9dOT za|3wIo&Ie8f`_o<+X`H2*iP;goMxvF2Jn~%=~Q9+MOYNK7U!iLAu&^4vAfDExbnvu zQBUDR&~{selcuPX{@t&cVAFE`tg5cIc7+7a*t!_E1lC#Wc**c3$36M5!|hf#sCeJ^ zxfc$5DyR`2^&h|kL%sNw1NW>2Dk>PHH5W$(1Y2!Ar@)u&b>s2t^Q%Rsnz%pK5rdaR z2x5;K@`NEa+~tcdy}CpL$BuTw;;1aaK6zbkaovt5G!CSbyEM?L))BudoEEs6>2v8hAE$^{EX|9?%K30RHk8^+6;Y*Cc4g-}Y7Eu8mxtjStPLSxA?_TAWhku^KnvSq}? zh=wdfonssOHr5zR2-&iQEYtj-`^+ia5zx#Ra?|t5LzEAa~bILS@6?3t?PYJQK z4fvE=bB#REpMrVmO;RTUoGdrTcD2mN+>*NKOi8YNvMc}OFvs-aVM)u?-?~}?x9u`5 zc5#ybP3y{Myx*pdKUdt6;?d1g^xo1VZrMgj?*DsN?p)M8_5OD!%k(c@EWZsbU>cg{ zz#SvP`I=XPseO~umJ=1aSgsXvH%(jN$WEs2{BN4S>G5PYOUXqYEz`{>Ou6!t&!~VF z+%{`yTA>H!E&V>VvxF7BV@io=#Hj`3MJPWXO?%L-x@CP(OG|?&JLO|`<&8Bvaerfb z$J7ox%2*mD2V3eDebBW3E>CtI*^C1wyh&Yr{z}^2Z<9FcdpGm{`znUP4eLx-<~Frb z|KA6vJDSJlAI8d7aY<_p>kCxSQV@)@g*zujL{N!}Ivc=T6HTy>VVCl4{jb%u<;JS+gIkI?7^XpRMEMLmI(b?IH&7X_UH|Ja%%h|zo%=P4F zhudGbwERB#j$t0BMzhJmk3#mXGdovy;QKq<@T0s$^SPaOX|r=U@9DSIyfn$v^1bCl zbF_a2OUlww<^{fuEcs`8TAEC0YuQ)Mm5W?zWWM6tmBZR(ng?e5Zl2?`OpR^Z+44@l zPnia{6TYiR#^&MK8b65XlC~Np&c)sT7s+3oNW#pIfimw z!Y$WU%Xc{sGRbjJD8GF652ZY8!>F&^?rp8B_a0`YEl1za7~{Y=aQ;UZM&Ktk4n%D| zKx$hbb>H~r)MdyLdHdxYYM35P6D~L7`)xzyd;f6m{yB)}4wy<=-o6~0xrWZ>*5MGZ z)_n54J>980fK9)8(25CldF|sAicPP>vyRnfqy9p9b@HNGY&tZM9XB4QQx80d9-N>< zmKHR}*^N6rOr*0d%dmsjCfeQSFqLf)NGBH#rifDK;@s-xov<=;; z+42Z>INXb!Uk39p3p()pDNT6z>()HYX&?_>wuG|6!g;t)8ETR?g0_VQ7`$;yL%!0Y zu=*CdmzJOP=T=Ai@!&eERP0Z4>7-j7E-<}6b$wKmgD)?ot77@=Z_3Sv4Pdvz4&0<>9U8iO3r*VOMU_^5q1H{~=~^Q%K3X@C29$kAo3`8{ z)7N5r=;~C?dthv)u|>|&Ue9>)`!Jd3rdQ`f)t{(Y^Y_qb_agjZiWk?i6s8poUHIy! z5?m~90bT!R3Z+hRetac=7+ZI)8-6q{ZO6rT*q*M#h%t@&@B26;Tf!T|3abtT*>ry9=(ZoR;s#(mj%(cXcVCJg16vBzoe zlfm+8)fKyVvI5KxISDR6oT~s8m`1b^j|I(e~$3@etcjoB6j@)-@TONLJ94A!j!{L)_S?lrVC-SDW2(GlI7B6<3NyrPf9B6Rp zn&X594!C|NlAVg3q(wyvTL)Y{)1L!#3o~lu@pt-i^PUd8+EPpX`EDE^_|#f{!N54K zl5Ze8?N~y!VtaAbjHc}LZGv@u)b5Fl<8XZWj*CPjhZ3H=b_fo;H_0FDu7SZ+74rTRL;pf#$sI z_+U2JHUQ~FE_4QfCmtNO6Zx+=W3@(Xn* z`n0?kv<82CF`9keRwpB^bTp0r+lya?T9o-!0Mow(=-i(Tc>F($)gOj0d$hWy$lHti z`n6Nt|8QsbDe?-ZR~0#{W?g01y%aZ7tJJx_N^^4A;_ApKJHBu+T7`GDXZLI;wWz{v zN_`xyZYJELWcwk?v&L_f6%?n;ZU@QZ%`r9HG{K-{jjpP=+KVZ4NLk96T7k~QIFspY zJ4$@el?D&mqk67sL*EBEQbWr^dRxk0c{ZI+LEBHLdRr3dt&6=XJbH2tHDzTe4sRAb3ML%>L!)0+K8(ZzfXB`2Ym7Hae8sN4OiGMFSWWC%J)kok!$5H z+~R67Wen}bg%j7)=+mJ*$ZjniIMs*4iZ7y=BB7j{G?{Mn=)+5HOrb)pJM*$Rt!d8b za2`6ZFC};E$QM3Wrcd8Gu~V5=ey8t4SF5j?D`=O)UUg{EFXV9ltjf-g zrua2EYW%lA%31qG`7|CwwbES3bfO;RYgwPxF347ElYGdfwkN$G*pYI}KT>C|O`wdg z6V;N+{V6x!6O~lZm_tE%iOeo-Ax`jfJplhiU(IvqN>M$Jq(PMI!t6w>Gh zg@4IW&!=TkM*Vkc=+9T^Mv3<{ziJfI)T!45J2P9#j1mbE%&FJDMG^fD-rQQSa5u>9YAL)sgr8U$f64$L!rSyty3@FOo{H z#y_Xo9_QuMex#r$=~QvL9ecPvqnJrAD7@`QT0ZC_J?du93q0+3?bdLG zT{z;%B-#_uokJ`3rjV-#_PpetKlzSr zFE=B_DK4}f?=I(0H{)A!P;5zxduuf33w3X+TnFY|e$I3_z6oDRJf~dEbvYq zTrNp_OAn{4VadvM;|dx*eUkd+$DI`4d!n+NpGJctS1Z5RixgzMx*#uwxl1RPmZB14 zo>A;EMYCpQ)97hqXzQu>bU%1KIlRuHoUJG1WwamZdedxLwJ3)=B{_2QGw&#MU3pGw z`;t165Btsin-V>kU8}L%Byjc zTFqOjHbvYehnd0ZfF+TFiY6LrZ`?te?Y>XN78pk^N%HOW)unW^UopB~z5r`R-gp&Tjd*S(_cywreI0=y`;8 z#N^}M8*h-g{8MW9*CU!dEFT{)nMZ{mKcRq(LcHL-Jx5e6#dTaBQ+$pq=c_KC1-r{n z&5V%yloe5f-D}bbNJ*4Z zEtqQzJxFHNhdcUiq!#x(^WpvL==jKiT;{`KDjm_8!-K|9jh_c_nD-2FUDt`Lni^2M z8WFr~bbspW+KwM}EkT3$(v6{-B3{(t+~?m8nk44Q*L!b-Y~GU9y_%E zuD+f2;fybJm3wGS_6WG7x}~^tZnA?iPj}_q@4MBv&1E^c`zzC)z4BkTyW&)nqYmtp z@2n{_=LHQ9qMG&eJVnhetr~qyCik4D>cPkRG-<>VH8{;ei|!Sr2T^ax zeVFlH-Dq)#lV;;r!>_rn2A5ccK7+SO3o;@GOQeU~g zRihGV!JvG6s=q~k)72B|x#p7f-@vQsFK9@2`P95Bm&SYN;|@U%JZWzx`4n;DJN4{2 z`eZqNQt2U$^sUU1`@YNjKMHtrXu&)5?w{Ih*^o_p2NKuuIZr!%<>t^Wod#_U=D9Zx zkO#Nt+gFcM!v{UN_lpGjr%Gq;61Rhzy^rLvi&s&*=W@+G7SOrN1KGRJT$#xfB)NUiq!D=M0B*Q&WFeE5}7ukXOt7*9XxDas1l1_pHZvxLAnL2XVZu z526n~D_h*K9w5fnunt0v4XX`ic9kEWw>0(O0>8Cmmx---w9hw{W$Dk&inz;H$YC7x z@hFuI9LVRFd-1ZWe%xzCZLamjj{^$rqqCvGe0P0EUirB@$95=5mF0)kM+=71qPM;I z=oIGITgG5^bDKm#XNK^mf`xgcdt>%m7swUv%XcaB3vzm=#+{B&MqS1Y zVLRuEbSgu>POR-h_}#tu4Za0oU{6=8w z01U#f1;#?qK~zjsNz_bxu!0D^;-bphgUv+QGVGbCr1oGHk?~ZT2vNX;B}6VF7DZ?e z`iNSHdWnA09vm-PCR!wN)*kc_Ezz~jgQZ1Tvez4XJHxJ(F2EWhY1%y(d1?C91A#n+KglZXzXWtvy&r_sR>stzOPQ48pmhXc0~yJc!ddN7ptF;`G0k2l-ai zMtiWS=!^95?C@mZ!4;zAqUs_%ICyZe>~GgZ6ey~tJy=fkQWn1xwbdSMB>F5pTuXQD z!TF+jB3y4gaCi_;sG%rGgeM9Q;tb@<4CB50)m|51tO&gqq6XT7$l8hOi+<1^^cStv zwatS|MVRqH)KPm7v(UqnlP6D|0F|lM8VpFF(UL{ zi5hDUBI_gy5aHhjyqoNA{YSAtH}DSx9>jIWkdLCy+JhK|9gG>NbVwatUr9D?tvJ&4WG!&(EU z5(S75g2$;9KC9?zjZSd*c2f)#VNwsfCsTTdN_SJ8}Oj*ly(!} zM}!gp>Xq=&b z+JmD+co2AixZLnU!)}-uoOYan@!EqUMHqrpit7gtVi=wk24F2b_>%}{1)F0tcyNMf zst6AnPY52263xUz$D^KPE&e0U1cV-*B|JD?3m(HzgBUnYH0b|4dQqa$qP`-`f(NnR zP!Z1jWLtqvFmSkNtSCx*5RbO62$uny!-E)xO)(5>;X!O(MO0EWTYC^6rX_T3^9IZ; zQ3Vk`^Wn1|JXlPGdjfo#jnN*gD8d&Me36OP9`qLB3l_dW&DS1mE2<-EEt;!6D0>*z zCKh4<4ge8)iAsr0-49Jw<_{<+vDFi~_7B z!q;?s-Cm(R*hb_diV!W+9$cb(_|RRXJ&0_zC|%T0X2F9ub#IOE7Cf#$3_=Kgovy*# zA}m5?^Wa)7_(qbU2JZ;bTPONkdk`4}zh2j%aaaDrB7`;%#%aL^NQN4`Cqxg6?`sbt zgWxyn8hjwa8e}#PZun1zwb@ugG{6iY4#47v+Jnd-_|3WoABi$_ZS&wJE%+v~4mJ2# zh#uBv;`*b=6d{D*x9A#tBElkMHV?*Y!8er*HTYDB9@ak79z+JgZ`Jiy+21ZpghdE# z9{fcM-Xs}n@VO8@40)kFhzx??rfcw}2y2kpJeVLd@D~atAw2j>_ptc2_8>9{{#RXt z*&?h#X7k{7EqIa)HTXt|o~$+O-s%EG2*K~rHTX`1MaXO(Ow@vB$xwsuh3H}J2kk** z5d3e}TFyUAjtGko+B~>Z3%;3TsKJjy^sx4m_8>9{K1tW$XA#yQvw3hAuKz9(isll+ zgSong#b2}skwNf#bPawLVGS~y2X|}1w~!1q_)UnOfyLi-0V0Ipf7dnmj|hv9**uu6 z1>aIK)L@ zgZYJ6gWBfv+spY+kq|<&wFn+8pby02g4%<~Aozp&Sg?@PScA;w!2^;(@PWDp3yTUF z@)s66pb&uwA^1Neg9nQUu?V%zgNG!8;M?dLbQGb7wMDfDkwNgr5eZR%#e`Ud+UCK- zl0oo6x(17j(8Jmi+Jnd-_%zAj!IDC(L9M-EHK`IpXtou>gHHNDEH0%zhzx?a=wrds zQezD=n+MI3LGbN#4Lalcqlv{XLU<4%1b<92c(9DnS%li=!K0Ev@a=UCmKC9QOfpyP zL1d6CuKx)MQGjkjEJAJb;Bm7h(-+cmoB@NeLnN zj=Babh|t60irRz7Ao$ag!Go2AScBT;!Bdh!@SSumH0&zt1F_g$dk_af@Mk512dfCN z2DQzDXC#B*gLMs76`_Z<)wBnZ1>^doI4>a@pob8PP}@9sPBI8SMAu+-5qel#LwgVz z1b!BU@L(+=)}Xd|@RDQ@d>36C zFy10GvDim@5E%sjmt^o@Z6Vg6wt4W1WDtB;U4y$C@ZiL5>8V>5UV8HU^w#c2;>7uHcIclXubB} zk0R+A*3GdQ4ge8iNQP*G_8^9#w@K7ndl1=X={*!})E?|3vi78p&EP>~nDJP&3D+OR zCTU_An(?B(+JnfpNH0^gS$nXb$l8-WHiHL|#fyI`iq{^*Ec9?2AE|w$fdfQarTI*> zMSHNn$l8-WHiHL|VaRjQFWQ3`ie7?ffc7A=ZPI%o+NwP`P-Iw}5@0iU5Fv)V5+!I4 zVis(uL1g$|;JqkOdl0kG+bJ4q^>Y1T5aM3`gJ_5L;4qQ3Cw*)N4m)%l0~Dn2a(}k{<~<8_TU(iwI_XS1`i^`j66}Y_8?}V zw^uY4pZ`&au^zNf{J)~#rG^K`iL5=Tu^Bvw3`6We?Lo{!54Z8-wFi;ymtH>MKJEA6 z<9~t_)~1Bm3?9Tm7*YV#9>g&84oLR1_8_u@(km$3uRStbhzvuDgW7|bg-ig9nk}`+{KN!_dP${e0~~WVn}i7oOH0Tp+UcB*SL#ATrFT3Th8xR#ja8a}vf_ zi~ooa_wv<*XSD|ximW}!uo*mv3^S^O+Jl&h-UZ1PX%8a1D7_lO^Y}-C;=FLN$l8<| zo56#~FvJto9>g&8a8JKPdk`6J<-LR#wFj4qtUbxF7rX(35JS8{?LiDf?}}v0vBRAJ9N} zLwm5h$l8-^6+Es#48kX(Owr%kgXy~WSolzT5E*)pMEA7^k=+s96y4Px%n%`aAiAwR zXkgGS5f)){cn}W;JuJjV@E|hu?uoEDJc!Ikp4Af37wy5fB4_C>66I Date: Fri, 4 Nov 2022 17:17:48 +0800 Subject: [PATCH 069/113] Changed premake to copy model compiler exe --- SHADE_Engine/premake5.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/premake5.lua b/SHADE_Engine/premake5.lua index 06fd7009..42f95520 100644 --- a/SHADE_Engine/premake5.lua +++ b/SHADE_Engine/premake5.lua @@ -116,7 +116,8 @@ project "SHADE_Engine" filter "configurations:Debug" postbuildcommands { - --"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Debug\\assimp-vc142-mtd.dll\" \"$(OutDir)\"", + "xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Debug\\assimp-vc142-mtd.dll\" \"$(OutDir)\"", + "xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Debug\\ModelCompiler.exe\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodL.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudioL.dll\" \"$(OutDir)\"" } @@ -124,7 +125,8 @@ project "SHADE_Engine" filter "configurations:Release" postbuildcommands { - --"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"", + "xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"", + "xcopy /r /y /q \"%{IncludeDir.ModelCompiler}\\bin\\Release\\ModelCompiler.exe\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmod.dll\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.fmod}\\lib\\fmodstudio.dll\" \"$(OutDir)\"" } From 6984653b6484f2f658ffbf1917957f9f45ea457d Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Fri, 4 Nov 2022 17:18:40 +0800 Subject: [PATCH 070/113] Renamed and restructured model asset members Added mesh asset vector for model asset to contain --- .../src/Assets/Asset Types/SHModelAsset.h | 25 ++++++++-------- .../Libraries/Loaders/SHModelLoader.cpp | 30 ++++++++++--------- .../Assets/Libraries/Loaders/SHModelLoader.h | 12 ++++++-- .../src/Resource/SHResourceManager.hpp | 12 ++++---- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h index 45b06198..88a0ab9e 100644 --- a/SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/SHModelAsset.h @@ -18,21 +18,11 @@ namespace SHADE { - struct SHMeshDataHeader + struct SHMeshAssetHeader { uint32_t vertexCount; uint32_t indexCount; - uint32_t charCount; - }; - - struct SHMeshData - { std::string name; - std::vector vertexPositions; - std::vector vertexTangents; - std::vector vertexNormals; - std::vector vertexTexCoords; - std::vector indices; }; struct SHModelAssetHeader @@ -40,10 +30,19 @@ namespace SHADE 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; + }; + struct SH_API SHModelAsset : SHAssetData { SHModelAssetHeader header; - std::vector headers; - std::vector meshes; + std::vector subMeshes; }; } \ 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 57d92b57..4a63a715 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -16,38 +16,39 @@ namespace SHADE { - void SHModelLoader::ReadHeader(std::ifstream& file, SHMeshDataHeader& header) noexcept + void SHModelLoader::ReadHeader(std::ifstream& file, SHMeshLoaderHeader& header) noexcept { file.read( reinterpret_cast(&header), - sizeof(SHMeshDataHeader) + sizeof(SHMeshLoaderHeader) ); } - void SHModelLoader::ReadData(std::ifstream& file, SHMeshDataHeader const& header, SHMeshData& data) noexcept + void SHModelLoader::ReadData(std::ifstream& file, SHMeshLoaderHeader const& header, SHMeshData& data) noexcept { - auto const vertexVec3Byte{ sizeof(SHVec3) * header.vertexCount }; - auto const vertexVec2Byte{ sizeof(SHVec2) * header.indexCount }; + auto const vertexVec2Byte{ sizeof(SHVec2) * header.vertexCount }; 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.name.data(), 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; } - void SHModelLoader::LoadSHMesh(AssetPath path, SHModelAsset& model) noexcept - { + void SHModelLoader::LoadSHMesh(AssetPath path, SHModelAsset& model) noexcept + { std::ifstream file{ path.string(), std::ios::in | std::ios::binary }; if (!file.is_open()) { @@ -58,17 +59,18 @@ namespace SHADE file.seekg(0); file.read( - reinterpret_cast(&model.header), + reinterpret_cast(&model.header), sizeof(SHModelAssetHeader) ); - model.headers.resize(model.header.meshCount); - model.meshes.resize(model.header.meshCount); + std::vector headers(model.header.meshCount); + model.subMeshes.resize(model.header.meshCount); for (auto i{ 0 }; i < model.header.meshCount; ++i) { - ReadHeader(file, model.headers[i]); - ReadData(file, model.headers[i], model.meshes[i]); + model.subMeshes[i] = new SHMeshData(); + ReadHeader(file, headers[i]); + ReadData(file, headers[i], *model.subMeshes[i]); } file.close(); } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h index 688e628e..35dc4514 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h @@ -18,8 +18,16 @@ namespace SHADE { class SHModelLoader : public SHAssetLoader { - void ReadHeader(std::ifstream& file, SHMeshDataHeader& header) noexcept; - void ReadData(std::ifstream& file, SHMeshDataHeader const& header, SHMeshData& data) noexcept; + struct SHMeshLoaderHeader + { + uint32_t vertexCount; + uint32_t indexCount; + uint32_t charCount; + }; + + + void ReadHeader(std::ifstream& file, SHMeshLoaderHeader& header) noexcept; + void ReadData(std::ifstream& file, SHMeshLoaderHeader const& header, SHMeshData& data) noexcept; public: void LoadSHMesh(AssetPath path, SHModelAsset& model) noexcept; diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index d078ce48..1fba71b9 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -49,7 +49,7 @@ namespace SHADE throw std::runtime_error("[SHResourceManager] Attempted to load graphics resource without a SHGraphicsSystem installed."); // Load - const SHModelAsset* assetData = SHAssetManager::GetData(assetId); + const SHMeshData* assetData = SHAssetManager::GetData(assetId); if (assetData == nullptr) { SHLog::Warning("[SHResourceManager] Attempted to load an asset with an invalid Asset ID."); @@ -59,11 +59,11 @@ namespace SHADE Handle meshHandle = gfxSystem->AddMesh ( - assetData->vertexPosition.size(), - assetData->vertexPosition.data(), - assetData->texCoords.data(), - assetData->vertexTangent.data(), - assetData->vertexNormal.data(), + assetData->vertexPositions.size(), + assetData->vertexPositions.data(), + assetData->vertexTexCoords.data(), + assetData->vertexTangents.data(), + assetData->vertexNormals.data(), assetData->indices.size(), assetData->indices.data() ); From 1f0d54486d76309aea0e8a9676f201d107839456 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Fri, 4 Nov 2022 17:20:42 +0800 Subject: [PATCH 071/113] Added sub asset data members Changed some control flow in asset manager to accommodate for subassets Read/Write meta files for assets with sub assets --- SHADE_Engine/src/Assets/SHAsset.h | 7 +- SHADE_Engine/src/Assets/SHAssetMacros.h | 7 +- SHADE_Engine/src/Assets/SHAssetManager.cpp | 184 ++++++++++++++---- SHADE_Engine/src/Assets/SHAssetManager.h | 9 +- .../src/Assets/SHAssetMetaHandler.cpp | 73 ++++++- SHADE_Engine/src/Assets/SHAssetMetaHandler.h | 3 + 6 files changed, 234 insertions(+), 49 deletions(-) diff --git a/SHADE_Engine/src/Assets/SHAsset.h b/SHADE_Engine/src/Assets/SHAsset.h index a4e9847c..29f43e0c 100644 --- a/SHADE_Engine/src/Assets/SHAsset.h +++ b/SHADE_Engine/src/Assets/SHAsset.h @@ -21,6 +21,11 @@ namespace SHADE AssetName name; AssetID id; AssetType type; - AssetPath path; + AssetPath path; + bool isSubAsset; + + std::vector subAssets; + + AssetID parent; }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/SHAssetMacros.h b/SHADE_Engine/src/Assets/SHAssetMacros.h index 52e5cb80..6253ee6d 100644 --- a/SHADE_Engine/src/Assets/SHAssetMacros.h +++ b/SHADE_Engine/src/Assets/SHAssetMacros.h @@ -46,10 +46,11 @@ enum class AssetType : AssetTypeMeta SHADER, SHADER_BUILT_IN, TEXTURE, - MESH, + MODEL, SCENE, PREFAB, MATERIAL, + MESH, MAX_COUNT }; constexpr size_t TYPE_COUNT{ static_cast(AssetType::MAX_COUNT) }; @@ -83,7 +84,7 @@ constexpr std::string_view SCENE_EXTENSION {".shade"}; constexpr std::string_view PREFAB_EXTENSION {".shprefab"}; constexpr std::string_view MATERIAL_EXTENSION {".shmat"}; constexpr std::string_view TEXTURE_EXTENSION {".shtex"}; -constexpr std::string_view MESH_EXTENSION {".shmesh"}; +constexpr std::string_view MODEL_EXTENSION {".shmodel"}; constexpr std::string_view EXTENSIONS[] = { AUDIO_EXTENSION, @@ -91,7 +92,7 @@ constexpr std::string_view EXTENSIONS[] = { SHADER_BUILT_IN_EXTENSION, MATERIAL_EXTENSION, TEXTURE_EXTENSION, - MESH_EXTENSION, + MODEL_EXTENSION, SCRIPT_EXTENSION, SCENE_EXTENSION, PREFAB_EXTENSION, diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp index 29e00075..689276b1 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.cpp +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -16,7 +16,7 @@ #include "SHAssetManager.h" #include "SHAssetMetaHandler.h" -#include "Libraries/Loaders/SHMeshLoader.h" +#include "Libraries/Loaders/SHModelLoader.h" #include "Libraries/Loaders/SHTextureLoader.h" #include "Libraries/Loaders/SHShaderSourceLoader.h" #include "Libraries/Loaders/SHTextBasedLoader.h" @@ -178,7 +178,8 @@ namespace SHADE name, id, type, - newPath + newPath, + false }; assetCollection.insert({ @@ -187,7 +188,8 @@ namespace SHADE name, id, type, - newPath + newPath, + false ) }); @@ -324,25 +326,32 @@ namespace SHADE return result; } - AssetID SHAssetManager::CompileAsset(AssetPath const& path) noexcept + void SHAssetManager::CompileAsset(AssetPath const& path) noexcept { - SHAsset newAsset - { - .name = path.stem().string() - }; - + if (!std::filesystem::exists(path)) + { + SHLOG_ERROR("Path provided does not point to a file: {}", path.string()); + return; + } + AssetPath newPath; auto const ext{ path.extension().string() }; if (ext == GLSL_EXTENSION.data()) { - newAsset.path = SHShaderSourceCompiler::LoadAndCompileShader(path).value(); - newAsset.id = GenerateAssetID(AssetType::SHADER_BUILT_IN); - newAsset.type = AssetType::SHADER_BUILT_IN; + newPath = SHShaderSourceCompiler::LoadAndCompileShader(path).value(); } + else if (ext == GLTF_EXTENSION.data() || ext == FBX_EXTENSION.data()) + { + std::string command = MODEL_COMPILER_EXE.data(); + command += " " + path.string(); + std::system(command.c_str()); - assetCollection.insert({ newAsset.id, newAsset }); - SHAssetMetaHandler::WriteMetaData(newAsset); + std::string modelPath = path.string().substr(0, path.string().find_last_of('.')); + modelPath += MODEL_EXTENSION; + newPath = modelPath; + + GenerateNewMeta(newPath); + } - return newAsset.id; } FolderPointer SHAssetManager::GetRootFolder() noexcept @@ -395,23 +404,15 @@ namespace SHADE for (auto const& path : paths) { - SHAsset newAsset - { - .name = path.stem().string() - }; - + AssetPath newPath; auto const ext{ path.extension().string() }; if (ext == GLSL_EXTENSION.data()) { - newAsset.path = SHShaderSourceCompiler::LoadAndCompileShader(path).value(); - newAsset.id = GenerateAssetID(AssetType::SHADER_BUILT_IN); - newAsset.type = AssetType::SHADER_BUILT_IN; + newPath = SHShaderSourceCompiler::LoadAndCompileShader(path).value(); } else if (ext == DDS_EXTENSION.data()) { - newAsset.path = SHTextureCompiler::CompileTextureAsset(path).value(); - newAsset.id = GenerateAssetID(AssetType::TEXTURE); - newAsset.type = AssetType::TEXTURE; + newPath = SHTextureCompiler::CompileTextureAsset(path).value(); } else if (ext == GLTF_EXTENSION.data() || ext == FBX_EXTENSION.data()) { @@ -419,15 +420,12 @@ namespace SHADE command += " " + path.string(); std::system(command.c_str()); - std::string newPath = path.string().substr(0, path.string().find_last_of('.')); - newPath += MESH_EXTENSION; - newAsset.path = newPath; - newAsset.id = GenerateAssetID(AssetType::MESH); - newAsset.type = AssetType::MESH; + std::string modelPath = path.string().substr(0, path.string().find_last_of('.')); + modelPath += MODEL_EXTENSION; + newPath = modelPath; } - assetCollection.insert({ newAsset.id, newAsset }); - SHAssetMetaHandler::WriteMetaData(newAsset); + GenerateNewMeta(newPath); } } @@ -442,10 +440,11 @@ namespace SHADE loaders[static_cast(AssetType::SHADER)] = dynamic_cast(new SHShaderSourceLoader()); loaders[static_cast(AssetType::SHADER_BUILT_IN)] = loaders[static_cast(AssetType::SHADER)]; loaders[static_cast(AssetType::TEXTURE)] = dynamic_cast(new SHTextureLoader()); - loaders[static_cast(AssetType::MESH)] = dynamic_cast(new SHMeshLoader()); + loaders[static_cast(AssetType::MODEL)] = dynamic_cast(new SHModelLoader()); loaders[static_cast(AssetType::SCENE)] = dynamic_cast(new SHTextBasedLoader()); loaders[static_cast(AssetType::PREFAB)] = loaders[static_cast(AssetType::SCENE)]; loaders[static_cast(AssetType::MATERIAL)] = loaders[static_cast(AssetType::SCENE)]; + loaders[static_cast(AssetType::MESH)] = nullptr; } /**************************************************************************** @@ -453,9 +452,9 @@ namespace SHADE ****************************************************************************/ void SHAssetManager::Load() noexcept { - //CompileAll(); BuildAssetCollection(); InitLoaders(); + //CompileAll(); //LoadAllData(); } @@ -473,8 +472,12 @@ namespace SHADE SHAssetData* SHAssetManager::LoadData(SHAsset const& asset) noexcept { - SHAssetData* data = loaders[static_cast(asset.type)]->Load(asset.path); + if (asset.isSubAsset) + { + return LoadSubData(asset); + } + SHAssetData* data = loaders[static_cast(asset.type)]->Load(asset.path); if (data == nullptr) { SHLOG_ERROR("Unable to load asset into memory: {}\n", asset.path.string()); @@ -487,13 +490,120 @@ namespace SHADE return data; } - void SHAssetManager::LoadDataFromScratch(AssetPath path) noexcept + SHAssetData* SHAssetManager::LoadSubData(SHAsset const& asset) noexcept { + auto const& parent = assetCollection[asset.parent]; + auto parentData = loaders[static_cast(parent.type)]->Load(parent.path); + if (parentData == nullptr) + { + SHLOG_ERROR("Unable to load asset into memory: {}\n", parent.path.string()); + } + else + { + assetData.emplace(parent.id, parentData); + if (parent.type == AssetType::MODEL) + { + auto parentModel = reinterpret_cast(parentData); + for (auto i {0}; i < parent.subAssets.size(); ++i) + { + assetData.emplace( + parent.subAssets[i]->id, + parentModel->subMeshes[i] + ); + } + } + + return assetData[asset.id]; + } + + return parentData; + } + + void SHAssetManager::LoadNewData(AssetPath path) noexcept + { + } + + void SHAssetManager::GenerateNewMeta(AssetPath path) noexcept + { + auto const ext = path.extension().string(); + if (ext == SHADER_BUILT_IN_EXTENSION.data()) + { + SHAsset newAsset{ + path.stem().string(), + GenerateAssetID(AssetType::SHADER_BUILT_IN), + AssetType::SHADER_BUILT_IN, + path, + false + }; + + assetCollection.emplace(newAsset.id, newAsset); + SHAssetMetaHandler::WriteMetaData(newAsset); + } + else if (ext == TEXTURE_EXTENSION.data()) + { + SHAsset newAsset{ + path.stem().string(), + GenerateAssetID(AssetType::SHADER_BUILT_IN), + AssetType::SHADER_BUILT_IN, + path, + false + }; + + assetCollection.emplace(newAsset.id, newAsset); + SHAssetMetaHandler::WriteMetaData(newAsset); + } + else if (ext == MODEL_EXTENSION) + { + SHAsset newAsset{ + path.stem().string(), + GenerateAssetID(AssetType::MODEL), + AssetType::MODEL, + path, + false + }; + + assetCollection.emplace(newAsset.id, newAsset); + + SHModelAsset* const data = reinterpret_cast(LoadData(newAsset)); + assetData.emplace(newAsset.id, data); + for(auto const& subMesh : data->subMeshes) + { + SHAsset subAsset{ + .name = subMesh->header.name, + .id = GenerateAssetID(AssetType::MESH), + .type = AssetType::MESH, + .isSubAsset = true, + .parent = newAsset.id + }; + + assetCollection.emplace(subAsset.id, subAsset); + assetCollection[newAsset.id].subAssets.push_back(&assetCollection[subAsset.id]); + + assetData.emplace(subAsset.id, subMesh); + } + + SHAssetMetaHandler::WriteMetaData(assetCollection[newAsset.id]); + } } void SHAssetManager::BuildAssetCollection() noexcept { SHFileSystem::BuildDirectory(ASSET_ROOT.data(), folderRoot, assetCollection); + + for (auto& asset : std::ranges::views::values(assetCollection)) + { + if (!asset.subAssets.empty()) + { + // Add subasset data into map, replace pointer and free heap memory + for (auto i{ 0 }; i < asset.subAssets.size(); ++i) + { + auto const id = asset.subAssets[i]->id; + assetCollection[id] = *asset.subAssets[i]; + delete asset.subAssets[i]; + asset.subAssets[i] = &assetCollection[id]; + } + } + } } } diff --git a/SHADE_Engine/src/Assets/SHAssetManager.h b/SHADE_Engine/src/Assets/SHAssetManager.h index 4daeb536..ba10d84f 100644 --- a/SHADE_Engine/src/Assets/SHAssetManager.h +++ b/SHADE_Engine/src/Assets/SHAssetManager.h @@ -87,7 +87,7 @@ namespace SHADE static std::vector GetAllDataOfType(AssetType type) noexcept; static std::vector GetAllRecordOfType(AssetType type) noexcept; - static AssetID CompileAsset(AssetPath const& path) noexcept; + static void CompileAsset(AssetPath const& path) noexcept; static FolderPointer GetRootFolder() noexcept; @@ -95,8 +95,11 @@ namespace SHADE static void InitLoaders() noexcept; static void LoadAllData() noexcept; - static SHAssetData* LoadData(SHAsset const& asset) noexcept; - static void LoadDataFromScratch(AssetPath path) noexcept; + static SHAssetData* LoadData(SHAsset const& asset) noexcept; + static SHAssetData* LoadSubData(SHAsset const& asset) noexcept; + static void LoadNewData(AssetPath path) noexcept; + static void GenerateNewMeta(AssetPath path) noexcept; + inline static void BuildAssetCollection() noexcept; static bool IsRecognised(char const*) noexcept; diff --git a/SHADE_Engine/src/Assets/SHAssetMetaHandler.cpp b/SHADE_Engine/src/Assets/SHAssetMetaHandler.cpp index 1bfec00d..9ae8cde2 100644 --- a/SHADE_Engine/src/Assets/SHAssetMetaHandler.cpp +++ b/SHADE_Engine/src/Assets/SHAssetMetaHandler.cpp @@ -9,7 +9,6 @@ ******************************************************************************/ #include "SHpch.h" #include "SHAssetMetaHandler.h" -#include #include namespace SHADE @@ -21,11 +20,15 @@ namespace SHADE * \brief Helper function to retrieve field value from meta data file * for processing ****************************************************************************/ - void GetFieldValue(std::ifstream& file, std::string& line) noexcept + bool GetFieldValue(std::ifstream& file, std::string& line) noexcept { line = ""; - std::getline(file, line); - line = line.substr(line.find_last_of(':') + 2, line.length()); + if (std::getline(file, line)) + { + line = line.substr(line.find_last_of(':') + 2, line.length()); + return true; + } + return false; } /**************************************************************************** @@ -66,7 +69,8 @@ namespace SHADE std::ifstream metaFile{ path.string(), std::ios_base::in }; if (!metaFile.is_open()) { - // Error unable to open + SHLOG_ERROR("Unable to open meta file: {}", path.string()); + return {}; } std::string line; @@ -92,6 +96,43 @@ namespace SHADE AssetTypeMeta type; typeStream >> type; meta.type = static_cast(type); + + meta.isSubAsset = false; + + // Burn Line + if (std::getline(metaFile, line)) + { + // Name Line + while(GetFieldValue(metaFile, line)) + { + auto subAsset = new SHAsset(); + + // Get resource name + std::stringstream nameStream{ line }; + AssetName name; + nameStream >> name; + subAsset->name = name; + + // Get resource id + GetFieldValue(metaFile, line); + std::stringstream idStream{ line }; + AssetID id; + idStream >> id; + subAsset->id = id; + + // Get resource type + GetFieldValue(metaFile, line); + std::stringstream typeStream{ line }; + AssetTypeMeta type; + typeStream >> type; + subAsset->type = static_cast(type); + + subAsset->isSubAsset = true; + subAsset->parent = meta.id; + + meta.subAssets.push_back(subAsset); + } + } metaFile.close(); @@ -108,6 +149,12 @@ namespace SHADE ****************************************************************************/ void SHAssetMetaHandler::WriteMetaData(SHAsset const& meta) noexcept { + if (meta.isSubAsset) + { + SHLOG_WARNING("Cannot write subasset meta: {}", meta.name); + return; + } + //TODO: Write into binary eventually std::string path{ meta.path.string() }; path.append(META_EXTENSION); @@ -124,7 +171,23 @@ namespace SHADE metaFile << "ID: " << meta.id << "\n"; metaFile << "Type: " << static_cast(meta.type) << std::endl; + if (!meta.subAssets.empty()) + { + metaFile << "Sub Assets:\n"; + + for (auto const& subAsset : meta.subAssets) + { + WriteSubAssetMeta(metaFile, *subAsset); + } + } + metaFile.close(); } + void SHAssetMetaHandler::WriteSubAssetMeta(std::ofstream& metaFile, SHAsset const& subAsset) noexcept + { + metaFile << "Name: " << subAsset.name << "\n"; + metaFile << "ID: " << subAsset.id << "\n"; + metaFile << "Type: " << static_cast(subAsset.type) << std::endl; + } } diff --git a/SHADE_Engine/src/Assets/SHAssetMetaHandler.h b/SHADE_Engine/src/Assets/SHAssetMetaHandler.h index 88b5329d..ffa965aa 100644 --- a/SHADE_Engine/src/Assets/SHAssetMetaHandler.h +++ b/SHADE_Engine/src/Assets/SHAssetMetaHandler.h @@ -12,6 +12,7 @@ #include "SHAssetMacros.h" #include "SHAsset.h" +#include namespace SHADE { @@ -45,6 +46,8 @@ namespace SHADE * \brief Writes meta data into text file ****************************************************************************/ static void WriteMetaData(SHAsset const&) noexcept; + + static void WriteSubAssetMeta(std::ofstream&, SHAsset const&) noexcept; }; } From 9ef956029edfdba1e1b834e2cae0538b187b9088 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Fri, 4 Nov 2022 17:20:52 +0800 Subject: [PATCH 072/113] Changed name check for hardcoded racoon mesh --- SHADE_Application/src/Scenes/SBTestScene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index 23b57dfc..d0c7496d 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -50,7 +50,7 @@ namespace Sandbox switch (asset.type) { case AssetType::MESH: - if (asset.name == "Cube.012") + if (asset.name == "Raccoon") handles.emplace_back(SHResourceManager::LoadOrGet(asset.id)); break; case AssetType::TEXTURE: From d6804c09f4762976f8d892f8f1d53c120e8cf44b Mon Sep 17 00:00:00 2001 From: Glence Date: Fri, 4 Nov 2022 17:31:53 +0800 Subject: [PATCH 073/113] fixed player throwing added item script player jump is now affected by item weight --- TempScriptsFolder/Item.cs | 18 ++++++++ TempScriptsFolder/PickAndThrow.cs | 61 +++++++++++++-------------- TempScriptsFolder/PlayerController.cs | 48 +++++++++++++++++---- 3 files changed, 87 insertions(+), 40 deletions(-) create mode 100644 TempScriptsFolder/Item.cs diff --git a/TempScriptsFolder/Item.cs b/TempScriptsFolder/Item.cs new file mode 100644 index 00000000..96ec092d --- /dev/null +++ b/TempScriptsFolder/Item.cs @@ -0,0 +1,18 @@ +using SHADE; +using System; +public class Item : Script +{ + public enum ItemCategory + { + LIGHT, + MEDIUM, + HEAVY + } + + public ItemCategory currCategory; + public Item(GameObject gameObj) : base(gameObj) { } + + protected override void awake() + { + } +} \ No newline at end of file diff --git a/TempScriptsFolder/PickAndThrow.cs b/TempScriptsFolder/PickAndThrow.cs index 80fcd61a..ed59d7e8 100644 --- a/TempScriptsFolder/PickAndThrow.cs +++ b/TempScriptsFolder/PickAndThrow.cs @@ -4,9 +4,10 @@ using static PlayerController; public class PickAndThrow : Script { - private PlayerController pc; - public GameObject item; public Vector3 throwForce = new Vector3(200.0f, 300.0f, 200.0f); + public GameObject item; + private PlayerController pc; + private Camera cam; private Transform itemTransform; private RigidBody itemRidibody; private Transform raccoonHoldLocation; @@ -23,40 +24,35 @@ public class PickAndThrow : Script Debug.Log("CHILD EMPTY"); else raccoonHoldLocation.LocalPosition = new Vector3(0.0f, 1.0f, 0.0f); - - itemTransform = item.GetComponent(); - if (itemTransform == null) - Debug.Log("Item transform EMPTY"); - - itemRidibody = item.GetComponent(); - if (itemRidibody == null) - Debug.Log("Item rb EMPTY"); } protected override void update() { - if (!pc.isMoveKeyPress) + if (cam == null) + cam = GetComponentInChildren(); + else if (cam != null) { - if (pc.xAxisMove != 0) - { - lastXDir = pc.xAxisMove; - lastZDir = 0.0f; - } - if (pc.zAxisMove != 0) - { - lastXDir = 0.0f; - lastZDir = pc.zAxisMove; - } + Vector3 camerAixs = cam.GetForward(); + camerAixs.y = 0; + camerAixs.Normalise(); + lastXDir = camerAixs.x; + lastZDir = camerAixs.z; } - else + + if (item.GetScript() != null && itemTransform == null && itemRidibody == null) { - lastXDir = pc.xAxisMove; - lastZDir = pc.zAxisMove; + itemTransform = item.GetComponent(); + if (itemTransform == null) + Debug.Log("Item transform EMPTY"); + + itemRidibody = item.GetComponent(); + if (itemRidibody == null) + Debug.Log("Item rb EMPTY"); } if (pc != null && inRange && !pc.holdItem && Input.GetKey(Input.KeyCode.E)) pc.holdItem = true; - if (pc != null && itemRidibody != null && itemTransform!= null && pc.holdItem) + if (pc != null && itemRidibody != null && itemTransform != null && pc.holdItem) { itemTransform.LocalPosition = raccoonHoldLocation.GlobalPosition; itemRidibody.IsGravityEnabled = false; @@ -66,13 +62,13 @@ public class PickAndThrow : Script if (Input.GetMouseButtonDown(Input.MouseCode.LeftButton)) { pc.holdItem = false; + inRange = false; itemRidibody.IsGravityEnabled = true; itemRidibody.AddForce(new Vector3(throwForce.x * lastXDir, throwForce.y, throwForce.z * lastZDir)); itemRidibody.LinearVelocity += pc.rb.LinearVelocity; - Debug.Log($"x: {itemRidibody.LinearVelocity.x} z: {itemRidibody.LinearVelocity.z}"); } } - else if(!pc.holdItem) + else if(!pc.holdItem && itemRidibody != null) itemRidibody.IsGravityEnabled = true; } protected override void onCollisionEnter(CollisionInfo info) @@ -80,20 +76,21 @@ public class PickAndThrow : Script } protected override void onTriggerEnter(CollisionInfo info) { - Debug.Log("ENTER"); - if (info.GameObject == item && !pc.holdItem) + //Debug.Log("ENTER"); + if (info.GameObject.GetScript() != null && !pc.holdItem) { + item = info.GameObject; inRange = true; } } protected override void onTriggerStay(CollisionInfo info) { - Debug.Log("STAY"); + //Debug.Log("STAY"); } protected override void onTriggerExit(CollisionInfo info) { - Debug.Log("EXIT"); - if (info.GameObject == item && !pc.holdItem) + //Debug.Log("EXIT"); + if (info.GameObject.GetScript() != null && !pc.holdItem) { inRange = false; } diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index 77a4b9d3..bcdf24d1 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -1,7 +1,6 @@ using SHADE; using System; - -//in air controls? +using static Item; public class PlayerController : Script { @@ -19,6 +18,7 @@ public class PlayerController : Script public RigidBody rb; private Transform tranform; private Camera cam; + private PickAndThrow pat; //to be remove public float drag = 2.0f; @@ -68,13 +68,24 @@ public class PlayerController : Script private float gravity = -9.8f; private float groundGravity = -0.5f; + //ItemMultipler================================================================== + public float lightMultiper = 0.75f; + public float mediumMultiper = 0.5f; + public float heavyMultiper = 0.25f; + public PlayerController(GameObject gameObj) : base(gameObj) { } protected override void awake() { - + //default setup isMoveKeyPress = false; holdItem = false; + + //Jump setup + float timeToApex = maxJumpTime / 2; + gravity = (-2 * maxJumpHeight) / MathF.Pow(timeToApex, 2); + initialJumpVel = (2 * maxJumpHeight) / timeToApex; + //rigidbody check rb = GetComponent(); if (rb == null) @@ -94,10 +105,10 @@ public class PlayerController : Script if(tranform == null) Debug.LogError("tranform is NULL!"); - //Jump setup - float timeToApex = maxJumpTime / 2; - gravity = (-2 * maxJumpHeight) / MathF.Pow(timeToApex, 2); - initialJumpVel = (2 * maxJumpHeight) / timeToApex; + //PickAndThrow checl + pat = GetScript(); + if (pat == null) + Debug.LogError("PickAndThrow is NULL!"); //toRemove tranform.LocalPosition = new Vector3(-3.0f, -2.0f, -5.0f); @@ -116,9 +127,11 @@ public class PlayerController : Script tranform.LocalPosition = new Vector3(-3.0f, -2.0f, -5.0f); } + GotCaught(); MoveKey(); + //Debug.Log(currentState.ToString() + " x:" + rb.LinearVelocity.x.ToString() + " y:" + rb.LinearVelocity.y.ToString() + " z:" + rb.LinearVelocity.z.ToString()); } @@ -202,7 +215,7 @@ public class PlayerController : Script { if (rb != null) { - rb.AddForce(new Vector3(moveForce * axisMove.x, 0.0f, moveForce * axisMove.y)); + rb.AddForce(new Vector3(axisMove.x, 0.0f,axisMove.y) * moveForce); if (isMoveKeyPress) { @@ -259,6 +272,16 @@ public class PlayerController : Script currentState = RaccoonStates.JUMP; Vector3 v = rb.LinearVelocity; v.y = initialJumpVel * 0.5f; + if (pat != null && pat.item.GetScript() != null && holdItem) + { + Item item = pat.item.GetScript(); + if (item.currCategory == ItemCategory.LIGHT) + v.y *= lightMultiper; + if (item.currCategory == ItemCategory.MEDIUM) + v.y *= mediumMultiper; + if (item.currCategory == ItemCategory.HEAVY) + v.y *= heavyMultiper; + } rb.LinearVelocity = v; } } @@ -319,6 +342,15 @@ public class PlayerController : Script } } + private void GotCaught() + { + if (currentState == RaccoonStates.CAUGHT && tranform != null) + { + currentState = RaccoonStates.IDILE; + tranform.LocalPosition = new Vector3(-3.0f, -2.0f, -5.0f); + } + } + protected override void onCollisionEnter(CollisionInfo info) { } From be16fbed19c4aeeb6c8a4b67e4c4d32ea35debe2 Mon Sep 17 00:00:00 2001 From: Glence Date: Fri, 4 Nov 2022 17:35:09 +0800 Subject: [PATCH 074/113] added item script in the scene --- SHADE_Application/src/Scenes/SBTestScene.cpp | 1 + TempScriptsFolder/PickAndThrow.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index fa5d7868..7d3ca4ff 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -182,6 +182,7 @@ namespace Sandbox scriptEngine->AddScript(racoon, "PickAndThrow"); scriptEngine->AddScript(racoonCamera, "ThirdPersonCamera"); scriptEngine->AddScript(AI, "AIPrototype"); + scriptEngine->AddScript(item, "Item"); auto raccoonShowcase = SHEntityManager::CreateEntity(); auto& renderableShowcase = *SHComponentManager::GetComponent_s(raccoonShowcase); diff --git a/TempScriptsFolder/PickAndThrow.cs b/TempScriptsFolder/PickAndThrow.cs index ed59d7e8..ea814b36 100644 --- a/TempScriptsFolder/PickAndThrow.cs +++ b/TempScriptsFolder/PickAndThrow.cs @@ -4,7 +4,7 @@ using static PlayerController; public class PickAndThrow : Script { - public Vector3 throwForce = new Vector3(200.0f, 300.0f, 200.0f); + public Vector3 throwForce = new Vector3(100.0f, 200.0f, 100.0f); public GameObject item; private PlayerController pc; private Camera cam; From 1267442210f26861c9fa6c73474f444d9df22c8b Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Fri, 4 Nov 2022 18:27:25 +0800 Subject: [PATCH 075/113] Deserialization flow is now as follows: Create entities and components >> Fetch Assets needed >> Load assets needed >> Initialize entities Some other bug fixes --- Assets/Scenes/M2Scene.shade | 2 +- .../AssetBrowser/SHAssetBrowser.cpp | 17 +- .../MaterialInspector/SHMaterialInspector.cpp | 2 +- .../src/Graphics/MiddleEnd/Batching/SHBatch.h | 1 + .../MiddleEnd/Batching/SHSuperBatch.cpp | 4 + .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 8 + .../MiddleEnd/Textures/SHTextureLibrary.cpp | 13 +- .../src/Resource/SHResourceManager.hpp | 2 +- .../src/Serialization/SHSerialization.cpp | 38 +- .../src/Serialization/SHSerialization.h | 6 +- .../Serialization/SHSerializationHelper.hpp | 380 +++--------------- .../src/Serialization/SHYAMLConverters.h | 317 +++++++++++++++ 12 files changed, 455 insertions(+), 335 deletions(-) create mode 100644 SHADE_Engine/src/Serialization/SHYAMLConverters.h diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade index 39caeeaa..35608ab6 100644 --- a/Assets/Scenes/M2Scene.shade +++ b/Assets/Scenes/M2Scene.shade @@ -32,7 +32,7 @@ Scale: {x: 49.4798889, y: 0.5, z: 17.5} Renderable Component: Mesh: 80365422 - Material: 126974645 + Material: 0 RigidBody Component: Type: Static Mass: 1 diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp index a18cd70f..d875d743 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp @@ -189,12 +189,17 @@ namespace SHADE switch (file.assetMeta->type) { case AssetType::INVALID: break; - case AssetType::SHADER: icon = ICON_FA_FILE_CODE; break; - case AssetType::SHADER_BUILT_IN: icon = ICON_FA_FILE_CODE; break; - case AssetType::TEXTURE: icon = ICON_FA_IMAGES; break; - case AssetType::MESH: icon = ICON_FA_CUBES; break; - case AssetType::SCENE: icon = ICON_MD_IMAGE; break; - case AssetType::PREFAB: icon = ICON_FA_BOX_OPEN; break; + case AssetType::SHADER: break; + case AssetType::SHADER_BUILT_IN: break; + case AssetType::TEXTURE: break; + case AssetType::MESH: break; + case AssetType::SCENE: + if(auto editor = SHSystemManager::GetSystem()) + { + editor->LoadScene(file.assetMeta->id); + } + break; + case AssetType::PREFAB: break; case AssetType::MATERIAL: if (auto matInspector = SHEditorWindowManager::GetEditorWindow()) { diff --git a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp index 3eb8564f..13ecb9fa 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MaterialInspector/SHMaterialInspector.cpp @@ -6,8 +6,8 @@ #include "Assets/SHAssetManager.h" #include "Editor/IconsMaterialDesign.h" -#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Editor/SHEditorWidgets.hpp" +#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Resource/SHResourceManager.h" namespace SHADE diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index de137b28..c9dd4eda 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -88,6 +88,7 @@ namespace SHADE /* Getter Functions */ /*-----------------------------------------------------------------------------*/ Handle GetPipeline() const noexcept { return pipeline; }; + bool IsEmpty() const noexcept { return subBatches.empty(); } private: /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp index df389879..b827652e 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp @@ -68,6 +68,10 @@ namespace SHADE return; batch->Remove(renderable); + + // If batch is empty, remove batch + if (batch->IsEmpty()) + batches.erase(batch); } void SHSuperBatch::Clear() noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 32a03e33..43de46bb 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -270,7 +270,15 @@ namespace SHADE worldRenderer->SetCameraDirector(cameraSystem->CreateDirector()); // Create default materials + std::array defaultTexture = { 255, 255, 255, 255 }; + std::vector mipOffsets{}; + mipOffsets.push_back(0); + auto tex = AddTexture(4, defaultTexture.data(), 1, 1, SHTexture::TextureFormat::eR8G8B8A8Unorm, mipOffsets); + BuildTextures(); + defaultMaterial = AddMaterial(defaultVertShader, defaultFragShader, gBufferSubpass); + defaultMaterial->SetProperty("data.textureIndex", tex->TextureArrayIndex); + // Create debug draw pipeline debugDrawPipeline = createDebugDrawPipeline(debugDrawNode->GetRenderpass(), debugDrawSubpass); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp index 8719458b..3b6448fa 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp @@ -156,14 +156,15 @@ namespace SHADE /* Build Descriptor Set with all the Textures only if there are textures */ if (!texOrder.empty()) { - if (!texDescriptors) + if (texDescriptors) { - texDescriptors = descPool->Allocate - ( - { SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS] }, - { static_cast(texOrder.size()) } - ); + texDescriptors.Free(); } + texDescriptors = descPool->Allocate + ( + { SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS] }, + { static_cast(texOrder.size()) } + ); texDescriptors->ModifyWriteDescImage ( SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS, diff --git a/SHADE_Engine/src/Resource/SHResourceManager.hpp b/SHADE_Engine/src/Resource/SHResourceManager.hpp index ee1a76ba..09c692e5 100644 --- a/SHADE_Engine/src/Resource/SHResourceManager.hpp +++ b/SHADE_Engine/src/Resource/SHResourceManager.hpp @@ -24,7 +24,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/Shaders/SHVkShaderModule.h" #include "Graphics/Devices/SHVkLogicalDevice.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" -#include "Serialization/SHSerializationHelper.hpp" +#include "Serialization/SHYAMLConverters.h" namespace SHADE { diff --git a/SHADE_Engine/src/Serialization/SHSerialization.cpp b/SHADE_Engine/src/Serialization/SHSerialization.cpp index dc79ac72..f2829b95 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.cpp +++ b/SHADE_Engine/src/Serialization/SHSerialization.cpp @@ -1,8 +1,8 @@ #include "SHpch.h" #include -#include "SHSerializationHelper.hpp" #include "SHSerialization.h" +#include "SHSerializationHelper.hpp" #include "ECS_Base/Managers/SHEntityManager.h" #include "Scene/SHSceneManager.h" @@ -118,8 +118,15 @@ namespace SHADE SHLOG_ERROR("Failed to create entities from deserializaiton") return NewSceneName.data(); } - //Initialize Entity auto entityVecIt = createdEntities.begin(); + AssetQueue assetQueue; + for (auto it = entities.begin(); it != entities.end(); ++it) + { + SHSerializationHelper::FetchAssetsFromComponent((*it)[ComponentsNode], *entityVecIt, assetQueue); + } + LoadAssetsFromAssetQueue(assetQueue); + //Initialize Entity + entityVecIt = createdEntities.begin(); for (auto it = entities.begin(); it != entities.end(); ++it) { InitializeEntity(*it, *entityVecIt++); @@ -256,6 +263,33 @@ namespace SHADE return componentIDList; } + void SHSerialization::LoadAssetsFromAssetQueue(AssetQueue& assetQueue) + { + for (auto& [assetId, assetType] : assetQueue) + { + switch(assetType) + { + case AssetType::INVALID: break; + case AssetType::SHADER: break; + case AssetType::SHADER_BUILT_IN: break; + case AssetType::TEXTURE: + SHResourceManager::LoadOrGet(assetId); + break; + case AssetType::MESH: + SHResourceManager::LoadOrGet(assetId); + break; + case AssetType::SCENE: break; + case AssetType::PREFAB: break; + case AssetType::MATERIAL: + SHResourceManager::LoadOrGet(assetId); + break; + case AssetType::MAX_COUNT: break; + default: ; + } + } + SHResourceManager::FinaliseChanges(); + } + void SHSerialization::InitializeEntity(YAML::Node const& entityNode, EntityID const& eid) { auto const componentsNode = entityNode[ComponentsNode]; diff --git a/SHADE_Engine/src/Serialization/SHSerialization.h b/SHADE_Engine/src/Serialization/SHSerialization.h index 865d53a3..3cb268f2 100644 --- a/SHADE_Engine/src/Serialization/SHSerialization.h +++ b/SHADE_Engine/src/Serialization/SHSerialization.h @@ -4,8 +4,10 @@ #include #include -#include + +#include "ECS_Base/SHECSMacros.h" #include +#include namespace YAML { @@ -42,6 +44,8 @@ namespace SHADE static EntityID DeserializeEntitiesFromString(std::string const& data, EntityID const& parentEID = MAX_EID) noexcept; static std::vector GetComponentIDList(YAML::Node const& componentsNode); + + static void LoadAssetsFromAssetQueue(std::unordered_map& assetQueue); private: static void InitializeEntity(YAML::Node const& entityNode, EntityID const& eid); diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index 46e591dc..77f10269 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -1,329 +1,20 @@ #pragma once +#include "SHYAMLConverters.h" #include #include "ECS_Base/Components/SHComponent.h" #include +#include #include "ECS_Base/Managers/SHComponentManager.h" -#include "Math/Vector/SHVec2.h" -#include "Math/Vector/SHVec3.h" -#include "Math/Vector/SHVec4.h" -#include "Resource/SHResourceManager.h" -#include "Graphics/MiddleEnd/Interface/SHRenderable.h" -#include "Graphics/MiddleEnd/Interface/SHMaterial.h" -#include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" -#include "SHSerializationTools.h" -#include "Physics/Components/SHColliderComponent.h" #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Tools/SHLog.h" -namespace YAML -{ - using namespace SHADE; - - template<> - struct convert - { - static constexpr const char* x = "x"; - static constexpr const char* y = "y"; - static constexpr const char* z = "z"; - static constexpr const char* w = "w"; - - static Node encode(SHVec4 const& rhs) - { - Node node; - node.SetStyle(EmitterStyle::Flow); - node[x] = rhs.x; - node[y] = rhs.y; - node[z] = rhs.z; - node[w] = rhs.w; - return node; - } - static bool decode(Node const& node, SHVec4& rhs) - { - if (node[x].IsDefined()) - rhs.x = node[x].as(); - if (node[y].IsDefined()) - rhs.y = node[y].as(); - if (node[z].IsDefined()) - rhs.z = node[z].as(); - if (node[w].IsDefined()) - rhs.w = node[w].as(); - return true; - } - }; - - template<> - struct convert - { - static constexpr const char* x = "x"; - static constexpr const char* y = "y"; - static constexpr const char* z = "z"; - - static Node encode(SHVec3 const& rhs) - { - Node node; - node.SetStyle(EmitterStyle::Flow); - node[x] = rhs.x; - node[y] = rhs.y; - node[z] = rhs.z; - return node; - } - static bool decode(Node const& node, SHVec3& rhs) - { - if (node[x].IsDefined()) - rhs.x = node[x].as(); - if (node[y].IsDefined()) - rhs.y = node[y].as(); - if (node[z].IsDefined()) - rhs.z = node[z].as(); - return true; - } - }; - - template<> - struct convert - { - static constexpr const char* x = "x"; - static constexpr const char* y = "y"; - - static Node encode(SHVec2 const& rhs) - { - Node node; - node.SetStyle(EmitterStyle::Flow); - node[x] = rhs.x; - node[y] = rhs.y; - return node; - } - static bool decode(Node const& node, SHVec2& rhs) - { - if (node[x].IsDefined()) - rhs.x = node[x].as(); - if (node[y].IsDefined()) - rhs.y = node[y].as(); - return true; - } - }; - - template<> - struct convert - { - static constexpr const char* IsTrigger = "Is Trigger"; - - static constexpr const char* Type = "Type"; - static constexpr const char* HalfExtents = "Half Extents"; - static constexpr const char* Radius = "Radius"; - - static constexpr const char* Friction = "Friction"; - static constexpr const char* Bounciness = "Bounciness"; - static constexpr const char* Density = "Density"; - static constexpr const char* PositionOffset = "Position Offset"; - - static Node encode(SHCollider& rhs) - { - Node node; - - node[IsTrigger] = rhs.IsTrigger(); - - rttr::type const shapeRttrType = rttr::type::get(); - rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); - SHCollider::Type colliderType = rhs.GetType(); - - node[Type] = enumAlign.value_to_name(colliderType).data(); - - switch (colliderType) - { - case SHCollider::Type::BOX: - { - auto const bb = reinterpret_cast(rhs.GetShape()); - node[HalfExtents] = bb->GetHalfExtents(); - } - break; - case SHCollider::Type::SPHERE: - { - auto const bs = reinterpret_cast(rhs.GetShape()); - node[Radius] = bs->GetRadius(); - } - break; - case SHCollider::Type::CAPSULE: break; - default:; - } - - node[Friction] = rhs.GetFriction(); - node[Bounciness] = rhs.GetBounciness(); - node[Density] = rhs.GetDensity(); - node[PositionOffset] = rhs.GetPositionOffset(); - - return node; - } - static bool decode(Node const& node, SHCollider& rhs) - { - if (node[IsTrigger].IsDefined()) - rhs.SetIsTrigger(node[IsTrigger].as()); - if (!node[Type].IsDefined()) - return false; - rttr::type const shapeRttrType = rttr::type::get(); - rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); - bool ok; - const SHCollider::Type colliderType = enumAlign.name_to_value(node[Type].as()).convert(&ok); - if (!ok) - return false; - switch (colliderType) - { - case SHCollider::Type::BOX: - { - if (node[HalfExtents].IsDefined()) - rhs.SetBoundingBox(node[HalfExtents].as()); - } - break; - case SHCollider::Type::SPHERE: - { - if (node[Radius].IsDefined()) - rhs.SetBoundingSphere(node[Radius].as()); - } - break; - case SHCollider::Type::CAPSULE: break; - default:; - } - if (node[Friction].IsDefined()) - rhs.SetFriction(node[Friction].as()); - if (node[Bounciness].IsDefined()) - rhs.SetBounciness(rhs.GetBounciness()); - if (node[Density].IsDefined()) - rhs.SetDensity(node[Density].as()); - if (node[PositionOffset].IsDefined()) - rhs.SetPositionOffset(node[PositionOffset].as()); - - return true; - } - }; - - template<> - struct convert - { - static constexpr const char* Colliders = "Colliders"; - static Node encode(SHColliderComponent& rhs) - { - Node node, collidersNode; - auto const& colliders = rhs.GetColliders(); - int const numColliders = static_cast(colliders.size()); - for (int i = 0; i < numColliders; ++i) - { - auto& collider = rhs.GetCollider(i); - Node colliderNode = convert::encode(collider); - if (colliderNode.IsDefined()) - collidersNode[i] = colliderNode; - } - node[Colliders] = collidersNode; - return node; - } - static bool decode(Node const& node, SHColliderComponent& rhs) - { - if (node[Colliders].IsDefined()) - { - int numColliders{}; - for (auto const& colliderNode : node[Colliders]) - { - rttr::type const shapeRttrType = rttr::type::get(); - rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); - bool ok = false; - const SHCollider::Type colliderType = enumAlign.name_to_value(colliderNode[convert::Type].as()).convert(&ok); - if (!ok) - return false; - - switch (colliderType) - { - case SHCollider::Type::BOX: rhs.AddBoundingBox(); break; - case SHCollider::Type::SPHERE: rhs.AddBoundingSphere(); break; - case SHCollider::Type::CAPSULE: break; - default:; - } - YAML::convert::decode(colliderNode, rhs.GetCollider(numColliders++)); - } - } - return true; - } - }; - - template<> - struct convert - { - static constexpr std::string_view VERT_SHADER_YAML_TAG = "VertexShader"; - static constexpr std::string_view FRAG_SHADER_YAML_TAG = "FragmentShader"; - static constexpr std::string_view SUBPASS_YAML_TAG = "SubPass"; - static constexpr std::string_view PROPS_YAML_TAG = "Properties"; - - static YAML::Node encode(SHMaterialSpec const& rhs) - { - YAML::Node node; - node[VERT_SHADER_YAML_TAG.data()] = rhs.vertexShader; - node[FRAG_SHADER_YAML_TAG.data()] = rhs.fragShader; - node[SUBPASS_YAML_TAG.data()] = rhs.subpassName; - node[PROPS_YAML_TAG.data()] = rhs.properties; - return node; - } - - static bool decode(YAML::Node const& node, SHMaterialSpec& rhs) - { - // Retrieve Shader Asset IDs - if (node[VERT_SHADER_YAML_TAG.data()].IsDefined()) - rhs.vertexShader = node[VERT_SHADER_YAML_TAG.data()].as(); - if (node[FRAG_SHADER_YAML_TAG.data()].IsDefined()) - rhs.fragShader = node[FRAG_SHADER_YAML_TAG.data()].as(); - - // Retrieve Subpass - if (node[SUBPASS_YAML_TAG.data()].IsDefined()) - rhs.subpassName = node[SUBPASS_YAML_TAG.data()].as(); - - // Retrieve - if (node[PROPS_YAML_TAG.data()].IsDefined()) - rhs.properties = node[PROPS_YAML_TAG.data()]; - - return true; - } - }; - - template<> - struct convert - { - static constexpr std::string_view MESH_YAML_TAG = "Mesh"; - static constexpr std::string_view MAT_YAML_TAG = "Material"; - - static YAML::Node encode(SHRenderable const& rhs) - { - YAML::Node node; - node[MESH_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMesh()).value_or(0); - node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMaterial()->GetBaseMaterial()).value_or(0); - return node; - } - static bool decode(YAML::Node const& node, SHRenderable& rhs) - { - if (node[MESH_YAML_TAG.data()].IsDefined()) - { - rhs.SetMesh(SHResourceManager::LoadOrGet(node[MESH_YAML_TAG.data()].as())); - } - if (node[MAT_YAML_TAG.data()].IsDefined()) - { - // Temporarily, use default material - auto gfxSystem = SHSystemManager::GetSystem(); - if (!gfxSystem) - return false; - Handle baseMat = SHResourceManager::LoadOrGet(node[MAT_YAML_TAG.data()].as()); - if (!baseMat) - { - baseMat = gfxSystem->GetDefaultMaterial(); - SHLog::Warning("[SHSerializationHelper] Unable to load specified material. Falling back to default material."); - } - rhs.SetMaterial(gfxSystem->AddOrGetBaseMaterialInstance(baseMat)); - } - return true; - } - }; -} namespace SHADE { + using AssetQueue = std::unordered_map; struct SHSerializationHelper { @@ -517,18 +208,73 @@ namespace SHADE } } + template , bool> = true> + static YAML::Node GetComponentNode(YAML::Node const& componentsNode, EntityID const& eid) + { + auto component = SHComponentManager::GetComponent_s(eid); + if (componentsNode.IsNull() && !component) + return {}; + auto rttrType = rttr::type::get(); + auto componentNode = componentsNode[rttrType.get_name().data()]; + if (!componentNode.IsDefined()) + return {}; + return componentNode; + } + template , bool> = true> static void ConvertNodeToComponent(YAML::Node const& componentsNode, EntityID const& eid) { auto component = SHComponentManager::GetComponent_s(eid); if (componentsNode.IsNull() && !component) return; - auto rttrType = rttr::type::get(); - auto componentNode = componentsNode[rttrType.get_name().data()]; - if (!componentNode.IsDefined()) - return; - YAML::convert::decode(componentNode, *component); + + YAML::convert::decode(GetComponentNode(componentsNode, eid), *component); } + template , bool> = true> + static void FetchAssetsFromComponent(YAML::Node const& componentsNode, EntityID const& eid, AssetQueue& assetQueue) + { + } + + template<> + static void FetchAssetsFromComponent(YAML::Node const& componentsNode, EntityID const& eid, AssetQueue& assetQueue) + { + auto node = GetComponentNode(componentsNode, eid); + if(!node.IsDefined()) + return; + if (auto const& meshNode = node[YAML::convert::MESH_YAML_TAG.data()]; meshNode.IsDefined()) + { + assetQueue.insert({meshNode.as(), AssetType::MESH}); + //SHResourceManager::LoadOrGet(node[YAML::convert::MESH_YAML_TAG.data()].as()); + } + if (auto const& matNode = node[YAML::convert::MAT_YAML_TAG.data()]; matNode.IsDefined()) + { + auto const matAsset = SHAssetManager::GetData(matNode.as()); + if(matAsset) + { + SHMaterialSpec spec; + YAML::convert::decode(*YAML::Load(matAsset->data).begin(), spec); + if(spec.properties.IsDefined()) + { + auto fragShader = SHResourceManager::LoadOrGet(spec.fragShader); + auto interface = fragShader->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA); + /*int const varCount = static_cast(interface->GetVariableCount()); + + for (int i = 0; i < varCount; ++i) + { + auto variable = interface->GetVariable(i); + if(variable->type != SHShaderBlockInterface::Variable::Type::INT) + return; + const std::string& VAR_NAME = interface->GetVariableName(i); + if(VAR_NAME.empty()) + continue; + assetQueue.insert({matNode.as(), AssetType::TEXTURE}); + }*/ + } + } + assetQueue.insert({matNode.as(), AssetType::MATERIAL}); + //SHResourceManager::LoadOrGet(node[YAML::convert::MAT_YAML_TAG.data()].as()); + } + } }; } diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h new file mode 100644 index 00000000..af0b280b --- /dev/null +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -0,0 +1,317 @@ +#pragma once +#include "Graphics/MiddleEnd/Interface/SHRenderable.h" +#include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" +#include "Math/Geometry/SHBoundingBox.h" +#include "Math/Geometry/SHBoundingSphere.h" +#include "Physics/SHCollider.h" +#include "Resource/SHResourceManager.h" +#include "Math/Vector/SHVec2.h" +#include "Math/Vector/SHVec3.h" +#include "Math/Vector/SHVec4.h" +#include "Graphics/MiddleEnd/Interface/SHMaterial.h" +#include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" +#include "SHSerializationTools.h" +#include "Physics/Components/SHColliderComponent.h" +namespace YAML +{ + using namespace SHADE; + + template<> + struct convert + { + static constexpr const char* x = "x"; + static constexpr const char* y = "y"; + static constexpr const char* z = "z"; + static constexpr const char* w = "w"; + + static Node encode(SHVec4 const& rhs) + { + Node node; + node.SetStyle(EmitterStyle::Flow); + node[x] = rhs.x; + node[y] = rhs.y; + node[z] = rhs.z; + node[w] = rhs.w; + return node; + } + static bool decode(Node const& node, SHVec4& rhs) + { + if (node[x].IsDefined()) + rhs.x = node[x].as(); + if (node[y].IsDefined()) + rhs.y = node[y].as(); + if (node[z].IsDefined()) + rhs.z = node[z].as(); + if (node[w].IsDefined()) + rhs.w = node[w].as(); + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* x = "x"; + static constexpr const char* y = "y"; + static constexpr const char* z = "z"; + + static Node encode(SHVec3 const& rhs) + { + Node node; + node.SetStyle(EmitterStyle::Flow); + node[x] = rhs.x; + node[y] = rhs.y; + node[z] = rhs.z; + return node; + } + static bool decode(Node const& node, SHVec3& rhs) + { + if (node[x].IsDefined()) + rhs.x = node[x].as(); + if (node[y].IsDefined()) + rhs.y = node[y].as(); + if (node[z].IsDefined()) + rhs.z = node[z].as(); + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* x = "x"; + static constexpr const char* y = "y"; + + static Node encode(SHVec2 const& rhs) + { + Node node; + node.SetStyle(EmitterStyle::Flow); + node[x] = rhs.x; + node[y] = rhs.y; + return node; + } + static bool decode(Node const& node, SHVec2& rhs) + { + if (node[x].IsDefined()) + rhs.x = node[x].as(); + if (node[y].IsDefined()) + rhs.y = node[y].as(); + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* IsTrigger = "Is Trigger"; + + static constexpr const char* Type = "Type"; + static constexpr const char* HalfExtents = "Half Extents"; + static constexpr const char* Radius = "Radius"; + + static constexpr const char* Friction = "Friction"; + static constexpr const char* Bounciness = "Bounciness"; + static constexpr const char* Density = "Density"; + static constexpr const char* PositionOffset = "Position Offset"; + + static Node encode(SHCollider& rhs) + { + Node node; + + node[IsTrigger] = rhs.IsTrigger(); + + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + SHCollider::Type colliderType = rhs.GetType(); + + node[Type] = enumAlign.value_to_name(colliderType).data(); + + switch (colliderType) + { + case SHCollider::Type::BOX: + { + auto const bb = reinterpret_cast(rhs.GetShape()); + node[HalfExtents] = bb->GetHalfExtents(); + } + break; + case SHCollider::Type::SPHERE: + { + auto const bs = reinterpret_cast(rhs.GetShape()); + node[Radius] = bs->GetRadius(); + } + break; + case SHCollider::Type::CAPSULE: break; + default:; + } + + node[Friction] = rhs.GetFriction(); + node[Bounciness] = rhs.GetBounciness(); + node[Density] = rhs.GetDensity(); + node[PositionOffset] = rhs.GetPositionOffset(); + + return node; + } + static bool decode(Node const& node, SHCollider& rhs) + { + if (node[IsTrigger].IsDefined()) + rhs.SetIsTrigger(node[IsTrigger].as()); + if (!node[Type].IsDefined()) + return false; + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + bool ok; + const SHCollider::Type colliderType = enumAlign.name_to_value(node[Type].as()).convert(&ok); + if (!ok) + return false; + switch (colliderType) + { + case SHCollider::Type::BOX: + { + if (node[HalfExtents].IsDefined()) + rhs.SetBoundingBox(node[HalfExtents].as()); + } + break; + case SHCollider::Type::SPHERE: + { + if (node[Radius].IsDefined()) + rhs.SetBoundingSphere(node[Radius].as()); + } + break; + case SHCollider::Type::CAPSULE: break; + default:; + } + if (node[Friction].IsDefined()) + rhs.SetFriction(node[Friction].as()); + if (node[Bounciness].IsDefined()) + rhs.SetBounciness(rhs.GetBounciness()); + if (node[Density].IsDefined()) + rhs.SetDensity(node[Density].as()); + if (node[PositionOffset].IsDefined()) + rhs.SetPositionOffset(node[PositionOffset].as()); + + return true; + } + }; + + template<> + struct convert + { + static constexpr const char* Colliders = "Colliders"; + static Node encode(SHColliderComponent& rhs) + { + Node node, collidersNode; + auto const& colliders = rhs.GetColliders(); + int const numColliders = static_cast(colliders.size()); + for (int i = 0; i < numColliders; ++i) + { + auto& collider = rhs.GetCollider(i); + Node colliderNode = convert::encode(collider); + if (colliderNode.IsDefined()) + collidersNode[i] = colliderNode; + } + node[Colliders] = collidersNode; + return node; + } + static bool decode(Node const& node, SHColliderComponent& rhs) + { + if (node[Colliders].IsDefined()) + { + int numColliders{}; + for (auto const& colliderNode : node[Colliders]) + { + rttr::type const shapeRttrType = rttr::type::get(); + rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); + bool ok = false; + const SHCollider::Type colliderType = enumAlign.name_to_value(colliderNode[convert::Type].as()).convert(&ok); + if (!ok) + return false; + + switch (colliderType) + { + case SHCollider::Type::BOX: rhs.AddBoundingBox(); break; + case SHCollider::Type::SPHERE: rhs.AddBoundingSphere(); break; + case SHCollider::Type::CAPSULE: break; + default:; + } + YAML::convert::decode(colliderNode, rhs.GetCollider(numColliders++)); + } + } + return true; + } + }; + + template<> + struct convert + { + static constexpr std::string_view VERT_SHADER_YAML_TAG = "VertexShader"; + static constexpr std::string_view FRAG_SHADER_YAML_TAG = "FragmentShader"; + static constexpr std::string_view SUBPASS_YAML_TAG = "SubPass"; + static constexpr std::string_view PROPS_YAML_TAG = "Properties"; + + static YAML::Node encode(SHMaterialSpec const& rhs) + { + YAML::Node node; + node[VERT_SHADER_YAML_TAG.data()] = rhs.vertexShader; + node[FRAG_SHADER_YAML_TAG.data()] = rhs.fragShader; + node[SUBPASS_YAML_TAG.data()] = rhs.subpassName; + node[PROPS_YAML_TAG.data()] = rhs.properties; + return node; + } + + static bool decode(YAML::Node const& node, SHMaterialSpec& rhs) + { + // Retrieve Shader Asset IDs + if (node[VERT_SHADER_YAML_TAG.data()].IsDefined()) + rhs.vertexShader = node[VERT_SHADER_YAML_TAG.data()].as(); + if (node[FRAG_SHADER_YAML_TAG.data()].IsDefined()) + rhs.fragShader = node[FRAG_SHADER_YAML_TAG.data()].as(); + + // Retrieve Subpass + if (node[SUBPASS_YAML_TAG.data()].IsDefined()) + rhs.subpassName = node[SUBPASS_YAML_TAG.data()].as(); + + // Retrieve + if (node[PROPS_YAML_TAG.data()].IsDefined()) + rhs.properties = node[PROPS_YAML_TAG.data()]; + + return true; + } + }; + + template<> + struct convert + { + static constexpr std::string_view MESH_YAML_TAG = "Mesh"; + static constexpr std::string_view MAT_YAML_TAG = "Material"; + + static YAML::Node encode(SHRenderable const& rhs) + { + YAML::Node node; + node[MESH_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMesh()).value_or(0); + node[MAT_YAML_TAG.data()] = SHResourceManager::GetAssetID(rhs.GetMaterial()->GetBaseMaterial()).value_or(0); + return node; + } + static bool decode(YAML::Node const& node, SHRenderable& rhs) + { + if (node[MESH_YAML_TAG.data()].IsDefined()) + { + rhs.SetMesh(SHResourceManager::LoadOrGet(node[MESH_YAML_TAG.data()].as())); + } + if (node[MAT_YAML_TAG.data()].IsDefined()) + { + // Temporarily, use default material + auto gfxSystem = SHSystemManager::GetSystem(); + if (!gfxSystem) + return false; + Handle baseMat = SHResourceManager::LoadOrGet(node[MAT_YAML_TAG.data()].as()); + if (!baseMat) + { + baseMat = gfxSystem->GetDefaultMaterial(); + SHLog::Warning("[SHSerializationHelper] Unable to load specified material. Falling back to default material."); + } + rhs.SetMaterial(gfxSystem->AddOrGetBaseMaterialInstance(baseMat)); + } + return true; + } + }; +} From d8ee9912130d29fe714308192ddd825afcb2d638 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 4 Nov 2022 18:39:45 +0800 Subject: [PATCH 076/113] Steps in the rendering process are now highlighted and named for debugging tools --- SHADE_Engine/src/Editor/SHEditor.cpp | 7 +++- .../Graphics/Commands/SHVkCommandBuffer.cpp | 38 +++++++++++++++++++ .../src/Graphics/Commands/SHVkCommandBuffer.h | 13 ++++++- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 2 + .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 3 ++ .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 6 +-- .../Graphics/RenderGraph/SHRenderGraph.cpp | 8 +++- .../src/Graphics/RenderGraph/SHRenderGraph.h | 5 ++- .../src/Graphics/RenderGraph/SHSubpass.cpp | 3 +- 9 files changed, 75 insertions(+), 10 deletions(-) diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 6974d213..86987479 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -441,9 +441,12 @@ namespace SHADE ImGui_ImplVulkan_DestroyFontUploadObjects(); - renderGraph->GetNode("ImGui Node")->GetSubpass("ImGui Draw")->AddExteriorDrawCalls([](Handle& cmd) { + renderGraph->GetNode("ImGui Node")->GetSubpass("ImGui Draw")->AddExteriorDrawCalls([](Handle& cmd) + { + cmd->BeginLabeledSegment("ImGui Draw"); ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd->GetVkCommandBuffer()); - }); + cmd->EndLabeledSegment(); + }); #endif } diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp index fb64bcc2..cc35303b 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp @@ -105,6 +105,9 @@ namespace SHADE // Set the state to recording if the call above succeeded. cmdBufferState = SH_CMD_BUFFER_STATE::RECORDING; + + // Reset segment count + segmentDepth = 0; } /***************************************************************************/ @@ -507,6 +510,41 @@ namespace SHADE SetState(SH_CMD_BUFFER_STATE::PENDING); } + void SHVkCommandBuffer::BeginLabeledSegment(const std::string& label) noexcept + { +#ifdef _DEBUG + static const std::array SEGMENT_COLOURS = + { + SHColour::LIGHTPINK, + SHColour::LIGHTBLUE, + SHColour::LIGHTGREEN, + SHColour::YELLOW, + SHColour::PINK, + SHColour::TEAL, + SHColour::LIME, + SHColour::ORANGE, + SHColour::VIOLET, + SHColour::MAROON, + SHColour::DARKGREEN, + SHColour::SANDYBROWN + }; + + const SHColour COLOR = SEGMENT_COLOURS[segmentDepth]; + ++segmentDepth; + if (segmentDepth >= static_cast(SEGMENT_COLOURS.size())) + segmentDepth = 0; + vkCommandBuffer.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT().setPLabelName(label.data()).setColor({ COLOR.x, COLOR.y, COLOR.z, COLOR.w })); +#endif + } + + void SHVkCommandBuffer::EndLabeledSegment() noexcept + { +#ifdef _DEBUG + vkCommandBuffer.endDebugUtilsLabelEXT(); + segmentDepth = std::max(segmentDepth - 1, 0); +#endif + } + //void SHVkCommandBuffer::PipelineBarrier(vk::PipelineStageFlags ) const noexcept //{ // //vkCommandBuffer.pipelineBarrier() diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h index bc0d4b04..022d4b62 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h @@ -7,6 +7,7 @@ #include "Resource/SHResourceLibrary.h" #include "Graphics/Pipeline/SHVkPipelineLayout.h" #include "Graphics/Pipeline/SHPipelineType.h" +#include "Math/SHColour.h" namespace SHADE { @@ -78,7 +79,11 @@ namespace SHADE std::array(SH_PIPELINE_TYPE::NUM_TYPES)> bindPointData; //! The push constant data for the command buffer - uint8_t pushConstantData[PUSH_CONSTANT_SIZE]; + uint8_t pushConstantData[PUSH_CONSTANT_SIZE]; + +#ifdef _DEBUG + int segmentDepth; +#endif /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER FUNCTIONS */ @@ -107,7 +112,7 @@ namespace SHADE void Reset(void); // Begins and Ends - void BeginRecording (void) noexcept; + void BeginRecording () noexcept; void EndRecording (void) noexcept; void BeginRenderpass (Handle const& renderpassHdl, Handle const& framebufferHdl, vk::Offset2D offset = {0, 0}, vk::Extent2D extent = {0, 0}) noexcept; void EndRenderpass (void) noexcept; @@ -148,6 +153,10 @@ namespace SHADE bool IsReadyToSubmit (void) const noexcept; void HandlePostSubmit (void) noexcept; + // Debugging + void BeginLabeledSegment(const std::string& label) noexcept; + void EndLabeledSegment() noexcept; + // Push Constant variable setting template void SetPushConstantVariable(std::string variableName, T const& data, SH_PIPELINE_TYPE bindPoint) noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 22d1875b..c2471f79 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -427,6 +427,7 @@ namespace SHADE // Bind all required objects before drawing static std::array dynamicOffset{ 0 }; + cmdBuffer->BeginLabeledSegment("SHBatch"); cmdBuffer->BindPipeline(pipeline); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0); cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::INTEGER_DATA, instancedIntegerBuffer[frameIndex], 0); @@ -441,6 +442,7 @@ namespace SHADE ); } cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); + cmdBuffer->EndLabeledSegment(); } /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 37d15808..c4dc2c8f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -98,6 +98,7 @@ namespace SHADE // Don't draw if no points if (numPoints[FRAME_IDX] > 0) { + cmdBuffer->BeginLabeledSegment("SHDebugDraw"); cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawPipeline()); cmdBuffer->SetLineWidth(LineWidth); cmdBuffer->BindVertexBuffer(0, vertexBuffers[FRAME_IDX], 0); @@ -113,10 +114,12 @@ namespace SHADE // Don't draw if no points if (numPersistentPoints[FRAME_IDX] > 0) { + cmdBuffer->BeginLabeledSegment("SHDebugDraw (Persistent)"); cmdBuffer->BindPipeline(GFX_SYSTEM->GetDebugDrawDepthPipeline()); cmdBuffer->SetLineWidth(LineWidth); cmdBuffer->BindVertexBuffer(0, persistentVertexBuffers[FRAME_IDX], 0); cmdBuffer->DrawArrays(numPersistentPoints[FRAME_IDX], 1, 0, 0); + cmdBuffer->EndLabeledSegment(); } }); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index be67f1b3..1884fa57 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -1,5 +1,5 @@ /************************************************************************************//*! -\file SHGraphicsSystem.cpp +\file SHGrphicsSystem.cpp \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Aug 21, 2022 @@ -164,7 +164,7 @@ namespace SHADE /* SCENE RENDER GRAPH RESOURCES */ /*-----------------------------------------------------------------------*/ // Initialize world render graph - worldRenderGraph->Init(device, swapchain); + worldRenderGraph->Init("World Render Graph", device, swapchain); worldRenderGraph->AddResource("Position", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Sfloat); worldRenderGraph->AddResource("Normals", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Sfloat); //worldRenderGraph->AddResource("Tangents", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR32G32B32A32Sfloat); @@ -331,7 +331,7 @@ namespace SHADE for (uint32_t i = 0; i < renderContextCmdPools.size(); ++i) renderContextCmdPools[i] = renderContext.GetFrameData(i).cmdPoolHdls[0]; - editorRenderGraph->Init(device, swapchain); + editorRenderGraph->Init("Editor Render Graph", device, swapchain); editorRenderGraph->AddResource("Present", { SH_ATT_DESC_TYPE_FLAGS::COLOR_PRESENT }, windowDims.first, windowDims.second); diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp index 93be2413..66dc6a0f 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp @@ -353,7 +353,7 @@ namespace SHADE */ /***************************************************************************/ - void SHRenderGraph::Init(Handle logicalDevice, Handle swapchain) noexcept + void SHRenderGraph::Init(std::string graphName, Handle logicalDevice, Handle swapchain) noexcept { resourceManager = std::make_shared(); @@ -365,6 +365,8 @@ namespace SHADE renderGraphStorage->resourceManager = resourceManager; renderGraphStorage->descriptorPool = logicalDevice->CreateDescriptorPools(); + + name = std::move(graphName); } /***************************************************************************/ @@ -390,6 +392,7 @@ namespace SHADE , nodeIndexing{ std::move(rhs.nodeIndexing) } , nodes{ std::move(rhs.nodes) } , resourceManager{ std::move(rhs.resourceManager) } + , name { std::move(rhs.name) } { } @@ -403,6 +406,7 @@ namespace SHADE nodeIndexing = std::move(rhs.nodeIndexing); nodes = std::move(rhs.nodes); resourceManager = std::move(rhs.resourceManager); + name = std::move(rhs.name); return *this; } @@ -516,8 +520,10 @@ namespace SHADE // better way to manage these void SHRenderGraph::Execute(uint32_t frameIndex, Handle cmdBuffer, Handle descPool) noexcept { + cmdBuffer->BeginLabeledSegment(name); for (auto& node : nodes) node->Execute(cmdBuffer, descPool, frameIndex); + cmdBuffer->EndLabeledSegment(); } void SHRenderGraph::FinaliseBatch(uint32_t frameIndex, Handle descPool) diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.h index d90b66df..741cc522 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.h @@ -69,6 +69,9 @@ namespace SHADE //! Resource library for graph handles std::shared_ptr resourceManager; + //! Name of the RenderGraph + std::string name; + public: /*-----------------------------------------------------------------------*/ /* CTORS AND DTORS */ @@ -81,7 +84,7 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* PUBLIC MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ - void Init (Handle logicalDevice, Handle swapchain) noexcept; + void Init (std::string graphName, Handle logicalDevice, Handle swapchain) noexcept; void AddResource(std::string resourceName, std::initializer_list typeFlags, uint32_t w = static_cast(-1), uint32_t h = static_cast(-1), vk::Format format = vk::Format::eB8G8R8A8Unorm, uint8_t levels = 1, vk::ImageUsageFlagBits usageFlags = {}, vk::ImageCreateFlagBits createFlags = {}); Handle AddNode (std::string nodeName, std::initializer_list resourceInstruction, std::initializer_list predecessorNodes) noexcept; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp index 6e78eb9f..23dbbde3 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp @@ -201,6 +201,7 @@ namespace SHADE void SHSubpass::Execute(Handle& commandBuffer, Handle descPool, uint32_t frameIndex) noexcept { + commandBuffer->BeginLabeledSegment(name); // Ensure correct transforms are provided superBatch->UpdateBuffers(frameIndex, descPool); @@ -212,7 +213,7 @@ namespace SHADE { drawCall(commandBuffer); } - + commandBuffer->EndLabeledSegment(); } void SHSubpass::HandleResize(void) noexcept From b30da7e495791557c676d5006d05dd121d5c8c23 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Fri, 4 Nov 2022 19:16:32 +0800 Subject: [PATCH 077/113] Fixes --- Assets/Materials/TestMat.shmat | 2 +- Assets/Scenes/M2Scene.shade | 14 +++++++------- .../src/Serialization/SHSerializationHelper.hpp | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Assets/Materials/TestMat.shmat b/Assets/Materials/TestMat.shmat index acfa2dc3..c1bb43c5 100644 --- a/Assets/Materials/TestMat.shmat +++ b/Assets/Materials/TestMat.shmat @@ -2,7 +2,7 @@ FragmentShader: 46377769 SubPass: G-Buffer Write Properties: - data.color: {x: 0, y: 0, z: 0, w: 1} + data.color: {x: 1, y: 1, z: 1, w: 1} data.textureIndex: 64651793 data.alpha: 0 data.beta: {x: 1, y: 1, z: 1} \ No newline at end of file diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade index 35608ab6..38c0a523 100644 --- a/Assets/Scenes/M2Scene.shade +++ b/Assets/Scenes/M2Scene.shade @@ -31,8 +31,8 @@ Rotate: {x: -0, y: 0, z: -0} Scale: {x: 49.4798889, y: 0.5, z: 17.5} Renderable Component: - Mesh: 80365422 - Material: 0 + Mesh: 149697411 + Material: 126974645 RigidBody Component: Type: Static Mass: 1 @@ -66,7 +66,7 @@ Rotate: {x: -0, y: 0, z: -0} Scale: {x: 2, y: 2, z: 2} Renderable Component: - Mesh: 80365422 + Mesh: 149697411 Material: 126974645 RigidBody Component: Type: Dynamic @@ -121,8 +121,8 @@ Rotate: {x: 0, y: 0, z: 0} Scale: {x: 2, y: 2, z: 2} Renderable Component: - Mesh: 80365422 - Material: 124370424 + Mesh: 149697411 + Material: 126974645 RigidBody Component: Type: Dynamic Mass: 1 @@ -163,7 +163,7 @@ Rotate: {x: 0, y: 0, z: 0} Scale: {x: 2, y: 2, z: 2} Renderable Component: - Mesh: 80365422 + Mesh: 149697411 Material: 126974645 RigidBody Component: Type: Dynamic @@ -198,7 +198,7 @@ Rotate: {x: 0, y: 0, z: 0} Scale: {x: 5, y: 5, z: 5} Renderable Component: - Mesh: 80365422 + Mesh: 149697411 Material: 126974645 Scripts: ~ - EID: 8 diff --git a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp index 77f10269..b062b348 100644 --- a/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp +++ b/SHADE_Engine/src/Serialization/SHSerializationHelper.hpp @@ -258,21 +258,21 @@ namespace SHADE { auto fragShader = SHResourceManager::LoadOrGet(spec.fragShader); auto interface = fragShader->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA); - /*int const varCount = static_cast(interface->GetVariableCount()); + int const varCount = static_cast(interface->GetVariableCount()); for (int i = 0; i < varCount; ++i) { auto variable = interface->GetVariable(i); if(variable->type != SHShaderBlockInterface::Variable::Type::INT) - return; + continue; const std::string& VAR_NAME = interface->GetVariableName(i); if(VAR_NAME.empty()) continue; - assetQueue.insert({matNode.as(), AssetType::TEXTURE}); - }*/ + assetQueue.insert({spec.properties[VAR_NAME.data()].as(), AssetType::TEXTURE}); + } } } - assetQueue.insert({matNode.as(), AssetType::MATERIAL}); + //assetQueue.insert({matNode.as(), AssetType::MATERIAL}); //SHResourceManager::LoadOrGet(node[YAML::convert::MAT_YAML_TAG.data()].as()); } } From b5bc64456c52930f75ca7eff8b177faea7e7f6d4 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 4 Nov 2022 19:16:57 +0800 Subject: [PATCH 078/113] Added debug names for some SHVkBuffers --- .../src/Graphics/Buffers/SHVkBuffer.cpp | 13 +++++- .../src/Graphics/Buffers/SHVkBuffer.h | 14 ++++-- .../Graphics/Devices/SHVkLogicalDevice.cpp | 5 +- .../src/Graphics/Devices/SHVkLogicalDevice.h | 27 ++++++++--- .../Graphics/Devices/SHVkLogicalDevice.hpp | 46 +++++++++++++++++++ .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 9 ++-- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 6 ++- SHADE_Engine/src/Graphics/SHVkUtil.cpp | 10 ++-- SHADE_Engine/src/Graphics/SHVkUtil.h | 4 +- 9 files changed, 110 insertions(+), 24 deletions(-) create mode 100644 SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp index 08481483..63f580b9 100644 --- a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp @@ -269,7 +269,7 @@ namespace SHADE */ /***************************************************************************/ - SHVkBuffer::SHVkBuffer(std::reference_wrapper allocator) noexcept + SHVkBuffer::SHVkBuffer(Handle logicalDevice, std::reference_wrapper allocator) noexcept : vkBuffer{} , stagingBuffer{} , sizeStored{ 0 } @@ -277,19 +277,23 @@ namespace SHADE , alloc {nullptr} , randomAccessOptimized{false} , vmaAllocator{allocator} + , device { logicalDevice } {} SHVkBuffer::SHVkBuffer( + Handle logicalDevice, uint32_t inSize, void* data, uint32_t srcSize, std::reference_wrapper allocator, vk::BufferUsageFlags bufferUsage, + const std::string& name, VmaMemoryUsage memUsage, VmaAllocationCreateFlags allocFlags ) noexcept - : SHVkBuffer(allocator) + : SHVkBuffer(logicalDevice, allocator) { + this->name = name; Init(inSize, data, srcSize, bufferUsage, memUsage, allocFlags); } @@ -304,6 +308,8 @@ namespace SHADE , bufferUsageFlags {rhs.bufferUsageFlags} , bufferCreateInfo { rhs.bufferCreateInfo } , allocCreateInfo { rhs.allocCreateInfo } + , name { std::move(rhs.name) } + , device { rhs.device } { rhs.vkBuffer = VK_NULL_HANDLE; @@ -325,6 +331,8 @@ namespace SHADE bufferCreateInfo = rhs.bufferCreateInfo; allocCreateInfo = rhs.allocCreateInfo; bufferUsageFlags = rhs.bufferUsageFlags; + name = std::move(rhs.name); + device = rhs.device; return *this; } @@ -402,6 +410,7 @@ namespace SHADE auto [tempBuffer, allocInfo] = createBuffer(sizeStored); vkBuffer = tempBuffer; + SET_VK_OBJ_NAME(*device, vk::ObjectType::eBuffer, vkBuffer, name); // This probably means that a HOST_CACHED memory type is used on allocation if (allocFlags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h index eb24d161..1119342c 100644 --- a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h +++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h @@ -12,6 +12,7 @@ namespace SHADE //using SHVkBufferUsageBits = vk::BufferUsageFlagBits; class SHVkCommandBuffer; + class SHVkLogicalDevice; class SHVkBuffer { @@ -51,6 +52,11 @@ namespace SHADE //VmaAllocator const& vmaAllocator; std::reference_wrapper vmaAllocator; + //! Name of this buffer if any + std::string name; + //! Handle to the logical device that created this buffer + Handle device; + /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ @@ -62,13 +68,15 @@ namespace SHADE /* CTORS AND DTORS */ /*-----------------------------------------------------------------------*/ SHVkBuffer (void) noexcept = delete; - SHVkBuffer (std::reference_wrapper allocator) noexcept; + SHVkBuffer (Handle logicalDevice, std::reference_wrapper allocator) noexcept; SHVkBuffer ( + Handle logicalDevice, uint32_t inSize, void* data, uint32_t srcSize, std::reference_wrapper allocator, - vk::BufferUsageFlags bufferUsage, + vk::BufferUsageFlags bufferUsage, + const std::string& name = "", VmaMemoryUsage memUsage = VMA_MEMORY_USAGE_AUTO, VmaAllocationCreateFlags allocFlags = {} ) noexcept; @@ -84,7 +92,7 @@ namespace SHADE uint32_t inSize, void* data, uint32_t srcSize, - vk::BufferUsageFlags bufferUsage, + vk::BufferUsageFlags bufferUsage, VmaMemoryUsage memUsage, VmaAllocationCreateFlags allocFlags ) noexcept; diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp index 6bf2e731..35c104ee 100644 --- a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp @@ -220,6 +220,7 @@ namespace SHADE else { SHVulkanDebugUtil::ReportVkSuccess("Successfully created a Logical Device. "); + SET_VK_OBJ_NAME((*this), vk::ObjectType::eDevice, vkLogicalDevice, "Logical Device"); } InitializeVMA(); @@ -419,9 +420,9 @@ namespace SHADE */ /***************************************************************************/ - Handle SHVkLogicalDevice::CreateBuffer(uint32_t inSize, void* data, uint32_t srcSize, vk::BufferUsageFlags bufferUsage, VmaMemoryUsage memUsage, VmaAllocationCreateFlags allocFlags) const noexcept + Handle SHVkLogicalDevice::CreateBuffer(uint32_t inSize, void* data, uint32_t srcSize, vk::BufferUsageFlags bufferUsage, VmaMemoryUsage memUsage, VmaAllocationCreateFlags allocFlags, const std::string& name) const noexcept { - return SHVkInstance::GetResourceManager().Create(inSize, data, srcSize, std::cref(vmaAllocator), bufferUsage, memUsage, allocFlags); + return SHVkInstance::GetResourceManager().Create(GetHandle(), inSize, data, srcSize, std::cref(vmaAllocator), bufferUsage, name, memUsage, allocFlags); } /***************************************************************************/ diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h index 2aa59941..827f12b3 100644 --- a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h @@ -1,5 +1,4 @@ -#ifndef SH_LOGICAL_DEVICE_H -#define SH_LOGICAL_DEVICE_H +#pragma once #include #include @@ -67,7 +66,6 @@ namespace SHADE class SHVkLogicalDevice : public ISelfHandle { private: - /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER VARIABLES */ /*-----------------------------------------------------------------------*/ @@ -147,7 +145,8 @@ namespace SHADE uint32_t srcSize, vk::BufferUsageFlags bufferUsage, VmaMemoryUsage memUsage, - VmaAllocationCreateFlags allocFlags + VmaAllocationCreateFlags allocFlags, + const std::string& name = "" ) const noexcept; Handle CreateImage ( @@ -202,7 +201,23 @@ namespace SHADE Handle CreateSemaphore (void) const noexcept; void UpdateDescriptorSets(std::vector const& writeDescSets) noexcept; - void UpdateDescriptorSet (vk::WriteDescriptorSet const& writeDescSet) noexcept; + void UpdateDescriptorSet(vk::WriteDescriptorSet const& writeDescSet) noexcept; + + /*-----------------------------------------------------------------------*/ + /* Debug Tools */ + /*-----------------------------------------------------------------------*/ +#ifdef _DEBUG + ///

+ /// Sets a Vulkan object's name for debugging purposes. This function will not be + /// compiled outsied of Debug configurations. Hence, it is advised to use provided + /// macro function SET_VK_OBJ_NAME() instead of using this function directly. + /// + /// Type of the object. + /// Handle to the Vulkan Object to name. + /// Object's name. + template + inline void SetVulkanObjectName(vk::ObjectType objType, T objHandle, const std::string& objName); +#endif /*-----------------------------------------------------------------------*/ /* SETTERS AND GETTERS */ @@ -220,4 +235,4 @@ namespace SHADE }; } -#endif +#include "SHVkLogicalDevice.hpp" diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp new file mode 100644 index 00000000..dd7ff609 --- /dev/null +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp @@ -0,0 +1,46 @@ +/************************************************************************************//*! +\file SHVkLogicalDevice.hpp +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 4, 2022 +\brief Contains implementation of inline and template functions of + SHVkLogicalDevice. + +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 "SHVkLogicalDevice.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Debug Tools */ + /*-----------------------------------------------------------------------------------*/ +#ifdef _DEBUG + template + void SHVkLogicalDevice::SetVulkanObjectName(vk::ObjectType objType, T objHandle, const std::string& objName) + { + if (objName.empty()) + return; + + vk::DebugUtilsObjectNameInfoEXT info; + info.objectType = objType; + info.objectHandle = (uint64_t) static_cast(objHandle); + info.pObjectName = objName.data(); + vkLogicalDevice.setDebugUtilsObjectNameEXT(info); + } +#endif +} + +/*-------------------------------------------------------------------------------------*/ +/* Macro Definitions */ +/*-------------------------------------------------------------------------------------*/ +#ifdef _DEBUG +#define SET_VK_OBJ_NAME(DEVICE, OBJ_TYPE, OBJ_HDL, OBJ_NAME) \ + (DEVICE).SetVulkanObjectName(OBJ_TYPE, OBJ_HDL, OBJ_NAME); +#else +#define SET_VK_OBJ_NAME(DEVICE, OBJ_TYPE, OBJ_HDL, OBJ_NAME) +#endif diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index c2471f79..3f9b6fa2 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -392,20 +392,23 @@ namespace SHADE SHVkUtil::EnsureBufferAndCopyHostVisibleData ( device, drawDataBuffer[frameIndex], drawData.data(), DRAW_DATA_BYTES, - BuffUsage::eIndirectBuffer + BuffUsage::eIndirectBuffer, + "Batch Draw Data Buffer" ); // - Transform Buffer const uint32_t TF_DATA_BYTES = static_cast(transformData.size() * sizeof(SHMatrix)); SHVkUtil::EnsureBufferAndCopyHostVisibleData ( device, transformDataBuffer[frameIndex], transformData.data(), TF_DATA_BYTES, - BuffUsage::eVertexBuffer + BuffUsage::eVertexBuffer, + "Batch Transform Buffer" ); const uint32_t EID_DATA_BYTES = static_cast(instancedIntegerData.size() * sizeof(SHInstancedIntegerData)); SHVkUtil::EnsureBufferAndCopyHostVisibleData ( device, instancedIntegerBuffer[frameIndex], instancedIntegerData.data(), EID_DATA_BYTES, - BuffUsage::eVertexBuffer + BuffUsage::eVertexBuffer, + "Batch Instance Data Buffer" ); // - Material Properties Buffer rebuildMaterialBuffers(frameIndex, descPool); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index c4dc2c8f..48e46a40 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -141,7 +141,8 @@ namespace SHADE 0, vk::BufferUsageFlagBits::eVertexBuffer, VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, - VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT + VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT, + "Debug Draw Non-Persistent Vertex Buffer" ); } // - Persistent Draws @@ -154,7 +155,8 @@ namespace SHADE 0, vk::BufferUsageFlagBits::eVertexBuffer, VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, - VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT + VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT, + "Debug Draw Persistent Vertex Buffer" ); } } diff --git a/SHADE_Engine/src/Graphics/SHVkUtil.cpp b/SHADE_Engine/src/Graphics/SHVkUtil.cpp index 8b21e7d1..a9ac543f 100644 --- a/SHADE_Engine/src/Graphics/SHVkUtil.cpp +++ b/SHADE_Engine/src/Graphics/SHVkUtil.cpp @@ -82,7 +82,7 @@ namespace SHADE } } - void SHVkUtil::EnsureBufferAndCopyData(Handle device, Handle cmdBuffer, Handle& bufferHandle, void* src, uint32_t size, vk::BufferUsageFlagBits usage) + void SHVkUtil::EnsureBufferAndCopyData(Handle device, Handle cmdBuffer, Handle& bufferHandle, void* src, uint32_t size, vk::BufferUsageFlagBits usage, const std::string& name) { if (bufferHandle) { @@ -100,7 +100,8 @@ namespace SHADE size, usage | BuffUsage::eTransferDst, VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, - VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT + VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, + name ); } @@ -108,7 +109,7 @@ namespace SHADE bufferHandle->TransferToDeviceResource(cmdBuffer); } - void SHVkUtil::EnsureBufferAndCopyHostVisibleData(Handle device, Handle& bufferHandle, void* src, uint32_t size, vk::BufferUsageFlagBits usage) + void SHVkUtil::EnsureBufferAndCopyHostVisibleData(Handle device, Handle& bufferHandle, void* src, uint32_t size, vk::BufferUsageFlagBits usage, const std::string& name) { if (bufferHandle) { @@ -126,7 +127,8 @@ namespace SHADE size, usage, VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, - VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT + VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT, + name ); } } diff --git a/SHADE_Engine/src/Graphics/SHVkUtil.h b/SHADE_Engine/src/Graphics/SHVkUtil.h index de700ea5..07e8f02e 100644 --- a/SHADE_Engine/src/Graphics/SHVkUtil.h +++ b/SHADE_Engine/src/Graphics/SHVkUtil.h @@ -54,7 +54,7 @@ namespace SHADE */ /***********************************************************************************/ - static void EnsureBufferAndCopyData(Handle device, Handle cmdBuffer, Handle& bufferHandle, void* src, uint32_t size, vk::BufferUsageFlagBits usage); + static void EnsureBufferAndCopyData(Handle device, Handle cmdBuffer, Handle& bufferHandle, void* src, uint32_t size, vk::BufferUsageFlagBits usage, const std::string& name = ""); /***********************************************************************************/ /*! @@ -80,7 +80,7 @@ namespace SHADE */ /***********************************************************************************/ - static void EnsureBufferAndCopyHostVisibleData(Handle device, Handle& bufferHandle, void* src, uint32_t size, vk::BufferUsageFlagBits usage); + static void EnsureBufferAndCopyHostVisibleData(Handle device, Handle& bufferHandle, void* src, uint32_t size, vk::BufferUsageFlagBits usage, const std::string& name = ""); static BindingAndSetHash GenBindingSetHash (uint32_t set, uint32_t binding) noexcept; }; From ab17d577560706321699e6cd7424e3c58455c6e2 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Fri, 4 Nov 2022 23:32:29 +0800 Subject: [PATCH 079/113] Asset browser now recurses down subassets --- Assets/Materials/BagMaterial.shmat | 8 +++ Assets/Materials/BagMaterial.shmat.shmeta | 3 + Assets/Scenes/M2Scene.shade | 27 +++++-- Assets/Scenes/Scene2.shade | 72 +++++++++++++++++++ Assets/Scenes/Scene2.shade.shmeta | 3 + .../AssetBrowser/SHAssetBrowser.cpp | 57 ++++++++++----- .../AssetBrowser/SHAssetBrowser.h | 2 +- 7 files changed, 147 insertions(+), 25 deletions(-) create mode 100644 Assets/Materials/BagMaterial.shmat create mode 100644 Assets/Materials/BagMaterial.shmat.shmeta create mode 100644 Assets/Scenes/Scene2.shade create mode 100644 Assets/Scenes/Scene2.shade.shmeta diff --git a/Assets/Materials/BagMaterial.shmat b/Assets/Materials/BagMaterial.shmat new file mode 100644 index 00000000..e538b834 --- /dev/null +++ b/Assets/Materials/BagMaterial.shmat @@ -0,0 +1,8 @@ +- VertexShader: 39210065 + 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/BagMaterial.shmat.shmeta b/Assets/Materials/BagMaterial.shmat.shmeta new file mode 100644 index 00000000..b903a854 --- /dev/null +++ b/Assets/Materials/BagMaterial.shmat.shmeta @@ -0,0 +1,3 @@ +Name: BagMaterial +ID: 123745521 +Type: 7 diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade index 38c0a523..60dbb5f9 100644 --- a/Assets/Scenes/M2Scene.shade +++ b/Assets/Scenes/M2Scene.shade @@ -50,7 +50,7 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.0170898438, y: 0.00048828125, z: 0.0170898438} + Half Extents: {x: 0.00427246094, y: 0.000122070312, z: 0.00427246094} Friction: 0.400000006 Bounciness: 0 Density: 1 @@ -59,7 +59,7 @@ - EID: 2 Name: Player IsActive: true - NumberOfChildren: 2 + NumberOfChildren: 3 Components: Transform Component: Translate: {x: -3.06177855, y: -2, z: -5} @@ -85,7 +85,7 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.0009765625, y: 0.0009765625, z: 0.0009765625} + Half Extents: {x: 0.000244140625, y: 0.000244140625, z: 0.000244140625} Friction: 0.400000006 Bounciness: 0 Density: 1 @@ -111,6 +111,19 @@ Rotate: {x: 0, y: 0, z: 0} Scale: {x: 1, y: 1, z: 1} Scripts: ~ +- EID: 9 + 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} + Renderable Component: + Mesh: 144838771 + Material: 123745521 + Scripts: ~ - EID: 5 Name: item IsActive: true @@ -121,7 +134,7 @@ Rotate: {x: 0, y: 0, z: 0} Scale: {x: 2, y: 2, z: 2} Renderable Component: - Mesh: 149697411 + Mesh: 144838771 Material: 126974645 RigidBody Component: Type: Dynamic @@ -140,14 +153,14 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.0009765625, y: 0.0009765625, z: 0.0009765625} + Half Extents: {x: 0.000244140625, y: 0.000244140625, z: 0.000244140625} Friction: 0.400000006 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0.5, z: 0} - Is Trigger: true Type: Box - Half Extents: {x: 0.001953125, y: 0.001953125, z: 0.001953125} + Half Extents: {x: 0.00048828125, y: 0.00048828125, z: 0.00048828125} Friction: 0.400000006 Bounciness: 0 Density: 1 @@ -182,7 +195,7 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.0009765625, y: 0.0009765625, z: 0.0009765625} + Half Extents: {x: 0.000244140625, y: 0.000244140625, z: 0.000244140625} Friction: 0.400000006 Bounciness: 0 Density: 1 diff --git a/Assets/Scenes/Scene2.shade b/Assets/Scenes/Scene2.shade new file mode 100644 index 00000000..2f38a933 --- /dev/null +++ b/Assets/Scenes/Scene2.shade @@ -0,0 +1,72 @@ +- EID: 0 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: 0.304069757, z: 1.73034382} + Rotate: {x: 0, y: 0, z: 0} + Scale: {x: 1, y: 1, z: 1} + Camera Component: + Position: {x: 0, y: 0.304069757, z: 1.73034382} + Pitch: 0 + Yaw: 0 + Roll: 0 + Width: 1200 + Height: 1080 + Near: 0.00999999978 + Far: 10000 + Perspective: true + Scripts: ~ +- EID: 1 + Name: Raccoon + IsActive: true + NumberOfChildren: 1 + Components: + Transform Component: + Translate: {x: 0, y: 0, z: 0} + Rotate: {x: 0, y: 0, z: 0} + Scale: {x: 1, y: 1, z: 1} + Renderable Component: + Mesh: 149697411 + Material: 126974645 + Scripts: ~ +- EID: 3 + Name: Bag + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0.006237939, y: -0.000393368304, z: 0} + Rotate: {x: -0, y: 2.79945588, z: 0} + Scale: {x: 1.0000881, y: 1, z: 1.0000881} + Renderable Component: + Mesh: 144838771 + Material: 123745521 + Scripts: ~ +- EID: 2 + Name: DirectionalLight + IsActive: true + NumberOfChildren: 0 + Components: + Light Component: + Position: {x: 0, y: 0, z: 0} + Type: Directional + Direction: {x: 0, y: 0, z: 1} + Color: {x: 1, y: 1, z: 1, w: 1} + Layer: 4294967295 + Strength: 0 + Scripts: ~ +- EID: 4 + Name: AmbientLight + IsActive: true + NumberOfChildren: 0 + Components: + Light Component: + Position: {x: 0, y: 0, z: 0} + Type: Ambient + Direction: {x: 0, y: 0, z: 1} + Color: {x: 1, y: 1, z: 1, w: 1} + Layer: 4294967295 + Strength: 0.600000024 + Scripts: ~ \ No newline at end of file diff --git a/Assets/Scenes/Scene2.shade.shmeta b/Assets/Scenes/Scene2.shade.shmeta new file mode 100644 index 00000000..bbcbc66c --- /dev/null +++ b/Assets/Scenes/Scene2.shade.shmeta @@ -0,0 +1,3 @@ +Name: Scene2 +ID: 87285316 +Type: 5 diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp index d875d743..37b8ecd4 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.cpp @@ -102,8 +102,10 @@ namespace SHADE } for (auto const& file : files) { + if(file.assetMeta == nullptr) + continue; const float horizontalLineSize = 25.0f; - const ImRect childRect = DrawFile(file); + const ImRect childRect = DrawFile(file.assetMeta); const float midPoint = (childRect.Min.y + childRect.Max.y) * 0.5f; drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1); vertLineEnd.y = midPoint; @@ -146,47 +148,48 @@ namespace SHADE //} } - ImRect SHAssetBrowser::DrawFile(SHFile const& file) noexcept + ImRect SHAssetBrowser::DrawFile(SHAsset const* const asset) noexcept { - if (file.assetMeta == nullptr) + if (asset == nullptr) return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); - const bool isSelected = std::ranges::find(selectedAssets, file.assetMeta->id) != selectedAssets.end(); - ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf; + const bool isSelected = std::ranges::find(selectedAssets, asset->id) != selectedAssets.end(); + ImGuiTreeNodeFlags flags = (!asset->subAssets.empty()) ? ImGuiTreeNodeFlags_OpenOnArrow : ImGuiTreeNodeFlags_Leaf; if (isSelected) flags |= ImGuiTreeNodeFlags_Selected; std::string icon{}; - switch (file.assetMeta->type) + switch (asset->type) { case AssetType::INVALID: break; case AssetType::SHADER: icon = ICON_FA_FILE_CODE; break; case AssetType::SHADER_BUILT_IN: icon = ICON_FA_FILE_CODE; break; case AssetType::TEXTURE: icon = ICON_FA_IMAGES; break; - case AssetType::MESH: icon = ICON_FA_CUBES; break; + case AssetType::MODEL: icon = ICON_FA_CUBES_STACKED; break; case AssetType::SCENE: icon = ICON_MD_IMAGE; break; case AssetType::PREFAB: icon = ICON_FA_BOX_OPEN; break; case AssetType::MATERIAL: break; + case AssetType::MESH: icon = ICON_FA_CUBES; break; case AssetType::MAX_COUNT: break; default:; } - - ImGui::TreeNodeEx(file.assetMeta, flags, "%s %s", icon.data(), file.assetMeta->name.data()); + + bool const isOpen = ImGui::TreeNodeEx(asset, flags, "%s %s", icon.data(), asset->name.data()); const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); if (SHDragDrop::BeginSource()) { - auto id = file.assetMeta->id; - ImGui::Text("Moving Asset: %s [%zu]", file.name.data(), file.assetMeta->id); + auto id = asset->id; + ImGui::Text("Moving Asset: %s [%zu]", asset->name.data(), asset->id); SHDragDrop::SetPayload(SHDragDrop::DRAG_RESOURCE, &id); SHDragDrop::EndSource(); } if (ImGui::IsItemClicked()) { selectedAssets.clear(); - selectedAssets.push_back(file.assetMeta->id); + selectedAssets.push_back(asset->id); } if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - switch (file.assetMeta->type) + switch (asset->type) { case AssetType::INVALID: break; case AssetType::SHADER: break; @@ -196,14 +199,14 @@ namespace SHADE case AssetType::SCENE: if(auto editor = SHSystemManager::GetSystem()) { - editor->LoadScene(file.assetMeta->id); + editor->LoadScene(asset->id); } break; case AssetType::PREFAB: break; case AssetType::MATERIAL: if (auto matInspector = SHEditorWindowManager::GetEditorWindow()) { - matInspector->OpenMaterial(file.assetMeta->id); + matInspector->OpenMaterial(asset->id); } break; case AssetType::MAX_COUNT: break; @@ -211,7 +214,27 @@ namespace SHADE } } - ImGui::TreePop(); + + //TODO: Combine Draw asset and Draw Folder recursive drawing + const ImColor treeLineColor = ImGui::GetColorU32(ImGuiCol_CheckMark); + const float horizontalOffset = 0.0f; + ImDrawList* drawList = ImGui::GetWindowDrawList(); + ImVec2 vertLineStart = ImGui::GetCursorScreenPos(); + vertLineStart.x += horizontalOffset; + ImVec2 vertLineEnd = vertLineStart; + if(isOpen) + { + for(auto const& subAsset : asset->subAssets) + { + const float horizontalLineSize = 25.0f; + const ImRect childRect = DrawFile(subAsset); + const float midPoint = (childRect.Min.y + childRect.Max.y) * 0.5f; + drawList->AddLine(ImVec2(vertLineStart.x, midPoint), ImVec2(vertLineStart.x + horizontalLineSize, midPoint), treeLineColor, 1); + vertLineEnd.y = midPoint; + } + drawList->AddLine(vertLineStart, vertLineEnd, treeLineColor, 1); + ImGui::TreePop(); + } return nodeRect; } @@ -222,7 +245,7 @@ namespace SHADE auto& path = std::get<0>(assetBeingCreated.value()); auto& type = std::get<1>(assetBeingCreated.value()); auto& assetName = std::get<2>(assetBeingCreated.value()); - if (ImGui::InputText("##newAssetname", &assetName, ImGuiInputTextFlags_EnterReturnsTrue)) + if (ImGui::InputText("##newAssetName", &assetName, ImGuiInputTextFlags_EnterReturnsTrue)) { AssetID assetId = SHAssetManager::CreateNewAsset(type, assetName); if (auto matInspector = SHEditorWindowManager::GetEditorWindow()) diff --git a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h index eec40262..00023ebe 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h +++ b/SHADE_Engine/src/Editor/EditorWindow/AssetBrowser/SHAssetBrowser.h @@ -21,7 +21,7 @@ namespace SHADE void DrawMenuBar(); ImRect RecursivelyDrawTree(FolderPointer folder); void DrawCurrentFolder(); - ImRect DrawFile(SHFile const& file) noexcept; + ImRect DrawFile(SHAsset const* const asset) noexcept; void DrawAssetBeingCreated() noexcept; FolderPointer rootFolder, prevFolder, currentFolder; From 40422a6d23c44c35221473ebdd017d6623daa10d Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sat, 5 Nov 2022 00:00:45 +0800 Subject: [PATCH 080/113] Added built-in primitives --- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 66 ++++++++---- .../MiddleEnd/Interface/SHGraphicsSystem.h | 102 +++++++++++++----- 2 files changed, 119 insertions(+), 49 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 43de46bb..573c7f7f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -41,6 +41,7 @@ of DigiPen Institute of Technology is prohibited. #include "Resource/SHResourceManager.h" #include "Graphics/SHVkUtil.h" #include "Graphics/RenderGraph/SHRenderGraphNodeCompute.h" +#include "../Meshes/SHPrimitiveGenerator.h" namespace SHADE { @@ -259,7 +260,6 @@ namespace SHADE // Generate world render graph worldRenderGraph->Generate(); - /*-----------------------------------------------------------------------*/ /* BIND RENDER GRAPH TO RENDERER */ /*-----------------------------------------------------------------------*/ @@ -269,17 +269,6 @@ namespace SHADE worldRenderer->SetCameraDirector(cameraSystem->CreateDirector()); - // Create default materials - std::array defaultTexture = { 255, 255, 255, 255 }; - std::vector mipOffsets{}; - mipOffsets.push_back(0); - auto tex = AddTexture(4, defaultTexture.data(), 1, 1, SHTexture::TextureFormat::eR8G8B8A8Unorm, mipOffsets); - BuildTextures(); - - defaultMaterial = AddMaterial(defaultVertShader, defaultFragShader, gBufferSubpass); - defaultMaterial->SetProperty("data.textureIndex", tex->TextureArrayIndex); - - // Create debug draw pipeline debugDrawPipeline = createDebugDrawPipeline(debugDrawNode->GetRenderpass(), debugDrawSubpass); debugDrawDepthPipeline = createDebugDrawPipeline(debugDrawNodeDepth->GetRenderpass(), debugDrawDepthSubpass); @@ -321,7 +310,29 @@ namespace SHADE lightingSubSystem = resourceManager.Create(); lightingSubSystem->Init(device, descPool); + } + void SHGraphicsSystem::InitBuiltInResources(void) + { + // Create default texture + std::array defaultTextureData = { 255, 255, 255, 255 }; + std::vector mipOffsets{}; + mipOffsets.push_back(0); + defaultTexture = AddTexture(4, defaultTextureData.data(), 1, 1, SHTexture::TextureFormat::eR8G8B8A8Unorm, mipOffsets); + BuildTextures(); + + // Create default meshes + primitiveMeshes[static_cast(PrimitiveType::Cube)] = SHPrimitiveGenerator::Cube(meshLibrary); + primitiveMeshes[static_cast(PrimitiveType::Sphere)] = SHPrimitiveGenerator::Sphere(meshLibrary); + BuildMeshBuffers(); + + // Create default materials + defaultMaterial = AddMaterial + ( + defaultVertShader, defaultFragShader, + worldRenderGraph->GetNode("G-Buffer")->GetSubpass("G-Buffer Write") + ); + defaultMaterial->SetProperty("data.textureIndex", defaultTexture->TextureArrayIndex); } #ifdef SHEDITOR @@ -364,8 +375,7 @@ namespace SHADE InitBoilerplate(); InitMiddleEnd(); InitSubsystems(); - - + InitBuiltInResources(); } void SHGraphicsSystem::Exit(void) @@ -690,7 +700,7 @@ namespace SHADE SHADE::Handle SHGraphicsSystem::AddMaterialInstanceCopy(Handle materialInst) { - return resourceManager.Create(materialInst->GetBaseMaterial()); + return resourceManager.Create(materialInst->GetBaseMaterial()); } void SHGraphicsSystem::RemoveMaterialInstance(Handle materialInstance) @@ -703,26 +713,38 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ 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) { - return meshLibrary.AddMesh(vertexCount, positions, texCoords, tangents, normals, indexCount, indices); + return meshLibrary.AddMesh(vertexCount, positions, texCoords, tangents, normals, indexCount, indices); } void SHGraphicsSystem::RemoveMesh(Handle mesh) { - meshLibrary.RemoveMesh(mesh); + meshLibrary.RemoveMesh(mesh); } void SHGraphicsSystem::BuildMeshBuffers() { transferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); device->WaitIdle(); - transferCmdBuffer->BeginRecording(); - meshLibrary.BuildBuffers(device, transferCmdBuffer); - transferCmdBuffer->EndRecording(); - graphicsQueue->SubmitCommandBuffer({ transferCmdBuffer }); + transferCmdBuffer->BeginRecording(); + meshLibrary.BuildBuffers(device, transferCmdBuffer); + transferCmdBuffer->EndRecording(); + graphicsQueue->SubmitCommandBuffer({ transferCmdBuffer }); device->WaitIdle(); transferCmdBuffer.Free(); transferCmdBuffer = {}; } + Handle SHGraphicsSystem::GetMeshPrimitive(PrimitiveType type) const noexcept + { + switch (type) + { + case PrimitiveType::Cube: + case PrimitiveType::Sphere: + return primitiveMeshes[static_cast(type)]; + default: + return {}; + } + } + /*---------------------------------------------------------------------------------*/ /* Texture Registration Functions */ /*---------------------------------------------------------------------------------*/ @@ -809,7 +831,7 @@ namespace SHADE void SHGraphicsSystem::BatcherDispatcherRoutine::Execute(double) noexcept { - auto& renderables = SHComponentManager::GetDense(); + auto& renderables = SHComponentManager::GetDense(); for (auto& renderable : renderables) { if (!renderable.HasChanged()) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 90299444..a5a5ada0 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -58,6 +58,19 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ /* Type Definitions */ /*---------------------------------------------------------------------------------*/ + /***********************************************************************************/ + /*! + \brief + Type of built-in primitive meshes that are available. + */ + /***********************************************************************************/ + enum class PrimitiveType + { + Cube, + Sphere + }; + static constexpr int MAX_PRIMITIVE_TYPES = 2; + /***********************************************************************************/ /*! \brief @@ -72,6 +85,7 @@ namespace SHADE void InitSceneRenderGraph (void) noexcept; void InitMiddleEnd (void) noexcept; void InitSubsystems (void) noexcept; + void InitBuiltInResources (void); #ifdef SHEDITOR void InitEditorRenderGraph (void) noexcept; @@ -81,25 +95,25 @@ namespace SHADE class SH_API BeginRoutine final : public SHSystemRoutine { public: - BeginRoutine(); + BeginRoutine(); virtual void Execute(double dt) noexcept override final; }; class SH_API RenderRoutine final : public SHSystemRoutine { public: - RenderRoutine(); + RenderRoutine(); virtual void Execute(double dt) noexcept override final; }; class SH_API EndRoutine final : public SHSystemRoutine { public: - EndRoutine(); + EndRoutine(); virtual void Execute(double dt) noexcept override final; }; class SH_API BatcherDispatcherRoutine final : public SHSystemRoutine { public: - BatcherDispatcherRoutine(); + BatcherDispatcherRoutine(); virtual void Execute(double dt) noexcept override final; }; @@ -152,34 +166,34 @@ namespace SHADE /*******************************************************************************/ /*! - \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. + \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. + 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 - indices. + Pointer to the first in a contiguous array of uint32_ts that define mesh + indices. - \return - Handle to the created Mesh. This is not valid to be used until a call to - BuildBuffers(). + \return + Handle to the created Mesh. This is not valid to be used until a call to + BuildBuffers(). */ /*******************************************************************************/ @@ -188,9 +202,9 @@ namespace SHADE /*! \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. + 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. @@ -207,6 +221,21 @@ namespace SHADE */ /***************************************************************************/ void BuildMeshBuffers(); + /*******************************************************************************/ + /*! + + \brief + Retrieves the built-in mesh specified. + + \param type + Type of built-in mesh to retrieve. + + \returns + Handle to the mesh that was specfied. However, if an invalid type is specified, + a null Handle will be returned. + */ + /*******************************************************************************/ + Handle GetMeshPrimitive(PrimitiveType type) const noexcept; /*-----------------------------------------------------------------------------*/ /* Texture Registration Functions */ @@ -278,6 +307,18 @@ namespace SHADE */ /***************************************************************************/ Handle GetTextureHandle(SHTexture::Index textureId) const; + /***************************************************************************/ + /*! + * + \brief + Retrieves the handle to the default texture. A white 1x1 texture. + + \returns + Handle to the default texture. + + */ + /***************************************************************************/ + Handle GetDefaultTexture() const noexcept { return defaultTexture; } void PrepareResize(uint32_t newWidth, uint32_t newHeight) noexcept; void HandleResize(void) noexcept; @@ -378,6 +419,13 @@ namespace SHADE Handle debugDrawPipeline; Handle debugDrawDepthPipeline; + // Built-In Textures + Handle defaultTexture; + + // Built-In Meshes + std::array, MAX_PRIMITIVE_TYPES> primitiveMeshes; + + // Render Graphs Handle worldRenderGraph; // Sub systems From 7bfe459c02638d86674624078550fc96d4e08508 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Sat, 5 Nov 2022 00:13:24 +0800 Subject: [PATCH 081/113] Called assimp git pull from root bat directly instead of calling bat inside dependency --- Dependencies.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dependencies.bat b/Dependencies.bat index dcd4750e..44185ff8 100644 --- a/Dependencies.bat +++ b/Dependencies.bat @@ -59,7 +59,7 @@ if %_e%==3 (goto :done) else (goto :ModelCompiler) echo -----------------------ModelCompiler---------------------------- rmdir "Dependencies/ModelCompiler" /S /Q git clone https://github.com/SHADE-DP/ModelCompiler.git "Dependencies/ModelCompiler" -Dependencies/ModelCompiler/Dependencies.bat +git clone https://github.com/SHADE-DP/assimp.git "Dependencies/ModelCompiler/Dependencies/assimp" if %_e%==4 (goto :done) else (goto :spdlog) @REM :ktx From 06afd384af2cb63da4373947971ee84d8dffac31 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sat, 5 Nov 2022 14:56:52 +0800 Subject: [PATCH 082/113] Scenes are now reloaded and scripts are no longer lost when building scripts --- .../src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index af2d5517..d1e359b6 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -120,12 +120,16 @@ namespace SHADE if (ImGui::Selectable("Build Scripts - Debug")) { auto* scriptEngine = static_cast(SHSystemManager::GetSystem()); + SHSerialization::SerializeSceneToFile(SHSceneManager::GetCurrentSceneAssetID()); scriptEngine->BuildScriptAssembly(true, true); + SHSceneManager::RestartScene(SHSceneManager::GetCurrentSceneAssetID()); } if (ImGui::Selectable("Build Scripts - Release")) { auto* scriptEngine = static_cast(SHSystemManager::GetSystem()); - scriptEngine->BuildScriptAssembly(false, true); + SHSerialization::SerializeSceneToFile(SHSceneManager::GetCurrentSceneAssetID()); + scriptEngine->BuildScriptAssembly(false, true); + SHSceneManager::RestartScene(SHSceneManager::GetCurrentSceneAssetID()); } ImGui::EndMenu(); } From 04e2c255b0d6c8f3baa82ca7db7ade914906edc5 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sat, 5 Nov 2022 15:34:56 +0800 Subject: [PATCH 083/113] Build script options are now disabled when in play or pause mode --- .../src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index d1e359b6..c18f0c8c 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -117,6 +117,7 @@ namespace SHADE auto* scriptEngine = static_cast(SHSystemManager::GetSystem()); scriptEngine->GenerateScriptsCsProjFile(); } + ImGui::BeginDisabled(SHSystemManager::GetSystem()->editorState != SHEditor::State::STOP); if (ImGui::Selectable("Build Scripts - Debug")) { auto* scriptEngine = static_cast(SHSystemManager::GetSystem()); @@ -131,6 +132,7 @@ namespace SHADE scriptEngine->BuildScriptAssembly(false, true); SHSceneManager::RestartScene(SHSceneManager::GetCurrentSceneAssetID()); } + ImGui::EndDisabled(); ImGui::EndMenu(); } From b9996c7b51bf73f7108c362bf5d7b15583c4b4bb Mon Sep 17 00:00:00 2001 From: Glence Date: Sat, 5 Nov 2022 17:44:34 +0800 Subject: [PATCH 084/113] made rb a properties --- TempScriptsFolder/PlayerController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TempScriptsFolder/PlayerController.cs b/TempScriptsFolder/PlayerController.cs index bcdf24d1..86ba7c98 100644 --- a/TempScriptsFolder/PlayerController.cs +++ b/TempScriptsFolder/PlayerController.cs @@ -15,7 +15,7 @@ public class PlayerController : Script TOTAL } - public RigidBody rb; + public RigidBody rb { get; set; } private Transform tranform; private Camera cam; private PickAndThrow pat; From de38b29f250f3628a6979dfb583a9b86724975b8 Mon Sep 17 00:00:00 2001 From: Sri Sham Haran Date: Sat, 5 Nov 2022 18:15:18 +0800 Subject: [PATCH 085/113] small fixes for ms2 submission --- Assets/Scenes/M2Scene.shade | 58 ++++++++++++++----- .../src/Serialization/SHYAMLConverters.h | 2 +- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade index 60dbb5f9..443d4a87 100644 --- a/Assets/Scenes/M2Scene.shade +++ b/Assets/Scenes/M2Scene.shade @@ -50,7 +50,7 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.00427246094, y: 0.000122070312, z: 0.00427246094} + Half Extents: {x: 24.7399445, y: 0.25, z: 8.75} Friction: 0.400000006 Bounciness: 0 Density: 1 @@ -85,12 +85,28 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.000244140625, y: 0.000244140625, z: 0.000244140625} + Half Extents: {x: 1, y: 1, z: 1} Friction: 0.400000006 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0.5, z: 0} - Scripts: ~ + Scripts: + - Type: PlayerController + drag: 2 + currentState: 0 + maxMoveVel: 2 + moveForce: 50 + sprintMultiplier: 2 + rotationFactorPerFrame: 1 + maxJumpHeight: 4 + maxJumpTime: 0.75 + fallMultipler: 2 + lightMultiper: 0.75 + mediumMultiper: 0.5 + heavyMultiper: 0.25 + - Type: PickAndThrow + throwForce: [200, 300, 200] + item: 5 - EID: 3 Name: Default IsActive: true @@ -110,7 +126,12 @@ Translate: {x: 0, y: 0, z: 0} Rotate: {x: 0, y: 0, z: 0} Scale: {x: 1, y: 1, z: 1} - Scripts: ~ + Scripts: + - Type: SHADE_Scripting.ThirdPersonCamera + armLength: 2 + turnSpeedPitch: 0.300000012 + turnSpeedYaw: 0.5 + pitchClamp: 45 - EID: 9 Name: Default IsActive: true @@ -135,7 +156,7 @@ Scale: {x: 2, y: 2, z: 2} Renderable Component: Mesh: 144838771 - Material: 126974645 + Material: 123745521 RigidBody Component: Type: Dynamic Mass: 1 @@ -153,19 +174,21 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.000244140625, y: 0.000244140625, z: 0.000244140625} + Half Extents: {x: 1, y: 1, z: 1} Friction: 0.400000006 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0.5, z: 0} - Is Trigger: true Type: Box - Half Extents: {x: 0.00048828125, y: 0.00048828125, z: 0.00048828125} + Half Extents: {x: 2, y: 2, z: 2} Friction: 0.400000006 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0.5, z: 0} - Scripts: ~ + Scripts: + - Type: Item + currCategory: 0 - EID: 6 Name: AI IsActive: true @@ -174,7 +197,7 @@ Transform Component: Translate: {x: -8, y: -2, z: 2.5} Rotate: {x: 0, y: 0, z: 0} - Scale: {x: 2, y: 2, z: 2} + Scale: {x: 1, y: 1, z: 1} Renderable Component: Mesh: 149697411 Material: 126974645 @@ -195,21 +218,28 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.000244140625, y: 0.000244140625, z: 0.000244140625} + Half Extents: {x: 0.5, y: 0.5, z: 0.5} Friction: 0.400000006 Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0.5, z: 0} - Scripts: ~ + Scripts: + - Type: AIPrototype + movementForceMultiplier: 100 + patrolSpeed: 0.400000006 + chaseSpeed: 0.800000012 + distanceToCapture: 1.20000005 + distanceToStartChase: 2 + distanceToEndChase: 2.5 - EID: 7 Name: Default IsActive: true NumberOfChildren: 0 Components: Transform Component: - Translate: {x: 3, y: -1, z: -1} - Rotate: {x: 0, y: 0, z: 0} - Scale: {x: 5, y: 5, z: 5} + Translate: {x: 0, y: -16.8647861, z: -14.039052} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 28.1434975, y: 28.1434975, z: 28.1434975} Renderable Component: Mesh: 149697411 Material: 126974645 diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index af0b280b..d4b97244 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -168,7 +168,7 @@ namespace YAML case SHCollider::Type::BOX: { if (node[HalfExtents].IsDefined()) - rhs.SetBoundingBox(node[HalfExtents].as()); + rhs.SetBoundingBox(node[HalfExtents].as() * 2.0f); } break; case SHCollider::Type::SPHERE: From 4bd9f0817a9b59b545908857f4feeea1ce5d3adf Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 7 Nov 2022 12:55:18 +0800 Subject: [PATCH 086/113] Buffers are now tagged as buffers for debug labels --- SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp | 3 ++- SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp | 2 +- SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp index 63f580b9..37dd2c74 100644 --- a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp @@ -410,7 +410,8 @@ namespace SHADE auto [tempBuffer, allocInfo] = createBuffer(sizeStored); vkBuffer = tempBuffer; - SET_VK_OBJ_NAME(*device, vk::ObjectType::eBuffer, vkBuffer, name); + if (!name.empty()) + SET_VK_OBJ_NAME(device, vk::ObjectType::eBuffer, vkBuffer, "[Buffer] " + name); // This probably means that a HOST_CACHED memory type is used on allocation if (allocFlags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp index 35c104ee..20332f1d 100644 --- a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp @@ -220,7 +220,7 @@ namespace SHADE else { SHVulkanDebugUtil::ReportVkSuccess("Successfully created a Logical Device. "); - SET_VK_OBJ_NAME((*this), vk::ObjectType::eDevice, vkLogicalDevice, "Logical Device"); + SET_VK_OBJ_NAME(this, vk::ObjectType::eDevice, vkLogicalDevice, "Logical Device"); } InitializeVMA(); diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp index dd7ff609..d72ab09e 100644 --- a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp @@ -40,7 +40,7 @@ namespace SHADE /*-------------------------------------------------------------------------------------*/ #ifdef _DEBUG #define SET_VK_OBJ_NAME(DEVICE, OBJ_TYPE, OBJ_HDL, OBJ_NAME) \ - (DEVICE).SetVulkanObjectName(OBJ_TYPE, OBJ_HDL, OBJ_NAME); + DEVICE->SetVulkanObjectName(OBJ_TYPE, OBJ_HDL, OBJ_NAME); #else #define SET_VK_OBJ_NAME(DEVICE, OBJ_TYPE, OBJ_HDL, OBJ_NAME) #endif From 782db1e2a428aed9f794c3b6aed29c253e145e58 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 7 Nov 2022 13:22:16 +0800 Subject: [PATCH 087/113] Added debug labels for Renderpasses --- SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp | 6 +++--- .../src/Graphics/RenderGraph/SHRenderGraphNode.cpp | 7 +++++-- SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h | 5 ++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp index 66dc6a0f..9c4e0d65 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp @@ -471,9 +471,9 @@ namespace SHADE } } - nodes.emplace_back(resourceManager->Create(renderGraphStorage, std::move(descInitParams), std::move(predecessors))); - nodeIndexing.emplace(nodeName, static_cast(nodes.size()) - 1u); - return nodes.at(nodeIndexing[nodeName]); + auto node = nodes.emplace_back(resourceManager->Create(nodeName, renderGraphStorage, std::move(descInitParams), std::move(predecessors))); + nodeIndexing.emplace(std::move(nodeName), static_cast(nodes.size()) - 1u); + return node; } /***************************************************************************/ diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp index 12f0e246..9aa42c0a 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp @@ -25,6 +25,7 @@ namespace SHADE void SHRenderGraphNode::CreateRenderpass(void) noexcept { renderpass = graphStorage->logicalDevice->CreateRenderpass(attachmentDescriptions, spDescs, spDeps); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eRenderPass, renderpass->GetVkRenderpass(), "[RenderPass] " + name); } /***************************************************************************/ @@ -116,7 +117,7 @@ namespace SHADE */ /***************************************************************************/ - SHRenderGraphNode::SHRenderGraphNode(Handle renderGraphStorage, std::vector attDescInitParams, std::vector> predecessors) noexcept + SHRenderGraphNode::SHRenderGraphNode(std::string nodeName, Handle renderGraphStorage, std::vector attDescInitParams, std::vector> predecessors) noexcept : graphStorage{ renderGraphStorage} , renderpass{} , framebuffers{} @@ -128,6 +129,7 @@ namespace SHADE , executed{ false } , configured{ false } , nodeComputes{} + , name { std::move(nodeName) } { // pipeline library initialization pipelineLibrary.Init(graphStorage->logicalDevice); @@ -189,6 +191,7 @@ namespace SHADE , spDescs{ std::move(rhs.spDescs) } , spDeps{ std::move(rhs.spDeps) } , nodeComputes{ std::move(rhs.nodeComputes) } + , name { std::move(rhs.name) } { rhs.renderpass = {}; @@ -213,7 +216,7 @@ namespace SHADE spDescs = std::move(rhs.spDescs); spDeps = std::move(rhs.spDeps); nodeComputes = std::move(rhs.nodeComputes); - + name = std::move(rhs.name); rhs.renderpass = {}; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h index 695d1c31..33868f5e 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h @@ -78,8 +78,11 @@ namespace SHADE //! Whether or not the node has been configured already or not bool configured; + //! Manages batching for this RenderPass SHBatcher batcher; + //! Name of this node + std::string name; /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER FUNCTIONS */ @@ -92,7 +95,7 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* CTORS AND DTORS */ /*-----------------------------------------------------------------------*/ - SHRenderGraphNode(Handle renderGraphStorage, std::vector attDescInitParams, std::vector> predecessors) noexcept; + SHRenderGraphNode(std::string nodeName, Handle renderGraphStorage, std::vector attDescInitParams, std::vector> predecessors) noexcept; SHRenderGraphNode(SHRenderGraphNode&& rhs) noexcept; SHRenderGraphNode& operator= (SHRenderGraphNode&& rhs) noexcept; From fb37742ee0e31c8e291a46805750279c0b81a7d8 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 7 Nov 2022 13:28:49 +0800 Subject: [PATCH 088/113] Staging Buffers are now labeled --- SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp index 37dd2c74..803c53be 100644 --- a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp @@ -220,6 +220,7 @@ namespace SHADE // then assign it to the hpp version stagingBuffer = tempBuffer; + SET_VK_OBJ_NAME(device, vk::ObjectType::eBuffer, stagingBuffer, "[Buffer] Staging - " + name); // Just map, copy then unmap void* stagingBufferMappedPtr = nullptr; From 0bda3d515188bd81b810ae11bf3c8d73e0b3e0fe Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 7 Nov 2022 14:08:19 +0800 Subject: [PATCH 089/113] Added labels for resources of Mesh and Texture libraries --- .../src/Graphics/Devices/SHVkLogicalDevice.h | 14 ++++++++++++-- .../src/Graphics/Devices/SHVkLogicalDevice.hpp | 15 +++++++++++++++ .../MiddleEnd/Interface/SHMeshLibrary.cpp | 15 ++++++++++----- .../MiddleEnd/Textures/SHTextureLibrary.cpp | 4 ++++ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h index 827f12b3..158c20b2 100644 --- a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h @@ -208,8 +208,8 @@ namespace SHADE /*-----------------------------------------------------------------------*/ #ifdef _DEBUG /// - /// Sets a Vulkan object's name for debugging purposes. This function will not be - /// compiled outsied of Debug configurations. Hence, it is advised to use provided + /// Sets a Vulkan HPP object's name for debugging purposes. This function will not be + /// compiled outside of Debug configurations. Hence, it is advised to use provided /// macro function SET_VK_OBJ_NAME() instead of using this function directly. /// /// Type of the object. @@ -217,6 +217,16 @@ namespace SHADE /// Object's name. template inline void SetVulkanObjectName(vk::ObjectType objType, T objHandle, const std::string& objName); + /// + /// Sets a Vulkan object's name for debugging purposes. This function will not be + /// compiled outside of Debug configurations. Hence, it is advised to use provided + /// macro function SET_VK_OBJ_NAME_VK() instead of using this function directly. + /// + /// Type of the object. + /// Handle to the Vulkan Object to name. + /// Object's name. + template + inline void SetVulkanObjectNameVk(vk::ObjectType objType, T objVkHandle, const std::string& objName); #endif /*-----------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp index d72ab09e..e8de27fb 100644 --- a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.hpp @@ -32,6 +32,18 @@ namespace SHADE info.pObjectName = objName.data(); vkLogicalDevice.setDebugUtilsObjectNameEXT(info); } + template + void SHVkLogicalDevice::SetVulkanObjectNameVk(vk::ObjectType objType, T objVkHandle, const std::string& objName) + { + if (objName.empty()) + return; + + vk::DebugUtilsObjectNameInfoEXT info; + info.objectType = objType; + info.objectHandle = (uint64_t) objVkHandle; + info.pObjectName = objName.data(); + vkLogicalDevice.setDebugUtilsObjectNameEXT(info); + } #endif } @@ -41,6 +53,9 @@ namespace SHADE #ifdef _DEBUG #define SET_VK_OBJ_NAME(DEVICE, OBJ_TYPE, OBJ_HDL, OBJ_NAME) \ DEVICE->SetVulkanObjectName(OBJ_TYPE, OBJ_HDL, OBJ_NAME); +#define SET_VK_OBJ_NAME_VK(DEVICE, OBJ_TYPE, OBJ_HDL, OBJ_NAME) \ + DEVICE->SetVulkanObjectNameVk(OBJ_TYPE, OBJ_HDL, OBJ_NAME); #else #define SET_VK_OBJ_NAME(DEVICE, OBJ_TYPE, OBJ_HDL, OBJ_NAME) +#define SET_VK_OBJ_NAME_VK(DEVICE, OBJ_TYPE, OBJ_HDL, OBJ_NAME) #endif diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp index 3474fea3..d34c1b7d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMeshLibrary.cpp @@ -165,35 +165,40 @@ namespace SHADE device, cmdBuffer, vertPosBuffer, vertPosStorage.data(), static_cast(vertPosStorage.size()) * sizeof(SHMesh::VertexPosition), - BuffUsage::eVertexBuffer + BuffUsage::eVertexBuffer, + "Mesh Library Vertex Positions" ); SHVkUtil::EnsureBufferAndCopyData ( device, cmdBuffer, vertTexCoordBuffer, vertTexCoordStorage.data(), static_cast(vertTexCoordStorage.size()) * sizeof(SHMesh::VertexTexCoord), - BuffUsage::eVertexBuffer + BuffUsage::eVertexBuffer, + "Mesh Library Vertex TexCoords" ); SHVkUtil::EnsureBufferAndCopyData ( device, cmdBuffer, vertTangentBuffer, vertTangentStorage.data(), static_cast(vertTangentStorage.size()) * sizeof(SHMesh::VertexTangent), - BuffUsage::eVertexBuffer + BuffUsage::eVertexBuffer, + "Mesh Library Vertex Tangents" ); SHVkUtil::EnsureBufferAndCopyData ( device, cmdBuffer, vertNormalBuffer, vertNormalStorage.data(), static_cast(vertNormalStorage.size()) * sizeof(SHMesh::VertexNormal), - BuffUsage::eVertexBuffer + BuffUsage::eVertexBuffer, + "Mesh Library Vertex Normals" ); SHVkUtil::EnsureBufferAndCopyData ( device, cmdBuffer, indexBuffer, indexStorage.data(), static_cast(indexStorage.size()) * sizeof(SHMesh::Index), - BuffUsage::eIndexBuffer + BuffUsage::eIndexBuffer, + "Mesh Library Indices" ); isDirty = false; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp index 3b6448fa..9c330b9f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp @@ -23,6 +23,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" #include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" #include "Graphics/Images/SHVkImage.h" +#include "Graphics/Images/SHVkImageView.h" #include "Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h" #include "Assets/Asset Types/SHTextureAsset.h" @@ -150,6 +151,9 @@ namespace SHADE texOrder.emplace_back(job.TextureHandle); combinedImageSamplers.emplace_back(std::make_tuple(job.TextureHandle->ImageView, job.Sampler, vk::ImageLayout::eShaderReadOnlyOptimal)); job.TextureHandle->TextureArrayIndex = static_cast(texOrder.size()) - 1U; + + SET_VK_OBJ_NAME(device, vk::ObjectType::eImage, job.Image->GetVkImage(), "[Image] Texture Library Texture #" + std::to_string(job.TextureHandle->TextureArrayIndex)); + SET_VK_OBJ_NAME(device, vk::ObjectType::eImageView, job.TextureHandle->ImageView->GetImageView(), "[ImageView] Texture Library Texture #" + std::to_string(job.TextureHandle->TextureArrayIndex)); } addJobs.clear(); From 44ae6d0a8f6138de1f033fd48520323652a311de Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 7 Nov 2022 17:28:03 +0800 Subject: [PATCH 090/113] Removed unused descriptor sets in SHGraphicsGlobalData --- .../src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp | 1 - .../src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h | 3 --- 2 files changed, 4 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp index 9717889d..155097a5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp @@ -13,7 +13,6 @@ namespace SHADE /* Static Definitions */ /*-----------------------------------------------------------------------------------*/ std::vector> SHGraphicsGlobalData::globalDescSetLayouts; - Handle SHGraphicsGlobalData::globalDescSets; SHVertexInputState SHGraphicsGlobalData::defaultVertexInputState; Handle SHGraphicsGlobalData::dummyPipelineLayout; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h index 344c2616..439acba5 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h @@ -17,9 +17,6 @@ namespace SHADE //! Global descriptor set layouts. Used to allocate descriptor sets static std::vector> globalDescSetLayouts; - //! Global Descriptor sets - static Handle globalDescSets; - //! Default vertex input state (used by everything). static SHVertexInputState defaultVertexInputState; From 4e02f6413338dce0472737549a1a140b4af7ec79 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 7 Nov 2022 18:06:43 +0800 Subject: [PATCH 091/113] Fixed bug where duplicate triggers were caught due to swapped entity IDs --- .../src/Math/Transform/SHTransformSystem.cpp | 6 ++--- SHADE_Engine/src/Physics/SHPhysicsSystem.cpp | 7 +++--- SHADE_Engine/src/Physics/SHPhysicsSystem.h | 22 +++++++++---------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp index f000aa5b..94c133dd 100644 --- a/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp +++ b/SHADE_Engine/src/Math/Transform/SHTransformSystem.cpp @@ -231,12 +231,12 @@ namespace SHADE tf.worldRotation = tf.localRotation + (parent ? parent->GetLocalRotation() : SHVec3::Zero); // Set the orientation - // Wrap rotations between -360 and 360 and convert to radians + // Wrap rotations between -720 and 720 and convert to radians SHVec3 worldRotRad, localRotRad; for (size_t i = 0; i < SHVec3::SIZE; ++i) { - worldRotRad[i] = SHMath::Wrap(tf.worldRotation[i], -SHMath::TWO_PI, SHMath::TWO_PI); - localRotRad[i] = SHMath::Wrap(tf.localRotation[i], -SHMath::TWO_PI, SHMath::TWO_PI); + worldRotRad[i] = SHMath::Wrap(tf.worldRotation[i], -2.0f * SHMath::TWO_PI, 2.0f * SHMath::TWO_PI); + localRotRad[i] = SHMath::Wrap(tf.localRotation[i], -2.0f * SHMath::TWO_PI, 2.0f * SHMath::TWO_PI); } tf.world.orientation = SHQuaternion::FromEuler(worldRotRad); diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp index bdee8ba1..66c28958 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp @@ -605,7 +605,6 @@ namespace SHADE if (rigidBodyComponent != nullptr) { - if (rigidBodyComponent->GetType() == SHRigidBodyComponent::Type::STATIC) continue; @@ -658,8 +657,10 @@ namespace SHADE { const auto IT = std::ranges::find_if(container.begin(), container.end(), [&](const SHCollisionEvent& e) { - const bool ENTITY_MATCH = e.value[0] == collisionEvent.value[0]; - const bool COLLIDERS_MATCH = e.value[1] == collisionEvent.value[1]; + const bool ENTITY_MATCH = (e.ids[0] == collisionEvent.ids[0] && e.ids[1] == collisionEvent.ids[1]) + || (e.ids[0] == collisionEvent.ids[1] && e.ids[1] == collisionEvent.ids[0]); + const bool COLLIDERS_MATCH = (e.ids[2] == collisionEvent.ids[2] && e.ids[3] == collisionEvent.ids[3]) + || (e.ids[2] == collisionEvent.ids[3] && e.ids[3] == collisionEvent.ids[2]); return ENTITY_MATCH && COLLIDERS_MATCH; }); diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/SHPhysicsSystem.h index 05e6e57e..0dd2754e 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.h @@ -183,24 +183,24 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - SHPhysicsObject* EnsurePhysicsObject (EntityID entityID) noexcept; - SHPhysicsObject* GetPhysicsObject (EntityID entityID) noexcept; - void DestroyPhysicsObject (EntityID entityID) noexcept; + SHPhysicsObject* EnsurePhysicsObject (EntityID entityID) noexcept; + SHPhysicsObject* GetPhysicsObject (EntityID entityID) noexcept; + void DestroyPhysicsObject (EntityID entityID) noexcept; - static void SyncActiveStates (SHPhysicsObject& physicsObject, bool componentActive) noexcept; - void SyncTransforms () noexcept; + static void SyncActiveStates (SHPhysicsObject& physicsObject, bool componentActive) noexcept; + void SyncTransforms () noexcept; - static void UpdateEventContainers (const SHCollisionEvent& collisionEvent, CollisionEvents& container) noexcept; - void ClearInvalidCollisions () noexcept; + static void UpdateEventContainers (const SHCollisionEvent& collisionEvent, CollisionEvents& container) noexcept; + void ClearInvalidCollisions () noexcept; - SHEventHandle AddPhysicsComponent (SHEventPtr addComponentEvent); - SHEventHandle RemovePhysicsComponent (SHEventPtr removeComponentEvent); - SHEventHandle ResetWorld (SHEventPtr editorStopEvent); + SHEventHandle AddPhysicsComponent (SHEventPtr addComponentEvent); + SHEventHandle RemovePhysicsComponent (SHEventPtr removeComponentEvent); + SHEventHandle ResetWorld (SHEventPtr editorStopEvent); template || std::is_same_v>> - SHCollisionEvent GenerateCollisionEvent (const RP3DCollisionPair& cp) noexcept; + SHCollisionEvent GenerateCollisionEvent (const RP3DCollisionPair& cp) noexcept; }; } // namespace SHADE From 70dcad13138a186353d988d950612af720d4f133 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Mon, 7 Nov 2022 18:19:39 +0800 Subject: [PATCH 092/113] Changed Collider to CollisionShape for improved clarity --- SHADE_Application/src/Scenes/SBTestScene.cpp | 18 ++--- .../Inspector/SHEditorComponentView.hpp | 16 ++--- .../Components/SHColliderComponent.cpp | 26 ++++---- .../Physics/Components/SHColliderComponent.h | 18 ++--- .../{SHCollider.cpp => SHCollisionShape.cpp} | 66 +++++++++---------- .../{SHCollider.h => SHCollisionShape.h} | 14 ++-- SHADE_Engine/src/Physics/SHPhysicsObject.cpp | 12 ++-- SHADE_Engine/src/Physics/SHPhysicsObject.h | 2 +- SHADE_Engine/src/Physics/SHPhysicsSystem.cpp | 8 +-- SHADE_Engine/src/Physics/SHPhysicsSystem.h | 2 +- SHADE_Engine/src/Physics/SHPhysicsUtils.cpp | 8 +-- SHADE_Engine/src/Physics/SHPhysicsUtils.h | 6 +- .../src/Serialization/SHYAMLConverters.h | 46 ++++++------- SHADE_Managed/src/Components/Collider.cxx | 10 +-- SHADE_Managed/src/Components/Collider.h++ | 6 +- 15 files changed, 129 insertions(+), 129 deletions(-) rename SHADE_Engine/src/Physics/{SHCollider.cpp => SHCollisionShape.cpp} (75%) rename SHADE_Engine/src/Physics/{SHCollider.h => SHCollisionShape.h} (91%) diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index 02977f19..ecacdc79 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -114,8 +114,8 @@ namespace Sandbox racoonTransform.SetWorldPosition({ -3.0f, -2.0f, -5.0f }); racoonCollider.AddBoundingBox(); - racoonCollider.GetCollider(0).SetPositionOffset(SHVec3(0.0f,0.5f,0.0f)); - racoonCollider.GetCollider(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + 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); @@ -140,13 +140,13 @@ namespace Sandbox itemCollider.AddBoundingBox(); itemCollider.AddBoundingBox(SHVec3(2.0f,2.0f,2.0f)); - itemCollider.GetCollider(1).SetIsTrigger(true); + itemCollider.GetCollisionShape(1).SetIsTrigger(true); - itemCollider.GetCollider(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - itemCollider.GetCollider(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.GetCollider(1).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - itemCollider.GetCollider(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); @@ -168,8 +168,8 @@ namespace Sandbox AITransform.SetWorldPosition({ -8.0f, -2.0f, 2.5f }); AICollider.AddBoundingBox(); - AICollider.GetCollider(0).SetPositionOffset(SHVec3(0.0f, 0.5f, 0.0f)); - AICollider.GetCollider(0).SetBoundingBox(SHVec3(0.5f, 0.5f, 0.5f)); + 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 72c38d6f..d7ae56c1 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -234,21 +234,21 @@ namespace SHADE { DrawContextMenu(component); - auto& colliders = component->GetColliders(); + auto& colliders = component->GetCollisionShapes(); int const size = static_cast(colliders.size()); - ImGui::BeginChild("Colliders", { 0.0f, colliders.empty() ? 1.0f : 250.0f }, true); + 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); - SHCollider* collider = &component->GetCollider(i); + SHCollisionShape* collider = &component->GetCollisionShape(i); auto cursorPos = ImGui::GetCursorPos(); //collider->IsTrigger - if (collider->GetType() == SHCollider::Type::BOX) + if (collider->GetType() == SHCollisionShape::Type::BOX) { - SHEditorWidgets::BeginPanel(std::format("{} Box Collider #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); + SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); SHEditorWidgets::CheckBox("Is Trigger", [collider]() {return collider->IsTrigger(); }, [collider](bool const& value) {collider->SetIsTrigger(value); }, "Is Trigger"); auto box = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragVec3 @@ -257,9 +257,9 @@ namespace SHADE [box, transformComponent] { return (box->GetHalfExtents() * 2.0f) / transformComponent->GetWorldScale(); }, [collider](SHVec3 const& vec) { collider->SetBoundingBox(vec); }); } - else if (collider->GetType() == SHCollider::Type::SPHERE) + else if (collider->GetType() == SHCollisionShape::Type::SPHERE) { - SHEditorWidgets::BeginPanel(std::format("{} Sphere Collider #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); + SHEditorWidgets::BeginPanel(std::format("{} Sphere #{}", ICON_MD_CIRCLE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); SHEditorWidgets::CheckBox("Is Trigger", [collider]() {return collider->IsTrigger(); }, [collider](bool const& value) {collider->SetIsTrigger(value); }, "Is Trigger"); auto sphere = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragFloat @@ -273,7 +273,7 @@ namespace SHADE }, [collider](float const& value) { collider->SetBoundingSphere(value); }); } - else if (collider->GetType() == SHCollider::Type::CAPSULE) + else if (collider->GetType() == SHCollisionShape::Type::CAPSULE) { } diff --git a/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp index c7e327fa..af3965fb 100644 --- a/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp @@ -48,17 +48,17 @@ namespace SHADE return orientation.ToEuler(); } - const SHColliderComponent::Colliders& SHColliderComponent::GetColliders() const noexcept + const SHColliderComponent::CollisionShapes& SHColliderComponent::GetCollisionShapes() const noexcept { - return colliders; + return collisionShapes; } - SHCollider& SHColliderComponent::GetCollider(int index) + SHCollisionShape& SHColliderComponent::GetCollisionShape(int index) { - if (index < 0 || static_cast(index) >= colliders.size()) + if (index < 0 || static_cast(index) >= collisionShapes.size()) throw std::invalid_argument("Out-of-range access!"); - return colliders[index]; + return collisionShapes[index]; } /*-----------------------------------------------------------------------------------*/ @@ -83,9 +83,9 @@ namespace SHADE return nullptr; } - static constexpr auto TYPE = SHCollider::Type::BOX; + static constexpr auto TYPE = SHCollisionShape::Type::BOX; - auto& collider = colliders.emplace_back(SHCollider{ GetEID(), TYPE }); + auto& collider = collisionShapes.emplace_back(SHCollisionShape{ GetEID(), TYPE }); collider.entityID = GetEID(); collider.SetPositionOffset(posOffset); @@ -105,9 +105,9 @@ namespace SHADE return nullptr; } - static constexpr auto TYPE = SHCollider::Type::SPHERE; + static constexpr auto TYPE = SHCollisionShape::Type::SPHERE; - auto& collider = colliders.emplace_back(SHCollider{ GetEID(), TYPE }); + auto& collider = collisionShapes.emplace_back(SHCollisionShape{ GetEID(), TYPE }); collider.entityID = GetEID(); collider.SetPositionOffset(posOffset); @@ -121,12 +121,12 @@ namespace SHADE void SHColliderComponent::RemoveCollider(int index) { - if (index < 0 || static_cast(index) >= colliders.size()) + if (index < 0 || static_cast(index) >= collisionShapes.size()) throw std::invalid_argument("Out-of-range access!"); int idx = 0; - auto it = colliders.begin(); - for (; it != colliders.end(); ++it) + auto it = collisionShapes.begin(); + for (; it != collisionShapes.end(); ++it) { if (idx == index) break; @@ -134,7 +134,7 @@ namespace SHADE ++idx; } - it = colliders.erase(it); + it = collisionShapes.erase(it); // Notify Physics System if (!system) diff --git a/SHADE_Engine/src/Physics/Components/SHColliderComponent.h b/SHADE_Engine/src/Physics/Components/SHColliderComponent.h index 7ce272a9..ac5e7528 100644 --- a/SHADE_Engine/src/Physics/Components/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Components/SHColliderComponent.h @@ -14,7 +14,7 @@ // Project Headers #include "ECS_Base/Components/SHComponent.h" -#include "Physics/SHCollider.h" +#include "Physics/SHCollisionShape.h" #include "Math/Geometry/SHBoundingBox.h" #include "Math/Geometry/SHBoundingSphere.h" @@ -43,7 +43,7 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using Colliders = std::vector; + using CollisionShapes = std::vector; public: @@ -67,14 +67,14 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] bool HasChanged () const noexcept; + [[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& GetPosition () const noexcept; + [[nodiscard]] const SHQuaternion& GetOrientation () const noexcept; + [[nodiscard]] SHVec3 GetRotation () const noexcept; - [[nodiscard]] const Colliders& GetColliders () const noexcept; - [[nodiscard]] SHCollider& GetCollider (int index); + [[nodiscard]] const CollisionShapes& GetCollisionShapes() const noexcept; + [[nodiscard]] SHCollisionShape& GetCollisionShape (int index); /*---------------------------------------------------------------------------------*/ /* Function Members */ @@ -98,7 +98,7 @@ namespace SHADE SHVec3 position; SHQuaternion orientation; - Colliders colliders; + CollisionShapes collisionShapes; RTTR_ENABLE() }; diff --git a/SHADE_Engine/src/Physics/SHCollider.cpp b/SHADE_Engine/src/Physics/SHCollisionShape.cpp similarity index 75% rename from SHADE_Engine/src/Physics/SHCollider.cpp rename to SHADE_Engine/src/Physics/SHCollisionShape.cpp index 6d455d67..78ec36f9 100644 --- a/SHADE_Engine/src/Physics/SHCollider.cpp +++ b/SHADE_Engine/src/Physics/SHCollisionShape.cpp @@ -11,7 +11,7 @@ #include // Primary Header -#include "SHCollider.h" +#include "SHCollisionShape.h" // Project Headers #include "Math/Geometry/SHBoundingBox.h" #include "Math/Geometry/SHBoundingSphere.h" @@ -25,7 +25,7 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollider::SHCollider(EntityID eid, Type colliderType, const SHPhysicsMaterial& physicsMaterial) + SHCollisionShape::SHCollisionShape(EntityID eid, Type colliderType, const SHPhysicsMaterial& physicsMaterial) : type { colliderType } , entityID { eid } , isTrigger { false } @@ -49,7 +49,7 @@ namespace SHADE } } - SHCollider::SHCollider(const SHCollider& rhs) noexcept + SHCollisionShape::SHCollisionShape(const SHCollisionShape& rhs) noexcept : type { rhs.type} , entityID { rhs.entityID } , isTrigger { rhs.isTrigger } @@ -61,7 +61,7 @@ namespace SHADE CopyShape(rhs.shape); } - SHCollider::SHCollider(SHCollider&& rhs) noexcept + SHCollisionShape::SHCollisionShape(SHCollisionShape&& rhs) noexcept : type { rhs.type} , entityID { rhs.entityID } , isTrigger { rhs.isTrigger } @@ -73,7 +73,7 @@ namespace SHADE CopyShape(rhs.shape); } - SHCollider::~SHCollider() noexcept + SHCollisionShape::~SHCollisionShape() noexcept { shape = nullptr; } @@ -82,7 +82,7 @@ namespace SHADE /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollider& SHCollider::operator=(const SHCollider& rhs) noexcept + SHCollisionShape& SHCollisionShape::operator=(const SHCollisionShape& rhs) noexcept { if (this == &rhs) return *this; @@ -100,7 +100,7 @@ namespace SHADE return *this; } - SHCollider& SHCollider::operator=(SHCollider&& rhs) noexcept + SHCollisionShape& SHCollisionShape::operator=(SHCollisionShape&& rhs) noexcept { type = rhs.type; entityID = rhs.entityID; @@ -119,52 +119,52 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - bool SHCollider::HasChanged() const noexcept + bool SHCollisionShape::HasChanged() const noexcept { return dirty; } - bool SHCollider::IsTrigger() const noexcept + bool SHCollisionShape::IsTrigger() const noexcept { return isTrigger; } - SHCollider::Type SHCollider::GetType() const noexcept + SHCollisionShape::Type SHCollisionShape::GetType() const noexcept { return type; } - float SHCollider::GetFriction() const noexcept + float SHCollisionShape::GetFriction() const noexcept { return material.GetFriction(); } - float SHCollider::GetBounciness() const noexcept + float SHCollisionShape::GetBounciness() const noexcept { return material.GetBounciness(); } - float SHCollider::GetDensity() const noexcept + float SHCollisionShape::GetDensity() const noexcept { return material.GetDensity(); } - const SHPhysicsMaterial& SHCollider::GetMaterial() const noexcept + const SHPhysicsMaterial& SHCollisionShape::GetMaterial() const noexcept { return material; } - const SHVec3& SHCollider::GetPositionOffset() const noexcept + const SHVec3& SHCollisionShape::GetPositionOffset() const noexcept { return positionOffset; } - const SHVec3& SHCollider::GetRotationOffset() const noexcept + const SHVec3& SHCollisionShape::GetRotationOffset() const noexcept { return rotationOffset; } - SHShape* SHCollider::GetShape() noexcept + SHShape* SHCollisionShape::GetShape() noexcept { dirty = true; return shape; @@ -174,7 +174,7 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollider::SetBoundingBox(const SHVec3& halfExtents) + void SHCollisionShape::SetBoundingBox(const SHVec3& halfExtents) { dirty = true; @@ -199,7 +199,7 @@ namespace SHADE } } - void SHCollider::SetBoundingSphere(float radius) + void SHCollisionShape::SetBoundingSphere(float radius) { dirty = true; @@ -230,37 +230,37 @@ namespace SHADE } - void SHCollider::SetIsTrigger(bool trigger) noexcept + void SHCollisionShape::SetIsTrigger(bool trigger) noexcept { dirty = true; isTrigger = trigger; } - void SHCollider::SetFriction(float friction) noexcept + void SHCollisionShape::SetFriction(float friction) noexcept { dirty = true; material.SetFriction(friction); } - void SHCollider::SetBounciness(float bounciness) noexcept + void SHCollisionShape::SetBounciness(float bounciness) noexcept { dirty = true; material.SetBounciness(bounciness); } - void SHCollider::SetDensity(float density) noexcept + void SHCollisionShape::SetDensity(float density) noexcept { dirty = true; material.SetDensity(density); } - void SHCollider::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept + void SHCollisionShape::SetMaterial(const SHPhysicsMaterial& newMaterial) noexcept { dirty = true; material = newMaterial; } - void SHCollider::SetPositionOffset(const SHVec3& posOffset) noexcept + void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept { dirty = true; positionOffset = posOffset; @@ -281,7 +281,7 @@ namespace SHADE } } - void SHCollider::SetRotationOffset(const SHVec3& rotOffset) noexcept + void SHCollisionShape::SetRotationOffset(const SHVec3& rotOffset) noexcept { dirty = true; rotationOffset = rotOffset; @@ -291,7 +291,7 @@ namespace SHADE /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHCollider::CopyShape(const SHShape* rhs) + void SHCollisionShape::CopyShape(const SHShape* rhs) { switch (type) { @@ -320,14 +320,14 @@ RTTR_REGISTRATION using namespace SHADE; using namespace rttr; - registration::enumeration("Collider Type") + registration::enumeration("Collider Type") ( - value("Box", SHCollider::Type::BOX), - value("Sphere", SHCollider::Type::SPHERE) + value("Box", SHCollisionShape::Type::BOX), + value("Sphere", SHCollisionShape::Type::SPHERE) // TODO(Diren): Add More Shapes ); - registration::class_("Collider") - .property("Position Offset", &SHCollider::GetPositionOffset, &SHCollider::SetPositionOffset) - .property("Rotation Offset", &SHCollider::GetRotationOffset, &SHCollider::SetRotationOffset) (metadata(META::angleInRad, true)); + registration::class_("Collider") + .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/SHCollider.h b/SHADE_Engine/src/Physics/SHCollisionShape.h similarity index 91% rename from SHADE_Engine/src/Physics/SHCollider.h rename to SHADE_Engine/src/Physics/SHCollisionShape.h index 8cc233c4..9c8c1d41 100644 --- a/SHADE_Engine/src/Physics/SHCollider.h +++ b/SHADE_Engine/src/Physics/SHCollisionShape.h @@ -24,7 +24,7 @@ namespace SHADE /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - class SH_API SHCollider + class SH_API SHCollisionShape { private: @@ -51,18 +51,18 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHCollider (EntityID eid, Type colliderType = Type::BOX, const SHPhysicsMaterial& physicsMaterial = SHPhysicsMaterial::DEFAULT); + SHCollisionShape (EntityID eid, Type colliderType = Type::BOX, const SHPhysicsMaterial& physicsMaterial = SHPhysicsMaterial::DEFAULT); - SHCollider (const SHCollider& rhs) noexcept; - SHCollider (SHCollider&& rhs) noexcept; - ~SHCollider () noexcept; + SHCollisionShape (const SHCollisionShape& rhs) noexcept; + SHCollisionShape (SHCollisionShape&& rhs) noexcept; + ~SHCollisionShape () noexcept; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHCollider& operator=(const SHCollider& rhs) noexcept; - SHCollider& operator=(SHCollider&& rhs) noexcept; + SHCollisionShape& operator=(const SHCollisionShape& rhs) noexcept; + SHCollisionShape& operator=(SHCollisionShape&& rhs) noexcept; /*---------------------------------------------------------------------------------*/ /* Getter Functions */ diff --git a/SHADE_Engine/src/Physics/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/SHPhysicsObject.cpp index 37c1269e..5658f304 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsObject.cpp @@ -128,13 +128,13 @@ namespace SHADE /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - int SHPhysicsObject::AddCollider(SHCollider* collider) + int SHPhysicsObject::AddCollider(SHCollisionShape* collider) { const rp3d::Transform OFFSETS{ collider->GetPositionOffset(), collider->GetRotationOffset() }; switch (collider->GetType()) { - case SHCollider::Type::BOX: + case SHCollisionShape::Type::BOX: { const auto* box = reinterpret_cast(collider->GetShape()); rp3d::BoxShape* newBox = factory->createBoxShape(box->GetHalfExtents()); @@ -142,7 +142,7 @@ namespace SHADE rp3dBody->addCollider(newBox, OFFSETS); break; } - case SHCollider::Type::SPHERE: + case SHCollisionShape::Type::SPHERE: { const auto* sphere = reinterpret_cast(collider->GetShape()); rp3d::SphereShape* newSphere = factory->createSphereShape(sphere->GetRadius()); @@ -173,7 +173,7 @@ namespace SHADE void SHPhysicsObject::SyncColliders(SHColliderComponent* c) const noexcept { int index = 0; - for (auto& collider : c->colliders) + for (auto& collider : c->collisionShapes) { if (!collider.dirty) continue; @@ -188,7 +188,7 @@ namespace SHADE switch (collider.GetType()) { - case SHCollider::Type::BOX: + case SHCollisionShape::Type::BOX: { const auto* box = reinterpret_cast(collider.GetShape()); @@ -197,7 +197,7 @@ namespace SHADE break; } - case SHCollider::Type::SPHERE: + case SHCollisionShape::Type::SPHERE: { const auto* sphere = reinterpret_cast(collider.GetShape()); diff --git a/SHADE_Engine/src/Physics/SHPhysicsObject.h b/SHADE_Engine/src/Physics/SHPhysicsObject.h index 64caacdb..09b70b11 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsObject.h +++ b/SHADE_Engine/src/Physics/SHPhysicsObject.h @@ -69,7 +69,7 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - int AddCollider (SHCollider* collider); + int AddCollider (SHCollisionShape* collider); void RemoveCollider (int index); void SyncColliders (SHColliderComponent* c) const noexcept; diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp index 66c28958..078a5d6a 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp @@ -348,7 +348,7 @@ namespace SHADE factory.destroyPhysicsWorld(world); } - void SHPhysicsSystem::AddCollisionShape(EntityID entityID, SHCollider* collider) + void SHPhysicsSystem::AddCollisionShape(EntityID entityID, SHCollisionShape* collider) { auto* physicsObject = GetPhysicsObject(entityID); @@ -735,7 +735,7 @@ namespace SHADE // Add collision shapes back into the body if (colliderComponent != nullptr) { - for (auto& collider : colliderComponent->colliders) + for (auto& collider : colliderComponent->collisionShapes) physicsObject->AddCollider(&collider); } } @@ -756,7 +756,7 @@ namespace SHADE } // Add Collision Shapes - for (auto& collider : colliderComponent->colliders) + for (auto& collider : colliderComponent->collisionShapes) physicsObject->AddCollider(&collider); } } @@ -801,7 +801,7 @@ namespace SHADE rp3d::Transform{ colliderComponent->position, colliderComponent->orientation } ); - for (auto& collider : colliderComponent->colliders) + for (auto& collider : colliderComponent->collisionShapes) physicsObject->AddCollider(&collider); } } diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/SHPhysicsSystem.h index 0dd2754e..55575c73 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.h @@ -114,7 +114,7 @@ namespace SHADE void Init () override; void Exit () override; - void AddCollisionShape (EntityID entityID, SHCollider* collider); + void AddCollisionShape (EntityID entityID, SHCollisionShape* collider); void RemoveCollisionShape (EntityID entityID, int index); void onContact (const rp3d::CollisionCallback::CallbackData& callbackData) override; diff --git a/SHADE_Engine/src/Physics/SHPhysicsUtils.cpp b/SHADE_Engine/src/Physics/SHPhysicsUtils.cpp index 8d5bc956..14b6cc2f 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsUtils.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsUtils.cpp @@ -75,14 +75,14 @@ namespace SHADE return SHComponentManager::GetComponent_s(ids[ENTITY_B]); } - const SHCollider* SHCollisionEvent::GetColliderA() const noexcept + const SHCollisionShape* SHCollisionEvent::GetColliderA() const noexcept { - return &SHComponentManager::GetComponent(ids[ENTITY_A])->GetCollider(ids[COLLIDER_A]); + return &SHComponentManager::GetComponent(ids[ENTITY_A])->GetCollisionShape(ids[COLLIDER_A]); } - const SHCollider* SHCollisionEvent::GetColliderB() const noexcept + const SHCollisionShape* SHCollisionEvent::GetColliderB() const noexcept { - return &SHComponentManager::GetComponent(ids[ENTITY_B])->GetCollider(ids[COLLIDER_B]); + return &SHComponentManager::GetComponent(ids[ENTITY_B])->GetCollisionShape(ids[COLLIDER_B]); } SHCollisionEvent::State SHCollisionEvent::GetCollisionState() const noexcept diff --git a/SHADE_Engine/src/Physics/SHPhysicsUtils.h b/SHADE_Engine/src/Physics/SHPhysicsUtils.h index 57f9c6fc..753f8d3b 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsUtils.h +++ b/SHADE_Engine/src/Physics/SHPhysicsUtils.h @@ -24,7 +24,7 @@ namespace SHADE struct SHPhysicsColliderAddedEvent { EntityID entityID; - SHCollider::Type colliderType; + SHCollisionShape::Type colliderType; int colliderIndex; }; @@ -88,8 +88,8 @@ namespace SHADE [[nodiscard]] EntityID GetEntityB () const noexcept; [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyA () const noexcept; [[nodiscard]] const SHRigidBodyComponent* GetRigidBodyB () const noexcept; - [[nodiscard]] const SHCollider* GetColliderA () const noexcept; - [[nodiscard]] const SHCollider* GetColliderB () const noexcept; + [[nodiscard]] const SHCollisionShape* GetColliderA () const noexcept; + [[nodiscard]] const SHCollisionShape* GetColliderB () const noexcept; [[nodiscard]] State GetCollisionState () const noexcept; private: diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index d4b97244..76be74ce 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -3,7 +3,7 @@ #include "Graphics/MiddleEnd/Materials/SHMaterialSpec.h" #include "Math/Geometry/SHBoundingBox.h" #include "Math/Geometry/SHBoundingSphere.h" -#include "Physics/SHCollider.h" +#include "Physics/SHCollisionShape.h" #include "Resource/SHResourceManager.h" #include "Math/Vector/SHVec2.h" #include "Math/Vector/SHVec3.h" @@ -101,7 +101,7 @@ namespace YAML }; template<> - struct convert + struct convert { static constexpr const char* IsTrigger = "Is Trigger"; @@ -114,33 +114,33 @@ namespace YAML static constexpr const char* Density = "Density"; static constexpr const char* PositionOffset = "Position Offset"; - static Node encode(SHCollider& rhs) + static Node encode(SHCollisionShape& rhs) { Node node; node[IsTrigger] = rhs.IsTrigger(); - rttr::type const shapeRttrType = rttr::type::get(); + rttr::type const shapeRttrType = rttr::type::get(); rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); - SHCollider::Type colliderType = rhs.GetType(); + SHCollisionShape::Type colliderType = rhs.GetType(); node[Type] = enumAlign.value_to_name(colliderType).data(); switch (colliderType) { - case SHCollider::Type::BOX: + case SHCollisionShape::Type::BOX: { auto const bb = reinterpret_cast(rhs.GetShape()); node[HalfExtents] = bb->GetHalfExtents(); } break; - case SHCollider::Type::SPHERE: + case SHCollisionShape::Type::SPHERE: { auto const bs = reinterpret_cast(rhs.GetShape()); node[Radius] = bs->GetRadius(); } break; - case SHCollider::Type::CAPSULE: break; + case SHCollisionShape::Type::CAPSULE: break; default:; } @@ -151,33 +151,33 @@ namespace YAML return node; } - static bool decode(Node const& node, SHCollider& rhs) + static bool decode(Node const& node, SHCollisionShape& rhs) { if (node[IsTrigger].IsDefined()) rhs.SetIsTrigger(node[IsTrigger].as()); if (!node[Type].IsDefined()) return false; - rttr::type const shapeRttrType = rttr::type::get(); + rttr::type const shapeRttrType = rttr::type::get(); rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); bool ok; - const SHCollider::Type colliderType = enumAlign.name_to_value(node[Type].as()).convert(&ok); + const SHCollisionShape::Type colliderType = enumAlign.name_to_value(node[Type].as()).convert(&ok); if (!ok) return false; switch (colliderType) { - case SHCollider::Type::BOX: + case SHCollisionShape::Type::BOX: { if (node[HalfExtents].IsDefined()) rhs.SetBoundingBox(node[HalfExtents].as() * 2.0f); } break; - case SHCollider::Type::SPHERE: + case SHCollisionShape::Type::SPHERE: { if (node[Radius].IsDefined()) rhs.SetBoundingSphere(node[Radius].as()); } break; - case SHCollider::Type::CAPSULE: break; + case SHCollisionShape::Type::CAPSULE: break; default:; } if (node[Friction].IsDefined()) @@ -200,12 +200,12 @@ namespace YAML static Node encode(SHColliderComponent& rhs) { Node node, collidersNode; - auto const& colliders = rhs.GetColliders(); + auto const& colliders = rhs.GetCollisionShapes(); int const numColliders = static_cast(colliders.size()); for (int i = 0; i < numColliders; ++i) { - auto& collider = rhs.GetCollider(i); - Node colliderNode = convert::encode(collider); + auto& collider = rhs.GetCollisionShape(i); + Node colliderNode = convert::encode(collider); if (colliderNode.IsDefined()) collidersNode[i] = colliderNode; } @@ -219,21 +219,21 @@ namespace YAML int numColliders{}; for (auto const& colliderNode : node[Colliders]) { - rttr::type const shapeRttrType = rttr::type::get(); + rttr::type const shapeRttrType = rttr::type::get(); rttr::enumeration const enumAlign = shapeRttrType.get_enumeration(); bool ok = false; - const SHCollider::Type colliderType = enumAlign.name_to_value(colliderNode[convert::Type].as()).convert(&ok); + const SHCollisionShape::Type colliderType = enumAlign.name_to_value(colliderNode[convert::Type].as()).convert(&ok); if (!ok) return false; switch (colliderType) { - case SHCollider::Type::BOX: rhs.AddBoundingBox(); break; - case SHCollider::Type::SPHERE: rhs.AddBoundingSphere(); break; - case SHCollider::Type::CAPSULE: break; + case SHCollisionShape::Type::BOX: rhs.AddBoundingBox(); break; + case SHCollisionShape::Type::SPHERE: rhs.AddBoundingSphere(); break; + case SHCollisionShape::Type::CAPSULE: break; default:; } - YAML::convert::decode(colliderNode, rhs.GetCollider(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 f2119b43..dc5d27af 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -150,7 +150,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ int Collider::CollisionShapeCount::get() { - return static_cast(GetNativeComponent()->GetColliders().size()); + return static_cast(GetNativeComponent()->GetCollisionShapes().size()); } /*---------------------------------------------------------------------------------*/ @@ -230,18 +230,18 @@ namespace SHADE // Populate the list int i = 0; - for (const auto& collider : GetNativeComponent()->GetColliders()) + for (const auto& collider : GetNativeComponent()->GetCollisionShapes()) { CollisionShape^ bound = nullptr; switch (collider.GetType()) { - case SHCollider::Type::BOX: + case SHCollisionShape::Type::BOX: bound = gcnew BoxCollider(i, Owner.GetEntity()); break; - case SHCollider::Type::SPHERE: + case SHCollisionShape::Type::SPHERE: bound = gcnew SphereCollider(i, Owner.GetEntity()); break; - case SHCollider::Type::CAPSULE: + case SHCollisionShape::Type::CAPSULE: // TODO break; default: diff --git a/SHADE_Managed/src/Components/Collider.h++ b/SHADE_Managed/src/Components/Collider.h++ index 1f8b43eb..6e165619 100644 --- a/SHADE_Managed/src/Components/Collider.h++ +++ b/SHADE_Managed/src/Components/Collider.h++ @@ -27,11 +27,11 @@ namespace SHADE try { - auto& bounds = collider->GetCollider(arrayIndex); - if (bounds.GetType() != SHCollider::Type::BOX) + auto& shape = collider->GetCollisionShape(arrayIndex); + if (shape.GetType() != SHCollisionShape::Type::BOX) throw gcnew System::InvalidOperationException("Attempted to retrieve invalid ColliderBound."); - return reinterpret_cast(bounds); + return reinterpret_cast(shape); } catch (std::invalid_argument&) { From d302d7e07d212b9a7eec08a75b0e7bfd80f3d9b8 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 7 Nov 2022 19:32:12 +0800 Subject: [PATCH 093/113] Added labels for more buffers, images, imageviews, frames, semaphores and more --- .../src/Graphics/Buffers/SHVkBuffer.cpp | 7 +++++- .../Graphics/Devices/SHVkLogicalDevice.cpp | 4 ++-- .../src/Graphics/Images/SHVkImage.cpp | 16 +++++++++---- SHADE_Engine/src/Graphics/Images/SHVkImage.h | 8 +++++-- .../Graphics/MiddleEnd/Batching/SHBatch.cpp | 10 +++++++- .../GlobalData/SHGraphicsGlobalData.cpp | 16 ++++++------- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 23 +++++++++++++------ .../MiddleEnd/Interface/SHMousePickSystem.cpp | 2 +- .../MiddleEnd/Interface/SHRenderer.cpp | 8 ++++++- .../MiddleEnd/Lights/SHLightingSubSystem.cpp | 12 ++++++---- .../MiddleEnd/PerFrame/SHPerFrameData.cpp | 7 ++++-- .../MiddleEnd/PerFrame/SHPerFrameData.h | 2 +- .../MiddleEnd/PerFrame/SHRenderContext.cpp | 8 ++++++- .../MiddleEnd/Textures/SHTextureLibrary.cpp | 8 ++++++- .../Graphics/RenderGraph/SHRenderGraph.cpp | 3 ++- .../RenderGraph/SHRenderGraphNode.cpp | 5 ++-- .../Graphics/RenderGraph/SHRenderGraphNode.h | 2 +- .../RenderGraph/SHRenderGraphNodeCompute.cpp | 11 ++++++++- .../RenderGraph/SHRenderGraphNodeCompute.h | 5 +++- .../RenderGraph/SHRenderGraphResource.cpp | 9 ++++++-- .../src/Graphics/RenderGraph/SHSubpass.cpp | 5 ++++ TempScriptsFolder/RaccoonSpin.cs | 5 ++++ 22 files changed, 132 insertions(+), 44 deletions(-) diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp index 803c53be..36108628 100644 --- a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp +++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp @@ -217,6 +217,7 @@ namespace SHADE &bufferInfo.operator VkBufferCreateInfo & (), // TODO: Verify if this works (can use RenderDoc to check buffer variables?) &allocCreateInfo, &tempBuffer, &stagingAlloc, &allocInfo); + SET_VK_OBJ_NAME_VK(device, vk::ObjectType::eDeviceMemory, allocInfo.deviceMemory, "[Memory] Staging - " + name); // then assign it to the hpp version stagingBuffer = tempBuffer; @@ -252,7 +253,11 @@ namespace SHADE auto result = vmaCreateBuffer(vmaAllocator, &bufferCreateInfo.operator VkBufferCreateInfo & (), &allocCreateInfo, - &tempBuffer, &alloc, &allocInfo); + &tempBuffer, &alloc, &allocInfo); +#ifdef _DEBUG + if (!name.empty()) + SET_VK_OBJ_NAME_VK(device, vk::ObjectType::eDeviceMemory, allocInfo.deviceMemory, "[Memory] " + name); +#endif if (result != VK_SUCCESS) SHVulkanDebugUtil::ReportVkError(vk::Result(result), "Failed to create vulkan buffer. "); diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp index 20332f1d..808ce750 100644 --- a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp +++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp @@ -456,12 +456,12 @@ namespace SHADE /***************************************************************************/ Handle SHVkLogicalDevice::CreateImage(uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) const noexcept { - return SHVkInstance::GetResourceManager().Create(&vmaAllocator, w, h, levels, format, usage, create); + return SHVkInstance::GetResourceManager().Create(GetHandle(), &vmaAllocator, w, h, levels, format, usage, create); } Handle SHVkLogicalDevice::CreateImage(SHImageCreateParams const& imageDetails, unsigned char* data, uint32_t dataSize, std::span inMipOffsets, VmaMemoryUsage memUsage, VmaAllocationCreateFlags allocFlags) noexcept { - return SHVkInstance::GetResourceManager().Create(&vmaAllocator, imageDetails, data, dataSize, inMipOffsets, memUsage, allocFlags); + return SHVkInstance::GetResourceManager().Create(GetHandle(), &vmaAllocator, imageDetails, data, dataSize, inMipOffsets, memUsage, allocFlags); } /***************************************************************************/ diff --git a/SHADE_Engine/src/Graphics/Images/SHVkImage.cpp b/SHADE_Engine/src/Graphics/Images/SHVkImage.cpp index 00cc31bf..33bed1b5 100644 --- a/SHADE_Engine/src/Graphics/Images/SHVkImage.cpp +++ b/SHADE_Engine/src/Graphics/Images/SHVkImage.cpp @@ -61,6 +61,7 @@ namespace SHADE &bufferInfo.operator VkBufferCreateInfo & (), // TODO: Verify if this works (can use renderdoc to check buffer variables?) &allocCreateInfo, &tempBuffer, &stagingAlloc, &allocInfo); + SET_VK_OBJ_NAME_VK(device, vk::ObjectType::eDeviceMemory, allocInfo.deviceMemory, "[Memory] Staging Buffer for Image"); // then assign it to the hpp version stagingBuffer = tempBuffer; @@ -107,6 +108,8 @@ namespace SHADE VkImage tempImage; auto result = vmaCreateImage(*vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo & (), &allocCreateInfo, &tempImage, &alloc, &allocInfo); vkImage = tempImage; + //SET_VK_OBJ_NAME_VK(device, vk::ObjectType::eImage, vkImage, "[Image] "); + SET_VK_OBJ_NAME_VK(device, vk::ObjectType::eDeviceMemory, allocInfo.deviceMemory, "[Device Memory] Image Memory"); if (result != VK_SUCCESS) SHVulkanDebugUtil::ReportVkError(vk::Result(result), "Failed to create vulkan image. "); @@ -115,7 +118,8 @@ namespace SHADE } SHVkImage::SHVkImage( - VmaAllocator const* allocator, + Handle logicalDeviceHdl, + VmaAllocator const* allocator, SHImageCreateParams const& imageDetails, const unsigned char* data, uint32_t dataSize, @@ -137,6 +141,7 @@ namespace SHADE , boundToCoherent{false} , randomAccessOptimized {false} , mappedPtr{nullptr} + , device { logicalDeviceHdl } { usageFlags = imageDetails.usageFlags; createFlags = imageDetails.createFlags; @@ -175,7 +180,9 @@ namespace SHADE VmaAllocationInfo allocInfo{}; VkImage tempImage; - auto result = vmaCreateImage(*vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo&(), &allocCreateInfo, &tempImage, &alloc, &allocInfo); + auto result = vmaCreateImage(*vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo & (), &allocCreateInfo, &tempImage, &alloc, &allocInfo); + //SET_VK_OBJ_NAME_VK(device, vk::ObjectType::eImage, vkImage, "[Image] "); + SET_VK_OBJ_NAME_VK(device, vk::ObjectType::eDeviceMemory, allocInfo.deviceMemory, "[Device Memory] Image Memory"); if (result != VK_SUCCESS) SHVulkanDebugUtil::ReportVkError(vk::Result(result), "Failed to create vulkan image. "); @@ -220,7 +227,7 @@ namespace SHADE //} } - SHVkImage::SHVkImage(VmaAllocator const* allocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept + SHVkImage::SHVkImage(Handle logicalDeviceHdl, VmaAllocator const* allocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept : width {w} , height{h} , depth {1} @@ -230,11 +237,12 @@ namespace SHADE , usageFlags{usage} , createFlags {create} , vmaAllocator {allocator} + , device { logicalDeviceHdl } { CreateFramebufferImage(); } - Handle SHVkImage::CreateImageView(Handle const& inLogicalDeviceHdl, Handle const& parent, SHImageViewDetails const& createParams) const noexcept + Handle SHVkImage::CreateImageView(Handle inLogicalDeviceHdl, Handle const& parent, SHImageViewDetails const& createParams) const noexcept { return SHVkInstance::GetResourceManager().Create(inLogicalDeviceHdl, parent, createParams); } diff --git a/SHADE_Engine/src/Graphics/Images/SHVkImage.h b/SHADE_Engine/src/Graphics/Images/SHVkImage.h index 4fb16017..ba459def 100644 --- a/SHADE_Engine/src/Graphics/Images/SHVkImage.h +++ b/SHADE_Engine/src/Graphics/Images/SHVkImage.h @@ -104,6 +104,9 @@ namespace SHADE //! Mipmap offsets for initializing the vk::BufferImageCopy during transfer to GPU resource std::span mipOffsets; + //! Handle to the device that creates these images + Handle device; + /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ @@ -117,6 +120,7 @@ namespace SHADE SHVkImage(void) noexcept = default; SHVkImage( + Handle logicalDeviceHdl, VmaAllocator const* allocator, SHImageCreateParams const& imageDetails, const unsigned char* data, @@ -126,7 +130,7 @@ namespace SHADE VmaAllocationCreateFlags allocFlags ) noexcept; - SHVkImage(VmaAllocator const* allocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept; + SHVkImage(Handle logicalDeviceHdl, VmaAllocator const* allocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept; SHVkImage(SHVkImage&& rhs) noexcept = default; SHVkImage& operator=(SHVkImage && rhs) noexcept = default; @@ -134,7 +138,7 @@ namespace SHADE /*-----------------------------------------------------------------------*/ /* PUBLIC MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ - Handle CreateImageView (Handle const& inLogicalDeviceHdl, Handle const& parent, SHImageViewDetails const& createParams) const noexcept; + Handle CreateImageView (Handle inLogicalDeviceHdl, Handle const& parent, SHImageViewDetails const& createParams) const noexcept; void TransferToDeviceResource (Handle cmdBufferHdl) noexcept; void PrepareImageTransitionInfo (vk::ImageLayout oldLayout, vk::ImageLayout newLayout, vk::ImageMemoryBarrier& barrier) noexcept; void HandleResizeFramebufferImage(uint32_t newWidth, uint32_t newHeight) noexcept; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 3f9b6fa2..1829096f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -465,7 +465,8 @@ namespace SHADE SHVkUtil::EnsureBufferAndCopyHostVisibleData ( device, matPropsBuffer[frameIndex], matPropsData.get(), static_cast(matPropsDataSize), - vk::BufferUsageFlagBits::eStorageBuffer + vk::BufferUsageFlagBits::eStorageBuffer, + "Batch Material Data" ); if (!matPropsDescSet[frameIndex]) @@ -475,6 +476,13 @@ namespace SHADE { SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE] }, { 0 } ); +#ifdef _DEBUG + const auto& DESC_SETS = matPropsDescSet[frameIndex]->GetVkHandle(); + for (auto descSet : DESC_SETS) + { + SET_VK_OBJ_NAME(device, vk::ObjectType::eDescriptorSet, descSet, "[Descriptor Set] Batch Material Data"); + } +#endif } std::array, 1> bufferList = { matPropsBuffer[frameIndex] }; matPropsDescSet[frameIndex]->ModifyWriteDescBuffer diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp index 155097a5..53adf2fe 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp @@ -19,7 +19,7 @@ namespace SHADE void SHGraphicsGlobalData::InitHighFrequencyGlobalData(void) noexcept { - } + } /*-----------------------------------------------------------------------------------*/ /* Function Definitions */ @@ -44,7 +44,8 @@ namespace SHADE }; // For global data (generic data and textures) - Handle staticGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS,{ genericDataBinding, texturesBinding }); + Handle staticGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS, { genericDataBinding, texturesBinding }); + SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, staticGlobalLayout->GetVkHandle(), "[Descriptor Set Layout] Static Globals"); std::vector lightBindings{}; @@ -70,11 +71,11 @@ namespace SHADE }); } - // For Dynamic global data (lights) Handle dynamicGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, lightBindings); + SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, dynamicGlobalLayout->GetVkHandle(), "[Descriptor Set Layout] Dynamic Globals"); - + // For High frequency global data (camera) SHVkDescriptorSetLayout::Binding cameraDataBinding { .Type = vk::DescriptorType::eUniformBufferDynamic, @@ -82,10 +83,10 @@ namespace SHADE .BindPoint = SHGraphicsConstants::DescriptorSetBindings::CAMERA_DATA, .DescriptorCount = 1, }; - - // For High frequency global data (camera) Handle cameraDataGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::HIGH_FREQUENCY_GLOBALS, { cameraDataBinding }); + SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, cameraDataGlobalLayout->GetVkHandle(), "[Descriptor Set Layout] High Frequency Globals"); + // For per instance data (transforms, materials, etc.) SHVkDescriptorSetLayout::Binding materialDataBinding { .Type = vk::DescriptorType::eStorageBufferDynamic, @@ -93,9 +94,8 @@ namespace SHADE .BindPoint = SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, .DescriptorCount = 1, }; - - // For High frequency global data (camera) Handle materialDataPerInstanceLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, { materialDataBinding }); + SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSetLayout, materialDataPerInstanceLayout->GetVkHandle(), "[Descriptor Set Layout] Material Globals"); globalDescSetLayouts.push_back(staticGlobalLayout); globalDescSetLayouts.push_back(dynamicGlobalLayout); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 5b2fea7c..27234781 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -213,6 +213,7 @@ namespace SHADE ssaoStorage = resourceManager.Create(); ssaoTransferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + SET_VK_OBJ_NAME(device, vk::ObjectType::eCommandBuffer, ssaoTransferCmdBuffer->GetVkCommandBuffer(), "[Command Buffer] SSAO Pass (Graphics)"); ssaoTransferCmdBuffer->BeginRecording(); ssaoStorage->Init(device, ssaoTransferCmdBuffer); @@ -234,7 +235,7 @@ namespace SHADE ssaoStorage->PrepareRotationVectorsVkData(device); - Handle ssaoPass = gBufferNode->AddNodeCompute(ssaoShader, {"Position", "Normals", "SSAO"}); + Handle ssaoPass = gBufferNode->AddNodeCompute("SSAO", ssaoShader, {"Position", "Normals", "SSAO"}); auto ssaoDataBuffer = ssaoStorage->GetBuffer(); ssaoPass->ModifyWriteDescBufferComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_BUFFER_BINDING, { &ssaoDataBuffer, 1 }, 0, ssaoStorage->GetBuffer()->GetSizeStored()); auto viewSamplerLayout = ssaoStorage->GetViewSamplerLayout(); @@ -242,12 +243,12 @@ namespace SHADE ssaoPass->ModifyWriteDescImageComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_IMAGE_BINDING, {&viewSamplerLayout, 1}); - Handle ssaoBlurPass = gBufferNode->AddNodeCompute(ssaoBlurShader, { "SSAO", "SSAO Blur"}); + Handle ssaoBlurPass = gBufferNode->AddNodeCompute("SSAO Blur Step", ssaoBlurShader, {"SSAO", "SSAO Blur"}); /*-----------------------------------------------------------------------*/ /* DEFERRED COMPOSITE SUBPASS INIT */ /*-----------------------------------------------------------------------*/ - gBufferNode->AddNodeCompute(deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "SSAO Blur", "Scene" }); + gBufferNode->AddNodeCompute("Deferred Composite", deferredCompositeShader, {"Position", "Normals", "Albedo", "Light Layer Indices", "SSAO Blur", "Scene"}); // Dummy Node auto dummyNode = worldRenderGraph->AddNode("Dummy Pass", { "Scene" }, { "Debug Draw" }); // no predecessors @@ -271,7 +272,11 @@ namespace SHADE // Create debug draw pipeline debugDrawPipeline = createDebugDrawPipeline(debugDrawNode->GetRenderpass(), debugDrawSubpass); + SET_VK_OBJ_NAME(device, vk::ObjectType::ePipeline, debugDrawPipeline->GetVkPipeline(), "[Pipeline] Debug Draw"); + SET_VK_OBJ_NAME(device, vk::ObjectType::ePipelineLayout, debugDrawPipeline->GetPipelineLayout()->GetVkPipelineLayout(), "[Pipeline] Debug Draw Pipeline Layout"); debugDrawDepthPipeline = createDebugDrawPipeline(debugDrawNodeDepth->GetRenderpass(), debugDrawDepthSubpass); + SET_VK_OBJ_NAME(device, vk::ObjectType::ePipeline, debugDrawDepthPipeline->GetVkPipeline(), "[Pipeline] Debug Draw with Depth Test"); + SET_VK_OBJ_NAME(device, vk::ObjectType::ePipelineLayout, debugDrawDepthPipeline->GetPipelineLayout()->GetVkPipelineLayout(), "[Pipeline] Debug Draw with Depth Test Pipeline Layout"); } void SHGraphicsSystem::InitMiddleEnd(void) noexcept @@ -677,6 +682,8 @@ namespace SHADE auto renderGraphNode = subpass->GetParentNode(); auto pipeline = renderGraphNode->GetOrCreatePipeline(std::make_pair(vertShader, fragShader), subpass); + SET_VK_OBJ_NAME(device, vk::ObjectType::ePipeline, pipeline->GetVkPipeline(), "[Pipeline] Custom Pipeline"); + SET_VK_OBJ_NAME(device, vk::ObjectType::ePipelineLayout, pipeline->GetPipelineLayout()->GetVkPipelineLayout(), "[Pipeline] Custom Pipeline Layout"); mat->SetPipeline(pipeline); @@ -723,14 +730,15 @@ namespace SHADE void SHGraphicsSystem::BuildMeshBuffers() { - transferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); - device->WaitIdle(); + transferCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + SET_VK_OBJ_NAME(device, vk::ObjectType::eCommandBuffer, transferCmdBuffer->GetVkCommandBuffer(), "[Command Buffer] Mesh Buffer Building (Transfer)"); + device->WaitIdle(); transferCmdBuffer->BeginRecording(); meshLibrary.BuildBuffers(device, transferCmdBuffer); transferCmdBuffer->EndRecording(); graphicsQueue->SubmitCommandBuffer({ transferCmdBuffer }); - device->WaitIdle(); - transferCmdBuffer.Free(); transferCmdBuffer = {}; + device->WaitIdle(); + transferCmdBuffer.Free(); transferCmdBuffer = {}; } Handle SHGraphicsSystem::GetMeshPrimitive(PrimitiveType type) const noexcept @@ -768,6 +776,7 @@ namespace SHADE void SHGraphicsSystem::BuildTextures() { graphicsTexCmdBuffer = graphicsCmdPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); + SET_VK_OBJ_NAME(device, vk::ObjectType::eCommandBuffer, graphicsTexCmdBuffer->GetVkCommandBuffer(), "[Command Buffer] Texture Building (Graphics)"); device->WaitIdle(); texLibrary.BuildTextures ( diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp index 46126ae1..86d85c16 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp @@ -76,7 +76,7 @@ namespace SHADE uint32_t bufferSize = entityIDAttachment->GetWidth() * entityIDAttachment->GetHeight() * SHVkUtil::GetBytesPerPixelFromFormat(entityIDAttachment->GetResourceFormat()); // Create the buffer - imageDataDstBuffer = logicalDevice->CreateBuffer(bufferSize, nullptr, bufferSize, vk::BufferUsageFlagBits::eTransferDst, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT); + imageDataDstBuffer = logicalDevice->CreateBuffer(bufferSize, nullptr, bufferSize, vk::BufferUsageFlagBits::eTransferDst, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, "Mouse Pick Image Data Destination"); } void SHMousePickSystem::SetViewportMousePos(SHVec2 vpMousePos) noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp index d0deb30c..63d374eb 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderer.cpp @@ -39,9 +39,15 @@ namespace SHADE cameraDescriptorSet = descriptorPool->Allocate({ cameraDescLayout }, { 1 }); +#ifdef _DEBUG + const auto& CAM_DESC_SETS = cameraDescriptorSet->GetVkHandle(); + for (int i = 0; i < static_cast(CAM_DESC_SETS.size()); ++i) + SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSet, CAM_DESC_SETS[i], "[Descriptor Set] Camera Data Frame #" + std::to_string(i)); +#endif + cameraDataAlignedSize = logicalDevice->PadUBOSize(sizeof(SHShaderCameraData)); - cameraBuffer = logicalDevice->CreateBuffer(cameraDataAlignedSize * numFrames, nullptr, cameraDataAlignedSize * numFrames, vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT); + cameraBuffer = logicalDevice->CreateBuffer(cameraDataAlignedSize * numFrames, nullptr, cameraDataAlignedSize * numFrames, vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, "Camera Data"); std::array cameraBufferArray{cameraBuffer}; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp index 485f859f..02bd8f1f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp @@ -134,7 +134,7 @@ namespace SHADE lightDataTotalAlignedSize = logicalDevice->PadSSBOSize(lightDataAlignedSize * maxLights); // We want to initialize 3 times the amount of data required. - dataBuffer = logicalDevice->CreateBuffer(lightDataTotalAlignedSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, nullptr, lightDataTotalAlignedSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT); + dataBuffer = logicalDevice->CreateBuffer(lightDataTotalAlignedSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, nullptr, lightDataTotalAlignedSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, "Light Data"); } else { @@ -385,8 +385,12 @@ namespace SHADE std::fill (variableSizes.begin(), variableSizes.end(), 1); // Create the descriptor set - lightingDataDescSet = descPool->Allocate({SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS]}, variableSizes); - + lightingDataDescSet = descPool->Allocate({ SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS] }, variableSizes); +#ifdef _DEBUG + const auto& CAM_DESC_SETS = lightingDataDescSet->GetVkHandle(); + for (int i = 0; i < static_cast(CAM_DESC_SETS.size()); ++i) + SET_VK_OBJ_NAME(logicalDevice, vk::ObjectType::eDescriptorSet, CAM_DESC_SETS[i], "[Descriptor Set] Light Data Frame #" + std::to_string(i)); +#endif for (uint32_t i = 0; i < NUM_LIGHT_TYPES; ++i) { @@ -402,7 +406,7 @@ namespace SHADE lightCountsAlignedSize = logicalDevice->PadUBOSize(lightCountsAlignedSize); // Create the GPU buffer to hold light count - lightCountsBuffer = logicalDevice->CreateBuffer(lightCountsAlignedSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, nullptr, lightCountsAlignedSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT); + lightCountsBuffer = logicalDevice->CreateBuffer(lightCountsAlignedSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, nullptr, lightCountsAlignedSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, "Light Count Data"); lightingDataDescSet->ModifyWriteDescBuffer(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, SHGraphicsConstants::DescriptorSetBindings::LIGHTING_COUNT, {&lightCountsBuffer, 1}, 0, sizeof (uint32_t) * NUM_LIGHT_TYPES); lightingDataDescSet->UpdateDescriptorSetBuffer(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, SHGraphicsConstants::DescriptorSetBindings::LIGHTING_COUNT); diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHPerFrameData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHPerFrameData.cpp index bbbdd015..96db1ad1 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHPerFrameData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHPerFrameData.cpp @@ -26,7 +26,7 @@ namespace SHADE */ /***************************************************************************/ - void SHPerFrameData::Recreate(Handle const& logicalDeviceHdl) noexcept + void SHPerFrameData::Recreate(Handle logicalDeviceHdl) noexcept { // Swapchain recreation means the images are just relinked to SHVkImages. Handles will remain the same. There is no need for this line. //swapchainImageHdl = params.swapchainHdl->GetSwapchainImage(frameIndex); @@ -44,14 +44,17 @@ namespace SHADE // Create image views for the swapchain swapchainImageViewHdl = swapchainImageHdl->CreateImageView(logicalDeviceHdl, swapchainImageHdl, viewDetails); + SET_VK_OBJ_NAME(logicalDeviceHdl, vk::ObjectType::eImageView, swapchainImageViewHdl->GetImageView(), "[Image View] Swap Chain"); // Create a fence fenceHdl = logicalDeviceHdl->CreateFence(); + SET_VK_OBJ_NAME(logicalDeviceHdl, vk::ObjectType::eFence, fenceHdl->GetVkFence(), "[Fence] Swap Chain"); // scope makes it easier to navigate semImgAvailableHdl = logicalDeviceHdl->SHVkLogicalDevice::CreateSemaphore(); + SET_VK_OBJ_NAME(logicalDeviceHdl, vk::ObjectType::eSemaphore, semImgAvailableHdl->GetVkSem(), "[Semaphore] Swap Chain Image Available"); semRenderFinishHdl = logicalDeviceHdl->SHVkLogicalDevice::CreateSemaphore(); - + SET_VK_OBJ_NAME(logicalDeviceHdl, vk::ObjectType::eSemaphore, semRenderFinishHdl->GetVkSem(), "[Semaphore] Swap Chain Render Finish"); } /***************************************************************************/ diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHPerFrameData.h b/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHPerFrameData.h index 5ac38503..d19764c2 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHPerFrameData.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHPerFrameData.h @@ -63,7 +63,7 @@ namespace SHADE /* PRIVATE MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ // These are made into functions (instead of ctor and dtor) because we want to call these functions again when we resize the window - void Recreate (Handle const& logicalDeviceHdl) noexcept; + void Recreate (Handle logicalDeviceHdl) noexcept; void Destroy (void); friend class SHRenderContext; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHRenderContext.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHRenderContext.cpp index 45103819..1d24d6f7 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHRenderContext.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/PerFrame/SHRenderContext.cpp @@ -50,7 +50,13 @@ namespace SHADE for (uint32_t j = 0; j < params.numThreads; ++j) { - frameData[i].cmdPoolHdls.push_back(logicalDeviceHdl->CreateCommandPool(params.cmdPoolQueueFamilyType, params.cmdPoolResetMode, params.cmdBufferTransient)); + auto cmdPool = logicalDeviceHdl->CreateCommandPool(params.cmdPoolQueueFamilyType, params.cmdPoolResetMode, params.cmdBufferTransient); + SET_VK_OBJ_NAME + ( + logicalDeviceHdl, vk::ObjectType::eCommandPool, cmdPool->GetVkCommandPool(), + "[Command Pool] Render Context #" + std::to_string(i) + " Pool #" + std::to_string(j) + ); + frameData[i].cmdPoolHdls.push_back(cmdPool); } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp index 9c330b9f..b92ccddf 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Textures/SHTextureLibrary.cpp @@ -79,6 +79,7 @@ namespace SHADE { job.Image = resourceManager.Create ( + device, &device->GetVMAAllocator(), SHImageCreateParams { @@ -143,6 +144,7 @@ namespace SHADE .layerCount = 1 }; job.TextureHandle->ImageView = job.Image->CreateImageView(device, job.Image, DETAILS); + SET_VK_OBJ_NAME(device, vk::ObjectType::eImageView, job.TextureHandle->ImageView->GetImageView(), "[Image View] Texture Library Texture #" + std::to_string(job.TextureHandle->TextureArrayIndex)); } // Add Textures @@ -153,7 +155,7 @@ namespace SHADE job.TextureHandle->TextureArrayIndex = static_cast(texOrder.size()) - 1U; SET_VK_OBJ_NAME(device, vk::ObjectType::eImage, job.Image->GetVkImage(), "[Image] Texture Library Texture #" + std::to_string(job.TextureHandle->TextureArrayIndex)); - SET_VK_OBJ_NAME(device, vk::ObjectType::eImageView, job.TextureHandle->ImageView->GetImageView(), "[ImageView] Texture Library Texture #" + std::to_string(job.TextureHandle->TextureArrayIndex)); + SET_VK_OBJ_NAME(device, vk::ObjectType::eImageView, job.TextureHandle->ImageView->GetImageView(), "[Image View] Texture Library Texture #" + std::to_string(job.TextureHandle->TextureArrayIndex)); } addJobs.clear(); @@ -169,6 +171,10 @@ namespace SHADE { SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS] }, { static_cast(texOrder.size()) } ); +#ifdef _DEBUG + for (auto set : texDescriptors->GetVkHandle()) + SET_VK_OBJ_NAME(device, vk::ObjectType::eDescriptorSet, set, "[Descriptor Set] Static Globals"); +#endif texDescriptors->ModifyWriteDescImage ( SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS, diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp index 9c4e0d65..500bcf04 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp @@ -59,7 +59,8 @@ namespace SHADE format = renderGraphStorage->swapchain->GetSurfaceFormatKHR().format; } - renderGraphStorage->graphResources->try_emplace(resourceName, resourceManager->Create(renderGraphStorage, resourceName, typeFlags, format, w, h, levels, usageFlags, createFlags)); + auto resource = resourceManager->Create(renderGraphStorage, resourceName, typeFlags, format, w, h, levels, usageFlags, createFlags); + renderGraphStorage->graphResources->try_emplace(resourceName, resource); } /***************************************************************************/ diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp index 9aa42c0a..b3b5b58b 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.cpp @@ -58,6 +58,7 @@ namespace SHADE framebuffers[i] = graphStorage->logicalDevice->CreateFramebuffer(renderpass, imageViews, fbWidth, fbHeight); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eFramebuffer, framebuffers[i]->GetVkFramebuffer(), "[Framebuffer] " + name + std::to_string(i)); } } @@ -266,7 +267,7 @@ namespace SHADE return subpass; } - Handle SHRenderGraphNode::AddNodeCompute(Handle computeShaderModule, std::initializer_list resources, std::unordered_set&& dynamicBufferBindings, float numWorkGroupScale/* = 1.0f*/) noexcept + Handle SHRenderGraphNode::AddNodeCompute(std::string nodeName, Handle computeShaderModule, std::initializer_list resources, std::unordered_set&& dynamicBufferBindings, float numWorkGroupScale/* = 1.0f*/) noexcept { // Look for the required resources in the graph std::vector> nodeComputeResources{}; @@ -279,7 +280,7 @@ namespace SHADE } // Create the subpass compute with the resources - auto nodeCompute = graphStorage->resourceManager->Create(graphStorage, computeShaderModule, std::move(nodeComputeResources), std::move (dynamicBufferBindings), nodeComputes.empty()); + auto nodeCompute = graphStorage->resourceManager->Create(std::move(nodeName), graphStorage, computeShaderModule, std::move(nodeComputeResources), std::move (dynamicBufferBindings), nodeComputes.empty()); nodeComputes.push_back(nodeCompute); return nodeCompute; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h index 33868f5e..4fdac45c 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNode.h @@ -103,7 +103,7 @@ namespace SHADE /* PUBLIC MEMBER FUNCTIONS */ /*-----------------------------------------------------------------------*/ Handle AddSubpass(std::string subpassName) noexcept; - Handle AddNodeCompute(Handle computeShaderModule, std::initializer_list resources, std::unordered_set&& dynamicBufferBindings = {}, float numWorkGroupScale = 1.0f) noexcept; + Handle AddNodeCompute(std::string nodeName, Handle computeShaderModule, std::initializer_list resources, std::unordered_set&& dynamicBufferBindings = {}, float numWorkGroupScale = 1.0f) noexcept; void AddDummySubpassIfNeeded (void) noexcept; // TODO: RemoveSubpass() diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp index a86acbc7..8cdff55a 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp @@ -13,7 +13,7 @@ namespace SHADE { - SHRenderGraphNodeCompute::SHRenderGraphNodeCompute(Handle graphStorage, Handle computeShaderModule, std::vector>&& subpassComputeResources, std::unordered_set&& dynamicBufferBindings, bool followingEndRP, float inNumWorkGroupScale/* = 1.0f*/) noexcept + SHRenderGraphNodeCompute::SHRenderGraphNodeCompute(std::string nodeName, Handle graphStorage, Handle computeShaderModule, std::vector>&& subpassComputeResources, std::unordered_set&& dynamicBufferBindings, bool followingEndRP, float inNumWorkGroupScale/* = 1.0f*/) noexcept : computePipeline{} , pipelineLayout{} , resources{} @@ -22,6 +22,7 @@ namespace SHADE , followingEndRenderpass {followingEndRP} , numWorkGroupScale {std::clamp(inNumWorkGroupScale, 0.0f, 1.0f)} , computeResource{} + , name { std::move(nodeName) } { SHPipelineLayoutParams pipelineLayoutParams { @@ -50,6 +51,10 @@ namespace SHADE for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) { graphResourceDescSets[i] = graphStorage->descriptorPool->Allocate({graphResourceLayout}, { 1 }); +#ifdef _DEBUG + for (auto set : graphResourceDescSets[i]->GetVkHandle()) + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eDescriptorSet, set, "[Descriptor Set] " + name + " #" + std::to_string(i)); +#endif } @@ -61,6 +66,10 @@ namespace SHADE computeResource = graphStorage->resourceManager->Create(); auto computeResourceLayout = layouts[SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE]; computeResource->descSet = graphStorage->descriptorPool->Allocate({ computeResourceLayout }, { 1 }); +#ifdef _DEBUG + for (auto set : computeResource->descSet->GetVkHandle()) + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eDescriptorSet, set, "[Descriptor Set] " + name + " Resources"); +#endif // Allocate for descriptor offsets for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h index 81157dc2..580f018c 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.h @@ -65,8 +65,11 @@ namespace SHADE std::array, SHGraphicsConstants::NUM_FRAME_BUFFERS> memoryBarriers; + //! Name of this node + std::string name; + public: - SHRenderGraphNodeCompute(Handle graphStorage, Handle computeShaderModule, std::vector>&& subpassComputeResources, std::unordered_set&& dynamicBufferBindings, bool followingEndRP, float inNumWorkGroupScale = 1.0f) noexcept; + SHRenderGraphNodeCompute(std::string nodeName, Handle graphStorage, Handle computeShaderModule, std::vector>&& subpassComputeResources, std::unordered_set&& dynamicBufferBindings, bool followingEndRP, float inNumWorkGroupScale = 1.0f) noexcept; void Execute (Handle cmdBuffer, uint32_t frameIndex) noexcept; void HandleResize (void) noexcept; diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphResource.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphResource.cpp index 502e09b2..4d4099c6 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphResource.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphResource.cpp @@ -82,6 +82,7 @@ namespace SHADE { images[i] = graphStorage->swapchain->GetSwapchainImage(i); imageViews[i] = images[i]->CreateImageView(graphStorage->logicalDevice, images[i], viewDetails); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eImageView, imageViews[i]->GetImageView(), "[Image View] " + resourceName + " #" + std::to_string(i)); } } else // if swapchain image resource @@ -129,7 +130,9 @@ namespace SHADE } // The resource is not a swapchain image, just use the first slot of the vector - images.push_back(graphStorage->logicalDevice->CreateImage(width, height, mipLevels, resourceFormat, usage, createFlags)); + auto image = graphStorage->logicalDevice->CreateImage(width, height, mipLevels, resourceFormat, usage, createFlags); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eImage, image->GetVkImage(), "[Image] " + resourceName); + images.push_back(image); // prepare image view details SHImageViewDetails viewDetails @@ -144,7 +147,9 @@ namespace SHADE }; // just 1 image view created - imageViews.push_back(images[0]->CreateImageView(graphStorage->logicalDevice, images[0], viewDetails)); + auto imageView = images[0]->CreateImageView(graphStorage->logicalDevice, images[0], viewDetails); + imageViews.push_back(imageView); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eImageView, imageView->GetImageView(), "[Image View] " + resourceName); } } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp index 23dbbde3..2ed84d92 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHSubpass.cpp @@ -302,6 +302,11 @@ namespace SHADE group.Free(); group = graphStorage->descriptorPool->Allocate({ inputDescriptorLayout }, variableCounts); +#ifdef _DEBUG + const auto& GROUP_HANDLES = group->GetVkHandle(); + for (int i = 0; i < static_cast(GROUP_HANDLES.size()); ++i) + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eDescriptorSet, GROUP_HANDLES[i], "[Descriptor Set] " + name + " #" + std::to_string(i)); +#endif uint32_t i = 0; for (auto& binding : bindings) diff --git a/TempScriptsFolder/RaccoonSpin.cs b/TempScriptsFolder/RaccoonSpin.cs index c5420ffb..06b3c163 100644 --- a/TempScriptsFolder/RaccoonSpin.cs +++ b/TempScriptsFolder/RaccoonSpin.cs @@ -14,8 +14,13 @@ public class RaccoonSpin : Script private Transform Transform; public RaccoonSpin(GameObject gameObj) : base(gameObj) { } + protected override void awake() { + testEvent = new CallbackEvent(); + Action action = (x) => Debug.Log($"{x}"); + testEvent.RegisterAction(action); + Transform = GetComponent(); if (Transform == null) { From 0b813d769ad1cad84796095c5eeff55da2a50a9c Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 7 Nov 2022 19:41:19 +0800 Subject: [PATCH 094/113] Added WIP Compute Pipelines labeling --- .../src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp | 6 +++--- .../src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 27234781..3c9ddfaa 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -235,7 +235,7 @@ namespace SHADE ssaoStorage->PrepareRotationVectorsVkData(device); - Handle ssaoPass = gBufferNode->AddNodeCompute("SSAO", ssaoShader, {"Position", "Normals", "SSAO"}); + Handle ssaoPass = gBufferNode->AddNodeCompute("SSAO", ssaoShader, { "Position", "Normals", "SSAO" }); auto ssaoDataBuffer = ssaoStorage->GetBuffer(); ssaoPass->ModifyWriteDescBufferComputeResource(SHGraphicsConstants::DescriptorSetIndex::RENDERGRAPH_NODE_COMPUTE_RESOURCE, SHSSAO::DESC_SET_BUFFER_BINDING, { &ssaoDataBuffer, 1 }, 0, ssaoStorage->GetBuffer()->GetSizeStored()); auto viewSamplerLayout = ssaoStorage->GetViewSamplerLayout(); @@ -273,9 +273,9 @@ namespace SHADE // Create debug draw pipeline debugDrawPipeline = createDebugDrawPipeline(debugDrawNode->GetRenderpass(), debugDrawSubpass); SET_VK_OBJ_NAME(device, vk::ObjectType::ePipeline, debugDrawPipeline->GetVkPipeline(), "[Pipeline] Debug Draw"); - SET_VK_OBJ_NAME(device, vk::ObjectType::ePipelineLayout, debugDrawPipeline->GetPipelineLayout()->GetVkPipelineLayout(), "[Pipeline] Debug Draw Pipeline Layout"); + SET_VK_OBJ_NAME(device, vk::ObjectType::ePipelineLayout, debugDrawPipeline->GetPipelineLayout()->GetVkPipelineLayout(), "[Pipeline Layout] Debug Draw Pipeline Layout"); debugDrawDepthPipeline = createDebugDrawPipeline(debugDrawNodeDepth->GetRenderpass(), debugDrawDepthSubpass); - SET_VK_OBJ_NAME(device, vk::ObjectType::ePipeline, debugDrawDepthPipeline->GetVkPipeline(), "[Pipeline] Debug Draw with Depth Test"); + SET_VK_OBJ_NAME(device, vk::ObjectType::ePipeline, debugDrawDepthPipeline->GetVkPipeline(), "[Pipeline Layout] Debug Draw with Depth Test"); SET_VK_OBJ_NAME(device, vk::ObjectType::ePipelineLayout, debugDrawDepthPipeline->GetPipelineLayout()->GetVkPipelineLayout(), "[Pipeline] Debug Draw with Depth Test Pipeline Layout"); } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp index 8cdff55a..b567c55d 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp @@ -32,13 +32,15 @@ namespace SHADE }; // Create pipeline layout from parameters - pipelineLayout = graphStorage->logicalDevice->CreatePipelineLayout (pipelineLayoutParams); + pipelineLayout = graphStorage->logicalDevice->CreatePipelineLayout(pipelineLayoutParams); // Create the compute pipeline computePipeline = graphStorage->logicalDevice->CreateComputePipeline(pipelineLayout); // and construct it computePipeline->ConstructPipeline(); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::ePipelineLayout, pipelineLayout->GetVkPipelineLayout(), "[Compute Pipeline Layout] " + nodeName); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::ePipeline, computePipeline->GetVkPipeline(), "[Compute Pipeline] " + nodeName); // save the resources resources = std::move (subpassComputeResources); From ed3131143d5f30c0da16b9180d777ab09c498c87 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Mon, 7 Nov 2022 23:51:48 +0800 Subject: [PATCH 095/113] Added debug labels for samplers and fixed issue with compute pipeline names not being labelled correctly --- .../MiddleEnd/Interface/SHGraphicsSystem.cpp | 12 ++++++++---- .../RenderGraph/SHRenderGraphNodeCompute.cpp | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 3c9ddfaa..15c8ec5d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -758,14 +758,18 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Handle SHGraphicsSystem::AddTexture(const SHTextureAsset& texAsset) { - auto sampler = samplerCache.GetSampler(device, SHVkSamplerParams { .maxLod = static_cast(texAsset.mipOffsets.size()) }); - return texLibrary.Add(texAsset, sampler); + const int MIPS = texAsset.mipOffsets.size(); + auto sampler = samplerCache.GetSampler(device, SHVkSamplerParams { .maxLod = static_cast(MIPS) }); + SET_VK_OBJ_NAME(device, vk::ObjectType::eSampler, sampler->GetVkSampler(), "[Sampler] Mips " + std::to_string(MIPS)); + return texLibrary.Add(texAsset, sampler); } SHADE::Handle SHGraphicsSystem::AddTexture(uint32_t pixelCount, const SHTexture::PixelChannel* const pixelData, uint32_t width, uint32_t height, SHTexture::TextureFormat format, std::vector mipOffsets) { - auto sampler = samplerCache.GetSampler(device, SHVkSamplerParams{ .maxLod = static_cast(mipOffsets.size()) }); - return texLibrary.Add(pixelCount, pixelData, width, height, format, mipOffsets, sampler); + const int MIPS = mipOffsets.size(); + auto sampler = samplerCache.GetSampler(device, SHVkSamplerParams{ .maxLod = static_cast(MIPS) }); + SET_VK_OBJ_NAME(device, vk::ObjectType::eSampler, sampler->GetVkSampler(), "[Sampler] Mips " + std::to_string(MIPS)); + return texLibrary.Add(pixelCount, pixelData, width, height, format, mipOffsets, sampler); } void SHGraphicsSystem::RemoveTexture(Handle tex) diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp index b567c55d..f4a103f7 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraphNodeCompute.cpp @@ -39,8 +39,8 @@ namespace SHADE // and construct it computePipeline->ConstructPipeline(); - SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::ePipelineLayout, pipelineLayout->GetVkPipelineLayout(), "[Compute Pipeline Layout] " + nodeName); - SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::ePipeline, computePipeline->GetVkPipeline(), "[Compute Pipeline] " + nodeName); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::ePipelineLayout, pipelineLayout->GetVkPipelineLayout(), "[Compute Pipeline Layout] " + name); + SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::ePipeline, computePipeline->GetVkPipeline(), "[Compute Pipeline] " + name); // save the resources resources = std::move (subpassComputeResources); From 8fbd32a1449b64038dc1f6f43f7cc9557092f755 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Tue, 8 Nov 2022 00:46:09 +0800 Subject: [PATCH 096/113] Added Relative Sizes to Shapes --- Assets/Scenes/M2Scene.shade | 59 ++++++++----------- SHADE_Application/src/Scenes/SBTestScene.cpp | 2 +- .../Inspector/SHEditorComponentView.hpp | 14 +---- .../src/Math/Geometry/SHBoundingBox.cpp | 38 ++++++++---- .../src/Math/Geometry/SHBoundingBox.h | 29 +++++---- .../src/Math/Geometry/SHBoundingSphere.cpp | 40 +++++++++---- .../src/Math/Geometry/SHBoundingSphere.h | 17 ++++-- .../Components/SHColliderComponent.cpp | 52 ++++++++++++++-- .../Physics/Components/SHColliderComponent.h | 14 +++-- SHADE_Engine/src/Physics/SHCollisionShape.cpp | 57 +++++++----------- SHADE_Engine/src/Physics/SHPhysicsObject.cpp | 8 +-- SHADE_Engine/src/Physics/SHPhysicsSystem.cpp | 13 ++-- .../src/Serialization/SHYAMLConverters.h | 6 +- SHADE_Managed/src/Components/Collider.cxx | 8 +-- 14 files changed, 211 insertions(+), 146 deletions(-) diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade index 443d4a87..6086eed9 100644 --- a/Assets/Scenes/M2Scene.shade +++ b/Assets/Scenes/M2Scene.shade @@ -50,7 +50,7 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 24.7399445, y: 0.25, z: 8.75} + Half Extents: {x: 1, y: 1, z: 1} Friction: 0.400000006 Bounciness: 0 Density: 1 @@ -90,23 +90,7 @@ Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0.5, z: 0} - Scripts: - - Type: PlayerController - drag: 2 - currentState: 0 - maxMoveVel: 2 - moveForce: 50 - sprintMultiplier: 2 - rotationFactorPerFrame: 1 - maxJumpHeight: 4 - maxJumpTime: 0.75 - fallMultipler: 2 - lightMultiper: 0.75 - mediumMultiper: 0.5 - heavyMultiper: 0.25 - - Type: PickAndThrow - throwForce: [200, 300, 200] - item: 5 + Scripts: ~ - EID: 3 Name: Default IsActive: true @@ -126,12 +110,7 @@ Translate: {x: 0, y: 0, z: 0} Rotate: {x: 0, y: 0, z: 0} Scale: {x: 1, y: 1, z: 1} - Scripts: - - Type: SHADE_Scripting.ThirdPersonCamera - armLength: 2 - turnSpeedPitch: 0.300000012 - turnSpeedYaw: 0.5 - pitchClamp: 45 + Scripts: ~ - EID: 9 Name: Default IsActive: true @@ -186,9 +165,7 @@ Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0.5, z: 0} - Scripts: - - Type: Item - currCategory: 0 + Scripts: ~ - EID: 6 Name: AI IsActive: true @@ -223,14 +200,7 @@ Bounciness: 0 Density: 1 Position Offset: {x: 0, y: 0.5, z: 0} - Scripts: - - Type: AIPrototype - movementForceMultiplier: 100 - patrolSpeed: 0.400000006 - chaseSpeed: 0.800000012 - distanceToCapture: 1.20000005 - distanceToStartChase: 2 - distanceToEndChase: 2.5 + Scripts: ~ - EID: 7 Name: Default IsActive: true @@ -256,4 +226,23 @@ Color: {x: 1, y: 1, z: 1, w: 1} Layer: 4294967295 Strength: 0.25 + Scripts: ~ +- EID: 10 + Name: Default + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: 2.45315814, z: -5} + Rotate: {x: -0, y: 0, z: -0} + Scale: {x: 2, y: 1, z: 1} + Collider Component: + Colliders: + - Is Trigger: false + Type: Box + Half Extents: {x: 2, y: 1, z: 1} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0, z: 0} Scripts: ~ \ No newline at end of file diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index ecacdc79..8281f114 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); - auto* floorBox = floorCollider.AddBoundingBox(); + floorCollider.AddBoundingBox(); // Create blank entity with a script //testObj = SHADE::SHEntityManager::CreateEntity(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp index d7ae56c1..85d10c1a 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp +++ b/SHADE_Engine/src/Editor/EditorWindow/Inspector/SHEditorComponentView.hpp @@ -224,9 +224,6 @@ namespace SHADE if (!component) return; - // Get transform component for extrapolating relative sizes - auto* transformComponent = SHComponentManager::GetComponent_s(component->GetEID()); - 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(); @@ -249,28 +246,21 @@ namespace SHADE if (collider->GetType() == SHCollisionShape::Type::BOX) { SHEditorWidgets::BeginPanel(std::format("{} Box #{}", ICON_FA_CUBE, i).data(), { ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y }); - SHEditorWidgets::CheckBox("Is Trigger", [collider]() {return collider->IsTrigger(); }, [collider](bool const& value) {collider->SetIsTrigger(value); }, "Is Trigger"); auto box = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragVec3 ( "Half Extents", { "X", "Y", "Z" }, - [box, transformComponent] { return (box->GetHalfExtents() * 2.0f) / transformComponent->GetWorldScale(); }, + [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 }); - SHEditorWidgets::CheckBox("Is Trigger", [collider]() {return collider->IsTrigger(); }, [collider](bool const& value) {collider->SetIsTrigger(value); }, "Is Trigger"); auto sphere = reinterpret_cast(collider->GetShape()); SHEditorWidgets::DragFloat ( "Radius", - [sphere, transformComponent] - { - const SHVec3& TF_WORLD_SCALE = transformComponent->GetWorldScale(); - const float MAX_SCALE = SHMath::Max({ TF_WORLD_SCALE.x, TF_WORLD_SCALE.y, TF_WORLD_SCALE.z }); - return (sphere->GetRadius() / MAX_SCALE) * 2.0f; - }, + [sphere] { return sphere->GetRelativeRadius(); }, [collider](float const& value) { collider->SetBoundingSphere(value); }); } else if (collider->GetType() == SHCollisionShape::Type::CAPSULE) diff --git a/SHADE_Engine/src/Math/Geometry/SHBoundingBox.cpp b/SHADE_Engine/src/Math/Geometry/SHBoundingBox.cpp index d0ba2f14..5bbf5e15 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBoundingBox.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHBoundingBox.cpp @@ -25,11 +25,13 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHBoundingBox::SHBoundingBox() noexcept + : RelativeExtents { SHVec3::One } { type = Type::BOX; } SHBoundingBox::SHBoundingBox(const SHVec3& c, const SHVec3& hE) noexcept + : RelativeExtents { SHVec3::One } { type = Type::BOX; @@ -45,16 +47,18 @@ namespace SHADE type = Type::BOX; - Center = rhs.Center; - Extents = rhs.Extents; + Center = rhs.Center; + Extents = rhs.Extents; + RelativeExtents = rhs.RelativeExtents; } SHBoundingBox::SHBoundingBox(SHBoundingBox&& rhs) noexcept { type = Type::BOX; - Center = rhs.Center; - Extents = rhs.Extents; + Center = rhs.Center; + Extents = rhs.Extents; + RelativeExtents = rhs.RelativeExtents; } /*-----------------------------------------------------------------------------------*/ @@ -69,8 +73,9 @@ namespace SHADE } else if (this != &rhs) { - Center = rhs.Center; - Extents = rhs.Extents; + Center = rhs.Center; + Extents = rhs.Extents; + RelativeExtents = rhs.RelativeExtents; } return *this; @@ -84,8 +89,9 @@ namespace SHADE } else { - Center = rhs.Center; - Extents = rhs.Extents; + Center = rhs.Center; + Extents = rhs.Extents; + RelativeExtents = rhs.RelativeExtents; } return *this; @@ -100,11 +106,16 @@ namespace SHADE return Center; } - SHVec3 SHBoundingBox::GetHalfExtents() const noexcept + SHVec3 SHBoundingBox::GetWorldExtents() const noexcept { return Extents; } + const SHVec3& SHBoundingBox::GetRelativeExtents() const noexcept + { + return RelativeExtents; + } + SHVec3 SHBoundingBox::GetMin() const noexcept { return SHVec3{ Center.x - Extents.x, Center.y - Extents.y, Center.z - Extents.z }; @@ -124,9 +135,14 @@ namespace SHADE Center = newCenter; } - void SHBoundingBox::SetHalfExtents(const SHVec3& newHalfExtents) noexcept + void SHBoundingBox::SetWorldExtents(const SHVec3& newWorldExtents) noexcept { - Extents = newHalfExtents; + Extents = newWorldExtents; + } + + void SHBoundingBox::SetRelativeExtents(const SHVec3& newRelativeExtents) noexcept + { + RelativeExtents = newRelativeExtents; } void SHBoundingBox::SetMin(const SHVec3& min) noexcept diff --git a/SHADE_Engine/src/Math/Geometry/SHBoundingBox.h b/SHADE_Engine/src/Math/Geometry/SHBoundingBox.h index 5b3d26d5..e2757c17 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBoundingBox.h +++ b/SHADE_Engine/src/Math/Geometry/SHBoundingBox.h @@ -54,21 +54,23 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] SHVec3 GetHalfExtents() 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 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 SetHalfExtents (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; + 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 */ @@ -89,6 +91,13 @@ namespace SHADE [[nodiscard]] static bool Intersect (const SHBoundingBox& lhs, const SHBoundingBox& rhs) noexcept; [[nodiscard]] static SHBoundingBox BuildFromBoxes (const SHBoundingBox* boxes, size_t numBoxes) noexcept; [[nodiscard]] static SHBoundingBox BuildFromVertices (const SHVec3* vertices, size_t numVertices, size_t stride = 0) noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + SHVec3 RelativeExtents; }; diff --git a/SHADE_Engine/src/Math/Geometry/SHBoundingSphere.cpp b/SHADE_Engine/src/Math/Geometry/SHBoundingSphere.cpp index 62bf12b2..f843a6bb 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBoundingSphere.cpp +++ b/SHADE_Engine/src/Math/Geometry/SHBoundingSphere.cpp @@ -25,11 +25,13 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ SHBoundingSphere::SHBoundingSphere() noexcept + : RelativeRadius { 1.0f } { type = Type::SPHERE; } SHBoundingSphere::SHBoundingSphere(const SHVec3& center, float radius) noexcept + : RelativeRadius { 1.0f } { type = Type::SPHERE; @@ -44,16 +46,18 @@ namespace SHADE type = Type::SPHERE; - Center = rhs.Center; - Radius = rhs.Radius; + Center = rhs.Center; + Radius = rhs.Radius; + RelativeRadius = rhs.RelativeRadius; } SHBoundingSphere::SHBoundingSphere(SHBoundingSphere&& rhs) noexcept { type = Type::SPHERE; - Center = rhs.Center; - Radius = rhs.Radius; + Center = rhs.Center; + Radius = rhs.Radius; + RelativeRadius = rhs.RelativeRadius; } /*-----------------------------------------------------------------------------------*/ @@ -68,8 +72,9 @@ namespace SHADE } else if (this != &rhs) { - Center = rhs.Center; - Radius = rhs.Radius; + Center = rhs.Center; + Radius = rhs.Radius; + RelativeRadius = rhs.RelativeRadius; } return *this; @@ -83,8 +88,9 @@ namespace SHADE } else { - Center = rhs.Center; - Radius = rhs.Radius; + Center = rhs.Center; + Radius = rhs.Radius; + RelativeRadius = rhs.RelativeRadius; } return *this; @@ -99,11 +105,16 @@ namespace SHADE return Center; } - float SHBoundingSphere::GetRadius() const noexcept + float SHBoundingSphere::GetWorldRadius() const noexcept { return Radius; } - + + float SHBoundingSphere::GetRelativeRadius() const noexcept + { + return RelativeRadius; + } + /*-----------------------------------------------------------------------------------*/ /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -113,9 +124,14 @@ namespace SHADE Center = center; } - void SHBoundingSphere::SetRadius(float radius) noexcept + void SHBoundingSphere::SetWorldRadius(float newWorldRadius) noexcept { - Radius = radius; + Radius = newWorldRadius; + } + + void SHBoundingSphere::SetRelativeRadius(float newRelativeRadius) noexcept + { + RelativeRadius = newRelativeRadius; } /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Math/Geometry/SHBoundingSphere.h b/SHADE_Engine/src/Math/Geometry/SHBoundingSphere.h index 001e889b..d94722d6 100644 --- a/SHADE_Engine/src/Math/Geometry/SHBoundingSphere.h +++ b/SHADE_Engine/src/Math/Geometry/SHBoundingSphere.h @@ -48,15 +48,17 @@ namespace SHADE /* Getter Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] SHVec3 GetCenter () const noexcept; - [[nodiscard]] float GetRadius () const noexcept; + [[nodiscard]] SHVec3 GetCenter () const noexcept; + [[nodiscard]] float GetWorldRadius () const noexcept; + [[nodiscard]] float GetRelativeRadius () const noexcept; /*---------------------------------------------------------------------------------*/ /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetCenter (const SHVec3& center) noexcept; - void SetRadius (float radius) noexcept; + void SetCenter (const SHVec3& center) noexcept; + void SetWorldRadius (float newWorldRadius) noexcept; + void SetRelativeRadius (float newRelativeRadius) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ @@ -79,5 +81,12 @@ namespace SHADE [[nodiscard]] static SHBoundingSphere BuildFromSpheres (const SHBoundingSphere* spheres, size_t numSpheres) noexcept; [[nodiscard]] static SHBoundingSphere 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/Components/SHColliderComponent.cpp b/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp index af3965fb..93126fc5 100644 --- a/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp +++ b/SHADE_Engine/src/Physics/Components/SHColliderComponent.cpp @@ -48,6 +48,11 @@ namespace SHADE return orientation.ToEuler(); } + const SHVec3& SHColliderComponent::GetScale() const noexcept + { + return scale; + } + const SHColliderComponent::CollisionShapes& SHColliderComponent::GetCollisionShapes() const noexcept { return collisionShapes; @@ -75,12 +80,46 @@ namespace SHADE } - SHBoundingBox* SHColliderComponent::AddBoundingBox(const SHVec3& halfExtents, const SHVec3& posOffset) noexcept + void SHColliderComponent::RecomputeCollisionShapes() noexcept + { + for (auto& collisionShape : collisionShapes) + { + switch (collisionShape.GetType()) + { + case SHCollisionShape::Type::BOX: + { + auto* box = reinterpret_cast(collisionShape.GetShape()); + 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.GetShape()); + 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 nullptr; + return -1; } static constexpr auto TYPE = SHCollisionShape::Type::BOX; @@ -89,20 +128,21 @@ namespace SHADE collider.entityID = GetEID(); collider.SetPositionOffset(posOffset); + collider.SetRotationOffset(rotOffset); collider.SetBoundingBox(halfExtents); // Notify Physics System system->AddCollisionShape(GetEID(), &collider); - return reinterpret_cast(collider.GetShape()); + return static_cast(collisionShapes.size()) - 1; } - SHBoundingSphere* SHColliderComponent::AddBoundingSphere(float radius, const SHVec3& posOffset) noexcept + int SHColliderComponent::AddBoundingSphere(float radius, const SHVec3& posOffset) noexcept { if (!system) { SHLOG_ERROR("Physics system does not exist, unable to add Sphere Collider!") - return nullptr; + return -1; } static constexpr auto TYPE = SHCollisionShape::Type::SPHERE; @@ -116,7 +156,7 @@ namespace SHADE // Notify Physics System system->AddCollisionShape(GetEID(), &collider); - return reinterpret_cast(collider.GetShape()); + return static_cast(collisionShapes.size()) - 1; } void SHColliderComponent::RemoveCollider(int index) diff --git a/SHADE_Engine/src/Physics/Components/SHColliderComponent.h b/SHADE_Engine/src/Physics/Components/SHColliderComponent.h index ac5e7528..5f9b7a1b 100644 --- a/SHADE_Engine/src/Physics/Components/SHColliderComponent.h +++ b/SHADE_Engine/src/Physics/Components/SHColliderComponent.h @@ -72,6 +72,7 @@ namespace SHADE [[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); @@ -80,13 +81,15 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - void OnCreate () override; - void OnDestroy () override; + void OnCreate () override; + void OnDestroy () override; - void RemoveCollider (int index); + void RecomputeCollisionShapes () noexcept; - SHBoundingBox* AddBoundingBox (const SHVec3& halfExtents = SHVec3::One, const SHVec3& posOffset = SHVec3::Zero) noexcept; - SHBoundingSphere* AddBoundingSphere (float radius = 1.0f, const SHVec3& posOffset = SHVec3::Zero) 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: @@ -98,6 +101,7 @@ namespace SHADE SHVec3 position; SHQuaternion orientation; + SHVec3 scale; CollisionShapes collisionShapes; RTTR_ENABLE() diff --git a/SHADE_Engine/src/Physics/SHCollisionShape.cpp b/SHADE_Engine/src/Physics/SHCollisionShape.cpp index 78ec36f9..c8f8020c 100644 --- a/SHADE_Engine/src/Physics/SHCollisionShape.cpp +++ b/SHADE_Engine/src/Physics/SHCollisionShape.cpp @@ -15,8 +15,8 @@ // Project Headers #include "Math/Geometry/SHBoundingBox.h" #include "Math/Geometry/SHBoundingSphere.h" -#include "Math/Transform/SHTransformComponent.h" #include "Math/SHMathHelpers.h" +#include "Physics/Components/SHColliderComponent.h" #include "Reflection/SHReflectionMetadata.h" namespace SHADE @@ -178,56 +178,43 @@ namespace SHADE { dirty = true; - // Set the half extents relative to transform - SHVec3 worldHalfExtents = halfExtents; + const auto* colliderComponent = SHComponentManager::GetComponent(entityID); + // Set the half extents relative to world scale + const SHVec3 WORLD_EXTENTS = halfExtents * colliderComponent->GetScale() * 0.5f; - const auto* transformComponent = SHComponentManager::GetComponent_s(entityID); - if (transformComponent != nullptr) - worldHalfExtents *= (transformComponent->GetWorldScale() * 0.5f); - - if (type == Type::BOX) - { - auto* box = reinterpret_cast(shape); - box->SetHalfExtents(worldHalfExtents); - } - else + if (type != Type::BOX) { type = Type::BOX; delete shape; - shape = new SHBoundingBox{ positionOffset, worldHalfExtents }; + shape = new SHBoundingBox{ positionOffset, WORLD_EXTENTS }; } + + auto* box = reinterpret_cast(shape); + box->SetWorldExtents(WORLD_EXTENTS); + box->SetRelativeExtents(halfExtents); } void SHCollisionShape::SetBoundingSphere(float radius) { dirty = true; - // Set the radius relative to transform - float worldRadius = radius; + const auto* colliderComponent = SHComponentManager::GetComponent(entityID); + // Set the radius relative to world scale + const SHVec3 WORLD_SCALE = colliderComponent->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; - const auto* transformComponent = SHComponentManager::GetComponent_s(entityID); - if (transformComponent != nullptr) - { - const SHVec3 TF_WORLD_SCALE = transformComponent->GetWorldScale(); - const float MAX_SCALE = SHMath::Max({ TF_WORLD_SCALE.x, TF_WORLD_SCALE.y, TF_WORLD_SCALE.z }); - - worldRadius *= MAX_SCALE * 0.5f; - } - - if (type == Type::SPHERE) - { - auto* sphere = reinterpret_cast(shape); - sphere->SetRadius(worldRadius); - } - else + if (type != Type::SPHERE) { type = Type::SPHERE; delete shape; - shape = new SHBoundingSphere{ positionOffset, worldRadius }; + shape = new SHBoundingSphere{ positionOffset, WORLD_RADIUS }; } - + + auto* sphere = reinterpret_cast(shape); + sphere->SetWorldRadius(WORLD_RADIUS); } void SHCollisionShape::SetIsTrigger(bool trigger) noexcept @@ -299,14 +286,14 @@ namespace SHADE { const auto* RHS_BOX = reinterpret_cast(rhs); - shape = new SHBoundingBox{ positionOffset, RHS_BOX->GetHalfExtents() }; + shape = new SHBoundingBox{ positionOffset, RHS_BOX->GetWorldExtents() }; break; } case Type::SPHERE: { const auto* RHS_SPHERE = reinterpret_cast(rhs); - shape = new SHBoundingSphere{ positionOffset, RHS_SPHERE->GetRadius() }; + shape = new SHBoundingSphere{ positionOffset, RHS_SPHERE->GetWorldRadius() }; break; } default: break; diff --git a/SHADE_Engine/src/Physics/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/SHPhysicsObject.cpp index 5658f304..00c6943b 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsObject.cpp @@ -137,7 +137,7 @@ namespace SHADE case SHCollisionShape::Type::BOX: { const auto* box = reinterpret_cast(collider->GetShape()); - rp3d::BoxShape* newBox = factory->createBoxShape(box->GetHalfExtents()); + rp3d::BoxShape* newBox = factory->createBoxShape(box->GetWorldExtents()); rp3dBody->addCollider(newBox, OFFSETS); break; @@ -145,7 +145,7 @@ namespace SHADE case SHCollisionShape::Type::SPHERE: { const auto* sphere = reinterpret_cast(collider->GetShape()); - rp3d::SphereShape* newSphere = factory->createSphereShape(sphere->GetRadius()); + rp3d::SphereShape* newSphere = factory->createSphereShape(sphere->GetWorldRadius()); rp3dBody->addCollider(newSphere, OFFSETS); break; @@ -193,7 +193,7 @@ namespace SHADE const auto* box = reinterpret_cast(collider.GetShape()); auto* rp3dBoxShape = reinterpret_cast(rp3dCollider->getCollisionShape()); - rp3dBoxShape->setHalfExtents(box->GetHalfExtents()); + rp3dBoxShape->setHalfExtents(box->GetWorldExtents()); break; } @@ -202,7 +202,7 @@ namespace SHADE const auto* sphere = reinterpret_cast(collider.GetShape()); auto* rp3dSphereShape = reinterpret_cast(rp3dCollider->getCollisionShape()); - rp3dSphereShape->setRadius(sphere->GetRadius()); + rp3dSphereShape->setRadius(sphere->GetWorldRadius()); break; } diff --git a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp index 078a5d6a..437b5ff8 100644 --- a/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/SHPhysicsSystem.cpp @@ -395,6 +395,7 @@ namespace SHADE { const auto WORLD_POS = transformComponent->GetWorldPosition(); const auto WORLD_ROT = transformComponent->GetWorldOrientation(); + const auto WORLD_SCL = transformComponent->GetWorldScale(); physicsObject.SetPosition(WORLD_POS); physicsObject.SetOrientation(WORLD_ROT); @@ -409,8 +410,11 @@ namespace SHADE if (colliderComponent) { - colliderComponent->position = WORLD_POS; - colliderComponent->orientation = WORLD_ROT; + colliderComponent->position = WORLD_POS; + colliderComponent->orientation = WORLD_ROT; + colliderComponent->scale = WORLD_SCL; + + colliderComponent->RecomputeCollisionShapes(); } } @@ -744,8 +748,9 @@ namespace SHADE { SHASSERT(colliderComponent != nullptr, "Collider Component was not added to Entity " + std::to_string(ENTITY_ID) + "!"); - colliderComponent->position = transformComponent->GetWorldPosition(); - colliderComponent->orientation = transformComponent->GetWorldOrientation(); + colliderComponent->position = transformComponent->GetWorldPosition(); + colliderComponent->orientation = transformComponent->GetWorldOrientation(); + colliderComponent->scale = transformComponent->GetWorldScale(); if (physicsObject->rp3dBody == nullptr) { diff --git a/SHADE_Engine/src/Serialization/SHYAMLConverters.h b/SHADE_Engine/src/Serialization/SHYAMLConverters.h index 76be74ce..1b93c63a 100644 --- a/SHADE_Engine/src/Serialization/SHYAMLConverters.h +++ b/SHADE_Engine/src/Serialization/SHYAMLConverters.h @@ -131,13 +131,13 @@ namespace YAML case SHCollisionShape::Type::BOX: { auto const bb = reinterpret_cast(rhs.GetShape()); - node[HalfExtents] = bb->GetHalfExtents(); + node[HalfExtents] = bb->GetRelativeExtents(); } break; case SHCollisionShape::Type::SPHERE: { auto const bs = reinterpret_cast(rhs.GetShape()); - node[Radius] = bs->GetRadius(); + node[Radius] = bs->GetRelativeRadius(); } break; case SHCollisionShape::Type::CAPSULE: break; @@ -168,7 +168,7 @@ namespace YAML case SHCollisionShape::Type::BOX: { if (node[HalfExtents].IsDefined()) - rhs.SetBoundingBox(node[HalfExtents].as() * 2.0f); + 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 dc5d27af..0e916b7b 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -55,11 +55,11 @@ namespace SHADE } Vector3 BoxCollider::HalfExtents::get() { - return Convert::ToCLI(getNativeBoundObject().GetHalfExtents()); + return Convert::ToCLI(getNativeBoundObject().GetWorldExtents()); } void BoxCollider::HalfExtents::set(Vector3 value) { - getNativeBoundObject().SetHalfExtents(Convert::ToNative(value)); + getNativeBoundObject().SetWorldExtents(Convert::ToNative(value)); } Vector3 BoxCollider::Min::get() { @@ -103,11 +103,11 @@ namespace SHADE } float SphereCollider::Radius::get() { - return getNativeBoundObject().GetRadius(); + return getNativeBoundObject().GetWorldRadius(); } void SphereCollider::Radius::set(float value) { - getNativeBoundObject().SetRadius(value); + getNativeBoundObject().SetWorldRadius(value); } /*---------------------------------------------------------------------------------*/ From ddd93a85f4f7791168b2b122f124a579fc1d1725 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 00:53:47 +0800 Subject: [PATCH 097/113] Added support for null GameObjects (loading is buggy) --- Assets/Scenes/M2Scene.shade | 5 +- SHADE_Engine/src/Editor/SHEditorUI.cpp | 11 +- SHADE_Engine/src/Editor/SHEditorUI.h | 6 +- SHADE_Managed/src/Editor/Editor.cxx | 7 +- SHADE_Managed/src/Engine/GameObject.cxx | 104 +++++++++++++----- SHADE_Managed/src/Engine/GameObject.hxx | 18 ++- .../src/Serialisation/ReflectionUtilities.cxx | 6 +- 7 files changed, 119 insertions(+), 38 deletions(-) diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade index 443d4a87..585582e4 100644 --- a/Assets/Scenes/M2Scene.shade +++ b/Assets/Scenes/M2Scene.shade @@ -256,4 +256,7 @@ Color: {x: 1, y: 1, z: 1, w: 1} Layer: 4294967295 Strength: 0.25 - Scripts: ~ \ No newline at end of file + Scripts: + - Type: PickAndThrow + throwForce: [100, 200, 100] + item: 1 \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/SHEditorUI.cpp b/SHADE_Engine/src/Editor/SHEditorUI.cpp index cc63c565..49cfbfd6 100644 --- a/SHADE_Engine/src/Editor/SHEditorUI.cpp +++ b/SHADE_Engine/src/Editor/SHEditorUI.cpp @@ -288,7 +288,7 @@ namespace SHADE return CHANGED; } - bool SHEditorUI::InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered) + bool SHEditorUI::InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered, bool alwaysNull) { ImGui::Text(label.c_str()); if (isHovered) @@ -296,7 +296,7 @@ namespace SHADE ImGui::SameLine(); SHEntity* entity = SHEntityManager::GetEntityByID(value); std::ostringstream oss; - if (entity) + if (!alwaysNull && entity) { oss << value << ": " << entity->name; } @@ -314,6 +314,13 @@ namespace SHADE SHDragDrop::EndTarget(); } } + ImGui::SameLine(); + if (ImGui::Button("Clear")) + { + value = MAX_EID; + changed = true; + } + return changed; } diff --git a/SHADE_Engine/src/Editor/SHEditorUI.h b/SHADE_Engine/src/Editor/SHEditorUI.h index d4104639..4e8f4400 100644 --- a/SHADE_Engine/src/Editor/SHEditorUI.h +++ b/SHADE_Engine/src/Editor/SHEditorUI.h @@ -313,8 +313,12 @@ namespace SHADE /// Label used to identify this widget. /// Reference to the variable to store the result. /// + /// If set, the field displayed will always be blank regardless of specified + /// GameObject. + /// /// True if the value was changed. - static bool InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered = nullptr); + static bool InputGameObjectField(const std::string& label, uint32_t& value, bool* isHovered = nullptr, bool alwaysNull = false); /// /// Creates a combo box for enumeration input. /// diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx index 55fe91da..2afe9697 100644 --- a/SHADE_Managed/src/Editor/Editor.cxx +++ b/SHADE_Managed/src/Editor/Editor.cxx @@ -290,9 +290,14 @@ namespace SHADE { GameObject gameObj = safe_cast(field->GetValue(object)); uint32_t entityId = gameObj.GetEntity(); - if (SHEditorUI::InputGameObjectField(Convert::ToNative(field->Name), entityId, &isHovered)) + if (SHEditorUI::InputGameObjectField(Convert::ToNative(field->Name), entityId, &isHovered, !gameObj)) { GameObject newVal = GameObject(entityId); + if (entityId != MAX_EID) + { + // Null GameObject set + newVal = GameObject(entityId); + } field->SetValue(object, newVal); registerUndoAction(object, field, newVal, gameObj); } diff --git a/SHADE_Managed/src/Engine/GameObject.cxx b/SHADE_Managed/src/Engine/GameObject.cxx index 7ceabfed..9f15c6c9 100644 --- a/SHADE_Managed/src/Engine/GameObject.cxx +++ b/SHADE_Managed/src/Engine/GameObject.cxx @@ -6,9 +6,9 @@ \brief Contains the definition of the functions for the GameObject managed class. Note: This file is written in C++17/CLI. - + Copyright (C) 2021 DigiPen Institute of Technology. -Reproduction or disclosure of this file or its contents without the prior written consent +Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ // Precompiled Headers @@ -36,10 +36,12 @@ namespace SHADE void GameObject::Destroy(GameObject obj) { + if (!obj.valid) + throw gcnew System::NullReferenceException("Attempted to destroy a null GameObject."); SHEntityManager::DestroyEntity(static_cast(obj.GetEntity())); } - System::Nullable GameObject::Find(System::String ^ name) + System::Nullable GameObject::Find(System::String^ name) { // Search the GameObjectLibrary for an Entity with the specified name const auto ENTITY_ID = SHEntityManager::GetEntityByName(Convert::ToNative(name)); @@ -50,21 +52,27 @@ namespace SHADE return GameObject(ENTITY_ID); } - + /*---------------------------------------------------------------------------------*/ /* Properties */ /*---------------------------------------------------------------------------------*/ System::String^ GameObject::Name::get() { + if (!valid) + throw gcnew System::NullReferenceException(); return Convert::ToCLI(GetNativeEntity().name); - - } + + } bool GameObject::IsActiveSelf::get() { + if (!valid) + throw gcnew System::NullReferenceException(); return GetNativeEntity().GetActive(); } bool GameObject::IsActiveInHierarchy::get() { + if (!valid) + throw gcnew System::NullReferenceException(); auto node = SHSceneManager::GetCurrentSceneGraph().GetNode(GetEntity()); if (!node) { @@ -75,39 +83,49 @@ namespace SHADE } Entity GameObject::EntityId::get() { + if (!valid) + throw gcnew System::NullReferenceException(); return entity; } GameObject^ GameObject::Parent::get() { - const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); - const auto* ROOT = SCENE_GRAPH.GetRoot(); + if (!valid) + throw gcnew System::NullReferenceException(); + const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); + const auto* ROOT = SCENE_GRAPH.GetRoot(); - const auto* NODE = SCENE_GRAPH.GetNode(entity); - if (NODE == nullptr) - throw gcnew System::InvalidOperationException("Unable to retrieve SceneGraphNode for Entity " + entity.ToString()); + const auto* NODE = SCENE_GRAPH.GetNode(entity); + if (NODE == nullptr) + throw gcnew System::InvalidOperationException("Unable to retrieve SceneGraphNode for Entity " + entity.ToString()); - const auto* PARENT = NODE->GetParent(); - return PARENT != ROOT ? gcnew GameObject(PARENT->GetEntityID()) : nullptr; + const auto* PARENT = NODE->GetParent(); + return PARENT != ROOT ? gcnew GameObject(PARENT->GetEntityID()) : nullptr; } void GameObject::Parent::set(GameObject^ newParent) { - const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); + if (!valid) + throw gcnew System::NullReferenceException(); + const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); - if (newParent == nullptr) - SCENE_GRAPH.SetParent(entity, nullptr); - else - SCENE_GRAPH.SetParent(entity, newParent->EntityId); + if (newParent == nullptr) + SCENE_GRAPH.SetParent(entity, nullptr); + else + SCENE_GRAPH.SetParent(entity, newParent->EntityId); } - + /*---------------------------------------------------------------------------------*/ /* GameObject Property Functions */ /*---------------------------------------------------------------------------------*/ void GameObject::SetName(System::String^ name) { + if (!valid) + throw gcnew System::NullReferenceException(); GetNativeEntity().name = Convert::ToNative(name); } void GameObject::SetActive(bool active) { + if (!valid) + throw gcnew System::NullReferenceException(); GetNativeEntity().SetActive(active); } @@ -117,63 +135,83 @@ namespace SHADE generic T GameObject::AddComponent() { + if (!valid) + throw gcnew System::NullReferenceException(); return ECS::AddComponent(entity); } generic T GameObject::GetComponent() { + if (!valid) + throw gcnew System::NullReferenceException(); return ECS::GetComponent(entity); } generic T GameObject::GetComponentInChildren() { + if (!valid) + throw gcnew System::NullReferenceException(); return ECS::GetComponentInChildren(entity); } generic T GameObject::EnsureComponent() { + if (!valid) + throw gcnew System::NullReferenceException(); return ECS::EnsureComponent(entity); } generic void GameObject::RemoveComponent() { + if (!valid) + throw gcnew System::NullReferenceException(); ECS::RemoveComponent(entity); } - + /*---------------------------------------------------------------------------------*/ /* Script Access Functions */ /*---------------------------------------------------------------------------------*/ generic T GameObject::AddScript() { + if (!valid) + throw gcnew System::NullReferenceException(); return ScriptStore::AddScript(entity); } generic T GameObject::GetScript() { + if (!valid) + throw gcnew System::NullReferenceException(); return ScriptStore::GetScript(entity); } generic T GameObject::GetScriptInChildren() { + if (!valid) + throw gcnew System::NullReferenceException(); return ScriptStore::GetScriptInChildren(entity); } generic System::Collections::Generic::IEnumerable^ GameObject::GetScripts() { + if (!valid) + throw gcnew System::NullReferenceException(); return ScriptStore::GetScripts(entity); } generic void GameObject::RemoveScript() { + if (!valid) + throw gcnew System::NullReferenceException(); ScriptStore::RemoveScript(entity); } @@ -181,20 +219,24 @@ namespace SHADE /* Constructors */ /*---------------------------------------------------------------------------------*/ GameObject::GameObject(const SHEntity& entity) - : entity { entity.GetEID() } - , children{ gcnew System::Collections::ArrayList } + : entity{ entity.GetEID() } + , children{ gcnew System::Collections::ArrayList } + , valid{ true } {} GameObject::GameObject(Entity entity) - : entity { entity } - , children{ gcnew System::Collections::ArrayList } + : entity{ entity } + , children{ gcnew System::Collections::ArrayList } + , valid{ true } {} - + /*---------------------------------------------------------------------------------*/ /* Getters */ /*---------------------------------------------------------------------------------*/ SHEntity& GameObject::GetNativeEntity() { + if (!valid) + throw gcnew System::NullReferenceException(); SHEntity* nativeEntity = SHEntityManager::GetEntityByID(entity); if (nativeEntity == nullptr) throw gcnew System::InvalidOperationException("[GameObject] Unable to obtain native Entity for GameObject."); @@ -202,14 +244,22 @@ namespace SHADE return *nativeEntity; } + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + GameObject::operator bool(GameObject gameObj) + { + return gameObj.valid; + } + /*---------------------------------------------------------------------------------*/ /* IEquatable */ /*---------------------------------------------------------------------------------*/ bool GameObject::Equals(GameObject other) { - return entity == other.entity; + return (!valid && !other.valid) || entity == other.entity; } - + /*---------------------------------------------------------------------------------*/ /* Object */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Engine/GameObject.hxx b/SHADE_Managed/src/Engine/GameObject.hxx index 99296a91..525b6c10 100644 --- a/SHADE_Managed/src/Engine/GameObject.hxx +++ b/SHADE_Managed/src/Engine/GameObject.hxx @@ -29,8 +29,8 @@ namespace SHADE /* Class Definitions */ /*---------------------------------------------------------------------------------*/ /// - /// Lightweight object for an PlushieEngine Entity that allows for easy access - /// to Component and Script operations. + /// Lightweight object for an Entity that allows for easy access to Component and + /// Script operations. /// public value class GameObject : public System::IEquatable { @@ -98,8 +98,8 @@ namespace SHADE ///

!9MAnS#ia3Wk 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 UNbk|?%M4sE@ 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

property GameObject^ Parent { - GameObject^ get(); - void set(GameObject^); + GameObject^ get(); + void set(GameObject^); } /*-----------------------------------------------------------------------------*/ @@ -247,12 +247,22 @@ namespace SHADE /// Native Entity object that this GameObject represents. SHEntity& GetNativeEntity(); + /*-----------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*-----------------------------------------------------------------------------*/ + /// + /// Implicit conversion operator to enable checking if a GameObject is valid. + /// + /// GameObjects to check. + static operator bool(GameObject gameObj); + private: /*-----------------------------------------------------------------------------*/ /* Data Members */ /*-----------------------------------------------------------------------------*/ Entity entity; System::Collections::ArrayList^ children; + bool valid; public: /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx index 4b4e44fc..66ef493a 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx @@ -171,7 +171,7 @@ namespace SHADE else if (fieldInfo->FieldType == GameObject::typeid) { GameObject gameObj = safe_cast(fieldInfo->GetValue(object)); - fieldNode = gameObj.GetEntity(); + fieldNode = gameObj ? gameObj.GetEntity() : MAX_EID; } else // Not any of the supported types { @@ -250,7 +250,9 @@ namespace SHADE } else if (fieldInfo->FieldType == GameObject::typeid) { - fieldInfo->SetValue(object, GameObject(node.as())); + const uint32_t EID = node.as(); + fieldInfo->SetValue(object, EID == MAX_EID ? GameObject() : GameObject(EID)); + Debug::LogError(std::to_string(EID)); } else // Not any of the supported types { From e0481ad8af4587008fe0b6d05c94f1efdaa17ce3 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 13:47:39 +0800 Subject: [PATCH 098/113] Removed debug log --- SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx index 66ef493a..651afb73 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx @@ -252,7 +252,6 @@ namespace SHADE { const uint32_t EID = node.as(); fieldInfo->SetValue(object, EID == MAX_EID ? GameObject() : GameObject(EID)); - Debug::LogError(std::to_string(EID)); } else // Not any of the supported types { From e3369c688d12a1ff1e445b4ed7b51f238e74838c Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 13:49:49 +0800 Subject: [PATCH 099/113] Added comment to document invalid/null GameObject usage --- SHADE_Managed/src/Engine/GameObject.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/SHADE_Managed/src/Engine/GameObject.hxx b/SHADE_Managed/src/Engine/GameObject.hxx index 525b6c10..030b917c 100644 --- a/SHADE_Managed/src/Engine/GameObject.hxx +++ b/SHADE_Managed/src/Engine/GameObject.hxx @@ -31,6 +31,7 @@ namespace SHADE /// /// Lightweight object for an Entity that allows for easy access to Component and /// Script operations. + /// Can be set to a invalid/null GameObject by default construction. /// public value class GameObject : public System::IEquatable { From 1f4a530dccaa3f78db5833d3b494de26b36c9f81 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 14:06:01 +0800 Subject: [PATCH 100/113] Fixed compile error in Release --- SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h index 022d4b62..fc348487 100644 --- a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h +++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h @@ -81,9 +81,8 @@ namespace SHADE //! The push constant data for the command buffer uint8_t pushConstantData[PUSH_CONSTANT_SIZE]; -#ifdef _DEBUG + //! Depth of segmenting of the command buffer (used for debug data) int segmentDepth; -#endif /*-----------------------------------------------------------------------*/ /* PRIVATE MEMBER FUNCTIONS */ From ee814fa61d418428bcf92c381feb33cc361fd147 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 15:02:08 +0800 Subject: [PATCH 101/113] Added support for parameter-less CallbackActions and CallbackEvents --- SHADE_CSharp/src/Events/CallbackAction.cs | 93 ++++++++++++++++ SHADE_CSharp/src/Events/CallbackAction.tt | 8 +- SHADE_CSharp/src/Events/CallbackEvent.cs | 126 ++++++++++++++++++---- SHADE_CSharp/src/Events/CallbackEvent.tt | 24 ++--- TempScriptsFolder/RaccoonSpin.cs | 4 + 5 files changed, 219 insertions(+), 36 deletions(-) diff --git a/SHADE_CSharp/src/Events/CallbackAction.cs b/SHADE_CSharp/src/Events/CallbackAction.cs index 623e4f59..b6082c0c 100644 --- a/SHADE_CSharp/src/Events/CallbackAction.cs +++ b/SHADE_CSharp/src/Events/CallbackAction.cs @@ -34,6 +34,99 @@ namespace SHADE Object TargetObject { get; } } + /// + /// Represents a function call that can be serialised and put togetheer with scripts. + /// This variant accepts functions with 0 parameter. + /// + public class CallbackAction : ICallbackAction + { + #region Properties ------------------------------------------------------------ + /// + public Object TargetObject { get; private set; } + /// + public string TargetMethodName => targetMethod == null ? "" : targetMethod.Name; + /// + public bool IsRuntimeAction => targetAction != null; + #endregion + + #region Fields ------------------------------------------------------------------ + private MethodInfo targetMethod; + private Action targetAction; + private Object[] parameters; + #endregion + + #region Constructors ------------------------------------------------------------ + /// + /// Constructs an empty Callback action. + /// + public CallbackAction() {} + /// + /// Constructs a CallbackAction that represents a call to the specified static + /// method. + /// + /// Method to call. + /// + /// Thrown if a method that is not compatible with the target is specified. The method's + /// source type must match the target's type. + /// + public CallbackAction(MethodInfo method) + { + // No errors, assign + targetMethod = method; + + // Create storage for parameters for calling + parameters = new Object[0]; + } + /// + /// Constructs a CallbackAction that represents a call to a specified member + /// method on the specified target. + /// + /// Object to call the method on. + /// Method to call. + /// + /// Thrown if a method that is not compatible with the target is specified. The method's + /// source type must match the target's type. + /// + public CallbackAction(Object target, MethodInfo method) + { + // Error Checks + if (method.DeclaringType != target.GetType()) + throw new ArgumentException("[CallbackAction] Attempted register an action using an incompatible target object and method."); + + // No errors, assign + TargetObject = target; + targetMethod = method; + + // Create storage for parameters for calling + parameters = new Object[0]; + } + /// + /// Constructs a Callback action based on an action. + /// + /// Action that wraps a function to be called. + public CallbackAction(Action action) + { + targetAction = action; + } + #endregion + + #region Usage Functions --------------------------------------------------------- + /// + /// Invokes the CallbackAction's stored method/action with the specified parameters. + /// + public void Invoke() + { + if (targetAction != null) + { + targetAction.Invoke(); + } + else if (targetMethod != null) + { + _ = targetMethod.Invoke(TargetObject, parameters); + } + } + #endregion + } /// /// Represents a function call that can be serialised and put togetheer with scripts. /// This variant accepts functions with 1 parameter. diff --git a/SHADE_CSharp/src/Events/CallbackAction.tt b/SHADE_CSharp/src/Events/CallbackAction.tt index 34789b67..3fbb7617 100644 --- a/SHADE_CSharp/src/Events/CallbackAction.tt +++ b/SHADE_CSharp/src/Events/CallbackAction.tt @@ -50,12 +50,12 @@ namespace SHADE Object TargetObject { get; } } -<# for (int i = 1; i <= max; ++i) { #> +<# for (int i = 0; i <= max; ++i) { #> /// /// Represents a function call that can be serialised and put togetheer with scripts. /// This variant accepts functions with <#=i#> parameter<# if (i > 1) {#>s<#} #>. /// - public class CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> : ICallbackAction + public class CallbackAction<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #> : ICallbackAction { #region Properties ------------------------------------------------------------ /// @@ -68,7 +68,7 @@ namespace SHADE #region Fields ------------------------------------------------------------------ private MethodInfo targetMethod; - private Action<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> targetAction; + private Action<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #> targetAction; private Object[] parameters; #endregion @@ -121,7 +121,7 @@ namespace SHADE /// Constructs a Callback action based on an action. /// /// Action that wraps a function to be called. - public CallbackAction(Action<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> action) + public CallbackAction(Action<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #> action) { targetAction = action; } diff --git a/SHADE_CSharp/src/Events/CallbackEvent.cs b/SHADE_CSharp/src/Events/CallbackEvent.cs index ec464ba9..2c3cc388 100644 --- a/SHADE_CSharp/src/Events/CallbackEvent.cs +++ b/SHADE_CSharp/src/Events/CallbackEvent.cs @@ -45,6 +45,92 @@ namespace SHADE IEnumerable Actions { get; } } + /// + /// A container of CallbackActions that is correlated to a specific scenario as + /// specified by the user of this class. + /// This variant accepts CallbackEvents with 1 generic parameter. + /// + public class CallbackEvent : ICallbackEvent + { + #region Properties -------------------------------------------------------------- + /// + public IEnumerable Actions => actions; + #endregion + + #region Fields ------------------------------------------------------------------ + private List actions = new List(); + #endregion + + #region Usage Functions --------------------------------------------------------- + /// + public void RegisterAction() + { + actions.Add(new CallbackAction()); + } + /// + public void RegisterAction(ICallbackAction action) + { + // Check if valid action + if (action.GetType() != typeof(CallbackAction)) + { + Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this); + return; + } + + actions.Add(action); + } + /// + /// Adds a CallbackAction into the event. + /// + /// CallbackAction to add. + public void RegisterAction(CallbackAction action) + { + actions.Add(action); + } + /// + /// Constructs and adds a CallbackAction into the event. + /// + /// System.Action to add as a CallbackAction. + public void RegisterAction(Action action) + { + actions.Add(new CallbackAction(action)); + } + /// + /// Constructs and adds a CallbackAction into the event. + /// + /// Object to call the method on. + /// Method to call. + public void RegisterAction(Object target, MethodInfo method) + { + actions.Add(new CallbackAction(target, method)); + } + /// + public void DeregisterAction(ICallbackAction action) + { + if (!actions.Remove(action)) + { + Debug.LogWarning("Attempted to deregister invalid action. Ignored.", this); + } + } + /// + /// Invokes all stored CallbackActions with the specified parameters. + /// + public void Invoke() + { + foreach (CallbackAction action in actions) + { + try + { + action.Invoke(); + } + catch (Exception e) + { + Debug.LogException(e, this); + } + } + } + #endregion + } /// /// A container of CallbackActions that is correlated to a specific scenario as /// specified by the user of this class. @@ -88,7 +174,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -96,7 +182,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -174,7 +260,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -182,7 +268,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -260,7 +346,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -268,7 +354,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -346,7 +432,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -354,7 +440,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -432,7 +518,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -440,7 +526,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -518,7 +604,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -526,7 +612,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -604,7 +690,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -612,7 +698,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -690,7 +776,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -698,7 +784,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -776,7 +862,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -784,7 +870,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. @@ -862,7 +948,7 @@ namespace SHADE actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. public void RegisterAction(Action action) @@ -870,7 +956,7 @@ namespace SHADE actions.Add(new CallbackAction(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. diff --git a/SHADE_CSharp/src/Events/CallbackEvent.tt b/SHADE_CSharp/src/Events/CallbackEvent.tt index 6a545601..66a8b6d9 100644 --- a/SHADE_CSharp/src/Events/CallbackEvent.tt +++ b/SHADE_CSharp/src/Events/CallbackEvent.tt @@ -61,13 +61,13 @@ namespace SHADE IEnumerable Actions { get; } } -<# for (int i = 1; i <= max; ++i) { #> +<# for (int i = 0; i <= max; ++i) { #> /// /// A container of CallbackActions that is correlated to a specific scenario as /// specified by the user of this class. /// This variant accepts CallbackEvents with 1 generic parameter. /// - public class CallbackEvent<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> : ICallbackEvent + public class CallbackEvent<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #> : ICallbackEvent { #region Properties -------------------------------------------------------------- /// @@ -82,13 +82,13 @@ namespace SHADE /// public void RegisterAction() { - actions.Add(new CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>>()); + actions.Add(new CallbackAction<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #>()); } /// public void RegisterAction(ICallbackAction action) { // Check if valid action - if (action.GetType() != typeof(CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>>)) + if (action.GetType() != typeof(CallbackAction<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #>)) { Debug.LogWarning("Attempted to register an invalid CallbackAction type. Ignoring.", this); return; @@ -100,26 +100,26 @@ namespace SHADE /// Adds a CallbackAction into the event. /// /// CallbackAction to add. - public void RegisterAction(CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> action) + public void RegisterAction(CallbackAction<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #> action) { actions.Add(action); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// System.Action to add as a CallbackAction. - public void RegisterAction(Action<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> action) + public void RegisterAction(Action<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #> action) { - actions.Add(new CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>>(action)); + actions.Add(new CallbackAction<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #>(action)); } /// - /// Constructs and adds a CallbackACtion into the event. + /// Constructs and adds a CallbackAction into the event. /// /// Object to call the method on. /// Method to call. public void RegisterAction(Object target, MethodInfo method) { - actions.Add(new CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>>(target, method)); + actions.Add(new CallbackAction<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #>(target, method)); } /// public void DeregisterAction(ICallbackAction action) @@ -132,9 +132,9 @@ namespace SHADE /// /// Invokes all stored CallbackActions with the specified parameters. /// - public void Invoke(<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#> t<#=t#><# if (t != i) { #>, <# } #><# } #>) + public void Invoke(<# if (i != 0) { for (int t = 1; t < i + 1; ++t) { #>T<#=t#> t<#=t#><# if (t != i) { #>, <# } #><# } } #>) { - foreach (CallbackAction<<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>> action in actions) + foreach (CallbackAction<# if (i != 0) { #><<# for (int t = 1; t < i + 1; ++t) { #>T<#=t#><# if (t != i) { #>, <# } #><# } #>><# } #> action in actions) { try { diff --git a/TempScriptsFolder/RaccoonSpin.cs b/TempScriptsFolder/RaccoonSpin.cs index 06b3c163..efdfadeb 100644 --- a/TempScriptsFolder/RaccoonSpin.cs +++ b/TempScriptsFolder/RaccoonSpin.cs @@ -8,6 +8,8 @@ public class RaccoonSpin : Script private float RotateSpeed = 1.0f; private float rotation = 0.0f; [SerializeField] + private CallbackEvent emptyEvent; + [SerializeField] private CallbackEvent testEvent; [SerializeField] private CallbackEvent testEvent3 = new CallbackEvent(); @@ -17,6 +19,8 @@ public class RaccoonSpin : Script protected override void awake() { + emptyEvent = new CallbackEvent(); + emptyEvent.RegisterAction(() => Debug.Log("Empty event action!")); testEvent = new CallbackEvent(); Action action = (x) => Debug.Log($"{x}"); testEvent.RegisterAction(action); From 715699b63bcb9d2d4561fb021f66f8f0574b458c Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 20:18:50 +0800 Subject: [PATCH 102/113] Add interface for C# light class and modified Color to match Unity's interface --- SHADE_Managed/src/Components/Light.cxx | 79 ++++++++++++ SHADE_Managed/src/Components/Light.hxx | 125 +++++++++++++++++++ SHADE_Managed/src/Engine/ECS.cxx | 10 +- SHADE_Managed/src/Graphics/Color.hxx | 160 ++++++++++++------------- SHADE_Managed/src/Utility/Convert.cxx | 10 ++ SHADE_Managed/src/Utility/Convert.hxx | 13 ++ 6 files changed, 308 insertions(+), 89 deletions(-) create mode 100644 SHADE_Managed/src/Components/Light.cxx create mode 100644 SHADE_Managed/src/Components/Light.hxx diff --git a/SHADE_Managed/src/Components/Light.cxx b/SHADE_Managed/src/Components/Light.cxx new file mode 100644 index 00000000..a220c79a --- /dev/null +++ b/SHADE_Managed/src/Components/Light.cxx @@ -0,0 +1,79 @@ +/************************************************************************************//*! +\file Light.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 8, 2022 +\brief Contains the definition of the functions of the managed Light 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Light.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Constructors */ + /*---------------------------------------------------------------------------------*/ + Light::Light(Entity entity) + : Component(entity) + {} + + /*---------------------------------------------------------------------------------*/ + /* Properties */ + /*---------------------------------------------------------------------------------*/ + Vector3 Light::Position::get() + { + return Convert::ToCLI(GetNativeComponent()->GetPosition()); + } + void Light::Position::set(Vector3 value) + { + GetNativeComponent()->SetPosition(Convert::ToNative(value)); + } + Light::Type Light::LightType::get() + { + return static_cast(GetNativeComponent()->GetType()); + } + void Light::LightType::set(Light::Type value) + { + GetNativeComponent()->SetType(static_cast(value)); + } + Vector3 Light::Direction::get() + { + return Convert::ToCLI(GetNativeComponent()->GetDirection()); + } + void Light::Direction::set(Vector3 value) + { + GetNativeComponent()->SetDirection(Convert::ToNative(value)); + } + Color Light::Color::get() + { + return Convert::ToCLI(SHColour(GetNativeComponent()->GetColor())); + } + void Light::Color::set(SHADE::Color value) + { + GetNativeComponent()->SetColor(Convert::ToNative(value)); + } + System::UInt32 Light::CullingMask::get() + { + return GetNativeComponent()->GetCullingMask(); + } + void Light::CullingMask::set(System::UInt32 value) + { + GetNativeComponent()->SetCullingMask(value); + } + float Light::Strength::get() + { + return GetNativeComponent()->GetStrength(); + } + void Light::Strength::set(float value) + { + GetNativeComponent()->SetStrength(value); + } +} diff --git a/SHADE_Managed/src/Components/Light.hxx b/SHADE_Managed/src/Components/Light.hxx new file mode 100644 index 00000000..9abb05d5 --- /dev/null +++ b/SHADE_Managed/src/Components/Light.hxx @@ -0,0 +1,125 @@ +/************************************************************************************//*! +\file Light.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 8, 2022 +\brief Contains the definition of the managed Light class with the declaration + of functions for working with it. + + 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 + +// Project Includes +#include "Components/Component.hxx" +#include "Math/Vector3.hxx" +// External Dependencies +#include "Graphics/MiddleEnd/Lights/SHLightComponent.h" + +namespace SHADE +{ + /// + /// CLR version of the SHADE Engine's SHLightComponent. + /// + public ref class Light : public Component + { + internal: + /*-----------------------------------------------------------------------------*/ + /* Constructors */ + /*-----------------------------------------------------------------------------*/ + /// + /// Constructs a Light Component that represents a native Light component tied to + /// the specified Entity. + /// + /// Entity that this Component will be tied to. + Light(Entity entity); + + public: + /*-----------------------------------------------------------------------------*/ + /* Constants */ + /*-----------------------------------------------------------------------------*/ + /// + /// Supported types of the Light Component. + /// + enum class Type + { + /// + /// Light applied uniformly across the scene at a specified direction. + /// + Directional, + /// + /// Light that originates from a certain point in all directions. + /// Not implemented yet. + /// + Point, + /// + /// Light that originates from a certain point within a angle. + /// Not implemented yet. + /// + Spot, + /// + /// Light applied to all objects. Has no source point. + /// + Ambient + }; + + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Position of the light. Only works for Point Light (unimplemented). + /// + [System::ObsoleteAttribute("Not implemented yet.", true)] + property Vector3 Position + { + Vector3 get(); + void set(Vector3 val); + } + /// + /// Type of lighting that this Light component will apply onto the scene. + /// + property Type LightType + { + Type get(); + void set(Type val); + } + /// + /// Direction of the light. Only applicable for Directional Lights. + /// + property Vector3 Direction + { + Vector3 get(); + void set(Vector3 val); + } + /// + /// Colour of the Light. + /// + property SHADE::Color Color + { + SHADE::Color get(); + void set(SHADE::Color val); + } + /// + /// Culling mask that is used to control what types of Materials would be + /// affected by this Light. + /// + property System::UInt32 CullingMask + { + System::UInt32 get(); + void set(System::UInt32 val); + } + /// + /// Intensity of the Light + /// + property float Strength + { + float get(); + void set(float val); + } + }; +} + diff --git a/SHADE_Managed/src/Engine/ECS.cxx b/SHADE_Managed/src/Engine/ECS.cxx index 3da39394..00c3c182 100644 --- a/SHADE_Managed/src/Engine/ECS.cxx +++ b/SHADE_Managed/src/Engine/ECS.cxx @@ -22,8 +22,8 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include "ECS_Base/Managers/SHEntityManager.h" #include "Math/Transform/SHTransformComponent.h" -#include "Physics\Components\SHColliderComponent.h" -#include "Physics\Components\SHRigidBodyComponent.h" +#include "Physics/Components/SHColliderComponent.h" +#include "Physics/Components/SHRigidBodyComponent.h" #include "Scene/SHSceneManager.h" #include "Scene/SHSceneGraph.h" #include "Tools/SHLog.h" @@ -31,10 +31,11 @@ of DigiPen Institute of Technology is prohibited. #include "Utility/Convert.hxx" #include "Utility/Debug.hxx" #include "Components/Transform.hxx" -#include "Components\RigidBody.hxx" -#include "Components\Collider.hxx" +#include "Components/RigidBody.hxx" +#include "Components/Collider.hxx" #include "Components/Camera.hxx" #include "Components/CameraArm.hxx" +#include "Components/Light.hxx" namespace SHADE { @@ -252,6 +253,7 @@ namespace SHADE componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); componentMap.Add(createComponentSet()); + componentMap.Add(createComponentSet()); } /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Graphics/Color.hxx b/SHADE_Managed/src/Graphics/Color.hxx index d6a46216..64152394 100644 --- a/SHADE_Managed/src/Graphics/Color.hxx +++ b/SHADE_Managed/src/Graphics/Color.hxx @@ -25,95 +25,85 @@ namespace SHADE { public: /*-----------------------------------------------------------------------------*/ - /* Type Definitions */ + /* Properties */ /*-----------------------------------------------------------------------------*/ /// - /// A static class that contains a set of default Colors. + /// Pure black. /// - ref class Defaults abstract sealed + static property Color Black { - public: - /*-------------------------------------------------------------------------*/ - /* Properties */ - /*-------------------------------------------------------------------------*/ - /// - /// Pure black. - /// - static property Color Black - { - Color get() { return Color(0.0f, 0.0f, 0.0f); } - } - /// - /// Light Gray, lighter than gray. - /// - static property Color LightGray - { - Color get() { return Color(0.827451f, 0.827451f, 0.827451f); } - } - /// - /// Gray, halfway between black and white. - /// - static property Color Gray - { - Color get() { return Color(0.5f, 0.5f, 0.5f); } - } - /// - /// Dark Gray, darker than gray. - /// - static property Color DarkGray - { - Color get() { return Color(0.622f, 0.622f, 0.622f); } - } - /// - /// Pure white. - /// - static property Color White - { - Color get() { return Color(1.0f, 1.0f, 1.0f); } - } - /// - /// Pure red. - /// - static property Color Red - { - Color get() { return Color(1.0f, 0.0f, 0.0f); } - } - /// - /// Pure green. - /// - static property Color Green - { - Color get() { return Color(0.0f, 1.0f, 0.0f); } - } - /// - /// Pure blue. - /// - static property Color Blue - { - Color get() { return Color(0.0f, 0.0f, 1.0f); } - } - /// - /// Pure cyan, mix of pure green and blue. - /// - static property Color Cyan - { - Color get() { return Color(0.0f, 1.0f, 1.0f); } - } - /// - /// Pure magenta, mix of pure red and blue. - /// - static property Color Magenta - { - Color get() { return Color(1.0f, 0.0f, 1.0f); } - } - /// - /// Pure yellow, mix of pure red and green. - /// - static property Color Yellow - { - Color get() { return Color(1.0f, 1.0f, 0.0f); } - } - }; + Color get() { return Color(0.0f, 0.0f, 0.0f); } + } + /// + /// Light Gray, lighter than gray. + /// + static property Color LightGray + { + Color get() { return Color(0.827451f, 0.827451f, 0.827451f); } + } + /// + /// Gray, halfway between black and white. + /// + static property Color Gray + { + Color get() { return Color(0.5f, 0.5f, 0.5f); } + } + /// + /// Dark Gray, darker than gray. + /// + static property Color DarkGray + { + Color get() { return Color(0.622f, 0.622f, 0.622f); } + } + /// + /// Pure white. + /// + static property Color White + { + Color get() { return Color(1.0f, 1.0f, 1.0f); } + } + /// + /// Pure red. + /// + static property Color Red + { + Color get() { return Color(1.0f, 0.0f, 0.0f); } + } + /// + /// Pure green. + /// + static property Color Green + { + Color get() { return Color(0.0f, 1.0f, 0.0f); } + } + /// + /// Pure blue. + /// + static property Color Blue + { + Color get() { return Color(0.0f, 0.0f, 1.0f); } + } + /// + /// Pure cyan, mix of pure green and blue. + /// + static property Color Cyan + { + Color get() { return Color(0.0f, 1.0f, 1.0f); } + } + /// + /// Pure magenta, mix of pure red and blue. + /// + static property Color Magenta + { + Color get() { return Color(1.0f, 0.0f, 1.0f); } + } + /// + /// Pure yellow, mix of pure red and green. + /// + static property Color Yellow + { + Color get() { return Color(1.0f, 1.0f, 0.0f); } + } /*-----------------------------------------------------------------------------*/ /* Constructors */ diff --git a/SHADE_Managed/src/Utility/Convert.cxx b/SHADE_Managed/src/Utility/Convert.cxx index 1d89569f..3b1f0f38 100644 --- a/SHADE_Managed/src/Utility/Convert.cxx +++ b/SHADE_Managed/src/Utility/Convert.cxx @@ -72,6 +72,16 @@ namespace SHADE return Ray(ToCLI(vec.position), ToCLI(vec.direction)); } + SHColour Convert::ToNative(Color col) + { + return SHColour(col.r, col.g, col.b, col.a); + } + + Color Convert::ToCLI(const SHColour& vec) + { + return Color(vec.x, vec.y, vec.z, vec.w); + } + /*---------------------------------------------------------------------------------*/ /* String Conversions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Utility/Convert.hxx b/SHADE_Managed/src/Utility/Convert.hxx index d3dca740..666b5062 100644 --- a/SHADE_Managed/src/Utility/Convert.hxx +++ b/SHADE_Managed/src/Utility/Convert.hxx @@ -29,6 +29,8 @@ of DigiPen Institute of Technology is prohibited. #include "Math/Quaternion.hxx" #include "Math/Ray.hxx" #include "Engine/GenericHandle.hxx" +#include "Math/SHColour.h" +#include "Graphics/Color.hxx" namespace SHADE { @@ -104,6 +106,17 @@ namespace SHADE /// The native Vector2 to convert from. /// Managed copy of a native Vector2. static Ray ToCLI(const SHRay& vec); + /// Converts from a managed Color to a native Colour. + ///
+ /// The managed Color to convert from. + /// Native copy of a managed Color. + static SHColour ToNative(Color col); + /// + /// Converts from a native Colour to a managed Color. + /// + /// The native Colour to convert from. + /// Managed copy of a native Colour. + static Color ToCLI(const SHColour& vec); /*-----------------------------------------------------------------------------*/ /* String Conversions */ From e89f5b4b9e0b640b93abe1456cdfd9b24491a777 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 20:49:07 +0800 Subject: [PATCH 103/113] Added OnDrawGizmos for debug draw for scripts --- .../src/Application/SBApplication.cpp | 1 + SHADE_Engine/src/Scripting/SHScriptEngine.cpp | 6 ++++++ SHADE_Engine/src/Scripting/SHScriptEngine.h | 7 +++++++ .../src/Scripting/SHScriptEngineRoutines.cpp | 11 +++++++++++ SHADE_Managed/src/Scripts/Script.cxx | 12 ++++++++++++ SHADE_Managed/src/Scripts/Script.hxx | 17 +++++++++++++++++ SHADE_Managed/src/Scripts/ScriptStore.cxx | 18 ++++++++++++++++++ SHADE_Managed/src/Scripts/ScriptStore.hxx | 4 ++++ 8 files changed, 76 insertions(+) diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index f4102067..6b67dbce 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -106,6 +106,7 @@ namespace Sandbox SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); + SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); SHSystemManager::RegisterRoutine(); diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index 4a73342e..827f45a2 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -379,6 +379,12 @@ namespace SHADE DEFAULT_CSHARP_LIB_NAME, DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", "ExecuteLateUpdate" + ); + csScriptsExecuteDrawGizmos = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".ScriptStore", + "ExecuteOnDrawGizmos" ); csScriptsExecutePhysicsEvents = dotNet.GetFunctionPtr ( diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.h b/SHADE_Engine/src/Scripting/SHScriptEngine.h index 9ddd617a..7d83606e 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.h +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.h @@ -55,6 +55,12 @@ namespace SHADE LateUpdateRoutine(); void Execute(double dt) noexcept override final; }; + class SH_API GizmosDrawRoutine final : public SHSystemRoutine + { + public: + GizmosDrawRoutine(); + void Execute(double dt) noexcept override final; + }; class SH_API FrameCleanUpRoutine final : public SHSystemRoutine { public: @@ -250,6 +256,7 @@ namespace SHADE CsFuncPtr csScriptsExecuteFixedUpdate = nullptr; CsFuncPtr csScriptsExecuteUpdate = nullptr; CsFuncPtr csScriptsExecuteLateUpdate = nullptr; + CsFuncPtr csScriptsExecuteDrawGizmos = nullptr; CsFuncPtr csScriptsExecutePhysicsEvents = nullptr; CsFuncPtr csScriptsFrameCleanUp = nullptr; CsScriptManipFuncPtr csScriptsAdd = nullptr; diff --git a/SHADE_Engine/src/Scripting/SHScriptEngineRoutines.cpp b/SHADE_Engine/src/Scripting/SHScriptEngineRoutines.cpp index a2981c06..699776ca 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngineRoutines.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngineRoutines.cpp @@ -50,6 +50,17 @@ namespace SHADE reinterpret_cast(system)->csScriptsExecuteLateUpdate(); } + /*-----------------------------------------------------------------------------------*/ + /* System Routine Functions - GizmosDrawRoutine */ + /*-----------------------------------------------------------------------------------*/ + SHScriptEngine::GizmosDrawRoutine::GizmosDrawRoutine() + : SHSystemRoutine("Script Engine Gizmos Draw", true) + {} + void SHScriptEngine::GizmosDrawRoutine::Execute(double dt) noexcept + { + reinterpret_cast(system)->csScriptsExecuteDrawGizmos(); + } + /*-----------------------------------------------------------------------------------*/ /* System Routine Functions - FrameCleanUpRoutine */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Scripts/Script.cxx b/SHADE_Managed/src/Scripts/Script.cxx index e476d69d..bd6d7bef 100644 --- a/SHADE_Managed/src/Scripts/Script.cxx +++ b/SHADE_Managed/src/Scripts/Script.cxx @@ -140,6 +140,13 @@ namespace SHADE lateUpdate(); SAFE_NATIVE_CALL_END(this) } + void Script::OnDrawGizmos() + { + SAFE_NATIVE_CALL_BEGIN + OnGizmosDrawOverriden = true; + onDrawGizmos(); + SAFE_NATIVE_CALL_END(this) + } void Script::OnDestroy() { SAFE_NATIVE_CALL_BEGIN @@ -194,6 +201,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Script::Script(GameObject gameObj) : owner { gameObj } + , OnGizmosDrawOverriden { false } {} /*---------------------------------------------------------------------------------*/ @@ -210,6 +218,10 @@ namespace SHADE void Script::fixedUpdate() {} void Script::update() {} void Script::lateUpdate() {} + void Script::onDrawGizmos() + { + OnGizmosDrawOverriden = false; + } void Script::onDestroy() {} /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Scripts/Script.hxx b/SHADE_Managed/src/Scripts/Script.hxx index afeaa8a0..bbe36784 100644 --- a/SHADE_Managed/src/Scripts/Script.hxx +++ b/SHADE_Managed/src/Scripts/Script.hxx @@ -164,6 +164,14 @@ namespace SHADE static operator bool(Script^ s); internal: + /*-----------------------------------------------------------------------------*/ + /* Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// If true, the OnGizmosDraw function was overridden. + /// + bool OnGizmosDrawOverriden; + /*-----------------------------------------------------------------------------*/ /* "All-Time" Lifecycle Functions */ /*-----------------------------------------------------------------------------*/ @@ -208,6 +216,11 @@ namespace SHADE /// void LateUpdate(); /// + /// Used to call onDrawGizmos(). This should be called just before rendering + /// the scene. This will only be called when working in the editor. + /// + void OnDrawGizmos(); + /// /// Used to call onDestroy(). This should be called at the end of the frame /// where the attached GameObject or this script is destroyed directly or /// indirectly due to destruction of the owner. @@ -308,6 +321,10 @@ namespace SHADE /// virtual void lateUpdate(); /// + /// Called every frame just before rendering but only if working in the editor. + /// + virtual void onDrawGizmos(); + /// /// Called just before the end of the frame where the attached GameObject or /// this script is destroyed directly or indirectly due to destruction of the /// owner. diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index 407d0fa8..a90b4f12 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -478,6 +478,24 @@ namespace SHADE SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") } + void ScriptStore::ExecuteOnDrawGizmos() + { + SAFE_NATIVE_CALL_BEGIN + for each (System::Collections::Generic::KeyValuePair entity in scripts) + { + // Check active state + if (!isEntityActive(entity.Key)) + continue; + + // Update each script + for each (Script^ script in entity.Value) + { + script->OnDrawGizmos(); + } + } + SAFE_NATIVE_CALL_END_N("SHADE_Managed.ScriptStore") + } + void ScriptStore::ExecuteCollisionFunctions() { SAFE_NATIVE_CALL_BEGIN diff --git a/SHADE_Managed/src/Scripts/ScriptStore.hxx b/SHADE_Managed/src/Scripts/ScriptStore.hxx index a4c6e824..c151392f 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.hxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.hxx @@ -234,6 +234,10 @@ namespace SHADE /// static void ExecuteLateUpdate(); /// + /// Executes OnDrawGizmos() for all scripts. + /// + static void ExecuteOnDrawGizmos(); + /// /// Executes OnCollision*() and OnTrigger*() for all scripts. /// static void ExecuteCollisionFunctions(); From 41b7cb842cdd50cadeb60de0f049c0903a34f820 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Tue, 8 Nov 2022 21:31:53 +0800 Subject: [PATCH 104/113] Added C# Gizmos class interface --- .../MiddleEnd/Interface/SHDebugDrawSystem.cpp | 2 +- SHADE_Managed/src/Utility/Gizmos.cxx | 23 ++++++++++ SHADE_Managed/src/Utility/Gizmos.hxx | 45 +++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 SHADE_Managed/src/Utility/Gizmos.cxx create mode 100644 SHADE_Managed/src/Utility/Gizmos.hxx diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp index 883e4894..daa6a23d 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHDebugDrawSystem.cpp @@ -323,7 +323,7 @@ namespace SHADE static const SHMeshData SPHERE = SHPrimitiveGenerator::Sphere(); for (const auto& idx : SPHERE.Indices) { - spherePoints.emplace_back(SPHERE.VertexPositions[idx]); + spherePoints.emplace_back(SPHERE.VertexPositions[idx] * radius); } } drawLineSet(storage, color, spherePoints.begin(), spherePoints.end()); diff --git a/SHADE_Managed/src/Utility/Gizmos.cxx b/SHADE_Managed/src/Utility/Gizmos.cxx new file mode 100644 index 00000000..47d86251 --- /dev/null +++ b/SHADE_Managed/src/Utility/Gizmos.cxx @@ -0,0 +1,23 @@ +/************************************************************************************//*! +\file Gizmos.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 8, 2022 +\brief Contains the definition of the functions for the Convert managed static + 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. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Gizmos.hxx" +// External Dependencies + +namespace SHADE +{ +} diff --git a/SHADE_Managed/src/Utility/Gizmos.hxx b/SHADE_Managed/src/Utility/Gizmos.hxx new file mode 100644 index 00000000..f0b082d7 --- /dev/null +++ b/SHADE_Managed/src/Utility/Gizmos.hxx @@ -0,0 +1,45 @@ +/************************************************************************************//*! +\file Gizmos.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 8, 2022 +\brief Contains the definition of the Gizmos static class and the + declaration of its functions. + + 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 + +// Project Includes +#include "Math/Vector3.hxx" +#include "Graphics/Color.hxx" + +namespace SHADE +{ + /// + /// Provides functions for implementing debug drawing. + /// + public value class Gizmos abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Debug Draw Functions */ + /*-----------------------------------------------------------------------------*/ + static void DrawLine(Vector3 from, Vector3 to); + static void DrawLine(Vector3 from, Vector3 to, Color color); + static void DrawWireCube(Vector3 center, Vector3 extents); + static void DrawWireCube(Vector3 center, Vector3 extents, Color color); + static void DrawWireSphere(Vector3 center, float radius); + static void DrawWireSphere(Vector3 center, float radius, Color color); + + private: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + Color defaultColor; + }; +} From 349f4a875b48cfae33226964414aab8660b307aa Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 9 Nov 2022 10:22:11 +0800 Subject: [PATCH 105/113] Added full implementation of Gizmos class --- SHADE_Managed/src/Utility/Gizmos.cxx | 46 +++++++++++++++++++++ SHADE_Managed/src/Utility/Gizmos.hxx | 60 ++++++++++++++++++++++++++-- TempScriptsFolder/RaccoonShowcase.cs | 6 +++ 3 files changed, 108 insertions(+), 4 deletions(-) diff --git a/SHADE_Managed/src/Utility/Gizmos.cxx b/SHADE_Managed/src/Utility/Gizmos.cxx index 47d86251..21636a5d 100644 --- a/SHADE_Managed/src/Utility/Gizmos.cxx +++ b/SHADE_Managed/src/Utility/Gizmos.cxx @@ -16,8 +16,54 @@ of DigiPen Institute of Technology is prohibited. #include "SHpch.h" // Primary Header #include "Gizmos.hxx" +#include "Convert.hxx" +#include "Tools/SHDebugDraw.h" // External Dependencies namespace SHADE { + /*---------------------------------------------------------------------------------*/ + /* Public Properties */ + /*---------------------------------------------------------------------------------*/ + Color Gizmos::Color::get() + { + return defaultColor; + } + void Gizmos::Color::set(SHADE::Color color) + { + defaultColor = color; + } + + /*---------------------------------------------------------------------------------*/ + /* Debug Draw Functions */ + /*---------------------------------------------------------------------------------*/ + void Gizmos::DrawLine(Vector3 from, Vector3 to) + { + DrawLine(from, to, defaultColor); + } + + void Gizmos::DrawLine(Vector3 from, Vector3 to, SHADE::Color color) + { + SHDebugDraw::Line(Convert::ToNative(color), Convert::ToNative(from), Convert::ToNative(to)); + } + + void Gizmos::DrawWireCube(Vector3 center, Vector3 extents) + { + DrawWireCube(center, extents, defaultColor); + } + + void Gizmos::DrawWireCube(Vector3 center, Vector3 extents, SHADE::Color color) + { + SHDebugDraw::Cube(Convert::ToNative(color), Convert::ToNative(center), Convert::ToNative(extents)); + } + + void Gizmos::DrawWireSphere(Vector3 center, float radius) + { + DrawWireSphere(center, radius, defaultColor); + } + + void Gizmos::DrawWireSphere(Vector3 center, float radius, SHADE::Color color) + { + SHDebugDraw::Sphere(Convert::ToNative(color), Convert::ToNative(center), radius); + } } diff --git a/SHADE_Managed/src/Utility/Gizmos.hxx b/SHADE_Managed/src/Utility/Gizmos.hxx index f0b082d7..1878d867 100644 --- a/SHADE_Managed/src/Utility/Gizmos.hxx +++ b/SHADE_Managed/src/Utility/Gizmos.hxx @@ -26,20 +26,72 @@ namespace SHADE public value class Gizmos abstract sealed { public: + /*-----------------------------------------------------------------------------*/ + /* Public Properties */ + /*-----------------------------------------------------------------------------*/ + /// + /// Default colour that will be used for drawing debug primitives if the color + /// parameter is not specified. + /// + static property Color Color + { + SHADE::Color get(); + void set(SHADE::Color color); + } /*-----------------------------------------------------------------------------*/ /* Debug Draw Functions */ /*-----------------------------------------------------------------------------*/ + /// + /// Renders a line between two points in world space. + /// Uses Color to render. + /// + /// First point of the line. + /// Second point of the line. static void DrawLine(Vector3 from, Vector3 to); - static void DrawLine(Vector3 from, Vector3 to, Color color); + /// + /// Renders a line between two points in world space. + /// + /// First point of the line. + /// Second point of the line. + /// Colour of the line. + static void DrawLine(Vector3 from, Vector3 to, SHADE::Color color); + /// + /// Renders a wireframe cube centered around the position specified in world + /// space. + /// Uses Color to render. + /// + /// Position where the cube wil be centered at. + /// Size of the rendered cube. static void DrawWireCube(Vector3 center, Vector3 extents); - static void DrawWireCube(Vector3 center, Vector3 extents, Color color); + /// + /// Renders a wireframe cube centered around the position specified in world + /// space. + /// + /// Position where the cube wil be centered at. + /// Size of the rendered cube. + /// Colour of the cube. + static void DrawWireCube(Vector3 center, Vector3 extents, SHADE::Color color); + /// + /// Renders a wireframe sphere centered around the position specified in world + /// space. + /// Uses Color to render. + /// + /// Position where the sphere wil be centered at. + /// Radius of the rendered sphere. static void DrawWireSphere(Vector3 center, float radius); - static void DrawWireSphere(Vector3 center, float radius, Color color); + /// + /// Renders a wireframe sphere centered around the position specified in world + /// space. + /// + /// Position where the sphere wil be centered at. + /// Radius of the rendered sphere. + /// Colour of the sphere. + static void DrawWireSphere(Vector3 center, float radius, SHADE::Color color); private: /*-----------------------------------------------------------------------------*/ /* Data Members */ /*-----------------------------------------------------------------------------*/ - Color defaultColor; + static SHADE::Color defaultColor = SHADE::Color::White; }; } diff --git a/TempScriptsFolder/RaccoonShowcase.cs b/TempScriptsFolder/RaccoonShowcase.cs index da0b89d2..4583a699 100644 --- a/TempScriptsFolder/RaccoonShowcase.cs +++ b/TempScriptsFolder/RaccoonShowcase.cs @@ -36,4 +36,10 @@ public class RaccoonShowcase : Script //Transform.LocalRotation = new Vector3(0.0f, rotation, 0.0f); //Transform.LocalScale = new Vector3(System.Math.Abs(System.Math.Sin(scale.x)) * originalScale, System.Math.Abs(System.Math.Cos(scale.y)) * originalScale, System.Math.Abs(System.Math.Sin(scale.z)) * originalScale); } + + protected override void onDrawGizmos() + { + Gizmos.DrawLine(new Vector3(-1.0f, 0.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); + Gizmos.DrawLine(new Vector3(-1.0f, 1.0f, 0.0f), new Vector3(1.0f, 1.0f, 0.0f), Color.Red); + } } \ No newline at end of file From a3fe98317dc4be18cbfa0b035062b75ac8abef38 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 9 Nov 2022 10:31:30 +0800 Subject: [PATCH 106/113] Fixed performance issue in scripts editor when multiple attributes are applied onto script fields --- SHADE_Managed/src/Editor/Editor.cxx | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx index 2afe9697..54200c1e 100644 --- a/SHADE_Managed/src/Editor/Editor.cxx +++ b/SHADE_Managed/src/Editor/Editor.cxx @@ -414,19 +414,10 @@ namespace SHADE generic Attribute Editor::hasAttribute(System::Reflection::FieldInfo^ field) { - array^ attributes = field->GetCustomAttributes(true); - for each (System::Object^ attrib in attributes) + array^ attributes = field->GetCustomAttributes(Attribute::typeid, false); + if (attributes->Length > 0) { - try - { - Attribute attribute = safe_cast(attrib); - if (attribute != nullptr) - return attribute; - } - catch (System::InvalidCastException^) - { - continue; - } + return safe_cast(attributes[0]); } // Failed to find return Attribute{}; From bbe8622d1e20c51e2f40499795e7d3bdcad5945f Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 9 Nov 2022 14:45:08 +0800 Subject: [PATCH 107/113] De-plushied SHADE_Managed comments --- SHADE_Managed/src/Components/Component.hxx | 2 +- SHADE_Managed/src/Editor/Editor.hxx | 6 +++--- SHADE_Managed/src/Engine/EngineInterface.hxx | 2 +- SHADE_Managed/src/Engine/GameObject.hxx | 2 +- SHADE_Managed/src/Math/Vector2.cxx | 2 +- SHADE_Managed/src/Math/Vector3.cxx | 2 +- SHADE_Managed/src/Math/Vector3.hxx | 3 ++- SHADE_Managed/src/Scripts/Script.cxx | 3 +-- SHADE_Managed/src/Scripts/ScriptStore.hxx | 2 +- SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.cxx | 2 +- SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.hxx | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/SHADE_Managed/src/Components/Component.hxx b/SHADE_Managed/src/Components/Component.hxx index 5ffa3952..e52ab3a7 100644 --- a/SHADE_Managed/src/Components/Component.hxx +++ b/SHADE_Managed/src/Components/Component.hxx @@ -97,7 +97,7 @@ namespace SHADE /// /// Removes all Scripts of the specified type from this GameObject. /// - /// Type of PLushieScripts to remove. + /// Type of Scripts to remove. generic where T : ref class, Script void RemoveScript(); diff --git a/SHADE_Managed/src/Editor/Editor.hxx b/SHADE_Managed/src/Editor/Editor.hxx index 6b59589a..109842b5 100644 --- a/SHADE_Managed/src/Editor/Editor.hxx +++ b/SHADE_Managed/src/Editor/Editor.hxx @@ -39,10 +39,10 @@ namespace SHADE /// The Entity to render the Scripts of. static void RenderScriptsInInspector(Entity entity); /// - /// Renders a dropdown button that allows for the addition of PlushieScripts - /// onto the specified Entity. + /// Renders a dropdown button that allows for the addition of Scripts onto the + /// specified Entity. /// - /// The Entity to add PlushieScripts to. + /// The Entity to add Scripts to. static void RenderScriptAddButton(Entity entity); /*-----------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Engine/EngineInterface.hxx b/SHADE_Managed/src/Engine/EngineInterface.hxx index 4fd8f7b3..37ded4eb 100644 --- a/SHADE_Managed/src/Engine/EngineInterface.hxx +++ b/SHADE_Managed/src/Engine/EngineInterface.hxx @@ -20,7 +20,7 @@ namespace SHADE { /// /// Static class that contains the functions for interfacing with the core - /// PlushieEngine written in C++ for managing the lifecycle of managed code. + /// SHADE Engine written in C++ for managing the lifecycle of managed code. /// private ref class EngineInterface abstract sealed { diff --git a/SHADE_Managed/src/Engine/GameObject.hxx b/SHADE_Managed/src/Engine/GameObject.hxx index 030b917c..02a0ed4f 100644 --- a/SHADE_Managed/src/Engine/GameObject.hxx +++ b/SHADE_Managed/src/Engine/GameObject.hxx @@ -210,7 +210,7 @@ namespace SHADE /// /// Removes all Scripts of the specified type from this GameObject. /// - /// Type of PLushieScripts to remove. + /// Type of Scripts to remove. generic where T : ref class, Script void RemoveScript(); diff --git a/SHADE_Managed/src/Math/Vector2.cxx b/SHADE_Managed/src/Math/Vector2.cxx index 42080d60..8242a11c 100644 --- a/SHADE_Managed/src/Math/Vector2.cxx +++ b/SHADE_Managed/src/Math/Vector2.cxx @@ -276,4 +276,4 @@ namespace SHADE { return !(lhs == rhs); } -} // namespace PlushieAPI::Mathematics \ No newline at end of file +} diff --git a/SHADE_Managed/src/Math/Vector3.cxx b/SHADE_Managed/src/Math/Vector3.cxx index 83adbb38..f2286aa7 100644 --- a/SHADE_Managed/src/Math/Vector3.cxx +++ b/SHADE_Managed/src/Math/Vector3.cxx @@ -294,4 +294,4 @@ namespace SHADE { return Vector3(vec); } -} // namespace PlushieAPI::Mathematics \ No newline at end of file +} diff --git a/SHADE_Managed/src/Math/Vector3.hxx b/SHADE_Managed/src/Math/Vector3.hxx index 4cdf653e..189f2930 100644 --- a/SHADE_Managed/src/Math/Vector3.hxx +++ b/SHADE_Managed/src/Math/Vector3.hxx @@ -439,4 +439,5 @@ namespace SHADE /// Vector2 to convert from. static explicit operator Vector3(Vector2 vec); }; -} // namespace PlushieAPI::Mathematics +} + diff --git a/SHADE_Managed/src/Scripts/Script.cxx b/SHADE_Managed/src/Scripts/Script.cxx index bd6d7bef..9d6cadb8 100644 --- a/SHADE_Managed/src/Scripts/Script.cxx +++ b/SHADE_Managed/src/Scripts/Script.cxx @@ -3,8 +3,7 @@ \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Oct 28, 2021 -\brief Contains the definition of the functions for the PlushieScript managed - class. +\brief Contains the definition of the functions for the Script managed class. Note: This file is written in C++17/CLI. diff --git a/SHADE_Managed/src/Scripts/ScriptStore.hxx b/SHADE_Managed/src/Scripts/ScriptStore.hxx index c151392f..23440f3d 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.hxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.hxx @@ -36,7 +36,7 @@ namespace SHADE /// /// /// Type of script to add. - /// This needs to be a default constructable PlushieScript. + /// This needs to be a default constructable Script. /// /// The entity to add a script to. /// Reference to the script added. diff --git a/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.cxx b/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.cxx index ebf2e987..e19c4d06 100644 --- a/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.cxx +++ b/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.cxx @@ -33,4 +33,4 @@ namespace SHADE { return nullptr; } -} // namespace PlushieAPI \ No newline at end of file +} diff --git a/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.hxx b/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.hxx index 433dd85e..14d612b3 100644 --- a/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.hxx +++ b/SHADE_Managed/src/Utility/DisposableAssemblyLoadContext.hxx @@ -36,4 +36,4 @@ namespace SHADE /*-----------------------------------------------------------------------------*/ System::Reflection::Assembly^ Load(System::Reflection::AssemblyName^ assemblyName) override; }; -} // namespace PlushieAPI \ No newline at end of file +} From eeab3494ba35bd395646ebee60829203863ee3b8 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 10 Nov 2022 03:30:02 +0800 Subject: [PATCH 108/113] Reworked SceneGraph Interface --- Assets/Scenes/M2Scene.shade | 84 ++--- .../HierarchyPanel/SHHierarchyPanel.cpp | 4 +- SHADE_Engine/src/Scene/SHSceneGraph.cpp | 337 ++++++------------ SHADE_Engine/src/Scene/SHSceneGraph.h | 109 ++---- SHADE_Engine/src/Scene/SHSceneNode.cpp | 143 ++++++++ SHADE_Engine/src/Scene/SHSceneNode.h | 82 +++++ SHADE_Managed/src/Engine/GameObject.cxx | 2 +- 7 files changed, 405 insertions(+), 356 deletions(-) create mode 100644 SHADE_Engine/src/Scene/SHSceneNode.cpp create mode 100644 SHADE_Engine/src/Scene/SHSceneNode.h diff --git a/Assets/Scenes/M2Scene.shade b/Assets/Scenes/M2Scene.shade index 30dea780..b2a5683f 100644 --- a/Assets/Scenes/M2Scene.shade +++ b/Assets/Scenes/M2Scene.shade @@ -124,48 +124,6 @@ Mesh: 144838771 Material: 123745521 Scripts: ~ -- EID: 5 - Name: item - IsActive: true - NumberOfChildren: 0 - Components: - Transform Component: - Translate: {x: 0, y: -2, z: -5} - Rotate: {x: 0, y: 0, z: 0} - Scale: {x: 2, y: 2, z: 2} - Renderable Component: - Mesh: 144838771 - Material: 123745521 - RigidBody Component: - Type: Dynamic - Mass: 1 - Drag: 0 - Angular Drag: 0 - Use Gravity: true - Interpolate: false - Freeze Position X: false - Freeze Position Y: false - Freeze Position Z: false - Freeze Rotation X: true - Freeze Rotation Y: true - Freeze Rotation Z: true - Collider Component: - Colliders: - - Is Trigger: false - Type: Box - Half Extents: {x: 1, y: 1, z: 1} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0.5, z: 0} - - Is Trigger: true - Type: Box - Half Extents: {x: 2, y: 2, z: 2} - Friction: 0.400000006 - Bounciness: 0 - Density: 1 - Position Offset: {x: 0, y: 0.5, z: 0} - Scripts: ~ - EID: 6 Name: AI IsActive: true @@ -226,4 +184,46 @@ Color: {x: 1, y: 1, z: 1, w: 1} Layer: 4294967295 Strength: 0.25 + Scripts: ~ +- EID: 5 + Name: item + IsActive: true + NumberOfChildren: 0 + Components: + Transform Component: + Translate: {x: 0, y: -2, z: -5} + Rotate: {x: 0, y: 0, z: 0} + Scale: {x: 2, y: 2, z: 2} + Renderable Component: + Mesh: 144838771 + Material: 123745521 + RigidBody Component: + Type: Dynamic + Mass: 1 + Drag: 0 + Angular Drag: 0 + Use Gravity: true + Interpolate: false + Freeze Position X: false + Freeze Position Y: false + Freeze Position Z: false + Freeze Rotation X: true + Freeze Rotation Y: true + Freeze Rotation Z: true + Collider Component: + Colliders: + - Is Trigger: false + Type: Box + Half Extents: {x: 1, y: 1, z: 1} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0.5, z: 0} + - Is Trigger: true + Type: Box + Half Extents: {x: 2, y: 2, z: 2} + Friction: 0.400000006 + Bounciness: 0 + Density: 1 + Position Offset: {x: 0, y: 0.5, z: 0} Scripts: ~ \ No newline at end of file diff --git a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp index e2c39ad8..ff65ba58 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/HierarchyPanel/SHHierarchyPanel.cpp @@ -419,7 +419,7 @@ namespace SHADE void SHEntityParentCommand::Execute() { - auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + auto& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); for (auto const& eid : entities) { if (entityParentData[eid].newParentEID == MAX_EID) @@ -431,7 +431,7 @@ namespace SHADE void SHEntityParentCommand::Undo() { - auto const& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); + auto& sceneGraph = SHSceneManager::GetCurrentSceneGraph(); for (auto const& eid : entities) { if (entityParentData[eid].oldParentEID == MAX_EID) diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index df46b3fb..900d482a 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -16,8 +16,6 @@ // Project Headers #include "ECS_Base/Managers/SHEntityManager.h" #include "Events/SHEventManager.hpp" -#include "Tools/SHLogger.h" -#include "Tools/SHException.h" namespace SHADE { @@ -25,56 +23,6 @@ namespace SHADE /* Constructors & Destructor Definitions */ /*-----------------------------------------------------------------------------------*/ - SHSceneNode::SHSceneNode(EntityID eid, SHSceneNode* parent) noexcept - : active { true } - , entityID { eid } - , parent { parent } - {} - - - SHSceneNode::SHSceneNode(const SHSceneNode& rhs) noexcept - : active { rhs.active } - , entityID { rhs.entityID } - , parent { rhs.parent } - { - std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children)); - } - - SHSceneNode::SHSceneNode(SHSceneNode&& rhs) noexcept - : active { rhs.active } - , entityID { rhs.entityID } - , parent { rhs.parent } - { - std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children)); - } - - SHSceneNode& SHSceneNode::operator=(const SHSceneNode& rhs) noexcept - { - if (this == &rhs) - return *this; - - active = rhs.active; - entityID = rhs.entityID; - parent = rhs.parent; - - children.clear(); - std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children)); - - return *this; - } - - SHSceneNode& SHSceneNode::operator=(SHSceneNode&& rhs) noexcept - { - active = rhs.active; - entityID = rhs.entityID; - parent = rhs.parent; - - children.clear(); - std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children)); - - return *this; - } - SHSceneGraph::SHSceneGraph() noexcept : root { nullptr } { @@ -110,56 +58,6 @@ namespace SHADE /* Getter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - bool SHSceneNode::IsActive() const noexcept - { - return active; - } - - EntityID SHSceneNode::GetEntityID() const noexcept - { - return entityID; - } - - SHSceneNode* SHSceneNode::GetParent() const noexcept - { - return parent; - } - - const std::vector& SHSceneNode::GetChildren() const noexcept - { - return children; - } - - SHSceneNode* SHSceneNode::GetChild(EntityID childID) const noexcept - { - //////////////////////////////////////// - // Error handling - if (!SHEntityManager::IsValidEID(childID)) - { - SHLOG_ERROR("Child Entity {} is invalid! Unable to get child from Entity {}", childID, entityID) - return nullptr; - } - - if (children.empty()) - { - SHLOG_WARNING("Entity {} has no children!", entityID) - return nullptr; - } - //////////////////////////////////////// - - // Find child - const auto ENTITY_MATCH = [&](const SHSceneNode* node) { return node->GetEntityID() == childID; }; - - const auto CHILD_ITER = std::ranges::find_if(children.begin(), children.end(),ENTITY_MATCH); - if (CHILD_ITER == children.end()) - { - SHLOG_WARNING("Entity {} is not a child of Entity {}! Unable to retrieve child node!", childID, entityID) - return nullptr; - } - - return *CHILD_ITER; - } - const SHSceneNode* SHSceneGraph::GetRoot() const noexcept { if (root != nullptr) @@ -313,41 +211,7 @@ namespace SHADE /* Setter Function Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHSceneNode::SetParent(SHSceneNode* parentNode) noexcept - { - if (parentNode == nullptr) - { - SHLOG_WARNING("Removing Entity {}'s parent", entityID) - - if (parent) - parent->RemoveChild(this); - - return; - } - - // Handle self assignment - if (parent && parentNode->entityID == parent->entityID) - return; - - if (parent) - parent->RemoveChild(this); - - parent = parentNode; - // Update parent's children - parent->AddChild(this); - } - - void SHSceneNode::SetActive(bool newActiveState) noexcept - { - active = newActiveState; - - for (auto* child : children) - { - SetActive(newActiveState); - } - } - - void SHSceneGraph::SetParent(EntityID entityID, SHSceneNode* parent) const noexcept + void SHSceneGraph::SetParent(EntityID entityID, SHSceneNode* newParent) noexcept { //////////////////////////////////////// // Error Handling @@ -369,18 +233,33 @@ namespace SHADE { .node = NODE_ITER->second , .oldParent = NODE_ITER->second->GetParent() - , .newParent = parent ? parent : root + , .newParent = newParent ? newParent : root }; - if (parent == nullptr) - parent = root; + if (newParent == nullptr) + newParent = root; - NODE_ITER->second->SetParent(parent); + ChangeParent(NODE_ITER->second, newParent); SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } - void SHSceneGraph::SetParent(EntityID entityID, EntityID parent) const noexcept + void SHSceneGraph::SetParent(SHSceneNode* node, SHSceneNode* newParent) noexcept + { + const SHSceneGraphChangeParentEvent EVENT_DATA + { + .node = node + , .oldParent = node->parent + , .newParent = newParent ? newParent : root + }; + + if (newParent == nullptr) + newParent = root; + + ChangeParent(node, newParent); + } + + void SHSceneGraph::SetParent(EntityID entityID, EntityID newParent) noexcept { //////////////////////////////////////// // Error Handling @@ -390,9 +269,9 @@ namespace SHADE return; } - if (!SHEntityManager::IsValidEID(parent)) + if (!SHEntityManager::IsValidEID(newParent)) { - SHLOG_ERROR("Parent Entity {} is invalid! Unable to set Entity {}'s parent!", parent, entityID) + SHLOG_ERROR("Parent Entity {} is invalid! Unable to set Entity {}'s parent!", newParent, entityID) return; } @@ -403,10 +282,10 @@ namespace SHADE return; } - auto PARENT_ITER = entityNodeMap.find(parent); + auto PARENT_ITER = entityNodeMap.find(newParent); if (PARENT_ITER == entityNodeMap.end()) { - SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to parent to Entity {}", parent, entityID) + SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to parent to Entity {}", newParent, entityID) return; } //////////////////////////////////////// @@ -419,92 +298,46 @@ namespace SHADE }; SHSceneNode* currentNode = NODE_ITER->second; - currentNode->SetParent(PARENT_ITER->second); + ChangeParent(currentNode, PARENT_ITER->second); SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } - /*-----------------------------------------------------------------------------------*/ - /* Public Function Member Definitions */ - /*-----------------------------------------------------------------------------------*/ - - void SHSceneNode::AddChild(SHSceneNode* newChild) noexcept + void SHSceneGraph::SetParent(SHSceneNode* node, EntityID newParent) noexcept { //////////////////////////////////////// // Error Handling - if (newChild == nullptr) + + if (!SHEntityManager::IsValidEID(newParent)) { - SHLOG_WARNING("Attempting to add a non-existent child to an entity!") + SHLOG_ERROR("Parent Entity {} is invalid! Unable to set Entity {}'s parent!", newParent, node->entityID) + return; + } + + auto PARENT_ITER = entityNodeMap.find(newParent); + if (PARENT_ITER == entityNodeMap.end()) + { + SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to parent to Entity {}", newParent, node->entityID) return; } //////////////////////////////////////// - if (newChild->parent) - newChild->parent->RemoveChild(newChild); - - newChild->parent = this; - children.emplace_back(newChild); + const SHSceneGraphChangeParentEvent EVENT_DATA + { + .node = node + , .oldParent = node->parent + , .newParent = PARENT_ITER->second + }; + + ChangeParent(node, PARENT_ITER->second); + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } - bool SHSceneNode::RemoveChild(EntityID childID) noexcept - { - //////////////////////////////////////// - // Error Handling - if (!SHEntityManager::IsValidEID(childID)) - { - SHLOG_ERROR("Entity {} is invalid!", childID) - return false; - } - //////////////////////////////////////// - auto childIter = std::find_if(children.begin(), children.end(), [&](SHSceneNode* node) - { - return node->GetEntityID() == childID; - }); - - if (childIter == children.end()) - { - SHLOG_WARNING("Unable to remove Entity {} from Entity {} since it is not it's child!", childID, entityID) - return false; - } - - (*childIter)->parent = nullptr; - childIter = children.erase(childIter); - - return true; - } - - bool SHSceneNode::RemoveChild(SHSceneNode* childToRemove) noexcept - { - //////////////////////////////////////// - // Error Handling - if (childToRemove == nullptr) - { - SHLOG_WARNING("Attempting to remove non-existent child from Entity {}", entityID) - return false; - } - //////////////////////////////////////// - - auto childIter = std::find(children.begin(), children.end(), childToRemove); - if (childIter == children.end()) - { - SHLOG_WARNING("Unable to remove Entity {} from Entity {} since it is not it's child!", childToRemove->entityID, entityID) - return false; - } - - childIter = children.erase(childIter); - childToRemove->parent = nullptr; - - return true; - } - - void SHSceneNode::RemoveAllChildren() noexcept - { - for (const auto child : children) - child->parent = nullptr; - - children.clear(); - } + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ SHSceneNode* SHSceneGraph::AddNode(EntityID entityID, SHSceneNode* parent) { @@ -528,13 +361,12 @@ namespace SHADE if (parent == nullptr) { // Specific handling for root to avoid a warning when removing a non-existent child - parent = root; newNode->parent = root; root->children.emplace_back(newNode); } else { - newNode->SetParent(parent); + ChangeParent(newNode, parent); } return newNode; @@ -557,9 +389,8 @@ namespace SHADE // Remove reference of current node from parent SHSceneNode* currentNode = NODE_ITER->second; - SHSceneNode* parent = currentNode->GetParent(); - if (parent != nullptr) - parent->RemoveChild(currentNode); + if (currentNode->parent != nullptr) + RemoveChild(currentNode->parent, currentNode); ReleaseNode(currentNode); return true; @@ -568,9 +399,8 @@ namespace SHADE bool SHSceneGraph::RemoveNode(SHSceneNode* nodeToRemove) noexcept { // Remove reference of current node from parent - SHSceneNode* parent = nodeToRemove->GetParent(); - if (parent != nullptr) - parent->RemoveChild(nodeToRemove); + if (nodeToRemove->parent != nullptr) + RemoveChild(nodeToRemove->parent, nodeToRemove); ReleaseNode(nodeToRemove); return true; @@ -594,11 +424,6 @@ namespace SHADE SHSceneNode* SHSceneGraph::AllocateNode(EntityID entityID) { SHSceneNode* newNode = new SHSceneNode{entityID}; - - //#ifdef _DEBUG - // SHLOG_INFO("Allocated a new Scene Node for Entity {}!", entityID) - //#endif - entityNodeMap.emplace(entityID, newNode); return newNode; } @@ -608,19 +433,65 @@ namespace SHADE SHASSERT(node != nullptr, "Attempting to release Invalid Node!") // Remove parent's reference to this node if there is a parent - if (node->GetParent() != nullptr) - node->GetParent()->RemoveChild(node); + if (node->parent != nullptr) + RemoveChild(node->parent, node); // Remove child's references to this node. Children end up as floating nodes. for (auto* child : node->GetChildren()) { - child->SetParent(nullptr); + ChangeParent(child, nullptr); } entityNodeMap.erase(node->GetEntityID()); delete node; } + void SHSceneGraph::ChangeParent(SHSceneNode* node, SHSceneNode* newParent) + { + // Handle self assignment + if (node->parent != nullptr && newParent != nullptr && node->parent->entityID == newParent->entityID) + return; + + // Remove child + if (node->parent) + RemoveChild(node->parent, node); + + if (newParent == nullptr) + { + SHLOG_WARNING("Removing Entity {}'s parent", node->entityID) + return; + } + + node->parent = newParent; + // Update parent's children + AddChild(newParent, node); + } + + 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); + + newChild->parent = node; + node->children.emplace_back(newChild); + } + + 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!") + + auto childIter = std::find(node->children.begin(), node->children.end(), childToRemove); + if (childIter == node->children.end()) + return; + + childIter = node->children.erase(childIter); + childToRemove->parent = nullptr; + } + void SHSceneGraph::TraverseAndInvokeFunction(const SHSceneNode* node, const UnaryFunction& function) { for (auto* child : node->children) diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index 45ab48e5..99de9c44 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -1,7 +1,7 @@ /**************************************************************************************** * \file SHSceneGraph.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Scene Graph & Scene Nodes. + * \brief Interface for a Scene Graph. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -15,81 +15,14 @@ // Project Headers #include "ECS_Base/Entity/SHEntity.h" #include "SH_API.h" +#include "SHSceneNode.h" namespace SHADE { - /*-----------------------------------------------------------------------------------*/ - /* Forward Declarations */ - /*-----------------------------------------------------------------------------------*/ - - class SHSceneGraph; - /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ - class SH_API SHSceneNode - { - private: - /*---------------------------------------------------------------------------------*/ - /* Friends */ - /*---------------------------------------------------------------------------------*/ - - friend class SHSceneGraph; - - public: - /*---------------------------------------------------------------------------------*/ - /* Constructors & Destructor */ - /*---------------------------------------------------------------------------------*/ - - ~SHSceneNode () = default; - - SHSceneNode (EntityID eid, SHSceneNode* parent = nullptr) noexcept; - SHSceneNode (const SHSceneNode& rhs) noexcept; - SHSceneNode (SHSceneNode&& rhs) noexcept; - SHSceneNode& operator= (const SHSceneNode& rhs) noexcept; - SHSceneNode& operator= (SHSceneNode&& rhs) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Getter Functions */ - /*---------------------------------------------------------------------------------*/ - - [[nodiscard]] bool IsActive () const noexcept; - [[nodiscard]] EntityID GetEntityID () const noexcept; - [[nodiscard]] SHSceneNode* GetParent () const noexcept; - [[nodiscard]] const std::vector& GetChildren () const noexcept; - - [[nodiscard]] SHSceneNode* GetChild (EntityID childID) const noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Setter Functions */ - /*---------------------------------------------------------------------------------*/ - - void SetParent (SHSceneNode* parentNode) noexcept; - void SetActive (bool newActiveState) noexcept; - - /*---------------------------------------------------------------------------------*/ - /* Function Members */ - /*---------------------------------------------------------------------------------*/ - - void AddChild (SHSceneNode* newChild) noexcept; - - bool RemoveChild (EntityID childID) noexcept; - bool RemoveChild (SHSceneNode* childToRemove) noexcept; - - void RemoveAllChildren () noexcept; - - private: - /*---------------------------------------------------------------------------------*/ - /* Data Members */ - /*---------------------------------------------------------------------------------*/ - - bool active; - EntityID entityID; - SHSceneNode* parent; - std::vector children; - }; - class SH_API SHSceneGraph { public: @@ -130,19 +63,22 @@ namespace SHADE /* Setter Functions */ /*---------------------------------------------------------------------------------*/ - void SetParent (EntityID entityID, SHSceneNode* parent) const noexcept; - void SetParent (EntityID entityID, EntityID parent) const noexcept; + void SetParent (EntityID entityID, SHSceneNode* newParent) noexcept; + void SetParent (SHSceneNode* node, SHSceneNode* newParent) noexcept; + + void SetParent (EntityID entityID, EntityID newParent) noexcept; + void SetParent (SHSceneNode* node, EntityID newParent) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ SHSceneNode* AddNode (EntityID entityID, SHSceneNode* parent = nullptr); - bool RemoveNode (EntityID entityID) noexcept; - bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; - void Reset () noexcept; + bool RemoveNode (EntityID entityID) noexcept; + bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; + void Reset () noexcept; - void Traverse (const UnaryFunction& function) const; + void Traverse (const UnaryFunction& function) const; private: /*---------------------------------------------------------------------------------*/ @@ -156,9 +92,14 @@ namespace SHADE /* Function Members */ /*---------------------------------------------------------------------------------*/ - SHSceneNode* AllocateNode (EntityID entityID); - void ReleaseNode (SHSceneNode* node) noexcept; - static void TraverseAndInvokeFunction (const SHSceneNode* node, const UnaryFunction& function); + 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); + + static void TraverseAndInvokeFunction (const SHSceneNode* node, const UnaryFunction& function); }; /*-----------------------------------------------------------------------------------*/ @@ -172,4 +113,16 @@ namespace SHADE SHSceneNode* newParent; }; + struct SHSceneGraphAddChildEvent + { + SHSceneNode* parent; + SHSceneNode* addedChild; + }; + + struct SHSceneGraphRemoveChildEvent + { + SHSceneNode* parent; + SHSceneNode* removedChild; + }; + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Scene/SHSceneNode.cpp b/SHADE_Engine/src/Scene/SHSceneNode.cpp new file mode 100644 index 00000000..b619d464 --- /dev/null +++ b/SHADE_Engine/src/Scene/SHSceneNode.cpp @@ -0,0 +1,143 @@ +/**************************************************************************************** + * \file SHSceneNode.c[[ + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Scene Node. + * + * \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 "SHSceneNode.h" + +// Project Headers +#include "ECS_Base/Managers/SHEntityManager.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHSceneNode::SHSceneNode(EntityID eid, SHSceneNode* parent) noexcept + : active { true } + , entityID { eid } + , parent { parent } + {} + + + SHSceneNode::SHSceneNode(const SHSceneNode& rhs) noexcept + : active { rhs.active } + , entityID { rhs.entityID } + , parent { rhs.parent } + { + std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children)); + } + + SHSceneNode::SHSceneNode(SHSceneNode&& rhs) noexcept + : active { rhs.active } + , entityID { rhs.entityID } + , parent { rhs.parent } + { + std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children)); + } + + SHSceneNode& SHSceneNode::operator=(const SHSceneNode& rhs) noexcept + { + if (this == &rhs) + return *this; + + active = rhs.active; + entityID = rhs.entityID; + parent = rhs.parent; + + children.clear(); + std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children)); + + return *this; + } + + SHSceneNode& SHSceneNode::operator=(SHSceneNode&& rhs) noexcept + { + active = rhs.active; + entityID = rhs.entityID; + parent = rhs.parent; + + children.clear(); + std::ranges::copy(rhs.children.begin(), rhs.children.end(), std::back_inserter(children)); + + return *this; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + bool SHSceneNode::IsActive() const noexcept + { + return active; + } + + EntityID SHSceneNode::GetEntityID() const noexcept + { + return entityID; + } + + SHSceneNode* SHSceneNode::GetParent() const noexcept + { + return parent; + } + + const std::vector& SHSceneNode::GetChildren() const noexcept + { + return children; + } + + SHSceneNode* SHSceneNode::GetChild(EntityID childID) const noexcept + { + //////////////////////////////////////// + // Error handling + if (!SHEntityManager::IsValidEID(childID)) + { + SHLOG_ERROR("Child Entity {} is invalid! Unable to get child from Entity {}", childID, entityID) + return nullptr; + } + + if (children.empty()) + { + SHLOG_WARNING("Entity {} has no children!", entityID) + return nullptr; + } + //////////////////////////////////////// + + // Find child + const auto ENTITY_MATCH = [&](const SHSceneNode* node) { return node->GetEntityID() == childID; }; + + const auto CHILD_ITER = std::ranges::find_if(children.begin(), children.end(),ENTITY_MATCH); + if (CHILD_ITER == children.end()) + { + SHLOG_WARNING("Entity {} is not a child of Entity {}! Unable to retrieve child node!", childID, entityID) + return nullptr; + } + + return *CHILD_ITER; + } + + /*-----------------------------------------------------------------------------------*/ + /* Setter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHSceneNode::SetActive(bool newActiveState) noexcept + { + active = newActiveState; + + for (auto* child : children) + { + SetActive(newActiveState); + } + } + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Scene/SHSceneNode.h b/SHADE_Engine/src/Scene/SHSceneNode.h new file mode 100644 index 00000000..87bd8d0b --- /dev/null +++ b/SHADE_Engine/src/Scene/SHSceneNode.h @@ -0,0 +1,82 @@ +/**************************************************************************************** + * \file SHSceneNode.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Scene Node. + * + * \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 "SH_API.h" +#include "SHSceneGraph.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Forward Declarations */ + /*-----------------------------------------------------------------------------------*/ + + class SHSceneGraph; + + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + class SH_API SHSceneNode + { + private: + /*---------------------------------------------------------------------------------*/ + /* Friends */ + /*---------------------------------------------------------------------------------*/ + + friend class SHSceneGraph; + + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + ~SHSceneNode () = default; + + SHSceneNode (EntityID eid, SHSceneNode* parent = nullptr) noexcept; + SHSceneNode (const SHSceneNode& rhs) noexcept; + SHSceneNode (SHSceneNode&& rhs) noexcept; + SHSceneNode& operator= (const SHSceneNode& rhs) noexcept; + SHSceneNode& operator= (SHSceneNode&& rhs) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] bool IsActive () const noexcept; + [[nodiscard]] EntityID GetEntityID () const noexcept; + [[nodiscard]] SHSceneNode* GetParent () const noexcept; + [[nodiscard]] const std::vector& GetChildren () const noexcept; + + [[nodiscard]] SHSceneNode* GetChild (EntityID childID) const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetActive (bool newActiveState) noexcept; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + bool active; + EntityID entityID; + SHSceneNode* parent; + std::vector children; + }; +} // namespace SHADE + diff --git a/SHADE_Managed/src/Engine/GameObject.cxx b/SHADE_Managed/src/Engine/GameObject.cxx index 9f15c6c9..bc17be8b 100644 --- a/SHADE_Managed/src/Engine/GameObject.cxx +++ b/SHADE_Managed/src/Engine/GameObject.cxx @@ -105,7 +105,7 @@ namespace SHADE { if (!valid) throw gcnew System::NullReferenceException(); - const auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); + auto& SCENE_GRAPH = SHSceneManager::GetCurrentSceneGraph(); if (newParent == nullptr) SCENE_GRAPH.SetParent(entity, nullptr); From ac217ffe973910baba4bf958bc87406030b834b3 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 10 Nov 2022 09:06:38 +0800 Subject: [PATCH 109/113] Added Add and Remove Child Events to SceneGraph --- SHADE_Engine/src/Events/SHEventDefines.h | 12 +++--- SHADE_Engine/src/Scene/SHSceneGraph.cpp | 18 ++++++++- SHADE_Engine/src/Scene/SHSceneGraph.h | 24 +----------- SHADE_Engine/src/Scene/SHSceneGraphEvents.h | 41 +++++++++++++++++++++ 4 files changed, 66 insertions(+), 29 deletions(-) create mode 100644 SHADE_Engine/src/Scene/SHSceneGraphEvents.h diff --git a/SHADE_Engine/src/Events/SHEventDefines.h b/SHADE_Engine/src/Events/SHEventDefines.h index d649fabf..d7bbf5f0 100644 --- a/SHADE_Engine/src/Events/SHEventDefines.h +++ b/SHADE_Engine/src/Events/SHEventDefines.h @@ -11,9 +11,11 @@ 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_PHYSICS_COLLIDER_ADDED_EVENT { 6 }; -constexpr SHEventIdentifier SH_PHYSICS_COLLIDER_REMOVED_EVENT { 7 }; -constexpr SHEventIdentifier SH_EDITOR_ON_PLAY_EVENT { 8 }; -constexpr SHEventIdentifier SH_EDITOR_ON_PAUSE_EVENT { 9 }; -constexpr SHEventIdentifier SH_EDITOR_ON_STOP_EVENT { 10 }; +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 }; diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index 900d482a..ff479eec 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -1,7 +1,7 @@ /**************************************************************************************** * \file SHSceneGraph.cpp * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Implementation for a Scene Graph & Scene Nodes. + * \brief Implementation for a Scene Graph. * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -477,6 +477,14 @@ namespace SHADE newChild->parent = node; node->children.emplace_back(newChild); + + const SHSceneGraphAddChildEvent EVENT_DATA + { + .parent = node + , .childAdded = newChild + }; + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_ADD_CHILD_EVENT); } void SHSceneGraph::RemoveChild(SHSceneNode* node, SHSceneNode* childToRemove) @@ -490,6 +498,14 @@ namespace SHADE childIter = node->children.erase(childIter); childToRemove->parent = nullptr; + + const SHSceneGraphRemoveChildEvent EVENT_DATA + { + .parent = node + , .childRemoved = childToRemove + }; + + SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_REMOVE_CHILD_EVENT); } void SHSceneGraph::TraverseAndInvokeFunction(const SHSceneNode* node, const UnaryFunction& function) diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index 99de9c44..1d448b37 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -16,6 +16,7 @@ #include "ECS_Base/Entity/SHEntity.h" #include "SH_API.h" #include "SHSceneNode.h" +#include "SHSceneGraphEvents.h" namespace SHADE { @@ -102,27 +103,4 @@ namespace SHADE static void TraverseAndInvokeFunction (const SHSceneNode* node, const UnaryFunction& function); }; - /*-----------------------------------------------------------------------------------*/ - /* Event Data Definitions */ - /*-----------------------------------------------------------------------------------*/ - - struct SHSceneGraphChangeParentEvent - { - SHSceneNode* node; - SHSceneNode* oldParent; - SHSceneNode* newParent; - }; - - struct SHSceneGraphAddChildEvent - { - SHSceneNode* parent; - SHSceneNode* addedChild; - }; - - struct SHSceneGraphRemoveChildEvent - { - SHSceneNode* parent; - SHSceneNode* removedChild; - }; - } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Scene/SHSceneGraphEvents.h b/SHADE_Engine/src/Scene/SHSceneGraphEvents.h new file mode 100644 index 00000000..ccdf06be --- /dev/null +++ b/SHADE_Engine/src/Scene/SHSceneGraphEvents.h @@ -0,0 +1,41 @@ +/**************************************************************************************** + * \file SHSceneGraphEvents.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for Scene Graph Events. + * + * \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 "SHSceneNode.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Event Data Definitions */ + /*-----------------------------------------------------------------------------------*/ + + struct SHSceneGraphChangeParentEvent + { + SHSceneNode* node; + SHSceneNode* oldParent; + SHSceneNode* newParent; + }; + + struct SHSceneGraphAddChildEvent + { + SHSceneNode* parent; + SHSceneNode* childAdded; + }; + + struct SHSceneGraphRemoveChildEvent + { + SHSceneNode* parent; + SHSceneNode* childRemoved; + }; + +} // namespace SHADE \ No newline at end of file From 08b3cbafcd4e2a08fd5701179fce20c700b89704 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 10 Nov 2022 10:20:54 +0800 Subject: [PATCH 110/113] Added IsChildOf method to SceneGraph --- SHADE_Engine/src/Scene/SHSceneGraph.cpp | 147 ++++++++++++++++++++++++ SHADE_Engine/src/Scene/SHSceneGraph.h | 14 ++- 2 files changed, 156 insertions(+), 5 deletions(-) diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index ff479eec..e2bd58e2 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -263,6 +263,7 @@ namespace SHADE { //////////////////////////////////////// // Error Handling + if (!SHEntityManager::IsValidEID(entityID)) { SHLOG_ERROR("Entity {} is invalid! Unable to set parent of an invalid entity!", entityID) @@ -374,6 +375,8 @@ namespace SHADE bool SHSceneGraph::RemoveNode(EntityID entityID) noexcept { + //////////////////////////////////////// + // Error Handling if (!SHEntityManager::IsValidEID(entityID)) { SHLOG_ERROR("Entity {} is invalid!", entityID) @@ -386,6 +389,7 @@ namespace SHADE SHLOG_ERROR("Entity {} does not exist in the scene!", entityID) return false; } + //////////////////////////////////////// // Remove reference of current node from parent SHSceneNode* currentNode = NODE_ITER->second; @@ -412,6 +416,149 @@ namespace SHADE ReleaseNode(node); } + bool SHSceneGraph::IsChildOf(EntityID entityID, SHSceneNode* targetNode) noexcept + { + //////////////////////////////////////// + // Error Handling + + if (!SHEntityManager::IsValidEID(entityID)) + { + SHLOG_ERROR("Entity {} is invalid!", entityID) + return false; + } + + auto NODE_ITER = entityNodeMap.find(entityID); + if (NODE_ITER == entityNodeMap.end()) + { + SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to check child!", entityID) + return false; + } + + //////////////////////////////////////// + + // Handle self-checks + if (NODE_ITER->second == targetNode) + { + SHLOG_WARNING("Entity {} cannot be a child of itself!", entityID) + return false; + } + + // Search for a matching target until the root + const SHSceneNode* CURRENT_TARGET = NODE_ITER->second->parent; + while (CURRENT_TARGET != root) + { + if (CURRENT_TARGET == targetNode) + return true; + + CURRENT_TARGET = CURRENT_TARGET->parent; + } + + return false; + } + + bool SHSceneGraph::IsChildOf(SHSceneNode* node, SHSceneNode* targetNode) noexcept + { + if (!node || !targetNode) + { + SHLOG_ERROR("Attempting to check for invalid scene nodes!") + return false; + } + + if (node == targetNode) + { + SHLOG_WARNING("Entity {} cannot be a child of itself!", node->entityID) + return false; + } + + const SHSceneNode* CURRENT_TARGET = node->parent; + while (CURRENT_TARGET != root) + { + if (CURRENT_TARGET == targetNode) + return true; + + CURRENT_TARGET = CURRENT_TARGET->parent; + } + + return false; + } + + bool SHSceneGraph::IsChildOf(EntityID entityID, EntityID targetID) noexcept + { + //////////////////////////////////////// + // Error Handling + + if (!SHEntityManager::IsValidEID(entityID)) + { + SHLOG_ERROR("Entity {} is invalid!", entityID) + return false; + } + + if (!SHEntityManager::IsValidEID(targetID)) + { + SHLOG_ERROR("Entity {} is invalid!", targetID) + return false; + } + + auto NODE_ITER = entityNodeMap.find(entityID); + if (NODE_ITER == entityNodeMap.end()) + { + SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to check child!", entityID) + return false; + } + + auto TARGET_ITER = entityNodeMap.find(targetID); + if (TARGET_ITER == entityNodeMap.end()) + { + SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to check child!", targetID) + return false; + } + + //////////////////////////////////////// + + const SHSceneNode* CURRENT_TARGET = NODE_ITER->second->parent; + while (CURRENT_TARGET != root) + { + if (CURRENT_TARGET == TARGET_ITER->second) + return true; + + CURRENT_TARGET = CURRENT_TARGET->parent; + } + + return false; + } + + bool SHSceneGraph::IsChildOf(SHSceneNode* node, EntityID targetID) noexcept + { + //////////////////////////////////////// + // Error Handling + + if (!SHEntityManager::IsValidEID(targetID)) + { + SHLOG_ERROR("Entity {} is invalid!", targetID) + return false; + } + + auto TARGET_ITER = entityNodeMap.find(targetID); + if (TARGET_ITER == entityNodeMap.end()) + { + SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to check child!", targetID) + return false; + } + + //////////////////////////////////////// + + const SHSceneNode* CURRENT_TARGET = node->parent; + while (CURRENT_TARGET != root) + { + if (CURRENT_TARGET == TARGET_ITER->second) + return true; + + CURRENT_TARGET = CURRENT_TARGET->parent; + } + + return false; + } + void SHSceneGraph::Traverse (const UnaryFunction& function) const { TraverseAndInvokeFunction(root, function); diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index 1d448b37..494f480e 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -66,7 +66,6 @@ namespace SHADE void SetParent (EntityID entityID, SHSceneNode* newParent) noexcept; void SetParent (SHSceneNode* node, SHSceneNode* newParent) noexcept; - void SetParent (EntityID entityID, EntityID newParent) noexcept; void SetParent (SHSceneNode* node, EntityID newParent) noexcept; @@ -75,11 +74,16 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ SHSceneNode* AddNode (EntityID entityID, SHSceneNode* parent = nullptr); - bool RemoveNode (EntityID entityID) noexcept; - bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; - void Reset () noexcept; + bool RemoveNode (EntityID entityID) noexcept; + bool RemoveNode (SHSceneNode* nodeToRemove) noexcept; + void Reset () noexcept; - void Traverse (const UnaryFunction& function) const; + bool IsChildOf (EntityID entityID, SHSceneNode* targetNode) noexcept; + bool IsChildOf (SHSceneNode* node, SHSceneNode* targetNode) noexcept; + bool IsChildOf (EntityID entityID, EntityID targetID) noexcept; + bool IsChildOf (SHSceneNode* node, EntityID targetID) noexcept; + + void Traverse (const UnaryFunction& function) const; private: /*---------------------------------------------------------------------------------*/ From 578f384808fe1f5fe26fed136cb033e2104e8490 Mon Sep 17 00:00:00 2001 From: Diren D Bharwani Date: Thu, 10 Nov 2022 10:24:09 +0800 Subject: [PATCH 111/113] Removed redundant overloads to restrict potential bad function calls --- SHADE_Engine/src/Scene/SHSceneGraph.cpp | 105 ------------------------ SHADE_Engine/src/Scene/SHSceneGraph.h | 6 +- 2 files changed, 1 insertion(+), 110 deletions(-) diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.cpp b/SHADE_Engine/src/Scene/SHSceneGraph.cpp index e2bd58e2..6240b7bf 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.cpp +++ b/SHADE_Engine/src/Scene/SHSceneGraph.cpp @@ -244,21 +244,6 @@ namespace SHADE SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } - void SHSceneGraph::SetParent(SHSceneNode* node, SHSceneNode* newParent) noexcept - { - const SHSceneGraphChangeParentEvent EVENT_DATA - { - .node = node - , .oldParent = node->parent - , .newParent = newParent ? newParent : root - }; - - if (newParent == nullptr) - newParent = root; - - ChangeParent(node, newParent); - } - void SHSceneGraph::SetParent(EntityID entityID, EntityID newParent) noexcept { //////////////////////////////////////// @@ -304,38 +289,6 @@ namespace SHADE SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); } - void SHSceneGraph::SetParent(SHSceneNode* node, EntityID newParent) noexcept - { - //////////////////////////////////////// - // Error Handling - - if (!SHEntityManager::IsValidEID(newParent)) - { - SHLOG_ERROR("Parent Entity {} is invalid! Unable to set Entity {}'s parent!", newParent, node->entityID) - return; - } - - auto PARENT_ITER = entityNodeMap.find(newParent); - if (PARENT_ITER == entityNodeMap.end()) - { - SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to parent to Entity {}", newParent, node->entityID) - return; - } - //////////////////////////////////////// - - const SHSceneGraphChangeParentEvent EVENT_DATA - { - .node = node - , .oldParent = node->parent - , .newParent = PARENT_ITER->second - }; - - ChangeParent(node, PARENT_ITER->second); - - SHEventManager::BroadcastEvent(EVENT_DATA, SH_SCENEGRAPH_CHANGE_PARENT_EVENT); - } - - /*-----------------------------------------------------------------------------------*/ /* Public Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -456,32 +409,6 @@ namespace SHADE return false; } - bool SHSceneGraph::IsChildOf(SHSceneNode* node, SHSceneNode* targetNode) noexcept - { - if (!node || !targetNode) - { - SHLOG_ERROR("Attempting to check for invalid scene nodes!") - return false; - } - - if (node == targetNode) - { - SHLOG_WARNING("Entity {} cannot be a child of itself!", node->entityID) - return false; - } - - const SHSceneNode* CURRENT_TARGET = node->parent; - while (CURRENT_TARGET != root) - { - if (CURRENT_TARGET == targetNode) - return true; - - CURRENT_TARGET = CURRENT_TARGET->parent; - } - - return false; - } - bool SHSceneGraph::IsChildOf(EntityID entityID, EntityID targetID) noexcept { //////////////////////////////////////// @@ -527,38 +454,6 @@ namespace SHADE return false; } - bool SHSceneGraph::IsChildOf(SHSceneNode* node, EntityID targetID) noexcept - { - //////////////////////////////////////// - // Error Handling - - if (!SHEntityManager::IsValidEID(targetID)) - { - SHLOG_ERROR("Entity {} is invalid!", targetID) - return false; - } - - auto TARGET_ITER = entityNodeMap.find(targetID); - if (TARGET_ITER == entityNodeMap.end()) - { - SHLOG_ERROR("Entity {} cannot be found in the scene! Unable to check child!", targetID) - return false; - } - - //////////////////////////////////////// - - const SHSceneNode* CURRENT_TARGET = node->parent; - while (CURRENT_TARGET != root) - { - if (CURRENT_TARGET == TARGET_ITER->second) - return true; - - CURRENT_TARGET = CURRENT_TARGET->parent; - } - - return false; - } - void SHSceneGraph::Traverse (const UnaryFunction& function) const { TraverseAndInvokeFunction(root, function); diff --git a/SHADE_Engine/src/Scene/SHSceneGraph.h b/SHADE_Engine/src/Scene/SHSceneGraph.h index 494f480e..5747be7b 100644 --- a/SHADE_Engine/src/Scene/SHSceneGraph.h +++ b/SHADE_Engine/src/Scene/SHSceneGraph.h @@ -65,9 +65,7 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ void SetParent (EntityID entityID, SHSceneNode* newParent) noexcept; - void SetParent (SHSceneNode* node, SHSceneNode* newParent) noexcept; void SetParent (EntityID entityID, EntityID newParent) noexcept; - void SetParent (SHSceneNode* node, EntityID newParent) noexcept; /*---------------------------------------------------------------------------------*/ /* Function Members */ @@ -79,9 +77,7 @@ namespace SHADE void Reset () noexcept; bool IsChildOf (EntityID entityID, SHSceneNode* targetNode) noexcept; - bool IsChildOf (SHSceneNode* node, SHSceneNode* targetNode) noexcept; - bool IsChildOf (EntityID entityID, EntityID targetID) noexcept; - bool IsChildOf (SHSceneNode* node, EntityID targetID) noexcept; + bool IsChildOf (EntityID entityID, EntityID targetID) noexcept; void Traverse (const UnaryFunction& function) const; From fa0787d349900ee3fb097baf7901c08b1168bbb7 Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Thu, 10 Nov 2022 10:33:21 +0800 Subject: [PATCH 112/113] Changed hardcoded paths to use defined asset root folder Shifted scripts and project into folder in assets --- .../Scripts}/AIPrototype.cs | 0 .../Scripts}/CameraControl.cs | 0 {TempScriptsFolder => Assets/Scripts}/CameraFix.cs | 0 {TempScriptsFolder => Assets/Scripts}/Item.cs | 0 .../Scripts}/PhysicsTest.cs | 0 .../Scripts}/PickAndThrow.cs | 0 .../Scripts}/PlayerController.cs | 0 .../Scripts}/PrintWhenActive.cs | 0 .../Scripts}/RaccoonShowcase.cs | 0 .../Scripts}/RaccoonSpin.cs | 0 .../Scripts}/ThirdPersonCamera.cs | 0 SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp | 10 ++++++---- .../EditorWindow/MenuBar/SHEditorMenuBar.cpp | 6 ++++-- SHADE_Engine/src/Editor/SHEditor.cpp | 14 +++++++++----- SHADE_Engine/src/Scripting/SHScriptEngine.cpp | 4 +++- 15 files changed, 22 insertions(+), 12 deletions(-) rename {TempScriptsFolder => Assets/Scripts}/AIPrototype.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/CameraControl.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/CameraFix.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/Item.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/PhysicsTest.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/PickAndThrow.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/PlayerController.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/PrintWhenActive.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/RaccoonShowcase.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/RaccoonSpin.cs (100%) rename {TempScriptsFolder => Assets/Scripts}/ThirdPersonCamera.cs (100%) diff --git a/TempScriptsFolder/AIPrototype.cs b/Assets/Scripts/AIPrototype.cs similarity index 100% rename from TempScriptsFolder/AIPrototype.cs rename to Assets/Scripts/AIPrototype.cs diff --git a/TempScriptsFolder/CameraControl.cs b/Assets/Scripts/CameraControl.cs similarity index 100% rename from TempScriptsFolder/CameraControl.cs rename to Assets/Scripts/CameraControl.cs diff --git a/TempScriptsFolder/CameraFix.cs b/Assets/Scripts/CameraFix.cs similarity index 100% rename from TempScriptsFolder/CameraFix.cs rename to Assets/Scripts/CameraFix.cs diff --git a/TempScriptsFolder/Item.cs b/Assets/Scripts/Item.cs similarity index 100% rename from TempScriptsFolder/Item.cs rename to Assets/Scripts/Item.cs diff --git a/TempScriptsFolder/PhysicsTest.cs b/Assets/Scripts/PhysicsTest.cs similarity index 100% rename from TempScriptsFolder/PhysicsTest.cs rename to Assets/Scripts/PhysicsTest.cs diff --git a/TempScriptsFolder/PickAndThrow.cs b/Assets/Scripts/PickAndThrow.cs similarity index 100% rename from TempScriptsFolder/PickAndThrow.cs rename to Assets/Scripts/PickAndThrow.cs diff --git a/TempScriptsFolder/PlayerController.cs b/Assets/Scripts/PlayerController.cs similarity index 100% rename from TempScriptsFolder/PlayerController.cs rename to Assets/Scripts/PlayerController.cs diff --git a/TempScriptsFolder/PrintWhenActive.cs b/Assets/Scripts/PrintWhenActive.cs similarity index 100% rename from TempScriptsFolder/PrintWhenActive.cs rename to Assets/Scripts/PrintWhenActive.cs diff --git a/TempScriptsFolder/RaccoonShowcase.cs b/Assets/Scripts/RaccoonShowcase.cs similarity index 100% rename from TempScriptsFolder/RaccoonShowcase.cs rename to Assets/Scripts/RaccoonShowcase.cs diff --git a/TempScriptsFolder/RaccoonSpin.cs b/Assets/Scripts/RaccoonSpin.cs similarity index 100% rename from TempScriptsFolder/RaccoonSpin.cs rename to Assets/Scripts/RaccoonSpin.cs diff --git a/TempScriptsFolder/ThirdPersonCamera.cs b/Assets/Scripts/ThirdPersonCamera.cs similarity index 100% rename from TempScriptsFolder/ThirdPersonCamera.cs rename to Assets/Scripts/ThirdPersonCamera.cs diff --git a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp index c3c7ef03..c913030b 100644 --- a/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp +++ b/SHADE_Engine/src/AudioSystem/SHAudioSystem.cpp @@ -17,6 +17,8 @@ #include #include +const std::string AUDIO_FOLDER_PATH{ std::string(ASSET_ROOT)+ "/Audio/" }; + namespace SHADE { SHAudioSystem::SHAudioSystem() @@ -79,10 +81,10 @@ namespace SHADE //SHResourceManager::LoadAllAudio(system, soundList); - LoadBank("../../Assets/Audio/Master.bank"); - LoadBank("../../Assets/Audio/Master.strings.bank"); - //LoadBank("../../Assets/Audio/Music.bank"); - LoadBank("../../Assets/Audio/footsteps.bank"); + LoadBank((AUDIO_FOLDER_PATH + "Master.bank").data()); + LoadBank((AUDIO_FOLDER_PATH + "Master.strings.bank").data()); + //LoadBank((AUDIO_FOLDER_PATH + "Music.bank").data()); + LoadBank((AUDIO_FOLDER_PATH + "footsteps.bank").data()); //auto clip = CreateAudioClip("event:/Characters/sfx_footsteps_human"); //clip->Play(); diff --git a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp index c18f0c8c..cfb36cd0 100644 --- a/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp +++ b/SHADE_Engine/src/Editor/EditorWindow/MenuBar/SHEditorMenuBar.cpp @@ -24,6 +24,9 @@ #include "Serialization/SHSerialization.h" #include "Serialization/Configurations/SHConfigurationManager.h" + +const std::string LAYOUT_FOLDER_PATH{ std::string(ASSET_ROOT) + "/Editor/Layouts" }; + namespace SHADE { constexpr ImGuiWindowFlags editorMenuBarFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | @@ -43,8 +46,7 @@ namespace SHADE void SHEditorMenuBar::Init() { SHEditorWindow::Init(); - constexpr std::string_view path = "../../Assets/Editor/Layouts"; - for(auto const& entry : std::filesystem::directory_iterator(path)) + for(auto const& entry : std::filesystem::directory_iterator(LAYOUT_FOLDER_PATH)) { layoutPaths.push_back(entry.path()); } diff --git a/SHADE_Engine/src/Editor/SHEditor.cpp b/SHADE_Engine/src/Editor/SHEditor.cpp index 19d147e6..c4ad3459 100644 --- a/SHADE_Engine/src/Editor/SHEditor.cpp +++ b/SHADE_Engine/src/Editor/SHEditor.cpp @@ -65,6 +65,10 @@ RTTR_REGISTRATION ); } +const std::string USER_LAYOUT_PATH{ std::string(ASSET_ROOT) + "/Editor/Layouts/UserLayout.ini" }; +const std::string DEFAULT_LAYOUT_PATH{ std::string(ASSET_ROOT) + "/Editor/Layouts/Default.ini" }; +const std::string FONT_FOLDER_PATH{ std::string(ASSET_ROOT) + "/Editor/Fonts/"}; + namespace SHADE { @@ -106,7 +110,7 @@ namespace SHADE io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io->ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //Enable for Multi-Viewports io->ConfigFlags |= ImGuiConfigFlags_DockingEnable; //Enable docking - io->IniFilename = "../../Assets/Editor/Layouts/UserLayout.ini"; + io->IniFilename = USER_LAYOUT_PATH.data(); io->ConfigWindowsMoveFromTitleBarOnly = true; InitLayout(); @@ -236,20 +240,20 @@ namespace SHADE { if(!std::filesystem::exists(io->IniFilename)) { - std::filesystem::copy_file("../../Assets/Editor/Layouts/Default.ini", io->IniFilename); + std::filesystem::copy_file(DEFAULT_LAYOUT_PATH.data(), io->IniFilename); } //eventually load preferred layout here } void SHEditor::InitFonts() noexcept { - ImFont* mainFont = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/Segoe UI.ttf", 20.f);//TODO: Change to config based assets path + ImFont* mainFont = io->Fonts->AddFontFromFileTTF(std::string(FONT_FOLDER_PATH + "Segoe UI.ttf").data(), 20.f);//TODO: Change to config based assets path ImFontConfig icons_config{}; icons_config.MergeMode = true; icons_config.GlyphOffset.y = 5.f; constexpr ImWchar icon_ranges_fa[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; - ImFont* UIFontFA = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/fa-solid-900.ttf", 20.f, &icons_config, icon_ranges_fa); //TODO: Change to config based assets path + ImFont* UIFontFA = io->Fonts->AddFontFromFileTTF(std::string(FONT_FOLDER_PATH + "fa-solid-900.ttf").data(), 20.f, &icons_config, icon_ranges_fa); //TODO: Change to config based assets path constexpr ImWchar icon_ranges_md[] = { ICON_MIN_MD, ICON_MAX_16_MD, 0 }; - ImFont* UIFontMD = io->Fonts->AddFontFromFileTTF("../../Assets/Editor/Fonts/MaterialIcons-Regular.ttf", 20.f, &icons_config, icon_ranges_md); //TODO: Change to config based assets path + ImFont* UIFontMD = io->Fonts->AddFontFromFileTTF(std::string(FONT_FOLDER_PATH + "MaterialIcons-Regular.ttf").data(), 20.f, &icons_config, icon_ranges_md); //TODO: Change to config based assets path io->Fonts->Build(); } diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index 827f45a2..4f3fbce6 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -26,13 +26,15 @@ of DigiPen Institute of Technology is prohibited. #include "Events/SHEventManager.hpp" #include "Physics/SHPhysicsSystem.h" +#include "Assets/SHAssetMacros.h" + namespace SHADE { /*-----------------------------------------------------------------------------------*/ /* Static Definitions */ /*----------------------------------------------------------------------------------*/ const std::string SHScriptEngine::DEFAULT_CSHARP_NAMESPACE = std::string("SHADE"); - const std::string SHScriptEngine::CSPROJ_DIR = "..\\..\\TempScriptsFolder"; + const std::string SHScriptEngine::CSPROJ_DIR = std::string(ASSET_ROOT) + "/Scripts"; const std::string SHScriptEngine::CSPROJ_PATH = std::string(CSPROJ_DIR) + "\\SHADE_Scripting.csproj"; /*-----------------------------------------------------------------------------------*/ From 35bcdc5239ca3cd86ea697504f98c3355d122a9e Mon Sep 17 00:00:00 2001 From: Xiao Qi Date: Thu, 10 Nov 2022 11:14:40 +0800 Subject: [PATCH 113/113] Missed out path for config --- .../src/Serialization/Configurations/SHConfigurationManager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.h b/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.h index abf679ca..767b8c2b 100644 --- a/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.h +++ b/SHADE_Engine/src/Serialization/Configurations/SHConfigurationManager.h @@ -25,8 +25,8 @@ namespace SHADE class SH_API SHConfigurationManager { public: - static constexpr std::string_view applicationConfigPath{"../../Assets/Application.SHConfig"}; - static constexpr std::string_view editorConfigPath{"../../Assets/Editor/Editor.SHConfig"}; + static inline std::string applicationConfigPath{ std::string(ASSET_ROOT) + "/Application.SHConfig"}; + static inline std::string editorConfigPath{ std::string(ASSET_ROOT) + "/Editor/Editor.SHConfig"}; static void SaveApplicationConfig(); static SHApplicationConfig& LoadApplicationConfig(WindowData* wndData = nullptr);