From 40c8259130dd4edda1b2a3fb2c866d40821249d0 Mon Sep 17 00:00:00 2001 From: Ian Curtis Date: Mon, 7 Nov 2022 21:33:01 +0000 Subject: [PATCH] Rewrite the whole project for GL4+. I figured if we removed the limitation of a legacy rendering API we could improve things a bit. With GL4+ we can do unsigned integer math in the shaders. This allows us to upload a direct copy of the real3d texture sheet, and texture directly from this memory given the x/y pos and type. This massively simplifies the binding and invalidation code. Also the crazy corner cases will work because it essentially works the same way as the original hardware. The standard triangle render requires gl 4.1 core, so should work on mac. The quad renderer runs on 4.5 core. The legacy renderer should still work, and when enabled a regular opengl context will be created, which allows functions marked depreciated in the core profiles to still work. This will only work in windows/linux I think. Apple doesn't support this. A GL 4.1 GPU is now the min required spec. Sorry if you have an OLDER gpu. GL 4.1 is over 12 years old now. This is a big update so I apologise in advance if I accidently broke something :] --- Src/Graphics/New3D/Model.h | 2 +- Src/Graphics/New3D/New3D.cpp | 172 +++++------ Src/Graphics/New3D/New3D.h | 6 +- Src/Graphics/New3D/R3DFrameBuffers.cpp | 179 +++++------- Src/Graphics/New3D/R3DFrameBuffers.h | 19 +- Src/Graphics/New3D/R3DScrollFog.cpp | 73 ++--- Src/Graphics/New3D/R3DScrollFog.h | 28 +- Src/Graphics/New3D/R3DShader.cpp | 103 ++++++- Src/Graphics/New3D/R3DShader.h | 21 +- Src/Graphics/New3D/R3DShaderQuads.h | 204 +++++++++++-- Src/Graphics/New3D/R3DShaderTriangles.h | 243 ++++++++++++---- Src/Graphics/New3D/Texture.cpp | 370 ------------------------ Src/Graphics/New3D/Texture.h | 42 --- Src/Graphics/New3D/TextureSheet.cpp | 181 ------------ Src/Graphics/New3D/TextureSheet.h | 38 --- Src/Graphics/New3D/VBO.cpp | 8 +- Src/Graphics/New3D/VBO.h | 12 +- Src/Graphics/Render2D.cpp | 84 +++--- Src/Graphics/Render2D.h | 8 +- Src/Graphics/Shaders2D.h | 121 +++----- Src/OSD/SDL/Main.cpp | 182 ++++++++++-- VS2008/Supermodel.vcxproj | 4 - VS2008/Supermodel.vcxproj.filters | 12 - 23 files changed, 941 insertions(+), 1171 deletions(-) delete mode 100644 Src/Graphics/New3D/Texture.cpp delete mode 100644 Src/Graphics/New3D/Texture.h delete mode 100644 Src/Graphics/New3D/TextureSheet.cpp delete mode 100644 Src/Graphics/New3D/TextureSheet.h 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