Loading and unloading working

This commit is contained in:
Chewico 2026-02-10 17:37:28 +01:00
parent 6ee3cc146f
commit 946c9e0027
27 changed files with 652 additions and 392 deletions

View File

@ -1,83 +0,0 @@
#pragma once
#include "DeerCore/Tools/Memory.h"
#include "DeerCore/Tools/TypeDefs.h"
#include <string>
#include <unordered_map>
#include <vector>
namespace Deer {
/*
class DataSource {
public:
using DataImporter = [ClassOfTheDataImporter]
};
*/
using StorageMetadata = std::unordered_map<std::string, std::string>;
template <typename T>
concept HasMetadata = requires(const std::string& location) {
{ T::loadMetadata(location) } -> std::same_as<StorageMetadata>;
{ T::saveMetadata(StorageMetadata{}, location) };
};
class StorageData {
public:
StorageData() = default;
StorageData(uint32_t dataSize) : size(dataSize), data(MakeScope<uint8_t[]>(dataSize)) {}
inline uint8_t* getData() { return data.get(); }
inline const uint8_t* getData() const { return data.get(); }
inline uint32_t getSize() const { return size; }
inline StorageMetadata& getMetadata() { return metadata; }
template <typename DataImporter, typename T>
Scope<T> deserialize();
template <typename DataImporter, typename T>
static StorageData serialize(const T&);
inline explicit operator bool() const { return size != 0; }
private:
StorageMetadata metadata;
Scope<uint8_t[]> data = nullptr;
uint32_t size = 0;
};
template <typename DataSource>
class StorageBackend {
public:
static StorageData loadData(const std::string& location);
static void saveData(const std::string& location, const StorageData& data);
static StorageMetadata loadMetadata(const std::string& location);
static void saveMetadata(const StorageMetadata& metadata, const std::string& location);
static std::vector<std::string> indexResources(const std::string& location);
};
template <typename DataSource>
class DataManager {
public:
template <typename T>
static Scope<T> load(const std::string& dataId) {
StorageData data = StorageBackend<DataSource>::loadData(dataId);
if constexpr (HasMetadata<StorageBackend<DataSource>>) {
data.getMetadata() = StorageBackend<DataSource>::loadMetadata(dataId);
data.getMetadata()["dataId"] = dataId;
}
return data.deserialize<typename DataSource::DataImporter, T>();
}
template <typename T>
static void store(const std::string& dataId, T& value) {
StorageData data = StorageData::serialize<typename DataSource::DataImporter, T>(value);
StorageBackend<DataSource>::saveData(dataId, data);
if constexpr (HasMetadata<StorageBackend<DataSource>>) {
StorageBackend<DataSource>::saveMetadata(data.getMetadata(), dataId);
}
}
};
} // namespace Deer

View File

@ -1,62 +0,0 @@
#pragma once
#include <filesystem>
#include <string>
#include <vector>
#include "DeerCore/Tools/Path.h"
#define DEER_RESOURCE_PATH "Assets"
#define DEER_VOXEL_PATH "Voxels"
#define DEER_VOXEL_DATA_PATH "Voxels/Data"
#define DEER_VOXEL_ASPECT_PATH "Voxels/Visuals"
#define DEER_VOXEL_TEXTURE_PATH "Voxels/Textures"
#define DEER_VOXEL_SHADER_PATH "Voxels/Shaders"
#define DEER_EDITOR_PATH "Editor"
#define DEER_EDITOR_PANEL_PATH "Editor/Panels"
#define DEER_EDITOR_SERVICE_PATH "Editor/Services"
#define DEER_MESH_EXTENSION ".dmesh"
#define DEER_SHADER_EXTENSION ".glsl"
#define DEER_SCRIPT_EXTENSION ".as"
#define DEER_BIN_PATH "bin"
#define DEER_TEMP_PATH "tmp"
#define DEER_NULL_PATH "null"
namespace Deer {
struct DirectoryData {
std::vector<Path> dirs;
std::vector<Path> elements;
};
// Namespace to manage memory interactions
namespace DataStore {
// Clears the cache of dir data
void clearCache();
// Rerturns a directory data with the elements relative to the id
const DirectoryData& getDirData(const Path& id, const Path& dir, const char* extension);
// TODO: Add safety
// Returns the data of the specified file path
bool loadFileData(const Path& id, const Path& name, uint8_t** data, uint32_t* size);
// Returns the data of the specified file path avoiding extension
bool loadGlobalFileData(const Path& id, const Path& name, uint8_t** data, uint32_t* size);
void freeFileData(uint8_t*);
void createFolder(const Path& path);
void saveFile(const Path&, uint8_t* data, uint32_t size);
uint8_t* readFile(const Path&, uint32_t* size);
void deleteFile(const Path&);
// Refactor----
void compressFiles(std::vector<Path> files, const Path& path);
std::vector<Path> getFiles(const Path& path,
const std::string& extension);
// Refactor----
} // namespace DataStore
} // namespace Deer

