1 /**************************************************************************************************
2 "POLYMOST" code originally written by Ken Silverman
3 Ken Silverman's official web site: http://www.advsys.net/ken
4 
5 "POLYMOST2" changes Copyright (c) 2018, Alex Dawson
6 **************************************************************************************************/
7 
8 
9 #ifdef USE_OPENGL
10 
11 #include "build.h"
12 #include "common.h"
13 #include "engine_priv.h"
14 #include "kplib.h"
15 #include "mdsprite.h"
16 #include "polymost.h"
17 #include "microprofile.h"
18 #include "tilepacker.h"
19 
20 extern char textfont[2048], smalltextfont[2048];
21 
22 int32_t rendmode=0;
23 int32_t usemodels=1;
24 int32_t usehightile=1;
25 
26 typedef struct { float x, cy[2], fy[2]; int32_t tag; int16_t n, p, ctag, ftag; } vsptyp;
27 #define VSPMAX 2048 //<- careful!
28 static vsptyp vsp[VSPMAX];
29 static int32_t gtag, viewportNodeCount;
30 static float xbl, xbr, xbt, xbb;
31 int32_t domost_rejectcount;
32 #ifdef YAX_ENABLE
33 typedef struct { float x, cy[2]; int32_t tag; int16_t n, p, ctag; } yax_vsptyp;
34 static yax_vsptyp yax_vsp[YAX_MAXBUNCHES*2][VSPMAX];
35 typedef struct { float x0, x1, cy[2], fy[2]; } yax_hole_t;
36 static yax_hole_t yax_holecf[2][VSPMAX];
37 static int32_t yax_holencf[2];
38 static int32_t yax_drawcf = -1;
39 #endif
40 
41 static float dxb1[MAXWALLSB], dxb2[MAXWALLSB];
42 
43 //POGOTODO: the SCISDIST could be set to 0 now to allow close objects to render properly,
44 //          but there's a nasty rendering bug that needs to be dug into when setting SCISDIST lower than 1
45 #define SCISDIST 1.f  //close plane clipping distance
46 
47 #define SOFTROTMAT 0
48 
49 float shadescale = 1.0f;
50 int32_t shadescale_unbounded = 0;
51 
52 int32_t r_polymostDebug = 0;
53 int32_t r_enablepolymost2 = 0;
54 int32_t r_usenewshading = 4;
55 int32_t r_usetileshades = 1;
56 int32_t r_npotwallmode = 2;
57 int32_t polymostcenterhoriz = 100;
58 
59 static float gviewxrange;
60 static float ghoriz, ghoriz2;
61 static float ghorizcorrect;
62 double gxyaspect;
63 float gyxscale, ghalfx, grhalfxdown10, grhalfxdown10x, ghalfy;
64 float gcosang, gsinang, gcosang2, gsinang2;
65 float gchang, gshang, gctang, gstang, gvisibility;
66 float gtang = 0.f;
67 float gvrcorrection = 1.f;
68 
69 static vec3d_t xtex, ytex, otex, xtex2, ytex2, otex2;
70 
71 float fcosglobalang, fsinglobalang;
72 float fxdim, fydim, fydimen, fviewingrange;
73 
74 float fsearchx, fsearchy, fsearchz;
75 int psectnum, pwallnum, pbottomwall, pisbottomwall, psearchstat, doeditorcheck = 0;
76 
77 static int32_t drawpoly_srepeat = 0, drawpoly_trepeat = 0;
78 #define MAX_DRAWPOLY_VERTS 8
79 #define BUFFER_OFFSET(bytes) (GLintptr) ((GLubyte*) NULL + (bytes))
80 // these cvars are never used directly in rendering -- only when glinit() is called/renderer reset
81 // We do this because we don't want to accidentally overshoot our existing buffer's bounds
82 uint32_t r_persistentStreamBuffer = 1;
83 uint32_t persistentStreamBuffer = r_persistentStreamBuffer;
84 int32_t r_drawpolyVertsBufferLength = 30000;
85 int32_t drawpolyVertsBufferLength = r_drawpolyVertsBufferLength;
86 static GLuint drawpolyVertsID = 0;
87 static GLint drawpolyVertsOffset = 0;
88 static int32_t drawpolyVertsSubBufferIndex = 0;
89 static GLsync drawpolyVertsSync[3] = { 0 };
90 static float defaultDrawpolyVertsArray[MAX_DRAWPOLY_VERTS*5];
91 static float* drawpolyVerts = defaultDrawpolyVertsArray;
92 
93 struct glfiltermodes glfiltermodes[NUMGLFILTERMODES] =
94 {
95     {"GL_NEAREST",GL_NEAREST,GL_NEAREST},
96     {"GL_LINEAR",GL_LINEAR,GL_LINEAR},
97     {"GL_NEAREST_MIPMAP_NEAREST",GL_NEAREST_MIPMAP_NEAREST,GL_NEAREST},
98     {"GL_LINEAR_MIPMAP_NEAREST",GL_LINEAR_MIPMAP_NEAREST,GL_LINEAR},
99     {"GL_NEAREST_MIPMAP_LINEAR",GL_NEAREST_MIPMAP_LINEAR,GL_NEAREST},
100     {"GL_LINEAR_MIPMAP_LINEAR",GL_LINEAR_MIPMAP_LINEAR,GL_LINEAR}
101 };
102 
103 int32_t glanisotropy = 0;            // 0 = maximum supported by card
104 int32_t gltexfiltermode = TEXFILTER_OFF;
105 
106 #ifdef EDUKE32_GLES
107 int32_t glusetexcompr = 2;
108 int32_t glusetexcache = 0, glusememcache = 0;
109 #else
110 int32_t glusetexcompr = 1;
111 int32_t glusetexcache = 2, glusememcache = 1;
112 int32_t r_polygonmode = 0;     // 0:GL_FILL,1:GL_LINE,2:GL_POINT //FUK
113 static int32_t lastglpolygonmode = 0; //FUK
114 #endif
115 #ifdef USE_GLEXT
116 int32_t r_detailmapping = 1;
117 int32_t r_glowmapping = 1;
118 #endif
119 
120 int polymost2d;
121 
122 int32_t gltexmaxsize = 0;      // 0 means autodetection on first run
123 int32_t gltexmiplevel = 0;		// discards this many mipmap levels
124 int32_t glprojectionhacks = 2;
125 static GLuint polymosttext = 0;
126 int32_t glrendmode = REND_POLYMOST;
127 int32_t r_shadeinterpolate = 1;
128 
129 // This variable, and 'shadeforfullbrightpass' control the drawing of
130 // fullbright tiles.  Also see 'fullbrightloadingpass'.
131 
132 int32_t r_fullbrights = 1;
133 int32_t r_vertexarrays = 1;
134 #ifdef USE_GLEXT
135 //POGOTODO: we no longer support rendering without VBOs -- update any outdated pre-GL2 code that renders without VBOs
136 int32_t r_vbos = 1;
137 int32_t r_vbocount = 64;
138 #endif
139 int32_t r_animsmoothing = 1;
140 int32_t r_downsize = 0;
141 int32_t r_downsizevar = -1;
142 int32_t r_brightnesshack = 0;
143 
144 int32_t r_rortexture = 0;
145 int32_t r_rortexturerange = 0;
146 int32_t r_rorphase = 0;
147 
148 int32_t r_yshearing = 0;
149 int32_t r_flatsky = 1;
150 
151 // used for fogcalc
152 static float fogresult, fogresult2;
153 coltypef fogcol, fogtable[MAXPALOOKUPS];
154 float fogfactor[MAXPALOOKUPS];
155 
156 static uint32_t currentShaderProgramID = 0;
157 static GLenum currentActiveTexture = 0;
158 static uint32_t currentTextureID = 0;
159 
160 static GLuint quadVertsID = 0;
161 static GLuint polymost2BasicShaderProgramID = 0;
162 static GLint texSamplerLoc = -1;
163 static GLint fullBrightSamplerLoc = -1;
164 static GLint projMatrixLoc = -1;
165 static GLint mvMatrixLoc = -1;
166 static GLint texOffsetLoc = -1;
167 static GLint texScaleLoc = -1;
168 static GLint tintLoc = -1;
169 static GLint alphaLoc = -1;
170 static GLint fogRangeLoc = -1;
171 static GLint fogColorLoc = -1;
172 
173 #define PALSWAP_TEXTURE_SIZE 2048
174 int32_t r_useindexedcolortextures = 1;
175 static GLuint tilesheetTexIDs[MAXTILESHEETS];
176 static GLint tilesheetSize = 0;
177 static vec2f_t tilesheetHalfTexelSize = { 0.f, 0.f };
178 static int32_t lastbasepal = -1;
179 static GLuint paletteTextureIDs[MAXBASEPALS];
180 static GLuint palswapTextureID = 0;
181 extern char const *polymost1Frag;
182 extern char const *polymost1Vert;
183 static GLuint polymost1CurrentShaderProgramID = 0;
184 static GLuint polymost1BasicShaderProgramID = 0;
185 static GLuint polymost1ExtendedShaderProgramID = 0;
186 static GLint polymost1TexSamplerLoc = -1;
187 static GLint polymost1PalSwapSamplerLoc = -1;
188 static GLint polymost1PaletteSamplerLoc = -1;
189 static GLint polymost1DetailSamplerLoc = -1;
190 static GLint polymost1GlowSamplerLoc = -1;
191 static GLint polymost1TexturePosSizeLoc = -1;
192 static vec4f_t polymost1TexturePosSize = { 0.f, 0.f, 1.f, 1.f };
193 static GLint polymost1HalfTexelSizeLoc = -1;
194 static vec2f_t polymost1HalfTexelSize = { 0.f, 0.f };
195 static GLint polymost1PalswapPosLoc = -1;
196 static vec2f_t polymost1PalswapPos = { 0.f, 0.f };
197 static GLint polymost1PalswapSizeLoc = -1;
198 static vec2f_t polymost1PalswapSize = { 0.f, 0.f };
199 static vec2f_t polymost1PalswapInnerSize = { 0.f, 0.f };
200 static GLint polymost1ClampLoc = -1;
201 static vec2f_t polymost1Clamp = { 0.f, 0.f };
202 static GLint polymost1ShadeLoc = -1;
203 static float polymost1Shade = 0.f;
204 static GLint polymost1NumShadesLoc = -1;
205 static float polymost1NumShades = 64.f;
206 static GLint polymost1VisFactorLoc = -1;
207 static float polymost1VisFactor = 128.f;
208 static GLint polymost1FogEnabledLoc = -1;
209 static float polymost1FogEnabled = 1.f;
210 static GLint polymost1UseColorOnlyLoc = -1;
211 static float polymost1UseColorOnly = 0.f;
212 static GLint polymost1UsePaletteLoc = -1;
213 static float polymost1UsePalette = 1.f;
214 static GLint polymost1UseDetailMappingLoc = -1;
215 static float polymost1UseDetailMapping = 0.f;
216 static GLint polymost1UseGlowMappingLoc = -1;
217 static float polymost1UseGlowMapping = 0.f;
218 static GLint polymost1NPOTEmulationLoc = -1;
219 static float polymost1NPOTEmulation = 0.f;
220 static GLint polymost1NPOTEmulationFactorLoc = -1;
221 static float polymost1NPOTEmulationFactor = 1.f;
222 static GLint polymost1NPOTEmulationXOffsetLoc = -1;
223 static float polymost1NPOTEmulationXOffset = 0.f;
224 static GLint polymost1BrightnessLoc = -1;
225 static float polymost1Brightness = 1.f;
226 static GLint polymost1RotMatrixLoc = -1;
227 static float polymost1RotMatrix[16] = { 1.f, 0.f, 0.f, 0.f,
228                                         0.f, 1.f, 0.f, 0.f,
229                                         0.f, 0.f, 1.f, 0.f,
230                                         0.f, 0.f, 0.f, 1.f };
231 static GLint polymost1ShadeInterpolateLoc = -1;
232 static float polymost1ShadeInterpolate = 1.f;
233 
float_trans(uint32_t maskprops,uint8_t blend)234 static inline float float_trans(uint32_t maskprops, uint8_t blend)
235 {
236     switch (maskprops)
237     {
238     case DAMETH_TRANS1:
239     case DAMETH_TRANS2:
240         return glblend[blend].def[maskprops-2].alpha;
241     default:
242         return 1.0f;
243     }
244 }
245 
246 char ptempbuf[MAXWALLSB<<1];
247 
248 // polymost ART sky control
249 int32_t r_parallaxskyclamping = 1;
250 int32_t r_parallaxskypanning = 1;
251 
252 #define MIN_CACHETIME_PRINT 10
253 
254 // this was faster in MSVC but slower with GCC... currently unknown on ARM where both
255 // the FPU and possibly the optimization path in the compiler need improvement
256 #if 0
257 static inline int32_t __float_as_int(float f) { return *(int32_t *) &f; }
258 static inline float __int_as_float(int32_t d) { return *(float *) &d; }
259 static inline float Bfabsf(float f) { return __int_as_float(__float_as_int(f)&0x7fffffff); }
260 #else
261 #define Bfabsf fabsf
262 #endif
263 
264 int32_t mdtims, omdtims;
265 uint8_t alphahackarray[MAXTILES];
266 int32_t drawingskybox = 0;
267 int32_t hicprecaching = 0;
268 
269 hitdata_t polymost_hitdata;
270 
polymost_outputGLDebugMessage(uint8_t severity,const char * format,...)271 void polymost_outputGLDebugMessage(uint8_t severity, const char* format, ...)
272 {
273     static char msg[8192];
274     va_list vArgs;
275 
276     if (!glinfo.debugoutput ||
277         r_polymostDebug < severity)
278     {
279         return;
280     }
281 
282     va_start(vArgs, format);
283     Bvsnprintf(msg, sizeof(msg), format, vArgs);
284     va_end(vArgs);
285 
286     glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB,
287                             GL_DEBUG_TYPE_OTHER_ARB,
288                             0,
289                             GL_DEBUG_SEVERITY_HIGH_ARB+severity-1,
290                             -1,
291                             msg);
292 }
293 
294 #if 0
295 static inline int32_t gltexmayhavealpha(int32_t dapicnum, int32_t dapalnum)
296 {
297     const int32_t j = (dapicnum&(GLTEXCACHEADSIZ-1));
298     pthtyp *pth;
299 
300     for (pth=texcache.list[j]; pth; pth=pth->next)
301         if (pth->picnum == dapicnum && pth->palnum == dapalnum)
302             return ((pth->flags&PTH_HASALPHA) != 0);
303 
304     return 1;
305 }
306 #endif
307 
gltexinvalidate(int32_t dapicnum,int32_t dapalnum,int32_t dameth)308 void gltexinvalidate(int32_t dapicnum, int32_t dapalnum, int32_t dameth)
309 {
310     const int32_t pic = (dapicnum&(GLTEXCACHEADSIZ-1));
311 
312     for (pthtyp *pth=texcache.list[pic]; pth; pth=pth->next)
313         if (pth->picnum == dapicnum && pth->palnum == dapalnum &&
314             (pth->flags & PTH_CLAMPED) == TO_PTH_CLAMPED(dameth))
315         {
316             pth->flags |= PTH_INVALIDATED;
317             if (pth->flags & PTH_HASFULLBRIGHT)
318                 pth->ofb->flags |= PTH_INVALIDATED;
319         }
320 }
321 
322 //Make all textures "dirty" so they reload, but not re-allocate
323 //This should be much faster than polymost_glreset()
324 //Use this for palette effects ... but not ones that change every frame!
gltexinvalidatetype(int32_t type)325 void gltexinvalidatetype(int32_t type)
326 {
327     for (bssize_t j=0; j<=GLTEXCACHEADSIZ-1; j++)
328     {
329         for (pthtyp *pth=texcache.list[j]; pth; pth=pth->next)
330         {
331             if (type == INVALIDATE_ALL ||
332                 (type == INVALIDATE_ALL_NON_INDEXED && !(pth->flags & PTH_INDEXED)) ||
333                 (type == INVALIDATE_ART && pth->hicr == NULL) ||
334                 (type == INVALIDATE_ART_NON_INDEXED && pth->hicr == NULL && !(pth->flags & PTH_INDEXED)))
335             {
336                 pth->flags |= PTH_INVALIDATED;
337                 if (pth->flags & PTH_HASFULLBRIGHT)
338                     pth->ofb->flags |= PTH_INVALIDATED;
339             }
340         }
341     }
342 
343     clearskins(type);
344 
345 #ifdef DEBUGGINGAIDS
346     OSD_Printf("gltexinvalidateall()\n");
347 #endif
348 }
349 
bind_2d_texture(GLuint texture,int filter)350 static void bind_2d_texture(GLuint texture, int filter)
351 {
352     if (filter == -1)
353         filter = gltexfiltermode;
354 
355     glBindTexture(GL_TEXTURE_2D, texture);
356     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glfiltermodes[filter].mag);
357     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glfiltermodes[filter].min);
358 #ifdef USE_GLEXT
359     if (glinfo.maxanisotropy > 1.f)
360         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glanisotropy);
361 #endif
362 }
363 
gltexapplyprops(void)364 void gltexapplyprops(void)
365 {
366     if (videoGetRenderMode() == REND_CLASSIC)
367         return;
368 
369     if (glinfo.maxanisotropy > 1.f)
370     {
371         if (glanisotropy <= 0 || glanisotropy > glinfo.maxanisotropy)
372             glanisotropy = (int32_t)glinfo.maxanisotropy;
373     }
374 
375     gltexfiltermode = clamp(gltexfiltermode, 0, NUMGLFILTERMODES-1);
376 
377     for (bssize_t i=0; i<=GLTEXCACHEADSIZ-1; i++)
378     {
379         for (pthtyp *pth=texcache.list[i]; pth; pth=pth->next)
380         {
381             if (pth->flags & PTH_INDEXED)
382             {
383                 //POGO: indexed textures should not be filtered
384                 continue;
385             }
386 
387             int32_t const filter = (pth->flags & PTH_FORCEFILTER) ? TEXFILTER_ON : -1;
388 
389             bind_2d_texture(pth->glpic, filter);
390 
391             if (r_fullbrights && pth->flags & PTH_HASFULLBRIGHT)
392                 bind_2d_texture(pth->ofb->glpic, filter);
393         }
394     }
395 
396     for (bssize_t i=0; i<nextmodelid; i++)
397     {
398         md2model_t *m = (md2model_t *)models[i];
399 
400         if (m->mdnum < 2)
401             continue;
402 
403         for (bssize_t j = 0; j < m->numskins * HICTINT_MEMORY_COMBINATIONS; j++)
404         {
405             if (!m->texid[j])
406                 continue;
407             bind_2d_texture(m->texid[j], -1);
408         }
409 
410         for (mdskinmap_t *sk = m->skinmap; sk; sk = sk->next)
411             for (bssize_t j = 0; j < HICTINT_MEMORY_COMBINATIONS; j++)
412             {
413                 if (!sk->texid[j])
414                     continue;
415                 bind_2d_texture(sk->texid[j], (sk->flags & HICR_FORCEFILTER) ? TEXFILTER_ON : -1);
416             }
417     }
418 }
419 
420 //--------------------------------------------------------------------------------------------------
421 
422 //Use this for both initialization and uninitialization of OpenGL.
423 static int32_t gltexcacnum = -1;
424 
425 //in-place multiply m0=m0*m1
multiplyMatrix4f(float m0[4* 4],const float m1[4* 4])426 float* multiplyMatrix4f(float m0[4*4], const float m1[4*4])
427 {
428     float mR[4*4];
429 
430 #define multMatrix4RowCol(r, c) mR[r*4+c] = m0[r*4]*m1[c] + m0[r*4+1]*m1[c+4] + m0[r*4+2]*m1[c+8] + m0[r*4+3]*m1[c+12]
431 
432     multMatrix4RowCol(0, 0);
433     multMatrix4RowCol(0, 1);
434     multMatrix4RowCol(0, 2);
435     multMatrix4RowCol(0, 3);
436 
437     multMatrix4RowCol(1, 0);
438     multMatrix4RowCol(1, 1);
439     multMatrix4RowCol(1, 2);
440     multMatrix4RowCol(1, 3);
441 
442     multMatrix4RowCol(2, 0);
443     multMatrix4RowCol(2, 1);
444     multMatrix4RowCol(2, 2);
445     multMatrix4RowCol(2, 3);
446 
447     multMatrix4RowCol(3, 0);
448     multMatrix4RowCol(3, 1);
449     multMatrix4RowCol(3, 2);
450     multMatrix4RowCol(3, 3);
451 
452     Bmemcpy(m0, mR, sizeof(float)*4*4);
453 
454     return m0;
455 
456 #undef multMatrix4RowCol
457 }
458 
calcmat(vec3f_t a0,const vec2f_t * offset,float f,float mat[16],int16_t angle)459 static void calcmat(vec3f_t a0, const vec2f_t *offset, float f, float mat[16], int16_t angle)
460 {
461     float g;
462     float k0, k1, k2, k3, k4, k5, k6, k7;
463 
464     k0 = a0.y;
465     k1 = a0.x;
466     a0.x += offset->x;
467     a0.z += offset->y;
468     f = gcosang2*gshang;
469     g = gsinang2*gshang;
470     k4 = (float)sintable[(angle+1024)&2047] * (1.f/16384.f);
471     k5 = (float)sintable[(angle+512)&2047] * (1.f/16384.f);
472     k2 = k0*(1-k4)+k1*k5;
473     k3 = k1*(1-k4)-k0*k5;
474     k6 = f*gstang - gsinang*gctang; k7 = g*gstang + gcosang*gctang;
475     mat[0] = k4*k6 + k5*k7; mat[4] = gchang*gstang; mat[ 8] = k4*k7 - k5*k6; mat[12] = k2*k6 + k3*k7;
476     k6 = f*gctang + gsinang*gstang; k7 = g*gctang - gcosang*gstang;
477     mat[1] = k4*k6 + k5*k7; mat[5] = gchang*gctang; mat[ 9] = k4*k7 - k5*k6; mat[13] = k2*k6 + k3*k7;
478     k6 =           gcosang2*gchang; k7 =           gsinang2*gchang;
479     mat[2] = k4*k6 + k5*k7; mat[6] =-gshang;        mat[10] = k4*k7 - k5*k6; mat[14] = k2*k6 + k3*k7;
480 
481     mat[12] = (mat[12] + a0.y*mat[0]) + (a0.z*mat[4] + a0.x*mat[ 8]);
482     mat[13] = (mat[13] + a0.y*mat[1]) + (a0.z*mat[5] + a0.x*mat[ 9]);
483     mat[14] = (mat[14] + a0.y*mat[2]) + (a0.z*mat[6] + a0.x*mat[10]);
484 }
485 
polymost2_compileShader(GLenum shaderType,const char * const source,int * pLength)486 GLuint polymost2_compileShader(GLenum shaderType, const char* const source, int * pLength)
487 {
488     GLuint shaderID = glCreateShader(shaderType);
489     if (shaderID == 0)
490     {
491         return 0;
492     }
493 
494     glShaderSource(shaderID,
495                    1,
496                    &source,
497                    pLength);
498     glCompileShader(shaderID);
499 
500     GLint compileStatus;
501     glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compileStatus);
502     if (!compileStatus)
503     {
504         GLint logLength;
505         glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &logLength);
506         OSD_Printf("Compile Status: %u\n", compileStatus);
507         if (logLength > 0)
508         {
509             char *infoLog = (char*)Bmalloc(logLength);
510             glGetShaderInfoLog(shaderID, logLength, &logLength, infoLog);
511             OSD_Printf("Log:\n%s\n", infoLog);
512             Bfree(infoLog);
513         }
514     }
515 
516     return shaderID;
517 }
518 
polymost2_compileShader(GLenum shaderType,const char * const source,int length)519 static GLuint polymost2_compileShader(GLenum shaderType, const char* const source, int length)
520 {
521     return polymost2_compileShader(shaderType, source, &length);
522 }
523 
polymost_glreset()524 void polymost_glreset()
525 {
526     for (bssize_t i=0; i<=MAXPALOOKUPS-1; i++)
527     {
528         fogtable[i].r = palookupfog[i].r * (1.f/255.f);
529         fogtable[i].g = palookupfog[i].g * (1.f/255.f);
530         fogtable[i].b = palookupfog[i].b * (1.f/255.f);
531         fogtable[i].a = 0;
532         fogfactor[i] = palookupfogfactor[i];
533     }
534 
535     //Reset if this is -1 (meaning 1st texture call ever), or > 0 (textures in memory)
536     if (gltexcacnum < 0)
537     {
538         gltexcacnum = 0;
539 
540         //Hack for polymost_dorotatesprite calls before 1st polymost_drawrooms()
541         gcosang = gcosang2 = 16384.f/262144.f;
542         gsinang = gsinang2 = 0.f;
543     }
544     else
545     {
546         for (bssize_t i = 0; i <= GLTEXCACHEADSIZ-1; i++)
547         {
548             for (pthtyp *pth = texcache.list[i]; pth;)
549             {
550                 pthtyp *const next = pth->next;
551 
552                 if (pth->flags & PTH_HASFULLBRIGHT)
553                 {
554                     glDeleteTextures(1, &pth->ofb->glpic);
555                     Xfree(pth->ofb);
556                 }
557 
558                 glDeleteTextures(1, &pth->glpic);
559                 Xfree(pth);
560                 pth = next;
561             }
562 
563             texcache.list[i] = NULL;
564         }
565 
566         clearskins(INVALIDATE_ALL);
567     }
568 
569     if (polymosttext)
570         glDeleteTextures(1,&polymosttext);
571     polymosttext=0;
572 
573 #ifdef USE_GLEXT
574     md_freevbos();
575 #endif
576 
577     Bmemset(texcache.list,0,sizeof(texcache.list));
578 
579     texcache_freeptrs();
580     texcache_syncmemcache();
581 
582 #ifdef DEBUGGINGAIDS
583     OSD_Printf("polymost_glreset()\n");
584 #endif
585 }
586 
587 #if defined EDUKE32_GLES
588 static void Polymost_DetermineTextureFormatSupport(void);
589 #endif
590 
591 // reset vertex pointers to polymost default
polymost_resetVertexPointers()592 void polymost_resetVertexPointers()
593 {
594     polymost_outputGLDebugMessage(3, "polymost_resetVertexPointers()");
595 
596     glBindBuffer(GL_ARRAY_BUFFER, drawpolyVertsID);
597 
598     glVertexPointer(3, GL_FLOAT, 5*sizeof(float), 0);
599     glTexCoordPointer(2, GL_FLOAT, 5*sizeof(float), (GLvoid*) (3*sizeof(float)));
600 
601 #ifdef USE_GLEXT
602     if (r_detailmapping)
603     {
604         glClientActiveTexture(GL_TEXTURE3);
605         glTexCoordPointer(2, GL_FLOAT, 5*sizeof(float), (GLvoid*) (3*sizeof(float)));
606     }
607     if (r_glowmapping)
608     {
609         glClientActiveTexture(GL_TEXTURE4);
610         glTexCoordPointer(2, GL_FLOAT, 5*sizeof(float), (GLvoid*) (3*sizeof(float)));
611     }
612     glClientActiveTexture(GL_TEXTURE0);
613 #endif
614 
615     polymost_resetProgram();
616 }
617 
polymost_disableProgram()618 void polymost_disableProgram()
619 {
620     if (videoGetRenderMode() != REND_POLYMOST)
621         return;
622 
623     polymost_outputGLDebugMessage(3, "polymost_disableProgram()");
624 
625     polymost_useShaderProgram(0);
626 }
627 
polymost_resetProgram()628 void polymost_resetProgram()
629 {
630     if (videoGetRenderMode() != REND_POLYMOST)
631         return;
632 
633     polymost_outputGLDebugMessage(3, "polymost_resetProgram()");
634 
635     if (r_enablepolymost2)
636         polymost_useShaderProgram(polymost2BasicShaderProgramID);
637     else
638         polymost_useShaderProgram(polymost1CurrentShaderProgramID);
639 
640     // ensure that palswapTexture and paletteTexture[curbasepal] is bound
641     glActiveTexture(GL_TEXTURE1);
642     glBindTexture(GL_TEXTURE_2D, palswapTextureID);
643     glActiveTexture(GL_TEXTURE2);
644     glBindTexture(GL_TEXTURE_2D, paletteTextureIDs[curbasepal]);
645     glActiveTexture(GL_TEXTURE0);
646 }
647 
polymost_setCurrentShaderProgram(uint32_t programID)648 static void polymost_setCurrentShaderProgram(uint32_t programID)
649 {
650     polymost_outputGLDebugMessage(3, "polymost_setCurrentShaderProgram(programID:%u)", programID);
651 
652     polymost1CurrentShaderProgramID = programID;
653     polymost_useShaderProgram(programID);
654 
655     //update the uniform locations
656     polymost1TexSamplerLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "s_texture");
657     polymost1PalSwapSamplerLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "s_palswap");
658     polymost1PaletteSamplerLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "s_palette");
659     polymost1DetailSamplerLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "s_detail");
660     polymost1GlowSamplerLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "s_glow");
661     polymost1TexturePosSizeLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_texturePosSize");
662     polymost1HalfTexelSizeLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_halfTexelSize");
663     polymost1PalswapPosLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_palswapPos");
664     polymost1PalswapSizeLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_palswapSize");
665     polymost1ClampLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_clamp");
666     polymost1ShadeLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_shade");
667     polymost1NumShadesLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_numShades");
668     polymost1VisFactorLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_visFactor");
669     polymost1FogEnabledLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_fogEnabled");
670     polymost1UsePaletteLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_usePalette");
671     polymost1UseColorOnlyLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_useColorOnly");
672     polymost1UseDetailMappingLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_useDetailMapping");
673     polymost1UseGlowMappingLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_useGlowMapping");
674     polymost1NPOTEmulationLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_npotEmulation");
675     polymost1NPOTEmulationFactorLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_npotEmulationFactor");
676     polymost1NPOTEmulationXOffsetLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_npotEmulationXOffset");
677     polymost1BrightnessLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_brightness");
678     polymost1RotMatrixLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_rotMatrix");
679     polymost1ShadeInterpolateLoc = glGetUniformLocation(polymost1CurrentShaderProgramID, "u_shadeInterpolate");
680 
681     //set the uniforms to the current values
682     glUniform4f(polymost1TexturePosSizeLoc, polymost1TexturePosSize.x, polymost1TexturePosSize.y, polymost1TexturePosSize.z, polymost1TexturePosSize.w);
683     glUniform2f(polymost1HalfTexelSizeLoc, polymost1HalfTexelSize.x, polymost1HalfTexelSize.y);
684     glUniform2f(polymost1PalswapPosLoc, polymost1PalswapPos.x, polymost1PalswapPos.y);
685     glUniform2f(polymost1PalswapSizeLoc, polymost1PalswapInnerSize.x, polymost1PalswapInnerSize.y);
686     glUniform2f(polymost1ClampLoc, polymost1Clamp.x, polymost1Clamp.y);
687     glUniform1f(polymost1ShadeLoc, polymost1Shade);
688     glUniform1f(polymost1NumShadesLoc, polymost1NumShades);
689     glUniform1f(polymost1VisFactorLoc, polymost1VisFactor);
690     glUniform1f(polymost1FogEnabledLoc, polymost1FogEnabled);
691     glUniform1f(polymost1UseColorOnlyLoc, polymost1UseColorOnly);
692     glUniform1f(polymost1UsePaletteLoc, polymost1UsePalette);
693     glUniform1f(polymost1UseDetailMappingLoc, polymost1UseDetailMapping);
694     glUniform1f(polymost1UseGlowMappingLoc, polymost1UseGlowMapping);
695     glUniform1f(polymost1NPOTEmulationLoc, polymost1NPOTEmulation);
696     glUniform1f(polymost1NPOTEmulationFactorLoc, polymost1NPOTEmulationFactor);
697     glUniform1f(polymost1NPOTEmulationXOffsetLoc, polymost1NPOTEmulationXOffset);
698     glUniform1f(polymost1BrightnessLoc, polymost1Brightness);
699     glUniformMatrix4fv(polymost1RotMatrixLoc, 1, false, polymost1RotMatrix);
700     glUniform1f(polymost1ShadeInterpolateLoc, polymost1ShadeInterpolate);
701 }
702 
polymost_setTexturePosSize(vec4f_t const & texturePosSize)703 void polymost_setTexturePosSize(vec4f_t const &texturePosSize)
704 {
705     if (currentShaderProgramID != polymost1CurrentShaderProgramID)
706         return;
707 
708     polymost1TexturePosSize = texturePosSize;
709     glUniform4f(polymost1TexturePosSizeLoc, polymost1TexturePosSize.x, polymost1TexturePosSize.y, polymost1TexturePosSize.z, polymost1TexturePosSize.w);
710 }
711 
polymost_setHalfTexelSize(vec2f_t const & halfTexelSize)712 void polymost_setHalfTexelSize(vec2f_t const &halfTexelSize)
713 {
714     if (currentShaderProgramID != polymost1CurrentShaderProgramID || (halfTexelSize.x == polymost1HalfTexelSize.x && halfTexelSize.y == polymost1HalfTexelSize.y))
715         return;
716 
717     polymost1HalfTexelSize = halfTexelSize;
718     glUniform2f(polymost1HalfTexelSizeLoc, polymost1HalfTexelSize.x, polymost1HalfTexelSize.y);
719 }
720 
polymost_setPalswap(uint32_t index)721 static void polymost_setPalswap(uint32_t index)
722 {
723     static uint32_t lastPalswapIndex;
724 
725     if (currentShaderProgramID != polymost1CurrentShaderProgramID || index == lastPalswapIndex)
726         return;
727 
728     lastPalswapIndex = index;
729     polymost1PalswapPos.x = index*polymost1PalswapSize.x;
730     polymost1PalswapPos.y = floorf(polymost1PalswapPos.x);
731     polymost1PalswapPos = { polymost1PalswapPos.x - polymost1PalswapPos.y + (0.5f/PALSWAP_TEXTURE_SIZE),
732                             polymost1PalswapPos.y * polymost1PalswapSize.y + (0.5f/PALSWAP_TEXTURE_SIZE) };
733     glUniform2f(polymost1PalswapPosLoc, polymost1PalswapPos.x, polymost1PalswapPos.y);
734 }
735 
polymost_setPalswapSize(uint32_t width,uint32_t height)736 static void polymost_setPalswapSize(uint32_t width, uint32_t height)
737 {
738     if (currentShaderProgramID != polymost1CurrentShaderProgramID)
739         return;
740 
741     polymost1PalswapSize = { width*(1.f/PALSWAP_TEXTURE_SIZE),
742                              height*(1.f/PALSWAP_TEXTURE_SIZE) };
743 
744     polymost1PalswapInnerSize = { (width-1)*(1.f/PALSWAP_TEXTURE_SIZE),
745                                   (height-1)*(1.f/PALSWAP_TEXTURE_SIZE) };
746 
747     glUniform2f(polymost1PalswapSizeLoc, polymost1PalswapInnerSize.x, polymost1PalswapInnerSize.y);
748 }
749 
polymost_getClamp()750 char polymost_getClamp()
751 {
752     return polymost1Clamp.x + polymost1Clamp.y*2.0;
753 }
754 
polymost_setClamp(char clamp)755 void polymost_setClamp(char clamp)
756 {
757     char clampx = clamp&1;
758     char clampy = clamp>>1;
759     if (currentShaderProgramID != polymost1CurrentShaderProgramID ||
760         (clampx == polymost1Clamp.x && clampy == polymost1Clamp.y))
761         return;
762 
763     polymost1Clamp.x = clampx;
764     polymost1Clamp.y = clampy;
765     glUniform2f(polymost1ClampLoc, polymost1Clamp.x, polymost1Clamp.y);
766 }
767 
polymost_setShade(int32_t shade)768 static void polymost_setShade(int32_t shade)
769 {
770     if (currentShaderProgramID != polymost1CurrentShaderProgramID)
771         return;
772 
773     if (!polymost_usetileshades())
774         shade = 0;
775 
776     static int32_t lastShade;
777     static int32_t lastNumShades;
778 
779     if (shade != lastShade)
780     {
781         lastShade = shade;
782         polymost1Shade = shade;
783         glUniform1f(polymost1ShadeLoc, polymost1Shade);
784     }
785 
786     if (numshades != lastNumShades)
787     {
788         lastNumShades = numshades;
789         polymost1NumShades = numshades;
790         glUniform1f(polymost1NumShadesLoc, polymost1NumShades);
791     }
792 }
793 
polymost_setVisibility(float visibility)794 void polymost_setVisibility(float visibility)
795 {
796     if (currentShaderProgramID != polymost1CurrentShaderProgramID)
797         return;
798 
799     if (!polymost_usetileshades())
800         visibility = -16;
801 
802     float visFactor = visibility * fviewingrange * (1.f / (64.f * 65536.f));
803     if (visFactor == polymost1VisFactor)
804         return;
805 
806     polymost1VisFactor = visFactor;
807     glUniform1f(polymost1VisFactorLoc, polymost1VisFactor);
808 }
809 
polymost_setFogEnabled(char fogEnabled)810 void polymost_setFogEnabled(char fogEnabled)
811 {
812     if (currentShaderProgramID != polymost1CurrentShaderProgramID || fogEnabled == polymost1FogEnabled)
813         return;
814 
815     polymost1FogEnabled = fogEnabled;
816     glUniform1f(polymost1FogEnabledLoc, polymost1FogEnabled);
817 }
818 
polymost_useColorOnly(char useColorOnly)819 void polymost_useColorOnly(char useColorOnly)
820 {
821     if (currentShaderProgramID != polymost1CurrentShaderProgramID || useColorOnly == polymost1UseColorOnly)
822         return;
823 
824     polymost1UseColorOnly = useColorOnly;
825     glUniform1f(polymost1UseColorOnlyLoc, polymost1UseColorOnly);
826 }
827 
polymost_usePaletteIndexing(char usePaletteIndexing)828 void polymost_usePaletteIndexing(char usePaletteIndexing)
829 {
830     if (currentShaderProgramID != polymost1CurrentShaderProgramID || usePaletteIndexing == polymost1UsePalette)
831         return;
832 
833     polymost1UsePalette = usePaletteIndexing;
834     glUniform1f(polymost1UsePaletteLoc, polymost1UsePalette);
835 }
836 
polymost_useDetailMapping(char useDetailMapping)837 void polymost_useDetailMapping(char useDetailMapping)
838 {
839     if (currentShaderProgramID != polymost1CurrentShaderProgramID || useDetailMapping == polymost1UseDetailMapping)
840         return;
841 
842     if (useDetailMapping)
843         polymost_setCurrentShaderProgram(polymost1ExtendedShaderProgramID);
844 
845     polymost1UseDetailMapping = useDetailMapping;
846     glUniform1f(polymost1UseDetailMappingLoc, polymost1UseDetailMapping);
847 }
848 
polymost_useGlowMapping(char useGlowMapping)849 void polymost_useGlowMapping(char useGlowMapping)
850 {
851     if (currentShaderProgramID != polymost1CurrentShaderProgramID || useGlowMapping == polymost1UseGlowMapping)
852         return;
853 
854     if (useGlowMapping)
855         polymost_setCurrentShaderProgram(polymost1ExtendedShaderProgramID);
856 
857     polymost1UseGlowMapping = useGlowMapping;
858     glUniform1f(polymost1UseGlowMappingLoc, polymost1UseGlowMapping);
859 }
860 
polymost_npotEmulation(char npotEmulation,float factor,float xOffset)861 void polymost_npotEmulation(char npotEmulation, float factor, float xOffset)
862 {
863     if (currentShaderProgramID != polymost1CurrentShaderProgramID || npotEmulation == polymost1NPOTEmulation)
864         return;
865 
866     polymost1NPOTEmulation = npotEmulation;
867     glUniform1f(polymost1NPOTEmulationLoc, polymost1NPOTEmulation);
868     polymost1NPOTEmulationFactor = factor;
869     glUniform1f(polymost1NPOTEmulationFactorLoc, polymost1NPOTEmulationFactor);
870     polymost1NPOTEmulationXOffset = xOffset;
871     glUniform1f(polymost1NPOTEmulationXOffsetLoc, polymost1NPOTEmulationXOffset);
872 }
873 
polymost_shadeInterpolate(int32_t shadeInterpolate)874 void polymost_shadeInterpolate(int32_t shadeInterpolate)
875 {
876     if (currentShaderProgramID == polymost1CurrentShaderProgramID)
877     {
878         polymost1ShadeInterpolate = shadeInterpolate;
879         glUniform1f(polymost1ShadeInterpolateLoc, polymost1ShadeInterpolate);
880     }
881 }
882 
polymost_setBrightness(int brightness)883 void polymost_setBrightness(int brightness)
884 {
885     if (currentShaderProgramID == polymost1CurrentShaderProgramID)
886     {
887         polymost1Brightness = 8.f / (brightness + 8.f);
888         glUniform1f(polymost1BrightnessLoc, polymost1Brightness);
889     }
890 }
891 
polymost_activeTexture(GLenum texture)892 void polymost_activeTexture(GLenum texture)
893 {
894     currentActiveTexture = texture;
895     glad_glActiveTexture(texture);
896 }
897 
898 //POGOTODO: replace this and polymost_activeTexture with proper draw call organization
polymost_bindTexture(GLenum target,uint32_t textureID)899 void polymost_bindTexture(GLenum target, uint32_t textureID)
900 {
901     if (currentTextureID != textureID ||
902         textureID == 0 ||
903         currentActiveTexture != GL_TEXTURE0 ||
904         videoGetRenderMode() != REND_POLYMOST)
905     {
906         glad_glBindTexture(target, textureID);
907         if (currentActiveTexture == GL_TEXTURE0)
908         {
909             currentTextureID = textureID;
910         }
911     }
912 }
913 
polymost_bindPth(pthtyp const * const pPth)914 static void polymost_bindPth(pthtyp const * const pPth)
915 {
916     Bassert(pPth);
917 
918     vec4f_t texturePosSize = { 0.f, 0.f, 1.f, 1.f };
919     vec2f_t halfTexelSize = { 0.f, 0.f };
920     if ((pPth->flags & PTH_INDEXED) &&
921         !(pPth->flags & PTH_HIGHTILE))
922     {
923         Tile tile;
924         char tileIsPacked = tilepacker_getTile(waloff[pPth->picnum] ? pPth->picnum+1 : 0, &tile);
925         //POGO: check the width and height to ensure that the tile hasn't been changed for a user tile that has different dimensions
926         if (tileIsPacked &&
927             (!waloff[pPth->picnum] ||
928              (tile.rect.width >= (uint32_t) tilesiz[pPth->picnum].y &&
929               tile.rect.height >= (uint32_t) tilesiz[pPth->picnum].x)))
930         {
931             texturePosSize = { tile.rect.u/(float) tilesheetSize,
932                                tile.rect.v/(float) tilesheetSize,
933                                tilesiz[pPth->picnum].y/(float) tilesheetSize,
934                                tilesiz[pPth->picnum].x/(float) tilesheetSize };
935             halfTexelSize = tilesheetHalfTexelSize;
936         }
937     }
938     polymost_setTexturePosSize(texturePosSize);
939     polymost_setHalfTexelSize(halfTexelSize);
940     glBindTexture(GL_TEXTURE_2D, pPth->glpic);
941 }
942 
polymost_useShaderProgram(uint32_t shaderID)943 void polymost_useShaderProgram(uint32_t shaderID)
944 {
945     glUseProgram(shaderID);
946     currentShaderProgramID = shaderID;
947 }
948 
949 // one-time initialization of OpenGL for polymost
polymost_glinit()950 void polymost_glinit()
951 {
952     glHint(GL_FOG_HINT, GL_NICEST);
953     glFogi(GL_FOG_MODE, (r_usenewshading < 2) ? GL_EXP2 : GL_LINEAR);
954     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
955 
956     glPixelStorei(GL_PACK_ALIGNMENT, 1);
957     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
958 
959     if (glinfo.depthclamp)
960         glEnable(GL_DEPTH_CLAMP);
961 
962     //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
963     //glEnable(GL_LINE_SMOOTH);
964 
965 #ifdef USE_GLEXT
966     if (r_persistentStreamBuffer && ((!glinfo.bufferstorage) || (!glinfo.sync)))
967     {
968         OSD_Printf("Your OpenGL implementation doesn't support the required extensions for persistent stream buffers. Disabling...\n");
969         r_persistentStreamBuffer = 0;
970     }
971 #endif
972 
973     //POGOTODO: require a max texture size >= 2048
974 
975     persistentStreamBuffer = r_persistentStreamBuffer;
976     drawpolyVertsBufferLength = r_drawpolyVertsBufferLength;
977 
978     drawpolyVertsOffset = 0;
979     drawpolyVertsSubBufferIndex = 0;
980 
981     GLuint ids[2];
982     glGenBuffers(2, ids);
983     drawpolyVertsID = ids[0];
984     glBindBuffer(GL_ARRAY_BUFFER, drawpolyVertsID);
985     if (persistentStreamBuffer)
986     {
987         // reset the sync objects, as old ones we had from any last GL context are gone now
988         Bmemset(drawpolyVertsSync, 0, sizeof(drawpolyVertsSync));
989 
990         GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
991         // we want to triple-buffer to avoid having to wait for the buffer to become available again,
992         // so triple the buffer size we expect to use
993         glBufferStorage(GL_ARRAY_BUFFER, 3*drawpolyVertsBufferLength*sizeof(float)*5, NULL, flags);
994         drawpolyVerts = (float*) glMapBufferRange(GL_ARRAY_BUFFER, 0, 3*drawpolyVertsBufferLength*sizeof(float)*5, flags);
995     }
996     else
997     {
998         drawpolyVerts = defaultDrawpolyVertsArray;
999         glBufferData(GL_ARRAY_BUFFER, drawpolyVertsBufferLength*sizeof(float)*5, NULL, GL_STREAM_DRAW);
1000     }
1001     glBindBuffer(GL_ARRAY_BUFFER, 0);
1002 
1003     currentTextureID = 0;
1004 
1005     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &tilesheetSize);
1006 #if (defined _MSC_VER) || (!defined BITNESS64)
1007     if (tilesheetSize > 8192)
1008         tilesheetSize = 8192;
1009 #endif
1010     tilesheetHalfTexelSize = { 0.5f/tilesheetSize, 0.5f/tilesheetSize };
1011     vec2_t maxTexDimensions = { tilesheetSize, tilesheetSize };
1012     char allPacked = false;
1013     static int numTilesheets = 0;
1014     //POGO: only pack the tilesheets once
1015     if (numTilesheets == 0)
1016     {
1017         // add a blank texture for tileUID 0
1018         tilepacker_addTile(0, 2, 2);
1019         for (int picnum = 0; picnum < MAXTILES; ++picnum)
1020         {
1021             tilepacker_addTile(picnum+1, (uint32_t) tilesiz[picnum].y, (uint32_t) tilesiz[picnum].x);
1022         }
1023 
1024         do
1025         {
1026             tilepacker_initTilesheet(numTilesheets, tilesheetSize, tilesheetSize);
1027             allPacked = tilepacker_pack(numTilesheets);
1028             ++numTilesheets;
1029         } while (!allPacked && numTilesheets < MAXTILESHEETS);
1030     }
1031     for (int i = 0; i < numTilesheets; ++i)
1032     {
1033         glGenTextures(1, tilesheetTexIDs+i);
1034         glBindTexture(GL_TEXTURE_2D, tilesheetTexIDs[i]);
1035         uploadtextureindexed(true, {0, 0}, maxTexDimensions, (intptr_t) NULL);
1036     }
1037 
1038     const char blankTex[] = {255, 255,
1039                              255, 255};
1040     Tile blankTile;
1041     tilepacker_getTile(0, &blankTile);
1042     glBindTexture(GL_TEXTURE_2D, tilesheetTexIDs[blankTile.tilesheetID]);
1043     uploadtextureindexed(false, {(int32_t) blankTile.rect.u, (int32_t) blankTile.rect.v}, {2, 2}, (intptr_t) blankTex);
1044 
1045     quadVertsID = ids[1];
1046     glBindBuffer(GL_ARRAY_BUFFER, quadVertsID);
1047     const float quadVerts[] =
1048         {
1049             -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, //top-left
1050             -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, //bottom-left
1051              0.5f, 1.0f, 0.0f, 1.0f, 1.0f, //top-right
1052              0.5f, 0.0f, 0.0f, 1.0f, 0.0f  //bottom-right
1053         };
1054     glBufferData(GL_ARRAY_BUFFER, sizeof(quadVerts), quadVerts, GL_STATIC_DRAW);
1055 
1056     //specify format/arrangement for vertex positions:
1057     glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(float) * 5, 0);
1058     //specify format/arrangement for vertex texture coords:
1059     glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(float) * 5, (const void*) (sizeof(float) * 3));
1060 
1061     glBindBuffer(GL_ARRAY_BUFFER, 0);
1062 
1063     const char* const POLYMOST2_BASIC_VERTEX_SHADER_CODE =
1064         "#version 110\n\
1065          \n\
1066          // input\n\
1067          attribute vec3 i_vertPos;\n\
1068          attribute vec2 i_texCoord;\n\
1069          uniform mat4 u_mvMatrix;\n\
1070          uniform mat4 u_projMatrix;\n\
1071          uniform vec2 u_texOffset;\n\
1072          uniform vec2 u_texScale;\n\
1073          \n\
1074          // output\n\
1075          varying vec2 v_texCoord;\n\
1076          varying float v_distance;\n\
1077          \n\
1078          void main()\n\
1079          {\n\
1080             vec4 eyeCoordPosition = u_mvMatrix * vec4(i_vertPos, 1.0);\n\
1081             gl_Position = u_projMatrix * eyeCoordPosition;\n\
1082             \n\
1083             eyeCoordPosition.xyz /= eyeCoordPosition.w;\n\
1084             \n\
1085             v_texCoord = i_texCoord * u_texScale + u_texOffset;\n\
1086             v_distance = eyeCoordPosition.z;\n\
1087          }\n";
1088     const char* const POLYMOST2_BASIC_FRAGMENT_SHADER_CODE =
1089         "#version 110\n\
1090          \n\
1091          varying vec2 v_texCoord;\n\
1092          uniform sampler2D s_texture;\n\
1093          uniform sampler2D s_fullBright;\n\
1094          \n\
1095          uniform vec4 u_tint;\n\
1096          uniform float u_alpha;\n\
1097          \n\
1098          varying float v_distance;\n\
1099          uniform vec2 u_fogRange;\n\
1100          uniform vec4 u_fogColor;\n\
1101          \n\
1102          const float c_zero = 0.0;\n\
1103          const float c_one  = 1.0;\n\
1104          \n\
1105          void main()\n\
1106          {\n\
1107              vec4 color = texture2D(s_texture, v_texCoord);\n\
1108              vec4 fullBrightColor = texture2D(s_fullBright, v_texCoord);\n\
1109              \n\
1110              float fogFactor = clamp((u_fogRange.y-v_distance)/(u_fogRange.y-u_fogRange.x), c_zero, c_one);\n\
1111              \n\
1112              color.rgb = mix(u_fogColor.rgb, color.rgb, fogFactor);\n\
1113              color.rgb *= u_tint.rgb * u_tint.a * color.a;\n\
1114              color.rgb = mix(color.rgb, fullBrightColor.rgb, fullBrightColor.a);\n\
1115              \n\
1116              color.a *= u_alpha;\n\
1117              \n\
1118              gl_FragColor = color;\n\
1119          }\n";
1120 
1121     polymost2BasicShaderProgramID = glCreateProgram();
1122     GLuint polymost2BasicVertexShaderID = polymost2_compileShader(GL_VERTEX_SHADER, POLYMOST2_BASIC_VERTEX_SHADER_CODE);
1123     GLuint polymost2BasicFragmentShaderID = polymost2_compileShader(GL_FRAGMENT_SHADER, POLYMOST2_BASIC_FRAGMENT_SHADER_CODE);
1124     glBindAttribLocation(polymost2BasicShaderProgramID, 0, "i_vertPos");
1125     glBindAttribLocation(polymost2BasicShaderProgramID, 1, "i_texCoord");
1126     glAttachShader(polymost2BasicShaderProgramID, polymost2BasicVertexShaderID);
1127     glAttachShader(polymost2BasicShaderProgramID, polymost2BasicFragmentShaderID);
1128     glLinkProgram(polymost2BasicShaderProgramID);
1129 
1130     // Get the attribute/uniform locations
1131     texSamplerLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "s_texture");
1132     fullBrightSamplerLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "s_fullBright");
1133     projMatrixLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "u_projMatrix");
1134     mvMatrixLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "u_mvMatrix");
1135     texOffsetLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "u_texOffset");
1136     texScaleLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "u_texScale");
1137     tintLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "u_tint");
1138     alphaLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "u_alpha");
1139     fogRangeLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "u_fogRange");
1140     fogColorLoc = glGetUniformLocation(polymost2BasicShaderProgramID, "u_fogColor");
1141 
1142     polymost1ExtendedShaderProgramID = glCreateProgram();
1143     GLuint polymost1BasicVertexShaderID = polymost2_compileShader(GL_VERTEX_SHADER, polymost1Vert);
1144     GLuint polymost1ExtendedFragmentShaderID = polymost2_compileShader(GL_FRAGMENT_SHADER, polymost1Frag);
1145     glAttachShader(polymost1ExtendedShaderProgramID, polymost1BasicVertexShaderID);
1146     glAttachShader(polymost1ExtendedShaderProgramID, polymost1ExtendedFragmentShaderID);
1147     glLinkProgram(polymost1ExtendedShaderProgramID);
1148 
1149     int polymost1BasicFragLen = strlen(polymost1Frag);
1150     char* polymost1BasicFrag = (char*) Bmalloc(polymost1BasicFragLen);
1151     memcpy(polymost1BasicFrag, polymost1Frag, polymost1BasicFragLen);
1152     char* extDefineSubstr = strstr(polymost1BasicFrag, " #define POLYMOST1_EXTENDED");
1153     if (extDefineSubstr)
1154     {
1155         //Disable extensions for basic fragment shader
1156         extDefineSubstr[0] = '/';
1157         extDefineSubstr[1] = '/';
1158     }
1159     polymost1BasicShaderProgramID = glCreateProgram();
1160     GLuint polymost1BasicFragmentShaderID = polymost2_compileShader(GL_FRAGMENT_SHADER, polymost1BasicFrag, polymost1BasicFragLen);
1161     glAttachShader(polymost1BasicShaderProgramID, polymost1BasicVertexShaderID);
1162     glAttachShader(polymost1BasicShaderProgramID, polymost1BasicFragmentShaderID);
1163     glLinkProgram(polymost1BasicShaderProgramID);
1164     Bfree(polymost1BasicFrag);
1165     polymost1BasicFrag = 0;
1166 
1167     // set defaults
1168     polymost_setCurrentShaderProgram(polymost1ExtendedShaderProgramID);
1169     glUniform1i(polymost1TexSamplerLoc, 0);
1170     glUniform1i(polymost1PalSwapSamplerLoc, 1);
1171     glUniform1i(polymost1PaletteSamplerLoc, 2);
1172     glUniform1i(polymost1DetailSamplerLoc, 3);
1173     glUniform1i(polymost1GlowSamplerLoc, 4);
1174     polymost_setPalswapSize(256, numshades+1);
1175     polymost_setCurrentShaderProgram(polymost1BasicShaderProgramID);
1176     glUniform1i(polymost1TexSamplerLoc, 0);
1177     glUniform1i(polymost1PalSwapSamplerLoc, 1);
1178     glUniform1i(polymost1PaletteSamplerLoc, 2);
1179     polymost_useShaderProgram(0);
1180 
1181     lastbasepal = -1;
1182     for (int basepalnum = 0; basepalnum < MAXBASEPALS; ++basepalnum)
1183     {
1184         paletteTextureIDs[basepalnum] = 0;
1185         uploadbasepalette(basepalnum);
1186     }
1187     palswapTextureID = 0;
1188     for (int palookupnum = 0; palookupnum < MAXPALOOKUPS; ++palookupnum)
1189     {
1190         uploadpalswap(palookupnum);
1191     }
1192 
1193     glEnableClientState(GL_VERTEX_ARRAY);
1194     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1195 
1196     polymost_resetVertexPointers();
1197 
1198     texcache_init();
1199     texcache_loadoffsets();
1200     texcache_openfiles();
1201 
1202     texcache_setupmemcache();
1203     texcache_checkgarbage();
1204 
1205 #if defined EDUKE32_GLES
1206     Polymost_DetermineTextureFormatSupport();
1207 #endif
1208 }
1209 
polymost_init()1210 void polymost_init()
1211 {
1212     lastbasepal = -1;
1213     polymost_resetVertexPointers();
1214 }
1215 
1216 ////////// VISIBILITY FOG ROUTINES //////////
1217 
1218 // only for r_usenewshading < 2 (not preferred)
fogcalc_old(int32_t shade,int32_t vis)1219 static void fogcalc_old(int32_t shade, int32_t vis)
1220 {
1221     float f;
1222 
1223     if (r_usenewshading == 1)
1224     {
1225         f = 0.9f * shade;
1226         f = (vis > 239) ? (float)(gvisibility * (vis - 240 + f)) :
1227                           (float)(gvisibility * (vis + 16 + f));
1228     }
1229     else
1230     {
1231         f = (shade < 0) ? shade * 3.5f : shade * .66f;
1232         f = (vis > 239) ? (float)(gvisibility * ((vis - 240 + f) / (klabs(vis - 256)))) :
1233                           (float)(gvisibility * (vis + 16 + f));
1234     }
1235 
1236     fogresult = clamp(f, 0.001f, 100.0f);
1237 }
1238 
1239 // For GL_LINEAR fog:
1240 #define FOGDISTCONST 600
1241 #define FULLVIS_BEGIN 2.9e30f
1242 #define FULLVIS_END 3.0e30f
1243 
fogcalc(int32_t shade,int32_t vis,int32_t pal)1244 static inline void fogcalc(int32_t shade, int32_t vis, int32_t pal)
1245 {
1246     fogcol = fogtable[pal];
1247 
1248     if (r_usenewshading < 2)
1249     {
1250         if (fogfactor[pal] == 0.f)
1251             fogresult = 0.001f;
1252         else
1253             fogcalc_old(shade, vis);
1254         return;
1255     }
1256 
1257     float combvis = (float) globalvisibility * (uint8_t) (vis+16);
1258 
1259     if (fogfactor[pal] == 0.f)
1260     {
1261         fogresult = FULLVIS_BEGIN;
1262         fogresult2 = FULLVIS_END;
1263     }
1264     else if (combvis == 0.f)
1265     {
1266         if (r_usenewshading == 2 && shade > 0)
1267         {
1268             // beg = -D*shade, end = D*(NUMSHADES-1-shade)
1269             //  => end/beg = -(NUMSHADES-1-shade)/shade
1270             fogresult = -FULLVIS_BEGIN;
1271             fogresult2 = FULLVIS_BEGIN * (float)(numshades-1-shade) / shade;
1272         }
1273         else
1274         {
1275             fogresult  = FULLVIS_BEGIN;
1276             fogresult2 = FULLVIS_END;
1277         }
1278     }
1279     else if (r_usenewshading == 3 && shade >= numshades-1)
1280     {
1281         fogresult = -1;
1282         fogresult2 = 0;
1283     }
1284     else
1285     {
1286         combvis = 1.f/combvis;
1287         fogresult = (r_usenewshading == 3 && shade > 0) ? 0.f : -(FOGDISTCONST * shade) * combvis;
1288         fogresult2 = (FOGDISTCONST * (numshades-1-shade)) * combvis;
1289     }
1290 }
1291 
1292 #define GL_FOG_MAX 1.0e37f
1293 
polymost2_calc_fog(int32_t shade,int32_t vis,int32_t pal)1294 void polymost2_calc_fog(int32_t shade, int32_t vis, int32_t pal)
1295 {
1296     if (nofog) return;
1297 
1298     fogcol = fogtable[pal];
1299 
1300     if (((uint8_t)(vis + 16)) > 0 && g_visibility > 0)
1301     {
1302         constexpr GLfloat glfogconstant = 262144.f;
1303         GLfloat fogrange = (frealmaxshade * glfogconstant) / (((uint8_t)(vis + 16)) * globalvisibility);
1304 
1305         fogresult = 0.f - (((min(shade, 0) - 0.5f) / frealmaxshade) * fogrange); // min() = subtract shades from fog
1306         fogresult2 = fogrange - (((shade - 0.5f) / frealmaxshade) * fogrange);
1307     }
1308     else
1309     {
1310         fogresult = 0.f;
1311         fogresult2 = -GL_FOG_MAX; // hide fog behind the camera
1312     }
1313 }
1314 
calc_and_apply_fog(int32_t shade,int32_t vis,int32_t pal)1315 void calc_and_apply_fog(int32_t shade, int32_t vis, int32_t pal)
1316 {
1317     if (nofog) return;
1318 
1319     if (r_usenewshading == 4)
1320     {
1321         fogresult = 0.f;
1322         fogcol = fogtable[pal];
1323 
1324         if (((uint8_t)(vis + 16)) > 0 && globalvisibility > 0 && fogfactor[pal] > 0.f)
1325         {
1326             constexpr GLfloat glfogconstant = 262144.f;
1327             GLfloat fogrange = (frealmaxshade * glfogconstant) / (((uint8_t)(vis + 16)) * globalvisibility);
1328 
1329             fogrange *= fogfactor[pal];
1330 
1331             fogresult = 0.f - (((min(shade, 0) - 0.5f) / frealmaxshade) * fogrange); // min() = subtract shades from fog
1332             fogresult2 = fogrange - (((shade - 0.5f) / frealmaxshade) * fogrange);
1333         }
1334         else
1335         {
1336             fogresult = 0.f;
1337             fogresult2 = -GL_FOG_MAX; // hide fog behind the camera
1338         }
1339 
1340         glFogf(GL_FOG_START, fogresult);
1341         glFogf(GL_FOG_END, fogresult2);
1342         glFogfv(GL_FOG_COLOR, (GLfloat *)&fogcol);
1343 
1344         return;
1345     }
1346 
1347     fogcalc(shade, vis, pal);
1348     glFogfv(GL_FOG_COLOR, (GLfloat *)&fogcol);
1349 
1350     if (r_usenewshading < 2)
1351         glFogf(GL_FOG_DENSITY, fogresult);
1352     else
1353     {
1354         glFogf(GL_FOG_START, fogresult);
1355         glFogf(GL_FOG_END, fogresult2);
1356     }
1357 }
1358 
calc_and_apply_fog_factor(int32_t shade,int32_t vis,int32_t pal,float factor)1359 void calc_and_apply_fog_factor(int32_t shade, int32_t vis, int32_t pal, float factor)
1360 {
1361     if (nofog) return;
1362 
1363     if (r_usenewshading == 4)
1364     {
1365         fogcol = fogtable[pal];
1366 
1367         if (((uint8_t)(vis + 16)) > 0 && ((((uint8_t)(vis + 16)) / 8.f) + shade) > 0)
1368         {
1369             GLfloat normalizedshade = (shade - 0.5f) / frealmaxshade;
1370             GLfloat fogrange = (((uint8_t)(vis + 16)) / (8.f * frealmaxshade)) + normalizedshade;
1371 
1372             fogrange *= fogfactor[pal];
1373 
1374             // subtract shades from fog
1375             if (normalizedshade > 0.f && normalizedshade < 1.f)
1376                 fogrange = (fogrange - normalizedshade) / (1.f - normalizedshade);
1377 
1378             fogresult = -(GL_FOG_MAX * fogrange);
1379             fogresult2 = GL_FOG_MAX - (GL_FOG_MAX * fogrange);
1380         }
1381         else
1382         {
1383             fogresult = 0.f;
1384             fogresult2 = -GL_FOG_MAX; // hide fog behind the camera
1385         }
1386 
1387         glFogf(GL_FOG_START, fogresult);
1388         glFogf(GL_FOG_END, fogresult2);
1389         glFogfv(GL_FOG_COLOR, (GLfloat *)&fogcol);
1390 
1391         return;
1392     }
1393 
1394     // NOTE: for r_usenewshading >= 2, the fog beginning/ending distance results are
1395     // unused.
1396     fogcalc(shade, vis, pal);
1397     fogresult *= fogfactor[pal];
1398     fogresult2 *= fogfactor[pal];
1399 
1400     glFogfv(GL_FOG_COLOR, (GLfloat *)&fogcol);
1401 
1402     if (r_usenewshading < 2)
1403         glFogf(GL_FOG_DENSITY, fogresult*factor);
1404     else
1405     {
1406         glFogf(GL_FOG_START, (GLfloat) FULLVIS_BEGIN);
1407         glFogf(GL_FOG_END, (GLfloat) FULLVIS_END);
1408     }
1409 }
1410 ////////////////////
1411 
1412 
get_projhack_ratio(void)1413 static float get_projhack_ratio(void)
1414 {
1415     if ((glprojectionhacks == 1) && !r_yshearing)
1416     {
1417         // calculates the extend of the zenith glitch
1418         float verticalfovtan = (fviewingrange * (windowxy2.y-windowxy1.y) * 5.f) / ((float)yxaspect * (windowxy2.x-windowxy1.x) * 4.f);
1419         float verticalfov = atanf(verticalfovtan) * (2.f / fPI);
1420         static constexpr float const maxhorizangle = 0.6361136f; // horiz of 199 in degrees
1421         float zenglitch = verticalfov + maxhorizangle - 0.95f; // less than 1 because the zenith glitch extends a bit
1422         if (zenglitch <= 0.f)
1423             return 1.f;
1424         float const zenglitchtan = tanf((verticalfov - zenglitch) * (fPI / 2.f));
1425         static constexpr float const maxcoshoriz = 0.54097117f; // 128/sqrt(128^2+199^2) = cos of an horiz diff of 199
1426         return 1.f + (((verticalfovtan / zenglitchtan) - 1.f) * ((1.f - Bfabsf(gchang)) / maxcoshoriz ));
1427     }
1428 
1429     // No projection hacks (legacy or new-aspect)
1430     return 1.f;
1431 }
1432 
resizeglcheck(void)1433 static void resizeglcheck(void)
1434 {
1435 #ifndef EDUKE32_GLES
1436     //FUK
1437     if (lastglpolygonmode != r_polygonmode)
1438     {
1439         lastglpolygonmode = r_polygonmode;
1440         switch (r_polygonmode)
1441         {
1442         default:
1443         case 0:
1444             glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); break;
1445         case 1:
1446             glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); break;
1447         case 2:
1448             glPolygonMode(GL_FRONT_AND_BACK,GL_POINT); break;
1449         }
1450     }
1451     if (r_polygonmode) //FUK
1452     {
1453         glClearColor(1.0,1.0,1.0,0.0);
1454         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
1455     }
1456 #else
1457     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
1458 #endif
1459 
1460     const int32_t ourxdimen = (windowxy2.x-windowxy1.x+1);
1461     float ratio = get_projhack_ratio();
1462     const int32_t fovcorrect = (int32_t)(ourxdimen*ratio - ourxdimen);
1463 
1464     ratio = 1.f/ratio;
1465 
1466     polymost2d = 0;
1467 
1468     glViewport(windowxy1.x-(fovcorrect/2), ydim-(windowxy2.y+1),
1469                 ourxdimen+fovcorrect, windowxy2.y-windowxy1.y+1);
1470 
1471     glMatrixMode(GL_PROJECTION);
1472 
1473     float m[4][4];
1474     Bmemset(m,0,sizeof(m));
1475 
1476     float const nearclip = 4.f / (gxyaspect * gyxscale * 1024.f);
1477     float const farclip = 64.f;
1478 
1479     m[0][0] = 1.f;
1480     m[1][1] = fxdimen / (fydimen * ratio);
1481     m[2][0] = 2.f * ghoriz2 * gstang / fxdimen;
1482     m[2][1] = 2.f * (ghoriz2 * gctang + ghorizcorrect) / fydimen;
1483     m[2][2] = (farclip + nearclip) / (farclip - nearclip);
1484     m[2][3] = 1.f;
1485     m[3][2] = -(2.f * farclip * nearclip) / (farclip - nearclip);
1486     glLoadMatrixf(&m[0][0]);
1487 
1488     glMatrixMode(GL_MODELVIEW);
1489     glLoadIdentity();
1490 
1491     if (!nofog) polymost_setFogEnabled(true);
1492 }
1493 
fixtransparency(coltype * dapic,vec2_t dasiz,vec2_t dasiz2,int32_t dameth)1494 static void fixtransparency(coltype *dapic, vec2_t dasiz, vec2_t dasiz2, int32_t dameth)
1495 {
1496     if (!(dameth & DAMETH_MASKPROPS))
1497         return;
1498 
1499     vec2_t doxy = { dasiz2.x-1, dasiz2.y-1 };
1500 
1501     if (dameth & DAMETH_CLAMPED)
1502         doxy = { min(doxy.x, dasiz.x), min(doxy.y, dasiz.y) };
1503     else  dasiz = dasiz2; //Make repeating textures duplicate top/left parts
1504 
1505     dasiz.x--; dasiz.y--; //Hacks for optimization inside loop
1506     int32_t const naxsiz2 = -dasiz2.x;
1507 
1508     //Set transparent pixels to average color of neighboring opaque pixels
1509     //Doing this makes bilinear filtering look much better for masked textures (I.E. sprites)
1510     for (bssize_t y=doxy.y; y>=0; y--)
1511     {
1512         coltype * wpptr = &dapic[y*dasiz2.x+doxy.x];
1513 
1514         for (bssize_t x=doxy.x; x>=0; x--,wpptr--)
1515         {
1516             if (wpptr->a) continue;
1517 
1518             int r = 0, g = 0, b = 0, j = 0;
1519 
1520             if ((x>     0) && (wpptr[     -1].a)) { r += wpptr[     -1].r; g += wpptr[     -1].g; b += wpptr[     -1].b; j++; }
1521             if ((x<dasiz.x) && (wpptr[     +1].a)) { r += wpptr[     +1].r; g += wpptr[     +1].g; b += wpptr[     +1].b; j++; }
1522             if ((y>     0) && (wpptr[naxsiz2].a)) { r += wpptr[naxsiz2].r; g += wpptr[naxsiz2].g; b += wpptr[naxsiz2].b; j++; }
1523             if ((y<dasiz.y) && (wpptr[dasiz2.x].a)) { r += wpptr[dasiz2.x].r; g += wpptr[dasiz2.x].g; b += wpptr[dasiz2.x].b; j++; }
1524 
1525             switch (j)
1526             {
1527             case 1:
1528                 wpptr->r =   r            ; wpptr->g =   g            ; wpptr->b =   b            ; break;
1529             case 2:
1530                 wpptr->r = ((r   +  1)>>1); wpptr->g = ((g   +  1)>>1); wpptr->b = ((b   +  1)>>1); break;
1531             case 3:
1532                 wpptr->r = ((r*85+128)>>8); wpptr->g = ((g*85+128)>>8); wpptr->b = ((b*85+128)>>8); break;
1533             case 4:
1534                 wpptr->r = ((r   +  2)>>2); wpptr->g = ((g   +  2)>>2); wpptr->b = ((b   +  2)>>2); break;
1535             }
1536         }
1537     }
1538 }
1539 
1540 #if defined EDUKE32_GLES
1541 // sorted first in increasing order of size, then in decreasing order of quality
1542 static int32_t const texfmts_rgb_mask[] = { GL_RGB5_A1, GL_RGBA, 0 };
1543 static int32_t const texfmts_rgb[] = { GL_RGB565, GL_RGB5_A1, GL_RGB, GL_RGBA, 0 };
1544 static int32_t const texfmts_rgba[] = { GL_RGBA4, GL_RGBA, 0 } ;
1545 
1546 static int32_t texfmt_rgb_mask;
1547 static int32_t texfmt_rgb;
1548 static int32_t texfmt_rgba;
1549 
1550 #if defined EDUKE32_IOS
1551 static int32_t const comprtexfmts_rgb[] = { GL_ETC1_RGB8_OES, 0 };
1552 static int32_t const comprtexfmts_rgba[] = { 0 };
1553 static int32_t const comprtexfmts_rgb_mask[] = { 0 };
1554 #else
1555 static int32_t const comprtexfmts_rgb[] =
1556 {
1557 #ifdef GL_COMPRESSED_RGB8_ETC2
1558     GL_COMPRESSED_RGB8_ETC2,
1559 #endif
1560 #ifdef GL_ETC1_RGB8_OES
1561     GL_ETC1_RGB8_OES,
1562 #endif
1563     0
1564     };
1565 // TODO: waiting on etcpak support for ETC2 with alpha
1566 static int32_t const comprtexfmts_rgba[] = { /*GL_COMPRESSED_RGBA8_ETC2_EAC,*/ 0 };
1567 static int32_t const comprtexfmts_rgb_mask[] = { /*GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,*/ 0 };
1568 #endif
1569 
1570 static int32_t comprtexfmt_rgb_mask;
1571 static int32_t comprtexfmt_rgb;
1572 static int32_t comprtexfmt_rgba;
1573 
1574 # ifdef __cplusplus
1575 extern "C" {
1576 # endif
1577 extern uint64_t ProcessRGB(uint8_t const *);
1578 extern uint64_t ProcessRGB_ETC2(uint8_t const *);
1579 # ifdef __cplusplus
1580 }
1581 # endif
1582 
1583 typedef uint64_t (*ETCFunction_t)(uint8_t const *);
1584 
Polymost_PickETCFunction(int32_t const comprtexfmt)1585 static ETCFunction_t Polymost_PickETCFunction(int32_t const comprtexfmt)
1586 {
1587     switch (comprtexfmt)
1588     {
1589 # ifdef GL_ETC1_RGB8_OES
1590         case GL_ETC1_RGB8_OES:
1591             return ProcessRGB;
1592 # endif
1593 
1594 # ifdef GL_COMPRESSED_RGB8_ETC2
1595         case GL_COMPRESSED_RGB8_ETC2:
1596             return ProcessRGB_ETC2;
1597 # endif
1598 
1599 # if 0
1600         case GL_COMPRESSED_RGBA8_ETC2_EAC:
1601             fallthrough__;
1602         case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
1603             fallthrough__;
1604 # endif
1605 
1606         default:
1607             EDUKE32_UNREACHABLE_SECTION(return NULL);
1608     }
1609 }
1610 
Polymost_ConfirmNoGLError(void)1611 static int Polymost_ConfirmNoGLError(void)
1612 {
1613     GLenum checkerr, err = GL_NO_ERROR;
1614     while ((checkerr = glGetError()) != GL_NO_ERROR)
1615         err = checkerr;
1616 
1617     return err == GL_NO_ERROR;
1618 }
1619 
Polymost_TryDummyTexture(coltype const * const pic,int32_t const * formats)1620 static int32_t Polymost_TryDummyTexture(coltype const * const pic, int32_t const * formats)
1621 {
1622     while (*formats)
1623     {
1624         glTexImage2D(GL_TEXTURE_2D, 0, *formats, 4,4, 0, GL_RGBA, GL_UNSIGNED_BYTE, pic);
1625 
1626         if (Polymost_ConfirmNoGLError())
1627             return *formats;
1628 
1629         ++formats;
1630     }
1631 
1632     initputs("No texture formats supported?!\n");
1633 
1634     return 0;
1635 }
1636 
Polymost_TryCompressedDummyTexture(coltype const * const pic,int32_t const * formats)1637 static int32_t Polymost_TryCompressedDummyTexture(coltype const * const pic, int32_t const * formats)
1638 {
1639     while (*formats)
1640     {
1641         ETCFunction_t func = Polymost_PickETCFunction(*formats);
1642         uint64_t const comprpic = func((uint8_t const *)pic);
1643         jwzgles_glCompressedTexImage2D(GL_TEXTURE_2D, 0, *formats, 4,4, 0, sizeof(uint64_t), &comprpic);
1644 
1645         if (Polymost_ConfirmNoGLError())
1646             return *formats;
1647 
1648         ++formats;
1649     }
1650 
1651     return 0;
1652 }
1653 
Polymost_DetermineTextureFormatSupport(void)1654 static void Polymost_DetermineTextureFormatSupport(void)
1655 {
1656     // init dummy texture to trigger possible failure of all compression modes
1657     coltype pic[4*4] = { { 0, 0, 0, 0 } };
1658     GLuint tex = 0;
1659 
1660     glGenTextures(1, &tex);
1661     glBindTexture(GL_TEXTURE_2D, tex);
1662 
1663     BuildGLErrorCheck(); // XXX: Clear errors.
1664 
1665     texfmt_rgb = Polymost_TryDummyTexture(pic, texfmts_rgb);
1666     texfmt_rgba = Polymost_TryDummyTexture(pic, texfmts_rgba);
1667     texfmt_rgb_mask = Polymost_TryDummyTexture(pic, texfmts_rgb_mask);
1668 
1669     comprtexfmt_rgb = Polymost_TryCompressedDummyTexture(pic, comprtexfmts_rgb);
1670     comprtexfmt_rgba = Polymost_TryCompressedDummyTexture(pic, comprtexfmts_rgba);
1671     comprtexfmt_rgb_mask = Polymost_TryCompressedDummyTexture(pic, comprtexfmts_rgb_mask);
1672 
1673     glDeleteTextures(1, &tex);
1674 }
1675 #endif
1676 
Polymost_SendTexToDriver(int32_t const doalloc,vec2_t const siz,int32_t const texfmt,coltype const * const pic,int32_t const intexfmt,int32_t const comprtexfmt,int32_t const texcompress_ok,int32_t const level)1677 static void Polymost_SendTexToDriver(int32_t const doalloc,
1678                                      vec2_t const siz,
1679                                      int32_t const texfmt,
1680                                      coltype const * const pic,
1681                                      int32_t const intexfmt,
1682 #if defined EDUKE32_GLES
1683                                      int32_t const comprtexfmt,
1684                                      int32_t const texcompress_ok,
1685 #endif
1686                                      int32_t const level)
1687 {
1688 #if defined EDUKE32_GLES
1689     if (texcompress_ok && comprtexfmt && (siz.x & 3) == 0 && (siz.y & 3) == 0)
1690     {
1691         size_t const picLength = siz.x * siz.y;
1692         size_t const fourRows = siz.x << 2u;
1693         GLsizei const imageSize = picLength >> 1u; // 4x4 pixels --> 8 bytes
1694         uint8_t * const comprpic = (uint8_t *)Xaligned_alloc(8, imageSize);
1695 
1696         ETCFunction_t func = Polymost_PickETCFunction(comprtexfmt);
1697 
1698         coltype buf[4*4];
1699         uint64_t * out = (uint64_t *)comprpic;
1700         for (coltype const * row = pic, * const pic_end = pic + picLength; row < pic_end; row += fourRows)
1701             for (coltype const * block = row, * const row_end = row + siz.x; block < row_end; block += 4)
1702             {
1703                 buf[0] = block[0];
1704                 buf[1] = block[siz.x];
1705                 buf[2] = block[siz.x*2];
1706                 buf[3] = block[siz.x*3];
1707                 buf[4] = block[1];
1708                 buf[5] = block[siz.x+1];
1709                 buf[6] = block[siz.x*2+1];
1710                 buf[7] = block[siz.x*3+1];
1711                 buf[8] = block[2];
1712                 buf[9] = block[siz.x+2];
1713                 buf[10] = block[siz.x*2+2];
1714                 buf[11] = block[siz.x*3+2];
1715                 buf[12] = block[3];
1716                 buf[13] = block[siz.x+3];
1717                 buf[14] = block[siz.x*2+3];
1718                 buf[15] = block[siz.x*3+3];
1719 
1720                 *out++ = func((uint8_t const *)buf);
1721             }
1722 
1723         if (doalloc & 1)
1724             jwzgles_glCompressedTexImage2D(GL_TEXTURE_2D, level, comprtexfmt, siz.x,siz.y, 0, imageSize, comprpic);
1725         else
1726             jwzgles_glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0,0, siz.x,siz.y, comprtexfmt, imageSize, comprpic);
1727 
1728         Xaligned_free(comprpic);
1729 
1730         return;
1731     }
1732 #endif
1733 
1734 #if B_BIG_ENDIAN
1735     GLenum type = GL_UNSIGNED_INT_8_8_8_8;
1736 #else
1737     GLenum type = GL_UNSIGNED_INT_8_8_8_8_REV;
1738 #endif
1739     if (doalloc & 1)
1740         glTexImage2D(GL_TEXTURE_2D, level, intexfmt, siz.x,siz.y, 0, texfmt, type, pic);
1741     else
1742         glTexSubImage2D(GL_TEXTURE_2D, level, 0,0, siz.x,siz.y, texfmt, type, pic);
1743 }
1744 
uploadtexture(int32_t doalloc,vec2_t siz,int32_t texfmt,coltype * pic,vec2_t tsiz,int32_t dameth)1745 void uploadtexture(int32_t doalloc, vec2_t siz, int32_t texfmt,
1746                    coltype *pic, vec2_t tsiz, int32_t dameth)
1747 {
1748     const int artimmunity = !!(dameth & DAMETH_ARTIMMUNITY);
1749     const int hi = !!(dameth & DAMETH_HI);
1750     const int nodownsize = !!(dameth & DAMETH_NODOWNSIZE) || artimmunity;
1751     const int nomiptransfix  = !!(dameth & DAMETH_NOFIX);
1752     const int texcompress_ok = !(dameth & DAMETH_NOTEXCOMPRESS) && (glusetexcompr == 2 || (glusetexcompr && !artimmunity));
1753 
1754 #if !defined EDUKE32_GLES
1755     int32_t intexfmt;
1756     if (texcompress_ok && glinfo.texcompr)
1757         intexfmt = GL_COMPRESSED_RGBA;
1758     else
1759         intexfmt = GL_RGBA8;
1760 #else
1761     const int hasalpha  = !!(dameth & (DAMETH_HASALPHA|DAMETH_ONEBITALPHA));
1762     const int onebitalpha  = !!(dameth & DAMETH_ONEBITALPHA);
1763 
1764     int32_t const intexfmt = hasalpha ? (onebitalpha ? texfmt_rgb_mask : texfmt_rgba) : texfmt_rgb;
1765     int32_t const comprtexfmt = hasalpha ? (onebitalpha ? comprtexfmt_rgb_mask : comprtexfmt_rgba) : comprtexfmt_rgb;
1766 #endif
1767 
1768     dameth &= ~DAMETH_UPLOADTEXTURE_MASK;
1769 
1770     if (gltexmaxsize <= 0)
1771     {
1772         GLint i = 0;
1773         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &i);
1774         if (!i) gltexmaxsize = 6;   // 2^6 = 64 == default GL max texture size
1775         else
1776         {
1777             gltexmaxsize = 0;
1778             for (; i>1; i>>=1) gltexmaxsize++;
1779 #ifdef EDUKE32_GLES
1780             while ((1<<(gltexmaxsize-1)) > xdim)
1781                 gltexmaxsize--;
1782 #endif
1783         }
1784     }
1785 
1786     gltexmiplevel = max(0, min(gltexmaxsize-1, gltexmiplevel));
1787 
1788     int miplevel = gltexmiplevel;
1789 
1790     while ((siz.x >> miplevel) > (1 << gltexmaxsize) || (siz.y >> miplevel) > (1 << gltexmaxsize))
1791         miplevel++;
1792 
1793     if (hi && !nodownsize && r_downsize > miplevel)
1794         miplevel = r_downsize;
1795 
1796     // don't use mipmaps if mipmapping is disabled
1797     //POGO: until the texcacheheader can be updated, generate the mipmaps texcache expects if it's enabled
1798     if (!glusetexcache &&
1799         (glfiltermodes[gltexfiltermode].min == GL_NEAREST ||
1800          glfiltermodes[gltexfiltermode].min == GL_LINEAR))
1801     {
1802         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1803         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
1804     }
1805 
1806     if (!miplevel)
1807         Polymost_SendTexToDriver(doalloc, siz, texfmt, pic,
1808                                  intexfmt,
1809 #if defined EDUKE32_GLES
1810                                  comprtexfmt,
1811                                  texcompress_ok,
1812 #endif
1813                                  0);
1814 
1815     // don't generate mipmaps if we're not going to use them
1816     if (!glusetexcache &&
1817         (glfiltermodes[gltexfiltermode].min == GL_NEAREST ||
1818          glfiltermodes[gltexfiltermode].min == GL_LINEAR))
1819     {
1820         return;
1821     }
1822 
1823     vec2_t siz2 = siz;
1824 
1825     for (bssize_t j=1; (siz2.x > 1) || (siz2.y > 1); j++)
1826     {
1827         vec2_t const siz3 = { max(1, siz2.x >> 1), max(1, siz2.y >> 1) };  // this came from the GL_ARB_texture_non_power_of_two spec
1828         //x3 = ((x2+1)>>1); y3 = ((y2+1)>>1);
1829 
1830         for (bssize_t y=0; y<siz3.y; y++)
1831         {
1832             coltype *wpptr = &pic[y*siz3.x];
1833             coltype const *rpptr = &pic[(y<<1)*siz2.x];
1834 
1835             for (bssize_t x=0; x<siz3.x; x++,wpptr++,rpptr+=2)
1836             {
1837                 int32_t r=0, g=0, b=0, a=0, k=0;
1838 
1839                 if (rpptr[0].a)                  { r += rpptr[0].r; g += rpptr[0].g; b += rpptr[0].b; a += rpptr[0].a; k++; }
1840                 if ((x+x+1 < siz2.x) && (rpptr[1].a)) { r += rpptr[1].r; g += rpptr[1].g; b += rpptr[1].b; a += rpptr[1].a; k++; }
1841                 if (y+y+1 < siz2.y)
1842                 {
1843                     if ((rpptr[siz2.x].a)) { r += rpptr[siz2.x  ].r; g += rpptr[siz2.x  ].g; b += rpptr[siz2.x  ].b; a += rpptr[siz2.x  ].a; k++; }
1844                     if ((x+x+1 < siz2.x) && (rpptr[siz2.x+1].a)) { r += rpptr[siz2.x+1].r; g += rpptr[siz2.x+1].g; b += rpptr[siz2.x+1].b; a += rpptr[siz2.x+1].a; k++; }
1845                 }
1846                 switch (k)
1847                 {
1848                 case 0:
1849                 case 1:
1850                     wpptr->r = r; wpptr->g = g; wpptr->b = b; wpptr->a = a; break;
1851                 case 2:
1852                     wpptr->r = ((r+1)>>1); wpptr->g = ((g+1)>>1); wpptr->b = ((b+1)>>1); wpptr->a = ((a+1)>>1); break;
1853                 case 3:
1854                     wpptr->r = ((r*85+128)>>8); wpptr->g = ((g*85+128)>>8); wpptr->b = ((b*85+128)>>8); wpptr->a = ((a*85+128)>>8); break;
1855                 case 4:
1856                     wpptr->r = ((r+2)>>2); wpptr->g = ((g+2)>>2); wpptr->b = ((b+2)>>2); wpptr->a = ((a+2)>>2); break;
1857                 default:
1858                     EDUKE32_UNREACHABLE_SECTION(break);
1859                 }
1860                 //if (wpptr->a) wpptr->a = 255;
1861             }
1862         }
1863 
1864         if (!nomiptransfix)
1865         {
1866             vec2_t const tsizzle = { (tsiz.x + (1 << j)-1) >> j, (tsiz.y + (1 << j)-1) >> j };
1867 
1868             fixtransparency(pic, tsizzle, siz3, dameth);
1869         }
1870 
1871         if (j >= miplevel)
1872             Polymost_SendTexToDriver(doalloc, siz3, texfmt, pic,
1873                                      intexfmt,
1874 #if defined EDUKE32_GLES
1875                                      comprtexfmt,
1876                                      texcompress_ok,
1877 #endif
1878                                      j - miplevel);
1879 
1880         siz2 = siz3;
1881     }
1882 }
1883 
uploadtextureindexed(int32_t doalloc,vec2_t offset,vec2_t siz,intptr_t tile)1884 void uploadtextureindexed(int32_t doalloc, vec2_t offset, vec2_t siz, intptr_t tile)
1885 {
1886     if (doalloc & 1)
1887     {
1888         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1889         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
1890         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1891         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1892         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
1893 
1894         glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, siz.y, siz.x, 0, GL_RED, GL_UNSIGNED_BYTE, (void*) tile);
1895     }
1896     else
1897     {
1898         glTexSubImage2D(GL_TEXTURE_2D, 0, offset.x, offset.y, siz.y, siz.x, GL_RED, GL_UNSIGNED_BYTE, (void*) tile);
1899     }
1900 }
1901 
uploadbasepalette(int32_t basepalnum)1902 void uploadbasepalette(int32_t basepalnum)
1903 {
1904     if (!polymost1BasicShaderProgramID)
1905     {
1906         //POGO: if we haven't initialized properly yet, we shouldn't be uploading base palettes
1907         return;
1908     }
1909     if (!basepaltable[basepalnum])
1910     {
1911         return;
1912     }
1913 
1914     //POGO: this is only necessary for GL fog/vertex color shade compatibility, since those features don't index into shade tables
1915     uint8_t basepalWFullBrightInfo[4*256];
1916     for (int i = 0; i < 256; ++i)
1917     {
1918         basepalWFullBrightInfo[i*4] = basepaltable[basepalnum][i*3];
1919         basepalWFullBrightInfo[i*4+1] = basepaltable[basepalnum][i*3+1];
1920         basepalWFullBrightInfo[i*4+2] = basepaltable[basepalnum][i*3+2];
1921         basepalWFullBrightInfo[i*4+3] = 0-(IsPaletteIndexFullbright(i) != 0);
1922     }
1923 
1924     char allocateTexture = !paletteTextureIDs[basepalnum];
1925     if (allocateTexture)
1926     {
1927         glGenTextures(1, &paletteTextureIDs[basepalnum]);
1928     }
1929     glBindTexture(GL_TEXTURE_2D, paletteTextureIDs[basepalnum]);
1930     if (allocateTexture)
1931     {
1932         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1933         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
1934         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1935         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1936         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
1937         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1938         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1939         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, basepalWFullBrightInfo);
1940     }
1941     else
1942     {
1943         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE, basepalWFullBrightInfo);
1944     }
1945 }
1946 
uploadpalswap(int32_t palookupnum)1947 void uploadpalswap(int32_t palookupnum)
1948 {
1949     if (!polymost1BasicShaderProgramID)
1950     {
1951         //POGO: if we haven't initialized properly yet, we shouldn't be uploading palette swap tables
1952         return;
1953     }
1954     if (!palookup[palookupnum])
1955     {
1956         return;
1957     }
1958 
1959     char allocateTexture = !palswapTextureID;
1960     if (allocateTexture)
1961     {
1962         glGenTextures(1, &palswapTextureID);
1963     }
1964     glBindTexture(GL_TEXTURE_2D, palswapTextureID);
1965     if (allocateTexture)
1966     {
1967         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1968         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
1969         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1970         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1971         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
1972         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1973         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1974         glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, PALSWAP_TEXTURE_SIZE, PALSWAP_TEXTURE_SIZE, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
1975     }
1976 
1977     int32_t column = palookupnum%(PALSWAP_TEXTURE_SIZE/256);
1978     int32_t row = palookupnum/(PALSWAP_TEXTURE_SIZE/256);
1979     int32_t rowOffset = (numshades+1)*row;
1980     if (rowOffset > PALSWAP_TEXTURE_SIZE)
1981     {
1982         OSD_Printf("Polymost: palswaps are too large for palswap tilesheet!\n");
1983         return;
1984     }
1985     //POGO: There was a reason why having an extra row of black pixels was necessary along the edge of the palswap (I believe it affected a particular IHV/GPU).
1986     //      It may be worth investigating what this reason was again, but for now, make sure we properly initialize this row.
1987     static char blackPixels256[256] = {0};
1988     glTexSubImage2D(GL_TEXTURE_2D, 0, 256*column, rowOffset+numshades, 256, 1, GL_RED, GL_UNSIGNED_BYTE, blackPixels256);
1989     glTexSubImage2D(GL_TEXTURE_2D, 0, 256*column, rowOffset, 256, numshades, GL_RED, GL_UNSIGNED_BYTE, palookup[palookupnum]);
1990 }
1991 
1992 
1993 #if 0
1994 // TODO: make configurable
1995 static int32_t tile_is_sky(int32_t tilenum)
1996 {
1997     return return (tilenum >= 78 /*CLOUDYOCEAN*/ && tilenum <= 99 /*REDSKY2*/);
1998 }
1999 # define clamp_if_tile_is_sky(x, y) (tile_is_sky(x) ? (y) : GL_REPEAT)
2000 #else
2001 # define clamp_if_tile_is_sky(x, y) (GL_REPEAT)
2002 #endif
2003 
polymost_setuptexture(const int32_t dameth,int filter)2004 void polymost_setuptexture(const int32_t dameth, int filter)
2005 {
2006     const GLuint clamp_mode = glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
2007 
2008     gltexfiltermode = clamp(gltexfiltermode, 0, NUMGLFILTERMODES-1);
2009 
2010     if (filter == -1)
2011         filter = gltexfiltermode;
2012 
2013     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glfiltermodes[filter].mag);
2014     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glfiltermodes[filter].min);
2015 
2016 #ifdef USE_GLEXT
2017     if (glinfo.maxanisotropy > 1.f)
2018     {
2019         uint32_t i = (unsigned)Blrintf(glinfo.maxanisotropy);
2020 
2021         if ((unsigned)glanisotropy > i)
2022             glanisotropy = i;
2023 
2024         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glanisotropy);
2025     }
2026 #endif
2027 
2028     if (!(dameth & DAMETH_CLAMPED))
2029     {
2030         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_if_tile_is_sky(dapic, clamp_mode));
2031         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
2032     }
2033     else
2034     {
2035         // For sprite textures, clamping looks better than wrapping
2036         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_mode);
2037         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp_mode);
2038     }
2039 }
2040 
gloadtile_art_indexed(int32_t dapic,int32_t dameth,pthtyp * pth,int32_t doalloc)2041 static void gloadtile_art_indexed(int32_t dapic, int32_t dameth, pthtyp *pth, int32_t doalloc)
2042 {
2043     vec2_16_t const & tsizart = tilesiz[dapic];
2044     vec2_t siz = { tsizart.x, tsizart.y };
2045     //POGOTODO: npoty
2046     char npoty = 0;
2047 
2048     //POGOTODO: if !glinfo.texnpot, then we could allocate a texture of the pow2 size, and then populate the subportion using buffersubdata func
2049     //if (!glinfo.texnpot)
2050 
2051     Tile tile = {};
2052     if (waloff[dapic])
2053     {
2054         char tileIsPacked = tilepacker_getTile(dapic+1, &tile);
2055         if (tileIsPacked)
2056         {
2057             if (tile.rect.width >= (uint32_t) tsizart.y &&
2058                 tile.rect.height >= (uint32_t) tsizart.x)
2059             {
2060                 if (pth->glpic != 0 &&
2061                     pth->glpic != tilesheetTexIDs[tile.tilesheetID])
2062                 {
2063                     //POGO: we have a separate texture for this tile, but we want to merge it back into the tilesheet
2064                     glDeleteTextures(1, &pth->glpic);
2065                 }
2066                 pth->glpic = tilesheetTexIDs[tile.tilesheetID];
2067                 doalloc = false;
2068             } else if (pth->glpic == tilesheetTexIDs[tile.tilesheetID])
2069             {
2070                 //POGO: we're reloading an invalidated art tile that has changed dimensions and no longer fits into our original tilesheet
2071                 doalloc = true;
2072             }
2073         } else
2074         {
2075             Tile blankTile = {};
2076             tilepacker_getTile(0, &blankTile);
2077             if (pth->glpic == tilesheetTexIDs[blankTile.tilesheetID])
2078             {
2079                 //POGO: we're reloading an invalidated art tile that had previously been added to the texcache while !waloff[dapic]
2080                 doalloc = true;
2081             }
2082         }
2083 
2084         if (doalloc)
2085         {
2086             glGenTextures(1, (GLuint *)&pth->glpic);
2087         }
2088         glBindTexture(GL_TEXTURE_2D, pth->glpic);
2089 
2090         if (doalloc)
2091         {
2092             const GLuint clamp_mode = glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
2093             if (!(dameth & DAMETH_CLAMPED))
2094             {
2095                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_if_tile_is_sky(dapic, clamp_mode));
2096                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
2097             }
2098             else
2099             {
2100                 // For sprite textures, clamping looks better than wrapping
2101                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_mode);
2102                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp_mode);
2103             }
2104         }
2105 
2106         if (!doalloc &&
2107             !tileIsPacked &&
2108             (siz.x != pth->siz.x ||
2109              siz.y != pth->siz.y))
2110         {
2111             //POGO: resize our texture to match the tile data
2112             doalloc = true;
2113         }
2114         uploadtextureindexed(doalloc, {(int32_t) tile.rect.u, (int32_t) tile.rect.v}, siz, waloff[dapic]);
2115     }
2116     else
2117     {
2118         tilepacker_getTile(0, &tile);
2119         pth->glpic = tilesheetTexIDs[tile.tilesheetID];
2120     }
2121 
2122     pth->picnum = dapic;
2123     pth->palnum = 0;
2124     pth->shade = 0;
2125     pth->effects = 0;
2126     pth->flags = TO_PTH_CLAMPED(dameth) | TO_PTH_NOTRANSFIX(dameth) | (PTH_HASALPHA|PTH_ONEBITALPHA) | (npoty*PTH_NPOTWALL) | PTH_INDEXED;
2127     pth->hicr = NULL;
2128     pth->siz = siz;
2129 }
2130 
gloadtile_art(int32_t dapic,int32_t dapal,int32_t tintpalnum,int32_t dashade,int32_t dameth,pthtyp * pth,int32_t doalloc)2131 void gloadtile_art(int32_t dapic, int32_t dapal, int32_t tintpalnum, int32_t dashade, int32_t dameth, pthtyp *pth, int32_t doalloc)
2132 {
2133     if (dameth & DAMETH_INDEXED)
2134     {
2135         return gloadtile_art_indexed(dapic, dameth, pth, doalloc);
2136     }
2137 
2138     static int32_t fullbrightloadingpass = 0;
2139     vec2_16_t const & tsizart = tilesiz[dapic];
2140     vec2_t siz = { 0, 0 }, tsiz = { tsizart.x, tsizart.y };
2141     int const picdim = tsiz.x*tsiz.y;
2142     char hasalpha = 0, hasfullbright = 0;
2143     char npoty = 0;
2144 
2145     texcacheheader cachead;
2146     char texcacheid[BMAX_PATH];
2147     {
2148         // Absolutely disgusting.
2149         uint32_t firstint = 0;
2150         if (waloff[dapic])
2151             Bmemcpy(&firstint, (void *)waloff[dapic], min(4, picdim));
2152         sprintf(texcacheid, "%08x", firstint);
2153     }
2154     texcache_calcid(texcacheid, texcacheid, picdim | ((unsigned)dapal<<24u), DAMETH_NARROW_MASKPROPS(dameth) | ((unsigned)dapic<<8u) | ((unsigned)dashade<<24u), tintpalnum);
2155     int32_t gotcache = texcache_readtexheader(texcacheid, &cachead, 0);
2156 
2157     if (gotcache && !texcache_loadtile(&cachead, &doalloc, pth))
2158     {
2159         hasalpha = !!(cachead.flags & CACHEAD_HASALPHA);
2160         hasfullbright = !!(cachead.flags & CACHEAD_HASFULLBRIGHT);
2161         npoty = !!(cachead.flags & CACHEAD_NPOTWALL);
2162     }
2163     else
2164     {
2165         if (!glinfo.texnpot)
2166         {
2167             for (siz.x = 1; siz.x < tsiz.x; siz.x += siz.x) { }
2168             for (siz.y = 1; siz.y < tsiz.y; siz.y += siz.y) { }
2169         }
2170         else
2171         {
2172             if ((tsiz.x|tsiz.y) == 0)
2173                 siz.x = siz.y = 1;
2174             else
2175                 siz = tsiz;
2176         }
2177 
2178         coltype *pic = (coltype *)Xmalloc(siz.x*siz.y*sizeof(coltype));
2179 
2180         if (!waloff[dapic])
2181         {
2182             //Force invalid textures to draw something - an almost purely transparency texture
2183             //This allows the Z-buffer to be updated for mirrors (which are invalidated textures)
2184             pic[0].r = pic[0].g = pic[0].b = 0; pic[0].a = 1;
2185             tsiz.x = tsiz.y = 1; hasalpha = 1;
2186         }
2187         else
2188         {
2189             const int dofullbright = !(picanm[dapic].sf & PICANM_NOFULLBRIGHT_BIT) && !(globalflags & GLOBAL_NO_GL_FULLBRIGHT);
2190 
2191             for (bssize_t y = 0; y < siz.y; y++)
2192             {
2193                 coltype *wpptr = &pic[y * siz.x];
2194                 int32_t y2 = (y < tsiz.y) ? y : y - tsiz.y;
2195 
2196                 for (bssize_t x = 0; x < siz.x; x++, wpptr++)
2197                 {
2198                     int32_t dacol;
2199                     int32_t x2 = (x < tsiz.x) ? x : x-tsiz.x;
2200 
2201                     if ((dameth & DAMETH_CLAMPED) && (x >= tsiz.x || y >= tsiz.y)) //Clamp texture
2202                     {
2203                         wpptr->r = wpptr->g = wpptr->b = wpptr->a = 0;
2204                         continue;
2205                     }
2206 
2207                     dacol = *(char *)(waloff[dapic]+x2*tsiz.y+y2);
2208 
2209                     if (dacol == 255)
2210                     {
2211                         wpptr->a = 0;
2212                         hasalpha = 1;
2213                     }
2214                     else
2215                         wpptr->a = 255;
2216 
2217                     char *p = (char *)(palookup[dapal])+(int32_t)(dashade<<8);
2218                     dacol = (uint8_t)p[dacol];
2219 
2220                     if (!fullbrightloadingpass)
2221                     {
2222                         // regular texture
2223                         if (IsPaletteIndexFullbright(dacol) && dofullbright)
2224                             hasfullbright = 1;
2225                     }
2226                     else
2227                     {
2228                         // texture with only fullbright areas
2229                         if (!IsPaletteIndexFullbright(dacol))    // regular colors
2230                         {
2231                             wpptr->a = 0;
2232                             hasalpha = 1;
2233                         }
2234                     }
2235 
2236                     bricolor((palette_t *)wpptr, dacol);
2237 
2238                     if (!fullbrightloadingpass && tintpalnum >= 0)
2239                     {
2240                         polytint_t const & tint = hictinting[tintpalnum];
2241                         polytintflags_t const effect = tint.f;
2242                         uint8_t const r = tint.r;
2243                         uint8_t const g = tint.g;
2244                         uint8_t const b = tint.b;
2245 
2246                         if (effect & HICTINT_GRAYSCALE)
2247                         {
2248                             wpptr->g = wpptr->r = wpptr->b = (uint8_t) ((wpptr->r * GRAYSCALE_COEFF_RED) +
2249                                                                   (wpptr->g * GRAYSCALE_COEFF_GREEN) +
2250                                                                   (wpptr->b * GRAYSCALE_COEFF_BLUE));
2251                         }
2252 
2253                         if (effect & HICTINT_INVERT)
2254                         {
2255                             wpptr->b = 255 - wpptr->b;
2256                             wpptr->g = 255 - wpptr->g;
2257                             wpptr->r = 255 - wpptr->r;
2258                         }
2259 
2260                         if (effect & HICTINT_COLORIZE)
2261                         {
2262                             wpptr->b = min((int32_t)((wpptr->b) * b) >> 6, 255);
2263                             wpptr->g = min((int32_t)((wpptr->g) * g) >> 6, 255);
2264                             wpptr->r = min((int32_t)((wpptr->r) * r) >> 6, 255);
2265                         }
2266 
2267                         switch (effect & HICTINT_BLENDMASK)
2268                         {
2269                             case HICTINT_BLEND_SCREEN:
2270                                 wpptr->b = 255 - (((255 - wpptr->b) * (255 - b)) >> 8);
2271                                 wpptr->g = 255 - (((255 - wpptr->g) * (255 - g)) >> 8);
2272                                 wpptr->r = 255 - (((255 - wpptr->r) * (255 - r)) >> 8);
2273                                 break;
2274                             case HICTINT_BLEND_OVERLAY:
2275                                 wpptr->b = wpptr->b < 128 ? (wpptr->b * b) >> 7 : 255 - (((255 - wpptr->b) * (255 - b)) >> 7);
2276                                 wpptr->g = wpptr->g < 128 ? (wpptr->g * g) >> 7 : 255 - (((255 - wpptr->g) * (255 - g)) >> 7);
2277                                 wpptr->r = wpptr->r < 128 ? (wpptr->r * r) >> 7 : 255 - (((255 - wpptr->r) * (255 - r)) >> 7);
2278                                 break;
2279                             case HICTINT_BLEND_HARDLIGHT:
2280                                 wpptr->b = b < 128 ? (wpptr->b * b) >> 7 : 255 - (((255 - wpptr->b) * (255 - b)) >> 7);
2281                                 wpptr->g = g < 128 ? (wpptr->g * g) >> 7 : 255 - (((255 - wpptr->g) * (255 - g)) >> 7);
2282                                 wpptr->r = r < 128 ? (wpptr->r * r) >> 7 : 255 - (((255 - wpptr->r) * (255 - r)) >> 7);
2283                                 break;
2284                         }
2285                     }
2286 
2287                     //swap r & b so that we deal with the data as BGRA
2288                     uint8_t tmpR = wpptr->r;
2289                     wpptr->r = wpptr->b;
2290                     wpptr->b = tmpR;
2291                 }
2292             }
2293         }
2294 
2295         if (doalloc) glGenTextures(1,(GLuint *)&pth->glpic); //# of textures (make OpenGL allocate structure)
2296         glBindTexture(GL_TEXTURE_2D, pth->glpic);
2297 
2298         fixtransparency(pic,tsiz,siz,dameth);
2299 
2300         if (picanm[dapic].tileflags & TILEFLAGS_TRUENPOT)
2301         {
2302             npoty = 0;
2303         }
2304         else if (polymost_want_npotytex(dameth, siz.y) && tsiz.x == siz.x && tsiz.y == siz.y)  // XXX
2305         {
2306             const int32_t nextpoty = 1 << ((picsiz[dapic] >> 4) + 1);
2307             const int32_t ydif = nextpoty - siz.y;
2308             coltype *paddedpic;
2309 
2310             Bassert(ydif < siz.y);
2311 
2312             paddedpic = (coltype *)Xrealloc(pic, siz.x * nextpoty * sizeof(coltype));
2313 
2314             pic = paddedpic;
2315             Bmemcpy(&pic[siz.x * siz.y], pic, siz.x * ydif * sizeof(coltype));
2316             siz.y = tsiz.y = nextpoty;
2317 
2318             npoty = 1;
2319         }
2320 
2321         if (!doalloc)
2322         {
2323             vec2_t pthSiz2 = pth->siz;
2324             if (!glinfo.texnpot)
2325             {
2326                 for (pthSiz2.x = 1; pthSiz2.x < pth->siz.x; pthSiz2.x += pthSiz2.x) { }
2327                 for (pthSiz2.y = 1; pthSiz2.y < pth->siz.y; pthSiz2.y += pthSiz2.y) { }
2328             }
2329             else
2330             {
2331                 if ((pthSiz2.x|pthSiz2.y) == 0)
2332                     pthSiz2.x = pthSiz2.y = 1;
2333                 else
2334                     pthSiz2 = pth->siz;
2335             }
2336             if (siz.x > pthSiz2.x ||
2337                 siz.y > pthSiz2.y)
2338             {
2339                 //POGO: grow our texture to hold the tile data
2340                 doalloc = true;
2341             }
2342         }
2343         uploadtexture(doalloc, siz, GL_BGRA, pic, tsiz,
2344                       dameth | DAMETH_ARTIMMUNITY |
2345                       (dapic >= MAXUSERTILES ? (DAMETH_NOTEXCOMPRESS|DAMETH_NODOWNSIZE) : 0) | /* never process these short-lived tiles */
2346                       (hasfullbright ? DAMETH_HASFULLBRIGHT : 0) |
2347                       (npoty ? DAMETH_NPOTWALL : 0) |
2348                       (hasalpha ? (DAMETH_HASALPHA|DAMETH_ONEBITALPHA) : 0));
2349 
2350         Xfree(pic);
2351     }
2352 
2353     polymost_setuptexture(dameth, -1);
2354 
2355     pth->picnum = dapic;
2356     pth->palnum = dapal;
2357     pth->shade = dashade;
2358     pth->effects = 0;
2359     pth->flags = TO_PTH_CLAMPED(dameth) | TO_PTH_NOTRANSFIX(dameth) | (hasalpha*(PTH_HASALPHA|PTH_ONEBITALPHA)) | (npoty*PTH_NPOTWALL);
2360     pth->hicr = NULL;
2361     pth->siz = tsiz;
2362 
2363 #if defined USE_GLEXT && !defined EDUKE32_GLES
2364     if (!gotcache && glinfo.texcompr && glusetexcache && glusetexcompr == 2 && dapic < MAXUSERTILES)
2365     {
2366         cachead.quality = 0;
2367         cachead.xdim = tsiz.x;
2368         cachead.ydim = tsiz.y;
2369 
2370         cachead.flags = (check_nonpow2(siz.x) || check_nonpow2(siz.y)) * CACHEAD_NONPOW2 |
2371                         npoty * CACHEAD_NPOTWALL |
2372                         hasalpha * CACHEAD_HASALPHA | hasfullbright * CACHEAD_HASFULLBRIGHT | CACHEAD_NODOWNSIZE;
2373 
2374         texcache_writetex_fromdriver(texcacheid, &cachead);
2375     }
2376 #endif
2377 
2378     if (hasfullbright && !fullbrightloadingpass)
2379     {
2380         // Load the ONLY texture that'll be assembled with the regular one to
2381         // make the final texture with fullbright pixels.
2382         fullbrightloadingpass = 1;
2383 
2384         if (!pth->ofb)
2385             pth->ofb = (pthtyp *)Xcalloc(1,sizeof(pthtyp));
2386 
2387         pth->flags |= PTH_HASFULLBRIGHT;
2388 
2389         gloadtile_art(dapic, dapal, -1, 0, (dameth & ~DAMETH_MASKPROPS) | DAMETH_MASK, pth->ofb, 1);
2390 
2391         fullbrightloadingpass = 0;
2392     }
2393 }
2394 
gloadtile_hi(int32_t dapic,int32_t dapalnum,int32_t facen,hicreplctyp * hicr,int32_t dameth,pthtyp * pth,int32_t doalloc,polytintflags_t effect)2395 int32_t gloadtile_hi(int32_t dapic,int32_t dapalnum, int32_t facen, hicreplctyp *hicr,
2396                             int32_t dameth, pthtyp *pth, int32_t doalloc, polytintflags_t effect)
2397 {
2398     if (!hicr) return -1;
2399 
2400     char *fn;
2401 
2402     if (facen > 0)
2403     {
2404         if (!hicr->skybox || facen > 6 || !hicr->skybox->face[facen-1])
2405             return -1;
2406 
2407         fn = hicr->skybox->face[facen-1];
2408     }
2409     else
2410     {
2411         if (!hicr->filename)
2412             return -1;
2413 
2414         fn = hicr->filename;
2415     }
2416 
2417     buildvfs_kfd filh;
2418     if (EDUKE32_PREDICT_FALSE((filh = kopen4load(fn, 0)) == buildvfs_kfd_invalid))
2419     {
2420         OSD_Printf("hightile: %s (pic %d) not found\n", fn, dapic);
2421         return -2;
2422     }
2423 
2424     int32_t picfillen = kfilelength(filh);
2425     kclose(filh);	// FIXME: shouldn't have to do this. bug in cache1d.c
2426 
2427     int32_t startticks = timerGetTicks(), willprint = 0;
2428 
2429     char onebitalpha = 1;
2430     char hasalpha;
2431     texcacheheader cachead;
2432     char texcacheid[BMAX_PATH];
2433     texcache_calcid(texcacheid, fn, picfillen+(dapalnum<<8), DAMETH_NARROW_MASKPROPS(dameth), effect & HICTINT_IN_MEMORY);
2434     int32_t gotcache = texcache_readtexheader(texcacheid, &cachead, 0);
2435     vec2_t siz = { 0, 0 }, tsiz = { 0, 0 };
2436 
2437     if (gotcache && !texcache_loadtile(&cachead, &doalloc, pth))
2438     {
2439         tsiz = { cachead.xdim, cachead.ydim };
2440         hasalpha = !!(cachead.flags & CACHEAD_HASALPHA);
2441     }
2442     else
2443     {
2444         // CODEDUP: mdloadskin
2445 
2446         int32_t isart = 0;
2447 
2448         gotcache = 0;	// the compressed version will be saved to disk
2449 
2450         int32_t const length = kpzbufload(fn);
2451         if (length == 0)
2452             return -1;
2453 
2454         // tsizx/y = replacement texture's natural size
2455         // xsiz/y = 2^x size of replacement
2456 
2457 #ifdef WITHKPLIB
2458         kpgetdim(kpzbuf,picfillen,&tsiz.x,&tsiz.y);
2459 #endif
2460 
2461         if (tsiz.x == 0 || tsiz.y == 0)
2462         {
2463             if (artCheckUnitFileHeader((uint8_t *)kpzbuf, picfillen))
2464                 return -1;
2465 
2466             tsiz = { B_LITTLE16(B_UNBUF16(&kpzbuf[16])), B_LITTLE16(B_UNBUF16(&kpzbuf[18])) };
2467 
2468             if (tsiz.x == 0 || tsiz.y == 0)
2469                 return -1;
2470 
2471             isart = 1;
2472         }
2473 
2474         if (!glinfo.texnpot)
2475         {
2476             for (siz.x=1; siz.x<tsiz.x; siz.x+=siz.x) { }
2477             for (siz.y=1; siz.y<tsiz.y; siz.y+=siz.y) { }
2478         }
2479         else
2480             siz = tsiz;
2481 
2482         if (isart)
2483         {
2484             if (tsiz.x * tsiz.y + ARTv1_UNITOFFSET > picfillen)
2485                 return -2;
2486         }
2487 
2488         int32_t const bytesperline = siz.x * sizeof(coltype);
2489         coltype *pic = (coltype *)Xcalloc(siz.y, bytesperline);
2490 
2491         static coltype *lastpic = NULL;
2492         static char *lastfn = NULL;
2493         static int32_t lastsize = 0;
2494 
2495         if (lastpic && lastfn && !Bstrcmp(lastfn,fn))
2496         {
2497             willprint=1;
2498             Bmemcpy(pic, lastpic, siz.x*siz.y*sizeof(coltype));
2499         }
2500         else
2501         {
2502             if (isart)
2503             {
2504                 artConvertRGB((palette_t *)pic, (uint8_t *)&kpzbuf[ARTv1_UNITOFFSET], siz.x, tsiz.x, tsiz.y);
2505             }
2506 #ifdef WITHKPLIB
2507             else
2508             {
2509                 if (kprender(kpzbuf,picfillen,(intptr_t)pic,bytesperline,siz.x,siz.y))
2510                 {
2511                     Xfree(pic);
2512                     return -2;
2513                 }
2514             }
2515 #endif
2516 
2517             willprint=2;
2518 
2519             if (hicprecaching)
2520             {
2521                 lastfn = fn;  // careful...
2522                 if (!lastpic)
2523                 {
2524                     lastpic = (coltype *)Xmalloc(siz.x*siz.y*sizeof(coltype));
2525                     lastsize = siz.x*siz.y;
2526                 }
2527                 else if (lastsize < siz.x*siz.y)
2528                 {
2529                     Xfree(lastpic);
2530                     lastpic = (coltype *)Xmalloc(siz.x*siz.y*sizeof(coltype));
2531                 }
2532                 if (lastpic)
2533                     Bmemcpy(lastpic, pic, siz.x*siz.y*sizeof(coltype));
2534             }
2535             else if (lastpic)
2536             {
2537                 DO_FREE_AND_NULL(lastpic);
2538                 lastfn = NULL;
2539                 lastsize = 0;
2540             }
2541         }
2542 
2543         char *cptr = britable[gammabrightness ? 0 : curbrightness];
2544 
2545         polytint_t const & tint = hictinting[dapalnum];
2546         int32_t r = (glinfo.bgra) ? tint.r : tint.b;
2547         int32_t g = tint.g;
2548         int32_t b = (glinfo.bgra) ? tint.b : tint.r;
2549 
2550         char al = 255;
2551 
2552         for (bssize_t y = 0, j = 0; y < tsiz.y; ++y, j += siz.x)
2553         {
2554             coltype tcol, *rpptr = &pic[j];
2555 
2556             for (bssize_t x = 0; x < tsiz.x; ++x)
2557             {
2558                 tcol.b = cptr[rpptr[x].b];
2559                 tcol.g = cptr[rpptr[x].g];
2560                 tcol.r = cptr[rpptr[x].r];
2561                 al &= tcol.a = rpptr[x].a;
2562                 onebitalpha &= tcol.a == 0 || tcol.a == 255;
2563 
2564                 if (effect & HICTINT_GRAYSCALE)
2565                 {
2566                     tcol.g = tcol.r = tcol.b = (uint8_t) ((tcol.b * GRAYSCALE_COEFF_RED) +
2567                                                           (tcol.g * GRAYSCALE_COEFF_GREEN) +
2568                                                           (tcol.r * GRAYSCALE_COEFF_BLUE));
2569                 }
2570 
2571                 if (effect & HICTINT_INVERT)
2572                 {
2573                     tcol.b = 255 - tcol.b;
2574                     tcol.g = 255 - tcol.g;
2575                     tcol.r = 255 - tcol.r;
2576                 }
2577 
2578                 if (effect & HICTINT_COLORIZE)
2579                 {
2580                     tcol.b = min((int32_t)((tcol.b) * r) >> 6, 255);
2581                     tcol.g = min((int32_t)((tcol.g) * g) >> 6, 255);
2582                     tcol.r = min((int32_t)((tcol.r) * b) >> 6, 255);
2583                 }
2584 
2585                 switch (effect & HICTINT_BLENDMASK)
2586                 {
2587                     case HICTINT_BLEND_SCREEN:
2588                         tcol.b = 255 - (((255 - tcol.b) * (255 - r)) >> 8);
2589                         tcol.g = 255 - (((255 - tcol.g) * (255 - g)) >> 8);
2590                         tcol.r = 255 - (((255 - tcol.r) * (255 - b)) >> 8);
2591                         break;
2592                     case HICTINT_BLEND_OVERLAY:
2593                         tcol.b = tcol.b < 128 ? (tcol.b * r) >> 7 : 255 - (((255 - tcol.b) * (255 - r)) >> 7);
2594                         tcol.g = tcol.g < 128 ? (tcol.g * g) >> 7 : 255 - (((255 - tcol.g) * (255 - g)) >> 7);
2595                         tcol.r = tcol.r < 128 ? (tcol.r * b) >> 7 : 255 - (((255 - tcol.r) * (255 - b)) >> 7);
2596                         break;
2597                     case HICTINT_BLEND_HARDLIGHT:
2598                         tcol.b = r < 128 ? (tcol.b * r) >> 7 : 255 - (((255 - tcol.b) * (255 - r)) >> 7);
2599                         tcol.g = g < 128 ? (tcol.g * g) >> 7 : 255 - (((255 - tcol.g) * (255 - g)) >> 7);
2600                         tcol.r = b < 128 ? (tcol.r * b) >> 7 : 255 - (((255 - tcol.r) * (255 - b)) >> 7);
2601                         break;
2602                 }
2603 
2604                 rpptr[x] = tcol;
2605             }
2606         }
2607 
2608         hasalpha = (al != 255);
2609         onebitalpha &= hasalpha;
2610 
2611         if ((!(dameth & DAMETH_CLAMPED)) || facen) //Duplicate texture pixels (wrapping tricks for non power of 2 texture sizes)
2612         {
2613             if (siz.x > tsiz.x)  // Copy left to right
2614             {
2615                 for (int32_t y = 0, *lptr = (int32_t *)pic; y < tsiz.y; y++, lptr += siz.x)
2616                     Bmemcpy(&lptr[tsiz.x], lptr, (siz.x - tsiz.x) << 2);
2617             }
2618 
2619             if (siz.y > tsiz.y)  // Copy top to bottom
2620                 Bmemcpy(&pic[siz.x * tsiz.y], pic, (siz.y - tsiz.y) * siz.x << 2);
2621         }
2622 
2623         if (!glinfo.bgra)
2624         {
2625             for (bssize_t i=siz.x*siz.y, j=0; j<i; j++)
2626                 swapchar(&pic[j].r, &pic[j].b);
2627         }
2628 
2629         // end CODEDUP
2630 
2631         if (tsiz.x>>r_downsize <= tilesiz[dapic].x || tsiz.y>>r_downsize <= tilesiz[dapic].y)
2632             hicr->flags |= HICR_ARTIMMUNITY;
2633 
2634         if ((doalloc&3)==1)
2635             glGenTextures(1, &pth->glpic); //# of textures (make OpenGL allocate structure)
2636         glBindTexture(GL_TEXTURE_2D, pth->glpic);
2637 
2638         fixtransparency(pic,tsiz,siz,dameth);
2639 
2640         int32_t const texfmt = glinfo.bgra ? GL_BGRA : GL_RGBA;
2641 
2642         if (!doalloc)
2643         {
2644             vec2_t pthSiz2 = pth->siz;
2645             if (!glinfo.texnpot)
2646             {
2647                 for (pthSiz2.x=1; pthSiz2.x < pth->siz.x; pthSiz2.x+=pthSiz2.x) { }
2648                 for (pthSiz2.y=1; pthSiz2.y < pth->siz.y; pthSiz2.y+=pthSiz2.y) { }
2649             }
2650             else
2651                 pthSiz2 = tsiz;
2652             if (siz.x > pthSiz2.x ||
2653                 siz.y > pthSiz2.y)
2654             {
2655                 //POGO: grow our texture to hold the tile data
2656                 doalloc = true;
2657             }
2658         }
2659         uploadtexture(doalloc,siz,texfmt,pic,tsiz,
2660                       dameth | DAMETH_HI | DAMETH_NOFIX |
2661                       TO_DAMETH_NODOWNSIZE(hicr->flags) |
2662                       TO_DAMETH_NOTEXCOMPRESS(hicr->flags) |
2663                       TO_DAMETH_ARTIMMUNITY(hicr->flags) |
2664                       (onebitalpha ? DAMETH_ONEBITALPHA : 0) |
2665                       (hasalpha ? DAMETH_HASALPHA : 0));
2666 
2667         Xfree(pic);
2668     }
2669 
2670     // precalculate scaling parameters for replacement
2671     if (facen > 0)
2672         pth->scale = { (float)tsiz.x * (1.0f/64.f), (float)tsiz.y * (1.0f/64.f) };
2673     else
2674         pth->scale = { (float)tsiz.x / (float)tilesiz[dapic].x, (float)tsiz.y / (float)tilesiz[dapic].y };
2675 
2676     polymost_setuptexture(dameth, (hicr->flags & HICR_FORCEFILTER) ? TEXFILTER_ON : -1);
2677 
2678     if (tsiz.x>>r_downsize <= tilesiz[dapic].x || tsiz.y>>r_downsize <= tilesiz[dapic].y)
2679         hicr->flags |= HICR_ARTIMMUNITY;
2680 
2681     pth->picnum = dapic;
2682     pth->effects = effect;
2683     pth->flags = TO_PTH_CLAMPED(dameth) | TO_PTH_NOTRANSFIX(dameth) |
2684                  PTH_HIGHTILE | ((facen>0) * PTH_SKYBOX) |
2685                  (onebitalpha ? PTH_ONEBITALPHA : 0) |
2686                  (hasalpha ? PTH_HASALPHA : 0) |
2687                  ((hicr->flags & HICR_FORCEFILTER) ? PTH_FORCEFILTER : 0);
2688     pth->skyface = facen;
2689     pth->hicr = hicr;
2690     pth->siz = tsiz;
2691 
2692 #if defined USE_GLEXT && !defined EDUKE32_GLES
2693     if (!gotcache && glinfo.texcompr && glusetexcache && !(hicr->flags & HICR_NOTEXCOMPRESS) &&
2694         (glusetexcompr == 2 || (glusetexcompr && !(hicr->flags & HICR_ARTIMMUNITY))))
2695     {
2696         const int32_t nonpow2 = check_nonpow2(siz.x) || check_nonpow2(siz.y);
2697 
2698         // save off the compressed version
2699         cachead.quality = (hicr->flags & (HICR_NODOWNSIZE|HICR_ARTIMMUNITY)) ? 0 : r_downsize;
2700         cachead.xdim = tsiz.x >> cachead.quality;
2701         cachead.ydim = tsiz.y >> cachead.quality;
2702 
2703         // handle nodownsize:
2704         cachead.flags = nonpow2 * CACHEAD_NONPOW2 | (hasalpha ? CACHEAD_HASALPHA : 0) |
2705                         ((hicr->flags & (HICR_NODOWNSIZE|HICR_ARTIMMUNITY)) ? CACHEAD_NODOWNSIZE : 0);
2706 
2707         ///            OSD_Printf("Caching \"%s\"\n", fn);
2708         texcache_writetex_fromdriver(texcacheid, &cachead);
2709 
2710         if (willprint)
2711         {
2712             int32_t etime = timerGetTicks() - startticks;
2713             if (etime >= MIN_CACHETIME_PRINT)
2714                 OSD_Printf("Load tile %4d: p%d-m%d-e%d %s... cached... %d ms\n", dapic, dapalnum, dameth, effect,
2715                            willprint == 2 ? fn : "", etime);
2716             willprint = 0;
2717         }
2718         else
2719             OSD_Printf("Cached \"%s\"\n", fn);
2720     }
2721 #endif
2722 
2723     if (willprint)
2724     {
2725         int32_t etime = timerGetTicks()-startticks;
2726         if (etime>=MIN_CACHETIME_PRINT)
2727             OSD_Printf("Load tile %4d: p%d-m%d-e%d %s... %d ms\n", dapic, dapalnum, dameth, effect,
2728                        willprint==2 ? fn : "", etime);
2729     }
2730 
2731     return 0;
2732 }
2733 
2734 #ifdef USE_GLEXT
polymost_setupdetailtexture(const int32_t texunits,const int32_t tex)2735 void polymost_setupdetailtexture(const int32_t texunits, const int32_t tex)
2736 {
2737     glActiveTexture(texunits);
2738 
2739     glBindTexture(GL_TEXTURE_2D, tex);
2740 
2741     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
2742     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
2743 
2744     glClientActiveTexture(texunits);
2745     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
2746 }
2747 
polymost_setupglowtexture(const int32_t texunits,const int32_t tex)2748 void polymost_setupglowtexture(const int32_t texunits, const int32_t tex)
2749 {
2750     glActiveTexture(texunits);
2751 
2752     glBindTexture(GL_TEXTURE_2D, tex);
2753 
2754     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
2755     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
2756 
2757     glClientActiveTexture(texunits);
2758     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
2759 }
2760 #endif
2761 
2762 
2763 //(dpx,dpy) specifies an n-sided polygon. The polygon must be a convex clockwise loop.
2764 //    n must be <= 8 (assume clipping can double number of vertices)
2765 //method: 0:solid, 1:masked(255 is transparent), 2:transluscent #1, 3:transluscent #2
2766 //    +4 means it's a sprite, so wraparound isn't needed
2767 
2768 // drawpoly's hack globals
2769 static int32_t pow2xsplit = 0, skyclamphack = 0, skyzbufferhack = 0, flatskyrender = 0;
2770 static float drawpoly_alpha = 0.f;
2771 static uint8_t drawpoly_blend = 0;
2772 
our_texcache_fetch(int32_t dameth)2773 static inline pthtyp *our_texcache_fetch(int32_t dameth)
2774 {
2775     return texcache_fetch(globalpicnum, globalpal, getpalookup(0, globalshade), dameth);
2776 }
2777 
polymost_maskWallHasTranslucency(uwalltype const * const wall)2778 int32_t polymost_maskWallHasTranslucency(uwalltype const * const wall)
2779 {
2780     if (wall->cstat & CSTAT_WALL_TRANSLUCENT)
2781         return true;
2782 
2783     //POGO: only hightiles may have translucency in their texture
2784     if (!usehightile)
2785         return false;
2786 
2787     uint8_t pal = wall->pal;
2788     if (palookup[pal] == NULL)
2789         pal = 0;
2790 
2791     pthtyp* pth = texcache_fetch(wall->picnum, pal, 0, DAMETH_MASK | DAMETH_WALL);
2792     return pth && (pth->flags & PTH_HASALPHA) && !(pth->flags & PTH_ONEBITALPHA);
2793 }
2794 
polymost_spriteHasTranslucency(tspritetype const * const tspr)2795 int32_t polymost_spriteHasTranslucency(tspritetype const * const tspr)
2796 {
2797     if ((tspr->cstat & CSTAT_SPRITE_TRANSLUCENT) || (tspr->clipdist & TSPR_FLAGS_DRAW_LAST) ||
2798         ((unsigned)tspr->owner < MAXSPRITES && spriteext[tspr->owner].alpha))
2799         return true;
2800 
2801     //POGO: only hightiles may have translucency in their texture
2802     if (!usehightile)
2803         return false;
2804 
2805     uint8_t pal = tspr->shade;
2806     if (palookup[pal] == NULL)
2807         pal = 0;
2808 
2809     pthtyp* pth = texcache_fetch(tspr->picnum, pal, 0, DAMETH_MASK | DAMETH_CLAMPED);
2810     return pth && (pth->flags & PTH_HASALPHA) && !(pth->flags & PTH_ONEBITALPHA);
2811 }
2812 
polymost_spriteIsModelOrVoxel(tspritetype const * const tspr)2813 int32_t polymost_spriteIsModelOrVoxel(tspritetype const * const tspr)
2814 {
2815     if ((unsigned)tspr->owner < MAXSPRITES && spriteext[tspr->owner].flags&SPREXT_NOTMD)
2816         return false;
2817 
2818     if (usemodels && tile2model[Ptile2tile(tspr->picnum, tspr->pal)].modelid >= 0 &&
2819         tile2model[Ptile2tile(tspr->picnum, tspr->pal)].framenum >= 0)
2820         return true;
2821 
2822     if (usevoxels && (tspr->cstat & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_SLAB && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]])
2823         return true;
2824 
2825     if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLAB && voxmodels[tspr->picnum])
2826         return true;
2827 
2828     return false;
2829 }
2830 
polymost2_drawVBO(GLenum mode,int32_t vertexBufferID,int32_t indexBufferID,const int32_t numElements,float projectionMatrix[4* 4],float modelViewMatrix[4* 4],int32_t dameth,float texScale[2],float texOffset[2],char cullFaces)2831 static void polymost2_drawVBO(GLenum mode,
2832                               int32_t vertexBufferID,
2833                               int32_t indexBufferID,
2834                               const int32_t numElements,
2835                               float projectionMatrix[4*4],
2836                               float modelViewMatrix[4*4],
2837                               int32_t dameth,
2838                               float texScale[2],
2839                               float texOffset[2],
2840                               char cullFaces)
2841 {
2842     if (dameth == DAMETH_BACKFACECULL ||
2843     #ifdef YAX_ENABLE
2844         g_nodraw ||
2845     #endif
2846         (uint32_t)globalpicnum >= MAXTILES)
2847     {
2848         return;
2849     }
2850 
2851     glDisableClientState(GL_VERTEX_ARRAY);
2852     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
2853 
2854     if (cullFaces)
2855     {
2856         glEnable(GL_CULL_FACE);
2857     }
2858     //POGOTODO: this is temporary, the permanent fix is to not allow the transform to affect the windings in the first place in polymost2_drawSprite()
2859     if (cullFaces == 1)
2860     {
2861         glCullFace(GL_BACK);
2862     }
2863     else
2864     {
2865         glCullFace(GL_FRONT);
2866     }
2867 
2868     //POGOTODO: in the future, state changes like binding these buffers can be batched.  For now, just switch on every VBO rendered
2869     glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
2870     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
2871 
2872     glEnableVertexAttribArray(0);
2873     glEnableVertexAttribArray(1);
2874 
2875     if (palookup[globalpal] == NULL)
2876     {
2877         globalpal = 0;
2878     }
2879 
2880     //Load texture (globalpicnum)
2881     setgotpic(globalpicnum);
2882     if (!waloff[globalpicnum])
2883     {
2884         tileLoad(globalpicnum);
2885     }
2886 
2887     pthtyp *pth = our_texcache_fetch(dameth | (r_useindexedcolortextures ? DAMETH_INDEXED : 0));
2888 
2889     if (!pth)
2890     {
2891         if (editstatus)
2892         {
2893             Bsprintf(ptempbuf, "pth==NULL! (bad pal?) pic=%d pal=%d", globalpicnum, globalpal);
2894             polymost_printtext256(8,8, editorcolors[15],editorcolors[5], ptempbuf, 0);
2895         }
2896         return;
2897     }
2898 
2899     glActiveTexture(GL_TEXTURE1);
2900     //POGO: temporarily swapped out blankTextureID for 0 (as the blank texture has been moved into the dynamic tilesheets)
2901     glBindTexture(GL_TEXTURE_2D, (pth && pth->flags & PTH_HASFULLBRIGHT && r_fullbrights) ? pth->ofb->glpic : 0);
2902     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
2903     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
2904 
2905     glActiveTexture(GL_TEXTURE0);
2906     polymost_bindPth(pth);
2907     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
2908     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
2909 
2910     //POGOTODO: handle tinting & shading completely with fragment shader
2911     //POGOTODO: handle fullbright & glow completely with fragment shader
2912 
2913     //POGOTODO: glAlphaFunc is deprecated, move this into the fragment shader
2914     float const al = waloff[globalpicnum] ? alphahackarray[globalpicnum] != 0 ? alphahackarray[globalpicnum] * (1.f/255.f):
2915                              (pth && pth->hicr && pth->hicr->alphacut >= 0.f ? pth->hicr->alphacut : 0.f) : 0.f;
2916     glAlphaFunc(GL_GREATER, al);
2917     //POGOTODO: batch this, only apply it to sprites that actually need blending
2918     glEnable(GL_BLEND);
2919     glEnable(GL_ALPHA_TEST);
2920 
2921     handle_blend((dameth & DAMETH_MASKPROPS) > DAMETH_MASK, drawpoly_blend, (dameth & DAMETH_MASKPROPS) == DAMETH_TRANS2);
2922 
2923     polymost_useShaderProgram(polymost2BasicShaderProgramID);
2924 
2925     //POGOTODO: batch uniform binding
2926     float tint[4] = {1.0f, 1.0f, 1.0f, 1.0f};
2927     polytint_t const & polytint = hictinting[globalpal];
2928     //POGOTODO: full bright pass uses its own globalshade...
2929     tint[0] = (1.f-(polytint.sr*(1.f/255.f)))*getshadefactor(globalshade, globalpal)+(polytint.sr*(1.f/255.f));
2930     tint[1] = (1.f-(polytint.sg*(1.f/255.f)))*getshadefactor(globalshade, globalpal)+(polytint.sg*(1.f/255.f));
2931     tint[2] = (1.f-(polytint.sb*(1.f/255.f)))*getshadefactor(globalshade, globalpal)+(polytint.sb*(1.f/255.f));
2932 
2933     // spriteext full alpha control
2934     float alpha = float_trans(dameth & DAMETH_MASKPROPS, drawpoly_blend) * (1.f - drawpoly_alpha);
2935 
2936     if (pth)
2937     {
2938         // tinting
2939         polytintflags_t const tintflags = hictinting[globalpal].f;
2940         if (!(tintflags & HICTINT_PRECOMPUTED))
2941         {
2942             if (pth->flags & PTH_HIGHTILE)
2943             {
2944                 if (pth->palnum != globalpal || (pth->effects & HICTINT_IN_MEMORY) || (tintflags & HICTINT_APPLYOVERALTPAL))
2945                     hictinting_apply(tint, globalpal);
2946             }
2947             else if (tintflags & (HICTINT_USEONART|HICTINT_ALWAYSUSEART))
2948                 hictinting_apply(tint, globalpal);
2949         }
2950 
2951         // global tinting
2952         if ((pth->flags & PTH_HIGHTILE) && have_basepal_tint())
2953             hictinting_apply(tint, MAXPALOOKUPS-1);
2954     }
2955 
2956     glUniformMatrix4fv(projMatrixLoc, 1, false, projectionMatrix);
2957     glUniformMatrix4fv(mvMatrixLoc, 1, false, modelViewMatrix);
2958     glUniform1i(texSamplerLoc, 0);
2959     glUniform1i(fullBrightSamplerLoc, 1);
2960     glUniform2fv(texOffsetLoc, 1, texOffset);
2961     glUniform2fv(texScaleLoc, 1, texScale);
2962     glUniform4fv(tintLoc, 1, tint);
2963     glUniform1f(alphaLoc, alpha);
2964     const float fogRange[2] = {fogresult, fogresult2};
2965     glUniform2fv(fogRangeLoc, 1, fogRange);
2966     glUniform4fv(fogColorLoc, 1, (GLfloat*) &fogcol);
2967 
2968     if (indexBufferID == 0)
2969     {
2970         glDrawArrays(mode,
2971                      0,
2972                      numElements);
2973     }
2974     else
2975     {
2976         glDrawElements(mode,
2977                        numElements,
2978                        GL_UNSIGNED_SHORT,
2979                        0);
2980     }
2981 
2982     glDisableVertexAttribArray(0);
2983     glDisableVertexAttribArray(1);
2984 
2985     //POGOTODO: again, these state changes should be batched in the future, rather than on each VBO rendered
2986     glBindBuffer(GL_ARRAY_BUFFER, 0);
2987     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2988 
2989     glDisable(GL_CULL_FACE);
2990 
2991     glEnableClientState(GL_VERTEX_ARRAY);
2992     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
2993 
2994     //polymost_resetVertexPointers();
2995 }
2996 
polymost_updatePalette()2997 void polymost_updatePalette()
2998 {
2999     if (videoGetRenderMode() != REND_POLYMOST)
3000         return;
3001 
3002     polymost_setPalswap(globalpal);
3003     polymost_setShade(globalshade);
3004 
3005     //POGO: only bind the base pal once when it's swapped
3006     if (curbasepal != lastbasepal)
3007     {
3008         glActiveTexture(GL_TEXTURE2);
3009         glBindTexture(GL_TEXTURE_2D, paletteTextureIDs[curbasepal]);
3010         lastbasepal = curbasepal;
3011         glActiveTexture(GL_TEXTURE0);
3012     }
3013 }
3014 
polymost_lockSubBuffer(uint32_t subBufferIndex)3015 static void polymost_lockSubBuffer(uint32_t subBufferIndex)
3016 {
3017     if (drawpolyVertsSync[subBufferIndex])
3018     {
3019         glDeleteSync(drawpolyVertsSync[subBufferIndex]);
3020     }
3021 
3022     drawpolyVertsSync[subBufferIndex] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3023 }
3024 
polymost_waitForSubBuffer(uint32_t subBufferIndex)3025 static void polymost_waitForSubBuffer(uint32_t subBufferIndex)
3026 {
3027     if (drawpolyVertsSync[subBufferIndex])
3028     {
3029         while (true)
3030         {
3031             // we only need to flush if there's a possibility that drawpolyVertsBufferLength is
3032             // so small that we can eat through 3 times the buffer size in a single frame
3033             GLenum waitResult = glClientWaitSync(drawpolyVertsSync[subBufferIndex], GL_SYNC_FLUSH_COMMANDS_BIT, 500000);
3034             if (waitResult == GL_ALREADY_SIGNALED ||
3035                 waitResult == GL_CONDITION_SATISFIED)
3036             {
3037                 return;
3038             }
3039             if (waitResult == GL_WAIT_FAILED)
3040             {
3041                 OSD_Printf("polymost_waitForSubBuffer: Wait failed! Error 0x%X. Disabling r_persistentStreamBuffer.\n", glGetError());
3042                 r_persistentStreamBuffer = 0;
3043                 videoResetMode();
3044                 if (videoSetGameMode(fullscreen,xres,yres,bpp,upscalefactor))
3045                 {
3046                     OSD_Printf("polymost_waitForSubBuffer: Video reset failed.  Please ensure r_persistentStreamBuffer = 0 and try restarting the game.\n");
3047                     Bexit(EXIT_FAILURE);
3048                 }
3049                 return;
3050             }
3051 
3052             static char loggedLongWait = false;
3053             if (waitResult == GL_TIMEOUT_EXPIRED &&
3054                 !loggedLongWait)
3055             {
3056                 OSD_Printf("polymost_waitForSubBuffer(): Had to wait for the drawpoly buffer to become available.  For performance, try increasing buffer size with r_drawpolyVertsBufferLength.\n");
3057                 loggedLongWait = true;
3058             }
3059         }
3060     }
3061 }
3062 
polymost_updaterotmat(void)3063 static void polymost_updaterotmat(void)
3064 {
3065     if (currentShaderProgramID == polymost1CurrentShaderProgramID)
3066     {
3067         float matrix[16] = {
3068             1.f, 0.f, 0.f, 0.f,
3069             0.f, 1.f, 0.f, 0.f,
3070             0.f, 0.f, 1.f, 0.f,
3071             0.f, 0.f, 0.f, 1.f,
3072         };
3073 #if !SOFTROTMAT
3074         //Up/down rotation
3075         float udmatrix[16] = {
3076             1.f, 0.f, 0.f, 0.f,
3077             0.f, gchang, -gshang*gvrcorrection, 0.f,
3078             0.f, gshang/gvrcorrection, gchang, 0.f,
3079             0.f, 0.f, 0.f, 1.f,
3080         };
3081         // Tilt rotation
3082         float tiltmatrix[16] = {
3083             gctang, -gstang, 0.f, 0.f,
3084             gstang, gctang, 0.f, 0.f,
3085             0.f, 0.f, 1.f, 0.f,
3086             0.f, 0.f, 0.f, 1.f,
3087         };
3088         multiplyMatrix4f(matrix, udmatrix);
3089         multiplyMatrix4f(matrix, tiltmatrix);
3090 #endif
3091         Bmemcpy(polymost1RotMatrix, matrix, sizeof(matrix));
3092         glUniformMatrix4fv(polymost1RotMatrixLoc, 1, false, polymost1RotMatrix);
3093     }
3094 }
3095 
polymost_identityrotmat(void)3096 static void polymost_identityrotmat(void)
3097 {
3098     if (currentShaderProgramID == polymost1CurrentShaderProgramID)
3099     {
3100         float matrix[16] = {
3101             1.f, 0.f, 0.f, 0.f,
3102             0.f, 1.f, 0.f, 0.f,
3103             0.f, 0.f, 1.f, 0.f,
3104             0.f, 0.f, 0.f, 1.f,
3105         };
3106         Bmemcpy(polymost1RotMatrix, matrix, sizeof(matrix));
3107         glUniformMatrix4fv(polymost1RotMatrixLoc, 1, false, polymost1RotMatrix);
3108     }
3109 }
3110 
polymost_polyeditorfunc(vec2f_t const * const dpxy,int n)3111 static void polymost_polyeditorfunc(vec2f_t const * const dpxy, int n)
3112 {
3113     if (!doeditorcheck || n < 3)
3114         return;
3115 
3116     for (int i = 0; i < n; i++)
3117     {
3118         float dx1 = dpxy[(i+1)%n].x-dpxy[i].x;
3119         float dy1 = dpxy[(i+1)%n].y-dpxy[i].y;
3120         float dx2 = fsearchx-dpxy[i].x;
3121         float dy2 = fsearchy-dpxy[i].y;
3122         float cross = dx1*dy2-dx2*dy1;
3123         if (cross < 0.f)
3124             return;
3125     }
3126 
3127     float const z = otex.d + xtex.d * fsearchx + ytex.d * fsearchy;
3128 
3129     if (z > fsearchz)
3130     {
3131         searchit = 1;
3132         searchsector = psectnum;
3133         searchwall = pwallnum;
3134         searchbottomwall = pbottomwall;
3135         searchisbottom = pisbottomwall;
3136         searchstat = psearchstat;
3137         fsearchz = z;
3138     }
3139 }
3140 
3141 static void polymost_flatskyrender(vec2f_t const* const dpxy, int32_t const n, int32_t method);
3142 
polymost_drawpoly(vec2f_t const * const dpxy,int32_t const n,int32_t method)3143 static void polymost_drawpoly(vec2f_t const * const dpxy, int32_t const n, int32_t method)
3144 {
3145     if (doeditorcheck && editstatus)
3146         polymost_polyeditorfunc(dpxy, n);
3147 
3148     if (method == DAMETH_BACKFACECULL ||
3149 #ifdef YAX_ENABLE
3150         g_nodraw ||
3151 #endif
3152         (uint32_t)globalpicnum >= MAXTILES)
3153         return;
3154 
3155     const int32_t method_ = method;
3156 
3157     if (n == 3)
3158     {
3159         if ((dpxy[0].x-dpxy[1].x) * (dpxy[2].y-dpxy[1].y) >=
3160             (dpxy[2].x-dpxy[1].x) * (dpxy[0].y-dpxy[1].y)) return; //for triangle
3161     }
3162     else if (n > 3)
3163     {
3164         float f = 0; //f is area of polygon / 2
3165 
3166         for (bssize_t i=n-2, j=n-1,k=0; k<n; i=j,j=k,k++)
3167             f += (dpxy[i].x-dpxy[k].x)*dpxy[j].y;
3168 
3169         if (f <= 0) return;
3170     }
3171     else if (n < 3)
3172         return;
3173 
3174     static int32_t skyzbufferhack_pass = 0;
3175     if (flatskyrender && skyzbufferhack_pass == 0)
3176     {
3177         polymost_flatskyrender(dpxy, n, method);
3178         return;
3179     }
3180 
3181     if (palookup[globalpal] == NULL)
3182         globalpal = 0;
3183 
3184     //Load texture (globalpicnum)
3185     setgotpic(globalpicnum);
3186     vec2_16_t const & tsizart = tilesiz[globalpicnum];
3187     vec2_t tsiz = { tsizart.x, tsizart.y };
3188 
3189     if (!waloff[globalpicnum])
3190     {
3191         tileLoad(globalpicnum);
3192     }
3193 
3194     Bassert(n <= MAX_DRAWPOLY_VERTS);
3195 
3196     int j = 0;
3197     float px[8], py[8], dd[8], uu[8], vv[8];
3198 #if SOFTROTMAT
3199     float const ozgs = (ghalfx / gvrcorrection) * gshang,
3200                 ozgc = (ghalfx / gvrcorrection) * gchang;
3201 #endif
3202 
3203     for (bssize_t i=0; i<n; ++i)
3204     {
3205 #if SOFTROTMAT
3206         //Up/down rotation
3207         vec3f_t const orot = { dpxy[i].x - ghalfx,
3208                               (dpxy[i].y - ghoriz) * gchang - ozgs,
3209                               (dpxy[i].y - ghoriz) * gshang + ozgc };
3210 
3211         // Tilt rotation
3212         float const r = (ghalfx / gvrcorrection) / orot.z;
3213 
3214         px[j] = ghalfx + (((orot.x * gctang) - (orot.y * gstang)) * r);
3215         py[j] = ghoriz + (((orot.x * gstang) + (orot.y * gctang)) * r);
3216 
3217         dd[j] = (dpxy[i].x * xtex.d + dpxy[i].y * ytex.d + otex.d) * r;
3218         if (dd[j] <= 0.f) // invalid polygon
3219             return;
3220         uu[j] = (dpxy[i].x * xtex.u + dpxy[i].y * ytex.u + otex.u) * r;
3221         vv[j] = (dpxy[i].x * xtex.v + dpxy[i].y * ytex.v + otex.v) * r;
3222 
3223         if ((!j) || (px[j] != px[j-1]) || (py[j] != py[j-1]))
3224             j++;
3225 #else
3226         px[j] = dpxy[i].x;
3227         py[j] = dpxy[i].y;
3228 
3229         dd[j] = (dpxy[i].x * xtex.d + dpxy[i].y * ytex.d + otex.d);
3230         if (dd[j] <= 0.f) // invalid polygon
3231             return;
3232         uu[j] = (dpxy[i].x * xtex.u + dpxy[i].y * ytex.u + otex.u);
3233         vv[j] = (dpxy[i].x * xtex.v + dpxy[i].y * ytex.v + otex.v);
3234         j++;
3235 #endif
3236     }
3237 
3238     while ((j >= 3) && (px[j-1] == px[0]) && (py[j-1] == py[0])) j--;
3239 
3240     if (j < 3)
3241         return;
3242 
3243     int const npoints = j;
3244 
3245     if (skyclamphack) method |= DAMETH_CLAMPED;
3246 
3247     polymost_outputGLDebugMessage(3, "polymost_drawpoly(dpxy:%p, n:%d, method_:%X), method: %X", dpxy, n, method_, method);
3248 
3249     pthtyp *pth = our_texcache_fetch(method | (videoGetRenderMode() == REND_POLYMOST && r_useindexedcolortextures ? DAMETH_INDEXED : 0));
3250 
3251     if (!pth)
3252     {
3253         if (editstatus)
3254         {
3255             Bsprintf(ptempbuf, "pth==NULL! (bad pal?) pic=%d pal=%d", globalpicnum, globalpal);
3256             polymost_printtext256(8,8, editorcolors[15],editorcolors[5], ptempbuf, 0);
3257         }
3258         return;
3259     }
3260 
3261     if (!waloff[globalpicnum])
3262     {
3263         tsiz.x = tsiz.y = 1;
3264         glColorMask(false, false, false, false); //Hack to update Z-buffer for invalid mirror textures
3265     }
3266 
3267     static int32_t fullbright_pass = 0;
3268 
3269     if (pth->flags & PTH_HASFULLBRIGHT && r_fullbrights)
3270     {
3271         if (!fullbright_pass)
3272             fullbright_pass = 1;
3273         else if (fullbright_pass == 2)
3274             pth = pth->ofb;
3275     }
3276 
3277     Bassert(pth);
3278 
3279     // If we aren't rendmode 3, we're in Polymer, which means this code is
3280     // used for rotatesprite only. Polymer handles all the material stuff,
3281     // just submit the geometry and don't mess with textures.
3282     if (videoGetRenderMode() == REND_POLYMOST)
3283     {
3284         polymost_bindPth(pth);
3285 
3286         //POGOTODO: I could move this into bindPth
3287         if (!(pth->flags & PTH_INDEXED))
3288             polymost_usePaletteIndexing(false);
3289         else if (polymost_usetileshades())
3290             polymost_setFogEnabled(false);
3291 
3292         if (drawpoly_srepeat)
3293             glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
3294         if (drawpoly_trepeat)
3295             glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
3296     }
3297 
3298     // texture scale by parkar request
3299     if (pth->hicr && !drawingskybox && ((pth->hicr->scale.x != 1.0f) || (pth->hicr->scale.y != 1.0f)))
3300     {
3301         glMatrixMode(GL_TEXTURE);
3302         glLoadIdentity();
3303         glScalef(pth->hicr->scale.x, pth->hicr->scale.y, 1.0f);
3304         glMatrixMode(GL_MODELVIEW);
3305     }
3306 
3307 #ifdef USE_GLEXT
3308     int32_t texunits = GL_TEXTURE0;
3309 
3310     if (videoGetRenderMode() == REND_POLYMOST)
3311     {
3312         polymost_updatePalette();
3313         texunits += 4;
3314     }
3315 
3316     // detail texture
3317     if (r_detailmapping)
3318     {
3319         pthtyp *detailpth = NULL;
3320 
3321         if (usehightile && !drawingskybox && hicfindsubst(globalpicnum, DETAILPAL, 1) &&
3322             (detailpth = texcache_fetch(globalpicnum, DETAILPAL, 0, method & ~DAMETH_MASKPROPS)) &&
3323             detailpth->hicr && detailpth->hicr->palnum == DETAILPAL)
3324         {
3325             polymost_useDetailMapping(true);
3326             polymost_setupdetailtexture(videoGetRenderMode() == REND_POLYMOST ? GL_TEXTURE3 : ++texunits, detailpth->glpic);
3327 
3328             glMatrixMode(GL_TEXTURE);
3329             glLoadIdentity();
3330 
3331             if (pth->hicr && ((pth->hicr->scale.x != 1.0f) || (pth->hicr->scale.y != 1.0f)))
3332                 glScalef(pth->hicr->scale.x, pth->hicr->scale.y, 1.0f);
3333 
3334             if ((detailpth->hicr->scale.x != 1.0f) || (detailpth->hicr->scale.y != 1.0f))
3335                 glScalef(detailpth->hicr->scale.x, detailpth->hicr->scale.y, 1.0f);
3336 
3337             glMatrixMode(GL_MODELVIEW);
3338             glActiveTexture(GL_TEXTURE0);
3339         }
3340     }
3341 
3342     // glow texture
3343     if (r_glowmapping)
3344     {
3345         pthtyp *glowpth = NULL;
3346 
3347         if (usehightile && !drawingskybox && hicfindsubst(globalpicnum, GLOWPAL, 1) &&
3348             (glowpth = texcache_fetch(globalpicnum, GLOWPAL, 0, (method & ~DAMETH_MASKPROPS) | DAMETH_MASK)) &&
3349             glowpth->hicr && (glowpth->hicr->palnum == GLOWPAL))
3350         {
3351             polymost_useGlowMapping(true);
3352             polymost_setupglowtexture(videoGetRenderMode() == REND_POLYMOST ? GL_TEXTURE4 : ++texunits, glowpth->glpic);
3353             glActiveTexture(GL_TEXTURE0);
3354         }
3355     }
3356 
3357     if (glinfo.texnpot && r_npotwallmode == 2 && (method & DAMETH_WALL) != 0 && !(picanm[globalpicnum].tileflags & TILEFLAGS_TRUENPOT))
3358     {
3359         int32_t size = tilesiz[globalpicnum].y;
3360         int32_t size2;
3361         for (size2 = 1; size2 < size; size2 += size2) {}
3362         if (size == size2)
3363             polymost_npotEmulation(false, 1.f, 0.f);
3364         else
3365         {
3366             float xOffset = 1.f / tilesiz[globalpicnum].x;
3367             polymost_npotEmulation(true, (1.f*size2) / size, xOffset);
3368         }
3369     }
3370     else
3371     {
3372         polymost_npotEmulation(false, 1.f, 0.f);
3373     }
3374 #endif
3375 
3376     vec2f_t hacksc = { 1.f, 1.f };
3377 
3378     if (pth->flags & PTH_HIGHTILE)
3379     {
3380         hacksc = pth->scale;
3381         tsiz = pth->siz;
3382     }
3383 
3384     vec2_t tsiz2 = tsiz;
3385 
3386     if (!glinfo.texnpot)
3387     {
3388         for (tsiz2.x = 1; tsiz2.x < tsiz.x; tsiz2.x += tsiz2.x)
3389             ; /* do nothing */
3390         for (tsiz2.y = 1; tsiz2.y < tsiz.y; tsiz2.y += tsiz2.y)
3391             ; /* do nothing */
3392     }
3393 
3394     if (method & DAMETH_MASKPROPS || fullbright_pass == 2)
3395     {
3396         float const al = alphahackarray[globalpicnum] != 0 ? alphahackarray[globalpicnum] * (1.f/255.f) :
3397                          (pth->hicr && pth->hicr->alphacut >= 0.f ? pth->hicr->alphacut : 0.f);
3398 
3399         glAlphaFunc(GL_GREATER, al);
3400         handle_blend((method & DAMETH_MASKPROPS) > DAMETH_MASK, drawpoly_blend, (method & DAMETH_MASKPROPS) == DAMETH_TRANS2);
3401     }
3402 
3403     float pc[4];
3404 
3405 #ifdef POLYMER
3406     if (videoGetRenderMode() == REND_POLYMER && pr_artmapping && !(globalflags & GLOBAL_NO_GL_TILESHADES) && polymer_eligible_for_artmap(globalpicnum, pth))
3407         pc[0] = pc[1] = pc[2] = 1.0f;
3408     else
3409 #endif
3410     {
3411         polytint_t const & tint = hictinting[globalpal];
3412         float shadeFactor = (pth->flags & PTH_INDEXED) && polymost_usetileshades() ? 1.f : getshadefactor(globalshade, globalpal);
3413         pc[0] = (1.f-(tint.sr*(1.f/255.f)))*shadeFactor+(tint.sr*(1.f/255.f));
3414         pc[1] = (1.f-(tint.sg*(1.f/255.f)))*shadeFactor+(tint.sg*(1.f/255.f));
3415         pc[2] = (1.f-(tint.sb*(1.f/255.f)))*shadeFactor+(tint.sb*(1.f/255.f));
3416     }
3417 
3418     // spriteext full alpha control
3419     pc[3] = float_trans(method & DAMETH_MASKPROPS, drawpoly_blend) * (1.f - drawpoly_alpha);
3420 
3421     // tinting
3422     polytintflags_t const tintflags = hictinting[globalpal].f;
3423     if (!(tintflags & HICTINT_PRECOMPUTED))
3424     {
3425         if (pth->flags & PTH_HIGHTILE)
3426         {
3427             if (pth->palnum != globalpal || (pth->effects & HICTINT_IN_MEMORY) || (tintflags & HICTINT_APPLYOVERALTPAL))
3428                 hictinting_apply(pc, globalpal);
3429         }
3430         else if (tintflags & (HICTINT_USEONART|HICTINT_ALWAYSUSEART))
3431             hictinting_apply(pc, globalpal);
3432     }
3433 
3434     // global tinting
3435     if ((pth->flags & PTH_HIGHTILE) && have_basepal_tint())
3436         hictinting_apply(pc, MAXPALOOKUPS-1);
3437 
3438     globaltinting_apply(pc);
3439 
3440     if (skyzbufferhack_pass)
3441         pc[3] = 0.01f;
3442 
3443     glColor4f(pc[0], pc[1], pc[2], pc[3]);
3444 
3445     //POGOTODO: remove this, replace it with a shader implementation
3446     //Hack for walls&masked walls which use textures that are not a power of 2
3447     if ((pow2xsplit) && (tsiz.x != tsiz2.x))
3448     {
3449         vec3f_t const opxy[3] = { { py[1] - py[2], py[2] - py[0], py[0] - py[1] },
3450                                   { px[2] - px[1], px[0] - px[2], px[1] - px[0] },
3451                                   { px[0] - .5f, py[0] - .5f, 0 } };
3452 
3453         float const r = 1.f / (opxy[0].x*px[0] + opxy[0].y*px[1] + opxy[0].z*px[2]);
3454 
3455         vec3f_t ngx = { (opxy[0].x * dd[0] + opxy[0].y * dd[1] + opxy[0].z * dd[2]) * r,
3456                         ((opxy[0].x * uu[0] + opxy[0].y * uu[1] + opxy[0].z * uu[2]) * r) * hacksc.x,
3457                         ((opxy[0].x * vv[0] + opxy[0].y * vv[1] + opxy[0].z * vv[2]) * r) * hacksc.y };
3458 
3459         vec3f_t ngy = { (opxy[1].x * dd[0] + opxy[1].y * dd[1] + opxy[1].z * dd[2]) * r,
3460                         ((opxy[1].x * uu[0] + opxy[1].y * uu[1] + opxy[1].z * uu[2]) * r) * hacksc.x,
3461                         ((opxy[1].x * vv[0] + opxy[1].y * vv[1] + opxy[1].z * vv[2]) * r) * hacksc.y };
3462 
3463         vec3f_t ngo = { dd[0] - opxy[2].x * ngx.d - opxy[2].y * ngy.d,
3464                         (uu[0] - opxy[2].x * ngx.u - opxy[2].y * ngy.u) * hacksc.x,
3465                         (vv[0] - opxy[2].x * ngx.v - opxy[2].y * ngy.v) * hacksc.y };
3466 
3467         float const uoffs = ((float)(tsiz2.x - tsiz.x) * 0.5f);
3468 
3469         ngx.u -= ngx.d * uoffs;
3470         ngy.u -= ngy.d * uoffs;
3471         ngo.u -= ngo.d * uoffs;
3472 
3473         float du0 = 0.f, du1 = 0.f;
3474 
3475         //Find min&max u coordinates (du0...du1)
3476         for (bssize_t i=0; i<npoints; ++i)
3477         {
3478             vec2f_t const o = { px[i], py[i] };
3479             float const f = (o.x*ngx.u + o.y*ngy.u + ngo.u) / (o.x*ngx.d + o.y*ngy.d + ngo.d);
3480             if (!i) { du0 = du1 = f; continue; }
3481             if (f < du0) du0 = f;
3482             else if (f > du1) du1 = f;
3483         }
3484 
3485         float const rf = 1.0f / tsiz.x;
3486         int const ix1 = (int)floorf(du1 * rf);
3487 
3488         for (bssize_t ix0 = (int)floorf(du0 * rf); ix0 <= ix1; ++ix0)
3489         {
3490             du0 = (float)(ix0 * tsiz.x);        // + uoffs;
3491             du1 = (float)((ix0 + 1) * tsiz.x);  // + uoffs;
3492 
3493             float duj = (px[0]*ngx.u + py[0]*ngy.u + ngo.u) / (px[0]*ngx.d + py[0]*ngy.d + ngo.d);
3494             int i = 0, nn = 0;
3495 
3496             do
3497             {
3498                 j = i + 1;
3499 
3500                 if (j == npoints)
3501                     j = 0;
3502 
3503                 float const dui = duj;
3504 
3505                 duj = (px[j]*ngx.u + py[j]*ngy.u + ngo.u) / (px[j]*ngx.d + py[j]*ngy.d + ngo.d);
3506 
3507                 if ((du0 <= dui) && (dui <= du1))
3508                 {
3509                     uu[nn] = px[i];
3510                     vv[nn] = py[i];
3511                     nn++;
3512                 }
3513 
3514                 //ox*(ngx.u-ngx.d*du1) + oy*(ngy.u-ngdy*du1) + (ngo.u-ngo.d*du1) = 0
3515                 //(px[j]-px[i])*f + px[i] = ox
3516                 //(py[j]-py[i])*f + py[i] = oy
3517 
3518                 ///Solve for f
3519                 //((px[j]-px[i])*f + px[i])*(ngx.u-ngx.d*du1) +
3520                 //((py[j]-py[i])*f + py[i])*(ngy.u-ngdy*du1) + (ngo.u-ngo.d*du1) = 0
3521 
3522                 auto mathyMcMatherson = [&](float const f) {
3523                     float const ff = -(px[i] * (ngx.u - ngx.d * f) + py[i] * (ngy.u - ngy.d * f) + (ngo.u - ngo.d * f))
3524                                     / ((px[j] - px[i]) * (ngx.u - ngx.d * f) + (py[j] - py[i]) * (ngy.u - ngy.d * f));
3525                     uu[nn] = (px[j] - px[i]) * ff + px[i];
3526                     vv[nn] = (py[j] - py[i]) * ff + py[i];
3527                     ++nn;
3528                 };
3529 
3530                 if (duj <= dui)
3531                 {
3532                     if ((du1 < duj) != (du1 < dui)) mathyMcMatherson(du1);
3533                     if ((du0 < duj) != (du0 < dui)) mathyMcMatherson(du0);
3534                 }
3535                 else
3536                 {
3537                     if ((du0 < duj) != (du0 < dui)) mathyMcMatherson(du0);
3538                     if ((du1 < duj) != (du1 < dui)) mathyMcMatherson(du1);
3539                 }
3540 
3541                 i = j;
3542             }
3543             while (i);
3544 
3545             if (nn < 3) continue;
3546 
3547             if (nn+drawpolyVertsOffset > (drawpolyVertsSubBufferIndex+1)*drawpolyVertsBufferLength)
3548             {
3549                 if (persistentStreamBuffer)
3550                 {
3551                     // lock this sub buffer
3552                     polymost_lockSubBuffer(drawpolyVertsSubBufferIndex);
3553                     drawpolyVertsSubBufferIndex = (drawpolyVertsSubBufferIndex+1)%3;
3554                     drawpolyVertsOffset = drawpolyVertsSubBufferIndex*drawpolyVertsBufferLength;
3555                     // wait for the next sub buffer to become available before writing to it
3556                     // our buffer size should be long enough that no waiting is ever necessary
3557                     polymost_waitForSubBuffer(drawpolyVertsSubBufferIndex);
3558                 }
3559                 else
3560                 {
3561                     glBufferData(GL_ARRAY_BUFFER, sizeof(float)*5*drawpolyVertsBufferLength, NULL, GL_STREAM_DRAW);
3562                     drawpolyVertsOffset = 0;
3563                 }
3564             }
3565 
3566             vec2f_t const invtsiz2 = { 1.f / tsiz2.x, 1.f / tsiz2.y };
3567             uint32_t off = persistentStreamBuffer ? drawpolyVertsOffset : 0;
3568             for (i = 0; i<nn; ++i)
3569             {
3570                 vec2f_t const o = { uu[i], vv[i] };
3571                 vec3f_t const p = { o.x * ngx.d + o.y * ngy.d + ngo.d,
3572                                     o.x * ngx.u + o.y * ngy.u + ngo.u,
3573                                     o.x * ngx.v + o.y * ngy.v + ngo.v };
3574                 float const r = 1.f/p.d;
3575 
3576                 //update verts
3577                 drawpolyVerts[(off+i)*5] = (o.x - ghalfx) * r * grhalfxdown10x;
3578                 drawpolyVerts[(off+i)*5+1] = (ghalfy - o.y) * r * grhalfxdown10;
3579                 drawpolyVerts[(off+i)*5+2] = r * (1.f / 1024.f);
3580 
3581                 //update texcoords
3582                 drawpolyVerts[(off+i)*5+3] = (p.u * r - du0 + uoffs) * invtsiz2.x;
3583                 drawpolyVerts[(off+i)*5+4] = p.v * r * invtsiz2.y;
3584             }
3585 
3586             if (!persistentStreamBuffer)
3587             {
3588                 glBufferSubData(GL_ARRAY_BUFFER, drawpolyVertsOffset*sizeof(float)*5, nn*sizeof(float)*5, drawpolyVerts);
3589             }
3590             glDrawArrays(GL_TRIANGLE_FAN, drawpolyVertsOffset, nn);
3591             drawpolyVertsOffset += nn;
3592         }
3593     }
3594     else
3595     {
3596         if (npoints+drawpolyVertsOffset > (drawpolyVertsSubBufferIndex+1)*drawpolyVertsBufferLength)
3597         {
3598             if (persistentStreamBuffer)
3599             {
3600                 // lock this sub buffer
3601                 polymost_lockSubBuffer(drawpolyVertsSubBufferIndex);
3602                 drawpolyVertsSubBufferIndex = (drawpolyVertsSubBufferIndex+1)%3;
3603                 drawpolyVertsOffset = drawpolyVertsSubBufferIndex*drawpolyVertsBufferLength;
3604                 // wait for the next sub buffer to become available before writing to it
3605                 // our buffer size should be long enough that no waiting is ever necessary
3606                 polymost_waitForSubBuffer(drawpolyVertsSubBufferIndex);
3607             }
3608             else
3609             {
3610                 glBufferData(GL_ARRAY_BUFFER, sizeof(float)*5*drawpolyVertsBufferLength, NULL, GL_STREAM_DRAW);
3611                 drawpolyVertsOffset = 0;
3612             }
3613         }
3614 
3615         vec2f_t const scale = { 1.f / tsiz2.x * hacksc.x, 1.f / tsiz2.y * hacksc.y };
3616         uint32_t off = persistentStreamBuffer ? drawpolyVertsOffset : 0;
3617         for (bssize_t i = 0; i < npoints; ++i)
3618         {
3619             float const r = 1.f / dd[i];
3620 
3621             //update verts
3622             drawpolyVerts[(off+i)*5] = (px[i] - ghalfx) * r * grhalfxdown10x;
3623             drawpolyVerts[(off+i)*5+1] = (ghalfy - py[i]) * r * grhalfxdown10;
3624             drawpolyVerts[(off+i)*5+2] = r * (1.f / 1024.f);
3625 
3626             //update texcoords
3627             drawpolyVerts[(off+i)*5+3] = uu[i] * r * scale.x;
3628             drawpolyVerts[(off+i)*5+4] = vv[i] * r * scale.y;
3629         }
3630 
3631         if (!persistentStreamBuffer)
3632         {
3633             glBufferSubData(GL_ARRAY_BUFFER, drawpolyVertsOffset*sizeof(float)*5, npoints*sizeof(float)*5, drawpolyVerts);
3634         }
3635         glDrawArrays(GL_TRIANGLE_FAN, drawpolyVertsOffset, npoints);
3636         drawpolyVertsOffset += npoints;
3637     }
3638 
3639 #ifdef USE_GLEXT
3640     if (videoGetRenderMode() != REND_POLYMOST)
3641     {
3642         while (texunits > GL_TEXTURE0)
3643         {
3644             glActiveTexture(texunits);
3645             glMatrixMode(GL_TEXTURE);
3646             glLoadIdentity();
3647             glMatrixMode(GL_MODELVIEW);
3648 
3649             glClientActiveTexture(texunits);
3650             glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3651 
3652             glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 1.0f);
3653 
3654             --texunits;
3655         }
3656     }
3657 
3658     polymost_useDetailMapping(false);
3659     polymost_useGlowMapping(false);
3660     polymost_npotEmulation(false, 1.f, 0.f);
3661 #endif
3662     if (pth->hicr)
3663     {
3664         glMatrixMode(GL_TEXTURE);
3665         glLoadIdentity();
3666         glMatrixMode(GL_MODELVIEW);
3667     }
3668 
3669     if (videoGetRenderMode() != REND_POLYMOST)
3670     {
3671         if (!waloff[globalpicnum])
3672             glColorMask(true, true, true, true);
3673 
3674         return;
3675     }
3676 
3677     if (!(pth->flags & PTH_INDEXED))
3678     {
3679         // restore palette usage if we were just rendering a non-indexed color texture
3680         polymost_usePaletteIndexing(true);
3681     }
3682     else if (!nofog)
3683         polymost_setFogEnabled(true);
3684 
3685     int const clamp_mode = glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
3686 
3687     if (drawpoly_srepeat)
3688         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp_mode);
3689 
3690     if (drawpoly_trepeat)
3691         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp_mode);
3692 
3693     if (fullbright_pass == 1)
3694     {
3695         int32_t const shade = globalshade;
3696 
3697         globalshade = -128;
3698         fullbright_pass = 2;
3699 
3700         polymost_setFogEnabled(false);
3701 
3702         glDepthFunc(GL_EQUAL);
3703 
3704         polymost_drawpoly(dpxy, n, method_);
3705 
3706         glDepthFunc(GL_LEQUAL);
3707 
3708         if (!nofog)
3709             polymost_setFogEnabled(true);
3710 
3711         globalshade = shade;
3712         fullbright_pass = 0;
3713     }
3714 
3715     if (skyzbufferhack && skyzbufferhack_pass == 0)
3716     {
3717         vec3d_t const bxtex = xtex, bytex = ytex, botex = otex;
3718         xtex = xtex2, ytex = ytex2, otex = otex2;
3719         skyzbufferhack_pass++;
3720         glColorMask(false, false, false, false);
3721         polymost_drawpoly(dpxy, n, DAMETH_MASK);
3722         glColorMask(true, true, true, true);
3723         xtex = bxtex, ytex = bytex, otex = botex;
3724         skyzbufferhack_pass--;
3725     }
3726 
3727     if (!waloff[globalpicnum])
3728         glColorMask(true, true, true, true);
3729 }
3730 
3731 
vsp_finalize_init(int32_t const vcnt)3732 static inline void vsp_finalize_init(int32_t const vcnt)
3733 {
3734     for (bssize_t i=0; i<vcnt; ++i)
3735     {
3736         vsp[i].cy[1] = vsp[i+1].cy[0]; vsp[i].ctag = i;
3737         vsp[i].fy[1] = vsp[i+1].fy[0]; vsp[i].ftag = i;
3738         vsp[i].n = i+1; vsp[i].p = i-1;
3739 //        vsp[i].tag = -1;
3740     }
3741     vsp[vcnt-1].n = 0; vsp[0].p = vcnt-1;
3742 
3743     //VSPMAX-1 is dummy empty node
3744     for (bssize_t i=vcnt; i<VSPMAX; i++) { vsp[i].n = i+1; vsp[i].p = i-1; }
3745     vsp[VSPMAX-1].n = vcnt; vsp[vcnt].p = VSPMAX-1;
3746 }
3747 
3748 #ifdef YAX_ENABLE
yax_vsp_finalize_init(int32_t const yaxbunch,int32_t const vcnt)3749 static inline void yax_vsp_finalize_init(int32_t const yaxbunch, int32_t const vcnt)
3750 {
3751     for (bssize_t i=0; i<vcnt; ++i)
3752     {
3753         yax_vsp[yaxbunch][i].cy[1] = yax_vsp[yaxbunch][i+1].cy[0]; yax_vsp[yaxbunch][i].ctag = i;
3754         yax_vsp[yaxbunch][i].n = i+1; yax_vsp[yaxbunch][i].p = i-1;
3755 //        vsp[i].tag = -1;
3756     }
3757     yax_vsp[yaxbunch][vcnt-1].n = 0; yax_vsp[yaxbunch][0].p = vcnt-1;
3758 
3759     //VSPMAX-1 is dummy empty node
3760     for (bssize_t i=vcnt; i<VSPMAX; i++) { yax_vsp[yaxbunch][i].n = i+1; yax_vsp[yaxbunch][i].p = i-1; }
3761     yax_vsp[yaxbunch][VSPMAX-1].n = vcnt; yax_vsp[yaxbunch][vcnt].p = VSPMAX-1;
3762 }
3763 #endif
3764 
3765 #define COMBINE_STRIPS
3766 
3767 #ifdef COMBINE_STRIPS
vsdel(int const i)3768 static inline void vsdel(int const i)
3769 {
3770     //Delete i
3771     int const pi = vsp[i].p;
3772     int const ni = vsp[i].n;
3773 
3774     vsp[ni].p = pi;
3775     vsp[pi].n = ni;
3776 
3777     //Add i to empty list
3778     vsp[i].n = vsp[VSPMAX-1].n;
3779     vsp[i].p = VSPMAX-1;
3780     vsp[vsp[VSPMAX-1].n].p = i;
3781     vsp[VSPMAX-1].n = i;
3782 }
3783 
vsmerge(int const i,int const ni)3784 static inline void vsmerge(int const i, int const ni)
3785 {
3786     vsp[i].cy[1] = vsp[ni].cy[1];
3787     vsp[i].fy[1] = vsp[ni].fy[1];
3788     vsdel(ni);
3789 }
3790 
3791 # ifdef YAX_ENABLE
yax_vsdel(int const yaxbunch,int const i)3792 static inline void yax_vsdel(int const yaxbunch, int const i)
3793 {
3794     //Delete i
3795     int const pi = yax_vsp[yaxbunch][i].p;
3796     int const ni = yax_vsp[yaxbunch][i].n;
3797 
3798     yax_vsp[yaxbunch][ni].p = pi;
3799     yax_vsp[yaxbunch][pi].n = ni;
3800 
3801     //Add i to empty list
3802     yax_vsp[yaxbunch][i].n = yax_vsp[yaxbunch][VSPMAX - 1].n;
3803     yax_vsp[yaxbunch][i].p = VSPMAX - 1;
3804     yax_vsp[yaxbunch][yax_vsp[yaxbunch][VSPMAX - 1].n].p = i;
3805     yax_vsp[yaxbunch][VSPMAX - 1].n = i;
3806 }
3807 # endif
3808 #endif
3809 
vsinsaft(int const i)3810 static inline int32_t vsinsaft(int const i)
3811 {
3812     //i = next element from empty list
3813     int32_t const r = vsp[VSPMAX-1].n;
3814     vsp[vsp[r].n].p = VSPMAX-1;
3815     vsp[VSPMAX-1].n = vsp[r].n;
3816 
3817     vsp[r] = vsp[i]; //copy i to r
3818 
3819     //insert r after i
3820     vsp[r].p = i; vsp[r].n = vsp[i].n;
3821     vsp[vsp[i].n].p = r; vsp[i].n = r;
3822 
3823     return r;
3824 }
3825 
3826 #ifdef YAX_ENABLE
yax_vsinsaft(int const yaxbunch,int const i)3827 static inline int32_t yax_vsinsaft(int const yaxbunch, int const i)
3828 {
3829     //i = next element from empty list
3830     int32_t const r = yax_vsp[yaxbunch][VSPMAX - 1].n;
3831     yax_vsp[yaxbunch][yax_vsp[yaxbunch][r].n].p = VSPMAX - 1;
3832     yax_vsp[yaxbunch][VSPMAX - 1].n = yax_vsp[yaxbunch][r].n;
3833 
3834     yax_vsp[yaxbunch][r] = yax_vsp[yaxbunch][i]; //copy i to r
3835 
3836     //insert r after i
3837     yax_vsp[yaxbunch][r].p = i; yax_vsp[yaxbunch][r].n = yax_vsp[yaxbunch][i].n;
3838     yax_vsp[yaxbunch][yax_vsp[yaxbunch][i].n].p = r; yax_vsp[yaxbunch][i].n = r;
3839 
3840     return r;
3841 }
3842 #endif
3843 
3844 static int32_t domostpolymethod = DAMETH_NOMASK;
3845 
3846 #define DOMOST_OFFSET .01f
3847 
polymost_clipmost(vec2f_t * dpxy,int & n,float x0,float x1,float y0top,float y0bot,float y1top,float y1bot)3848 static void polymost_clipmost(vec2f_t *dpxy, int &n, float x0, float x1, float y0top, float y0bot, float y1top, float y1bot)
3849 {
3850     if (y0bot < y0top || y1bot < y1top)
3851         return;
3852 
3853     //Clip to (x0,y0top)-(x1,y1top)
3854 
3855     vec2f_t dp2[8];
3856 
3857     float t0, t1;
3858     int n2 = 0;
3859     t1 = -((dpxy[0].x - x0) * (y1top - y0top) - (dpxy[0].y - y0top) * (x1 - x0));
3860 
3861     for (bssize_t i=0; i<n; i++)
3862     {
3863         int j = i + 1;
3864 
3865         if (j >= n)
3866             j = 0;
3867 
3868         t0 = t1;
3869         t1 = -((dpxy[j].x - x0) * (y1top - y0top) - (dpxy[j].y - y0top) * (x1 - x0));
3870 
3871         if (t0 >= 0)
3872             dp2[n2++] = dpxy[i];
3873 
3874         if ((t0 >= 0) != (t1 >= 0) && (t0 <= 0) != (t1 <= 0))
3875         {
3876             float const r = t0 / (t0 - t1);
3877             dp2[n2] = { (dpxy[j].x - dpxy[i].x) * r + dpxy[i].x,
3878                         (dpxy[j].y - dpxy[i].y) * r + dpxy[i].y };
3879             n2++;
3880         }
3881     }
3882 
3883     if (n2 < 3)
3884     {
3885         n = 0;
3886         return;
3887     }
3888 
3889     //Clip to (x1,y1bot)-(x0,y0bot)
3890     t1 = -((dp2[0].x - x1) * (y0bot - y1bot) - (dp2[0].y - y1bot) * (x0 - x1));
3891     n = 0;
3892 
3893     for (bssize_t i = 0, j = 1; i < n2; j = ++i + 1)
3894     {
3895         if (j >= n2)
3896             j = 0;
3897 
3898         t0 = t1;
3899         t1 = -((dp2[j].x - x1) * (y0bot - y1bot) - (dp2[j].y - y1bot) * (x0 - x1));
3900 
3901         if (t0 >= 0)
3902             dpxy[n++] = dp2[i];
3903 
3904         if ((t0 >= 0) != (t1 >= 0) && (t0 <= 0) != (t1 <= 0))
3905         {
3906             float const r = t0 / (t0 - t1);
3907             dpxy[n] = { (dp2[j].x - dp2[i].x) * r + dp2[i].x,
3908                         (dp2[j].y - dp2[i].y) * r + dp2[i].y };
3909             n++;
3910         }
3911     }
3912 
3913     if (n < 3)
3914     {
3915         n = 0;
3916         return;
3917     }
3918 }
3919 
polymost_domost(float x0,float y0,float x1,float y1,float y0top=0.f,float y0bot=-1.f,float y1top=0.f,float y1bot=-1.f)3920 static void polymost_domost(float x0, float y0, float x1, float y1, float y0top = 0.f, float y0bot = -1.f, float y1top = 0.f, float y1bot = -1.f)
3921 {
3922     int const dir = (x0 < x1);
3923 
3924     polymost_outputGLDebugMessage(3, "polymost_domost(x0:%f, y0:%f, x1:%f, y1:%f, y0top:%f, y0bot:%f, y1top:%f, y1bot:%f)",
3925                                   x0, y0, x1, y1, y0top, y0bot, y1top, y1bot);
3926 
3927     y0top -= DOMOST_OFFSET;
3928     y1top -= DOMOST_OFFSET;
3929     y0bot += DOMOST_OFFSET;
3930     y1bot += DOMOST_OFFSET;
3931 
3932     if (dir) //clip dmost (floor)
3933     {
3934         y0 -= DOMOST_OFFSET;
3935         y1 -= DOMOST_OFFSET;
3936     }
3937     else //clip umost (ceiling)
3938     {
3939         if (x0 == x1) return;
3940         swapfloat(&x0, &x1);
3941         swapfloat(&y0, &y1);
3942         swapfloat(&y0top, &y1top);
3943         swapfloat(&y0bot, &y1bot);
3944         y0 += DOMOST_OFFSET;
3945         y1 += DOMOST_OFFSET; //necessary?
3946     }
3947 
3948     // Test if span is outside screen bounds
3949     if (x1 < xbl || x0 > xbr)
3950     {
3951         domost_rejectcount++;
3952         return;
3953     }
3954 
3955     vec2f_t dm0 = { x0 - DOMOST_OFFSET, y0 };
3956     vec2f_t dm1 = { x1 + DOMOST_OFFSET, y1 };
3957 
3958     float const slop = (dm1.y - dm0.y) / (dm1.x - dm0.x);
3959 
3960     if (dm0.x < xbl)
3961     {
3962         dm0.y += slop*(xbl-dm0.x);
3963         dm0.x = xbl;
3964     }
3965 
3966     if (dm1.x > xbr)
3967     {
3968         dm1.y += slop*(xbr-dm1.x);
3969         dm1.x = xbr;
3970     }
3971 
3972     drawpoly_alpha = 0.f;
3973     drawpoly_blend = 0;
3974 
3975     vec2f_t n0, n1;
3976     float spx[4];
3977     int32_t  spt[4];
3978     int firstnode = vsp[0].n;
3979 
3980     for (bssize_t newi, i=vsp[0].n; i; i=newi)
3981     {
3982         newi = vsp[i].n; n0.x = vsp[i].x; n1.x = vsp[newi].x;
3983 
3984         if (dm0.x >= n1.x)
3985         {
3986             firstnode = i;
3987             continue;
3988         }
3989 
3990         if (n0.x >= dm1.x)
3991             break;
3992 
3993         if (vsp[i].ctag <= 0) continue;
3994 
3995         float const dx = n1.x-n0.x;
3996         float const cy[2] = { vsp[i].cy[0], vsp[i].fy[0] },
3997                     cv[2] = { vsp[i].cy[1]-cy[0], vsp[i].fy[1]-cy[1] };
3998 
3999         int scnt = 0;
4000 
4001         //Test if left edge requires split (dm0.x,dm0.y) (nx0,cy(0)),<dx,cv(0)>
4002         if ((dm0.x > n0.x) && (dm0.x < n1.x))
4003         {
4004             float const t = (dm0.x-n0.x)*cv[dir] - (dm0.y-cy[dir])*dx;
4005             if (((!dir) && (t < 0.f)) || ((dir) && (t > 0.f)))
4006                 { spx[scnt] = dm0.x; spt[scnt] = -1; scnt++; }
4007         }
4008 
4009         //Test for intersection on umost (0) and dmost (1)
4010 
4011         float const d[2] = { ((dm0.y - dm1.y) * dx) - ((dm0.x - dm1.x) * cv[0]),
4012                              ((dm0.y - dm1.y) * dx) - ((dm0.x - dm1.x) * cv[1]) };
4013 
4014         float const n[2] = { ((dm0.y - cy[0]) * dx) - ((dm0.x - n0.x) * cv[0]),
4015                              ((dm0.y - cy[1]) * dx) - ((dm0.x - n0.x) * cv[1]) };
4016 
4017         float const fnx[2] = { dm0.x + ((n[0] / d[0]) * (dm1.x - dm0.x)),
4018                                dm0.x + ((n[1] / d[1]) * (dm1.x - dm0.x)) };
4019 
4020         if ((Bfabsf(d[0]) > Bfabsf(n[0])) && (d[0] * n[0] >= 0.f) && (fnx[0] > n0.x) && (fnx[0] < n1.x))
4021             spx[scnt] = fnx[0], spt[scnt++] = 0;
4022 
4023         if ((Bfabsf(d[1]) > Bfabsf(n[1])) && (d[1] * n[1] >= 0.f) && (fnx[1] > n0.x) && (fnx[1] < n1.x))
4024             spx[scnt] = fnx[1], spt[scnt++] = 1;
4025 
4026         //Nice hack to avoid full sort later :)
4027         if ((scnt >= 2) && (spx[scnt-1] < spx[scnt-2]))
4028         {
4029             swapfloat(&spx[scnt-1], &spx[scnt-2]);
4030             swaplong(&spt[scnt-1], &spt[scnt-2]);
4031         }
4032 
4033         //Test if right edge requires split
4034         if ((dm1.x > n0.x) && (dm1.x < n1.x))
4035         {
4036             float const t = (dm1.x-n0.x)*cv[dir] - (dm1.y-cy[dir])*dx;
4037             if (((!dir) && (t < 0.f)) || ((dir) && (t > 0.f)))
4038                 { spx[scnt] = dm1.x; spt[scnt] = -1; scnt++; }
4039         }
4040 
4041         vsp[i].tag = vsp[newi].tag = -1;
4042 
4043         float const rdx = 1.f/dx;
4044 
4045         for (bssize_t z=0, vcnt=0; z<=scnt; z++,i=vcnt)
4046         {
4047             float t;
4048 
4049             if (z == scnt)
4050                 goto skip;
4051 
4052             t = (spx[z]-n0.x)*rdx;
4053             vcnt = vsinsaft(i);
4054             vsp[i].cy[1] = t*cv[0] + cy[0];
4055             vsp[i].fy[1] = t*cv[1] + cy[1];
4056             vsp[vcnt].x = spx[z];
4057             vsp[vcnt].cy[0] = vsp[i].cy[1];
4058             vsp[vcnt].fy[0] = vsp[i].fy[1];
4059             vsp[vcnt].tag = spt[z];
4060 
4061 skip: ;
4062             int32_t const ni = vsp[i].n; if (!ni) continue; //this 'if' fixes many bugs!
4063             float const dx0 = vsp[i].x; if (dm0.x > dx0) continue;
4064             float const dx1 = vsp[ni].x; if (dm1.x < dx1) continue;
4065             n0.y = (dx0-dm0.x)*slop + dm0.y;
4066             n1.y = (dx1-dm0.x)*slop + dm0.y;
4067 
4068             //      dx0           dx1
4069             //       ~             ~
4070             //----------------------------
4071             //     t0+=0         t1+=0
4072             //   vsp[i].cy[0]  vsp[i].cy[1]
4073             //============================
4074             //     t0+=1         t1+=3
4075             //============================
4076             //   vsp[i].fy[0]    vsp[i].fy[1]
4077             //     t0+=2         t1+=6
4078             //
4079             //     ny0 ?         ny1 ?
4080 
4081             int k = 4;
4082 
4083             if ((vsp[i].tag == 0) || (n0.y <= vsp[i].cy[0]+DOMOST_OFFSET)) k--;
4084             if ((vsp[i].tag == 1) || (n0.y >= vsp[i].fy[0]-DOMOST_OFFSET)) k++;
4085             if ((vsp[ni].tag == 0) || (n1.y <= vsp[i].cy[1]+DOMOST_OFFSET)) k -= 3;
4086             if ((vsp[ni].tag == 1) || (n1.y >= vsp[i].fy[1]-DOMOST_OFFSET)) k += 3;
4087 
4088 #if 0
4089             //POGO: This GL1 debug code draws a green line that represents the new line, and the current VSP floor & ceil as red and blue respectively.
4090             //      To enable this, ensure that in polymost_drawrooms() that you are clearing the stencil buffer and color buffer.
4091             //      Additionally, disable any calls to glColor4f in polymost_drawpoly and disable culling triangles with area==0/removing duplicate points
4092             //      If you don't want any lines showing up from mirrors/skyboxes, be sure to disable them as well.
4093             glEnable(GL_STENCIL_TEST);
4094             glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
4095             glStencilFunc(GL_ALWAYS, 1, 0xFF);
4096             glDisable(GL_DEPTH_TEST);
4097             polymost_useColorOnly(true);
4098             glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
4099 
4100             glColor4f(0.f, 1.f, 0.f, 1.f);
4101             vec2f_t nline[3] = {{dx0, n0.y}, {dx1, n1.y}, {dx0, n0.y}};
4102             polymost_drawpoly(nline, 3, domostpolymethod);
4103 
4104             glColor4f(1.f, 0.f, 0.f, 1.f);
4105             vec2f_t floor[3] = {{vsp[i].x, vsp[i].fy[0]}, {vsp[ni].x, vsp[i].fy[1]}, {vsp[i].x, vsp[i].fy[0]}};
4106             polymost_drawpoly(floor, 3, domostpolymethod);
4107 
4108             glColor4f(0.f, 0.f, 1.f, 1.f);
4109             vec2f_t ceil[3] = {{vsp[i].x, vsp[i].cy[0]}, {vsp[ni].x, vsp[i].cy[1]}, {vsp[i].x, vsp[i].cy[0]}};
4110             polymost_drawpoly(ceil, 3, domostpolymethod);
4111 
4112             glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
4113             polymost_useColorOnly(false);
4114             glEnable(GL_DEPTH_TEST);
4115             glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
4116             glStencilFunc(GL_EQUAL, 0, 0xFF);
4117             glColor4f(1.f, 1.f, 1.f, 1.f);
4118 #endif
4119 
4120             if (!dir)
4121             {
4122                 switch (k)
4123                 {
4124                     case 4:
4125                     case 5:
4126                     case 7:
4127                     {
4128                         vec2f_t dpxy[8] = {
4129                             { dx0, vsp[i].cy[0] }, { dx1, vsp[i].cy[1] }, { dx1, n1.y }, { dx0, n0.y }
4130                         };
4131 
4132                         int n = 4;
4133                         polymost_clipmost(dpxy, n, x0, x1, y0top, y0bot, y1top, y1bot);
4134 #ifdef YAX_ENABLE
4135                         if (g_nodraw)
4136                         {
4137                             if (yax_drawcf != -1)
4138                                 yax_holecf[yax_drawcf][yax_holencf[yax_drawcf]++] = { dx0, dx1, vsp[i].cy[0], vsp[i].cy[1], n0.y, n1.y };
4139 
4140                             if (editstatus && doeditorcheck)
4141                                 polymost_polyeditorfunc(dpxy, n);
4142                         }
4143                         else
4144 #endif
4145                             polymost_drawpoly(dpxy, n, domostpolymethod);
4146 
4147                         vsp[i].cy[0] = n0.y;
4148                         vsp[i].cy[1] = n1.y;
4149                         vsp[i].ctag = gtag;
4150                     }
4151                     break;
4152                     case 1:
4153                     case 2:
4154                     {
4155                         vec2f_t dpxy[8] = { { dx0, vsp[i].cy[0] }, { dx1, vsp[i].cy[1] }, { dx0, n0.y } };
4156 
4157                         int n = 3;
4158                         polymost_clipmost(dpxy, n, x0, x1, y0top, y0bot, y1top, y1bot);
4159 #ifdef YAX_ENABLE
4160                         if (g_nodraw)
4161                         {
4162                             if (yax_drawcf != -1)
4163                                 yax_holecf[yax_drawcf][yax_holencf[yax_drawcf]++] = { dx0, dx1, vsp[i].cy[0], vsp[i].cy[1], n0.y, vsp[i].cy[1] };
4164 
4165                             if (editstatus && doeditorcheck)
4166                                 polymost_polyeditorfunc(dpxy, n);
4167                         }
4168                         else
4169 #endif
4170                             polymost_drawpoly(dpxy, n, domostpolymethod);
4171 
4172                         vsp[i].cy[0] = n0.y;
4173                         vsp[i].ctag = gtag;
4174                     }
4175                     break;
4176                     case 3:
4177                     case 6:
4178                     {
4179                         vec2f_t dpxy[8] = { { dx0, vsp[i].cy[0] }, { dx1, vsp[i].cy[1] }, { dx1, n1.y } };
4180 
4181                         int n = 3;
4182                         polymost_clipmost(dpxy, n, x0, x1, y0top, y0bot, y1top, y1bot);
4183 #ifdef YAX_ENABLE
4184                         if (g_nodraw)
4185                         {
4186                             if (yax_drawcf != -1)
4187                                 yax_holecf[yax_drawcf][yax_holencf[yax_drawcf]++] = { dx0, dx1, vsp[i].cy[0], vsp[i].cy[1], vsp[i].cy[0], n1.y };
4188 
4189                             if (editstatus && doeditorcheck)
4190                                 polymost_polyeditorfunc(dpxy, n);
4191                         }
4192                         else
4193 #endif
4194                             polymost_drawpoly(dpxy, n, domostpolymethod);
4195 
4196                         vsp[i].cy[1] = n1.y;
4197                         vsp[i].ctag = gtag;
4198                     }
4199                     break;
4200                     case 8:
4201                     {
4202                         vec2f_t dpxy[8] = {
4203                             { dx0, vsp[i].cy[0] }, { dx1, vsp[i].cy[1] }, { dx1, vsp[i].fy[1] }, { dx0, vsp[i].fy[0] }
4204                         };
4205 
4206                         int n = 4;
4207                         polymost_clipmost(dpxy, n, x0, x1, y0top, y0bot, y1top, y1bot);
4208 #ifdef YAX_ENABLE
4209                         if (g_nodraw)
4210                         {
4211                             if (yax_drawcf != -1)
4212                                 yax_holecf[yax_drawcf][yax_holencf[yax_drawcf]++] = { dx0, dx1, vsp[i].cy[0], vsp[i].cy[1], vsp[i].fy[0], vsp[i].fy[1] };
4213 
4214                             if (editstatus && doeditorcheck)
4215                                 polymost_polyeditorfunc(dpxy, n);
4216                         }
4217                         else
4218 #endif
4219                             polymost_drawpoly(dpxy, n, domostpolymethod);
4220 
4221                         vsp[i].ctag = vsp[i].ftag = -1;
4222                     }
4223                     default: break;
4224                 }
4225             }
4226             else
4227             {
4228                 switch (k)
4229                 {
4230                 case 4:
4231                 case 3:
4232                 case 1:
4233                 {
4234                     vec2f_t dpxy[8] = {
4235                         { dx0, n0.y }, { dx1, n1.y }, { dx1, vsp[i].fy[1] }, { dx0, vsp[i].fy[0] }
4236                     };
4237 
4238                     int n = 4;
4239                     polymost_clipmost(dpxy, n, x0, x1, y0top, y0bot, y1top, y1bot);
4240 #ifdef YAX_ENABLE
4241                     if (g_nodraw)
4242                     {
4243                         if (yax_drawcf != -1)
4244                             yax_holecf[yax_drawcf][yax_holencf[yax_drawcf]++] = { dx0, dx1, n0.y, n1.y, vsp[i].fy[0], vsp[i].fy[1] };
4245 
4246                         if (editstatus && doeditorcheck)
4247                             polymost_polyeditorfunc(dpxy, n);
4248                     }
4249                     else
4250 #endif
4251                         polymost_drawpoly(dpxy, n, domostpolymethod);
4252 
4253                     vsp[i].fy[0] = n0.y;
4254                     vsp[i].fy[1] = n1.y;
4255                     vsp[i].ftag = gtag;
4256                 }
4257                     break;
4258                 case 7:
4259                 case 6:
4260                 {
4261                     vec2f_t dpxy[8] = { { dx0, n0.y }, { dx1, vsp[i].fy[1] }, { dx0, vsp[i].fy[0] } };
4262 
4263                     int n = 3;
4264                     polymost_clipmost(dpxy, n, x0, x1, y0top, y0bot, y1top, y1bot);
4265 #ifdef YAX_ENABLE
4266                     if (g_nodraw)
4267                     {
4268                         if (yax_drawcf != -1)
4269                             yax_holecf[yax_drawcf][yax_holencf[yax_drawcf]++] = { dx0, dx1, n0.y, vsp[i].fy[1], vsp[i].fy[0], vsp[i].fy[1] };
4270 
4271                         if (editstatus && doeditorcheck)
4272                             polymost_polyeditorfunc(dpxy, n);
4273                     }
4274                     else
4275 #endif
4276                         polymost_drawpoly(dpxy, n, domostpolymethod);
4277 
4278                     vsp[i].fy[0] = n0.y;
4279                     vsp[i].ftag = gtag;
4280                 }
4281                     break;
4282                 case 5:
4283                 case 2:
4284                 {
4285                     vec2f_t dpxy[8] = { { dx0, vsp[i].fy[0] }, { dx1, n1.y }, { dx1, vsp[i].fy[1] } };
4286 
4287                     int n = 3;
4288                     polymost_clipmost(dpxy, n, x0, x1, y0top, y0bot, y1top, y1bot);
4289 #ifdef YAX_ENABLE
4290                     if (g_nodraw)
4291                     {
4292                         if (yax_drawcf != -1)
4293                             yax_holecf[yax_drawcf][yax_holencf[yax_drawcf]++] = { dx0, dx1, vsp[i].fy[0], n1.y, vsp[i].fy[0], vsp[i].fy[1] };
4294 
4295                         if (editstatus && doeditorcheck)
4296                             polymost_polyeditorfunc(dpxy, n);
4297                     }
4298                     else
4299 #endif
4300                         polymost_drawpoly(dpxy, n, domostpolymethod);
4301 
4302                     vsp[i].fy[1] = n1.y;
4303                     vsp[i].ftag = gtag;
4304                 }
4305                     break;
4306                 case 0:
4307                 {
4308                     vec2f_t dpxy[8] = { { dx0, vsp[i].cy[0] }, { dx1, vsp[i].cy[1] }, { dx1, vsp[i].fy[1] }, { dx0, vsp[i].fy[0] } };
4309 
4310                     int n = 4;
4311                     polymost_clipmost(dpxy, n, x0, x1, y0top, y0bot, y1top, y1bot);
4312 #ifdef YAX_ENABLE
4313                     if (g_nodraw)
4314                     {
4315                         if (yax_drawcf != -1)
4316                             yax_holecf[yax_drawcf][yax_holencf[yax_drawcf]++] = { dx0, dx1, vsp[i].cy[0], vsp[i].cy[1], vsp[i].fy[0], vsp[i].fy[1] };
4317 
4318                         if (editstatus && doeditorcheck)
4319                             polymost_polyeditorfunc(dpxy, n);
4320                     }
4321                     else
4322 #endif
4323                         polymost_drawpoly(dpxy, n, domostpolymethod);
4324 
4325                     vsp[i].ctag = vsp[i].ftag = -1;
4326                 }
4327                 default:
4328                     break;
4329                 }
4330             }
4331         }
4332     }
4333 
4334     gtag++;
4335 
4336     //Combine neighboring vertical strips with matching collinear top&bottom edges
4337     //This prevents x-splits from propagating through the entire scan
4338 #ifdef COMBINE_STRIPS
4339     int i = firstnode;
4340 
4341     do
4342     {
4343         if (vsp[i].x >= dm1.x)
4344             break;
4345 
4346         if ((vsp[i].cy[0]+DOMOST_OFFSET*2 >= vsp[i].fy[0]) && (vsp[i].cy[1]+DOMOST_OFFSET*2 >= vsp[i].fy[1]))
4347             vsp[i].ctag = vsp[i].ftag = -1;
4348 
4349         int const ni = vsp[i].n;
4350 
4351         //POGO: specially treat the viewport nodes so that we will never end up in a situation where we accidentally access the sentinel node
4352         if (ni >= viewportNodeCount)
4353         {
4354             if ((vsp[i].ctag == vsp[ni].ctag) && (vsp[i].ftag == vsp[ni].ftag))
4355             {
4356                 vsmerge(i, ni);
4357 #if 0
4358                 //POGO: This GL1 debug code draws the resulting merged VSP segment with floor and ceiling bounds lines as yellow and cyan respectively
4359                 //      To enable this, ensure that in polymost_drawrooms() that you are clearing the stencil buffer and color buffer.
4360                 //      Additionally, disable any calls to glColor4f in polymost_drawpoly and disable culling triangles with area==0
4361                 //      If you don't want any lines showing up from mirrors/skyboxes, be sure to disable them as well.
4362                 glEnable(GL_STENCIL_TEST);
4363                 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
4364                 glStencilFunc(GL_ALWAYS, 1, 0xFF);
4365                 glDisable(GL_DEPTH_TEST);
4366                 polymost_useColorOnly(true);
4367                 glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
4368 
4369                 glColor4f(1.f, 1.f, 0.f, 1.f);
4370                 vec2f_t dfloor[3] = {{vsp[i].x, vsp[i].fy[0]}, {vsp[vsp[i].n].x, vsp[i].fy[1]}, {vsp[i].x, vsp[i].fy[0]}};
4371                 polymost_drawpoly(dfloor, 3, domostpolymethod);
4372 
4373                 glColor4f(0.f, 1.f, 1.f, 1.f);
4374                 vec2f_t dceil[3] = {{vsp[i].x, vsp[i].cy[0]}, {vsp[vsp[i].n].x, vsp[i].cy[1]}, {vsp[i].x, vsp[i].cy[0]}};
4375                 polymost_drawpoly(dceil, 3, domostpolymethod);
4376 
4377                 glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
4378                 polymost_useColorOnly(false);
4379                 glEnable(GL_DEPTH_TEST);
4380                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
4381                 glStencilFunc(GL_EQUAL, 0, 0xFF);
4382                 glColor4f(1.f, 1.f, 1.f, 1.f);
4383 #endif
4384                 continue;
4385             }
4386             if (vsp[ni].x - vsp[i].x < DOMOST_OFFSET)
4387             {
4388                 vsp[i].x = vsp[ni].x;
4389                 vsp[i].cy[0] = vsp[ni].cy[0];
4390                 vsp[i].fy[0] = vsp[ni].fy[0];
4391                 vsp[i].ctag = vsp[ni].ctag;
4392                 vsp[i].ftag = vsp[ni].ftag;
4393                 vsmerge(i, ni);
4394                 continue;
4395             }
4396         }
4397         i = ni;
4398     }
4399     while (i);
4400 #endif
4401 }
4402 
4403 #ifdef YAX_ENABLE
yax_polymost_domost(const int yaxbunch,float x0,float y0,float x1,float y1)4404 static void yax_polymost_domost(const int yaxbunch, float x0, float y0, float x1, float y1)
4405 {
4406     int const dir = (x0 < x1);
4407 
4408     if (dir) //clip dmost (floor)
4409     {
4410         y0 -= DOMOST_OFFSET;
4411         y1 -= DOMOST_OFFSET;
4412     }
4413     else //clip umost (ceiling)
4414     {
4415         if (x0 == x1) return;
4416         swapfloat(&x0, &x1);
4417         swapfloat(&y0, &y1);
4418         y0 += DOMOST_OFFSET;
4419         y1 += DOMOST_OFFSET; //necessary?
4420     }
4421 
4422     // Test if span is outside screen bounds
4423     if (x1 < xbl || x0 > xbr)
4424     {
4425         domost_rejectcount++;
4426         return;
4427     }
4428 
4429     vec2f_t dm0 = { x0, y0 };
4430     vec2f_t dm1 = { x1, y1 };
4431 
4432     float const slop = (dm1.y - dm0.y) / (dm1.x - dm0.x);
4433 
4434     if (dm0.x < xbl)
4435     {
4436         dm0.y += slop*(xbl-dm0.x);
4437         dm0.x = xbl;
4438     }
4439 
4440     if (dm1.x > xbr)
4441     {
4442         dm1.y += slop*(xbr-dm1.x);
4443         dm1.x = xbr;
4444     }
4445 
4446     vec2f_t n0, n1;
4447     float spx[4];
4448     int32_t  spt[4];
4449 
4450     for (bssize_t newi, i=yax_vsp[yaxbunch][0].n; i; i=newi)
4451     {
4452         newi = yax_vsp[yaxbunch][i].n; n0.x = yax_vsp[yaxbunch][i].x; n1.x = yax_vsp[yaxbunch][newi].x;
4453 
4454         if ((dm0.x >= n1.x) || (n0.x >= dm1.x) || (yax_vsp[yaxbunch][i].ctag <= 0)) continue;
4455 
4456         double const dx = double(n1.x)-double(n0.x);
4457         double const cy = yax_vsp[yaxbunch][i].cy[0],
4458                      cv = yax_vsp[yaxbunch][i].cy[1]-cy;
4459 
4460         int scnt = 0;
4461 
4462         //Test if left edge requires split (dm0.x,dm0.y) (nx0,cy(0)),<dx,cv(0)>
4463         if ((dm0.x > n0.x) && (dm0.x < n1.x))
4464         {
4465             double const t = (dm0.x-n0.x)*cv - (dm0.y-cy)*dx;
4466             if (((!dir) && (t <= 0.0)) || ((dir) && (t >= 0.0)))
4467                 { spx[scnt] = dm0.x; spt[scnt] = -1; scnt++; }
4468         }
4469 
4470         //Test for intersection on umost (0) and dmost (1)
4471 
4472         double const d = ((double(dm0.y) - double(dm1.y)) * dx) - ((double(dm0.x) - double(dm1.x)) * cv);
4473 
4474         double const n = ((double(dm0.y) - cy) * dx) - ((double(dm0.x) - double(n0.x)) * cv);
4475 
4476         double const fnx = double(dm0.x) + ((n / d) * (double(dm1.x) - double(dm0.x)));
4477 
4478         if ((fabs(d) > fabs(n)) && (d * n >= 0.0) && (fnx > n0.x) && (fnx < n1.x))
4479             spx[scnt] = fnx, spt[scnt++] = 0;
4480 
4481         //Nice hack to avoid full sort later :)
4482         if ((scnt >= 2) && (spx[scnt-1] < spx[scnt-2]))
4483         {
4484             swapfloat(&spx[scnt-1], &spx[scnt-2]);
4485             swaplong(&spt[scnt-1], &spt[scnt-2]);
4486         }
4487 
4488         //Test if right edge requires split
4489         if ((dm1.x > n0.x) && (dm1.x < n1.x))
4490         {
4491             double const t = (double(dm1.x)- double(n0.x))*cv - (double(dm1.y)- double(cy))*dx;
4492             if (((!dir) && (t <= 0.0)) || ((dir) && (t >= 0.0)))
4493                 { spx[scnt] = dm1.x; spt[scnt] = -1; scnt++; }
4494         }
4495 
4496         yax_vsp[yaxbunch][i].tag = yax_vsp[yaxbunch][newi].tag = -1;
4497 
4498         float const rdx = 1.f/dx;
4499 
4500         for (bssize_t z=0, vcnt=0; z<=scnt; z++,i=vcnt)
4501         {
4502             float t;
4503 
4504             if (z == scnt)
4505                 goto skip;
4506 
4507             t = (spx[z]-n0.x)*rdx;
4508             vcnt = yax_vsinsaft(yaxbunch, i);
4509             yax_vsp[yaxbunch][i].cy[1] = t*cv + cy;
4510             yax_vsp[yaxbunch][vcnt].x = spx[z];
4511             yax_vsp[yaxbunch][vcnt].cy[0] = yax_vsp[yaxbunch][i].cy[1];
4512             yax_vsp[yaxbunch][vcnt].tag = spt[z];
4513 
4514 skip: ;
4515             int32_t const ni = yax_vsp[yaxbunch][i].n; if (!ni) continue; //this 'if' fixes many bugs!
4516             float const dx0 = yax_vsp[yaxbunch][i].x; if (dm0.x > dx0) continue;
4517             float const dx1 = yax_vsp[yaxbunch][ni].x; if (dm1.x < dx1) continue;
4518             n0.y = (dx0-dm0.x)*slop + dm0.y;
4519             n1.y = (dx1-dm0.x)*slop + dm0.y;
4520 
4521             //      dx0           dx1
4522             //       ~             ~
4523             //----------------------------
4524             //     t0+=0         t1+=0
4525             //   vsp[i].cy[0]  vsp[i].cy[1]
4526             //============================
4527             //     t0+=1         t1+=3
4528             //============================
4529             //   vsp[i].fy[0]    vsp[i].fy[1]
4530             //     t0+=2         t1+=6
4531             //
4532             //     ny0 ?         ny1 ?
4533 
4534             int k = 4;
4535 
4536             if (!dir)
4537             {
4538                 if ((yax_vsp[yaxbunch][i].tag == 0) || (n0.y <= yax_vsp[yaxbunch][i].cy[0]+DOMOST_OFFSET)) k--;
4539                 if ((yax_vsp[yaxbunch][ni].tag == 0) || (n1.y <= yax_vsp[yaxbunch][i].cy[1]+DOMOST_OFFSET)) k -= 3;
4540                 switch (k)
4541                 {
4542                     case 4:
4543                     {
4544                         yax_vsp[yaxbunch][i].cy[0] = n0.y;
4545                         yax_vsp[yaxbunch][i].cy[1] = n1.y;
4546                         yax_vsp[yaxbunch][i].ctag = gtag;
4547                     }
4548                     break;
4549                     case 1:
4550                     case 2:
4551                     {
4552                         yax_vsp[yaxbunch][i].cy[0] = n0.y;
4553                         yax_vsp[yaxbunch][i].ctag = gtag;
4554                     }
4555                     break;
4556                     case 3:
4557                     {
4558                         yax_vsp[yaxbunch][i].cy[1] = n1.y;
4559                         yax_vsp[yaxbunch][i].ctag = gtag;
4560                     }
4561                     break;
4562                     default: break;
4563                 }
4564             }
4565             else
4566             {
4567                 if ((yax_vsp[yaxbunch][i].tag == 0) || (n0.y >= yax_vsp[yaxbunch][i].cy[0]-DOMOST_OFFSET)) k++;
4568                 if ((yax_vsp[yaxbunch][ni].tag == 0) || (n1.y >= yax_vsp[yaxbunch][i].cy[1]-DOMOST_OFFSET)) k += 3;
4569                 switch (k)
4570                 {
4571                 case 4:
4572                 {
4573                     yax_vsp[yaxbunch][i].cy[0] = n0.y;
4574                     yax_vsp[yaxbunch][i].cy[1] = n1.y;
4575                     yax_vsp[yaxbunch][i].ctag = gtag;
4576                 }
4577                     break;
4578                 case 7:
4579                 case 6:
4580                 {
4581                     yax_vsp[yaxbunch][i].cy[0] = n0.y;
4582                     yax_vsp[yaxbunch][i].ctag = gtag;
4583                 }
4584                     break;
4585                 case 5:
4586                 {
4587                     yax_vsp[yaxbunch][i].cy[1] = n1.y;
4588                     yax_vsp[yaxbunch][i].ctag = gtag;
4589                 }
4590                     break;
4591                 default:
4592                     break;
4593                 }
4594             }
4595         }
4596     }
4597 
4598     gtag++;
4599 
4600     //Combine neighboring vertical strips with matching collinear top&bottom edges
4601     //This prevents x-splits from propagating through the entire scan
4602 #ifdef COMBINE_STRIPS
4603     int i = yax_vsp[yaxbunch][0].n;
4604 
4605     do
4606     {
4607         int const ni = yax_vsp[yaxbunch][i].n;
4608 
4609         if ((yax_vsp[yaxbunch][i].ctag == yax_vsp[yaxbunch][ni].ctag))
4610         {
4611             yax_vsp[yaxbunch][i].cy[1] = yax_vsp[yaxbunch][ni].cy[1];
4612             yax_vsdel(yaxbunch, ni);
4613         }
4614         else i = ni;
4615     }
4616     while (i);
4617 #endif
4618 }
4619 
should_clip_cfwall(float x0,float y0,float x1,float y1)4620 static int32_t should_clip_cfwall(float x0, float y0, float x1, float y1)
4621 {
4622     int const dir = (x0 < x1);
4623 
4624     if (dir && yax_globallev >= YAX_MAXDRAWS)
4625         return 1;
4626 
4627     if (!dir && yax_globallev <= YAX_MAXDRAWS)
4628         return 1;
4629 
4630     if (dir) //clip dmost (floor)
4631     {
4632         y0 -= DOMOST_OFFSET;
4633         y1 -= DOMOST_OFFSET;
4634     }
4635     else //clip umost (ceiling)
4636     {
4637         if (x0 == x1) return 1;
4638         swapfloat(&x0, &x1);
4639         swapfloat(&y0, &y1);
4640         y0 += DOMOST_OFFSET;
4641         y1 += DOMOST_OFFSET; //necessary?
4642     }
4643 
4644     x0 -= DOMOST_OFFSET;
4645     x1 += DOMOST_OFFSET;
4646 
4647     // Test if span is outside screen bounds
4648     if (x1 < xbl || x0 > xbr)
4649         return 1;
4650 
4651     vec2f_t dm0 = { x0, y0 };
4652     vec2f_t dm1 = { x1, y1 };
4653 
4654     float const slop = (dm1.y - dm0.y) / (dm1.x - dm0.x);
4655 
4656     if (dm0.x < xbl)
4657     {
4658         dm0.y += slop*(xbl-dm0.x);
4659         dm0.x = xbl;
4660     }
4661 
4662     if (dm1.x > xbr)
4663     {
4664         dm1.y += slop*(xbr-dm1.x);
4665         dm1.x = xbr;
4666     }
4667 
4668     vec2f_t n0, n1;
4669     float spx[6], spcy[6], spfy[6];
4670     int32_t spt[6];
4671 
4672     for (bssize_t newi, i=vsp[0].n; i; i=newi)
4673     {
4674         newi = vsp[i].n; n0.x = vsp[i].x; n1.x = vsp[newi].x;
4675 
4676         if ((dm0.x >= n1.x) || (n0.x >= dm1.x) || (vsp[i].ctag <= 0)) continue;
4677 
4678         float const dx = n1.x-n0.x;
4679         float const cy[2] = { vsp[i].cy[0], vsp[i].fy[0] },
4680                     cv[2] = { vsp[i].cy[1]-cy[0], vsp[i].fy[1]-cy[1] };
4681 
4682         int scnt = 0;
4683 
4684         spx[scnt] = n0.x; spt[scnt] = -1; scnt++;
4685 
4686         //Test if left edge requires split (dm0.x,dm0.y) (nx0,cy(0)),<dx,cv(0)>
4687         if ((dm0.x > n0.x) && (dm0.x < n1.x))
4688         {
4689             float const t = (dm0.x-n0.x)*cv[dir] - (dm0.y-cy[dir])*dx;
4690             if (((!dir) && (t < 0.f)) || ((dir) && (t > 0.f)))
4691                 { spx[scnt] = dm0.x; spt[scnt] = -1; scnt++; }
4692         }
4693 
4694         //Test for intersection on umost (0) and dmost (1)
4695 
4696         float const d[2] = { ((dm0.y - dm1.y) * dx) - ((dm0.x - dm1.x) * cv[0]),
4697                              ((dm0.y - dm1.y) * dx) - ((dm0.x - dm1.x) * cv[1]) };
4698 
4699         float const n[2] = { ((dm0.y - cy[0]) * dx) - ((dm0.x - n0.x) * cv[0]),
4700                              ((dm0.y - cy[1]) * dx) - ((dm0.x - n0.x) * cv[1]) };
4701 
4702         float const fnx[2] = { dm0.x + ((n[0] / d[0]) * (dm1.x - dm0.x)),
4703                                dm0.x + ((n[1] / d[1]) * (dm1.x - dm0.x)) };
4704 
4705         if ((Bfabsf(d[0]) > Bfabsf(n[0])) && (d[0] * n[0] >= 0.f) && (fnx[0] > n0.x) && (fnx[0] < n1.x))
4706             spx[scnt] = fnx[0], spt[scnt++] = 0;
4707 
4708         if ((Bfabsf(d[1]) > Bfabsf(n[1])) && (d[1] * n[1] >= 0.f) && (fnx[1] > n0.x) && (fnx[1] < n1.x))
4709             spx[scnt] = fnx[1], spt[scnt++] = 1;
4710 
4711         //Nice hack to avoid full sort later :)
4712         if ((scnt >= 2) && (spx[scnt-1] < spx[scnt-2]))
4713         {
4714             swapfloat(&spx[scnt-1], &spx[scnt-2]);
4715             swaplong(&spx[scnt-1], &spx[scnt-2]);
4716         }
4717 
4718         //Test if right edge requires split
4719         if ((dm1.x > n0.x) && (dm1.x < n1.x))
4720         {
4721             float const t = (dm1.x-n0.x)*cv[dir] - (dm1.y-cy[dir])*dx;
4722             if (((!dir) && (t < 0.f)) || ((dir) && (t > 0.f)))
4723                 { spx[scnt] = dm1.x; spt[scnt] = -1; scnt++; }
4724         }
4725 
4726         spx[scnt] = n1.x; spt[scnt] = -1; scnt++;
4727 
4728         float const rdx = 1.f/dx;
4729         for (bssize_t z=0; z<scnt; z++)
4730         {
4731             float const t = (spx[z]-n0.x)*rdx;
4732             spcy[z] = t*cv[0]+cy[0];
4733             spfy[z] = t*cv[1]+cy[1];
4734         }
4735 
4736         for (bssize_t z=0; z<scnt-1; z++)
4737         {
4738             float const dx0 = spx[z];
4739             float const dx1 = spx[z+1];
4740             n0.y = (dx0-dm0.x)*slop + dm0.y;
4741             n1.y = (dx1-dm0.x)*slop + dm0.y;
4742 
4743             //      dx0           dx1
4744             //       ~             ~
4745             //----------------------------
4746             //     t0+=0         t1+=0
4747             //   vsp[i].cy[0]  vsp[i].cy[1]
4748             //============================
4749             //     t0+=1         t1+=3
4750             //============================
4751             //   vsp[i].fy[0]    vsp[i].fy[1]
4752             //     t0+=2         t1+=6
4753             //
4754             //     ny0 ?         ny1 ?
4755 
4756             int k = 4;
4757             if (dir)
4758             {
4759                 if ((spt[z] == 0) || (n0.y <= spcy[z]+DOMOST_OFFSET)) k--;
4760                 if ((spt[z+1] == 0) || (n1.y <= spcy[z+1]+DOMOST_OFFSET)) k -= 3;
4761                 if (k != 0)
4762                     return 1;
4763             }
4764             else
4765             {
4766                 if ((spt[z] == 1) || (n0.y >= spfy[z]-DOMOST_OFFSET)) k++;
4767                 if ((spt[z+1] == 1) || (n1.y >= spfy[z+1]-DOMOST_OFFSET)) k += 3;
4768                 if (k != 8)
4769                     return 1;
4770             }
4771         }
4772     }
4773     return 0;
4774 }
4775 
4776 #endif
4777 
polymost_editorfunc(void)4778 void polymost_editorfunc(void)
4779 {
4780     const float ratio = r_usenewaspect ? (fxdim / fydim) / (320.f / 240.f) : 1.f;
4781 
4782     vec3f_t tvect = { 1.f,
4783                       (fsearchx-ghalfx)/ghalfx*ratio,
4784                       ((fsearchy-ghoriz)*16.f*(240.f/320.f))/ghoriz };
4785 
4786     //Standard Left/right rotation
4787     vec3_t v = { Blrintf(tvect.x * fcosglobalang - tvect.y * fsinglobalang),
4788                  Blrintf(tvect.x * fsinglobalang + tvect.y * fcosglobalang), Blrintf(tvect.z * 16384.f) };
4789 
4790     vec3_t vect = { globalposx, globalposy, globalposz };
4791 
4792     hitdata_t *hit = &polymost_hitdata;
4793 
4794     hitallsprites = 1;
4795 
4796     hitscan((const vec3_t *) &vect, globalcursectnum, //Start position
4797         v.x, v.y, v.z, hit, 0xffff0030);
4798 
4799     hitallsprites = 0;
4800 }
4801 
4802 
4803 // variables that are set to ceiling- or floor-members, depending
4804 // on which one is processed right now
4805 static int32_t global_cf_z;
4806 static float global_cf_xpanning, global_cf_ypanning, global_cf_heinum;
4807 static int32_t global_cf_shade, global_cf_pal, global_cf_fogpal;
4808 static float (*global_getzofslope_func)(usectorptr_t, float, float);
4809 
polymost_internal_nonparallaxed(vec2f_t n0,vec2f_t n1,float ryp0,float ryp1,float x0,float x1,float y0,float y1,int32_t sectnum)4810 static void polymost_internal_nonparallaxed(vec2f_t n0, vec2f_t n1, float ryp0, float ryp1, float x0, float x1,
4811                                             float y0, float y1, int32_t sectnum)
4812 {
4813     int const have_floor = sectnum & MAXSECTORS;
4814     sectnum &= ~MAXSECTORS;
4815     auto const sec = (usectorptr_t)&sector[sectnum];
4816 
4817     // comments from floor code:
4818             //(singlobalang/-16384*(sx-ghalfx) + 0*(sy-ghoriz) + (cosviewingrangeglobalang/16384)*ghalfx)*d + globalposx    = u*16
4819             //(cosglobalang/ 16384*(sx-ghalfx) + 0*(sy-ghoriz) + (sinviewingrangeglobalang/16384)*ghalfx)*d + globalposy    = v*16
4820             //(                  0*(sx-ghalfx) + 1*(sy-ghoriz) + (                             0)*ghalfx)*d + globalposz/16 = (sec->floorz/16)
4821 
4822     float ft[4] = { fglobalposx, fglobalposy, fcosglobalang, fsinglobalang };
4823 
4824     polymost_outputGLDebugMessage(3, "polymost_internal_nonparallaxed(n0:{x:%f, y:%f}, n1:{x:%f, y:%f}, ryp0:%f, ryp1:%f, x0:%f, x1:%f, y0:%f, y1:%f, sectnum:%d)",
4825                                   n0.x, n0.y, n1.x, n1.y, ryp0, ryp1, x0, x1, y0, y1, sectnum);
4826 
4827     if (globalorientation & 64)
4828     {
4829         //relative alignment
4830         vec2_t const xy = { wall[wall[sec->wallptr].point2].x - wall[sec->wallptr].x,
4831                             wall[wall[sec->wallptr].point2].y - wall[sec->wallptr].y };
4832         float r;
4833 
4834         if (globalorientation & 2)
4835         {
4836             int i = krecipasm(nsqrtasm(uhypsq(xy.x,xy.y)));
4837             r = i * (1.f/1073741824.f);
4838         }
4839         else
4840         {
4841             int i = nsqrtasm(uhypsq(xy.x,xy.y)); if (i == 0) i = 1024; else i = tabledivide32(1048576, i);
4842             r = i * (1.f/1048576.f);
4843         }
4844 
4845         vec2f_t const fxy = { xy.x*r, xy.y*r };
4846 
4847         ft[0] = ((float)(globalposx - wall[sec->wallptr].x)) * fxy.x + ((float)(globalposy - wall[sec->wallptr].y)) * fxy.y;
4848         ft[1] = ((float)(globalposy - wall[sec->wallptr].y)) * fxy.x - ((float)(globalposx - wall[sec->wallptr].x)) * fxy.y;
4849         ft[2] = fcosglobalang * fxy.x + fsinglobalang * fxy.y;
4850         ft[3] = fsinglobalang * fxy.x - fcosglobalang * fxy.y;
4851 
4852         globalorientation ^= (!(globalorientation & 4)) ? 32 : 16;
4853     }
4854 
4855     xtex.d = 0;
4856     ytex.d = gxyaspect;
4857 
4858     if (!(globalorientation&2) && global_cf_z-globalposz)  // PK 2012: don't allow div by zero
4859             ytex.d /= (double)(global_cf_z-globalposz);
4860 
4861     otex.d = -ghoriz * ytex.d;
4862 
4863     if (globalorientation & 8)
4864     {
4865         ft[0] *=  (1.f / 8.f);
4866         ft[1] *= -(1.f / 8.f);
4867         ft[2] *=  (1.f / 2097152.f);
4868         ft[3] *=  (1.f / 2097152.f);
4869     }
4870     else
4871     {
4872         ft[0] *=  (1.f / 16.f);
4873         ft[1] *= -(1.f / 16.f);
4874         ft[2] *=  (1.f / 4194304.f);
4875         ft[3] *=  (1.f / 4194304.f);
4876     }
4877 
4878     xtex.u = ft[3] * -(1.f / 65536.f) * (double)viewingrange;
4879     xtex.v = ft[2] * -(1.f / 65536.f) * (double)viewingrange;
4880     ytex.u = ft[0] * ytex.d;
4881     ytex.v = ft[1] * ytex.d;
4882     otex.u = ft[0] * otex.d;
4883     otex.v = ft[1] * otex.d;
4884     otex.u += (ft[2] - xtex.u) * ghalfx;
4885     otex.v -= (ft[3] + xtex.v) * ghalfx;
4886 
4887     //Texture flipping
4888     if (globalorientation&4)
4889     {
4890         swapdouble(&xtex.u, &xtex.v);
4891         swapdouble(&ytex.u, &ytex.v);
4892         swapdouble(&otex.u, &otex.v);
4893     }
4894 
4895     if (globalorientation&16) { xtex.u = -xtex.u; ytex.u = -ytex.u; otex.u = -otex.u; }
4896     if (globalorientation&32) { xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; }
4897 
4898     //Texture panning
4899     vec2f_t fxy = { global_cf_xpanning * ((float)(1 << (picsiz[globalpicnum] & 15))) * (1.0f / 256.f),
4900                     global_cf_ypanning * ((float)(1 << (picsiz[globalpicnum] >> 4))) * (1.0f / 256.f) };
4901 
4902     if ((globalorientation&(2+64)) == (2+64)) //Hack for panning for slopes w/ relative alignment
4903     {
4904         float r = global_cf_heinum * (1.0f / 4096.f);
4905         r = polymost_invsqrt_approximation(r * r + 1);
4906 
4907         if (!(globalorientation & 4))
4908             fxy.y *= r;
4909         else
4910             fxy.x *= r;
4911     }
4912     ytex.u += ytex.d*fxy.x; otex.u += otex.d*fxy.x;
4913     ytex.v += ytex.d*fxy.y; otex.v += otex.d*fxy.y;
4914 
4915     if (globalorientation&2) //slopes
4916     {
4917         //Pick some point guaranteed to be not collinear to the 1st two points
4918         vec2f_t dxy = { n1.y - n0.y, n0.x - n1.x };
4919 
4920         float const dxyr = polymost_invsqrt_approximation(dxy.x * dxy.x + dxy.y * dxy.y);
4921 
4922         dxy.x *= dxyr * 4096.f;
4923         dxy.y *= dxyr * 4096.f;
4924 
4925         vec2f_t const oxy = { n0.x + dxy.x, n0.y + dxy.y };
4926 
4927         float const ox2 = (oxy.y - fglobalposy) * gcosang - (oxy.x - fglobalposx) * gsinang;
4928         float oy2 = 1.f / ((oxy.x - fglobalposx) * gcosang2 + (oxy.y - fglobalposy) * gsinang2);
4929 
4930         double const px[3] = { x0, x1, (double)ghalfx * ox2 * oy2 + ghalfx };
4931 
4932         oy2 *= gyxscale;
4933 
4934         double py[3] = { ryp0 + (double)ghoriz, ryp1 + (double)ghoriz, oy2 + (double)ghoriz };
4935 
4936         vec3d_t const duv[3] = {
4937             { (px[0] * xtex.d + py[0] * ytex.d + otex.d),
4938               (px[0] * xtex.u + py[0] * ytex.u + otex.u),
4939               (px[0] * xtex.v + py[0] * ytex.v + otex.v)
4940             },
4941             { (px[1] * xtex.d + py[1] * ytex.d + otex.d),
4942               (px[1] * xtex.u + py[1] * ytex.u + otex.u),
4943               (px[1] * xtex.v + py[1] * ytex.v + otex.v)
4944             },
4945             { (px[2] * xtex.d + py[2] * ytex.d + otex.d),
4946               (px[2] * xtex.u + py[2] * ytex.u + otex.u),
4947               (px[2] * xtex.v + py[2] * ytex.v + otex.v)
4948             }
4949         };
4950 
4951         py[0] = y0;
4952         py[1] = y1;
4953         py[2] = double(global_getzofslope_func((usectorptr_t)&sector[sectnum], oxy.x, oxy.y) - globalposz) * oy2 + ghoriz;
4954 
4955         vec3f_t oxyz[2] = { { (float)(py[1] - py[2]), (float)(py[2] - py[0]), (float)(py[0] - py[1]) },
4956                             { (float)(px[2] - px[1]), (float)(px[0] - px[2]), (float)(px[1] - px[0]) } };
4957 
4958         float const r = 1.f / (oxyz[0].x * px[0] + oxyz[0].y * px[1] + oxyz[0].z * px[2]);
4959 
4960         xtex.d = (oxyz[0].x * duv[0].d + oxyz[0].y * duv[1].d + oxyz[0].z * duv[2].d) * r;
4961         xtex.u = (oxyz[0].x * duv[0].u + oxyz[0].y * duv[1].u + oxyz[0].z * duv[2].u) * r;
4962         xtex.v = (oxyz[0].x * duv[0].v + oxyz[0].y * duv[1].v + oxyz[0].z * duv[2].v) * r;
4963 
4964         ytex.d = (oxyz[1].x * duv[0].d + oxyz[1].y * duv[1].d + oxyz[1].z * duv[2].d) * r;
4965         ytex.u = (oxyz[1].x * duv[0].u + oxyz[1].y * duv[1].u + oxyz[1].z * duv[2].u) * r;
4966         ytex.v = (oxyz[1].x * duv[0].v + oxyz[1].y * duv[1].v + oxyz[1].z * duv[2].v) * r;
4967 
4968         otex.d = duv[0].d - px[0] * xtex.d - py[0] * ytex.d;
4969         otex.u = duv[0].u - px[0] * xtex.u - py[0] * ytex.u;
4970         otex.v = duv[0].v - px[0] * xtex.v - py[0] * ytex.v;
4971 
4972         if (globalorientation&64) //Hack for relative alignment on slopes
4973         {
4974             float r = global_cf_heinum * (1.0f / 4096.f);
4975             r = Bsqrtf(r*r+1);
4976             if (!(globalorientation&4)) { xtex.v *= r; ytex.v *= r; otex.v *= r; }
4977             else { xtex.u *= r; ytex.u *= r; otex.u *= r; }
4978         }
4979     }
4980 
4981     domostpolymethod = (globalorientation>>7) & DAMETH_MASKPROPS;
4982 
4983     pow2xsplit = 0;
4984     drawpoly_alpha = 0.f;
4985     drawpoly_blend = 0;
4986 
4987     if (!polymost_usetileshades() || (usehightile && hicfindsubst(globalpicnum, globalpal, hictinting[globalpal].f & HICTINT_ALWAYSUSEART)))
4988         calc_and_apply_fog(fogshade(global_cf_shade, global_cf_pal), sec->visibility, POLYMOST_CHOOSE_FOG_PAL(global_cf_fogpal, global_cf_pal));
4989 
4990     if (have_floor)
4991     {
4992         if (globalposz > getflorzofslope(sectnum, globalposx, globalposy))
4993             domostpolymethod = DAMETH_BACKFACECULL; //Back-face culling
4994 
4995         if (domostpolymethod & DAMETH_MASKPROPS)
4996             glEnable(GL_BLEND);
4997 
4998         polymost_domost(x0, y0, x1, y1); //flor
4999     }
5000     else
5001     {
5002         if (globalposz < getceilzofslope(sectnum, globalposx, globalposy))
5003             domostpolymethod = DAMETH_BACKFACECULL; //Back-face culling
5004 
5005         if (domostpolymethod & DAMETH_MASKPROPS)
5006             glEnable(GL_BLEND);
5007 
5008         polymost_domost(x1, y1, x0, y0); //ceil
5009     }
5010 
5011     if (domostpolymethod & DAMETH_MASKPROPS)
5012         glDisable(GL_BLEND);
5013 
5014     domostpolymethod = DAMETH_NOMASK;
5015 }
5016 
calc_ypanning(int32_t refposz,float ryp0,float ryp1,float x0,float x1,uint8_t ypan,uint8_t yrepeat,int32_t dopancor)5017 static void calc_ypanning(int32_t refposz, float ryp0, float ryp1,
5018                           float x0, float x1, uint8_t ypan, uint8_t yrepeat,
5019                           int32_t dopancor)
5020 {
5021     float const t0 = ((float)(refposz-globalposz))*ryp0 + ghoriz;
5022     float const t1 = ((float)(refposz-globalposz))*ryp1 + ghoriz;
5023     float t = (float(xtex.d*x0 + otex.d) * (float)yrepeat) / ((x1-x0) * ryp0 * 2048.f);
5024     int i = (1<<(picsiz[globalpicnum]>>4));
5025     if (i < tilesiz[globalpicnum].y) i <<= 1;
5026 
5027 #ifdef NEW_MAP_FORMAT
5028     if (g_loadedMapVersion >= 10)
5029         i = tilesiz[globalpicnum].y;
5030     else
5031 #endif
5032     if (picanm[globalpicnum].tileflags & TILEFLAGS_TRUENPOT)
5033     {
5034         i = tilesiz[globalpicnum].y;
5035     }
5036     else if (polymost_is_npotmode())
5037     {
5038         t *= (float)tilesiz[globalpicnum].y / i;
5039         i = tilesiz[globalpicnum].y;
5040     }
5041     else if (!(glinfo.texnpot && r_npotwallmode == 2) && dopancor)
5042     {
5043         // Carry out panning "correction" to make it look like classic in some
5044         // cases, but failing in the general case.
5045         int32_t yoffs = Blrintf((i-tilesiz[globalpicnum].y)*(255.f/i));
5046 
5047         if (ypan > 256-yoffs)
5048             ypan -= yoffs;
5049     }
5050 
5051     float const fy = (float)(ypan * i) * (1.f / 256.f);
5052 
5053     xtex.v = double(t0 - t1) * t;
5054     ytex.v = double(x1 - x0) * t;
5055     otex.v = -xtex.v * x0 - ytex.v * t0 + fy * otex.d;
5056     xtex.v += fy * xtex.d;
5057     ytex.v += fy * ytex.d;
5058 }
5059 
testvisiblemost(float const x0,float const x1)5060 static inline int32_t testvisiblemost(float const x0, float const x1)
5061 {
5062     for (bssize_t i=vsp[0].n, newi; i; i=newi)
5063     {
5064         newi = vsp[i].n;
5065         if ((x0 < vsp[newi].x) && (vsp[i].x < x1) && (vsp[i].ctag >= 0))
5066             return 1;
5067     }
5068     return 0;
5069 }
5070 
polymost_getclosestpointonwall(vec2_t const * const pos,int32_t dawall,vec2_t * const n)5071 static inline int polymost_getclosestpointonwall(vec2_t const * const pos, int32_t dawall, vec2_t * const n)
5072 {
5073     vec2_t const w = { wall[dawall].x, wall[dawall].y };
5074     vec2_t const d = { POINT2(dawall).x - w.x, POINT2(dawall).y - w.y };
5075     int64_t i = d.x * ((int64_t)pos->x - w.x) + d.y * ((int64_t)pos->y - w.y);
5076 
5077     if (i < 0)
5078         return 1;
5079 
5080     int64_t const j = (int64_t)d.x * d.x + (int64_t)d.y * d.y;
5081 
5082     if (i > j)
5083         return 1;
5084 
5085     i = tabledivide64((i << 15), j) << 15;
5086 
5087     n->x = w.x + ((d.x * i) >> 30);
5088     n->y = w.y + ((d.y * i) >> 30);
5089 
5090     return 0;
5091 }
5092 
fgetceilzofslope(usectorptr_t sec,float dax,float day)5093 float fgetceilzofslope(usectorptr_t sec, float dax, float day)
5094 {
5095     if (!(sec->ceilingstat&2))
5096         return float(sec->ceilingz);
5097 
5098     auto const wal  = (uwallptr_t)&wall[sec->wallptr];
5099     auto const wal2 = (uwallptr_t)&wall[wal->point2];
5100 
5101     vec2_t const w = *(vec2_t const *)wal;
5102     vec2_t const d = { wal2->x - w.x, wal2->y - w.y };
5103 
5104     int const i = nsqrtasm(uhypsq(d.x,d.y))<<5;
5105     if (i == 0) return sec->ceilingz;
5106 
5107     float const j = (d.x*(day-w.y)-d.y*(dax-w.x))*(1.f/8.f);
5108     return float(sec->ceilingz) + (sec->ceilingheinum*j)/i;
5109 }
5110 
fgetflorzofslope(usectorptr_t sec,float dax,float day)5111 float fgetflorzofslope(usectorptr_t sec, float dax, float day)
5112 {
5113     if (!(sec->floorstat&2))
5114         return float(sec->floorz);
5115 
5116     auto const wal  = (uwallptr_t)&wall[sec->wallptr];
5117     auto const wal2 = (uwallptr_t)&wall[wal->point2];
5118 
5119     vec2_t const w = *(vec2_t const *)wal;
5120     vec2_t const d = { wal2->x - w.x, wal2->y - w.y };
5121 
5122     int const i = nsqrtasm(uhypsq(d.x,d.y))<<5;
5123     if (i == 0) return sec->floorz;
5124 
5125     float const j = (d.x*(day-w.y)-d.y*(dax-w.x))*(1.f/8.f);
5126     return float(sec->floorz) + (sec->floorheinum*j)/i;
5127 }
5128 
fgetzsofslope(usectorptr_t sec,float dax,float day,float * ceilz,float * florz)5129 void fgetzsofslope(usectorptr_t sec, float dax, float day, float* ceilz, float *florz)
5130 {
5131     *ceilz = float(sec->ceilingz); *florz = float(sec->floorz);
5132 
5133     if (((sec->ceilingstat|sec->floorstat)&2) != 2)
5134         return;
5135 
5136     auto const wal  = (uwallptr_t)&wall[sec->wallptr];
5137     auto const wal2 = (uwallptr_t)&wall[wal->point2];
5138 
5139     vec2_t const d = { wal2->x - wal->x, wal2->y - wal->y };
5140 
5141     int const i = nsqrtasm(uhypsq(d.x,d.y))<<5;
5142     if (i == 0) return;
5143 
5144     float const j = (d.x*(day-wal->y)-d.y*(dax-wal->x))*(1.f/8.f);
5145     if (sec->ceilingstat&2)
5146         *ceilz += (sec->ceilingheinum*j)/i;
5147     if (sec->floorstat&2)
5148         *florz += (sec->floorheinum*j)/i;
5149 }
5150 
polymost_flatskyrender(vec2f_t const * const dpxy,int32_t const n,int32_t method)5151 static void polymost_flatskyrender(vec2f_t const* const dpxy, int32_t const n, int32_t method)
5152 {
5153     flatskyrender = 0;
5154     vec2f_t xys[8];
5155 
5156     // Transform polygon to sky coordinates
5157     for (int i = 0; i < n; i++)
5158     {
5159         vec3f_t const o = { dpxy[i].x-ghalfx, dpxy[i].y-ghalfy, ghalfx / gvrcorrection };
5160 
5161         //Up/down rotation
5162         vec3d_t v = { o.x, o.y * gchang - o.z * gshang, o.z * gchang + o.y * gshang };
5163         float const r = (ghalfx / gvrcorrection) / v.z;
5164         xys[i].x = v.x * r + ghalfx;
5165         xys[i].y = v.y * r + ghalfy;
5166     }
5167 
5168     float const fglobalang = fix16_to_float(qglobalang);
5169     int32_t dapyscale, dapskybits, dapyoffs, daptileyscale;
5170     int8_t const * dapskyoff = getpsky(globalpicnum, &dapyscale, &dapskybits, &dapyoffs, &daptileyscale);
5171 
5172     ghoriz = (qglobalhoriz*(1.f/65536.f)-float(ydimen>>1))*dapyscale*(1.f/65536.f)+float(ydimen>>1)+ghorizcorrect;
5173 
5174     float const dd = fxdimen*.0000001f; //Adjust sky depth based on screen size!
5175     float vv[2];
5176     float t = (float)((1<<(picsiz[globalpicnum]&15))<<dapskybits);
5177     vv[1] = dd*((float)xdimscale*fviewingrange) * (1.f/(daptileyscale*65536.f));
5178     vv[0] = dd*((float)((tilesiz[globalpicnum].y>>1)+dapyoffs)) - vv[1]*ghoriz;
5179     int ti = (1<<(picsiz[globalpicnum]>>4)); if (ti != tilesiz[globalpicnum].y) ti += ti;
5180     vec3f_t o;
5181 
5182     skyclamphack = 0;
5183 
5184     xtex.d = xtex.v = 0;
5185     ytex.d = ytex.u = 0;
5186     otex.d = dd;
5187     xtex.u = otex.d * (t * double(((uint64_t)xdimscale * yxaspect) * viewingrange)) *
5188                         (1.0 / (16384.0 * 65536.0 * 65536.0 * 5.0 * 1024.0));
5189     ytex.v = vv[1];
5190     otex.v = r_parallaxskypanning ? vv[0] + dd*(float)global_cf_ypanning*(float)ti*(1.f/256.f) : vv[0];
5191 
5192     float x0 = xys[0].x, x1 = xys[0].x;
5193 
5194     for (bssize_t i=n-1; i>=1; i--)
5195     {
5196         if (xys[i].x < x0) x0 = xys[i].x;
5197         if (xys[i].x > x1) x1 = xys[i].x;
5198     }
5199 
5200     int const npot = (1<<(picsiz[globalpicnum]&15)) != tilesiz[globalpicnum].x;
5201     int const xpanning = (r_parallaxskypanning?global_cf_xpanning:0);
5202 
5203     polymost_setClamp((npot || xpanning != 0) ? 0 : 2);
5204 
5205     int picnumbak = globalpicnum;
5206     ti = globalpicnum;
5207     o.y = fviewingrange/(ghalfx*256.f); o.z = 1.f/o.y;
5208 
5209     int y = ((int32_t)(((x0-ghalfx)*o.y)+fglobalang)>>(11-dapskybits));
5210     float fx = x0;
5211 
5212     do
5213     {
5214         globalpicnum = dapskyoff[y&((1<<dapskybits)-1)]+ti;
5215         if (npot)
5216         {
5217             float fx = ((float)((y<<(11-dapskybits))-fglobalang))*o.z+ghalfx;
5218             int tang = (y<<(11-dapskybits))&2047;
5219             otex.u = otex.d*(t*((float)(tang)) * (1.f/2048.f) + xpanning) - xtex.u*fx;
5220         }
5221         else
5222             otex.u = otex.d*(t*((float)(fglobalang-(y<<(11-dapskybits)))) * (1.f/2048.f) + xpanning) - xtex.u*ghalfx;
5223         y++;
5224         o.x = fx; fx = ((float)((y<<(11-dapskybits))-fglobalang))*o.z+ghalfx;
5225 
5226         if (fx > x1) { fx = x1; ti = -1; }
5227 
5228         vec3d_t otexbak = otex, xtexbak = xtex, ytexbak = ytex;
5229 
5230         // Transform texture mapping factors
5231         vec2f_t fxy[3] = { { ghalfx * (1.f - 0.25f), ghalfy * (1.f - 0.25f) },
5232                           { ghalfx, ghalfy * (1.f + 0.25f) },
5233                           { ghalfx * (1.f + 0.25f), ghalfy * (1.f - 0.25f) } };
5234 
5235         vec3d_t duv[3] = {
5236             { (fxy[0].x * xtex.d + fxy[0].y * ytex.d + otex.d),
5237               (fxy[0].x * xtex.u + fxy[0].y * ytex.u + otex.u),
5238               (fxy[0].x * xtex.v + fxy[0].y * ytex.v + otex.v)
5239             },
5240             { (fxy[1].x * xtex.d + fxy[1].y * ytex.d + otex.d),
5241               (fxy[1].x * xtex.u + fxy[1].y * ytex.u + otex.u),
5242               (fxy[1].x * xtex.v + fxy[1].y * ytex.v + otex.v)
5243             },
5244             { (fxy[2].x * xtex.d + fxy[2].y * ytex.d + otex.d),
5245               (fxy[2].x * xtex.u + fxy[2].y * ytex.u + otex.u),
5246               (fxy[2].x * xtex.v + fxy[2].y * ytex.v + otex.v)
5247             }
5248         };
5249         vec2f_t fxyt[3];
5250         vec3d_t duvt[3];
5251 
5252         for (int i = 0; i < 3; i++)
5253         {
5254             vec2f_t const o = { fxy[i].x-ghalfx, fxy[i].y-ghalfy };
5255             vec3f_t const o2 = { o.x, o.y, ghalfx / gvrcorrection };
5256 
5257             //Up/down rotation (backwards)
5258             vec3d_t v = { o2.x, o2.y * gchang + o2.z * gshang, o2.z * gchang - o2.y * gshang };
5259             float const r = (ghalfx / gvrcorrection) / v.z;
5260             fxyt[i].x = v.x * r + ghalfx;
5261             fxyt[i].y = v.y * r + ghalfy;
5262             duvt[i].d = duv[i].d*r;
5263             duvt[i].u = duv[i].u*r;
5264             duvt[i].v = duv[i].v*r;
5265         }
5266 
5267         vec3f_t oxyz[2] = { { (float)(fxyt[1].y - fxyt[2].y), (float)(fxyt[2].y - fxyt[0].y), (float)(fxyt[0].y - fxyt[1].y) },
5268                             { (float)(fxyt[2].x - fxyt[1].x), (float)(fxyt[0].x - fxyt[2].x), (float)(fxyt[1].x - fxyt[0].x) } };
5269 
5270         float const rr = 1.f / (oxyz[0].x * fxyt[0].x + oxyz[0].y * fxyt[1].x + oxyz[0].z * fxyt[2].x);
5271 
5272         xtex.d = (oxyz[0].x * duvt[0].d + oxyz[0].y * duvt[1].d + oxyz[0].z * duvt[2].d) * rr;
5273         xtex.u = (oxyz[0].x * duvt[0].u + oxyz[0].y * duvt[1].u + oxyz[0].z * duvt[2].u) * rr;
5274         xtex.v = (oxyz[0].x * duvt[0].v + oxyz[0].y * duvt[1].v + oxyz[0].z * duvt[2].v) * rr;
5275 
5276         ytex.d = (oxyz[1].x * duvt[0].d + oxyz[1].y * duvt[1].d + oxyz[1].z * duvt[2].d) * rr;
5277         ytex.u = (oxyz[1].x * duvt[0].u + oxyz[1].y * duvt[1].u + oxyz[1].z * duvt[2].u) * rr;
5278         ytex.v = (oxyz[1].x * duvt[0].v + oxyz[1].y * duvt[1].v + oxyz[1].z * duvt[2].v) * rr;
5279 
5280         otex.d = duvt[0].d - fxyt[0].x * xtex.d - fxyt[0].y * ytex.d;
5281         otex.u = duvt[0].u - fxyt[0].x * xtex.u - fxyt[0].y * ytex.u;
5282         otex.v = duvt[0].v - fxyt[0].x * xtex.v - fxyt[0].y * ytex.v;
5283 
5284         vec2f_t cxy[8];
5285         vec2f_t cxy2[8];
5286         int n2 = 0, n3 = 0;
5287 
5288         // Clip to o.x
5289         for (bssize_t i=0; i<n; i++)
5290         {
5291             int const j = i < n-1 ? i + 1 : 0;
5292 
5293             if (xys[i].x >= o.x)
5294                 cxy[n2++] = xys[i];
5295 
5296             if ((xys[i].x >= o.x) != (xys[j].x >= o.x))
5297             {
5298                 float const r = (o.x - xys[i].x) / (xys[j].x - xys[i].x);
5299                 cxy[n2++] = { o.x, (xys[j].y - xys[i].y) * r + xys[i].y };
5300             }
5301         }
5302 
5303         // Clip to fx
5304         for (bssize_t i=0; i<n2; i++)
5305         {
5306             int const j = i < n2-1 ? i + 1 : 0;
5307 
5308             if (cxy[i].x <= fx)
5309                 cxy2[n3++] = cxy[i];
5310 
5311             if ((cxy[i].x <= fx) != (cxy[j].x <= fx))
5312             {
5313                 float const r = (fx - cxy[i].x) / (cxy[j].x - cxy[i].x);
5314                 cxy2[n3++] = { fx, (cxy[j].y - cxy[i].y) * r + cxy[i].y };
5315             }
5316         }
5317 
5318         // Transform back to polymost coordinates
5319         for (int i = 0; i < n3; i++)
5320         {
5321             vec3f_t const o = { cxy2[i].x-ghalfx, cxy2[i].y-ghalfy, ghalfx / gvrcorrection };
5322 
5323             //Up/down rotation
5324             vec3d_t v = { o.x, o.y * gchang + o.z * gshang, o.z * gchang - o.y * gshang };
5325             float const r = (ghalfx / gvrcorrection) / v.z;
5326             cxy[i].x = v.x * r + ghalfx;
5327             cxy[i].y = v.y * r + ghalfy;
5328         }
5329 
5330         polymost_drawpoly(cxy, n3, method|DAMETH_WALL);
5331 
5332         otex = otexbak, xtex = xtexbak, ytex = ytexbak;
5333     }
5334     while (ti >= 0);
5335 
5336     globalpicnum = picnumbak;
5337 
5338     polymost_setClamp(0);
5339 
5340     flatskyrender = 1;
5341 }
5342 
polymost_drawalls(int32_t const bunch)5343 static void polymost_drawalls(int32_t const bunch)
5344 {
5345     drawpoly_alpha = 0.f;
5346     drawpoly_blend = 0;
5347 
5348     int32_t const sectnum = thesector[bunchfirst[bunch]];
5349     auto const sec = (usectorptr_t)&sector[sectnum];
5350     float const fglobalang = fix16_to_float(qglobalang);
5351 
5352     polymost_outputGLDebugMessage(3, "polymost_drawalls(bunch:%d)", bunch);
5353 
5354     //DRAW WALLS SECTION!
5355     for (bssize_t z=bunchfirst[bunch]; z>=0; z=bunchp2[z])
5356     {
5357         int32_t const wallnum = thewall[z];
5358 
5359         auto const wal = (uwallptr_t)&wall[wallnum];
5360         auto const wal2 = (uwallptr_t)&wall[wal->point2];
5361         int32_t const nextsectnum = wal->nextsector;
5362         auto const nextsec = nextsectnum>=0 ? (usectorptr_t)&sector[nextsectnum] : NULL;
5363 
5364         //Offset&Rotate 3D coordinates to screen 3D space
5365         vec2f_t walpos = { (float)(wal->x-globalposx), (float)(wal->y-globalposy) };
5366 
5367         vec2f_t p0 = { walpos.y * gcosang - walpos.x * gsinang, walpos.x * gcosang2 + walpos.y * gsinang2 };
5368         vec2f_t const op0 = p0;
5369 
5370         walpos = { (float)(wal2->x - globalposx),
5371                    (float)(wal2->y - globalposy) };
5372 
5373         vec2f_t p1 = { walpos.y * gcosang - walpos.x * gsinang, walpos.x * gcosang2 + walpos.y * gsinang2 };
5374 
5375         //Clip to close parallel-screen plane
5376 
5377         vec2f_t n0, n1;
5378         float t0, t1;
5379 
5380         if (p0.y < SCISDIST)
5381         {
5382             if (p1.y < SCISDIST) continue;
5383             t0 = (SCISDIST-p0.y)/(p1.y-p0.y);
5384             p0 = { (p1.x-p0.x)*t0+p0.x, SCISDIST };
5385             n0 = { (wal2->x-wal->x)*t0+wal->x,
5386                    (wal2->y-wal->y)*t0+wal->y };
5387         }
5388         else
5389         {
5390             t0 = 0.f;
5391             n0 = { (float)wal->x, (float)wal->y };
5392         }
5393 
5394         if (p1.y < SCISDIST)
5395         {
5396             t1 = (SCISDIST-op0.y)/(p1.y-op0.y);
5397             p1 = { (p1.x-op0.x)*t1+op0.x, SCISDIST };
5398             n1 = { (wal2->x-wal->x)*t1+wal->x,
5399                    (wal2->y-wal->y)*t1+wal->y };
5400         }
5401         else
5402         {
5403             t1 = 1.f;
5404             n1 = { (float)wal2->x, (float)wal2->y };
5405         }
5406 
5407         float ryp0 = 1.f/p0.y, ryp1 = 1.f/p1.y;
5408 
5409         //Generate screen coordinates for front side of wall
5410         float const x0 = ghalfx*p0.x*ryp0 + ghalfx, x1 = ghalfx*p1.x*ryp1 + ghalfx;
5411 
5412         if (x1 <= x0) continue;
5413 
5414         ryp0 *= gyxscale; ryp1 *= gyxscale;
5415 
5416         float cz, fz;
5417 
5418         fgetzsofslope((usectorptr_t)&sector[sectnum],n0.x,n0.y,&cz,&fz);
5419         float const cy0 = (cz-globalposz)*ryp0 + ghoriz, fy0 = (fz-globalposz)*ryp0 + ghoriz;
5420 
5421         fgetzsofslope((usectorptr_t)&sector[sectnum],n1.x,n1.y,&cz,&fz);
5422         float const cy1 = (cz-globalposz)*ryp1 + ghoriz, fy1 = (fz-globalposz)*ryp1 + ghoriz;
5423 
5424         xtex2.d = (ryp0 - ryp1)*gxyaspect / (x0 - x1);
5425         ytex2.d = 0;
5426         otex2.d = ryp0 * gxyaspect - xtex2.d*x0;
5427 
5428         xtex2.u = ytex2.u = otex2.u = 0;
5429         xtex2.v = ytex2.v = otex2.v = 0;
5430 
5431         // Floor
5432 
5433         if (searchit == 2
5434 #ifdef YAX_ENABLE
5435             && (yax_getbunch(sectnum, YAX_FLOOR) < 0 || showinvisibility || (sec->floorstat&(256+128)) || klabs(yax_globallev-YAX_MAXDRAWS)==YAX_MAXDRAWS)
5436 #endif
5437             )
5438         {
5439             psectnum = sectnum;
5440             pwallnum = wallnum;
5441             psearchstat = 2;
5442             doeditorcheck = 1;
5443         }
5444 
5445 #ifdef YAX_ENABLE
5446 
5447         int32_t checkcf = 0;
5448         int16_t bn[2] = {};
5449 
5450         if (g_nodraw)
5451         {
5452             int32_t baselevp;
5453             baselevp = (yax_globallev == YAX_MAXDRAWS);
5454 
5455             yax_getbunches(sectnum, &bn[0], &bn[1]);
5456             checkcf = (bn[0]>=0) + ((bn[1]>=0)<<1);
5457             if (!baselevp)
5458                 checkcf &= (1<<yax_globalcf);
5459         }
5460         yax_holencf[YAX_FLOOR] = 0;
5461         yax_drawcf = YAX_FLOOR;
5462 #endif
5463 
5464         globalpicnum = sec->floorpicnum;
5465         globalshade = sec->floorshade;
5466         globalpal = sec->floorpal;
5467         globalorientation = sec->floorstat;
5468         globvis = (sector[sectnum].visibility != 0) ?
5469                   mulscale4(globalcisibility, (uint8_t)(sector[sectnum].visibility + 16)) :
5470                   globalcisibility;
5471         globvis2 = (sector[sectnum].visibility != 0) ?
5472                   mulscale4(globalcisibility2, (uint8_t)(sector[sectnum].visibility + 16)) :
5473                   globalcisibility2;
5474         polymost_setVisibility(globvis2);
5475 
5476         tileUpdatePicnum(&globalpicnum, sectnum);
5477 
5478         int32_t dapyscale, dapskybits, dapyoffs, daptileyscale;
5479         int8_t const * dapskyoff = getpsky(globalpicnum, &dapyscale, &dapskybits, &dapyoffs, &daptileyscale);
5480 
5481         global_cf_fogpal = sec->fogpal;
5482         global_cf_shade = sec->floorshade, global_cf_pal = sec->floorpal; global_cf_z = sec->floorz;  // REFACT
5483         global_cf_xpanning = sec->floorxpanning; global_cf_ypanning = sec->floorypanning, global_cf_heinum = sec->floorheinum;
5484         global_getzofslope_func = &fgetflorzofslope;
5485 
5486 #ifdef YAX_ENABLE
5487         // this is to prevent double-drawing of translucent masked floors
5488         if (g_nodraw || r_tror_nomaskpass==0 || yax_globallev==YAX_MAXDRAWS || (sec->floorstat&256)==0 ||
5489             yax_nomaskpass==1 || !(yax_gotsector[sectnum>>3]&pow2char[sectnum&7]))
5490         {
5491 #endif
5492         if (globalpicnum >= r_rortexture && globalpicnum < r_rortexture + r_rortexturerange && r_rorphase == 0)
5493         {
5494             xtex.d = (ryp0-ryp1)*gxyaspect / (x0-x1);
5495             ytex.d = 0;
5496             otex.d = ryp0*gxyaspect - xtex.d*x0;
5497 
5498             xtex.u = ytex.u = otex.u = 0;
5499             xtex.v = ytex.v = otex.v = 0;
5500             polymost_domost(x0, fy0, x1, fy1);
5501         }
5502         else if (!(globalorientation&1))
5503         {
5504             int32_t fz = getflorzofslope(sectnum, globalposx, globalposy);
5505             if (globalposz <= fz)
5506                 polymost_internal_nonparallaxed(n0, n1, ryp0, ryp1, x0, x1, fy0, fy1, sectnum | MAXSECTORS);
5507 #ifdef YAX_ENABLE
5508             else if (g_nodraw && (checkcf & 2) != 0)
5509                 polymost_domost(x0, fy0, x1, fy1);
5510 #endif
5511         }
5512         else if ((nextsectnum < 0) || (!(sector[nextsectnum].floorstat&1)))
5513         {
5514             //Parallaxing sky... hacked for Ken's mountain texture
5515             if (!polymost_usetileshades() || (usehightile && hicfindsubst(globalpicnum, globalpal, hictinting[globalpal].f & HICTINT_ALWAYSUSEART)))
5516                 calc_and_apply_fog_factor(sec->floorshade, sec->visibility, sec->floorpal, 0.005f);
5517 
5518             globvis2 = globalpisibility;
5519             if (sec->visibility != 0)
5520                 globvis2 = mulscale4(globvis2, (uint8_t)(sec->visibility + 16));
5521             float viscale = xdimscale*fxdimen*(.0000001f/256.f);
5522             polymost_setVisibility(globvis2*viscale);
5523 
5524             //Use clamping for tiled sky textures
5525             //(don't wrap around edges if the sky use multiple panels)
5526             for (bssize_t i=(1<<dapskybits)-1; i>0; i--)
5527                 if (dapskyoff[i] != dapskyoff[i-1])
5528                     { skyclamphack = r_parallaxskyclamping; break; }
5529 
5530             skyzbufferhack = 1;
5531 
5532             if (!usehightile || !hicfindskybox(globalpicnum, globalpal))
5533             {
5534                 float const ghorizbak = ghoriz;
5535                 if (r_flatsky && ! r_yshearing)
5536                 {
5537                     pow2xsplit = 0;
5538                     skyclamphack = 0;
5539                     flatskyrender = 1;
5540                     globalshade += globvis2*xdimscale*fviewingrange*(1.f / (64.f * 65536.f * 256.f * 1024.f));
5541                     polymost_setVisibility(0.f);
5542                     polymost_domost(x0,fy0,x1,fy1);
5543                     flatskyrender = 0;
5544                 }
5545                 else
5546                 {
5547                     if (r_yshearing)
5548                         ghoriz = (qglobalhoriz*(1.f/65536.f)-float(ydimen>>1))*(dapyscale-65536.f)*(1.f/65536.f)+float(ydimen>>1);
5549 
5550                     float const dd = fxdimen*.0000001f; //Adjust sky depth based on screen size!
5551                     float vv[2];
5552                     float t = (float)((1<<(picsiz[globalpicnum]&15))<<dapskybits);
5553                     vv[1] = dd*((float)xdimscale*fviewingrange) * (1.f/(daptileyscale*65536.f));
5554                     vv[0] = dd*((float)((tilesiz[globalpicnum].y>>1)+dapyoffs)) - vv[1]*ghoriz;
5555                     int i = (1<<(picsiz[globalpicnum]>>4)); if (i != tilesiz[globalpicnum].y) i += i;
5556                     vec3f_t o;
5557 
5558                     if ((tilesiz[globalpicnum].y * daptileyscale * (1.f/65536.f)) > 256)
5559                     {
5560                         //Hack to draw black rectangle below sky when looking down...
5561                         xtex.d = xtex.u = xtex.v = 0;
5562 
5563                         ytex.d = gxyaspect * (1.0 / 262144.0);
5564                         ytex.u = 0;
5565                         ytex.v = double(tilesiz[globalpicnum].y - 1) * ytex.d;
5566 
5567                         otex.d = -ghoriz * ytex.d;
5568                         otex.u = 0;
5569                         otex.v = double(tilesiz[globalpicnum].y - 1) * otex.d;
5570 
5571                         o.y = ((float)tilesiz[globalpicnum].y*dd-vv[0])/vv[1];
5572 
5573                         if ((o.y > fy0) && (o.y > fy1))
5574                             polymost_domost(x0,o.y,x1,o.y);
5575                         else if ((o.y > fy0) != (o.y > fy1))
5576                         {
5577                             //  fy0                      fy1
5578                             //     \                    /
5579                             //oy----------      oy----------
5580                             //        \              /
5581                             //         fy1        fy0
5582                             o.x = (o.y-fy0)*(x1-x0)/(fy1-fy0) + x0;
5583                             if (o.y > fy0)
5584                             {
5585                                 polymost_domost(x0,o.y,o.x,o.y);
5586                                 polymost_domost(o.x,o.y,x1,fy1);
5587                             }
5588                             else
5589                             {
5590                                 polymost_domost(x0,fy0,o.x,o.y);
5591                                 polymost_domost(o.x,o.y,x1,o.y);
5592                             }
5593                         }
5594                         else
5595                             polymost_domost(x0,fy0,x1,fy1);
5596 
5597 #if 0
5598                         //Hack to draw color rectangle above sky when looking up...
5599                         xtex.d = xtex.u = xtex.v = 0;
5600 
5601                         ytex.d = gxyaspect * (1.f / -262144.f);
5602                         ytex.u = 0;
5603                         ytex.v = 0;
5604 
5605                         otex.d = -ghoriz * ytex.d;
5606                         otex.u = 0;
5607                         otex.v = 0;
5608 
5609                         o.y = -vv[0]/vv[1];
5610 
5611                         if ((o.y < fy0) && (o.y < fy1))
5612                             polymost_domost(x1,o.y,x0,o.y);
5613                         else if ((o.y < fy0) != (o.y < fy1))
5614                         {
5615                             o.x = (o.y-fy0)*(x1-x0)/(fy1-fy0) + x0;
5616                             if (o.y < fy0)
5617                             {
5618                                 polymost_domost(o.x,o.y,x0,o.y);
5619                                 polymost_domost(x1,fy1,o.x,o.y);
5620                             }
5621                             else
5622                             {
5623                                 polymost_domost(o.x,o.y,x0,fy0);
5624                                 polymost_domost(x1,o.y,o.x,o.y);
5625                             }
5626                         }
5627                         else
5628                             polymost_domost(x1,fy1,x0,fy0);
5629 #endif
5630                     }
5631                     else
5632                         skyclamphack = 0;
5633 
5634                     xtex.d = xtex.v = 0;
5635                     ytex.d = ytex.u = 0;
5636                     otex.d = dd;
5637                     xtex.u = otex.d * (t * double(((uint64_t)xdimscale * yxaspect) * viewingrange)) *
5638                                       (1.0 / (16384.0 * 65536.0 * 65536.0 * 5.0 * 1024.0));
5639                     ytex.v = vv[1];
5640                     otex.v = r_parallaxskypanning ? vv[0] + dd*(float)sec->floorypanning*(float)i*(1.f/256.f) : vv[0];
5641 
5642                     int const npot = (1<<(picsiz[globalpicnum]&15)) != tilesiz[globalpicnum].x;
5643                     int const xpanning = (r_parallaxskypanning?sec->floorxpanning:0);
5644 
5645                     i = globalpicnum;
5646                     float const r = (fy1-fy0)/(x1-x0); //slope of line
5647                     o.y = fviewingrange/(ghalfx*256.f); o.z = 1.f/o.y;
5648 
5649                     int y = ((int32_t)(((x0-ghalfx)*o.y)+fglobalang)>>(11-dapskybits));
5650                     float fx = x0;
5651                     do
5652                     {
5653                         globalpicnum = dapskyoff[y&((1<<dapskybits)-1)]+i;
5654                         if (npot)
5655                         {
5656                             float fx = ((float)((y<<(11-dapskybits))-fglobalang))*o.z+ghalfx;
5657                             int tang = (y<<(11-dapskybits))&2047;
5658                             otex.u = otex.d*(t*((float)(tang)) * (1.f/2048.f) + xpanning) - xtex.u*fx;
5659                         }
5660                         else
5661                             otex.u = otex.d*(t*((float)(fglobalang-(y<<(11-dapskybits)))) * (1.f/2048.f) + xpanning) - xtex.u*ghalfx;
5662                         y++;
5663                         o.x = fx; fx = ((float)((y<<(11-dapskybits))-fglobalang))*o.z+ghalfx;
5664                         if (fx > x1) { fx = x1; i = -1; }
5665 
5666                         pow2xsplit = 0; polymost_domost(o.x,(o.x-x0)*r+fy0,fx,(fx-x0)*r+fy0); //flor
5667                     }
5668                     while (i >= 0);
5669                 }
5670                 ghoriz = ghorizbak;
5671             }
5672             else  //NOTE: code copied from ceiling code... lots of duplicated stuff :/
5673             {
5674                 //Skybox code for parallax floor!
5675                 float sky_t0, sky_t1; // _nx0, _ny0, _nx1, _ny1;
5676                 float sky_ryp0, sky_ryp1, sky_x0, sky_x1, sky_cy0, sky_fy0, sky_cy1, sky_fy1, sky_ox0, sky_ox1;
5677                 static vec2f_t const skywal[4] = { { -512, -512 }, { 512, -512 }, { 512, 512 }, { -512, 512 } };
5678 
5679                 pow2xsplit = 0;
5680                 skyclamphack = 1;
5681 
5682                 for (bssize_t i=0; i<4; i++)
5683                 {
5684                     walpos = skywal[i&3];
5685                     vec2f_t skyp0 = { walpos.y * gcosang - walpos.x * gsinang,
5686                                       walpos.x * gcosang2 + walpos.y * gsinang2 };
5687 
5688                     walpos = skywal[(i + 1) & 3];
5689                     vec2f_t skyp1 = { walpos.y * gcosang - walpos.x * gsinang,
5690                                       walpos.x * gcosang2 + walpos.y * gsinang2 };
5691 
5692                     vec2f_t const oskyp0 = skyp0;
5693 
5694                     //Clip to close parallel-screen plane
5695                     if (skyp0.y < SCISDIST)
5696                     {
5697                         if (skyp1.y < SCISDIST) continue;
5698                         sky_t0 = (SCISDIST - skyp0.y) / (skyp1.y - skyp0.y);
5699                         skyp0  = { (skyp1.x - skyp0.x) * sky_t0 + skyp0.x, SCISDIST };
5700                     }
5701                     else { sky_t0 = 0.f; }
5702 
5703                     if (skyp1.y < SCISDIST)
5704                     {
5705                         sky_t1  = (SCISDIST - oskyp0.y) / (skyp1.y - oskyp0.y);
5706                         skyp1 = { (skyp1.x - oskyp0.x) * sky_t1 + oskyp0.x, SCISDIST };
5707                     }
5708                     else { sky_t1 = 1.f; }
5709 
5710                     sky_ryp0 = 1.f/skyp0.y; sky_ryp1 = 1.f/skyp1.y;
5711 
5712                     //Generate screen coordinates for front side of wall
5713                     sky_x0 = ghalfx*skyp0.x*sky_ryp0 + ghalfx;
5714                     sky_x1 = ghalfx*skyp1.x*sky_ryp1 + ghalfx;
5715                     if ((sky_x1 <= sky_x0) || (sky_x0 >= x1) || (x0 >= sky_x1)) continue;
5716 
5717                     sky_ryp0 *= gyxscale; sky_ryp1 *= gyxscale;
5718 
5719                     sky_cy0 = -8192.f*sky_ryp0 + ghoriz;
5720                     sky_fy0 =  8192.f*sky_ryp0 + ghoriz;
5721                     sky_cy1 = -8192.f*sky_ryp1 + ghoriz;
5722                     sky_fy1 =  8192.f*sky_ryp1 + ghoriz;
5723 
5724                     sky_ox0 = sky_x0; sky_ox1 = sky_x1;
5725 
5726                     //Make sure: x0<=_x0<_x1<=x1
5727                     float nfy[2] = { fy0, fy1 };
5728 
5729                     if (sky_x0 < x0)
5730                     {
5731                         float const t = (x0-sky_x0)/(sky_x1-sky_x0);
5732                         sky_cy0 += (sky_cy1-sky_cy0)*t;
5733                         sky_fy0 += (sky_fy1-sky_fy0)*t;
5734                         sky_x0 = x0;
5735                     }
5736                     else if (sky_x0 > x0) nfy[0] += (sky_x0-x0)*(fy1-fy0)/(x1-x0);
5737 
5738                     if (sky_x1 > x1)
5739                     {
5740                         float const t = (x1-sky_x1)/(sky_x1-sky_x0);
5741                         sky_cy1 += (sky_cy1-sky_cy0)*t;
5742                         sky_fy1 += (sky_fy1-sky_fy0)*t;
5743                         sky_x1 = x1;
5744                     }
5745                     else if (sky_x1 < x1) nfy[1] += (sky_x1-x1)*(fy1-fy0)/(x1-x0);
5746 
5747                     //   (skybox floor)
5748                     //(_x0,_fy0)-(_x1,_fy1)
5749                     //   (skybox wall)
5750                     //(_x0,_cy0)-(_x1,_cy1)
5751                     //   (skybox ceiling)
5752                     //(_x0,nfy0)-(_x1,nfy1)
5753 
5754                     //floor of skybox
5755                     drawingskybox = 6; //floor/6th texture/index 5 of skybox
5756                     float const ft[4] = { 512 / 16, 512 / -16, fcosglobalang * (1.f / 2147483648.f),
5757                                           fsinglobalang * (1.f / 2147483648.f) };
5758 
5759                     xtex.d = 0;
5760                     ytex.d = gxyaspect*(1.0/4194304.0);
5761                     otex.d = -ghoriz*ytex.d;
5762                     xtex.u = ft[3]*fviewingrange*(-1.0/65536.0);
5763                     xtex.v = ft[2]*fviewingrange*(-1.0/65536.0);
5764                     ytex.u = ft[0]*ytex.d; ytex.v = ft[1]*ytex.d;
5765                     otex.u = ft[0]*otex.d; otex.v = ft[1]*otex.d;
5766                     otex.u += (ft[2]-xtex.u)*ghalfx;
5767                     otex.v -= (ft[3]+xtex.v)*ghalfx;
5768                     xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; //y-flip skybox floor
5769 
5770                     if ((sky_fy0 > nfy[0]) && (sky_fy1 > nfy[1]))
5771                         polymost_domost(sky_x0,sky_fy0,sky_x1,sky_fy1);
5772                     else if ((sky_fy0 > nfy[0]) != (sky_fy1 > nfy[1]))
5773                     {
5774                         //(ox,oy) is intersection of: (_x0,_fy0)-(_x1,_fy1)
5775                         //                            (_x0,nfy0)-(_x1,nfy1)
5776                         float const t = (sky_fy0-nfy[0])/(nfy[1]-nfy[0]-sky_fy1+sky_fy0);
5777                         vec2f_t const o = { sky_x0 + (sky_x1-sky_x0)*t, sky_fy0 + (sky_fy1-sky_fy0)*t };
5778                         if (nfy[0] > sky_fy0)
5779                         {
5780                             polymost_domost(sky_x0,nfy[0],o.x,o.y);
5781                             polymost_domost(o.x,o.y,sky_x1,sky_fy1);
5782                         }
5783                         else
5784                         {
5785                             polymost_domost(sky_x0,sky_fy0,o.x,o.y);
5786                             polymost_domost(o.x,o.y,sky_x1,nfy[1]);
5787                         }
5788                     }
5789                     else
5790                         polymost_domost(sky_x0,nfy[0],sky_x1,nfy[1]);
5791 
5792                     //wall of skybox
5793                     drawingskybox = i+1; //i+1th texture/index i of skybox
5794                     xtex.d = (sky_ryp0-sky_ryp1)*gxyaspect*(1.0/512.0) / (sky_ox0-sky_ox1);
5795                     ytex.d = 0;
5796                     otex.d = sky_ryp0*gxyaspect*(1.0/512.0) - xtex.d*sky_ox0;
5797                     xtex.u = (sky_t0*sky_ryp0 - sky_t1*sky_ryp1)*gxyaspect*(64.0/512.0) / (sky_ox0-sky_ox1);
5798                     otex.u = sky_t0*sky_ryp0*gxyaspect*(64.0/512.0) - xtex.u*sky_ox0;
5799                     ytex.u = 0;
5800                     sky_t0 = -8192.f*sky_ryp0 + ghoriz;
5801                     sky_t1 = -8192.f*sky_ryp1 + ghoriz;
5802                     float const t = ((xtex.d*sky_ox0 + otex.d)*8.f) / ((sky_ox1-sky_ox0) * sky_ryp0 * 2048.f);
5803                     xtex.v = (sky_t0-sky_t1)*t;
5804                     ytex.v = (sky_ox1-sky_ox0)*t;
5805                     otex.v = -xtex.v*sky_ox0 - ytex.v*sky_t0;
5806 
5807                     if ((sky_cy0 > nfy[0]) && (sky_cy1 > nfy[1]))
5808                         polymost_domost(sky_x0,sky_cy0,sky_x1,sky_cy1);
5809                     else if ((sky_cy0 > nfy[0]) != (sky_cy1 > nfy[1]))
5810                     {
5811                         //(ox,oy) is intersection of: (_x0,_fy0)-(_x1,_fy1)
5812                         //                            (_x0,nfy0)-(_x1,nfy1)
5813                         float const t = (sky_cy0-nfy[0])/(nfy[1]-nfy[0]-sky_cy1+sky_cy0);
5814                         vec2f_t const o = { sky_x0 + (sky_x1 - sky_x0) * t, sky_cy0 + (sky_cy1 - sky_cy0) * t };
5815                         if (nfy[0] > sky_cy0)
5816                         {
5817                             polymost_domost(sky_x0,nfy[0],o.x,o.y);
5818                             polymost_domost(o.x,o.y,sky_x1,sky_cy1);
5819                         }
5820                         else
5821                         {
5822                             polymost_domost(sky_x0,sky_cy0,o.x,o.y);
5823                             polymost_domost(o.x,o.y,sky_x1,nfy[1]);
5824                         }
5825                     }
5826                     else
5827                         polymost_domost(sky_x0,nfy[0],sky_x1,nfy[1]);
5828                 }
5829 
5830                 //Ceiling of skybox
5831                 drawingskybox = 5; //ceiling/5th texture/index 4 of skybox
5832                 float const ft[4] = { 512 / 16, -512 / -16, fcosglobalang * (1.f / 2147483648.f),
5833                                       fsinglobalang * (1.f / 2147483648.f) };
5834 
5835                 xtex.d = 0;
5836                 ytex.d = gxyaspect*(-1.0/4194304.0);
5837                 otex.d = -ghoriz*ytex.d;
5838                 xtex.u = ft[3]*fviewingrange*(-1.0/65536.0);
5839                 xtex.v = ft[2]*fviewingrange*(-1.0/65536.0);
5840                 ytex.u = ft[0]*ytex.d; ytex.v = ft[1]*ytex.d;
5841                 otex.u = ft[0]*otex.d; otex.v = ft[1]*otex.d;
5842                 otex.u += (ft[2]-xtex.u)*ghalfx;
5843                 otex.v -= (ft[3]+xtex.v)*ghalfx;
5844 
5845                 polymost_domost(x0,fy0,x1,fy1);
5846 
5847                 skyclamphack = 0;
5848                 drawingskybox = 0;
5849             }
5850 
5851             skyclamphack = 0;
5852             skyzbufferhack = 0;
5853             if (!nofog)
5854                 polymost_setFogEnabled(true);
5855         }
5856 #ifdef YAX_ENABLE
5857         }
5858         else
5859         {
5860             g_nodraw = 1;
5861             yax_drawcf = -1;
5862             polymost_domost(x0, fy0, x1, fy1);
5863             g_nodraw = 0;
5864         }
5865 #endif
5866 
5867         doeditorcheck = 0;
5868 
5869         // Ceiling
5870 
5871         if (searchit == 2
5872 #ifdef YAX_ENABLE
5873             && (yax_getbunch(sectnum, YAX_CEILING) < 0 || showinvisibility || (sec->floorstat&(256+128)) || klabs(yax_globallev-YAX_MAXDRAWS)==YAX_MAXDRAWS)
5874 #endif
5875             )
5876         {
5877             psectnum = sectnum;
5878             pwallnum = wallnum;
5879             psearchstat = 1;
5880             doeditorcheck = 1;
5881         }
5882 
5883 #ifdef YAX_ENABLE
5884         yax_holencf[YAX_CEILING] = 0;
5885         yax_drawcf = YAX_CEILING;
5886 #endif
5887 
5888         globalpicnum = sec->ceilingpicnum;
5889         globalshade = sec->ceilingshade;
5890         globalpal = sec->ceilingpal;
5891         globalorientation = sec->ceilingstat;
5892         globvis = (sector[sectnum].visibility != 0) ?
5893                   mulscale4(globalcisibility, (uint8_t)(sector[sectnum].visibility + 16)) :
5894                   globalcisibility;
5895         globvis2 = (sector[sectnum].visibility != 0) ?
5896                   mulscale4(globalcisibility2, (uint8_t)(sector[sectnum].visibility + 16)) :
5897                   globalcisibility2;
5898         polymost_setVisibility(globvis2);
5899 
5900         tileUpdatePicnum(&globalpicnum, sectnum);
5901 
5902 
5903         dapskyoff = getpsky(globalpicnum, &dapyscale, &dapskybits, &dapyoffs, &daptileyscale);
5904 
5905         global_cf_fogpal = sec->fogpal;
5906         global_cf_shade = sec->ceilingshade, global_cf_pal = sec->ceilingpal; global_cf_z = sec->ceilingz;  // REFACT
5907         global_cf_xpanning = sec->ceilingxpanning; global_cf_ypanning = sec->ceilingypanning, global_cf_heinum = sec->ceilingheinum;
5908         global_getzofslope_func = &fgetceilzofslope;
5909 
5910 #ifdef YAX_ENABLE
5911         // this is to prevent double-drawing of translucent masked ceilings
5912         if (g_nodraw || r_tror_nomaskpass==0 || yax_globallev==YAX_MAXDRAWS || (sec->ceilingstat&256)==0 ||
5913             yax_nomaskpass==1 || !(yax_gotsector[sectnum>>3]&pow2char[sectnum&7]))
5914         {
5915 #endif
5916         if (globalpicnum >= r_rortexture && globalpicnum < r_rortexture + r_rortexturerange && r_rorphase == 0)
5917         {
5918             xtex.d = (ryp0-ryp1)*gxyaspect / (x0-x1);
5919             ytex.d = 0;
5920             otex.d = ryp0*gxyaspect - xtex.d*x0;
5921 
5922             xtex.u = ytex.u = otex.u = 0;
5923             xtex.v = ytex.v = otex.v = 0;
5924             polymost_domost(x1, cy1, x0, cy0);
5925         }
5926         else if (!(globalorientation&1))
5927         {
5928             int32_t cz = getceilzofslope(sectnum, globalposx, globalposy);
5929             if (globalposz >= cz)
5930                 polymost_internal_nonparallaxed(n0, n1, ryp0, ryp1, x0, x1, cy0, cy1, sectnum);
5931 #ifdef YAX_ENABLE
5932             else if (g_nodraw && (checkcf & 1) != 0)
5933                 polymost_domost(x1, cy1, x0, cy0);
5934 #endif
5935         }
5936         else if ((nextsectnum < 0) || (!(sector[nextsectnum].ceilingstat&1)))
5937         {
5938             //Parallaxing sky... hacked for Ken's mountain texture
5939             if (!polymost_usetileshades() || (usehightile && hicfindsubst(globalpicnum, globalpal, hictinting[globalpal].f & HICTINT_ALWAYSUSEART)))
5940                 calc_and_apply_fog_factor(sec->ceilingshade, sec->visibility, sec->ceilingpal, 0.005f);
5941 
5942             globvis2 = globalpisibility;
5943             if (sec->visibility != 0)
5944                 globvis2 = mulscale4(globvis2, (uint8_t)(sec->visibility + 16));
5945             float viscale = xdimscale*fxdimen*(.0000001f/256.f);
5946             polymost_setVisibility(globvis2*viscale);
5947 
5948             //Use clamping for tiled sky textures
5949             //(don't wrap around edges if the sky use multiple panels)
5950             for (bssize_t i=(1<<dapskybits)-1; i>0; i--)
5951                 if (dapskyoff[i] != dapskyoff[i-1])
5952                     { skyclamphack = r_parallaxskyclamping; break; }
5953 
5954             skyzbufferhack = 1;
5955 
5956             if (!usehightile || !hicfindskybox(globalpicnum, globalpal))
5957             {
5958                 float const ghorizbak = ghoriz;
5959                 if (r_flatsky && ! r_yshearing)
5960                 {
5961                     pow2xsplit = 0;
5962                     skyclamphack = 0;
5963                     flatskyrender = 1;
5964                     globalshade += globvis2*xdimscale*fviewingrange*(1.f / (64.f * 65536.f * 256.f * 1024.f));
5965                     polymost_setVisibility(0.f);
5966                     polymost_domost(x1,cy1,x0,cy0);
5967                     flatskyrender = 0;
5968                 }
5969                 else
5970                 {
5971                     if (r_yshearing)
5972                         ghoriz = (qglobalhoriz*(1.f/65536.f)-float(ydimen>>1))*(dapyscale-65536.f)*(1.f/65536.f)+float(ydimen>>1);
5973 
5974                     float const dd = fxdimen*.0000001f; //Adjust sky depth based on screen size!
5975                     float vv[2];
5976                     float t = (float)((1<<(picsiz[globalpicnum]&15))<<dapskybits);
5977                     vv[1] = dd*((float)xdimscale*fviewingrange) * (1.f/(daptileyscale*65536.f));
5978                     vv[0] = dd*((float)((tilesiz[globalpicnum].y>>1)+dapyoffs)) - vv[1]*ghoriz;
5979                     int i = (1<<(picsiz[globalpicnum]>>4)); if (i != tilesiz[globalpicnum].y) i += i;
5980                     vec3f_t o;
5981 
5982                     if ((tilesiz[globalpicnum].y * daptileyscale * (1.f/65536.f)) > 256 && !r_yshearing)
5983                     {
5984 #if 0
5985                         //Hack to draw black rectangle below sky when looking down...
5986                         xtex.d = xtex.u = xtex.v = 0;
5987 
5988                         ytex.d = gxyaspect * (1.f / 262144.f);
5989                         ytex.u = 0;
5990                         ytex.v = (float)(tilesiz[globalpicnum].y - 1) * ytex.d;
5991 
5992                         otex.d = -ghoriz * ytex.d;
5993                         otex.u = 0;
5994                         otex.v = (float)(tilesiz[globalpicnum].y - 1) * otex.d;
5995 
5996                         o.y = ((float)tilesiz[globalpicnum].y*dd-vv[0])/vv[1];
5997 
5998                         if ((o.y > cy0) && (o.y > cy1))
5999                             polymost_domost(x0,o.y,x1,o.y);
6000                         else if ((o.y > cy0) != (o.y > cy1))
6001                         {
6002                             o.x = (o.y-cy0)*(x1-x0)/(cy1-cy0) + x0;
6003                             if (o.y > cy0)
6004                             {
6005                                 polymost_domost(x0,o.y,o.x,o.y);
6006                                 polymost_domost(o.x,o.y,x1,cy1);
6007                             }
6008                             else
6009                             {
6010                                 polymost_domost(x0,cy0,o.x,o.y);
6011                                 polymost_domost(o.x,o.y,x1,o.y);
6012                             }
6013                         }
6014                         else
6015                             polymost_domost(x0,cy0,x1,cy1);
6016 #endif
6017 
6018                         //Hack to draw color rectangle above sky when looking up...
6019                         xtex.d = xtex.u = xtex.v = 0;
6020 
6021                         ytex.d = gxyaspect * (1.0 / -262144.0);
6022                         ytex.u = 0;
6023                         ytex.v = 0;
6024 
6025                         otex.d = -ghoriz * ytex.d;
6026                         otex.u = 0;
6027                         otex.v = 0;
6028 
6029                         o.y = -vv[0]/vv[1];
6030 
6031                         if ((o.y < cy0) && (o.y < cy1))
6032                             polymost_domost(x1,o.y,x0,o.y);
6033                         else if ((o.y < cy0) != (o.y < cy1))
6034                         {
6035                             /*         cy1        cy0
6036                             //        /              \
6037                             //oy----------      oy---------
6038                             //    /                   \
6039                             //  cy0                     cy1 */
6040                             o.x = (o.y-cy0)*(x1-x0)/(cy1-cy0) + x0;
6041                             if (o.y < cy0)
6042                             {
6043                                 polymost_domost(o.x,o.y,x0,o.y);
6044                                 polymost_domost(x1,cy1,o.x,o.y);
6045                             }
6046                             else
6047                             {
6048                                 polymost_domost(o.x,o.y,x0,cy0);
6049                                 polymost_domost(x1,o.y,o.x,o.y);
6050                             }
6051                         }
6052                         else
6053                             polymost_domost(x1,cy1,x0,cy0);
6054                     }
6055                     else
6056                         skyclamphack = 0;
6057 
6058                     xtex.d = xtex.v = 0;
6059                     ytex.d = ytex.u = 0;
6060                     otex.d = dd;
6061                     xtex.u = otex.d * (t * double(((uint64_t)xdimscale * yxaspect) * viewingrange)) *
6062                                       (1.0 / (16384.0 * 65536.0 * 65536.0 * 5.0 * 1024.0));
6063                     ytex.v = vv[1];
6064                     otex.v = r_parallaxskypanning ? vv[0] + dd*(float)sec->ceilingypanning*(float)i*(1.f/256.f) : vv[0];
6065 
6066                     int const npot = (1<<(picsiz[globalpicnum]&15)) != tilesiz[globalpicnum].x;
6067                     int const xpanning = (r_parallaxskypanning?sec->ceilingxpanning:0);
6068 
6069                     i = globalpicnum;
6070                     float const r = (cy1-cy0)/(x1-x0); //slope of line
6071                     o.y = fviewingrange/(ghalfx*256.f); o.z = 1.f/o.y;
6072 
6073                     int y = ((int32_t)(((x0-ghalfx)*o.y)+fglobalang)>>(11-dapskybits));
6074                     float fx = x0;
6075                     do
6076                     {
6077                         globalpicnum = dapskyoff[y&((1<<dapskybits)-1)]+i;
6078                         if (npot)
6079                         {
6080                             float fx = ((float)((y<<(11-dapskybits))-fglobalang))*o.z+ghalfx;
6081                             int tang = (y<<(11-dapskybits))&2047;
6082                             otex.u = otex.d*(t*((float)(tang)) * (1.f/2048.f) + xpanning) - xtex.u*fx;
6083                         }
6084                         else
6085                             otex.u = otex.d*(t*((float)(fglobalang-(y<<(11-dapskybits)))) * (1.f/2048.f) + xpanning) - xtex.u*ghalfx;
6086                         y++;
6087                         o.x = fx; fx = (((float) (y<<(11-dapskybits))-fglobalang))*o.z+ghalfx;
6088                         if (fx > x1) { fx = x1; i = -1; }
6089 
6090                         pow2xsplit = 0; polymost_domost(fx,(fx-x0)*r+cy0,o.x,(o.x-x0)*r+cy0); //ceil
6091                     }
6092                     while (i >= 0);
6093 
6094                 }
6095                 ghoriz = ghorizbak;
6096             }
6097             else
6098             {
6099                 //Skybox code for parallax ceiling!
6100                 float sky_t0, sky_t1; // _nx0, _ny0, _nx1, _ny1;
6101                 float sky_ryp0, sky_ryp1, sky_x0, sky_x1, sky_cy0, sky_fy0, sky_cy1, sky_fy1, sky_ox0, sky_ox1;
6102                 static vec2f_t const skywal[4] = { { -512, -512 }, { 512, -512 }, { 512, 512 }, { -512, 512 } };
6103 
6104                 pow2xsplit = 0;
6105                 skyclamphack = 1;
6106 
6107                 for (bssize_t i=0; i<4; i++)
6108                 {
6109                     walpos = skywal[i&3];
6110                     vec2f_t skyp0 = { walpos.y * gcosang - walpos.x * gsinang,
6111                                       walpos.x * gcosang2 + walpos.y * gsinang2 };
6112 
6113                     walpos = skywal[(i + 1) & 3];
6114                     vec2f_t skyp1 = { walpos.y * gcosang - walpos.x * gsinang,
6115                                       walpos.x * gcosang2 + walpos.y * gsinang2 };
6116 
6117                     vec2f_t const oskyp0 = skyp0;
6118 
6119                     //Clip to close parallel-screen plane
6120                     if (skyp0.y < SCISDIST)
6121                     {
6122                         if (skyp1.y < SCISDIST) continue;
6123                         sky_t0 = (SCISDIST - skyp0.y) / (skyp1.y - skyp0.y);
6124                         skyp0  = { (skyp1.x - skyp0.x) * sky_t0 + skyp0.x, SCISDIST };
6125                     }
6126                     else { sky_t0 = 0.f; }
6127 
6128                     if (skyp1.y < SCISDIST)
6129                     {
6130                         sky_t1 = (SCISDIST - oskyp0.y) / (skyp1.y - oskyp0.y);
6131                         skyp1  = { (skyp1.x - oskyp0.x) * sky_t1 + oskyp0.x, SCISDIST };
6132                     }
6133                     else { sky_t1 = 1.f; }
6134 
6135                     sky_ryp0 = 1.f/skyp0.y; sky_ryp1 = 1.f/skyp1.y;
6136 
6137                     //Generate screen coordinates for front side of wall
6138                     sky_x0 = ghalfx*skyp0.x*sky_ryp0 + ghalfx;
6139                     sky_x1 = ghalfx*skyp1.x*sky_ryp1 + ghalfx;
6140                     if ((sky_x1 <= sky_x0) || (sky_x0 >= x1) || (x0 >= sky_x1)) continue;
6141 
6142                     sky_ryp0 *= gyxscale; sky_ryp1 *= gyxscale;
6143 
6144                     sky_cy0 = -8192.f*sky_ryp0 + ghoriz;
6145                     sky_fy0 =  8192.f*sky_ryp0 + ghoriz;
6146                     sky_cy1 = -8192.f*sky_ryp1 + ghoriz;
6147                     sky_fy1 =  8192.f*sky_ryp1 + ghoriz;
6148 
6149                     sky_ox0 = sky_x0; sky_ox1 = sky_x1;
6150 
6151                     //Make sure: x0<=_x0<_x1<=x1
6152                     float ncy[2] = { cy0, cy1 };
6153 
6154                     if (sky_x0 < x0)
6155                     {
6156                         float const t = (x0-sky_x0)/(sky_x1-sky_x0);
6157                         sky_cy0 += (sky_cy1-sky_cy0)*t;
6158                         sky_fy0 += (sky_fy1-sky_fy0)*t;
6159                         sky_x0 = x0;
6160                     }
6161                     else if (sky_x0 > x0) ncy[0] += (sky_x0-x0)*(cy1-cy0)/(x1-x0);
6162 
6163                     if (sky_x1 > x1)
6164                     {
6165                         float const t = (x1-sky_x1)/(sky_x1-sky_x0);
6166                         sky_cy1 += (sky_cy1-sky_cy0)*t;
6167                         sky_fy1 += (sky_fy1-sky_fy0)*t;
6168                         sky_x1 = x1;
6169                     }
6170                     else if (sky_x1 < x1) ncy[1] += (sky_x1-x1)*(cy1-cy0)/(x1-x0);
6171 
6172                     //   (skybox ceiling)
6173                     //(_x0,_cy0)-(_x1,_cy1)
6174                     //   (skybox wall)
6175                     //(_x0,_fy0)-(_x1,_fy1)
6176                     //   (skybox floor)
6177                     //(_x0,ncy0)-(_x1,ncy1)
6178 
6179                     //ceiling of skybox
6180                     drawingskybox = 5; //ceiling/5th texture/index 4 of skybox
6181                     float const ft[4] = { 512 / 16, -512 / -16, fcosglobalang * (1.f / 2147483648.f),
6182                                           fsinglobalang * (1.f / 2147483648.f) };
6183 
6184                     xtex.d = 0;
6185                     ytex.d = gxyaspect*(-1.0/4194304.0);
6186                     otex.d = -ghoriz*ytex.d;
6187                     xtex.u = ft[3]*fviewingrange*(-1.0/65536.0);
6188                     xtex.v = ft[2]*fviewingrange*(-1.0/65536.0);
6189                     ytex.u = ft[0]*ytex.d; ytex.v = ft[1]*ytex.d;
6190                     otex.u = ft[0]*otex.d; otex.v = ft[1]*otex.d;
6191                     otex.u += (ft[2]-xtex.u)*ghalfx;
6192                     otex.v -= (ft[3]+xtex.v)*ghalfx;
6193 
6194 
6195                     if ((sky_cy0 < ncy[0]) && (sky_cy1 < ncy[1]))
6196                         polymost_domost(sky_x1,sky_cy1,sky_x0,sky_cy0);
6197                     else if ((sky_cy0 < ncy[0]) != (sky_cy1 < ncy[1]))
6198                     {
6199                         //(ox,oy) is intersection of: (_x0,_cy0)-(_x1,_cy1)
6200                         //                            (_x0,ncy0)-(_x1,ncy1)
6201                         float const t = (sky_cy0-ncy[0])/(ncy[1]-ncy[0]-sky_cy1+sky_cy0);
6202                         vec2f_t const o = { sky_x0 + (sky_x1-sky_x0)*t, sky_cy0 + (sky_cy1-sky_cy0)*t };
6203                         if (ncy[0] < sky_cy0)
6204                         {
6205                             polymost_domost(o.x,o.y,sky_x0,ncy[0]);
6206                             polymost_domost(sky_x1,sky_cy1,o.x,o.y);
6207                         }
6208                         else
6209                         {
6210                             polymost_domost(o.x,o.y,sky_x0,sky_cy0);
6211                             polymost_domost(sky_x1,ncy[1],o.x,o.y);
6212                         }
6213                     }
6214                     else
6215                         polymost_domost(sky_x1,ncy[1],sky_x0,ncy[0]);
6216 
6217                     //wall of skybox
6218                     drawingskybox = i+1; //i+1th texture/index i of skybox
6219                     xtex.d = (sky_ryp0-sky_ryp1)*gxyaspect*(1.0/512.0) / (sky_ox0-sky_ox1);
6220                     ytex.d = 0;
6221                     otex.d = sky_ryp0*gxyaspect*(1.0/512.0) - xtex.d*sky_ox0;
6222                     xtex.u = (sky_t0*sky_ryp0 - sky_t1*sky_ryp1)*gxyaspect*(64.0/512.0) / (sky_ox0-sky_ox1);
6223                     otex.u = sky_t0*sky_ryp0*gxyaspect*(64.0/512.0) - xtex.u*sky_ox0;
6224                     ytex.u = 0;
6225                     sky_t0 = -8192.f*sky_ryp0 + ghoriz;
6226                     sky_t1 = -8192.f*sky_ryp1 + ghoriz;
6227                     float const t = ((xtex.d*sky_ox0 + otex.d)*8.f) / ((sky_ox1-sky_ox0) * sky_ryp0 * 2048.f);
6228                     xtex.v = (sky_t0-sky_t1)*t;
6229                     ytex.v = (sky_ox1-sky_ox0)*t;
6230                     otex.v = -xtex.v*sky_ox0 - ytex.v*sky_t0;
6231 
6232                     if ((sky_fy0 < ncy[0]) && (sky_fy1 < ncy[1]))
6233                         polymost_domost(sky_x1,sky_fy1,sky_x0,sky_fy0);
6234                     else if ((sky_fy0 < ncy[0]) != (sky_fy1 < ncy[1]))
6235                     {
6236                         //(ox,oy) is intersection of: (_x0,_fy0)-(_x1,_fy1)
6237                         //                            (_x0,ncy0)-(_x1,ncy1)
6238                         float const t = (sky_fy0-ncy[0])/(ncy[1]-ncy[0]-sky_fy1+sky_fy0);
6239                         vec2f_t const o = { sky_x0 + (sky_x1 - sky_x0) * t, sky_fy0 + (sky_fy1 - sky_fy0) * t };
6240                         if (ncy[0] < sky_fy0)
6241                         {
6242                             polymost_domost(o.x,o.y,sky_x0,ncy[0]);
6243                             polymost_domost(sky_x1,sky_fy1,o.x,o.y);
6244                         }
6245                         else
6246                         {
6247                             polymost_domost(o.x,o.y,sky_x0,sky_fy0);
6248                             polymost_domost(sky_x1,ncy[1],o.x,o.y);
6249                         }
6250                     }
6251                     else
6252                         polymost_domost(sky_x1,ncy[1],sky_x0,ncy[0]);
6253                 }
6254 
6255                 //Floor of skybox
6256                 drawingskybox = 6; //floor/6th texture/index 5 of skybox
6257                 float const ft[4] = { 512 / 16, 512 / -16, fcosglobalang * (1.f / 2147483648.f),
6258                                       fsinglobalang * (1.f / 2147483648.f) };
6259 
6260                 xtex.d = 0;
6261                 ytex.d = gxyaspect*(1.0/4194304.0);
6262                 otex.d = -ghoriz*ytex.d;
6263                 xtex.u = ft[3]*fviewingrange*(-1.0/65536.0);
6264                 xtex.v = ft[2]*fviewingrange*(-1.0/65536.0);
6265                 ytex.u = ft[0]*ytex.d; ytex.v = ft[1]*ytex.d;
6266                 otex.u = ft[0]*otex.d; otex.v = ft[1]*otex.d;
6267                 otex.u += (ft[2]-xtex.u)*ghalfx;
6268                 otex.v -= (ft[3]+xtex.v)*ghalfx;
6269                 xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; //y-flip skybox floor
6270                 polymost_domost(x1,cy1,x0,cy0);
6271 
6272                 skyclamphack = 0;
6273                 drawingskybox = 0;
6274             }
6275 
6276             skyclamphack = 0;
6277             skyzbufferhack = 0;
6278             if (!nofog)
6279                 polymost_setFogEnabled(true);
6280         }
6281 #ifdef YAX_ENABLE
6282         }
6283         else
6284         {
6285             g_nodraw = 1;
6286             yax_drawcf = -1;
6287             polymost_domost(x1, cy1, x0, cy0);
6288             g_nodraw = 0;
6289         }
6290 #endif
6291 
6292         doeditorcheck = 0;
6293 
6294 #ifdef YAX_ENABLE
6295         if (g_nodraw)
6296         {
6297             int32_t i, j;
6298             for (i=0; i<2; i++)
6299                 if (checkcf&(1<<i))
6300                 {
6301                     if ((haveymost[bn[i]>>3]&pow2char[bn[i]&7])==0)
6302                     {
6303                         // init yax *most arrays for that bunch
6304                         haveymost[bn[i]>>3] |= pow2char[bn[i]&7];
6305                         yax_vsp[bn[i]*2][1].x = xbl;
6306                         yax_vsp[bn[i]*2][2].x = xbr;
6307                         yax_vsp[bn[i]*2][1].cy[0] = xbb;
6308                         yax_vsp[bn[i]*2][2].cy[0] = xbb;
6309                         yax_vsp_finalize_init(bn[i]*2, 3);
6310                         yax_vsp[bn[i]*2+1][1].x = xbl;
6311                         yax_vsp[bn[i]*2+1][2].x = xbr;
6312                         yax_vsp[bn[i]*2+1][1].cy[0] = xbt;
6313                         yax_vsp[bn[i]*2+1][2].cy[0] = xbt;
6314                         yax_vsp_finalize_init(bn[i]*2+1, 3);
6315                     }
6316 
6317                     for (j = 0; j < yax_holencf[i]; j++)
6318                     {
6319                         yax_hole_t *hole = &yax_holecf[i][j];
6320                         yax_polymost_domost(bn[i]*2, hole->x0, hole->cy[0], hole->x1, hole->cy[1]);
6321                         yax_polymost_domost(bn[i]*2+1, hole->x1, hole->fy[1], hole->x0, hole->fy[0]);
6322                     }
6323                 }
6324         }
6325 #endif
6326 
6327         // Wall
6328 
6329 #ifdef YAX_ENABLE
6330         yax_drawcf = -1;
6331 #endif
6332 
6333         xtex.d = (ryp0-ryp1)*gxyaspect / (x0-x1);
6334         ytex.d = 0;
6335         otex.d = ryp0*gxyaspect - xtex.d*x0;
6336 
6337         xtex.u = (t0*ryp0 - t1*ryp1)*gxyaspect*(float)wal->xrepeat*8.f / (x0-x1);
6338         otex.u = t0*ryp0*gxyaspect*wal->xrepeat*8.0 - xtex.u*x0;
6339         otex.u += (float)wal->xpanning*otex.d;
6340         xtex.u += (float)wal->xpanning*xtex.d;
6341         ytex.u = 0;
6342 
6343         float const ogux = xtex.u, oguy = ytex.u, oguo = otex.u;
6344 
6345         Bassert(domostpolymethod == DAMETH_NOMASK);
6346         domostpolymethod = DAMETH_WALL;
6347 
6348 #ifdef YAX_ENABLE
6349         if (yax_nomaskpass==0 || !yax_isislandwall(wallnum, !yax_globalcf) || (yax_nomaskdidit=1, 0))
6350 #endif
6351         if (nextsectnum >= 0)
6352         {
6353             fgetzsofslope((usectorptr_t)&sector[nextsectnum],n0.x,n0.y,&cz,&fz);
6354             float const ocy0 = (cz-globalposz)*ryp0 + ghoriz;
6355             float const ofy0 = (fz-globalposz)*ryp0 + ghoriz;
6356             fgetzsofslope((usectorptr_t)&sector[nextsectnum],n1.x,n1.y,&cz,&fz);
6357             float const ocy1 = (cz-globalposz)*ryp1 + ghoriz;
6358             float const ofy1 = (fz-globalposz)*ryp1 + ghoriz;
6359 
6360             if ((wal->cstat&48) == 16) maskwall[maskwallcnt++] = z;
6361 
6362             if (((cy0 < ocy0) || (cy1 < ocy1)) && (!((sec->ceilingstat&sector[nextsectnum].ceilingstat)&1)))
6363             {
6364                 if (searchit == 2)
6365                 {
6366                     psectnum = sectnum;
6367                     pbottomwall = pwallnum = wallnum;
6368                     pisbottomwall = 0;
6369                     psearchstat = 0;
6370                     doeditorcheck = 1;
6371                 }
6372                 globalpicnum = wal->picnum; globalshade = wal->shade; globalpal = (int32_t)((uint8_t)wal->pal);
6373                 globvis = globalvisibility;
6374                 if (sector[sectnum].visibility != 0) globvis = mulscale4(globvis, (uint8_t)(sector[sectnum].visibility+16));
6375                 globvis2 = globalvisibility2;
6376                 if (sector[sectnum].visibility != 0) globvis2 = mulscale4(globvis2, (uint8_t)(sector[sectnum].visibility+16));
6377                 polymost_setVisibility(globvis2);
6378                 globalorientation = wal->cstat;
6379                 tileUpdatePicnum(&globalpicnum, wallnum+16384);
6380 
6381                 int i = (!(wal->cstat&4)) ? sector[nextsectnum].ceilingz : sec->ceilingz;
6382 
6383                 // over
6384                 calc_ypanning(i, ryp0, ryp1, x0, x1, wal->ypanning, wal->yrepeat, wal->cstat&4);
6385 
6386                 if (wal->cstat&8) //xflip
6387                 {
6388                     float const t = (float)(wal->xrepeat*8 + wal->xpanning*2);
6389                     xtex.u = xtex.d*t - xtex.u;
6390                     ytex.u = ytex.d*t - ytex.u;
6391                     otex.u = otex.d*t - otex.u;
6392                 }
6393                 if (wal->cstat&256) { xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; } //yflip
6394 
6395                 if (!polymost_usetileshades() || (usehightile && hicfindsubst(globalpicnum, globalpal, hictinting[globalpal].f & HICTINT_ALWAYSUSEART)))
6396                     calc_and_apply_fog(fogshade(wal->shade, wal->pal), sec->visibility, get_floor_fogpal(sec));
6397 
6398                 pow2xsplit = 1;
6399 #ifdef YAX_ENABLE
6400                 if (should_clip_cfwall(x1,cy1,x0,cy0))
6401 #endif
6402                 polymost_domost(x1,ocy1,x0,ocy0,cy1,ocy1,cy0,ocy0);
6403                 if (wal->cstat&8) { xtex.u = ogux; ytex.u = oguy; otex.u = oguo; }
6404 
6405                 doeditorcheck = 0;
6406             }
6407             if (((ofy0 < fy0) || (ofy1 < fy1)) && (!((sec->floorstat&sector[nextsectnum].floorstat)&1)))
6408             {
6409                 uwallptr_t nwal;
6410 
6411                 if (searchit == 2)
6412                 {
6413                     psectnum = sectnum;
6414                     pbottomwall = pwallnum = wallnum;
6415                     pisbottomwall = 1;
6416                     if ((wal->cstat&2) > 0) pbottomwall = wal->nextwall;
6417                     psearchstat = 0;
6418                     doeditorcheck = 1;
6419                 }
6420                 if (!(wal->cstat&2)) nwal = wal;
6421                 else
6422                 {
6423                     nwal = (uwallptr_t)&wall[wal->nextwall];
6424                     otex.u += (float)(nwal->xpanning - wal->xpanning) * otex.d;
6425                     xtex.u += (float)(nwal->xpanning - wal->xpanning) * xtex.d;
6426                     ytex.u += (float)(nwal->xpanning - wal->xpanning) * ytex.d;
6427                 }
6428                 globalpicnum = nwal->picnum; globalshade = nwal->shade; globalpal = (int32_t)((uint8_t)nwal->pal);
6429                 globvis = globalvisibility;
6430                 if (sector[sectnum].visibility != 0) globvis = mulscale4(globvis, (uint8_t)(sector[sectnum].visibility+16));
6431                 globvis2 = globalvisibility2;
6432                 if (sector[sectnum].visibility != 0) globvis2 = mulscale4(globvis2, (uint8_t)(sector[sectnum].visibility+16));
6433                 polymost_setVisibility(globvis2);
6434                 globalorientation = nwal->cstat;
6435                 tileUpdatePicnum(&globalpicnum, wallnum+16384);
6436 
6437                 int i = (!(nwal->cstat&4)) ? sector[nextsectnum].floorz : sec->ceilingz;
6438 
6439                 // under
6440                 calc_ypanning(i, ryp0, ryp1, x0, x1, nwal->ypanning, wal->yrepeat, !(nwal->cstat&4));
6441 
6442                 if (wal->cstat&8) //xflip
6443                 {
6444                     float const t = (float)(wal->xrepeat*8 + nwal->xpanning*2);
6445                     xtex.u = xtex.d*t - xtex.u;
6446                     ytex.u = ytex.d*t - ytex.u;
6447                     otex.u = otex.d*t - otex.u;
6448                 }
6449                 if (nwal->cstat&256) { xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; } //yflip
6450 
6451                 if (!polymost_usetileshades() || (usehightile && hicfindsubst(globalpicnum, globalpal, hictinting[globalpal].f & HICTINT_ALWAYSUSEART)))
6452                     calc_and_apply_fog(fogshade(nwal->shade, nwal->pal), sec->visibility, get_floor_fogpal(sec));
6453 
6454                 pow2xsplit = 1;
6455 #ifdef YAX_ENABLE
6456                 if (should_clip_cfwall(x0,fy0,x1,fy1))
6457 #endif
6458                 polymost_domost(x0,ofy0,x1,ofy1,ofy0,fy0,ofy1,fy1);
6459                 if (wal->cstat&(2+8)) { otex.u = oguo; xtex.u = ogux; ytex.u = oguy; }
6460 
6461                 doeditorcheck = 0;
6462             }
6463         }
6464 
6465         if ((nextsectnum < 0) || (wal->cstat&32))   //White/1-way wall
6466         {
6467             do
6468             {
6469                 const int maskingOneWay = (nextsectnum >= 0 && (wal->cstat&32));
6470 
6471                 if (searchit == 2)
6472                 {
6473                     psectnum = sectnum;
6474                     pbottomwall = pwallnum = wallnum;
6475                     psearchstat = (nextsectnum < 0) ? 0 : 4;
6476                     doeditorcheck = 1;
6477                 }
6478 
6479                 if (maskingOneWay)
6480                 {
6481                     vec2_t n, pos = { globalposx, globalposy };
6482                     if (!polymost_getclosestpointonwall(&pos, wallnum, &n) && klabs(pos.x - n.x) + klabs(pos.y - n.y) <= 128)
6483                         break;
6484                 }
6485 
6486                 globalpicnum = (nextsectnum < 0) ? wal->picnum : wal->overpicnum;
6487 
6488                 globalshade = wal->shade;
6489                 globalpal = wal->pal;
6490                 globvis = (sector[sectnum].visibility != 0) ?
6491                           mulscale4(globalvisibility, (uint8_t)(sector[sectnum].visibility + 16)) :
6492                           globalvisibility;
6493                 globvis2 = (sector[sectnum].visibility != 0) ?
6494                           mulscale4(globalvisibility2, (uint8_t)(sector[sectnum].visibility + 16)) :
6495                           globalvisibility2;
6496                 polymost_setVisibility(globvis2);
6497                 globalorientation = wal->cstat;
6498                 tileUpdatePicnum(&globalpicnum, wallnum+16384);
6499 
6500                 int i;
6501                 int const nwcs4 = !(wal->cstat & 4);
6502 
6503                 if (nextsectnum >= 0) { i = nwcs4 ? nextsec->ceilingz : sec->ceilingz; }
6504                 else { i = nwcs4 ? sec->ceilingz : sec->floorz; }
6505 
6506                 // white / 1-way
6507                 calc_ypanning(i, ryp0, ryp1, x0, x1, wal->ypanning, wal->yrepeat, nwcs4 && !maskingOneWay);
6508 
6509                 if (wal->cstat&8) //xflip
6510                 {
6511                     float const t = (float) (wal->xrepeat*8 + wal->xpanning*2);
6512                     xtex.u = xtex.d*t - xtex.u;
6513                     ytex.u = ytex.d*t - ytex.u;
6514                     otex.u = otex.d*t - otex.u;
6515                 }
6516                 if (wal->cstat&256) { xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; } //yflip
6517 
6518                 if (!polymost_usetileshades() || (usehightile && hicfindsubst(globalpicnum, globalpal, hictinting[globalpal].f & HICTINT_ALWAYSUSEART)))
6519                     calc_and_apply_fog(fogshade(wal->shade, wal->pal), sec->visibility, get_floor_fogpal(sec));
6520 
6521                 pow2xsplit = 1;
6522 
6523 #ifdef YAX_ENABLE
6524                 // TODO: slopes?
6525 
6526                 if (globalposz > sec->floorz && yax_isislandwall(wallnum, YAX_FLOOR))
6527                     polymost_domost(x1, fy1, x0, fy0, cy1, fy1, cy0, fy0);
6528                 else
6529 #endif
6530                     polymost_domost(x0, cy0, x1, cy1, cy0, fy0, cy1, fy1);
6531                 doeditorcheck = 0;
6532             } while (0);
6533         }
6534 
6535         domostpolymethod = DAMETH_NOMASK;
6536 
6537         if (nextsectnum >= 0)
6538             if ((!(gotsector[nextsectnum>>3]&pow2char[nextsectnum&7])) && testvisiblemost(x0,x1))
6539                 polymost_scansector(nextsectnum);
6540     }
6541 }
6542 
polymost_bunchfront(const int32_t b1,const int32_t b2)6543 static int32_t polymost_bunchfront(const int32_t b1, const int32_t b2)
6544 {
6545     int b1f = bunchfirst[b1];
6546     const float x2b2 = dxb2[bunchlast[b2]];
6547     const float x1b1 = dxb1[b1f];
6548 
6549     if (nexttowardf(x1b1, x2b2) >= x2b2)
6550         return -1;
6551 
6552     int b2f = bunchfirst[b2];
6553     const float x1b2 = dxb1[b2f];
6554 
6555     if (nexttowardf(x1b2, dxb2[bunchlast[b1]]) >= dxb2[bunchlast[b1]])
6556         return -1;
6557 
6558     if (nexttowardf(x1b1, x1b2) > x1b2)
6559     {
6560         while (nexttowardf(dxb2[b2f], x1b1) <= x1b1) b2f=bunchp2[b2f];
6561         return wallfront(b1f, b2f);
6562     }
6563 
6564     while (nexttowardf(dxb2[b1f], x1b2) <= x1b2) b1f=bunchp2[b1f];
6565     return wallfront(b1f, b2f);
6566 }
6567 
polymost_scansector(int32_t sectnum)6568 void polymost_scansector(int32_t sectnum)
6569 {
6570     if (sectnum < 0) return;
6571 
6572     if (automapping)
6573         show2dsector[sectnum>>3] |= pow2char[sectnum&7];
6574 
6575     sectorborder[0] = sectnum;
6576     int sectorbordercnt = 1;
6577     do
6578     {
6579         sectnum = sectorborder[--sectorbordercnt];
6580 
6581 #ifdef YAX_ENABLE
6582         if (scansector_collectsprites)
6583 #endif
6584         for (bssize_t z=headspritesect[sectnum]; z>=0; z=nextspritesect[z])
6585         {
6586             auto const spr = (uspriteptr_t)&sprite[z];
6587 
6588             if ((spr->cstat & 0x8000 && !showinvisibility) || spr->xrepeat == 0 || spr->yrepeat == 0)
6589                 continue;
6590 
6591             vec2_t const s = { spr->x-globalposx, spr->y-globalposy };
6592 
6593             if ((spr->cstat&48) ||
6594                 (usemodels && tile2model[spr->picnum].modelid>=0) ||
6595                 ((s.x * gcosang) + (s.y * gsinang) > 0))
6596             {
6597                 if ((spr->cstat&(64+48))!=(64+16) ||
6598                     (usevoxels && tiletovox[spr->picnum] >= 0 && voxmodels[tiletovox[spr->picnum]]) ||
6599                     dmulscale6(sintable[(spr->ang+512)&2047],-s.x, sintable[spr->ang&2047],-s.y) > 0)
6600                     if (renderAddTsprite(z, sectnum))
6601                         break;
6602             }
6603         }
6604 
6605         gotsector[sectnum>>3] |= pow2char[sectnum&7];
6606 
6607         int const bunchfrst = numbunches;
6608         int const onumscans = numscans;
6609         int const startwall = sector[sectnum].wallptr;
6610         int const endwall   = sector[sectnum].wallnum + startwall;
6611 
6612         int scanfirst = numscans;
6613 
6614         vec2d_t p2 = { 0, 0 };
6615 
6616         uwallptr_t wal;
6617         int z;
6618 
6619         for (z=startwall,wal=(uwallptr_t)&wall[z]; z<endwall; z++,wal++)
6620         {
6621             auto const wal2 = (uwallptr_t)&wall[wal->point2];
6622 
6623             vec2d_t const fp1 = { double(wal->x - globalposx), double(wal->y - globalposy) };
6624             vec2d_t const fp2 = { double(wal2->x - globalposx), double(wal2->y - globalposy) };
6625 
6626             int const nextsectnum = wal->nextsector; //Scan close sectors
6627 
6628             if (nextsectnum >= 0 && !(wal->cstat&32) && sectorbordercnt < ARRAY_SSIZE(sectorborder))
6629 #ifdef YAX_ENABLE
6630             if (yax_nomaskpass==0 || !yax_isislandwall(z, !yax_globalcf) || (yax_nomaskdidit=1, 0))
6631 #endif
6632             if ((gotsector[nextsectnum>>3]&pow2char[nextsectnum&7]) == 0)
6633             {
6634                 double const d = fp1.x*fp2.y - fp2.x*fp1.y;
6635                 vec2d_t const p1 = { fp2.x-fp1.x, fp2.y-fp1.y };
6636 
6637                 // this said (SCISDIST*SCISDIST*260.f), but SCISDIST is 1 and the significance of 260 isn't obvious to me
6638                 // is 260 fudged to solve a problem, and does the problem still apply to our version of the renderer?
6639                 if (d*d < (p1.x*p1.x + p1.y*p1.y) * 256.f
6640 #ifdef YAX_ENABLE
6641                     && yax_globallev == YAX_MAXDRAWS
6642 #endif
6643                     )
6644                 {
6645                     sectorborder[sectorbordercnt++] = nextsectnum;
6646                     gotsector[nextsectnum>>3] |= pow2char[nextsectnum&7];
6647                 }
6648             }
6649 
6650             vec2d_t p1;
6651 
6652             if ((z == startwall) || (wall[z-1].point2 != z))
6653             {
6654                 p1 = { (((fp1.y * fcosglobalang) - (fp1.x * fsinglobalang)) * (1.0/64.0)),
6655                        (((fp1.x * cosviewingrangeglobalang) + (fp1.y * sinviewingrangeglobalang)) * (1.0/64.0)) };
6656             }
6657             else { p1 = p2; }
6658 
6659             p2 = { (((fp2.y * fcosglobalang) - (fp2.x * fsinglobalang)) * (1.0/64.0)),
6660                    (((fp2.x * cosviewingrangeglobalang) + (fp2.y * sinviewingrangeglobalang)) * (1.0/64.0)) };
6661 
6662             if (numscans >= MAXWALLSB-1)
6663             {
6664                 OSD_Printf("!!numscans\n");
6665                 return;
6666             }
6667 
6668             //if wall is facing you...
6669             if ((p1.y >= SCISDIST || p2.y >= SCISDIST) && (nexttoward(p1.x*p2.y, p2.x*p1.y) < p2.x*p1.y))
6670             {
6671                 dxb1[numscans] = (p1.y >= SCISDIST) ? float(p1.x*ghalfx/p1.y + ghalfx) : -1e32f;
6672                 dxb2[numscans] = (p2.y >= SCISDIST) ? float(p2.x*ghalfx/p2.y + ghalfx) : 1e32f;
6673 
6674                 if (dxb1[numscans] < xbl)
6675                     dxb1[numscans] = xbl;
6676                 else if (dxb1[numscans] > xbr)
6677                     dxb1[numscans] = xbr;
6678                 if (dxb2[numscans] < xbl)
6679                     dxb2[numscans] = xbl;
6680                 else if (dxb2[numscans] > xbr)
6681                     dxb2[numscans] = xbr;
6682 
6683                 if (nexttowardf(dxb1[numscans], dxb2[numscans]) < dxb2[numscans])
6684                 {
6685                     thesector[numscans] = sectnum;
6686                     thewall[numscans] = z;
6687                     bunchp2[numscans] = numscans + 1;
6688                     numscans++;
6689                 }
6690             }
6691 
6692             if ((wall[z].point2 < z) && (scanfirst < numscans))
6693             {
6694                 bunchp2[numscans-1] = scanfirst;
6695                 scanfirst = numscans;
6696             }
6697         }
6698 
6699         for (bssize_t z=onumscans; z<numscans; z++)
6700         {
6701             if ((wall[thewall[z]].point2 != thewall[bunchp2[z]]) || (dxb2[z] > nexttowardf(dxb1[bunchp2[z]], dxb2[z])))
6702             {
6703                 bunchfirst[numbunches++] = bunchp2[z];
6704                 bunchp2[z] = -1;
6705 #ifdef YAX_ENABLE
6706                 if (scansector_retfast)
6707                     return;
6708 #endif
6709             }
6710         }
6711 
6712         for (bssize_t z=bunchfrst; z<numbunches; z++)
6713         {
6714             int zz;
6715             for (zz=bunchfirst[z]; bunchp2[zz]>=0; zz=bunchp2[zz]) { }
6716             bunchlast[z] = zz;
6717         }
6718     }
6719     while (sectorbordercnt > 0);
6720 }
6721 
6722 /*Init viewport boundary (must be 4 point convex loop):
6723 //      (px[0],py[0]).----.(px[1],py[1])
6724 //                  /      \
6725 //                /          \
6726 // (px[3],py[3]).--------------.(px[2],py[2])
6727 */
6728 
polymost_initmosts(const float * px,const float * py,int const n)6729 static void polymost_initmosts(const float * px, const float * py, int const n)
6730 {
6731     if (n < 3) return;
6732 
6733     int32_t imin = (px[1] < px[0]);
6734 
6735     for (bssize_t i=n-1; i>=2; i--)
6736         if (px[i] < px[imin]) imin = i;
6737 
6738     int32_t vcnt = 1; //0 is dummy solid node
6739 
6740     vsp[vcnt].x = px[imin];
6741     vsp[vcnt].cy[0] = vsp[vcnt].fy[0] = py[imin];
6742     vcnt++;
6743 
6744     int i = imin+1, j = imin-1;
6745     if (i >= n) i = 0;
6746     if (j < 0) j = n-1;
6747 
6748     do
6749     {
6750         if (px[i] < px[j])
6751         {
6752             if (px[i] <= vsp[vcnt-1].x) vcnt--;
6753             vsp[vcnt].x = px[i];
6754             vsp[vcnt].cy[0] = py[i];
6755             int k = j+1; if (k >= n) k = 0;
6756             //(px[k],py[k])
6757             //(px[i],?)
6758             //(px[j],py[j])
6759             vsp[vcnt].fy[0] = (px[i]-px[k])*(py[j]-py[k])/(px[j]-px[k]) + py[k];
6760             vcnt++;
6761             i++; if (i >= n) i = 0;
6762         }
6763         else if (px[j] < px[i])
6764         {
6765             if (px[j] <= vsp[vcnt-1].x) vcnt--;
6766             vsp[vcnt].x = px[j];
6767             vsp[vcnt].fy[0] = py[j];
6768             int k = i-1; if (k < 0) k = n-1;
6769             //(px[k],py[k])
6770             //(px[j],?)
6771             //(px[i],py[i])
6772             vsp[vcnt].cy[0] = (px[j]-px[k])*(py[i]-py[k])/(px[i]-px[k]) + py[k];
6773             vcnt++;
6774             j--; if (j < 0) j = n-1;
6775         }
6776         else
6777         {
6778             if (px[i] <= vsp[vcnt-1].x) vcnt--;
6779             vsp[vcnt].x = px[i];
6780             vsp[vcnt].cy[0] = py[i];
6781             vsp[vcnt].fy[0] = py[j];
6782             vcnt++;
6783             i++; if (i >= n) i = 0; if (i == j) break;
6784             j--; if (j < 0) j = n-1;
6785         }
6786     } while (i != j);
6787 
6788     if (px[i] > vsp[vcnt-1].x)
6789     {
6790         vsp[vcnt].x = px[i];
6791         vsp[vcnt].cy[0] = vsp[vcnt].fy[0] = py[i];
6792         vcnt++;
6793     }
6794 
6795     domost_rejectcount = 0;
6796 
6797     vsp_finalize_init(vcnt);
6798 
6799     xbl = px[0];
6800     xbr = px[0];
6801     xbt = py[0];
6802     xbb = py[0];
6803 
6804     for (bssize_t i=n-1; i>=1; i--)
6805     {
6806         if (xbl > px[i]) xbl = px[i];
6807         if (xbr < px[i]) xbr = px[i];
6808         if (xbt > py[i]) xbt = py[i];
6809         if (xbb < py[i]) xbb = py[i];
6810     }
6811 
6812     gtag = vcnt;
6813     viewportNodeCount = vcnt;
6814 }
6815 
polymost_drawrooms()6816 void polymost_drawrooms()
6817 {
6818     MICROPROFILE_SCOPEI("Engine", EDUKE32_FUNCTION, MP_AUTO);
6819 
6820     if (videoGetRenderMode() == REND_CLASSIC) return;
6821 
6822     polymost_outputGLDebugMessage(3, "polymost_drawrooms()");
6823 
6824     videoBeginDrawing();
6825     frameoffset = frameplace + windowxy1.y*bytesperline + windowxy1.x;
6826 
6827 #ifdef YAX_ENABLE
6828     if (numyaxbunches==0)
6829 #endif
6830     if (editstatus)
6831         glClear(GL_COLOR_BUFFER_BIT);
6832 
6833 #ifdef YAX_ENABLE
6834     if (yax_polymostclearzbuffer)
6835 #endif
6836     glClear(GL_DEPTH_BUFFER_BIT);
6837 
6838     glDisable(GL_BLEND);
6839     glDisable(GL_ALPHA_TEST);
6840     glEnable(GL_DEPTH_TEST);
6841     glDepthFunc(GL_ALWAYS); //NEVER,LESS,(,L)EQUAL,GREATER,(NOT,G)EQUAL,ALWAYS
6842 //        glDepthRange(0.0, 1.0); //<- this is more widely supported than glPolygonOffset
6843 
6844     polymost_setBrightness(r_brightnesshack);
6845 
6846     gvrcorrection = viewingrange*(1.f/65536.f);
6847     if (glprojectionhacks == 2)
6848     {
6849         // calculates the extend of the zenith glitch
6850         float verticalfovtan = (fviewingrange * (windowxy2.y-windowxy1.y) * 5.f) / ((float)yxaspect * (windowxy2.x-windowxy1.x) * 4.f);
6851         float verticalfov = atanf(verticalfovtan) * (2.f / fPI);
6852         static constexpr float const maxhorizangle = 0.6361136f; // horiz of 199 in degrees
6853         float zenglitch = verticalfov + maxhorizangle - 0.95f; // less than 1 because the zenith glitch extends a bit
6854         if (zenglitch > 0.f)
6855             gvrcorrection /= (zenglitch * 2.5f) + 1.f;
6856     }
6857 
6858     //Polymost supports true look up/down :) Here, we convert horizon to angle.
6859     //gchang&gshang are cos&sin of this angle (respectively)
6860     gyxscale = ((float)xdimenscale)*(1.0f/131072.f);
6861     gxyaspect = ((double)xyaspect*fviewingrange)*(5.0/(65536.0*262144.0));
6862     gviewxrange = fviewingrange * fxdimen * (1.f/(32768.f*1024.f));
6863     gcosang = fcosglobalang*(1.0f/262144.f);
6864     gsinang = fsinglobalang*(1.0f/262144.f);
6865     gcosang2 = gcosang * (fviewingrange * (1.0f/65536.f));
6866     gsinang2 = gsinang * (fviewingrange * (1.0f/65536.f));
6867     ghalfx = (float)(xdimen>>1);
6868     ghalfy = (float)(ydimen>>1);
6869     grhalfxdown10 = 1.f/(ghalfx*1024.f);
6870     ghoriz = fix16_to_float(qglobalhoriz);
6871     ghorizcorrect = fix16_to_float((100-polymostcenterhoriz)*divscale16(xdimenscale, viewingrange));
6872 
6873     gvisibility = ((float)globalvisibility)*FOGSCALE;
6874 
6875 
6876     polymost_shadeInterpolate(r_shadeinterpolate);
6877 
6878     //global cos/sin height angle
6879     if (r_yshearing)
6880     {
6881         gshang  = 0.f;
6882         gchang  = 1.f;
6883         ghoriz2 = (float)(ydimen >> 1) - (ghoriz + ghorizcorrect);
6884     }
6885     else
6886     {
6887         float r = (float)(ydimen >> 1) - (ghoriz + ghorizcorrect);
6888         gshang  = r / Bsqrtf(r * r + ghalfx * ghalfx / (gvrcorrection * gvrcorrection));
6889         gchang  = Bsqrtf(1.f - gshang * gshang);
6890         ghoriz2 = 0.f;
6891     }
6892 
6893     ghoriz = (float)(ydimen>>1);
6894 
6895     resizeglcheck();
6896     float const ratio = 1.f/get_projhack_ratio();
6897 
6898     //global cos/sin tilt angle
6899     gctang = cosf(gtang);
6900     gstang = sinf(gtang);
6901 
6902     if (Bfabsf(gstang) < .001f)  // This hack avoids nasty precision bugs in domost()
6903     {
6904         gstang = 0.f;
6905         gctang = (gctang > 0.f) ? 1.f : -1.f;
6906     }
6907 
6908     if (inpreparemirror)
6909         gstang = -gstang;
6910 
6911     //Generate viewport trapezoid (for handling screen up/down)
6912     vec3f_t p[4] = {  { 0-1,                                        0-1+ghorizcorrect,                                  0 },
6913                       { (float)(windowxy2.x + 1 - windowxy1.x + 2), 0-1+ghorizcorrect,                                  0 },
6914                       { (float)(windowxy2.x + 1 - windowxy1.x + 2), (float)(windowxy2.y + 1 - windowxy1.y + 2)+ghorizcorrect, 0 },
6915                       { 0-1,                                        (float)(windowxy2.y + 1 - windowxy1.y + 2)+ghorizcorrect, 0 } };
6916 
6917     for (auto & v : p)
6918     {
6919         //Tilt rotation (backwards)
6920         vec2f_t const o = { (v.x-ghalfx)*ratio, (v.y-ghoriz)*ratio };
6921         vec3f_t const o2 = { o.x*gctang + o.y*gstang, o.y*gctang - o.x*gstang + ghoriz2, ghalfx / gvrcorrection };
6922 
6923         //Up/down rotation (backwards)
6924         v = { o2.x, o2.y * gchang + o2.z * gshang, o2.z * gchang - o2.y * gshang };
6925     }
6926 
6927 #if !SOFTROTMAT
6928     if (inpreparemirror)
6929         gstang = -gstang;
6930 #endif
6931 
6932     //Clip to SCISDIST plane
6933     int n = 0;
6934 
6935     vec3f_t p2[6];
6936 
6937     for (bssize_t i=0; i<4; i++)
6938     {
6939         int const j = i < 3 ? i + 1 : 0;
6940 
6941         if (p[i].z >= SCISDIST)
6942             p2[n++] = p[i];
6943 
6944         if ((p[i].z >= SCISDIST) != (p[j].z >= SCISDIST))
6945         {
6946             float const r = (SCISDIST - p[i].z) / (p[j].z - p[i].z);
6947             p2[n++] = { (p[j].x - p[i].x) * r + p[i].x, (p[j].y - p[i].y) * r + p[i].y, SCISDIST };
6948         }
6949     }
6950 
6951     if (n < 3) { glDepthFunc(GL_LEQUAL); videoEndDrawing(); return; }
6952 
6953     float sx[6], sy[6];
6954 
6955     for (bssize_t i = 0; i < n; i++)
6956     {
6957         float const r = (ghalfx / gvrcorrection) / p2[i].z;
6958         sx[i] = p2[i].x * r + ghalfx;
6959         sy[i] = p2[i].y * r + ghoriz;
6960     }
6961 
6962     polymost_initmosts(sx, sy, n);
6963 
6964 #ifdef YAX_ENABLE
6965     if (yax_globallev != YAX_MAXDRAWS)
6966     {
6967         int i, newi;
6968         int32_t nodrawbak = g_nodraw;
6969         g_nodraw = 1;
6970         for (i = yax_vsp[yax_globalbunch*2][0].n; i; i=newi)
6971         {
6972             newi = yax_vsp[yax_globalbunch*2][i].n;
6973             if (!newi)
6974                 break;
6975             polymost_domost(yax_vsp[yax_globalbunch*2][newi].x, yax_vsp[yax_globalbunch*2][i].cy[1]-DOMOST_OFFSET, yax_vsp[yax_globalbunch*2][i].x, yax_vsp[yax_globalbunch*2][i].cy[0]-DOMOST_OFFSET);
6976         }
6977         for (i = yax_vsp[yax_globalbunch*2+1][0].n; i; i=newi)
6978         {
6979             newi = yax_vsp[yax_globalbunch*2+1][i].n;
6980             if (!newi)
6981                 break;
6982             polymost_domost(yax_vsp[yax_globalbunch*2+1][i].x, yax_vsp[yax_globalbunch*2+1][i].cy[0]+DOMOST_OFFSET, yax_vsp[yax_globalbunch*2+1][newi].x, yax_vsp[yax_globalbunch*2+1][i].cy[1]+DOMOST_OFFSET);
6983         }
6984         g_nodraw = nodrawbak;
6985 
6986 #ifdef COMBINE_STRIPS
6987         i = vsp[0].n;
6988 
6989         do
6990         {
6991             int const ni = vsp[i].n;
6992 
6993             //POGO: specially treat the viewport nodes so that we will never end up in a situation where we accidentally access the sentinel node
6994             if (ni >= viewportNodeCount)
6995             {
6996                 if (Bfabsf(vsp[i].cy[1]-vsp[ni].cy[0]) < 0.1f && Bfabsf(vsp[i].fy[1]-vsp[ni].fy[0]) < 0.1f)
6997                 {
6998                     float const dx = 1.f/(vsp[ni].x-vsp[i].x);
6999                     float const dx2 = 1.f/(vsp[vsp[ni].n].x-vsp[i].x);
7000                     float const cslop[2] = { vsp[i].cy[1]-vsp[i].cy[0], vsp[ni].cy[1]-vsp[i].cy[0] };
7001                     float const fslop[2] = { vsp[i].fy[1]-vsp[i].fy[0], vsp[ni].fy[1]-vsp[i].fy[0] };
7002 
7003                     if (Bfabsf(cslop[0]*dx-cslop[1]*dx2) < 0.001f && Bfabsf(fslop[0]*dx-fslop[1]*dx2) < 0.001f)
7004                     {
7005                         vsmerge(i, ni);
7006                         continue;
7007                     }
7008                 }
7009             }
7010             i = ni;
7011         }
7012         while (i);
7013 #endif
7014     }
7015     //else if (!g_nodraw) { videoEndDrawing(); return; }
7016 #endif
7017 
7018     doeditorcheck = 0;
7019 
7020     if (searchit >= 1)
7021     {
7022         vec2f_t const o = { (searchx-ghalfx)*ratio, (searchy-ghoriz)*ratio };
7023         vec3f_t const o2 = { o.x*gctang + o.y*gstang, o.y*gctang - o.x*gstang + ghoriz2, (ghalfx / gvrcorrection) };
7024         vec3f_t const v = { o2.x, o2.y * gchang + o2.z * gshang, o2.z * gchang - o2.y * gshang };
7025         float const r = (ghalfx / gvrcorrection) / v.z;
7026         fsearchx = v.x * r + ghalfx;
7027         fsearchy = v.y * r + ghoriz;
7028         if (searchit == 2)
7029             fsearchz = 0.f;
7030         polymost_editorfunc();
7031     }
7032 
7033     polymost_updaterotmat();
7034 
7035     numscans = numbunches = 0;
7036 
7037     // MASKWALL_BAD_ACCESS
7038     // Fixes access of stale maskwall[maskwallcnt] (a "scan" index, in BUILD lingo):
7039     maskwallcnt = 0;
7040 
7041     // NOTE: globalcursectnum has been already adjusted in ADJUST_GLOBALCURSECTNUM.
7042     Bassert((unsigned)globalcursectnum < MAXSECTORS);
7043 #ifdef YAX_ENABLE
7044     if (yax_globallev != YAX_MAXDRAWS)
7045     {
7046         int k;
7047         for (SECTORS_OF_BUNCH(yax_globalbunch, !yax_globalcf, k))
7048         {
7049             polymost_scansector(k);
7050         }
7051     }
7052     else
7053 #endif
7054     polymost_scansector(globalcursectnum);
7055 
7056 #ifdef YAX_ENABLE
7057     int startbunches = numbunches;
7058     if (yax_globallev != YAX_MAXDRAWS)
7059     {
7060         for (bssize_t i = 0; i < startbunches; i++)
7061         {
7062             bunchfirst[MAXWALLSB-1-i] = bunchfirst[i];
7063             bunchlast[MAXWALLSB-1-i] = bunchlast[i];
7064         }
7065     }
7066 #endif
7067 
7068     grhalfxdown10x = grhalfxdown10;
7069 
7070     if (inpreparemirror)
7071     {
7072         // see engine.c: INPREPAREMIRROR_NO_BUNCHES
7073         if (numbunches > 0)
7074         {
7075             grhalfxdown10x = -grhalfxdown10;
7076             polymost_drawalls(0);
7077             numbunches--;
7078             bunchfirst[0] = bunchfirst[numbunches];
7079             bunchlast[0] = bunchlast[numbunches];
7080         } else
7081         {
7082             inpreparemirror = 0;
7083         }
7084     }
7085 
7086     while (numbunches > 0)
7087     {
7088         Bmemset(ptempbuf,0,numbunches+3); ptempbuf[0] = 1;
7089 
7090         int32_t closest = 0;              //Almost works, but not quite :(
7091 
7092         for (bssize_t i=1; i<numbunches; ++i)
7093         {
7094             int const bnch = polymost_bunchfront(i,closest); if (bnch < 0) continue;
7095             ptempbuf[i] = 1;
7096             if (!bnch) { ptempbuf[closest] = 1; closest = i; }
7097         }
7098         for (bssize_t i=0; i<numbunches; ++i) //Double-check
7099         {
7100             if (ptempbuf[i]) continue;
7101             int const bnch = polymost_bunchfront(i,closest); if (bnch < 0) continue;
7102             ptempbuf[i] = 1;
7103             if (!bnch) { ptempbuf[closest] = 1; closest = i; i = 0; }
7104         }
7105 
7106 #ifdef YAX_ENABLE
7107         int bunchnodraw = 0;
7108 
7109         if (yax_globallev != YAX_MAXDRAWS)
7110         {
7111             bunchnodraw = 1;
7112             for (bssize_t i = 0; i < startbunches; i++)
7113             {
7114                 int const sbunch = MAXWALLSB-1-i;
7115                 if (bunchfirst[closest] == bunchfirst[sbunch])
7116                 {
7117                     bunchnodraw = 0;
7118                     break;
7119                 }
7120                 int const bnch = polymost_bunchfront(sbunch,closest); if (bnch < 0) continue;
7121                 if (!bnch)
7122                 {
7123                     bunchnodraw = 0;
7124                     break;
7125                 }
7126             }
7127         }
7128 
7129         if (!bunchnodraw)
7130 #endif
7131         polymost_drawalls(closest);
7132 
7133         if (automapping)
7134         {
7135             for (int z=bunchfirst[closest]; z>=0; z=bunchp2[z])
7136                 show2dwall[thewall[z]>>3] |= pow2char[thewall[z]&7];
7137         }
7138 
7139         numbunches--;
7140         bunchfirst[closest] = bunchfirst[numbunches];
7141         bunchlast[closest] = bunchlast[numbunches];
7142     }
7143 
7144     glDepthFunc(GL_LEQUAL); //NEVER,LESS,(,L)EQUAL,GREATER,(NOT,G)EQUAL,ALWAYS
7145 //        glDepthRange(0.0, 1.0); //<- this is more widely supported than glPolygonOffset
7146     polymost_identityrotmat();
7147 
7148     videoEndDrawing();
7149 }
7150 
polymost_drawmaskwallinternal(int32_t wallIndex)7151 static void polymost_drawmaskwallinternal(int32_t wallIndex)
7152 {
7153     auto const wal = (uwallptr_t)&wall[wallIndex];
7154     auto const wal2 = (uwallptr_t)&wall[wal->point2];
7155     int32_t const sectnum = wall[wal->nextwall].nextsector;
7156     auto const sec = (usectorptr_t)&sector[sectnum];
7157 
7158 //    if (wal->nextsector < 0) return;
7159     // Without MASKWALL_BAD_ACCESS fix:
7160     // wal->nextsector is -1, WGR2 SVN Lochwood Hollow (Til' Death L1)  (or trueror1.map)
7161 
7162     auto const nsec = (usectorptr_t)&sector[wal->nextsector];
7163 
7164     polymost_outputGLDebugMessage(3, "polymost_drawmaskwallinternal(wallIndex:%d)", wallIndex);
7165 
7166     globalpicnum = wal->overpicnum;
7167     if ((uint32_t)globalpicnum >= MAXTILES)
7168         globalpicnum = 0;
7169 
7170     globalorientation = (int32_t)wal->cstat;
7171     tileUpdatePicnum(&globalpicnum, (int16_t)wallIndex+16384);
7172 
7173     globvis = globalvisibility;
7174     globvis = (sector[sectnum].visibility != 0) ? mulscale4(globvis, (uint8_t)(sector[sectnum].visibility + 16)) : globalvisibility;
7175 
7176     globvis2 = globalvisibility2;
7177     globvis2 = (sector[sectnum].visibility != 0) ? mulscale4(globvis2, (uint8_t)(sector[sectnum].visibility + 16)) : globalvisibility2;
7178     polymost_setVisibility(globvis2);
7179 
7180     globalshade = (int32_t)wal->shade;
7181     globalpal = (int32_t)((uint8_t)wal->pal);
7182 
7183     vec2f_t s0 = { (float)(wal->x-globalposx), (float)(wal->y-globalposy) };
7184     vec2f_t p0 = { s0.y*gcosang - s0.x*gsinang, s0.x*gcosang2 + s0.y*gsinang2 };
7185 
7186     vec2f_t s1 = { (float)(wal2->x-globalposx), (float)(wal2->y-globalposy) };
7187     vec2f_t p1 = { s1.y*gcosang - s1.x*gsinang, s1.x*gcosang2 + s1.y*gsinang2 };
7188 
7189     if ((p0.y < SCISDIST) && (p1.y < SCISDIST)) return;
7190 
7191     //Clip to close parallel-screen plane
7192     vec2f_t const op0 = p0;
7193 
7194     float t0 = 0.f;
7195 
7196     if (p0.y < SCISDIST)
7197     {
7198         t0 = (SCISDIST - p0.y) / (p1.y - p0.y);
7199         p0 = { (p1.x - p0.x) * t0 + p0.x, SCISDIST };
7200     }
7201 
7202     float t1 = 1.f;
7203 
7204     if (p1.y < SCISDIST)
7205     {
7206         t1 = (SCISDIST - op0.y) / (p1.y - op0.y);
7207         p1 = { (p1.x - op0.x) * t1 + op0.x, SCISDIST };
7208     }
7209 
7210     int32_t m0 = (int32_t)((wal2->x - wal->x) * t0 + wal->x);
7211     int32_t m1 = (int32_t)((wal2->y - wal->y) * t0 + wal->y);
7212     int32_t cz[4], fz[4];
7213     getzsofslope(sectnum, m0, m1, &cz[0], &fz[0]);
7214     getzsofslope(wal->nextsector, m0, m1, &cz[1], &fz[1]);
7215     m0 = (int32_t)((wal2->x - wal->x) * t1 + wal->x);
7216     m1 = (int32_t)((wal2->y - wal->y) * t1 + wal->y);
7217     getzsofslope(sectnum, m0, m1, &cz[2], &fz[2]);
7218     getzsofslope(wal->nextsector, m0, m1, &cz[3], &fz[3]);
7219 
7220     float ryp0 = 1.f/p0.y;
7221     float ryp1 = 1.f/p1.y;
7222 
7223     //Generate screen coordinates for front side of wall
7224     float const x0 = ghalfx*p0.x*ryp0 + ghalfx;
7225     float const x1 = ghalfx*p1.x*ryp1 + ghalfx;
7226     if (x1 <= x0) return;
7227 
7228     ryp0 *= gyxscale; ryp1 *= gyxscale;
7229 
7230     xtex.d = (ryp0-ryp1)*gxyaspect / (x0-x1);
7231     ytex.d = 0;
7232     otex.d = ryp0*gxyaspect - xtex.d*x0;
7233 
7234     //gux*x0 + guo = t0*wal->xrepeat*8*yp0
7235     //gux*x1 + guo = t1*wal->xrepeat*8*yp1
7236     xtex.u = (t0*ryp0 - t1*ryp1)*gxyaspect*(float)wal->xrepeat*8.f / (x0-x1);
7237     otex.u = t0*ryp0*gxyaspect*(float)wal->xrepeat*8.f - xtex.u*x0;
7238     otex.u += (float)wal->xpanning*otex.d;
7239     xtex.u += (float)wal->xpanning*xtex.d;
7240     ytex.u = 0;
7241 
7242     // mask
7243     calc_ypanning((!(wal->cstat & 4)) ? max(nsec->ceilingz, sec->ceilingz) : min(nsec->floorz, sec->floorz), ryp0, ryp1,
7244                   x0, x1, wal->ypanning, wal->yrepeat, 0);
7245 
7246     if (wal->cstat&8) //xflip
7247     {
7248         float const t = (float)(wal->xrepeat*8 + wal->xpanning*2);
7249         xtex.u = xtex.d*t - xtex.u;
7250         ytex.u = ytex.d*t - ytex.u;
7251         otex.u = otex.d*t - otex.u;
7252     }
7253     if (wal->cstat&256) { xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; } //yflip
7254 
7255     int method = DAMETH_MASK | DAMETH_WALL;
7256 
7257     if (wal->cstat & 128)
7258         method = DAMETH_WALL | (((wal->cstat & 512)) ? DAMETH_TRANS2 : DAMETH_TRANS1);
7259 
7260 #ifdef NEW_MAP_FORMAT
7261     uint8_t const blend = wal->blend;
7262 #else
7263     uint8_t const blend = wallext[wallIndex].blend;
7264 #endif
7265     handle_blend(!!(wal->cstat & 128), blend, !!(wal->cstat & 512));
7266 
7267     drawpoly_alpha = 0.f;
7268     drawpoly_blend = blend;
7269 
7270     if (!polymost_usetileshades() || (usehightile && hicfindsubst(globalpicnum, globalpal, hictinting[globalpal].f & HICTINT_ALWAYSUSEART)))
7271         calc_and_apply_fog(fogshade(wal->shade, wal->pal), sec->visibility, get_floor_fogpal(sec));
7272 
7273     float const csy[4] = { ((float)(cz[0] - globalposz)) * ryp0 + ghoriz,
7274                            ((float)(cz[1] - globalposz)) * ryp0 + ghoriz,
7275                            ((float)(cz[2] - globalposz)) * ryp1 + ghoriz,
7276                            ((float)(cz[3] - globalposz)) * ryp1 + ghoriz };
7277 
7278     float const fsy[4] = { ((float)(fz[0] - globalposz)) * ryp0 + ghoriz,
7279                            ((float)(fz[1] - globalposz)) * ryp0 + ghoriz,
7280                            ((float)(fz[2] - globalposz)) * ryp1 + ghoriz,
7281                            ((float)(fz[3] - globalposz)) * ryp1 + ghoriz };
7282 
7283     //Clip 2 quadrilaterals
7284     //               /csy3
7285     //             /   |
7286     // csy0------/----csy2
7287     //   |     /xxxxxxx|
7288     //   |   /xxxxxxxxx|
7289     // csy1/xxxxxxxxxxx|
7290     //   |xxxxxxxxxxx/fsy3
7291     //   |xxxxxxxxx/   |
7292     //   |xxxxxxx/     |
7293     // fsy0----/------fsy2
7294     //   |   /
7295     // fsy1/
7296 
7297     vec2f_t dpxy[8] = { { x0, csy[1] }, { x1, csy[3] }, { x1, fsy[3] }, { x0, fsy[1] } };
7298 
7299     //Clip to (x0,csy[0])-(x1,csy[2])
7300 
7301     vec2f_t dp2[8];
7302 
7303     int n2 = 0;
7304     t1 = -((dpxy[0].x - x0) * (csy[2] - csy[0]) - (dpxy[0].y - csy[0]) * (x1 - x0));
7305 
7306     for (bssize_t i=0; i<4; i++)
7307     {
7308         int j = i + 1;
7309 
7310         if (j >= 4)
7311             j = 0;
7312 
7313         t0 = t1;
7314         t1 = -((dpxy[j].x - x0) * (csy[2] - csy[0]) - (dpxy[j].y - csy[0]) * (x1 - x0));
7315 
7316         if (t0 >= 0)
7317             dp2[n2++] = dpxy[i];
7318 
7319         if ((t0 >= 0) != (t1 >= 0) && (t0 <= 0) != (t1 <= 0))
7320         {
7321             float const r = t0 / (t0 - t1);
7322             dp2[n2++] = { (dpxy[j].x - dpxy[i].x) * r + dpxy[i].x, (dpxy[j].y - dpxy[i].y) * r + dpxy[i].y };
7323         }
7324     }
7325 
7326     if (n2 < 3)
7327         return;
7328 
7329     //Clip to (x1,fsy[2])-(x0,fsy[0])
7330     t1 = -((dp2[0].x - x1) * (fsy[0] - fsy[2]) - (dp2[0].y - fsy[2]) * (x0 - x1));
7331     int n = 0;
7332 
7333     for (bssize_t i = 0, j = 1; i < n2; j = ++i + 1)
7334     {
7335         if (j >= n2)
7336             j = 0;
7337 
7338         t0 = t1;
7339         t1 = -((dp2[j].x - x1) * (fsy[0] - fsy[2]) - (dp2[j].y - fsy[2]) * (x0 - x1));
7340 
7341         if (t0 >= 0)
7342             dpxy[n++] = dp2[i];
7343 
7344         if ((t0 >= 0) != (t1 >= 0) && (t0 <= 0) != (t1 <= 0))
7345         {
7346             float const r = t0 / (t0 - t1);
7347             dpxy[n++] = { (dp2[j].x - dp2[i].x) * r + dp2[i].x, (dp2[j].y - dp2[i].y) * r + dp2[i].y };
7348         }
7349     }
7350 
7351     if (n < 3)
7352         return;
7353 
7354     pow2xsplit = 0;
7355     skyclamphack = 0;
7356 
7357     polymost_updaterotmat();
7358     if (searchit >= 1)
7359     {
7360         psectnum = sectnum;
7361         pbottomwall = pwallnum = wallIndex;
7362         psearchstat = 4;
7363         doeditorcheck = 1;
7364     }
7365     polymost_drawpoly(dpxy, n, method);
7366     doeditorcheck = 0;
7367     polymost_identityrotmat();
7368 }
7369 
polymost_drawmaskwall(int32_t damaskwallcnt)7370 void polymost_drawmaskwall(int32_t damaskwallcnt)
7371 {
7372     int const z = maskwall[damaskwallcnt];
7373     polymost_drawmaskwallinternal(thewall[z]);
7374 }
7375 
polymost_prepareMirror(int32_t dax,int32_t day,int32_t daz,fix16_t daang,fix16_t dahoriz,int16_t mirrorWall)7376 void polymost_prepareMirror(int32_t dax, int32_t day, int32_t daz, fix16_t daang, fix16_t dahoriz, int16_t mirrorWall)
7377 {
7378     polymost_outputGLDebugMessage(3, "polymost_prepareMirror(%u)", mirrorWall);
7379 
7380     //POGO: prepare necessary globals for drawing, as we intend to call this outside of drawrooms
7381     gvrcorrection = viewingrange*(1.f/65536.f);
7382     if (glprojectionhacks == 2)
7383     {
7384         // calculates the extend of the zenith glitch
7385         float verticalfovtan = (fviewingrange * (windowxy2.y-windowxy1.y) * 5.f) / ((float)yxaspect * (windowxy2.x-windowxy1.x) * 4.f);
7386         float verticalfov = atanf(verticalfovtan) * (2.f / fPI);
7387         static constexpr float const maxhorizangle = 0.6361136f; // horiz of 199 in degrees
7388         float zenglitch = verticalfov + maxhorizangle - 0.95f; // less than 1 because the zenith glitch extends a bit
7389         if (zenglitch > 0.f)
7390             gvrcorrection /= (zenglitch * 2.5f) + 1.f;
7391     }
7392 
7393     set_globalpos(dax, day, daz);
7394     set_globalang(daang);
7395     globalhoriz = mulscale16(fix16_to_int(dahoriz)-100,divscale16(xdimenscale,viewingrange))+(ydimen>>1);
7396     qglobalhoriz = mulscale16(dahoriz-F16(100), divscale16(xdimenscale, viewingrange))+fix16_from_int(ydimen>>1);
7397     gyxscale = ((float)xdimenscale)*(1.0f/131072.f);
7398     gxyaspect = ((double)xyaspect*fviewingrange)*(5.0/(65536.0*262144.0));
7399     gviewxrange = fviewingrange * fxdimen * (1.f/(32768.f*1024.f));
7400     gcosang = fcosglobalang*(1.0f/262144.f);
7401     gsinang = fsinglobalang*(1.0f/262144.f);
7402     gcosang2 = gcosang * (fviewingrange * (1.0f/65536.f));
7403     gsinang2 = gsinang * (fviewingrange * (1.0f/65536.f));
7404     ghalfx = (float)(xdimen>>1);
7405     ghalfy = (float)(ydimen>>1);
7406     grhalfxdown10 = 1.f/(ghalfx*1024.f);
7407     ghoriz = fix16_to_float(qglobalhoriz);
7408     ghorizcorrect = fix16_to_float((100-polymostcenterhoriz)*divscale16(xdimenscale, viewingrange));
7409     gvisibility = ((float)globalvisibility)*FOGSCALE;
7410     resizeglcheck();
7411     if (r_yshearing)
7412     {
7413         gshang  = 0.f;
7414         gchang  = 1.f;
7415         ghoriz2 = (float)(ydimen >> 1) - (ghoriz+ghorizcorrect);
7416     }
7417     else
7418     {
7419         float r = (float)(ydimen >> 1) - (ghoriz+ghorizcorrect);
7420         gshang  = r / Bsqrtf(r * r + ghalfx * ghalfx / (gvrcorrection * gvrcorrection));
7421         gchang  = Bsqrtf(1.f - gshang * gshang);
7422         ghoriz2 = 0.f;
7423     }
7424     ghoriz = (float)(ydimen>>1);
7425     gctang = cosf(gtang);
7426     gstang = sinf(gtang);
7427     if (Bfabsf(gstang) < .001f)
7428     {
7429         gstang = 0.f;
7430         gctang = (gctang > 0.f) ? 1.f : -1.f;
7431     }
7432     grhalfxdown10x = grhalfxdown10;
7433 
7434     //POGO: write the mirror region to the stencil buffer to allow showing mirrors & skyboxes at the same time
7435     glEnable(GL_STENCIL_TEST);
7436     glClear(GL_STENCIL_BUFFER_BIT);
7437     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
7438     glStencilFunc(GL_ALWAYS, 1, 0xFF);
7439     glDisable(GL_ALPHA_TEST);
7440     glDisable(GL_DEPTH_TEST);
7441     polymost_drawmaskwallinternal(mirrorWall);
7442     glEnable(GL_ALPHA_TEST);
7443     glEnable(GL_DEPTH_TEST);
7444 
7445     //POGO: render only to the mirror region
7446     glStencilFunc(GL_EQUAL, 1, 0xFF);
7447     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
7448 }
7449 
polymost_completeMirror()7450 void polymost_completeMirror()
7451 {
7452     polymost_outputGLDebugMessage(3, "polymost_completeMirror()");
7453     glDisable(GL_STENCIL_TEST);
7454 }
7455 
7456 typedef struct
7457 {
7458     uint32_t wrev;
7459     uint32_t srev;
7460     int16_t wall;
7461     int8_t wdist;
7462     int8_t filler;
7463 } wallspriteinfo_t;
7464 
7465 wallspriteinfo_t wsprinfo[MAXSPRITES];
7466 
Polymost_prepare_loadboard(void)7467 void Polymost_prepare_loadboard(void)
7468 {
7469     Bmemset(wsprinfo, 0, sizeof(wsprinfo));
7470 }
7471 
polymost_findwall(tspritetype const * const tspr,vec2_t const * const tsiz,int32_t * rd)7472 static inline int32_t polymost_findwall(tspritetype const * const tspr, vec2_t const * const tsiz, int32_t * rd)
7473 {
7474     int32_t dist = 4, closest = -1;
7475     auto const sect = (usectortype  * )&sector[tspr->sectnum];
7476     vec2_t n;
7477 
7478     for (bssize_t i=sect->wallptr; i<sect->wallptr + sect->wallnum; i++)
7479     {
7480         if ((wall[i].nextsector == -1 || ((sector[wall[i].nextsector].ceilingz > (tspr->z - ((tsiz->y * tspr->yrepeat) << 2))) ||
7481              sector[wall[i].nextsector].floorz < tspr->z)) && !polymost_getclosestpointonwall((const vec2_t *) tspr, i, &n))
7482         {
7483             int const dst = klabs(tspr->x - n.x) + klabs(tspr->y - n.y);
7484 
7485             if (dst <= dist)
7486             {
7487                 dist = dst;
7488                 closest = i;
7489             }
7490         }
7491     }
7492 
7493     *rd = dist;
7494 
7495     return closest;
7496 }
7497 
polymost_lintersect(int32_t x1,int32_t y1,int32_t x2,int32_t y2,int32_t x3,int32_t y3,int32_t x4,int32_t y4)7498 int32_t polymost_lintersect(int32_t x1, int32_t y1, int32_t x2, int32_t y2,
7499                             int32_t x3, int32_t y3, int32_t x4, int32_t y4)
7500 {
7501     // p1 to p2 is a line segment
7502     int32_t const x21 = x2 - x1, x34 = x3 - x4;
7503     int32_t const y21 = y2 - y1, y34 = y3 - y4;
7504     int32_t const bot = x21 * y34 - y21 * x34;
7505 
7506     if (!bot)
7507         return 0;
7508 
7509     int32_t const x31 = x3 - x1, y31 = y3 - y1;
7510     int32_t const topt = x31 * y34 - y31 * x34;
7511 
7512     int rv = 1;
7513 
7514     if (bot > 0)
7515     {
7516         if ((unsigned)topt >= (unsigned)bot)
7517             rv = 0;
7518 
7519         int32_t topu = x21 * y31 - y21 * x31;
7520 
7521         if ((unsigned)topu >= (unsigned)bot)
7522             rv = 0;
7523     }
7524     else
7525     {
7526         if ((unsigned)topt <= (unsigned)bot)
7527             rv = 0;
7528 
7529         int32_t topu = x21 * y31 - y21 * x31;
7530 
7531         if ((unsigned)topu <= (unsigned)bot)
7532             rv = 0;
7533     }
7534 
7535     return rv;
7536 }
7537 
7538 #define TSPR_OFFSET_FACTOR .000008f
7539 #define TSPR_OFFSET(tspr) ((TSPR_OFFSET_FACTOR + ((tspr->owner != -1 ? tspr->owner & 63 : 1) * TSPR_OFFSET_FACTOR)) * (float)sepdist(globalposx - tspr->x, globalposy - tspr->y, globalposz - tspr->z) * 0.025f)
7540 
polymost2_drawsprite(int32_t snum)7541 void polymost2_drawsprite(int32_t snum)
7542 {
7543     auto const tspr = tspriteptr[snum];
7544 
7545     if (EDUKE32_PREDICT_FALSE(bad_tspr(tspr)))
7546         return;
7547 
7548     usectorptr_t sec;
7549 
7550     int32_t spritenum = tspr->owner;
7551 
7552     tileUpdatePicnum(&tspr->picnum, spritenum + 32768);
7553 
7554     globalpicnum = tspr->picnum;
7555     globalshade = tspr->shade;
7556     globalpal = tspr->pal;
7557     globalorientation = tspr->cstat;
7558     globvis = globalvisibility;
7559 
7560     if (sector[tspr->sectnum].visibility != 0)
7561         globvis = mulscale4(globvis, (uint8_t)(sector[tspr->sectnum].visibility + 16));
7562 
7563     vec2f_t off = { 0.f, 0.f };
7564 
7565     if ((globalorientation & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_SLAB)  // only non-voxel sprites should do this
7566     {
7567         int const flag = usehightile && h_xsize[globalpicnum];
7568         off.x = (float)((int32_t)tspr->xoffset + (flag ? h_xoffs[globalpicnum] : picanm[globalpicnum].xofs));
7569         off.y = (float)((int32_t)tspr->yoffset + (flag ? h_yoffs[globalpicnum] : picanm[globalpicnum].yofs));
7570     }
7571 
7572     int32_t method = DAMETH_MASK | DAMETH_CLAMPED;
7573 
7574     if (tspr->cstat & CSTAT_SPRITE_TRANSLUCENT)
7575         method = DAMETH_CLAMPED | ((tspr->cstat & CSTAT_SPRITE_TRANSLUCENT_INVERT) ? DAMETH_TRANS2 : DAMETH_TRANS1);
7576 
7577     handle_blend(!!(tspr->cstat & CSTAT_SPRITE_TRANSLUCENT), tspr->blend, !!(tspr->cstat & CSTAT_SPRITE_TRANSLUCENT_INVERT));
7578 
7579     drawpoly_alpha = spriteext[spritenum].alpha;
7580     drawpoly_blend = tspr->blend;
7581 
7582     sec = (usectorptr_t)&sector[tspr->sectnum];
7583 
7584     polymost2_calc_fog(fogshade(globalshade, globalpal), sec->visibility, get_floor_fogpal(sec));
7585 
7586     //POGOTODO: this while is an if statement
7587     while (!(spriteext[spritenum].flags & SPREXT_NOTMD))
7588     {
7589     	//POGOTODO: switch these to if/else for readability and rearrange for performance
7590         if (usemodels && tile2model[Ptile2tile(tspr->picnum, tspr->pal)].modelid >= 0 &&
7591             tile2model[Ptile2tile(tspr->picnum, tspr->pal)].framenum >= 0)
7592         {
7593             if (polymost_mddraw(tspr)) return;
7594             break;  // else, render as flat sprite
7595         }
7596 
7597         if (usevoxels && (tspr->cstat & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_SLAB && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]])
7598         {
7599             if (polymost_voxdraw(voxmodels[tiletovox[tspr->picnum]], tspr)) return;
7600             break;  // else, render as flat sprite
7601         }
7602 
7603         if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLAB && voxmodels[tspr->picnum])
7604         {
7605             polymost_voxdraw(voxmodels[tspr->picnum], tspr);
7606             return;
7607         }
7608 
7609         break;
7610     }
7611 
7612     //POGO: some comments seem to indicate that spinning sprites were intended to be supported before the
7613     //      decision was made to implement that behaviour with voxels.
7614     //      Skip SLAB aligned sprites when not rendering as voxels.
7615     if ((globalorientation & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLAB)
7616     {
7617         return;
7618     }
7619 
7620     vec2_t pos = tspr->pos.vec2;
7621 
7622     if (spriteext[spritenum].flags & SPREXT_AWAY1)
7623     {
7624         pos.x += (sintable[(tspr->ang + 512) & 2047] >> 13);
7625         pos.y += (sintable[(tspr->ang) & 2047] >> 13);
7626     }
7627     else if (spriteext[spritenum].flags & SPREXT_AWAY2)
7628     {
7629         pos.x -= (sintable[(tspr->ang + 512) & 2047] >> 13);
7630         pos.y -= (sintable[(tspr->ang) & 2047] >> 13);
7631     }
7632 
7633     vec2_16_t const oldsiz = tilesiz[globalpicnum];
7634     vec2_t tsiz = { oldsiz.x, oldsiz.y };
7635 
7636     if (usehightile && h_xsize[globalpicnum])
7637     {
7638         tsiz.x = h_xsize[globalpicnum];
7639         tsiz.y = h_ysize[globalpicnum];
7640     }
7641 
7642     if (tsiz.x <= 0 || tsiz.y <= 0)
7643         return;
7644 
7645     vec2f_t const ftsiz = { (float) tsiz.x, (float) tsiz.y };
7646 
7647     //POGOTODO: some of these cases where we return could be done further up in order to skip doing throw away computation
7648     if ((globalorientation & CSTAT_SPRITE_ALIGNMENT_FLOOR) &&
7649         (globalorientation & CSTAT_SPRITE_ONE_SIDED) != 0 &&
7650         (globalposz > tspr->z) == (!(globalorientation & CSTAT_SPRITE_YFLIP)))
7651     {
7652         return;
7653     }
7654 
7655     //POGOTODO: in polymost1 any sprites that are too close are pre-clipped here before any calculation
7656 
7657     tilesiz[globalpicnum].x = tsiz.x;
7658     tilesiz[globalpicnum].y = tsiz.y;
7659 
7660     float texScale[2] = {1.0f, -1.0f};
7661     float texOffset[2] = {((float) (spriteext[spritenum].xpanning) * (1.0f / 255.f)),
7662                           ((float) (spriteext[spritenum].ypanning) * (1.0f / 255.f))};
7663 
7664     float transformMatrix[4*4] =
7665         {
7666             1.0f, 0.0f, 0.0f, 0.0f,
7667             0.0f, 1.0f, 0.0f, 0.0f,
7668             0.0f, 0.0f, 1.0f, 0.0f,
7669             0.0f, 0.0f, 0.0f, 1.0f
7670         };
7671 
7672     float modelViewMatrix[4*4] =
7673         {
7674             1.0f, 0.0f, 0.0f, 0.0f,
7675             0.0f, 1.0f, 0.0f, 0.0f,
7676             0.0f, 0.0f, 1.0f, 0.0f,
7677             0.0f, 0.0f, 0.0f, 1.0f
7678         };
7679 
7680     float f = (65536.f*512.f) / (fxdimen*viewingrange);
7681     float g = 32.f / (fxdimen*gxyaspect);
7682 
7683     float horzScale = ftsiz.x*(1.f/64.f);
7684     float vertScale = ftsiz.y*(1.f/64.f);
7685 
7686     horzScale *= ((float)tspr->xrepeat) * (1.f/64.f);
7687     vertScale *= ((float)tspr->yrepeat) * (1.f/64.f);
7688 
7689     if ((globalorientation & CSTAT_SPRITE_ALIGNMENT)==CSTAT_SPRITE_ALIGNMENT_FACING)
7690     {
7691         horzScale *= 256.f/320.f;
7692     }
7693     else if ((globalorientation & CSTAT_SPRITE_ALIGNMENT)==CSTAT_SPRITE_ALIGNMENT_FLOOR)
7694     {
7695         //POGOTODO: fix floor sprites to be scaled up slightly by the right amount, and note their tex is slightly clipped on the leading edges
7696         vertScale += 1.f/320.f;
7697     }
7698 
7699     horzScale *= f;
7700     vertScale *= g;
7701 
7702     //handle sprite flipping
7703     horzScale *= -2.f*((globalorientation & CSTAT_SPRITE_XFLIP) != 0) + 1.f;
7704     vertScale *= -2.f*(((globalorientation & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_FLOOR) &
7705                        ((globalorientation & CSTAT_SPRITE_YFLIP) != 0)) + 1.f;
7706 
7707     //POGOTODO: replace this with simply using off.x and a different float for z offsets
7708     //          switching that over should fix floor sprite offsets so that they flip properly when yflip/xflip is applied
7709     //handle orientation offsets
7710     vec2f_t orientationOffset = {0.f, 0.f};
7711     vec3f_t offs = { 0.f, 0.f, 0.f };
7712 
7713     off.x = 0.2f * ((float)tspr->xrepeat) * (((float) off.x) + (tsiz.x & 1)*0.5f*(((globalorientation & CSTAT_SPRITE_XFLIP) == 0)*-2.f + 1.f));
7714     off.y = 4.f * ((float)tspr->yrepeat) * (((float) off.y) + (((globalorientation & CSTAT_SPRITE_YCENTER) != 0) & tsiz.y & 1)*0.5f);
7715 
7716     int16_t angle = globalang;
7717     float combinedClipScale = 1.f;
7718     if ((globalorientation & CSTAT_SPRITE_ALIGNMENT)==CSTAT_SPRITE_ALIGNMENT_FACING)
7719     {
7720         int const ang = (getangle(tspr->x - globalposx, tspr->y - globalposy) + 1024) & 2047;
7721         float const foffs = TSPR_OFFSET(tspr);
7722         offs = { (float) (sintable[(ang + 512) & 2047] >> 6) * foffs,
7723                  (float) (sintable[(ang) & 2047] >> 6) * foffs,
7724                  0.f};
7725     }
7726     else if ((globalorientation & CSTAT_SPRITE_ALIGNMENT)==CSTAT_SPRITE_ALIGNMENT_WALL)
7727     {
7728         angle = (tspr->ang+1024)&2047;
7729         /*float const foffs = TSPR_OFFSET(tspr);
7730         offs = { (float) (sintable[(tspr->ang + 512) & 2047] >> 6) * foffs,
7731                  (float) (sintable[(tspr->ang) & 2047] >> 6) * foffs};*/
7732 
7733         //POGOTODO: For now, just handle this exactly the same way as in polymost1.
7734         //          Eventually, I should change how all sprites avoid z-fighting by offsetting the z-buffer depth
7735         //          rather than offsetting the entire object in space.
7736         vec2f_t const extent = { (float)tspr->xrepeat * (float)sintable[(tspr->ang) & 2047] * (1.0f / 65536.f),
7737                                  (float)tspr->xrepeat * (float)sintable[(tspr->ang + 1536) & 2047] * (1.0f / 65536.f) };
7738         //POGOTODO: this needs to be calculated before I make my adjustments to off.x above!
7739         float f = (float)(tsiz.x >> 1) + (float)off.x;
7740         vec2f_t const vf = { extent.x * f, extent.y * f };
7741 
7742         int32_t const s = tspr->owner;
7743         int32_t walldist = 1;
7744         int32_t w = (s == -1) ? -1 : wsprinfo[s].wall;
7745 
7746         // find the wall most likely to be what the sprite is supposed to be ornamented against
7747         // this is really slow, so cache the result
7748         if (s == -1 || !wsprinfo[s].wall || (spritechanged[s] != wsprinfo[s].srev) ||
7749             (w != -1 && wallchanged[w] != wsprinfo[s].wrev))
7750         {
7751             w = polymost_findwall(tspr, &tsiz, &walldist);
7752 
7753             if (s != -1)
7754             {
7755                 wallspriteinfo_t *ws = &wsprinfo[s];
7756                 ws->wall = w;
7757 
7758                 if (w != -1)
7759                 {
7760                     ws->wdist = walldist;
7761                     ws->wrev = wallchanged[w];
7762                     ws->srev = spritechanged[s];
7763                 }
7764             }
7765         }
7766         else if (s != -1)
7767             walldist = wsprinfo[s].wdist;
7768 
7769         // detect if the sprite is either on the wall line or the wall line and sprite intersect
7770         if (w != -1)
7771         {
7772             vec2_t v = { /*Blrintf(vf.x)*/(int)vf.x, /*Blrintf(vf.y)*/(int)vf.y };
7773 
7774             if (walldist <= 2 || ((pos.x - v.x) + (pos.x + v.x)) == (wall[w].x + POINT2(w).x) ||
7775                 ((pos.y - v.y) + (pos.y + v.y)) == (wall[w].y + POINT2(w).y) ||
7776                 polymost_lintersect(pos.x - v.x, pos.y - v.y, pos.x + v.x, pos.y + v.y, wall[w].x, wall[w].y,
7777                                     POINT2(w).x, POINT2(w).y))
7778             {
7779                 int32_t const ang = getangle(wall[w].x - POINT2(w).x, wall[w].y - POINT2(w).y);
7780                 float const foffs = TSPR_OFFSET(tspr);
7781                 offs = { -(float)(sintable[(ang + 1024) & 2047] >> 6) * foffs,
7782                          -(float)(sintable[(ang + 512) & 2047] >> 6) * foffs,
7783                          0.f};
7784             }
7785         }
7786 
7787         //POGO: for full compatibility, facing sprites should also clip similarly (see polymost_drawsprite())
7788         // Clip sprites to ceilings/floors when no parallaxing
7789         float fullCenterYOff = off.y + (((globalorientation & CSTAT_SPRITE_YCENTER) != 0) * 2.f)
7790                                             * ftsiz.y * ((float)tspr->yrepeat);
7791         if ((!(sector[tspr->sectnum].ceilingstat & 1)) &&
7792             sector[tspr->sectnum].ceilingz > tspr->z + fullCenterYOff - ((tspr->yrepeat * tsiz.y) << 2))
7793         {
7794             float clipScale = ((float) (tspr->z + fullCenterYOff - sector[tspr->sectnum].ceilingz))/((float)((tspr->yrepeat * tsiz.y) << 2));
7795             if (clipScale <= 0.f)
7796             {
7797                 //don't draw sprites fully clipped by the ceiling
7798                 return;
7799             }
7800 
7801             texScale[1] *= clipScale;
7802             texOffset[1] += (1.f-clipScale)*(-1.f*((globalorientation & CSTAT_SPRITE_YFLIP) == CSTAT_SPRITE_YFLIP));
7803             vertScale *= clipScale;
7804             combinedClipScale *= clipScale;
7805         }
7806         if ((!(sector[tspr->sectnum].floorstat & 1)) &&
7807             sector[tspr->sectnum].floorz < tspr->z + fullCenterYOff)
7808         {
7809             float span = ((tspr->yrepeat * tsiz.y) << 2) - (tspr->z + fullCenterYOff - sector[tspr->sectnum].floorz);
7810             float clipScale = span/((float)((tspr->yrepeat * tsiz.y) << 2));
7811             if (clipScale <= 0.f)
7812             {
7813                 //don't draw sprites fully clipped by the floor
7814                 return;
7815             }
7816 
7817             texScale[1] *= clipScale;
7818             texOffset[1] += (1.f-clipScale)*(-1.f*((globalorientation & CSTAT_SPRITE_YFLIP) != CSTAT_SPRITE_YFLIP));
7819             vertScale *= clipScale;
7820             combinedClipScale *= clipScale;
7821             off.y += (float) (((tspr->yrepeat * tsiz.y) << 2) - span);
7822         }
7823         if (globalorientation & CSTAT_SPRITE_YCENTER)
7824         {
7825             combinedClipScale = 1.f;
7826         }
7827     }
7828 
7829     off.x *= ((float) ((globalorientation & CSTAT_SPRITE_XFLIP) != 0))*-2.f + 1.f;
7830     off.y *= ((float) (((globalorientation & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_FACING) &
7831                        ((globalorientation & CSTAT_SPRITE_YFLIP) != 0)))*-2.f + 1.f;
7832 
7833     if ((globalorientation & CSTAT_SPRITE_ALIGNMENT)==CSTAT_SPRITE_ALIGNMENT_FLOOR)
7834     {
7835         vertScale = -vertScale;
7836         orientationOffset.x += ftsiz.y*((float) tspr->yrepeat)*(1.f/8.f);
7837 
7838         // unfortunately, offsetting by only 1 isn't enough on most Android devices
7839         if (tspr->z == sec->ceilingz || tspr->z == sec->ceilingz + 1)
7840             tspr->z = sec->ceilingz + 2, orientationOffset.y += (tspr->owner & 31);
7841 
7842         if (tspr->z == sec->floorz || tspr->z == sec->floorz - 1)
7843             tspr->z = sec->floorz - 2, orientationOffset.y -= ((tspr->owner & 31));
7844 
7845         angle = tspr->ang;
7846     }
7847     else
7848     {
7849         off.y -= (((globalorientation & CSTAT_SPRITE_YCENTER) != 0) * 2.f +
7850                   ((globalorientation & CSTAT_SPRITE_YFLIP) != 0)*-4.f)
7851                       * combinedClipScale * ftsiz.y * ((float)tspr->yrepeat);
7852     }
7853 
7854     vec3f_t a0;
7855     a0.x = ((float)(pos.y-globalposy)+offs.y) * -(1.f/1024.f)*-f;
7856     a0.y = ((float)(pos.x-globalposx)+offs.x) * (1.f/1024.f)*f;
7857     a0.z = ((float)(tspr->z-globalposz)+offs.z) * -(1.f/16384.f)*g;
7858     orientationOffset.x *= -(1.f/1024.f)*-f;
7859     orientationOffset.y *= -(1.f/16384.f)*g;
7860     calcmat(a0, &orientationOffset, f, modelViewMatrix, angle);
7861 
7862     if ((globalorientation & CSTAT_SPRITE_ALIGNMENT)==CSTAT_SPRITE_ALIGNMENT_FLOOR)
7863     {
7864         float temp = modelViewMatrix[4]; modelViewMatrix[4] = modelViewMatrix[8]*16.f; modelViewMatrix[8] = -temp*(1.f/16.f);
7865         temp = modelViewMatrix[5]; modelViewMatrix[5] = modelViewMatrix[9]*16.f; modelViewMatrix[9] = -temp*(1.f/16.f);
7866         temp = modelViewMatrix[6]; modelViewMatrix[6] = modelViewMatrix[10]*16.f; modelViewMatrix[10] = -temp*(1.f/16.f);
7867     }
7868 
7869     // mirrors
7870     if (grhalfxdown10x < 0)
7871     {
7872         modelViewMatrix[0] = -modelViewMatrix[0]; modelViewMatrix[4] = -modelViewMatrix[4]; modelViewMatrix[8] = -modelViewMatrix[8]; modelViewMatrix[12] = -modelViewMatrix[12];
7873     }
7874 
7875     float ratio = 1.0f/get_projhack_ratio();
7876     float projectionMatrix[4*4] =
7877         {
7878             fydimen * ratio,    0.0f,  1.0f,            0.0f,
7879                        0.0f, fxdimen,  1.0f,            0.0f,
7880                        0.0f,    0.0f,  1.0f, fydimen * ratio,
7881                        0.0f,    0.0f, -1.0f,            0.0f
7882         };
7883 
7884     float scaleMatrix[4*4] =
7885         {
7886             horzScale,      0.0f,  0.0f, 0.0f,
7887                  0.0f, vertScale,  0.0f, 0.0f,
7888                  0.0f,      0.0f,  1.0f, 0.0f,
7889                  0.0f,      0.0f,  0.0f, 1.0f
7890         };
7891     float offsetMatrix[4*4] =
7892         {
7893                              1.0f,                         0.0f,  0.0f, 0.0f,
7894                              0.0f,                         1.0f,  0.0f, 0.0f,
7895                              0.0f,                         0.0f,  1.0f, 0.0f,
7896             -off.x*(1.f/1024.f)*f,      off.y * (1.f/16384.f)*g,  0.0f, 1.0f
7897         };
7898 
7899     multiplyMatrix4f(transformMatrix, scaleMatrix);
7900     multiplyMatrix4f(transformMatrix, offsetMatrix);
7901     //POGOTODO: for later optimization purposes (batching/caching), I need to split the modelViewMatrix into modelMatrix and viewMatrix
7902     multiplyMatrix4f(transformMatrix, modelViewMatrix);
7903 
7904     //POGOTODO: I should instead implement one-sided sprites & culling by switching the xflip/yflip from flipping scale to instead flipping texScale
7905     //          Doing that will allow me to simplify a lot of this code, but it will require a lot of changes
7906     polymost2_drawVBO(GL_TRIANGLE_STRIP,
7907                       quadVertsID,
7908                       0,
7909                       4,
7910                       projectionMatrix,
7911                       transformMatrix,
7912                       method,
7913                       texScale,
7914                       texOffset,
7915                       ((globalorientation & CSTAT_SPRITE_ONE_SIDED) != 0)*3 &
7916                        ((((globalorientation & CSTAT_SPRITE_XFLIP) != 0) ^
7917                          ((globalorientation & CSTAT_SPRITE_YFLIP) != 0))+1));
7918 
7919     drawpoly_srepeat = 0;
7920     drawpoly_trepeat = 0;
7921 
7922     tilesiz[globalpicnum] = oldsiz;
7923 }
7924 
polymost_mdeditorfunc(tspriteptr_t const tspr)7925 void polymost_mdeditorfunc(tspriteptr_t const tspr)
7926 {
7927     if (searchit == 0)
7928         return;
7929 
7930     // Project 3D to 2D
7931     vec3f_t xyz = { (float)tspr->x, (float)tspr->y, (float)tspr->z };
7932     vec2_t off = { tspr->xoffset, tspr->yoffset };
7933 
7934     if (tspr->cstat&CSTAT_SPRITE_XFLIP) off.x = -off.x;
7935     if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_FACING
7936         && tspr->cstat&CSTAT_SPRITE_YFLIP) off.y = -off.y;
7937 
7938     vec2f_t s0 = { (float)(xyz.x - globalposx),
7939                     (float)(xyz.y - globalposy)};
7940 
7941     vec2f_t p0 = { s0.y * gcosang - s0.x * gsinang, s0.x * gcosang2 + s0.y * gsinang2 };
7942 
7943     if (p0.y <= SCISDIST)
7944         return;
7945 
7946     float const ryp0 = 1.f / p0.y;
7947     s0 = { ghalfx * p0.x * ryp0 + ghalfx, ((float)(xyz.z - globalposz)) * gyxscale * ryp0 + ghoriz };
7948 
7949     float const f = ryp0 * fxdimen * (1.0f / 160.f);
7950 
7951     vec2_16_t const oldsiz = tilesiz[globalpicnum];
7952     vec2_t tsiz = { oldsiz.x, oldsiz.y };
7953 
7954     vec2f_t const ftsiz = { (float)tsiz.x, (float)tsiz.y };
7955 
7956     vec2f_t ff = { ((float)tspr->xrepeat) * f,
7957                     ((float)tspr->yrepeat) * f * ((float)yxaspect * (1.0f / 65536.f)) };
7958 
7959     if (tsiz.x & 1)
7960         s0.x += ff.x * 0.5f;
7961     if (globalorientation & 128 && tsiz.y & 1)
7962         s0.y += ff.y * 0.5f;
7963 
7964     s0.x -= ff.x * (float) off.x;
7965     s0.y -= ff.y * (float) off.y;
7966 
7967     ff.x *= ftsiz.x;
7968     ff.y *= ftsiz.y;
7969 
7970     vec2f_t pxy[4];
7971 
7972     pxy[0].x = pxy[3].x = s0.x - ff.x * 0.5f;
7973     pxy[1].x = pxy[2].x = s0.x + ff.x * 0.5f;
7974 
7975     if (!(globalorientation & 128))
7976     {
7977         pxy[0].y = pxy[1].y = s0.y - ff.y;
7978         pxy[2].y = pxy[3].y = s0.y;
7979     }
7980     else
7981     {
7982         pxy[0].y = pxy[1].y = s0.y - ff.y * 0.5f;
7983         pxy[2].y = pxy[3].y = s0.y + ff.y * 0.5f;
7984     }
7985 
7986 #ifdef YAX_ENABLE
7987     if (yax_globallev == YAX_MAXDRAWS || searchit == 2)
7988 #endif
7989     if (searchit >= 1)
7990     {
7991         otex.d = ryp0 * gviewxrange;
7992         xtex.d = ytex.d = 0;
7993         psectnum = tspr->sectnum;
7994         pwallnum = tspr->owner;
7995         psearchstat = 3;
7996         doeditorcheck = 1;
7997         polymost_polyeditorfunc(pxy, 4);
7998     }
7999 }
8000 
polymost_voxeditorfunc(voxmodel_t * m,tspriteptr_t const tspr)8001 void polymost_voxeditorfunc(voxmodel_t *m, tspriteptr_t const tspr)
8002 {
8003     if (searchit == 0)
8004         return;
8005 
8006     // Project 3D to 2D
8007     vec3f_t xyz = { (float)tspr->x, (float)tspr->y, (float)tspr->z };
8008     vec2_t off = { tspr->xoffset, tspr->yoffset };
8009 
8010     if (!(tspr->cstat&CSTAT_SPRITE_YCENTER))
8011     {
8012         if (tspr->cstat&CSTAT_SPRITE_YFLIP)
8013             xyz.z += m->piv.z*tspr->yrepeat*m->scale*(1.f/256.f);
8014         else
8015             xyz.z -= m->piv.z*tspr->yrepeat*m->scale*(1.f/256.f);
8016     }
8017 
8018     if (tspr->cstat&CSTAT_SPRITE_XFLIP) off.x = -off.x;
8019     if (tspr->cstat&CSTAT_SPRITE_YFLIP) off.y = -off.y;
8020 
8021     if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_WALL)
8022     {
8023         const int32_t xv = tspr->xrepeat * sintable[(tspr->ang + 2560 + 1536) & 2047];
8024         const int32_t yv = tspr->xrepeat * sintable[(tspr->ang + 2048 + 1536) & 2047];
8025 
8026 
8027         xyz.x -= xv*off.x*(1.f/65536.f);
8028         xyz.y -= yv*off.x*(1.f/65536.f);
8029     }
8030 
8031     off.x = 0;
8032 
8033     vec2f_t s0 = { (float)(xyz.x - globalposx),
8034                     (float)(xyz.y - globalposy)};
8035 
8036     vec2f_t p0 = { s0.y * gcosang - s0.x * gsinang, s0.x * gcosang2 + s0.y * gsinang2 };
8037 
8038     if (p0.y <= SCISDIST)
8039         return;
8040 
8041     float const ryp0 = 1.f / p0.y;
8042     s0 = { ghalfx * p0.x * ryp0 + ghalfx, ((float)(xyz.z - globalposz)) * gyxscale * ryp0 + ghoriz };
8043 
8044     float const f = ryp0 * fxdimen * (1.0f / 160.f) * m->scale;
8045 
8046     vec2_t tsiz = { (m->siz.x+m->siz.y)>>1, m->siz.z };
8047 
8048     vec2f_t const ftsiz = { (float)tsiz.x, (float)tsiz.y };
8049 
8050     vec2f_t ff = { ((float)tspr->xrepeat) * f,
8051                     ((float)tspr->yrepeat) * f * ((float)yxaspect * (1.0f / 65536.f)) };
8052 
8053     if (tsiz.x & 1)
8054         s0.x += ff.x * 0.5f;
8055     if (globalorientation & 128 && tsiz.y & 1)
8056         s0.y += ff.y * 0.5f;
8057 
8058     s0.x -= ff.x * (float) off.x;
8059     s0.y -= ff.y * (float) off.y;
8060 
8061     ff.x *= ftsiz.x;
8062     ff.y *= ftsiz.y;
8063 
8064     vec2f_t pxy[4];
8065 
8066     pxy[0].x = pxy[3].x = s0.x - ff.x * 0.5f;
8067     pxy[1].x = pxy[2].x = s0.x + ff.x * 0.5f;
8068 
8069     if (!(globalorientation & 128))
8070     {
8071         pxy[0].y = pxy[1].y = s0.y - ff.y;
8072         pxy[2].y = pxy[3].y = s0.y;
8073     }
8074     else
8075     {
8076         pxy[0].y = pxy[1].y = s0.y - ff.y * 0.5f;
8077         pxy[2].y = pxy[3].y = s0.y + ff.y * 0.5f;
8078     }
8079 
8080 #ifdef YAX_ENABLE
8081     if (yax_globallev == YAX_MAXDRAWS || searchit == 2)
8082 #endif
8083     if (searchit >= 1)
8084     {
8085         otex.d = ryp0 * gviewxrange;
8086         xtex.d = ytex.d = 0;
8087         psectnum = tspr->sectnum;
8088         pwallnum = tspr->owner;
8089         psearchstat = 3;
8090         doeditorcheck = 1;
8091         polymost_polyeditorfunc(pxy, 4);
8092     }
8093 }
8094 
polymost_drawsprite(int32_t snum)8095 void polymost_drawsprite(int32_t snum)
8096 {
8097     if (r_enablepolymost2)
8098     {
8099         polymost2_drawsprite(snum);
8100         return;
8101     }
8102 
8103     auto const tspr = tspriteptr[snum];
8104 
8105     if (EDUKE32_PREDICT_FALSE(bad_tspr(tspr)))
8106         return;
8107 
8108     usectorptr_t sec;
8109 
8110     int32_t spritenum = tspr->owner;
8111 
8112     polymost_outputGLDebugMessage(3, "polymost_drawsprite(snum:%d)", snum);
8113 
8114     if ((tspr->cstat&48) != 48)
8115         tileUpdatePicnum(&tspr->picnum, spritenum + 32768);
8116 
8117     globalpicnum = tspr->picnum;
8118     globalshade = tspr->shade;
8119     globalpal = tspr->pal;
8120     globalorientation = tspr->cstat;
8121     globvis = globalvisibility;
8122 
8123     if (sector[tspr->sectnum].visibility != 0)
8124         globvis = mulscale4(globvis, (uint8_t)(sector[tspr->sectnum].visibility + 16));
8125 
8126     globvis2 = globalvisibility2;
8127     if (sector[tspr->sectnum].visibility != 0)
8128         globvis2 = mulscale4(globvis2, (uint8_t)(sector[tspr->sectnum].visibility + 16));
8129     polymost_setVisibility(globvis2);
8130 
8131     vec2_t off = { 0, 0 };
8132 
8133     if ((globalorientation & 48) != 48)  // only non-voxel sprites should do this
8134     {
8135         int const flag = usehightile && h_xsize[globalpicnum];
8136         off = { flag ? h_xoffs[globalpicnum] : picanm[globalpicnum].xofs,
8137                 flag ? h_yoffs[globalpicnum] : picanm[globalpicnum].yofs };
8138         if (!(tspr->clipdist & TSPR_FLAGS_SLOPE_SPRITE))
8139         {
8140             off.x += tspr->xoffset;
8141             off.y += tspr->yoffset;
8142         }
8143     }
8144 
8145     int32_t method = DAMETH_MASK | DAMETH_CLAMPED;
8146 
8147     if (tspr->cstat & 2)
8148         method = DAMETH_CLAMPED | ((tspr->cstat & 512) ? DAMETH_TRANS2 : DAMETH_TRANS1);
8149 
8150     handle_blend(!!(tspr->cstat & 2), tspr->blend, !!(tspr->cstat & 512));
8151 
8152     drawpoly_alpha = spriteext[spritenum].alpha;
8153     drawpoly_blend = tspr->blend;
8154 
8155     sec = (usectorptr_t)&sector[tspr->sectnum];
8156 
8157     if (!polymost_usetileshades() || (usehightile && hicfindsubst(globalpicnum, globalpal, hictinting[globalpal].f & HICTINT_ALWAYSUSEART))
8158         || (usemodels && md_tilehasmodel(globalpicnum, globalpal) >= 0))
8159         calc_and_apply_fog(fogshade(globalshade, globalpal), sec->visibility, get_floor_fogpal(sec));
8160 
8161     while (!(spriteext[spritenum].flags & SPREXT_NOTMD))
8162     {
8163         if (usemodels && tile2model[Ptile2tile(tspr->picnum, tspr->pal)].modelid >= 0 &&
8164             tile2model[Ptile2tile(tspr->picnum, tspr->pal)].framenum >= 0)
8165         {
8166             if (polymost_mddraw(tspr))
8167             {
8168                 if (editstatus)
8169                     polymost_mdeditorfunc(tspr);
8170 
8171                 return;
8172             }
8173             break;  // else, render as flat sprite
8174         }
8175 
8176         if (usevoxels && (tspr->cstat & 48) != 48 && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]])
8177         {
8178             if (polymost_voxdraw(voxmodels[tiletovox[tspr->picnum]], tspr))
8179             {
8180                 if (editstatus)
8181                     polymost_voxeditorfunc(voxmodels[tiletovox[tspr->picnum]], tspr);
8182 
8183                 return;
8184             }
8185             break;  // else, render as flat sprite
8186         }
8187 
8188         if ((tspr->cstat & 48) == 48 && voxmodels[tspr->picnum])
8189         {
8190             polymost_voxdraw(voxmodels[tspr->picnum], tspr);
8191 
8192             if (editstatus)
8193                 polymost_voxeditorfunc(voxmodels[tspr->picnum], tspr);
8194 
8195             return;
8196         }
8197 
8198         break;
8199     }
8200 
8201     vec3_t pos = tspr->pos;
8202 
8203     if (spriteext[spritenum].flags & SPREXT_AWAY1)
8204     {
8205         pos.x += (sintable[(tspr->ang + 512) & 2047] >> 13);
8206         pos.y += (sintable[(tspr->ang) & 2047] >> 13);
8207     }
8208     else if (spriteext[spritenum].flags & SPREXT_AWAY2)
8209     {
8210         pos.x -= (sintable[(tspr->ang + 512) & 2047] >> 13);
8211         pos.y -= (sintable[(tspr->ang) & 2047] >> 13);
8212     }
8213 
8214     vec2_16_t const oldsiz = tilesiz[globalpicnum];
8215     vec2_t tsiz = { oldsiz.x, oldsiz.y };
8216 
8217     if (usehightile && h_xsize[globalpicnum])
8218         tsiz = { h_xsize[globalpicnum], h_ysize[globalpicnum] };
8219 
8220     if (tsiz.x <= 0 || tsiz.y <= 0)
8221         return;
8222 
8223     polymost_updaterotmat();
8224 
8225     vec2f_t const ftsiz = { (float) tsiz.x, (float) tsiz.y };
8226 
8227     switch ((globalorientation >> 4) & 3)
8228     {
8229         case 0:  // Face sprite
8230         {
8231             // Project 3D to 2D
8232             if (globalorientation & 4)
8233                 off.x = -off.x;
8234             // NOTE: yoff not negated not for y flipping, unlike wall and floor
8235             // aligned sprites.
8236 
8237             int const ang = (getangle(tspr->x - globalposx, tspr->y - globalposy) + 1024) & 2047;
8238 
8239             float const foffs = TSPR_OFFSET(tspr);
8240 
8241             vec2f_t const offs = { (float) (sintable[(ang + 512) & 2047] >> 6) * foffs,
8242                 (float) (sintable[(ang) & 2047] >> 6) * foffs };
8243 
8244             vec2f_t s0 = { (float)(tspr->x - globalposx) + offs.x,
8245                            (float)(tspr->y - globalposy) + offs.y};
8246 
8247             vec2f_t p0 = { s0.y * gcosang - s0.x * gsinang, s0.x * gcosang2 + s0.y * gsinang2 };
8248 
8249             if (p0.y <= SCISDIST)
8250                 goto _drawsprite_return;
8251 
8252             float const ryp0 = 1.f / p0.y;
8253             s0 = { ghalfx * p0.x * ryp0 + ghalfx, ((float)(pos.z - globalposz)) * gyxscale * ryp0 + ghoriz };
8254 
8255             float const f = ryp0 * fxdimen * (1.0f / 160.f);
8256 
8257             vec2f_t ff = { ((float)tspr->xrepeat) * f,
8258                            ((float)tspr->yrepeat) * f * ((float)yxaspect * (1.0f / 65536.f)) };
8259 
8260             if (tsiz.x & 1)
8261                 s0.x += ff.x * 0.5f;
8262             if (globalorientation & 128 && tsiz.y & 1)
8263                 s0.y += ff.y * 0.5f;
8264 
8265             s0.x -= ff.x * (float) off.x;
8266             s0.y -= ff.y * (float) off.y;
8267 
8268             ff.x *= ftsiz.x;
8269             ff.y *= ftsiz.y;
8270 
8271             vec2f_t pxy[4];
8272 
8273             pxy[0].x = pxy[3].x = s0.x - ff.x * 0.5f;
8274             pxy[1].x = pxy[2].x = s0.x + ff.x * 0.5f;
8275             if (!(globalorientation & 128))
8276             {
8277                 pxy[0].y = pxy[1].y = s0.y - ff.y;
8278                 pxy[2].y = pxy[3].y = s0.y;
8279             }
8280             else
8281             {
8282                 pxy[0].y = pxy[1].y = s0.y - ff.y * 0.5f;
8283                 pxy[2].y = pxy[3].y = s0.y + ff.y * 0.5f;
8284             }
8285 
8286             xtex.d = ytex.d = ytex.u = xtex.v = 0;
8287             otex.d = ryp0 * gviewxrange;
8288 
8289             if (!(globalorientation & 4))
8290             {
8291                 xtex.u = ftsiz.x * otex.d / (pxy[1].x - pxy[0].x + .002f);
8292                 otex.u = -xtex.u * (pxy[0].x - .001f);
8293             }
8294             else
8295             {
8296                 xtex.u = ftsiz.x * otex.d / (pxy[0].x - pxy[1].x - .002f);
8297                 otex.u = -xtex.u * (pxy[1].x + .001f);
8298             }
8299 
8300             if (!(globalorientation & 8))
8301             {
8302                 ytex.v = ftsiz.y * otex.d / (pxy[3].y - pxy[0].y + .002f);
8303                 otex.v = -ytex.v * (pxy[0].y - .001f);
8304             }
8305             else
8306             {
8307                 ytex.v = ftsiz.y * otex.d / (pxy[0].y - pxy[3].y - .002f);
8308                 otex.v = -ytex.v * (pxy[3].y + .001f);
8309             }
8310 
8311             // sprite panning
8312             if (spriteext[spritenum].xpanning)
8313             {
8314                 ytex.u -= ytex.d * ((float) (spriteext[spritenum].xpanning) * (1.0f / 255.f)) * ftsiz.x;
8315                 otex.u -= otex.d * ((float) (spriteext[spritenum].xpanning) * (1.0f / 255.f)) * ftsiz.x;
8316                 drawpoly_srepeat = 1;
8317             }
8318 
8319             if (spriteext[spritenum].ypanning)
8320             {
8321                 ytex.v -= ytex.d * ((float) (spriteext[spritenum].ypanning) * (1.0f / 255.f)) * ftsiz.y;
8322                 otex.v -= otex.d * ((float) (spriteext[spritenum].ypanning) * (1.0f / 255.f)) * ftsiz.y;
8323                 drawpoly_trepeat = 1;
8324             }
8325 
8326             // Clip sprites to ceilings/floors when no parallaxing and not sloped
8327             if (!(sector[tspr->sectnum].ceilingstat & 3))
8328             {
8329                 s0.y = ((float) (sector[tspr->sectnum].ceilingz - globalposz)) * gyxscale * ryp0 + ghoriz;
8330                 if (pxy[0].y < s0.y)
8331                     pxy[0].y = pxy[1].y = s0.y;
8332             }
8333 
8334             if (!(sector[tspr->sectnum].floorstat & 3))
8335             {
8336                 s0.y = ((float) (sector[tspr->sectnum].floorz - globalposz)) * gyxscale * ryp0 + ghoriz;
8337                 if (pxy[2].y > s0.y)
8338                     pxy[2].y = pxy[3].y = s0.y;
8339             }
8340 
8341             tilesiz[globalpicnum] = { (int16_t)tsiz.x, (int16_t)tsiz.y };
8342             pow2xsplit = 0;
8343 #ifdef YAX_ENABLE
8344             if (yax_globallev == YAX_MAXDRAWS || searchit == 2)
8345 #endif
8346             if (searchit >= 1)
8347             {
8348                 psectnum = tspr->sectnum;
8349                 pwallnum = spritenum;
8350                 psearchstat = 3;
8351                 doeditorcheck = 1;
8352             }
8353             polymost_drawpoly(pxy, 4, method);
8354             doeditorcheck = 0;
8355 
8356             drawpoly_srepeat = 0;
8357             drawpoly_trepeat = 0;
8358         }
8359         break;
8360 
8361         case 1:  // Wall sprite
8362         {
8363             // Project 3D to 2D
8364             if (globalorientation & 4)
8365                 off.x = -off.x;
8366 
8367             if (globalorientation & 8)
8368                 off.y = -off.y;
8369 
8370             vec2f_t const extent = { (float)tspr->xrepeat * (float)sintable[(tspr->ang) & 2047] * (1.0f / 65536.f),
8371                                      (float)tspr->xrepeat * (float)sintable[(tspr->ang + 1536) & 2047] * (1.0f / 65536.f) };
8372 
8373             float f = (float)(tsiz.x >> 1) + (float)off.x;
8374 
8375             vec2f_t const vf = { extent.x * f, extent.y * f };
8376 
8377             vec2f_t vec0 = { (float)(pos.x - globalposx) - vf.x,
8378                              (float)(pos.y - globalposy) - vf.y };
8379 
8380             int32_t const s = tspr->owner;
8381             int32_t walldist = 1;
8382             int32_t w = (s == -1) ? -1 : wsprinfo[s].wall;
8383 
8384             // find the wall most likely to be what the sprite is supposed to be ornamented against
8385             // this is really slow, so cache the result
8386             if (s == -1 || !wsprinfo[s].wall || (spritechanged[s] != wsprinfo[s].srev) ||
8387                 (w != -1 && wallchanged[w] != wsprinfo[s].wrev))
8388             {
8389                 w = polymost_findwall(tspr, &tsiz, &walldist);
8390 
8391                 if (s != -1)
8392                 {
8393                     wallspriteinfo_t *ws = &wsprinfo[s];
8394                     ws->wall = w;
8395 
8396                     if (w != -1)
8397                     {
8398                         ws->wdist = walldist;
8399                         ws->wrev = wallchanged[w];
8400                         ws->srev = spritechanged[s];
8401                     }
8402                 }
8403             }
8404             else if (s != -1)
8405                 walldist = wsprinfo[s].wdist;
8406 
8407             // detect if the sprite is either on the wall line or the wall line and sprite intersect
8408             if (w != -1)
8409             {
8410                 vec2_t v = { /*Blrintf(vf.x)*/(int)vf.x, /*Blrintf(vf.y)*/(int)vf.y };
8411 
8412                 if (walldist <= 2 || ((pos.x - v.x) + (pos.x + v.x)) == (wall[w].x + POINT2(w).x) ||
8413                     ((pos.y - v.y) + (pos.y + v.y)) == (wall[w].y + POINT2(w).y) ||
8414                     polymost_lintersect(pos.x - v.x, pos.y - v.y, pos.x + v.x, pos.y + v.y, wall[w].x, wall[w].y,
8415                                         POINT2(w).x, POINT2(w).y))
8416                 {
8417                     int32_t const ang = getangle(wall[w].x - POINT2(w).x, wall[w].y - POINT2(w).y);
8418                     float const foffs = TSPR_OFFSET(tspr);
8419                     vec2f_t const offs = { (float)(sintable[(ang + 1024) & 2047] >> 6) * foffs,
8420                                      (float)(sintable[(ang + 512) & 2047] >> 6) * foffs};
8421 
8422                     vec0.x -= offs.x;
8423                     vec0.y -= offs.y;
8424                 }
8425             }
8426 
8427             vec2f_t p0 = { vec0.y * gcosang - vec0.x * gsinang,
8428                            vec0.x * gcosang2 + vec0.y * gsinang2 };
8429 
8430             vec2f_t const pp = { extent.x * ftsiz.x + vec0.x,
8431                                  extent.y * ftsiz.x + vec0.y };
8432 
8433             vec2f_t p1 = { pp.y * gcosang - pp.x * gsinang,
8434                            pp.x * gcosang2 + pp.y * gsinang2 };
8435 
8436             if ((p0.y <= SCISDIST) && (p1.y <= SCISDIST))
8437                 goto _drawsprite_return;
8438 
8439             // Clip to close parallel-screen plane
8440             vec2f_t const op0 = p0;
8441 
8442             float t0 = 0.f, t1 = 1.f;
8443 
8444             if (p0.y < SCISDIST)
8445             {
8446                 t0 = (SCISDIST - p0.y) / (p1.y - p0.y);
8447                 p0 = { (p1.x - p0.x) * t0 + p0.x, SCISDIST };
8448             }
8449 
8450             if (p1.y < SCISDIST)
8451             {
8452                 t1 = (SCISDIST - op0.y) / (p1.y - op0.y);
8453                 p1 = { (p1.x - op0.x) * t1 + op0.x, SCISDIST };
8454             }
8455 
8456             f = 1.f / p0.y;
8457             const float ryp0 = f * gyxscale;
8458             float sx0 = ghalfx * p0.x * f + ghalfx;
8459 
8460             f = 1.f / p1.y;
8461             const float ryp1 = f * gyxscale;
8462             float sx1 = ghalfx * p1.x * f + ghalfx;
8463 
8464             pos.z -= ((off.y * tspr->yrepeat) << 2);
8465 
8466             if (globalorientation & 128)
8467             {
8468                 pos.z += ((tsiz.y * tspr->yrepeat) << 1);
8469 
8470                 if (tsiz.y & 1)
8471                     pos.z += (tspr->yrepeat << 1);  // Odd yspans
8472             }
8473 
8474             xtex.d = (ryp0 - ryp1) * gxyaspect / (sx0 - sx1);
8475             ytex.d = 0;
8476             otex.d = ryp0 * gxyaspect - xtex.d * sx0;
8477 
8478             if (globalorientation & 4)
8479             {
8480                 t0 = 1.f - t0;
8481                 t1 = 1.f - t1;
8482             }
8483 
8484             // sprite panning
8485             if (spriteext[spritenum].xpanning)
8486             {
8487                 float const xpan = ((float)(spriteext[spritenum].xpanning) * (1.0f / 255.f));
8488                 t0 -= xpan;
8489                 t1 -= xpan;
8490                 drawpoly_srepeat = 1;
8491             }
8492 
8493             xtex.u = (t0 * ryp0 - t1 * ryp1) * gxyaspect * ftsiz.x / (sx0 - sx1);
8494             ytex.u = 0;
8495             otex.u = t0 * ryp0 * gxyaspect * ftsiz.x - xtex.u * sx0;
8496 
8497             f = ((float) tspr->yrepeat) * ftsiz.y * 4;
8498 
8499             float sc0 = ((float) (pos.z - globalposz - f)) * ryp0 + ghoriz;
8500             float sc1 = ((float) (pos.z - globalposz - f)) * ryp1 + ghoriz;
8501             float sf0 = ((float) (pos.z - globalposz)) * ryp0 + ghoriz;
8502             float sf1 = ((float) (pos.z - globalposz)) * ryp1 + ghoriz;
8503 
8504             // gvx*sx0 + gvy*sc0 + gvo = 0
8505             // gvx*sx1 + gvy*sc1 + gvo = 0
8506             // gvx*sx0 + gvy*sf0 + gvo = tsizy*(gdx*sx0 + gdo)
8507             f = ftsiz.y * (xtex.d * sx0 + otex.d) / ((sx0 - sx1) * (sc0 - sf0));
8508 
8509             if (!(globalorientation & 8))
8510             {
8511                 xtex.v = (sc0 - sc1) * f;
8512                 ytex.v = (sx1 - sx0) * f;
8513                 otex.v = -xtex.v * sx0 - ytex.v * sc0;
8514             }
8515             else
8516             {
8517                 xtex.v = (sf1 - sf0) * f;
8518                 ytex.v = (sx0 - sx1) * f;
8519                 otex.v = -xtex.v * sx0 - ytex.v * sf0;
8520             }
8521 
8522             // sprite panning
8523             if (spriteext[spritenum].ypanning)
8524             {
8525                 float const ypan = ((float)(spriteext[spritenum].ypanning) * (1.0f / 255.f)) * ftsiz.y;
8526                 xtex.v -= xtex.d * ypan;
8527                 ytex.v -= ytex.d * ypan;
8528                 otex.v -= otex.d * ypan;
8529                 drawpoly_trepeat = 1;
8530             }
8531 
8532             // Clip sprites to ceilings/floors when no parallaxing
8533             if (!(sector[tspr->sectnum].ceilingstat & 1))
8534             {
8535                 if (sector[tspr->sectnum].ceilingz > pos.z - (float)((tspr->yrepeat * tsiz.y) << 2))
8536                 {
8537                     sc0 = (float)(sector[tspr->sectnum].ceilingz - globalposz) * ryp0 + ghoriz;
8538                     sc1 = (float)(sector[tspr->sectnum].ceilingz - globalposz) * ryp1 + ghoriz;
8539                 }
8540             }
8541             if (!(sector[tspr->sectnum].floorstat & 1))
8542             {
8543                 if (sector[tspr->sectnum].floorz < pos.z)
8544                 {
8545                     sf0 = (float)(sector[tspr->sectnum].floorz - globalposz) * ryp0 + ghoriz;
8546                     sf1 = (float)(sector[tspr->sectnum].floorz - globalposz) * ryp1 + ghoriz;
8547                 }
8548             }
8549 
8550             if (sx0 > sx1)
8551             {
8552                 if (globalorientation & 64)
8553                     goto _drawsprite_return;  // 1-sided sprite
8554 
8555                 swapfloat(&sx0, &sx1);
8556                 swapfloat(&sc0, &sc1);
8557                 swapfloat(&sf0, &sf1);
8558             }
8559 
8560             vec2f_t const pxy[4] = { { sx0, sc0 }, { sx1, sc1 }, { sx1, sf1 }, { sx0, sf0 } };
8561 
8562             tilesiz[globalpicnum] = { (int16_t)tsiz.x, (int16_t)tsiz.y };
8563             pow2xsplit = 0;
8564 #ifdef YAX_ENABLE
8565             if (yax_globallev == YAX_MAXDRAWS || searchit == 2)
8566 #endif
8567             if (searchit >= 1)
8568             {
8569                 psectnum = tspr->sectnum;
8570                 pwallnum = spritenum;
8571                 psearchstat = 3;
8572                 doeditorcheck = 1;
8573             }
8574             polymost_drawpoly(pxy, 4, method);
8575             doeditorcheck = 0;
8576 
8577             drawpoly_srepeat = 0;
8578             drawpoly_trepeat = 0;
8579         }
8580         break;
8581 
8582         case 2:  // Floor sprite
8583             globvis2 = globalhisibility2;
8584             if (sector[tspr->sectnum].visibility != 0)
8585                 globvis2 = mulscale4(globvis2, (uint8_t)(sector[tspr->sectnum].visibility + 16));
8586             polymost_setVisibility(globvis2);
8587 
8588             if ((globalorientation & 64) != 0
8589              && (globalposz > tspriteGetZOfSlope(tspr, globalposx, globalposy)) == (!(globalorientation & 8)))
8590                 goto _drawsprite_return;
8591             else
8592             {
8593                 int16_t const heinum = tspriteGetSlope(tspr);
8594                 float const fheinum = heinum * (1.f / 4096.f);
8595                 float ratio = polymost_invsqrt_approximation(fheinum * fheinum + 1.f);
8596 
8597                 if ((globalorientation & 4) > 0)
8598                     off.x = -off.x;
8599                 if ((globalorientation & 8) > 0)
8600                     off.y = -off.y;
8601 
8602                 vec2f_t const p0 = { (float)(((tsiz.x + 1) >> 1) - off.x) * tspr->xrepeat,
8603                                      (float)(((tsiz.y + 1) >> 1) - off.y) * tspr->yrepeat * ratio },
8604                               p1 = { (float)((tsiz.x >> 1) + off.x) * tspr->xrepeat,
8605                                      (float)((tsiz.y >> 1) + off.y) * tspr->yrepeat * ratio };
8606 
8607                 float const c = sintable[(tspr->ang + 512) & 2047] * (1.0f / 65536.f);
8608                 float const s = sintable[tspr->ang & 2047] * (1.0f / 65536.f);
8609 
8610                 vec3f_t pxy[6];
8611 
8612                 // Project 3D to 2D
8613                 for (bssize_t j = 0; j < 4; j++)
8614                 {
8615                     vec2f_t s0 = { (float)(tspr->x - globalposx), (float)(tspr->y - globalposy) };
8616 
8617                     if ((j + 0) & 2)
8618                     {
8619                         s0.y -= s * p0.y;
8620                         s0.x -= c * p0.y;
8621                     }
8622                     else
8623                     {
8624                         s0.y += s * p1.y;
8625                         s0.x += c * p1.y;
8626                     }
8627                     if ((j + 1) & 2)
8628                     {
8629                         s0.x -= s * p0.x;
8630                         s0.y += c * p0.x;
8631                     }
8632                     else
8633                     {
8634                         s0.x += s * p1.x;
8635                         s0.y -= c * p1.x;
8636                     }
8637 
8638                     pxy[j] = { s0.y * gcosang - s0.x * gsinang, s0.x * gcosang2 + s0.y * gsinang2, float(tspriteGetZOfSlopeFloat(tspr, s0.x+globalposx, s0.y+globalposy))};
8639                 }
8640 
8641                 if (tspriteGetZOfSlope(tspr, globalposx, globalposy) < globalposz)  // if floor sprite is above you, reverse order of points
8642                 {
8643                     vec3f_t t = pxy[0];
8644                     pxy[0] = pxy[1];
8645                     pxy[1] = t;
8646 
8647                     t = pxy[2];
8648                     pxy[2] = pxy[3];
8649                     pxy[3] = t;
8650                 }
8651 
8652                 // Clip to SCISDIST plane
8653                 int32_t npoints = 0;
8654                 vec3f_t p2[6];
8655 
8656                 for (bssize_t i = 0, j = 1; i < 4; j = ((++i + 1) & 3))
8657                 {
8658                     if (pxy[i].y >= SCISDIST)
8659                         p2[npoints++] = pxy[i];
8660 
8661                     if ((pxy[i].y >= SCISDIST) != (pxy[j].y >= SCISDIST))
8662                     {
8663                         float const f = (SCISDIST - pxy[i].y) / (pxy[j].y - pxy[i].y);
8664                         vec3f_t const t = { (pxy[j].x - pxy[i].x) * f + pxy[i].x,
8665                                             (pxy[j].y - pxy[i].y) * f + pxy[i].y,
8666                                             (pxy[j].z - pxy[i].z) * f + pxy[i].z };
8667                         p2[npoints++] = t;
8668                     }
8669                 }
8670 
8671                 if (npoints < 3)
8672                     goto _drawsprite_return;
8673 
8674                 // Project rotated 3D points to screen
8675 
8676                 int fadjust = 0;
8677 
8678                 if (heinum == 0)
8679                 {
8680                     // unfortunately, offsetting by only 1 isn't enough on most Android devices
8681                     if (pos.z == sec->ceilingz || pos.z == sec->ceilingz + 1)
8682                         pos.z = sec->ceilingz + 2, fadjust = (tspr->owner & 31);
8683 
8684                     if (pos.z == sec->floorz || pos.z == sec->floorz - 1)
8685                         pos.z = sec->floorz - 2, fadjust = -((tspr->owner & 31));
8686                 }
8687 
8688                 vec2f_t pxy2[6];
8689                 double pfy[6];
8690 
8691                 for (bssize_t j = 0; j < npoints; j++)
8692                 {
8693                     float const ryp0 = 1.f / p2[j].y;
8694                     float const fs = (float)(p2[j].z - globalposz + fadjust) * gyxscale;
8695                     pxy2[j] = { ghalfx * p2[j].x * ryp0 + ghalfx, fs * ryp0 + ghoriz };
8696                     pfy[j] = double(gyxscale * ryp0) + ghoriz;
8697                 }
8698 
8699                 // gd? Copied from floor rendering code
8700 
8701                 xtex.d = 0;
8702                 ytex.d = gxyaspect;
8703                 if (heinum == 0)
8704                     ytex.d /= (double)(pos.z - globalposz + fadjust);
8705                 otex.d = -ghoriz * ytex.d;
8706 
8707                 // copied&modified from relative alignment
8708                 vec2f_t const vv = { (float)tspr->x + s * p1.x + c * p1.y, (float)tspr->y + s * p1.y - c * p1.x };
8709                 vec2f_t ff = { -(p0.x + p1.x) * s, (p0.x + p1.x) * c };
8710 
8711                 float f = polymost_invsqrt_approximation(ff.x * ff.x + ff.y * ff.y);
8712 
8713                 ff.x *= f;
8714                 ff.y *= f;
8715 
8716                 float const ft[4] = { ((float)(globalposy - vv.y)) * ff.y + ((float)(globalposx - vv.x)) * ff.x,
8717                                       ((float)(globalposx - vv.x)) * ff.y - ((float)(globalposy - vv.y)) * ff.x,
8718                                       fsinglobalang * ff.y + fcosglobalang * ff.x,
8719                                       fsinglobalang * ff.x - fcosglobalang * ff.y };
8720 
8721                 f = fviewingrange * -(1.f / (65536.f * 262144.f));
8722                 xtex.u = (float)ft[3] * f;
8723                 xtex.v = (float)ft[2] * f;
8724                 ytex.u = ft[0] * ytex.d;
8725                 ytex.v = ft[1] * ytex.d;
8726                 otex.u = ft[0] * otex.d;
8727                 otex.v = ft[1] * otex.d;
8728                 otex.u += (ft[2] * (1.0f / 262144.f) - xtex.u) * ghalfx;
8729                 otex.v -= (ft[3] * (1.0f / 262144.f) + xtex.v) * ghalfx;
8730 
8731                 f = 4.f / (float)tspr->xrepeat;
8732                 xtex.u *= f;
8733                 ytex.u *= f;
8734                 otex.u *= f;
8735 
8736                 f = -4.f / (float)tspr->yrepeat;
8737                 xtex.v *= f;
8738                 ytex.v *= f;
8739                 otex.v *= f;
8740 
8741                 if (globalorientation & 4)
8742                 {
8743                     xtex.u = ftsiz.x * xtex.d - xtex.u;
8744                     ytex.u = ftsiz.x * ytex.d - ytex.u;
8745                     otex.u = ftsiz.x * otex.d - otex.u;
8746                 }
8747 
8748                 // sprite panning
8749                 if (spriteext[spritenum].xpanning)
8750                 {
8751                     float const f = ((float)(spriteext[spritenum].xpanning) * (1.0f / 255.f)) * ftsiz.x;
8752                     ytex.u -= ytex.d * f;
8753                     otex.u -= otex.d * f;
8754                     drawpoly_srepeat = 1;
8755                 }
8756 
8757                 if (spriteext[spritenum].ypanning)
8758                 {
8759                     float const f = ((float)(spriteext[spritenum].ypanning) * (1.0f / 255.f)) * ftsiz.y * ratio;
8760                     ytex.v -= ytex.d * f;
8761                     otex.v -= otex.d * f;
8762                     drawpoly_trepeat = 1;
8763                 }
8764 
8765                 if (heinum != 0)
8766                 {
8767                     vec3d_t const duv[3] = {
8768                         { (pxy2[0].x * xtex.d + pfy[0] * ytex.d + otex.d),
8769                           (pxy2[0].x * xtex.u + pfy[0] * ytex.u + otex.u),
8770                           (pxy2[0].x * xtex.v + pfy[0] * ytex.v + otex.v)
8771                         },
8772                         { (pxy2[1].x * xtex.d + pfy[1] * ytex.d + otex.d),
8773                           (pxy2[1].x * xtex.u + pfy[1] * ytex.u + otex.u),
8774                           (pxy2[1].x * xtex.v + pfy[1] * ytex.v + otex.v)
8775                         },
8776                         { (pxy2[2].x * xtex.d + pfy[2] * ytex.d + otex.d),
8777                           (pxy2[2].x * xtex.u + pfy[2] * ytex.u + otex.u),
8778                           (pxy2[2].x * xtex.v + pfy[2] * ytex.v + otex.v)
8779                         }
8780                     };
8781 
8782                     vec3f_t oxyz[2] = { { (float)(pxy2[1].y - pxy2[2].y), (float)(pxy2[2].y - pxy2[0].y), (float)(pxy2[0].y - pxy2[1].y) },
8783                                         { (float)(pxy2[2].x - pxy2[1].x), (float)(pxy2[0].x - pxy2[2].x), (float)(pxy2[1].x - pxy2[0].x) } };
8784 
8785                     float const r = 1.f / (oxyz[0].x * pxy2[0].x + oxyz[0].y * pxy2[1].x + oxyz[0].z * pxy2[2].x);
8786 
8787                     xtex.d = (oxyz[0].x * duv[0].d + oxyz[0].y * duv[1].d + oxyz[0].z * duv[2].d) * r;
8788                     xtex.u = (oxyz[0].x * duv[0].u + oxyz[0].y * duv[1].u + oxyz[0].z * duv[2].u) * r;
8789                     xtex.v = (oxyz[0].x * duv[0].v + oxyz[0].y * duv[1].v + oxyz[0].z * duv[2].v) * r;
8790 
8791                     ytex.d = (oxyz[1].x * duv[0].d + oxyz[1].y * duv[1].d + oxyz[1].z * duv[2].d) * r;
8792                     ytex.u = (oxyz[1].x * duv[0].u + oxyz[1].y * duv[1].u + oxyz[1].z * duv[2].u) * r;
8793                     ytex.v = (oxyz[1].x * duv[0].v + oxyz[1].y * duv[1].v + oxyz[1].z * duv[2].v) * r;
8794 
8795                     otex.d = duv[0].d - pxy2[0].x * xtex.d - pxy2[0].y * ytex.d;
8796                     otex.u = duv[0].u - pxy2[0].x * xtex.u - pxy2[0].y * ytex.u;
8797                     otex.v = duv[0].v - pxy2[0].x * xtex.v - pxy2[0].y * ytex.v;
8798 
8799                     float const rr = Bsqrtf(fheinum * fheinum + 1.f);
8800                     xtex.v *= rr; ytex.v *= rr; otex.v *= rr;
8801                 }
8802 
8803                 tilesiz[globalpicnum] = { (int16_t)tsiz.x, (int16_t)tsiz.y };
8804                 pow2xsplit = 0;
8805 
8806 #ifdef YAX_ENABLE
8807                 if (yax_globallev == YAX_MAXDRAWS || searchit == 2)
8808 #endif
8809                 if (searchit >= 1)
8810                 {
8811                     psectnum = tspr->sectnum;
8812                     pwallnum = spritenum;
8813                     psearchstat = 3;
8814                     doeditorcheck = 1;
8815                 }
8816                 polymost_drawpoly(pxy2, npoints, method);
8817                 doeditorcheck = 0;
8818 
8819                 drawpoly_srepeat = 0;
8820                 drawpoly_trepeat = 0;
8821             }
8822 
8823             break;
8824 
8825         case 3:  // Voxel sprite
8826             break;
8827     }
8828 
8829     if (automapping == 1 && (unsigned)spritenum < MAXSPRITES)
8830         show2dsprite[spritenum>>3] |= pow2char[spritenum&7];
8831 
8832 _drawsprite_return:
8833     polymost_identityrotmat();
8834     tilesiz[globalpicnum] = oldsiz;
8835 }
8836 
8837 EDUKE32_STATIC_ASSERT((int)RS_YFLIP == (int)HUDFLAG_FLIPPED);
8838 
8839 //sx,sy       center of sprite; screen coords*65536
8840 //z           zoom*65536. > is zoomed in
8841 //a           angle (0 is default)
8842 //dastat&1    1:translucence
8843 //dastat&2    1:auto-scale mode (use 320*200 coordinates)
8844 //dastat&4    1:y-flip
8845 //dastat&8    1:don't clip to startumost/startdmost
8846 //dastat&16   1:force point passed to be top-left corner, 0:Editart center
8847 //dastat&32   1:reverse translucence
8848 //dastat&64   1:non-masked, 0:masked
8849 //dastat&128  1:draw all pages (permanent)
8850 //cx1,...     clip window (actual screen coords)
8851 
polymost_dorotatespritemodel(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 uniqid)8852 void polymost_dorotatespritemodel(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum,
8853     int8_t dashade, char dapalnum, int32_t dastat, uint8_t daalpha, uint8_t dablend, int32_t uniqid)
8854 {
8855     float d, cosang, sinang, cosang2, sinang2;
8856     float m[4][4];
8857 
8858     const int32_t tilenum = Ptile2tile(picnum, dapalnum);
8859 
8860     if (tile2model[tilenum].modelid == -1 || tile2model[tilenum].framenum == -1)
8861         return;
8862 
8863     vec3f_t vec1;
8864 
8865     tspritetype tspr{};
8866 
8867     hudtyp const * const hud = tile2model[tilenum].hudmem[(dastat&4)>>2];
8868 
8869     if (!hud || hud->flags & HUDFLAG_HIDE)
8870         return;
8871 
8872     polymost_outputGLDebugMessage(3, "polymost_dorotatespritemodel(sx:%d, sy:%d, z:%d, a:%hd, picnum:%hd, dashade:%hhd, dapalnum:%hhu, dastat:%d, daalpha:%hhu, dablend:%hhu, uniqid:%d)",
8873                                   sx, sy, z, a, picnum, dashade, dapalnum, dastat, daalpha, dablend, uniqid);
8874 
8875     float const ogchang = gchang; gchang = 1.f;
8876     float const ogshang = gshang; gshang = 0.f; d = (float) z*(1.0f/(65536.f*16384.f));
8877     float const ogctang = gctang; gctang = (float) sintable[(a+512)&2047]*d;
8878     float const ogstang = gstang; gstang = (float) sintable[a&2047]*d;
8879     int const ogshade  = globalshade;  globalshade  = dashade;
8880     int const ogpal    = globalpal;    globalpal    = (int32_t) ((uint8_t) dapalnum);
8881     double const ogxyaspect = gxyaspect; gxyaspect = 1.f;
8882     int const oldviewingrange = viewingrange; viewingrange = 65536;
8883     float const oldfviewingrange = fviewingrange; fviewingrange = 65536.f;
8884     float const ogvrcorrection = gvrcorrection; gvrcorrection = 1.f;
8885 
8886     vec1 = hud->add;
8887 
8888 #ifdef POLYMER
8889     if (pr_overridehud) {
8890         vec1.x = pr_hudxadd;
8891         vec1.y = pr_hudyadd;
8892         vec1.z = pr_hudzadd;
8893     }
8894 #endif
8895     if (!(hud->flags & HUDFLAG_NOBOB))
8896     {
8897         vec2f_t f = { (float)sx * (1.f / 65536.f), (float)sy * (1.f / 65536.f) };
8898 
8899         if (dastat & RS_TOPLEFT)
8900         {
8901             vec2_16_t siz = tilesiz[picnum];
8902             vec2_16_t off = { (int16_t)((siz.x >> 1) + picanm[picnum].xofs), (int16_t)((siz.y >> 1) + picanm[picnum].yofs) };
8903 
8904             d = (float)z * (1.0f / (65536.f * 16384.f));
8905             cosang2 = cosang = (float)sintable[(a + 512) & 2047] * d;
8906             sinang2 = sinang = (float)sintable[a & 2047] * d;
8907 
8908             if ((dastat & RS_AUTO) || (!(dastat & RS_NOCLIP)))  // Don't aspect unscaled perms
8909             {
8910                 d = (float)xyaspect * (1.0f / 65536.f);
8911                 cosang2 *= d;
8912                 sinang2 *= d;
8913             }
8914 
8915             vec2f_t const foff = { (float)off.x, (float)off.y };
8916             f.x += -foff.x * cosang2 + foff.y * sinang2;
8917             f.y += -foff.x * sinang  - foff.y * cosang;
8918         }
8919 
8920         if (!(dastat & RS_AUTO))
8921         {
8922             vec1.x += f.x / ((float)(xdim << 15)) - 1.f;  //-1: left of screen, +1: right of screen
8923             vec1.y += f.y / ((float)(ydim << 15)) - 1.f;  //-1: top of screen, +1: bottom of screen
8924         }
8925         else
8926         {
8927             vec1.x += f.x * (1.0f / 160.f) - 1.f;  //-1: left of screen, +1: right of screen
8928             vec1.y += f.y * (1.0f / 100.f) - 1.f;  //-1: top of screen, +1: bottom of screen
8929         }
8930     }
8931     tspr.ang = hud->angadd+globalang;
8932 
8933 #ifdef POLYMER
8934     if (pr_overridehud) {
8935         tspr.ang = pr_hudangadd + globalang;
8936     }
8937 #endif
8938 
8939     if (dastat & RS_YFLIP) { vec1.x = -vec1.x; vec1.y = -vec1.y; }
8940 
8941     // In Polymost, we don't care if the model is very big
8942 #ifdef POLYMER
8943     if (videoGetRenderMode() == REND_POLYMER)
8944     {
8945         vec3f_t const vec2 = { fglobalposx + (gcosang * vec1.z - gsinang * vec1.x) * 2560.f,
8946                                fglobalposy + (gsinang * vec1.z + gcosang * vec1.x) * 2560.f,
8947                                fglobalposz + (vec1.y * (2560.f * 0.8f)) };
8948         *(vec3f_t *)&tspr = vec2;
8949         tspr.xrepeat = tspr.yrepeat = 5;
8950     }
8951     else
8952 #endif
8953     {
8954         tspr.xrepeat = tspr.yrepeat = 32;
8955 
8956         tspr.x = globalposx + Blrintf((gcosang*vec1.z - gsinang*vec1.x)*16384.f);
8957         tspr.y = globalposy + Blrintf((gsinang*vec1.z + gcosang*vec1.x)*16384.f);
8958         tspr.z = globalposz + Blrintf(vec1.y * (16384.f * 0.8f));
8959     }
8960 
8961     tspr.picnum = picnum;
8962     tspr.shade = dashade;
8963     tspr.pal = dapalnum;
8964     tspr.owner = uniqid+MAXSPRITES;
8965     // 1 -> 1
8966     // 32 -> 32*16 = 512
8967     // 4 -> 8
8968     tspr.cstat = globalorientation = (dastat&RS_TRANS1) | ((dastat&RS_TRANS2)<<4) | ((dastat&RS_YFLIP)<<1);
8969 
8970     if ((dastat&(RS_AUTO|RS_NOCLIP)) == RS_AUTO)
8971         glViewport(windowxy1.x, ydim-(windowxy2.y+1), windowxy2.x-windowxy1.x+1, windowxy2.y-windowxy1.y+1);
8972     else
8973         glViewport(0, 0, xdim, ydim);
8974 
8975     if (videoGetRenderMode() < REND_POLYMER)
8976     {
8977         glMatrixMode(GL_PROJECTION);
8978         Bmemset(m, 0, sizeof(m));
8979 
8980         if ((dastat&(RS_AUTO|RS_NOCLIP)) == RS_AUTO)
8981         {
8982             float f = 1.f;
8983             int32_t fov = hud->fov;
8984 #ifdef POLYMER
8985             if (pr_overridehud)
8986                 fov = pr_hudfov;
8987 #endif
8988             if (fov != -1)
8989                 f = 1.f/tanf(((float)fov * 2.56f) * ((.5f * fPI) * (1.0f/2048.f)));
8990 
8991             m[0][0] = f*fydimen; m[0][2] = 1.f;
8992             m[1][1] = f*fxdimen; m[1][2] = 1.f;
8993             m[2][2] = 1.f; m[2][3] = fydimen;
8994             m[3][2] =-1.f;
8995         }
8996         else
8997         {
8998             m[0][0] = m[2][3] = 1.f;
8999             m[1][1] = fxdim/fydim;
9000             m[2][2] = 1.0001f;
9001             m[3][2] = 1-m[2][2];
9002         }
9003 
9004         glLoadMatrixf(&m[0][0]);
9005 
9006         glMatrixMode(GL_MODELVIEW);
9007         glLoadIdentity();
9008     }
9009 
9010     if (hud->flags & HUDFLAG_NODEPTH)
9011         glDisable(GL_DEPTH_TEST);
9012     else
9013     {
9014         static int32_t onumframes = 0;
9015 
9016         glEnable(GL_DEPTH_TEST);
9017 
9018         if (onumframes != numframes)
9019         {
9020             onumframes = numframes;
9021             glClear(GL_DEPTH_BUFFER_BIT);
9022         }
9023     }
9024 
9025     spriteext[tspr.owner].alpha = daalpha * (1.0f / 255.0f);
9026     tspr.blend = dablend;
9027 
9028     polymost_setFogEnabled(false);
9029 
9030     if (videoGetRenderMode() == REND_POLYMOST)
9031         polymost_mddraw(&tspr);
9032 # ifdef POLYMER
9033     else
9034     {
9035         int32_t fov;
9036 
9037         tspriteptr[maxspritesonscreen] = &tspr;
9038 
9039         glEnable(GL_ALPHA_TEST);
9040         glEnable(GL_BLEND);
9041 
9042         spriteext[tspr.owner].mdroll = a;
9043         spriteext[tspr.owner].mdpivot_offset.z = z;
9044 
9045         fov = hud->fov;
9046 
9047         if (fov == -1)
9048             fov = pr_fov;
9049 
9050         if (pr_overridehud)
9051             fov = pr_hudfov;
9052 
9053         polymer_setaspect(fov);
9054 
9055         polymer_drawsprite(maxspritesonscreen);
9056 
9057         polymer_setaspect(pr_fov);
9058 
9059         spriteext[tspr.owner].mdpivot_offset.z = 0;
9060         spriteext[tspr.owner].mdroll = 0;
9061 
9062         glDisable(GL_BLEND);
9063         glDisable(GL_ALPHA_TEST);
9064     }
9065 # endif
9066     if (!nofog) polymost_setFogEnabled(true);
9067 
9068     gvrcorrection = ogvrcorrection;
9069     viewingrange = oldviewingrange;
9070     fviewingrange = oldfviewingrange;
9071     gxyaspect = ogxyaspect;
9072     globalshade  = ogshade;
9073     globalpal    = ogpal;
9074     gchang = ogchang;
9075     gshang = ogshang;
9076     gctang = ogctang;
9077     gstang = ogstang;
9078 }
9079 
polymost_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)9080 void polymost_dorotatesprite(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum,
9081                              int8_t dashade, char dapalnum, int32_t dastat, uint8_t daalpha, uint8_t dablend,
9082                              int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2, int32_t uniqid)
9083 {
9084     if (usemodels && tile2model[picnum].hudmem[(dastat&4)>>2])
9085     {
9086         polymost_dorotatespritemodel(sx, sy, z, a, picnum, dashade, dapalnum, dastat, daalpha, dablend, uniqid);
9087         return;
9088     }
9089 
9090     polymost_outputGLDebugMessage(3, "polymost_dorotatesprite(sx:%d, sy:%d, z:%d, a:%hd, picnum:%hd, dashade:%hhd, dapalnum:%hhu, dastat:%d, daalpha:%hhu, dablend:%hhu, cx1:%d, cy1:%d, cx2:%d, cy2:%d, uniqid:%d)",
9091                                   sx, sy, z, a, picnum, dashade, dapalnum, dastat, daalpha, dablend, cx1, cy1, cx2, cy2, uniqid);
9092 
9093     glViewport(0,0,xdim,ydim);
9094     glMatrixMode(GL_PROJECTION);
9095     glPushMatrix();
9096 
9097     globvis = 0;
9098     globvis2 = 0;
9099     polymost_setClamp(1+2);
9100     polymost_setVisibility(globvis2);
9101 
9102     int32_t const ogpicnum = globalpicnum;
9103     globalpicnum = picnum;
9104     int32_t const  ogshade = globalshade;
9105     globalshade = dashade;
9106     int32_t const  ogpal = globalpal;
9107     globalpal = (int32_t)((uint8_t)dapalnum);
9108     float const  oghalfx = ghalfx;
9109     ghalfx = fxdim * .5f;
9110     float const  oghalfy = ghalfy;
9111     ghalfy = fydim * .5f;
9112     float const  ogrhalfxdown10 = grhalfxdown10;
9113     grhalfxdown10 = 1.f / (ghalfx * 1024.f);
9114     float const  ogrhalfxdown10x = grhalfxdown10x;
9115     grhalfxdown10x = grhalfxdown10;
9116     float const  oghoriz = ghoriz;
9117     ghoriz = fydim * .5f;
9118     int32_t const  ofoffset = frameoffset;
9119     frameoffset = frameplace;
9120     float const  ogchang = gchang;
9121     gchang = 1.f;
9122     float const  ogshang = gshang;
9123     gshang = 0.f;
9124     float const  ogctang = gctang;
9125     gctang = 1.f;
9126     float const  ogstang = gstang;
9127     gstang = 0.f;
9128     float const  ogvrcorrection = gvrcorrection;
9129     gvrcorrection = 1.f;
9130 
9131     polymost_updaterotmat();
9132 
9133     float m[4][4];
9134 
9135     Bmemset(m,0,sizeof(m));
9136     m[0][0] = m[2][3] = 1.0f;
9137     m[1][1] = fxdim / fydim;
9138     m[2][2] = 1.0001f;
9139     m[3][2] = 1 - m[2][2];
9140 
9141     glLoadMatrixf(&m[0][0]);
9142     glMatrixMode(GL_MODELVIEW);
9143     glPushMatrix();
9144     glLoadIdentity();
9145 
9146     glDisable(GL_DEPTH_TEST);
9147 
9148 #if defined(POLYMER)
9149 # ifdef USE_GLEXT
9150     const int32_t olddetailmapping = r_detailmapping, oldglowmapping = r_glowmapping;
9151 # endif
9152     const int32_t oldnormalmapping = pr_normalmapping;
9153 #endif
9154 
9155     int32_t method = DAMETH_CLAMPED; //Use OpenGL clamping - dorotatesprite never repeats
9156 
9157     if (!(dastat & RS_NOMASK))
9158     {
9159         glEnable(GL_ALPHA_TEST);
9160         glEnable(GL_BLEND);
9161 
9162         if (dastat & RS_TRANS1)
9163             method |= (dastat & RS_TRANS2) ? DAMETH_TRANS2 : DAMETH_TRANS1;
9164         else
9165             method |= DAMETH_MASK;
9166     }
9167     else
9168     {
9169         glDisable(GL_ALPHA_TEST);
9170         glDisable(GL_BLEND);
9171     }
9172 
9173     handle_blend(!!(dastat & RS_TRANS1), dablend, !!(dastat & RS_TRANS2));
9174 
9175 #ifdef POLYMER
9176     if (videoGetRenderMode() == REND_POLYMER)
9177     {
9178         pr_normalmapping = 0;
9179         polymer_inb4rotatesprite(picnum, dapalnum, dashade, method);
9180         polymost_resetVertexPointers();
9181 # ifdef USE_GLEXT
9182         r_detailmapping = 0;
9183         r_glowmapping = 0;
9184 # endif
9185     }
9186 #endif
9187 
9188     drawpoly_alpha = daalpha * (1.0f / 255.0f);
9189     drawpoly_blend = dablend;
9190 
9191     vec2_16_t const siz = tilesiz[globalpicnum];
9192     vec2_16_t ofs = { 0, 0 };
9193 
9194     if (!(dastat & RS_TOPLEFT))
9195     {
9196         ofs = { int16_t(picanm[globalpicnum].xofs + (siz.x>>1)),
9197                 int16_t(picanm[globalpicnum].yofs + (siz.y>>1)) };
9198     }
9199 
9200     if (dastat & RS_YFLIP)
9201         ofs.y = siz.y - ofs.y;
9202 
9203     int32_t ourxyaspect, ouryxaspect;
9204     dorotspr_handle_bit2(&sx, &sy, &z, dastat, cx1 + cx2, cy1 + cy2, &ouryxaspect, &ourxyaspect);
9205 
9206     int32_t cosang = mulscale14(sintable[(a + 512) & 2047], z);
9207     int32_t cosang2 = cosang;
9208     int32_t sinang = mulscale14(sintable[a & 2047], z);
9209     int32_t sinang2 = sinang;
9210 
9211     if ((dastat & RS_AUTO) || (!(dastat & RS_NOCLIP)))  // Don't aspect unscaled perms
9212     {
9213         cosang2 = mulscale16(cosang2,ourxyaspect);
9214         sinang2 = mulscale16(sinang2,ourxyaspect);
9215     }
9216 
9217     int32_t const cx = sx - ofs.x * cosang2 + ofs.y * sinang2;
9218     int32_t const cy = sy - ofs.x * sinang  - ofs.y * cosang;
9219 
9220     vec2_t pxy[8] = { { cx, cy },
9221                       { cx + siz.x * cosang2, cy + siz.x * sinang },
9222                       { 0, 0 },
9223                       { cx - siz.y * sinang2, cy + siz.y * cosang } };
9224 
9225     pxy[2]= { pxy[1].x + pxy[3].x - pxy[0].x,
9226               pxy[1].y + pxy[3].y - pxy[0].y };
9227 
9228     vec2_t const gxy = pxy[0];
9229 
9230     //Clippoly4
9231 
9232     int32_t n = 4, nn = 0, nz = 0;
9233     int32_t px2[8], py2[8];
9234 
9235     cx2++;
9236     cy2++;
9237 
9238     cx1 <<= 16;
9239     cy1 <<= 16;
9240     cx2 <<= 16;
9241     cy2 <<= 16;
9242 
9243     do
9244     {
9245         int32_t zz = nz+1; if (zz == n) zz = 0;
9246         int32_t const x1 = pxy[nz].x, x2 = pxy[zz].x-x1;
9247         if ((cx1 <= x1) && (x1 <= cx2)) { px2[nn] = x1; py2[nn] = pxy[nz].y; nn++; }
9248         int32_t fx = (x2 <= 0 ? cx2 : cx1), t = fx-x1;
9249         if ((t < x2) != (t < 0)) { px2[nn] = fx; py2[nn] = scale(pxy[zz].y-pxy[nz].y,t,x2) + pxy[nz].y; nn++; }
9250         fx = (x2 <= 0 ? cx1 : cx2); t = fx-x1;
9251         if ((t < x2) != (t < 0)) { px2[nn] = fx; py2[nn] = scale(pxy[zz].y-pxy[nz].y,t,x2) + pxy[nz].y; nn++; }
9252         nz = zz;
9253     }
9254     while (nz);
9255 
9256     n = 0;
9257 
9258     if (nn >= 3)
9259     {
9260         nz = 0;
9261         do
9262         {
9263             int32_t zz = nz+1; if (zz == nn) zz = 0;
9264             int32_t const y1 = py2[nz], y2 = py2[zz]-y1;
9265             if ((cy1 <= y1) && (y1 <= cy2)) { pxy[n].y = y1; pxy[n].x = px2[nz]; n++; }
9266             int32_t fy = (y2 <= 0 ? cy2 : cy1), t = fy - y1;
9267             if ((t < y2) != (t < 0)) { pxy[n].y = fy; pxy[n].x = scale(px2[zz]-px2[nz],t,y2) + px2[nz]; n++; }
9268             fy = (y2 <= 0 ? cy1 : cy2); t = fy - y1;
9269             if ((t < y2) != (t < 0)) { pxy[n].y = fy; pxy[n].x = scale(px2[zz]-px2[nz],t,y2) + px2[nz]; n++; }
9270             nz = zz;
9271         }
9272         while (nz);
9273     }
9274 
9275     if (n >= 3)
9276     {
9277         int32_t i = divscale32(1,z);
9278         int32_t xv = mulscale14(sintable[a&2047],i);
9279         int32_t yv = mulscale14(sintable[(a+512)&2047],i);
9280         int32_t xv2, yv2;
9281         if ((dastat&RS_AUTO) || (dastat&RS_NOCLIP)==0) //Don't aspect unscaled perms
9282         {
9283             yv2 = mulscale16(-xv,ouryxaspect);
9284             xv2 = mulscale16(yv,ouryxaspect);
9285         }
9286         else
9287         {
9288             yv2 = -xv;
9289             xv2 = yv;
9290         }
9291 
9292         int32_t lx = pxy[0].x;
9293         for (int v=n-1; v>0; v--)
9294             if (pxy[v].x < lx) lx = pxy[v].x;
9295 
9296         vec2_t oxy = { (lx>>16), 0 };
9297         int32_t x = (oxy.x<<16)-1-gxy.x;
9298         int32_t y = (oxy.y<<16)+65535-gxy.y;
9299         int32_t bx = dmulscale16(x,xv2,y,xv);
9300         int32_t by = dmulscale16(x,yv2,y,yv);
9301 
9302         if (dastat & RS_YFLIP)
9303         {
9304             yv = -yv;
9305             yv2 = -yv2;
9306             by = (siz.y<<16)-1-by;
9307         }
9308 
9309         vec2f_t fpxy[8];
9310         for (int v=0; v<n; v++)
9311             fpxy[v] = { float((pxy[v].x+8192)>>16), float((pxy[v].y+8192)>>16) };
9312 
9313         xtex.d = 0; ytex.d = 0; otex.d = 1.0;
9314         otex.u = (bx-(oxy.x-1+0.7)*xv2-(oxy.y+0.7)*xv)*(1.0/65536.0);
9315         xtex.u = xv2*(1.0/65536.0);
9316         ytex.u = xv*(1.0/65536.0);
9317         otex.v = (by-(oxy.x-1+0.7)*yv2-(oxy.y+0.7)*yv)*(1.0/65536.0);
9318         xtex.v = yv2*(1.0/65536.0);
9319         ytex.v = yv*(1.0/65536.0);
9320 
9321         polymost_setFogEnabled(false);
9322         pow2xsplit = 0; polymost_drawpoly(fpxy,n,method);
9323         if (!nofog) polymost_setFogEnabled(true);
9324     }
9325 
9326     glDisable(GL_ALPHA_TEST);
9327     glDisable(GL_BLEND);
9328     polymost_setClamp(0);
9329 
9330 #ifdef POLYMER
9331     if (videoGetRenderMode() == REND_POLYMER)
9332     {
9333 # ifdef USE_GLEXT
9334         r_detailmapping = olddetailmapping;
9335         r_glowmapping = oldglowmapping;
9336 # endif
9337         polymer_postrotatesprite();
9338         pr_normalmapping = oldnormalmapping;
9339     }
9340 #endif
9341     glPopMatrix();
9342     glMatrixMode(GL_PROJECTION);
9343     glPopMatrix();
9344 
9345     globalpicnum = ogpicnum;
9346     globalshade  = ogshade;
9347     globalpal    = ogpal;
9348     ghalfx       = oghalfx;
9349     ghalfy       = oghalfy;
9350     grhalfxdown10 = ogrhalfxdown10;
9351     grhalfxdown10x = ogrhalfxdown10x;
9352     ghoriz       = oghoriz;
9353     frameoffset  = ofoffset;
9354     gchang = ogchang;
9355     gshang = ogshang;
9356     gctang = ogctang;
9357     gstang = ogstang;
9358     gvrcorrection = ogvrcorrection;
9359 
9360     polymost_identityrotmat();
9361 }
9362 
9363 static float trapextx[2];
drawtrap(float x0,float x1,float y0,float x2,float x3,float y1)9364 static void drawtrap(float x0, float x1, float y0, float x2, float x3, float y1)
9365 {
9366     if (y0 == y1) return;
9367 
9368     float px[4], py[4];
9369     int32_t n = 3;
9370 
9371     px[0] = x0; py[0] = y0;  py[2] = y1;
9372     if (x0 == x1) { px[1] = x3; py[1] = y1; px[2] = x2; }
9373     else if (x2 == x3) { px[1] = x1; py[1] = y0; px[2] = x3; }
9374     else               { px[1] = x1; py[1] = y0; px[2] = x3; px[3] = x2; py[3] = y1; n = 4; }
9375 
9376     glBegin(GL_TRIANGLE_FAN);
9377     for (bssize_t i=0; i<n; i++)
9378     {
9379         px[i] = min(max(px[i],trapextx[0]),trapextx[1]);
9380         glTexCoord2f(px[i]*xtex.u + py[i]*ytex.u + otex.u,
9381                       px[i]*xtex.v + py[i]*ytex.v + otex.v);
9382         glVertex2f(px[i],py[i]);
9383     }
9384     glEnd();
9385 }
9386 
tessectrap(const float * px,const float * py,const int32_t * point2,int32_t numpoints)9387 static void tessectrap(const float *px, const float *py, const int32_t *point2, int32_t numpoints)
9388 {
9389     float x0, x1, m0, m1;
9390     int32_t i, j, k, z, i0, i1, i2, i3, npoints, gap, numrst;
9391 
9392     static int32_t allocpoints = 0, *slist = 0, *npoint2 = 0;
9393     typedef struct { float x, y, xi; int32_t i; } raster;
9394     static raster *rst = 0;
9395     if (numpoints+16 > allocpoints) //16 for safety
9396     {
9397         allocpoints = numpoints+16;
9398         rst = (raster *)Xrealloc(rst,allocpoints*sizeof(raster));
9399         slist = (int32_t *)Xrealloc(slist,allocpoints*sizeof(int32_t));
9400         npoint2 = (int32_t *)Xrealloc(npoint2,allocpoints*sizeof(int32_t));
9401     }
9402 
9403     //Remove unnecessary collinear points:
9404     for (i=0; i<numpoints; i++) npoint2[i] = point2[i];
9405     npoints = numpoints; z = 0;
9406     for (i=0; i<numpoints; i++)
9407     {
9408         j = npoint2[i]; if ((i < numpoints-1) && (point2[i] < i)) z = 3;
9409         if (j < 0) continue;
9410         k = npoint2[j];
9411         m0 = (px[j]-px[i])*(py[k]-py[j]);
9412         m1 = (py[j]-py[i])*(px[k]-px[j]);
9413         if (m0 < m1) { z |= 1; continue; }
9414         if (m0 > m1) { z |= 2; continue; }
9415         npoint2[i] = k; npoint2[j] = -1; npoints--; i--; //collinear
9416     }
9417     if (!z) return;
9418     trapextx[0] = trapextx[1] = px[0];
9419     for (i=j=0; i<numpoints; i++)
9420     {
9421         if (npoint2[i] < 0) continue;
9422         if (px[i] < trapextx[0]) trapextx[0] = px[i];
9423         if (px[i] > trapextx[1]) trapextx[1] = px[i];
9424         slist[j++] = i;
9425     }
9426     if (z != 3) //Simple polygon... early out
9427     {
9428         glBegin(GL_TRIANGLE_FAN);
9429         for (i=0; i<npoints; i++)
9430         {
9431             j = slist[i];
9432             glTexCoord2f(px[j]*xtex.u + py[j]*ytex.u + otex.u,
9433                           px[j]*xtex.v + py[j]*ytex.v + otex.v);
9434             glVertex2f(px[j],py[j]);
9435         }
9436         glEnd();
9437         return;
9438     }
9439 
9440     //Sort points by y's
9441     for (gap=(npoints>>1); gap; gap>>=1)
9442         for (i=0; i<npoints-gap; i++)
9443             for (j=i; j>=0; j-=gap)
9444             {
9445                 if (py[npoint2[slist[j]]] <= py[npoint2[slist[j+gap]]]) break;
9446                 k = slist[j]; slist[j] = slist[j+gap]; slist[j+gap] = k;
9447             }
9448 
9449     numrst = 0;
9450     for (z=0; z<npoints; z++)
9451     {
9452         i0 = slist[z]; i1 = npoint2[i0]; if (py[i0] == py[i1]) continue;
9453         i2 = i1; i3 = npoint2[i1];
9454         if (py[i1] == py[i3]) { i2 = i3; i3 = npoint2[i3]; }
9455 
9456         //i0        i3
9457         //  \      /
9458         //   i1--i2
9459         //  /      \ ~
9460         //i0        i3
9461 
9462         if ((py[i1] < py[i0]) && (py[i2] < py[i3])) //Insert raster
9463         {
9464             for (i=numrst; i>0; i--)
9465             {
9466                 if (rst[i-1].xi*(py[i1]-rst[i-1].y) + rst[i-1].x < px[i1]) break;
9467                 rst[i+1] = rst[i-1];
9468             }
9469             numrst += 2;
9470 
9471             if (i&1) //split inside area
9472             {
9473                 j = i-1;
9474 
9475                 x0 = (py[i1] - rst[j  ].y)*rst[j  ].xi + rst[j  ].x;
9476                 x1 = (py[i1] - rst[j+1].y)*rst[j+1].xi + rst[j+1].x;
9477                 drawtrap(rst[j].x,rst[j+1].x,rst[j].y,x0,x1,py[i1]);
9478                 rst[j  ].x = x0; rst[j  ].y = py[i1];
9479                 rst[j+3].x = x1; rst[j+3].y = py[i1];
9480             }
9481 
9482             m0 = (px[i0]-px[i1]) / (py[i0]-py[i1]);
9483             m1 = (px[i3]-px[i2]) / (py[i3]-py[i2]);
9484             j = ((px[i1] > px[i2]) || ((i1 == i2) && (m0 >= m1))) + i;
9485             k = (i<<1)+1 - j;
9486             rst[j].i = i0; rst[j].xi = m0; rst[j].x = px[i1]; rst[j].y = py[i1];
9487             rst[k].i = i3; rst[k].xi = m1; rst[k].x = px[i2]; rst[k].y = py[i2];
9488         }
9489         else
9490         {
9491             //NOTE:don't count backwards!
9492             if (i1 == i2) { for (i=0; i<numrst; i++) if (rst[i].i == i1) break; }
9493             else { for (i=0; i<numrst; i++) if ((rst[i].i == i1) || (rst[i].i == i2)) break; }
9494             j = i&~1;
9495 
9496             if ((py[i1] > py[i0]) && (py[i2] > py[i3])) //Delete raster
9497             {
9498                 for (; j<=i+1; j+=2)
9499                 {
9500                     x0 = (py[i1] - rst[j  ].y)*rst[j  ].xi + rst[j  ].x;
9501                     if ((i == j) && (i1 == i2)) x1 = x0; else x1 = (py[i1] - rst[j+1].y)*rst[j+1].xi + rst[j+1].x;
9502                     drawtrap(rst[j].x,rst[j+1].x,rst[j].y,x0,x1,py[i1]);
9503                     rst[j  ].x = x0; rst[j  ].y = py[i1];
9504                     rst[j+1].x = x1; rst[j+1].y = py[i1];
9505                 }
9506                 numrst -= 2; for (; i<numrst; i++) rst[i] = rst[i+2];
9507             }
9508             else
9509             {
9510                 x0 = (py[i1] - rst[j  ].y)*rst[j  ].xi + rst[j  ].x;
9511                 x1 = (py[i1] - rst[j+1].y)*rst[j+1].xi + rst[j+1].x;
9512                 drawtrap(rst[j].x,rst[j+1].x,rst[j].y,x0,x1,py[i1]);
9513                 rst[j  ].x = x0; rst[j  ].y = py[i1];
9514                 rst[j+1].x = x1; rst[j+1].y = py[i1];
9515 
9516                 if (py[i0] < py[i3]) { rst[i].x = px[i2]; rst[i].y = py[i2]; rst[i].i = i3; }
9517                 else { rst[i].x = px[i1]; rst[i].y = py[i1]; rst[i].i = i0; }
9518                 rst[i].xi = (px[rst[i].i] - rst[i].x) / (py[rst[i].i] - py[i1]);
9519             }
9520         }
9521     }
9522 }
9523 
polymost_fillpolygon(int32_t npoints)9524 void polymost_fillpolygon(int32_t npoints)
9525 {
9526     polymost_outputGLDebugMessage(3, "polymost_fillpolygon(npoints:%d)", npoints);
9527 
9528     globvis2 = 0;
9529     polymost_setVisibility(globvis2);
9530 
9531     globalx1 = mulscale16(globalx1,xyaspect);
9532     globaly2 = mulscale16(globaly2,xyaspect);
9533     xtex.u = ((float)asm1) * (1.f / 4294967296.f);
9534     xtex.v = ((float)asm2) * (1.f / 4294967296.f);
9535     ytex.u = ((float)globalx1) * (1.f / 4294967296.f);
9536     ytex.v = ((float)globaly2) * (-1.f / 4294967296.f);
9537     otex.u = (fxdim * xtex.u + fydim * ytex.u) * -0.5f + fglobalposx * (1.f / 4294967296.f);
9538     otex.v = (fxdim * xtex.v + fydim * ytex.v) * -0.5f - fglobalposy * (1.f / 4294967296.f);
9539 
9540     //Convert int32_t to float (in-place)
9541     for (bssize_t i=0; i<npoints; ++i)
9542     {
9543         ((float *)rx1)[i] = ((float)rx1[i])*(1.0f/4096.f);
9544         ((float *)ry1)[i] = ((float)ry1[i])*(1.0f/4096.f);
9545     }
9546 
9547     if (!polymost2d) polymostSet2dView(); //disables blending, texturing, and depth testing
9548     glEnable(GL_ALPHA_TEST);
9549     pthtyp const * const pth = our_texcache_fetch(DAMETH_NOMASK | (videoGetRenderMode() == REND_POLYMOST && r_useindexedcolortextures ? DAMETH_INDEXED : 0));
9550 
9551     if (pth)
9552     {
9553         polymost_bindPth(pth);
9554 
9555         if (!(pth->flags & PTH_INDEXED))
9556             polymost_usePaletteIndexing(false);
9557     }
9558 
9559     polymost_updatePalette();
9560 
9561     float const f = getshadefactor(globalshade, globalpal);
9562 
9563     uint8_t const maskprops = (globalorientation>>7)&DAMETH_MASKPROPS;
9564     handle_blend(maskprops > DAMETH_MASK, 0, maskprops == DAMETH_TRANS2);
9565     if (maskprops > DAMETH_MASK)
9566     {
9567         glEnable(GL_BLEND);
9568         glColor4f(f, f, f, float_trans(maskprops, 0));
9569     }
9570     else
9571     {
9572         glDisable(GL_BLEND);
9573         glColor3f(f, f, f);
9574     }
9575 
9576     tessectrap((float *)rx1,(float *)ry1,xb1,npoints);
9577 
9578     if (pth && !(pth->flags & PTH_INDEXED))
9579     {
9580         // restore palette usage if we were just rendering a non-indexed color texture
9581         polymost_usePaletteIndexing(true);
9582     }
9583 }
9584 
polymost_drawtilescreen(int32_t tilex,int32_t tiley,int32_t wallnum,int32_t dimen,int32_t tilezoom,int32_t usehitile,uint8_t * loadedhitile)9585 int32_t polymost_drawtilescreen(int32_t tilex, int32_t tiley, int32_t wallnum, int32_t dimen, int32_t tilezoom,
9586                                 int32_t usehitile, uint8_t *loadedhitile)
9587 {
9588     float xdime, ydime, xdimepad, ydimepad, scx, scy, ratio = 1.f;
9589     int32_t i;
9590     pthtyp *pth;
9591 
9592     if (videoGetRenderMode() < REND_POLYMOST || !in3dmode())
9593         return -1;
9594 
9595     polymost_outputGLDebugMessage(3, "polymost_drawtilescreen(tilex:%d, tiley:%d, wallnum:%d, dimen:%d, tilezoom:%d, usehitile:%d, loadedhitile:%p)",
9596                                   tilex, tiley, wallnum, dimen, tilezoom, usehitile, loadedhitile);
9597 
9598     if (!glinfo.texnpot)
9599     {
9600         i = (1<<(picsiz[wallnum]&15)); if (i < tilesiz[wallnum].x) i += i; xdimepad = (float)i;
9601         i = (1<<(picsiz[wallnum]>>4)); if (i < tilesiz[wallnum].y) i += i; ydimepad = (float)i;
9602     }
9603     else
9604     {
9605         xdimepad = (float)tilesiz[wallnum].x;
9606         ydimepad = (float)tilesiz[wallnum].y;
9607     }
9608     xdime = (float)tilesiz[wallnum].x; xdimepad = xdime/xdimepad;
9609     ydime = (float)tilesiz[wallnum].y; ydimepad = ydime/ydimepad;
9610 
9611     if ((xdime <= dimen) && (ydime <= dimen))
9612     {
9613         scx = xdime;
9614         scy = ydime;
9615     }
9616     else
9617     {
9618         scx = (float)dimen;
9619         scy = (float)dimen;
9620         if (xdime < ydime)
9621             scx *= xdime/ydime;
9622         else
9623             scy *= ydime/xdime;
9624     }
9625 
9626     int32_t const ousehightile = usehightile;
9627     usehightile = usehitile && usehightile;
9628     pth = texcache_fetch(wallnum, 0, 0, DAMETH_CLAMPED | (videoGetRenderMode() == REND_POLYMOST && r_useindexedcolortextures ? DAMETH_INDEXED : 0));
9629     if (usehightile)
9630         loadedhitile[wallnum>>3] |= pow2char[wallnum&7];
9631     usehightile = ousehightile;
9632 
9633     if (pth)
9634     {
9635         polymost_bindPth(pth);
9636 
9637         if (!(pth->flags & PTH_INDEXED))
9638             polymost_usePaletteIndexing(false);
9639     }
9640 
9641     globalshade = 0;
9642     polymost_updatePalette();
9643 
9644     glDisable(GL_ALPHA_TEST);
9645     glDisable(GL_BLEND);
9646 
9647     if (tilezoom)
9648     {
9649         if (scx > scy) ratio = dimen/scx;
9650         else ratio = dimen/scy;
9651     }
9652 
9653 #ifdef POLYMER
9654     if (videoGetRenderMode() == REND_POLYMER)
9655         glEnable(GL_TEXTURE_2D);
9656 #endif
9657 
9658     glColor3f(1,1,1);
9659     glBegin(GL_TRIANGLE_FAN);
9660     glTexCoord2f(0,              0); glVertex2f((float)tilex            ,(float)tiley);
9661     glTexCoord2f(xdimepad,       0); glVertex2f((float)tilex+(scx*ratio),(float)tiley);
9662     glTexCoord2f(xdimepad,ydimepad); glVertex2f((float)tilex+(scx*ratio),(float)tiley+(scy*ratio));
9663     glTexCoord2f(0,       ydimepad); glVertex2f((float)tilex            ,(float)tiley+(scy*ratio));
9664     glEnd();
9665 
9666 #ifdef POLYMER
9667     if (videoGetRenderMode() == REND_POLYMER)
9668         glDisable(GL_TEXTURE_2D);
9669 #endif
9670 
9671     if (pth && !(pth->flags & PTH_INDEXED))
9672     {
9673         // restore palette usage if we were just rendering a non-indexed color texture
9674         polymost_usePaletteIndexing(true);
9675     }
9676 
9677     return 0;
9678 }
9679 
gen_font_glyph_tex(void)9680 static int32_t gen_font_glyph_tex(void)
9681 {
9682     // construct a 256x128 texture for the font glyph matrix
9683 
9684     glGenTextures(1,&polymosttext);
9685 
9686     if (!polymosttext) return -1;
9687 
9688     char * const tbuf = (char *)Xmalloc(256*128*4);
9689 
9690     Bmemset(tbuf, 0, 256*128*4);
9691 
9692     char * cptr = (char *)textfont;
9693 
9694     for (bssize_t h=0; h<256; h++)
9695     {
9696         char *tptr = tbuf + (h%32)*8*4 + (h/32)*256*8*4;
9697         for (bssize_t i=0; i<8; i++)
9698         {
9699             for (bssize_t j=0; j<8; j++)
9700             {
9701                 if (cptr[h*8+i] & pow2char[7-j])
9702                 {
9703                     Bmemset(tptr+4*j, 255, 4);
9704                 }
9705             }
9706             tptr += 256*4;
9707         }
9708     }
9709 
9710     cptr = (char *)smalltextfont;
9711 
9712     for (bssize_t h=0; h<256; h++)
9713     {
9714         char *tptr = tbuf + 256*64*4 + (h%32)*8*4 + (h/32)*256*8*4;
9715         for (bssize_t i=1; i<7; i++)
9716         {
9717             for (bssize_t j=2; j<6; j++)
9718             {
9719                 if (cptr[h*8+i] & pow2char[7-j])
9720                 {
9721                     Bmemset(tptr+4*(j-2), 255, 4);
9722                 }
9723             }
9724             tptr += 256*4;
9725         }
9726     }
9727 
9728     glBindTexture(GL_TEXTURE_2D, polymosttext);
9729     glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,256,128,0,GL_RGBA,GL_UNSIGNED_BYTE,(GLvoid *)tbuf);
9730     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
9731     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
9732     Xfree(tbuf);
9733 
9734     return 0;
9735 }
9736 
polymost_printtext256(int32_t xpos,int32_t ypos,int16_t col,int16_t backcol,const char * name,char fontsize)9737 int32_t polymost_printtext256(int32_t xpos, int32_t ypos, int16_t col, int16_t backcol, const char *name, char fontsize)
9738 {
9739     int const arbackcol = (unsigned)backcol < 256 ? backcol : 0;
9740 
9741     polymost_outputGLDebugMessage(3, "polymost_printtext256(xpos:%d, ypos:%d, col:%hd, backcol:%hd, name:%p, fontsize:%hhu)",
9742                                       xpos, ypos, col, backcol, name, fontsize);
9743 
9744     // FIXME?
9745     if (col < 0)
9746         col = 0;
9747 
9748     palette_t p, b;
9749 
9750     bricolor(&p, col);
9751     bricolor(&b, arbackcol);
9752 
9753     if (videoGetRenderMode() < REND_POLYMOST || !in3dmode() || (!polymosttext && gen_font_glyph_tex() < 0))
9754         return -1;
9755 
9756     glBindTexture(GL_TEXTURE_2D, polymosttext);
9757     polymost_setTexturePosSize({0, 0, 1, 1});
9758 
9759     polymost_usePaletteIndexing(false);
9760 
9761     polymostSet2dView();	// disables blending, texturing, and depth testing
9762 
9763     glDisable(GL_ALPHA_TEST);
9764     glDepthMask(GL_FALSE);	// disable writing to the z-buffer
9765 
9766 //    glPushAttrib(GL_POLYGON_BIT|GL_ENABLE_BIT);
9767     // XXX: Don't fogify the OSD text in Mapster32 with r_usenewshading >= 2.
9768     polymost_setFogEnabled(false);
9769     // We want to have readable text in wireframe mode, too:
9770     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
9771     lastglpolygonmode = 0;
9772 
9773     if (backcol >= 0)
9774     {
9775         int const c = Bstrlen(name);
9776 
9777         glColor4ub(b.r,b.g,b.b,255);
9778 
9779         glBegin(GL_QUADS);
9780 
9781         glVertex2i(xpos,ypos);
9782         glVertex2i(xpos,ypos+(fontsize?6:8));
9783         glVertex2i(xpos+(c<<(3-fontsize)), ypos+(fontsize ? 6 : 8));
9784         glVertex2i(xpos+(c<<(3-fontsize)), ypos);
9785 
9786         glEnd();
9787     }
9788 
9789     glEnable(GL_BLEND);
9790     glColor4ub(p.r,p.g,p.b,255);
9791 
9792     vec2f_t const tc = { fontsize ? (4.f / 256.f) : (8.f / 256.f),
9793                          fontsize ? (6.f / 128.f) : (8.f / 128.f) };
9794 
9795     glBegin(GL_QUADS);
9796 
9797     for (bssize_t c=0; name[c]; ++c)
9798     {
9799         if (name[c] == '^' && isdigit(name[c+1]))
9800         {
9801             char smallbuf[8];
9802             int bi = 0;
9803 
9804             while (isdigit(name[c+1]) && bi<3)
9805             {
9806                 smallbuf[bi++]=name[c+1];
9807                 c++;
9808             }
9809 
9810             smallbuf[bi++] = 0;
9811 
9812             if (col)
9813             {
9814                 col = Batol(smallbuf);
9815 
9816                 if ((unsigned) col >= 256)
9817                     col = 0;
9818             }
9819 
9820             bricolor(&p, col);
9821 
9822             glColor4ub(p.r, p.g, p.b, 255);
9823 
9824             continue;
9825         }
9826 
9827         vec2f_t const t = { (float)(name[c] % 32) * (1.0f / 32.f),
9828                             (float)((name[c] / 32) + (fontsize * 8)) * (1.0f / 16.f) };
9829 
9830         glTexCoord2f(t.x, t.y);
9831         glVertex2i(xpos, ypos);
9832 
9833         glTexCoord2f(t.x + tc.x, t.y);
9834         glVertex2i(xpos + (8 >> fontsize), ypos);
9835 
9836         glTexCoord2f(t.x + tc.x, t.y + tc.y);
9837         glVertex2i(xpos + (8 >> fontsize), ypos + (fontsize ? 6 : 8));
9838 
9839         glTexCoord2f(t.x, t.y + tc.y);
9840         glVertex2i(xpos, ypos + (fontsize ? 6 : 8));
9841 
9842         xpos += (8>>fontsize);
9843     }
9844 
9845     glEnd();
9846 
9847     glDepthMask(GL_TRUE);	// re-enable writing to the z-buffer
9848 
9849 //    glPopAttrib();
9850 
9851     if (!nofog) polymost_setFogEnabled(true);
9852 
9853     polymost_usePaletteIndexing(true);
9854 
9855     return 0;
9856 }
9857 
9858 // Console commands by JBF
gltexturemode(osdcmdptr_t parm)9859 static int32_t gltexturemode(osdcmdptr_t parm)
9860 {
9861     int32_t m;
9862     char *p;
9863 
9864     if (parm->numparms != 1)
9865     {
9866         OSD_Printf("Current texturing mode is %s\n", glfiltermodes[gltexfiltermode].name);
9867         OSD_Printf("  Vaild modes are:\n");
9868         for (m = 0; m < NUMGLFILTERMODES; m++)
9869             OSD_Printf("     %d - %s\n", m, glfiltermodes[m].name);
9870 
9871         return OSDCMD_OK;
9872     }
9873 
9874     m = Bstrtoul(parm->parms[0], &p, 10);
9875     if (p == parm->parms[0])
9876     {
9877         // string
9878         for (m = 0; m < NUMGLFILTERMODES; m++)
9879         {
9880             if (!Bstrcasecmp(parm->parms[0], glfiltermodes[m].name))
9881                 break;
9882         }
9883 
9884         if (m == NUMGLFILTERMODES)
9885             m = gltexfiltermode;   // no change
9886     }
9887     else
9888     {
9889         m = clamp(m, 0, NUMGLFILTERMODES-1);
9890     }
9891 
9892     gltexfiltermode = m;
9893     gltexapplyprops();
9894 
9895     OSD_Printf("Texture filtering mode changed to %s\n", glfiltermodes[gltexfiltermode].name);
9896 
9897     return OSDCMD_OK;
9898 }
9899 
osdcmd_cvar_set_polymost(osdcmdptr_t parm)9900 static int osdcmd_cvar_set_polymost(osdcmdptr_t parm)
9901 {
9902     int32_t r = osdcmd_cvar_set(parm);
9903 
9904     if (xdim == 0 || ydim == 0 || bpp == 0) // video not set up yet
9905     {
9906         if (r == OSDCMD_OK)
9907         {
9908 #ifdef POLYMER
9909             if (!Bstrcasecmp(parm->name, "r_pr_maxlightpasses"))
9910                 pr_maxlightpasses = r_pr_maxlightpasses;
9911 #endif
9912         }
9913 
9914         return r;
9915     }
9916 
9917     if (r == OSDCMD_OK)
9918     {
9919         if (!Bstrcasecmp(parm->name, "r_vsync"))
9920             vsync = videoSetVsync(vsync);
9921         else if (!Bstrcasecmp(parm->name, "r_downsize"))
9922         {
9923             if (r_downsizevar == -1)
9924                 r_downsizevar = r_downsize;
9925 
9926             if (in3dmode() && r_downsize != r_downsizevar)
9927             {
9928                 texcache_invalidate();
9929                 videoResetMode();
9930                 if (videoSetGameMode(fullscreen,xres,yres,bpp,upscalefactor))
9931                     OSD_Printf("restartvid: Reset failed...\n");
9932             }
9933 
9934             r_downsizevar = r_downsize;
9935         }
9936         else if (!Bstrcasecmp(parm->name, "r_anisotropy"))
9937             gltexapplyprops();
9938         else if (!Bstrcasecmp(parm->name, "r_texfilter"))
9939             gltexturemode(parm);
9940         else if (!Bstrcasecmp(parm->name, "r_usenewshading"))
9941             glFogi(GL_FOG_MODE, (r_usenewshading < 2) ? GL_EXP2 : GL_LINEAR);
9942 #ifdef POLYMER
9943         else if (!Bstrcasecmp(parm->name, "r_pr_maxlightpasses"))
9944         {
9945             if (pr_maxlightpasses != r_pr_maxlightpasses)
9946             {
9947                 polymer_invalidatelights();
9948                 pr_maxlightpasses = r_pr_maxlightpasses;
9949             }
9950         }
9951 #endif
9952     }
9953 
9954     return r;
9955 }
9956 
polymost_initosdfuncs(void)9957 void polymost_initosdfuncs(void)
9958 {
9959     uint32_t i;
9960 
9961     static osdcvardata_t cvars_polymost[] =
9962     {
9963 #if 0
9964         { "r_enablepolymost2","enable/disable polymost2",(void *) &r_enablepolymost2, CVAR_BOOL, 0, 0 }, //POGO: temporarily disable this variable
9965         { "r_pogoDebug","",(void *) &r_pogoDebug, CVAR_BOOL | CVAR_NOSAVE, 0, 1 },
9966 #endif
9967         { "r_texfilter", "changes the texture filtering settings (requires r_useindexedcolortextures to be off, may require restartvid)", (void *) &gltexfiltermode, CVAR_INT|CVAR_FUNCPTR, 0, 5 },
9968         { "r_polymostDebug","Set the verbosity of Polymost GL debug messages",(void *) &r_polymostDebug, CVAR_INT, 0, 3 },
9969 #ifdef USE_GLEXT
9970         { "r_detailmapping","enable/disable detail mapping",(void *) &r_detailmapping, CVAR_BOOL, 0, 1 },
9971         { "r_glowmapping","enable/disable glow mapping",(void *) &r_glowmapping, CVAR_BOOL, 0, 1 },
9972 #endif
9973 #ifndef EDUKE32_GLES
9974         { "r_memcache","enable/disable texture cache memory cache",(void *) &glusememcache, CVAR_BOOL, 0, 1 },
9975         { "r_polygonmode","debugging feature",(void *) &r_polygonmode, CVAR_INT | CVAR_NOSAVE, 0, 3 },
9976         { "r_texcache","enable/disable OpenGL compressed texture cache",(void *) &glusetexcache, CVAR_INT, 0, 2 },
9977 #endif
9978         { "r_animsmoothing","enable/disable model animation smoothing",(void *) &r_animsmoothing, CVAR_BOOL, 0, 1 },
9979         { "r_anisotropy", "changes the OpenGL texture anisotropy setting (requires r_useindexedcolortextures to be off)", (void *) &glanisotropy, CVAR_INT|CVAR_FUNCPTR, 0, 16 },
9980         { "r_downsize","controls downsizing factor (quality) for hires textures",(void *) &r_downsize, CVAR_INT|CVAR_FUNCPTR, 0, 5 },
9981         { "r_finishbeforeswap", "run glFinish() before swapping when 'r_glfinish' is 1 and when not using KMT vsync", (void *) &r_finishbeforeswap, CVAR_BOOL, 0, 1 },
9982         { "r_fullbrights","enable/disable fullbright textures",(void *) &r_fullbrights, CVAR_BOOL, 0, 1 },
9983         { "r_glfinish", "run glFinish() after swapping buffers when not using KMT vsync", (void *) &r_glfinish, CVAR_BOOL, 0, 1 },
9984         { "r_hightile","enable/disable hightile texture rendering",(void *) &usehightile, CVAR_BOOL, 0, 1 },
9985         { "r_models", "enable/disable model rendering", (void *)&usemodels, CVAR_BOOL, 0, 1 },
9986         { "r_nofog", "enable/disable GL fog", (void *)&nofog, CVAR_BOOL, 0, 1},
9987         { "r_npotwallmode", "enable/disable emulation of walls with non-power-of-two height textures (Polymost, r_hightile 0)",
9988           (void *) &r_npotwallmode, CVAR_INT | CVAR_NOSAVE, 0, 2 },
9989         { "r_parallaxskyclamping","enable/disable parallaxed floor/ceiling sky texture clamping", (void *) &r_parallaxskyclamping, CVAR_BOOL, 0, 1 },
9990         { "r_parallaxskypanning","enable/disable parallaxed floor/ceiling panning when drawing a parallaxing sky", (void *) &r_parallaxskypanning, CVAR_BOOL, 0, 1 },
9991         { "r_projectionhack", "enable/disable projection hack", (void *) &glprojectionhacks, CVAR_INT, 0, 2 },
9992         { "r_shadeinterpolate", "enable/disable shade interpolation", (void *) &r_shadeinterpolate, CVAR_BOOL, 0, 1 },
9993         { "r_shadescale","multiplier for shading",(void *) &shadescale, CVAR_FLOAT, 0, 10 },
9994         { "r_shadescale_unbounded","enable/disable allowance of complete blackness",(void *) &shadescale_unbounded, CVAR_BOOL, 0, 1 },
9995         { "r_texcompr","enable/disable OpenGL texture compression: 0: off  1: hightile only  2: ART and hightile",(void *) &glusetexcompr, CVAR_INT, 0, 2 },
9996         { "r_texturemaxsize","changes the maximum OpenGL texture size limit",(void *) &gltexmaxsize, CVAR_INT | CVAR_NOSAVE, 0, 4096 },
9997         { "r_texturemiplevel","changes the highest OpenGL mipmap level used",(void *) &gltexmiplevel, CVAR_INT, 0, 6 },
9998         { "r_useindexedcolortextures", "enable/disable indexed color texture rendering (always disables r_texfilter and r_anisotropy)", (void *) &r_useindexedcolortextures, CVAR_BOOL, 0, 1 },
9999         { "r_usenewshading", "visibility/fog code: 0: orig. Polymost   1: 07/2011   2: linear 12/2012   3: no neg. start 03/2014   4: base constant on shade table 11/2017",
10000           (void *) &r_usenewshading, CVAR_INT|CVAR_FUNCPTR, 0, 4 },
10001         { "r_usetileshades", "enable/disable apply shade tables to art tiles", (void *) &r_usetileshades, CVAR_BOOL, 0, 1 },
10002 
10003         { "r_vsync",
10004           "VSync mode:\n"
10005           "  -1: adaptive (video driver)\n"
10006           "   0: disabled\n"
10007           "   1: enabled (video driver)\n"
10008 #if defined _WIN32 && defined RENDERTYPESDL
10009           "   2: KMT\n"
10010 #endif
10011           ,
10012           (void *)&vsync, CVAR_INT | CVAR_FUNCPTR, -1, 2 },
10013 
10014         { "r_vertexarrays","enable/disable using vertex arrays when drawing models",(void *) &r_vertexarrays, CVAR_BOOL, 0, 1 },
10015         { "r_yshearing", "enable/disable y-shearing", (void*) &r_yshearing, CVAR_BOOL, 0, 1 },
10016         { "r_flatsky", "enable/disable flat skies", (void*)& r_flatsky, CVAR_BOOL, 0, 1 },
10017 #ifdef USE_GLEXT
10018         { "r_vbocount","sets the number of Vertex Buffer Objects to use when drawing models",(void *) &r_vbocount, CVAR_INT, 1, 256 },
10019         { "r_persistentStreamBuffer","enable/disable persistent stream buffering (requires renderer restart)",(void *) &r_persistentStreamBuffer, CVAR_BOOL, 0, 1 },
10020         { "r_drawpolyVertsBufferLength","sets the size of the vertex buffer for polymost's streaming VBO rendering (requires renderer restart)",(void *) &r_drawpolyVertsBufferLength, CVAR_INT, MAX_DRAWPOLY_VERTS, 1000000 },
10021 #endif
10022 #ifdef POLYMER
10023         { "r_pr_artmapping", "enable/disable art mapping", (void *) &pr_artmapping, CVAR_BOOL | CVAR_INVALIDATEART, 0, 1 },
10024         { "r_pr_ati_fboworkaround", "enable this to workaround an ATI driver bug that causes sprite shadows to be square - you need to restart the renderer for it to take effect", (void *) &pr_ati_fboworkaround, CVAR_BOOL | CVAR_NOSAVE, 0, 1 },
10025         { "r_pr_ati_nodepthoffset", "enable this to workaround an ATI driver bug that causes sprite drawing to freeze the game on Radeon X1x00 hardware - you need to restart the renderer for it to take effect", (void *) &pr_ati_nodepthoffset, CVAR_BOOL | CVAR_NOSAVE, 0, 1 },
10026         { "r_pr_billboardingmode", "face sprite display method. 0: classic mode; 1: polymost mode", (void *) &pr_billboardingmode, CVAR_BOOL, 0, 1 },
10027         { "r_pr_buckets", "controls batching of primitives. 0: no batching. 1: buckets of materials.", (void *)&pr_buckets, CVAR_BOOL | CVAR_NOSAVE | CVAR_RESTARTVID, 0, 1 },
10028         { "r_pr_customaspect", "if non-zero, forces the 3D view aspect ratio", (void *) &pr_customaspect, CVAR_DOUBLE, 0, 3 },
10029         { "r_pr_gpusmoothing", "toggles model animation interpolation", (void *)&pr_gpusmoothing, CVAR_BOOL, 0, 1 },
10030         { "r_pr_highpalookups", "enable/disable highpalookups", (void *) &pr_highpalookups, CVAR_BOOL, 0, 1 },
10031         { "r_pr_hudangadd", "overriden HUD angadd; see r_pr_overridehud", (void *) &pr_hudangadd, CVAR_INT | CVAR_NOSAVE, -1024, 1024 },
10032         { "r_pr_hudfov", "overriden HUD fov; see r_pr_overridehud", (void *) &pr_hudfov, CVAR_INT | CVAR_NOSAVE, 0, 1023 },
10033         { "r_pr_hudxadd", "overriden HUD xadd; see r_pr_overridehud", (void *) &pr_hudxadd, CVAR_FLOAT | CVAR_NOSAVE, -100, 100 },
10034         { "r_pr_hudyadd", "overriden HUD yadd; see r_pr_overridehud", (void *) &pr_hudyadd, CVAR_FLOAT | CVAR_NOSAVE, -100, 100 },
10035         { "r_pr_hudzadd", "overriden HUD zadd; see r_pr_overridehud", (void *) &pr_hudzadd, CVAR_FLOAT | CVAR_NOSAVE, -100, 100 },
10036         { "r_pr_lighting", "enable/disable dynamic lights - restarts renderer", (void *) &pr_lighting, CVAR_INT | CVAR_RESTARTVID, 0, 2 },
10037         { "r_pr_maxlightpasses", "the maximal amount of lights a single object can by affected by", (void *) &r_pr_maxlightpasses, CVAR_INT|CVAR_FUNCPTR, 0, PR_MAXLIGHTS },
10038         { "r_pr_maxlightpriority", "lowering that value removes less meaningful lights from the scene", (void *) &pr_maxlightpriority, CVAR_INT, 0, PR_MAXLIGHTPRIORITY },
10039         { "r_pr_normalmapping", "enable/disable virtual displacement mapping", (void *) &pr_normalmapping, CVAR_BOOL, 0, 1 },
10040         { "r_pr_nullrender", "disable all draws when enabled, 2: disables updates too", (void *)&pr_nullrender, CVAR_INT | CVAR_NOSAVE, 0, 3 },
10041         { "r_pr_overridehud", "overrides hud model parameters with values from the pr_hud* cvars; use it to fine-tune DEF tokens", (void *) &pr_overridehud, CVAR_BOOL | CVAR_NOSAVE, 0, 1 },
10042         { "r_pr_overridemodelscale", "overrides model scale if non-zero; use it to fine-tune DEF tokens", (void *) &pr_overridemodelscale, CVAR_FLOAT | CVAR_NOSAVE, 0, 500 },
10043         { "r_pr_overrideparallax", "overrides parallax mapping scale and bias values with values from the pr_parallaxscale and pr_parallaxbias cvars; use it to fine-tune DEF tokens", (void *) &pr_overrideparallax, CVAR_BOOL | CVAR_NOSAVE, 0, 1 },
10044         { "r_pr_overridespecular", "overrides specular material power and factor values with values from the pr_specularpower and pr_specularfactor cvars; use it to fine-tune DEF tokens", (void *) &pr_overridespecular, CVAR_BOOL | CVAR_NOSAVE, 0, 1 },
10045         { "r_pr_parallaxbias", "overriden parallax mapping offset bias", (void *) &pr_parallaxbias, CVAR_FLOAT | CVAR_NOSAVE, -10, 10 },
10046         { "r_pr_parallaxscale", "overriden parallax mapping offset scale", (void *) &pr_parallaxscale, CVAR_FLOAT | CVAR_NOSAVE, -10, 10 },
10047         { "r_pr_shadowcount", "maximal amount of shadow emitting lights on screen - you need to restart the renderer for it to take effect", (void *) &pr_shadowcount, CVAR_INT, 0, 64 },
10048         { "r_pr_shadowdetail", "sets the shadow map resolution - you need to restart the renderer for it to take effect", (void *) &pr_shadowdetail, CVAR_INT, 0, 5 },
10049         { "r_pr_shadowfiltering", "enable/disable shadow edges filtering - you need to restart the renderer for it to take effect", (void *) &pr_shadowfiltering, CVAR_BOOL, 0, 1 },
10050         { "r_pr_shadows", "enable/disable dynamic shadows", (void *) &pr_shadows, CVAR_BOOL, 0, 1 },
10051         { "r_pr_specularfactor", "overriden specular material factor", (void *) &pr_specularfactor, CVAR_FLOAT | CVAR_NOSAVE, -10, 1000 },
10052         { "r_pr_specularmapping", "enable/disable specular mapping", (void *) &pr_specularmapping, CVAR_BOOL, 0, 1 },
10053         { "r_pr_specularpower", "overriden specular material power", (void *) &pr_specularpower, CVAR_FLOAT | CVAR_NOSAVE, -10, 1000 },
10054         { "r_pr_vbos", "contols Vertex Buffer Object usage. 0: no VBOs. 1: VBOs for map data. 2: VBOs for model data.", (void *) &pr_vbos, CVAR_INT | CVAR_RESTARTVID, 0, 2 },
10055         { "r_pr_verbosity", "verbosity level of the polymer renderer", (void *) &pr_verbosity, CVAR_INT, 0, 3 },
10056         { "r_pr_wireframe", "toggles wireframe mode", (void *) &pr_wireframe, CVAR_BOOL | CVAR_NOSAVE, 0, 1 },
10057 #endif
10058     };
10059 
10060     for (i=0; i<ARRAY_SIZE(cvars_polymost); i++)
10061         OSD_RegisterCvar(&cvars_polymost[i], (cvars_polymost[i].flags & CVAR_FUNCPTR) ? osdcmd_cvar_set_polymost : osdcmd_cvar_set);
10062 }
10063 
polymost_precache(int32_t dapicnum,int32_t dapalnum,int32_t datype)10064 void polymost_precache(int32_t dapicnum, int32_t dapalnum, int32_t datype)
10065 {
10066     // dapicnum and dapalnum are like you'd expect
10067     // datype is 0 for a wall/floor/ceiling and 1 for a sprite
10068     //    basically this just means walls are repeating
10069     //    while sprites are clamped
10070 
10071     if (videoGetRenderMode() < REND_POLYMOST) return;
10072     if ((dapalnum < (MAXPALOOKUPS - RESERVEDPALS)) && (palookup[dapalnum] == NULL)) return;//dapalnum = 0;
10073 
10074     //OSD_Printf("precached %d %d type %d\n", dapicnum, dapalnum, datype);
10075     hicprecaching = 1;
10076     texcache_fetch(dapicnum, dapalnum, 0, (datype & 1)*(DAMETH_CLAMPED|DAMETH_MASK));
10077     hicprecaching = 0;
10078 
10079     if (datype == 0 || !usemodels) return;
10080 
10081     int const mid = md_tilehasmodel(dapicnum, dapalnum);
10082 
10083     if (mid < 0 || models[mid]->mdnum < 2) return;
10084 
10085     int const surfaces = (models[mid]->mdnum == 3) ? ((md3model_t *)models[mid])->head.numsurfs : 0;
10086 
10087     for (int i = 0; i <= surfaces; i++)
10088         mdloadskin((md2model_t *)models[mid], 0, dapalnum, i);
10089 }
10090 
10091 #else /* if !defined USE_OPENGL */
10092 
10093 #include "compat.h"
10094 
polymost_drawtilescreen(int32_t tilex,int32_t tiley,int32_t wallnum,int32_t dimen,int32_t usehitile,uint8_t * loadedhitile)10095 int32_t polymost_drawtilescreen(int32_t tilex, int32_t tiley, int32_t wallnum, int32_t dimen,
10096                                 int32_t usehitile, uint8_t *loadedhitile)
10097 {
10098     UNREFERENCED_PARAMETER(tilex);
10099     UNREFERENCED_PARAMETER(tiley);
10100     UNREFERENCED_PARAMETER(wallnum);
10101     UNREFERENCED_PARAMETER(dimen);
10102     UNREFERENCED_PARAMETER(usehitile);
10103     UNREFERENCED_PARAMETER(loadedhitile);
10104     return -1;
10105 }
10106 
10107 #endif
10108 
10109 // vim:ts=4:sw=4:
10110