1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 2018-2023 Ketmar Dark
12 //** This program is free software: you can redistribute it and/or modify
13 //** it under the terms of the GNU General Public License as published by
14 //** the Free Software Foundation, version 3 of the License ONLY.
16 //** This program is distributed in the hope that it will be useful,
17 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 //** GNU General Public License for more details.
21 //** You should have received a copy of the GNU General Public License
22 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**************************************************************************
25 // decal, decalgroup, decal animator
26 #ifndef VAVOOM_PSIM_DECAL_HEADER
27 #define VAVOOM_PSIM_DECAL_HEADER
30 // ////////////////////////////////////////////////////////////////////////// //
36 // ////////////////////////////////////////////////////////////////////////// //
37 struct DecalFloatVal
{
51 inline DecalFloatVal (ENoInit
) noexcept
{}
53 inline DecalFloatVal () noexcept
: value(0.0f
), type(T_Undefined
), rndMin(0.0f
), rndMax(0.0f
) {}
54 inline DecalFloatVal (const float aval
) noexcept
: value(aval
), type(T_Fixed
), rndMin(aval
), rndMax(aval
) {}
55 inline DecalFloatVal (const float amin
, const float amax
) noexcept
: value(amin
), type(T_Random
), rndMin(amin
), rndMax(amax
) {}
56 inline DecalFloatVal
clone () noexcept
;
58 void genValue (float defval
) noexcept
;
60 float getMinValue () const noexcept
;
61 float getMaxValue () const noexcept
;
63 void doIO (VStr prefix
, VStream
&strm
, VNTValueIOEx
&vio
);
65 VVA_CHECKRESULT
inline bool isDefined () const noexcept
{ return (type
!= T_Undefined
); }
69 // ////////////////////////////////////////////////////////////////////////// //
71 struct VDecalCloneParams
{
72 DecalFloatVal scaleX
, scaleY
;
76 bool hasScaleX
, hasScaleY
, hasAlpha
, hasAnimator
, hasShade
;
78 inline VDecalCloneParams () noexcept
79 : scaleX(1.0f
), scaleY(1.0f
), alpha(1.0f
), animator(nullptr), shadeclr(-1)
80 , hasScaleX(false), hasScaleY(false), hasAlpha(false), hasAnimator(false), hasShade(false)
85 // ////////////////////////////////////////////////////////////////////////// //
86 // linked list of all known decals
96 static VDecalDef
*listHead
;
97 static TMapNC
<VName
, VDecalDef
*> decalNameMap
; // all names are lowercased
99 VDecalDef
*next
; // in decalDefHead
104 static void addToList (VDecalDef
*dc
) noexcept
;
105 static void removeFromList (VDecalDef
*dc
, bool deleteIt
=false) noexcept
;
110 // name is not parsed yet
111 bool parse (VScriptParser
*sc
);
114 // decaldef properties
119 //float scaleX, scaleY;
120 DecalFloatVal scaleX
, scaleY
;
121 int flipX
, flipY
; // FlipXXX constant
122 DecalFloatVal alpha
; // decal alpha
123 //DecalFloatVal addAlpha; // alpha for additive translucency (if zero, use `alpha`)
125 bool fuzzy
; // draw decal with "fuzzy" effect (not supported yet)
128 bool bloodSplat
, bootPrint
;
129 bool flipXValue
, flipYValue
; // valid after `genValues()`
130 DecalFloatVal angleWall
;
131 DecalFloatVal angleFlat
;
132 int shadeclr
; // -1: no shade; >=0: shade rgb
135 VDecalAnim
*animator
; // decal animator (can be nullptr)
136 // the following values will be inited by `genValues()`
137 DecalFloatVal boottime
;
139 int bootshade
, boottranslation
;
141 VTerrainBootprint
*bootprint
;
143 DecalFloatVal floorDamageAlphaMul
;
144 DecalFloatVal floorDamagePlayer
; // default is true
145 DecalFloatVal floorDamageMonsters
; // default is false
146 DecalFloatVal floorDamageSuitLeak
; // default is 5
147 VName floorDamageType
;
148 DecalFloatVal floorDamage
;
149 DecalFloatVal floorDamageTick
; // damage each this tick
153 DecalFloatVal commonScale
;
157 ScaleX_Is_Y_Multiply
,
158 ScaleY_Is_X_Multiply
,
164 // used in various decal spreading code
166 float spheight
; // spreading height
168 inline float bbWidth () const noexcept
{ return bbox2d
[BOX2D_MAXX
]-bbox2d
[BOX2D_MINX
]; }
169 inline float bbHeight () const noexcept
{ return bbox2d
[BOX2D_MAXY
]-bbox2d
[BOX2D_MINY
]; }
172 inline VDecalDef () noexcept
173 : next(nullptr), animname(NAME_None
), animoptional(false), name(NAME_None
), texid(-1)/*pic(NAME_None)*/, id(-1)
174 , scaleX(1.0f
), scaleY(1.0f
), flipX(FlipNone
), flipY(FlipNone
), alpha(1.0f
)/*, addAlpha(0.0f)*/
175 , additive(false) , fuzzy(false), fullbright(false), noWall(false), noFlat(false), bloodSplat(false), bootPrint(false)
176 , flipXValue(false), flipYValue(false)
177 , angleWall(0.0f
), angleFlat(0.0f
, 360.0f
), shadeclr(-1)
178 , lowername(NAME_None
), bootprintname(NAME_None
)
180 , boottime(4.0f
, 8.0f
), bootanimator(NAME_None
)
181 , bootshade(-2), boottranslation(-2), bootalpha(-1.0f
)
183 , hasFloorDamage(false), floorDamageAlphaMul(0.0f
), floorDamagePlayer(1.0f
), floorDamageMonsters(0.0f
), floorDamageSuitLeak(5.0f
)
184 , floorDamageType(NAME_None
), floorDamage(0.0f
), floorDamageTick(0.0f
)
185 , useCommonScale(false), scaleSpecial(Scale_No_Special
), scaleMultiply(1.0f
)
187 ~VDecalDef () noexcept
;
189 void genValues () noexcept
;
191 // `genValues()` must be already called
193 // calculate decal bbox, sets spreading height
194 void CalculateBBox (const float worldx
, const float worldy
, const float angle
) noexcept
;
196 // used to generate maximum scale for animators
197 // must be called after attaching animator (obviously)
198 void genMaxScales (float *sx
, float *sy
) const noexcept
;
201 static VDecalDef
*find (const char *aname
) noexcept
;
202 static VDecalDef
*find (VStr aname
) noexcept
;
203 static VDecalDef
*find (VName aname
) noexcept
;
204 static VDecalDef
*findById (int id
) noexcept
;
206 static VDecalDef
*getDecal (const char *aname
) noexcept
;
207 static VDecalDef
*getDecal (VStr aname
) noexcept
;
208 static VDecalDef
*getDecal (VName aname
) noexcept
;
209 static VDecalDef
*getDecalById (int id
) noexcept
;
211 static bool hasDecal (VName aname
) noexcept
;
214 static void parseNumOrRandom (VScriptParser
*sc
, DecalFloatVal
*value
, bool withSign
=false);
217 friend void ParseDecalDef (VScriptParser
*sc
);
218 friend void ProcessDecalDefs ();
219 friend class VDecalGroup
;
223 // ////////////////////////////////////////////////////////////////////////// //
224 // will choose a random decal
227 static VDecalGroup
*listHead
;
228 static TMapNC
<VName
, VDecalGroup
*> decalNameMap
; // all names are lowercased
230 VDecalGroup
*next
; // in decalDefHead
233 struct NameListItem
{
237 inline NameListItem () noexcept
: name(NAME_None
), weight(0) {}
238 inline NameListItem (VName aname
, vuint16 aweight
) noexcept
: name(aname
), weight(aweight
) {}
245 inline ListItem () noexcept
: dd(nullptr), dg(nullptr) {}
246 inline ListItem (VDecalDef
*add
, VDecalGroup
*adg
) noexcept
: dd(add
), dg(adg
) {}
250 static void addToList (VDecalGroup
*dg
) noexcept
;
251 static void removeFromList (VDecalGroup
*dg
, bool deleteIt
=false) noexcept
;
256 // name is not parsed yet
257 bool parse (VScriptParser
*sc
);
260 // decaldef properties
262 TArray
<NameListItem
> nameList
; // can be empty in cloned/loaded object
263 //FIXME: it can refer another decal group
264 TWeightedList
</*VDecalDef*/ListItem
*> list
; // can contain less items than `nameList`
267 inline VDecalGroup () noexcept
: next(nullptr), name(NAME_None
), nameList(), list() {}
268 inline ~VDecalGroup () noexcept
{}
270 VDecalDef
*chooseDecal (int reclevel
=0) noexcept
;
273 static VDecalGroup
*find (const char *aname
) noexcept
;
274 static VDecalGroup
*find (VStr aname
) noexcept
;
275 static VDecalGroup
*find (VName aname
) noexcept
;
278 friend void ParseDecalDef (VScriptParser
*sc
);
279 friend void ProcessDecalDefs ();
280 friend class VDecalDef
;
284 // ////////////////////////////////////////////////////////////////////////// //
288 TDecAnimStretcher
= 2,
290 TDecAnimColorChanger
= 4,
291 TDecAnimCombiner
= 5,
295 // ////////////////////////////////////////////////////////////////////////// //
296 // base decal animator class
299 enum { TypeId
= TDecAnimBase
};
302 VDecalAnim
*next
; // animDefHead
305 static void addToList (VDecalAnim
*anim
) noexcept
;
306 static void removeFromList (VDecalAnim
*anim
, bool deleteIt
=false) noexcept
;
313 virtual void doIO (VStream
&strm
, VNTValueIOEx
&vio
) = 0;
314 virtual void fixup ();
317 virtual bool parse (VScriptParser
*sc
) = 0;
320 // decaldef properties
324 inline void copyBaseFrom (const VDecalAnim
*src
) noexcept
{ empty
= src
->empty
; timePassed
= src
->timePassed
; name
= src
->name
; }
327 inline VDecalAnim (ENoInit
) noexcept
: next(nullptr), empty(true), name(NAME_None
) {}
328 inline VDecalAnim () noexcept
: next(nullptr), empty(true), timePassed(0.0f
), name(NAME_None
) {}
329 virtual ~VDecalAnim ();
331 virtual vuint8
getTypeId () const noexcept
;
332 virtual const char *getTypeName () const noexcept
= 0;
334 inline bool isEmpty () const noexcept
{ return empty
; }
336 // this does deep clone, so we can attach it to the actual decal object
337 // by default, returns `nullptr` for empty animators
338 virtual VDecalAnim
*clone (bool forced
=false) = 0;
340 virtual void genValues () noexcept
= 0;
342 // return `false` to stop continue animation; set decal alpha to 0 (or negative) to remove decal on next cleanup
343 virtual bool animate (decal_t
*decal
, float timeDelta
) = 0;
345 virtual bool calcWillDisappear () const noexcept
;
346 virtual void calcMaxScales (float *sx
, float *sy
) const noexcept
;
348 virtual bool isFader () const noexcept
;
349 virtual bool isStretcher () const noexcept
;
350 virtual bool isSlider () const noexcept
;
351 virtual bool isColorChanger () const noexcept
;
352 virtual bool isCombiner () const noexcept
;
354 // for combiners, let it be here too
355 virtual int getCount () const noexcept
;
356 virtual VDecalAnim
*getAt (int idx
) noexcept
;
357 virtual bool hasTypeId (vuint8 tid
, int depth
=0) const noexcept
;
359 static void SerialiseNested (VStream
&strm
, VNTValueIOEx
&vio
, VDecalAnim
*&aptr
);
360 static void Serialise (VStream
&Strm
, VDecalAnim
*&aptr
);
363 static VDecalAnim
*find (const char *aname
) noexcept
;
364 static VDecalAnim
*find (VStr aname
) noexcept
;
365 static VDecalAnim
*find (VName aname
) noexcept
;
367 // used by decal spawners
368 static VDecalAnim
*GetAnimatorByName (VName animator
) noexcept
;
371 static VDecalAnim
*listHead
;
373 friend void ParseDecalDef (VScriptParser
*sc
);
374 friend void ProcessDecalDefs ();
378 // ////////////////////////////////////////////////////////////////////////// //
382 FlagNothingZero
= 0u,
384 SlideFloor
= 0x00001U
, // curz: offset with floorz, slide with it
385 SlideCeil
= 0x00002U
, // curz: offset with ceilingz, slide with it
388 Fullbright
= 0x00100U
,
392 SideDefOne
= 0x00800U
,
393 NoMidTex
= 0x01000U
, // don't render on middle texture
394 NoTopTex
= 0x02000U
, // don't render on top texture
395 NoBotTex
= 0x04000U
, // don't render on bottom texture
396 FromV2
= 0x08000U
, // use `v2` vertex as base (for wall decals on partner segs)
397 // special decal types
398 BloodSplat
= 0x10000U
,
399 BootPrint
= 0x20000U
,
400 Permanent
= 0x40000U
, // will not be removed by limiter
406 Floor
= 1u, // either base region floor, or 3d subregion floor
407 Ceiling
= 2u, // either base region ceiling, or 3d subregion ceiling
408 // note that `3u` is invalid
410 SurfTypeMask
= 0x03u
,
413 decal_t
*prev
; // in seg/sreg/slidesec
414 decal_t
*next
; // in seg/sreg/slidesec
415 seg_t
*seg
; // this is non-null for wall decals
416 subregion_t
*sreg
; // this is non-null for floor/ceiling decals (only set if renderer is active)
417 vuint32 dcsurf
; // decal type
418 sector_t
*slidesec
; // backsector for SlideXXX
419 subsector_t
*sub
; // owning subsector for floor/ceiling decal
420 int eregindex
; // sector region index for floor/ceiling decal (only in VLevel list)
421 VDecalDef
*proto
; // prototype for this decal; was needed to recreate textures
422 float boottime
; // how long it bootprints should be emited after stepping onto this? (copied from proto)
423 VName bootanimator
; // NAME_None means "don't change"; 'none' means "reset"
424 int bootshade
; // -2: use this decal shade
425 int boottranslation
; // <0: use this decal translation
426 float bootalpha
; // <0: use current decal alpha
429 int shadeclr
; // -1: no shade
430 int origshadeclr
; // for animators
432 // z and x positions has no image offset added
433 float worldx
, worldy
; // world coordinates for floor/ceiling decals
434 float angle
; // decal rotation angle (around its center point)
435 float orgz
; // original z position for wall decals
436 float curz
; // z position (offset with floor/ceiling TexZ if not midtex, see `flags`)
437 float xdist
; // offset from linedef start, in map units
438 float ofsX
, ofsY
; // for animators
439 float origScaleX
, origScaleY
; // for animators
440 float scaleX
, scaleY
; // actual
441 float origAlpha
; // for animators
442 float alpha
; // decal alpha
443 VDecalAnim
*animator
; // decal animator (can be nullptr)
444 decal_t
*prevanimated
; // so we can skip static decals
445 decal_t
*nextanimated
; // so we can skip static decals
447 float bbox2d
[4]; // 2d bounding box for the original (maximum) flat decal size
448 /* will take it from proto
449 int flatDamageTicks; // >0: do flat damage
452 VName flatDamageType;
454 // in VLevel subsector decal list
457 // in renderer region decal list
462 unsigned uid; // unique id, so we can process all clones
463 // linked list of all decals with the given uid
470 float lastScaleX
, lastScaleY
;
471 float lastWorldX
, lastWorldY
;
473 float lastOfsX
, lastOfsY
;
475 float lastPlaneDist
, lastTexZ
;
476 // calculated cached values
481 inline bool isAdditive () const noexcept
{ return (flags
&Additive
); }
482 inline bool isPermanent () const noexcept
{ return (flags
&Permanent
); }
484 // nore that floor/ceiling type should be correctly set for 3d floor subregions
485 // i.e. decal on top of 3d floor is ceiling decal
487 inline bool isWall () const noexcept
{ return (dcsurf
== Wall
); }
488 inline bool isNonWall () const noexcept
{ return ((dcsurf
&SurfTypeMask
) != Wall
); }
489 inline bool isFloor () const noexcept
{ return ((dcsurf
&SurfTypeMask
) == Floor
); }
490 inline bool isCeiling () const noexcept
{ return ((dcsurf
&SurfTypeMask
) == Ceiling
); }
491 // for fake floor/ceiling? (not implemented yet)
492 inline bool isFake () const noexcept
{ return (dcsurf
&FakeFlag
); }
494 inline bool isFloorBloodSplat () const noexcept
{ return (((dcsurf
&SurfTypeMask
) == Floor
) && (flags
&BloodSplat
)); }
496 // WARNING! call this ONLY for known non-wall decals!
497 // non-base regions has their floor and ceiling switched
498 // so floor decal for 3d floor region is actually ceiling decal, and vice versa
499 //inline bool isFloorEx () const noexcept { return (eregindex ? isCeiling() : isFloor()); }
501 // should be called after animator was set
502 // might be called by animation thinker too
503 void calculateBBox () noexcept
;
505 inline bool needRecalc (const float pdist
, const float tz
) const noexcept
{
507 lastFlags
!= flags
||
508 FASI(lastPlaneDist
) != FASI(pdist
) ||
509 FASI(lastTexZ
) != tz
||
510 FASI(lastScaleX
) != FASI(scaleX
) ||
511 FASI(lastScaleY
) != FASI(scaleY
) ||
512 FASI(lastWorldX
) != FASI(worldx
) ||
513 FASI(lastWorldY
) != FASI(worldy
) ||
514 FASI(lastOfsX
) != FASI(ofsX
) ||
515 FASI(lastOfsY
) != FASI(ofsY
) ||
516 FASI(lastCurZ
) != FASI(curz
) ||
517 FASI(lastAngle
) != FASI(angle
);
520 // doesn't update axes and offsets
521 inline void updateCache (const float pdist
, const float tz
) noexcept
{
523 lastPlaneDist
= pdist
;
535 inline void invalidateCache () noexcept
{ lastPlaneDist
= FLT_MAX
; } // arbitrary number
539 // ////////////////////////////////////////////////////////////////////////// //
540 // used to calculate decal bboxes
541 // returns vertical spread height
542 // zero means "no texture found or invalid arguments" (bbox2d is zeroed too)
543 float CalculateTextureBBox (float bbox2d
[4], int texid
,
544 const float worldx
, const float worldy
, const float angle
,
545 const float scaleX
, const float scaleY
) noexcept
;
547 void ParseDecalDef (VScriptParser
*sc
);
549 void ProcessDecalDefs ();
551 extern VDecalAnim
*DummyDecalAnimator
; // used to set empty decal animator