diff --git a/Editor/src/ComponentsPanel/LightPanel.h b/Editor/src/ComponentsPanel/LightPanel.h index 07204b6f..5082e4be 100644 --- a/Editor/src/ComponentsPanel/LightPanel.h +++ b/Editor/src/ComponentsPanel/LightPanel.h @@ -58,7 +58,23 @@ public: ImGui::TableNextColumn(); const char* lightTypes[] = { "Directional", "Point", "Spot" }; - ComponentDropDown(lightTypes, Nuake::LightType, component.Type) + const char* current_item = lightTypes[(int)component.Type]; \ + if (ImGui::BeginCombo("Type", current_item)) \ + { \ + for (int n = 0; n < IM_ARRAYSIZE(lightTypes); n++) \ + { \ + bool is_selected = (current_item == lightTypes[n]); \ + if (ImGui::Selectable(lightTypes[n], is_selected)) \ + { \ + current_item = lightTypes[n]; \ + component.Type = (Nuake::LightType)n; \ + } \ + if (is_selected) \ + ImGui::SetItemDefaultFocus(); \ + } \ + ImGui::EndCombo(); \ + } + //ComponentDropDown(lightTypes, Nuake::LightType, component.Type) ImGui::TableNextColumn(); ComponentTableReset(component.Type, Nuake::LightType::Point); diff --git a/Editor/src/ComponentsPanel/MeshPanel.h b/Editor/src/ComponentsPanel/MeshPanel.h index 15770266..5cc8dbc6 100644 --- a/Editor/src/ComponentsPanel/MeshPanel.h +++ b/Editor/src/ComponentsPanel/MeshPanel.h @@ -44,10 +44,10 @@ public: std::string label = "None"; - const bool isModelNone = component.ModelResource == nullptr; + const bool isModelNone = component.ModelResource.Get() == nullptr; if (!isModelNone) { - label = std::to_string(component.ModelResource->ID); + label = std::to_string(component.ModelResource.ID); } if (ImGui::Button(label.c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0))) @@ -56,7 +56,7 @@ public: { if (!_expanded) { - _modelInspector = CreateScope(component.ModelResource); + _modelInspector = CreateScope(component.ModelResource.Get()); } _expanded = !_expanded; @@ -80,19 +80,19 @@ public: if (Nuake::String::EndsWith(fullPath, ".mesh")) { component.ModelPath = fullPath; - component.ModelResource = ResourceLoader::LoadModel(fullPath); + //component.ModelResource = ResourceLoader::LoadModel(fullPath); } else { // Convert to .Model - component.ModelPath = fullPath; - component.LoadModel(); - - _importedPathMesh = fullPath; - - auto loader = ModelLoader(); - auto modelResource = loader.LoadModel(fullPath); - shouldConvert = true; + //component.ModelPath = fullPath; + //component.LoadModel(); + // + //_importedPathMesh = fullPath; + // + //auto loader = ModelLoader(); + //auto modelResource = loader.LoadModel(fullPath); + //shouldConvert = true; } } ImGui::EndDragDropTarget(); @@ -101,20 +101,20 @@ public: if (PopupHelper::DefineConfirmationDialog("##ConvertAsset", "Convert Asset")) { // Convert to disk - auto loader = ModelLoader(); - Ref modelResource = loader.LoadModel(_importedPathMesh); - json serializedData = modelResource->SerializeData(); - - const std::string exportedMeshPath = _importedPathMesh + ".mesh"; - FileSystem::BeginWriteFile(exportedMeshPath); - FileSystem::WriteLine(serializedData.dump()); - FileSystem::EndWriteFile(); - - ResourceManager::RegisterResource(modelResource); - - // Update component - component.ModelPath = exportedMeshPath; - component.ModelResource = modelResource; + //auto loader = ModelLoader(); + //Ref modelResource = loader.LoadModel(_importedPathMesh); + //json serializedData = modelResource->SerializeData(); + // + //const std::string exportedMeshPath = _importedPathMesh + ".mesh"; + //FileSystem::BeginWriteFile(exportedMeshPath); + //FileSystem::WriteLine(serializedData.dump()); + //FileSystem::EndWriteFile(); + // + //ResourceManager::RegisterResource(modelResource); + // + //// Update component + //component.ModelPath = exportedMeshPath; + //component.ModelResource = modelResource; } if (shouldConvert) diff --git a/Editor/src/ComponentsPanel/NavMeshVolumePanel.h b/Editor/src/ComponentsPanel/NavMeshVolumePanel.h index 1994864d..1395eaa5 100644 --- a/Editor/src/ComponentsPanel/NavMeshVolumePanel.h +++ b/Editor/src/ComponentsPanel/NavMeshVolumePanel.h @@ -363,7 +363,7 @@ public: auto& transformComponent = brush.GetComponent(); auto& modelComponent = brush.GetComponent(); - for (auto& mesh : modelComponent.ModelResource->GetMeshes()) + for (auto& mesh : modelComponent.ModelResource.Get()->GetMeshes()) { Nuake::NavManager::Get().PushMesh(mesh, transformComponent.GetGlobalTransform()); } diff --git a/Editor/src/Misc/GizmoDrawer.cpp b/Editor/src/Misc/GizmoDrawer.cpp index d65f4347..ccd85ed1 100644 --- a/Editor/src/Misc/GizmoDrawer.cpp +++ b/Editor/src/Misc/GizmoDrawer.cpp @@ -433,12 +433,12 @@ void GizmoDrawer::DrawShapes(Ref scene, bool occluded) auto [transform, mesh, model] = scene->m_Registry.get(e); // Component has no mesh set. - if (!model.ModelResource) + if (!model.ModelResource.Get()) { continue; } - auto& resource = model.ModelResource; + auto resource = model.ModelResource.Get(); const auto& meshes = resource->GetMeshes(); if (mesh.SubMesh >= meshes.size()) diff --git a/Editor/src/Windows/EditorInterface.cpp b/Editor/src/Windows/EditorInterface.cpp index 2951627f..c9745c70 100644 --- a/Editor/src/Windows/EditorInterface.cpp +++ b/Editor/src/Windows/EditorInterface.cpp @@ -718,18 +718,18 @@ namespace Nuake { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("_Model")) { - char* file = (char*)payload->Data; - std::string fullPath = std::string(file, 256); - fullPath = Nuake::FileSystem::AbsoluteToRelative(fullPath); - - auto loader = ModelLoader(); - auto modelResource = loader.LoadModel(fullPath); - - auto entity = Engine::GetCurrentScene()->CreateEntity(FileSystem::GetFileNameFromPath(fullPath)); - ModelComponent& modelComponent = entity.AddComponent(); - modelComponent.ModelPath = fullPath; - modelComponent.ModelResource = modelResource; - entity.GetComponent().SetLocalPosition(dragnDropWorldPos); + //char* file = (char*)payload->Data; + //std::string fullPath = std::string(file, 256); + //fullPath = Nuake::FileSystem::AbsoluteToRelative(fullPath); + // + //auto loader = ModelLoader(); + //auto modelResource = loader.LoadModel(fullPath); + // + //auto entity = Engine::GetCurrentScene()->CreateEntity(FileSystem::GetFileNameFromPath(fullPath)); + //ModelComponent& modelComponent = entity.AddComponent(); + //modelComponent.ModelPath = fullPath; + //modelComponent.ModelResource = modelResource; + //entity.GetComponent().SetLocalPosition(dragnDropWorldPos); } if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("_Map")) diff --git a/Editor/src/Windows/FileSystemUI.cpp b/Editor/src/Windows/FileSystemUI.cpp index eeaba816..fe929339 100644 --- a/Editor/src/Windows/FileSystemUI.cpp +++ b/Editor/src/Windows/FileSystemUI.cpp @@ -677,7 +677,6 @@ namespace Nuake } Ref material = CreateRef(); - material->IsEmbedded = false; auto jsonData = material->Serialize(); FileSystem::BeginWriteFile(finalPath, true); @@ -699,7 +698,6 @@ namespace Nuake } Ref sky = CreateRef(FileSystem::AbsoluteToRelative(finalPath)); - sky->IsEmbedded = false; auto jsonData = sky->Serialize(); FileSystem::BeginWriteFile(finalPath, true); @@ -721,7 +719,6 @@ namespace Nuake } Ref env = CreateRef(FileSystem::AbsoluteToRelative(finalPath)); - env->IsEmbedded = false; auto jsonData = env->Serialize(); FileSystem::BeginWriteFile(finalPath, true); diff --git a/Nuake/Engine.cpp b/Nuake/Engine.cpp index 9472f2af..8423ae00 100644 --- a/Nuake/Engine.cpp +++ b/Nuake/Engine.cpp @@ -26,6 +26,8 @@ #endif #include +#include + namespace Nuake { @@ -68,6 +70,11 @@ namespace Nuake Modules::StartupModules(); InitializeCoreSubsystems(); + + // Init base resolvers + auto& resolvers = ResourceResolverManager::Get(); + resolvers.RegisterResolver(CreateRef()); + resolvers.RegisterResolver(CreateRef()); } void Engine::Tick() diff --git a/Nuake/src/FileSystem/File.h b/Nuake/src/FileSystem/File.h index ad39b59b..03132dd3 100644 --- a/Nuake/src/FileSystem/File.h +++ b/Nuake/src/FileSystem/File.h @@ -16,6 +16,7 @@ namespace Nuake public: std::string GetName() const { return name; } + std::string GetFullName() const { return name + GetExtension(); } std::string GetExtension() const { return type; } std::string GetRelativePath() const { return relativePath; } std::string GetAbsolutePath() const { return absolutePath; } diff --git a/Nuake/src/FileSystem/FileSystem.cpp b/Nuake/src/FileSystem/FileSystem.cpp index 9f7b74d9..67bde920 100644 --- a/Nuake/src/FileSystem/FileSystem.cpp +++ b/Nuake/src/FileSystem/FileSystem.cpp @@ -278,7 +278,7 @@ Ref FileSystem::GetFile(const std::string& inPath) // Find in files if can't find in directories. for (auto& f : currentDirComparator->Files) { - if (f->GetName() == currentDirName) + if (f->GetName() == currentDirName || f->GetFullName() == currentDirName) { return f; } diff --git a/Nuake/src/Rendering/Mesh/Mesh.cpp b/Nuake/src/Rendering/Mesh/Mesh.cpp index dbe1d103..bf5060ea 100644 --- a/Nuake/src/Rendering/Mesh/Mesh.cpp +++ b/Nuake/src/Rendering/Mesh/Mesh.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include "src/Resource/ResourceManager.h" namespace Nuake { @@ -50,12 +52,19 @@ namespace Nuake Ref Mesh::GetMaterial() const { - return m_Material; + Ref material = ResourceManager::GetResource(MaterialResource.ID); + if (!material) + { + assert(false && "material not found"); + } + + return material; } void Mesh::SetMaterial(Ref material) { m_Material = material; + MaterialResource = material->ID; MaterialManager::Get()->RegisterMaterial(material); } @@ -127,6 +136,9 @@ namespace Nuake json Mesh::Serialize() { + + + BEGIN_SERIALIZE(); if (m_Material) @@ -161,7 +173,6 @@ namespace Nuake j["Vertices"][i] = v; } - END_SERIALIZE(); } diff --git a/Nuake/src/Rendering/Mesh/Mesh.h b/Nuake/src/Rendering/Mesh/Mesh.h index 862098b0..22e2c95b 100644 --- a/Nuake/src/Rendering/Mesh/Mesh.h +++ b/Nuake/src/Rendering/Mesh/Mesh.h @@ -4,7 +4,7 @@ #include "src/Resource/Resource.h" #include "src/Resource/Serializable.h" #include "src/Rendering/Vulkan/VkMesh.h" - +#include "src/Resource/RID.h" namespace Nuake { @@ -41,7 +41,9 @@ namespace Nuake return m_Mesh; } + RID MaterialResource; private: + Ref m_Material = nullptr; std::vector m_Indices; std::vector m_Vertices; diff --git a/Nuake/src/Rendering/SceneRenderer.cpp b/Nuake/src/Rendering/SceneRenderer.cpp index 1eb93720..59e5a691 100644 --- a/Nuake/src/Rendering/SceneRenderer.cpp +++ b/Nuake/src/Rendering/SceneRenderer.cpp @@ -233,7 +233,7 @@ namespace Nuake if (!entity.IsValid()) continue; - if (mesh.ModelResource && visibility.Visible) + if (mesh.ModelResource.Get() && visibility.Visible) { transform.PreviousTransform = mProjection * mView * transform.GetGlobalTransform(); } @@ -965,9 +965,9 @@ namespace Nuake { auto [transform, mesh, visibility] = view.get(e); - if (mesh.ModelResource && visibility.Visible) + if (mesh.ModelResource.Get() && visibility.Visible) { - for (auto& m : mesh.ModelResource->GetMeshes()) + for (auto& m : mesh.ModelResource.Get()->GetMeshes()) { Renderer::SubmitMesh(m, transform.GetGlobalTransform(), (uint32_t)e, transform.PreviousTransform); } diff --git a/Nuake/src/Rendering/Textures/Material.h b/Nuake/src/Rendering/Textures/Material.h index 1cbfe7dc..b8ab77bc 100644 --- a/Nuake/src/Rendering/Textures/Material.h +++ b/Nuake/src/Rendering/Textures/Material.h @@ -60,7 +60,7 @@ namespace Nuake }; } public: - UUID AlbedoImage; + UUID AlbedoImage = UUID(0); UUID AOImage; UUID MetalnessImage; UUID RoughnessImage; @@ -99,7 +99,7 @@ namespace Nuake inline void SetUnlit(bool value) { data.u_Unlit = value; } inline bool GetUnlit() { return data.u_Unlit == 1; } - bool HasAlbedo() { return m_Albedo != nullptr; } + bool HasAlbedo() { return (m_Albedo != nullptr || AlbedoImage != UUID(0)); } void SetAlbedo(const std::string path) { m_Albedo = CreateRef(path); } void SetAlbedo(Ref texture) { m_Albedo = texture; } @@ -174,6 +174,7 @@ namespace Nuake bool Deserialize(const json& j) override { + ID = static_cast(j["UUID"]); if (j.contains("Albedo")) { diff --git a/Nuake/src/Rendering/Textures/TextureManager.cpp b/Nuake/src/Rendering/Textures/TextureManager.cpp index ee90c987..1e77b937 100644 --- a/Nuake/src/Rendering/Textures/TextureManager.cpp +++ b/Nuake/src/Rendering/Textures/TextureManager.cpp @@ -58,6 +58,8 @@ namespace Nuake m_Registry2.emplace(Resources_Images_folder_icon_png_path, CreateRef(Resources_Images_folder_icon_png, Resources_Images_folder_icon_png_len)); m_Registry2.emplace(Resources_Images_audio_file_icon_png_path, CreateRef(Resources_Images_audio_file_icon_png, Resources_Images_audio_file_icon_png_len)); m_Registry2.emplace("missing_texture", CreateRef(Resources_Images_missing_texture_png, Resources_Images_missing_texture_png_len)); + + GPUResources::Get().AddTexture(m_Registry2["missing_texture"]); // //unsigned char whitePixel[] = { 255, 255, 255, 255 }; //m_Registry.emplace("Resources/Textures/Default.png", CreateRef(Vector2( 1, 1 ), GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, &whitePixel)); diff --git a/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.cpp b/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.cpp index 473469cb..5aa6c3ab 100644 --- a/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.cpp +++ b/Nuake/src/Rendering/Vulkan/SceneRenderPipeline.cpp @@ -47,13 +47,13 @@ ShadowRenderPipeline::ShadowRenderPipeline() for (auto e : view) { auto [transform, mesh, visibility] = view.get(e); - if (!mesh.ModelResource || !visibility.Visible) + if (!mesh.ModelResource.Get() || !visibility.Visible) { continue; } const int entityId = Entity((entt::entity)e, scene.get()).GetID(); - for (auto& m : mesh.ModelResource->GetMeshes()) + for (auto& m : mesh.ModelResource.Get()->GetMeshes()) { Ref vkMesh = m->GetVkMesh(); cmd.BindDescriptorSet(ctx.renderPass->PipelineLayout, vkMesh->GetDescriptorSet(), 1); @@ -145,14 +145,14 @@ SceneRenderPipeline::SceneRenderPipeline() for (auto e : view) { auto [transform, mesh, visibility] = view.get(e); - if (!mesh.ModelResource || !visibility.Visible) + if (!mesh.ModelResource.Get() || !visibility.Visible) { continue; } gbufferConstant.Index = res.ModelMatrixMapping[Entity((entt::entity)e, scene.get()).GetID()]; - for (auto& m : mesh.ModelResource->GetMeshes()) + for (auto& m : mesh.ModelResource.Get()->GetMeshes()) { Ref vkMesh = m->GetVkMesh(); diff --git a/Nuake/src/Rendering/Vulkan/VulkanSceneRenderer.cpp b/Nuake/src/Rendering/Vulkan/VulkanSceneRenderer.cpp index c95bc156..874244fd 100644 --- a/Nuake/src/Rendering/Vulkan/VulkanSceneRenderer.cpp +++ b/Nuake/src/Rendering/Vulkan/VulkanSceneRenderer.cpp @@ -162,7 +162,10 @@ void VkSceneRenderer::BeginScene(RenderContext inContext) } auto [transform, mesh, visibility] = view.get(e); - if (!mesh.ModelResource || !visibility.Visible) + + // Get() will load the resource if its not loaded already + Ref modelResource = mesh.ModelResource.Get(); + if (!modelResource || !visibility.Visible) { continue; } @@ -172,11 +175,12 @@ void VkSceneRenderer::BeginScene(RenderContext inContext) gpu.ModelMatrixMapping[Entity((entt::entity)e, scene.get()).GetID()] = currentIndex; // Upload mesh material to GPU resources - for (auto& m : mesh.ModelResource->GetMeshes()) + for (auto& m : modelResource->GetMeshes()) { // TODO: Avoid duplicated materials if (Ref material = m->GetMaterial(); material) { + auto missingTextureID = TextureManager::Get()->GetTexture2("missing_texture")->GetID(); MaterialBufferStruct materialBuffer { .HasAlbedo = material->HasAlbedo(), @@ -188,7 +192,7 @@ void VkSceneRenderer::BeginScene(RenderContext inContext) .MetalnessValue = material->data.u_MetalnessValue, .RoughnessValue = material->data.u_RoughnessValue, .AoValue = material->data.u_AOValue, - .AlbedoTextureId = material->HasAlbedo() ? gpu.GetBindlessTextureID(material->AlbedoImage) : 0, + .AlbedoTextureId = material->HasAlbedo() ? gpu.GetBindlessTextureID(material->AlbedoImage) : gpu.GetBindlessTextureID(missingTextureID), .NormalTextureId = material->HasNormal() ? gpu.GetBindlessTextureID(material->NormalImage) : 0, .MetalnessTextureId = material->HasMetalness() ? gpu.GetBindlessTextureID(material->MetalnessImage) : 0, .RoughnessTextureId = material->HasRoughness() ? gpu.GetBindlessTextureID(material->RoughnessImage) : 0, @@ -244,8 +248,6 @@ void VkSceneRenderer::BeginScene(RenderContext inContext) for (int i = 0; i < CSM_AMOUNT; i++) { light.TransformId[i] = gpu.GetBindlessCameraID(lightComp.m_LightViews[i].CameraID); - - light.ShadowMapTextureId[i] = gpu.GetBindlessTextureID(lightComp.m_ShadowMaps[i]->GetID()); } } diff --git a/Nuake/src/Resource/Model.cpp b/Nuake/src/Resource/Model.cpp index b8d3f1f1..ee34cfe5 100644 --- a/Nuake/src/Resource/Model.cpp +++ b/Nuake/src/Resource/Model.cpp @@ -3,6 +3,7 @@ #include "src/Core/Logger.h" #include "src/Rendering/Mesh/Mesh.h" #include "src/Resource/ModelLoader.h" +#include "Serializer/BinarySerializer.h" namespace Nuake { @@ -48,8 +49,11 @@ namespace Nuake else { j["UUID"] = static_cast(ID); + + BinarySerializer serializer; for (uint32_t i = 0; i < std::size(m_Meshes); i++) { + serializer.SerializeMesh(FileSystem::RelativeToAbsolute("mesh" + std::to_string(i) + ".nkmesh"), m_Meshes[i]); j["Meshes"][i] = m_Meshes[i]->Serialize(); } } @@ -60,7 +64,7 @@ namespace Nuake { if (j.contains("Path")) { - this->IsEmbedded = true; + ModelLoader loader; auto otherModel = loader.LoadModel(j["Path"], false); diff --git a/Nuake/src/Resource/Project.cpp b/Nuake/src/Resource/Project.cpp index f8e684b4..fad233ca 100644 --- a/Nuake/src/Resource/Project.cpp +++ b/Nuake/src/Resource/Project.cpp @@ -1,10 +1,13 @@ #include "Project.h" -#include "src/FileSystem/FileSystem.h" + #include "Engine.h" -#include "src/Core/Logger.h" #include "src/Audio/AudioManager.h" -#include "src/Scripting/ScriptingEngineNet.h" +#include "src/Core/Logger.h" #include "src/FileSystem/File.h" +#include "src/FileSystem/FileSystem.h" +#include "src/Resource/ResourceManager.h" +#include "src/Scripting/ScriptingEngineNet.h" + #include #include @@ -12,358 +15,364 @@ #include -namespace Nuake +using namespace Nuake; + +Project::Project(const std::string& Name, const std::string& Description, const std::string& FullPath, const std::string& defaultScenePath) { - Project::Project(const std::string& Name, const std::string& Description, const std::string& FullPath, const std::string& defaultScenePath) - { - this->Name = Name; - this->Description = Description; - this->FullPath = FullPath; - this->TrenchbroomPath = ""; + this->Name = Name; + this->Description = Description; + this->FullPath = FullPath; + this->TrenchbroomPath = ""; - if (defaultScenePath != "") + if (defaultScenePath != "") + { + Ref scene = CreateRef(); + scene->Deserialize(defaultScenePath); + } + + this->EntityDefinitionsFile = CreateRef(); + + SaveAs(FullPath); +} + +Project::Project() +{ + this->Name = ""; + this->Description = ""; + this->FullPath = ""; + this->TrenchbroomPath = ""; + + this->EntityDefinitionsFile = CreateRef(); +} + +void Project::Save() +{ + SaveAs(this->FullPath); +} + +void Project::SaveAs(const std::string& FullPath) +{ + json j = Serialize(); + std::string serialized_string = j.dump(4); + + IsDirty = false; + + // TODO: Use file interface here... + // Write to file. + std::ofstream projectFile; + projectFile.open(FullPath); + projectFile << serialized_string; + projectFile.close(); +} + +bool Project::FileExist() +{ + return std::filesystem::exists(this->FullPath.c_str()); +} + +Ref Project::New(const std::string& Name, const std::string& Description, const std::string& FullPath) +{ + return CreateRef(Name, Description, FullPath); +} + +Ref Project::New() +{ + return CreateRef(); +} + +Ref Project::Load(std::string& path) +{ + std::ifstream i(path); + json j; + i >> j; + + // validation + std::string projectName; + if (!j.contains("ProjectName")) + return nullptr; + + projectName = j["ProjectName"]; + + std::string description; + if (j.contains("Description")) + description = j["Description"]; + + + Ref project = CreateRef(projectName, description, path); + + project->Settings = ProjectSettings(); + + if (j.contains("Settings")) + { + project->Settings.Deserialize(j["Settings"]); + + Engine::SetPhysicsStep(project->Settings.PhysicsStep); + + AudioManager::Get().SetGlobalVolume(project->Settings.GlobalVolume); + } + + return project; +} + +void Project::ExportEntitiesToTrenchbroom() +{ + Ref file = EntityDefinitionsFile; + + file->BrushEntities.clear(); + for (auto& [name, type] : ScriptingEngineNet::Get().GetBrushEntities()) + { + FGDBrushEntity brushEntity = FGDBrushEntity(name); + brushEntity.Script = name; + brushEntity.Description = type.Description; + brushEntity.IsTrigger = type.isTrigger; + for (auto& t : type.exposedVars) { - Ref scene = CreateRef(); - scene->Deserialize(defaultScenePath); - } + ClassProperty classProp; + classProp.name = t.Name; + classProp.type = ClassPropertyType::String; - this->EntityDefinitionsFile = CreateRef(); - - SaveAs(FullPath); - } - - Project::Project() - { - this->Name = ""; - this->Description = ""; - this->FullPath = ""; - this->TrenchbroomPath = ""; - - this->EntityDefinitionsFile = CreateRef(); - } - - void Project::Save() - { - SaveAs(this->FullPath); - } - - void Project::SaveAs(const std::string& FullPath) - { - json j = Serialize(); - std::string serialized_string = j.dump(4); - - IsDirty = false; - - // TODO: Use file interface here... - // Write to file. - std::ofstream projectFile; - projectFile.open(FullPath); - projectFile << serialized_string; - projectFile.close(); - } - - bool Project::FileExist() - { - return std::filesystem::exists(this->FullPath.c_str()); - } - - Ref Project::New(const std::string& Name, const std::string& Description, const std::string& FullPath) - { - return CreateRef(Name, Description, FullPath); - } - - Ref Project::New() - { - return CreateRef(); - } - - Ref Project::Load(std::string& path) - { - std::ifstream i(path); - json j; - i >> j; - - // validation - std::string projectName; - if (!j.contains("ProjectName")) - return nullptr; - - projectName = j["ProjectName"]; - - std::string description; - if (j.contains("Description")) - description = j["Description"]; - - - Ref project = CreateRef(projectName, description, path); - - project->Settings = ProjectSettings(); - - if (j.contains("Settings")) - { - project->Settings.Deserialize(j["Settings"]); - - Engine::SetPhysicsStep(project->Settings.PhysicsStep); - - AudioManager::Get().SetGlobalVolume(project->Settings.GlobalVolume); - } - - return project; - } - - void Project::ExportEntitiesToTrenchbroom() - { - Ref file = EntityDefinitionsFile; - - file->BrushEntities.clear(); - for (auto& [name, type] : ScriptingEngineNet::Get().GetBrushEntities()) - { - FGDBrushEntity brushEntity = FGDBrushEntity(name); - brushEntity.Script = name; - brushEntity.Description = type.Description; - brushEntity.IsTrigger = type.isTrigger; - for (auto& t : type.exposedVars) + if (t.Type == ExposedVarTypes::String && t.Value.has_value()) { - ClassProperty classProp; - classProp.name = t.Name; - classProp.type = ClassPropertyType::String; + classProp.value = std::any_cast(t.Value); + } + else if (t.Type == ExposedVarTypes::Float && t.Value.has_value()) + { + classProp.value = std::any_cast(t.Value); + classProp.type = ClassPropertyType::Float; + } + else if (t.Type == ExposedVarTypes::Int) + { + classProp.value = std::to_string(std::any_cast(t.Value)); + } + else + { + classProp.value = ""; + } - if (t.Type == ExposedVarTypes::String && t.Value.has_value()) + brushEntity.Properties.push_back(classProp); + } + + file->BrushEntities.push_back(brushEntity); + } + + file->PointEntities.clear(); + std::vector bases; + for (auto& [name, type] : ScriptingEngineNet::Get().GetPointEntities()) + { + FGDPointEntity pointEntity = FGDPointEntity(name); + pointEntity.Script = name; + pointEntity.Description = type.Description; + for (auto& t : type.exposedVars) + { + ClassProperty classProp; + classProp.name = t.Name; + classProp.type = ClassPropertyType::String; + if (t.Type == ExposedVarTypes::String) + { + if (t.Value.has_value()) { classProp.value = std::any_cast(t.Value); } - else if (t.Type == ExposedVarTypes::Float && t.Value.has_value()) - { - classProp.value = std::any_cast(t.Value); - classProp.type = ClassPropertyType::Float; - } - else if (t.Type == ExposedVarTypes::Int) + + classProp.type = ClassPropertyType::String; + } + else if (t.Type == ExposedVarTypes::Int) + { + if (t.Value.has_value()) { classProp.value = std::to_string(std::any_cast(t.Value)); } - else - { - classProp.value = ""; - } - brushEntity.Properties.push_back(classProp); + classProp.type = ClassPropertyType::Integer; } - - file->BrushEntities.push_back(brushEntity); - } - - file->PointEntities.clear(); - std::vector bases; - for (auto& [name, type] : ScriptingEngineNet::Get().GetPointEntities()) - { - FGDPointEntity pointEntity = FGDPointEntity(name); - pointEntity.Script = name; - pointEntity.Description = type.Description; - for (auto& t : type.exposedVars) + else if (t.Type == ExposedVarTypes::Float) { - ClassProperty classProp; - classProp.name = t.Name; - classProp.type = ClassPropertyType::String; - if (t.Type == ExposedVarTypes::String) + if (t.Value.has_value()) { - if (t.Value.has_value()) - { - classProp.value = std::any_cast(t.Value); - } - - classProp.type = ClassPropertyType::String; - } - else if (t.Type == ExposedVarTypes::Int) - { - if (t.Value.has_value()) - { - classProp.value = std::to_string(std::any_cast(t.Value)); - } - - classProp.type = ClassPropertyType::Integer; - } - else if (t.Type == ExposedVarTypes::Float) - { - if (t.Value.has_value()) - { - classProp.value = std::to_string(std::any_cast(t.Value)); - } - - classProp.type = ClassPropertyType::Float; - } - else if (t.Type == ExposedVarTypes::Bool) - { - if (t.Value.has_value()) - { - classProp.value = std::to_string(std::any_cast(t.Value)); - } - - classProp.type = ClassPropertyType::Float; - } - else - { - classProp.value = ""; + classProp.value = std::to_string(std::any_cast(t.Value)); } - pointEntity.Properties.push_back(classProp); + classProp.type = ClassPropertyType::Float; } - - file->PointEntities.push_back(pointEntity); - } - - for (auto& p : FileSystem::GetAllFiles(FileType::Prefab)) - { - const std::string& fileContent = FileSystem::ReadFile(p->GetRelativePath()); - json fileJson = json::parse(fileContent); - - std::string name = p->GetName(); - if (fileJson.contains("DisplayName")) + else if (t.Type == ExposedVarTypes::Bool) { - name = fileJson["DisplayName"]; + if (t.Value.has_value()) + { + classProp.value = std::to_string(std::any_cast(t.Value)); + } + + classProp.type = ClassPropertyType::Float; + } + else + { + classProp.value = ""; } - FGDPointEntity pointEntity = FGDPointEntity(name); - pointEntity.Description = "A Prefab entity"; - pointEntity.Prefab = p->GetRelativePath(); - file->PointEntities.push_back(pointEntity); + pointEntity.Properties.push_back(classProp); } - for (auto& b : bases) - { - FGDBaseEntity baseEntity; - baseEntity.Name = b; - file->BaseEntities.push_back(baseEntity); - } - - file->Export(); + file->PointEntities.push_back(pointEntity); } - json Project::Serialize() + for (auto& p : FileSystem::GetAllFiles(FileType::Prefab)) { - BEGIN_SERIALIZE(); - SERIALIZE_VAL(Name); - SERIALIZE_VAL(Description); + const std::string& fileContent = FileSystem::ReadFile(p->GetRelativePath()); + json fileJson = json::parse(fileContent); - if(DefaultScene) - SERIALIZE_VAL_LBL("DefaultScene", DefaultScene->Path); + std::string name = p->GetName(); + if (fileJson.contains("DisplayName")) + { + name = fileJson["DisplayName"]; + } - if(EntityDefinitionsFile) - SERIALIZE_VAL_LBL("EntityDefinition", EntityDefinitionsFile->Path); - SERIALIZE_VAL(TrenchbroomPath); - - // Project Settings - j["Settings"] = Settings.Serialize(); - - END_SERIALIZE(); + FGDPointEntity pointEntity = FGDPointEntity(name); + pointEntity.Description = "A Prefab entity"; + pointEntity.Prefab = p->GetRelativePath(); + file->PointEntities.push_back(pointEntity); } - bool Project::Deserialize(const json& j) + for (auto& b : bases) { - Logger::Log("Starting deserializing project", "window", VERBOSE); - if (!j.contains("Name") || !j.contains("Description")) - return false; - - Name = j["Name"]; - Description = j["Description"]; - - if (j.contains("EntityDefinition") && j["EntityDefinition"] != "") - { - std::string path = j["EntityDefinition"]; - EntityDefinitionsFile = CreateRef(path); - std::string content = FileSystem::ReadFile(path, false); - EntityDefinitionsFile->Deserialize(nlohmann::json::parse(content)); - } - - if (j.contains("TrenchbroomPath")) - { - this->TrenchbroomPath = j["TrenchbroomPath"]; - } - - if (j.contains("Settings")) - { - Settings = ProjectSettings(); - Settings.Deserialize(j["Settings"]); - Engine::GetCurrentWindow()->SetVSync(Settings.VSync); - - Engine::SetPhysicsStep(Settings.PhysicsStep); - - AudioManager::Get().SetGlobalVolume(Settings.GlobalVolume); - } - - DefaultScene = Scene::New(); - - // Load default scene, a project can have no default scene set. - if (!j.contains("DefaultScene") ) - return true; - - std::string scenePath = j["DefaultScene"]; - if (scenePath == "") // Not set correctly. - return true; - - if (!FileSystem::FileExists(scenePath)) - return true; - - Logger::Log("Starting deserializing scene", "window", VERBOSE); - std::string sceneContent = FileSystem::ReadFile(scenePath, false); - if (!DefaultScene->Deserialize(nlohmann::json::parse(sceneContent))) - { - Logger::Log("Error loading scene: " + scenePath, "project", CRITICAL); - } - - DefaultScene->Path = scenePath; - Logger::Log("Loaded scene: " + scenePath); - - return true; // Success + FGDBaseEntity baseEntity; + baseEntity.Name = b; + file->BaseEntities.push_back(baseEntity); } - json ProjectSettings::Serialize() - { - BEGIN_SERIALIZE(); - - SERIALIZE_VAL(VSync); - SERIALIZE_VAL(ShowGizmos); - SERIALIZE_VAL(ShowAxis); - SERIALIZE_VAL(ResolutionScale); - SERIALIZE_VAL(GizmoSize); - SERIALIZE_VAL(OutlineRadius); - SERIALIZE_VEC4(PrimaryColor); - SERIALIZE_VAL(SmoothCamera); - SERIALIZE_VAL(SmoothCameraSpeed); - SERIALIZE_VAL(PhysicsStep); - SERIALIZE_VAL(MaxPhysicsSubStep); - SERIALIZE_VAL(MaxPhysicsBodies); - SERIALIZE_VAL(MaxPhysicsBodyPair); - SERIALIZE_VAL(MaxPhysicsContactConstraints); - SERIALIZE_VAL(GlobalVolume); - SERIALIZE_VAL(MaxActiveVoiceCount); - END_SERIALIZE(); - } - - bool ProjectSettings::Deserialize(const json& j) - { - DESERIALIZE_VAL(VSync); - DESERIALIZE_VAL(ShowGizmos); - DESERIALIZE_VAL(ShowAxis); - DESERIALIZE_VAL(ResolutionScale); - DESERIALIZE_VAL(OutlineRadius); - DESERIALIZE_VAL(GizmoSize); - DESERIALIZE_VAL(SmoothCamera); - DESERIALIZE_VAL(SmoothCameraSpeed); - DESERIALIZE_VAL(PhysicsStep); - DESERIALIZE_VAL(MaxPhysicsSubStep); - DESERIALIZE_VAL(MaxPhysicsBodies); - DESERIALIZE_VAL(MaxPhysicsBodyPair); - DESERIALIZE_VAL(MaxPhysicsContactConstraints); - - DESERIALIZE_VAL(GlobalVolume); - DESERIALIZE_VAL(MaxActiveVoiceCount); - - if (j.contains("PrimaryColor")) - { - DESERIALIZE_VEC4(j["PrimaryColor"], PrimaryColor); - } - - return true; - } + file->Export(); } +json Project::Serialize() +{ + BEGIN_SERIALIZE(); + SERIALIZE_VAL(Name); + SERIALIZE_VAL(Description); + + if(DefaultScene) + SERIALIZE_VAL_LBL("DefaultScene", DefaultScene->Path); + + if(EntityDefinitionsFile) + SERIALIZE_VAL_LBL("EntityDefinition", EntityDefinitionsFile->Path); + SERIALIZE_VAL(TrenchbroomPath); + + // Project Settings + j["Settings"] = Settings.Serialize(); + + ResourceManager::Manifest.Serialize("res.manifest"); + END_SERIALIZE(); +} + +bool Project::Deserialize(const json& j) +{ + Logger::Log("Starting deserializing project", "window", VERBOSE); + if (!j.contains("Name") || !j.contains("Description")) + return false; + + if (FileSystem::FileExists("res.manifest")) + { + ResourceManager::Manifest.Deserialize("res.manifest"); + } + + Name = j["Name"]; + Description = j["Description"]; + + if (j.contains("EntityDefinition") && j["EntityDefinition"] != "") + { + std::string path = j["EntityDefinition"]; + EntityDefinitionsFile = CreateRef(path); + std::string content = FileSystem::ReadFile(path, false); + EntityDefinitionsFile->Deserialize(nlohmann::json::parse(content)); + } + + if (j.contains("TrenchbroomPath")) + { + this->TrenchbroomPath = j["TrenchbroomPath"]; + } + + if (j.contains("Settings")) + { + Settings = ProjectSettings(); + Settings.Deserialize(j["Settings"]); + Engine::GetCurrentWindow()->SetVSync(Settings.VSync); + + Engine::SetPhysicsStep(Settings.PhysicsStep); + + AudioManager::Get().SetGlobalVolume(Settings.GlobalVolume); + } + + DefaultScene = Scene::New(); + + // Load default scene, a project can have no default scene set. + if (!j.contains("DefaultScene") ) + return true; + + std::string scenePath = j["DefaultScene"]; + if (scenePath == "") // Not set correctly. + return true; + + if (!FileSystem::FileExists(scenePath)) + return true; + + Logger::Log("Starting deserializing scene", "window", VERBOSE); + std::string sceneContent = FileSystem::ReadFile(scenePath, false); + if (!DefaultScene->Deserialize(nlohmann::json::parse(sceneContent))) + { + Logger::Log("Error loading scene: " + scenePath, "project", CRITICAL); + } + + DefaultScene->Path = scenePath; + Logger::Log("Loaded scene: " + scenePath); + + return true; // Success +} + +json ProjectSettings::Serialize() +{ + BEGIN_SERIALIZE(); + + SERIALIZE_VAL(VSync); + SERIALIZE_VAL(ShowGizmos); + SERIALIZE_VAL(ShowAxis); + SERIALIZE_VAL(ResolutionScale); + SERIALIZE_VAL(GizmoSize); + SERIALIZE_VAL(OutlineRadius); + SERIALIZE_VEC4(PrimaryColor); + SERIALIZE_VAL(SmoothCamera); + SERIALIZE_VAL(SmoothCameraSpeed); + SERIALIZE_VAL(PhysicsStep); + SERIALIZE_VAL(MaxPhysicsSubStep); + SERIALIZE_VAL(MaxPhysicsBodies); + SERIALIZE_VAL(MaxPhysicsBodyPair); + SERIALIZE_VAL(MaxPhysicsContactConstraints); + SERIALIZE_VAL(GlobalVolume); + SERIALIZE_VAL(MaxActiveVoiceCount); + END_SERIALIZE(); +} + +bool ProjectSettings::Deserialize(const json& j) +{ + DESERIALIZE_VAL(VSync); + DESERIALIZE_VAL(ShowGizmos); + DESERIALIZE_VAL(ShowAxis); + DESERIALIZE_VAL(ResolutionScale); + DESERIALIZE_VAL(OutlineRadius); + DESERIALIZE_VAL(GizmoSize); + DESERIALIZE_VAL(SmoothCamera); + DESERIALIZE_VAL(SmoothCameraSpeed); + DESERIALIZE_VAL(PhysicsStep); + DESERIALIZE_VAL(MaxPhysicsSubStep); + DESERIALIZE_VAL(MaxPhysicsBodies); + DESERIALIZE_VAL(MaxPhysicsBodyPair); + DESERIALIZE_VAL(MaxPhysicsContactConstraints); + + DESERIALIZE_VAL(GlobalVolume); + DESERIALIZE_VAL(MaxActiveVoiceCount); + + if (j.contains("PrimaryColor")) + { + DESERIALIZE_VEC4(j["PrimaryColor"], PrimaryColor); + } + + return true; +} + + diff --git a/Nuake/src/Resource/RID.cpp b/Nuake/src/Resource/RID.cpp new file mode 100644 index 00000000..8efe5917 --- /dev/null +++ b/Nuake/src/Resource/RID.cpp @@ -0,0 +1,4 @@ +#include "RID.h" + + +using namespace Nuake; diff --git a/Nuake/src/Resource/RID.h b/Nuake/src/Resource/RID.h new file mode 100644 index 00000000..8094c4df --- /dev/null +++ b/Nuake/src/Resource/RID.h @@ -0,0 +1,40 @@ +#pragma once +#include "src/Core/Core.h" +#include "src/Resource/UUID.h" + +#include "src/Resource/ResourceManager.h" + +namespace Nuake +{ + class Resource; + class RID + { + public: + UUID ID; + Ref Data; + + RID() : ID(0), Data(nullptr) {} + RID(UUID id) : ID(id) {} + + public: + template + Ref Get() + { + if (!Data) + { + Load(); + } + + return std::static_pointer_cast(Data); + } + + template + void Load() + { + if (!Data) + { + Data = ResourceManager::GetResource(ID); + } + } + }; +} \ No newline at end of file diff --git a/Nuake/src/Resource/Resolvers/IResourceResolver.h b/Nuake/src/Resource/Resolvers/IResourceResolver.h new file mode 100644 index 00000000..c1199fee --- /dev/null +++ b/Nuake/src/Resource/Resolvers/IResourceResolver.h @@ -0,0 +1,27 @@ +#pragma once + +#include "src/Core/Core.h" +#include "src/FileSystem/File.h" +#include "src/Resource/UUID.h" +#include "src/Resource/Resource.h" + +#include +#include + +namespace Nuake +{ + class IResourceResolver + { + private: + std::string Extension; + + public: + IResourceResolver(const std::string& extension) : Extension(extension) {} + virtual ~IResourceResolver() = default; + + // Converts a file to a resource UUID + virtual Ref Resolve(const Ref& file) = 0; + + std::string GetExtension() const { return Extension; } + }; +} \ No newline at end of file diff --git a/Nuake/src/Resource/Resolvers/MaterialResolver.h b/Nuake/src/Resource/Resolvers/MaterialResolver.h new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/Nuake/src/Resource/Resolvers/MaterialResolver.h @@ -0,0 +1 @@ +#pragma once diff --git a/Nuake/src/Resource/Resolvers/MeshResolver.cpp b/Nuake/src/Resource/Resolvers/MeshResolver.cpp new file mode 100644 index 00000000..27acaf4b --- /dev/null +++ b/Nuake/src/Resource/Resolvers/MeshResolver.cpp @@ -0,0 +1,28 @@ +#include "MeshResolver.h" + +#include "src/Resource/Model.h" +#include "src/Rendering/Textures/Material.h" + +#include "src/Resource/ResourceManager.h" +#include "src/Resource/Serializer/BinarySerializer.h" + +using namespace Nuake; + +Ref MeshResolver::Resolve(const Ref& file) +{ + BinarySerializer deserializer = BinarySerializer(); + return deserializer.DeserializeModel(file->GetRelativePath()); +} + +Ref MaterialResolver::Resolve(const Ref& file) +{ + // Read json content + std::string fileContent = FileSystem::ReadFile(file->GetRelativePath()); + json jsonData = json::parse(fileContent); + + // Create material from json data + Ref material = CreateRef(); + material->Deserialize(jsonData); + + return material; +} diff --git a/Nuake/src/Resource/Resolvers/MeshResolver.h b/Nuake/src/Resource/Resolvers/MeshResolver.h new file mode 100644 index 00000000..65de7a81 --- /dev/null +++ b/Nuake/src/Resource/Resolvers/MeshResolver.h @@ -0,0 +1,21 @@ +#pragma once +#include "IResourceResolver.h" + +#include "src/Resource/Resource.h" + +namespace Nuake +{ + class MeshResolver : public IResourceResolver + { + public: + MeshResolver() : IResourceResolver(".nkmesh") {} + Ref Resolve(const Ref& file) override; + }; + + class MaterialResolver : public IResourceResolver + { + public: + MaterialResolver() : IResourceResolver(".material") {} + Ref Resolve(const Ref& file) override; + }; +} \ No newline at end of file diff --git a/Nuake/src/Resource/Resolvers/ResolverManager.h b/Nuake/src/Resource/Resolvers/ResolverManager.h new file mode 100644 index 00000000..f10811c5 --- /dev/null +++ b/Nuake/src/Resource/Resolvers/ResolverManager.h @@ -0,0 +1,42 @@ +#pragma once +#include "IResourceResolver.h" + +#include "src/Core/Core.h" +#include "src/Core/Logger.h" +#include "src/FileSystem/File.h" + +#include +#include + +namespace Nuake +{ + class ResourceResolverManager + { + private: + std::map> Resolvers; + + public: + static ResourceResolverManager& Get() + { + static ResourceResolverManager instance; + return instance; + } + + void RegisterResolver(Ref resolver) + { + Resolvers[resolver->GetExtension()] = resolver; + } + + Ref Resolve(const Ref file) + { + std::string extension = file->GetExtension(); + if (Resolvers.find(extension) != Resolvers.end()) + { + return Resolvers[extension]->Resolve(file); + } + + Logger::Log("Failed to find resolver for file type: " + extension, "resolver", CRITICAL); + return nullptr; + } + }; +} diff --git a/Nuake/src/Resource/Resource.h b/Nuake/src/Resource/Resource.h index e78f19e6..f56dbfd4 100644 --- a/Nuake/src/Resource/Resource.h +++ b/Nuake/src/Resource/Resource.h @@ -1,6 +1,5 @@ #pragma once #include "src/Resource/UUID.h" - #include namespace Nuake @@ -9,12 +8,6 @@ namespace Nuake { public: UUID ID; - - bool IsEmbedded = false; - std::string Path; // Only if embedded - - void MakeExternal(); - void Duplicate(); - void MakeEmbedded(); + std::string Path; }; } diff --git a/Nuake/src/Resource/ResourceLoader.cpp b/Nuake/src/Resource/ResourceLoader.cpp index 78661e67..a93555bb 100644 --- a/Nuake/src/Resource/ResourceLoader.cpp +++ b/Nuake/src/Resource/ResourceLoader.cpp @@ -41,18 +41,7 @@ Ref ResourceLoader::LoadMaterial(const std::string& path) UUID uuid = ReadUUID(j); // Check if resource is already loaded. - if (ResourceManager::IsResourceLoaded(uuid)) - { - return ResourceManager::GetResource(uuid); - } - - Ref material = CreateRef(); - material->ID = uuid; - material->Path = path; - material->Deserialize(j); - ResourceManager::RegisterResource(material); - - return material; + return ResourceManager::GetResource(uuid); } Ref ResourceLoader::LoadModel(const std::string& path) diff --git a/Nuake/src/Resource/ResourceManager.cpp b/Nuake/src/Resource/ResourceManager.cpp index 0d84f60f..642e5ef4 100644 --- a/Nuake/src/Resource/ResourceManager.cpp +++ b/Nuake/src/Resource/ResourceManager.cpp @@ -3,4 +3,5 @@ namespace Nuake { std::map> ResourceManager::m_Resources; + ResourceManifest ResourceManager::Manifest; } diff --git a/Nuake/src/Resource/ResourceManager.h b/Nuake/src/Resource/ResourceManager.h index 4fc8ddc4..1575df1a 100644 --- a/Nuake/src/Resource/ResourceManager.h +++ b/Nuake/src/Resource/ResourceManager.h @@ -1,10 +1,19 @@ #pragma once #include "src/Core/Core.h" + +#include "src/FileSystem/FileSystem.h" +#include "src/FileSystem/File.h" + #include "src/Resource/Resource.h" +#include "src/Resource/ResourceManifest.h" +#include "src/Resource/Serializer/BinarySerializer.h" #include "src/Resource/UUID.h" +#include "src/Resource/Resolvers/ResolverManager.h" + #include + namespace Nuake { class ResourceManager @@ -13,6 +22,7 @@ namespace Nuake static std::map> m_Resources; public: + static ResourceManifest Manifest; static Ref GetResource(const UUID& uuid); static void RegisterResource(Ref resource) @@ -25,10 +35,62 @@ namespace Nuake return m_Resources.find(uuid) != m_Resources.end(); } + static Ref RegisterUnregisteredRessource(Ref file) + { + Ref resolvedResource = ResourceResolverManager::Get().Resolve(file); + RegisterResource(resolvedResource); + Manifest.RegisterResource(resolvedResource->ID, file->GetRelativePath()); + return resolvedResource; + } + template static Ref GetResource(const UUID& uuid) { - return std::static_pointer_cast(m_Resources[uuid]); + Ref resource; + if (m_Resources.find(uuid) == m_Resources.end()) + { + // We need to load from disk + auto resourcePath = Manifest.GetResourcePath(uuid); + if (resourcePath.empty()) + { + //assert(false && "Resource not found in manifest"); + return nullptr; + } + + if (!FileSystem::FileExists(resourcePath)) + { + return nullptr; + } + + Ref file = FileSystem::GetFile(resourcePath); + if (file) + { + resource = std::static_pointer_cast(RegisterUnregisteredRessource(file)); + } + } + else + { + resource = std::static_pointer_cast(m_Resources[uuid]); + } + + return resource; + } + + template + static Ref GetResourceFromFile(Ref file) + { + if (UUID id = Manifest.GetResourceUUID(file->GetRelativePath()); id != 0) + { + if (IsResourceLoaded(id)) + { + return std::static_pointer_cast(m_Resources[id]); + } + + // Resource is in manifest, but not loaded? Corruption? + } + + auto resource = RegisterUnregisteredRessource(file); + return std::static_pointer_cast(resource); } }; } \ No newline at end of file diff --git a/Nuake/src/Resource/ResourceManifest.h b/Nuake/src/Resource/ResourceManifest.h new file mode 100644 index 00000000..89074fec --- /dev/null +++ b/Nuake/src/Resource/ResourceManifest.h @@ -0,0 +1,78 @@ +#pragma once + +#include "src/Resource/UUID.h" +#include "src/Resource/Serializable.h" +#include "src/FileSystem/FileSystem.h" + +#include +#include + +namespace Nuake +{ + // This class holds the mapping between resource UUIDs and their paths. + // Ideally, this is the first steps for a VFS for shipped games + class ResourceManifest + { + private: + std::map PathToMap; + std::map MapToPath; + + public: + void RegisterResource(UUID uuid, const std::string& path) + { + // New resource is overwritting so we need to clear UUID -> Path map + if (PathToMap.find(path) != PathToMap.end()) + { + MapToPath.erase(PathToMap[path]); + } + + PathToMap[path] = uuid; + MapToPath[uuid] = path; + } + + std::string GetResourcePath(UUID uuid) + { + if (MapToPath.find(uuid) != MapToPath.end()) + { + return MapToPath[uuid]; + } + + return ""; + } + + UUID GetResourceUUID(const std::string& path) + { + if (PathToMap.find(path) != PathToMap.end()) + { + return PathToMap[path]; + } + + return UUID(0); + } + + void Serialize(const std::string& path) + { + json j; + for (auto& [path, uuid] : PathToMap) + { + j[path] = static_cast(uuid); + } + + FileSystem::BeginWriteFile(path); + FileSystem::WriteLine(j.dump(4)); + FileSystem::EndWriteFile(); + } + + void Deserialize(const std::string& path) + { + auto content = FileSystem::ReadFile(path); + json j = json::parse(content); + for (auto& [path, uuid] : j.items()) + { + uint64_t id = uuid; + PathToMap[path] = UUID(id); + MapToPath[UUID(id)] = path; + } + } + }; +} \ No newline at end of file diff --git a/Nuake/src/Resource/Serializable.h b/Nuake/src/Resource/Serializable.h index 69eb853e..2f767dce 100644 --- a/Nuake/src/Resource/Serializable.h +++ b/Nuake/src/Resource/Serializable.h @@ -1,11 +1,15 @@ #pragma once #include #include + +#include "src/Core/Core.h" + using json = nlohmann::json; #define BEGIN_SERIALIZE() json j; #define SERIALIZE_VAL_LBL(lbl, v) j[lbl] = v; #define SERIALIZE_VAL(v) j[#v] = this->v; +#define SERIALIZE_RID(lbl, v) j[#lbl] = static_cast(v); #define SERIALIZE_RES_FILE(v) \ bool validFile = this->v.file != nullptr && this->v.file->Exist(); \ j["validFile"#v] = validFile; \ @@ -90,9 +94,35 @@ p = j[#p]; \ if(HasComponent()) \ GetComponent().PostDeserialize(*m_Scene); -class ISerializable +namespace Nuake { -public: - virtual json Serialize() = 0; - virtual bool Deserialize(const json& j) = 0; -}; + class Mesh; + class Model; + class Material; + + class ISerializer + { + public: + virtual Ref DeserializeMaterial(const std::string& path) = 0; + virtual bool SerializeMaterial(const std::string& path, Ref material) = 0; + + virtual bool SerializeModel(const std::string& path, Ref model) = 0; + virtual Ref DeserializeModel(const std::string& path) = 0; + + virtual Ref DeserializeMesh(const std::string& path) = 0; + virtual bool SerializeMesh(const std::string& path, Ref mesh) = 0; + + private: + + }; + + class ISerializable + { + public: + virtual json Serialize() = 0; + virtual bool Deserialize(const json& j) = 0; + + virtual void AcceptSerialize(ISerializer& serializer) {}; + }; +} + diff --git a/Nuake/src/Resource/Serializer/BinarySerializer.cpp b/Nuake/src/Resource/Serializer/BinarySerializer.cpp new file mode 100644 index 00000000..a895fb55 --- /dev/null +++ b/Nuake/src/Resource/Serializer/BinarySerializer.cpp @@ -0,0 +1,229 @@ +#include "BinarySerializer.h" + +#include "src/Resource/Resource.h" +#include "src/Resource/Model.h" +#include "src/Rendering/Textures/Material.h" + +#include +#include + +using namespace Nuake; + +struct BinaryMesh +{ + uint64_t RID; + uint64_t MaterialRID; + uint32_t VertexCount; + std::vector Vertices; + uint32_t IndexCount; + std::vector Indices; +}; + +struct BinaryModel +{ + uint64_t RID; + uint32_t MeshCount; + std::vector Meshes; +}; + +class BinaryReader +{ +private: + std::ifstream File; + +public: + BinaryReader(const std::string& path) + { + File = std::ifstream(path, std::ios::binary); + } + + ~BinaryReader() + { + File.close(); + } + +public: + bool IsOpen() const { return File.is_open(); } + + template + void Read(T* data, uint32_t count = 1) + { + File.read(reinterpret_cast(data), count * sizeof(T)); + } +}; + +class BinaryWriter +{ +private: + std::ofstream File; + +public: + BinaryWriter(const std::string& path) + { + File = std::ofstream(path, std::ios::binary); + } + + ~BinaryWriter() + { + File.close(); + } + +public: + bool IsOpen() const { return File.is_open(); } + template + void Write(T* data, uint32_t count = 1) + { + File.write(reinterpret_cast(data), count * sizeof(T)); + } +}; + +Ref BinarySerializer::DeserializeMaterial(const std::string& path) +{ + BinaryReader reader = BinaryReader(path); + if (!reader.IsOpen()) + { + return nullptr; + } + + return nullptr; +} + +bool BinarySerializer::SerializeMaterial(const std::string& path, Ref material) +{ + BinaryWriter writer = BinaryWriter(path); + return true; +} + +Ref BinarySerializer::DeserializeModel(const std::string& path) +{ + BinaryReader reader = BinaryReader(FileSystem::RelativeToAbsolute(path)); + if (!reader.IsOpen()) + { + return nullptr; + } + + BinaryModel binaryModel{ }; + reader.Read(&binaryModel.RID); + reader.Read(&binaryModel.MeshCount); + + for (int i = 0; i < binaryModel.MeshCount; i++) + { + BinaryMesh binaryMesh{}; + reader.Read(&binaryMesh.MaterialRID); + reader.Read(&binaryMesh.VertexCount); + binaryMesh.Vertices.resize(binaryMesh.VertexCount); + reader.Read(binaryMesh.Vertices.data(), binaryMesh.VertexCount); + + reader.Read(&binaryMesh.IndexCount); + binaryMesh.Indices.resize(binaryMesh.IndexCount); + reader.Read(binaryMesh.Indices.data(), binaryMesh.IndexCount); + + binaryModel.Meshes.push_back(std::move(binaryMesh)); + } + + Ref model = CreateRef(); + model->ID = binaryModel.RID; + for (auto& binaryMesh : binaryModel.Meshes) + { + Ref mesh = CreateRef(); + mesh->AddSurface(binaryMesh.Vertices, binaryMesh.Indices); + mesh->MaterialResource = RID(binaryMesh.MaterialRID); + model->AddMesh(mesh); + } + + return model; +} + +bool BinarySerializer::SerializeModel(const std::string& path, Ref model) +{ + const uint32_t meshCount = static_cast(model->GetMeshes().size()); + BinaryModel binaryModel + { + .RID = model->ID, + .MeshCount = meshCount + }; + + binaryModel.Meshes.reserve(meshCount); + + for (auto& mesh : model->GetMeshes()) + { + BinaryMesh binaryMesh + { + .MaterialRID = static_cast(mesh->MaterialResource.ID), + .VertexCount = static_cast(mesh->GetVertices().size()), + .Vertices = mesh->GetVertices(), + .IndexCount = static_cast(mesh->GetIndices().size()), + .Indices = mesh->GetIndices() + }; + + binaryModel.Meshes.push_back(binaryMesh); + } + + // Write data to file + { + BinaryWriter writer(path); + if (!writer.IsOpen()) + { + return false; + } + + writer.Write(&binaryModel.RID); + writer.Write(&binaryModel.MeshCount); + for (int i = 0; i < binaryModel.MeshCount; i++) + { + BinaryMesh& binaryMesh = binaryModel.Meshes[i]; + writer.Write(&binaryMesh.MaterialRID); + writer.Write(&binaryMesh.VertexCount); + writer.Write(binaryMesh.Vertices.data(), binaryMesh.VertexCount); + writer.Write(&binaryMesh.IndexCount); + writer.Write(binaryMesh.Indices.data(), binaryMesh.IndexCount); + } + } + + return true; +} + +Ref BinarySerializer::DeserializeMesh(const std::string& path) +{ + std::ifstream file(path, std::ios::binary); + if (!file.is_open()) + { + return nullptr; + } + + BinaryMesh binaryMesh; + file.read((char*)&binaryMesh.VertexCount, sizeof(uint32_t)); + binaryMesh.Vertices.resize(binaryMesh.VertexCount); + file.read((char*)binaryMesh.Vertices.data(), binaryMesh.VertexCount * sizeof(Vertex)); + file.read((char*)&binaryMesh.IndexCount, sizeof(uint32_t)); + binaryMesh.Indices.resize(binaryMesh.IndexCount); + file.read((char*)binaryMesh.Indices.data(), binaryMesh.IndexCount * sizeof(uint32_t)); + file.close(); + + Ref mesh = CreateRef(); + mesh->AddSurface(binaryMesh.Vertices, binaryMesh.Indices); + return mesh; +} + +bool BinarySerializer::SerializeMesh(const std::string& path, Ref mesh) +{ + BinaryMesh binaryMesh; + binaryMesh.VertexCount = mesh->GetVertices().size(); + binaryMesh.Vertices = mesh->GetVertices(); + binaryMesh.IndexCount = mesh->GetIndices().size(); + binaryMesh.Indices = mesh->GetIndices(); + + std::ofstream file(path, std::ios::binary); + if (!file.is_open()) + { + return false; + } + + file.write((char*)&binaryMesh.VertexCount, sizeof(uint32_t)); + file.write((char*)binaryMesh.Vertices.data(), binaryMesh.VertexCount * sizeof(Vertex)); + file.write((char*)&binaryMesh.IndexCount, sizeof(uint32_t)); + file.write((char*)binaryMesh.Indices.data(), binaryMesh.IndexCount * sizeof(uint32_t)); + file.close(); + + return true; +} \ No newline at end of file diff --git a/Nuake/src/Resource/Serializer/BinarySerializer.h b/Nuake/src/Resource/Serializer/BinarySerializer.h new file mode 100644 index 00000000..3ad665ff --- /dev/null +++ b/Nuake/src/Resource/Serializer/BinarySerializer.h @@ -0,0 +1,27 @@ +#pragma once +#include "src/Core/Core.h" +#include "src/Resource/Serializable.h" + +namespace Nuake +{ + class Mesh; + class Model; + class Material; + + class BinarySerializer : public ISerializer + { + public: + BinarySerializer() = default; + ~BinarySerializer() = default; + + public: + Ref DeserializeMaterial(const std::string& path) override; + bool SerializeMaterial(const std::string& path, Ref material) override; + + Ref DeserializeModel(const std::string& path) override; + bool SerializeModel(const std::string& path, Ref model) override; + + Ref DeserializeMesh(const std::string& path) override; + bool SerializeMesh(const std::string& path, Ref mesh) override; + }; +} \ No newline at end of file diff --git a/Nuake/src/Resource/SkinnedModel.cpp b/Nuake/src/Resource/SkinnedModel.cpp index 9c3578fb..e43970b2 100644 --- a/Nuake/src/Resource/SkinnedModel.cpp +++ b/Nuake/src/Resource/SkinnedModel.cpp @@ -122,7 +122,6 @@ namespace Nuake { if (j.contains("Path")) { - this->IsEmbedded = true; ModelLoader loader; auto otherModel = loader.LoadSkinnedModel(j["Path"], false); diff --git a/Nuake/src/Scene/Components/ModelComponent.cpp b/Nuake/src/Scene/Components/ModelComponent.cpp index bac8967e..2eaa8335 100644 --- a/Nuake/src/Scene/Components/ModelComponent.cpp +++ b/Nuake/src/Scene/Components/ModelComponent.cpp @@ -16,7 +16,7 @@ namespace Nuake { void ModelComponent::LoadModel() { auto loader = ModelLoader(); - this->ModelResource = loader.LoadModel(ModelPath); + //this->ModelResource = loader.LoadModel(ModelPath); } } diff --git a/Nuake/src/Scene/Components/ModelComponent.h b/Nuake/src/Scene/Components/ModelComponent.h index ac27cfce..7070c9bd 100644 --- a/Nuake/src/Scene/Components/ModelComponent.h +++ b/Nuake/src/Scene/Components/ModelComponent.h @@ -1,7 +1,7 @@ #pragma once #include "Component.h" - +#include "src/Resource/RID.h" #include "src/Core/String.h" #include "src/Resource/Model.h" #include "src/Resource/ResourceLoader.h" @@ -9,6 +9,7 @@ #include #include +#include namespace Nuake @@ -17,7 +18,8 @@ namespace Nuake { NUAKECOMPONENT(ModelComponent, "Model") - Ref ModelResource; + RID ModelResource; + //Ref ModelResource; std::string ModelPath; ModelComponent(); @@ -30,31 +32,40 @@ namespace Nuake { BEGIN_SERIALIZE(); SERIALIZE_VAL(ModelPath); - if (ModelResource) - { - SERIALIZE_OBJECT(ModelResource); - } + SERIALIZE_RID(ModelResource, ModelResource.ID); + //if (ModelResource) + //{ + // BinarySerializer serializer; + // serializer.SerializeModel(FileSystem::RelativeToAbsolute("model.nkmesh"), ModelResource); + // SERIALIZE_OBJECT(ModelResource); + //} END_SERIALIZE(); } bool Deserialize(const json& j) { ModelPath = j["ModelPath"]; - ModelResource = CreateRef(); - if (ModelPath.empty() || !String::EndsWith(ModelPath, ".mesh")) + if (j.contains("ModelResource")) { - if (j.contains("ModelResource")) - { - auto& res = j["ModelResource"]; - ModelResource->Deserialize(res); - } - } - else - { - ModelResource = ResourceLoader::LoadModel(ModelPath); + ModelResource.ID = UUID(j["ModelResource"]); } + //ModelResource = CreateRef(); + // + //if (ModelPath.empty() || !String::EndsWith(ModelPath, ".mesh")) + //{ + // if (j.contains("ModelResource")) + // { + // auto& res = j["ModelResource"]; + // ModelResource->Deserialize(res); + // } + //} + //else + //{ + // ModelResource = ResourceLoader::LoadModel(ModelPath); + //} + return true; } }; diff --git a/Nuake/src/Scene/Components/QuakeMap.h b/Nuake/src/Scene/Components/QuakeMap.h index fd3bb888..67836356 100644 --- a/Nuake/src/Scene/Components/QuakeMap.h +++ b/Nuake/src/Scene/Components/QuakeMap.h @@ -12,6 +12,7 @@ #include #include +#include namespace Nuake { @@ -55,8 +56,12 @@ namespace Nuake { j["Brushes"][i] = m_Brushes[i].GetID(); } + BinarySerializer serializer; for (unsigned int i = 0; i < m_Meshes.size(); i++) { + const std::string& path = FileSystem::RelativeToAbsolute("mesh" + std::to_string(i) + ".mesh"); + serializer.SerializeMesh(path, m_Meshes[i]); + j["Meshes"][i] = m_Meshes[i]->Serialize(); } diff --git a/Nuake/src/Scene/Systems/PhysicsSystem.cpp b/Nuake/src/Scene/Systems/PhysicsSystem.cpp index 0b9f0145..d97e9be4 100644 --- a/Nuake/src/Scene/Systems/PhysicsSystem.cpp +++ b/Nuake/src/Scene/Systems/PhysicsSystem.cpp @@ -205,18 +205,18 @@ namespace Nuake { const auto& modelComponent = entity.GetComponent(); - if (modelComponent.ModelResource) - { - uint32_t subMeshId = meshColliderComponent.SubMesh; - const std::vector>& submeshes = modelComponent.ModelResource->GetMeshes(); - if (subMeshId >= submeshes.size()) - { - Logger::Log("Cannot create mesh collider, invalid submesh ID", "physics", WARNING); - } - - Ref mesh = submeshes[subMeshId]; - meshColliderComponent.Shape = CreateRef(mesh); - } + //if (modelComponent.ModelResource.Get()) + //{ + // uint32_t subMeshId = meshColliderComponent.SubMesh; + // const std::vector>& submeshes = modelComponent.ModelResource.Get()->GetMeshes(); + // if (subMeshId >= submeshes.size()) + // { + // Logger::Log("Cannot create mesh collider, invalid submesh ID", "physics", WARNING); + // } + // + // Ref mesh = submeshes[subMeshId]; + // meshColliderComponent.Shape = CreateRef(mesh); + //} } } } @@ -281,18 +281,19 @@ namespace Nuake isTrigger = component.IsTrigger; - if (modelComponent.ModelResource) - { - uint32_t subMeshId = component.SubMesh; - const std::vector>& submeshes = modelComponent.ModelResource->GetMeshes(); - if (subMeshId >= submeshes.size()) - { - Logger::Log("Cannot create mesh collider, invalid submesh ID", "physics", WARNING); - } - - Ref mesh = submeshes[subMeshId]; - shape = CreateRef(mesh); - } + //modelComponent.ModelResource.Data() + //if (modelComponent.ModelResource.Get()) + //{ + // uint32_t subMeshId = component.SubMesh; + // const std::vector>& submeshes = modelComponent.ModelResource.Get()->GetMeshes(); + // if (subMeshId >= submeshes.size()) + // { + // Logger::Log("Cannot create mesh collider, invalid submesh ID", "physics", WARNING); + // } + // + // Ref mesh = submeshes[subMeshId]; + // shape = CreateRef(mesh); + //} } if (!shape) diff --git a/Nuake/src/Scene/Systems/QuakeMapBuilder.cpp b/Nuake/src/Scene/Systems/QuakeMapBuilder.cpp index 6f5099e0..9417491f 100644 --- a/Nuake/src/Scene/Systems/QuakeMapBuilder.cpp +++ b/Nuake/src/Scene/Systems/QuakeMapBuilder.cpp @@ -30,6 +30,8 @@ extern "C" { #include #include +#include "src/Resource/ResourceManager.h" + #include #include @@ -286,9 +288,8 @@ namespace Nuake { if (std::string(texture->name) != "__TB_empty") { std::string path = FileSystem::Root + "Textures/" + std::string(texture->name) + ".png"; - - if (const std::string materialPath = "Materials/" + std::string(texture->name) + ".material"; - FileSystem::FileExists(materialPath)) + const std::string materialPath = "Materials/" + std::string(texture->name) + ".material"; + if (FileSystem::FileExists(materialPath)) { Ref material = ResourceLoader::LoadMaterial(materialPath); m_Materials[path] = material; @@ -297,7 +298,14 @@ namespace Nuake { { if (m_Materials.find(path) == m_Materials.end()) { - m_Materials[path] = MaterialManager::Get()->GetMaterial(path); + Ref newMaterial = CreateRef(path); + auto materialJson = newMaterial->Serialize().dump(4); + FileSystem::BeginWriteFile(materialPath); + FileSystem::WriteLine(materialJson); + FileSystem::EndWriteFile(); + ResourceManager::RegisterResource(newMaterial); + ResourceManager::Manifest.RegisterResource(newMaterial->ID, materialPath); + m_Materials[path] = newMaterial; } } @@ -375,7 +383,7 @@ namespace Nuake { // CreateBrush(brush_inst, brush_geo_inst, m_Scene, newEntity, target, targetname); } - // Batching process + // Batching process` Ref model = CreateRef(); for (auto& mat : m_StaticWorld) { @@ -402,7 +410,7 @@ namespace Nuake { if (!bsp.IsTrigger) { ModelComponent& modelComponent = currentNonWorldEntity.AddComponent(); - modelComponent.ModelResource = model; + //modelComponent.ModelResource = model; } } } @@ -411,7 +419,8 @@ namespace Nuake { std::map> m_Materials; std::map, std::vector> m_StaticWorld; - Entity brushEntity = m_Scene->CreateEntity("Brush " + std::to_string(e)); + const std::string entityName = "Brush " + std::to_string(e); + Entity brushEntity = m_Scene->CreateEntity(entityName); worldspawnEntity.AddChild(brushEntity); auto& transformComponent = brushEntity.GetComponent(); @@ -440,24 +449,43 @@ namespace Nuake { if (std::string(texture->name) != "__TB_empty") { std::string path = FileSystem::Root + "Textures/" + std::string(texture->name) + ".png"; - - if (const std::string materialPath = "Materials/" + std::string(texture->name) + ".material"; - FileSystem::FileExists(materialPath)) + const std::string materialPath = "Materials/" + std::string(texture->name) + ".material"; + if (FileSystem::FileExists(materialPath)) { - Ref material = ResourceLoader::LoadMaterial(materialPath); - m_Materials[path] = material; + currentMaterial = ResourceManager::GetResourceFromFile(FileSystem::GetFile(materialPath)); + m_Materials[path] = currentMaterial; } else { if (m_Materials.find(path) == m_Materials.end()) { - m_Materials[path] = MaterialManager::Get()->GetMaterial(path); + Ref newMaterial = CreateRef(path); + newMaterial->AlbedoImage = TextureManager::Get()->GetTexture2(path)->GetID(); + + auto materialJson = newMaterial->Serialize().dump(4); + FileSystem::BeginWriteFile(materialPath); + FileSystem::WriteLine(materialJson); + FileSystem::EndWriteFile(); + + FileSystem::Scan(); + + if (auto file = FileSystem::GetFile(materialPath); file != nullptr) + { + ResourceManager::RegisterUnregisteredRessource(file); + } + else + { + Logger::Log("File at path: " + materialPath + " was not found in internal file system", "FS", CRITICAL); + } + + m_Materials[path] = newMaterial; + currentMaterial = newMaterial; } } currentMaterial = m_Materials[path]; - texture->height = currentMaterial->m_Albedo->GetHeight(); - texture->width = currentMaterial->m_Albedo->GetWidth(); + texture->height = 64; + texture->width = 64; } else { @@ -553,8 +581,15 @@ namespace Nuake { //bsp.Meshes.push_back(mesh); } + BinarySerializer serializer; + const std::string assetPath = quakeMapC.Path.GetAbsolutePath() + "." + entityName + ".nkmesh"; + serializer.SerializeModel(assetPath, model); + + ResourceManager::RegisterResource(model); + ResourceManager::Manifest.RegisterResource(model->ID, FileSystem::AbsoluteToRelative(assetPath)); + ModelComponent& modelComponent = brushEntity.AddComponent(); - modelComponent.ModelResource = model; + modelComponent.ModelResource = model->ID; } isEntity = false; diff --git a/Nuake/src/Scene/Systems/WadConverter.cpp b/Nuake/src/Scene/Systems/WadConverter.cpp index 604ef99d..b02e0a75 100644 --- a/Nuake/src/Scene/Systems/WadConverter.cpp +++ b/Nuake/src/Scene/Systems/WadConverter.cpp @@ -450,8 +450,6 @@ namespace Nuake for (auto& t : ConvertedTextures) { Ref material = CreateRef(); - material->IsEmbedded = false; - if (t.fullbright) { diff --git a/NuakeNet/src/Scene.cs b/NuakeNet/src/Scene.cs index ce25a1e1..67aca328 100644 --- a/NuakeNet/src/Scene.cs +++ b/NuakeNet/src/Scene.cs @@ -7,7 +7,7 @@ namespace Nuake.Net public class Scene { internal static unsafe delegate* GetEntityIcall; - internal static unsafe delegate**, void> GetEntityScriptFromNameIcall; + internal static unsafe delegate* unmanaged*, void> GetEntityScriptFromNameIcall; internal static unsafe delegate*> GetEntityScriptFromHandleIcall; internal static unsafe delegate* InstancePrefabIcall;