libvwad: updated -- vwadwrite: free file buffers on close (otherwise archive creation...
[k8vavoom.git] / source / psim / p_decal.h
blob3c4d6e4b824c30a0db22e7db3e5c90158ddd07b2
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 2018-2023 Ketmar Dark
11 //**
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.
15 //**
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.
20 //**
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/>.
23 //**
24 //**************************************************************************
25 // decal, decalgroup, decal animator
26 #ifndef VAVOOM_PSIM_DECAL_HEADER
27 #define VAVOOM_PSIM_DECAL_HEADER
30 // ////////////////////////////////////////////////////////////////////////// //
31 class VDecalDef;
32 class VDecalAnim;
33 class VDecalGroup;
36 // ////////////////////////////////////////////////////////////////////////// //
37 struct DecalFloatVal {
38 public:
39 enum Type {
40 T_Fixed,
41 T_Random,
42 T_Undefined,
45 public:
46 float value;
47 int type;
48 float rndMin, rndMax;
50 public:
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 // ////////////////////////////////////////////////////////////////////////// //
70 class VDecalAnim;
71 struct VDecalCloneParams {
72 DecalFloatVal scaleX, scaleY;
73 DecalFloatVal alpha;
74 VDecalAnim *animator;
75 int shadeclr;
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
87 class VDecalDef {
88 public:
89 enum {
90 FlipNone = 0,
91 FlipAlways = 1,
92 FlipRandom = 2,
95 private:
96 static VDecalDef *listHead;
97 static TMapNC<VName, VDecalDef *> decalNameMap; // all names are lowercased
99 VDecalDef *next; // in decalDefHead
100 VName animname;
101 bool animoptional;
103 private:
104 static void addToList (VDecalDef *dc) noexcept;
105 static void removeFromList (VDecalDef *dc, bool deleteIt=false) noexcept;
107 void fixup ();
109 public:
110 // name is not parsed yet
111 bool parse (VScriptParser *sc);
113 public:
114 // decaldef properties
115 VName name;
116 //VName pic;
117 int texid;
118 int id;
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`)
124 bool additive;
125 bool fuzzy; // draw decal with "fuzzy" effect (not supported yet)
126 bool fullbright;
127 bool noWall, noFlat;
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
133 VName lowername;
134 VName bootprintname;
135 VDecalAnim *animator; // decal animator (can be nullptr)
136 // the following values will be inited by `genValues()`
137 DecalFloatVal boottime;
138 VName bootanimator;
139 int bootshade, boottranslation;
140 float bootalpha;
141 VTerrainBootprint *bootprint;
142 bool hasFloorDamage;
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
151 protected:
152 bool useCommonScale;
153 DecalFloatVal commonScale;
155 enum {
156 Scale_No_Special,
157 ScaleX_Is_Y_Multiply,
158 ScaleY_Is_X_Multiply,
160 int scaleSpecial;
161 float scaleMultiply;
163 public:
164 // used in various decal spreading code
165 float bbox2d[4];
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]; }
171 public:
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)
179 , animator(nullptr)
180 , boottime(4.0f, 8.0f), bootanimator(NAME_None)
181 , bootshade(-2), boottranslation(-2), bootalpha(-1.0f)
182 , bootprint(nullptr)
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;
200 public:
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;
213 public:
214 static void parseNumOrRandom (VScriptParser *sc, DecalFloatVal *value, bool withSign=false);
216 private:
217 friend void ParseDecalDef (VScriptParser *sc);
218 friend void ProcessDecalDefs ();
219 friend class VDecalGroup;
223 // ////////////////////////////////////////////////////////////////////////// //
224 // will choose a random decal
225 class VDecalGroup {
226 private:
227 static VDecalGroup *listHead;
228 static TMapNC<VName, VDecalGroup *> decalNameMap; // all names are lowercased
230 VDecalGroup *next; // in decalDefHead
232 public:
233 struct NameListItem {
234 VName name;
235 vuint16 weight;
237 inline NameListItem () noexcept : name(NAME_None), weight(0) {}
238 inline NameListItem (VName aname, vuint16 aweight) noexcept : name(aname), weight(aweight) {}
241 struct ListItem {
242 VDecalDef *dd;
243 VDecalGroup *dg;
245 inline ListItem () noexcept : dd(nullptr), dg(nullptr) {}
246 inline ListItem (VDecalDef *add, VDecalGroup *adg) noexcept : dd(add), dg(adg) {}
249 private:
250 static void addToList (VDecalGroup *dg) noexcept;
251 static void removeFromList (VDecalGroup *dg, bool deleteIt=false) noexcept;
253 void fixup ();
255 public:
256 // name is not parsed yet
257 bool parse (VScriptParser *sc);
259 public:
260 // decaldef properties
261 VName name;
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`
266 public:
267 inline VDecalGroup () noexcept : next(nullptr), name(NAME_None), nameList(), list() {}
268 inline ~VDecalGroup () noexcept {}
270 VDecalDef *chooseDecal (int reclevel=0) noexcept;
272 public:
273 static VDecalGroup *find (const char *aname) noexcept;
274 static VDecalGroup *find (VStr aname) noexcept;
275 static VDecalGroup *find (VName aname) noexcept;
277 private:
278 friend void ParseDecalDef (VScriptParser *sc);
279 friend void ProcessDecalDefs ();
280 friend class VDecalDef;
284 // ////////////////////////////////////////////////////////////////////////// //
285 enum {
286 TDecAnimBase = 0,
287 TDecAnimFader = 1,
288 TDecAnimStretcher = 2,
289 TDecAnimSlider = 3,
290 TDecAnimColorChanger = 4,
291 TDecAnimCombiner = 5,
295 // ////////////////////////////////////////////////////////////////////////// //
296 // base decal animator class
297 class VDecalAnim {
298 public:
299 enum { TypeId = TDecAnimBase };
301 private:
302 VDecalAnim *next; // animDefHead
304 private:
305 static void addToList (VDecalAnim *anim) noexcept;
306 static void removeFromList (VDecalAnim *anim, bool deleteIt=false) noexcept;
308 protected:
309 bool empty;
310 // working data
311 float timePassed;
313 virtual void doIO (VStream &strm, VNTValueIOEx &vio) = 0;
314 virtual void fixup ();
316 public:
317 virtual bool parse (VScriptParser *sc) = 0;
319 public:
320 // decaldef properties
321 VName name;
323 protected:
324 inline void copyBaseFrom (const VDecalAnim *src) noexcept { empty = src->empty; timePassed = src->timePassed; name = src->name; }
326 public:
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);
362 public:
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;
370 private:
371 static VDecalAnim *listHead;
373 friend void ParseDecalDef (VScriptParser *sc);
374 friend void ProcessDecalDefs ();
378 // ////////////////////////////////////////////////////////////////////////// //
379 struct decal_t {
380 // flags bit values
381 enum {
382 FlagNothingZero = 0u,
384 SlideFloor = 0x00001U, // curz: offset with floorz, slide with it
385 SlideCeil = 0x00002U, // curz: offset with ceilingz, slide with it
386 FlipX = 0x00010U,
387 FlipY = 0x00020U,
388 Fullbright = 0x00100U,
389 Fuzzy = 0x00200U,
390 Additive = 0x00400U,
391 // sides
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
403 // dcsurf bit values
404 enum {
405 Wall = 0u,
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
409 FakeFlag = 4u,
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
427 VTextureID texture;
428 int translation;
429 int shadeclr; // -1: no shade
430 int origshadeclr; // for animators
431 unsigned flags;
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
446 // for flat 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
450 int flatDamageMin;
451 int flatDamageMax;
452 VName flatDamageType;
454 // in VLevel subsector decal list
455 decal_t *subprev;
456 decal_t *subnext;
457 // in renderer region decal list
458 decal_t *sregprev;
459 decal_t *sregnext;
462 unsigned uid; // unique id, so we can process all clones
463 // linked list of all decals with the given uid
464 decal_t *uidprev;
465 decal_t *uidnext;
468 // cache
469 unsigned lastFlags;
470 float lastScaleX, lastScaleY;
471 float lastWorldX, lastWorldY;
472 float lastAngle;
473 float lastOfsX, lastOfsY;
474 float lastCurZ;
475 float lastPlaneDist, lastTexZ;
476 // calculated cached values
477 TVec saxis, taxis;
478 float soffs, toffs;
479 TVec v1, v2, v3, v4;
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 {
506 return
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 {
522 lastFlags = flags;
523 lastPlaneDist = pdist;
524 lastTexZ = tz;
525 lastScaleX = scaleX;
526 lastScaleY = scaleY;
527 lastWorldX = worldx;
528 lastWorldY = worldy;
529 lastOfsX = ofsX;
530 lastOfsY = ofsY;
531 lastCurZ = curz;
532 lastAngle = angle;
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
554 #endif