View File

@ -1,7 +1,6 @@
#pragma once
#include "DeerCore/Components.h"
#include "DeerCore/Log.h"
#include "DeerCore/Resource.h"
#include "DeerCore/Tools/Memory.h"
#include "entt/entt.hpp"

View File

@ -1,140 +0,0 @@
#pragma once
#include "DeerCore/DataManagment.h"
#include "DeerCore/Log.h"
#include "DeerCore/Tools/Memory.h"
#include "DeerCore/Tools/Path.h"
#include "DeerCore/Tools/TypeDefs.h"
#include <cxxabi.h>
#include <unordered_map>
#include <vector>
namespace Deer {
template <typename T>
class ResourceManager;
template <typename T>
class Resource {
public:
int32_t getResourceId() const { return resourceId; }
bool isValid() const { return ResourceManager<T>::isValid(*this); }
T& getData() { return ResourceManager<T>::getResourceData(*this); }
const std::string& getStorageId() const { return ResourceManager<T>::getStorageId(*this); }
inline explicit operator bool() const { return resourceId >= 0; }
static Resource<T> unsafeFromId(int32_t id) {
Resource<T> res;
res.resourceId = id;
return res;
}
private:
// -1 = no resource loaded
int32_t resourceId = -1;
friend ResourceManager<T>;
};
template <typename T>
class ResourceBuilder {
public:
using BaseDataType = char;
static Scope<T> buildResource(const BaseDataType& baseData) {
static_assert(sizeof(T) == 0, "ResourceBuilder must be specialized for this type T");
return nullptr;
}
};
template <typename T>
class ResourceManager {
private:
struct ResourceData {
public:
ResourceData(const std::string& _resourceId, Scope<T>&& _data)
: storageId(_resourceId), data(std::move(_data)) {}
Scope<T> data;
const std::string storageId;
};
static std::vector<ResourceData> resources;
static std::unordered_map<std::string, Resource<T>> resourceCache;
public:
template <typename DataSource>
static Resource<T> loadResource(const std::string& storageId) {
if (resourceCache.contains(storageId))
return resourceCache[storageId];
using ResourceBuilderBaseDataType = typename ResourceBuilder<T>::BaseDataType;
Scope<T> data;
if constexpr (!std::is_void_v<ResourceBuilderBaseDataType>) {
Scope<ResourceBuilderBaseDataType> baseData = DataManager<DataSource>::template load<ResourceBuilderBaseDataType>(storageId);
if (!baseData) {
const char* baseDataType = abi::__cxa_demangle(typeid(ResourceBuilderBaseDataType).name(), 0, 0, nullptr);
const char* dataType = abi::__cxa_demangle(typeid(T).name(), 0, 0, nullptr);
DEER_CORE_ERROR("Error loading base resource {} for resource {} with id {}", baseDataType, dataType, storageId.c_str());
return Resource<T>();
}
data = ResourceBuilder<T>::buildResource(*baseData.get());
} else {
data = ResourceBuilder<T>::buildResource(); // No base data
}
Resource<T> resource = Resource<T>::unsafeFromId(resources.size());
resources.push_back({storageId, std::move(data)});
resourceCache[storageId] = resource;
return resource;
}
static Resource<T> getResource(const std::string& storageId) {
if (resourceCache.contains(storageId))
return resourceCache[storageId];
return Resource<T>();
}
static Resource<T> loadResourceFromData(const typename ResourceBuilder<T>::BaseDataType& resourceData, const std::string& storageId) {
if (resourceCache.contains(storageId))
return resourceCache[storageId];
Scope<T> data = ResourceBuilder<T>::buildResource(resourceData);
Resource<T> resource = Resource<T>::unsafeFromId(resources.size());
resources.push_back({storageId, std::move(data)});
resourceCache[storageId] = resource;
return resource;
}
static void unloadResources() {
resourceCache.clear();
resources.clear();
}
static inline bool isValid(Resource<T> res) {
return res.resourceId >= 0 && res.resourceId < static_cast<int32_t>(resources.size());
}
static inline const std::string& getStorageId(Resource<T> res) {
const static std::string invalid("NULL");
if (!isValid(res))
return invalid;
return resources[res.resourceId].storageId;
}
static T& getResourceData(Resource<T> res) {
return *resources[res.resourceId].data;
}
};
template <typename T>
std::vector<typename ResourceManager<T>::ResourceData> ResourceManager<T>::resources;
template <typename T>
std::unordered_map<std::string, Resource<T>> ResourceManager<T>::resourceCache;
} // namespace Deer

View File

