Color atlas serialization fixes
This commit is contained in:
@@ -80,7 +80,8 @@ void ImGuiRenderListener::preViewportUpdate(
|
||||
}
|
||||
|
||||
// Render startup menu in game mode (inside ImGui frame scope)
|
||||
if (m_editorApp && m_editorApp->getGameMode() == EditorApp::GameMode::Game &&
|
||||
if (m_editorApp &&
|
||||
m_editorApp->getGameMode() == EditorApp::GameMode::Game &&
|
||||
m_editorApp->getGamePlayState() == EditorApp::GamePlayState::Menu) {
|
||||
StartupMenuSystem *sms = m_editorApp->getStartupMenuSystem();
|
||||
if (sms)
|
||||
@@ -209,7 +210,8 @@ void EditorApp::setup()
|
||||
m_uiSystem = std::make_unique<EditorUISystem>(
|
||||
m_world, m_sceneMgr, getRenderWindow());
|
||||
if (m_uiSystem)
|
||||
m_uiSystem->setEditorUIEnabled(m_gameMode == GameMode::Editor);
|
||||
m_uiSystem->setEditorUIEnabled(m_gameMode ==
|
||||
GameMode::Editor);
|
||||
|
||||
// Setup physics system
|
||||
m_physicsSystem = std::make_unique<EditorPhysicsSystem>(
|
||||
@@ -238,13 +240,13 @@ void EditorApp::setup()
|
||||
// Setup ProceduralTexture system
|
||||
m_proceduralTextureSystem =
|
||||
std::make_unique<ProceduralTextureSystem>(m_world,
|
||||
m_sceneMgr);
|
||||
m_sceneMgr);
|
||||
m_proceduralTextureSystem->initialize();
|
||||
|
||||
// Setup ProceduralMaterial system
|
||||
m_proceduralMaterialSystem =
|
||||
std::make_unique<ProceduralMaterialSystem>(m_world,
|
||||
m_sceneMgr);
|
||||
m_sceneMgr);
|
||||
m_proceduralMaterialSystem->initialize();
|
||||
|
||||
// Setup ProceduralMesh system
|
||||
@@ -263,8 +265,8 @@ void EditorApp::setup()
|
||||
m_animationTreeSystem->initialize();
|
||||
|
||||
// Setup Character physics system
|
||||
m_characterSystem = std::make_unique<CharacterSystem>(
|
||||
m_world, m_sceneMgr);
|
||||
m_characterSystem =
|
||||
std::make_unique<CharacterSystem>(m_world, m_sceneMgr);
|
||||
m_characterSystem->initialize();
|
||||
|
||||
// Setup CellGrid system
|
||||
@@ -278,9 +280,8 @@ void EditorApp::setup()
|
||||
m_roomLayoutSystem->initialize();
|
||||
|
||||
// Setup game systems
|
||||
m_startupMenuSystem =
|
||||
std::make_unique<StartupMenuSystem>(m_world, m_sceneMgr,
|
||||
this);
|
||||
m_startupMenuSystem = std::make_unique<StartupMenuSystem>(
|
||||
m_world, m_sceneMgr, this);
|
||||
m_playerControllerSystem =
|
||||
std::make_unique<PlayerControllerSystem>(
|
||||
m_world, m_sceneMgr, this);
|
||||
@@ -317,7 +318,8 @@ void EditorApp::setup()
|
||||
|
||||
// Create and register ImGui render listener
|
||||
m_imguiListener = std::make_unique<ImGuiRenderListener>(
|
||||
m_imguiOverlay, m_uiSystem.get(), getRenderWindow(), this);
|
||||
m_imguiOverlay, m_uiSystem.get(), getRenderWindow(),
|
||||
this);
|
||||
getRenderWindow()->addListener(m_imguiListener.get());
|
||||
|
||||
// Register input listeners
|
||||
@@ -509,10 +511,13 @@ void EditorApp::createGrid()
|
||||
|
||||
grid->end();
|
||||
|
||||
Ogre::SceneNode *gridNode =
|
||||
m_gridNode =
|
||||
m_sceneMgr->getRootSceneNode()->createChildSceneNode(
|
||||
"GridNode");
|
||||
gridNode->attachObject(grid);
|
||||
m_gridNode->attachObject(grid);
|
||||
|
||||
// Set initial visibility based on game mode
|
||||
m_gridNode->setVisible(m_gameMode == GameMode::Editor);
|
||||
} catch (const std::exception &e) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"Grid creation failed: " + Ogre::String(e.what()));
|
||||
@@ -553,12 +558,15 @@ void EditorApp::createAxes()
|
||||
axisZ->end();
|
||||
|
||||
// Create axis node
|
||||
Ogre::SceneNode *axisNode =
|
||||
m_axisNode =
|
||||
m_sceneMgr->getRootSceneNode()->createChildSceneNode(
|
||||
"AxisNode");
|
||||
axisNode->attachObject(axisX);
|
||||
axisNode->attachObject(axisY);
|
||||
axisNode->attachObject(axisZ);
|
||||
m_axisNode->attachObject(axisX);
|
||||
m_axisNode->attachObject(axisY);
|
||||
m_axisNode->attachObject(axisZ);
|
||||
|
||||
// Set initial visibility based on game mode
|
||||
m_axisNode->setVisible(m_gameMode == GameMode::Editor);
|
||||
} catch (const std::exception &e) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"Axis creation failed: " + Ogre::String(e.what()));
|
||||
|
||||
@@ -123,14 +123,23 @@ public:
|
||||
|
||||
// Game mode management
|
||||
void setGameMode(GameMode mode);
|
||||
GameMode getGameMode() const { return m_gameMode; }
|
||||
GamePlayState getGamePlayState() const { return m_gamePlayState; }
|
||||
GameMode getGameMode() const
|
||||
{
|
||||
return m_gameMode;
|
||||
}
|
||||
GamePlayState getGamePlayState() const
|
||||
{
|
||||
return m_gamePlayState;
|
||||
}
|
||||
void setGamePlayState(GamePlayState state);
|
||||
void startNewGame(const Ogre::String &scenePath);
|
||||
void clearScene();
|
||||
|
||||
// Input access
|
||||
GameInputState &getGameInputState() { return m_gameInput; }
|
||||
GameInputState &getGameInputState()
|
||||
{
|
||||
return m_gameInput;
|
||||
}
|
||||
|
||||
// Getters
|
||||
flecs::entity getSelectedEntity() const;
|
||||
@@ -142,7 +151,10 @@ public:
|
||||
{
|
||||
return &m_world;
|
||||
}
|
||||
EditorCamera *getEditorCamera() const { return m_camera.get(); }
|
||||
EditorCamera *getEditorCamera() const
|
||||
{
|
||||
return m_camera.get();
|
||||
}
|
||||
AnimationTreeSystem *getAnimationTreeSystem() const
|
||||
{
|
||||
return m_animationTreeSystem.get();
|
||||
@@ -155,7 +167,10 @@ public:
|
||||
{
|
||||
return m_startupMenuSystem.get();
|
||||
}
|
||||
Ogre::ImGuiOverlay *getImGuiOverlay() const { return m_imguiOverlay; }
|
||||
Ogre::ImGuiOverlay *getImGuiOverlay() const
|
||||
{
|
||||
return m_imguiOverlay;
|
||||
}
|
||||
|
||||
private:
|
||||
// Ogre objects
|
||||
@@ -195,6 +210,10 @@ private:
|
||||
GamePlayState m_gamePlayState = GamePlayState::Menu;
|
||||
GameInputState m_gameInput;
|
||||
bool m_setupComplete = false;
|
||||
|
||||
// Editor visualization nodes
|
||||
Ogre::SceneNode *m_gridNode = nullptr;
|
||||
Ogre::SceneNode *m_axisNode = nullptr;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_EDITORAPP_HPP
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <string>
|
||||
#include <Ogre.h>
|
||||
#include <flecs.h>
|
||||
#include "ProceduralTexture.hpp"
|
||||
|
||||
/**
|
||||
* @brief Component for creating procedural Ogre materials
|
||||
@@ -11,32 +12,47 @@
|
||||
* The material can be referenced by name for use with meshes/entities.
|
||||
*/
|
||||
struct ProceduralMaterialComponent {
|
||||
// Material name for Ogre resource
|
||||
std::string materialName;
|
||||
|
||||
// Reference to entity with ProceduralTextureComponent (for diffuse)
|
||||
flecs::entity diffuseTextureEntity;
|
||||
|
||||
// Whether the material needs regeneration
|
||||
bool dirty = true;
|
||||
|
||||
// Whether the material has been created
|
||||
bool created = false;
|
||||
|
||||
// Pointer to the created Ogre material
|
||||
Ogre::MaterialPtr ogreMaterial;
|
||||
|
||||
// Track texture version for automatic rebuild when texture changes
|
||||
unsigned int textureVersion = 0;
|
||||
|
||||
// Material properties
|
||||
float ambient[3] = {0.2f, 0.2f, 0.2f}; // Ambient color (RGB 0-1)
|
||||
float diffuse[3] = {1.0f, 1.0f, 1.0f}; // Diffuse color multiplier (RGB 0-1)
|
||||
float specular[3] = {0.0f, 0.0f, 0.0f}; // Specular color (RGB 0-1)
|
||||
float shininess = 32.0f; // Specular shininess
|
||||
float roughness = 0.5f; // Roughness (0-1, for PBR-style)
|
||||
|
||||
void markDirty() {
|
||||
dirty = true;
|
||||
}
|
||||
// Unique identifier for persistent referencing across scene loads
|
||||
std::string materialId;
|
||||
|
||||
// Material name for Ogre resource
|
||||
std::string materialName;
|
||||
|
||||
// Persistent reference to texture by ID
|
||||
std::string diffuseTextureId;
|
||||
|
||||
// Runtime reference to entity with ProceduralTextureComponent (for diffuse)
|
||||
flecs::entity diffuseTextureEntity = flecs::entity::null();
|
||||
|
||||
// Whether the material needs regeneration
|
||||
bool dirty = true;
|
||||
|
||||
// Whether the material has been created
|
||||
bool created = false;
|
||||
|
||||
// Pointer to the created Ogre material
|
||||
Ogre::MaterialPtr ogreMaterial;
|
||||
|
||||
// Track texture version for automatic rebuild when texture changes
|
||||
unsigned int textureVersion = 0;
|
||||
|
||||
// Material properties
|
||||
float ambient[3] = { 0.2f, 0.2f, 0.2f }; // Ambient color (RGB 0-1)
|
||||
float diffuse[3] = { 1.0f, 1.0f,
|
||||
1.0f }; // Diffuse color multiplier (RGB 0-1)
|
||||
float specular[3] = { 0.0f, 0.0f, 0.0f }; // Specular color (RGB 0-1)
|
||||
float shininess = 32.0f; // Specular shininess
|
||||
float roughness = 0.5f; // Roughness (0-1, for PBR-style)
|
||||
|
||||
void markDirty()
|
||||
{
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// Helper to check if texture reference is valid
|
||||
bool hasValidTexture() const
|
||||
{
|
||||
return diffuseTextureEntity.is_alive() &&
|
||||
diffuseTextureEntity.has<ProceduralTextureComponent>();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,13 +10,26 @@
|
||||
* @brief Information about a named rectangle in the texture atlas
|
||||
*/
|
||||
struct TextureRectInfo {
|
||||
std::string name;
|
||||
float u1, v1; // Top-left UV (0-1 range)
|
||||
float u2, v2; // Bottom-right UV (0-1 range)
|
||||
|
||||
TextureRectInfo() : u1(0), v1(0), u2(1), v2(1) {}
|
||||
TextureRectInfo(const std::string& n, float left, float top, float right, float bottom)
|
||||
: name(n), u1(left), v1(top), u2(right), v2(bottom) {}
|
||||
std::string name;
|
||||
float u1, v1; // Top-left UV (0-1 range)
|
||||
float u2, v2; // Bottom-right UV (0-1 range)
|
||||
|
||||
TextureRectInfo()
|
||||
: u1(0)
|
||||
, v1(0)
|
||||
, u2(1)
|
||||
, v2(1)
|
||||
{
|
||||
}
|
||||
TextureRectInfo(const std::string &n, float left, float top,
|
||||
float right, float bottom)
|
||||
: name(n)
|
||||
, u1(left)
|
||||
, v1(top)
|
||||
, u2(right)
|
||||
, v2(bottom)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -27,129 +40,144 @@ struct TextureRectInfo {
|
||||
* Also supports named rectangles for texture atlas/UV mapping.
|
||||
*/
|
||||
struct ProceduralTextureComponent {
|
||||
// Texture name for Ogre resource
|
||||
std::string textureName;
|
||||
|
||||
// Grid dimensions (default 10x10)
|
||||
static constexpr int GRID_SIZE = 10;
|
||||
static constexpr int RECT_COUNT = GRID_SIZE * GRID_SIZE;
|
||||
|
||||
// Colors for each rectangle (stored as float4: r, g, b, a)
|
||||
std::array<float, RECT_COUNT * 4> colors;
|
||||
|
||||
// Named rectangles for texture atlas (name -> rect info)
|
||||
std::map<std::string, TextureRectInfo> namedRects;
|
||||
|
||||
// Texture size (default 512x512)
|
||||
int textureSize = 512;
|
||||
|
||||
// UV margin for texture mapping (default 0.01, range 0.01-0.025)
|
||||
// Used to prevent color bleeding by adding padding around UV coordinates
|
||||
float uvMargin = 0.01f;
|
||||
|
||||
// Whether the texture needs regeneration
|
||||
bool dirty = true;
|
||||
|
||||
// Whether the texture has been generated
|
||||
bool generated = false;
|
||||
|
||||
// Version counter - increments each time texture is regenerated
|
||||
// Used by dependent systems to detect texture changes
|
||||
unsigned int version = 0;
|
||||
|
||||
// Pointer to the generated Ogre texture
|
||||
Ogre::TexturePtr ogreTexture;
|
||||
|
||||
ProceduralTextureComponent() {
|
||||
// Initialize with default colors (checkerboard pattern)
|
||||
for (int i = 0; i < RECT_COUNT; ++i) {
|
||||
int row = i / GRID_SIZE;
|
||||
int col = i % GRID_SIZE;
|
||||
bool isWhite = (row + col) % 2 == 0;
|
||||
|
||||
colors[i * 4 + 0] = isWhite ? 1.0f : 0.0f; // R
|
||||
colors[i * 4 + 1] = isWhite ? 1.0f : 0.0f; // G
|
||||
colors[i * 4 + 2] = isWhite ? 1.0f : 0.0f; // B
|
||||
colors[i * 4 + 3] = 1.0f; // A
|
||||
}
|
||||
}
|
||||
|
||||
// Get color for a specific rectangle
|
||||
Ogre::ColourValue getColor(int index) const {
|
||||
if (index < 0 || index >= RECT_COUNT) return Ogre::ColourValue::White;
|
||||
return Ogre::ColourValue(
|
||||
colors[index * 4 + 0],
|
||||
colors[index * 4 + 1],
|
||||
colors[index * 4 + 2],
|
||||
colors[index * 4 + 3]
|
||||
);
|
||||
}
|
||||
|
||||
// Set color for a specific rectangle
|
||||
void setColor(int index, const Ogre::ColourValue& color) {
|
||||
if (index < 0 || index >= RECT_COUNT) return;
|
||||
colors[index * 4 + 0] = color.r;
|
||||
colors[index * 4 + 1] = color.g;
|
||||
colors[index * 4 + 2] = color.b;
|
||||
colors[index * 4 + 3] = color.a;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// Calculate UV coordinates for a rectangle index
|
||||
void getRectUVs(int index, float& u1, float& v1, float& u2, float& v2) const {
|
||||
int row = index / GRID_SIZE;
|
||||
int col = index % GRID_SIZE;
|
||||
float cellSize = 1.0f / GRID_SIZE;
|
||||
|
||||
u1 = col * cellSize;
|
||||
v1 = row * cellSize;
|
||||
u2 = (col + 1) * cellSize;
|
||||
v2 = (row + 1) * cellSize;
|
||||
}
|
||||
|
||||
// Add a named rectangle
|
||||
bool addNamedRect(const std::string& name, int rectIndex) {
|
||||
if (name.empty() || rectIndex < 0 || rectIndex >= RECT_COUNT) return false;
|
||||
|
||||
float u1, v1, u2, v2;
|
||||
getRectUVs(rectIndex, u1, v1, u2, v2);
|
||||
|
||||
namedRects[name] = TextureRectInfo(name, u1, v1, u2, v2);
|
||||
dirty = true; // Mark texture as dirty since rect atlas changed
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove a named rectangle
|
||||
bool removeNamedRect(const std::string& name) {
|
||||
auto it = namedRects.find(name);
|
||||
if (it != namedRects.end()) {
|
||||
namedRects.erase(it);
|
||||
dirty = true; // Mark texture as dirty since rect atlas changed
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get named rectangle info
|
||||
const TextureRectInfo* getNamedRect(const std::string& name) const {
|
||||
auto it = namedRects.find(name);
|
||||
if (it != namedRects.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if a name exists
|
||||
bool hasNamedRect(const std::string& name) const {
|
||||
return namedRects.find(name) != namedRects.end();
|
||||
}
|
||||
|
||||
// Get all named rectangles
|
||||
const std::map<std::string, TextureRectInfo>& getAllNamedRects() const {
|
||||
return namedRects;
|
||||
}
|
||||
|
||||
void markDirty() {
|
||||
dirty = true;
|
||||
}
|
||||
// Unique identifier for persistent referencing across scene loads
|
||||
std::string textureId;
|
||||
|
||||
// Texture name for Ogre resource
|
||||
std::string textureName;
|
||||
|
||||
// Grid dimensions (default 10x10)
|
||||
static constexpr int GRID_SIZE = 10;
|
||||
static constexpr int RECT_COUNT = GRID_SIZE * GRID_SIZE;
|
||||
|
||||
// Colors for each rectangle (stored as float4: r, g, b, a)
|
||||
std::array<float, RECT_COUNT * 4> colors;
|
||||
|
||||
// Named rectangles for texture atlas (name -> rect info)
|
||||
std::map<std::string, TextureRectInfo> namedRects;
|
||||
|
||||
// Texture size (default 512x512)
|
||||
int textureSize = 512;
|
||||
|
||||
// UV margin for texture mapping (default 0.01, range 0.01-0.025)
|
||||
// Used to prevent color bleeding by adding padding around UV coordinates
|
||||
float uvMargin = 0.01f;
|
||||
|
||||
// Whether the texture needs regeneration
|
||||
bool dirty = true;
|
||||
|
||||
// Whether the texture has been generated
|
||||
bool generated = false;
|
||||
|
||||
// Version counter - increments each time texture is regenerated
|
||||
// Used by dependent systems to detect texture changes
|
||||
unsigned int version = 0;
|
||||
|
||||
// Pointer to the generated Ogre texture
|
||||
Ogre::TexturePtr ogreTexture;
|
||||
|
||||
ProceduralTextureComponent()
|
||||
{
|
||||
// Initialize with default colors (checkerboard pattern)
|
||||
for (int i = 0; i < RECT_COUNT; ++i) {
|
||||
int row = i / GRID_SIZE;
|
||||
int col = i % GRID_SIZE;
|
||||
bool isWhite = (row + col) % 2 == 0;
|
||||
|
||||
colors[i * 4 + 0] = isWhite ? 1.0f : 0.0f; // R
|
||||
colors[i * 4 + 1] = isWhite ? 1.0f : 0.0f; // G
|
||||
colors[i * 4 + 2] = isWhite ? 1.0f : 0.0f; // B
|
||||
colors[i * 4 + 3] = 1.0f; // A
|
||||
}
|
||||
}
|
||||
|
||||
// Get color for a specific rectangle
|
||||
Ogre::ColourValue getColor(int index) const
|
||||
{
|
||||
if (index < 0 || index >= RECT_COUNT)
|
||||
return Ogre::ColourValue::White;
|
||||
return Ogre::ColourValue(colors[index * 4 + 0],
|
||||
colors[index * 4 + 1],
|
||||
colors[index * 4 + 2],
|
||||
colors[index * 4 + 3]);
|
||||
}
|
||||
|
||||
// Set color for a specific rectangle
|
||||
void setColor(int index, const Ogre::ColourValue &color)
|
||||
{
|
||||
if (index < 0 || index >= RECT_COUNT)
|
||||
return;
|
||||
colors[index * 4 + 0] = color.r;
|
||||
colors[index * 4 + 1] = color.g;
|
||||
colors[index * 4 + 2] = color.b;
|
||||
colors[index * 4 + 3] = color.a;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// Calculate UV coordinates for a rectangle index
|
||||
void getRectUVs(int index, float &u1, float &v1, float &u2,
|
||||
float &v2) const
|
||||
{
|
||||
int row = index / GRID_SIZE;
|
||||
int col = index % GRID_SIZE;
|
||||
float cellSize = 1.0f / GRID_SIZE;
|
||||
|
||||
u1 = col * cellSize;
|
||||
v1 = row * cellSize;
|
||||
u2 = (col + 1) * cellSize;
|
||||
v2 = (row + 1) * cellSize;
|
||||
}
|
||||
|
||||
// Add a named rectangle
|
||||
bool addNamedRect(const std::string &name, int rectIndex)
|
||||
{
|
||||
if (name.empty() || rectIndex < 0 || rectIndex >= RECT_COUNT)
|
||||
return false;
|
||||
|
||||
float u1, v1, u2, v2;
|
||||
getRectUVs(rectIndex, u1, v1, u2, v2);
|
||||
|
||||
namedRects[name] = TextureRectInfo(name, u1, v1, u2, v2);
|
||||
dirty = true; // Mark texture as dirty since rect atlas changed
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove a named rectangle
|
||||
bool removeNamedRect(const std::string &name)
|
||||
{
|
||||
auto it = namedRects.find(name);
|
||||
if (it != namedRects.end()) {
|
||||
namedRects.erase(it);
|
||||
dirty = true; // Mark texture as dirty since rect atlas changed
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get named rectangle info
|
||||
const TextureRectInfo *getNamedRect(const std::string &name) const
|
||||
{
|
||||
auto it = namedRects.find(name);
|
||||
if (it != namedRects.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if a name exists
|
||||
bool hasNamedRect(const std::string &name) const
|
||||
{
|
||||
return namedRects.find(name) != namedRects.end();
|
||||
}
|
||||
|
||||
// Get all named rectangles
|
||||
const std::map<std::string, TextureRectInfo> &getAllNamedRects() const
|
||||
{
|
||||
return namedRects;
|
||||
}
|
||||
|
||||
void markDirty()
|
||||
{
|
||||
dirty = true;
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,170 +2,207 @@
|
||||
#include "../components/ProceduralTexture.hpp"
|
||||
#include <imgui.h>
|
||||
|
||||
ProceduralMaterialEditor::ProceduralMaterialEditor(Ogre::SceneManager* sceneMgr)
|
||||
ProceduralMaterialEditor::ProceduralMaterialEditor(Ogre::SceneManager *sceneMgr)
|
||||
: m_sceneMgr(sceneMgr)
|
||||
{
|
||||
}
|
||||
|
||||
void ProceduralMaterialEditor::renderTextureSelector(flecs::entity entity, ProceduralMaterialComponent &material)
|
||||
void ProceduralMaterialEditor::renderTextureSelector(
|
||||
flecs::entity entity, ProceduralMaterialComponent &material)
|
||||
{
|
||||
// Get the world from the entity
|
||||
flecs::world world = entity.world();
|
||||
|
||||
|
||||
// Collect all entities with ProceduralTextureComponent
|
||||
std::vector<flecs::entity> textureEntities;
|
||||
std::vector<std::string> textureNames;
|
||||
std::vector<std::string> displayNames;
|
||||
|
||||
|
||||
int currentIndex = -1;
|
||||
int noneIndex = 0;
|
||||
|
||||
|
||||
// Add "None" option
|
||||
displayNames.push_back("None");
|
||||
textureEntities.push_back(flecs::entity::null());
|
||||
textureNames.push_back("");
|
||||
|
||||
world.query<ProceduralTextureComponent>().each([&](flecs::entity e, ProceduralTextureComponent& texture) {
|
||||
textureEntities.push_back(e);
|
||||
|
||||
std::string name = texture.textureName.empty() ?
|
||||
"Texture " + std::to_string(e.id()) : texture.textureName;
|
||||
textureNames.push_back(name);
|
||||
|
||||
// Build display name
|
||||
std::string display = name;
|
||||
if (texture.generated) {
|
||||
display += " (generated)";
|
||||
} else {
|
||||
display += " (pending)";
|
||||
}
|
||||
displayNames.push_back(display);
|
||||
|
||||
if (material.diffuseTextureEntity == e) {
|
||||
currentIndex = (int)textureEntities.size() - 1;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
world.query<ProceduralTextureComponent>().each(
|
||||
[&](flecs::entity e, ProceduralTextureComponent &texture) {
|
||||
textureEntities.push_back(e);
|
||||
|
||||
std::string name =
|
||||
texture.textureName.empty() ?
|
||||
"Texture " + std::to_string(e.id()) :
|
||||
texture.textureName;
|
||||
textureNames.push_back(name);
|
||||
|
||||
// Build display name
|
||||
std::string display = name;
|
||||
if (texture.generated) {
|
||||
display += " (generated)";
|
||||
} else {
|
||||
display += " (pending)";
|
||||
}
|
||||
displayNames.push_back(display);
|
||||
|
||||
if (material.diffuseTextureEntity == e) {
|
||||
currentIndex = (int)textureEntities.size() - 1;
|
||||
}
|
||||
});
|
||||
|
||||
// Default to none if not found
|
||||
if (currentIndex == -1) {
|
||||
currentIndex = noneIndex;
|
||||
}
|
||||
|
||||
|
||||
// Build combo string
|
||||
std::string comboItems;
|
||||
for (size_t i = 0; i < displayNames.size(); ++i) {
|
||||
if (i > 0) comboItems += '\0';
|
||||
if (i > 0)
|
||||
comboItems += '\0';
|
||||
comboItems += displayNames[i];
|
||||
}
|
||||
comboItems += '\0';
|
||||
|
||||
|
||||
int newIndex = currentIndex;
|
||||
if (ImGui::Combo("Diffuse Texture", &newIndex, comboItems.c_str())) {
|
||||
if (newIndex >= 0 && newIndex < (int)textureEntities.size()) {
|
||||
if (newIndex == noneIndex) {
|
||||
material.diffuseTextureEntity = flecs::entity::null();
|
||||
material.diffuseTextureEntity =
|
||||
flecs::entity::null();
|
||||
material.diffuseTextureId = "";
|
||||
} else {
|
||||
material.diffuseTextureEntity = textureEntities[newIndex];
|
||||
material.diffuseTextureEntity =
|
||||
textureEntities[newIndex];
|
||||
// Also store the persistent texture ID
|
||||
if (material.diffuseTextureEntity.is_alive() &&
|
||||
material.diffuseTextureEntity
|
||||
.has<ProceduralTextureComponent>()) {
|
||||
const auto &texture =
|
||||
material.diffuseTextureEntity.get<
|
||||
ProceduralTextureComponent>();
|
||||
material.diffuseTextureId =
|
||||
texture.textureId;
|
||||
} else {
|
||||
material.diffuseTextureId = "";
|
||||
}
|
||||
}
|
||||
material.markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Show warning if no textures available
|
||||
if (textureEntities.size() <= 1) {
|
||||
ImGui::TextColored(ImVec4(1, 0.5f, 0, 1), "Create a Procedural Texture first!");
|
||||
ImGui::TextColored(ImVec4(1, 0.5f, 0, 1),
|
||||
"Create a Procedural Texture first!");
|
||||
}
|
||||
}
|
||||
|
||||
bool ProceduralMaterialEditor::renderComponent(flecs::entity entity, ProceduralMaterialComponent &material)
|
||||
bool ProceduralMaterialEditor::renderComponent(
|
||||
flecs::entity entity, ProceduralMaterialComponent &material)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
if (ImGui::CollapsingHeader("Procedural Material", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
|
||||
if (ImGui::CollapsingHeader("Procedural Material",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Indent();
|
||||
|
||||
|
||||
// Material info
|
||||
if (material.created) {
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), "Status: Created");
|
||||
ImGui::Text("Material: %s", material.materialName.c_str());
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1),
|
||||
"Status: Created");
|
||||
ImGui::Text("Material: %s",
|
||||
material.materialName.c_str());
|
||||
} else if (material.dirty) {
|
||||
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Status: Needs Creation");
|
||||
ImGui::TextColored(ImVec4(1, 1, 0, 1),
|
||||
"Status: Needs Creation");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1), "Status: Not Created");
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1),
|
||||
"Status: Not Created");
|
||||
}
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Material name
|
||||
char nameBuf[256];
|
||||
strncpy(nameBuf, material.materialName.c_str(), sizeof(nameBuf) - 1);
|
||||
strncpy(nameBuf, material.materialName.c_str(),
|
||||
sizeof(nameBuf) - 1);
|
||||
nameBuf[sizeof(nameBuf) - 1] = '\0';
|
||||
|
||||
if (ImGui::InputText("Material Name", nameBuf, sizeof(nameBuf))) {
|
||||
|
||||
if (ImGui::InputText("Material Name", nameBuf,
|
||||
sizeof(nameBuf))) {
|
||||
material.materialName = nameBuf;
|
||||
material.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
ImGui::TextDisabled("(Leave empty for auto-generated name)");
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Diffuse texture selector
|
||||
renderTextureSelector(entity, material);
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Material colors
|
||||
ImGui::Text("Material Properties:");
|
||||
|
||||
|
||||
if (ImGui::ColorEdit3("Ambient", material.ambient)) {
|
||||
material.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::ColorEdit3("Diffuse Multiplier", material.diffuse)) {
|
||||
material.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::ColorEdit3("Specular", material.specular)) {
|
||||
material.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ImGui::SliderFloat("Shininess", &material.shininess, 1.0f, 128.0f)) {
|
||||
|
||||
if (ImGui::SliderFloat("Shininess", &material.shininess, 1.0f,
|
||||
128.0f)) {
|
||||
material.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (ImGui::SliderFloat("Roughness", &material.roughness, 0.0f, 1.0f)) {
|
||||
|
||||
if (ImGui::SliderFloat("Roughness", &material.roughness, 0.0f,
|
||||
1.0f)) {
|
||||
material.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Recreate button
|
||||
if (ImGui::Button("Recreate Material")) {
|
||||
material.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
// Reset to defaults
|
||||
if (ImGui::Button("Reset to Defaults")) {
|
||||
material.ambient[0] = 0.2f; material.ambient[1] = 0.2f; material.ambient[2] = 0.2f;
|
||||
material.diffuse[0] = 1.0f; material.diffuse[1] = 1.0f; material.diffuse[2] = 1.0f;
|
||||
material.specular[0] = 0.0f; material.specular[1] = 0.0f; material.specular[2] = 0.0f;
|
||||
material.ambient[0] = 0.2f;
|
||||
material.ambient[1] = 0.2f;
|
||||
material.ambient[2] = 0.2f;
|
||||
material.diffuse[0] = 1.0f;
|
||||
material.diffuse[1] = 1.0f;
|
||||
material.diffuse[2] = 1.0f;
|
||||
material.specular[0] = 0.0f;
|
||||
material.specular[1] = 0.0f;
|
||||
material.specular[2] = 0.0f;
|
||||
material.shininess = 32.0f;
|
||||
material.roughness = 0.5f;
|
||||
material.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user