载入中...
搜索中...
未找到
FogInteractor.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
5
6#include <algorithm>
7#include <cmath>
8
9#include <glm/common.hpp>
10#include <glm/geometric.hpp>
11#include <glm/vec3.hpp>
12
13namespace eve::graphics::fog {
14namespace {
15
16[[nodiscard]] bool finiteVec(const glm::vec3& v) noexcept {
17 return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z);
18}
19
20[[nodiscard]] glm::vec3 safeNormalize(const glm::vec3& v, const glm::vec3& fallback) noexcept {
21 const float len2 = glm::dot(v, v);
22 if (len2 < 1e-12f) return fallback;
23 return v * (1.f / std::sqrt(len2));
24}
25
26} // namespace
27
28Result<void> FogInteractor::setProxies(std::vector<FogSolidProxy> proxies) {
29 for (std::size_t i = 0; i < proxies.size(); ++i) {
30 const auto& p = proxies[i];
31 if (!finiteVec(p.position) || !finiteVec(p.velocity) || !finiteVec(p.extents) ||
32 !finiteVec(p.axis) || !finiteVec(p.axisX) || !finiteVec(p.axisY) || !finiteVec(p.axisZ)) {
34 DiagnosticCode::InvalidArgument, "proxy vectors must be finite", "proxies", {},
35 "graphics.fog"));
36 }
37 if (p.extents.x <= 0.f || p.extents.y <= 0.f || p.extents.z <= 0.f) {
39 DiagnosticCode::InvalidArgument, "proxy extents must be positive", "extents", {},
40 "graphics.fog"));
41 }
42 if (!std::isfinite(p.drag) || p.drag < 0.f || p.drag > 1.f || !std::isfinite(p.wakeStrength) ||
43 p.wakeStrength < 0.f) {
45 DiagnosticCode::InvalidArgument, "drag must be in [0,1] and wakeStrength >= 0",
46 "drag", {}, "graphics.fog"));
47 }
48 }
49 proxies_ = std::move(proxies);
50 return Result<void>::success();
51}
52
53const FogSolidProxy& FogInteractor::proxyAt(std::size_t index) const {
54 return proxies_.at(index);
55}
56
57float FogInteractor::signedDistance(const FogSolidProxy& proxy, const glm::vec3& world) const noexcept {
58 switch (proxy.shape) {
60 return glm::length(world - proxy.position) - proxy.extents.x;
61 }
63 const glm::vec3 a = proxy.position - proxy.axis;
64 const glm::vec3 b = proxy.position + proxy.axis;
65 const glm::vec3 pa = world - a;
66 const glm::vec3 ba = b - a;
67 const float h = std::clamp(glm::dot(pa, ba) / std::max(glm::dot(ba, ba), 1e-8f), 0.f, 1.f);
68 return glm::length(pa - ba * h) - proxy.extents.x;
69 }
70 case FogProxyShape::Obb: {
71 const glm::vec3 d = world - proxy.position;
72 const glm::vec3 local(glm::dot(d, proxy.axisX), glm::dot(d, proxy.axisY),
73 glm::dot(d, proxy.axisZ));
74 const glm::vec3 q = glm::abs(local) - proxy.extents;
75 return glm::length(glm::max(q, glm::vec3(0.f))) +
76 std::min(std::max(q.x, std::max(q.y, q.z)), 0.f);
77 }
78 }
79 return 1.f;
80}
81
82glm::vec3 FogInteractor::closestPoint(const FogSolidProxy& proxy, const glm::vec3& world) const noexcept {
83 const float sd = signedDistance(proxy, world);
84 if (sd >= 0.f) {
85 const glm::vec3 n = surfaceNormal(proxy, world);
86 return world - n * sd;
87 }
88 const glm::vec3 n = surfaceNormal(proxy, world);
89 return world - n * sd;
90}
91
92glm::vec3 FogInteractor::surfaceNormal(const FogSolidProxy& proxy, const glm::vec3& world) const noexcept {
93 constexpr float e = 1e-3f;
94 const float dx = signedDistance(proxy, world + glm::vec3(e, 0.f, 0.f)) -
95 signedDistance(proxy, world - glm::vec3(e, 0.f, 0.f));
96 const float dy = signedDistance(proxy, world + glm::vec3(0.f, e, 0.f)) -
97 signedDistance(proxy, world - glm::vec3(0.f, e, 0.f));
98 const float dz = signedDistance(proxy, world + glm::vec3(0.f, 0.f, e)) -
99 signedDistance(proxy, world - glm::vec3(0.f, 0.f, e));
100 return safeNormalize(glm::vec3(dx, dy, dz), glm::vec3(0.f, 1.f, 0.f));
101}
102
103bool FogInteractor::isSolidWorld(const glm::vec3& world) const noexcept {
104 for (const auto& p : proxies_) {
105 if (!p.enabled) continue;
106 if (signedDistance(p, world) <= 0.f) return true;
107 }
108 return false;
109}
110
112 if (!std::isfinite(dt) || dt < 0.f) {
114 DiagnosticCode::InvalidArgument, "dt must be finite and >= 0", "dt", {}, "graphics.fog"));
115 }
116 if (grid.width() <= 0) {
118 "grid", {}, "graphics.fog"));
119 }
120
121 const FogWorldBounds& bounds = grid.bounds_;
122 const glm::vec3 cell = grid.cellSize_;
123
124 std::fill(grid.solid_.begin(), grid.solid_.end(), 0);
125
126 for (int z = 0; z < grid.depth_; ++z) {
127 for (int y = 0; y < grid.height_; ++y) {
128 for (int x = 0; x < grid.width_; ++x) {
129 const glm::vec3 world =
130 bounds.minimum + glm::vec3((x + 0.5f) * cell.x, (y + 0.5f) * cell.y,
131 (z + 0.5f) * cell.z);
132 if (!isSolidWorld(world)) continue;
133 grid.solid_[grid.cellIndex(x, y, z)] = 1;
134 grid.setDensity(x, y, z, 0.f);
135
136 for (const auto& proxy : proxies_) {
137 if (!proxy.enabled) continue;
138 if (signedDistance(proxy, world) > cell.x) continue;
139 const glm::vec3 n = surfaceNormal(proxy, world);
140 const float vn = glm::dot(proxy.velocity, n);
141 // Windward push, leeward suction, tangential drag.
142 const glm::vec3 tangential = proxy.velocity - n * vn;
143 const glm::vec3 wake =
144 n * (vn * proxy.wakeStrength) + tangential * (1.f - proxy.drag);
145 // Stamp onto adjacent faces (wake pair).
146 if (x + 1 < grid.width_)
147 grid.u_[grid.uIndex(x + 1, y, z)] += wake.x * dt;
148 if (x > 0) grid.u_[grid.uIndex(x, y, z)] += wake.x * dt;
149 if (y + 1 < grid.height_)
150 grid.v_[grid.vIndex(x, y + 1, z)] += wake.y * dt;
151 if (y > 0) grid.v_[grid.vIndex(x, y, z)] += wake.y * dt;
152 if (z + 1 < grid.depth_)
153 grid.w_[grid.wIndex(x, y, z + 1)] += wake.z * dt;
154 if (z > 0) grid.w_[grid.wIndex(x, y, z)] += wake.z * dt;
155 }
156 }
157 }
158 }
159
160 // Zero velocity into solid faces.
161 for (int z = 0; z < grid.depth_; ++z) {
162 for (int y = 0; y < grid.height_; ++y) {
163 for (int i = 0; i <= grid.width_; ++i) {
164 const bool leftSolid = i > 0 && grid.solid_[grid.cellIndex(i - 1, y, z)];
165 const bool rightSolid = i < grid.width_ && grid.solid_[grid.cellIndex(i, y, z)];
166 if (leftSolid || rightSolid) grid.u_[grid.uIndex(i, y, z)] = 0.f;
167 }
168 }
169 }
170 for (int z = 0; z < grid.depth_; ++z) {
171 for (int j = 0; j <= grid.height_; ++j) {
172 for (int x = 0; x < grid.width_; ++x) {
173 const bool downSolid = j > 0 && grid.solid_[grid.cellIndex(x, j - 1, z)];
174 const bool upSolid = j < grid.height_ && grid.solid_[grid.cellIndex(x, j, z)];
175 if (downSolid || upSolid) grid.v_[grid.vIndex(x, j, z)] = 0.f;
176 }
177 }
178 }
179 for (int k = 0; k <= grid.depth_; ++k) {
180 for (int y = 0; y < grid.height_; ++y) {
181 for (int x = 0; x < grid.width_; ++x) {
182 const bool backSolid = k > 0 && grid.solid_[grid.cellIndex(x, y, k - 1)];
183 const bool frontSolid = k < grid.depth_ && grid.solid_[grid.cellIndex(x, y, k)];
184 if (backSolid || frontSolid) grid.w_[grid.wIndex(x, y, k)] = 0.f;
185 }
186 }
187 }
188 return Result<void>::success();
189}
190
191glm::vec3 FogInteractor::clipAdvection(const glm::vec3& startLocal, const glm::vec3& deltaLocal,
192 const FogWorldBounds& bounds,
193 const glm::vec3& cellSize) const noexcept {
194 if (proxies_.empty()) return startLocal + deltaLocal;
195
196 const float len = glm::length(deltaLocal);
197 if (len < 1e-8f) return startLocal;
198
199 const int steps = std::max(1, static_cast<int>(std::ceil(len * 2.f)));
200 const glm::vec3 step = deltaLocal / static_cast<float>(steps);
201 glm::vec3 p = startLocal;
202 for (int i = 0; i < steps; ++i) {
203 const glm::vec3 next = p + step;
204 const glm::vec3 world =
205 bounds.minimum + glm::vec3((next.x + 0.5f) * cellSize.x, (next.y + 0.5f) * cellSize.y,
206 (next.z + 0.5f) * cellSize.z);
207 if (isSolidWorld(world)) return p;
208 p = next;
209 }
210 return p;
211}
212
213} // namespace eve::graphics::fog
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
glm::vec4 p[6]
Stable, structured diagnostics shared by engine modules.
glm::vec3 n
Definition Grass.cpp:63
std::array< double, 10 > q
float v
int h
std::string local
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
World3D * world
float d
int steps
float dz
float dy
float dx
Cell cell
float step
Definition TreeMesh.cpp:314
uint32_t index
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
Result< void > applyToGrid(MacFluidGrid &grid, float dt) const
Rasterize solids into the MAC solid mask, clear interior density, and inject wake / drag into face ve...
const FogSolidProxy & proxyAt(std::size_t index) const
Result< void > setProxies(std::vector< FogSolidProxy > proxies)
Replace the proxy set atomically after validation.
bool isSolidWorld(const glm::vec3 &world) const noexcept
True when a world point lies inside any enabled solid.
glm::vec3 clipAdvection(const glm::vec3 &startLocal, const glm::vec3 &deltaLocal, const FogWorldBounds &bounds, const glm::vec3 &cellSize) const noexcept
Clip a local-cell advection displacement against thin solids.
MAC (Marker-And-Cell) staggered-grid Eulerian fog fluid.
One analytic solid that carves / wakes the fog domain.
Definition FogTypes.h:142
Axis-aligned world bounds for a fog simulation domain.
Definition FogTypes.h:96
glm::vec4 bounds