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) {
53 const glm::vec4
clip = viewProjection * glm::vec4(
anchor.worldX,
anchor.worldY,
55 if (!std::isfinite(
clip.w) ||
clip.w <= 0.f) {
60 const glm::vec3 ndc = glm::vec3(
clip) /
clip.w;
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;
79 if (
anchor.distanceScale) {
80 const float distance = glm::length(glm::vec3(
anchor.worldX - cameraX,
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});
98 std::sort(results.begin(), results.end(), [](
const auto &
a,
const auto &
b) {
99 return a.stableIndex < b.stableIndex;
102 return *std::lower_bound(results.begin(), results.end(), stableIndex,
103 [](
const auto &result, std::size_t
value) {
104 return result.stableIndex < value;
107 if (viewportWidth <= 0.f || viewportHeight <= 0.f)
return results;
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;
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;
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) /
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);
135 const float y = item.screenY + displacementY;
136 const AnchorRect candidate = anchorRect(item, item.screenX,
y);
137 if (!insideViewport(candidate, item.safeMargin, viewportWidth, viewportHeight))
139 if (std::any_of(
occupied.begin(),
occupied.end(), [&](
const AnchorRect &other) {
140 return intersects(candidate, other);
143 auto &result = resultFor(item.stableIndex);
145 result.displacementY = displacementY;
146 result.render =
true;
151 if (!
placed) resultFor(item.stableIndex).render =
false;
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.