@ -35,7 +35,7 @@ namespace Deer {
std::string baseTypeName;
std::string moduleName;
std::vector<SystemEvent> events;
SystemDescription(const std::string& _baseTypeName, const std::string& _moduleName, const std::vector<SystemEvent>& _events = {}) : baseTypeName(_baseTypeName), moduleName(_moduleName), events(_events) {}
SystemDescription(const std::string& _baseTypeName = "", const std::string& _moduleName = "", const std::vector<SystemEvent>& _events = {}) : baseTypeName(_baseTypeName), moduleName(_moduleName), events(_events) {}
};
// Functions called by Engine

View File

@ -0,0 +1,31 @@
#pragma once
#include "cereal/cereal.hpp"
#ifdef DEER_RENDER
#include "DeerRender/Mesh.h"
#include "DeerRender/Shader.h"
#include "DeerRender/Tools/Memory.h"
#include <functional>
#endif
namespace Deer {
struct WorldSerializationSettings {
#ifdef DEER_RENDER
bool includeServer = false;
bool includeClient = true;
std::function<Scope<MeshData>(const Path&)> meshLoadingFunction = nullptr;
std::function<Scope<ShaderData>(const Path&)> shaderLoadingFunction = nullptr;
#else
bool includeServer = true;
bool includeClient = false;
#endif
template <class Archive>
void serialize(Archive& archive) {
archive(CEREAL_NVP(includeServer));
archive(CEREAL_NVP(includeClient));
}
};
} // namespace Deer

View File

