mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-08 10:03:51 +00:00
feat(editor): river/road tool supports multi-point polylines
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Path capture replaced with std::vector<glm::vec3> pathPoints_. The flow is now: click "Click Start Point" → click terrain for first point → keep clicking for additional waypoints → press Apply when done, or Cancel to scrap. Apply iterates each consecutive pair as a segment, calling carveRiver + paintAlongPath + fillWaterAlongPath (or flattenRoad for road mode) per segment. Hard-capped at 64 captured points so a runaway click handler can't unboundedly grow the polyline. Toast at end reports the segment count so users see the polyline took. PathCapture states extended: None / WaitingStart / WaitingEnd / WaitingMore. Backwards-compatible getPathStart()/getPathEnd() return first/last points so the existing path-preview wiring keeps working.
This commit is contained in:
parent
158ab192f0
commit
4e4102bf4a
2 changed files with 68 additions and 39 deletions
|
|
@ -152,14 +152,23 @@ void EditorUI::processActions(EditorApp& app) {
|
|||
}
|
||||
|
||||
void EditorUI::setPathPoint(const glm::vec3& pos) {
|
||||
// Hard cap so a runaway click handler doesn't grow the polyline
|
||||
// unboundedly. 64 segments is more than enough for a tile-sized
|
||||
// river or road.
|
||||
constexpr size_t kMaxPathPoints = 64;
|
||||
if (pathPoints_.size() >= kMaxPathPoints) return;
|
||||
if (pathCapture_ == PathCapture::WaitingStart) {
|
||||
pathStart_ = pos;
|
||||
pathStartSet_ = true;
|
||||
pathPoints_.clear();
|
||||
pathPoints_.push_back(pos);
|
||||
pathCapture_ = PathCapture::WaitingEnd;
|
||||
} else if (pathCapture_ == PathCapture::WaitingEnd) {
|
||||
pathEnd_ = pos;
|
||||
pathEndSet_ = true;
|
||||
pathCapture_ = PathCapture::None;
|
||||
pathPoints_.push_back(pos);
|
||||
// After the second point we stay in capture mode but switch to
|
||||
// WaitingMore so the user can either click "Apply" with the
|
||||
// 2-segment polyline or keep adding waypoints.
|
||||
pathCapture_ = PathCapture::WaitingMore;
|
||||
} else if (pathCapture_ == PathCapture::WaitingMore) {
|
||||
pathPoints_.push_back(pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1023,11 +1032,9 @@ void EditorUI::renderBrushPanel(EditorApp& app) {
|
|||
ImGui::SliderFloat("Width##path", &pathWidth_, 2.0f, 50.0f);
|
||||
if (pathMode_ == 0) ImGui::SliderFloat("Depth##path", &pathDepth_, 1.0f, 30.0f);
|
||||
|
||||
if (pathCapture_ == PathCapture::None && !pathStartSet_) {
|
||||
if (pathCapture_ == PathCapture::None && pathPoints_.empty()) {
|
||||
if (ImGui::Button("Click Start Point", ImVec2(-1, 0))) {
|
||||
pathCapture_ = PathCapture::WaitingStart;
|
||||
pathStartSet_ = false;
|
||||
pathEndSet_ = false;
|
||||
app.showToast("Click terrain to set start point");
|
||||
}
|
||||
} else if (pathCapture_ == PathCapture::WaitingStart) {
|
||||
|
|
@ -1036,39 +1043,54 @@ void EditorUI::renderBrushPanel(EditorApp& app) {
|
|||
pathCapture_ = PathCapture::None;
|
||||
}
|
||||
} else if (pathCapture_ == PathCapture::WaitingEnd) {
|
||||
ImGui::TextColored(ImVec4(0.3f, 1, 0.3f, 1), "Start set at (%.0f, %.0f) — click for END",
|
||||
pathStart_.x, pathStart_.y);
|
||||
ImGui::TextColored(ImVec4(0.3f, 1, 0.3f, 1),
|
||||
"Start at (%.0f, %.0f) — click for next point",
|
||||
pathPoints_[0].x, pathPoints_[0].y);
|
||||
if (ImGui::SmallButton("Cancel##path")) {
|
||||
clearPath();
|
||||
}
|
||||
} else if (pathStartSet_ && pathEndSet_) {
|
||||
} else if (pathCapture_ == PathCapture::WaitingMore || isPathReady()) {
|
||||
// Multi-point: show running count and let the user keep
|
||||
// clicking to add waypoints, or hit Apply with the current
|
||||
// polyline. The Apply branch iterates each segment.
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.9f, 0.5f, 1),
|
||||
"Start: (%.0f,%.0f) End: (%.0f,%.0f)", pathStart_.x, pathStart_.y, pathEnd_.x, pathEnd_.y);
|
||||
"%zu point(s) captured — click for more, or Apply",
|
||||
pathPoints_.size());
|
||||
if (ImGui::Button("Apply Path", ImVec2(-1, 0))) {
|
||||
if (pathMode_ == 0) {
|
||||
app.getTerrainEditor().carveRiver(pathStart_, pathEnd_, pathWidth_, pathDepth_);
|
||||
app.getTexturePainter().paintAlongPath(pathStart_, pathEnd_, pathWidth_ * 1.5f,
|
||||
"Tileset\\Ashenvale\\AshenvaleSand.blp");
|
||||
// After carving, fill water in the chunks along
|
||||
// the river path so the channel actually looks
|
||||
// like a river. liquidType 0 = water (1=ocean,
|
||||
// 2=magma, 3=slime).
|
||||
app.getTerrainEditor().fillWaterAlongPath(
|
||||
pathStart_, pathEnd_, pathWidth_, 0);
|
||||
app.showToast("River carved + banks textured + water filled");
|
||||
} else {
|
||||
app.getTerrainEditor().flattenRoad(pathStart_, pathEnd_, pathWidth_);
|
||||
app.getTexturePainter().paintAlongPath(pathStart_, pathEnd_, pathWidth_,
|
||||
"Tileset\\Elwynn\\ElwynnCobblestoneBase.blp");
|
||||
app.showToast("Road flattened + textured");
|
||||
int segCount = 0;
|
||||
for (size_t k = 0; k + 1 < pathPoints_.size(); ++k) {
|
||||
const glm::vec3& a = pathPoints_[k];
|
||||
const glm::vec3& b = pathPoints_[k + 1];
|
||||
if (pathMode_ == 0) {
|
||||
app.getTerrainEditor().carveRiver(a, b, pathWidth_, pathDepth_);
|
||||
app.getTexturePainter().paintAlongPath(a, b,
|
||||
pathWidth_ * 1.5f,
|
||||
"Tileset\\Ashenvale\\AshenvaleSand.blp");
|
||||
app.getTerrainEditor().fillWaterAlongPath(a, b,
|
||||
pathWidth_, 0);
|
||||
} else {
|
||||
app.getTerrainEditor().flattenRoad(a, b, pathWidth_);
|
||||
app.getTexturePainter().paintAlongPath(a, b,
|
||||
pathWidth_,
|
||||
"Tileset\\Elwynn\\ElwynnCobblestoneBase.blp");
|
||||
}
|
||||
segCount++;
|
||||
}
|
||||
if (pathMode_ == 0)
|
||||
app.showToast("River applied across " +
|
||||
std::to_string(segCount) + " segment(s)");
|
||||
else
|
||||
app.showToast("Road applied across " +
|
||||
std::to_string(segCount) + " segment(s)");
|
||||
clearPath();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Reset##path")) clearPath();
|
||||
} else if (pathStartSet_) {
|
||||
if (ImGui::Button("Click End Point", ImVec2(-1, 0)))
|
||||
pathCapture_ = PathCapture::WaitingEnd;
|
||||
} else if (!pathPoints_.empty()) {
|
||||
// Captured at least one point but the user dismissed the
|
||||
// capture mode without setting more — let them resume.
|
||||
if (ImGui::Button("Click Next Point", ImVec2(-1, 0)))
|
||||
pathCapture_ = PathCapture::WaitingMore;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,15 +24,23 @@ public:
|
|||
|
||||
PaintMode getPaintMode() const { return paintMode_; }
|
||||
|
||||
// Path point capture: when active, next terrain click sets the point
|
||||
enum class PathCapture { None, WaitingStart, WaitingEnd };
|
||||
// Path point capture: when active, next terrain click appends a
|
||||
// point. WaitingStart for the first, WaitingMore for any subsequent
|
||||
// (the user can keep clicking until they hit Apply or Finish).
|
||||
enum class PathCapture { None, WaitingStart, WaitingEnd, WaitingMore };
|
||||
PathCapture getPathCapture() const { return pathCapture_; }
|
||||
void setPathPoint(const glm::vec3& pos);
|
||||
glm::vec3 getPathStart() const { return pathStart_; }
|
||||
glm::vec3 getPathEnd() const { return pathEnd_; }
|
||||
bool isPathReady() const { return pathStartSet_ && pathEndSet_; }
|
||||
// Backwards-compatible getters: start = first point, end = last.
|
||||
glm::vec3 getPathStart() const {
|
||||
return pathPoints_.empty() ? glm::vec3(0) : pathPoints_.front();
|
||||
}
|
||||
glm::vec3 getPathEnd() const {
|
||||
return pathPoints_.empty() ? glm::vec3(0) : pathPoints_.back();
|
||||
}
|
||||
const std::vector<glm::vec3>& getPathPoints() const { return pathPoints_; }
|
||||
bool isPathReady() const { return pathPoints_.size() >= 2; }
|
||||
float getPathWidth() const { return pathWidth_; }
|
||||
void clearPath() { pathStartSet_ = false; pathEndSet_ = false; pathCapture_ = PathCapture::None; }
|
||||
void clearPath() { pathPoints_.clear(); pathCapture_ = PathCapture::None; }
|
||||
|
||||
private:
|
||||
void renderMenuBar(EditorApp& app);
|
||||
|
|
@ -86,8 +94,7 @@ private:
|
|||
|
||||
// Path point capture
|
||||
PathCapture pathCapture_ = PathCapture::None;
|
||||
glm::vec3 pathStart_{0}, pathEnd_{0};
|
||||
bool pathStartSet_ = false, pathEndSet_ = false;
|
||||
std::vector<glm::vec3> pathPoints_;
|
||||
int pathMode_ = 0; // 0=river, 1=road
|
||||
float pathWidth_ = 8.0f, pathDepth_ = 5.0f;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue