diff --git a/Assets/RaccoonBag_Color_Ver4.dds b/Assets/RaccoonBag_Color_Ver4.dds new file mode 100644 index 00000000..a229bc00 Binary files /dev/null and b/Assets/RaccoonBag_Color_Ver4.dds differ diff --git a/Assets/RaccoonPreTexturedVer1_Base9.dds b/Assets/RaccoonPreTexturedVer1_Base9.dds new file mode 100644 index 00000000..1a35b4de Binary files /dev/null and b/Assets/RaccoonPreTexturedVer1_Base9.dds differ diff --git a/Assets/racoon.fbx b/Assets/racoon.fbx new file mode 100644 index 00000000..45ed752b Binary files /dev/null and b/Assets/racoon.fbx differ diff --git a/Dependencies.bat b/Dependencies.bat index 0b1e1a54..7c1d159d 100644 --- a/Dependencies.bat +++ b/Dependencies.bat @@ -18,10 +18,11 @@ echo "K - RTTR" echo "L - yamlcpp" echo "M - SDL" echo "N - dotnet" +echo "O - tinyddsloader" echo --------------------------------------------------- echo. -choice /C ABCDEFGHIJKLMN /T 10 /D A +choice /C ABCDEFGHIJKLMNO /T 10 /D A set _e=%ERRORLEVEL% if %_e%==1 goto VMA @@ -38,6 +39,7 @@ if %_e%==11 goto RTTR if %_e%==12 goto yamlcpp if %_e%==13 goto SDL if %_e%==14 goto dotnet +if %_e%==15 goto tinyddsloader :VMA echo -----------------------VMA---------------------------- @@ -136,6 +138,13 @@ robocopy "Dependencies/dotnet/tmp/shared/Microsoft.NETCore.App/6.0.8/" "Dependen rmdir "Dependencies/dotnet/tmp/" /s /q del "Dependencies/dotnet/dotnet.zip" powershell -Command "& {Remove-Item "Dependencies/dotnet/dotnet.zip"}" +if %_e%==14 (goto :done) else (goto :tinyddsloader) + + +:tinyddsloader +echo --------------------tinyddsloader------------------------- +rmdir "Dependencies/tinyddsloader" /S /Q +git clone https://github.com/benikabocha/tinyddsloader.git "Dependencies/tinyddsloader" :done echo DONE! diff --git a/Dependencies.lua b/Dependencies.lua index 8e8df730..6dd7a711 100644 --- a/Dependencies.lua +++ b/Dependencies.lua @@ -14,4 +14,5 @@ IncludeDir["reactphysics3d"] = "%{wks.location}\\Dependencies\\reactphysics3d" IncludeDir["SDL"] = "%{wks.location}\\Dependencies\\SDL" IncludeDir["VULKAN"] = "$(VULKAN_SDK)" IncludeDir["dotnet"] = "%{wks.location}\\Dependencies\\dotnet" +IncludeDir["tinyddsloader"] = "%{wks.location}\\Dependencies\\tinyddsloader" IncludeDir["fmod"] = "%{wks.location}\\Dependencies\\fmod" \ No newline at end of file diff --git a/SHADE_Application/premake5.lua b/SHADE_Application/premake5.lua index c79d436e..a51eb8e6 100644 --- a/SHADE_Application/premake5.lua +++ b/SHADE_Application/premake5.lua @@ -34,6 +34,7 @@ project "SHADE_Application" "%{IncludeDir.VULKAN}/include", "%{IncludeDir.VMA}/include", "%{IncludeDir.VULKAN}/Source/SPIRV-Reflect", + "%{IncludeDir.tinyddsloader}" "%{IncludeDir.fmod}/include", "%{IncludeDir.RTTR}\\include" } diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index a7aa1cb7..4295e692 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -30,6 +30,8 @@ #include "Scenes/SBTestScene.h" #include "Math/Transform/SHTransformComponent.h" +#include "Assets/SHAssetManager.h" + using namespace SHADE; namespace Sandbox @@ -78,6 +80,12 @@ namespace Sandbox SHADE::SHComponentManager::CreateComponentSparseSet(); SHADE::SHSystemManager::RegisterRoutine(); + + //TODO: REMOVE AFTER PRESENTATION + SHADE::SHAssetManager::LoadDataTemp("../../Assets/racoon.fbx"); + SHADE::SHAssetManager::LoadDataTemp("../../Assets/RaccoonBag_Color_Ver4.dds"); + SHADE::SHAssetManager::LoadDataTemp("../../Assets/RaccoonPreTexturedVer1_Base9.dds"); + //TODO: REMOVE AFTER PRESENTATION SHADE::SHSystemManager::RegisterRoutine(); //SHADE::SHComponentManager::CreateComponentSparseSet(); diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index fc2f9731..01d2a18e 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -10,6 +10,8 @@ #include "Scripting/SHScriptEngine.h" #include "Math/Transform/SHTransformComponent.h" +#include "Assets/SHAssetManager.h" + using namespace SHADE; namespace Sandbox @@ -33,6 +35,23 @@ namespace Sandbox SHADE::SHGraphicsSystem* graphicsSystem = static_cast(SHADE::SHSystemManager::GetSystem()); // Create temp meshes const auto CUBE_MESH = SHADE::SHPrimitiveGenerator::Cube(*graphicsSystem); + //graphicsSystem->BuildMeshBuffers(); + + //Test Racoon mesh + auto meshes = SHADE::SHAssetManager::GetAllMeshes(); + std::vector> handles; + for (auto const& mesh : meshes) + { + handles.push_back(graphicsSystem->AddMesh( + mesh.header.vertexCount, + mesh.vertexPosition.data(), + mesh.texCoords.data(), + mesh.vertexTangent.data(), + mesh.vertexNormal.data(), + mesh.header.indexCount, + mesh.indices.data() + )); + } graphicsSystem->BuildMeshBuffers(); // Create Materials @@ -44,22 +63,33 @@ namespace Sandbox constexpr int NUM_COLS = 1; static const SHVec3 TEST_OBJ_SPACING = { 1.0f, 1.0f, 1.0f }; static const SHVec3 TEST_OBJ_START_POS = { - (NUM_COLS / 2 * TEST_OBJ_SPACING.x ), 0.0f, 0.0f }; - for (int z = 0; z < NUM_ROWS; ++z) - for (int x = 0; x < NUM_COLS; ++x) - { + //for (int z = 0; z < NUM_ROWS; ++z) + //for (int x = 0; x < NUM_COLS; ++x) + //{ + // auto entity = SHEntityManager::CreateEntity(); + // auto& renderable = *SHComponentManager::GetComponent_s(entity); + // auto& transform = *SHComponentManager::GetComponent_s(entity); + + // renderable.Mesh = handles.front(); + // renderable.SetMaterial(matInst); + + // // Set initial positions + // transform.SetWorldPosition(TEST_OBJ_START_POS + SHVec3{ x * TEST_OBJ_SPACING.x, 0.0f, z * TEST_OBJ_SPACING.z }); + // //transform.SetLocalScale(TEST_OBJ_SCALE); + + // stressTestObjects.emplace_back(entity); + //} + auto entity = SHEntityManager::CreateEntity(); auto& renderable = *SHComponentManager::GetComponent_s(entity); auto& transform = *SHComponentManager::GetComponent_s(entity); - renderable.Mesh = CUBE_MESH; + renderable.Mesh = handles.front(); renderable.SetMaterial(matInst); - // Set initial positions - transform.SetWorldPosition(TEST_OBJ_START_POS + SHVec3{ x * TEST_OBJ_SPACING.x, 0.0f, z * TEST_OBJ_SPACING.z }); //transform.SetLocalScale(TEST_OBJ_SCALE); stressTestObjects.emplace_back(entity); - } // Create blank entity with a script testObj = SHADE::SHEntityManager::CreateEntity(); diff --git a/SHADE_Engine/premake5.lua b/SHADE_Engine/premake5.lua index 4ab2f9d1..73ac8e68 100644 --- a/SHADE_Engine/premake5.lua +++ b/SHADE_Engine/premake5.lua @@ -42,7 +42,8 @@ project "SHADE_Engine" "%{IncludeDir.VULKAN}\\include", "%{IncludeDir.VULKAN}\\Source\\SPIRV-Reflect", "%{IncludeDir.dotnet}\\include", - "%{IncludeDir.fmod}\\include", + "%{IncludeDir.tinyddsloader}", + "%{IncludeDir.fmod}\\include" } externalwarnings "Off" @@ -105,9 +106,15 @@ project "SHADE_Engine" { "xcopy /s /r /y /q \"%{IncludeDir.spdlog}\\bin\" \"$(OutDir)\"", "xcopy /r /y /q \"%{IncludeDir.SDL}\\lib\\SDL2.dll\" \"$(OutDir)\"", - "xcopy /s /r /y /q \"%{IncludeDir.dotnet}\\bin\" \"$(OutDir)\"" + "xcopy /s /r /y /q \"%{IncludeDir.dotnet}\\bin\" \"$(OutDir)\"" } + filter "configurations:Debug" + postbuildcommands {"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Debug\\assimp-vc142-mtd.dll\" \"$(OutDir)\""} + + filter "configurations:Release" + postbuildcommands {"xcopy /r /y /q \"%{IncludeDir.assimp}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\""} + warnings 'Extra' filter "configurations:Debug" diff --git a/SHADE_Engine/src/Assets/Asset Types/SHDDSAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHDDSAsset.h new file mode 100644 index 00000000..30f22c4e --- /dev/null +++ b/SHADE_Engine/src/Assets/Asset Types/SHDDSAsset.h @@ -0,0 +1,11 @@ +#pragma once + +#include "tinyddsloader.h" + +namespace SHADE +{ + struct SHDDSAsset + { + tinyddsloader::DDSFile image; + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h b/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h new file mode 100644 index 00000000..a927d54b --- /dev/null +++ b/SHADE_Engine/src/Assets/Asset Types/SHMeshAsset.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include "Math/SHMath.h" + +namespace SHADE +{ + struct SHMeshAssetHeader + { + uint32_t vertexCount; + uint32_t indexCount; + }; + + struct SHMeshAsset + { + bool compiled; + bool changed; + + SHMeshAssetHeader header; + + std::string meshName; + + std::vector vertexPosition; + std::vector texCoords; + std::vector vertexTangent; + std::vector vertexNormal; + std::vector indices; + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Libraries/SHDDSLoader.cpp b/SHADE_Engine/src/Assets/Libraries/SHDDSLoader.cpp new file mode 100644 index 00000000..32eab9a9 --- /dev/null +++ b/SHADE_Engine/src/Assets/Libraries/SHDDSLoader.cpp @@ -0,0 +1,38 @@ +#include "SHpch.h" +#include "SHDDSLoader.h" + +namespace SHADE +{ + std::string SHDDSLoader::TinyDDSResultToString(tinyddsloader::Result value) + { + switch (value) + { + case tinyddsloader::Result::ErrorFileOpen: + return "File open err"; + case tinyddsloader::Result::ErrorRead: + return "File read err"; + case tinyddsloader::Result::ErrorMagicWord: + return "File header magicword err"; + case tinyddsloader::Result::ErrorSize: + return "File size err"; + case tinyddsloader::Result::ErrorVerify: + return "Pixel format err"; + case tinyddsloader::Result::ErrorNotSupported: + return "Unsupported format"; + case tinyddsloader::Result::ErrorInvalidData: + return "Invalid data"; + default: + return "Unknown"; + } + } + + void SHDDSLoader::LoadImageAsset(AssetPath path, SHDDSAsset& asset) + { + tinyddsloader::Result loadResult = tinyddsloader::Result::Success; + loadResult = asset.image.Load(path.string().c_str()); + if (loadResult != tinyddsloader::Result::Success) + { + SHLOG_ERROR("Unable to load DDS file: {} at {}", TinyDDSResultToString(loadResult), path.string()); + } + } +} diff --git a/SHADE_Engine/src/Assets/Libraries/SHDDSLoader.h b/SHADE_Engine/src/Assets/Libraries/SHDDSLoader.h new file mode 100644 index 00000000..e2bd734a --- /dev/null +++ b/SHADE_Engine/src/Assets/Libraries/SHDDSLoader.h @@ -0,0 +1,18 @@ +#pragma once +#define TINYDDSLOADER_IMPLEMENTATION + +#include "../SHAssetMacros.h" +#include "../Asset Types/SHDDSAsset.h" +#include "tinyddsloader.h" +#include + +namespace SHADE +{ + class SHDDSLoader + { + private: + static std::string TinyDDSResultToString(tinyddsloader::Result value); + public: + static void LoadImageAsset(AssetPath paths, SHDDSAsset& image); + }; +} diff --git a/SHADE_Engine/src/Assets/Libraries/SHMeshLoader.cpp b/SHADE_Engine/src/Assets/Libraries/SHMeshLoader.cpp new file mode 100644 index 00000000..c1637e1f --- /dev/null +++ b/SHADE_Engine/src/Assets/Libraries/SHMeshLoader.cpp @@ -0,0 +1,131 @@ +#include "SHpch.h" +#include "SHMeshLoader.h" +#include + +namespace SHADE +{ + Assimp::Importer SHMeshLoader::aiImporter; + + void SHMeshLoader::ProcessNode(aiNode const& node, aiScene const& scene, std::vector& meshes) + { + for (size_t i {0}; i < node.mNumMeshes; ++i) + { + aiMesh* mesh = scene.mMeshes[node.mMeshes[i]]; + meshes.push_back(ProcessMesh(*mesh, scene)); + } + + for (size_t i{ 0 }; i < node.mNumChildren; ++i) + { + ProcessNode(*node.mChildren[i], scene, meshes); + } + } + + SHMeshAsset SHMeshLoader::ProcessMesh(aiMesh const& mesh, aiScene const& scene) + { + (void)scene; + + SHMeshAsset result + { + .compiled { false}, + .changed { false }, + .meshName { mesh.mName.C_Str() } + }; + + 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 = result.vertexPosition.size(); + result.header.indexCount = result.indices.size(); + + return result; + } + + bool SHMeshLoader::LoadMesh(std::vector& meshes, AssetPath path) + { + 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_PreTransformVertices // pre-transform all vertices + | 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 false; + } + //TODO MATERIALS FROM MESHES + //if (scene->HasMaterials()) + //{ + // for (int i{0}; i < scene->mNumMaterials; ++i) + // { + // if (scene->mMaterials[i]->mNumProperties > 0) + // { + // for (int j{0}; j < scene->mMaterials[i]->mProperties[j].) + // } + //std::cout << scene->mMaterials[i]->; + // } + //} + + ProcessNode(*scene->mRootNode, *scene, meshes); + + return true; + } +} diff --git a/SHADE_Engine/src/Assets/Libraries/SHMeshLoader.h b/SHADE_Engine/src/Assets/Libraries/SHMeshLoader.h new file mode 100644 index 00000000..fc8b548a --- /dev/null +++ b/SHADE_Engine/src/Assets/Libraries/SHMeshLoader.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include +#include "../SHAssetMacros.h" +#include "../Asset Types/SHMeshAsset.h" +#include + +namespace SHADE +{ + class SHMeshLoader + { + private: + static Assimp::Importer aiImporter; + + static void ProcessNode(aiNode const& node, aiScene const& scene, std::vector& meshes); + + static SHMeshAsset ProcessMesh(aiMesh const& mesh, aiScene const& scene); + public: + static bool LoadMesh(std::vector& meshes, AssetPath path); + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/SHAsset.h b/SHADE_Engine/src/Assets/SHAsset.h new file mode 100644 index 00000000..0ba2285f --- /dev/null +++ b/SHADE_Engine/src/Assets/SHAsset.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Filesystem/SHFileSystem.h" +#include "SHAssetMacros.h" + +namespace SHADE +{ + struct SHAsset + { + AssetName name; + AssetID id; + AssetType type; + AssetPath path; + FolderLocation location; + }; +} \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/SHAssetMacros.h b/SHADE_Engine/src/Assets/SHAssetMacros.h new file mode 100644 index 00000000..b8940d3e --- /dev/null +++ b/SHADE_Engine/src/Assets/SHAssetMacros.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * \file SHAssetMacros.h + * \author Loh Xiao Qi + * \brief Macros and typedefs for assets + * + * \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 + ******************************************************************************/ +#ifndef SH_ASSET_MACROS_H +#define SH_ASSET_MACROS_H + +#include +#include +#include + +// FMOD Fwd Declare +namespace FMOD +{ + class Sound; + class System; + class ChannelGroup; + class Channel; +} +enum FMOD_RESULT : int; +enum FMOD_SPEAKERMODE : int; + +// Typedefs +typedef uint32_t AssetID; +typedef std::string AssetName; +typedef std::filesystem::path AssetPath; +typedef unsigned char* AssetData; +typedef std::string AssetMetaVersion; +typedef std::string AssetExtension; +typedef unsigned char AssetTypeMeta; + +typedef FMOD::Sound* SHSound; + +// Asset Meta Version +#define ASSET_META_VER "1.0" + +// Asset type enum +enum class AssetType : uint8_t +{ + INVALID = 0, + AUDIO = 1, + SHADER, + MATERIAL, + IMAGE, + TEXTURE, + MESH, + SCRIPT, + SCENE, + PREFAB, + AUDIO_WAV, + DDS +}; + +//Directory +#define ASSET_ROOT "./Assets/" + +// ASSET EXTENSIONS +#define META_EXTENSION ".shmeta" +#define IMAGE_EXTENSION ".png" +#define AUDIO_EXTENSION ".ogg" +#define AUDIO_WAV_EXTENSION ".wav" +#define SHADER_EXTENSION ".glsl" +#define SCRIPT_EXTENSION ".cs" +#define SCENE_EXTENSION ".SHADE" +#define PREFAB_EXTENSION ".SHPrefab" +#define MATERIAL_EXTENSION ".SHMat" +#define TEXTURE_EXTENSION ".dds" +#define MESH_EXTENSION ".fbx" + +std::string const EXTENSIONS[] = { + AUDIO_EXTENSION, + SHADER_EXTENSION, + MATERIAL_EXTENSION, + IMAGE_EXTENSION, + TEXTURE_EXTENSION, + MESH_EXTENSION, + SCRIPT_EXTENSION, + SCENE_EXTENSION, + PREFAB_EXTENSION, + AUDIO_WAV_EXTENSION +}; + +// Error flags +#define FILE_NOT_FOUND_ERR "FILE NOT FOUND" +#define META_NOT_FOUND_ERR "META NOT FOUND" +#define ASSET_NOT_FOUND_ERR "ASSET NOT FOUND" +#define EXT_DOES_NOT_EXIST "TYPE DOES NOT HAVE EXTENSION DEFINED" + +#endif // !SH_ASSET_MACROS_H diff --git a/SHADE_Engine/src/Assets/SHAssetManager.cpp b/SHADE_Engine/src/Assets/SHAssetManager.cpp new file mode 100644 index 00000000..ac05df59 --- /dev/null +++ b/SHADE_Engine/src/Assets/SHAssetManager.cpp @@ -0,0 +1,430 @@ +/****************************************************************************** + * \file SHAssetManager.cpp + * \author Loh Xiao Qi + * \brief Implementations for SHAssetManager.h + * + * \copyright Copyright (c) 2021 Digipen Institute of Technology. Reproduction + * or disclosure of this file or its contents without the prior + * written consent of Digipen Institute of Technology is prohibited. + ******************************************************************************/ +#include "SHpch.h" +#include +#include +#include "SHAssetManager.h" +#include "SHAssetMetaHandler.h" +#include "Filesystem/SHFileSystem.h" + +#include "Libraries/SHMeshLoader.h" +#include "Libraries/SHDDSLoader.h" + +namespace SHADE +{ + FMOD::System* SHAssetManager::audioSystem; + std::unordered_map* SHAssetManager::audioSoundList; + + std::vector SHAssetManager::assetCollection; + std::unordered_map SHAssetManager::assetRegistry; + + std::unordered_map SHAssetManager::meshCollection; + std::unordered_map SHAssetManager::ddsCollection; + + /**************************************************************************** + * \brief Static function to generate asset ID. + ****************************************************************************/ + AssetID SHAssetManager::GenerateAssetID(AssetType type) noexcept + { + std::default_random_engine randEngine{ + static_cast(std::chrono::system_clock::now().time_since_epoch().count()) }; + std::mt19937 idGen{ randEngine() }; + AssetID result{ static_cast(type) << 24}; + AssetID unique{ idGen() & ((1 << 24) - 1) }; + + result |= unique; + + while (result == 0) + { + result = GenerateAssetID(type); + } + return result; + } + + /**************************************************************************** + * \brief Deallocate all memory used by asset data + ****************************************************************************/ + void SHAssetManager::Unload() noexcept + { + for (auto const& asset : assetCollection) + { + SHAssetMetaHandler::WriteMetaData(asset); + } + } + + AssetPath SHAssetManager::GenerateLocalPath(AssetPath path) noexcept + { + if (!IsRecognised(path.extension().string().c_str())) + { + //TODO:ASSERT UNRECOGNISED FILE TYPE + return std::filesystem::path(); + } + + AssetType type = SHAssetMetaHandler::GetTypeFromExtension(path.extension().string().c_str()); + std::string folder; + switch (type) + { + default: + //TODO:ASSERT UNSUPPORTED FILE TYPE + return std::filesystem::path(); + } + + return std::filesystem::path(ASSET_ROOT + folder + path.filename().string()); + } + + /**************************************************************************** + * \brief Get record of all assets currently loaded with name and id. + * + * \return const& to unordered_map + ****************************************************************************/ + std::vector const& SHAssetManager::GetAllAssets() noexcept + { + return assetCollection; + } + + /**************************************************************************** + * \brief Create record for new asset. CAN ONLY CREATE FOR CUSTOM + * ASSETS CREATED BY THE ENGINE. + * + * \param type of asset + * \param name of asset + * \return asset id generated for new asset + ****************************************************************************/ + AssetID SHAssetManager::CreateNewAsset(AssetType type, AssetName name) noexcept + { + AssetID id{ GenerateAssetID(type) }; + SHAsset meta; + meta.id = id; + meta.type = type; + + std::string folder; + switch (type) + { + default: + folder = ""; + break; + } + AssetPath path{ ASSET_ROOT + folder + name + SHAssetMetaHandler::GetExtensionFromType(type) }; + + SHAssetMetaHandler::WriteMetaData(meta); + + assetCollection.push_back(meta); + + return id; + } + + /**************************************************************************** + * \brief Import new asset from outside editor window. + * + * \param path - c style string to full path + * \return asset if generated for new + ****************************************************************************/ + AssetID SHAssetManager::ImportNewAsset(char const* p) noexcept + { + std::filesystem::path const path{ p }; + + std::filesystem::path const newPath{ GenerateLocalPath(path) }; + if (newPath.empty()) + { + SHLOG_WARNING("Unsupported file format for asset: {}", path.string()); + return 0; + } + + std::filesystem::copy(path, newPath); + + AssetID id{ RetrieveAsset(newPath.string().c_str()) }; + if (id != 0) + { + LoadData(id); + } + + return id; + } + + /**************************************************************************** + * \brief Search through assets folder for new unregistered assets. + * Takes in no params and returns nothing. Only updates internally. + ****************************************************************************/ + void SHAssetManager::RefreshAllAssets() noexcept + { + std::vector metaFiles; + std::vector AssetFiles; + + //SHFileSystem::LoadAllFiles(metaFiles, AssetFiles); + //std::vector AssetFilesVerified; + std::vector AssetFilesNew; + + for (auto const& asset : AssetFiles) + { + bool found = false; + for (auto it {metaFiles.begin()}; it != metaFiles.end(); ++it) + { + std::string fileExtCheck{ asset.filename().string() }; + fileExtCheck += META_EXTENSION; + if (it->filename().string() == fileExtCheck) + { + metaFiles.erase(it); + found = true; + break; + } + } + + if (!found && IsRecognised(asset.extension().string().c_str())) + { + AssetFilesNew.push_back(asset); + } + } + + std::vector newLoad; + newLoad.reserve(AssetFilesNew.size()); + + //TODO: Handle if meta does not match all assets (if meta exist and asset doesnt, vice versa) + for (auto const& file : AssetFilesNew) + { + newLoad.push_back(RegisterAssetNew(file)); + } + + //UpdateAllSpriteSets(); + + } + + void SHAssetManager::LoadDataTemp(std::string p) noexcept + { + AssetPath path{ p }; + + if (path.extension().string() == MESH_EXTENSION) + { + LoadGLTF( + { + .name {path.filename().string()}, + .id {0}, + .type {AssetType::MESH}, + .path {path}, + .location {0} + } + ); + } + else if (path.extension().string() == TEXTURE_EXTENSION) + { + LoadDDS( + { + .name {path.filename().string()}, + .id {0}, + .type {AssetType::DDS}, + .path {path}, + .location {0} + } + ); + } + } + + std::vector SHAssetManager::GetAllMeshes() noexcept + { + std::vector result; + for (auto const& mesh : meshCollection) + { + result.push_back(mesh.second); + } + + return result; + } + + /**************************************************************************** + * \param Path for meta data file + * \param Path for asset file + + * \brief Links meta data to asset in registries. Meta data should + * already exist + ****************************************************************************/ + void SHAssetManager::RegisterAsset(AssetPath const& metaPath, AssetPath const& path) noexcept + { + SHAsset const meta = SHAssetMetaHandler::RetrieveMetaData(metaPath); + + assetCollection.push_back(meta); + } + + /**************************************************************************** + * \param Path for asset file + + * \brief Creates new meta data for new asset. + ****************************************************************************/ + SHAsset SHAssetManager::RegisterAssetNew(AssetPath const& asset) noexcept + { + SHAsset meta; + meta.type = SHAssetMetaHandler::GetTypeFromExtension(asset.extension().string()); + meta.id = GenerateAssetID(meta.type); + + assetCollection.push_back(meta); + + SHAssetMetaHandler::WriteMetaData(meta); + return assetCollection.back(); + } + + bool SHAssetManager::IsRecognised(char const* ext) noexcept + { + for (auto const& e : EXTENSIONS) + { + if (strcmp(ext, e.c_str()) == 0) + { + return true; + } + } + + return false; + } + + void SHAssetManager::LoadGLTF(SHAsset asset) noexcept + { + std::vector meshes; + + SHMeshLoader::LoadMesh(meshes, asset.path); + + for (auto const& mesh : meshes) + { + meshCollection.emplace(GenerateAssetID(AssetType::MESH), mesh); + } + } + + void SHAssetManager::LoadDDS(SHAsset asset) noexcept + { + SHDDSAsset image; + + SHDDSLoader::LoadImageAsset(asset.path, image); + + ddsCollection.emplace(GenerateAssetID(AssetType::DDS), image); + } + + /**************************************************************************** + * \brief Load all assets that are in the folder + ****************************************************************************/ + void SHAssetManager::Load() noexcept + { + RetrieveAssets(); + LoadAllData(); + } + + /**************************************************************************** + * \brief Load asset data into memory + ****************************************************************************/ + void SHAssetManager::LoadAllData() noexcept + { + for (auto const& asset : assetCollection) + { + } + } + + void SHAssetManager::LoadData(AssetID id) noexcept + { + (void)id; + } + + /**************************************************************************** + * \brief Retrieve all asset files and meta files from filesystem + ****************************************************************************/ + void SHAssetManager::RetrieveAssets() noexcept + { + std::vector metaFiles; + std::vector AssetFiles; + + //TODO: Write new function for file manager to loop through all files + SHFileSystem::StartupFillDirectories(ASSET_ROOT); + FolderPointer rootFolder = SHFileSystem::GetRoot(); + + for (auto const& meta : metaFiles) + { + for (std::vector::const_iterator it{ AssetFiles.cbegin() }; + it != AssetFiles.cend(); + ++it) + { + // Asset exists for meta file + std::string fileExtCheck{ it->filename().string() }; + fileExtCheck += META_EXTENSION; + if (meta.filename().string() == fileExtCheck) + { + RegisterAsset(meta, *it); + AssetFiles.erase(it); + break; + } + } + } + + //TODO: Handle if meta does not match all assets (if meta exist and asset doesnt, vice versa) + for (auto const& file : AssetFiles) + { + if (IsRecognised(file.extension().string().c_str())) + { + SHAssetMetaHandler::WriteMetaData(RegisterAssetNew(file)); + } + else + { + std::cout << "Unsupported File Format: " << file.filename() << "\n"; + } + } + } + + AssetID SHAssetManager::RetrieveAsset(char const* path) noexcept + { + std::filesystem::path p{ path }; + if (IsRecognised(p.extension().string().c_str())) + { + SHAsset const& meta{ RegisterAssetNew(p) }; + SHAssetMetaHandler::WriteMetaData(meta); + return meta.id; + } + else + { + std::cout << "Unsupported File Format: " << p.filename() << "\n"; + } + + // Assert that file imported is not recognised + return 0; + } + + /**************************************************************************** + * \param Full path of file + + * \brief Extracts file name from path. Formats file name into readable + * with spaces and capitalises first letter of every word + ****************************************************************************/ + AssetName SHAssetManager::GetNameFromPath(AssetPath filepath) noexcept + { + std::string name{ filepath.filename().string() }; + name = name.substr(0, name.find_last_of('.')); + + //if (name[0] <= 122 && name[0] >= 97) + //{ + // name[0] -= 32; + //} + + //for (size_t i{ 1 }; i < name.length(); ++i) + //{ + // // Replace all underscores with spaces + // if (name[i] == '_') + // { + // name[i] = ' '; + // continue; + // } + + // if (name[i + 1] <= 'Z' && name[i + 1] >= 'A' + // && name[i] <= 'z' && name[i] >= 'a') + // { + // name.insert(i + 1, 1, ' '); + // continue; + // } + + // if (name[i - 1] == ' ' && name[i] <= 'z' && name[i] >= 'a') + // { + // name[i] -= 32; + // } + //} + + return name; + } +} diff --git a/SHADE_Engine/src/Assets/SHAssetManager.h b/SHADE_Engine/src/Assets/SHAssetManager.h new file mode 100644 index 00000000..8547e9e3 --- /dev/null +++ b/SHADE_Engine/src/Assets/SHAssetManager.h @@ -0,0 +1,132 @@ +/****************************************************************************** + * \file SHAssetManager.h + * \author Loh Xiao Qi + * \brief Interface for resource manager, to be used by engine side + * operations. + * + * \copyright Copyright (c) 2021 Digipen Institute of Technology. Reproduction + * or disclosure of this file or its contents without the prior + * written consent of Digipen Institute of Technology is prohibited. +******************************************************************************/ +#pragma once +#include "tinyddsloader.h" +#include "SHAsset.h" + +#include "Asset Types/SHMeshAsset.h" +#include "Asset Types/SHDDSAsset.h" +#include "SH_API.h" + +namespace SHADE +{ + class SH_API SHAssetManager + { + public: + /**************************************************************************** + * \brief Static function to generate resource ID. + ****************************************************************************/ + static AssetID GenerateAssetID(AssetType type) noexcept; + + static AssetPath GenerateLocalPath(AssetPath path) noexcept; + + /**************************************************************************** + * \brief Deallocate all memory used by resource data + ****************************************************************************/ + static void Unload() noexcept; + + /**************************************************************************** + * \brief Load all resources that are in the folder + ****************************************************************************/ + static void Load() noexcept; + + /**************************************************************************** + * \brief Get record of all resources currently loaded with name and id. + * + * \return const& to unordered_map + ****************************************************************************/ + static std::vector const& GetAllAssets() noexcept; + + /**************************************************************************** + * \brief Create record for new resource. CAN ONLY CREATE FOR CUSTOM + * RESOURCES CREATED BY THE ENGINE. + * + * \param type of resource + * \param name of resource + * \return resource id generated for new asset + ****************************************************************************/ + static AssetID CreateNewAsset(AssetType, AssetName) noexcept; + + /**************************************************************************** + * \brief Import new resource from outside editor window. + * + * \param path - c style string to full path + * \return resource if generated for new + ****************************************************************************/ + static AssetID ImportNewAsset(char const* path) noexcept; + + /**************************************************************************** + * \brief Search through resources folder for new unregistered assets. + * Takes in no params and returns nothing. Only updates internally. + ****************************************************************************/ + static void RefreshAllAssets() noexcept; + // -------------------------------------------------------------------------/ + + //TODO: TEMPORARY FOR TESTING GLTF & DDS + static void LoadDataTemp(std::string path) noexcept; + static std::vector GetAllMeshes() noexcept; + + private: + /**************************************************************************** + * \brief Load resource data into memory + ****************************************************************************/ + static void LoadAllData() noexcept; + + static void LoadData(AssetID id) noexcept; + + /**************************************************************************** + * \brief Retrieve all resource files and meta files from filesystem + ****************************************************************************/ + static void RetrieveAssets() noexcept; + + static AssetID RetrieveAsset(char const* path) noexcept; + + /**************************************************************************** + * \param Full path of file + + * \brief Extracts file name from path. Formats file name into readable + * with spaces and capitalises first letter of every word + ****************************************************************************/ + static AssetName GetNameFromPath(AssetPath) noexcept; + + /**************************************************************************** + * \param Path for meta data file + * \param Path for resource file + + * \brief Links meta data to resource in registries. Meta data should + * already exist + ****************************************************************************/ + static void RegisterAsset(AssetPath const&, AssetPath const&) noexcept; + + /**************************************************************************** + * \param Path for resource file + + * \brief Creates new meta data for new resource. + ****************************************************************************/ + static SHAsset RegisterAssetNew(AssetPath const&) noexcept; + + static bool IsRecognised(char const*) noexcept; + + // Specialised load calls + static void LoadGLTF(SHAsset asset) noexcept; + static void LoadDDS(SHAsset asset) noexcept; + + static FMOD::System* audioSystem; + static std::unordered_map* audioSoundList; + + // For all resources + static std::vector assetCollection; + static std::unordered_map assetRegistry; + + static std::unordered_map meshCollection; + static std::unordered_map ddsCollection; + }; +} diff --git a/SHADE_Engine/src/Assets/SHAssetMetaHandler.cpp b/SHADE_Engine/src/Assets/SHAssetMetaHandler.cpp new file mode 100644 index 00000000..aabb0dc0 --- /dev/null +++ b/SHADE_Engine/src/Assets/SHAssetMetaHandler.cpp @@ -0,0 +1,118 @@ +/****************************************************************************** + * \file SHAssetMetaHandler.cpp + * \author Loh Xiao Qi + * \brief Implementations for SHAssetMetaHandler.h + * + * \copyright Copyright (c) 2021 Digipen Institute of Technology. Reproduction + * or disclosure of this file or its contents without the prior + * written consent of Digipen Institute of Technology is prohibited + ******************************************************************************/ +#include "SHpch.h" +#include "SHAssetMetaHandler.h" +#include +#include + +namespace SHADE +{ + /**************************************************************************** + * \param reference to ifstream file to read line from + * \param reference to string to store line into + + * \brief Helper function to retrieve field value from meta data file + * for processing + ****************************************************************************/ + void GetFieldValue(std::ifstream& file, std::string& line) noexcept + { + line = ""; + std::getline(file, line); + line = line.substr(line.find_last_of(':') + 2, line.length()); + } + + /**************************************************************************** + * \param String containing extension of resource file + + * \brief Get correct resource type from file extension of resource. + ****************************************************************************/ + AssetType SHAssetMetaHandler::GetTypeFromExtension(AssetExtension ext) noexcept + { + for (int i{0}; i < EXTENSIONS->size(); ++i) + { + if (ext == EXTENSIONS[i]) + { + return static_cast(i); + } + } + + return AssetType::INVALID; + } + + /**************************************************************************** + * \param String containing extension of resource file + + * \brief Get correct resource type from file extension of resource. + ****************************************************************************/ + AssetExtension SHAssetMetaHandler::GetExtensionFromType(AssetType type) noexcept + { + return EXTENSIONS[static_cast(type)]; + } + + /**************************************************************************** + * \param Create class containing meta data from meta file + + * \brief path to meta data file + ****************************************************************************/ + SHAsset SHAssetMetaHandler::RetrieveMetaData(AssetPath const& path) noexcept + { + std::ifstream metaFile{ path.string(), std::ios_base::in }; + if (!metaFile.is_open()) + { + // Error unable to open + } + + std::string line; + SHAsset meta; + + // Get resource id + GetFieldValue(metaFile, line); + std::stringstream idStream{ line }; + AssetID id; + idStream >> id; + meta.id = id; + + // Get resource type + GetFieldValue(metaFile, line); + std::stringstream typeStream{ line }; + AssetTypeMeta type; + typeStream >> type; + meta.type = static_cast(type); + + metaFile.close(); + + return meta; + } + + /**************************************************************************** + * \param Asset meta data to be written into + * \param Path to be written into + + * \brief Writes meta data into text file + ****************************************************************************/ + void SHAssetMetaHandler::WriteMetaData(SHAsset const& meta) noexcept + { + std::string path{ meta.path.string() }; + path.append(META_EXTENSION); + + std::ofstream metaFile{ path, std::ios_base::out }; + + if (!metaFile.is_open()) + { + SHLOG_ERROR("Asset write path is invalid: {}", path); + return; + } + + metaFile << "ID: " << meta.id << "\n"; + metaFile << "Type: " << static_cast(meta.type) << std::endl; + metaFile.close(); + } + +} diff --git a/SHADE_Engine/src/Assets/SHAssetMetaHandler.h b/SHADE_Engine/src/Assets/SHAssetMetaHandler.h new file mode 100644 index 00000000..88b5329d --- /dev/null +++ b/SHADE_Engine/src/Assets/SHAssetMetaHandler.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * \file SHAssetMetaHandler.h + * \author Loh Xiao Qi + * \brief Handler classes for meta data for all resources + * + * \copyright Copyright (c) 2021 Digipen Institute of Technology. Reproduction + * or disclosure of this file or its contents without the prior + * written consent of Digipen Institute of Technology is prohibited + ******************************************************************************/ +#ifndef SH_RESOURCE_META_HANDLER_H +#define SH_RESOURCE_META_HANDLER_H + +#include "SHAssetMacros.h" +#include "SHAsset.h" + +namespace SHADE +{ + struct SHAssetMetaHandler + { + /**************************************************************************** + * \param String containing extension of resource file + + * \brief Get correct resource type from file extension of resource. + ****************************************************************************/ + static AssetType GetTypeFromExtension(AssetExtension) noexcept; + + /**************************************************************************** + * \param String containing extension of resource file + + * \brief Get correct resource type from file extension of resource. + ****************************************************************************/ + static AssetExtension GetExtensionFromType(AssetType) noexcept; + + /**************************************************************************** + * \param Create class containing meta data from meta file + + * \brief path to meta data file + ****************************************************************************/ + static SHAsset RetrieveMetaData(AssetPath const&) noexcept; + + /**************************************************************************** + * \param Asset meta data to be written into + * \param Path to be written into + + * \brief Writes meta data into text file + ****************************************************************************/ + static void WriteMetaData(SHAsset const&) noexcept; + }; +} + +#endif // !SH_RESOURCE_META_HANDLER_H diff --git a/SHADE_Engine/src/Filesystem/SHFileSystem.cpp b/SHADE_Engine/src/Filesystem/SHFileSystem.cpp index 5663dadd..16175578 100644 --- a/SHADE_Engine/src/Filesystem/SHFileSystem.cpp +++ b/SHADE_Engine/src/Filesystem/SHFileSystem.cpp @@ -80,6 +80,12 @@ namespace SHADE { if (!dirEntry.is_directory()) { + folder->files.emplace_back( + dirEntry.path().filename().string(), + dirEntry.path().string(), + dirEntry.path().extension().string() + ); + continue; } @@ -103,6 +109,11 @@ namespace SHADE } } + FolderPointer SHFileSystem::GetRoot() noexcept + { + return root; + } + FolderPointer SHFileSystem::CreateFolder(FolderPath path, FolderLocation parent, FolderHandle location, FolderName name) noexcept { assert( diff --git a/SHADE_Engine/src/Filesystem/SHFileSystem.h b/SHADE_Engine/src/Filesystem/SHFileSystem.h index 9b8b94a2..8df794fd 100644 --- a/SHADE_Engine/src/Filesystem/SHFileSystem.h +++ b/SHADE_Engine/src/Filesystem/SHFileSystem.h @@ -14,12 +14,22 @@ namespace SHADE typedef uint64_t FolderLocation; typedef uint64_t FolderHandle; typedef std::string FolderName; + typedef std::string FileName; typedef std::string FolderPath; + typedef std::string FilePath; + typedef std::string FileExt; typedef SHFolder* FolderPointer; constexpr char FOLDER_BIT_ALLOCATE{ 4 }; constexpr char FOLDER_MAX_DEPTH{ 16 }; + struct SHFile + { + FileName name; + FilePath path; + FileExt ext; + }; + class SHFolder { public: @@ -28,6 +38,7 @@ namespace SHADE FolderHandle id; FolderName name; std::vector subFolders; + std::vector files; bool folded; @@ -45,6 +56,8 @@ namespace SHADE static void StartupFillDirectories(FolderPath path) noexcept; + static FolderPointer GetRoot() noexcept; + private: static FolderPointer root;