6#include <glm/gtc/matrix_transform.hpp>
7#include <glm/gtc/quaternion.hpp>
8#include <glm/gtx/quaternion.hpp>
22 bool sphereVisible(
const glm::vec3 &
center,
float radius)
const {
23 for (
const auto &pl :
p) {
31Frustum extractFrustum(
const glm::mat4 &
m) {
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;
47 for (
auto &pl :
f.
p) norm(pl);
51glm::mat4 decalModel(
const DecalInstance &
d) {
53 const float len = glm::length(
normal);
54 if (len < 1e-6f)
normal = glm::vec3(0.f, 1.f, 0.f);
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));
74int DecalManager::limitFor(
const std::string &
kind)
const {
75 for (
const auto &l : limits_) {
76 if (l.kind ==
kind)
return l.limit;
85 float metalStrength,
float emissiveStrength) {
89 for (
const auto &
d : decals_) {
90 if (
d.kind ==
kind) ++ofKind;
92 while (ofKind >=
limit) {
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;
99 if (it == decals_.end())
break;
113 d.yaw = randomYaw ? (
seed != 0 ? float((
seed * 2654435761u) % 6283u) / 1000.f
114 : float(std::rand()) / float(RAND_MAX) * 6.28318f)
119 d.fadeIn = fadeIn > 0.f ? fadeIn : 0.f;
120 d.fadeOut = fadeOut > 0.f ? fadeOut : 0.f;
123 d.roughnessStrength = roughnessStrength;
124 d.metalStrength = metalStrength;
125 d.emissiveStrength = emissiveStrength;
127 decals_.push_back(
d);
132 for (
auto it = decals_.begin(); it != decals_.end(); ++it) {
142 for (
const auto&
d : decals_) {
143 if (
d.id ==
id)
return true;
153 float metalStrength,
float emissiveStrength) {
154 for (
auto &
d : decals_) {
155 if (
d.id !=
id)
continue;
157 d.roughnessStrength = roughnessStrength;
158 d.metalStrength = metalStrength;
159 d.emissiveStrength = emissiveStrength;
166 for (
auto &
d : decals_) {
167 if (
d.id !=
id)
continue;
178 for (
auto &
d : decals_) {
179 if (
d.id !=
id)
continue;
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;
200 float blendSharpness) {
202 if (mode ==
"triplanar")
204 else if (mode ==
"spherical")
206 else if (mode ==
"world")
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;
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;
231 d.parallaxMinLayers = minLayers;
232 d.parallaxMaxLayers = maxLayers;
239 if (!std::isfinite(
width) || width < 0.f || width > 0.49f)
241 for (
auto &
d : decals_) {
242 if (
d.id !=
id)
continue;
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 ||
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;
271 const int limit = limitFor(candidate.
kind);
273 while (std::count_if(next.begin(), next.end(), [&](
const DecalInstance& decal) {
274 return decal.kind == candidate.kind;
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;
284 candidate.
id = nextId_++;
286 next.push_back(std::move(candidate));
288 return decals_.back().id;
292 for (
auto &l : limits_) {
293 if (l.kind ==
kind) {
302 if (dt <= 0.f)
return;
303 for (
auto it = decals_.begin(); it != decals_.end();) {
306 it->lifetime > 0.f && it->age >= it->lifetime + std::max(it->fadeOut, 0.f);
308 it = decals_.erase(it);
315 const glm::mat4 &
viewProj,
float aspect) {
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;
326 if (!
frustum.sphereVisible(
c,
d.size * 1.25f))
continue;
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;
332 const float span = std::max(
d.fadeOut, 0.001f);
333 fade *= std::max(0.f, 1.f - outT / span);
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);
graphics::Texture * albedo
std::array< float, 3 > scale
graphics::Canvas * previous
std::weak_ptr< const void > lifetime
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.
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).