Normal display tool implemented
This commit is contained in:
@@ -30,6 +30,7 @@ set(EDITSCENE_SOURCES
|
||||
systems/ProceduralMaterialSystem.cpp
|
||||
systems/ProceduralMeshSystem.cpp
|
||||
systems/CellGridSystem.cpp
|
||||
systems/NormalDebugSystem.cpp
|
||||
systems/RoomLayoutSystem.cpp
|
||||
systems/FurnitureLibrary.cpp
|
||||
systems/StartupMenuSystem.cpp
|
||||
@@ -149,6 +150,7 @@ set(EDITSCENE_HEADERS
|
||||
systems/PlayerControllerSystem.hpp
|
||||
systems/EditorUISystem.hpp
|
||||
systems/CellGridSystem.hpp
|
||||
systems/NormalDebugSystem.hpp
|
||||
systems/RoomLayoutSystem.hpp
|
||||
systems/FurnitureLibrary.hpp
|
||||
systems/ProceduralMaterialSystem.hpp
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "systems/NavMeshSystem.hpp"
|
||||
#include "systems/CharacterSystem.hpp"
|
||||
#include "systems/CellGridSystem.hpp"
|
||||
#include "systems/NormalDebugSystem.hpp"
|
||||
#include "systems/RoomLayoutSystem.hpp"
|
||||
#include "systems/StartupMenuSystem.hpp"
|
||||
#include "systems/PlayerControllerSystem.hpp"
|
||||
@@ -243,12 +244,12 @@ void EditorApp::setup()
|
||||
m_world, m_physicsSystem->getPhysicsWrapper());
|
||||
m_buoyancySystem->initialize();
|
||||
|
||||
m_sunSystem = std::make_unique<EditorSunSystem>(m_world, m_sceneMgr);
|
||||
m_skyboxSystem =
|
||||
std::make_unique<EditorSkyboxSystem>(m_world, m_sceneMgr);
|
||||
m_waterPlaneSystem =
|
||||
std::make_unique<EditorWaterPlaneSystem>(m_world,
|
||||
m_sceneMgr);
|
||||
m_sunSystem =
|
||||
std::make_unique<EditorSunSystem>(m_world, m_sceneMgr);
|
||||
m_skyboxSystem = std::make_unique<EditorSkyboxSystem>(
|
||||
m_world, m_sceneMgr);
|
||||
m_waterPlaneSystem = std::make_unique<EditorWaterPlaneSystem>(
|
||||
m_world, m_sceneMgr);
|
||||
|
||||
// Apply debug setting if it was set before system creation
|
||||
if (m_debugBuoyancy) {
|
||||
@@ -305,12 +306,11 @@ void EditorApp::setup()
|
||||
m_world, m_sceneMgr);
|
||||
m_animationTreeSystem->initialize();
|
||||
m_behaviorTreeSystem = std::make_unique<BehaviorTreeSystem>(
|
||||
m_world, m_sceneMgr,
|
||||
m_animationTreeSystem.get());
|
||||
m_world, m_sceneMgr, m_animationTreeSystem.get());
|
||||
|
||||
// Setup NavMesh system
|
||||
m_navMeshSystem = std::make_unique<NavMeshSystem>(
|
||||
m_world, m_sceneMgr);
|
||||
m_navMeshSystem =
|
||||
std::make_unique<NavMeshSystem>(m_world, m_sceneMgr);
|
||||
|
||||
// Setup Character physics system
|
||||
m_characterSystem =
|
||||
@@ -324,8 +324,17 @@ void EditorApp::setup()
|
||||
|
||||
// Wire CellGridSystem into NavMeshSystem so it can collect
|
||||
// batched frame/furniture geometry from StaticGeometry.
|
||||
m_navMeshSystem->setCellGridSystem(
|
||||
m_cellGridSystem.get());
|
||||
m_navMeshSystem->setCellGridSystem(m_cellGridSystem.get());
|
||||
|
||||
// Setup NormalDebug system (disabled by default)
|
||||
m_normalDebugSystem = std::make_unique<NormalDebugSystem>(
|
||||
m_world, m_sceneMgr, m_cellGridSystem.get());
|
||||
|
||||
// Wire NormalDebugSystem into UI for toggle
|
||||
if (m_uiSystem) {
|
||||
m_uiSystem->setNormalDebugSystem(
|
||||
m_normalDebugSystem.get());
|
||||
}
|
||||
|
||||
// Setup RoomLayout system
|
||||
m_roomLayoutSystem =
|
||||
@@ -694,8 +703,7 @@ bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt)
|
||||
if (m_animationTreeSystem) {
|
||||
m_animationTreeSystem->update(evt.timeSinceLastFrame);
|
||||
if (m_behaviorTreeSystem)
|
||||
m_behaviorTreeSystem->update(
|
||||
evt.timeSinceLastFrame);
|
||||
m_behaviorTreeSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
if (m_proceduralMeshSystem) {
|
||||
m_proceduralMeshSystem->update();
|
||||
@@ -709,6 +717,11 @@ bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt)
|
||||
m_cellGridSystem->update();
|
||||
}
|
||||
|
||||
/* --- Normal debug visualization (after geometry is built) --- */
|
||||
if (m_normalDebugSystem) {
|
||||
m_normalDebugSystem->update();
|
||||
}
|
||||
|
||||
/* --- NavMesh builds after static geometry is ready --- */
|
||||
if (m_navMeshSystem) {
|
||||
m_navMeshSystem->update(evt.timeSinceLastFrame);
|
||||
|
||||
@@ -34,6 +34,7 @@ class BuoyancySystem;
|
||||
class EditorSunSystem;
|
||||
class EditorSkyboxSystem;
|
||||
class EditorWaterPlaneSystem;
|
||||
class NormalDebugSystem;
|
||||
class EditorApp;
|
||||
|
||||
/**
|
||||
@@ -217,6 +218,7 @@ private:
|
||||
std::unique_ptr<NavMeshSystem> m_navMeshSystem;
|
||||
std::unique_ptr<CharacterSystem> m_characterSystem;
|
||||
std::unique_ptr<CellGridSystem> m_cellGridSystem;
|
||||
std::unique_ptr<NormalDebugSystem> m_normalDebugSystem;
|
||||
std::unique_ptr<RoomLayoutSystem> m_roomLayoutSystem;
|
||||
|
||||
// Game systems
|
||||
|
||||
@@ -11,6 +11,7 @@ FileSystem=resources/buildings/parts/pier
|
||||
FileSystem=resources/buildings/parts/furniture
|
||||
FileSystem=resources/vehicles
|
||||
FileSystem=resources/fonts
|
||||
FileSystem=resources/debug
|
||||
|
||||
[Popular]
|
||||
FileSystem=resources/materials/programs
|
||||
|
||||
@@ -6,186 +6,241 @@
|
||||
#include "../components/RigidBody.hpp"
|
||||
#include "../components/PhysicsCollider.hpp"
|
||||
|
||||
namespace Procedural {
|
||||
class TriangleBuffer;
|
||||
namespace Procedural
|
||||
{
|
||||
class TriangleBuffer;
|
||||
}
|
||||
|
||||
namespace Ogre {
|
||||
class StaticGeometry;
|
||||
namespace Ogre
|
||||
{
|
||||
class StaticGeometry;
|
||||
}
|
||||
|
||||
class CellGridSystem {
|
||||
public:
|
||||
CellGridSystem(flecs::world& world, Ogre::SceneManager* sceneMgr);
|
||||
~CellGridSystem();
|
||||
|
||||
void initialize();
|
||||
void update();
|
||||
|
||||
// Force rebuild of a specific cell grid
|
||||
void rebuildCellGrid(flecs::entity entity);
|
||||
|
||||
// Create/Update town material
|
||||
void updateTownMaterial(flecs::entity townEntity);
|
||||
|
||||
// Expose frame/furniture placements for navmesh generation
|
||||
struct PlacementInfo {
|
||||
std::string meshName;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion rotation;
|
||||
};
|
||||
std::vector<PlacementInfo> getFramePlacements(flecs::entity entity) const;
|
||||
std::vector<PlacementInfo> getFurniturePlacements(flecs::entity entity) const;
|
||||
|
||||
CellGridSystem(flecs::world &world, Ogre::SceneManager *sceneMgr);
|
||||
~CellGridSystem();
|
||||
|
||||
void initialize();
|
||||
void update();
|
||||
|
||||
// Force rebuild of a specific cell grid
|
||||
void rebuildCellGrid(flecs::entity entity);
|
||||
|
||||
// Create/Update town material
|
||||
void updateTownMaterial(flecs::entity townEntity);
|
||||
|
||||
// Expose frame/furniture placements for navmesh generation
|
||||
struct PlacementInfo {
|
||||
std::string meshName;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion rotation;
|
||||
};
|
||||
std::vector<PlacementInfo>
|
||||
getFramePlacements(flecs::entity entity) const;
|
||||
std::vector<PlacementInfo>
|
||||
getFurniturePlacements(flecs::entity entity) const;
|
||||
|
||||
// Plaza mesh storage per district entity
|
||||
struct PlazaData {
|
||||
std::string meshName;
|
||||
Ogre::Entity *entity = nullptr;
|
||||
// Track texture dependency for automatic rebuild when texture changes
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
unsigned int textureVersion = 0;
|
||||
flecs::entity physicsParentEntity = flecs::entity::null();
|
||||
};
|
||||
|
||||
// Lot base mesh storage per lot entity
|
||||
struct LotBaseData {
|
||||
std::string meshName;
|
||||
Ogre::Entity *entity = nullptr;
|
||||
// Track texture dependency for automatic rebuild when texture changes
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
unsigned int textureVersion = 0;
|
||||
flecs::entity physicsParentEntity = flecs::entity::null();
|
||||
};
|
||||
|
||||
// Expose plaza and lot base mesh data for debug visualization
|
||||
const std::unordered_map<uint64_t, PlazaData> &getPlazaMeshes() const
|
||||
{
|
||||
return m_plazaMeshes;
|
||||
}
|
||||
const std::unordered_map<uint64_t, LotBaseData> &
|
||||
getLotBaseMeshes() const
|
||||
{
|
||||
return m_lotBaseMeshes;
|
||||
}
|
||||
|
||||
private:
|
||||
flecs::world& m_world;
|
||||
Ogre::SceneManager* m_sceneMgr;
|
||||
|
||||
flecs::query<struct CellGridComponent> m_cellGridQuery;
|
||||
flecs::query<struct TownComponent> m_townQuery;
|
||||
flecs::query<struct DistrictComponent> m_districtQuery;
|
||||
flecs::query<struct LotComponent> m_lotQuery;
|
||||
|
||||
bool m_initialized = false;
|
||||
int m_meshCount = 0;
|
||||
|
||||
std::vector<flecs::entity> m_pendingPhysicsBuilds;
|
||||
|
||||
// Build geometry from cell grid
|
||||
void buildCellGrid(flecs::entity entity, struct CellGridComponent& grid);
|
||||
|
||||
// Create triangle buffers for different parts
|
||||
void buildFloorsAndCeilings(const struct CellGridComponent& grid, Procedural::TriangleBuffer& floorTb, Procedural::TriangleBuffer& ceilingTb);
|
||||
void buildWalls(const struct CellGridComponent& grid,
|
||||
Procedural::TriangleBuffer& extWallTb,
|
||||
Procedural::TriangleBuffer& intWallTb,
|
||||
Procedural::TriangleBuffer& intWindowsTb);
|
||||
void buildDoors(const struct CellGridComponent& grid, Procedural::TriangleBuffer& extDoorsTb, Procedural::TriangleBuffer& intDoorsTb);
|
||||
void buildWindows(const struct CellGridComponent& grid, Procedural::TriangleBuffer& extWindowsTb, Procedural::TriangleBuffer& intWindowsTb);
|
||||
void buildCorners(const struct CellGridComponent& grid, Procedural::TriangleBuffer& extWallTb);
|
||||
void buildInternalCorners(const struct CellGridComponent& grid, Procedural::TriangleBuffer& intWallTb);
|
||||
|
||||
// Build roofs
|
||||
void buildRoofs(flecs::entity lotEntity, const struct CellGridComponent& grid,
|
||||
Procedural::TriangleBuffer& roofTopTb, Procedural::TriangleBuffer& roofSideTb);
|
||||
|
||||
// Build plaza for district
|
||||
void buildDistrictPlaza(flecs::entity entity, struct DistrictComponent& district);
|
||||
|
||||
// Build lot base geometry
|
||||
void buildLotBase(flecs::entity entity, struct LotComponent& lot);
|
||||
|
||||
// Build furniture meshes
|
||||
void buildFurniture(flecs::entity entity, const struct CellGridComponent& grid);
|
||||
void destroyFurniture(flecs::entity entity);
|
||||
|
||||
// Build window and door frames (3D frame meshes)
|
||||
void buildFrames(flecs::entity entity, const struct CellGridComponent& grid, const std::string& materialName);
|
||||
void createWindowFrameMeshes(const struct CellGridComponent& grid, const std::string& materialName,
|
||||
const std::string& meshPrefix, flecs::entity materialEntity);
|
||||
void createDoorFrameMeshes(const struct CellGridComponent& grid, const std::string& materialName,
|
||||
const std::string& meshPrefix, flecs::entity materialEntity);
|
||||
void placeWindowFrames(flecs::entity entity, const struct CellGridComponent& grid, Ogre::SceneNode* parentNode, std::vector<Ogre::Entity*>& frameEntities);
|
||||
void placeDoorFrames(flecs::entity entity, const struct CellGridComponent& grid, Ogre::SceneNode* parentNode, std::vector<Ogre::Entity*>& frameEntities);
|
||||
void destroyFrames(flecs::entity entity);
|
||||
|
||||
// StaticGeometry versions (batched for better performance)
|
||||
void placeWindowFramesInStaticGeometry(flecs::entity entity, const struct CellGridComponent& grid,
|
||||
const Ogre::Vector3& parentPos, const Ogre::Quaternion& parentRot, const Ogre::Vector3& parentScale,
|
||||
Ogre::StaticGeometry* staticGeom, int& frameCount);
|
||||
void placeDoorFramesInStaticGeometry(flecs::entity entity, const struct CellGridComponent& grid,
|
||||
const Ogre::Vector3& parentPos, const Ogre::Quaternion& parentRot, const Ogre::Vector3& parentScale,
|
||||
Ogre::StaticGeometry* staticGeom, int& frameCount);
|
||||
|
||||
// Convert triangle buffer to mesh
|
||||
Ogre::MeshPtr convertToMesh(const std::string& name, Procedural::TriangleBuffer& tb, const std::string& materialName);
|
||||
|
||||
// Generate LOD levels for a mesh (shared settings for all procedural editor meshes)
|
||||
void generateLodForMesh(Ogre::MeshPtr mesh);
|
||||
|
||||
// Apply UV mapping from color rects
|
||||
void applyUVMapping(Procedural::TriangleBuffer& tb, flecs::entity entity, const std::string& rectName);
|
||||
|
||||
// Apply UV mapping using texture coordinates directly
|
||||
void applyUVMappingToBuffer(Procedural::TriangleBuffer& tb, const std::string& rectName, flecs::entity materialEntity);
|
||||
|
||||
// Destroy existing mesh
|
||||
void destroyCellGridMeshes(struct CellGridComponent& grid);
|
||||
void destroyCellGridMeshes(flecs::entity entity);
|
||||
|
||||
// Physics helpers
|
||||
void addPhysicsCollider(flecs::entity physicsParent,
|
||||
const std::string& meshName,
|
||||
const Ogre::Vector3& position,
|
||||
const Ogre::Quaternion& rotation);
|
||||
void buildPhysicsColliders(flecs::entity entity);
|
||||
void destroyPhysicsColliders(flecs::entity entity);
|
||||
|
||||
// Mesh storage per entity
|
||||
struct MeshData {
|
||||
struct ColliderPlacement {
|
||||
std::string meshName;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion rotation;
|
||||
};
|
||||
|
||||
std::string floorMesh;
|
||||
std::string ceilingMesh;
|
||||
std::string extWallMesh;
|
||||
std::string intWallMesh;
|
||||
std::string intWindowsMesh;
|
||||
std::vector<std::string> doorMeshes;
|
||||
std::vector<std::string> windowMeshes;
|
||||
std::string roofMesh;
|
||||
std::string roofSideMesh;
|
||||
std::vector<Ogre::Entity*> entities;
|
||||
|
||||
// Track texture dependency for automatic rebuild when texture changes
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
unsigned int textureVersion = 0;
|
||||
|
||||
// Frame meshes (unique per CellGrid for cellSize/cellHeight adaptation)
|
||||
std::string externalWindowFrameMesh;
|
||||
std::string internalWindowFrameMesh;
|
||||
std::string externalDoorFrameMesh;
|
||||
std::string internalDoorFrameMesh;
|
||||
|
||||
// Frame entities attached to the SceneNode (so they move with transform)
|
||||
std::vector<Ogre::Entity*> frameEntities;
|
||||
|
||||
// StaticGeometry for frames (batches all frames into single draw call)
|
||||
Ogre::StaticGeometry* frameStaticGeometry = nullptr;
|
||||
|
||||
// Furniture entities
|
||||
std::vector<Ogre::Entity*> furnitureEntities;
|
||||
|
||||
// Physics placement data for compound shape
|
||||
std::vector<ColliderPlacement> framePlacements;
|
||||
std::vector<ColliderPlacement> furniturePlacements;
|
||||
std::vector<flecs::entity> isolatedFurnitureEntities;
|
||||
flecs::entity physicsParentEntity = flecs::entity::null();
|
||||
};
|
||||
std::unordered_map<uint64_t, MeshData> m_entityMeshes;
|
||||
|
||||
// Plaza mesh storage per district entity
|
||||
struct PlazaData {
|
||||
std::string meshName;
|
||||
Ogre::Entity* entity = nullptr;
|
||||
// Track texture dependency for automatic rebuild when texture changes
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
unsigned int textureVersion = 0;
|
||||
flecs::entity physicsParentEntity = flecs::entity::null();
|
||||
};
|
||||
std::unordered_map<uint64_t, PlazaData> m_plazaMeshes;
|
||||
|
||||
// Lot base mesh storage per lot entity
|
||||
struct LotBaseData {
|
||||
std::string meshName;
|
||||
Ogre::Entity* entity = nullptr;
|
||||
// Track texture dependency for automatic rebuild when texture changes
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
unsigned int textureVersion = 0;
|
||||
flecs::entity physicsParentEntity = flecs::entity::null();
|
||||
};
|
||||
std::unordered_map<uint64_t, LotBaseData> m_lotBaseMeshes;
|
||||
flecs::world &m_world;
|
||||
Ogre::SceneManager *m_sceneMgr;
|
||||
|
||||
flecs::query<struct CellGridComponent> m_cellGridQuery;
|
||||
flecs::query<struct TownComponent> m_townQuery;
|
||||
flecs::query<struct DistrictComponent> m_districtQuery;
|
||||
flecs::query<struct LotComponent> m_lotQuery;
|
||||
|
||||
bool m_initialized = false;
|
||||
int m_meshCount = 0;
|
||||
|
||||
std::vector<flecs::entity> m_pendingPhysicsBuilds;
|
||||
|
||||
// Build geometry from cell grid
|
||||
void buildCellGrid(flecs::entity entity,
|
||||
struct CellGridComponent &grid);
|
||||
|
||||
// Create triangle buffers for different parts
|
||||
void buildFloorsAndCeilings(const struct CellGridComponent &grid,
|
||||
Procedural::TriangleBuffer &floorTb,
|
||||
Procedural::TriangleBuffer &ceilingTb);
|
||||
void buildWalls(const struct CellGridComponent &grid,
|
||||
Procedural::TriangleBuffer &extWallTb,
|
||||
Procedural::TriangleBuffer &intWallTb,
|
||||
Procedural::TriangleBuffer &intWindowsTb);
|
||||
void buildDoors(const struct CellGridComponent &grid,
|
||||
Procedural::TriangleBuffer &extDoorsTb,
|
||||
Procedural::TriangleBuffer &intDoorsTb);
|
||||
void buildWindows(const struct CellGridComponent &grid,
|
||||
Procedural::TriangleBuffer &extWindowsTb,
|
||||
Procedural::TriangleBuffer &intWindowsTb);
|
||||
void buildCorners(const struct CellGridComponent &grid,
|
||||
Procedural::TriangleBuffer &extWallTb);
|
||||
void buildInternalCorners(const struct CellGridComponent &grid,
|
||||
Procedural::TriangleBuffer &intWallTb);
|
||||
|
||||
// Build roofs
|
||||
void buildRoofs(flecs::entity lotEntity,
|
||||
const struct CellGridComponent &grid,
|
||||
Procedural::TriangleBuffer &roofTopTb,
|
||||
Procedural::TriangleBuffer &roofSideTb);
|
||||
|
||||
// Build plaza for district
|
||||
void buildDistrictPlaza(flecs::entity entity,
|
||||
struct DistrictComponent &district);
|
||||
|
||||
// Build lot base geometry
|
||||
void buildLotBase(flecs::entity entity, struct LotComponent &lot);
|
||||
|
||||
// Build furniture meshes
|
||||
void buildFurniture(flecs::entity entity,
|
||||
const struct CellGridComponent &grid);
|
||||
void destroyFurniture(flecs::entity entity);
|
||||
|
||||
// Build window and door frames (3D frame meshes)
|
||||
void buildFrames(flecs::entity entity,
|
||||
const struct CellGridComponent &grid,
|
||||
const std::string &materialName);
|
||||
void createWindowFrameMeshes(const struct CellGridComponent &grid,
|
||||
const std::string &materialName,
|
||||
const std::string &meshPrefix,
|
||||
flecs::entity materialEntity);
|
||||
void createDoorFrameMeshes(const struct CellGridComponent &grid,
|
||||
const std::string &materialName,
|
||||
const std::string &meshPrefix,
|
||||
flecs::entity materialEntity);
|
||||
void placeWindowFrames(flecs::entity entity,
|
||||
const struct CellGridComponent &grid,
|
||||
Ogre::SceneNode *parentNode,
|
||||
std::vector<Ogre::Entity *> &frameEntities);
|
||||
void placeDoorFrames(flecs::entity entity,
|
||||
const struct CellGridComponent &grid,
|
||||
Ogre::SceneNode *parentNode,
|
||||
std::vector<Ogre::Entity *> &frameEntities);
|
||||
void destroyFrames(flecs::entity entity);
|
||||
|
||||
// StaticGeometry versions (batched for better performance)
|
||||
void placeWindowFramesInStaticGeometry(
|
||||
flecs::entity entity, const struct CellGridComponent &grid,
|
||||
const Ogre::Vector3 &parentPos,
|
||||
const Ogre::Quaternion &parentRot,
|
||||
const Ogre::Vector3 &parentScale,
|
||||
Ogre::StaticGeometry *staticGeom, int &frameCount);
|
||||
void placeDoorFramesInStaticGeometry(
|
||||
flecs::entity entity, const struct CellGridComponent &grid,
|
||||
const Ogre::Vector3 &parentPos,
|
||||
const Ogre::Quaternion &parentRot,
|
||||
const Ogre::Vector3 &parentScale,
|
||||
Ogre::StaticGeometry *staticGeom, int &frameCount);
|
||||
|
||||
// Convert triangle buffer to mesh
|
||||
Ogre::MeshPtr convertToMesh(const std::string &name,
|
||||
Procedural::TriangleBuffer &tb,
|
||||
const std::string &materialName);
|
||||
|
||||
// Generate LOD levels for a mesh (shared settings for all procedural editor meshes)
|
||||
void generateLodForMesh(Ogre::MeshPtr mesh);
|
||||
|
||||
// Apply UV mapping from color rects
|
||||
void applyUVMapping(Procedural::TriangleBuffer &tb,
|
||||
flecs::entity entity, const std::string &rectName);
|
||||
|
||||
// Apply UV mapping using texture coordinates directly
|
||||
void applyUVMappingToBuffer(Procedural::TriangleBuffer &tb,
|
||||
const std::string &rectName,
|
||||
flecs::entity materialEntity);
|
||||
|
||||
// Destroy existing mesh
|
||||
void destroyCellGridMeshes(struct CellGridComponent &grid);
|
||||
void destroyCellGridMeshes(flecs::entity entity);
|
||||
|
||||
// Physics helpers
|
||||
void addPhysicsCollider(flecs::entity physicsParent,
|
||||
const std::string &meshName,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation);
|
||||
void buildPhysicsColliders(flecs::entity entity);
|
||||
void destroyPhysicsColliders(flecs::entity entity);
|
||||
|
||||
// Mesh storage per entity
|
||||
struct MeshData {
|
||||
struct ColliderPlacement {
|
||||
std::string meshName;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion rotation;
|
||||
};
|
||||
|
||||
std::string floorMesh;
|
||||
std::string ceilingMesh;
|
||||
std::string extWallMesh;
|
||||
std::string intWallMesh;
|
||||
std::string intWindowsMesh;
|
||||
std::vector<std::string> doorMeshes;
|
||||
std::vector<std::string> windowMeshes;
|
||||
std::string roofMesh;
|
||||
std::string roofSideMesh;
|
||||
std::vector<Ogre::Entity *> entities;
|
||||
|
||||
// Track texture dependency for automatic rebuild when texture changes
|
||||
flecs::entity textureEntity = flecs::entity::null();
|
||||
unsigned int textureVersion = 0;
|
||||
|
||||
// Frame meshes (unique per CellGrid for cellSize/cellHeight adaptation)
|
||||
std::string externalWindowFrameMesh;
|
||||
std::string internalWindowFrameMesh;
|
||||
std::string externalDoorFrameMesh;
|
||||
std::string internalDoorFrameMesh;
|
||||
|
||||
// Frame entities attached to the SceneNode (so they move with transform)
|
||||
std::vector<Ogre::Entity *> frameEntities;
|
||||
|
||||
// StaticGeometry for frames (batches all frames into single draw call)
|
||||
Ogre::StaticGeometry *frameStaticGeometry = nullptr;
|
||||
|
||||
// Furniture entities
|
||||
std::vector<Ogre::Entity *> furnitureEntities;
|
||||
|
||||
// Physics placement data for compound shape
|
||||
std::vector<ColliderPlacement> framePlacements;
|
||||
std::vector<ColliderPlacement> furniturePlacements;
|
||||
std::vector<flecs::entity> isolatedFurnitureEntities;
|
||||
flecs::entity physicsParentEntity = flecs::entity::null();
|
||||
};
|
||||
std::unordered_map<uint64_t, MeshData> m_entityMeshes;
|
||||
|
||||
// Plaza mesh storage per district entity
|
||||
std::unordered_map<uint64_t, PlazaData> m_plazaMeshes;
|
||||
|
||||
// Lot base mesh storage per lot entity
|
||||
std::unordered_map<uint64_t, LotBaseData> m_lotBaseMeshes;
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "PhysicsSystem.hpp"
|
||||
#include "BuoyancySystem.hpp"
|
||||
#include "NavMeshSystem.hpp"
|
||||
#include "NormalDebugSystem.hpp"
|
||||
#include <imgui.h>
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
@@ -322,6 +323,22 @@ void EditorUISystem::renderHierarchyWindow()
|
||||
}
|
||||
}
|
||||
|
||||
// Normal debug visualization toggle
|
||||
if (m_normalDebugSystem) {
|
||||
bool normalDebug =
|
||||
m_normalDebugSystem->isEnabled();
|
||||
if (ImGui::Checkbox(
|
||||
"Show Polygon Normals",
|
||||
&normalDebug)) {
|
||||
m_normalDebugSystem->setEnabled(
|
||||
normalDebug);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Show vertex normals for plaza and lot base geometry");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
@@ -753,7 +770,8 @@ void EditorUISystem::renderComponentList(flecs::entity entity)
|
||||
// Render AnimationTreeTemplate if present
|
||||
if (entity.has<AnimationTreeTemplate>()) {
|
||||
auto &templ = entity.get_mut<AnimationTreeTemplate>();
|
||||
m_componentRegistry.render<AnimationTreeTemplate>(entity, templ);
|
||||
m_componentRegistry.render<AnimationTreeTemplate>(entity,
|
||||
templ);
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
@@ -860,7 +878,8 @@ void EditorUISystem::renderComponentList(flecs::entity entity)
|
||||
auto &nav = entity.get_mut<NavMeshComponent>();
|
||||
if (m_componentRegistry.render<NavMeshComponent>(entity, nav)) {
|
||||
if (nav.debugDraw && NavMeshSystem::getInstance())
|
||||
NavMeshSystem::getInstance()->setDebugDraw(entity, nav.debugDraw);
|
||||
NavMeshSystem::getInstance()->setDebugDraw(
|
||||
entity, nav.debugDraw);
|
||||
}
|
||||
componentCount++;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// Forward declarations
|
||||
class EditorPhysicsSystem;
|
||||
class BuoyancySystem;
|
||||
class NormalDebugSystem;
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
@@ -124,6 +125,14 @@ public:
|
||||
m_buoyancySystem = buoyancy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set normal debug system for toggle
|
||||
*/
|
||||
void setNormalDebugSystem(NormalDebugSystem *normalDebug)
|
||||
{
|
||||
m_normalDebugSystem = normalDebug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable editor UI rendering
|
||||
*/
|
||||
@@ -215,6 +224,9 @@ private:
|
||||
// Buoyancy system reference (for configuration)
|
||||
BuoyancySystem *m_buoyancySystem = nullptr;
|
||||
|
||||
// Normal debug system reference (for toggle)
|
||||
NormalDebugSystem *m_normalDebugSystem = nullptr;
|
||||
|
||||
// Editor UI enabled flag
|
||||
bool m_editorUIEnabled = true;
|
||||
|
||||
|
||||
290
src/features/editScene/systems/NormalDebugSystem.cpp
Normal file
290
src/features/editScene/systems/NormalDebugSystem.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
#include "NormalDebugSystem.hpp"
|
||||
#include "CellGridSystem.hpp"
|
||||
#include "../components/CellGrid.hpp"
|
||||
#include "../components/Transform.hpp"
|
||||
#include <OgreManualObject.h>
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreSubMesh.h>
|
||||
#include <OgreMesh.h>
|
||||
#include <OgreLogManager.h>
|
||||
#include <OgreStringConverter.h>
|
||||
|
||||
// Normal line length in world units
|
||||
static const float NORMAL_LINE_LENGTH = 0.5f;
|
||||
|
||||
NormalDebugSystem::NormalDebugSystem(flecs::world &world,
|
||||
Ogre::SceneManager *sceneMgr,
|
||||
CellGridSystem *cellGridSystem)
|
||||
: m_world(world)
|
||||
, m_sceneMgr(sceneMgr)
|
||||
, m_cellGridSystem(cellGridSystem)
|
||||
{
|
||||
// Create ManualObject for normal lines
|
||||
m_manualObj = m_sceneMgr->createManualObject("NormalDebugLines");
|
||||
m_manualObj->setDynamic(true);
|
||||
|
||||
// Create scene node for the debug visualization
|
||||
m_node = m_sceneMgr->getRootSceneNode()->createChildSceneNode(
|
||||
"NormalDebugNode");
|
||||
m_node->attachObject(m_manualObj);
|
||||
m_node->setVisible(false); // Disabled by default
|
||||
}
|
||||
|
||||
NormalDebugSystem::~NormalDebugSystem()
|
||||
{
|
||||
if (m_node) {
|
||||
m_node->detachAllObjects();
|
||||
m_sceneMgr->destroySceneNode(m_node);
|
||||
m_node = nullptr;
|
||||
}
|
||||
if (m_manualObj) {
|
||||
m_sceneMgr->destroyManualObject(m_manualObj);
|
||||
m_manualObj = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NormalDebugSystem::setEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
if (m_node) {
|
||||
m_node->setVisible(enabled);
|
||||
}
|
||||
if (enabled) {
|
||||
rebuildAll();
|
||||
} else {
|
||||
// Clear the manual object
|
||||
if (m_manualObj) {
|
||||
m_manualObj->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NormalDebugSystem::update()
|
||||
{
|
||||
if (!m_enabled || !m_manualObj || !m_cellGridSystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if plaza/lot entities have changed by comparing cached IDs
|
||||
bool needsRebuild = false;
|
||||
|
||||
// Get current plaza entities from CellGridSystem
|
||||
const auto &plazaMeshes = m_cellGridSystem->getPlazaMeshes();
|
||||
std::vector<uint64_t> currentPlazaEntities;
|
||||
for (const auto &pair : plazaMeshes) {
|
||||
currentPlazaEntities.push_back(pair.first);
|
||||
}
|
||||
if (currentPlazaEntities != m_cachedPlazaEntities) {
|
||||
needsRebuild = true;
|
||||
m_cachedPlazaEntities = currentPlazaEntities;
|
||||
}
|
||||
|
||||
// Get current lot base entities from CellGridSystem
|
||||
const auto &lotBaseMeshes = m_cellGridSystem->getLotBaseMeshes();
|
||||
std::vector<uint64_t> currentLotEntities;
|
||||
for (const auto &pair : lotBaseMeshes) {
|
||||
currentLotEntities.push_back(pair.first);
|
||||
}
|
||||
if (currentLotEntities != m_cachedLotEntities) {
|
||||
needsRebuild = true;
|
||||
m_cachedLotEntities = currentLotEntities;
|
||||
}
|
||||
|
||||
if (needsRebuild) {
|
||||
rebuildAll();
|
||||
}
|
||||
}
|
||||
|
||||
void NormalDebugSystem::rebuildAll()
|
||||
{
|
||||
if (!m_manualObj || !m_cellGridSystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_manualObj->clear();
|
||||
|
||||
const auto &plazaMeshes = m_cellGridSystem->getPlazaMeshes();
|
||||
const auto &lotBaseMeshes = m_cellGridSystem->getLotBaseMeshes();
|
||||
|
||||
// Count total normals to draw
|
||||
size_t totalNormals = 0;
|
||||
for (const auto &pair : plazaMeshes) {
|
||||
if (pair.second.entity) {
|
||||
Ogre::MeshPtr mesh = pair.second.entity->getMesh();
|
||||
if (mesh.isNull())
|
||||
continue;
|
||||
for (unsigned int i = 0; i < mesh->getNumSubMeshes();
|
||||
++i) {
|
||||
Ogre::SubMesh *sub = mesh->getSubMesh(i);
|
||||
if (sub && sub->vertexData) {
|
||||
totalNormals +=
|
||||
sub->vertexData->vertexCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &pair : lotBaseMeshes) {
|
||||
if (pair.second.entity) {
|
||||
Ogre::MeshPtr mesh = pair.second.entity->getMesh();
|
||||
if (mesh.isNull())
|
||||
continue;
|
||||
for (unsigned int i = 0; i < mesh->getNumSubMeshes();
|
||||
++i) {
|
||||
Ogre::SubMesh *sub = mesh->getSubMesh(i);
|
||||
if (sub && sub->vertexData) {
|
||||
totalNormals +=
|
||||
sub->vertexData->vertexCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalNormals == 0) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"NormalDebug: No plaza or lot meshes found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Begin drawing lines (2 vertices per normal)
|
||||
m_manualObj->begin("Debug/NormalOverlay",
|
||||
Ogre::RenderOperation::OT_LINE_LIST);
|
||||
|
||||
// Draw plaza normals
|
||||
for (const auto &pair : plazaMeshes) {
|
||||
if (!pair.second.entity)
|
||||
continue;
|
||||
|
||||
// Find the scene node for this plaza entity
|
||||
Ogre::SceneNode *parentNode = nullptr;
|
||||
flecs::entity districtEntity = m_world.entity(pair.first);
|
||||
if (districtEntity.is_alive() &&
|
||||
districtEntity.has<TransformComponent>()) {
|
||||
const auto &transform =
|
||||
districtEntity.get<TransformComponent>();
|
||||
parentNode = transform.node;
|
||||
}
|
||||
|
||||
drawMeshNormals(pair.second.entity, parentNode, m_manualObj);
|
||||
}
|
||||
|
||||
// Draw lot base normals
|
||||
for (const auto &pair : lotBaseMeshes) {
|
||||
if (!pair.second.entity)
|
||||
continue;
|
||||
|
||||
// Find the scene node for this lot entity
|
||||
Ogre::SceneNode *parentNode = nullptr;
|
||||
flecs::entity lotEntity = m_world.entity(pair.first);
|
||||
if (lotEntity.is_alive() &&
|
||||
lotEntity.has<TransformComponent>()) {
|
||||
const auto &transform =
|
||||
lotEntity.get<TransformComponent>();
|
||||
parentNode = transform.node;
|
||||
}
|
||||
|
||||
drawMeshNormals(pair.second.entity, parentNode, m_manualObj);
|
||||
}
|
||||
|
||||
m_manualObj->end();
|
||||
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"NormalDebug: Drew " +
|
||||
Ogre::StringConverter::toString(totalNormals) +
|
||||
" normal lines");
|
||||
}
|
||||
|
||||
void NormalDebugSystem::drawMeshNormals(Ogre::Entity *entity,
|
||||
Ogre::SceneNode *parentNode,
|
||||
Ogre::ManualObject *manualObj)
|
||||
{
|
||||
if (!entity || !manualObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ogre::MeshPtr mesh = entity->getMesh();
|
||||
if (mesh.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get world transform from parent node
|
||||
Ogre::Vector3 worldPos(0, 0, 0);
|
||||
Ogre::Quaternion worldRot = Ogre::Quaternion::IDENTITY;
|
||||
Ogre::Vector3 worldScale(1, 1, 1);
|
||||
if (parentNode) {
|
||||
worldPos = parentNode->_getDerivedPosition();
|
||||
worldRot = parentNode->_getDerivedOrientation();
|
||||
worldScale = parentNode->_getDerivedScale();
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < mesh->getNumSubMeshes(); ++i) {
|
||||
Ogre::SubMesh *subMesh = mesh->getSubMesh(i);
|
||||
if (!subMesh || !subMesh->vertexData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Ogre::VertexData *vertexData = subMesh->vertexData;
|
||||
const Ogre::VertexElement *posElem =
|
||||
vertexData->vertexDeclaration->findElementBySemantic(
|
||||
Ogre::VES_POSITION);
|
||||
const Ogre::VertexElement *normElem =
|
||||
vertexData->vertexDeclaration->findElementBySemantic(
|
||||
Ogre::VES_NORMAL);
|
||||
|
||||
if (!posElem || !normElem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get vertex buffer
|
||||
Ogre::HardwareVertexBufferSharedPtr vbuf =
|
||||
vertexData->vertexBufferBinding->getBuffer(0);
|
||||
unsigned char *vertexDataPtr = static_cast<unsigned char *>(
|
||||
vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
|
||||
float *pFloat;
|
||||
Ogre::Vector3 pos;
|
||||
Ogre::Vector3 normal;
|
||||
|
||||
for (size_t v = 0; v < vertexData->vertexCount; ++v) {
|
||||
// Read position
|
||||
posElem->baseVertexPointerToElement(vertexDataPtr,
|
||||
&pFloat);
|
||||
pos.x = *pFloat++;
|
||||
pos.y = *pFloat++;
|
||||
pos.z = *pFloat;
|
||||
|
||||
// Read normal
|
||||
normElem->baseVertexPointerToElement(vertexDataPtr,
|
||||
&pFloat);
|
||||
normal.x = *pFloat++;
|
||||
normal.y = *pFloat++;
|
||||
normal.z = *pFloat;
|
||||
|
||||
// Transform position and normal to world space
|
||||
Ogre::Vector3 worldVertex =
|
||||
worldPos + (worldRot * (pos * worldScale));
|
||||
Ogre::Vector3 worldNormal = worldRot * normal;
|
||||
worldNormal.normalise();
|
||||
|
||||
// Calculate end point of normal line
|
||||
Ogre::Vector3 endPoint =
|
||||
worldVertex + worldNormal * NORMAL_LINE_LENGTH;
|
||||
|
||||
// Colour based on normal direction for visual clarity
|
||||
// Red = X, Green = Y, Blue = Z
|
||||
Ogre::ColourValue colour(std::abs(worldNormal.x),
|
||||
std::abs(worldNormal.y),
|
||||
std::abs(worldNormal.z), 1.0f);
|
||||
|
||||
// Draw line from vertex along normal
|
||||
manualObj->colour(colour);
|
||||
manualObj->position(worldVertex);
|
||||
manualObj->colour(colour);
|
||||
manualObj->position(endPoint);
|
||||
|
||||
vertexDataPtr += vbuf->getVertexSize();
|
||||
}
|
||||
|
||||
vbuf->unlock();
|
||||
}
|
||||
}
|
||||
71
src/features/editScene/systems/NormalDebugSystem.hpp
Normal file
71
src/features/editScene/systems/NormalDebugSystem.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef NORMAL_DEBUG_SYSTEM_HPP
|
||||
#define NORMAL_DEBUG_SYSTEM_HPP
|
||||
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
class CellGridSystem;
|
||||
|
||||
/**
|
||||
* Debug visualization system for polygon normals.
|
||||
*
|
||||
* Draws normal lines for plaza (DistrictComponent) and lot base
|
||||
* (LotComponent) geometry using ManualObject with a see-through
|
||||
* overlay material (Debug/NormalOverlay).
|
||||
*
|
||||
* Disabled by default. Toggle via EditorUISystem Settings menu.
|
||||
*/
|
||||
class NormalDebugSystem {
|
||||
public:
|
||||
NormalDebugSystem(flecs::world &world, Ogre::SceneManager *sceneMgr,
|
||||
CellGridSystem *cellGridSystem);
|
||||
~NormalDebugSystem();
|
||||
|
||||
/**
|
||||
* Update normal visualization each frame.
|
||||
* Rebuilds ManualObject if mesh data has changed.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Enable/disable normal visualization.
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
bool isEnabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Rebuild all normal visualizations from scratch.
|
||||
*/
|
||||
void rebuildAll();
|
||||
|
||||
/**
|
||||
* Draw normals for a single mesh entity.
|
||||
* @param entity The Ogre::Entity whose mesh normals to visualize.
|
||||
* @param parentNode The scene node the entity is attached to (for world-space transform).
|
||||
* @param manualObj The ManualObject to draw into (must be in begin/end scope).
|
||||
*/
|
||||
void drawMeshNormals(Ogre::Entity *entity, Ogre::SceneNode *parentNode,
|
||||
Ogre::ManualObject *manualObj);
|
||||
|
||||
flecs::world &m_world;
|
||||
Ogre::SceneManager *m_sceneMgr;
|
||||
CellGridSystem *m_cellGridSystem;
|
||||
|
||||
bool m_enabled = false;
|
||||
|
||||
// ManualObject for normal lines
|
||||
Ogre::ManualObject *m_manualObj = nullptr;
|
||||
Ogre::SceneNode *m_node = nullptr;
|
||||
|
||||
// Cached entity IDs to detect changes
|
||||
std::vector<uint64_t> m_cachedPlazaEntities;
|
||||
std::vector<uint64_t> m_cachedLotEntities;
|
||||
};
|
||||
|
||||
#endif // NORMAL_DEBUG_SYSTEM_HPP
|
||||
Reference in New Issue
Block a user