diff --git a/assets/prefabs/rosa.json b/assets/prefabs/rosa.json index 16dc7542..671178ca 100644 --- a/assets/prefabs/rosa.json +++ b/assets/prefabs/rosa.json @@ -20,8 +20,7 @@ "type": "RPGEntity" }, "player": { - "type": "RPGPlayer", - "camera": "camera" + "type": "RPGPlayer" } } } \ No newline at end of file diff --git a/assets/scenes/test_rpg_scene.json b/assets/scenes/test_rpg_scene.json index 69b2f4a6..3b570def 100644 --- a/assets/scenes/test_rpg_scene.json +++ b/assets/scenes/test_rpg_scene.json @@ -23,7 +23,12 @@ "rosa": { "prefab": "rosa", - "position": [ 0, 0, 0 ] + "position": [ 0, 0, 0 ], + "components": { + "player": { + "camera": "camera" + } + } }, "npc": { diff --git a/src/dawn/asset/loader/scene/LoaderForSceneItems.hpp b/src/dawn/asset/loader/scene/LoaderForSceneItems.hpp index 3ca2ee07..0001baf5 100644 --- a/src/dawn/asset/loader/scene/LoaderForSceneItems.hpp +++ b/src/dawn/asset/loader/scene/LoaderForSceneItems.hpp @@ -32,5 +32,8 @@ namespace Dawn { ); ~LoaderForSceneItems(); + + friend class SceneLoader; + friend class PrefabLoader; }; } \ No newline at end of file diff --git a/src/dawn/asset/loader/scene/PrefabLoader.cpp b/src/dawn/asset/loader/scene/PrefabLoader.cpp index cf32b0bb..7715a526 100644 --- a/src/dawn/asset/loader/scene/PrefabLoader.cpp +++ b/src/dawn/asset/loader/scene/PrefabLoader.cpp @@ -10,6 +10,7 @@ #include "asset/loader/TextureLoader.hpp" #include "scene/Scene.hpp" #include "component/SceneComponentRegistry.hpp" +#include "util/JSON.hpp" using namespace Dawn; @@ -35,6 +36,7 @@ void PrefabLoader::updateSync() { assertNotNull(this->jsonLoader, "JSON Loader is NULL?"); if(!this->jsonLoader->loaded) return; this->state = PrefabLoaderState::LOADING_DEPENDENCIES; + this->ctx->data = this->jsonLoader->data; this->setupDependencies(); break; @@ -62,37 +64,6 @@ std::string PrefabLoader::getAssetType() const { return PrefabLoader::ASSET_TYPE; } -void PrefabLoader::stagePrefab(std::shared_ptr item) { - assertTrue(this->loaded, "Prefab not loaded"); - - // Force-set new context values - ctx->data = this->jsonLoader->data; - - // Load the scene item. - item->load(ctx); - - auto &itemData = this->jsonLoader->data; - if(itemData.contains("components")) { - for(auto &cmpItem : itemData["components"].items()) { - auto &cmpName = cmpItem.key(); - auto &cmpData = cmpItem.value(); - assertTrue( - cmpData.contains("type"), - "Component missing type in %s", - cmpName.c_str() - ); - - auto cmpType = cmpData["type"].get(); - auto cmp = SceneComponentRegistry::createComponent( - cmpType, item - ); - ctx->data = cmpData; - ctx->components[cmpName] = cmp; - cmp->load(ctx); - } - } -} - PrefabLoader::~PrefabLoader() { } \ No newline at end of file diff --git a/src/dawn/asset/loader/scene/PrefabLoader.hpp b/src/dawn/asset/loader/scene/PrefabLoader.hpp index 910f64da..bec92332 100644 --- a/src/dawn/asset/loader/scene/PrefabLoader.hpp +++ b/src/dawn/asset/loader/scene/PrefabLoader.hpp @@ -34,13 +34,6 @@ namespace Dawn { void updateAsync() override; std::string getAssetType() const override; - /** - * Stages this prefab onto a scene item. - * - * @param item The scene item to stage. - */ - void stagePrefab(std::shared_ptr item); - ~PrefabLoader(); }; } \ No newline at end of file diff --git a/src/dawn/asset/loader/scene/SceneLoadContext.hpp b/src/dawn/asset/loader/scene/SceneLoadContext.hpp index 082b12a1..76b26327 100644 --- a/src/dawn/asset/loader/scene/SceneLoadContext.hpp +++ b/src/dawn/asset/loader/scene/SceneLoadContext.hpp @@ -42,7 +42,7 @@ namespace Dawn { auto it = assets.find(j); if(it == assets.end()) { auto parent = this->parent.lock(); - assertNotNull(parent, "Couldn't find asset."); + assertNotNull(parent, "Couldn't find asset %s", j.c_str()); return parent->getAsset(j); } auto asset = std::dynamic_pointer_cast(it->second); @@ -80,5 +80,6 @@ namespace Dawn { friend class PrefabLoader; friend class LoaderForSceneItems; friend class SceneLoader; + friend class SceneItem; }; } \ No newline at end of file diff --git a/src/dawn/asset/loader/scene/SceneLoader.cpp b/src/dawn/asset/loader/scene/SceneLoader.cpp index 0cab200f..63a4d12c 100644 --- a/src/dawn/asset/loader/scene/SceneLoader.cpp +++ b/src/dawn/asset/loader/scene/SceneLoader.cpp @@ -7,7 +7,8 @@ #include "game/Game.hpp" #include "assert/assert.hpp" #include "scene/Scene.hpp" -#include "component/SceneComponentRegistry.hpp" +#include "util/JSON.hpp" +#include "PrefabLoader.hpp" using namespace Dawn; @@ -76,29 +77,33 @@ void SceneLoader::updateSync() { auto &itemName = item.key(); auto &itemData = item.value(); auto sceneItem = ctx->items[itemName]; + sceneItem->name = itemName; - ctx->data = itemData; - sceneItem->load(ctx); - - if(itemData.contains("components")) { - for(auto &cmpItem : itemData["components"].items()) { - auto &cmpName = cmpItem.key(); - auto &cmpData = cmpItem.value(); - assertTrue( - cmpData.contains("type"), - "Component missing type in %s", - cmpName.c_str() - ); - - auto cmpType = cmpData["type"].get(); - auto cmp = SceneComponentRegistry::createComponent( - cmpType, sceneItem - ); - ctx->data = cmpData; - ctx->components[cmpName] = cmp; - cmp->load(ctx); - } + std::shared_ptr itemCtx; + if(itemData.contains("prefab")) { + auto prefabLoader = ctx->getAsset( + itemData["prefab"].get() + ); + assertNotNull(prefabLoader, "Prefab loader not found"); + assertTrue(prefabLoader->loaded, "Prefab loader not loaded"); + assertNotNull( + prefabLoader->jsonLoader, + "Prefab JSON loader not found" + ); + assertTrue( + prefabLoader->jsonLoader->loaded, + "Prefab JSON loader not loaded" + ); + itemCtx = prefabLoader->ctx; + itemCtx->data = json(prefabLoader->jsonLoader->data); + itemCtx->parent = this->ctx; + JSON::mergeObjectsRecursive(itemCtx->data, itemData); + } else { + itemCtx = this->ctx; + itemCtx->data = json(itemData); } + + sceneItem->load(itemCtx); } } diff --git a/src/dawn/scene/SceneItem.cpp b/src/dawn/scene/SceneItem.cpp index d3af012f..de211d14 100644 --- a/src/dawn/scene/SceneItem.cpp +++ b/src/dawn/scene/SceneItem.cpp @@ -8,6 +8,7 @@ #include "util/JSON.hpp" #include "assert/assert.hpp" #include "asset/loader/scene/PrefabLoader.hpp" +#include "component/SceneComponentRegistry.hpp" using namespace Dawn; @@ -77,13 +78,6 @@ void SceneItem::deinit() { } void SceneItem::load(std::shared_ptr ctx) { - // Load prefab first, it can be overriden by other properties. - if(ctx->data.contains("prefab")) { - this->name = ctx->data["prefab"].get(); - auto prefabLoader = ctx->getAsset(this->name); - prefabLoader->stagePrefab(shared_from_this()); - } - if(ctx->data.contains("name")) { this->name = ctx->data["name"].get(); } @@ -108,6 +102,39 @@ void SceneItem::load(std::shared_ptr ctx) { if(ctx->data.contains("scale")) { this->setLocalScale(JSON::vec3(ctx->data["scale"])); } + + if(ctx->data.contains("components")) { + assertTrue( + ctx->data["components"].is_object(), + "Components must be an object in %s", + this->name.c_str() + ); + + auto components = json(ctx->data["components"]); + for(auto &cmpItem : components.items()) { + auto &cmpName = cmpItem.key(); + auto &cmpData = cmpItem.value(); + assertTrue( + cmpData.is_object(), + "Component data must be an object in %s", + cmpName.c_str() + ); + assertTrue( + cmpData.contains("type"), + "Component missing type in %s", + cmpName.c_str() + ); + + auto cmpType = cmpData["type"].get(); + auto component = SceneComponentRegistry::createComponent( + cmpType, shared_from_this() + ); + assertNotNull(component, "Component %s not found", cmpType.c_str()); + ctx->data = json(cmpData); + ctx->components[cmpName] = component; + component->load(ctx); + } + } } void SceneItem::remove() { diff --git a/src/dawn/util/JSON.cpp b/src/dawn/util/JSON.cpp index 361515cb..38fa12ac 100644 --- a/src/dawn/util/JSON.cpp +++ b/src/dawn/util/JSON.cpp @@ -76,4 +76,27 @@ struct Color JSON::color(const json &j) { assertUnreachable("Invalid JSON type for color"); return COLOR_WHITE; +} + +void JSON::mergeObjectsRecursive(json &a, const json &b) { + assertTrue(a.type() == json::value_t::object, "a is not an object"); + assertTrue(b.type() == json::value_t::object, "b is not an object"); + + for(auto &item : b.items()) { + auto &key = item.key(); + auto &value = item.value(); + + if(a.contains(key)) { + if( + a[key].type() == json::value_t::object && + value.type() == json::value_t::object + ) { + mergeObjectsRecursive(a[key], value); + } else { + a[key] = json(value); + } + } else { + a[key] = json(value); + } + } } \ No newline at end of file diff --git a/src/dawn/util/JSON.hpp b/src/dawn/util/JSON.hpp index 1a591701..2d055fd4 100644 --- a/src/dawn/util/JSON.hpp +++ b/src/dawn/util/JSON.hpp @@ -25,5 +25,20 @@ namespace Dawn { * @return Parsed color. */ static struct Color color(const json &j); + + /** + * Merges all objects in the given JSON object into the first object, + * recursively. + * + * e.g. if; &a is { a: { b: 1, c: 2 }, d: 3 } + * &b is { a: { b: 4, d: 5 }, e: 4 } + * + * The output will become; + * { a: { b: 4, c: 2, d: 5 }, e: 4 } + * + * @param a JSON object to merge into. + * @param b JSON object to merge from. + */ + static void mergeObjectsRecursive(json &a, const json &b); }; } \ No newline at end of file