1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18/* OpenGL implementation of NewGfx */
19
20#include "C4Config.h"
21
22#include <Standard.h>
23#include "StdApp.h"
24#include <StdGL.h>
25#include "C4Config.h"
26#include <C4Surface.h>
27#include <C4Log.h>
28#include <StdWindow.h>
29
30#ifndef USE_CONSOLE
31
32#include <array>
33
34#include <stdio.h>
35#include <math.h>
36#include <limits.h>
37
38void CStdGLShader::Compile()
39{
40 if (shader) // recompiling?
41 {
42 glDeleteShader(shader);
43 }
44
45 GLenum t;
46 switch (type)
47 {
48 case Type::Vertex:
49 t = GL_VERTEX_SHADER;
50 break;
51
52 case Type::TesselationControl:
53 t = GL_TESS_CONTROL_SHADER;
54 break;
55
56 case Type::TesselationEvaluation:
57 t = GL_TESS_EVALUATION_SHADER;
58 break;
59
60 case Type::Geometry:
61 t = GL_GEOMETRY_SHADER;
62 break;
63
64 case Type::Fragment:
65 t = GL_FRAGMENT_SHADER;
66 break;
67
68 default:
69 throw Exception{"Invalid shader type"};
70 }
71
72 shader = glCreateShader(t);
73 if (!shader)
74 {
75 throw Exception{"Could not create shader"};
76 }
77
78 PrepareSource();
79
80 GLint status = 0;
81 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
82 if (!status)
83 {
84 GLint size = 0;
85 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
86 if (size)
87 {
88 std::string errorMessage;
89 errorMessage.resize(n: size);
90 glGetShaderInfoLog(shader, size, nullptr, errorMessage.data());
91 errorMessage = errorMessage.c_str();
92 throw Exception{errorMessage};
93 }
94
95 throw Exception{"Compile failed"};
96 }
97}
98
99void CStdGLShader::Clear()
100{
101 if (shader)
102 {
103 glDeleteShader(shader);
104 shader = 0;
105 }
106
107 CStdShader::Clear();
108}
109
110void CStdGLShader::PrepareSource()
111{
112 size_t pos = source.find(s: "#version");
113 if (pos == std::string::npos)
114 {
115 glDeleteShader(shader);
116 throw Exception{"Version directive must be first statement and may not be repeated"};
117 }
118
119 pos = source.find(c: '\n', pos: pos + 1);
120 assert(pos != std::string::npos);
121
122 std::string copy = source;
123 std::string buffer = "";
124
125 for (const auto &[key, value] : macros)
126 {
127 buffer.append(s: "#define ");
128 buffer.append(str: key);
129 buffer.append(s: " ");
130 buffer.append(str: value);
131 buffer.append(s: "\n");
132 }
133
134 buffer.append(s: "#line 1\n");
135
136 copy.insert(pos1: pos + 1, str: buffer);
137
138 const char *s = copy.c_str();
139 glShaderSource(shader, 1, &s, nullptr);
140 glCompileShader(shader);
141}
142
143void CStdGLShaderProgram::Link()
144{
145 EnsureProgram();
146 glLinkProgram(shaderProgram);
147
148 CheckStatus(GL_LINK_STATUS);
149
150 for (const auto &shader : shaders)
151 {
152 glDetachShader(shaderProgram, dynamic_cast<CStdGLShader *>(shader)->GetHandle());
153 }
154
155 shaders.clear();
156}
157
158void CStdGLShaderProgram::Validate()
159{
160 EnsureProgram();
161 glValidateProgram(shaderProgram);
162 CheckStatus(GL_VALIDATE_STATUS);
163}
164
165void CStdGLShaderProgram::Clear()
166{
167 for (const auto &shader : shaders)
168 {
169 glDetachShader(shaderProgram, dynamic_cast<CStdGLShader *>(shader)->GetHandle());
170 }
171
172 if (shaderProgram)
173 {
174 glDeleteProgram(shaderProgram);
175 shaderProgram = 0;
176 }
177
178 attributeLocations.clear();
179 uniformLocations.clear();
180
181 CStdShaderProgram::Clear();
182}
183
184void CStdGLShaderProgram::EnsureProgram()
185{
186 if (!shaderProgram)
187 {
188 shaderProgram = glCreateProgram();
189 }
190 assert(shaderProgram);
191}
192
193bool CStdGLShaderProgram::AddShaderInt(CStdShader *shader)
194{
195 if (auto *s = dynamic_cast<CStdGLShader *>(shader); s)
196 {
197 glAttachShader(shaderProgram, s->GetHandle());
198 return true;
199 }
200
201 return false;
202}
203
204void CStdGLShaderProgram::OnSelect()
205{
206 assert(shaderProgram);
207 glUseProgram(shaderProgram);
208}
209
210void CStdGLShaderProgram::OnDeselect()
211{
212 glUseProgram(GL_NONE);
213}
214
215void CStdGLShaderProgram::CheckStatus(const GLenum type)
216{
217 GLint status{0};
218 glGetProgramiv(shaderProgram, type, &status);
219 if (!status)
220 {
221 GLint size = 0;
222 glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &size);
223 if (size)
224 {
225 std::string errorMessage;
226 errorMessage.resize(n: size);
227 glGetProgramInfoLog(shaderProgram, size, NULL, errorMessage.data());
228 errorMessage = errorMessage.c_str();
229 throw Exception{errorMessage};
230 }
231
232 throw Exception{"Status error"};
233 }
234}
235
236template<GLenum T, std::size_t Dimensions>
237CStdGLTexture<T, Dimensions>::CStdGLTexture(std::array<int32_t, Dimensions> dimensions, const GLenum internalFormat, const GLenum format, const GLenum type)
238 : dimensions{std::move(dimensions)}, internalFormat{internalFormat}, format{format}, type{type}
239{
240 glPixelStorei(GL_UNPACK_ALIGNMENT, param: 1);
241 ThrowIfGLError();
242 glGenTextures(n: 1, textures: &texture);
243 ThrowIfGLError();
244}
245
246template<GLenum T, std::size_t Dimensions>
247CStdGLTexture<T, Dimensions>::~CStdGLTexture()
248{
249 Clear();
250}
251
252template<GLenum T, std::size_t Dimensions>
253void CStdGLTexture<T, Dimensions>::Bind(const GLenum offset)
254{
255 glActiveTexture(GL_TEXTURE0 + offset);
256 glBindTexture(target: Target, texture);
257}
258
259template<GLenum T, std::size_t Dimensions>
260void CStdGLTexture<T, Dimensions>::SetData(const void *const data)
261{
262 std::apply(GetSetDataFunction(), std::tuple_cat(std::tuple{Target, 0, internalFormat}, dimensions, std::tuple{0, format, type, data}));
263 ThrowIfGLError();
264}
265
266template<GLenum T, std::size_t Dimensions>
267void CStdGLTexture<T, Dimensions>::UpdateData(const void *const data)
268{
269 constexpr std::array<std::int32_t, Dimensions> Offset{};
270 std::apply(GetUpdateDataFunction(), std::tuple_cat(std::tuple{Target, 0}, Offset, dimensions, std::tuple{format, type, data}));
271 ThrowIfGLError();
272}
273
274template<GLenum T, std::size_t Dimensions>
275void CStdGLTexture<T, Dimensions>::Clear()
276{
277 if (texture)
278 {
279 glDeleteTextures(n: 1, textures: &texture);
280 texture = GL_NONE;
281 }
282}
283
284template<GLenum T, std::size_t Dimensions>
285void CStdGLTexture<T, Dimensions>::ThrowIfGLError()
286{
287 if (const GLenum error{glGetError()}; error != GL_NO_ERROR)
288 {
289 throw Exception{reinterpret_cast<const char *>(gluErrorString(error))};
290 }
291}
292
293template<GLenum T, std::size_t Dimensions>
294auto CStdGLTexture<T, Dimensions>::GetSetDataFunction()
295{
296 if constexpr (Dimensions == 1)
297 {
298 return glTexImage1D;
299 }
300 else if constexpr (Dimensions == 2)
301 {
302 return glTexImage2D;
303 }
304 else if constexpr (Dimensions == 3)
305 {
306 return glTexImage3D;
307 }
308}
309
310template<GLenum T, std::size_t Dimensions>
311auto CStdGLTexture<T, Dimensions>::GetUpdateDataFunction()
312{
313 if constexpr (Dimensions == 1)
314 {
315 return glTexSubImage1D;
316 }
317 else if constexpr (Dimensions == 2)
318 {
319 return glTexSubImage2D;
320 }
321 else if constexpr (Dimensions == 3)
322 {
323 return glTexSubImage3D;
324 }
325}
326
327static void glColorDw(const uint32_t dwClr)
328{
329 glColor4ub(
330 red: static_cast<GLubyte>(dwClr >> 16),
331 green: static_cast<GLubyte>(dwClr >> 8),
332 blue: static_cast<GLubyte>(dwClr),
333 alpha: static_cast<GLubyte>(dwClr >> 24));
334}
335
336CStdGL::CStdGL()
337{
338 Default();
339 // global ptr
340 pGL = this;
341}
342
343CStdGL::~CStdGL()
344{
345 Clear();
346 pGL = nullptr;
347}
348
349void CStdGL::Clear()
350{
351#ifndef USE_SDL_MAINLOOP
352 CStdDDraw::Clear();
353#endif
354 NoPrimaryClipper();
355 if (pTexMgr) pTexMgr->IntUnlock();
356 InvalidateDeviceObjects();
357 NoPrimaryClipper();
358 // del font objects
359 // del main surfaces
360 delete lpPrimary;
361 lpPrimary = lpBack = nullptr;
362 RenderTarget = nullptr;
363 // clear context
364 if (pCurrCtx) pCurrCtx->Deselect();
365 MainCtx.Clear();
366 pCurrCtx = nullptr;
367#ifndef USE_SDL_MAINLOOP
368 CStdDDraw::Clear();
369#endif
370}
371
372void CStdGL::PageFlip()
373{
374 // call from gfx thread only!
375 if (!pApp || !pApp->AssertMainThread()) return;
376 // safety
377 if (!pCurrCtx) return;
378 // end the scene and present it
379 pCurrCtx->PageFlip();
380}
381
382void CStdGL::FillBG(const uint32_t dwClr)
383{
384 if (!pCurrCtx && !MainCtx.Select()) return;
385 glClearColor(
386 GetBValue(dwClr) / 255.0f,
387 GetGValue(dwClr) / 255.0f,
388 GetRValue(dwClr) / 255.0f,
389 alpha: 1.0f);
390 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
391}
392
393bool CStdGL::UpdateClipper()
394{
395 int iX, iY, iWdt, iHgt;
396 // no render target or clip all? do nothing
397 if (!CalculateClipper(iX: &iX, iY: &iY, iWdt: &iWdt, iHgt: &iHgt)) return true;
398 const auto scale = pApp->GetScale();
399 glLineWidth(width: scale);
400 glPointSize(size: scale);
401 // set it
402 glViewport(x: static_cast<int32_t>(floorf(x: iX * scale)), y: static_cast<int32_t>(floorf(x: (RenderTarget->Hgt - iY - iHgt) * scale)), width: static_cast<int32_t>(ceilf(x: iWdt * scale)), height: static_cast<int32_t>(ceilf(x: iHgt * scale)));
403 glMatrixMode(GL_PROJECTION);
404 glLoadIdentity();
405 gluOrtho2D(
406 left: static_cast<GLdouble>(iX), right: static_cast<GLdouble>(iX + iWdt),
407 bottom: static_cast<GLdouble>(iY + iHgt), top: static_cast<GLdouble>(iY));
408 return true;
409}
410
411bool CStdGL::PrepareRendering(C4Surface *const sfcToSurface)
412{
413 // call from gfx thread only!
414 if (!pApp || !pApp->AssertMainThread()) return false;
415 // device?
416 if (!pCurrCtx && !MainCtx.Select()) return false;
417 // not ready?
418 if (!Active) return false;
419 // target?
420 if (!sfcToSurface) return false;
421 // target locked?
422 assert(!sfcToSurface->Locked);
423 // target is already set as render target?
424 if (sfcToSurface != RenderTarget)
425 {
426 // target is a render-target?
427 if (!sfcToSurface->IsRenderTarget()) return false;
428 // set target
429 RenderTarget = sfcToSurface;
430 // new target has different size; needs other clipping rect
431 UpdateClipper();
432 }
433 // done
434 return true;
435}
436
437void CStdGL::PerformBlt(CBltData &rBltData, C4TexRef *const pTex,
438 const uint32_t dwModClr, bool fMod2, const bool fExact)
439{
440 // global modulation map
441 uint32_t dwModMask = 0;
442 bool fAnyModNotBlack;
443 bool fModClr = false;
444 if (fUseClrModMap && dwModClr)
445 {
446 fAnyModNotBlack = false;
447 for (auto &vertex : rBltData.vtVtx)
448 {
449 float x{vertex.ftx};
450 float y{vertex.fty};
451 if (rBltData.pTransform)
452 {
453 rBltData.pTransform->TransformPoint(rX&: x, rY&: y);
454 }
455 vertex.dwModClr = pClrModMap->GetModAt(x: static_cast<int>(x), y: static_cast<int>(y));
456 if (vertex.dwModClr >> 24) dwModMask = 0xff000000;
457 ModulateClr(dst&: vertex.dwModClr, src: dwModClr);
458 if (vertex.dwModClr) fAnyModNotBlack = true;
459 if (vertex.dwModClr != 0xffffff) fModClr = true;
460 }
461 }
462 else
463 {
464 fAnyModNotBlack = !!dwModClr;
465 for (auto &vertex : rBltData.vtVtx)
466 {
467 vertex.dwModClr = dwModClr;
468 }
469 if (dwModClr != 0xffffff) fModClr = true;
470 }
471 // reset MOD2 for completely black modulations
472 if (fMod2 && !fAnyModNotBlack) fMod2 = 0;
473 if (BlitShader)
474 {
475 dwModMask = 0;
476 if (fMod2 && BlitShaderMod2)
477 {
478 BlitShaderMod2.Select();
479 }
480 else
481 {
482 BlitShader.Select();
483 }
484
485 if (!fModClr) glColor4f(red: 1.0f, green: 1.0f, blue: 1.0f, alpha: 0.0f);
486 }
487 // modulated blit
488 else if (fModClr)
489 {
490 if (fMod2 || ((dwModClr >> 24 || dwModMask) && !Config.Graphics.NoAlphaAdd))
491 {
492 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
493 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, param: fMod2 ? GL_ADD_SIGNED : GL_MODULATE);
494 glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, param: fMod2 ? 2.0f : 1.0f);
495 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
496 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
497 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
498 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
499 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
500 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
501 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
502 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
503 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
504 dwModMask = 0;
505 }
506 else
507 {
508 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
509 glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, param: 1.0f);
510 dwModMask = 0xff000000;
511 }
512 }
513 else
514 {
515 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
516 glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, param: 1.0f);
517 }
518 // set texture+modes
519 glShadeModel(mode: (fUseClrModMap && fModClr && !Config.Graphics.NoBoxFades) ? GL_SMOOTH : GL_FLAT);
520 glBindTexture(GL_TEXTURE_2D, texture: pTex->texName);
521
522 if (GammaRedTexture)
523 {
524 BindGammaTextures();
525 }
526
527 const auto enableTextureFiltering = (pApp->GetScale() != 1.f || (!fExact && !Config.Graphics.PointFiltering));
528 if (enableTextureFiltering)
529 {
530 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
531 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
532 }
533
534 glMatrixMode(GL_TEXTURE);
535 float matrix[16];
536 matrix[0] = rBltData.TexPos.mat[0]; matrix[ 1] = rBltData.TexPos.mat[3]; matrix[ 2] = 0; matrix[ 3] = rBltData.TexPos.mat[6];
537 matrix[4] = rBltData.TexPos.mat[1]; matrix[ 5] = rBltData.TexPos.mat[4]; matrix[ 6] = 0; matrix[ 7] = rBltData.TexPos.mat[7];
538 matrix[8] = 0; matrix[ 9] = 0; matrix[10] = 1; matrix[11] = 0;
539 matrix[12] = rBltData.TexPos.mat[2]; matrix[13] = rBltData.TexPos.mat[5]; matrix[14] = 0; matrix[15] = rBltData.TexPos.mat[8];
540 glLoadMatrixf(m: matrix);
541
542 glMatrixMode(GL_MODELVIEW);
543 if (rBltData.pTransform)
544 {
545 const float *const mat = rBltData.pTransform->mat;
546 matrix[ 0] = mat[0]; matrix[ 1] = mat[3]; matrix[ 2] = 0; matrix[ 3] = mat[6];
547 matrix[ 4] = mat[1]; matrix[ 5] = mat[4]; matrix[ 6] = 0; matrix[ 7] = mat[7];
548 matrix[ 8] = 0; matrix[ 9] = 0; matrix[10] = 1; matrix[11] = 0;
549 matrix[12] = mat[2]; matrix[13] = mat[5]; matrix[14] = 0; matrix[15] = mat[8];
550 glLoadMatrixf(m: matrix);
551 }
552 else
553 {
554 glLoadIdentity();
555 }
556
557 glBegin(GL_TRIANGLE_STRIP);
558 for (const auto vertex : rBltData.vtVtx)
559 {
560 if (fModClr) glColorDw(dwClr: vertex.dwModClr | dwModMask);
561 glTexCoord2f(s: vertex.ftx, t: vertex.fty);
562 glVertex2f(x: vertex.ftx, y: vertex.fty);
563 }
564 glEnd();
565 glLoadIdentity();
566 if (BlitShader)
567 {
568 CStdShaderProgram::Deselect();
569 }
570
571 if (enableTextureFiltering)
572 {
573 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
574 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
575 }
576}
577
578void CStdGL::BlitLandscape(C4Surface *const sfcSource, C4Surface *const sfcSource2,
579 C4Surface *const sfcLiquidAnimation, const int fx, const int fy,
580 C4Surface *const sfcTarget, const int tx, const int ty, const int wdt, const int hgt)
581{
582 // safety
583 if (!sfcSource || !sfcTarget || !wdt || !hgt) return;
584 assert(sfcTarget->IsRenderTarget());
585 assert(!(dwBlitMode & C4GFXBLIT_MOD2));
586 // bound
587 if (ClipAll) return;
588 // inside screen?
589 if (wdt <= 0 || hgt <= 0) return;
590 // prepare rendering to surface
591 if (!PrepareRendering(sfcToSurface: sfcTarget)) return;
592 // texture present?
593 if (!sfcSource->ppTex) return;
594 // blit with basesfc?
595 // get involved texture offsets
596 int iTexSize = sfcSource->iTexSize;
597 const int iTexX = (std::max)(a: fx / iTexSize, b: 0);
598 const int iTexY = (std::max)(a: fy / iTexSize, b: 0);
599 const int iTexX2 = (std::min)(a: (fx + wdt - 1) / iTexSize + 1, b: sfcSource->iTexX);
600 const int iTexY2 = (std::min)(a: (fy + hgt - 1) / iTexSize + 1, b: sfcSource->iTexY);
601 // blit from all these textures
602 SetTexture();
603 if (sfcSource2)
604 {
605 glActiveTexture(GL_TEXTURE1);
606 glEnable(GL_TEXTURE_2D);
607 glActiveTexture(GL_TEXTURE2);
608 glEnable(GL_TEXTURE_2D);
609 glBindTexture(GL_TEXTURE_2D, texture: (*sfcLiquidAnimation->ppTex)->texName);
610 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
611 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
612 glActiveTexture(GL_TEXTURE0);
613 }
614
615 if (GammaRedTexture)
616 {
617 BindGammaTextures();
618 }
619
620 uint32_t dwModMask = 0;
621 if (LandscapeShader)
622 {
623 LandscapeShader.Select();
624
625 if (sfcSource2)
626 {
627 static GLfloat value[4] = { -0.6f / 3, 0.0f, 0.6f / 3, 0.0f };
628 value[0] += 0.05f; value[1] += 0.05f; value[2] += 0.05f;
629 GLfloat mod[4];
630 for (int i = 0; i < 3; ++i)
631 {
632 if (value[i] > 0.9f) value[i] = -0.3f;
633 mod[i] = (value[i] > 0.3f ? 0.6f - value[i] : value[i]) / 3.0f;
634 }
635 mod[3] = 0;
636
637 LandscapeShader.SetUniform(key: "modulation", glUniform4fv, args: 1, args: mod);
638 }
639 }
640 // texture environment
641 else
642 {
643 if (Config.Graphics.NoAlphaAdd)
644 {
645 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
646 glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, param: 1.0f);
647 dwModMask = 0xff000000;
648 }
649 else
650 {
651 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
652 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
653 glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, param: 1.0f);
654 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
655 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
656 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
657 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
658 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
659 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
660 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
661 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
662 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
663 dwModMask = 0;
664 }
665 }
666 // set texture+modes
667 glShadeModel(mode: (fUseClrModMap && !Config.Graphics.NoBoxFades) ? GL_SMOOTH : GL_FLAT);
668 glMatrixMode(GL_MODELVIEW);
669 glLoadIdentity();
670 glMatrixMode(GL_TEXTURE);
671 glLoadIdentity();
672
673 const uint32_t dwModClr = BlitModulated ? BlitModulateClr : 0xffffff;
674 int chunkSize = iTexSize;
675 if (fUseClrModMap && dwModClr)
676 {
677 chunkSize = std::min(a: iTexSize, b: 64);
678 }
679
680 for (int iY = iTexY; iY < iTexY2; ++iY)
681 {
682 for (int iX = iTexX; iX < iTexX2; ++iX)
683 {
684 // blit
685
686 if (sfcSource2) glActiveTexture(GL_TEXTURE0);
687 const auto *const pTex = *(sfcSource->ppTex + iY * sfcSource->iTexX + iX);
688 // get current blitting offset in texture (beforing any last-tex-size-changes)
689 const int iBlitX = iTexSize * iX;
690 const int iBlitY = iTexSize * iY;
691 glBindTexture(GL_TEXTURE_2D, texture: pTex->texName);
692 if (sfcSource2)
693 {
694 const auto *const pTex = *(sfcSource2->ppTex + iY * sfcSource2->iTexX + iX);
695 glActiveTexture(GL_TEXTURE1);
696 glBindTexture(GL_TEXTURE_2D, texture: pTex->texName);
697 }
698
699 int maxXChunk = std::min<int>(a: (fx + wdt - iBlitX - 1) / chunkSize + 1, b: iTexSize / chunkSize);
700 int maxYChunk = std::min<int>(a: (fy + hgt - iBlitY - 1) / chunkSize + 1, b: iTexSize / chunkSize);
701
702 // size changed? recalc dependent, relevant (!) values
703 if (iTexSize != pTex->iSize) iTexSize = pTex->iSize;
704 for (int yChunk = std::max<int>(a: (fy - iBlitY) / chunkSize, b: 0); yChunk < maxYChunk; ++yChunk)
705 {
706 for (int xChunk = std::max<int>(a: (fx - iBlitX) / chunkSize, b: 0); xChunk < maxXChunk; ++xChunk)
707 {
708 int xOffset = xChunk * chunkSize;
709 int yOffset = yChunk * chunkSize;
710 // draw triangle strip
711 glBegin(GL_TRIANGLE_STRIP);
712 // Get new texture source bounds
713 const auto fTexBltLeft = static_cast<float>(std::max(a: xOffset, b: fx - iBlitX));
714 const auto fTexBltTop = static_cast<float>(std::max(a: yOffset, b: fy - iBlitY));
715 const auto fTexBltRight = static_cast<float>(std::min(a: fx + wdt - iBlitX, b: xOffset + chunkSize));
716 const auto fTexBltBottom = static_cast<float>(std::min(a: fy + hgt - iBlitY, b: yOffset + chunkSize));
717 // Get new dest bounds
718 const float tTexBltLeft {fTexBltLeft + iBlitX - fx + tx};
719 const float tTexBltTop {fTexBltTop + iBlitY - fy + ty};
720 const float tTexBltRight {fTexBltRight + iBlitX - fx + tx};
721 const float tTexBltBottom{fTexBltBottom + iBlitY - fy + ty};
722
723 // Get blit positions
724 const std::array<float, 4>
725 ftx{tTexBltLeft, tTexBltRight, tTexBltLeft , tTexBltRight },
726 fty{tTexBltTop , tTexBltTop , tTexBltBottom, tTexBltBottom},
727 tcx{fTexBltLeft, fTexBltRight, fTexBltLeft , fTexBltRight },
728 tcy{fTexBltTop , fTexBltTop , fTexBltBottom, fTexBltBottom};
729
730 uint32_t fdwModClr[4]; // color modulation
731 // global modulation map
732 if (fUseClrModMap && dwModClr)
733 {
734 for (int i = 0; i < 4; ++i)
735 {
736 fdwModClr[i] = pClrModMap->GetModAt(
737 x: static_cast<int>(ftx[i]), y: static_cast<int>(fty[i]));
738 ModulateClr(dst&: fdwModClr[i], src: dwModClr);
739 }
740 }
741 else
742 {
743 std::fill(first: fdwModClr, last: std::end(arr&: fdwModClr), value: dwModClr);
744 }
745
746 for (int i = 0; i < 4; ++i)
747 {
748 glColorDw(dwClr: fdwModClr[i] | dwModMask);
749 glTexCoord2f(s: (tcx[i] + texIndent) / iTexSize,
750 t: (tcy[i] + texIndent) / iTexSize);
751 if (sfcSource2)
752 {
753 glMultiTexCoord2f(GL_TEXTURE1_ARB,
754 (tcx[i] + texIndent) / iTexSize,
755 (tcy[i] + texIndent) / iTexSize);
756 glMultiTexCoord2f(GL_TEXTURE2_ARB,
757 (tcx[i] + texIndent) / sfcLiquidAnimation->iTexSize,
758 (tcy[i] + texIndent) / sfcLiquidAnimation->iTexSize);
759 }
760 glVertex2f(x: ftx[i] + blitOffset, y: fty[i] + blitOffset);
761 }
762
763 glEnd();
764 }
765 }
766 }
767 }
768 if (LandscapeShader)
769 {
770 CStdShaderProgram::Deselect();
771 }
772 if (sfcSource2)
773 {
774 glActiveTexture(GL_TEXTURE1);
775 glDisable(GL_TEXTURE_2D);
776 glActiveTexture(GL_TEXTURE2);
777 glDisable(GL_TEXTURE_2D);
778 glActiveTexture(GL_TEXTURE0);
779 }
780 // reset texture
781 ResetTexture();
782}
783
784bool CStdGL::CreateDirectDraw()
785{
786 logger->info(msg: "Using OpenGL...");
787 return true;
788}
789
790CStdGLCtx *CStdGL::CreateContext(CStdWindow *const pWindow, CStdApp *const pApp)
791{
792 // safety
793 if (!pWindow) return nullptr;
794
795#ifdef USE_SDL_MAINLOOP
796 if (SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) != 0)
797 {
798 logger->error("SDL: Enabling context sharing failed: {}", SDL_GetError());
799 }
800#endif
801
802 // create it
803 const auto pCtx = new CStdGLCtx();
804 if (!pCtx->Init(pWindow, pApp))
805 {
806 delete pCtx; logger->error(msg: "Error creating secondary context!"); return nullptr;
807 }
808 // done
809 return pCtx;
810}
811
812#ifdef _WIN32
813CStdGLCtx *CStdGL::CreateContext(const HWND hWindow, CStdApp *const pApp)
814{
815 // safety
816 if (!hWindow) return nullptr;
817 // create it
818 const auto pCtx = new CStdGLCtx();
819 if (!pCtx->Init(nullptr, pApp, hWindow))
820 {
821 delete pCtx; logger->error("Error creating secondary context!"); return nullptr;
822 }
823 // done
824 return pCtx;
825}
826#endif
827
828bool CStdGL::CreatePrimarySurfaces()
829{
830 // create lpPrimary and lpBack (used in first context selection)
831 lpPrimary = lpBack = new C4Surface();
832 lpPrimary->fPrimary = true;
833 lpPrimary->AttachSfc(sfcSurface: nullptr);
834
835 // create+select gl context
836 if (!MainCtx.Init(pWindow: pApp->pWindow, pApp))
837 {
838 logger->error(msg: "Error initializing context");
839 return false;
840 }
841
842 // done, init device stuff
843 return RestoreDeviceObjects();
844}
845
846void CStdGL::DrawQuadDw(C4Surface *const sfcTarget, int *const ipVtx,
847 uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3, uint32_t dwClr4)
848{
849 // prepare rendering to target
850 if (!PrepareRendering(sfcToSurface: sfcTarget)) return;
851
852 CStdGLShaderProgram::Deselect();
853
854 // apply global modulation
855 ClrByCurrentBlitMod(rdwClr&: dwClr1);
856 ClrByCurrentBlitMod(rdwClr&: dwClr2);
857 ClrByCurrentBlitMod(rdwClr&: dwClr3);
858 ClrByCurrentBlitMod(rdwClr&: dwClr4);
859 // apply modulation map
860 if (fUseClrModMap)
861 {
862 ModulateClr(dst&: dwClr1, src: pClrModMap->GetModAt(x: ipVtx[0], y: ipVtx[1]));
863 ModulateClr(dst&: dwClr2, src: pClrModMap->GetModAt(x: ipVtx[2], y: ipVtx[3]));
864 ModulateClr(dst&: dwClr3, src: pClrModMap->GetModAt(x: ipVtx[4], y: ipVtx[5]));
865 ModulateClr(dst&: dwClr4, src: pClrModMap->GetModAt(x: ipVtx[6], y: ipVtx[7]));
866 }
867 // no clr fading supported
868 if (Config.Graphics.NoBoxFades)
869 {
870 NormalizeColors(dwClr1, dwClr2, dwClr3, dwClr4);
871 glShadeModel(GL_FLAT);
872 }
873 else
874 glShadeModel(mode: (dwClr1 == dwClr2 && dwClr1 == dwClr3 && dwClr1 == dwClr4) ? GL_FLAT : GL_SMOOTH);
875 // set blitting state
876 const int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;
877 glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, dfactor: iAdditive ? GL_ONE : GL_SRC_ALPHA);
878 // draw two triangles
879 if (DummyShader)
880 {
881 DummyShader.Select();
882 BindGammaTextures();
883 }
884 glBegin(GL_TRIANGLE_STRIP);
885 glColorDw(dwClr: dwClr1); glVertex2f(x: ipVtx[0] + blitOffset, y: ipVtx[1] + blitOffset);
886 glColorDw(dwClr: dwClr2); glVertex2f(x: ipVtx[2] + blitOffset, y: ipVtx[3] + blitOffset);
887 glColorDw(dwClr: dwClr4); glVertex2f(x: ipVtx[6] + blitOffset, y: ipVtx[7] + blitOffset);
888 glColorDw(dwClr: dwClr3); glVertex2f(x: ipVtx[4] + blitOffset, y: ipVtx[5] + blitOffset);
889 glEnd();
890 glShadeModel(GL_FLAT);
891}
892
893void CStdGL::DrawLineDw(C4Surface *const sfcTarget,
894 const float x1, const float y1, const float x2, const float y2, uint32_t dwClr)
895{
896 // apply color modulation
897 ClrByCurrentBlitMod(rdwClr&: dwClr);
898 // render target?
899 assert(sfcTarget->IsRenderTarget());
900 // prepare rendering to target
901 if (!PrepareRendering(sfcToSurface: sfcTarget)) return;
902
903 CStdGLShaderProgram::Deselect();
904
905 // set blitting state
906 const int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;
907 // use a different blendfunc here, because GL_LINE_SMOOTH expects this one
908 glBlendFunc(GL_SRC_ALPHA, dfactor: iAdditive ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
909 // draw one line
910 if (DummyShader)
911 {
912 DummyShader.Select();
913 BindGammaTextures();
914 }
915 glBegin(GL_LINES);
916 // global clr modulation map
917 uint32_t dwClr1 = dwClr;
918 if (fUseClrModMap)
919 {
920 ModulateClr(dst&: dwClr1, src: pClrModMap->GetModAt(
921 x: static_cast<int>(x1), y: static_cast<int>(y1)));
922 }
923 // convert from clonk-alpha to GL_LINE_SMOOTH alpha
924 glColorDw(dwClr: InvertRGBAAlpha(dwFromClr: dwClr1));
925 glVertex2f(x: x1 + 0.5f, y: y1 + 0.5f);
926 if (fUseClrModMap)
927 {
928 ModulateClr(dst&: dwClr, src: pClrModMap->GetModAt(
929 x: static_cast<int>(x2), y: static_cast<int>(y2)));
930 glColorDw(dwClr: InvertRGBAAlpha(dwFromClr: dwClr));
931 }
932 glVertex2f(x: x2 + 0.5f, y: y2 + 0.5f);
933 glEnd();
934}
935
936void CStdGL::DrawPixInt(C4Surface *const sfcTarget,
937 const float tx, const float ty, const uint32_t dwClr)
938{
939 // render target?
940 assert(sfcTarget->IsRenderTarget());
941
942 if (!PrepareRendering(sfcToSurface: sfcTarget)) return;
943
944 CStdGLShaderProgram::Deselect();
945
946 const int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;
947 // use a different blendfunc here because of GL_POINT_SMOOTH
948 glBlendFunc(GL_SRC_ALPHA, dfactor: iAdditive ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
949 // convert the alpha value for that blendfunc
950 if (DummyShader)
951 {
952 DummyShader.Select();
953 BindGammaTextures();
954 }
955 glBegin(GL_POINTS);
956 glColorDw(dwClr: InvertRGBAAlpha(dwFromClr: dwClr));
957 glVertex2f(x: tx + 0.5f, y: ty + 0.5f);
958 glEnd();
959}
960
961void CStdGL::DisableGamma()
962{
963 if (gammaDisabled)
964 {
965 return;
966 }
967 else
968 {
969 CStdDDraw::DisableGamma();
970 }
971}
972
973void CStdGL::EnableGamma()
974{
975 if (gammaDisabled)
976 {
977 return;
978 }
979
980 CStdDDraw::EnableGamma();
981}
982
983bool CStdGL::ApplyGammaRamp(CGammaControl &ramp, bool force)
984{
985 if (gammaDisabled) return true;
986
987 else if (GammaRedTexture)
988 {
989 glActiveTexture(GL_TEXTURE3);
990 GammaRedTexture.UpdateData(data: ramp.red);
991 glActiveTexture(GL_TEXTURE4);
992 GammaGreenTexture.UpdateData(data: ramp.green);
993 glActiveTexture(GL_TEXTURE5);
994 GammaBlueTexture.UpdateData(data: ramp.blue);
995 glActiveTexture(GL_TEXTURE0);
996 return true;
997 }
998
999 return ApplyGammaRampToMonitor(ramp, force);
1000}
1001
1002bool CStdGL::SaveDefaultGammaRamp(CStdWindow *window)
1003{
1004 if (gammaDisabled || GammaRedTexture)
1005 {
1006 DefRamp.Default();
1007 Gamma.Set(dwClr1: 0x000000, dwClr2: 0x808080, dwClr3: 0xffffff, size: 256, ref: &DefRamp);
1008 return true;
1009 }
1010
1011 return SaveDefaultGammaRampToMonitor(window);
1012}
1013
1014bool CStdGL::RestoreDeviceObjects()
1015{
1016 // safety
1017 if (!lpPrimary) return false;
1018 // restore primary/back
1019 RenderTarget = lpPrimary;
1020 lpPrimary->AttachSfc(sfcSurface: nullptr);
1021
1022 // set states
1023 const bool fSuccess = pCurrCtx ? pCurrCtx->Select() : MainCtx.Select();
1024 // activate if successful
1025 Active = fSuccess;
1026 // reset blit states
1027 dwBlitMode = 0;
1028
1029 blitOffset = static_cast<float>(Config.Graphics.BlitOffset) / 100;
1030 texIndent = static_cast<float>(Config.Graphics.TexIndent) / 1000;
1031 gammaDisabled = Config.Graphics.DisableGamma;
1032
1033 if (Config.Graphics.Shader && !BlitShader)
1034 {
1035 try
1036 {
1037 CStdGLShader vertexShader{CStdShader::Type::Vertex,
1038 R"(
1039 #version 120
1040
1041 void main()
1042 {
1043 gl_Position = ftransform();
1044 gl_FrontColor = gl_Color;
1045 gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
1046 #ifdef LC_COLOR_ANIMATION
1047 gl_TexCoord[1] = gl_TextureMatrix[0] * gl_MultiTexCoord1;
1048 gl_TexCoord[2] = gl_TextureMatrix[0] * gl_MultiTexCoord2;
1049 #endif
1050 }
1051 )"
1052 };
1053
1054 vertexShader.Compile();
1055
1056 CStdGLShader blitFragmentShader{CStdShader::Type::Fragment,
1057 R"(
1058 #version 120
1059
1060 uniform sampler2D textureSampler;
1061
1062 #ifdef LC_GAMMA
1063 uniform sampler1D gammaRed;
1064 uniform sampler1D gammaGreen;
1065 uniform sampler1D gammaBlue;
1066 #endif
1067
1068 void main()
1069 {
1070 vec4 fragColor = texture2D(textureSampler, gl_TexCoord[0].st);
1071
1072 #ifdef LC_MOD2
1073 fragColor.rgb += gl_Color.rgb;
1074 fragColor.rgb = clamp(fragColor.rgb * 2.0 - 1.0, 0.0, 1.0);
1075 #else
1076 fragColor.rgb *= gl_Color.rgb;
1077 fragColor.rgb = clamp(fragColor.rgb, 0.0, 1.0);
1078 fragColor.a = clamp(fragColor.a + gl_Color.a, 0.0, 1.0);
1079 #endif
1080
1081 #ifdef LC_GAMMA
1082 fragColor.r = texture1D(gammaRed, fragColor.r).r;
1083 fragColor.g = texture1D(gammaGreen, fragColor.g).r;
1084 fragColor.b = texture1D(gammaBlue, fragColor.b).r;
1085 #endif
1086
1087 gl_FragColor = fragColor;
1088 }
1089 )"
1090 };
1091
1092 if (Config.Graphics.UseShaderGamma && !gammaDisabled)
1093 {
1094 blitFragmentShader.SetMacro(key: "LC_GAMMA", value: "1");
1095 }
1096
1097 blitFragmentShader.Compile();
1098
1099 BlitShader.AddShader(shader: &vertexShader);
1100 BlitShader.AddShader(shader: &blitFragmentShader);
1101 BlitShader.Link();
1102
1103 blitFragmentShader.SetMacro(key: "LC_MOD2", value: "1");
1104
1105 blitFragmentShader.Compile();
1106
1107 BlitShaderMod2.AddShader(shader: &vertexShader);
1108 BlitShaderMod2.AddShader(shader: &blitFragmentShader);
1109 BlitShaderMod2.Link();
1110
1111 CStdGLShader landscapeFragmentShader{CStdShader::Type::Fragment,
1112 R"(
1113 #version 120
1114
1115 uniform sampler2D textureSampler;
1116 #ifdef LC_COLOR_ANIMATION
1117 uniform sampler2D maskSampler;
1118 uniform sampler2D liquidSampler;
1119 uniform vec4 modulation;
1120 #endif
1121
1122 #ifdef LC_GAMMA
1123 uniform sampler1D gammaRed;
1124 uniform sampler1D gammaGreen;
1125 uniform sampler1D gammaBlue;
1126 #endif
1127
1128 void main()
1129 {
1130 vec4 fragColor = texture2D(textureSampler, gl_TexCoord[0].st);
1131 #ifdef LC_COLOR_ANIMATION
1132 float mask = texture2D(maskSampler, gl_TexCoord[1].st).a;
1133 vec3 liquid = texture2D(liquidSampler, gl_TexCoord[2].st).rgb;
1134 liquid -= vec3(0.5, 0.5, 0.5);
1135 liquid = vec3(dot(liquid, modulation.rgb));
1136 liquid *= mask;
1137 fragColor.rgb = fragColor.rgb + liquid;
1138 #endif
1139 fragColor.rgb = clamp(fragColor.rgb, 0.0, 1.0) * gl_Color.rgb;
1140 fragColor.a = clamp(fragColor.a + gl_Color.a, 0.0, 1.0);
1141
1142 #ifdef LC_GAMMA
1143 fragColor.r = texture1D(gammaRed, fragColor.r).r;
1144 fragColor.g = texture1D(gammaGreen, fragColor.g).r;
1145 fragColor.b = texture1D(gammaBlue, fragColor.b).r;
1146 #endif
1147
1148 gl_FragColor = fragColor;
1149 }
1150 )"};
1151
1152 if (Config.Graphics.ColorAnimation)
1153 {
1154 vertexShader.SetMacro(key: "LC_COLOR_ANIMATION", value: "1");
1155 vertexShader.Compile();
1156 landscapeFragmentShader.SetMacro(key: "LC_COLOR_ANIMATION", value: "1");
1157 }
1158
1159 if (Config.Graphics.UseShaderGamma && !gammaDisabled)
1160 {
1161 landscapeFragmentShader.SetMacro(key: "LC_GAMMA", value: "1");
1162 }
1163
1164 landscapeFragmentShader.Compile();
1165
1166 LandscapeShader.AddShader(shader: &vertexShader);
1167 LandscapeShader.AddShader(shader: &landscapeFragmentShader);
1168 LandscapeShader.Link();
1169
1170 if (Config.Graphics.UseShaderGamma && !gammaDisabled)
1171 {
1172 CStdGLShader dummyVertexShader{CStdShader::Type::Vertex,
1173 R"(
1174 #version 120
1175
1176 void main()
1177 {
1178 gl_Position = ftransform();
1179 gl_FrontColor = gl_Color;
1180 }
1181 )"};
1182
1183 dummyVertexShader.Compile();
1184
1185 CStdGLShader dummyFragmentShader{CStdShader::Type::Fragment,
1186 R"(
1187 #version 120
1188
1189 uniform sampler1D gammaRed;
1190 uniform sampler1D gammaGreen;
1191 uniform sampler1D gammaBlue;
1192
1193 void main()
1194 {
1195 gl_FragColor.r = texture1D(gammaRed, gl_Color.r).r;
1196 gl_FragColor.g = texture1D(gammaGreen, gl_Color.g).r;
1197 gl_FragColor.b = texture1D(gammaBlue, gl_Color.b).r;
1198 gl_FragColor.a = gl_Color.a;
1199 }
1200 )"
1201 };
1202
1203 dummyFragmentShader.Compile();
1204
1205 DummyShader.AddShader(shader: &dummyVertexShader);
1206 DummyShader.AddShader(shader: &dummyFragmentShader);
1207 DummyShader.Link();
1208 }
1209
1210 const auto setUniforms = [this](CStdGLShaderProgram &program)
1211 {
1212 program.Select();
1213 program.SetUniform(key: "texIndent", value: texIndent);
1214 program.SetUniform(key: "blitOffset", value: blitOffset);
1215 program.SetUniform(key: "textureSampler", glUniform1i, args: 0);
1216
1217 if (Config.Graphics.UseShaderGamma && !gammaDisabled)
1218 {
1219 program.SetUniform(key: "gammaRed", glUniform1i, args: 3);
1220 program.SetUniform(key: "gammaGreen", glUniform1i, args: 4);
1221 program.SetUniform(key: "gammaBlue", glUniform1i, args: 5);
1222 }
1223 };
1224
1225 setUniforms(BlitShader);
1226 BlitShader.Validate();
1227
1228 setUniforms(BlitShaderMod2);
1229 BlitShaderMod2.Validate();
1230
1231 if (DummyShader)
1232 {
1233 setUniforms(DummyShader);
1234 DummyShader.Validate();
1235 }
1236
1237 setUniforms(LandscapeShader); // Last so that the shader is selected for the subsequent calls
1238
1239 LandscapeShader.SetUniform(key: "maskSampler", glUniform1i, args: 1);
1240 LandscapeShader.SetUniform(key: "liquidSampler", glUniform1i, args: 2);
1241
1242 LandscapeShader.Validate();
1243
1244 CStdShaderProgram::Deselect();
1245
1246 if (Config.Graphics.UseShaderGamma && !gammaDisabled)
1247 {
1248 const auto createTexture = [this](auto &texture, const std::int32_t offset)
1249 {
1250 texture = {{Gamma.GetSize()}, GL_R16, GL_RED, GL_UNSIGNED_SHORT};
1251 texture.Bind(offset);
1252 glEnable(GL_TEXTURE_1D);
1253
1254 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1255 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1256 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1257
1258 texture.SetData(nullptr);
1259 };
1260
1261 createTexture(GammaRedTexture, 3);
1262 createTexture(GammaGreenTexture, 4);
1263 createTexture(GammaBlueTexture, 5);
1264
1265 // Don't switch back to GL_TEXTURE0 - EnableGamma does this
1266 }
1267 }
1268 catch (const CStdRenderException &e)
1269 {
1270 LogFatalNTr(message: e.what());
1271 return Active = false;
1272 }
1273 }
1274 // restore gamma if active
1275 if (Active) EnableGamma();
1276 // done
1277 return Active;
1278}
1279
1280bool CStdGL::InvalidateDeviceObjects()
1281{
1282 // clear gamma
1283#ifdef USE_SDL_MAINLOOP
1284 if (GammaRedTexture)
1285#endif
1286 CStdGL::DisableGamma();
1287 // deactivate
1288 Active = false;
1289 // invalidate font objects
1290 // invalidate primary surfaces
1291 if (lpPrimary) lpPrimary->Clear();
1292 if (BlitShader)
1293 {
1294 BlitShader.Clear();
1295 BlitShaderMod2.Clear();
1296 LandscapeShader.Clear();
1297 DummyShader.Clear();
1298 }
1299
1300 if (GammaRedTexture)
1301 {
1302 GammaRedTexture.Clear();
1303 GammaGreenTexture.Clear();
1304 GammaBlueTexture.Clear();
1305
1306 glActiveTexture(GL_TEXTURE3);
1307 glDisable(GL_TEXTURE_1D);
1308 glActiveTexture(GL_TEXTURE4);
1309 glDisable(GL_TEXTURE_1D);
1310 glActiveTexture(GL_TEXTURE5);
1311 glDisable(GL_TEXTURE_1D);
1312 glActiveTexture(GL_TEXTURE0);
1313 }
1314
1315 gammaDisabled = false;
1316
1317 return true;
1318}
1319
1320void CStdGL::SetTexture()
1321{
1322 glBlendFunc(GL_ONE_MINUS_SRC_ALPHA,
1323 dfactor: (dwBlitMode & C4GFXBLIT_ADDITIVE) ? GL_ONE : GL_SRC_ALPHA);
1324 glEnable(GL_TEXTURE_2D);
1325}
1326
1327void CStdGL::ResetTexture()
1328{
1329 // disable texturing
1330 glDisable(GL_TEXTURE_2D);
1331}
1332
1333CStdGL *pGL = nullptr;
1334
1335bool CStdGL::OnResolutionChanged()
1336{
1337 InvalidateDeviceObjects();
1338 RestoreDeviceObjects();
1339 // Re-create primary clipper to adapt to new size.
1340 CreatePrimaryClipper();
1341 return true;
1342}
1343
1344void CStdGL::Default()
1345{
1346 CStdDDraw::Default();
1347 sfcFmt = 0;
1348 MainCtx.Clear();
1349}
1350
1351void CStdGL::BindGammaTextures()
1352{
1353 assert(GammaRedTexture);
1354
1355 GammaRedTexture.Bind(offset: 3);
1356 GammaGreenTexture.Bind(offset: 4);
1357 GammaBlueTexture.Bind(offset: 5);
1358 glActiveTexture(GL_TEXTURE0);
1359}
1360#endif
1361