1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2021, 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#pragma once
21
22#ifndef USE_CONSOLE
23
24#include <GL/glew.h>
25
26#if defined(__APPLE__)
27#include <OpenGL/glu.h>
28#else
29#include <GL/glu.h>
30#endif
31#include <StdDDraw2.h>
32
33#include <concepts>
34#include <type_traits>
35
36class CStdWindow;
37
38class CStdGLShader : public CStdShader
39{
40public:
41 using CStdShader::CStdShader;
42
43 void Compile() override;
44 void Clear() override;
45
46 virtual int64_t GetHandle() const override { return shader; }
47
48protected:
49 virtual void PrepareSource();
50
51protected:
52 GLuint shader{GL_NONE};
53};
54
55class CStdGLShaderProgram : public CStdShaderProgram
56{
57public:
58 using CStdShaderProgram::CStdShaderProgram;
59
60 explicit operator bool() const override { return /*glIsProgram(*/shaderProgram/*)*/; }
61
62 void Link() override;
63 void Validate() override;
64 void Clear() override;
65
66 void EnsureProgram() override;
67
68 template<typename Func, typename... Args> requires std::invocable<Func, GLint, Args...>
69 bool SetAttribute(std::string_view key, Func function, Args... args)
70 {
71 return SetAttribute(key, &CStdGLShaderProgram::attributeLocations, glGetAttribLocation, function, args...);
72 }
73
74 template<typename Func, typename... Args> requires std::invocable<Func, GLint, Args...>
75 bool SetUniform(std::string_view key, Func function, Args... args)
76 {
77 return SetAttribute(key, &CStdGLShaderProgram::uniformLocations, glGetUniformLocation, function, args...);
78 }
79
80 bool SetUniform(std::string_view key, float value) { return SetUniform(key, glUniform1f, args: value); }
81
82 void EnterGroup(std::string_view name) { group.assign(svt: name).append(s: "."); }
83 void LeaveGroup() { group.clear(); }
84
85 virtual int64_t GetProgram() const override { return shaderProgram; }
86
87protected:
88 bool AddShaderInt(CStdShader *shader) override;
89 void OnSelect() override;
90 void OnDeselect() override;
91
92 using Locations = std::unordered_map<std::string, GLint>;
93
94 template<typename MapFunc, typename SetFunc, typename... Args>
95 requires (std::is_invocable_r_v<GLint, MapFunc, GLuint, const char *> && std::invocable<SetFunc, GLint, Args...>)
96 bool SetAttribute(std::string_view key, Locations CStdGLShaderProgram::*locationPointer, MapFunc mapFunction, SetFunc setFunction, Args... args)
97 {
98 assert(shaderProgram);
99
100 std::string realKey{group};
101 realKey.append(svt: key);
102
103 GLint location;
104 Locations &locations{this->*locationPointer};
105 if (auto it = locations.find(x: realKey); it != locations.end())
106 {
107 location = it->second;
108 assert(location != -1);
109 }
110 else
111 {
112 location = mapFunction(shaderProgram, realKey.c_str());
113 if (location == -1)
114 {
115 return false;
116 }
117
118 locations.emplace(args&: realKey, args&: location);
119 }
120 setFunction(location, args...);
121
122 return true;
123 }
124
125private:
126 void CheckStatus(GLenum type);
127
128protected:
129 GLuint shaderProgram{GL_NONE};
130
131 Locations attributeLocations;
132 Locations uniformLocations;
133
134 std::string group;
135};
136
137// one OpenGL texture
138template<GLenum T, std::size_t Dimensions>
139class CStdGLTexture
140{
141 static_assert(Dimensions >= 1 && Dimensions <= 3);
142
143public:
144 static constexpr GLenum Target{T};
145
146public:
147 class Exception : public CStdRenderException
148 {
149 public:
150 using CStdRenderException::CStdRenderException;
151 };
152
153public:
154 CStdGLTexture() = default;
155
156 CStdGLTexture(std::array<std::int32_t, Dimensions> dimensions, GLenum internalFormat, GLenum format, GLenum type);
157
158 CStdGLTexture(const CStdGLTexture &) = delete;
159 CStdGLTexture &operator=(const CStdGLTexture &) = delete;
160
161 CStdGLTexture(CStdGLTexture &&other)
162 : CStdGLTexture{}
163 {
164 swap(*this, other);
165 }
166
167 CStdGLTexture &operator=(CStdGLTexture &&other)
168 {
169 CStdGLTexture temp{std::move(other)};
170 swap(*this, temp);
171 return *this;
172 }
173
174 ~CStdGLTexture();
175
176public:
177 GLuint GetTexture() const { return texture; }
178
179 void Bind(GLenum offset);
180 void SetData(const void *const data);
181 void UpdateData(const void *data);
182
183 void Clear();
184
185public:
186 explicit operator bool() const { return texture; }
187
188private:
189 static void ThrowIfGLError();
190 static auto GetSetDataFunction();
191 static auto GetUpdateDataFunction();
192
193private:
194 std::array<std::int32_t, Dimensions> dimensions;
195 GLenum internalFormat;
196 GLenum format;
197 GLenum type;
198 GLuint texture{GL_NONE};
199
200private:
201 friend void swap(CStdGLTexture &first, CStdGLTexture &second)
202 {
203 using std::swap;
204 swap(first.dimensions, second.dimensions);
205 swap(first.internalFormat, second.internalFormat);
206 swap(first.format, second.format);
207 swap(first.type, second.type);
208 swap(first.texture, second.texture);
209 }
210};
211
212// one OpenGL context
213class CStdGLCtx
214{
215public:
216 CStdGLCtx();
217 ~CStdGLCtx() { Clear(); }
218
219 void Clear(); // clear objects
220#ifdef _WIN32
221 bool Init(CStdWindow *pWindow, CStdApp *pApp, HWND hWindow = nullptr);
222#else
223 bool Init(CStdWindow *pWindow, CStdApp *pApp);
224#endif
225
226 bool Select(bool verbose = false, bool selectOnly = false); // select this context
227 void Deselect(bool secondary = false); // select this context
228 bool UpdateSize(); // get new size from hWnd
229
230 bool PageFlip(); // present scene
231 void Finish();
232
233protected:
234 void DoDeselect();
235 // this handles are declared as pointers to structs
236 CStdWindow *pWindow; // window to draw in
237#ifdef _WIN32
238 HGLRC hrc; // rendering context
239 HWND hWindow; // used if pWindow==nullptr
240 HDC hDC; // device context handle
241#elif defined(USE_X11)
242 typedef struct __GLXcontextRec *GLXContext;
243 GLXContext ctx;
244#elif defined(USE_SDL_MAINLOOP)
245 /*SDL_GLContext*/ void *ctx;
246#endif
247 int cx, cy; // context window size
248
249 friend class CStdGL;
250 friend class C4Surface;
251};
252
253// OpenGL encapsulation
254class CStdGL : public CStdDDraw
255{
256public:
257 CStdGL();
258 ~CStdGL();
259 void PageFlip() override;
260
261protected:
262 GLenum sfcFmt; // texture surface format
263 CStdGLCtx MainCtx; // main GL context
264 CStdGLCtx *pCurrCtx; // current context
265 CStdGLShaderProgram BlitShader;
266 CStdGLShaderProgram BlitShaderMod2;
267 CStdGLShaderProgram LandscapeShader;
268 CStdGLShaderProgram DummyShader;
269 CStdGLTexture<GL_TEXTURE_1D, 1> GammaRedTexture;
270 CStdGLTexture<GL_TEXTURE_1D, 1> GammaGreenTexture;
271 CStdGLTexture<GL_TEXTURE_1D, 1> GammaBlueTexture;
272 bool gammaDisabled{false};
273
274public:
275 // General
276 void Clear() override;
277 void Default() override;
278 virtual int GetEngine() override { return 1; } // get indexed engine
279 std::string_view GetEngineName() const override { return "CStdGL"; }
280 virtual bool OnResolutionChanged() override; // reinit OpenGL and window for new resolution
281
282 // Clipper
283 bool UpdateClipper() override; // set current clipper to render target
284
285 // Surface
286 bool PrepareRendering(C4Surface *sfcToSurface) override; // check if/make rendering possible to given surface
287 CStdGLCtx &GetMainCtx() { return MainCtx; }
288 virtual CStdGLCtx *CreateContext(CStdWindow *pWindow, CStdApp *pApp) override;
289#ifdef _WIN32
290 virtual CStdGLCtx *CreateContext(HWND hWindow, CStdApp *pApp) override;
291#endif
292
293 // Blit
294 void PerformBlt(CBltData &rBltData, C4TexRef *pTex, uint32_t dwModClr, bool fMod2, bool fExact) override;
295 virtual void BlitLandscape(C4Surface *sfcSource, C4Surface *sfcSource2, C4Surface *sfcLiquidAnimation, int fx, int fy,
296 C4Surface *sfcTarget, int tx, int ty, int wdt, int hgt) override;
297 void FillBG(uint32_t dwClr = 0) override;
298
299 // Drawing
300 void DrawQuadDw(C4Surface *sfcTarget, int *ipVtx, uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3, uint32_t dwClr4) override;
301 void DrawLineDw(C4Surface *sfcTarget, float x1, float y1, float x2, float y2, uint32_t dwClr) override;
302 void DrawPixInt(C4Surface *sfcDest, float tx, float ty, uint32_t dwCol) override;
303
304 // Gamma
305 void DisableGamma() override;
306 void EnableGamma() override;
307 virtual bool ApplyGammaRamp(CGammaControl &ramp, bool force) override;
308 virtual bool SaveDefaultGammaRamp(CStdWindow *window) override;
309
310 // device objects
311 bool RestoreDeviceObjects() override; // init/restore device dependent objects
312 bool InvalidateDeviceObjects() override; // free device dependent objects
313 void SetTexture() override;
314 void ResetTexture() override;
315#ifdef _WIN32
316 bool DeviceReady() override { return !!MainCtx.hrc; }
317#elif defined(USE_X11)
318 bool DeviceReady() override { return !!MainCtx.ctx; }
319#else
320 bool DeviceReady() override { return true; } // SDL
321#endif
322
323protected:
324 bool CreatePrimarySurfaces() override;
325 bool CreateDirectDraw() override;
326
327#ifdef USE_X11
328 // Size of gamma ramps
329 int gammasize{};
330#endif
331
332private:
333 bool ApplyGammaRampToMonitor(CGammaControl &ramp, bool force);
334 bool SaveDefaultGammaRampToMonitor(CStdWindow *window);
335 void BindGammaTextures();
336
337 friend class C4Surface;
338 friend class C4TexRef;
339 friend class CPattern;
340 friend class CStdGLCtx;
341 friend class C4StartupOptionsDlg;
342 friend class C4FullScreen;
343 friend class CStdWindow;
344};
345
346// Global access pointer
347extern CStdGL *pGL;
348
349#endif
350