1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2010-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 /* OpenGL implementation of NewGfx */
18 
19 #include "C4ForbidLibraryCompilation.h"
20 #if !defined(INC_StdGL) && !defined(USE_CONSOLE)
21 #define INC_StdGL
22 
23 #ifdef _WIN32
24 #include "platform/C4windowswrapper.h"
25 #endif
26 
27 #include <GL/glew.h>
28 
29 #ifdef USE_COCOA
30 #import "platform/ObjectiveCAssociated.h"
31 #endif
32 #include "graphics/C4Draw.h"
33 #include "graphics/C4Shader.h"
34 
35 class C4Window;
36 
37 class C4DrawGLError: public std::exception
38 {
39 public:
C4DrawGLError(const StdStrBuf & buf)40 	C4DrawGLError(const StdStrBuf& buf): Buf(buf) {}
41 	~C4DrawGLError() throw() override = default;
42 
what()43 	const char* what() const throw() override { return Buf.getData(); }
44 
45 private:
46 	StdCopyStrBuf Buf;
47 };
48 
49 // Uniform data we give the sprite shader (constants from its viewpoint)
50 enum C4SS_Uniforms
51 {
52 	C4SSU_ProjectionMatrix, // 4x4
53 	C4SSU_ModelViewMatrix,  // 4x4
54 	C4SSU_NormalMatrix,     // 3x3, transpose-inverse of modelview matrix
55 
56 	C4SSU_ClrMod, // always
57 	C4SSU_Gamma, // always
58 
59 	C4SSU_BaseTex, // C4SSC_BASE
60 	C4SSU_OverlayTex, // C4SSC_OVERLAY
61 	C4SSU_OverlayClr, // C4SSC_OVERLAY
62 
63 	C4SSU_LightTex, // C4SSC_LIGHT
64 	C4SSU_LightTransform, // C4SSC_LIGHT
65 	C4SSU_NormalTex, // C4SSC_LIGHT | C4SSC_NORMAL
66 
67 	C4SSU_AmbientTex, // C4SSC_LIGHT
68 	C4SSU_AmbientTransform, // C4SSC_LIGHT
69 	C4SSU_AmbientBrightness, // C4SSC_LIGHT
70 
71 	C4SSU_MaterialAmbient, // for meshes
72 	C4SSU_MaterialDiffuse, // for meshes
73 	C4SSU_MaterialSpecular, // for meshes
74 	C4SSU_MaterialEmission, // for meshes
75 	C4SSU_MaterialShininess, // for meshes
76 
77 	C4SSU_Bones, // for meshes
78 	C4SSU_CullMode, // for meshes
79 
80 	C4SSU_FrameCounter, // for custom shaders
81 
82 	C4SSU_Count
83 };
84 
85 // Attribute data for sprites and meshes
86 enum C4SS_Attributes
87 {
88 	C4SSA_Position, // 2d for sprites, 3d for meshes
89 	C4SSA_Normal,  // meshes only
90 	C4SSA_TexCoord, // 2d
91 	C4SSA_Color,    // sprites only, 4d
92 
93 	C4SSA_BoneIndices0,
94 	C4SSA_BoneIndices1,
95 
96 	C4SSA_BoneWeights0,
97 	C4SSA_BoneWeights1,
98 
99 	C4SSA_Count
100 };
101 
102 // one OpenGL context
103 class CStdGLCtx
104 #ifdef USE_COCOA
105 	: public ObjectiveCAssociated
106 #endif
107 {
108 public:
109 	CStdGLCtx();  // ctor
~CStdGLCtx()110 	virtual ~CStdGLCtx() { Clear(); } // dtor
111 
112 	virtual void Clear(bool multisample_change = false);               // clear objects
113 
114 #ifdef USE_WGL
115 	std::vector<int> EnumerateMultiSamples() const;
116 #endif
117 	virtual bool Init(C4Window * pWindow, C4AbstractApp *pApp);
118 
119 	virtual bool Select(bool verbose = false);              // select this context
120 	virtual void Deselect();              // select this context
121 
122 	virtual bool PageFlip();            // present scene
123 
124 protected:
125 	void SelectCommon();
126 	// this handles are declared as pointers to structs
127 	C4Window * pWindow{nullptr}; // window to draw in
128 #ifdef USE_WGL
129 	HDC hDC{nullptr};                    // device context handle
130 #elif defined(USE_SDL_MAINLOOP)
131 	void * ctx;
132 #endif
133 
134 	// Global list of all OpenGL contexts in use
135 	static std::list<CStdGLCtx*> contexts;
136 	std::list<CStdGLCtx*>::iterator this_context;
137 
138 	// VAOs available on this context
139 	std::vector<GLuint> hVAOs;
140 	// VAOs to be deleted the next time this context is being made current.
141 	std::vector<unsigned int> VAOsToBeDeleted;
142 
143 	friend class CStdGL;
144 	friend class C4Surface;
145 };
146 
147 #ifdef WITH_QT_EDITOR
148 // OpenGL context with Qt as backend. Implemented as subclass to allow co-existance with a different backend for fullscreen.
149 class CStdGLCtxQt : public CStdGLCtx
150 {
151 public:
152 	CStdGLCtxQt();
~CStdGLCtxQt()153 	~CStdGLCtxQt() override { Clear(); }
154 
155 	void Clear(bool multisample_change = false) override;               // clear objects
156 	bool Init(C4Window * pWindow, C4AbstractApp *pApp) override;
157 	bool Select(bool verbose = false) override;              // select this context
158 	void Deselect() override;              // select this context
159 	bool PageFlip() override;            // present scene
160 
161 private:
162 	class QOpenGLContext *context = nullptr;
163 	class QOffscreenSurface *surface = nullptr;
164 };
165 #endif
166 
167 // OpenGL encapsulation
168 class CStdGL : public C4Draw
169 {
170 public:
171 	CStdGL();
172 	~CStdGL() override;
173 protected:
174 
175 	int iPixelFormat;           // used pixel format
176 
177 	GLenum sfcFmt;              // texture surface format
178 	CStdGLCtx * pMainCtx{nullptr};         // main GL context
179 	CStdGLCtx *pCurrCtx;        // current context (owned if fullscreen)
180 	// texture for smooth lines
181 	GLuint lines_tex;
182 
183 	// The orthographic projection matrix
184 	StdProjectionMatrix ProjectionMatrix;
185 
186 	// programs for drawing points, lines, quads
187 
188 	// Sprite shaders -- there is a variety of shaders to avoid
189 	// conditionals in the GLSL code.
190 	C4Shader SpriteShader;
191 	C4Shader SpriteShaderMod2;
192 	C4Shader SpriteShaderBase;
193 	C4Shader SpriteShaderBaseMod2;
194 	C4Shader SpriteShaderBaseOverlay;
195 	C4Shader SpriteShaderBaseOverlayMod2;
196 
197 	C4Shader SpriteShaderLight;
198 	C4Shader SpriteShaderLightMod2;
199 	C4Shader SpriteShaderLightBase;
200 	C4Shader SpriteShaderLightBaseMod2;
201 	C4Shader SpriteShaderLightBaseOverlay;
202 	C4Shader SpriteShaderLightBaseOverlayMod2;
203 	C4Shader SpriteShaderLightBaseNormal;
204 	C4Shader SpriteShaderLightBaseNormalMod2;
205 	C4Shader SpriteShaderLightBaseNormalOverlay;
206 	C4Shader SpriteShaderLightBaseNormalOverlayMod2;
207 
208 	// Generic VBOs for rendering arbitrary points, lines and
209 	// triangles, used by PerformMultiBlt. Use more than one VBO, so that
210 	// two PerformMultiBlt calls in quick succession can use two different
211 	// buffers. Otherwise, the second call would need to wait until the
212 	// rendering pipeline has actually drained the buffer. Each buffer
213 	// starts with a fixed size. If more than this many vertices need to
214 	// be rendered (can happen e.g. for PXS), then the buffer is resized.
215 	static const unsigned int N_GENERIC_VBOS = 16;
216 	static const unsigned int GENERIC_VBO_SIZE = 3 * 64; // vertices
217 	GLuint GenericVBOs[N_GENERIC_VBOS];
218 	unsigned int GenericVBOSizes[N_GENERIC_VBOS];
219 	unsigned int CurrentVBO{0};
220 	// We need twice as much VAOs, since the sprite rendering routines work
221 	// both with and without textures (in which case we either need texture
222 	// coordinates or not).
223 	unsigned int GenericVAOs[N_GENERIC_VBOS * 2];
224 
225 	// VAO IDs currently in use.
226 	std::set<unsigned int> VAOIDs;
227 	std::set<unsigned int>::iterator NextVAOID;
228 
229 public:
230 	// Create a new (unique) VAO ID. A VAO ID is a number that identifies
231 	// a certain VAO across all OpenGL contexts. This indirection is needed
232 	// because, unlike most other GL state, VAOs are not shared between
233 	// OpenGL contexts.
234 	unsigned int GenVAOID();
235 
236 	// Free the given VAO ID, i.e. it can be re-used for new VAOs. This causes
237 	// the VAO associated with this ID to be deleted in all OpenGL contexts.
238 	void FreeVAOID(unsigned int vaoid);
239 
240 	// Return a VAO with the given vao ID in the "vao" output variable.
241 	// If the function returns false, the VAO was newly created, otherwise
242 	// an existing VAO is returned.
243 	bool GetVAO(unsigned int vaoid, GLuint& vao);
244 
245 	// General
246 	void Clear() override;
247 	void Default() override ;
248 	bool OnResolutionChanged(unsigned int iXRes, unsigned int iYRes) override; // reinit clipper for new resolution
249 	// Clipper
250 	bool UpdateClipper() override; // set current clipper to render target
GetProjectionMatrix()251 	const StdProjectionMatrix& GetProjectionMatrix() const { return ProjectionMatrix; }
252 	bool PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterialLoader& loader, StdMeshMaterial& mat) override;
253 	// Surface
254 	bool PrepareRendering(C4Surface * sfcToSurface) override; // check if/make rendering possible to given surface
255 	bool PrepareSpriteShader(C4Shader& shader, const char* name, int ssc, C4GroupSet* pGroups, const char* const* additionalDefines, const char* const* additionalSlices) override;
256 	bool EnsureMainContextSelected() override;
257 
258 	CStdGLCtx *CreateContext(C4Window * pWindow, C4AbstractApp *pApp) override;
259 	// Blit
260 	void SetupMultiBlt(C4ShaderCall& call, const C4BltTransform* pTransform, GLuint baseTex, GLuint overlayTex, GLuint normalTex, DWORD dwOverlayModClr, StdProjectionMatrix* out_modelview);
261 	void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, DWORD dwPlayerColor, C4BltTransform* pTransform) override;
262 	void FillBG(DWORD dwClr=0) override;
263 	// Drawing
264 	void PerformMultiPix(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices, C4ShaderCall* shader_call) override;
265 	void PerformMultiLines(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices, float width, C4ShaderCall* shader_call) override;
266 	void PerformMultiTris(C4Surface* sfcTarget, const C4BltVertex* vertices, unsigned int n_vertices, const C4BltTransform* pTransform, C4TexRef* pTex, C4TexRef* pOverlay, C4TexRef* pNormal, DWORD dwOverlayClrMod, C4ShaderCall* shader_call) override;
267 	void PerformMultiBlt(C4Surface* sfcTarget, DrawOperation op, const C4BltVertex* vertices, unsigned int n_vertices, bool has_tex, C4ShaderCall* shader_call);
268 	// device objects
269 	bool RestoreDeviceObjects() override;    // restore device dependent objects
270 	bool InvalidateDeviceObjects() override; // free device dependent objects
DeviceReady()271 	bool DeviceReady() override { return !!pMainCtx; }
272 	bool InitShaders(C4GroupSet* pGroups); // load shaders from given group
273 	C4Shader* GetSpriteShader(int ssc);
274 	C4Shader* GetSpriteShader(bool haveBase, bool haveOverlay, bool haveNormal);
275 
276 	struct
277 	{
278 		bool LowMaxVertexUniformCount;
279 		bool ForceSoftwareTransform;
280 	} Workarounds;
281 	void ObjectLabel(uint32_t identifier, uint32_t name, int32_t length, const char * label);
282 
283 protected:
284 	bool CheckGLError(const char *szAtOp);
285 	const char* GLErrorString(GLenum code);
286 	bool Error(const char *szMsg) override;
287 
288 	friend class C4Surface;
289 	friend class C4TexRef;
290 	friend class C4Pattern;
291 	friend class CStdGLCtx;
292 	friend class C4StartupOptionsDlg;
293 	friend class C4FullScreen;
294 	friend class C4Window;
295 	friend class C4ShaderCall;
296 	friend class C4FoWRegion;
297 #ifdef WITH_QT_EDITOR
298 	friend class CStdGLCtxQt;
299 #endif
300 };
301 
302 // Global access pointer
303 extern CStdGL *pGL;
304 
305 #endif // INC_StdGL
306