mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-06 00:53:52 +00:00
feat(editor): terrain holes, recent textures, sculpt panel polish
- Punch Hole / Fill Hole buttons in Sculpt panel: creates terrain holes (4x4 bitmask) for cave entrances, mine shafts, etc. Uses brush radius to determine affected area. - Recent Textures: paint panel shows last 6 used textures as quick- select buttons (no need to re-search the full list) - Holes saved in ADT format (MCNK holes field) and respected by the mesh generator (triangles skipped at hole positions)
This commit is contained in:
parent
cc6a72e7b2
commit
f5fe9a0101
5 changed files with 93 additions and 0 deletions
|
|
@ -230,6 +230,15 @@ void EditorUI::renderBrushPanel(EditorApp& app) {
|
|||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Set target height from cursor position");
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Terrain Holes (cave entrances):");
|
||||
auto& brush = app.getTerrainEditor().brush();
|
||||
if (ImGui::Button("Punch Hole", ImVec2(120, 0)) && brush.isActive())
|
||||
app.getTerrainEditor().punchHole(brush.getPosition(), s.radius);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Fill Hole", ImVec2(120, 0)) && brush.isActive())
|
||||
app.getTerrainEditor().fillHole(brush.getPosition(), s.radius);
|
||||
|
||||
ImGui::Separator();
|
||||
auto& hist = app.getTerrainEditor().history();
|
||||
ImGui::Text("Undo: %zu Redo: %zu", hist.undoCount(), hist.redoCount());
|
||||
|
|
@ -304,6 +313,25 @@ void EditorUI::renderTexturePaintPanel(EditorApp& app) {
|
|||
if (!selectedTexture_.empty())
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.9f, 0.5f, 1.0f), "Active: %s",
|
||||
selectedTexture_.c_str());
|
||||
|
||||
// Recent textures
|
||||
auto& recent = app.getTexturePainter().getRecentTextures();
|
||||
if (!recent.empty()) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Recent:");
|
||||
for (int i = 0; i < static_cast<int>(recent.size()) && i < 6; i++) {
|
||||
std::string disp = recent[i];
|
||||
auto sl = disp.rfind('\\');
|
||||
if (sl != std::string::npos) disp = disp.substr(sl + 1);
|
||||
if (ImGui::SmallButton(disp.c_str())) {
|
||||
selectedTexture_ = recent[i];
|
||||
app.getTexturePainter().setActiveTexture(recent[i]);
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", recent[i].c_str());
|
||||
if (i < 5 && i + 1 < static_cast<int>(recent.size())) ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -586,5 +586,59 @@ void TerrainEditor::removeWater(const glm::vec3& center, float radius) {
|
|||
}
|
||||
}
|
||||
|
||||
void TerrainEditor::punchHole(const glm::vec3& center, float radius) {
|
||||
if (!terrain_) return;
|
||||
auto affected = getAffectedChunks(center, radius);
|
||||
for (int ci : affected) {
|
||||
auto& chunk = terrain_->chunks[ci];
|
||||
// Each chunk has 8x8 quads, holes use a 4x4 bitmask (each bit covers 2x2 quads)
|
||||
for (int hy = 0; hy < 4; hy++) {
|
||||
for (int hx = 0; hx < 4; hx++) {
|
||||
// Center of this 2x2 quad group
|
||||
int cx = ci % 16, cy = ci / 16;
|
||||
float tileNW_X = (32.0f - static_cast<float>(terrain_->coord.y)) * TILE_SIZE;
|
||||
float tileNW_Y = (32.0f - static_cast<float>(terrain_->coord.x)) * TILE_SIZE;
|
||||
float qx = tileNW_X - cy * CHUNK_SIZE - (hy * 2 + 1) * CHUNK_SIZE / 8.0f;
|
||||
float qy = tileNW_Y - cx * CHUNK_SIZE - (hx * 2 + 1) * CHUNK_SIZE / 8.0f;
|
||||
float dist = std::sqrt((qx - center.x) * (qx - center.x) +
|
||||
(qy - center.y) * (qy - center.y));
|
||||
if (dist < radius) {
|
||||
int bit = 1 << (hy * 4 + hx);
|
||||
chunk.holes |= static_cast<uint16_t>(bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (std::find(dirtyChunks_.begin(), dirtyChunks_.end(), ci) == dirtyChunks_.end())
|
||||
dirtyChunks_.push_back(ci);
|
||||
dirty_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainEditor::fillHole(const glm::vec3& center, float radius) {
|
||||
if (!terrain_) return;
|
||||
auto affected = getAffectedChunks(center, radius);
|
||||
for (int ci : affected) {
|
||||
auto& chunk = terrain_->chunks[ci];
|
||||
for (int hy = 0; hy < 4; hy++) {
|
||||
for (int hx = 0; hx < 4; hx++) {
|
||||
int cx = ci % 16, cy = ci / 16;
|
||||
float tileNW_X = (32.0f - static_cast<float>(terrain_->coord.y)) * TILE_SIZE;
|
||||
float tileNW_Y = (32.0f - static_cast<float>(terrain_->coord.x)) * TILE_SIZE;
|
||||
float qx = tileNW_X - cy * CHUNK_SIZE - (hy * 2 + 1) * CHUNK_SIZE / 8.0f;
|
||||
float qy = tileNW_Y - cx * CHUNK_SIZE - (hx * 2 + 1) * CHUNK_SIZE / 8.0f;
|
||||
float dist = std::sqrt((qx - center.x) * (qx - center.x) +
|
||||
(qy - center.y) * (qy - center.y));
|
||||
if (dist < radius) {
|
||||
int bit = 1 << (hy * 4 + hx);
|
||||
chunk.holes &= ~static_cast<uint16_t>(bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (std::find(dirtyChunks_.begin(), dirtyChunks_.end(), ci) == dirtyChunks_.end())
|
||||
dirtyChunks_.push_back(ci);
|
||||
dirty_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace wowee
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ public:
|
|||
void setWaterLevel(const glm::vec3& center, float radius, float waterHeight, uint16_t liquidType = 0);
|
||||
void removeWater(const glm::vec3& center, float radius);
|
||||
|
||||
// Hole editing (4x4 bitmask per chunk — cave entrances, mine shafts)
|
||||
void punchHole(const glm::vec3& center, float radius);
|
||||
void fillHole(const glm::vec3& center, float radius);
|
||||
|
||||
bool hasUnsavedChanges() const { return dirty_; }
|
||||
void markSaved() { dirty_ = false; }
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ namespace editor {
|
|||
|
||||
void TexturePainter::setActiveTexture(const std::string& texturePath) {
|
||||
activeTexture_ = texturePath;
|
||||
// Track recent textures (max 10)
|
||||
auto it = std::find(recentTextures_.begin(), recentTextures_.end(), texturePath);
|
||||
if (it != recentTextures_.end()) recentTextures_.erase(it);
|
||||
recentTextures_.insert(recentTextures_.begin(), texturePath);
|
||||
if (recentTextures_.size() > 10) recentTextures_.pop_back();
|
||||
}
|
||||
|
||||
uint32_t TexturePainter::ensureTextureInList(const std::string& path) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public:
|
|||
|
||||
void setActiveTexture(const std::string& texturePath);
|
||||
const std::string& getActiveTexture() const { return activeTexture_; }
|
||||
const std::vector<std::string>& getRecentTextures() const { return recentTextures_; }
|
||||
|
||||
// Paint the active texture at the given world position
|
||||
// Returns list of modified chunk indices
|
||||
|
|
@ -33,6 +34,7 @@ private:
|
|||
|
||||
pipeline::ADTTerrain* terrain_ = nullptr;
|
||||
std::string activeTexture_;
|
||||
std::vector<std::string> recentTextures_;
|
||||
|
||||
static constexpr float TILE_SIZE = 533.33333f;
|
||||
static constexpr float CHUNK_SIZE = TILE_SIZE / 16.0f;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue