/ source / blood / src / sectorfx.cpp
sectorfx.cpp
  1  //-------------------------------------------------------------------------
  2  /*
  3  Copyright (C) 2010-2019 EDuke32 developers and contributors
  4  Copyright (C) 2019 Nuke.YKT
  5  
  6  This file is part of NBlood.
  7  
  8  NBlood is free software; you can redistribute it and/or
  9  modify it under the terms of the GNU General Public License version 2
 10  as published by the Free Software Foundation.
 11  
 12  This program is distributed in the hope that it will be useful,
 13  but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15  
 16  See the GNU General Public License for more details.
 17  
 18  You should have received a copy of the GNU General Public License
 19  along with this program; if not, write to the Free Software
 20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 21  */
 22  //-------------------------------------------------------------------------
 23  #include "compat.h"
 24  #include "build.h"
 25  #include "pragmas.h"
 26  #include "common_game.h"
 27  
 28  #include "blood.h"
 29  #include "db.h"
 30  #include "gameutil.h"
 31  #include "globals.h"
 32  #include "trig.h"
 33  #include "sectorfx.h"
 34  #include "view.h"
 35  
 36  char flicker1[] = {
 37      0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0,
 38      1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1,
 39      0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1,
 40      0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1
 41  };
 42  
 43  char flicker2[] = {
 44      1, 2, 4, 2, 3, 4, 3, 2, 0, 0, 1, 2, 4, 3, 2, 0,
 45      2, 1, 0, 1, 0, 2, 3, 4, 3, 2, 1, 1, 2, 0, 0, 1,
 46      1, 2, 3, 4, 4, 3, 2, 1, 2, 3, 4, 4, 2, 1, 0, 1,
 47      0, 0, 0, 0, 1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 3, 2
 48  };
 49  
 50  char flicker3[] = {
 51      4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 4, 2, 4, 3, 4, 4,
 52      4, 4, 2, 1, 3, 3, 3, 4, 3, 4, 4, 4, 4, 4, 2, 4,
 53      4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 1, 0, 1,
 54      0, 1, 0, 1, 0, 2, 3, 4, 4, 4, 4, 4, 4, 4, 3, 4
 55  };
 56  
 57  char flicker4[] = {
 58      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 59      4, 0, 0, 3, 0, 1, 0, 1, 0, 4, 4, 4, 4, 4, 2, 0,
 60      0, 0, 0, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1,
 61      0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 4, 3, 2,
 62      0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0, 0, 0 ,0, 0,
 63      0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0, 0, 0 ,0, 0,
 64      0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0, 0, 0 ,0, 0,
 65      0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0, 0, 0 ,0, 0
 66  };
 67  
 68  char strobe[] = {
 69      64, 64, 64, 48, 36, 27, 20, 15, 11, 9, 6, 5, 4, 3, 2, 2,
 70      1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 71      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 72      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 73  };
 74  
 75  int GetWaveValue(int a, int b, int c)
 76  {
 77      b &= 2047;
 78      switch (a)
 79      {
 80      case 0:
 81          return c;
 82      case 1:
 83          return (b>>10)*c;
 84      case 2:
 85          return (klabs(128-(b>>3))*c)>>7;
 86      case 3:
 87          return ((b>>3)*c)>>8;
 88      case 4:
 89          return ((255-(b>>3))*c)>>8;
 90      case 5:
 91          return (c+mulscale30(c,Sin(b)))>>1;
 92      case 6:
 93          return flicker1[b>>5]*c;
 94      case 7:
 95          return (flicker2[b>>5]*c)>>2;
 96      case 8:
 97          return (flicker3[b>>5]*c)>>2;
 98      case 9:
 99          return (flicker4[b>>4]*c)>>2;
100      case 10:
101          return (strobe[b>>5]*c)>>6;
102      case 11:
103          if (b*4 > 2048)
104              return 0;
105          return (c-mulscale30(c, Cos(b*4)))>>1;
106      }
107      return 0;
108  }
109  
110  int shadeCount = 0;
111  short shadeList[kMaxXSectors];
112  int panCount = 0;
113  short panList[kMaxXSectors];
114  char gotsectorROR[bitmap_size(kMaxSectors)]; // this is the same as gotsector, except it includes any ROR drawn sectors
115  
116  void DoSectorLighting(void)
117  {
118      for (int i = 0; i < shadeCount; i++)
119      {
120          int nXSector = shadeList[i];
121          XSECTOR *pXSector = &xsector[nXSector];
122          int nSector = pXSector->reference;
123          dassert(sector[nSector].extra == nXSector);
124          if (pXSector->shade)
125          {
126              int nShade = pXSector->shade;
127              if (pXSector->shadeFloor)
128              {
129                  sector[nSector].floorshade -= nShade;
130                  if (pXSector->color)
131                  {
132                      int nTemp = pXSector->floorpal;
133                      pXSector->floorpal = sector[nSector].floorpal;
134                      sector[nSector].floorpal = nTemp;
135                  }
136              }
137              if (pXSector->shadeCeiling)
138              {
139                  sector[nSector].ceilingshade -= nShade;
140                  if (pXSector->color)
141                  {
142                      int nTemp = pXSector->ceilpal;
143                      pXSector->ceilpal = sector[nSector].ceilingpal;
144                      sector[nSector].ceilingpal = nTemp;
145                  }
146              }
147              if (pXSector->shadeWalls)
148              {
149                  int nStartWall = sector[nSector].wallptr;
150                  int nEndWall = nStartWall + sector[nSector].wallnum;
151                  for (int j = nStartWall; j < nEndWall; j++)
152                  {
153                      wall[j].shade -= nShade;
154                      if (pXSector->color)
155                      {
156                          wall[j].pal = sector[nSector].floorpal;
157                      }
158                  }
159              }
160              pXSector->shade = 0;
161          }
162          if (pXSector->shadeAlways || pXSector->busy)
163          {
164              int nPhase = pXSector->wave;
165              int nAmp = pXSector->amplitude;
166              if (!pXSector->shadeAlways && pXSector->busy)
167              {
168                  nAmp = mulscale16(nAmp, pXSector->busy);
169              }
170              int nFreq = pXSector->freq;
171              int nShade = GetWaveValue(nPhase, (pXSector->phase*8)+(nFreq*(int)totalclock), nAmp);
172              if (pXSector->shadeFloor)
173              {
174                  sector[nSector].floorshade = ClipRange(sector[nSector].floorshade+nShade, -128, 127);
175                  if (pXSector->color && nShade != 0)
176                  {
177                      int nTemp = pXSector->floorpal;
178                      pXSector->floorpal = sector[nSector].floorpal;
179                      sector[nSector].floorpal = nTemp;
180                  }
181              }
182              if (pXSector->shadeCeiling)
183              {
184                  sector[nSector].ceilingshade = ClipRange(sector[nSector].ceilingshade+nShade, -128, 127);
185                  if (pXSector->color && nShade != 0)
186                  {
187                      int nTemp = pXSector->ceilpal;
188                      pXSector->ceilpal = sector[nSector].ceilingpal;
189                      sector[nSector].ceilingpal = nTemp;
190                  }
191              }
192              if (pXSector->shadeWalls)
193              {
194                  int nStartWall = sector[nSector].wallptr;
195                  int nEndWall = nStartWall + sector[nSector].wallnum;
196                  for (int j = nStartWall; j < nEndWall; j++)
197                  {
198                      wall[j].shade = ClipRange(wall[j].shade+nShade, -128, 127);
199                      if (pXSector->color && nShade != 0)
200                      {
201                          wall[j].pal = sector[nSector].floorpal;
202                      }
203                  }
204              }
205              pXSector->shade = nShade;
206          }
207      }
208  }
209  
210  void UndoSectorLighting(void)
211  {
212      for (int i = 0; i < numsectors; i++)
213      {
214          int nXSprite = sector[i].extra;
215          if (nXSprite > 0)
216          {
217              XSECTOR *pXSector = &xsector[i];
218              if (pXSector->shade)
219              {
220                  int v4 = pXSector->shade;
221                  if (pXSector->shadeFloor)
222                  {
223                      sector[i].floorshade -= v4;
224                      if (pXSector->color)
225                      {
226                          int nTemp = pXSector->floorpal;
227                          pXSector->floorpal = sector[i].floorpal;
228                          sector[i].floorpal = nTemp;
229                      }
230                  }
231                  if (pXSector->shadeCeiling)
232                  {
233                      sector[i].ceilingshade -= v4;
234                      if (pXSector->color)
235                      {
236                          int nTemp = pXSector->ceilpal;
237                          pXSector->ceilpal = sector[i].ceilingpal;
238                          sector[i].ceilingpal = nTemp;
239                      }
240                  }
241                  if (pXSector->shadeWalls)
242                  {
243                      int nStartWall = sector[i].wallptr;
244                      int nEndWall = nStartWall + sector[i].wallnum;
245                      for (int j = nStartWall; j < nEndWall; j++)
246                      {
247                          wall[j].shade -= v4;
248                          if (pXSector->color)
249                          {
250                              wall[j].pal = sector[i].floorpal;
251                          }
252                      }
253                  }
254                  pXSector->shade = 0;
255              }
256          }
257      }
258  }
259  
260  short wallPanList[kMaxXWalls];
261  short wallPanListSect[kMaxXWalls];
262  short wallPanListNextSect[kMaxXWalls];
263  int wallPanCount;
264  
265  void DoSectorPanning(void)
266  {
267      const char bInterp = (gViewMode == 3) && !VanillaMode();
268      const char bInterpFloor = ((gViewMode == 3) || (gViewMode == 4)) && !VanillaMode(); // map view sees floor sectors, allow them to be interpolated
269      for (int i = 0; i < panCount; i++)
270      {
271          int nXSector = panList[i];
272          XSECTOR *pXSector = &xsector[nXSector];
273          int nSector = pXSector->reference;
274          dassert(nSector >= 0 && nSector < kMaxSectors);
275          sectortype *pSector = &sector[nSector];
276          dassert(pSector->extra == nXSector);
277          if (pXSector->panAlways || pXSector->busy)
278          {
279              int angle = pXSector->panAngle+1024;
280              int speed = pXSector->panVel<<10;
281              if (!pXSector->panAlways && (pXSector->busy&0xffff))
282                  speed = mulscale16(speed, pXSector->busy);
283  
284              if (pXSector->panFloor) // Floor
285              {
286                  int nTile = pSector->floorpicnum;
287                  int px = (pSector->floorxpanning<<8)+pXSector->floorXPanFrac;
288                  int py = (pSector->floorypanning<<8)+pXSector->floorYPanFrac;
289                  if (pSector->floorstat&64)
290                      angle -= 512;
291                  int xBits = (picsiz[nTile]&15)-((pSector->floorstat&8)!=0);
292                  px += mulscale30(speed<<2, Cos(angle))>>xBits;
293                  int yBits = (picsiz[nTile]/16)-((pSector->floorstat&8)!=0);
294                  py -= mulscale30(speed<<2, Sin(angle))>>yBits;
295                  if (bInterpFloor && TestBitString(gotsectorROR, nSector))
296                      viewInterpolatePanningFloor(nSector, pSector);
297                  pSector->floorxpanning = px>>8;
298                  pSector->floorypanning = py>>8;
299                  pXSector->floorXPanFrac = px&255;
300                  pXSector->floorYPanFrac = py&255;
301              }
302              if (pXSector->panCeiling) // Ceiling
303              {
304                  int nTile = pSector->ceilingpicnum;
305                  int px = (pSector->ceilingxpanning<<8)+pXSector->ceilXPanFrac;
306                  int py = (pSector->ceilingypanning<<8)+pXSector->ceilYPanFrac;
307                  if (pSector->ceilingstat&64)
308                      angle -= 512;
309                  int xBits = (picsiz[nTile]&15)-((pSector->ceilingstat&8)!=0);
310                  px += mulscale30(speed<<2, Cos(-angle))>>xBits;
311                  int yBits = (picsiz[nTile]/16)-((pSector->ceilingstat&8)!=0);
312                  py -= mulscale30(speed<<2, Sin(-angle))>>yBits;
313                  if (bInterp && TestBitString(gotsectorROR, nSector))
314                      viewInterpolatePanningCeiling(nSector, pSector);
315                  pSector->ceilingxpanning = px>>8;
316                  pSector->ceilingypanning = py>>8;
317                  pXSector->ceilXPanFrac = px&255;
318                  pXSector->ceilYPanFrac = py&255;
319              }
320          }
321      }
322      for (int i = 0; i < wallPanCount; i++)
323      {
324          int nXWall = wallPanList[i];
325          XWALL *pXWall = &xwall[nXWall];
326          int nWall = pXWall->reference;
327          dassert(wall[nWall].extra == nXWall);
328          if (pXWall->panAlways || pXWall->busy)
329          {
330              int psx = pXWall->panXVel<<10;
331              int psy = pXWall->panYVel<<10;
332              if (!pXWall->panAlways && (pXWall->busy & 0xffff))
333              {
334                  psx = mulscale16(psx, pXWall->busy);
335                  psy = mulscale16(psy, pXWall->busy);
336              }
337              int nTile = wall[nWall].picnum;
338              int px = (wall[nWall].xpanning<<8)+pXWall->xpanFrac;
339              int py = (wall[nWall].ypanning<<8)+pXWall->ypanFrac;
340              px += (psx<<2)>>((uint8_t)picsiz[nTile]&15);
341              py += (psy<<2)>>((uint8_t)picsiz[nTile]/16);
342              if (bInterp && (TestBitString(gotsectorROR, wallPanListSect[i]) || (wallPanListNextSect[i] >= 0 && TestBitString(gotsectorROR, wallPanListNextSect[i]))))
343                  viewInterpolatePanningWall(nWall, &wall[nWall]);
344              wall[nWall].xpanning = px>>8;
345              wall[nWall].ypanning = py>>8;
346              pXWall->xpanFrac = px&255;
347              pXWall->ypanFrac = py&255;
348          }
349      }
350  }
351  
352  void InitSectorFX(void)
353  {
354      shadeCount = 0;
355      panCount = 0;
356      wallPanCount = 0;
357      ClearGotSectorSectorFX(true);
358      for (int i = 0; i < numsectors; i++)
359      {
360          int nXSector = sector[i].extra;
361          if (nXSector > 0)
362          {
363              XSECTOR *pXSector = &xsector[nXSector];
364              if (pXSector->amplitude)
365                  shadeList[shadeCount++] = nXSector;
366              if (pXSector->panVel)
367                  panList[panCount++] = nXSector;
368          }
369      }
370      for (int i = 0; i < numwalls; i++)
371      {
372          int nXWall = wall[i].extra;
373          if (nXWall > 0)
374          {
375              XWALL *pXWall = &xwall[nXWall];
376              if (pXWall->panXVel || pXWall->panYVel)
377              {
378                  for (int j = 0; j < numsectors; j++) // lookup sector for wall
379                  {
380                      short startwall = sector[j].wallptr;
381                      const short endwall = startwall + sector[j].wallnum;
382                      if ((i >= startwall) && (i < endwall))
383                      {
384                          wallPanListSect[wallPanCount] = j;
385                          break;
386                      }
387                  }
388                  wallPanListNextSect[wallPanCount] = wall[i].nextsector;
389                  wallPanList[wallPanCount++] = nXWall;
390              }
391          }
392      }
393  }
394  
395  static char bGotsectorCleared = 0;
396  
397  void ClearGotSectorSectorFX(const bool bClearAll)
398  {
399      Bmemset(gotsectorROR, 0, bClearAll ? sizeof(gotsectorROR) : bitmap_size(numsectors));
400      bGotsectorCleared = 1;
401  }
402  
403  void UpdateGotSectorSectorFX(void)
404  {
405      if (bGotsectorCleared) // fresh start, don't bother doing compare
406      {
407          Bmemcpy(gotsectorROR, gotsector, bitmap_size(numsectors));
408          bGotsectorCleared = 0;
409          return;
410      }
411      for (int i = 0; i < bitmap_size(numsectors); i += 4)
412      {
413          gotsectorROR[i]   |= gotsector[i];
414          gotsectorROR[i+1] |= gotsector[i+1];
415          gotsectorROR[i+2] |= gotsector[i+2];
416          gotsectorROR[i+3] |= gotsector[i+3];
417      }
418  }
419  
420  class CSectorListMgr
421  {
422  public:
423      CSectorListMgr();
424      int CreateList(short);
425      void AddSector(int, short);
426      int GetSectorCount(int);
427      short *GetSectorList(int);
428  private:
429      int nLists;
430      int nListSize[32];
431      int nListStart[32];
432      short nSectors[kMaxSectors];
433  };
434  
435  CSectorListMgr::CSectorListMgr()
436  {
437      nLists = 0;
438  }
439  
440  int CSectorListMgr::CreateList(short nSector)
441  {
442      int nStart = 0;
443      if (nLists)
444          nStart = nListStart[nLists-1]+nListStart[nLists-1];
445      int nList = nLists;
446      nListStart[nList] = nStart;
447      nListSize[nList] = 1;
448      nLists++;
449      short *pList = GetSectorList(nList);
450      pList[0] = nSector;
451      return nList;
452  }
453  
454  void CSectorListMgr::AddSector(int nList, short nSector)
455  {
456      for (int i = nLists; i > nList; i--)
457      {
458          short *pList = GetSectorList(i);
459          int nCount = GetSectorCount(i);
460          memmove(pList+1,pList,nCount*sizeof(short));
461          nListStart[i]++;
462      }
463      short *pList = GetSectorList(nList);
464      int nCount = GetSectorCount(nList);
465      pList[nCount] = nSector;
466      nListSize[nList]++;
467  }
468  
469  int CSectorListMgr::GetSectorCount(int nList)
470  {
471      return nListSize[nList];
472  }
473  
474  short * CSectorListMgr::GetSectorList(int nList)
475  {
476      return nSectors+nListStart[nList];
477  }