engine: reject mbf21 and shit24 wads. there is no way to know if it is safe to ignore...
[k8vavoom.git] / source / drawer.cpp
blob18e6dd93f009bef2e7c2e0cc1991c854ae9a2c86
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 //**
27 //** Game completion, final screen animation.
28 //**
29 //**************************************************************************
30 #include "gamedefs.h"
31 #include "drawer.h"
32 #include "screen.h"
35 VCvarF gl_maxdist("gl_maxdist", "8192", "Max view distance (too big values will cause z-buffer issues).", CVAR_Archive|CVAR_NoShadow);
38 TArray<void (*) (int phase)> VDrawer::cbInitDeinit;
40 float VDrawer::LightFadeMult = 1.0f;
41 float VDrawer::mWindowAspect = 1.0f;
42 VMatrix4 VDrawer::LightViewMatrix[6];
45 static const TVec LightViewForward[6] = {
46 TVec(-1.0f, 0.0f, 0.0f), // positive x
47 TVec( 1.0f, 0.0f, 0.0f), // negative x
48 TVec( 0.0f, 1.0f, 0.0f), // positive y
49 TVec( 0.0f, -1.0f, 0.0f), // negative y
50 TVec( 0.0f, 0.0f, -1.0f), // positive z
51 TVec( 0.0f, 0.0f, 1.0f), // negative z
54 static const TVec LightViewUp[6] = {
55 TVec( 0.0f, -1.0f, 0.0f), // positive x
56 TVec( 0.0f, -1.0f, 0.0f), // negative x
57 TVec( 0.0f, 0.0f, -1.0f), // positive y
58 TVec( 0.0f, 0.0f, 1.0f), // negative y
59 TVec( 0.0f, -1.0f, 0.0f), // positive z
60 TVec( 0.0f, -1.0f, 0.0f), // negative z
64 //==========================================================================
66 // CalculateLightViewMatrix
68 //==========================================================================
69 static void CalculateLightViewMatrix (VMatrix4 &ModelMat, const unsigned int facenum) {
70 ModelMat.SetIdentity();
72 const TVec forward = LightViewForward[facenum];
73 const TVec left = LightViewUp[facenum].cross(forward).normalise();
74 const TVec up = forward.cross(left).normalise();
76 ModelMat.m[0][0] = left.x;
77 ModelMat.m[0][1] = left.y;
78 ModelMat.m[0][2] = left.z;
79 ModelMat.m[1][0] = up.x;
80 ModelMat.m[1][1] = up.y;
81 ModelMat.m[1][2] = up.z;
82 ModelMat.m[2][0] = forward.x;
83 ModelMat.m[2][1] = forward.y;
84 ModelMat.m[2][2] = forward.z;
87 //TVec VDrawer::GetLightViewForward (unsigned int facenum) noexcept { return TVec(LightViewMatrix[facenum].m[2][0], LightViewMatrix[facenum].m[2][1], LightViewMatrix[facenum].m[2][2]); }
88 TVec VDrawer::GetLightViewForward (unsigned int facenum) noexcept { return LightViewForward[facenum]; }
89 TVec VDrawer::GetLightViewUp (unsigned int facenum) noexcept { return TVec(LightViewMatrix[facenum].m[1][0], LightViewMatrix[facenum].m[1][1], LightViewMatrix[facenum].m[1][2]); }
90 TVec VDrawer::GetLightViewRight (unsigned int facenum) noexcept { return TVec(-LightViewMatrix[facenum].m[0][0], -LightViewMatrix[facenum].m[0][1], -LightViewMatrix[facenum].m[0][2]); }
93 //**************************************************************************
95 // VDrawer methods
97 //**************************************************************************
99 // ////////////////////////////////////////////////////////////////////////// //
100 // POV related
101 // this should be prolly moved to renderer, and recorded in render list
102 // if i'll do that, i will be able to render stacked sectors, and mirrors
103 // without portals.
105 // in render lists it is prolly enough to store current view transformation
106 // matrix, because surface visibility flags are already set by the queue manager.
108 // another thing to consider is queue manager limitation: one surface can be
109 // queued only once. this is not a hard limitation, though, as queue manager is
110 // using arrays to keep surface pointers, but it is handy for various render
111 // checks. we prolly need to increment queue frame counter when view changes.
113 // half-done
115 extern TVec vieworg;
116 extern TVec viewforward;
117 extern TVec viewright;
118 extern TVec viewup;
119 extern TAVec viewangles;
120 extern TFrustum viewfrustum;
122 extern bool MirrorFlip;
123 extern bool MirrorClip;
126 //==========================================================================
128 // VDrawer::VDrawer
130 //==========================================================================
131 VDrawer::VDrawer () noexcept
132 : mInitialized(false)
133 , isShittyGPU(false)
134 , shittyGPUCheckDone(false)
135 , useReverseZ(false)
136 , HaveDepthClamp(false)
137 , DepthZeroOne(false)
138 , canRenderShadowmaps(false)
139 , updateFrame(0)
140 , needCrosshair(false)
141 , prevCrosshairPic(-1)
142 , CurrentTime(0.0)
143 , RendLev(nullptr)
145 #ifdef CLIENT
146 ScrWdt = max2(1, ScreenWidth);
147 ScrHgt = max2(1, ScreenHeight);
148 #else
149 // it doesn't matter
150 ScrWdt = 320;
151 ScrHgt = 200;
152 #endif
153 LightFadeMult = 1.0f;
154 mWindowAspect = 1.0f;
156 vieworg = TVec(0.0f, 0.0f, 0.0f);
157 viewangles = TAVec(0.0f, 0.0f, 0.0f);
158 viewforward = TVec(0.0f, 0.0f, 0.0f);
159 viewright = TVec(0.0f, 0.0f, 0.0f);
160 viewup = TVec(0.0f, 0.0f, 0.0f);
161 MirrorFlip = MirrorClip = false;
163 for (unsigned int facenum = 0; facenum < 6; ++facenum) CalculateLightViewMatrix(LightViewMatrix[facenum], facenum);
167 //==========================================================================
169 // VDrawer::~VDrawer
171 //==========================================================================
172 VDrawer::~VDrawer () {
176 //==========================================================================
178 // VDrawer::RegisterICB
180 //==========================================================================
181 void VDrawer::RegisterICB (void (*cb) (int phase)) {
182 if (!cb) return;
183 for (int f = 0; f < cbInitDeinit.length(); ++f) {
184 if (cbInitDeinit[f] == cb) return;
186 cbInitDeinit.append(cb);
190 //==========================================================================
192 // VDrawer::callICB
194 //==========================================================================
195 void VDrawer::callICB (int phase) {
196 for (int f = 0; f < cbInitDeinit.length(); ++f) cbInitDeinit[f](phase);
200 //==========================================================================
202 // VDrawer::ResetTextureUpdateFrames
204 //==========================================================================
205 void VDrawer::ResetTextureUpdateFrames () noexcept {
206 for (int i = 0; i < GTextureManager.GetNumTextures(); ++i) {
207 VTexture *tex = GTextureManager.getIgnoreAnim(i);
208 if (tex) tex->lastUpdateFrame = 0;
210 for (int i = 0; i < GTextureManager.GetNumMapTextures(); ++i) {
211 VTexture *tex = GTextureManager.getMapTexIgnoreAnim(i);
212 if (tex) tex->lastUpdateFrame = 0;
217 //==========================================================================
219 // VDrawer::CalcRealHexHeight
221 //==========================================================================
222 float VDrawer::CalcRealHexHeight (float h) noexcept {
223 if (h < 1.0f) return 0.0f;
224 h = h/3.0f*2.0f;
225 const float dividery = 3.0f;
226 const float hdiv = h/dividery;
227 return h+hdiv;
231 //==========================================================================
233 // VDrawer::CalcHexVertices
235 //==========================================================================
236 void VDrawer::CalcHexVertices (float vx[6], float vy[6], float x0, float y0, float w, float h) noexcept {
237 h = h/3.0f*2.0f;
238 const float dividery = 3.0f;
239 const float hdiv = h/dividery;
240 const float wdiv = w*0.5f;
241 y0 += hdiv;
242 const float yr0 = y0;
243 const float yr1 = y0+h;
244 const float ytop = yr0-hdiv;
245 const float ybot = yr1+hdiv;
246 vx[0] = x0; vy[0] = yr0;
247 vx[1] = x0+wdiv; vy[1] = ytop;
248 vx[2] = x0+w; vy[2] = yr0;
249 vx[3] = x0+w; vy[3] = yr1;
250 vx[4] = x0+wdiv; vy[4] = ybot;
251 vx[5] = x0; vy[5] = yr1;
255 //==========================================================================
257 // VDrawer::IsPointInsideHex
259 //==========================================================================
260 bool VDrawer::IsPointInsideHex (float x, float y, float x0, float y0, float w, float h) noexcept {
261 if (w <= 0.0f || h <= 0.0f) return false;
262 float vx[6];
263 float vy[6];
264 CalcHexVertices(vx, vy, x0, y0, w, h);
265 return IsPointInside2DPoly(x, y, 6, vx, vy);
269 //==========================================================================
271 // VDrawer::IsPointInside2DPolyInternal
273 // polygon must be convex
275 //==========================================================================
276 bool VDrawer::IsPointInside2DPolyInternal (const float x, const float y, int vcount, const float vx[], size_t xstride, const float vy[], size_t ystride) noexcept {
277 if (vcount < 2) return false;
278 // this whole thing can be optimised by using differentials, but meh...
279 float lastx = vx[(vcount-1)*xstride], lasty = vy[(vcount-1)*ystride];
280 while (vcount--) {
281 // calculates the area of the parallelogram of the three points
282 // this is actually the same as the area of the triangle defined by the three points, multiplied by 2
283 const float bx = vx[0];
284 const float by = vy[0];
285 const float area = (lastx-x)*(by-y)-(lasty-y)*(bx-x);
286 if (area < 0) return false; // wrong side
287 lastx = bx;
288 lasty = by;
289 vx += xstride;
290 vy += ystride;
292 return true;
296 //==========================================================================
298 // VDrawer::IsPointInside2DPoly
300 // polygon must be convex
302 //==========================================================================
303 bool VDrawer::IsPointInside2DPoly (const float x, const float y, int vcount, const float vx[], const float vy[]) noexcept {
304 return IsPointInside2DPolyInternal(x, y, vcount, vx, 1, vy, 1);
308 //==========================================================================
310 // VDrawer::IsPointInside2DPoly
312 // polygon must be convex
313 // here `vxy` contains `x`,`y` pairs
315 //==========================================================================
316 bool VDrawer::IsPointInside2DPoly (const float x, const float y, int vcount, const float vxy[]) noexcept {
317 return IsPointInside2DPolyInternal(x, y, vcount, vxy+0, 2, vxy+1, 2);
322 //**************************************************************************
324 // calculate matrices
326 //**************************************************************************
328 //==========================================================================
330 // VDrawer::CalcProjectionMatrix
332 //==========================================================================
333 void VDrawer::CalcProjectionMatrix (VMatrix4 &ProjMat, /*VRenderLevelDrawer *rlev,*/ const refdef_t *rd, bool noFarNearPlanes) {
334 const float zNear = 1.0f;
335 if (!CanUseRevZ()) {
336 // normal
337 //glClearDepth(1.0f);
338 //glDepthFunc(GL_LEQUAL);
339 // const tanHalfFovy = tan(fovy*0.5f);
340 // rd->fovx = aspect*tanHalfFovy;
341 // rd->fovy = tanHalfFovy;
342 ProjMat.SetZero();
343 ProjMat[0][0] = 1.0f/rd->fovx;
344 ProjMat[1][1] = 1.0f/rd->fovy;
345 ProjMat[2][3] = -1.0f;
346 //ProjMat[3][3] = 0.0f;
347 if (/*!HaveDepthClamp && rlev && rlev->IsShadowVolumeRenderer() && !rlev->IsShadowMapRenderer()*/noFarNearPlanes) {
348 ProjMat[2][2] = -1.0f;
349 ProjMat[3][2] = -2.0f;
350 } else {
351 float maxdist = gl_maxdist.asFloat();
352 if (maxdist < 1.0f || !isFiniteF(maxdist)) maxdist = 32767.0f;
353 if (DepthZeroOne) {
354 ProjMat[2][2] = maxdist/(zNear-maxdist); // zFar/(zNear-zFar);
355 ProjMat[3][2] = -(maxdist*zNear)/(maxdist-zNear); // -(zFar*zNear)/(zFar-zNear);
356 } else {
357 ProjMat[2][2] = -(maxdist+zNear)/(maxdist-zNear); // -(zFar+zNear)/(zFar-zNear);
358 ProjMat[3][2] = -(2.0f*maxdist*zNear)/(maxdist-zNear); // -(2.0f*zFar*zNear)/(zFar-zNear);
361 } else {
362 // reversed
363 // see https://nlguillemot.wordpress.com/2016/12/07/reversed-z-in-opengl/
364 //FIXME: zNear should prolly be slightly less than `1.0f` here. dunno, i sucks at math.
365 //glClearDepth(0.0f);
366 //glDepthFunc(GL_GEQUAL);
367 ProjMat.SetZero();
368 ProjMat[0][0] = 1.0f/rd->fovx;
369 ProjMat[1][1] = 1.0f/rd->fovy;
370 ProjMat[2][3] = -1.0f;
371 ProjMat[3][2] = zNear;
373 //RestoreDepthFunc();
377 //==========================================================================
379 // VDrawer::CalcShadowMapProjectionMatrix
381 //==========================================================================
382 void VDrawer::CalcShadowMapProjectionMatrix (VMatrix4 &ProjMat, float Radius) {
383 const float fov = 90.0f;
384 const float fovx = tanf(DEG2RADF(fov)*0.5f);
385 //if (aspect <= 0.0f || !isFiniteF(aspect)) aspect = 1.0f;
386 const float fovy = fovx;//*aheight/awidth/aspect;
387 const float zNear = 1.0f;
388 ProjMat.SetZero();
389 ProjMat[0][0] = 1.0f/fovx;
390 ProjMat[1][1] = 1.0f/fovy;
391 ProjMat[2][3] = -1.0f;
392 //ProjMat[3][3] = 0.0f;
393 if (Radius < 1.0f || !isFiniteF(Radius)) Radius = 32767.0f;
394 if (/*DepthZeroOne*/false) {
395 ProjMat[2][2] = Radius/(zNear-Radius); // zFar/(zNear-zFar);
396 ProjMat[3][2] = -(Radius*zNear)/(Radius-zNear); // -(zFar*zNear)/(zFar-zNear);
397 } else {
398 ProjMat[2][2] = -(Radius+zNear)/(Radius-zNear); // -(zFar+zNear)/(zFar-zNear);
399 ProjMat[3][2] = -(2.0f*Radius*zNear)/(Radius-zNear); // -(2.0f*zFar*zNear)/(zFar-zNear);
404 //==========================================================================
406 // VDrawer::CalcModelMatrix
408 //==========================================================================
409 void VDrawer::CalcModelMatrix (VMatrix4 &ModelMat, const TVec &origin, const TAVec &angles, bool MirrorFlip) {
410 ModelMat.SetIdentity();
411 ModelMat *= VMatrix4::RotateX(-90.0f); //glRotatef(-90, 1, 0, 0);
412 ModelMat *= VMatrix4::RotateZ(90.0f); //glRotatef(90, 0, 0, 1);
413 if (MirrorFlip) ModelMat *= VMatrix4::Scale(TVec(1, -1, 1)); //glScalef(1, -1, 1);
414 ModelMat *= VMatrix4::RotateX(-angles.roll); //glRotatef(-viewangles.roll, 1, 0, 0);
415 ModelMat *= VMatrix4::RotateY(-angles.pitch); //glRotatef(-viewangles.pitch, 0, 1, 0);
416 ModelMat *= VMatrix4::RotateZ(-angles.yaw); //glRotatef(-viewangles.yaw, 0, 0, 1);
417 ModelMat *= VMatrix4::Translate(-origin); //glTranslatef(-vieworg.x, -vieworg.y, -vieworg.z);
421 //==========================================================================
423 // VDrawer::CalcSpotLightFaceView
425 // do not even ask me. if was found by brute force.
427 //==========================================================================
428 void VDrawer::CalcSpotLightFaceView (VMatrix4 &ModelMat, const TVec &origin, unsigned int facenum) {
429 if (facenum > 5) facenum = 0;
431 ModelMat.SetIdentity();
433 const TVec forward = LightViewForward[facenum].normalise();
434 const TVec left = LightViewUp[facenum].cross(forward).normalise();
435 const TVec up = forward.cross(left).normalise();
437 ModelMat.m[0][0] = left.x;
438 ModelMat.m[0][1] = left.y;
439 ModelMat.m[0][2] = left.z;
440 ModelMat.m[1][0] = up.x;
441 ModelMat.m[1][1] = up.y;
442 ModelMat.m[1][2] = up.z;
443 ModelMat.m[2][0] = forward.x;
444 ModelMat.m[2][1] = forward.y;
445 ModelMat.m[2][2] = forward.z;
447 ModelMat = LightViewMatrix[facenum];
448 ModelMat *= VMatrix4::Translate(-origin);
452 //==========================================================================
454 // VDrawer::CalcOrthoMatrix
456 //==========================================================================
457 void VDrawer::CalcOrthoMatrix (VMatrix4 &OrthoMat, const float left, const float right, const float bottom, const float top) {
458 const float nearVal = -666.0f;
459 const float farVal = 666.0f;
460 OrthoMat.SetZero();
461 OrthoMat[0][0] = 2.0f/(right-left);
462 OrthoMat[1][1] = 2.0f/(top-bottom); // [1+4]
463 OrthoMat[2][2] = -2.0f/(farVal-nearVal); // [2+8]
464 OrthoMat[3][0] = -(right+left)/(right-left); // [0+12]
465 OrthoMat[3][1] = -(top+bottom)/(top-bottom); // [1+12]
466 OrthoMat[3][2] = -(farVal+nearVal)/(farVal-nearVal); // [2+12]
467 OrthoMat[3][3] = 1.0f; // [3+12]
471 //==========================================================================
473 // VDrawer::SetOrthoProjection
475 //==========================================================================
476 void VDrawer::SetOrthoProjection (const float left, const float right, const float bottom, const float top) {
477 VMatrix4 omat;
478 CalcOrthoMatrix(omat, left, right, bottom, top);
479 SetProjectionMatrix(omat);
483 //==========================================================================
485 // VDrawer::DrawCrosshair
487 //==========================================================================
488 void VDrawer::DrawCrosshair () {
489 if (!needCrosshair) return;
490 needCrosshair = false;
491 if (RendLev) RendLev->RenderCrosshair();
495 //==========================================================================
497 // VDrawer::CalcEntityLight
499 //==========================================================================
500 vuint32 VDrawer::CalcEntityLight (VEntity *lowner, unsigned flags) {
501 if (!lowner || !RendLev) return 0;
502 return RendLev->CalcEntityLight(lowner, flags);
507 //**************************************************************************
509 // VRenderLevelDrawer methods
511 //**************************************************************************
513 //==========================================================================
515 // VRenderLevelDrawer::VRenderLevelDrawer
517 //==========================================================================
518 VRenderLevelDrawer::VRenderLevelDrawer (VLevel *ALevel) noexcept
519 : VRenderLevelPublic(ALevel)
520 , mIsShadowVolumeRenderer(false)
521 , PortalDepth(0)
522 , PortalUsingStencil(0)
523 , currDLightFrame(0)
524 , currQueueFrame(0)
529 //==========================================================================
531 // VRenderLevelDrawer::ResetDrawStack
533 // should be called before rendering a frame
534 // (i.e. in initial render, `RenderPlayerView()`)
535 // creates 0th element of the stack
537 //==========================================================================
538 void VRenderLevelDrawer::ResetDrawStack () {
539 DrawListStack.reset();
540 DrawLists &dls = DrawListStack.alloc();
541 dls.resetAll();
542 vassert(DrawListStack.length() > 0);
546 //==========================================================================
548 // VRenderLevelDrawer::PushDrawLists
550 //==========================================================================
551 void VRenderLevelDrawer::PushDrawLists () {
552 DrawLists &dls = DrawListStack.alloc();
553 dls.resetAll();
557 //==========================================================================
559 // VRenderLevelDrawer::PopDrawLists
561 //==========================================================================
562 void VRenderLevelDrawer::PopDrawLists () {
563 vensure(DrawListStack.length() != 0);
564 GetCurrentDLS().resetAll();
565 DrawListStack.setLength<false>(DrawListStack.length()-1); // don't resize
566 vassert(DrawListStack.length() > 0);
570 // code that tells windows we're High DPI aware so it doesn't scale our windows
571 // taken from Yamagi Quake II
573 typedef enum D3_PROCESS_DPI_AWARENESS {
574 D3_PROCESS_DPI_UNAWARE = 0,
575 D3_PROCESS_SYSTEM_DPI_AWARE = 1,
576 D3_PROCESS_PER_MONITOR_DPI_AWARE = 2
577 } YQ2_PROCESS_DPI_AWARENESS;
580 void R_FuckOffShitdoze () {
581 #ifdef _WIN32
582 /* for Vista, Win7 and Win8 */
583 BOOL(WINAPI *SetProcessDPIAware)(void) = nullptr;
584 HINSTANCE userDLL = LoadLibrary("USER32.DLL");
585 if (userDLL) SetProcessDPIAware = (BOOL(WINAPI *)(void))(void *)GetProcAddress(userDLL, "SetProcessDPIAware");
587 /* Win8.1 and later */
588 HRESULT(WINAPI *SetProcessDpiAwareness)(D3_PROCESS_DPI_AWARENESS dpiAwareness) = nullptr;
589 HINSTANCE shcoreDLL = LoadLibrary("SHCORE.DLL");
590 if (shcoreDLL) SetProcessDpiAwareness = (HRESULT(WINAPI *)(D3_PROCESS_DPI_AWARENESS))(void *)GetProcAddress(shcoreDLL, "SetProcessDpiAwareness");
592 if (SetProcessDpiAwareness) {
593 GCon->Log(NAME_Init, "DPI GTFO 8.1+");
594 SetProcessDpiAwareness(D3_PROCESS_PER_MONITOR_DPI_AWARE);
595 } else if (SetProcessDPIAware) {
596 GCon->Log(NAME_Init, "DPI GTFO 8");
597 SetProcessDPIAware();
599 #endif