From b3b75a716c720d1d78a9d9a2c541667443c6a7a7 Mon Sep 17 00:00:00 2001 From: superp00t Date: Wed, 15 Nov 2023 19:31:16 -0500 Subject: [PATCH] feat(gx): add uncompleted CGxDeviceGLSDL targeting Windows and Linux --- src/gx/CGxDevice.cpp | 14 +- src/gx/CMakeLists.txt | 15 + src/gx/glsdl/CGxDeviceGLSDL.cpp | 1326 ++++++++++++++ src/gx/glsdl/CGxDeviceGLSDL.hpp | 82 + src/gx/glsdl/GL.cpp | 50 + src/gx/glsdl/GL.hpp | 33 + src/gx/glsdl/GLBatch.hpp | 45 + src/gx/glsdl/GLBuffer.cpp | 130 ++ src/gx/glsdl/GLBuffer.hpp | 47 + src/gx/glsdl/GLBufferPool.hpp | 8 + src/gx/glsdl/GLDebugMipmap2D.hpp | 8 + src/gx/glsdl/GLFramebuffer.cpp | 150 ++ src/gx/glsdl/GLFramebuffer.hpp | 38 + src/gx/glsdl/GLGLSLProgram.cpp | 7 + src/gx/glsdl/GLGLSLProgram.hpp | 14 + src/gx/glsdl/GLMipmap.cpp | 480 ++++++ src/gx/glsdl/GLMipmap.hpp | 62 + src/gx/glsdl/GLObject.cpp | 22 + src/gx/glsdl/GLObject.hpp | 22 + src/gx/glsdl/GLPixelShader.cpp | 17 + src/gx/glsdl/GLPixelShader.hpp | 12 + src/gx/glsdl/GLPool.hpp | 57 + src/gx/glsdl/GLSDLContext.cpp | 48 + src/gx/glsdl/GLSDLContext.hpp | 20 + src/gx/glsdl/GLSDLDevice.cpp | 2769 ++++++++++++++++++++++++++++++ src/gx/glsdl/GLSDLDevice.hpp | 190 ++ src/gx/glsdl/GLSDLWindow.cpp | 131 ++ src/gx/glsdl/GLSDLWindow.hpp | 44 + src/gx/glsdl/GLShader.cpp | 135 ++ src/gx/glsdl/GLShader.hpp | 75 + src/gx/glsdl/GLShaderInput.hpp | 33 + src/gx/glsdl/GLTexture.cpp | 632 +++++++ src/gx/glsdl/GLTexture.hpp | 101 ++ src/gx/glsdl/GLTypes.cpp | 58 + src/gx/glsdl/GLTypes.hpp | 376 ++++ src/gx/glsdl/GLUtil.cpp | 22 + src/gx/glsdl/GLUtil.hpp | 9 + src/gx/glsdl/GLVertexArray.cpp | 316 ++++ src/gx/glsdl/GLVertexArray.hpp | 46 + src/gx/glsdl/GLVertexFormat.hpp | 13 + src/gx/glsdl/GLVertexShader.cpp | 17 + src/gx/glsdl/GLVertexShader.hpp | 12 + 42 files changed, 7681 insertions(+), 5 deletions(-) create mode 100644 src/gx/glsdl/CGxDeviceGLSDL.cpp create mode 100644 src/gx/glsdl/CGxDeviceGLSDL.hpp create mode 100644 src/gx/glsdl/GL.cpp create mode 100644 src/gx/glsdl/GL.hpp create mode 100644 src/gx/glsdl/GLBatch.hpp create mode 100644 src/gx/glsdl/GLBuffer.cpp create mode 100644 src/gx/glsdl/GLBuffer.hpp create mode 100644 src/gx/glsdl/GLBufferPool.hpp create mode 100644 src/gx/glsdl/GLDebugMipmap2D.hpp create mode 100644 src/gx/glsdl/GLFramebuffer.cpp create mode 100644 src/gx/glsdl/GLFramebuffer.hpp create mode 100644 src/gx/glsdl/GLGLSLProgram.cpp create mode 100644 src/gx/glsdl/GLGLSLProgram.hpp create mode 100644 src/gx/glsdl/GLMipmap.cpp create mode 100644 src/gx/glsdl/GLMipmap.hpp create mode 100644 src/gx/glsdl/GLObject.cpp create mode 100644 src/gx/glsdl/GLObject.hpp create mode 100644 src/gx/glsdl/GLPixelShader.cpp create mode 100644 src/gx/glsdl/GLPixelShader.hpp create mode 100644 src/gx/glsdl/GLPool.hpp create mode 100644 src/gx/glsdl/GLSDLContext.cpp create mode 100644 src/gx/glsdl/GLSDLContext.hpp create mode 100644 src/gx/glsdl/GLSDLDevice.cpp create mode 100644 src/gx/glsdl/GLSDLDevice.hpp create mode 100644 src/gx/glsdl/GLSDLWindow.cpp create mode 100644 src/gx/glsdl/GLSDLWindow.hpp create mode 100644 src/gx/glsdl/GLShader.cpp create mode 100644 src/gx/glsdl/GLShader.hpp create mode 100644 src/gx/glsdl/GLShaderInput.hpp create mode 100644 src/gx/glsdl/GLTexture.cpp create mode 100644 src/gx/glsdl/GLTexture.hpp create mode 100644 src/gx/glsdl/GLTypes.cpp create mode 100644 src/gx/glsdl/GLTypes.hpp create mode 100644 src/gx/glsdl/GLUtil.cpp create mode 100644 src/gx/glsdl/GLUtil.hpp create mode 100644 src/gx/glsdl/GLVertexArray.cpp create mode 100644 src/gx/glsdl/GLVertexArray.hpp create mode 100644 src/gx/glsdl/GLVertexFormat.hpp create mode 100644 src/gx/glsdl/GLVertexShader.cpp create mode 100644 src/gx/glsdl/GLVertexShader.hpp diff --git a/src/gx/CGxDevice.cpp b/src/gx/CGxDevice.cpp index 16387a9..a5c5653 100644 --- a/src/gx/CGxDevice.cpp +++ b/src/gx/CGxDevice.cpp @@ -14,6 +14,10 @@ #include "gx/d3d/CGxDeviceD3d.hpp" #endif +#if defined(WHOA_SYSTEM_LINUX) || defined(WHOA_SYSTEM_WIN) + #include "gx/glsdl/CGxDeviceGLSDL.hpp" +#endif + #if defined(WHOA_SYSTEM_MAC) #include "gx/gll/CGxDeviceGLL.hpp" #endif @@ -119,13 +123,13 @@ CGxDevice* CGxDevice::NewGLL() { } #endif -CGxDevice* CGxDevice::NewOpenGl() { - // TODO - // auto m = SMemAlloc(sizeof(CGxDeviceOpenGl), __FILE__, __LINE__, 0x0); - // return new (m) CGxDeviceOpenGl(); - return nullptr; +#if defined(WHOA_SYSTEM_WIN) || defined(WHOA_SYSTEM_LINUX) +CGxDevice* CGxDevice::NewOpenGl() { + auto m = SMemAlloc(sizeof(CGxDeviceGLSDL), __FILE__, __LINE__, 0x0); + return new (m) CGxDeviceGLSDL(); } +#endif uint32_t CGxDevice::PrimCalcCount(EGxPrim primType, uint32_t count) { auto div = CGxDevice::s_primVtxDiv[primType]; diff --git a/src/gx/CMakeLists.txt b/src/gx/CMakeLists.txt index 33338b0..c99ec4e 100644 --- a/src/gx/CMakeLists.txt +++ b/src/gx/CMakeLists.txt @@ -19,6 +19,12 @@ if(WHOA_SYSTEM_MAC) list(APPEND GX_SOURCES ${GLL_SOURCES}) endif() +# Build OpenGL/SDL graphics device on Windows and Linux +if(WHOA_SYSTEM_WIN OR WHOA_SYSTEM_LINUX) + file(GLOB GLSDL_SOURCES "glsdl/*.cpp") + list(APPEND GX_SOURCES ${GLSDL_SOURCES}) +endif() + add_library(gx STATIC ${GX_SOURCES}) target_include_directories(gx @@ -47,6 +53,15 @@ if(WHOA_SYSTEM_WIN) ) endif() +# Link SDL3 and GLEW for Windows and Linux +if (WHOA_SYSTEM_WIN OR WHOA_SYSTEM_LINUX) + target_link_libraries(gx + PRIVATE + SDL3::SDL3 + libglew_static + ) +endif() + if(WHOA_SYSTEM_MAC) target_link_libraries(gx PRIVATE diff --git a/src/gx/glsdl/CGxDeviceGLSDL.cpp b/src/gx/glsdl/CGxDeviceGLSDL.cpp new file mode 100644 index 0000000..4c814d8 --- /dev/null +++ b/src/gx/glsdl/CGxDeviceGLSDL.cpp @@ -0,0 +1,1326 @@ +#include "gx/glsdl/CGxDeviceGLSDL.hpp" +#include "event/Input.hpp" +#include "gx/Blit.hpp" +#include "gx/CGxBatch.hpp" +#include "gx/Shader.hpp" +#include "gx/Window.hpp" +#include "gx/texture/CGxTex.hpp" +#include +#include +#include +#include + +GLEnum CGxDeviceGLSDL::s_glCubeMapFaces[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +GLEnum CGxDeviceGLSDL::s_glDstBlend[] = { + GL_ZERO, // GxBlend_Opaque + GL_ZERO, // GxBlend_AlphaKey + GL_ONE_MINUS_SRC_ALPHA, // GxBlend_Alpha + GL_ONE, // GxBlend_Add + GL_ZERO, // GxBlend_Mod + GL_SRC_COLOR, // GxBlend_Mod2x + GL_ONE, // GxBlend_ModAdd + GL_ONE, // GxBlend_InvSrcAlphaAdd + GL_ZERO, // GxBlend_InvSrcAlphaOpaque + GL_ZERO, // GxBlend_SrcAlphaOpaque + GL_ONE, // GxBlend_NoAlphaAdd + GL_ONE_MINUS_CONSTANT_ALPHA // GxBlend_ConstantAlpha +}; + +GLEnum CGxDeviceGLSDL::s_glSrcBlend[] = { + GL_ONE, // GxBlend_Opaque + GL_ONE, // GxBlend_AlphaKey + GL_SRC_ALPHA, // GxBlend_Alpha + GL_SRC_ALPHA, // GxBlend_Add + GL_DST_COLOR, // GxBlend_Mod + GL_DST_COLOR, // GxBlend_Mod2x + GL_DST_COLOR, // GxBlend_ModAdd + GL_ONE_MINUS_SRC_ALPHA, // GxBlend_InvSrcAlphaAdd + GL_ONE_MINUS_SRC_ALPHA, // GxBlend_InvSrcAlphaOpaque + GL_SRC_ALPHA, // GxBlend_SrcAlphaOpaque + GL_ONE, // GxBlend_NoAlphaAdd + GL_CONSTANT_ALPHA // GxBlend_ConstantAlpha +}; + +GLTextureFormat CGxDeviceGLSDL::s_gxTexFmtToGLSDLFmt[] = { + GLTF_INVALID, // GxTex_Unknown + GLTF_ABGR8888, // GxTex_Abgr8888 + GLTF_ARGB8888, // GxTex_Argb8888 + GLTF_ARGB4444, // GxTex_Argb4444 + GLTF_ARGB1555, // GxTex_Argb1555 + GLTF_RGB565, // GxTex_Rgb565 + GLTF_DXT1, // GxTex_Dxt1 + GLTF_DXT3, // GxTex_Dxt3 + GLTF_DXT5, // GxTex_Dxt5 + GLTF_INVALID, // GxTex_Uv88 + GLTF_INVALID, // GxTex_Gr1616F + GLTF_INVALID, // GxTex_R32F + GLTF_D24 // GxTex_D24X8 +}; + +GLEnum CGxDeviceGLSDL::s_poolTarget2BufferFormat[] = { + GL_ZERO, // GxPoolTarget_Vertex + GL_UNSIGNED_SHORT // GxPoolTarget_Index +}; + +GLEnum CGxDeviceGLSDL::s_poolTarget2BufferType[] = { + GL_ARRAY_BUFFER, // GxPoolTarget_Vertex + GL_ELEMENT_ARRAY_BUFFER // GxPoolTarget_Index +}; + +GLEnum CGxDeviceGLSDL::s_poolUsage2BufferUsage[] = { + GL_STATIC_DRAW, // GxPoolUsage_Static + GL_DYNAMIC_DRAW, // GxPoolUsage_Dynamic + GL_DYNAMIC_DRAW // GxPoolUsage_Stream +}; + +GLEnum CGxDeviceGLSDL::s_primitiveConversion[] = { + GL_POINTS, // GxPrim_Points + GL_LINES, // GxPrim_Lines + GL_LINE_STRIP, // GxPrim_LineStrip + GL_TRIANGLES, // GxPrim_Triangles + GL_TRIANGLE_STRIP, // GxPrim_TriangleStrip + GL_TRIANGLE_FAN, // GxPrim_TriangleFan + GL_ZERO // GxPrims_Last +}; + +CGxDeviceGLSDL::CGxDeviceGLSDL() : CGxDevice() { + // TODO + + this->m_api = GxApi_OpenGl; + this->m_caps.m_colorFormat = GxCF_rgba; + + // TODO + + this->DeviceCreatePools(); + this->DeviceCreateStreamBufs(); + + // TODO +} + +char* CGxDeviceGLSDL::BufLock(CGxBuf* buf) { + CGxDevice::BufLock(buf); + return this->IBufLock(buf); +} + +int32_t CGxDeviceGLSDL::BufUnlock(CGxBuf* buf, uint32_t size) { + CGxDevice::BufUnlock(buf, size); + return this->IBufUnlock(buf); +} + +void CGxDeviceGLSDL::BufData(CGxBuf* buf, const void* data, size_t size, uintptr_t offset) { + CGxDevice::BufData(buf, data, size, offset); + + auto bufData = this->IBufLock(buf); + memcpy(&bufData[offset], data, size); + this->IBufUnlock(buf); +} + +void CGxDeviceGLSDL::CapsWindowSize(CRect& rect) { + CRect windowRect = this->DeviceCurWindow(); + + rect.minX = windowRect.minX; + rect.minY = windowRect.minY; + rect.maxX = windowRect.maxX; + rect.maxY = windowRect.maxY; +} + +void CGxDeviceGLSDL::CapsWindowSizeInScreenCoords(CRect& dst) { + if (this->IDevIsWindowed()) { + auto windowRect = this->DeviceCurWindow(); + auto deviceRect = this->m_GLSDLWindow.GetRect(); + + dst.minX = windowRect.minX + deviceRect.origin.x; + dst.maxX = windowRect.maxX + deviceRect.origin.x; + dst.minY = windowRect.minY + deviceRect.origin.y; + dst.maxY = windowRect.maxY + deviceRect.origin.y; + } else { + dst = this->DeviceCurWindow(); + } +} + +int32_t CGxDeviceGLSDL::DeviceCreate(int32_t (*windowProc)(void* window, uint32_t message, uintptr_t wparam, intptr_t lparam), const CGxFormat& format) { + GLSDLWindowRect rect; + Rect* bounds; + Rect* zoomedBounds = GetSavedZoomedWindowBounds(); + + if ( + zoomedBounds + && zoomedBounds->bottom - zoomedBounds->top > 599 + && zoomedBounds->right - zoomedBounds->left > 799 + ) { + bounds = GetSavedZoomedWindowBounds(); + } else { + bounds = GetSavedWindowBounds(); + } + + if ( + bounds->bottom - bounds->top > 599 + && bounds->right - bounds->left > 799 + ) { + rect.origin.x = bounds->left; + rect.origin.y = bounds->top; + rect.size.width = bounds->right - bounds->left; + rect.size.height = bounds->bottom - bounds->top; + } else { + Rect newBounds = { + 0, + 0, + static_cast(std::floor((static_cast(format.size.y) / static_cast(format.size.x)) * 1024.0f)), + 1024, + }; + + SetSavedWindowBounds(newBounds); + + rect.origin.x = newBounds.left; + rect.origin.y = newBounds.top; + rect.size.width = newBounds.right; + rect.size.height = newBounds.bottom; + } + + this->m_GLSDLWindow.Create("World of Warcraft", rect, GLTF_D24, 1); + this->m_GLSDLDevice.Init(&this->m_GLSDLWindow, "WoW", 0); + // this->m_GLSDLDevice.Init(&this->m_GLSDLWindow, "WoW", 4); + + GLSDLDevice::SetOption(GLSDLDevice::eShaderConstantBindings, false); + GLSDLDevice::SetOption(GLSDLDevice::eUseMTGL, true); + + if (CGxDevice::DeviceCreate(windowProc, format)) { + // this->m_glWindow.Show(); + + GLSDLDevice::SetOption(GLSDLDevice::eUseHybridShader, true); + + this->ISetCaps(format); + + this->m_context = 1; + + this->ICursorCreate(format); + + return 1; + } else { + return 0; + } +} + +int32_t CGxDeviceGLSDL::DeviceSetFormat(const CGxFormat& format) { + static GLTextureFormat glSDLTexFormats[] = { + GLTF_RGB565, + GLTF_XRGB8888, + GLTF_ARGB8888, + GLTF_A2RGB10, + GLTF_D16, + GLTF_D24, + GLTF_D24S8, + GLTF_D32 + }; + + bool v7 = false; + bool v10 = false; + + Rect v15 = { + 0, + 0, + static_cast(format.size.y), + static_cast(format.size.x) + }; + + if (format.window && format.maximize != 1) { + Rect* zoomedBounds = GetSavedZoomedWindowBounds(); + Rect* bounds; + + if ( + zoomedBounds + && zoomedBounds->bottom - zoomedBounds->top > 599 + && zoomedBounds->right - zoomedBounds->left > 799 + ) { + bounds = GetSavedZoomedWindowBounds(); + } else { + bounds = GetSavedWindowBounds(); + } + + if ( + bounds->bottom - bounds->top > 599 + && bounds->right - bounds->left > 799 + ) { + v15.right = bounds->right - bounds->left; + v15.bottom = bounds->bottom - bounds->top; + } else { + Rect newBounds = { + 0, + 0, + static_cast(std::floor((static_cast(format.size.y) / static_cast(format.size.x)) * 1024.0f)), + 1024, + }; + + SetSavedWindowBounds(newBounds); + + v15.right = newBounds.right; + v15.bottom = newBounds.bottom; + } + + // TODO GxMacPrivate::SetAspectRatio(static_cast(format.size.x) / static_cast(format.size.y)); + + // TODO auto v8 = LMGetMainDevice(); + // TODO GxMacPrivate::ConstrainWindowRect(v15, v8); + + v7 = true; + v10 = true; + } + + CRect wind = { + 0.0f, + 0.0f, + static_cast(v15.bottom - v15.top), + static_cast(v15.right - v15.left) + }; + + this->DeviceSetDefWindow(wind); + + this->m_GLSDLDevice.SetDisplay( + v15.right - v15.left, + v15.bottom - v15.top, + glSDLTexFormats[format.colorFormat], + glSDLTexFormats[format.depthFormat], + format.refreshRate, + v7, + format.window ^ 1, + format.sampleCount + ); + + OsInputPostEvent(OS_INPUT_SIZE, v15.right - v15.left, v15.bottom - v15.top, 0, 0); + + // TODO + // GameFrame::SetSize(v15.right - v15.left, v15.bottom - v15.top); + + // TODO + // this->m_GLSDLDevice.SetVSync(format.vsync != 0); + + this->IStateSetGLSDLDefaults(); + + if (v10) { + // TODO + // this->m_GLSDLDevice.RestoreDefaultGamma(); + } else { + // TODO + // - unknown vtable call + } + + // TODO + // - this seems to belong elsewhere + CGxDevice::DeviceSetFormat(format); + + return 1; +} + +void* CGxDeviceGLSDL::DeviceWindow() { + return &this->m_GLSDLWindow; +} + +void CGxDeviceGLSDL::Draw(CGxBatch* batch, int32_t indexed) { + if (!this->m_context) { + return; + } + + // TODO + // unk conditional check early return + + this->IStateSync(); + this->ValidateDraw(batch, indexed); + + uint32_t baseIndex = 0; + if (!this->m_caps.int10) { + baseIndex = this->m_primVertexFormatBuf[0]->m_index / this->m_primVertexFormatBuf[0]->m_itemSize; + } + + if (indexed) { + this->m_GLSDLDevice.DrawIndexed( + CGxDeviceGLSDL::s_primitiveConversion[batch->m_primType], + batch->m_minIndex, + batch->m_maxIndex, + baseIndex, + batch->m_start + (this->m_primIndexBuf->m_index / 2), + batch->m_count + ); + } else { + this->m_GLSDLDevice.Draw( + CGxDeviceGLSDL::s_primitiveConversion[batch->m_primType], + baseIndex, + batch->m_count + ); + } +} + +char* CGxDeviceGLSDL::IBufLock(CGxBuf* buf) { + if (!this->m_context) { + // TODO + // EmergencyMem + return nullptr; + } + + CGxPool* pool = buf->m_pool; + + GLBuffer::eMapFlag mapFlag = GLBuffer::GLMap_None; + + if (pool->m_usage == GxPoolUsage_Stream) { + mapFlag = GLBuffer::GLMap_Unk1; + + uint32_t v7 = pool->unk1C + buf->m_itemSize - 1; + uint32_t alignedNext = v7 - v7 % buf->m_itemSize; + + if (alignedNext + buf->m_size > pool->m_size) { + pool->Discard(); + alignedNext = 0; + mapFlag = GLBuffer::GLMap_Unk2; + } + + buf->m_index = alignedNext; + pool->unk1C = alignedNext + buf->m_size; + } else { + mapFlag = pool->m_usage == GxPoolUsage_Dynamic ? GLBuffer::GLMap_Unk1 : GLBuffer::GLMap_None; + } + + GLBuffer* glBuf = reinterpret_cast(pool->m_apiSpecific); + + if (!glBuf) { + glBuf = this->m_GLSDLDevice.CreateBuffer( + CGxDeviceGLSDL::s_poolTarget2BufferType[pool->m_target], + pool->m_size, + nullptr, + CGxDeviceGLSDL::s_poolUsage2BufferUsage[pool->m_usage], + CGxDeviceGLSDL::s_poolTarget2BufferFormat[pool->m_target] + ); + + pool->m_apiSpecific = glBuf; + } + + if (!glBuf) { + // TODO + // EmergencyMem + return nullptr; + } + + return glBuf->Map(buf->m_index, buf->m_size, mapFlag); +} + +int32_t CGxDeviceGLSDL::IBufUnlock(CGxBuf* buf) { + CGxPool* pool = buf->m_pool; + + // TODO + // EmergencyMem + + auto glBuf = reinterpret_cast(pool->m_apiSpecific); + + glBuf->Unmap(0); + buf->unk1D = 1; + + return 1; +} + +void CGxDeviceGLSDL::IRsSendToHw(EGxRenderState which) { + auto state = &this->m_appRenderStates[which]; + + switch (which) { + // TODO + // - remaining render states + + case GxRs_BlendingMode: { + int32_t blend = static_cast(state->m_value); + + if (blend <= GxBlend_AlphaKey) { + this->m_GLSDLDevice.SetAlphaBlendEnable(0); + } else { + this->m_GLSDLDevice.SetAlphaBlendEnable(1); + this->m_GLSDLDevice.SetAlphaBlend( + CGxDeviceGLSDL::s_glSrcBlend[blend], + CGxDeviceGLSDL::s_glDstBlend[blend], + GL_FUNC_ADD + ); + } + + break; + } + + case GxRs_AlphaRef: { + int32_t alphaRef = static_cast(state->m_value); + + if (alphaRef <= 0) { + this->m_GLSDLDevice.SetAlphaTestEnable(0); + } else { + this->m_GLSDLDevice.SetAlphaTest(GL_GEQUAL, static_cast(alphaRef) / static_cast(255)); + this->m_GLSDLDevice.SetAlphaTestEnable(1); + } + + break; + } + + case GxRs_FogStart: { + auto fogStart = static_cast(state->m_value); + + this->m_GLSDLDevice.SetFogParam(GL_FOG_START, fogStart); + + break; + } + + case GxRs_FogEnd: { + auto fogEnd = static_cast(state->m_value); + + this->m_GLSDLDevice.SetFogParam(GL_FOG_END, fogEnd); + + break; + } + + case GxRs_FogColor: { + auto fogColor = static_cast(state->m_value); + + this->m_GLSDLDevice.SetFogColor( + fogColor.r / 255.0f, + fogColor.g / 255.0f, + fogColor.b / 255.0f, + fogColor.a / 255.0f + ); + + break; + } + + case GxRs_Lighting: { + int32_t lightingEnable = 0; + + if (this->MasterEnable(GxMasterEnable_Lighting)) { + lightingEnable = static_cast(state->m_value); + } + + this->m_GLSDLDevice.SetLightingEnable(lightingEnable); + + break; + } + + case GxRs_Fog: { + int32_t fogEnable = 0; + + if (this->MasterEnable(GxMasterEnable_Fog)) { + fogEnable = static_cast(state->m_value); + } + + this->m_GLSDLDevice.SetFogEnable(fogEnable); + + break; + } + + case GxRs_DepthTest: { + int32_t depthTest = 0; + + if (this->MasterEnable(GxMasterEnable_DepthTest)) { + depthTest = static_cast(state->m_value); + } + + this->m_GLSDLDevice.SetDepthTestEnable(depthTest != 0); + + break; + } + + case GxRs_DepthFunc: { + static GLEnum s_glDepthFunc[] = { + GL_LEQUAL, + GL_EQUAL, + GL_GEQUAL, + GL_LESS, + GL_NONE, + GL_NONE + }; + + GLEnum depthFunc = s_glDepthFunc[static_cast(state->m_value)]; + this->m_GLSDLDevice.SetDepthTestFunc(depthFunc); + + break; + } + + case GxRs_DepthWrite: { + int32_t depthWrite = static_cast(state->m_value); + bool depthWriteMask = this->MasterEnable(GxMasterEnable_DepthWrite) && depthWrite + ? 1 : 0; + + this->m_GLSDLDevice.SetDepthWriteMask(depthWriteMask); + + break; + } + + case GxRs_Culling: { + static GLEnum cullMode[] = { + GL_ZERO, + GL_CW, + GL_CCW + }; + + // TODO + // - only use state if unk flag 0x20 set, otherwise use 0 + + int32_t mode = static_cast(state->m_value); + + // TODO + // BLIZZARD_ASSERT(mode <= GxCullState_CCW); + + this->m_GLSDLDevice.SetCullMode(cullMode[mode]); + + break; + } + + case GxRs_Texture0: + case GxRs_Texture1: + case GxRs_Texture2: + case GxRs_Texture3: + case GxRs_Texture4: + case GxRs_Texture5: + case GxRs_Texture6: + case GxRs_Texture7: + case GxRs_Texture8: + case GxRs_Texture9: + case GxRs_Texture10: + case GxRs_Texture11: + case GxRs_Texture12: + case GxRs_Texture13: + case GxRs_Texture14: + case GxRs_Texture15: { + int32_t tmu = which - GxRs_Texture0; + + if (tmu <= 15) { + CGxTex* texture = static_cast(static_cast(state->m_value)); + + if (texture) { + this->ITexBind(); + this->ITexMarkAsUpdated(texture); + + GLTexture* glTexture = static_cast(texture->m_apiSpecificData); + this->m_GLSDLDevice.SetTexture(tmu, glTexture); + } else { + this->m_GLSDLDevice.SetTexture(tmu, nullptr); + } + } + + break; + } + + case GxRs_TexGen0: + case GxRs_TexGen1: + case GxRs_TexGen2: + case GxRs_TexGen3: + case GxRs_TexGen4: + case GxRs_TexGen5: + case GxRs_TexGen6: + case GxRs_TexGen7: { + // TODO + + break; + } + + case GxRs_VertexShader: { + auto vs = static_cast(static_cast(state->m_value)); + this->IShaderBindVertex(vs); + + break; + } + + case GxRs_PixelShader: { + auto ps = static_cast(static_cast(state->m_value)); + this->IShaderBindPixel(ps); + + break; + } + + default: { + fprintf(stderr, "Unhandled render state in CGxDeviceGLSDL::IRsSendToHw: %i\n", which); + + break; + } + } +} + +void CGxDeviceGLSDL::ISceneBegin() { + if (this->m_context) { + this->ShaderConstantsClear(); + } + + // TODO GameMovie::ReadFrame(this); +} + +void CGxDeviceGLSDL::ISetCaps(const CGxFormat& format) { + // TODO fill in proper implementation + + this->m_caps.m_pixelCenterOnEdge = 1; + this->m_caps.m_texelCenterOnEdge = 1; + + this->m_caps.m_colorFormat = GxCF_rgba; + + this->m_caps.m_generateMipMaps = 1; + + this->m_caps.int10 = 1; + + this->m_caps.m_texFmt[GxTex_Dxt1] = 1; + this->m_caps.m_texFmt[GxTex_Dxt3] = 1; + this->m_caps.m_texFmt[GxTex_Dxt5] = 1; + + this->m_caps.m_shaderTargets[GxSh_Vertex] = GxShVS_arbvp1; + this->m_caps.m_shaderTargets[GxSh_Pixel] = GxShPS_arbfp1; + + this->m_caps.m_texFilterAnisotropic = 1; + this->m_caps.m_maxTexAnisotropy = 16; + + this->m_caps.m_texTarget[GxTex_2d] = 1; + this->m_caps.m_texTarget[GxTex_CubeMap] = 1; + this->m_caps.m_texTarget[GxTex_Rectangle] = 1; + this->m_caps.m_texTarget[GxTex_NonPow2] = 1; + + this->m_caps.m_texMaxSize[GxTex_2d] = 4096; + this->m_caps.m_texMaxSize[GxTex_CubeMap] = 4096; + this->m_caps.m_texMaxSize[GxTex_Rectangle] = 4096; + this->m_caps.m_texMaxSize[GxTex_NonPow2] = 4096; + + // TODO +} + +void CGxDeviceGLSDL::IShaderBindPixel(CGxShader* sh) { + CGxDevice::IShaderBind(); + + if (!sh) { + this->m_GLSDLDevice.SetShader(GLShader::ePixelShader, nullptr); + return; + } + + if (!sh->loaded) { + this->IShaderCreatePixel(sh); + } + + BLIZZARD_ASSERT(sh->Valid()); + + this->m_GLSDLDevice.SetShader(GLShader::ePixelShader, static_cast(sh->apiSpecific)); +} + +void CGxDeviceGLSDL::IShaderBindVertex(CGxShader* sh) { + CGxDevice::IShaderBind(); + + if (!sh) { + this->m_GLSDLDevice.SetShader(GLShader::eVertexShader, nullptr); + return; + } + + if (!sh->loaded) { + this->IShaderCreateVertex(sh); + } + + BLIZZARD_ASSERT(sh->Valid()); + + this->m_GLSDLDevice.SetShader(GLShader::eVertexShader, static_cast(sh->apiSpecific)); +} + +void CGxDeviceGLSDL::IShaderConstantsFlush() { + // Vertex shader constants + auto vsConst = &CGxDevice::s_shadowConstants[1]; + if (vsConst->unk2 <= vsConst->unk1) { + this->m_GLSDLDevice.SetShaderConstants( + GLShader::eVertexShader, + vsConst->unk2, + reinterpret_cast(&vsConst->constants[vsConst->unk2]), + vsConst->unk1 - vsConst->unk2 + 1 + ); + } + vsConst->unk2 = 255; + vsConst->unk1 = 0; + + // Pixel shader constants + auto psConst = &CGxDevice::s_shadowConstants[0]; + if (psConst->unk2 <= psConst->unk1) { + this->m_GLSDLDevice.SetShaderConstants( + GLShader::ePixelShader, + psConst->unk2, + reinterpret_cast(&psConst->constants[psConst->unk2]), + psConst->unk1 - psConst->unk2 + 1 + ); + } + psConst->unk2 = 255; + psConst->unk1 = 0; +} + +void CGxDeviceGLSDL::IShaderCreate(CGxShader* sh) { + if (sh->target == GxSh_Vertex) { + this->IShaderCreateVertex(sh); + } else if (sh->target == GxSh_Pixel) { + this->IShaderCreatePixel(sh); + } +} + +void CGxDeviceGLSDL::IShaderCreatePixel(CGxShader* ps) { + BLIZZARD_ASSERT(!ps->loaded); + + ps->loaded = 1; + ps->valid = 0; + + unsigned char* codeStr = ps->code.m_data; + uint32_t codeLen = ps->code.Count(); + + if (codeLen) { + this->PatchPixelShader(ps); + + GLShader* glShader = this->m_GLSDLDevice.CreateShader( + GLShader::ShaderType::ePixelShader, + codeStr, + codeLen, + ps->m_key.m_str + ); + + glShader->Compile(nullptr); + + ps->apiSpecific = glShader; + ps->valid = 1; + } +} + +void CGxDeviceGLSDL::IShaderCreateVertex(CGxShader* vs) { + BLIZZARD_ASSERT(!vs->loaded); + + vs->loaded = 1; + vs->valid = 0; + + unsigned char* code = vs->code.m_data; + uint32_t codeLen = vs->code.Count(); + + if (codeLen) { + this->PatchVertexShader(vs); + + GLShader* glShader = this->m_GLSDLDevice.CreateShader( + GLShader::ShaderType::eVertexShader, + code, + codeLen, + vs->m_key.m_str + ); + + glShader->Compile(nullptr); + + vs->apiSpecific = glShader; + vs->valid = 1; + } +} + +void CGxDeviceGLSDL::IStateSetGLSDLDefaults() { + this->IRsForceUpdate(); + this->IRsSync(0); + + // TODO a1->unk8 = -1; + // TODO a1->unk13 = 0; + + this->m_GLSDLDevice.SetIndexBuffer(nullptr); + + GLTexture* a2 = nullptr; + GLTexture* a3 = nullptr; + // TODO this->m_GLSDLDevice.GetBackBuffer(&a2, &a3, nullptr); + // TODO this->gllunk2[12] = a2->GetMipmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + // TODO this->gllunk2[13] = a3->GetMipmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + + this->m_GLSDLDevice.SetFogParam(GL_FOG_DENSITY, 0.0f); + // TODO this->InitLights(); + + // TODO this->gllunk2[10] = 0; + + this->ISceneBegin(); +} + +void CGxDeviceGLSDL::IStateSync() { + this->IShaderConstantsFlush(); + this->IRsSync(0); + this->IStateSyncXforms(); + this->IStateSyncLights(); + this->IStateSyncEnables(); + this->IStateSyncMaterial(); + // TODO clip plane + this->IStateSyncScissorRect(); + this->IStateSyncVertexPtrs(); + this->IStateSyncIndexPtr(); +} + +void CGxDeviceGLSDL::IStateSyncEnables() { + // TODO +} + +void CGxDeviceGLSDL::IStateSyncIndexPtr() { + if (this->m_primIndexDirty) { + this->m_primIndexDirty = 0; + this->m_GLSDLDevice.SetIndexBuffer(static_cast(this->m_primIndexBuf->m_pool->m_apiSpecific)); + } +} + +void CGxDeviceGLSDL::IStateSyncLights() { + // TODO +} + +void CGxDeviceGLSDL::IStateSyncMaterial() { + // TODO +} + +void CGxDeviceGLSDL::IStateSyncScissorRect() { + // TODO +} + +void CGxDeviceGLSDL::IStateSyncVertexPtrs() { + static int32_t gxAttribSlot[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; + + static int32_t gxAttribTypeToGLSDLAttribType[] = { + GLVT_UBYTE4N, + GLVT_UBYTE4, + GLVT_UBYTE4N, + GLVT_FLOAT2, + GLVT_FLOAT3, + GLVT_SHORT2N, + GLVT_FLOAT1 + }; + + if (this->m_primVertexFormat < GxVertexBufferFormats_Last) { + GLVertexFormat* glFormat = &this->m_glFormats[this->m_primVertexFormat]; + + if (glFormat->m_Size) { + this->m_GLSDLDevice.SetVertexFormat(glFormat); + + this->m_GLSDLDevice.SetVertexBuffer( + 0, + static_cast(this->m_primVertexBuf->m_pool->m_apiSpecific), + this->m_primVertexBuf->m_index, + this->m_primVertexSize + ); + + return; + } + } + + GLVertexFormat* glFormat = nullptr; + + if (this->m_primVertexFormat < GxVertexBufferFormats_Last) { + glFormat = &this->m_glFormats[this->m_primVertexFormat]; + } + + for (int32_t i = 0; i < GxVAs_Last; i++) { + if ((1 << i) & this->m_primVertexMask) { + CGxVertexAttrib* attrib = &this->m_primVertexFormatAttrib[i]; + + uint32_t j = glFormat->m_Size; + + glFormat->m_Attribs[j].stream = 0; + glFormat->m_Attribs[j].slot = gxAttribSlot[attrib->attrib]; + glFormat->m_Attribs[j].type = gxAttribTypeToGLSDLAttribType[attrib->type]; + glFormat->m_Attribs[j].offset = attrib->offset; + + glFormat->m_Size++; + } + } + + this->m_GLSDLDevice.SetVertexFormat(glFormat); + + this->m_GLSDLDevice.SetVertexBuffer( + 0, + static_cast(this->m_primVertexBuf->m_pool->m_apiSpecific), + this->m_primVertexBuf->m_index, + this->m_primVertexSize + ); +} + +void CGxDeviceGLSDL::IStateSyncXforms() { + // TODO this->IXformSetWorld(); + // TODO this->IXformSetTex(); + + this->IXformSetViewport(); +} + +void CGxDeviceGLSDL::ITexCreate(CGxTex* gxTex) { + uint32_t width, height, baseMip, mipCount; + this->ITexWHDStartEnd(gxTex, width, height, baseMip, mipCount); + + uint32_t flags = 0; + + flags |= gxTex->m_flags.m_renderTarget ? GLTFLAG_RENDERTARGET : 0x0; + flags |= gxTex->m_format == GxTex_D24X8 ? GLTFLAG_DEPTH : 0x0; + flags |= gxTex->m_flags.m_generateMipMaps ? GLTFLAG_AUTOGEN_MIPMAP : 0x0; + + // TODO + // 2 bits, appearing at m_maxAnisotropy + 1 bit + // flags |= gxTex->m_flags & 0xC000 : GLTFLAG_READ_ACCESS : 0x0; + + if (gxTex->m_target == 1) { + gxTex->m_apiSpecificData = this->m_GLSDLDevice.CreateTextureCubeMap( + width, + mipCount - baseMip, + CGxDeviceGLSDL::s_gxTexFmtToGLSDLFmt[gxTex->m_format], + flags + ); + } else { + gxTex->m_apiSpecificData = this->m_GLSDLDevice.CreateTexture2D( + width, + height, + mipCount - baseMip, + CGxDeviceGLSDL::s_gxTexFmtToGLSDLFmt[gxTex->m_format], + flags + ); + + if (gxTex->m_flags.m_renderTarget) { + gxTex->m_apiSpecificData2 = this->m_GLSDLDevice.CreateTexture2D( + width, + height, + mipCount - baseMip, + GLTF_D24, + flags | GLTFLAG_DEPTH + ); + } + } + + this->ITexSetFlags(gxTex); + + // TODO + // CGxDevice::IOnAcquireCPUTex(); + + gxTex->m_needsCreation = 0; +} + +void CGxDeviceGLSDL::ITexMarkAsUpdated(CGxTex* texId) { + if (texId->m_needsFlagUpdate && !texId->m_needsCreation && (texId->m_apiSpecificData || texId->m_apiSpecificData2)) { + this->ITexSetFlags(texId); + } + + if (texId->m_needsUpdate/* TODO && (this + 4000) */) { + if (texId->m_needsCreation || (!texId->m_apiSpecificData && !texId->m_apiSpecificData2)) { + this->ITexCreate(texId); + } + + if (!texId->m_needsCreation && (texId->m_apiSpecificData || texId->m_apiSpecificData2)) { + if (texId->m_userFunc) { + this->ITexUpload(texId); + } + + CGxDevice::ITexMarkAsUpdated(texId); + } + } +} + +void CGxDeviceGLSDL::ITexSetFlags(CGxTex* texId) { + GLTexture* v2 = static_cast(texId->m_apiSpecificData); + GLSDLTexSetFlags(texId, v2); + texId->m_needsFlagUpdate = 0; +} + +void CGxDeviceGLSDL::ITexUpload(CGxTex* texId) { + uint32_t texelStrideInBytes; + const void* texels = nullptr; + + texId->m_userFunc( + GxTex_Lock, + texId->m_width, + texId->m_height, + 0, + 0, + texId->m_userArg, + texelStrideInBytes, + texels + ); + + uint32_t width, height, baseMip, mipCount; + this->ITexWHDStartEnd(texId, width, height, baseMip, mipCount); + + int32_t numFace = texId->m_target == GxTex_CubeMap ? 6 : 1; + + for (int32_t face = 0; face < numFace; face++) { + for (int32_t mipLevel = baseMip; mipLevel < mipCount; mipLevel++) { + texels = nullptr; + + texId->m_userFunc( + GxTex_Latch, + texId->m_width >> mipLevel, + texId->m_height >> mipLevel, + face, + mipLevel, + texId->m_userArg, + texelStrideInBytes, + texels + ); + + BLIZZARD_ASSERT(texels != nullptr || texId->m_flags.m_renderTarget); + + if (!texId->m_flags.m_renderTarget) { + GLMipmap* mipmap = static_cast(texId->m_apiSpecificData)->GetMipmap(mipLevel - baseMip, CGxDeviceGLSDL::s_glCubeMapFaces[face]); + + CiRect lockRect = { + texId->m_updateRect.minY >> mipLevel, + texId->m_updateRect.minX >> mipLevel, + (texId->m_updateRect.maxY >> mipLevel) + 1, + (texId->m_updateRect.maxX >> mipLevel) + 1 + }; + + lockRect.maxY = std::min(lockRect.maxY, static_cast(height)); + lockRect.maxX = std::min(lockRect.maxX, static_cast(width)); + + BLIZZARD_ASSERT(lockRect.minX >= 0 && lockRect.maxX <= static_cast(width)); + BLIZZARD_ASSERT(lockRect.minY >= 0 && lockRect.maxY <= static_cast(height)); + + GLRect rect = { + lockRect.minX, + lockRect.minY, + lockRect.maxX - lockRect.minX, + lockRect.maxY - lockRect.minY + }; + + if (mipmap->GetFormatInfo().m_IsCompressed) { + int32_t v17 = rect.width + (rect.left & 3); + rect.left &= 0xFFFFFFFC; + rect.width = (v17 + 3) & 0xFFFFFFFC; + rect.width = std::max(rect.width, 4); + + int32_t v20 = rect.height + (rect.top & 3); + rect.top &= 0xFFFFFFFC; + rect.height = (v20 + 3) & 0xFFFFFFFC; + rect.height = std::max(rect.height, 4); + } + + const void* src = texels; + + if (texId->m_flags.m_bit15) { + src = texels; + } else if (texId->m_dataFormat == GxTex_Dxt1 || texId->m_dataFormat == GxTex_Dxt3 || texId->m_dataFormat == GxTex_Dxt5) { + uint32_t bytesPerBlock = CGxDevice::s_texFormatBytesPerBlock[texId->m_dataFormat]; + uint32_t offset = (bytesPerBlock * (rect.left >> 2)) + (texelStrideInBytes * (rect.top >> 2)); + + src = static_cast(texels) + offset; + } else { + uint32_t bitDepth = CGxDevice::s_texFormatBitDepth[texId->m_dataFormat]; + uint32_t offset = ((bitDepth * rect.left) >> 3) + (texelStrideInBytes * rect.top); + + src = static_cast(texels) + offset; + } + + uint32_t dstStride = mipmap->GetPitch(); + + BlitFormat dstFmt = GxGetBlitFormat(texId->m_format); + + void* dst = mipmap->Map(GL_WRITE_ONLY, &rect); + + BlitFormat srcFmt = GxGetBlitFormat(texId->m_dataFormat); + + C2iVector size = { rect.width, rect.height }; + + Blit(size, BlitAlpha_0, src, texelStrideInBytes, srcFmt, dst, dstStride, dstFmt); + + mipmap->Unmap(); + } + + width = std::max(width >> 1, 1u); + height = std::max(height >> 1, 1u); + } + } + + texId->m_userFunc( + GxTex_Unlock, + texId->m_width, + texId->m_height, + 0, + 0, + texId->m_userArg, + texelStrideInBytes, + texels + ); +} + +void CGxDeviceGLSDL::IXformSetProjection(const C44Matrix& matrix) { + C44Matrix glSDLMat = matrix; + + if (!this->MasterEnable(GxMasterEnable_NormalProjection) && matrix.d3 != 1.0f) { + C44Matrix shrink = { + 0.2f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.2f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.2f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + glSDLMat = glSDLMat * shrink; + } + + this->m_GLSDLDevice.SetTransform(GL_PROJECTION, reinterpret_cast(&glSDLMat)); + + this->m_projNative = glSDLMat; + + this->m_projNative.a1 *= -1.0f; + this->m_projNative.b1 *= -1.0f; + this->m_projNative.c1 *= -1.0f; + this->m_projNative.d1 *= -1.0f; +} + +void CGxDeviceGLSDL::IXformSetView(const C44Matrix& matrix) { + this->m_GLSDLDevice.SetTransform('VIEW', reinterpret_cast(&matrix)); +} + +void CGxDeviceGLSDL::IXformSetViewport() { + auto window = this->DeviceCurWindow(); + + GLRect viewport = { + static_cast((this->m_viewport.x.l * window.maxX) + 0.5f), + static_cast(((1.0f - this->m_viewport.y.h) * window.maxY) + 0.5f), + static_cast(((this->m_viewport.x.h - this->m_viewport.x.l) * window.maxX) + 0.5f), + static_cast(((this->m_viewport.y.h - this->m_viewport.y.l) * window.maxY) + 0.5f), + }; + + this->m_GLSDLDevice.SetViewport(viewport, this->m_viewport.z.l, this->m_viewport.z.h); + + // TODO (this + 4020) = 0; +} + +void CGxDeviceGLSDL::PatchPixelShader(CGxShader* ps) { + // TODO +} + +void CGxDeviceGLSDL::PatchVertexShader(CGxShader* vs) { + if (vs->patched) { + return; + } + + vs->patched = 1; + + // TODO properly calculate v2 + int32_t v2 = 192; + + char buf[64]; + + for (int32_t i = 0; i < vs->code.Count(); i++) { + char* str = reinterpret_cast(&vs->code[i]); + + if (!SStrCmp(str, ".local", 6)) { + memcpy(str, ".env ", 6); + + int v16, v12, n; + if (str[6] == '[' && sscanf(&str[4], " [%d..%d]%n", &v16, &v12, &n) == 2 && v12 == 93) { + SStrPrintf(buf, sizeof(buf), "[%d..%d] ", v16, v2 - 1); + memcpy(&str[4], buf, n); + } + } + + int32_t v13, n; + if (sscanf(str, "PARAM c[%d] = { %n", &v13, &n) == 1 && v13 == 94) { + SStrPrintf(buf, sizeof(buf), "PARAM c[%d]={ ", v2); + memcpy(str, buf, n); + } + } +} + +void CGxDeviceGLSDL::PoolSizeSet(CGxPool* pool, uint32_t size) { + if (pool->m_usage == GxPoolUsage_Stream) { + pool->unk1C = 0; + } + + GLBuffer* buffer = reinterpret_cast(pool->m_apiSpecific); + + if (buffer) { + delete buffer; + } + + pool->m_size = size; + + pool->m_apiSpecific = this->m_GLSDLDevice.CreateBuffer( + CGxDeviceGLSDL::s_poolTarget2BufferType[pool->m_target], + size, + nullptr, + CGxDeviceGLSDL::s_poolUsage2BufferUsage[pool->m_usage], + CGxDeviceGLSDL::s_poolTarget2BufferFormat[pool->m_target] + ); +} + +void CGxDeviceGLSDL::Resize(uint32_t width, uint32_t height) { + this->m_GLSDLDevice.Resize(width, height); + + CRect rect = { 0.0f, 0.0f, static_cast(height), static_cast(width) }; + this->DeviceSetDefWindow(rect); +} + +void CGxDeviceGLSDL::SceneClear(uint32_t mask, CImVector color) { + CGxDevice::SceneClear(mask, color); + + if (!this->m_context) { + return; + } + + uint32_t glMask = 0x0; + + if (mask & 0x1) { + glMask |= GL_COLOR_BUFFER_BIT; + } + + if (mask & 0x2) { + glMask |= GL_DEPTH_BUFFER_BIT; + } + + this->IXformSetViewport(); + this->IStateSyncScissorRect(); + + GLColor4f glColor = { + color.r / 255.0f, + color.g / 255.0f, + color.b / 255.0f, + color.a / 255.0f + }; + + this->m_GLSDLDevice.Clear(glMask, glColor, 1.0, 0); +} + +void CGxDeviceGLSDL::ScenePresent() { + if (this->m_context) { + // TODO + + CGxDevice::ScenePresent(); + + // TODO + + this->m_GLSDLDevice.Swap(); + + // TODO + + CGxDevice::ShaderConstantsClear(); + } + + // TODO +} + +void CGxDeviceGLSDL::ShaderCreate(CGxShader* shaders[], EGxShTarget target, const char* a4, const char* a5, int32_t permutations) { + CGxDevice::ShaderCreate(shaders, target, a4, a5, permutations); + + if (permutations == 1 && !shaders[0]->loaded) { + if (target == GxSh_Vertex) { + this->IShaderCreateVertex(shaders[0]); + } else if (target == GxSh_Pixel) { + this->IShaderCreatePixel(shaders[0]); + } + } +} + +int32_t CGxDeviceGLSDL::StereoEnabled() { + return 0; +} + +void CGxDeviceGLSDL::TexDestroy(CGxTex* texId) { + BLIZZARD_ASSERT(texId); + + auto texture = static_cast(texId->m_apiSpecificData); + + if (texture) { + texture->Release(); + texId->m_apiSpecificData = nullptr; + } + + // TODO CGxDevice::IOnReleaseCPUTex(texId); + CGxDevice::TexDestroy(texId); +} + +void CGxDeviceGLSDL::XformSetProjection(const C44Matrix& matrix) { + CGxDevice::XformSetProjection(matrix); + this->IXformSetProjection(matrix); +} + +void CGxDeviceGLSDL::XformSetView(const C44Matrix& matrix) { + CGxDevice::XformSetView(matrix); + this->IXformSetView(matrix); +} diff --git a/src/gx/glsdl/CGxDeviceGLSDL.hpp b/src/gx/glsdl/CGxDeviceGLSDL.hpp new file mode 100644 index 0000000..316317c --- /dev/null +++ b/src/gx/glsdl/CGxDeviceGLSDL.hpp @@ -0,0 +1,82 @@ +#ifndef GX_GL_SDL_C_GX_DEVICE_GL_SDL_HPP +#define GX_GL_SDL_C_GX_DEVICE_GL_SDL_HPP + +#include "gx/CGxDevice.hpp" +#include "gx/glsdl/GLSDLDevice.hpp" +#include "gx/glsdl/GLSDLWindow.hpp" + +class CGxBatch; +class CGxShader; + +class CGxDeviceGLSDL : public CGxDevice { + public: + // Static variables + static GLEnum s_glCubeMapFaces[]; + static GLEnum s_glDstBlend[]; + static GLEnum s_glSrcBlend[]; + static GLTextureFormat s_gxTexFmtToGLSDLFmt[]; + static GLEnum s_poolTarget2BufferFormat[]; + static GLEnum s_poolTarget2BufferType[]; + static GLEnum s_poolUsage2BufferUsage[]; + static GLEnum s_primitiveConversion[]; + + // Member variables + GLSDLDevice m_GLSDLDevice; + GLSDLWindow m_GLSDLWindow; + GLVertexFormat m_glFormats[GxVertexBufferFormats_Last] = {}; + + // Virtual member functions + virtual void ITexMarkAsUpdated(CGxTex*); + virtual void IRsSendToHw(EGxRenderState); + virtual int32_t DeviceCreate(int32_t (*windowProc)(void* window, uint32_t message, uintptr_t wparam, intptr_t lparam), const CGxFormat&); + virtual int32_t DeviceSetFormat(const CGxFormat&); + virtual void* DeviceWindow(); + virtual void DeviceWM(EGxWM wm, uintptr_t param1, uintptr_t param2) {}; + virtual void CapsWindowSize(CRect&); + virtual void CapsWindowSizeInScreenCoords(CRect& dst); + virtual void ScenePresent(); + virtual void SceneClear(uint32_t, CImVector); + virtual void XformSetProjection(const C44Matrix&); + virtual void XformSetView(const C44Matrix&); + virtual void Draw(CGxBatch* batch, int32_t indexed); + virtual void PoolSizeSet(CGxPool*, uint32_t); + virtual char* BufLock(CGxBuf*); + virtual int32_t BufUnlock(CGxBuf*, uint32_t); + virtual void BufData(CGxBuf* buf, const void* data, size_t size, uintptr_t offset); + virtual void TexDestroy(CGxTex* texId); + virtual void IShaderCreate(CGxShader*); + virtual void ShaderCreate(CGxShader*[], EGxShTarget, const char*, const char*, int32_t); + virtual int32_t StereoEnabled(); + + // Member functions + CGxDeviceGLSDL(); + char* IBufLock(CGxBuf*); + int32_t IBufUnlock(CGxBuf*); + void ISceneBegin(); + void ISetCaps(const CGxFormat& format); + void IShaderBindPixel(CGxShader*); + void IShaderBindVertex(CGxShader*); + void IShaderConstantsFlush(); + void IShaderCreatePixel(CGxShader*); + void IShaderCreateVertex(CGxShader*); + void IStateSetGLSDLDefaults(); + void IStateSync(); + void IStateSyncEnables(); + void IStateSyncIndexPtr(); + void IStateSyncLights(); + void IStateSyncMaterial(); + void IStateSyncScissorRect(); + void IStateSyncVertexPtrs(); + void IStateSyncXforms(); + void ITexCreate(CGxTex*); + void ITexSetFlags(CGxTex*); + void ITexUpload(CGxTex*); + void IXformSetProjection(const C44Matrix&); + void IXformSetView(const C44Matrix&); + void IXformSetViewport(); + void PatchPixelShader(CGxShader*); + void PatchVertexShader(CGxShader*); + void Resize(uint32_t width, uint32_t height); +}; + +#endif diff --git a/src/gx/glsdl/GL.cpp b/src/gx/glsdl/GL.cpp new file mode 100644 index 0000000..e5c5e8e --- /dev/null +++ b/src/gx/glsdl/GL.cpp @@ -0,0 +1,50 @@ +#include "gx/glsdl/GL.hpp" + +TextureFormatInfo k_TextureFormatInfo[GLTF_NUM_TEXTURE_FORMATS] = { + { 0, 0, 0, 0, 0, "GLTF INVALID!!" }, + { GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, 4, "ARGB8888" }, + { GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, 4, "XRGB8888" }, + { GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 0, 4, "RGBA8888" }, + { GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, 4, "ABGR8888" }, + { GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0, 4, "ARGB0888" }, + { GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, 0, 3, "RGB888" }, + { GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE, 0, 3, "BGR888" }, + { GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT, 0, 16, "RGBA32F" }, + { GL_RGBA16F_ARB, GL_RGBA, GL_HALF_FLOAT, 0, 8, "RGBA16F" }, + { GL_RGB16F_ARB, GL_RGB, GL_HALF_FLOAT, 0, 6, "RG16F" }, + { GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0, 4, "D32" }, + { GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0, 4, "D24" }, + { GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0, 2, "D16" }, + { GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, GL_HALF_FLOAT, 0, 4, "DF" }, + { GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0, 4, "D24S8" }, + { GL_ALPHA8, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, 0, 1, "S8" }, + { GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV, 0, 2, "ARGB4444" }, + { GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0, 2, "ARGB1555" }, + { GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0, 2, "ARGB0555" }, + { GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0, 2, "RGB565" }, + { GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV, 0, 4, "A2RGB10" }, + { GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT, 0, 6, "RGB16" }, + { GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0, 1, "L8" }, + { GL_ALPHA8, GL_ALPHA, GL_UNSIGNED_BYTE, 0, 1, "A8" }, + { GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0, 2, "A8L8" }, + { GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_BYTE, 1, 8, "DXT1" }, + { GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_BYTE, 1, 16, "DXT3" }, + { GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_BYTE, 1, 16, "DXT5" } +}; + +VertexTypeInfo k_VertexTypeInfo[GLVT_NUM_VERTEX_TYPES] = { + { 0, 0, 0, 0, "INVALID" }, + { GL_FLOAT, 1, 0, 4, "FLOAT1" }, + { GL_FLOAT, 2, 0, 8, "FLOAT2" }, + { GL_FLOAT, 3, 0, 12, "FLOAT3" }, + { GL_FLOAT, 4, 0, 16, "FLOAT4" }, + { GL_UNSIGNED_BYTE, 4, 0, 4, "UBYTE4" }, + { GL_UNSIGNED_BYTE, 4, 1, 4, "UBYTE4N" }, + { GL_SHORT, 1, 0, 2, "SHORT" }, + { GL_SHORT, 2, 0, 4, "SHORT2" }, + { GL_SHORT, 4, 0, 8, "SHORT4" }, + { GL_SHORT, 2, 1, 4, "SHORT2N" }, + { GL_SHORT, 4, 1, 8, "SHORT4N" }, + { GL_UNSIGNED_SHORT, 2, 1, 4, "USHORT2N" }, + { GL_UNSIGNED_SHORT, 4, 1, 8, "USHORT4N" } +}; diff --git a/src/gx/glsdl/GL.hpp b/src/gx/glsdl/GL.hpp new file mode 100644 index 0000000..80ab4bd --- /dev/null +++ b/src/gx/glsdl/GL.hpp @@ -0,0 +1,33 @@ +#ifndef GX_GL_SDL_GL_HPP +#define GX_GL_SDL_GL_HPP + +#include +#include + +#include "gx/glsdl/GLTypes.hpp" + +typedef GLenum GLEnum; + +#define kMAX_VERTEX_ATTRIBS 16 + +struct TextureFormatInfo { + GLenum m_InternalFormat; + GLenum m_DataFormat; + GLenum m_DataType; + int32_t m_IsCompressed; + int32_t m_BytePerPixel; + char m_Name[16]; +}; + +struct VertexTypeInfo { + GLenum m_Type; + GLint m_Size; + GLboolean m_Normalized; + GLint m_ByteSize; + const char* m_Name; +}; + +extern TextureFormatInfo k_TextureFormatInfo[GLTF_NUM_TEXTURE_FORMATS]; +extern VertexTypeInfo k_VertexTypeInfo[GLVT_NUM_VERTEX_TYPES]; + +#endif diff --git a/src/gx/glsdl/GLBatch.hpp b/src/gx/glsdl/GLBatch.hpp new file mode 100644 index 0000000..83632d1 --- /dev/null +++ b/src/gx/glsdl/GLBatch.hpp @@ -0,0 +1,45 @@ +#ifndef GX_GL_SDL_GL_BATCH_HPP +#define GX_GL_SDL_GL_BATCH_HPP + +#include "gx/glsdl/GLBuffer.hpp" +#include "gx/glsdl/GLShader.hpp" +#include "gx/glsdl/GLTexture.hpp" +#include "gx/glsdl/GLTypes.hpp" +#include "gx/glsdl/GLVertexFormat.hpp" +#include + +class GLBatch { + public: + GLShader* var0; + GLShader* var1; + GLTexture* textures[16]; + GLBuffer* var3; + GLBuffer* var4[4]; + uint32_t var5[4]; + uint32_t var6[4]; + GLVertexFormat* var7; + uint32_t var8; + uint32_t var9; + uint32_t var10; + uint32_t var11; + uint32_t var12; + uint32_t var13; + uint32_t var14; + int32_t var15; + bool var16; + bool var17; + GLStates var18; + GLTexture2D* colorBuffer[4]; + GLTexture2D* var20; + uint32_t var21[128]; + char var22[64]; + char var23[1024]; + uint32_t var24; + bool var25; + int64_t var26; + int64_t var27; + float var28[4096]; + float var29[1024]; +}; + +#endif diff --git a/src/gx/glsdl/GLBuffer.cpp b/src/gx/glsdl/GLBuffer.cpp new file mode 100644 index 0000000..e0f22e2 --- /dev/null +++ b/src/gx/glsdl/GLBuffer.cpp @@ -0,0 +1,130 @@ +#include "gx/glsdl/GLBuffer.hpp" +#include "gx/glsdl/GLSDLDevice.hpp" +#include "gx/glsdl/GLPool.hpp" +#include "gx/glsdl/GL.hpp" +#include +#include + +bool GLBuffer::m_UsingVBO = 1; + +GLEnum GLBuffer::s_FlagToAccess[] = { + GL_READ_WRITE, // GLMap_None + GL_WRITE_ONLY, // GLMap_Unk1 + GL_WRITE_ONLY, // GLMap_Unk2 + GL_READ_ONLY // GLMap_Unk3 +}; + +GLBuffer* GLBuffer::Create(GLEnum type, uint32_t size, const void* a3, GLEnum usage, GLEnum format) { + GLBuffer* buffer = GLPool::Get()->GetNextObject(); + + buffer->m_Type = type; + buffer->m_Size = size; + buffer->m_Usage = usage; + buffer->m_IndexFormat = format; + + GLSDLDevice* device = GLSDLDevice::Get(); + device->BindBuffer(buffer, GL_ZERO); + + if (GLBuffer::m_UsingVBO) { + glBufferData(buffer->m_Type, buffer->m_Size, a3, buffer->m_Usage); + // glBufferParameteriAPPLE(buffer->m_Type, GL_BUFFER_SERIALIZED_MODIFY_APPLE, buffer->m_Usage - GL_DYNAMIC_DRAW > 1); + // glBufferParameteriAPPLE(buffer->m_Type, GL_BUFFER_FLUSHING_UNMAP_APPLE, 0); + } else { + Blizzard::Memory::Free(buffer->m_Data); + + void* data = Blizzard::Memory::Allocate(size); + if (a3) { + memcpy(data, a3, size); + } + + buffer->m_Data = reinterpret_cast(data); + } + + // TODO + // buffer->m_TimeStamp = Blizzard::Time::GetTimestamp(); + + return buffer; +} + +GLBuffer::GLBuffer() : GLObject() { + if (GLBuffer::m_UsingVBO) { + this->m_BufferID = GLPool::Get()->GetNextName(); + } +} + +char* GLBuffer::Map(uint32_t offset, uint32_t size, eMapFlag flag) { + BLIZZARD_ASSERT((offset + size) <= this->m_Size); + BLIZZARD_ASSERT(this->m_Usage == GL_STATIC_DRAW || flag != GLMap_None); + BLIZZARD_ASSERT(this->m_MapFlag == GLMap_NotMapped); + BLIZZARD_ASSERT(flag >= GLMap_None && flag < GLMap_Count); + + this->m_MapOffset = offset; + this->m_MapSize = offset + size == 0 ? this->m_Size : size; + this->m_MapFlag = flag; + + if (GLBuffer::m_UsingVBO) { + GLSDLDevice* device = GLSDLDevice::Get(); + device->BindBuffer(this, GL_ZERO); + + if (flag == GLMap_Unk2) { + if (this->m_Usage - GL_DYNAMIC_DRAW <= 1) { + BLIZZARD_ASSERT(offset == 0); + } + + glBufferData(this->m_Type, this->m_Size, nullptr, this->m_Usage); + } + + void* data = glMapBuffer(this->m_Type, GLBuffer::s_FlagToAccess[flag]); + this->m_Data = reinterpret_cast(data); + + BLIZZARD_ASSERT(this->m_Data != nullptr); + } + + return this->m_Data + offset; +} + +void GLBuffer::ReleaseObject() { + if (GLBuffer::m_UsingVBO) { + if (this->m_Type) { + GLSDLDevice* device = GLSDLDevice::Get(); + device->BindBuffer(this, GL_ZERO); + + glBufferData(this->m_Type, 1, nullptr, this->m_Usage); + + // TODO GLPool::GLObjectPool::Push((GLPool::m_pool + 32776), this); + } else { + // TODO GLPool::GLObjectPool::Push((GLPool::m_pool + 32776), this); + } + } else { + Blizzard::Memory::Free(this->m_Data); + this->m_Data = nullptr; + + // TODO GLPool::GLObjectPool::Push((GLPool::m_pool + 32776), this); + } +} + +void GLBuffer::Unmap(uint32_t size) { + BLIZZARD_ASSERT((this->m_MapOffset + size) <= m_Size); + + GLSDLDevice* device = GLSDLDevice::Get(); + device->BindBuffer(this, GL_ZERO); + + if (this->m_MapFlag != 3) { + if (GLBuffer::m_UsingVBO) { + glFlushMappedBufferRange(this->m_Type, this->m_MapOffset, size ? size : this->m_MapSize); + } + + // TODO + // this->m_TimeStamp = Blizzard::Time::GetTimestamp(); + } + + if (!GLBuffer::m_UsingVBO) { + this->m_MapFlag = GLMap_NotMapped; + return; + } + + GLboolean result = glUnmapBuffer(this->m_Type); + BLIZZARD_ASSERT(result); + + this->m_MapFlag = GLMap_NotMapped; +} diff --git a/src/gx/glsdl/GLBuffer.hpp b/src/gx/glsdl/GLBuffer.hpp new file mode 100644 index 0000000..93ed6d5 --- /dev/null +++ b/src/gx/glsdl/GLBuffer.hpp @@ -0,0 +1,47 @@ +#ifndef GX_GL_SDL_GL_BUFFER_HPP +#define GX_GL_SDL_GL_BUFFER_HPP + +#include "gx/glsdl/GL.hpp" +#include "gx/glsdl/GLObject.hpp" +#include "gx/glsdl/GLTypes.hpp" + +class GLBuffer : public GLObject { + public: + // Types + enum eMapFlag { + GLMap_NotMapped = -1, + GLMap_None = 0, + GLMap_Unk1 = 1, + GLMap_Unk2 = 2, + GLMap_Unk3 = 3, + GLMap_Count = 4 + }; + + // Static variables + static bool m_UsingVBO; + static GLEnum s_FlagToAccess[]; + + // Static functions + static GLBuffer* Create(GLEnum, uint32_t, const void*, GLEnum, GLEnum); + + // Member variables + uint32_t m_Size = 0; + GLEnum m_Type = 0; + GLEnum m_Usage = 0; + uint32_t m_BufferID = 0; + GLEnum m_IndexFormat = 0; + char* m_Data = nullptr; + uint32_t m_MapOffset = 0; + uint32_t m_MapSize = 0; + uint32_t m_MapFlag = GLMap_NotMapped; + + // Virtual member functions + virtual void ReleaseObject(); + + // Member functions + GLBuffer(); + char* Map(uint32_t, uint32_t, eMapFlag); + void Unmap(uint32_t); +}; + +#endif diff --git a/src/gx/glsdl/GLBufferPool.hpp b/src/gx/glsdl/GLBufferPool.hpp new file mode 100644 index 0000000..b00d198 --- /dev/null +++ b/src/gx/glsdl/GLBufferPool.hpp @@ -0,0 +1,8 @@ +#ifndef GX_GL_SDL_GL_BUFFER_POOL_HPP +#define GX_GL_SDL_GL_BUFFER_POOL_HPP + +class GLBufferPool { + public: +}; + +#endif diff --git a/src/gx/glsdl/GLDebugMipmap2D.hpp b/src/gx/glsdl/GLDebugMipmap2D.hpp new file mode 100644 index 0000000..7ad41aa --- /dev/null +++ b/src/gx/glsdl/GLDebugMipmap2D.hpp @@ -0,0 +1,8 @@ +#ifndef GX_GL_SDL_GL_DEBUG_MIPMAP_2D_HPP +#define GX_GL_SDL_GL_DEBUG_MIPMAP_2D_HPP + +class GLDebugMipmap2D { + public: +}; + +#endif diff --git a/src/gx/glsdl/GLFramebuffer.cpp b/src/gx/glsdl/GLFramebuffer.cpp new file mode 100644 index 0000000..ade6c35 --- /dev/null +++ b/src/gx/glsdl/GLFramebuffer.cpp @@ -0,0 +1,150 @@ +#include "gx/glsdl/GLFramebuffer.hpp" +#include "gx/glsdl/GLSDLDevice.hpp" +#include "gx/glsdl/GLMipmap.hpp" +#include "gx/glsdl/GLPool.hpp" +#include "gx/glsdl/GLTypes.hpp" +#include + +GLFramebuffer* GLFramebuffer::Create(bool a1) { + GLFramebuffer* framebuffer = new GLFramebuffer(a1); + + if (!a1) { + // TODO + // BLIZZARD_ASSERT(framebuffer->m_FramebufferID >= PoolStats::NAME_POOL_FIRST_NAME); + } + + BLIZZARD_ASSERT(framebuffer->m_NumAttach == 0); + + framebuffer->m_Width = 0; + framebuffer->m_Height = 0; + framebuffer->m_Device = GLSDLDevice::Get(); + + return framebuffer; +} + +GLFramebuffer::GLFramebuffer(bool a1) : GLObject() { + if (!a1) { + this->m_FramebufferID = GLPool::Get()->GetNextName(); + } +} + +void GLFramebuffer::Attach(GLMipmap* image, GLenum a3, int32_t a4) { + BLIZZARD_ASSERT(this->m_Device == GLSDLDevice::Get()); + + if (!image) { + this->Detach(a3); + return; + } + + if (a3 == GL_DEPTH_STENCIL) { + BLIZZARD_ASSERT(image->GetFormat() == GLTF_D24S8); + + this->Attach(image, GL_DEPTH_ATTACHMENT, 0); + this->Attach(image, GL_STENCIL_ATTACHMENT, 0); + + (*image->m_AttachPoints)[this->m_FramebufferID].point = GL_DEPTH_STENCIL; + + return; + } + + int32_t index; + + if (a3 == GL_DEPTH_ATTACHMENT) { + index = 4; + } else if (a3 == GL_STENCIL_ATTACHMENT) { + index = 5; + } else { + index = a3 - GL_COLOR_ATTACHMENT0; + } + + BLIZZARD_ASSERT(index < MAX_ATTACHMENT); + + GLMipmap* oldImage = this->m_Attachments[index]; + + if (image != oldImage) { + if (oldImage) { + oldImage->Detach(this, a3, true); + } else { + ++this->m_NumAttach; + } + + this->m_Attachments[index] = image; + + this->m_Width = image->m_Width; + this->m_Height = image->m_Height; + + image->Attach(this, a3, a4); + this->m_Device->Sub38460(0); + } + + BLIZZARD_ASSERT((*image->m_AttachPoints)[m_FramebufferID].framebuffer == this); +} + +void GLFramebuffer::Detach(GLenum a2) { + int32_t v2 = a2; + int32_t index; + + if (a2 == GL_DEPTH_STENCIL) { + index = 5; + v2 = GL_STENCIL_ATTACHMENT; + this->Detach(GL_DEPTH_ATTACHMENT); + } else if (a2 == GL_DEPTH_ATTACHMENT) { + index = 4; + } else if (a2 == GL_STENCIL_ATTACHMENT) { + index = 5; + } else { + index = a2 - GL_COLOR_ATTACHMENT0; + } + + BLIZZARD_ASSERT(index < MAX_ATTACHMENT); + + GLMipmap* oldImage = this->m_Attachments[index]; + + if (oldImage) { + oldImage->Detach(this, v2, 0); + + --this->m_NumAttach; + + this->m_Attachments[index] = 0; + + if (this->m_Device == GLSDLDevice::Get()) { + this->m_Device->Sub38460(0); + } + + if (this->m_NumAttach == 0) { + this->m_Width = 0; + this->m_Height = 0; + } + } +} + +GLMipmap* GLFramebuffer::GetAttachment(GLEnum a2) { + int32_t index; + + if (a2 == GL_DEPTH_ATTACHMENT) { + index = 4; + } else if (a2 == GL_STENCIL_ATTACHMENT) { + index = 5; + } else { + index = a2 - GL_COLOR_ATTACHMENT0; + } + + BLIZZARD_ASSERT(index < MAX_ATTACHMENT); + + return this->m_Attachments[index]; +} + +int32_t GLFramebuffer::GetSampleCount() { + return this->m_FramebufferID + ? 1 + : this->m_Device->m_Context.GetSampleCount(); +} + +bool GLFramebuffer::IsValid() { + auto status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); + return status == GL_FRAMEBUFFER_COMPLETE; +} + +void GLFramebuffer::ReleaseObject() { + // TODO +} diff --git a/src/gx/glsdl/GLFramebuffer.hpp b/src/gx/glsdl/GLFramebuffer.hpp new file mode 100644 index 0000000..1fee673 --- /dev/null +++ b/src/gx/glsdl/GLFramebuffer.hpp @@ -0,0 +1,38 @@ +#ifndef GX_GL_SDL_GL_FRAMEBUFFER_HPP +#define GX_GL_SDL_GL_FRAMEBUFFER_HPP + +#include "gx/glsdl/GL.hpp" +#include "gx/glsdl/GLObject.hpp" +#include + +#define MAX_ATTACHMENT 6 + +class GLSDLDevice; +class GLMipmap; + +class GLFramebuffer : public GLObject { + public: + // Static functions + static GLFramebuffer* Create(bool); + + // Member variables + int32_t m_Width = 0; + int32_t m_Height = 0; + uint32_t m_FramebufferID = 0; + GLSDLDevice* m_Device; + GLMipmap* m_Attachments[6] = {}; + uint32_t m_NumAttach = 0; + + // Virtual member functions + virtual void ReleaseObject(); + + // Member functions + GLFramebuffer(bool); + void Attach(GLMipmap*, GLenum, int32_t); + void Detach(GLenum); + GLMipmap* GetAttachment(GLEnum); + int32_t GetSampleCount(void); + bool IsValid(); +}; + +#endif diff --git a/src/gx/glsdl/GLGLSLProgram.cpp b/src/gx/glsdl/GLGLSLProgram.cpp new file mode 100644 index 0000000..55e59c0 --- /dev/null +++ b/src/gx/glsdl/GLGLSLProgram.cpp @@ -0,0 +1,7 @@ +#include "gx/glsdl/GLGLSLProgram.hpp" +#include "gx/glsdl/GLShader.hpp" + +GLGLSLProgram* GLGLSLProgram::Find(GLShader* a1, GLShader* a2) { + // TODO + return nullptr; +} diff --git a/src/gx/glsdl/GLGLSLProgram.hpp b/src/gx/glsdl/GLGLSLProgram.hpp new file mode 100644 index 0000000..45f2390 --- /dev/null +++ b/src/gx/glsdl/GLGLSLProgram.hpp @@ -0,0 +1,14 @@ +#ifndef GX_GL_SDL_GL_GLSL_PROGRAM_HPP +#define GX_GL_SDL_GL_GLSL_PROGRAM_HPP + +#include "gx/glsdl/GLObject.hpp" + +class GLShader; + +class GLGLSLProgram : public GLObject { + public: + // Static functions + static GLGLSLProgram* Find(GLShader*, GLShader*); +}; + +#endif diff --git a/src/gx/glsdl/GLMipmap.cpp b/src/gx/glsdl/GLMipmap.cpp new file mode 100644 index 0000000..e913ed8 --- /dev/null +++ b/src/gx/glsdl/GLMipmap.cpp @@ -0,0 +1,480 @@ +#include "gx/glsdl/GLMipmap.hpp" +#include "gx/glsdl/GLSDLDevice.hpp" +#include "gx/glsdl/GLFramebuffer.hpp" +#include + +int32_t GLMipmap::GetDepthBits() { + return this->m_DepthBits; +} + +void GLMipmap::Attach(GLFramebuffer* framebuffer, GLenum attachPoint, int32_t a4) { + if (!this->m_AttachPoints) { + this->m_AttachPoints = new std::vector(); + } + + auto& attachPoints = *this->m_AttachPoints; + auto framebufferID = framebuffer->m_FramebufferID; + + if (framebufferID >= attachPoints.size()) { + attachPoints.resize(framebufferID + 1); + } else { + BLIZZARD_ASSERT(attachPoints[framebufferID].framebuffer != framebuffer || attachPoints[framebufferID].point != attachPoint); + + auto& attach = attachPoints[framebufferID]; + + if ( + attach.point + && (attach.point != GL_DEPTH_ATTACHMENT || attachPoint != GL_STENCIL_ATTACHMENT) + && (attach.point != GL_STENCIL_ATTACHMENT || attachPoint != GL_DEPTH_ATTACHMENT) + ) { + framebuffer->Detach(attach.point); + } + } + + GLSDLDevice* device = GLSDLDevice::Get(); + + auto currentTarget = device->GetCurrentTarget(); + device->BindFramebuffer(framebuffer); + + if (framebufferID) { + if (this->m_Target == GL_TEXTURE_3D) { + glFramebufferTexture3DEXT( + GL_FRAMEBUFFER, + attachPoint, + GL_TEXTURE_3D, + this->m_Texture->m_TextureID, + this->m_Level, + a4 + ); + } else { + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER, + attachPoint, + this->m_Target, + this->m_Texture->m_TextureID, + this->m_Level + ); + } + } + + if (attachPoint == GL_DEPTH_ATTACHMENT && !this->m_DepthBits) { + GLint depthBits = 0; + glGetIntegerv(GL_DEPTH_BITS, &depthBits); + this->m_DepthBits = depthBits; + } + + device->BindFramebuffer(currentTarget); + + auto& attach = attachPoints[framebufferID]; + attach.framebuffer = framebuffer; + attach.zOffset = a4; + + if ( + (attach.point != GL_DEPTH_ATTACHMENT || attachPoint != GL_STENCIL_ATTACHMENT) + && (attach.point != GL_STENCIL_ATTACHMENT || attachPoint != GL_DEPTH_ATTACHMENT) + ) { + attach.point = attachPoint; + } else { + attach.point = GL_DEPTH_STENCIL; + } +} + +void GLMipmap::Detach(GLFramebuffer* framebuffer, GLenum attachPoint, bool a4) { + GLuint framebufferID = framebuffer->m_FramebufferID; + + auto& attachPoints = *this->m_AttachPoints; + + BLIZZARD_ASSERT(attachPoints.size() >= framebufferID); + BLIZZARD_ASSERT(attachPoints[framebufferID].framebuffer == framebuffer); + + if (!a4 && framebufferID) { + GLSDLDevice* v12 = GLSDLDevice::Get(); + GLFramebuffer* v14 = v12->GetCurrentTarget(); + v12->BindFramebuffer(framebuffer); + + if (this->m_Target == GL_TEXTURE_3D) { + glFramebufferTexture3DEXT(GL_FRAMEBUFFER, attachPoint, GL_TEXTURE_3D, 0, 0, 0); + } else { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, attachPoint, this->m_Target, 0, 0); + } + + v12->BindFramebuffer(v14); + } + + GLAttachPoint* v9 = &attachPoints[framebufferID]; + + if (v9->point == GL_DEPTH_STENCIL) { + BLIZZARD_ASSERT(this->GetFormat() == GLTF_D24S8); + + if (attachPoint == GL_DEPTH_ATTACHMENT) { + v9->point = GL_STENCIL_ATTACHMENT; + } else if (attachPoint == GL_STENCIL_ATTACHMENT) { + v9->point = GL_DEPTH_ATTACHMENT; + } else { + BLIZZARD_ASSERT(false); + } + } else { + BLIZZARD_ASSERT(attachPoints[framebufferID].point == attachPoint); + + v9->framebuffer = 0; + v9->point = 0; + v9->zOffset = 0; + + // TODO + // this->m_Texture->m_TimeStamp = Blizzard::Time::GetTimestamp(); + } +} + +void GLMipmap::DetachAll() { + if (!this->m_AttachPoints) { + return; + } + + auto& attachPoints = *this->m_AttachPoints; + for (int32_t i = 0; i < attachPoints.size(); i++) { + BLIZZARD_ASSERT(attachPoints[i].point != GL_ZERO); + BLIZZARD_ASSERT(attachPoints[i].framebuffer->m_FramebufferID == i); + + attachPoints[i].framebuffer->Detach(attachPoints[i].point); + } +} + +GLTextureFormat GLMipmap::GetFormat() { + return this->m_Texture->GetFormat(); +} + +TextureFormatInfo& GLMipmap::GetFormatInfo() { + return this->m_Texture->GetFormatInfo(); +}; + +uint16_t GLMipmap::GetHeight() { + return this->m_Height; +} + +int32_t GLMipmap::GetPitch() { + int32_t bpp = this->GetFormatInfo().m_BytePerPixel; + int32_t v4 = this->m_Texture->var12 >> this->m_Level; + return v4 >= bpp ? v4 : bpp; +} + +GLTexture* GLMipmap::GetTexture() { + return this->m_Texture; +} + +uint32_t GLMipmap::GetTextureID() { + return this->m_Texture->m_TextureID; +} + +uint16_t GLMipmap::GetWidth() { + return this->m_Width; +} + +void* GLMipmap::Map(GLEnum mode, const GLBox* area) { + BLIZZARD_ASSERT(!this->m_Texture->IsSystemBuffer()); + BLIZZARD_ASSERT(this->m_Data != nullptr); + BLIZZARD_ASSERT(!this->m_Texture->IsRenderTarget()); + BLIZZARD_ASSERT(mode != GL_ZERO); + BLIZZARD_ASSERT(this->m_MapParams == nullptr); + + if (mode != GL_READ_ONLY) { + this->m_Texture->m_MappedMipmaps++; + } + + MapParams* mapParams = new MapParams(); + this->m_MapParams = mapParams; + + if (area) { + BLIZZARD_ASSERT(area->width > 0); + BLIZZARD_ASSERT(area->height > 0); + BLIZZARD_ASSERT(area->depth > 0); + BLIZZARD_ASSERT(!this->GetFormatInfo().m_IsCompressed || ((area->top & 0x3) == 0 && (area->left & 0x3) == 0 && (area->width & 0x3) == 0 && (area->height & 0x3) == 0)); + BLIZZARD_ASSERT((area->height + area->top) <= this->m_Height); + BLIZZARD_ASSERT((area->depth + area->front) <= this->m_Depth); + BLIZZARD_ASSERT((area->width + area->left) <= this->m_Width); + + mapParams->m_MapArea = { + area->left, + area->top, + area->front, + area->width, + area->height, + area->depth + }; + + int32_t size = this->GetFormatInfo().m_BytePerPixel + * this->m_Width + * mapParams->m_MapArea.depth + * mapParams->m_MapArea.height; + + mapParams->m_Size = this->GetFormatInfo().m_IsCompressed + ? size >> 4 + : size; + + mapParams->m_Unk7 = (this->GetFormatInfo().m_BytePerPixel * mapParams->m_MapArea.left) + >> this->GetFormatInfo().m_IsCompressed ? 2 : 0; + } else { + mapParams->m_MapArea = { + 0, + 0, + 0, + this->m_Width, + this->m_Height, + this->m_Depth + }; + + mapParams->m_Size = this->m_Size; + + mapParams->m_Unk7 = 0; + } + + mapParams->m_MapMode = mode; + + int32_t rowPitch = this->GetPitch(); + + BLIZZARD_ASSERT(((mapParams->m_MapArea.top * rowPitch) + mapParams->m_MapArea.left * this->GetFormatInfo().m_BytePerPixel) < (this->GetFormatInfo().m_IsCompressed ? this->m_Size << 4 : this->m_Size)); + + int32_t v22 = rowPitch * this->m_Height; + if (this->GetFormatInfo().m_IsCompressed) { + v22 >>= 2; + } + + unsigned char* v24 = this->m_Data + + ((mapParams->m_MapArea.top * rowPitch) >> (this->GetFormatInfo().m_IsCompressed ? 2 : 0)) + + (mapParams->m_MapArea.front * v22); + + mapParams->m_Unk8 = v24; + + return v24 + mapParams->m_Unk7; +} + +void* GLMipmap::Map(GLEnum mode, const GLRect* rect) { + if (rect) { + GLBox area = { + rect->left, + rect->top, + 0, + rect->width, + rect->height, + 1 + }; + + return this->Map(mode, &area); + } else { + return this->Map(mode, static_cast(nullptr)); + } +} + +void GLMipmap::ReleaseObject() { + BLIZZARD_ASSERT(this->m_MapParams == nullptr); + + this->RemoveDebugMipmap(); + this->DetachAll(); + + if (this->m_AttachPoints) { + delete this->m_AttachPoints; + } + + this->m_AttachPoints = nullptr; + this->m_Unk24 = 0; +} + +void GLMipmap::RemoveDebugMipmap() { + // TODO +} + +void GLMipmap::ResetData(GLEnum target, int32_t level, unsigned char* data) { + BLIZZARD_ASSERT(this->m_Target != GL_TEXTURE_3D || !this->GetFormatInfo().m_IsCompressed); + + this->m_Target = target; + this->m_Level = level; + this->m_Data = data; + + BLIZZARD_ASSERT(this->GetFormat() != GLTF_INVALID); + BLIZZARD_ASSERT(this->GetFormat() < GLTF_NUM_TEXTURE_FORMATS); + + if (!this->m_Texture->IsSystemBuffer() && this->m_Texture->IsRenderTarget()) { + BLIZZARD_ASSERT(!this->GetFormatInfo().m_IsCompressed); + + this->TexImage(nullptr); + this->m_Unk24 = 1; + } +} + +void GLMipmap::ResetSize(uint32_t width, uint32_t height, uint32_t depth) { + this->m_Width = width ? width : 1; + this->m_Height = height ? height : 1; + this->m_Depth = depth ? depth : 1; + + if (this->GetFormatInfo().m_IsCompressed) { + BLIZZARD_ASSERT(this->m_Depth == 1); + + this->m_Width = (this->m_Width + 3) & 0xFFFC; + this->m_Height = (this->m_Height + 3) & 0xFFFC; + } + + uint32_t v11 = this->GetFormatInfo().m_BytePerPixel; + uint32_t v20 = this->m_Texture->var12 >> this->m_Level; + + if (v20 >= v11) { + v11 = v20; + } + + uint32_t v15 = v11 * this->m_Height; + uint32_t v12; + + if (this->GetFormatInfo().m_IsCompressed) { + v12 = v15 >> 2; + } else { + v12 = v15; + } + + this->m_Size = this->m_Depth * v12; +} + +void GLMipmap::TexImage(const void* pixels) { + BLIZZARD_ASSERT((this->m_Texture->IsRenderTarget() || pixels != nullptr) && GLSDLDevice::Get()->GetVertexArrayStates().buffers[eGLBT_PIXEL_UNPACK] == 0); + + if (this->m_Target == GL_TEXTURE_3D) { + glTexImage3D( + GL_TEXTURE_3D, + this->m_Level, + this->GetFormatInfo().m_InternalFormat, + this->m_Width, + this->m_Height, + this->m_Depth, + 0, + this->GetFormatInfo().m_DataFormat, + this->GetFormatInfo().m_DataType, + pixels + ); + } else if (this->GetFormatInfo().m_IsCompressed) { + glCompressedTexImage2D( + this->m_Target, + this->m_Level, + this->GetFormatInfo().m_InternalFormat, + this->m_Width, + this->m_Height, + 0, + this->m_Size, + pixels + ); + } else { + glTexImage2D( + this->m_Target, + this->m_Level, + this->GetFormatInfo().m_InternalFormat, + this->m_Width, + this->m_Height, + 0, + this->GetFormatInfo().m_DataFormat, + this->GetFormatInfo().m_DataType, + pixels + ); + } +} + +void GLMipmap::TexSubImage(const GLBox& a2, int32_t size, const void* pixels) { + BLIZZARD_ASSERT(!this->m_Texture->IsRenderTarget() && pixels != nullptr && GLSDLDevice::Get()->GetVertexArrayStates().buffers[eGLBT_PIXEL_UNPACK] == 0); + + if (this->m_Target == GL_TEXTURE_3D) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, this->m_Width); + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, this->m_Height); + + glTexSubImage3D( + this->m_Target, + this->m_Level, + a2.left, + a2.top, + a2.front, + a2.width, + a2.height, + a2.depth, + this->GetFormatInfo().m_DataFormat, + this->GetFormatInfo().m_DataType, + pixels + ); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); + } else if (this->GetFormatInfo().m_IsCompressed) { + glCompressedTexSubImage2D( + this->m_Target, + this->m_Level, + 0, + a2.top, + this->m_Width, + a2.height, + this->GetFormatInfo().m_InternalFormat, + size, + pixels + ); + } else { + glPixelStorei(GL_UNPACK_ROW_LENGTH, this->m_Width); + + glTexSubImage2D( + this->m_Target, + this->m_Level, + a2.left, + a2.top, + a2.width, + a2.height, + this->GetFormatInfo().m_DataFormat, + this->GetFormatInfo().m_DataType, + pixels + ); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } +} + +void GLMipmap::Unmap() { + BLIZZARD_ASSERT(!this->m_Texture->IsRenderTarget()); + BLIZZARD_ASSERT(!this->m_Texture->IsSystemBuffer()); + BLIZZARD_ASSERT(this->m_MapParams != nullptr); + + if (this->m_MapParams->m_MapMode == GL_READ_ONLY) { + delete this->m_MapParams; + this->m_MapParams = nullptr; + return; + } + + GLSDLDevice* device = GLSDLDevice::Get(); + + BLIZZARD_ASSERT(this->m_Texture->m_MappedMipmaps > 0); + + this->Unmap(this->m_MapParams); + + this->m_MapParams = nullptr; +} + +void GLMipmap::Unmap(MapParams* mapParams) { + BLIZZARD_ASSERT(mapParams != nullptr); + + this->m_Texture->Bind(nullptr, 0); + + if (this->m_Unk24) { + if (this->GetFormatInfo().m_IsCompressed) { + GLBox area = { + 0, + mapParams->m_MapArea.top, + mapParams->m_MapArea.front, + this->m_Width, + mapParams->m_MapArea.height, + mapParams->m_MapArea.depth + }; + + this->TexSubImage(area, mapParams->m_Size, mapParams->m_Unk8); + } else { + this->TexSubImage(mapParams->m_MapArea, mapParams->m_Size, mapParams->m_Unk8 + mapParams->m_Unk7); + } + } else { + this->TexImage(this->m_Data); + this->m_Unk24 = 1; + } + + delete mapParams; + + // TODO + + this->m_Texture->m_MappedMipmaps--; +} diff --git a/src/gx/glsdl/GLMipmap.hpp b/src/gx/glsdl/GLMipmap.hpp new file mode 100644 index 0000000..33e7b8c --- /dev/null +++ b/src/gx/glsdl/GLMipmap.hpp @@ -0,0 +1,62 @@ +#ifndef GX_GL_SDL_GL_MIPMAP_HPP +#define GX_GL_SDL_GL_MIPMAP_HPP + +#include "gx/glsdl/GL.hpp" +#include "gx/glsdl/GLTypes.hpp" +#include +#include + +class GLFramebuffer; +class GLTexture; + +class GLMipmap { + public: + // Types + struct MapParams { + GLBox m_MapArea; + int32_t m_MapMode = 0; + int32_t m_Unk7 = 0; + unsigned char* m_Unk8 = nullptr; + int32_t m_Size = 0; + }; + + // Member variables + GLTexture* m_Texture = nullptr; + uint16_t m_Width = 0; + uint16_t m_Height = 0; + uint16_t m_Depth = 0; + uint8_t m_Level = 0; + uint8_t m_DepthBits = 0; + uint32_t m_Size = 0; + int32_t m_Target = 0; + unsigned char* m_Data = nullptr; // TODO proper type + MapParams* m_MapParams = nullptr; + std::vector* m_AttachPoints = nullptr; + uint32_t m_Unk20 = 0; + uint32_t m_Unk24 = 0; + + // Member functions + void Attach(GLFramebuffer*, GLenum, int32_t); + void Detach(GLFramebuffer*, GLenum, bool); + void DetachAll(); + int32_t GetDepthBits(void); + GLTextureFormat GetFormat(void); + TextureFormatInfo& GetFormatInfo(void); + uint16_t GetHeight(void); + int32_t GetPitch(void); + GLTexture* GetTexture(); + uint32_t GetTextureID(void); + uint16_t GetWidth(void); + void* Map(GLEnum, const GLBox*); + void* Map(GLEnum, const GLRect*); + void ReleaseObject(); + void RemoveDebugMipmap(); + void ResetData(GLEnum, int32_t, unsigned char*); + void ResetSize(uint32_t, uint32_t, uint32_t); + void TexImage(const void*); + void TexSubImage(const GLBox&, int32_t, const void*); + void Unmap(void); + void Unmap(MapParams*); +}; + +#endif diff --git a/src/gx/glsdl/GLObject.cpp b/src/gx/glsdl/GLObject.cpp new file mode 100644 index 0000000..205acca --- /dev/null +++ b/src/gx/glsdl/GLObject.cpp @@ -0,0 +1,22 @@ +#include "gx/glsdl/GLObject.hpp" +#include + +void GLObject::AddRefTwin() { +} + +uint32_t GLObject::Release() { + BLIZZARD_ASSERT(this->m_RefCount > 0); + + this->m_RefCount--; + + if (this->m_RefCount == 0) { + this->ReleaseObject(); + } + + this->ReleaseTwin(); + + return this->m_RefCount; +} + +void GLObject::ReleaseTwin() { +} diff --git a/src/gx/glsdl/GLObject.hpp b/src/gx/glsdl/GLObject.hpp new file mode 100644 index 0000000..32e0eba --- /dev/null +++ b/src/gx/glsdl/GLObject.hpp @@ -0,0 +1,22 @@ +#ifndef GX_GL_SDL_GL_OBJECT_HPP +#define GX_GL_SDL_GL_OBJECT_HPP + +#include + +class GLObject { + public: + // Member variables + GLObject* m_Next; + uint32_t m_RefCount; + int64_t m_TimeStamp; + + // Virtual member functions + virtual void ReleaseObject() = 0; + virtual void AddRefTwin(); + virtual void ReleaseTwin(); + + // Member functions + uint32_t Release(); +}; + +#endif diff --git a/src/gx/glsdl/GLPixelShader.cpp b/src/gx/glsdl/GLPixelShader.cpp new file mode 100644 index 0000000..dfe4f50 --- /dev/null +++ b/src/gx/glsdl/GLPixelShader.cpp @@ -0,0 +1,17 @@ +#include "gx/glsdl/GLPixelShader.hpp" +#include "gx/glsdl/GL.hpp" + +GLPixelShader* GLPixelShader::Create() { + // TODO + // GLPool stuff + + GLPixelShader* shader = new GLPixelShader(); + + shader->m_ShaderID = 0; + shader->m_RefCount = 1; + shader->m_ShaderType = ePixelShader; + shader->m_UsingGLSL = 0; + shader->var5 = GL_FRAGMENT_PROGRAM_ARB; + + return shader; +} diff --git a/src/gx/glsdl/GLPixelShader.hpp b/src/gx/glsdl/GLPixelShader.hpp new file mode 100644 index 0000000..eec6ded --- /dev/null +++ b/src/gx/glsdl/GLPixelShader.hpp @@ -0,0 +1,12 @@ +#ifndef GX_SDL_GL_PIXEL_SHADER_HPP +#define GX_SDL_GL_PIXEL_SHADER_HPP + +#include "gx/glsdl/GLShader.hpp" + +class GLPixelShader : public GLShader { + public: + // Static functions + static GLPixelShader* Create(void); +}; + +#endif diff --git a/src/gx/glsdl/GLPool.hpp b/src/gx/glsdl/GLPool.hpp new file mode 100644 index 0000000..18a829b --- /dev/null +++ b/src/gx/glsdl/GLPool.hpp @@ -0,0 +1,57 @@ +#ifndef GX_GL_SDL_GL_POOL_HPP +#define GX_GL_SDL_GL_POOL_HPP + +#include +#include + +template +class GLPool { + public: + // Static variables + static GLPool* m_pool; + + // Static functions + static GLPool* Get(void); + static void Init(void); + + // Member variables + std::atomic m_NextName; + + // Member functions + uint32_t GetNextName(void); + T* GetNextObject(void); + void SetNextName(uint32_t); +}; + +template +GLPool* GLPool::m_pool; + +template +GLPool* GLPool::Get() { + return GLPool::m_pool; +} + +template +void GLPool::Init() { + GLPool::m_pool = new GLPool(); +} + +template +uint32_t GLPool::GetNextName() { + return this->m_NextName++; +} + +template +T* GLPool::GetNextObject() { + // TODO + // - pop off of GLObjectPool + + return new T(); +} + +template +void GLPool::SetNextName(uint32_t name) { + this->m_NextName = name; +} + +#endif diff --git a/src/gx/glsdl/GLSDLContext.cpp b/src/gx/glsdl/GLSDLContext.cpp new file mode 100644 index 0000000..23a94fc --- /dev/null +++ b/src/gx/glsdl/GLSDLContext.cpp @@ -0,0 +1,48 @@ +#include "gx/glsdl/GLSDLContext.hpp" +#include +#include + +static bool s_GLEW_Initialized = false; + +void GLSDLContext::Create(GLSDLWindow* window) { + BLIZZARD_ASSERT(this->m_sdlGLContext == nullptr); + + this->m_sdlGLContext = SDL_GL_CreateContext(window->m_sdlWindow); + + BLIZZARD_ASSERT(this->m_sdlGLContext != nullptr); + + if (s_GLEW_Initialized == false) { + glewExperimental = true; + + auto glewError = glewInit(); + + if (glewError != GLEW_OK) { + SErrDisplayAppFatal("Error initializing GLEW: %s\n", glewGetErrorString(glewError)); + } + + s_GLEW_Initialized = true; + } +} + +void GLSDLContext::Destroy() { + BLIZZARD_ASSERT(this->m_sdlGLContext != nullptr); + + SDL_GL_DeleteContext(this->m_sdlGLContext); + this->m_sdlGLContext = nullptr; +} + +bool GLSDLContext::IsCurrentContext() { + return this->m_sdlGLContext == SDL_GL_GetCurrentContext(); +} + +void GLSDLContext::MakeCurrent(GLSDLWindow* window) { + auto status = SDL_GL_MakeCurrent(window->m_sdlWindow, this->m_sdlGLContext); + BLIZZARD_ASSERT(status == 0); +} + +int32_t GLSDLContext::GetSampleCount() { + int samples; + auto status = SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples); + BLIZZARD_ASSERT(status == 0); + return static_cast(samples); +} diff --git a/src/gx/glsdl/GLSDLContext.hpp b/src/gx/glsdl/GLSDLContext.hpp new file mode 100644 index 0000000..8fd074a --- /dev/null +++ b/src/gx/glsdl/GLSDLContext.hpp @@ -0,0 +1,20 @@ +#ifndef GX_GL_SDL_GL_SDL_CONTEXT_HPP + +#include + +#include "gx/glsdl/GLSDLWindow.hpp" +#include "gx/glsdl/GLTypes.hpp" + +class GLSDLContext { + public: + SDL_GLContext m_sdlGLContext = nullptr; + + GLSDLContext() = default; + void Create(GLSDLWindow* window); + void Destroy(); + bool IsCurrentContext(); + void MakeCurrent(GLSDLWindow* window); + int32_t GetSampleCount(); +}; + +#endif diff --git a/src/gx/glsdl/GLSDLDevice.cpp b/src/gx/glsdl/GLSDLDevice.cpp new file mode 100644 index 0000000..dd49d79 --- /dev/null +++ b/src/gx/glsdl/GLSDLDevice.cpp @@ -0,0 +1,2769 @@ +#include "gx/glsdl/GLSDLDevice.hpp" +#include "gx/glsdl/GLPool.hpp" +#include "gx/glsdl/GLUtil.hpp" +#include "gx/glsdl/GL.hpp" +#include +#include +#include +#include +#include + +#define GL_MAX_STREAM 4 + +class GLPixelShader; +class GLVertexShader; + +Blizzard::Thread::TLSSlot GLSDLDevice::m_CurrentDevice; +std::vector> GLSDLDevice::m_Devices; +bool GLSDLDevice::m_ExtColorMaskIndexed = false; +int32_t GLSDLDevice::m_StaticResourcesRefCount = 0; +GLSDLDevice::RendererInfo GLSDLDevice::m_RendererInfo; +bool GLSDLDevice::m_UseHybridShader = 0; +bool GLSDLDevice::m_ExtARBShadow = 0; +bool GLSDLDevice::m_ShaderConstantBindings = 1; +GLBuffer* GLSDLDevice::m_BlitQuadVBO = nullptr; +GLShader* GLSDLDevice::m_DeviceShaders[11] = {}; +GLTexture* GLSDLDevice::m_DeviceTextures[4] = {}; +GLVertexFormat GLSDLDevice::m_NormalBlitVF; +GLVertexFormat GLSDLDevice::m_InvertedBlitVF; +GLFramebuffer* GLSDLDevice::m_F8330C = nullptr; + +const char* GLSDLBlitVsCode = R"(!!ARBvp1.0 +PARAM c0 = { 1.0 }; +MOV result.position.xyz, vertex.attrib[0]; +MOV result.position.w, c0.x; +MOV result.texcoord[0].xyz, vertex.attrib[1]; +END)"; + +const char* GLSDLDummyVsCode = R"(!!ARBvp1.0 +PARAM c = {0.0, 0.0, 0.0, 1.0}; +MOV result.position, c; +END)"; + +const char* GLSDLBlitPsCode = R"(!!ARBfp1.0 +PARAM c0 = { 1.0 }; +TEX result.color.rgba, fragment.texcoord[0], texture[0], 2D; +END)"; + +inline void COPY_TRANSFORM(GLTransform& dst, const GLTransform& src) { + if (src.isIdentity) { + dst.isIdentity = true; + dst.isDirty = true; + } else { + dst.isIdentity = false; + dst.isDirty = true; + memcpy(dst.m, src.m, sizeof(float) * 16); + } +} + +void* Sub1D210(void* ptr) { + GLSDLDevice** ptrptr = new GLSDLDevice*; + *ptrptr = nullptr; + return ptrptr; +} + +void Sub1D230(void* ptr) { + delete static_cast(ptr); +} + +GLSDLDevice* GLSDLDevice::Get() { + return *static_cast( + Blizzard::Thread::RegisterLocalStorage(&GLSDLDevice::m_CurrentDevice, Sub1D210, 0, Sub1D230) + ); +} + +void GLSDLDevice::Set(GLSDLDevice* device) { + *static_cast( + Blizzard::Thread::RegisterLocalStorage(&GLSDLDevice::m_CurrentDevice, Sub1D210, 0, Sub1D230) + ) = device; +} + +GLSDLDevice::RendererInfo GLSDLDevice::GetRendererInfo() { + if (!GLSDLDevice::m_RendererInfo.init) { + GLSDLDevice::InitRendererInfo(); + } + + return GLSDLDevice::m_RendererInfo; +} + +void GLSDLDevice::InitRendererInfo() { + // TODO + + GLSDLDevice::m_RendererInfo.init = 1; +} + +void GLSDLDevice::InitPools() { + // TODO + // - init all pools + + GLPool::Init(); + GLPool::Get()->SetNextName(1); + + GLPool::Init(); + GLPool::Get()->SetNextName(0x21); + + GLPool::Init(); + GLPool::Get()->SetNextName(0x1); + + GLPool::Init(); + GLPool::Get()->SetNextName(0x2001); + + GLPool::Init(); + GLPool::Get()->SetNextName(0x1); +} + +void GLSDLDevice::SetOption(GLSDLDeviceOption option, bool enable) { + switch (option) { + case eUseMTGL: { + break; + } + + case eUseVertexArray: { + break; + } + + case eUseGLSL: { + break; + } + + case eCheckGLStates: { + break; + } + + case eFlushBeforeDraw: { + break; + } + + case eDeviceOption5: { + break; + } + + case eUseHybridShader: { + break; + } + + case eDeviceOption7: { + break; + } + + case eDeviceOption8: { + break; + } + + case eShaderConstantBindings: { + GLSDLDevice::m_ShaderConstantBindings = enable; + break; + } + + default: { + BLIZZARD_ASSERT(false); + } + } +} + +void GLSDLDevice::StaticInit() { + static float blitQuad[] = { + -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f + }; + + GLSDLDevice::m_BlitQuadVBO = GLBuffer::Create( + GL_ARRAY_BUFFER, + sizeof(blitQuad), + blitQuad, + GL_STATIC_DRAW, + 0 + ); + + GLSDLDevice::m_InvertedBlitVF.m_Attribs[0] = { 0, 0, GLVT_FLOAT3, 0 }; + GLSDLDevice::m_InvertedBlitVF.m_Attribs[1] = { 0, 1, GLVT_FLOAT2, 12 }; + GLSDLDevice::m_InvertedBlitVF.m_Size = 2; + + GLSDLDevice::m_NormalBlitVF.m_Attribs[0] = { 0, 0, GLVT_FLOAT3, 0 }; + GLSDLDevice::m_NormalBlitVF.m_Attribs[1] = { 0, 1, GLVT_FLOAT2, 20 }; + GLSDLDevice::m_NormalBlitVF.m_Size = 2; + + GLSDLDevice::m_F8330C = GLFramebuffer::Create(0); + + GLSDLDevice::m_DeviceShaders[0] = GLShader::Create( + GLShader::eVertexShader, + GLSDLDevice::m_UseHybridShader, + false, + "", + GLSDLBlitVsCode, + strlen(GLSDLBlitVsCode), + "", + "GL SDL Blit VS", + nullptr + ); + + GLSDLDevice::m_DeviceShaders[0]->Compile(nullptr); + + GLSDLDevice::m_DeviceShaders[1] = GLShader::Create( + GLShader::eVertexShader, + GLSDLDevice::m_UseHybridShader, + false, + "", + GLSDLDummyVsCode, + strlen(GLSDLDummyVsCode), + "", + "GL SDL Dummy VS", + nullptr + ); + + GLSDLDevice::m_DeviceShaders[1]->Compile(nullptr); + + GLSDLDevice::m_DeviceShaders[2] = GLShader::Create( + GLShader::ePixelShader, + GLSDLDevice::m_UseHybridShader, + false, + "", + GLSDLBlitPsCode, + strlen(GLSDLBlitPsCode), + "", + "GL SDL Blit PS", + nullptr + ); + + GLSDLDevice::m_DeviceShaders[2]->Compile(nullptr); + + // TODO + // - remaining device shaders + + // Device textures + + uint32_t v30 = 0; + uint32_t v31 = 0; + + auto texture0 = GLTexture2D::Create(1, 1, 1, GLTF_RGBA8888, 0x0); + *static_cast(texture0->Map(0, nullptr, v30, GL_WRITE_ONLY)) = 0xFF; + texture0->Unmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + GLSDLDevice::m_DeviceTextures[0] = texture0; + + // TODO + // auto texture1 = GLTexture3D::Create(1, 1, 1, 1, GLTF_RGBA8888, 0x0); + // *texture1->Map(0, nullptr, v30, v31, GL_WRITE_ONLY) = 0xFF; + // texture1->Unmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + // GLSDLDevice::m_DeviceTextures[1] = texture1; + + // TODO texture2 + + auto texture3 = GLTexture2D::Create(1, 1, 1, GLTF_D32, 0x0); + *static_cast(texture3->Map(0, nullptr, v30, GL_WRITE_ONLY)) = 0xFF; + texture3->Unmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + GLSDLDevice::m_DeviceTextures[3] = texture3; +} + +GLSDLDevice::GLSDLDevice() : m_Context(), m_DefaultVertexArrayObject(true) { + // TODO + // - fill in remaining initializers +} + +void GLSDLDevice::ApplyGLBindings(const GLStates& states, bool a3) { + static GLenum texTarget[4] = { + GL_TEXTURE_2D, + GL_TEXTURE_3D, + GL_TEXTURE_CUBE_MAP, + GL_TEXTURE_RECTANGLE + }; + + for (int32_t i = 0; i < 16; ++i) { + for (int32_t t = 0; t < 4; ++t) { + if (this->m_States.binding.texture[t][i] != states.binding.texture[t][i] || a3) { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(texTarget[t], states.binding.texture[t][i]); + } + } + } + + glActiveTexture(GL_TEXTURE0 + states.binding.currentActiveTexture); + + if (this->m_States.binding.framebuffer != states.binding.framebuffer || a3) { + glBindFramebufferEXT(GL_FRAMEBUFFER, states.binding.framebuffer); + } + + if (this->m_States.binding.renderbuffer != states.binding.renderbuffer || a3) { + glBindRenderbufferEXT(GL_RENDERBUFFER, states.binding.renderbuffer); + } + + if (this->m_States.binding.vertexArrayObject != states.binding.vertexArrayObject || a3) { + glBindVertexArray(states.binding.vertexArrayObject); + } + + if (this->m_States.binding.vertexProgram != states.binding.vertexProgram || a3) { + glBindProgramARB(GL_VERTEX_PROGRAM_ARB, states.binding.vertexProgram); + } + + if (this->m_States.binding.pixelProgram != states.binding.pixelProgram || a3) { + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, states.binding.pixelProgram); + } + + if (this->m_States.binding.glslProgram != states.binding.glslProgram || a3) { + glUseProgram(states.binding.glslProgram); + } + + memcpy(&this->m_States.binding, &states.binding, sizeof(this->m_States.binding)); +} + +void GLSDLDevice::ApplyGLStates(const GLStates& states, bool force) { + if (force) { + for (int32_t i = 0; i < 8; ++i) { + glActiveTexture(GL_TEXTURE0 + i); + + float sPlane[] = { 1.0, 0.0, 0.0, 0.0 }; + float tPlane[] = { 0.0, 1.0, 0.0, 0.0 }; + float rPlane[] = { 0.0, 0.0, 1.0, 0.0 }; + float qPlane[] = { 0.0, 0.0, 0.0, 1.0 }; + + glTexGenfv(GL_S, GL_EYE_PLANE, sPlane); + glTexGenfv(GL_T, GL_EYE_PLANE, tPlane); + glTexGenfv(GL_R, GL_EYE_PLANE, rPlane); + glTexGenfv(GL_Q, GL_EYE_PLANE, qPlane); + glTexGenfv(GL_S, GL_OBJECT_PLANE, sPlane); + glTexGenfv(GL_T, GL_OBJECT_PLANE, tPlane); + glTexGenfv(GL_R, GL_OBJECT_PLANE, rPlane); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, qPlane); + + glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, 1); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&GLColor4f::WHITE); + } + + glActiveTexture(GL_TEXTURE0 + states.binding.currentActiveTexture); + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR_EXT); + glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + } + + if (this->m_States.depth.testEnable != states.depth.testEnable || force) { + if (states.depth.testEnable) { + glEnable(GL_DEPTH_TEST); + } else { + glDisable(GL_DEPTH_TEST); + } + } + + if (this->m_States.depth.compareFunc != states.depth.compareFunc || force) { + glDepthFunc(states.depth.compareFunc); + } + + if (this->m_States.depth.writeMask != states.depth.writeMask || force) { + glDepthMask(states.depth.writeMask); + } + + if (this->m_States.stencil.testEnable != states.stencil.testEnable || force) { + if (states.stencil.testEnable) { + glEnable(GL_STENCIL_TEST); + } else { + glDisable(GL_STENCIL_TEST); + } + } + + if (this->m_States.stencil.front.compareFunc != states.stencil.front.compareFunc + || this->m_States.stencil.back.compareFunc != states.stencil.back.compareFunc + || this->m_States.stencil.ref != states.stencil.ref + || this->m_States.stencil.mask != states.stencil.mask + || force) + { + glStencilFuncSeparate( + states.stencil.front.compareFunc, + states.stencil.back.compareFunc, + states.stencil.ref, + states.stencil.mask + ); + } + + if (this->m_States.stencil.front.opFail != states.stencil.front.opFail + || this->m_States.stencil.front.opZFail != states.stencil.front.opZFail + || this->m_States.stencil.front.opZPass != states.stencil.front.opZPass + || force) + { + glStencilOpSeparate( + states.stencil.useTwoSidedStencil ? GL_FRONT : GL_FRONT_AND_BACK, + states.stencil.front.opFail, + states.stencil.front.opZFail, + states.stencil.front.opZPass + ); + } + + if (states.stencil.useTwoSidedStencil) { + if (this->m_States.stencil.back.opFail != states.stencil.back.opFail + || this->m_States.stencil.back.opZFail != states.stencil.back.opZFail + || this->m_States.stencil.back.opZPass != states.stencil.back.opZPass + || force) + { + glStencilOpSeparate( + GL_BACK, + states.stencil.back.opFail, + states.stencil.back.opZFail, + states.stencil.back.opZPass + ); + } + } + + if (this->m_States.stencil.writeMask != states.stencil.writeMask || force) { + glStencilMask(states.stencil.writeMask); + } + + if (this->m_States.rasterizer.cullFaceMode != states.rasterizer.cullFaceMode || force) { + glCullFace(states.rasterizer.cullFaceMode); + } + + if (this->m_States.rasterizer.cullMode != states.rasterizer.cullMode || force) { + if (states.rasterizer.cullMode != 0) { + glEnable(GL_CULL_FACE); + glFrontFace(states.rasterizer.cullMode); + } else { + glDisable(GL_CULL_FACE); + } + } + + if (this->m_States.rasterizer.fillMode != states.rasterizer.fillMode || force) { + glPolygonMode(GL_FRONT_AND_BACK, states.rasterizer.fillMode); + } + + if (this->m_CurrentTargetDepth + && (this->m_States.rasterizer.constantDepthBias != states.rasterizer.constantDepthBias + || this->m_States.rasterizer.slopeScaledDepthBias != states.rasterizer.slopeScaledDepthBias + || force)) + { + if (states.rasterizer.slopeScaledDepthBias == 0.0 && states.rasterizer.constantDepthBias == 0.0) { + glDisable(GL_POLYGON_OFFSET_FILL); + } else { + glEnable(GL_POLYGON_OFFSET_FILL); + } + + float units; + + if (states.rasterizer.constantDepthBias == 0.0 ) { + units = 0.0; + } else { + units = + (states.rasterizer.constantDepthBias * 2.0) + * (1 << this->m_CurrentTargetDepth->GetDepthBits()); + } + + glPolygonOffset(states.rasterizer.slopeScaledDepthBias * 2.0, units); + } + + if (this->m_States.rasterizer.viewport.left != states.rasterizer.viewport.left + || this->m_States.rasterizer.viewport.top != states.rasterizer.viewport.top + || this->m_States.rasterizer.viewport.width != states.rasterizer.viewport.width + || this->m_States.rasterizer.viewport.height != states.rasterizer.viewport.height + || force) + { + glViewport( + states.rasterizer.viewport.left, + states.rasterizer.viewport.top, + states.rasterizer.viewport.width, + states.rasterizer.viewport.height + ); + } + + if (this->m_States.rasterizer.zNear != states.rasterizer.zNear + || this->m_States.rasterizer.zFar != states.rasterizer.zFar + || force) + { + glDepthRange(states.rasterizer.zNear, states.rasterizer.zFar); + } + + if (this->m_States.rasterizer.scissorEnable != states.rasterizer.scissorEnable || force) { + if (states.rasterizer.scissorEnable) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + } + + if (this->m_States.rasterizer.scissor.left != states.rasterizer.scissor.left + || this->m_States.rasterizer.scissor.top != states.rasterizer.scissor.top + || this->m_States.rasterizer.scissor.width != states.rasterizer.scissor.width + || this->m_States.rasterizer.scissor.height != states.rasterizer.scissor.height + || force) + { + glScissor( + states.rasterizer.scissor.left, + states.rasterizer.scissor.top, + states.rasterizer.scissor.width, + states.rasterizer.scissor.height + ); + } + + int32_t maxClipPlaneIndex = GLSDLDevice::GetRendererInfo().unk36; + + if (this->m_States.rasterizer.clipPlaneMask != states.rasterizer.clipPlaneMask) { + for (int32_t i = 0; i < maxClipPlaneIndex; ++i) { + if (!(states.rasterizer.clipPlaneMask & this->m_States.rasterizer.clipPlaneMask & (1 << i))) { + if ((1 << i) & states.rasterizer.clipPlaneMask) { + glEnable(GL_CLIP_PLANE0 + i); + } else { + glDisable(GL_CLIP_PLANE0 + i); + } + } + } + } + + for (int32_t i = 0; i < maxClipPlaneIndex; ++i) { + if (memcmp(&this->m_States.rasterizer.clipPlanes[i].plane, &states.rasterizer.clipPlanes[i].plane, sizeof(this->m_States.rasterizer.clipPlanes[i].plane))) { + glClipPlane(GL_CLIP_PLANE0 + i, states.rasterizer.clipPlanes[i].plane); + } + } + + int32_t maxColorMaskIndex = GLSDLDevice::m_ExtColorMaskIndexed ? GLSDLDevice::GetRendererInfo().max_color_attachments : 1; + + for (int32_t i = 0; i < maxColorMaskIndex; ++i) { + if (memcmp(&this->m_States.blend.colorMask[i], &states.blend.colorMask[i], sizeof(GLColor4f)) || force) { + if (GLSDLDevice::m_ExtColorMaskIndexed) { + glColorMaskIndexedEXT( + i, + states.blend.colorMask[i].red, + states.blend.colorMask[i].green, + states.blend.colorMask[i].blue, + states.blend.colorMask[i].alpha + ); + } else { + glColorMask( + states.blend.colorMask[i].red, + states.blend.colorMask[i].green, + states.blend.colorMask[i].blue, + states.blend.colorMask[i].alpha + ); + } + } + } + + if (this->m_States.blend.alphaBlend != states.blend.alphaBlend || force) { + if (states.blend.alphaBlend) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + } + + if (this->m_States.blend.srcBlendFactor != states.blend.srcBlendFactor + || this->m_States.blend.destBlendFactor != states.blend.destBlendFactor + || force) + { + glBlendFunc(states.blend.srcBlendFactor, states.blend.destBlendFactor); + } + + if (this->m_States.blend.blendOp != states.blend.blendOp || force) { + glBlendEquation(states.blend.blendOp); + } + + if (memcmp(&this->m_States.blend.blendColor, &states.blend.blendColor, sizeof(GLColor4f)) || force) { + glBlendColor( + states.blend.blendColor.r, + states.blend.blendColor.g, + states.blend.blendColor.b, + states.blend.blendColor.a + ); + } + + if (this->m_States.fixedFunc.fogEnable != states.fixedFunc.fogEnable) { + if (states.fixedFunc.fogEnable) { + glEnable(GL_FOG); + } else { + glDisable(GL_FOG); + } + } + + if (memcmp(&this->m_States.fixedFunc.fogColor, &states.fixedFunc.fogColor, sizeof(GLColor4f)) || force) { + glFogfv(GL_FOG_COLOR, (GLfloat*)&states.fixedFunc.fogColor); + } + + if (this->m_States.fixedFunc.fogMode != states.fixedFunc.fogMode || force) { + glFogi(GL_FOG_MODE, states.fixedFunc.fogMode); + } + + if (this->m_States.fixedFunc.fogStart != states.fixedFunc.fogStart || force) { + glFogf(GL_FOG_START, states.fixedFunc.fogStart); + } + + if (this->m_States.fixedFunc.fogEnd != states.fixedFunc.fogEnd || force) { + glFogf(GL_FOG_END, states.fixedFunc.fogEnd); + } + + if (this->m_States.fixedFunc.fogDensity != states.fixedFunc.fogDensity || force) { + glFogf(GL_FOG_DENSITY, states.fixedFunc.fogDensity); + } + + if (this->m_States.fixedFunc.alphaTestEnable != states.fixedFunc.alphaTestEnable || force) { + if (states.fixedFunc.alphaTestEnable) { + glEnable(GL_ALPHA_TEST); + } else { + glDisable(GL_ALPHA_TEST); + } + } + + if (this->m_States.fixedFunc.alphaTestFunc != states.fixedFunc.alphaTestFunc + || this->m_States.fixedFunc.alphaTestRef != states.fixedFunc.alphaTestRef + || force) + { + glAlphaFunc(states.fixedFunc.alphaTestFunc, states.fixedFunc.alphaTestRef); + } + + if (this->m_States.fixedFunc.transforms.modelView.isIdentity != states.fixedFunc.transforms.modelView.isIdentity + || memcmp(this->m_States.fixedFunc.transforms.modelView.m, states.fixedFunc.transforms.modelView.m, sizeof(float) * 16) + || force) + { + glMatrixMode(GL_MODELVIEW); + + if (states.fixedFunc.transforms.modelView.isIdentity) { + glLoadIdentity(); + } else { + glLoadMatrixf(states.fixedFunc.transforms.modelView.m); + } + + const_cast(states).fixedFunc.transforms.modelView.isDirty = false; + } + + if (this->m_States.fixedFunc.transforms.projection.isIdentity != states.fixedFunc.transforms.projection.isIdentity + || memcmp(this->m_States.fixedFunc.transforms.projection.m, states.fixedFunc.transforms.projection.m, sizeof(float) * 16) + || force) + { + glMatrixMode(GL_PROJECTION); + + GLTransform projection = { + true, + { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + }, + true + }; + + if (!states.fixedFunc.transforms.projection.isIdentity) { + projection.isIdentity = false; + + memcpy(projection.m, states.fixedFunc.transforms.projection.m, sizeof(projection.m)); + projection.isDirty = true; + } + + if (projection.isIdentity) { + projection.SetIdentity(); + } + + projection.a1 *= -1.0f; + projection.b1 *= -1.0f; + projection.c1 *= -1.0f; + projection.d1 *= -1.0f; + + auto isIdentity = projection.a0 == 1.0f + && projection.a1 == 0.0f + && projection.a2 == 0.0f + && projection.a3 == 0.0f + && projection.b0 == 0.0f + && projection.b1 == 1.0f + && projection.b2 == 0.0f + && projection.b3 == 0.0f + && projection.c0 == 0.0f + && projection.c1 == 0.0f + && projection.c2 == 1.0f + && projection.c3 == 0.0f + && projection.d0 == 0.0f + && projection.d1 == 0.0f + && projection.d2 == 0.0f + && projection.d3 == 1.0f; + + projection.isDirty = true; + + if (isIdentity) { + glLoadIdentity(); + } else { + glLoadMatrixf(projection.m); + } + + projection.isDirty = false; + } + + glMatrixMode(states.fixedFunc.transforms.matrixMode); + glMatrixMode(GL_MODELVIEW); + + if (states.fixedFunc.transforms.view.isIdentity) { + glLoadIdentity(); + } else { + glLoadMatrixf(states.fixedFunc.transforms.view.m); + } + + const_cast(states).fixedFunc.transforms.view.isDirty = false; + + if (this->m_States.fixedFunc.lighting.enable != states.fixedFunc.lighting.enable || force) { + if (states.fixedFunc.lighting.enable) { + glEnable(GL_LIGHTING); + } else { + glDisable(GL_LIGHTING); + } + } + + for (int32_t i = 0; i < 8; ++i) { + // TODO + // Set up each light + } + + if (memcmp(&this->m_States.fixedFunc.lighting.sceneAmbient, &states.fixedFunc.lighting.sceneAmbient, sizeof(GLColor4f)) || force) { + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (GLfloat*)&states.fixedFunc.lighting.sceneAmbient); + } + + if (this->m_States.fixedFunc.lighting.material.materialSource != states.fixedFunc.lighting.material.materialSource || force) { + glColorMaterial(GL_FRONT_AND_BACK, states.fixedFunc.lighting.material.materialSource); + + // TODO + // this->Sub38A20(); + } + + if (this->m_States.fixedFunc.lighting.material.colorTracking != states.fixedFunc.lighting.material.colorTracking || force) { + if (states.fixedFunc.lighting.material.colorTracking) { + glEnable(GL_COLOR_MATERIAL); + } else { + glDisable(GL_COLOR_MATERIAL); + } + + // TODO + // this->Sub38A20(); + } + + if (memcmp(&this->m_States.fixedFunc.lighting.material.ambient, &states.fixedFunc.lighting.material.ambient, sizeof(GLColor4f)) || force) { + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat*)&states.fixedFunc.lighting.material.ambient); + } + + if (memcmp(&this->m_States.fixedFunc.lighting.material.diffuse, &states.fixedFunc.lighting.material.diffuse, sizeof(GLColor4f)) || force) { + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (GLfloat*)&states.fixedFunc.lighting.material.diffuse); + } + + if (memcmp(&this->m_States.fixedFunc.lighting.material.specular, &states.fixedFunc.lighting.material.specular, sizeof(GLColor4f)) || force) { + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat*)&states.fixedFunc.lighting.material.specular); + } + + if (memcmp(&this->m_States.fixedFunc.lighting.material.emission, &states.fixedFunc.lighting.material.emission, sizeof(GLColor4f)) || force) { + glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, (GLfloat*)&states.fixedFunc.lighting.material.emission); + } + + if (this->m_States.fixedFunc.lighting.material.shininess != states.fixedFunc.lighting.material.shininess || force) { + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, states.fixedFunc.lighting.material.shininess); + } + + for (int32_t i = 0; i < 8; ++i) { + // TODO fixedFunc.transforms.texture[i] + // TODO fixedFunc.texOp[i] + // TODO fixedFunc.texGen[i] + } + + glMatrixMode(states.fixedFunc.transforms.matrixMode); + + if (this->m_States.fixedFunc.normalizeNormal != states.fixedFunc.normalizeNormal) { + if (states.fixedFunc.normalizeNormal) { + glEnable(GL_NORMALIZE); + } else { + glDisable(GL_NORMALIZE); + } + } + + if (this->m_States.fixedFunc.pointSprite.enable != states.fixedFunc.pointSprite.enable || force) { + if (states.fixedFunc.pointSprite.enable) { + glEnable(GL_POINT_SPRITE); + glEnable(GL_PROGRAM_POINT_SIZE_EXT); + } else { + glDisable(GL_POINT_SPRITE); + glDisable(GL_PROGRAM_POINT_SIZE_EXT); + } + } + + if (this->m_States.fixedFunc.pointSprite.size != states.fixedFunc.pointSprite.size || force) { + glPointSize(states.fixedFunc.pointSprite.size); + } + + if (memcmp(&this->m_States.fixedFunc.pointSprite.attenuation, &states.fixedFunc.pointSprite.attenuation, sizeof(this->m_States.fixedFunc.pointSprite.attenuation)) || force) { + glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION, states.fixedFunc.pointSprite.attenuation); + } + + if (this->m_States.fixedFunc.pointSprite.min != states.fixedFunc.pointSprite.min || force ) { + glPointParameterfARB(GL_POINT_SIZE_MIN, states.fixedFunc.pointSprite.min); + } + + if (this->m_States.fixedFunc.pointSprite.max != states.fixedFunc.pointSprite.max || force) { + glPointParameterfARB(GL_POINT_SIZE_MAX, states.fixedFunc.pointSprite.max); + } + + if (this->m_States.shader.vertexShaderEnable != states.shader.vertexShaderEnable || force) { + if (states.shader.vertexShaderEnable) { + glEnable(GL_VERTEX_PROGRAM_ARB); + } else { + glDisable(GL_VERTEX_PROGRAM_ARB); + } + } + + glProgramEnvParameters4fvEXT(GL_VERTEX_PROGRAM_ARB, 0, 256, (GLfloat*)states.shader.vertexShaderConst); + + if (this->m_States.shader.pixelShaderEnable != states.shader.pixelShaderEnable || force) { + if (states.shader.pixelShaderEnable) { + glEnable(GL_FRAGMENT_PROGRAM_ARB); + } else { + glDisable(GL_FRAGMENT_PROGRAM_ARB); + } + } + + glProgramEnvParameters4fvEXT(GL_FRAGMENT_PROGRAM_ARB, 0, 64, (GLfloat*)states.shader.pixelShaderConst); + + if (memcmp(&this->m_States.clear.clearColor, &states.clear.clearColor, sizeof(GLColor4f)) || force) { + glClearColor( + states.clear.clearColor.r, + states.clear.clearColor.g, + states.clear.clearColor.b, + states.clear.clearColor.a + ); + } + + if (this->m_States.clear.clearDepth != states.clear.clearDepth || force) { + glClearDepth(states.clear.clearDepth); + } + + if (this->m_States.clear.clearStencil != states.clear.clearStencil || force) { + glClearStencil(states.clear.clearStencil); + } + + // Copy provided states into current states + + memcpy(&this->m_States.depth, &states.depth, sizeof(this->m_States.depth)); + memcpy(&this->m_States.stencil, &states.stencil, sizeof(this->m_States.stencil)); + + if (!states.stencil.useTwoSidedStencil) { + memcpy(&this->m_States.stencil.back, &states.stencil.front, sizeof(this->m_States.stencil.back)); + } + + if (this->m_CurrentTargetDepth) { + memcpy(&this->m_States.rasterizer, &states.rasterizer, sizeof(this->m_States.rasterizer)); + } else { + float v117 = this->m_States.rasterizer.constantDepthBias; + float v118 = this->m_States.rasterizer.slopeScaledDepthBias; + memcpy(&this->m_States.rasterizer, &states.rasterizer, sizeof(this->m_States.rasterizer)); + this->m_States.rasterizer.constantDepthBias = v117; + this->m_States.rasterizer.slopeScaledDepthBias = v118; + } + + memcpy(&this->m_States.blend, &states.blend, sizeof(this->m_States.blend)); + memcpy(&this->m_States.clear, &states.clear, sizeof(this->m_States.clear)); + + this->m_States.fixedFunc.fogEnable = states.fixedFunc.fogEnable; + memcpy(&this->m_States.fixedFunc.fogColor, &states.fixedFunc.fogColor, sizeof(GLColor4f)); + this->m_States.fixedFunc.fogMode = states.fixedFunc.fogMode; + this->m_States.fixedFunc.fogStart = states.fixedFunc.fogStart; + this->m_States.fixedFunc.fogEnd = states.fixedFunc.fogEnd; + + this->m_States.fixedFunc.alphaTestEnable = states.fixedFunc.alphaTestEnable; + this->m_States.fixedFunc.alphaTestRef = states.fixedFunc.alphaTestRef; + + memcpy(this->m_States.fixedFunc.texOp, states.fixedFunc.texOp, sizeof(this->m_States.fixedFunc.texOp)); + + this->m_States.fixedFunc.lighting.enable = states.fixedFunc.lighting.enable; + memcpy(&this->m_States.fixedFunc.lighting.sceneAmbient, &states.fixedFunc.lighting.sceneAmbient, sizeof(GLColor4f)); + + for (int32_t i = 0; i < 8; ++i) { + this->m_States.fixedFunc.lighting.lights[i].enable = states.fixedFunc.lighting.lights[i].enable; + memcpy(&this->m_States.fixedFunc.lighting.lights[i].position, &states.fixedFunc.lighting.lights[i].position, sizeof(GLfloat4)); + COPY_TRANSFORM(this->m_States.fixedFunc.lighting.lights[i].view, states.fixedFunc.lighting.lights[i].view); + this->m_States.fixedFunc.lighting.lights[i].constantAttenuation = states.fixedFunc.lighting.lights[i].constantAttenuation; + this->m_States.fixedFunc.lighting.lights[i].linearAttenuation = states.fixedFunc.lighting.lights[i].linearAttenuation; + this->m_States.fixedFunc.lighting.lights[i].quadraticAttenuation = states.fixedFunc.lighting.lights[i].quadraticAttenuation; + memcpy(&this->m_States.fixedFunc.lighting.lights[i].ambient, &states.fixedFunc.lighting.lights[i].ambient, sizeof(GLColor4f)); + memcpy(&this->m_States.fixedFunc.lighting.lights[i].diffuse, &states.fixedFunc.lighting.lights[i].diffuse, sizeof(GLColor4f)); + memcpy(&this->m_States.fixedFunc.lighting.lights[i].specular, &states.fixedFunc.lighting.lights[i].specular, sizeof(GLColor4f)); + } + + memcpy(&this->m_States.fixedFunc.lighting.material, &states.fixedFunc.lighting.material, sizeof(this->m_States.fixedFunc.lighting.material)); + + COPY_TRANSFORM(this->m_States.fixedFunc.transforms.modelView, states.fixedFunc.transforms.modelView); + COPY_TRANSFORM(this->m_States.fixedFunc.transforms.world, states.fixedFunc.transforms.world); + COPY_TRANSFORM(this->m_States.fixedFunc.transforms.view, states.fixedFunc.transforms.view); + COPY_TRANSFORM(this->m_States.fixedFunc.transforms.projection, states.fixedFunc.transforms.projection); + + for (int32_t i = 0; i < 8; ++i) { + COPY_TRANSFORM(this->m_States.fixedFunc.transforms.texture[i], states.fixedFunc.transforms.texture[i]); + } + + memcpy(this->m_States.fixedFunc.texCoordIndex, states.fixedFunc.texCoordIndex, sizeof(this->m_States.fixedFunc.texCoordIndex)); + memcpy(this->m_States.fixedFunc.texGen, states.fixedFunc.texGen, sizeof(this->m_States.fixedFunc.texGen)); + + memcpy(this->m_States.samplers, states.samplers, sizeof(this->m_States.samplers)); + memcpy(&this->m_States.shader, &states.shader, sizeof(this->m_States.shader)); +} + +void GLSDLDevice::ApplyShaderConstants() { + GLShader* vs = this->m_VertexShader; + + if (vs) { + if (vs->m_UsingGLSL) { + vs->FlushUniforms(this->m_GLSLProgram); + } else { + auto start = this->m_DirtyVertexShaderConsts.start; + auto end = this->m_DirtyVertexShaderConsts.end; + + if (start != end) { + glProgramEnvParameters4fvEXT( + GL_VERTEX_PROGRAM_ARB, + start, + end - start, + reinterpret_cast(&this->m_States.shader.vertexShaderConst[start]) + ); + + this->m_DirtyVertexShaderConsts.start = 0; + this->m_DirtyVertexShaderConsts.end = 0; + } + } + } + + GLShader* ps = this->m_PixelShader; + + if (ps) { + if (ps->m_UsingGLSL) { + ps->FlushUniforms(this->m_GLSLProgram); + } else { + auto start = this->m_DirtyPixelShaderConsts.start; + auto end = this->m_DirtyPixelShaderConsts.end; + + if (start != end) { + glProgramEnvParameters4fvEXT( + GL_FRAGMENT_PROGRAM_ARB, + start, + end - start, + reinterpret_cast(&this->m_States.shader.pixelShaderConst[start]) + ); + + this->m_DirtyPixelShaderConsts.start = 0; + this->m_DirtyPixelShaderConsts.end = 0; + } + } + } +} + +void GLSDLDevice::ApplyTransforms() { + this->SetModelView(GL_MODELVIEW); + + auto& projection = this->m_States.fixedFunc.transforms.projection; + if (projection.isDirty) { + if (projection.isIdentity) { + projection.SetIdentity(); + } + + projection.a1 *= -1.0f; + projection.b1 *= -1.0f; + projection.c1 *= -1.0f; + projection.d1 *= -1.0f; + + projection.isIdentity = projection.a0 == 1.0f + && projection.a1 == 0.0f + && projection.a2 == 0.0f + && projection.a3 == 0.0f + && projection.b0 == 0.0f + && projection.b1 == 1.0f + && projection.b2 == 0.0f + && projection.b3 == 0.0f + && projection.c0 == 0.0f + && projection.c1 == 0.0f + && projection.c2 == 1.0f + && projection.c3 == 0.0f + && projection.d0 == 0.0f + && projection.d1 == 0.0f + && projection.d2 == 0.0f + && projection.d3 == 1.0f; + + projection.isDirty = true; + + if (this->m_States.fixedFunc.transforms.matrixMode != GL_PROJECTION) { + glMatrixMode(GL_PROJECTION); + this->m_States.fixedFunc.transforms.matrixMode = GL_PROJECTION; + } + + if (projection.isIdentity) { + glLoadIdentity(); + } else { + glLoadMatrixf(projection.m); + } + + projection.isDirty = false; + } + + // TODO texture transforms +} + +void GLSDLDevice::BindBuffer(GLBuffer* buffer, GLEnum target) { + BLIZZARD_ASSERT(this->m_Context.IsCurrentContext()); + BLIZZARD_ASSERT(buffer != nullptr || target != GL_ZERO); + + GLEnum bindTarget = target == GL_ZERO ? buffer->m_Type : target; + GLuint bindName = buffer == nullptr ? 0 : buffer->m_BufferID; + + int32_t bindIndex; + + if (bindTarget == GL_ARRAY_BUFFER) { + bindIndex = 0; + } else if (bindTarget == GL_ELEMENT_ARRAY_BUFFER) { + bindIndex = 1; + } else if (bindTarget == GL_PIXEL_PACK_BUFFER) { + bindIndex = 2; + } else if (bindTarget == GL_PIXEL_UNPACK_BUFFER) { + bindIndex = 3; + } else { + BLIZZARD_ASSERT(false); + } + + if (bindTarget == GL_ARRAY_BUFFER) { + if (this->m_States.binding.vertexArrayObject && this->m_VertexArrayObject != &this->m_DefaultVertexArrayObject) { + glBindVertexArray(0); + this->m_States.binding.vertexArrayObject = 0; + this->m_VertexArrayObject = &this->m_DefaultVertexArrayObject; + } + } else if (bindTarget == GL_PIXEL_PACK_BUFFER) { + this->m_VertexArrayObject->m_Properties.m_PixelPackBuffer = buffer; + } else if (bindTarget == GL_PIXEL_UNPACK_BUFFER) { + this->m_VertexArrayObject->m_Properties.m_PixelUnpackBuffer = buffer; + } + + if (this->m_VertexArrayObject->m_GLStates.buffers[bindIndex] != bindName) { + glBindBuffer(bindTarget, bindName); + this->m_VertexArrayObject->m_GLStates.buffers[bindIndex] = bindName; + } +} + +void GLSDLDevice::BindFramebuffer(GLFramebuffer* framebuffer) { + BLIZZARD_ASSERT(this->m_Context.IsCurrentContext()); + + GLuint v3; + + if (framebuffer) { + v3 = framebuffer->m_FramebufferID; + } else { + v3 = 0; + } + + this->m_CurrentTarget = framebuffer; + + if (this->m_States.binding.framebuffer != v3) { + glBindFramebufferEXT(GL_FRAMEBUFFER, v3); + this->m_States.binding.framebuffer = v3; + } +} + +void GLSDLDevice::BindGLSLProgram(GLGLSLProgram* a2) { + // TODO +} + +void GLSDLDevice::BindShader(GLShader* shader) { + BLIZZARD_ASSERT(this->m_Context.IsCurrentContext()); + BLIZZARD_ASSERT(shader); + + if (shader->var5 == GL_FRAGMENT_PROGRAM_ARB) { + if (this->m_States.binding.pixelProgram != shader->m_ShaderID) { + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader->m_ShaderID); + this->m_States.binding.pixelProgram = shader->m_ShaderID; + } + } else if (shader->var5 == GL_VERTEX_PROGRAM_ARB) { + if (this->m_States.binding.vertexProgram != shader->m_ShaderID) { + glBindProgramARB(GL_VERTEX_PROGRAM_ARB, shader->m_ShaderID); + this->m_States.binding.vertexProgram = shader->m_ShaderID; + } + } else { + BLIZZARD_ASSERT(false); + } +} + +void GLSDLDevice::BindTexture(GLEnum textureType, GLTexture* texture) { + BLIZZARD_ASSERT(this->m_Context.IsCurrentContext()); + BLIZZARD_ASSERT(texture == nullptr || textureType == texture->m_TextureType); + + uint32_t textureID = texture ? texture->m_TextureID : 0; + uint32_t index = GLSDLTextureTypeToIndex(textureType); + + BLIZZARD_ASSERT(this->m_States.binding.texture[index][this->m_States.binding.currentActiveTexture] != textureID); + BLIZZARD_ASSERT(this->m_BoundTextures[index][this->m_States.binding.currentActiveTexture] != texture); + + GLTexture* boundTexture = this->m_BoundTextures[index][this->m_States.binding.currentActiveTexture]; + if (boundTexture) { + boundTexture->Unbind(this, this->m_States.binding.currentActiveTexture); + } + + glBindTexture(textureType, textureID); + this->m_BoundTextures[index][this->m_States.binding.currentActiveTexture] = texture; + this->m_States.binding.texture[index][this->m_States.binding.currentActiveTexture] = textureID; + + if (!texture || this->m_WorkerDevice || textureType == GL_TEXTURE_RECTANGLE) { + return; + } + + GLSDLDevice* mainDevice = GLSDLDevice::m_Devices[0]; + + if (texture->var7 == mainDevice->m_TextureList.begin()) { + texture->var7 = mainDevice->m_TextureList.insert(mainDevice->m_TextureList.begin(), texture); + + mainDevice->m_TextureTotalSize += texture->m_Size; + texture->m_LastFrameUsed = mainDevice->m_FrameNumber; + } else if (texture->m_LastFrameUsed == 0 || texture->m_LastFrameUsed + 60 < mainDevice->m_FrameNumber) { + if (texture->m_LastFrameUsed == 0) { + texture->RecreateGLTexture(); + mainDevice->m_TextureTotalSize += texture->m_Size; + } + + // TODO + // - oldest texture logic + + // TODO + // - list transfer logic + + texture->m_LastFrameUsed = mainDevice->m_FrameNumber; + } + + if (mainDevice->m_TextureTotalSize > 384 * 1024 * 1024) { + // TODO + // - track / clean up oldest textures + } +} + +void GLSDLDevice::BindVertexArray(GLVertexArray* a2) { + BLIZZARD_ASSERT(this->m_Context.IsCurrentContext()); + + int32_t v4 = a2 ? a2->m_VertexArrayID : 0; + + if (this->m_States.binding.vertexArrayObject != v4) { + glBindVertexArray(v4); + this->m_States.binding.vertexArrayObject = v4; + this->m_VertexArrayObject = a2 ? a2 : &this->m_DefaultVertexArrayObject; + } +} + +void GLSDLDevice::BlitFramebuffer(GLMipmap* src, const GLRect* srcRect, GLMipmap* dst, const GLRect* dstRect, GLEnum mask, GLEnum filter) { + BLIZZARD_ASSERT(mask == GL_COLOR_BUFFER_BIT); + BLIZZARD_ASSERT(src != nullptr); + + GLRect fullSrcRect = { + 0, + 0, + src->GetWidth(), + src->GetHeight() + }; + + GLRect fullDstRect = { + 0, + 0, + dst ? dst->GetWidth() : this->m_Window->GetWidth(), + dst ? dst->GetHeight() : this->m_Window->GetHeight() + }; + + BLIZZARD_ASSERT(filter == GL_NEAREST); + // TODO + // BLIZZARD_ASSERT(srcRect == nullptr || *srcRect == fullSrcRect); + + // TODO + // - non-shader code path + + glEnable(GL_VERTEX_PROGRAM_ARB); + glEnable(GL_FRAGMENT_PROGRAM_ARB); + + auto alphaTestEnable = this->m_States.fixedFunc.alphaTestEnable; + auto depthTestEnable = this->m_States.depth.testEnable; + auto alphaBlend = this->m_States.blend.alphaBlend; + auto cullEnable = this->m_States.rasterizer.cullMode != 0; + auto scissorEnable = this->m_States.rasterizer.scissorEnable; + auto stencilTestEnable = this->m_States.stencil.testEnable; + uint32_t width; + uint32_t height; + + if (alphaTestEnable) { + glDisable(GL_ALPHA_TEST); + } + + if (depthTestEnable) { + glDisable(GL_DEPTH_TEST); + } + + if (alphaBlend) { + glDisable(GL_BLEND); + } + + if (cullEnable) { + glDisable(GL_CULL_FACE); + } + + if (scissorEnable) { + glDisable(GL_SCISSOR_TEST); + } + + if (stencilTestEnable) { + glDisable(GL_STENCIL_TEST); + } + + if (GLSDLDevice::m_ExtColorMaskIndexed) { + glColorMaskIndexedEXT(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } else { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + + if (dst) { + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + dst->m_Target, + dst->GetTextureID(), + dst->m_Level + ); + + auto currentTargetDepth = this->m_CurrentTargetDepth; + if (currentTargetDepth) { + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + currentTargetDepth->m_Target, + 0, + 0 + ); + } + + auto currentTargetStencil = this->m_CurrentTargetStencil; + if (currentTargetStencil) { + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + currentTargetStencil->m_Target, + 0, + 0 + ); + } + + dst->m_Texture->SetAddressModeS(GL_CLAMP_TO_EDGE); + dst->m_Texture->SetAddressModeT(GL_CLAMP_TO_EDGE); + dst->m_Texture->SetMinFilterMode(GL_LINEAR); + dst->m_Texture->SetMagFilterMode(GL_LINEAR); + + width = dst->m_Width; + height = dst->m_Height; + } else { + glBindFramebufferEXT(GL_FRAMEBUFFER, 0); + + width = this->m_Window->GetWidth(); + height = this->m_Window->GetHeight(); + } + + if ( + this->m_States.rasterizer.viewport.left != 0 + || this->m_States.rasterizer.viewport.top != 0 + || this->m_States.rasterizer.viewport.width != width + || this->m_States.rasterizer.viewport.height != height + ) { + glViewport(0, 0, width, height); + } + + this->SetActiveTexture(0); + src->m_Texture->Bind(nullptr, true); + src->m_Texture->SetAddressModeS(GL_CLAMP_TO_EDGE); + src->m_Texture->SetAddressModeT(GL_CLAMP_TO_EDGE); + src->m_Texture->SetMinFilterMode(filter); + src->m_Texture->SetMagFilterMode(filter); + + this->SetVertexBuffer(0, GLSDLDevice::m_BlitQuadVBO, 0, 28); + + GLVertexFormat* format = dst + ? &GLSDLDevice::m_NormalBlitVF + : &GLSDLDevice::m_InvertedBlitVF; + + this->SetVertexFormat(format); + + auto vertexShader = this->m_VertexShader; + auto pixelShader = this->m_PixelShader; + this->BindGLSLProgram(nullptr); + + this->SetShader(GLShader::eVertexShader, GLSDLDevice::m_DeviceShaders[0]); + this->SetShader(GLShader::ePixelShader, GLSDLDevice::m_DeviceShaders[2]); + + this->m_DefaultVertexArrayObject.m_Properties.m_VertexBase = 0; + GLVertexArray::FindVertexArray(this, this->m_DefaultVertexArrayObject); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + this->SetShader(GLShader::eVertexShader, vertexShader); + this->SetShader(GLShader::ePixelShader, pixelShader); + + if (alphaTestEnable) { + glEnable(GL_ALPHA_TEST); + } + + if (depthTestEnable) { + glEnable(GL_DEPTH_TEST); + } + + if (alphaBlend) { + glEnable(GL_BLEND); + } + + if (cullEnable) { + glEnable(GL_CULL_FACE); + } + + if (scissorEnable) { + glEnable(GL_SCISSOR_TEST); + } + + if (stencilTestEnable) { + glEnable(GL_STENCIL_TEST); + } + + if (GLSDLDevice::m_ExtColorMaskIndexed) { + glColorMaskIndexedEXT( + 0, + this->m_States.blend.colorMask[0].red, + this->m_States.blend.colorMask[0].green, + this->m_States.blend.colorMask[0].blue, + this->m_States.blend.colorMask[0].alpha + ); + } else { + glColorMask( + this->m_States.blend.colorMask[0].red, + this->m_States.blend.colorMask[0].green, + this->m_States.blend.colorMask[0].blue, + this->m_States.blend.colorMask[0].alpha + ); + } + + if (dst) { + auto currentTargetColor = this->m_CurrentTargetColor[0]; + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + dst->m_Target, + currentTargetColor->GetTextureID(), + 0 + ); + + auto currentTargetDepth = this->m_CurrentTargetDepth; + if (currentTargetDepth) { + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + currentTargetDepth->m_Target, + currentTargetDepth->GetTextureID(), + currentTargetDepth->m_Level + ); + } + + auto currentTargetStencil = this->m_CurrentTargetStencil; + if (currentTargetStencil) { + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + currentTargetStencil->m_Target, + currentTargetStencil->GetTextureID(), + currentTargetStencil->m_Level + ); + } + } + + if ( + this->m_States.rasterizer.viewport.left != 0 + || this->m_States.rasterizer.viewport.top != 0 + || this->m_States.rasterizer.viewport.width != width + || this->m_States.rasterizer.viewport.height != height + ) { + glViewport( + this->m_States.rasterizer.viewport.left, + this->m_States.rasterizer.viewport.top, + this->m_States.rasterizer.viewport.width, + this->m_States.rasterizer.viewport.height + ); + } +} + +void GLSDLDevice::CheckDepthTarget() { + BLIZZARD_ASSERT(this->m_CurrentTargetColor[0] == nullptr || this->m_CurrentTargetColor[0]->GetTexture()->IsValid()); + BLIZZARD_ASSERT(this->m_CurrentTargetColor[1] == nullptr || this->m_CurrentTargetColor[1]->GetTexture()->IsValid()); + BLIZZARD_ASSERT(this->m_CurrentTargetColor[2] == nullptr || this->m_CurrentTargetColor[2]->GetTexture()->IsValid()); + BLIZZARD_ASSERT(this->m_CurrentTargetColor[3] == nullptr || this->m_CurrentTargetColor[3]->GetTexture()->IsValid()); + + BLIZZARD_ASSERT(this->m_CurrentTargetDepth == nullptr || this->m_CurrentDepthBuffer != nullptr); + + if (!this->m_CurrentDepthBuffer) { + return; + } + + BLIZZARD_ASSERT(this->m_CurrentDepthBuffer->GetTexture()->IsValid()); + + auto currentTargetColor = this->m_CurrentTargetColor[0]; + auto currentDepthBuffer = this->m_CurrentDepthBuffer; + + if ( + !currentTargetColor + || (currentTargetColor->m_Width == currentDepthBuffer->m_Width && currentTargetColor->m_Height == currentDepthBuffer->m_Height) + ) { + if (this->m_CurrentTargetDepth != currentDepthBuffer) { + this->Sub34BB0(GL_DEPTH_ATTACHMENT, currentDepthBuffer, 0); + this->m_CurrentTarget->Attach(currentDepthBuffer, GL_DEPTH_ATTACHMENT, 0); + auto currentTargetDepth = this->m_CurrentTargetDepth; + this->m_CurrentTargetDepth = currentDepthBuffer; + this->m_CurrentDepthBuffer = currentDepthBuffer; + + if ( + (currentTargetDepth == nullptr && currentDepthBuffer != nullptr) + || (currentDepthBuffer != nullptr && currentTargetDepth != nullptr && currentTargetDepth->m_DepthBits != currentDepthBuffer->m_DepthBits) + ) { + this->SetDepthBias(this->m_ConstantDepthBias, this->m_SlopeScaledDepthBias); + } + + if (this->m_CurrentDepthBuffer->GetFormat() == GLTF_D24S8) { + this->Sub34BB0(GL_STENCIL_ATTACHMENT, this->m_CurrentDepthBuffer, 0); + this->m_CurrentTarget->Attach(this->m_CurrentDepthBuffer, GL_STENCIL_ATTACHMENT, 0); + this->m_CurrentTargetStencil = this->m_CurrentDepthBuffer; + } + } + + return; + } + + auto currentTargetDepth = this->m_CurrentTargetDepth; + + if (currentTargetDepth == currentDepthBuffer) { + this->Sub34BB0(GL_DEPTH_ATTACHMENT, nullptr, 0); + this->m_CurrentTarget->Attach(nullptr, GL_DEPTH_ATTACHMENT, 0); + this->m_CurrentTargetDepth = nullptr; + this->m_CurrentDepthBuffer = currentTargetDepth; + + if (currentTargetDepth->GetFormat() == GLTF_D24S8) { + this->Sub34BB0(GL_STENCIL_ATTACHMENT, nullptr, 0); + this->m_CurrentTarget->Attach(nullptr, GL_STENCIL_ATTACHMENT, 0); + this->m_CurrentTargetStencil = nullptr; + } + } +} + +void GLSDLDevice::Clear(uint32_t clearMask, const GLColor4f& clearColor, double clearDepth, int32_t clearStencil) { + this->CheckDepthTarget(); + this->RestoreTextures(); + + auto colorMask = this->m_States.blend.colorMask[0]; + + if (clearMask & 0x4000) { + for (int32_t i = 0; i < 4; i++) { + if (this->m_CurrentTargetColor[i] && this->m_CurrentTargetColor[i]->GetTextureID() == this->m_States.binding.texture[0][this->m_States.binding.currentActiveTexture]) { + this->BindTexture(this->m_CurrentTargetColor[i]->m_Target, nullptr); + } + } + + this->SetColorWriteMask(1, 1, 1, 1, 0); + this->SetClearColor(clearColor); + } + + if (clearMask & 0x100) { + if (this->m_CurrentTargetDepth) { + if (this->m_CurrentTargetDepth->GetTextureID() == this->m_States.binding.texture[0][this->m_States.binding.currentActiveTexture]) { + this->BindTexture(this->m_CurrentTargetDepth->m_Target, nullptr); + } + + if (!this->m_States.depth.writeMask) { + glDepthMask(1); + } + + this->SetClearDepth(clearDepth); + } else { + clearMask &= ~0x100; + } + } else { + if (this->m_CurrentTargetDepth) { + this->m_CurrentTarget->Detach(GL_DEPTH_ATTACHMENT); + } + } + + if (clearMask & 0x400) { + if (this->m_CurrentTargetStencil) { + if (this->m_CurrentTargetStencil->GetTextureID() == this->m_States.binding.texture[0][this->m_States.binding.currentActiveTexture]) { + this->BindTexture(this->m_CurrentTargetStencil->m_Target, nullptr); + } + + if (this->m_States.stencil.writeMask != -1) { + glStencilMask(-1); + } + + this->SetClearStencil(clearStencil); + } else { + clearMask &= ~0x400; + } + } else { + if (this->m_CurrentTargetStencil) { + this->m_CurrentTarget->Detach(GL_STENCIL_ATTACHMENT); + } + } + + auto scissorEnable = this->m_States.rasterizer.scissorEnable; + auto scissorRect = this->m_States.rasterizer.scissor; + auto currentTargetColor = this->m_CurrentTargetColor[0]; + + if ( + currentTargetColor + && ( + this->m_States.rasterizer.viewport.left + || this->m_States.rasterizer.viewport.top + || this->m_States.rasterizer.viewport.width != currentTargetColor->m_Width + || this->m_States.rasterizer.viewport.height != currentTargetColor->m_Height + ) + ) { + this->SetScissor(1, this->m_States.rasterizer.viewport); + glClear(clearMask); + this->SetScissor(scissorEnable, scissorRect); + } else { + glClear(clearMask); + } + + if (clearMask & 0x4000) { + this->SetColorWriteMask(colorMask.red, colorMask.blue, colorMask.green, colorMask.alpha, 0); + } + + if (clearMask & 0x100) { + if (!this->m_States.depth.writeMask) { + glDepthMask(0); + } + } else { + if (this->m_CurrentTargetDepth) { + this->m_CurrentTarget->Attach(this->m_CurrentTargetDepth, GL_DEPTH_ATTACHMENT, 0); + } + } + + if (clearMask & 0x400) { + if (this->m_States.stencil.writeMask != -1) { + glStencilMask(this->m_States.stencil.writeMask); + } + } else { + if (this->m_CurrentTargetStencil) { + this->m_CurrentTarget->Attach(this->m_CurrentTargetStencil, GL_STENCIL_ATTACHMENT, 0); + } + } +} + +void GLSDLDevice::CopyTex(uint32_t a2, uint32_t a3, GLMipmap* dst, const GLRect* framebufferRect) { + BLIZZARD_ASSERT(framebufferRect->width == dst->GetWidth()); + BLIZZARD_ASSERT(framebufferRect->height == dst->GetHeight()); + + dst->m_Texture->Bind(nullptr, false); + + glCopyTexSubImage2D( + dst->m_Target, + dst->m_Level, + a2, + a3, + framebufferRect->left, + framebufferRect->top, + framebufferRect->width, + framebufferRect->height + ); +} + +GLBuffer* GLSDLDevice::CreateBuffer(GLEnum type, uint32_t a3, const void* a4, GLEnum usage, GLEnum format) { + return GLBuffer::Create(type, a3, a4, usage, format); +} + +GLShader* GLSDLDevice::CreateShader(GLShader::ShaderType type, const void* buf, int32_t codeLen, const char* name) { + // TODO + // Blizzard::Debug::Assert(this); + + return GLShader::Create(type, GLSDLDevice::m_UseHybridShader, false, "", buf, codeLen, "", name, nullptr); +} + +GLTexture* GLSDLDevice::CreateTexture2D(uint32_t width, uint32_t height, uint32_t numMipMap, GLTextureFormat format, uint32_t flags) { + return GLTexture2D::Create(width, height, numMipMap, format, flags); +} + +GLTexture* GLSDLDevice::CreateTextureCubeMap(uint32_t size, uint32_t numMipMap, GLTextureFormat format, uint32_t flags) { + // TODO + + return nullptr; +} + +void GLSDLDevice::Draw(GLEnum primitive, uint32_t a3, uint32_t a4) { + // TODO +} + +void GLSDLDevice::DrawIndexed(GLEnum primitive, uint32_t a3, uint32_t a4, uint32_t a5, uint32_t a6, uint32_t count) { + this->GLSDLDraw(primitive, a3, a4, a5, a6, count); +} + +void GLSDLDevice::DrawRect() { + if (!this->m_Window) { + return; + } + + if (!this->m_UseWindowSystemBuffer || this->m_FlippedSystemBuffer) { + GLTexture2D* backBuffer = this->m_BackBufferColor; + + if (!backBuffer) { + return; + } + + GLMipmap* backBufferImage = backBuffer->GetMipmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + this->BlitFramebuffer(backBufferImage, nullptr, nullptr, nullptr, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + glBindFramebufferEXT(GL_FRAMEBUFFER, 0); + this->m_States.binding.framebuffer = 0; + } + + this->m_Window->Swap(); + + if (!this->m_UseWindowSystemBuffer || this->m_FlippedSystemBuffer) { + glBindFramebufferEXT(GL_FRAMEBUFFER, this->m_CurrentTarget->m_FramebufferID); + this->m_States.binding.framebuffer = this->m_CurrentTarget->m_FramebufferID; + } +} + +uint32_t GLSDLDevice::GetID() { + return this->m_ID; +} + +GLFramebuffer* GLSDLDevice::GetCurrentTarget() { + return this->m_CurrentTarget; +} + +GLShader* GLSDLDevice::GetShader(GLShader::ShaderType shaderType) { + if (shaderType == GLShader::eVertexShader) { + return this->m_VertexShader; + } else if (shaderType == GLShader::ePixelShader) { + return this->m_PixelShader; + } else { + return nullptr; + } +} + +const GLStates::VertexArrayObject& GLSDLDevice::GetVertexArrayStates() { + return this->m_VertexArrayObject->m_GLStates; +} + +void GLSDLDevice::GLSDLDraw(GLEnum mode, uint32_t start, uint32_t end, uint32_t a5, uint32_t a6, uint32_t count) { + BLIZZARD_ASSERT(this->m_Context.IsCurrentContext()); + + this->CheckDepthTarget(); + + // if (!this->m_DrawCount) { + // if (this->m_Context.m_MTGLEnabled) { + // glFlush(); + // } + + // // GLFence::TestFences(); + // } + + this->m_DrawCount++; + + this->RestoreTextures(); + + this->m_DefaultVertexArrayObject.m_Properties.m_VertexBase = a5; + GLVertexArray::FindVertexArray(this, this->m_DefaultVertexArrayObject); + + auto vs = this->m_VertexShader; + auto ps = this->m_PixelShader; + + if (ps) { + // TODO + // this->Sub30440(); + } + + // if (this->m_TexWorker && !this->m_TexWorker->m_UnkA1) { + // this->m_TexWorker->WaitOnGLObjects(); + // } + + if (vs) { + this->SetShader(GLShader::eVertexShader, vs); + } else { + this->ApplyTransforms(); + } + + if (ps) { + this->SetShader(GLShader::ePixelShader, ps); + } else { + this->UpdateFFPTexturing(); + } + + if (vs && vs->m_UsingGLSL && ps && ps->m_UsingGLSL) { + this->m_GLSLProgram = GLGLSLProgram::Find(vs, ps); + } else { + this->BindGLSLProgram(nullptr); + } + + this->ApplyShaderConstants(); + + if (count) { + GLBuffer* buffer = this->m_VertexArrayObject->m_Properties.m_IndexBuffer; + GLEnum format = buffer->m_IndexFormat; + + uint32_t v18; + + if (format == GL_UNSIGNED_SHORT) { + v18 = 1; + } else if (format == GL_UNSIGNED_INT) { + v18 = 2; + } else { + BLIZZARD_ASSERT(!"buffer uses unknown format"); + } + + void* indices = GLBuffer::m_UsingVBO + ? reinterpret_cast(a6 << v18) + : buffer->m_Data + (a6 << v18); + + glDrawRangeElements(mode, start, end, count, buffer->m_IndexFormat, indices); + } else { + glDrawArrays(mode, start, end - start); + } +} + +void GLSDLDevice::Init(GLSDLWindow* window, const char* debugName, uint32_t flags) { + if (this->m_Init) { + return; + } + + this->m_BatchViewerEnabled = flags & 0x1; + this->m_UseWindowSystemBuffer = flags & (0x2 | 0x4); + this->m_FlippedSystemBuffer = flags & 0x4; + this->m_ShaderCompiler = flags & 0x8; + this->m_WorkerDevice = flags & 0x10; + + this->m_DebugName.assign(debugName, strlen(debugName)); + + this->m_Window = window; + + // *** sets + // this->m_UseWindowSystemBuffer = false; + // *** + + GLSDLDevice::Set(this); + + this->m_Context.Create(window); + this->m_Context.MakeCurrent(window); + + // TODO + // if (dword_10329E4 == dword_10329E0) + // GLRendererInfo::InitExtensions(); + + this->LoadDefaultStates(); + + this->m_Init = true; + + if (this->m_ID == -1) { + this->m_ID = GLSDLDevice::m_Devices.size(); + GLSDLDevice::m_Devices.insert(GLSDLDevice::m_Devices.end(), this); + } else { + GLSDLDevice::m_Devices[this->m_ID] = this; + } + + if (!GLSDLDevice::m_StaticResourcesRefCount) { + // GLContext::s_MainContext = this->m_Context.m_Context->context; + GLSDLDevice::InitPools(); + GLSDLDevice::StaticInit(); + } + + ++GLSDLDevice::m_StaticResourcesRefCount; + + // if (!this->m_WorkerDevice) { + // GLWorker* v11 = new GLWorker(this); + // this->m_TexWorker = v11; + // } + + glBindFramebufferEXT(GL_FRAMEBUFFER, 0); + glClear(GL_COLOR_BUFFER_BIT); + + GLFramebuffer* v12 = GLFramebuffer::Create(0); + this->m_FBOTarget = v12; + GLFramebuffer* currentTarget = v12; + + // TODO verify this check + if (this->m_UseWindowSystemBuffer) { + GLFramebuffer* v14 = GLFramebuffer::Create(1); + this->m_SystemTarget = v14; + currentTarget = v14; + } + + this->m_CurrentTarget = currentTarget; + + GLuint v15 = 0; + + if (currentTarget) { + v15 = currentTarget->m_FramebufferID; + } + + if (this->m_States.binding.framebuffer != v15) { + glBindFramebufferEXT(GL_FRAMEBUFFER, v15); + this->m_States.binding.framebuffer = v15; + } +} + +void GLSDLDevice::LoadDefaultStates() { + // Depth + + this->m_States.depth.testEnable = false; + this->m_States.depth.compareFunc = GL_GEQUAL; + this->m_States.depth.writeMask = true; + + // Stencil + + this->m_States.stencil.testEnable = false; + this->m_States.stencil.ref = 0; + this->m_States.stencil.mask = 0xFFFFFFFF; + this->m_States.stencil.writeMask = 0xFFFFFFFF; + this->m_States.stencil.useTwoSidedStencil = false; + + this->m_States.stencil.front.compareFunc = GL_ALWAYS; + this->m_States.stencil.front.opFail = GL_KEEP; + this->m_States.stencil.front.opZFail = GL_KEEP; + this->m_States.stencil.front.opZPass = GL_KEEP; + + this->m_States.stencil.back.compareFunc = GL_ALWAYS; + this->m_States.stencil.back.opFail = GL_KEEP; + this->m_States.stencil.back.opZFail = GL_KEEP; + this->m_States.stencil.back.opZPass = GL_KEEP; + + // Rasterizer + + this->m_States.rasterizer.cullMode = GL_CCW; + this->m_States.rasterizer.cullFaceMode = GL_BACK; + this->m_States.rasterizer.fillMode = GL_FILL; + this->m_States.rasterizer.constantDepthBias = 0.0; + this->m_States.rasterizer.slopeScaledDepthBias = 0.0; + + this->m_States.rasterizer.viewport = { + 0, + 0, + this->m_Window->GetWidth(), + this->m_Window->GetHeight() + }; + + this->m_States.rasterizer.zNear = 0.0; + this->m_States.rasterizer.zFar = 1.0; + this->m_States.rasterizer.scissorEnable = false; + + this->m_States.rasterizer.scissor = { + 0, + 0, + this->m_Window->GetWidth(), + this->m_Window->GetHeight() + }; + + this->m_States.rasterizer.clipPlaneMask = 0; + memset(this->m_States.rasterizer.clipPlanes, 0, sizeof(this->m_States.rasterizer.clipPlanes)); + + // Blend + + for (int32_t i = 0; i < 4; ++i) { + this->m_States.blend.colorMask[i] = { true, true, true, true }; + } + + this->m_States.blend.alphaBlend = 0; + this->m_States.blend.srcBlendFactor = 1; + this->m_States.blend.destBlendFactor = 0; + this->m_States.blend.blendOp = GL_FUNC_ADD; + this->m_States.blend.blendColor = GLColor4f::ZERO; + + // FixedFunc + + this->m_States.fixedFunc.fogColor = { 0.0, 0.0, 0.0, 0.0 }; + this->m_States.fixedFunc.fogStart = 0.0; + this->m_States.fixedFunc.alphaTestRef = 0.0; + this->m_States.fixedFunc.fogEnable = 0; + this->m_States.fixedFunc.fogMode = GL_LINEAR; + this->m_States.fixedFunc.fogEnd = 1.0; + this->m_States.fixedFunc.fogDensity = 1.0; + this->m_States.fixedFunc.alphaTestEnable = 0; + this->m_States.fixedFunc.alphaTestFunc = GL_ALWAYS; + + for (int32_t i = 0; i < 8; ++i) { + this->m_States.fixedFunc.texOp[i].texturing = 0; + this->m_States.fixedFunc.texOp[i].constant = GLColor4f::ZERO; + this->m_States.fixedFunc.texOp[i].colorOp = GL_MODULATE; + this->m_States.fixedFunc.texOp[i].colorScale = 1.0; + this->m_States.fixedFunc.texOp[i].colorArg0 = GL_TEXTURE; + this->m_States.fixedFunc.texOp[i].colorArg1 = GL_PREVIOUS; + this->m_States.fixedFunc.texOp[i].colorArg2 = GL_CONSTANT; + this->m_States.fixedFunc.texOp[i].alphaOp = GL_MODULATE; + this->m_States.fixedFunc.texOp[i].alphaScale = 1.0; + this->m_States.fixedFunc.texOp[i].alphaArg0 = GL_TEXTURE; + this->m_States.fixedFunc.texOp[i].alphaArg1 = GL_PREVIOUS; + this->m_States.fixedFunc.texOp[i].alphaArg2 = GL_CONSTANT; + } + + this->m_States.fixedFunc.lighting.enable = true; + this->m_States.fixedFunc.lighting.sceneAmbient = { 0.0, 0.0, 0.0, 0.0 }; + + for (int32_t i = 0; i < 8; ++i) { + this->m_States.fixedFunc.lighting.lights[i].enable = false; + this->m_States.fixedFunc.lighting.lights[i].position = { 0.0, 0.0, 1.0, 0.0 }; + this->m_States.fixedFunc.lighting.lights[i].view.isDirty = true; + this->m_States.fixedFunc.lighting.lights[i].view.isIdentity = true; + this->m_States.fixedFunc.lighting.lights[i].constantAttenuation = 0.0; + this->m_States.fixedFunc.lighting.lights[i].linearAttenuation = 0.0; + this->m_States.fixedFunc.lighting.lights[i].quadraticAttenuation = 0.0; + this->m_States.fixedFunc.lighting.lights[i].ambient = GLColor4f::ZERO; + this->m_States.fixedFunc.lighting.lights[i].diffuse = GLColor4f::WHITE; + this->m_States.fixedFunc.lighting.lights[i].specular = GLColor4f::ZERO; + } + + this->m_States.fixedFunc.lighting.lights[0].enable = true; + + this->m_States.fixedFunc.lighting.material.materialSource = 4609; + this->m_States.fixedFunc.lighting.material.ambient = GLColor4f::ZERO; + this->m_States.fixedFunc.lighting.material.diffuse = GLColor4f::WHITE; + this->m_States.fixedFunc.lighting.material.specular = GLColor4f::ZERO; + this->m_States.fixedFunc.lighting.material.shininess = 0.0; + this->m_States.fixedFunc.lighting.material.emission = GLColor4f::ZERO; + + this->m_States.fixedFunc.transforms.matrixMode = 5888; + this->m_States.fixedFunc.transforms.modelviewStatus = 5888; + this->m_States.fixedFunc.transforms.modelView.isIdentity = true; + this->m_States.fixedFunc.transforms.modelView.isDirty = true; + this->m_States.fixedFunc.transforms.world.isIdentity = true; + this->m_States.fixedFunc.transforms.world.isDirty = true; + this->m_States.fixedFunc.transforms.view.isIdentity = true; + this->m_States.fixedFunc.transforms.view.isDirty = true; + this->m_States.fixedFunc.transforms.projection.isIdentity = true; + this->m_States.fixedFunc.transforms.projection.isDirty = true; + + for (int32_t i = 0; i < 8; ++i) { + this->m_States.fixedFunc.transforms.texture[i].isIdentity = true; + this->m_States.fixedFunc.transforms.texture[i].isDirty = true; + } + + for (int32_t i = 0; i < 8; ++i) { + this->m_States.fixedFunc.texCoordIndex[i] = i; + } + + for (int32_t i = 0; i < 8; ++i) { + this->m_States.fixedFunc.texGen[i].S = 0; + this->m_States.fixedFunc.texGen[i].T = 0; + this->m_States.fixedFunc.texGen[i].R = 0; + this->m_States.fixedFunc.texGen[i].Q = 0; + } + + this->m_States.fixedFunc.pointSprite.enable = false; + this->m_States.fixedFunc.pointSprite.size = 1.0; + this->m_States.fixedFunc.pointSprite.attenuation[0] = 1.0; + this->m_States.fixedFunc.pointSprite.attenuation[1] = 0.0; + this->m_States.fixedFunc.pointSprite.attenuation[2] = 0.0; + this->m_States.fixedFunc.pointSprite.min = 0.0; + this->m_States.fixedFunc.pointSprite.max = 1.0; + + this->m_States.fixedFunc.normalizeNormal = false; + + // Samplers + + for (int32_t i = 0; i < 16; ++i) { + this->m_States.samplers[i].mipmapBias = 0.0; + this->m_States.samplers[i].addressModeS = 10497; + this->m_States.samplers[i].addressModeT = 10497; + this->m_States.samplers[i].addressModeR = 10497; + this->m_States.samplers[i].magFilterMode = 9729; + this->m_States.samplers[i].minFilterMode = 9729; + this->m_States.samplers[i].maxAnisotropy = 1.0; + this->m_States.samplers[i].borderColor = { 0.0, 0.0, 0.0, 0.0 }; + } + + // Shader + + this->m_States.shader.vertexShaderEnable = false; + + for (int32_t i = 0; i < 256; ++i) { + this->m_States.shader.vertexShaderConst[i] = { 0.0, 0.0, 0.0, 1.0 }; + } + + this->m_States.shader.pixelShaderEnable = false; + + for (int32_t i = 0; i < 64; ++i) { + this->m_States.shader.pixelShaderConst[i] = { 0.0, 0.0, 0.0, 1.0 }; + } + + // Binding + + memset(&this->m_States.binding, 0, sizeof(this->m_States.binding)); + + // Clear + + this->m_States.clear.clearColor = GLColor4f::ZERO; + this->m_States.clear.clearDepth = 1.0; + this->m_States.clear.clearStencil = 0; + + // Misc + + this->m_States.misc.drawBuffers[0] = 0; + this->m_States.misc.drawBuffers[1] = 0; + this->m_States.misc.drawBuffers[2] = 0; + this->m_States.misc.drawBuffers[3] = 0; + this->m_States.misc.readBuffer = 0; + + // Assign default states + memcpy(&this->m_DefaultStates.depth, &this->m_States.depth, sizeof(this->m_DefaultStates.depth)); + memcpy(&this->m_DefaultStates.stencil, &this->m_States.stencil, sizeof(this->m_DefaultStates.stencil)); + memcpy(&this->m_DefaultStates.rasterizer, &this->m_States.rasterizer, sizeof(this->m_DefaultStates.rasterizer)); + memcpy(&this->m_DefaultStates.blend, &this->m_States.blend, sizeof(this->m_DefaultStates.blend)); + memcpy(&this->m_DefaultStates.clear, &this->m_States.clear, sizeof(this->m_DefaultStates.clear)); + memcpy(&this->m_DefaultStates.fixedFunc, &this->m_States.fixedFunc, sizeof(this->m_DefaultStates.fixedFunc)); + memcpy(this->m_DefaultStates.samplers, this->m_States.samplers, sizeof(this->m_DefaultStates.samplers)); + memcpy(&this->m_DefaultStates.shader, &this->m_States.shader, sizeof(this->m_DefaultStates.shader)); + memcpy(&this->m_DefaultStates.binding, &this->m_States.binding, sizeof(this->m_DefaultStates.binding)); + memcpy(&this->m_DefaultStates.misc, &this->m_States.misc, sizeof(this->m_DefaultStates.misc)); + + // TODO + // if (v54 != this->m_States.misc.unpackClientStorage) { + // glPixelStorei(34226, v54); + // this->m_States.misc.unpackClientStorage = v54; + // } + + this->ApplyGLBindings(this->m_States, 1); + this->ApplyGLStates(this->m_States, 1); +} + +void GLSDLDevice::ResetBackbuffer(uint32_t width, uint32_t height, GLTextureFormat colorFormat, GLTextureFormat depthFormat, uint32_t sampleCount) { + BLIZZARD_ASSERT(this->m_Context.IsCurrentContext()); + + if ( + this->m_BackBufferColor + && this->m_BackBufferColor->m_Width == width + && this->m_BackBufferColor->m_Height == height + && this->m_BackBufferColor->m_Format == colorFormat + && this->m_BackBufferDepth + && this->m_BackBufferDepth->m_Format == depthFormat + ) { + if (this->m_UseWindowSystemBuffer && this->m_CurrentTarget->GetSampleCount() != sampleCount) { + // this->m_Context.SetContextFormat(depthFormat, sampleCount); + } + + return; + } + + auto v24 = this->m_BackBufferColor != 0; + + if (this->m_BackBufferColor) { + this->m_BackBufferColor->Release(); + this->m_BackBufferColor = nullptr; + } + + if (this->m_BackBufferStencil) { + this->m_BackBufferStencil->Release(); + this->m_BackBufferStencil = nullptr; + } + + if (this->m_BackBufferDepth) { + this->m_BackBufferDepth->Release(); + this->m_BackBufferDepth = nullptr; + } + + GLTextureFormat v10 = this->m_UseWindowSystemBuffer ? depthFormat : GLTF_INVALID; + // this->m_Context.SetContextFormat(v10, sampleCount); + + this->BindFramebuffer( + this->m_UseWindowSystemBuffer + ? this->m_SystemTarget + : this->m_FBOTarget + ); + + if (this->m_UseWindowSystemBuffer && !v24) { + this->Clear(0x4500, GLColor4f::BLACK, 1.0, 0); + this->m_Window->Swap(); + } + + uint32_t v13 = GLTFLAG_SYSTEM_BUFFER; + if (!this->m_UseWindowSystemBuffer) { + v13 = 0; + } + + uint32_t v14 = GLTFLAG_RENDERTARGET; + if (!this->m_FlippedSystemBuffer) { + v14 = v13 | GLTFLAG_RENDERTARGET; + } + + auto v15 = GLTexture2D::Create(width, height, 1, colorFormat, v14); + this->m_BackBufferColor = v15; + auto v16 = v15->GetMipmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + this->m_CurrentTargetColor[0] = v16; + this->m_CurrentTarget->Attach(v16, GL_COLOR_ATTACHMENT0, 0); + + GLRect v29 = { 0, 0, static_cast(width), static_cast(height) }; + this->SetViewport(v29, 0.0, 1.0); + + if (this->m_FlippedSystemBuffer) { + auto v19 = this->m_BackBufferColor->GetMipmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + GLRect v25 = { 0, 0, v19->m_Width, v19->m_Height }; + this->CopyTex(0, 0, v19, &v25); + } + + if (depthFormat) { + if (depthFormat == GLTF_D24S8) { + auto v20 = GLTexture2D::Create(width, height, 1, GLTF_D24S8, v13 | GLTFLAG_RENDERTARGET | GLTFLAG_DEPTH | GLTFLAG_STENCIL); + this->m_BackBufferDepth = v20; + this->m_BackBufferStencil = v20; + + // TODO how to properly increment GLObject ref counts? + v20->AddRefTwin(); + v20->m_RefCount++; + + auto v22 = this->m_BackBufferStencil->GetMipmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + this->m_CurrentTargetStencil = v22; + this->m_CurrentTarget->Attach(v22, GL_STENCIL_ATTACHMENT, 0); + } else { + this->m_BackBufferDepth = GLTexture2D::Create(width, height, 1, depthFormat, v13 | GLTFLAG_RENDERTARGET | GLTFLAG_DEPTH); + } + + auto v17 = this->m_BackBufferDepth->GetMipmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + this->m_CurrentTargetDepth = v17; + this->m_CurrentTarget->Attach(v17, GL_DEPTH_ATTACHMENT, 0); + this->m_CurrentDepthBuffer = this->m_CurrentTargetDepth; + this->SetDepthTestEnable(1); + } + + BLIZZARD_ASSERT(this->m_CurrentTarget->IsValid()); + + this->Clear(0x4500, GLColor4f::BLACK, 1.0, 0); +} + +void GLSDLDevice::Resize(uint32_t width, uint32_t height) { + auto colorFormat = this->m_BackBufferColor ? this->m_BackBufferColor->m_Format : GLTF_INVALID; + auto depthFormat = this->m_BackBufferDepth ? this->m_BackBufferDepth->m_Format : GLTF_INVALID; + + this->SetDisplay( + width, + height, + colorFormat, + depthFormat, + 0, + false, + false, + this->m_Context.GetSampleCount() + ); +} + +void GLSDLDevice::RestoreTextures() { + BLIZZARD_ASSERT(this->m_Context.IsCurrentContext()); + + for (int32_t i = 0; i < 16; i++) { + GLTexture* texture = this->m_Textures[i]; + + if (texture && !texture->IsValid()) { + this->m_Textures[i] = nullptr; + } + + this->SetTexture(i, this->m_Textures[i]); + } +} + +void GLSDLDevice::SetActiveTexture(uint32_t a2) { + if (this->m_States.binding.currentActiveTexture != a2) { + glActiveTexture(GL_TEXTURE0 + a2); + this->m_States.binding.currentActiveTexture = a2; + } +} + +void GLSDLDevice::SetAlphaBlend(GLEnum srcBlend, GLEnum dstBlend, GLEnum blendOp) { + if (this->m_States.blend.srcBlendFactor != srcBlend || this->m_States.blend.destBlendFactor != dstBlend) { + glBlendFunc(srcBlend, dstBlend); + this->m_States.blend.srcBlendFactor = srcBlend; + this->m_States.blend.destBlendFactor = dstBlend; + } + + if (this->m_States.blend.blendOp != blendOp) { + glBlendEquation(blendOp); + this->m_States.blend.blendOp = blendOp; + } +} + +void GLSDLDevice::SetAlphaBlendEnable(bool enable) { + if (this->m_States.blend.alphaBlend != enable) { + if (enable) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + + this->m_States.blend.alphaBlend = enable; + } +} + +void GLSDLDevice::SetAlphaTest(GLEnum func, float ref) { + if (this->m_States.fixedFunc.alphaTestFunc != func || this->m_States.fixedFunc.alphaTestRef != ref) { + BLIZZARD_ASSERT(ref <= 1.0f); + + glAlphaFunc(func, ref); + this->m_States.fixedFunc.alphaTestFunc = func; + this->m_States.fixedFunc.alphaTestRef = ref; + } +} + +void GLSDLDevice::SetAlphaTestEnable(bool enable) { + if (this->m_States.fixedFunc.alphaTestEnable != enable) { + if (enable) { + glEnable(GL_ALPHA_TEST); + } else { + glDisable(GL_ALPHA_TEST); + } + } +} + +void GLSDLDevice::SetClearColor(const GLColor4f& clearColor) { + if ( + this->m_States.clear.clearColor.r != clearColor.r + || this->m_States.clear.clearColor.g != clearColor.g + || this->m_States.clear.clearColor.b != clearColor.b + || this->m_States.clear.clearColor.a != clearColor.a + ) { + glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + this->m_States.clear.clearColor = { clearColor.r, clearColor.g, clearColor.b, clearColor.a }; + } +} + +void GLSDLDevice::SetClearDepth(double clearDepth) { + if (this->m_States.clear.clearDepth != clearDepth) { + glClearDepth(clearDepth); + this->m_States.clear.clearDepth = clearDepth; + } +} + +void GLSDLDevice::SetClearStencil(int32_t clearStencil) { + if (this->m_States.clear.clearStencil != clearStencil) { + glClearStencil(clearStencil); + this->m_States.clear.clearStencil = clearStencil; + } +} + +void GLSDLDevice::SetColorWriteMask(bool red, bool green, bool blue, bool alpha, uint32_t index) { + if ( + this->m_States.blend.colorMask[index].red != red + || this->m_States.blend.colorMask[index].green != green + || this->m_States.blend.colorMask[index].blue != blue + || this->m_States.blend.colorMask[index].alpha != alpha + ) { + if (GLSDLDevice::m_ExtColorMaskIndexed) { + glColorMaskIndexedEXT(index, red, green, blue, alpha); + } else if (index == 0) { + glColorMask(red, green, blue, alpha); + } + + this->m_States.blend.colorMask[index].red = red; + this->m_States.blend.colorMask[index].green = green; + this->m_States.blend.colorMask[index].blue = blue; + this->m_States.blend.colorMask[index].alpha = alpha; + } +} + +void GLSDLDevice::SetCullMode(GLEnum cullMode) { + if (this->m_States.rasterizer.cullMode != cullMode) { + if (cullMode) { + if (cullMode - GL_CW <= 1) { + glEnable(GL_CULL_FACE); + glFrontFace(cullMode); + } + + this->m_States.rasterizer.cullMode = cullMode; + } else { + glDisable(GL_CULL_FACE); + this->m_States.rasterizer.cullMode = 0; + } + } +} + +void GLSDLDevice::SetDepthBias(float constantBias, float slopeScaledBias) { + // TODO +} + +void GLSDLDevice::SetDepthTestEnable(bool enable) { + if (this->m_States.depth.testEnable != enable) { + if (enable) { + glEnable(GL_DEPTH_TEST); + } else { + glDisable(GL_DEPTH_TEST); + } + + this->m_States.depth.testEnable = enable; + } +} + +void GLSDLDevice::SetDepthTestFunc(GLEnum func) { + if (this->m_States.depth.compareFunc != func) { + glDepthFunc(func); + this->m_States.depth.compareFunc = func; + } +} + +void GLSDLDevice::SetDepthWriteMask(bool enable) { + if (this->m_States.depth.writeMask != enable) { + glDepthMask(enable); + this->m_States.depth.writeMask = enable; + } +} + +void GLSDLDevice::SetDisplay(uint32_t width, uint32_t height, GLTextureFormat colorFormat, GLTextureFormat depthFormat, uint32_t refreshRate, bool windowed, bool captureDisplay, uint32_t sampleCount) { + // uint32_t v9 = a9; + + // if (this->m_PBOPool) { + // // TODO + // // this->m_PBOPool->Flush(1); + // } + + // if (a9 > 7) { + // uint32_t v10; + + // if (a7) { + // // CGDirectDisplayID mainDisplay = CGMainDisplayID(); + // // CGRect bounds = CGDisplayBounds(mainDisplay); + // // CGFloat boundsWidth = CGRectGetWidth(bounds); + // // CGFloat boundsHeight = CGRectGetHeight(bounds); + + // // v10 = std::floor(boundsWidth) * std::floor(boundsHeight); + // } else { + // v10 = width * height; + // } + + // if (v10 >= 2304001) { + // v9 = 6; + // } + // } + + // if (this->m_Context.m_Window) { + // // TODO + // // - callback related... set to default callbacks? + // // (this->m_Context.m_Window + 84)(); + // } + + // if (a7) { + // if (this->m_Context.m_Window) { + // this->m_Context.m_Window->Resize(width, height); + // } + + // this->m_Context.SetWindow(this->m_Context.m_Window, 0); + // } else { + // this->m_Context.SetFullscreenMode(width, height, a6, a8); + // } + + GLSDLWindowRect newRect; + newRect.size.width = width; + newRect.size.height = height; + + this->m_Window->Resize(newRect); + + this->ResetBackbuffer(width, height, colorFormat, depthFormat, sampleCount); + + // if (this->m_Context.m_Window) { + // // TODO + // // - set active callbacks to callbacks + // // (this->m_Context.m_Window + 88)(); + // } +} + +void GLSDLDevice::SetFogColor(float r, float g, float b, float a) { + if ( + this->m_States.fixedFunc.fogColor.r != r + || this->m_States.fixedFunc.fogColor.g != g + || this->m_States.fixedFunc.fogColor.b != b + || this->m_States.fixedFunc.fogColor.a != a + ) { + this->m_States.fixedFunc.fogColor.r = r; + this->m_States.fixedFunc.fogColor.g = g; + this->m_States.fixedFunc.fogColor.b = b; + this->m_States.fixedFunc.fogColor.a = a; + + glFogfv(GL_FOG_COLOR, reinterpret_cast(&this->m_States.fixedFunc.fogColor)); + + // TODO logic related to a renderer info value + } +} + +void GLSDLDevice::SetFogEnable(bool enable) { + if (this->m_States.fixedFunc.fogEnable != enable) { + if (enable) { + glEnable(GL_FOG); + } else { + glDisable(GL_FOG); + } + + this->m_States.fixedFunc.fogEnable = enable; + } +} + +void GLSDLDevice::SetFogParam(GLEnum param, float value) { + if (param == GL_FOG_START) { + if (this->m_States.fixedFunc.fogStart != value) { + glFogf(GL_FOG_START, value); + this->m_States.fixedFunc.fogStart = value; + } + } else if (param == GL_FOG_END) { + if (this->m_States.fixedFunc.fogEnd != value) { + glFogf(GL_FOG_END, value); + this->m_States.fixedFunc.fogEnd = value; + } + } else if (param == GL_FOG_DENSITY) { + if (this->m_States.fixedFunc.fogDensity != value) { + glFogf(GL_FOG_DENSITY, value); + this->m_States.fixedFunc.fogDensity = value; + } + } else { + BLIZZARD_ASSERT(false); + } +} + +void GLSDLDevice::SetIndexBuffer(GLBuffer* buffer) { + BLIZZARD_ASSERT(buffer == nullptr || buffer->m_IndexFormat != GL_ZERO); + this->m_DefaultVertexArrayObject.m_Properties.m_IndexBuffer = buffer; +} + +void GLSDLDevice::SetLightingEnable(bool enable) { + if (this->m_States.fixedFunc.lighting.enable != enable) { + if (enable) { + glEnable(GL_LIGHTING); + } else { + glDisable(GL_LIGHTING); + } + + this->m_States.fixedFunc.lighting.enable = enable; + } +} + +void GLSDLDevice::SetModelView(GLEnum transform) { + if (transform == 'VIEW') { + // TODO + return; + } + + if (transform == 'WRLD') { + // TODO + return; + } + + if (transform != GL_MODELVIEW) { + BLIZZARD_ASSERT(false); + } + + auto& world = this->m_States.fixedFunc.transforms.world; + auto& view = this->m_States.fixedFunc.transforms.view; + auto& modelView = this->m_States.fixedFunc.transforms.modelView; + + if (this->m_States.fixedFunc.transforms.modelviewStatus != transform || modelView.isDirty) { + if (world.isIdentity && view.isIdentity) { + modelView.isIdentity = true; + modelView.isDirty = true; + } else if (world.isIdentity) { + modelView = view; + modelView.isIdentity = false; + modelView.isDirty = true; + } else if (view.isIdentity) { + modelView = world; + modelView.isIdentity = false; + modelView.isDirty = true; + } else { + // TODO assign model * view to modelView + BLIZZARD_ASSERT(!"Unimplemented"); + } + + if (this->m_States.fixedFunc.transforms.matrixMode != GL_MODELVIEW) { + glMatrixMode(GL_MODELVIEW); + this->m_States.fixedFunc.transforms.matrixMode = GL_MODELVIEW; + } + + if (modelView.isIdentity) { + glLoadIdentity(); + } else { + glLoadMatrixf(modelView.m); + } + + this->m_States.fixedFunc.transforms.modelviewStatus = transform; + } +} + +void GLSDLDevice::SetScissor(bool a2, const GLRect& a3) { + // TODO +} + +void GLSDLDevice::SetShader(GLShader::ShaderType shaderType, GLShader* shader) { + if (shader) { + if (shader->var18) { + // TODO + } + + BLIZZARD_ASSERT(shader->IsEnabled()); + BLIZZARD_ASSERT(shader->GetShaderType() == shaderType); + + this->BindShader(shader); + } + + int32_t enable = shader != nullptr; + + if (shaderType == GLShader::eVertexShader) { + if (this->m_States.shader.vertexShaderEnable != enable) { + if (enable) { + glEnable(GL_VERTEX_PROGRAM_ARB); + } else { + glDisable(GL_VERTEX_PROGRAM_ARB); + } + + this->m_States.shader.vertexShaderEnable = enable; + } + + this->m_VertexShader = shader; + } else if (shaderType == GLShader::ePixelShader) { + if (this->m_States.shader.pixelShaderEnable != enable) { + if (enable) { + glEnable(GL_FRAGMENT_PROGRAM_ARB); + } else { + glDisable(GL_FRAGMENT_PROGRAM_ARB); + } + + this->m_States.shader.pixelShaderEnable = enable; + } + + this->m_PixelShader = shader; + } else { + BLIZZARD_ASSERT(!"Unknown shader type!"); + } +} + +void GLSDLDevice::SetShaderConstants(GLShader::ShaderType shaderType, uint32_t index, const float* constants, uint32_t count) { + BLIZZARD_ASSERT(count != 0); + + GLShader* shader = nullptr; + + if (shaderType == GLShader::eVertexShader) { + shader = this->m_VertexShader; + } else if (shaderType == GLShader::ePixelShader) { + shader = this->m_PixelShader; + } + + if (GLSDLDevice::m_ShaderConstantBindings) { + if (!shader) { + return; + } + + if (shader && shader->var10) { + shader->SetShaderConstants(shaderType, index, constants, count); + return; + } + } + + this->SetShaderConstantsInternal(shaderType, index, constants, count); +} + +void GLSDLDevice::SetShaderConstantsInternal(GLShader::ShaderType shaderType, uint32_t index, const float* constants, uint32_t count) { + if (shaderType == GLShader::eVertexShader) { + BLIZZARD_ASSERT((index + count) <= std::extentm_States.shader.vertexShaderConst)>::value); + + memcpy(&this->m_States.shader.vertexShaderConst[index], constants, (sizeof(float) * 4) * count); + + BLIZZARD_ASSERT(index <= 0xFFFF); + BLIZZARD_ASSERT(count <= 0xFFFF); + + uint16_t start = std::min(static_cast(index), this->m_DirtyVertexShaderConsts.start); + uint16_t end = std::max(static_cast(index + count), this->m_DirtyVertexShaderConsts.end); + + this->m_DirtyVertexShaderConsts.start = start; + this->m_DirtyVertexShaderConsts.end = end; + } else if (shaderType == GLShader::ePixelShader) { + BLIZZARD_ASSERT((index + count) <= std::extentm_States.shader.pixelShaderConst)>::value); + + memcpy(&this->m_States.shader.pixelShaderConst[index], constants, (sizeof(float) * 4) * count); + + BLIZZARD_ASSERT(index <= 0xFFFF); + BLIZZARD_ASSERT(count <= 0xFFFF); + + uint16_t start = std::min(static_cast(index), this->m_DirtyPixelShaderConsts.start); + uint16_t end = std::max(static_cast(index + count), this->m_DirtyPixelShaderConsts.end); + + this->m_DirtyPixelShaderConsts.start = start; + this->m_DirtyPixelShaderConsts.end = end; + } else { + // TODO + // BLIZZARD_ASSERT(false, "Unknown shader type %d!", shaderType); + } +} + +void GLSDLDevice::SetTexture(uint32_t stage, GLTexture* texture) { + if (stage > 15) { + BLIZZARD_ASSERT(!"setting an unsupported texture stage to a non-NULL texture"); + } + + BLIZZARD_ASSERT(texture == nullptr || texture->IsValid()); + + uint32_t textureID = 0; + GLEnum textureType = GL_TEXTURE_2D; + + if (this->m_Textures[stage]) { + textureType = this->m_Textures[stage]->m_TextureType; + } + + if (texture) { + textureID = texture->m_TextureID; + textureType = texture->m_TextureType; + } + + uint32_t index = GLSDLTextureTypeToIndex(textureType); + + if (this->m_States.binding.texture[index][stage] != textureID) { + this->SetActiveTexture(stage); + + if (texture) { + texture->Bind(this, 1); + } else { + this->BindTexture(textureType, nullptr); + } + } + + this->m_Textures[stage] = texture; +} + +void GLSDLDevice::SetTransform(GLEnum transform, const float* a3) { + GLTransform* t; + + if (transform == 'VIEW') { + t = &this->m_States.fixedFunc.transforms.view; + } else if (transform == 'WRLD') { + t = &this->m_States.fixedFunc.transforms.world; + } else if (transform == GL_PROJECTION) { + t = &this->m_States.fixedFunc.transforms.projection; + } else if (transform >= GL_TEXTURE0 && transform <= GL_TEXTURE7) { + t = &this->m_States.fixedFunc.transforms.texture[transform - GL_TEXTURE0]; + } else { + BLIZZARD_ASSERT(false); + } + + if (*t != a3) { + t->Set(a3); + } + + if (t->isDirty) { + if (transform == 'VIEW' || transform == 'WRLD') { + this->m_States.fixedFunc.transforms.modelView.isDirty = true; + } + } +} + +void GLSDLDevice::SetUnpackClientStorage(bool enable) { + // TODO + // Blizzard::Debug::Assert(!this->IsUsingPBO() || enable == GL_FALSE); + + if (this->m_States.misc.unpackClientStorage != enable) { + // glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, enable); + this->m_States.misc.unpackClientStorage = enable; + } +} + +void GLSDLDevice::SetVertexBuffer(uint32_t index, GLBuffer* buffer, uint32_t offset, uint32_t stride) { + BLIZZARD_ASSERT(index < GL_MAX_STREAM); + BLIZZARD_ASSERT(buffer == nullptr || buffer->m_IndexFormat == GL_ZERO); + + auto properties = &this->m_DefaultVertexArrayObject.m_Properties; + properties->m_VertexBuffer[index] = buffer; + properties->m_VertexBufferOffset[index] = offset; + properties->m_VertexBufferStride[index] = stride; +} + +void GLSDLDevice::SetVertexFormat(GLVertexFormat* format) { + BLIZZARD_ASSERT(format->m_Size <= kMAX_VERTEX_ATTRIBS); + this->m_DefaultVertexArrayObject.m_Properties.m_VertexBufferFormat = format; +} + +void GLSDLDevice::SetViewport(const GLRect& viewport, double zNear, double zFar) { + if ( + this->m_States.rasterizer.viewport.left != viewport.left + || this->m_States.rasterizer.viewport.top != viewport.top + || this->m_States.rasterizer.viewport.width != viewport.width + || this->m_States.rasterizer.viewport.height != viewport.height + ) { + glViewport(viewport.left, viewport.top, viewport.width, viewport.height); + this->m_States.rasterizer.viewport = viewport; + } + + if ( + this->m_States.rasterizer.zNear != zNear + || this->m_States.rasterizer.zFar != zFar + ) { + glDepthRange(zNear, zFar); + this->m_States.rasterizer.zNear = zNear; + this->m_States.rasterizer.zFar = zFar; + } +} + +void GLSDLDevice::Sub34BB0(GLEnum a2, GLMipmap* a3, uint32_t index) { + if (!a3 || !this->m_UseWindowSystemBuffer) { + return; + } + + GLFramebuffer* target; + + if (a3->GetTexture() == this->m_BackBufferColor || a3->GetTexture() == this->m_BackBufferDepth || a3->GetTexture() == this->m_BackBufferStencil) { + if (this->m_CurrentTarget == this->m_SystemTarget) { + return; + } + + switch (a2) { + case GL_DEPTH_ATTACHMENT: + this->Sub34BB0(GL_DEPTH_ATTACHMENT, nullptr, 0); + this->m_CurrentTarget->Attach(nullptr, GL_DEPTH_ATTACHMENT, 0); + this->m_CurrentTargetDepth = nullptr; + this->m_CurrentDepthBuffer = nullptr; + break; + + case GL_STENCIL_ATTACHMENT: + this->Sub34BB0(GL_STENCIL_ATTACHMENT, nullptr, 0); + this->m_CurrentTarget->Attach(nullptr, GL_STENCIL_ATTACHMENT, 0); + this->m_CurrentTargetStencil = nullptr; + break; + + case GL_COLOR_ATTACHMENT0: + // TODO this->SetColorTarget(0, index); + break; + } + + target = this->m_SystemTarget; + } else { + target = this->m_FBOTarget; + } + + this->BindFramebuffer(target); +} + +void GLSDLDevice::Sub38460(bool a2) { + if (this->m_States.binding.framebuffer == 0) { + glDrawBuffer(GL_BACK); + this->m_States.misc.drawBuffers[0] = GL_BACK; + + glReadBuffer(GL_BACK); + this->m_States.misc.readBuffer = GL_BACK; + + return; + } + + if (a2) { + // TODO + + return; + } + + // TODO +} + +void GLSDLDevice::Swap() { + // if (this->m_Context.m_Window) { + // if (this->m_FlippedSystemBuffer) { + // GLRect rect = { + // 0, + // 0, + // static_cast(this->m_BackBufferColor->m_Width), + // static_cast(this->m_BackBufferColor->m_Height) + // }; + + // GLMipmap* image = this->m_BackBufferColor->GetMipmap(0, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + // this->CopyTex(0, 0, image, &rect); + // } + + // this->DrawRect(); + + // this->m_FrameNumber++; + // this->m_DrawCount = 0; + // } else { + // // glFlushRender(); + // this->m_DrawCount = 0; + // } + + this->DrawRect(); + this->m_FrameNumber++; + this->m_DrawCount = 0; +} + +void GLSDLDevice::UpdateFFPTexturing() { + // TODO +} diff --git a/src/gx/glsdl/GLSDLDevice.hpp b/src/gx/glsdl/GLSDLDevice.hpp new file mode 100644 index 0000000..fd7cd1f --- /dev/null +++ b/src/gx/glsdl/GLSDLDevice.hpp @@ -0,0 +1,190 @@ +#ifndef GX_GL_SDL_GL_SDL_DEVICE_HPP +#define GX_GL_SDL_GL_SDL_DEVICE_HPP + +#include "gx/glsdl/GL.hpp" +#include "gx/glsdl/GLSDLWindow.hpp" +#include "gx/glsdl/GLBatch.hpp" +#include "gx/glsdl/GLBufferPool.hpp" +#include "gx/glsdl/GLSDLContext.hpp" +#include "gx/glsdl/GLDebugMipmap2D.hpp" +#include "gx/glsdl/GLFramebuffer.hpp" +#include "gx/glsdl/GLGLSLProgram.hpp" +#include "gx/glsdl/GLMipmap.hpp" +#include "gx/glsdl/GLShader.hpp" +#include "gx/glsdl/GLTexture.hpp" +#include "gx/glsdl/GLTypes.hpp" +#include "gx/glsdl/GLVertexArray.hpp" +#include +#include +#include +#include +#include + +class GLSDLDevice { + public: + // Types + enum GLSDLDeviceOption { + eUseMTGL = 0, + eUseVertexArray = 1, + eUseGLSL = 2, + eCheckGLStates = 3, + eFlushBeforeDraw = 4, + eDeviceOption5 = 5, + eUseHybridShader = 6, + eDeviceOption7 = 7, + eDeviceOption8 = 8, + eShaderConstantBindings = 9 + }; + + struct RendererInfo { + uint8_t init = 0; + uint32_t vendor_id; + uint32_t renderer_id; + uint32_t max_color_attachments; + uint32_t unk36; // max clip planes + uint32_t unk100; + }; + + // Static variables + static Blizzard::Thread::TLSSlot m_CurrentDevice; + static std::vector m_Devices; + static bool m_ExtARBShadow; + static bool m_ExtColorMaskIndexed; + static RendererInfo m_RendererInfo; + static bool m_ShaderConstantBindings; + static int32_t m_StaticResourcesRefCount; + static bool m_UseHybridShader; + static GLBuffer* m_BlitQuadVBO; + static GLShader* m_DeviceShaders[]; + static GLTexture* m_DeviceTextures[]; + static GLVertexFormat m_NormalBlitVF; + static GLVertexFormat m_InvertedBlitVF; + static GLFramebuffer* m_F8330C; + + // Static functions + static GLSDLDevice* Get(); + static void Set(GLSDLDevice* device); + static void InitPools(); + static RendererInfo GetRendererInfo(); + static void InitRendererInfo(); + static void SetOption(GLSDLDeviceOption option, bool enable); + static void StaticInit(); + + // Member variables + std::string m_DebugName; + GLStates m_States; + GLTexture* m_Textures[16] = {}; + GLShader* m_PixelShader = nullptr; + GLShader* m_VertexShader = nullptr; + GLGLSLProgram* m_GLSLProgram = nullptr; + GLVertexArray* m_VertexArrayObject = &m_DefaultVertexArrayObject; + GLFramebuffer* m_SystemTarget = nullptr; + GLFramebuffer* m_FBOTarget = nullptr; + GLFramebuffer* m_CurrentTarget = nullptr; + GLMipmap* m_CurrentTargetColor[4] = {}; + GLMipmap* m_CurrentTargetDepth = nullptr; + GLMipmap* m_CurrentTargetStencil = nullptr; + GLMipmap* m_CurrentDepthBuffer = nullptr; + GLTexture2D* m_BackBufferColor = nullptr; + GLTexture2D* m_BackBufferDepth = nullptr; + GLTexture2D* m_BackBufferStencil = nullptr; + GLSDLWindow* m_Window = nullptr; + GLBufferPool* m_PBOPool = nullptr; + GLSDLContext m_Context; + GLTexture* m_BoundTextures[4][16] = {}; + GLVertexArray m_DefaultVertexArrayObject; + GLDirtyRange m_DirtyVertexShaderConsts; + GLDirtyRange m_DirtyPixelShaderConsts; + float m_ConstantDepthBias = 0.0f; + float m_SlopeScaledDepthBias = 0.0f; + bool m_Init = 0; + uint32_t m_DrawCount = 0; + uint32_t m_ID = -1; + std::list m_TextureList; + std::list::iterator m_OldestActiveTexture; + uint32_t m_TextureTotalSize = 0; + uint32_t m_FrameNumber = 1; + std::list m_DebugMipmaps; + bool m_BatchViewerEnabled = 0; + bool m_UseWindowSystemBuffer = 0; + bool m_FlippedSystemBuffer = 0; + bool m_ShaderCompiler = 0; + bool m_WorkerDevice; + GLStates m_DefaultStates; + std::vector* m_FrameBatches; + bool m_CaptureOnlyOneFrame = 0; + bool m_StopCapturingBatches = 0; + bool m_CaptureBatches = 0; + int32_t m_IndentLevel = 0; + + // Member functions + GLSDLDevice(); + void ApplyGLBindings(const GLStates& states, bool a3); + void ApplyGLStates(const GLStates& states, bool force); + void ApplyShaderConstants(); + void ApplyTransforms(); + void BindBuffer(GLBuffer* buffer, GLEnum target); + void BindFramebuffer(GLFramebuffer* framebuffer); + void BindGLSLProgram(GLGLSLProgram* a2); + void BindShader(GLShader* shader); + void BindTexture(GLEnum textureType, GLTexture* texture); + void BindVertexArray(GLVertexArray* a2); + void BlitFramebuffer(GLMipmap* src, const GLRect* srcRect, GLMipmap* dst, const GLRect* dstRect, GLEnum mask, GLEnum filter); + void CheckDepthTarget(); + void Clear(uint32_t clearMask, const GLColor4f& clearColor, double clearDepth, int32_t clearStencil); + void CopyTex(uint32_t a2, uint32_t a3, GLMipmap* dst, const GLRect* framebufferRect); + GLBuffer* CreateBuffer(GLEnum type, uint32_t a3, const void* a4, GLEnum usage, GLEnum format); + GLShader* CreateShader(GLShader::ShaderType type, const void* buf, int32_t codeLen, const char* name); + GLTexture* CreateTexture2D(uint32_t width, uint32_t height, uint32_t numMipMap, GLTextureFormat format, uint32_t flags); + GLTexture* CreateTextureCubeMap(uint32_t size, uint32_t numMipMap, GLTextureFormat format, uint32_t flags); + void Draw(GLEnum primitive, uint32_t a3, uint32_t a4); + void DrawIndexed(GLEnum primitive, uint32_t a3, uint32_t a4, uint32_t a5, uint32_t a6, uint32_t count); + void DrawRect(); + GLFramebuffer* GetCurrentTarget(); // invented name + uint32_t GetID(); + GLShader* GetShader(GLShader::ShaderType shaderType); + const GLStates::VertexArrayObject& GetVertexArrayStates(); + void GLSDLDraw(GLEnum mode, uint32_t start, uint32_t end, uint32_t a5, uint32_t a6, uint32_t count); + void Init(GLSDLWindow* a2, const char* a3, uint32_t a4); + void LoadDefaultStates(); + void ResetBackbuffer(uint32_t width, uint32_t height, GLTextureFormat colorFormat, GLTextureFormat depthFormat,uint32_t sampleCount); + void Resize(uint32_t width, uint32_t height); + void RestoreTextures(); + void SetActiveTexture(uint32_t a2); + void SetAlphaBlend(GLEnum srcBlend, GLEnum dstBlend, GLEnum blendOp); + void SetAlphaBlendEnable(bool enable); + void SetAlphaTest(GLEnum func, float ref); + void SetAlphaTestEnable(bool enable); + void SetClearColor(const GLColor4f& clearColor); + void SetClearDepth(double clearDepth); + void SetClearStencil(int32_t clearStencil); + void SetColorWriteMask(bool red, bool green, bool blue, bool alpha, uint32_t index); + void SetCullMode(GLEnum cullMode); + void SetDepthBias(float constantBias, float slopeScaledBias); + void SetDepthTestEnable(bool enable); + void SetDepthTestFunc(GLEnum func); + void SetDepthWriteMask(bool enable); + void SetDisplay(uint32_t width, uint32_t height, GLTextureFormat colorFormat, GLTextureFormat depthFormat, uint32_t refreshRate, bool windowed, bool captureDisplay, uint32_t sampleCount); + void SetFogColor(float r, float g, float b, float a); + void SetFogEnable(bool enable); + void SetFogParam(GLEnum param, float value); + void SetIndexBuffer(GLBuffer* buffer); + void SetLightingEnable(bool enable); + void SetModelView(GLEnum transform); + void SetScissor(bool a2, const GLRect& a3); + void SetShader(GLShader::ShaderType shaderType, GLShader* shader); + void SetShaderConstants(GLShader::ShaderType shaderType, uint32_t index, const float* constants, uint32_t count); + void SetShaderConstantsInternal(GLShader::ShaderType shaderType, uint32_t index, const float* constants, uint32_t count); + void SetTexture(uint32_t stage, GLTexture* texture); + void SetTransform(GLEnum transform, const float* a3); + void SetUnpackClientStorage(bool enable); + void SetVertexBuffer(uint32_t index, GLBuffer* buffer, uint32_t offset, uint32_t stride); + void SetVertexFormat(GLVertexFormat* format); + void SetViewport(const GLRect& viewport, double zNear, double zFar); + void Sub34BB0(GLEnum a2, GLMipmap* a3, uint32_t index); + void Sub38460(bool a2); + void Swap(); + void UpdateFFPTexturing(); +}; + +#endif diff --git a/src/gx/glsdl/GLSDLWindow.cpp b/src/gx/glsdl/GLSDLWindow.cpp new file mode 100644 index 0000000..2ac49af --- /dev/null +++ b/src/gx/glsdl/GLSDLWindow.cpp @@ -0,0 +1,131 @@ +#include "gx/glsdl/GLSDLWindow.hpp" + +#include + +static bool s_GLSDL_Initialized = false; + +void GLSDLWindow::Create(const char* title, const GLSDLWindowRect& rect, GLTextureFormat depthFormat, uint32_t sampleCount) { + BLIZZARD_ASSERT(this->m_sdlWindow == nullptr); + + if (!s_GLSDL_Initialized) { + // Initialize SDL video context + SDL_Init(SDL_INIT_VIDEO); + + // Set GL version profile + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + s_GLSDL_Initialized = true; + } + + // Set depth and stencil size according to format + uint32_t depthSize = 0; + uint32_t stencilSize = 0; + + switch (depthFormat) { + case GLTF_INVALID: + break; + case GLTF_D32: + depthSize = 32; + break; + case GLTF_D24: + depthSize = 24; + break; + case GLTF_D16: + depthSize = 16; + break; + case GLTF_D24S8: + depthSize = 24; + stencilSize = 8; + break; + default: + BLIZZARD_ASSERT(false); + break; + } + + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthSize); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilSize); + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + + // Set multisampling + if (sampleCount >= 1) { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, sampleCount); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + } + + this->m_sdlWindow = SDL_CreateWindow( + title, + static_cast(rect.size.width), static_cast(rect.size.height), + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE + ); + + BLIZZARD_ASSERT(this->m_sdlWindow != nullptr); +} + +void GLSDLWindow::Swap() { + SDL_GL_SwapWindow(this->m_sdlWindow); +} + +void GLSDLWindow::Destroy() { + SDL_DestroyWindow(this->m_sdlWindow); + this->m_sdlWindow = nullptr; +} + +GLSDLWindowRect GLSDLWindow::GetRect() { + // Default rectangle + GLSDLWindowRect rect; + + int origin_x = 0; + int origin_y = 0; + if (SDL_GetWindowPosition(this->m_sdlWindow, &origin_x, &origin_y) == 0) { + rect.origin.x = static_cast(origin_x); + rect.origin.y = static_cast(origin_y); + } + + int width = 0; + int height = 0; + if (SDL_GetWindowSize(this->m_sdlWindow, &width, &height) == 0) { + rect.size.width = static_cast(width); + rect.size.height = static_cast(height); + } + + return rect; +} + +GLSDLWindowRect GLSDLWindow::GetBackingRect() { + // Default rectangle + GLSDLWindowRect rect; + + // Query backing width/height + int width = 0; + int height = 0; + if (SDL_GetWindowSizeInPixels(this->m_sdlWindow, &width, &height) == 0) { + rect.size.width = static_cast(width); + rect.size.height = static_cast(height); + } + + return rect; +} + +void GLSDLWindow::Resize(const GLSDLWindowRect& rect) { + auto current = this->GetBackingRect(); + + if (current.size.width != rect.size.width || current.size.height != rect.size.width) { + auto status = SDL_SetWindowSize(this->m_sdlWindow, rect.size.width, rect.size.height); + BLIZZARD_ASSERT(status == 0); + } +} + +int32_t GLSDLWindow::GetWidth() { + return this->GetBackingRect().size.width; +} + +int32_t GLSDLWindow::GetHeight() { + return this->GetBackingRect().size.height; +} + diff --git a/src/gx/glsdl/GLSDLWindow.hpp b/src/gx/glsdl/GLSDLWindow.hpp new file mode 100644 index 0000000..40d84bb --- /dev/null +++ b/src/gx/glsdl/GLSDLWindow.hpp @@ -0,0 +1,44 @@ +#ifndef GX_GL_SDL_GL_SDL_WINDOW_HPP +#define GX_GL_SDL_GL_SDL_WINDOW_HPP + +#include +#include + +#include "gx/glsdl/GLTypes.hpp" + +class GLSDLWindowRect { + public: + struct Point { + int32_t x = 0; + int32_t y = 0; + }; + + struct Size { + int32_t width = 0; + int32_t height = 0; + }; + + Point origin; + Size size; +}; + +class GLSDLWindow { + public: + SDL_Window* m_sdlWindow = nullptr; + + // Create an SDL window with the requested OpenGL attributes + void Create(const char* title, const GLSDLWindowRect& rect, GLTextureFormat depthFormat, uint32_t sampleCount); + void Destroy(); + void Swap(); + void Resize(const GLSDLWindowRect& rect); + + GLSDLWindowRect GetRect(); + GLSDLWindowRect GetBackingRect(); + + int32_t GetWidth(); + int32_t GetHeight(); + + +}; + +#endif diff --git a/src/gx/glsdl/GLShader.cpp b/src/gx/glsdl/GLShader.cpp new file mode 100644 index 0000000..94fec2f --- /dev/null +++ b/src/gx/glsdl/GLShader.cpp @@ -0,0 +1,135 @@ +#include "gx/glsdl/GLShader.hpp" +#include "gx/glsdl/GLSDLDevice.hpp" +#include "gx/glsdl/GLPixelShader.hpp" +#include "gx/glsdl/GLPool.hpp" +#include "gx/glsdl/GLVertexShader.hpp" +#include "gx/glsdl/GL.hpp" +#include + +// TODO +// - threaded compiler support +// - glsl support +// - hybrid support +GLShader* GLShader::Create(ShaderType shaderType, bool hybrid, bool usingCG, const char* a4, const void* buf, int32_t codeLen, const char* a7, const char* name, GLShaderLogInfo* logInfo) { + const char* shaderCode = reinterpret_cast(buf); + + if (*reinterpret_cast(buf) == 'GSL1') { + BLIZZARD_ASSERT(!usingCG); + + const ShaderDataHeader header = *reinterpret_cast(buf); + + BLIZZARD_ASSERT(header.shaderType == shaderType); + BLIZZARD_ASSERT(header.size == codeLen); + BLIZZARD_ASSERT(header.codePos >= sizeof(ShaderDataHeader)); + BLIZZARD_ASSERT(header.codeSize > 0); + + shaderCode = &reinterpret_cast(buf)[header.codePos]; + } + + GLShader* shader = nullptr; + + if (shaderType == ePixelShader) { + shader = GLPixelShader::Create(); + } else if (shaderType == eVertexShader) { + shader = GLVertexShader::Create(); + } else { + // TODO + // sub_1C5E0(&v38, "Unknown shader type %d!", shaderType); + } + + shader->m_UsingCG = usingCG; + + if (usingCG) { + shader->CompileCG(a4, shaderCode, codeLen, a7, logInfo); + } else { + shader->m_Code.assign(shaderCode, codeLen); + + // TODO + // sub_5CD10(shader); + } + + return shader; +} + +bool GLShader::CheckErrorsARB(GLShaderLogInfo* logInfo) { + GLint errorPos; + + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); + const GLubyte* errorStr = glGetString(GL_PROGRAM_ERROR_STRING_ARB); + + // TODO + // Blizzard::Debug::VAssert(logInfo != 0 || errorPos == -1, errorStr); + + return errorPos == -1; +} + +void GLShader::Compile(GLShaderLogInfo* logInfo) { + this->ImmediateCompile(logInfo); +} + +void GLShader::CompileCG(const char* a2, const void* shaderCode, int32_t codeLen, const char* a5, GLShaderLogInfo* logInfo) { + // TODO +} + +void GLShader::FlushUniforms(GLGLSLProgram* program) { + // TODO +} + +std::string& GLShader::GetCode() { + return this->m_Code; +} + +int32_t GLShader::GetShaderType() { + return this->m_ShaderType; +} + +void GLShader::ImmediateCompile(GLShaderLogInfo* logInfo) { + BLIZZARD_ASSERT(!this->GetCode().empty()); + + this->m_Device = GLSDLDevice::Get(); + + if (!this->m_UsingGLSL) { + if (!this->m_ShaderID) { + if (this->m_ShaderType == eVertexShader) { + this->m_ShaderID = GLPool::Get()->GetNextName(); + } else { + this->m_ShaderID = GLPool::Get()->GetNextName(); + } + } + + this->m_Device->BindShader(this); + + const char* arbCode = this->GetCode().c_str(); + size_t arbLen = strlen(arbCode); + + glProgramStringARB(this->var5, GL_PROGRAM_FORMAT_ASCII_ARB, arbLen, arbCode); + + BLIZZARD_ASSERT(this->CheckErrorsARB(logInfo)); + } else { + // TODO + // - handle GLSL shaders + // - handle hybrid shaders + } + + if (logInfo) { + // TODO + // this->var20 = logInfo[0]; + } else { + this->var20 = 1; + } + + // TODO + // this->m_TimeStamp = Blizzard::Time::GetTimestamp(); +} + +bool GLShader::IsEnabled() { + return this->m_Enabled; +} + +void GLShader::ReleaseObject() { + // TODO +} + +void GLShader::SetShaderConstants(ShaderType shaderType, uint32_t index, const float* constants, uint32_t count) { + // TODO +} diff --git a/src/gx/glsdl/GLShader.hpp b/src/gx/glsdl/GLShader.hpp new file mode 100644 index 0000000..3ca970e --- /dev/null +++ b/src/gx/glsdl/GLShader.hpp @@ -0,0 +1,75 @@ +#ifndef GX_GL_SDL_GL_SHADER_HPP +#define GX_GL_SDL_GL_SHADER_HPP + +#include "gx/glsdl/GLObject.hpp" +#include "gx/glsdl/GLShaderInput.hpp" +#include +#include +#include + +class GLSDLDevice; +class GLGLSLProgram; +class GLShaderLogInfo; + +class GLShader : public GLObject { + public: + // Types + enum ShaderType { + eVertexShader = 1, + ePixelShader = 2, + eShaderTypeCount = 3 + }; + + struct ShaderDataHeader { + uint32_t signature; + uint32_t size; + ShaderType shaderType; + uint32_t codePos; + uint32_t codeSize; + uint32_t unk1; + uint32_t unk2; + uint32_t unk3; + }; + + // Static functions + static GLShader* Create(ShaderType, bool, bool, const char*, const void*, int32_t, const char*, const char*, GLShaderLogInfo*); + + // Member variables + int32_t m_ShaderType = 0; + int32_t var5 = 0; + uint32_t m_ShaderID = 0; + bool m_UsingCG = false; + bool m_UsingGLSL = false; + uint32_t m_UniformRegisterCount = 0; + GLShaderInput** var10 = nullptr; + float* var11 = nullptr; + bool var12 = false; + uint32_t var13 = 0; + uint32_t var14 = 0; + std::vector> var15; + std::vector> var16; + std::vector> var17; + GLShader* var18 = nullptr; + GLSDLDevice* m_Device = nullptr; + bool var20 = 0; + bool m_Enabled = true; + std::basic_string, std::allocator> m_Code; + std::basic_string, std::allocator> var23; + + // Virtual member functions + virtual void ReleaseObject(); + + // Member functions + bool CheckErrorsARB(GLShaderLogInfo*); + bool CheckErrorsGLSL(GLShaderLogInfo*); + void Compile(GLShaderLogInfo*); + void CompileCG(const char*, const void*, int32_t, const char*, GLShaderLogInfo*); + void FlushUniforms(GLGLSLProgram*); + std::string& GetCode(void); + int32_t GetShaderType(void); + void ImmediateCompile(GLShaderLogInfo*); + bool IsEnabled(void); + void SetShaderConstants(ShaderType, uint32_t, const float*, uint32_t); +}; + +#endif diff --git a/src/gx/glsdl/GLShaderInput.hpp b/src/gx/glsdl/GLShaderInput.hpp new file mode 100644 index 0000000..dca3947 --- /dev/null +++ b/src/gx/glsdl/GLShaderInput.hpp @@ -0,0 +1,33 @@ +#ifndef GX_GL_SDL_GL_SHADER_INPUT_HPP +#define GX_GL_SDL_GL_SHADER_INPUT_HPP + +#include + +class GLShader; + +class GLShaderInput { + public: + // Member variables + GLShader *m_Shader; + std::basic_string, std::allocator> m_Name; + std::basic_string, std::allocator> var2; + int16_t m_MapIndex; + int16_t var4; + int16_t var5; + int16_t m_UsedSize; + int16_t var7; + int16_t var8; + GLShaderInput *m_Parent; + int32_t var10; + int32_t var11; + int32_t var12; + int8_t m_ChildrenCount; + int8_t var14; + uint16_t var15; + int8_t var16; + int8_t m_Variability; + int8_t var18; + int8_t var19; +}; + +#endif diff --git a/src/gx/glsdl/GLTexture.cpp b/src/gx/glsdl/GLTexture.cpp new file mode 100644 index 0000000..6f0d9ff --- /dev/null +++ b/src/gx/glsdl/GLTexture.cpp @@ -0,0 +1,632 @@ +#include "gx/glsdl/GLTexture.hpp" +#include "gx/glsdl/GLSDLDevice.hpp" +#include "gx/glsdl/GLPool.hpp" +#include "gx/glsdl/GLUtil.hpp" +#include "gx/texture/CGxTex.hpp" +#include +#include +#include + +Blizzard::Thread::TLSSlot GLTexture::m_Bindings[4]; + +void* GLTexture::CreateBindings(void* ptr) { + return new std::deque>; +} + +void GLTexture::DestroyBindings(void* ptr) { + delete static_cast>*>(ptr); +} + +void GLTexture::Bind(GLSDLDevice* device, bool force) { + BLIZZARD_ASSERT(!this->IsSystemBuffer()); + BLIZZARD_ASSERT(this->m_Depth != 0); + + if (!device) { + device = GLSDLDevice::Get(); + } + + BLIZZARD_ASSERT(device != nullptr); + + auto& bindings = this->GetBindings(); + uint32_t deviceID = device->GetID(); + + if (deviceID >= bindings.size()) { + bindings.resize(deviceID + 1); + } + + bindings[deviceID].device = device; + + uint32_t currentActiveTexture = device->m_States.binding.currentActiveTexture; + + if (bindings[deviceID].boundStages[currentActiveTexture]) { + return; + } + + if (force) { + bindings[deviceID].boundStages[currentActiveTexture] = 1; + device->BindTexture(this->m_TextureType, this); + return; + } + + for (int32_t i = 0; i < 16; i++) { + if (bindings[deviceID].boundStages[i]) { + device->SetActiveTexture(i); + return; + } + } + + bindings[deviceID].boundStages[currentActiveTexture] = 1; + device->BindTexture(this->m_TextureType, this); +} + +void GLTexture::FreeTexture() { + auto device = GLSDLDevice::Get(); + + int32_t numFace = this->m_TextureType == GL_TEXTURE_CUBE_MAP ? 6 : 1; + for (int32_t face = 0; face < numFace; face++) { + if (this->m_Mipmaps[face]) { + delete[] this->m_Mipmaps[face]; + } + } + + if (this->m_Mipmaps) { + delete[] this->m_Mipmaps; + } + + this->m_Mipmaps = nullptr; + this->m_Depth = 0; + + // TODO this->Sub690D0(); + + glDeleteTextures(1, &this->m_TextureID); + + this->m_GenerateMipmaps = 0; + this->m_MaxMipmapLevel = 1000; + this->m_BaseMipmapLevel = 0; + this->m_CompareMode = 0; + + this->m_Sampler.mipmapBias = 0.0f; + this->m_Sampler.borderColor = { 0.0f, 0.0f, 0.0f, 0.0f }; + this->m_Sampler.addressModeS = GL_REPEAT; + this->m_Sampler.addressModeT = GL_REPEAT; + this->m_Sampler.addressModeR = GL_REPEAT; + this->m_Sampler.magFilterMode = GL_LINEAR; + this->m_Sampler.minFilterMode = GL_NEAREST_MIPMAP_LINEAR; + this->m_Sampler.maxAnisotropy = 1.0f; + + Blizzard::Memory::Free(this->m_Data); + this->m_Data = nullptr; + + switch (this->m_TextureType) { + case GL_TEXTURE_3D: + // TODO GLPool::GLObjectPool::Push(GLPool::m_pool + 264, this); + break; + + case GL_TEXTURE_CUBE_MAP: + // TODO GLPool::GLObjectPool::Push(GLPool::m_pool + 520, this); + break; + + case GL_TEXTURE_2D: + // TODO GLPool::GLObjectPool::Push(GLPool::m_pool + 131080, this); + break; + } +} + +std::vector& GLTexture::GetBindings() { + uint32_t index = GLSDLTextureTypeToIndex(this->m_TextureType); + + uint32_t id; + + if (index == 0) { + id = this->m_TextureID - 33; + } else if (index == 1) { + id = this->m_TextureID - 32801; + } else if (index == 2) { + id = this->m_TextureID - 32865; + } else if (index == 3) { + id = this->m_TextureID - 1; + } + + auto target = static_cast>*>( + Blizzard::Thread::RegisterLocalStorage( + &GLTexture::m_Bindings[index], + GLTexture::CreateBindings, + nullptr, + GLTexture::DestroyBindings + ) + ); + + if (id >= target->size()) { + target->resize(id + 1); + } + + return (*target)[id]; +} + +GLTextureFormat GLTexture::GetFormat() { + return this->m_Format; +} + +TextureFormatInfo& GLTexture::GetFormatInfo() { + return k_TextureFormatInfo[this->m_Format]; +} + +GLMipmap* GLTexture::GetMipmap(uint32_t level, GLEnum face) { + BLIZZARD_ASSERT(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); + BLIZZARD_ASSERT(level < this->m_NumMipmap); + BLIZZARD_ASSERT(this->m_Mipmaps != nullptr); + BLIZZARD_ASSERT(this->m_Mipmaps[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X] != nullptr); + + return &this->m_Mipmaps[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X][level]; +} + +bool GLTexture::IsRenderTarget() { + return this->m_Flags & GLTFLAG_RENDERTARGET; +} + +bool GLTexture::IsSystemBuffer() { + return this->m_Flags & GLTFLAG_SYSTEM_BUFFER; +} + +bool GLTexture::IsValid() { + return this->m_Depth; +} + +void* GLTexture::Map(uint32_t level, const GLRect* a3, uint32_t& a4, GLEnum a5) { + BLIZZARD_ASSERT(this->m_TextureType != GL_TEXTURE_3D); + + auto mipmap = this->GetMipmap(level, GL_TEXTURE_CUBE_MAP_POSITIVE_X); + a4 = mipmap->GetPitch(); + return mipmap->Map(a5, a3); +} + +void GLTexture::RecreateGLTexture() { + if (this->m_TextureType == GL_TEXTURE_RECTANGLE) { + return; + } + + bool isCubeMap = this->m_TextureType == GL_TEXTURE_CUBE_MAP; + int32_t numFace = isCubeMap ? 6 : 1; + + for (int32_t face = 0; face < numFace; face++) { + for (int32_t level = 0; level < this->m_NumMipmap; level++) { + this->m_Mipmaps[face][level].Map(GL_WRITE_ONLY, static_cast(nullptr)); + this->m_Mipmaps[face][level].Unmap(); + } + } + + glTexParameterf(this->m_TextureType, GL_TEXTURE_LOD_BIAS, this->m_Sampler.mipmapBias); + glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_S, this->m_Sampler.addressModeS); + glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_T, this->m_Sampler.addressModeT); + glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_R, this->m_Sampler.addressModeR); + glTexParameteri(this->m_TextureType, GL_TEXTURE_MIN_FILTER, this->m_Sampler.minFilterMode); + glTexParameteri(this->m_TextureType, GL_TEXTURE_MAG_FILTER, this->m_Sampler.magFilterMode); + glTexParameterf(this->m_TextureType, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->m_Sampler.maxAnisotropy); + glTexParameterfv(this->m_TextureType, GL_TEXTURE_BORDER_COLOR, reinterpret_cast(&this->m_Sampler.borderColor)); + glTexParameteri(this->m_TextureType, GL_TEXTURE_MAX_LEVEL, this->m_MaxMipmapLevel); + glTexParameteri(this->m_TextureType, GL_TEXTURE_BASE_LEVEL, this->m_BaseMipmapLevel); + glTexParameteri(this->m_TextureType, GL_GENERATE_MIPMAP, this->m_GenerateMipmaps); +} + +void GLTexture::ResizeMipmaps() { + BLIZZARD_ASSERT(this->m_Mipmaps == nullptr); + + int32_t numFace = this->m_TextureType == GL_TEXTURE_CUBE_MAP ? 6 : 1; + + this->m_Mipmaps = new GLMipmap*[numFace]; + + for (int32_t face = 0; face < numFace; face++) { + this->m_Mipmaps[face] = new GLMipmap[this->m_NumMipmap]; + } +} + +void GLTexture::SetAddressModeR(GLEnum mode) { + // TODO +} + +void GLTexture::SetAddressModeS(GLEnum mode) { + if (this->m_Sampler.addressModeS == mode) { + return; + } + + if (mode == GL_CLAMP_TO_EDGE) { + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_S, mode); + this->m_Sampler.addressModeS = mode; + } else { + // Workaround for buggy GPU (possibly ATI Radeon X1900) + if (GLSDLDevice::GetRendererInfo().renderer_id == 0x21900) { + if (this->m_Width & (this->m_Width - 1)) { + return; + } + + if (this->m_Height & (this->m_Height - 1)) { + return; + } + } + + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_S, mode); + this->m_Sampler.addressModeS = mode; + } +} + +void GLTexture::SetAddressModeT(GLEnum mode) { + if (this->m_Sampler.addressModeT == mode) { + return; + } + + if (mode == GL_CLAMP_TO_EDGE) { + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_T, mode); + this->m_Sampler.addressModeT = mode; + } else { + // Workaround for buggy GPU (possibly ATI Radeon X1900) + if (GLSDLDevice::GetRendererInfo().renderer_id == 0x21900) { + if (this->m_Width & (this->m_Width - 1)) { + return; + } + + if (this->m_Height & (this->m_Height - 1)) { + return; + } + } + + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_T, mode); + this->m_Sampler.addressModeT = mode; + } +} + +void GLTexture::SetBorderColor(const GLColor4f& color) { + // TODO +} + +void GLTexture::SetCompareMode(GLEnum compareMode) { + BLIZZARD_ASSERT( + this->GetFormatInfo().m_DataFormat == GL_DEPTH_COMPONENT + || this->GetFormatInfo().m_DataFormat == GL_DEPTH_STENCIL_EXT + || compareMode == GL_NONE + ); + + if (this->m_CompareMode != compareMode) { + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_COMPARE_MODE, compareMode); + this->m_CompareMode = compareMode; + } +} + +void GLTexture::SetMagFilterMode(GLEnum mode) { + if (this->m_Sampler.magFilterMode == mode) { + return; + } + + if (GLSDLDevice::GetRendererInfo().vendor_id == 2 && this->IsRenderTarget() && mode != GL_LINEAR) { + return; + } + + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_MAG_FILTER, mode); + this->m_Sampler.magFilterMode = mode; +} + +void GLTexture::SetMaxAnisotropy(int32_t maxAnisotropy) { + if (this->m_Sampler.maxAnisotropy == maxAnisotropy) { + return; + } + + this->Bind(nullptr, 0); + glTexParameterf(this->m_TextureType, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy); + this->m_Sampler.maxAnisotropy = maxAnisotropy; +} + +void GLTexture::SetMinFilterMode(GLEnum mode) { + if (this->m_Sampler.minFilterMode == mode) { + return; + } + + if (GLSDLDevice::GetRendererInfo().vendor_id == 2 && this->IsRenderTarget() && mode != GL_LINEAR) { + return; + } + + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_MIN_FILTER, mode); + this->m_Sampler.minFilterMode = mode; +} + +void GLTexture::SetupTexture() { + BLIZZARD_ASSERT(this->m_NumMipmap == 1 || (this->m_Flags & GLTFLAG_AUTOGEN_MIPMAP) == 0); + BLIZZARD_ASSERT(!this->IsRenderTarget() || this->m_NumMipmap == 1); + BLIZZARD_ASSERT(!this->IsRenderTarget() || (this->m_Flags & GLTFLAG_READ_ACCESS) == 0); + + GLSDLDevice* device = GLSDLDevice::Get(); + + if (this->GetFormatInfo().m_IsCompressed) { + int32_t smallestDim = std::min(this->m_Width, this->m_Height); + + BLIZZARD_ASSERT(smallestDim >= 4); + + if (smallestDim == 4) { + this->m_NumMipmap = 1; + } else if (smallestDim == 8) { + this->m_NumMipmap = 2; + } else if (smallestDim == 16) { + this->m_NumMipmap = 3; + } else if (smallestDim == 32) { + this->m_NumMipmap = 4; + } else if (smallestDim == 64) { + this->m_NumMipmap = 5; + } else if (smallestDim == 128) { + this->m_NumMipmap = 6; + } else if (smallestDim == 256) { + this->m_NumMipmap = 7; + } else if (smallestDim == 512) { + this->m_NumMipmap = 8; + } else if (smallestDim == 1024) { + this->m_NumMipmap = 9; + } else if (smallestDim == 2048) { + this->m_NumMipmap = 10; + } else if (smallestDim == 4096) { + this->m_NumMipmap = 11; + } else { + int32_t i = smallestDim >> 1; + int32_t n = 0; + + while (i) { + i >>= 1; + n++; + } + + this->m_NumMipmap = n - 1; + } + } else { + int32_t largestDim = std::max(this->m_Width, this->m_Height); + + if (largestDim == 1) { + this->m_NumMipmap = 1; + } else if (largestDim == 2) { + this->m_NumMipmap = 2; + } else if (largestDim == 4) { + this->m_NumMipmap = 3; + } else if (largestDim == 8) { + this->m_NumMipmap = 4; + } else if (largestDim == 16) { + this->m_NumMipmap = 5; + } else if (largestDim == 32) { + this->m_NumMipmap = 6; + } else if (largestDim == 64) { + this->m_NumMipmap = 7; + } else if (largestDim == 128) { + this->m_NumMipmap = 8; + } else if (largestDim == 256) { + this->m_NumMipmap = 9; + } else if (largestDim == 512) { + this->m_NumMipmap = 10; + } else if (largestDim == 1024) { + this->m_NumMipmap = 11; + } else if (largestDim == 2048) { + this->m_NumMipmap = 12; + } else if (largestDim == 4096) { + this->m_NumMipmap = 13; + } else { + int32_t i = largestDim >> 1; + int32_t n = 0; + + while (i) { + i >>= 1; + n++; + } + + this->m_NumMipmap = n + 1; + } + } + + if (!(this->m_Flags & GLTFLAG_SYSTEM_BUFFER)) { + BLIZZARD_ASSERT(this->m_RequestedNumMipmaps != 0); + + this->m_NumMipmap = std::min(this->m_NumMipmap, this->m_RequestedNumMipmaps); + } + + this->var12 = this->GetFormatInfo().m_BytePerPixel * this->m_Width; + if (this->GetFormatInfo().m_IsCompressed) { + this->var12 >>= 2; + } + + this->ResizeMipmaps(); + + bool isCubeMap = this->m_TextureType == GL_TEXTURE_CUBE_MAP; + + this->m_Size = 0; + + int32_t numFace = isCubeMap ? 6 : 1; + + for (int32_t face = 0; face < numFace; face++) { + for (int32_t level = 0; level < this->m_NumMipmap; level++) { + GLMipmap* mip = this->GetMipmap(level, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face); + + mip->m_Level = level; + mip->m_Texture = this; + mip->ResetSize(this->m_Width >> level, this->m_Height >> level, this->m_Depth >> level); + + this->m_Size += mip->m_Size; + } + } + + this->var7 = GLSDLDevice::m_Devices[0]->m_TextureList.begin(); + + if (this->m_Flags & GLTFLAG_SYSTEM_BUFFER) { + return; + } + + BLIZZARD_ASSERT(this->m_Data == nullptr); + + if (!this->IsRenderTarget()) { + this->m_Data = static_cast(Blizzard::Memory::Allocate(this->m_Size)); + } + + this->Bind(nullptr, 0); + + if (this->IsRenderTarget()) { + this->SetAddressModeR(GL_CLAMP_TO_EDGE); + this->SetAddressModeS(GL_CLAMP_TO_EDGE); + this->SetAddressModeT(GL_CLAMP_TO_EDGE); + this->SetMinFilterMode(GL_LINEAR); + this->SetMagFilterMode(GL_LINEAR); + + // glTexParameteri(this->m_TextureType, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE); + } else { + this->SetAddressModeR(GL_REPEAT); + this->SetAddressModeS(GL_REPEAT); + this->SetAddressModeT(GL_REPEAT); + this->SetMinFilterMode(GL_NEAREST_MIPMAP_NEAREST); + this->SetMagFilterMode(GL_NEAREST); + } + + if (this->GetFormatInfo().m_DataFormat == GL_DEPTH_COMPONENT || this->GetFormatInfo().m_DataFormat == GL_DEPTH_STENCIL) { + this->SetCompareMode(GLSDLDevice::m_ExtARBShadow >= 1 ? GL_COMPARE_R_TO_TEXTURE : 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + glTexParameteri(this->m_TextureType, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); + } else { + this->SetCompareMode(0); + } + + this->SetMaxAnisotropy(1); + + this->SetBorderColor(GLColor4f::ZERO); + + int32_t autogenMipmap = this->m_Flags & GLTFLAG_AUTOGEN_MIPMAP; + if (autogenMipmap != this->m_GenerateMipmaps) { + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_GENERATE_MIPMAP, autogenMipmap); + this->m_GenerateMipmaps = autogenMipmap; + } + + int32_t maxMipmapLevel = this->m_NumMipmap - 1; + if (maxMipmapLevel != this->m_MaxMipmapLevel) { + this->Bind(nullptr, 0); + glTexParameteri(this->m_TextureType, GL_TEXTURE_MAX_LEVEL, maxMipmapLevel); + this->m_MaxMipmapLevel = maxMipmapLevel; + } + + if (this->m_TextureType == GL_TEXTURE_RECTANGLE) { + return; + } + + if (this->IsRenderTarget()) { + device->SetUnpackClientStorage(0); + } + + unsigned char* data = reinterpret_cast(this->m_Data); + + for (int32_t face = 0; face < numFace; face++) { + for (int32_t level = 0; level < this->m_NumMipmap; level++) { + GLMipmap* mip = this->GetMipmap(level, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face); + + if (this->m_TextureType == GL_TEXTURE_CUBE_MAP) { + mip->ResetData(face, level, data); + } else { + mip->ResetData(this->m_TextureType, level, data); + } + + if (data) { + data += mip->m_Size; + } + } + } + + // TODO + // this->m_TimeStamp = Blizzard::Time::GetTimestamp(); + + if (this->IsRenderTarget()) { + device->SetUnpackClientStorage(1); + } +} + +void GLTexture::Unbind(GLSDLDevice* device, uint32_t stage) { + auto& bindings = this->GetBindings(); + + BLIZZARD_ASSERT(device->GetID() < bindings.size()); + BLIZZARD_ASSERT(bindings[device->GetID()].device == device); + BLIZZARD_ASSERT(bindings[device->GetID()].boundStages[stage]); + + bindings[device->GetID()].boundStages[stage] = 0; +} + +void GLTexture::Unmap(uint32_t level, GLEnum face) { + auto mipmap = this->GetMipmap(level, face); + mipmap->Unmap(); +} + +GLTexture2D* GLTexture2D::Create(uint32_t width, uint32_t height, uint32_t numMipMap, GLTextureFormat format, uint32_t flags) { + GLTexture2D* tex; + + // TODO + // tex = GLPool::GLObjectPool::Pop(GLPool::m_pool + 131080); + tex = nullptr; + + if (!tex) { + tex = new GLTexture2D(); + } + + // TODO + // Blizzard::Debug::Assert(tex->m_refCount == 0); + + tex->m_RefCount = 1; + + // TODO + // Blizzard::Debug::Assert(tex->m_TextureID >= PoolStats::NAME_POOL_FIRST_NAME); + + tex->m_TextureType = GL_TEXTURE_2D; + tex->m_Width = width; + tex->m_Depth = 1; + tex->m_Height = height; + tex->m_Format = format; + tex->m_NumMipmap = numMipMap; + tex->m_RequestedNumMipmaps = numMipMap; + tex->m_Flags = flags; + + tex->SetupTexture(); + + return tex; +} + +GLTexture2D::GLTexture2D() : GLTexture() { + this->m_TextureType = GL_TEXTURE_2D; + this->m_TextureID = GLPool::Get()->GetNextName(); + + // TODO + // Blizzard::Debug::Assert(this->m_TextureID >= PoolStats::NAME_POOL_FIRST_NAME); +} + +void GLTexture2D::ReleaseObject() { + BLIZZARD_ASSERT(this->m_TextureType == GL_TEXTURE_2D); + this->FreeTexture(); +} + +void GLSDLTexSetFlags(CGxTex* texId, GLTexture* a2) { + static GLEnum convertMagFilterToOgl[] = { + GL_NEAREST, + GL_LINEAR, + GL_NEAREST, + GL_LINEAR, + GL_LINEAR, + GL_LINEAR + }; + + static GLEnum convertMinFilterToOgl[] = { + GL_NEAREST, + GL_LINEAR, + GL_NEAREST_MIPMAP_NEAREST, + GL_LINEAR_MIPMAP_NEAREST, + GL_LINEAR_MIPMAP_LINEAR, + GL_LINEAR_MIPMAP_LINEAR + }; + + a2->SetMagFilterMode(convertMagFilterToOgl[texId->m_flags.m_filter]); + a2->SetMinFilterMode(convertMinFilterToOgl[texId->m_flags.m_filter]); + + a2->SetAddressModeS(texId->m_flags.m_wrapU ? GL_REPEAT : GL_CLAMP_TO_EDGE); + a2->SetAddressModeT(texId->m_flags.m_wrapV ? GL_REPEAT : GL_CLAMP_TO_EDGE); + + a2->SetMaxAnisotropy(texId->m_flags.m_maxAnisotropy); +} diff --git a/src/gx/glsdl/GLTexture.hpp b/src/gx/glsdl/GLTexture.hpp new file mode 100644 index 0000000..00832fb --- /dev/null +++ b/src/gx/glsdl/GLTexture.hpp @@ -0,0 +1,101 @@ +#ifndef GX_GL_SDL_GL_TEXTURE_HPP +#define GX_GL_SDL_GL_TEXTURE_HPP + +#include "gx/glsdl/GL.hpp" +#include "gx/glsdl/GLObject.hpp" +#include "gx/glsdl/GLTypes.hpp" +#include +#include +#include +#include + +#define GLTFLAG_RENDERTARGET 0x1 +#define GLTFLAG_DEPTH 0x2 +#define GLTFLAG_STENCIL 0x4 +#define GLTFLAG_AUTOGEN_MIPMAP 0x8 +#define GLTFLAG_READ_ACCESS 0x10 +#define GLTFLAG_SYSTEM_BUFFER 0x20 + +class CGxTex; +class GLMipmap; +class GLSDLDevice; + +class GLTexture : public GLObject { + public: + // Types + struct Binding { + uint8_t boundStages[16]; + GLSDLDevice* device; + }; + + // Static variables + static Blizzard::Thread::TLSSlot m_Bindings[4]; + + // Static functions + static void* CreateBindings(void*); + static void DestroyBindings(void*); + + // Member variables + uint32_t m_TextureID = 0; + GLEnum m_TextureType = 0; + GLMipmap** m_Mipmaps = nullptr; + std::list::iterator var7; + uint32_t m_LastFrameUsed; + uint32_t m_Width = 0; + uint32_t m_Height = 0; + uint32_t m_Depth = 0; + uint32_t var12 = 0; + uint32_t m_Size = 0; + GLTextureFormat m_Format = GLTF_INVALID; + uint32_t m_Flags = 0; + uint32_t m_NumMipmap = 0; + uint32_t m_RequestedNumMipmaps; + char* m_Data = nullptr; + std::atomic m_MappedMipmaps = { 0 }; + GLStates::Sampler m_Sampler; + bool m_GenerateMipmaps = 0; + int32_t m_MaxMipmapLevel = 1000; + int32_t m_BaseMipmapLevel = 0; + int32_t m_CompareMode = 0; + + // Member functions + void Bind(GLSDLDevice*, bool); + void FreeTexture(); + std::vector& GetBindings(void); // invented name + GLTextureFormat GetFormat(void); + TextureFormatInfo& GetFormatInfo(void); + GLMipmap* GetMipmap(uint32_t, GLEnum); + bool IsRenderTarget(void); + bool IsSystemBuffer(void); + bool IsValid(void); + void* Map(uint32_t, const GLRect*, uint32_t&, GLEnum); + void RecreateGLTexture(void); + void ResizeMipmaps(void); + void SetAddressModeR(GLEnum); + void SetAddressModeS(GLEnum); + void SetAddressModeT(GLEnum); + void SetBorderColor(const GLColor4f&); + void SetCompareMode(GLEnum); + void SetMagFilterMode(GLEnum); + void SetMaxAnisotropy(int32_t); + void SetMinFilterMode(GLEnum); + void SetupTexture(void); + void Unbind(GLSDLDevice*, uint32_t); // invented name + void Unmap(uint32_t level, GLEnum face); +}; + +class GLTexture2D : public GLTexture { + public: + // Static functions + static GLTexture2D* Create(uint32_t, uint32_t, uint32_t, GLTextureFormat, uint32_t); + + // Virtual member functions + virtual void ReleaseObject(); + + // Member functions + GLTexture2D(); +}; + +void GLSDLTexSetFlags(CGxTex*, GLTexture*); + +#endif diff --git a/src/gx/glsdl/GLTypes.cpp b/src/gx/glsdl/GLTypes.cpp new file mode 100644 index 0000000..82dd7d2 --- /dev/null +++ b/src/gx/glsdl/GLTypes.cpp @@ -0,0 +1,58 @@ +#include "gx/glsdl/GLTypes.hpp" +#include + +GLColor4f GLColor4f::ZERO = { 0.0, 0.0, 0.0, 0.0 }; +GLColor4f GLColor4f::WHITE = { 1.0, 1.0, 1.0, 1.0 }; +GLColor4f GLColor4f::BLACK = { 0.0, 0.0, 0.0, 1.0 }; + +bool GLTransform::operator==(const float m[16]) const { + return this->m[0] == m[0] + && this->m[1] == m[1] + && this->m[2] == m[2] + && this->m[3] == m[3] + && this->m[4] == m[4] + && this->m[5] == m[5] + && this->m[6] == m[6] + && this->m[7] == m[7] + && this->m[8] == m[8] + && this->m[9] == m[9] + && this->m[10] == m[10] + && this->m[11] == m[11] + && this->m[12] == m[12] + && this->m[13] == m[13] + && this->m[14] == m[14] + && this->m[15] == m[15]; +} + +bool GLTransform::operator!=(const float m[16]) const { + return !(*this == m); +} + +void GLTransform::Set(const float m[16]) { + memcpy(this->m, m, sizeof(this->m)); + this->isDirty = true; + this->isIdentity = this->a0 == 1.0f + && this->a1 == 0.0f + && this->a2 == 0.0f + && this->a3 == 0.0f + && this->b0 == 0.0f + && this->b1 == 1.0f + && this->b2 == 0.0f + && this->b3 == 0.0f + && this->c0 == 0.0f + && this->c1 == 0.0f + && this->c2 == 1.0f + && this->c3 == 0.0f + && this->d0 == 0.0f + && this->d1 == 0.0f + && this->d2 == 0.0f + && this->d3 == 1.0f; +} + +void GLTransform::SetIdentity() { + memset(this->m, 0, sizeof(this->m)); + this->a0 = 1.0f; + this->b1 = 1.0f; + this->c2 = 1.0f; + this->d3 = 1.0f; +} diff --git a/src/gx/glsdl/GLTypes.hpp b/src/gx/glsdl/GLTypes.hpp new file mode 100644 index 0000000..0df1480 --- /dev/null +++ b/src/gx/glsdl/GLTypes.hpp @@ -0,0 +1,376 @@ +#ifndef GX_GL_SDL_GL_TYPES_HPP +#define GX_GL_SDL_GL_TYPES_HPP + +#include + +#include + +class GLBuffer; +class GLFramebuffer; + +enum GLTextureFormat { + GLTF_INVALID = 0, + GLTF_ARGB8888 = 1, + GLTF_XRGB8888 = 2, + GLTF_RGBA8888 = 3, + GLTF_ABGR8888 = 4, + GLTF_ARGB0888 = 5, + GLTF_RGB888 = 6, + GLTF_BGR888 = 7, + GLTF_RGBA32F = 8, + GLTF_RGBA16F = 9, + GLTF_RG16F = 10, + GLTF_D32 = 11, + GLTF_D24 = 12, + GLTF_D16 = 13, + GLTF_DF = 14, + GLTF_D24S8 = 15, + GLTF_S8 = 16, + GLTF_ARGB4444 = 17, + GLTF_ARGB1555 = 18, + GLTF_ARGB0555 = 19, + GLTF_RGB565 = 20, + GLTF_A2RGB10 = 21, + GLTF_RGB16 = 22, + GLTF_L8 = 23, + GLTF_A8 = 24, + GLTF_A8L8 = 25, + GLTF_DXT1 = 26, + GLTF_DXT3 = 27, + GLTF_DXT5 = 28, + GLTF_NUM_TEXTURE_FORMATS = 29 +}; + +enum GLVertexType { + GLVT_INVALID = 0, + GLVT_FLOAT1 = 1, + GLVT_FLOAT2 = 2, + GLVT_FLOAT3 = 3, + GLVT_FLOAT4 = 4, + GLVT_UBYTE4 = 5, + GLVT_UBYTE4N = 6, + GLVT_SHORT = 7, + GLVT_SHORT2 = 8, + GLVT_SHORT4 = 9, + GLVT_SHORT2N = 10, + GLVT_SHORT4N = 11, + GLVT_USHORT2N = 12, + GLVT_USHORT4N = 13, + GLVT_NUM_VERTEX_TYPES = 14 +}; + +enum GLBufferType { + eGLBT_PIXEL_UNPACK = 3, +}; + +struct GLAttachPoint { + GLFramebuffer* framebuffer; + int32_t point; // TODO GLenum? + int32_t zOffset; // TODO check type +}; + +struct GLBox { + int32_t left; + int32_t top; + int32_t front; + int32_t width; + int32_t height; + int32_t depth; +}; + +struct GLColor4f { + float r; + float g; + float b; + float a; + + static GLColor4f ZERO; + static GLColor4f WHITE; + static GLColor4f BLACK; +}; + +struct GLDirtyRange { + uint16_t start; + uint16_t end; +}; + +struct GLfloat4 { + float x; + float y; + float z; + float w; +}; + +struct GLRect { + int32_t left; + int32_t top; + int32_t width; + int32_t height; +}; + +struct GLTransform { + bool isDirty; + + union { + struct { + float a0; + float a1; + float a2; + float a3; + float b0; + float b1; + float b2; + float b3; + float c0; + float c1; + float c2; + float c3; + float d0; + float d1; + float d2; + float d3; + }; + + struct { + float rows[4][4]; + }; + + float m[16]; + }; + + bool isIdentity; + + bool operator==(const float m[16]) const; + bool operator!=(const float m[16]) const; + void Set(const float m[16]); + void SetIdentity(); +}; + +struct GLStates { + struct Depth { + bool testEnable; + int32_t compareFunc; + bool writeMask; + }; + + struct Stencil { + struct StencilFace { + int32_t compareFunc; + int32_t opFail; + int32_t opZFail; + int32_t opZPass; + }; + + bool testEnable; + int32_t ref; + uint32_t mask; + uint32_t writeMask; + bool useTwoSidedStencil; + StencilFace front; + StencilFace back; + }; + + struct Rasterizer { + struct ClipPlane { + double plane[4]; + }; + + int32_t cullMode; + int32_t cullFaceMode; + int32_t fillMode; + float constantDepthBias; + float slopeScaledDepthBias; + GLRect viewport; + double zNear; + double zFar; + bool scissorEnable; + GLRect scissor; + uint32_t clipPlaneMask; + ClipPlane clipPlanes[6]; + }; + + struct Blend { + struct ColorMask { + bool red; + bool green; + bool blue; + bool alpha; + }; + + ColorMask colorMask[4]; + bool alphaBlend; + int32_t srcBlendFactor; + int32_t destBlendFactor; + int32_t blendOp; + GLColor4f blendColor; + }; + + struct Clear { + GLColor4f clearColor; + double clearDepth; + int32_t clearStencil; + }; + + struct FixedFunc { + struct TexOp { + int32_t texturing; + GLColor4f constant; + int32_t colorOp; + float colorScale; + int32_t colorArg0; + int32_t colorArg1; + int32_t colorArg2; + int32_t alphaOp; + float alphaScale; + int32_t alphaArg0; + int32_t alphaArg1; + int32_t alphaArg2; + }; + + struct Light { + bool enable; + GLfloat4 position; + GLTransform view; + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; + GLColor4f ambient; + GLColor4f diffuse; + GLColor4f specular; + }; + + struct Material { + bool colorTracking; + int32_t materialSource; + GLColor4f ambient; + GLColor4f diffuse; + GLColor4f specular; + float shininess; + GLColor4f emission; + }; + + struct Lighting { + bool enable; + GLColor4f sceneAmbient; + Light lights[8]; + Material material; + }; + + struct Transforms { + int32_t matrixMode; + int32_t modelviewStatus; + GLTransform modelView; + GLTransform world; + GLTransform view; + GLTransform projection; + GLTransform texture[8]; + }; + + struct TexGen { + int32_t S; + int32_t T; + int32_t R; + int32_t Q; + }; + + struct PointSprite { + bool enable; + float size; + float attenuation[3]; + float min; + float max; + }; + + bool fogEnable; + GLColor4f fogColor; + int32_t fogMode; + float fogStart; + float fogEnd; + float fogDensity; + bool alphaTestEnable; + int32_t alphaTestFunc; + float alphaTestRef; + TexOp texOp[8]; + Lighting lighting; + Transforms transforms; + int32_t texCoordIndex[8]; + TexGen texGen[8]; + PointSprite pointSprite; + bool normalizeNormal; + }; + + struct Sampler { + float mipmapBias; + int32_t addressModeS; + int32_t addressModeT; + int32_t addressModeR; + int32_t magFilterMode; + int32_t minFilterMode; + float maxAnisotropy; + GLColor4f borderColor; + }; + + struct Shader { + bool vertexShaderEnable; + GLfloat4 vertexShaderConst[256]; + bool pixelShaderEnable; + GLfloat4 pixelShaderConst[64]; + }; + + struct Binding { + uint32_t currentActiveTexture; + uint32_t texture[4][16]; + uint32_t framebuffer; + uint32_t renderbuffer; + uint32_t vertexProgram; + uint32_t pixelProgram; + uint32_t glslProgram; + uint32_t vertexArrayObject; + }; + + struct Misc { + bool unpackClientStorage; + int32_t drawBuffers[4]; + int32_t readBuffer; + }; + + struct VertexArrayObject { + struct VertexAttrib { + bool enable = 0; + uint32_t size = 4; + int32_t type = GL_FLOAT; + bool normalized = 0; + uint32_t stride = 0; + void* offset = nullptr; + GLBuffer* buffer = nullptr; + }; + + uint32_t buffers[4] = {}; + VertexAttrib vertexAttribs[16]; + VertexAttrib position; + VertexAttrib normal; + VertexAttrib color0; + VertexAttrib color1; + VertexAttrib texCoord[8]; + }; + + Depth depth; + Stencil stencil; + Rasterizer rasterizer; + Blend blend; + Clear clear; + FixedFunc fixedFunc; + Sampler samplers[16]; + Shader shader; + Binding binding; + Misc misc; +}; + +struct GLVertexAttrib { + uint32_t stream; + int32_t slot; + int32_t type; + uint32_t offset; +}; + +#endif diff --git a/src/gx/glsdl/GLUtil.cpp b/src/gx/glsdl/GLUtil.cpp new file mode 100644 index 0000000..c57b3e4 --- /dev/null +++ b/src/gx/glsdl/GLUtil.cpp @@ -0,0 +1,22 @@ +#include "gx/glsdl/GLUtil.hpp" +#include + +uint32_t GLSDLTextureTypeToIndex(GLEnum textureType) { + uint32_t index; + + if (textureType == GL_TEXTURE_2D) { + index = 0; + } else if (textureType == GL_TEXTURE_3D) { + index = 1; + } else if (textureType == GL_TEXTURE_CUBE_MAP) { + index = 2; + } else if (textureType == GL_TEXTURE_RECTANGLE) { + index = 3; + } else { + index = 5; + + BLIZZARD_ASSERT(false); + } + + return index; +} diff --git a/src/gx/glsdl/GLUtil.hpp b/src/gx/glsdl/GLUtil.hpp new file mode 100644 index 0000000..425f75c --- /dev/null +++ b/src/gx/glsdl/GLUtil.hpp @@ -0,0 +1,9 @@ +#ifndef GX_GL_SDL_GL_UTIL_HPP +#define GX_GL_SDL_GL_UTIL_HPP + +#include "gx/glsdl/GL.hpp" +#include + +uint32_t GLSDLTextureTypeToIndex(GLEnum textureType); + +#endif diff --git a/src/gx/glsdl/GLVertexArray.cpp b/src/gx/glsdl/GLVertexArray.cpp new file mode 100644 index 0000000..d7841f8 --- /dev/null +++ b/src/gx/glsdl/GLVertexArray.cpp @@ -0,0 +1,316 @@ +#include "gx/glsdl/GLVertexArray.hpp" +#include "gx/glsdl/GLSDLDevice.hpp" +#include + +bool GLVertexArray::s_VertexArrayEnable = false; + +GLVertexArray::GLVertexArray(bool a2) { + // TODO +} + +void GLVertexArray::FindVertexArray(GLSDLDevice* a1, GLVertexArray& a2) { + if (GLVertexArray::s_VertexArrayEnable) { + // TODO + } + + a2.ApplyVertexFormat(a1); +} + +void GLVertexArray::ApplyGLStates(GLStates::VertexArrayObject& vao) { + GLSDLDevice* device = GLSDLDevice::Get(); + + device->BindVertexArray(this); + + for (int32_t i = 0; i < kMAX_VERTEX_ATTRIBS; i++) { + auto& attrib = vao.vertexAttribs[i]; + + if (attrib.enable) { + glBindBuffer(attrib.buffer->m_Type, attrib.buffer->m_BufferID); + + glVertexAttribPointerARB( + i, + attrib.size, + attrib.type, + attrib.normalized, + attrib.stride, + reinterpret_cast(attrib.offset) + ); + + glEnableVertexAttribArrayARB(i); + } else { + glDisableVertexAttribArrayARB(i); + } + } + + if (vao.position.enable) { + glBindBuffer(vao.position.buffer->m_Type, vao.position.buffer->m_BufferID); + glVertexPointer(vao.position.size, vao.position.type, vao.position.stride, vao.position.offset); + glEnableClientState(GL_VERTEX_ARRAY); + } else { + glDisableClientState(GL_VERTEX_ARRAY); + } + + if (vao.normal.enable) { + glBindBuffer(vao.normal.buffer->m_Type, vao.normal.buffer->m_BufferID); + glNormalPointer(vao.normal.type, vao.normal.stride, vao.normal.offset); + glEnableClientState(GL_NORMAL_ARRAY); + } else { + glDisableClientState(GL_NORMAL_ARRAY); + } + + if (vao.color0.enable) { + glBindBuffer(vao.color0.buffer->m_Type, vao.color0.buffer->m_BufferID); + glColorPointer(vao.color0.size, vao.color0.type, vao.color0.stride, vao.color0.offset); + glEnableClientState(GL_COLOR_ARRAY); + } else { + glDisableClientState(GL_COLOR_ARRAY); + } + + if (vao.color1.enable) { + glBindBuffer(vao.color1.buffer->m_Type, vao.color1.buffer->m_BufferID); + glColorPointer(vao.color1.size, vao.color1.type, vao.color1.stride, vao.color1.offset); + glEnableClientState(GL_SECONDARY_COLOR_ARRAY); + } else { + glDisableClientState(GL_SECONDARY_COLOR_ARRAY); + } + + for (int32_t i = 0; i < 8; i++) { + glClientActiveTextureARB(GL_TEXTURE0 + i); + + if (vao.texCoord[i].enable) { + glBindBuffer(vao.texCoord[i].buffer->m_Type, vao.texCoord[0].buffer->m_BufferID); + glTexCoordPointer(vao.texCoord[i].size, vao.texCoord[i].type, vao.texCoord[i].stride, vao.texCoord[i].offset); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } else { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + } + + glBindBuffer(GL_ARRAY_BUFFER, vao.buffers[0]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vao.buffers[1]); + glBindBuffer(GL_PIXEL_PACK_BUFFER, vao.buffers[2]); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, vao.buffers[3]); + + this->m_GLStates = vao; +} + +void GLVertexArray::ApplyVertexFormat(GLSDLDevice* device) { + if (GLVertexArray::s_VertexArrayEnable) { + device->BindVertexArray(this); + } + + auto indexBuffer = this->m_Properties.m_IndexBuffer; + uint32_t indexBufferID = indexBuffer ? indexBuffer->m_BufferID : 0; + + if (this->m_GLStates.buffers[1] != indexBufferID) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID); + this->m_GLStates.buffers[1] = indexBufferID; + } + + BLIZZARD_ASSERT(this->GetProperties().m_VertexBufferFormat != nullptr); + + auto& properties = this->GetProperties(); + bool attribEnable[16] = {}; + + bool useVertexShader = device->GetShader(GLShader::eVertexShader) != nullptr; + + for (int32_t index = 0; index < this->GetProperties().m_VertexBufferFormat->m_Size; index++) { + BLIZZARD_ASSERT(index < kMAX_VERTEX_ATTRIBS); + + auto& attrib = this->GetProperties().m_VertexBufferFormat->m_Attribs[index]; + + BLIZZARD_ASSERT(attrib.type != GLVT_INVALID); + BLIZZARD_ASSERT(attrib.type < GLVT_NUM_VERTEX_TYPES); + + auto vertexBuffer = this->GetProperties().m_VertexBuffer[attrib.stream]; + + if (useVertexShader || static_cast(attrib.slot - 1) > 1) { + if (this->m_GLStates.buffers[0] != vertexBuffer->m_BufferID) { + glBindBuffer(vertexBuffer->m_Type, vertexBuffer->m_BufferID); + this->m_GLStates.buffers[0] = vertexBuffer->m_BufferID; + } + + attribEnable[attrib.slot] = 1; + + int32_t stride = properties.m_VertexBufferStride[attrib.stream]; + int32_t offset = attrib.offset + + properties.m_VertexBufferOffset[attrib.stream] + + properties.m_VertexBase * stride; + + if (useVertexShader) { + glVertexAttribPointerARB( + attrib.slot, + k_VertexTypeInfo[attrib.type].m_Size, + k_VertexTypeInfo[attrib.type].m_Type, + k_VertexTypeInfo[attrib.type].m_Normalized, + stride, + reinterpret_cast(offset) + ); + } else { + switch (attrib.slot) { + case 0: { + glVertexPointer( + k_VertexTypeInfo[attrib.type].m_Size, + k_VertexTypeInfo[attrib.type].m_Type, + stride, + reinterpret_cast(offset) + ); + + break; + } + + case 3: { + glNormalPointer( + k_VertexTypeInfo[attrib.type].m_Type, + stride, + reinterpret_cast(offset) + ); + + break; + } + + case 4: { + glColorPointer( + k_VertexTypeInfo[attrib.type].m_Size, + k_VertexTypeInfo[attrib.type].m_Type, + stride, + reinterpret_cast(offset) + ); + + break; + } + + case 5: { + glSecondaryColorPointer( + k_VertexTypeInfo[attrib.type].m_Size, + k_VertexTypeInfo[attrib.type].m_Type, + stride, + reinterpret_cast(offset) + ); + + break; + } + + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: { + auto tmu = attrib.slot - 6; + auto texCoordIndex = device->m_States.fixedFunc.texCoordIndex[tmu]; + glClientActiveTextureARB(GL_TEXTURE0 + texCoordIndex); + glTexCoordPointer( + k_VertexTypeInfo[attrib.type].m_Size, + k_VertexTypeInfo[attrib.type].m_Type, + stride, + reinterpret_cast(offset) + ); + + break; + } + } + } + } + } + + for (int32_t s = 0; s < 16; s++) { + // Shader + if (useVertexShader) { + auto prevAttribEnable = &this->m_GLStates.vertexAttribs[s].enable; + + if (*prevAttribEnable != attribEnable[s]) { + if (attribEnable[s]) { + glEnableVertexAttribArrayARB(s); + } else { + glDisableVertexAttribArrayARB(s); + } + } + + *prevAttribEnable = attribEnable[s]; + + // FFP + } else { + bool* prevAttribEnable = nullptr; + GLenum glArray = GL_NONE; + + switch (s) { + case 0: { + prevAttribEnable = &this->m_GLStates.position.enable; + glArray = GL_VERTEX_ARRAY; + + break; + } + + case 3: { + prevAttribEnable = &this->m_GLStates.normal.enable; + glArray = GL_NORMAL_ARRAY; + + break; + } + + case 4: { + prevAttribEnable = &this->m_GLStates.color0.enable; + glArray = GL_COLOR_ARRAY; + + break; + } + + case 5: { + prevAttribEnable = &this->m_GLStates.color1.enable; + glArray = GL_SECONDARY_COLOR_ARRAY; + + break; + } + + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: { + auto tmu = s - 6; + auto texCoordIndex = device->m_States.fixedFunc.texCoordIndex[tmu]; + + prevAttribEnable = &this->m_GLStates.texCoord[texCoordIndex].enable; + glArray = GL_TEXTURE_COORD_ARRAY; + + glClientActiveTextureARB(GL_TEXTURE0 + texCoordIndex); + + break; + } + + default: + break; + } + + if (prevAttribEnable) { + if (*prevAttribEnable != attribEnable[s]) { + if (attribEnable[s]) { + glEnableClientState(glArray); + } else { + glDisableClientState(glArray); + } + } + + *prevAttribEnable = attribEnable[s]; + } + } + } + + if (!useVertexShader) { + // TODO device->SetColorMaterial(this->m_GLStates.color0.enable); + } +} + +GLVertexArray::Properties& GLVertexArray::GetProperties() { + return this->m_Properties; +} + +void GLVertexArray::ReleaseObject() { + // TODO +} diff --git a/src/gx/glsdl/GLVertexArray.hpp b/src/gx/glsdl/GLVertexArray.hpp new file mode 100644 index 0000000..49eae5d --- /dev/null +++ b/src/gx/glsdl/GLVertexArray.hpp @@ -0,0 +1,46 @@ +#ifndef GX_GL_SDL_GL_VERTEX_ARRAY_HPP +#define GX_GL_SDL_GL_VERTEX_ARRAY_HPP + +#include "gx/glsdl/GLBuffer.hpp" +#include "gx/glsdl/GLObject.hpp" +#include "gx/glsdl/GLVertexFormat.hpp" +#include + +class GLSDLDevice; + +class GLVertexArray : public GLObject { + public: + // Types + struct Properties { + GLVertexFormat* m_VertexBufferFormat = nullptr; + GLBuffer* m_IndexBuffer = nullptr; + GLBuffer* m_PixelPackBuffer = nullptr; + GLBuffer* m_PixelUnpackBuffer = nullptr; + GLBuffer* m_VertexBuffer[4] = {}; + uint32_t m_VertexBase = 0; + uint32_t m_VertexBufferOffset[4] = {}; + uint32_t m_VertexBufferStride[4] = {}; + }; + + // Static variables + static bool s_VertexArrayEnable; + + // Static functions + static void FindVertexArray(GLSDLDevice*, GLVertexArray&); + + // Member variables + Properties m_Properties; + GLStates::VertexArrayObject m_GLStates; + uint32_t m_VertexArrayID = 0; + + // Virtual member functions + virtual void ReleaseObject(); + + // Member functions + GLVertexArray(bool); + void ApplyGLStates(GLStates::VertexArrayObject&); + void ApplyVertexFormat(GLSDLDevice*); + Properties& GetProperties(void); +}; + +#endif diff --git a/src/gx/glsdl/GLVertexFormat.hpp b/src/gx/glsdl/GLVertexFormat.hpp new file mode 100644 index 0000000..5c14b6e --- /dev/null +++ b/src/gx/glsdl/GLVertexFormat.hpp @@ -0,0 +1,13 @@ +#ifndef GX_GL_SDL_GL_VERTEX_FORMAT_HPP +#define GX_GL_SDL_GL_VERTEX_FORMAT_HPP + +#include "gx/glsdl/GLTypes.hpp" +#include + +class GLVertexFormat { + public: + uint32_t m_Size; + GLVertexAttrib m_Attribs[16]; +}; + +#endif diff --git a/src/gx/glsdl/GLVertexShader.cpp b/src/gx/glsdl/GLVertexShader.cpp new file mode 100644 index 0000000..89e0ebd --- /dev/null +++ b/src/gx/glsdl/GLVertexShader.cpp @@ -0,0 +1,17 @@ +#include "gx/glsdl/GLVertexShader.hpp" +#include "gx/glsdl/GL.hpp" + +GLVertexShader* GLVertexShader::Create() { + // TODO + // GLPool stuff + + GLVertexShader* shader = new GLVertexShader(); + + shader->m_ShaderID = 0; + shader->m_RefCount = 1; + shader->m_ShaderType = eVertexShader; + shader->m_UsingGLSL = 0; + shader->var5 = GL_VERTEX_PROGRAM_ARB; + + return shader; +} diff --git a/src/gx/glsdl/GLVertexShader.hpp b/src/gx/glsdl/GLVertexShader.hpp new file mode 100644 index 0000000..511abe3 --- /dev/null +++ b/src/gx/glsdl/GLVertexShader.hpp @@ -0,0 +1,12 @@ +#ifndef GX_GL_SDL_GL_VERTEX_SHADER_HPP +#define GX_GL_SDL_GL_VERTEX_SHADER_HPP + +#include "gx/glsdl/GLShader.hpp" + +class GLVertexShader : public GLShader { + public: + // Static functions + static GLVertexShader* Create(void); +}; + +#endif