mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-18 00:49:19 +02:00
ECS System WIP
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ bin/
|
||||
#Visual Studio [Code] folders
|
||||
.vscode/
|
||||
.vs/
|
||||
.cache/
|
||||
|
||||
# JetBrains CLion folders
|
||||
cmake-build-*/
|
||||
|
||||
@@ -181,6 +181,7 @@ set(SOURCES
|
||||
"scripting/pythonscreenviewer.cpp"
|
||||
"utilities/dictionary.cpp"
|
||||
"rendering/particles.cpp"
|
||||
"registry/NameRegistry.cpp"
|
||||
"utilities/headtrack.cpp"
|
||||
"scripting/ladderlogic.cpp"
|
||||
"rendering/geometrybank.cpp"
|
||||
@@ -236,8 +237,6 @@ set(SOURCES
|
||||
|
||||
"vr/vr_interface.cpp"
|
||||
entitysystem/components/BasicComponents.h
|
||||
entitysystem/ecs.cpp
|
||||
entitysystem/ecs.h
|
||||
entitysystem/components/RenderComponents.h
|
||||
)
|
||||
|
||||
@@ -397,7 +396,21 @@ configure_file(${CMAKE_SOURCE_DIR}/eu07.rc.in
|
||||
|
||||
if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
|
||||
set(SOURCES ${SOURCES} ${CMAKE_BINARY_DIR}/eu07.rc)
|
||||
set(SOURCES ${SOURCES} eu07.ico)
|
||||
set(SOURCES ${SOURCES} eu07.ico
|
||||
entitysystem/ECScene.cpp
|
||||
entitysystem/ECScene.h
|
||||
entitysystem/ECWorld.cpp
|
||||
entitysystem/ECWorld.h
|
||||
entitysystem/scenes/GameScene.cpp
|
||||
entitysystem/scenes/GameScene.h
|
||||
entitysystem/SceneManager.cpp
|
||||
entitysystem/SceneManager.h
|
||||
entitysystem/systems/SystemManager.cpp
|
||||
entitysystem/systems/SystemManager.h
|
||||
entitysystem/systems/BaseSystem.cpp
|
||||
entitysystem/systems/BaseSystem.h
|
||||
entitysystem/systems/MovementSystem.cpp
|
||||
entitysystem/systems/MovementSystem.h)
|
||||
add_compile_options("/utf-8")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include <discord_rpc.h>
|
||||
#endif
|
||||
|
||||
#include "entitysystem/SceneManager.h"
|
||||
#include "entitysystem/scenes/GameScene.h"
|
||||
|
||||
#include <chrono>
|
||||
#include "utilities/translation.h"
|
||||
|
||||
@@ -452,6 +455,8 @@ bool eu07_application::is_client() const
|
||||
int eu07_application::run()
|
||||
{
|
||||
auto frame{0};
|
||||
|
||||
sceneManager.SetScene(std::make_unique<GameScene>());
|
||||
// main application loop
|
||||
while (!glfwWindowShouldClose(m_windows.front()) && !m_modestack.empty())
|
||||
{
|
||||
@@ -474,6 +479,9 @@ int eu07_application::run()
|
||||
|
||||
double frameStartTime = Timer::GetTime();
|
||||
|
||||
float dt = (float) Timer::GetDeltaTime();
|
||||
sceneManager.Update(dt);
|
||||
|
||||
if (m_modes[m_modestack.top()]->is_command_processor())
|
||||
{
|
||||
// active mode is doing real calculations (e.g. drivermode)
|
||||
|
||||
@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include "application/applicationmode.h"
|
||||
#include "entitysystem/SceneManager.h"
|
||||
#include "scripting/PyInt.h"
|
||||
#include "network/manager.h"
|
||||
#include "utilities/headtrack.h"
|
||||
@@ -38,6 +39,7 @@ public:
|
||||
run();
|
||||
// issues request for a worker thread to perform specified task. returns: true if task was scheduled
|
||||
|
||||
SceneManager sceneManager;
|
||||
#ifdef WITH_DISCORD_RPC
|
||||
void DiscordRPCService(); // discord rich presence service function (runs as separate thread)
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,9 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "utilities/translation.h"
|
||||
#include "application/application.h"
|
||||
#include "application/editormode.h"
|
||||
#include "entitysystem/ECScene.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
|
||||
#include "imgui/imgui_impl_glfw.h"
|
||||
|
||||
@@ -383,6 +386,10 @@ void ui_layer::render()
|
||||
render_menu();
|
||||
render_quit_widget();
|
||||
render_hierarchy();
|
||||
if (auto* scene = Application.sceneManager.CurrentScene())
|
||||
{
|
||||
render_entity_hierarchy(scene->World());
|
||||
}
|
||||
// template method implementation
|
||||
render_();
|
||||
|
||||
@@ -470,7 +477,171 @@ void ui_layer::render_hierarchy(){
|
||||
ImGui::End();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ui_layer::render_entity_hierarchy(ECWorld& world)
|
||||
{
|
||||
ImGui::SetNextWindowSize(ImVec2(900, 500), ImGuiCond_FirstUseEver);
|
||||
|
||||
if (!ImGui::Begin(STR_C("Entity Hierarchy"), &m_entity_hierarchy))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
const float left_panel_width = 280.0f;
|
||||
|
||||
ImGui::BeginChild("entity_list_panel", ImVec2(left_panel_width, 0), true);
|
||||
ImGui::Text("Registered entities: %zu", world.GetEntityCount());
|
||||
ImGui::Separator();
|
||||
|
||||
for (auto entity : world.GetEntities())
|
||||
{
|
||||
char buf[64];
|
||||
std::snprintf(buf, sizeof(buf), "Entity %u", static_cast<unsigned>(entity));
|
||||
|
||||
const bool selected = (m_selected_entity == entity);
|
||||
|
||||
if (ImGui::Selectable(buf, selected))
|
||||
{
|
||||
m_selected_entity = entity;
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetTooltip("Entity ID: %u", static_cast<unsigned>(entity));
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginChild("entity_inspector_panel", ImVec2(0, 0), true);
|
||||
|
||||
if (m_selected_entity == entt::null)
|
||||
{
|
||||
ImGui::TextDisabled("No entity selected");
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!world.IsAlive(m_selected_entity))
|
||||
{
|
||||
ImGui::TextDisabled("Selected entity is no longer valid");
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::Text("Selected entity: %u", static_cast<unsigned>(m_selected_entity));
|
||||
ImGui::Separator();
|
||||
|
||||
// Identification
|
||||
if (auto* id = world.GetComponent<ECSComponent::Identification>(m_selected_entity))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("Identification", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
char nameBuffer[256];
|
||||
std::snprintf(nameBuffer, sizeof(nameBuffer), "%s", id->Name.ToString().c_str());
|
||||
|
||||
if (ImGui::InputText("Name", nameBuffer, sizeof(nameBuffer)))
|
||||
{
|
||||
id->Name = nameBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transform
|
||||
if (auto* transform = world.GetComponent<ECSComponent::Transform>(m_selected_entity))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
|
||||
float pos[3] = { static_cast<float>(transform->Position.x), static_cast<float>(transform->Position.y), static_cast<float>(transform->Position.z) };
|
||||
|
||||
if(ImGui::DragFloat3("Position", pos, 0.1f)){
|
||||
|
||||
}
|
||||
|
||||
ImGui::DragFloat3("Rotation",
|
||||
&transform->Rotation.x, 0.1, -360.0, 360.0);
|
||||
|
||||
ImGui::DragFloat3("Scale",
|
||||
&transform->Scale.x, 0.01, 0.0, 100000.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Velocity
|
||||
if (auto* velocity = world.GetComponent<ECSComponent::Velocity>(m_selected_entity))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("Velocity", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::DragFloat3("Value",
|
||||
&velocity->Value.x, 0.1);
|
||||
|
||||
if (ImGui::Button("Stop"))
|
||||
{
|
||||
velocity->Value = glm::dvec3(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LODController
|
||||
if (auto* lod = world.GetComponent<ECSComponent::LODController>(m_selected_entity))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("LODController", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
float range_min = lod->RangeMin;
|
||||
float range_max = lod->RangeMax;
|
||||
ImGui::DragFloat("Range Min", &range_min, 0.1, 0.0, 100000.0);
|
||||
ImGui::DragFloat("Range Max", &range_max, 0.1, 0.0, 100000.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Sound
|
||||
|
||||
if(auto* sound = world.GetComponent<ECSComponent::SoundComponent>(m_selected_entity)){
|
||||
if(ImGui::CollapsingHeader("Sound", ImGuiTreeNodeFlags_DefaultOpen)){
|
||||
ImGui::Text("Sound file: %s", sound->sound.name().c_str());
|
||||
ImGui::DragFloat("Volume", &sound->volume, 0.01f, 0.0f, 1.0f);
|
||||
ImGui::DragFloat("Pitch", &sound->pitch, 0.01f, 0.0f, 2.0f);
|
||||
ImGui::Checkbox("Looping", &sound->loop);
|
||||
ImGui::Checkbox("Playing", &sound->isPlaying);
|
||||
ImGui::Checkbox("Play On Start", &sound->playOnStart);
|
||||
}
|
||||
}
|
||||
|
||||
// MeshRenderer
|
||||
|
||||
if(auto* mesh_renderer = world.GetComponent<ECSComponent::MeshRenderer>(m_selected_entity))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("MeshRenderer", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
//ImGui::Text("Mesh: %s", mesh_renderer->meshHandle.ToString().c_str());
|
||||
ImGui::Checkbox("Visible", &mesh_renderer->visible);
|
||||
}
|
||||
}
|
||||
|
||||
// SpotLight
|
||||
|
||||
if (auto* light = world.GetComponent<ECSComponent::SpotLight>(m_selected_entity))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("SpotLight", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Checkbox("Enabled", &light->Enabled);
|
||||
ImGui::ColorEdit3("Color", &light->Color.x);
|
||||
ImGui::DragFloat("Intensity", &light->Intensity, 0.1f, 0.0f, 100.0f);
|
||||
ImGui::DragFloat("Range", &light->Range, 0.5f, 0.0f, 500.0f);
|
||||
ImGui::DragFloat("Inner Angle", &light->InnerAngle, 0.1f, 0.0f, light->OuterAngle);
|
||||
ImGui::DragFloat("Outer Angle", &light->OuterAngle, 0.1f, light->InnerAngle, 90.0f);
|
||||
ImGui::Checkbox("Cast Shadows", &light->CastShadows);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
}
|
||||
void ui_layer::set_cursor(int const Mode)
|
||||
{
|
||||
glfwSetInputMode(m_window, GLFW_CURSOR, Mode);
|
||||
|
||||
@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include <string>
|
||||
#include "model/Texture.h"
|
||||
#include "widgets/popup.h"
|
||||
#include "entitysystem/ECWorld.h"
|
||||
|
||||
// GuiLayer -- basic user interface class. draws requested information on top of openGL screen
|
||||
|
||||
@@ -148,6 +149,7 @@ protected:
|
||||
void render_menu();
|
||||
void render_quit_widget();
|
||||
void render_hierarchy();
|
||||
void render_entity_hierarchy(ECWorld& world);
|
||||
// draws a quad between coordinates x,y and z,w with uv-coordinates spanning 0-1
|
||||
void quad( glm::vec4 const &Coordinates, glm::vec4 const &Color );
|
||||
// members
|
||||
@@ -161,4 +163,6 @@ protected:
|
||||
bool m_imgui_demo = false;
|
||||
bool m_editor_hierarchy = false;
|
||||
bool m_editor_change_history = false;
|
||||
entt::entity m_selected_entity = entt::null;
|
||||
bool m_entity_hierarchy = true;
|
||||
};
|
||||
|
||||
69
entitysystem/ECScene.cpp
Normal file
69
entitysystem/ECScene.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#include "ECScene.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
ECScene::ECScene()
|
||||
: m_loaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
ECScene::~ECScene()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
void ECScene::Load()
|
||||
{
|
||||
if (m_loaded)
|
||||
return;
|
||||
|
||||
m_systems.Create(m_world);
|
||||
OnLoad();
|
||||
m_loaded = true;
|
||||
}
|
||||
void ECScene::Unload()
|
||||
{
|
||||
if (!m_loaded)
|
||||
return;
|
||||
|
||||
OnUnload();
|
||||
m_world.Clear();
|
||||
m_loaded = false;
|
||||
}
|
||||
|
||||
void ECScene::Update(float dt)
|
||||
{
|
||||
if (!m_loaded){
|
||||
|
||||
return;
|
||||
}
|
||||
m_systems.Update(m_world, dt);
|
||||
OnUpdate(dt);
|
||||
}
|
||||
|
||||
|
||||
ECWorld& ECScene::World()
|
||||
{
|
||||
return m_world;
|
||||
}
|
||||
|
||||
const ECWorld& ECScene::World() const
|
||||
{
|
||||
return m_world;
|
||||
}
|
||||
|
||||
void ECScene::OnLoad()
|
||||
{
|
||||
}
|
||||
|
||||
void ECScene::OnUnload()
|
||||
{;
|
||||
}
|
||||
|
||||
void ECScene::OnUpdate(float dt)
|
||||
{
|
||||
(void)dt;
|
||||
}
|
||||
42
entitysystem/ECScene.h
Normal file
42
entitysystem/ECScene.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#ifndef EU07_ECSCENE_H
|
||||
#define EU07_ECSCENE_H
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ECWorld.h"
|
||||
#include "systems/SystemManager.h"
|
||||
|
||||
class ECScene
|
||||
{
|
||||
public:
|
||||
ECScene();
|
||||
virtual ~ECScene();
|
||||
|
||||
void Load();
|
||||
void Unload();
|
||||
void Update(float dt);
|
||||
|
||||
ECWorld& World();
|
||||
const ECWorld& World() const;
|
||||
|
||||
protected:
|
||||
virtual void OnLoad();
|
||||
virtual void OnUnload();
|
||||
virtual void OnUpdate(float dt);
|
||||
|
||||
protected:
|
||||
ECWorld m_world;
|
||||
SystemManager m_systems;
|
||||
|
||||
private:
|
||||
bool m_loaded;
|
||||
};
|
||||
|
||||
|
||||
#endif //EU07_ECSCENE_H
|
||||
54
entitysystem/ECWorld.cpp
Normal file
54
entitysystem/ECWorld.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#include "ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
|
||||
entt::entity ECWorld::CreateEntity()
|
||||
{
|
||||
return m_registry.create();
|
||||
}
|
||||
|
||||
void ECWorld::DestroyEntity(entt::entity entity)
|
||||
{
|
||||
if (m_registry.valid(entity))
|
||||
m_registry.destroy(entity);
|
||||
}
|
||||
|
||||
void ECWorld::Clear()
|
||||
{
|
||||
m_registry.clear();
|
||||
}
|
||||
|
||||
bool ECWorld::IsAlive(entt::entity entity) const
|
||||
{
|
||||
return m_registry.valid(entity);
|
||||
}
|
||||
|
||||
|
||||
entt::entity ECWorld::FindEntityByName(const std::string& name) const
|
||||
{
|
||||
auto view = m_registry.view<ECSComponent::Identification>();
|
||||
|
||||
for (auto entity : view)
|
||||
{
|
||||
const auto& id = view.get<ECSComponent::Identification>(entity);
|
||||
if (id.Name.ToString() == name)
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
return entt::null;
|
||||
}
|
||||
|
||||
entt::registry& ECWorld::Registry()
|
||||
{
|
||||
return m_registry;
|
||||
}
|
||||
|
||||
const entt::registry& ECWorld::Registry() const
|
||||
{
|
||||
return m_registry;
|
||||
}
|
||||
135
entitysystem/ECWorld.h
Normal file
135
entitysystem/ECWorld.h
Normal file
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#ifndef EU07_ECWORLD_H
|
||||
#define EU07_ECWORLD_H
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
class ECWorld
|
||||
{
|
||||
public:
|
||||
ECWorld() = default;
|
||||
~ECWorld() = default;
|
||||
|
||||
entt::entity CreateEntity();
|
||||
void DestroyEntity(entt::entity entity);
|
||||
void Clear();
|
||||
|
||||
bool IsAlive(entt::entity entity) const;
|
||||
|
||||
entt::registry& Registry();
|
||||
const entt::registry& Registry() const;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T& AddComponent(entt::entity entity, Args&&... args);
|
||||
|
||||
template<typename T>
|
||||
bool HasComponent(entt::entity entity) const;
|
||||
|
||||
template<typename T>
|
||||
T* GetComponent(entt::entity entity);
|
||||
|
||||
template<typename T>
|
||||
const T* GetComponent(entt::entity entity) const;
|
||||
|
||||
template<typename T>
|
||||
void RemoveComponent(entt::entity entity);
|
||||
|
||||
entt::entity FindEntityByName(const std::string& name) const;
|
||||
|
||||
template<typename... Components>
|
||||
auto View()
|
||||
{
|
||||
return m_registry.view<Components...>();
|
||||
}
|
||||
|
||||
template<typename... Components, typename Func>
|
||||
void Each(Func&& func)
|
||||
{
|
||||
auto view = m_registry.view<Components...>();
|
||||
|
||||
for (auto entity : view)
|
||||
{
|
||||
auto components = std::forward_as_tuple(view.get<Components>(entity)...);
|
||||
|
||||
std::apply([&](Components&... comps)
|
||||
{
|
||||
func(entity, comps...);
|
||||
}, components);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<entt::entity> GetEntities() const
|
||||
{
|
||||
std::vector<entt::entity> entities;
|
||||
auto *storage = m_registry.storage<entt::entity>();
|
||||
|
||||
for (auto entity : *storage)
|
||||
{
|
||||
|
||||
entities.push_back(entity);
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
std::size_t GetEntityCount() const
|
||||
{
|
||||
return m_registry.storage<entt::entity>()->size();
|
||||
}
|
||||
|
||||
private:
|
||||
entt::registry m_registry;
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T& ECWorld::AddComponent(entt::entity entity, Args&&... args)
|
||||
{
|
||||
static_assert(std::is_constructible_v<T, Args...>,
|
||||
"Component must be constructible with provided arguments");
|
||||
|
||||
if (m_registry.all_of<T>(entity))
|
||||
{
|
||||
return m_registry.replace<T>(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
return m_registry.emplace<T>(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ECWorld::HasComponent(entt::entity entity) const
|
||||
{
|
||||
return m_registry.all_of<T>(entity);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* ECWorld::GetComponent(entt::entity entity)
|
||||
{
|
||||
return m_registry.try_get<T>(entity);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* ECWorld::GetComponent(entt::entity entity) const
|
||||
{
|
||||
return m_registry.try_get<T>(entity);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ECWorld::RemoveComponent(entt::entity entity)
|
||||
{
|
||||
if (m_registry.all_of<T>(entity))
|
||||
m_registry.remove<T>(entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern ECWorld& GetComponentSystem();
|
||||
#define ECS GetComponentSystem()
|
||||
#endif //EU07_ECWORLD_H
|
||||
59
entitysystem/SceneManager.cpp
Normal file
59
entitysystem/SceneManager.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#include "SceneManager.h"
|
||||
#include "ECScene.h"
|
||||
|
||||
SceneManager::SceneManager() = default;
|
||||
|
||||
SceneManager::~SceneManager()
|
||||
{
|
||||
if (m_currentScene)
|
||||
m_currentScene->Unload();
|
||||
}
|
||||
|
||||
void SceneManager::SetScene(std::unique_ptr<ECScene> scene)
|
||||
{
|
||||
m_pendingScene = std::move(scene);
|
||||
if (m_currentScene)
|
||||
m_currentScene->Load();
|
||||
}
|
||||
|
||||
void SceneManager::Update(float dt)
|
||||
{
|
||||
if (m_pendingScene)
|
||||
SwitchScene();
|
||||
|
||||
if (m_currentScene){
|
||||
m_currentScene->Update(dt);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void SceneManager::SwitchScene()
|
||||
{
|
||||
if (m_currentScene)
|
||||
m_currentScene->Unload();
|
||||
|
||||
m_currentScene = std::move(m_pendingScene);
|
||||
|
||||
if (m_currentScene)
|
||||
m_currentScene->Load();
|
||||
}
|
||||
|
||||
ECScene* SceneManager::CurrentScene()
|
||||
{
|
||||
return m_currentScene.get();
|
||||
}
|
||||
|
||||
const ECScene* SceneManager::CurrentScene() const
|
||||
{
|
||||
return m_currentScene.get();
|
||||
}
|
||||
|
||||
bool SceneManager::HasScene() const
|
||||
{
|
||||
return m_currentScene != nullptr;
|
||||
}
|
||||
40
entitysystem/SceneManager.h
Normal file
40
entitysystem/SceneManager.h
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#ifndef EU07_SCENEMANAGER_H
|
||||
#define EU07_SCENEMANAGER_H
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class ECScene;
|
||||
|
||||
class SceneManager
|
||||
{
|
||||
public:
|
||||
SceneManager();
|
||||
~SceneManager();
|
||||
|
||||
void SetScene(std::unique_ptr<ECScene> scene);
|
||||
void Update(float dt);
|
||||
|
||||
ECScene* CurrentScene();
|
||||
const ECScene* CurrentScene() const;
|
||||
|
||||
bool HasScene() const;
|
||||
|
||||
private:
|
||||
void SwitchScene();
|
||||
|
||||
private:
|
||||
std::unique_ptr<ECScene> m_currentScene;
|
||||
std::unique_ptr<ECScene> m_pendingScene;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //EU07_SCENEMANAGER_H
|
||||
@@ -3,11 +3,14 @@
|
||||
*/
|
||||
#ifndef EU07_BASICCOMPONENTS_H
|
||||
#define EU07_BASICCOMPONENTS_H
|
||||
#include "audio/sound.h"
|
||||
#include "entt/entity/entity.hpp"
|
||||
#include "glm/vec3.hpp"
|
||||
#include "glm/gtc/quaternion.hpp"
|
||||
#include <string>
|
||||
#include "registry/FName.h"
|
||||
#include "utils/uuid.hpp"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ECSComponent
|
||||
{
|
||||
@@ -16,18 +19,23 @@ namespace ECSComponent
|
||||
///</summary>
|
||||
struct Transform
|
||||
{
|
||||
glm::vec3 Position{0.f}; // object position
|
||||
glm::dvec3 Position{0.f}; // object position
|
||||
glm::quat Rotation{1.f, 0.f, 0.f, 0.f}; // object rotation
|
||||
glm::vec3 Scale{1.f}; // object scale
|
||||
};
|
||||
|
||||
struct Velocity
|
||||
{
|
||||
glm::vec3 Value{0.f}; // object velocity
|
||||
};
|
||||
|
||||
///< summary>
|
||||
/// Basic component for naming entities
|
||||
/// in future for scenery hierarchy
|
||||
///</summary>
|
||||
struct Identification
|
||||
{
|
||||
std::string Name; // object name - may contain slashes for editor hierarchy "directories"
|
||||
FName Name; // object name - may contain slashes for editor hierarchy "directories"
|
||||
std::uint64_t Id{0}; // id in scene
|
||||
};
|
||||
|
||||
@@ -39,6 +47,16 @@ struct Parent
|
||||
entt::entity value{entt::null};
|
||||
};
|
||||
|
||||
struct SoundComponent
|
||||
{
|
||||
sound_source sound; // sound source
|
||||
float volume = 1.0f; // 0–1
|
||||
float pitch = 1.0f; // speed / pitch
|
||||
float range = 1.0f; // range
|
||||
bool loop = false;
|
||||
bool playOnStart = false;
|
||||
bool isPlaying = false;
|
||||
};
|
||||
///< summary>
|
||||
/// Empty component for entities that are disabled and should not be processed by systems
|
||||
///</summary>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef EU07_RENDERCOMPONENTS_H
|
||||
#define EU07_RENDERCOMPONENTS_H
|
||||
|
||||
#include "registry/FName.h"
|
||||
#include "rendering/geometrybank.h"
|
||||
|
||||
namespace ECSComponent
|
||||
{
|
||||
/// <summary>
|
||||
@@ -11,6 +14,8 @@ namespace ECSComponent
|
||||
/// </remarks>
|
||||
struct MeshRenderer
|
||||
{
|
||||
gfx::geometry_handle meshHandle;
|
||||
bool visible = true;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -19,7 +24,53 @@ struct MeshRenderer
|
||||
/// <remarks>Currently empty
|
||||
/// TODO: Add component members
|
||||
/// </remarks>
|
||||
struct SpotLight{};
|
||||
struct SpotLight
|
||||
{
|
||||
glm::vec3 Color{ 1.0f, 1.0f, 1.0f };
|
||||
|
||||
float Intensity = 1.0f;
|
||||
float Range = 25.0f;
|
||||
|
||||
float InnerAngle = 15.0f;
|
||||
float OuterAngle = 25.0f;
|
||||
|
||||
bool CastShadows = false;
|
||||
bool Enabled = true;
|
||||
};
|
||||
|
||||
// TODO: AreaLight like blender
|
||||
// TODO: SunLight for scene
|
||||
|
||||
|
||||
struct ParticleEmitter
|
||||
{
|
||||
FName particleEffectPath = ("");
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
struct Decal
|
||||
{
|
||||
FName decalTexturePath = ("");
|
||||
float size = 1.0f;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
struct Billboard
|
||||
{
|
||||
FName texturePath = ("");
|
||||
float size = 1.0f;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
struct Line {
|
||||
glm::vec3 start{ 0.0f };
|
||||
glm::vec3 end{ 1.0f, 0.0f, 0.0f };
|
||||
glm::vec3 color{ 1.0f, 1.0f, 1.0f };
|
||||
float thickness = 1.0f;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Component for entities that can be rendered with LOD
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
//
|
||||
// Created by Hirek on 3/14/2026.
|
||||
//
|
||||
|
||||
#include "ecs.h"
|
||||
|
||||
void ECS::ClearWorld()
|
||||
{
|
||||
world_.clear();
|
||||
}
|
||||
|
||||
entt::entity ECS::CreateObject()
|
||||
{
|
||||
const auto e = world_.create();
|
||||
world_.emplace<ECSComponent::Transform>(e);
|
||||
// add UID
|
||||
auto id = world_.emplace<ECSComponent::Identification>(e);
|
||||
id.Id = nextId_++;
|
||||
return e;
|
||||
}
|
||||
|
||||
void ECS::DestroyObject(entt::entity Entity)
|
||||
{
|
||||
if (Entity == entt::null) // check if Entity is not null
|
||||
return;
|
||||
if (!world_.valid(Entity)) // check if exist
|
||||
return;
|
||||
world_.destroy(Entity);
|
||||
}
|
||||
|
||||
bool ECS::ValidObject(entt::entity entity) const
|
||||
{
|
||||
return entity != entt::null && world_.valid(entity);
|
||||
}
|
||||
|
||||
entt::entity ECS::FindById(std::uint64_t id)
|
||||
{
|
||||
auto view = world_.view<const ECSComponent::Identification>();
|
||||
for (auto e : view)
|
||||
{
|
||||
const auto &ident = view.get<ECSComponent::Identification>(e);
|
||||
if (ident.Id == id)
|
||||
return e;
|
||||
}
|
||||
return entt::null;
|
||||
}
|
||||
|
||||
std::vector<entt::entity> ECS::FindByName(std::string_view name)
|
||||
{
|
||||
std::vector<entt::entity> result;
|
||||
|
||||
auto view = world_.view<const ECSComponent::Identification>();
|
||||
for (auto e : view)
|
||||
{
|
||||
const auto &ident = view.get<ECSComponent::Identification>(e);
|
||||
if (ident.Name == name)
|
||||
{
|
||||
result.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ECS &GetComponentSystem()
|
||||
{
|
||||
static ECS _ecs;
|
||||
return _ecs;
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
//
|
||||
// Created by Hirek on 3/14/2026.
|
||||
//
|
||||
|
||||
#ifndef EU07_ECS_H
|
||||
#define EU07_ECS_H
|
||||
#include "components/BasicComponents.h"
|
||||
#include "entt/entity/registry.hpp"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
class ECS final
|
||||
{
|
||||
private:
|
||||
entt::registry world_; // scene registry
|
||||
std::uint64_t nextId_ = 1; // 0 is invalid/none
|
||||
|
||||
|
||||
public:
|
||||
//
|
||||
// World lifecycle
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Clears the world, removing all entities and components
|
||||
/// </summary>
|
||||
void ClearWorld();
|
||||
|
||||
//
|
||||
// Objects
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Creates new object with basic components (Transform, Identification) and returns its entity handle.
|
||||
/// </summary>
|
||||
/// <returns>Entity handle of created object</returns>
|
||||
entt::entity CreateObject();
|
||||
|
||||
/// <summary>
|
||||
/// Destroys object with it's all components
|
||||
/// </summary>
|
||||
/// <param name="Entity">Entity to be removed</param>
|
||||
void DestroyObject(entt::entity Entity);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if object with given entity handle exists in the registry
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity handle to check</param>
|
||||
/// <returns>true if object exists, false otherwise</returns>
|
||||
bool ValidObject(entt::entity entity) const;
|
||||
|
||||
//
|
||||
// Identification lookups
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Finds object with given UID. Returns null entity if not found.
|
||||
/// </summary>
|
||||
/// <param name="id">UID of object</param>
|
||||
entt::entity FindById(std::uint64_t id);
|
||||
|
||||
/// <summary>
|
||||
/// Finds objects with given name. Returns empty vector if not found.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of object</param>
|
||||
/// <remarks>May return more than 1 as many objects can have the same name</remarks>
|
||||
std::vector<entt::entity> FindByName(std::string_view name);
|
||||
|
||||
//
|
||||
// Components
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Adds component of type T to entity, forwarding provided arguments to component constructor. If component already exists, it will be replaced.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
/// <param name="entity">Entity to which component will be added</param>
|
||||
/// <param name="args">Arguments forwarded to component constructor</param>
|
||||
/// <returns>Reference to added component</returns>
|
||||
template <class T, class... Args> T &AddComponent(entt::entity entity, Args &&...args)
|
||||
{
|
||||
return world_.emplace<T>(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns entity's component
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> type</typeparam>
|
||||
/// <param name="entity">Entity to which component will be added</param>
|
||||
/// <returns>Reference to added component</returns>
|
||||
template <class T> T &GetComponent(entt::entity entity)
|
||||
{
|
||||
return world_.get<T>(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns entity's component
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> type</typeparam>
|
||||
/// <param name="entity">Entity to which component will be added</param>
|
||||
/// <returns>Reference to added component</returns>
|
||||
template <class T> const T &GetComponent(entt::entity entity) const
|
||||
{
|
||||
return world_.get<T>(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get component of type T from entity. Returns nullptr if component does not exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
/// <param name="entity">Entity from which component will be retrieved</param>
|
||||
/// <returns>Pointer to component if exists, nullptr otherwise</returns>
|
||||
template <class T> T *TryGetComponent(entt::entity entity)
|
||||
{
|
||||
return world_.try_get<T>(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get component of type T from entity. Returns nullptr if component does not exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
/// <param name="entity">Entity from which component will be retrieved</param>
|
||||
/// <returns>Pointer to component if exists, nullptr otherwise</returns>
|
||||
template <class T> const T *TryGetComponent(entt::entity entity) const
|
||||
{
|
||||
return world_.try_get<T>(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if entity has component of type T.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Component type</typeparam>
|
||||
/// <param name="entity">Entity to check</param>
|
||||
/// <returns>true if entity has component, false otherwise</returns>
|
||||
template <class... T> bool HasComponent(entt::entity entity) const
|
||||
{
|
||||
return world_.all_of<T...>(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes component of type T from entity. Does nothing if component does not exist.
|
||||
/// </summary> <typeparam name="T">Component type</typeparam>
|
||||
/// <param name="entity">Entity from which component will be removed</param>
|
||||
template <class... T> void RemoveComponent(entt::entity entity)
|
||||
{
|
||||
world_.remove<T...>(entity);
|
||||
}
|
||||
};
|
||||
|
||||
extern ECS& GetComponentSystem();
|
||||
#define CS GetComponentSystem()
|
||||
#endif // EU07_ECS_H
|
||||
35
entitysystem/scenes/GameScene.cpp
Normal file
35
entitysystem/scenes/GameScene.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#include "GameScene.h"
|
||||
|
||||
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
#include "entitysystem/systems/MovementSystem.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
GameScene::GameScene() = default;
|
||||
GameScene::~GameScene() = default ;
|
||||
|
||||
void GameScene::OnLoad()
|
||||
{
|
||||
m_systems.AddSystem<MovementSystem>();
|
||||
|
||||
}
|
||||
|
||||
void GameScene::OnUpdate(float dt)
|
||||
{
|
||||
|
||||
//auto& world = World();
|
||||
|
||||
//auto view = world.View<ECSComponent::Transform>();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GameScene::OnUnload()
|
||||
{
|
||||
World().Clear();
|
||||
}
|
||||
30
entitysystem/scenes/GameScene.h
Normal file
30
entitysystem/scenes/GameScene.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#ifndef EU07_GAMESCENE_H
|
||||
#define EU07_GAMESCENE_H
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "entitysystem/ECScene.h"
|
||||
|
||||
class GameScene final : public ECScene
|
||||
{
|
||||
public:
|
||||
GameScene();
|
||||
~GameScene() override;
|
||||
|
||||
private:
|
||||
void OnLoad() override;
|
||||
void OnUnload() override;
|
||||
void OnUpdate(float dt) override;
|
||||
|
||||
private:
|
||||
void CreateTestEntities();
|
||||
};
|
||||
|
||||
|
||||
#endif //EU07_GAMESCENE_H
|
||||
5
entitysystem/systems/BaseSystem.cpp
Normal file
5
entitysystem/systems/BaseSystem.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#include "BaseSystem.h"
|
||||
25
entitysystem/systems/BaseSystem.h
Normal file
25
entitysystem/systems/BaseSystem.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#ifndef EU07_BASICSYSTEM_H
|
||||
#define EU07_BASICSYSTEM_H
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
class ECWorld;
|
||||
|
||||
class BaseSystem
|
||||
{
|
||||
public:
|
||||
virtual ~BaseSystem() = default;
|
||||
|
||||
virtual void OnCreate(ECWorld& world) {}
|
||||
virtual void OnDestroy(ECWorld& world) {}
|
||||
|
||||
virtual void Update(ECWorld& world, float dt) {}
|
||||
};
|
||||
|
||||
|
||||
#endif //EU07_BASICSYSTEM_H
|
||||
29
entitysystem/systems/MovementSystem.cpp
Normal file
29
entitysystem/systems/MovementSystem.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#include "MovementSystem.h"
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
|
||||
void MovementSystem::Update(ECWorld& world, float dt)
|
||||
{
|
||||
constexpr double damping = 0.90; // 0.0 = instant stop, 1.0 = without damping
|
||||
|
||||
world.Each<ECSComponent::Transform, ECSComponent::Velocity>(
|
||||
[dt](auto entity, auto& transform, auto& velocity)
|
||||
{
|
||||
|
||||
transform.Position += velocity.Value * dt;
|
||||
|
||||
velocity.Value *= damping;
|
||||
|
||||
if (glm::length(velocity.Value) < 0.001)
|
||||
{
|
||||
velocity.Value = glm::dvec3(0.0);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
25
entitysystem/systems/MovementSystem.h
Normal file
25
entitysystem/systems/MovementSystem.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#ifndef EU07_MOVEMENTSYSTEM_H
|
||||
#define EU07_MOVEMENTSYSTEM_H
|
||||
|
||||
|
||||
#pragma once
|
||||
#include "BaseSystem.h"
|
||||
|
||||
namespace ECSComponent
|
||||
{
|
||||
struct Transform;
|
||||
struct Velocity;
|
||||
}
|
||||
|
||||
class MovementSystem : public BaseSystem
|
||||
{
|
||||
public:
|
||||
void Update(ECWorld& world, float dt) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //EU07_MOVEMENTSYSTEM_H
|
||||
33
entitysystem/systems/SystemManager.cpp
Normal file
33
entitysystem/systems/SystemManager.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#include "SystemManager.h"
|
||||
#include "entitysystem/systems/BaseSystem.h"
|
||||
#include "entitysystem/ECWorld.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
SystemManager::~SystemManager() = default;
|
||||
|
||||
void SystemManager::Create(ECWorld& world)
|
||||
{
|
||||
WriteLog("[SystemManager] Creating systems");
|
||||
for (auto& system : m_systems){
|
||||
system->OnCreate(world);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SystemManager::Destroy(ECWorld& world)
|
||||
{
|
||||
WriteLog("[SystemManager] Destroying systems");
|
||||
for (auto& system : m_systems)
|
||||
system->OnDestroy(world);
|
||||
}
|
||||
|
||||
void SystemManager::Update(ECWorld& world, float dt)
|
||||
{
|
||||
|
||||
for (auto& system : m_systems)
|
||||
system->Update(world, dt);
|
||||
}
|
||||
46
entitysystem/systems/SystemManager.h
Normal file
46
entitysystem/systems/SystemManager.h
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Created by Daniu
|
||||
//
|
||||
|
||||
#ifndef EU07_SYSTEMMANAGER_H
|
||||
#define EU07_SYSTEMMANAGER_H
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseSystem.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class ECWorld;
|
||||
|
||||
class SystemManager
|
||||
{
|
||||
public:
|
||||
SystemManager() = default;
|
||||
~SystemManager();
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T& AddSystem(Args&&... args)
|
||||
{
|
||||
static_assert(std::is_base_of_v<BaseSystem, T>);
|
||||
auto system = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
T& ref = *system;
|
||||
m_systems.emplace_back(std::move(system));
|
||||
return ref;
|
||||
}
|
||||
|
||||
void Create(ECWorld& world);
|
||||
void Destroy(ECWorld& world);
|
||||
|
||||
void Update(ECWorld& world, float dt);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<BaseSystem>> m_systems;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //EU07_SYSTEMMANAGER_H
|
||||
63
registry/FName.h
Normal file
63
registry/FName.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "NameRegistry.h"
|
||||
|
||||
struct FName
|
||||
{
|
||||
using value_type = uint64_t;
|
||||
|
||||
static constexpr value_type INVALID_ID = 0;
|
||||
|
||||
value_type id = INVALID_ID;
|
||||
|
||||
constexpr FName() noexcept = default;
|
||||
|
||||
constexpr explicit FName(value_type value) noexcept
|
||||
: id(value)
|
||||
{
|
||||
}
|
||||
|
||||
FName(const char* text)
|
||||
: id(text ? NameRegistry::GetOrCreate(text) : INVALID_ID)
|
||||
{
|
||||
}
|
||||
|
||||
FName(const std::string& text)
|
||||
: id(text.empty() ? INVALID_ID : NameRegistry::GetOrCreate(text))
|
||||
{
|
||||
}
|
||||
|
||||
FName(std::string_view text)
|
||||
: id(text.empty() ? INVALID_ID : NameRegistry::GetOrCreate(text))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool IsValid() const noexcept
|
||||
{
|
||||
return id != INVALID_ID;
|
||||
}
|
||||
|
||||
constexpr value_type GetId() const noexcept
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
const std::string& ToString() const
|
||||
{
|
||||
return NameRegistry::GetString(id);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const FName& other) const noexcept
|
||||
{
|
||||
return id == other.id;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const FName& other) const noexcept
|
||||
{
|
||||
return id != other.id;
|
||||
}
|
||||
};
|
||||
41
registry/NameHash.h
Normal file
41
registry/NameHash.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
constexpr uint64_t FNV1A_OFFSET_BASIS_64 = 14695981039346656037ull;
|
||||
constexpr uint64_t FNV1A_PRIME_64 = 1099511628211ull;
|
||||
|
||||
constexpr uint64_t HashLiteral(const char* str, std::size_t len) noexcept
|
||||
{
|
||||
uint64_t hash = FNV1A_OFFSET_BASIS_64;
|
||||
for (std::size_t i = 0; i < len; ++i)
|
||||
{
|
||||
hash ^= static_cast<unsigned char>(str[i]);
|
||||
hash *= FNV1A_PRIME_64;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
constexpr uint64_t HashLiteral(const char* str) noexcept
|
||||
{
|
||||
uint64_t hash = FNV1A_OFFSET_BASIS_64;
|
||||
while (*str)
|
||||
{
|
||||
hash ^= static_cast<unsigned char>(*str++);
|
||||
hash *= FNV1A_PRIME_64;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline uint64_t HashRuntime(std::string_view str) noexcept
|
||||
{
|
||||
uint64_t hash = FNV1A_OFFSET_BASIS_64;
|
||||
for (char c : str)
|
||||
{
|
||||
hash ^= static_cast<unsigned char>(c);
|
||||
hash *= FNV1A_PRIME_64;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
11
registry/NameLiteral.h
Normal file
11
registry/NameLiteral.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "FName.h"
|
||||
#include "NameHash.h"
|
||||
|
||||
consteval FName operator"" _name(const char* str, std::size_t len)
|
||||
{
|
||||
return FName{ HashLiteral(str, len) };
|
||||
}
|
||||
52
registry/NameRegistry.cpp
Normal file
52
registry/NameRegistry.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "NameRegistry.h"
|
||||
#include "NameHash.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
uint64_t NameRegistry::GetOrCreate(std::string_view name)
|
||||
{
|
||||
if (name.empty())
|
||||
return 0;
|
||||
|
||||
const uint64_t hash = HashRuntime(name);
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetMutex());
|
||||
auto& map = GetMap();
|
||||
|
||||
auto [it, inserted] = map.emplace(hash, std::string(name));
|
||||
if (!inserted)
|
||||
{
|
||||
assert(it->second == name && "Name hash collision detected");
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
const std::string& NameRegistry::GetString(uint64_t id)
|
||||
{
|
||||
static const std::string Empty;
|
||||
|
||||
if (id == 0)
|
||||
return Empty;
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetMutex());
|
||||
auto& map = GetMap();
|
||||
|
||||
auto it = map.find(id);
|
||||
if (it != map.end())
|
||||
return it->second;
|
||||
|
||||
return Empty;
|
||||
}
|
||||
|
||||
std::unordered_map<uint64_t, std::string>& NameRegistry::GetMap()
|
||||
{
|
||||
static std::unordered_map<uint64_t, std::string> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
std::mutex& NameRegistry::GetMutex()
|
||||
{
|
||||
static std::mutex mutex;
|
||||
return mutex;
|
||||
}
|
||||
18
registry/NameRegistry.h
Normal file
18
registry/NameRegistry.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
class NameRegistry
|
||||
{
|
||||
public:
|
||||
static uint64_t GetOrCreate(std::string_view name);
|
||||
static const std::string& GetString(uint64_t id);
|
||||
|
||||
private:
|
||||
static std::unordered_map<uint64_t, std::string>& GetMap();
|
||||
static std::mutex& GetMutex();
|
||||
};
|
||||
@@ -25,7 +25,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "rendering/lightarray.h"
|
||||
#include "world/TractionPower.h"
|
||||
#include "application/application.h"
|
||||
#include "entitysystem/ecs.h"
|
||||
#include "entitysystem/ECScene.h"
|
||||
#include "entitysystem/components/BasicComponents.h"
|
||||
#include "entitysystem/components/RenderComponents.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include "utilities/Logs.h"
|
||||
@@ -429,17 +430,18 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch
|
||||
>> nodedata.name
|
||||
>> nodedata.type;
|
||||
|
||||
// Add node to ComponentSystem
|
||||
entt::entity CSEntity = CS.CreateObject();
|
||||
|
||||
auto nodeTransform = CS.GetComponent<ECSComponent::Transform>(CSEntity);
|
||||
nodeTransform.Position = Scratchpad.location.offset.empty() ? glm::dvec3(0.0) : Scratchpad.location.offset.top();
|
||||
nodeTransform.Rotation = Scratchpad.location.rotation;
|
||||
ECWorld& world = Application.sceneManager.CurrentScene()->World();
|
||||
|
||||
entt::entity entity = world.CreateEntity();
|
||||
|
||||
auto transformComponent = world.AddComponent<ECSComponent::Transform>(entity);
|
||||
transformComponent.Position = Scratchpad.location.offset.empty() ? glm::dvec3(0.0) : Scratchpad.location.offset.top();
|
||||
transformComponent.Rotation = Scratchpad.location.rotation;
|
||||
//nodeTransform.Scale = Scratchpad.location.scale;
|
||||
|
||||
auto lodController = CS.AddComponent<ECSComponent::LODController>(CSEntity);
|
||||
lodController.RangeMax = nodedata.range_max;
|
||||
lodController.RangeMin = nodedata.range_min;
|
||||
// auto LODComponent = world.AddComponent<ECSComponent::LODController>(entity);
|
||||
// LODComponent.RangeMax = nodedata.range_max;
|
||||
// LODComponent.RangeMin = nodedata.range_min;
|
||||
|
||||
if( nodedata.name == "none" )
|
||||
{
|
||||
@@ -447,11 +449,12 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch
|
||||
}
|
||||
else
|
||||
{
|
||||
CS.GetComponent<ECSComponent::Identification>(CSEntity).Name = nodedata.name;
|
||||
world.AddComponent<ECSComponent::Identification>(entity).Name = nodedata.name;
|
||||
|
||||
}
|
||||
// type-based deserialization. not elegant but it'll do
|
||||
if( nodedata.type == "dynamic" ) {
|
||||
|
||||
|
||||
auto *vehicle { deserialize_dynamic( Input, Scratchpad, nodedata ) };
|
||||
// vehicle import can potentially fail
|
||||
if( vehicle == nullptr ) { return; }
|
||||
@@ -471,6 +474,9 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch
|
||||
ErrorLog( "Bad scenario: duplicate vehicle name \"" + vehicle->name() + "\" defined in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
|
||||
}
|
||||
|
||||
auto velocityComponent = world.AddComponent<ECSComponent::Velocity>(entity);
|
||||
velocityComponent.Value = glm::dvec3(0.0);
|
||||
|
||||
if( ( vehicle->MoverParameters->CategoryFlag == 1 ) // trains only
|
||||
&& ( ( ( vehicle->LightList( end::front ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 )
|
||||
|| ( ( vehicle->LightList( end::rear ) & ( light::headlight_left | light::headlight_right | light::headlight_upper ) ) != 0 ) ) ) {
|
||||
@@ -572,7 +578,25 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch
|
||||
}
|
||||
scene::Groups.insert( scene::Groups.handle(), instance );
|
||||
simulation::Region->insert( instance );
|
||||
|
||||
ECWorld& world = Application.sceneManager.CurrentScene()->World();
|
||||
|
||||
entt::entity entity = world.FindEntityByName(instance->name());
|
||||
|
||||
if(entity != entt::null) {
|
||||
|
||||
auto meshComponent = world.AddComponent<ECSComponent::MeshRenderer>(entity);
|
||||
//meshComponent.meshHandle = instance->Model()->MeshHandle();
|
||||
meshComponent.visible = true;
|
||||
|
||||
auto* transformComponent = world.GetComponent<ECSComponent::Transform>(entity);
|
||||
transformComponent->Position = {instance->location().x, instance->location().y, instance->location().z};
|
||||
transformComponent->Rotation = {instance->Angles().x, instance->Angles().y, instance->Angles().z, 1.0f};
|
||||
//transformComponent->Scale = {instance->scale().x, instance->scale().y, instance->scale().z};
|
||||
}
|
||||
|
||||
scene::basic_node *hierarchy_node = instance;
|
||||
|
||||
if (hierarchy_node)
|
||||
{ scene::Hierarchy[hierarchy_node->uuid.to_string()] = hierarchy_node;
|
||||
}
|
||||
@@ -650,6 +674,15 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch
|
||||
else if( nodedata.type == "sound" ) {
|
||||
|
||||
auto *sound { deserialize_sound( Input, Scratchpad, nodedata ) };
|
||||
|
||||
auto soundComponent = world.AddComponent<ECSComponent::SoundComponent>(entity);
|
||||
|
||||
soundComponent.sound = *sound;
|
||||
soundComponent.volume = 1;
|
||||
soundComponent.pitch = 1;
|
||||
soundComponent.range = sound->range();
|
||||
soundComponent.isPlaying = sound->is_playing();
|
||||
|
||||
if( false == simulation::Sounds.insert( sound ) ) {
|
||||
ErrorLog( "Bad scenario: duplicate sound node name \"" + sound->name() + "\" defined in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user