载入中...
搜索中...
未找到
WorldAnchorProjection.cpp
浏览该文件的文档.
2
3#include <glm/geometric.hpp>
4#include <glm/vec3.hpp>
5#include <glm/vec4.hpp>
6
7#include <algorithm>
8#include <cmath>
9
10namespace eve::ui {
11namespace {
12
13struct AnchorRect {
14 float left;
15 float top;
16 float right;
17 float bottom;
18};
19
20AnchorRect anchorRect(const WorldAnchorLayoutItem &item, float x, float y) {
21 const float padding = std::max(0.f, item.padding) * 0.5f;
22 const float width = std::max(1.f, item.width);
23 const float height = std::max(1.f, item.height);
24 const float left = x - item.pivotX * width;
25 const float top = y - item.pivotY * height;
26 return {left - padding, top - padding, left + width + padding,
27 top + height + padding};
28}
29
30bool intersects(const AnchorRect &a, const AnchorRect &b) {
31 return a.left < b.right && a.right > b.left && a.top < b.bottom &&
32 a.bottom > b.top;
33}
34
35bool insideViewport(const AnchorRect &rect, float margin, float width, float height) {
36 margin = std::max(0.f, margin);
37 return rect.left >= margin && rect.top >= margin && rect.right <= width - margin &&
38 rect.bottom <= height - margin;
39}
40
41} // namespace
42
44 const glm::mat4 &viewProjection, float cameraX,
45 float cameraY, float cameraZ, float viewportWidth,
46 float viewportHeight) {
48 if (!anchor.enabled) return result;
49 if (viewportWidth <= 0.f || viewportHeight <= 0.f) {
51 return result;
52 }
53 const glm::vec4 clip = viewProjection * glm::vec4(anchor.worldX, anchor.worldY,
54 anchor.worldZ, 1.f);
55 if (!std::isfinite(clip.w) || clip.w <= 0.f) {
57 result.render = !anchor.hideBehindCamera;
58 return result;
59 }
60 const glm::vec3 ndc = glm::vec3(clip) / clip.w;
61 result.depth = ndc.z;
62 result.screenX = (ndc.x * 0.5f + 0.5f) * viewportWidth + anchor.offsetX;
63 result.screenY = (ndc.y * 0.5f + 0.5f) * viewportHeight + anchor.offsetY;
64 const bool outside = ndc.x < -1.f || ndc.x > 1.f || ndc.y < -1.f || ndc.y > 1.f ||
65 ndc.z < 0.f || ndc.z > 1.f;
66 if (outside && anchor.edgePolicy == WorldAnchorEdgePolicy::Hide) {
68 return result;
69 }
70 if (outside) {
71 const float margin = std::max(0.f, anchor.safeMargin);
72 result.screenX = std::clamp(result.screenX, margin,
73 std::max(margin, viewportWidth - margin));
74 result.screenY = std::clamp(result.screenY, margin,
75 std::max(margin, viewportHeight - margin));
76 }
78 result.render = true;
79 if (anchor.distanceScale) {
80 const float distance = glm::length(glm::vec3(anchor.worldX - cameraX,
81 anchor.worldY - cameraY,
82 anchor.worldZ - cameraZ));
83 const float reference = std::max(anchor.referenceDistance, 0.001f);
84 result.scale = std::clamp(reference / std::max(distance, 0.001f),
85 std::min(anchor.minScale, anchor.maxScale),
86 std::max(anchor.minScale, anchor.maxScale));
87 }
88 return result;
89}
90
91std::vector<WorldAnchorLayoutResult> resolveWorldAnchorOverlaps(
92 std::vector<WorldAnchorLayoutItem> items, float viewportWidth, float viewportHeight) {
93 std::vector<WorldAnchorLayoutResult> results;
94 results.reserve(items.size());
95 for (const auto &item : items) {
96 results.push_back({item.stableIndex, item.screenX, item.screenY, 0.f, 0.f, false});
97 }
98 std::sort(results.begin(), results.end(), [](const auto &a, const auto &b) {
99 return a.stableIndex < b.stableIndex;
100 });
101 const auto resultFor = [&](std::size_t stableIndex) -> WorldAnchorLayoutResult & {
102 return *std::lower_bound(results.begin(), results.end(), stableIndex,
103 [](const auto &result, std::size_t value) {
104 return result.stableIndex < value;
105 });
106 };
107 if (viewportWidth <= 0.f || viewportHeight <= 0.f) return results;
108
109 std::vector<AnchorRect> occupied;
110 occupied.reserve(items.size());
111 for (const auto &item : items) {
112 if (item.avoidOverlap) continue;
113 occupied.push_back(anchorRect(item, item.screenX, item.screenY));
114 resultFor(item.stableIndex).render = true;
115 }
116
117 std::stable_sort(items.begin(), items.end(), [](const auto &a, const auto &b) {
118 if (a.avoidOverlap != b.avoidOverlap) return a.avoidOverlap > b.avoidOverlap;
119 if (a.priority != b.priority) return a.priority > b.priority;
120 if (a.depth != b.depth) return a.depth < b.depth;
121 return a.stableIndex < b.stableIndex;
122 });
123
124 for (const auto &item : items) {
125 if (!item.avoidOverlap) continue;
126 const float step = std::max(1.f, std::max(1.f, item.height) +
127 std::max(0.f, item.padding));
128 const int attempts = std::max(0, int(std::floor(std::max(0.f, item.maxDisplacement) /
129 step)));
130 bool placed = false;
131 for (int attempt = 0; attempt <= attempts * 2; ++attempt) {
132 const int level = (attempt + 1) / 2;
133 const float direction = attempt == 0 ? 0.f : (attempt % 2 == 1 ? -1.f : 1.f);
134 const float displacementY = direction * float(level) * step;
135 const float y = item.screenY + displacementY;
136 const AnchorRect candidate = anchorRect(item, item.screenX, y);
137 if (!insideViewport(candidate, item.safeMargin, viewportWidth, viewportHeight))
138 continue;
139 if (std::any_of(occupied.begin(), occupied.end(), [&](const AnchorRect &other) {
140 return intersects(candidate, other);
141 }))
142 continue;
143 auto &result = resultFor(item.stableIndex);
144 result.screenY = y;
145 result.displacementY = displacementY;
146 result.render = true;
147 occupied.push_back(candidate);
148 placed = true;
149 break;
150 }
151 if (!placed) resultFor(item.stableIndex).render = false;
152 }
153
154 return results;
155}
156
157} // namespace eve::ui
double value
SQInteger top
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
Vec3 anchor
Definition CaveMesh.cpp:90
glm::vec4 clip
HexVec3 left
HexVec3 right
std::uint32_t height
std::uint32_t width
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
int level
RoadLaneDirection direction
bool occupied
bool placed
int margin
float step
Definition TreeMesh.cpp:314
float bottom
std::vector< WorldAnchorLayoutResult > resolveWorldAnchorOverlaps(std::vector< WorldAnchorLayoutItem > items, float viewportWidth, float viewportHeight)
Resolves projected host overlap with bounded deterministic vertical displacement.
WorldAnchorProjection projectWorldAnchor(const UIHost::WorldAnchor &anchor, const glm::mat4 &viewProjection, float cameraX, float cameraY, float cameraZ, float viewportWidth, float viewportHeight)
Projects one world-space UI anchor using a Vulkan RH-ZO view-projection matrix.
Optional 3D-world projection for this UI host.
Definition UIHost.h:307
Owning placement result corresponding to WorldAnchorLayoutItem::stableIndex.
Immutable result of projecting a UI world anchor into a viewport.