Camera and Light components
This commit is contained in:
@@ -14,10 +14,17 @@ set(EDITSCENE_SOURCES
|
||||
systems/EditorUISystem.cpp
|
||||
systems/SceneSerializer.cpp
|
||||
systems/PhysicsSystem.cpp
|
||||
systems/LightSystem.cpp
|
||||
systems/CameraSystem.cpp
|
||||
ui/TransformEditor.cpp
|
||||
ui/RenderableEditor.cpp
|
||||
ui/PhysicsColliderEditor.cpp
|
||||
ui/RigidBodyEditor.cpp
|
||||
ui/LightEditor.cpp
|
||||
ui/CameraEditor.cpp
|
||||
ui/ComponentRegistration.cpp
|
||||
components/LightModule.cpp
|
||||
components/CameraModule.cpp
|
||||
camera/EditorCamera.cpp
|
||||
gizmo/Gizmo.cpp
|
||||
physics/physics.cpp
|
||||
@@ -31,15 +38,22 @@ set(EDITSCENE_HEADERS
|
||||
components/Relationship.hpp
|
||||
components/PhysicsCollider.hpp
|
||||
components/RigidBody.hpp
|
||||
components/Light.hpp
|
||||
components/Camera.hpp
|
||||
systems/EditorUISystem.hpp
|
||||
systems/SceneSerializer.hpp
|
||||
systems/PhysicsSystem.hpp
|
||||
systems/LightSystem.hpp
|
||||
systems/CameraSystem.hpp
|
||||
ui/ComponentEditor.hpp
|
||||
ui/ComponentRegistry.hpp
|
||||
ui/ComponentRegistration.hpp
|
||||
ui/TransformEditor.hpp
|
||||
ui/RenderableEditor.hpp
|
||||
ui/PhysicsColliderEditor.hpp
|
||||
ui/RigidBodyEditor.hpp
|
||||
ui/LightEditor.hpp
|
||||
ui/CameraEditor.hpp
|
||||
camera/EditorCamera.hpp
|
||||
gizmo/Gizmo.hpp
|
||||
physics/physics.h
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "EditorApp.hpp"
|
||||
#include "systems/EditorUISystem.hpp"
|
||||
#include "systems/PhysicsSystem.hpp"
|
||||
#include "systems/LightSystem.hpp"
|
||||
#include "systems/CameraSystem.hpp"
|
||||
#include "camera/EditorCamera.hpp"
|
||||
#include "components/EntityName.hpp"
|
||||
#include "components/Transform.hpp"
|
||||
@@ -9,6 +11,8 @@
|
||||
#include "components/EditorMarker.hpp"
|
||||
#include "components/PhysicsCollider.hpp"
|
||||
#include "components/RigidBody.hpp"
|
||||
#include "components/Light.hpp"
|
||||
#include "components/Camera.hpp"
|
||||
#include <OgreRTShaderSystem.h>
|
||||
#include <imgui.h>
|
||||
|
||||
@@ -111,6 +115,12 @@ void EditorApp::setup()
|
||||
m_physicsSystem = std::make_unique<EditorPhysicsSystem>(m_world, m_sceneMgr);
|
||||
m_physicsSystem->initialize();
|
||||
m_uiSystem->setPhysicsSystem(m_physicsSystem.get());
|
||||
|
||||
// Setup light system
|
||||
m_lightSystem = std::make_unique<EditorLightSystem>(m_world, m_sceneMgr);
|
||||
|
||||
// Setup camera system
|
||||
m_cameraSystem = std::make_unique<EditorCameraSystem>(m_world, m_sceneMgr);
|
||||
|
||||
// Add default entities to UI cache
|
||||
for (auto &e : m_defaultEntities) {
|
||||
@@ -143,6 +153,10 @@ void EditorApp::setupECS()
|
||||
// Register physics components
|
||||
m_world.component<PhysicsColliderComponent>();
|
||||
m_world.component<RigidBodyComponent>();
|
||||
|
||||
// Register light and camera components
|
||||
m_world.component<LightComponent>();
|
||||
m_world.component<CameraComponent>();
|
||||
}
|
||||
|
||||
void EditorApp::createDefaultEntities()
|
||||
@@ -270,6 +284,16 @@ bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt)
|
||||
if (m_physicsSystem) {
|
||||
m_physicsSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
// Update lights
|
||||
if (m_lightSystem) {
|
||||
m_lightSystem->update();
|
||||
}
|
||||
|
||||
// Update cameras
|
||||
if (m_cameraSystem) {
|
||||
m_cameraSystem->update();
|
||||
}
|
||||
|
||||
// Don't call base class - it crashes when iterating input listeners
|
||||
return true;
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
class EditorUISystem;
|
||||
class EditorCamera;
|
||||
class EditorPhysicsSystem;
|
||||
class EditorLightSystem;
|
||||
class EditorCameraSystem;
|
||||
|
||||
/**
|
||||
* RenderTargetListener for ImGui frame management
|
||||
@@ -80,6 +82,8 @@ private:
|
||||
std::unique_ptr<EditorCamera> m_camera;
|
||||
std::unique_ptr<ImGuiRenderListener> m_imguiListener;
|
||||
std::unique_ptr<EditorPhysicsSystem> m_physicsSystem;
|
||||
std::unique_ptr<EditorLightSystem> m_lightSystem;
|
||||
std::unique_ptr<EditorCameraSystem> m_cameraSystem;
|
||||
|
||||
// State
|
||||
uint16_t m_currentModifiers;
|
||||
|
||||
37
src/features/editScene/components/Camera.hpp
Normal file
37
src/features/editScene/components/Camera.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef EDITSCENE_CAMERA_HPP
|
||||
#define EDITSCENE_CAMERA_HPP
|
||||
#pragma once
|
||||
|
||||
#include <Ogre.h>
|
||||
|
||||
/**
|
||||
* Camera component - attaches an Ogre::Camera to the entity's SceneNode
|
||||
*/
|
||||
struct CameraComponent {
|
||||
// Camera properties
|
||||
float fovY = 45.0f; // Vertical field of view in degrees
|
||||
float nearClip = 0.1f; // Near clip distance
|
||||
float farClip = 1000.0f; // Far clip distance
|
||||
float aspectRatio = 16.0f / 9.0f; // Aspect ratio (width/height)
|
||||
|
||||
// For orthographic camera
|
||||
bool orthographic = false;
|
||||
float orthoWidth = 10.0f; // Width for orthographic view
|
||||
float orthoHeight = 10.0f; // Height for orthographic view
|
||||
|
||||
// The Ogre camera object (created by CameraSystem)
|
||||
Ogre::Camera* camera = nullptr;
|
||||
|
||||
// For preview (optional RTT - created on demand)
|
||||
Ogre::RenderTarget* previewTarget = nullptr;
|
||||
Ogre::TexturePtr previewTexture;
|
||||
bool showPreview = false;
|
||||
int previewWidth = 400;
|
||||
int previewHeight = 300;
|
||||
|
||||
void markDirty() { needsRebuild = true; }
|
||||
bool needsRebuild = true;
|
||||
bool needsPreviewUpdate = false;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_CAMERA_HPP
|
||||
53
src/features/editScene/components/CameraModule.cpp
Normal file
53
src/features/editScene/components/CameraModule.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "Camera.hpp"
|
||||
#include "Transform.hpp"
|
||||
#include "../ui/ComponentRegistration.hpp"
|
||||
#include "../ui/CameraEditor.hpp"
|
||||
#include "../systems/CameraSystem.hpp"
|
||||
|
||||
// Register Camera component
|
||||
REGISTER_COMPONENT("Camera", CameraComponent, CameraEditor)
|
||||
{
|
||||
registry.registerComponent<CameraComponent>(
|
||||
"Camera",
|
||||
std::make_unique<CameraEditor>(sceneMgr),
|
||||
// Adder
|
||||
[sceneMgr](flecs::entity e) {
|
||||
if (!e.has<CameraComponent>()) {
|
||||
// Camera requires Transform
|
||||
if (!e.has<TransformComponent>()) {
|
||||
// Auto-add transform if missing
|
||||
TransformComponent transform;
|
||||
transform.node = sceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
e.set<TransformComponent>(transform);
|
||||
}
|
||||
e.set<CameraComponent>({});
|
||||
}
|
||||
},
|
||||
// Remover
|
||||
[sceneMgr](flecs::entity e) {
|
||||
if (e.has<CameraComponent>()) {
|
||||
auto& camera = e.get_mut<CameraComponent>();
|
||||
// Clean up Ogre camera and preview resources
|
||||
if (camera.camera) {
|
||||
// Clean up preview
|
||||
if (camera.previewTarget) {
|
||||
camera.previewTarget->removeAllListeners();
|
||||
}
|
||||
if (camera.previewTexture) {
|
||||
Ogre::TextureManager::getSingleton().remove(
|
||||
camera.previewTexture->getName());
|
||||
}
|
||||
|
||||
// Detach and destroy camera
|
||||
Ogre::SceneNode* parent = camera.camera->getParentSceneNode();
|
||||
if (parent) {
|
||||
parent->detachObject(camera.camera);
|
||||
}
|
||||
sceneMgr->destroyCamera(camera.camera);
|
||||
camera.camera = nullptr;
|
||||
}
|
||||
e.remove<CameraComponent>();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
48
src/features/editScene/components/Light.hpp
Normal file
48
src/features/editScene/components/Light.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef EDITSCENE_LIGHT_HPP
|
||||
#define EDITSCENE_LIGHT_HPP
|
||||
#pragma once
|
||||
|
||||
#include <Ogre.h>
|
||||
|
||||
/**
|
||||
* Light component - attaches an Ogre::Light to the entity's SceneNode
|
||||
*/
|
||||
struct LightComponent {
|
||||
enum class LightType {
|
||||
Point, // Omnidirectional light
|
||||
Directional, // Parallel rays (sun/moon)
|
||||
Spotlight // Cone-shaped light
|
||||
};
|
||||
|
||||
LightType lightType = LightType::Point;
|
||||
|
||||
// Common properties
|
||||
Ogre::ColourValue diffuseColor{0.8f, 0.8f, 0.8f};
|
||||
Ogre::ColourValue specularColor{0.5f, 0.5f, 0.5f};
|
||||
float intensity = 1.0f;
|
||||
|
||||
// Attenuation (for Point and Spot)
|
||||
float range = 100.0f;
|
||||
float constantAttenuation = 1.0f;
|
||||
float linearAttenuation = 0.0f;
|
||||
float quadraticAttenuation = 0.0f;
|
||||
|
||||
// Spotlight specific
|
||||
float spotlightInnerAngle = 30.0f; // Degrees
|
||||
float spotlightOuterAngle = 45.0f; // Degrees
|
||||
float spotlightFalloff = 1.0f;
|
||||
|
||||
// Direction (for Directional and Spot, relative to node orientation)
|
||||
Ogre::Vector3 direction{0, -1, 0};
|
||||
|
||||
// Cast shadows
|
||||
bool castShadows = true;
|
||||
|
||||
// The Ogre light object (created by LightSystem)
|
||||
Ogre::Light* light = nullptr;
|
||||
|
||||
void markDirty() { needsRebuild = true; }
|
||||
bool needsRebuild = true;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_LIGHT_HPP
|
||||
43
src/features/editScene/components/LightModule.cpp
Normal file
43
src/features/editScene/components/LightModule.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "Light.hpp"
|
||||
#include "Transform.hpp"
|
||||
#include "../ui/ComponentRegistration.hpp"
|
||||
#include "../ui/LightEditor.hpp"
|
||||
#include "../systems/LightSystem.hpp"
|
||||
|
||||
// Register Light component
|
||||
REGISTER_COMPONENT("Light", LightComponent, LightEditor)
|
||||
{
|
||||
registry.registerComponent<LightComponent>(
|
||||
"Light",
|
||||
std::make_unique<LightEditor>(),
|
||||
// Adder
|
||||
[sceneMgr](flecs::entity e) {
|
||||
if (!e.has<LightComponent>()) {
|
||||
// Light requires Transform
|
||||
if (!e.has<TransformComponent>()) {
|
||||
// Auto-add transform if missing
|
||||
TransformComponent transform;
|
||||
transform.node = sceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
e.set<TransformComponent>(transform);
|
||||
}
|
||||
e.set<LightComponent>({});
|
||||
}
|
||||
},
|
||||
// Remover
|
||||
[sceneMgr](flecs::entity e) {
|
||||
if (e.has<LightComponent>()) {
|
||||
auto& light = e.get_mut<LightComponent>();
|
||||
// Clean up Ogre light
|
||||
if (light.light) {
|
||||
Ogre::SceneNode* parent = light.light->getParentSceneNode();
|
||||
if (parent) {
|
||||
parent->detachObject(light.light);
|
||||
}
|
||||
sceneMgr->destroyLight(light.light);
|
||||
light.light = nullptr;
|
||||
}
|
||||
e.remove<LightComponent>();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
118
src/features/editScene/systems/CameraSystem.cpp
Normal file
118
src/features/editScene/systems/CameraSystem.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "CameraSystem.hpp"
|
||||
#include "../components/Camera.hpp"
|
||||
#include "../components/Transform.hpp"
|
||||
#include <OgreLogManager.h>
|
||||
|
||||
EditorCameraSystem::EditorCameraSystem(flecs::world& world, Ogre::SceneManager* sceneMgr)
|
||||
: m_world(world)
|
||||
, m_sceneMgr(sceneMgr)
|
||||
, m_cameraQuery(world.query<CameraComponent, TransformComponent>())
|
||||
{
|
||||
}
|
||||
|
||||
EditorCameraSystem::~EditorCameraSystem() = default;
|
||||
|
||||
void EditorCameraSystem::update()
|
||||
{
|
||||
m_cameraQuery.each([&](flecs::entity entity, CameraComponent& camera, TransformComponent& transform) {
|
||||
if (camera.needsRebuild || !camera.camera) {
|
||||
updateCamera(entity, camera, transform);
|
||||
}
|
||||
|
||||
// Update preview if needed
|
||||
if (camera.showPreview && camera.needsPreviewUpdate && camera.previewTarget) {
|
||||
camera.previewTarget->update();
|
||||
camera.needsPreviewUpdate = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EditorCameraSystem::processAllCameras()
|
||||
{
|
||||
m_cameraQuery.each([&](flecs::entity entity, CameraComponent& camera, TransformComponent& transform) {
|
||||
updateCamera(entity, camera, transform);
|
||||
});
|
||||
}
|
||||
|
||||
Ogre::Camera* EditorCameraSystem::getActiveCamera() const
|
||||
{
|
||||
Ogre::Camera* result = nullptr;
|
||||
m_cameraQuery.each([&](flecs::entity entity, CameraComponent& camera, TransformComponent& transform) {
|
||||
if (!result && camera.camera) {
|
||||
result = camera.camera;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EditorCameraSystem::updateCamera(flecs::entity entity, CameraComponent& camera,
|
||||
TransformComponent& transform)
|
||||
{
|
||||
// Remove existing camera if any
|
||||
if (camera.camera) {
|
||||
removeCamera(camera);
|
||||
}
|
||||
|
||||
// Need a valid scene node to attach the camera
|
||||
if (!transform.node) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"CameraSystem: Cannot create camera - entity has no SceneNode");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create unique name for the camera
|
||||
std::string cameraName = "Camera_" + std::to_string(entity.id());
|
||||
|
||||
// Create the Ogre camera
|
||||
camera.camera = m_sceneMgr->createCamera(cameraName);
|
||||
|
||||
// Attach to the entity's scene node
|
||||
transform.node->attachObject(camera.camera);
|
||||
|
||||
// Set projection type
|
||||
if (camera.orthographic) {
|
||||
camera.camera->setProjectionType(Ogre::PT_ORTHOGRAPHIC);
|
||||
camera.camera->setOrthoWindow(camera.orthoWidth, camera.orthoHeight);
|
||||
} else {
|
||||
camera.camera->setProjectionType(Ogre::PT_PERSPECTIVE);
|
||||
camera.camera->setFOVy(Ogre::Degree(camera.fovY));
|
||||
}
|
||||
|
||||
// Set clip distances
|
||||
camera.camera->setNearClipDistance(camera.nearClip);
|
||||
camera.camera->setFarClipDistance(camera.farClip);
|
||||
|
||||
// Set aspect ratio
|
||||
camera.camera->setAspectRatio(camera.aspectRatio);
|
||||
|
||||
camera.needsRebuild = false;
|
||||
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"CameraSystem: Created " +
|
||||
std::string(camera.orthographic ? "orthographic" : "perspective") +
|
||||
" camera '" + cameraName + "'");
|
||||
}
|
||||
|
||||
void EditorCameraSystem::removeCamera(CameraComponent& camera)
|
||||
{
|
||||
if (camera.camera) {
|
||||
// Clean up preview resources
|
||||
if (camera.previewTarget) {
|
||||
camera.previewTarget->removeAllListeners();
|
||||
camera.previewTarget = nullptr;
|
||||
}
|
||||
if (camera.previewTexture) {
|
||||
Ogre::TextureManager::getSingleton().remove(camera.previewTexture->getName());
|
||||
camera.previewTexture.reset();
|
||||
}
|
||||
|
||||
// Detach from parent node first
|
||||
Ogre::SceneNode* parent = camera.camera->getParentSceneNode();
|
||||
if (parent) {
|
||||
parent->detachObject(camera.camera);
|
||||
}
|
||||
|
||||
m_sceneMgr->destroyCamera(camera.camera);
|
||||
camera.camera = nullptr;
|
||||
}
|
||||
}
|
||||
40
src/features/editScene/systems/CameraSystem.hpp
Normal file
40
src/features/editScene/systems/CameraSystem.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef EDITSCENE_CAMERASYSTEM_HPP
|
||||
#define EDITSCENE_CAMERASYSTEM_HPP
|
||||
#pragma once
|
||||
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
|
||||
/**
|
||||
* Camera system - manages Ogre::Camera objects for entities with CameraComponent
|
||||
*/
|
||||
class EditorCameraSystem {
|
||||
public:
|
||||
EditorCameraSystem(flecs::world& world, Ogre::SceneManager* sceneMgr);
|
||||
~EditorCameraSystem();
|
||||
|
||||
// Update cameras (process dirty components)
|
||||
void update();
|
||||
|
||||
// Process all cameras immediately (e.g., after scene load)
|
||||
void processAllCameras();
|
||||
|
||||
// Get the active editor camera (first camera found, or nullptr)
|
||||
Ogre::Camera* getActiveCamera() const;
|
||||
|
||||
private:
|
||||
// Create/update a camera from component
|
||||
void updateCamera(flecs::entity entity, class CameraComponent& camera,
|
||||
class TransformComponent& transform);
|
||||
|
||||
// Remove a camera
|
||||
void removeCamera(CameraComponent& camera);
|
||||
|
||||
flecs::world& m_world;
|
||||
Ogre::SceneManager* m_sceneMgr;
|
||||
|
||||
// Query for entities with Camera and Transform
|
||||
flecs::query<class CameraComponent, class TransformComponent> m_cameraQuery;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_CAMERASYSTEM_HPP
|
||||
@@ -5,10 +5,13 @@
|
||||
#include "../components/EditorMarker.hpp"
|
||||
#include "../components/PhysicsCollider.hpp"
|
||||
#include "../components/RigidBody.hpp"
|
||||
#include "../components/Light.hpp"
|
||||
#include "../components/Camera.hpp"
|
||||
#include "../ui/TransformEditor.hpp"
|
||||
#include "../ui/RenderableEditor.hpp"
|
||||
#include "../ui/PhysicsColliderEditor.hpp"
|
||||
#include "../ui/RigidBodyEditor.hpp"
|
||||
#include "../ui/ComponentRegistration.hpp"
|
||||
#include "PhysicsSystem.hpp"
|
||||
#include <imgui.h>
|
||||
#include <algorithm>
|
||||
@@ -143,6 +146,15 @@ void EditorUISystem::registerComponentEditors()
|
||||
e.remove<PhysicsColliderComponent>();
|
||||
}
|
||||
});
|
||||
|
||||
// Register modular components (Light, Camera, etc.)
|
||||
registerModularComponents();
|
||||
}
|
||||
|
||||
void EditorUISystem::registerModularComponents()
|
||||
{
|
||||
// This calls all component modules registered via REGISTER_COMPONENT macro
|
||||
ComponentRegistration::registerAll(m_componentRegistry, m_sceneMgr, m_physicsSystem);
|
||||
}
|
||||
|
||||
void EditorUISystem::update()
|
||||
@@ -341,6 +353,10 @@ void EditorUISystem::renderEntityNode(flecs::entity entity, int depth)
|
||||
indicators += " [T]";
|
||||
if (entity.has<RenderableComponent>())
|
||||
indicators += " [R]";
|
||||
if (entity.has<LightComponent>())
|
||||
indicators += " [L]";
|
||||
if (entity.has<CameraComponent>())
|
||||
indicators += " [Cam]";
|
||||
if (entity.has<RigidBodyComponent>())
|
||||
indicators += " [RB]";
|
||||
if (entity.has<PhysicsColliderComponent>())
|
||||
@@ -453,35 +469,53 @@ void EditorUISystem::renderComponentList(flecs::entity entity)
|
||||
// Render component editors
|
||||
ImGui::BeginChild("Components", ImVec2(0, 0), true);
|
||||
|
||||
// Dynamically render all components the entity has
|
||||
int componentCount = 0;
|
||||
|
||||
// Render Transform first if present (it's the base component)
|
||||
if (entity.has<TransformComponent>()) {
|
||||
auto &transform = entity.get_mut<TransformComponent>();
|
||||
m_componentRegistry.render<TransformComponent>(entity,
|
||||
transform);
|
||||
m_componentRegistry.render<TransformComponent>(entity, transform);
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
// Render Renderable
|
||||
if (entity.has<RenderableComponent>()) {
|
||||
auto &renderable = entity.get_mut<RenderableComponent>();
|
||||
m_componentRegistry.render<RenderableComponent>(entity,
|
||||
renderable);
|
||||
m_componentRegistry.render<RenderableComponent>(entity, renderable);
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
// Render Light if present
|
||||
if (entity.has<LightComponent>()) {
|
||||
auto &light = entity.get_mut<LightComponent>();
|
||||
m_componentRegistry.render<LightComponent>(entity, light);
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
// Render Camera if present
|
||||
if (entity.has<CameraComponent>()) {
|
||||
auto &camera = entity.get_mut<CameraComponent>();
|
||||
m_componentRegistry.render<CameraComponent>(entity, camera);
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
// Render RigidBody
|
||||
if (entity.has<RigidBodyComponent>()) {
|
||||
auto &rigidBody = entity.get_mut<RigidBodyComponent>();
|
||||
m_componentRegistry.render<RigidBodyComponent>(entity,
|
||||
rigidBody);
|
||||
m_componentRegistry.render<RigidBodyComponent>(entity, rigidBody);
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
// Render PhysicsCollider
|
||||
if (entity.has<PhysicsColliderComponent>()) {
|
||||
auto &collider = entity.get_mut<PhysicsColliderComponent>();
|
||||
m_componentRegistry.render<PhysicsColliderComponent>(entity,
|
||||
collider);
|
||||
m_componentRegistry.render<PhysicsColliderComponent>(entity, collider);
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
// Show message if no components
|
||||
if (!entity.has<TransformComponent>() &&
|
||||
!entity.has<RenderableComponent>() &&
|
||||
!entity.has<RigidBodyComponent>() &&
|
||||
!entity.has<PhysicsColliderComponent>()) {
|
||||
if (componentCount == 0) {
|
||||
ImGui::TextDisabled("No components");
|
||||
ImGui::Text("Click 'Add Component' to add components");
|
||||
}
|
||||
@@ -495,42 +529,19 @@ void EditorUISystem::renderAddComponentMenu(flecs::entity entity)
|
||||
ImGui::Text("Add Component:");
|
||||
ImGui::Separator();
|
||||
|
||||
bool hasTransform = entity.has<TransformComponent>();
|
||||
bool hasRenderable = entity.has<RenderableComponent>();
|
||||
bool hasRigidBody = entity.has<RigidBodyComponent>();
|
||||
bool hasCollider = entity.has<PhysicsColliderComponent>();
|
||||
|
||||
if (!hasTransform) {
|
||||
if (ImGui::MenuItem("Transform")) {
|
||||
m_componentRegistry
|
||||
.addComponent<TransformComponent>(
|
||||
entity);
|
||||
// Dynamically list all registered components that the entity doesn't have
|
||||
bool hasAny = false;
|
||||
m_componentRegistry.forEach([&](const std::type_index& type, const ComponentRegistry::ComponentInfo& info) {
|
||||
if (!m_componentRegistry.entityHasComponent(entity, type)) {
|
||||
hasAny = true;
|
||||
if (ImGui::MenuItem(info.name)) {
|
||||
m_componentRegistry.addComponentByType(entity, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasRenderable) {
|
||||
if (ImGui::MenuItem("Renderable")) {
|
||||
m_componentRegistry
|
||||
.addComponent<RenderableComponent>(
|
||||
entity);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Physics:");
|
||||
|
||||
if (!hasRigidBody) {
|
||||
if (ImGui::MenuItem("Rigid Body")) {
|
||||
m_componentRegistry
|
||||
.addComponent<RigidBodyComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasCollider) {
|
||||
if (ImGui::MenuItem("Physics Collider")) {
|
||||
m_componentRegistry
|
||||
.addComponent<PhysicsColliderComponent>(entity);
|
||||
}
|
||||
if (!hasAny) {
|
||||
ImGui::TextDisabled("All components added");
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
@@ -539,51 +550,26 @@ void EditorUISystem::renderAddComponentMenu(flecs::entity entity)
|
||||
|
||||
void EditorUISystem::renderRemoveComponentMenu(flecs::entity entity)
|
||||
{
|
||||
{
|
||||
if (ImGui::BeginPopup("RemoveComponentMenu")) {
|
||||
ImGui::Text("Remove Component:");
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginPopup("RemoveComponentMenu")) {
|
||||
ImGui::Text("Remove Component:");
|
||||
ImGui::Separator();
|
||||
|
||||
bool hasAny = false;
|
||||
|
||||
if (entity.has<TransformComponent>()) {
|
||||
// Dynamically list all registered components that the entity has
|
||||
bool hasAny = false;
|
||||
m_componentRegistry.forEach([&](const std::type_index& type, const ComponentRegistry::ComponentInfo& info) {
|
||||
if (m_componentRegistry.entityHasComponent(entity, type)) {
|
||||
hasAny = true;
|
||||
if (ImGui::MenuItem("Transform")) {
|
||||
m_componentRegistry.removeComponent<
|
||||
TransformComponent>(entity);
|
||||
if (ImGui::MenuItem(info.name)) {
|
||||
m_componentRegistry.removeComponentByType(entity, type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (entity.has<RenderableComponent>()) {
|
||||
hasAny = true;
|
||||
if (ImGui::MenuItem("Renderable")) {
|
||||
m_componentRegistry.removeComponent<
|
||||
RenderableComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.has<RigidBodyComponent>()) {
|
||||
hasAny = true;
|
||||
if (ImGui::MenuItem("Rigid Body")) {
|
||||
m_componentRegistry.removeComponent<
|
||||
RigidBodyComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.has<PhysicsColliderComponent>()) {
|
||||
hasAny = true;
|
||||
if (ImGui::MenuItem("Physics Collider")) {
|
||||
m_componentRegistry.removeComponent<
|
||||
PhysicsColliderComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasAny) {
|
||||
ImGui::TextDisabled("No components to remove");
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
if (!hasAny) {
|
||||
ImGui::TextDisabled("No components to remove");
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ private:
|
||||
|
||||
// Helper functions
|
||||
void registerComponentEditors();
|
||||
void registerModularComponents();
|
||||
flecs::entity findEntityParent(flecs::entity entity);
|
||||
std::vector<flecs::entity> getEntityChildren(flecs::entity entity);
|
||||
bool isDescendantOf(flecs::entity potentialChild,
|
||||
|
||||
123
src/features/editScene/systems/LightSystem.cpp
Normal file
123
src/features/editScene/systems/LightSystem.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "LightSystem.hpp"
|
||||
#include "../components/Light.hpp"
|
||||
#include "../components/Transform.hpp"
|
||||
#include <OgreLogManager.h>
|
||||
|
||||
EditorLightSystem::EditorLightSystem(flecs::world& world, Ogre::SceneManager* sceneMgr)
|
||||
: m_world(world)
|
||||
, m_sceneMgr(sceneMgr)
|
||||
, m_lightQuery(world.query<LightComponent, TransformComponent>())
|
||||
{
|
||||
}
|
||||
|
||||
EditorLightSystem::~EditorLightSystem() = default;
|
||||
|
||||
void EditorLightSystem::update()
|
||||
{
|
||||
m_lightQuery.each([&](flecs::entity entity, LightComponent& light, TransformComponent& transform) {
|
||||
if (light.needsRebuild || !light.light) {
|
||||
updateLight(entity, light, transform);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EditorLightSystem::processAllLights()
|
||||
{
|
||||
m_lightQuery.each([&](flecs::entity entity, LightComponent& light, TransformComponent& transform) {
|
||||
updateLight(entity, light, transform);
|
||||
});
|
||||
}
|
||||
|
||||
void EditorLightSystem::updateLight(flecs::entity entity, LightComponent& light,
|
||||
TransformComponent& transform)
|
||||
{
|
||||
// Remove existing light if any
|
||||
if (light.light) {
|
||||
removeLight(light);
|
||||
}
|
||||
|
||||
// Need a valid scene node to attach the light
|
||||
if (!transform.node) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"LightSystem: Cannot create light - entity has no SceneNode");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create unique name for the light
|
||||
std::string lightName = "Light_" + std::to_string(entity.id());
|
||||
|
||||
// Create the Ogre light
|
||||
light.light = m_sceneMgr->createLight(lightName);
|
||||
|
||||
// Attach to the entity's scene node
|
||||
transform.node->attachObject(light.light);
|
||||
|
||||
// Set light type
|
||||
switch (light.lightType) {
|
||||
case LightComponent::LightType::Point:
|
||||
light.light->setType(Ogre::Light::LT_POINT);
|
||||
break;
|
||||
case LightComponent::LightType::Directional:
|
||||
light.light->setType(Ogre::Light::LT_DIRECTIONAL);
|
||||
break;
|
||||
case LightComponent::LightType::Spotlight:
|
||||
light.light->setType(Ogre::Light::LT_SPOTLIGHT);
|
||||
break;
|
||||
}
|
||||
|
||||
// Set colors (apply intensity)
|
||||
light.light->setDiffuseColour(light.diffuseColor * light.intensity);
|
||||
light.light->setSpecularColour(light.specularColor * light.intensity);
|
||||
|
||||
// Set attenuation for point/spot lights
|
||||
if (light.lightType == LightComponent::LightType::Point ||
|
||||
light.lightType == LightComponent::LightType::Spotlight) {
|
||||
light.light->setAttenuation(light.range,
|
||||
light.constantAttenuation,
|
||||
light.linearAttenuation,
|
||||
light.quadraticAttenuation);
|
||||
}
|
||||
|
||||
// Set direction for directional/spot lights by rotating the node
|
||||
if (light.lightType == LightComponent::LightType::Directional ||
|
||||
light.lightType == LightComponent::LightType::Spotlight) {
|
||||
// Convert direction to orientation - the light's default direction is typically (0,0,-1) or (0,-1,0)
|
||||
// We need to orient the node to point in the desired direction
|
||||
Ogre::Vector3 defaultDir(0, -1, 0); // Ogre lights typically point down by default
|
||||
Ogre::Quaternion rot = defaultDir.getRotationTo(light.direction);
|
||||
transform.node->setOrientation(rot);
|
||||
}
|
||||
|
||||
// Set spotlight parameters
|
||||
if (light.lightType == LightComponent::LightType::Spotlight) {
|
||||
light.light->setSpotlightRange(
|
||||
Ogre::Degree(light.spotlightInnerAngle),
|
||||
Ogre::Degree(light.spotlightOuterAngle),
|
||||
light.spotlightFalloff);
|
||||
}
|
||||
|
||||
// Set shadow casting
|
||||
light.light->setCastShadows(light.castShadows);
|
||||
|
||||
light.needsRebuild = false;
|
||||
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"LightSystem: Created " +
|
||||
std::string(light.lightType == LightComponent::LightType::Point ? "point" :
|
||||
light.lightType == LightComponent::LightType::Directional ? "directional" : "spot") +
|
||||
" light '" + lightName + "'");
|
||||
}
|
||||
|
||||
void EditorLightSystem::removeLight(LightComponent& light)
|
||||
{
|
||||
if (light.light) {
|
||||
// Detach from parent node first
|
||||
Ogre::SceneNode* parent = light.light->getParentSceneNode();
|
||||
if (parent) {
|
||||
parent->detachObject(light.light);
|
||||
}
|
||||
|
||||
m_sceneMgr->destroyLight(light.light);
|
||||
light.light = nullptr;
|
||||
}
|
||||
}
|
||||
37
src/features/editScene/systems/LightSystem.hpp
Normal file
37
src/features/editScene/systems/LightSystem.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef EDITSCENE_LIGHTSYSTEM_HPP
|
||||
#define EDITSCENE_LIGHTSYSTEM_HPP
|
||||
#pragma once
|
||||
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
|
||||
/**
|
||||
* Light system - manages Ogre::Light objects for entities with LightComponent
|
||||
*/
|
||||
class EditorLightSystem {
|
||||
public:
|
||||
EditorLightSystem(flecs::world& world, Ogre::SceneManager* sceneMgr);
|
||||
~EditorLightSystem();
|
||||
|
||||
// Update lights (process dirty components)
|
||||
void update();
|
||||
|
||||
// Process all lights immediately (e.g., after scene load)
|
||||
void processAllLights();
|
||||
|
||||
private:
|
||||
// Create/update a light from component
|
||||
void updateLight(flecs::entity entity, class LightComponent& light,
|
||||
class TransformComponent& transform);
|
||||
|
||||
// Remove a light
|
||||
void removeLight(LightComponent& light);
|
||||
|
||||
flecs::world& m_world;
|
||||
Ogre::SceneManager* m_sceneMgr;
|
||||
|
||||
// Query for entities with Light and Transform
|
||||
flecs::query<class LightComponent, class TransformComponent> m_lightQuery;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_LIGHTSYSTEM_HPP
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "../components/EditorMarker.hpp"
|
||||
#include "../components/RigidBody.hpp"
|
||||
#include "../components/PhysicsCollider.hpp"
|
||||
#include "../components/Light.hpp"
|
||||
#include "../components/Camera.hpp"
|
||||
#include "EditorUISystem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -123,6 +125,14 @@ nlohmann::json SceneSerializer::serializeEntity(flecs::entity entity)
|
||||
json["collider"] = serializeCollider(entity);
|
||||
}
|
||||
|
||||
if (entity.has<LightComponent>()) {
|
||||
json["light"] = serializeLight(entity);
|
||||
}
|
||||
|
||||
if (entity.has<CameraComponent>()) {
|
||||
json["camera"] = serializeCamera(entity);
|
||||
}
|
||||
|
||||
// Serialize children
|
||||
json["children"] = nlohmann::json::array();
|
||||
entity.children([&](flecs::entity child) {
|
||||
@@ -174,6 +184,14 @@ void SceneSerializer::deserializeEntity(const nlohmann::json& json, flecs::entit
|
||||
deserializeCollider(entity, json["collider"]);
|
||||
}
|
||||
|
||||
if (json.contains("light")) {
|
||||
deserializeLight(entity, json["light"]);
|
||||
}
|
||||
|
||||
if (json.contains("camera")) {
|
||||
deserializeCamera(entity, json["camera"]);
|
||||
}
|
||||
|
||||
// Add to UI system if provided
|
||||
if (uiSystem) {
|
||||
uiSystem->addEntity(entity);
|
||||
@@ -313,6 +331,80 @@ nlohmann::json SceneSerializer::serializeCollider(flecs::entity entity)
|
||||
return json;
|
||||
}
|
||||
|
||||
nlohmann::json SceneSerializer::serializeLight(flecs::entity entity)
|
||||
{
|
||||
auto& light = entity.get<LightComponent>();
|
||||
nlohmann::json json;
|
||||
|
||||
// Serialize light type
|
||||
switch (light.lightType) {
|
||||
case LightComponent::LightType::Point:
|
||||
json["lightType"] = "point";
|
||||
break;
|
||||
case LightComponent::LightType::Directional:
|
||||
json["lightType"] = "directional";
|
||||
break;
|
||||
case LightComponent::LightType::Spotlight:
|
||||
json["lightType"] = "spotlight";
|
||||
break;
|
||||
}
|
||||
|
||||
// Colors
|
||||
json["diffuseColor"] = {
|
||||
{"r", light.diffuseColor.r},
|
||||
{"g", light.diffuseColor.g},
|
||||
{"b", light.diffuseColor.b},
|
||||
{"a", light.diffuseColor.a}
|
||||
};
|
||||
|
||||
json["specularColor"] = {
|
||||
{"r", light.specularColor.r},
|
||||
{"g", light.specularColor.g},
|
||||
{"b", light.specularColor.b},
|
||||
{"a", light.specularColor.a}
|
||||
};
|
||||
|
||||
json["intensity"] = light.intensity;
|
||||
|
||||
// Attenuation
|
||||
json["range"] = light.range;
|
||||
json["constantAttenuation"] = light.constantAttenuation;
|
||||
json["linearAttenuation"] = light.linearAttenuation;
|
||||
json["quadraticAttenuation"] = light.quadraticAttenuation;
|
||||
|
||||
// Spotlight settings
|
||||
json["spotlightInnerAngle"] = light.spotlightInnerAngle;
|
||||
json["spotlightOuterAngle"] = light.spotlightOuterAngle;
|
||||
json["spotlightFalloff"] = light.spotlightFalloff;
|
||||
|
||||
// Direction
|
||||
json["direction"] = {
|
||||
{"x", light.direction.x},
|
||||
{"y", light.direction.y},
|
||||
{"z", light.direction.z}
|
||||
};
|
||||
|
||||
json["castShadows"] = light.castShadows;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
nlohmann::json SceneSerializer::serializeCamera(flecs::entity entity)
|
||||
{
|
||||
auto& camera = entity.get<CameraComponent>();
|
||||
nlohmann::json json;
|
||||
|
||||
json["fovY"] = camera.fovY;
|
||||
json["nearClip"] = camera.nearClip;
|
||||
json["farClip"] = camera.farClip;
|
||||
json["aspectRatio"] = camera.aspectRatio;
|
||||
json["orthographic"] = camera.orthographic;
|
||||
json["orthoWidth"] = camera.orthoWidth;
|
||||
json["orthoHeight"] = camera.orthoHeight;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
void SceneSerializer::deserializeTransform(flecs::entity entity, const nlohmann::json& json, flecs::entity parentEntity)
|
||||
{
|
||||
TransformComponent transform;
|
||||
@@ -486,3 +578,87 @@ void SceneSerializer::deserializeCollider(flecs::entity entity, const nlohmann::
|
||||
|
||||
entity.set<PhysicsColliderComponent>(collider);
|
||||
}
|
||||
|
||||
void SceneSerializer::deserializeLight(flecs::entity entity, const nlohmann::json& json)
|
||||
{
|
||||
LightComponent light;
|
||||
|
||||
// Deserialize light type
|
||||
std::string lightType = json.value("lightType", "point");
|
||||
if (lightType == "point") {
|
||||
light.lightType = LightComponent::LightType::Point;
|
||||
} else if (lightType == "directional") {
|
||||
light.lightType = LightComponent::LightType::Directional;
|
||||
} else if (lightType == "spotlight") {
|
||||
light.lightType = LightComponent::LightType::Spotlight;
|
||||
}
|
||||
|
||||
// Deserialize colors
|
||||
if (json.contains("diffuseColor")) {
|
||||
auto& col = json["diffuseColor"];
|
||||
light.diffuseColor = Ogre::ColourValue(
|
||||
col.value("r", 0.8f),
|
||||
col.value("g", 0.8f),
|
||||
col.value("b", 0.8f),
|
||||
col.value("a", 1.0f)
|
||||
);
|
||||
}
|
||||
|
||||
if (json.contains("specularColor")) {
|
||||
auto& col = json["specularColor"];
|
||||
light.specularColor = Ogre::ColourValue(
|
||||
col.value("r", 0.5f),
|
||||
col.value("g", 0.5f),
|
||||
col.value("b", 0.5f),
|
||||
col.value("a", 1.0f)
|
||||
);
|
||||
}
|
||||
|
||||
light.intensity = json.value("intensity", 1.0f);
|
||||
|
||||
// Attenuation
|
||||
light.range = json.value("range", 100.0f);
|
||||
light.constantAttenuation = json.value("constantAttenuation", 1.0f);
|
||||
light.linearAttenuation = json.value("linearAttenuation", 0.0f);
|
||||
light.quadraticAttenuation = json.value("quadraticAttenuation", 0.0f);
|
||||
|
||||
// Spotlight settings
|
||||
light.spotlightInnerAngle = json.value("spotlightInnerAngle", 30.0f);
|
||||
light.spotlightOuterAngle = json.value("spotlightOuterAngle", 45.0f);
|
||||
light.spotlightFalloff = json.value("spotlightFalloff", 1.0f);
|
||||
|
||||
// Direction
|
||||
if (json.contains("direction")) {
|
||||
auto& dir = json["direction"];
|
||||
light.direction = Ogre::Vector3(
|
||||
dir.value("x", 0.0f),
|
||||
dir.value("y", -1.0f),
|
||||
dir.value("z", 0.0f)
|
||||
);
|
||||
}
|
||||
|
||||
light.castShadows = json.value("castShadows", true);
|
||||
|
||||
// Mark as dirty so light system will create the light
|
||||
light.needsRebuild = true;
|
||||
|
||||
entity.set<LightComponent>(light);
|
||||
}
|
||||
|
||||
void SceneSerializer::deserializeCamera(flecs::entity entity, const nlohmann::json& json)
|
||||
{
|
||||
CameraComponent camera;
|
||||
|
||||
camera.fovY = json.value("fovY", 45.0f);
|
||||
camera.nearClip = json.value("nearClip", 0.1f);
|
||||
camera.farClip = json.value("farClip", 1000.0f);
|
||||
camera.aspectRatio = json.value("aspectRatio", 16.0f / 9.0f);
|
||||
camera.orthographic = json.value("orthographic", false);
|
||||
camera.orthoWidth = json.value("orthoWidth", 10.0f);
|
||||
camera.orthoHeight = json.value("orthoHeight", 10.0f);
|
||||
|
||||
// Mark as dirty so camera system will create the camera
|
||||
camera.needsRebuild = true;
|
||||
|
||||
entity.set<CameraComponent>(camera);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ private:
|
||||
nlohmann::json serializeEntityName(flecs::entity entity);
|
||||
nlohmann::json serializeRigidBody(flecs::entity entity);
|
||||
nlohmann::json serializeCollider(flecs::entity entity);
|
||||
nlohmann::json serializeLight(flecs::entity entity);
|
||||
nlohmann::json serializeCamera(flecs::entity entity);
|
||||
|
||||
// Component deserialization
|
||||
void deserializeTransform(flecs::entity entity, const nlohmann::json& json, flecs::entity parentEntity);
|
||||
@@ -51,6 +53,8 @@ private:
|
||||
void deserializeEntityName(flecs::entity entity, const nlohmann::json& json);
|
||||
void deserializeRigidBody(flecs::entity entity, const nlohmann::json& json);
|
||||
void deserializeCollider(flecs::entity entity, const nlohmann::json& json);
|
||||
void deserializeLight(flecs::entity entity, const nlohmann::json& json);
|
||||
void deserializeCamera(flecs::entity entity, const nlohmann::json& json);
|
||||
|
||||
flecs::world& m_world;
|
||||
Ogre::SceneManager* m_sceneMgr;
|
||||
|
||||
88
src/features/editScene/ui/CameraEditor.cpp
Normal file
88
src/features/editScene/ui/CameraEditor.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "CameraEditor.hpp"
|
||||
#include <imgui.h>
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreRenderTexture.h>
|
||||
#include <OgreViewport.h>
|
||||
#include <OgreLogManager.h>
|
||||
|
||||
CameraEditor::CameraEditor(Ogre::SceneManager* sceneMgr)
|
||||
: m_sceneMgr(sceneMgr)
|
||||
{
|
||||
}
|
||||
|
||||
bool CameraEditor::renderComponent(flecs::entity entity, CameraComponent &camera)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
if (ImGui::CollapsingHeader("Camera", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Indent();
|
||||
|
||||
// Projection type
|
||||
bool ortho = camera.orthographic;
|
||||
if (ImGui::Checkbox("Orthographic", &camera.orthographic)) {
|
||||
camera.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (camera.orthographic) {
|
||||
// Orthographic settings
|
||||
if (ImGui::DragFloat("Ortho Width", &camera.orthoWidth, 0.1f, 0.1f, 1000.0f)) {
|
||||
camera.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::DragFloat("Ortho Height", &camera.orthoHeight, 0.1f, 0.1f, 1000.0f)) {
|
||||
camera.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
} else {
|
||||
// Perspective settings
|
||||
if (ImGui::SliderFloat("FOV Y", &camera.fovY, 10.0f, 120.0f, "%.1f deg")) {
|
||||
camera.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Clip distances
|
||||
ImGui::Separator();
|
||||
if (ImGui::DragFloat("Near Clip", &camera.nearClip, 0.01f, 0.001f, 10.0f)) {
|
||||
camera.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::DragFloat("Far Clip", &camera.farClip, 1.0f, 10.0f, 10000.0f)) {
|
||||
camera.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Aspect ratio (auto or manual)
|
||||
ImGui::Separator();
|
||||
static bool autoAspect = true;
|
||||
ImGui::Checkbox("Auto Aspect Ratio", &autoAspect);
|
||||
if (!autoAspect) {
|
||||
if (ImGui::DragFloat("Aspect Ratio", &camera.aspectRatio, 0.01f, 0.1f, 10.0f)) {
|
||||
camera.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Preview section - simplified for now
|
||||
ImGui::Separator();
|
||||
if (camera.camera) {
|
||||
ImGui::Text("Camera Preview: (Right-click viewport to use)");
|
||||
if (ImGui::Button("Use This Camera")) {
|
||||
// This would switch the main viewport to use this camera
|
||||
// Implementation depends on viewport management
|
||||
ImGui::OpenPopup("Camera Switch");
|
||||
}
|
||||
if (ImGui::BeginPopup("Camera Switch")) {
|
||||
ImGui::Text("Camera switch not yet implemented");
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
} else {
|
||||
ImGui::TextDisabled("Camera not yet created");
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
27
src/features/editScene/ui/CameraEditor.hpp
Normal file
27
src/features/editScene/ui/CameraEditor.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef EDITSCENE_CAMERAEDITOR_HPP
|
||||
#define EDITSCENE_CAMERAEDITOR_HPP
|
||||
#pragma once
|
||||
|
||||
#include "ComponentEditor.hpp"
|
||||
#include "../components/Camera.hpp"
|
||||
#include <OgreSceneManager.h>
|
||||
|
||||
/**
|
||||
* Editor for CameraComponent with preview functionality
|
||||
*/
|
||||
class CameraEditor : public ComponentEditor<CameraComponent> {
|
||||
public:
|
||||
CameraEditor(Ogre::SceneManager* sceneMgr);
|
||||
bool renderComponent(flecs::entity entity, CameraComponent &camera) override;
|
||||
const char *getName() const override { return "Camera"; }
|
||||
|
||||
private:
|
||||
void updatePreviewTexture(CameraComponent& camera);
|
||||
void renderPreview(CameraComponent& camera);
|
||||
|
||||
Ogre::SceneManager* m_sceneMgr;
|
||||
// Track last rendered frame to avoid flickering
|
||||
unsigned long m_lastFrame = 0;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_CAMERAEDITOR_HPP
|
||||
21
src/features/editScene/ui/ComponentRegistration.cpp
Normal file
21
src/features/editScene/ui/ComponentRegistration.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "ComponentRegistration.hpp"
|
||||
|
||||
std::vector<ComponentRegistration::RegistrationFunc>& ComponentRegistration::getRegistrants()
|
||||
{
|
||||
static std::vector<RegistrationFunc> registrants;
|
||||
return registrants;
|
||||
}
|
||||
|
||||
void ComponentRegistration::registerComponent(RegistrationFunc func)
|
||||
{
|
||||
getRegistrants().push_back(func);
|
||||
}
|
||||
|
||||
void ComponentRegistration::registerAll(ComponentRegistry& registry,
|
||||
Ogre::SceneManager* sceneMgr,
|
||||
EditorPhysicsSystem* physicsSystem)
|
||||
{
|
||||
for (auto& func : getRegistrants()) {
|
||||
func(registry, sceneMgr, physicsSystem);
|
||||
}
|
||||
}
|
||||
79
src/features/editScene/ui/ComponentRegistration.hpp
Normal file
79
src/features/editScene/ui/ComponentRegistration.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef EDITSCENE_COMPONENTREGISTRATION_HPP
|
||||
#define EDITSCENE_COMPONENTREGISTRATION_HPP
|
||||
#pragma once
|
||||
|
||||
#include "ComponentRegistry.hpp"
|
||||
#include <OgreSceneManager.h>
|
||||
#include <flecs.h>
|
||||
#include <functional>
|
||||
|
||||
// Forward declarations
|
||||
class EditorPhysicsSystem;
|
||||
|
||||
/**
|
||||
* Helper class to modularize component registration
|
||||
* Each component module registers itself using static initialization
|
||||
*/
|
||||
class ComponentRegistration {
|
||||
public:
|
||||
using RegistrationFunc = std::function<void(ComponentRegistry&,
|
||||
Ogre::SceneManager*,
|
||||
EditorPhysicsSystem*)>;
|
||||
|
||||
/**
|
||||
* Register a component module
|
||||
* Call this from a .cpp file to auto-register the component
|
||||
*/
|
||||
static void registerComponent(RegistrationFunc func);
|
||||
|
||||
/**
|
||||
* Register all components with the registry
|
||||
* Call this once during editor initialization
|
||||
*/
|
||||
static void registerAll(ComponentRegistry& registry,
|
||||
Ogre::SceneManager* sceneMgr,
|
||||
EditorPhysicsSystem* physicsSystem);
|
||||
|
||||
private:
|
||||
static std::vector<RegistrationFunc>& getRegistrants();
|
||||
};
|
||||
|
||||
/**
|
||||
* Macro to simplify component registration
|
||||
* Usage in a .cpp file:
|
||||
*
|
||||
* REGISTER_COMPONENT("My Component", MyComponent, MyComponentEditor)
|
||||
* {
|
||||
* // Adder
|
||||
* registry.registerComponent<MyComponent>(
|
||||
* name, std::make_unique<MyComponentEditor>(...),
|
||||
* [](flecs::entity e) { ... },
|
||||
* [](flecs::entity e) { ... });
|
||||
* }
|
||||
*/
|
||||
#define REGISTER_COMPONENT(nameStr, ComponentType, EditorType) \
|
||||
static void register_##ComponentType(ComponentRegistry& registry, \
|
||||
Ogre::SceneManager* sceneMgr, \
|
||||
EditorPhysicsSystem* physics); \
|
||||
struct ComponentType##_registrar { \
|
||||
ComponentType##_registrar() { \
|
||||
ComponentRegistration::registerComponent(register_##ComponentType); \
|
||||
} \
|
||||
} static ComponentType##_instance; \
|
||||
static void register_##ComponentType(ComponentRegistry& registry, \
|
||||
Ogre::SceneManager* sceneMgr, \
|
||||
EditorPhysicsSystem* physics)
|
||||
|
||||
/**
|
||||
* Helper for standard SceneNode-attached components
|
||||
*/
|
||||
template<typename T>
|
||||
struct SceneNodeComponentTraits {
|
||||
// Override these for your component type
|
||||
static const char* getName() { return "Component"; }
|
||||
static void onAdd(T& comp, Ogre::SceneNode* node) {}
|
||||
static void onRemove(T& comp, Ogre::SceneNode* node) {}
|
||||
static void onUpdate(T& comp) {}
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_COMPONENTREGISTRATION_HPP
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <flecs.h>
|
||||
#include "ComponentEditor.hpp"
|
||||
|
||||
/**
|
||||
@@ -17,6 +19,11 @@ using ComponentAdder = std::function<void(flecs::entity)>;
|
||||
*/
|
||||
using ComponentRemover = std::function<void(flecs::entity)>;
|
||||
|
||||
/**
|
||||
* Function type for checking if an entity has a component
|
||||
*/
|
||||
using ComponentChecker = std::function<bool(flecs::entity)>;
|
||||
|
||||
/**
|
||||
* Registry for component editors and their add/remove functions
|
||||
*/
|
||||
@@ -27,6 +34,7 @@ public:
|
||||
std::unique_ptr<IComponentEditor> editor;
|
||||
ComponentAdder adder;
|
||||
ComponentRemover remover;
|
||||
ComponentChecker checker; // Checks if entity has this component
|
||||
};
|
||||
|
||||
ComponentRegistry() = default;
|
||||
@@ -53,6 +61,7 @@ public:
|
||||
info.editor = std::move(editor);
|
||||
info.adder = adder;
|
||||
info.remover = remover;
|
||||
info.checker = [](flecs::entity e) { return e.has<T>(); };
|
||||
m_components[std::type_index(typeid(T))] = std::move(info);
|
||||
}
|
||||
|
||||
@@ -104,15 +113,37 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered component types
|
||||
* Check if entity has a specific component type
|
||||
*/
|
||||
std::vector<std::type_index> getRegisteredTypes() const
|
||||
bool entityHasComponent(flecs::entity entity, const std::type_index &type) const
|
||||
{
|
||||
std::vector<std::type_index> types;
|
||||
for (const auto &pair : m_components) {
|
||||
types.push_back(pair.first);
|
||||
auto it = m_components.find(type);
|
||||
if (it != m_components.end() && it->second.checker) {
|
||||
return it->second.checker(entity);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a component by type_index
|
||||
*/
|
||||
void addComponentByType(flecs::entity entity, const std::type_index &type)
|
||||
{
|
||||
auto it = m_components.find(type);
|
||||
if (it != m_components.end() && it->second.adder) {
|
||||
it->second.adder(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a component by type_index
|
||||
*/
|
||||
void removeComponentByType(flecs::entity entity, const std::type_index &type)
|
||||
{
|
||||
auto it = m_components.find(type);
|
||||
if (it != m_components.end() && it->second.remover) {
|
||||
it->second.remover(entity);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,27 +159,32 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if entity has a specific component type
|
||||
* Get all registered component types
|
||||
*/
|
||||
bool entityHasComponent(flecs::entity entity,
|
||||
const std::type_index &type) const
|
||||
std::vector<std::type_index> getRegisteredTypes() const
|
||||
{
|
||||
// This is a simplified check - in practice you'd need to
|
||||
// store flecs component IDs and check those
|
||||
return false;
|
||||
std::vector<std::type_index> types;
|
||||
for (const auto &pair : m_components) {
|
||||
types.push_back(pair.first);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render add component menu for all registered components
|
||||
* Returns true if a component was added
|
||||
* Iterate over all registered components
|
||||
*/
|
||||
bool renderAddComponentMenu(flecs::entity entity);
|
||||
template<typename Func>
|
||||
void forEach(Func&& func) const
|
||||
{
|
||||
for (const auto& pair : m_components) {
|
||||
func(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render remove component menu for components the entity has
|
||||
* Returns true if a component was removed
|
||||
* Get the number of registered components
|
||||
*/
|
||||
bool renderRemoveComponentMenu(flecs::entity entity);
|
||||
size_t getComponentCount() const { return m_components.size(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::type_index, ComponentInfo> m_components;
|
||||
|
||||
136
src/features/editScene/ui/LightEditor.cpp
Normal file
136
src/features/editScene/ui/LightEditor.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "LightEditor.hpp"
|
||||
#include <imgui.h>
|
||||
|
||||
void LightEditor::renderColorEdit(const char* label, Ogre::ColourValue& color)
|
||||
{
|
||||
float col[4] = { color.r, color.g, color.b, color.a };
|
||||
if (ImGui::ColorEdit4(label, col)) {
|
||||
color.r = col[0];
|
||||
color.g = col[1];
|
||||
color.b = col[2];
|
||||
color.a = col[3];
|
||||
}
|
||||
}
|
||||
|
||||
bool LightEditor::renderComponent(flecs::entity entity, LightComponent &light)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
if (ImGui::CollapsingHeader("Light", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Indent();
|
||||
|
||||
// Light type selector
|
||||
const char* lightTypes[] = { "Point", "Directional", "Spotlight" };
|
||||
int currentType = static_cast<int>(light.lightType);
|
||||
if (ImGui::Combo("Type", ¤tType, lightTypes, IM_ARRAYSIZE(lightTypes))) {
|
||||
light.lightType = static_cast<LightComponent::LightType>(currentType);
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Colors
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Colors");
|
||||
|
||||
Ogre::ColourValue diffuse = light.diffuseColor;
|
||||
renderColorEdit("Diffuse", light.diffuseColor);
|
||||
if (light.diffuseColor != diffuse) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
Ogre::ColourValue specular = light.specularColor;
|
||||
renderColorEdit("Specular", light.specularColor);
|
||||
if (light.specularColor != specular) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Intensity
|
||||
ImGui::Separator();
|
||||
float intensity = light.intensity;
|
||||
if (ImGui::DragFloat("Intensity", &light.intensity, 0.1f, 0.0f, 10.0f)) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Cast shadows
|
||||
bool castShadows = light.castShadows;
|
||||
if (ImGui::Checkbox("Cast Shadows", &light.castShadows)) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Type-specific properties
|
||||
ImGui::Separator();
|
||||
|
||||
if (light.lightType == LightComponent::LightType::Point ||
|
||||
light.lightType == LightComponent::LightType::Spotlight) {
|
||||
ImGui::Text("Attenuation");
|
||||
|
||||
if (ImGui::DragFloat("Range", &light.range, 1.0f, 0.1f, 10000.0f)) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ImGui::DragFloat("Constant", &light.constantAttenuation, 0.1f, 0.0f, 1.0f)) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ImGui::DragFloat("Linear", &light.linearAttenuation, 0.001f, 0.0f, 1.0f, "%.4f")) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ImGui::DragFloat("Quadratic", &light.quadraticAttenuation, 0.0001f, 0.0f, 1.0f, "%.6f")) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (light.lightType == LightComponent::LightType::Directional ||
|
||||
light.lightType == LightComponent::LightType::Spotlight) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Direction");
|
||||
float dir[3] = { light.direction.x, light.direction.y, light.direction.z };
|
||||
if (ImGui::DragFloat3("Direction", dir, 0.1f)) {
|
||||
light.direction = Ogre::Vector3(dir[0], dir[1], dir[2]).normalisedCopy();
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (light.lightType == LightComponent::LightType::Spotlight) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Spotlight Settings");
|
||||
|
||||
if (ImGui::SliderFloat("Inner Angle", &light.spotlightInnerAngle, 0.0f, 90.0f)) {
|
||||
// Ensure inner <= outer
|
||||
if (light.spotlightInnerAngle > light.spotlightOuterAngle) {
|
||||
light.spotlightOuterAngle = light.spotlightInnerAngle;
|
||||
}
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ImGui::SliderFloat("Outer Angle", &light.spotlightOuterAngle, 0.0f, 90.0f)) {
|
||||
// Ensure outer >= inner
|
||||
if (light.spotlightOuterAngle < light.spotlightInnerAngle) {
|
||||
light.spotlightInnerAngle = light.spotlightOuterAngle;
|
||||
}
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ImGui::DragFloat("Falloff", &light.spotlightFalloff, 0.1f, 0.0f, 10.0f)) {
|
||||
light.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
20
src/features/editScene/ui/LightEditor.hpp
Normal file
20
src/features/editScene/ui/LightEditor.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef EDITSCENE_LIGHTEDITOR_HPP
|
||||
#define EDITSCENE_LIGHTEDITOR_HPP
|
||||
#pragma once
|
||||
|
||||
#include "ComponentEditor.hpp"
|
||||
#include "../components/Light.hpp"
|
||||
|
||||
/**
|
||||
* Editor for LightComponent
|
||||
*/
|
||||
class LightEditor : public ComponentEditor<LightComponent> {
|
||||
public:
|
||||
bool renderComponent(flecs::entity entity, LightComponent &light) override;
|
||||
const char *getName() const override { return "Light"; }
|
||||
|
||||
private:
|
||||
void renderColorEdit(const char* label, Ogre::ColourValue& color);
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_LIGHTEDITOR_HPP
|
||||
Reference in New Issue
Block a user