mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-05 16:43:52 +00:00
feat(editor): terrain stamp/clone tool for replicating terrain features
- Copy Stamp: captures all vertex heights within brush radius at cursor
position, storing relative offsets from center
- Paste Stamp: applies the copied height pattern at a new location,
finding nearest vertices and setting their heights
- Stamp status shown in panel ("Stamp ready" / "No stamp copied")
- Auto-stitches chunk edges after paste for seamless results
- Useful for replicating hills, craters, or other terrain features
This commit is contained in:
parent
9a547f66d2
commit
1ba1a50112
3 changed files with 81 additions and 0 deletions
|
|
@ -452,6 +452,21 @@ void EditorUI::renderBrushPanel(EditorApp& app) {
|
|||
"Exaggerate (>1) or flatten (<1) terrain relief");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::CollapsingHeader("Stamp / Clone")) {
|
||||
auto& brush2 = app.getTerrainEditor().brush();
|
||||
if (ImGui::Button("Copy Stamp", ImVec2(120, 0)) && brush2.isActive())
|
||||
app.getTerrainEditor().copyStamp(brush2.getPosition(), s.radius);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Paste Stamp", ImVec2(120, 0)) && brush2.isActive() &&
|
||||
app.getTerrainEditor().hasStamp())
|
||||
app.getTerrainEditor().pasteStamp(brush2.getPosition());
|
||||
if (app.getTerrainEditor().hasStamp())
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.9f, 0.5f, 1), "Stamp ready");
|
||||
else
|
||||
ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1), "No stamp copied");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Terrain Holes (cave entrances):");
|
||||
auto& brush = app.getTerrainEditor().brush();
|
||||
|
|
|
|||
|
|
@ -666,6 +666,63 @@ void TerrainEditor::scaleHeights(float factor) {
|
|||
dirty_ = true;
|
||||
}
|
||||
|
||||
void TerrainEditor::copyStamp(const glm::vec3& center, float radius) {
|
||||
if (!terrain_) return;
|
||||
stampData_.clear();
|
||||
stampCenter_ = center;
|
||||
|
||||
for (int ci = 0; ci < 256; ci++) {
|
||||
if (!terrain_->chunks[ci].hasHeightMap()) continue;
|
||||
for (int v = 0; v < 145; v++) {
|
||||
glm::vec3 pos = chunkVertexWorldPos(ci, v);
|
||||
float dx = pos.x - center.x;
|
||||
float dy = pos.y - center.y;
|
||||
if (std::sqrt(dx * dx + dy * dy) <= radius) {
|
||||
StampVertex sv;
|
||||
sv.dx = dx;
|
||||
sv.dy = dy;
|
||||
sv.height = terrain_->chunks[ci].heightMap.heights[v];
|
||||
stampData_.push_back(sv);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_INFO("Stamp copied: ", stampData_.size(), " vertices in radius ", radius);
|
||||
}
|
||||
|
||||
void TerrainEditor::pasteStamp(const glm::vec3& center) {
|
||||
if (!terrain_ || stampData_.empty()) return;
|
||||
|
||||
for (const auto& sv : stampData_) {
|
||||
float wx = center.x + sv.dx;
|
||||
float wy = center.y + sv.dy;
|
||||
|
||||
// Find nearest vertex and set its height
|
||||
float bestDist = 1e30f;
|
||||
int bestChunk = -1, bestVert = -1;
|
||||
for (int ci = 0; ci < 256; ci++) {
|
||||
if (!terrain_->chunks[ci].hasHeightMap()) continue;
|
||||
for (int v = 0; v < 145; v++) {
|
||||
glm::vec3 pos = chunkVertexWorldPos(ci, v);
|
||||
float d = std::sqrt((pos.x - wx) * (pos.x - wx) + (pos.y - wy) * (pos.y - wy));
|
||||
if (d < bestDist && d < 3.0f) {
|
||||
bestDist = d;
|
||||
bestChunk = ci;
|
||||
bestVert = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestChunk >= 0) {
|
||||
terrain_->chunks[bestChunk].heightMap.heights[bestVert] = sv.height;
|
||||
if (std::find(dirtyChunks_.begin(), dirtyChunks_.end(), bestChunk) == dirtyChunks_.end())
|
||||
dirtyChunks_.push_back(bestChunk);
|
||||
}
|
||||
}
|
||||
|
||||
for (int ci : dirtyChunks_) stitchEdges(ci);
|
||||
dirty_ = true;
|
||||
LOG_INFO("Stamp pasted at (", center.x, ",", center.y, ")");
|
||||
}
|
||||
|
||||
void TerrainEditor::clampHeights(float minH, float maxH) {
|
||||
if (!terrain_) return;
|
||||
for (int ci = 0; ci < 256; ci++) {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,11 @@ public:
|
|||
// Scale all heights by a factor (useful for exaggerating or flattening)
|
||||
void scaleHeights(float factor);
|
||||
|
||||
// Terrain stamp: copy heights from source area, paste at destination
|
||||
void copyStamp(const glm::vec3& center, float radius);
|
||||
void pasteStamp(const glm::vec3& center);
|
||||
bool hasStamp() const { return !stampData_.empty(); }
|
||||
|
||||
// Import/export heightmap (raw 16-bit grayscale, 129x129)
|
||||
bool importHeightmap(const std::string& path, float heightScale);
|
||||
bool exportHeightmap(const std::string& path, float heightScale);
|
||||
|
|
@ -90,6 +95,10 @@ private:
|
|||
float getVertexHeight(int chunkIdx, int vertIdx) const;
|
||||
void setVertexHeight(int chunkIdx, int vertIdx, float height);
|
||||
|
||||
struct StampVertex { float dx, dy, height; };
|
||||
std::vector<StampVertex> stampData_;
|
||||
glm::vec3 stampCenter_{0};
|
||||
|
||||
pipeline::ADTTerrain* terrain_ = nullptr;
|
||||
EditorBrush brush_;
|
||||
EditorHistory history_;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue