载入中...
搜索中...
未找到
ReflectionProbeRegistry.cpp
浏览该文件的文档.
2
5
6#include <algorithm>
7#include <cmath>
8#include <limits>
9
10namespace eve::graphics {
11
13
15 if (!probe) return;
16 const auto found = std::find_if(entries_.begin(), entries_.end(),
17 [probe](const Entry &entry) { return entry.probe == probe; });
18 if (found == entries_.end()) {
19 entries_.push_back(Entry{probe, nextOrder_++});
20 probe->attachRegistry(this);
21 }
22}
23
25 if (probe) probe->detachRegistry(this);
26 entries_.erase(std::remove_if(entries_.begin(), entries_.end(),
27 [probe](const Entry &entry) { return entry.probe == probe; }),
28 entries_.end());
29 lastSelected_.erase(std::remove(lastSelected_.begin(), lastSelected_.end(), probe),
30 lastSelected_.end());
31}
32
34 for (const Entry &entry : entries_)
35 if (entry.probe) entry.probe->detachRegistry(this);
36 entries_.clear();
37 lastSelected_.clear();
38 lastSelectedCount_ = 0;
39 lastCapturedFaceCount_ = 0;
40 lastPublishedCount_ = 0;
41 lastCandidateCount_ = 0;
42 lastDistanceCulledCount_ = 0;
43}
44
46 if (!std::isfinite(distance) || distance <= 0.f)
48 "Reflection probe render distance must be positive and finite",
49 "graphics.reflectionProbe.maxRenderDistance"));
50 maxRenderDistance_ = distance;
51 return Result<void>::success();
52}
53
55 selectionHysteresis_ = std::clamp(distance, 0.f, 100.f);
56}
57
59 const uint32_t mask = static_cast<uint32_t>(changedMask);
60 int queued = 0;
61 for (Entry &entry : entries_) {
62 if (!entry.probe || (static_cast<uint32_t>(entry.probe->getCaptureMask()) & mask) == 0u)
63 continue;
64 entry.probe->queueCapture();
65 ++queued;
66 }
67 return queued;
68}
69
70int ReflectionProbeRegistry::queueCaptureAABB(float minX, float minY, float minZ, float maxX,
71 float maxY, float maxZ, int changedMask) {
72 const float changedMin[3] = {std::min(minX, maxX), std::min(minY, maxY),
73 std::min(minZ, maxZ)};
74 const float changedMax[3] = {std::max(minX, maxX), std::max(minY, maxY),
75 std::max(minZ, maxZ)};
76 const uint32_t mask = static_cast<uint32_t>(changedMask);
77 int queued = 0;
78 for (Entry &entry : entries_) {
79 ReflectionProbeCapture *probe = entry.probe;
80 if (!probe || (static_cast<uint32_t>(probe->getCaptureMask()) & mask) == 0u) continue;
81 const float center[3] = {probe->getCenterX(), probe->getCenterY(), probe->getCenterZ()};
82 float distance2 = 0.f;
83 for (int axis = 0; axis < 3; ++axis) {
84 const float nearest = std::clamp(center[axis], changedMin[axis], changedMax[axis]);
85 const float delta = center[axis] - nearest;
86 distance2 += delta * delta;
87 }
88 const float captureRadius = probe->getCaptureFarDistance();
89 if (distance2 > captureRadius * captureRadius) continue;
90 probe->queueCapture();
91 ++queued;
92 }
93 return queued;
94}
95
96int ReflectionProbeRegistry::tick(int faceBudget, int filterBudget, int filterSamples) {
97 lastCapturedFaceCount_ = 0;
98 lastPublishedCount_ = 0;
99 faceBudget = std::max(faceBudget, 0);
100 filterBudget = std::max(filterBudget, 0);
101 filterSamples = std::clamp(filterSamples, 8, 512);
102
103 for (Entry &entry : entries_) {
104 if (!entry.probe || (distanceCullingEnabled_ && entry.distanceCulled)) continue;
105 entry.probe->advanceRefreshPolicy();
106 const bool waiting = entry.probe->isCapturePending() ||
107 entry.probe->getPublishedRevision() != entry.probe->getRevision();
108 if (waiting && entry.waitAge < std::numeric_limits<uint32_t>::max()) ++entry.waitAge;
109 }
110
111 auto better = [](const Entry *a, const Entry *b) {
112 const int priorityA = a->probe->getInfluencePriority();
113 const int priorityB = b->probe->getInfluencePriority();
114 if (priorityA != priorityB) return priorityA > priorityB;
115 if (a->waitAge != b->waitAge) return a->waitAge > b->waitAge;
116 return a->order < b->order;
117 };
118
119 while (faceBudget-- > 0) {
120 Entry *selected = nullptr;
121 for (Entry &entry : entries_) {
122 if (!entry.probe || !entry.probe->isCapturePending()) continue;
123 if (!selected || better(&entry, selected)) selected = &entry;
124 }
125 if (!selected) break;
126 selected->probe->update(1);
127 selected->waitAge = 0;
128 lastCapturedFaceCount_ += selected->probe->getLastCapturedFaceCount();
129 }
130
131 while (filterBudget-- > 0) {
132 Entry *selected = nullptr;
133 for (Entry &entry : entries_) {
134 if (!entry.probe || entry.probe->isCapturePending() ||
135 entry.probe->getRevision() == entry.probe->getPublishedRevision())
136 continue;
137 if (!selected || better(&entry, selected)) selected = &entry;
138 }
139 if (!selected) break;
140 if (selected->probe->stageCapturedFaces() &&
141 selected->probe->filterAndPublish(filterSamples)) {
142 selected->waitAge = 0;
143 ++lastPublishedCount_;
144 } else {
145 break;
146 }
147 }
148 return lastPublishedCount_;
149}
150
152 lastSelectedCount_ = 0;
153 lastCandidateCount_ = 0;
154 lastDistanceCulledCount_ = 0;
155 if (!camera) {
156 lastSelected_.clear();
157 return 0;
158 }
159 for (int slot = 0; slot < Camera3D::Data::kMaxReflectionProbes; ++slot)
160 camera->clearReflectionProbe(slot);
161
162 struct Candidate {
163 ReflectionProbeCapture *probe = nullptr;
164 int priority = 0;
165 float distance2 = 0.f;
166 bool retained = false;
167 uint64_t order = 0;
168 };
169 std::vector<Candidate> candidates;
170 candidates.reserve(entries_.size());
171 const auto cameraData = camera->data();
172 const float eye[3] = {cameraData->eyeX, cameraData->eyeY, cameraData->eyeZ};
173 const float target[3] = {cameraData->targetX, cameraData->targetY, cameraData->targetZ};
174 const float viewSegment[3] = {target[0] - eye[0], target[1] - eye[1],
175 target[2] - eye[2]};
176 const float viewSegmentLength2 = viewSegment[0] * viewSegment[0] +
177 viewSegment[1] * viewSegment[1] +
178 viewSegment[2] * viewSegment[2];
179 for (Entry &entry : entries_) {
180 ReflectionProbeCapture *probe = entry.probe;
181 if (!probe) continue;
182 const float center[3] = {probe->getCenterX(), probe->getCenterY(), probe->getCenterZ()};
183 const float eyeDx = center[0] - eye[0], eyeDy = center[1] - eye[1], eyeDz = center[2] - eye[2];
184 const bool distanceCulled = distanceCullingEnabled_ &&
185 eyeDx * eyeDx + eyeDy * eyeDy + eyeDz * eyeDz >= maxRenderDistance_ * maxRenderDistance_;
186 if (entry.distanceCulled && !distanceCulled) probe->queueCapture();
187 entry.distanceCulled = distanceCulled;
188 if (distanceCulled) { ++lastDistanceCulledCount_; continue; }
189 if (!probe->getActiveCubemap() || probe->getPublishedRevision() == 0 ||
190 probe->getInfluenceIntensity() <= 0.f)
191 continue;
192 const float extent[3] = {probe->getInfluenceExtentX(), probe->getInfluenceExtentY(),
193 probe->getInfluenceExtentZ()};
194 float closestT = 0.f;
195 if (viewSegmentLength2 > 1e-8f) {
196 const float toCenter[3] = {center[0] - eye[0], center[1] - eye[1],
197 center[2] - eye[2]};
198 closestT = std::clamp((toCenter[0] * viewSegment[0] +
199 toCenter[1] * viewSegment[1] +
200 toCenter[2] * viewSegment[2]) /
201 viewSegmentLength2,
202 0.f, 1.f);
203 }
204 const float closestViewPoint[3] = {
205 eye[0] + viewSegment[0] * closestT,
206 eye[1] + viewSegment[1] * closestT,
207 eye[2] + viewSegment[2] * closestT,
208 };
209 float distance2 = 0.f;
210 for (int axis = 0; axis < 3; ++axis) {
211 const float outside =
212 std::max(std::abs(closestViewPoint[axis] - center[axis]) - extent[axis], 0.f);
213 distance2 += outside * outside;
214 }
215 const bool retained =
216 std::find(lastSelected_.begin(), lastSelected_.end(), probe) != lastSelected_.end();
217 const float distance = std::sqrt(distance2);
218 const float biasedDistance =
219 retained ? std::max(distance - selectionHysteresis_, 0.f) : distance;
220 candidates.push_back(Candidate{probe, probe->getInfluencePriority(),
221 biasedDistance * biasedDistance, retained, entry.order});
222 }
223 lastCandidateCount_ = static_cast<int>(candidates.size());
224 std::stable_sort(candidates.begin(), candidates.end(),
225 [](const Candidate &a, const Candidate &b) {
226 if (a.priority != b.priority) return a.priority > b.priority;
227 if (a.distance2 != b.distance2) return a.distance2 < b.distance2;
228 if (a.retained != b.retained) return a.retained;
229 return a.order < b.order;
230 });
231
232 const int selected = std::min(static_cast<int>(candidates.size()),
234 lastSelected_.clear();
235 lastSelected_.reserve(static_cast<size_t>(selected));
236 for (int slot = 0; slot < selected; ++slot) {
237 ReflectionProbeCapture *probe = candidates[static_cast<size_t>(slot)].probe;
238 if (probe->applyConfiguredToCamera(camera, slot)) {
239 lastSelected_.push_back(probe);
240 ++lastSelectedCount_;
241 }
242 }
243 return lastSelectedCount_;
244}
245
246} // namespace eve::graphics
LogicalId target
int mask
int priority
float camera[2]
float u
Definition Grass.cpp:233
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
std::vector< std::int32_t > order
size_t queued
Definition OnnxGpgpu.cpp:60
glm::vec3 eye
bool found
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
EVENGINE_API_BACKENDS public API.
Incremental six-face HDR scene capture for a runtime reflection probe.
float getInfluenceExtentY() const
Return persistent influence half extent Y.
float getCaptureFarDistance() const
Return the capture far distance used for spatial dirty culling.
bool applyConfiguredToCamera(Camera3D *camera, int slot)
Submit the persistent influence and active cubemap to a Camera3D slot. @compatibility Preserves the s...
float getInfluenceExtentX() const
Return persistent influence half extent X.
float getCenterY() const
Return probe world-space center Y.
uint32_t getPublishedRevision() const
Last capture revision atomically published for lighting.
int getInfluencePriority() const
Return persistent overlap-selection priority.
float getInfluenceExtentZ() const
Return persistent influence half extent Z.
float getInfluenceIntensity() const
Return persistent reflection intensity.
void queueCapture()
Queue a coherent recapture without restarting a revision already in progress.
float getCenterZ() const
Return probe world-space center Z.
int getCaptureMask() const
Return the object-layer mask visible to this reflection capture.
float getCenterX() const
Return probe world-space center X.
Texture * getActiveCubemap() const
Last fully filtered cubemap, safe to assign to Camera3D reflection probes. @lifetime Returned texture...
int tick(int faceBudget=1, int filterBudget=1, int filterSamples=64)
Advance every refresh policy while enforcing scene-wide GPU work budgets.
~ReflectionProbeRegistry()
Detach all probes before script-owned destruction.
void remove(ReflectionProbeCapture *probe)
Remove a registered probe.
Result< void > setMaxRenderDistance(float distance)
Set the positive Pcg probe render distance; failure preserves the previous value.
void setSelectionHysteresis(float distance)
Set world-space distance hysteresis used to retain previous camera selections.
int queueCaptureAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int changedMask=-1)
Queue probes whose mask and capture-radius sphere overlap a changed world AABB.
void clear()
Remove every registered probe.
int queueCapture(int changedMask=-1)
Queue coherent recapture for probes whose capture mask intersects changedMask.
int updateCamera(Camera3D *camera)
Select eight probes nearest the camera view segment and replace Camera3D slots.
void add(ReflectionProbeCapture *probe)
Register a probe once; duplicate registrations are ignored.
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
static constexpr int kMaxReflectionProbes