Navmesh generation works with cell grids
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include "../ui/ComponentRegistration.hpp"
|
||||
#include "../ui/ProceduralMaterialEditor.hpp"
|
||||
#include <OgreMaterialManager.h>
|
||||
#include <OgreRTShaderSystem.h>
|
||||
|
||||
// Register ProceduralMaterial component
|
||||
REGISTER_COMPONENT_GROUP("Procedural Material", "Material", ProceduralMaterialComponent, ProceduralMaterialEditor)
|
||||
@@ -22,6 +23,12 @@ REGISTER_COMPONENT_GROUP("Procedural Material", "Material", ProceduralMaterialCo
|
||||
// Clean up Ogre material - wrap in try/catch since MaterialManager may be shutting down
|
||||
if (material.ogreMaterial) {
|
||||
try {
|
||||
Ogre::RTShader::ShaderGenerator *shaderGen =
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
if (shaderGen) {
|
||||
shaderGen->removeAllShaderBasedTechniques(
|
||||
*material.ogreMaterial);
|
||||
}
|
||||
if (Ogre::MaterialManager::getSingletonPtr()) {
|
||||
Ogre::MaterialManager::getSingleton().remove(material.ogreMaterial);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,14 @@ static bool isNavMeshGeometry(const Ogre::String &meshName)
|
||||
Ogre::String lower = meshName;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
static const char *exclude[] = { "wall", "ceiling", "roof",
|
||||
"door", "window", "frame",
|
||||
"furniture" };
|
||||
|
||||
// Exclude geometry that doesn't affect floor-level navigation.
|
||||
// Ceilings are above walkable floors and don't block movement.
|
||||
// Furniture is handled separately (or excluded to keep navmesh
|
||||
// focused on architectural walkable surfaces).
|
||||
// Frames placed via StaticGeometry aren't traversed by the
|
||||
// SceneNode walker anyway.
|
||||
static const char *exclude[] = { "ceiling", "frame", "furniture" };
|
||||
for (const char *pat : exclude) {
|
||||
if (lower.find(pat) != Ogre::String::npos)
|
||||
return false;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <OgreTechnique.h>
|
||||
#include <OgrePass.h>
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreRTShaderSystem.h>
|
||||
#include <OgreMeshLodGenerator.h>
|
||||
#include <ProceduralTriangleBuffer.h>
|
||||
#include <ProceduralBoxGenerator.h>
|
||||
@@ -2639,6 +2640,19 @@ void CellGridSystem::updateTownMaterial(flecs::entity townEntity)
|
||||
pass->setAmbient(Ogre::ColourValue(0.4f, 0.4f, 0.4f));
|
||||
pass->setSpecular(Ogre::ColourValue(0.1f, 0.1f, 0.1f));
|
||||
|
||||
// Register with RTSS so the material works on shader-only render systems
|
||||
Ogre::RTShader::ShaderGenerator *shaderGen =
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
if (shaderGen) {
|
||||
shaderGen->createShaderBasedTechnique(
|
||||
*mat,
|
||||
Ogre::MaterialManager::DEFAULT_SCHEME_NAME,
|
||||
Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
|
||||
shaderGen->validateMaterial(
|
||||
Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME,
|
||||
town.materialName);
|
||||
}
|
||||
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"CellGrid: Updated town material '" + town.materialName + "'");
|
||||
}
|
||||
|
||||
@@ -19,14 +19,13 @@ static bool isNavMeshGeometry(const Ogre::String &meshName)
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
|
||||
// Exclude vertical / enclosing geometry that causes Recast
|
||||
// span-merging artefacts. When a floor span merges with a wall
|
||||
// span the walkable surface is pushed to the top of the wall;
|
||||
// if a roof is close above, rcFilterWalkableLowHeightSpans
|
||||
// removes the whole column.
|
||||
static const char *exclude[] = { "wall", "ceiling", "roof",
|
||||
"door", "window", "frame",
|
||||
"furniture" };
|
||||
// Exclude geometry that doesn't affect floor-level navigation.
|
||||
// Ceilings are above walkable floors and don't block movement.
|
||||
// Furniture is handled separately (or excluded to keep navmesh
|
||||
// focused on architectural walkable surfaces).
|
||||
// Frames placed via StaticGeometry aren't traversed by the
|
||||
// SceneNode walker anyway.
|
||||
static const char *exclude[] = { "ceiling", "frame", "furniture" };
|
||||
for (const char *pat : exclude) {
|
||||
if (lower.find(pat) != Ogre::String::npos)
|
||||
return false;
|
||||
|
||||
@@ -67,22 +67,32 @@ void ProceduralMaterialSystem::createMaterial(
|
||||
"_" + std::to_string(m_createdCount++);
|
||||
}
|
||||
|
||||
// Destroy old material if exists
|
||||
if (component.ogreMaterial) {
|
||||
destroyMaterial(component);
|
||||
Ogre::MaterialManager &matMgr =
|
||||
Ogre::MaterialManager::getSingleton();
|
||||
|
||||
// Re-use the existing material object when possible.
|
||||
// Destroying and recreating breaks cached MaterialPtr
|
||||
// references held by Ogre::SubEntity instances that use
|
||||
// this material.
|
||||
if (!component.ogreMaterial) {
|
||||
if (matMgr.resourceExists(component.materialName)) {
|
||||
component.ogreMaterial =
|
||||
matMgr.getByName(component.materialName);
|
||||
} else {
|
||||
component.ogreMaterial = matMgr.create(
|
||||
component.materialName,
|
||||
Ogre::ResourceGroupManager::
|
||||
DEFAULT_RESOURCE_GROUP_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Create new material
|
||||
component.ogreMaterial =
|
||||
Ogre::MaterialManager::getSingleton().create(
|
||||
component.materialName,
|
||||
Ogre::ResourceGroupManager::
|
||||
DEFAULT_RESOURCE_GROUP_NAME);
|
||||
|
||||
// Get the default technique and pass
|
||||
// Clear existing technique and rebuild passes so that every
|
||||
// SubEntity::mMaterialPtr (which points to this same object)
|
||||
// sees the updated state.
|
||||
Ogre::Technique *technique =
|
||||
component.ogreMaterial->getTechnique(0);
|
||||
Ogre::Pass *pass = technique->getPass(0);
|
||||
technique->removeAllPasses();
|
||||
Ogre::Pass *pass = technique->createPass();
|
||||
|
||||
// Set material colors
|
||||
pass->setAmbient(component.ambient[0], component.ambient[1],
|
||||
@@ -116,10 +126,12 @@ void ProceduralMaterialSystem::createMaterial(
|
||||
}
|
||||
}
|
||||
|
||||
// Register with RTSS to generate shaders (fixes "RenderSystem does not support FixedFunction")
|
||||
// Re-generate RTSS shaders for the updated material
|
||||
Ogre::RTShader::ShaderGenerator *shaderGen =
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
if (shaderGen && component.ogreMaterial) {
|
||||
shaderGen->removeAllShaderBasedTechniques(
|
||||
*component.ogreMaterial);
|
||||
shaderGen->createShaderBasedTechnique(
|
||||
*component.ogreMaterial,
|
||||
Ogre::MaterialManager::DEFAULT_SCHEME_NAME,
|
||||
@@ -136,7 +148,7 @@ void ProceduralMaterialSystem::createMaterial(
|
||||
component.textureVersion = currentTexVersion;
|
||||
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"ProceduralMaterial: Created '" +
|
||||
"ProceduralMaterial: Updated '" +
|
||||
component.materialName + "'");
|
||||
|
||||
} catch (const Ogre::Exception &e) {
|
||||
@@ -151,6 +163,15 @@ void ProceduralMaterialSystem::destroyMaterial(
|
||||
{
|
||||
if (component.ogreMaterial) {
|
||||
try {
|
||||
// Remove RTSS shader techniques before destroying the
|
||||
// material, otherwise the shader generator keeps stale
|
||||
// cache entries keyed by material name.
|
||||
Ogre::RTShader::ShaderGenerator *shaderGen =
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
if (shaderGen) {
|
||||
shaderGen->removeAllShaderBasedTechniques(
|
||||
*component.ogreMaterial);
|
||||
}
|
||||
Ogre::MaterialManager::getSingleton().remove(
|
||||
component.ogreMaterial);
|
||||
} catch (...) {
|
||||
|
||||
@@ -6,128 +6,147 @@
|
||||
#include <OgreImage.h>
|
||||
#include <OgreLogManager.h>
|
||||
|
||||
void ProceduralTextureEditor::renderColorButton(int index, ProceduralTextureComponent &texture)
|
||||
void ProceduralTextureEditor::renderColorButton(
|
||||
int index, ProceduralTextureComponent &texture)
|
||||
{
|
||||
Ogre::ColourValue color = texture.getColor(index);
|
||||
|
||||
|
||||
// Create a unique ID for this button
|
||||
ImGui::PushID(index);
|
||||
|
||||
|
||||
// Show color as a small colored button
|
||||
ImVec4 buttonColor(color.r, color.g, color.b, color.a);
|
||||
ImGui::ColorButton("##color", buttonColor, ImGuiColorEditFlags_NoTooltip, ImVec2(24, 24));
|
||||
|
||||
ImGui::ColorButton("##color", buttonColor,
|
||||
ImGuiColorEditFlags_NoTooltip, ImVec2(24, 24));
|
||||
|
||||
// Handle click
|
||||
if (ImGui::IsItemClicked()) {
|
||||
m_selectedRect = index;
|
||||
}
|
||||
|
||||
|
||||
// Show tooltip with rectangle index
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Rect %d: (%.2f, %.2f, %.2f)", index, color.r, color.g, color.b);
|
||||
ImGui::SetTooltip("Rect %d: (%.2f, %.2f, %.2f)", index, color.r,
|
||||
color.g, color.b);
|
||||
}
|
||||
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void ProceduralTextureEditor::renderColorGrid(flecs::entity entity, ProceduralTextureComponent &texture)
|
||||
void ProceduralTextureEditor::renderColorGrid(
|
||||
flecs::entity entity, ProceduralTextureComponent &texture)
|
||||
{
|
||||
const int gridSize = ProceduralTextureComponent::GRID_SIZE;
|
||||
|
||||
ImGui::Text("10x10 Grid - Click a rectangle to edit its color or add to atlas:");
|
||||
|
||||
|
||||
ImGui::Text(
|
||||
"10x10 Grid - Click a rectangle to edit its color or add to atlas:");
|
||||
|
||||
// Calculate grid width for centering
|
||||
float buttonSize = 24.0f;
|
||||
float spacing = 2.0f;
|
||||
float gridWidth = gridSize * buttonSize + (gridSize - 1) * spacing;
|
||||
|
||||
|
||||
// Get window width for centering
|
||||
float windowWidth = ImGui::GetContentRegionAvail().x;
|
||||
float startX = ImGui::GetCursorPosX();
|
||||
float offset = (windowWidth - gridWidth) * 0.5f;
|
||||
if (offset < 0) offset = 0;
|
||||
|
||||
if (offset < 0)
|
||||
offset = 0;
|
||||
|
||||
// Render the grid - each row needs its own offset
|
||||
for (int row = 0; row < gridSize; ++row) {
|
||||
// Set cursor position for this row
|
||||
ImGui::SetCursorPosX(startX + offset);
|
||||
|
||||
|
||||
for (int col = 0; col < gridSize; ++col) {
|
||||
int index = row * gridSize + col;
|
||||
renderColorButton(index, texture);
|
||||
|
||||
|
||||
if (col < gridSize - 1) {
|
||||
ImGui::SameLine(0, spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Color picker and naming for selected rectangle
|
||||
if (m_selectedRect >= 0 && m_selectedRect < ProceduralTextureComponent::RECT_COUNT) {
|
||||
if (m_selectedRect >= 0 &&
|
||||
m_selectedRect < ProceduralTextureComponent::RECT_COUNT) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Editing Rectangle %d (Row %d, Col %d)",
|
||||
m_selectedRect,
|
||||
m_selectedRect / gridSize,
|
||||
m_selectedRect % gridSize);
|
||||
|
||||
ImGui::Text("Editing Rectangle %d (Row %d, Col %d)",
|
||||
m_selectedRect, m_selectedRect / gridSize,
|
||||
m_selectedRect % gridSize);
|
||||
|
||||
// UV coordinates display
|
||||
float u1, v1, u2, v2;
|
||||
texture.getRectUVs(m_selectedRect, u1, v1, u2, v2);
|
||||
ImGui::TextDisabled("UVs: (%.3f, %.3f) - (%.3f, %.3f)", u1, v1, u2, v2);
|
||||
|
||||
Ogre::ColourValue currentColor = texture.getColor(m_selectedRect);
|
||||
float colorArray[4] = { currentColor.r, currentColor.g, currentColor.b, currentColor.a };
|
||||
|
||||
ImGui::TextDisabled("UVs: (%.3f, %.3f) - (%.3f, %.3f)", u1, v1,
|
||||
u2, v2);
|
||||
|
||||
Ogre::ColourValue currentColor =
|
||||
texture.getColor(m_selectedRect);
|
||||
float colorArray[4] = { currentColor.r, currentColor.g,
|
||||
currentColor.b, currentColor.a };
|
||||
|
||||
if (ImGui::ColorEdit4("Color", colorArray)) {
|
||||
Ogre::ColourValue newColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
|
||||
Ogre::ColourValue newColor(colorArray[0], colorArray[1],
|
||||
colorArray[2],
|
||||
colorArray[3]);
|
||||
texture.setColor(m_selectedRect, newColor);
|
||||
}
|
||||
|
||||
|
||||
// Add to atlas section
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Add to Texture Atlas:");
|
||||
|
||||
|
||||
ImGui::InputText("Name", m_nameBuffer, sizeof(m_nameBuffer));
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
if (ImGui::Button("Add")) {
|
||||
if (strlen(m_nameBuffer) > 0) {
|
||||
if (texture.hasNamedRect(m_nameBuffer)) {
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Name already exists!");
|
||||
ImGui::TextColored(
|
||||
ImVec4(1, 0, 0, 1),
|
||||
"Name already exists!");
|
||||
} else {
|
||||
texture.addNamedRect(m_nameBuffer, m_selectedRect);
|
||||
m_nameBuffer[0] = '\0'; // Clear buffer
|
||||
texture.addNamedRect(m_nameBuffer,
|
||||
m_selectedRect);
|
||||
m_nameBuffer[0] = '\0'; // Clear buffer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::Button("Deselect")) {
|
||||
m_selectedRect = -1;
|
||||
m_nameBuffer[0] = '\0';
|
||||
}
|
||||
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
// Preset colors
|
||||
ImGui::Text("Presets:");
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
const ImVec4 presets[] = {
|
||||
ImVec4(1, 1, 1, 1), // White
|
||||
ImVec4(0, 0, 0, 1), // Black
|
||||
ImVec4(1, 0, 0, 1), // Red
|
||||
ImVec4(0, 1, 0, 1), // Green
|
||||
ImVec4(0, 0, 1, 1), // Blue
|
||||
ImVec4(1, 1, 0, 1), // Yellow
|
||||
ImVec4(1, 0, 1, 1), // Magenta
|
||||
ImVec4(0, 1, 1, 1), // Cyan
|
||||
ImVec4(0.5f, 0.5f, 0.5f, 1), // Gray
|
||||
ImVec4(1, 1, 1, 1), // White
|
||||
ImVec4(0, 0, 0, 1), // Black
|
||||
ImVec4(1, 0, 0, 1), // Red
|
||||
ImVec4(0, 1, 0, 1), // Green
|
||||
ImVec4(0, 0, 1, 1), // Blue
|
||||
ImVec4(1, 1, 0, 1), // Yellow
|
||||
ImVec4(1, 0, 1, 1), // Magenta
|
||||
ImVec4(0, 1, 1, 1), // Cyan
|
||||
ImVec4(0.5f, 0.5f, 0.5f, 1), // Gray
|
||||
};
|
||||
|
||||
|
||||
for (size_t i = 0; i < IM_ARRAYSIZE(presets); ++i) {
|
||||
ImGui::PushID((int)i);
|
||||
if (ImGui::ColorButton("##preset", presets[i], ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20))) {
|
||||
Ogre::ColourValue presetColor(presets[i].x, presets[i].y, presets[i].z, presets[i].w);
|
||||
if (ImGui::ColorButton("##preset", presets[i],
|
||||
ImGuiColorEditFlags_NoTooltip,
|
||||
ImVec2(20, 20))) {
|
||||
Ogre::ColourValue presetColor(presets[i].x,
|
||||
presets[i].y,
|
||||
presets[i].z,
|
||||
presets[i].w);
|
||||
texture.setColor(m_selectedRect, presetColor);
|
||||
}
|
||||
ImGui::PopID();
|
||||
@@ -138,35 +157,40 @@ void ProceduralTextureEditor::renderColorGrid(flecs::entity entity, ProceduralTe
|
||||
}
|
||||
}
|
||||
|
||||
void ProceduralTextureEditor::renderNamedRects(flecs::entity entity, ProceduralTextureComponent &texture)
|
||||
void ProceduralTextureEditor::renderNamedRects(
|
||||
flecs::entity entity, ProceduralTextureComponent &texture)
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Texture Atlas (Named Rectangles):");
|
||||
|
||||
const auto& namedRects = texture.getAllNamedRects();
|
||||
|
||||
|
||||
const auto &namedRects = texture.getAllNamedRects();
|
||||
|
||||
if (namedRects.empty()) {
|
||||
ImGui::TextDisabled("No named rectangles. Select a rectangle above and add it to the atlas.");
|
||||
ImGui::TextDisabled(
|
||||
"No named rectangles. Select a rectangle above and add it to the atlas.");
|
||||
} else {
|
||||
// Table header
|
||||
if (ImGui::BeginTable("NamedRects", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (ImGui::BeginTable("NamedRects", 3,
|
||||
ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("UV Coordinates");
|
||||
ImGui::TableSetupColumn("Action");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto& pair : namedRects) {
|
||||
const auto& rect = pair.second;
|
||||
|
||||
for (const auto &pair : namedRects) {
|
||||
const auto &rect = pair.second;
|
||||
ImGui::TableNextRow();
|
||||
|
||||
|
||||
// Name column
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", rect.name.c_str());
|
||||
|
||||
|
||||
// UVs column
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("(%.3f, %.3f) - (%.3f, %.3f)", rect.u1, rect.v1, rect.u2, rect.v2);
|
||||
|
||||
ImGui::Text("(%.3f, %.3f) - (%.3f, %.3f)",
|
||||
rect.u1, rect.v1, rect.u2, rect.v2);
|
||||
|
||||
// Action column
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(rect.name.c_str());
|
||||
@@ -175,49 +199,62 @@ void ProceduralTextureEditor::renderNamedRects(flecs::entity entity, ProceduralT
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TextDisabled("Total: %zu named rectangles", namedRects.size());
|
||||
|
||||
ImGui::TextDisabled("Total: %zu named rectangles",
|
||||
namedRects.size());
|
||||
}
|
||||
}
|
||||
|
||||
bool ProceduralTextureEditor::renderComponent(flecs::entity entity, ProceduralTextureComponent &texture)
|
||||
bool ProceduralTextureEditor::renderComponent(
|
||||
flecs::entity entity, ProceduralTextureComponent &texture)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
if (ImGui::CollapsingHeader("Procedural Texture", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
|
||||
if (ImGui::CollapsingHeader("Procedural Texture",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Indent();
|
||||
|
||||
|
||||
// Texture info
|
||||
if (texture.generated) {
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), "Status: Generated");
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1),
|
||||
"Status: Generated");
|
||||
ImGui::Text("Texture: %s", texture.textureName.c_str());
|
||||
ImGui::Text("Size: %dx%d", texture.textureSize, texture.textureSize);
|
||||
|
||||
ImGui::Text("Size: %dx%d", texture.textureSize,
|
||||
texture.textureSize);
|
||||
|
||||
// Save PNG button
|
||||
if (ImGui::Button("Save as PNG...")) {
|
||||
ImGui::OpenPopup("SaveTexturePopup");
|
||||
}
|
||||
|
||||
|
||||
// Save popup
|
||||
if (ImGui::BeginPopup("SaveTexturePopup")) {
|
||||
static char filename[256] = "procedural_texture.png";
|
||||
ImGui::InputText("Filename", filename, sizeof(filename));
|
||||
|
||||
static char filename[256] =
|
||||
"procedural_texture.png";
|
||||
ImGui::InputText("Filename", filename,
|
||||
sizeof(filename));
|
||||
|
||||
if (ImGui::Button("Save", ImVec2(120, 0))) {
|
||||
try {
|
||||
if (texture.ogreTexture) {
|
||||
Ogre::Image image;
|
||||
texture.ogreTexture->convertToImage(image);
|
||||
texture.ogreTexture
|
||||
->convertToImage(
|
||||
image);
|
||||
image.save(filename);
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"ProceduralTexture: Saved to " + std::string(filename));
|
||||
Ogre::LogManager::getSingleton()
|
||||
.logMessage(
|
||||
"ProceduralTexture: Saved to " +
|
||||
std::string(
|
||||
filename));
|
||||
}
|
||||
} catch (const Ogre::Exception& e) {
|
||||
} catch (const Ogre::Exception &e) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"ProceduralTexture ERROR: Failed to save: " + e.getDescription());
|
||||
"ProceduralTexture ERROR: Failed to save: " +
|
||||
e.getDescription());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
@@ -228,91 +265,106 @@ bool ProceduralTextureEditor::renderComponent(flecs::entity entity, ProceduralTe
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
} else if (texture.dirty) {
|
||||
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Status: Needs Generation");
|
||||
ImGui::TextColored(ImVec4(1, 1, 0, 1),
|
||||
"Status: Needs Generation");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1), "Status: Not Generated");
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1),
|
||||
"Status: Not Generated");
|
||||
}
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Texture size selector
|
||||
const int sizes[] = { 128, 256, 512, 1024, 2048 };
|
||||
int currentSizeIndex = 2; // Default 512
|
||||
int currentSizeIndex = 2; // Default 512
|
||||
for (int i = 0; i < IM_ARRAYSIZE(sizes); ++i) {
|
||||
if (sizes[i] == texture.textureSize) {
|
||||
currentSizeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char* sizeNames[] = { "128x128", "256x256", "512x512", "1024x1024", "2048x2048" };
|
||||
if (ImGui::Combo("Texture Size", ¤tSizeIndex, sizeNames, IM_ARRAYSIZE(sizeNames))) {
|
||||
|
||||
const char *sizeNames[] = { "128x128", "256x256", "512x512",
|
||||
"1024x1024", "2048x2048" };
|
||||
if (ImGui::Combo("Texture Size", ¤tSizeIndex, sizeNames,
|
||||
IM_ARRAYSIZE(sizeNames))) {
|
||||
texture.textureSize = sizes[currentSizeIndex];
|
||||
texture.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
// UV Margin control to prevent color bleeding
|
||||
ImGui::Separator();
|
||||
ImGui::Text("UV Margin (prevents color bleeding):");
|
||||
if (ImGui::DragFloat("Margin", &texture.uvMargin, 0.001f, 0.01f, 0.025f, "%.3f")) {
|
||||
if (ImGui::DragFloat("Margin", &texture.uvMargin, 0.001f, 0.01f,
|
||||
0.035f, "%.3f")) {
|
||||
// Clamp to valid range
|
||||
if (texture.uvMargin < 0.01f) texture.uvMargin = 0.01f;
|
||||
if (texture.uvMargin > 0.025f) texture.uvMargin = 0.025f;
|
||||
if (texture.uvMargin < 0.01f)
|
||||
texture.uvMargin = 0.01f;
|
||||
if (texture.uvMargin > 0.035f)
|
||||
texture.uvMargin = 0.035f;
|
||||
texture.markDirty();
|
||||
modified = true;
|
||||
}
|
||||
ImGui::TextDisabled("Default: 0.010, Range: 0.010-0.025, Step: 0.001");
|
||||
ImGui::TextDisabled("Higher values = more padding, less color bleeding");
|
||||
|
||||
ImGui::TextDisabled(
|
||||
"Default: 0.010, Range: 0.010-0.035, Step: 0.001");
|
||||
ImGui::TextDisabled(
|
||||
"Higher values = more padding, less color bleeding");
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Color grid
|
||||
renderColorGrid(entity, texture);
|
||||
|
||||
|
||||
// Named rectangles list
|
||||
renderNamedRects(entity, texture);
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Global actions
|
||||
if (ImGui::Button("Randomize Colors")) {
|
||||
for (int i = 0; i < ProceduralTextureComponent::RECT_COUNT; ++i) {
|
||||
for (int i = 0;
|
||||
i < ProceduralTextureComponent::RECT_COUNT; ++i) {
|
||||
Ogre::ColourValue randomColor(
|
||||
(float)rand() / RAND_MAX,
|
||||
(float)rand() / RAND_MAX,
|
||||
(float)rand() / RAND_MAX,
|
||||
1.0f
|
||||
);
|
||||
(float)rand() / RAND_MAX, 1.0f);
|
||||
texture.setColor(i, randomColor);
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
if (ImGui::Button("Reset to Checkerboard")) {
|
||||
for (int i = 0; i < ProceduralTextureComponent::RECT_COUNT; ++i) {
|
||||
int row = i / ProceduralTextureComponent::GRID_SIZE;
|
||||
int col = i % ProceduralTextureComponent::GRID_SIZE;
|
||||
for (int i = 0;
|
||||
i < ProceduralTextureComponent::RECT_COUNT; ++i) {
|
||||
int row = i /
|
||||
ProceduralTextureComponent::GRID_SIZE;
|
||||
int col = i %
|
||||
ProceduralTextureComponent::GRID_SIZE;
|
||||
bool isWhite = (row + col) % 2 == 0;
|
||||
Ogre::ColourValue color(isWhite ? 1.0f : 0.0f, isWhite ? 1.0f : 0.0f, isWhite ? 1.0f : 0.0f, 1.0f);
|
||||
Ogre::ColourValue color(isWhite ? 1.0f : 0.0f,
|
||||
isWhite ? 1.0f : 0.0f,
|
||||
isWhite ? 1.0f : 0.0f,
|
||||
1.0f);
|
||||
texture.setColor(i, color);
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
if (ImGui::Button("All White")) {
|
||||
for (int i = 0; i < ProceduralTextureComponent::RECT_COUNT; ++i) {
|
||||
for (int i = 0;
|
||||
i < ProceduralTextureComponent::RECT_COUNT; ++i) {
|
||||
texture.setColor(i, Ogre::ColourValue::White);
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user