@ -1,4 +1,5 @@
#pragma once
#include "DeerCore/Serialization/WorldSettings.h"
#include "DeerCore/Tools/Path.h"
namespace Deer {
@ -7,8 +8,9 @@ namespace Deer {
namespace Universe {
World* createWorld(const WorldSettings&);
World* loadWorldFromJson(const WorldSettings&, WorldSerializationSettings&, const Path&);
void saveWorldInJson(World*, const Path& path);
void saveWorldInJson(World*, WorldSerializationSettings& serializationSettings, const Path& path);
void destroyAllWorlds();
void flushDestroyedWorlds();

View File

@ -1,2 +1,78 @@
#pragma once
#include "DeerCore/DataManagment.h"
#include "DeerCore/Tools/Memory.h"
#include "DeerCore/Tools/TypeDefs.h"
#include <string>
#include <unordered_map>
#include <vector>
namespace Deer {
using StorageMetadata = std::unordered_map<std::string, std::string>;
template <typename T>
concept HasMetadata = requires(const std::string& location) {
{ T::loadMetadata(location) } -> std::same_as<StorageMetadata>;
{ T::saveMetadata(StorageMetadata{}, location) };
};
class StorageData {
public:
StorageData() = default;
StorageData(uint32_t dataSize) : size(dataSize), data(MakeScope<uint8_t[]>(dataSize)) {}
inline uint8_t* getData() { return data.get(); }
inline const uint8_t* getData() const { return data.get(); }
inline uint32_t getSize() const { return size; }
inline StorageMetadata& getMetadata() { return metadata; }
template <typename DataImporter, typename T>
Scope<T> deserialize();
template <typename DataImporter, typename T>
static StorageData serialize(const T&);
inline explicit operator bool() const { return size != 0; }
private:
StorageMetadata metadata;
Scope<uint8_t[]> data = nullptr;
uint32_t size = 0;
};
template <typename DataSource>
class StorageBackend {
public:
static StorageData loadData(const std::string& location);
static void saveData(const std::string& location, const StorageData& data);
static StorageMetadata loadMetadata(const std::string& location);
static void saveMetadata(const StorageMetadata& metadata, const std::string& location);
static std::vector<std::string> indexResources(const std::string& location);
};
template <typename DataSource>
class DataManager {
public:
template <typename T>
static Scope<T> load(const std::string& dataId) {
StorageData data = StorageBackend<DataSource>::loadData(dataId);
if constexpr (HasMetadata<StorageBackend<DataSource>>) {
data.getMetadata() = StorageBackend<DataSource>::loadMetadata(dataId);
data.getMetadata()["dataId"] = dataId;
}
return data.deserialize<typename DataSource::DataImporter, T>();
}
template <typename T>
static void store(const std::string& dataId, T& value) {
StorageData data = StorageData::serialize<typename DataSource::DataImporter, T>(value);
StorageBackend<DataSource>::saveData(dataId, data);
if constexpr (HasMetadata<StorageBackend<DataSource>>) {
StorageBackend<DataSource>::saveMetadata(data.getMetadata(), dataId);
}
}
};
} // namespace Deer

View File

@ -1,2 +1,140 @@
#pragma once
#include "DeerCore/Resource.h"
#include "DeerRender/DataManagment.h"
#include "DeerRender/Log.h"
#include "DeerRender/Tools/Memory.h"
#include "DeerRender/Tools/Path.h"
#include "DeerRender/Tools/TypeDefs.h"
#include <cxxabi.h>
#include <unordered_map>
#include <vector>
namespace Deer {
template <typename T>
class ResourceManager;
template <typename T>
class Resource {
public:
int32_t getResourceId() const { return resourceId; }
bool isValid() const { return ResourceManager<T>::isValid(*this); }
T& getData() { return ResourceManager<T>::getResourceData(*this); }
const std::string& getStorageId() const { return ResourceManager<T>::getStorageId(*this); }
inline explicit operator bool() const { return resourceId >= 0; }
static Resource<T> unsafeFromId(int32_t id) {
Resource<T> res;
res.resourceId = id;
return res;
}
private:
// -1 = no resource loaded
int32_t resourceId = -1;
friend ResourceManager<T>;
};
template <typename T>
class ResourceBuilder {
public:
using BaseDataType = char;
static Scope<T> buildResource(const BaseDataType& baseData) {
static_assert(sizeof(T) == 0, "ResourceBuilder must be specialized for this type T");
return nullptr;
}
};
template <typename T>
class ResourceManager {
private:
struct ResourceData {
public:
ResourceData(const std::string& _resourceId, Scope<T>&& _data)
: storageId(_resourceId), data(std::move(_data)) {}
Scope<T> data;
const std::string storageId;
};
static std::vector<ResourceData> resources;
static std::unordered_map<std::string, Resource<T>> resourceCache;
public:
template <typename DataSource>
static Resource<T> loadResource(const std::string& storageId) {
if (resourceCache.contains(storageId))
return resourceCache[storageId];
using ResourceBuilderBaseDataType = typename ResourceBuilder<T>::BaseDataType;
Scope<T> data;
if constexpr (!std::is_void_v<ResourceBuilderBaseDataType>) {
Scope<ResourceBuilderBaseDataType> baseData = DataManager<DataSource>::template load<ResourceBuilderBaseDataType>(storageId);
if (!baseData) {
const char* baseDataType = abi::__cxa_demangle(typeid(ResourceBuilderBaseDataType).name(), 0, 0, nullptr);
const char* dataType = abi::__cxa_demangle(typeid(T).name(), 0, 0, nullptr);
DEER_CORE_ERROR("Error loading base resource {} for resource {} with id {}", baseDataType, dataType, storageId.c_str());
return Resource<T>();
}
data = ResourceBuilder<T>::buildResource(*baseData.get());
} else {
data = ResourceBuilder<T>::buildResource(); // No base data
}
Resource<T> resource = Resource<T>::unsafeFromId(resources.size());
resources.push_back({storageId, std::move(data)});
resourceCache[storageId] = resource;
return resource;
}
static Resource<T> getResource(const std::string& storageId) {
if (resourceCache.contains(storageId))
return resourceCache[storageId];
return Resource<T>();
}
static Resource<T> loadResourceFromData(const typename ResourceBuilder<T>::BaseDataType& resourceData, const std::string& storageId) {
if (resourceCache.contains(storageId))
return resourceCache[storageId];
Scope<T> data = ResourceBuilder<T>::buildResource(resourceData);
Resource<T> resource = Resource<T>::unsafeFromId(resources.size());
resources.push_back({storageId, std::move(data)});
resourceCache[storageId] = resource;
return resource;
}
static void unloadResources() {
resourceCache.clear();
resources.clear();
}
static inline bool isValid(Resource<T> res) {
return res.resourceId >= 0 && res.resourceId < static_cast<int32_t>(resources.size());
}
static inline const std::string& getStorageId(Resource<T> res) {
const static std::string invalid("NULL");
if (!isValid(res))
return invalid;
return resources[res.resourceId].storageId;
}
static T& getResourceData(Resource<T> res) {
return *resources[res.resourceId].data;
}
};
template <typename T>
std::vector<typename ResourceManager<T>::ResourceData> ResourceManager<T>::resources;
template <typename T>
std::unordered_map<std::string, Resource<T>> ResourceManager<T>::resourceCache;
} // namespace Deer

View File

@ -6,6 +6,11 @@
#include "DeerCore/Serialization/Components/TransformComponent.h"
#ifdef DEER_RENDER
#include "DeerRender/Serialization/Components/MeshComponentSerialization.h"
#include "DeerRender/Serialization/Components/ShaderComponentSerialization.h"
#endif
#include "cereal/cereal.hpp"
#include <string>
@ -16,18 +21,34 @@ namespace Deer {
uint32_t entityId;
template <class Archive, class T, class S>
void saveComponent(Archive& archive, Entity& entity) {
void saveComponent(Archive& archive, Entity& entity, const char* name) const {
std::string containsComponentString = "Contains_";
containsComponentString += typeid(T).name();
containsComponentString += name;
bool hasComponent = entity.hasComponent<T>();
archive(cereal::make_nvp(containsComponentString.c_str(), hasComponent));
if (hasComponent) {
S serialization;
serialization.component = entity.getComponent<T>();
serialization.settings = settings;
S serialization{
.component = entity.getComponent<T>(),
.settings = settings};
archive(cereal::make_nvp(typeid(T).name(), serialization));
archive(cereal::make_nvp(name, serialization));
}
}
template <class Archive, class T, class S>
void loadComponent(Archive& archive, Entity& entity, const char* name) {
std::string containsComponentString = "Contains_";
containsComponentString += name;
bool hasComponent;
archive(cereal::make_nvp(containsComponentString.c_str(), hasComponent));
if (hasComponent) {
S serialization{
.component = entity.addComponent<T>(),
.settings = settings};
archive(cereal::make_nvp(name, serialization));
}
}
@ -43,14 +64,46 @@ namespace Deer {
archive(cereal::make_nvp("NetworkBehaviour", tag.networkBehaviour));
archive(cereal::make_nvp("Parent", relation.parent_id));
TransformComponentSerialization serialization;
serialization.settings = settings;
serialization.component = entity.getComponent<T>();
archive(cereal::make_nvp(typeid(TransformComponent).name(), serialization));
TransformComponentSerialization transformSerialization{
.settings = settings,
.component = entity.getComponent<TransformComponent>()};
archive(cereal::make_nvp("TransformComponent", transformSerialization));
if (settings.includeClient) {
#ifdef DEER_RENDER
saveComponent<Archive, MeshComponent, MeshComponentSerialization>(archive, entity, "MeshComponent");
saveComponent<Archive, ShaderComponent, ShaderComponentSerialization>(archive, entity, "ShaderComponent");
#else
DEER_CORE_ERROR("Can not include client on Deer Core Headless Compile");
#endif
}
}
template <class Archive>
void load(Archive& archive) {
archive(cereal::make_nvp("Id", entityId));
Entity& entity = entityEnvironment->createEntityWithId(entityId);
TagComponent& tag = entity.getComponent<TagComponent>();
RelationshipComponent& relation = entity.getComponent<RelationshipComponent>();
archive(cereal::make_nvp("Tag", tag.tag));
archive(cereal::make_nvp("NetworkBehaviour", tag.networkBehaviour));
archive(cereal::make_nvp("Parent", tag.networkBehaviour));
TransformComponentSerialization transformSerialization{
.settings = settings,
.component = entity.getComponent<TransformComponent>()};
archive(cereal::make_nvp("TransformComponent", transformSerialization));
if (settings.includeClient) {
#ifdef DEER_RENDER
loadComponent<Archive, MeshComponent, MeshComponentSerialization>(archive, entity, "MeshComponent");
loadComponent<Archive, ShaderComponent, ShaderComponentSerialization>(archive, entity, "ShaderComponent");
#else
DEER_CORE_ERROR("Can not include client on Deer Core Headless Compile");
#endif
}
}
};
} // namespace Deer

View File

@ -18,7 +18,7 @@ namespace Deer {
void save(Archive& archive) const {
std::vector<EntitySerialization> entities;
size_t entityIndex = 0;
uint32_t entityIndex = 0;
size_t remainingEntities = entityEnvironment->getEntityCount();
while (remainingEntities > 0) {
if (!entityEnvironment->entityExists(entityIndex)) {
@ -43,13 +43,12 @@ namespace Deer {
entityIndex++;
}
entities.push_back({});
EntitySerialization entityS{
.settings = settings,
.entityEnvironment = entityEnvironment,
.entityId = entityIndex};
EntitySerialization& entityS = entities.push_back();
entityS.settings = settings;
entityS.entityId = entityEnvironment;
entityS.entityId = entityIndex;
entities.push_back(entityS);
remainingEntities--;
entityIndex++;
@ -63,14 +62,20 @@ namespace Deer {
template <class Archive>
void load(Archive& archive) {
size_t entitySize;
archive(cereal::make_nvp("Entities", entitySize));
archive(cereal::make_size_tag(entitySize));
std::vector<uint32_t> entities;
for (size_t i = 0; i < entitySize; i++) {
EntitySerialization entityS;
entityS.entityEnvironment = entityEnvironment;
entityS.settings = settings;
EntitySerialization entityS{.settings = settings, .entityEnvironment = entityEnvironment};
archive(entityS);
entities.push_back(entityS.entityId);
}
for (uint32_t entity_id : entities) {
Entity& entity = entityEnvironment->getEntity(entity_id);
RelationshipComponent& tag = entity.getComponent<RelationshipComponent>();
entity.setParent(entityEnvironment->getEntity(tag.parent_id));
}
}
};

View File

@ -8,18 +8,15 @@ namespace Deer {
class World;
struct WorldSerialization {
WorldSerializationSettings settings;
EntityEnvironment* entityEnv;
uint32_t entityId;
WorldSerializationSettings& settings;
World* world;
template <class Archive>
void serialize(Archive& archive) {
EntityEnvironmentSerialization entityEnvironment;
entityEnvironment.settings = settings;
entityEnvironment.entityEnvironment = world->entityEnvironment.get();
EntityEnvironmentSerialization entityEnvironment{
.settings = settings,
.entityEnvironment = world->entityEnvironment.get()};
archive(cereal::make_nvp("SerializationSettings", settings));
archive(cereal::make_nvp("EntityEnvironment", entityEnvironment));
}
};

View File

@ -1,25 +0,0 @@
#pragma once
#include "cereal/cereal.hpp"
namespace Deer {
struct WorldSerializationSettings {
bool includeServer;
bool includeClient;
WorldSerializationSettings() {
#ifdef DEER_RENDER
includeServer = false;
includeClient = true;
#else
includeServer = true;
includeClient = false;
#endif
};
template <class Archive>
void serialize(Archive& archive) {
archive(CEREAL_NVP(includeServer));
archive(CEREAL_NVP(includeClient));
}
};
} // namespace Deer

View File

@ -1,6 +1,35 @@
#include "DeerCore/Universe.h"
#include "DeerCore/Serialization/World.h"
#include "cereal/archives/json.hpp"
#include <fstream>
namespace Deer {
void Universe::saveWorldInJson(World* world, const Path& path) {
void Universe::saveWorldInJson(World* world, WorldSerializationSettings& serializationSettings, const Path& path) {
std::ofstream file(path);
cereal::JSONOutputArchive archive(file);
WorldSerialization worldSerialization{
.settings = serializationSettings,
.world = world};
archive(cereal::make_nvp("SerializationSettings", serializationSettings));
archive(cereal::make_nvp("World", worldSerialization));
}
World* Universe::loadWorldFromJson(const WorldSettings& worldSettings, WorldSerializationSettings& serializationSettings, const Path& path) {
std::ifstream file(path);
cereal::JSONInputArchive archive(file);
World* world = Universe::createWorld(worldSettings);
WorldSerialization worldSerialization{
.settings = serializationSettings,
.world = world};
archive(cereal::make_nvp("SerializationSettings", serializationSettings));
archive(cereal::make_nvp("World", worldSerialization));
return world;
}
} // namespace Deer

View File

@ -20,7 +20,7 @@ namespace Deer {
}
World::~World() {
DEER_CORE_ASSERT(executingState == WorldState::Stopped, "Invalid executing state while destroying world");
DEER_CORE_ASSERT(executingState == WorldState::ReadyToDestroy, "Invalid executing state while destroying world");
}
void World::stopExecution() {

View File

@ -65,6 +65,10 @@ namespace Deer {
RenderCommand::init();
RenderUtils::initializeRenderUtils();
Builtin::cube();
Builtin::simpleShader();
Builtin::sphere();
ImGuiLayer::init(*window);
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "DeerCore/Serialization/WorldSettings.h"
#include "DeerRender/Components.h"
#include "cereal/cereal.hpp"
namespace Deer {
struct MeshComponentSerialization {
WorldSerializationSettings& settings;
MeshComponent& component;
template <class Archive>
void save(Archive& archive) const {
archive(cereal::make_nvp("IsActive", component.active));
archive(cereal::make_nvp("Mesh", component.mesh.getStorageId()));
}
template <class Archive>
void load(Archive& archive) {
std::string storageId;
archive(cereal::make_nvp("IsActive", component.active));
archive(cereal::make_nvp("Mesh", storageId));
Scope<MeshData> meshData = settings.meshLoadingFunction(storageId);
Resource<GPUMesh> mesh = ResourceManager<GPUMesh>::loadResourceFromData(*meshData.get(), storageId);
component.mesh = mesh;
}
};
} // namespace Deer

View File

@ -0,0 +1,30 @@
#pragma once
#include "DeerCore/Serialization/WorldSettings.h"
#include "DeerRender/Components.h"
#include "cereal/cereal.hpp"
namespace Deer {
struct ShaderComponentSerialization {
WorldSerializationSettings& settings;
ShaderComponent& component;
template <class Archive>
void save(Archive& archive) const {
archive(cereal::make_nvp("Shader", component.shader.getStorageId()));
}
template <class Archive>
void load(Archive& archive) {
std::string storageId;
archive(cereal::make_nvp("Shader", storageId));
Scope<ShaderData> shaderData = settings.shaderLoadingFunction(storageId);
Resource<Shader> shader = ResourceManager<Shader>::loadResourceFromData(*shaderData.get(), storageId);
component.shader = shader;
}
};
} // namespace Deer

View File

@ -1,20 +0,0 @@
#pragma once
#include "DeerCore/Serialization/WorldSettings.h"
#include "DeerRender/Components.h"
#include "cereal/cereal.hpp"
namespace Deer {
struct MeshComponentSerialization {
WorldSerializationSettings& settings;
TransformComponent& component;
template <class Archive>
void serialize(Archive& archive) {
archive(cereal::make_nvp("Position", component.position));
archive(cereal::make_nvp("Scale", component.scale));
archive(cereal::make_nvp("Rotation", component.rotation));
}
};
} // namespace Deer

View File

@ -5,9 +5,13 @@
int main(int, char**);
namespace Deer {
class World;
namespace DeerStudio {
void onRender();
void onUpdate();
extern World* world;
void worldUpdate(World&);
void worldRender(World&);
void onEvent(Event& e);
void main();
@ -16,5 +20,6 @@ namespace Deer {
// Other
void onPanelMenuBar();
void worldManagerMenu();
} // namespace DeerStudio
} // namespace Deer

View File

@ -24,6 +24,5 @@ namespace Deer {
CScriptDictionary* setResourceMetadata(std::string& resource);
ResourceType getResourceType(std::string&);
} // namespace StudioAPI
} // namespace Deer

View File

@ -4,7 +4,8 @@ namespace Deer {
class World;
namespace StudioPanel {
void init(World*);
void init();
void setWorld(World*);
void update();
void render();

View File

@ -16,7 +16,7 @@ namespace Deer {
class FrameBuffer;
namespace DeerStudio {
Universe::WorldHandle mainWorld;
World* world;
void onUpdate();
void onRender();
@ -29,6 +29,7 @@ namespace Deer {
void main() {
Engine::init();
Engine::setEventCallback(DeerStudio::onEvent);
StudioPanel::init();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20, 20));
initializeFonts();
@ -41,10 +42,12 @@ namespace Deer {
.renderFrequency = 166,
};
mainWorld = Universe::createWorld(worldSettings);
StudioPanel::init(&Universe::getWorld(mainWorld));
world = Universe::createWorld(worldSettings);
Universe::getWorld(mainWorld).execute();
while (world) {
StudioPanel::setWorld(world);
world->execute();
}
Engine::shutdown();
} // namespace DeerStudio
@ -80,8 +83,7 @@ namespace Deer {
}
static bool p_open = true;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,
ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::Begin("DockSpace Demo", &p_open, window_flags);
@ -93,17 +95,17 @@ namespace Deer {
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("Panel")) {
onPanelMenuBar();
if (ImGui::BeginMenu("World")) {
DeerStudio::worldManagerMenu();
ImGui::EndMenu();
}
if (ImGui::MenuItem("Reload Editor")) {
}
ImGui::EndMenuBar();
}
ImGui::PopStyleVar();
StudioPanel::render();
if (world.getExecutionState() == Deer::WorldState::Executing)
StudioPanel::render();
ImGui::End();
@ -117,7 +119,8 @@ namespace Deer {
}
bool onWindowCloseEvent(WindowCloseEvent&) {
Universe::getWorld(mainWorld).stopExecution();
world->stopExecution();
world = nullptr;
return true;
}
} // namespace DeerStudio

View File

@ -13,12 +13,15 @@ namespace Deer {
void renderSystem(ScriptSystem* system);
} // namespace StudioPanel
void StudioPanel::init(World* world) {
void StudioPanel::init() {
StudioScripting::registerStudioScripting();
Scripting::registerInterface("Panel");
Scripting::registerInterfaceFunction("Panel", "onImGui", Scripting::EventType::void_event);
Scripting::compileFiles("Editor/Scripts", "Editor");
}
void StudioPanel::setWorld(World* world) {
Scripting::SystemDescription systemDescription("Panel", "Editor");
systemDescription.events.push_back(Scripting::SystemEvent("onInit", Scripting::EventType::void_event));
systemDescription.events.push_back(Scripting::SystemEvent("onShutdown", Scripting::EventType::void_event));
@ -67,5 +70,6 @@ namespace Deer {
void StudioPanel::shutdown() {
editorInstance->executeOnGroup_voidEvent(1);
studioPanelEnvironment.release();
}
} // namespace Deer

View File

@ -0,0 +1,47 @@
#include "DeerCore/Serialization/WorldSettings.h"
#include "DeerCore/Tools/Memory.h"
#include "DeerCore/Universe.h"
#include "DeerStudio/EditorDataImporter.h"
#include "DeerStudio/EditorDataSource.h"
#include "DeerStudio/StudioPanel.h"
#include "DeerCore/World.h"
#include "DeerStudio/DeerStudio.h"
#include "imgui.h"
namespace Deer {
template <typename T>
Scope<T> loadResourceData(const Path& storageId) {
StorageData storageData = StorageBackend<EditorDataSource>::loadData(storageId);
return storageData.deserialize<EditorDataImporter, T>();
}
void DeerStudio::worldManagerMenu() {
if (ImGui::MenuItem("Save world")) {
WorldSerializationSettings serializationSettings{
.includeServer = true,
.includeClient = true};
Universe::saveWorldInJson(world, serializationSettings, "Worlds/world.json");
}
if (ImGui::MenuItem("Load world")) {
StudioPanel::shutdown();
world->destroy();
WorldSettings worldSettings = {
.updateCallback = worldUpdate,
.updateFrequency = 60,
.renderCallback = worldRender,
.renderFrequency = 166,
};
WorldSerializationSettings serializationSettings{
.meshLoadingFunction = loadResourceData<MeshData>,
.shaderLoadingFunction = loadResourceData<ShaderData>};
World* world_new = Universe::loadWorldFromJson(worldSettings, serializationSettings, "Worlds/world.json");
world = world_new;
}
}
} // namespace Deer

135
Worlds/world.json Normal file
View File

@ -0,0 +1,135 @@
{
"SerializationSettings": {
"includeServer": true,
"includeClient": true
},
"World": {
"EntityEnvironment": [
{
"Id": 0,
"Tag": "root",
"NetworkBehaviour": 0,
"Parent": 0,
"TransformComponent": {
"Position": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"Scale": {
"x": 1.0,
"y": 1.0,
"z": 1.0
},
"Rotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
}
},
"Contains_MeshComponent": false,
"Contains_ShaderComponent": false
},
{
"Id": 1,
"Tag": "node",
"NetworkBehaviour": 0,
"Parent": 0,
"TransformComponent": {
"Position": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"Scale": {
"x": 1.0,
"y": 1.0,
"z": 1.0
},
"Rotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
}
},
"Contains_MeshComponent": true,
"MeshComponent": {
"IsActive": true,
"Mesh": "Builtin:Cube"
},
"Contains_ShaderComponent": true,
"ShaderComponent": {
"Shader": "Builtin:SimpleShader"
}
},
{
"Id": 2,
"Tag": "node",
"NetworkBehaviour": 0,
"Parent": 0,
"TransformComponent": {
"Position": {
"x": 2.299999952316284,
"y": 0.0,
"z": 0.0
},
"Scale": {
"x": 1.0,
"y": 1.0,
"z": 1.0
},
"Rotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
}
},
"Contains_MeshComponent": true,
"MeshComponent": {
"IsActive": true,
"Mesh": "Builtin:Sphere"
},
"Contains_ShaderComponent": true,
"ShaderComponent": {
"Shader": "Builtin:SimpleShader"
}
},
{
"Id": 3,
"Tag": "node",
"NetworkBehaviour": 0,
"Parent": 0,
"TransformComponent": {
"Position": {
"x": -3.700000047683716,
"y": 0.0,
"z": 0.0
},
"Scale": {
"x": 1.0,
"y": 1.0,
"z": 1.0
},
"Rotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": 1.0
}
},
"Contains_MeshComponent": true,
"MeshComponent": {
"IsActive": true,
"Mesh": "train-locomotive-a.obj"
},
"Contains_ShaderComponent": true,
"ShaderComponent": {
"Shader": "shader_darkBlue.glsl"
}
}
]
}
}

View File

@ -1,6 +1,6 @@
[Window][DockSpace Demo]
Pos=0,0
Size=1920,1011
Size=2560,1371
Collapsed=0
[Window][Debug##Default]
@ -10,30 +10,30 @@ Collapsed=0
[Window][ViewportPanel]
Pos=561,26
Size=934,493
Size=1574,853
Collapsed=0
DockId=0x00000005,0
[Window][PropertiesPanel]
Pos=1497,26
Size=423,493
Pos=2137,26
Size=423,853
Collapsed=0
DockId=0x00000006,0
[Window][ResourceExplorer]
Pos=0,521
Size=1920,490
Pos=0,881
Size=2560,490
Collapsed=0
DockId=0x00000004,0
[Window][TreePanel]
Pos=0,26
Size=559,493
Size=559,853
Collapsed=0
DockId=0x00000001,0
[Docking][Data]
DockSpace ID=0x0AC2E849 Window=0xD0388BC8 Pos=0,26 Size=1920,985 Split=Y
DockSpace ID=0x0AC2E849 Window=0xD0388BC8 Pos=0,26 Size=2560,1345 Split=Y
DockNode ID=0x00000003 Parent=0x0AC2E849 SizeRef=1920,493 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=559,985 Selected=0x16E3C1E7
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1359,985 Split=X