Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions editor/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ set(SRCS
editor/src/DocumentWindows/InspectorWindow/Show.cpp
editor/src/DocumentWindows/InspectorWindow/Shutdown.cpp
editor/src/DocumentWindows/InspectorWindow/Update.cpp
editor/src/DocumentWindows/InspectorWindow/AddComponent.cpp
editor/src/DocumentWindows/MaterialInspector/Init.cpp
editor/src/DocumentWindows/MaterialInspector/Show.cpp
editor/src/DocumentWindows/MaterialInspector/Shutdown.cpp
Expand Down
2 changes: 1 addition & 1 deletion editor/src/DocumentWindows/EditorScene/EditorScene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ namespace nexo::editor {
*/
void show() override;

bool showToolbar = false;
bool showToolbar = true;
bool isPhysicsRunning = false;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ namespace nexo::editor {
ImGui::EndGroup();
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
ImGui::TreePop();
}
ImGui::TreePop();

if (m_popupManager.showPopupModal("Create new material")) {
createMaterialPopup(entity);
Expand Down
63 changes: 63 additions & 0 deletions editor/src/DocumentWindows/InspectorWindow/AddComponent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//// AddComponent.cpp ///////////////////////////////////////////////////////////////
//
// ⢀⢀⢀⣤⣤⣤⡀⢀⢀⢀⢀⢀⢀⢠⣤⡄⢀⢀⢀⢀⣠⣤⣤⣤⣤⣤⣤⣤⣤⣤⡀⢀⢀⢀⢠⣤⣄⢀⢀⢀⢀⢀⢀⢀⣤⣤⢀⢀⢀⢀⢀⢀⢀⢀⣀⣄⢀⢀⢠⣄⣀⢀⢀⢀⢀⢀⢀⢀
// ⢀⢀⢀⣿⣿⣿⣷⡀⢀⢀⢀⢀⢀⢸⣿⡇⢀⢀⢀⢀⣿⣿⡟⡛⡛⡛⡛⡛⡛⡛⢁⢀⢀⢀⢀⢻⣿⣦⢀⢀⢀⢀⢠⣾⡿⢃⢀⢀⢀⢀⢀⣠⣾⣿⢿⡟⢀⢀⡙⢿⢿⣿⣦⡀⢀⢀⢀⢀
// ⢀⢀⢀⣿⣿⡛⣿⣷⡀⢀⢀⢀⢀⢸⣿⡇⢀⢀⢀⢀⣿⣿⡇⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⡙⣿⡷⢀⢀⣰⣿⡟⢁⢀⢀⢀⢀⢀⣾⣿⡟⢁⢀⢀⢀⢀⢀⢀⢀⡙⢿⣿⡆⢀⢀⢀
// ⢀⢀⢀⣿⣿⢀⡈⢿⣷⡄⢀⢀⢀⢸⣿⡇⢀⢀⢀⢀⣿⣿⣇⣀⣀⣀⣀⣀⣀⣀⢀⢀⢀⢀⢀⢀⢀⡈⢀⢀⣼⣿⢏⢀⢀⢀⢀⢀⢀⣼⣿⡏⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⡘⣿⣿⢀⢀⢀
// ⢀⢀⢀⣿⣿⢀⢀⡈⢿⣿⡄⢀⢀⢸⣿⡇⢀⢀⢀⢀⣿⣿⣿⢿⢿⢿⢿⢿⢿⢿⢇⢀⢀⢀⢀⢀⢀⢀⢠⣾⣿⣧⡀⢀⢀⢀⢀⢀⢀⣿⣿⡇⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⣿⣿⢀⢀⢀
// ⢀⢀⢀⣿⣿⢀⢀⢀⡈⢿⣿⢀⢀⢸⣿⡇⢀⢀⢀⢀⣿⣿⡇⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⣰⣿⡟⡛⣿⣷⡄⢀⢀⢀⢀⢀⢿⣿⣇⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⣿⣿⢀⢀⢀
// ⢀⢀⢀⣿⣿⢀⢀⢀⢀⡈⢿⢀⢀⢸⣿⡇⢀⢀⢀⢀⡛⡟⢁⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⢀⣼⣿⡟⢀⢀⡈⢿⣿⣄⢀⢀⢀⢀⡘⣿⣿⣄⢀⢀⢀⢀⢀⢀⢀⢀⢀⣼⣿⢏⢀⢀⢀
// ⢀⢀⢀⣿⣿⢀⢀⢀⢀⢀⢀⢀⢀⢸⣿⡇⢀⢀⢀⢀⢀⣀⣀⣀⣀⣀⣀⣀⣀⣀⡀⢀⢀⢀⣠⣾⡿⢃⢀⢀⢀⢀⢀⢻⣿⣧⡀⢀⢀⢀⡈⢻⣿⣷⣦⣄⢀⢀⣠⣤⣶⣿⡿⢋⢀⢀⢀⢀
// ⢀⢀⢀⢿⢿⢀⢀⢀⢀⢀⢀⢀⢀⢸⢿⢃⢀⢀⢀⢀⢻⢿⢿⢿⢿⢿⢿⢿⢿⢿⢃⢀⢀⢀⢿⡟⢁⢀⢀⢀⢀⢀⢀⢀⡙⢿⡗⢀⢀⢀⢀⢀⡈⡉⡛⡛⢀⢀⢹⡛⢋⢁⢀⢀⢀⢀⢀⢀
//
// Author: Marie GIACOMEL
// Date: 2025-10-30
// Description: Source file for the AddComponent functions
//
/////////////////////////////////////////////////////////////////////////////////////

#include <ImNexo/Elements.hpp>

#include "InspectorWindow.hpp"

namespace nexo::editor {

void searchComponentBar()
{
// TODO: Implement search bar for components
}

void addComponentPopup(const ecs::Entity entity)
{
const auto& coord = nexo::Application::m_coordinator;
const auto signature = coord->getSignature(entity);
ImGui::Text("Add component to %u", entity);
ImGui::Separator();
for (const auto& [componentType, description] : coord->getComponentDescriptions()) {
// Skip empty components or if the entity already has it
if (description->name.empty() || signature.test(componentType)) continue;
if (ImGui::Selectable(description->name.c_str())) {
LOG(NEXO_INFO, "Adding component {} to entity {}", description->name, entity);
coord->addComponentWithDefault(entity, componentType);
}
}
ImGui::Separator();
if (ImNexo::Button("Cancel", ImNexo::ButtonTypes::CANCEL)) {
PopupManager::closePopup();
}
PopupManager::endPopup();
}

void InspectorWindow::showAddComponentButton(const ecs::Entity entity)
{
if (ImGui::Button("Add Component")) {
ImGui::Text("add component to entity %u", entity);
m_popupManager.openPopup("Add Component Popup");
}

if (m_popupManager.showPopup("Add Component Popup")) {
addComponentPopup(entity);
}
}

} // namespace nexo::editor
1 change: 1 addition & 0 deletions editor/src/DocumentWindows/InspectorWindow/Init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ namespace nexo::editor {
const auto& componentDescriptions = coordinator->getComponentDescriptions();

for (const auto& [componentType, description] : componentDescriptions) {
if (description->internalComponent) continue;
registerProperty(componentType, std::make_shared<TypeErasedProperty>(*this, componentType, description));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "DocumentWindows/EntityProperties/AEntityProperty.hpp"
#include "core/scene/SceneManager.hpp"

#include <DocumentWindows/PopupManager.hpp>
#include <unordered_map>

namespace nexo::editor {
Expand Down Expand Up @@ -217,5 +218,9 @@ namespace nexo::editor {
}
m_entityProperties[type] = std::move(property);
}

void showAddComponentButton(ecs::Entity entity);

PopupManager m_popupManager;
};
}; // namespace nexo::editor
1 change: 1 addition & 0 deletions editor/src/DocumentWindows/InspectorWindow/Show.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ namespace nexo::editor {
m_entityProperties[type]->show(entity);
}
}
showAddComponentButton(entity);
}

void InspectorWindow::show()
Expand Down
2 changes: 1 addition & 1 deletion editor/src/DocumentWindows/PopupManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ namespace nexo::editor {
*/
struct TransparentHasher {
using is_transparent = void; // Required for heterogeneous lookup
std::size_t operator()(std::string_view key) const noexcept
std::size_t operator()(const std::string_view key) const noexcept
{
return std::hash<std::string_view>{}(key);
}
Expand Down
10 changes: 5 additions & 5 deletions engine/src/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ namespace nexo {
m_coordinator->setRestoreComponent<components::SpotLightComponent>();
m_coordinator->registerComponent<components::UuidComponent>();
m_coordinator->setRestoreComponent<components::UuidComponent>();
m_coordinator->registerComponent<components::PerspectiveCameraController>();
m_coordinator->registerComponent<components::PerspectiveCameraController>("Perspective Camera Controller");
m_coordinator->setRestoreComponent<components::PerspectiveCameraController>();
m_coordinator->registerComponent<components::PerspectiveCameraTarget>();
m_coordinator->registerComponent<components::PerspectiveCameraTarget>("Perspective Camera Target");
m_coordinator->setRestoreComponent<components::PerspectiveCameraTarget>();
m_coordinator->registerComponent<components::EditorCameraTag>();
m_coordinator->setRestoreComponent<components::EditorCameraTag>();
Expand All @@ -107,11 +107,11 @@ namespace nexo {
m_coordinator->registerComponent<components::ParentComponent>();
m_coordinator->registerComponent<components::ModelComponent>();
m_coordinator->registerComponent<components::BillboardComponent>();
m_coordinator->registerComponent<components::VideoComponent>();
m_coordinator->registerComponent<components::MaterialComponent>();
m_coordinator->registerComponent<components::VideoComponent>("Video");
m_coordinator->registerComponent<components::MaterialComponent>("Material");
m_coordinator->registerComponent<components::NameComponent>();
m_coordinator->registerSingletonComponent<components::RenderContext>();
m_coordinator->registerComponent<components::PhysicsBodyComponent>();
m_coordinator->registerComponent<components::PhysicsBodyComponent>("Physic Body");
}

void Application::registerWindowCallbacks() const
Expand Down
26 changes: 26 additions & 0 deletions engine/src/ecs/ComponentArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,32 @@ namespace nexo::ecs {
++m_size;
}

void TypeErasedComponentArray::insertRawWithConstructor(Entity entity, void (*constructor)(void* memoryDst))
{
if (entity >= MAX_ENTITIES) THROW_EXCEPTION(OutOfRange, entity);

ensureSparseCapacity(entity);

if (hasComponent(entity)) {
LOG(NEXO_WARN, "Entity {} already has component", entity);
return;
}

const size_t newIndex = m_size;
m_sparse[entity] = newIndex;
m_dense.push_back(entity);

// Resize component data vector if needed
const size_t requiredSize = (m_size + 1) * m_componentSize;
if (m_componentData.size() < requiredSize) {
m_componentData.resize(requiredSize);
}

// Call the constructor to initialize the component in place
constructor(m_componentData.data());
++m_size;
}

void TypeErasedComponentArray::remove(const Entity entity)
{
if (!hasComponent(entity)) THROW_EXCEPTION(ComponentNotFound, entity);
Expand Down
52 changes: 52 additions & 0 deletions engine/src/ecs/ComponentArray.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@ namespace nexo::ecs {
*/
virtual void insertRaw(Entity entity, const void* componentData) = 0;

/**
* @brief Inserts a new component for the given entity using a constructor.
*
* @param entity The entity to add the component to
* @param constructor Pointer to the constructor function or functor
* @throws OutOfRange if entity ID exceeds MAX_ENTITIES
*
* @pre The entity must be a valid entity ID
*/
virtual void insertRawWithConstructor(Entity entity, void (*constructor)(void* memoryDst)) = 0;

/**
* @brief Removes the component for the given entity.
*
Expand Down Expand Up @@ -262,6 +273,36 @@ namespace nexo::ecs {
}
}

/**
* @brief Inserts a new component for the given entity using a constructor.
*
* @param entity The entity to add the component to
* @param constructor Pointer to the constructor function or functor
* @throws OutOfRange if entity ID exceeds MAX_ENTITIES
*
* @pre The entity must be a valid entity ID
*/
void insertRawWithConstructor(Entity entity, void (*constructor)(void* memoryDst))
{
if (entity >= MAX_ENTITIES) THROW_EXCEPTION(OutOfRange, entity);

ensureSparseCapacity(entity);

if (hasComponent(entity)) {
LOG(NEXO_WARN, "Entity {} already has component: {}", entity, typeid(T).name());
return;
}

const size_t newIndex = m_size;
m_sparse[entity] = newIndex;
m_dense.push_back(entity);

// allocate new component in the array
m_componentArray.emplace_back();
constructor(&m_componentArray[newIndex]);
++m_size;
}

/**
* @brief Removes the component for the given entity.
*
Expand Down Expand Up @@ -656,6 +697,17 @@ namespace nexo::ecs {
*/
void insertRaw(Entity entity, const void* componentData) override;

/**
* @brief Inserts a new component for the given entity using a constructor.
*
* @param entity The entity to add the component to
* @param constructor Pointer to the constructor function or functor
* @throws OutOfRange if entity ID exceeds MAX_ENTITIES
*
* @pre The entity must be a valid entity ID
*/
void insertRawWithConstructor(Entity entity, void (*constructor)(void* memoryDst)) override;

/**
* @brief Removes the component for the given entity
* @param entity The entity to remove the component from
Expand Down
30 changes: 30 additions & 0 deletions engine/src/ecs/Components.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,36 @@ namespace nexo::ecs {
}
}

/**
* @brief Adds a component to an entity using type ID and constructor
*
* Adds the component using the component type ID and a constructor function,
* useful for runtime component type handling. Updates any groups that
* match the entity's new signature.
*
* @param entity The entity to add the component to
* @param componentType The type ID of the component to add
* @param constructor Pointer to the constructor function
* @param oldSignature The entity's signature before adding the component
* @param newSignature The entity's signature after adding the component
*
* @pre componentType must be a valid-registered component type
*/
void addComponentWithConstructor(const Entity entity, const ComponentType componentType,
void (*constructor)(void* memoryDst), const Signature oldSignature,
const Signature newSignature)
{
getComponentArray(componentType)->insertRawWithConstructor(entity, constructor);

for (const auto& group : std::ranges::views::values(m_groupRegistry)) {
// Check if entity qualifies now but did not qualify before.
if (((oldSignature & group->allSignature()) != group->allSignature()) &&
((newSignature & group->allSignature()) == group->allSignature())) {
group->addToGroup(entity);
}
}
}

/**
* @brief Removes a component from an entity using type ID
*
Expand Down
38 changes: 35 additions & 3 deletions engine/src/ecs/Coordinator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,11 @@ namespace nexo::ecs {
* @brief Registers a new component type within the ComponentManager.
*/
template<typename T>
void registerComponent()
void registerComponent(const std::string& displayName = "")
{
m_componentManager->registerComponent<T>();
addComponentDescription(getComponentType<T>(),
ComponentDescription{displayName, {}, true, [](void* memoryDst) { new (memoryDst) T{}; }});

m_getComponentFunctions[typeid(T)] = [this](const Entity entity) -> std::any {
return this->getComponent<T>(entity);
Expand Down Expand Up @@ -240,6 +242,12 @@ namespace nexo::ecs {
m_systemManager->entitySignatureChanged(entity, oldSignature, signature);
}

template<typename T>
void addComponent(const Entity entity) const
{
addComponent(entity, T{});
}

/**
* @brief Adds a component to an entity, updates its signature, and notifies systems.
*
Expand All @@ -264,6 +272,30 @@ namespace nexo::ecs {
m_systemManager->entitySignatureChanged(entity, oldSignature, signature);
}

/**
* @brief Adds a component to an entity using ComponentType, default-constructs it,
* updates its signature, and notifies systems.
*
* @param entity - The ID of the entity.
* @param componentType - The ID of the component type to add.
*/
void addComponentWithDefault(const Entity entity, const ComponentType componentType) const
{
Signature signature = m_entityManager->getSignature(entity);
const Signature oldSignature = signature;
signature.set(componentType, true);
const auto& componentDescription = m_componentDescriptions.at(componentType);
if (componentDescription == nullptr) {
THROW_EXCEPTION(ComponentNotRegistered);
}
m_componentManager->addComponentWithConstructor(entity, componentType, componentDescription->constructor, oldSignature,
signature);

m_entityManager->setSignature(entity, signature);

m_systemManager->entitySignatureChanged(entity, oldSignature, signature);
}

/**
* @brief Removes a component from an entity using ComponentType, updates its signature, and notifies systems.
*
Expand Down Expand Up @@ -528,7 +560,7 @@ namespace nexo::ecs {
* @brief Retrieves all registered component descriptions.
*
* @return const std::unordered_map<ComponentType, std::shared_ptr<ComponentDescription>>&
* A const reference to the map of component types to their descriptions.
* A const reference to the map of component types to their descriptions.
*/
[[nodiscard]] const std::unordered_map<ComponentType, std::shared_ptr<ComponentDescription>>&
getComponentDescriptions() const
Expand All @@ -548,7 +580,7 @@ namespace nexo::ecs {
{
auto newQuerySystem = m_systemManager->registerQuerySystem<T>(std::forward<Args>(args)...);
const std::span<const Entity> livingEntities = m_entityManager->getLivingEntities();
const Signature querySystemSignature = newQuerySystem->getSignature();
const Signature querySystemSignature = newQuerySystem->getSignature();
for (Entity entity : livingEntities) {
const Signature entitySignature = m_entityManager->getSignature(entity);
if ((entitySignature & querySystemSignature) == querySystemSignature) {
Expand Down
6 changes: 4 additions & 2 deletions engine/src/ecs/TypeErasedComponent/ComponentDescription.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
namespace nexo::ecs {

struct ComponentDescription {
std::string name; // Name of the component
std::vector<Field> fields; // List of fields in the component
std::string name; // Name of the component
std::vector<Field> fields; // List of fields in the component
bool internalComponent = false; // Indicates if the component is internal
void (*constructor)(void* memoryDst); // Pointer to the constructor function
};

} // namespace nexo::ecs
Loading