mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-06 17:13:51 +00:00
feat(editor): visualize patrol path of selected NPC as ribbon with waypoints
Adds setPatrolPath() that draws a multi-segment orange ribbon between the NPC's spawn position and each waypoint, plus diamond markers at each point (green for start = NPC home, white for waypoints). Renders only while the NPC is selected and has a patrol path defined.
This commit is contained in:
parent
191ff9ec16
commit
eb8f5a09b1
3 changed files with 109 additions and 0 deletions
|
|
@ -133,6 +133,18 @@ void EditorApp::run() {
|
|||
gizmo.setMode(TransformMode::None);
|
||||
}
|
||||
|
||||
// Patrol path visualization for the selected NPC
|
||||
if (auto* selNpc = npcSpawner_.getSelected();
|
||||
selNpc && !selNpc->patrolPath.empty()) {
|
||||
std::vector<glm::vec3> pts;
|
||||
pts.reserve(selNpc->patrolPath.size() + 1);
|
||||
pts.push_back(selNpc->position);
|
||||
for (const auto& wp : selNpc->patrolPath) pts.push_back(wp.position);
|
||||
viewport_.setPatrolPath(pts);
|
||||
} else {
|
||||
viewport_.clearPatrolPath();
|
||||
}
|
||||
|
||||
uint32_t imageIndex = 0;
|
||||
VkCommandBuffer cmd = vkCtx->beginFrame(imageIndex);
|
||||
if (cmd == VK_NULL_HANDLE) continue;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ void EditorViewport::shutdown() {
|
|||
if (npcMarkerVB_) { vmaDestroyBuffer(vkCtx_->getAllocator(), npcMarkerVB_, npcMarkerVBAlloc_); npcMarkerVB_ = VK_NULL_HANDLE; }
|
||||
if (brushVB_) { vmaDestroyBuffer(vkCtx_->getAllocator(), brushVB_, brushVBAlloc_); brushVB_ = VK_NULL_HANDLE; }
|
||||
if (pathVB_) { vmaDestroyBuffer(vkCtx_->getAllocator(), pathVB_, pathVBAlloc_); pathVB_ = VK_NULL_HANDLE; }
|
||||
if (patrolVB_) { vmaDestroyBuffer(vkCtx_->getAllocator(), patrolVB_, patrolVBAlloc_); patrolVB_ = VK_NULL_HANDLE; }
|
||||
gizmo_.shutdown();
|
||||
waterRenderer_.shutdown();
|
||||
|
||||
|
|
@ -505,6 +506,80 @@ void EditorViewport::setPathPreview(const glm::vec3& start, const glm::vec3& end
|
|||
}
|
||||
}
|
||||
|
||||
void EditorViewport::setPatrolPath(const std::vector<glm::vec3>& points, float width) {
|
||||
if (patrolVB_) {
|
||||
vmaDestroyBuffer(vkCtx_->getAllocator(), patrolVB_, patrolVBAlloc_);
|
||||
patrolVB_ = VK_NULL_HANDLE;
|
||||
patrolVertCount_ = 0;
|
||||
}
|
||||
if (points.size() < 2) return;
|
||||
|
||||
struct BV { float pos[3]; float color[4]; };
|
||||
std::vector<BV> verts;
|
||||
verts.reserve(points.size() * 24);
|
||||
|
||||
auto addRibbon = [&](const glm::vec3& a, const glm::vec3& b, float r, float g, float bl, float al) {
|
||||
glm::vec2 dir = glm::vec2(b.x - a.x, b.y - a.y);
|
||||
float len = glm::length(dir);
|
||||
if (len < 0.001f) return;
|
||||
dir /= len;
|
||||
glm::vec2 perp(-dir.y, dir.x);
|
||||
float hw = width * 0.5f;
|
||||
float z0 = a.z + 1.5f;
|
||||
float z1 = b.z + 1.5f;
|
||||
BV v;
|
||||
v.color[0] = r; v.color[1] = g; v.color[2] = bl; v.color[3] = al;
|
||||
v.pos[0] = a.x - perp.x*hw; v.pos[1] = a.y - perp.y*hw; v.pos[2] = z0; verts.push_back(v);
|
||||
v.pos[0] = a.x + perp.x*hw; v.pos[1] = a.y + perp.y*hw; v.pos[2] = z0; verts.push_back(v);
|
||||
v.pos[0] = b.x - perp.x*hw; v.pos[1] = b.y - perp.y*hw; v.pos[2] = z1; verts.push_back(v);
|
||||
v.pos[0] = b.x - perp.x*hw; v.pos[1] = b.y - perp.y*hw; v.pos[2] = z1; verts.push_back(v);
|
||||
v.pos[0] = a.x + perp.x*hw; v.pos[1] = a.y + perp.y*hw; v.pos[2] = z0; verts.push_back(v);
|
||||
v.pos[0] = b.x + perp.x*hw; v.pos[1] = b.y + perp.y*hw; v.pos[2] = z1; verts.push_back(v);
|
||||
};
|
||||
|
||||
auto addWaypoint = [&](const glm::vec3& p, float r, float g, float bl) {
|
||||
float s = 1.5f;
|
||||
BV v;
|
||||
v.color[0] = r; v.color[1] = g; v.color[2] = bl; v.color[3] = 0.95f;
|
||||
glm::vec3 top(p.x, p.y, p.z + s * 2);
|
||||
glm::vec3 bot(p.x, p.y, p.z + 0.2f);
|
||||
glm::vec3 n(p.x, p.y + s, p.z + s);
|
||||
glm::vec3 s2(p.x, p.y - s, p.z + s);
|
||||
glm::vec3 e(p.x + s, p.y, p.z + s);
|
||||
glm::vec3 w(p.x - s, p.y, p.z + s);
|
||||
auto pushV = [&](const glm::vec3& vv){ v.pos[0]=vv.x; v.pos[1]=vv.y; v.pos[2]=vv.z; verts.push_back(v); };
|
||||
pushV(top); pushV(n); pushV(e);
|
||||
pushV(top); pushV(e); pushV(s2);
|
||||
pushV(top); pushV(s2); pushV(w);
|
||||
pushV(top); pushV(w); pushV(n);
|
||||
pushV(bot); pushV(e); pushV(n);
|
||||
pushV(bot); pushV(s2); pushV(e);
|
||||
pushV(bot); pushV(w); pushV(s2);
|
||||
pushV(bot); pushV(n); pushV(w);
|
||||
};
|
||||
|
||||
for (size_t i = 0; i + 1 < points.size(); i++) {
|
||||
addRibbon(points[i], points[i+1], 1.0f, 0.7f, 0.2f, 0.55f);
|
||||
}
|
||||
for (size_t i = 0; i < points.size(); i++) {
|
||||
bool isStart = (i == 0);
|
||||
addWaypoint(points[i], isStart ? 0.2f : 1.0f, isStart ? 1.0f : 0.85f, isStart ? 0.3f : 0.2f);
|
||||
}
|
||||
|
||||
patrolVertCount_ = static_cast<uint32_t>(verts.size());
|
||||
VkBufferCreateInfo bufInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
|
||||
bufInfo.size = verts.size() * sizeof(BV);
|
||||
bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
||||
VmaAllocationCreateInfo allocInfo{};
|
||||
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
|
||||
allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
VmaAllocationInfo mapInfo{};
|
||||
if (vmaCreateBuffer(vkCtx_->getAllocator(), &bufInfo, &allocInfo,
|
||||
&patrolVB_, &patrolVBAlloc_, &mapInfo) == VK_SUCCESS) {
|
||||
std::memcpy(mapInfo.pMappedData, verts.data(), verts.size() * sizeof(BV));
|
||||
}
|
||||
}
|
||||
|
||||
void EditorViewport::updateNpcMarkers(const std::vector<CreatureSpawn>& npcs) {
|
||||
if (npcMarkerVB_) {
|
||||
vmaDestroyBuffer(vkCtx_->getAllocator(), npcMarkerVB_, npcMarkerVBAlloc_);
|
||||
|
|
@ -685,6 +760,20 @@ void EditorViewport::render(VkCommandBuffer cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
// Patrol path ribbon for selected NPC
|
||||
if (patrolVB_ && patrolVertCount_ > 0) {
|
||||
auto* waterPipeline = waterRenderer_.getPipeline();
|
||||
auto* waterLayout = waterRenderer_.getPipelineLayout();
|
||||
if (waterPipeline && waterLayout) {
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, waterPipeline);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, waterLayout,
|
||||
0, 1, &perFrameSet, 0, nullptr);
|
||||
VkDeviceSize off = 0;
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &patrolVB_, &off);
|
||||
vkCmdDraw(cmd, patrolVertCount_, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
gizmo_.render(cmd, perFrameSet);
|
||||
|
||||
// NPC markers — render with water pipeline (pos+color, alpha blend)
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ public:
|
|||
TransformGizmo& getGizmo() { return gizmo_; }
|
||||
void setBrushIndicator(const glm::vec3& center, float radius, bool active);
|
||||
void setPathPreview(const glm::vec3& start, const glm::vec3& end, float width, bool visible);
|
||||
/** Show a multi-segment patrol path as a ribbon. Empty `points` clears it. */
|
||||
void setPatrolPath(const std::vector<glm::vec3>& points, float width = 1.5f);
|
||||
void clearPatrolPath() { setPatrolPath({}); }
|
||||
|
||||
void setWireframe(bool enabled);
|
||||
void setShowNpcMarkers(bool show) { showNpcMarkers_ = show; }
|
||||
|
|
@ -134,6 +137,11 @@ private:
|
|||
VmaAllocation pathVBAlloc_ = VK_NULL_HANDLE;
|
||||
uint32_t pathVertCount_ = 0;
|
||||
bool pathVisible_ = false;
|
||||
|
||||
// Patrol path ribbon (selected NPC)
|
||||
VkBuffer patrolVB_ = VK_NULL_HANDLE;
|
||||
VmaAllocation patrolVBAlloc_ = VK_NULL_HANDLE;
|
||||
uint32_t patrolVertCount_ = 0;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue