diff --git a/Src/Graphics/New3D/Model.h b/Src/Graphics/New3D/Model.h index a081d29..b317b66 100644 --- a/Src/Graphics/New3D/Model.h +++ b/Src/Graphics/New3D/Model.h @@ -5,7 +5,7 @@ #include #include #include -#include "Texture.h" +#include "Types.h" #include "Mat4.h" namespace New3D { diff --git a/Src/Graphics/New3D/New3D.cpp b/Src/Graphics/New3D/New3D.cpp index bd6dee8..c21a85e 100644 --- a/Src/Graphics/New3D/New3D.cpp +++ b/Src/Graphics/New3D/New3D.cpp @@ -1,5 +1,4 @@ #include "New3D.h" -#include "Texture.h" #include "Vec.h" #include #include @@ -16,10 +15,12 @@ namespace New3D { -CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName) - : m_r3dShader(config), - m_r3dScrollFog(config), - m_gameName(gameName) +CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName) : + m_r3dShader(config), + m_r3dScrollFog(config), + m_gameName(gameName), + m_textureBuffer(0), + m_vao(0) { m_cullingRAMLo = nullptr; m_cullingRAMHi = nullptr; @@ -40,6 +41,17 @@ CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName) CNew3D::~CNew3D() { m_vbo.Destroy(); + if (m_vao) { + glDeleteVertexArrays(1, &m_vao); + m_vao = 0; + } + + if (m_textureBuffer) { + glDeleteTextures(1, &m_textureBuffer); + m_textureBuffer = 0; + } + + m_r3dShader.UnloadShader(); } void CNew3D::AttachMemory(const UINT32 *cullingRAMLoPtr, const UINT32 *cullingRAMHiPtr, const UINT32 *polyRAMPtr, const UINT32 *vromPtr, const UINT16 *textureRAMPtr) @@ -67,8 +79,6 @@ void CNew3D::SetStepping(int stepping) m_offset = 2; // 8 words m_vertexFactor = (1.0f / 128.0f); // 17.7 } - - m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(FVertex) * (MAX_RAM_VERTS + MAX_ROM_VERTS)); } bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam) @@ -84,34 +94,55 @@ bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yR m_totalYRes = totalYResParam; m_r3dShader.LoadShader(); + glUseProgram(0); m_r3dFrameBuffers.CreateFBO(totalXResParam, totalYResParam); - glUseProgram(0); + // setup our texture memory - return OKAY; // OKAY ? wtf .. + glGenTextures(1, &m_textureBuffer); + glBindTexture(GL_TEXTURE_2D, m_textureBuffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, 2048, 2048, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, nullptr); // allocate storage + + // setup up our vertex buffer memory + + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(FVertex) * (MAX_RAM_VERTS + MAX_ROM_VERTS)); + m_vbo.Bind(true); + + glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inVertex")); + glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inNormal")); + glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inTexCoord")); + glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inColour")); + glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inFaceNormal")); + glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inFixedShade")); + + // before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil .. + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inVertex"), 4, GL_FLOAT, GL_FALSE, sizeof(FVertex), 0); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, normal)); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inTexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, texcoords)); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inColour"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FVertex), (void*)offsetof(FVertex, faceColour)); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFaceNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, faceNormal)); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFixedShade"), 1, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, fixedShade)); + + glBindVertexArray(0); + m_vbo.Bind(false); + + return OKAY; } void CNew3D::UploadTextures(unsigned level, unsigned x, unsigned y, unsigned width, unsigned height) { - if (level == 0) { - m_texSheet.Invalidate(x, y, width, height); // base textures only - } - else if (level == 1) { - // we want to work out what the base level is, and invalidate the entire texture - // the mipmap data in some cases is being sent later + glBindTexture(GL_TEXTURE_2D, m_textureBuffer); + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); - int page = y / 1024; - y -= (page * 1024); // remove page from tex y - - int xPos = (x - 1024) * 2; - int yPos = (y - 512) * 2; - - yPos += page * 1024; - width *= 2; - height *= 2; - - m_texSheet.Invalidate(xPos, yPos, width, height); + for (unsigned i = 0; i < height; i++) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y + i, width, 1, GL_RED_INTEGER, GL_UNSIGNED_SHORT, m_textureRAM + ((y + i) * 2048) + x); } } @@ -170,6 +201,9 @@ CheckScroll: bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_textureBuffer); + bool hasOverlay = false; // (high priority polys) for (auto &n : m_nodes) { @@ -178,8 +212,6 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer) continue; } - std::shared_ptr tex1; - CalcViewport(&n.viewport, std::abs(m_nfPairs[priority].zNear*0.96f), std::abs(m_nfPairs[priority].zFar*1.05f)); // make planes 5% bigger glViewport(n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height); @@ -207,34 +239,6 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer) matrixLoaded = true; // do this here to stop loading matrices we don't need. Ie when rendering non transparent etc } - if (mesh.textured) { - - int x, y; - CalcTexOffset(m.textureOffsetX, m.textureOffsetY, m.page, mesh.x, mesh.y, x, y); - - if (tex1 && tex1->Compare(x, y, mesh.width, mesh.height, mesh.format)) { - // texture already bound - } - else { - tex1 = m_texSheet.BindTexture(m_textureRAM, mesh.format, x, y, mesh.width, mesh.height); - if (tex1) { - tex1->BindTexture(); - } - } - - if (mesh.microTexture) { - - int mX, mY; - glActiveTexture(GL_TEXTURE1); - m_texSheet.GetMicrotexPos(y / 1024, mesh.microTextureID, mX, mY); - auto tex2 = m_texSheet.BindTexture(m_textureRAM, 0, mX, mY, 128, 128); - if (tex2) { - tex2->BindTexture(); - } - glActiveTexture(GL_TEXTURE0); - } - } - m_r3dShader.SetMeshUniforms(&mesh); glDrawArrays(m_primType, mesh.vboOffset, mesh.vertexCount); } @@ -260,23 +264,10 @@ bool CNew3D::SkipLayer(int layer) void CNew3D::SetRenderStates() { m_vbo.Bind(true); + glBindVertexArray(m_vao); + m_r3dShader.SetShader(true); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glEnableVertexAttribArray(3); - glEnableVertexAttribArray(4); - glEnableVertexAttribArray(5); - - // before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil .. - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inVertex"), 4, GL_FLOAT, GL_FALSE, sizeof(FVertex), 0); - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, normal)); - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inTexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, texcoords)); - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inColour"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FVertex), (void*)offsetof(FVertex, faceColour)); - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFaceNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, faceNormal)); - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFixedShade"), 1, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, fixedShade)); - glDepthFunc (GL_LEQUAL); glEnable (GL_DEPTH_TEST); glDepthMask (GL_TRUE); @@ -292,16 +283,11 @@ void CNew3D::SetRenderStates() void CNew3D::DisableRenderStates() { m_vbo.Bind(false); + glBindVertexArray(0); + m_r3dShader.SetShader(false); glDisable(GL_STENCIL_TEST); - - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(2); - glDisableVertexAttribArray(3); - glDisableVertexAttribArray(4); - glDisableVertexAttribArray(5); } void CNew3D::RenderFrame(void) @@ -992,6 +978,30 @@ void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector& vertex } } +void CNew3D::GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut) +{ + uOut = (uIn * uvScale) / width; + vOut = (vIn * uvScale) / height; +} + +int CNew3D::GetTexFormat(int originalFormat, bool contour) +{ + if (!contour) { + return originalFormat; // the same + } + + switch (originalFormat) + { + case 1: + case 2: + case 3: + case 4: + return originalFormat + 7; // these formats are identical to 1-4, except they lose the 4 bit alpha part when contour is enabled + default: + return originalFormat; + } +} + void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph) { //copy attributes @@ -1012,7 +1022,7 @@ void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph) if (currentMesh->textured) { - currentMesh->format = m_texSheet.GetTexFormat(ph.TexFormat(), ph.AlphaTest()); + currentMesh->format = GetTexFormat(ph.TexFormat(), ph.AlphaTest()); if (currentMesh->format == 7) { currentMesh->alphaTest = false; // alpha test is a 1 bit test, this format needs a lower threshold, since it has 16 levels of transparency @@ -1127,7 +1137,7 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) //check if we need to recalc tex coords - will only happen if tex tiles are different + sharing vertices if (hash != lastHash) { if (currentMesh->textured) { - Texture::GetCoordinates(currentMesh->width, currentMesh->height, texCoords[j][0], texCoords[j][1], uvScale, p.v[j].texcoords[0], p.v[j].texcoords[1]); + GetCoordinates(currentMesh->width, currentMesh->height, texCoords[j][0], texCoords[j][1], uvScale, p.v[j].texcoords[0], p.v[j].texcoords[1]); } } @@ -1209,7 +1219,7 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) // tex coords if (currentMesh->textured) { - Texture::GetCoordinates(currentMesh->width, currentMesh->height, (UINT16)(it >> 16), (UINT16)(it & 0xFFFF), uvScale, texU, texV); + GetCoordinates(currentMesh->width, currentMesh->height, (UINT16)(it >> 16), (UINT16)(it & 0xFFFF), uvScale, texU, texV); } p.v[j].texcoords[0] = texU; diff --git a/Src/Graphics/New3D/New3D.h b/Src/Graphics/New3D/New3D.h index 0bd81eb..33b9432 100644 --- a/Src/Graphics/New3D/New3D.h +++ b/Src/Graphics/New3D/New3D.h @@ -31,7 +31,6 @@ #include #include "Types.h" -#include "TextureSheet.h" #include "Graphics/IRender3D.h" #include "Model.h" #include "Mat4.h" @@ -207,9 +206,11 @@ private: void RenderViewport(UINT32 addr); // building the scene + int GetTexFormat(int originalFormat, bool contour); void SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph); void CacheModel(Model *m, const UINT32 *data); void CopyVertexData(const R3DPoly& r3dPoly, std::vector& vertexArray); + void GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut); bool RenderScene(int priority, bool renderOverlay, Layer layer); // returns if has overlay plane bool IsDynamicModel(UINT32 *data); // check if the model has a colour palette @@ -259,7 +260,7 @@ private: UINT32 m_colorTableAddr = 0x400; // address of color table in polygon RAM LODBlendTable* m_LODBlendTable; - TextureSheet m_texSheet; + GLuint m_textureBuffer; NodeAttributes m_nodeAttribs; Mat4 m_modelMat; // current modelview matrix @@ -280,6 +281,7 @@ private: std::vector m_polyBufferRom; // rom polys std::unordered_map>> m_romMap; // a hash table for all the ROM models. The meshes don't have model matrices or tex offsets yet + GLuint m_vao; VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow R3DShader m_r3dShader; R3DScrollFog m_r3dScrollFog; diff --git a/Src/Graphics/New3D/R3DFrameBuffers.cpp b/Src/Graphics/New3D/R3DFrameBuffers.cpp index 303ee9f..4f11ffd 100644 --- a/Src/Graphics/New3D/R3DFrameBuffers.cpp +++ b/Src/Graphics/New3D/R3DFrameBuffers.cpp @@ -13,6 +13,7 @@ R3DFrameBuffers::R3DFrameBuffers() m_renderBufferIDCopy = 0; m_width = 0; m_height = 0; + m_vao = 0; for (auto &i : m_texIDs) { i = 0; @@ -24,13 +25,10 @@ R3DFrameBuffers::R3DFrameBuffers() AllocShaderBase(); AllocShaderWipe(); - FBVertex vertices[4]; - vertices[0].Set(-1,-1, 0, 0); - vertices[1].Set(-1, 1, 0, 1); - vertices[2].Set( 1,-1, 1, 0); - vertices[3].Set( 1, 1, 1, 1); - - m_vbo.Create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, sizeof(vertices), vertices); + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + // no states needed since we do it in the shader + glBindVertexArray(0); } R3DFrameBuffers::~R3DFrameBuffers() @@ -39,7 +37,10 @@ R3DFrameBuffers::~R3DFrameBuffers() m_shaderTrans.UnloadShaders(); m_shaderBase.UnloadShaders(); m_shaderWipe.UnloadShaders(); - m_vbo.Destroy(); + if (m_vao) { + glDeleteVertexArrays(1, &m_vao); + m_vao = 0; + } } bool R3DFrameBuffers::CreateFBO(int width, int height) @@ -201,80 +202,86 @@ void R3DFrameBuffers::AllocShaderBase() { const char *vertexShader = R"glsl( - #version 120 - - // inputs - attribute vec3 inVertex; - attribute vec2 inTexCoord; + #version 410 core // outputs - varying vec2 fsTexCoord; + out vec2 fsTexCoord; void main(void) { - fsTexCoord = inTexCoord; - gl_Position = vec4(inVertex,1.0); + const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4( 1.0, -1.0, 0.0, 1.0), + vec4( 1.0, 1.0, 0.0, 1.0)); + + fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0; + gl_Position = vertices[gl_VertexID % 4]; } )glsl"; const char *fragmentShader = R"glsl( - #version 120 + #version 410 core + // inputs uniform sampler2D tex1; // base tex + in vec2 fsTexCoord; - varying vec2 fsTexCoord; + // outputs + out vec4 fragColor; void main() { - vec4 colBase = texture2D( tex1, fsTexCoord); + vec4 colBase = texture(tex1, fsTexCoord); if(colBase.a < 1.0) discard; - gl_FragColor = colBase; + fragColor = colBase; } )glsl"; m_shaderBase.LoadShaders(vertexShader, fragmentShader); m_shaderBase.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1"); - m_shaderBase.attribLoc[0] = m_shaderTrans.GetAttributeLocation("inVertex"); - m_shaderBase.attribLoc[1] = m_shaderTrans.GetAttributeLocation("inTexCoord"); } void R3DFrameBuffers::AllocShaderTrans() { const char *vertexShader = R"glsl( - #version 120 - - // inputs - attribute vec3 inVertex; - attribute vec2 inTexCoord; + #version 410 core // outputs - varying vec2 fsTexCoord; + out vec2 fsTexCoord; void main(void) { - fsTexCoord = inTexCoord; - gl_Position = vec4(inVertex,1.0); + const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4( 1.0, -1.0, 0.0, 1.0), + vec4( 1.0, 1.0, 0.0, 1.0)); + + fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0; + gl_Position = vertices[gl_VertexID % 4]; } )glsl"; const char *fragmentShader = R"glsl( - #version 120 + #version 410 core uniform sampler2D tex1; // trans layer 1 uniform sampler2D tex2; // trans layer 2 - varying vec2 fsTexCoord; + in vec2 fsTexCoord; + + // outputs + out vec4 fragColor; void main() { - vec4 colTrans1 = texture2D( tex1, fsTexCoord); - vec4 colTrans2 = texture2D( tex2, fsTexCoord); + vec4 colTrans1 = texture( tex1, fsTexCoord); + vec4 colTrans2 = texture( tex2, fsTexCoord); if(colTrans1.a+colTrans2.a > 0.0) { vec3 col1 = colTrans1.rgb * colTrans1.a; @@ -284,7 +291,7 @@ void R3DFrameBuffers::AllocShaderTrans() colTrans1.a+colTrans2.a); } - gl_FragColor = colTrans1; + fragColor = colTrans1; } )glsl"; @@ -293,49 +300,51 @@ void R3DFrameBuffers::AllocShaderTrans() m_shaderTrans.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1"); m_shaderTrans.uniformLoc[1] = m_shaderTrans.GetUniformLocation("tex2"); - - m_shaderTrans.attribLoc[0] = m_shaderTrans.GetAttributeLocation("inVertex"); - m_shaderTrans.attribLoc[1] = m_shaderTrans.GetAttributeLocation("inTexCoord"); } void R3DFrameBuffers::AllocShaderWipe() { const char *vertexShader = R"glsl( - #version 120 - - // inputs - attribute vec3 inVertex; - attribute vec2 inTexCoord; + #version 410 core // outputs - varying vec2 fsTexCoord; + out vec2 fsTexCoord; void main(void) { - fsTexCoord = inTexCoord; - gl_Position = vec4(inVertex,1.0); + const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4( 1.0, -1.0, 0.0, 1.0), + vec4( 1.0, 1.0, 0.0, 1.0)); + + fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0; + gl_Position = vertices[gl_VertexID % 4]; } )glsl"; const char *fragmentShader = R"glsl( - #version 120 + #version 410 core uniform sampler2D texColor; // base colour layer - varying vec2 fsTexCoord; + in vec2 fsTexCoord; + + // outputs + layout (location = 0) out vec4 fragColor0; + layout (location = 1) out vec4 fragColor1; void main() { - vec4 colBase = texture2D( texColor, fsTexCoord); + vec4 colBase = texture(texColor, fsTexCoord); if(colBase.a == 0.0) { - discard; // no colour pixels have been written + discard; // no colour pixels have been written } - gl_FragData[0] = vec4(0.0); // wipe these parts of the alpha buffer - gl_FragData[1] = vec4(0.0); // since they have been overwritten by the next priority layer + fragColor0 = vec4(0.0); // wipe these parts of the alpha buffer + fragColor1 = vec4(0.0); // since they have been overwritten by the next priority layer } )glsl"; @@ -343,9 +352,6 @@ void R3DFrameBuffers::AllocShaderWipe() m_shaderWipe.LoadShaders(vertexShader, fragmentShader); m_shaderWipe.uniformLoc[0] = m_shaderTrans.GetUniformLocation("texColor"); - - m_shaderWipe.attribLoc[0] = m_shaderTrans.GetAttributeLocation("inVertex"); - m_shaderWipe.attribLoc[1] = m_shaderTrans.GetAttributeLocation("inTexCoord"); } void R3DFrameBuffers::Draw() @@ -361,18 +367,18 @@ void R3DFrameBuffers::Draw() glBindTexture(GL_TEXTURE_2D, m_texIDs[i]); } - glActiveTexture (GL_TEXTURE0); - m_vbo.Bind (true); + glActiveTexture (GL_TEXTURE0); + glBindVertexArray (m_vao); - DrawBaseLayer (); + DrawBaseLayer (); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); - DrawAlphaLayer (); + DrawAlphaLayer (); - glDisable (GL_BLEND); - m_vbo.Bind (false); + glDisable (GL_BLEND); + glBindVertexArray (0); } void R3DFrameBuffers::CompositeBaseLayer() @@ -389,11 +395,11 @@ void R3DFrameBuffers::CompositeBaseLayer() } glActiveTexture(GL_TEXTURE0); - m_vbo.Bind(true); + glBindVertexArray(m_vao); DrawBaseLayer(); - m_vbo.Bind(false); + glBindVertexArray(0); } void R3DFrameBuffers::CompositeAlphaLayer() @@ -409,15 +415,15 @@ void R3DFrameBuffers::CompositeAlphaLayer() } glActiveTexture(GL_TEXTURE0); - m_vbo.Bind(true); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); + glBindVertexArray(m_vao); DrawAlphaLayer(); glDisable(GL_BLEND); - m_vbo.Bind(false); + glBindVertexArray(0); } void R3DFrameBuffers::DrawOverTransLayers() @@ -431,44 +437,24 @@ void R3DFrameBuffers::DrawOverTransLayers() glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, m_texIDs[0]); - - m_vbo.Bind(true); - + + glBindVertexArray(m_vao); m_shaderWipe.EnableShader(); glUniform1i(m_shaderWipe.uniformLoc[0], 0); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glVertexAttribPointer(m_shaderWipe.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts)); - glVertexAttribPointer(m_shaderWipe.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords)); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); m_shaderWipe.DisableShader(); - - m_vbo.Bind(false); + glBindVertexArray(0); } void R3DFrameBuffers::DrawBaseLayer() { m_shaderBase.EnableShader(); - glUniform1i(m_shaderTrans.uniformLoc[0], 0); - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glVertexAttribPointer(m_shaderTrans.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts)); - glVertexAttribPointer(m_shaderTrans.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords)); + glUniform1i(m_shaderTrans.uniformLoc[0], 0); // to do check this glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - m_shaderBase.DisableShader(); } @@ -478,17 +464,8 @@ void R3DFrameBuffers::DrawAlphaLayer() glUniform1i(m_shaderTrans.uniformLoc[0], 1); // tex unit 1 glUniform1i(m_shaderTrans.uniformLoc[1], 2); // tex unit 2 - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glVertexAttribPointer(m_shaderTrans.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts)); - glVertexAttribPointer(m_shaderTrans.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords)); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - m_shaderTrans.DisableShader(); } diff --git a/Src/Graphics/New3D/R3DFrameBuffers.h b/Src/Graphics/New3D/R3DFrameBuffers.h index 1a73fb3..19a8596 100644 --- a/Src/Graphics/New3D/R3DFrameBuffers.h +++ b/Src/Graphics/New3D/R3DFrameBuffers.h @@ -29,21 +29,6 @@ public: private: - struct FBVertex - { - void Set(float x, float y, float s, float t) - { - texCoords[0] = s; - texCoords[1] = t; - verts[0] = x; - verts[1] = y; - verts[2] = 0.f; // z = 0 - } - - float texCoords[2]; - float verts[3]; - }; - bool CreateFBODepthCopy(int width, int height); GLuint CreateTexture(int width, int height); void AllocShaderTrans(); @@ -67,8 +52,8 @@ private: GLSLShader m_shaderTrans; GLSLShader m_shaderWipe; - // vertices for fbo - VBO m_vbo; + // vao + GLuint m_vao; // this really needed if we don't actually use vertex attribs? }; } diff --git a/Src/Graphics/New3D/R3DScrollFog.cpp b/Src/Graphics/New3D/R3DScrollFog.cpp index be8e1af..6305c7a 100644 --- a/Src/Graphics/New3D/R3DScrollFog.cpp +++ b/Src/Graphics/New3D/R3DScrollFog.cpp @@ -1,26 +1,27 @@ #include "R3DScrollFog.h" #include "Graphics/Shader.h" -#include "Mat4.h" namespace New3D { static const char *vertexShaderFog = R"glsl( -#version 120 - -uniform mat4 mvp; -attribute vec3 inVertex; +#version 410 core void main(void) { - gl_Position = mvp * vec4(inVertex,1.0); + const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4( 1.0, -1.0, 0.0, 1.0), + vec4( 1.0, 1.0, 0.0, 1.0)); + + gl_Position = vertices[gl_VertexID % 4]; } )glsl"; static const char *fragmentShaderFog = R"glsl( -#version 120 +#version 410 core uniform float fogAttenuation; uniform float fogAmbient; @@ -38,6 +39,9 @@ float lfogAttenuation; vec3 lFogColor; vec4 scrollFog; +// outputs +out vec4 fragColor; + void main() { // Scroll fog base color @@ -58,67 +62,59 @@ void main() scrollFog = vec4(lFogColor + lSpotFogColor, fogColour.a); // Final Color - gl_FragColor = scrollFog; + fragColor = scrollFog; } )glsl"; R3DScrollFog::R3DScrollFog(const Util::Config::Node &config) - : m_config(config) + : m_config(config), + m_vao(0) + { - //default coordinates are NDC -1,1 etc - - m_triangles[0].p1.Set(-1,-1, 0); - m_triangles[0].p2.Set(-1, 1, 0); - m_triangles[0].p3.Set( 1, 1, 0); - - m_triangles[1].p1.Set(-1,-1, 0); - m_triangles[1].p2.Set( 1, 1, 0); - m_triangles[1].p3.Set( 1,-1, 0); - m_shaderProgram = 0; m_vertexShader = 0; m_fragmentShader = 0; AllocResources(); + + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + // no states needed since we do it in the shader + glBindVertexArray(0); } R3DScrollFog::~R3DScrollFog() { DeallocResources(); + + if (m_vao) { + glDeleteVertexArrays(1, &m_vao); + m_vao = 0; + } } void R3DScrollFog::DrawScrollFog(float rgba[4], float attenuation, float ambient, float *spotRGB, float *spotEllipse) { - //======= - Mat4 mvp; - //======= - - // yeah this would have been much easier with immediate mode and fixed function .. >_< - // some ogl states glDepthMask (GL_FALSE); // disable z writes glDisable (GL_DEPTH_TEST); // disable depth testing glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m_vbo.Bind (true); + glBindVertexArray (m_vao); glUseProgram (m_shaderProgram); - glUniform4f (m_locFogColour, rgba[0], rgba[1], rgba[2], rgba[3]); + glUniform4fv (m_locFogColour, 1, rgba); glUniform1f (m_locFogAttenuation, attenuation); glUniform1f (m_locFogAmbient, ambient); - glUniform3f (m_locSpotFogColor, spotRGB[0], spotRGB[1], spotRGB[2]); - glUniform4f (m_locSpotEllipse, spotEllipse[0], spotEllipse[1], spotEllipse[2], spotEllipse[3]); - glUniformMatrix4fv (m_locMVP, 1, GL_FALSE, mvp); + glUniform3fv (m_locSpotFogColor, 1, spotRGB); + glUniform4fv (m_locSpotEllipse, 1, spotEllipse); - glEnableVertexAttribArray (0); - glVertexAttribPointer (m_locInVertex, 3, GL_FLOAT, GL_FALSE, sizeof(SFVertex), 0); - glDrawArrays (GL_TRIANGLES, 0, 6); - glDisableVertexAttribArray (0); + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); glUseProgram (0); - m_vbo.Bind (false); + glBindVertexArray (0); glDisable (GL_BLEND); glDepthMask (GL_TRUE); @@ -128,16 +124,11 @@ void R3DScrollFog::AllocResources() { bool success = LoadShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, m_config["VertexShaderFog"].ValueAs(), m_config["FragmentShaderFog"].ValueAs(), vertexShaderFog, fragmentShaderFog); - m_locMVP = glGetUniformLocation(m_shaderProgram, "mvp"); m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour"); m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation"); m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient"); m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor"); m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse"); - - m_locInVertex = glGetAttribLocation(m_shaderProgram, "inVertex"); - - m_vbo.Create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, sizeof(SFTriangle) * (2), m_triangles); } void R3DScrollFog::DeallocResources() @@ -149,8 +140,6 @@ void R3DScrollFog::DeallocResources() m_shaderProgram = 0; m_vertexShader = 0; m_fragmentShader = 0; - - m_vbo.Destroy(); } } diff --git a/Src/Graphics/New3D/R3DScrollFog.h b/Src/Graphics/New3D/R3DScrollFog.h index d00ce31..6e93449 100644 --- a/Src/Graphics/New3D/R3DScrollFog.h +++ b/Src/Graphics/New3D/R3DScrollFog.h @@ -2,7 +2,7 @@ #define _R3DSCROLLFOG_H_ #include "Util/NewConfig.h" -#include "VBO.h" +#include namespace New3D { @@ -22,41 +22,17 @@ private: const Util::Config::Node &m_config; - struct SFVertex - { - void Set(float x, float y, float z) { - v[0] = x; - v[1] = y; - v[2] = z; - } - - float v[3]; - }; - - struct SFTriangle - { - SFVertex p1; - SFVertex p2; - SFVertex p3; - }; - - SFTriangle m_triangles[2]; - GLuint m_shaderProgram; GLuint m_vertexShader; GLuint m_fragmentShader; GLint m_locFogColour; - GLint m_locMVP; GLint m_locFogAttenuation; GLint m_locFogAmbient; GLint m_locSpotFogColor; GLint m_locSpotEllipse; - // vertex attrib locs - GLint m_locInVertex; - - VBO m_vbo; + GLuint m_vao; }; } diff --git a/Src/Graphics/New3D/R3DShader.cpp b/Src/Graphics/New3D/R3DShader.cpp index 1356be8..922eb8d 100644 --- a/Src/Graphics/New3D/R3DShader.cpp +++ b/Src/Graphics/New3D/R3DShader.cpp @@ -34,9 +34,18 @@ void R3DShader::Start() m_shininess = 0; m_specularValue = 0; m_microTexScale = 0; + m_microTexID = -1; - m_baseTexSize[0] = 0; - m_baseTexSize[1] = 0; + m_baseTexInfo[0] = -1; + m_baseTexInfo[1] = -1; + m_baseTexInfo[2] = -1; + m_baseTexInfo[3] = -1; + + m_baseTexType = -1; + + m_transX = -1; + m_transY = -1; + m_transPage = -1; m_texWrapMode[0] = 0; m_texWrapMode[1] = 0; @@ -53,10 +62,15 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader) const char* gShader = ""; const char* fShader = fragmentShaderR3D; + std::string fragmentShaderCombined; + if (quads) { vShader = vertexShaderR3DQuads; gShader = geometryShaderR3DQuads; - fShader = fragmentShaderR3DQuads; + + fragmentShaderCombined += fragmentShaderR3DQuads1; + fragmentShaderCombined += fragmentShaderR3DQuads2; + fShader = fragmentShaderCombined.c_str(); } m_shaderProgram = glCreateProgram(); @@ -87,13 +101,14 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader) PrintProgramResult(m_shaderProgram); m_locTexture1 = glGetUniformLocation(m_shaderProgram, "tex1"); - m_locTexture2 = glGetUniformLocation(m_shaderProgram, "tex2"); m_locTexture1Enabled = glGetUniformLocation(m_shaderProgram, "textureEnabled"); m_locTexture2Enabled = glGetUniformLocation(m_shaderProgram, "microTexture"); m_locTextureAlpha = glGetUniformLocation(m_shaderProgram, "textureAlpha"); m_locAlphaTest = glGetUniformLocation(m_shaderProgram, "alphaTest"); m_locMicroTexScale = glGetUniformLocation(m_shaderProgram, "microTextureScale"); - m_locBaseTexSize = glGetUniformLocation(m_shaderProgram, "baseTexSize"); + m_locMicroTexID = glGetUniformLocation(m_shaderProgram, "microTextureID"); + m_locBaseTexInfo = glGetUniformLocation(m_shaderProgram, "baseTexInfo"); + m_locBaseTexType = glGetUniformLocation(m_shaderProgram, "baseTexType"); m_locTextureInverted = glGetUniformLocation(m_shaderProgram, "textureInverted"); m_locTexWrapMode = glGetUniformLocation(m_shaderProgram, "textureWrapMode"); @@ -129,12 +144,37 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader) return true; } +void R3DShader::UnloadShader() +{ + // make sure no shader is bound + glUseProgram(0); + + if (m_vertexShader) { + glDeleteShader(m_vertexShader); + m_vertexShader = 0; + } + + if (m_geoShader) { + glDeleteShader(m_geoShader); + m_geoShader = 0; + } + + if (m_fragmentShader) { + glDeleteShader(m_fragmentShader); + m_fragmentShader = 0; + } + + if (m_shaderProgram) { + glDeleteProgram(m_shaderProgram); + m_shaderProgram = 0; + } +} + GLint R3DShader::GetVertexAttribPos(const std::string& attrib) { if (m_vertexLocCache.count(attrib)==0) { auto pos = glGetAttribLocation(m_shaderProgram, attrib.c_str()); m_vertexLocCache[attrib] = pos; - return pos; } return m_vertexLocCache[attrib]; @@ -160,7 +200,6 @@ void R3DShader::SetMeshUniforms(const Mesh* m) if (m_dirtyMesh) { glUniform1i(m_locTexture1, 0); - glUniform1i(m_locTexture2, 1); } if (m_dirtyMesh || m->textured != m_textured1) { @@ -178,10 +217,27 @@ void R3DShader::SetMeshUniforms(const Mesh* m) m_microTexScale = m->microTextureScale; } - if (m_dirtyMesh || (m_baseTexSize[0] != m->width || m_baseTexSize[1] != m->height)) { - m_baseTexSize[0] = (float)m->width; - m_baseTexSize[1] = (float)m->height; - glUniform2fv(m_locBaseTexSize, 1, m_baseTexSize); + if (m_dirtyMesh || m->microTextureID != m_microTexID) { + glUniform1i(m_locMicroTexID, m->microTextureID); + m_microTexID = m->microTextureID; + } + + if (m_dirtyMesh || (m_baseTexInfo[0] != m->x || m_baseTexInfo[1] != m->y) || m_baseTexInfo[2] != m->width || m_baseTexInfo[3] != m->height) { + + m_baseTexInfo[0] = m->x; + m_baseTexInfo[1] = m->y; + m_baseTexInfo[2] = m->width; + m_baseTexInfo[3] = m->height; + + int translatedX, translatedY; + CalcTexOffset(m_transX, m_transY, m_transPage, m->x, m->y, translatedX, translatedY); // need to apply model translation + + glUniform4i(m_locBaseTexInfo, translatedX, translatedY, m->width, m->height); + } + + if (m_dirtyMesh || m_baseTexType != m->format) { + m_baseTexType = m->format; + glUniform1i(m_locBaseTexType, m_baseTexType); } if (m_dirtyMesh || m->inverted != m_textureInverted) { @@ -282,6 +338,13 @@ void R3DShader::SetModelStates(const Model* model) m_modelScale = model->scale; } + m_transX = model->textureOffsetX; + m_transY = model->textureOffsetY; + m_transPage = model->page; + + // reset texture values + for (auto& i : m_baseTexInfo) { i = -1; } + glUniformMatrix4fv(m_locModelMat, 1, GL_FALSE, model->modelMat); m_dirtyModel = false; @@ -332,4 +395,22 @@ void R3DShader::PrintProgramResult(GLuint program) printf("%s\n", infoLog.data()); } } + +void R3DShader::CalcTexOffset(int offX, int offY, int page, int x, int y, int& newX, int& newY) +{ + newX = (x + offX) & 2047; // wrap around 2048, shouldn't be required + + int oldPage = y / 1024; + + y -= (oldPage * 1024); // remove page from tex y + + // calc newY with wrap around, wraps around in the same sheet, not into another memory sheet + + newY = (y + offY) & 1023; + + // add page to Y + + newY += ((oldPage + page) & 1) * 1024; // max page 0-1 +} + } // New3D diff --git a/Src/Graphics/New3D/R3DShader.h b/Src/Graphics/New3D/R3DShader.h index 226dddf..af74905 100644 --- a/Src/Graphics/New3D/R3DShader.h +++ b/Src/Graphics/New3D/R3DShader.h @@ -4,7 +4,7 @@ #include #include "Util/NewConfig.h" #include "Model.h" -#include +#include #include namespace New3D { @@ -15,6 +15,7 @@ public: R3DShader(const Util::Config::Node &config); bool LoadShader (const char* vertexShader = nullptr, const char* fragmentShader = nullptr); + void UnloadShader (); void SetMeshUniforms (const Mesh* m); void SetModelStates (const Model* model); void SetViewportUniforms (const Viewport *vp); @@ -28,6 +29,8 @@ private: void PrintShaderResult(GLuint shader); void PrintProgramResult(GLuint program); + void CalcTexOffset(int offX, int offY, int page, int x, int y, int& newX, int& newY); + // run-time config const Util::Config::Node &m_config; @@ -39,13 +42,14 @@ private: // mesh uniform locations GLint m_locTexture1; - GLint m_locTexture2; GLint m_locTexture1Enabled; GLint m_locTexture2Enabled; GLint m_locTextureAlpha; GLint m_locAlphaTest; GLint m_locMicroTexScale; - GLint m_locBaseTexSize; + GLint m_locMicroTexID; + GLint m_locBaseTexInfo; + GLint m_locBaseTexType; GLint m_locTextureInverted; GLint m_locTexWrapMode; GLint m_locTranslatorMap; @@ -65,12 +69,17 @@ private: bool m_layered; float m_microTexScale; - float m_baseTexSize[2]; + int m_microTexID; + int m_baseTexInfo[4]; + int m_baseTexType; int m_texWrapMode[2]; bool m_textureInverted; // cached model values float m_modelScale; + int m_transX; + int m_transY; + int m_transPage; // are our cache values dirty bool m_dirtyMesh; @@ -109,11 +118,11 @@ private: GLint m_locDiscardAlpha; // vertex attribute position cache - std::unordered_map m_vertexLocCache; + std::map m_vertexLocCache; }; } // New3D -#endif +#endif \ No newline at end of file diff --git a/Src/Graphics/New3D/R3DShaderQuads.h b/Src/Graphics/New3D/R3DShaderQuads.h index 209fb5a..ced2ad3 100644 --- a/Src/Graphics/New3D/R3DShaderQuads.h +++ b/Src/Graphics/New3D/R3DShaderQuads.h @@ -168,18 +168,19 @@ void main(void) )glsl"; -static const char *fragmentShaderR3DQuads = R"glsl( +static const char *fragmentShaderR3DQuads1 = R"glsl( #version 450 core -uniform sampler2D tex1; // base tex -uniform sampler2D tex2; // micro tex (optional) +uniform usampler2D tex1; // entire texture sheet // texturing uniform bool textureEnabled; uniform bool microTexture; uniform float microTextureScale; -uniform vec2 baseTexSize; +uniform int microTextureID; +uniform ivec4 baseTexInfo; // x/y are x,y positions in the texture sheet. z/w are with and height +uniform int baseTexType; uniform bool textureInverted; uniform bool textureAlpha; uniform bool alphaTest; @@ -207,7 +208,7 @@ uniform float fogAmbient; uniform bool fixedShading; uniform int hardwareStep; -// test +// matrices (shared with vertex shader) uniform mat4 projMat; //interpolated inputs from geometry shader @@ -340,6 +341,140 @@ void QuadraticInterpolation() gl_FragDepth = depth * 0.5 + 0.5; } +vec4 ExtractColour(int type, uint value) +{ + vec4 c = vec4(0.0); + + if(type==0) { // T1RGB5 + c.r = float((value >> 10) & 0x1F) / 31.0; + c.g = float((value >> 5 ) & 0x1F) / 31.0; + c.b = float((value ) & 0x1F) / 31.0; + c.a = 1.0 - float((value >> 15) & 0x1); + } + else if(type==1) { // Interleaved A4L4 (low byte) + c.rgb = vec3(float(value&0xF) / 15.0); + c.a = float((value >> 4) & 0xF) / 15.0; + } + else if(type==2) { + c.a = float(value&0xF) / 15.0; + c.rgb = vec3(float((value >> 4) & 0xF) / 15.0); + } + else if(type==3) { + c.rgb = vec3(float((value>>8)&0xF) / 15.0); + c.a = float((value >> 12) & 0xF) / 15.0; + } + else if(type==4) { + c.a = float((value>>8)&0xF) / 15.0; + c.rgb = vec3(float((value >> 12) & 0xF) / 15.0); + } + else if(type==5) { + c = vec4(float(value&0xFF) / 255.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==6) { + c = vec4(float((value>>8)&0xFF) / 255.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==7) { // RGBA4 + c.r = float((value>>12)&0xF) / 15.0; + c.g = float((value>> 8)&0xF) / 15.0; + c.b = float((value>> 4)&0xF) / 15.0; + c.a = float((value>> 0)&0xF) / 15.0; + } + else if(type==8) { // low byte, low nibble + c = vec4(float(value&0xF) / 15.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==9) { // low byte, high nibble + c = vec4(float((value>>4)&0xF) / 15.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==10) { // high byte, low nibble + c = vec4(float((value>>8)&0xF) / 15.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==11) { // high byte, high nibble + c = vec4(float((value>>12)&0xF) / 15.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + + return c; +} + +ivec2 GetTexturePosition(int level, ivec2 pos) +{ + const int mipXBase[] = { 0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047 }; + const int mipYBase[] = { 0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023 }; + + int mipDivisor = 1 << level; + + int page = pos.y / 1024; + pos.y -= (page * 1024); // remove page from tex y + + ivec2 retPos; + retPos.x = mipXBase[level] + (pos.x / mipDivisor); + retPos.y = mipYBase[level] + (pos.y / mipDivisor); + + retPos.y += (page * 1024); // add page back to tex y + + return retPos; +} + +ivec2 GetTextureSize(int level, ivec2 size) +{ + int mipDivisor = 1 << level; + + return size / mipDivisor; +} + +ivec2 GetMicroTexturePos(int id) +{ + int xCoords[8] = { 0, 0, 128, 128, 0, 0, 128, 128 }; + int yCoords[8] = { 0, 128, 0, 128, 256, 384, 256, 384 }; + + return ivec2(xCoords[id],yCoords[id]); +} + +int GetPage(int yCoord) +{ + return yCoord / 1024; +} + +int GetNextPage(int yCoord) +{ + return (GetPage(yCoord) + 1) & 1; +} + +int GetNextPageOffset(int yCoord) +{ + return GetNextPage(yCoord) * 1024; +} + +// wrapping tex coords would be super easy but we combined tex sheets so have to handle wrap around between sheets +// hardware testing would be useful because i don't know exactly what happens if you try to read outside the texture sheet +// wrap around is a good guess +ivec2 WrapTexCoords(ivec2 pos, ivec2 coordinate) +{ + ivec2 newCoord; + + newCoord.x = coordinate.x & 2047; + newCoord.y = coordinate.y; + + int page = GetPage(pos.y); + + newCoord.y -= (page * 1024); // remove page + newCoord.y &= 1023; // wrap around in the same sheet + newCoord.y += (page * 1024); // add page back + + return newCoord; +} + float mip_map_level(in vec2 texture_coordinate) // in texel units { vec2 dx_vtc = dFdx(texture_coordinate); @@ -396,16 +531,16 @@ float LinearTexLocations(int wrapMode, float size, float u, out float u0, out fl } } -vec4 texBiLinear(sampler2D texSampler, float level, ivec2 wrapMode, vec2 texSize, vec2 texCoord) +vec4 texBiLinear(usampler2D texSampler, ivec2 wrapMode, vec2 texSize, ivec2 texPos, vec2 texCoord) { float tx[2], ty[2]; float a = LinearTexLocations(wrapMode.s, texSize.x, texCoord.x, tx[0], tx[1]); float b = LinearTexLocations(wrapMode.t, texSize.y, texCoord.y, ty[0], ty[1]); - - vec4 p0q0 = textureLod(texSampler, vec2(tx[0],ty[0]), level); - vec4 p1q0 = textureLod(texSampler, vec2(tx[1],ty[0]), level); - vec4 p0q1 = textureLod(texSampler, vec2(tx[0],ty[1]), level); - vec4 p1q1 = textureLod(texSampler, vec2(tx[1],ty[1]), level); + + vec4 p0q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[0]) * texSize + texPos)), 0).r); + vec4 p1q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[0]) * texSize + texPos)), 0).r); + vec4 p0q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[1]) * texSize + texPos)), 0).r); + vec4 p1q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[1]) * texSize + texPos)), 0).r); if(alphaTest) { if(p0q0.a > p1q0.a) { p1q0.rgb = p0q0.rgb; } @@ -428,47 +563,58 @@ vec4 texBiLinear(sampler2D texSampler, float level, ivec2 wrapMode, vec2 texSize return mix( pInterp_q0, pInterp_q1, b ); // Interpolate in Y direction. } -vec4 textureR3D(sampler2D texSampler, ivec2 wrapMode, vec2 texSize, vec2 texCoord) +vec4 textureR3D(usampler2D texSampler, ivec2 wrapMode, ivec2 texSize, ivec2 texPos, vec2 texCoord) { - float numLevels = floor(log2(min(texSize.x, texSize.y))); // r3d only generates down to 1:1 for square textures, otherwise its the min dimension - float fLevel = min(mip_map_level(texCoord * texSize), numLevels); + float numLevels = floor(log2(min(float(texSize.x), float(texSize.y)))); // r3d only generates down to 1:1 for square textures, otherwise its the min dimension + float fLevel = min(mip_map_level(texCoord * vec2(texSize)), numLevels); - fLevel *= alphaTest ? 0.5 : 0.8; + if(alphaTest) fLevel *= 0.5; + else fLevel *= 0.8; - float iLevel = floor(fLevel); // value as an 'int' + int iLevel = int(fLevel); - vec2 texSize0 = texSize / exp2(iLevel); - vec2 texSize1 = texSize / exp2(iLevel+1.0); + ivec2 texPos0 = GetTexturePosition(iLevel,texPos); + ivec2 texPos1 = GetTexturePosition(iLevel+1,texPos); - vec4 texLevel0 = texBiLinear(texSampler, iLevel, wrapMode, texSize0, texCoord); - vec4 texLevel1 = texBiLinear(texSampler, iLevel+1.0, wrapMode, texSize1, texCoord); + ivec2 texSize0 = GetTextureSize(iLevel, texSize); + ivec2 texSize1 = GetTextureSize(iLevel+1, texSize); + + vec4 texLevel0 = texBiLinear(texSampler, wrapMode, vec2(texSize0), texPos0, texCoord); + vec4 texLevel1 = texBiLinear(texSampler, wrapMode, vec2(texSize1), texPos1, texCoord); return mix(texLevel0, texLevel1, fract(fLevel)); // linear blend between our mipmap levels } vec4 GetTextureValue() { - vec4 tex1Data = textureR3D(tex1, textureWrapMode, baseTexSize, fsTexCoord); + vec4 tex1Data = textureR3D(tex1, textureWrapMode, ivec2(baseTexInfo.zw), ivec2(baseTexInfo.xy), fsTexCoord); if(textureInverted) { tex1Data.rgb = vec3(1.0) - vec3(tex1Data.rgb); } if (microTexture) { - vec2 scale = (baseTexSize / 128.0) * microTextureScale; - vec4 tex2Data = textureR3D( tex2, ivec2(0.0), vec2(128.0), fsTexCoord * scale); + vec2 scale = (vec2(baseTexInfo.zw) / 128.0) * microTextureScale; + ivec2 pos = GetMicroTexturePos(microTextureID); + + // add page offset to microtexture position + pos.y += GetNextPageOffset(baseTexInfo.y); + + vec4 tex2Data = textureR3D(tex1, ivec2(0), ivec2(128), pos, fsTexCoord * scale); float lod = mip_map_level(fsTexCoord * scale * vec2(128.0)); float blendFactor = max(lod - 1.5, 0.0); // bias -1.5 blendFactor = min(blendFactor, 1.0); // clamp to max value 1 - blendFactor = blendFactor * 0.5 + 0.5; // 0.5 - 1 range + blendFactor = (blendFactor + 1.0) / 2.0; // 0.5 - 1 range tex1Data = mix(tex2Data, tex1Data, blendFactor); } - if (alphaTest && (tex1Data.a < (32.0/255.0))) { - discard; + if (alphaTest) { + if (tex1Data.a < (32.0/255.0)) { + discard; + } } if(textureAlpha) { @@ -484,7 +630,7 @@ vec4 GetTextureValue() } } - if (!textureAlpha) { + if (textureAlpha == false) { tex1Data.a = 1.0; } @@ -526,6 +672,10 @@ float sqr_length(vec2 a) return a.x*a.x + a.y*a.y; } +)glsl"; + +static const char* fragmentShaderR3DQuads2 = R"glsl( + void main() { vec4 tex1Data; diff --git a/Src/Graphics/New3D/R3DShaderTriangles.h b/Src/Graphics/New3D/R3DShaderTriangles.h index 574782b..274958d 100644 --- a/Src/Graphics/New3D/R3DShaderTriangles.h +++ b/Src/Graphics/New3D/R3DShaderTriangles.h @@ -3,7 +3,7 @@ static const char *vertexShaderR3D = R"glsl( -#version 120 +#version 410 core // uniforms uniform float modelScale; @@ -12,20 +12,20 @@ uniform mat4 projMat; uniform bool translatorMap; // attributes -attribute vec4 inVertex; -attribute vec3 inNormal; -attribute vec2 inTexCoord; -attribute vec4 inColour; -attribute vec3 inFaceNormal; // used to emulate r3d culling -attribute float inFixedShade; +in vec4 inVertex; +in vec3 inNormal; +in vec2 inTexCoord; +in vec4 inColour; +in vec3 inFaceNormal; // used to emulate r3d culling +in float inFixedShade; // outputs to fragment shader -varying vec3 fsViewVertex; -varying vec3 fsViewNormal; // per vertex normal vector -varying vec2 fsTexCoord; -varying vec4 fsColor; -varying float fsDiscard; // can't have varying bool (glsl spec) -varying float fsFixedShade; +out vec3 fsViewVertex; +out vec3 fsViewNormal; // per vertex normal vector +out vec2 fsTexCoord; +out vec4 fsColor; +out float fsDiscard; // can't have varying bool (glsl spec) +out float fsFixedShade; vec4 GetColour(vec4 colour) { @@ -61,17 +61,17 @@ void main(void) static const char *fragmentShaderR3D = R"glsl( -#version 120 -#extension GL_ARB_shader_texture_lod : require +#version 410 core -uniform sampler2D tex1; // base tex -uniform sampler2D tex2; // micro tex (optional) +uniform usampler2D tex1; // entire texture sheet // texturing uniform bool textureEnabled; uniform bool microTexture; uniform float microTextureScale; -uniform vec2 baseTexSize; +uniform int microTextureID; +uniform ivec4 baseTexInfo; // x/y are x,y positions in the texture sheet. z/w are with and height +uniform int baseTexType; uniform bool textureInverted; uniform bool textureAlpha; uniform bool alphaTest; @@ -100,12 +100,149 @@ uniform bool fixedShading; uniform int hardwareStep; //interpolated inputs from vertex shader -varying vec3 fsViewVertex; -varying vec3 fsViewNormal; // per vertex normal vector -varying vec4 fsColor; -varying vec2 fsTexCoord; -varying float fsDiscard; -varying float fsFixedShade; +in vec3 fsViewVertex; +in vec3 fsViewNormal; // per vertex normal vector +in vec4 fsColor; +in vec2 fsTexCoord; +in float fsDiscard; +in float fsFixedShade; + +//outputs +out vec4 outColor; + +vec4 ExtractColour(int type, uint value) +{ + vec4 c = vec4(0.0); + + if(type==0) { // T1RGB5 + c.r = float((value >> 10) & 0x1F) / 31.0; + c.g = float((value >> 5 ) & 0x1F) / 31.0; + c.b = float((value ) & 0x1F) / 31.0; + c.a = 1.0 - float((value >> 15) & 0x1); + } + else if(type==1) { // Interleaved A4L4 (low byte) + c.rgb = vec3(float(value&0xF) / 15.0); + c.a = float((value >> 4) & 0xF) / 15.0; + } + else if(type==2) { + c.a = float(value&0xF) / 15.0; + c.rgb = vec3(float((value >> 4) & 0xF) / 15.0); + } + else if(type==3) { + c.rgb = vec3(float((value>>8)&0xF) / 15.0); + c.a = float((value >> 12) & 0xF) / 15.0; + } + else if(type==4) { + c.a = float((value>>8)&0xF) / 15.0; + c.rgb = vec3(float((value >> 12) & 0xF) / 15.0); + } + else if(type==5) { + c = vec4(float(value&0xFF) / 255.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==6) { + c = vec4(float((value>>8)&0xFF) / 255.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==7) { // RGBA4 + c.r = float((value>>12)&0xF) / 15.0; + c.g = float((value>> 8)&0xF) / 15.0; + c.b = float((value>> 4)&0xF) / 15.0; + c.a = float((value>> 0)&0xF) / 15.0; + } + else if(type==8) { // low byte, low nibble + c = vec4(float(value&0xF) / 15.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==9) { // low byte, high nibble + c = vec4(float((value>>4)&0xF) / 15.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==10) { // high byte, low nibble + c = vec4(float((value>>8)&0xF) / 15.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + else if(type==11) { // high byte, high nibble + c = vec4(float((value>>12)&0xF) / 15.0); + if(c.a==1.0) { c.a = 0.0; } + else { c.a = 1.0; } + } + + return c; +} + +ivec2 GetTexturePosition(int level, ivec2 pos) +{ + const int mipXBase[] = int[](0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047); + const int mipYBase[] = int[](0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023); + + int mipDivisor = 1 << level; + + int page = pos.y / 1024; + pos.y -= (page * 1024); // remove page from tex y + + ivec2 retPos; + retPos.x = mipXBase[level] + (pos.x / mipDivisor); + retPos.y = mipYBase[level] + (pos.y / mipDivisor); + + retPos.y += (page * 1024); // add page back to tex y + + return retPos; +} + +ivec2 GetTextureSize(int level, ivec2 size) +{ + int mipDivisor = 1 << level; + + return size / mipDivisor; +} + +ivec2 GetMicroTexturePos(int id) +{ + const int xCoords[8] = int[](0, 0, 128, 128, 0, 0, 128, 128); + const int yCoords[8] = int[](0, 128, 0, 128, 256, 384, 256, 384); + + return ivec2(xCoords[id],yCoords[id]); +} + +int GetPage(int yCoord) +{ + return yCoord / 1024; +} + +int GetNextPage(int yCoord) +{ + return (GetPage(yCoord) + 1) & 1; +} + +int GetNextPageOffset(int yCoord) +{ + return GetNextPage(yCoord) * 1024; +} + +// wrapping tex coords would be super easy but we combined tex sheets so have to handle wrap around between sheets +// hardware testing would be useful because i don't know exactly what happens if you try to read outside the texture sheet +// wrap around is a good guess +ivec2 WrapTexCoords(ivec2 pos, ivec2 coordinate) +{ + ivec2 newCoord; + + newCoord.x = coordinate.x & 2047; + newCoord.y = coordinate.y; + + int page = GetPage(pos.y); + + newCoord.y -= (page * 1024); // remove page + newCoord.y &= 1023; // wrap around in the same sheet + newCoord.y += (page * 1024); // add page back + + return newCoord; +} float mip_map_level(in vec2 texture_coordinate) // in texel units { @@ -163,16 +300,16 @@ float LinearTexLocations(int wrapMode, float size, float u, out float u0, out fl } } -vec4 texBiLinear(sampler2D texSampler, float level, ivec2 wrapMode, vec2 texSize, vec2 texCoord) +vec4 texBiLinear(usampler2D texSampler, ivec2 wrapMode, vec2 texSize, ivec2 texPos, vec2 texCoord) { float tx[2], ty[2]; float a = LinearTexLocations(wrapMode.s, texSize.x, texCoord.x, tx[0], tx[1]); float b = LinearTexLocations(wrapMode.t, texSize.y, texCoord.y, ty[0], ty[1]); - - vec4 p0q0 = texture2DLod(texSampler, vec2(tx[0],ty[0]), level); - vec4 p1q0 = texture2DLod(texSampler, vec2(tx[1],ty[0]), level); - vec4 p0q1 = texture2DLod(texSampler, vec2(tx[0],ty[1]), level); - vec4 p1q1 = texture2DLod(texSampler, vec2(tx[1],ty[1]), level); + + vec4 p0q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[0]) * texSize + texPos)), 0).r); + vec4 p1q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[0]) * texSize + texPos)), 0).r); + vec4 p0q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[1]) * texSize + texPos)), 0).r); + vec4 p1q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[1]) * texSize + texPos)), 0).r); if(alphaTest) { if(p0q0.a > p1q0.a) { p1q0.rgb = p0q0.rgb; } @@ -195,43 +332,51 @@ vec4 texBiLinear(sampler2D texSampler, float level, ivec2 wrapMode, vec2 texSize return mix( pInterp_q0, pInterp_q1, b ); // Interpolate in Y direction. } -vec4 textureR3D(sampler2D texSampler, ivec2 wrapMode, vec2 texSize, vec2 texCoord) +vec4 textureR3D(usampler2D texSampler, ivec2 wrapMode, ivec2 texSize, ivec2 texPos, vec2 texCoord) { - float numLevels = floor(log2(min(texSize.x, texSize.y))); // r3d only generates down to 1:1 for square textures, otherwise its the min dimension - float fLevel = min(mip_map_level(texCoord * texSize), numLevels); + float numLevels = floor(log2(min(float(texSize.x), float(texSize.y)))); // r3d only generates down to 1:1 for square textures, otherwise its the min dimension + float fLevel = min(mip_map_level(texCoord * vec2(texSize)), numLevels); if(alphaTest) fLevel *= 0.5; else fLevel *= 0.8; - float iLevel = floor(fLevel); // value as an 'int' + int iLevel = int(fLevel); - vec2 texSize0 = texSize / pow(2, iLevel); - vec2 texSize1 = texSize / pow(2, iLevel+1.0); + ivec2 texPos0 = GetTexturePosition(iLevel,texPos); + ivec2 texPos1 = GetTexturePosition(iLevel+1,texPos); - vec4 texLevel0 = texBiLinear(texSampler, iLevel, wrapMode, texSize0, texCoord); - vec4 texLevel1 = texBiLinear(texSampler, iLevel+1.0, wrapMode, texSize1, texCoord); + ivec2 texSize0 = GetTextureSize(iLevel, texSize); + ivec2 texSize1 = GetTextureSize(iLevel+1, texSize); + + vec4 texLevel0 = texBiLinear(texSampler, wrapMode, vec2(texSize0), texPos0, texCoord); + vec4 texLevel1 = texBiLinear(texSampler, wrapMode, vec2(texSize1), texPos1, texCoord); return mix(texLevel0, texLevel1, fract(fLevel)); // linear blend between our mipmap levels } vec4 GetTextureValue() { - vec4 tex1Data = textureR3D(tex1, textureWrapMode, baseTexSize, fsTexCoord); + vec4 tex1Data = textureR3D(tex1, textureWrapMode, ivec2(baseTexInfo.zw), ivec2(baseTexInfo.xy), fsTexCoord); if(textureInverted) { tex1Data.rgb = vec3(1.0) - vec3(tex1Data.rgb); } if (microTexture) { - vec2 scale = (baseTexSize / 128.0) * microTextureScale; - vec4 tex2Data = textureR3D( tex2, ivec2(0), vec2(128.0), fsTexCoord * scale); - - float lod = mip_map_level(fsTexCoord * scale * vec2(128.0)); - - float blendFactor = max(lod - 1.5, 0.0); // bias -1.5 - blendFactor = min(blendFactor, 1.0); // clamp to max value 1 - blendFactor = (blendFactor + 1.0) / 2.0; // 0.5 - 1 range - + vec2 scale = (vec2(baseTexInfo.zw) / 128.0) * microTextureScale; + ivec2 pos = GetMicroTexturePos(microTextureID); + + // add page offset to microtexture position + pos.y += GetNextPageOffset(baseTexInfo.y); + + vec4 tex2Data = textureR3D(tex1, ivec2(0), ivec2(128), pos, fsTexCoord * scale); + + float lod = mip_map_level(fsTexCoord * scale * vec2(128.0)); + + float blendFactor = max(lod - 1.5, 0.0); // bias -1.5 + blendFactor = min(blendFactor, 1.0); // clamp to max value 1 + blendFactor = (blendFactor + 1.0) / 2.0; // 0.5 - 1 range + tex1Data = mix(tex2Data, tex1Data, blendFactor); } @@ -421,7 +566,7 @@ void main() // Fog & spotlight applied finalData.rgb = mix(finalData.rgb, fogData.rgb + lSpotFogColor, fogData.a); - gl_FragColor = finalData; + outColor = finalData; } )glsl"; diff --git a/Src/Graphics/New3D/Texture.cpp b/Src/Graphics/New3D/Texture.cpp deleted file mode 100644 index dfa737b..0000000 --- a/Src/Graphics/New3D/Texture.cpp +++ /dev/null @@ -1,370 +0,0 @@ -#include "Texture.h" -#include -#include -#include - -namespace New3D { - -Texture::Texture() -{ - Reset(); -} - -Texture::~Texture() -{ - DeleteTexture(); // make sure to have valid context before destroying -} - -void Texture::DeleteTexture() -{ - if (m_textureID) { - glDeleteTextures(1, &m_textureID); - Reset(); - } -} - -void Texture::Reset() -{ - m_x = 0; - m_y = 0; - m_width = 0; - m_height = 0; - m_format = 0; - m_textureID = 0; -} - -void Texture::BindTexture() -{ - glBindTexture(GL_TEXTURE_2D, m_textureID); -} - -void Texture::GetCoordinates(UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut) -{ - uOut = (uIn*uvScale) / m_width; - vOut = (vIn*uvScale) / m_height; -} - -void Texture::GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut) -{ - uOut = (uIn*uvScale) / width; - vOut = (vIn*uvScale) / height; -} - -void Texture::UploadTextureMip(int level, const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height) -{ - int xi, yi, i; - int subWidth, subHeight; - GLubyte texel; - GLubyte c, a; - - i = 0; - - subWidth = width; - subHeight = height; - - if (subWidth + x > 2048) { - subWidth = 2048 - x; - } - - if (subHeight + y > 2048) { - subHeight = 2048 - y; - } - - switch (format) - { - default: // Debug texture - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - scratch[i++] = 255; // R - scratch[i++] = 0; // G - scratch[i++] = 0; // B - scratch[i++] = 255; // A - } - } - break; - - case 0: // T1RGB5 <- correct - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - scratch[i++] = ((src[yi * 2048 + xi] >> 10) & 0x1F) * 255 / 0x1F; // R - scratch[i++] = ((src[yi * 2048 + xi] >> 5) & 0x1F) * 255 / 0x1F; // G - scratch[i++] = ((src[yi * 2048 + xi] >> 0) & 0x1F) * 255 / 0x1F; // B - scratch[i++] = ((src[yi * 2048 + xi] & 0x8000) ? 0 : 255); // T - } - } - break; - - case 1: // Interleaved A4L4 (low byte) - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - // Interpret as A4L4 - texel = src[yi * 2048 + xi] & 0xFF; - c = (texel & 0xF) * 17; - a = (texel >> 4) * 17; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = a; - } - } - break; - - case 2: // luminance alpha texture <- this one is correct - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] & 0xFF; - c = ((texel >> 4) & 0xF) * 17; - a = (texel & 0xF) * 17; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = a; - } - } - break; - - case 3: // 8-bit, A4L4 (high byte) - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] >> 8; - c = (texel & 0xF) * 17; - a = (texel >> 4) * 17; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = a; - } - } - break; - - case 4: // 8-bit, L4A4 (high byte) - - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] >> 8; - c = ((texel >> 4) & 0xF) * 17; - a = (texel & 0xF) * 17; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = a; - } - } - break; - - case 5: // 8-bit grayscale - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] & 0xFF; - scratch[i++] = texel; - scratch[i++] = texel; - scratch[i++] = texel; - scratch[i++] = (texel == 255 ? 0 : 255); - } - } - break; - - case 6: // 8-bit grayscale <-- this one is correct - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] >> 8; - scratch[i++] = texel; - scratch[i++] = texel; - scratch[i++] = texel; - scratch[i++] = (texel == 255 ? 0 : 255); - } - } - break; - - case 7: // RGBA4 - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - scratch[i++] = ((src[yi * 2048 + xi] >> 12) & 0xF) * 17;// R - scratch[i++] = ((src[yi * 2048 + xi] >> 8) & 0xF) * 17; // G - scratch[i++] = ((src[yi * 2048 + xi] >> 4) & 0xF) * 17; // B - scratch[i++] = ((src[yi * 2048 + xi] >> 0) & 0xF) * 17; // A - } - } - break; - - // - // 4 bit texture types - all luminance textures (no alpha), only seem to be enabled when contour is enabled ( white = contour value ) - // - - case 8: // low byte, low nibble - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] & 0xFF; - c = (texel & 0xF) * 17; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = (c == 255 ? 0 : 255); - } - } - break; - - case 9: // low byte, high nibble - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] & 0xFF; - c = ((texel >> 4) & 0xF) * 17; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = (c == 255 ? 0 : 255); - } - } - break; - - case 10: // high byte, low nibble - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] >> 8; - c = (texel & 0xF) * 17; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = (c == 255 ? 0 : 255); - } - } - break; - - case 11: // high byte, high nibble - - for (yi = y; yi < (y + subHeight); yi++) - { - for (xi = x; xi < (x + subWidth); xi++) - { - texel = src[yi * 2048 + xi] >> 8; - c = ((texel >> 4) & 0xF) * 17; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = c; - scratch[i++] = (c == 255 ? 0 : 255); - } - } - break; - } - - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, subWidth, subHeight, GL_RGBA, GL_UNSIGNED_BYTE, scratch); -} - -UINT32 Texture::UploadTexture(const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height) -{ - const int mipXBase[] = { 0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047 }; - const int mipYBase[] = { 0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023 }; - const int mipDivisor[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; - - if (!src || !scratch) { - return 0; // sanity checking - } - - DeleteTexture(); // free any existing texture - CreateTextureObject(format, x, y, width, height); - - int page = y / 1024; - - y -= (page * 1024); // remove page from tex y - - for (int i = 0; width > 0 && height > 0; i++) { - - int xPos = mipXBase[i] + (x / mipDivisor[i]); - int yPos = mipYBase[i] + (y / mipDivisor[i]); - - UploadTextureMip(i, src, scratch, format, xPos, yPos + (page * 1024), width, height); - - width /= 2; - height /= 2; - } - - return m_textureID; -} - -void Texture::GetDetails(int& x, int&y, int& width, int& height, int& format) -{ - x = m_x; - y = m_y; - width = m_width; - height = m_height; - format = m_format; -} - -bool Texture::Compare(int x, int y, int width, int height, int format) -{ - if (m_x == x && m_y == y && m_width == width && m_height == height && m_format == format) { - return true; - } - - return false; -} - -bool Texture::CheckMapPos(int ax1, int ax2, int ay1, int ay2) -{ - int bx1 = m_x; - int bx2 = m_x + m_width; - int by1 = m_y; - int by2 = m_y + m_height; - - if (ax1bx1 && - ay1by1) { - return true; // rectangles overlap - } - - return false; -} - -void Texture::CreateTextureObject(int format, int x, int y, int width, int height) -{ - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // rgba is always 4 byte aligned - glGenTextures(1, &m_textureID); - glBindTexture(GL_TEXTURE_2D, m_textureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - - int minD = std::min(width, height); - int count = 0; - - while (minD > 1) { - minD /= 2; - count++; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count); - - m_x = x; - m_y = y; - m_width = width; - m_height = height; - m_format = format; -} - -} // New3D diff --git a/Src/Graphics/New3D/Texture.h b/Src/Graphics/New3D/Texture.h deleted file mode 100644 index 5a25a66..0000000 --- a/Src/Graphics/New3D/Texture.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _TEXTURE_H_ -#define _TEXTURE_H_ - -#include "Types.h" -#include - -namespace New3D { - -class Texture -{ -public: - - Texture(); - ~Texture(); - - UINT32 UploadTexture (const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height); - void DeleteTexture (); - void BindTexture (); - void GetCoordinates (UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut); - void GetDetails (int& x, int&y, int& width, int& height, int& format); - bool Compare (int x, int y, int width, int height, int format); - bool CheckMapPos (int ax1, int ax2, int ay1, int ay2); //check to see if textures overlap - - static void GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut); - -private: - - void CreateTextureObject(int format, int x, int y, int width, int height); - void UploadTextureMip(int level, const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height); - void Reset(); - - int m_x; - int m_y; - int m_width; - int m_height; - int m_format; - GLuint m_textureID; -}; - -} // New3D - -#endif diff --git a/Src/Graphics/New3D/TextureSheet.cpp b/Src/Graphics/New3D/TextureSheet.cpp deleted file mode 100644 index 1bb206c..0000000 --- a/Src/Graphics/New3D/TextureSheet.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "TextureSheet.h" - -namespace New3D { - -TextureSheet::TextureSheet() -{ - m_temp.resize(1024 * 1024 * 4); // temporary buffer for textures -} - -int TextureSheet::ToIndex(int x, int y) -{ - return (y * 2048) + x; -} - -std::shared_ptr TextureSheet::BindTexture(const UINT16* src, int format, int x, int y, int width, int height) -{ - //======== - int index; - //======== - - x &= 2047; - y &= 2047; - - if (width > 1024 || height > 1024) { // sanity checking - return nullptr; - } - - index = ToIndex(x, y); - - auto range = m_texMap.equal_range(index); - - if (range.first == range.second) { - - // nothing found so create a new texture - - std::shared_ptr t(new Texture()); - m_texMap.insert(std::pair>(index, t)); - t->UploadTexture(src, m_temp.data(), format, x, y, width, height); - return t; - } - else { - - // iterate to try and find a match - - for (auto it = range.first; it != range.second; ++it) { - - int x2, y2, width2, height2, format2; - - it->second->GetDetails(x2, y2, width2, height2, format2); - - if (width == width2 && height == height2 && format == format2) { - return it->second; - } - } - - // nothing found so create a new entry - - std::shared_ptr t(new Texture()); - m_texMap.insert(std::pair>(index, t)); - t->UploadTexture(src, m_temp.data(), format, x, y, width, height); - return t; - } -} - -void TextureSheet::Release() -{ - m_texMap.clear(); -} - -void TextureSheet::Invalidate(int x, int y, int width, int height) -{ - //============ - int newX; - int newY; - int newWidth; - int newHeight; - int count; - int sWidth; // sample width - int sHeight; // sample height - //============ - - if (width <= 512) { - newX = (x + width) - 512; - newWidth = 512; - } - else { - newX = x; - newWidth = width; - } - - if (height <= 512) { - newY = (y + height) - 512; - newHeight = 512; - } - else { - newY = y; - newHeight = height; - } - - CropTile(x, y, newX, newY, newWidth, newHeight); - - sWidth = newWidth / 32; - sHeight = newHeight / 32; - count = sWidth * sHeight; - - for (int i = 0; i < count; i++) { - - int posX = newX + ((i%sWidth) * 32); - int posY = newY + ((i / sWidth) * 32); - int index = ToIndex(posX, posY); - - if (posX >= x && posY >= y) { // invalidate this area of memory - m_texMap.erase(index); - } - else { // check for overlapping data tiles and invalidate as necessary - - auto range = m_texMap.equal_range(index); - - for (auto it = range.first; it != range.second; ++it) { - - if (it->second->CheckMapPos(x, x + width, y, y + height)) { - m_texMap.erase(index); - break; - } - } - } - } -} - -void TextureSheet::CropTile(int oldX, int oldY, int &newX, int &newY, int &newWidth, int &newHeight) -{ - if (newX < 0) { - newWidth += newX; - newX = 0; - } - - if (newY < 0) { - newHeight += newY; - newY = 0; - } - - if (oldY >= 1024 && newY < 1024) { // gone into next memory page, limitation of our flat model - newHeight -= 1024 - newY; - newY = 1024; - } -} - -int TextureSheet::GetTexFormat(int originalFormat, bool contour) -{ - if (!contour) { - return originalFormat; // the same - } - - switch (originalFormat) - { - case 1: - case 2: - case 3: - case 4: - return originalFormat + 7; // these formats are identical to 1-4, except they lose the 4 bit alpha part when contour is enabled - default: - return originalFormat; - } -} - -void TextureSheet::GetMicrotexPos(int basePage, int id, int& x, int& y) -{ - int xCoords[8] = { 0, 0, 128, 128, 0, 0, 128, 128 }; - int yCoords[8] = { 0, 128, 0, 128, 256, 384, 256, 384 }; - - // i'm assuming .. the micro texture map is always on the other memory bank to the base texture - // this logic works for all our current games - // the microtextures are always on the top left of each texture sheet - - basePage = (basePage + 1) & 1; // wrap around base page - - x = xCoords[id]; - y = yCoords[id] + (basePage * 1024); -} - -} // New3D diff --git a/Src/Graphics/New3D/TextureSheet.h b/Src/Graphics/New3D/TextureSheet.h deleted file mode 100644 index 827e382..0000000 --- a/Src/Graphics/New3D/TextureSheet.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _TEXTURE_SHEET_H_ -#define _TEXTURE_SHEET_H_ - -#include "Types.h" -#include -#include -#include -#include "Texture.h" - -namespace New3D { - -class TextureSheet -{ -public: - TextureSheet(); - - std::shared_ptr BindTexture (const UINT16* src, int format, int x, int y, int width, int height); - void Invalidate (int x, int y, int width, int height); // release parts of the memory - void Release (); // release all texture objects and memory - int GetTexFormat (int originalFormat, bool contour); - void GetMicrotexPos (int basePage, int id, int& x, int& y); - -private: - - int ToIndex(int x, int y); - void CropTile(int oldX, int oldY, int &newX, int &newY, int &newWidth, int &newHeight); - - std::unordered_multimap> m_texMap; - - // the key for the above maps is the x/y position in the 2048x2048 texture - // array of 8 planes for each texture type - - std::vector m_temp; -}; - -} // New3D - -#endif \ No newline at end of file diff --git a/Src/Graphics/New3D/VBO.cpp b/Src/Graphics/New3D/VBO.cpp index 364bd24..d59318e 100644 --- a/Src/Graphics/New3D/VBO.cpp +++ b/Src/Graphics/New3D/VBO.cpp @@ -1,7 +1,5 @@ #include "VBO.h" -namespace New3D { - VBO::VBO() { m_id = 0; @@ -17,7 +15,7 @@ void VBO::Create(GLenum target, GLenum usage, GLsizeiptr size, const void* data) glBufferData(target, size, data, usage); // upload data to video card m_target = target; - m_capacity = size; + m_capacity = (int)size; m_size = 0; Bind(false); // unbind @@ -40,7 +38,7 @@ bool VBO::AppendData(GLsizeiptr size, const GLvoid* data) BufferSubData(m_size, size, data); - m_size += size; + m_size += (int)size; return true; } @@ -80,5 +78,3 @@ int VBO::GetCapacity() { return m_capacity; } - -} // New3D diff --git a/Src/Graphics/New3D/VBO.h b/Src/Graphics/New3D/VBO.h index 015b719..ac86c37 100644 --- a/Src/Graphics/New3D/VBO.h +++ b/Src/Graphics/New3D/VBO.h @@ -3,8 +3,6 @@ #include -namespace New3D { - class VBO { public: @@ -20,12 +18,10 @@ public: int GetCapacity (); private: - GLuint m_id; - GLenum m_target; - int m_capacity; - int m_size; + GLuint m_id; + GLenum m_target; + int m_capacity; + int m_size; }; -} // New3D - #endif diff --git a/Src/Graphics/Render2D.cpp b/Src/Graphics/Render2D.cpp index 06294eb..3e47ae4 100644 --- a/Src/Graphics/Render2D.cpp +++ b/Src/Graphics/Render2D.cpp @@ -495,15 +495,20 @@ std::pair CRender2D::DrawTilemaps(uint32_t *pixelsBottom, uint32_t * // Draws a surface to the screen (0 is top and 1 is bottom) void CRender2D::DisplaySurface(int surface) { + // Shader program + m_shader.EnableShader(); + + glBindVertexArray(m_vao); + // Draw the surface glActiveTexture(GL_TEXTURE0); // texture unit 0 glBindTexture(GL_TEXTURE_2D, m_texID[surface]); - glBegin(GL_QUADS); - glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f); - glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, 0.0f); - glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, 1.0f); - glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 1.0f); - glEnd(); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindVertexArray(0); + + m_shader.DisableShader(); } // Set up viewport and OpenGL state for 2D rendering (sets up blending function but disables blending) @@ -515,9 +520,6 @@ void CRender2D::Setup2D(bool isBottom) // Disable Z-buffering glDisable(GL_DEPTH_TEST); - // Shader program - glUseProgram(m_shaderProgram); - // Clear everything if requested or just overscan areas for wide screen mode if (isBottom) { @@ -534,11 +536,6 @@ void CRender2D::Setup2D(bool isBottom) { glViewport(m_xOffset - m_correction, m_yOffset + m_correction, m_xPixels, m_yPixels); //Preserve aspect ratio of tile layer by constraining and centering viewport } - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, 1.0, 1.0, 0.0, 1.0, -1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); } void CRender2D::BeginFrame(void) @@ -626,15 +623,6 @@ void CRender2D::AttachVRAM(const uint8_t *vramPtr) bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes) { - // Load shaders - if (OKAY != LoadShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, m_config["VertexShader2D"].ValueAs(), m_config["FragmentShader2D"].ValueAs(), s_vertexShaderSource, s_fragmentShaderSource)) - return FAIL; - - // Get locations of the uniforms - glUseProgram(m_shaderProgram); // bind program - m_textureMapLoc = glGetUniformLocation(m_shaderProgram, "textureMap"); - glUniform1i(m_textureMapLoc, 0); // attach it to texture unit 0 - // Allocate memory for layer surfaces m_memoryPool = new(std::nothrow) uint8_t[MEMORY_POOL_SIZE]; if (NULL == m_memoryPool) @@ -654,35 +642,55 @@ bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned m_totalYPixels = totalYRes; m_correction = (UINT32)(((yRes / 384.f) * 2) + 0.5f); // for some reason the 2d layer is 2 pixels off the 3D + DebugLog("Render2D initialized (allocated %1.1f MB)\n", float(MEMORY_POOL_SIZE) / 0x100000); + return OKAY; +} + +CRender2D::CRender2D(const Util::Config::Node& config) + : m_config(config), + m_vao(0) +{ + DebugLog("Built Render2D\n"); + + m_shader.LoadShaders(s_vertexShaderSource, s_fragmentShaderSource); + m_shader.GetUniformLocationMap("tex1"); + m_shader.EnableShader(); + + // update uniform memory + glUniform1i(m_shader.uniformLocMap["tex1"], 0); // bind to texture unit zero + + m_shader.DisableShader(); + // Create textures glActiveTexture(GL_TEXTURE0); // texture unit 0 glGenTextures(2, m_texID); for (int i = 0; i < 2; i++) { - glBindTexture(GL_TEXTURE_2D, m_texID[i]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 496, 384, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, m_texID[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 496, 384, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } - DebugLog("Render2D initialized (allocated %1.1f MB)\n", float(MEMORY_POOL_SIZE) / 0x100000); - return OKAY; -} - -CRender2D::CRender2D(const Util::Config::Node &config) - : m_config(config) -{ - DebugLog("Built Render2D\n"); + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + // no states needed since we do it in the shader + glBindVertexArray(0); } CRender2D::~CRender2D(void) { - DestroyShaderProgram(m_shaderProgram, m_vertexShader, m_fragmentShader); + m_shader.UnloadShaders(); glDeleteTextures(2, m_texID); + if (m_vao) { + glDeleteVertexArrays(1, &m_vao); + m_vao = 0; + } + if (m_memoryPool) { delete [] m_memoryPool; diff --git a/Src/Graphics/Render2D.h b/Src/Graphics/Render2D.h index fd993f5..ed04571 100644 --- a/Src/Graphics/Render2D.h +++ b/Src/Graphics/Render2D.h @@ -30,6 +30,7 @@ #include #include "Util/NewConfig.h" +#include "New3D/GLSLShader.h" /* @@ -195,11 +196,8 @@ private: unsigned m_totalYPixels; unsigned m_correction = 0; - // Shader programs and input data locations - GLuint m_shaderProgram; // shader program object - GLuint m_vertexShader; // vertex shader handle - GLuint m_fragmentShader; // fragment shader - GLuint m_textureMapLoc; // location of "textureMap" uniform + GLuint m_vao; + GLSLShader m_shader; // PreRenderFrame() tracks which surfaces exist in current frame std::pair m_surfaces_present = std::pair(false, false); diff --git a/Src/Graphics/Shaders2D.h b/Src/Graphics/Shaders2D.h index f4dc926..2536b9a 100644 --- a/Src/Graphics/Shaders2D.h +++ b/Src/Graphics/Shaders2D.h @@ -29,89 +29,46 @@ #define INCLUDED_SHADERS2D_H // Vertex shader -static const char s_vertexShaderSource[] = -{ -"/**\n" -" ** Supermodel\n" -" ** A Sega Model 3 Arcade Emulator.\n" -" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n" -" **\n" -" ** This file is part of Supermodel.\n" -" **\n" -" ** Supermodel is free software: you can redistribute it and/or modify it under\n" -" ** the terms of the GNU General Public License as published by the Free \n" -" ** Software Foundation, either version 3 of the License, or (at your option)\n" -" ** any later version.\n" -" **\n" -" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n" -" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n" -" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n" -" ** more details.\n" -" **\n" -" ** You should have received a copy of the GNU General Public License along\n" -" ** with Supermodel. If not, see .\n" -" **/\n" -"\n" -"/*\n" -" * Vertex2D.glsl\n" -" *\n" -" * Vertex shader for 2D tilemap rendering.\n" -" */\n" -" \n" -"#version 120\n" -"\n" -"void main(void)\n" -"{\n" -"\tgl_TexCoord[0] = gl_MultiTexCoord0;\n" -"\tgl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;\n" -"}\n" -}; +static const char s_vertexShaderSource[] = R"glsl( + + #version 410 core + + // outputs + out vec2 fsTexCoord; + + void main(void) + { + const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4( 1.0, -1.0, 0.0, 1.0), + vec4( 1.0, 1.0, 0.0, 1.0)); + + fsTexCoord = ((vertices[gl_VertexID % 4].xy + 1.0) / 2.0); + fsTexCoord.y = 1.0 - fsTexCoord.y; // flip upside down + gl_Position = vertices[gl_VertexID % 4]; + } + + )glsl"; + // Fragment shader -static const char s_fragmentShaderSource[] = -{ -"/**\n" -" ** Supermodel\n" -" ** A Sega Model 3 Arcade Emulator.\n" -" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n" -" **\n" -" ** This file is part of Supermodel.\n" -" **\n" -" ** Supermodel is free software: you can redistribute it and/or modify it under\n" -" ** the terms of the GNU General Public License as published by the Free \n" -" ** Software Foundation, either version 3 of the License, or (at your option)\n" -" ** any later version.\n" -" **\n" -" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n" -" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n" -" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n" -" ** more details.\n" -" **\n" -" ** You should have received a copy of the GNU General Public License along\n" -" ** with Supermodel. If not, see .\n" -" **/\n" -" \n" -"/*\n" -" * Fragment2D.glsl\n" -" *\n" -" * Fragment shader for 2D tilemap rendering.\n" -" */\n" -"\n" -"#version 120\n" -"\n" -"// Global uniforms\n" -"uniform sampler2D\ttextureMap;\t\t// 512x512 layer surface\n" -"\n" -"/*\n" -" * main():\n" -" *\n" -" * Fragment shader entry point.\n" -" */\n" -"\n" -"void main(void)\n" -"{\t\n" -"\tgl_FragColor = texture2D(textureMap, gl_TexCoord[0].st);\n" -"}\n" -}; +static const char s_fragmentShaderSource[] = R"glsl( + + #version 410 core + + // inputs + uniform sampler2D tex1; // texture + in vec2 fsTexCoord; + + // outputs + out vec4 fragColor; + + void main() + { + fragColor = texture(tex1, fsTexCoord); + } + + )glsl"; + #endif // INCLUDED_SHADERS2D_H diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index e185a43..633ffc9 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -75,6 +75,7 @@ #include "Model3/IEmulator.h" #include "Model3/Model3.h" #include "OSD/Audio.h" +#include "Graphics/New3D/VBO.h" #include #include "Util/BMPFile.h" @@ -169,6 +170,11 @@ static bool SetGLGeometry(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned * return OKAY; } +static void GLAPIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) +{ + printf("OGLDebug:: 0x%X: %s\n", id, message); +} + /* * CreateGLScreen(): * @@ -181,7 +187,7 @@ static bool SetGLGeometry(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned * * NOTE: keepAspectRatio should always be true. It has not yet been tested with * the wide screen hack. */ -static bool CreateGLScreen(const std::string &caption, bool focusWindow, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio, bool fullScreen) +static bool CreateGLScreen(bool coreContext, bool quadRendering, const std::string &caption, bool focusWindow, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio, bool fullScreen) { GLenum err; @@ -205,6 +211,19 @@ static bool CreateGLScreen(const std::string &caption, bool focusWindow, unsigne SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,8); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1); + if (coreContext) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + if (quadRendering) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); + } + else { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + } + } + // Set video mode s_window = SDL_CreateWindow(caption.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, *xResPtr, *yResPtr, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | (fullScreen ? SDL_WINDOW_FULLSCREEN : 0)); if (nullptr == s_window) @@ -240,6 +259,26 @@ static bool CreateGLScreen(const std::string &caption, bool focusWindow, unsigne return FAIL; } + // print some basic GPU info + GLint profile = 0; + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile); + + printf("GPU info: %s ", glGetString(GL_VERSION)); + + if (profile & GL_CONTEXT_CORE_PROFILE_BIT) { + printf("(core profile)"); + } + + if (profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) { + printf("(compatability profile)"); + } + + printf("\n\n"); + + //glDebugMessageCallback(DebugCallback, NULL); + //glDebugMessageControl(GL_DONT_CARE,GL_DONT_CARE,GL_DONT_CARE, 0, 0, GL_TRUE); + //glEnable(GL_DEBUG_OUTPUT); + return SetGLGeometry(xOffsetPtr, yOffsetPtr, xResPtr, yResPtr, totalXResPtr, totalYResPtr, keepAspectRatio); } @@ -275,7 +314,7 @@ static void PrintGLInfo(bool createScreen, bool infoLog, bool printExtensions) unsigned xOffset, yOffset, xRes=496, yRes=384, totalXRes, totalYRes; if (createScreen) { - if (OKAY != CreateGLScreen("Supermodel - Querying OpenGL Information...", false, &xOffset, &yOffset, &xRes, &yRes, &totalXRes, &totalYRes, false, false)) + if (OKAY != CreateGLScreen(false, false, "Supermodel - Querying OpenGL Information...", false, &xOffset, &yOffset, &xRes, &yRes, &totalXRes, &totalYRes, false, false)) { ErrorLog("Unable to query OpenGL.\n"); return; @@ -694,31 +733,130 @@ static void LoadNVRAM(IEmulator *Model3) Currently, only does crosshairs for light gun games. ******************************************************************************/ +struct BasicDraw +{ +public: + + struct BasicVertex + { + BasicVertex(float x, float y, float z) : x(x), y(y), z(z) {} + BasicVertex(float x, float y) : x(x), y(y), z(0.f) {} + float x, y, z; + }; + + const int MaxVerts = 1024; // per draw call + + void Draw(GLenum mode, const float mvpMatrix[16], const BasicVertex* vertices, int count, float r, float g, float b, float a) + { + if (count > MaxVerts) { + count = MaxVerts; // maybe we could error out somehow + } + + if (!m_initialised) { + Setup(); + } + + m_shader.EnableShader(); + + // update uniform memory + glUniformMatrix4fv(m_shader.uniformLocMap["mvp"], 1, GL_FALSE, mvpMatrix); + glUniform4f(m_shader.uniformLocMap["colour"], r, g, b, a); + + // update vbo mem + m_vbo.Bind(true); + m_vbo.BufferSubData(0, count * sizeof(BasicVertex), vertices); + + glBindVertexArray(m_vao); + glDrawArrays(mode, 0, count); + glBindVertexArray(0); + + m_shader.DisableShader(); + } + +private: + + void Setup() + { + const char* vertexShader = R"glsl( + + #version 410 core + + uniform mat4 mvp; + layout(location = 0) in vec3 inVertices; + + void main(void) + { + gl_Position = mvp * vec4(inVertices,1.0); + } + )glsl"; + + const char* fragmentShader = R"glsl( + + #version 410 core + + uniform vec4 colour; + out vec4 fragColour; + + void main(void) + { + fragColour = colour; + } + )glsl"; + + m_shader.LoadShaders(vertexShader, fragmentShader); + m_shader.GetUniformLocationMap("mvp"); + m_shader.GetUniformLocationMap("colour"); + + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(BasicVertex) * (MaxVerts)); + m_vbo.Bind(true); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(BasicVertex), 0); + + glBindVertexArray(0); + m_vbo.Bind(false); + + m_initialised = true; + } + + GLSLShader m_shader; + VBO m_vbo; + GLuint m_vao = 0; + bool m_initialised = false; + +} basicDraw; + static void GunToViewCoords(float *x, float *y) { *x = (*x-150.0f)/(651.0f-150.0f); // Scale [150,651] -> [0.0,1.0] *y = (*y-80.0f)/(465.0f-80.0f); // Scale [80,465] -> [0.0,1.0] } -static void DrawCrosshair(float x, float y, float r, float g, float b) +static void DrawCrosshair(const float* matrix, float x, float y, float r, float g, float b) { float base = 0.01f, height = 0.02f; // geometric parameters of each triangle float dist = 0.004f; // distance of triangle tip from center float a = (float)xRes/(float)yRes; // aspect ratio (to square the crosshair) - glColor3f(r, g, b); - glVertex2f(x, y+dist); // bottom triangle - glVertex2f(x+base/2.0f, y+(dist+height)*a); - glVertex2f(x-base/2.0f, y+(dist+height)*a); - glVertex2f(x, y-dist); // top triangle - glVertex2f(x-base/2.0f, y-(dist+height)*a); - glVertex2f(x+base/2.0f, y-(dist+height)*a); - glVertex2f(x-dist, y); // left triangle - glVertex2f(x-dist-height, y+(base/2.0f)*a); - glVertex2f(x-dist-height, y-(base/2.0f)*a); - glVertex2f(x+dist, y); // right triangle - glVertex2f(x+dist+height, y-(base/2.0f)*a); - glVertex2f(x+dist+height, y+(base/2.0f)*a); + std::vector verts; + + verts.emplace_back(x, y+dist); // bottom triangle + verts.emplace_back(x+base/2.0f, y+(dist+height)*a); + verts.emplace_back(x-base/2.0f, y+(dist+height)*a); + verts.emplace_back(x, y-dist); // top triangle + verts.emplace_back(x-base/2.0f, y-(dist+height)*a); + verts.emplace_back(x+base/2.0f, y-(dist+height)*a); + verts.emplace_back(x-dist, y); // left triangle + verts.emplace_back(x-dist-height, y+(base/2.0f)*a); + verts.emplace_back(x-dist-height, y-(base/2.0f)*a); + verts.emplace_back(x+dist, y); // right triangle + verts.emplace_back(x+dist+height, y-(base/2.0f)*a); + verts.emplace_back(x+dist+height, y+(base/2.0f)*a); + + basicDraw.Draw(GL_TRIANGLES, matrix, verts.data(), (int)verts.size(), r, g, b, 1.0f); } /* @@ -740,7 +878,6 @@ static void PrintGLError(GLenum error) */ static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned crosshairs) - { bool offscreenTrigger[2]; float x[2], y[2]; @@ -762,6 +899,9 @@ static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned c glDisable(GL_DEPTH_TEST); // no Z-buffering needed glDisable(GL_LIGHTING); + New3D::Mat4 m; + m.Ortho(0.0, 1.0, 1.0, 0.0, -1.0f, 1.0f); + // Convert gun coordinates to viewspace coordinates if (currentInputs & Game::INPUT_ANALOG_GUN1) { @@ -790,12 +930,10 @@ static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned c offscreenTrigger[1] = (Inputs->trigger[1]->offscreenValue) > 0; } // Draw visible crosshairs - glBegin(GL_TRIANGLES); if ((crosshairs & 1) && !offscreenTrigger[0]) // Player 1 - DrawCrosshair(x[0], y[0], 1.0f, 0.0f, 0.0f); + DrawCrosshair(m,x[0], y[0], 1.0f, 0.0f, 0.0f); if ((crosshairs & 2) && !offscreenTrigger[1]) // Player 2 - DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f); - glEnd(); + DrawCrosshair(m,x[1], y[1], 0.0f, 1.0f, 0.0f); //PrintGLError(glGetError()); } @@ -1901,7 +2039,7 @@ int main(int argc, char **argv) // Create a window xRes = 496; yRes = 384; - if (OKAY != CreateGLScreen("Supermodel", false, &xOffset, &yOffset, &xRes, &yRes, &totalXRes, &totalYRes, false, false)) + if (OKAY != CreateGLScreen(s_runtime_config["New3DEngine"].ValueAs(), s_runtime_config["QuadRendering"].ValueAs(),"Supermodel", false, &xOffset, &yOffset, &xRes, &yRes, &totalXRes, &totalYRes, false, false)) { exitCode = 1; goto Exit; diff --git a/VS2008/Supermodel.vcxproj b/VS2008/Supermodel.vcxproj index 0046347..e23a43d 100644 --- a/VS2008/Supermodel.vcxproj +++ b/VS2008/Supermodel.vcxproj @@ -311,8 +311,6 @@ xcopy /D /Y "$(ProjectDir)..\Config\*" "$(TargetDir)Config" - - @@ -487,8 +485,6 @@ xcopy /D /Y "$(ProjectDir)..\Config\*" "$(TargetDir)Config" - - diff --git a/VS2008/Supermodel.vcxproj.filters b/VS2008/Supermodel.vcxproj.filters index a8b902b..496677b 100644 --- a/VS2008/Supermodel.vcxproj.filters +++ b/VS2008/Supermodel.vcxproj.filters @@ -377,12 +377,6 @@ Source Files\Graphics\New - - Source Files\Graphics\New - - - Source Files\Graphics\New - Source Files\Graphics\New @@ -835,12 +829,6 @@ Header Files\Graphics\New - - Header Files\Graphics\New - - - Header Files\Graphics\New - Header Files\Graphics\New