Line data Source code
1 : /**
2 : * @file rendergl.cpp
3 : * @brief Core OpenGL rendering
4 : *
5 : * rendergl.cpp handles the main rendering functions, which render the scene
6 : * using OpenGL features aliased in this file. This file also handles the
7 : * position of the camera and the projection frustum handling.
8 : *
9 : * While this file does not handle light and texture rendering, it does handle
10 : * the simple world depth fog in libprimis.
11 : */
12 : #include "../libprimis-headers/cube.h"
13 : #include "../../shared/geomexts.h"
14 : #include "../../shared/glemu.h"
15 : #include "../../shared/glexts.h"
16 :
17 : #include <format>
18 :
19 : #include "aa.h"
20 : #include "grass.h"
21 : #include "hdr.h"
22 : #include "hud.h"
23 : #include "octarender.h"
24 : #include "postfx.h"
25 : #include "radiancehints.h"
26 : #include "rendergl.h"
27 : #include "renderlights.h"
28 : #include "rendermodel.h"
29 : #include "renderparticles.h"
30 : #include "rendersky.h"
31 : #include "rendertimers.h"
32 : #include "renderva.h"
33 : #include "renderwindow.h"
34 : #include "shader.h"
35 : #include "shaderparam.h"
36 : #include "texture.h"
37 : #include "water.h"
38 :
39 : #include "world/material.h"
40 : #include "world/octaedit.h"
41 : #include "world/octaworld.h"
42 : #include "world/raycube.h"
43 : #include "world/world.h"
44 :
45 : #include "interface/console.h"
46 : #include "interface/control.h"
47 : #include "interface/input.h"
48 : #include "interface/menus.h"
49 : #include "interface/ui.h"
50 :
51 : bool hasFBMSBS = false,
52 : hasTQ = false,
53 : hasDBT = false,
54 : hasEGPU4 = false,
55 : hasES3 = false,
56 : hasCI = false;
57 :
58 : //used in iengine
59 : VAR(outline, 0, 0, 1); //vertex/edge highlighting in edit mode
60 :
61 : //read-only info for gl debugging
62 : static VAR(glversion, 1, 0, 0);
63 : VAR(glslversion, 1, 0, 0);
64 :
65 : // GL_EXT_framebuffer_blit
66 : PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer_ = nullptr;
67 :
68 : // GL_EXT_framebuffer_multisample
69 : PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample_ = nullptr;
70 :
71 : // GL_ARB_texture_multisample
72 : PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample_ = nullptr;
73 :
74 : // OpenGL 1.3
75 : #ifdef WIN32
76 : PFNGLACTIVETEXTUREPROC glActiveTexture_ = nullptr;
77 :
78 : PFNGLBLENDEQUATIONEXTPROC glBlendEquation_ = nullptr;
79 : PFNGLBLENDCOLOREXTPROC glBlendColor_ = nullptr;
80 :
81 : PFNGLTEXIMAGE3DPROC glTexImage3D_ = nullptr;
82 : PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D_ = nullptr;
83 : PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D_ = nullptr;
84 :
85 : PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D_ = nullptr;
86 : PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D_ = nullptr;
87 : PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D_ = nullptr;
88 : PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D_ = nullptr;
89 : PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D_ = nullptr;
90 : PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D_ = nullptr;
91 : PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage_ = nullptr;
92 :
93 : PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements_ = nullptr;
94 : #endif
95 :
96 : // GL_EXT_depth_bounds_test
97 : PFNGLDEPTHBOUNDSEXTPROC glDepthBounds_ = nullptr;
98 :
99 : // GL_ARB_copy_image
100 : PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData_ = nullptr;
101 :
102 0 : void masktiles(uint *tiles, float sx1, float sy1, float sx2, float sy2)
103 : {
104 : int tx1, ty1, tx2, ty2;
105 0 : calctilebounds(sx1, sy1, sx2, sy2, tx1, ty1, tx2, ty2);
106 0 : for(int ty = ty1; ty < ty2; ty++)
107 : {
108 0 : tiles[ty] |= ((1<<(tx2-tx1))-1)<<tx1;
109 : }
110 0 : }
111 :
112 0 : static void *getprocaddress(const char *name)
113 : {
114 0 : return SDL_GL_GetProcAddress(name);
115 : }
116 :
117 : static VAR(glerr, 0, 0, 1);
118 :
119 : /**
120 : * @brief Prints out a GL error to the command line.
121 : *
122 : * Used by glerror(). This function is a helper to allow glerror() to print out
123 : * the location and file where a GL error occured.
124 : *
125 : * @param a filename to use to help locate where the error is
126 : * @param line the line of code in the file
127 : * @param error the GL error code to print out
128 : */
129 0 : static void glerror(const char *file, int line, GLenum error)
130 : {
131 0 : const char *desc = "unknown";
132 0 : switch(error)
133 : {
134 0 : case GL_NO_ERROR:
135 : {
136 0 : desc = "no error";
137 0 : break;
138 : }
139 0 : case GL_INVALID_ENUM:
140 : {
141 0 : desc = "invalid enum";
142 0 : break;
143 : }
144 0 : case GL_INVALID_VALUE:
145 : {
146 0 : desc = "invalid value";
147 0 : break;
148 : }
149 0 : case GL_INVALID_OPERATION:
150 : {
151 0 : desc = "invalid operation";
152 0 : break;
153 : }
154 0 : case GL_STACK_OVERFLOW:
155 : {
156 0 : desc = "stack overflow";
157 0 : break;
158 : }
159 0 : case GL_STACK_UNDERFLOW:
160 : {
161 0 : desc = "stack underflow";
162 0 : break;
163 : }
164 0 : case GL_OUT_OF_MEMORY:
165 : {
166 0 : desc = "out of memory";
167 0 : break;
168 : }
169 : }
170 0 : std::printf("GL error: %s:%d: %s (%x)\n", file, line, desc, error);
171 0 : }
172 :
173 0 : void glerror()
174 : {
175 0 : if(glerr)
176 : {
177 0 : GLenum error = glGetError();
178 0 : if(error != GL_NO_ERROR)
179 : {
180 0 : glerror(__FILE__, __LINE__, error);
181 : }
182 : }
183 0 : }
184 :
185 : VAR(intel_texalpha_bug, 0, 0, 1); //used in rendergl.h
186 : VAR(mesa_swap_bug, 0, 0, 1); //used in rendergl.h
187 : VAR(usetexgather, 1, 0, 0); //used in rendergl.h
188 : static VAR(maxdrawbufs, 1, 0, 0);
189 : VAR(maxdualdrawbufs, 1, 0, 0); //used in rendergl.h
190 :
191 : static VAR(debugexts, 0, 0, 1);
192 :
193 : static std::unordered_set<std::string> glexts;
194 :
195 : /**
196 : * @brief Adds available GL extensions to glexts vector.
197 : *
198 : * This populates glexts with the string names for the available extensions on the
199 : * current running system. If called multiple times, fails to insert any duplicate
200 : * values (which should mean no entries in most circumstances).
201 : */
202 0 : static void parseglexts()
203 : {
204 0 : GLint numexts = 0;
205 0 : glGetIntegerv(GL_NUM_EXTENSIONS, &numexts);
206 0 : for(int i = 0; i < numexts; ++i)
207 : {
208 : //cast from uchar * to char *
209 0 : const char *ext = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
210 0 : glexts.insert(ext);
211 : }
212 0 : }
213 :
214 : /**
215 : * @brief Searches for the specified extension in the glexts map.
216 : *
217 : * @param ext the extension to search for
218 : *
219 : * @return true if the string is in the glexts map, false otherwise
220 : */
221 1 : static bool hasext(const char *ext)
222 : {
223 3 : return glexts.find(ext)!=glexts.end();
224 : }
225 :
226 0 : static bool checkdepthtexstencilrb()
227 : {
228 0 : uint w = 256,
229 0 : h = 256;
230 0 : GLuint fbo = 0;
231 0 : glGenFramebuffers(1, &fbo);
232 0 : glBindFramebuffer(GL_FRAMEBUFFER, fbo);
233 :
234 0 : GLuint depthtex = 0;
235 0 : glGenTextures(1, &depthtex);
236 0 : createtexture(depthtex, w, h, nullptr, 3, 0, GL_DEPTH_COMPONENT24, GL_TEXTURE_RECTANGLE);
237 0 : glBindTexture(GL_TEXTURE_RECTANGLE, 0);
238 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_RECTANGLE, depthtex, 0);
239 :
240 0 : GLuint stencilrb = 0;
241 0 : glGenRenderbuffers(1, &stencilrb);
242 0 : glBindRenderbuffer(GL_RENDERBUFFER, stencilrb);
243 0 : glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h);
244 0 : glBindRenderbuffer(GL_RENDERBUFFER, 0);
245 0 : glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencilrb);
246 :
247 0 : bool supported = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
248 :
249 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
250 0 : glDeleteFramebuffers(1, &fbo);
251 0 : glDeleteTextures(1, &depthtex);
252 0 : glDeleteRenderbuffers(1, &stencilrb);
253 :
254 0 : return supported;
255 : }
256 :
257 0 : void gl_checkextensions()
258 : {
259 0 : bool mesa = false,
260 0 : intel = false,
261 0 : amd = false,
262 0 : nvidia = false;
263 0 : const char *vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR)),
264 0 : *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER)),
265 0 : *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
266 0 : conoutf(Console_Init, "Renderer: %s (%s)", renderer, vendor);
267 0 : conoutf(Console_Init, "Driver: %s", version);
268 :
269 0 : if(!renderer || !vendor || !version)
270 : {
271 0 : fatal("Could not get rendering context information!");
272 : }
273 0 : if(std::strstr(renderer, "Mesa") || std::strstr(version, "Mesa"))
274 : {
275 0 : mesa = true;
276 0 : if(std::strstr(renderer, "Intel"))
277 : {
278 0 : intel = true;
279 : }
280 : }
281 0 : else if(std::strstr(vendor, "NVIDIA"))
282 : {
283 0 : nvidia = true;
284 : }
285 0 : else if(std::strstr(vendor, "ATI") || std::strstr(vendor, "Advanced Micro Devices"))
286 : {
287 0 : amd = true;
288 : }
289 0 : else if(std::strstr(vendor, "Intel"))
290 : {
291 0 : intel = true;
292 : }
293 :
294 : uint glmajorversion, glminorversion;
295 0 : if(std::sscanf(version, " %u.%u", &glmajorversion, &glminorversion) != 2)
296 : {
297 0 : glversion = 100; //__really__ legacy systems (which won't run anyways)
298 : }
299 : else
300 : {
301 0 : glversion = glmajorversion*100 + glminorversion*10;
302 : }
303 0 : if(glversion < 400)
304 : {
305 0 : fatal("OpenGL 4.0 or greater is required!");
306 : }
307 :
308 0 : const char *glslstr = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
309 0 : conoutf(Console_Init, "GLSL: %s", glslstr ? glslstr : "unknown");
310 :
311 : uint glslmajorversion, glslminorversion;
312 0 : if(glslstr && std::sscanf(glslstr, " %u.%u", &glslmajorversion, &glslminorversion) == 2)
313 : {
314 0 : glslversion = glslmajorversion*100 + glslminorversion;
315 : }
316 0 : if(glslversion < 400)
317 : {
318 0 : fatal("GLSL 4.00 or greater is required!");
319 : }
320 0 : parseglexts();
321 0 : GLint texsize = 0,
322 0 : texunits = 0,
323 0 : vtexunits = 0,
324 0 : cubetexsize = 0,
325 0 : drawbufs = 0;
326 0 : glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texsize);
327 0 : hwtexsize = texsize;
328 0 : if(hwtexsize < 4096)
329 : {
330 0 : fatal("Large texture support is required!");
331 : }
332 0 : glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &texunits);
333 0 : hwtexunits = texunits;
334 0 : if(hwtexunits < 16)
335 : {
336 0 : fatal("Hardware does not support at least 16 texture units.");
337 : }
338 0 : glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &vtexunits);
339 0 : hwvtexunits = vtexunits;
340 0 : if(hwvtexunits < 16)
341 : {
342 0 : fatal("Hardware does not support at least 16 vertex texture units.");
343 : }
344 0 : glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &cubetexsize);
345 0 : hwcubetexsize = cubetexsize;
346 0 : glGetIntegerv(GL_MAX_DRAW_BUFFERS, &drawbufs);
347 0 : maxdrawbufs = drawbufs;
348 0 : if(maxdrawbufs < 4)
349 : {
350 0 : fatal("Hardware does not support at least 4 draw buffers.");
351 : }
352 : //OpenGL 3.0
353 :
354 0 : if(hasext("GL_EXT_gpu_shader4"))
355 : {
356 0 : hasEGPU4 = true;
357 0 : if(debugexts)
358 : {
359 0 : conoutf(Console_Init, "Using GL_EXT_gpu_shader4 extension.");
360 : }
361 : }
362 0 : glRenderbufferStorageMultisample_ = reinterpret_cast<PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC>(getprocaddress("glRenderbufferStorageMultisample"));
363 :
364 : //OpenGL 3.2
365 0 : glTexImage2DMultisample_ = reinterpret_cast<PFNGLTEXIMAGE2DMULTISAMPLEPROC>(getprocaddress("glTexImage2DMultisample"));
366 0 : if(hasext("GL_EXT_framebuffer_multisample_blit_scaled"))
367 : {
368 0 : hasFBMSBS = true;
369 0 : if(debugexts)
370 : {
371 0 : conoutf(Console_Init, "Using GL_EXT_framebuffer_multisample_blit_scaled extension.");
372 : }
373 : }
374 : //OpenGL 3.3
375 0 : if(hasext("GL_EXT_texture_filter_anisotropic"))
376 : {
377 0 : GLint val = 0;
378 0 : glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &val);
379 0 : hwmaxaniso = val;
380 0 : if(debugexts)
381 : {
382 0 : conoutf(Console_Init, "Using GL_EXT_texture_filter_anisotropic extension.");
383 : }
384 : }
385 : else
386 : {
387 0 : fatal("Anisotropic filtering support is required!");
388 : }
389 0 : if(hasext("GL_EXT_depth_bounds_test"))
390 : {
391 0 : glDepthBounds_ = reinterpret_cast<PFNGLDEPTHBOUNDSEXTPROC>(getprocaddress("glDepthBoundsEXT"));
392 0 : hasDBT = true;
393 0 : if(debugexts)
394 : {
395 0 : conoutf(Console_Init, "Using GL_EXT_depth_bounds_test extension.");
396 : }
397 : }
398 0 : GLint dualbufs = 0;
399 0 : glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, &dualbufs);
400 0 : maxdualdrawbufs = dualbufs;
401 0 : usetexgather = !intel && !nvidia ? 2 : 1;
402 : //OpenGL 4.x
403 0 : if(glversion >= 430 || hasext("GL_ARB_ES3_compatibility"))
404 : {
405 0 : hasES3 = true;
406 0 : if(glversion < 430 && debugexts)
407 : {
408 0 : conoutf(Console_Init, "Using GL_ARB_ES3_compatibility extension.");
409 : }
410 : }
411 :
412 0 : if(glversion >= 430 || hasext("GL_ARB_copy_image"))
413 : {
414 0 : glCopyImageSubData_ = reinterpret_cast<PFNGLCOPYIMAGESUBDATAPROC>(getprocaddress("glCopyImageSubData"));
415 :
416 0 : hasCI = true;
417 0 : if(glversion < 430 && debugexts)
418 : {
419 0 : conoutf(Console_Init, "Using GL_ARB_copy_image extension.");
420 : }
421 : }
422 0 : else if(hasext("GL_NV_copy_image"))
423 : {
424 0 : glCopyImageSubData_ = reinterpret_cast<PFNGLCOPYIMAGESUBDATAPROC>(getprocaddress("glCopyImageSubDataNV"));
425 :
426 0 : hasCI = true;
427 0 : if(debugexts)
428 : {
429 0 : conoutf(Console_Init, "Using GL_NV_copy_image extension.");
430 : }
431 : }
432 :
433 0 : if(amd)
434 : {
435 0 : msaalineardepth = glineardepth = 1; // reading back from depth-stencil still buggy on newer cards, and requires stencil for MSAA
436 : }
437 0 : else if(nvidia) //no errata on nvidia cards (yet)
438 : {
439 : }
440 0 : else if(intel)
441 : {
442 0 : smgather = 1; // native shadow filter is slow
443 0 : if(mesa)
444 : {
445 0 : batchsunlight = 0; // causes massive slowdown in linux driver
446 0 : msaalineardepth = 1; // MSAA depth texture access is buggy and resolves are slow
447 : }
448 : else
449 : {
450 : // causes massive slowdown in windows driver if reading depth-stencil texture
451 0 : if(checkdepthtexstencilrb())
452 : {
453 0 : gdepthstencil = 1;
454 0 : gstencil = 1;
455 : }
456 : // sampling alpha by itself from a texture generates garbage on Intel drivers on Windows
457 0 : intel_texalpha_bug = 1;
458 : }
459 : }
460 0 : if(mesa)
461 : {
462 0 : mesa_swap_bug = 1;
463 : }
464 0 : tqaaresolvegather = 1;
465 0 : }
466 :
467 : /**
468 : * @brief checks for existence of glext
469 : *
470 : * returns to console 1 if hashtable glexts contains glext (with the name passed)
471 : * and returns 0 otherwise
472 : *
473 : * glexts is a global variable
474 : *
475 : * @param ext the ext to check for
476 : */
477 1 : static void glext(const char *ext)
478 : {
479 1 : intret(hasext(ext) ? 1 : 0);
480 1 : }
481 :
482 :
483 0 : void gl_resize()
484 : {
485 0 : gl_setupframe();
486 0 : glViewport(0, 0, hudw(), hudh());
487 0 : }
488 :
489 0 : void gl_init()
490 : {
491 0 : glerror();
492 :
493 0 : glClearColor(0, 0, 0, 0);
494 0 : glClearDepth(1);
495 0 : glClearStencil(0);
496 0 : glDepthFunc(GL_LESS);
497 0 : glDisable(GL_DEPTH_TEST);
498 0 : glDisable(GL_STENCIL_TEST);
499 0 : glStencilFunc(GL_ALWAYS, 0, ~0);
500 0 : glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
501 :
502 0 : glEnable(GL_LINE_SMOOTH);
503 : //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
504 :
505 0 : glFrontFace(GL_CW);
506 0 : glCullFace(GL_BACK);
507 0 : glDisable(GL_CULL_FACE);
508 :
509 0 : gle::setup();
510 0 : setupshaders();
511 :
512 0 : glerror();
513 :
514 0 : gl_resize();
515 0 : }
516 :
517 : VAR(wireframe, 0, 0, 1); //used in rendergl.h
518 :
519 : vec worldpos; //used in iengine
520 :
521 : //these three cam() functions replace global variables that previously tracked their respective transforms of cammatrix
522 0 : vec camdir()
523 : {
524 0 : vec out;
525 0 : cammatrix.transposedtransformnormal(vec(viewmatrix.b), out);
526 0 : return out;
527 : }
528 :
529 0 : vec camright()
530 : {
531 0 : vec out;
532 0 : cammatrix.transposedtransformnormal(vec(viewmatrix.a).neg(), out);
533 0 : return out;
534 : }
535 :
536 0 : vec camup()
537 : {
538 0 : vec out;
539 0 : cammatrix.transposedtransformnormal(vec(viewmatrix.c), out);
540 0 : return out;
541 : }
542 :
543 0 : static void setcammatrix()
544 : {
545 : // move from RH to Z-up LH quake style worldspace
546 0 : cammatrix = viewmatrix;
547 0 : cammatrix.rotate_around_y(camera1->roll/RAD);
548 0 : cammatrix.rotate_around_x(camera1->pitch/-RAD);
549 0 : cammatrix.rotate_around_z(camera1->yaw/-RAD);
550 0 : cammatrix.translate(vec(camera1->o).neg());
551 :
552 0 : if(!drawtex)
553 : {
554 0 : if(raycubepos(camera1->o, camdir(), worldpos, 0, Ray_ClipMat|Ray_SkipFirst) == -1)
555 : {
556 0 : worldpos = camdir().mul(2*rootworld.mapsize()).add(camera1->o); // if nothing is hit, just far away in the view direction
557 : }
558 : }
559 0 : }
560 :
561 0 : static void setcamprojmatrix(bool init = true, bool flush = false)
562 : {
563 0 : if(init)
564 : {
565 0 : setcammatrix();
566 : }
567 0 : jitteraa();
568 0 : camprojmatrix.muld(projmatrix, cammatrix);
569 0 : GLOBALPARAM(camprojmatrix, camprojmatrix);
570 0 : GLOBALPARAM(lineardepthscale, projmatrix.lineardepthscale()); //(invprojmatrix.c.z, invprojmatrix.d.z));
571 0 : if(flush && Shader::lastshader)
572 : {
573 0 : Shader::lastshader->flushparams();
574 : }
575 0 : }
576 :
577 : matrix4 hudmatrix;
578 : static std::array<matrix4, 64> hudmatrixstack;
579 :
580 : int hudmatrixpos = 0;
581 :
582 0 : void resethudmatrix()
583 : {
584 0 : hudmatrixpos = 0;
585 0 : GLOBALPARAM(hudmatrix, hudmatrix);
586 0 : }
587 :
588 0 : void pushhudmatrix()
589 : {
590 0 : if(hudmatrixpos >= 0 && hudmatrixpos < static_cast<int>(sizeof(hudmatrixstack)/sizeof(hudmatrixstack[0])))
591 : {
592 0 : hudmatrixstack[hudmatrixpos] = hudmatrix;
593 : }
594 0 : ++hudmatrixpos;
595 0 : }
596 :
597 0 : void flushhudmatrix(bool flushparams)
598 : {
599 0 : GLOBALPARAM(hudmatrix, hudmatrix);
600 0 : if(flushparams && Shader::lastshader)
601 : {
602 0 : Shader::lastshader->flushparams();
603 : }
604 0 : }
605 :
606 0 : void pophudmatrix(bool flush, bool flushparams)
607 : {
608 0 : --hudmatrixpos;
609 0 : if(hudmatrixpos >= 0 && hudmatrixpos < static_cast<int>(sizeof(hudmatrixstack)/sizeof(hudmatrixstack[0])))
610 : {
611 0 : hudmatrix = hudmatrixstack[hudmatrixpos];
612 0 : if(flush)
613 : {
614 0 : flushhudmatrix(flushparams);
615 : }
616 : }
617 0 : }
618 :
619 0 : void pushhudscale(float scale)
620 : {
621 0 : pushhudmatrix();
622 0 : hudmatrix.scale(scale, scale, 1);
623 0 : flushhudmatrix();
624 0 : }
625 :
626 0 : void pushhudtranslate(float tx, float ty, float sx, float sy)
627 : {
628 0 : if(!sy)
629 : {
630 0 : sy = sx;
631 : }
632 0 : pushhudmatrix();
633 0 : hudmatrix.translate(tx, ty, 0);
634 0 : if(sy)
635 : {
636 0 : hudmatrix.scale(sx, sy, 1);
637 : }
638 0 : flushhudmatrix();
639 0 : }
640 :
641 : static float curfov, aspect;
642 : float fovy;
643 : static float curavatarfov;
644 : int farplane;
645 : static VARP(zoominvel, 0, 40, 500);
646 : static VARP(zoomoutvel, 0, 50, 500);
647 : static VARP(zoomfov, 10, 42, 90);
648 : static VARP(fov, 10, 100, 150);
649 : static VAR(avatarzoomfov, 1, 1, 1);
650 : static VAR(avatarfov, 10, 40, 100);
651 : static FVAR(avatardepth, 0, 0.7f, 1);
652 : FVARNP(aspect, forceaspect, 0, 0, 1e3f);
653 :
654 : static float zoomprogress = 0;
655 : VAR(zoom, -1, 0, 1);
656 :
657 : //used in iengine
658 0 : void disablezoom()
659 : {
660 0 : zoom = 0;
661 0 : zoomprogress = 0;
662 0 : }
663 :
664 : //used in iengine
665 0 : void computezoom()
666 : {
667 0 : if(!zoom)
668 : {
669 0 : zoomprogress = 0;
670 0 : curfov = fov;
671 0 : curavatarfov = avatarfov;
672 0 : return;
673 : }
674 0 : if(zoom > 0)
675 : {
676 0 : zoomprogress = zoominvel ? std::min(zoomprogress + static_cast<float>(elapsedtime) / zoominvel, 1.0f) : 1;
677 : }
678 : else
679 : {
680 0 : zoomprogress = zoomoutvel ? std::max(zoomprogress - static_cast<float>(elapsedtime) / zoomoutvel, 0.0f) : 0;
681 0 : if(zoomprogress <= 0)
682 : {
683 0 : zoom = 0;
684 : }
685 : }
686 0 : curfov = zoomfov*zoomprogress + fov*(1 - zoomprogress);
687 0 : curavatarfov = avatarzoomfov*zoomprogress + avatarfov*(1 - zoomprogress);
688 : }
689 :
690 : static FVARP(zoomsens, 1e-4f, 4.5f, 1e4f);
691 : static FVARP(zoomaccel, 0, 0, 1000);
692 : static VARP(zoomautosens, 0, 1, 1);
693 : static FVARP(sensitivity, 0.01f, 3, 100.f);
694 : static FVARP(sensitivityscale, 1e-4f, 100, 1e4f);
695 : /* Sensitivity scales:
696 : * 100: Quake/Source (TF2, Q3, Apex, L4D)
697 : * 333: COD, Destiny, Overwatch, ~BL2/3
698 : * 400: Cube/RE
699 : */
700 : static VARP(invmouse, 0, 0, 1); //toggles inverting the mouse
701 : static FVARP(mouseaccel, 0, 0, 1000);
702 :
703 : physent *camera1 = nullptr;
704 : //used in iengine.h
705 : bool detachedcamera = false;
706 :
707 : //used in iengine.h
708 0 : bool isthirdperson()
709 : {
710 0 : return player!=camera1 || detachedcamera;
711 : }
712 :
713 0 : void fixcamerarange()
714 : {
715 0 : constexpr float maxpitch = 90.0f;
716 0 : if(camera1->pitch>maxpitch)
717 : {
718 0 : camera1->pitch = maxpitch;
719 : }
720 0 : if(camera1->pitch<-maxpitch)
721 : {
722 0 : camera1->pitch = -maxpitch;
723 : }
724 0 : while(camera1->yaw<0.0f)
725 : {
726 0 : camera1->yaw += 360.0f;
727 : }
728 0 : while(camera1->yaw>=360.0f)
729 : {
730 0 : camera1->yaw -= 360.0f;
731 : }
732 0 : }
733 :
734 0 : void modifyorient(float yaw, float pitch)
735 : {
736 0 : camera1->yaw += yaw;
737 0 : camera1->pitch += pitch;
738 0 : fixcamerarange();
739 0 : if(camera1!=player && !detachedcamera)
740 : {
741 0 : player->yaw = camera1->yaw;
742 0 : player->pitch = camera1->pitch;
743 : }
744 0 : }
745 :
746 0 : void mousemove(int dx, int dy)
747 : {
748 0 : float cursens = sensitivity,
749 0 : curaccel = mouseaccel;
750 0 : if(zoom)
751 : {
752 0 : if(zoomautosens)
753 : {
754 0 : cursens = static_cast<float>(sensitivity*zoomfov)/fov;
755 0 : curaccel = static_cast<float>(mouseaccel*zoomfov)/fov;
756 : }
757 : else
758 : {
759 0 : cursens = zoomsens;
760 0 : curaccel = zoomaccel;
761 : }
762 : }
763 0 : if(curaccel && curtime && (dx || dy))
764 : {
765 0 : cursens += curaccel * sqrtf(dx*dx + dy*dy)/curtime;
766 : }
767 0 : cursens /= (sensitivityscale/4); //hard factor of 4 for 40 dots/deg like Quake/Source/etc.
768 0 : modifyorient(dx*cursens, dy*cursens*(invmouse ? 1 : -1));
769 0 : }
770 :
771 : matrix4 cammatrix, projmatrix, camprojmatrix;
772 :
773 : FVAR(nearplane, 0.01f, 0.54f, 2.0f); //used in rendergl
774 :
775 0 : vec calcavatarpos(const vec &pos, float dist)
776 : {
777 0 : vec eyepos;
778 0 : cammatrix.transform(pos, eyepos);
779 0 : GLdouble ydist = nearplane * std::tan(curavatarfov/(2*RAD)),
780 0 : xdist = ydist * aspect;
781 0 : vec4<float> scrpos;
782 0 : scrpos.x = eyepos.x*nearplane/xdist;
783 0 : scrpos.y = eyepos.y*nearplane/ydist;
784 0 : scrpos.z = (eyepos.z*(farplane + nearplane) - 2*nearplane*farplane) / (farplane - nearplane);
785 0 : scrpos.w = -eyepos.z;
786 :
787 0 : vec worldpos = camprojmatrix.inverse().perspectivetransform(scrpos);
788 0 : vec dir = vec(worldpos).sub(camera1->o).rescale(dist);
789 0 : return dir.add(camera1->o);
790 : }
791 :
792 0 : void renderavatar(void (*hudfxn)())
793 : {
794 0 : if(isthirdperson())
795 : {
796 0 : return;
797 : }
798 0 : matrix4 oldprojmatrix = nojittermatrix;
799 0 : projmatrix.perspective(curavatarfov, aspect, nearplane, farplane);
800 0 : projmatrix.scalez(avatardepth);
801 0 : setcamprojmatrix(false);
802 :
803 0 : enableavatarmask();
804 0 : hudfxn();
805 0 : disableavatarmask();
806 :
807 0 : projmatrix = oldprojmatrix;
808 0 : setcamprojmatrix(false);
809 : }
810 :
811 : static FVAR(polygonoffsetfactor, -1e4f, -3.0f, 1e4f);
812 : static FVAR(polygonoffsetunits, -1e4f, -3.0f, 1e4f);
813 : static FVAR(depthoffset, -1e4f, 0.01f, 1e4f);
814 :
815 : static matrix4 nooffsetmatrix;
816 :
817 : //used in rendergl.h
818 0 : void enablepolygonoffset(GLenum type)
819 : {
820 0 : if(!depthoffset)
821 : {
822 0 : glPolygonOffset(polygonoffsetfactor, polygonoffsetunits);
823 0 : glEnable(type);
824 0 : return;
825 : }
826 :
827 0 : projmatrix = nojittermatrix;
828 0 : nooffsetmatrix = projmatrix;
829 0 : projmatrix.d.z += depthoffset * projmatrix.c.z;
830 0 : setcamprojmatrix(false, true);
831 : }
832 :
833 : //used in rendergl.h
834 0 : void disablepolygonoffset(GLenum type)
835 : {
836 0 : if(!depthoffset)
837 : {
838 0 : glDisable(type);
839 0 : return;
840 : }
841 :
842 0 : projmatrix = nooffsetmatrix;
843 0 : setcamprojmatrix(false, true);
844 : }
845 :
846 : //used in renderlights
847 0 : bool calcspherescissor(const vec ¢er, float size, float &sx1, float &sy1, float &sx2, float &sy2, float &sz1, float &sz2)
848 : {
849 : //dim must be 0..2
850 : //dir should be +/- 1
851 0 : auto checkplane = [] (int dim, float dc, int dir, float focaldist, float &low, float &high, float cz, float drt, const vec &e) -> void
852 : {
853 0 : float nzc = (cz*cz + 1) / (cz + dir*drt) - cz,
854 0 : pz = dc/(nzc*e[dim] - e.z);
855 0 : if(pz > 0)
856 : {
857 0 : float c = (focaldist)*nzc,
858 0 : pc = pz*nzc;
859 0 : if(pc < e[dim])
860 : {
861 0 : low = c;
862 : }
863 0 : else if(pc > e[dim])
864 : {
865 0 : high = c;
866 : }
867 : }
868 0 : };
869 :
870 0 : vec e;
871 0 : cammatrix.transform(center, e);
872 0 : if(e.z > 2*size)
873 : {
874 0 : sx1 = sy1 = sz1 = 1;
875 0 : sx2 = sy2 = sz2 = -1;
876 0 : return false;
877 : }
878 0 : if(drawtex == Draw_TexMinimap)
879 : {
880 0 : vec dir(size, size, size);
881 0 : if(projmatrix.a.x < 0)
882 : {
883 0 : dir.x = -dir.x;
884 : }
885 0 : if(projmatrix.b.y < 0)
886 : {
887 0 : dir.y = -dir.y;
888 : }
889 0 : if(projmatrix.c.z < 0)
890 : {
891 0 : dir.z = -dir.z;
892 : }
893 0 : sx1 = std::max(projmatrix.a.x*(e.x - dir.x) + projmatrix.d.x, -1.0f);
894 0 : sx2 = std::min(projmatrix.a.x*(e.x + dir.x) + projmatrix.d.x, 1.0f);
895 0 : sy1 = std::max(projmatrix.b.y*(e.y - dir.y) + projmatrix.d.y, -1.0f);
896 0 : sy2 = std::min(projmatrix.b.y*(e.y + dir.y) + projmatrix.d.y, 1.0f);
897 0 : sz1 = std::max(projmatrix.c.z*(e.z - dir.z) + projmatrix.d.z, -1.0f);
898 0 : sz2 = std::min(projmatrix.c.z*(e.z + dir.z) + projmatrix.d.z, 1.0f);
899 0 : return sx1 < sx2 && sy1 < sy2 && sz1 < sz2;
900 : }
901 0 : float zzrr = e.z*e.z - size*size,
902 0 : dx = e.x*e.x + zzrr,
903 0 : dy = e.y*e.y + zzrr,
904 0 : focaldist = 1.0f/std::tan(fovy*0.5f/RAD);
905 0 : sx1 = sy1 = -1;
906 0 : sx2 = sy2 = 1;
907 0 : if(dx > 0)
908 : {
909 0 : float cz = e.x/e.z,
910 0 : drt = sqrtf(dx)/size;
911 0 : checkplane(0, dx, -1, focaldist/aspect, sx1, sx2, cz, drt, e);
912 0 : checkplane(0, dx, 1, focaldist/aspect, sx1, sx2, cz, drt, e);
913 : }
914 0 : if(dy > 0)
915 : {
916 0 : float cz = e.y/e.z,
917 0 : drt = sqrtf(dy)/size;
918 0 : checkplane(1, dy, -1, focaldist, sy1, sy2, cz, drt, e);
919 0 : checkplane(1, dy, 1, focaldist, sy1, sy2, cz, drt, e);
920 : }
921 0 : float z1 = std::min(e.z + size, -1e-3f - nearplane),
922 0 : z2 = std::min(e.z - size, -1e-3f - nearplane);
923 0 : sz1 = (z1*projmatrix.c.z + projmatrix.d.z) / (z1*projmatrix.c.w + projmatrix.d.w);
924 0 : sz2 = (z2*projmatrix.c.z + projmatrix.d.z) / (z2*projmatrix.c.w + projmatrix.d.w);
925 0 : return sx1 < sx2 && sy1 < sy2 && sz1 < sz2;
926 : }
927 :
928 : //used in rendergl.h
929 0 : bool calcbbscissor(const ivec &bbmin, const ivec &bbmax, float &sx1, float &sy1, float &sx2, float &sy2)
930 : {
931 0 : auto addxyscissor = [&] (const vec4<float> &p)
932 : {
933 0 : if(p.z >= -p.w)
934 : {
935 0 : float x = p.x / p.w,
936 0 : y = p.y / p.w;
937 0 : sx1 = std::min(sx1, x);
938 0 : sy1 = std::min(sy1, y);
939 0 : sx2 = std::max(sx2, x);
940 0 : sy2 = std::max(sy2, y);
941 : }
942 0 : };
943 :
944 0 : std::array<vec4<float>, 8> v;
945 0 : sx1 = sy1 = 1;
946 0 : sx2 = sy2 = -1;
947 0 : camprojmatrix.transform(vec(bbmin.x, bbmin.y, bbmin.z), v[0]);
948 0 : addxyscissor(v[0]);
949 0 : camprojmatrix.transform(vec(bbmax.x, bbmin.y, bbmin.z), v[1]);
950 0 : addxyscissor(v[1]);
951 0 : camprojmatrix.transform(vec(bbmin.x, bbmax.y, bbmin.z), v[2]);
952 0 : addxyscissor(v[2]);
953 0 : camprojmatrix.transform(vec(bbmax.x, bbmax.y, bbmin.z), v[3]);
954 0 : addxyscissor(v[3]);
955 0 : camprojmatrix.transform(vec(bbmin.x, bbmin.y, bbmax.z), v[4]);
956 0 : addxyscissor(v[4]);
957 0 : camprojmatrix.transform(vec(bbmax.x, bbmin.y, bbmax.z), v[5]);
958 0 : addxyscissor(v[5]);
959 0 : camprojmatrix.transform(vec(bbmin.x, bbmax.y, bbmax.z), v[6]);
960 0 : addxyscissor(v[6]);
961 0 : camprojmatrix.transform(vec(bbmax.x, bbmax.y, bbmax.z), v[7]);
962 0 : addxyscissor(v[7]);
963 0 : if(sx1 > sx2 || sy1 > sy2)
964 : {
965 0 : return false;
966 : }
967 0 : for(int i = 0; i < 8; ++i)
968 : {
969 0 : const vec4<float> &p = v[i];
970 0 : if(p.z >= -p.w)
971 : {
972 0 : continue;
973 : }
974 0 : for(int j = 0; j < 3; ++j)
975 : {
976 0 : const vec4<float> &o = v[i^(1<<j)];
977 0 : if(o.z <= -o.w)
978 : {
979 0 : continue;
980 : }
981 :
982 0 : float t = (p.z + p.w)/(p.z + p.w - o.z - o.w),
983 0 : w = p.w + t*(o.w - p.w),
984 0 : x = (p.x + t*(o.x - p.x))/w,
985 0 : y = (p.y + t*(o.y - p.y))/w;
986 0 : sx1 = std::min(sx1, x);
987 0 : sy1 = std::min(sy1, y);
988 0 : sx2 = std::max(sx2, x);
989 0 : sy2 = std::max(sy2, y);
990 : }
991 : }
992 :
993 :
994 0 : sx1 = std::max(sx1, -1.0f);
995 0 : sy1 = std::max(sy1, -1.0f);
996 0 : sx2 = std::min(sx2, 1.0f);
997 0 : sy2 = std::min(sy2, 1.0f);
998 0 : return true;
999 : }
1000 :
1001 : //used in renderlights
1002 0 : bool calcspotscissor(const vec &origin, float radius, const vec &dir, int spot, const vec &spotx, const vec &spoty, float &sx1, float &sy1, float &sx2, float &sy2, float &sz1, float &sz2)
1003 : {
1004 0 : static auto addxyzscissor = [] (const vec4<float> &p, float &sx1, float &sy1, float &sx2, float &sy2, float &sz1, float &sz2) -> void
1005 : {
1006 0 : if(p.z >= -p.w)
1007 : {
1008 0 : float x = p.x / p.w,
1009 0 : y = p.y / p.w,
1010 0 : z = p.z / p.w;
1011 0 : sx1 = std::min(sx1, x);
1012 0 : sy1 = std::min(sy1, y);
1013 0 : sz1 = std::min(sz1, z);
1014 0 : sx2 = std::max(sx2, x);
1015 0 : sy2 = std::max(sy2, y);
1016 0 : sz2 = std::max(sz2, z);
1017 : }
1018 0 : };
1019 0 : float spotscale = radius * tan360(spot);
1020 0 : vec up = vec(spotx).mul(spotscale),
1021 0 : right = vec(spoty).mul(spotscale),
1022 0 : center = vec(dir).mul(radius).add(origin);
1023 0 : std::array<vec4<float>, 5> v;
1024 0 : sx1 = sy1 = sz1 = 1;
1025 0 : sx2 = sy2 = sz2 = -1;
1026 0 : camprojmatrix.transform(vec(center).sub(right).sub(up), v[0]);
1027 0 : addxyzscissor(v[0], sx1, sy1, sx2, sy2, sz1, sz2);
1028 0 : camprojmatrix.transform(vec(center).add(right).sub(up), v[1]);
1029 0 : addxyzscissor(v[1], sx1, sy1, sx2, sy2, sz1, sz2);
1030 0 : camprojmatrix.transform(vec(center).sub(right).add(up), v[2]);
1031 0 : addxyzscissor(v[2], sx1, sy1, sx2, sy2, sz1, sz2);
1032 0 : camprojmatrix.transform(vec(center).add(right).add(up), v[3]);
1033 0 : addxyzscissor(v[3], sx1, sy1, sx2, sy2, sz1, sz2);
1034 0 : camprojmatrix.transform(origin, v[4]);
1035 0 : addxyzscissor(v[4], sx1, sy1, sx2, sy2, sz1, sz2);
1036 :
1037 0 : static auto interpxyzscissor = [] (const vec4<float> &p, const vec4<float> &o, float &sx1, float &sy1, float &sx2, float &sy2, float &sz1) -> void
1038 : {
1039 0 : float t = (p.z + p.w)/(p.z + p.w - o.z - o.w),
1040 0 : w = p.w + t*(o.w - p.w),
1041 0 : x = (p.x + t*(o.x - p.x))/w,
1042 0 : y = (p.y + t*(o.y - p.y))/w;
1043 0 : sx1 = std::min(sx1, x);
1044 0 : sy1 = std::min(sy1, y);
1045 0 : sz1 = std::min(sz1, -1.0f);
1046 0 : sx2 = std::max(sx2, x);
1047 0 : sy2 = std::max(sy2, y);
1048 0 : };
1049 :
1050 0 : if(sx1 > sx2 || sy1 > sy2 || sz1 > sz2)
1051 : {
1052 0 : return false;
1053 : }
1054 0 : for(int i = 0; i < 4; ++i)
1055 : {
1056 0 : const vec4<float> &p = v[i];
1057 0 : if(p.z >= -p.w)
1058 : {
1059 0 : continue;
1060 : }
1061 0 : for(int j = 0; j < 2; ++j)
1062 : {
1063 0 : const vec4<float> &o = v[i^(1<<j)];
1064 0 : if(o.z <= -o.w)
1065 : {
1066 0 : continue;
1067 : }
1068 :
1069 0 : interpxyzscissor(p, o, sx1, sy1, sx2, sy2, sz1);
1070 : }
1071 0 : if(v[4].z > -v[4].w)
1072 : {
1073 0 : interpxyzscissor(p, v[4], sx1, sy1, sx2, sy2, sz1);
1074 : }
1075 : }
1076 0 : if(v[4].z < -v[4].w)
1077 : {
1078 0 : for(int j = 0; j < 4; ++j)
1079 : {
1080 0 : const vec4<float> &o = v[j];
1081 0 : if(o.z <= -o.w)
1082 : {
1083 0 : continue;
1084 : }
1085 0 : interpxyzscissor(v[4], o, sx1, sy1, sx2, sy2, sz1);
1086 : }
1087 : }
1088 :
1089 0 : sx1 = std::max(sx1, -1.0f);
1090 0 : sy1 = std::max(sy1, -1.0f);
1091 0 : sz1 = std::max(sz1, -1.0f);
1092 0 : sx2 = std::min(sx2, 1.0f);
1093 0 : sy2 = std::min(sy2, 1.0f);
1094 0 : sz2 = std::min(sz2, 1.0f);
1095 0 : return true;
1096 : }
1097 :
1098 : static GLuint screenquadvbo = 0;
1099 :
1100 0 : static void setupscreenquad()
1101 : {
1102 0 : if(!screenquadvbo)
1103 : {
1104 0 : glGenBuffers(1, &screenquadvbo);
1105 0 : gle::bindvbo(screenquadvbo);
1106 0 : vec2 verts[4] = { vec2(1, -1), vec2(-1, -1), vec2(1, 1), vec2(-1, 1) };
1107 0 : glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
1108 0 : gle::clearvbo();
1109 : }
1110 0 : }
1111 :
1112 0 : static void cleanupscreenquad()
1113 : {
1114 0 : if(screenquadvbo)
1115 : {
1116 0 : glDeleteBuffers(1, &screenquadvbo);
1117 0 : screenquadvbo = 0;
1118 : }
1119 0 : }
1120 :
1121 0 : void screenquad()
1122 : {
1123 0 : setupscreenquad();
1124 0 : gle::bindvbo(screenquadvbo);
1125 0 : gle::enablevertex();
1126 0 : gle::vertexpointer(sizeof(vec2), nullptr, GL_FLOAT, 2);
1127 0 : glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1128 0 : gle::disablevertex();
1129 0 : gle::clearvbo();
1130 0 : }
1131 :
1132 : //sets screentexcoord0,screentexcoord1 in glsl
1133 0 : static void setscreentexcoord(int i, float w, float h, float x = 0, float y = 0)
1134 : {
1135 : static std::array<LocalShaderParam, 2> screentexcoord =
1136 : {
1137 : LocalShaderParam("screentexcoord0"),
1138 : LocalShaderParam("screentexcoord1")
1139 0 : };
1140 0 : screentexcoord[i].setf(w*0.5f, h*0.5f, x + w*0.5f, y + std::fabs(h)*0.5f);
1141 0 : }
1142 :
1143 0 : void screenquad(float sw, float sh)
1144 : {
1145 0 : setscreentexcoord(0, sw, sh);
1146 0 : screenquad();
1147 0 : }
1148 :
1149 0 : void screenquad(float sw, float sh, float sw2, float sh2)
1150 : {
1151 0 : setscreentexcoord(0, sw, sh);
1152 0 : setscreentexcoord(1, sw2, sh2);
1153 0 : screenquad();
1154 0 : }
1155 :
1156 0 : void screenquadoffset(float x, float y, float w, float h)
1157 : {
1158 0 : setscreentexcoord(0, w, h, x, y);
1159 0 : screenquad();
1160 0 : }
1161 :
1162 : // creates a hud quad for hudquad, debugquad
1163 0 : static void createhudquad(float x1, float y1, float x2, float y2, float sx1, float sy1, float sx2, float sy2) {
1164 0 : gle::defvertex(2);
1165 0 : gle::deftexcoord0();
1166 0 : gle::begin(GL_TRIANGLE_STRIP);
1167 0 : gle::attribf(x2, y1); gle::attribf(sx2, sy1);
1168 0 : gle::attribf(x1, y1); gle::attribf(sx1, sy1);
1169 0 : gle::attribf(x2, y2); gle::attribf(sx2, sy2);
1170 0 : gle::attribf(x1, y2); gle::attribf(sx1, sy2);
1171 0 : gle::end();
1172 0 : }
1173 :
1174 0 : void hudquad(float x, float y, float w, float h, float tx, float ty, float tw, float th)
1175 : {
1176 0 : createhudquad(x, y, x+w, y+h, tx, ty, tx+tw, ty+th);
1177 0 : }
1178 :
1179 0 : void debugquad(float x, float y, float w, float h, float tx, float ty, float tw, float th)
1180 : {
1181 0 : createhudquad(x, y, x+w, y+h, tx, ty+th, tx+tw, ty);
1182 0 : }
1183 :
1184 : //used in iengine
1185 : VARR(fog, 16, 4000, 1000024);
1186 0 : static CVARR(fogcolor, 0x8099B3);
1187 : static VAR(fogoverlay, 0, 1, 1);
1188 :
1189 0 : static float findsurface(int fogmat, const vec &v, int &abovemat)
1190 : {
1191 0 : fogmat &= MatFlag_Volume;
1192 0 : ivec o(v), co;
1193 : int csize;
1194 : do
1195 : {
1196 0 : const cube &c = rootworld.lookupcube(o, 0, co, csize);
1197 0 : int mat = c.material&MatFlag_Volume;
1198 0 : if(mat != fogmat)
1199 : {
1200 0 : abovemat = IS_LIQUID(mat) ? c.material : +Mat_Air;
1201 0 : return o.z;
1202 : }
1203 0 : o.z = co.z + csize;
1204 0 : } while(o.z < rootworld.mapsize());
1205 0 : abovemat = Mat_Air;
1206 0 : return rootworld.mapsize();
1207 : }
1208 :
1209 0 : static void blendfog(int fogmat, float below, float blend, float logblend, float &start, float &end, vec &fogc)
1210 : {
1211 0 : switch(fogmat&MatFlag_Volume)
1212 : {
1213 0 : case Mat_Water:
1214 : {
1215 0 : const bvec &wcol = getwatercolor(fogmat),
1216 0 : &wdeepcol = getwaterdeepcolor(fogmat);
1217 0 : int wfog = getwaterfog(fogmat),
1218 0 : wdeep = getwaterdeep(fogmat);
1219 0 : float deepfade = std::clamp(below/std::max(wdeep, wfog), 0.0f, 1.0f);
1220 0 : vec color;
1221 0 : color.lerp(wcol.tocolor(), wdeepcol.tocolor(), deepfade);
1222 0 : fogc.add(vec(color).mul(blend));
1223 0 : end += logblend*std::min(fog, std::max(wfog*2, 16));
1224 0 : break;
1225 : }
1226 0 : default:
1227 : {
1228 0 : fogc.add(fogcolor.tocolor().mul(blend));
1229 0 : start += logblend*(fog+64)/8;
1230 0 : end += logblend*fog;
1231 0 : break;
1232 : }
1233 : }
1234 0 : }
1235 :
1236 : static vec curfogcolor(0, 0, 0);
1237 :
1238 0 : void setfogcolor(const vec &v)
1239 : {
1240 0 : GLOBALPARAM(fogcolor, v);
1241 0 : }
1242 :
1243 0 : void zerofogcolor()
1244 : {
1245 0 : setfogcolor(vec(0, 0, 0));
1246 0 : }
1247 :
1248 0 : void resetfogcolor()
1249 : {
1250 0 : setfogcolor(curfogcolor);
1251 0 : }
1252 :
1253 : static FVAR(fogintensity, 0, 0.15f, 1);
1254 :
1255 0 : float calcfogdensity(float dist)
1256 : {
1257 0 : return std::log(fogintensity)/(M_LN2*dist);
1258 : }
1259 :
1260 : static FVAR(fogcullintensity, 0, 1e-3f, 1);
1261 :
1262 0 : float calcfogcull()
1263 : {
1264 0 : return std::log(fogcullintensity) / (M_LN2*calcfogdensity(fog - (fog+64)/8));
1265 : }
1266 :
1267 0 : static void setfog(int fogmat, float below = 0, float blend = 1, int abovemat = Mat_Air)
1268 : {
1269 0 : float start = 0,
1270 0 : end = 0,
1271 0 : logscale = 256,
1272 0 : logblend = std::log(1 + (logscale - 1)*blend) / std::log(logscale);
1273 :
1274 0 : curfogcolor = vec(0, 0, 0);
1275 0 : blendfog(fogmat, below, blend, logblend, start, end, curfogcolor);
1276 0 : if(blend < 1)
1277 : {
1278 0 : blendfog(abovemat, 0, 1-blend, 1-logblend, start, end, curfogcolor);
1279 : }
1280 0 : curfogcolor.mul(ldrscale);
1281 0 : GLOBALPARAM(fogcolor, curfogcolor);
1282 0 : float fogdensity = calcfogdensity(end-start);
1283 0 : GLOBALPARAMF(fogdensity, fogdensity, 1/std::exp(M_LN2*start*fogdensity));
1284 0 : }
1285 :
1286 0 : static void blendfogoverlay(int fogmat, float below, float blend, vec &overlay)
1287 : {
1288 0 : switch(fogmat&MatFlag_Volume)
1289 : {
1290 0 : case Mat_Water:
1291 : {
1292 0 : const bvec &wcol = getwatercolor(fogmat),
1293 0 : &wdeepcol = getwaterdeepcolor(fogmat);
1294 0 : const int wfog = getwaterfog(fogmat),
1295 0 : wdeep = getwaterdeep(fogmat);
1296 0 : const float deepfade = std::clamp(below/std::max(wdeep, wfog), 0.0f, 1.0f);
1297 0 : vec color = vec(wcol.r(), wcol.g(), wcol.b()).lerp(vec(wdeepcol.r(), wdeepcol.g(), wdeepcol.b()), deepfade);
1298 0 : overlay.add(color.div(std::min(32.0f + std::max(color.r(), std::max(color.g(), color.b()))*7.0f/8.0f, 255.0f)).max(0.4f).mul(blend));
1299 0 : break;
1300 : }
1301 0 : default:
1302 : {
1303 0 : overlay.add(blend);
1304 0 : break;
1305 : }
1306 : }
1307 0 : }
1308 :
1309 0 : void drawfogoverlay(int fogmat, float fogbelow, float fogblend, int abovemat)
1310 : {
1311 0 : SETSHADER(fogoverlay);
1312 :
1313 0 : glEnable(GL_BLEND);
1314 0 : glBlendFunc(GL_ZERO, GL_SRC_COLOR);
1315 0 : vec overlay(0, 0, 0);
1316 0 : blendfogoverlay(fogmat, fogbelow, fogblend, overlay);
1317 0 : blendfogoverlay(abovemat, 0, 1-fogblend, overlay);
1318 :
1319 0 : gle::color(overlay);
1320 0 : screenquad();
1321 :
1322 0 : glDisable(GL_BLEND);
1323 0 : }
1324 :
1325 : int drawtex = 0;
1326 :
1327 : /* =========================== minimap functionality ======================== */
1328 :
1329 : static GLuint minimaptex = 0;
1330 : vec minimapcenter(0, 0, 0),
1331 : minimapradius(0, 0, 0),
1332 : minimapscale(0, 0, 0);
1333 :
1334 0 : float calcfrustumboundsphere(float nearplane, float farplane, const vec &pos, const vec &view, vec ¢er)
1335 : {
1336 0 : if(drawtex == Draw_TexMinimap)
1337 : {
1338 0 : center = minimapcenter;
1339 0 : return minimapradius.magnitude();
1340 : }
1341 :
1342 0 : float width = std::tan(fov/(2.0f*RAD)),
1343 0 : height = width / aspect,
1344 0 : cdist = ((nearplane + farplane)/2)*(1 + width*width + height*height);
1345 0 : if(cdist <= farplane)
1346 : {
1347 0 : center = vec(view).mul(cdist).add(pos);
1348 0 : return vec(width*nearplane, height*nearplane, cdist-nearplane).magnitude();
1349 : }
1350 : else
1351 : {
1352 0 : center = vec(view).mul(farplane).add(pos);
1353 0 : return vec(width*farplane, height*farplane, 0).magnitude();
1354 : }
1355 : }
1356 :
1357 0 : static void clearminimap()
1358 : {
1359 0 : if(minimaptex)
1360 : {
1361 0 : glDeleteTextures(1, &minimaptex);
1362 0 : minimaptex = 0;
1363 : }
1364 0 : }
1365 :
1366 : static VARR(minimapheight, 0, 0, 2<<16); //height above bottom of map to render at
1367 0 : static CVARR(minimapcolor, 0);
1368 : static VARR(minimapclip, 0, 0, 1);
1369 : static VARP(minimapsize, 7, 10, 12); //2^n size of the minimap texture (along edge)
1370 : static VARP(showminimap, 0, 1, 1);
1371 0 : static CVARP(nominimapcolor, 0x101010); //color for the part of the minimap that isn't the map texture
1372 :
1373 : //used in iengine
1374 0 : void bindminimap()
1375 : {
1376 0 : glBindTexture(GL_TEXTURE_2D, minimaptex);
1377 0 : }
1378 :
1379 0 : static void clipminimap(ivec &bbmin, ivec &bbmax, const std::array<cube, 8> &c, const ivec &co = ivec(0, 0, 0), int size = rootworld.mapsize()>>1)
1380 : {
1381 0 : for(int i = 0; i < 8; ++i)
1382 : {
1383 0 : ivec o(i, co, size);
1384 0 : if(c[i].children)
1385 : {
1386 0 : clipminimap(bbmin, bbmax, *(c[i].children), o, size>>1);
1387 : }
1388 0 : else if(!(c[i].issolid()) && (c[i].material&MatFlag_Clip)!=Mat_Clip)
1389 : {
1390 0 : for(int k = 0; k < 3; ++k)
1391 : {
1392 0 : bbmin[k] = std::min(bbmin[k], o[k]);
1393 : }
1394 0 : for(int k = 0; k < 3; ++k)
1395 : {
1396 0 : bbmax[k] = std::max(bbmax[k], o[k] + size);
1397 : }
1398 : }
1399 : }
1400 0 : }
1401 :
1402 : //used in iengine
1403 0 : void drawminimap(int yaw, int pitch, vec loc, const cubeworld& world, int scalefactor)
1404 : {
1405 0 : if(!showminimap)
1406 : {
1407 0 : if(!minimaptex)
1408 : {
1409 0 : glGenTextures(1, &minimaptex);
1410 : }
1411 : std::array<uchar, 3> v;
1412 0 : v[0] = nominimapcolor.r();
1413 0 : v[1] = nominimapcolor.g();
1414 0 : v[2] = nominimapcolor.b();
1415 0 : createtexture(minimaptex, 1, 1, v.data(), 3, 0, GL_RGB, GL_TEXTURE_2D);
1416 0 : return;
1417 : }
1418 :
1419 0 : glerror();
1420 :
1421 0 : drawtex = Draw_TexMinimap;
1422 :
1423 0 : glerror();
1424 0 : gl_setupframe(true);
1425 :
1426 0 : int size = 1<<minimapsize,
1427 0 : sizelimit = std::min(hwtexsize, std::min(gw, gh));
1428 0 : while(size > sizelimit)
1429 : {
1430 0 : size = size - 128;
1431 : }
1432 0 : if(!minimaptex)
1433 : {
1434 0 : glGenTextures(1, &minimaptex);
1435 : }
1436 0 : ivec bbmin(rootworld.mapsize(), rootworld.mapsize(), rootworld.mapsize()),
1437 0 : bbmax(0, 0, 0);
1438 0 : for(size_t i = 0; i < valist.size(); i++)
1439 : {
1440 0 : const vtxarray *va = valist[i];
1441 0 : for(int k = 0; k < 3; ++k)
1442 : {
1443 0 : if(va->geommin[k]>va->geommax[k])
1444 : {
1445 0 : continue;
1446 : }
1447 0 : bbmin[k] = std::min(bbmin[k], va->geommin[k]);
1448 0 : bbmax[k] = std::max(bbmax[k], va->geommax[k]);
1449 : }
1450 : }
1451 0 : if(minimapclip)
1452 : {
1453 0 : ivec clipmin(rootworld.mapsize(), rootworld.mapsize(), rootworld.mapsize()),
1454 0 : clipmax(0, 0, 0);
1455 0 : clipminimap(clipmin, clipmax, *world.worldroot);
1456 0 : for(int k = 0; k < 2; ++k)
1457 : {
1458 0 : bbmin[k] = std::max(bbmin[k], clipmin[k]);
1459 : }
1460 0 : for(int k = 0; k < 2; ++k)
1461 : {
1462 0 : bbmax[k] = std::min(bbmax[k], clipmax[k]);
1463 : }
1464 : }
1465 :
1466 0 : minimapradius = vec(bbmax).sub(vec(bbmin)).div(scalefactor);
1467 0 : minimapcenter = loc;
1468 0 : minimapradius.x = minimapradius.y = std::max(minimapradius.x, minimapradius.y);
1469 0 : minimapscale = vec((0.5f - 1.0f/size)/minimapradius.x, (0.5f - 1.0f/size)/minimapradius.y, 1.0f);
1470 :
1471 0 : physent *oldcamera = camera1;
1472 0 : physent cmcamera = *player;
1473 0 : cmcamera.reset();
1474 0 : cmcamera.type = physent::PhysEnt_Camera;
1475 0 : cmcamera.o = loc;
1476 0 : cmcamera.yaw = yaw;
1477 0 : cmcamera.pitch = pitch;
1478 0 : cmcamera.roll = 0;
1479 0 : camera1 = &cmcamera;
1480 :
1481 0 : float oldldrscale = ldrscale;
1482 0 : int oldfarplane = farplane,
1483 0 : oldvieww = vieww,
1484 0 : oldviewh = viewh;
1485 0 : farplane = rootworld.mapsize()*2;
1486 0 : vieww = viewh = size;
1487 :
1488 0 : float zscale = std::max(static_cast<float>(minimapheight), minimapcenter.z + minimapradius.z + 1) + 1;
1489 :
1490 0 : projmatrix.ortho(-minimapradius.x, minimapradius.x, -minimapradius.y, minimapradius.y, 0, 2*zscale);
1491 0 : setcamprojmatrix();
1492 :
1493 0 : glEnable(GL_CULL_FACE);
1494 0 : glEnable(GL_DEPTH_TEST);
1495 :
1496 0 : xtravertsva = xtraverts = glde = gbatches = vtris = vverts = 0;
1497 0 : occlusionengine.flipqueries();
1498 :
1499 0 : ldrscale = 1;
1500 :
1501 0 : view.visiblecubes(false);
1502 0 : gbuf.rendergbuffer();
1503 0 : gbuf.rendershadowatlas();
1504 :
1505 0 : gbuf.shademinimap(minimapcolor.tocolor().mul(ldrscale));
1506 :
1507 0 : if(minimapheight > 0 && minimapheight < minimapcenter.z + minimapradius.z)
1508 : {
1509 0 : camera1->o.z = minimapcenter.z + minimapradius.z + 1;
1510 0 : projmatrix.ortho(-minimapradius.x, minimapradius.x, -minimapradius.y, minimapradius.y, -zscale, zscale);
1511 0 : setcamprojmatrix();
1512 0 : gbuf.rendergbuffer(false);
1513 0 : gbuf.shademinimap();
1514 : }
1515 :
1516 0 : glDisable(GL_DEPTH_TEST);
1517 0 : glDisable(GL_CULL_FACE);
1518 :
1519 0 : farplane = oldfarplane;
1520 0 : vieww = oldvieww;
1521 0 : viewh = oldviewh;
1522 0 : ldrscale = oldldrscale;
1523 :
1524 0 : camera1 = oldcamera;
1525 0 : drawtex = 0;
1526 :
1527 0 : createtexture(minimaptex, size, size, nullptr, 3, 1, GL_RGB5, GL_TEXTURE_2D);
1528 0 : glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
1529 0 : glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
1530 0 : GLfloat border[4] = { minimapcolor.x/255.0f, minimapcolor.y/255.0f, minimapcolor.z/255.0f, 1.0f };
1531 0 : glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
1532 0 : glBindTexture(GL_TEXTURE_2D, 0);
1533 :
1534 0 : GLuint fbo = 0;
1535 0 : glGenFramebuffers(1, &fbo);
1536 0 : glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1537 0 : glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, minimaptex, 0);
1538 0 : copyhdr(size, size, fbo);
1539 0 : glBindFramebuffer(GL_FRAMEBUFFER, 0);
1540 0 : glDeleteFramebuffers(1, &fbo);
1541 :
1542 0 : glViewport(0, 0, hudw(), hudh());
1543 : }
1544 :
1545 : static VAR(modelpreviewfov, 10, 20, 100); //y axis field of view
1546 : static VAR(modelpreviewpitch, -90, -15, 90); //pitch above model to render
1547 :
1548 : /* ======================== model preview windows =========================== */
1549 :
1550 :
1551 0 : void ModelPreview::start(int xcoord, int ycoord, int width, int height, bool bg, bool usescissor)
1552 : {
1553 0 : x = xcoord;
1554 0 : y = ycoord;
1555 0 : w = width;
1556 0 : h = height;
1557 0 : background = bg;
1558 0 : scissor = usescissor;
1559 :
1560 0 : gbuf.setupgbuffer();
1561 :
1562 0 : useshaderbyname("modelpreview");
1563 :
1564 0 : drawtex = Draw_TexModelPreview;
1565 :
1566 0 : oldcamera = camera1;
1567 0 : camera = *camera1;
1568 0 : camera.reset();
1569 0 : camera.type = physent::PhysEnt_Camera;
1570 0 : camera.o = vec(0, 0, 0);
1571 0 : camera.yaw = 0;
1572 0 : camera.pitch = modelpreviewpitch;
1573 0 : camera.roll = 0;
1574 0 : camera1 = &camera;
1575 :
1576 0 : oldaspect = aspect;
1577 0 : oldfovy = fovy;
1578 0 : oldfov = curfov;
1579 0 : oldldrscale = ldrscale;
1580 0 : oldfarplane = farplane;
1581 0 : oldvieww = vieww;
1582 0 : oldviewh = viewh;
1583 0 : oldprojmatrix = projmatrix;
1584 :
1585 0 : aspect = w/static_cast<float>(h);
1586 0 : fovy = modelpreviewfov;
1587 0 : curfov = 2*std::atan2(std::tan(fovy/(2*RAD)), 1/aspect)*RAD;
1588 0 : farplane = 1024;
1589 0 : vieww = std::min(gw, w);
1590 0 : viewh = std::min(gh, h);
1591 0 : ldrscale = 1;
1592 :
1593 0 : projmatrix.perspective(fovy, aspect, nearplane, farplane);
1594 0 : setcamprojmatrix();
1595 :
1596 0 : glEnable(GL_CULL_FACE);
1597 0 : glEnable(GL_DEPTH_TEST);
1598 0 : }
1599 :
1600 0 : void ModelPreview::end()
1601 : {
1602 0 : gbuf.rendermodelbatches();
1603 :
1604 0 : glDisable(GL_DEPTH_TEST);
1605 0 : glDisable(GL_CULL_FACE);
1606 :
1607 0 : gbuf.shademodelpreview(x, y, w, h, background, scissor);
1608 :
1609 0 : aspect = oldaspect;
1610 0 : fovy = oldfovy;
1611 0 : curfov = oldfov;
1612 0 : farplane = oldfarplane;
1613 0 : vieww = oldvieww;
1614 0 : viewh = oldviewh;
1615 0 : ldrscale = oldldrscale;
1616 :
1617 0 : camera1 = oldcamera;
1618 0 : drawtex = 0;
1619 :
1620 0 : projmatrix = oldprojmatrix;
1621 0 : setcamprojmatrix();
1622 0 : }
1623 :
1624 0 : vec calcmodelpreviewpos(const vec &radius, float &yaw)
1625 : {
1626 0 : yaw = std::fmod(lastmillis/10000.0f*360.0f, 360.0f);
1627 0 : float dist = std::max(radius.magnitude2()/aspect, radius.magnitude())/std::sin(fovy/(2*RAD));
1628 0 : return vec(0, dist, 0).rotate_around_x(camera1->pitch/RAD);
1629 : }
1630 :
1631 : int xtraverts, xtravertsva;
1632 :
1633 : /* ============================= core rendering ============================= */
1634 :
1635 : //main scene rendering function
1636 0 : void gl_drawview(void (*gamefxn)(), void(*hudfxn)(), void(*editfxn)())
1637 : {
1638 0 : GLuint scalefbo = gbuf.shouldscale();
1639 0 : if(scalefbo)
1640 : {
1641 0 : vieww = gw;
1642 0 : viewh = gh;
1643 : }
1644 0 : float fogmargin = 1 + wateramplitude + nearplane;
1645 0 : int fogmat = rootworld.lookupmaterial(vec(camera1->o.x, camera1->o.y, camera1->o.z - fogmargin))&(MatFlag_Volume|MatFlag_Index),
1646 0 : abovemat = Mat_Air;
1647 0 : float fogbelow = 0;
1648 0 : if(IS_LIQUID(fogmat&MatFlag_Volume)) //if in the water
1649 : {
1650 0 : float z = findsurface(fogmat, vec(camera1->o.x, camera1->o.y, camera1->o.z - fogmargin), abovemat) - wateroffset;
1651 0 : if(camera1->o.z < z + fogmargin)
1652 : {
1653 0 : fogbelow = z - camera1->o.z;
1654 : }
1655 : else
1656 : {
1657 0 : fogmat = abovemat;
1658 : }
1659 : }
1660 : else
1661 : {
1662 0 : fogmat = Mat_Air; //use air fog
1663 : }
1664 0 : setfog(abovemat);
1665 : //setfog(fogmat, fogbelow, 1, abovemat);
1666 :
1667 0 : farplane = rootworld.mapsize()*2;
1668 : //set the camera location
1669 0 : projmatrix.perspective(fovy, aspect, nearplane, farplane);
1670 0 : setcamprojmatrix();
1671 :
1672 0 : glEnable(GL_CULL_FACE);
1673 0 : glEnable(GL_DEPTH_TEST);
1674 :
1675 0 : ldrscale = 0.5f;
1676 : //do occlusion culling
1677 0 : view.visiblecubes();
1678 : //set to wireframe if applicable
1679 0 : if(wireframe && editmode)
1680 : {
1681 0 : glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1682 : }
1683 : //construct g-buffer (build basic scene)
1684 0 : gbuf.rendergbuffer(true, gamefxn);
1685 0 : if(wireframe && editmode) //done with wireframe mode now
1686 : {
1687 0 : glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1688 : }
1689 0 : else if(limitsky() && editmode)
1690 : {
1691 0 : renderexplicitsky(true);
1692 : }
1693 :
1694 : //ambient obscurance (ambient occlusion) on geometry & models only
1695 0 : gbuf.renderao();
1696 0 : glerror();
1697 :
1698 : // render avatar after AO to avoid weird contact shadows
1699 0 : renderavatar(hudfxn);
1700 0 : glerror();
1701 :
1702 : // render grass after AO to avoid disturbing shimmering patterns
1703 0 : generategrass();
1704 0 : rendergrass();
1705 0 : glerror();
1706 :
1707 0 : glFlush();
1708 : //global illumination
1709 0 : gbuf.renderradiancehints();
1710 0 : glerror();
1711 : //lighting
1712 0 : gbuf.rendershadowatlas();
1713 0 : glerror();
1714 : //shading
1715 0 : shadegbuffer();
1716 0 : glerror();
1717 :
1718 : //fog
1719 0 : if(fogmat)
1720 : {
1721 0 : setfog(fogmat, fogbelow, 1, abovemat);
1722 :
1723 0 : gbuf.renderwaterfog(fogmat, fogbelow);
1724 :
1725 0 : setfog(fogmat, fogbelow, std::clamp(fogbelow, 0.0f, 1.0f), abovemat);
1726 : }
1727 :
1728 : //alpha
1729 0 : gbuf.rendertransparent();
1730 0 : glerror();
1731 :
1732 0 : if(fogmat)
1733 : {
1734 0 : setfog(fogmat, fogbelow, 1, abovemat);
1735 : }
1736 :
1737 : //volumetric lights
1738 0 : gbuf.rendervolumetric();
1739 0 : glerror();
1740 :
1741 0 : if(editmode)
1742 : {
1743 0 : if(!wireframe && outline)
1744 : {
1745 0 : renderoutline(); //edit mode geometry outline
1746 : }
1747 0 : glerror();
1748 0 : rendereditmaterials();
1749 0 : glerror();
1750 0 : gbuf.renderparticles();
1751 0 : glerror();
1752 0 : if(showhud)
1753 : {
1754 0 : glDepthMask(GL_FALSE);
1755 0 : editfxn(); //edit cursor, passed as pointer
1756 0 : glDepthMask(GL_TRUE);
1757 : }
1758 : }
1759 :
1760 : //we're done with depth/geometry stuff so we don't need this functionality
1761 0 : glDisable(GL_CULL_FACE);
1762 0 : glDisable(GL_DEPTH_TEST);
1763 :
1764 0 : if(fogoverlay && fogmat != Mat_Air)
1765 : {
1766 0 : drawfogoverlay(fogmat, fogbelow, std::clamp(fogbelow, 0.0f, 1.0f), abovemat);
1767 : }
1768 : //antialiasing
1769 0 : doaa(setuppostfx(gbuf, vieww, viewh, scalefbo), gbuf);
1770 : //postfx
1771 0 : renderpostfx(scalefbo);
1772 0 : if(scalefbo)
1773 : {
1774 0 : gbuf.doscale();
1775 : }
1776 0 : }
1777 :
1778 0 : int renderw()
1779 : {
1780 0 : return std::min(scr_w, screenw);
1781 : }
1782 :
1783 0 : int renderh()
1784 : {
1785 0 : return std::min(scr_h, screenh);
1786 : }
1787 :
1788 3 : int hudw()
1789 : {
1790 3 : return screenw;
1791 : }
1792 :
1793 2 : int hudh()
1794 : {
1795 2 : return screenh;
1796 : }
1797 :
1798 0 : void gl_setupframe(bool force)
1799 : {
1800 0 : if(!force)
1801 : {
1802 0 : return;
1803 : }
1804 0 : setuplights(gbuf);
1805 : }
1806 :
1807 0 : void gl_drawframe(int crosshairindex, void (*gamefxn)(), void (*hudfxn)(), void (*editfxn)(), void (*hud2d)())
1808 : {
1809 0 : synctimers();
1810 0 : xtravertsva = xtraverts = glde = gbatches = vtris = vverts = 0;
1811 0 : occlusionengine.flipqueries();
1812 0 : aspect = forceaspect ? forceaspect : hudw()/static_cast<float>(hudh());
1813 0 : fovy = 2*std::atan2(std::tan(curfov/(2*RAD)), aspect)*RAD;
1814 0 : vieww = hudw();
1815 0 : viewh = hudh();
1816 0 : if(mainmenu)
1817 : {
1818 0 : renderbackground(nullptr, nullptr, nullptr, nullptr, true);
1819 : }
1820 : else
1821 : {
1822 0 : gl_drawview(gamefxn, hudfxn, editfxn);
1823 : }
1824 0 : UI::render();
1825 0 : gl_drawhud(crosshairindex, hud2d);
1826 0 : }
1827 :
1828 0 : void cleanupgl()
1829 : {
1830 0 : clearminimap();
1831 0 : cleanuptimers();
1832 0 : cleanupscreenquad();
1833 0 : gle::cleanup();
1834 0 : }
1835 :
1836 1 : void initrenderglcmds()
1837 : {
1838 1 : addcommand("glext", reinterpret_cast<identfun>(glext), "s", Id_Command);
1839 2 : addcommand("getcamyaw", reinterpret_cast<identfun>(+[](){floatret(camera1 ? camera1->yaw : 0);}), "", Id_Command);
1840 2 : addcommand("getcampitch", reinterpret_cast<identfun>(+[](){floatret(camera1 ? camera1->pitch : 0);}), "", Id_Command);
1841 2 : addcommand("getcamroll", reinterpret_cast<identfun>(+[](){floatret(camera1 ? camera1->roll : 0);}), "", Id_Command);
1842 1 : addcommand("getcampos", reinterpret_cast<identfun>(+[]()
1843 : {
1844 1 : if(!camera1)
1845 : {
1846 1 : result("no camera");
1847 : }
1848 : else
1849 : {
1850 0 : std::string pos = std::format("{} {} {}",floatstr(camera1->o.x), floatstr(camera1->o.y), floatstr(camera1->o.z));
1851 0 : result(pos.c_str());
1852 0 : }
1853 2 : }), "", Id_Command);
1854 1 : }
|