1 // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
2 // Ken Silverman's official web site: "http://www.advsys.net/ken"
3 // See the included license file "BUILDLIC.TXT" for license info.
4 //
5 // This file has been modified from Ken Silverman's original release
6 // by Jonathon Fowler (jf@jonof.id.au)
7 // by the EDuke32 team (development@voidpoint.com)
8 
9 #define engine_c_
10 
11 #include "a.h"
12 #include "baselayer.h"
13 #include "build.h"
14 #include "cache1d.h"
15 #include "colmatch.h"
16 #include "communityapi.h"
17 #include "compat.h"
18 #include "crc32.h"
19 #include "editor.h"
20 #include "engine_priv.h"
21 #include "lz4.h"
22 #include "microprofile.h"
23 #include "osd.h"
24 #include "palette.h"
25 #include "pragmas.h"
26 #include "scriptfile.h"
27 #include "softsurface.h"
28 #include "vfs.h"
29 
30 #ifdef USE_OPENGL
31 # include "glad/glad.h"
32 # include "glsurface.h"
33 # include "hightile.h"
34 # include "mdsprite.h"
35 # ifdef POLYMER
36 #  include "polymer.h"
37 # endif
38 # include "polymost.h"
39 #endif
40 
41 //////////
42 // Compilation switches for optional/extended engine features
43 
44 #if !defined(__arm__) && !defined(GEKKO)
45 # define HIGH_PRECISION_SPRITE
46 #endif
47 
48 #if !defined EDUKE32_TOUCH_DEVICES && !defined GEKKO && !defined __OPENDINGUX__
49 // Handle absolute z difference of floor/ceiling to camera >= 1<<24.
50 // Also: higher precision view-relative x and y for drawvox().
51 # define CLASSIC_Z_DIFF_64
52 #endif
53 
54 #define MULTI_COLUMN_VLINE
55 //#define DEBUG_TILESIZY_512
56 //#define DEBUG_TILEOFFSETS
57 //////////
58 
59 #ifdef DEBUGGINGAIDS
60 float debug1, debug2;
61 #endif
62 
63 int32_t mapversion=7; // JBF 20040211: default mapversion to 7
64 int32_t g_loadedMapVersion = -1;  // -1: none (e.g. started new)
65 
66 static int32_t get_mapversion(void);
67 
68 // Handle nonpow2-ysize walls the old way?
oldnonpow2(void)69 static FORCE_INLINE int32_t oldnonpow2(void)
70 {
71 #if !defined CLASSIC_NONPOW2_YSIZE_WALLS
72     return 1;
73 #else
74     return ((g_loadedMapVersion < 10) && !(picanm[globalpicnum].tileflags & TILEFLAGS_TRUENPOT));
75 #endif
76 }
77 
78 uint8_t globalr = 255, globalg = 255, globalb = 255;
79 
80 int16_t pskybits_override = -1;
81 
82 void (*loadvoxel_replace)(int32_t voxindex) = NULL;
83 int16_t tiletovox[MAXTILES];
84 int32_t usevoxels = 1;
85 #ifdef USE_OPENGL
86 static char *voxfilenames[MAXVOXELS];
87 #endif
88 char g_haveVoxels;
89 //#define kloadvoxel loadvoxel
90 
91 int32_t novoxmips = 1;
92 
93 //These variables need to be copied into BUILD
94 #define MAXXSIZ 256
95 #define MAXYSIZ 256
96 #define MAXZSIZ 255
97 #ifdef EDUKE32_TOUCH_DEVICES
98 # define DISTRECIPSIZ (65536+256)
99 #else
100 # define DISTRECIPSIZ 131072
101 #endif
102 static char voxlock[MAXVOXELS][MAXVOXMIPS];
103 int32_t voxscale[MAXVOXELS];
104 
105 static int32_t ggxinc[MAXXSIZ+1], ggyinc[MAXXSIZ+1];
106 static int32_t lowrecip[1024], nytooclose;
107 static const int32_t nytoofar = DISTRECIPSIZ*16384ull - 1048576;
108 static uint32_t *distrecip;
109 #define DISTRECIPCACHESIZE 3
110 static struct {
111     uint32_t *distrecip;
112     int32_t xdimen;
113     int32_t age;
114 } distrecipcache[DISTRECIPCACHESIZE];
115 static int32_t distrecipagecnt = 0;
116 
117 static int32_t *lookups = NULL;
118 static int32_t beforedrawrooms = 1;
119 
120 int32_t benchmarkScreenshot = 0;
121 
122 static int32_t oxdimen = -1, oviewingrange = -1, oxyaspect = -1;
123 
124 // r_usenewaspect is the cvar, newaspect_enable to trigger the new behaviour in the code
125 int32_t r_usenewaspect = 1, newaspect_enable=0;
126 uint32_t r_screenxy = 0;
127 
128 int32_t r_fpgrouscan = 1;
129 int32_t r_displayindex = 0;
130 int32_t r_borderless = 2;
131 int32_t globalflags;
132 
133 float g_videoGamma = DEFAULT_GAMMA;
134 float g_videoContrast = DEFAULT_CONTRAST;
135 float g_videoBrightness = DEFAULT_BRIGHTNESS;
136 
137 //Textured Map variables
138 static char globalpolytype;
139 static int16_t **dotp1, **dotp2;
140 
141 static int8_t tempbuf[MAXWALLS];
142 
143 // referenced from asm
144 #if !defined(NOASM) && defined __cplusplus
145 extern "C" {
146 #endif
147 int32_t ebpbak, espbak;
148 int32_t reciptable[2048], fpuasm;
149 intptr_t asm1, asm2, asm3, asm4, palookupoffse[4];
150 uint32_t vplce[4];
151 int32_t vince[4];
152 intptr_t bufplce[4];
153 int32_t globaltilesizy;
154 int32_t globalx1, globaly2, globalx3, globaly3;
155 #if !defined(NOASM) && defined __cplusplus
156 }
157 #endif
158 
159 int32_t sloptable[SLOPTABLESIZ];
160 #define SLOPALOOKUPSIZ 16384
161 static intptr_t slopalookup[SLOPALOOKUPSIZ];    // was 2048
162 
163 static int32_t no_radarang2 = 0;
164 static int16_t radarang[1280];
165 static int32_t qradarang[10240], *radarang2;
166 const char ATTRIBUTE((used)) pow2char_[8] = {1,2,4,8,16,32,64,128};
167 
168 uint16_t ATTRIBUTE((used)) sqrtable[4096], ATTRIBUTE((used)) shlookup[4096+256], ATTRIBUTE((used)) sqrtable_old[2048];
169 
170 char britable[16][256]; // JBF 20040207: full 8bit precision
171 
172 extern char textfont[2048], smalltextfont[2048];
173 
174 static char kensmessage[128];
175 const char *engineerrstr = "No error";
176 
177 int32_t showfirstwall=0;
178 int32_t showheightindicators=1;
179 int32_t circlewall=-1;
180 
181 static void classicScanSector(int16_t startsectnum);
182 static void draw_rainbow_background(void);
183 
184 int16_t editstatus = 0;
185 static fix16_t global100horiz;  // (-100..300)-scale horiz (the one passed to drawrooms)
186 
187 int32_t(*getpalookup_replace)(int32_t davis, int32_t dashade) = NULL;
188 
189 int32_t bloodhack = 0;
190 
191 #ifndef EDUKE32_STANDALONE
192 int32_t enginecompatibilitymode = ENGINE_EDUKE32;
193 #endif
194 
195 // adapted from build.c
getclosestpointonwall_internal(vec2_t const p,int32_t const dawall,vec2_t * const closest)196 static void getclosestpointonwall_internal(vec2_t const p, int32_t const dawall, vec2_t *const closest)
197 {
198     vec2_t const w  = wall[dawall].pos;
199     vec2_t const w2 = wall[wall[dawall].point2].pos;
200     vec2_t const d  = { w2.x - w.x, w2.y - w.y };
201 
202     int64_t i = d.x * ((int64_t)p.x - w.x) + d.y * ((int64_t)p.y - w.y);
203 
204     if (i <= 0)
205     {
206         *closest = w;
207         return;
208     }
209 
210     int64_t const j = (int64_t)d.x * d.x + (int64_t)d.y * d.y;
211 
212     if (i >= j)
213     {
214         *closest = w2;
215         return;
216     }
217 
218     i = tabledivide64((i << 15), j) << 15;
219 
220     *closest = { (int32_t)(w.x + ((d.x * i) >> 30)), (int32_t)(w.y + ((d.y * i) >> 30)) };
221 }
222 
223 ////////// YAX //////////
224 
225 int32_t numgraysects = 0;
226 uint8_t graysectbitmap[(MAXSECTORS+7)>>3];
227 uint8_t graywallbitmap[(MAXWALLS+7)>>3];
228 int32_t autogray = 0, showinnergray = 1;
229 
230 //#define YAX_DEBUG_YMOSTS
231 
232 #ifdef YAX_DEBUG
233 // XXX: This could be replaced with the use of gethiticks().
234 double u64tickspersec;
235 #endif
236 #ifdef ENGINE_SCREENSHOT_DEBUG
237 int32_t engine_screenshot = 0;
238 #endif
239 
get_alwaysshowgray(void)240 int32_t get_alwaysshowgray(void)
241 {
242     return showinnergray || !(editorzrange[0]==INT32_MIN && editorzrange[1]==INT32_MAX);
243 }
244 
yax_updategrays(int32_t posze)245 void yax_updategrays(int32_t posze)
246 {
247     int32_t i, j;
248 #ifdef YAX_ENABLE
249     int32_t mingoodz=INT32_MAX, maxgoodz=INT32_MIN;
250 #else
251     UNREFERENCED_PARAMETER(posze);
252 #endif
253 
254     Bmemset(graysectbitmap, 0, sizeof(graysectbitmap));
255     Bmemset(graywallbitmap, 0, sizeof(graywallbitmap));
256 
257     for (i=0; i<numsectors; i++)
258     {
259 #ifdef YAX_ENABLE
260         int16_t cb, fb;
261         yax_getbunches(i, &cb, &fb);
262 
263         // Update grayouts due to TROR, has to be --v--       half-open      --v--
264         // because only one level should ever be    v                          v
265         // active.                                  v                          v
266         int32_t keep = ((cb<0 || sector[i].ceilingz < posze) && (fb<0 || posze <= sector[i].floorz));
267         if (autogray && (cb>=0 || fb>=0) && (sector[i].ceilingz <= posze && posze <= sector[i].floorz))
268         {
269             mingoodz = min(mingoodz, TrackerCast(sector[i].ceilingz));
270             maxgoodz = max(maxgoodz, TrackerCast(sector[i].floorz));
271         }
272 #endif
273         // update grayouts due to editorzrange
274         keep &= (sector[i].ceilingz >= editorzrange[0] && sector[i].floorz <= editorzrange[1]);
275 
276         if (!keep)  // outside bounds, gray out!
277             graysectbitmap[i>>3] |= pow2char[i&7];
278     }
279 
280 #ifdef YAX_ENABLE
281     if (autogray && mingoodz<=maxgoodz)
282     {
283         for (i=0; i<numsectors; i++)
284             if (!(mingoodz <= sector[i].ceilingz && sector[i].floorz <= maxgoodz))
285                 graysectbitmap[i>>3] |= pow2char[i&7];
286     }
287 #endif
288 
289     numgraysects = 0;
290     for (i=0; i<numsectors; i++)
291     {
292         if (graysectbitmap[i>>3]&pow2char[i&7])
293         {
294             numgraysects++;
295             for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
296                 graywallbitmap[j>>3] |= pow2char[j&7];
297         }
298     }
299 }
300 
301 
302 #if !defined YAX_ENABLE
303 # warning Non-TROR builds are supported only for debugging. Expect savegame breakage etc...
304 #endif
305 
306 #ifdef YAX_ENABLE
307 // all references to floor/ceiling bunchnums should be through the
308 // get/set functions!
309 
310 int32_t g_nodraw = 0;
311 int32_t scansector_retfast = 0;
312 int32_t scansector_collectsprites = 1;
313 int32_t yax_globalcf = -1, yax_nomaskpass=0, yax_nomaskdidit;  // engine internal
314 int32_t r_tror_nomaskpass = 1;  // cvar
315 int32_t yax_globallev = YAX_MAXDRAWS;
316 int32_t yax_globalbunch = -1;
317 int32_t yax_polymostclearzbuffer = 1;
318 
319 // duplicated tsprites
320 //  [i]:
321 //   i==MAXDRAWS: base level
322 //   i<MAXDRAWS: MAXDRAWS-i-1 is level towards ceiling
323 //   i>MAXDRAWS: i-MAXDRAWS-1 is level towards floor
324 static int16_t yax_spritesortcnt[1 + 2*YAX_MAXDRAWS];
325 static uint16_t yax_tsprite[1 + 2*YAX_MAXDRAWS][MAXSPRITESONSCREEN];
326 static uint8_t yax_tsprfrombunch[1 + 2*YAX_MAXDRAWS][MAXSPRITESONSCREEN];
327 
328 // drawn sectors
329 uint8_t yax_gotsector[(MAXSECTORS+7)>>3];  // engine internal
330 
331 # if !defined NEW_MAP_FORMAT
332 // Game-time YAX data structures, V7-V9 map formats.
333 int16_t yax_bunchnum[MAXSECTORS][2];
334 int16_t yax_nextwall[MAXWALLS][2];
335 
yax_islockededge(int32_t line,int32_t cf)336 static FORCE_INLINE int32_t yax_islockededge(int32_t line, int32_t cf)
337 {
338     return !!(wall[line].cstat&(YAX_NEXTWALLBIT(cf)));
339 }
340 
341 #define YAX_PTRBUNCHNUM(Ptr, Sect, Cf) (*(&Ptr[Sect].ceilingxpanning + 8*Cf))
342 #define YAX_BUNCHNUM(Sect, Cf) YAX_PTRBUNCHNUM(sector, Sect, Cf)
343 
344 //// bunch getters/setters
yax_getbunch(int16_t i,int16_t cf)345 int16_t yax_getbunch(int16_t i, int16_t cf)
346 {
347     if (editstatus==0)
348         return yax_bunchnum[i][cf];
349 
350     return (*(&sector[i].ceilingstat + cf) & YAX_BIT) ? YAX_BUNCHNUM(i, cf) : -1;
351 }
352 # else
353 #  define YAX_PTRBUNCHNUM(Ptr, Sect, Cf) (*((Cf) ? &(Ptr)[Sect].floorbunch : &(Ptr)[Sect].ceilingbunch))
354 #  define YAX_BUNCHNUM(Sect, Cf) YAX_PTRBUNCHNUM(sector, Sect, Cf)
355 
356 #  if !defined NEW_MAP_FORMAT
yax_islockededge(int32_t line,int32_t cf)357 static FORCE_INLINE int32_t yax_islockededge(int32_t line, int32_t cf)
358 {
359     return (yax_getnextwall(line, cf) >= 0);
360 }
361 #  endif
362 # endif
363 
364 // bunchnum: -1: also clear yax-nextwalls (forward and reverse)
365 //           -2: don't clear reverse yax-nextwalls
366 //           -3: don't clear either forward or reverse yax-nextwalls
yax_setbunch(int16_t i,int16_t cf,int16_t bunchnum)367 void yax_setbunch(int16_t i, int16_t cf, int16_t bunchnum)
368 {
369     if (editstatus==0)
370     {
371 #ifdef NEW_MAP_FORMAT
372         YAX_BUNCHNUM(i, cf) = bunchnum;
373 #else
374         yax_bunchnum[i][cf] = bunchnum;
375 #endif
376         return;
377     }
378 
379     if (bunchnum < 0)
380     {
381         if (bunchnum > -3)
382         {
383             // TODO: for in-game too?
384             for (bssize_t ynw, j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
385             {
386                 ynw = yax_getnextwall(j, cf);
387                 if (ynw >= 0)
388                 {
389                     if (bunchnum > -2)
390                         yax_setnextwall(ynw, !cf, -1);
391                     yax_setnextwall(j, cf, -1);
392                 }
393             }
394         }
395 
396 #if !defined NEW_MAP_FORMAT
397         *(&sector[i].ceilingstat + cf) &= ~YAX_BIT;
398         // NOTE: Don't reset xpanning-as-index, since we can be called from
399         // e.g. Mapster32's "Inner loop made into new sector" functionality.
400 //        YAX_BUNCHNUM(i, cf) = 0;
401 #else
402         YAX_BUNCHNUM(i, cf) = -1;
403 #endif
404         return;
405     }
406 
407 #if !defined NEW_MAP_FORMAT
408     *(&sector[i].ceilingstat + cf) |= YAX_BIT;
409 #endif
410     YAX_BUNCHNUM(i, cf) = bunchnum;
411 }
412 
yax_setbunches(int16_t i,int16_t cb,int16_t fb)413 void yax_setbunches(int16_t i, int16_t cb, int16_t fb)
414 {
415     yax_setbunch(i, YAX_CEILING, cb);
416     yax_setbunch(i, YAX_FLOOR, fb);
417 }
418 
419 # if !defined NEW_MAP_FORMAT
420 //// nextwall getters/setters
yax_getnextwall(int16_t wal,int16_t cf)421 int16_t yax_getnextwall(int16_t wal, int16_t cf)
422 {
423     if (editstatus==0)
424         return yax_nextwall[wal][cf];
425 
426     return yax_islockededge(wal, cf) ? YAX_NEXTWALL(wal, cf) : -1;
427 }
428 
429 // unchecked!
yax_setnextwall(int16_t wal,int16_t cf,int16_t thenextwall)430 void yax_setnextwall(int16_t wal, int16_t cf, int16_t thenextwall)
431 {
432     if (editstatus==0)
433     {
434         yax_nextwall[wal][cf] = thenextwall;
435         return;
436     }
437 
438     if (thenextwall >= 0)
439     {
440         wall[wal].cstat |= YAX_NEXTWALLBIT(cf);
441         YAX_NEXTWALL(wal, cf) = thenextwall;
442     }
443     else
444     {
445         wall[wal].cstat &= ~YAX_NEXTWALLBIT(cf);
446         YAX_NEXTWALL(wal, cf) = YAX_NEXTWALLDEFAULT(cf);
447     }
448 }
449 # endif
450 
451 // make one step in the vertical direction, and if the wall we arrive at
452 // is red, return its nextsector.
yax_vnextsec(int16_t line,int16_t cf)453 int16_t yax_vnextsec(int16_t line, int16_t cf)
454 {
455     int16_t const ynw = yax_getnextwall(line, cf);
456     return (ynw < 0) ? -1 : wall[ynw].nextsector;
457 }
458 
459 
460 //// in-struct --> array transfer (only resetstat==0); list construction
461 // resetstat:  0: reset and read data from structs and construct linked lists etc.
462 //             1: only reset
463 //             2: read data from game-time arrays and construct linked lists etc.
yax_update(int32_t resetstat)464 void yax_update(int32_t resetstat)
465 {
466     int32_t i;
467 #if !defined NEW_MAP_FORMAT
468     int32_t j;
469     const int32_t oeditstatus=editstatus;
470 #endif
471     int16_t cb, fb;
472 
473     if (resetstat != 2)
474         numyaxbunches = 0;
475 
476     for (i=0; i<MAXSECTORS; i++)
477     {
478 #if !defined NEW_MAP_FORMAT
479         if (resetstat != 2 || i>=numsectors)
480             yax_bunchnum[i][0] = yax_bunchnum[i][1] = -1;
481 #endif
482         nextsectbunch[0][i] = nextsectbunch[1][i] = -1;
483     }
484     for (i=0; i<YAX_MAXBUNCHES; i++)
485         headsectbunch[0][i] = headsectbunch[1][i] = -1;
486 #if !defined NEW_MAP_FORMAT
487     for (i=0; i<MAXWALLS; i++)
488         if (resetstat != 2 || i>=numwalls)
489             yax_nextwall[i][0] = yax_nextwall[i][1] = -1;
490 #endif
491 
492     if (resetstat==1)
493         return;
494 
495     // Constuct singly linked list of sectors-of-bunch.
496 
497 #if !defined NEW_MAP_FORMAT
498     // Read bunchnums directly from the sector struct in yax_[gs]etbunch{es}!
499     editstatus = (resetstat==0);
500     // NOTE: Use oeditstatus to check for in-gamedness from here on!
501 #endif
502 
503     if (resetstat==0)
504     {
505         // make bunchnums consecutive
506         uint8_t *const havebunch = (uint8_t *)tempbuf;
507         uint8_t *const bunchmap = havebunch + ((YAX_MAXBUNCHES+7)>>3);
508         int32_t dasub = 0;
509 
510         Bmemset(havebunch, 0, (YAX_MAXBUNCHES+7)>>3);
511         for (i=0; i<numsectors; i++)
512         {
513             yax_getbunches(i, &cb, &fb);
514             if (cb>=0)
515                 havebunch[cb>>3] |= pow2char[cb&7];
516             if (fb>=0)
517                 havebunch[fb>>3] |= pow2char[fb&7];
518         }
519 
520         for (i=0; i<YAX_MAXBUNCHES; i++)
521         {
522             if ((havebunch[i>>3]&pow2char[i&7])==0)
523             {
524                 bunchmap[i] = 255;
525                 dasub++;
526                 continue;
527             }
528 
529             bunchmap[i] = i-dasub;
530         }
531 
532         for (i=0; i<numsectors; i++)
533         {
534             yax_getbunches(i, &cb, &fb);
535             if (cb>=0)
536                 yax_setbunch(i, YAX_CEILING, bunchmap[cb]);
537             if (fb>=0)
538                 yax_setbunch(i, YAX_FLOOR, bunchmap[fb]);
539         }
540     }
541 
542     // In-struct --> array transfer (resetstat==0 and !defined NEW_MAP_FORMAT)
543     // and list construction.
544     for (i=numsectors-1; i>=0; i--)
545     {
546         yax_getbunches(i, &cb, &fb);
547 #if !defined NEW_MAP_FORMAT
548         if (resetstat==0)
549         {
550             yax_bunchnum[i][0] = cb;
551             yax_bunchnum[i][1] = fb;
552         }
553 #endif
554 
555         if (cb >= 0)
556         {
557 #if !defined NEW_MAP_FORMAT
558             if (resetstat==0)
559                 for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
560                 {
561                     if (yax_islockededge(j,YAX_CEILING))
562                     {
563                         yax_nextwall[j][0] = YAX_NEXTWALL(j,0);
564                         if (oeditstatus==0)
565                             YAX_NEXTWALL(j,0) = 0;  // reset lotag!
566                     }
567                 }
568 #endif
569             if (headsectbunch[0][cb] == -1)
570             {
571                 headsectbunch[0][cb] = i;
572                 // not duplicated in floors, since every extended ceiling
573                 // must have a corresponding floor:
574                 if (resetstat==0)
575                     numyaxbunches++;
576             }
577             else
578             {
579                 int32_t tmpsect = headsectbunch[0][cb];
580                 headsectbunch[0][cb] = i;
581                 nextsectbunch[0][i] = tmpsect;
582             }
583         }
584 
585         if (fb >= 0)
586         {
587 #if !defined NEW_MAP_FORMAT
588             if (resetstat==0)
589                 for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
590                 {
591                     if (yax_islockededge(j,YAX_FLOOR))
592                     {
593                         yax_nextwall[j][1] = YAX_NEXTWALL(j,1);
594                         if (oeditstatus==0)
595                             YAX_NEXTWALL(j,1) = -1;  // reset extra!
596                     }
597                 }
598 #endif
599             if (headsectbunch[1][fb] == -1)
600                 headsectbunch[1][fb] = i;
601             else
602             {
603                 int32_t tmpsect = headsectbunch[1][fb];
604                 headsectbunch[1][fb] = i;
605                 nextsectbunch[1][i] = tmpsect;
606             }
607         }
608     }
609 
610 #if !defined NEW_MAP_FORMAT
611     editstatus = oeditstatus;
612 #else
613     mapversion = get_mapversion();
614 #endif
615 }
616 
yax_getneighborsect(int32_t x,int32_t y,int32_t sectnum,int32_t cf)617 int32_t yax_getneighborsect(int32_t x, int32_t y, int32_t sectnum, int32_t cf)
618 {
619     int16_t bunchnum = yax_getbunch(sectnum, cf);
620 
621     if (bunchnum < 0)
622         return -1;
623 
624     for (bssize_t SECTORS_OF_BUNCH(bunchnum, !cf, i))
625         if (inside(x, y, i)==1)
626             return i;
627 
628     return -1;
629 }
630 
631 // indexed as a list:
632 static int16_t bunches[2][YAX_MAXBUNCHES];
633 // indexed with bunchnums directly:
634 static int16_t bunchsec[YAX_MAXBUNCHES], bunchdist[YAX_MAXBUNCHES];
635 
636 static int32_t ymostallocsize = 0;  // numyaxbunches*xdimen (no sizeof(int16_t) here!)
637 static int16_t *yumost=NULL, *ydmost=NULL;  // used as if [numyaxbunches][xdimen]
638 uint8_t haveymost[(YAX_MAXBUNCHES+7)>>3];
639 
yax_walldist(int32_t w)640 static inline int32_t yax_walldist(int32_t w)
641 {
642     vec2_t closest;
643     getclosestpointonwall_internal({ globalposx, globalposy }, w, &closest);
644     return klabs(closest.x-globalposx) + klabs(closest.y-globalposy);
645 }
646 
647 // calculate distances to bunches and best start-drawing sectors
yax_scanbunches(int32_t bbeg,int32_t numhere,const uint8_t * lastgotsector)648 static void yax_scanbunches(int32_t bbeg, int32_t numhere, const uint8_t *lastgotsector)
649 {
650     int32_t bnchcnt, bunchnum, j, k;
651     int32_t startwall, endwall;
652 
653     UNREFERENCED_PARAMETER(lastgotsector);
654 
655     scansector_collectsprites = 0;
656 
657     for (bnchcnt=bbeg; bnchcnt<bbeg+numhere; bnchcnt++)
658     {
659         int32_t walldist, bestsec=-1;
660         int32_t bestwalldist=INT32_MAX;
661 
662         bunchnum = bunches[yax_globalcf][bnchcnt];
663 
664         for (SECTORS_OF_BUNCH(bunchnum,!yax_globalcf, k))
665         {
666             if (inside(globalposx, globalposy, k)==1)
667             {
668                 bestsec = k;
669                 bestwalldist = 0;
670                 break;
671             }
672 
673             startwall = sector[k].wallptr;
674             endwall = startwall+sector[k].wallnum;
675 
676             for (j=startwall; j<endwall; j++)
677             {
678 /*
679                 if ((w=yax_getnextwall(j,!yax_globalcf))>=0)
680                     if ((ns=wall[w].nextsector)>=0)
681                         if ((lastgotsector[ns>>3]&pow2char[ns&7])==0)
682                             continue;
683 */
684                 walldist = yax_walldist(j);
685                 if (walldist < bestwalldist)
686                 {
687                     bestsec = k;
688                     bestwalldist = walldist;
689                 }
690             }
691         }
692 
693         bunchsec[bunchnum] = bestsec;
694         bunchdist[bunchnum] = bestwalldist;
695     }
696 
697     scansector_collectsprites = 1;
698 }
699 
yax_cmpbunches(const void * b1,const void * b2)700 static int yax_cmpbunches(const void *b1, const void *b2)
701 {
702     return (bunchdist[B_UNBUF16(b2)] - bunchdist[B_UNBUF16(b1)]);
703 }
704 
705 
yax_tweakpicnums(int32_t bunchnum,int32_t cf,int32_t restore)706 void yax_tweakpicnums(int32_t bunchnum, int32_t cf, int32_t restore)
707 {
708     // for polymer, this is called before polymer_drawrooms() with restore==0
709     // and after polymer_drawmasks() with restore==1
710 
711     int32_t i, dastat;
712     static int16_t opicnum[2][MAXSECTORS];
713 #ifdef DEBUGGINGAIDS
714     static uint8_t expect_restore[2][YAX_MAXBUNCHES];
715 
716     // must call this with restore == 0, 1,  0, 1,  0, 1,  ...
717     Bassert(expect_restore[cf][bunchnum] == restore);
718     expect_restore[cf][bunchnum] = !expect_restore[cf][bunchnum];
719 #endif
720 
721     for (SECTORS_OF_BUNCH(bunchnum, cf, i))
722     {
723         dastat = (SECTORFLD(i,stat, cf)&(128+256));
724 
725         // only consider non-masked ceilings/floors
726         if (dastat==0 || (restore==1 && opicnum[cf][i]&0x8000))
727         {
728             if (!restore)
729             {
730                 opicnum[cf][i] = SECTORFLD(i,picnum, cf);
731                 if (editstatus && showinvisibility)
732                     SECTORFLD(i,picnum, cf) = MAXTILES-1;
733                 else //if ((dastat&(128+256))==0)
734                     SECTORFLD(i,picnum, cf) = bloodhack ? MAXTILES-2 : 13; //FOF;
735             }
736             else
737             {
738                 SECTORFLD(i,picnum, cf) = opicnum[cf][i];
739             }
740 #ifdef POLYMER
741             // will be called only in editor
742             if (videoGetRenderMode() == REND_POLYMER)
743             {
744                 if (!restore)
745                 {
746                     SECTORFLD(i,stat, cf) |= 128;
747                     opicnum[cf][i] |= 0x8000;
748                 }
749                 else
750                 {
751                     SECTORFLD(i,stat, cf) &= ~128;
752                     SECTORFLD(i,picnum, cf) &= 0x7fff;
753                     opicnum[cf][i] = 0;
754                 }
755             }
756 #endif
757         }
758     }
759 }
760 
yax_copytsprites()761 static void yax_copytsprites()
762 {
763     int32_t i, spritenum, gotthrough, sectnum;
764     int32_t sortcnt = yax_spritesortcnt[yax_globallev];
765     uspriteptr_t spr;
766 
767     for (i=0; i<sortcnt; i++)
768     {
769         spritenum = yax_tsprite[yax_globallev][i];
770 
771         gotthrough = spritenum&(MAXSPRITES|(MAXSPRITES<<1));
772 
773         spritenum &= MAXSPRITES-1;
774         spr = (uspriteptr_t)&sprite[spritenum];
775         sectnum = spr->sectnum;
776 
777         if (gotthrough == (MAXSPRITES|(MAXSPRITES<<1)))
778         {
779             if (yax_globalbunch != yax_tsprfrombunch[yax_globallev][i])
780                 continue;
781         }
782         else
783         {
784             int32_t cf = -1;
785 
786             if (gotthrough == MAXSPRITES)
787                 cf = YAX_CEILING;  // sprite got here through the ceiling of lower sector
788             else if (gotthrough == (MAXSPRITES<<1))
789                 cf = YAX_FLOOR;  // sprite got here through the floor of upper sector
790 
791             if (cf != -1)
792             {
793                 if ((yax_globallev-YAX_MAXDRAWS)*(-1 + 2*cf) > 0)
794                     if (yax_getbunch(sectnum, cf) != yax_globalbunch)
795                         continue;
796 
797                 sectnum = yax_getneighborsect(spr->x, spr->y, sectnum, cf);
798                 if (sectnum < 0)
799                     continue;
800             }
801         }
802 
803         if (spritesortcnt >= maxspritesonscreen)
804             break;
805 
806         tspriteptr_t tsp = renderAddTSpriteFromSprite(spritenum);
807         tsp->sectnum = sectnum;  // potentially tweak sectnum!
808     }
809 }
810 
811 
yax_preparedrawrooms(void)812 void yax_preparedrawrooms(void)
813 {
814     if (videoGetRenderMode() == REND_POLYMER || numyaxbunches==0)
815         return;
816 
817     g_nodraw = 1;
818     Bmemset(yax_spritesortcnt, 0, sizeof(yax_spritesortcnt));
819     Bmemset(haveymost, 0, (numyaxbunches+7)>>3);
820 
821     if (videoGetRenderMode() == REND_CLASSIC && ymostallocsize < xdimen*numyaxbunches)
822     {
823         ymostallocsize = xdimen*numyaxbunches;
824         yumost = (int16_t *)Xrealloc(yumost, ymostallocsize*sizeof(int16_t));
825         ydmost = (int16_t *)Xrealloc(ydmost, ymostallocsize*sizeof(int16_t));
826     }
827 }
828 
yax_drawrooms(void (* SpriteAnimFunc)(int32_t,int32_t,int32_t,int32_t,int32_t),int16_t sectnum,int32_t didmirror,int32_t smoothr)829 void yax_drawrooms(void (*SpriteAnimFunc)(int32_t,int32_t,int32_t,int32_t,int32_t),
830                    int16_t sectnum, int32_t didmirror, int32_t smoothr)
831 {
832     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
833 
834     static uint8_t havebunch[(YAX_MAXBUNCHES+7)>>3];
835 
836     const fix16_t horiz = global100horiz;
837 
838     int32_t i, j, k, lev, cf, nmp;
839     int32_t bnchcnt, bnchnum[2] = {0,0}, maxlev[2];
840     int16_t ourbunch[2] = {-1,-1}, osectnum=sectnum;
841     int32_t bnchbeg[YAX_MAXDRAWS][2], bnchend[YAX_MAXDRAWS][2];
842     int32_t bbeg, numhere;
843 
844     // original (1st-draw) and accumulated ('per-level') gotsector bitmaps
845     static uint8_t ogotsector[(MAXSECTORS+7)>>3], lgotsector[(MAXSECTORS+7)>>3];
846 #ifdef YAX_DEBUG
847     uint64_t t;
848 #endif
849 
850     if (videoGetRenderMode() == REND_POLYMER || numyaxbunches==0)
851     {
852 #ifdef ENGINE_SCREENSHOT_DEBUG
853         engine_screenshot = 0;
854 #endif
855         return;
856     }
857 
858     // if we're here, there was just a drawrooms() call with g_nodraw=1
859 
860     Bmemcpy(ogotsector, gotsector, (numsectors+7)>>3);
861 
862     if (sectnum >= 0)
863         yax_getbunches(sectnum, &ourbunch[0], &ourbunch[1]);
864     Bmemset(&havebunch, 0, (numyaxbunches+7)>>3);
865 
866     // first scan all bunches above, then all below...
867     for (cf=0; cf<2; cf++)
868     {
869         yax_globalcf = cf;
870 
871         if (cf==1)
872         {
873             sectnum = osectnum;
874             Bmemcpy(gotsector, ogotsector, (numsectors+7)>>3);
875         }
876 
877         for (lev=0; /*lev<YAX_MAXDRAWS*/; lev++)
878         {
879             yax_globallev = YAX_MAXDRAWS + (-1 + 2*cf)*(lev+1);
880 
881             bbeg = bnchbeg[lev][cf] = bnchend[lev][cf] = bnchnum[cf];
882             numhere = 0;
883 
884             for (i=0; i<numsectors; i++)
885             {
886                 if (!(gotsector[i>>3]&pow2char[i&7]))
887                     continue;
888 
889                 j = yax_getbunch(i, cf);
890                 if (j >= 0 && !(havebunch[j>>3]&pow2char[j&7]))
891                 {
892                     if (videoGetRenderMode() == REND_CLASSIC && (haveymost[j>>3]&pow2char[j&7])==0)
893                     {
894                         yaxdebug("%s, l %d: skipped bunch %d (no *most)", cf?"v":"^", lev, j);
895                         continue;
896                     }
897 
898                     if ((SECTORFLD(i,stat, cf)&2) ||
899                             (cf==0 && globalposz >= sector[i].ceilingz) ||
900                             (cf==1 && globalposz <= sector[i].floorz))
901                     {
902                         havebunch[j>>3] |= pow2char[j&7];
903                         bunches[cf][bnchnum[cf]++] = j;
904                         bnchend[lev][cf]++;
905                         numhere++;
906                     }
907                 }
908             }
909 
910             if (numhere > 0)
911             {
912                 // found bunches -- need to fake-draw
913 
914                 yax_scanbunches(bbeg, numhere, (uint8_t *)gotsector);
915 
916                 qsort(&bunches[cf][bbeg], numhere, sizeof(int16_t), &yax_cmpbunches);
917 
918                 if (numhere > 1 && lev != YAX_MAXDRAWS-1)
919                     Bmemset(lgotsector, 0, sizeof(lgotsector));
920 
921                 for (bnchcnt=bbeg; bnchcnt < bbeg+numhere; bnchcnt++)
922                 {
923                     j = bunches[cf][bnchcnt];  // the actual bunchnum...
924                     yax_globalbunch = j;
925 #ifdef YAX_DEBUG
926                     t=timerGetPerformanceCounter();
927 #endif
928                     k = bunchsec[j];
929 
930                     if (k < 0)
931                     {
932                         yaxprintf("%s, l %d: skipped bunch %d\n", cf?"v":"^", lev, j);
933                         continue;
934                     }
935 
936                     if (lev != YAX_MAXDRAWS-1)
937                     {
938 #ifdef YAX_DEBUG
939                         int32_t odsprcnt = yax_spritesortcnt[yax_globallev];
940 #endif
941                         // +MAXSECTORS: force
942                         renderDrawRoomsQ16(globalposx,globalposy,globalposz,qglobalang,horiz,k+MAXSECTORS);
943                         if (numhere > 1)
944                             for (i=0; i<(numsectors+7)>>3; i++)
945                                 lgotsector[i] |= gotsector[i];
946 
947                         yaxdebug("l%d: faked (bn %2d) sec %4d,%3d dspr, ob=[%2d,%2d], sn=%4d, %.3f ms",
948                                  yax_globallev-YAX_MAXDRAWS, j, k, yax_spritesortcnt[yax_globallev]-odsprcnt,
949                                  ourbunch[0],ourbunch[1],sectnum,
950                                  (double)(1000*(timerGetPerformanceCounter()-t))/u64tickspersec);
951                     }
952 
953                     if (ourbunch[cf]==j)
954                     {
955                         ourbunch[cf] = yax_getbunch(k, cf);
956                         sectnum = k;
957                     }
958                 }
959 
960                 if (numhere > 1 && lev != YAX_MAXDRAWS-1)
961                     Bmemcpy(gotsector, lgotsector, (numsectors+7)>>3);
962             }
963 
964             if (numhere==0 || lev==YAX_MAXDRAWS-1)
965             {
966                 // no new bunches or max level reached
967                 maxlev[cf] = lev - (numhere==0);
968                 break;
969             }
970         }
971     }
972 
973 //    yax_globalcf = -1;
974 
975     // now comes the real drawing!
976     g_nodraw = 0;
977     scansector_collectsprites = 0;
978 
979     if (editstatus==1 && in3dmode())
980     {
981         if (videoGetRenderMode() == REND_CLASSIC)
982         {
983             videoBeginDrawing();
984             draw_rainbow_background();
985             videoEndDrawing();
986         }
987 #ifdef USE_OPENGL
988         else
989         {
990             glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
991         }
992 #endif
993     }
994 
995 #ifdef USE_OPENGL
996     if (videoGetRenderMode() == REND_POLYMOST)
997     {
998         glClear(GL_DEPTH_BUFFER_BIT);
999         yax_polymostclearzbuffer = 0;
1000     }
1001 #endif
1002 
1003     for (cf=0; cf<2; cf++)
1004     {
1005         yax_globalcf = cf;
1006 
1007         for (lev=maxlev[cf]; lev>=0; lev--)
1008         {
1009             yax_globallev = YAX_MAXDRAWS + (-1 + 2*cf)*(lev+1);
1010             scansector_collectsprites = (lev == YAX_MAXDRAWS-1);
1011 
1012             for (bnchcnt=bnchbeg[lev][cf]; bnchcnt<bnchend[lev][cf]; bnchcnt++)
1013             {
1014                 j = bunches[cf][bnchcnt];  // the actual bunchnum...
1015                 k = bunchsec[j];  // best start-drawing sector
1016                 yax_globalbunch = j;
1017 #ifdef YAX_DEBUG
1018                 t=timerGetPerformanceCounter();
1019 #endif
1020                 yax_tweakpicnums(j, cf, 0);
1021                 if (k < 0)
1022                     continue;
1023 
1024                 yax_nomaskdidit = 0;
1025                 for (nmp=r_tror_nomaskpass; nmp>=0; nmp--)
1026                 {
1027                     yax_nomaskpass = nmp;
1028                     renderDrawRoomsQ16(globalposx,globalposy,globalposz,qglobalang,horiz,k+MAXSECTORS);  // +MAXSECTORS: force
1029 
1030                     if (nmp==1)
1031                     {
1032                         yaxdebug("nm1 l%d: DRAWN (bn %2d) sec %4d,          %.3f ms",
1033                                  yax_globallev-YAX_MAXDRAWS, j, k,
1034                                  (double)(1000*(timerGetPerformanceCounter()-t))/u64tickspersec);
1035 
1036                         if (!yax_nomaskdidit)
1037                         {
1038                             yax_nomaskpass = 0;
1039                             break;  // no need to draw the same stuff twice
1040                         }
1041                         Bmemcpy(yax_gotsector, gotsector, (numsectors+7)>>3);
1042                     }
1043                 }
1044 
1045                 if (!scansector_collectsprites)
1046                     spritesortcnt = 0;
1047                 yax_copytsprites();
1048                 yaxdebug("nm0 l%d: DRAWN (bn %2d) sec %4d,%3d tspr, %.3f ms",
1049                          yax_globallev-YAX_MAXDRAWS, j, k, spritesortcnt,
1050                          (double)(1000*(timerGetPerformanceCounter()-t))/u64tickspersec);
1051 
1052                 SpriteAnimFunc(globalposx, globalposy, globalposz, globalang, smoothr);
1053                 renderDrawMasks();
1054             }
1055 
1056             if (lev < maxlev[cf])
1057                 for (bnchcnt=bnchbeg[lev+1][cf]; bnchcnt<bnchend[lev+1][cf]; bnchcnt++)
1058                     yax_tweakpicnums(bunches[cf][bnchcnt], cf, 1);  // restore picnums
1059         }
1060     }
1061 
1062 #ifdef YAX_DEBUG
1063     t=timerGetPerformanceCounter();
1064 #endif
1065     yax_globalcf = -1;
1066     yax_globalbunch = -1;
1067     yax_globallev = YAX_MAXDRAWS;
1068     scansector_collectsprites = 0;
1069 
1070     // draw base level
1071     renderDrawRoomsQ16(globalposx,globalposy,globalposz,qglobalang,horiz,
1072               osectnum + MAXSECTORS*didmirror);
1073 //    if (scansector_collectsprites)
1074 //        spritesortcnt = 0;
1075     yax_copytsprites();
1076     yaxdebug("DRAWN base level sec %d,%3d tspr, %.3f ms", osectnum,
1077              spritesortcnt, (double)(1000*(timerGetPerformanceCounter()-t))/u64tickspersec);
1078     scansector_collectsprites = 1;
1079 
1080     for (cf=0; cf<2; cf++)
1081         if (maxlev[cf] >= 0)
1082             for (bnchcnt=bnchbeg[0][cf]; bnchcnt<bnchend[0][cf]; bnchcnt++)
1083                 yax_tweakpicnums(bunches[cf][bnchcnt], cf, 1);  // restore picnums
1084 
1085 #ifdef ENGINE_SCREENSHOT_DEBUG
1086     engine_screenshot = 0;
1087 #endif
1088 
1089 #ifdef YAX_DEBUG_YMOSTS
1090     if (videoGetRenderMode() == REND_CLASSIC && numyaxbunches>0)
1091     {
1092         char purple = paletteGetClosestColor(255, 0, 255);
1093         char yellow = paletteGetClosestColor(255, 255, 0);
1094 
1095         videoBeginDrawing();
1096         for (i=0; i<numyaxbunches; i++)
1097         {
1098             int32_t x, x1;
1099 
1100             if ((haveymost[i>>3]&(1<<i&7))==0)
1101                 continue;
1102 
1103             x1 = i*xdimen;
1104 
1105             for (x=x1; x<x1+xdimen; x++)
1106             {
1107                 if (yumost[x] >= 0 && yumost[x] < ydim && (x&1))
1108                     *((char *)frameplace + yumost[x]*bytesperline + x-x1) = purple;
1109 
1110                 if (ydmost[x]-1 >= 0 && ydmost[x]-1 < ydim && !(x&1))
1111                     *((char *)frameplace + (ydmost[x]-1)*bytesperline + x-x1) = yellow;
1112             }
1113         }
1114         videoEndDrawing();
1115     }
1116 #endif
1117 #ifdef USE_OPENGL
1118     if (videoGetRenderMode() == REND_POLYMOST)
1119         yax_polymostclearzbuffer = 1;
1120 #endif
1121 }
1122 
1123 #endif  // defined YAX_ENABLE
1124 
1125 // must have writable frame buffer, i.e. done begindrawing()
draw_rainbow_background(void)1126 static void draw_rainbow_background(void)
1127 {
1128     int32_t y, i;
1129     const int32_t N = 240;  // don't use fullbright colors
1130     const int32_t numfull=bytesperline/N, numrest=bytesperline%N;
1131 
1132     const char *const src = palookup[0] + 256*18;
1133     char *dst = (char *)frameplace;
1134 
1135     for (y=0; y<ydim; y++)
1136     {
1137         for (i=0; i<numfull; i++)
1138             Bmemcpy(&dst[N*i], src, N);
1139         if (numrest > 0)
1140             Bmemcpy(&dst[N*i], src, numrest);
1141 
1142         dst += bytesperline;
1143     }
1144 }
1145 
1146 //
1147 // setslope
1148 //
setslope(int32_t sectnum,int32_t cf,int16_t slope)1149 void setslope(int32_t sectnum, int32_t cf, int16_t slope)
1150 {
1151     if (slope==0)
1152     {
1153         SECTORFLD(sectnum,stat, cf) &= ~2;
1154         SECTORFLD(sectnum,heinum, cf) = 0;
1155     }
1156     else
1157     {
1158         SECTORFLD(sectnum,stat, cf) |= 2;
1159         SECTORFLD(sectnum,heinum, cf) = slope;
1160     }
1161 }
1162 
1163 #define WALLS_ARE_CONSISTENT(k) ((wall[k].x == x2 && wall[k].y == y2)   \
1164                                  && ((wall[wall[k].point2]).x == x1 && (wall[wall[k].point2]).y == y1))
1165 
getscore(int32_t w1c,int32_t w1f,int32_t w2c,int32_t w2f)1166 static int32_t getscore(int32_t w1c, int32_t w1f, int32_t w2c, int32_t w2f)
1167 {
1168     if (w1c > w1f)
1169         swaplong(&w1c, &w1f);
1170     if (w2c > w2f)
1171         swaplong(&w2c, &w2f);
1172 
1173     // now: c <= f for each "wall-vline"
1174 
1175     int32_t maxceil = max(w1c, w2c);
1176     int32_t minflor = min(w1f, w2f);
1177 
1178     return minflor-maxceil;
1179 }
1180 
1181 const int16_t *chsecptr_onextwall = NULL;
1182 
checksectorpointer(int16_t i,int16_t sectnum)1183 int32_t checksectorpointer(int16_t i, int16_t sectnum)
1184 {
1185     int32_t startsec, endsec;
1186     int32_t j, k, startwall, endwall, x1, y1, x2, y2, numnewwalls=0;
1187     int32_t bestnextwall=-1, bestnextsec=-1, bestwallscore=INT32_MIN;
1188     int32_t cz[4], fz[4], tmp[2], tmpscore=0;
1189 #ifdef YAX_ENABLE
1190     int16_t cb[2], fb[2];
1191 #endif
1192 
1193 #if 0
1194     if (checksectorpointer_warn && (i<0 || i>=max(numwalls,newnumwalls)))
1195     {
1196         char buf[128];
1197         Bsprintf(buf, "WARN: checksectorpointer called with i=%d but (new)numwalls=%d", i, max(numwalls,newnumwalls));
1198         OSD_Printf("%s\n", buf);
1199         printmessage16("%s", buf);
1200         return 0;
1201     }
1202 #endif
1203 
1204     x1 = wall[i].x;
1205     y1 = wall[i].y;
1206     x2 = (wall[wall[i].point2]).x;
1207     y2 = (wall[wall[i].point2]).y;
1208 
1209     k = wall[i].nextwall;
1210     if (k >= 0)          //Check for early exit
1211     {
1212         if (WALLS_ARE_CONSISTENT(k))
1213             return 0;
1214 
1215         wall[k].nextwall = wall[k].nextsector = -1;
1216     }
1217 
1218     if ((unsigned)wall[i].nextsector < (unsigned)numsectors && wall[i].nextwall < 0)
1219     {
1220         // if we have a nextsector but no nextwall, take this as a hint
1221         // to search only the walls of that sector
1222         startsec = wall[i].nextsector;
1223         endsec = startsec+1;
1224     }
1225     else
1226     {
1227         startsec = 0;
1228         endsec = numsectors;
1229     }
1230 
1231     wall[i].nextsector = wall[i].nextwall = -1;
1232 
1233     if (chsecptr_onextwall && (k=chsecptr_onextwall[i])>=0 && wall[k].nextwall<0)
1234     {
1235         // old next wall found
1236         if (WALLS_ARE_CONSISTENT(k))
1237         {
1238             j = sectorofwall(k);
1239 
1240             wall[i].nextsector = j;
1241             wall[i].nextwall = k;
1242             wall[k].nextsector = sectnum;
1243             wall[k].nextwall = i;
1244 
1245             return 1;
1246         }
1247     }
1248 
1249     for (j=startsec; j<endsec; j++)
1250     {
1251         if (j == sectnum)
1252             continue;
1253 
1254         YAX_SKIPSECTOR(j);
1255 
1256         startwall = sector[j].wallptr;
1257         endwall = startwall + sector[j].wallnum;
1258         for (k=startwall; k<endwall; k++)
1259         {
1260             if (!WALLS_ARE_CONSISTENT(k))
1261                 continue;
1262 
1263             // Don't create link if the other side is connected to another wall.
1264             // The nextwall relation should be definitely one-to-one at all times!
1265             if (wall[k].nextwall>=0 && wall[k].nextwall != i)
1266                 continue;
1267 #ifdef YAX_ENABLE
1268             yax_getbunches(sectnum, &cb[0], &fb[0]);
1269             yax_getbunches(j, &cb[1], &fb[1]);
1270 
1271             if ((cb[0]>=0 && cb[0]==cb[1]) || (fb[0]>=0 && fb[0]==fb[1]))
1272             {
1273                 tmpscore = INT32_MAX;
1274             }
1275             else
1276 #endif
1277             {
1278                 getzsofslope(sectnum, x1,y1, &cz[0],&fz[0]);
1279                 getzsofslope(sectnum, x2,y2, &cz[1],&fz[1]);
1280                 getzsofslope(j, x1,y1, &cz[2],&fz[2]);
1281                 getzsofslope(j, x2,y2, &cz[3],&fz[3]);
1282 
1283                 tmp[0] = getscore(cz[0],fz[0], cz[2],fz[2]);
1284                 tmp[1] = getscore(cz[1],fz[1], cz[3],fz[3]);
1285 
1286                 if ((tmp[0]^tmp[1]) >= 0)
1287                     tmpscore = tmp[0]+tmp[1];
1288                 else
1289                     tmpscore = max(tmp[0], tmp[1]);
1290             }
1291 
1292             if (bestnextwall == -1 || tmpscore > bestwallscore)
1293             {
1294                 bestwallscore = tmpscore;
1295                 bestnextwall = k;
1296                 bestnextsec = j;
1297             }
1298 
1299             numnewwalls++;
1300         }
1301     }
1302 
1303     // sectnum -2 means dry run
1304     if (bestnextwall >= 0 && sectnum!=-2)
1305 #ifdef YAX_ENABLE
1306         // for walls with TROR neighbors, be conservative in case if score <=0
1307         // (meaning that no wall area is mutually visible) -- it could be that
1308         // another sector is a better candidate later on
1309         if ((yax_getnextwall(i, 0)<0 && yax_getnextwall(i, 1)<0) || bestwallscore>0)
1310 #endif
1311         {
1312 //    initprintf("w%d new nw=%d (score %d)\n", i, bestnextwall, bestwallscore)
1313             wall[i].nextsector = bestnextsec;
1314             wall[i].nextwall = bestnextwall;
1315             wall[bestnextwall].nextsector = sectnum;
1316             wall[bestnextwall].nextwall = i;
1317         }
1318 
1319     return numnewwalls;
1320 }
1321 
1322 #undef WALLS_ARE_CONSISTENT
1323 
1324 int32_t xb1[MAXWALLSB];  // Polymost uses this as a temp array
1325 static int32_t yb1[MAXWALLSB], xb2[MAXWALLSB], yb2[MAXWALLSB];
1326 int32_t rx1[MAXWALLSB], ry1[MAXWALLSB];
1327 static int32_t rx2[MAXWALLSB], ry2[MAXWALLSB];
1328 int16_t bunchp2[MAXWALLSB], thesector[MAXWALLSB];
1329 
1330 int16_t bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB];
1331 
1332 static int32_t nodesperline, ysavecnt;
1333 static int16_t *smost, *umost, *dmost, *bakumost, *bakdmost;
1334 static int16_t *uplc, *dplc, *uwall, *dwall;
1335 static int32_t *swplc, *lplc, *swall, *lwall;
1336 #ifdef HIGH_PRECISION_SPRITE
1337 static float *swallf;
1338 #endif
1339 
1340 uint8_t* mirrorBuffer;
1341 
1342 static int32_t smostcnt;
1343 static int32_t smoststart[MAXWALLSB];
1344 static char smostwalltype[MAXWALLSB];
1345 static int32_t smostwall[MAXWALLSB], smostwallcnt = -1;
1346 
1347 static vec3_t spritesxyz[MAXSPRITESONSCREEN+1];
1348 
1349 int32_t xdimen = -1, xdimenrecip, halfxdimen, xdimenscale, xdimscale;
1350 float fxdimen = -1.f;
1351 int32_t ydimen;
1352 intptr_t frameoffset;
1353 
1354 static int32_t nrx1[8], nry1[8], nrx2[8], nry2[8]; // JBF 20031206: Thanks Ken
1355 
1356 int32_t rxi[8], ryi[8];
1357 static int32_t rzi[8], rxi2[8], ryi2[8], rzi2[8];
1358 static int32_t xsi[8], ysi[8], horizycent;
1359 static int32_t *horizlookup=0, *horizlookup2=0;
1360 
1361 int32_t globalposx, globalposy, globalposz, globalhoriz;
1362 fix16_t qglobalhoriz;
1363 float fglobalposx, fglobalposy, fglobalposz;
1364 int16_t globalang, globalcursectnum;
1365 fix16_t qglobalang;
1366 int32_t globalpal, cosglobalang, singlobalang;
1367 int32_t cosviewingrangeglobalang, sinviewingrangeglobalang;
1368 static int32_t globaluclip, globaldclip;
1369 int32_t globvis, globalvisibility;
1370 int32_t globalhisibility, globalpisibility, globalcisibility;
1371 #ifdef USE_OPENGL
1372 int32_t globvis2, globalvisibility2, globalhisibility2, globalpisibility2, globalcisibility2;
1373 #endif
1374 //char globparaceilclip, globparaflorclip;
1375 
1376 int32_t xyaspect;
1377 static int32_t viewingrangerecip;
1378 
1379 static char globalxshift, globalyshift;
1380 static int32_t globalxpanning, globalypanning;
1381 int32_t globalshade, globalorientation;
1382 int16_t globalpicnum;
1383 static int16_t globalshiftval;
1384 #ifdef HIGH_PRECISION_SPRITE
1385 static int64_t globalzd;
1386 #else
1387 static int32_t globalzd;
1388 #endif
1389 static int32_t globalyscale;
1390 static int32_t globalxspan, globalyspan, globalispow2=1;  // true if texture has power-of-two x and y size
1391 static intptr_t globalbufplc;
1392 
1393 static int32_t globaly1, globalx2;
1394 
1395 int16_t sectorborder[256];
1396 int32_t ydim16, qsetmode = 0;
1397 int16_t pointhighlight=-1, linehighlight=-1, sectorhighlight = -1, highlightcnt=0;
1398 static int32_t *lastx;
1399 
1400 int32_t halfxdim16, midydim16;
1401 
1402 typedef struct
1403 {
1404     int32_t sx, sy, z;
1405     int16_t a, picnum;
1406     int8_t dashade;
1407     char dapalnum, dastat;
1408     uint8_t daalpha, dablend;
1409     char pagesleft;
1410     int32_t cx1, cy1, cx2, cy2;
1411     int32_t uniqid;    //JF extension
1412 } permfifotype;
1413 static permfifotype permfifo[MAXPERMS];
1414 static int32_t permhead = 0, permtail = 0;
1415 
1416 EDUKE32_STATIC_ASSERT(MAXWALLSB < INT16_MAX);
1417 int16_t numscans, numbunches;
1418 static int16_t numhits;
1419 
1420 uint8_t vgapal16[4*256] =
1421 {
1422     0,0,0,0,
1423     170,0,0,0,
1424     0,170,0,0,
1425     170,170,0,0,
1426     0,0,170,0,
1427     170,0,170,0,
1428     0,85,170,0,
1429     170,170,170,0,
1430     85,85,85,0,
1431     255,85,85,0,
1432     85,255,85,0,
1433     255,255,85,0,
1434     85,85,255,0,
1435     255,85,255,0,
1436     85,255,255,0,
1437     255,255,255,0
1438 };
1439 
1440 int16_t searchit;
1441 int32_t searchx = -1, searchy;                          //search input
1442 int16_t searchsector, searchwall, searchstat;     //search output
1443 
1444 // SEARCHBOTTOMWALL:
1445 //   When aiming at a the bottom part of a 2-sided wall whose bottom part
1446 //   is swapped (.cstat&2), searchbottomwall equals that wall's .nextwall. In all
1447 //   other cases (when aiming at a wall), searchbottomwall equals searchwall.
1448 //
1449 // SEARCHISBOTTOM:
1450 //  When aiming at a 2-sided wall, 1 if aiming at the bottom part, 0 else
1451 int16_t searchbottomwall, searchisbottom;
1452 
1453 char inpreparemirror = 0;
1454 static int32_t mirrorsx1, mirrorsy1, mirrorsx2, mirrorsy2;
1455 
1456 #define MAXSETVIEW 4
1457 
1458 static int32_t setviewcnt = 0; // interface layers use this now
1459 static intptr_t bakframeplace[MAXSETVIEW];
1460 static int32_t bakxsiz[MAXSETVIEW], bakysiz[MAXSETVIEW];
1461 static vec2_t bakwindowxy1[MAXSETVIEW], bakwindowxy2[MAXSETVIEW];
1462 #ifdef USE_OPENGL
1463 static int32_t bakrendmode;
1464 #endif
1465 static int32_t baktile;
1466 
1467 #ifdef APPNAME
1468 char apptitle[256] = APPNAME;
1469 #else
1470 char apptitle[256] = "Build Engine";
1471 #endif
1472 
1473 //
1474 // Internal Engine Functions
1475 //
1476 
1477 // returns: 0=continue sprite collecting;
1478 //          1=break out of sprite collecting;
renderAddTsprite(int16_t z,int16_t sectnum)1479 int32_t renderAddTsprite(int16_t z, int16_t sectnum)
1480 {
1481     auto const spr = (uspriteptr_t)&sprite[z];
1482 #ifdef YAX_ENABLE
1483     if (g_nodraw==0)
1484     {
1485         if (numyaxbunches==0)
1486         {
1487 #endif
1488             if (spritesortcnt >= maxspritesonscreen)
1489                 return 1;
1490 
1491             renderAddTSpriteFromSprite(z);
1492 
1493 #ifdef YAX_ENABLE
1494         }
1495     }
1496     else
1497         if (yax_nomaskpass==0)
1498     {
1499         int16_t *sortcnt = &yax_spritesortcnt[yax_globallev];
1500 
1501         if (*sortcnt >= maxspritesonscreen)
1502             return 1;
1503 
1504         yax_tsprite[yax_globallev][*sortcnt] = z;
1505         if (yax_globalbunch >= 0)
1506         {
1507             yax_tsprite[yax_globallev][*sortcnt] |= (MAXSPRITES|(MAXSPRITES<<1));
1508             yax_tsprfrombunch[yax_globallev][*sortcnt] = yax_globalbunch;
1509         }
1510         (*sortcnt)++;
1511 
1512         // now check whether the tsprite needs duplication into another level
1513         if ((spr->cstat&48)==32)
1514             return 0;
1515 
1516         int16_t cb, fb;
1517 
1518         yax_getbunches(sectnum, &cb, &fb);
1519         if (cb < 0 && fb < 0)
1520             return 0;
1521 
1522         int32_t spheight;
1523         int16_t spzofs = spriteheightofs(z, &spheight, 1);
1524 
1525         // TODO: get*zofslope?
1526         if (cb>=0 && spr->z+spzofs-spheight < sector[sectnum].ceilingz)
1527         {
1528             sortcnt = &yax_spritesortcnt[yax_globallev-1];
1529             if (*sortcnt < maxspritesonscreen)
1530             {
1531                 yax_tsprite[yax_globallev-1][*sortcnt] = z|MAXSPRITES;
1532                 (*sortcnt)++;
1533             }
1534         }
1535         if (fb>=0 && spr->z+spzofs > sector[sectnum].floorz)
1536         {
1537             sortcnt = &yax_spritesortcnt[yax_globallev+1];
1538             if (*sortcnt < maxspritesonscreen)
1539             {
1540                 yax_tsprite[yax_globallev+1][*sortcnt] = z|(MAXSPRITES<<1);
1541                 (*sortcnt)++;
1542             }
1543         }
1544     }
1545 #endif
1546 
1547     return 0;
1548 }
1549 
get_rel_coords(int32_t const x,int32_t const y)1550 static FORCE_INLINE vec2_t get_rel_coords(int32_t const x, int32_t const y)
1551 {
1552     return { dmulscale6(y, cosglobalang, -x, singlobalang),
1553              dmulscale6(x, cosviewingrangeglobalang, y, sinviewingrangeglobalang) };
1554 }
1555 
1556 // Note: the returned y coordinates are not actually screen coordinates, but
1557 // potentially clipped player-relative y coordinates.
get_screen_coords(const vec2_t & p1,const vec2_t & p2,int32_t * sx1ptr,int32_t * sy1ptr,int32_t * sx2ptr,int32_t * sy2ptr)1558 static int get_screen_coords(const vec2_t &p1, const vec2_t &p2,
1559                              int32_t *sx1ptr, int32_t *sy1ptr,
1560                              int32_t *sx2ptr, int32_t *sy2ptr)
1561 {
1562     int32_t sx1, sy1, sx2, sy2;
1563 
1564     // First point.
1565 
1566     if (p1.x >= -p1.y)
1567     {
1568         if (p1.x > p1.y || p1.y == 0)
1569             return 0;
1570 
1571         sx1 = halfxdimen + scale(p1.x, halfxdimen, p1.y)
1572             + (p1.x >= 0);  // Fix for SIGNED divide
1573         if (sx1 >= xdimen)
1574             sx1 = xdimen-1;
1575 
1576         sy1 = p1.y;
1577     }
1578     else
1579     {
1580         if (p2.x < -p2.y)
1581             return 0;
1582 
1583         sx1 = 0;
1584 
1585         int32_t tempint = (p1.x + p1.y) - (p2.x + p2.y);
1586         if (tempint == 0)
1587             return 0;
1588         sy1 = p1.y + scale(p2.y-p1.y, p1.x+p1.y, tempint);
1589     }
1590 
1591     if (sy1 < 256)
1592         return 0;
1593 
1594     // Second point.
1595 
1596     if (p2.x <= p2.y)
1597     {
1598         if (p2.x < -p2.y || p2.y == 0)
1599             return 0;
1600 
1601         sx2 = halfxdimen + scale(p2.x,halfxdimen,p2.y) - 1
1602             + (p2.x >= 0);  // Fix for SIGNED divide
1603         if (sx2 >= xdimen)
1604             sx2 = xdimen-1;
1605 
1606         sy2 = p2.y;
1607     }
1608     else
1609     {
1610         if (p1.x > p1.y)
1611             return 0;
1612 
1613         sx2 = xdimen-1;
1614 
1615         int32_t const tempint = (p1.y - p1.x) + (p2.x - p2.y);
1616 
1617         sy2 = p1.y + scale(p2.y-p1.y, p1.y-p1.x, tempint);
1618     }
1619 
1620     if (sy2 < 256 || sx1 > sx2)
1621         return 0;
1622 
1623     *sx1ptr = sx1; *sy1ptr = sy1;
1624     *sx2ptr = sx2; *sy2ptr = sy2;
1625 
1626     return 1;
1627 }
1628 
1629 
findUnusedTile(void)1630 static inline int findUnusedTile(void)
1631 {
1632     static int lastUnusedTile = MAXUSERTILES-1;
1633 
1634     for (; lastUnusedTile >= 0; --lastUnusedTile)
1635         if ((tilesiz[lastUnusedTile].x|tilesiz[lastUnusedTile].y) == 0)
1636             return lastUnusedTile;
1637 
1638     return -1;
1639 }
1640 
1641 //
1642 // scansector (internal)
1643 //
classicScanSector(int16_t startsectnum)1644 static void classicScanSector(int16_t startsectnum)
1645 {
1646     if (startsectnum < 0)
1647         return;
1648 
1649     if (automapping)
1650         show2dsector[startsectnum>>3] |= pow2char[startsectnum&7];
1651 
1652     sectorborder[0] = startsectnum;
1653     int32_t sectorbordercnt = 1;
1654 
1655     do
1656     {
1657         const int32_t sectnum = sectorborder[--sectorbordercnt];
1658 
1659 #ifdef YAX_ENABLE
1660         if (scansector_collectsprites)
1661 #endif
1662         for (bssize_t i=headspritesect[sectnum]; i>=0; i=nextspritesect[i])
1663         {
1664             auto const spr = (uspriteptr_t)&sprite[i];
1665 
1666             if (((spr->cstat & 0x8000) && !showinvisibility) || spr->xrepeat == 0 || spr->yrepeat == 0)
1667                 continue;
1668 
1669             vec2_t const s = { spr->x-globalposx, spr->y-globalposy };
1670 
1671             if ((spr->cstat&48) || ((coord_t)s.x*cosglobalang+(coord_t)s.y*singlobalang > 0))
1672                 if ((spr->cstat&(64+48))!=(64+16) || dmulscale6(sintable[(spr->ang+512)&2047],-s.x, sintable[spr->ang&2047],-s.y) > 0)
1673                     if (renderAddTsprite(i, sectnum))
1674                         break;
1675         }
1676 
1677         gotsector[sectnum>>3] |= pow2char[sectnum&7];
1678 
1679         const int32_t onumbunches = numbunches;
1680         const int32_t onumscans = numscans;
1681 
1682         const int32_t startwall = sector[sectnum].wallptr;
1683         const int32_t endwall = startwall + sector[sectnum].wallnum;
1684         int32_t scanfirst = numscans;
1685 
1686         vec2_t p1, p2 = { 0, 0 };
1687 
1688         for (bssize_t w=startwall; w<endwall; w++)
1689         {
1690             auto const wal = (uwallptr_t)&wall[w];
1691             const int32_t nextsectnum = wal->nextsector;
1692             auto const wal2 = (uwallptr_t)&wall[wal->point2];
1693 
1694             const int32_t x1 = wal->x-globalposx, y1 = wal->y-globalposy;
1695             const int32_t x2 = wal2->x-globalposx, y2 = wal2->y-globalposy;
1696 
1697             // The following block checks for a potential collection of a
1698             // sector that is "thin" in screen space. This is necessary because
1699             // not all sectors that are needed to be drawn may be collected via
1700             // drawalls() -> scansector() (although those are the majority).
1701             // Example: standing at exactly the intersection of a large sector
1702             // into four quadrant sub-sectors.
1703 #if 1
1704             if (nextsectnum >= 0 && (wal->cstat&32) == 0 && sectorbordercnt < ARRAY_SSIZE(sectorborder))
1705 #ifdef YAX_ENABLE
1706                 if (yax_nomaskpass==0 || !yax_isislandwall(w, !yax_globalcf) || (yax_nomaskdidit=1, 0))
1707 #endif
1708                 if ((gotsector[nextsectnum>>3]&pow2char[nextsectnum&7]) == 0)
1709                 {
1710                     // OV: E2L10
1711                     coord_t temp = (coord_t)x1*y2-(coord_t)x2*y1;
1712                     int32_t tempint = temp;
1713                     if (
1714 #ifdef YAX_ENABLE
1715                         yax_globallev == YAX_MAXDRAWS &&
1716 #endif
1717                         ((uint64_t)tempint+262144) < 524288)  // BXY_MAX?
1718                         if (mulscale5(tempint,tempint) <= (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
1719                         {
1720                             sectorborder[sectorbordercnt++] = nextsectnum;
1721                             gotsector[nextsectnum>>3] |= pow2char[nextsectnum&7];
1722                         }
1723                 }
1724 #endif
1725             p1 = (w == startwall || wall[w - 1].point2 != w) ? get_rel_coords(x1, y1) : p2;
1726             p2 = get_rel_coords(x2, y2);
1727 
1728             if (p1.y < 256 && p2.y < 256)
1729                 goto skipitaddwall;
1730 
1731             // If wall's NOT facing you
1732             if (dmulscale32(p1.x, p2.y, -p2.x, p1.y) >= 0)
1733                 goto skipitaddwall;
1734 
1735             if (numscans >= MAXWALLSB-1)
1736             {
1737                 OSD_Printf("!!numscans\n");
1738                 return;
1739             }
1740 
1741             if (get_screen_coords(p1, p2, &xb1[numscans], &yb1[numscans], &xb2[numscans], &yb2[numscans]))
1742             {
1743                 // Made it all the way!
1744                 thesector[numscans] = sectnum; thewall[numscans] = w;
1745                 rx1[numscans] = p1.x; ry1[numscans] = p1.y;
1746                 rx2[numscans] = p2.x; ry2[numscans] = p2.y;
1747                 bunchp2[numscans] = numscans+1;
1748                 numscans++;
1749             }
1750 
1751 skipitaddwall:
1752             if (wall[w].point2 < w && scanfirst < numscans)
1753                 bunchp2[numscans-1] = scanfirst, scanfirst = numscans;
1754         }
1755 
1756         for (bssize_t s=onumscans; s<numscans; s++)
1757             if (wall[thewall[s]].point2 != thewall[bunchp2[s]] || xb2[s] >= xb1[bunchp2[s]])
1758             {
1759                 bunchfirst[numbunches++] = bunchp2[s], bunchp2[s] = -1;
1760 #ifdef YAX_ENABLE
1761                 if (scansector_retfast)
1762                     return;
1763 #endif
1764             }
1765 
1766         for (bssize_t bn=onumbunches; bn<numbunches; bn++)
1767         {
1768             int32_t s;
1769             for (s=bunchfirst[bn]; bunchp2[s]>=0; s=bunchp2[s])
1770                 /* do nothing */;
1771             bunchlast[bn] = s;
1772         }
1773     }
1774     while (sectorbordercnt > 0);
1775 }
1776 
1777 #if DEBUGGINGAIDS >= 2
1778 // Printing functions for collected scans (called "wall proxies" by
1779 // http://fabiensanglard.net/duke3d/build_engine_internals.php) and
1780 // bunches. For use from within the debugger.
1781 
printscans(void)1782 void printscans(void)
1783 {
1784     static uint8_t didscan[(MAXWALLSB+7)>>3];
1785 
1786     Bmemset(didscan, 0, sizeof(didscan));
1787 
1788     for (bssize_t s=0; s<numscans; s++)
1789     {
1790         if (bunchp2[s] >= 0 && (didscan[s>>3] & pow2char[s&7])==0)
1791         {
1792             printf("scan ");
1793 
1794             int z = s;
1795             do
1796             {
1797                 const int cond = (wall[thewall[z]].point2 != thewall[bunchp2[z]] ||
1798                                   xb2[z] >= xb1[bunchp2[z]]);
1799 
1800                 printf("%s%d(%d) ", cond ? "!" : "", z, thewall[z]);
1801 
1802                 if (didscan[z>>3] & pow2char[z&7])
1803                 {
1804                     printf("*");
1805                     break;
1806                 }
1807 
1808                 didscan[z>>3] |= pow2char[z&7];
1809                 z = bunchp2[z];
1810             } while (z >= 0);
1811 
1812             printf("\n");
1813         }
1814     }
1815 }
1816 
printbunches(void)1817 void printbunches(void)
1818 {
1819     for (bssize_t bn=0; bn<numbunches; bn++)
1820     {
1821         printf("bunch %d: ", bn);
1822         for (bssize_t s=bunchfirst[bn]; s>=0; s=bunchp2[s])
1823             printf("%d(%d) ", s, thewall[s]);
1824         printf("\n");
1825     }
1826 }
1827 #endif
1828 
1829 ////////// *WALLSCAN HELPERS //////////
1830 
1831 #define WSHELPER_DECL inline //ATTRIBUTE((always_inline))
1832 
tweak_tsizes(vec2_16_t * tsiz)1833 static WSHELPER_DECL void tweak_tsizes(vec2_16_t *tsiz)
1834 {
1835     if (pow2long[picsiz[globalpicnum]&15] == tsiz->x)
1836         tsiz->x--;
1837     else
1838         tsiz->x = -tsiz->x;
1839 
1840     if (pow2long[picsiz[globalpicnum]>>4] == tsiz->y)
1841         tsiz->y = (picsiz[globalpicnum]>>4);
1842     else
1843         tsiz->y = -tsiz->y;
1844 }
1845 
calc_bufplc(intptr_t * bufplc,int32_t lw,vec2_16_t tsiz)1846 static WSHELPER_DECL void calc_bufplc(intptr_t *bufplc, int32_t lw, vec2_16_t tsiz)
1847 {
1848     // CAUTION: lw can be negative!
1849     int32_t i = lw + globalxpanning;
1850 
1851 //    if (i >= tsizx)
1852     {
1853         if (tsiz.x < 0)
1854             i = (uint32_t)i % -tsiz.x;
1855         else
1856             i &= tsiz.x;
1857     }
1858 
1859     if (tsiz.y < 0)
1860         i *= -tsiz.y;
1861     else
1862         i <<= tsiz.y;
1863 
1864 //    Bassert(i >= 0 && i < tilesiz[globalpicnum].x*tilesiz[globalpicnum].y);
1865 
1866     // Address is at the first row of tile storage (which is column-major).
1867     *bufplc = waloff[globalpicnum] + i;
1868 }
1869 
calc_vplcinc_wall(uint32_t * vplc,int32_t * vinc,inthi_t sw,int32_t y1v)1870 static WSHELPER_DECL void calc_vplcinc_wall(uint32_t *vplc, int32_t *vinc, inthi_t sw, int32_t y1v)
1871 {
1872     *vinc = sw*globalyscale;
1873     *vplc = globalzd + (uint32_t)(*vinc)*(y1v-globalhoriz+1);
1874 }
1875 
1876 #ifdef HIGH_PRECISION_SPRITE
calc_vplcinc_sprite(uint32_t * vplc,int32_t * vinc,int32_t x,int32_t y1v)1877 static WSHELPER_DECL void calc_vplcinc_sprite(uint32_t *vplc, int32_t *vinc, int32_t x, int32_t y1v)
1878 {
1879     inthi_t const tmpvinc = inthi_rintf(swallf[x]);
1880     inthi_t const tmpvplc = globalzd + tmpvinc*(y1v-globalhoriz+1);
1881 
1882     *vinc = tmpvinc;
1883     // Clamp the vertical texture coordinate!
1884     *vplc = min<inthi_t>(max<inthi_t>(0, tmpvplc), UINT32_MAX);
1885 }
1886 #endif
1887 
1888 static int32_t drawing_sprite = 0;
1889 
calc_vplcinc(uint32_t * vplc,int32_t * vinc,const int32_t * swal,int32_t x,int32_t y1v)1890 static WSHELPER_DECL void calc_vplcinc(uint32_t *vplc, int32_t *vinc, const int32_t *swal, int32_t x, int32_t y1v)
1891 {
1892 #if !defined HIGH_PRECISION_SPRITE
1893     (void)drawing_sprite;
1894 #else
1895     if (drawing_sprite)
1896         calc_vplcinc_sprite(vplc, vinc, x, y1v);
1897     else
1898 #endif
1899         calc_vplcinc_wall(vplc, vinc, swal[x], y1v);
1900 }
1901 
1902 #undef NONPOW2_YSIZE_ASM
1903 #if !defined ENGINE_USING_A_C
1904 # if defined CLASSIC_NONPOW2_YSIZE_WALLS || defined CLASSIC_NONPOW2_YSIZE_SPRITES
1905 #  define NONPOW2_YSIZE_ASM
1906 # endif
1907 #endif
1908 
1909 
1910 //
1911 // maskwallscan (internal)
1912 //
maskwallscan(int32_t x1,int32_t x2,int32_t saturatevplc)1913 static void maskwallscan(int32_t x1, int32_t x2, int32_t saturatevplc)
1914 {
1915     if (globalshiftval < 0) return;
1916     if ((uwall[x1] > ydimen) && (uwall[x2] > ydimen)) return;
1917     if ((dwall[x1] < 0) && (dwall[x2] < 0)) return;
1918 
1919     vec2_16_t tsiz = tilesiz[globalpicnum];
1920     if ((tsiz.x <= 0) || (tsiz.y <= 0)) return;
1921 
1922     setgotpic(globalpicnum);
1923 
1924     if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
1925 
1926     tweak_tsizes(&tsiz);
1927 
1928     if (EDUKE32_PREDICT_FALSE(palookup[globalpal] == NULL))
1929         globalpal = 0;
1930 
1931     intptr_t const fpalookup = FP_OFF(palookup[globalpal]);
1932 
1933     setupmvlineasm(globalshiftval, saturatevplc);
1934 
1935     int32_t x = x1;
1936     while ((x <= x2) && (startumost[x+windowxy1.x] > startdmost[x+windowxy1.x]))
1937         x++;
1938 
1939     intptr_t p = x+frameoffset;
1940 
1941     int32_t y1ve[4], y2ve[4];
1942 
1943 #ifdef NONPOW2_YSIZE_ASM
1944     if (globalshiftval==0)
1945         goto do_mvlineasm1;
1946 #endif
1947 
1948 #ifdef MULTI_COLUMN_VLINE
1949     for (; (x<=x2)&&(p&3); x++,p++)
1950     {
1951         y1ve[0] = max<int>(uwall[x],startumost[x+windowxy1.x]-windowxy1.y);
1952         y2ve[0] = min<int>(dwall[x],startdmost[x+windowxy1.x]-windowxy1.y);
1953         if (y2ve[0] <= y1ve[0]) continue;
1954 
1955         palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swall[x],globvis));
1956 
1957         calc_bufplc(&bufplce[0], lwall[x], tsiz);
1958         calc_vplcinc(&vplce[0], &vince[0], swall, x, y1ve[0]);
1959 
1960         mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0],p+ylookup[y1ve[0]]);
1961     }
1962     for (; x<=x2-3; x+=4,p+=4)
1963     {
1964         char bad = 0;
1965 
1966         for (bssize_t z=3,dax=x+3; z>=0; z--,dax--)
1967         {
1968             y1ve[z] = max<int>(uwall[dax],startumost[dax+windowxy1.x]-windowxy1.y);
1969             y2ve[z] = min<int>(dwall[dax],startdmost[dax+windowxy1.x]-windowxy1.y)-1;
1970             if (y2ve[z] < y1ve[z]) { bad += pow2char[z]; continue; }
1971 
1972             calc_bufplc(&bufplce[z], lwall[dax], tsiz);
1973             calc_vplcinc(&vplce[z], &vince[z], swall, dax, y1ve[z]);
1974         }
1975         if (bad == 15) continue;
1976 
1977         palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swall[x],globvis));
1978         palookupoffse[3] = fpalookup + getpalookupsh(mulscale16(swall[x+3],globvis));
1979 
1980         if ((palookupoffse[0] == palookupoffse[3]) && ((bad&0x9) == 0))
1981         {
1982             palookupoffse[1] = palookupoffse[0];
1983             palookupoffse[2] = palookupoffse[0];
1984         }
1985         else
1986         {
1987             palookupoffse[1] = fpalookup + getpalookupsh(mulscale16(swall[x+1],globvis));
1988             palookupoffse[2] = fpalookup + getpalookupsh(mulscale16(swall[x+2],globvis));
1989         }
1990 
1991         int32_t const u4 = max(max(y1ve[0],y1ve[1]),max(y1ve[2],y1ve[3]));
1992         int32_t const d4 = min(min(y2ve[0],y2ve[1]),min(y2ve[2],y2ve[3]));
1993 
1994         if ((bad > 0) || (u4 >= d4))
1995         {
1996             if (!(bad&1)) mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0],vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
1997             if (!(bad&2)) mvlineasm1(vince[1],palookupoffse[1],y2ve[1]-y1ve[1],vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
1998             if (!(bad&4)) mvlineasm1(vince[2],palookupoffse[2],y2ve[2]-y1ve[2],vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
1999             if (!(bad&8)) mvlineasm1(vince[3],palookupoffse[3],y2ve[3]-y1ve[3],vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
2000             continue;
2001         }
2002 
2003         if (u4 > y1ve[0]) vplce[0] = mvlineasm1(vince[0],palookupoffse[0],u4-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
2004         if (u4 > y1ve[1]) vplce[1] = mvlineasm1(vince[1],palookupoffse[1],u4-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
2005         if (u4 > y1ve[2]) vplce[2] = mvlineasm1(vince[2],palookupoffse[2],u4-y1ve[2]-1,vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
2006         if (u4 > y1ve[3]) vplce[3] = mvlineasm1(vince[3],palookupoffse[3],u4-y1ve[3]-1,vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
2007 
2008         if (d4 >= u4) mvlineasm4(d4-u4+1, (char *)(ylookup[u4]+p));
2009 
2010         intptr_t const pp = p+ylookup[d4+1];
2011 
2012         if (y2ve[0] > d4) mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-d4-1,vplce[0],bufplce[0],pp+0);
2013         if (y2ve[1] > d4) mvlineasm1(vince[1],palookupoffse[1],y2ve[1]-d4-1,vplce[1],bufplce[1],pp+1);
2014         if (y2ve[2] > d4) mvlineasm1(vince[2],palookupoffse[2],y2ve[2]-d4-1,vplce[2],bufplce[2],pp+2);
2015         if (y2ve[3] > d4) mvlineasm1(vince[3],palookupoffse[3],y2ve[3]-d4-1,vplce[3],bufplce[3],pp+3);
2016     }
2017 #endif
2018 
2019 #ifdef NONPOW2_YSIZE_ASM
2020 do_mvlineasm1:
2021 #endif
2022     for (; x<=x2; x++,p++)
2023     {
2024         y1ve[0] = max<int>(uwall[x],startumost[x+windowxy1.x]-windowxy1.y);
2025         y2ve[0] = min<int>(dwall[x],startdmost[x+windowxy1.x]-windowxy1.y);
2026         if (y2ve[0] <= y1ve[0]) continue;
2027 
2028         palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swall[x],globvis));
2029 
2030         calc_bufplc(&bufplce[0], lwall[x], tsiz);
2031         calc_vplcinc(&vplce[0], &vince[0], swall, x, y1ve[0]);
2032 
2033 #ifdef NONPOW2_YSIZE_ASM
2034         if (globalshiftval==0)
2035             mvlineasm1nonpow2(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0],p+ylookup[y1ve[0]]);
2036         else
2037 #endif
2038         mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0],p+ylookup[y1ve[0]]);
2039     }
2040 
2041     faketimerhandler();
2042 }
2043 
2044 
2045 //
2046 // wallfront (internal)
2047 //
wallfront(int32_t l1,int32_t l2)2048 int32_t wallfront(int32_t l1, int32_t l2)
2049 {
2050     vec2_t const l1vect   = wall[thewall[l1]].pos;
2051     vec2_t const l1p2vect = wall[wall[thewall[l1]].point2].pos;
2052     vec2_t const l2vect   = wall[thewall[l2]].pos;
2053     vec2_t const l2p2vect = wall[wall[thewall[l2]].point2].pos;
2054     vec2_t d = { l1p2vect.x - l1vect.x, l1p2vect.y - l1vect.y };
2055     int32_t t1 = dmulscale2(l2vect.x-l1vect.x, d.y, -d.x, l2vect.y-l1vect.y); //p1(l2) vs. l1
2056     int32_t t2 = dmulscale2(l2p2vect.x-l1vect.x, d.y, -d.x, l2p2vect.y-l1vect.y); //p2(l2) vs. l1
2057 
2058     if (t1 == 0) { if (t2 == 0) return -1; t1 = t2; }
2059     if (t2 == 0) t2 = t1;
2060 
2061     if ((t1^t2) >= 0) //pos vs. l1
2062         return (dmulscale2(globalposx-l1vect.x, d.y, -d.x, globalposy-l1vect.y) ^ t1) >= 0;
2063 
2064     d.x = l2p2vect.x-l2vect.x;
2065     d.y = l2p2vect.y-l2vect.y;
2066 
2067     t1 = dmulscale2(l1vect.x-l2vect.x, d.y, -d.x, l1vect.y-l2vect.y); //p1(l1) vs. l2
2068     t2 = dmulscale2(l1p2vect.x-l2vect.x, d.y, -d.x, l1p2vect.y-l2vect.y); //p2(l1) vs. l2
2069 
2070     if (t1 == 0) { if (t2 == 0) return -1; t1 = t2; }
2071     if (t2 == 0) t2 = t1;
2072 
2073     if ((t1^t2) >= 0) //pos vs. l2
2074         return (dmulscale2(globalposx-l2vect.x,d.y,-d.x,globalposy-l2vect.y) ^ t1) < 0;
2075 
2076     return -2;
2077 }
2078 
2079 //
2080 // spritewallfront (internal)
2081 //
spritewallfront(tspritetype const * const s,int32_t w)2082 static inline int32_t spritewallfront(tspritetype const * const s, int32_t w)
2083 {
2084     auto const wal = (uwallptr_t)&wall[w];
2085     auto const wal2 = (uwallptr_t)&wall[wal->point2];
2086     const vec2_t v = { wal->x, wal->y };
2087 
2088     return dmulscale32(wal2->x - v.x, s->y - v.y, -(s->x - v.x), wal2->y - v.y) >= 0;
2089 }
2090 
2091 //
2092 //  spritebehindwall(internal)
2093 //
2094 #if 0
2095 static int32_t spriteobstructswall(spritetype *s, int32_t w)
2096 {
2097     walltype *wal;
2098     int32_t x, y;
2099     int32_t x1, y1;
2100     int32_t x2, y2;
2101     double a1, b1, c1;
2102     double a2, b2, c2;
2103     double d1, d2;
2104 
2105     // wall line equation
2106     wal = &wall[w]; x1 = wal->x - globalposx; y1 = wal->y - globalposy;
2107     wal = &wall[wal->point2]; x2 = wal->x - globalposx; y2 = wal->y - globalposy;
2108     if ((x2 - x1) != 0)
2109         a1 = (float)(y2 - y1)/(x2 - x1);
2110     else
2111         a1 = 1e+37; // not infinite, but almost ;)
2112     b1 = -1;
2113     c1 = (y1 - (a1 * x1));
2114 
2115     // player to sprite line equation
2116     if ((s->x - globalposx) != 0)
2117         a2 = (float)(s->y - globalposy)/(s->x - globalposx);
2118     else
2119         a2 = 1e+37;
2120     b2 = -1;
2121     c2 = 0;
2122 
2123     // intersection point
2124     d1 = (float)(1) / (a1*b2 - a2*b1);
2125     x = ((b1*c2 - b2*c1) * d1);
2126     y = ((a2*c1 - a1*c2) * d1);
2127 
2128     // distance between the sprite and the player
2129     a1 = s->x - globalposx;
2130     b1 = s->y - globalposy;
2131     d1 = (a1 * a1 + b1 * b1);
2132 
2133     // distance between the intersection point and the player
2134     d2 = (x * x + y * y);
2135 
2136     // check if the sprite obstructs the wall
2137     if ((d1 < d2) && (min(x1, x2) <= x) && (x <= max(x1, x2)) && (min(y1, y2) <= y) && (y <= max(y1, y2)))
2138         return 1;
2139     else
2140         return 0;
2141 }
2142 #endif
2143 //
2144 // bunchfront (internal)
2145 //
bunchfront(int32_t b1,int32_t b2)2146 static inline int32_t bunchfront(int32_t b1, int32_t b2)
2147 {
2148     int b1f = bunchfirst[b1];
2149     int const x1b1 = xb1[b1f];
2150     int const x2b2 = xb2[bunchlast[b2]] + 1;
2151 
2152     if (x1b1 >= x2b2)
2153         return -1;
2154 
2155     int b2f = bunchfirst[b2];
2156     int const x1b2 = xb1[b2f];
2157     int const x2b1 = xb2[bunchlast[b1]] + 1;
2158 
2159     if (x1b2 >= x2b1)
2160         return -1;
2161 
2162     if (x1b1 >= x1b2)
2163     {
2164         for (; xb2[b2f] < x1b1; b2f = bunchp2[b2f]) { }
2165         return wallfront(b1f, b2f);
2166     }
2167 
2168     for (; xb2[b1f] < x1b2; b1f = bunchp2[b1f]) { }
2169     return wallfront(b1f, b2f);
2170 }
2171 
2172 
2173 //
2174 // hline (internal)
2175 //
hline(int32_t xr,int32_t yp)2176 static inline void hline(int32_t xr, int32_t yp)
2177 {
2178     int32_t const xl = lastx[yp];
2179     if (xl > xr) return;
2180     int32_t const r = horizlookup2[yp-globalhoriz+horizycent];
2181     asm1 = (inthi_t)mulscale6(globalx1, r);
2182     asm2 = (inthi_t)mulscale6(globaly2, r);
2183     int32_t const s = getpalookupsh(mulscale22(r,globvis));
2184 
2185     hlineasm4(xr-xl,0,s,(uint32_t)mulscale6(globalx2,r)+globalypanning,(uint32_t)mulscale6(globaly1,r)+globalxpanning,
2186               ylookup[yp]+xr+frameoffset);
2187 }
2188 
2189 
2190 //
2191 // slowhline (internal)
2192 //
slowhline(int32_t xr,int32_t yp)2193 static inline void slowhline(int32_t xr, int32_t yp)
2194 {
2195     int32_t const xl = lastx[yp]; if (xl > xr) return;
2196     int32_t const r = horizlookup2[yp-globalhoriz+horizycent];
2197     asm1 = (inthi_t)mulscale6(globalx1, r);
2198     asm2 = (inthi_t)mulscale6(globaly2, r);
2199 
2200     asm3 = (intptr_t)globalpalwritten + getpalookupsh(mulscale22(r,globvis));
2201     if (!(globalorientation&256))
2202     {
2203         mhline(globalbufplc,(uint32_t)mulscale6(globaly1,r)+globalxpanning-asm1*(xr-xl),(xr-xl)<<16,0L,
2204                (uint32_t)mulscale6(globalx2,r)+globalypanning-asm2*(xr-xl),ylookup[yp]+xl+frameoffset);
2205         return;
2206     }
2207     thline(globalbufplc,(uint32_t)mulscale6(globaly1,r)+globalxpanning-asm1*(xr-xl),(xr-xl)<<16,0L,
2208            (uint32_t)mulscale6(globalx2,r)+globalypanning-asm2*(xr-xl),ylookup[yp]+xl+frameoffset);
2209 }
2210 
2211 
2212 //
2213 // prepwall (internal)
2214 //
prepwall(int32_t z,uwallptr_t wal)2215 static void prepwall(int32_t z, uwallptr_t wal)
2216 {
2217     int32_t l=0, ol=0, x;
2218 
2219     int32_t walxrepeat = (wal->xrepeat<<3);
2220 
2221     //lwall calculation
2222     int32_t tmpx = xb1[z]-halfxdimen;
2223 
2224     const int32_t topinc = -(ry1[z]>>2);
2225     const int32_t botinc = (ry2[z]-ry1[z])>>8;
2226     int32_t top = mulscale5(rx1[z],xdimen) + mulscale2(topinc,tmpx);
2227     int32_t bot = mulscale11(rx1[z]-rx2[z],xdimen) + mulscale2(botinc,tmpx);
2228 
2229     const int32_t splc = mulscale19(ry1[z],xdimscale);
2230     const int32_t sinc = mulscale16(ry2[z]-ry1[z],xdimscale);
2231 
2232     x = xb1[z];
2233     if (bot != 0)
2234     {
2235         l = divscale12(top,bot);
2236         swall[x] = mulscale21(l,sinc)+splc;
2237         l *= walxrepeat;
2238         lwall[x] = (l>>18);
2239     }
2240     while (x+4 <= xb2[z])
2241     {
2242         int32_t i;
2243 
2244         top += topinc; bot += botinc;
2245         if (bot != 0)
2246         {
2247             ol = l; l = divscale12(top,bot);
2248             swall[x+4] = mulscale21(l,sinc)+splc;
2249             l *= walxrepeat;
2250             lwall[x+4] = (l>>18);
2251         }
2252 
2253         i = (ol+l)>>1;
2254 
2255         lwall[x+2] = i>>18;
2256         lwall[x+1] = (ol+i)>>19;
2257         lwall[x+3] = (l+i)>>19;
2258 
2259         swall[x+2] = (swall[x]+swall[x+4])>>1;
2260         swall[x+1] = (swall[x]+swall[x+2])>>1;
2261         swall[x+3] = (swall[x+4]+swall[x+2])>>1;
2262 
2263         x += 4;
2264     }
2265     if (x+2 <= xb2[z])
2266     {
2267         top += (topinc>>1); bot += (botinc>>1);
2268         if (bot != 0)
2269         {
2270             ol = l; l = divscale12(top,bot);
2271             swall[x+2] = mulscale21(l,sinc)+splc;
2272             l *= walxrepeat;
2273             lwall[x+2] = (l>>18);
2274         }
2275         lwall[x+1] = (l+ol)>>19;
2276         swall[x+1] = (swall[x]+swall[x+2])>>1;
2277         x += 2;
2278     }
2279     if (x+1 <= xb2[z])
2280     {
2281         bot += (botinc>>2);
2282         if (bot != 0)
2283         {
2284             l = divscale12(top+(topinc>>2),bot);
2285             swall[x+1] = mulscale21(l,sinc)+splc;
2286             lwall[x+1] = mulscale18(l,walxrepeat);
2287         }
2288     }
2289 
2290     if (lwall[xb1[z]] < 0)
2291         lwall[xb1[z]] = 0;
2292     if (lwall[xb2[z]] >= walxrepeat && walxrepeat)
2293         lwall[xb2[z]] = walxrepeat-1;
2294 
2295     if (wal->cstat&8)
2296     {
2297         walxrepeat--;
2298         for (x=xb1[z]; x<=xb2[z]; x++)
2299             lwall[x] = walxrepeat-lwall[x];
2300     }
2301 }
2302 
2303 
2304 //
2305 // animateoffs (internal)
2306 //
2307 int32_t (*animateoffs_replace)(int const tilenum, int fakevar) = NULL;
animateoffs(int const tilenum,int fakevar)2308 int32_t animateoffs(int const tilenum, int fakevar)
2309 {
2310     if (animateoffs_replace)
2311     {
2312         return animateoffs_replace(tilenum, fakevar);
2313     }
2314 
2315     int const animnum = picanm[tilenum].num;
2316 
2317     if (animnum <= 0)
2318         return 0;
2319 
2320     int const i = (int) totalclocklock >> (picanm[tilenum].sf & PICANM_ANIMSPEED_MASK);
2321     int offs = 0;
2322 
2323     switch (picanm[tilenum].sf & PICANM_ANIMTYPE_MASK)
2324     {
2325         case PICANM_ANIMTYPE_OSC:
2326         {
2327             int k = (i % (animnum << 1));
2328             offs = (k < animnum) ? k : (animnum << 1) - k;
2329         }
2330         break;
2331         case PICANM_ANIMTYPE_FWD: offs = i % (animnum + 1); break;
2332         case PICANM_ANIMTYPE_BACK: offs = -(i % (animnum + 1)); break;
2333     }
2334 
2335     return offs;
2336 }
2337 
2338 
wallmosts_finish(int16_t * mostbuf,int32_t z1,int32_t z2,int32_t ix1,int32_t iy1,int32_t ix2,int32_t iy2)2339 static inline void wallmosts_finish(int16_t *mostbuf, int32_t z1, int32_t z2,
2340                                     int32_t ix1, int32_t iy1, int32_t ix2, int32_t iy2)
2341 {
2342     const int32_t y = scale(z1, xdimenscale, iy1)<<4;
2343 
2344 #if 0
2345     // enable for paranoia:
2346     ix1 = clamp(ix1, 0, xdim-1);
2347     ix2 = clamp(ix2, 0, xdim-1);
2348     if (ix2-ix1 < 0)
2349         swaplong(&ix1, &ix2);
2350 #endif
2351     // PK 20110423: a bit consistency checking is a good thing:
2352     int32_t const tmp = (ix2 - ix1 >= 0) ? (ix2 - ix1 + 1) : 1;
2353     int32_t const yinc = tabledivide32((scale(z2, xdimenscale, iy2) << 4) - y, tmp);
2354 
2355     qinterpolatedown16short((intptr_t)&mostbuf[ix1], tmp, y + (globalhoriz << 16), yinc);
2356 
2357     mostbuf[ix1] = clamp(mostbuf[ix1], 0, ydimen);
2358     mostbuf[ix2] = clamp(mostbuf[ix2], 0, ydimen);
2359 }
2360 
2361 #ifdef CLASSIC_Z_DIFF_64
2362 typedef int64_t zint_t;
2363 
2364 // For drawvox()
mulscale16z(int32_t a,int32_t d)2365 static FORCE_INLINE zint_t mulscale16z(int32_t a, int32_t d)
2366 {
2367     return ((zint_t)a * d)>>16;
2368 }
2369 
mulscale20z(int32_t a,int32_t d)2370 static FORCE_INLINE zint_t mulscale20z(int32_t a, int32_t d)
2371 {
2372     return ((zint_t)a * d)>>20;
2373 }
2374 
dmulscale24z(int32_t a,int32_t d,int32_t S,int32_t D)2375 static FORCE_INLINE zint_t dmulscale24z(int32_t a, int32_t d, int32_t S, int32_t D)
2376 {
2377     return (((zint_t)a * d) + ((zint_t)S * D)) >> 24;
2378 }
2379 #else
2380 typedef int32_t zint_t;
2381 # define mulscale16z mulscale16
2382 # define mulscale20z mulscale20
2383 # define dmulscale24z dmulscale24
2384 #endif
2385 
2386 //
2387 // owallmost (internal)
2388 //
owallmost(int16_t * mostbuf,int32_t w,zint_t z)2389 static int32_t owallmost(int16_t *mostbuf, int32_t w, zint_t z)
2390 {
2391     z <<= 7;
2392     const zint_t s1 = mulscale20z(globaluclip,yb1[w]), s2 = mulscale20z(globaluclip,yb2[w]);
2393     const zint_t s3 = mulscale20z(globaldclip,yb1[w]), s4 = mulscale20z(globaldclip,yb2[w]);
2394     const int32_t bad = (z<s1)+((z<s2)<<1)+((z>s3)<<2)+((z>s4)<<3);
2395 
2396     int32_t ix1 = xb1[w], iy1 = yb1[w];
2397     int32_t ix2 = xb2[w], iy2 = yb2[w];
2398 
2399     if ((bad&3) == 3)
2400     {
2401         for (bssize_t i=ix1; i<=ix2; i++)
2402             mostbuf[i] = 0;
2403         return bad;
2404     }
2405 
2406     if ((bad&12) == 12)
2407     {
2408         for (bssize_t i=ix1; i<=ix2; i++)
2409             mostbuf[i] = ydimen;
2410         return bad;
2411     }
2412 
2413     if (bad&3)
2414     {
2415         int32_t t = divscale30(z-s1,s2-s1);
2416         int32_t inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
2417         int32_t xcross = xb1[w] + scale(mulscale30(yb2[w],t),xb2[w]-xb1[w],inty);
2418 
2419         if ((bad&3) == 2)
2420         {
2421             if (xb1[w] <= xcross) { iy2 = inty; ix2 = xcross; }
2422             for (bssize_t i=xcross+1; i<=xb2[w]; i++)
2423                 mostbuf[i] = 0;
2424         }
2425         else
2426         {
2427             if (xcross <= xb2[w]) { iy1 = inty; ix1 = xcross; }
2428             for (bssize_t i=xb1[w]; i<=xcross; i++)
2429                 mostbuf[i] = 0;
2430         }
2431     }
2432 
2433     if (bad&12)
2434     {
2435         int32_t t = divscale30(z-s3,s4-s3);
2436         int32_t inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
2437         int32_t xcross = xb1[w] + scale(mulscale30(yb2[w],t),xb2[w]-xb1[w],inty);
2438 
2439         if ((bad&12) == 8)
2440         {
2441             if (xb1[w] <= xcross) { iy2 = inty; ix2 = xcross; }
2442             for (bssize_t i=xcross+1; i<=xb2[w]; i++)
2443                 mostbuf[i] = ydimen;
2444         }
2445         else
2446         {
2447             if (xcross <= xb2[w]) { iy1 = inty; ix1 = xcross; }
2448             for (bssize_t i=xb1[w]; i<=xcross; i++)
2449                 mostbuf[i] = ydimen;
2450         }
2451     }
2452 
2453     wallmosts_finish(mostbuf, z, z, ix1, iy1, ix2, iy2);
2454 
2455     return bad;
2456 }
2457 
wallmost_getz(int32_t fw,int32_t t,zint_t z,int32_t x1,int32_t y1,int32_t x2,int32_t y2,int32_t xv,int32_t yv,int32_t dx,int32_t dy)2458 static inline zint_t wallmost_getz(int32_t fw, int32_t t, zint_t z,
2459                                    int32_t x1, int32_t y1, int32_t x2, int32_t y2,
2460                                    int32_t xv, int32_t yv, int32_t dx, int32_t dy)
2461 {
2462     // XXX: OVERFLOW with huge sectors and sloped ceilngs/floors!
2463     int32_t i = xv*(y1-globalposy) - yv*(x1-globalposx);
2464     const int32_t j = yv*x2 - xv*y2;
2465 
2466     if (klabs(j) > klabs(i>>3))
2467         i = divscale28(i,j);
2468 
2469     return dmulscale24z(dx*t, mulscale20z(y2,i)+((y1-wall[fw].y)<<8),
2470                         -dy*t, mulscale20z(x2,i)+((x1-wall[fw].x)<<8)) + ((z-globalposz)<<7);
2471 }
2472 
2473 //
2474 // wallmost (internal)
2475 //
wallmost(int16_t * mostbuf,int32_t w,int32_t sectnum,char dastat)2476 static int32_t wallmost(int16_t *mostbuf, int32_t w, int32_t sectnum, char dastat)
2477 {
2478     int32_t t, z;
2479     int32_t xv, yv;
2480 
2481     if (dastat == 0)
2482     {
2483         z = sector[sectnum].ceilingz-globalposz;
2484         if ((sector[sectnum].ceilingstat&2) == 0)
2485             return owallmost(mostbuf,w,z);
2486     }
2487     else
2488     {
2489         z = sector[sectnum].floorz-globalposz;
2490         if ((sector[sectnum].floorstat&2) == 0)
2491             return owallmost(mostbuf,w,z);
2492     }
2493 
2494     const int wi = thewall[w];
2495     if (wi == sector[sectnum].wallptr)
2496         return owallmost(mostbuf,w,z);
2497 
2498     auto const wal = (uwallptr_t)&wall[wi];
2499     const int32_t x1 = wal->x, x2 = wall[wal->point2].x-x1;
2500     const int32_t y1 = wal->y, y2 = wall[wal->point2].y-y1;
2501 
2502     const int w1 = sector[sectnum].wallptr, w2 = wall[w1].point2;
2503     const int32_t dx = wall[w2].x-wall[w1].x, dy = wall[w2].y-wall[w1].y;
2504     const int32_t dasqr = krecipasm(nsqrtasm(uhypsq(dx,dy)));
2505 
2506     if (dastat == 0)
2507     {
2508         t = mulscale15(sector[sectnum].ceilingheinum, dasqr);
2509         z = sector[sectnum].ceilingz;
2510     }
2511     else
2512     {
2513         t = mulscale15(sector[sectnum].floorheinum,dasqr);
2514         z = sector[sectnum].floorz;
2515     }
2516 
2517 
2518     if (xb1[w] == 0)
2519         { xv = cosglobalang+sinviewingrangeglobalang; yv = singlobalang-cosviewingrangeglobalang; }
2520     else
2521         { xv = x1-globalposx; yv = y1-globalposy; }
2522     zint_t z1 = wallmost_getz(w1, t, z, x1, y1, x2, y2, xv, yv, dx, dy);
2523 
2524 
2525     if (xb2[w] == xdimen-1)
2526         { xv = cosglobalang-sinviewingrangeglobalang; yv = singlobalang+cosviewingrangeglobalang; }
2527     else
2528         { xv = (x2+x1)-globalposx; yv = (y2+y1)-globalposy; }
2529     zint_t z2 = wallmost_getz(w1, t, z, x1, y1, x2, y2, xv, yv, dx, dy);
2530 
2531 
2532     const zint_t s1 = mulscale20(globaluclip,yb1[w]), s2 = mulscale20(globaluclip,yb2[w]);
2533     const zint_t s3 = mulscale20(globaldclip,yb1[w]), s4 = mulscale20(globaldclip,yb2[w]);
2534     const int32_t bad = (z1<s1)+((z2<s2)<<1)+((z1>s3)<<2)+((z2>s4)<<3);
2535 
2536     int32_t ix1 = xb1[w], ix2 = xb2[w];
2537     int32_t iy1 = yb1[w], iy2 = yb2[w];
2538 
2539     if ((bad&3) == 3)
2540     {
2541         for (bssize_t i=ix1; i<=ix2; i++)
2542             mostbuf[i] = 0;
2543         return bad;
2544     }
2545 
2546     if ((bad&12) == 12)
2547     {
2548         for (bssize_t i=ix1; i<=ix2; i++)
2549             mostbuf[i] = ydimen;
2550         return bad;
2551     }
2552 
2553     const int32_t oz1 = z1, oz2 = z2;
2554 
2555     if (bad&3)
2556     {
2557         //inty = intz / (globaluclip>>16)
2558         t = divscale30(oz1-s1,s2-s1+oz1-oz2);
2559         int32_t inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
2560         int32_t intz = oz1 + mulscale30(oz2-oz1,t);
2561         int32_t xcross = xb1[w] + scale(mulscale30(yb2[w],t),xb2[w]-xb1[w],inty);
2562 
2563         //t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4));
2564         //inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
2565         //intz = z1 + mulscale30(z2-z1,t);
2566 
2567         if ((bad&3) == 2)
2568         {
2569             if (xb1[w] <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; }
2570             for (bssize_t i=xcross+1; i<=xb2[w]; i++)
2571                 mostbuf[i] = 0;
2572         }
2573         else
2574         {
2575             if (xcross <= xb2[w]) { z1 = intz; iy1 = inty; ix1 = xcross; }
2576             for (bssize_t i=xb1[w]; i<=xcross; i++)
2577                 mostbuf[i] = 0;
2578         }
2579     }
2580 
2581     if (bad&12)
2582     {
2583         //inty = intz / (globaldclip>>16)
2584         t = divscale30(oz1-s3,s4-s3+oz1-oz2);
2585         int32_t inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
2586         int32_t intz = oz1 + mulscale30(oz2-oz1,t);
2587         int32_t xcross = xb1[w] + scale(mulscale30(yb2[w],t),xb2[w]-xb1[w],inty);
2588 
2589         //t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4));
2590         //inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
2591         //intz = z1 + mulscale30(z2-z1,t);
2592 
2593         if ((bad&12) == 8)
2594         {
2595             if (xb1[w] <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; }
2596             for (bssize_t i=xcross+1; i<=xb2[w]; i++)
2597                 mostbuf[i] = ydimen;
2598         }
2599         else
2600         {
2601             if (xcross <= xb2[w]) { z1 = intz; iy1 = inty; ix1 = xcross; }
2602             for (bssize_t i=xb1[w]; i<=xcross; i++)
2603                 mostbuf[i] = ydimen;
2604         }
2605     }
2606 
2607     wallmosts_finish(mostbuf, z1, z2, ix1, iy1, ix2, iy2);
2608 
2609     return bad;
2610 }
2611 
2612 
2613 // globalpicnum --> globalxshift, globalyshift
calc_globalshifts(void)2614 static void calc_globalshifts(void)
2615 {
2616     globalxshift = (8-(picsiz[globalpicnum]&15));
2617     globalyshift = (8-(picsiz[globalpicnum]>>4));
2618     if (globalorientation&8) { globalxshift++; globalyshift++; }
2619     // PK: the following can happen for large (>= 512) tile sizes.
2620     // NOTE that global[xy]shift are unsigned chars.
2621     if (globalxshift > 31) globalxshift=0;
2622     if (globalyshift > 31) globalyshift=0;
2623 }
2624 
setup_globals_cf1(usectorptr_t sec,int32_t pal,int32_t zd,int32_t picnum,int32_t shade,int32_t stat,int32_t xpanning,int32_t ypanning,int32_t x1)2625 static int32_t setup_globals_cf1(usectorptr_t sec, int32_t pal, int32_t zd,
2626                                  int32_t picnum, int32_t shade, int32_t stat,
2627                                  int32_t xpanning, int32_t ypanning, int32_t x1)
2628 {
2629     int32_t i;
2630 
2631     if (palookup[pal] != globalpalwritten)
2632     {
2633         globalpalwritten = palookup[pal];
2634         if (!globalpalwritten) globalpalwritten = palookup[globalpal];  // JBF: fixes null-pointer crash
2635         setpalookupaddress(globalpalwritten);
2636     }
2637 
2638     globalzd = zd;
2639     if (globalzd > 0) return 1;
2640 
2641     globalpicnum = picnum;
2642     if ((unsigned)globalpicnum >= MAXTILES) globalpicnum = 0;
2643     tileUpdatePicnum(&globalpicnum, 0);
2644     setgotpic(globalpicnum);
2645     if ((tilesiz[globalpicnum].x <= 0) || (tilesiz[globalpicnum].y <= 0)) return 1;
2646     if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
2647 
2648     globalbufplc = waloff[globalpicnum];
2649 
2650     globalshade = shade;
2651     globvis = globalcisibility;
2652     if (sec->visibility != 0) globvis = mulscale4(globvis, (uint8_t)(sec->visibility+16));
2653     globalorientation = stat;
2654 
2655     if ((globalorientation&64) == 0)
2656     {
2657         globalx1 = singlobalang; globalx2 = singlobalang;
2658         globaly1 = cosglobalang; globaly2 = cosglobalang;
2659         globalxpanning = ((inthi_t)globalposx<<20);
2660         globalypanning = -((inthi_t)globalposy<<20);
2661     }
2662     else
2663     {
2664         vec2_t const xy = { wall[wall[sec->wallptr].point2].x - wall[sec->wallptr].x,
2665                             wall[wall[sec->wallptr].point2].y - wall[sec->wallptr].y };
2666         i = nsqrtasm(uhypsq(xy.x,xy.y)); if (i == 0) i = 1024; else i = tabledivide32(1048576, i);
2667         int const wcos = mulscale6(xy.x, i), wsin = mulscale6(xy.y, i);
2668         globalx1 = dmulscale14(wcos,singlobalang,-wsin,cosglobalang);
2669         globaly1 = dmulscale14(wcos,cosglobalang,wsin,singlobalang);
2670         globalx2 = -globalx1;
2671         globaly2 = -globaly1;
2672 
2673         globalxpanning = (coord_t)((globalposx - wall[sec->wallptr].x)<<6) * wcos + (coord_t)((globalposy - wall[sec->wallptr].y)<<6) * wsin;
2674         globalypanning = (coord_t)((globalposy - wall[sec->wallptr].y)<<6) * wcos - (coord_t)((globalposx - wall[sec->wallptr].x)<<6) * wsin;
2675     }
2676     globalx2 = mulscale16(globalx2,viewingrangerecip);
2677     globaly1 = mulscale16(globaly1,viewingrangerecip);
2678 
2679     calc_globalshifts();
2680 
2681     if ((globalorientation&0x4) > 0)
2682     {
2683         i = globalxpanning; globalxpanning = globalypanning; globalypanning = i;
2684         i = globalx2; globalx2 = -globaly1; globaly1 = -i;
2685         i = globalx1; globalx1 = globaly2; globaly2 = i;
2686     }
2687     if ((globalorientation&0x10) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalxpanning = -(inthi_t)globalxpanning;
2688     if ((globalorientation&0x20) > 0) globalx2 = -globalx2, globaly2 = -globaly2, globalypanning = -(inthi_t)globalypanning;
2689     globalx1 <<= globalxshift; globaly1 <<= globalxshift;
2690     globalx2 <<= globalyshift;  globaly2 <<= globalyshift;
2691     globalxpanning <<= globalxshift; globalypanning <<= globalyshift;
2692     globalxpanning = (uint32_t)globalxpanning + (xpanning<<24);
2693     globalypanning = (uint32_t)globalypanning + (ypanning<<24);
2694     globaly1 = (-globalx1-globaly1)*halfxdimen;
2695     globalx2 = (globalx2-globaly2)*halfxdimen;
2696 
2697     sethlinesizes(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4,globalbufplc);
2698 
2699     globalx2 += globaly2*(x1-1);
2700     globaly1 += globalx1*(x1-1);
2701     globalx1 = mulscale16(globalx1,globalzd);
2702     globalx2 = mulscale16(globalx2,globalzd);
2703     globaly1 = mulscale16(globaly1,globalzd);
2704     globaly2 = mulscale16(globaly2,globalzd);
2705     globvis = klabs(mulscale10(globvis,globalzd));
2706 
2707     return 0;
2708 }
2709 
2710 //
2711 // ceilscan (internal)
2712 //
ceilscan(int32_t x1,int32_t x2,int32_t sectnum)2713 static void ceilscan(int32_t x1, int32_t x2, int32_t sectnum)
2714 {
2715     int32_t x, y1, y2;
2716     auto const sec = (usectorptr_t)&sector[sectnum];
2717 
2718     if (setup_globals_cf1(sec, sec->ceilingpal, sec->ceilingz-globalposz,
2719                           sec->ceilingpicnum, sec->ceilingshade, sec->ceilingstat,
2720                           sec->ceilingxpanning, sec->ceilingypanning, x1))
2721         return;
2722 
2723     if (!(globalorientation&0x180))
2724     {
2725         y1 = umost[x1]; y2 = y1;
2726         for (x=x1; x<=x2; x++)
2727         {
2728             const int32_t twall = umost[x]-1;
2729             const int32_t bwall = min(uplc[x],dmost[x]);
2730 
2731             if (twall < bwall-1)
2732             {
2733                 if (twall >= y2)
2734                 {
2735                     while (y1 < y2-1) hline(x-1,++y1);
2736                     y1 = twall;
2737                 }
2738                 else
2739                 {
2740                     while (y1 < twall) hline(x-1,++y1);
2741                     while (y1 > twall) lastx[y1--] = x;
2742                 }
2743                 while (y2 > bwall) hline(x-1,--y2);
2744                 while (y2 < bwall) lastx[y2++] = x;
2745             }
2746             else
2747             {
2748                 while (y1 < y2-1) hline(x-1,++y1);
2749                 if (x == x2) { globalx2 += globaly2; globaly1 += globalx1; break; }
2750                 y1 = umost[x+1]; y2 = y1;
2751             }
2752             globalx2 += globaly2; globaly1 += globalx1;
2753         }
2754         while (y1 < y2-1) hline(x2,++y1);
2755         faketimerhandler();
2756         return;
2757     }
2758 
2759     switch (globalorientation&0x180)
2760     {
2761     case 128:
2762         msethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
2763         break;
2764     case 256:
2765         setup_blend(0, 0);
2766         tsethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
2767         break;
2768     case 384:
2769         setup_blend(0, 1);
2770         tsethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
2771         break;
2772     }
2773 
2774     y1 = umost[x1]; y2 = y1;
2775     for (x=x1; x<=x2; x++)
2776     {
2777         const int32_t twall = umost[x]-1;
2778         const int32_t bwall = min(uplc[x],dmost[x]);
2779 
2780         if (twall < bwall-1)
2781         {
2782             if (twall >= y2)
2783             {
2784                 while (y1 < y2-1) slowhline(x-1,++y1);
2785                 y1 = twall;
2786             }
2787             else
2788             {
2789                 while (y1 < twall) slowhline(x-1,++y1);
2790                 while (y1 > twall) lastx[y1--] = x;
2791             }
2792             while (y2 > bwall) slowhline(x-1,--y2);
2793             while (y2 < bwall) lastx[y2++] = x;
2794         }
2795         else
2796         {
2797             while (y1 < y2-1) slowhline(x-1,++y1);
2798             if (x == x2) { globalx2 += globaly2; globaly1 += globalx1; break; }
2799             y1 = umost[x+1]; y2 = y1;
2800         }
2801         globalx2 += globaly2; globaly1 += globalx1;
2802     }
2803     while (y1 < y2-1) slowhline(x2,++y1);
2804     faketimerhandler();
2805 }
2806 
2807 
2808 //
2809 // florscan (internal)
2810 //
florscan(int32_t x1,int32_t x2,int32_t sectnum)2811 static void florscan(int32_t x1, int32_t x2, int32_t sectnum)
2812 {
2813      int32_t x, y1, y2;
2814      auto const sec = (usectorptr_t)&sector[sectnum];
2815 
2816      if (setup_globals_cf1(sec, sec->floorpal, globalposz-sec->floorz,
2817                            sec->floorpicnum, sec->floorshade, sec->floorstat,
2818                            sec->floorxpanning, sec->floorypanning, x1))
2819          return;
2820 
2821     if (!(globalorientation&0x180))
2822     {
2823         y1 = max(dplc[x1],umost[x1]); y2 = y1;
2824         for (x=x1; x<=x2; x++)
2825         {
2826             const int32_t twall = max(dplc[x],umost[x])-1;
2827             const int32_t bwall = dmost[x];
2828 
2829             if (twall < bwall-1)
2830             {
2831                 if (twall >= y2)
2832                 {
2833                     while (y1 < y2-1) hline(x-1,++y1);
2834                     y1 = twall;
2835                 }
2836                 else
2837                 {
2838                     while (y1 < twall) hline(x-1,++y1);
2839                     while (y1 > twall) lastx[y1--] = x;
2840                 }
2841                 while (y2 > bwall) hline(x-1,--y2);
2842                 while (y2 < bwall) lastx[y2++] = x;
2843             }
2844             else
2845             {
2846                 while (y1 < y2-1) hline(x-1,++y1);
2847                 if (x == x2) { globalx2 += globaly2; globaly1 += globalx1; break; }
2848                 y1 = max(dplc[x+1],umost[x+1]); y2 = y1;
2849             }
2850             globalx2 += globaly2; globaly1 += globalx1;
2851         }
2852         while (y1 < y2-1) hline(x2,++y1);
2853         faketimerhandler();
2854         return;
2855     }
2856 
2857     switch (globalorientation&0x180)
2858     {
2859     case 128:
2860         msethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
2861         break;
2862     case 256:
2863         setup_blend(0, 0);
2864         tsethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
2865         break;
2866     case 384:
2867         setup_blend(0, 1);
2868         tsethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
2869         break;
2870     }
2871 
2872     y1 = max(dplc[x1],umost[x1]); y2 = y1;
2873     for (x=x1; x<=x2; x++)
2874     {
2875         const int32_t twall = max(dplc[x],umost[x])-1;
2876         const int32_t bwall = dmost[x];
2877 
2878         if (twall < bwall-1)
2879         {
2880             if (twall >= y2)
2881             {
2882                 while (y1 < y2-1) slowhline(x-1,++y1);
2883                 y1 = twall;
2884             }
2885             else
2886             {
2887                 while (y1 < twall) slowhline(x-1,++y1);
2888                 while (y1 > twall) lastx[y1--] = x;
2889             }
2890             while (y2 > bwall) slowhline(x-1,--y2);
2891             while (y2 < bwall) lastx[y2++] = x;
2892         }
2893         else
2894         {
2895             while (y1 < y2-1) slowhline(x-1,++y1);
2896             if (x == x2) { globalx2 += globaly2; globaly1 += globalx1; break; }
2897             y1 = max(dplc[x+1],umost[x+1]); y2 = y1;
2898         }
2899         globalx2 += globaly2; globaly1 += globalx1;
2900     }
2901     while (y1 < y2-1) slowhline(x2,++y1);
2902     faketimerhandler();
2903 }
2904 
2905 
2906 //
2907 // wallscan (internal)
2908 //
wallscan(int32_t x1,int32_t x2,const int16_t * uwal,const int16_t * dwal,const int32_t * swal,const int32_t * lwal)2909 static void wallscan(int32_t x1, int32_t x2,
2910                      const int16_t *uwal, const int16_t *dwal,
2911                      const int32_t *swal, const int32_t *lwal)
2912 {
2913     int32_t x;
2914     intptr_t fpalookup;
2915     int32_t y1ve[4], y2ve[4];
2916     vec2_16_t tsiz;
2917 #ifdef MULTI_COLUMN_VLINE
2918     char bad;
2919     int32_t u4, d4, z;
2920     uintptr_t p;
2921 #endif
2922 
2923 #ifdef YAX_ENABLE
2924     if (g_nodraw)
2925         return;
2926 #endif
2927     setgotpic(globalpicnum);
2928     if (globalshiftval < 0)
2929         return;
2930 
2931     if (x2 >= xdim)
2932         x2 = xdim-1;
2933     assert((unsigned)x1 < (unsigned)xdim);
2934 
2935     tsiz = tilesiz[globalpicnum];
2936 
2937     if ((tsiz.x <= 0) || (tsiz.y <= 0)) return;
2938     if ((uwal[x1] > ydimen) && (uwal[x2] > ydimen)) return;
2939     if ((dwal[x1] < 0) && (dwal[x2] < 0)) return;
2940 
2941     if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
2942 
2943     tweak_tsizes(&tsiz);
2944 
2945     fpalookup = FP_OFF(palookup[globalpal]);
2946 
2947     setupvlineasm(globalshiftval);
2948 
2949 
2950     x = x1;
2951     while ((x <= x2) && (umost[x] > dmost[x]))
2952         x++;
2953 
2954 #ifdef NONPOW2_YSIZE_ASM
2955     if (globalshiftval==0)
2956         goto do_vlineasm1;
2957 #endif
2958 
2959 #ifdef MULTI_COLUMN_VLINE
2960     for (; (x<=x2)&&((x+frameoffset)&3); x++)
2961     {
2962         y1ve[0] = max(uwal[x],umost[x]);
2963         y2ve[0] = min(dwal[x],dmost[x]);
2964         if (y2ve[0] <= y1ve[0]) continue;
2965 
2966         palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swal[x],globvis));
2967 
2968         calc_bufplc(&bufplce[0], lwal[x], tsiz);
2969         calc_vplcinc(&vplce[0], &vince[0], swal, x, y1ve[0]);
2970 
2971         vlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0],x+frameoffset+ylookup[y1ve[0]]);
2972     }
2973     for (; x<=x2-3; x+=4)
2974     {
2975         bad = 0;
2976         for (z=3; z>=0; z--)
2977         {
2978             y1ve[z] = max(uwal[x+z],umost[x+z]);
2979             y2ve[z] = min(dwal[x+z],dmost[x+z])-1;
2980             if (y2ve[z] < y1ve[z]) { bad += pow2char[z]; continue; }
2981 
2982             calc_bufplc(&bufplce[z], lwal[x+z], tsiz);
2983             calc_vplcinc(&vplce[z], &vince[z], swal, x+z, y1ve[z]);
2984         }
2985         if (bad == 15) continue;
2986 
2987         palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swal[x],globvis));
2988         palookupoffse[3] = fpalookup + getpalookupsh(mulscale16(swal[x+3],globvis));
2989 
2990         if ((palookupoffse[0] == palookupoffse[3]) && ((bad&0x9) == 0))
2991         {
2992             palookupoffse[1] = palookupoffse[0];
2993             palookupoffse[2] = palookupoffse[0];
2994         }
2995         else
2996         {
2997             palookupoffse[1] = fpalookup + getpalookupsh(mulscale16(swal[x+1],globvis));
2998             palookupoffse[2] = fpalookup + getpalookupsh(mulscale16(swal[x+2],globvis));
2999         }
3000 
3001         u4 = max(max(y1ve[0],y1ve[1]),max(y1ve[2],y1ve[3]));
3002         d4 = min(min(y2ve[0],y2ve[1]),min(y2ve[2],y2ve[3]));
3003 
3004         if ((bad != 0) || (u4 >= d4))
3005         {
3006             if (!(bad&1)) prevlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0],vplce[0],bufplce[0],ylookup[y1ve[0]]+x+frameoffset+0);
3007             if (!(bad&2)) prevlineasm1(vince[1],palookupoffse[1],y2ve[1]-y1ve[1],vplce[1],bufplce[1],ylookup[y1ve[1]]+x+frameoffset+1);
3008             if (!(bad&4)) prevlineasm1(vince[2],palookupoffse[2],y2ve[2]-y1ve[2],vplce[2],bufplce[2],ylookup[y1ve[2]]+x+frameoffset+2);
3009             if (!(bad&8)) prevlineasm1(vince[3],palookupoffse[3],y2ve[3]-y1ve[3],vplce[3],bufplce[3],ylookup[y1ve[3]]+x+frameoffset+3);
3010             continue;
3011         }
3012 
3013         if (u4 > y1ve[0]) vplce[0] = prevlineasm1(vince[0],palookupoffse[0],u4-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+x+frameoffset+0);
3014         if (u4 > y1ve[1]) vplce[1] = prevlineasm1(vince[1],palookupoffse[1],u4-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+x+frameoffset+1);
3015         if (u4 > y1ve[2]) vplce[2] = prevlineasm1(vince[2],palookupoffse[2],u4-y1ve[2]-1,vplce[2],bufplce[2],ylookup[y1ve[2]]+x+frameoffset+2);
3016         if (u4 > y1ve[3]) vplce[3] = prevlineasm1(vince[3],palookupoffse[3],u4-y1ve[3]-1,vplce[3],bufplce[3],ylookup[y1ve[3]]+x+frameoffset+3);
3017 
3018         if (d4 >= u4) vlineasm4(d4-u4+1, (char *)(ylookup[u4]+x+frameoffset));
3019 
3020         p = x+frameoffset+ylookup[d4+1];
3021         if (y2ve[0] > d4) prevlineasm1(vince[0],palookupoffse[0],y2ve[0]-d4-1,vplce[0],bufplce[0],p+0);
3022         if (y2ve[1] > d4) prevlineasm1(vince[1],palookupoffse[1],y2ve[1]-d4-1,vplce[1],bufplce[1],p+1);
3023         if (y2ve[2] > d4) prevlineasm1(vince[2],palookupoffse[2],y2ve[2]-d4-1,vplce[2],bufplce[2],p+2);
3024         if (y2ve[3] > d4) prevlineasm1(vince[3],palookupoffse[3],y2ve[3]-d4-1,vplce[3],bufplce[3],p+3);
3025     }
3026 #endif
3027 
3028 #ifdef NONPOW2_YSIZE_ASM
3029 do_vlineasm1:
3030 #endif
3031     for (; x<=x2; x++)
3032     {
3033         y1ve[0] = max(uwal[x],umost[x]);
3034         y2ve[0] = min(dwal[x],dmost[x]);
3035         if (y2ve[0] <= y1ve[0]) continue;
3036 
3037         palookupoffse[0] = fpalookup + getpalookupsh(mulscale16(swal[x],globvis));
3038 
3039         calc_bufplc(&bufplce[0], lwal[x], tsiz);
3040         calc_vplcinc(&vplce[0], &vince[0], swal, x, y1ve[0]);
3041 
3042 #ifdef NONPOW2_YSIZE_ASM
3043         if (globalshiftval==0)
3044             vlineasm1nonpow2(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0],x+frameoffset+ylookup[y1ve[0]]);
3045         else
3046 #endif
3047         vlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0],x+frameoffset+ylookup[y1ve[0]]);
3048     }
3049 
3050     faketimerhandler();
3051 }
3052 
3053 //
3054 // transmaskvline (internal)
3055 //
transmaskvline(int32_t x)3056 static void transmaskvline(int32_t x)
3057 {
3058     if ((unsigned)x >= (unsigned)xdimen) return;
3059 
3060     int32_t const y1v = max<int>(uwall[x],startumost[x+windowxy1.x]-windowxy1.y);
3061     int32_t const y2v = min<int>(dwall[x],startdmost[x+windowxy1.x]-windowxy1.y) - 1;
3062 
3063     if (y2v < y1v) return;
3064 
3065     intptr_t palookupoffs = FP_OFF(palookup[globalpal]) + getpalookupsh(mulscale16(swall[x],globvis));
3066 
3067     vec2_16_t const ntsiz = { (int16_t)-tilesiz[globalpicnum].x, (int16_t)-tilesiz[globalpicnum].y };
3068     intptr_t bufplc;
3069     calc_bufplc(&bufplc, lwall[x], ntsiz);
3070     uint32_t vplc;
3071     int32_t vinc;
3072     calc_vplcinc(&vplc, &vinc, swall, x, y1v);
3073 
3074     intptr_t p = ylookup[y1v]+x+frameoffset;
3075 
3076 #ifdef NONPOW2_YSIZE_ASM
3077     if (globalshiftval==0)
3078         tvlineasm1nonpow2(vinc,palookupoffs,y2v-y1v,vplc,bufplc,p);
3079     else
3080 #endif
3081     tvlineasm1(vinc,palookupoffs,y2v-y1v,vplc,bufplc,p);
3082 }
3083 
3084 
3085 //
3086 // transmaskvline2 (internal)
3087 //
3088 #ifdef MULTI_COLUMN_VLINE
transmaskvline2(int32_t x)3089 static void transmaskvline2(int32_t x)
3090 {
3091     if ((unsigned)x >= (unsigned)xdimen) return;
3092     if (x == xdimen-1) { transmaskvline(x); return; }
3093 
3094     int32_t y1ve[2], y2ve[2];
3095     int32_t x2 = x+1;
3096 
3097     y1ve[0] = max<int>(uwall[x],startumost[x+windowxy1.x]-windowxy1.y);
3098     y2ve[0] = min<int>(dwall[x],startdmost[x+windowxy1.x]-windowxy1.y)-1;
3099     if (y2ve[0] < y1ve[0]) { transmaskvline(x2); return; }
3100     y1ve[1] = max<int>(uwall[x2],startumost[x2+windowxy1.x]-windowxy1.y);
3101     y2ve[1] = min<int>(dwall[x2],startdmost[x2+windowxy1.x]-windowxy1.y)-1;
3102     if (y2ve[1] < y1ve[1]) { transmaskvline(x); return; }
3103 
3104     palookupoffse[0] = FP_OFF(palookup[globalpal]) + getpalookupsh(mulscale16(swall[x],globvis));
3105     palookupoffse[1] = FP_OFF(palookup[globalpal]) + getpalookupsh(mulscale16(swall[x2],globvis));
3106 
3107     setuptvlineasm2(globalshiftval,palookupoffse[0],palookupoffse[1]);
3108 
3109     vec2_16_t const ntsiz = { (int16_t)-tilesiz[globalpicnum].x, (int16_t)-tilesiz[globalpicnum].y };
3110 
3111     calc_bufplc(&bufplce[0], lwall[x], ntsiz);
3112     calc_bufplc(&bufplce[1], lwall[x2], ntsiz);
3113     calc_vplcinc(&vplce[0], &vince[0], swall, x, y1ve[0]);
3114     calc_vplcinc(&vplce[1], &vince[1], swall, x2, y1ve[1]);
3115 
3116     int32_t const y1 = max(y1ve[0],y1ve[1]);
3117     int32_t const y2 = min(y2ve[0],y2ve[1]);
3118 
3119     uintptr_t p = x+frameoffset;
3120 
3121     if (y1ve[0] != y1ve[1])
3122     {
3123         if (y1ve[0] < y1)
3124             vplce[0] = tvlineasm1(vince[0],palookupoffse[0],y1-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+p);
3125         else
3126             vplce[1] = tvlineasm1(vince[1],palookupoffse[1],y1-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
3127     }
3128 
3129     if (y2 > y1)
3130     {
3131         asm1 = vince[1];
3132         asm2 = ylookup[y2]+p+1;
3133         tvlineasm2(vplce[1],vince[0],bufplce[0],bufplce[1],vplce[0],ylookup[y1]+p);
3134     }
3135     else
3136     {
3137         asm1 = vplce[0];
3138         asm2 = vplce[1];
3139     }
3140 
3141     if (y2ve[0] > y2ve[1])
3142         tvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y2-1,asm1,bufplce[0],ylookup[y2+1]+p);
3143     else if (y2ve[0] < y2ve[1])
3144         tvlineasm1(vince[1],palookupoffse[1],y2ve[1]-y2-1,asm2,bufplce[1],ylookup[y2+1]+p+1);
3145 
3146     faketimerhandler();
3147 }
3148 #endif
3149 
3150 //
3151 // transmaskwallscan (internal)
3152 //
transmaskwallscan(int32_t x1,int32_t x2,int32_t saturatevplc)3153 static void transmaskwallscan(int32_t x1, int32_t x2, int32_t saturatevplc)
3154 {
3155     setgotpic(globalpicnum);
3156 
3157     Bassert(globalshiftval>=0 || ((tilesiz[globalpicnum].x <= 0) || (tilesiz[globalpicnum].y <= 0)));
3158     // globalshiftval<0 implies following condition
3159     if ((tilesiz[globalpicnum].x <= 0) || (tilesiz[globalpicnum].y <= 0))
3160         return;
3161 
3162     if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
3163 
3164     setuptvlineasm(globalshiftval, saturatevplc);
3165 
3166     int32_t x = x1;
3167     while ((x <= x2) && (startumost[x+windowxy1.x] > startdmost[x+windowxy1.x]))
3168         ++x;
3169 
3170 #ifndef ENGINE_USING_A_C
3171     if (globalshiftval==0)
3172     {
3173         while (x <= x2) transmaskvline(x++);
3174     }
3175     else
3176 #endif
3177     {
3178 #ifdef MULTI_COLUMN_VLINE
3179         if ((x <= x2) && (x&1)) transmaskvline(x++);
3180         while (x < x2) transmaskvline2(x), x += 2;
3181 #endif
3182         while (x <= x2) transmaskvline(x++);
3183     }
3184 
3185     faketimerhandler();
3186 }
3187 
3188 
3189 ////////// NON-power-of-two replacements for mhline/thline, adapted from a.c //////////
3190 #if defined(__GNUC__) && defined(__i386__) && !defined(NOASM)
3191 // from pragmas.h
3192 # define ourdivscale32(d,b) \
3193     ({ int32_t __d=(d), __b=(b), __r; \
3194        __asm__ __volatile__ ("xorl %%eax, %%eax; divl %%ebx" \
3195         : "=a" (__r), "=d" (__d) : "d" (__d), "b" (__b) : "cc"); \
3196      __r; })
3197 #else
3198 # define ourdivscale32(d,b) divscale32(d,b)
3199 #endif
3200 
3201 // cntup16>>16 iterations
3202 
nonpow2_mhline(intptr_t bufplc,uint32_t bx,int32_t cntup16,uint32_t by,char * p)3203 static void nonpow2_mhline(intptr_t bufplc, uint32_t bx, int32_t cntup16, uint32_t by, char *p)
3204 {
3205     char ch;
3206 
3207     const char *const A_C_RESTRICT buf = (char *)bufplc;
3208     const char *const A_C_RESTRICT pal = (char *)asm3;
3209 
3210     const uint32_t xmul = globalxspan;
3211     const uint32_t ymul = globalyspan;
3212     const uint32_t yspan = globalyspan;
3213     const int32_t xinc = asm1, yinc = asm2;
3214 
3215     for (cntup16>>=16; cntup16>0; cntup16--)
3216     {
3217         ch = buf[mulscale31(bx>>1, xmul)*yspan + mulscale31(by>>1, ymul)];
3218 
3219         if (ch != 255) *p = pal[ch];
3220         bx += xinc;
3221         by += yinc;
3222         p++;
3223     }
3224 }
3225 
3226 // cntup16>>16 iterations
nonpow2_thline(intptr_t bufplc,uint32_t bx,int32_t cntup16,uint32_t by,char * p)3227 static void nonpow2_thline(intptr_t bufplc, uint32_t bx, int32_t cntup16, uint32_t by, char *p)
3228 {
3229     char ch;
3230 
3231     const char *const A_C_RESTRICT buf = (char *)bufplc;
3232     const char *const A_C_RESTRICT pal = (char *)asm3;
3233     const char *const A_C_RESTRICT trans = paletteGetBlendTable(globalblend);
3234 
3235     const uint32_t xmul = globalxspan;
3236     const uint32_t ymul = globalyspan;
3237     const uint32_t yspan = globalyspan;
3238     const int32_t xinc = asm1, yinc = asm2;
3239 
3240     if (globalorientation&512)
3241     {
3242         for (cntup16>>=16; cntup16>0; cntup16--)
3243         {
3244             ch = buf[mulscale31(bx>>1, xmul)*yspan + mulscale31(by>>1, ymul)];
3245             if (ch != 255) *p = trans[(*p)|(pal[ch]<<8)];
3246             bx += xinc;
3247             by += yinc;
3248             p++;
3249         }
3250     }
3251     else
3252     {
3253         for (cntup16>>=16; cntup16>0; cntup16--)
3254         {
3255             ch = buf[mulscale31(bx>>1, xmul)*yspan + mulscale31(by>>1, ymul)];
3256             if (ch != 255) *p = trans[((*p)<<8)|pal[ch]];
3257             bx += xinc;
3258             by += yinc;
3259             p++;
3260         }
3261     }
3262 }
3263 ////////// END non-power-of-two replacements //////////
3264 
3265 //
3266 // ceilspritehline (internal)
3267 //
ceilspritehline(int32_t x2,int32_t y)3268 static void ceilspritehline(int32_t x2, int32_t y)
3269 {
3270     int32_t x1, v, bx, by;
3271 
3272     //x = x1 + (x2-x1)t + (y1-y2)u  ~  x = 160v
3273     //y = y1 + (y2-y1)t + (x2-x1)u  ~  y = (scrx-160)v
3274     //z = z1 = z2                   ~  z = posz + (scry-horiz)v
3275 
3276     x1 = lastx[y]; if (x2 < x1) return;
3277 
3278     v = mulscale20(globalzd,horizlookup[y-globalhoriz+horizycent]);
3279     bx = (uint32_t)mulscale14(globalx2*x1+globalx1,v) + globalxpanning;
3280     by = (uint32_t)mulscale14(globaly2*x1+globaly1,v) + globalypanning;
3281     asm1 = mulscale14(globalx2,v);
3282     asm2 = mulscale14(globaly2,v);
3283 
3284     asm3 = FP_OFF(palookup[globalpal]) + getpalookupsh(mulscale28(klabs(v),globvis));
3285 
3286     if (globalispow2)
3287     {
3288         if ((globalorientation&2) == 0)
3289             mhline(globalbufplc,bx,(x2-x1)<<16,0L,by,ylookup[y]+x1+frameoffset);
3290         else
3291             thline(globalbufplc,bx,(x2-x1)<<16,0L,by,ylookup[y]+x1+frameoffset);
3292     }
3293     else
3294     {
3295         if ((globalorientation&2) == 0)
3296             nonpow2_mhline(globalbufplc,bx,(x2-x1)<<16,by,(char *)(ylookup[y]+x1+frameoffset));
3297         else
3298             nonpow2_thline(globalbufplc,bx,(x2-x1)<<16,by,(char *)(ylookup[y]+x1+frameoffset));
3299     }
3300 }
3301 
3302 
3303 //
3304 // ceilspritescan (internal)
3305 //
ceilspritescan(int32_t x1,int32_t x2)3306 static void ceilspritescan(int32_t x1, int32_t x2)
3307 {
3308     int32_t y1 = uwall[x1];
3309     int32_t y2 = y1;
3310 
3311     for (bssize_t x=x1; x<=x2; ++x)
3312     {
3313         const int32_t twall = uwall[x]-1;
3314         const int32_t bwall = dwall[x];
3315 
3316         if (twall < bwall-1)
3317         {
3318             if (twall >= y2)
3319             {
3320                 while (y1 < y2-1) ceilspritehline(x-1,++y1);
3321                 y1 = twall;
3322             }
3323             else
3324             {
3325                 while (y1 < twall) ceilspritehline(x-1,++y1);
3326                 while (y1 > twall) lastx[y1--] = x;
3327             }
3328             while (y2 > bwall) ceilspritehline(x-1,--y2);
3329             while (y2 < bwall) lastx[y2++] = x;
3330         }
3331         else
3332         {
3333             while (y1 < y2-1) ceilspritehline(x-1,++y1);
3334             if (x == x2) break;
3335             y1 = uwall[x+1]; y2 = y1;
3336         }
3337     }
3338     while (y1 < y2-1) ceilspritehline(x2,++y1);
3339     faketimerhandler();
3340 }
3341 
3342 ////////// translucent slope vline, based on a-c.c's slopevlin //////////
3343 static int32_t gglogx, gglogy, ggpinc;
3344 static char *ggbuf, *ggpal;
3345 
3346 #ifdef ENGINE_USING_A_C
3347 extern int32_t gpinc;
3348 #endif
3349 
setupslopevlin_alsotrans(int32_t logylogx,intptr_t bufplc,int32_t pinc)3350 static inline void setupslopevlin_alsotrans(int32_t logylogx, intptr_t bufplc, int32_t pinc)
3351 {
3352 #ifdef ENGINE_USING_A_C
3353     sethlinesizes(logylogx&255, logylogx>>8, bufplc);
3354     gpinc = pinc;
3355 #else
3356     setupslopevlin(logylogx, bufplc, pinc);
3357 #endif
3358     gglogx = (logylogx&255); gglogy = (logylogx>>8);
3359     ggbuf = (char *)bufplc; ggpinc = pinc;
3360     ggpal = palookup[globalpal] + getpalookupsh(0);
3361 }
3362 
3363 // cnt iterations
tslopevlin(uint8_t * p,const intptr_t * slopalptr,bssize_t cnt,int32_t bx,int32_t by)3364 static void tslopevlin(uint8_t *p, const intptr_t *slopalptr, bssize_t cnt, int32_t bx, int32_t by)
3365 {
3366     const char *const A_C_RESTRICT buf = ggbuf;
3367     const char *const A_C_RESTRICT trans = paletteGetBlendTable(0);
3368     const int32_t bzinc = (asm1>>3), pinc = ggpinc;
3369 
3370     const int32_t transmode = (globalorientation&128);
3371     const uint32_t xtou = globalx3, ytov = globaly3;
3372     const int32_t logx = gglogx, logy = gglogy;
3373 
3374     int32_t bz = asm3;
3375 
3376     do
3377     {
3378         int const i = (sloptable[(bz>>6)+HALFSLOPTABLESIZ]); bz += bzinc;
3379         uint32_t u = bx + xtou*i;
3380         uint32_t v = by + ytov*i;
3381         uint8_t ch = buf[((u>>(32-logx))<<logy)+(v>>(32-logy))];
3382         if (ch != 255)
3383         {
3384             ch = *(uint8_t *)(slopalptr[0] + ch);
3385             *p = trans[transmode ? *p|(ch<<8) : (*p<<8)|ch];
3386         }
3387 
3388         slopalptr--;
3389         p += pinc;
3390     }
3391     while (--cnt);
3392 }
3393 
3394 // cnt iterations
mslopevlin(uint8_t * p,const intptr_t * slopalptr,bssize_t cnt,int32_t bx,int32_t by)3395 static void mslopevlin(uint8_t *p, const intptr_t *slopalptr, bssize_t cnt, int32_t bx, int32_t by)
3396 {
3397     const char *const A_C_RESTRICT buf = ggbuf;
3398     const int32_t bzinc = (asm1>>3), pinc = ggpinc;
3399 
3400     const uint32_t xtou = globalx3, ytov = globaly3;
3401     const int32_t logx = gglogx, logy = gglogy;
3402 
3403     int32_t bz = asm3;
3404 
3405     do
3406     {
3407         int const i = (sloptable[(bz>>6)+HALFSLOPTABLESIZ]); bz += bzinc;
3408         uint32_t u = bx + xtou*i;
3409         uint32_t v = by + ytov*i;
3410         uint8_t ch = buf[((u>>(32-logx))<<logy)+(v>>(32-logy))];
3411         if (ch != 255)
3412             *p = *(uint8_t *)(slopalptr[0] + ch);
3413 
3414         slopalptr--;
3415         p += pinc;
3416     }
3417     while (--cnt);
3418 }
3419 
3420 //
3421 // grouscan (internal)
3422 //
3423 #define BITSOFPRECISION 3  //Don't forget to change this in A.ASM also!
fgrouscan(int32_t dax1,int32_t dax2,int32_t sectnum,char dastat)3424 static void fgrouscan(int32_t dax1, int32_t dax2, int32_t sectnum, char dastat)
3425 {
3426     int32_t i, j, l, globalx1, globaly1, y1, y2, daslope, daz, wxi, wyi;
3427     float fi, wx, wy, dasqr;
3428     float globalx, globaly, globalx2, globaly2, globalx3, globaly3, globalz, globalzd, globalzx;
3429     int32_t shoffs, m1, m2, shy1, shy2;
3430     intptr_t *mptr1, *mptr2;
3431 
3432     const usectortype *const sec = (usectortype *)&sector[sectnum];
3433     const uwalltype *wal;
3434 
3435     if (dastat == 0)
3436     {
3437         if (globalposz <= getceilzofslope(sectnum,globalposx,globalposy))
3438             return;  //Back-face culling
3439         globalorientation = sec->ceilingstat;
3440         globalpicnum = sec->ceilingpicnum;
3441         globalshade = sec->ceilingshade;
3442         globalpal = sec->ceilingpal;
3443         daslope = sec->ceilingheinum;
3444         daz = sec->ceilingz;
3445     }
3446     else
3447     {
3448         if (globalposz >= getflorzofslope(sectnum,globalposx,globalposy))
3449             return;  //Back-face culling
3450         globalorientation = sec->floorstat;
3451         globalpicnum = sec->floorpicnum;
3452         globalshade = sec->floorshade;
3453         globalpal = sec->floorpal;
3454         daslope = sec->floorheinum;
3455         daz = sec->floorz;
3456     }
3457 
3458     tileUpdatePicnum(&globalpicnum, sectnum);
3459     setgotpic(globalpicnum);
3460     if ((tilesiz[globalpicnum].x <= 0) || (tilesiz[globalpicnum].y <= 0)) return;
3461     if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
3462 
3463     wal = (uwalltype *)&wall[sec->wallptr];
3464     wxi = wall[wal->point2].x - wal->x;
3465     wyi = wall[wal->point2].y - wal->y;
3466     dasqr = 1073741824.f/nsqrtasm(uhypsq(wxi,wyi));
3467     fi = daslope*dasqr*(1.f/2097152.f);
3468     wx = wxi*fi; wy = wyi*fi;
3469 
3470     globalx = -float(singlobalang)*float(xdimenrecip)*(1.f/524288.f);
3471     globaly = float(cosglobalang)*float(xdimenrecip)*(1.f/524288.f);
3472     globalx1 = globalposx<<8;
3473     globaly1 = -globalposy<<8;
3474     fi = (dax1-halfxdimen)*xdimenrecip;
3475     globalx2 = float(cosglobalang)*float(viewingrangerecip)*(1.f/4096.f) - float(singlobalang)*fi*(1.f/134217728.f);
3476     globaly2 = float(singlobalang)*float(viewingrangerecip)*(1.f/4096.f) + float(cosglobalang)*fi*(1.f/134217728.f);
3477     globalzd = xdimscale*512.f;
3478     globalzx = -(wx*globaly2-wy*globalx2)*(1.f/131072.f) + (1-globalhoriz)*globalzd*(1.f/1024.f);
3479     globalz = -(wx*globaly-wy*globalx)*(1.f/33554432.f);
3480 
3481     if (globalorientation&64)  //Relative alignment
3482     {
3483         float dx, dy, x, y;
3484         dx = (wall[wal->point2].x-wal->x)*dasqr*(1.f/16384.f);
3485         dy = (wall[wal->point2].y-wal->y)*dasqr*(1.f/16384.f);
3486 
3487         fi = float(nsqrtasm(daslope*daslope+16777216));
3488 
3489         x = globalx; y = globaly;
3490         globalx = (x*dx+y*dy)*(1.f/65536.f);
3491         globaly = (-y*dx+x*dy)*fi*(1.f/268435456.f);
3492 
3493         x = (wal->x-globalposx)*256.f; y = (wal->y-globalposy)*256.f;
3494         globalx1 = Blrintf((-x*dx-y*dy)*(1.f/65536.f));
3495         globaly1 = Blrintf((-y*dx+x*dy)*fi*(1.f/268435456.f));
3496 
3497         x = globalx2; y = globaly2;
3498         globalx2 = (x*dx+y*dy)*(1.f/65536.f);
3499         globaly2 = (-y*dx+x*dy)*fi*(1.f/268435456.f);
3500     }
3501     if (globalorientation&0x4)
3502     {
3503         fi = globalx; globalx = -globaly; globaly = -fi;
3504         i = globalx1; globalx1 = globaly1; globaly1 = i;
3505         fi = globalx2; globalx2 = -globaly2; globaly2 = -fi;
3506     }
3507     if (globalorientation&0x10) { globalx1 = -globalx1, globalx2 = -globalx2, globalx = -globalx; }
3508     if (globalorientation&0x20) { globaly1 = -globaly1, globaly2 = -globaly2, globaly = -globaly; }
3509 
3510     float fdaz = (wx*(globalposy-wal->y)-wy*(globalposx-wal->x))*(1.f/512.f) + (daz-globalposz)*256.f;
3511     globalx2 = (globalx2*fdaz)*(1.f/1048576.f); globalx = (globalx*fdaz)*(1.f/268435456.f);
3512     globaly2 = (globaly2*-fdaz)*(1.f/1048576.f); globaly = (globaly*-fdaz)*(1.f/268435456.f);
3513 
3514     i = 8-(picsiz[globalpicnum]&15); j = 8-(picsiz[globalpicnum]>>4);
3515     if (globalorientation&8) { i++; j++; }
3516     globalx1 <<= (i+12); globalx2 *= 1<<i; globalx *= 1<<i;
3517     globaly1 <<= (j+12); globaly2 *= 1<<j; globaly *= 1<<j;
3518 
3519     if (dastat == 0)
3520     {
3521         globalx1 += (uint32_t)sec->ceilingxpanning<<24;
3522         globaly1 += (uint32_t)sec->ceilingypanning<<24;
3523     }
3524     else
3525     {
3526         globalx1 += (uint32_t)sec->floorxpanning<<24;
3527         globaly1 += (uint32_t)sec->floorypanning<<24;
3528     }
3529 
3530     globalx1 >>= 16;
3531     globaly1 >>= 16;
3532 
3533     //asm1 = -(globalzd>>(16-BITSOFPRECISION));
3534     float bzinc = -globalzd*(1.f/65536.f);
3535 
3536     int32_t const vis = (sec->visibility != 0) ? mulscale4(globalvisibility, (uint8_t)(sec->visibility+16)) : globalvisibility;
3537     globvis = ((((uint64_t)(vis*fdaz)) >> 13) * xdimscale) >> 16;
3538 
3539     intptr_t fj = FP_OFF(palookup[globalpal]);
3540 
3541     setupslopevlin_alsotrans((picsiz[globalpicnum]&15) + ((picsiz[globalpicnum]>>4)<<8),
3542                              waloff[globalpicnum],-ylookup[1]);
3543 
3544     l = Blrintf((globalzd)*(1.f/65536.f));
3545 
3546     int32_t const shinc = Blrintf(globalz*xdimenscale*(1.f/65536.f));
3547 
3548     shoffs = (shinc > 0) ? (4 << 15) : ((16380 - ydimen) << 15);  // JBF: was 2044
3549     y1     = (dastat == 0) ? umost[dax1] : max(umost[dax1], dplc[dax1]);
3550 
3551     m1 = Blrintf((y1*globalzd)*(1.f/65536.f) + globalzx*(1.f/64.f));
3552     //Avoid visibility overflow by crossing horizon
3553     m1 += klabs(l);
3554     m2 = m1+l;
3555     shy1 = y1+(shoffs>>15);
3556     if ((unsigned)shy1 >= SLOPALOOKUPSIZ-1)
3557     {
3558         OSD_Printf("%s:%d: slopalookup[%" PRId32 "] overflow drawing sector %d!\n", EDUKE32_FUNCTION, __LINE__, shy1, sectnum);
3559         return;
3560     }
3561 
3562     mptr1 = &slopalookup[shy1]; mptr2 = mptr1+1;
3563 
3564     for (int x=dax1; x<=dax2; x++)
3565     {
3566         if (dastat == 0) { y1 = umost[x]; y2 = min(dmost[x],uplc[x])-1; }
3567         else { y1 = max(umost[x],dplc[x]); y2 = dmost[x]-1; }
3568         if (y1 <= y2)
3569         {
3570             shy1 = y1+(shoffs>>15);
3571             shy2 = y2+(shoffs>>15);
3572 
3573             // Ridiculously steep gradient?
3574             if ((unsigned)shy1 >= SLOPALOOKUPSIZ)
3575             {
3576                 OSD_Printf("%s:%d: slopalookup[%" PRId32 "] overflow drawing sector %d!\n", EDUKE32_FUNCTION, __LINE__, shy1, sectnum);
3577                 goto next_most;
3578             }
3579             if ((unsigned)shy2 >= SLOPALOOKUPSIZ)
3580             {
3581                 OSD_Printf("%s:%d: slopalookup[%" PRId32 "] overflow drawing sector %d!\n", EDUKE32_FUNCTION, __LINE__, shy2, sectnum);
3582                 goto next_most;
3583             }
3584 
3585             intptr_t *nptr1 = &slopalookup[shy1];
3586             intptr_t *nptr2 = &slopalookup[shy2];
3587 
3588             while (nptr1 <= mptr1)
3589             {
3590                 *mptr1-- = fj + getpalookupsh(mulscale24(krecipasm(m1),globvis));
3591                 m1 -= l;
3592             }
3593             while (nptr2 >= mptr2)
3594             {
3595                 *mptr2++ = fj + getpalookupsh(mulscale24(krecipasm(m2),globvis));
3596                 m2 += l;
3597             }
3598 
3599             globalx3 = globalx2*(1.f/1024.f);
3600             globaly3 = globaly2*(1.f/1024.f);
3601             float bz = (y2*globalzd)*(1.f/65536.f) + globalzx*(1.f/64.f);
3602             uint8_t *p = (uint8_t*)(ylookup[y2]+x+frameoffset);
3603             intptr_t* A_C_RESTRICT slopalptr = (intptr_t*)nptr2;
3604             const char* const A_C_RESTRICT trans = paletteGetBlendTable(0);
3605             uint32_t u, v;
3606             int cnt = y2-y1+1;
3607 #define LINTERPSIZ 4
3608             int u0 = Blrintf(1048576.f*globalx3/bz);
3609             int v0 = Blrintf(1048576.f*globaly3/bz);
3610             switch (globalorientation&0x180)
3611             {
3612             case 0:
3613                 while (cnt > 0)
3614                 {
3615                     bz += bzinc*(1<<LINTERPSIZ);
3616                     int u1 = Blrintf(1048576.f*globalx3/bz);
3617                     int v1 = Blrintf(1048576.f*globaly3/bz);
3618                     u1 = (u1-u0)>>LINTERPSIZ;
3619                     v1 = (v1-v0)>>LINTERPSIZ;
3620                     int cnt2 = min(cnt, 1<<LINTERPSIZ);
3621                     for (; cnt2>0; cnt2--)
3622                     {
3623                         u = (globalx1+u0)&0xffff;
3624                         v = (globaly1+v0)&0xffff;
3625                         *p = *(uint8_t *)(((intptr_t)slopalptr[0])+ggbuf[((u>>(16-gglogx))<<gglogy)+(v>>(16-gglogy))]);
3626                         slopalptr--;
3627                         p += ggpinc;
3628                         u0 += u1;
3629                         v0 += v1;
3630                     }
3631                     cnt -= 1<<LINTERPSIZ;
3632                 }
3633                 break;
3634             case 128:
3635                 while (cnt > 0)
3636                 {
3637                     bz += bzinc*(1<<LINTERPSIZ);
3638                     int u1 = Blrintf(1048576.f*globalx3/bz);
3639                     int v1 = Blrintf(1048576.f*globaly3/bz);
3640                     u1 = (u1-u0)>>LINTERPSIZ;
3641                     v1 = (v1-v0)>>LINTERPSIZ;
3642                     int cnt2 = min(cnt, 1<<LINTERPSIZ);
3643                     for (; cnt2>0; cnt2--)
3644                     {
3645                         u = (globalx1+u0)&0xffff;
3646                         v = (globaly1+v0)&0xffff;
3647                         uint8_t ch = ggbuf[((u>>(16-gglogx))<<gglogy)+(v>>(16-gglogy))];
3648                         if (ch != 255)
3649                             *p = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
3650                         slopalptr--;
3651                         p += ggpinc;
3652                         u0 += u1;
3653                         v0 += v1;
3654                     }
3655                     cnt -= 1<<LINTERPSIZ;
3656                 }
3657                 break;
3658             case 256:
3659                 while (cnt > 0)
3660                 {
3661                     bz += bzinc*(1<<LINTERPSIZ);
3662                     int u1 = Blrintf(1048576.f*globalx3/bz);
3663                     int v1 = Blrintf(1048576.f*globaly3/bz);
3664                     u1 = (u1-u0)>>LINTERPSIZ;
3665                     v1 = (v1-v0)>>LINTERPSIZ;
3666                     int cnt2 = min(cnt, 1<<LINTERPSIZ);
3667                     for (; cnt2>0; cnt2--)
3668                     {
3669                         u = (globalx1+u0)&0xffff;
3670                         v = (globaly1+v0)&0xffff;
3671                         uint8_t ch = ggbuf[((u>>(16-gglogx))<<gglogy)+(v>>(16-gglogy))];
3672                         if (ch != 255)
3673                         {
3674                             ch = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
3675                             *p = trans[(*p<<8)|ch];
3676                         }
3677                         slopalptr--;
3678                         p += ggpinc;
3679                         u0 += u1;
3680                         v0 += v1;
3681                     }
3682                     cnt -= 1<<LINTERPSIZ;
3683                 }
3684                 break;
3685             case 384:
3686                 while (cnt > 0)
3687                 {
3688                     bz += bzinc*(1<<LINTERPSIZ);
3689                     int u1 = Blrintf(1048576.f*globalx3/bz);
3690                     int v1 = Blrintf(1048576.f*globaly3/bz);
3691                     u1 = (u1-u0)>>LINTERPSIZ;
3692                     v1 = (v1-v0)>>LINTERPSIZ;
3693                     int cnt2 = min(cnt, 1<<LINTERPSIZ);
3694                     for (; cnt2>0; cnt2--)
3695                     {
3696                         u = (globalx1+u0)&0xffff;
3697                         v = (globaly1+v0)&0xffff;
3698                         uint8_t ch = ggbuf[((u>>(16-gglogx))<<gglogy)+(v>>(16-gglogy))];
3699                         if (ch != 255)
3700                         {
3701                             ch = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
3702                             *p = trans[ch<<8|*p];
3703                         }
3704                         slopalptr--;
3705                         p += ggpinc;
3706                         u0 += u1;
3707                         v0 += v1;
3708                     }
3709                     cnt -= 1<<LINTERPSIZ;
3710                 }
3711                 break;
3712             }
3713 #undef LINTERPSIZ
3714             if ((x&15) == 0) faketimerhandler();
3715         }
3716 next_most:
3717         globalx2 += globalx;
3718         globaly2 += globaly;
3719         globalzx += globalz;
3720         shoffs += shinc;
3721     }
3722 }
3723 
grouscan(int32_t dax1,int32_t dax2,int32_t sectnum,char dastat)3724 static void grouscan(int32_t dax1, int32_t dax2, int32_t sectnum, char dastat)
3725 {
3726     if (r_fpgrouscan)
3727     {
3728         fgrouscan(dax1, dax2, sectnum, dastat);
3729         return;
3730     }
3731     int32_t i, l, x, y, dx, dy, wx, wy, y1, y2, daz;
3732     int32_t daslope, dasqr;
3733     int32_t shoffs, m1, m2, shy1, shy2;
3734     intptr_t *mptr1, *mptr2, j;
3735 
3736     // Er, yes, they're not global anymore:
3737     int32_t globalx, globaly, globalz, globalzx;
3738 
3739     auto const sec = (usectorptr_t)&sector[sectnum];
3740     uwallptr_t wal;
3741 
3742     if (dastat == 0)
3743     {
3744         if (globalposz <= getceilzofslope(sectnum,globalposx,globalposy))
3745             return;  //Back-face culling
3746         globalorientation = sec->ceilingstat;
3747         globalpicnum = sec->ceilingpicnum;
3748         globalshade = sec->ceilingshade;
3749         globalpal = sec->ceilingpal;
3750         daslope = sec->ceilingheinum;
3751         daz = sec->ceilingz;
3752     }
3753     else
3754     {
3755         if (globalposz >= getflorzofslope(sectnum,globalposx,globalposy))
3756             return;  //Back-face culling
3757         globalorientation = sec->floorstat;
3758         globalpicnum = sec->floorpicnum;
3759         globalshade = sec->floorshade;
3760         globalpal = sec->floorpal;
3761         daslope = sec->floorheinum;
3762         daz = sec->floorz;
3763     }
3764 
3765     tileUpdatePicnum(&globalpicnum, sectnum);
3766     setgotpic(globalpicnum);
3767     if ((tilesiz[globalpicnum].x <= 0) || (tilesiz[globalpicnum].y <= 0)) return;
3768     if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
3769 
3770     wal = (uwallptr_t)&wall[sec->wallptr];
3771     wx = wall[wal->point2].x - wal->x;
3772     wy = wall[wal->point2].y - wal->y;
3773     dasqr = krecipasm(nsqrtasm(uhypsq(wx,wy)));
3774     i = mulscale21(daslope,dasqr);
3775     wx *= i; wy *= i;
3776 
3777     globalx = -mulscale19(singlobalang,xdimenrecip);
3778     globaly = mulscale19(cosglobalang,xdimenrecip);
3779     globalx1 = (globalposx<<8);
3780     globaly1 = -(globalposy<<8);
3781     i = (dax1-halfxdimen)*xdimenrecip;
3782     globalx2 = mulscale16(cosglobalang<<4,viewingrangerecip) - mulscale27(singlobalang,i);
3783     globaly2 = mulscale16(singlobalang<<4,viewingrangerecip) + mulscale27(cosglobalang,i);
3784     globalzd = decltype(globalzd)(xdimscale)<<9;
3785     globalzx = -dmulscale17(wx,globaly2,-wy,globalx2) + mulscale10(1-globalhoriz,globalzd);
3786     globalz = -dmulscale25(wx,globaly,-wy,globalx);
3787 
3788     if (globalorientation&64)  //Relative alignment
3789     {
3790         dx = mulscale14(wall[wal->point2].x-wal->x,dasqr);
3791         dy = mulscale14(wall[wal->point2].y-wal->y,dasqr);
3792 
3793         i = nsqrtasm(daslope*daslope+16777216);
3794 
3795         x = globalx; y = globaly;
3796         globalx = dmulscale16(x,dx,y,dy);
3797         globaly = mulscale12(dmulscale16(-y,dx,x,dy),i);
3798 
3799         x = ((wal->x-globalposx)<<8); y = ((wal->y-globalposy)<<8);
3800         globalx1 = dmulscale16(-x,dx,-y,dy);
3801         globaly1 = mulscale12(dmulscale16(-y,dx,x,dy),i);
3802 
3803         x = globalx2; y = globaly2;
3804         globalx2 = dmulscale16(x,dx,y,dy);
3805         globaly2 = mulscale12(dmulscale16(-y,dx,x,dy),i);
3806     }
3807     if (globalorientation&0x4)
3808     {
3809         i = globalx; globalx = -globaly; globaly = -i;
3810         i = globalx1; globalx1 = globaly1; globaly1 = i;
3811         i = globalx2; globalx2 = -globaly2; globaly2 = -i;
3812     }
3813     if (globalorientation&0x10) { globalx1 = -globalx1, globalx2 = -globalx2, globalx = -globalx; }
3814     if (globalorientation&0x20) { globaly1 = -globaly1, globaly2 = -globaly2, globaly = -globaly; }
3815 
3816     daz = dmulscale9(wx,globalposy-wal->y,-wy,globalposx-wal->x) + ((daz-globalposz)<<8);
3817     globalx2 = mulscale20(globalx2,daz); globalx = mulscale28(globalx,daz);
3818     globaly2 = mulscale20(globaly2,-daz); globaly = mulscale28(globaly,-daz);
3819 
3820     i = 8-(picsiz[globalpicnum]&15); j = 8-(picsiz[globalpicnum]>>4);
3821     if (globalorientation&8) { i++; j++; }
3822     globalx1 <<= (i+12); globalx2 <<= i; globalx <<= i;
3823     globaly1 <<= (j+12); globaly2 <<= j; globaly <<= j;
3824 
3825     if (dastat == 0)
3826     {
3827         globalx1 += (uint32_t)sec->ceilingxpanning<<24;
3828         globaly1 += (uint32_t)sec->ceilingypanning<<24;
3829     }
3830     else
3831     {
3832         globalx1 += (uint32_t)sec->floorxpanning<<24;
3833         globaly1 += (uint32_t)sec->floorypanning<<24;
3834     }
3835 
3836     asm1 = -(globalzd>>(16-BITSOFPRECISION));
3837 
3838     int32_t const vis = (sec->visibility != 0) ? mulscale4(globalvisibility, (uint8_t)(sec->visibility+16)) : globalvisibility;
3839     globvis = ((((uint64_t)(vis*daz)) >> 13) * xdimscale) >> 16;
3840 
3841     j = FP_OFF(palookup[globalpal]);
3842 
3843     setupslopevlin_alsotrans((picsiz[globalpicnum]&15) + ((picsiz[globalpicnum]>>4)<<8),
3844                              waloff[globalpicnum],-ylookup[1]);
3845 
3846     l = (globalzd>>16);
3847 
3848     int32_t const shinc = mulscale16(globalz,xdimenscale);
3849 
3850     shoffs = (shinc > 0) ? (4 << 15) : ((16380 - ydimen) << 15);  // JBF: was 2044
3851     y1     = (dastat == 0) ? umost[dax1] : max(umost[dax1], dplc[dax1]);
3852 
3853     m1 = mulscale16(y1,globalzd) + (globalzx>>6);
3854     //Avoid visibility overflow by crossing horizon
3855     m1 += klabs((int32_t) (globalzd>>16));
3856     m2 = m1+l;
3857     shy1 = y1+(shoffs>>15);
3858     if ((unsigned)shy1 >= SLOPALOOKUPSIZ - 1)
3859     {
3860         OSD_Printf("%s:%d: slopalookup[%" PRId32 "] overflow drawing sector %d!\n", EDUKE32_FUNCTION, __LINE__, shy1, sectnum);
3861         return;
3862     }
3863 
3864     mptr1 = &slopalookup[shy1]; mptr2 = mptr1+1;
3865 
3866     for (x=dax1; x<=dax2; x++)
3867     {
3868         if (dastat == 0) { y1 = umost[x]; y2 = min(dmost[x],uplc[x])-1; }
3869         else { y1 = max(umost[x],dplc[x]); y2 = dmost[x]-1; }
3870         if (y1 <= y2)
3871         {
3872             shy1 = y1+(shoffs>>15);
3873             shy2 = y2+(shoffs>>15);
3874 
3875             // Ridiculously steep gradient?
3876             if ((unsigned)shy1 >= SLOPALOOKUPSIZ)
3877             {
3878                 OSD_Printf("%s:%d: slopalookup[%" PRId32 "] overflow drawing sector %d!\n", EDUKE32_FUNCTION, __LINE__, shy1, sectnum);
3879                 goto next_most;
3880             }
3881             if ((unsigned)shy2 >= SLOPALOOKUPSIZ)
3882             {
3883                 OSD_Printf("%s:%d: slopalookup[%" PRId32 "] overflow drawing sector %d!\n", EDUKE32_FUNCTION, __LINE__, shy2, sectnum);
3884                 goto next_most;
3885             }
3886 
3887             intptr_t *nptr1 = &slopalookup[shy1];
3888             intptr_t *nptr2 = &slopalookup[shy2];
3889 
3890             while (nptr1 <= mptr1)
3891             {
3892                 *mptr1-- = j + getpalookupsh(mulscale24(krecipasm(m1),globvis));
3893                 m1 -= l;
3894             }
3895             while (nptr2 >= mptr2)
3896             {
3897                 *mptr2++ = j + getpalookupsh(mulscale24(krecipasm(m2),globvis));
3898                 m2 += l;
3899             }
3900 
3901             globalx3 = (globalx2>>10);
3902             globaly3 = (globaly2>>10);
3903             asm3 = mulscale16(y2,globalzd) + (globalzx>>6);
3904             switch (globalorientation&0x180)
3905             {
3906             case 0:
3907                 slopevlin(ylookup[y2]+x+frameoffset,krecipasm(asm3>>3),(intptr_t)nptr2,y2-y1+1,globalx1,globaly1);
3908                 break;
3909             case 128:
3910                 mslopevlin((uint8_t *)(ylookup[y2]+x+frameoffset),nptr2,y2-y1+1,globalx1,globaly1);
3911                 break;
3912             case 256:
3913             case 384:
3914                 tslopevlin((uint8_t *)(ylookup[y2]+x+frameoffset),nptr2,y2-y1+1,globalx1,globaly1);
3915                 break;
3916             }
3917 
3918             if ((x&15) == 0) faketimerhandler();
3919         }
3920 next_most:
3921         globalx2 += globalx;
3922         globaly2 += globaly;
3923         globalzx += globalz;
3924         shoffs += shinc;
3925     }
3926 }
3927 
3928 
3929 //
3930 // parascan (internal)
3931 //
parascan(char dastat,int32_t bunch)3932 static void parascan(char dastat, int32_t bunch)
3933 {
3934     int32_t j, k, l, m, n, x, z, wallnum, nextsectnum, globalhorizbak;
3935     int16_t *topptr, *botptr;
3936 
3937     int32_t logtilesizy, tsizy;
3938 
3939     int32_t sectnum = thesector[bunchfirst[bunch]];
3940     auto const sec = (usectorptr_t)&sector[sectnum];
3941 
3942     globalhorizbak = globalhoriz;
3943     globvis = globalpisibility;
3944     //globalorientation = 0L;
3945     if (sec->visibility != 0)
3946         globvis = mulscale4(globvis, (uint8_t)(sec->visibility+16));
3947 
3948     if (dastat == 0)
3949     {
3950         globalpal = sec->ceilingpal;
3951         globalpicnum = sec->ceilingpicnum;
3952         globalshade = (int32_t)sec->ceilingshade;
3953         globalxpanning = (int32_t)sec->ceilingxpanning;
3954         globalypanning = (int32_t)sec->ceilingypanning;
3955         topptr = umost;
3956         botptr = uplc;
3957     }
3958     else
3959     {
3960         globalpal = sec->floorpal;
3961         globalpicnum = sec->floorpicnum;
3962         globalshade = (int32_t)sec->floorshade;
3963         globalxpanning = (int32_t)sec->floorxpanning;
3964         globalypanning = (int32_t)sec->floorypanning;
3965         topptr = dplc;
3966         botptr = dmost;
3967     }
3968 
3969     if ((unsigned)globalpicnum >= MAXTILES) globalpicnum = 0;
3970     tileUpdatePicnum(&globalpicnum, sectnum);
3971     setgotpic(globalpicnum);
3972 
3973     logtilesizy = (picsiz[globalpicnum]>>4);
3974     tsizy = tilesiz[globalpicnum].y;
3975 
3976     if (tsizy==0)
3977         return;
3978 
3979 
3980     int32_t dapyscale, dapskybits, dapyoffs, daptileyscale;
3981     int8_t const * const dapskyoff = getpsky(globalpicnum, &dapyscale, &dapskybits, &dapyoffs, &daptileyscale);
3982 
3983     globalshiftval = logtilesizy;
3984 
3985     // before proper non-power-of-two tilesizy drawing
3986     if (oldnonpow2() && pow2long[logtilesizy] != tsizy)
3987         globalshiftval++;
3988 #ifdef CLASSIC_NONPOW2_YSIZE_WALLS
3989     // non power-of-two y size textures!
3990     if ((!oldnonpow2() && pow2long[logtilesizy] != tsizy) || tsizy >= 512)
3991     {
3992         globaltilesizy = tsizy;
3993         globalyscale = 65536 / tsizy;
3994         globalshiftval = 0;
3995         globalzd = divscale32(((tsizy>>1)+dapyoffs), tsizy) + ((uint32_t)globalypanning<<24);
3996     }
3997     else
3998 #endif
3999     {
4000         globalshiftval = 32-globalshiftval;
4001         globalyscale = (8<<(globalshiftval-19));
4002         globalzd = (decltype(globalzd)((tsizy >> 1) + dapyoffs) << globalshiftval) + (decltype(globalzd)(globalypanning) << 24);
4003     }
4004     globalyscale = divscale16(globalyscale,daptileyscale);
4005 
4006     //if (globalorientation&256) globalyscale = -globalyscale, globalzd = -globalzd;
4007 
4008     if (dapyscale != 65536)
4009         globalhoriz = mulscale16(globalhoriz-(ydimen>>1),dapyscale) + (ydimen>>1);
4010 
4011     k = 27 - (picsiz[globalpicnum]&15) - dapskybits;
4012 
4013     // WGR2 SVN: select new episode after playing wgmicky1 with Polymer
4014     //  (maybe switched to classic earlier).
4015     //  --> rendmode==0, glrendermode == REND_POLYMER, we end up with globalpicnum==266,
4016     //      picsiz...==9 and dapskybits==3
4017     // FIXME ?
4018     if (k < 0)
4019         k = 0;
4020 
4021     x = -1;
4022 
4023     for (z=bunchfirst[bunch]; z>=0; z=bunchp2[z])
4024     {
4025         wallnum = thewall[z]; nextsectnum = wall[wallnum].nextsector;
4026 
4027         if (nextsectnum >= 0)  //else negative array access
4028         {
4029             if (dastat == 0) j = sector[nextsectnum].ceilingstat;
4030             else j = sector[nextsectnum].floorstat;
4031         }
4032 
4033         if ((nextsectnum < 0) || (wall[wallnum].cstat&32) || ((j&1) == 0))
4034         {
4035             if (x == -1) x = xb1[z];
4036 
4037             if (parallaxtype == 0 || no_radarang2)
4038             {
4039                 n = mulscale16(xdimenrecip,viewingrange);
4040                 for (j=xb1[z]; j<=xb2[z]; j++)
4041                     lplc[j] = ((mulscale7(j-halfxdimen,n)+qglobalang)&0x7FFFFFF)>>k;
4042             }
4043             else
4044             {
4045                 for (j=xb1[z]; j<=xb2[z]; j++)
4046                     lplc[j] = ((radarang2[j]+qglobalang)&0x7FFFFFF)>>k;
4047             }
4048 
4049             if (parallaxtype == 2 && !no_radarang2)
4050             {
4051                 n = mulscale16(xdimscale,viewingrange);
4052                 for (j=xb1[z]; j<=xb2[z]; j++)
4053                     swplc[j] = mulscale14(sintable[((radarang2[j]>>16)+512)&2047],n);
4054             }
4055             else
4056                 clearbuf(&swplc[xb1[z]],xb2[z]-xb1[z]+1,mulscale16(xdimscale,viewingrange));
4057         }
4058         else if (x >= 0)
4059         {
4060             l = globalpicnum; m = (picsiz[globalpicnum]&15);
4061             globalpicnum = l + dapskyoff[lplc[x]>>m];
4062 
4063             if (((lplc[x]^lplc[xb1[z]-1])>>m) == 0)
4064                 wallscan(x,xb1[z]-1,topptr,botptr,swplc,lplc);
4065             else
4066             {
4067                 j = x;
4068                 while (x < xb1[z])
4069                 {
4070                     n = l + dapskyoff[lplc[x]>>m];
4071                     if (n != globalpicnum)
4072                     {
4073                         wallscan(j,x-1,topptr,botptr,swplc,lplc);
4074                         j = x;
4075                         globalpicnum = n;
4076                     }
4077                     x++;
4078                 }
4079                 if (j < x)
4080                     wallscan(j,x-1,topptr,botptr,swplc,lplc);
4081             }
4082 
4083             globalpicnum = l;
4084             x = -1;
4085         }
4086     }
4087 
4088     if (x >= 0)
4089     {
4090         l = globalpicnum; m = (picsiz[globalpicnum]&15);
4091         globalpicnum = l + dapskyoff[lplc[x]>>m];
4092 
4093         if (((lplc[x]^lplc[xb2[bunchlast[bunch]]])>>m) == 0)
4094             wallscan(x,xb2[bunchlast[bunch]],topptr,botptr,swplc,lplc);
4095         else
4096         {
4097             j = x;
4098             while (x <= xb2[bunchlast[bunch]])
4099             {
4100                 n = l + dapskyoff[lplc[x]>>m];
4101                 if (n != globalpicnum)
4102                 {
4103                     wallscan(j,x-1,topptr,botptr,swplc,lplc);
4104                     j = x;
4105                     globalpicnum = n;
4106                 }
4107                 x++;
4108             }
4109             if (j <= x)
4110                 wallscan(j,x-1,topptr,botptr,swplc,lplc);
4111         }
4112         globalpicnum = l;
4113     }
4114     globalhoriz = globalhorizbak;
4115 }
4116 
4117 
4118 // set orientation, panning, shade, pal; picnum
setup_globals_wall1(uwallptr_t wal,int32_t dapicnum)4119 static void setup_globals_wall1(uwallptr_t wal, int32_t dapicnum)
4120 {
4121     globalorientation = wal->cstat;
4122 
4123     globalpicnum = dapicnum;
4124     if ((unsigned)globalpicnum >= MAXTILES) globalpicnum = 0;
4125     tileUpdatePicnum(&globalpicnum, 16384);
4126 
4127     globalxpanning = wal->xpanning;
4128     globalypanning = wal->ypanning;
4129 
4130     globalshade = wal->shade;
4131     globalpal = wal->pal;
4132     if (palookup[globalpal] == NULL) globalpal = 0;    // JBF: fixes crash
4133 }
4134 
setup_globals_wall2(uwallptr_t wal,uint8_t secvisibility,int32_t topzref,int32_t botzref)4135 static void setup_globals_wall2(uwallptr_t wal, uint8_t secvisibility, int32_t topzref, int32_t botzref)
4136 {
4137     const int32_t logtilesizy = (picsiz[globalpicnum]>>4);
4138     const int32_t tsizy = tilesiz[globalpicnum].y;
4139 
4140     if (tsizy==0)
4141     {
4142         globalshiftval = -1;
4143         return;
4144     }
4145 
4146     globvis = globalvisibility;
4147     if (secvisibility != 0)
4148         globvis = mulscale4(globvis, (uint8_t)(secvisibility+16));
4149 
4150     globalshiftval = logtilesizy;
4151 
4152     // before proper non-power-of-two tilesizy drawing
4153     if (oldnonpow2() && pow2long[logtilesizy] != tsizy)
4154         globalshiftval++;
4155 #ifdef CLASSIC_NONPOW2_YSIZE_WALLS
4156     // non power-of-two y size textures!
4157     if ((!oldnonpow2() && pow2long[logtilesizy] != tsizy) || tsizy >= 512)
4158     {
4159         globaltilesizy = tsizy;
4160         globalyscale = divscale13(wal->yrepeat, tsizy);
4161         globalshiftval = 0;
4162     }
4163     else
4164 #endif
4165     {
4166         // globalshiftval==13 --> globalshiftval==19
4167         //  ==> upper texture y size limit *here* = 8192
4168         globalshiftval = 32-globalshiftval;
4169         globalyscale = wal->yrepeat<<(globalshiftval-19);
4170     }
4171 
4172     if ((globalorientation&4) == 0)
4173         globalzd = (((int64_t)(globalposz-topzref)*globalyscale)<<8);
4174     else  // bottom-aligned
4175         globalzd = (((int64_t)(globalposz-botzref)*globalyscale)<<8);
4176 
4177     globalzd += decltype(globalzd)(globalypanning) << 24;
4178 
4179     if (globalorientation&256)  // y-flipped
4180         globalyscale = -globalyscale, globalzd = -(inthi_t)globalzd;
4181 }
4182 
4183 
4184 /* _______________
4185  * X umost #######
4186  * ###### ________
4187  * ______/
4188  * X dwall
4189  *
4190  * ________
4191  * X uwall \______
4192  * ///////////////
4193  * _______________
4194  * X dmost
4195  */
4196 
4197 #ifdef YAX_ENABLE
4198 // returns: should dmost be raised when drawing a "ceiling wall"?
should_clip_cwall(int32_t x1,int32_t x2)4199 static int32_t should_clip_cwall(int32_t x1, int32_t x2)
4200 {
4201     int32_t x;
4202 
4203     if (yax_globallev <= YAX_MAXDRAWS)
4204         return 1;
4205 
4206     for (x=x1; x<=x2; x++)
4207         if (dwall[x] < dmost[x] || uplc[x] < dmost[x])
4208             return 1;
4209 
4210     return 0;
4211 }
4212 
4213 // returns: should umost be lowered when drawing a "floor wall"?
should_clip_fwall(int32_t x1,int32_t x2)4214 static int32_t should_clip_fwall(int32_t x1, int32_t x2)
4215 {
4216     int32_t x;
4217 
4218     if (yax_globallev >= YAX_MAXDRAWS)
4219         return 1;
4220 
4221     for (x=x1; x<=x2; x++)
4222         if (uwall[x] > umost[x] || dplc[x] > umost[x])
4223             return 1;
4224 
4225     return 0;
4226 }
4227 #endif
4228 
4229 //
4230 // drawalls (internal)
4231 //
classicDrawBunches(int32_t bunch)4232 static void classicDrawBunches(int32_t bunch)
4233 {
4234     int32_t i, x;
4235 
4236     int32_t z = bunchfirst[bunch];
4237 
4238     const int32_t sectnum = thesector[z];
4239     auto const sec = (usectorptr_t)&sector[sectnum];
4240 
4241     uint8_t andwstat1 = 0xff, andwstat2 = 0xff;
4242 
4243     for (; z>=0; z=bunchp2[z]) //uplc/dplc calculation
4244     {
4245         andwstat1 &= wallmost(uplc,z,sectnum,(uint8_t)0);
4246         andwstat2 &= wallmost(dplc,z,sectnum,(uint8_t)1);
4247     }
4248 
4249 #ifdef YAX_ENABLE
4250     if (g_nodraw)
4251     {
4252         int32_t baselevp, checkcf;
4253         int16_t bn[2];
4254 # if 0
4255         int32_t obunchchk = (1 && yax_globalbunch>=0 &&
4256                              haveymost[yax_globalbunch>>3]&pow2char[yax_globalbunch&7]);
4257 
4258         // if (obunchchk)
4259         const int32_t x2 = yax_globalbunch*xdimen;
4260 # endif
4261         baselevp = (yax_globallev == YAX_MAXDRAWS);
4262 
4263         yax_getbunches(sectnum, &bn[0], &bn[1]);
4264         checkcf = (bn[0]>=0) + ((bn[1]>=0)<<1);
4265         if (!baselevp)
4266             checkcf &= (1<<yax_globalcf);
4267 
4268         if ((andwstat1&3) == 3)  // ceilings clipped
4269             checkcf &= ~1;
4270         if ((andwstat2&12) == 12)  // floors clipped
4271             checkcf &= ~2;
4272 
4273         for (i=0; i<2; i++)
4274             if (checkcf&(1<<i))
4275             {
4276                 if ((haveymost[bn[i]>>3]&pow2char[bn[i]&7])==0)
4277                 {
4278                     // init yax *most arrays for that bunch
4279                     haveymost[bn[i]>>3] |= pow2char[bn[i]&7];
4280                     for (x=xdimen*bn[i]; x<xdimen*(bn[i]+1); x++)
4281                     {
4282                         yumost[x] = ydimen;
4283                         ydmost[x] = 0;
4284                     }
4285                 }
4286 
4287                 const int32_t x1 = bn[i]*xdimen;
4288 
4289                 for (x=x1+xb1[bunchfirst[bunch]]; x<=x1+xb2[bunchlast[bunch]]; x++)
4290                 {
4291                     if (i==YAX_CEILING)
4292                     {
4293                         yumost[x] = min(yumost[x], umost[x-x1]);
4294                         ydmost[x] = max(ydmost[x], min(dmost[x-x1], uplc[x-x1]));
4295                     }
4296                     else
4297                     {
4298                         yumost[x] = min(yumost[x], max(umost[x-x1], dplc[x-x1]));
4299                         ydmost[x] = max(ydmost[x], dmost[x-x1]);
4300                     }
4301 # if 0
4302                     if (obunchchk)
4303                     {
4304                         yumost[x] = max(yumost[x], yumost[x-x1+x2]);
4305                         ydmost[x] = min(ydmost[x], ydmost[x-x1+x2]);
4306                     }
4307 # endif
4308                 }
4309             }
4310     }
4311     else
4312 #endif
4313     {
4314         if ((andwstat1&3) != 3)     //draw ceilings
4315 #ifdef YAX_ENABLE
4316             // this is to prevent double-drawing of translucent masked ceilings
4317             if (r_tror_nomaskpass==0 || yax_globallev==YAX_MAXDRAWS || (sec->ceilingstat&256)==0 ||
4318                 yax_nomaskpass==1 || !(yax_gotsector[sectnum>>3]&pow2char[sectnum&7]))
4319 #endif
4320         {
4321             if ((sec->ceilingstat&3) == 2)
4322                 grouscan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum,0);
4323             else if ((sec->ceilingstat&1) == 0)
4324                 ceilscan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum);
4325             else
4326                 parascan(0,bunch);
4327         }
4328 
4329         if ((andwstat2&12) != 12)   //draw floors
4330 #ifdef YAX_ENABLE
4331             // this is to prevent double-drawing of translucent masked floors
4332             if (r_tror_nomaskpass==0 || yax_globallev==YAX_MAXDRAWS || (sec->floorstat&256)==0 ||
4333                 yax_nomaskpass==1 || !(yax_gotsector[sectnum>>3]&pow2char[sectnum&7]))
4334 #endif
4335         {
4336             if ((sec->floorstat&3) == 2)
4337                 grouscan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum,1);
4338             else if ((sec->floorstat&1) == 0)
4339                 florscan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum);
4340             else
4341                 parascan(1,bunch);
4342         }
4343     }
4344 
4345 
4346     //DRAW WALLS SECTION!
4347     for (z=bunchfirst[bunch]; z>=0; z=bunchp2[z])
4348     {
4349         const int32_t x1 = xb1[z], x2 = xb2[z];
4350 
4351         if (umost[x2] >= dmost[x2])
4352         {
4353             for (x=x1; x<x2; x++)
4354                 if (umost[x] < dmost[x])
4355                     break;
4356             if (x >= x2)
4357             {
4358                 smostwall[smostwallcnt] = z;
4359                 smostwalltype[smostwallcnt] = 0;
4360                 smostwallcnt++;
4361                 continue;
4362             }
4363         }
4364 
4365         const int32_t wallnum = thewall[z];
4366         auto const wal = (uwallptr_t)&wall[wallnum];
4367 
4368         const int32_t nextsectnum = wal->nextsector;
4369         auto const nextsec = nextsectnum>=0 ? (usectorptr_t)&sector[nextsectnum] : NULL;
4370 
4371         int32_t gotswall = 0;
4372 
4373         const int32_t startsmostwallcnt = smostwallcnt;
4374         const int32_t startsmostcnt = smostcnt;
4375 
4376         if (searchit == 2 && (searchx >= x1 && searchx <= x2))
4377         {
4378             if (searchy <= uplc[searchx]
4379 #ifdef YAX_ENABLE
4380                 && umost[searchx] <= searchy && getceilzofslope(sectnum, globalposx, globalposy) <= globalposz
4381                 && (yax_getbunch(sectnum, YAX_CEILING) < 0 || showinvisibility || (sec->ceilingstat&(256+128)) || klabs(yax_globallev-YAX_MAXDRAWS)==YAX_MAXDRAWS)
4382 #endif
4383                 ) //ceiling
4384             {
4385                 searchsector = sectnum; searchwall = wallnum;
4386                 searchstat = 1; searchit = 1;
4387             }
4388             else if (dplc[searchx] <= searchy
4389 #ifdef YAX_ENABLE
4390                      && searchy < dmost[searchx] && getflorzofslope(sectnum, globalposx, globalposy) >= globalposz
4391                      && (yax_getbunch(sectnum, YAX_FLOOR) < 0 || showinvisibility || (sec->floorstat&(256+128)) || klabs(yax_globallev-YAX_MAXDRAWS)==YAX_MAXDRAWS)
4392 #endif
4393                 ) //floor
4394             {
4395                 searchsector = sectnum; searchwall = wallnum;
4396                 searchstat = 2; searchit = 1;
4397             }
4398         }
4399 
4400 #ifdef YAX_ENABLE
4401         if (yax_nomaskpass==0 || !yax_isislandwall(wallnum, !yax_globalcf) || (yax_nomaskdidit=1, 0))
4402 #endif
4403         if (nextsectnum >= 0)
4404         {
4405             // 2       <---       3
4406             // x------------------x
4407             // 0       --->       1
4408             //
4409             //     4 (our pos, z wrt the nextsector!)
4410 
4411             int32_t cz[5], fz[5];
4412 
4413             getzsofslope((int16_t)sectnum,wal->x,wal->y,&cz[0],&fz[0]);
4414             getzsofslope((int16_t)sectnum,wall[wal->point2].x,wall[wal->point2].y,&cz[1],&fz[1]);
4415             getzsofslope((int16_t)nextsectnum,wal->x,wal->y,&cz[2],&fz[2]);
4416             getzsofslope((int16_t)nextsectnum,wall[wal->point2].x,wall[wal->point2].y,&cz[3],&fz[3]);
4417             getzsofslope((int16_t)nextsectnum,globalposx,globalposy,&cz[4],&fz[4]);
4418 
4419             if ((wal->cstat&48) == 16)
4420                 maskwall[maskwallcnt++] = z;
4421 
4422             if (((sec->ceilingstat&1) == 0) || ((nextsec->ceilingstat&1) == 0))
4423             {
4424                 if ((cz[2] <= cz[0]) && (cz[3] <= cz[1]))
4425                 {
4426 //                    if (globparaceilclip)
4427                     if (getceilzofslope(sectnum, globalposx, globalposy) <= globalposz)
4428                         for (x=x1; x<=x2; x++)
4429                             if (uplc[x] > umost[x])
4430                                 if (umost[x] <= dmost[x])
4431                                 {
4432                                     umost[x] = uplc[x];
4433                                     if (umost[x] > dmost[x]) numhits--;
4434                                 }
4435                 }
4436                 else
4437                 {
4438                     wallmost(dwall,z,nextsectnum,(uint8_t)0);
4439 
4440                     if ((cz[2] > fz[0]) || (cz[3] > fz[1]))
4441                         for (i=x1; i<=x2; i++) if (dwall[i] > dplc[i]) dwall[i] = dplc[i];
4442 
4443                     if (searchit == 2 && (searchx >= x1 && searchx <= x2))
4444 #ifdef YAX_ENABLE
4445                         if (uplc[searchx] <= searchy)
4446 #endif
4447                         if (searchy <= dwall[searchx]) //wall
4448                         {
4449                             searchsector = sectnum; searchbottomwall = searchwall = wallnum;
4450                             searchisbottom = 0;
4451                             searchstat = 0; searchit = 1;
4452                         }
4453 
4454                     setup_globals_wall1(wal, wal->picnum);
4455                     setup_globals_wall2(wal, sec->visibility, nextsec->ceilingz, sec->ceilingz);
4456 
4457                     gotswall = 1;
4458                     prepwall(z,wal);
4459                     wallscan(x1,x2,uplc,dwall,swall,lwall);
4460 
4461                     if ((cz[2] >= cz[0]) && (cz[3] >= cz[1]))
4462                     {
4463 #ifdef YAX_ENABLE
4464                         if (should_clip_cwall(x1, x2))
4465                         {
4466 #endif
4467                         for (x=x1; x<=x2; x++)
4468                             if (dwall[x] > umost[x])
4469                                 if (umost[x] <= dmost[x])
4470                                 {
4471                                     umost[x] = dwall[x];
4472                                     if (umost[x] > dmost[x]) numhits--;
4473                                 }
4474 #ifdef YAX_ENABLE
4475                         }
4476                         else if (getceilzofslope(sectnum, globalposx, globalposy) <= globalposz)
4477                             for (x=x1; x<=x2; x++)
4478                                 if (uplc[x] > umost[x])
4479                                     if (umost[x] <= dmost[x])
4480                                     {
4481                                         umost[x] = uplc[x];
4482                                         if (umost[x] > dmost[x]) numhits--;
4483                                     }
4484 #endif
4485                     }
4486                     else
4487                     {
4488 #ifdef YAX_ENABLE
4489                         if (should_clip_cwall(x1, x2))
4490                         {
4491 #endif
4492                         for (x=x1; x<=x2; x++)
4493                             if (umost[x] <= dmost[x])
4494                             {
4495                                 i = max(uplc[x],dwall[x]);
4496                                 if (i > umost[x])
4497                                 {
4498                                     umost[x] = i;
4499                                     if (umost[x] > dmost[x]) numhits--;
4500                                 }
4501                             }
4502 #ifdef YAX_ENABLE
4503                         }
4504                         else if (getceilzofslope(sectnum, globalposx, globalposy) <= globalposz)
4505                             for (x=x1; x<=x2; x++)
4506                                 if (uplc[x] > umost[x])
4507                                     if (umost[x] <= dmost[x])
4508                                     {
4509                                         umost[x] = uplc[x];
4510                                         if (umost[x] > dmost[x]) numhits--;
4511                                     }
4512 #endif
4513                     }
4514                 }
4515 
4516                 if ((cz[2] < cz[0]) || (cz[3] < cz[1]) || (globalposz < cz[4]))
4517                 {
4518                     i = x2-x1+1;
4519                     if (smostcnt+i < ysavecnt)
4520                     {
4521                         smoststart[smostwallcnt] = smostcnt;
4522                         smostwall[smostwallcnt] = z;
4523                         smostwalltype[smostwallcnt] = 1;   //1 for umost
4524                         smostwallcnt++;
4525                         copybufbyte(&umost[x1],&smost[smostcnt],i*sizeof(smost[0]));
4526                         smostcnt += i;
4527                     }
4528                 }
4529             }
4530 
4531             if (((sec->floorstat&1) == 0) || ((nextsec->floorstat&1) == 0))
4532             {
4533                 if ((fz[2] >= fz[0]) && (fz[3] >= fz[1]))
4534                 {
4535 //                    if (globparaflorclip)
4536                     if (getflorzofslope(sectnum, globalposx, globalposy) >= globalposz)
4537                         for (x=x1; x<=x2; x++)
4538                             if (dplc[x] < dmost[x])
4539                                 if (umost[x] <= dmost[x])
4540                                 {
4541                                     dmost[x] = dplc[x];
4542                                     if (umost[x] > dmost[x]) numhits--;
4543                                 }
4544                 }
4545                 else
4546                 {
4547                     wallmost(uwall,z,nextsectnum,(uint8_t)1);
4548 
4549                     if ((fz[2] < cz[0]) || (fz[3] < cz[1]))
4550                         for (i=x1; i<=x2; i++) if (uwall[i] < uplc[i]) uwall[i] = uplc[i];
4551 
4552                     if (searchit == 2 && (searchx >= x1 && searchx <= x2))
4553 #ifdef YAX_ENABLE
4554                         if (dplc[searchx] >= searchy)
4555 #endif
4556                         if (searchy >= uwall[searchx]) //wall
4557                         {
4558                             searchsector = sectnum; searchbottomwall = searchwall = wallnum;
4559                             if ((wal->cstat&2) > 0) searchbottomwall = wal->nextwall;
4560                             searchisbottom = 1;
4561                             searchstat = 0; searchit = 1;
4562                         }
4563 
4564                     auto const twal = (wal->cstat&2) ? (uwallptr_t)&wall[wal->nextwall] : wal;
4565                     setup_globals_wall1(twal, twal->picnum);
4566 
4567                     setup_globals_wall2(wal, sec->visibility, nextsec->floorz, sec->ceilingz);
4568 
4569                     if (gotswall == 0) { gotswall = 1; prepwall(z,wal); }
4570                     wallscan(x1,x2,uwall,dplc,swall,lwall);
4571 
4572                     if ((fz[2] <= fz[0]) && (fz[3] <= fz[1]))
4573                     {
4574 #ifdef YAX_ENABLE
4575                         if (should_clip_fwall(x1, x2))
4576                         {
4577 #endif
4578                         for (x=x1; x<=x2; x++)
4579                             if (uwall[x] < dmost[x] && umost[x] <= dmost[x])
4580                             {
4581                                 dmost[x] = uwall[x];
4582                                 if (umost[x] > dmost[x]) numhits--;
4583                             }
4584 #ifdef YAX_ENABLE
4585                         }
4586                         else if (getflorzofslope(sectnum, globalposx, globalposy) >= globalposz)
4587                             for (x = x1; x <= x2; x++)
4588                                 if (dplc[x] < dmost[x])
4589                                     if (umost[x] <= dmost[x])
4590                                     {
4591                                         dmost[x] = dplc[x];
4592                                         if (umost[x] > dmost[x]) numhits--;
4593                                     }
4594 #endif
4595                     }
4596                     else
4597                     {
4598 #ifdef YAX_ENABLE
4599                         if (should_clip_fwall(x1, x2))
4600                         {
4601 #endif
4602                         for (x=x1; x<=x2; x++)
4603                             if (umost[x] <= dmost[x])
4604                             {
4605                                 i = min(dplc[x],uwall[x]);
4606                                 if (i < dmost[x])
4607                                 {
4608                                     dmost[x] = i;
4609                                     if (umost[x] > dmost[x]) numhits--;
4610                                 }
4611                             }
4612 #ifdef YAX_ENABLE
4613                         }
4614                         else if (getflorzofslope(sectnum, globalposx, globalposy) >= globalposz)
4615                             for (x = x1; x <= x2; x++)
4616                                 if (dplc[x] < dmost[x])
4617                                     if (umost[x] <= dmost[x])
4618                                     {
4619                                         dmost[x] = dplc[x];
4620                                         if (umost[x] > dmost[x]) numhits--;
4621                                     }
4622 #endif
4623                     }
4624                 }
4625 
4626                 if ((fz[2] > fz[0]) || (fz[3] > fz[1]) || (globalposz > fz[4]))
4627                 {
4628                     i = x2-x1+1;
4629                     if (smostcnt+i < ysavecnt)
4630                     {
4631                         smoststart[smostwallcnt] = smostcnt;
4632                         smostwall[smostwallcnt] = z;
4633                         smostwalltype[smostwallcnt] = 2;   //2 for dmost
4634                         smostwallcnt++;
4635                         copybufbyte(&dmost[x1],&smost[smostcnt],i*sizeof(smost[0]));
4636                         smostcnt += i;
4637                     }
4638                 }
4639             }
4640 
4641             if (numhits < 0)
4642                 return;
4643 
4644             if (!(wal->cstat&32) && (gotsector[nextsectnum>>3]&pow2char[nextsectnum&7]) == 0)
4645             {
4646                 if (umost[x2] < dmost[x2])
4647                     classicScanSector(nextsectnum);
4648                 else
4649                 {
4650                     for (x=x1; x<x2; x++)
4651                         if (umost[x] < dmost[x])
4652                             { classicScanSector(nextsectnum); break; }
4653 
4654                     //If can't see sector beyond, then cancel smost array and just
4655                     //store wall!
4656                     if (x == x2)
4657                     {
4658                         smostwallcnt = startsmostwallcnt;
4659                         smostcnt = startsmostcnt;
4660                         smostwall[smostwallcnt] = z;
4661                         smostwalltype[smostwallcnt] = 0;
4662                         smostwallcnt++;
4663                     }
4664                 }
4665             }
4666         }
4667 
4668         if (nextsectnum < 0 || (wal->cstat&32))   //White/1-way wall
4669         {
4670             setup_globals_wall1(wal, (nextsectnum < 0) ? wal->picnum : wal->overpicnum);
4671             setup_globals_wall2(wal, sec->visibility,
4672                                 (nextsectnum >= 0) ? nextsec->ceilingz : sec->ceilingz,
4673                                 (nextsectnum >= 0) ? sec->ceilingz : sec->floorz);
4674 
4675             if (gotswall == 0) { gotswall = 1; prepwall(z,wal); }
4676             wallscan(x1,x2,uplc,dplc,swall,lwall);
4677 
4678 #ifdef YAX_ENABLE
4679             // TODO: slopes?
4680 
4681             if (globalposz > sec->floorz && yax_isislandwall(wallnum, YAX_FLOOR))
4682             {
4683                 for (x=x1; x<=x2; x++)
4684                     if (dplc[x] > umost[x] && umost[x] <= dmost[x])
4685                     {
4686                         umost[x] = dplc[x];
4687                         if (umost[x] > dmost[x]) numhits--;
4688                     }
4689             }
4690             else if (globalposz < sec->ceilingz && yax_isislandwall(wallnum, YAX_CEILING))
4691             {
4692                 for (x=x1; x<=x2; x++)
4693                     if (uplc[x] < dmost[x] && umost[x] <= dmost[x])
4694                     {
4695                         dmost[x] = uplc[x];
4696                         if (umost[x] > dmost[x]) numhits--;
4697                     }
4698             }
4699             else
4700 #endif
4701             for (x=x1; x<=x2; x++)
4702                 if (umost[x] <= dmost[x])
4703                     { umost[x] = 1; dmost[x] = 0; numhits--; }
4704             smostwall[smostwallcnt] = z;
4705             smostwalltype[smostwallcnt] = 0;
4706             smostwallcnt++;
4707 
4708             if (searchit == 2 && (x1 <= searchx && searchx <= x2))
4709 #ifdef YAX_ENABLE
4710                 if (uplc[searchx] <= searchy && searchy < dplc[searchx])
4711 #endif
4712             {
4713                 searchit = 1; searchsector = sectnum;
4714                 searchbottomwall = searchwall = wallnum;
4715                 searchstat = (nextsectnum < 0) ? 0 : 4;
4716             }
4717         }
4718 
4719 #ifdef ENGINE_SCREENSHOT_DEBUG
4720         if (engine_screenshot)
4721 # ifdef YAX_ENABLE
4722         if (!g_nodraw)
4723 # endif
4724         {
4725             static char fn[32], tmpbuf[80];
4726             char purple = paletteGetClosestColor(255, 0, 255);
4727             char yellow = paletteGetClosestColor(255, 255, 0);
4728             char *bakframe = (char *)Xaligned_alloc(16, xdim*ydim);
4729 
4730             videoBeginDrawing();  //{{{
4731             Bmemcpy(bakframe, (char *)frameplace, xdim*ydim);
4732             for (x=0; x<xdim; x++)
4733             {
4734                 if (umost[x] > dmost[x])
4735                 {
4736                     *((char *)frameplace + (ydim/2)*bytesperline + x) = yellow;
4737                     *((char *)frameplace + (ydim/2+1)*bytesperline + x) = purple;
4738                     continue;
4739                 }
4740 
4741                 if (umost[x] >= 0 && umost[x] < ydim)
4742                     *((char *)frameplace + umost[x]*bytesperline + x) = purple;
4743 
4744                 if (dmost[x]-1 >= 0 && dmost[x]-1 < ydim)
4745                     *((char *)frameplace + (dmost[x]-1)*bytesperline + x) = yellow;
4746             }
4747 
4748             Bsprintf(tmpbuf, "nmp%d l%d b%d s%d w%d", yax_nomaskpass, yax_globallev-YAX_MAXDRAWS,
4749                      yax_globalbunch, sectnum, wallnum);
4750             printext256(8,8, whitecol,0, tmpbuf, 0);
4751 
4752             Bsprintf(fn, "engshot%04d.png", engine_screenshot);
4753             videoCaptureScreen(fn, 0);
4754             engine_screenshot++;
4755 
4756             Bmemcpy((char *)frameplace, bakframe, xdim*ydim);
4757             videoEndDrawing();  //}}}
4758 
4759             Xaligned_free(bakframe);
4760         }
4761 #endif
4762     }
4763 }
4764 
4765 // High-precision integer type for view-relative x and y in drawvox().
4766 typedef zint_t voxint_t;
4767 
4768 //
4769 // drawvox
4770 //
classicDrawVoxel(int32_t dasprx,int32_t daspry,int32_t dasprz,int32_t dasprang,int32_t daxscale,int32_t dayscale,int32_t daindex,int8_t dashade,char dapal,const int32_t * daumost,const int32_t * dadmost,const int8_t cstat,const int32_t clipcf,int32_t floorz,int32_t ceilingz)4771 static void classicDrawVoxel(int32_t dasprx, int32_t daspry, int32_t dasprz, int32_t dasprang,
4772                              int32_t daxscale, int32_t dayscale, int32_t daindex,
4773                              int8_t dashade, char dapal, const int32_t *daumost, const int32_t *dadmost,
4774                              const int8_t cstat, const int32_t clipcf, int32_t floorz, int32_t ceilingz)
4775 {
4776     int32_t i, j, k, x, y, mip;
4777 
4778     int32_t cosang = cosglobalang;
4779     int32_t sinang = singlobalang;
4780     int32_t sprcosang = sintable[(dasprang+512)&2047];
4781     int32_t sprsinang = sintable[dasprang&2047];
4782 
4783     i = klabs(dmulscale6(dasprx-globalposx, cosang, daspry-globalposy, sinang));
4784     j = getpalookup(mulscale21(globvis,i), dashade)<<8;
4785     setupdrawslab(ylookup[1], FP_OFF(palookup[dapal])+j);
4786 
4787     j = 1310720;
4788     //j *= min(daxscale,dayscale); j >>= 6;  //New hacks (for sized-down voxels)
4789     for (k=0; k<MAXVOXMIPS; k++)
4790     {
4791         if (i < j) { i = k; break; }
4792         j <<= 1;
4793     }
4794     if (k >= MAXVOXMIPS)
4795         i = MAXVOXMIPS-1;
4796 
4797     mip = 0;
4798 
4799     if (novoxmips)
4800     {
4801         mip = i;
4802         i = 0;
4803     }
4804 
4805     char *davoxptr = (char *)voxoff[daindex][i];
4806     if (!davoxptr && i > 0) { davoxptr = (char *)voxoff[daindex][0]; mip = i; i = 0;}
4807     if (!davoxptr)
4808         return;
4809 
4810     if (voxscale[daindex] == 65536)
4811         { daxscale <<= (i+8); dayscale <<= (i+8); }
4812     else
4813     {
4814         daxscale = mulscale8(daxscale<<i,voxscale[daindex]);
4815         dayscale = mulscale8(dayscale<<i,voxscale[daindex]);
4816     }
4817 
4818     const int32_t odayscale = dayscale;
4819     daxscale = mulscale16(daxscale,xyaspect);
4820     daxscale = scale(daxscale, xdimenscale, xdimen<<8);
4821     dayscale = scale(dayscale, mulscale16(xdimenscale,viewingrangerecip), xdimen<<8);
4822 
4823     const int32_t daxscalerecip = divideu32_noinline(1<<30, daxscale);
4824 
4825     int32_t *longptr = (int32_t *)davoxptr;
4826     const int32_t daxsiz = B_LITTLE32(longptr[0]), daysiz = B_LITTLE32(longptr[1]), dazsiz = B_LITTLE32(longptr[2]);
4827     int32_t daxpivot = B_LITTLE32(longptr[3]), daypivot = B_LITTLE32(longptr[4]), dazpivot = B_LITTLE32(longptr[5]);
4828     if (cstat & 4) daxpivot = (daxsiz<<8)-daxpivot;
4829     davoxptr += (6<<2);
4830 
4831     x = mulscale16(globalposx-dasprx, daxscalerecip);
4832     y = mulscale16(globalposy-daspry, daxscalerecip);
4833     const int32_t backx = (dmulscale10(x,sprcosang, y,sprsinang)+daxpivot)>>8;
4834     const int32_t backy = (dmulscale10(y,sprcosang, x,-sprsinang)+daypivot)>>8;
4835     const int32_t cbackx = min(max(backx,0),daxsiz-1);
4836     const int32_t cbacky = min(max(backy,0),daysiz-1);
4837 
4838     sprcosang = mulscale14(daxscale, sprcosang);
4839     sprsinang = mulscale14(daxscale, sprsinang);
4840 
4841     x = (dasprx-globalposx) - dmulscale18(daxpivot,sprcosang, daypivot,-sprsinang);
4842     y = (daspry-globalposy) - dmulscale18(daypivot,sprcosang, daxpivot,sprsinang);
4843 
4844     cosang <<= 2;
4845     sinang <<= 2;
4846 
4847     cosang >>= mip;
4848     sinang >>= mip;
4849 
4850     const voxint_t gxstart = (voxint_t)y*cosang - (voxint_t)x*sinang;
4851     const voxint_t gystart = (voxint_t)x*cosang + (voxint_t)y*sinang;
4852     const int32_t gxinc = dmulscale10(sprsinang,cosang, sprcosang,-sinang);
4853     const int32_t gyinc = dmulscale10(sprcosang,cosang, sprsinang,sinang);
4854 
4855     x = 0; y = 0; j = max(daxsiz,daysiz);
4856     for (i=0; i<=j; i++)
4857     {
4858         ggxinc[i] = x; x += gxinc;
4859         ggyinc[i] = y; y += gyinc;
4860     }
4861 
4862     if ((klabs(globalposz-dasprz)>>10) >= klabs(odayscale))
4863         return;
4864 
4865     int32_t zoff = dazsiz<<14;
4866     if (!(cstat & 128))
4867         zoff += dazpivot<<7;
4868     else if ((cstat&48) != 48)
4869     {
4870         zoff += dazpivot<<7;
4871         zoff -= dazsiz<<14;
4872     }
4873 
4874     const int32_t syoff = divscale21(globalposz-dasprz,odayscale)+zoff;
4875     floorz = min(floorz, dasprz+mulscale21(-zoff+(dazsiz<<15),odayscale));
4876     ceilingz = max(ceilingz, dasprz+mulscale21(-zoff, odayscale));
4877     const int32_t flooroff = divscale21(floorz-globalposz,odayscale);
4878     const int32_t ceilingoff = divscale21(ceilingz-globalposz,odayscale);
4879     int32_t yoff = (klabs(gxinc)+klabs(gyinc))>>1;
4880     longptr = (int32_t *)davoxptr;
4881     int32_t xyvoxoffs = (daxsiz+1)<<2;
4882 
4883     videoBeginDrawing(); //{{{
4884 
4885     for (bssize_t cnt=0; cnt<8; cnt++)
4886     {
4887         int32_t xs=0, ys=0, xi=0, yi=0;
4888 
4889         switch (cnt)
4890         {
4891         case 0:
4892             xs = 0;        ys = 0;        xi = 1;  yi = 1;  break;
4893         case 1:
4894             xs = daxsiz-1; ys = 0;        xi = -1; yi = 1;  break;
4895         case 2:
4896             xs = 0;        ys = daysiz-1; xi = 1;  yi = -1; break;
4897         case 3:
4898             xs = daxsiz-1; ys = daysiz-1; xi = -1; yi = -1; break;
4899         case 4:
4900             xs = 0;        ys = cbacky;   xi = 1;  yi = 2;  break;
4901         case 5:
4902             xs = daxsiz-1; ys = cbacky;   xi = -1; yi = 2;  break;
4903         case 6:
4904             xs = cbackx;   ys = 0;        xi = 2;  yi = 1;  break;
4905         case 7:
4906             xs = cbackx;   ys = daysiz-1; xi = 2;  yi = -1; break;
4907         }
4908 
4909         int32_t xe = cbackx, ye = cbacky;
4910 
4911         if (cnt < 4)
4912         {
4913             if ((xi < 0) && (xe >= xs)) continue;
4914             if ((xi > 0) && (xe <= xs)) continue;
4915             if ((yi < 0) && (ye >= ys)) continue;
4916             if ((yi > 0) && (ye <= ys)) continue;
4917         }
4918         else
4919         {
4920             if ((xi < 0) && (xe > xs)) continue;
4921             if ((xi > 0) && (xe < xs)) continue;
4922             if ((yi < 0) && (ye > ys)) continue;
4923             if ((yi > 0) && (ye < ys)) continue;
4924             xe += xi; ye += yi;
4925         }
4926 
4927         int32_t x1=0, y1=0, z1, x2=0, y2=0, z2;
4928 
4929         i = ksgn(ys-backy) + ksgn(xs-backx)*3 + 4;
4930         switch (i)
4931         {
4932         case 6:
4933         case 7:
4934             x1 = 0; y1 = 0; break;
4935         case 8:
4936         case 5:
4937             x1 = gxinc; y1 = gyinc; break;
4938         case 0:
4939         case 3:
4940             x1 = gyinc; y1 = -gxinc; break;
4941         case 2:
4942         case 1:
4943             x1 = gxinc+gyinc; y1 = gyinc-gxinc; break;
4944         }
4945         switch (i)
4946         {
4947         case 2:
4948         case 5:
4949             x2 = 0; y2 = 0; break;
4950         case 0:
4951         case 1:
4952             x2 = gxinc; y2 = gyinc; break;
4953         case 8:
4954         case 7:
4955             x2 = gyinc; y2 = -gxinc; break;
4956         case 6:
4957         case 3:
4958             x2 = gxinc+gyinc; y2 = gyinc-gxinc; break;
4959         }
4960 
4961         char oand = pow2char[(xs<backx)+0] + pow2char[(ys<backy)+2];
4962 
4963         if (cstat&4)
4964             oand ^= 3;
4965 
4966         char oand16 = oand+16;
4967         char oand32 = oand+32;
4968 
4969         if (cstat&8)
4970         {
4971             oand16 = oand+32;
4972             oand32 = oand+16;
4973         }
4974 
4975         int32_t dagxinc, dagyinc;
4976 
4977         if (yi > 0) { dagxinc = gxinc; dagyinc = mulscale16(gyinc,viewingrangerecip); }
4978         else { dagxinc = -gxinc; dagyinc = -mulscale16(gyinc,viewingrangerecip); }
4979 
4980         //Fix for non 90 degree viewing ranges
4981         const int32_t nxoff = mulscale16(x2-x1,viewingrangerecip);
4982         x1 = mulscale16(x1, viewingrangerecip);
4983 
4984         const voxint_t ggxstart = gxstart + ggyinc[ys];
4985         const voxint_t ggystart = gystart - ggxinc[ys];
4986 
4987         for (x=xs; x!=xe; x+=xi)
4988         {
4989             const int32_t xf = (cstat & 4) ? daxsiz-1-x : x;
4990             const intptr_t slabxoffs = (intptr_t)&davoxptr[B_LITTLE32(longptr[xf])];
4991             int16_t *const shortptr = (int16_t *)&davoxptr[((xf*(daysiz+1))<<1) + xyvoxoffs];
4992 
4993             voxint_t nx = mulscale16z(ggxstart+ggxinc[x], viewingrangerecip) + x1;
4994             voxint_t ny = ggystart + ggyinc[x];
4995 
4996             for (y=ys; y!=ye; y+=yi,nx+=dagyinc,ny-=dagxinc)
4997             {
4998                 if (ny <= nytooclose || ny >= nytoofar)
4999                     continue;
5000 
5001                 char *voxptr = (char *)(B_LITTLE16(shortptr[y])+slabxoffs);
5002                 char *const voxend = (char *)(B_LITTLE16(shortptr[y+1])+slabxoffs);
5003                 if (voxptr == voxend)
5004                     continue;
5005 
5006                 // AMCTC V1 MEGABASE: (ny+y1)>>14 == 65547
5007                 // (after long corridor with the blinds)
5008                 //
5009                 // Also, OOB (<0?) in my amcvoxels_crash.map.
5010                 const int32_t il = clamp((ny+y1)>>14, 1, DISTRECIPSIZ-1);
5011                 int32_t lx = mulscale32(nx>>3, distrecip[il]) + halfxdimen;
5012                 if (lx < 0)
5013                     lx = 0;
5014 
5015                 const int32_t ir = clamp((ny+y2)>>14, 1, DISTRECIPSIZ-1);
5016                 int32_t rx = mulscale32((nx+nxoff)>>3, distrecip[ir]) + halfxdimen;
5017                 if (rx > xdimen)
5018                     rx = xdimen;
5019 
5020                 if (rx <= lx)
5021                     continue;
5022 
5023                 rx -= lx;
5024 
5025                 const int32_t l1 = mulscale(distrecip[clamp((ny-yoff)>>14, 1, DISTRECIPSIZ-1)], dayscale, 12+mip);
5026                 // FIXME! AMCTC RC2/beta shotgun voxel
5027                 // (e.g. training map right after M16 shooting):
5028                 const int32_t l2 = mulscale(distrecip[clamp((ny+yoff)>>14, 1, DISTRECIPSIZ-1)], dayscale, 12+mip);
5029                 int32_t cz1 = 0, cz2 = INT32_MAX;
5030 
5031                 if (clipcf)
5032                 {
5033                     cz1 = mulscale32((ceilingoff < 0) ? l1 : l2, ceilingoff) + globalhoriz;
5034                     cz2 = mulscale32((flooroff < 0) ? l2 : l1, flooroff) + globalhoriz;
5035                 }
5036 
5037                 for (; voxptr<voxend; voxptr+=voxptr[1]+3)
5038                 {
5039                     if (cstat&8)
5040                         j = dazsiz-voxptr[0]-voxptr[1];
5041                     else
5042                         j = voxptr[0];
5043                     j = (j<<15)-syoff;
5044 
5045                     if (j < 0)
5046                     {
5047                         k = j+(voxptr[1]<<15);
5048                         if (k < 0)
5049                         {
5050                             if ((voxptr[2]&oand32) == 0) continue;
5051                             z2 = mulscale32(l2,k) + globalhoriz;     //Below slab
5052                         }
5053                         else
5054                         {
5055                             if ((voxptr[2]&oand) == 0) continue;    //Middle of slab
5056                             z2 = mulscale32(l1,k) + globalhoriz;
5057                         }
5058                         z1 = mulscale32(l1,j) + globalhoriz;
5059                     }
5060                     else
5061                     {
5062                         if ((voxptr[2]&oand16) == 0) continue;
5063                         z1 = mulscale32(l2,j) + globalhoriz;        //Above slab
5064                         z2 = mulscale32(l1,j+(voxptr[1]<<15)) + globalhoriz;
5065                     }
5066 
5067                     int32_t yplc, yinc=0;
5068 
5069                     const int32_t um = max(daumost[lx], cz1);
5070                     const int32_t dm = min(dadmost[lx], cz2);
5071                     if (voxptr[1] == 1)
5072                     {
5073                         yplc = 0; yinc = 0;
5074                         if (z1 < um)
5075                             z1 = um;
5076                     }
5077                     else
5078                     {
5079                         if (z2-z1 >= 1024)
5080                             yinc = divscale16(voxptr[1], z2-z1);
5081                         else if (z2 > z1)
5082                             yinc = lowrecip[z2-z1]*voxptr[1]>>8;
5083 
5084                         if (z1 < um) { yplc = yinc*(um-z1); z1 = um; }
5085                         else yplc = 0;
5086 
5087                         if (cstat & 8)
5088                             yinc = -yinc;
5089                         if (cstat & 8)
5090                             yplc = ((voxptr[1])<<16) - yplc + yinc;
5091                     }
5092 
5093                     if (z2 > dm)
5094                         z2 = dm;
5095                     z2 -= z1;
5096                     if (z2 <= 0)
5097                         continue;
5098 
5099                     drawslab(rx, yplc, z2, yinc, (intptr_t)&voxptr[3], ylookup[z1]+lx+frameoffset);
5100                 }
5101             }
5102         }
5103     }
5104 
5105 #if 0
5106     for (x=0; x<xdimen; x++)
5107     {
5108         if (daumost[x]>=0 && daumost[x]<ydimen)
5109             *(char *)(frameplace + x + bytesperline*daumost[x]) = editorcolors[13];
5110         if (dadmost[x]>=0 && dadmost[x]<ydimen)
5111             *(char *)(frameplace + x + bytesperline*dadmost[x]) = editorcolors[14];
5112     }
5113 #endif
5114 
5115     videoEndDrawing();   //}}}
5116 }
5117 
5118 
setup_globals_sprite1(tspriteptr_t tspr,usectorptr_t sec,int32_t yspan,int32_t yoff,int32_t tilenum,int32_t cstat,int32_t * z1ptr,int32_t * z2ptr)5119 static void setup_globals_sprite1(tspriteptr_t tspr, usectorptr_t sec,
5120                                      int32_t yspan, int32_t yoff, int32_t tilenum,
5121                                      int32_t cstat, int32_t *z1ptr, int32_t *z2ptr)
5122 {
5123     int32_t logtilesizy, tsizy;
5124     int32_t z1, z2 = tspr->z - ((yoff*tspr->yrepeat)<<2);
5125 
5126     if (cstat&128)
5127     {
5128         z2 += ((yspan*tspr->yrepeat)<<1);
5129         if (yspan&1) z2 += (tspr->yrepeat<<1);        //Odd yspans
5130     }
5131     z1 = z2 - ((yspan*tspr->yrepeat)<<2);
5132 
5133     globalorientation = 0;
5134     globalpicnum = tilenum;
5135     if ((unsigned)globalpicnum >= MAXTILES) globalpicnum = 0;
5136     // sprite panning
5137     globalxpanning = (((256-spriteext[tspr->owner].xpanning)&255) * tilesiz[globalpicnum].x)>>8;
5138     globalypanning = 0;
5139 
5140     globvis = globalvisibility;
5141     if (sec->visibility != 0) globvis = mulscale4(globvis, (uint8_t)(sec->visibility+16));
5142 
5143     logtilesizy = (picsiz[globalpicnum]>>4);
5144     tsizy = tilesiz[globalpicnum].y;
5145 
5146     globalshiftval = logtilesizy;
5147 #if !defined CLASSIC_NONPOW2_YSIZE_SPRITES
5148     // before proper non-power-of-two tilesizy drawing
5149     if (pow2long[logtilesizy] != tsizy)
5150         globalshiftval++;
5151 #else
5152     // non power-of-two y size textures!
5153     if (pow2long[logtilesizy] != tsizy || tsizy >= 512)
5154     {
5155         globaltilesizy = tsizy;
5156         globalyscale = (1<<22)/(tsizy*tspr->yrepeat);
5157         globalshiftval = 0;
5158     }
5159     else
5160 #endif
5161     {
5162         globalshiftval = 32-globalshiftval;
5163         globalyscale = divscale(512,tspr->yrepeat,globalshiftval-19);
5164     }
5165 
5166     globalzd = ((int64_t)(globalposz-z1)*globalyscale)<<8;
5167     if ((cstat&8) > 0)
5168     {
5169         globalyscale = -globalyscale;
5170         globalzd = ((int64_t)(globalposz-z2)*globalyscale)<<8;
5171     }
5172 
5173     *z1ptr = z1;
5174     *z2ptr = z2;
5175 }
5176 
5177 //
5178 // drawsprite (internal)
5179 //
5180 
falpha_to_blend(float alpha,int32_t * cstatptr,uint8_t * blendptr,int32_t transbit1,int32_t transbit2)5181 static size_t falpha_to_blend(float alpha, int32_t *cstatptr, uint8_t *blendptr, int32_t transbit1, int32_t transbit2)
5182 {
5183     int32_t cstat = *cstatptr | transbit1;
5184 
5185     int32_t const twonumalphatabs = 2*numalphatabs + (numalphatabs&1);
5186     int32_t blendidx = Blrintf(alpha * twonumalphatabs);
5187     if (blendidx > numalphatabs)
5188     {
5189         blendidx = twonumalphatabs - blendidx;
5190         cstat |= transbit2;
5191     }
5192     else
5193     {
5194         cstat &= ~transbit2;
5195     }
5196 
5197     if (blendidx < 1)
5198         return cstat&transbit2;
5199 
5200     // blendidx now in [1 .. numalphatabs]
5201 
5202     *cstatptr = cstat;
5203     *blendptr = blendidx;
5204     return 0;
5205 }
5206 
mulscale_triple30(int32_t a,int32_t b,int32_t c)5207 static FORCE_INLINE int32_t mulscale_triple30(int32_t a, int32_t b, int32_t c)
5208 {
5209     return ((int64_t)a * b * c)>>30;
5210 }
5211 
classicDrawSprite(int32_t snum)5212 static void classicDrawSprite(int32_t snum)
5213 {
5214     auto const tspr = tspriteptr[snum];
5215     const int32_t sectnum = tspr->sectnum;
5216 
5217     if (sectnum < 0 || bad_tspr(tspr))
5218         return;
5219 
5220     int32_t x1, y1, x2, y2, i, j, k, x;
5221     int32_t z, zz, z1, z2, xp1, yp1, xp2, yp2;
5222     int32_t dax, day, dax1, dax2, y;
5223     int32_t vtilenum = 0;
5224 
5225 
5226     uint8_t blendidx = tspr->blend;
5227     const int32_t xb = spritesxyz[snum].x;
5228     const int32_t yp = spritesxyz[snum].y;
5229 
5230     const int32_t spritenum = tspr->owner;
5231     const float alpha = spriteext[spritenum].alpha;
5232 
5233     auto const sec = (usectorptr_t)&sector[sectnum];
5234 
5235     int32_t cstat=tspr->cstat, tilenum;
5236 
5237     if ((cstat&48) != 48)
5238         tileUpdatePicnum(&tspr->picnum, spritenum+32768);
5239 
5240     if (!(cstat&2) && alpha > 0.0f)
5241     {
5242         if (alpha >= 1.0f)
5243             return;
5244 
5245         if (numalphatabs != 0)
5246         {
5247             if (falpha_to_blend(alpha, &cstat, &blendidx, 2, 512))
5248                 return;
5249         }
5250         else if (alpha >= 1.f/3.f)
5251         {
5252             cstat |= 2;
5253 
5254             if (alpha >= 2.f/3.f)
5255                 cstat |= 512;
5256             else
5257                 cstat &= ~512;
5258 
5259             // Blood's transparency table is inverted
5260             if (bloodhack)
5261                 cstat ^= 512;
5262         }
5263 
5264         tspr->cstat = cstat;
5265     }
5266 
5267     tilenum = tspr->picnum;
5268 
5269     if ((cstat&48)==48)
5270         vtilenum = tilenum; // if the game wants voxels, it gets voxels
5271     else if ((cstat & 48) != 32 && usevoxels && tiletovox[tilenum] != -1 && spritenum != -1 && !(spriteext[spritenum].flags&SPREXT_NOTMD))
5272     {
5273         vtilenum = tiletovox[tilenum];
5274         cstat |= 48;
5275     }
5276 
5277     if ((cstat&48) != 48)
5278     {
5279         if (spritenum < 0 || tilesiz[tilenum].x <= 0 || tilesiz[tilenum].y <= 0)
5280             return;
5281     }
5282 
5283     if (!tspr->xrepeat || !tspr->yrepeat)
5284         return;
5285 
5286     globalpal = tspr->pal;
5287     if (palookup[globalpal] == NULL) globalpal = 0;    // JBF: fixes null-pointer crash
5288     globalshade = tspr->shade;
5289 
5290     if (cstat&2)
5291         setup_blend(blendidx, cstat&512);
5292 
5293     vec2_t off = { picanm[tilenum].xofs, picanm[tilenum].yofs };
5294     int32_t const slope = tspriteGetSlope(tspr);
5295     if (slope == 0)
5296     {
5297         off.x += tspr->xoffset;
5298         off.y += tspr->yoffset;
5299     }
5300 
5301     if ((cstat&48) == 0)
5302     {
5303         int32_t startum, startdm;
5304         int32_t linum, linuminc;
5305 
5306 draw_as_face_sprite:
5307         if (yp <= (4<<8)) return;
5308 
5309         int const isiz = divscale19(xdimenscale,yp);
5310         int const xv = mulscale16(((int32_t)tspr->xrepeat)<<16,xyaspect);
5311         vec2_16_t const span = tilesiz[tilenum];
5312         vec2_t const siz = { mulscale30(isiz, xv * span.x), mulscale14(isiz, tspr->yrepeat * span.y) };
5313 
5314         if (EDUKE32_PREDICT_FALSE((span.x>>11) >= siz.x || span.y >= (siz.y>>1)))
5315             return;  //Watch out for divscale overflow
5316 
5317         x1 = xb-(siz.x>>1);
5318         if (span.x&1) x1 += mulscale31(isiz,xv);  //Odd xspans
5319         i = mulscale30(isiz,xv*off.x);
5320         if ((cstat&4) == 0) x1 -= i; else x1 += i;
5321 
5322         y1 = mulscale16(tspr->z-globalposz,isiz);
5323         y1 -= mulscale14(isiz,tspr->yrepeat*off.y);
5324         y1 += (globalhoriz<<8)-siz.y;
5325         if (cstat&128)
5326         {
5327             y1 += (siz.y>>1);
5328             if (span.y&1) y1 += mulscale15(isiz,tspr->yrepeat);  //Odd yspans
5329         }
5330 
5331         x2 = x1+siz.x-1;
5332         y2 = y1+siz.y-1;
5333         if ((y1|255) >= (y2|255)) return;
5334 
5335         int32_t lx = (x1>>8)+1; if (lx < 0) lx = 0;
5336         int32_t rx = (x2>>8); if (rx >= xdimen) rx = xdimen-1;
5337         if (lx > rx) return;
5338 
5339         startum = ((sec->ceilingstat&3) == 0) ? globalhoriz+mulscale24(isiz,sec->ceilingz-globalposz)-1 : 0;
5340         startdm = ((sec->floorstat&3) == 0) ? globalhoriz+mulscale24(isiz,sec->floorz-globalposz)+1 : INT32_MAX;
5341 
5342         if ((y1>>8) > startum) startum = (y1>>8);
5343         if ((y2>>8) < startdm) startdm = (y2>>8);
5344 
5345         if (startum < -32768) startum = -32768;
5346         if (startdm > 32767) startdm = 32767;
5347         if (startum >= startdm) return;
5348 
5349         if ((cstat&4) == 0)
5350         {
5351             linuminc = divscale24(span.x,siz.x);
5352             linum = mulscale8((lx<<8)-x1,linuminc);
5353         }
5354         else
5355         {
5356             linuminc = -divscale24(span.x,siz.x);
5357             linum = mulscale8((lx<<8)-x2,linuminc);
5358         }
5359 
5360         if ((cstat&8) > 0)
5361             swaplong(&y1, &y2);
5362 
5363         x = lx;
5364 #ifdef CLASSIC_SLICE_BY_4
5365         for (; x<=rx-4; x+=4)
5366         {
5367             uwall[x] =   max<int>(startumost[windowxy1.x+x]-windowxy1.y,   startum);
5368             uwall[x+1] = max<int>(startumost[windowxy1.x+x+1]-windowxy1.y, startum);
5369             uwall[x+2] = max<int>(startumost[windowxy1.x+x+2]-windowxy1.y, startum);
5370             uwall[x+3] = max<int>(startumost[windowxy1.x+x+3]-windowxy1.y, startum);
5371 
5372             dwall[x] =   min<int>(startdmost[windowxy1.x+x]-windowxy1.y,   startdm);
5373             dwall[x+1] = min<int>(startdmost[windowxy1.x+x+1]-windowxy1.y, startdm);
5374             dwall[x+2] = min<int>(startdmost[windowxy1.x+x+2]-windowxy1.y, startdm);
5375             dwall[x+3] = min<int>(startdmost[windowxy1.x+x+3]-windowxy1.y, startdm);
5376         }
5377 #endif
5378         for (; x<=rx; x++)
5379         {
5380             uwall[x] = max<int>(startumost[windowxy1.x+x]-windowxy1.y,startum);
5381             dwall[x] = min<int>(startdmost[windowxy1.x+x]-windowxy1.y,startdm);
5382         }
5383 
5384         int32_t daclip = 0;
5385         for (i=smostwallcnt-1; i>=0; i--)
5386         {
5387             if (smostwalltype[i]&daclip) continue;
5388             j = smostwall[i];
5389             if ((xb1[j] > rx) || (xb2[j] < lx)) continue;
5390             if ((yp <= yb1[j]) && (yp <= yb2[j])) continue;
5391             if (spritewallfront(tspr,(int32_t)thewall[j]) && ((yp <= yb1[j]) || (yp <= yb2[j]))) continue;
5392 
5393             const int32_t dalx2 = max(xb1[j],lx);
5394             const int32_t darx2 = min(xb2[j],rx);
5395 
5396             switch (smostwalltype[i])
5397             {
5398             case 0:
5399                 if (dalx2 <= darx2)
5400                 {
5401                     if ((dalx2 == lx) && (darx2 == rx)) return;
5402                     //clearbufbyte(&dwall[dalx2],(darx2-dalx2+1)*sizeof(dwall[0]),0L);
5403                     for (k=dalx2; k<=darx2; k++) dwall[k] = 0;
5404                 }
5405                 break;
5406             case 1:
5407                 k = smoststart[i] - xb1[j];
5408                 x = dalx2;
5409 #ifdef CLASSIC_SLICE_BY_4 // ok, this one is really by 2 ;)
5410                 for (; x<=darx2-2; x+=2)
5411                 {
5412                     if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
5413                     if (smost[k+x+1] > uwall[x+1]) uwall[x+1] = smost[k+x+1];
5414                 }
5415 #endif
5416                 for (; x<=darx2; x++)
5417                     if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
5418                 if ((dalx2 == lx) && (darx2 == rx)) daclip |= 1;
5419                 break;
5420             case 2:
5421                 k = smoststart[i] - xb1[j];
5422                 x = dalx2;
5423 #ifdef CLASSIC_SLICE_BY_4
5424                 for (; x<=darx2-4; x+=4)
5425                 {
5426                     if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
5427                     if (smost[k+x+1] < dwall[x+1]) dwall[x+1] = smost[k+x+1];
5428                     if (smost[k+x+2] < dwall[x+2]) dwall[x+2] = smost[k+x+2];
5429                     if (smost[k+x+3] < dwall[x+3]) dwall[x+3] = smost[k+x+3];
5430                 }
5431 #endif
5432                 for (; x<=darx2; x++)
5433                     if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
5434                 if ((dalx2 == lx) && (darx2 == rx)) daclip |= 2;
5435                 break;
5436             }
5437         }
5438 
5439         if (uwall[rx] >= dwall[rx])
5440         {
5441             for (x=lx; x<rx; x++)
5442                 if (uwall[x] < dwall[x]) break;
5443             if (x == rx) return;
5444         }
5445 
5446         //sprite
5447 #ifdef YAX_ENABLE
5448         if (yax_globallev==YAX_MAXDRAWS || searchit==2)
5449 #endif
5450         if (searchit >= 1 && (searchx >= lx && searchx <= rx))
5451             if (searchy >= uwall[searchx] && searchy < dwall[searchx])
5452             {
5453                 searchsector = sectnum; searchwall = spritenum;
5454                 searchstat = 3; searchit = 1;
5455             }
5456 
5457         setup_globals_sprite1(tspr, sec, span.y, off.y, tilenum, cstat, &z1, &z2);
5458 
5459         qinterpolatedown16((intptr_t)&lwall[lx],rx-lx+1,linum,linuminc);
5460         clearbuf(&swall[lx],rx-lx+1,mulscale19(yp,xdimscale));
5461 
5462         {
5463 #ifdef HIGH_PRECISION_SPRITE
5464             union { float f; int32_t i; } sw = {
5465                 // initialize the float of the union
5466                 ((cstat&8) ? -1 : 1)
5467                 * (float)yp * xdimscale
5468                 * (1<<(22-19)) / (span.y*tspr->yrepeat)
5469             };
5470 
5471             clearbuf(&swallf[lx], rx-lx+1, sw.i);
5472 #endif
5473         }
5474 
5475         drawing_sprite = 1;
5476 
5477         if ((cstat&2) == 0)
5478             maskwallscan(lx,rx, (cstat&8)==0);
5479         else
5480             transmaskwallscan(lx,rx, (cstat&8)==0);
5481 
5482         drawing_sprite = 0;
5483     }
5484     else if ((cstat&48) == 16)
5485     {
5486         const int32_t xspan = tilesiz[tilenum].x;
5487         const int32_t yspan = tilesiz[tilenum].y;
5488         const int32_t xv = tspr->xrepeat*sintable[(tspr->ang+2560+1536)&2047];
5489         const int32_t yv = tspr->xrepeat*sintable[(tspr->ang+2048+1536)&2047];
5490 
5491         if ((cstat&4) > 0) off.x = -off.x;
5492         if ((cstat&8) > 0) off.y = -off.y;
5493 
5494         i = (xspan>>1) + off.x;
5495         x1 = tspr->x-globalposx-mulscale16(xv,i); x2 = x1+mulscale16(xv,xspan);
5496         y1 = tspr->y-globalposy-mulscale16(yv,i); y2 = y1+mulscale16(yv,xspan);
5497 
5498         vec2_t p1 = get_rel_coords(x1, y1);
5499         vec2_t p2 = get_rel_coords(x2, y2);
5500 
5501         if (p1.y <= 0 && p2.y <= 0)
5502             return;
5503 
5504         x1 += globalposx; y1 += globalposy;
5505         x2 += globalposx; y2 += globalposy;
5506 
5507         int32_t swapped = 0;
5508         if (dmulscale32(p1.x, p2.y, -p2.x, p1.y) >= 0)  // If wall's NOT facing you
5509         {
5510             if ((cstat&64) != 0)
5511                 return;
5512 
5513             const vec2_t pt = p2;
5514             p2 = p1;
5515             p1 = pt;
5516             i = x1, x1 = x2, x2 = i;
5517             i = y1, y1 = y2, y2 = i;
5518             swapped = 1;
5519         }
5520 
5521         int32_t sx1, sx2, sy1, sy2;
5522         if (!get_screen_coords(p1, p2, &sx1, &sy1, &sx2, &sy2))
5523             return;
5524 
5525         const int32_t topinc = -mulscale10(p1.y,xspan);
5526         int32_t top = ((mulscale10(p1.x,xdimen) - mulscale9(sx1-halfxdimen,p1.y))*xspan)>>3;
5527         const int32_t botinc = (p2.y-p1.y)>>8;
5528         int32_t bot = mulscale11(p1.x-p2.x,xdimen) + mulscale2(sx1-halfxdimen,botinc);
5529 
5530         j = sx2+3;
5531         z = mulscale20(top,krecipasm(bot));
5532         lwall[sx1] = (z>>8);
5533         for (x=sx1+4; x<=j; x+=4)
5534         {
5535             top += topinc; bot += botinc;
5536             zz = z; z = mulscale20(top,krecipasm(bot));
5537             i = ((z+zz)>>1);
5538             lwall[x-3] = ((i+zz)>>9);
5539             lwall[x-2] = (i>>8);
5540             lwall[x-1] = ((i+z)>>9);
5541             lwall[x] = (z>>8);
5542         }
5543 
5544         if (lwall[sx1] < 0) lwall[sx1] = 0;
5545         if (lwall[sx2] >= xspan) lwall[sx2] = xspan-1;
5546 
5547         if ((swapped^((cstat&4)>0)) > 0)
5548         {
5549             j = xspan-1;
5550             for (x=sx1; x<=sx2; x++)
5551                 lwall[x] = j-lwall[x];
5552         }
5553 
5554         // XXX: UNUSED?
5555         rx1[MAXWALLSB-1] = p1.x; ry1[MAXWALLSB-1] = p1.y;
5556         rx2[MAXWALLSB-1] = p2.x; ry2[MAXWALLSB-1] = p2.y;
5557 
5558         setup_globals_sprite1(tspr, sec, yspan, off.y, tilenum, cstat, &z1, &z2);
5559 
5560         if ((sec->ceilingstat&1) == 0 && z1 < sec->ceilingz)
5561             z1 = sec->ceilingz;
5562         if ((sec->floorstat&1) == 0 && z2 > sec->floorz)
5563             z2 = sec->floorz;
5564 
5565         xb1[MAXWALLSB-1] = sx1;
5566         xb2[MAXWALLSB-1] = sx2;
5567         yb1[MAXWALLSB-1] = sy1;
5568         yb2[MAXWALLSB-1] = sy2;
5569         owallmost(uwall, MAXWALLSB-1, z1-globalposz);
5570         owallmost(dwall, MAXWALLSB-1, z2-globalposz);
5571 
5572         int32_t hplc = divscale19(xdimenscale,sy1);
5573         const int32_t hplc2 = divscale19(xdimenscale,sy2);
5574         const int32_t idiv = sx2-sx1;
5575         int32_t hinc[4] = { idiv ? tabledivide32(hplc2-hplc, idiv) : 0 };
5576 
5577 #ifdef HIGH_PRECISION_SPRITE
5578         float const cc     = ((1<<19) * fxdimen * (float)yxaspect) * (1.f/320.f);
5579         float const loopcc = ((cstat&8) ? -1.f : 1.f) * (float(1<<30) * float(1<<24)) / float(yspan * tspr->yrepeat);
5580 
5581         float hplcf    = cc / sy1;
5582         float hincf[4] = { idiv ? (cc / sy2 - hplcf) / idiv : 0 };
5583 
5584 #ifdef CLASSIC_SLICE_BY_4
5585         hincf[1] = hincf[0] * 2.f;
5586         hincf[2] = hincf[0] * 3.f;
5587         hincf[3] = hincf[0] * 4.f;
5588 #endif // CLASSIC_SLICE_BY_4
5589 #endif // HIGH_PRECISION_SPRITE
5590 #ifdef CLASSIC_SLICE_BY_4
5591         hinc[1] = hinc[0]<<1;
5592         hinc[2] = hinc[0]*3;
5593         hinc[3] = hinc[0]<<2;
5594 #endif
5595         i = sx1;
5596 
5597 #ifdef CLASSIC_SLICE_BY_4
5598         for (; i<=sx2-4; i+=4)
5599         {
5600             swall[i] = (krecipasm(hplc)<<2);
5601             swall[i+1] = (krecipasm(hplc+hinc[0])<<2);
5602             swall[i+2] = (krecipasm(hplc+hinc[1])<<2);
5603             swall[i+3] = (krecipasm(hplc+hinc[2])<<2);
5604             hplc += hinc[3];
5605 #ifdef HIGH_PRECISION_SPRITE
5606             swallf[i] =   loopcc/hplcf;
5607             swallf[i+1] = loopcc/(hplcf+hincf[0]);
5608             swallf[i+2] = loopcc/(hplcf+hincf[1]);
5609             swallf[i+3] = loopcc/(hplcf+hincf[2]);
5610             hplcf += hincf[3];
5611 #endif // HIGH_PRECISION_SPRITE
5612         }
5613 #endif // CLASSIC_SLICE_BY_4
5614 
5615         for (; i<=sx2; i++)
5616         {
5617             swall[i] = (krecipasm(hplc)<<2);
5618             hplc += hinc[0];
5619 #ifdef HIGH_PRECISION_SPRITE
5620             swallf[i] = loopcc/hplcf;
5621             hplcf += hincf[0];
5622 #endif
5623         }
5624 
5625         for (i=smostwallcnt-1; i>=0; i--)
5626         {
5627             j = smostwall[i];
5628 
5629             if (xb1[j] > sx2 || xb2[j] < sx1)
5630                 continue;
5631 
5632             int32_t dalx2 = xb1[j];
5633             int32_t darx2 = xb2[j];
5634 
5635             if (max(sy1,sy2) > min(yb1[j],yb2[j]))
5636             {
5637                 if (min(sy1,sy2) > max(yb1[j],yb2[j]))
5638                 {
5639                     x = INT32_MIN;
5640                 }
5641                 else
5642                 {
5643                     x = thewall[j]; xp1 = wall[x].x; yp1 = wall[x].y;
5644                     x = wall[x].point2; xp2 = wall[x].x; yp2 = wall[x].y;
5645 
5646                     z1 = (xp2-xp1)*(y1-yp1) - (yp2-yp1)*(x1-xp1);
5647                     z2 = (xp2-xp1)*(y2-yp1) - (yp2-yp1)*(x2-xp1);
5648                     if ((z1^z2) >= 0)
5649                         x = (z1+z2);
5650                     else
5651                     {
5652                         z1 = (x2-x1)*(yp1-y1) - (y2-y1)*(xp1-x1);
5653                         z2 = (x2-x1)*(yp2-y1) - (y2-y1)*(xp2-x1);
5654 
5655                         if ((z1^z2) >= 0)
5656                             x = -(z1+z2);
5657                         else
5658                         {
5659                             if ((xp2-xp1)*(tspr->y-yp1) == (tspr->x-xp1)*(yp2-yp1))
5660                             {
5661                                 if (wall[thewall[j]].nextsector == tspr->sectnum)
5662                                     x = INT32_MIN;
5663                                 else
5664                                     x = INT32_MAX;
5665                             }
5666                             else
5667                             {
5668                                 //INTERSECTION!
5669                                 x = (xp1-globalposx) + scale(xp2-xp1,z1,z1-z2);
5670                                 y = (yp1-globalposy) + scale(yp2-yp1,z1,z1-z2);
5671 
5672                                 yp1 = dmulscale14(x,cosviewingrangeglobalang,y,sinviewingrangeglobalang);
5673 
5674                                 if (yp1 > 0)
5675                                 {
5676                                     xp1 = dmulscale14(y,cosglobalang,-x,singlobalang);
5677 
5678                                     x = halfxdimen + scale(xp1,halfxdimen,yp1);
5679                                     if (xp1 >= 0) x++;   //Fix for SIGNED divide
5680 
5681                                     if (z1 < 0)
5682                                         { if (dalx2 < x) dalx2 = x; }
5683                                     else
5684                                         { if (darx2 > x) darx2 = x; }
5685                                     x = INT32_MIN+1;
5686                                 }
5687                                 else
5688                                     x = INT32_MAX;
5689                             }
5690                         }
5691                     }
5692                 }
5693 
5694                 if (x < 0)
5695                 {
5696                     if (dalx2 < sx1) dalx2 = sx1;
5697                     if (darx2 > sx2) darx2 = sx2;
5698 
5699                     switch (smostwalltype[i])
5700                     {
5701                     case 0:
5702                         if (dalx2 <= darx2)
5703                         {
5704                             if ((dalx2 == sx1) && (darx2 == sx2)) return;
5705                             //clearbufbyte(&dwall[dalx2],(darx2-dalx2+1)*sizeof(dwall[0]),0L);
5706                             for (k=dalx2; k<=darx2; k++) dwall[k] = 0;
5707                         }
5708                         break;
5709                     case 1:
5710                         k = smoststart[i] - xb1[j];
5711                         x = dalx2;
5712 #ifdef CLASSIC_SLICE_BY_4
5713                         for (; x<=darx2-2; x+=2)
5714                         {
5715                             if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
5716                             if (smost[k+x+1] > uwall[x+1]) uwall[x+1] = smost[k+x+1];
5717                         }
5718 #endif
5719                         for (; x<=darx2; x++)
5720                             if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
5721                         break;
5722                     case 2:
5723                         k = smoststart[i] - xb1[j];
5724                         x = dalx2;
5725 #ifdef CLASSIC_SLICE_BY_4
5726                         for (; x<=darx2-4; x+=4)
5727                         {
5728                             if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
5729                             if (smost[k+x+1] < dwall[x+1]) dwall[x+1] = smost[k+x+1];
5730                             if (smost[k+x+2] < dwall[x+2]) dwall[x+2] = smost[k+x+2];
5731                             if (smost[k+x+3] < dwall[x+3]) dwall[x+3] = smost[k+x+3];
5732                         }
5733 #endif
5734                         for (; x<=darx2; x++)
5735                             if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
5736                         break;
5737                     }
5738                 }
5739             }
5740         }
5741 
5742         //sprite
5743 #ifdef YAX_ENABLE
5744         if (yax_globallev==YAX_MAXDRAWS || searchit==2)
5745 #endif
5746         if (searchit >= 1 && (searchx >= sx1 && searchx <= sx2))
5747             if (searchy >= uwall[searchx] && searchy <= dwall[searchx])
5748             {
5749                 searchsector = sectnum; searchwall = spritenum;
5750                 searchstat = 3; searchit = 1;
5751             }
5752 
5753         drawing_sprite = 1;
5754 
5755         if ((cstat&2) == 0)
5756             maskwallscan(sx1,sx2, (cstat&8)==0);
5757         else
5758             transmaskwallscan(sx1,sx2, (cstat&8)==0);
5759 
5760         drawing_sprite = 0;
5761     }
5762     else if ((cstat&48) == 32)
5763     {
5764         if ((cstat&64) != 0)
5765             if ((globalposz > tspriteGetZOfSlope(tspr, globalposx, globalposy)) == ((cstat&8)==0))
5766                 return;
5767 
5768         if ((cstat&4) > 0) off.x = -off.x;
5769         if ((cstat&8) > 0) off.y = -off.y;
5770         vec2_16_t const span = tilesiz[tilenum];
5771         const int32_t ratio = nsqrtasm(slope * slope + 16777216);
5772 
5773         //Rotate center point
5774         dax = tspr->x-globalposx;
5775         day = tspr->y-globalposy;
5776         const int32_t cz = dmulscale10(cosglobalang,dax,singlobalang,day);
5777         const int32_t cx = dmulscale10(cosglobalang,day,-singlobalang,dax);
5778 
5779         //Get top-left corner
5780         int32_t const cosang = dmulscale14(sintable[(tspr->ang+512)&2047], cosglobalang, sintable[tspr->ang&2047], singlobalang);
5781         int32_t const sinang = dmulscale14(sintable[(tspr->ang+512)&2047], -singlobalang, sintable[tspr->ang&2047], cosglobalang);
5782         dax = (((span.x>>1)+off.x)*tspr->xrepeat)<<8;
5783         day = divscale20(((span.y>>1)+off.y)*tspr->yrepeat,ratio);
5784         rzi[0] = cz+dmulscale20(sinang,dax,cosang,day);
5785         rxi[0] = cx+dmulscale20(sinang,day,-cosang,dax);
5786 
5787         //Get other 3 corners
5788         dax = (span.x*tspr->xrepeat)<<8;
5789         day = divscale20(span.y*tspr->yrepeat,ratio);
5790         rzi[1] = rzi[0]-mulscale20(sinang,dax);
5791         rxi[1] = rxi[0]+mulscale20(cosang,dax);
5792         dax = -mulscale20(cosang,day);
5793         day = -mulscale20(sinang,day);
5794         rzi[2] = rzi[1]+dax; rxi[2] = rxi[1]+day;
5795         rzi[3] = rzi[0]+dax; rxi[3] = rxi[0]+day;
5796 
5797         float sgzd = 0, sgzx = 0, sgz = 0, sdaz = 0;
5798         vec2f_t sg_f = {}, sg_f2 = {};
5799         vec2_t sg1 = {};
5800 
5801         if (slope != 0)
5802         {
5803             int daz = 0;
5804             float const fslope  = (float)slope;
5805             float const fsinang = (float)sinang;
5806             float const fcosang = (float)cosang;
5807 
5808             for (i = 0; i < 4; i++)
5809             {
5810                 int const j = dmulscale8(-sinang, rxi[i]-cx,
5811                                         -cosang, rzi[i]-cz);
5812                 int const z = (tspr->z + mulscale18(slope, j) - globalposz);
5813 
5814                 if (i == 0)
5815                     daz = z;
5816 
5817                 ryi[i] = scale(z,yxaspect,320<<8);
5818             }
5819 
5820             float const fi = (0-halfxdimen)*xdimenrecip;
5821             sg_f = { fcosang*float(xdimenrecip)*(1.f/524288.f), fsinang*float(xdimenrecip)*(1.f/524288.f) };
5822             sg_f2 = { -fsinang*float(viewingrangerecip)*(1.f/4096.f) + fcosang*fi*(1.f/134217728.f),
5823                        fcosang*float(viewingrangerecip)*(1.f/4096.f) + fsinang*fi*(1.f/134217728.f) };
5824 
5825             sgzd = xdimscale*512.f;
5826             sgzx = sg_f2.y*fslope*(1.f/256.f) + (1-globalhoriz)*sgzd*(1.f/1024.f);
5827             sgz = sg_f.y*fslope*(1.f/65536.f);
5828 
5829             float const fx = 64.f/float(tspr->xrepeat);
5830             float const fy = 64.f/float(tspr->yrepeat) * float(ratio) * (1.f / 4096.f);
5831 
5832             sg_f.x *= fx;
5833             sg_f.y *= fy;
5834             sg_f2.x *= fx;
5835             sg_f2.y *= fy;
5836 
5837             sg1 = { divscale6(dmulscale10(rxi[0], -cosang, rzi[0], sinang), tspr->xrepeat),
5838                     mulscale12(divscale6(dmulscale10(rxi[0], sinang, rzi[0], cosang), tspr->yrepeat), ratio) };
5839 
5840             if (cstat & 4)
5841             {
5842                 sg1.x = -sg1.x;
5843                 sg_f2.x = -sg_f2.x;
5844                 sg_f.x = -sg_f.x;
5845             }
5846 
5847             sdaz = fslope*(fsinang*float(rxi[0])+fcosang*float(rzi[0]))*(1.f/262144.f) + float(daz)*256.f;
5848             sg_f = { (sg_f.x*sdaz)*(1.f/268435456.f), (sg_f.y*-sdaz)*(1.f/268435456.f) };
5849             sg_f2 = { (sg_f2.x*sdaz)*(1.f/1048576.f), (sg_f2.y*-sdaz)*(1.f/1048576.f) };
5850 
5851             rzi[0] = mulscale16(rzi[0], viewingrange);
5852             rzi[1] = mulscale16(rzi[1], viewingrange);
5853             rzi[2] = mulscale16(rzi[2], viewingrange);
5854             rzi[3] = mulscale16(rzi[3], viewingrange);
5855 
5856             //If ceilsprite is above you, reverse order of points
5857             if (globalposz > tspriteGetZOfSlope(tspr, globalposx, globalposy))
5858             {
5859                 i = rxi[1]; rxi[1] = rxi[3]; rxi[3] = i;
5860                 i = rzi[1]; rzi[1] = rzi[3]; rzi[3] = i;
5861                 i = ryi[1]; ryi[1] = ryi[3]; ryi[3] = i;
5862             }
5863         }
5864         else
5865         {
5866             //Put all points on same z
5867             ryi[0] = scale((tspr->z-globalposz),yxaspect,320<<8);
5868             if (ryi[0] == 0) return;
5869             ryi[1] = ryi[2] = ryi[3] = ryi[0];
5870 
5871             if ((cstat&4) == 0)
5872                 { z = 0; z1 = 1; z2 = 3; }
5873             else
5874                 { z = 1; z1 = 0; z2 = 2; }
5875 
5876             dax = rzi[z1]-rzi[z]; day = rxi[z1]-rxi[z];
5877             int32_t bot = dmulscale8(dax,dax,day,day);
5878             if ((klabs(dax)>>13) >= bot || (klabs(day)>>13) >= bot)
5879                 return;
5880             globalx1 = divscale18(dax,bot);
5881             globalx2 = divscale18(day,bot);
5882 
5883             dax = rzi[z2]-rzi[z]; day = rxi[z2]-rxi[z];
5884             bot = dmulscale8(dax,dax,day,day);
5885             if ((klabs(dax)>>13) >= bot || (klabs(day)>>13) >= bot)
5886                 return;
5887             globaly1 = divscale18(dax,bot);
5888             globaly2 = divscale18(day,bot);
5889 
5890             //Calculate globals for hline texture mapping function
5891             globalxpanning = (rxi[z]<<12);
5892             globalypanning = (rzi[z]<<12);
5893             globalzd = decltype(globalzd)(ryi[z])<<12;
5894 
5895             rzi[0] = mulscale16(rzi[0],viewingrange);
5896             rzi[1] = mulscale16(rzi[1],viewingrange);
5897             rzi[2] = mulscale16(rzi[2],viewingrange);
5898             rzi[3] = mulscale16(rzi[3],viewingrange);
5899 
5900             if (ryi[0] < 0)   //If ceilsprite is above you, reverse order of points
5901             {
5902                 i = rxi[1]; rxi[1] = rxi[3]; rxi[3] = i;
5903                 i = rzi[1]; rzi[1] = rzi[3]; rzi[3] = i;
5904             }
5905         }
5906 
5907         //Clip polygon in 3-space
5908         int32_t npoints = 4;
5909 
5910         //Clip edge 1
5911         int32_t npoints2 = 0;
5912         int32_t zzsgn = rxi[0]+rzi[0], zsgn;
5913         for (z=0; z<npoints; z++)
5914         {
5915             zz = z+1; if (zz == npoints) zz = 0;
5916             zsgn = zzsgn; zzsgn = rxi[zz]+rzi[zz];
5917             if (zsgn >= 0)
5918             {
5919                 rxi2[npoints2] = rxi[z]; ryi2[npoints2] = ryi[z]; rzi2[npoints2] = rzi[z];
5920                 npoints2++;
5921             }
5922             if ((zsgn^zzsgn) < 0)
5923             {
5924                 int32_t t = divscale30(zsgn,zsgn-zzsgn);
5925                 rxi2[npoints2] = rxi[z] + mulscale30(t,rxi[zz]-rxi[z]);
5926                 ryi2[npoints2] = ryi[z] + mulscale30(t,ryi[zz]-ryi[z]);
5927                 rzi2[npoints2] = rzi[z] + mulscale30(t,rzi[zz]-rzi[z]);
5928                 npoints2++;
5929             }
5930         }
5931         if (npoints2 <= 2) return;
5932 
5933         //Clip edge 2
5934         npoints = 0;
5935         zzsgn = rxi2[0]-rzi2[0];
5936         for (z=0; z<npoints2; z++)
5937         {
5938             zz = z+1; if (zz == npoints2) zz = 0;
5939             zsgn = zzsgn; zzsgn = rxi2[zz]-rzi2[zz];
5940             if (zsgn <= 0)
5941             {
5942                 rxi[npoints] = rxi2[z]; ryi[npoints] = ryi2[z]; rzi[npoints] = rzi2[z];
5943                 npoints++;
5944             }
5945             if ((zsgn^zzsgn) < 0)
5946             {
5947                 int32_t t = divscale30(zsgn,zsgn-zzsgn);
5948                 rxi[npoints] = rxi2[z] + mulscale30(t,rxi2[zz]-rxi2[z]);
5949                 ryi[npoints] = ryi2[z] + mulscale30(t,ryi2[zz]-ryi2[z]);
5950                 rzi[npoints] = rzi2[z] + mulscale30(t,rzi2[zz]-rzi2[z]);
5951                 npoints++;
5952             }
5953         }
5954         if (npoints <= 2) return;
5955 
5956         //Clip edge 3
5957         npoints2 = 0;
5958         zzsgn = ryi[0]*halfxdimen + (rzi[0]*(globalhoriz-0));
5959         for (z=0; z<npoints; z++)
5960         {
5961             zz = z+1; if (zz == npoints) zz = 0;
5962             zsgn = zzsgn; zzsgn = ryi[zz]*halfxdimen + (rzi[zz]*(globalhoriz-0));
5963             if (zsgn >= 0)
5964             {
5965                 rxi2[npoints2] = rxi[z];
5966                 ryi2[npoints2] = ryi[z];
5967                 rzi2[npoints2] = rzi[z];
5968                 npoints2++;
5969             }
5970             if ((zsgn^zzsgn) < 0)
5971             {
5972                 int32_t t = divscale30(zsgn,zsgn-zzsgn);
5973                 rxi2[npoints2] = rxi[z] + mulscale30(t,rxi[zz]-rxi[z]);
5974                 ryi2[npoints2] = ryi[z] + mulscale30(t,ryi[zz]-ryi[z]);
5975                 rzi2[npoints2] = rzi[z] + mulscale30(t,rzi[zz]-rzi[z]);
5976                 npoints2++;
5977             }
5978         }
5979         if (npoints2 <= 2) return;
5980 
5981         //Clip edge 4
5982         npoints = 0;
5983         zzsgn = ryi2[0]*halfxdimen + (rzi2[0]*(globalhoriz-ydimen));
5984         for (z=0; z<npoints2; z++)
5985         {
5986             zz = z+1; if (zz == npoints2) zz = 0;
5987             zsgn = zzsgn; zzsgn = ryi2[zz]*halfxdimen + (rzi2[zz]*(globalhoriz-ydimen));
5988             if (zsgn <= 0)
5989             {
5990                 rxi[npoints] = rxi2[z];
5991                 ryi[npoints] = ryi2[z];
5992                 rzi[npoints] = rzi2[z];
5993                 npoints++;
5994             }
5995             if ((zsgn^zzsgn) < 0)
5996             {
5997                 int32_t t = divscale30(zsgn,zsgn-zzsgn);
5998                 rxi[npoints] = rxi2[z] + mulscale30(t,rxi2[zz]-rxi2[z]);
5999                 ryi[npoints] = ryi2[z] + mulscale30(t,ryi2[zz]-ryi2[z]);
6000                 rzi[npoints] = rzi2[z] + mulscale30(t,rzi2[zz]-rzi2[z]);
6001                 npoints++;
6002             }
6003         }
6004         if (npoints <= 2) return;
6005 
6006         //Project onto screen
6007         int32_t lpoint = -1, lmax = INT32_MAX;
6008         int32_t rpoint = -1, rmax = INT32_MIN;
6009         for (z=0; z<npoints; z++)
6010         {
6011             xsi[z] = scale(rxi[z],xdimen<<15,rzi[z]) + (xdimen<<15);
6012             ysi[z] = scale(ryi[z],xdimen<<15,rzi[z]) + (globalhoriz<<16);
6013             if (xsi[z] < 0) xsi[z] = 0;
6014             if (xsi[z] > (xdimen<<16)) xsi[z] = (xdimen<<16);
6015             if (ysi[z] < ((int32_t)0<<16)) ysi[z] = ((int32_t)0<<16);
6016             if (ysi[z] > ((int32_t)ydimen<<16)) ysi[z] = ((int32_t)ydimen<<16);
6017             if (xsi[z] < lmax) lmax = xsi[z], lpoint = z;
6018             if (xsi[z] > rmax) rmax = xsi[z], rpoint = z;
6019         }
6020 
6021         //Get uwall arrays
6022         for (z=lpoint; z!=rpoint; z=zz)
6023         {
6024             zz = z+1; if (zz == npoints) zz = 0;
6025 
6026             dax1 = ((xsi[z]+65535)>>16);
6027             dax2 = ((xsi[zz]+65535)>>16);
6028             if (dax2 > dax1)
6029             {
6030                 int32_t yinc = divscale16(ysi[zz]-ysi[z],xsi[zz]-xsi[z]);
6031                 y = ysi[z] + mulscale16((dax1<<16)-xsi[z],yinc);
6032                 qinterpolatedown16short((intptr_t)(&uwall[dax1]),dax2-dax1,y,yinc);
6033             }
6034         }
6035 
6036         //Get dwall arrays
6037         for (; z!=lpoint; z=zz)
6038         {
6039             zz = z+1; if (zz == npoints) zz = 0;
6040 
6041             dax1 = ((xsi[zz]+65535)>>16);
6042             dax2 = ((xsi[z]+65535)>>16);
6043             if (dax2 > dax1)
6044             {
6045                 int32_t yinc = divscale16(ysi[zz]-ysi[z],xsi[zz]-xsi[z]);
6046                 y = ysi[zz] + mulscale16((dax1<<16)-xsi[zz],yinc);
6047                 qinterpolatedown16short((intptr_t)(&dwall[dax1]),dax2-dax1,y,yinc);
6048             }
6049         }
6050 
6051 
6052         const int32_t lx = ((lmax+65535)>>16);
6053         const int32_t rx = min(((rmax+65535)>>16), xdim-1);
6054         // min(): OOB prevention. Simple test case: have a floor-aligned sprite
6055         // to the right of the player. Slowly rotate right toward it. When it
6056         // just becomes visible, the condition rx == xdim can occur.
6057 
6058         // Don't pointlessly keep going. If the following condition holds, the
6059         // ceilspritescan() at the end of our block would not draw any lines,
6060         // and moreover may access uwall[] OOB (with x1==xdim).
6061         if (rx-1 < lx)
6062             return;
6063 
6064         for (x=lx; x<=rx; x++)
6065         {
6066             uwall[x] = max<int>(uwall[x],startumost[x+windowxy1.x]-windowxy1.y);
6067             dwall[x] = min<int>(dwall[x],startdmost[x+windowxy1.x]-windowxy1.y);
6068         }
6069 
6070         //Additional uwall/dwall clipping goes here
6071         for (i=smostwallcnt-1; i>=0; i--)
6072         {
6073             j = smostwall[i];
6074             if ((xb1[j] > rx) || (xb2[j] < lx)) continue;
6075             if ((yp <= yb1[j]) && (yp <= yb2[j])) continue;
6076 
6077             //if (spritewallfront(tspr,thewall[j]) == 0)
6078             x = thewall[j]; xp1 = wall[x].x; yp1 = wall[x].y;
6079             x = wall[x].point2; xp2 = wall[x].x; yp2 = wall[x].y;
6080             x = (xp2-xp1)*(tspr->y-yp1)-(tspr->x-xp1)*(yp2-yp1);
6081             if ((yp > yb1[j]) && (yp > yb2[j])) x = -1;
6082             if ((x >= 0) && ((x != 0) || (wall[thewall[j]].nextsector != tspr->sectnum))) continue;
6083 
6084             const int32_t dalx2 = max(xb1[j],lx);
6085             const int32_t darx2 = min(xb2[j],rx);
6086 
6087             switch (smostwalltype[i])
6088             {
6089             case 0:
6090                 if (dalx2 <= darx2)
6091                 {
6092                     if ((dalx2 == lx) && (darx2 == rx)) return;
6093                     //clearbufbyte(&dwall[dalx2],(darx2-dalx2+1)*sizeof(dwall[0]),0L);
6094                     for (x=dalx2; x<=darx2; x++) dwall[x] = 0;
6095                 }
6096                 break;
6097             case 1:
6098                 k = smoststart[i] - xb1[j];
6099                 for (x=dalx2; x<=darx2; x++)
6100                     if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
6101                 break;
6102             case 2:
6103                 k = smoststart[i] - xb1[j];
6104                 for (x=dalx2; x<=darx2; x++)
6105                     if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
6106                 break;
6107             }
6108         }
6109 
6110         //sprite
6111 #ifdef YAX_ENABLE
6112         if (yax_globallev==YAX_MAXDRAWS || searchit==2)
6113 #endif
6114         if (searchit >= 1 && (searchx >= lx && searchx <= rx))
6115             if (searchy >= uwall[searchx] && searchy <= dwall[searchx])
6116             {
6117                 searchsector = sectnum; searchwall = spritenum;
6118                 searchstat = 3; searchit = 1;
6119             }
6120 
6121         globalorientation = cstat;
6122         globalpicnum = tilenum;
6123         if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
6124 
6125         if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
6126         setgotpic(globalpicnum);
6127         globalbufplc = waloff[globalpicnum];
6128 
6129         if (slope != 0)
6130         {
6131             x = picsiz[globalpicnum]; y = ((x>>4)&15); x &= 15;
6132             int const ispow2 = (pow2long[x]==span.x && pow2long[y]==span.y);
6133             sg1 = { divscale20(sg1.x, span.x) >> 16, divscale20(sg1.y, span.y) >> 16 };
6134             vec2f_t const tmp = { 256.f/float(span.x), 256.f/float(span.y) };
6135             sg_f2.x *= tmp.x;
6136             sg_f2.y *= tmp.y;
6137             sg_f.x *= tmp.x;
6138             sg_f.y *= tmp.y;
6139 
6140             //asm1 = -(globalzd>>(16-BITSOFPRECISION));
6141 #define LINTERPSIZ 4
6142             float const bzinc = -sgzd*(1.f/65536.f) * (1<<LINTERPSIZ);
6143             int32_t const vis = (sec->visibility != 0) ? mulscale4(globalhisibility, (uint8_t)(sec->visibility+16)) : globalhisibility;
6144             globvis = ((((uint64_t)(vis*sdaz)) >> 13) * xdimscale) >> 16;
6145 
6146             intptr_t fj = FP_OFF(palookup[globalpal]);
6147 
6148             setupslopevlin_alsotrans((picsiz[globalpicnum]&15) + ((picsiz[globalpicnum]>>4)<<8),
6149                                      waloff[globalpicnum],-ylookup[1]);
6150 
6151             int32_t const l = Blrintf((sgzd)*(1.f/65536.f));
6152 
6153             int32_t const shinc = Blrintf(sgz*xdimenscale*(1.f/65536.f));
6154 
6155             int32_t shoffs = (shinc > 0) ? (4 << 15) : ((16380 - ydimen) << 15);  // JBF: was 2044
6156             int32_t y1 = uwall[lx];
6157 
6158             int32_t m1 = Blrintf((y1*sgzd)*(1.f/65536.f) + sgzx*(1.f/64.f));
6159             //Avoid visibility overflow by crossing horizon
6160             m1 += klabs(l);
6161             int32_t m2 = m1+l;
6162             int32_t shy1 = y1+(shoffs>>15);
6163             int32_t shy2;
6164             if ((unsigned)shy1 >= SLOPALOOKUPSIZ-1)
6165             {
6166                 OSD_Printf("%s:%d: slopalookup[%" PRId32 "] overflow drawing sprite %d!\n", EDUKE32_FUNCTION, __LINE__, shy1, spritenum);
6167                 return;
6168             }
6169 
6170             intptr_t *mptr1 = &slopalookup[shy1]; intptr_t *mptr2 = mptr1+1;
6171             sg_f2.x += sg_f.x * lx;
6172             sg_f2.y += sg_f.y * lx;
6173             sgzx += sgz * lx;
6174             shoffs += shinc *lx;
6175 
6176             for (int x=lx; x<rx; x++)
6177             {
6178                 y1 = uwall[x]+1; y2 = dwall[x]-1;
6179                 if (y1 <= y2)
6180                 {
6181                     shy1 = y1+(shoffs>>15);
6182                     shy2 = y2+(shoffs>>15);
6183 
6184                     // Ridiculously steep gradient?
6185                     if ((unsigned)shy1 >= SLOPALOOKUPSIZ || (unsigned)shy2 >= SLOPALOOKUPSIZ)
6186                     {
6187                         OSD_Printf("%s:%d: slopalookup[%" PRId32 "] overflow drawing spritenum %d!\n", EDUKE32_FUNCTION, __LINE__,
6188                                    (unsigned)shy1 >= SLOPALOOKUPSIZ ? shy1 : shy2, spritenum);
6189                         goto next_most;
6190                     }
6191 
6192                     intptr_t *nptr1 = &slopalookup[shy1];
6193                     intptr_t *nptr2 = &slopalookup[shy2];
6194 
6195                     while (nptr1 <= mptr1)
6196                     {
6197                         *mptr1-- = fj + getpalookupsh(mulscale24(krecipasm(m1),globvis));
6198                         m1 -= l;
6199                     }
6200                     while (nptr2 >= mptr2)
6201                     {
6202                         *mptr2++ = fj + getpalookupsh(mulscale24(krecipasm(m2),globvis));
6203                         m2 += l;
6204                     }
6205 
6206                     vec2f_t const sg_f3 = { sg_f2.x*(1.f/1024.f)*1048576.f, sg_f2.y*(1.f/1024.f)*1048576.f };
6207                     float bz = (y2*sgzd)*(1.f/65536.f) + sgzx*(1.f/64.f);
6208                     uint8_t *p = (uint8_t*)(ylookup[y2]+x+frameoffset);
6209                     intptr_t* A_C_RESTRICT slopalptr = (intptr_t*)nptr2;
6210                     int cnt = y2-y1+1;
6211                     int u0 = Blrintf(sg_f3.x/bz);
6212                     int v0 = Blrintf(sg_f3.y/bz);
6213                     if (ispow2)
6214                     {
6215                         if ((cstat&2)==0)
6216                         {
6217                             while (cnt > 0)
6218                             {
6219                                 bz += bzinc;
6220                                 int const u1 = ((int)(sg_f3.x/bz)-u0)>>LINTERPSIZ;
6221                                 int const v1 = ((int)(sg_f3.y/bz)-v0)>>LINTERPSIZ;
6222                                 int cnt2 = min(cnt, 1<<LINTERPSIZ);
6223                                 for (; cnt2>0; cnt2--)
6224                                 {
6225                                     uint16_t u = (sg1.x+u0)&0xffff;
6226                                     uint16_t v = (sg1.y+v0)&0xffff;
6227                                     char ch = ggbuf[((u>>(16-gglogx))<<gglogy)+(v>>(16-gglogy))];
6228                                     if (ch != 255)
6229                                         *p = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
6230                                     slopalptr--;
6231                                     p += ggpinc;
6232                                     u0 += u1;
6233                                     v0 += v1;
6234                                 }
6235                                 cnt -= 1<<LINTERPSIZ;
6236                             }
6237                         }
6238                         else
6239                         {
6240                             const char* const trans = paletteGetBlendTable(globalblend);
6241                             if (cstat & 512)
6242                             {
6243                                 while (cnt > 0)
6244                                 {
6245                                     bz += bzinc;
6246                                     int const u1 = ((int)(sg_f3.x/bz)-u0)>>LINTERPSIZ;
6247                                     int const v1 = ((int)(sg_f3.y/bz)-v0)>>LINTERPSIZ;
6248                                     int cnt2 = min(cnt, 1<<LINTERPSIZ);
6249                                     for (; cnt2>0; cnt2--)
6250                                     {
6251                                         uint16_t u = (sg1.x+u0)&0xffff;
6252                                         uint16_t v = (sg1.y+v0)&0xffff;
6253                                         char ch = ggbuf[((u>>(16-gglogx))<<gglogy)+(v>>(16-gglogy))];
6254                                         if (ch != 255)
6255                                         {
6256                                             ch = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
6257                                             *p = trans[(ch<<8)|*p];
6258                                         }
6259                                         slopalptr--;
6260                                         p += ggpinc;
6261                                         u0 += u1;
6262                                         v0 += v1;
6263                                     }
6264                                     cnt -= 1<<LINTERPSIZ;
6265                                 }
6266                             }
6267                             else
6268                             {
6269                                 while (cnt > 0)
6270                                 {
6271                                     bz += bzinc;
6272                                     int const u1 = ((int)(sg_f3.x/bz)-u0)>>LINTERPSIZ;
6273                                     int const v1 = ((int)(sg_f3.y/bz)-v0)>>LINTERPSIZ;
6274                                     int cnt2 = min(cnt, 1<<LINTERPSIZ);
6275                                     for (; cnt2>0; cnt2--)
6276                                     {
6277                                         uint16_t u = (sg1.x+u0)&0xffff;
6278                                         uint16_t v = (sg1.y+v0)&0xffff;
6279                                         char ch = ggbuf[((u>>(16-gglogx))<<gglogy)+(v>>(16-gglogy))];
6280                                         if (ch != 255)
6281                                         {
6282                                             ch = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
6283                                             *p = trans[(*p<<8)|ch];
6284                                         }
6285                                         slopalptr--;
6286                                         p += ggpinc;
6287                                         u0 += u1;
6288                                         v0 += v1;
6289                                     }
6290                                     cnt -= 1<<LINTERPSIZ;
6291                                 }
6292                             }
6293                         }
6294                     }
6295                     else // the slow path...
6296                     {
6297                         if ((cstat&2)==0)
6298                         {
6299                             while (cnt > 0)
6300                             {
6301                                 bz += bzinc;
6302                                 int const u1 = ((int)(sg_f3.x/bz)-u0)>>LINTERPSIZ;
6303                                 int const v1 = ((int)(sg_f3.y/bz)-v0)>>LINTERPSIZ;
6304                                 int cnt2 = min(cnt, 1<<LINTERPSIZ);
6305                                 for (; cnt2>0; cnt2--)
6306                                 {
6307                                     uint16_t u = (sg1.x+u0)&0xffff;
6308                                     uint16_t v = (sg1.y+v0)&0xffff;
6309                                     char ch = ggbuf[mulscale16(u, span.x)*span.y+mulscale16(v, span.y)];
6310                                     if (ch != 255)
6311                                         *p = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
6312                                     slopalptr--;
6313                                     p += ggpinc;
6314                                     u0 += u1;
6315                                     v0 += v1;
6316                                 }
6317                                 cnt -= 1<<LINTERPSIZ;
6318                             }
6319                         }
6320                         else
6321                         {
6322                             const char* const trans = paletteGetBlendTable(globalblend);
6323                             if (cstat & 512)
6324                             {
6325                                 while (cnt > 0)
6326                                 {
6327                                     bz += bzinc;
6328                                     int const u1 = ((int)(sg_f3.x/bz)-u0)>>LINTERPSIZ;
6329                                     int const v1 = ((int)(sg_f3.y/bz)-v0)>>LINTERPSIZ;
6330                                     int cnt2 = min(cnt, 1<<LINTERPSIZ);
6331                                     for (; cnt2>0; cnt2--)
6332                                     {
6333                                         uint16_t u = (sg1.x+u0)&0xffff;
6334                                         uint16_t v = (sg1.y+v0)&0xffff;
6335                                         char ch = ggbuf[mulscale16(u, span.x)*span.y+mulscale16(v, span.y)];
6336                                         if (ch != 255)
6337                                         {
6338                                             ch = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
6339                                             *p = trans[(ch<<8)|*p];
6340                                         }
6341                                         slopalptr--;
6342                                         p += ggpinc;
6343                                         u0 += u1;
6344                                         v0 += v1;
6345                                     }
6346                                     cnt -= 1<<LINTERPSIZ;
6347                                 }
6348                             }
6349                             else
6350                             {
6351                                 while (cnt > 0)
6352                                 {
6353                                     bz += bzinc;
6354                                     int const u1 = ((int)(sg_f3.x/bz)-u0)>>LINTERPSIZ;
6355                                     int const v1 = ((int)(sg_f3.y/bz)-v0)>>LINTERPSIZ;
6356                                     int cnt2 = min(cnt, 1<<LINTERPSIZ);
6357                                     for (; cnt2>0; cnt2--)
6358                                     {
6359                                         uint16_t u = (sg1.x+u0)&0xffff;
6360                                         uint16_t v = (sg1.y+v0)&0xffff;
6361                                         char ch = ggbuf[mulscale16(u, span.x)*span.y+mulscale16(v, span.y)];
6362                                         if (ch != 255)
6363                                         {
6364                                             ch = *(uint8_t *)(((intptr_t)slopalptr[0])+ch);
6365                                             *p = trans[(*p<<8)|ch];
6366                                         }
6367                                         slopalptr--;
6368                                         p += ggpinc;
6369                                         u0 += u1;
6370                                         v0 += v1;
6371                                     }
6372                                     cnt -= 1<<LINTERPSIZ;
6373                                 }
6374                             }
6375                         }
6376                     }
6377 #undef LINTERPSIZ
6378                     if ((x&15) == 0) faketimerhandler();
6379                 }
6380 next_most:
6381                 sg_f2.x += sg_f.x;
6382                 sg_f2.y += sg_f.y;
6383                 sgzx += sgz;
6384                 shoffs += shinc;
6385             }
6386         }
6387         else
6388         {
6389             globvis = mulscale16(globalhisibility,viewingrange);
6390             if (sec->visibility != 0) globvis = mulscale4(globvis, (uint8_t)(sec->visibility+16));
6391 
6392             x = picsiz[globalpicnum]; y = ((x>>4)&15); x &= 15;
6393 #if 0
6394             if (pow2long[x] != xspan)
6395             {
6396                 x++;
6397                 globalx1 = mulscale(globalx1,xspan,x);
6398                 globalx2 = mulscale(globalx2,xspan,x);
6399             }
6400 #endif
6401             dax = globalxpanning; day = globalypanning;
6402             globalxpanning = -dmulscale6(globalx1,day,globalx2,dax);
6403             globalypanning = -dmulscale6(globaly1,day,globaly2,dax);
6404 
6405             globalx2 = mulscale16(globalx2,viewingrange);
6406             globaly2 = mulscale16(globaly2,viewingrange);
6407             globalzd = mulscale16(globalzd,viewingrangerecip);
6408 
6409             globalx1 = (globalx1-globalx2)*halfxdimen;
6410             globaly1 = (globaly1-globaly2)*halfxdimen;
6411 
6412             if ((cstat&2) == 0)
6413                 msethlineshift(x,y);
6414             else
6415                 tsethlineshift(x,y);
6416 
6417             globalispow2 = (pow2long[x]==span.x && pow2long[y]==span.y);
6418             globalxspan = span.x;
6419             globalyspan = span.y;
6420 
6421             //Draw it!
6422             ceilspritescan(lx,rx-1);
6423             globalispow2 = 1;
6424         }
6425     }
6426     else if ((cstat&48) == 48)
6427     {
6428         const int32_t daxrepeat = ((sprite[spritenum].cstat&48)==16) ?
6429             (tspr->xrepeat * 5) / 4 :
6430             tspr->xrepeat;
6431 
6432         const int32_t lx = 0, rx = xdim-1;
6433 
6434         for (x=lx; x<=rx; x++)
6435         {
6436             lwall[x] = startumost[x+windowxy1.x]-windowxy1.y;
6437             swall[x] = startdmost[x+windowxy1.x]-windowxy1.y;
6438         }
6439         for (i=smostwallcnt-1; i>=0; i--)
6440         {
6441             j = smostwall[i];
6442             if ((xb1[j] > rx) || (xb2[j] < lx)) continue;
6443             if ((yp <= yb1[j]) && (yp <= yb2[j])) continue;
6444             if (spritewallfront(tspr,(int32_t)thewall[j]) && ((yp <= yb1[j]) || (yp <= yb2[j]))) continue;
6445 
6446             const int32_t dalx2 = max(xb1[j],lx);
6447             const int32_t darx2 = min(xb2[j],rx);
6448 
6449             switch (smostwalltype[i])
6450             {
6451             case 0:
6452                 if (dalx2 <= darx2)
6453                 {
6454                     if ((dalx2 == lx) && (darx2 == rx)) return;
6455                     //clearbufbyte(&swall[dalx2],(darx2-dalx2+1)*sizeof(swall[0]),0L);
6456                     for (x=dalx2; x<=darx2; x++) swall[x] = 0;
6457                 }
6458                 break;
6459             case 1:
6460                 k = smoststart[i] - xb1[j];
6461                 x = dalx2;
6462 #ifdef CLASSIC_SLICE_BY_4
6463                 for (; x<=darx2-2; x+=2)
6464                 {
6465                     if (smost[k+x] > lwall[x]) lwall[x] = smost[k+x];
6466                     if (smost[k+x+1] > lwall[x+1]) lwall[x+1] = smost[k+x+1];
6467                 }
6468 #endif
6469                 for (; x<=darx2; x++)
6470                     if (smost[k+x] > lwall[x]) lwall[x] = smost[k+x];
6471                 break;
6472             case 2:
6473                 k = smoststart[i] - xb1[j];
6474                 x = dalx2;
6475 #ifdef CLASSIC_SLICE_BY_4
6476                 for (; x<=darx2-4; x+=4)
6477                 {
6478                     if (smost[k+x] < swall[x]) swall[x] = smost[k+x];
6479                     if (smost[k+x+1] < swall[x+1]) swall[x+1] = smost[k+x+1];
6480                     if (smost[k+x+2] < swall[x+2]) swall[x+2] = smost[k+x+2];
6481                     if (smost[k+x+3] < swall[x+3]) swall[x+3] = smost[k+x+3];
6482                 }
6483 #endif
6484                 for (; x<=darx2; x++)
6485                     if (smost[k+x] < swall[x]) swall[x] = smost[k+x];
6486                 break;
6487             }
6488         }
6489 
6490         if (lwall[rx] >= swall[rx])
6491         {
6492             for (x=lx; x<rx; x++)
6493                 if (lwall[x] < swall[x]) break;
6494             if (x == rx) return;
6495         }
6496 
6497         for (i=0; i<MAXVOXMIPS; i++)
6498             if (!voxoff[vtilenum][i])
6499             {
6500                 if (loadvoxel_replace)
6501                     loadvoxel_replace(vtilenum);
6502                 break;
6503             }
6504 
6505         const int32_t *const longptr = (int32_t *)voxoff[vtilenum][0];
6506         if (longptr == NULL)
6507         {
6508             globalshade = 32;
6509             tspr->xrepeat = tspr->yrepeat = 255;
6510             goto draw_as_face_sprite;
6511         }
6512 
6513         int32_t nxrepeat, nyrepeat;
6514 
6515         if (voxscale[vtilenum] == 65536)
6516         {
6517             nxrepeat = (daxrepeat<<16);
6518             nyrepeat = (((int32_t)tspr->yrepeat)<<16);
6519         }
6520         else
6521         {
6522             nxrepeat = daxrepeat*voxscale[vtilenum];
6523             nyrepeat = ((int32_t)tspr->yrepeat)*voxscale[vtilenum];
6524         }
6525 
6526         off.x = tspr->xoffset;
6527         off.y = /*picanm[sprite[tspr->owner].picnum].yofs +*/ tspr->yoffset;
6528         if (cstat & 4) off.x = -off.x;
6529         if ((cstat & 8) && (tspr->cstat&48) != 0) off.y = -off.y;
6530         tspr->z -= off.y * tspr->yrepeat << 2;
6531 
6532         const float xfactor = (tspr->cstat&48) != 16 ? (256.f/320.f) : 1.f;
6533         const int32_t xv = (int32_t)(tspr->xrepeat*sintable[(tspr->ang+2560+1536)&2047]*xfactor);
6534         const int32_t yv = (int32_t)(tspr->xrepeat*sintable[(tspr->ang+2048+1536)&2047]*xfactor);
6535 
6536         tspr->x -= mulscale16(xv, off.x);
6537         tspr->y -= mulscale16(yv, off.x);
6538 
6539         globvis = globalvisibility;
6540         if (sec->visibility != 0) globvis = mulscale4(globvis, (uint8_t)(sec->visibility+16));
6541 
6542 #ifdef YAX_ENABLE
6543         if (yax_globallev==YAX_MAXDRAWS || searchit==2)
6544 #endif
6545         if (searchit >= 1 && yp > (4<<8) && (searchy >= lwall[searchx] && searchy < swall[searchx]))
6546         {
6547             int32_t const xdsiz = divscale19(xdimenscale,yp);
6548             int32_t const xv = mulscale16(nxrepeat,xyaspect);
6549 
6550             int32_t const xspan = ((B_LITTLE32(longptr[0]) + B_LITTLE32(longptr[1])) >> 1);
6551             int32_t const yspan = B_LITTLE32(longptr[2]);
6552 
6553             vec2_t const siz = { mulscale_triple30(xdsiz, xv, xspan), mulscale_triple30(xdsiz, nyrepeat, yspan) };
6554 
6555             //Watch out for divscale overflow
6556             if (((xspan>>11) < siz.x) && (yspan < (siz.y>>1)))
6557             {
6558                 x1 = xb-(siz.x>>1);
6559                 if (xspan&1) x1 += mulscale31(xdsiz,xv);  //Odd xspans
6560                 i = mulscale30(xdsiz,xv*off.x);
6561                 if ((cstat&4) == 0) x1 -= i; else x1 += i;
6562 
6563                 y1 = mulscale16(tspr->z-globalposz,xdsiz);
6564 
6565                 if (!(cstat & 128))
6566                     y1 -= mulscale16(mulscale22(B_LITTLE32(longptr[5]), nyrepeat), xdsiz);
6567                 //y1 -= mulscale30(xdsiz,nyrepeat*yoff);
6568                 y1 += (globalhoriz<<8)-siz.y;
6569                 //if (cstat&128)  //Already fixed up above
6570                 y1 += (siz.y>>1);
6571 
6572                 x2 = x1+siz.x-1;
6573                 y2 = y1+siz.y-1;
6574                 if ((y1|255) < (y2|255) && searchx >= (x1>>8)+1 && searchx <= (x2>>8))
6575                 {
6576                     int32_t startum, startdm;
6577 
6578                     if ((sec->ceilingstat&3) == 0)
6579                         startum = globalhoriz+mulscale24(xdsiz,sec->ceilingz-globalposz)-1;
6580                     else
6581                         startum = 0;
6582 
6583                     if ((sec->floorstat&3) == 0)
6584                         startdm = globalhoriz+mulscale24(xdsiz,sec->floorz-globalposz)+1;
6585                     else
6586                         startdm = INT32_MAX;
6587 
6588                     //sprite
6589                     if (searchy >= max(startum,(y1>>8)) && searchy < min(startdm,(y2>>8)))
6590                     {
6591                         searchsector = sectnum; searchwall = spritenum;
6592                         searchstat = 3; searchit = 1;
6593                     }
6594                 }
6595             }
6596         }
6597 
6598         x = tspr->x + spriteext[spritenum].mdposition_offset.x;
6599         y = tspr->y + spriteext[spritenum].mdposition_offset.y;
6600         z = tspr->z + spriteext[spritenum].mdposition_offset.z;
6601 
6602         i = (int32_t)tspr->ang+1536;
6603         i += spriteext[spritenum].mdangoff;
6604 
6605         const int32_t ceilingz = (sec->ceilingstat&3) == 0 ? sec->ceilingz : INT32_MIN;
6606         const int32_t floorz = (sec->floorstat&3) == 0 ? sec->floorz : INT32_MAX;
6607 
6608         classicDrawVoxel(x,y,z,i,daxrepeat,(int32_t)tspr->yrepeat,vtilenum,
6609             tspr->shade,tspr->pal,lwall,swall,tspr->cstat,(tspr->cstat&48)!=48,floorz,ceilingz);
6610     }
6611 
6612     if (automapping == 1 && (unsigned)spritenum < MAXSPRITES)
6613         show2dsprite[spritenum>>3] |= pow2char[spritenum&7];
6614 }
6615 
renderDrawSprite(int32_t snum)6616 static void renderDrawSprite(int32_t snum)
6617 {
6618     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
6619 
6620     switch (videoGetRenderMode())
6621     {
6622     case REND_CLASSIC:
6623         classicDrawSprite(snum);
6624         return;
6625 #ifdef USE_OPENGL
6626     case REND_POLYMOST:
6627         polymost_drawsprite(snum);
6628         return;
6629 # ifdef POLYMER
6630     case REND_POLYMER:
6631         glEnable(GL_ALPHA_TEST);
6632         glEnable(GL_BLEND);
6633         polymer_drawsprite(snum);
6634         glDisable(GL_BLEND);
6635         glDisable(GL_ALPHA_TEST);
6636         return;
6637 # endif
6638 #endif
6639     }
6640 }
6641 
6642 
6643 //
6644 // drawmaskwall (internal)
6645 //
renderDrawMaskedWall(int16_t damaskwallcnt)6646 static void renderDrawMaskedWall(int16_t damaskwallcnt)
6647 {
6648     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
6649 
6650     //============================================================================= //POLYMOST BEGINS
6651 #ifdef USE_OPENGL
6652     if (videoGetRenderMode() == REND_POLYMOST) { polymost_drawmaskwall(damaskwallcnt); return; }
6653 # ifdef POLYMER
6654     else if (videoGetRenderMode() == REND_POLYMER)
6655     {
6656         glEnable(GL_ALPHA_TEST);
6657         glEnable(GL_BLEND);
6658 
6659         polymer_drawmaskwall(damaskwallcnt);
6660 
6661         glDisable(GL_BLEND);
6662         glDisable(GL_ALPHA_TEST);
6663 
6664         return;
6665     }
6666 #endif
6667 #endif
6668     //============================================================================= //POLYMOST ENDS
6669 
6670     int32_t z = maskwall[damaskwallcnt];
6671     auto wal = (uwallptr_t)&wall[thewall[z]];
6672     int32_t sectnum = thesector[z];
6673     auto sec = (usectorptr_t)&sector[sectnum];
6674     auto nsec = (usectorptr_t)&sector[wal->nextsector];
6675     int32_t z1 = max(nsec->ceilingz,sec->ceilingz);
6676     int32_t z2 = min(nsec->floorz,sec->floorz);
6677 
6678     wallmost(uwall,z,sectnum,(uint8_t)0);
6679     wallmost(uplc,z,(int32_t)wal->nextsector,(uint8_t)0);
6680     for (bssize_t x=xb1[z]; x<=xb2[z]; x++)
6681         if (uplc[x] > uwall[x])
6682             uwall[x] = uplc[x];
6683     wallmost(dwall,z,sectnum,(uint8_t)1);
6684     wallmost(dplc,z,(int32_t)wal->nextsector,(uint8_t)1);
6685     for (bssize_t x=xb1[z]; x<=xb2[z]; x++)
6686         if (dplc[x] < dwall[x])
6687             dwall[x] = dplc[x];
6688     prepwall(z,wal);
6689 
6690     setup_globals_wall1(wal, wal->overpicnum);
6691     setup_globals_wall2(wal, sec->visibility, z1, z2);
6692 
6693     for (bssize_t i=smostwallcnt-1; i>=0; i--)
6694     {
6695         int j=smostwall[i];
6696         if ((xb1[j] > xb2[z]) || (xb2[j] < xb1[z])) continue;
6697         if (wallfront(j,z)) continue;
6698 
6699         int lx = max(xb1[j],xb1[z]);
6700         int rx = min(xb2[j],xb2[z]);
6701 
6702         switch (smostwalltype[i])
6703         {
6704         case 0:
6705             if (lx <= rx)
6706             {
6707                 if ((lx == xb1[z]) && (rx == xb2[z])) return;
6708                 //clearbufbyte(&dwall[lx],(rx-lx+1)*sizeof(dwall[0]),0L);
6709                 for (bssize_t x=lx; x<=rx; x++) dwall[x] = 0;
6710             }
6711             break;
6712         case 1:
6713             for (bssize_t x=lx, k = smoststart[i] - xb1[j]; x<=rx; x++)
6714                 if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
6715             break;
6716         case 2:
6717             for (bssize_t x=lx, k = smoststart[i] - xb1[j]; x<=rx; x++)
6718                 if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
6719             break;
6720         }
6721     }
6722 
6723     //maskwall
6724     if (searchit >= 1 && (searchx >= xb1[z] && searchx <= xb2[z]))
6725         if (searchy >= uwall[searchx] && searchy <= dwall[searchx])
6726         {
6727             searchsector = sectnum; searchbottomwall = searchwall = thewall[z];
6728             searchstat = 4; searchit = 1;
6729         }
6730 
6731     if ((globalorientation&128) == 0)
6732     {
6733         maskwallscan(xb1[z],xb2[z], 0);
6734     }
6735     else
6736     {
6737         if (globalorientation&128)
6738 #ifdef NEW_MAP_FORMAT
6739             setup_blend(wal->blend, globalorientation&512);
6740 #else
6741             setup_blend(wallext[thewall[z]].blend, globalorientation&512);
6742 #endif
6743         transmaskwallscan(xb1[z],xb2[z], 0);
6744     }
6745 }
6746 
6747 
6748 //
6749 // fillpolygon (internal)
6750 //
renderFillPolygon(int32_t npoints)6751 static void renderFillPolygon(int32_t npoints)
6752 {
6753     int32_t i, z, y, miny, maxy;
6754 
6755     // fix for bad next-point (xb1) values...
6756     for (z=0; z<npoints; z++)
6757         if ((unsigned)xb1[z] >= (unsigned)npoints)
6758             xb1[z] = 0;
6759 
6760 #ifdef USE_OPENGL
6761     if (videoGetRenderMode() >= REND_POLYMOST && in3dmode())
6762     {
6763 #ifdef POLYMER
6764         if (videoGetRenderMode() == REND_POLYMER)
6765             polymer_fillpolygon(npoints);
6766         else
6767 #endif
6768             polymost_fillpolygon(npoints);
6769 
6770         return;
6771     }
6772 #endif
6773 
6774     // 1. Calculate y bounds.
6775     miny = INT32_MAX; maxy = INT32_MIN;
6776     for (z=npoints-1; z>=0; z--)
6777     {
6778         y = ry1[z];
6779         miny = min(miny,y);
6780         maxy = max(maxy,y);
6781     }
6782 
6783     miny >>= 12;
6784     maxy >>= 12;
6785 
6786     if (miny < 0)
6787         miny = 0;
6788     if (maxy >= ydim)
6789         maxy = ydim-1;
6790 
6791     for (i=0, y=miny; y<=maxy; y++, i++)
6792     {
6793         //They're pointers! - watch how you optimize this thing
6794         dotp1[y] = &smost[i*nodesperline];
6795         dotp2[y] = &smost[i*nodesperline + (nodesperline>>1)];
6796     }
6797 
6798     for (z=npoints-1; z>=0; z--)
6799     {
6800         const int32_t zz=xb1[z];
6801 
6802         // NOTE: clamp for crash prevention... :-/
6803         // r1874 says: "Fix more overheadmap crashes, this time with 'Last
6804         // Pissed Time'"
6805         const int32_t y1 = clamp(ry1[z], 0, (ydim<<12)-1);
6806         const int32_t y2 = clamp(ry1[zz], 0, (ydim<<12)-1);
6807 
6808         const int32_t day1 = y1>>12;
6809         const int32_t day2 = y2>>12;
6810 
6811         if (day1 != day2)
6812         {
6813             int32_t x1=rx1[z], x2=rx1[zz];
6814             const int32_t xinc = divscale12(x2-x1, y2-y1);
6815 
6816             if (day2 > day1)
6817             {
6818                 x1 += mulscale12((day1<<12)+4095-y1, xinc);
6819                 for (y=day1; y<day2; y++)
6820                 {
6821                     Bassert(dotp2[y]);
6822                     *(dotp2[y]++) = x1>>12;
6823                     x1 += xinc;
6824                 }
6825             }
6826             else
6827             {
6828                 x2 += mulscale12((day2<<12)+4095-y2, xinc);
6829                 for (y=day2; y<day1; y++)
6830                 {
6831                     Bassert(dotp1[y]);
6832                     *(dotp1[y]++) = x2>>12;
6833                     x2 += xinc;
6834                 }
6835             }
6836         }
6837     }
6838 
6839     globalx1 = mulscale16(globalx1,xyaspect);
6840     globaly2 = mulscale16(globaly2,xyaspect);
6841 
6842     {
6843         const int32_t oy = miny+1-(ydim>>1);
6844         globalposx += oy*(int64_t)globalx1;
6845         globalposy += oy*(int64_t)globaly2;
6846     }
6847 
6848     setuphlineasm4(asm1,asm2);
6849 
6850     for (i=0, y=miny; y<=maxy; y++, i++)
6851     {
6852         int16_t *const xptr = &smost[i*nodesperline];
6853         int16_t *const xptr2 = &smost[i*nodesperline + (nodesperline>>1)];
6854 
6855         const bssize_t cnt = dotp1[y]-xptr;
6856 
6857         for (z=cnt-1; z>=0; z--)
6858         {
6859             int32_t x1, x2;
6860             int32_t zz, i1=0, i2=0;  // point indices (like loop z)
6861 
6862             for (zz=z; zz>0; zz--)
6863             {
6864                 if (xptr[zz] < xptr[i1])
6865                     i1 = zz;
6866                 if (xptr2[zz] < xptr2[i2])
6867                     i2 = zz;
6868             }
6869 
6870             x1 = xptr[i1];
6871             xptr[i1] = xptr[z];
6872 
6873             x2 = xptr2[i2]-1;
6874             xptr2[i2] = xptr2[z];
6875 
6876             if (x1 > x2)
6877                 continue;
6878 
6879             if ((unsigned)x1 >= xdim+0u || (unsigned)x2 >= xdim+0u)
6880                 continue;
6881 
6882             if (globalpolytype < 1)
6883             {
6884                 //maphline
6885                 const int32_t ox = x2+1-(xdim>>1);
6886 
6887                 hlineasm4(x2 - x1, -1L, globalshade << 8,
6888                           ox * asm2 - globalposy, ox * asm1 + globalposx,
6889                           ylookup[y] + x2 + frameplace);
6890             }
6891             else
6892             {
6893                 //maphline
6894                 const int32_t ox = x1+1-(xdim>>1);
6895                 const int32_t bx = ox*asm1 + globalposx;
6896                 const int32_t by = ox*asm2 - globalposy;
6897 
6898                 const intptr_t p = ylookup[y]+x1+frameplace;
6899 
6900                 if (globalispow2)
6901                 {
6902                     if (globalpolytype == 1)
6903                         mhline(globalbufplc,bx,(x2-x1)<<16,0L,by,p);
6904                     else
6905                         thline(globalbufplc,bx,(x2-x1)<<16,0L,by,p);
6906                 }
6907                 else
6908                 {
6909                     if (globalpolytype == 1)
6910                         nonpow2_mhline(globalbufplc,bx,(x2-x1)<<16,by,(char *)p);
6911                     else
6912                         nonpow2_thline(globalbufplc,bx,(x2-x1)<<16,by,(char *)p);
6913                 }
6914             }
6915         }
6916 
6917         globalposx += (int64_t)globalx1;
6918         globalposy += (int64_t)globaly2;
6919     }
6920 
6921     faketimerhandler();
6922 }
6923 
addscaleclamp(int32_t a,int32_t b,int32_t s1,int32_t s2)6924 static inline int32_t addscaleclamp(int32_t a, int32_t b, int32_t s1, int32_t s2)
6925 {
6926     // a + scale(b, s1, s1-s2), but without arithmetic exception when the
6927     // scale() expression overflows
6928 
6929     int64_t tmp = (int64_t)a + tabledivide64((int64_t)b*s1, s1-s2);
6930 
6931     if (EDUKE32_PREDICT_FALSE(tmp <= INT32_MIN+1))
6932         return INT32_MIN+1;
6933     if (EDUKE32_PREDICT_FALSE(tmp >= INT32_MAX))
6934         return INT32_MAX;
6935     return tmp;
6936 }
6937 
6938 //
6939 // clippoly (internal)
6940 //
clippoly(int32_t npoints,int32_t clipstat)6941 static int32_t clippoly(int32_t npoints, int32_t clipstat)
6942 {
6943     int32_t z, zz, s1, s2, t, npoints2, start2, z1, z2, z3, z4, splitcnt;
6944     int32_t cx1, cy1, cx2, cy2;
6945 
6946     cx1 = windowxy1.x;
6947     cy1 = windowxy1.y;
6948     cx2 = windowxy2.x+1;
6949     cy2 = windowxy2.y+1;
6950     cx1 <<= 12; cy1 <<= 12; cx2 <<= 12; cy2 <<= 12;
6951 
6952     if (clipstat&0xa)   //Need to clip top or left
6953     {
6954         npoints2 = 0; start2 = 0; z = 0; splitcnt = 0;
6955         do
6956         {
6957             s2 = cx1-rx1[z];
6958             do
6959             {
6960                 zz = xb1[z]; xb1[z] = -1;
6961                 s1 = s2; s2 = cx1-rx1[zz];
6962                 if (s1 < 0)
6963                 {
6964                     rx2[npoints2] = rx1[z]; ry2[npoints2] = ry1[z];
6965                     xb2[npoints2] = npoints2+1; npoints2++;
6966                 }
6967                 if ((s1^s2) < 0)
6968                 {
6969                     rx2[npoints2] = addscaleclamp(rx1[z], rx1[zz]-rx1[z], s1, s2);
6970                     ry2[npoints2] = addscaleclamp(ry1[z], ry1[zz]-ry1[z], s1, s2);
6971                     if (s1 < 0) bunchp2[splitcnt++] = npoints2;
6972                     xb2[npoints2] = npoints2+1;
6973                     npoints2++;
6974                 }
6975                 z = zz;
6976             }
6977             while (xb1[z] >= 0);
6978 
6979             if (npoints2 >= start2+3)
6980                 xb2[npoints2-1] = start2, start2 = npoints2;
6981             else
6982                 npoints2 = start2;
6983 
6984             z = 1;
6985             while ((z < npoints) && (xb1[z] < 0)) z++;
6986         }
6987         while (z < npoints);
6988         if (npoints2 <= 2) return 0;
6989 
6990         for (z=1; z<splitcnt; z++)
6991             for (zz=0; zz<z; zz++)
6992             {
6993                 z1 = bunchp2[z]; z2 = xb2[z1]; z3 = bunchp2[zz]; z4 = xb2[z3];
6994                 s1  = klabs(rx2[z1]-rx2[z2])+klabs(ry2[z1]-ry2[z2]);
6995                 s1 += klabs(rx2[z3]-rx2[z4])+klabs(ry2[z3]-ry2[z4]);
6996                 s2  = klabs(rx2[z1]-rx2[z4])+klabs(ry2[z1]-ry2[z4]);
6997                 s2 += klabs(rx2[z3]-rx2[z2])+klabs(ry2[z3]-ry2[z2]);
6998                 if (s2 < s1)
6999                     { t = xb2[bunchp2[z]]; xb2[bunchp2[z]] = xb2[bunchp2[zz]]; xb2[bunchp2[zz]] = t; }
7000             }
7001 
7002 
7003         npoints = 0; start2 = 0; z = 0; splitcnt = 0;
7004         do
7005         {
7006             s2 = cy1-ry2[z];
7007             do
7008             {
7009                 zz = xb2[z]; xb2[z] = -1;
7010                 s1 = s2; s2 = cy1-ry2[zz];
7011                 if (s1 < 0)
7012                 {
7013                     rx1[npoints] = rx2[z]; ry1[npoints] = ry2[z];
7014                     xb1[npoints] = npoints+1; npoints++;
7015                 }
7016                 if ((s1^s2) < 0)
7017                 {
7018                     rx1[npoints] = addscaleclamp(rx2[z], rx2[zz]-rx2[z], s1, s2);
7019                     ry1[npoints] = addscaleclamp(ry2[z], ry2[zz]-ry2[z], s1, s2);
7020                     if (s1 < 0) bunchp2[splitcnt++] = npoints;
7021                     xb1[npoints] = npoints+1;
7022                     npoints++;
7023                 }
7024                 z = zz;
7025             }
7026             while (xb2[z] >= 0);
7027 
7028             if (npoints >= start2+3)
7029                 xb1[npoints-1] = start2, start2 = npoints;
7030             else
7031                 npoints = start2;
7032 
7033             z = 1;
7034             while ((z < npoints2) && (xb2[z] < 0)) z++;
7035         }
7036         while (z < npoints2);
7037         if (npoints <= 2) return 0;
7038 
7039         for (z=1; z<splitcnt; z++)
7040             for (zz=0; zz<z; zz++)
7041             {
7042                 z1 = bunchp2[z]; z2 = xb1[z1]; z3 = bunchp2[zz]; z4 = xb1[z3];
7043                 s1  = klabs(rx1[z1]-rx1[z2])+klabs(ry1[z1]-ry1[z2]);
7044                 s1 += klabs(rx1[z3]-rx1[z4])+klabs(ry1[z3]-ry1[z4]);
7045                 s2  = klabs(rx1[z1]-rx1[z4])+klabs(ry1[z1]-ry1[z4]);
7046                 s2 += klabs(rx1[z3]-rx1[z2])+klabs(ry1[z3]-ry1[z2]);
7047                 if (s2 < s1)
7048                     { t = xb1[bunchp2[z]]; xb1[bunchp2[z]] = xb1[bunchp2[zz]]; xb1[bunchp2[zz]] = t; }
7049             }
7050     }
7051     if (clipstat&0x5)   //Need to clip bottom or right
7052     {
7053         npoints2 = 0; start2 = 0; z = 0; splitcnt = 0;
7054         do
7055         {
7056             s2 = rx1[z]-cx2;
7057             do
7058             {
7059                 zz = xb1[z]; xb1[z] = -1;
7060                 s1 = s2; s2 = rx1[zz]-cx2;
7061                 if (s1 < 0)
7062                 {
7063                     rx2[npoints2] = rx1[z]; ry2[npoints2] = ry1[z];
7064                     xb2[npoints2] = npoints2+1; npoints2++;
7065                 }
7066                 if ((s1^s2) < 0)
7067                 {
7068                     rx2[npoints2] = addscaleclamp(rx1[z], rx1[zz]-rx1[z], s1, s2);
7069                     ry2[npoints2] = addscaleclamp(ry1[z], ry1[zz]-ry1[z], s1, s2);
7070                     if (s1 < 0) bunchp2[splitcnt++] = npoints2;
7071                     xb2[npoints2] = npoints2+1;
7072                     npoints2++;
7073                 }
7074                 z = zz;
7075             }
7076             while (xb1[z] >= 0);
7077 
7078             if (npoints2 >= start2+3)
7079                 xb2[npoints2-1] = start2, start2 = npoints2;
7080             else
7081                 npoints2 = start2;
7082 
7083             z = 1;
7084             while ((z < npoints) && (xb1[z] < 0)) z++;
7085         }
7086         while (z < npoints);
7087         if (npoints2 <= 2) return 0;
7088 
7089         for (z=1; z<splitcnt; z++)
7090             for (zz=0; zz<z; zz++)
7091             {
7092                 z1 = bunchp2[z]; z2 = xb2[z1]; z3 = bunchp2[zz]; z4 = xb2[z3];
7093                 s1  = klabs(rx2[z1]-rx2[z2])+klabs(ry2[z1]-ry2[z2]);
7094                 s1 += klabs(rx2[z3]-rx2[z4])+klabs(ry2[z3]-ry2[z4]);
7095                 s2  = klabs(rx2[z1]-rx2[z4])+klabs(ry2[z1]-ry2[z4]);
7096                 s2 += klabs(rx2[z3]-rx2[z2])+klabs(ry2[z3]-ry2[z2]);
7097                 if (s2 < s1)
7098                     { t = xb2[bunchp2[z]]; xb2[bunchp2[z]] = xb2[bunchp2[zz]]; xb2[bunchp2[zz]] = t; }
7099             }
7100 
7101 
7102         npoints = 0; start2 = 0; z = 0; splitcnt = 0;
7103         do
7104         {
7105             s2 = ry2[z]-cy2;
7106             do
7107             {
7108                 zz = xb2[z]; xb2[z] = -1;
7109                 s1 = s2; s2 = ry2[zz]-cy2;
7110                 if (s1 < 0)
7111                 {
7112                     rx1[npoints] = rx2[z]; ry1[npoints] = ry2[z];
7113                     xb1[npoints] = npoints+1; npoints++;
7114                 }
7115                 if ((s1^s2) < 0)
7116                 {
7117                     rx1[npoints] = addscaleclamp(rx2[z], rx2[zz]-rx2[z], s1, s2);
7118                     ry1[npoints] = addscaleclamp(ry2[z], ry2[zz]-ry2[z], s1, s2);
7119                     if (s1 < 0) bunchp2[splitcnt++] = npoints;
7120                     xb1[npoints] = npoints+1;
7121                     npoints++;
7122                 }
7123                 z = zz;
7124             }
7125             while (xb2[z] >= 0);
7126 
7127             if (npoints >= start2+3)
7128                 xb1[npoints-1] = start2, start2 = npoints;
7129             else
7130                 npoints = start2;
7131 
7132             z = 1;
7133             while ((z < npoints2) && (xb2[z] < 0)) z++;
7134         }
7135         while (z < npoints2);
7136         if (npoints <= 2) return 0;
7137 
7138         for (z=1; z<splitcnt; z++)
7139             for (zz=0; zz<z; zz++)
7140             {
7141                 z1 = bunchp2[z]; z2 = xb1[z1]; z3 = bunchp2[zz]; z4 = xb1[z3];
7142                 s1  = klabs(rx1[z1]-rx1[z2])+klabs(ry1[z1]-ry1[z2]);
7143                 s1 += klabs(rx1[z3]-rx1[z4])+klabs(ry1[z3]-ry1[z4]);
7144                 s2  = klabs(rx1[z1]-rx1[z4])+klabs(ry1[z1]-ry1[z4]);
7145                 s2 += klabs(rx1[z3]-rx1[z2])+klabs(ry1[z3]-ry1[z2]);
7146                 if (s2 < s1)
7147                     { t = xb1[bunchp2[z]]; xb1[bunchp2[z]] = xb1[bunchp2[zz]]; xb1[bunchp2[zz]] = t; }
7148             }
7149     }
7150     return npoints;
7151 }
7152 
7153 
7154 //
7155 // clippoly4 (internal)
7156 //
7157 //Assume npoints=4 with polygon on &nrx1,&nry1
7158 //JBF 20031206: Thanks to Ken's hunting, s/(rx1|ry1|rx2|ry2)/n\1/ in this function
clippoly4(int32_t cx1,int32_t cy1,int32_t cx2,int32_t cy2)7159 static int32_t clippoly4(int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2)
7160 {
7161     int32_t n, nn, z, zz, x, x1, x2, y, y1, y2, t;
7162 
7163     nn = 0; z = 0;
7164     do
7165     {
7166         zz = ((z+1)&3);
7167         x1 = nrx1[z]; x2 = nrx1[zz]-x1;
7168 
7169         if ((cx1 <= x1) && (x1 <= cx2))
7170             nrx2[nn] = x1, nry2[nn] = nry1[z], nn++;
7171 
7172         if (x2 <= 0) x = cx2; else x = cx1;
7173         t = x-x1;
7174         if (((t-x2)^t) < 0)
7175             nrx2[nn] = x, nry2[nn] = nry1[z]+scale(t,nry1[zz]-nry1[z],x2), nn++;
7176 
7177         if (x2 <= 0) x = cx1; else x = cx2;
7178         t = x-x1;
7179         if (((t-x2)^t) < 0)
7180             nrx2[nn] = x, nry2[nn] = nry1[z]+scale(t,nry1[zz]-nry1[z],x2), nn++;
7181 
7182         z = zz;
7183     }
7184     while (z != 0);
7185     if (nn < 3) return 0;
7186 
7187     n = 0; z = 0;
7188     do
7189     {
7190         zz = z+1; if (zz == nn) zz = 0;
7191         y1 = nry2[z]; y2 = nry2[zz]-y1;
7192 
7193         if ((cy1 <= y1) && (y1 <= cy2))
7194             nry1[n] = y1, nrx1[n] = nrx2[z], n++;
7195 
7196         if (y2 <= 0) y = cy2; else y = cy1;
7197         t = y-y1;
7198         if (((t-y2)^t) < 0)
7199             nry1[n] = y, nrx1[n] = nrx2[z]+scale(t,nrx2[zz]-nrx2[z],y2), n++;
7200 
7201         if (y2 <= 0) y = cy1; else y = cy2;
7202         t = y-y1;
7203         if (((t-y2)^t) < 0)
7204             nry1[n] = y, nrx1[n] = nrx2[z]+scale(t,nrx2[zz]-nrx2[z],y2), n++;
7205 
7206         z = zz;
7207     }
7208     while (z != 0);
7209     return n;
7210 }
7211 
7212 
7213 // INTERNAL helper function for classic/polymost dorotatesprite
7214 //  sxptr, sxptr, z: in/out
7215 //  ret_yxaspect, ret_xyaspect: out
dorotspr_handle_bit2(int32_t * sxptr,int32_t * syptr,int32_t * z,int32_t dastat,int32_t cx1_plus_cx2,int32_t cy1_plus_cy2,int32_t * ret_yxaspect,int32_t * ret_xyaspect)7216 void dorotspr_handle_bit2(int32_t *sxptr, int32_t *syptr, int32_t *z, int32_t dastat,
7217                           int32_t cx1_plus_cx2, int32_t cy1_plus_cy2,
7218                           int32_t *ret_yxaspect, int32_t *ret_xyaspect)
7219 {
7220     if ((dastat & RS_AUTO) == 0)
7221     {
7222         if (!(dastat & RS_STRETCH) && 4*ydim <= 3*xdim)
7223         {
7224             *ret_yxaspect = (12<<16)/10;
7225             *ret_xyaspect = (10<<16)/12;
7226         }
7227         else
7228         {
7229             *ret_yxaspect = yxaspect;
7230             *ret_xyaspect = xyaspect;
7231         }
7232 
7233         // *sxptr and *syptr and *z are left unchanged
7234 
7235         return;
7236     }
7237     else
7238     {
7239         // dastat&2: Auto window size scaling
7240         const int32_t oxdim = xdim;
7241         const int32_t oydim = ydim;
7242         int32_t xdim = oxdim;  // SHADOWS global
7243         int32_t ydim = oydim;
7244 
7245         int32_t zoomsc, sx=*sxptr, sy=*syptr;
7246         int32_t ouryxaspect = yxaspect, ourxyaspect = xyaspect;
7247 
7248         sy += rotatesprite_y_offset;
7249 
7250         if (!(dastat & RS_STRETCH) && 4*ydim <= 3*xdim)
7251         {
7252             if ((dastat & RS_ALIGN_MASK) && (dastat & RS_ALIGN_MASK) != RS_ALIGN_MASK)
7253                 sx += NEGATE_ON_CONDITION(scale(120<<16,xdim,ydim) - (160<<16), !(dastat & RS_ALIGN_R));
7254 
7255             if ((dastat & RS_ALIGN_MASK) == RS_ALIGN_MASK)
7256                 ydim = scale(xdim, 3, 4);
7257             else
7258                 xdim = scale(ydim, 4, 3);
7259 
7260             ouryxaspect = (12<<16)/10;
7261             ourxyaspect = (10<<16)/12;
7262         }
7263 
7264         ouryxaspect = mulscale16(ouryxaspect, rotatesprite_yxaspect);
7265         ourxyaspect = divscale16(ourxyaspect, rotatesprite_yxaspect);
7266 
7267         // screen center to s[xy], 320<<16 coords.
7268         const int32_t normxofs = sx-(320<<15), normyofs = sy-(200<<15);
7269 
7270         // nasty hacks go here
7271         if (!(dastat & RS_NOCLIP))
7272         {
7273             const int32_t twice_midcx = cx1_plus_cx2+2;
7274 
7275             // screen x center to sx1, scaled to viewport
7276             const int32_t scaledxofs = scale(normxofs, scale(xdimen, xdim, oxdim), 320);
7277 
7278             sx = ((twice_midcx)<<15) + scaledxofs;
7279 
7280             zoomsc = xdimenscale;   //= scale(xdimen,yxaspect,320);
7281             zoomsc = mulscale16(zoomsc, rotatesprite_yxaspect);
7282 
7283             if ((dastat & RS_ALIGN_MASK) == RS_ALIGN_MASK)
7284                 zoomsc = scale(zoomsc, ydim, oydim);
7285 
7286             sy = ((cy1_plus_cy2+2)<<15) + mulscale16(normyofs, zoomsc);
7287         }
7288         else
7289         {
7290             //If not clipping to startmosts, & auto-scaling on, as a
7291             //hard-coded bonus, scale to full screen instead
7292 
7293             sx = (xdim<<15)+32768 + scale(normxofs,xdim,320);
7294 
7295             zoomsc = scale(xdim, ouryxaspect, 320);
7296             sy = (ydim<<15)+32768 + mulscale16(normyofs, zoomsc);
7297 
7298             if ((dastat & RS_ALIGN_MASK) == RS_ALIGN_MASK)
7299                 sy += (oydim-ydim)<<15;
7300             else
7301                 sx += (oxdim-xdim)<<15;
7302 
7303             if (dastat & RS_CENTERORIGIN)
7304                 sx += oxdim<<15;
7305         }
7306 
7307         *sxptr = sx;
7308         *syptr = sy;
7309         *z = mulscale16(*z, zoomsc);
7310 
7311         *ret_yxaspect = ouryxaspect;
7312         *ret_xyaspect = ourxyaspect;
7313     }
7314 }
7315 
7316 
7317 //
7318 // dorotatesprite (internal)
7319 //
7320 //JBF 20031206: Thanks to Ken's hunting, s/(rx1|ry1|rx2|ry2)/n\1/ in this function
dorotatesprite(int32_t sx,int32_t sy,int32_t z,int16_t a,int16_t picnum,int8_t dashade,char dapalnum,int32_t dastat,uint8_t daalpha,uint8_t dablend,int32_t cx1,int32_t cy1,int32_t cx2,int32_t cy2,int32_t uniqid)7321 static void dorotatesprite(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum,
7322                            int8_t dashade, char dapalnum, int32_t dastat, uint8_t daalpha, uint8_t dablend,
7323                            int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2,
7324                            int32_t uniqid)
7325 {
7326     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
7327 
7328     // NOTE: if these are made unsigned (for safety), angled tiles may draw
7329     // incorrectly, showing vertical seams at intervals.
7330     int32_t bx, by;
7331 
7332     int32_t cosang, sinang, v, nextv, dax1, dax2, oy;
7333     int32_t i, x, y, x1, y1, x2, y2, gx1, gy1;
7334     intptr_t p, bufplc, palookupoffs;
7335     int32_t xsiz, ysiz, xoff, yoff, npoints, yplc, yinc, lx, rx;
7336     int32_t xv, yv, xv2, yv2;
7337 
7338     int32_t ouryxaspect, ourxyaspect;
7339 
7340     if (g_rotatespriteNoWidescreen)
7341     {
7342         dastat |= RS_STRETCH;
7343         dastat &= ~RS_ALIGN_MASK;
7344     }
7345 
7346     //============================================================================= //POLYMOST BEGINS
7347 #ifdef USE_OPENGL
7348     if (videoGetRenderMode() >= REND_POLYMOST && in3dmode())
7349     {
7350         polymost_dorotatesprite(sx,sy,z,a,picnum,dashade,dapalnum,dastat,daalpha,dablend,cx1,cy1,cx2,cy2,uniqid);
7351         return;
7352     }
7353 #else
7354     UNREFERENCED_PARAMETER(uniqid);
7355 #endif
7356     //============================================================================= //POLYMOST ENDS
7357 
7358     // bound clipping rectangle to screen
7359     if (cx1 < 0) cx1 = 0;
7360     else if (cx1 > xdim-1) cx1 = xdim-1;
7361     if (cy1 < 0) cy1 = 0;
7362     else if (cy1 > ydim-1) cy1 = ydim-1;
7363     if (cx2 < 0) cx2 = 0;
7364     else if (cx2 > xdim-1) cx2 = xdim-1;
7365     if (cy2 < 0) cy2 = 0;
7366     else if (cy2 > ydim-1) cy2 = ydim-1;
7367 
7368     xsiz = tilesiz[picnum].x;
7369     ysiz = tilesiz[picnum].y;
7370 
7371     if (dastat & RS_TOPLEFT)
7372     {
7373         // Bit 1<<4 set: origin is top left corner?
7374         xoff = 0;
7375         yoff = 0;
7376     }
7377     else
7378     {
7379         // Bit 1<<4 clear: origin is center of tile, and per-tile offset is applied.
7380         // TODO: split the two?
7381         xoff = picanm[picnum].xofs + (xsiz>>1);
7382         yoff = picanm[picnum].yofs + (ysiz>>1);
7383     }
7384 
7385     // Bit 1<<2: invert y
7386     if (dastat & RS_YFLIP)
7387         yoff = ysiz-yoff;
7388 
7389     cosang = sintable[(a+512)&2047];
7390     sinang = sintable[a&2047];
7391 
7392     dorotspr_handle_bit2(&sx, &sy, &z, dastat, cx1+cx2, cy1+cy2, &ouryxaspect, &ourxyaspect);
7393 
7394     xv = mulscale14(cosang,z);
7395     yv = mulscale14(sinang,z);
7396     if ((dastat&RS_AUTO) || (dastat&RS_NOCLIP) == 0) //Don't aspect unscaled perms
7397     {
7398         xv2 = mulscale16(xv,ourxyaspect);
7399         yv2 = mulscale16(yv,ourxyaspect);
7400     }
7401     else
7402     {
7403         xv2 = xv;
7404         yv2 = yv;
7405     }
7406 
7407     nry1[0] = sy - (yv*xoff + xv*yoff);
7408     nry1[1] = nry1[0] + yv*xsiz;
7409     nry1[3] = nry1[0] + xv*ysiz;
7410     nry1[2] = nry1[1]+nry1[3]-nry1[0];
7411     i = (cy1<<16); if ((nry1[0]<i) && (nry1[1]<i) && (nry1[2]<i) && (nry1[3]<i)) return;
7412     i = (cy2<<16); if ((nry1[0]>i) && (nry1[1]>i) && (nry1[2]>i) && (nry1[3]>i)) return;
7413 
7414     nrx1[0] = sx - (xv2*xoff - yv2*yoff);
7415     nrx1[1] = nrx1[0] + xv2*xsiz;
7416     nrx1[3] = nrx1[0] - yv2*ysiz;
7417     nrx1[2] = nrx1[1]+nrx1[3]-nrx1[0];
7418     i = (cx1<<16); if ((nrx1[0]<i) && (nrx1[1]<i) && (nrx1[2]<i) && (nrx1[3]<i)) return;
7419     i = (cx2<<16); if ((nrx1[0]>i) && (nrx1[1]>i) && (nrx1[2]>i) && (nrx1[3]>i)) return;
7420 
7421     gx1 = nrx1[0]; gy1 = nry1[0];   //back up these before clipping
7422 
7423     npoints = clippoly4(cx1<<16,cy1<<16,(cx2+1)<<16,(cy2+1)<<16);
7424     if (npoints < 3) return;
7425 
7426     lx = nrx1[0]; rx = nrx1[0];
7427 
7428     nextv = 0;
7429     for (v=npoints-1; v>=0; v--)
7430     {
7431         x1 = nrx1[v]; x2 = nrx1[nextv];
7432         dax1 = (x1>>16); if (x1 < lx) lx = x1;
7433         dax2 = (x2>>16); if (x1 > rx) rx = x1;
7434         if (dax1 != dax2)
7435         {
7436             y1 = nry1[v]; y2 = nry1[nextv];
7437             yinc = divscale16(y2-y1,x2-x1);
7438             if (dax2 > dax1)
7439             {
7440                 yplc = y1 + mulscale16((dax1<<16)+65535-x1,yinc);
7441                 // Assertion fails with DNF mod: in mapster32,
7442                 // set dt_t 3864  (bike HUD, 700x220)
7443                 // set dt_a 100
7444                 // set dt_z 1280000  <- CRASH!
7445                 Bassert((unsigned)dax1 < MAXXDIM && (unsigned)dax2 < MAXXDIM+1);
7446                 qinterpolatedown16short((intptr_t)&uplc[dax1], dax2-dax1, yplc, yinc);
7447             }
7448             else
7449             {
7450                 yplc = y2 + mulscale16((dax2<<16)+65535-x2,yinc);
7451                 Bassert((unsigned)dax2 < MAXXDIM && (unsigned)dax1 < MAXXDIM+1);
7452                 qinterpolatedown16short((intptr_t)&dplc[dax2], dax1-dax2, yplc, yinc);
7453             }
7454         }
7455         nextv = v;
7456     }
7457 
7458     if (waloff[picnum] == 0) tileLoad(picnum);
7459     setgotpic(picnum);
7460     bufplc = waloff[picnum];
7461 
7462     if (palookup[dapalnum] == NULL) dapalnum = 0;
7463     palookupoffs = FP_OFF(palookup[dapalnum]) + (getpalookup(0, dashade)<<8);
7464 
7465     // Alpha handling
7466     if (!(dastat&RS_TRANS1) && daalpha > 0)
7467     {
7468         if (daalpha == 255)
7469             return;
7470 
7471         if (numalphatabs != 0)
7472         {
7473             if (falpha_to_blend((float)daalpha / 255.0f, &dastat, &dablend, RS_TRANS1, RS_TRANS2))
7474                 return;
7475         }
7476         else if (daalpha > 84)
7477         {
7478             dastat |= RS_TRANS1;
7479 
7480             if (daalpha > 168)
7481                 dastat |= RS_TRANS2;
7482             else
7483                 dastat &= ~RS_TRANS2;
7484 
7485             // Blood's transparency table is inverted
7486             if (bloodhack)
7487                 dastat ^= RS_TRANS2;
7488         }
7489     }
7490 
7491     i = divscale32(1L,z);
7492     xv = mulscale14(sinang,i);
7493     yv = mulscale14(cosang,i);
7494     if ((dastat&RS_AUTO) || (dastat&RS_NOCLIP)==0) //Don't aspect unscaled perms
7495     {
7496         yv2 = mulscale16(-xv,ouryxaspect);
7497         xv2 = mulscale16(yv,ouryxaspect);
7498     }
7499     else
7500     {
7501         yv2 = -xv;
7502         xv2 = yv;
7503     }
7504 
7505     x1 = (lx>>16);
7506     x2 = (rx>>16);
7507 
7508     oy = 0;
7509     x = (x1<<16)-1-gx1;
7510     y = (oy<<16)+65535-gy1;
7511     bx = dmulscale16(x,xv2,y,xv);
7512     by = dmulscale16(x,yv2,y,yv);
7513 
7514     if (dastat & RS_YFLIP)
7515     {
7516         yv = -yv;
7517         yv2 = -yv2;
7518         by = (ysiz<<16)-1-by;
7519     }
7520 
7521 #if defined ENGINE_USING_A_C
7522     if ((dastat&RS_TRANS1)==0 && ((a&1023) == 0) && (ysiz <= 256))  //vlineasm4 has 256 high limit!
7523 #else
7524     if ((dastat&RS_TRANS1) == 0)
7525 #endif
7526     {
7527         int32_t y1ve[4], y2ve[4], u4, d4;
7528 
7529         if (((a&1023) == 0) && (ysiz <= 256))  //vlineasm4 has 256 high limit!
7530         {
7531             if (dastat & RS_NOMASK)
7532                 setupvlineasm(24L);
7533             else
7534                 setupmvlineasm(24L, 0);
7535 
7536             by <<= 8; yv <<= 8; yv2 <<= 8;
7537 
7538             palookupoffse[0] = palookupoffse[1] = palookupoffse[2] = palookupoffse[3] = palookupoffs;
7539             vince[0] = vince[1] = vince[2] = vince[3] = yv;
7540 
7541             for (x=x1; x<x2; x+=4)
7542             {
7543                 char bad;
7544                 int32_t xx, xend;
7545 
7546                 bad = 15; xend = min(x2-x,4);
7547                 for (xx=0; xx<xend; xx++)
7548                 {
7549                     bx += xv2;
7550 
7551                     y1 = uplc[x+xx]; y2 = dplc[x+xx];
7552                     if ((dastat & RS_NOCLIP) == 0)
7553                     {
7554                         if (startumost[x+xx] > y1) y1 = startumost[x+xx];
7555                         if (startdmost[x+xx] < y2) y2 = startdmost[x+xx];
7556                     }
7557                     if (y2 <= y1) continue;
7558 
7559                     by += (uint32_t)yv*(y1-oy); oy = y1;
7560 
7561                     // Assertion would fail with DNF mod without (uint32_t) below: in mapster32,
7562                     // set dt_t 3864  (bike HUD, 700x220)
7563                     // set dt_z 16777216
7564                     // <Increase yxaspect by pressing [9]>  <-- CRASH!
7565                     // (It also fails when wrecking the bike in-game by driving into a wall.)
7566 //                    Bassert(bx >= 0);
7567 
7568                     bufplce[xx] = ((uint32_t)bx>>16)*ysiz+bufplc;
7569                     vplce[xx] = by;
7570                     y1ve[xx] = y1;
7571                     y2ve[xx] = y2-1;
7572                     bad &= ~pow2char[xx];
7573                 }
7574 
7575                 p = x+frameplace;
7576 
7577                 u4 = INT32_MIN;
7578                 d4 = INT32_MAX;
7579                 for (xx=0; xx<4; xx++)
7580                     if (!(bad&pow2char[xx]))
7581                     {
7582                         u4 = max(u4, y1ve[xx]);
7583                         d4 = min(d4, y2ve[xx]);
7584                     }
7585                 // This version may access uninitialized y?ve[] values with
7586                 // thin tiles, e.g. 3085 (MINIFONT period, 1x5):
7587 //                u4 = max(max(y1ve[0],y1ve[1]),max(y1ve[2],y1ve[3]));
7588 //                d4 = min(min(y2ve[0],y2ve[1]),min(y2ve[2],y2ve[3]));
7589 
7590                 if (dastat & RS_NOMASK)
7591                 {
7592                     if ((bad != 0) || (u4 >= d4))
7593                     {
7594                         if (!(bad&1)) prevlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0],vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
7595                         if (!(bad&2)) prevlineasm1(vince[1],palookupoffse[1],y2ve[1]-y1ve[1],vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
7596                         if (!(bad&4)) prevlineasm1(vince[2],palookupoffse[2],y2ve[2]-y1ve[2],vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
7597                         if (!(bad&8)) prevlineasm1(vince[3],palookupoffse[3],y2ve[3]-y1ve[3],vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
7598                         continue;
7599                     }
7600 
7601                     if (u4 > y1ve[0]) vplce[0] = prevlineasm1(vince[0],palookupoffse[0],u4-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
7602                     if (u4 > y1ve[1]) vplce[1] = prevlineasm1(vince[1],palookupoffse[1],u4-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
7603                     if (u4 > y1ve[2]) vplce[2] = prevlineasm1(vince[2],palookupoffse[2],u4-y1ve[2]-1,vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
7604                     if (u4 > y1ve[3]) vplce[3] = prevlineasm1(vince[3],palookupoffse[3],u4-y1ve[3]-1,vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
7605 
7606                     if (d4 >= u4) vlineasm4(d4-u4+1, (char *)(ylookup[u4]+p));
7607 
7608                     intptr_t i = p+ylookup[d4+1];
7609                     if (y2ve[0] > d4) prevlineasm1(vince[0],palookupoffse[0],y2ve[0]-d4-1,vplce[0],bufplce[0],i+0);
7610                     if (y2ve[1] > d4) prevlineasm1(vince[1],palookupoffse[1],y2ve[1]-d4-1,vplce[1],bufplce[1],i+1);
7611                     if (y2ve[2] > d4) prevlineasm1(vince[2],palookupoffse[2],y2ve[2]-d4-1,vplce[2],bufplce[2],i+2);
7612                     if (y2ve[3] > d4) prevlineasm1(vince[3],palookupoffse[3],y2ve[3]-d4-1,vplce[3],bufplce[3],i+3);
7613                 }
7614                 else
7615                 {
7616                     if ((bad != 0) || (u4 >= d4))
7617                     {
7618                         if (!(bad&1)) mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0],vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
7619                         if (!(bad&2)) mvlineasm1(vince[1],palookupoffse[1],y2ve[1]-y1ve[1],vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
7620                         if (!(bad&4)) mvlineasm1(vince[2],palookupoffse[2],y2ve[2]-y1ve[2],vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
7621                         if (!(bad&8)) mvlineasm1(vince[3],palookupoffse[3],y2ve[3]-y1ve[3],vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
7622                         continue;
7623                     }
7624 
7625                     if (u4 > y1ve[0]) vplce[0] = mvlineasm1(vince[0],palookupoffse[0],u4-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
7626                     if (u4 > y1ve[1]) vplce[1] = mvlineasm1(vince[1],palookupoffse[1],u4-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
7627                     if (u4 > y1ve[2]) vplce[2] = mvlineasm1(vince[2],palookupoffse[2],u4-y1ve[2]-1,vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
7628                     if (u4 > y1ve[3]) vplce[3] = mvlineasm1(vince[3],palookupoffse[3],u4-y1ve[3]-1,vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
7629 
7630                     if (d4 >= u4) mvlineasm4(d4-u4+1, (char *)(ylookup[u4]+p));
7631 
7632                     intptr_t i = p+ylookup[d4+1];
7633                     if (y2ve[0] > d4) mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-d4-1,vplce[0],bufplce[0],i+0);
7634                     if (y2ve[1] > d4) mvlineasm1(vince[1],palookupoffse[1],y2ve[1]-d4-1,vplce[1],bufplce[1],i+1);
7635                     if (y2ve[2] > d4) mvlineasm1(vince[2],palookupoffse[2],y2ve[2]-d4-1,vplce[2],bufplce[2],i+2);
7636                     if (y2ve[3] > d4) mvlineasm1(vince[3],palookupoffse[3],y2ve[3]-d4-1,vplce[3],bufplce[3],i+3);
7637                 }
7638 
7639                 faketimerhandler();
7640             }
7641         }
7642 #ifndef ENGINE_USING_A_C
7643         else
7644         {
7645             int32_t ny1, ny2;
7646             int32_t qlinemode = 0;
7647 
7648             if (dastat & RS_NOMASK)
7649             {
7650                 if ((xv2&0x0000ffff) == 0)
7651                 {
7652                     qlinemode = 1;
7653                     setupqrhlineasm4(0L,yv2<<16,(xv2>>16)*ysiz+(yv2>>16),palookupoffs,0L,0L);
7654                 }
7655                 else
7656                 {
7657                     qlinemode = 0;
7658                     setuprhlineasm4(xv2<<16,yv2<<16,(xv2>>16)*ysiz+(yv2>>16),palookupoffs,ysiz,0L);
7659                 }
7660             }
7661             else
7662                 setuprmhlineasm4(xv2<<16,yv2<<16,(xv2>>16)*ysiz+(yv2>>16),palookupoffs,ysiz,0L);
7663 
7664             y1 = uplc[x1];
7665             if (((dastat & RS_NOCLIP) == 0) && startumost[x1] > y1)
7666                 y1 = startumost[x1];
7667             y2 = y1;
7668             for (x=x1; x<x2; x++)
7669             {
7670                 ny1 = uplc[x]-1; ny2 = dplc[x];
7671                 if ((dastat & RS_NOCLIP) == 0)
7672                 {
7673                     if (startumost[x]-1 > ny1) ny1 = startumost[x]-1;
7674                     if (startdmost[x] < ny2) ny2 = startdmost[x];
7675                 }
7676 
7677                 if (ny1 < ny2-1)
7678                 {
7679                     if (ny1 >= y2)
7680                     {
7681                         while (y1 < y2-1)
7682                         {
7683                             y1++; if ((y1&31) == 0) faketimerhandler();
7684 
7685                             //x,y1
7686                             bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1;
7687                             if (dastat & RS_NOMASK)
7688                             {
7689                                 if (qlinemode) qrhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y1]+x+frameplace);
7690                                 else rhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
7691                             }
7692                             else rmhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
7693                         }
7694                         y1 = ny1;
7695                     }
7696                     else
7697                     {
7698                         while (y1 < ny1)
7699                         {
7700                             y1++; if ((y1&31) == 0) faketimerhandler();
7701 
7702                             //x,y1
7703                             bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1;
7704                             if (dastat & RS_NOMASK)
7705                             {
7706                                 if (qlinemode) qrhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y1]+x+frameplace);
7707                                 else rhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
7708                             }
7709                             else rmhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
7710                         }
7711 
7712                         while (y1 > ny1) lastx[y1--] = x;
7713                     }
7714 
7715                     while (y2 > ny2)
7716                     {
7717                         y2--; if ((y2&31) == 0) faketimerhandler();
7718 
7719                         //x,y2
7720                         bx += xv*(y2-oy); by += yv*(y2-oy); oy = y2;
7721                         if (dastat & RS_NOMASK)
7722                         {
7723                             if (qlinemode) qrhlineasm4(x-lastx[y2],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y2]+x+frameplace);
7724                             else rhlineasm4(x-lastx[y2],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y2]+x+frameplace);
7725                         }
7726                         else rmhlineasm4(x-lastx[y2],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y2]+x+frameplace);
7727                     }
7728 
7729                     while (y2 < ny2) lastx[y2++] = x;
7730                 }
7731                 else
7732                 {
7733                     while (y1 < y2-1)
7734                     {
7735                         y1++; if ((y1&31) == 0) faketimerhandler();
7736 
7737                         //x,y1
7738                         bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1;
7739                         if (dastat & RS_NOMASK)
7740                         {
7741                             if (qlinemode) qrhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y1]+x+frameplace);
7742                             else rhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
7743                         }
7744                         else rmhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
7745                     }
7746 
7747                     if (x == x2-1) { bx += xv2; by += yv2; break; }
7748                     y1 = uplc[x+1];
7749                     if (((dastat & RS_NOCLIP) == 0) && startumost[x+1] > y1)
7750                         y1 = startumost[x+1];
7751                     y2 = y1;
7752                 }
7753                 bx += xv2; by += yv2;
7754             }
7755 
7756             while (y1 < y2-1)
7757             {
7758                 y1++; if ((y1&31) == 0) faketimerhandler();
7759 
7760                 //x2,y1
7761                 bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1;
7762                 if (dastat & RS_NOMASK)
7763                 {
7764                     if (qlinemode) qrhlineasm4(x2-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L,by<<16,ylookup[y1]+x2+frameplace);
7765                     else rhlineasm4(x2-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x2+frameplace);
7766                 }
7767                 else rmhlineasm4(x2-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x2+frameplace);
7768             }
7769         }
7770 #endif  // !defined ENGINE_USING_A_C
7771     }
7772     else
7773     {
7774         if ((dastat & RS_TRANS1) == 0)
7775         {
7776 #if !defined ENGINE_USING_A_C
7777             if (dastat & RS_NOMASK)
7778                 setupspritevline(palookupoffs,(xv>>16)*ysiz,xv<<16,ysiz,yv,0L);
7779             else
7780                 msetupspritevline(palookupoffs,(xv>>16)*ysiz,xv<<16,ysiz,yv,0L);
7781 #else
7782             if (dastat & RS_NOMASK)
7783                 setupspritevline(palookupoffs,xv,yv,ysiz);
7784             else
7785                 msetupspritevline(palookupoffs,xv,yv,ysiz);
7786 #endif
7787         }
7788         else
7789         {
7790 #if !defined ENGINE_USING_A_C
7791             tsetupspritevline(palookupoffs,(xv>>16)*ysiz,xv<<16,ysiz,yv,0L);
7792 #else
7793             tsetupspritevline(palookupoffs,xv,yv,ysiz);
7794 #endif
7795             setup_blend(dablend, dastat & RS_TRANS2);
7796         }
7797 
7798         for (x=x1; x<x2; x++)
7799         {
7800             bx += xv2; by += yv2;
7801 
7802             y1 = uplc[x]; y2 = dplc[x];
7803             if ((dastat & RS_NOCLIP) == 0)
7804             {
7805                 if (startumost[x] > y1) y1 = startumost[x];
7806                 if (startdmost[x] < y2) y2 = startdmost[x];
7807             }
7808             if (y2 <= y1) continue;
7809 
7810             switch (y1-oy)
7811             {
7812             case -1:
7813                 bx -= xv; by -= yv; oy = y1; break;
7814             case 0:
7815                 break;
7816             case 1:
7817                 bx += xv; by += yv; oy = y1; break;
7818             default:
7819                 bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1; break;
7820             }
7821 
7822             p = ylookup[y1]+x+frameplace;
7823 
7824             if ((dastat & RS_TRANS1) == 0)
7825             {
7826 #if !defined ENGINE_USING_A_C
7827                 if (dastat & RS_NOMASK)
7828                     spritevline(0L,by<<16,y2-y1+1,bx<<16,(bx>>16)*ysiz+(by>>16)+bufplc,p);
7829                 else
7830                     mspritevline(0L,by<<16,y2-y1+1,bx<<16,(bx>>16)*ysiz+(by>>16)+bufplc,p);
7831 #else
7832                 if (dastat & RS_NOMASK)
7833                     spritevline(bx&65535,by&65535,y2-y1+1,(bx>>16)*ysiz+(by>>16)+bufplc,p);
7834                 else
7835                     mspritevline(bx&65535,by&65535,y2-y1+1,(bx>>16)*ysiz+(by>>16)+bufplc,p);
7836 #endif
7837             }
7838             else
7839             {
7840 #if !defined ENGINE_USING_A_C
7841                 tspritevline(0L,by<<16,y2-y1+1,bx<<16,(bx>>16)*ysiz+(by>>16)+bufplc,p);
7842 #else
7843                 tspritevline(bx&65535,by&65535,y2-y1+1,(bx>>16)*ysiz+(by>>16)+bufplc,p);
7844                 //transarea += (y2-y1);
7845 #endif
7846             }
7847 
7848             faketimerhandler();
7849         }
7850     }
7851 
7852     /*  if ((dastat & RS_PERM) && (origbuffermode == 0))
7853         {
7854             buffermode = obuffermode;
7855             setactivepage(activepage);
7856         }*/
7857 }
7858 
msqrtasm(uint32_t c)7859 static uint32_t msqrtasm(uint32_t c)
7860 {
7861     uint32_t a = 0x40000000l, b = 0x20000000l;
7862 
7863     do
7864     {
7865         if (c >= a)
7866         {
7867             c -= a;
7868             a += b*4;
7869         }
7870         a -= b;
7871         a >>= 1;
7872         b >>= 2;
7873     } while (b);
7874 
7875     if (c >= a)
7876         a++;
7877 
7878     return a >> 1;
7879 }
7880 
7881 //
7882 // initksqrt (internal)
7883 //
initksqrt(void)7884 static inline void initksqrt(void)
7885 {
7886     int32_t i, j, k;
7887     uint32_t root, num;
7888     int32_t temp;
7889 
7890     j = 1; k = 0;
7891     for (i=0; i<4096; i++)
7892     {
7893         if (i >= j) { j <<= 2; k++; }
7894         sqrtable[i] = (uint16_t)(msqrtasm((i<<18)+131072)<<1);
7895         shlookup[i] = (k<<1)+((10-k)<<8);
7896         if (i < 256) shlookup[i+4096] = ((k+6)<<1)+((10-(k+6))<<8);
7897     }
7898 
7899     for(i=0;i<2048;i++)
7900     {
7901         root = 128;
7902         num = i<<20;
7903         do
7904         {
7905             temp = root;
7906             root = (root+num/root)>>1;
7907         } while((temp-root+1) > 2);
7908         temp = root*root-num;
7909         while (klabs(int32_t(temp-2*root+1)) < klabs(temp))
7910         {
7911             temp += -(2*root)+1;
7912             root--;
7913         }
7914         while (klabs(int32_t(temp+2*root+1)) < klabs(temp))
7915         {
7916             temp += 2*root+1;
7917             root++;
7918         }
7919         sqrtable_old[i] = root;
7920     }
7921 }
7922 
7923 
7924 //
7925 // dosetaspect
7926 //
dosetaspect(void)7927 static void dosetaspect(void)
7928 {
7929     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
7930 
7931     int32_t i, j;
7932 
7933     if (xyaspect != oxyaspect)
7934     {
7935         oxyaspect = xyaspect;
7936         j = xyaspect*320;
7937         horizycent = (ydim*4)>>1;
7938         horizlookup2[horizycent-1] = divscale32(131072,j);
7939 
7940         for (i=0; i < horizycent-1; i++)
7941         {
7942             horizlookup[i] = divscale28(1, i-(horizycent-1));
7943             horizlookup2[i] = divscale20(klabs(horizlookup[i]), j);
7944         }
7945 
7946         for (i=horizycent; i < ydim*4-1; i++)
7947         {
7948             horizlookup[i] = divscale28(1, i-(horizycent-1));
7949             horizlookup2[i] = divscale20(klabs(horizlookup[i]), j);
7950         }
7951     }
7952 
7953     if (xdimen != oxdimen || viewingrange != oviewingrange)
7954     {
7955         int32_t k, x, xinc;
7956 
7957         no_radarang2 = 0;
7958         oviewingrange = viewingrange;
7959 
7960         xinc = mulscale32(viewingrange*2560,xdimenrecip);
7961         x = (5120<<16)-mulscale1(xinc,xdimen);
7962 
7963         for (i=0; i<xdimen; i++)
7964         {
7965             j = (x&65535); k = (x>>16); x += xinc;
7966 
7967             if (k < 0 || k >= (int32_t)ARRAY_SIZE(qradarang)-1)
7968             {
7969                 no_radarang2 = 1;
7970 #ifdef DEBUGGINGAIDS
7971                 if (editstatus)
7972                     initprintf("no rad2\n");
7973 #endif
7974                 break;
7975             }
7976 
7977             if (j != 0)
7978                 j = mulscale16(qradarang[k+1]-qradarang[k], j);
7979             radarang2[i] = ((qradarang[k]+j)>>6);
7980         }
7981 
7982         if (xdimen != oxdimen)
7983         {
7984             distrecip = NULL;
7985             for (i = 0; i < DISTRECIPCACHESIZE; i++)
7986             {
7987                 if (distrecipcache[i].xdimen == xdimen)
7988                     distrecip = distrecipcache[i].distrecip;
7989             }
7990             if (distrecip == NULL)
7991             {
7992                 int32_t minAge = 0;
7993                 for (i = 1; i < DISTRECIPCACHESIZE; i++)
7994                 {
7995                     if (distrecipcache[i].age < distrecipcache[minAge].age)
7996                         minAge = i;
7997                 }
7998                 if (distrecipcache[minAge].distrecip == NULL)
7999                     distrecipcache[minAge].distrecip = (uint32_t *)Xaligned_alloc(16, DISTRECIPSIZ * sizeof(uint32_t));
8000 
8001                 distrecipcache[minAge].age = ++distrecipagecnt;
8002                 distrecipcache[minAge].xdimen = xdimen;
8003 
8004                 distrecip = distrecipcache[minAge].distrecip;
8005 
8006                 if (xdimen < 1 << 11)
8007                 {
8008                     for (i = 1; i < DISTRECIPSIZ; i++)
8009                         distrecip[i] = tabledivide32(xdimen << 20, i);
8010                 }
8011                 else
8012                 {
8013                     for (i = 1; i < DISTRECIPSIZ; i++)
8014                         distrecip[i] = tabledivide64((uint64_t)xdimen << 20, i);
8015                 }
8016             }
8017 
8018             nytooclose = xdimen*2100;
8019         }
8020 
8021         oxdimen = xdimen;
8022     }
8023 }
8024 
8025 
8026 //
8027 // loadtables (internal)
8028 //
calcbritable(void)8029 static inline void calcbritable(void)
8030 {
8031     int32_t i, j;
8032     float a, b;
8033 
8034     for (i=0; i<16; i++)
8035     {
8036         a = 8.f / ((float)i+8.f);
8037         b = 255.f / powf(255.f, a);
8038 
8039         for (j=0; j<256; j++) // JBF 20040207: full 8bit precision
8040             britable[i][j] = (uint8_t) (powf((float)j, a) * b);
8041     }
8042 }
8043 
engineLoadTables(void)8044 static int32_t engineLoadTables(void)
8045 {
8046     static char tablesloaded = 0;
8047 
8048     if (tablesloaded == 0)
8049     {
8050         int32_t i;
8051 
8052         initksqrt();
8053 
8054         for (i=0; i<2048; i++)
8055             reciptable[i] = divscale30(2048, i+2048);
8056 
8057         for (i=0; i<SLOPTABLESIZ; i++)
8058             sloptable[i] = krecipasm(i-HALFSLOPTABLESIZ);
8059 
8060         for (i=0; i<=512; i++)
8061             sintable[i] = (int16_t)(16384.f * sinf((float)i * BANG2RAD) + 0.0001f);
8062         for (i=513; i<1024; i++)
8063             sintable[i] = sintable[1024-i];
8064         for (i=1024; i<2048; i++)
8065             sintable[i] = -sintable[i-1024];
8066 
8067         for (i=0; i<640; i++)
8068             radarang[i] = (int16_t)(atanf(((float)(640-i)-0.5f) * (1.f/160.f)) * (-64.f * (1.f/BANG2RAD)) + 0.0001f);
8069         for (i=0; i<640; i++)
8070             radarang[1279-i] = -radarang[i];
8071 
8072         for (i=0; i<5120; i++)
8073             qradarang[i] = fix16_from_float(atanf(((float)(5120-i)-0.5f) * (1.f/1280.f)) * (-64.f * (1.f/BANG2RAD)));
8074         for (i=0; i<5120; i++)
8075             qradarang[10239-i] = -qradarang[i];
8076 
8077 #ifdef B_LITTLE_ENDIAN
8078         i = 0;
8079         if (Bcrc32((uint8_t *)sintable, sizeof(sintable), 0) != 0xee1e7aba)
8080             i |= 1;
8081         if (Bcrc32((uint8_t *)radarang, 640*sizeof(radarang[0]), 0) != 0xee893d92)
8082             i |= 2;
8083 
8084         if (i != 0)
8085         {
8086             static const char *str[3] = { "sine table", "arctangent table",
8087                                           "sine and arctangent tables" };
8088             initprintf("WARNING: Calculated %s differ%s from original!\n",
8089                        str[i-1], i==3 ? "" : "s");
8090         }
8091 #endif
8092         // TABLES.DAT format:
8093         //kread(fil,sintable,2048*2);
8094         //kread(fil,radarang,640*2);
8095         //kread(fil,textfont,1024);
8096         //kread(fil,smalltextfont,1024);
8097         //kread(fil,britable,1024);
8098 
8099         calcbritable();
8100 
8101         tablesloaded = 1;
8102     }
8103 
8104     return 0;
8105 }
8106 
8107 
8108 ////////// SPRITE LIST MANIPULATION FUNCTIONS //////////
8109 
8110 #ifdef NETCODE_DISABLE
8111 # define LISTFN_STATIC static
8112 #else
8113 # define LISTFN_STATIC
8114 #endif
8115 
8116 ///// sector lists of sprites /////
8117 
8118 // insert sprite at the head of sector list, change .sectnum
do_insertsprite_at_headofsect(int16_t spritenum,int16_t sectnum)8119 LISTFN_STATIC void do_insertsprite_at_headofsect(int16_t spritenum, int16_t sectnum)
8120 {
8121     int16_t const ohead = headspritesect[sectnum];
8122 
8123     prevspritesect[spritenum] = -1;
8124     nextspritesect[spritenum] = ohead;
8125     if (ohead >= 0)
8126         prevspritesect[ohead] = spritenum;
8127     headspritesect[sectnum] = spritenum;
8128 
8129     sprite[spritenum].sectnum = sectnum;
8130 }
8131 
8132 // remove sprite 'deleteme' from its sector list
do_deletespritesect(int16_t deleteme)8133 LISTFN_STATIC void do_deletespritesect(int16_t deleteme)
8134 {
8135     int32_t const sectnum = sprite[deleteme].sectnum;
8136     int32_t const prev = prevspritesect[deleteme];
8137     int32_t const next = nextspritesect[deleteme];
8138 
8139     if (headspritesect[sectnum] == deleteme)
8140         headspritesect[sectnum] = next;
8141     if (prev >= 0)
8142         nextspritesect[prev] = next;
8143     if (next >= 0)
8144         prevspritesect[next] = prev;
8145 }
8146 
8147 ///// now, status lists /////
8148 
8149 // insert sprite at head of status list, change .statnum
do_insertsprite_at_headofstat(int16_t spritenum,int16_t statnum)8150 LISTFN_STATIC void do_insertsprite_at_headofstat(int16_t spritenum, int16_t statnum)
8151 {
8152     int16_t const ohead = headspritestat[statnum];
8153 
8154     prevspritestat[spritenum] = -1;
8155     nextspritestat[spritenum] = ohead;
8156     if (ohead >= 0)
8157         prevspritestat[ohead] = spritenum;
8158     headspritestat[statnum] = spritenum;
8159 
8160     sprite[spritenum].statnum = statnum;
8161 }
8162 
8163 // insertspritestat (internal)
insertspritestat(int16_t statnum)8164 LISTFN_STATIC int32_t insertspritestat(int16_t statnum)
8165 {
8166     if ((statnum >= MAXSTATUS) || (headspritestat[MAXSTATUS] == -1))
8167         return -1;  //list full
8168 
8169     // remove one sprite from the statnum-freelist
8170     int16_t const blanktouse = headspritestat[MAXSTATUS];
8171     headspritestat[MAXSTATUS] = nextspritestat[blanktouse];
8172 
8173     // make back-link of the new freelist head point to nil
8174     if (headspritestat[MAXSTATUS] >= 0)
8175         prevspritestat[headspritestat[MAXSTATUS]] = -1;
8176     else if (enginecompatibilitymode == ENGINE_EDUKE32)
8177         tailspritefree = -1;
8178 
8179     do_insertsprite_at_headofstat(blanktouse, statnum);
8180 
8181     return blanktouse;
8182 }
8183 
8184 // remove sprite 'deleteme' from its status list
do_deletespritestat(int16_t deleteme)8185 LISTFN_STATIC void do_deletespritestat(int16_t deleteme)
8186 {
8187     int32_t const sectnum = sprite[deleteme].statnum;
8188     int32_t const prev = prevspritestat[deleteme];
8189     int32_t const next = nextspritestat[deleteme];
8190 
8191     if (headspritestat[sectnum] == deleteme)
8192         headspritestat[sectnum] = next;
8193     if (prev >= 0)
8194         nextspritestat[prev] = next;
8195     if (next >= 0)
8196         prevspritestat[next] = prev;
8197 }
8198 
8199 
8200 //
8201 // insertsprite
8202 //
8203 int32_t(*insertsprite_replace)(int16_t sectnum, int16_t statnum) = NULL;
insertsprite(int16_t sectnum,int16_t statnum)8204 int32_t insertsprite(int16_t sectnum, int16_t statnum)
8205 {
8206     if (insertsprite_replace)
8207         return insertsprite_replace(sectnum, statnum);
8208     // TODO: guard against bad sectnum?
8209     int32_t const newspritenum = insertspritestat(statnum);
8210 
8211     if (newspritenum >= 0)
8212     {
8213         Bassert((unsigned)sectnum < MAXSECTORS);
8214 
8215         do_insertsprite_at_headofsect(newspritenum, sectnum);
8216         Numsprites++;
8217     }
8218 
8219     return newspritenum;
8220 
8221 }
8222 
8223 //
8224 // deletesprite
8225 //
8226 int32_t (*deletesprite_replace)(int16_t spritenum) = NULL;
deletesprite(int16_t spritenum)8227 int32_t deletesprite(int16_t spritenum)
8228 {
8229     if (deletesprite_replace)
8230         return deletesprite_replace(spritenum);
8231     Bassert((sprite[spritenum].statnum == MAXSTATUS)
8232             == (sprite[spritenum].sectnum == MAXSECTORS));
8233 
8234     if (sprite[spritenum].statnum == MAXSTATUS)
8235         return -1;  // already not in the world
8236 
8237     do_deletespritestat(spritenum);
8238     do_deletespritesect(spritenum);
8239 
8240     // (dummy) insert at tail of sector freelist, compat
8241     // for code that checks .sectnum==MAXSECTOR
8242     sprite[spritenum].sectnum = MAXSECTORS;
8243 
8244     // insert at tail of status freelist
8245     if (enginecompatibilitymode != ENGINE_EDUKE32)
8246         do_insertsprite_at_headofstat(spritenum, MAXSTATUS);
8247     else
8248     {
8249         prevspritestat[spritenum] = tailspritefree;
8250         nextspritestat[spritenum] = -1;
8251         if (tailspritefree >= 0)
8252             nextspritestat[tailspritefree] = spritenum;
8253         else
8254             headspritestat[MAXSTATUS] = spritenum;
8255         sprite[spritenum].statnum = MAXSTATUS;
8256 
8257         tailspritefree = spritenum;
8258     }
8259     Numsprites--;
8260 
8261     return 0;
8262 }
8263 
8264 //
8265 // changespritesect
8266 //
8267 int32_t (*changespritesect_replace)(int16_t spritenum, int16_t newsectnum) = NULL;
changespritesect(int16_t spritenum,int16_t newsectnum)8268 int32_t changespritesect(int16_t spritenum, int16_t newsectnum)
8269 {
8270     if (changespritesect_replace)
8271         return changespritesect_replace(spritenum, newsectnum);
8272     // XXX: NOTE: MAXSECTORS is allowed
8273     if ((newsectnum < 0 || newsectnum > MAXSECTORS) || (sprite[spritenum].sectnum == MAXSECTORS))
8274         return -1;
8275 
8276     if (sprite[spritenum].sectnum == newsectnum)
8277         return 0;
8278 
8279     do_deletespritesect(spritenum);
8280     do_insertsprite_at_headofsect(spritenum, newsectnum);
8281 
8282     return 0;
8283 }
8284 
8285 //
8286 // changespritestat
8287 //
8288 int32_t (*changespritestat_replace)(int16_t spritenum, int16_t newstatnum) = NULL;
changespritestat(int16_t spritenum,int16_t newstatnum)8289 int32_t changespritestat(int16_t spritenum, int16_t newstatnum)
8290 {
8291     if (changespritestat_replace)
8292         return changespritestat_replace(spritenum, newstatnum);
8293     // XXX: NOTE: MAXSTATUS is allowed
8294     if ((newstatnum < 0 || newstatnum > MAXSTATUS) || (sprite[spritenum].statnum == MAXSTATUS))
8295         return -1;  // can't set the statnum of a sprite not in the world
8296 
8297     if (sprite[spritenum].statnum == newstatnum)
8298         return 0;  // sprite already has desired statnum
8299 
8300     do_deletespritestat(spritenum);
8301     do_insertsprite_at_headofstat(spritenum, newstatnum);
8302 
8303     return 0;
8304 }
8305 
8306 //
8307 // lintersect (internal)
8308 //
lintersect(const int32_t originX,const int32_t originY,const int32_t originZ,const int32_t destX,const int32_t destY,const int32_t destZ,const int32_t lineStartX,const int32_t lineStartY,const int32_t lineEndX,const int32_t lineEndY,int32_t * intersectionX,int32_t * intersectionY,int32_t * intersectionZ)8309 int32_t lintersect(const int32_t originX, const int32_t originY, const int32_t originZ,
8310                    const int32_t destX, const int32_t destY, const int32_t destZ,
8311                    const int32_t lineStartX, const int32_t lineStartY, const int32_t lineEndX, const int32_t lineEndY,
8312                    int32_t *intersectionX, int32_t *intersectionY, int32_t *intersectionZ)
8313 {
8314     const vec2_t ray = { destX-originX,
8315                          destY-originY };
8316     const vec2_t lineVec = { lineEndX-lineStartX,
8317                              lineEndY-lineStartY };
8318     const vec2_t originDiff = { lineStartX-originX,
8319                                 lineStartY-originY };
8320 
8321     const int32_t rayCrossLineVec = ray.x*lineVec.y - ray.y*lineVec.x;
8322     const int32_t originDiffCrossRay = originDiff.x*ray.y - originDiff.y*ray.x;
8323 
8324     if (rayCrossLineVec == 0)
8325     {
8326         if (originDiffCrossRay != 0 || enginecompatibilitymode != ENGINE_EDUKE32)
8327         {
8328             // line segments are parallel
8329             return 0;
8330         }
8331 
8332         // line segments are collinear
8333         const int32_t rayLengthSquared = ray.x*ray.x + ray.y*ray.y;
8334         const int32_t rayDotOriginDiff = ray.x*originDiff.x + ray.y*originDiff.y;
8335         const int32_t rayDotLineEndDiff = rayDotOriginDiff + ray.x*lineVec.x + ray.y*lineVec.y;
8336         int64_t t = min(rayDotOriginDiff, rayDotLineEndDiff);
8337         if (rayDotOriginDiff < 0)
8338         {
8339             if (rayDotLineEndDiff < 0)
8340                 return 0;
8341 
8342             t = 0;
8343         }
8344         else if (rayDotOriginDiff > rayLengthSquared)
8345         {
8346             if (rayDotLineEndDiff > rayLengthSquared)
8347                 return 0;
8348 
8349             t = rayDotLineEndDiff;
8350         }
8351         t = tabledivide64(t << 24L, rayLengthSquared);
8352 
8353         *intersectionX = originX + mulscale24(ray.x, t);
8354         *intersectionY = originY + mulscale24(ray.y, t);
8355         *intersectionZ = originZ + mulscale24(destZ-originZ, t);
8356 
8357         return 1;
8358     }
8359 
8360     const int32_t originDiffCrossLineVec = originDiff.x*lineVec.y - originDiff.y*lineVec.x;
8361     static const int32_t signBit = 1u<<31u;
8362     // Any point on either line can be expressed as p+t*r and q+u*s
8363     // The two line segments intersect when we can find a t & u such that p+t*r = q+u*s
8364     // If the point is outside of the bounds of the line segment, we know we don't have an intersection.
8365     // t is < 0 if (originDiffCrossLineVec^rayCrossLineVec) & signBit)
8366     // u is < 0 if (originDiffCrossRay^rayCrossLineVec) & signBit
8367     // t is > 1 if klabs(originDiffCrossLineVec) > klabs(rayCrossLineVec)
8368     // u is > 1 if klabs(originDiffCrossRay) > klabs(rayCrossLineVec)
8369     // where int32_t u = tabledivide64(((int64_t) originDiffCrossRay) << 24L, rayCrossLineVec);
8370     if (((originDiffCrossLineVec^rayCrossLineVec) & signBit) ||
8371         ((originDiffCrossRay^rayCrossLineVec) & signBit) ||
8372         klabs(originDiffCrossLineVec) > klabs(rayCrossLineVec) ||
8373         klabs(originDiffCrossRay) > klabs(rayCrossLineVec))
8374     {
8375         // line segments do not overlap
8376         return 0;
8377     }
8378 
8379     int64_t t = tabledivide64(((int64_t) originDiffCrossLineVec) << 24L, rayCrossLineVec);
8380     // For sake of completeness/readability, alternative to the above approach for an early out & avoidance of an extra division:
8381 #if 0
8382     int64_t u = tabledivide64(((int64_t) originDiffCrossRay) << 24L, rayCrossLineVec);
8383     if (u < 0 || u > 1 << 24 || t < 0 || t > 1 << 24)
8384     {
8385         return 0;
8386     }
8387 #endif
8388 
8389     *intersectionX = originX + mulscale24(ray.x, t);
8390     *intersectionY = originY + mulscale24(ray.y, t);
8391     *intersectionZ = originZ + mulscale24(destZ-originZ, t);
8392 
8393     return 1;
8394 }
8395 
8396 //
8397 // rintersect (internal)
8398 //
8399 // returns: -1 if didn't intersect, coefficient (x3--x4 fraction)<<16 else
rintersect_old(int32_t x1,int32_t y1,int32_t z1,int32_t vx,int32_t vy,int32_t vz,int32_t x3,int32_t y3,int32_t x4,int32_t y4,int32_t * intx,int32_t * inty,int32_t * intz)8400 int32_t rintersect_old(int32_t x1, int32_t y1, int32_t z1,
8401                    int32_t vx, int32_t vy, int32_t vz,
8402                    int32_t x3, int32_t y3, int32_t x4, int32_t y4,
8403                    int32_t *intx, int32_t *inty, int32_t *intz)
8404 {
8405     //p1 towards p2 is a ray
8406 
8407     int32_t const x34=x3-x4, y34=y3-y4;
8408     int32_t const x31=x3-x1, y31=y3-y1;
8409 
8410     int32_t const bot  = vx*y34 - vy*x34;
8411     int32_t const topt = x31*y34 - y31*x34;
8412 
8413     if (bot == 0)
8414         return -1;
8415 
8416     int32_t const topu = vx*y31 - vy*x31;
8417 
8418     if (bot > 0 && (topt < 0 || topu < 0 || topu >= bot))
8419         return -1;
8420     else if (bot < 0 && (topt > 0 || topu > 0 || topu <= bot))
8421         return -1;
8422 
8423     int32_t t = divscale16(topt, bot);
8424     *intx = x1 + mulscale16(vx, t);
8425     *inty = y1 + mulscale16(vy, t);
8426     *intz = z1 + mulscale16(vz, t);
8427 
8428     t = divscale16(topu, bot);
8429 
8430     return t;
8431 }
8432 
rintersect(int32_t x1,int32_t y1,int32_t z1,int32_t vx,int32_t vy,int32_t vz,int32_t x3,int32_t y3,int32_t x4,int32_t y4,int32_t * intx,int32_t * inty,int32_t * intz)8433 int32_t rintersect(int32_t x1, int32_t y1, int32_t z1,
8434                    int32_t vx, int32_t vy, int32_t vz,
8435                    int32_t x3, int32_t y3, int32_t x4, int32_t y4,
8436                    int32_t *intx, int32_t *inty, int32_t *intz)
8437 {
8438     //p1 towards p2 is a ray
8439 
8440     if (enginecompatibilitymode != ENGINE_EDUKE32)
8441         return rintersect_old(x1,y1,z1,vx,vy,vz,x3,y3,x4,y4,intx,inty,intz);
8442 
8443     int64_t const x34=x3-x4, y34=y3-y4;
8444     int64_t const x31=x3-x1, y31=y3-y1;
8445 
8446     int64_t const bot  = vx*y34 - vy*x34;
8447     int64_t const topt = x31*y34 - y31*x34;
8448 
8449     if (bot == 0)
8450         return -1;
8451 
8452     int64_t const topu = vx*y31 - vy*x31;
8453 
8454     if (bot > 0 && (topt < 0 || topu < 0 || topu >= bot))
8455         return -1;
8456     else if (bot < 0 && (topt > 0 || topu > 0 || topu <= bot))
8457         return -1;
8458 
8459     int64_t t = tabledivide64_noinline(topt<<16, bot);
8460     *intx = x1 + ((vx*t)>>16);
8461     *inty = y1 + ((vy*t)>>16);
8462     *intz = z1 + ((vz*t)>>16);
8463 
8464     t = tabledivide64_noinline(topu<<16, bot);
8465 
8466     Bassert((unsigned)t < 65536);
8467 
8468     return t;
8469 }
8470 
rayintersect(int32_t x1,int32_t y1,int32_t z1,int32_t vx,int32_t vy,int32_t vz,int32_t x3,int32_t y3,int32_t x4,int32_t y4,int32_t * intx,int32_t * inty,int32_t * intz)8471 int32_t rayintersect(int32_t x1, int32_t y1, int32_t z1, int32_t vx, int32_t vy, int32_t vz, int32_t x3,
8472                      int32_t y3, int32_t x4, int32_t y4, int32_t *intx, int32_t *inty, int32_t *intz)
8473 {
8474     return (rintersect(x1, y1, z1, vx, vy, vz, x3, y3, x4, y4, intx, inty, intz) != -1);
8475 }
8476 
8477 //
8478 // multi-pskies
8479 //
8480 
tileSetupSky(int32_t const tilenum)8481 psky_t * tileSetupSky(int32_t const tilenum)
8482 {
8483     for (bssize_t i = 0; i < pskynummultis; i++)
8484         if (multipskytile[i] == tilenum)
8485             return &multipsky[i];
8486 
8487     int32_t const newPskyID = pskynummultis++;
8488     multipsky = (psky_t *)Xrealloc(multipsky, pskynummultis * sizeof(psky_t));
8489     multipskytile = (int32_t *)Xrealloc(multipskytile, pskynummultis * sizeof(int32_t));
8490 
8491     psky_t * const newPsky = &multipsky[newPskyID];
8492     Bmemset(newPsky, 0, sizeof(psky_t));
8493     multipskytile[newPskyID] = tilenum;
8494     newPsky->yscale = 65536;
8495 
8496     return newPsky;
8497 }
8498 
8499 //
8500 // Exported Engine Functions
8501 //
8502 
8503 #if !defined _WIN32 && defined DEBUGGINGAIDS && !defined GEKKO
8504 #ifdef GEKKO
8505 #define __rtems__
8506 #define _POSIX_REALTIME_SIGNALS
8507 #endif
8508 #include <signal.h>
sighandler(int sig,siginfo_t * info,void * ctx)8509 static void sighandler(int sig, siginfo_t *info, void *ctx)
8510 {
8511     const char *s;
8512     UNREFERENCED_PARAMETER(ctx);
8513     switch (sig)
8514     {
8515     case SIGFPE:
8516         switch (info->si_code)
8517         {
8518         case FPE_INTDIV:
8519             s = "FPE_INTDIV (integer divide by zero)"; break;
8520         case FPE_INTOVF:
8521             s = "FPE_INTOVF (integer overflow)"; break;
8522         case FPE_FLTDIV:
8523             s = "FPE_FLTDIV (floating-point divide by zero)"; break;
8524         case FPE_FLTOVF:
8525             s = "FPE_FLTOVF (floating-point overflow)"; break;
8526         case FPE_FLTUND:
8527             s = "FPE_FLTUND (floating-point underflow)"; break;
8528         case FPE_FLTRES:
8529             s = "FPE_FLTRES (floating-point inexact result)"; break;
8530         case FPE_FLTINV:
8531             s = "FPE_FLTINV (floating-point invalid operation)"; break;
8532         case FPE_FLTSUB:
8533             s = "FPE_FLTSUB (floating-point subscript out of range)"; break;
8534         default:
8535             s = "?! (unknown)"; break;
8536         }
8537         ERRprintf("Caught SIGFPE at address %p, code %s. Aborting.\n", info->si_addr, s);
8538         break;
8539     default:
8540         break;
8541     }
8542     abort();
8543 }
8544 #endif
8545 
8546 //
8547 // E_FatalError
8548 //
engineFatalError(char const * const msg)8549 int32_t engineFatalError(char const * const msg)
8550 {
8551     engineerrstr = msg;
8552     initprintf("ERROR: %s\n", engineerrstr);
8553     return -1;
8554 }
8555 
8556 //
8557 // preinitengine
8558 //
8559 static int32_t preinitcalled = 0;
8560 
8561 #if !defined DEBUG_MAIN_ARRAYS
8562 static spriteext_t spriteext_s[MAXSPRITES+MAXUNIQHUDID];
8563 static spritesmooth_t spritesmooth_s[MAXSPRITES+MAXUNIQHUDID];
8564 static sectortype sector_s[MAXSECTORS + M32_FIXME_SECTORS];
8565 static walltype wall_s[MAXWALLS + M32_FIXME_WALLS];
8566 #ifndef NEW_MAP_FORMAT
8567 static wallext_t wallext_s[MAXWALLS];
8568 #endif
8569 static spritetype sprite_s[MAXSPRITES];
8570 static tspritetype tsprite_s[MAXSPRITESONSCREEN];
8571 #endif
8572 
enginePreInit(void)8573 int32_t enginePreInit(void)
8574 {
8575     baselayer_init();
8576     initdivtables();
8577 
8578     if (initsystem())
8579         fatal_exit("Failure in initsystem()!\n");
8580 
8581     makeasmwriteable();
8582 
8583 #if !defined DEBUG_MAIN_ARRAYS
8584     sector = sector_s;
8585     wall = wall_s;
8586 # ifndef NEW_MAP_FORMAT
8587     wallext = wallext_s;
8588 # endif
8589     sprite = sprite_s;
8590     tsprite = tsprite_s;
8591     spriteext = spriteext_s;
8592     spritesmooth = spritesmooth_s;
8593 #endif
8594 
8595 #if !defined ENGINE_USING_A_C
8596     mmxoverlay();
8597 #endif
8598 
8599     validmodecnt = 0;
8600     videoGetModes();
8601 
8602     initcrc32table();
8603 
8604 #ifdef HAVE_CLIPSHAPE_FEATURE
8605     engineInitClipMaps();
8606 #endif
8607     preinitcalled = 1;
8608 
8609     return 0;
8610 }
8611 
8612 
8613 //
8614 // initengine
8615 //
engineInit(void)8616 int32_t engineInit(void)
8617 {
8618     int32_t i;
8619 
8620 #if !defined _WIN32 && defined DEBUGGINGAIDS && !defined GEKKO
8621     struct sigaction sigact, oldact;
8622     memset(&sigact, 0, sizeof(sigact));
8623     sigact.sa_sigaction = &sighandler;
8624     sigact.sa_flags = SA_SIGINFO;
8625     sigaction(SIGFPE, &sigact, &oldact);
8626 #endif
8627 
8628     if (!preinitcalled)
8629     {
8630         i = enginePreInit();
8631         if (i) return i;
8632     }
8633 
8634 #ifdef YAX_DEBUG
8635     u64tickspersec = (double)timerGetPerformanceFrequency();
8636     if (u64tickspersec==0.0)
8637         u64tickspersec = 1.0;
8638 #endif
8639 
8640     if (engineLoadTables())
8641         return 1;
8642 
8643     xyaspect = -1;
8644 
8645     rotatesprite_y_offset = 0;
8646     rotatesprite_yxaspect = 65536;
8647 
8648     showinvisibility = 0;
8649 
8650     for (i=1; i<1024; i++)
8651         lowrecip[i] = ((1<<24)-1)/i;
8652 
8653     Bmemset(voxoff, 0, sizeof(voxoff));
8654     Bmemset(voxlock, 0, sizeof(voxlock));
8655 
8656     for (i=0; i<MAXTILES; i++)
8657         tiletovox[i] = -1;
8658     clearbuf(voxscale, sizeof(voxscale)>>2, 65536);
8659     clearbufbyte(voxrotate, sizeof(voxrotate), 0);
8660 
8661     paletteloaded = 0;
8662 
8663     searchit = 0; searchstat = -1;
8664 
8665     totalclock = 0;
8666     g_visibility = 512;
8667     parallaxvisibility = 512;
8668 
8669     maxspritesonscreen = MAXSPRITESONSCREEN;
8670 
8671     paletteLoadFromDisk();
8672 
8673 #ifdef USE_OPENGL
8674     if (!hicinitcounter) hicinit();
8675     if (!mdinited) mdinit();
8676 #endif
8677 
8678     return 0;
8679 }
8680 
8681 //
8682 // E_PostInit
8683 //
enginePostInit(void)8684 int32_t enginePostInit(void)
8685 {
8686     if (!(paletteloaded & PALETTE_MAIN))
8687         return engineFatalError("No palette found.");
8688     if (!(paletteloaded & PALETTE_SHADE))
8689         return engineFatalError("No shade table found.");
8690     if (!(paletteloaded & PALETTE_TRANSLUC))
8691         return engineFatalError("No translucency table found.");
8692 
8693     palettePostLoadTables();
8694 
8695     return 0;
8696 }
8697 
8698 //
8699 // uninitengine
8700 //
engineUnInit(void)8701 void engineUnInit(void)
8702 {
8703     communityapiShutdown();
8704 
8705 #ifdef USE_OPENGL
8706     polymost_glreset();
8707     hicinit();
8708     freeallmodels();
8709 # ifdef POLYMER
8710     polymer_uninit();
8711 # endif
8712 #endif
8713 
8714     Buninitart();
8715 
8716     for (bssize_t i=0; i<DISTRECIPCACHESIZE; i++)
8717         ALIGNED_FREE_AND_NULL(distrecipcache[i].distrecip);
8718     Bmemset(distrecipcache, 0, sizeof(distrecipcache));
8719 
8720     paletteloaded = 0;
8721 
8722     for (bssize_t i=0; i<MAXPALOOKUPS; i++)
8723         if (i==0 || palookup[i] != palookup[0])
8724         {
8725             // Take care of handling aliased ^^^ cases!
8726             Xaligned_free(palookup[i]);
8727         }
8728     Bmemset(palookup, 0, sizeof(palookup));
8729 
8730     for (bssize_t i=0; i<MAXBLENDTABS; i++)
8731         Xfree(blendtable[i]);
8732     Bmemset(blendtable, 0, sizeof(blendtable));
8733 
8734     for (bssize_t i=1; i<MAXBASEPALS; i++)
8735         Xfree(basepaltable[i]);
8736     Bmemset(basepaltable, 0, sizeof(basepaltable));
8737     basepaltable[0] = palette;
8738 
8739     DO_FREE_AND_NULL(kpzbuf);
8740     kpzbufsiz = 0;
8741 
8742     for (bssize_t i = 0; i < num_usermaphacks; i++)
8743     {
8744         Xfree(usermaphacks[i].mhkfile);
8745         Xfree(usermaphacks[i].title);
8746     }
8747     DO_FREE_AND_NULL(usermaphacks);
8748     num_usermaphacks = 0;
8749 
8750     DO_FREE_AND_NULL(multipsky);
8751     DO_FREE_AND_NULL(multipskytile);
8752     pskynummultis = 0;
8753 
8754     DO_FREE_AND_NULL(g_defNamePtr);
8755 
8756     uninitsystem();
8757 }
8758 
8759 
8760 //
8761 // initspritelists
8762 //
8763 void (*initspritelists_replace)(void) = NULL;
initspritelists(void)8764 void initspritelists(void)
8765 {
8766     if (initspritelists_replace)
8767     {
8768         initspritelists_replace();
8769         return;
8770     }
8771     int32_t i;
8772 
8773     // initial list state for statnum lists:
8774     //
8775     //  statnum 0: nil
8776     //  statnum 1: nil
8777     //     . . .
8778     //  statnum MAXSTATUS-1: nil
8779     //  "statnum MAXSTATUS": nil <- 0 <-> 1 <-> 2 <-> ... <-> MAXSPRITES-1 -> nil
8780     //
8781     // That is, the dummy MAXSTATUS statnum has all sprites.
8782 
8783     for (i=0; i<MAXSECTORS; i++)   //Init doubly-linked sprite sector lists
8784         headspritesect[i] = -1;
8785     headspritesect[MAXSECTORS] = 0;
8786 
8787     for (i=0; i<MAXSPRITES; i++)
8788     {
8789         prevspritesect[i] = i-1;
8790         nextspritesect[i] = i+1;
8791         sprite[i].sectnum = MAXSECTORS;
8792     }
8793     prevspritesect[0] = -1;
8794     nextspritesect[MAXSPRITES-1] = -1;
8795 
8796 
8797     for (i=0; i<MAXSTATUS; i++)   //Init doubly-linked sprite status lists
8798         headspritestat[i] = -1;
8799     headspritestat[MAXSTATUS] = 0;
8800 
8801     for (i=0; i<MAXSPRITES; i++)
8802     {
8803         prevspritestat[i] = i-1;
8804         nextspritestat[i] = i+1;
8805         sprite[i].statnum = MAXSTATUS;
8806     }
8807     prevspritestat[0] = -1;
8808     nextspritestat[MAXSPRITES-1] = -1;
8809 
8810     tailspritefree = MAXSPRITES-1;
8811     Numsprites = 0;
8812 }
8813 
8814 
set_globalang(fix16_t const ang)8815 void set_globalang(fix16_t const ang)
8816 {
8817     globalang = fix16_to_int(ang)&2047;
8818     qglobalang = ang & 0x7FFFFFF;
8819 
8820     float const f_ang = fix16_to_float(ang);
8821     float const f_ang_radians = f_ang * M_PI * (1.f/1024.f);
8822 
8823     float const fcosang = cosf(f_ang_radians) * 16384.f;
8824     float const fsinang = sinf(f_ang_radians) * 16384.f;
8825 
8826 #ifdef USE_OPENGL
8827     fcosglobalang = fcosang;
8828     fsinglobalang = fsinang;
8829 #endif
8830 
8831     cosglobalang = (int)fcosang;
8832     singlobalang = (int)fsinang;
8833 
8834     cosviewingrangeglobalang = mulscale16(cosglobalang,viewingrange);
8835     sinviewingrangeglobalang = mulscale16(singlobalang,viewingrange);
8836 }
8837 
8838 //
8839 // drawrooms
8840 //
renderDrawRoomsQ16(int32_t daposx,int32_t daposy,int32_t daposz,fix16_t daang,fix16_t dahoriz,int16_t dacursectnum)8841 int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz,
8842                            fix16_t daang, fix16_t dahoriz, int16_t dacursectnum)
8843 {
8844     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
8845 
8846     int32_t i, j, /*cz, fz,*/ closest;
8847     int16_t *shortptr1, *shortptr2;
8848 
8849     beforedrawrooms = 0;
8850 
8851     set_globalpos(daposx, daposy, daposz);
8852     set_globalang(daang);
8853 
8854     global100horiz = dahoriz;
8855 
8856     // xdimenscale is scale(xdimen,yxaspect,320);
8857     // normalization by viewingrange so that center-of-aim doesn't depend on it
8858     qglobalhoriz = mulscale16(dahoriz-F16(100), divscale16(xdimenscale, viewingrange))+fix16_from_int(ydimen>>1);
8859     globalhoriz = fix16_to_int(qglobalhoriz);
8860 
8861     globaluclip = (0-globalhoriz)*xdimscale;
8862     globaldclip = (ydimen-globalhoriz)*xdimscale;
8863 
8864     i = mulscale16(xdimenscale,viewingrangerecip);
8865     globalpisibility = mulscale16(parallaxvisibility,i);
8866     switch (videoGetRenderMode())
8867     {
8868         // switch on renderers to make fog look almost the same everywhere
8869 
8870     case REND_CLASSIC:
8871         globalvisibility = mulscale16(g_visibility,i);
8872         break;
8873 #ifdef USE_OPENGL
8874     case REND_POLYMOST:
8875         // NOTE: In Polymost, the fragment depth depends on the x screen size!
8876         if (r_usenewshading == 4)
8877             globalvisibility = g_visibility * xdimen;
8878         else if (r_usenewshading >= 2)
8879             globalvisibility = scale(g_visibility<<2, xdimen, 1680);
8880         else
8881             globalvisibility = scale(g_visibility<<2, xdimen, 1100);
8882         globalvisibility2 = mulscale16(g_visibility, i);
8883         break;
8884 # ifdef POLYMER
8885     case REND_POLYMER:
8886         if (r_usenewshading == 4)
8887             globalvisibility = g_visibility<<5;
8888         else if (r_usenewshading > 1)
8889         /* 0.65127==150/230, another constant found out by experiment. :/
8890          * (150 is Polymost's old FOGDISTCONST.) */
8891             globalvisibility = (g_visibility<<2) * 150 / (230 * 35);
8892         else
8893             globalvisibility = (g_visibility<<2) / 35;
8894         break;
8895 # endif
8896 #endif
8897     }
8898 
8899     globalhisibility = mulscale16(globalvisibility,xyaspect);
8900     globalcisibility = mulscale8(globalhisibility,320);
8901 
8902 #ifdef USE_OPENGL
8903     globalhisibility2 = mulscale16(globalvisibility2,xyaspect);
8904     globalcisibility2 = mulscale8(globalhisibility2,320);
8905 #endif
8906 
8907     globalcursectnum = dacursectnum;
8908     totalclocklock = totalclock;
8909 
8910     if ((xyaspect != oxyaspect) || (xdimen != oxdimen) || (viewingrange != oviewingrange))
8911         dosetaspect();
8912 
8913     Bmemset(gotsector, 0, sizeof(gotsector));
8914 
8915     if (videoGetRenderMode() != REND_CLASSIC
8916 #ifdef YAX_ENABLE
8917         || yax_globallev==YAX_MAXDRAWS
8918 #endif
8919         )
8920     {
8921         shortptr1 = (int16_t *)&startumost[windowxy1.x];
8922         shortptr2 = (int16_t *)&startdmost[windowxy1.x];
8923         i = xdimen-1;
8924         do
8925         {
8926             umost[i] = shortptr1[i]-windowxy1.y;
8927             dmost[i] = shortptr2[i]-windowxy1.y;
8928         }
8929         while (i--);  // xdimen == 1 is OK!
8930         umost[0] = shortptr1[0]-windowxy1.y;
8931         dmost[0] = shortptr2[0]-windowxy1.y;
8932     }
8933 
8934     for (int i = 0; i < numwalls; ++i)
8935     {
8936         if (wall[i].cstat & CSTAT_WALL_ROTATE_90)
8937         {
8938             auto &w    = wall[i];
8939             auto &tile = rottile[w.picnum+animateoffs(w.picnum,16384)];
8940 
8941             if (tile.newtile == -1 && tile.owner == -1)
8942             {
8943                 tile.newtile = findUnusedTile();
8944                 Bassert(tile.newtile != -1);
8945 
8946                 rottile[tile.newtile].owner = w.picnum+animateoffs(w.picnum,16384);
8947 
8948                 auto &siz  = tilesiz[w.picnum+animateoffs(w.picnum,16384)];
8949                 tileSetSize(tile.newtile, siz.x, siz.y);
8950 
8951                 tileLoad(tile.newtile);
8952                 // Bassert(waloff[tile.newtile]);
8953             }
8954         }
8955     }
8956 
8957 #ifdef USE_OPENGL
8958 # ifdef POLYMER
8959     if (videoGetRenderMode() == REND_POLYMER)
8960     {
8961 #  ifdef YAX_ENABLE
8962         // BEGIN_TWEAK ceiling/floor fake 'TROR' pics, see END_TWEAK in build.c
8963         if (editstatus && showinvisibility)
8964         {
8965             for (i=0; i<numyaxbunches; i++)
8966             {
8967                 yax_tweakpicnums(i, YAX_CEILING, 0);
8968                 yax_tweakpicnums(i, YAX_FLOOR, 0);
8969             }
8970         }
8971 #  endif
8972         polymer_glinit();
8973         polymer_drawrooms(daposx, daposy, daposz, daang, dahoriz, dacursectnum);
8974         glDisable(GL_CULL_FACE);
8975         polymost2d = 0;
8976         return 0;
8977     }
8978 # endif
8979 #endif
8980 
8981     // Update starting sector number (common to classic and Polymost).
8982     // ADJUST_GLOBALCURSECTNUM.
8983     if (globalcursectnum >= MAXSECTORS)
8984         globalcursectnum -= MAXSECTORS;
8985     else
8986     {
8987         i = globalcursectnum;
8988         updatesector(globalposx,globalposy,&globalcursectnum);
8989         if (globalcursectnum < 0) globalcursectnum = i;
8990 
8991         // PK 20110123: I'm not sure what the line above is supposed to do, but 'i'
8992         //              *can* be negative, so let's just quit here in that case...
8993         if (globalcursectnum<0)
8994             return 0;
8995     }
8996 
8997 #ifdef USE_OPENGL
8998     //============================================================================= //POLYMOST BEGINS
8999     polymost_drawrooms();
9000 
9001     if (videoGetRenderMode() != REND_CLASSIC)
9002         return inpreparemirror;
9003     //============================================================================= //POLYMOST ENDS
9004 #endif
9005 
9006     videoBeginDrawing(); //{{{
9007 
9008 #ifdef ENGINE_CLEAR_SCREEN
9009 #ifdef YAX_ENABLE
9010     if (!g_nodraw)
9011 #endif
9012     if (numyaxbunches==0)
9013         draw_rainbow_background();
9014 #endif
9015 
9016     frameoffset = frameplace + windowxy1.y*bytesperline + windowxy1.x;
9017 
9018     //if (smostwallcnt < 0)
9019     //  if (getkensmessagecrc(FP_OFF(kensmessage)) != 0x56c764d4)
9020     //      { /* setvmode(0x3);*/ OSD_Printf("Nice try.\n"); Bexit(EXIT_SUCCESS); }
9021 
9022     numhits = xdimen; numscans = 0; numbunches = 0;
9023     maskwallcnt = 0; smostwallcnt = 0; smostcnt = 0; spritesortcnt = 0;
9024 
9025 #ifdef YAX_ENABLE
9026     if (yax_globallev != YAX_MAXDRAWS)
9027     {
9028         j = yax_globalbunch*xdimen;
9029 
9030         Bmemcpy(umost, yumost+j, xdimen*sizeof(int16_t));
9031         Bmemcpy(dmost, ydmost+j, xdimen*sizeof(int16_t));
9032 
9033         for (i=0; i<xdimen; i++)
9034             if (umost[i] > dmost[i])
9035                 numhits--;
9036 
9037 //        yaxdebug("cf %d, tlev %d, bunch %d: numhits=%d", yax_globalcf, yax_globallev, yax_globalbunch, numhits);
9038     }
9039 #endif
9040 
9041 /*
9042     globparaceilclip = 1;
9043     globparaflorclip = 1;
9044     getzsofslope(globalcursectnum,globalposx,globalposy,&cz,&fz);
9045     if (globalposz < cz) globparaceilclip = 0;
9046     if (globalposz > fz) globparaflorclip = 0;
9047 */
9048 #ifdef YAX_ENABLE
9049     if (yax_globallev != YAX_MAXDRAWS)
9050     {
9051         int k;
9052         for (SECTORS_OF_BUNCH(yax_globalbunch, !yax_globalcf, k))
9053         {
9054             classicScanSector(k);
9055         }
9056     }
9057     else
9058 #endif
9059     classicScanSector(globalcursectnum);
9060 #ifdef YAX_ENABLE
9061     int startbunches = numbunches;
9062     if (yax_globallev != YAX_MAXDRAWS)
9063     {
9064         for (bssize_t i = 0; i < startbunches; i++)
9065         {
9066             bunchfirst[MAXWALLSB-1-i] = bunchfirst[i];
9067             bunchlast[MAXWALLSB-1-i] = bunchlast[i];
9068         }
9069     }
9070 #endif
9071 
9072     if (inpreparemirror)
9073     {
9074         // INPREPAREMIRROR_NO_BUNCHES
9075         // numbunches==0 can happen if the mirror is far away... the game code decides
9076         // to draw it, but scansector gets zero bunches.  Result: big screwup!
9077         // Set inpreparemirror to 0 to indicate that we were unable to render the mirror
9078         if (numbunches==0)
9079         {
9080             inpreparemirror = 0;
9081             videoEndDrawing();  //!!!
9082             return 0;
9083         }
9084 
9085         mirrorsx1 = xdimen-1; mirrorsx2 = 0;
9086         for (i=numscans-1; i>=0; i--)
9087         {
9088             if (wall[thewall[i]].nextsector >= 0)
9089             {
9090                 if (xb1[i] < mirrorsx1) mirrorsx1 = xb1[i];
9091                 if (xb2[i] > mirrorsx2) mirrorsx2 = xb2[i];
9092             }
9093         }
9094 
9095         for (i=0; i<mirrorsx1; i++)
9096             if (umost[i] <= dmost[i])
9097                 { umost[i] = 1; dmost[i] = 0; numhits--; }
9098         for (i=mirrorsx2+1; i<xdimen; i++)
9099             if (umost[i] <= dmost[i])
9100                 { umost[i] = 1; dmost[i] = 0; numhits--; }
9101 
9102         classicDrawBunches(0L);
9103         numbunches--;
9104         bunchfirst[0] = bunchfirst[numbunches];
9105         bunchlast[0] = bunchlast[numbunches];
9106 
9107         mirrorsy1 = min(umost[mirrorsx1],umost[mirrorsx2]);
9108         mirrorsy2 = max(dmost[mirrorsx1],dmost[mirrorsx2]);
9109     }
9110 
9111     while ((numbunches > 0) && (numhits > 0))
9112     {
9113         Bmemset(tempbuf, 0, numbunches);
9114         tempbuf[0] = 1;
9115 
9116         closest = 0;              //Almost works, but not quite :(
9117         for (i=1; i<numbunches; i++)
9118         {
9119             if ((j = bunchfront(i,closest)) < 0) continue;
9120             tempbuf[i] = 1;
9121             if (j == 0) tempbuf[closest] = 1, closest = i;
9122         }
9123         for (i=0; i<numbunches; i++) //Double-check
9124         {
9125             if (tempbuf[i]) continue;
9126             if ((j = bunchfront(i,closest)) < 0) continue;
9127             tempbuf[i] = 1;
9128             if (j == 0) tempbuf[closest] = 1, closest = i, i = 0;
9129         }
9130 
9131 #ifdef YAX_ENABLE
9132         int bunchnodraw = 0;
9133 
9134         if (yax_globallev != YAX_MAXDRAWS)
9135         {
9136             bunchnodraw = 1;
9137             for (i = 0; i < startbunches; i++)
9138             {
9139                 int const sbunch = MAXWALLSB-1-i;
9140                 if (bunchfirst[closest] == bunchfirst[sbunch])
9141                 {
9142                     bunchnodraw = 0;
9143                     break;
9144                 }
9145                 int const bnch = bunchfront(sbunch,closest); if (bnch < 0) continue;
9146                 if (!bnch)
9147                 {
9148                     bunchnodraw = 0;
9149                     break;
9150                 }
9151             }
9152         }
9153 
9154         if (!bunchnodraw)
9155 #endif
9156         classicDrawBunches(closest);
9157 
9158         if (automapping)
9159         {
9160             for (int z=bunchfirst[closest]; z>=0; z=bunchp2[z])
9161                 show2dwall[thewall[z]>>3] |= pow2char[thewall[z]&7];
9162         }
9163 
9164         numbunches--;
9165         bunchfirst[closest] = bunchfirst[numbunches];
9166         bunchlast[closest] = bunchlast[numbunches];
9167     }
9168 
9169     videoEndDrawing();   //}}}
9170 
9171     return inpreparemirror;
9172 }
9173 
9174 // UTILITY TYPES AND FUNCTIONS FOR DRAWMASKS OCCLUSION TREE
9175 // typedef struct          s_maskleaf
9176 // {
9177 //     int32_t                index;
9178 //     _point2d            p1, p2;
9179 //     _equation           maskeq, p1eq, p2eq;
9180 //     struct s_maskleaf*  branch[MAXWALLSB];
9181 //     int32_t                 drawing;
9182 // }                       _maskleaf;
9183 //
9184 // _maskleaf               maskleaves[MAXWALLSB];
9185 
9186 // returns equation of a line given two points
equation(float const x1,float const y1,float const x2,float const y2)9187 static inline _equation equation(float const x1, float const y1, float const x2, float const y2)
9188 {
9189     const float f = x2-x1;
9190 
9191     // vertical
9192     if (f == 0.f)
9193        return { 1, 0, -x1 };
9194     else
9195     {
9196         float const ff = (y2 - y1) / f;
9197         return { ff, -1, (y1 - (ff * x1)) };
9198     }
9199 }
9200 
wallvisible(int32_t const x,int32_t const y,int16_t const wallnum)9201 int32_t wallvisible(int32_t const x, int32_t const y, int16_t const wallnum)
9202 {
9203     // 1 if wall is in front of player 0 otherwise
9204     auto w1 = (uwallptr_t)&wall[wallnum];
9205     auto w2 = (uwallptr_t)&wall[w1->point2];
9206 
9207     int32_t const a1 = getangle(w1->x - x, w1->y - y);
9208     int32_t const a2 = getangle(w2->x - x, w2->y - y);
9209 
9210     return (((a2 + (2048 - a1)) & 2047) <= 1024);
9211 }
9212 
9213 #if 0
9214 // returns the intersection point between two lines
9215 _point2d        intersection(_equation eq1, _equation eq2)
9216 {
9217     _point2d    ret;
9218     float       det;
9219 
9220     det = (float)(1) / (eq1.a*eq2.b - eq2.a*eq1.b);
9221     ret.x = ((eq1.b*eq2.c - eq2.b*eq1.c) * det);
9222     ret.y = ((eq2.a*eq1.c - eq1.a*eq2.c) * det);
9223 
9224     return ret;
9225 }
9226 
9227 // check if a point that's on the line is within the segment boundaries
9228 int32_t             pointonmask(_point2d point, _maskleaf* wall)
9229 {
9230     if ((min(wall->p1.x, wall->p2.x) <= point.x) && (point.x <= max(wall->p1.x, wall->p2.x)) && (min(wall->p1.y, wall->p2.y) <= point.y) && (point.y <= max(wall->p1.y, wall->p2.y)))
9231         return 1;
9232     return 0;
9233 }
9234 
9235 // returns 1 if wall2 is hidden by wall1
9236 int32_t             wallobstructswall(_maskleaf* wall1, _maskleaf* wall2)
9237 {
9238     _point2d    cross;
9239 
9240     cross = intersection(wall2->p1eq, wall1->maskeq);
9241     if (pointonmask(cross, wall1))
9242         return 1;
9243 
9244     cross = intersection(wall2->p2eq, wall1->maskeq);
9245     if (pointonmask(cross, wall1))
9246         return 1;
9247 
9248     cross = intersection(wall1->p1eq, wall2->maskeq);
9249     if (pointonmask(cross, wall2))
9250         return 1;
9251 
9252     cross = intersection(wall1->p2eq, wall2->maskeq);
9253     if (pointonmask(cross, wall2))
9254         return 1;
9255 
9256     return 0;
9257 }
9258 
9259 // recursive mask drawing function
9260 static inline void    drawmaskleaf(_maskleaf* wall)
9261 {
9262     int32_t i;
9263 
9264     wall->drawing = 1;
9265     i = 0;
9266     while (wall->branch[i] != NULL)
9267     {
9268         if (wall->branch[i]->drawing == 0)
9269         {
9270             //OSD_Printf("Drawing parent of %i : mask %i\n", wall->index, wall->branch[i]->index);
9271             drawmaskleaf(wall->branch[i]);
9272         }
9273         i++;
9274     }
9275 
9276     //OSD_Printf("Drawing mask %i\n", wall->index);
9277     drawmaskwall(wall->index);
9278 }
9279 #endif
9280 
sameside(const _equation * eq,const vec2f_t * p1,const vec2f_t * p2)9281 static inline int32_t         sameside(const _equation *eq, const vec2f_t *p1, const vec2f_t *p2)
9282 {
9283     const float sign1 = (eq->a * p1->x) + (eq->b * p1->y) + eq->c;
9284     const float sign2 = (eq->a * p2->x) + (eq->b * p2->y) + eq->c;
9285     return (sign1 * sign2) > 0.f;
9286 }
9287 
9288 // x1, y1: in/out
9289 // rest x/y: out
9290 
9291 
9292 
9293 #ifdef DEBUG_MASK_DRAWING
9294 int32_t g_maskDrawMode = 0;
9295 #endif
9296 
comparetsprites(int const k,int const l)9297 static inline int comparetsprites(int const k, int const l)
9298 {
9299 #ifdef USE_OPENGL
9300     if (videoGetRenderMode() == REND_POLYMOST)
9301     {
9302         if ((tspriteptr[k]->cstat & 48) != (tspriteptr[l]->cstat & 48))
9303             return (tspriteptr[k]->cstat & 48) - (tspriteptr[l]->cstat & 48);
9304 
9305         if ((tspriteptr[k]->cstat & 48) == 16 && tspriteptr[k]->ang != tspriteptr[l]->ang)
9306             return tspriteptr[k]->ang - tspriteptr[l]->ang;
9307     }
9308 #endif
9309     if (tspriteptr[k]->statnum != tspriteptr[l]->statnum)
9310         return tspriteptr[k]->statnum - tspriteptr[l]->statnum;
9311 
9312     if (tspriteptr[k]->x == tspriteptr[l]->x &&
9313         tspriteptr[k]->y == tspriteptr[l]->y &&
9314         tspriteptr[k]->z == tspriteptr[l]->z &&
9315         (tspriteptr[k]->cstat & 48) == (tspriteptr[l]->cstat & 48) &&
9316         tspriteptr[k]->owner != tspriteptr[l]->owner)
9317         return tspriteptr[k]->owner - tspriteptr[l]->owner;
9318 
9319     if (klabs(spritesxyz[k].z-globalposz) != klabs(spritesxyz[l].z-globalposz))
9320         return klabs(spritesxyz[k].z-globalposz)-klabs(spritesxyz[l].z-globalposz);
9321 
9322     return 0;
9323 }
9324 
sortsprites(int const start,int const end)9325 static void sortsprites(int const start, int const end)
9326 {
9327     int32_t i, gap, y, ys;
9328 
9329     if (start >= end)
9330         return;
9331 
9332     gap = 1; while (gap < end - start) gap = (gap<<1)+1;
9333     for (gap>>=1; gap>0; gap>>=1)   //Sort sprite list
9334         for (i=start; i<end-gap; i++)
9335             for (bssize_t l=i; l>=start; l-=gap)
9336             {
9337                 if (spritesxyz[l].y <= spritesxyz[l+gap].y) break;
9338                 swapptr(&tspriteptr[l],&tspriteptr[l+gap]);
9339                 swaplong(&spritesxyz[l].x,&spritesxyz[l+gap].x);
9340                 swaplong(&spritesxyz[l].y,&spritesxyz[l+gap].y);
9341             }
9342 
9343     ys = spritesxyz[start].y; i = start;
9344     for (bssize_t j=start+1; j<=end; j++)
9345     {
9346         if (j < end)
9347         {
9348             y = spritesxyz[j].y;
9349             if (y == ys)
9350                 continue;
9351 
9352             ys = y;
9353         }
9354 
9355         if (j > i+1)
9356         {
9357             for (bssize_t k=i; k<j; k++)
9358             {
9359                 auto const s = tspriteptr[k];
9360 
9361                 spritesxyz[k].z = s->z;
9362                 if ((s->cstat&48) != 32)
9363                 {
9364                     int32_t yoff = picanm[s->picnum].yofs + s->yoffset;
9365                     int32_t yspan = (tilesiz[s->picnum].y*s->yrepeat<<2);
9366 
9367                     spritesxyz[k].z -= (yoff*s->yrepeat)<<2;
9368 
9369                     if (!(s->cstat&128))
9370                         spritesxyz[k].z -= (yspan>>1);
9371                     if (klabs(spritesxyz[k].z-globalposz) < (yspan>>1))
9372                         spritesxyz[k].z = globalposz;
9373                 }
9374             }
9375 
9376             for (bssize_t k=i+1; k<j; k++)
9377                 for (bssize_t l=i; l<k; l++)
9378                     if (comparetsprites(k, l) < 0)
9379                     {
9380                         swapptr(&tspriteptr[k],&tspriteptr[l]);
9381                         vec3_t tv3 = spritesxyz[k];
9382                         spritesxyz[k] = spritesxyz[l];
9383                         spritesxyz[l] = tv3;
9384                     }
9385         }
9386         i = j;
9387     }
9388 }
9389 
9390 //
9391 // drawmasks
9392 //
renderDrawMasks(void)9393 void renderDrawMasks(void)
9394 {
9395     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
9396 
9397 #ifdef DEBUG_MASK_DRAWING
9398         static struct {
9399             int16_t di;  // &32768: &32767 is tspriteptr[], else thewall[] index
9400             int16_t i;   // sprite[] or wall[] index
9401         } debugmask[MAXWALLSB + MAXSPRITESONSCREEN + 1];
9402 
9403         int32_t dmasknum = 0;
9404 
9405 # define debugmask_add(dispidx, idx) do { \
9406         if (g_maskDrawMode && videoGetRenderMode()==REND_CLASSIC) { \
9407             debugmask[dmasknum].di = dispidx; \
9408             debugmask[dmasknum++].i = idx; \
9409         } \
9410     } while (0)
9411 #else
9412 # define debugmask_add(dispidx, idx) do {} while (0)
9413 #endif
9414     int32_t i = spritesortcnt-1;
9415     int32_t numSprites = spritesortcnt;
9416 
9417 #ifdef USE_OPENGL
9418     if (videoGetRenderMode() == REND_POLYMOST)
9419     {
9420         spritesortcnt = 0;
9421         int32_t back = i;
9422         for (; i >= 0; --i)
9423         {
9424             if (polymost_spriteHasTranslucency(&tsprite[i]))
9425             {
9426                 tspriteptr[spritesortcnt] = &tsprite[i];
9427                 ++spritesortcnt;
9428             } else
9429             {
9430                 tspriteptr[back] = &tsprite[i];
9431                 --back;
9432             }
9433         }
9434     } else
9435 #endif
9436     {
9437         for (; i >= 0; --i)
9438         {
9439             tspriteptr[i] = &tsprite[i];
9440         }
9441     }
9442 
9443     for (i=numSprites-1; i>=0; --i)
9444     {
9445         const int32_t xs = tspriteptr[i]->x-globalposx, ys = tspriteptr[i]->y-globalposy;
9446         const int32_t yp = dmulscale6(xs,cosviewingrangeglobalang,ys,sinviewingrangeglobalang);
9447 #ifdef USE_OPENGL
9448         const int32_t modelp = polymost_spriteIsModelOrVoxel(tspriteptr[i]);
9449 #endif
9450 
9451         if (yp > (4<<8))
9452         {
9453             const int32_t xp = dmulscale6(ys,cosglobalang,-xs,singlobalang);
9454 
9455             if (mulscale24(labs(xp+yp),xdimen) >= yp)
9456                 goto killsprite;
9457 
9458             spritesxyz[i].x = scale(xp+yp,xdimen<<7,yp);
9459         }
9460         else if ((tspriteptr[i]->cstat&48) == 0)
9461         {
9462 killsprite:
9463 #ifdef USE_OPENGL
9464             if (!modelp)
9465 #endif
9466             {
9467                 //Delete face sprite if on wrong side!
9468                 if (i >= spritesortcnt)
9469                 {
9470                     --numSprites;
9471                     if (i != numSprites)
9472                     {
9473                         tspriteptr[i] = tspriteptr[numSprites];
9474                         spritesxyz[i].x = spritesxyz[numSprites].x;
9475                         spritesxyz[i].y = spritesxyz[numSprites].y;
9476                     }
9477                 }
9478                 else
9479                 {
9480                     --numSprites;
9481                     --spritesortcnt;
9482                     if (i != numSprites)
9483                     {
9484                         tspriteptr[i] = tspriteptr[spritesortcnt];
9485                         spritesxyz[i].x = spritesxyz[spritesortcnt].x;
9486                         spritesxyz[i].y = spritesxyz[spritesortcnt].y;
9487                         tspriteptr[spritesortcnt] = tspriteptr[numSprites];
9488                         spritesxyz[spritesortcnt].x = spritesxyz[numSprites].x;
9489                         spritesxyz[spritesortcnt].y = spritesxyz[numSprites].y;
9490                     }
9491                 }
9492                 continue;
9493             }
9494         }
9495         spritesxyz[i].y = yp;
9496     }
9497 
9498     sortsprites(0, spritesortcnt);
9499     sortsprites(spritesortcnt, numSprites);
9500 
9501     videoBeginDrawing(); //{{{
9502 
9503 #ifdef USE_OPENGL
9504     if (videoGetRenderMode() == REND_POLYMOST)
9505     {
9506         glDisable(GL_BLEND);
9507         glEnable(GL_ALPHA_TEST);
9508         polymost_setClamp(1+2);
9509 
9510         if (spritesortcnt < numSprites)
9511         {
9512             for (bssize_t i = numSprites-1; i >= spritesortcnt;)
9513             {
9514                 int32_t py = spritesxyz[i].y;
9515                 int32_t pcstat = tspriteptr[i]->cstat & 48;
9516                 int32_t pangle = tspriteptr[i]->ang;
9517                 int j = i - 1;
9518                 if (!polymost_spriteIsModelOrVoxel(tspriteptr[i]))
9519                 {
9520                     while (j >= spritesortcnt && py == spritesxyz[j].y && pcstat == (tspriteptr[j]->cstat & 48) && (pcstat != 16 || pangle == tspriteptr[j]->ang)
9521                         && !polymost_spriteIsModelOrVoxel(tspriteptr[j]))
9522                     {
9523                         j--;
9524                     }
9525                 }
9526                 if (i - j == 1)
9527                 {
9528                     debugmask_add(i | 32768, tspriteptr[i]->owner);
9529                     renderDrawSprite(i);
9530                     tspriteptr[i] = NULL;
9531                 }
9532                 else
9533                 {
9534                     glDepthMask(GL_FALSE);
9535 
9536                     for (bssize_t k = i; k > j; k--)
9537                     {
9538                         debugmask_add(k | 32768, tspriteptr[k]->owner);
9539                         renderDrawSprite(k);
9540                     }
9541 
9542                     glDepthMask(GL_TRUE);
9543 
9544                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
9545 
9546                     for (bssize_t k = i; k > j; k--)
9547                     {
9548                         renderDrawSprite(k);
9549                         tspriteptr[k] = NULL;
9550                     }
9551 
9552                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
9553 
9554                 }
9555                 i = j;
9556             }
9557         }
9558 
9559         polymost_setClamp(0);
9560         int32_t numMaskWalls = maskwallcnt;
9561         maskwallcnt = 0;
9562         for (i = 0; i < numMaskWalls; i++)
9563         {
9564             if (polymost_maskWallHasTranslucency((uwalltype *) &wall[thewall[maskwall[i]]]))
9565             {
9566                 maskwall[maskwallcnt] = maskwall[i];
9567                 maskwallcnt++;
9568             }
9569             else
9570                 renderDrawMaskedWall(i);
9571         }
9572 
9573         glEnable(GL_BLEND);
9574         glEnable(GL_ALPHA_TEST);
9575         glDepthMask(GL_FALSE);
9576     }
9577 #endif
9578 
9579 #if 0
9580         for (i=spritesortcnt-1; i>=0; i--)
9581         {
9582             double xs = tspriteptr[i]->x-globalposx;
9583             double ys = tspriteptr[i]->y-globalposy;
9584             int32_t zs = tspriteptr[i]->z-globalposz;
9585 
9586             int32_t xp = ys*cosglobalang-xs*singlobalang;
9587             int32_t yp = (zs<<1);
9588             int32_t zp = xs*cosglobalang+ys*singlobalang;
9589 
9590             xs = ((double)xp*(halfxdimen<<12)/zp)+((halfxdimen+windowxy1.x)<<12);
9591             ys = ((double)yp*(xdimenscale<<12)/zp)+((globalhoriz+windowxy1.y)<<12);
9592 
9593             if (xs >= INT32_MIN && xs <= INT32_MAX && ys >= INT32_MIN && ys <= INT32_MAX)
9594             {
9595                 drawline256(xs-65536,ys-65536,xs+65536,ys+65536,31);
9596                 drawline256(xs+65536,ys-65536,xs-65536,ys+65536,31);
9597             }
9598         }
9599 #endif
9600 
9601     vec2f_t pos;
9602 
9603     pos.x = fglobalposx;
9604     pos.y = fglobalposy;
9605 
9606     // CAUTION: maskwallcnt and spritesortcnt may be zero!
9607     // Writing e.g. "while (maskwallcnt--)" is wrong!
9608     while (maskwallcnt)
9609     {
9610         // PLAG: sorting stuff
9611         const int32_t w = (videoGetRenderMode()==REND_POLYMER) ?
9612             maskwall[maskwallcnt-1] : thewall[maskwall[maskwallcnt-1]];
9613 
9614         maskwallcnt--;
9615 
9616         vec2f_t dot    = { (float)wall[w].x, (float)wall[w].y };
9617         vec2f_t dot2   = { (float)wall[wall[w].point2].x, (float)wall[wall[w].point2].y };
9618         vec2f_t middle = { (dot.x + dot2.x) * .5f, (dot.y + dot2.y) * .5f };
9619 
9620         _equation maskeq = equation(dot.x, dot.y, dot2.x, dot2.y);
9621         _equation p1eq   = equation(pos.x, pos.y, dot.x, dot.y);
9622         _equation p2eq   = equation(pos.x, pos.y, dot2.x, dot2.y);
9623 
9624 #ifdef USE_OPENGL
9625         if (videoGetRenderMode() == REND_POLYMOST)
9626             polymost_setClamp(1+2);
9627 #endif
9628 
9629         i = spritesortcnt;
9630         while (i)
9631         {
9632             i--;
9633             if (tspriteptr[i] != NULL)
9634             {
9635                 vec2f_t spr;
9636                 auto const tspr = tspriteptr[i];
9637 
9638                 spr.x = (float)tspr->x;
9639                 spr.y = (float)tspr->y;
9640 
9641                 if (!sameside(&maskeq, &spr, &pos))
9642                 {
9643                     // Sprite and camera are on different sides of the
9644                     // masked wall.
9645 
9646                     // Check if the sprite is inside the 'cone' given by
9647                     // the rays from the camera to the two wall-points.
9648                     const int32_t inleft = sameside(&p1eq, &middle, &spr);
9649                     const int32_t inright = sameside(&p2eq, &middle, &spr);
9650 
9651                     int32_t ok = (inleft && inright);
9652 
9653                     if (!ok)
9654                     {
9655                         // If not, check if any of the border points are...
9656                         int32_t xx[4] = { tspr->x };
9657                         int32_t yy[4] = { tspr->y };
9658                         int32_t numpts, jj;
9659 
9660                         const _equation pineq = inleft ? p1eq : p2eq;
9661 
9662                         if ((tspr->cstat & 48) == 32)
9663                         {
9664                             numpts = 4;
9665                             get_floorspr_points(tspr, 0, 0,
9666                                                 &xx[0], &xx[1], &xx[2], &xx[3],
9667                                                 &yy[0], &yy[1], &yy[2], &yy[3],
9668                                                 tspriteGetSlope(tspr));
9669                         }
9670                         else
9671                         {
9672                             const int32_t oang = tspr->ang;
9673                             numpts = 2;
9674 
9675                             // Consider face sprites as wall sprites with camera ang.
9676                             // XXX: factor 4/5 needed?
9677                             if ((tspr->cstat & 48) != 16)
9678                                 tspriteptr[i]->ang = globalang;
9679 
9680                             get_wallspr_points(tspr, &xx[0], &xx[1], &yy[0], &yy[1]);
9681 
9682                             if ((tspr->cstat & 48) != 16)
9683                                 tspriteptr[i]->ang = oang;
9684                         }
9685 
9686                         for (jj=0; jj<numpts; jj++)
9687                         {
9688                             spr.x = (float)xx[jj];
9689                             spr.y = (float)yy[jj];
9690 
9691                             if (!sameside(&maskeq, &spr, &pos))  // behind the maskwall,
9692                                 if ((sameside(&p1eq, &middle, &spr) &&  // inside the 'cone',
9693                                         sameside(&p2eq, &middle, &spr))
9694                                         || !sameside(&pineq, &middle, &spr))  // or on the other outside.
9695                                 {
9696                                     ok = 1;
9697                                     break;
9698                                 }
9699                         }
9700                     }
9701 
9702                     if (ok)
9703                     {
9704                         debugmask_add(i | 32768, tspr->owner);
9705                         renderDrawSprite(i);
9706 
9707                         tspriteptr[i] = NULL;
9708                     }
9709                 }
9710             }
9711         }
9712 
9713         debugmask_add(maskwall[maskwallcnt], thewall[maskwall[maskwallcnt]]);
9714 #ifdef USE_OPENGL
9715         if (videoGetRenderMode() == REND_POLYMOST)
9716             polymost_setClamp(0);
9717 #endif
9718         renderDrawMaskedWall(maskwallcnt);
9719     }
9720 
9721 #ifdef USE_OPENGL
9722     if (videoGetRenderMode() == REND_POLYMOST)
9723         polymost_setClamp(1+2);
9724 #endif
9725     while (spritesortcnt)
9726     {
9727         --spritesortcnt;
9728         if (tspriteptr[spritesortcnt] != NULL)
9729         {
9730             debugmask_add(i | 32768, tspriteptr[i]->owner);
9731             renderDrawSprite(spritesortcnt);
9732             tspriteptr[spritesortcnt] = NULL;
9733         }
9734     }
9735 #ifdef USE_OPENGL
9736     if (videoGetRenderMode() == REND_POLYMOST)
9737     {
9738         glDepthMask(GL_TRUE);
9739         polymost_setClamp(0);
9740     }
9741 #endif
9742 
9743 #ifdef POLYMER
9744     if (videoGetRenderMode() == REND_POLYMER)
9745         polymer_drawmasks();
9746 #endif
9747 #ifdef DEBUG_MASK_DRAWING
9748     if (g_maskDrawMode && videoGetRenderMode() == REND_CLASSIC)
9749     {
9750         for (i=0; i<dmasknum; i++)
9751         {
9752             EDUKE32_STATIC_ASSERT(MAXWALLS <= 32768 && MAXSPRITES <= 32768);
9753             int32_t spritep = !!(debugmask[i].di & 32768);
9754             int32_t di = debugmask[i].di & 32767;
9755 //            int32_t ii = debugmask[i].i;
9756 
9757             char numstr[12];
9758             Bsprintf(numstr, "%d", i+1);
9759 
9760             if (spritep)
9761             {
9762                 int32_t sx = spritesxyz[di].x>>8, sy = ydim/2 + 8;
9763                 // XXX: printext256 really ought to do bound checking on the
9764                 // x/y coords!
9765                 sx = clamp(sx, 0, xdim-8*Bstrlen(numstr)-1);
9766                 printext256(sx, sy, 241, 0, numstr, 0);
9767             }
9768             else
9769             {
9770                 int32_t sx = xb1[di] + (xb2[di]-xb1[di])/2, sy = ydim/2;
9771                 sx = clamp(sx, 0, xdim-8*Bstrlen(numstr)-1);
9772                 printext256(sx, sy, 31, 0, numstr, 0);
9773             }
9774         }
9775     }
9776 #endif
9777 
9778     videoEndDrawing();   //}}}
9779 }
9780 
9781 //
9782 // drawmapview
9783 //
renderDrawMapView(int32_t dax,int32_t day,int32_t zoome,int16_t ang)9784 void renderDrawMapView(int32_t dax, int32_t day, int32_t zoome, int16_t ang)
9785 {
9786     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
9787 
9788     int32_t i, j, k, l;
9789     int32_t x, y;
9790     int32_t s, ox, oy;
9791 
9792     int32_t const oyxaspect = yxaspect, oviewingrange = viewingrange;
9793 
9794     renderSetAspect(65536, divscale16((320*5)/8, 200));
9795 
9796     beforedrawrooms = 0;
9797 
9798     Bmemset(gotsector, 0, sizeof(gotsector));
9799 
9800     vec2_t const c1 = { (windowxy1.x<<12), (windowxy1.y<<12) };
9801     vec2_t const c2 = { ((windowxy2.x+1)<<12)-1, ((windowxy2.y+1)<<12)-1 };
9802 
9803     zoome <<= 8;
9804 
9805     vec2_t const bakgvect = { divscale28(sintable[(1536 - ang) & 2047], zoome),
9806                         divscale28(sintable[(2048 - ang) & 2047], zoome) };
9807     vec2_t const vect = { mulscale8(sintable[(2048 - ang) & 2047], zoome), mulscale8(sintable[(1536 - ang) & 2047], zoome) };
9808     vec2_t const vect2 = { mulscale16(vect.x, yxaspect), mulscale16(vect.y, yxaspect) };
9809 
9810     int32_t sortnum = 0;
9811 
9812     videoBeginDrawing(); //{{{
9813 
9814     usectorptr_t sec;
9815 
9816     for (s=0,sec=(usectorptr_t)&sector[s]; s<numsectors; s++,sec++)
9817         if (show2dsector[s>>3]&pow2char[s&7])
9818         {
9819 #ifdef YAX_ENABLE
9820             if (yax_getbunch(s, YAX_FLOOR) >= 0 && (sector[s].floorstat&(256+128))==0)
9821                 continue;
9822 #endif
9823             int32_t npoints = 0; i = 0;
9824             int32_t startwall = sec->wallptr;
9825 #if 0
9826             for (w=sec->wallnum,wal=&wall[startwall]; w>0; w--,wal++)
9827             {
9828                 ox = wal->x - dax; oy = wal->y - day;
9829                 x = dmulscale16(ox,xvect,-oy,yvect) + (xdim<<11);
9830                 y = dmulscale16(oy,xvect2,ox,yvect2) + (ydim<<11);
9831                 i |= getclipmask(x-cx1,cx2-x,y-cy1,cy2-y);
9832                 rx1[npoints] = x;
9833                 ry1[npoints] = y;
9834                 xb1[npoints] = wal->point2 - startwall;
9835                 npoints++;
9836             }
9837 #else
9838             j = startwall; l = 0;
9839             uwallptr_t wal;
9840             int32_t w;
9841             for (w=sec->wallnum,wal=(uwallptr_t)&wall[startwall]; w>0; w--,wal++,j++)
9842             {
9843                 k = lastwall(j);
9844                 if ((k > j) && (npoints > 0)) { xb1[npoints-1] = l; l = npoints; } //overwrite point2
9845                 //wall[k].x wal->x wall[wal->point2].x
9846                 //wall[k].y wal->y wall[wal->point2].y
9847                 if (!dmulscale1(wal->x-wall[k].x,wall[wal->point2].y-wal->y,-(wal->y-wall[k].y),wall[wal->point2].x-wal->x)) continue;
9848                 ox = wal->x - dax; oy = wal->y - day;
9849                 x = dmulscale16(ox,vect.x,-oy,vect.y) + (xdim<<11);
9850                 y = dmulscale16(oy,vect2.x,ox,vect2.y) + (ydim<<11);
9851                 i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y);
9852                 rx1[npoints] = x;
9853                 ry1[npoints] = y;
9854                 xb1[npoints] = npoints+1;
9855                 npoints++;
9856             }
9857             if (npoints > 0) xb1[npoints-1] = l; //overwrite point2
9858 #endif
9859             if ((i&0xf0) != 0xf0) continue;
9860 
9861             vec2_t bak = { rx1[0], mulscale16(ry1[0]-(ydim<<11),xyaspect)+(ydim<<11) };
9862 
9863             if (i&0x0f)
9864             {
9865                 npoints = clippoly(npoints,i);
9866                 if (npoints < 3) continue;
9867             }
9868 
9869             //Collect floor sprites to draw
9870             for (i=headspritesect[s]; i>=0; i=nextspritesect[i])
9871             {
9872                 if (sprite[i].cstat & 32768)
9873                     continue;
9874 
9875                 if ((sprite[i].cstat & 48) >= 32)
9876                 {
9877                     if ((sprite[i].cstat & (64 + 8)) == (64 + 8))
9878                         continue;
9879                     tsprite[sortnum++].owner = i;
9880                 }
9881             }
9882             gotsector[s>>3] |= pow2char[s&7];
9883 
9884             globalorientation = (int32_t)sec->floorstat;
9885             if ((globalorientation&1) != 0) continue;
9886 
9887             globalpal = sec->floorpal;
9888 
9889             if (palookup[sec->floorpal] != globalpalwritten)
9890             {
9891                 globalpalwritten = palookup[sec->floorpal];
9892                 if (!globalpalwritten) globalpalwritten = palookup[0];	// JBF: fixes null-pointer crash
9893                 setpalookupaddress(globalpalwritten);
9894             }
9895             globalpicnum = sec->floorpicnum;
9896             if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
9897             tileUpdatePicnum(&globalpicnum, s);
9898             setgotpic(globalpicnum);
9899             if ((tilesiz[globalpicnum].x <= 0) || (tilesiz[globalpicnum].y <= 0)) continue;
9900             if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
9901             globalbufplc = waloff[globalpicnum];
9902             globalshade = max(min<int>(sec->floorshade,numshades-1),0);
9903             globvis = globalhisibility;
9904             if (sec->visibility != 0) globvis = mulscale4(globvis, (uint8_t)(sec->visibility+16));
9905             globalpolytype = 0;
9906             if ((globalorientation&64) == 0)
9907             {
9908                 set_globalpos(dax, day, globalposz);
9909                 globalx1 = bakgvect.x; globaly1 = bakgvect.y;
9910                 globalx2 = bakgvect.x; globaly2 = bakgvect.y;
9911             }
9912             else
9913             {
9914                 ox = wall[wall[startwall].point2].x - wall[startwall].x;
9915                 oy = wall[wall[startwall].point2].y - wall[startwall].y;
9916                 i = nsqrtasm(uhypsq(ox,oy)); if (i == 0) continue;
9917                 i = 1048576/i;
9918                 globalx1 = mulscale10(dmulscale10(ox,bakgvect.x,oy,bakgvect.y),i);
9919                 globaly1 = mulscale10(dmulscale10(ox,bakgvect.y,-oy,bakgvect.x),i);
9920                 ox = (bak.x>>4)-(xdim<<7); oy = (bak.y>>4)-(ydim<<7);
9921                 globalposx = dmulscale28(-oy, globalx1, -ox, globaly1);
9922                 globalposy = dmulscale28(-ox, globalx1, oy, globaly1);
9923                 globalx2 = -globalx1;
9924                 globaly2 = -globaly1;
9925 
9926                 int32_t const daslope = sector[s].floorheinum;
9927                 i = nsqrtasm(daslope*daslope+16777216);
9928                 set_globalpos(globalposx, mulscale12(globalposy,i), globalposz);
9929                 globalx2 = mulscale12(globalx2,i);
9930                 globaly2 = mulscale12(globaly2,i);
9931             }
9932 
9933             calc_globalshifts();
9934 
9935             sethlinesizes(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4,globalbufplc);
9936 
9937             if ((globalorientation&0x4) > 0)
9938             {
9939                 i = globalposx; globalposx = -globalposy; globalposy = -i;
9940                 i = globalx2; globalx2 = globaly1; globaly1 = i;
9941                 i = globalx1; globalx1 = -globaly2; globaly2 = -i;
9942             }
9943             if ((globalorientation&0x10) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalposx = -globalposx;
9944             if ((globalorientation&0x20) > 0) globalx2 = -globalx2, globaly2 = -globaly2, globalposy = -globalposy;
9945             asm1 = (globaly1<<globalxshift);
9946             asm2 = (globalx2<<globalyshift);
9947             globalx1 <<= globalxshift;
9948             globaly2 <<= globalyshift;
9949             set_globalpos(((int64_t) globalposx<<(20+globalxshift))+(((uint32_t) sec->floorxpanning)<<24),
9950                 ((int64_t) globalposy<<(20+globalyshift))-(((uint32_t) sec->floorypanning)<<24),
9951                 globalposz);
9952             renderFillPolygon(npoints);
9953         }
9954 
9955     //Sort sprite list
9956     int32_t gap = 1;
9957 
9958     while (gap < sortnum) gap = (gap << 1) + 1;
9959 
9960     for (gap>>=1; gap>0; gap>>=1)
9961         for (i=0; i<sortnum-gap; i++)
9962             for (j=i; j>=0; j-=gap)
9963             {
9964                 if (sprite[tsprite[j].owner].z <= sprite[tsprite[j+gap].owner].z) break;
9965                 swapshort(&tsprite[j].owner,&tsprite[j+gap].owner);
9966             }
9967 
9968     for (s=sortnum-1; s>=0; s--)
9969     {
9970         auto const spr = (uspritetype * )&sprite[tsprite[s].owner];
9971         if ((spr->cstat&48) >= 32)
9972         {
9973             const int32_t xspan = tilesiz[spr->picnum].x;
9974             const int32_t yspan = tilesiz[spr->picnum].y;
9975 
9976             int32_t npoints = 0;
9977             vec2_t v1 = { spr->x, spr->y }, v2, v3, v4;
9978 
9979             get_floorspr_points(spr, 0, 0, &v1.x, &v2.x, &v3.x, &v4.x,
9980                                 &v1.y, &v2.y, &v3.y, &v4.y, spriteGetSlope(tsprite[s].owner));
9981 
9982             xb1[0] = 1; xb1[1] = 2; xb1[2] = 3; xb1[3] = 0;
9983             npoints = 4;
9984 
9985             i = 0;
9986 
9987             ox = v1.x - dax; oy = v1.y - day;
9988             x = dmulscale16(ox,vect.x,-oy,vect.y) + (xdim<<11);
9989             y = dmulscale16(oy,vect2.x,ox,vect2.y) + (ydim<<11);
9990             i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y);
9991             rx1[0] = x; ry1[0] = y;
9992 
9993             ox = v2.x - dax; oy = v2.y - day;
9994             x = dmulscale16(ox,vect.x,-oy,vect.y) + (xdim<<11);
9995             y = dmulscale16(oy,vect2.x,ox,vect2.y) + (ydim<<11);
9996             i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y);
9997             rx1[1] = x; ry1[1] = y;
9998 
9999             ox = v3.x - dax; oy = v3.y - day;
10000             x = dmulscale16(ox,vect.x,-oy,vect.y) + (xdim<<11);
10001             y = dmulscale16(oy,vect2.x,ox,vect2.y) + (ydim<<11);
10002             i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y);
10003             rx1[2] = x; ry1[2] = y;
10004 
10005             x = rx1[0]+rx1[2]-rx1[1];
10006             y = ry1[0]+ry1[2]-ry1[1];
10007             i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y);
10008             rx1[3] = x; ry1[3] = y;
10009 
10010             if ((i&0xf0) != 0xf0) continue;
10011 
10012             vec2_t bak = { rx1[0], mulscale16(ry1[0] - (ydim << 11), xyaspect) + (ydim << 11) };
10013 
10014             if (i&0x0f)
10015             {
10016                 npoints = clippoly(npoints,i);
10017                 if (npoints < 3) continue;
10018             }
10019 
10020             globalpicnum = spr->picnum;
10021             globalpal = spr->pal; // GL needs this, software doesn't
10022             if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
10023             tileUpdatePicnum(&globalpicnum, s);
10024             setgotpic(globalpicnum);
10025             if ((tilesiz[globalpicnum].x <= 0) || (tilesiz[globalpicnum].y <= 0)) continue;
10026             if (waloff[globalpicnum] == 0) tileLoad(globalpicnum);
10027             globalbufplc = waloff[globalpicnum];
10028 
10029             // 'loading' the tile doesn't actually guarantee that it's there afterwards.
10030             // This can really happen when drawing the second frame of a floor-aligned
10031             // 'storm icon' sprite (4894+1)
10032             if (!globalbufplc)
10033                 continue;
10034 
10035             if ((sector[spr->sectnum].ceilingstat&1) > 0)
10036                 globalshade = ((int32_t)sector[spr->sectnum].ceilingshade);
10037             else
10038                 globalshade = ((int32_t)sector[spr->sectnum].floorshade);
10039             globalshade = max(min(globalshade+spr->shade+6,numshades-1),0);
10040             asm3 = FP_OFF(palookup[spr->pal]+(globalshade<<8));
10041             globvis = globalhisibility;
10042             if (sec->visibility != 0) globvis = mulscale4(globvis, (uint8_t)(sec->visibility+16));
10043             globalpolytype = ((spr->cstat&2)>>1)+1;
10044 
10045             //relative alignment stuff
10046             ox = v2.x-v1.x; oy = v2.y-v1.y;
10047             i = ox*ox+oy*oy; if (i == 0) continue; i = tabledivide32_noinline(65536*16384, i);
10048             globalx1 = mulscale10(dmulscale10(ox,bakgvect.x,oy,bakgvect.y),i);
10049             globaly1 = mulscale10(dmulscale10(ox,bakgvect.y,-oy,bakgvect.x),i);
10050             ox = v1.y-v4.y; oy = v4.x-v1.x;
10051             i = ox*ox+oy*oy; if (i == 0) continue; i = tabledivide32_noinline(65536*16384, i);
10052             globalx2 = mulscale10(dmulscale10(ox,bakgvect.x,oy,bakgvect.y),i);
10053             globaly2 = mulscale10(dmulscale10(ox,bakgvect.y,-oy,bakgvect.x),i);
10054 
10055             ox = picsiz[globalpicnum]; oy = ((ox>>4)&15); ox &= 15;
10056 
10057             bak.x = (bak.x>>4)-(xdim<<7); bak.y = (bak.y>>4)-(ydim<<7);
10058             globalposx = dmulscale28(-bak.y,globalx1,-bak.x,globaly1);
10059             globalposy = dmulscale28(bak.x,globalx2,-bak.y,globaly2);
10060 
10061             if ((spr->cstat&2) == 0)
10062                 msethlineshift(ox,oy);
10063             else
10064             {
10065                 setup_blend(spr->blend, spr->cstat&512);
10066                 tsethlineshift(ox,oy);
10067             }
10068 
10069             if ((spr->cstat&0x4) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalposx = -globalposx;
10070             asm1 = (globaly1<<2); globalx1 <<= 2; globalposx <<= (20+2);
10071             asm2 = (globalx2<<2); globaly2 <<= 2; globalposy <<= (20+2);
10072 
10073             set_globalpos(globalposx, globalposy, globalposz);
10074 
10075             globalispow2 = (pow2long[ox]==xspan && pow2long[oy]==yspan);
10076             globalxspan = xspan;
10077             globalyspan = yspan;
10078 
10079             // so polymost can get the translucency. ignored in software mode:
10080             globalorientation = ((spr->cstat&2)<<7) | ((spr->cstat&512)>>2);
10081             renderFillPolygon(npoints);
10082 
10083             globalispow2 = 1;
10084         }
10085     }
10086 
10087     videoEndDrawing();   //}}}
10088 
10089     if (r_usenewaspect)
10090         renderSetAspect(oviewingrange, oyxaspect);
10091     else
10092         renderSetAspect(65536, divscale16(ydim*320, xdim*200));
10093 }
10094 
10095 //////////////////// LOADING AND SAVING ROUTINES ////////////////////
10096 
have_maptext(void)10097 static FORCE_INLINE int32_t have_maptext(void)
10098 {
10099     return (mapversion >= 10);
10100 }
10101 
enginePrepareLoadBoard(buildvfs_kfd fil,vec3_t * dapos,int16_t * daang,int16_t * dacursectnum)10102 static int enginePrepareLoadBoard(buildvfs_kfd fil, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum)
10103 {
10104     initspritelists();
10105 
10106     Bmemset(show2dsector, 0, sizeof(show2dsector));
10107     Bmemset(show2dsprite, 0, sizeof(show2dsprite));
10108     Bmemset(show2dwall, 0, sizeof(show2dwall));
10109     Bmemset(editwall, 0, sizeof(editwall));
10110 #ifdef USE_STRUCT_TRACKERS
10111     Bmemset(sectorchanged, 0, sizeof(sectorchanged));
10112     Bmemset(spritechanged, 0, sizeof(spritechanged));
10113     Bmemset(wallchanged, 0, sizeof(wallchanged));
10114 #endif
10115 
10116 #ifdef USE_OPENGL
10117     Polymost_prepare_loadboard();
10118 #endif
10119 
10120     if (!have_maptext())
10121     {
10122         if (kread_and_test(fil, &dapos->x, 4)) return -1;
10123         dapos->x = B_LITTLE32(dapos->x);
10124         if (kread_and_test(fil, &dapos->y, 4)) return -1;
10125         dapos->y = B_LITTLE32(dapos->y);
10126         if (kread_and_test(fil, &dapos->z, 4)) return -1;
10127         dapos->z = B_LITTLE32(dapos->z);
10128         if (kread_and_test(fil, daang, 2)) return -1;
10129         *daang = B_LITTLE16(*daang) & 2047;
10130         if (kread_and_test(fil, dacursectnum, 2)) return -1;
10131         *dacursectnum = B_LITTLE16(*dacursectnum);
10132     }
10133 
10134     return 0;
10135 }
10136 
engineFinishLoadBoard(const vec3_t * dapos,int16_t * dacursectnum,int16_t numsprites,char myflags)10137 static int32_t engineFinishLoadBoard(const vec3_t *dapos, int16_t *dacursectnum, int16_t numsprites, char myflags)
10138 {
10139     int32_t i, realnumsprites=numsprites, numremoved;
10140 
10141 #if !defined USE_OPENGL || !defined POLYMER
10142     UNREFERENCED_PARAMETER(myflags);
10143 #endif
10144 
10145     for (i=0; i<numsprites; i++)
10146     {
10147         int32_t removeit = 0;
10148 
10149         if ((sprite[i].cstat & 48) == 48 && (sprite[i].xoffset|sprite[i].yoffset) == 0)
10150             sprite[i].cstat &= ~48;
10151 
10152         if (sprite[i].statnum == MAXSTATUS)
10153         {
10154             // Sprite was removed in loadboard() -> check_sprite(). Insert it
10155             // for now, because we must maintain the sprite numbering.
10156             sprite[i].statnum = sprite[i].sectnum = 0;
10157             removeit = 1;
10158         }
10159 
10160         insertsprite(sprite[i].sectnum, sprite[i].statnum);
10161 
10162         if (removeit)
10163         {
10164             // Flag .statnum==MAXSTATUS, temporarily creating an inconsistency
10165             // with sprite list.
10166             sprite[i].statnum = MAXSTATUS;
10167             realnumsprites--;
10168         }
10169     }
10170 
10171     if (numsprites != realnumsprites)
10172     {
10173         for (i=0; i<numsprites; i++)
10174             if (sprite[i].statnum == MAXSTATUS)
10175             {
10176                 // Now remove it for real!
10177                 sprite[i].statnum = 0;
10178                 deletesprite(i);
10179             }
10180     }
10181 
10182     numremoved = (numsprites-realnumsprites);
10183     numsprites = realnumsprites;
10184     Bassert(numsprites == Numsprites);
10185 
10186     //Must be after loading sectors, etc!
10187     updatesector(dapos->x, dapos->y, dacursectnum);
10188 
10189 #ifdef HAVE_CLIPSHAPE_FEATURE
10190     if (!quickloadboard)
10191 #endif
10192     {
10193         Bmemset(spriteext, 0, sizeof(spriteext_t)*MAXSPRITES);
10194 #ifndef NEW_MAP_FORMAT
10195         Bmemset(wallext, 0, sizeof(wallext_t)*MAXWALLS);
10196 #endif
10197 
10198 #ifdef USE_OPENGL
10199         Bmemset(spritesmooth, 0, sizeof(spritesmooth_t)*(MAXSPRITES+MAXUNIQHUDID));
10200 
10201 # ifdef POLYMER
10202         if (videoGetRenderMode() == REND_POLYMER)
10203         {
10204             if ((myflags&4)==0)
10205                 polymer_loadboard();
10206         }
10207 # endif
10208 #endif
10209     }
10210 
10211     guniqhudid = 0;
10212 
10213     Bmemset(tilecols, 0, sizeof(tilecols));
10214     return numremoved;
10215 }
10216 
10217 
10218 #define MYMAXSECTORS() (MAXSECTORS==MAXSECTORSV7 || mapversion <= 7 ? MAXSECTORSV7 : MAXSECTORSV8)
10219 #define MYMAXWALLS()   (MAXSECTORS==MAXSECTORSV7 || mapversion <= 7 ? MAXWALLSV7 : MAXWALLSV8)
10220 #define MYMAXSPRITES() (MAXSECTORS==MAXSECTORSV7 || mapversion <= 7 ? MAXSPRITESV7 : MAXSPRITESV8)
10221 
10222 // Sprite checking
10223 
remove_sprite(int32_t i)10224 static void remove_sprite(int32_t i)
10225 {
10226     Bmemset(&sprite[i], 0, sizeof(spritetype));
10227     sprite[i].statnum = MAXSTATUS;
10228     sprite[i].sectnum = MAXSECTORS;
10229 }
10230 
10231 // This is only to be run after reading the sprite array!
check_sprite(int32_t i)10232 static void check_sprite(int32_t i)
10233 {
10234     if ((unsigned)sprite[i].statnum >= MAXSTATUS)
10235     {
10236         initprintf("%sMap error: sprite #%d (%d,%d) with illegal statnum (%d) REMOVED.\n", osd->draw.errorfmt,
10237                    i, TrackerCast(sprite[i].x), TrackerCast(sprite[i].y), TrackerCast(sprite[i].statnum));
10238         remove_sprite(i);
10239     }
10240     else if ((unsigned)sprite[i].picnum >= MAXTILES)
10241     {
10242         initprintf("%sMap error: sprite #%d (%d,%d) with illegal picnum (%d) REMOVED.\n", osd->draw.errorfmt,
10243                    i, TrackerCast(sprite[i].x), TrackerCast(sprite[i].y), TrackerCast(sprite[i].sectnum));
10244         remove_sprite(i);
10245     }
10246     else if ((unsigned)sprite[i].sectnum >= (unsigned)numsectors)
10247     {
10248         const int32_t osectnum = sprite[i].sectnum;
10249 
10250         sprite[i].sectnum = -1;
10251         updatesector(sprite[i].x, sprite[i].y, &sprite[i].sectnum);
10252 
10253         if (sprite[i].sectnum < 0)
10254             remove_sprite(i);
10255 
10256         initprintf("%sMap error: sprite #%d (%d,%d) with illegal sector (%d) ", osd->draw.errorfmt,
10257                    i, TrackerCast(sprite[i].x), TrackerCast(sprite[i].y), osectnum);
10258 
10259         if (sprite[i].statnum != MAXSTATUS)
10260             initprintf("changed to sector %d.\n", TrackerCast(sprite[i].sectnum));
10261         else
10262             initprintf("REMOVED.\n");
10263     }
10264 }
10265 
10266 #ifdef NEW_MAP_FORMAT
10267 // Returns the number of sprites, or <0 on error.
10268 int32_t (*loadboard_maptext)(buildvfs_kfd fil, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum);
10269 #endif
10270 
10271 #include "md4.h"
10272 
10273 int32_t(*loadboard_replace)(const char *filename, char flags, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum) = NULL;
10274 
10275 // flags: 1, 2: former parameter "fromwhere"
10276 //           4: don't call polymer_loadboard
10277 //           8: don't autoexec <mapname>.cfg
10278 // returns: on success, number of removed sprites
10279 //          -1: file not found
10280 //          -2: invalid version
10281 //          -3: invalid number of sectors, walls or sprites
10282 //       <= -4: map-text error
engineLoadBoard(const char * filename,char flags,vec3_t * dapos,int16_t * daang,int16_t * dacursectnum)10283 int32_t engineLoadBoard(const char *filename, char flags, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum)
10284 {
10285     if (loadboard_replace)
10286         return loadboard_replace(filename, flags, dapos, daang, dacursectnum);
10287     int32_t i;
10288     int16_t numsprites;
10289     const char myflags = flags&(~3);
10290 
10291     flags &= 3;
10292 
10293     buildvfs_kfd fil;
10294     if ((fil = kopen4load(filename,flags)) == buildvfs_kfd_invalid)
10295         { mapversion = 7; return -1; }
10296 
10297     if (kread(fil, &mapversion, 4) != 4)
10298     {
10299         kclose(fil);
10300         return -2;
10301     }
10302 
10303     {
10304         int32_t ok = 0;
10305 
10306 #ifdef NEW_MAP_FORMAT
10307         // Check for map-text first.
10308         if (!Bmemcmp(&mapversion, "--ED", 4))
10309         {
10310             mapversion = 10;
10311             ok = 1;
10312         }
10313         else
10314 #endif
10315         {
10316             // Not map-text. We expect a little-endian version int now.
10317             mapversion = B_LITTLE32(mapversion);
10318 #ifdef YAX_ENABLE
10319             ok |= (mapversion==9);
10320 #endif
10321 #if MAXSECTORS==MAXSECTORSV8
10322             // v8 engine
10323             ok |= (mapversion==8);
10324 #endif
10325             ok |= (mapversion==7);
10326         }
10327 
10328         if (!ok)
10329         {
10330             kclose(fil);
10331             return -2;
10332         }
10333     }
10334 
10335     if (enginePrepareLoadBoard(fil, dapos, daang, dacursectnum)) goto error;
10336 
10337 #ifdef NEW_MAP_FORMAT
10338     if (have_maptext())
10339     {
10340         int32_t ret = klseek(fil, 0, SEEK_SET);
10341 
10342         if (ret == 0)
10343             ret = loadboard_maptext(fil, dapos, daang, dacursectnum);
10344 
10345         if (ret < 0)
10346         {
10347             kclose(fil);
10348             return ret;
10349         }
10350 
10351         numsprites = ret;
10352         goto skip_reading_mapbin;
10353     }
10354 #endif
10355 
10356     ////////// Read sectors //////////
10357 
10358     if (kread_and_test(fil,&numsectors,2)) goto error;
10359     numsectors = B_LITTLE16(numsectors);
10360     if ((unsigned)numsectors >= MYMAXSECTORS() + 1)
10361     {
10362     error:
10363         numsectors = 0;
10364         numwalls   = 0;
10365         numsprites = 0;
10366         kclose(fil);
10367         return -3;
10368     }
10369 
10370     if (kread_and_test(fil, sector, sizeof(sectortypev7)*numsectors)) goto error;
10371 
10372     for (i=numsectors-1; i>=0; i--)
10373     {
10374 #ifdef NEW_MAP_FORMAT
10375         Bmemmove(&sector[i], &(((sectortypev7 *)sector)[i]), sizeof(sectortypevx));
10376         inplace_vx_from_v7_sector(&sector[i]);
10377 #endif
10378         sector[i].wallptr       = B_LITTLE16(sector[i].wallptr);
10379         sector[i].wallnum       = B_LITTLE16(sector[i].wallnum);
10380         sector[i].ceilingz      = B_LITTLE32(sector[i].ceilingz);
10381         sector[i].floorz        = B_LITTLE32(sector[i].floorz);
10382         sector[i].ceilingstat   = B_LITTLE16(sector[i].ceilingstat);
10383         sector[i].floorstat     = B_LITTLE16(sector[i].floorstat);
10384         sector[i].ceilingpicnum = B_LITTLE16(sector[i].ceilingpicnum);
10385         sector[i].ceilingheinum = B_LITTLE16(sector[i].ceilingheinum);
10386         sector[i].floorpicnum   = B_LITTLE16(sector[i].floorpicnum);
10387         sector[i].floorheinum   = B_LITTLE16(sector[i].floorheinum);
10388         sector[i].lotag         = B_LITTLE16(sector[i].lotag);
10389         sector[i].hitag         = B_LITTLE16(sector[i].hitag);
10390         sector[i].extra         = B_LITTLE16(sector[i].extra);
10391 #ifdef NEW_MAP_FORMAT
10392         inplace_vx_tweak_sector(&sector[i], mapversion==9);
10393 #endif
10394     }
10395 
10396 
10397     ////////// Read walls //////////
10398 
10399     if (kread_and_test(fil,&numwalls,2)) goto error;
10400     numwalls = B_LITTLE16(numwalls);
10401     if ((unsigned)numwalls >= MYMAXWALLS()+1) goto error;
10402 
10403     if (kread_and_test(fil, wall, sizeof(walltypev7)*numwalls)) goto error;
10404 
10405     for (i=numwalls-1; i>=0; i--)
10406     {
10407 #ifdef NEW_MAP_FORMAT
10408         Bmemmove(&wall[i], &(((walltypev7 *)wall)[i]), sizeof(walltypevx));
10409         inplace_vx_from_v7_wall(&wall[i]);
10410 #endif
10411         wall[i].x          = B_LITTLE32(wall[i].x);
10412         wall[i].y          = B_LITTLE32(wall[i].y);
10413         wall[i].point2     = B_LITTLE16(wall[i].point2);
10414         wall[i].nextwall   = B_LITTLE16(wall[i].nextwall);
10415         wall[i].nextsector = B_LITTLE16(wall[i].nextsector);
10416         wall[i].cstat      = B_LITTLE16(wall[i].cstat);
10417         wall[i].picnum     = B_LITTLE16(wall[i].picnum);
10418         wall[i].overpicnum = B_LITTLE16(wall[i].overpicnum);
10419         wall[i].lotag      = B_LITTLE16(wall[i].lotag);
10420         wall[i].hitag      = B_LITTLE16(wall[i].hitag);
10421         wall[i].extra      = B_LITTLE16(wall[i].extra);
10422 #ifdef NEW_MAP_FORMAT
10423         inplace_vx_tweak_wall(&wall[i], mapversion==9);
10424 #endif
10425     }
10426 
10427 
10428     ////////// Read sprites //////////
10429 
10430     if (kread_and_test(fil,&numsprites,2)) goto error;
10431     numsprites = B_LITTLE16(numsprites);
10432     if ((unsigned)numsprites >= MYMAXSPRITES()+1) goto error;
10433 
10434     if (kread_and_test(fil, sprite, sizeof(spritetype)*numsprites)) goto error;
10435 
10436 #ifdef NEW_MAP_FORMAT
10437 skip_reading_mapbin:
10438 #endif
10439 
10440     klseek(fil, 0, SEEK_SET);
10441     int32_t boardsize = kfilelength(fil);
10442     uint8_t *fullboard = (uint8_t*)Xmalloc(boardsize);
10443     if (kread_and_test(fil, fullboard, boardsize)) { Xfree(fullboard); goto error; }
10444     md4once(fullboard, boardsize, g_loadedMapHack.md4);
10445     Xfree(fullboard);
10446 
10447     kclose(fil);
10448     // Done reading file.
10449 
10450     if (!have_maptext())
10451     {
10452         for (i=numsprites-1; i>=0; i--)
10453         {
10454             sprite[i].x       = B_LITTLE32(sprite[i].x);
10455             sprite[i].y       = B_LITTLE32(sprite[i].y);
10456             sprite[i].z       = B_LITTLE32(sprite[i].z);
10457             sprite[i].cstat   = B_LITTLE16(sprite[i].cstat);
10458             sprite[i].picnum  = B_LITTLE16(sprite[i].picnum);
10459             sprite[i].sectnum = B_LITTLE16(sprite[i].sectnum);
10460             sprite[i].statnum = B_LITTLE16(sprite[i].statnum);
10461             sprite[i].ang     = B_LITTLE16(sprite[i].ang);
10462             sprite[i].owner   = B_LITTLE16(sprite[i].owner);
10463             sprite[i].xvel    = B_LITTLE16(sprite[i].xvel);
10464             sprite[i].yvel    = B_LITTLE16(sprite[i].yvel);
10465             sprite[i].zvel    = B_LITTLE16(sprite[i].zvel);
10466             sprite[i].lotag   = B_LITTLE16(sprite[i].lotag);
10467             sprite[i].hitag   = B_LITTLE16(sprite[i].hitag);
10468             sprite[i].extra   = B_LITTLE16(sprite[i].extra);
10469 
10470             check_sprite(i);
10471         }
10472     }
10473     else
10474     {
10475         for (i=numsprites-1; i>=0; i--)
10476             check_sprite(i);
10477     }
10478 
10479     // Back up the map version of the *loaded* map. Must be before yax_update().
10480     g_loadedMapVersion = mapversion;
10481 #ifdef YAX_ENABLE
10482     yax_update(mapversion<9);
10483     if (editstatus)
10484         yax_updategrays(dapos->z);
10485 #endif
10486 
10487     if ((myflags&8)==0)
10488     {
10489         char fn[BMAX_PATH];
10490 
10491         Bstrcpy(fn, filename);
10492         append_ext_UNSAFE(fn, ".cfg");
10493 
10494         OSD_Exec(fn);
10495 
10496         system_getcvars();
10497 
10498         // Per-map ART
10499         artSetupMapArt(filename);
10500     }
10501 
10502     // initprintf("Loaded map \"%s\" (md4sum: %08x%08x%08x%08x)\n", filename, B_BIG32(*((int32_t*)&md4out[0])), B_BIG32(*((int32_t*)&md4out[4])), B_BIG32(*((int32_t*)&md4out[8])), B_BIG32(*((int32_t*)&md4out[12])));
10503 
10504     return engineFinishLoadBoard(dapos, dacursectnum, numsprites, myflags);
10505 }
10506 
10507 
10508 //
10509 // loadboardv5/6
10510 //
10511 #include "engine_oldmap.h"
10512 
10513 // Powerslave uses v6
10514 // Witchaven 1 and TekWar and LameDuke use v5
engineLoadBoardV5V6(const char * filename,char fromwhere,vec3_t * dapos,int16_t * daang,int16_t * dacursectnum)10515 int32_t engineLoadBoardV5V6(const char *filename, char fromwhere, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum)
10516 {
10517     buildvfs_kfd fil;
10518 
10519     if ((fil = kopen4load(filename, fromwhere)) == buildvfs_kfd_invalid)
10520     {
10521         mapversion = 5L;
10522         return -1;
10523     }
10524 
10525     if (kread_and_test(fil, &mapversion, sizeof(int32_t)))
10526         goto error;
10527 
10528     mapversion = B_LITTLE32(mapversion);
10529 
10530     if (mapversion != 5L && mapversion != 6L)
10531     {
10532         kclose(fil);
10533         return -2;
10534     }
10535 
10536     if (enginePrepareLoadBoard(fil, dapos, daang, dacursectnum))
10537         goto error;
10538 
10539     if (kread_and_test(fil, &numsectors, sizeof(int16_t)))
10540         goto error;
10541 
10542     numsectors = B_LITTLE16(numsectors);
10543 
10544     if (numsectors > MAXSECTORS)
10545     {
10546     error:
10547         kclose(fil);
10548         return -1;
10549     }
10550 
10551     switch (mapversion)
10552     {
10553         case 5:
10554         {
10555             struct sectortypev5 v5sect;
10556             struct sectortypev6 v6sect;
10557 
10558             for (int i = 0; i < numsectors; i++)
10559             {
10560                 if (kread_and_test(fil, &v5sect, sizeof(struct sectortypev5)))
10561                     goto error;
10562 
10563                 v5sect.wallptr       = B_LITTLE16(v5sect.wallptr);
10564                 v5sect.wallnum       = B_LITTLE16(v5sect.wallnum);
10565                 v5sect.ceilingpicnum = B_LITTLE16(v5sect.ceilingpicnum);
10566                 v5sect.floorpicnum   = B_LITTLE16(v5sect.floorpicnum);
10567                 v5sect.ceilingheinum = B_LITTLE16(v5sect.ceilingheinum);
10568                 v5sect.floorheinum   = B_LITTLE16(v5sect.floorheinum);
10569                 v5sect.ceilingz      = B_LITTLE32(v5sect.ceilingz);
10570                 v5sect.floorz        = B_LITTLE32(v5sect.floorz);
10571                 v5sect.lotag         = B_LITTLE16(v5sect.lotag);
10572                 v5sect.hitag         = B_LITTLE16(v5sect.hitag);
10573                 v5sect.extra         = B_LITTLE16(v5sect.extra);
10574 
10575                 convertv5sectv6(&v5sect, &v6sect);
10576                 convertv6sectv7(&v6sect, &sector[i]);
10577             }
10578             break;
10579         }
10580 
10581         case 6:
10582         {
10583             struct sectortypev6 v6sect;
10584 
10585             for (int i = 0; i < numsectors; i++)
10586             {
10587                 if (kread_and_test(fil, &v6sect, sizeof(struct sectortypev6)))
10588                     goto error;
10589 
10590                 v6sect.wallptr       = B_LITTLE16(v6sect.wallptr);
10591                 v6sect.wallnum       = B_LITTLE16(v6sect.wallnum);
10592                 v6sect.ceilingpicnum = B_LITTLE16(v6sect.ceilingpicnum);
10593                 v6sect.floorpicnum   = B_LITTLE16(v6sect.floorpicnum);
10594                 v6sect.ceilingheinum = B_LITTLE16(v6sect.ceilingheinum);
10595                 v6sect.floorheinum   = B_LITTLE16(v6sect.floorheinum);
10596                 v6sect.ceilingz      = B_LITTLE32(v6sect.ceilingz);
10597                 v6sect.floorz        = B_LITTLE32(v6sect.floorz);
10598                 v6sect.lotag         = B_LITTLE16(v6sect.lotag);
10599                 v6sect.hitag         = B_LITTLE16(v6sect.hitag);
10600                 v6sect.extra         = B_LITTLE16(v6sect.extra);
10601 
10602                 convertv6sectv7(&v6sect, &sector[i]);
10603             }
10604             break;
10605         }
10606     }
10607 
10608     if (kread_and_test(fil, &numwalls, sizeof(int16_t)))
10609         goto error;
10610 
10611     numwalls = B_LITTLE16(numwalls);
10612 
10613     if (numwalls > MAXWALLS)
10614         goto error;
10615 
10616     switch (mapversion)
10617     {
10618         case 5:
10619         {
10620             struct walltypev5 v5wall;
10621             struct walltypev6 v6wall;
10622 
10623             for (int i = 0; i < numwalls; i++)
10624             {
10625                 if (kread_and_test(fil, &v5wall, sizeof(struct walltypev5)))
10626                     goto error;
10627 
10628                 v5wall.x           = B_LITTLE32(v5wall.x);
10629                 v5wall.y           = B_LITTLE32(v5wall.y);
10630                 v5wall.point2      = B_LITTLE16(v5wall.point2);
10631                 v5wall.picnum      = B_LITTLE16(v5wall.picnum);
10632                 v5wall.overpicnum  = B_LITTLE16(v5wall.overpicnum);
10633                 v5wall.cstat       = B_LITTLE16(v5wall.cstat);
10634                 v5wall.nextsector1 = B_LITTLE16(v5wall.nextsector1);
10635                 v5wall.nextwall1   = B_LITTLE16(v5wall.nextwall1);
10636                 v5wall.nextsector2 = B_LITTLE16(v5wall.nextsector2);
10637                 v5wall.nextwall2   = B_LITTLE16(v5wall.nextwall2);
10638                 v5wall.lotag       = B_LITTLE16(v5wall.lotag);
10639                 v5wall.hitag       = B_LITTLE16(v5wall.hitag);
10640                 v5wall.extra       = B_LITTLE16(v5wall.extra);
10641 
10642                 convertv5wallv6(&v5wall, &v6wall, i);
10643                 convertv6wallv7(&v6wall, &wall[i]);
10644             }
10645             break;
10646         }
10647 
10648         case 6:
10649         {
10650             struct walltypev6 v6wall;
10651 
10652             for (int i = 0; i < numwalls; i++)
10653             {
10654                 if (kread_and_test(fil, &v6wall, sizeof(struct walltypev6)))
10655                     goto error;
10656 
10657                 v6wall.x          = B_LITTLE32(v6wall.x);
10658                 v6wall.y          = B_LITTLE32(v6wall.y);
10659                 v6wall.point2     = B_LITTLE16(v6wall.point2);
10660                 v6wall.nextsector = B_LITTLE16(v6wall.nextsector);
10661                 v6wall.nextwall   = B_LITTLE16(v6wall.nextwall);
10662                 v6wall.picnum     = B_LITTLE16(v6wall.picnum);
10663                 v6wall.overpicnum = B_LITTLE16(v6wall.overpicnum);
10664                 v6wall.cstat      = B_LITTLE16(v6wall.cstat);
10665                 v6wall.lotag      = B_LITTLE16(v6wall.lotag);
10666                 v6wall.hitag      = B_LITTLE16(v6wall.hitag);
10667                 v6wall.extra      = B_LITTLE16(v6wall.extra);
10668 
10669                 convertv6wallv7(&v6wall, &wall[i]);
10670             }
10671             break;
10672         }
10673     }
10674 
10675     int16_t numsprites;
10676 
10677     if (kread_and_test(fil, &numsprites, sizeof(int16_t)))
10678         goto error;
10679 
10680     numsprites = B_LITTLE16(numsprites);
10681 
10682     if (numsprites > MAXSPRITES)
10683         goto error;
10684 
10685     switch (mapversion)
10686     {
10687         case 5:
10688         {
10689             struct spritetypev5 v5spr;
10690             struct spritetypev6 v6spr;
10691 
10692             for (int i = 0; i < numsprites; i++)
10693             {
10694                 if (kread_and_test(fil, &v5spr, sizeof(struct spritetypev5)))
10695                     goto error;
10696 
10697                 v5spr.x       = B_LITTLE32(v5spr.x);
10698                 v5spr.y       = B_LITTLE32(v5spr.y);
10699                 v5spr.z       = B_LITTLE32(v5spr.z);
10700                 v5spr.picnum  = B_LITTLE16(v5spr.picnum);
10701                 v5spr.ang     = B_LITTLE16(v5spr.ang);
10702                 v5spr.xvel    = B_LITTLE16(v5spr.xvel);
10703                 v5spr.yvel    = B_LITTLE16(v5spr.yvel);
10704                 v5spr.zvel    = B_LITTLE16(v5spr.zvel);
10705                 v5spr.owner   = B_LITTLE16(v5spr.owner);
10706                 v5spr.sectnum = B_LITTLE16(v5spr.sectnum);
10707                 v5spr.statnum = B_LITTLE16(v5spr.statnum);
10708                 v5spr.lotag   = B_LITTLE16(v5spr.lotag);
10709                 v5spr.hitag   = B_LITTLE16(v5spr.hitag);
10710                 v5spr.extra   = B_LITTLE16(v5spr.extra);
10711 
10712                 convertv5sprv6(&v5spr, &v6spr);
10713                 convertv6sprv7(&v6spr, &sprite[i]);
10714                 check_sprite(i);
10715             }
10716             break;
10717         }
10718 
10719         case 6:
10720         {
10721             struct spritetypev6 v6spr;
10722 
10723             for (int i = 0; i < numsprites; i++)
10724             {
10725                 if (kread_and_test(fil, &v6spr, sizeof(struct spritetypev6)))
10726                     goto error;
10727 
10728                 v6spr.x       = B_LITTLE32(v6spr.x);
10729                 v6spr.y       = B_LITTLE32(v6spr.y);
10730                 v6spr.z       = B_LITTLE32(v6spr.z);
10731                 v6spr.cstat   = B_LITTLE16(v6spr.cstat);
10732                 v6spr.picnum  = B_LITTLE16(v6spr.picnum);
10733                 v6spr.ang     = B_LITTLE16(v6spr.ang);
10734                 v6spr.xvel    = B_LITTLE16(v6spr.xvel);
10735                 v6spr.yvel    = B_LITTLE16(v6spr.yvel);
10736                 v6spr.zvel    = B_LITTLE16(v6spr.zvel);
10737                 v6spr.owner   = B_LITTLE16(v6spr.owner);
10738                 v6spr.sectnum = B_LITTLE16(v6spr.sectnum);
10739                 v6spr.statnum = B_LITTLE16(v6spr.statnum);
10740                 v6spr.lotag   = B_LITTLE16(v6spr.lotag);
10741                 v6spr.hitag   = B_LITTLE16(v6spr.hitag);
10742                 v6spr.extra   = B_LITTLE16(v6spr.extra);
10743 
10744                 convertv6sprv7(&v6spr, &sprite[i]);
10745                 check_sprite(i);
10746             }
10747             break;
10748         }
10749     }
10750 
10751     kclose(fil);
10752     // Done reading file.
10753 
10754     g_loadedMapVersion = mapversion;
10755 
10756     return engineFinishLoadBoard(dapos, dacursectnum, numsprites, 0);
10757 }
10758 
10759 
10760 #ifdef NEW_MAP_FORMAT
10761 int32_t (*saveboard_maptext)(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum);
10762 #endif
10763 
10764 // Get map version of external map format (<10: old binary format, ==10: new
10765 // 'VX' map-text format).
get_mapversion(void)10766 static int32_t get_mapversion(void)
10767 {
10768 #ifdef YAX_ENABLE
10769     if (numyaxbunches > 0)
10770 # ifdef NEW_MAP_FORMAT
10771         return 10;
10772 # else
10773         return 9;
10774 # endif
10775 #endif
10776 
10777 #ifdef NEW_MAP_FORMAT
10778     {
10779         int32_t i;
10780         for (i=0; i<numwalls; i++)
10781             if (wall[i].blend != 0)
10782                 return 10;
10783     }
10784 #endif
10785     if (numsectors > MAXSECTORSV7 || numwalls > MAXWALLSV7 || Numsprites > MAXSPRITESV7)
10786         return 8;
10787 
10788     return 7;
10789 }
10790 
10791 //
10792 // saveboard
10793 //
10794 int32_t(*saveboard_replace)(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum) = NULL;
saveboard(const char * filename,const vec3_t * dapos,int16_t daang,int16_t dacursectnum)10795 int32_t saveboard(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum)
10796 {
10797     if (saveboard_replace)
10798         return saveboard_replace(filename, dapos, daang, dacursectnum);
10799 
10800     int16_t numsprites, ts;
10801     int32_t i, j, tl;
10802 
10803     // First, some checking.
10804     for (j=0; j<MAXSPRITES; j++)
10805     {
10806         if ((unsigned)sprite[j].statnum > MAXSTATUS)
10807         {
10808             initprintf("Map error: sprite #%d(%d,%d) with an illegal statnum(%d)\n",
10809                        j,TrackerCast(sprite[j].x),TrackerCast(sprite[j].y),TrackerCast(sprite[j].statnum));
10810             changespritestat(j,0);
10811         }
10812 
10813         if ((unsigned)sprite[j].sectnum > MAXSECTORS)
10814         {
10815             initprintf("Map error: sprite #%d(%d,%d) with an illegal sectnum(%d)\n",
10816                        j,TrackerCast(sprite[j].x),TrackerCast(sprite[j].y),TrackerCast(sprite[j].sectnum));
10817             changespritesect(j,0);
10818         }
10819     }
10820 
10821     // Count the number of sprites.
10822     numsprites = 0;
10823     for (j=0; j<MAXSPRITES; j++)
10824     {
10825         if (sprite[j].statnum != MAXSTATUS)
10826             numsprites++;
10827     }
10828 
10829     // Check consistency of sprite-in-the-world predicate (.statnum != MAXSTATUS)
10830     // and the engine-reported number of sprites 'Numsprites'.
10831     Bassert(numsprites == Numsprites);
10832 
10833     // Determine the map version.
10834     mapversion = get_mapversion();
10835 
10836 #ifdef NEW_MAP_FORMAT
10837     if (mapversion == 10)
10838     {
10839         initprintf("Saving of TROR maps not yet accessible in the Lunatic preview build\n");
10840         return -1;
10841 //        return saveboard_maptext(filename, dapos, daang, dacursectnum);
10842     }
10843 #endif
10844 
10845     buildvfs_fd fil = buildvfs_open_write(filename);
10846 
10847     if (fil == buildvfs_fd_invalid)
10848     {
10849         initprintf("Couldn't open \"%s\" for writing: %s\n", filename, strerror(errno));
10850         return -1;
10851     }
10852 
10853     tl = B_LITTLE32(mapversion);    buildvfs_write(fil,&tl,4);
10854 
10855     tl = B_LITTLE32(dapos->x);      buildvfs_write(fil,&tl,4);
10856     tl = B_LITTLE32(dapos->y);      buildvfs_write(fil,&tl,4);
10857     tl = B_LITTLE32(dapos->z);      buildvfs_write(fil,&tl,4);
10858     ts = B_LITTLE16(daang);        buildvfs_write(fil,&ts,2);
10859     ts = B_LITTLE16(dacursectnum); buildvfs_write(fil,&ts,2);
10860 
10861     ts = B_LITTLE16(numsectors);    buildvfs_write(fil,&ts,2);
10862 
10863     while (1)  // if, really
10864     {
10865         usectortypev7 *const tsect = (usectortypev7 *)Xmalloc(sizeof(usectortypev7) * numsectors);
10866         uwalltypev7 *twall;
10867 
10868 #ifdef NEW_MAP_FORMAT
10869         for (i=0; i<numsectors; i++)
10870             copy_v7_from_vx_sector(&tsect[i], &sector[i]);
10871 #else
10872         Bmemcpy(tsect, sector, sizeof(sectortypev7)*numsectors);
10873 #endif
10874 
10875         for (i=0; i<numsectors; i++)
10876         {
10877             usectortypev7 *const sec = &tsect[i];
10878 
10879             sec->wallptr       = B_LITTLE16(sec->wallptr);
10880             sec->wallnum       = B_LITTLE16(sec->wallnum);
10881             sec->ceilingz      = B_LITTLE32(sec->ceilingz);
10882             sec->floorz        = B_LITTLE32(sec->floorz);
10883             sec->ceilingstat   = B_LITTLE16(sec->ceilingstat);
10884             sec->floorstat     = B_LITTLE16(sec->floorstat);
10885             sec->ceilingpicnum = B_LITTLE16(sec->ceilingpicnum);
10886             sec->ceilingheinum = B_LITTLE16(sec->ceilingheinum);
10887             sec->floorpicnum   = B_LITTLE16(sec->floorpicnum);
10888             sec->floorheinum   = B_LITTLE16(sec->floorheinum);
10889             sec->lotag         = B_LITTLE16(sec->lotag);
10890             sec->hitag         = B_LITTLE16(sec->hitag);
10891             sec->extra         = B_LITTLE16(sec->extra);
10892 #ifdef YAX_ENABLE__COMPAT
10893             if (editstatus == 0)
10894             {
10895                 // if in-game, pack game-time bunchnum data back into structs
10896                 int32_t cf, bn;
10897 
10898                 for (cf=0; cf<2; cf++)
10899                     if ((bn=yax_getbunch(i, cf)) >= 0)
10900                         YAX_PTRBUNCHNUM(tsect, i, cf) = bn;
10901             }
10902 #endif
10903         }
10904 
10905         buildvfs_write(fil, tsect, sizeof(sectortypev7)*numsectors);
10906         Xfree(tsect);
10907 
10908         ts = B_LITTLE16(numwalls);
10909         buildvfs_write(fil,&ts,2);
10910 
10911         twall = (uwalltypev7 *)Xmalloc(sizeof(uwalltypev7) * numwalls);
10912 
10913 #ifdef NEW_MAP_FORMAT
10914         for (i=0; i<numwalls; i++)
10915             copy_v7_from_vx_wall(&twall[i], &wall[i]);
10916 #else
10917         Bmemcpy(twall, wall, sizeof(walltypev7)*numwalls);
10918 #endif
10919 
10920         for (i=0; i<numwalls; i++)
10921         {
10922             uwalltypev7 *const wal = &twall[i];
10923 
10924             wal->x          = B_LITTLE32(wal->x);
10925             wal->y          = B_LITTLE32(wal->y);
10926             wal->point2     = B_LITTLE16(wal->point2);
10927             wal->nextwall   = B_LITTLE16(wal->nextwall);
10928             wal->nextsector = B_LITTLE16(wal->nextsector);
10929             wal->cstat      = B_LITTLE16(wal->cstat);
10930             wal->picnum     = B_LITTLE16(wal->picnum);
10931             wal->overpicnum = B_LITTLE16(wal->overpicnum);
10932 #ifdef YAX_ENABLE__COMPAT
10933             if (editstatus == 0)
10934             {
10935                 // if in-game, pack game-time yax-nextwall data back into structs
10936                 int16_t ynw;
10937                 if ((ynw=yax_getnextwall(i, YAX_CEILING))>=0)
10938                     YAX_PTRNEXTWALL(twall,i,YAX_CEILING) = ynw;
10939                 if ((ynw=yax_getnextwall(i, YAX_FLOOR))>=0)
10940                     YAX_PTRNEXTWALL(twall,i,YAX_FLOOR) = ynw;
10941             }
10942 #endif
10943             wal->lotag      = B_LITTLE16(wal->lotag);
10944             wal->hitag      = B_LITTLE16(wal->hitag);
10945             wal->extra      = B_LITTLE16(wal->extra);
10946         }
10947 
10948         buildvfs_write(fil, twall, sizeof(walltypev7)*numwalls);
10949         Xfree(twall);
10950 
10951         ts = B_LITTLE16(numsprites);    buildvfs_write(fil,&ts,2);
10952 
10953         if (numsprites > 0)
10954         {
10955             auto const uspri = (uspritetype *)Xmalloc(sizeof(spritetype) * numsprites);
10956             auto spri = uspri;
10957 
10958             for (j=0; j<MAXSPRITES; j++)
10959             {
10960                 if (sprite[j].statnum != MAXSTATUS)
10961                 {
10962                     Bmemcpy(spri, &sprite[j], sizeof(spritetype));
10963                     spri->x       = B_LITTLE32(spri->x);
10964                     spri->y       = B_LITTLE32(spri->y);
10965                     spri->z       = B_LITTLE32(spri->z);
10966                     spri->cstat   = B_LITTLE16(spri->cstat);
10967                     spri->picnum  = B_LITTLE16(spri->picnum);
10968                     spri->sectnum = B_LITTLE16(spri->sectnum);
10969                     spri->statnum = B_LITTLE16(spri->statnum);
10970                     spri->ang     = B_LITTLE16(spri->ang);
10971                     spri->owner   = B_LITTLE16(spri->owner);
10972                     spri->xvel    = B_LITTLE16(spri->xvel);
10973                     spri->yvel    = B_LITTLE16(spri->yvel);
10974                     spri->zvel    = B_LITTLE16(spri->zvel);
10975                     spri->lotag   = B_LITTLE16(spri->lotag);
10976                     spri->hitag   = B_LITTLE16(spri->hitag);
10977                     spri->extra   = B_LITTLE16(spri->extra);
10978                     spri++;
10979                 }
10980             }
10981 
10982             buildvfs_write(fil, uspri, sizeof(spritetype)*numsprites);
10983             Xfree(uspri);
10984         }
10985 
10986         buildvfs_close(fil);
10987         return 0;
10988     }
10989 
10990     buildvfs_close(fil);
10991     return -1;
10992 }
10993 
10994 #define YSAVES ((xdim*MAXSPRITES)>>7)
10995 
videoAllocateBuffers(void)10996 static void videoAllocateBuffers(void)
10997 {
10998     int32_t i;
10999     // Needed for the game's TILT_SETVIEWTOTILE_320.
11000     const int32_t clamped_ydim = max(ydim, 320);
11001 
11002     struct
11003     {
11004         void **ptr;
11005         size_t size;
11006     } dynarray[] = {
11007           { (void **)&smost, YSAVES * sizeof(int16_t) },
11008           { (void **)&umost, xdim * sizeof(int16_t) },
11009           { (void **)&dmost, xdim * sizeof(int16_t) },
11010           { (void **)&startumost, xdim * sizeof(int16_t) },
11011           { (void **)&startdmost, xdim * sizeof(int16_t) },
11012           { (void **)&bakumost, xdim * sizeof(int16_t) },
11013           { (void **)&bakdmost, xdim * sizeof(int16_t) },
11014           { (void **)&uplc, xdim * sizeof(int16_t) },
11015           { (void **)&dplc, xdim * sizeof(int16_t) },
11016           { (void **)&uwall, xdim * sizeof(int16_t) },
11017           { (void **)&dwall, xdim * sizeof(int16_t) },
11018           { (void **)&swplc, xdim * sizeof(int32_t) },
11019           { (void **)&lplc, xdim * sizeof(int32_t) },
11020           { (void **)&swall, xdim * sizeof(int32_t) },
11021 #ifdef HIGH_PRECISION_SPRITE
11022           { (void **)&swallf, xdim * sizeof(float) },
11023 #endif
11024           { (void **)&lwall, (xdim + 4) * sizeof(int32_t) },
11025           { (void **)&radarang2, xdim * sizeof(int32_t) },
11026           { (void **)&dotp1, clamped_ydim * sizeof(intptr_t) },
11027           { (void **)&dotp2, clamped_ydim * sizeof(intptr_t) },
11028           { (void **)&lastx, clamped_ydim * sizeof(int32_t) },
11029           { (void **)&mirrorBuffer, (size_t) (xdim * ydim)},
11030           { (void **)&lookups, (ydim << 2) * (sizeof(lookups[0]) << 1) },
11031       };
11032 
11033     for (i = 0; i < (signed)ARRAY_SIZE(dynarray); i++)
11034     {
11035         Xaligned_free(*dynarray[i].ptr);
11036 
11037         *dynarray[i].ptr = Xaligned_alloc(16, dynarray[i].size);
11038     }
11039 
11040     horizlookup  = lookups;
11041     horizlookup2 = lookups + (ydim << 2);
11042     horizycent   = (ydim << 2) >> 1;
11043 
11044     ysavecnt = YSAVES;
11045     nodesperline = tabledivide32_noinline(YSAVES, ydim);
11046 
11047 #ifdef RENDERTYPESDL
11048     if (videoGetRenderMode() == REND_CLASSIC)
11049     {
11050 # ifdef USE_OPENGL
11051         if (!nogl)
11052         {
11053             glsurface_initialize({ xdim, ydim });
11054         }
11055         else
11056 # endif
11057         {
11058             softsurface_initialize({ xdim, ydim },
11059                                    { xres, yres });
11060         }
11061     }
11062 #endif
11063 }
11064 
11065 #ifdef USE_OPENGL
11066 void (*PolymostProcessVoxels_Callback)(void) = NULL;
PolymostProcessVoxels(void)11067 static void PolymostProcessVoxels(void)
11068 {
11069 # ifdef USE_GLEXT
11070     for (bssize_t i = 0; i < MAXVOXELS; i++)
11071     {
11072         if (voxmodels[i])
11073             voxvboalloc(voxmodels[i]);
11074     }
11075 
11076     if (models)
11077     {
11078         for (bssize_t i = 0; i < nextmodelid; i++)
11079         {
11080             if (models[i]->mdnum == 1)
11081                 voxvboalloc((voxmodel_t*)models[i]);
11082         }
11083     }
11084 # endif
11085     if (PolymostProcessVoxels_Callback)
11086         PolymostProcessVoxels_Callback();
11087     if (g_haveVoxels != 1)
11088         return;
11089 
11090     g_haveVoxels = 2;
11091 
11092     OSD_Printf("Generating voxel models for Polymost. This may take a while...\n");
11093     videoNextPage();
11094 
11095     for (bssize_t i=0; i<MAXVOXELS; i++)
11096     {
11097         if (voxfilenames[i])
11098         {
11099             voxmodels[i] = voxload(voxfilenames[i]);
11100             voxmodels[i]->scale = voxscale[i]*(1.f/65536.f);
11101 # ifdef USE_GLEXT
11102             voxvboalloc(voxmodels[i]);
11103 # endif
11104             DO_FREE_AND_NULL(voxfilenames[i]);
11105         }
11106     }
11107 }
11108 
PolymostFreeVBOs(void)11109 static void PolymostFreeVBOs(void)
11110 {
11111 # ifdef USE_GLEXT
11112     for (bssize_t i = 0; i < MAXVOXELS; i++)
11113     {
11114         if (voxmodels[i])
11115             voxvbofree(voxmodels[i]);
11116     }
11117 
11118     if (models)
11119     {
11120         for (bssize_t i = 0; i < nextmodelid; i++)
11121         {
11122             if (models[i]->mdnum == 1)
11123                 voxvbofree((voxmodel_t*)models[i]);
11124         }
11125     }
11126 # endif
11127 }
11128 #endif
11129 
11130 //
11131 // setgamemode
11132 //
11133 // JBF: davidoption now functions as a windowed-mode flag (0 == windowed, 1 == fullscreen)
11134 extern char videomodereset;
videoSetGameMode(char davidoption,int32_t daupscaledxdim,int32_t daupscaledydim,int32_t dabpp,int32_t daupscalefactor)11135 int32_t videoSetGameMode(char davidoption, int32_t daupscaledxdim, int32_t daupscaledydim, int32_t dabpp, int32_t daupscalefactor)
11136 {
11137 #ifdef USE_OPENGL
11138     if (nogl) dabpp = 8;
11139 #endif
11140     daupscaledxdim = max(320, daupscaledxdim);
11141     daupscaledydim = max(200, daupscaledydim);
11142 
11143     if (in3dmode() && videomodereset == 0 && (davidoption == fullscreen) &&
11144         (xres == daupscaledxdim) && (yres == daupscaledydim) && (bpp == dabpp) && (upscalefactor == daupscalefactor))
11145         return 0;
11146 
11147     Bstrcpy(kensmessage,"!!!! BUILD engine&tools programmed by Ken Silverman of E.G. RI."
11148            "  (c) Copyright 1995 Ken Silverman.  Summary:  BUILD = Ken. !!!!");
11149     //  if (getkensmessagecrc(FP_OFF(kensmessage)) != 0x56c764d4)
11150     //      { OSD_Printf("Nice try.\n"); Bexit(EXIT_SUCCESS); }
11151 
11152     //if (checkvideomode(&daxdim, &daydim, dabpp, davidoption)<0) return -1;
11153 
11154     //bytesperline is set in this function
11155 
11156     g_lastpalettesum = 0;
11157 #ifdef USE_OPENGL
11158     PolymostFreeVBOs();
11159 #endif
11160     if (videoSetMode(daupscaledxdim,daupscaledydim,dabpp,davidoption) < 0) return -1;
11161 
11162     // Workaround possible bugs in the GL driver
11163     makeasmwriteable();
11164 
11165 #ifdef USE_OPENGL
11166     if (dabpp > 8) rendmode = glrendmode;    // GL renderer
11167     else rendmode = REND_CLASSIC;
11168 #endif
11169 
11170     upscalefactor = max(1, min(tabledivide32(yres, 200), daupscalefactor));
11171     //POGOTODO: Polymost/Polymer could work with upscaling with a couple more changes
11172     int32_t scalefactor = upscalefactor;
11173 #ifdef RENDERTYPESDL
11174     if (bpp != 8)
11175 #endif
11176     {
11177         scalefactor = 1;
11178     }
11179     xdim = daupscaledxdim/scalefactor;
11180     ydim = daupscaledydim/scalefactor;
11181 
11182 #ifdef USE_OPENGL
11183     fxdim = (float) xdim;
11184     fydim = (float) ydim;
11185 #endif
11186 
11187     OSD_ResizeDisplay(xdim, ydim);
11188 
11189     videoAllocateBuffers();
11190 
11191     //Force drawrooms to call dosetaspect & recalculate stuff
11192     oxyaspect = oxdimen = oviewingrange = -1;
11193 
11194     calc_ylookup(bytesperline, ydim);
11195 
11196     videoSetViewableArea(0L,0L,xdim-1,ydim-1);
11197     videoClearScreen(0L);
11198     videoSetPalette(curbrightness,0,0);
11199 
11200     if (searchx < 0) { searchx = halfxdimen; searchy = (ydimen>>1); }
11201 
11202 #ifdef USE_OPENGL
11203     if (videoGetRenderMode() >= REND_POLYMOST)
11204     {
11205         polymost_glreset();
11206         polymost_glinit();
11207 
11208         if (videoGetRenderMode() == REND_POLYMOST)
11209             PolymostProcessVoxels();
11210     }
11211 # ifdef POLYMER
11212     if (videoGetRenderMode() == REND_POLYMER)
11213     {
11214         if (!polymer_init())
11215             rendmode = REND_POLYMOST;
11216     }
11217 #endif
11218 #endif
11219 
11220 #if MICROPROFILE_ENABLED == 1
11221     static int webServerStarted;
11222 
11223     if (!webServerStarted)
11224     {
11225         webServerStarted = true;
11226         MicroProfileWebServerStart();
11227     }
11228 #endif
11229 
11230     qsetmode = 200;
11231     return 0;
11232 }
11233 
11234 
11235 //
11236 // nextpage
11237 //
videoNextPage(void)11238 void videoNextPage(void)
11239 {
11240     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
11241 
11242     permfifotype *per;
11243 
11244     //char snotbuf[32];
11245     //j = 0; k = 0;
11246     //for(i=0;i<4096;i++)
11247     //   if (waloff[i] != 0)
11248     //   {
11249     //      sprintf(snotbuf,"%d-%d",i,tilesizx[i]*tilesizy[i]);
11250     //      printext256((j>>5)*40+32,(j&31)*6,walock[i]>>3,-1,snotbuf,1);
11251     //      k += tilesizx[i]*tilesizy[i];
11252     //      j++;
11253     //   }
11254     //sprintf(snotbuf,"Total: %d",k);
11255     //printext256((j>>5)*40+32,(j&31)*6,31,-1,snotbuf,1);
11256 
11257     if (in3dmode())
11258     {
11259         videoBeginDrawing(); //{{{
11260         for (bssize_t i=permtail; i!=permhead; i=((i+1)&(MAXPERMS-1)))
11261         {
11262             per = &permfifo[i];
11263             if ((per->pagesleft > 0) && (per->pagesleft <= numpages))
11264                 dorotatesprite(per->sx,per->sy,per->z,per->a,per->picnum,
11265                                per->dashade,per->dapalnum,per->dastat,per->daalpha,per->dablend,
11266                                per->cx1,per->cy1,per->cx2,per->cy2,per->uniqid);
11267         }
11268         videoEndDrawing();   //}}}
11269 
11270         if (benchmarkScreenshot)
11271         {
11272             videoCaptureScreen("reference0000.png", 0);
11273             benchmarkScreenshot = 0;
11274         }
11275 
11276         OSD_Draw();
11277         videoShowFrame(0);
11278 
11279         videoBeginDrawing(); //{{{
11280         for (bssize_t i=permtail; i!=permhead; i=((i+1)&(MAXPERMS-1)))
11281         {
11282             per = &permfifo[i];
11283             if (per->pagesleft >= 130)
11284                 dorotatesprite(per->sx,per->sy,per->z,per->a,per->picnum,
11285                                per->dashade,per->dapalnum,per->dastat,per->daalpha,per->dablend,
11286                                per->cx1,per->cy1,per->cx2,per->cy2,per->uniqid);
11287 
11288             if (per->pagesleft&127) per->pagesleft--;
11289             if (((per->pagesleft&127) == 0) && (i == permtail))
11290                 permtail = ((permtail+1)&(MAXPERMS-1));
11291         }
11292         videoEndDrawing();   //}}}
11293     }
11294 
11295     faketimerhandler();
11296     g_cache.ageBlocks();
11297 
11298 #ifdef USE_OPENGL
11299     omdtims = mdtims;
11300     mdtims = timerGetTicks();
11301 
11302     for (native_t i = 0; i < MAXSPRITES + MAXUNIQHUDID; ++i)
11303         if ((mdpause && spriteext[i].mdanimtims) || (spriteext[i].flags & SPREXT_NOMDANIM))
11304             spriteext[i].mdanimtims += mdtims - omdtims;
11305 #endif
11306 
11307     beforedrawrooms = 1;
11308     numframes++;
11309 }
11310 
11311 //
11312 // qloadkvx
11313 //
qloadkvx(int32_t voxindex,const char * filename)11314 int32_t qloadkvx(int32_t voxindex, const char *filename)
11315 {
11316     if ((unsigned)voxindex >= MAXVOXELS)
11317         return -1;
11318 
11319     const buildvfs_kfd fil = kopen4load(filename, 0);
11320     if (fil == buildvfs_kfd_invalid)
11321         return -1;
11322 
11323     int32_t lengcnt = 0;
11324     const int32_t lengtot = kfilelength(fil);
11325 
11326     for (bssize_t i=0; i<MAXVOXMIPS; i++)
11327     {
11328         int32_t dasiz;
11329         kread(fil, &dasiz, 4); dasiz = B_LITTLE32(dasiz);
11330 
11331         //Must store filenames to use cacheing system :(
11332         voxlock[voxindex][i] = CACHE1D_PERMANENT;
11333         g_cache.allocateBlock(&voxoff[voxindex][i], dasiz, &voxlock[voxindex][i]);
11334 
11335         char *ptr = (char *) voxoff[voxindex][i];
11336         kread(fil, ptr, dasiz);
11337 
11338         lengcnt += dasiz+4;
11339         if (lengcnt >= lengtot-768)
11340             break;
11341     }
11342 
11343     kclose(fil);
11344 
11345 #ifdef USE_OPENGL
11346     if (voxmodels[voxindex])
11347     {
11348         voxfree(voxmodels[voxindex]);
11349         voxmodels[voxindex] = NULL;
11350     }
11351 
11352     Xfree(voxfilenames[voxindex]);
11353     voxfilenames[voxindex] = Xstrdup(filename);
11354 #endif
11355 
11356     g_haveVoxels = 1;
11357 
11358     return 0;
11359 }
11360 
vox_undefine(int32_t const tile)11361 void vox_undefine(int32_t const tile)
11362 {
11363     ssize_t voxindex = tiletovox[tile];
11364     if (voxindex < 0)
11365         return;
11366 
11367 #ifdef USE_OPENGL
11368     if (voxmodels[voxindex])
11369     {
11370         voxfree(voxmodels[voxindex]);
11371         voxmodels[voxindex] = NULL;
11372     }
11373     DO_FREE_AND_NULL(voxfilenames[voxindex]);
11374 #endif
11375 
11376     for (ssize_t j = 0; j < MAXVOXMIPS; ++j)
11377     {
11378         // CACHE1D_FREE
11379         voxlock[voxindex][j] = CACHE1D_FREE;
11380         voxoff[voxindex][j] = 0;
11381     }
11382     voxscale[voxindex] = 65536;
11383     voxrotate[voxindex>>3] &= ~pow2char[voxindex&7];
11384     tiletovox[tile] = -1;
11385 
11386     // TODO: nextvoxid
11387 }
11388 
11389 //
11390 // inside
11391 //
11392 // See http://fabiensanglard.net/duke3d/build_engine_internals.php,
11393 // "Inside details" for the idea behind the algorithm.
inside_19950829(int32_t x,int32_t y,int16_t sectnum)11394 static int32_t inside_19950829(int32_t x, int32_t y, int16_t sectnum)
11395 {
11396     if (sectnum >= 0 && sectnum < numsectors)
11397     {
11398         int32_t cnt = 0;
11399         auto wal       = (uwallptr_t)&wall[sector[sectnum].wallptr];
11400         int  wallsleft = sector[sectnum].wallnum;
11401 
11402         do
11403         {
11404             vec2_t v1 = { wal->x - x, wal->y - y };
11405             auto const &wal2 = *(uwallptr_t)&wall[wal->point2];
11406             vec2_t v2 = { wal2.x - x, wal2.y - y };
11407 
11408             if ((v1.y^v2.y) < 0)
11409                 cnt ^= (((v1.x^v2.x) < 0) ? (v1.x*v2.y<v2.x*v1.y)^(v1.y<v2.y) : (v1.x >= 0));
11410 
11411             wal++;
11412         }
11413         while (--wallsleft);
11414 
11415         return cnt;
11416     }
11417 
11418     return -1;
11419 }
11420 
inside_compat(int32_t x,int32_t y,int16_t sectnum)11421 static int32_t inside_compat(int32_t x, int32_t y, int16_t sectnum)
11422 {
11423     if (sectnum >= 0 && sectnum < numsectors)
11424     {
11425         uint32_t cnt = 0;
11426         auto wal       = (uwallptr_t)&wall[sector[sectnum].wallptr];
11427         int  wallsleft = sector[sectnum].wallnum;
11428 
11429         do
11430         {
11431             // Get the x and y components of the [tested point]-->[wall
11432             // point{1,2}] vectors.
11433             vec2_t v1 = { wal->x - x, wal->y - y };
11434             auto const &wal2 = *(uwallptr_t)&wall[wal->point2];
11435             vec2_t v2 = { wal2.x - x, wal2.y - y };
11436 
11437             // If their signs differ[*], ...
11438             //
11439             // [*] where '-' corresponds to <0 and '+' corresponds to >=0.
11440             // Equivalently, the branch is taken iff
11441             //   y1 != y2 AND y_m <= y < y_M,
11442             // where y_m := min(y1, y2) and y_M := max(y1, y2).
11443             if ((v1.y^v2.y) < 0)
11444                 cnt ^= (((v1.x^v2.x) >= 0) ? v1.x : (v1.x*v2.y-v2.x*v1.y)^v2.y);
11445 
11446             wal++;
11447         }
11448         while (--wallsleft);
11449 
11450         return cnt>>31;
11451     }
11452 
11453     return -1;
11454 }
11455 
inside(int32_t x,int32_t y,int16_t sectnum)11456 int32_t inside(int32_t x, int32_t y, int16_t sectnum)
11457 {
11458     switch (enginecompatibilitymode)
11459     {
11460     case ENGINE_EDUKE32:
11461         if ((unsigned)sectnum < (unsigned)numsectors)
11462         {
11463             uint32_t cnt1 = 0, cnt2 = 0;
11464 
11465             auto wal       = (uwallptr_t)&wall[sector[sectnum].wallptr];
11466             int  wallsleft = sector[sectnum].wallnum;
11467 
11468             do
11469             {
11470                 // Get the x and y components of the [tested point]-->[wall
11471                 // point{1,2}] vectors.
11472                 vec2_t v1 = { wal->x - x, wal->y - y };
11473                 auto const &wal2 = *(uwallptr_t)&wall[wal->point2];
11474                 vec2_t v2 = { wal2.x - x, wal2.y - y };
11475 
11476                 // First, test if the point is EXACTLY_ON_WALL_POINT.
11477                 if ((v1.x|v1.y) == 0 || (v2.x|v2.y)==0)
11478                     return 1;
11479 
11480                 // If their signs differ[*], ...
11481                 //
11482                 // [*] where '-' corresponds to <0 and '+' corresponds to >=0.
11483                 // Equivalently, the branch is taken iff
11484                 //   y1 != y2 AND y_m <= y < y_M,
11485                 // where y_m := min(y1, y2) and y_M := max(y1, y2).
11486                 if ((v1.y^v2.y) < 0)
11487                     cnt1 ^= (((v1.x^v2.x) >= 0) ? v1.x : (v1.x*v2.y-v2.x*v1.y)^v2.y);
11488 
11489                 v1.y--;
11490                 v2.y--;
11491 
11492                 // Now, do the same comparisons, but with the interval half-open on
11493                 // the other side! That is, take the branch iff
11494                 //   y1 != y2 AND y_m < y <= y_M,
11495                 // For a rectangular sector, without EXACTLY_ON_WALL_POINT, this
11496                 // would still leave the lower left and upper right points
11497                 // "outside" the sector.
11498                 if ((v1.y^v2.y) < 0)
11499                 {
11500                     v1.x--;
11501                     v2.x--;
11502 
11503                     cnt2 ^= (((v1.x^v2.x) >= 0) ? v1.x : (v1.x*v2.y-v2.x*v1.y)^v2.y);
11504                 }
11505 
11506                 wal++;
11507             }
11508             while (--wallsleft);
11509 
11510             return (cnt1|cnt2)>>31;
11511         }
11512         return -1;
11513     case ENGINE_19950829:
11514         return inside_19950829(x, y, sectnum);
11515     default:
11516         return inside_compat(x, y, sectnum);
11517     }
11518 }
11519 
getangle(int32_t xvect,int32_t yvect)11520 int32_t __fastcall getangle(int32_t xvect, int32_t yvect)
11521 {
11522     int32_t rv;
11523 
11524     if ((xvect | yvect) == 0)
11525         rv = 0;
11526     else if (xvect == 0)
11527         rv = 512 + ((yvect < 0) << 10);
11528     else if (yvect == 0)
11529         rv = ((xvect < 0) << 10);
11530     else if (xvect == yvect)
11531         rv = 256 + ((xvect < 0) << 10);
11532     else if (xvect == -yvect)
11533         rv = 768 + ((xvect > 0) << 10);
11534     else if (klabs(xvect) > klabs(yvect))
11535         rv = ((radarang[640 + scale(160, yvect, xvect)] >> 6) + ((xvect < 0) << 10)) & 2047;
11536     else rv = ((radarang[640 - scale(160, xvect, yvect)] >> 6) + 512 + ((yvect < 0) << 10)) & 2047;
11537 
11538     return rv;
11539 }
11540 
gethiq16angle(int32_t xvect,int32_t yvect)11541 fix16_t __fastcall gethiq16angle(int32_t xvect, int32_t yvect)
11542 {
11543     fix16_t rv;
11544 
11545     if ((xvect | yvect) == 0)
11546         rv = 0;
11547     else if (xvect == 0)
11548         rv = fix16_from_int(512 + ((yvect < 0) << 10));
11549     else if (yvect == 0)
11550         rv = fix16_from_int(((xvect < 0) << 10));
11551     else if (xvect == yvect)
11552         rv = fix16_from_int(256 + ((xvect < 0) << 10));
11553     else if (xvect == -yvect)
11554         rv = fix16_from_int(768 + ((xvect > 0) << 10));
11555     else if (klabs(xvect) > klabs(yvect))
11556         rv = ((qradarang[5120 + scale(1280, yvect, xvect)] >> 6) + fix16_from_int(((xvect < 0) << 10))) & 0x7FFFFFF;
11557     else rv = ((qradarang[5120 - scale(1280, xvect, yvect)] >> 6) + fix16_from_int(512 + ((yvect < 0) << 10))) & 0x7FFFFFF;
11558 
11559     return rv;
11560 }
11561 
11562 //
11563 // ksqrt
11564 //
ksqrt(uint32_t num)11565 int32_t ksqrt(uint32_t num)
11566 {
11567     if (enginecompatibilitymode == ENGINE_19950829)
11568         return ksqrtasm_old(num);
11569     return nsqrtasm(num);
11570 }
11571 
11572 // Gets the BUILD unit height and z offset of a sprite.
11573 // Returns the z offset, 'height' may be NULL.
spriteheightofsptr(uspriteptr_t spr,int32_t * height,int32_t alsotileyofs)11574 int32_t spriteheightofsptr(uspriteptr_t spr, int32_t *height, int32_t alsotileyofs)
11575 {
11576     int32_t hei, zofs=0;
11577     const int32_t picnum=spr->picnum, yrepeat=spr->yrepeat;
11578 
11579     hei = (tilesiz[picnum].y*yrepeat)<<2;
11580     if (height != NULL)
11581         *height = hei;
11582 
11583     if (spr->cstat&128)
11584         zofs = hei>>1;
11585 
11586     // NOTE: a positive per-tile yoffset translates the sprite into the
11587     // negative world z direction (i.e. upward).
11588     if (alsotileyofs)
11589         zofs -= picanm[picnum].yofs*yrepeat<<2;
11590 
11591     return zofs;
11592 }
11593 
11594 //
11595 // setsprite
11596 //
setsprite(int16_t spritenum,const vec3_t * newpos)11597 int32_t setsprite(int16_t spritenum, const vec3_t *newpos)
11598 {
11599     int16_t tempsectnum = sprite[spritenum].sectnum;
11600 
11601     if ((void const *) newpos != (void *) &sprite[spritenum])
11602         sprite[spritenum].pos = *newpos;
11603 
11604     updatesector(newpos->x,newpos->y,&tempsectnum);
11605 
11606     if (tempsectnum < 0)
11607         return -1;
11608     if (tempsectnum != sprite[spritenum].sectnum)
11609         changespritesect(spritenum,tempsectnum);
11610 
11611     return 0;
11612 }
11613 
setspritez(int16_t spritenum,const vec3_t * newpos)11614 int32_t setspritez(int16_t spritenum, const vec3_t *newpos)
11615 {
11616     int16_t tempsectnum = sprite[spritenum].sectnum;
11617 
11618     if ((void const *)newpos != (void *)&sprite[spritenum])
11619         sprite[spritenum].pos = *newpos;
11620 
11621     updatesectorz(newpos->x,newpos->y,newpos->z,&tempsectnum);
11622 
11623     if (tempsectnum < 0)
11624         return -1;
11625     if (tempsectnum != sprite[spritenum].sectnum)
11626         changespritesect(spritenum,tempsectnum);
11627 
11628     return 0;
11629 }
11630 
11631 
11632 //
11633 // nextsectorneighborz
11634 //
11635 // -1: ceiling or up
11636 //  1: floor or down
nextsectorneighborz(int16_t sectnum,int32_t refz,int16_t topbottom,int16_t direction)11637 int32_t nextsectorneighborz(int16_t sectnum, int32_t refz, int16_t topbottom, int16_t direction)
11638 {
11639     int32_t nextz = (direction==1) ? INT32_MAX : INT32_MIN;
11640     int32_t sectortouse = -1;
11641 
11642     auto wal = (uwallptr_t)&wall[sector[sectnum].wallptr];
11643     int32_t i = sector[sectnum].wallnum;
11644 
11645     do
11646     {
11647         const int32_t ns = wal->nextsector;
11648 
11649         if (ns >= 0)
11650         {
11651             const int32_t testz = (topbottom == 1) ?
11652                 sector[ns].floorz : sector[ns].ceilingz;
11653 
11654             const int32_t update = (direction == 1) ?
11655                 (nextz > testz && testz > refz) :
11656                 (nextz < testz && testz < refz);
11657 
11658             if (update)
11659             {
11660                 nextz = testz;
11661                 sectortouse = ns;
11662             }
11663         }
11664 
11665         wal++;
11666         i--;
11667     }
11668     while (i != 0);
11669 
11670     return sectortouse;
11671 }
11672 
11673 
11674 //
11675 // cansee
11676 //
cansee_19950829(int32_t xs,int32_t ys,int32_t zs,int16_t sectnums,int32_t xe,int32_t ye,int32_t ze,int16_t sectnume)11677 int32_t cansee_19950829(int32_t xs, int32_t ys, int32_t zs, int16_t sectnums, int32_t xe, int32_t ye, int32_t ze, int16_t sectnume)
11678 {
11679     sectortype *sec, *nsec;
11680     walltype *wal, *wal2;
11681     int32_t intx, inty, intz, i, cnt, nextsector, dasectnum, dacnt, danum;
11682 
11683     if ((xs == xe) && (ys == ye) && (sectnums == sectnume)) return 1;
11684 
11685     clipsectorlist[0] = sectnums; danum = 1;
11686     for(dacnt=0;dacnt<danum;dacnt++)
11687     {
11688         dasectnum = clipsectorlist[dacnt]; sec = &sector[dasectnum];
11689 
11690         for(cnt=sec->wallnum,wal=&wall[sec->wallptr];cnt>0;cnt--,wal++)
11691         {
11692             wal2 = &wall[wal->point2];
11693             if (lintersect(xs,ys,zs,xe,ye,ze,wal->x,wal->y,wal2->x,wal2->y,&intx,&inty,&intz) != 0)
11694             {
11695                 nextsector = wal->nextsector; if (nextsector < 0) return 0;
11696 
11697                 if (intz <= sec->ceilingz) return 0;
11698                 if (intz >= sec->floorz) return 0;
11699                 nsec = &sector[nextsector];
11700                 if (intz <= nsec->ceilingz) return 0;
11701                 if (intz >= nsec->floorz) return 0;
11702 
11703                 for(i=danum-1;i>=0;i--)
11704                     if (clipsectorlist[i] == nextsector) break;
11705                 if (i < 0) clipsectorlist[danum++] = nextsector;
11706             }
11707         }
11708 
11709         if (clipsectorlist[dacnt] == sectnume)
11710             return 1;
11711     }
11712     return 0;
11713 }
11714 
cansee(int32_t x1,int32_t y1,int32_t z1,int16_t sect1,int32_t x2,int32_t y2,int32_t z2,int16_t sect2)11715 int32_t cansee(int32_t x1, int32_t y1, int32_t z1, int16_t sect1, int32_t x2, int32_t y2, int32_t z2, int16_t sect2)
11716 {
11717     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
11718 
11719     if (enginecompatibilitymode == ENGINE_19950829)
11720         return cansee_19950829(x1, y1, z1, sect1, x2, y2, z2, sect2);
11721     int32_t dacnt, danum;
11722     const int32_t x21 = x2-x1, y21 = y2-y1, z21 = z2-z1;
11723 
11724     static uint8_t sectbitmap[(MAXSECTORS+7)>>3];
11725 #ifdef YAX_ENABLE
11726     int16_t pendingsectnum;
11727     vec3_t pendingvec;
11728 
11729     // Negative sectnums can happen, for example if the player is using noclip.
11730     // MAXSECTORS can happen from C-CON, e.g. canseespr with a sprite not in
11731     // the game world.
11732     if ((unsigned)sect1 >= MAXSECTORS || (unsigned)sect2 >= MAXSECTORS)
11733         return 0;
11734 
11735     Bmemset(&pendingvec, 0, sizeof(vec3_t));  // compiler-happy
11736 #endif
11737     Bmemset(sectbitmap, 0, sizeof(sectbitmap));
11738 #ifdef YAX_ENABLE
11739 restart_grand:
11740 #endif
11741     if (x1 == x2 && y1 == y2)
11742         return (sect1 == sect2);
11743 
11744 #ifdef YAX_ENABLE
11745     pendingsectnum = -1;
11746 #endif
11747     sectbitmap[sect1>>3] |= pow2char[sect1&7];
11748     clipsectorlist[0] = sect1; danum = 1;
11749 
11750     for (dacnt=0; dacnt<danum; dacnt++)
11751     {
11752         const int32_t dasectnum = clipsectorlist[dacnt];
11753         auto const sec = (usectorptr_t)&sector[dasectnum];
11754         uwallptr_t wal;
11755         bssize_t cnt;
11756 #ifdef YAX_ENABLE
11757         int32_t cfz1[2], cfz2[2];  // both wrt dasectnum
11758         int16_t bn[2];
11759 
11760         yax_getbunches(dasectnum, &bn[0], &bn[1]);
11761         getzsofslope(dasectnum, x1,y1, &cfz1[0], &cfz1[1]);
11762         getzsofslope(dasectnum, x2,y2, &cfz2[0], &cfz2[1]);
11763 #endif
11764         for (cnt=sec->wallnum,wal=(uwallptr_t)&wall[sec->wallptr]; cnt>0; cnt--,wal++)
11765         {
11766             auto const wal2 = (uwallptr_t)&wall[wal->point2];
11767             const int32_t x31 = wal->x-x1, x34 = wal->x-wal2->x;
11768             const int32_t y31 = wal->y-y1, y34 = wal->y-wal2->y;
11769 
11770             int32_t x, y, z, nexts, t, bot;
11771             int32_t cfz[2];
11772 
11773             bot = y21*x34-x21*y34; if (bot <= 0) continue;
11774             // XXX: OVERFLOW
11775             t = y21*x31-x21*y31; if ((unsigned)t >= (unsigned)bot) continue;
11776             t = y31*x34-x31*y34;
11777             if ((unsigned)t >= (unsigned)bot)
11778             {
11779 #ifdef YAX_ENABLE
11780                 if (t >= bot)
11781                 {
11782                     int32_t cf, frac, ns;
11783                     for (cf=0; cf<2; cf++)
11784                     {
11785                         if ((cf==0 && bn[0]>=0 && z1 > cfz1[0] && cfz2[0] > z2) ||
11786                             (cf==1 && bn[1]>=0 && z1 < cfz1[1] && cfz2[1] < z2))
11787                         {
11788                             if ((cfz1[cf]-cfz2[cf])-(z1-z2)==0)
11789                                 continue;
11790                             frac = divscale24(z1-cfz1[cf], (z1-z2)-(cfz1[cf]-cfz2[cf]));
11791 
11792                             if ((unsigned)frac >= (1<<24))
11793                                 continue;
11794 
11795                             x = x1 + mulscale24(x21,frac);
11796                             y = y1 + mulscale24(y21,frac);
11797 
11798                             ns = yax_getneighborsect(x, y, dasectnum, cf);
11799                             if (ns < 0)
11800                                 continue;
11801 
11802                             if (!(sectbitmap[ns>>3] & pow2char[ns&7]) && pendingsectnum==-1)
11803                             {
11804                                 sectbitmap[ns>>3] |= pow2char[ns&7];
11805                                 pendingsectnum = ns;
11806                                 pendingvec.x = x;
11807                                 pendingvec.y = y;
11808                                 pendingvec.z = z1 + mulscale24(z21,frac);
11809                             }
11810                         }
11811                     }
11812                 }
11813 #endif
11814                 continue;
11815             }
11816 
11817             nexts = wal->nextsector;
11818 
11819 #ifdef YAX_ENABLE
11820             if (bn[0]<0 && bn[1]<0)
11821 #endif
11822                 if (nexts < 0 || wal->cstat&32)
11823                     return 0;
11824 
11825             t = divscale24(t,bot);
11826             x = x1 + mulscale24(x21,t);
11827             y = y1 + mulscale24(y21,t);
11828             z = z1 + mulscale24(z21,t);
11829 
11830             getzsofslope(dasectnum, x,y, &cfz[0],&cfz[1]);
11831 
11832             if (z <= cfz[0] || z >= cfz[1])
11833             {
11834 #ifdef YAX_ENABLE
11835                 int32_t cf, frac;
11836 
11837                 // XXX: Is this any good?
11838                 for (cf=0; cf<2; cf++)
11839                     if ((cf==0 && bn[0]>=0 && z <= cfz[0] && z1 >= cfz1[0]) ||
11840                         (cf==1 && bn[1]>=0 && z >= cfz[1] && z1 <= cfz1[1]))
11841                     {
11842                         if ((cfz1[cf]-cfz[cf])-(z1-z)==0)
11843                             continue;
11844 
11845                         frac = divscale24(z1-cfz1[cf], (z1-z)-(cfz1[cf]-cfz[cf]));
11846                         t = mulscale24(t, frac);
11847 
11848                         if ((unsigned)t < (1<<24))
11849                         {
11850                             x = x1 + mulscale24(x21,t);
11851                             y = y1 + mulscale24(y21,t);
11852 
11853                             nexts = yax_getneighborsect(x, y, dasectnum, cf);
11854                             if (nexts >= 0)
11855                                 goto add_nextsector;
11856                         }
11857                     }
11858 
11859 #endif
11860                 return 0;
11861             }
11862 
11863 #ifdef YAX_ENABLE
11864             if (nexts < 0 || (wal->cstat&32))
11865                 return 0;
11866 #endif
11867             getzsofslope(nexts, x,y, &cfz[0],&cfz[1]);
11868             if (z <= cfz[0] || z >= cfz[1])
11869                 return 0;
11870 
11871 add_nextsector:
11872             if (!(sectbitmap[nexts>>3] & pow2char[nexts&7]))
11873             {
11874                 sectbitmap[nexts>>3] |= pow2char[nexts&7];
11875                 clipsectorlist[danum++] = nexts;
11876             }
11877         }
11878 
11879 #ifdef YAX_ENABLE
11880         if (pendingsectnum>=0)
11881         {
11882             sect1 = pendingsectnum;
11883             x1 = pendingvec.x;
11884             y1 = pendingvec.y;
11885             z1 = pendingvec.z;
11886             goto restart_grand;
11887         }
11888 #endif
11889     }
11890 
11891     if (sectbitmap[sect2>>3] & pow2char[sect2&7])
11892         return 1;
11893 
11894     return 0;
11895 }
11896 
11897 //
11898 // neartag
11899 //
neartag(int32_t xs,int32_t ys,int32_t zs,int16_t sectnum,int16_t ange,int16_t * neartagsector,int16_t * neartagwall,int16_t * neartagsprite,int32_t * neartaghitdist,int32_t neartagrange,uint8_t tagsearch,int32_t (* blacklist_sprite_func)(int32_t))11900 void neartag(int32_t xs, int32_t ys, int32_t zs, int16_t sectnum, int16_t ange,
11901              int16_t *neartagsector, int16_t *neartagwall, int16_t *neartagsprite, int32_t *neartaghitdist,  /* out */
11902              int32_t neartagrange, uint8_t tagsearch,
11903              int32_t (*blacklist_sprite_func)(int32_t))
11904 {
11905     int16_t tempshortcnt, tempshortnum;
11906 
11907     const int32_t vx = mulscale14(sintable[(ange+2560)&2047],neartagrange);
11908     const int32_t vy = mulscale14(sintable[(ange+2048)&2047],neartagrange);
11909     vec3_t hitv = { xs+vx, ys+vy, 0 };
11910     const vec3_t sv = { xs, ys, zs };
11911 
11912     *neartagsector = -1; *neartagwall = -1; *neartagsprite = -1;
11913     *neartaghitdist = 0;
11914 
11915     if (sectnum < 0 || (tagsearch & 3) == 0)
11916         return;
11917 
11918     clipsectorlist[0] = sectnum;
11919     tempshortcnt = 0; tempshortnum = 1;
11920 
11921     do
11922     {
11923         const int32_t dasector = clipsectorlist[tempshortcnt];
11924 
11925         const int32_t startwall = sector[dasector].wallptr;
11926         const int32_t endwall = startwall + sector[dasector].wallnum - 1;
11927         uwallptr_t wal;
11928         int32_t z;
11929 
11930         for (z=startwall,wal=(uwallptr_t)&wall[startwall]; z<=endwall; z++,wal++)
11931         {
11932             auto const wal2 = (uwallptr_t)&wall[wal->point2];
11933             const int32_t nextsector = wal->nextsector;
11934 
11935             const int32_t x1=wal->x, y1=wal->y, x2=wal2->x, y2=wal2->y;
11936             int32_t intx, inty, intz, good = 0;
11937 
11938             if (nextsector >= 0)
11939             {
11940                 if ((tagsearch&1) && sector[nextsector].lotag) good |= 1;
11941                 if ((tagsearch&2) && sector[nextsector].hitag) good |= 1;
11942             }
11943 
11944             if ((tagsearch&1) && wal->lotag) good |= 2;
11945             if ((tagsearch&2) && wal->hitag) good |= 2;
11946 
11947             if ((good == 0) && (nextsector < 0)) continue;
11948             if ((coord_t)(x1-xs)*(y2-ys) < (coord_t)(x2-xs)*(y1-ys)) continue;
11949 
11950             if (lintersect(xs,ys,zs,hitv.x,hitv.y,hitv.z,x1,y1,x2,y2,&intx,&inty,&intz) == 1)
11951             {
11952                 if (good != 0)
11953                 {
11954                     if (good&1) *neartagsector = nextsector;
11955                     if (good&2) *neartagwall = z;
11956                     *neartaghitdist = dmulscale14(intx-xs,sintable[(ange+2560)&2047],inty-ys,sintable[(ange+2048)&2047]);
11957                     hitv.x = intx; hitv.y = inty; hitv.z = intz;
11958                 }
11959 
11960                 if (nextsector >= 0)
11961                 {
11962                     int32_t zz;
11963                     for (zz=tempshortnum-1; zz>=0; zz--)
11964                         if (clipsectorlist[zz] == nextsector) break;
11965                     if (zz < 0) clipsectorlist[tempshortnum++] = nextsector;
11966                 }
11967             }
11968         }
11969 
11970         tempshortcnt++;
11971 
11972         if (tagsearch & 4)
11973             continue; // skip sprite search
11974 
11975         for (z=headspritesect[dasector]; z>=0; z=nextspritesect[z])
11976         {
11977             auto const spr = (uspriteptr_t)&sprite[z];
11978 
11979             if (blacklist_sprite_func && blacklist_sprite_func(z))
11980                 continue;
11981 
11982             if (((tagsearch&1) && spr->lotag) || ((tagsearch&2) && spr->hitag))
11983             {
11984                 if (try_facespr_intersect(spr, sv, vx, vy, 0, &hitv, 1))
11985                 {
11986                     *neartagsprite = z;
11987                     *neartaghitdist = dmulscale14(hitv.x-xs, sintable[(ange+2560)&2047],
11988                                                   hitv.y-ys, sintable[(ange+2048)&2047]);
11989                 }
11990             }
11991         }
11992     }
11993     while (tempshortcnt < tempshortnum);
11994 }
11995 
11996 
11997 //
11998 // dragpoint
11999 //
12000 // flags:
12001 //  1: don't reset walbitmap[] (the bitmap of already dragged vertices)
12002 //  2: In the editor, do wall[].cstat |= (1<<14) also for the lastwall().
dragpoint(int16_t pointhighlight,int32_t dax,int32_t day,uint8_t flags)12003 void dragpoint(int16_t pointhighlight, int32_t dax, int32_t day, uint8_t flags)
12004 #ifdef YAX_ENABLE
12005 {
12006     int32_t i, numyaxwalls=0;
12007     static int16_t yaxwalls[MAXWALLS];
12008 
12009     uint8_t *const walbitmap = (uint8_t *)tempbuf;
12010 
12011     if ((flags&1)==0)
12012         Bmemset(walbitmap, 0, (numwalls+7)>>3);
12013     yaxwalls[numyaxwalls++] = pointhighlight;
12014 
12015     for (i=0; i<numyaxwalls; i++)
12016     {
12017         int32_t clockwise = 0;
12018         int32_t w = yaxwalls[i];
12019         const int32_t tmpstartwall = w;
12020 
12021         bssize_t cnt = MAXWALLS;
12022 
12023         while (1)
12024         {
12025             int32_t j, tmpcf;
12026 
12027             wall[w].x = dax;
12028             wall[w].y = day;
12029             walbitmap[w>>3] |= pow2char[w&7];
12030 
12031             for (YAX_ITER_WALLS(w, j, tmpcf))
12032             {
12033                 if ((walbitmap[j>>3]&pow2char[j&7])==0)
12034                 {
12035                     walbitmap[j>>3] |= pow2char[j&7];
12036                     yaxwalls[numyaxwalls++] = j;
12037                 }
12038             }
12039 
12040             if (!clockwise)  //search points CCW
12041             {
12042                 if (wall[w].nextwall >= 0)
12043                     w = wall[wall[w].nextwall].point2;
12044                 else
12045                 {
12046                     w = tmpstartwall;
12047                     clockwise = 1;
12048                 }
12049             }
12050 
12051             cnt--;
12052             if (cnt==0)
12053             {
12054                 initprintf("dragpoint %d: infloop!\n", pointhighlight);
12055                 i = numyaxwalls;
12056                 break;
12057             }
12058 
12059             if (clockwise)
12060             {
12061                 int32_t thelastwall = lastwall(w);
12062                 if (wall[thelastwall].nextwall >= 0)
12063                     w = wall[thelastwall].nextwall;
12064                 else
12065                     break;
12066             }
12067 
12068             if ((walbitmap[w>>3] & pow2char[w&7]))
12069             {
12070                 if (clockwise)
12071                     break;
12072 
12073                 w = tmpstartwall;
12074                 clockwise = 1;
12075                 continue;
12076             }
12077         }
12078     }
12079 
12080     if (editstatus)
12081     {
12082         int32_t w;
12083         // TODO: extern a separate bitmap instead?
12084         for (w=0; w<numwalls; w++)
12085             if (walbitmap[w>>3] & pow2char[w&7])
12086             {
12087                 editwall[w>>3] |= 1<<(w&7);
12088                 if (flags&2)
12089                 {
12090                     int wn = lastwall(w);
12091                     editwall[wn>>3] |= 1<<(wn&7);
12092                 }
12093             }
12094     }
12095 }
12096 #else
12097 {
12098     int16_t cnt, tempshort;
12099     int32_t thelastwall;
12100 
12101     tempshort = pointhighlight;    //search points CCW
12102     cnt = MAXWALLS;
12103 
12104     wall[tempshort].x = dax;
12105     wall[tempshort].y = day;
12106 
12107     if (editstatus)
12108     {
12109         editwall[pointhighlight>>3] |= 1<<(pointhighlight&7);
12110         if (linehighlight >= 0 && linehighlight < MAXWALLS)
12111             editwall[linehighlight>>3] |= 1<<(linehighlight&7);
12112         int wn = lastwall(pointhighlight);
12113         editwall[wn>>3] |= 1<<(wn&7);
12114     }
12115 
12116     do
12117     {
12118         if (wall[tempshort].nextwall >= 0)
12119         {
12120             tempshort = wall[wall[tempshort].nextwall].point2;
12121 
12122             wall[tempshort].x = dax;
12123             wall[tempshort].y = day;
12124             editwall[tempshort>>3] |= 1<<(tempshort&7);
12125         }
12126         else
12127         {
12128             tempshort = pointhighlight;    //search points CW if not searched all the way around
12129             do
12130             {
12131                 thelastwall = lastwall(tempshort);
12132                 if (wall[thelastwall].nextwall >= 0)
12133                 {
12134                     tempshort = wall[thelastwall].nextwall;
12135                     wall[tempshort].x = dax;
12136                     wall[tempshort].y = day;
12137                     editwall[tempshort>>3] |= 1<<(tempshort&7);
12138                 }
12139                 else
12140                 {
12141                     break;
12142                 }
12143                 cnt--;
12144             }
12145             while ((tempshort != pointhighlight) && (cnt > 0));
12146             break;
12147         }
12148         cnt--;
12149     }
12150     while ((tempshort != pointhighlight) && (cnt > 0));
12151 }
12152 #endif
12153 
12154 //
12155 // lastwall
12156 //
lastwall(int16_t point)12157 int32_t lastwall(int16_t point)
12158 {
12159     if (point > 0 && wall[point-1].point2 == point)
12160         return point-1;
12161 
12162     int i = point, cnt = numwalls;
12163     do
12164     {
12165         int const j = wall[i].point2;
12166 
12167         if (j == point)
12168         {
12169             point = i;
12170             break;
12171         }
12172 
12173         i = j;
12174     }
12175     while (--cnt);
12176 
12177     return point;
12178 }
12179 
12180 ////////// UPDATESECTOR* FAMILY OF FUNCTIONS //////////
12181 
12182 /* Different "is inside" predicates.
12183  * NOTE: The redundant bound checks are expected to be optimized away in the
12184  * inlined code. */
12185 
inside_exclude_p(int32_t const x,int32_t const y,int const sectnum,const uint8_t * excludesectbitmap)12186 static FORCE_INLINE CONSTEXPR int inside_exclude_p(int32_t const x, int32_t const y, int const sectnum, const uint8_t *excludesectbitmap)
12187 {
12188     return (sectnum>=0 && !bitmap_test(excludesectbitmap, sectnum) && inside_p(x, y, sectnum));
12189 }
12190 
12191 /* NOTE: no bound check */
inside_z_p(int32_t const x,int32_t const y,int32_t const z,int const sectnum)12192 static inline int inside_z_p(int32_t const x, int32_t const y, int32_t const z, int const sectnum)
12193 {
12194     int32_t cz, fz;
12195     getzsofslope(sectnum, x, y, &cz, &fz);
12196     return (z >= cz && z <= fz && inside_p(x, y, sectnum));
12197 }
12198 
getwalldist(vec2_t const in,int const wallnum)12199 int32_t getwalldist(vec2_t const in, int const wallnum)
12200 {
12201     vec2_t closest;
12202     getclosestpointonwall_internal(in, wallnum, &closest);
12203     return klabs(closest.x - in.x) + klabs(closest.y - in.y);
12204 }
12205 
getwalldist(vec2_t const in,int const wallnum,vec2_t * const out)12206 int32_t getwalldist(vec2_t const in, int const wallnum, vec2_t * const out)
12207 {
12208     getclosestpointonwall_internal(in, wallnum, out);
12209     return klabs(out->x - in.x) + klabs(out->y - in.y);
12210 }
12211 
12212 
getsectordist(vec2_t const in,int const sectnum,vec2_t * const out)12213 int32_t getsectordist(vec2_t const in, int const sectnum, vec2_t * const out /*= nullptr*/)
12214 {
12215     if (inside_p(in.x, in.y, sectnum))
12216     {
12217         if (out)
12218             *out = in;
12219         return 0;
12220     }
12221 
12222     int32_t distance = INT32_MAX;
12223 
12224     auto const sec       = (usectorptr_t)&sector[sectnum];
12225     int const  startwall = sec->wallptr;
12226     int const  endwall   = sec->wallptr + sec->wallnum;
12227     auto       uwal      = (uwallptr_t)&wall[startwall];
12228     vec2_t     closest = {};
12229 
12230     for (int j = startwall; j < endwall; j++, uwal++)
12231     {
12232         vec2_t p;
12233         int32_t const walldist = getwalldist(in, j, &p);
12234 
12235         if (walldist < distance)
12236         {
12237             distance = walldist;
12238             closest = p;
12239         }
12240     }
12241 
12242     if (out)
12243         *out = closest;
12244 
12245     return distance;
12246 }
12247 
findwallbetweensectors(int sect1,int sect2)12248 int findwallbetweensectors(int sect1, int sect2)
12249 {
12250     if (sector[sect1].wallnum > sector[sect2].wallnum)
12251         swaplong(&sect1, &sect2);
12252 
12253     auto const sec  = (usectorptr_t)&sector[sect1];
12254     int const  last = sec->wallptr + sec->wallnum;
12255 
12256     for (int i = sec->wallptr; i < last; i++)
12257         if (wall[i].nextsector == sect2)
12258             return i;
12259 
12260     return -1;
12261 }
12262 
12263 //
12264 // updatesector[z]
12265 //
updatesector(int32_t const x,int32_t const y,int16_t * const sectnum)12266 void updatesector(int32_t const x, int32_t const y, int16_t * const sectnum)
12267 {
12268     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
12269 
12270     if (enginecompatibilitymode == ENGINE_EDUKE32)
12271     {
12272         int16_t sect = *sectnum;
12273         updatesectorneighbor(x, y, &sect, INITIALUPDATESECTORDIST, MAXUPDATESECTORDIST);
12274         if (sect != -1)
12275             SET_AND_RETURN(*sectnum, sect);
12276     }
12277     else
12278     {
12279         if (inside_p(x, y, *sectnum))
12280             return;
12281 
12282         if ((unsigned)*sectnum < (unsigned)numsectors)
12283         {
12284             const uwalltype *wal = (uwalltype *)&wall[sector[*sectnum].wallptr];
12285             int wallsleft = sector[*sectnum].wallnum;
12286 
12287             do
12288             {
12289                 int const next = wal->nextsector;
12290                 if (inside_p(x, y, next))
12291                     SET_AND_RETURN(*sectnum, next);
12292                 wal++;
12293             }
12294             while (--wallsleft);
12295         }
12296     }
12297 
12298     // we need to support passing in a sectnum of -1, unfortunately
12299 
12300     for (int i = numsectors - 1; i >= 0; --i)
12301         if (inside_p(x, y, i))
12302             SET_AND_RETURN(*sectnum, i);
12303 
12304     *sectnum = -1;
12305 }
12306 
updatesectorexclude(int32_t const x,int32_t const y,int16_t * const sectnum,const uint8_t * const excludesectbitmap)12307 void updatesectorexclude(int32_t const x, int32_t const y, int16_t * const sectnum, const uint8_t * const excludesectbitmap)
12308 {
12309     if (inside_exclude_p(x, y, *sectnum, excludesectbitmap))
12310         return;
12311 
12312     if (*sectnum >= 0 && *sectnum < numsectors)
12313     {
12314         auto wal = (uwallptr_t)&wall[sector[*sectnum].wallptr];
12315         int wallsleft = sector[*sectnum].wallnum;
12316 
12317         do
12318         {
12319             int const next = wal->nextsector;
12320             if (inside_exclude_p(x, y, next, excludesectbitmap))
12321                 SET_AND_RETURN(*sectnum, next);
12322             wal++;
12323         }
12324         while (--wallsleft);
12325     }
12326 
12327     for (bssize_t i=numsectors-1; i>=0; --i)
12328         if (inside_exclude_p(x, y, i, excludesectbitmap))
12329             SET_AND_RETURN(*sectnum, i);
12330 
12331     *sectnum = -1;
12332 }
12333 
12334 // new: if *sectnum >= MAXSECTORS, *sectnum-=MAXSECTORS is considered instead
12335 //      as starting sector and the 'initial' z check is skipped
12336 //      (not initial anymore because it follows the sector updating due to TROR)
12337 
updatesectorz(int32_t const x,int32_t const y,int32_t const z,int16_t * const sectnum)12338 void updatesectorz(int32_t const x, int32_t const y, int32_t const z, int16_t * const sectnum)
12339 {
12340     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
12341 
12342     if (enginecompatibilitymode == ENGINE_EDUKE32)
12343     {
12344         int16_t sect = *sectnum;
12345         updatesectorneighborz(x, y, z, &sect, INITIALUPDATESECTORDIST, MAXUPDATESECTORDIST);
12346         if (sect != -1)
12347             SET_AND_RETURN(*sectnum, sect);
12348     }
12349     else
12350     {
12351         if ((uint32_t)(*sectnum) < 2*MAXSECTORS)
12352         {
12353             int32_t nofirstzcheck = 0;
12354 
12355             if (*sectnum >= MAXSECTORS)
12356             {
12357                 *sectnum -= MAXSECTORS;
12358                 nofirstzcheck = 1;
12359             }
12360 
12361 #ifdef YAX_ENABLE
12362             int mcf = -1;
12363 restart:
12364 #endif
12365             // this block used to be outside the "if" and caused crashes in Polymost Mapster32
12366             int32_t cz, fz;
12367             getzsofslope(*sectnum, x, y, &cz, &fz);
12368 
12369 #ifdef YAX_ENABLE
12370             if ((mcf == -1 || mcf == YAX_CEILING) && z < cz)
12371             {
12372                 int const next = yax_getneighborsect(x, y, *sectnum, YAX_CEILING);
12373                 if (next >= 0)
12374                 {
12375                     if (z >= getceilzofslope(next, x, y))
12376                         SET_AND_RETURN(*sectnum, next);
12377 
12378                     *sectnum = next;
12379                     mcf = YAX_CEILING;
12380                     goto restart;
12381                 }
12382             }
12383 
12384             if ((mcf == -1 || mcf == YAX_FLOOR) && z > fz)
12385             {
12386                 int const next = yax_getneighborsect(x, y, *sectnum, YAX_FLOOR);
12387                 if (next >= 0)
12388                 {
12389                     if (z <= getflorzofslope(next, x, y))
12390                         SET_AND_RETURN(*sectnum, next);
12391 
12392                     *sectnum = next;
12393                     mcf = YAX_FLOOR;
12394                     goto restart;
12395                 }
12396             }
12397 #endif
12398             if (nofirstzcheck || (z >= cz && z <= fz))
12399                 if (inside_p(x, y, *sectnum))
12400                     return;
12401 
12402             uwalltype const * wal = (uwalltype *)&wall[sector[*sectnum].wallptr];
12403             int wallsleft = sector[*sectnum].wallnum;
12404             do
12405             {
12406                 // YAX: TODO: check neighboring sectors here too?
12407                 int const next = wal->nextsector;
12408                 if (next>=0 && inside_z_p(x,y,z, next))
12409                     SET_AND_RETURN(*sectnum, next);
12410 
12411                 wal++;
12412             }
12413             while (--wallsleft);
12414         }
12415     }
12416 
12417     // we need to support passing in a sectnum of -1, unfortunately
12418     for (int i = numsectors - 1; i >= 0; --i)
12419         if (inside_z_p(x, y, z, i))
12420             SET_AND_RETURN(*sectnum, i);
12421 
12422     *sectnum = -1;
12423 }
12424 
updatesectorneighbor(int32_t const x,int32_t const y,int16_t * const sectnum,int32_t initialMaxDistance,int32_t maxDistance)12425 void updatesectorneighbor(int32_t const x, int32_t const y, int16_t * const sectnum, int32_t initialMaxDistance /*= INITIALUPDATESECTORDIST*/, int32_t maxDistance /*= MAXUPDATESECTORDIST*/)
12426 {
12427     int const initialsectnum = *sectnum;
12428 
12429     if ((unsigned)initialsectnum < (unsigned)numsectors && getsectordist({x, y}, initialsectnum) <= initialMaxDistance)
12430     {
12431         if (inside_p(x, y, initialsectnum))
12432             return;
12433 
12434         static int16_t sectlist[MAXSECTORS];
12435         static uint8_t sectbitmap[(MAXSECTORS+7)>>3];
12436         int16_t nsecs;
12437 
12438         bfirst_search_init(sectlist, sectbitmap, &nsecs, MAXSECTORS, initialsectnum);
12439 
12440         for (int sectcnt=0; sectcnt<nsecs; sectcnt++)
12441         {
12442             int const listsectnum = sectlist[sectcnt];
12443 
12444             if (inside_p(x, y, listsectnum))
12445                 SET_AND_RETURN(*sectnum, listsectnum);
12446 
12447             auto const sec       = &sector[listsectnum];
12448             int const  startwall = sec->wallptr;
12449             int const  endwall   = sec->wallptr + sec->wallnum;
12450             auto       uwal      = (uwallptr_t)&wall[startwall];
12451 
12452             for (int j=startwall; j<endwall; j++, uwal++)
12453                 if (uwal->nextsector >= 0 && getsectordist({x, y}, uwal->nextsector) <= maxDistance)
12454                     bfirst_search_try(sectlist, sectbitmap, &nsecs, uwal->nextsector);
12455         }
12456     }
12457 
12458     *sectnum = -1;
12459 }
12460 
updatesectorneighborz(int32_t const x,int32_t const y,int32_t const z,int16_t * const sectnum,int32_t initialMaxDistance,int32_t maxDistance)12461 void updatesectorneighborz(int32_t const x, int32_t const y, int32_t const z, int16_t * const sectnum, int32_t initialMaxDistance /*= 0*/, int32_t maxDistance /*= 0*/)
12462 {
12463     bool nofirstzcheck = false;
12464 
12465     if (*sectnum >= MAXSECTORS && *sectnum - MAXSECTORS < numsectors)
12466     {
12467         *sectnum -= MAXSECTORS;
12468         nofirstzcheck = true;
12469     }
12470 
12471 #ifdef YAX_ENABLE
12472     int mcf = -1;
12473 restart:
12474 #endif
12475     uint32_t const correctedsectnum = (unsigned)*sectnum;
12476     if (correctedsectnum < (unsigned)numsectors && getsectordist({x, y}, correctedsectnum) <= initialMaxDistance)
12477     {
12478         int32_t cz, fz;
12479         getzsofslope(correctedsectnum, x, y, &cz, &fz);
12480 
12481 #ifdef YAX_ENABLE
12482         if ((mcf == -1 || mcf == YAX_CEILING) && z < cz)
12483         {
12484             int const next = yax_getneighborsect(x, y, correctedsectnum, YAX_CEILING);
12485             if (next >= 0)
12486             {
12487                 if (z >= getceilzofslope(next, x, y))
12488                     SET_AND_RETURN(*sectnum, next);
12489 
12490                 *sectnum = next;
12491                 mcf = YAX_CEILING;
12492                 goto restart;
12493             }
12494         }
12495 
12496         if ((mcf == -1 || mcf == YAX_FLOOR) && z > fz)
12497         {
12498             int const next = yax_getneighborsect(x, y, correctedsectnum, YAX_FLOOR);
12499             if (next >= 0)
12500             {
12501                 if (z <= getflorzofslope(next, x, y))
12502                     SET_AND_RETURN(*sectnum, next);
12503 
12504                 *sectnum = next;
12505                 mcf = YAX_FLOOR;
12506                 goto restart;
12507             }
12508         }
12509 #endif
12510         if ((nofirstzcheck || (z >= cz && z <= fz)) && inside_p(x, y, *sectnum))
12511             return;
12512 
12513         static int16_t sectlist[MAXSECTORS];
12514         static uint8_t sectbitmap[(MAXSECTORS+7)>>3];
12515         int16_t nsecs;
12516 
12517         bfirst_search_init(sectlist, sectbitmap, &nsecs, MAXSECTORS, correctedsectnum);
12518 
12519         for (int sectcnt=0; sectcnt<nsecs; sectcnt++)
12520         {
12521             int const listsectnum = sectlist[sectcnt];
12522 
12523             if (inside_z_p(x, y, z, listsectnum))
12524                 SET_AND_RETURN(*sectnum, listsectnum);
12525 
12526             auto const sec       = &sector[listsectnum];
12527             int const  startwall = sec->wallptr;
12528             int const  endwall   = sec->wallptr + sec->wallnum;
12529             auto       uwal      = (uwallptr_t)&wall[startwall];
12530 
12531             for (int j=startwall; j<endwall; j++, uwal++)
12532                 if (uwal->nextsector >= 0 && getsectordist({x, y}, uwal->nextsector) <= maxDistance)
12533                     bfirst_search_try(sectlist, sectbitmap, &nsecs, uwal->nextsector);
12534         }
12535     }
12536 
12537     *sectnum = -1;
12538 }
12539 
12540 //
12541 // rotatepoint
12542 //
rotatepoint(vec2_t const pivot,vec2_t p,int16_t const daang,vec2_t * const p2)12543 void rotatepoint(vec2_t const pivot, vec2_t p, int16_t const daang, vec2_t * const p2)
12544 {
12545     int const dacos = sintable[(daang+2560)&2047];
12546     int const dasin = sintable[(daang+2048)&2047];
12547     p.x -= pivot.x;
12548     p.y -= pivot.y;
12549     *p2 = { dmulscale14(p.x, dacos, -p.y, dasin) + pivot.x, dmulscale14(p.y, dacos, p.x, dasin) + pivot.y };
12550 }
12551 
12552 
12553 //
12554 // getmousevalues
12555 //
12556 
mouseGetValues(int32_t * mousx,int32_t * mousy,int32_t * bstatus)12557 void mouseGetValues(int32_t *mousx, int32_t *mousy, int32_t *bstatus)
12558 {
12559     mouseReadPos(mousx, mousy);
12560     *bstatus = mouseReadButtons();
12561 }
12562 
12563 
12564 #if KRANDDEBUG
12565 # include <execinfo.h>
12566 # define KRD_MAXCALLS 262144
12567 # define KRD_DEPTH 8
12568 static int32_t krd_numcalls=0;
12569 static void *krd_fromwhere[KRD_MAXCALLS][KRD_DEPTH];
12570 static int32_t krd_enabled=0;
12571 
krd_enable(int which)12572 void krd_enable(int which)  // 0: disable, 1: rec, 2: play
12573 {
12574     krd_enabled = which;
12575 
12576     if (which)
12577         Bmemset(krd_fromwhere, 0, sizeof(krd_fromwhere));
12578 }
12579 
krd_print(const char * filename)12580 int32_t krd_print(const char *filename)
12581 {
12582     buildvfs_FILE fp;
12583     int32_t i, j;
12584 
12585     if (!krd_enabled) return 1;
12586     krd_enabled = 0;
12587 
12588     fp = buildvfs_fopen_write(filename);
12589     if (!fp) { OSD_Printf("krd_print (2): fopen"); return 1; }
12590 
12591     for (i=0; i<krd_numcalls; i++)
12592     {
12593         for (j=1;; j++)  // skip self entry
12594         {
12595             if (j>=KRD_DEPTH || krd_fromwhere[i][j]==NULL)
12596             {
12597                 fprintf(fp, "\n");
12598                 break;
12599             }
12600             fprintf(fp, " [%p]", krd_fromwhere[i][j]);
12601         }
12602     }
12603 
12604     krd_numcalls = 0;
12605 
12606     buildvfs_fclose(fp);
12607     return 0;
12608 }
12609 #endif  // KRANDDEBUG
12610 
12611 #if KRANDDEBUG
12612 //
12613 // krand
12614 //
krand(void)12615 int32_t krand(void)
12616 {
12617 //    randomseed = (randomseed*27584621)+1;
12618     randomseed = (randomseed * 1664525ul) + 221297ul;
12619     if (krd_enabled)
12620         if (krd_numcalls < KRD_MAXCALLS)
12621         {
12622             backtrace(krd_fromwhere[krd_numcalls], KRD_DEPTH);
12623             krd_numcalls++;
12624         }
12625     return ((uint32_t)randomseed)>>16;
12626 }
12627 #endif
12628 
12629 int32_t setaspect_new_use_dimen = 0;
12630 
videoSetCorrectedAspect()12631 void videoSetCorrectedAspect()
12632 {
12633     if (r_usenewaspect && newaspect_enable && videoGetRenderMode() != REND_POLYMER)
12634     {
12635         // In DOS the game world is displayed with an aspect of 1.28 instead 1.333,
12636         // meaning we have to stretch it by a factor of 1.25 instead of 1.2
12637         // to get perfect squares
12638         int32_t yx = (65536 * 5) / 4;
12639         int32_t vr, y, x;
12640 
12641         const int32_t xd = setaspect_new_use_dimen ? xdimen : xdim;
12642         const int32_t yd = setaspect_new_use_dimen ? ydimen : ydim;
12643 
12644         if (fullscreen && !setaspect_new_use_dimen)
12645         {
12646             const int32_t screenw = r_screenxy/100;
12647             const int32_t screenh = r_screenxy%100;
12648 
12649             if (screenw==0 || screenh==0)
12650             {
12651                 // Assume square pixel aspect.
12652                 x = xd;
12653                 y = yd;
12654             }
12655             else
12656             {
12657                 int32_t pixratio;
12658 
12659                 x = screenw;
12660                 y = screenh;
12661 
12662                 pixratio = divscale16(xdim*screenh, ydim*screenw);
12663                 yx = divscale16(yx, pixratio);
12664             }
12665         }
12666         else
12667         {
12668             x = xd;
12669             y = yd;
12670         }
12671 
12672         vr = divscale16(x*3, y*4);
12673 
12674         renderSetAspect(vr, yx);
12675     }
12676     else
12677         renderSetAspect(65536, divscale16(ydim*320, xdim*200));
12678 }
12679 
12680 //
12681 // setview
12682 //
videoSetViewableArea(int32_t x1,int32_t y1,int32_t x2,int32_t y2)12683 void videoSetViewableArea(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
12684 {
12685     windowxy1.x = x1; wx1 = (x1<<12);
12686     windowxy1.y = y1; wy1 = (y1<<12);
12687     windowxy2.x = x2; wx2 = ((x2+1)<<12);
12688     windowxy2.y = y2; wy2 = ((y2+1)<<12);
12689 
12690     xdimen = (x2-x1)+1; halfxdimen = (xdimen>>1);
12691     xdimenrecip = divscale32(1L,xdimen);
12692     ydimen = (y2-y1)+1;
12693 
12694     fxdimen = (float) xdimen;
12695 #ifdef USE_OPENGL
12696     fydimen = (float) ydimen;
12697 #endif
12698     videoSetCorrectedAspect();
12699 
12700     for (bssize_t i=0; i<windowxy1.x; i++) { startumost[i] = 1, startdmost[i] = 0; }
12701     Bassert(windowxy2.x < xdim);  // xdim is the number of alloc'd elements in start*most[].
12702     for (bssize_t i=windowxy1.x; i<=windowxy2.x; i++)
12703         { startumost[i] = windowxy1.y, startdmost[i] = windowxy2.y+1; }
12704     for (bssize_t i=windowxy2.x+1; i<xdim; i++) { startumost[i] = 1, startdmost[i] = 0; }
12705 }
12706 
12707 
12708 //
12709 // setaspect
12710 //
renderSetAspect(int32_t daxrange,int32_t daaspect)12711 void renderSetAspect(int32_t daxrange, int32_t daaspect)
12712 {
12713     viewingrange = daxrange;
12714     viewingrangerecip = divscale32(1,daxrange);
12715 #ifdef USE_OPENGL
12716     fviewingrange = (float) daxrange;
12717 #endif
12718 
12719     yxaspect = daaspect;
12720     xyaspect = divscale32(1,yxaspect);
12721     xdimenscale = scale(xdimen,yxaspect,320);
12722     xdimscale = scale(320,xyaspect,xdimen);
12723 }
12724 
12725 
12726 //
12727 // flushperms
12728 //
renderFlushPerms(void)12729 void renderFlushPerms(void)
12730 {
12731     permhead = permtail = 0;
12732 }
12733 
12734 
12735 //
12736 // rotatesprite
12737 //
rotatesprite_(int32_t sx,int32_t sy,int32_t z,int16_t a,int16_t picnum,int8_t dashade,char dapalnum,int32_t dastat,uint8_t daalpha,uint8_t dablend,int32_t cx1,int32_t cy1,int32_t cx2,int32_t cy2)12738 void rotatesprite_(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum,
12739                    int8_t dashade, char dapalnum, int32_t dastat, uint8_t daalpha, uint8_t dablend,
12740                    int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2)
12741 {
12742     int32_t i;
12743 
12744     if ((unsigned)picnum >= MAXTILES)
12745         return;
12746 
12747     if ((cx1 > cx2) || (cy1 > cy2)) return;
12748     if (z <= 16) return;
12749     tileUpdatePicnum(&picnum, (int16_t)0xc000);
12750     if ((tilesiz[picnum].x <= 0) || (tilesiz[picnum].y <= 0)) return;
12751 
12752     // Experimental / development bits. ONLY FOR INTERNAL USE!
12753     //  bit RS_CENTERORIGIN: see dorotspr_handle_bit2
12754     ////////////////////
12755 
12756     if (((dastat & RS_PERM) == 0) || (numpages < 2) || (beforedrawrooms != 0))
12757     {
12758         videoBeginDrawing(); //{{{
12759         dorotatesprite(sx,sy,z,a,picnum,dashade,dapalnum,dastat,daalpha,dablend,cx1,cy1,cx2,cy2,guniqhudid);
12760         videoEndDrawing();   //}}}
12761     }
12762 
12763     if ((dastat & RS_NOMASK) && (cx1 <= 0) && (cy1 <= 0) && (cx2 >= xdim-1) && (cy2 >= ydim-1) &&
12764             (sx == (160<<16)) && (sy == (100<<16)) && (z == 65536L) && (a == 0) && ((dastat&RS_TRANS1) == 0))
12765         permhead = permtail = 0;
12766 
12767     if ((dastat & RS_PERM) == 0)
12768         return;
12769 
12770     if (numpages >= 2)
12771     {
12772         permfifotype *per = &permfifo[permhead];
12773 
12774         per->sx = sx; per->sy = sy; per->z = z; per->a = a;
12775         per->picnum = picnum;
12776         per->dashade = dashade; per->dapalnum = dapalnum;
12777         per->dastat = dastat;
12778         per->daalpha = daalpha;
12779         per->dablend = dablend;
12780         per->pagesleft = numpages+((beforedrawrooms&1)<<7);
12781         per->cx1 = cx1; per->cy1 = cy1; per->cx2 = cx2; per->cy2 = cy2;
12782         per->uniqid = guniqhudid;   //JF extension
12783 
12784         //Would be better to optimize out true bounding boxes
12785         if (dastat & RS_NOMASK)  //If non-masking write, checking for overlapping cases
12786         {
12787             for (i=permtail; i!=permhead; i=((i+1)&(MAXPERMS-1)))
12788             {
12789                 permfifotype *per2 = &permfifo[i];
12790 
12791                 if ((per2->pagesleft&127) == 0) continue;
12792                 if (per2->sx != per->sx) continue;
12793                 if (per2->sy != per->sy) continue;
12794                 if (per2->z != per->z) continue;
12795                 if (per2->a != per->a) continue;
12796                 if (tilesiz[per2->picnum].x > tilesiz[per->picnum].x) continue;
12797                 if (tilesiz[per2->picnum].y > tilesiz[per->picnum].y) continue;
12798                 if (per2->cx1 < per->cx1) continue;
12799                 if (per2->cy1 < per->cy1) continue;
12800                 if (per2->cx2 > per->cx2) continue;
12801                 if (per2->cy2 > per->cy2) continue;
12802 
12803                 per2->pagesleft = 0;
12804             }
12805 
12806             if ((per->z == 65536) && (per->a == 0))
12807                 for (i=permtail; i!=permhead; i=((i+1)&(MAXPERMS-1)))
12808                 {
12809                     permfifotype *per2 = &permfifo[i];
12810 
12811                     if ((per2->pagesleft&127) == 0) continue;
12812                     if (per2->z != 65536) continue;
12813                     if (per2->a != 0) continue;
12814                     if (per2->cx1 < per->cx1) continue;
12815                     if (per2->cy1 < per->cy1) continue;
12816                     if (per2->cx2 > per->cx2) continue;
12817                     if (per2->cy2 > per->cy2) continue;
12818                     if ((per2->sx>>16) < (per->sx>>16)) continue;
12819                     if ((per2->sy>>16) < (per->sy>>16)) continue;
12820                     if ((per2->sx>>16)+tilesiz[per2->picnum].x > (per->sx>>16)+tilesiz[per->picnum].x) continue;
12821                     if ((per2->sy>>16)+tilesiz[per2->picnum].y > (per->sy>>16)+tilesiz[per->picnum].y) continue;
12822 
12823                     per2->pagesleft = 0;
12824                 }
12825         }
12826 
12827         permhead = ((permhead+1)&(MAXPERMS-1));
12828     }
12829 }
12830 
12831 
12832 //
12833 // clearview
12834 //
videoClearViewableArea(int32_t dacol)12835 void videoClearViewableArea(int32_t dacol)
12836 {
12837     if (!in3dmode() && dacol != -1) return;
12838 
12839     if (dacol == -1) dacol = 0;
12840 
12841 #ifdef USE_OPENGL
12842     if (videoGetRenderMode() >= REND_POLYMOST)
12843     {
12844         palette_t const p = paletteGetColor(dacol);
12845 
12846         glClearColor((float)p.r * (1.f/255.f),
12847                       (float)p.g * (1.f/255.f),
12848                       (float)p.b * (1.f/255.f),
12849                       0);
12850         glClear(GL_COLOR_BUFFER_BIT);
12851         return;
12852     }
12853 #endif
12854 
12855     videoBeginDrawing(); //{{{
12856     //dacol += (dacol<<8); dacol += (dacol<<16);
12857     int const dx = windowxy2.x-windowxy1.x+1;
12858     intptr_t p = frameplace+ylookup[windowxy1.y]+windowxy1.x;
12859     for (bssize_t y=windowxy1.y; y<=windowxy2.y; ++y)
12860     {
12861         //clearbufbyte((void*)p,dx,dacol);
12862         Bmemset((void *)p,dacol,dx);
12863         p += ylookup[1];
12864     }
12865     videoEndDrawing();   //}}}
12866 
12867     faketimerhandler();
12868 }
12869 
12870 
12871 //
12872 // clearallviews
12873 //
videoClearScreen(int32_t dacol)12874 void videoClearScreen(int32_t dacol)
12875 {
12876     if (!in3dmode()) return;
12877     //dacol += (dacol<<8); dacol += (dacol<<16);
12878 
12879 #ifdef USE_OPENGL
12880     if (videoGetRenderMode() >= REND_POLYMOST)
12881     {
12882         palette_t const p = paletteGetColor(dacol);
12883 
12884         glViewport(0,0,xdim,ydim);
12885         glClearColor((float)p.r * (1.f/255.f),
12886                       (float)p.g * (1.f/255.f),
12887                       (float)p.b * (1.f/255.f),
12888                       0);
12889         glClear(GL_COLOR_BUFFER_BIT);
12890         return;
12891     }
12892 #endif
12893 
12894     videoBeginDrawing(); //{{{
12895     Bmemset((void *)frameplace,dacol,bytesperline*ydim);
12896     videoEndDrawing();   //}}}
12897     //nextpage();
12898 
12899     faketimerhandler();
12900 }
12901 
12902 
12903 //MUST USE RESTOREFORDRAWROOMS AFTER DRAWING
12904 
12905 //
12906 // setviewtotile
12907 //
renderSetTarget(int16_t tilenume,int32_t xsiz,int32_t ysiz)12908 void renderSetTarget(int16_t tilenume, int32_t xsiz, int32_t ysiz)
12909 {
12910     if (setviewcnt >= MAXSETVIEW-1)
12911         return;
12912     if (xsiz <= 0 ||
12913         ysiz <= 0)
12914         return;
12915 
12916     //DRAWROOMS TO TILE BACKUP&SET CODE
12917     tilesiz[tilenume].x = xsiz; tilesiz[tilenume].y = ysiz;
12918     bakxsiz[setviewcnt] = xdim; bakysiz[setviewcnt] = ydim;
12919     bakframeplace[setviewcnt] = frameplace; frameplace = waloff[tilenume];
12920     bakwindowxy1[setviewcnt] = windowxy1;
12921     bakwindowxy2[setviewcnt] = windowxy2;
12922 
12923     if (setviewcnt == 0)
12924     {
12925 #ifdef USE_OPENGL
12926         bakrendmode = rendmode;
12927 #endif
12928         baktile = tilenume;
12929     }
12930 
12931 #ifdef USE_OPENGL
12932     rendmode = REND_CLASSIC;
12933 #endif
12934 
12935     copybufbyte(&startumost[windowxy1.x],&bakumost[windowxy1.x],(windowxy2.x-windowxy1.x+1)*sizeof(bakumost[0]));
12936     copybufbyte(&startdmost[windowxy1.x],&bakdmost[windowxy1.x],(windowxy2.x-windowxy1.x+1)*sizeof(bakdmost[0]));
12937     setviewcnt++;
12938 
12939     offscreenrendering = 1;
12940     xdim = ysiz;
12941     ydim = xsiz;
12942     videoSetViewableArea(0,0,ysiz-1,xsiz-1);
12943     renderSetAspect(65536,65536);
12944 
12945     calc_ylookup(ysiz, xsiz);
12946 }
12947 
12948 
12949 //
12950 // setviewback
12951 //
renderRestoreTarget(void)12952 void renderRestoreTarget(void)
12953 {
12954     if (setviewcnt <= 0) return;
12955     setviewcnt--;
12956 
12957     offscreenrendering = (setviewcnt>0);
12958 #ifdef USE_OPENGL
12959     if (setviewcnt == 0)
12960     {
12961         rendmode = bakrendmode;
12962         tileInvalidate(baktile,-1,-1);
12963     }
12964 #endif
12965 
12966     xdim = bakxsiz[setviewcnt];
12967     ydim = bakysiz[setviewcnt];
12968     videoSetViewableArea(bakwindowxy1[setviewcnt].x,bakwindowxy1[setviewcnt].y,
12969             bakwindowxy2[setviewcnt].x,bakwindowxy2[setviewcnt].y);
12970     copybufbyte(&bakumost[windowxy1.x],&startumost[windowxy1.x],(windowxy2.x-windowxy1.x+1)*sizeof(startumost[0]));
12971     copybufbyte(&bakdmost[windowxy1.x],&startdmost[windowxy1.x],(windowxy2.x-windowxy1.x+1)*sizeof(startdmost[0]));
12972     frameplace = bakframeplace[setviewcnt];
12973 
12974     calc_ylookup((setviewcnt == 0) ? bytesperline : bakxsiz[setviewcnt],
12975                  bakysiz[setviewcnt]);
12976 
12977     modechange=1;
12978 }
12979 
12980 
12981 //
12982 // squarerotatetile
12983 //
squarerotatetile(int16_t tilenume)12984 void squarerotatetile(int16_t tilenume)
12985 {
12986     int const siz = tilesiz[tilenume].x;
12987 
12988     if (siz != tilesiz[tilenume].y)
12989         return;
12990 
12991     char *ptr1, *ptr2;
12992 
12993     for (bssize_t i=siz-1, j; i>=3; i-=4)
12994     {
12995         ptr2 = ptr1 = (char *) (waloff[tilenume]+i*(siz+1));
12996         swapchar(--ptr1, (ptr2 -= siz));
12997         for (j=(i>>1)-1; j>=0; --j)
12998             swapchar2((ptr1 -= 2), (ptr2 -= (siz<<1)), siz);
12999 
13000         ptr2 = ptr1 = (char *) (waloff[tilenume]+(i-1)*(siz+1));
13001         for (j=((i-1)>>1)-1; j>=0; --j)
13002             swapchar2((ptr1 -= 2), (ptr2 -= (siz<<1)), siz);
13003 
13004         ptr2 = ptr1 = (char *) (waloff[tilenume]+(i-2)*(siz+1));
13005         swapchar(--ptr1, (ptr2 -= siz));
13006 
13007         for (j=((i-2)>>1)-1; j>=0; --j)
13008             swapchar2((ptr1 -= 2), (ptr2 -= (siz<<1)), siz);
13009 
13010         ptr2 = ptr1 = (char *) (waloff[tilenume]+(i-3)*(siz+1));
13011 
13012         for (j=((i-3)>>1)-1; j>=0; --j)
13013             swapchar2((ptr1 -= 2), (ptr2 -= (siz<<1)), siz);
13014     }
13015 }
13016 
13017 //
13018 // preparemirror
13019 //
renderPrepareMirror(int32_t dax,int32_t day,int32_t daz,fix16_t daang,fix16_t dahoriz,int16_t dawall,int32_t * tposx,int32_t * tposy,fix16_t * tang)13020 void renderPrepareMirror(int32_t dax, int32_t day, int32_t daz, fix16_t daang, fix16_t dahoriz, int16_t dawall,
13021                          int32_t *tposx, int32_t *tposy, fix16_t *tang)
13022 {
13023     const int32_t x = wall[dawall].x, dx = wall[wall[dawall].point2].x-x;
13024     const int32_t y = wall[dawall].y, dy = wall[wall[dawall].point2].y-y;
13025 
13026     const int32_t j = dx*dx + dy*dy;
13027     if (j == 0)
13028         return;
13029 
13030     int i = ((dax-x)*dx + (day-y)*dy)<<1;
13031 
13032     *tposx = (x<<1) + scale(dx,i,j) - dax;
13033     *tposy = (y<<1) + scale(dy,i,j) - day;
13034     *tang  = (fix16_from_int(getangle(dx, dy) << 1) - daang) & 0x7FFFFFF;
13035 
13036     inpreparemirror = 1;
13037 
13038 #ifdef USE_OPENGL
13039     if (videoGetRenderMode() == REND_POLYMOST)
13040         polymost_prepareMirror(dax, day, daz, daang, dahoriz, dawall);
13041 #endif
13042 }
13043 
13044 
13045 //
13046 // completemirror
13047 //
renderCompleteMirror(void)13048 void renderCompleteMirror(void)
13049 {
13050 #ifdef USE_OPENGL
13051     if (videoGetRenderMode() == REND_POLYMOST)
13052         polymost_completeMirror();
13053 #endif
13054 
13055     // Don't try to complete a mirror if we haven't drawn the reflection for one
13056     if (!inpreparemirror) { return; }
13057     inpreparemirror = 0;
13058 
13059 #ifdef USE_OPENGL
13060     if (videoGetRenderMode() != REND_CLASSIC)
13061         return;
13062 #endif
13063 
13064     // The mirroring code maps the rightmost pixel to the right neighbor of the
13065     // leftmost one (see copybufreverse() call below). Thus, the leftmost would
13066     // be mapped to the right neighbor of the rightmost one, which would be out
13067     // of bounds.
13068     if (mirrorsx1 == 0)
13069         mirrorsx1 = 1;
13070 
13071     // Require that the mirror is at least one pixel wide before padding.
13072     if (mirrorsx1 > mirrorsx2)
13073         return;
13074 
13075     // Variables mirrorsx{1,2} refer to the source scene here, the one drawn
13076     // from the inside of the mirror.
13077 
13078     videoBeginDrawing();
13079 
13080     // Width in pixels (screen x's are inclusive on both sides):
13081     int const width = mirrorsx2-mirrorsx1+1;
13082     // Height in pixels (screen y's are half-open because they come from umost/dmost):
13083     int const height = mirrorsy2-mirrorsy1;
13084 
13085     // Address of the mirror wall's top left corner in the source scene:
13086     intptr_t s = (intptr_t) mirrorBuffer + ylookup[windowxy1.y+mirrorsy1] + windowxy1.x+mirrorsx1;
13087 
13088     // Pointer to the mirror line's left corner in the destination:
13089     intptr_t d = (intptr_t) frameplace + ylookup[windowxy1.y+mirrorsy1] + windowxy2.x-mirrorsx2;
13090 
13091     for (bssize_t y=0; y<height; y++)
13092     {
13093 #if 0
13094         if ((p-mirrorBuffer) + width-1 >= bytesperline*ydim)
13095             printf("oob read: mirrorsx1=%d, mirrorsx2=%d\n", mirrorsx1, mirrorsx2);
13096 #endif
13097         copybufreverse((void *)(s+width-1), (void *)(d+1), width);
13098 
13099         s += ylookup[1];
13100         d += ylookup[1];
13101         faketimerhandler();
13102     }
13103 
13104     videoEndDrawing();
13105 }
13106 
13107 
13108 //
13109 // sectorofwall
13110 //
sectorofwall_internal(int16_t wallNum)13111 static int32_t sectorofwall_internal(int16_t wallNum)
13112 {
13113     native_t gap = numsectors>>1, sectNum = gap;
13114 
13115     while (gap > 1)
13116     {
13117         gap >>= 1;
13118         native_t const n = !!(sector[sectNum].wallptr < wallNum);
13119         sectNum += (n ^ (n - 1)) * gap;
13120     }
13121     while (sector[sectNum].wallptr > wallNum) sectNum--;
13122     while (sector[sectNum].wallptr + sector[sectNum].wallnum <= wallNum) sectNum++;
13123 
13124     return sectNum;
13125 }
13126 
sectorofwall(int16_t wallNum)13127 int32_t sectorofwall(int16_t wallNum)
13128 {
13129     if (EDUKE32_PREDICT_FALSE((unsigned)wallNum >= (unsigned)numwalls))
13130         return -1;
13131 
13132     native_t const w = wall[wallNum].nextwall;
13133 
13134     return ((unsigned)w < MAXWALLS) ? wall[w].nextsector : sectorofwall_internal(wallNum);
13135 }
13136 
sectorofwall_noquick(int16_t wallNum)13137 int32_t sectorofwall_noquick(int16_t wallNum)
13138 {
13139     if (EDUKE32_PREDICT_FALSE((unsigned) wallNum >= (unsigned) numwalls))
13140         return -1;
13141 
13142     return sectorofwall_internal(wallNum);
13143 }
13144 
13145 
getceilzofslopeptr(usectorptr_t sec,int32_t dax,int32_t day)13146 int32_t getceilzofslopeptr(usectorptr_t sec, int32_t dax, int32_t day)
13147 {
13148     if (!(sec->ceilingstat&2))
13149         return sec->ceilingz;
13150 
13151     auto const wal  = (uwallptr_t)&wall[sec->wallptr];
13152     auto const wal2 = (uwallptr_t)&wall[wal->point2];
13153 
13154     vec2_t const w = *(vec2_t const *)wal;
13155     vec2_t const d = { wal2->x - w.x, wal2->y - w.y };
13156 
13157     int const i = nsqrtasm(uhypsq(d.x,d.y))<<5;
13158     if (i == 0) return sec->ceilingz;
13159 
13160     int const j = dmulscale3(d.x, day-w.y, -d.y, dax-w.x);
13161     int const shift = (enginecompatibilitymode == ENGINE_EDUKE32);
13162     return sec->ceilingz + (scale(sec->ceilingheinum,j>>shift,i)<<shift);
13163 }
13164 
getflorzofslopeptr(usectorptr_t sec,int32_t dax,int32_t day)13165 int32_t getflorzofslopeptr(usectorptr_t sec, int32_t dax, int32_t day)
13166 {
13167     if (!(sec->floorstat&2))
13168         return sec->floorz;
13169 
13170     auto const wal  = (uwallptr_t)&wall[sec->wallptr];
13171     auto const wal2 = (uwallptr_t)&wall[wal->point2];
13172 
13173     vec2_t const w = *(vec2_t const *)wal;
13174     vec2_t const d = { wal2->x - w.x, wal2->y - w.y };
13175 
13176     int const i = nsqrtasm(uhypsq(d.x,d.y))<<5;
13177     if (i == 0) return sec->floorz;
13178 
13179     int const j = dmulscale3(d.x, day-w.y, -d.y, dax-w.x);
13180     int const shift = (enginecompatibilitymode == ENGINE_EDUKE32);
13181     return sec->floorz + (scale(sec->floorheinum,j>>shift,i)<<shift);
13182 }
13183 
getzsofslopeptr(usectorptr_t sec,int32_t dax,int32_t day,int32_t * ceilz,int32_t * florz)13184 void getzsofslopeptr(usectorptr_t sec, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz)
13185 {
13186     *ceilz = sec->ceilingz; *florz = sec->floorz;
13187 
13188     if (((sec->ceilingstat|sec->floorstat)&2) != 2)
13189         return;
13190 
13191     auto const wal  = (uwallptr_t)&wall[sec->wallptr];
13192     auto const wal2 = (uwallptr_t)&wall[wal->point2];
13193 
13194     vec2_t const d = { wal2->x - wal->x, wal2->y - wal->y };
13195 
13196     int const i = nsqrtasm(uhypsq(d.x,d.y))<<5;
13197     if (i == 0) return;
13198 
13199     int const j = dmulscale3(d.x,day-wal->y, -d.y,dax-wal->x);
13200     int const shift = (enginecompatibilitymode == ENGINE_EDUKE32);
13201     if (sec->ceilingstat&2)
13202         *ceilz += scale(sec->ceilingheinum,j>>shift,i)<<shift;
13203     if (sec->floorstat&2)
13204         *florz += scale(sec->floorheinum,j>>shift,i)<<shift;
13205 }
13206 
13207 #ifdef YAX_ENABLE
yax_getzsofslope(int sectNum,int playerX,int playerY,int32_t * pCeilZ,int32_t * pFloorZ)13208 void yax_getzsofslope(int sectNum, int playerX, int playerY, int32_t *pCeilZ, int32_t *pFloorZ)
13209 {
13210     int didCeiling = 0;
13211     int didFloor   = 0;
13212     int testSector = 0;
13213     int nextSector = 0;
13214 
13215     if ((sector[sectNum].ceilingstat & 512) == 0)
13216     {
13217         testSector = -1;
13218         nextSector = sectNum;
13219         while ((nextSector = yax_getneighborsect(playerX, playerY, nextSector, YAX_CEILING)) >= 0)
13220             testSector = nextSector;
13221 
13222         if (testSector >= 0)
13223         {
13224 ceiling:
13225             *pCeilZ    = getcorrectceilzofslope(testSector, playerX, playerY);
13226             didCeiling = 1;
13227         }
13228     }
13229 
13230     if ((sector[sectNum].floorstat & 512) == 0)
13231     {
13232         testSector = -1;
13233         nextSector = sectNum;
13234         while ((nextSector = yax_getneighborsect(playerX, playerY, nextSector, YAX_FLOOR)) >= 0)
13235             testSector = nextSector;
13236 
13237         if (testSector >= 0)
13238         {
13239 floor:
13240             *pFloorZ = getcorrectflorzofslope(testSector, playerX, playerY);
13241             didFloor = 1;
13242         }
13243     }
13244 
13245     testSector = sectNum;
13246 
13247     if (!didCeiling)
13248         goto ceiling;
13249     else if (!didFloor)
13250         goto floor;
13251 }
13252 
yax_getceilzofslope(int const sectnum,vec2_t const vect)13253 int32_t yax_getceilzofslope(int const sectnum, vec2_t const vect)
13254 {
13255     if ((sector[sectnum].ceilingstat&512)==0)
13256     {
13257         int testsectnum = sectnum, nsect;
13258         while ((nsect = yax_getneighborsect(vect.x, vect.y, testsectnum, YAX_CEILING)) >= 0)
13259             testsectnum = nsect;
13260         return getcorrectceilzofslope(testsectnum, vect.x, vect.y);
13261     }
13262 
13263     return getcorrectceilzofslope(sectnum, vect.x, vect.y);
13264 }
13265 
yax_getflorzofslope(int const sectnum,vec2_t const vect)13266 int32_t yax_getflorzofslope(int const sectnum, vec2_t const vect)
13267 {
13268     if ((sector[sectnum].floorstat&512)==0)
13269     {
13270         int testsectnum = sectnum, nsect;
13271         while ((nsect = yax_getneighborsect(vect.x, vect.y, testsectnum, YAX_FLOOR)) >= 0)
13272             testsectnum = nsect;
13273         return getcorrectflorzofslope(testsectnum, vect.x, vect.y);
13274     }
13275 
13276     return getcorrectflorzofslope(sectnum, vect.x, vect.y);
13277 }
13278 #endif
13279 
13280 //
13281 // alignceilslope
13282 //
alignceilslope(int16_t dasect,int32_t x,int32_t y,int32_t z)13283 void alignceilslope(int16_t dasect, int32_t x, int32_t y, int32_t z)
13284 {
13285     auto const wal = (uwallptr_t)&wall[sector[dasect].wallptr];
13286     const int32_t dax = wall[wal->point2].x-wal->x;
13287     const int32_t day = wall[wal->point2].y-wal->y;
13288 
13289     const int32_t i = (y-wal->y)*dax - (x-wal->x)*day;
13290     if (i == 0)
13291         return;
13292 
13293     sector[dasect].ceilingheinum = scale((z-sector[dasect].ceilingz)<<8,
13294                                          nsqrtasm(uhypsq(dax,day)), i);
13295     if (sector[dasect].ceilingheinum == 0)
13296         sector[dasect].ceilingstat &= ~2;
13297     else sector[dasect].ceilingstat |= 2;
13298 }
13299 
13300 
13301 //
13302 // alignflorslope
13303 //
alignflorslope(int16_t dasect,int32_t x,int32_t y,int32_t z)13304 void alignflorslope(int16_t dasect, int32_t x, int32_t y, int32_t z)
13305 {
13306     auto const wal = (uwallptr_t)&wall[sector[dasect].wallptr];
13307     const int32_t dax = wall[wal->point2].x-wal->x;
13308     const int32_t day = wall[wal->point2].y-wal->y;
13309 
13310     const int32_t i = (y-wal->y)*dax - (x-wal->x)*day;
13311     if (i == 0)
13312         return;
13313 
13314     sector[dasect].floorheinum = scale((z-sector[dasect].floorz)<<8,
13315                                        nsqrtasm(uhypsq(dax,day)), i);
13316     if (sector[dasect].floorheinum == 0)
13317         sector[dasect].floorstat &= ~2;
13318     else sector[dasect].floorstat |= 2;
13319 }
13320 
13321 
13322 //
13323 // loopnumofsector
13324 //
loopnumofsector(int16_t sectnum,int16_t wallnum)13325 int32_t loopnumofsector(int16_t sectnum, int16_t wallnum)
13326 {
13327     int32_t numloops = 0;
13328     const int32_t startwall = sector[sectnum].wallptr;
13329     const int32_t endwall = startwall + sector[sectnum].wallnum;
13330 
13331     for (bssize_t i=startwall; i<endwall; i++)
13332     {
13333         if (i == wallnum)
13334             return numloops;
13335 
13336         if (wall[i].point2 < i)
13337             numloops++;
13338     }
13339 
13340     return -1;
13341 }
13342 
13343 
13344 //
13345 // setfirstwall
13346 //
setfirstwall(int16_t sectnum,int16_t newfirstwall)13347 void setfirstwall(int16_t sectnum, int16_t newfirstwall)
13348 {
13349     int32_t i, j, numwallsofloop;
13350     int32_t dagoalloop;
13351     uwalltype *tmpwall;
13352 
13353     const int32_t startwall = sector[sectnum].wallptr;
13354     const int32_t danumwalls = sector[sectnum].wallnum;
13355     const int32_t endwall = startwall+danumwalls;
13356 
13357     if (newfirstwall < startwall || newfirstwall >= startwall+danumwalls)
13358         return;
13359 
13360     tmpwall = (uwalltype *)Xmalloc(danumwalls * sizeof(walltype));
13361 
13362     Bmemcpy(tmpwall, &wall[startwall], danumwalls*sizeof(walltype));
13363 
13364     numwallsofloop = 0;
13365     i = newfirstwall;
13366     do
13367     {
13368         numwallsofloop++;
13369         i = wall[i].point2;
13370     }
13371     while (i != newfirstwall);
13372 
13373     //Put correct loop at beginning
13374     dagoalloop = loopnumofsector(sectnum,newfirstwall);
13375     if (dagoalloop > 0)
13376     {
13377         j = 0;
13378         while (loopnumofsector(sectnum,j+startwall) != dagoalloop)
13379             j++;
13380 
13381         for (i=0; i<danumwalls; i++)
13382         {
13383             int32_t k = i+j;
13384             if (k >= danumwalls) k -= danumwalls;
13385             Bmemcpy(&wall[startwall+i], &tmpwall[k], sizeof(walltype));
13386 
13387             wall[startwall+i].point2 += danumwalls-startwall-j;
13388             if (wall[startwall+i].point2 >= danumwalls)
13389                 wall[startwall+i].point2 -= danumwalls;
13390             wall[startwall+i].point2 += startwall;
13391         }
13392 
13393         newfirstwall += danumwalls-j;
13394         if (newfirstwall >= startwall+danumwalls)
13395             newfirstwall -= danumwalls;
13396     }
13397 
13398     for (i=0; i<numwallsofloop; i++)
13399         Bmemcpy(&tmpwall[i], &wall[i+startwall], sizeof(walltype));
13400     for (i=0; i<numwallsofloop; i++)
13401     {
13402         int32_t k = i+newfirstwall-startwall;
13403         if (k >= numwallsofloop) k -= numwallsofloop;
13404         Bmemcpy(&wall[startwall+i], &tmpwall[k], sizeof(walltype));
13405 
13406         wall[startwall+i].point2 += numwallsofloop-newfirstwall;
13407         if (wall[startwall+i].point2 >= numwallsofloop)
13408             wall[startwall+i].point2 -= numwallsofloop;
13409         wall[startwall+i].point2 += startwall;
13410     }
13411 
13412     for (i=startwall; i<endwall; i++)
13413         if (wall[i].nextwall >= 0)
13414             wall[wall[i].nextwall].nextwall = i;
13415 
13416 #ifdef YAX_ENABLE
13417     int16_t cb, fb;
13418     yax_getbunches(sectnum, &cb, &fb);
13419 
13420     if (cb>=0 || fb>=0)
13421     {
13422         for (i=startwall; i<endwall; i++)
13423         {
13424             j = yax_getnextwall(i, YAX_CEILING);
13425             if (j >= 0)
13426                 yax_setnextwall(j, YAX_FLOOR, i);
13427 
13428             j = yax_getnextwall(i, YAX_FLOOR);
13429             if (j >= 0)
13430                 yax_setnextwall(j, YAX_CEILING, i);
13431         }
13432     }
13433 #endif
13434 
13435     Xfree(tmpwall);
13436 }
13437 
13438 //
13439 // qsetmodeany
13440 //
videoSet2dMode(int32_t daxdim,int32_t daydim)13441 void videoSet2dMode(int32_t daxdim, int32_t daydim)
13442 {
13443     if (daxdim < 640) daxdim = 640;
13444     if (daydim < 480) daydim = 480;
13445 
13446     if (qsetmode != ((daxdim<<16)|(daydim&0xffff)))
13447     {
13448         g_lastpalettesum = 0;
13449         if (videoSetMode(daxdim, daydim, 8, fullscreen) < 0)
13450             return;
13451 
13452         xdim = xres;
13453         ydim = yres;
13454 
13455 #ifdef USE_OPENGL
13456         fxdim = (float) xdim;
13457         fydim = (float) ydim;
13458 
13459         rendmode = REND_CLASSIC;
13460 #endif
13461 
13462         OSD_ResizeDisplay(xdim, ydim);
13463 
13464         videoAllocateBuffers();
13465 
13466         ydim16 = ydim - STATUS2DSIZ2;
13467         halfxdim16 = xdim >> 1;
13468         midydim16 = ydim16 >> 1; // scale(200,ydim,480);
13469 
13470         videoBeginDrawing(); //{{{
13471         Bmemset((char *)frameplace, 0, ydim*bytesperline);
13472         videoEndDrawing();   //}}}
13473     }
13474 
13475     qsetmode = ((daxdim<<16)|(daydim&0xffff));
13476 }
13477 
printext_checkypos(int32_t ypos,int32_t * yminptr,int32_t * ymaxptr)13478 static int32_t printext_checkypos(int32_t ypos, int32_t *yminptr, int32_t *ymaxptr)
13479 {
13480     int32_t ymin=0, ymax=7;
13481 
13482     if (ypos < 0)
13483     {
13484 /*
13485         ymin = 0-ypos;
13486         if (ymin > 7)
13487             return 1;
13488 */
13489     }
13490     else if (ypos+7 >= ydim)
13491     {
13492         ymax = ydim-ypos-1;
13493         if (ymax < 0)
13494             return 1;
13495     }
13496 
13497     *yminptr = ymin;
13498     *ymaxptr = ymax;
13499 
13500     return 0;
13501 }
13502 
13503 //
13504 // printext16
13505 //
printext16(int32_t xpos,int32_t ypos,int16_t col,int16_t backcol,const char * name,char fontsize)13506 int32_t printext16(int32_t xpos, int32_t ypos, int16_t col, int16_t backcol, const char *name, char fontsize)
13507 {
13508     int32_t ymin, ymax;
13509 
13510     if (printext_checkypos(ypos, &ymin, &ymax))
13511         return 0;
13512 
13513     if (fontsize & 2) printext16(xpos+1, ypos+1, 0, -1, name, (fontsize & ~2) | 4);
13514 
13515     int32_t const ocol = col, obackcol = backcol;
13516     char smallbuf[4];
13517 
13518     int32_t stx = xpos;
13519     const int32_t xpos0 = xpos;
13520 
13521     char const * const fontptr = (fontsize & 1) ? smalltextfont : textfont;
13522     int const charxsiz = 8 - ((fontsize & 1)<<2);
13523 
13524     for (bssize_t i=0; name[i]; i++)
13525     {
13526         if (name[i] == '^')
13527         {
13528             i++;
13529             if (name[i] == 'O') // ^O resets formatting
13530             {
13531                 if (fontsize & 4) continue;
13532 
13533                 col = ocol;
13534                 backcol = obackcol;
13535                 continue;
13536             }
13537             if (isdigit(name[i]))
13538             {
13539                 if (isdigit(name[i+1]))
13540                 {
13541                     if (isdigit(name[i+2]))
13542                     {
13543                         Bmemcpy(&smallbuf[0],&name[i],3);
13544                         i += 2;
13545                         smallbuf[3] = '\0';
13546                     }
13547                     else
13548                     {
13549                         Bmemcpy(&smallbuf[0],&name[i],2);
13550                         i++;
13551                         smallbuf[2] = '\0';
13552                     }
13553                 }
13554                 else
13555                 {
13556                     smallbuf[0] = name[i];
13557                     smallbuf[1] = '\0';
13558                 }
13559                 if (!(fontsize & 4))
13560                     col = editorcolors[Batol(smallbuf)];
13561 
13562                 if (name[i+1] == ',' && isdigit(name[i+2]))
13563                 {
13564                     i+=2;
13565                     if (isdigit(name[i+1]))
13566                     {
13567                         if (isdigit(name[i+2]))
13568                         {
13569                             Bmemcpy(&smallbuf[0],&name[i],3);
13570                             i += 2;
13571                             smallbuf[3] = '\0';
13572                         }
13573                         else
13574                         {
13575                             Bmemcpy(&smallbuf[0],&name[i],2);
13576                             i++;
13577                             smallbuf[2] = '\0';
13578                         }
13579                     }
13580                     else
13581                     {
13582                         smallbuf[0] = name[i];
13583                         smallbuf[1] = '\0';
13584                     }
13585 
13586                     if (!(fontsize & 4))
13587                         backcol = editorcolors[Batol(smallbuf)];
13588                 }
13589                 continue;
13590             }
13591         }
13592 
13593         if (name[i] == '\n')
13594         {
13595             xpos = stx = xpos0;
13596             ypos += 8;
13597             if (printext_checkypos(ypos, &ymin, &ymax))
13598                 return 0;
13599             continue;
13600         }
13601 
13602         if (stx<0)
13603         {
13604             stx += charxsiz;
13605             continue;
13606         }
13607 
13608         char const * const letptr = &fontptr[name[i]<<3];
13609 
13610         videoBeginDrawing(); //{{{
13611         char *ptr = (char *)(bytesperline*ypos + (stx-(fontsize&1)) + frameplace);
13612         int const trans = (obackcol < -1);
13613 
13614         if (trans && backcol < 0)
13615             backcol = -backcol;
13616 
13617         if (backcol >= 0)
13618         {
13619             for (bssize_t y=ymin; y<=ymax; y++)
13620             {
13621                 for (bssize_t x=0; x<charxsiz; x++)
13622                 {
13623                     if ((unsigned) (stx+x) >= (unsigned)xdim || ptr < (char *) frameplace) break;
13624                     ptr[x] = (letptr[y] & pow2char[7 - (fontsize & 1) - x]) ?
13625                              (uint8_t)col :
13626                              trans ? (uint8_t)blendtable[0][(ptr[x] * 256) + backcol] : backcol;
13627                 }
13628                 ptr += bytesperline;
13629             }
13630         }
13631         else
13632         {
13633             for (bssize_t y=ymin; y<=ymax; y++)
13634             {
13635                 for (bssize_t x=0; x<charxsiz; x++)
13636                 {
13637                     if ((unsigned) (stx+x) >= (unsigned)xdim || ptr < (char *) frameplace) break;
13638                     if (letptr[y]&pow2char[7-(fontsize&1)-x]) ptr[x] = (uint8_t) col;
13639                 }
13640                 ptr += bytesperline;
13641             }
13642         }
13643         videoEndDrawing();   //}}}
13644 
13645         stx += charxsiz;
13646 
13647         if (stx >= xdim)
13648             break;
13649     }
13650 
13651     return stx;
13652 }
13653 
13654 
13655 //
13656 // printext256
13657 //
printext256(int32_t xpos,int32_t ypos,int16_t col,int16_t backcol,const char * name,char fontsize)13658 void printext256(int32_t xpos, int32_t ypos, int16_t col, int16_t backcol, const char *name, char fontsize)
13659 {
13660     int32_t stx, i, x, y, charxsiz;
13661     char *fontptr, *letptr, *ptr;
13662 
13663     stx = xpos;
13664 
13665     if (fontsize) { fontptr = smalltextfont; charxsiz = 4; }
13666     else { fontptr = textfont; charxsiz = 8; }
13667 
13668 #ifdef USE_OPENGL
13669 #ifdef POLYMER
13670     if (videoGetRenderMode() == REND_POLYMER)
13671     {
13672         if (!polymer_printtext256(xpos,ypos,col,backcol,name,fontsize)) return;
13673     }
13674     else
13675 #endif
13676         if (!polymost_printtext256(xpos,ypos,col,backcol,name,fontsize)) return;
13677 # if 0
13678     if (videoGetRenderMode() >= REND_POLYMOST && in3dmode())
13679     {
13680         int32_t xx, yy;
13681         int32_t lc=-1;
13682         palette_t p=getpal(col), b=getpal(backcol);
13683 
13684         setpolymost2dview();
13685         glDisable(GL_ALPHA_TEST);
13686         glDepthMask(GL_FALSE);	// disable writing to the z-buffer
13687 
13688         glBegin(GL_POINTS);
13689 
13690         for (i=0; name[i]; i++)
13691         {
13692             // TODO: factor out!
13693             if (name[i] == '^' && isdigit(name[i+1]))
13694             {
13695                 char smallbuf[8];
13696                 int32_t bi=0;
13697 
13698                 while (isdigit(name[i+1]) && bi<3)
13699                 {
13700                     smallbuf[bi++]=name[i+1];
13701                     i++;
13702                 }
13703                 smallbuf[bi++]=0;
13704                 if (col)
13705                     col = Batol(smallbuf);
13706 
13707                 p = getpal(col);
13708 
13709                 continue;
13710             }
13711             letptr = &fontptr[name[i]<<3];
13712             xx = stx-fontsize;
13713             yy = ypos+7 + 2; //+1 is hack!
13714             for (y=7; y>=0; y--)
13715             {
13716                 for (x=charxsiz-1; x>=0; x--)
13717                 {
13718                     if (letptr[y]&pow2char[7-fontsize-x])
13719                     {
13720                         if (lc!=col)
13721                             glColor4ub(p.r,p.g,p.b,255);
13722                         lc = col;
13723                         glVertex2i(xx+x,yy);
13724                     }
13725                     else if (backcol >= 0)
13726                     {
13727                         if (lc!=backcol)
13728                             glColor4ub(b.r,b.g,b.b,255);
13729                         lc = backcol;
13730                         glVertex2i(xx+x,yy);
13731                     }
13732                 }
13733                 yy--;
13734             }
13735             stx += charxsiz;
13736         }
13737 
13738         glEnd();
13739         glDepthMask(GL_TRUE);  // re-enable writing to the z-buffer
13740 
13741         return;
13742     }
13743 # endif
13744 #endif
13745 
13746     videoBeginDrawing(); //{{{
13747     for (i=0; name[i]; i++)
13748     {
13749         if (name[i] == '^' && isdigit(name[i+1]))
13750         {
13751             char smallbuf[8];
13752             int32_t bi=0;
13753             while (isdigit(name[i+1]) && bi<3)
13754             {
13755                 smallbuf[bi++]=name[i+1];
13756                 i++;
13757             }
13758             smallbuf[bi++]=0;
13759             if (col)col = Batol(smallbuf);
13760             continue;
13761         }
13762 
13763         if (stx-fontsize+charxsiz > xdim)
13764             break;
13765 
13766         letptr = &fontptr[name[i]<<3];
13767         ptr = (char *)(ylookup[ypos+7]+(stx-fontsize)+frameplace);
13768         for (y=7; y>=0; y--)
13769         {
13770             for (x=charxsiz-1; x>=0; x--)
13771             {
13772                 if (letptr[y]&pow2char[7-fontsize-x])
13773                     ptr[x] = (uint8_t)col;
13774                 else if (backcol >= 0)
13775                     ptr[x] = (uint8_t)backcol;
13776             }
13777             ptr -= ylookup[1];
13778         }
13779         stx += charxsiz;
13780     }
13781     videoEndDrawing();   //}}}
13782 }
13783 
13784 
13785 #ifdef POLYMER
PolymerProcessModels(void)13786 static void PolymerProcessModels(void)
13787 {
13788     // potentially deferred MD3 postprocessing
13789     for (bssize_t i=0; i<nextmodelid; i++)
13790     {
13791         if (models[i]->mdnum==3 && ((md3model_t *)models[i])->head.surfs[0].geometry == NULL)
13792         {
13793             static int32_t warned=0;
13794 
13795             if (!warned)
13796             {
13797                 OSD_Printf("Post-processing MD3 models for Polymer. This may take a while...\n");
13798                 videoNextPage();
13799                 warned = 1;
13800             }
13801 
13802             if (!md3postload_polymer((md3model_t *)models[i]))
13803                 OSD_Printf("INTERNAL ERROR: mdmodel %s failed postprocessing!\n",
13804                            ((md3model_t *)models[i])->head.nam);
13805 
13806             if (((md3model_t *)models[i])->head.surfs[0].geometry == NULL)
13807                 OSD_Printf("INTERNAL ERROR: wtf?\n");
13808         }
13809 //        else
13810 //            OSD_Printf("mdmodel %d already postprocessed.\n", i);
13811     }
13812 }
13813 #endif
13814 
13815 //
13816 // setrendermode
13817 //
videoSetRenderMode(int32_t renderer)13818 int32_t videoSetRenderMode(int32_t renderer)
13819 {
13820     UNREFERENCED_PARAMETER(renderer);
13821 
13822 #ifdef USE_OPENGL
13823     if (bpp == 8)
13824     {
13825         glDisable(GL_BLEND);
13826         glDisable(GL_ALPHA_TEST);
13827         renderer = REND_CLASSIC;
13828     }
13829 # ifdef POLYMER
13830     else
13831         renderer = clamp(renderer, REND_POLYMOST, REND_POLYMER);
13832 
13833     if (renderer == REND_POLYMER)
13834     {
13835         PolymerProcessModels();
13836 
13837         if (!polymer_init())
13838             renderer = REND_POLYMOST;
13839     }
13840     else if (videoGetRenderMode() == REND_POLYMER)  // going from Polymer to another renderer
13841     {
13842         engineClearLightsFromMHK();
13843         G_Polymer_UnInit();
13844         polymer_uninit();
13845     }
13846 # else
13847     else renderer = REND_POLYMOST;
13848 # endif
13849 
13850     basepalreset = 1;
13851 
13852     rendmode = renderer;
13853     if (videoGetRenderMode() >= REND_POLYMOST)
13854         glrendmode = rendmode;
13855 
13856     if (renderer == REND_POLYMOST)
13857     {
13858         polymost_init();
13859     }
13860 #endif
13861 
13862     return 0;
13863 }
13864 
13865 //
13866 // setrollangle
13867 //
13868 #ifdef USE_OPENGL
renderSetRollAngle(int32_t rolla)13869 void renderSetRollAngle(int32_t rolla)
13870 {
13871     gtang = (float)rolla * (fPI * (1.f/1024.f));
13872 }
13873 #endif
13874 
13875 
13876 //
13877 // invalidatetile
13878 //  pal: pass -1 to invalidate all palettes for the tile, or >=0 for a particular palette
13879 //  how: pass -1 to invalidate all instances of the tile in texture memory, or a bitfield
13880 //         bit 0: opaque or masked (non-translucent) texture, using repeating
13881 //         bit 1: ignored
13882 //         bit 2: ignored (33% translucence, using repeating)
13883 //         bit 3: ignored (67% translucence, using repeating)
13884 //         bit 4: opaque or masked (non-translucent) texture, using clamping
13885 //         bit 5: ignored
13886 //         bit 6: ignored (33% translucence, using clamping)
13887 //         bit 7: ignored (67% translucence, using clamping)
13888 //       clamping is for sprites, repeating is for walls
13889 //
tileInvalidate(int16_t tilenume,int32_t pal,int32_t how)13890 void tileInvalidate(int16_t tilenume, int32_t pal, int32_t how)
13891 {
13892 #if !defined USE_OPENGL
13893     UNREFERENCED_PARAMETER(tilenume);
13894     UNREFERENCED_PARAMETER(pal);
13895     UNREFERENCED_PARAMETER(how);
13896 #else
13897     if (videoGetRenderMode() >= REND_POLYMOST)
13898     {
13899         const int32_t firstpal = (pal < 0) ? 0 : pal;
13900         const int32_t numpals = (pal < 0) ? MAXPALOOKUPS : 1;
13901 
13902         for (bssize_t hp = 0; hp <= 4; hp+=4)
13903         {
13904             if (how & pow2long[hp])
13905                 for (bssize_t np = firstpal; np < firstpal+numpals; np++)
13906                     gltexinvalidate(tilenume, np, hp);
13907         }
13908 
13909 #ifdef POLYMER
13910         if (videoGetRenderMode() == REND_POLYMER)
13911             polymer_invalidateartmap(tilenume);
13912 #endif
13913     }
13914 #endif
13915 }
13916 
13917 /*
13918  * vim:ts=8:
13919  */
13920 
13921