Component+Scene: Restructure scene .json to allow multiple components

This commit is contained in:
Riyyi
2024-01-01 13:10:20 +01:00
parent c607ebcc72
commit 0dd7a05d46
16 changed files with 385 additions and 103 deletions
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/format/print.h"
#include "ruc/json/json.h"
#include "inferno/component/cameracomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, CameraComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("type")) {
value.type = json.at("type").get<std::string>() == "orthographic" ? CameraType::Orthographic : CameraType::Perspective;
}
if (json.exists("zoom-level")) {
json.at("zoom-level").getTo(value.zoomLevel);
}
}
} // namespace Inferno
+3
View File
@@ -8,6 +8,7 @@
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/json/json.h"
namespace Inferno {
@@ -32,4 +33,6 @@ struct CameraComponent {
glm::mat4 projection { 1.0f }; // Identity matrix
};
void fromJson(const ruc::Json& json, CameraComponent& value);
} // namespace Inferno
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/id-component.h"
namespace Inferno {
void fromJson(const ruc::Json& json, IDComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("id")) {
json.at("id").getTo(value.id);
}
}
} // namespace Inferno
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "ruc/json/json.h"
#include "inferno/uid.h"
namespace Inferno {
struct IDComponent {
UID id;
IDComponent() = default;
IDComponent(UID id)
: id(id)
{
}
IDComponent(const IDComponent&) = default;
};
void fromJson(const ruc::Json& json, IDComponent& value);
} // namespace Inferno
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/luascriptcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, LuaScriptComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
VERIFY(json.exists("path"), "path not found");
json.at("path").getTo(value.path);
}
} // namespace Inferno
+6 -1
View File
@@ -6,9 +6,11 @@
#pragma once
#include <string> // std::string
#include <string>
#include <utility> // std::move
#include "ruc/json/json.h"
namespace Inferno {
class LuaScript;
@@ -24,4 +26,7 @@ struct LuaScriptComponent {
{
}
};
void fromJson(const ruc::Json& json, LuaScriptComponent& value);
} // namespace Inferno
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/nativescriptcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, NativeScriptComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
VERIFY(json.exists("name"), "name not found");
json.at("name").getTo(value.name);
}
} // namespace Inferno
+16 -3
View File
@@ -7,6 +7,7 @@
#pragma once
#include <string>
#include <utility> // std::move
#include "inferno/script/nativescript.h"
@@ -14,17 +15,29 @@ namespace Inferno {
struct NativeScriptComponent {
NativeScript* instance { nullptr };
std::string name;
NativeScript::InitializeFunction initialize { nullptr };
NativeScript::DestroyFunction destroy { nullptr };
// Dont allow manually setting instance during construction
NativeScriptComponent() {}
NativeScriptComponent(const std::string& binding)
NativeScriptComponent(const std::string& name)
: name(std::move(name))
{
initialize = NativeScriptBinding::the().initializeBinding(binding);
destroy = NativeScriptBinding::the().destroyBinding(binding);
bind();
}
void bind()
{
VERIFY(initialize == nullptr && destroy == nullptr, "NativeScript already bound");
VERIFY(name != "", "name not set");
initialize = NativeScriptBinding::the().initializeBinding(name);
destroy = NativeScriptBinding::the().destroyBinding(name);
}
};
void fromJson(const ruc::Json& json, NativeScriptComponent& value);
} // namespace Inferno
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/json/json.h"
#include "inferno/component/tagcomponent.h"
namespace Inferno {
void fromJson(const ruc::Json& json, TagComponent& value)
{
VERIFY(json.type() == ruc::Json::Type::Object);
if (json.exists("tag")) {
json.at("tag").getTo(value.tag);
}
}
} // namespace Inferno
+4
View File
@@ -9,6 +9,8 @@
#include <string> // std::string
#include <utility> // std::move
#include "ruc/json/json.h"
namespace Inferno {
struct TagComponent {
@@ -23,4 +25,6 @@ struct TagComponent {
operator const std::string&() const { return tag; }
};
void fromJson(const ruc::Json& json, TagComponent& value);
} // namespace Inferno
+65 -55
View File
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <limits> // std::numeric_limits
@@ -13,6 +14,7 @@
#include "ruc/meta/assert.h"
#include "inferno/component/cameracomponent.h"
#include "inferno/component/id-component.h"
#include "inferno/component/luascriptcomponent.h"
#include "inferno/component/nativescriptcomponent.h"
#include "inferno/component/spritecomponent.h"
@@ -26,6 +28,7 @@
#include "inferno/system/scriptsystem.h"
#include "inferno/system/textareasystem.h"
#include "inferno/system/transformsystem.h"
#include "inferno/uid.h"
namespace Inferno {
@@ -47,54 +50,66 @@ void Scene::initialize()
auto sceneJson = ruc::Json::parse(ruc::File("assets/scene/scene1.json").data());
// Camera
VERIFY(sceneJson.exists("camera"), "scene doesnt contain a camera");
auto& cameraJson = sceneJson.at("camera");
uint32_t camera = loadEntity(cameraJson);
auto& cameraComponent = addComponent<CameraComponent>(camera);
if (cameraJson.exists("type") && cameraJson.at("type").get<std::string>() == "orthographic") {
cameraComponent.type = CameraType::Orthographic;
}
if (cameraJson.exists("zoom-level") && cameraJson.at("zoom-level").type() == ruc::Json::Type::Number) {
cameraComponent.zoomLevel = cameraJson.at("zoom-level").asDouble();
if (sceneJson.exists("init")) {
// TODO: load either NativeScript or LuaScript?
}
if (cameraJson.exists("script")) {
auto& cameraScript = cameraJson.at("script");
if (cameraScript.exists("type") && cameraScript.exists("name")) {
auto name = cameraScript.at("name").get<std::string>();
if (cameraScript.at("type").get<std::string>() == "lua") {
addComponent<LuaScriptComponent>(camera, name);
// Entities
// -------------------------------------
if (sceneJson.exists("entities")) {
const auto& entityJson = sceneJson.at("entities");
VERIFY(entityJson.type() == ruc::Json::Type::Array);
const auto& entities = entityJson.asArray();
for (size_t i = 0; i < entities.size(); ++i) {
uint32_t entity = createEntity();
VERIFY(entities.at(i).type() == ruc::Json::Type::Object);
const auto& components = entities.at(i);
// ID is required
VERIFY(components.exists("id"), "id not found");
auto& id = getComponent<IDComponent>(entity);
components.at("id").getTo(id);
if (components.exists("tag")) {
auto& tag = getComponent<TagComponent>(entity);
components.at("tag").getTo(tag);
}
else {
addComponent<NativeScriptComponent>(camera, name);
if (components.exists("transform")) {
auto& transform = getComponent<TransformComponent>(entity);
components.at("transform").getTo(transform);
}
if (components.exists("camera")) {
auto& camera = addComponent<CameraComponent>(entity);
components.at("camera").getTo(camera);
}
if (components.exists("lua-scripts")) {
VERIFY(components.at("lua-scripts").type() == ruc::Json::Type::Array);
const auto& scripts = components.at("lua-scripts").asArray();
for (size_t j = 0; j < scripts.size(); ++j) {
auto& script = addComponent<LuaScriptComponent>(entity);
scripts.at(j).getTo(script);
}
}
if (components.exists("native-scripts")) {
VERIFY(components.at("native-scripts").type() == ruc::Json::Type::Array);
const auto& scripts = components.at("native-scripts").asArray();
for (size_t j = 0; j < scripts.size(); ++j) {
auto& script = addComponent<NativeScriptComponent>(entity);
scripts.at(j).getTo(script);
script.bind();
}
}
if (components.exists("sprite")) {
auto& sprite = addComponent<SpriteComponent>(entity);
components.at("sprite").getTo(sprite);
}
if (components.exists("text")) {
auto& text = addComponent<TextAreaComponent>(entity);
components.at("text").getTo(text);
}
}
}
// Quads
if (sceneJson.exists("quad") && sceneJson.at("quad").type() == ruc::Json::Type::Array) {
auto& quads = sceneJson.at("quad").asArray().elements();
for (const auto& quad : quads) {
uint32_t quadEntity = loadEntity(quad);
addComponent<SpriteComponent>(quadEntity);
auto& spriteComponent = getComponent<SpriteComponent>(quadEntity);
quad.getTo(spriteComponent);
}
}
// Text
if (sceneJson.exists("text") && sceneJson.at("text").type() == ruc::Json::Type::Array) {
auto& texts = sceneJson.at("text").asArray().elements();
for (const auto& text : texts) {
uint32_t textEntity = loadEntity(text);
addComponent<TextAreaComponent>(textEntity);
auto& textAreaComponent = getComponent<TextAreaComponent>(textEntity);
text.getTo(textAreaComponent);
}
}
@@ -125,20 +140,15 @@ void Scene::destroy()
uint32_t Scene::createEntity(const std::string& name)
{
uint32_t entity = static_cast<uint32_t>(m_registry->create());
addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name);
addComponent<TransformComponent>(entity);
return entity;
return createEntityWithUID(UID(), name);
}
uint32_t Scene::loadEntity(ruc::Json json)
uint32_t Scene::createEntityWithUID(UID id, const std::string& name)
{
uint32_t entity = createEntity((json.exists("name"))
? json.at("name").get<std::string>()
: "");
auto& transform = getComponent<TransformComponent>(entity);
json.getTo(transform);
uint32_t entity = static_cast<uint32_t>(m_registry->create());
addComponent<IDComponent>(entity, id);
addComponent<TagComponent>(entity, name.empty() ? "Unnamed Entity" : name);
addComponent<TransformComponent>(entity);
return entity;
}
+7 -3
View File
@@ -6,6 +6,7 @@
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <memory> // std::shared_ptr
@@ -14,6 +15,8 @@
#include "ruc/format/format.h"
#include "ruc/json/json.h"
#include "inferno/uid.h"
namespace Inferno {
class Camera;
@@ -27,7 +30,7 @@ public:
void destroy();
uint32_t createEntity(const std::string& name = "");
uint32_t loadEntity(ruc::Json json);
uint32_t createEntityWithUID(UID id, const std::string& name = "");
uint32_t findEntity(std::string_view name);
void destroyEntity(uint32_t entity);
@@ -49,7 +52,8 @@ public:
return m_registry->any<T...>(entt::entity { entity });
}
// @Todo Should replace be allowed? could trigger memory leaks with nativescript
// TODO: Should replace be allowed? could trigger memory leaks with nativescript
// TODO: Replace will make it so an entity cant have multiple scripts
template<typename T, typename... P>
T& addComponent(uint32_t entity, P&&... parameters) const
{
@@ -64,7 +68,7 @@ public:
return m_registry->remove_if_exists<T>(entt::entity { entity });
}
// @Todo Should replace be allowed? could trigger memory leaks with nativescript
// TODO: Should replace be allowed? could trigger memory leaks with nativescript
template<typename T, typename... P>
T& getComponent(uint32_t entity, P&&... parameters) const
{
+2 -2
View File
@@ -55,8 +55,8 @@ public:
m_detroyBindings.emplace(binding, destroy);
}
NativeScript::InitializeFunction initializeBinding(const std::string& binding) { return m_initializeBindings[binding]; }
NativeScript::DestroyFunction destroyBinding(const std::string& binding) { return m_detroyBindings[binding]; }
NativeScript::InitializeFunction initializeBinding(const std::string& name) { return m_initializeBindings[name]; }
NativeScript::DestroyFunction destroyBinding(const std::string& name) { return m_detroyBindings[name]; }
private:
std::unordered_map<std::string, NativeScript::InitializeFunction> m_initializeBindings;
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint>
#include <random>
#include "ruc/format/log.h"
#include "ruc/json/json.h"
#include "inferno/uid.h"
namespace Inferno {
static std::random_device s_seed;
static std::mt19937_64 s_engine(s_seed());
static std::uniform_int_distribution<uint64_t> s_distribution;
UID::UID()
: m_uid(s_distribution(s_engine))
{
}
UID::UID(uint64_t uid)
: m_uid(uid)
{
}
void fromJson(const ruc::Json& json, UID& value)
{
VERIFY(json.type() == ruc::Json::Type::Number);
value = UID((int64_t)json.asDouble());
}
} // namespace Inferno
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // uint64_t
#include "ruc/json/json.h"
namespace Inferno {
class UID {
public:
UID();
UID(uint64_t uid);
// Comparison operator for std::map
bool operator<(const UID& other) const
{
return m_uid < other.m_uid;
}
operator uint64_t() const { return m_uid; }
private:
uint64_t m_uid;
};
void fromJson(const ruc::Json& json, UID& value);
} // namespace Inferno
namespace std {
// Hash function for std::unordered_map
template<>
struct hash<Inferno::UID> {
size_t operator()(const Inferno::UID& uid) const
{
return hash<uint64_t>()(static_cast<uint64_t>(uid));
}
};
} // namespace std