From c7ffd0a80817a4c503b1c823d2ccf3887235dd56 Mon Sep 17 00:00:00 2001 From: Ian Curtis Date: Sat, 16 Jun 2018 21:31:29 +0000 Subject: [PATCH] Draw transparent polys to separate layers and composite at the end. This solves a tonne of transparency errors we had been battling with for a long time. The model3 is strange in the fact it only supports a max of two translucent overlapped polys. They are not blended into the frame normally. Doing this means the topmost translucent polys only are visible in the scene, the equivalent of doing a depth pass first, but without the added cost. --- Src/Graphics/New3D/GLSLShader.cpp | 112 ++++++++ Src/Graphics/New3D/GLSLShader.h | 34 +++ Src/Graphics/New3D/Model.h | 27 +- Src/Graphics/New3D/New3D.cpp | 182 +++++++------ Src/Graphics/New3D/New3D.h | 8 +- Src/Graphics/New3D/R3DFrameBuffers.cpp | 344 +++++++++++++++++++++++++ Src/Graphics/New3D/R3DFrameBuffers.h | 73 ++++++ Src/Graphics/New3D/R3DScrollFog.h | 40 +-- Src/Graphics/New3D/R3DShader.cpp | 13 +- VS2008/Supermodel.vcxproj | 4 + VS2008/Supermodel.vcxproj.filters | 12 + 11 files changed, 738 insertions(+), 111 deletions(-) create mode 100644 Src/Graphics/New3D/GLSLShader.cpp create mode 100644 Src/Graphics/New3D/GLSLShader.h create mode 100644 Src/Graphics/New3D/R3DFrameBuffers.cpp create mode 100644 Src/Graphics/New3D/R3DFrameBuffers.h diff --git a/Src/Graphics/New3D/GLSLShader.cpp b/Src/Graphics/New3D/GLSLShader.cpp new file mode 100644 index 0000000..0f2a358 --- /dev/null +++ b/Src/Graphics/New3D/GLSLShader.cpp @@ -0,0 +1,112 @@ +#include "GLSLShader.h" +#include + +GLSLShader::GLSLShader() { + + m_vShader = 0; + m_fShader = 0; + m_program = 0; + + for (auto &i : uniformLoc) { + i = 0; + } + + for (auto &i : attribLoc) { + i = 0; + } +} + +GLSLShader::~GLSLShader() { + + UnloadShaders(); +} + +bool GLSLShader::LoadShaders(const char *vertexShader, const char *fragmentShader) { + + m_program = glCreateProgram(); + m_vShader = glCreateShader(GL_VERTEX_SHADER); + m_fShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource(m_vShader, 1, &vertexShader, NULL); + glShaderSource(m_fShader, 1, &fragmentShader, NULL); + + glCompileShader(m_vShader); + glCompileShader(m_fShader); + + glAttachShader(m_program, m_vShader); + glAttachShader(m_program, m_fShader); + + glLinkProgram(m_program); + + PrintShaderInfoLog(m_vShader); + PrintShaderInfoLog(m_fShader); + PrintProgramInfoLog(m_program); + + return true; +} + +void GLSLShader::UnloadShaders() +{ + if (m_program) { + glDeleteShader(m_vShader); + glDeleteShader(m_fShader); + glDeleteProgram(m_program); + } + + m_vShader = 0; + m_fShader = 0; + m_program = 0; +} + +void GLSLShader::EnableShader() { + + glUseProgram(m_program); +} + +void GLSLShader::DisableShader() { + + glUseProgram(0); +} + +void GLSLShader::PrintShaderInfoLog(GLuint obj) { + + int infologLength = 0; + int charsWritten = 0; + + glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &infologLength); + + if (infologLength > 0) { + char *infoLog = new char[infologLength]; + glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog); + printf("%s\n", infoLog); + delete[] infoLog; + } +} + +void GLSLShader::PrintProgramInfoLog(GLuint obj) { + + int infologLength = 0; + int charsWritten = 0; + + glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &infologLength); + + if (infologLength > 0) { + char *infoLog = new char[infologLength]; + glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog); + printf("%s\n", infoLog); + delete[] infoLog; + } +} + +int GLSLShader::GetUniformLocation(const char *str) +{ + return glGetUniformLocation(m_program, str); +} + +int GLSLShader::GetAttributeLocation(const char *str) +{ + return glGetAttribLocation(m_program, str); +} + + + diff --git a/Src/Graphics/New3D/GLSLShader.h b/Src/Graphics/New3D/GLSLShader.h new file mode 100644 index 0000000..580a521 --- /dev/null +++ b/Src/Graphics/New3D/GLSLShader.h @@ -0,0 +1,34 @@ +#ifndef _GLSLSHADER_H_ +#define _GLSLSHADER_H_ + +#include "Pkgs/glew.h" + +class GLSLShader { + +public: + GLSLShader(); + ~GLSLShader(); + + bool LoadShaders(const char *vertexShader, const char *fragmentShader); + void UnloadShaders(); + + void EnableShader(); + void DisableShader(); + + int GetUniformLocation(const char *str); + int GetAttributeLocation(const char *str); + + int uniformLoc[64]; + int attribLoc[16]; + +private: + + void PrintShaderInfoLog(GLuint obj); + void PrintProgramInfoLog(GLuint obj); + + GLuint m_program; + GLuint m_vShader; + GLuint m_fShader; +}; + +#endif \ No newline at end of file diff --git a/Src/Graphics/New3D/Model.h b/Src/Graphics/New3D/Model.h index 07d458e..ff3380d 100644 --- a/Src/Graphics/New3D/Model.h +++ b/Src/Graphics/New3D/Model.h @@ -86,20 +86,32 @@ struct Poly // our polys are always 3 triangles, unlike the real h/w FVertex p3; }; +enum class Layer { colour, trans1, trans2, all, none }; + struct Mesh { //helper funcs - bool Render(bool alpha) + bool Render(Layer layer) { - if (alpha) { - if (!textureAlpha && !polyAlpha) { - return false; - } - } - else { + switch (layer) + { + case Layer::colour: if (polyAlpha) { return false; } + break; + case Layer::trans1: + if (!textureAlpha && !polyAlpha || transLSelect) { + return false; + } + break; + case Layer::trans2: + if (!textureAlpha && !polyAlpha || !transLSelect) { + return false; + } + break; + default: // not using these types + return false; } return true; @@ -117,7 +129,6 @@ struct Mesh float microTextureScale = 0; // attributes - bool doubleSided = false; bool textured = false; bool polyAlpha = false; // specified in the rgba colour bool textureAlpha = false; // use alpha in texture diff --git a/Src/Graphics/New3D/New3D.cpp b/Src/Graphics/New3D/New3D.cpp index 01bb389..d006949 100644 --- a/Src/Graphics/New3D/New3D.cpp +++ b/Src/Graphics/New3D/New3D.cpp @@ -79,6 +79,8 @@ bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yR m_r3dShader.LoadShader(); + m_r3dFrameBuffers.CreateFBO(totalXResParam, totalYResParam); + glUseProgram(0); return OKAY; // OKAY ? wtf .. @@ -147,14 +149,10 @@ void CNew3D::DrawScrollFog() } } -bool CNew3D::RenderScene(int priority, bool renderOverlay, bool alpha) +bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer) { bool hasOverlay = false; // (high priority polys) - if (alpha) { - glEnable(GL_BLEND); - } - for (auto &n : m_nodes) { if (n.viewport.priority != priority || n.models.empty()) { @@ -188,7 +186,7 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, bool alpha) hasOverlay = true; } - if (!mesh.Render(alpha)) continue; + if (!mesh.Render(layer)) continue; if (mesh.highPriority != renderOverlay) continue; if (!matrixLoaded) { @@ -236,6 +234,65 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, bool alpha) return hasOverlay; } +bool CNew3D::SkipLayer(int layer) +{ + for (const auto &n : m_nodes) { + if (n.viewport.priority == layer) { + if (n.models.size()) { + return false; + } + } + } + + return true; +} + +void CNew3D::SetRenderStates() +{ + m_vbo.Bind(true); + 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_LESS); + glEnable (GL_DEPTH_TEST); + glDepthMask (GL_TRUE); + glActiveTexture (GL_TEXTURE0); + glDisable (GL_CULL_FACE); // we'll emulate this in the shader + + glStencilFunc (GL_EQUAL, 0, 0xFF); // basically stencil test passes if the value is zero + glStencilOp (GL_KEEP, GL_INCR, GL_INCR); // if the stencil test passes, we incriment the value + glStencilMask (0xFF); +} + +void CNew3D::DisableRenderStates() +{ + m_vbo.Bind(false); + m_r3dShader.SetShader(false); + + glDisable(GL_STENCIL_TEST); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glDisableVertexAttribArray(4); + glDisableVertexAttribArray(5); +} + void CNew3D::RenderFrame(void) { for (int i = 0; i < 4; i++) { @@ -252,17 +309,6 @@ void CNew3D::RenderFrame(void) RenderViewport(0x800000); // build model structure DrawScrollFog(); // fog layer if applicable must be drawn here - - glDepthFunc (GL_LEQUAL); - glEnable (GL_DEPTH_TEST); - glDepthMask (GL_TRUE); - glActiveTexture (GL_TEXTURE0); - glDisable (GL_CULL_FACE); // we'll emulate this in the shader - glFrontFace (GL_CW); - - glStencilFunc (GL_EQUAL, 0, 0xFF); // basically stencil test passes if the value is zero - glStencilOp (GL_KEEP, GL_INCR, GL_INCR); // if the stencil test passes, we incriment the value - glStencilMask (0xFF); m_vbo.Bind(true); m_vbo.BufferSubData(MAX_ROM_POLYS*sizeof(Poly), m_polyBufferRam.size()*sizeof(Poly), m_polyBufferRam.data()); // upload all the dynamic data to GPU in one go @@ -287,23 +333,6 @@ void CNew3D::RenderFrame(void) } } } - - 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)); - - m_r3dShader.SetShader(true); for (int pri = 0; pri <= 3; pri++) { @@ -311,36 +340,37 @@ void CNew3D::RenderFrame(void) bool hasOverlay; //============== - glViewport (0, 0, m_totalXRes, m_totalYRes); // clear whole viewport - glClear (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); + if (SkipLayer(pri)) continue; - m_r3dShader.DiscardAlpha(true); // chuck out alpha pixels in texture alpha only polys - hasOverlay = RenderScene(pri, false, false); - m_r3dShader.DiscardAlpha(false); - hasOverlay = RenderScene(pri, false, true); + for (int i = 0; i < 2; i++) { - if (hasOverlay) { - //clear depth buffer and render high priority polys - glViewport(0, 0, m_totalXRes, m_totalYRes); // clear whole viewport - glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - m_r3dShader.DiscardAlpha(true); - RenderScene(pri, true, false); - m_r3dShader.DiscardAlpha(false); - RenderScene(pri, true, true); + bool renderOverlay = (i == 1); + + m_r3dFrameBuffers.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + SetRenderStates(); + + m_r3dShader.DiscardAlpha(true); // discard all translucent pixels in opaque pass + m_r3dFrameBuffers.SetFBO(Layer::colour); + hasOverlay = RenderScene(pri, renderOverlay, Layer::colour); + + m_r3dShader.DiscardAlpha(false); // render only translucent pixels + m_r3dFrameBuffers.StoreDepth(); // save depth buffer for 1st trans pass + m_r3dFrameBuffers.SetFBO(Layer::trans1); + RenderScene(pri, renderOverlay, Layer::trans1); + + m_r3dFrameBuffers.RestoreDepth(); // restore depth buffer, trans layers don't seem to depth test against each other + m_r3dFrameBuffers.SetFBO(Layer::trans2); + RenderScene(pri, renderOverlay, Layer::trans2); + + DisableRenderStates(); + m_r3dFrameBuffers.Draw(); // draw current layer to back buffer + + if (!hasOverlay) break; // no high priority polys } } - m_r3dShader.SetShader(false); // unbind shader - m_vbo.Bind(false); - - glDisable(GL_STENCIL_TEST); - - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(2); - glDisableVertexAttribArray(3); - glDisableVertexAttribArray(4); - glDisableVertexAttribArray(5); + m_r3dFrameBuffers.DisableFBO(); // draw to back buffer normally } void CNew3D::BeginFrame(void) @@ -469,6 +499,13 @@ void CNew3D::DescendCullingNode(UINT32 addr) matrixOffset = node[0x03 - m_offset] & 0xFFF; lodTablePointer = (node[0x03 - m_offset] >> 12) & 0x7F; + // parse siblings + if ((node[0x00] & 0x07) != 0x06) { // colour table seems to indicate no siblings + if (!(sibling2Ptr & 0x1000000) && sibling2Ptr) { + DescendCullingNode(sibling2Ptr); // no need to mask bit, would already be zero + } + } + if ((node[0x00] & 0x04)) { m_colorTableAddr = ((node[0x03 - m_offset] >> 19) << 0) | ((node[0x07 - m_offset] >> 28) << 13) | ((node[0x08 - m_offset] >> 25) << 17); m_colorTableAddr &= 0x000FFFFF; // clamp to 4MB (in words) range @@ -558,13 +595,6 @@ void CNew3D::DescendCullingNode(UINT32 addr) // Restore old texture offsets m_nodeAttribs.Pop(); - - // parse siblings - if ((node[0x00] & 0x07) != 0x06) { // colour table seems to indicate no siblings - if (!(sibling2Ptr & 0x1000000) && sibling2Ptr) { - DescendCullingNode(sibling2Ptr); // no need to mask bit, would already be zero - } - } } void CNew3D::DescendNodePtr(UINT32 nodeAddr) @@ -954,7 +984,6 @@ void CNew3D::OffsetTexCoords(R3DPoly& r3dPoly, float offset[2]) void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph) { //copy attributes - currentMesh->doubleSided = false; // we will double up polys currentMesh->textured = ph.TexEnabled(); currentMesh->alphaTest = ph.AlphaTest(); currentMesh->textureAlpha = ph.TextureAlpha(); @@ -963,16 +992,11 @@ void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph) currentMesh->fixedShading = ph.FixedShading() && !ph.SmoothShading(); currentMesh->highPriority = ph.HighPriority(); currentMesh->transLSelect = ph.TranslucencyPatternSelect(); - - if (ph.Layered() || (!ph.TexEnabled() && ph.PolyAlpha())) { - currentMesh->layered = true; - } - - currentMesh->specular = ph.SpecularEnabled(); - currentMesh->shininess = ph.Shininess(); - currentMesh->specularValue = ph.SpecularValue(); - - currentMesh->fogIntensity = ph.LightModifier(); + currentMesh->layered = ph.Layered(); + currentMesh->specular = ph.SpecularEnabled(); + currentMesh->shininess = ph.Shininess(); + currentMesh->specularValue = ph.SpecularValue(); + currentMesh->fogIntensity = ph.LightModifier(); if (currentMesh->textured) { @@ -1107,7 +1131,7 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) if (ph.Discard1() && !ph.Discard2()) { p.faceColour[3] /= 2; } - + // if we have flat shading, we can't re-use normals from shared vertices for (i = 0; i < p.number && !ph.SmoothShading(); i++) { p.v[i].normal[0] = p.faceNormal[0]; @@ -1607,7 +1631,7 @@ void CNew3D::CalcViewport(Viewport* vp, float near, float far) * the same as a 496x384 Model 3 display. The display will be distorted. */ float windowAR = (float)m_totalXRes / (float)m_totalYRes; - float viewableAreaAR = (float)m_xRes / (float)m_yRes; + float viewableAreaAR = (float)m_xRes / (float)m_yRes; // Will expand horizontal frustum planes only in non-stretch mode (wide- // screen and non-wide-screen modes have identical resolution parameters diff --git a/Src/Graphics/New3D/New3D.h b/Src/Graphics/New3D/New3D.h index d10dadd..321d37b 100644 --- a/Src/Graphics/New3D/New3D.h +++ b/Src/Graphics/New3D/New3D.h @@ -1,3 +1,4 @@ + /** ** Supermodel ** A Sega Model 3 Arcade Emulator. @@ -41,6 +42,7 @@ #include "Vec.h" #include "R3DScrollFog.h" #include "PolyHeader.h" +#include "R3DFrameBuffers.h" namespace New3D { @@ -199,11 +201,14 @@ private: void CopyVertexData(const R3DPoly& r3dPoly, std::vector& polyArray); void OffsetTexCoords(R3DPoly& r3dPoly, float offset[2]); - bool RenderScene(int priority, bool renderOverlay, bool alpha); // returns if has overlay plane + bool RenderScene(int priority, bool renderOverlay, Layer layer); // returns if has overlay plane float Determinant3x3(const float m[16]); bool IsDynamicModel(UINT32 *data); // check if the model has a colour palette bool IsVROMModel(UINT32 modelAddr); void DrawScrollFog(); + bool SkipLayer(int layer); + void SetRenderStates(); + void DisableRenderStates(); void CalcTexOffset(int offX, int offY, int page, int x, int y, int& newX, int& newY); @@ -253,6 +258,7 @@ private: 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; + R3DFrameBuffers m_r3dFrameBuffers; Plane m_planes[4]; diff --git a/Src/Graphics/New3D/R3DFrameBuffers.cpp b/Src/Graphics/New3D/R3DFrameBuffers.cpp new file mode 100644 index 0000000..56215b2 --- /dev/null +++ b/Src/Graphics/New3D/R3DFrameBuffers.cpp @@ -0,0 +1,344 @@ +#include "R3DFrameBuffers.h" +#include "Mat4.h" + +#define countof(a) (sizeof(a)/sizeof(*(a))) + +namespace New3D { + +R3DFrameBuffers::R3DFrameBuffers() +{ + m_frameBufferID = 0; + m_renderBufferID = 0; + m_frameBufferIDCopy = 0; + m_renderBufferIDCopy = 0; + m_width = 0; + m_height = 0; + + for (auto &i : m_texIDs) { + i = 0; + } + + m_lastLayer = Layer::none; + + AllocShaderTrans(); + AllocShaderBase(); + + 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); +} + +R3DFrameBuffers::~R3DFrameBuffers() +{ + DestroyFBO(); + m_shaderTrans.UnloadShaders(); + m_vbo.Destroy(); +} + +bool R3DFrameBuffers::CreateFBO(int width, int height) +{ + m_width = width; + m_height = height; + + m_texIDs[0] = CreateTexture(width, height); // colour buffer + m_texIDs[1] = CreateTexture(width, height); // trans layer1 + m_texIDs[2] = CreateTexture(width, height); // trans layer2 + + glGenFramebuffers(1, &m_frameBufferID); + glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID); + + // colour attachments + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texIDs[0], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_texIDs[1], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, m_texIDs[2], 0); + + // depth/stencil attachment + glGenRenderbuffers(1, &m_renderBufferID); + glBindRenderbuffer(GL_RENDERBUFFER, m_renderBufferID); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferID); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferID); + + // check setup was successful + auto fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); //created R3DFrameBuffers now disable it + + CreateFBODepthCopy(width, height); + + return (fboStatus == GL_FRAMEBUFFER_COMPLETE); +} + +bool R3DFrameBuffers::CreateFBODepthCopy(int width, int height) +{ + glGenFramebuffers(1, &m_frameBufferIDCopy); + glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferIDCopy); + + glGenRenderbuffers(1, &m_renderBufferIDCopy); + glBindRenderbuffer(GL_RENDERBUFFER, m_renderBufferIDCopy); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferIDCopy); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferIDCopy); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // check setup was successful + auto fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + return (fboStatus == GL_FRAMEBUFFER_COMPLETE); +} + +void R3DFrameBuffers::StoreDepth() +{ + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_frameBufferID); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_frameBufferIDCopy); + glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); +} + +void R3DFrameBuffers::RestoreDepth() +{ + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_frameBufferIDCopy); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_frameBufferID); + glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); +} + +void R3DFrameBuffers::DestroyFBO() +{ + if (m_frameBufferID) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteRenderbuffers(1, &m_renderBufferID); + glDeleteFramebuffers(1, &m_frameBufferID); + } + + if (m_frameBufferIDCopy) { + glDeleteRenderbuffers(1, &m_renderBufferIDCopy); + glDeleteFramebuffers(1, &m_frameBufferIDCopy); + } + + for (auto &i : m_texIDs) { + if (i) { + glDeleteTextures(1, &i); + i = 0; + } + } + + m_frameBufferID = 0; + m_renderBufferID = 0; + m_frameBufferIDCopy = 0; + m_renderBufferIDCopy = 0; + m_width = 0; + m_height = 0; +} + +GLuint R3DFrameBuffers::CreateTexture(int width, int height) +{ + GLuint texId; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_2D, texId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + return texId; +} + +void R3DFrameBuffers::BindTexture(Layer layer) +{ + glBindTexture(GL_TEXTURE_2D, m_texIDs[(int)layer]); +} + +void R3DFrameBuffers::SetFBO(Layer layer) +{ + if (m_lastLayer == layer) { + return; + } + + glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID); + GLenum buffers[] = { GL_COLOR_ATTACHMENT0 + (int)layer }; + glDrawBuffers(countof(buffers), buffers); + m_lastLayer = layer; +} + +void R3DFrameBuffers::DisableFBO() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDrawBuffer(GL_BACK); + m_lastLayer = Layer::none; +} + +void R3DFrameBuffers::Clear(GLbitfield mask) +{ + if (m_lastLayer != Layer::all) { + glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID); // bind frame buffer + GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; + glDrawBuffers(countof(buffers), buffers); + } + + glViewport(0, 0, m_width, m_height); + glClear(mask); + m_lastLayer = Layer::all; +} + +void R3DFrameBuffers::AllocShaderBase() +{ + const char *vertexShader = R"glsl( + + // inputs + attribute vec3 inVertex; + attribute vec2 inTexCoord; + + // outputs + varying vec2 fsTexCoord; + + void main(void) + { + fsTexCoord = inTexCoord; + gl_Position = vec4(inVertex,1.0); + }; + + )glsl"; + + const char *fragmentShader = R"glsl( + + uniform sampler2D tex1; // base tex + + varying vec2 fsTexCoord; + + void main() + { + vec4 colBase = texture2D( tex1, fsTexCoord); + if(colBase.a < 1.0) discard; + gl_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( + + // inputs + attribute vec3 inVertex; + attribute vec2 inTexCoord; + + // outputs + varying vec2 fsTexCoord; + + void main(void) + { + fsTexCoord = inTexCoord; + gl_Position = vec4(inVertex,1.0); + }; + + )glsl"; + + const char *fragmentShader = R"glsl( + + uniform sampler2D tex1; // trans layer 1 + uniform sampler2D tex2; // trans layer 2 + + varying vec2 fsTexCoord; + + void main() + { + vec4 colTrans1 = texture2D( tex1, fsTexCoord); + vec4 colTrans2 = texture2D( tex2, fsTexCoord); + + if(colTrans1.a+colTrans2.a > 0.0) { + vec3 col1 = (colTrans1.rgb * colTrans1.a) / ( colTrans1.a + colTrans2.a); // this is my best guess at the blending between the layers + vec3 col2 = (colTrans2.rgb * colTrans2.a) / ( colTrans1.a + colTrans2.a); + + colTrans1 = vec4(col1+col2,colTrans1.a+colTrans2.a); + } + + gl_FragColor = colTrans1; + }; + + )glsl"; + + m_shaderTrans.LoadShaders(vertexShader, fragmentShader); + + 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::Draw() +{ + DisableFBO (); // make sure to draw on the back buffer + glViewport (0, 0, m_width, m_height); // cover the entire screen + glDisable (GL_DEPTH_TEST); // disable depth testing / writing + glDisable (GL_CULL_FACE); + + for (int i = 0; i < countof(m_texIDs); i++) { // bind our textures to correct texture units + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, m_texIDs[i]); + } + + glActiveTexture (GL_TEXTURE0); + m_vbo.Bind (true); + + DrawBaseLayer (); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); + + DrawAlphaLayer (); + + glDisable (GL_BLEND); + m_vbo.Bind (false); +} + +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)); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + + m_shaderBase.DisableShader(); +} + +void R3DFrameBuffers::DrawAlphaLayer() +{ + m_shaderTrans.EnableShader(); + 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(); +} + +} \ No newline at end of file diff --git a/Src/Graphics/New3D/R3DFrameBuffers.h b/Src/Graphics/New3D/R3DFrameBuffers.h new file mode 100644 index 0000000..87d2a9d --- /dev/null +++ b/Src/Graphics/New3D/R3DFrameBuffers.h @@ -0,0 +1,73 @@ +#ifndef FBO_H +#define FBO_H + +#include "Pkgs/glew.h" +#include "VBO.h" +#include "GLSLShader.h" +#include "Model.h" + +namespace New3D { + +class R3DFrameBuffers { + +public: + R3DFrameBuffers(); + ~R3DFrameBuffers(); + + void Draw(); // draw and composite the transparent layers + + bool CreateFBO(int width, int height); + void DestroyFBO(); + + void BindTexture(Layer layer); + void SetFBO(Layer layer); + void DisableFBO(); + void Clear(GLbitfield mask); + void StoreDepth(); + void RestoreDepth(); + +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; // z = 0 + } + + float texCoords[2]; + float verts[3]; + }; + + bool CreateFBODepthCopy(int width, int height); + GLuint CreateTexture(int width, int height); + void AllocShaderTrans(); + void AllocShaderBase(); + + void DrawBaseLayer(); + void DrawAlphaLayer(); + + GLuint m_frameBufferID; + GLuint m_renderBufferID; + GLuint m_texIDs[3]; + GLuint m_frameBufferIDCopy; + GLuint m_renderBufferIDCopy; + Layer m_lastLayer; + int m_width; + int m_height; + + // shaders + GLSLShader m_shaderBase; + GLSLShader m_shaderTrans; + + // vertices for fbo + VBO m_vbo; +}; + +} + +#endif \ No newline at end of file diff --git a/Src/Graphics/New3D/R3DScrollFog.h b/Src/Graphics/New3D/R3DScrollFog.h index c71b9dc..d00ce31 100644 --- a/Src/Graphics/New3D/R3DScrollFog.h +++ b/Src/Graphics/New3D/R3DScrollFog.h @@ -10,13 +10,13 @@ class R3DScrollFog { public: - R3DScrollFog(const Util::Config::Node &config); - ~R3DScrollFog(); - - void DrawScrollFog(float rbga[4], float attenuation, float ambient, float *spotRGB, float *spotEllipse); - -private: - + R3DScrollFog(const Util::Config::Node &config); + ~R3DScrollFog(); + + void DrawScrollFog(float rbga[4], float attenuation, float ambient, float *spotRGB, float *spotEllipse); + +private: + void AllocResources(); void DeallocResources(); @@ -45,19 +45,19 @@ private: 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; -}; + + 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; +}; } diff --git a/Src/Graphics/New3D/R3DShader.cpp b/Src/Graphics/New3D/R3DShader.cpp index 41c809c..66ca861 100644 --- a/Src/Graphics/New3D/R3DShader.cpp +++ b/Src/Graphics/New3D/R3DShader.cpp @@ -114,9 +114,16 @@ vec4 GetTextureValue() } } - if(discardAlpha && textureAlpha) { - if (tex1Data.a < 1.0) { - discard; + if(textureAlpha) { + if(discardAlpha) { // opaque 1st pass + if (tex1Data.a < 1.0) { + discard; + } + } + else { // transparent 2nd pass + if ((tex1Data.a * fsColor.a) >= 1.0) { + discard; + } } } diff --git a/VS2008/Supermodel.vcxproj b/VS2008/Supermodel.vcxproj index b608e70..08cab1b 100644 --- a/VS2008/Supermodel.vcxproj +++ b/VS2008/Supermodel.vcxproj @@ -311,11 +311,13 @@ xcopy /D /Y "$(ProjectDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDi + + @@ -537,6 +539,7 @@ xcopy /D /Y "$(ProjectDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDi + @@ -544,6 +547,7 @@ xcopy /D /Y "$(ProjectDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDi + diff --git a/VS2008/Supermodel.vcxproj.filters b/VS2008/Supermodel.vcxproj.filters index ce0b0e0..e010abd 100644 --- a/VS2008/Supermodel.vcxproj.filters +++ b/VS2008/Supermodel.vcxproj.filters @@ -464,6 +464,12 @@ Source Files\Debugger + + Source Files\Graphics\New + + + Source Files\Graphics\New + @@ -862,6 +868,12 @@ Header Files\Debugger + + Source Files\Graphics\New + + + Source Files\Graphics\New +