diff --git a/assets/scene/scene1.json b/assets/scene/scene1.json index 1f05ad9..2b57ba1 100644 --- a/assets/scene/scene1.json +++ b/assets/scene/scene1.json @@ -1,50 +1,68 @@ { "init": "assets/lua/scene1-init.lua", - "camera": { - "name": "Camera", - "translate": [ 0.0, 0.0, 1.0 ], - "rotate": [ 0.0, 0.0, -1.0 ], - "scale": [ 1.0, 1.0, 1.0 ], - "type": "perspective", - "script": { - "type": "lua", - "name": "assets/lua/cameracontroller.lua" - } - }, - "quad": [ + "entities": [ { - "name": "Quad", - "translate": [ 0.0, 0.0, 0.0 ], - "rotate": [ 0.0, 0.0, 0.0 ], - "scale": [ 1.0, 1.0, 1.0 ], - "color": [ 1.0, 1.0, 1.0, 1.0 ], - "texture": "assets/gfx/test.png" + "id": { "id": 12312312 }, + "tag": { "tag": "Camera" }, + "transform" : { + "translate": [0.0, 0.0, 1.0], + "rotate": [0.0, 0.0, -1.0], + "scale": [1.0, 1.0, 1.0] + }, + "camera": { "type": "perspective" }, + "lua-scripts": [ + { "path": "assets/lua/cameracontroller.lua" } + ] }, { - "name": "Quad 2", - "translate": [ 1.1, 0.0, 0.0 ], - "rotate": [ 0.0, 0.0, 0.0 ], - "scale": [ 1.0, 1.0, 1.0 ], - "color": [ 0.5, 0.6, 0.8, 1.0 ], - "texture": "assets/gfx/test.png" + "id": { "id": 564564564 }, + "tag": { "tag": "Quad" }, + "transform" : { + "translate": [ 0.0, 0.0, 0.0 ], + "rotate": [ 0.0, 0.0, 0.0 ], + "scale": [ 1.0, 1.0, 1.0 ] + }, + "sprite": { + "color": [ 1.0, 1.0, 1.0, 1.0 ], + "texture": "assets/gfx/test.png" + } }, { - "name": "Quad 3", - "translate": [ 2.2, 0.0, 0.0 ], - "rotate": [ 0.0, 0.0, 0.0 ], - "scale": [ 1.0, 1.0, 1.0 ], - "color": [ 1.0, 1.0, 1.0, 1.0 ], - "texture": "assets/gfx/test-inverted.png" - } - ], - "text": [ + "id": { "id": 97897897 }, + "tag": { "tag": "Quad 2" }, + "transform" : { + "translate": [ 1.1, 0.0, 0.0 ], + "rotate": [ 0.0, 0.0, 0.0 ], + "scale": [ 1.0, 1.0, 1.0 ] + }, + "sprite": { + "color": [ 0.5, 0.6, 0.8, 1.0 ], + "texture": "assets/gfx/test.png" + } + }, + { + "id": { "id": 3424242 }, + "tag": { "tag": "Quad 3" }, + "transform" : { + "translate": [ 2.2, 0.0, 0.0 ], + "rotate": [ 0.0, 0.0, 0.0 ], + "scale": [ 1.0, 1.0, 1.0 ] + }, + "sprite": { + "color": [ 1.0, 1.0, 1.0, 1.0 ], + "texture": "assets/gfx/test-inverted.png" + } + }, { - "name": "Text", - "content": "Hello World!", - "font": "assets/fnt/open-sans", - "font-size": 24, - "line-spacing": 1.0, - "width": 150 + "id": { "id": 675754 }, + "tag": { "tag": "Text" }, + "text": { + "content": "Hello World!", + "font": "assets/fnt/open-sans", + "font-size": 24, + "line-spacing": 1.0, + "width": 150 + } } ] } diff --git a/src/inferno/component/cameracomponent.cpp b/src/inferno/component/cameracomponent.cpp new file mode 100644 index 0000000..fe694c6 --- /dev/null +++ b/src/inferno/component/cameracomponent.cpp @@ -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() == "orthographic" ? CameraType::Orthographic : CameraType::Perspective; + } + if (json.exists("zoom-level")) { + json.at("zoom-level").getTo(value.zoomLevel); + } +} + +} // namespace Inferno diff --git a/src/inferno/component/cameracomponent.h b/src/inferno/component/cameracomponent.h index 1c56bd0..d34163c 100644 --- a/src/inferno/component/cameracomponent.h +++ b/src/inferno/component/cameracomponent.h @@ -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 diff --git a/src/inferno/component/id-component.cpp b/src/inferno/component/id-component.cpp new file mode 100644 index 0000000..7b8220c --- /dev/null +++ b/src/inferno/component/id-component.cpp @@ -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 diff --git a/src/inferno/component/id-component.h b/src/inferno/component/id-component.h new file mode 100644 index 0000000..0231747 --- /dev/null +++ b/src/inferno/component/id-component.h @@ -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 diff --git a/src/inferno/component/luascriptcomponent.cpp b/src/inferno/component/luascriptcomponent.cpp new file mode 100644 index 0000000..5ff4fc5 --- /dev/null +++ b/src/inferno/component/luascriptcomponent.cpp @@ -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 diff --git a/src/inferno/component/luascriptcomponent.h b/src/inferno/component/luascriptcomponent.h index c0b4ff3..c50f96f 100644 --- a/src/inferno/component/luascriptcomponent.h +++ b/src/inferno/component/luascriptcomponent.h @@ -6,9 +6,11 @@ #pragma once -#include // std::string +#include #include // 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 diff --git a/src/inferno/component/nativescriptcomponent.cpp b/src/inferno/component/nativescriptcomponent.cpp new file mode 100644 index 0000000..9d82438 --- /dev/null +++ b/src/inferno/component/nativescriptcomponent.cpp @@ -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 diff --git a/src/inferno/component/nativescriptcomponent.h b/src/inferno/component/nativescriptcomponent.h index 47becb9..1b7977e 100644 --- a/src/inferno/component/nativescriptcomponent.h +++ b/src/inferno/component/nativescriptcomponent.h @@ -7,6 +7,7 @@ #pragma once #include +#include // 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 diff --git a/src/inferno/component/tagcomponent.cpp b/src/inferno/component/tagcomponent.cpp new file mode 100644 index 0000000..d4a5a00 --- /dev/null +++ b/src/inferno/component/tagcomponent.cpp @@ -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 diff --git a/src/inferno/component/tagcomponent.h b/src/inferno/component/tagcomponent.h index 4deef69..e502096 100644 --- a/src/inferno/component/tagcomponent.h +++ b/src/inferno/component/tagcomponent.h @@ -9,6 +9,8 @@ #include // std::string #include // 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 diff --git a/src/inferno/scene/scene.cpp b/src/inferno/scene/scene.cpp index 559f326..5e1c80a 100644 --- a/src/inferno/scene/scene.cpp +++ b/src/inferno/scene/scene.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: MIT */ +#include // size_t #include // uint32_t #include // 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(camera); - if (cameraJson.exists("type") && cameraJson.at("type").get() == "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(); - if (cameraScript.at("type").get() == "lua") { - addComponent(camera, name); - } - else { - addComponent(camera, name); - } - } - } + // Entities + // ------------------------------------- - // Quads + 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) { - 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(quadEntity); - auto& spriteComponent = getComponent(quadEntity); - quad.getTo(spriteComponent); - } - } + uint32_t entity = createEntity(); - // Text + VERIFY(entities.at(i).type() == ruc::Json::Type::Object); + const auto& components = entities.at(i); - 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(textEntity); - auto& textAreaComponent = getComponent(textEntity); - text.getTo(textAreaComponent); + // ID is required + VERIFY(components.exists("id"), "id not found"); + auto& id = getComponent(entity); + components.at("id").getTo(id); + + if (components.exists("tag")) { + auto& tag = getComponent(entity); + components.at("tag").getTo(tag); + } + if (components.exists("transform")) { + auto& transform = getComponent(entity); + components.at("transform").getTo(transform); + } + if (components.exists("camera")) { + auto& camera = addComponent(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(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(entity); + scripts.at(j).getTo(script); + script.bind(); + } + } + if (components.exists("sprite")) { + auto& sprite = addComponent(entity); + components.at("sprite").getTo(sprite); + } + if (components.exists("text")) { + auto& text = addComponent(entity); + components.at("text").getTo(text); + } } } @@ -125,20 +140,15 @@ void Scene::destroy() uint32_t Scene::createEntity(const std::string& name) { - uint32_t entity = static_cast(m_registry->create()); - addComponent(entity, name.empty() ? "Unnamed Entity" : name); - addComponent(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() - : ""); - auto& transform = getComponent(entity); - json.getTo(transform); + uint32_t entity = static_cast(m_registry->create()); + addComponent(entity, id); + addComponent(entity, name.empty() ? "Unnamed Entity" : name); + addComponent(entity); return entity; } diff --git a/src/inferno/scene/scene.h b/src/inferno/scene/scene.h index ba34b15..b1347f7 100644 --- a/src/inferno/scene/scene.h +++ b/src/inferno/scene/scene.h @@ -6,6 +6,7 @@ #pragma once +#include // size_t #include // uint32_t #include // 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(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 T& addComponent(uint32_t entity, P&&... parameters) const { @@ -64,7 +68,7 @@ public: return m_registry->remove_if_exists(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 T& getComponent(uint32_t entity, P&&... parameters) const { diff --git a/src/inferno/script/nativescript.h b/src/inferno/script/nativescript.h index ec41a22..d15112a 100644 --- a/src/inferno/script/nativescript.h +++ b/src/inferno/script/nativescript.h @@ -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 m_initializeBindings; diff --git a/src/inferno/uid.cpp b/src/inferno/uid.cpp new file mode 100644 index 0000000..d641ff5 --- /dev/null +++ b/src/inferno/uid.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#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 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 diff --git a/src/inferno/uid.h b/src/inferno/uid.h new file mode 100644 index 0000000..c181312 --- /dev/null +++ b/src/inferno/uid.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include // 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 { + size_t operator()(const Inferno::UID& uid) const + { + return hash()(static_cast(uid)); + } +}; + +} // namespace std