载入中...
搜索中...
未找到
DecalManager.cpp
浏览该文件的文档.
2
3#include "graphics/Graphics.h"
5
6#include <glm/gtc/matrix_transform.hpp>
7#include <glm/gtc/quaternion.hpp>
8#include <glm/gtx/quaternion.hpp>
9
10#include <algorithm>
11#include <cmath>
12#include <cstdlib>
13
14namespace eve::decal {
15
16namespace {
17
19struct Frustum {
20 glm::vec4 p[6]{};
21
22 bool sphereVisible(const glm::vec3 &center, float radius) const {
23 for (const auto &pl : p) {
24 const float d = pl.x * center.x + pl.y * center.y + pl.z * center.z + pl.w;
25 if (d < -radius) return false;
26 }
27 return true;
28 }
29};
30
31Frustum extractFrustum(const glm::mat4 &m) {
32 Frustum f;
33 const glm::vec4 r0(m[0][0], m[1][0], m[2][0], m[3][0]);
34 const glm::vec4 r1(m[0][1], m[1][1], m[2][1], m[3][1]);
35 const glm::vec4 r2(m[0][2], m[1][2], m[2][2], m[3][2]);
36 const glm::vec4 r3(m[0][3], m[1][3], m[2][3], m[3][3]);
37 auto norm = [](glm::vec4 &v) {
38 const float l = glm::length(glm::vec3(v));
39 if (l > 1e-8f) v /= l;
40 };
41 f.p[0] = r3 + r0; // left
42 f.p[1] = r3 - r0; // right
43 f.p[2] = r3 + r1; // bottom
44 f.p[3] = r3 - r1; // top
45 f.p[4] = r2; // near (Vulkan zero-to-one depth)
46 f.p[5] = r3 - r2; // far
47 for (auto &pl : f.p) norm(pl);
48 return f;
49}
50
51glm::mat4 decalModel(const DecalInstance &d) {
52 glm::vec3 normal(d.nx, d.ny, d.nz);
53 const float len = glm::length(normal);
54 if (len < 1e-6f) normal = glm::vec3(0.f, 1.f, 0.f);
55 else normal /= len;
56
57 // Lift slightly along the normal to avoid z-fighting with the surface.
58 glm::mat4 m = glm::translate(glm::mat4(1.f),
59 glm::vec3(d.x, d.y, d.z) + normal * 0.008f);
60 const glm::quat orient = glm::rotation(glm::vec3(0.f, 0.f, 1.f), normal);
61 m *= glm::mat4_cast(orient);
62 m = glm::rotate(m, d.yaw, glm::vec3(0.f, 0.f, 1.f));
63 m = glm::scale(m, glm::vec3(d.size, d.size, d.depth));
64 return m;
65}
66
67} // namespace
68
70 static DecalManager manager;
71 return manager;
72}
73
74int DecalManager::limitFor(const std::string &kind) const {
75 for (const auto &l : limits_) {
76 if (l.kind == kind) return l.limit;
77 }
78 return 0;
79}
80
81int DecalManager::project(float x, float y, float z, float nx, float ny, float nz,
82 graphics::Texture *albedo, const std::string &kind, float size,
83 float depth, bool randomYaw, int seed, float fadeIn, float lifetime,
84 float fadeOut, float normalStrength, float roughnessStrength,
85 float metalStrength, float emissiveStrength) {
86 const int limit = limitFor(kind);
87 if (limit > 0) {
88 int ofKind = 0;
89 for (const auto &d : decals_) {
90 if (d.kind == kind) ++ofKind;
91 }
92 while (ofKind >= limit) {
93 // Evict the oldest instance of this kind.
94 auto it = decals_.end();
95 for (auto i = decals_.begin(); i != decals_.end(); ++i) {
96 if (i->kind != kind) continue;
97 if (it == decals_.end() || i->age > it->age) it = i;
98 }
99 if (it == decals_.end()) break;
100 decals_.erase(it);
101 --ofKind;
102 }
103 }
104
106 d.id = nextId_++;
107 d.x = x;
108 d.y = y;
109 d.z = z;
110 d.nx = nx;
111 d.ny = ny;
112 d.nz = nz;
113 d.yaw = randomYaw ? (seed != 0 ? float((seed * 2654435761u) % 6283u) / 1000.f
114 : float(std::rand()) / float(RAND_MAX) * 6.28318f)
115 : 0.f;
116 d.size = size > 0.f ? size : 0.5f;
117 d.depth = depth > 0.f ? depth : 0.15f;
118 d.lifetime = lifetime > 0.f ? lifetime : 0.f;
119 d.fadeIn = fadeIn > 0.f ? fadeIn : 0.f;
120 d.fadeOut = fadeOut > 0.f ? fadeOut : 0.f;
121 d.albedo = albedo;
122 d.normalStrength = normalStrength;
123 d.roughnessStrength = roughnessStrength;
124 d.metalStrength = metalStrength;
125 d.emissiveStrength = emissiveStrength;
126 d.kind = kind;
127 decals_.push_back(d);
128 return d.id;
129}
130
132 for (auto it = decals_.begin(); it != decals_.end(); ++it) {
133 if (it->id == id) {
134 decals_.erase(it);
135 return true;
136 }
137 }
138 return false;
139}
140
141bool DecalManager::contains(int id) const {
142 for (const auto& d : decals_) {
143 if (d.id == id) return true;
144 }
145 return false;
146}
147
148void DecalManager::clearAll() { decals_.clear(); }
149
150int DecalManager::count() const { return int(decals_.size()); }
151
152bool DecalManager::setStrength(int id, float normalStrength, float roughnessStrength,
153 float metalStrength, float emissiveStrength) {
154 for (auto &d : decals_) {
155 if (d.id != id) continue;
156 d.normalStrength = normalStrength;
157 d.roughnessStrength = roughnessStrength;
158 d.metalStrength = metalStrength;
159 d.emissiveStrength = emissiveStrength;
160 return true;
161 }
162 return false;
163}
164
165bool DecalManager::setUvRect(int id, float x, float y, float w, float h) {
166 for (auto &d : decals_) {
167 if (d.id != id) continue;
168 d.uvRect[0] = x;
169 d.uvRect[1] = y;
170 d.uvRect[2] = w;
171 d.uvRect[3] = h;
172 return true;
173 }
174 return false;
175}
176
178 for (auto &d : decals_) {
179 if (d.id != id) continue;
180 d.normal = normal;
181 d.params = params;
182 return true;
183 }
184 return false;
185}
186
187bool DecalManager::setBlend(int id, const std::string &mode) {
188 int blend = 0;
189 if (mode == "add") blend = 1;
190 else if (mode != "over" && !mode.empty()) return false;
191 for (auto &d : decals_) {
192 if (d.id != id) continue;
193 d.blendMode = blend;
194 return true;
195 }
196 return false;
197}
198
200 float blendSharpness) {
201 int projection = 0;
202 if (mode == "triplanar")
203 projection = 1;
204 else if (mode == "spherical")
205 projection = 2;
206 else if (mode == "world")
207 projection = 3;
208 else if (mode != "planar" && !mode.empty())
210 if (!std::isfinite(blendSharpness) || blendSharpness <= 0.f)
212 for (auto &d : decals_) {
213 if (d.id != id) continue;
214 d.projectionMode = projection;
215 d.blendSharpness = blendSharpness;
217 }
219}
220
222 float maxLayers) {
223 if (!std::isfinite(scale) || scale < 0.f || scale > 1.f)
225 if (!std::isfinite(minLayers) || !std::isfinite(maxLayers) || minLayers < 1.f ||
226 maxLayers < minLayers || maxLayers > 64.f)
228 for (auto &d : decals_) {
229 if (d.id != id) continue;
230 d.parallaxScale = scale;
231 d.parallaxMinLayers = minLayers;
232 d.parallaxMaxLayers = maxLayers;
234 }
236}
237
239 if (!std::isfinite(width) || width < 0.f || width > 0.49f)
241 for (auto &d : decals_) {
242 if (d.id != id) continue;
243 d.edgeFadeWidth = width;
245 }
247}
248
249int DecalManager::replace(int previousId, DecalInstance candidate) {
250 const float normalLength2 = candidate.nx * candidate.nx + candidate.ny * candidate.ny +
251 candidate.nz * candidate.nz;
252 if (!std::isfinite(normalLength2) || normalLength2 <= 1e-12f ||
253 !std::isfinite(candidate.size) || candidate.size <= 0.f ||
254 !std::isfinite(candidate.depth) || candidate.depth <= 0.f ||
255 candidate.blendMode < 0 || candidate.blendMode > 1 || candidate.projectionMode < 0 ||
256 candidate.projectionMode > 3 || !std::isfinite(candidate.blendSharpness) ||
257 candidate.blendSharpness <= 0.f || !std::isfinite(candidate.parallaxScale) ||
258 candidate.parallaxScale < 0.f || candidate.parallaxScale > 1.f ||
259 !std::isfinite(candidate.parallaxMinLayers) || !std::isfinite(candidate.parallaxMaxLayers) ||
260 candidate.parallaxMinLayers < 1.f || candidate.parallaxMaxLayers < candidate.parallaxMinLayers ||
261 candidate.parallaxMaxLayers > 64.f || !std::isfinite(candidate.edgeFadeWidth) ||
262 candidate.edgeFadeWidth < 0.f || candidate.edgeFadeWidth > 0.49f)
263 return 0;
264 std::vector<DecalInstance> next = decals_;
265 if (previousId != 0) {
266 auto previous = std::find_if(next.begin(), next.end(),
267 [previousId](const DecalInstance& decal) { return decal.id == previousId; });
268 if (previous == next.end()) return 0;
269 next.erase(previous);
270 }
271 const int limit = limitFor(candidate.kind);
272 if (limit > 0) {
273 while (std::count_if(next.begin(), next.end(), [&](const DecalInstance& decal) {
274 return decal.kind == candidate.kind;
275 }) >= limit) {
276 auto oldest = next.end();
277 for (auto it = next.begin(); it != next.end(); ++it)
278 if (it->kind == candidate.kind &&
279 (oldest == next.end() || it->age > oldest->age)) oldest = it;
280 if (oldest == next.end()) break;
281 next.erase(oldest);
282 }
283 }
284 candidate.id = nextId_++;
285 candidate.age = 0.f;
286 next.push_back(std::move(candidate));
287 decals_.swap(next);
288 return decals_.back().id;
289}
290
291void DecalManager::setLimit(const std::string &kind, int limit) {
292 for (auto &l : limits_) {
293 if (l.kind == kind) {
294 l.limit = limit > 0 ? limit : 0;
295 return;
296 }
297 }
298 limits_.push_back({kind, limit > 0 ? limit : 0});
299}
300
301void DecalManager::update(float dt) {
302 if (dt <= 0.f) return;
303 for (auto it = decals_.begin(); it != decals_.end();) {
304 it->age += dt;
305 const bool expired =
306 it->lifetime > 0.f && it->age >= it->lifetime + std::max(it->fadeOut, 0.f);
307 if (expired)
308 it = decals_.erase(it);
309 else
310 ++it;
311 }
312}
313
314void DecalManager::drawAll(graphics::Graphics &gfx, float eyeX, float eyeY, float eyeZ,
315 const glm::mat4 &viewProj, float aspect) {
316 (void)aspect;
317 const Frustum frustum = extractFrustum(viewProj);
318 const glm::vec3 eye(eyeX, eyeY, eyeZ);
319 constexpr float kMaxDist = 80.f;
320 constexpr float kMaxDistSq = kMaxDist * kMaxDist;
321 for (const auto &d : decals_) {
322 if (!d.albedo) continue;
323 const glm::vec3 c(d.x, d.y, d.z);
324 const glm::vec3 toEye = c - eye;
325 if (glm::dot(toEye, toEye) > kMaxDistSq) continue; // distance LOD
326 if (!frustum.sphereVisible(c, d.size * 1.25f)) continue;
327 float fade = 1.f;
328 if (d.fadeIn > 0.f && d.age < d.fadeIn) fade = std::min(1.f, d.age / d.fadeIn);
329 if (d.lifetime > 0.f) {
330 const float outT = d.age - d.lifetime;
331 if (outT >= 0.f) {
332 const float span = std::max(d.fadeOut, 0.001f);
333 fade *= std::max(0.f, 1.f - outT / span);
334 }
335 }
336 if (fade <= 0.001f) continue;
337 gfx.drawDecal(decalModel(d), d.albedo, d.normal, d.params, d.uvRect, fade,
338 d.normalStrength, d.roughnessStrength, d.metalStrength, d.emissiveStrength,
339 d.blendMode, d.projectionMode, d.blendSharpness, d.parallaxScale,
340 d.parallaxMinLayers, d.parallaxMaxLayers, d.edgeFadeWidth);
341 }
342}
343
344} // namespace eve::decal
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float normalStrength
float nx
float nz
float ny
graphics::Texture * albedo
glm::vec4 p[6]
float v
std::int32_t c
float blend
int h
std::uint32_t width
TokenKind kind
std::array< float, 3 > scale
Texture * normal
graphics::Canvas * previous
float f
std::weak_ptr< const void > lifetime
float radius
std::uint32_t seed
Definition PointSet.cpp:807
float d
glm::vec3 eye
FrustumPlanes frustum
glm::mat4 viewProj
int limit
Definition TreeMesh.cpp:164
float size
Definition TreeMesh.cpp:156
std::uint32_t depth
float m[16]
CPU-side decal registry: spawn / remove / fade / quotas, and the per-frame drawer that feeds the grap...
bool setUvRect(int id, float x, float y, float w, float h)
Sets the uv rect.
bool setTextures(int id, graphics::Texture *normal, graphics::Texture *params)
Sets the textures.
void drawAll(graphics::Graphics &gfx, float eyeX, float eyeY, float eyeZ, const glm::mat4 &viewProj, float aspect)
Draws all live instances into the active graphics decal pass.
bool contains(int id) const
Whether a projected decal id is still registered.
bool setStrength(int id, float normalStrength, float roughnessStrength, float metalStrength, float emissiveStrength)
Sets the strength.
int replace(int previousId, DecalInstance candidate)
Atomically install a fully configured instance and remove a previous generation.
DecalProjectionStatus setProjection(int id, const std::string &mode, float blendSharpness=4.f)
Projection mode: "planar" (default) or "triplanar".
static DecalManager & inst()
Inst.
DecalEdgeFadeStatus setEdgeFade(int id, float width)
Configure normalized edge feather width in [0, 0.49].
void setLimit(const std::string &kind, int limit)
Per-kind quota ("blood" -> 64 etc.); oldest of the kind is evicted.
DecalParallaxStatus setParallax(int id, float scale, float minLayers=8.f, float maxLayers=24.f)
Configure POM; scale zero disables it and layers satisfy 1 <= min <= max <= 64.
void update(float dt)
Advance instance ages; expire and evict finished decals.
int count() const
Returns the number of .
bool setBlend(int id, const std::string &mode)
Sets the blend.
bool remove(int id)
Removes .
int project(float x, float y, float z, float nx, float ny, float nz, graphics::Texture *albedo, const std::string &kind, float size, float depth, bool randomYaw, int seed, float fadeIn, float lifetime, float fadeOut, float normalStrength, float roughnessStrength, float metalStrength, float emissiveStrength)
Spawn a decal at a world-space point facing along (nx,ny,nz).
virtual void drawDecal(const glm::mat4 &model, Texture *albedo, Texture *normal, Texture *params, const float uvRect[4], float fade, float normalStrength, float roughnessStrength, float metalStrength, float emissiveStrength, int blendMode=0, int projectionMode=0, float blendSharpness=4.f, float parallaxScale=0.f, float parallaxMinLayers=8.f, float parallaxMaxLayers=24.f, float edgeFadeWidth=0.06f)=0
Queue one box-projected decal draw. model maps the unit decal box ([-0.5, 0.5]^3, +Z = decal forward)...
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
std::vector< ParamSpec > params
DecalParallaxStatus
Outcome of applying height-driven parallax settings to a live decal.
DecalProjectionStatus
Outcome of DecalManager::setProjection / Decal::setProjection.
DecalEdgeFadeStatus
Outcome of changing the projection-volume edge mask width.
One live decal instance (CPU-side state; GPU draw happens per frame).