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 (*(§or[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 *(§or[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 *(§or[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)§or[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)§or[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 *)§or[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)§or[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)§or[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)§or[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)§or[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)§or[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)§or[sectnum];
6674 auto nsec = (usectorptr_t)§or[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)§or[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(§or[i], &(((sectortypev7 *)sector)[i]), sizeof(sectortypevx));
10376 inplace_vx_from_v7_sector(§or[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(§or[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, §or[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, §or[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], §or[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 = §or[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 = §or[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)§or[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)§or[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(§1, §2);
12252
12253 auto const sec = (usectorptr_t)§or[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, §, 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, §, 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 = §or[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 = §or[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