Scene context improving but not finished.

This commit is contained in:
2024-12-05 15:48:27 -06:00
parent 5998037994
commit a6ac4f029e
25 changed files with 206 additions and 163 deletions

View File

@ -2,7 +2,7 @@
"name": "Rosa",
"assets": {
"rosa": {
"rosa_texture": {
"type": "texture",
"path": "rosa.texture"
}
@ -11,7 +11,7 @@
"components": {
"material": {
"type": "SimpleTexturedMaterial",
"texture": "rosa"
"texture": "rosa_texture"
},
"meshRenderer": {
"type": "MeshRenderer"
@ -20,8 +20,7 @@
"type": "RPGEntity"
},
"player": {
"type": "RPGPlayer",
"camera": "camera"
"type": "RPGPlayer"
}
}
}

View File

@ -20,11 +20,6 @@
"rosa": {
"prefab": "rosa",
"position": [ 0, 0, 0 ]
},
"rosa2": {
"prefab": "rosa",
"position": [ 2, 0, 0 ]
}
}
}

View File

@ -9,7 +9,7 @@ target_sources(${DAWN_TARGET_NAME}
TextureLoader.cpp
JSONLoader.cpp
TrueTypeLoader.cpp
SceneLoader.cpp
LoaderForSceneItems.cpp
PrefabLoader.cpp
)
)
# Subdirs
add_subdirectory(scene)

View File

@ -0,0 +1,12 @@
# Copyright (c) 2025 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
SceneLoader.cpp
LoaderForSceneItems.cpp
PrefabLoader.cpp
)

View File

