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.

This commit is contained in:
Ian Curtis 2018-06-16 21:31:29 +00:00
parent 7497e3f1a6
commit c7ffd0a808
11 changed files with 738 additions and 111 deletions

View file

@ -0,0 +1,112 @@
#include "GLSLShader.h"
#include <stdio.h>
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);
}

View file

@ -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

View file

@ -86,20 +86,32 @@ struct Poly // our polys are always 3 triangles, unlike the real h/w
FVertex p3; FVertex p3;
}; };
enum class Layer { colour, trans1, trans2, all, none };
struct Mesh struct Mesh
{ {
//helper funcs //helper funcs
bool Render(bool alpha) bool Render(Layer layer)
{ {
if (alpha) { switch (layer)
if (!textureAlpha && !polyAlpha) { {
return false; case Layer::colour:
}
}
else {
if (polyAlpha) { if (polyAlpha) {
return false; 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; return true;
@ -117,7 +129,6 @@ struct Mesh
float microTextureScale = 0; float microTextureScale = 0;
// attributes // attributes
bool doubleSided = false;
bool textured = false; bool textured = false;
bool polyAlpha = false; // specified in the rgba colour bool polyAlpha = false; // specified in the rgba colour
bool textureAlpha = false; // use alpha in texture bool textureAlpha = false; // use alpha in texture

View file

@ -79,6 +79,8 @@ bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yR
m_r3dShader.LoadShader(); m_r3dShader.LoadShader();
m_r3dFrameBuffers.CreateFBO(totalXResParam, totalYResParam);
glUseProgram(0); glUseProgram(0);
return OKAY; // OKAY ? wtf .. 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) bool hasOverlay = false; // (high priority polys)
if (alpha) {
glEnable(GL_BLEND);
}
for (auto &n : m_nodes) { for (auto &n : m_nodes) {
if (n.viewport.priority != priority || n.models.empty()) { if (n.viewport.priority != priority || n.models.empty()) {
@ -188,7 +186,7 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, bool alpha)
hasOverlay = true; hasOverlay = true;
} }
if (!mesh.Render(alpha)) continue; if (!mesh.Render(layer)) continue;
if (mesh.highPriority != renderOverlay) continue; if (mesh.highPriority != renderOverlay) continue;
if (!matrixLoaded) { if (!matrixLoaded) {
@ -236,6 +234,65 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, bool alpha)
return hasOverlay; 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) void CNew3D::RenderFrame(void)
{ {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -252,17 +309,6 @@ void CNew3D::RenderFrame(void)
RenderViewport(0x800000); // build model structure RenderViewport(0x800000); // build model structure
DrawScrollFog(); // fog layer if applicable must be drawn here 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.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 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++) { for (int pri = 0; pri <= 3; pri++) {
@ -311,36 +340,37 @@ void CNew3D::RenderFrame(void)
bool hasOverlay; bool hasOverlay;
//============== //==============
glViewport (0, 0, m_totalXRes, m_totalYRes); // clear whole viewport if (SkipLayer(pri)) continue;
glClear (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
m_r3dShader.DiscardAlpha(true); // chuck out alpha pixels in texture alpha only polys for (int i = 0; i < 2; i++) {
hasOverlay = RenderScene(pri, false, false);
m_r3dShader.DiscardAlpha(false);
hasOverlay = RenderScene(pri, false, true);
if (hasOverlay) { bool renderOverlay = (i == 1);
//clear depth buffer and render high priority polys
glViewport(0, 0, m_totalXRes, m_totalYRes); // clear whole viewport m_r3dFrameBuffers.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
m_r3dShader.DiscardAlpha(true); SetRenderStates();
RenderScene(pri, true, false);
m_r3dShader.DiscardAlpha(false); m_r3dShader.DiscardAlpha(true); // discard all translucent pixels in opaque pass
RenderScene(pri, true, true); 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_r3dFrameBuffers.DisableFBO(); // draw to back buffer normally
m_vbo.Bind(false);
glDisable(GL_STENCIL_TEST);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(4);
glDisableVertexAttribArray(5);
} }
void CNew3D::BeginFrame(void) void CNew3D::BeginFrame(void)
@ -469,6 +499,13 @@ void CNew3D::DescendCullingNode(UINT32 addr)
matrixOffset = node[0x03 - m_offset] & 0xFFF; matrixOffset = node[0x03 - m_offset] & 0xFFF;
lodTablePointer = (node[0x03 - m_offset] >> 12) & 0x7F; 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)) { 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 = ((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 m_colorTableAddr &= 0x000FFFFF; // clamp to 4MB (in words) range
@ -558,13 +595,6 @@ void CNew3D::DescendCullingNode(UINT32 addr)
// Restore old texture offsets // Restore old texture offsets
m_nodeAttribs.Pop(); 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) void CNew3D::DescendNodePtr(UINT32 nodeAddr)
@ -954,7 +984,6 @@ void CNew3D::OffsetTexCoords(R3DPoly& r3dPoly, float offset[2])
void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph) void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph)
{ {
//copy attributes //copy attributes
currentMesh->doubleSided = false; // we will double up polys
currentMesh->textured = ph.TexEnabled(); currentMesh->textured = ph.TexEnabled();
currentMesh->alphaTest = ph.AlphaTest(); currentMesh->alphaTest = ph.AlphaTest();
currentMesh->textureAlpha = ph.TextureAlpha(); currentMesh->textureAlpha = ph.TextureAlpha();
@ -963,16 +992,11 @@ void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph)
currentMesh->fixedShading = ph.FixedShading() && !ph.SmoothShading(); currentMesh->fixedShading = ph.FixedShading() && !ph.SmoothShading();
currentMesh->highPriority = ph.HighPriority(); currentMesh->highPriority = ph.HighPriority();
currentMesh->transLSelect = ph.TranslucencyPatternSelect(); currentMesh->transLSelect = ph.TranslucencyPatternSelect();
currentMesh->layered = ph.Layered();
if (ph.Layered() || (!ph.TexEnabled() && ph.PolyAlpha())) { currentMesh->specular = ph.SpecularEnabled();
currentMesh->layered = true; currentMesh->shininess = ph.Shininess();
} currentMesh->specularValue = ph.SpecularValue();
currentMesh->fogIntensity = ph.LightModifier();
currentMesh->specular = ph.SpecularEnabled();
currentMesh->shininess = ph.Shininess();
currentMesh->specularValue = ph.SpecularValue();
currentMesh->fogIntensity = ph.LightModifier();
if (currentMesh->textured) { if (currentMesh->textured) {
@ -1107,7 +1131,7 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
if (ph.Discard1() && !ph.Discard2()) { if (ph.Discard1() && !ph.Discard2()) {
p.faceColour[3] /= 2; p.faceColour[3] /= 2;
} }
// if we have flat shading, we can't re-use normals from shared vertices // if we have flat shading, we can't re-use normals from shared vertices
for (i = 0; i < p.number && !ph.SmoothShading(); i++) { for (i = 0; i < p.number && !ph.SmoothShading(); i++) {
p.v[i].normal[0] = p.faceNormal[0]; 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. * the same as a 496x384 Model 3 display. The display will be distorted.
*/ */
float windowAR = (float)m_totalXRes / (float)m_totalYRes; 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- // Will expand horizontal frustum planes only in non-stretch mode (wide-
// screen and non-wide-screen modes have identical resolution parameters // screen and non-wide-screen modes have identical resolution parameters

View file

@ -1,3 +1,4 @@
/** /**
** Supermodel ** Supermodel
** A Sega Model 3 Arcade Emulator. ** A Sega Model 3 Arcade Emulator.
@ -41,6 +42,7 @@
#include "Vec.h" #include "Vec.h"
#include "R3DScrollFog.h" #include "R3DScrollFog.h"
#include "PolyHeader.h" #include "PolyHeader.h"
#include "R3DFrameBuffers.h"
namespace New3D { namespace New3D {
@ -199,11 +201,14 @@ private:
void CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray); void CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray);
void OffsetTexCoords(R3DPoly& r3dPoly, float offset[2]); 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]); float Determinant3x3(const float m[16]);
bool IsDynamicModel(UINT32 *data); // check if the model has a colour palette bool IsDynamicModel(UINT32 *data); // check if the model has a colour palette
bool IsVROMModel(UINT32 modelAddr); bool IsVROMModel(UINT32 modelAddr);
void DrawScrollFog(); 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); 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 VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow
R3DShader m_r3dShader; R3DShader m_r3dShader;
R3DScrollFog m_r3dScrollFog; R3DScrollFog m_r3dScrollFog;
R3DFrameBuffers m_r3dFrameBuffers;
Plane m_planes[4]; Plane m_planes[4];

View file

@ -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();
}
}

View file

@ -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

View file

@ -10,13 +10,13 @@ class R3DScrollFog
{ {
public: public:
R3DScrollFog(const Util::Config::Node &config); R3DScrollFog(const Util::Config::Node &config);
~R3DScrollFog(); ~R3DScrollFog();
void DrawScrollFog(float rbga[4], float attenuation, float ambient, float *spotRGB, float *spotEllipse); void DrawScrollFog(float rbga[4], float attenuation, float ambient, float *spotRGB, float *spotEllipse);
private: private:
void AllocResources(); void AllocResources();
void DeallocResources(); void DeallocResources();
@ -45,19 +45,19 @@ private:
GLuint m_shaderProgram; GLuint m_shaderProgram;
GLuint m_vertexShader; GLuint m_vertexShader;
GLuint m_fragmentShader; GLuint m_fragmentShader;
GLint m_locFogColour; GLint m_locFogColour;
GLint m_locMVP; GLint m_locMVP;
GLint m_locFogAttenuation; GLint m_locFogAttenuation;
GLint m_locFogAmbient; GLint m_locFogAmbient;
GLint m_locSpotFogColor; GLint m_locSpotFogColor;
GLint m_locSpotEllipse; GLint m_locSpotEllipse;
// vertex attrib locs // vertex attrib locs
GLint m_locInVertex; GLint m_locInVertex;
VBO m_vbo; VBO m_vbo;
}; };
} }

View file

@ -114,9 +114,16 @@ vec4 GetTextureValue()
} }
} }
if(discardAlpha && textureAlpha) { if(textureAlpha) {
if (tex1Data.a < 1.0) { if(discardAlpha) { // opaque 1st pass
discard; if (tex1Data.a < 1.0) {
discard;
}
}
else { // transparent 2nd pass
if ((tex1Data.a * fsColor.a) >= 1.0) {
discard;
}
} }
} }

View file

@ -311,11 +311,13 @@ xcopy /D /Y "$(ProjectDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDi
<ClCompile Include="..\Src\Graphics\Legacy3D\Legacy3D.cpp" /> <ClCompile Include="..\Src\Graphics\Legacy3D\Legacy3D.cpp" />
<ClCompile Include="..\Src\Graphics\Legacy3D\Models.cpp" /> <ClCompile Include="..\Src\Graphics\Legacy3D\Models.cpp" />
<ClCompile Include="..\Src\Graphics\Legacy3D\TextureRefs.cpp" /> <ClCompile Include="..\Src\Graphics\Legacy3D\TextureRefs.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\GLSLShader.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\Mat4.cpp" /> <ClCompile Include="..\Src\Graphics\New3D\Mat4.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\Model.cpp" /> <ClCompile Include="..\Src\Graphics\New3D\Model.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\New3D.cpp" /> <ClCompile Include="..\Src\Graphics\New3D\New3D.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\PolyHeader.cpp" /> <ClCompile Include="..\Src\Graphics\New3D\PolyHeader.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\R3DFloat.cpp" /> <ClCompile Include="..\Src\Graphics\New3D\R3DFloat.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\R3DFrameBuffers.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\R3DScrollFog.cpp" /> <ClCompile Include="..\Src\Graphics\New3D\R3DScrollFog.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\R3DShader.cpp" /> <ClCompile Include="..\Src\Graphics\New3D\R3DShader.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\Texture.cpp" /> <ClCompile Include="..\Src\Graphics\New3D\Texture.cpp" />
@ -537,6 +539,7 @@ xcopy /D /Y "$(ProjectDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDi
<ClInclude Include="..\Src\Graphics\Legacy3D\Legacy3D.h" /> <ClInclude Include="..\Src\Graphics\Legacy3D\Legacy3D.h" />
<ClInclude Include="..\Src\Graphics\Legacy3D\Shaders3D.h" /> <ClInclude Include="..\Src\Graphics\Legacy3D\Shaders3D.h" />
<ClInclude Include="..\Src\Graphics\Legacy3D\TextureRefs.h" /> <ClInclude Include="..\Src\Graphics\Legacy3D\TextureRefs.h" />
<ClInclude Include="..\Src\Graphics\New3D\GLSLShader.h" />
<ClInclude Include="..\Src\Graphics\New3D\Mat4.h" /> <ClInclude Include="..\Src\Graphics\New3D\Mat4.h" />
<ClInclude Include="..\Src\Graphics\New3D\Model.h" /> <ClInclude Include="..\Src\Graphics\New3D\Model.h" />
<ClInclude Include="..\Src\Graphics\New3D\New3D.h" /> <ClInclude Include="..\Src\Graphics\New3D\New3D.h" />
@ -544,6 +547,7 @@ xcopy /D /Y "$(ProjectDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDi
<ClInclude Include="..\Src\Graphics\New3D\PolyHeader.h" /> <ClInclude Include="..\Src\Graphics\New3D\PolyHeader.h" />
<ClInclude Include="..\Src\Graphics\New3D\R3DData.h" /> <ClInclude Include="..\Src\Graphics\New3D\R3DData.h" />
<ClInclude Include="..\Src\Graphics\New3D\R3DFloat.h" /> <ClInclude Include="..\Src\Graphics\New3D\R3DFloat.h" />
<ClInclude Include="..\Src\Graphics\New3D\R3DFrameBuffers.h" />
<ClInclude Include="..\Src\Graphics\New3D\R3DScrollFog.h" /> <ClInclude Include="..\Src\Graphics\New3D\R3DScrollFog.h" />
<ClInclude Include="..\Src\Graphics\New3D\R3DShader.h" /> <ClInclude Include="..\Src\Graphics\New3D\R3DShader.h" />
<ClInclude Include="..\Src\Graphics\New3D\Texture.h" /> <ClInclude Include="..\Src\Graphics\New3D\Texture.h" />

View file

@ -464,6 +464,12 @@
<ClCompile Include="..\Src\Debugger\DebuggerIO.cpp"> <ClCompile Include="..\Src\Debugger\DebuggerIO.cpp">
<Filter>Source Files\Debugger</Filter> <Filter>Source Files\Debugger</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\Src\Graphics\New3D\R3DFrameBuffers.cpp">
<Filter>Source Files\Graphics\New</Filter>
</ClCompile>
<ClCompile Include="..\Src\Graphics\New3D\GLSLShader.cpp">
<Filter>Source Files\Graphics\New</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<MASM Include="..\Src\CPU\68K\Turbo68K\Turbo68K.asm"> <MASM Include="..\Src\CPU\68K\Turbo68K\Turbo68K.asm">
@ -862,6 +868,12 @@
<ClInclude Include="..\Src\Debugger\DebuggerIO.h"> <ClInclude Include="..\Src\Debugger\DebuggerIO.h">
<Filter>Header Files\Debugger</Filter> <Filter>Header Files\Debugger</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Src\Graphics\New3D\R3DFrameBuffers.h">
<Filter>Source Files\Graphics\New</Filter>
</ClInclude>
<ClInclude Include="..\Src\Graphics\New3D\GLSLShader.h">
<Filter>Source Files\Graphics\New</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CustomBuild Include="..\Src\Debugger\ReadMe.txt"> <CustomBuild Include="..\Src\Debugger\ReadMe.txt">