1 | #include "backend_opengl.h" |
2 | |
3 | #include <engine/graphics.h> |
4 | |
5 | #include <engine/client/backend_sdl.h> |
6 | |
7 | #include <base/detect.h> |
8 | #include <base/system.h> |
9 | |
10 | #if defined(BACKEND_AS_OPENGL_ES) || !defined(CONF_BACKEND_OPENGL_ES) |
11 | |
12 | #include <engine/client/backend/opengl/opengl_sl.h> |
13 | #include <engine/client/backend/opengl/opengl_sl_program.h> |
14 | #include <engine/client/blocklist_driver.h> |
15 | |
16 | #include <engine/client/backend/glsl_shader_compiler.h> |
17 | |
18 | #include <engine/gfx/image_manipulation.h> |
19 | |
20 | #ifndef BACKEND_AS_OPENGL_ES |
21 | #include <GL/glew.h> |
22 | #else |
23 | #include <GLES3/gl3.h> |
24 | #define GL_TEXTURE_2D_ARRAY_EXT GL_TEXTURE_2D_ARRAY |
25 | // GLES doesn't support GL_QUADS, but the code is also never executed |
26 | #define GL_QUADS GL_TRIANGLES |
27 | #ifndef CONF_BACKEND_OPENGL_ES3 |
28 | #include <GLES/gl.h> |
29 | #define glOrtho glOrthof |
30 | #else |
31 | #define BACKEND_GL_MODERN_API 1 |
32 | #endif |
33 | #endif |
34 | |
35 | // ------------ CCommandProcessorFragment_OpenGL |
36 | void CCommandProcessorFragment_OpenGL::Cmd_Update_Viewport(const CCommandBuffer::SCommand_Update_Viewport *pCommand) |
37 | { |
38 | if(pCommand->m_ByResize) |
39 | { |
40 | m_CanvasWidth = (uint32_t)pCommand->m_Width; |
41 | m_CanvasHeight = (uint32_t)pCommand->m_Height; |
42 | } |
43 | glViewport(x: pCommand->m_X, y: pCommand->m_Y, width: pCommand->m_Width, height: pCommand->m_Height); |
44 | } |
45 | |
46 | size_t CCommandProcessorFragment_OpenGL::GLFormatToPixelSize(int GLFormat) |
47 | { |
48 | switch(GLFormat) |
49 | { |
50 | case GL_RGBA: return 4; |
51 | case GL_RGB: return 3; |
52 | case GL_RED: return 1; |
53 | case GL_ALPHA: return 1; |
54 | default: return 4; |
55 | } |
56 | } |
57 | |
58 | bool CCommandProcessorFragment_OpenGL::IsTexturedState(const CCommandBuffer::SState &State) |
59 | { |
60 | return State.m_Texture >= 0 && State.m_Texture < (int)m_vTextures.size(); |
61 | } |
62 | |
63 | void CCommandProcessorFragment_OpenGL::SetState(const CCommandBuffer::SState &State, bool Use2DArrayTextures) |
64 | { |
65 | #ifndef BACKEND_GL_MODERN_API |
66 | // blend |
67 | switch(State.m_BlendMode) |
68 | { |
69 | case CCommandBuffer::BLEND_NONE: |
70 | glDisable(GL_BLEND); |
71 | break; |
72 | case CCommandBuffer::BLEND_ALPHA: |
73 | glEnable(GL_BLEND); |
74 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
75 | break; |
76 | case CCommandBuffer::BLEND_ADDITIVE: |
77 | glEnable(GL_BLEND); |
78 | glBlendFunc(GL_SRC_ALPHA, GL_ONE); |
79 | break; |
80 | default: |
81 | dbg_msg(sys: "render" , fmt: "unknown blendmode %d\n" , State.m_BlendMode); |
82 | }; |
83 | m_LastBlendMode = State.m_BlendMode; |
84 | |
85 | // clip |
86 | if(State.m_ClipEnable) |
87 | { |
88 | glScissor(x: State.m_ClipX, y: State.m_ClipY, width: State.m_ClipW, height: State.m_ClipH); |
89 | glEnable(GL_SCISSOR_TEST); |
90 | m_LastClipEnable = true; |
91 | } |
92 | else if(m_LastClipEnable) |
93 | { |
94 | // Don't disable it always |
95 | glDisable(GL_SCISSOR_TEST); |
96 | m_LastClipEnable = false; |
97 | } |
98 | |
99 | glDisable(GL_TEXTURE_2D); |
100 | if(!m_HasShaders) |
101 | { |
102 | if(m_Has3DTextures) |
103 | glDisable(GL_TEXTURE_3D); |
104 | if(m_Has2DArrayTextures) |
105 | { |
106 | glDisable(cap: m_2DArrayTarget); |
107 | } |
108 | } |
109 | |
110 | if(m_HasShaders && IsNewApi()) |
111 | { |
112 | glBindSampler(0, 0); |
113 | } |
114 | |
115 | // texture |
116 | if(IsTexturedState(State)) |
117 | { |
118 | if(!Use2DArrayTextures) |
119 | { |
120 | glEnable(GL_TEXTURE_2D); |
121 | glBindTexture(GL_TEXTURE_2D, texture: m_vTextures[State.m_Texture].m_Tex); |
122 | |
123 | if(m_vTextures[State.m_Texture].m_LastWrapMode != State.m_WrapMode) |
124 | { |
125 | switch(State.m_WrapMode) |
126 | { |
127 | case CCommandBuffer::WRAP_REPEAT: |
128 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); |
129 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); |
130 | break; |
131 | case CCommandBuffer::WRAP_CLAMP: |
132 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
133 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
134 | break; |
135 | default: |
136 | dbg_msg(sys: "render" , fmt: "unknown wrapmode %d\n" , State.m_WrapMode); |
137 | }; |
138 | m_vTextures[State.m_Texture].m_LastWrapMode = State.m_WrapMode; |
139 | } |
140 | } |
141 | else |
142 | { |
143 | if(m_Has2DArrayTextures) |
144 | { |
145 | if(!m_HasShaders) |
146 | glEnable(cap: m_2DArrayTarget); |
147 | glBindTexture(target: m_2DArrayTarget, texture: m_vTextures[State.m_Texture].m_Tex2DArray); |
148 | } |
149 | else if(m_Has3DTextures) |
150 | { |
151 | if(!m_HasShaders) |
152 | glEnable(GL_TEXTURE_3D); |
153 | glBindTexture(GL_TEXTURE_3D, texture: m_vTextures[State.m_Texture].m_Tex2DArray); |
154 | } |
155 | else |
156 | { |
157 | dbg_msg(sys: "opengl" , fmt: "ERROR: this call should not happen." ); |
158 | } |
159 | } |
160 | } |
161 | |
162 | // screen mapping |
163 | glMatrixMode(GL_PROJECTION); |
164 | glLoadIdentity(); |
165 | glOrtho(left: State.m_ScreenTL.x, right: State.m_ScreenBR.x, bottom: State.m_ScreenBR.y, top: State.m_ScreenTL.y, zNear: -10.0f, zFar: 10.f); |
166 | #endif |
167 | } |
168 | |
169 | static void ParseVersionString(EBackendType BackendType, const char *pStr, int &VersionMajor, int &VersionMinor, int &VersionPatch) |
170 | { |
171 | if(pStr) |
172 | { |
173 | // if backend is GLES, it starts with "OpenGL ES " or OpenGL ES-CM for older contexts, rest is the same |
174 | if(BackendType == BACKEND_TYPE_OPENGL_ES) |
175 | { |
176 | int StrLenGLES = str_length(str: "OpenGL ES " ); |
177 | int StrLenGLESCM = str_length(str: "OpenGL ES-CM " ); |
178 | if(str_comp_num(a: pStr, b: "OpenGL ES " , num: StrLenGLES) == 0) |
179 | pStr += StrLenGLES; |
180 | else if(str_comp_num(a: pStr, b: "OpenGL ES-CM " , num: StrLenGLESCM) == 0) |
181 | pStr += StrLenGLESCM; |
182 | } |
183 | |
184 | char aCurNumberStr[32]; |
185 | size_t CurNumberStrLen = 0; |
186 | size_t TotalNumbersPassed = 0; |
187 | int aNumbers[3] = {0}; |
188 | bool LastWasNumber = false; |
189 | while(*pStr && TotalNumbersPassed < 3) |
190 | { |
191 | if(str_isnum(c: *pStr)) |
192 | { |
193 | aCurNumberStr[CurNumberStrLen++] = (char)*pStr; |
194 | LastWasNumber = true; |
195 | } |
196 | else if(LastWasNumber && (*pStr == '.' || *pStr == ' ')) |
197 | { |
198 | if(CurNumberStrLen > 0) |
199 | { |
200 | aCurNumberStr[CurNumberStrLen] = 0; |
201 | aNumbers[TotalNumbersPassed++] = str_toint(str: aCurNumberStr); |
202 | CurNumberStrLen = 0; |
203 | } |
204 | |
205 | LastWasNumber = false; |
206 | |
207 | if(*pStr != '.') |
208 | break; |
209 | } |
210 | else |
211 | { |
212 | break; |
213 | } |
214 | |
215 | ++pStr; |
216 | } |
217 | |
218 | VersionMajor = aNumbers[0]; |
219 | VersionMinor = aNumbers[1]; |
220 | VersionPatch = aNumbers[2]; |
221 | } |
222 | } |
223 | |
224 | #ifndef BACKEND_AS_OPENGL_ES |
225 | static const char *GetGLErrorName(GLenum Type) |
226 | { |
227 | if(Type == GL_DEBUG_TYPE_ERROR) |
228 | return "ERROR" ; |
229 | else if(Type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) |
230 | return "DEPRECATED BEHAVIOR" ; |
231 | else if(Type == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR) |
232 | return "UNDEFINED BEHAVIOR" ; |
233 | else if(Type == GL_DEBUG_TYPE_PORTABILITY) |
234 | return "PORTABILITY" ; |
235 | else if(Type == GL_DEBUG_TYPE_PERFORMANCE) |
236 | return "PERFORMANCE" ; |
237 | else if(Type == GL_DEBUG_TYPE_OTHER) |
238 | return "OTHER" ; |
239 | else if(Type == GL_DEBUG_TYPE_MARKER) |
240 | return "MARKER" ; |
241 | else if(Type == GL_DEBUG_TYPE_PUSH_GROUP) |
242 | return "PUSH_GROUP" ; |
243 | else if(Type == GL_DEBUG_TYPE_POP_GROUP) |
244 | return "POP_GROUP" ; |
245 | return "UNKNOWN" ; |
246 | }; |
247 | |
248 | static const char *GetGLSeverity(GLenum Type) |
249 | { |
250 | if(Type == GL_DEBUG_SEVERITY_HIGH) |
251 | return "high" ; // All OpenGL Errors, shader compilation/linking errors, or highly-dangerous undefined behavior |
252 | else if(Type == GL_DEBUG_SEVERITY_MEDIUM) |
253 | return "medium" ; // Major performance warnings, shader compilation/linking warnings, or the use of deprecated functionality |
254 | else if(Type == GL_DEBUG_SEVERITY_LOW) |
255 | return "low" ; // Redundant state change performance warning, or unimportant undefined behavior |
256 | else if(Type == GL_DEBUG_SEVERITY_NOTIFICATION) |
257 | return "notification" ; // Anything that isn't an error or performance issue. |
258 | |
259 | return "unknown" ; |
260 | } |
261 | |
262 | static void GLAPIENTRY |
263 | GfxOpenGLMessageCallback(GLenum Source, |
264 | GLenum Type, |
265 | GLuint Id, |
266 | GLenum Severity, |
267 | GLsizei Length, |
268 | const GLchar *pMsg, |
269 | const void *pUserParam) |
270 | { |
271 | dbg_msg(sys: "gfx" , fmt: "[%s] (importance: %s) %s" , GetGLErrorName(Type), GetGLSeverity(Type: Severity), pMsg); |
272 | } |
273 | #endif |
274 | |
275 | bool CCommandProcessorFragment_OpenGL::GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) |
276 | { |
277 | if(m_CanvasWidth == 0 || m_CanvasHeight == 0) |
278 | { |
279 | return false; |
280 | } |
281 | else |
282 | { |
283 | Width = m_CanvasWidth; |
284 | Height = m_CanvasHeight; |
285 | Format = CImageInfo::FORMAT_RGBA; |
286 | vDstData.resize(new_size: (size_t)Width * (Height + 1) * 4); // +1 for flipping image |
287 | glReadBuffer(GL_FRONT); |
288 | GLint Alignment; |
289 | glGetIntegerv(GL_PACK_ALIGNMENT, params: &Alignment); |
290 | glPixelStorei(GL_PACK_ALIGNMENT, param: 1); |
291 | glReadPixels(x: 0, y: 0, width: m_CanvasWidth, height: m_CanvasHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels: vDstData.data()); |
292 | glPixelStorei(GL_PACK_ALIGNMENT, param: Alignment); |
293 | |
294 | uint8_t *pTempRow = vDstData.data() + Width * Height * 4; |
295 | for(uint32_t Y = 0; Y < Height / 2; ++Y) |
296 | { |
297 | mem_copy(dest: pTempRow, source: vDstData.data() + Y * Width * 4, size: Width * 4); |
298 | mem_copy(dest: vDstData.data() + Y * Width * 4, source: vDstData.data() + ((Height - Y) - 1) * Width * 4, size: Width * 4); |
299 | mem_copy(dest: vDstData.data() + ((Height - Y) - 1) * Width * 4, source: pTempRow, size: Width * 4); |
300 | } |
301 | |
302 | return true; |
303 | } |
304 | } |
305 | |
306 | bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand) |
307 | { |
308 | m_IsOpenGLES = pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL_ES; |
309 | |
310 | *pCommand->m_pReadPresentedImageDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) { |
311 | return GetPresentedImageData(Width, Height, Format, vDstData); |
312 | }; |
313 | |
314 | const char *pVendorString = (const char *)glGetString(GL_VENDOR); |
315 | dbg_msg(sys: "opengl" , fmt: "Vendor string: %s" , pVendorString); |
316 | |
317 | // check what this context can do |
318 | const char *pVersionString = (const char *)glGetString(GL_VERSION); |
319 | dbg_msg(sys: "opengl" , fmt: "Version string: %s" , pVersionString); |
320 | |
321 | const char *pRendererString = (const char *)glGetString(GL_RENDERER); |
322 | |
323 | str_copy(dst: pCommand->m_pVendorString, src: pVendorString, dst_size: gs_GpuInfoStringSize); |
324 | str_copy(dst: pCommand->m_pVersionString, src: pVersionString, dst_size: gs_GpuInfoStringSize); |
325 | str_copy(dst: pCommand->m_pRendererString, src: pRendererString, dst_size: gs_GpuInfoStringSize); |
326 | |
327 | // parse version string |
328 | ParseVersionString(BackendType: pCommand->m_RequestedBackend, pStr: pVersionString, VersionMajor&: pCommand->m_pCapabilities->m_ContextMajor, VersionMinor&: pCommand->m_pCapabilities->m_ContextMinor, VersionPatch&: pCommand->m_pCapabilities->m_ContextPatch); |
329 | |
330 | *pCommand->m_pInitError = 0; |
331 | |
332 | int BlocklistMajor = -1, BlocklistMinor = -1, BlocklistPatch = -1; |
333 | bool RequiresWarning = false; |
334 | const char *pErrString = ParseBlocklistDriverVersions(pVendorStr: pVendorString, pVersionStr: pVersionString, BlocklistMajor, BlocklistMinor, BlocklistPatch, RequiresWarning); |
335 | // if the driver is buggy, and the requested GL version is the default, fallback |
336 | if(pErrString != NULL && pCommand->m_RequestedMajor == 3 && pCommand->m_RequestedMinor == 0 && pCommand->m_RequestedPatch == 0) |
337 | { |
338 | // if not already in the error state, set the GL version |
339 | if(g_Config.m_GfxDriverIsBlocked == 0) |
340 | { |
341 | // fallback to known good GL version |
342 | pCommand->m_pCapabilities->m_ContextMajor = BlocklistMajor; |
343 | pCommand->m_pCapabilities->m_ContextMinor = BlocklistMinor; |
344 | pCommand->m_pCapabilities->m_ContextPatch = BlocklistPatch; |
345 | |
346 | // set backend error string |
347 | if(RequiresWarning) |
348 | *pCommand->m_pErrStringPtr = pErrString; |
349 | *pCommand->m_pInitError = -2; |
350 | |
351 | g_Config.m_GfxDriverIsBlocked = 1; |
352 | } |
353 | } |
354 | // if the driver was in a blocked error state, but is not anymore, reset all config variables |
355 | else if(pErrString == NULL && g_Config.m_GfxDriverIsBlocked == 1) |
356 | { |
357 | pCommand->m_pCapabilities->m_ContextMajor = 3; |
358 | pCommand->m_pCapabilities->m_ContextMinor = 0; |
359 | pCommand->m_pCapabilities->m_ContextPatch = 0; |
360 | |
361 | // tell the caller to reinitialize the context |
362 | *pCommand->m_pInitError = -2; |
363 | |
364 | g_Config.m_GfxDriverIsBlocked = 0; |
365 | } |
366 | |
367 | int MajorV = pCommand->m_pCapabilities->m_ContextMajor; |
368 | |
369 | if(pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL) |
370 | { |
371 | #ifndef BACKEND_AS_OPENGL_ES |
372 | int MinorV = pCommand->m_pCapabilities->m_ContextMinor; |
373 | if(*pCommand->m_pInitError == 0) |
374 | { |
375 | if(MajorV < pCommand->m_RequestedMajor) |
376 | { |
377 | *pCommand->m_pInitError = -2; |
378 | } |
379 | else if(MajorV == pCommand->m_RequestedMajor) |
380 | { |
381 | if(MinorV < pCommand->m_RequestedMinor) |
382 | { |
383 | *pCommand->m_pInitError = -2; |
384 | } |
385 | else if(MinorV == pCommand->m_RequestedMinor) |
386 | { |
387 | int PatchV = pCommand->m_pCapabilities->m_ContextPatch; |
388 | if(PatchV < pCommand->m_RequestedPatch) |
389 | { |
390 | *pCommand->m_pInitError = -2; |
391 | } |
392 | } |
393 | } |
394 | } |
395 | |
396 | if(*pCommand->m_pInitError == 0) |
397 | { |
398 | MajorV = pCommand->m_RequestedMajor; |
399 | MinorV = pCommand->m_RequestedMinor; |
400 | |
401 | pCommand->m_pCapabilities->m_2DArrayTexturesAsExtension = false; |
402 | pCommand->m_pCapabilities->m_NPOTTextures = true; |
403 | pCommand->m_pCapabilities->m_TrianglesAsQuads = false; |
404 | |
405 | if(MajorV >= 4 || (MajorV == 3 && MinorV == 3)) |
406 | { |
407 | pCommand->m_pCapabilities->m_TileBuffering = true; |
408 | pCommand->m_pCapabilities->m_QuadBuffering = true; |
409 | pCommand->m_pCapabilities->m_TextBuffering = true; |
410 | pCommand->m_pCapabilities->m_QuadContainerBuffering = true; |
411 | pCommand->m_pCapabilities->m_ShaderSupport = true; |
412 | |
413 | pCommand->m_pCapabilities->m_MipMapping = true; |
414 | pCommand->m_pCapabilities->m_3DTextures = true; |
415 | pCommand->m_pCapabilities->m_2DArrayTextures = true; |
416 | |
417 | pCommand->m_pCapabilities->m_TrianglesAsQuads = true; |
418 | } |
419 | else if(MajorV == 3) |
420 | { |
421 | pCommand->m_pCapabilities->m_MipMapping = true; |
422 | // check for context native 2D array texture size |
423 | pCommand->m_pCapabilities->m_3DTextures = false; |
424 | pCommand->m_pCapabilities->m_2DArrayTextures = false; |
425 | pCommand->m_pCapabilities->m_ShaderSupport = true; |
426 | |
427 | int TextureLayers = 0; |
428 | glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, params: &TextureLayers); |
429 | if(TextureLayers >= 256) |
430 | { |
431 | pCommand->m_pCapabilities->m_2DArrayTextures = true; |
432 | } |
433 | |
434 | int Texture3DSize = 0; |
435 | glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, params: &Texture3DSize); |
436 | if(Texture3DSize >= 256) |
437 | { |
438 | pCommand->m_pCapabilities->m_3DTextures = true; |
439 | } |
440 | |
441 | if(!pCommand->m_pCapabilities->m_3DTextures && !pCommand->m_pCapabilities->m_2DArrayTextures) |
442 | { |
443 | *pCommand->m_pInitError = -2; |
444 | pCommand->m_pCapabilities->m_ContextMajor = 1; |
445 | pCommand->m_pCapabilities->m_ContextMinor = 5; |
446 | pCommand->m_pCapabilities->m_ContextPatch = 0; |
447 | } |
448 | |
449 | pCommand->m_pCapabilities->m_TileBuffering = pCommand->m_pCapabilities->m_2DArrayTextures; |
450 | pCommand->m_pCapabilities->m_QuadBuffering = false; |
451 | pCommand->m_pCapabilities->m_TextBuffering = false; |
452 | pCommand->m_pCapabilities->m_QuadContainerBuffering = false; |
453 | } |
454 | else if(MajorV == 2) |
455 | { |
456 | pCommand->m_pCapabilities->m_MipMapping = true; |
457 | // check for context extension: 2D array texture and its max size |
458 | pCommand->m_pCapabilities->m_3DTextures = false; |
459 | pCommand->m_pCapabilities->m_2DArrayTextures = false; |
460 | |
461 | pCommand->m_pCapabilities->m_ShaderSupport = false; |
462 | |
463 | int Texture3DSize = 0; |
464 | glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, params: &Texture3DSize); |
465 | if(Texture3DSize >= 256) |
466 | { |
467 | pCommand->m_pCapabilities->m_3DTextures = true; |
468 | } |
469 | |
470 | pCommand->m_pCapabilities->m_TileBuffering = false; |
471 | pCommand->m_pCapabilities->m_QuadBuffering = false; |
472 | pCommand->m_pCapabilities->m_TextBuffering = false; |
473 | pCommand->m_pCapabilities->m_QuadContainerBuffering = false; |
474 | |
475 | pCommand->m_pCapabilities->m_NPOTTextures = GLEW_ARB_texture_non_power_of_two || pCommand->m_GlewMajor > 2; |
476 | |
477 | if(!pCommand->m_pCapabilities->m_NPOTTextures || (!pCommand->m_pCapabilities->m_3DTextures && !pCommand->m_pCapabilities->m_2DArrayTextures)) |
478 | { |
479 | *pCommand->m_pInitError = -2; |
480 | pCommand->m_pCapabilities->m_ContextMajor = 1; |
481 | pCommand->m_pCapabilities->m_ContextMinor = 5; |
482 | pCommand->m_pCapabilities->m_ContextPatch = 0; |
483 | } |
484 | } |
485 | else if(MajorV < 2) |
486 | { |
487 | pCommand->m_pCapabilities->m_TileBuffering = false; |
488 | pCommand->m_pCapabilities->m_QuadBuffering = false; |
489 | pCommand->m_pCapabilities->m_TextBuffering = false; |
490 | pCommand->m_pCapabilities->m_QuadContainerBuffering = false; |
491 | pCommand->m_pCapabilities->m_ShaderSupport = false; |
492 | |
493 | pCommand->m_pCapabilities->m_MipMapping = false; |
494 | pCommand->m_pCapabilities->m_3DTextures = false; |
495 | pCommand->m_pCapabilities->m_2DArrayTextures = false; |
496 | pCommand->m_pCapabilities->m_NPOTTextures = false; |
497 | } |
498 | } |
499 | #endif |
500 | } |
501 | else if(pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL_ES) |
502 | { |
503 | if(MajorV < 3) |
504 | { |
505 | pCommand->m_pCapabilities->m_TileBuffering = false; |
506 | pCommand->m_pCapabilities->m_QuadBuffering = false; |
507 | pCommand->m_pCapabilities->m_TextBuffering = false; |
508 | pCommand->m_pCapabilities->m_QuadContainerBuffering = false; |
509 | pCommand->m_pCapabilities->m_ShaderSupport = false; |
510 | |
511 | pCommand->m_pCapabilities->m_MipMapping = false; |
512 | pCommand->m_pCapabilities->m_3DTextures = false; |
513 | pCommand->m_pCapabilities->m_2DArrayTextures = false; |
514 | pCommand->m_pCapabilities->m_NPOTTextures = false; |
515 | |
516 | pCommand->m_pCapabilities->m_TrianglesAsQuads = false; |
517 | } |
518 | else |
519 | { |
520 | pCommand->m_pCapabilities->m_TileBuffering = true; |
521 | pCommand->m_pCapabilities->m_QuadBuffering = true; |
522 | pCommand->m_pCapabilities->m_TextBuffering = true; |
523 | pCommand->m_pCapabilities->m_QuadContainerBuffering = true; |
524 | pCommand->m_pCapabilities->m_ShaderSupport = true; |
525 | |
526 | pCommand->m_pCapabilities->m_MipMapping = true; |
527 | pCommand->m_pCapabilities->m_3DTextures = true; |
528 | pCommand->m_pCapabilities->m_2DArrayTextures = true; |
529 | pCommand->m_pCapabilities->m_NPOTTextures = true; |
530 | |
531 | pCommand->m_pCapabilities->m_TrianglesAsQuads = true; |
532 | } |
533 | } |
534 | |
535 | if(*pCommand->m_pInitError != -2) |
536 | { |
537 | // set some default settings |
538 | glEnable(GL_BLEND); |
539 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
540 | glDisable(GL_CULL_FACE); |
541 | glDisable(GL_DEPTH_TEST); |
542 | |
543 | #ifndef BACKEND_GL_MODERN_API |
544 | if(!IsNewApi()) |
545 | { |
546 | glAlphaFunc(GL_GREATER, ref: 0); |
547 | glEnable(GL_ALPHA_TEST); |
548 | } |
549 | #endif |
550 | |
551 | glDepthMask(flag: 0); |
552 | |
553 | #ifndef BACKEND_AS_OPENGL_ES |
554 | if(g_Config.m_DbgGfx != DEBUG_GFX_MODE_NONE) |
555 | { |
556 | if(GLEW_KHR_debug || GLEW_ARB_debug_output) |
557 | { |
558 | // During init, enable debug output |
559 | if(GLEW_KHR_debug) |
560 | { |
561 | glEnable(GL_DEBUG_OUTPUT); |
562 | glDebugMessageCallback((GLDEBUGPROC)GfxOpenGLMessageCallback, 0); |
563 | } |
564 | else if(GLEW_ARB_debug_output) |
565 | { |
566 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); |
567 | glDebugMessageCallbackARB((GLDEBUGPROC)GfxOpenGLMessageCallback, 0); |
568 | } |
569 | dbg_msg(sys: "gfx" , fmt: "Enabled OpenGL debug mode" ); |
570 | } |
571 | else |
572 | dbg_msg(sys: "gfx" , fmt: "Requested OpenGL debug mode, but the driver does not support the required extension" ); |
573 | } |
574 | #endif |
575 | |
576 | return true; |
577 | } |
578 | else |
579 | return false; |
580 | } |
581 | |
582 | bool CCommandProcessorFragment_OpenGL::Cmd_Init(const SCommand_Init *pCommand) |
583 | { |
584 | if(!InitOpenGL(pCommand)) |
585 | return false; |
586 | |
587 | m_pTextureMemoryUsage = pCommand->m_pTextureMemoryUsage; |
588 | m_pTextureMemoryUsage->store(i: 0, m: std::memory_order_relaxed); |
589 | m_MaxTexSize = -1; |
590 | |
591 | m_OpenGLTextureLodBIAS = 0; |
592 | |
593 | m_Has2DArrayTextures = pCommand->m_pCapabilities->m_2DArrayTextures; |
594 | if(pCommand->m_pCapabilities->m_2DArrayTexturesAsExtension) |
595 | { |
596 | m_Has2DArrayTexturesAsExtension = true; |
597 | m_2DArrayTarget = GL_TEXTURE_2D_ARRAY_EXT; |
598 | } |
599 | else |
600 | { |
601 | m_Has2DArrayTexturesAsExtension = false; |
602 | m_2DArrayTarget = GL_TEXTURE_2D_ARRAY; |
603 | } |
604 | |
605 | m_Has3DTextures = pCommand->m_pCapabilities->m_3DTextures; |
606 | m_HasMipMaps = pCommand->m_pCapabilities->m_MipMapping; |
607 | m_HasNPOTTextures = pCommand->m_pCapabilities->m_NPOTTextures; |
608 | |
609 | m_LastBlendMode = CCommandBuffer::BLEND_ALPHA; |
610 | m_LastClipEnable = false; |
611 | |
612 | return true; |
613 | } |
614 | |
615 | void CCommandProcessorFragment_OpenGL::TextureUpdate(int Slot, int X, int Y, int Width, int Height, int GLFormat, uint8_t *pTexData) |
616 | { |
617 | glBindTexture(GL_TEXTURE_2D, texture: m_vTextures[Slot].m_Tex); |
618 | |
619 | if(!m_HasNPOTTextures) |
620 | { |
621 | float ResizeW = m_vTextures[Slot].m_ResizeWidth; |
622 | float ResizeH = m_vTextures[Slot].m_ResizeHeight; |
623 | if(ResizeW > 0 && ResizeH > 0) |
624 | { |
625 | int ResizedW = (int)(Width * ResizeW); |
626 | int ResizedH = (int)(Height * ResizeH); |
627 | |
628 | uint8_t *pTmpData = ResizeImage(pImageData: pTexData, Width, Height, NewWidth: ResizedW, NewHeight: ResizedH, BPP: GLFormatToPixelSize(GLFormat)); |
629 | free(ptr: pTexData); |
630 | pTexData = pTmpData; |
631 | |
632 | Width = ResizedW; |
633 | Height = ResizedH; |
634 | } |
635 | } |
636 | |
637 | if(m_vTextures[Slot].m_RescaleCount > 0) |
638 | { |
639 | int OldWidth = Width; |
640 | int OldHeight = Height; |
641 | for(int i = 0; i < m_vTextures[Slot].m_RescaleCount; ++i) |
642 | { |
643 | Width >>= 1; |
644 | Height >>= 1; |
645 | |
646 | X /= 2; |
647 | Y /= 2; |
648 | } |
649 | |
650 | uint8_t *pTmpData = ResizeImage(pImageData: pTexData, Width: OldWidth, Height: OldHeight, NewWidth: Width, NewHeight: Height, BPP: GLFormatToPixelSize(GLFormat)); |
651 | free(ptr: pTexData); |
652 | pTexData = pTmpData; |
653 | } |
654 | |
655 | glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: X, yoffset: Y, width: Width, height: Height, format: GLFormat, GL_UNSIGNED_BYTE, pixels: pTexData); |
656 | free(ptr: pTexData); |
657 | } |
658 | |
659 | void CCommandProcessorFragment_OpenGL::Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand) |
660 | { |
661 | TextureUpdate(Slot: pCommand->m_Slot, X: pCommand->m_X, Y: pCommand->m_Y, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_RGBA, pTexData: pCommand->m_pData); |
662 | } |
663 | |
664 | void CCommandProcessorFragment_OpenGL::DestroyTexture(int Slot) |
665 | { |
666 | m_pTextureMemoryUsage->store(i: m_pTextureMemoryUsage->load(m: std::memory_order_relaxed) - m_vTextures[Slot].m_MemSize, m: std::memory_order_relaxed); |
667 | |
668 | if(m_vTextures[Slot].m_Tex != 0) |
669 | { |
670 | glDeleteTextures(n: 1, textures: &m_vTextures[Slot].m_Tex); |
671 | } |
672 | |
673 | if(m_vTextures[Slot].m_Tex2DArray != 0) |
674 | { |
675 | glDeleteTextures(n: 1, textures: &m_vTextures[Slot].m_Tex2DArray); |
676 | } |
677 | |
678 | if(IsNewApi()) |
679 | { |
680 | if(m_vTextures[Slot].m_Sampler != 0) |
681 | { |
682 | glDeleteSamplers(1, &m_vTextures[Slot].m_Sampler); |
683 | } |
684 | if(m_vTextures[Slot].m_Sampler2DArray != 0) |
685 | { |
686 | glDeleteSamplers(1, &m_vTextures[Slot].m_Sampler2DArray); |
687 | } |
688 | } |
689 | |
690 | m_vTextures[Slot].m_Tex = 0; |
691 | m_vTextures[Slot].m_Sampler = 0; |
692 | m_vTextures[Slot].m_Tex2DArray = 0; |
693 | m_vTextures[Slot].m_Sampler2DArray = 0; |
694 | m_vTextures[Slot].m_LastWrapMode = CCommandBuffer::WRAP_REPEAT; |
695 | } |
696 | |
697 | void CCommandProcessorFragment_OpenGL::Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand) |
698 | { |
699 | DestroyTexture(Slot: pCommand->m_Slot); |
700 | } |
701 | |
702 | void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int Height, int GLFormat, int GLStoreFormat, int Flags, uint8_t *pTexData) |
703 | { |
704 | #ifndef BACKEND_GL_MODERN_API |
705 | |
706 | if(m_MaxTexSize == -1) |
707 | { |
708 | // fix the alignment to allow even 1byte changes, e.g. for alpha components |
709 | glPixelStorei(GL_UNPACK_ALIGNMENT, param: 1); |
710 | glGetIntegerv(GL_MAX_TEXTURE_SIZE, params: &m_MaxTexSize); |
711 | } |
712 | |
713 | while(Slot >= (int)m_vTextures.size()) |
714 | m_vTextures.resize(new_size: m_vTextures.size() * 2); |
715 | |
716 | m_vTextures[Slot].m_ResizeWidth = -1.f; |
717 | m_vTextures[Slot].m_ResizeHeight = -1.f; |
718 | |
719 | if(!m_HasNPOTTextures) |
720 | { |
721 | int PowerOfTwoWidth = HighestBit(OfVar: Width); |
722 | int PowerOfTwoHeight = HighestBit(OfVar: Height); |
723 | if(Width != PowerOfTwoWidth || Height != PowerOfTwoHeight) |
724 | { |
725 | uint8_t *pTmpData = ResizeImage(pImageData: pTexData, Width, Height, NewWidth: PowerOfTwoWidth, NewHeight: PowerOfTwoHeight, BPP: GLFormatToPixelSize(GLFormat)); |
726 | free(ptr: pTexData); |
727 | pTexData = pTmpData; |
728 | |
729 | m_vTextures[Slot].m_ResizeWidth = (float)PowerOfTwoWidth / (float)Width; |
730 | m_vTextures[Slot].m_ResizeHeight = (float)PowerOfTwoHeight / (float)Height; |
731 | |
732 | Width = PowerOfTwoWidth; |
733 | Height = PowerOfTwoHeight; |
734 | } |
735 | } |
736 | |
737 | int RescaleCount = 0; |
738 | if(GLFormat == GL_RGBA) |
739 | { |
740 | int OldWidth = Width; |
741 | int OldHeight = Height; |
742 | bool NeedsResize = false; |
743 | |
744 | if(Width > m_MaxTexSize || Height > m_MaxTexSize) |
745 | { |
746 | do |
747 | { |
748 | Width >>= 1; |
749 | Height >>= 1; |
750 | ++RescaleCount; |
751 | } while(Width > m_MaxTexSize || Height > m_MaxTexSize); |
752 | NeedsResize = true; |
753 | } |
754 | |
755 | if(NeedsResize) |
756 | { |
757 | uint8_t *pTmpData = ResizeImage(pImageData: pTexData, Width: OldWidth, Height: OldHeight, NewWidth: Width, NewHeight: Height, BPP: GLFormatToPixelSize(GLFormat)); |
758 | free(ptr: pTexData); |
759 | pTexData = pTmpData; |
760 | } |
761 | } |
762 | m_vTextures[Slot].m_Width = Width; |
763 | m_vTextures[Slot].m_Height = Height; |
764 | m_vTextures[Slot].m_RescaleCount = RescaleCount; |
765 | |
766 | const size_t PixelSize = GLFormatToPixelSize(GLFormat); |
767 | |
768 | if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0) |
769 | { |
770 | glGenTextures(n: 1, textures: &m_vTextures[Slot].m_Tex); |
771 | glBindTexture(GL_TEXTURE_2D, texture: m_vTextures[Slot].m_Tex); |
772 | } |
773 | |
774 | if(Flags & CCommandBuffer::TEXFLAG_NOMIPMAPS || !m_HasMipMaps) |
775 | { |
776 | if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0) |
777 | { |
778 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
779 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
780 | glTexImage2D(GL_TEXTURE_2D, level: 0, internalformat: GLStoreFormat, width: Width, height: Height, border: 0, format: GLFormat, GL_UNSIGNED_BYTE, pixels: pTexData); |
781 | } |
782 | } |
783 | else |
784 | { |
785 | if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0) |
786 | { |
787 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
788 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
789 | glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); |
790 | |
791 | #ifndef BACKEND_AS_OPENGL_ES |
792 | if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES) |
793 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, param: ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f)); |
794 | #endif |
795 | |
796 | glTexImage2D(GL_TEXTURE_2D, level: 0, internalformat: GLStoreFormat, width: Width, height: Height, border: 0, format: GLFormat, GL_UNSIGNED_BYTE, pixels: pTexData); |
797 | } |
798 | |
799 | int Flag2DArrayTexture = CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE; |
800 | int Flag3DTexture = CCommandBuffer::TEXFLAG_TO_3D_TEXTURE; |
801 | if((Flags & (Flag2DArrayTexture | Flag3DTexture)) != 0) |
802 | { |
803 | bool Is3DTexture = (Flags & Flag3DTexture) != 0; |
804 | |
805 | glGenTextures(n: 1, textures: &m_vTextures[Slot].m_Tex2DArray); |
806 | |
807 | GLenum Target = GL_TEXTURE_3D; |
808 | |
809 | if(Is3DTexture) |
810 | { |
811 | Target = GL_TEXTURE_3D; |
812 | } |
813 | else |
814 | { |
815 | Target = m_2DArrayTarget; |
816 | } |
817 | |
818 | glBindTexture(target: Target, texture: m_vTextures[Slot].m_Tex2DArray); |
819 | |
820 | if(IsNewApi()) |
821 | { |
822 | glGenSamplers(1, &m_vTextures[Slot].m_Sampler2DArray); |
823 | glBindSampler(0, m_vTextures[Slot].m_Sampler2DArray); |
824 | } |
825 | |
826 | glTexParameteri(target: Target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
827 | if(Is3DTexture) |
828 | { |
829 | glTexParameteri(target: Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
830 | if(IsNewApi()) |
831 | glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
832 | } |
833 | else |
834 | { |
835 | glTexParameteri(target: Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
836 | glTexParameteri(target: Target, GL_GENERATE_MIPMAP, GL_TRUE); |
837 | if(IsNewApi()) |
838 | glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
839 | } |
840 | |
841 | glTexParameteri(target: Target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
842 | glTexParameteri(target: Target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
843 | glTexParameteri(target: Target, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT); |
844 | |
845 | #ifndef BACKEND_AS_OPENGL_ES |
846 | if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES) |
847 | glTexParameterf(target: Target, GL_TEXTURE_LOD_BIAS, param: ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f)); |
848 | #endif |
849 | |
850 | if(IsNewApi()) |
851 | { |
852 | glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
853 | glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
854 | glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT); |
855 | |
856 | #ifndef BACKEND_AS_OPENGL_ES |
857 | if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES) |
858 | glSamplerParameterf(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f)); |
859 | #endif |
860 | |
861 | glBindSampler(0, 0); |
862 | } |
863 | |
864 | uint8_t *p3DImageData = static_cast<uint8_t *>(malloc(size: (size_t)Width * Height * PixelSize)); |
865 | int Image3DWidth, Image3DHeight; |
866 | |
867 | int ConvertWidth = Width; |
868 | int ConvertHeight = Height; |
869 | |
870 | if(ConvertWidth == 0 || (ConvertWidth % 16) != 0 || ConvertHeight == 0 || (ConvertHeight % 16) != 0) |
871 | { |
872 | dbg_msg(sys: "gfx" , fmt: "3D/2D array texture was resized" ); |
873 | int NewWidth = maximum<int>(a: HighestBit(OfVar: ConvertWidth), b: 16); |
874 | int NewHeight = maximum<int>(a: HighestBit(OfVar: ConvertHeight), b: 16); |
875 | uint8_t *pNewTexData = ResizeImage(pImageData: pTexData, Width: ConvertWidth, Height: ConvertHeight, NewWidth, NewHeight, BPP: GLFormatToPixelSize(GLFormat)); |
876 | |
877 | ConvertWidth = NewWidth; |
878 | ConvertHeight = NewHeight; |
879 | |
880 | free(ptr: pTexData); |
881 | pTexData = pNewTexData; |
882 | } |
883 | |
884 | if(Texture2DTo3D(pImageBuffer: pTexData, ImageWidth: ConvertWidth, ImageHeight: ConvertHeight, PixelSize, SplitCountWidth: 16, SplitCountHeight: 16, pTarget3DImageData: p3DImageData, Target3DImageWidth&: Image3DWidth, Target3DImageHeight&: Image3DHeight)) |
885 | { |
886 | glTexImage3D(Target, 0, GLStoreFormat, Image3DWidth, Image3DHeight, 256, 0, GLFormat, GL_UNSIGNED_BYTE, p3DImageData); |
887 | } |
888 | |
889 | free(ptr: p3DImageData); |
890 | } |
891 | } |
892 | |
893 | // This is the initial value for the wrap modes |
894 | m_vTextures[Slot].m_LastWrapMode = CCommandBuffer::WRAP_REPEAT; |
895 | |
896 | // calculate memory usage |
897 | m_vTextures[Slot].m_MemSize = (size_t)Width * Height * PixelSize; |
898 | while(Width > 2 && Height > 2) |
899 | { |
900 | Width >>= 1; |
901 | Height >>= 1; |
902 | m_vTextures[Slot].m_MemSize += (size_t)Width * Height * PixelSize; |
903 | } |
904 | m_pTextureMemoryUsage->store(i: m_pTextureMemoryUsage->load(m: std::memory_order_relaxed) + m_vTextures[Slot].m_MemSize, m: std::memory_order_relaxed); |
905 | |
906 | free(ptr: pTexData); |
907 | #endif |
908 | } |
909 | |
910 | void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand) |
911 | { |
912 | TextureCreate(Slot: pCommand->m_Slot, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_RGBA, GL_RGBA, Flags: pCommand->m_Flags, pTexData: pCommand->m_pData); |
913 | } |
914 | |
915 | void CCommandProcessorFragment_OpenGL::Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand) |
916 | { |
917 | TextureUpdate(Slot: pCommand->m_Slot, X: pCommand->m_X, Y: pCommand->m_Y, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_ALPHA, pTexData: pCommand->m_pData); |
918 | } |
919 | |
920 | void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Destroy(const CCommandBuffer::SCommand_TextTextures_Destroy *pCommand) |
921 | { |
922 | DestroyTexture(Slot: pCommand->m_Slot); |
923 | DestroyTexture(Slot: pCommand->m_SlotOutline); |
924 | } |
925 | |
926 | void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand) |
927 | { |
928 | TextureCreate(Slot: pCommand->m_Slot, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_ALPHA, GL_ALPHA, Flags: CCommandBuffer::TEXFLAG_NOMIPMAPS, pTexData: pCommand->m_pTextData); |
929 | TextureCreate(Slot: pCommand->m_SlotOutline, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_ALPHA, GL_ALPHA, Flags: CCommandBuffer::TEXFLAG_NOMIPMAPS, pTexData: pCommand->m_pTextOutlineData); |
930 | } |
931 | |
932 | void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) |
933 | { |
934 | // if clip is still active, force disable it for clearing, enable it again afterwards |
935 | bool ClipWasEnabled = m_LastClipEnable; |
936 | if(ClipWasEnabled) |
937 | { |
938 | glDisable(GL_SCISSOR_TEST); |
939 | } |
940 | glClearColor(red: pCommand->m_Color.r, green: pCommand->m_Color.g, blue: pCommand->m_Color.b, alpha: 0.0f); |
941 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
942 | if(ClipWasEnabled) |
943 | { |
944 | glEnable(GL_SCISSOR_TEST); |
945 | } |
946 | } |
947 | |
948 | void CCommandProcessorFragment_OpenGL::Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand) |
949 | { |
950 | #ifndef BACKEND_GL_MODERN_API |
951 | SetState(State: pCommand->m_State); |
952 | |
953 | glVertexPointer(size: 2, GL_FLOAT, stride: sizeof(CCommandBuffer::SVertex), pointer: (char *)pCommand->m_pVertices); |
954 | glTexCoordPointer(size: 2, GL_FLOAT, stride: sizeof(CCommandBuffer::SVertex), pointer: (char *)pCommand->m_pVertices + sizeof(float) * 2); |
955 | glColorPointer(size: 4, GL_UNSIGNED_BYTE, stride: sizeof(CCommandBuffer::SVertex), pointer: (char *)pCommand->m_pVertices + sizeof(float) * 4); |
956 | glEnableClientState(GL_VERTEX_ARRAY); |
957 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
958 | glEnableClientState(GL_COLOR_ARRAY); |
959 | |
960 | switch(pCommand->m_PrimType) |
961 | { |
962 | case CCommandBuffer::PRIMTYPE_QUADS: |
963 | #ifndef BACKEND_AS_OPENGL_ES |
964 | glDrawArrays(GL_QUADS, first: 0, count: pCommand->m_PrimCount * 4); |
965 | #endif |
966 | break; |
967 | case CCommandBuffer::PRIMTYPE_LINES: |
968 | glDrawArrays(GL_LINES, first: 0, count: pCommand->m_PrimCount * 2); |
969 | break; |
970 | case CCommandBuffer::PRIMTYPE_TRIANGLES: |
971 | glDrawArrays(GL_TRIANGLES, first: 0, count: pCommand->m_PrimCount * 3); |
972 | break; |
973 | default: |
974 | dbg_msg(sys: "render" , fmt: "unknown primtype %d\n" , pCommand->m_PrimType); |
975 | }; |
976 | #endif |
977 | } |
978 | |
979 | void CCommandProcessorFragment_OpenGL::Cmd_ReadPixel(const CCommandBuffer::SCommand_TrySwapAndReadPixel *pCommand) |
980 | { |
981 | // get size of viewport |
982 | GLint aViewport[4] = {0, 0, 0, 0}; |
983 | glGetIntegerv(GL_VIEWPORT, params: aViewport); |
984 | const int h = aViewport[3]; |
985 | |
986 | // fetch the pixel |
987 | uint8_t aPixelData[3]; |
988 | GLint Alignment; |
989 | glGetIntegerv(GL_PACK_ALIGNMENT, params: &Alignment); |
990 | glPixelStorei(GL_PACK_ALIGNMENT, param: 1); |
991 | glReadPixels(x: pCommand->m_Position.x, y: h - 1 - pCommand->m_Position.y, width: 1, height: 1, GL_RGB, GL_UNSIGNED_BYTE, pixels: aPixelData); |
992 | glPixelStorei(GL_PACK_ALIGNMENT, param: Alignment); |
993 | |
994 | // fill in the information |
995 | *pCommand->m_pColor = ColorRGBA(aPixelData[0] / 255.0f, aPixelData[1] / 255.0f, aPixelData[2] / 255.0f, 1.0f); |
996 | } |
997 | |
998 | void CCommandProcessorFragment_OpenGL::Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand) |
999 | { |
1000 | // fetch image data |
1001 | GLint aViewport[4] = {0, 0, 0, 0}; |
1002 | glGetIntegerv(GL_VIEWPORT, params: aViewport); |
1003 | |
1004 | int w = aViewport[2]; |
1005 | int h = aViewport[3]; |
1006 | |
1007 | // we allocate one more row to use when we are flipping the texture |
1008 | unsigned char *pPixelData = (unsigned char *)malloc(size: (size_t)w * (h + 1) * 4); |
1009 | unsigned char *pTempRow = pPixelData + w * h * 4; |
1010 | |
1011 | // fetch the pixels |
1012 | GLint Alignment; |
1013 | glGetIntegerv(GL_PACK_ALIGNMENT, params: &Alignment); |
1014 | glPixelStorei(GL_PACK_ALIGNMENT, param: 1); |
1015 | glReadPixels(x: 0, y: 0, width: w, height: h, GL_RGBA, GL_UNSIGNED_BYTE, pixels: pPixelData); |
1016 | glPixelStorei(GL_PACK_ALIGNMENT, param: Alignment); |
1017 | |
1018 | // flip the pixel because opengl works from bottom left corner |
1019 | for(int y = 0; y < h / 2; y++) |
1020 | { |
1021 | mem_copy(dest: pTempRow, source: pPixelData + y * w * 4, size: w * 4); |
1022 | mem_copy(dest: pPixelData + y * w * 4, source: pPixelData + (h - y - 1) * w * 4, size: w * 4); |
1023 | mem_copy(dest: pPixelData + (h - y - 1) * w * 4, source: pTempRow, size: w * 4); |
1024 | for(int x = 0; x < w; x++) |
1025 | { |
1026 | pPixelData[y * w * 4 + x * 4 + 3] = 255; |
1027 | pPixelData[(h - y - 1) * w * 4 + x * 4 + 3] = 255; |
1028 | } |
1029 | } |
1030 | |
1031 | // fill in the information |
1032 | pCommand->m_pImage->m_Width = w; |
1033 | pCommand->m_pImage->m_Height = h; |
1034 | pCommand->m_pImage->m_Format = CImageInfo::FORMAT_RGBA; |
1035 | pCommand->m_pImage->m_pData = pPixelData; |
1036 | } |
1037 | |
1038 | CCommandProcessorFragment_OpenGL::CCommandProcessorFragment_OpenGL() |
1039 | { |
1040 | m_vTextures.resize(new_size: CCommandBuffer::MAX_TEXTURES); |
1041 | m_HasShaders = false; |
1042 | } |
1043 | |
1044 | ERunCommandReturnTypes CCommandProcessorFragment_OpenGL::RunCommand(const CCommandBuffer::SCommand *pBaseCommand) |
1045 | { |
1046 | switch(pBaseCommand->m_Cmd) |
1047 | { |
1048 | case CCommandProcessorFragment_OpenGL::CMD_INIT: |
1049 | Cmd_Init(pCommand: static_cast<const SCommand_Init *>(pBaseCommand)); |
1050 | break; |
1051 | case CCommandProcessorFragment_OpenGL::CMD_SHUTDOWN: |
1052 | Cmd_Shutdown(pCommand: static_cast<const SCommand_Shutdown *>(pBaseCommand)); |
1053 | break; |
1054 | case CCommandBuffer::CMD_TEXTURE_CREATE: |
1055 | Cmd_Texture_Create(pCommand: static_cast<const CCommandBuffer::SCommand_Texture_Create *>(pBaseCommand)); |
1056 | break; |
1057 | case CCommandBuffer::CMD_TEXTURE_DESTROY: |
1058 | Cmd_Texture_Destroy(pCommand: static_cast<const CCommandBuffer::SCommand_Texture_Destroy *>(pBaseCommand)); |
1059 | break; |
1060 | case CCommandBuffer::CMD_TEXTURE_UPDATE: |
1061 | Cmd_Texture_Update(pCommand: static_cast<const CCommandBuffer::SCommand_Texture_Update *>(pBaseCommand)); |
1062 | break; |
1063 | case CCommandBuffer::CMD_TEXT_TEXTURES_CREATE: |
1064 | Cmd_TextTextures_Create(pCommand: static_cast<const CCommandBuffer::SCommand_TextTextures_Create *>(pBaseCommand)); |
1065 | break; |
1066 | case CCommandBuffer::CMD_TEXT_TEXTURES_DESTROY: |
1067 | Cmd_TextTextures_Destroy(pCommand: static_cast<const CCommandBuffer::SCommand_TextTextures_Destroy *>(pBaseCommand)); |
1068 | break; |
1069 | case CCommandBuffer::CMD_TEXT_TEXTURE_UPDATE: |
1070 | Cmd_TextTexture_Update(pCommand: static_cast<const CCommandBuffer::SCommand_TextTexture_Update *>(pBaseCommand)); |
1071 | break; |
1072 | case CCommandBuffer::CMD_CLEAR: |
1073 | Cmd_Clear(pCommand: static_cast<const CCommandBuffer::SCommand_Clear *>(pBaseCommand)); |
1074 | break; |
1075 | case CCommandBuffer::CMD_RENDER: |
1076 | Cmd_Render(pCommand: static_cast<const CCommandBuffer::SCommand_Render *>(pBaseCommand)); |
1077 | break; |
1078 | case CCommandBuffer::CMD_RENDER_TEX3D: |
1079 | Cmd_RenderTex3D(pCommand: static_cast<const CCommandBuffer::SCommand_RenderTex3D *>(pBaseCommand)); |
1080 | break; |
1081 | case CCommandBuffer::CMD_TRY_SWAP_AND_READ_PIXEL: |
1082 | Cmd_ReadPixel(pCommand: static_cast<const CCommandBuffer::SCommand_TrySwapAndReadPixel *>(pBaseCommand)); |
1083 | break; |
1084 | case CCommandBuffer::CMD_TRY_SWAP_AND_SCREENSHOT: |
1085 | Cmd_Screenshot(pCommand: static_cast<const CCommandBuffer::SCommand_TrySwapAndScreenshot *>(pBaseCommand)); |
1086 | break; |
1087 | case CCommandBuffer::CMD_UPDATE_VIEWPORT: |
1088 | Cmd_Update_Viewport(pCommand: static_cast<const CCommandBuffer::SCommand_Update_Viewport *>(pBaseCommand)); |
1089 | break; |
1090 | |
1091 | case CCommandBuffer::CMD_CREATE_BUFFER_OBJECT: Cmd_CreateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_CreateBufferObject *>(pBaseCommand)); break; |
1092 | case CCommandBuffer::CMD_UPDATE_BUFFER_OBJECT: Cmd_UpdateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_UpdateBufferObject *>(pBaseCommand)); break; |
1093 | case CCommandBuffer::CMD_RECREATE_BUFFER_OBJECT: Cmd_RecreateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_RecreateBufferObject *>(pBaseCommand)); break; |
1094 | case CCommandBuffer::CMD_COPY_BUFFER_OBJECT: Cmd_CopyBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_CopyBufferObject *>(pBaseCommand)); break; |
1095 | case CCommandBuffer::CMD_DELETE_BUFFER_OBJECT: Cmd_DeleteBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_DeleteBufferObject *>(pBaseCommand)); break; |
1096 | |
1097 | case CCommandBuffer::CMD_CREATE_BUFFER_CONTAINER: Cmd_CreateBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_CreateBufferContainer *>(pBaseCommand)); break; |
1098 | case CCommandBuffer::CMD_UPDATE_BUFFER_CONTAINER: Cmd_UpdateBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_UpdateBufferContainer *>(pBaseCommand)); break; |
1099 | case CCommandBuffer::CMD_DELETE_BUFFER_CONTAINER: Cmd_DeleteBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_DeleteBufferContainer *>(pBaseCommand)); break; |
1100 | case CCommandBuffer::CMD_INDICES_REQUIRED_NUM_NOTIFY: Cmd_IndicesRequiredNumNotify(pCommand: static_cast<const CCommandBuffer::SCommand_IndicesRequiredNumNotify *>(pBaseCommand)); break; |
1101 | |
1102 | case CCommandBuffer::CMD_RENDER_TILE_LAYER: Cmd_RenderTileLayer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderTileLayer *>(pBaseCommand)); break; |
1103 | case CCommandBuffer::CMD_RENDER_BORDER_TILE: Cmd_RenderBorderTile(pCommand: static_cast<const CCommandBuffer::SCommand_RenderBorderTile *>(pBaseCommand)); break; |
1104 | case CCommandBuffer::CMD_RENDER_QUAD_LAYER: Cmd_RenderQuadLayer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadLayer *>(pBaseCommand)); break; |
1105 | case CCommandBuffer::CMD_RENDER_TEXT: Cmd_RenderText(pCommand: static_cast<const CCommandBuffer::SCommand_RenderText *>(pBaseCommand)); break; |
1106 | case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER: Cmd_RenderQuadContainer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainer *>(pBaseCommand)); break; |
1107 | case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_EX: Cmd_RenderQuadContainerEx(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainerEx *>(pBaseCommand)); break; |
1108 | case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_SPRITE_MULTIPLE: Cmd_RenderQuadContainerAsSpriteMultiple(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *>(pBaseCommand)); break; |
1109 | default: return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_UNHANDLED; |
1110 | } |
1111 | |
1112 | return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_HANDLED; |
1113 | } |
1114 | |
1115 | // ------------ CCommandProcessorFragment_OpenGL2 |
1116 | |
1117 | void CCommandProcessorFragment_OpenGL2::UseProgram(CGLSLTWProgram *pProgram) |
1118 | { |
1119 | pProgram->UseProgram(); |
1120 | } |
1121 | |
1122 | void CCommandProcessorFragment_OpenGL2::SetState(const CCommandBuffer::SState &State, CGLSLTWProgram *pProgram, bool Use2DArrayTextures) |
1123 | { |
1124 | if(m_LastBlendMode == CCommandBuffer::BLEND_NONE) |
1125 | { |
1126 | m_LastBlendMode = CCommandBuffer::BLEND_ALPHA; |
1127 | glEnable(GL_BLEND); |
1128 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
1129 | } |
1130 | if(State.m_BlendMode != m_LastBlendMode && State.m_BlendMode != CCommandBuffer::BLEND_NONE) |
1131 | { |
1132 | // blend |
1133 | switch(State.m_BlendMode) |
1134 | { |
1135 | case CCommandBuffer::BLEND_NONE: |
1136 | // We don't really need this anymore |
1137 | // glDisable(GL_BLEND); |
1138 | break; |
1139 | case CCommandBuffer::BLEND_ALPHA: |
1140 | // glEnable(GL_BLEND); |
1141 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
1142 | break; |
1143 | case CCommandBuffer::BLEND_ADDITIVE: |
1144 | // glEnable(GL_BLEND); |
1145 | glBlendFunc(GL_SRC_ALPHA, GL_ONE); |
1146 | break; |
1147 | default: |
1148 | dbg_msg(sys: "render" , fmt: "unknown blendmode %d\n" , State.m_BlendMode); |
1149 | }; |
1150 | |
1151 | m_LastBlendMode = State.m_BlendMode; |
1152 | } |
1153 | |
1154 | // clip |
1155 | if(State.m_ClipEnable) |
1156 | { |
1157 | glScissor(x: State.m_ClipX, y: State.m_ClipY, width: State.m_ClipW, height: State.m_ClipH); |
1158 | glEnable(GL_SCISSOR_TEST); |
1159 | m_LastClipEnable = true; |
1160 | } |
1161 | else if(m_LastClipEnable) |
1162 | { |
1163 | // Don't disable it always |
1164 | glDisable(GL_SCISSOR_TEST); |
1165 | m_LastClipEnable = false; |
1166 | } |
1167 | |
1168 | if(!IsNewApi()) |
1169 | { |
1170 | glDisable(GL_TEXTURE_2D); |
1171 | if(!m_HasShaders) |
1172 | { |
1173 | if(m_Has3DTextures) |
1174 | glDisable(GL_TEXTURE_3D); |
1175 | if(m_Has2DArrayTextures) |
1176 | { |
1177 | glDisable(cap: m_2DArrayTarget); |
1178 | } |
1179 | } |
1180 | } |
1181 | |
1182 | // texture |
1183 | if(IsTexturedState(State)) |
1184 | { |
1185 | int Slot = 0; |
1186 | if(!Use2DArrayTextures) |
1187 | { |
1188 | if(!IsNewApi() && !m_HasShaders) |
1189 | glEnable(GL_TEXTURE_2D); |
1190 | glBindTexture(GL_TEXTURE_2D, texture: m_vTextures[State.m_Texture].m_Tex); |
1191 | if(IsNewApi()) |
1192 | glBindSampler(Slot, m_vTextures[State.m_Texture].m_Sampler); |
1193 | } |
1194 | else |
1195 | { |
1196 | if(!m_Has2DArrayTextures) |
1197 | { |
1198 | if(!IsNewApi() && !m_HasShaders) |
1199 | glEnable(GL_TEXTURE_3D); |
1200 | glBindTexture(GL_TEXTURE_3D, texture: m_vTextures[State.m_Texture].m_Tex2DArray); |
1201 | if(IsNewApi()) |
1202 | glBindSampler(Slot, m_vTextures[State.m_Texture].m_Sampler2DArray); |
1203 | } |
1204 | else |
1205 | { |
1206 | if(!IsNewApi() && !m_HasShaders) |
1207 | glEnable(cap: m_2DArrayTarget); |
1208 | glBindTexture(target: m_2DArrayTarget, texture: m_vTextures[State.m_Texture].m_Tex2DArray); |
1209 | if(IsNewApi()) |
1210 | glBindSampler(Slot, m_vTextures[State.m_Texture].m_Sampler2DArray); |
1211 | } |
1212 | } |
1213 | |
1214 | if(pProgram->m_LastTextureSampler != Slot) |
1215 | { |
1216 | pProgram->SetUniform(Loc: pProgram->m_LocTextureSampler, Value: Slot); |
1217 | pProgram->m_LastTextureSampler = Slot; |
1218 | } |
1219 | |
1220 | if(m_vTextures[State.m_Texture].m_LastWrapMode != State.m_WrapMode && !Use2DArrayTextures) |
1221 | { |
1222 | switch(State.m_WrapMode) |
1223 | { |
1224 | case CCommandBuffer::WRAP_REPEAT: |
1225 | if(IsNewApi()) |
1226 | { |
1227 | glSamplerParameteri(m_vTextures[State.m_Texture].m_Sampler, GL_TEXTURE_WRAP_S, GL_REPEAT); |
1228 | glSamplerParameteri(m_vTextures[State.m_Texture].m_Sampler, GL_TEXTURE_WRAP_T, GL_REPEAT); |
1229 | } |
1230 | break; |
1231 | case CCommandBuffer::WRAP_CLAMP: |
1232 | if(IsNewApi()) |
1233 | { |
1234 | glSamplerParameteri(m_vTextures[State.m_Texture].m_Sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
1235 | glSamplerParameteri(m_vTextures[State.m_Texture].m_Sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
1236 | } |
1237 | break; |
1238 | default: |
1239 | dbg_msg(sys: "render" , fmt: "unknown wrapmode %d\n" , State.m_WrapMode); |
1240 | }; |
1241 | m_vTextures[State.m_Texture].m_LastWrapMode = State.m_WrapMode; |
1242 | } |
1243 | } |
1244 | |
1245 | if(pProgram->m_LastScreenTL != State.m_ScreenTL || pProgram->m_LastScreenBR != State.m_ScreenBR) |
1246 | { |
1247 | pProgram->m_LastScreenTL = State.m_ScreenTL; |
1248 | pProgram->m_LastScreenBR = State.m_ScreenBR; |
1249 | |
1250 | // screen mapping |
1251 | // orthographic projection matrix |
1252 | // the z coordinate is the same for every vertex, so just ignore the z coordinate and set it in the shaders |
1253 | float m[2 * 4] = { |
1254 | 2.f / (State.m_ScreenBR.x - State.m_ScreenTL.x), |
1255 | 0, |
1256 | 0, |
1257 | -((State.m_ScreenBR.x + State.m_ScreenTL.x) / (State.m_ScreenBR.x - State.m_ScreenTL.x)), |
1258 | 0, |
1259 | (2.f / (State.m_ScreenTL.y - State.m_ScreenBR.y)), |
1260 | 0, |
1261 | -((State.m_ScreenTL.y + State.m_ScreenBR.y) / (State.m_ScreenTL.y - State.m_ScreenBR.y)), |
1262 | }; |
1263 | |
1264 | // transpose bcs of column-major order of opengl |
1265 | glUniformMatrix4x2fv(pProgram->m_LocPos, 1, true, (float *)&m); |
1266 | } |
1267 | } |
1268 | |
1269 | #ifndef BACKEND_GL_MODERN_API |
1270 | bool CCommandProcessorFragment_OpenGL2::DoAnalyzeStep(size_t CheckCount, size_t VerticesCount, uint8_t aFakeTexture[], size_t SingleImageSize) |
1271 | { |
1272 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
1273 | |
1274 | int Slot = 0; |
1275 | if(m_HasShaders) |
1276 | { |
1277 | CGLSLTWProgram *pProgram = m_pPrimitive3DProgramTextured; |
1278 | UseProgram(pProgram); |
1279 | |
1280 | pProgram->SetUniform(Loc: pProgram->m_LocTextureSampler, Value: Slot); |
1281 | |
1282 | float m[2 * 4] = { |
1283 | 1, 0, 0, 0, |
1284 | 0, 1, 0, 0}; |
1285 | |
1286 | // transpose bcs of column-major order of opengl |
1287 | glUniformMatrix4x2fv(pProgram->m_LocPos, 1, true, (float *)&m); |
1288 | } |
1289 | else |
1290 | { |
1291 | glMatrixMode(GL_PROJECTION); |
1292 | glLoadIdentity(); |
1293 | glOrtho(left: -1, right: 1, bottom: -1, top: 1, zNear: -10.0f, zFar: 10.f); |
1294 | } |
1295 | |
1296 | glEnableClientState(GL_VERTEX_ARRAY); |
1297 | glEnableClientState(GL_COLOR_ARRAY); |
1298 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
1299 | |
1300 | glVertexPointer(size: 2, GL_FLOAT, stride: sizeof(m_aStreamVertices[0]), pointer: m_aStreamVertices); |
1301 | glColorPointer(size: 4, GL_FLOAT, stride: sizeof(m_aStreamVertices[0]), pointer: (uint8_t *)m_aStreamVertices + (ptrdiff_t)(sizeof(vec2))); |
1302 | glTexCoordPointer(size: 3, GL_FLOAT, stride: sizeof(m_aStreamVertices[0]), pointer: (uint8_t *)m_aStreamVertices + (ptrdiff_t)(sizeof(vec2) + sizeof(vec4))); |
1303 | |
1304 | glDrawArrays(GL_QUADS, first: 0, count: VerticesCount); |
1305 | |
1306 | glDisableClientState(GL_VERTEX_ARRAY); |
1307 | glDisableClientState(GL_COLOR_ARRAY); |
1308 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); |
1309 | |
1310 | if(m_HasShaders) |
1311 | { |
1312 | glUseProgram(0); |
1313 | } |
1314 | |
1315 | glFinish(); |
1316 | |
1317 | GLint aViewport[4] = {0, 0, 0, 0}; |
1318 | glGetIntegerv(GL_VIEWPORT, params: aViewport); |
1319 | |
1320 | int w = aViewport[2]; |
1321 | int h = aViewport[3]; |
1322 | |
1323 | size_t PixelDataSize = (size_t)w * h * 3; |
1324 | if(PixelDataSize == 0) |
1325 | return false; |
1326 | uint8_t *pPixelData = (uint8_t *)malloc(size: PixelDataSize); |
1327 | |
1328 | // fetch the pixels |
1329 | GLint Alignment; |
1330 | glGetIntegerv(GL_PACK_ALIGNMENT, params: &Alignment); |
1331 | glPixelStorei(GL_PACK_ALIGNMENT, param: 1); |
1332 | glReadPixels(x: 0, y: 0, width: w, height: h, GL_RGB, GL_UNSIGNED_BYTE, pixels: pPixelData); |
1333 | glPixelStorei(GL_PACK_ALIGNMENT, param: Alignment); |
1334 | |
1335 | // now analyse the image data |
1336 | bool CheckFailed = false; |
1337 | int WidthTile = w / 16; |
1338 | int HeightTile = h / 16; |
1339 | int StartX = WidthTile / 2; |
1340 | int StartY = HeightTile / 2; |
1341 | for(size_t d = 0; d < CheckCount; ++d) |
1342 | { |
1343 | int CurX = (int)d % 16; |
1344 | int CurY = (int)d / 16; |
1345 | |
1346 | int CheckX = StartX + CurX * WidthTile; |
1347 | int CheckY = StartY + CurY * HeightTile; |
1348 | |
1349 | ptrdiff_t OffsetPixelData = (CheckY * (w * 3)) + (CheckX * 3); |
1350 | ptrdiff_t OffsetFakeTexture = SingleImageSize * d; |
1351 | OffsetPixelData = clamp<ptrdiff_t>(val: OffsetPixelData, lo: 0, hi: (ptrdiff_t)PixelDataSize); |
1352 | OffsetFakeTexture = clamp<ptrdiff_t>(val: OffsetFakeTexture, lo: 0, hi: (ptrdiff_t)(SingleImageSize * CheckCount)); |
1353 | uint8_t *pPixel = pPixelData + OffsetPixelData; |
1354 | uint8_t *pPixelTex = aFakeTexture + OffsetFakeTexture; |
1355 | for(size_t i = 0; i < 3; ++i) |
1356 | { |
1357 | if((pPixel[i] < pPixelTex[i] - 25) || (pPixel[i] > pPixelTex[i] + 25)) |
1358 | { |
1359 | CheckFailed = true; |
1360 | break; |
1361 | } |
1362 | } |
1363 | } |
1364 | |
1365 | free(ptr: pPixelData); |
1366 | return !CheckFailed; |
1367 | } |
1368 | |
1369 | bool CCommandProcessorFragment_OpenGL2::IsTileMapAnalysisSucceeded() |
1370 | { |
1371 | glClearColor(red: 0, green: 0, blue: 0, alpha: 1); |
1372 | |
1373 | // create fake texture 1024x1024 |
1374 | const size_t ImageWidth = 1024; |
1375 | const size_t ImageHeight = 1024; |
1376 | uint8_t *pFakeTexture = (uint8_t *)malloc(size: sizeof(uint8_t) * ImageWidth * ImageHeight * 4); |
1377 | // fill by colors stepping by 50 => (255 / 50 ~ 5) => 5 times 3(color channels) = 5 ^ 3 = 125 possibilities to check |
1378 | size_t CheckCount = 5 * 5 * 5; |
1379 | // always fill 4 pixels of the texture, so the sampling is accurate |
1380 | int aCurColor[4] = {25, 25, 25, 255}; |
1381 | const size_t SingleImageWidth = 64; |
1382 | const size_t SingleImageHeight = 64; |
1383 | size_t SingleImageSize = SingleImageWidth * SingleImageHeight * 4; |
1384 | for(size_t d = 0; d < CheckCount; ++d) |
1385 | { |
1386 | uint8_t *pCurFakeTexture = pFakeTexture + (ptrdiff_t)(SingleImageSize * d); |
1387 | |
1388 | uint8_t aCurColorUint8[SingleImageWidth * SingleImageHeight * 4]; |
1389 | for(size_t y = 0; y < SingleImageHeight; ++y) |
1390 | { |
1391 | for(size_t x = 0; x < SingleImageWidth; ++x) |
1392 | { |
1393 | for(size_t i = 0; i < 4; ++i) |
1394 | { |
1395 | aCurColorUint8[(y * SingleImageWidth * 4) + (x * 4) + i] = (uint8_t)aCurColor[i]; |
1396 | } |
1397 | } |
1398 | } |
1399 | mem_copy(dest: pCurFakeTexture, source: aCurColorUint8, size: sizeof(aCurColorUint8)); |
1400 | |
1401 | aCurColor[2] += 50; |
1402 | if(aCurColor[2] > 225) |
1403 | { |
1404 | aCurColor[2] -= 250; |
1405 | aCurColor[1] += 50; |
1406 | } |
1407 | if(aCurColor[1] > 225) |
1408 | { |
1409 | aCurColor[1] -= 250; |
1410 | aCurColor[0] += 50; |
1411 | } |
1412 | if(aCurColor[0] > 225) |
1413 | { |
1414 | break; |
1415 | } |
1416 | } |
1417 | |
1418 | // upload the texture |
1419 | GLuint FakeTexture; |
1420 | glGenTextures(n: 1, textures: &FakeTexture); |
1421 | |
1422 | GLenum Target = GL_TEXTURE_3D; |
1423 | if(m_Has2DArrayTextures) |
1424 | { |
1425 | Target = m_2DArrayTarget; |
1426 | } |
1427 | |
1428 | glBindTexture(target: Target, texture: FakeTexture); |
1429 | glTexParameteri(target: Target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
1430 | if(!m_Has2DArrayTextures) |
1431 | { |
1432 | glTexParameteri(target: Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
1433 | } |
1434 | else |
1435 | { |
1436 | glTexParameteri(target: Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
1437 | glTexParameteri(target: Target, GL_GENERATE_MIPMAP, GL_TRUE); |
1438 | } |
1439 | |
1440 | glTexParameteri(target: Target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
1441 | glTexParameteri(target: Target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
1442 | glTexParameteri(target: Target, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT); |
1443 | |
1444 | glTexImage3D(Target, 0, GL_RGBA, ImageWidth / 16, ImageHeight / 16, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pFakeTexture); |
1445 | |
1446 | glEnable(GL_BLEND); |
1447 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
1448 | glDisable(GL_SCISSOR_TEST); |
1449 | |
1450 | if(!m_HasShaders) |
1451 | { |
1452 | glDisable(GL_TEXTURE_2D); |
1453 | if(m_Has3DTextures) |
1454 | glDisable(GL_TEXTURE_3D); |
1455 | if(m_Has2DArrayTextures) |
1456 | { |
1457 | glDisable(cap: m_2DArrayTarget); |
1458 | } |
1459 | |
1460 | if(!m_Has2DArrayTextures) |
1461 | { |
1462 | glEnable(GL_TEXTURE_3D); |
1463 | glBindTexture(GL_TEXTURE_3D, texture: FakeTexture); |
1464 | } |
1465 | else |
1466 | { |
1467 | glEnable(cap: m_2DArrayTarget); |
1468 | glBindTexture(target: m_2DArrayTarget, texture: FakeTexture); |
1469 | } |
1470 | } |
1471 | |
1472 | static_assert(sizeof(m_aStreamVertices) / sizeof(m_aStreamVertices[0]) >= 256 * 4, "Keep the number of stream vertices >= 256 * 4." ); |
1473 | |
1474 | size_t VertexCount = 0; |
1475 | for(size_t i = 0; i < CheckCount; ++i) |
1476 | { |
1477 | float XPos = (float)(i % 16); |
1478 | float YPos = (float)(i / 16); |
1479 | |
1480 | GL_SVertexTex3D *pVertex = &m_aStreamVertices[VertexCount++]; |
1481 | GL_SVertexTex3D *pVertexBefore = pVertex; |
1482 | pVertex->m_Pos.x = XPos / 16.f; |
1483 | pVertex->m_Pos.y = YPos / 16.f; |
1484 | pVertex->m_Color.r = 1; |
1485 | pVertex->m_Color.g = 1; |
1486 | pVertex->m_Color.b = 1; |
1487 | pVertex->m_Color.a = 1; |
1488 | pVertex->m_Tex.u = 0; |
1489 | pVertex->m_Tex.v = 0; |
1490 | |
1491 | pVertex = &m_aStreamVertices[VertexCount++]; |
1492 | pVertex->m_Pos.x = XPos / 16.f + 1.f / 16.f; |
1493 | pVertex->m_Pos.y = YPos / 16.f; |
1494 | pVertex->m_Color.r = 1; |
1495 | pVertex->m_Color.g = 1; |
1496 | pVertex->m_Color.b = 1; |
1497 | pVertex->m_Color.a = 1; |
1498 | pVertex->m_Tex.u = 1; |
1499 | pVertex->m_Tex.v = 0; |
1500 | |
1501 | pVertex = &m_aStreamVertices[VertexCount++]; |
1502 | pVertex->m_Pos.x = XPos / 16.f + 1.f / 16.f; |
1503 | pVertex->m_Pos.y = YPos / 16.f + 1.f / 16.f; |
1504 | pVertex->m_Color.r = 1; |
1505 | pVertex->m_Color.g = 1; |
1506 | pVertex->m_Color.b = 1; |
1507 | pVertex->m_Color.a = 1; |
1508 | pVertex->m_Tex.u = 1; |
1509 | pVertex->m_Tex.v = 1; |
1510 | |
1511 | pVertex = &m_aStreamVertices[VertexCount++]; |
1512 | pVertex->m_Pos.x = XPos / 16.f; |
1513 | pVertex->m_Pos.y = YPos / 16.f + 1.f / 16.f; |
1514 | pVertex->m_Color.r = 1; |
1515 | pVertex->m_Color.g = 1; |
1516 | pVertex->m_Color.b = 1; |
1517 | pVertex->m_Color.a = 1; |
1518 | pVertex->m_Tex.u = 0; |
1519 | pVertex->m_Tex.v = 1; |
1520 | |
1521 | for(size_t n = 0; n < 4; ++n) |
1522 | { |
1523 | pVertexBefore[n].m_Pos.x *= 2; |
1524 | pVertexBefore[n].m_Pos.x -= 1; |
1525 | pVertexBefore[n].m_Pos.y *= 2; |
1526 | pVertexBefore[n].m_Pos.y -= 1; |
1527 | if(m_Has2DArrayTextures) |
1528 | { |
1529 | pVertexBefore[n].m_Tex.w = i; |
1530 | } |
1531 | else |
1532 | { |
1533 | pVertexBefore[n].m_Tex.w = (i + 0.5f) / 256.f; |
1534 | } |
1535 | } |
1536 | } |
1537 | |
1538 | // everything build up, now do the analyze steps |
1539 | bool NoError = DoAnalyzeStep(CheckCount, VerticesCount: VertexCount, aFakeTexture: pFakeTexture, SingleImageSize); |
1540 | |
1541 | glDeleteTextures(n: 1, textures: &FakeTexture); |
1542 | free(ptr: pFakeTexture); |
1543 | |
1544 | return NoError; |
1545 | } |
1546 | |
1547 | bool CCommandProcessorFragment_OpenGL2::Cmd_Init(const SCommand_Init *pCommand) |
1548 | { |
1549 | if(!CCommandProcessorFragment_OpenGL::Cmd_Init(pCommand)) |
1550 | return false; |
1551 | |
1552 | m_pTileProgram = nullptr; |
1553 | m_pTileProgramTextured = nullptr; |
1554 | m_pPrimitive3DProgram = nullptr; |
1555 | m_pPrimitive3DProgramTextured = nullptr; |
1556 | |
1557 | m_OpenGLTextureLodBIAS = g_Config.m_GfxGLTextureLODBIAS; |
1558 | |
1559 | m_HasShaders = pCommand->m_pCapabilities->m_ShaderSupport; |
1560 | |
1561 | bool HasAllFunc = true; |
1562 | #ifndef BACKEND_AS_OPENGL_ES |
1563 | if(m_HasShaders) |
1564 | { |
1565 | HasAllFunc &= (glUniformMatrix4x2fv != NULL) && (glGenBuffers != NULL); |
1566 | HasAllFunc &= (glBindBuffer != NULL) && (glBufferData != NULL); |
1567 | HasAllFunc &= (glEnableVertexAttribArray != NULL) && (glVertexAttribPointer != NULL) && (glVertexAttribIPointer != NULL); |
1568 | HasAllFunc &= (glDisableVertexAttribArray != NULL) && (glDeleteBuffers != NULL); |
1569 | HasAllFunc &= (glUseProgram != NULL) && (glTexImage3D != NULL); |
1570 | HasAllFunc &= (glBindAttribLocation != NULL) && (glTexImage3D != NULL); |
1571 | HasAllFunc &= (glBufferSubData != NULL) && (glGetUniformLocation != NULL); |
1572 | HasAllFunc &= (glUniform1i != NULL) && (glUniform1f != NULL); |
1573 | HasAllFunc &= (glUniform1ui != NULL) && (glUniform1i != NULL); |
1574 | HasAllFunc &= (glUniform1fv != NULL) && (glUniform2fv != NULL); |
1575 | HasAllFunc &= (glUniform4fv != NULL) && (glGetAttachedShaders != NULL); |
1576 | HasAllFunc &= (glGetProgramInfoLog != NULL) && (glGetProgramiv != NULL); |
1577 | HasAllFunc &= (glLinkProgram != NULL) && (glDetachShader != NULL); |
1578 | HasAllFunc &= (glAttachShader != NULL) && (glDeleteProgram != NULL); |
1579 | HasAllFunc &= (glCreateProgram != NULL) && (glShaderSource != NULL); |
1580 | HasAllFunc &= (glCompileShader != NULL) && (glGetShaderiv != NULL); |
1581 | HasAllFunc &= (glGetShaderInfoLog != NULL) && (glDeleteShader != NULL); |
1582 | HasAllFunc &= (glCreateShader != NULL); |
1583 | } |
1584 | #endif |
1585 | |
1586 | bool AnalysisCorrect = true; |
1587 | if(HasAllFunc) |
1588 | { |
1589 | if(m_HasShaders) |
1590 | { |
1591 | m_pTileProgram = new CGLSLTileProgram; |
1592 | m_pTileProgramTextured = new CGLSLTileProgram; |
1593 | m_pBorderTileProgram = new CGLSLTileProgram; |
1594 | m_pBorderTileProgramTextured = new CGLSLTileProgram; |
1595 | m_pPrimitive3DProgram = new CGLSLPrimitiveProgram; |
1596 | m_pPrimitive3DProgramTextured = new CGLSLPrimitiveProgram; |
1597 | |
1598 | CGLSLCompiler ShaderCompiler(g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch, m_IsOpenGLES, m_OpenGLTextureLodBIAS / 1000.0f); |
1599 | ShaderCompiler.SetHasTextureArray(pCommand->m_pCapabilities->m_2DArrayTextures); |
1600 | |
1601 | if(pCommand->m_pCapabilities->m_2DArrayTextures) |
1602 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY); |
1603 | else |
1604 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D); |
1605 | { |
1606 | CGLSL PrimitiveVertexShader; |
1607 | CGLSL PrimitiveFragmentShader; |
1608 | PrimitiveVertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/pipeline.vert" , GL_VERTEX_SHADER); |
1609 | PrimitiveFragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/pipeline.frag" , GL_FRAGMENT_SHADER); |
1610 | |
1611 | m_pPrimitive3DProgram->CreateProgram(); |
1612 | m_pPrimitive3DProgram->AddShader(pShader: &PrimitiveVertexShader); |
1613 | m_pPrimitive3DProgram->AddShader(pShader: &PrimitiveFragmentShader); |
1614 | m_pPrimitive3DProgram->LinkProgram(); |
1615 | |
1616 | UseProgram(pProgram: m_pPrimitive3DProgram); |
1617 | |
1618 | m_pPrimitive3DProgram->m_LocPos = m_pPrimitive3DProgram->GetUniformLoc(pName: "gPos" ); |
1619 | } |
1620 | |
1621 | if(pCommand->m_pCapabilities->m_2DArrayTextures) |
1622 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY); |
1623 | else |
1624 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D); |
1625 | { |
1626 | CGLSL PrimitiveVertexShader; |
1627 | CGLSL PrimitiveFragmentShader; |
1628 | ShaderCompiler.AddDefine(pDefineName: "TW_TEXTURED" , pDefineValue: "" ); |
1629 | if(!pCommand->m_pCapabilities->m_2DArrayTextures) |
1630 | ShaderCompiler.AddDefine(pDefineName: "TW_3D_TEXTURED" , pDefineValue: "" ); |
1631 | PrimitiveVertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/pipeline.vert" , GL_VERTEX_SHADER); |
1632 | PrimitiveFragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/pipeline.frag" , GL_FRAGMENT_SHADER); |
1633 | ShaderCompiler.ClearDefines(); |
1634 | |
1635 | m_pPrimitive3DProgramTextured->CreateProgram(); |
1636 | m_pPrimitive3DProgramTextured->AddShader(pShader: &PrimitiveVertexShader); |
1637 | m_pPrimitive3DProgramTextured->AddShader(pShader: &PrimitiveFragmentShader); |
1638 | m_pPrimitive3DProgramTextured->LinkProgram(); |
1639 | |
1640 | UseProgram(pProgram: m_pPrimitive3DProgramTextured); |
1641 | |
1642 | m_pPrimitive3DProgramTextured->m_LocPos = m_pPrimitive3DProgramTextured->GetUniformLoc(pName: "gPos" ); |
1643 | m_pPrimitive3DProgramTextured->m_LocTextureSampler = m_pPrimitive3DProgramTextured->GetUniformLoc(pName: "gTextureSampler" ); |
1644 | } |
1645 | if(pCommand->m_pCapabilities->m_2DArrayTextures) |
1646 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY); |
1647 | else |
1648 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D); |
1649 | { |
1650 | CGLSL VertexShader; |
1651 | CGLSL FragmentShader; |
1652 | VertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile.vert" , GL_VERTEX_SHADER); |
1653 | FragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile.frag" , GL_FRAGMENT_SHADER); |
1654 | |
1655 | m_pTileProgram->CreateProgram(); |
1656 | m_pTileProgram->AddShader(pShader: &VertexShader); |
1657 | m_pTileProgram->AddShader(pShader: &FragmentShader); |
1658 | |
1659 | glBindAttribLocation(m_pTileProgram->GetProgramId(), 0, "inVertex" ); |
1660 | |
1661 | m_pTileProgram->LinkProgram(); |
1662 | |
1663 | UseProgram(pProgram: m_pTileProgram); |
1664 | |
1665 | m_pTileProgram->m_LocPos = m_pTileProgram->GetUniformLoc(pName: "gPos" ); |
1666 | m_pTileProgram->m_LocColor = m_pTileProgram->GetUniformLoc(pName: "gVertColor" ); |
1667 | } |
1668 | if(pCommand->m_pCapabilities->m_2DArrayTextures) |
1669 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY); |
1670 | else |
1671 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D); |
1672 | { |
1673 | CGLSL VertexShader; |
1674 | CGLSL FragmentShader; |
1675 | ShaderCompiler.AddDefine(pDefineName: "TW_TILE_TEXTURED" , pDefineValue: "" ); |
1676 | if(!pCommand->m_pCapabilities->m_2DArrayTextures) |
1677 | ShaderCompiler.AddDefine(pDefineName: "TW_TILE_3D_TEXTURED" , pDefineValue: "" ); |
1678 | VertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile.vert" , GL_VERTEX_SHADER); |
1679 | FragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile.frag" , GL_FRAGMENT_SHADER); |
1680 | ShaderCompiler.ClearDefines(); |
1681 | |
1682 | m_pTileProgramTextured->CreateProgram(); |
1683 | m_pTileProgramTextured->AddShader(pShader: &VertexShader); |
1684 | m_pTileProgramTextured->AddShader(pShader: &FragmentShader); |
1685 | |
1686 | glBindAttribLocation(m_pTileProgram->GetProgramId(), 0, "inVertex" ); |
1687 | glBindAttribLocation(m_pTileProgram->GetProgramId(), 1, "inVertexTexCoord" ); |
1688 | |
1689 | m_pTileProgramTextured->LinkProgram(); |
1690 | |
1691 | UseProgram(pProgram: m_pTileProgramTextured); |
1692 | |
1693 | m_pTileProgramTextured->m_LocPos = m_pTileProgramTextured->GetUniformLoc(pName: "gPos" ); |
1694 | m_pTileProgramTextured->m_LocTextureSampler = m_pTileProgramTextured->GetUniformLoc(pName: "gTextureSampler" ); |
1695 | m_pTileProgramTextured->m_LocColor = m_pTileProgramTextured->GetUniformLoc(pName: "gVertColor" ); |
1696 | } |
1697 | if(pCommand->m_pCapabilities->m_2DArrayTextures) |
1698 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY); |
1699 | else |
1700 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D); |
1701 | { |
1702 | CGLSL VertexShader; |
1703 | CGLSL FragmentShader; |
1704 | VertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile_border.vert" , GL_VERTEX_SHADER); |
1705 | FragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile_border.frag" , GL_FRAGMENT_SHADER); |
1706 | ShaderCompiler.ClearDefines(); |
1707 | |
1708 | m_pBorderTileProgram->CreateProgram(); |
1709 | m_pBorderTileProgram->AddShader(pShader: &VertexShader); |
1710 | m_pBorderTileProgram->AddShader(pShader: &FragmentShader); |
1711 | |
1712 | glBindAttribLocation(m_pBorderTileProgram->GetProgramId(), 0, "inVertex" ); |
1713 | |
1714 | m_pBorderTileProgram->LinkProgram(); |
1715 | |
1716 | UseProgram(pProgram: m_pBorderTileProgram); |
1717 | |
1718 | m_pBorderTileProgram->m_LocPos = m_pBorderTileProgram->GetUniformLoc(pName: "gPos" ); |
1719 | m_pBorderTileProgram->m_LocColor = m_pBorderTileProgram->GetUniformLoc(pName: "gVertColor" ); |
1720 | m_pBorderTileProgram->m_LocOffset = m_pBorderTileProgram->GetUniformLoc(pName: "gOffset" ); |
1721 | m_pBorderTileProgram->m_LocScale = m_pBorderTileProgram->GetUniformLoc(pName: "gScale" ); |
1722 | } |
1723 | if(pCommand->m_pCapabilities->m_2DArrayTextures) |
1724 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY); |
1725 | else |
1726 | ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D); |
1727 | { |
1728 | CGLSL VertexShader; |
1729 | CGLSL FragmentShader; |
1730 | ShaderCompiler.AddDefine(pDefineName: "TW_TILE_TEXTURED" , pDefineValue: "" ); |
1731 | if(!pCommand->m_pCapabilities->m_2DArrayTextures) |
1732 | ShaderCompiler.AddDefine(pDefineName: "TW_TILE_3D_TEXTURED" , pDefineValue: "" ); |
1733 | VertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile_border.vert" , GL_VERTEX_SHADER); |
1734 | FragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile_border.frag" , GL_FRAGMENT_SHADER); |
1735 | ShaderCompiler.ClearDefines(); |
1736 | |
1737 | m_pBorderTileProgramTextured->CreateProgram(); |
1738 | m_pBorderTileProgramTextured->AddShader(pShader: &VertexShader); |
1739 | m_pBorderTileProgramTextured->AddShader(pShader: &FragmentShader); |
1740 | |
1741 | glBindAttribLocation(m_pBorderTileProgramTextured->GetProgramId(), 0, "inVertex" ); |
1742 | glBindAttribLocation(m_pBorderTileProgramTextured->GetProgramId(), 1, "inVertexTexCoord" ); |
1743 | |
1744 | m_pBorderTileProgramTextured->LinkProgram(); |
1745 | |
1746 | UseProgram(pProgram: m_pBorderTileProgramTextured); |
1747 | |
1748 | m_pBorderTileProgramTextured->m_LocPos = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gPos" ); |
1749 | m_pBorderTileProgramTextured->m_LocTextureSampler = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gTextureSampler" ); |
1750 | m_pBorderTileProgramTextured->m_LocColor = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gVertColor" ); |
1751 | m_pBorderTileProgramTextured->m_LocOffset = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gOffset" ); |
1752 | m_pBorderTileProgramTextured->m_LocScale = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gScale" ); |
1753 | } |
1754 | |
1755 | glUseProgram(0); |
1756 | } |
1757 | |
1758 | if(g_Config.m_Gfx3DTextureAnalysisRan == 0 || str_comp(a: g_Config.m_Gfx3DTextureAnalysisRenderer, b: pCommand->m_pRendererString) != 0 || str_comp(a: g_Config.m_Gfx3DTextureAnalysisVersion, b: pCommand->m_pVersionString) != 0) |
1759 | { |
1760 | AnalysisCorrect = IsTileMapAnalysisSucceeded(); |
1761 | if(AnalysisCorrect) |
1762 | { |
1763 | g_Config.m_Gfx3DTextureAnalysisRan = 1; |
1764 | str_copy(dst&: g_Config.m_Gfx3DTextureAnalysisRenderer, src: pCommand->m_pRendererString); |
1765 | str_copy(dst&: g_Config.m_Gfx3DTextureAnalysisVersion, src: pCommand->m_pVersionString); |
1766 | } |
1767 | } |
1768 | } |
1769 | |
1770 | if(!AnalysisCorrect || !HasAllFunc) |
1771 | { |
1772 | // downgrade to opengl 1.5 |
1773 | *pCommand->m_pInitError = -2; |
1774 | pCommand->m_pCapabilities->m_ContextMajor = 1; |
1775 | pCommand->m_pCapabilities->m_ContextMinor = 5; |
1776 | pCommand->m_pCapabilities->m_ContextPatch = 0; |
1777 | |
1778 | return false; |
1779 | } |
1780 | |
1781 | return true; |
1782 | } |
1783 | |
1784 | void CCommandProcessorFragment_OpenGL2::Cmd_Shutdown(const SCommand_Shutdown *pCommand) |
1785 | { |
1786 | // TODO: cleanup the OpenGL context too |
1787 | delete m_pTileProgram; |
1788 | delete m_pTileProgramTextured; |
1789 | delete m_pPrimitive3DProgram; |
1790 | delete m_pPrimitive3DProgramTextured; |
1791 | for(auto &BufferObject : m_vBufferObjectIndices) |
1792 | free(ptr: BufferObject.m_pData); |
1793 | } |
1794 | |
1795 | void CCommandProcessorFragment_OpenGL2::Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand) |
1796 | { |
1797 | if(m_HasShaders) |
1798 | { |
1799 | CGLSLPrimitiveProgram *pProgram = NULL; |
1800 | if(IsTexturedState(State: pCommand->m_State)) |
1801 | { |
1802 | pProgram = m_pPrimitive3DProgramTextured; |
1803 | } |
1804 | else |
1805 | pProgram = m_pPrimitive3DProgram; |
1806 | |
1807 | UseProgram(pProgram); |
1808 | |
1809 | SetState(State: pCommand->m_State, pProgram, Use2DArrayTextures: true); |
1810 | } |
1811 | else |
1812 | { |
1813 | CCommandProcessorFragment_OpenGL::SetState(State: pCommand->m_State, Use2DArrayTextures: true); |
1814 | } |
1815 | |
1816 | glEnableClientState(GL_VERTEX_ARRAY); |
1817 | glEnableClientState(GL_COLOR_ARRAY); |
1818 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
1819 | |
1820 | glVertexPointer(size: 2, GL_FLOAT, stride: sizeof(pCommand->m_pVertices[0]), pointer: pCommand->m_pVertices); |
1821 | glColorPointer(size: 4, GL_UNSIGNED_BYTE, stride: sizeof(pCommand->m_pVertices[0]), pointer: (uint8_t *)pCommand->m_pVertices + (ptrdiff_t)(sizeof(vec2))); |
1822 | glTexCoordPointer(size: 3, GL_FLOAT, stride: sizeof(pCommand->m_pVertices[0]), pointer: (uint8_t *)pCommand->m_pVertices + (ptrdiff_t)(sizeof(vec2) + sizeof(unsigned char) * 4)); |
1823 | |
1824 | switch(pCommand->m_PrimType) |
1825 | { |
1826 | case CCommandBuffer::PRIMTYPE_QUADS: |
1827 | glDrawArrays(GL_QUADS, first: 0, count: pCommand->m_PrimCount * 4); |
1828 | break; |
1829 | case CCommandBuffer::PRIMTYPE_TRIANGLES: |
1830 | glDrawArrays(GL_TRIANGLES, first: 0, count: pCommand->m_PrimCount * 3); |
1831 | break; |
1832 | default: |
1833 | dbg_msg(sys: "render" , fmt: "unknown primtype %d\n" , pCommand->m_PrimType); |
1834 | }; |
1835 | |
1836 | glDisableClientState(GL_VERTEX_ARRAY); |
1837 | glDisableClientState(GL_COLOR_ARRAY); |
1838 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); |
1839 | |
1840 | if(m_HasShaders) |
1841 | { |
1842 | glUseProgram(0); |
1843 | } |
1844 | } |
1845 | |
1846 | void CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferObject(const CCommandBuffer::SCommand_CreateBufferObject *pCommand) |
1847 | { |
1848 | void *pUploadData = pCommand->m_pUploadData; |
1849 | int Index = pCommand->m_BufferIndex; |
1850 | // create necessary space |
1851 | if((size_t)Index >= m_vBufferObjectIndices.size()) |
1852 | { |
1853 | for(int i = m_vBufferObjectIndices.size(); i < Index + 1; ++i) |
1854 | { |
1855 | m_vBufferObjectIndices.emplace_back(args: 0); |
1856 | } |
1857 | } |
1858 | |
1859 | GLuint VertBufferId = 0; |
1860 | |
1861 | glGenBuffers(1, &VertBufferId); |
1862 | glBindBuffer(GL_ARRAY_BUFFER, VertBufferId); |
1863 | glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(pCommand->m_DataSize), pUploadData, GL_STATIC_DRAW); |
1864 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
1865 | |
1866 | SBufferObject &BufferObject = m_vBufferObjectIndices[Index]; |
1867 | BufferObject.m_BufferObjectId = VertBufferId; |
1868 | BufferObject.m_DataSize = pCommand->m_DataSize; |
1869 | BufferObject.m_pData = static_cast<uint8_t *>(malloc(size: pCommand->m_DataSize)); |
1870 | if(pUploadData) |
1871 | mem_copy(dest: BufferObject.m_pData, source: pUploadData, size: pCommand->m_DataSize); |
1872 | |
1873 | if(pCommand->m_DeletePointer) |
1874 | free(ptr: pUploadData); |
1875 | } |
1876 | |
1877 | void CCommandProcessorFragment_OpenGL2::Cmd_RecreateBufferObject(const CCommandBuffer::SCommand_RecreateBufferObject *pCommand) |
1878 | { |
1879 | void *pUploadData = pCommand->m_pUploadData; |
1880 | int Index = pCommand->m_BufferIndex; |
1881 | SBufferObject &BufferObject = m_vBufferObjectIndices[Index]; |
1882 | |
1883 | glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectId); |
1884 | glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(pCommand->m_DataSize), pUploadData, GL_STATIC_DRAW); |
1885 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
1886 | |
1887 | BufferObject.m_DataSize = pCommand->m_DataSize; |
1888 | free(ptr: BufferObject.m_pData); |
1889 | BufferObject.m_pData = static_cast<uint8_t *>(malloc(size: pCommand->m_DataSize)); |
1890 | if(pUploadData) |
1891 | mem_copy(dest: BufferObject.m_pData, source: pUploadData, size: pCommand->m_DataSize); |
1892 | |
1893 | if(pCommand->m_DeletePointer) |
1894 | free(ptr: pUploadData); |
1895 | } |
1896 | |
1897 | void CCommandProcessorFragment_OpenGL2::Cmd_UpdateBufferObject(const CCommandBuffer::SCommand_UpdateBufferObject *pCommand) |
1898 | { |
1899 | void *pUploadData = pCommand->m_pUploadData; |
1900 | int Index = pCommand->m_BufferIndex; |
1901 | SBufferObject &BufferObject = m_vBufferObjectIndices[Index]; |
1902 | |
1903 | glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectId); |
1904 | glBufferSubData(GL_ARRAY_BUFFER, (GLintptr)(pCommand->m_pOffset), (GLsizeiptr)(pCommand->m_DataSize), pUploadData); |
1905 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
1906 | |
1907 | if(pUploadData) |
1908 | mem_copy(dest: BufferObject.m_pData + (ptrdiff_t)pCommand->m_pOffset, source: pUploadData, size: pCommand->m_DataSize); |
1909 | |
1910 | if(pCommand->m_DeletePointer) |
1911 | free(ptr: pUploadData); |
1912 | } |
1913 | |
1914 | void CCommandProcessorFragment_OpenGL2::Cmd_CopyBufferObject(const CCommandBuffer::SCommand_CopyBufferObject *pCommand) |
1915 | { |
1916 | int WriteIndex = pCommand->m_WriteBufferIndex; |
1917 | int ReadIndex = pCommand->m_ReadBufferIndex; |
1918 | |
1919 | SBufferObject &ReadBufferObject = m_vBufferObjectIndices[ReadIndex]; |
1920 | SBufferObject &WriteBufferObject = m_vBufferObjectIndices[WriteIndex]; |
1921 | |
1922 | mem_copy(dest: WriteBufferObject.m_pData + (ptrdiff_t)pCommand->m_WriteOffset, source: ReadBufferObject.m_pData + (ptrdiff_t)pCommand->m_ReadOffset, size: pCommand->m_CopySize); |
1923 | |
1924 | glBindBuffer(GL_ARRAY_BUFFER, WriteBufferObject.m_BufferObjectId); |
1925 | glBufferSubData(GL_ARRAY_BUFFER, (GLintptr)(pCommand->m_WriteOffset), (GLsizeiptr)(pCommand->m_CopySize), WriteBufferObject.m_pData + (ptrdiff_t)pCommand->m_WriteOffset); |
1926 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
1927 | } |
1928 | |
1929 | void CCommandProcessorFragment_OpenGL2::Cmd_DeleteBufferObject(const CCommandBuffer::SCommand_DeleteBufferObject *pCommand) |
1930 | { |
1931 | int Index = pCommand->m_BufferIndex; |
1932 | SBufferObject &BufferObject = m_vBufferObjectIndices[Index]; |
1933 | |
1934 | glDeleteBuffers(1, &BufferObject.m_BufferObjectId); |
1935 | |
1936 | free(ptr: BufferObject.m_pData); |
1937 | BufferObject.m_pData = NULL; |
1938 | } |
1939 | |
1940 | void CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferContainer(const CCommandBuffer::SCommand_CreateBufferContainer *pCommand) |
1941 | { |
1942 | int Index = pCommand->m_BufferContainerIndex; |
1943 | // create necessary space |
1944 | if((size_t)Index >= m_vBufferContainers.size()) |
1945 | { |
1946 | for(int i = m_vBufferContainers.size(); i < Index + 1; ++i) |
1947 | { |
1948 | SBufferContainer Container; |
1949 | Container.m_ContainerInfo.m_Stride = 0; |
1950 | Container.m_ContainerInfo.m_VertBufferBindingIndex = -1; |
1951 | m_vBufferContainers.push_back(x: Container); |
1952 | } |
1953 | } |
1954 | |
1955 | SBufferContainer &BufferContainer = m_vBufferContainers[Index]; |
1956 | |
1957 | for(size_t i = 0; i < pCommand->m_AttrCount; ++i) |
1958 | { |
1959 | BufferContainer.m_ContainerInfo.m_vAttributes.push_back(x: pCommand->m_pAttributes[i]); |
1960 | } |
1961 | |
1962 | BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride; |
1963 | BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex = pCommand->m_VertBufferBindingIndex; |
1964 | } |
1965 | |
1966 | void CCommandProcessorFragment_OpenGL2::Cmd_UpdateBufferContainer(const CCommandBuffer::SCommand_UpdateBufferContainer *pCommand) |
1967 | { |
1968 | SBufferContainer &BufferContainer = m_vBufferContainers[pCommand->m_BufferContainerIndex]; |
1969 | |
1970 | BufferContainer.m_ContainerInfo.m_vAttributes.clear(); |
1971 | |
1972 | for(size_t i = 0; i < pCommand->m_AttrCount; ++i) |
1973 | { |
1974 | BufferContainer.m_ContainerInfo.m_vAttributes.push_back(x: pCommand->m_pAttributes[i]); |
1975 | } |
1976 | |
1977 | BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride; |
1978 | BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex = pCommand->m_VertBufferBindingIndex; |
1979 | } |
1980 | |
1981 | void CCommandProcessorFragment_OpenGL2::Cmd_DeleteBufferContainer(const CCommandBuffer::SCommand_DeleteBufferContainer *pCommand) |
1982 | { |
1983 | SBufferContainer &BufferContainer = m_vBufferContainers[pCommand->m_BufferContainerIndex]; |
1984 | |
1985 | if(pCommand->m_DestroyAllBO) |
1986 | { |
1987 | int VertBufferId = BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex; |
1988 | if(VertBufferId != -1) |
1989 | { |
1990 | glDeleteBuffers(1, &m_vBufferObjectIndices[VertBufferId].m_BufferObjectId); |
1991 | |
1992 | free(ptr: m_vBufferObjectIndices[VertBufferId].m_pData); |
1993 | m_vBufferObjectIndices[VertBufferId].m_pData = NULL; |
1994 | } |
1995 | } |
1996 | |
1997 | BufferContainer.m_ContainerInfo.m_vAttributes.clear(); |
1998 | } |
1999 | |
2000 | void CCommandProcessorFragment_OpenGL2::Cmd_IndicesRequiredNumNotify(const CCommandBuffer::SCommand_IndicesRequiredNumNotify *pCommand) |
2001 | { |
2002 | } |
2003 | |
2004 | void CCommandProcessorFragment_OpenGL2::Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand) |
2005 | { |
2006 | int Index = pCommand->m_BufferContainerIndex; |
2007 | // if space not there return |
2008 | if((size_t)Index >= m_vBufferContainers.size()) |
2009 | return; |
2010 | |
2011 | SBufferContainer &BufferContainer = m_vBufferContainers[Index]; |
2012 | |
2013 | CGLSLTileProgram *pProgram = NULL; |
2014 | if(IsTexturedState(State: pCommand->m_State)) |
2015 | pProgram = m_pBorderTileProgramTextured; |
2016 | else |
2017 | pProgram = m_pBorderTileProgram; |
2018 | UseProgram(pProgram); |
2019 | |
2020 | SetState(State: pCommand->m_State, pProgram, Use2DArrayTextures: true); |
2021 | pProgram->SetUniformVec4(Loc: pProgram->m_LocColor, Count: 1, pValue: (float *)&pCommand->m_Color); |
2022 | |
2023 | pProgram->SetUniformVec2(Loc: pProgram->m_LocOffset, Count: 1, pValue: (float *)&pCommand->m_Offset); |
2024 | pProgram->SetUniformVec2(Loc: pProgram->m_LocScale, Count: 1, pValue: (float *)&pCommand->m_Scale); |
2025 | |
2026 | bool IsTextured = BufferContainer.m_ContainerInfo.m_vAttributes.size() == 2; |
2027 | |
2028 | SBufferObject &BufferObject = m_vBufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex]; |
2029 | |
2030 | glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectId); |
2031 | |
2032 | glEnableVertexAttribArray(0); |
2033 | glVertexAttribPointer(0, 2, GL_FLOAT, false, BufferContainer.m_ContainerInfo.m_Stride, BufferContainer.m_ContainerInfo.m_vAttributes[0].m_pOffset); |
2034 | if(IsTextured) |
2035 | { |
2036 | glEnableVertexAttribArray(1); |
2037 | glVertexAttribIPointer(1, 4, GL_UNSIGNED_BYTE, BufferContainer.m_ContainerInfo.m_Stride, BufferContainer.m_ContainerInfo.m_vAttributes[1].m_pOffset); |
2038 | } |
2039 | |
2040 | size_t RealDrawCount = pCommand->m_DrawNum * 4; |
2041 | GLint RealOffset = (GLint)((((size_t)(uintptr_t)(pCommand->m_pIndicesOffset)) / (6 * sizeof(unsigned int))) * 4); |
2042 | glDrawArrays(GL_QUADS, first: RealOffset, count: RealDrawCount); |
2043 | |
2044 | glDisableVertexAttribArray(0); |
2045 | if(IsTextured) |
2046 | glDisableVertexAttribArray(1); |
2047 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
2048 | glUseProgram(0); |
2049 | } |
2050 | |
2051 | void CCommandProcessorFragment_OpenGL2::Cmd_RenderTileLayer(const CCommandBuffer::SCommand_RenderTileLayer *pCommand) |
2052 | { |
2053 | int Index = pCommand->m_BufferContainerIndex; |
2054 | // if space not there return |
2055 | if((size_t)Index >= m_vBufferContainers.size()) |
2056 | return; |
2057 | |
2058 | SBufferContainer &BufferContainer = m_vBufferContainers[Index]; |
2059 | |
2060 | if(pCommand->m_IndicesDrawNum == 0) |
2061 | { |
2062 | return; // nothing to draw |
2063 | } |
2064 | |
2065 | CGLSLTileProgram *pProgram = NULL; |
2066 | if(IsTexturedState(State: pCommand->m_State)) |
2067 | { |
2068 | pProgram = m_pTileProgramTextured; |
2069 | } |
2070 | else |
2071 | pProgram = m_pTileProgram; |
2072 | |
2073 | UseProgram(pProgram); |
2074 | |
2075 | SetState(State: pCommand->m_State, pProgram, Use2DArrayTextures: true); |
2076 | pProgram->SetUniformVec4(Loc: pProgram->m_LocColor, Count: 1, pValue: (float *)&pCommand->m_Color); |
2077 | |
2078 | bool IsTextured = BufferContainer.m_ContainerInfo.m_vAttributes.size() == 2; |
2079 | |
2080 | SBufferObject &BufferObject = m_vBufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex]; |
2081 | |
2082 | glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectId); |
2083 | |
2084 | glEnableVertexAttribArray(0); |
2085 | glVertexAttribPointer(0, 2, GL_FLOAT, false, BufferContainer.m_ContainerInfo.m_Stride, BufferContainer.m_ContainerInfo.m_vAttributes[0].m_pOffset); |
2086 | if(IsTextured) |
2087 | { |
2088 | glEnableVertexAttribArray(1); |
2089 | glVertexAttribIPointer(1, 4, GL_UNSIGNED_BYTE, BufferContainer.m_ContainerInfo.m_Stride, BufferContainer.m_ContainerInfo.m_vAttributes[1].m_pOffset); |
2090 | } |
2091 | |
2092 | for(int i = 0; i < pCommand->m_IndicesDrawNum; ++i) |
2093 | { |
2094 | size_t RealDrawCount = (pCommand->m_pDrawCount[i] / 6) * 4; |
2095 | GLint RealOffset = (GLint)((((size_t)(uintptr_t)(pCommand->m_pIndicesOffsets[i])) / (6 * sizeof(unsigned int))) * 4); |
2096 | glDrawArrays(GL_QUADS, first: RealOffset, count: RealDrawCount); |
2097 | } |
2098 | |
2099 | glDisableVertexAttribArray(0); |
2100 | if(IsTextured) |
2101 | glDisableVertexAttribArray(1); |
2102 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
2103 | glUseProgram(0); |
2104 | } |
2105 | |
2106 | #undef BACKEND_GL_MODERN_API |
2107 | |
2108 | #endif |
2109 | |
2110 | #endif |
2111 | |