#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_GLSDL; 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", 0x4); // 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.m_sdlWindow; } 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; this->m_caps.m_hardwareCursor = 0; // 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::IXformSetWorld() { auto& stack = this->m_xforms[GxXform_World]; this->m_GLSDLDevice.SetTransform('WRLD', reinterpret_cast(&stack.TopConst())); stack.m_dirty = 0; } void CGxDeviceGLSDL::IStateSyncXforms() { if (this->m_xforms[GxXform_World].m_dirty) { this->IXformSetWorld(); } // 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(); this->ICursorDraw(); // 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); }