@ -5,17 +5,24 @@
#include "LoaderForSceneItems.hpp"
#include "asset/loader/TextureLoader.hpp"
#include "asset/loader/PrefabLoader.hpp"
#include "asset/loader/scene/PrefabLoader.hpp"
using namespace Dawn;
LoaderForSceneItems::LoaderForSceneItems(
const std::shared_ptr<AssetManager> assetManager,
const std::string name
) : AssetLoader(assetManager, name) {
) :
AssetLoader(assetManager, name),
ctx(std::make_shared<SceneLoadContext>())
{
}
void LoaderForSceneItems::setupDependencies() {
assertNotNull(this->jsonLoader, "JSON Loader is NULL?");
assertNotNull(ctx, "SceneLoadContext is NULL?");
assertTrue(this->jsonLoader->loaded, "JSON loader not loaded?");
// Begin loading dependencies.
auto &data = this->jsonLoader->data;
if(data.contains("assets")) {
@ -27,6 +34,18 @@ void LoaderForSceneItems::setupDependencies() {
auto type = assetData["type"].get<std::string>();
auto path = assetData["path"].get<std::string>();
// Is this asset already named?
if(ctx->assets.find(assetName) != ctx->assets.end()) {
// Check if path and type already the same.
auto &existing = ctx->assets[assetName];
assertTrue(
existing->name == path && existing->getAssetType() == type,
"Asset already exists with different path or type: %s",
assetName.c_str()
);
continue;
}
std::shared_ptr<AssetLoader> loader;
if(type == "texture") {
loader = getAssetManager()->get<TextureLoader>(path);
@ -36,24 +55,18 @@ void LoaderForSceneItems::setupDependencies() {
} else if(type == "prefab") {
auto prefabLoader = getAssetManager()->get<PrefabLoader>(path);
prefabLoader->parentLoader = weak_from_this();
prefabLoader->ctx->parent = ctx;
loader = prefabLoader;
} else {
assertUnreachable("Unknown asset type: %s", type.c_str());
}
assertMapNotHasKey(
ctx.assets,
assetName,
"Asset already exists: %s", assetName.c_str()
);
ctx.assets[assetName] = loader;
ctx->assets[assetName] = loader;
}
}
}
LoaderForSceneItems::~LoaderForSceneItems() {
jsonLoader = nullptr;
}

View File

@ -4,8 +4,8 @@
// https://opensource.org/licenses/MIT
#pragma once
#include "JSONLoader.hpp"
#include "scene/Scene.hpp"
#include "asset/loader/JSONLoader.hpp"
#include "SceneLoadContext.hpp"
namespace Dawn {
class LoaderForSceneitems;
@ -24,7 +24,7 @@ namespace Dawn {
void setupDependencies();
public:
struct SceneComponentLoadContext ctx;
const std::shared_ptr<SceneLoadContext> ctx;
LoaderForSceneItems(
const std::shared_ptr<AssetManager> assetManager,

View File

@ -42,7 +42,7 @@ void PrefabLoader::updateSync() {
assertTrue(this->jsonLoader->loaded, "JSON loader not loaded?");
// Check if all dependencies are loaded.
for(auto &asset : ctx.assets) {
for(auto &asset : ctx->assets) {
if(!asset.second->loaded) return;
}
@ -65,14 +65,10 @@ std::string PrefabLoader::getAssetType() const {
void PrefabLoader::stagePrefab(std::shared_ptr<SceneItem> item) {
assertTrue(this->loaded, "Prefab not loaded");
// Can we merge with the parent context?
auto parentLoaderLock = this->parentLoader.lock();
if(parentLoaderLock) this->ctx.merge(parentLoaderLock->ctx);
// Force-set new context values
ctx.scene = item->getScene();
ctx.data = this->jsonLoader->data;
ctx->data = this->jsonLoader->data;
// Load the scene item.
item->load(ctx);
auto &itemData = this->jsonLoader->data;
@ -90,8 +86,8 @@ void PrefabLoader::stagePrefab(std::shared_ptr<SceneItem> item) {
auto cmp = SceneComponentRegistry::createComponent(
cmpType, item
);
ctx.data = cmpData;
ctx.components[cmpName] = cmp;
ctx->data = cmpData;
ctx->components[cmpName] = cmp;
cmp->load(ctx);
}
}

View File

@ -24,7 +24,6 @@ namespace Dawn {
public:
const static std::string ASSET_TYPE;
std::weak_ptr<LoaderForSceneItems> parentLoader;
PrefabLoader(
const std::shared_ptr<AssetManager> assetManager,

View File

@ -0,0 +1,6 @@
// Copyright (c) 2024 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SceneLoadContext.hpp"

View File

@ -0,0 +1,51 @@
// Copyright (c) 2024 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawn.hpp"
#include "assert/assert.hpp"
namespace Dawn {
class Scene;
class SceneItem;
class SceneComponent;
class AssetLoader;
class SceneLoadContext;
class SceneLoadContext {
public:
std::weak_ptr<SceneLoadContext> parent;
std::unordered_map<std::string, std::shared_ptr<SceneItem>> items;
std::unordered_map<std::string, std::shared_ptr<SceneComponent>> components;
std::unordered_map<std::string, std::shared_ptr<AssetLoader>> assets;
json data;
std::shared_ptr<Scene> currentScene;
std::shared_ptr<SceneItem> currentItem;
std::shared_ptr<SceneComponent> currentComponent;
/**
* Gets an asset from the context.
*
* @param j Name of the asset to get.
* @return Asset from the context.
*/
template<class T>
std::shared_ptr<T> getAsset(const std::string &j) const {
auto it = assets.find(j);
if(it == assets.end()) {
auto parent = this->parent.lock();
assertNotNull(parent, "Couldn't find asset.");
return parent->getAsset<T>(j);
}
auto asset = std::dynamic_pointer_cast<T>(it->second);
assertNotNull(asset, "Asset is not of the correct type.");
return asset;
}
};
}

View File

@ -33,6 +33,7 @@ void SceneLoader::updateAsync() {
case SceneLoaderState::LOADING_JSON:
assertNotNull(this->jsonLoader, "JSON Loader is NULL?");
if(!this->jsonLoader->loaded) return;
this->ctx->data = this->jsonLoader->data;
this->setupDependencies();
this->state = SceneLoaderState::LOADING_DEPENDENCIES;
break;
@ -40,10 +41,12 @@ void SceneLoader::updateAsync() {
case SceneLoaderState::LOADING_DEPENDENCIES:
assertTrue(this->jsonLoader->loaded, "JSON loader not loaded?");
// Check if all dependencies are loaded.
for(auto &asset : ctx.assets) {
for(auto &asset : ctx->assets) {
if(!asset.second->loaded) return;
}
ctx.scene = std::make_shared<Scene>(this->getAssetManager()->getGame());
ctx->currentScene = std::make_shared<Scene>(
this->getAssetManager()->getGame()
);
this->state = SceneLoaderState::PENDING_STAGE;
break;
@ -54,6 +57,8 @@ void SceneLoader::updateAsync() {
void SceneLoader::updateSync() {
if(this->state != SceneLoaderState::PENDING_STAGE) return;
assertNotNull(this->jsonLoader, "JSON Loader is NULL?");
assertNotNull(ctx, "SceneLoadContext is NULL?");
assertTrue(this->jsonLoader->loaded, "JSON loader not loaded?");
auto &data = this->jsonLoader->data;
@ -62,17 +67,17 @@ void SceneLoader::updateSync() {
for(auto &item : data["items"].items()) {
auto &itemName = item.key();
auto &itemData = item.value();
auto sceneItem = ctx.scene->createSceneItem();
ctx.items[itemName] = sceneItem;
auto sceneItem = ctx->currentScene->createSceneItem();
ctx->items[itemName] = sceneItem;
}
// Add components to each scene item
for(auto &item : data["items"].items()) {
auto &itemName = item.key();
auto &itemData = item.value();
auto sceneItem = ctx.items[itemName];
auto sceneItem = ctx->items[itemName];
ctx.data = itemData;
ctx->data = itemData;
sceneItem->load(ctx);
if(itemData.contains("components")) {
@ -89,8 +94,8 @@ void SceneLoader::updateSync() {
auto cmp = SceneComponentRegistry::createComponent(
cmpType, sceneItem
);
ctx.data = cmpData;
ctx.components[cmpName] = cmp;
ctx->data = cmpData;
ctx->components[cmpName] = cmp;
cmp->load(ctx);
}
}
@ -100,11 +105,6 @@ void SceneLoader::updateSync() {
this->jsonLoader = nullptr;
this->state = SceneLoaderState::DONE;
this->loaded = true;
ctx.assets.clear();
ctx.components.clear();
ctx.items.clear();
ctx.data = {};
}
std::string SceneLoader::getAssetType() const {
@ -112,10 +112,10 @@ std::string SceneLoader::getAssetType() const {
}
std::shared_ptr<Scene> SceneLoader::getScene() {
return ctx.scene;
assertNotNull(ctx, "Context is NULL?");
assertNotNull(ctx->currentScene, "Scene not loaded?");
return ctx->currentScene;
}
SceneLoader::~SceneLoader() {
ctx = {};
jsonLoader = nullptr;
}

View File

@ -21,65 +21,65 @@ void Camera::onDispose() {
renderTarget = nullptr;
}
void Camera::load(SceneComponentLoadContext &ctx) {
void Camera::load(std::shared_ptr<SceneLoadContext> ctx) {
SceneComponent::load(ctx);
if(ctx.data.contains("fov")) {
this->fov = Math::deg2rad(ctx.data["fov"].get<float_t>());
if(ctx->data.contains("fov")) {
this->fov = Math::deg2rad(ctx->data["fov"].get<float_t>());
}
if(ctx.data.contains("cameraType")) {
if(ctx->data.contains("cameraType")) {
if(
ctx.data["cameraType"] == "orthogonal" ||
ctx.data["cameraType"] == "orthographic" ||
ctx.data["cameraType"] == "ortho"
ctx->data["cameraType"] == "orthogonal" ||
ctx->data["cameraType"] == "orthographic" ||
ctx->data["cameraType"] == "ortho"
) {
this->type = CameraType::ORTHOGONAL;
} else if(ctx.data["cameraType"] == "perspective") {
} else if(ctx->data["cameraType"] == "perspective") {
this->type = CameraType::PERSPECTIVE;
} else {
assertUnreachable("Invalid Camera Type!");
}
}
if(ctx.data.contains("orthoLeft")) {
this->orthoLeft = ctx.data["orthoLeft"].get<float_t>();
} else if(ctx.data.contains("left")) {
this->orthoLeft = ctx.data["left"].get<float_t>();
if(ctx->data.contains("orthoLeft")) {
this->orthoLeft = ctx->data["orthoLeft"].get<float_t>();
} else if(ctx->data.contains("left")) {
this->orthoLeft = ctx->data["left"].get<float_t>();
}
if(ctx.data.contains("orthoRight")) {
this->orthoRight = ctx.data["orthoRight"].get<float_t>();
} else if(ctx.data.contains("right")) {
this->orthoRight = ctx.data["right"].get<float_t>();
if(ctx->data.contains("orthoRight")) {
this->orthoRight = ctx->data["orthoRight"].get<float_t>();
} else if(ctx->data.contains("right")) {
this->orthoRight = ctx->data["right"].get<float_t>();
}
if(ctx.data.contains("orthoBottom")) {
this->orthoBottom = ctx.data["orthoBottom"].get<float_t>();
} else if(ctx.data.contains("bottom")) {
this->orthoBottom = ctx.data["bottom"].get<float_t>();
if(ctx->data.contains("orthoBottom")) {
this->orthoBottom = ctx->data["orthoBottom"].get<float_t>();
} else if(ctx->data.contains("bottom")) {
this->orthoBottom = ctx->data["bottom"].get<float_t>();
}
if(ctx.data.contains("orthoTop")) {
this->orthoTop = ctx.data["orthoTop"].get<float_t>();
} else if(ctx.data.contains("top")) {
this->orthoTop = ctx.data["top"].get<float_t>();
if(ctx->data.contains("orthoTop")) {
this->orthoTop = ctx->data["orthoTop"].get<float_t>();
} else if(ctx->data.contains("top")) {
this->orthoTop = ctx->data["top"].get<float_t>();
}
if(ctx.data.contains("clipNear")) {
this->clipNear = ctx.data["clipNear"].get<float_t>();
} else if(ctx.data.contains("near")) {
this->clipNear = ctx.data["near"].get<float_t>();
} else if(ctx.data.contains("zNear")) {
this->clipNear = ctx.data["zNear"].get<float_t>();
if(ctx->data.contains("clipNear")) {
this->clipNear = ctx->data["clipNear"].get<float_t>();
} else if(ctx->data.contains("near")) {
this->clipNear = ctx->data["near"].get<float_t>();
} else if(ctx->data.contains("zNear")) {
this->clipNear = ctx->data["zNear"].get<float_t>();
}
if(ctx.data.contains("clipFar")) {
this->clipFar = ctx.data["clipFar"].get<float_t>();
} else if(ctx.data.contains("far")) {
this->clipFar = ctx.data["far"].get<float_t>();
} else if(ctx.data.contains("zFar")) {
this->clipFar = ctx.data["zFar"].get<float_t>();
if(ctx->data.contains("clipFar")) {
this->clipFar = ctx->data["clipFar"].get<float_t>();
} else if(ctx->data.contains("far")) {
this->clipFar = ctx->data["far"].get<float_t>();
} else if(ctx->data.contains("zFar")) {
this->clipFar = ctx->data["zFar"].get<float_t>();
}
}

View File

@ -33,7 +33,7 @@ namespace Dawn {
void onInit() override;
void onDispose() override;
void load(SceneComponentLoadContext &ctx) override;
void load(std::shared_ptr<SceneLoadContext> ctx) override;
/**
* Returns the aspect ratio that the camera is using. In future I may

View File

@ -9,14 +9,14 @@
using namespace Dawn;
void SimpleTexturedMaterial::load(SceneComponentLoadContext &ctx) {
if(ctx.data.contains("color")) {
this->setColor(JSON::color(ctx.data["color"]));
void SimpleTexturedMaterial::load(std::shared_ptr<SceneLoadContext> ctx) {
if(ctx->data.contains("color")) {
this->setColor(JSON::color(ctx->data["color"]));
}
if(ctx.data.contains("texture")) {
auto asset = ctx.getAsset<TextureLoader>(
ctx.data["texture"].get<std::string>()
if(ctx->data.contains("texture")) {
auto asset = ctx->getAsset<TextureLoader>(
ctx->data["texture"].get<std::string>()
);
this->setTexture(asset->getTexture());
}

View File

@ -15,7 +15,7 @@ namespace Dawn {
std::shared_ptr<Texture> texture;
public:
void load(SceneComponentLoadContext &ctx) override;
void load(std::shared_ptr<SceneLoadContext> ctx) override;
/**
* Returns the color of this material.

View File

@ -10,10 +10,7 @@
using namespace Dawn;
void CubeMeshComponent::onInit() {
if(!mesh) {
mesh = std::make_shared<Mesh>();
}
if(!mesh) mesh = std::make_shared<Mesh>();
mesh->createBuffers(CUBE_VERTICE_COUNT, CUBE_INDICE_COUNT);
CubeMesh::buffer(
mesh, glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(1.0f, 1.0f, 1.0f), 0, 0
@ -27,7 +24,7 @@ void CubeMeshComponent::onDispose() {
mesh = nullptr;
}
void CubeMeshComponent::load(SceneComponentLoadContext &ctx) {
void CubeMeshComponent::load(std::shared_ptr<SceneLoadContext> ctx) {
SceneComponent::load(ctx);
if(!mesh) mesh = std::make_shared<Mesh>();
}

View File

@ -14,6 +14,6 @@ namespace Dawn {
void onInit() override;
void onDispose() override;
void load(SceneComponentLoadContext &ctx) override;
void load(std::shared_ptr<SceneLoadContext> ctx) override;
};
}

View File

@ -6,7 +6,7 @@
#include "game/Game.hpp"
#include "scene/Scene.hpp"
#include "util/Flag.hpp"
#include "asset/loader/SceneLoader.hpp"
#include "asset/loader/scene/SceneLoader.hpp"
#include "component/SceneComponentRegistry.hpp"
// Components to register

View File

@ -59,8 +59,11 @@ std::shared_ptr<Game> SceneComponent::getGame() {
return this->getScene()->getGame();
}
void SceneComponent::load(SceneComponentLoadContext &context) {
void SceneComponent::load(std::shared_ptr<SceneLoadContext> context) {
// Override this method to load data from a JSON object.
if(context->data.contains("name")) {
this->name = context->data["name"].get<std::string>();
}
}
SceneComponent::~SceneComponent() {

View File

@ -4,15 +4,12 @@
// https://opensource.org/licenses/MIT
#pragma once
#include "dawn.hpp"
#include "assert/assert.hpp"
#include "asset/loader/scene/SceneLoadContext.hpp"
namespace Dawn {
class Game;
class Scene;
class SceneItem;
class SceneComponent;
class AssetLoader;
enum class SceneComponentState : flag_t {
INITIAL = FLAG(0),
@ -20,38 +17,6 @@ namespace Dawn {
DISPOSED = FLAG(2)
};
struct SceneComponentLoadContext {
json data;
std::shared_ptr<Scene> scene;
std::unordered_map<std::string, std::shared_ptr<SceneItem>> items;
std::unordered_map<std::string, std::shared_ptr<SceneComponent>> components;
std::unordered_map<std::string, std::shared_ptr<AssetLoader>> assets;
template<class T>
std::shared_ptr<T> getAsset(const std::string &j) const {
auto it = assets.find(j);
assertTrue(it != assets.end(), "Asset %s not found.", j.c_str());
auto asset = std::dynamic_pointer_cast<T>(it->second);
assertNotNull(asset, "Asset is not of the correct type.");
return asset;
}
void merge(const struct SceneComponentLoadContext &ctx) {
for(auto &item : ctx.items) {
if(items.find(item.first) != items.end()) continue;
this->items[item.first] = item.second;
}
for(auto &cmp : ctx.components) {
if(components.find(cmp.first) != components.end()) continue;
this->components[cmp.first] = cmp.second;
}
for(auto &asset : ctx.assets) {
if(assets.find(asset.first) != assets.end()) continue;
this->assets[asset.first] = asset.second;
}
}
};
class SceneComponent : std::enable_shared_from_this<SceneComponent> {
private:
std::weak_ptr<SceneItem> item;
@ -73,6 +38,8 @@ namespace Dawn {
virtual void onDispose() = 0;
public:
std::string name;
/**
* Initializes this scene component.
*
@ -119,7 +86,7 @@ namespace Dawn {
*
* @param json JSON Data that this object needs to load.
*/
virtual void load(SceneComponentLoadContext &context);
virtual void load(std::shared_ptr<SceneLoadContext> context);
/**
* Disposes this scene component.

View File

@ -7,7 +7,7 @@
#include "scene/Scene.hpp"
#include "util/JSON.hpp"
#include "assert/assert.hpp"
#include "asset/loader/PrefabLoader.hpp"
#include "asset/loader/scene/PrefabLoader.hpp"
using namespace Dawn;
@ -76,22 +76,25 @@ void SceneItem::deinit() {
this->components.clear();
}
void SceneItem::load(SceneComponentLoadContext &ctx) {
void SceneItem::load(std::shared_ptr<SceneLoadContext> ctx) {
// Load prefab first, it can be overriden by other properties.
if(ctx.data.contains("prefab")) {
auto prefabLoader = ctx.getAsset<PrefabLoader>(
ctx.data["prefab"].get<std::string>()
);
if(ctx->data.contains("prefab")) {
this->name = ctx->data["prefab"].get<std::string>();
auto prefabLoader = ctx->getAsset<PrefabLoader>(this->name);
prefabLoader->stagePrefab(shared_from_this());
}
// Transforms
if(ctx.data.contains("position")) {
this->setLocalPosition(JSON::vec3(ctx.data["position"]));
if(ctx->data.contains("name")) {
this->name = ctx->data["name"].get<std::string>();
}
if(ctx.data.contains("lookAt")) {
auto &lookAt = ctx.data["lookAt"];
// Transforms
if(ctx->data.contains("position")) {
this->setLocalPosition(JSON::vec3(ctx->data["position"]));
}
if(ctx->data.contains("lookAt")) {
auto &lookAt = ctx->data["lookAt"];
glm::vec3 pos = glm::vec3(3, 3, 3);
glm::vec3 look = glm::vec3(0, 0, 0);
glm::vec3 up = glm::vec3(0, 1, 0);
@ -102,8 +105,8 @@ void SceneItem::load(SceneComponentLoadContext &ctx) {
this->lookAt(pos, look, up);
}
if(ctx.data.contains("scale")) {
this->setLocalScale(JSON::vec3(ctx.data["scale"]));
if(ctx->data.contains("scale")) {
this->setLocalScale(JSON::vec3(ctx->data["scale"]));
}
}

View File

@ -29,6 +29,8 @@ namespace Dawn {
std::shared_ptr<SceneItem> sceneItemComponentsSelf() override;
public:
std::string name;
/**
* Constructs a scene item.
*
@ -55,7 +57,7 @@ namespace Dawn {
*
* @param ctx Context to load this scene item from.
*/
void load(SceneComponentLoadContext &ctx);
void load(std::shared_ptr<SceneLoadContext> ctx);
/**
* Returns the scene that this scene item belongs to.

View File

@ -71,12 +71,12 @@ void RPGPlayer::onDispose() {
this->rpgEntity = nullptr;
}
void RPGPlayer::load(SceneComponentLoadContext &ctx) {
void RPGPlayer::load(std::shared_ptr<SceneLoadContext> ctx) {
SceneComponent::load(ctx);
if(ctx.data.contains("camera")) {
assertMapHasKey(ctx.components, ctx.data["camera"], "Camera not found!");
const auto component = ctx.components[ctx.data["camera"].get<std::string>()];
if(ctx->data.contains("camera")) {
assertMapHasKey(ctx->components, ctx->data["camera"], "Camera not found!");
const auto component = ctx->components[ctx->data["camera"].get<std::string>()];
this->camera = std::dynamic_pointer_cast<Camera>(component);
assertNotNull(this->camera, "Camera not found!");
}

View File

@ -18,6 +18,6 @@ namespace Dawn {
void onInit() override;
void onDispose() override;
void load(SceneComponentLoadContext &ctx) override;
void load(std::shared_ptr<SceneLoadContext> ctx) override;
};
}