1 /**
2 * @file
3 */
4
5 /*
6 Copyright (C) 1997-2001 Id Software, Inc.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 */
24
25 #include "r_local.h"
26 #include "r_program.h"
27 #include "r_error.h"
28
29 /* useful for particles, pics, etc.. */
30 const vec2_t default_texcoords[4] = {
31 {0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}
32 };
33
34 /* Hack to keep track of active rendering program (have to reinit binings¶meters once it changed)*/
35 static r_program_t* lastProgram = nullptr;
36
37 /**
38 * @brief Returns false if the texunit is not supported
39 */
R_SelectTexture(gltexunit_t * texunit)40 bool R_SelectTexture (gltexunit_t* texunit)
41 {
42 if (texunit == r_state.active_texunit)
43 return true;
44
45 /* not supported */
46 if (texunit->texture >= r_config.maxTextureCoords + GL_TEXTURE0)
47 return false;
48
49 r_state.active_texunit = texunit;
50
51 qglActiveTexture(texunit->texture);
52 qglClientActiveTexture(texunit->texture);
53 return true;
54 }
55
R_BindTexture_(int texnum)56 static void R_BindTexture_ (int texnum)
57 {
58 if (texnum == r_state.active_texunit->texnum)
59 return;
60
61 assert(texnum > 0);
62
63 r_state.active_texunit->texnum = texnum;
64
65 glBindTexture(GL_TEXTURE_2D, texnum);
66 R_CheckError();
67 }
68
R_BindTextureDebug(int texnum,const char * file,int line,const char * function)69 void R_BindTextureDebug (int texnum, const char* file, int line, const char* function)
70 {
71 if (texnum <= 0) {
72 Com_Printf("Bad texnum (%d) in: %s (%d): %s\n", texnum, file, line, function);
73 }
74 R_BindTexture_(texnum);
75 }
76
R_BindTextureForTexUnit(GLuint texnum,gltexunit_t * texunit)77 void R_BindTextureForTexUnit (GLuint texnum, gltexunit_t* texunit)
78 {
79 /* small optimization to save state changes */
80 if (texnum == texunit->texnum)
81 return;
82
83 R_SelectTexture(texunit);
84
85 R_BindTexture(texnum);
86
87 R_SelectTexture(&texunit_diffuse);
88 }
89
R_BindLightmapTexture(GLuint texnum)90 void R_BindLightmapTexture (GLuint texnum)
91 {
92 R_BindTextureForTexUnit(texnum, &texunit_lightmap);
93 }
94
R_BindDeluxemapTexture(GLuint texnum)95 void R_BindDeluxemapTexture (GLuint texnum)
96 {
97 R_BindTextureForTexUnit(texnum, &texunit_deluxemap);
98 }
99
R_BindNormalmapTexture(GLuint texnum)100 void R_BindNormalmapTexture (GLuint texnum)
101 {
102 R_BindTextureForTexUnit(texnum, &texunit_normalmap);
103 }
104
R_UseMaterial(const material_t * material)105 void R_UseMaterial (const material_t* material)
106 {
107 static float last_b, last_p, last_s, last_h;
108 float b;
109
110 if (r_state.active_material == material)
111 return;
112
113 if (!r_state.active_normalmap)
114 return;
115
116 if (material)
117 r_state.active_material = material;
118 else
119 r_state.active_material = &defaultMaterial;
120
121 b = r_state.active_material->bump * r_bumpmap->value;
122 if (b != last_b)
123 R_ProgramParameter1f("BUMP", b);
124 last_b = b;
125
126 if (r_state.active_program != r_state.world_program || r_programs->integer > 1) {
127 const float p = r_state.active_material->parallax * r_parallax->value;
128 if (p != last_p)
129 R_ProgramParameter1f("PARALLAX", p);
130 last_p = p;
131
132 const float h = r_state.active_material->hardness * r_hardness->value;
133 if (h != last_h)
134 R_ProgramParameter1f("HARDNESS", h);
135 last_h = h;
136
137 const float s = r_state.active_material->specular * r_specular->value;
138 if (s != last_s)
139 R_ProgramParameter1f("SPECULAR", s);
140 last_s = s;
141 } else {
142 last_p = -1;
143 last_h = -1;
144 last_s = -1;
145 }
146 }
147
R_BindArray(GLenum target,GLenum type,const void * array)148 void R_BindArray (GLenum target, GLenum type, const void* array)
149 {
150 const int v = static_cast<int>(target);
151 switch (v) {
152 case GL_VERTEX_ARRAY:
153 glVertexPointer(COMPONENTS_VERTEX_ARRAY3D, type, 0, array);
154 break;
155 case GL_TEXTURE_COORD_ARRAY:
156 glTexCoordPointer(COMPONENTS_TEXCOORD_ARRAY, type, 0, array);
157 break;
158 case GL_COLOR_ARRAY:
159 glColorPointer(COMPONENTS_COLOR_ARRAY, type, 0, array);
160 break;
161 case GL_NORMAL_ARRAY:
162 glNormalPointer(type, 0, array);
163 break;
164 case GL_TANGENT_ARRAY:
165 R_AttributePointer("TANGENTS", COMPONENTS_TANGENT_ARRAY, array);
166 break;
167 case GL_NEXT_VERTEX_ARRAY:
168 R_AttributePointer("NEXT_FRAME_VERTS", COMPONENTS_VERTEX_ARRAY3D, array);
169 break;
170 case GL_NEXT_NORMAL_ARRAY:
171 R_AttributePointer("NEXT_FRAME_NORMALS", COMPONENTS_NORMAL_ARRAY, array);
172 break;
173 case GL_NEXT_TANGENT_ARRAY:
174 R_AttributePointer("NEXT_FRAME_TANGENTS", COMPONENTS_TANGENT_ARRAY, array);
175 break;
176 }
177 }
178
179 /**
180 * @brief Binds the appropriate shared vertex array to the specified target.
181 */
R_BindDefaultArray(GLenum target)182 void R_BindDefaultArray (GLenum target)
183 {
184 const int v = static_cast<int>(target);
185 switch (v) {
186 case GL_VERTEX_ARRAY:
187 R_BindArray(target, GL_FLOAT, r_state.vertex_array_3d);
188 break;
189 case GL_TEXTURE_COORD_ARRAY:
190 R_BindArray(target, GL_FLOAT, r_state.active_texunit->texcoord_array);
191 break;
192 case GL_COLOR_ARRAY:
193 R_BindArray(target, GL_FLOAT, r_state.color_array);
194 break;
195 case GL_NORMAL_ARRAY:
196 R_BindArray(target, GL_FLOAT, r_state.normal_array);
197 break;
198 case GL_TANGENT_ARRAY:
199 R_BindArray(target, GL_FLOAT, r_state.tangent_array);
200 break;
201 case GL_NEXT_VERTEX_ARRAY:
202 R_BindArray(target, GL_FLOAT, r_state.next_vertex_array_3d);
203 break;
204 case GL_NEXT_NORMAL_ARRAY:
205 R_BindArray(target, GL_FLOAT, r_state.next_normal_array);
206 break;
207 case GL_NEXT_TANGENT_ARRAY:
208 R_BindArray(target, GL_FLOAT, r_state.next_tangent_array);
209 break;
210 }
211 }
212
R_BindBuffer(GLenum target,GLenum type,GLuint id)213 void R_BindBuffer (GLenum target, GLenum type, GLuint id)
214 {
215 if (!qglBindBuffer)
216 return;
217
218 if (!r_vertexbuffers->integer)
219 return;
220
221 if (target == GL_ELEMENT_ARRAY_BUFFER) {
222 qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id);
223 return; /* No need to call gl***Poiner -- this binding is special and automates that */
224 } else {
225 qglBindBuffer(GL_ARRAY_BUFFER, id);
226 }
227
228 if (type && id) /* assign the array pointer as well */
229 R_BindArray(target, type, nullptr);
230 }
231
R_BlendFunc(GLenum src,GLenum dest)232 void R_BlendFunc (GLenum src, GLenum dest)
233 {
234 if (r_state.blend_src == src && r_state.blend_dest == dest)
235 return;
236
237 r_state.blend_src = src;
238 r_state.blend_dest = dest;
239
240 glBlendFunc(src, dest);
241 }
242
R_EnableMultisample(bool enable)243 void R_EnableMultisample (bool enable)
244 {
245 if (r_multisample->integer == 0 && enable)
246 return;
247
248 if (r_state.multisample_enabled == enable)
249 return;
250
251 r_state.multisample_enabled = enable;
252
253 if (enable) {
254 glEnable(GL_MULTISAMPLE);
255 } else {
256 glDisable(GL_MULTISAMPLE);
257 }
258 R_CheckError();
259 }
260
R_EnableBlend(bool enable)261 void R_EnableBlend (bool enable)
262 {
263 if (r_state.blend_enabled == enable)
264 return;
265
266 r_state.blend_enabled = enable;
267
268 if (enable) {
269 glEnable(GL_BLEND);
270 glDepthMask(GL_FALSE);
271 } else {
272 glDisable(GL_BLEND);
273 glDepthMask(GL_TRUE);
274 }
275 }
276
R_EnableAlphaTest(bool enable)277 void R_EnableAlphaTest (bool enable)
278 {
279 if (r_state.alpha_test_enabled == enable)
280 return;
281
282 r_state.alpha_test_enabled = enable;
283
284 if (enable)
285 glEnable(GL_ALPHA_TEST);
286 else
287 glDisable(GL_ALPHA_TEST);
288 }
289
R_EnableStencilTest(bool enable)290 void R_EnableStencilTest (bool enable)
291 {
292 if (r_state.stencil_test_enabled == enable)
293 return;
294
295 r_state.stencil_test_enabled = enable;
296
297 if (enable)
298 glEnable(GL_STENCIL_TEST);
299 else
300 glDisable(GL_STENCIL_TEST);
301 }
302
R_EnableTexture(gltexunit_t * texunit,bool enable)303 void R_EnableTexture (gltexunit_t* texunit, bool enable)
304 {
305 if (enable == texunit->enabled)
306 return;
307
308 texunit->enabled = enable;
309
310 R_SelectTexture(texunit);
311
312 if (enable) {
313 /* activate texture unit */
314 glEnable(GL_TEXTURE_2D);
315
316 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
317
318 if (texunit == &texunit_lightmap) {
319 if (r_lightmap->integer)
320 R_TexEnv(GL_REPLACE);
321 else
322 R_TexEnv(GL_MODULATE);
323 }
324 } else {
325 /* disable on the second texture unit */
326 glDisable(GL_TEXTURE_2D);
327 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
328 }
329 R_SelectTexture(&texunit_diffuse);
330 }
331
R_EnableColorArray(bool enable)332 void R_EnableColorArray (bool enable)
333 {
334 if (r_state.color_array_enabled == enable)
335 return;
336
337 r_state.color_array_enabled = enable;
338
339 if (enable)
340 glEnableClientState(GL_COLOR_ARRAY);
341 else
342 glDisableClientState(GL_COLOR_ARRAY);
343 }
344
345 /**
346 * @brief Enables hardware-accelerated lighting with the specified program. This
347 * should be called after any texture units which will be active for lighting
348 * have been enabled.
349 */
R_EnableLighting(r_program_t * program,bool enable)350 bool R_EnableLighting (r_program_t* program, bool enable)
351 {
352 if (!r_programs->integer)
353 return r_state.lighting_enabled;
354
355 if (enable && (!program || !program->id))
356 return r_state.lighting_enabled;
357
358 if (r_state.lighting_enabled == enable && r_state.active_program == program)
359 return r_state.lighting_enabled;
360
361 r_state.lighting_enabled = enable;
362
363 if (enable) { /* toggle state */
364 R_UseProgram(program);
365
366 glEnableClientState(GL_NORMAL_ARRAY);
367 } else {
368 glDisableClientState(GL_NORMAL_ARRAY);
369
370 R_UseProgram(nullptr);
371 }
372
373 return r_state.lighting_enabled;
374 }
375
R_SetupSpotLight(int index,const light_t * light)376 void R_SetupSpotLight (int index, const light_t* light)
377 {
378 const vec4_t blackColor = {0.0, 0.0, 0.0, 1.0};
379 vec4_t position;
380
381 if (!r_programs->integer || !r_dynamic_lights->integer)
382 return;
383
384 if (index < 0 || index >= r_dynamic_lights->integer)
385 return;
386
387 index += GL_LIGHT0;
388
389 glEnable(index);
390 glLightf(index, GL_CONSTANT_ATTENUATION, MIN_GL_CONSTANT_ATTENUATION);
391 glLightf(index, GL_LINEAR_ATTENUATION, 0);
392 glLightf(index, GL_QUADRATIC_ATTENUATION, 16.0 / (light->radius * light->radius));
393
394 VectorCopy(light->origin, position);
395 position[3] = 1.0; /* spot light */
396
397 glLightfv(index, GL_POSITION, position);
398 glLightfv(index, GL_AMBIENT, blackColor);
399 glLightfv(index, GL_DIFFUSE, light->color);
400 glLightfv(index, GL_SPECULAR, blackColor);
401 }
402
R_DisableSpotLight(int index)403 void R_DisableSpotLight (int index)
404 {
405 const vec4_t blackColor = {0.0, 0.0, 0.0, 1.0};
406
407 if (!r_programs->integer || !r_dynamic_lights)
408 return;
409
410 if (index < 0 || index >= MAX_GL_LIGHTS - 1)
411 return;
412
413 index += GL_LIGHT0;
414
415 glDisable(index);
416 glLightf(index, GL_CONSTANT_ATTENUATION, MIN_GL_CONSTANT_ATTENUATION);
417 glLightf(index, GL_LINEAR_ATTENUATION, 0.0);
418 glLightf(index, GL_QUADRATIC_ATTENUATION, 0.0);
419 glLightfv(index, GL_AMBIENT, blackColor);
420 glLightfv(index, GL_DIFFUSE, blackColor);
421 glLightfv(index, GL_SPECULAR, blackColor);
422 }
423
424 /**
425 * @brief Enables animation using keyframe interpolation on the GPU
426 * @param mesh The mesh to animate
427 * @param backlerp The temporal proximity to the previous keyframe (in the range 0.0 to 1.0)
428 * @param enable Whether to turn animation on or off
429 */
R_EnableAnimation(const mAliasMesh_t * mesh,float backlerp,bool enable)430 void R_EnableAnimation (const mAliasMesh_t* mesh, float backlerp, bool enable)
431 {
432 if (!r_programs->integer || !r_state.lighting_enabled)
433 return;
434
435 r_state.animation_enabled = enable;
436
437 if (enable) {
438 R_EnableAttribute("NEXT_FRAME_VERTS");
439 R_EnableAttribute("NEXT_FRAME_NORMALS");
440 R_EnableAttribute("NEXT_FRAME_TANGENTS");
441 R_ProgramParameter1i("ANIMATE", 1);
442
443 R_ProgramParameter1f("TIME", backlerp);
444
445 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, mesh->texcoords);
446 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, mesh->verts);
447 R_BindArray(GL_NORMAL_ARRAY, GL_FLOAT, mesh->normals);
448 R_BindArray(GL_TANGENT_ARRAY, GL_FLOAT, mesh->tangents);
449 R_BindArray(GL_NEXT_VERTEX_ARRAY, GL_FLOAT, mesh->next_verts);
450 R_BindArray(GL_NEXT_NORMAL_ARRAY, GL_FLOAT, mesh->next_normals);
451 R_BindArray(GL_NEXT_TANGENT_ARRAY, GL_FLOAT, mesh->next_tangents);
452 } else {
453 R_DisableAttribute("NEXT_FRAME_VERTS");
454 R_DisableAttribute("NEXT_FRAME_NORMALS");
455 R_DisableAttribute("NEXT_FRAME_TANGENTS");
456 R_ProgramParameter1i("ANIMATE", 0);
457 }
458 }
459
460 /**
461 * @brief Enables bumpmapping and binds the given normalmap.
462 * @note Don't forget to bind the deluxe map, too.
463 * @sa R_BindDeluxemapTexture
464 */
R_EnableBumpmap(const image_t * normalmap)465 void R_EnableBumpmap (const image_t* normalmap)
466 {
467 if (!r_state.lighting_enabled)
468 return;
469
470 if (!r_bumpmap->value)
471 return;
472
473 if (r_state.active_normalmap == normalmap)
474 return;
475
476 if (!normalmap) {
477 /* disable bump mapping */
478 R_ProgramParameter1i("BUMPMAP", 0);
479
480 r_state.active_normalmap = normalmap;
481 return;
482 }
483
484 if (!r_state.active_normalmap) {
485 /* enable bump mapping */
486 R_ProgramParameter1i("BUMPMAP", 1);
487 /* default material to use if no material gets loaded */
488 R_UseMaterial(&defaultMaterial);
489 }
490
491 R_BindNormalmapTexture(normalmap->texnum);
492
493 r_state.active_normalmap = normalmap;
494 }
495
R_EnableWarp(r_program_t * program,bool enable)496 void R_EnableWarp (r_program_t* program, bool enable)
497 {
498 if (!r_programs->integer)
499 return;
500
501 if (enable && (!program || !program->id))
502 return;
503
504 if (!r_warp->integer || r_state.warp_enabled == enable)
505 return;
506
507 r_state.warp_enabled = enable;
508
509 R_SelectTexture(&texunit_lightmap);
510
511 if (enable) {
512 glEnable(GL_TEXTURE_2D);
513 R_BindTexture(r_warpTexture->texnum);
514 R_UseProgram(program);
515 } else {
516 glDisable(GL_TEXTURE_2D);
517 R_UseProgram(nullptr);
518 }
519
520 R_SelectTexture(&texunit_diffuse);
521 }
522
R_EnableBlur(r_program_t * program,bool enable,r_framebuffer_t * source,r_framebuffer_t * dest,int dir)523 void R_EnableBlur (r_program_t* program, bool enable, r_framebuffer_t* source, r_framebuffer_t* dest, int dir)
524 {
525 if (!r_programs->integer)
526 return;
527
528 if (enable && (!program || !program->id))
529 return;
530
531 if (!r_postprocess->integer || r_state.blur_enabled == enable)
532 return;
533
534 r_state.blur_enabled = enable;
535
536 R_SelectTexture(&texunit_lightmap);
537
538 if (enable) {
539 float userdata[] = { static_cast<float>(source->width), static_cast<float>(dir) };
540 R_UseFramebuffer(dest);
541 program->userdata = userdata;
542 R_UseProgram(program);
543 } else {
544 R_UseFramebuffer(fbo_screen);
545 R_UseProgram(nullptr);
546 }
547
548 R_SelectTexture(&texunit_diffuse);
549 }
550
R_EnableShell(bool enable)551 void R_EnableShell (bool enable)
552 {
553 if (enable == r_state.shell_enabled)
554 return;
555
556 r_state.shell_enabled = enable;
557
558 if (enable) {
559 glEnable(GL_POLYGON_OFFSET_FILL);
560 glPolygonOffset(-1.0, 1.0);
561
562 R_EnableDrawAsGlow(true);
563 R_EnableBlend(true);
564 R_BlendFunc(GL_SRC_ALPHA, GL_ONE);
565
566 if (r_state.lighting_enabled)
567 R_ProgramParameter1f("OFFSET", refdef.time / 3.0);
568 } else {
569 R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
570 R_EnableBlend(false);
571 R_EnableDrawAsGlow(false);
572
573 glPolygonOffset(0.0, 0.0);
574 glDisable(GL_POLYGON_OFFSET_FILL);
575
576 if (r_state.lighting_enabled)
577 R_ProgramParameter1f("OFFSET", 0.0);
578 }
579 }
580
581 #define FOG_START 300.0
582 #define FOG_END 2500.0
583
584 vec2_t fogRange = {FOG_START, FOG_END};
585
R_EnableFog(bool enable)586 void R_EnableFog (bool enable)
587 {
588 if (!r_fog->integer || r_state.fog_enabled == enable)
589 return;
590
591 r_state.fog_enabled = false;
592
593 /* This is ugly. Shaders could be enabled or disabled between this and rendering call, so we have to setup both FFP and GLSL */
594 if (enable) {
595 if (((refdef.weather & WEATHER_FOG) && r_fog->integer) || r_fog->integer == 2) {
596 r_state.fog_enabled = true;
597
598 glFogfv(GL_FOG_COLOR, refdef.fogColor);
599 glFogf(GL_FOG_DENSITY, refdef.fogColor[3]);
600 glEnable(GL_FOG);
601
602 if (r_programs->integer && (r_state.active_program == r_state.world_program || r_state.active_program == r_state.model_program || r_state.active_program == r_state.warp_program)) {
603 R_ProgramParameter3fv("FOGCOLOR", refdef.fogColor);
604 R_ProgramParameter1f("FOGDENSITY", refdef.fogColor[3]);
605 R_ProgramParameter2fv("FOGRANGE", fogRange);
606 }
607 }
608 } else {
609 glFogf(GL_FOG_DENSITY, 0.0);
610 glDisable(GL_FOG);
611 if (r_programs->integer && (r_state.active_program == r_state.world_program || r_state.active_program == r_state.model_program || r_state.active_program == r_state.warp_program))
612 R_ProgramParameter1f("FOGDENSITY", 0.0f);
613 }
614 }
615
R_UpdateGlowBufferBinding(void)616 static void R_UpdateGlowBufferBinding (void)
617 {
618 #ifndef GL_VERSION_ES_CM_1_0
619 static GLenum glowRenderTarget = GL_COLOR_ATTACHMENT1_EXT; // Not supported in GLES
620
621 if (r_state.active_program) {
622 /* got an active shader */
623 if (r_state.draw_glow_enabled) {
624 /* switch the draw buffer to the glow buffer */
625 R_BindColorAttachments(1, &glowRenderTarget);
626 R_ProgramParameter1f("GLOWSCALE", 0.0); /* Plumbing to avoid state leak */
627 } else {
628 /* switch back to the draw buffer we are currently rendering into ... */
629 R_DrawBuffers(2);
630 /* ... and enable glow, if there is a glow map */
631 if (r_state.glowmap_enabled) {
632 R_ProgramParameter1f("GLOWSCALE", 1.0);
633 } else {
634 R_ProgramParameter1f("GLOWSCALE", 0.0);
635 }
636 }
637 } else {
638 /* no shader */
639 if (r_state.draw_glow_enabled) {
640 /* switch the draw buffer to the glow buffer */
641 R_BindColorAttachments(1, &glowRenderTarget);
642 } else {
643 if (!r_state.glowmap_enabled) {
644 /* Awkward case. Postprocessing is requested, but fragment
645 * shader is disabled, and we are supposed to draw non-glowing object.
646 * So we just draw this object into color buffer only and hope that it's not
647 * supposed to overlay any glowing objects.
648 */
649 R_DrawBuffers(1);
650 } else {
651 /* FIXME: Had to draw glowmapped object, but don't know how to do it
652 * when rendering through FFP.
653 * So -- just copy color into glow buffer.
654 * (R_DrawAsGlow may hit this)
655 */
656 R_DrawBuffers(2);
657 /*Con_Print("GLSL glow with no program!\n");*/
658 }
659 }
660 }
661 #endif
662 }
663
R_EnableGlowMap(const image_t * image)664 void R_EnableGlowMap (const image_t* image)
665 {
666 if (!r_programs->integer)
667 return;
668
669 if (image)
670 R_BindTextureForTexUnit(image->texnum, &texunit_glowmap);
671
672 /** @todo Is the following condition correct or not? Either fix it or write the comment why it should be done that way */
673 if (!image && r_state.glowmap_enabled == !!image && r_state.active_program == lastProgram)
674 return;
675
676 r_state.glowmap_enabled = !!image;
677
678 /* Shouldn't render glow without GLSL, so enable simple program for it */
679 if (image) {
680 if (!r_state.active_program)
681 R_UseProgram(r_state.simple_glow_program);
682 } else {
683 if (r_state.active_program == r_state.simple_glow_program)
684 R_UseProgram(nullptr);
685 }
686
687 lastProgram = r_state.active_program;
688
689 R_UpdateGlowBufferBinding();
690 }
691
R_EnableDrawAsGlow(bool enable)692 void R_EnableDrawAsGlow (bool enable)
693 {
694 if (!r_programs->integer)
695 return;
696
697 if (r_state.draw_glow_enabled == enable && r_state.active_program == lastProgram)
698 return;
699
700 r_state.draw_glow_enabled = enable;
701
702 lastProgram = r_state.active_program;
703
704 R_UpdateGlowBufferBinding();
705 }
706
R_EnableSpecularMap(const image_t * image,bool enable)707 void R_EnableSpecularMap (const image_t* image, bool enable)
708 {
709 if (!r_state.dynamic_lighting_enabled)
710 return;
711
712 if (r_programs->integer < 2)
713 return;
714
715 if (enable && image != nullptr) {
716 R_BindTextureForTexUnit(image->texnum, &texunit_specularmap);
717 R_ProgramParameter1i("SPECULARMAP", 1);
718 r_state.specularmap_enabled = enable;
719 } else {
720 R_ProgramParameter1i("SPECULARMAP", 0);
721 r_state.specularmap_enabled = false;
722 }
723 }
724
R_EnableRoughnessMap(const image_t * image,bool enable)725 void R_EnableRoughnessMap (const image_t* image, bool enable)
726 {
727 if (!r_state.dynamic_lighting_enabled)
728 return;
729
730 if (enable && image != nullptr) {
731 R_BindTextureForTexUnit(image->texnum, &texunit_roughnessmap);
732 R_ProgramParameter1i("ROUGHMAP", 1);
733 r_state.roughnessmap_enabled = enable;
734 } else {
735 R_ProgramParameter1i("ROUGHMAP", 0);
736 r_state.roughnessmap_enabled = false;
737 }
738 }
739
740 /**
741 * @sa R_Setup3D
742 */
MYgluPerspective(GLfloat zNear,GLfloat zFar)743 static void MYgluPerspective (GLfloat zNear, GLfloat zFar)
744 {
745 GLfloat xmin, xmax, ymin, ymax, yaspect = (float) viddef.context.height / viddef.context.width;
746
747 if (r_isometric->integer) {
748 glOrtho(-10 * refdef.fieldOfViewX, 10 * refdef.fieldOfViewX, -10 * refdef.fieldOfViewX * yaspect, 10 * refdef.fieldOfViewX * yaspect, -zFar, zFar);
749 } else {
750 xmax = zNear * tan(refdef.fieldOfViewX * (M_PI / 360.0));
751 xmin = -xmax;
752
753 ymin = xmin * yaspect;
754 ymax = xmax * yaspect;
755
756 glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
757 }
758 }
759
760 /**
761 * @sa R_Setup2D
762 */
R_Setup3D(void)763 void R_Setup3D (void)
764 {
765 /* only for the battlescape rendering */
766 if ((refdef.rendererFlags & RDF_NOWORLDMODEL) == 0) {
767 int x, x2, y2, y, w, h;
768
769 /* set up viewport */
770 x = floorf(viddef.x * viddef.context.width / viddef.context.width);
771 x2 = ceilf((viddef.x + viddef.viewWidth) * viddef.context.width / viddef.context.width);
772 y = floorf(viddef.context.height - viddef.y * viddef.context.height / viddef.context.height);
773 y2 = ceilf(viddef.context.height - (viddef.y + viddef.viewHeight) * viddef.context.height / viddef.context.height);
774
775 w = x2 - x;
776 h = y - y2;
777
778 glViewport(x, y2, w, h);
779 R_CheckError();
780 }
781
782 /* set up projection matrix */
783 glMatrixMode(GL_PROJECTION);
784
785 glLoadIdentity();
786 if ((refdef.rendererFlags & RDF_NOWORLDMODEL) != 0) {
787 /* center image into the viewport */
788 float x = viddef.x + (viddef.viewWidth - VID_NORM_WIDTH) / 2.0 - (viddef.virtualWidth - VID_NORM_WIDTH) / 2.0;
789 float y = viddef.y + (viddef.viewHeight - VID_NORM_HEIGHT) / 2.0 - (viddef.virtualHeight - VID_NORM_HEIGHT) / 2.0;
790 /* @todo magic coef, i dont know where it come from */
791 x *= 2.0f / (float) viddef.virtualWidth;
792 y *= 2.0f / (float) viddef.virtualHeight;
793 glTranslatef(x, -y, 0.0f);
794 }
795 MYgluPerspective(4.0, MAX_WORLD_WIDTH);
796
797 glMatrixMode(GL_MODELVIEW);
798 glLoadIdentity();
799 glRotatef(-90.0, 1.0, 0.0, 0.0); /* put Z going up */
800 glRotatef(90.0, 0.0, 0.0, 1.0); /* put Z going up */
801 glRotatef(-refdef.viewAngles[2], 1.0, 0.0, 0.0);
802 glRotatef(-refdef.viewAngles[0], 0.0, 1.0, 0.0);
803 glRotatef(-refdef.viewAngles[1], 0.0, 0.0, 1.0);
804 glTranslatef(-refdef.viewOrigin[0], -refdef.viewOrigin[1], -refdef.viewOrigin[2]);
805
806 /* retrieve the resulting matrix for other manipulations */
807 glGetFloatv(GL_MODELVIEW_MATRIX, r_locals.world_matrix);
808
809 /* set vertex array pointer */
810 R_BindDefaultArray(GL_VERTEX_ARRAY);
811
812 glDisable(GL_BLEND);
813
814 glEnable(GL_DEPTH_TEST);
815
816 /* set up framebuffers for postprocessing */
817 R_EnableRenderbuffer(true);
818
819 R_CheckError();
820 }
821
822 /**
823 * @sa R_Setup3D
824 */
R_Setup2D(void)825 void R_Setup2D (void)
826 {
827 /* only for the battlescape rendering */
828 if ((refdef.rendererFlags & RDF_NOWORLDMODEL) == 0) {
829 /* set 2D virtual screen size */
830 glViewport(0, 0, viddef.context.width, viddef.context.height);
831 }
832
833 glMatrixMode(GL_PROJECTION);
834 glLoadIdentity();
835
836 /* switch to orthographic (2 dimensional) projection
837 * don't draw anything before skybox */
838 glOrtho(0, viddef.context.width, viddef.context.height, 0, 9999.0f, SKYBOX_DEPTH);
839
840 glMatrixMode(GL_MODELVIEW);
841 glLoadIdentity();
842
843 /* bind default vertex array */
844 R_BindDefaultArray(GL_VERTEX_ARRAY);
845
846 R_Color(nullptr);
847
848 glEnable(GL_BLEND);
849
850 glDisable(GL_DEPTH_TEST);
851
852 glDisable(GL_LIGHTING);
853
854 /* disable render-to-framebuffer */
855 R_EnableRenderbuffer(false);
856
857 R_CheckError();
858 }
859
R_SetDefaultState(void)860 void R_SetDefaultState (void)
861 {
862 int i;
863
864 r_state.shell_enabled = false;
865 r_state.blend_enabled = false;
866 r_state.color_array_enabled = false;
867 r_state.alpha_test_enabled = false;
868 r_state.stencil_test_enabled = false;
869 r_state.lighting_enabled = false;
870 r_state.warp_enabled = false;
871 r_state.fog_enabled = false;
872 r_state.blur_enabled = false;
873 r_state.glowmap_enabled = false;
874 r_state.draw_glow_enabled = false;
875 r_state.dynamic_lighting_enabled = false;
876 r_state.specularmap_enabled = false;
877 r_state.roughnessmap_enabled = false;
878 r_state.animation_enabled = false;
879 r_state.renderbuffer_enabled = false;
880 r_state.active_material = nullptr;
881 r_state.blend_src = 0;
882 r_state.blend_dest = 0;
883 r_state.active_texunit = nullptr;
884
885 glClearColor(0, 0, 0, 0);
886
887 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
888
889 R_ReallocateStateArrays(GL_ARRAY_LENGTH_CHUNK);
890 R_ReallocateTexunitArray(&texunit_0, GL_ARRAY_LENGTH_CHUNK);
891 R_ReallocateTexunitArray(&texunit_1, GL_ARRAY_LENGTH_CHUNK);
892 R_ReallocateTexunitArray(&texunit_2, GL_ARRAY_LENGTH_CHUNK);
893 R_ReallocateTexunitArray(&texunit_3, GL_ARRAY_LENGTH_CHUNK);
894 R_ReallocateTexunitArray(&texunit_4, GL_ARRAY_LENGTH_CHUNK);
895
896 /* setup vertex array pointers */
897 glEnableClientState(GL_VERTEX_ARRAY);
898 R_BindDefaultArray(GL_VERTEX_ARRAY);
899
900 R_EnableColorArray(true);
901 R_BindDefaultArray(GL_COLOR_ARRAY);
902 R_EnableColorArray(false);
903
904 glEnableClientState(GL_NORMAL_ARRAY);
905 R_BindDefaultArray(GL_NORMAL_ARRAY);
906 glDisableClientState(GL_NORMAL_ARRAY);
907
908 R_EnableAlphaTest(false);
909
910 /* reset gl error state */
911 R_CheckError();
912
913 /* setup texture units */
914 for (i = 0; i < r_config.maxTextureCoords && i < MAX_GL_TEXUNITS; i++) {
915 gltexunit_t* tex = &r_state.texunits[i];
916 tex->texture = GL_TEXTURE0 + i;
917 tex->enabled = false;
918
919 R_EnableTexture(tex, true);
920
921 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
922 R_TexEnv(GL_MODULATE);
923 R_ReallocateTexunitArray(tex, GL_ARRAY_LENGTH_CHUNK);
924
925 if (i > 0) /* turn them off for now */
926 R_EnableTexture(tex, false);
927
928 R_CheckError();
929 }
930
931 R_SelectTexture(&texunit_diffuse);
932 /* alpha test parameters */
933 glAlphaFunc(GL_GREATER, 0.01f);
934
935 /* fog parameters */
936 glFogi(GL_FOG_MODE, GL_LINEAR);
937 glFogf(GL_FOG_START, FOG_START);
938 glFogf(GL_FOG_END, FOG_END);
939
940 /* stencil test parameters */
941 glStencilFunc(GL_GEQUAL, 1, 0xff);
942 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
943
944 /* polygon offset parameters */
945 glPolygonOffset(1, 1);
946
947 /* alpha blend parameters */
948 R_EnableBlend(true);
949 R_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
950
951 /* remove leftover lights */
952 R_ClearStaticLights();
953
954 /* reset gl error state */
955 R_CheckError();
956 }
957
R_TexEnv(GLenum mode)958 void R_TexEnv (GLenum mode)
959 {
960 if (mode == r_state.active_texunit->texenv)
961 return;
962
963 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode);
964 r_state.active_texunit->texenv = mode;
965 }
966
967 /**
968 * @brief Sets special texture environment mode to override texture color; don't forget to call R_TexOverride(nullptr) to reset after drawing; intended for UI only, will conflict with lightmaps
969 */
R_TexOverride(vec4_t rgba)970 void R_TexOverride (vec4_t rgba)
971 {
972 R_SelectTexture(&texunit_lightmap); /* abuse lightmap texture unit for color manipulation */
973
974 if (!rgba) {
975 R_TexEnv(GL_MODULATE);
976 glDisable(GL_TEXTURE_2D);
977 R_SelectTexture(&texunit_diffuse);
978 return;
979 }
980
981 glEnable(GL_TEXTURE_2D);
982
983 R_TexEnv(GL_COMBINE); /* enable color combiner */
984 /* setup texture combiner to blend between actual color of previous texture stage and the constant rgb color given as the parameter to this function
985 * amount of blending is defined by alpha channel of constant color
986 */
987 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT); /* set constant color as blending target*/
988 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
989 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); /* set incoming color as blending source */
990 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
991 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT); /* set constant color alpha as blending factor */
992 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
993 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); /* set blending mode to interpolation from src1 to src0 */
994 /* copy alpha from incoming color, it could be used later for framebuffer operations */
995 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
996 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
997 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
998
999 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
1000
1001 R_BindTexture(r_dummyTexture->texnum);
1002
1003 R_SelectTexture(&texunit_diffuse);
1004 }
1005
1006 const vec4_t color_white = {1, 1, 1, 1};
1007
1008 /**
1009 * @brief Change the color to given value
1010 * @param[in] rgba A pointer to a vec4_t with rgba color value
1011 * @note To reset the color let rgba be nullptr
1012 */
R_Color(const vec4_t rgba)1013 void R_Color (const vec4_t rgba)
1014 {
1015 const float* color;
1016 if (rgba)
1017 color = rgba;
1018 else
1019 color = color_white;
1020
1021 glColor4f(color[0], color[1], color[2], color[3]);
1022 R_CheckError();
1023 }
1024
1025 /**
1026 * @brief Reallocate arrays of GL primitives if needed
1027 * @param size The new array size
1028 * @note Also resets all non-default GL array bindings
1029 * @sa R_ReallocateTexunitArray
1030 */
R_ReallocateStateArrays(int size)1031 void R_ReallocateStateArrays (int size)
1032 {
1033 if (size <= r_state.array_size)
1034 return;
1035 r_state.vertex_array_3d = (GLfloat*) Mem_SafeReAlloc(r_state.vertex_array_3d, size * COMPONENTS_VERTEX_ARRAY3D * sizeof(GLfloat));
1036 r_state.vertex_array_2d = (GLshort*) Mem_SafeReAlloc(r_state.vertex_array_2d, size * COMPONENTS_VERTEX_ARRAY2D * sizeof(GLshort));
1037 r_state.color_array = (GLfloat*) Mem_SafeReAlloc(r_state.color_array, size * COMPONENTS_COLOR_ARRAY * sizeof(GLfloat));
1038 r_state.index_array = (GLint*) Mem_SafeReAlloc(r_state.index_array, size * COMPONENTS_INDEX_ARRAY * sizeof(GLint));
1039 r_state.normal_array = (GLfloat*) Mem_SafeReAlloc(r_state.normal_array, size * COMPONENTS_NORMAL_ARRAY * sizeof(GLfloat));
1040 r_state.tangent_array = (GLfloat*) Mem_SafeReAlloc(r_state.tangent_array, size * COMPONENTS_TANGENT_ARRAY * sizeof(GLfloat));
1041 r_state.next_vertex_array_3d = (GLfloat*) Mem_SafeReAlloc(r_state.next_vertex_array_3d, size * COMPONENTS_VERTEX_ARRAY3D * sizeof(GLfloat));
1042 r_state.next_normal_array = (GLfloat*) Mem_SafeReAlloc(r_state.next_normal_array, size * COMPONENTS_NORMAL_ARRAY * sizeof(GLfloat));
1043 r_state.next_tangent_array = (GLfloat*) Mem_SafeReAlloc(r_state.next_tangent_array, size * COMPONENTS_TANGENT_ARRAY * sizeof(GLfloat));
1044 r_state.array_size = size;
1045 R_BindDefaultArray(GL_VERTEX_ARRAY);
1046 R_BindDefaultArray(GL_COLOR_ARRAY);
1047 R_BindDefaultArray(GL_NORMAL_ARRAY);
1048 R_BindDefaultArray(GL_TANGENT_ARRAY);
1049 R_BindDefaultArray(GL_NEXT_VERTEX_ARRAY);
1050 R_BindDefaultArray(GL_NEXT_NORMAL_ARRAY);
1051 R_BindDefaultArray(GL_NEXT_TANGENT_ARRAY);
1052 }
1053
1054 /**
1055 * @brief Reallocate texcoord array of the specified texunit, if needed
1056 * @param texunit Pointer to texunit (TODO: remove this comment as obvious and redundant)
1057 * @param size The new array size
1058 * @note Also resets active texunit
1059 * @sa R_ReallocateStateArrays
1060 */
R_ReallocateTexunitArray(gltexunit_t * texunit,int size)1061 void R_ReallocateTexunitArray (gltexunit_t* texunit, int size)
1062 {
1063 if (size <= texunit->array_size)
1064 return;
1065 texunit->texcoord_array = (GLfloat*) Mem_SafeReAlloc(texunit->texcoord_array, size * COMPONENTS_TEXCOORD_ARRAY * sizeof(GLfloat));
1066 texunit->array_size = size;
1067 if (!r_state.active_texunit)
1068 r_state.active_texunit = texunit;
1069 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
1070 }
1071