mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-24 06:35:41 +00:00
6595b9320e
Some games update the tilegen after the ping_ping bit has flipped at 66% of the frame, so we need to split the tilegen drawing up into two stages to get some effects to work. So having the tilegen draw independantly of the 3d chip can make this happen.
348 lines
8.9 KiB
C++
348 lines
8.9 KiB
C++
#include "R3DFrameBuffers.h"
|
|
|
|
namespace New3D {
|
|
|
|
R3DFrameBuffers::R3DFrameBuffers()
|
|
{
|
|
m_frameBufferID = 0;
|
|
m_renderBufferID = 0;
|
|
m_frameBufferIDCopy = 0;
|
|
m_renderBufferIDCopy = 0;
|
|
m_width = 0;
|
|
m_height = 0;
|
|
m_vao = 0;
|
|
|
|
for (auto &i : m_texIDs) {
|
|
i = 0;
|
|
}
|
|
|
|
m_lastLayer = Layer::none;
|
|
|
|
AllocShaderTrans();
|
|
AllocShaderBase();
|
|
|
|
glGenVertexArrays(1, &m_vao);
|
|
glBindVertexArray(m_vao);
|
|
// no states needed since we do it in the shader
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
R3DFrameBuffers::~R3DFrameBuffers()
|
|
{
|
|
DestroyFBO();
|
|
m_shaderTrans.UnloadShaders();
|
|
m_shaderBase.UnloadShaders();
|
|
|
|
if (m_vao) {
|
|
glDeleteVertexArrays(1, &m_vao);
|
|
m_vao = 0;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
switch (layer)
|
|
{
|
|
case Layer::colour:
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
|
|
GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
|
|
glDrawBuffers((GLsizei)std::size(buffers), buffers);
|
|
break;
|
|
}
|
|
case Layer::trans1:
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
|
|
GLenum buffers[] = { GL_NONE, GL_COLOR_ATTACHMENT1, GL_NONE };
|
|
glDrawBuffers((GLsizei)std::size(buffers), buffers);
|
|
break;
|
|
}
|
|
case Layer::trans2:
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
|
|
GLenum buffers[] = { GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT2 };
|
|
glDrawBuffers((GLsizei)std::size(buffers), buffers);
|
|
break;
|
|
}
|
|
case Layer::none:
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glDrawBuffer(GL_BACK);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_lastLayer = layer;
|
|
}
|
|
|
|
void R3DFrameBuffers::AllocShaderBase()
|
|
{
|
|
const char *vertexShader = 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;
|
|
gl_Position = vertices[gl_VertexID % 4];
|
|
}
|
|
|
|
)glsl";
|
|
|
|
const char *fragmentShader = R"glsl(
|
|
|
|
#version 410 core
|
|
|
|
// inputs
|
|
uniform sampler2D tex1; // base tex
|
|
in vec2 fsTexCoord;
|
|
|
|
// outputs
|
|
out vec4 fragColor;
|
|
|
|
void main()
|
|
{
|
|
vec4 colBase = texture(tex1, fsTexCoord);
|
|
if(colBase.a < 1.0) discard;
|
|
fragColor = colBase;
|
|
}
|
|
|
|
)glsl";
|
|
|
|
m_shaderBase.LoadShaders(vertexShader, fragmentShader);
|
|
m_shaderBase.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1");
|
|
}
|
|
|
|
void R3DFrameBuffers::AllocShaderTrans()
|
|
{
|
|
const char *vertexShader = 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;
|
|
gl_Position = vertices[gl_VertexID % 4];
|
|
}
|
|
|
|
)glsl";
|
|
|
|
const char *fragmentShader = R"glsl(
|
|
|
|
#version 410 core
|
|
|
|
uniform sampler2D tex1; // trans layer 1
|
|
uniform sampler2D tex2; // trans layer 2
|
|
|
|
in vec2 fsTexCoord;
|
|
|
|
// outputs
|
|
out vec4 fragColor;
|
|
|
|
void main()
|
|
{
|
|
vec4 colTrans1 = texture(tex1, fsTexCoord);
|
|
vec4 colTrans2 = texture(tex2, fsTexCoord);
|
|
|
|
if(colTrans1.a+colTrans2.a > 0.0) {
|
|
vec3 col1 = colTrans1.rgb * colTrans1.a;
|
|
vec3 col2 = colTrans2.rgb * colTrans2.a;
|
|
|
|
colTrans1 = vec4((col1+col2) / (colTrans1.a + colTrans2.a), // this is my best guess at the blending between the layers
|
|
colTrans1.a+colTrans2.a);
|
|
}
|
|
|
|
fragColor = colTrans1;
|
|
}
|
|
|
|
)glsl";
|
|
|
|
m_shaderTrans.LoadShaders(vertexShader, fragmentShader);
|
|
|
|
m_shaderTrans.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1");
|
|
m_shaderTrans.uniformLoc[1] = m_shaderTrans.GetUniformLocation("tex2");
|
|
}
|
|
|
|
void R3DFrameBuffers::Draw()
|
|
{
|
|
SetFBO (Layer::none); // 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);
|
|
glDisable (GL_BLEND);
|
|
|
|
for (int i = 0; i < (int)std::size(m_texIDs); i++) { // bind our textures to correct texture units
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
|
glBindTexture(GL_TEXTURE_2D, m_texIDs[i]);
|
|
}
|
|
|
|
glActiveTexture (GL_TEXTURE0);
|
|
glBindVertexArray (m_vao);
|
|
|
|
DrawBaseLayer ();
|
|
|
|
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable (GL_BLEND);
|
|
|
|
DrawAlphaLayer ();
|
|
|
|
glDisable (GL_BLEND);
|
|
glBindVertexArray (0);
|
|
}
|
|
|
|
void R3DFrameBuffers::DrawBaseLayer()
|
|
{
|
|
m_shaderBase.EnableShader();
|
|
glUniform1i(m_shaderTrans.uniformLoc[0], 0);
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
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
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
m_shaderTrans.DisableShader();
|
|
}
|
|
|
|
}
|