engine: reject mbf21 and shit24 wads. there is no way to know if it is safe to ignore...
[k8vavoom.git] / source / level / level_decals.cpp
blobc86303063863f0d1929bbeeb42185a81d6a15f0a
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 #include "../gamedefs.h"
27 #include "../psim/p_decal.h"
28 #ifdef CLIENT
29 # include "../drawer.h" /* VRenderLevelPublic */
30 #endif
33 extern VCvarB r_decals;
34 extern VCvarB r_decals_flat;
35 extern VCvarB r_decals_wall;
36 extern VCvarB k8gore_enabled;
37 extern VCvarI k8gore_enabled_override;
38 extern VCvarI k8gore_enabled_override_decal;
40 static VCvarB r_decal_switch_special("r_decal_switch_special", true, "Make decals more translucent on switch textures?", CVAR_Archive|CVAR_NoShadow);
41 static VCvarF r_decal_switch_blood_alpha("r_decal_switch_blood_alpha", "0.36", "Force this transparency for blood decals on switches.", CVAR_Archive|CVAR_NoShadow);
42 static VCvarF r_decal_switch_boot_alpha("r_decal_switch_boot_alpha", "0.36", "Force this transparency for boot decals on switches.", CVAR_Archive|CVAR_NoShadow);
43 static VCvarF r_decal_switch_other_alpha("r_decal_switch_other_alpha", "0.56", "Force this transparency for decals on switches.", CVAR_Archive|CVAR_NoShadow);
45 // make renderer life somewhat easier by not allowing a lot of decals
46 // main work is done by `VLevel->CleanupSegDecals()`
47 VCvarI gl_bigdecal_limit("gl_bigdecal_limit", "16", "Limit for big decals on one seg (usually produced by gore mod).", /*CVAR_PreInit|*/CVAR_Archive|CVAR_NoShadow);
48 VCvarI gl_smalldecal_limit("gl_smalldecal_limit", "64", "Limit for small decals on one seg (usually produced by shots).", /*CVAR_PreInit|*/CVAR_Archive|CVAR_NoShadow);
50 VCvarI gl_flatdecal_limit("gl_flatdecal_limit", "16", "Limit for overlapping decals on floor/ceiling.", /*CVAR_PreInit|*/CVAR_Archive|CVAR_NoShadow);
53 TMapNC<VName, bool> VLevel::baddecals;
54 TArrayNC<int> VLevel::dcLineTouchMark;
55 TArrayNC<int> VLevel::dcSegTouchMark;
56 int VLevel::dcLineTouchCounter = 0;
59 // sorry for this static
60 // it is used in decal cleanup code
61 static TArrayNC<decal_t *> dc2kill;
64 //==========================================================================
66 // isGoreEnabled
68 //==========================================================================
69 static VVA_FORCEINLINE bool isGoreEnabled () {
70 const int ovd = k8gore_enabled_override_decal.asInt();
71 if (ovd < 0) return false;
72 if (ovd > 0) return true;
73 const int ov = k8gore_enabled_override.asInt();
74 if (ov < 0) return false;
75 if (ov > 0) return true;
76 return k8gore_enabled.asBool();
80 //==========================================================================
82 // decal_t::calculateBBox
84 //==========================================================================
85 void decal_t::calculateBBox () noexcept {
86 return (void)CalculateTextureBBox(bbox2d, texture.id, worldx, worldy, angle, scaleX, scaleY);
90 //==========================================================================
92 // VLevel::CalcDecalAlpha
94 //==========================================================================
95 float VLevel::CalcDecalAlpha (const VDecalDef *dec, const float alpha) noexcept {
96 vassert(dec);
97 float dcalpha = dec->alpha.value;
98 if (alpha >= 0.0f) {
99 //GCon->Logf(NAME_Debug, "decal '%s': orig alpha=%g (forced alpha=%g)", *dec->name, dcalpha, alpha);
100 if (alpha < 1000.0f) dcalpha = alpha; else dcalpha *= alpha-1000.0f;
101 //GCon->Logf(NAME_Debug, "decal '%s': final alpha=%g", *dec->name, dcalpha);
103 if (dcalpha < 0.004f) return 0.0f;
104 return min2(1.0f, dcalpha);
108 //==========================================================================
110 // VLevel::CalcSwitchDecalAlpha
112 // switches should be more visible, so make decals almost transparent
114 //==========================================================================
115 float VLevel::CalcSwitchDecalAlpha (const VDecalDef *dec, const float ovralpha) {
116 vassert(dec);
117 const float alpha = CalcDecalAlpha(dec, ovralpha);
118 if (!r_decal_switch_special.asBool() || alpha <= 0.004f) return alpha;
119 // blood?
120 if (dec->bloodSplat) return clampval(min2(alpha, r_decal_switch_blood_alpha.asFloat()), 0.0f, 1.0f);
121 if (dec->bootPrint) return clampval(min2(alpha, r_decal_switch_boot_alpha.asFloat()), 0.0f, 1.0f);
122 // others
123 return clampval(min2(alpha, r_decal_switch_other_alpha.asFloat()), 0.0f, 1.0f);
127 //==========================================================================
129 // VLevel::IncLineTouchCounter
131 //==========================================================================
132 void VLevel::IncLineTouchCounter () noexcept {
133 if (dcLineTouchMark.length() == NumLines && dcSegTouchMark.length() == NumSegs) {
134 if (++dcLineTouchCounter != MAX_VINT32) return;
135 } else {
136 dcLineTouchMark.setLength(NumLines);
137 dcSegTouchMark.setLength(NumSegs);
139 for (auto &&v : dcLineTouchMark) v = 0;
140 for (auto &&v : dcSegTouchMark) v = 0;
141 dcLineTouchCounter = 1;
145 //==========================================================================
147 // VLevel::IncSubTouchCounter
149 //==========================================================================
150 void VLevel::IncSubTouchCounter () noexcept {
151 dcPobjTouchedReset = false;
152 if (dcSubTouchMark.length() == NumSubsectors) {
153 if (++dcSubTouchCounter != MAX_VINT32) return;
154 } else {
155 dcSubTouchMark.setLength(NumSubsectors);
157 for (auto &&v : dcSubTouchMark) v = 0;
158 dcSubTouchCounter = 1;
162 //==========================================================================
164 // VLevel::AddAnimatedDecal
166 //==========================================================================
167 void VLevel::AddAnimatedDecal (decal_t *dc) {
168 if (!dc || !dc->animator) return;
169 vassert(!dc->prevanimated);
170 vassert(!dc->nextanimated);
171 vassert(decanimlist != dc);
172 if (decanimlist) decanimlist->prevanimated = dc;
173 dc->nextanimated = decanimlist;
174 decanimlist = dc;
178 //==========================================================================
180 // VLevel::RemoveDecalAnimator
182 // this will also delete animator
183 // safe to call on decals without an animator
185 //==========================================================================
186 void VLevel::RemoveDecalAnimator (decal_t *dc) {
187 if (!dc || !dc->animator) return;
188 vassert(dc->prevanimated || dc->nextanimated || decanimlist == dc);
189 if (dc->prevanimated) dc->prevanimated->nextanimated = dc->nextanimated; else decanimlist = dc->nextanimated;
190 if (dc->nextanimated) dc->nextanimated->prevanimated = dc->prevanimated;
191 delete dc->animator;
192 dc->animator = nullptr;
193 dc->prevanimated = dc->nextanimated = nullptr;
197 //==========================================================================
199 // VLevel::AllocSegDecal
201 //==========================================================================
202 decal_t *VLevel::AllocSegDecal (seg_t *seg, VDecalDef *dec, float alpha, VDecalAnim *animator) {
203 vassert(seg);
204 vassert(dec);
205 //alpha = clampval((alpha >= 0.0f ? alpha : dec->alpha.value), 0.0f, 1.0f);
206 alpha = CalcDecalAlpha(dec, alpha);
207 decal_t *decal = new decal_t;
208 memset((void *)decal, 0, sizeof(decal_t));
209 //decal->dectype = dec->name;
210 decal->proto = dec;
211 decal->texture = dec->texid;
212 decal->shadeclr = decal->origshadeclr = dec->shadeclr;
213 //decal->translation = translation;
214 //decal->orgz = decal->curz = orgz;
215 //decal->xdist = lineofs;
216 decal->angle = AngleMod(dec->angleWall.value);
217 decal->ofsX = decal->ofsY = 0.0f;
218 decal->scaleX = decal->origScaleX = dec->scaleX.value;
219 decal->scaleY = decal->origScaleY = dec->scaleY.value;
220 decal->alpha = decal->origAlpha = alpha;
221 decal->flags =
222 (dec->fullbright ? decal_t::Fullbright : decal_t::FlagNothingZero)|
223 (dec->fuzzy ? decal_t::Fuzzy : decal_t::FlagNothingZero)|
224 (dec->bloodSplat ? decal_t::BloodSplat : decal_t::FlagNothingZero)|
225 (dec->bootPrint ? decal_t::BootPrint : decal_t::FlagNothingZero)|
226 (dec->additive ? decal_t::Additive : decal_t::FlagNothingZero);
228 decal->boottime = 0.0f; //dec->boottime.value;
229 decal->bootanimator = NAME_None; //dec->bootanimator;
230 decal->bootshade = -2;
231 decal->boottranslation = -1;
232 decal->bootalpha = -1.0f;
234 decal->animator = (animator ? animator : dec->animator);
235 //if (decal->animator) GCon->Logf(NAME_Debug, "setting animator for '%s': <%s> (%s : %d)", *dec->name, *decal->animator->name, decal->animator->getTypeName(), (int)decal->animator->isEmpty());
236 if (decal->animator && decal->animator->isEmpty()) decal->animator = nullptr;
237 if (decal->animator) decal->animator = decal->animator->clone();
238 //if (decal->animator) GCon->Logf(NAME_Debug, "set animator for '%s': <%s> (%s : %d)", *dec->name, *decal->animator->name, decal->animator->getTypeName(), (int)decal->animator->isEmpty());
239 seg->appendDecal(decal);
240 if (decal->animator) AddAnimatedDecal(decal);
242 return decal;
246 //==========================================================================
248 // decalAreaCompare
250 //==========================================================================
251 static int decalAreaCompare (const void *aa, const void *bb, void * /*udata*/) {
252 if (aa == bb) return 0;
253 const decal_t *adc = *(const decal_t **)aa;
254 const decal_t *bdc = *(const decal_t **)bb;
255 const float areaA = BBox2DArea(adc->bbox2d);
256 const float areaB = BBox2DArea(bdc->bbox2d);
257 // if one decal is more than two times smaller than the other, prefer smaller decal
258 if (areaA <= areaB*0.5f) return -1;
259 if (areaB <= areaA*0.5f) return +1;
260 // if the size is roughly the same, prefer animated
261 if (!!adc->animator != !!bdc->animator) {
262 const float prcsz = min2(areaA, areaB)/max2(areaA, areaB);
263 if (prcsz >= 0.88f) return (adc->animator ? -1 : +1);
265 // prefer decal that will disappear anyway
266 if (adc->animator || bdc->animator) {
267 const bool aWillDie = (adc->animator ? adc->animator->calcWillDisappear() : false);
268 const bool bWillDie = (bdc->animator ? bdc->animator->calcWillDisappear() : false);
269 if (aWillDie != bWillDie) return (aWillDie ? -1 : +1);
271 // normal area comparison
272 const float adiff = areaA-areaB;
273 if (adiff < 0.0f) return -1;
274 if (adiff > 0.0f) return +1;
275 return 0;
279 //==========================================================================
281 // VLevel::CleanupSegDecals
283 // permament decals are simply ignored
285 //==========================================================================
286 void VLevel::CleanupSegDecals (seg_t *seg) {
287 if (!seg) return;
288 const int bigLimit = gl_bigdecal_limit.asInt();
289 const int smallLimit = gl_smalldecal_limit.asInt();
290 if (bigLimit < 1 && smallLimit < 1) return; // nothing to do
292 dc2kill.resetNoDtor();
294 // count decals
295 int bigDecalCount = 0, smallDecalCount = 0;
296 decal_t *dc = seg->decalhead;
297 while (dc) {
298 decal_t *cdc = dc;
299 dc = dc->next;
301 int dcTexId = cdc->texture;
302 auto dtex = GTextureManager[dcTexId];
303 if (!dtex || dtex->Width < 1 || dtex->Height < 1) {
304 // remove this decal (just in case)
305 DestroyDecal(cdc);
306 continue;
309 const int twdt = (int)(dtex->GetScaledWidthF()*cdc->scaleX);
310 const int thgt = (int)(dtex->GetScaledHeightF()*cdc->scaleY);
312 if (!cdc->animator && (twdt < 1 || thgt < 1 || cdc->alpha < 0.004f)) {
313 // remove this decal (just in case)
314 DestroyDecal(cdc);
315 continue;
318 if (cdc->isPermanent()) continue;
320 if (twdt >= BigDecalWidth || thgt >= BigDecalHeight) ++bigDecalCount; else ++smallDecalCount;
321 dc2kill.append(cdc);
324 int toKillBig = (bigLimit > 0 ? bigDecalCount-bigLimit : 0);
325 int toKillSmall = (smallLimit > 0 ? smallDecalCount-smallLimit : 0);
326 if (toKillBig <= 0 && toKillSmall <= 0) return;
328 if (toKillBig < 0) toKillBig = 0;
329 if (toKillSmall < 0) toKillSmall = 0;
331 // hack: if we have to remove big decals, limit small decals too
332 if (toKillBig > 0 && smallDecalCount > bigLimit) {
333 int biglim = bigLimit+bigLimit/4;
334 //if (biglim < 1) biglim = 1;
335 int tks = smallDecalCount-biglim;
336 if (tks > toKillSmall) {
337 //GCon->Logf(NAME_Debug, "force-kill %d small decals instead of %d", tks, toKillSmall);
338 toKillSmall = tks;
342 //if (toKillBig) GCon->Logf(NAME_Debug, "have to kill %d big decals...", toKillBig);
343 //if (toKillSmall) GCon->Logf(NAME_Debug, "have to kill %d small decals...", toKillSmall);
345 // prefer smaller and animated decals
346 int dcCount = toKillBig+toKillSmall;
348 // sort by preference
349 smsort_r(dc2kill.ptr(), dc2kill.length(), sizeof(decal_t *), &decalAreaCompare, nullptr);
350 for (decal_t *dcdie : dc2kill) {
351 if (dcCount) {
352 --dcCount;
353 //GCon->Logf(NAME_Debug, "killing wall %sdecal '%s' with area %g", (dcdie->animator ? "animated " : ""), *GTextureManager[dcdie->texture]->Name, BBox2DArea(dcdie->bbox2d));
354 DestroyDecal(dcdie);
360 //==========================================================================
362 // VLevel::DestroyFlatDecal
364 // this will also destroy decal and its animator!
366 //==========================================================================
367 void VLevel::DestroyFlatDecal (decal_t *dc) {
368 vassert(dc);
369 vassert(dc->isFloor() || dc->isCeiling());
370 vassert(dc->sub);
371 vassert(NumSubsectors > 0);
372 vassert(subsectorDecalList);
373 // remove from renderer
374 #ifdef CLIENT
375 if (dc->sreg && Renderer) Renderer->RemoveFlatDecal(dc);
376 #endif
377 const int sidx = (int)(ptrdiff_t)(dc->sub-&Subsectors[0]);
378 if (sidx < 0 || sidx >= NumSubsectors) return;
379 // remove from subsector list
380 VDecalList *lst = &subsectorDecalList[sidx];
381 DLListRemoveEx(dc, lst->head, lst->tail, subprev, subnext);
382 // remove from global list
383 DLListRemove(dc, subdecalhead, subdecaltail);
384 // remove animator
385 RemoveDecalAnimator(dc);
386 // and kill decal
387 delete dc;
391 //==========================================================================
393 // VLevel::KillAllSubsectorDecals
395 //==========================================================================
396 void VLevel::KillAllSubsectorDecals () {
397 while (subdecalhead) DestroyFlatDecal(subdecalhead);
398 vassert(!subdecalhead);
399 vassert(!subdecaltail);
400 if (subsectorDecalList) {
401 delete[] subsectorDecalList;
402 subsectorDecalList = nullptr;
407 //==========================================================================
409 // VLevel::AppendDecalToSubsectorList
411 // permament decals are simply ignored by the limiter
413 //==========================================================================
414 void VLevel::AppendDecalToSubsectorList (decal_t *dc) {
415 if (!dc) return;
417 if (dc->animator) AddAnimatedDecal(dc);
418 dc->calculateBBox();
420 if (NumSubsectors == 0/*just in case*/) {
421 RemoveDecalAnimator(dc);
422 delete dc;
423 return;
426 vassert(dc->sub);
427 vassert(dc->isFloor() || dc->isCeiling());
428 vassert(!dc->prev);
429 vassert(!dc->next);
430 vassert(!dc->subprev);
431 vassert(!dc->subnext);
432 vassert(!dc->sregprev);
433 vassert(!dc->sregnext);
434 vassert(!dc->seg);
435 vassert(!dc->sreg);
436 vassert(!dc->slidesec);
437 vassert(dc->eregindex >= 0);
439 // append to global sector decal list
440 DLListAppend(dc, subdecalhead, subdecaltail);
442 // append to list of decals in the given sector
443 if (!subsectorDecalList) {
444 subsectorDecalList = new VDecalList[NumSubsectors];
445 for (int f = 0; f < NumSubsectors; ++f) subsectorDecalList[f].head = subsectorDecalList[f].tail = nullptr;
448 VDecalList *lst = &subsectorDecalList[(unsigned)(ptrdiff_t)(dc->sub-&Subsectors[0])];
449 DLListAppendEx(dc, lst->head, lst->tail, subprev, subnext);
451 #ifdef CLIENT
452 if (Renderer) Renderer->AppendFlatDecal(dc);
453 #endif
455 // check subsector decals limit
456 const int dclimit = gl_flatdecal_limit.asInt();
457 if (dclimit > 0 && !dc->isPermanent()) {
458 // prefer decals with animators (they are usually transient blood or something like that)
459 dc2kill.resetNoDtor();
460 constexpr float shrinkRatio = 0.8f;
461 float mybbox[4];
462 ShrinkBBox2D(mybbox, dc->bbox2d, shrinkRatio);
463 float curbbox[4];
464 int dcCount = 0;
465 decal_t *prdc = lst->head;
466 while (prdc != dc) {
467 decal_t *curdc = prdc;
468 prdc = prdc->subnext;
469 if (curdc->eregindex != dc->eregindex) continue;
470 if (curdc->dcsurf != dc->dcsurf) continue;
471 if (curdc->isPermanent()) continue;
472 ShrinkBBox2D(curbbox, curdc->bbox2d, shrinkRatio);
473 if (Are2DBBoxesOverlap(mybbox, curbbox)) {
474 ++dcCount;
475 dc2kill.append(curdc);
478 // do we need to remove some decals?
479 if (dcCount > dclimit) {
480 // prefer smallest decals
481 //GCon->Logf(NAME_Debug, "%d flat decals to remove (%d total)...", dcCount-dclimit, dcCount);
482 dcCount -= dclimit; // decals left to remove
483 // sort by preference
484 smsort_r(dc2kill.ptr(), dc2kill.length(), sizeof(decal_t *), &decalAreaCompare, nullptr);
485 for (decal_t *dcdie : dc2kill) {
486 if (dcCount) {
487 --dcCount;
488 //GCon->Logf(NAME_Debug, "killing flat %sdecal '%s' with area %g", (dcdie->animator ? "animated " : ""), *GTextureManager[dcdie->texture]->Name, BBox2DArea(dcdie->bbox2d));
489 DestroyFlatDecal(dcdie);
497 //==========================================================================
499 // VLevel::NewFlatDecal
501 //==========================================================================
502 void VLevel::NewFlatDecal (bool asFloor, subsector_t *sub, const int eregidx, const float wx, const float wy,
503 VDecalDef *dec, const DecalParams &params)
505 vassert(sub);
506 vassert(eregidx >= 0);
507 vassert(dec);
509 const float dcalpha = CalcDecalAlpha(dec, params.alpha);
511 decal_t *decal = new decal_t;
512 memset((void *)decal, 0, sizeof(decal_t));
513 //decal->dectype = dec->name;
514 decal->proto = dec;
515 decal->texture = dec->texid;
516 decal->translation = params.translation;
517 decal->shadeclr = decal->origshadeclr = (params.shadeclr != -2 ? params.shadeclr : dec->shadeclr);
518 decal->slidesec = nullptr;
519 decal->sub = sub;
520 decal->eregindex = eregidx;
521 decal->dcsurf = (asFloor ? decal_t::Floor : decal_t::Ceiling);
522 decal->worldx = wx;
523 decal->worldy = wy;
524 decal->angle = AngleMod(params.angle);
525 //decal->orgz = org.z; // doesn't matter
526 //!decal->height = height;
527 //decal->curz = 0.0f; // doesn't matter
528 //decal->xdist = 0.0f; // doesn't matter
529 //decal->ofsX = decal->ofsY = 0.0f;
530 decal->scaleX = decal->origScaleX = dec->scaleX.value;
531 decal->scaleY = decal->origScaleY = dec->scaleY.value;
532 decal->alpha = decal->origAlpha = dcalpha;
533 decal->flags =
534 params.orflags|
535 (dec->fullbright ? decal_t::Fullbright : decal_t::FlagNothingZero)|
536 (dec->fuzzy ? decal_t::Fuzzy : decal_t::FlagNothingZero)|
537 (dec->bloodSplat ? decal_t::BloodSplat : decal_t::FlagNothingZero)|
538 (dec->bootPrint ? decal_t::BootPrint : decal_t::FlagNothingZero)|
539 (dec->additive ? decal_t::Additive : decal_t::FlagNothingZero);
541 decal->boottime = dec->boottime.value;
542 decal->bootanimator = dec->bootanimator;
543 decal->bootshade = dec->bootshade;
544 decal->boottranslation = dec->boottranslation;
545 decal->bootalpha = dec->bootalpha;
547 decal->animator = (params.animator ? params.animator : dec->animator);
548 //if (decal->animator) GCon->Logf(NAME_Debug, "anim: %s(%s) (%d)", *decal->animator->name, decal->animator->getTypeName(), (int)decal->animator->isEmpty());
549 if (decal->animator && decal->animator->isEmpty()) decal->animator = nullptr;
550 //decal->animator = nullptr;
551 if (decal->animator) decal->animator = decal->animator->clone();
553 AppendDecalToSubsectorList(decal);
557 //==========================================================================
559 // VLevel::DestroyDecal
561 // this will also destroy decal and its animator!
563 //==========================================================================
564 void VLevel::DestroyDecal (decal_t *dc) {
565 if (!dc) return;
566 if (dc->isWall()) {
567 RemoveDecalAnimator(dc);
568 dc->seg->removeDecal(dc);
569 delete dc;
570 } else {
571 return DestroyFlatDecal(dc);
576 //==========================================================================
578 // VLevel::AddDecal
580 //==========================================================================
581 void VLevel::AddDecal (TVec org, VName dectype, int side, line_t *li, int level, DecalParams &params) {
582 if (!r_decals || !r_decals_wall) return;
583 if (!li || dectype == NAME_None || VStr::strEquCI(*dectype, "none")) return; // just in case
585 // replace blood decals
586 if (canReplaceBlood && isGoreEnabled()) {
587 if (VStr::strEquCI(*dectype, "BloodSplat")) dectype = goreBloodDecalSplat;
588 else if (VStr::strEquCI(*dectype, "BloodSmear")) dectype = goreBloodDecalSmear;
589 if (VStr::strEquCI(*dectype, "BloodSplatRadius")) dectype = goreBloodDecalSplatRadius;
590 else if (VStr::strEquCI(*dectype, "BloodSmearRadius")) dectype = goreBloodDecalSmearRadius;
593 //GCon->Logf(NAME_Debug, "%s: oorg:(%g,%g,%g); org:(%g,%g,%g); trans=%d", *dectype, org.x, org.y, org.z, li->landAlongNormal(org).x, li->landAlongNormal(org).y, li->landAlongNormal(org).z, translation);
595 VDecalDef *dec = VDecalDef::getDecal(dectype);
596 //if (dec->animator) GCon->Logf(NAME_Debug, " animator: <%s> (%s : %d)", *dec->animator->name, dec->animator->getTypeName(), (int)dec->animator->isEmpty());
597 //if (animator) GCon->Logf(NAME_Debug, " forced animator: <%s> (%s : %d)", *animator->name, animator->getTypeName(), (int)animator->isEmpty());
598 if (dec) {
599 org = li->landAlongNormal(org);
600 //GCon->Logf(NAME_Debug, "DECAL '%s'; name is '%s', texid is %d; org=(%g,%g,%g)", *dectype, *dec->name, dec->texid, org.x, org.y, org.z);
601 AddOneDecal(level, org, dec, side, li, params);
602 } else {
603 if (!baddecals.put(dectype, true)) GCon->Logf(NAME_Warning, "NO DECAL: '%s'", *dectype);
608 //==========================================================================
610 // VLevel::AddDecalById
612 //==========================================================================
613 void VLevel::AddDecalById (TVec org, int id, int side, line_t *li, int level, DecalParams &params) {
614 if (!r_decals || !r_decals_wall) return;
615 if (!li || id < 0) return; // just in case
616 VDecalDef *dec = VDecalDef::getDecalById(id);
617 if (dec) {
618 org = li->landAlongNormal(org);
619 params.forcePermanent = true; // always
620 AddOneDecal(level, org, dec, side, li, params);
625 //==========================================================================
627 // VLevel::AddFlatDecal
629 // z coord matters!
630 // `height` is from `org.z`
631 // zero height means "take from decal texture"
633 //==========================================================================
634 void VLevel::AddFlatDecal (TVec org, VName dectype, float range, DecalParams &params) {
635 if (!r_decals || !r_decals_flat) return;
636 if (dectype == NAME_None || VStr::strEquCI(*dectype, "none")) return; // just in case
638 VDecalDef *dec = VDecalDef::getDecal(dectype);
639 if (!dec) {
640 if (!baddecals.put(dectype, true)) GCon->Logf(NAME_Warning, "NO DECAL: '%s'", *dectype);
641 return;
644 range = max2(2.0f, fabsf(range));
645 SpreadFlatDecalEx(org, range, dec, 0, params);
650 //**************************************************************************
652 // VavoomC API
654 //**************************************************************************
656 //native final void AddDecal (TVec org, name dectype, int side, line_t *li, optional int translation,
657 // optional int shadeclr, optional float alpha, optional name animator,
658 // optional bool permanent, optional float angle, optional bool forceFlipX);
659 IMPLEMENT_FUNCTION(VLevel, AddDecal) {
660 TVec org;
661 VName dectype;
662 int side;
663 line_t *li;
664 VOptParamInt translation(0);
665 VOptParamInt shadeclr(-2);
666 VOptParamFloat alpha(-2.0f);
667 VOptParamName animator(NAME_None);
668 VOptParamBool permanent(false);
669 VOptParamFloat angle(INFINITY);
670 VOptParamBool forceFlipX(false);
671 vobjGetParamSelf(org, dectype, side, li, translation, shadeclr, alpha, animator, permanent, angle, forceFlipX);
672 //if (!angle.specified) angle.value = INFINITY;
673 DecalParams params;
674 params.translation = translation.value;
675 params.shadeclr = shadeclr.value;
676 params.alpha = alpha.value;
677 params.animator = VDecalAnim::GetAnimatorByName(animator.value);
678 params.angle = angle.value;
679 params.forceFlipX = forceFlipX.value;
680 params.forcePermanent = permanent.value;
681 Self->AddDecal(org, dectype, side, li, 0, params);
684 //native final void AddDecalById (TVec org, int id, int side, line_t *li, optional int translation,
685 // optional int shadeclr, optional float alpha, optional name animator);
686 IMPLEMENT_FUNCTION(VLevel, AddDecalById) {
687 TVec org;
688 int id;
689 int side;
690 line_t *li;
691 VOptParamInt translation(0);
692 VOptParamInt shadeclr(-2);
693 VOptParamFloat alpha(-2.0f);
694 VOptParamName animator(NAME_None);
695 vobjGetParamSelf(org, id, side, li, translation, shadeclr, alpha, animator);
696 DecalParams params;
697 params.translation = translation.value;
698 params.shadeclr = shadeclr.value;
699 params.alpha = alpha.value;
700 params.animator = VDecalAnim::GetAnimatorByName(animator.value);
701 params.angle = INFINITY;
702 params.forceFlipX = false;
703 params.forcePermanent = true;
704 Self->AddDecalById(org, id, side, li, 0, params);
708 //native final void AddFlatDecal (TVec org, name dectype, float range, optional int translation, optional int shadeclr, optional float alpha,
709 // optional name animator, optional float angle, optional bool forceFlipX, optional bool permanent);
710 IMPLEMENT_FUNCTION(VLevel, AddFlatDecal) {
711 TVec org;
712 VName dectype;
713 float range;
714 VOptParamInt translation(0);
715 VOptParamInt shadeclr(-2);
716 VOptParamFloat alpha(-2.0f);
717 VOptParamName animator(NAME_None);
718 VOptParamFloat angle(INFINITY);
719 VOptParamBool forceFlipX(false);
720 VOptParamBool permanent(false);
721 vobjGetParamSelf(org, dectype, range, translation, shadeclr, alpha, animator, angle, forceFlipX, permanent);
722 //if (!angle.specified) angle = INFINITY;
723 DecalParams params;
724 params.translation = translation.value;
725 params.shadeclr = shadeclr.value;
726 params.alpha = alpha.value;
727 params.animator = VDecalAnim::GetAnimatorByName(animator.value);
728 params.angle = angle.value;
729 params.forceFlipX = forceFlipX.value;
730 params.forcePermanent = permanent.value;
731 Self->AddFlatDecal(org, dectype, range, params);
735 // check what kind of bootprint decal is required at `org`
736 // returns `false` if none (params are undefined)
737 // native /*final*/ bool CheckBootPrints (TVec org, subsector_t *sub, out VBootPrintDecalParams params);
738 IMPLEMENT_FUNCTION(VLevel, CheckBootPrints) {
739 TVec org;
740 subsector_t *sub;
741 VBootPrintDecalParams *params;
742 vobjGetParamSelf(org, sub, params);
743 RET_BOOL(Self->CheckBootPrints(org, sub, *params));
747 // native /*final*/ void CheckFloorDecalDamage (bool isPlayer, TVec org, subsector_t *sub, void delegate (int damage, name damageType) dg);
748 IMPLEMENT_FUNCTION(VLevel, CheckFloorDecalDamage) {
749 bool isPlayer;
750 TVec org;
751 subsector_t *sub;
752 VObject *dgSelf;
753 VMethod *dgFunc;
754 vobjGetParamSelf(isPlayer, org, sub, dgSelf, dgFunc);
755 if (dgFunc) Self->CheckFloorDecalDamage(isPlayer, org, sub, dgSelf, dgFunc);
759 COMMAND(RemoveAllDecals) {
760 VLevel *lvl = (GLevel ? GLevel : GClLevel);
761 if (!lvl) {
762 GCon->Log(NAME_Error, "no level loaded");
763 return;
765 lvl->KillAllMapDecals();
766 GCon->Log("removed all decals");