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 +