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)§or[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)§or[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)§or[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)§or[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)§or[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)§or[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)§or[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)§or[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§or[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§or[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)§or[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)§or[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 * )§or[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)§or[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)§or[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