1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
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.
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.
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/>.
25 //**************************************************************************
27 //** Game completion, final screen animation.
29 //**************************************************************************
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 //**************************************************************************
97 //**************************************************************************
99 // ////////////////////////////////////////////////////////////////////////// //
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
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.
116 extern TVec viewforward;
117 extern TVec viewright;
119 extern TAVec viewangles;
120 extern TFrustum viewfrustum;
122 extern bool MirrorFlip;
123 extern bool MirrorClip;
126 //==========================================================================
130 //==========================================================================
131 VDrawer::VDrawer () noexcept
132 : mInitialized(false)
134 , shittyGPUCheckDone(false)
136 , HaveDepthClamp(false)
137 , DepthZeroOne(false)
138 , canRenderShadowmaps(false)
140 , needCrosshair(false)
141 , prevCrosshairPic(-1)
146 ScrWdt
= max2(1, ScreenWidth
);
147 ScrHgt
= max2(1, ScreenHeight
);
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 //==========================================================================
171 //==========================================================================
172 VDrawer::~VDrawer () {
176 //==========================================================================
178 // VDrawer::RegisterICB
180 //==========================================================================
181 void VDrawer::RegisterICB (void (*cb
) (int phase
)) {
183 for (int f
= 0; f
< cbInitDeinit
.length(); ++f
) {
184 if (cbInitDeinit
[f
] == cb
) return;
186 cbInitDeinit
.append(cb
);
190 //==========================================================================
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
;
225 const float dividery
= 3.0f
;
226 const float hdiv
= h
/dividery
;
231 //==========================================================================
233 // VDrawer::CalcHexVertices
235 //==========================================================================
236 void VDrawer::CalcHexVertices (float vx
[6], float vy
[6], float x0
, float y0
, float w
, float h
) noexcept
{
238 const float dividery
= 3.0f
;
239 const float hdiv
= h
/dividery
;
240 const float wdiv
= w
*0.5f
;
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;
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
];
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
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
;
337 //glClearDepth(1.0f);
338 //glDepthFunc(GL_LEQUAL);
339 // const tanHalfFovy = tan(fovy*0.5f);
340 // rd->fovx = aspect*tanHalfFovy;
341 // rd->fovy = tanHalfFovy;
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
;
351 float maxdist
= gl_maxdist
.asFloat();
352 if (maxdist
< 1.0f
|| !isFiniteF(maxdist
)) maxdist
= 32767.0f
;
354 ProjMat
[2][2] = maxdist
/(zNear
-maxdist
); // zFar/(zNear-zFar);
355 ProjMat
[3][2] = -(maxdist
*zNear
)/(maxdist
-zNear
); // -(zFar*zNear)/(zFar-zNear);
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);
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);
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
;
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);
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
;
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
) {
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)
522 , PortalUsingStencil(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();
542 vassert(DrawListStack
.length() > 0);
546 //==========================================================================
548 // VRenderLevelDrawer::PushDrawLists
550 //==========================================================================
551 void VRenderLevelDrawer::PushDrawLists () {
552 DrawLists
&dls
= DrawListStack
.alloc();
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 () {
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();