From b5f9ad96510704b6f25b25de2b2c5d738c1ab89f Mon Sep 17 00:00:00 2001 From: Ian Curtis Date: Thu, 13 Sep 2018 12:50:34 +0000 Subject: [PATCH] Quad rendering engine. Set QuadRendering = 1 in the ini file to use, or -quad-rendering at the end of the command line to use. --- Src/Graphics/New3D/Model.h | 96 +++-- Src/Graphics/New3D/New3D.cpp | 137 +++--- Src/Graphics/New3D/New3D.h | 11 +- Src/Graphics/New3D/R3DShader.cpp | 485 ++++++---------------- Src/Graphics/New3D/R3DShader.h | 11 +- Src/Graphics/New3D/R3DShaderQuads.h | 527 ++++++++++++++++++++++++ Src/Graphics/New3D/R3DShaderTriangles.h | 300 ++++++++++++++ Src/OSD/SDL/Main.cpp | 3 + VS2008/Supermodel.sln | 8 + VS2008/Supermodel.vcxproj | 2 + VS2008/Supermodel.vcxproj.filters | 6 + 11 files changed, 1123 insertions(+), 463 deletions(-) create mode 100644 Src/Graphics/New3D/R3DShaderQuads.h create mode 100644 Src/Graphics/New3D/R3DShaderTriangles.h diff --git a/Src/Graphics/New3D/Model.h b/Src/Graphics/New3D/Model.h index ff3380d..e6c1b59 100644 --- a/Src/Graphics/New3D/Model.h +++ b/Src/Graphics/New3D/Model.h @@ -22,12 +22,43 @@ struct ClipPoly int count = 0; }; + struct Vertex // half vertex { float pos[4]; float normal[3]; float texcoords[2]; float fixedShade; + + static bool Equal(const Vertex& p1, const Vertex& p2) + { + if (p1.pos[0] == p2.pos[0] && + p1.pos[1] == p2.pos[1] && + p1.pos[2] == p2.pos[2]) + { + return true; + } + + return false; + } + + static void Average(const Vertex& p1, const Vertex& p2, Vertex& p3) + { + p3.pos[3] = 1.0f; //always 1 + p3.fixedShade = (p1.fixedShade + p2.fixedShade) / 2.0f; + + for (int i = 0; i < 3; i++) { p3.pos[i] = (p1.pos[i] + p2.pos[i]) / 2.0f; } + for (int i = 0; i < 3; i++) { p3.normal[i] = (p1.normal[i] + p2.normal[i]) / 2.0f; } + for (int i = 0; i < 2; i++) { p3.texcoords[i] = (p1.texcoords[i] + p2.texcoords[i]) / 2.0f; } + } +}; + +struct R3DPoly +{ + Vertex v[4]; // just easier to have them as an array + float faceNormal[3]; // we need this to help work out poly winding, i assume the h/w uses this instead of calculating normals itself + UINT8 faceColour[4]; // per face colour + int number = 4; }; struct FVertex : Vertex // full vertex including face attributes @@ -40,50 +71,31 @@ struct FVertex : Vertex // full vertex including face attributes memcpy(this, &vertex, sizeof(Vertex)); return *this; } -}; -struct R3DPoly -{ - Vertex v[4]; // just easier to have them as an array - float faceNormal[3]; // we need this to help work out poly winding, i assume the h/w uses this instead of calculating normals itself - UINT8 faceColour[4]; // per face colour - int number = 4; -}; + FVertex() {} + FVertex(const R3DPoly& r3dPoly, int index) + { + for (int i = 0; i < 4; i++) { faceColour[i] = r3dPoly.faceColour[i]; } + for (int i = 0; i < 3; i++) { faceNormal[i] = r3dPoly.faceNormal[i]; } -struct Poly // our polys are always 3 triangles, unlike the real h/w -{ - Poly() {}; // default - - Poly(bool firstTriangle, const R3DPoly& r3dPoly) { - - if (firstTriangle) { - p1 = r3dPoly.v[0]; - p2 = r3dPoly.v[1]; - p3 = r3dPoly.v[2]; - } - else { - p1 = r3dPoly.v[0]; - p2 = r3dPoly.v[2]; - p3 = r3dPoly.v[3]; - } - - // copy face attributes - for (int i = 0; i < 4; i++) { - p1.faceColour[i] = r3dPoly.faceColour[i]; - p2.faceColour[i] = r3dPoly.faceColour[i]; - p3.faceColour[i] = r3dPoly.faceColour[i]; - } - - for (int i = 0; i < 3; i++) { - p1.faceNormal[i] = r3dPoly.faceNormal[i]; - p2.faceNormal[i] = r3dPoly.faceNormal[i]; - p3.faceNormal[i] = r3dPoly.faceNormal[i]; - } + *this = r3dPoly.v[index]; } - FVertex p1; - FVertex p2; - FVertex p3; + FVertex(const R3DPoly& r3dPoly, int index1, int index2) // average of 2 points + { + Vertex::Average(r3dPoly.v[index1], r3dPoly.v[index2], *this); + + // copy face attributes + for (int i = 0; i < 4; i++) { faceColour[i] = r3dPoly.faceColour[i]; } + for (int i = 0; i < 3; i++) { faceNormal[i] = r3dPoly.faceNormal[i]; } + } + + static void Average(const FVertex& p1, const FVertex& p2, FVertex& p3) + { + Vertex::Average(p1, p2, p3); + for (int i = 0; i < 4; i++) { p3.faceColour[i] = p1.faceColour[i]; } + for (int i = 0; i < 3; i++) { p3.faceNormal[i] = p1.faceNormal[i]; } + } }; enum class Layer { colour, trans1, trans2, all, none }; @@ -150,12 +162,12 @@ struct Mesh // opengl resources int vboOffset = 0; // this will be calculated later - int triangleCount = 0; + int vertexCount = 0; // /3 for triangles /4 for quads }; struct SortingMesh : public Mesh // This struct temporarily holds the model data, before it gets copied to the main buffer { - std::vector polys; + std::vector verts; }; struct Model diff --git a/Src/Graphics/New3D/New3D.cpp b/Src/Graphics/New3D/New3D.cpp index bde3bdc..eb00dc7 100644 --- a/Src/Graphics/New3D/New3D.cpp +++ b/Src/Graphics/New3D/New3D.cpp @@ -7,8 +7,8 @@ #include #include "R3DFloat.h" -#define MAX_RAM_POLYS 100000 -#define MAX_ROM_POLYS 500000 +#define MAX_RAM_VERTS 300000 +#define MAX_ROM_VERTS 1500000 #define BYTE_TO_FLOAT(B) ((2.0f * (B) + 1.0f) * (1.0F/255.0f)) @@ -26,6 +26,13 @@ CNew3D::CNew3D(const Util::Config::Node &config, std::string gameName) m_textureRAM = nullptr; m_sunClamp = true; m_shadeIsSigned = true; + m_numPolyVerts = 3; + m_primType = GL_TRIANGLES; + + if (config["QuadRendering"].ValueAs()) { + m_numPolyVerts = 4; + m_primType = GL_LINES_ADJACENCY; + } } CNew3D::~CNew3D() @@ -59,7 +66,7 @@ void CNew3D::SetStepping(int stepping) m_vertexFactor = (1.0f / 128.0f); // 17.7 } - m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(Poly) * (MAX_RAM_POLYS + MAX_ROM_POLYS)); + m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(FVertex) * (MAX_RAM_VERTS + MAX_ROM_VERTS)); } bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam) @@ -159,11 +166,7 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer) std::shared_ptr tex1; CalcViewport(&n.viewport, std::abs(m_nfPairs[priority].zNear*0.95f), std::abs(m_nfPairs[priority].zFar*1.05f)); // make planes 5% bigger - - glViewport (n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height); - glMatrixMode (GL_PROJECTION); - glLoadMatrixf (n.viewport.projectionMatrix); - glMatrixMode (GL_MODELVIEW); + glViewport(n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height); m_r3dShader.SetViewportUniforms(&n.viewport); @@ -221,7 +224,7 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer) } m_r3dShader.SetMeshUniforms(&mesh); - glDrawArrays(GL_TRIANGLES, mesh.vboOffset*3, mesh.triangleCount*3); // times 3 to convert triangles to vertices + glDrawArrays(m_primType, mesh.vboOffset, mesh.vertexCount); } } } @@ -308,25 +311,25 @@ void CNew3D::RenderFrame(void) DrawScrollFog(); // fog layer if applicable must be drawn here 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_VERTS*sizeof(FVertex), m_polyBufferRam.size()*sizeof(FVertex), m_polyBufferRam.data()); // upload all the dynamic data to GPU in one go if (m_polyBufferRom.size()) { // sync rom memory with vbo - int romBytes = (int)m_polyBufferRom.size() * sizeof(Poly); + int romBytes = (int)m_polyBufferRom.size() * sizeof(FVertex); int vboBytes = m_vbo.GetSize(); int size = romBytes - vboBytes; if (size) { //check we haven't blown up the memory buffers //we will lose rom models for 1 frame is this happens, not the end of the world, as probably won't ever happen anyway - if (m_polyBufferRom.size() >= MAX_ROM_POLYS) { + if (m_polyBufferRom.size() >= MAX_ROM_VERTS) { m_polyBufferRom.clear(); m_romMap.clear(); m_vbo.Reset(); } else { - m_vbo.AppendData(size, &m_polyBufferRom[vboBytes / sizeof(Poly)]); + m_vbo.AppendData(size, &m_polyBufferRom[vboBytes / sizeof(FVertex)]); } } } @@ -423,7 +426,7 @@ bool CNew3D::DrawModel(UINT32 modelAddr) modelAddress = TranslateModelAddress(modelAddr); // create a new model to push onto the vector - m_nodes.back().models.emplace_back(Model()); + m_nodes.back().models.emplace_back(); // get the last model in the array m = &m_nodes.back().models.back(); @@ -811,7 +814,6 @@ void CNew3D::RenderViewport(UINT32 addr) vp->angle_top = (1.0f / cw) * -(0.0f - io); // calculate the frustum shape, near/far pair are dummy values - CalcViewport(vp, 1, 1000); // calculate frustum planes @@ -924,12 +926,46 @@ void CNew3D::RenderViewport(UINT32 addr) } } -void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector& polyArray) +void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector& vertexArray) { - polyArray.emplace_back(Poly(true,r3dPoly)); // create object directly in array without temporary copy + if (m_numPolyVerts==4) { + if (r3dPoly.number == 4) { + vertexArray.emplace_back(r3dPoly, 0); // construct directly inside container without copy + vertexArray.emplace_back(r3dPoly, 1); + vertexArray.emplace_back(r3dPoly, 2); + vertexArray.emplace_back(r3dPoly, 3); - if (r3dPoly.number == 4) { - polyArray.emplace_back(Poly(false, r3dPoly)); // copy second triangle + // check for identical points (ie forced triangle) and replace with average point + // if we don't do this our quad code falls apart + FVertex* v = (&vertexArray.back()) - 3; + + for (int i = 0; i < 4; i++) { + + int next1 = (i + 1) % 4; + int next2 = (i + 2) % 4; + + if (FVertex::Equal(v[i], v[next1])) { + FVertex::Average(v[i], v[next1], v[next2]); + } + } + } + else { + vertexArray.emplace_back(r3dPoly, 0); + vertexArray.emplace_back(r3dPoly, 1); + vertexArray.emplace_back(r3dPoly, 2); + vertexArray.emplace_back(r3dPoly, 0, 2); // last point is an average of 0 and 2 + } + } + else { + vertexArray.emplace_back(r3dPoly, 0); + vertexArray.emplace_back(r3dPoly, 1); + vertexArray.emplace_back(r3dPoly, 2); + + if (r3dPoly.number == 4) { + vertexArray.emplace_back(r3dPoly, 0); + vertexArray.emplace_back(r3dPoly, 2); + vertexArray.emplace_back(r3dPoly, 3); + } } } @@ -1062,7 +1098,7 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) currentMesh = &sMap[hash]; //make space for our vertices - currentMesh->polys.reserve(numTriangles); + currentMesh->verts.reserve(numTriangles * 3); //set mesh values SetMeshValues(currentMesh, ph); @@ -1222,12 +1258,12 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) V3::inverse(tempP.v[i].normal); } - CopyVertexData(tempP, currentMesh->polys); + CopyVertexData(tempP, currentMesh->verts); } // Copy this polygon into the model buffer if (!ph.Discard()) { - CopyVertexData(p, currentMesh->polys); + CopyVertexData(p, currentMesh->verts); } // Copy current vertices into previous vertex array @@ -1249,19 +1285,19 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) if (m->dynamic) { // calculate VBO values for current mesh - it.second.vboOffset = m_polyBufferRam.size() + MAX_ROM_POLYS; - it.second.triangleCount = it.second.polys.size(); + it.second.vboOffset = (int)m_polyBufferRam.size() + MAX_ROM_VERTS; + it.second.vertexCount = (int)it.second.verts.size(); // copy poly data to main buffer - m_polyBufferRam.insert(m_polyBufferRam.end(), it.second.polys.begin(), it.second.polys.end()); + m_polyBufferRam.insert(m_polyBufferRam.end(), it.second.verts.begin(), it.second.verts.end()); } else { // calculate VBO values for current mesh - it.second.vboOffset = m_polyBufferRom.size(); - it.second.triangleCount = it.second.polys.size(); + it.second.vboOffset = (int)m_polyBufferRom.size(); + it.second.vertexCount = (int)it.second.verts.size(); // copy poly data to main buffer - m_polyBufferRom.insert(m_polyBufferRom.end(), it.second.polys.begin(), it.second.polys.end()); + m_polyBufferRom.insert(m_polyBufferRom.end(), it.second.verts.begin(), it.second.verts.end()); } //copy the temp mesh into the model structure @@ -1270,21 +1306,6 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) } } -float CNew3D::Determinant3x3(const float m[16]) { - - /* - | A B C | - M = | D E F | - | G H I | - - then the determinant is calculated as follows: - - det M = A * (EI - HF) - B * (DI - GF) + C * (DH - GE) - */ - - return m[0] * ((m[5] * m[10]) - (m[6] * m[9])) - m[4] * ((m[1] * m[10]) - (m[2] * m[9])) + m[8] * ((m[1] * m[6]) - (m[2] * m[5])); -} - bool CNew3D::IsDynamicModel(UINT32 *data) { if (data == NULL) { @@ -1551,18 +1572,18 @@ void CNew3D::ClipPolygon(ClipPoly& clipPoly, Plane planes[4]) void CNew3D::ClipModel(const Model *m) { - //================ - ClipPoly clipPoly; - std::vector *polys; - int offset; - //================ + //=============================== + ClipPoly clipPoly; + std::vector* vertices; + int offset; + //=============================== if (m->dynamic) { - polys = &m_polyBufferRam; - offset = MAX_ROM_POLYS; + vertices = &m_polyBufferRam; + offset = MAX_ROM_VERTS; } else { - polys = &m_polyBufferRom; + vertices = &m_polyBufferRom; offset = 0; } @@ -1570,17 +1591,13 @@ void CNew3D::ClipModel(const Model *m) int start = mesh.vboOffset - offset; - for (int i = 0; i < mesh.triangleCount; i++) { + for (int i = 0; i < mesh.vertexCount; i += m_numPolyVerts) { // inc to next poly - //================================== - Poly& poly = (*polys)[start + i]; - //================================== + for (int j = 0; j < m_numPolyVerts; j++) { + MultVec(m->modelMat, (*vertices)[start + i + j].pos, clipPoly.list[j].pos); // copy all 3 of 4 our transformed vertices into our clip poly struct + } - MultVec(m->modelMat, poly.p1.pos, clipPoly.list[0].pos); - MultVec(m->modelMat, poly.p2.pos, clipPoly.list[1].pos); - MultVec(m->modelMat, poly.p3.pos, clipPoly.list[2].pos); - - clipPoly.count = 3; + clipPoly.count = m_numPolyVerts; ClipPolygon(clipPoly, m_planes); diff --git a/Src/Graphics/New3D/New3D.h b/Src/Graphics/New3D/New3D.h index 1e74263..a499e41 100644 --- a/Src/Graphics/New3D/New3D.h +++ b/Src/Graphics/New3D/New3D.h @@ -198,11 +198,10 @@ private: // building the scene void SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph); void CacheModel(Model *m, const UINT32 *data); - void CopyVertexData(const R3DPoly& r3dPoly, std::vector& polyArray); + void CopyVertexData(const R3DPoly& r3dPoly, std::vector& vertexArray); void OffsetTexCoords(R3DPoly& r3dPoly, float offset[2]); 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(); @@ -218,6 +217,8 @@ private: // Misc std::string m_gameName; + int m_numPolyVerts; + GLenum m_primType; // GPU configuration bool m_sunClamp; @@ -253,9 +254,9 @@ private: Vertex m_prev[4]; // these are class variables because sega bass fishing starts meshes with shared vertices from the previous one UINT16 m_prevTexCoords[4][2]; // basically relying on undefined behavour - std::vector m_nodes; // this represents the entire render frame - std::vector m_polyBufferRam; // dynamic polys - std::vector m_polyBufferRom; // rom polys + std::vector m_nodes; // this represents the entire render frame + std::vector m_polyBufferRam; // dynamic polys + std::vector m_polyBufferRom; // rom polys std::unordered_map>> m_romMap; // a hash table for all the ROM models. The meshes don't have model matrices or tex offsets yet VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow diff --git a/Src/Graphics/New3D/R3DShader.cpp b/Src/Graphics/New3D/R3DShader.cpp index a7961a4..1a29916 100644 --- a/Src/Graphics/New3D/R3DShader.cpp +++ b/Src/Graphics/New3D/R3DShader.cpp @@ -1,307 +1,18 @@ #include "R3DShader.h" -#include "Graphics/Shader.h" +#include "R3DShaderQuads.h" +#include "R3DShaderTriangles.h" + +// having 2 sets of shaders to maintain is really less than ideal +// but hopefully not too many breaking changes at this point namespace New3D { -static const char *vertexShaderR3D = R"glsl( - -#version 120 - -// uniforms -uniform float modelScale; - -// attributes -attribute vec4 inVertex; -attribute vec3 inNormal; -attribute vec2 inTexCoord; -attribute vec4 inColour; -attribute vec3 inFaceNormal; // used to emulate r3d culling -attribute float inFixedShade; - -// outputs to fragment shader -varying vec3 fsViewVertex; -varying vec3 fsViewNormal; // per vertex normal vector -varying vec2 fsTexCoord; -varying vec4 fsColor; -varying float fsDiscard; // can't have varying bool (glsl spec) -varying float fsFixedShade; - -float CalcBackFace(in vec3 viewVertex) -{ - vec3 vt = viewVertex - vec3(0.0); - vec3 vn = (mat3(gl_ModelViewMatrix) * inFaceNormal); - - // dot product of face normal with view direction - return dot(vt, vn); -} - -void main(void) -{ - fsViewVertex = vec3(gl_ModelViewMatrix * inVertex); - fsViewNormal = (mat3(gl_ModelViewMatrix) * inNormal) / modelScale; - fsDiscard = CalcBackFace(fsViewVertex); - fsColor = inColour; - fsTexCoord = inTexCoord; - fsFixedShade = inFixedShade; - gl_Position = gl_ModelViewProjectionMatrix * inVertex; -} -)glsl"; - -static const char *fragmentShaderR3D = R"glsl( - -#version 120 - -uniform sampler2D tex1; // base tex -uniform sampler2D tex2; // micro tex (optional) - -// texturing -uniform bool textureEnabled; -uniform bool microTexture; -uniform float microTextureScale; -uniform vec2 baseTexSize; -uniform bool textureInverted; -uniform bool textureAlpha; -uniform bool alphaTest; -uniform bool discardAlpha; - -// general -uniform vec3 fogColour; -uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height) -uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit -uniform vec3 spotColor; // spotlight RGB color -uniform vec3 spotFogColor; // spotlight RGB color on fog -uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0) -uniform bool lightEnabled; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity -uniform bool sunClamp; // not used by daytona and la machine guns -uniform bool intensityClamp; // some games such as daytona and -uniform bool specularEnabled; // specular enabled -uniform float specularValue; // specular coefficient -uniform float shininess; // specular shininess -uniform float fogIntensity; -uniform float fogDensity; -uniform float fogStart; -uniform float fogAttenuation; -uniform float fogAmbient; -uniform bool fixedShading; -uniform int hardwareStep; - -//interpolated inputs from vertex shader -varying vec3 fsViewVertex; -varying vec3 fsViewNormal; // per vertex normal vector -varying vec4 fsColor; -varying vec2 fsTexCoord; -varying float fsDiscard; -varying float fsFixedShade; - -vec4 GetTextureValue() -{ - vec4 tex1Data = texture2D( tex1, fsTexCoord.st); - - if(textureInverted) { - tex1Data.rgb = vec3(1.0) - vec3(tex1Data.rgb); - } - - if (microTexture) { - vec2 scale = (baseTexSize / 128.0) * microTextureScale; - vec4 tex2Data = texture2D( tex2, fsTexCoord.st * scale); - tex1Data = (tex1Data+tex2Data)/2.0; - } - - if (alphaTest) { - if (tex1Data.a < (8.0/16.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; - } - } - } - - if (textureAlpha == false) { - tex1Data.a = 1.0; - } - - return tex1Data; -} - -void Step15Luminous(inout vec4 colour) -{ - // luminous polys seem to behave very differently on step 1.5 hardware - // when fixed shading is enabled the colour is modulated by the vp ambient + fixed shade value - // when disabled it appears to be multiplied by 1.5, presumably to allow a higher range - if(hardwareStep==0x15) { - if(!lightEnabled && textureEnabled) { - if(fixedShading) { - colour.rgb *= 1.0 + fsFixedShade + lighting[1].y; - } - else { - colour.rgb *= vec3(1.5); - } - } - } -} - -float CalcFog() -{ - float z = -fsViewVertex.z; - float fog = fogIntensity * clamp(fogStart + z * fogDensity, 0.0, 1.0); - - return fog; -} - -void main() -{ - vec4 tex1Data; - vec4 colData; - vec4 finalData; - vec4 fogData; - - if(fsDiscard>=0) { - discard; //emulate back face culling here - } - - fogData = vec4(fogColour.rgb * fogAmbient, CalcFog()); - tex1Data = vec4(1.0, 1.0, 1.0, 1.0); - - if(textureEnabled) { - tex1Data = GetTextureValue(); - } - - colData = fsColor; - Step15Luminous(colData); // no-op for step 2.0+ - finalData = tex1Data * colData; - - if (finalData.a < (1.0/16.0)) { // basically chuck out any totally transparent pixels value = 1/16 the smallest transparency level h/w supports - discard; - } - - float ellipse; - ellipse = length((gl_FragCoord.xy - spotEllipse.xy) / spotEllipse.zw); - ellipse = pow(ellipse, 2.0); // decay rate = square of distance from center - ellipse = 1.0 - ellipse; // invert - ellipse = max(0.0, ellipse); // clamp - - // Compute spotlight and apply lighting - float enable, absExtent, d, inv_r, range; - - // start of spotlight - enable = step(spotRange.x, -fsViewVertex.z); - - if (spotRange.y == 0.0) { - range = 0.0; - } - else { - absExtent = abs(spotRange.y); - - d = spotRange.x + absExtent + fsViewVertex.z; - d = min(d, 0.0); - - // slope of decay function - inv_r = 1.0 / (1.0 + absExtent); - - // inverse-linear falloff - // Reference: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/ - // y = 1 / (d/r + 1)^2 - range = 1.0 / pow(d * inv_r - 1.0, 2.0); - range *= enable; - } - - float lobeEffect = range * ellipse; - float lobeFogEffect = enable * ellipse; - - if (lightEnabled) { - vec3 lightIntensity; - vec3 sunVector; // sun lighting vector (as reflecting away from vertex) - float sunFactor; // sun light projection along vertex normal (0.0 to 1.0) - - // Sun angle - sunVector = lighting[0]; - - // Compute diffuse factor for sunlight - if(fixedShading) { - sunFactor = fsFixedShade; - } - else { - sunFactor = dot(sunVector, fsViewNormal); - } - - // Clamp ceil, fix for upscaled models without "modelScale" defined - sunFactor = clamp(sunFactor,-1.0,1.0); - - // Optional clamping, value is allowed to be negative - if(sunClamp) { - sunFactor = max(sunFactor,0.0); - } - - // Total light intensity: sum of all components - lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient - - lightIntensity.rgb += spotColor*lobeEffect; - - // Upper clamp is optional, step 1.5+ games will drive brightness beyond 100% - if(intensityClamp) { - lightIntensity = min(lightIntensity,1.0); - } - - finalData.rgb *= lightIntensity; - - // for now assume fixed shading doesn't work with specular - if (specularEnabled) { - - float exponent, NdotL, specularFactor; - vec4 biasIndex, expIndex, multIndex; - - // Always clamp floor to zero, we don't want deep black areas - NdotL = max(0.0,sunFactor); - - expIndex = vec4(8.0, 16.0, 32.0, 64.0); - multIndex = vec4(2.0, 2.0, 3.0, 4.0); - biasIndex = vec4(0.95, 0.95, 1.05, 1.0); - exponent = expIndex[int(shininess)] / biasIndex[int(shininess)]; - - specularFactor = pow(NdotL, exponent); - specularFactor *= multIndex[int(shininess)]; - specularFactor *= biasIndex[int(shininess)]; - - specularFactor *= specularValue; - specularFactor *= lighting[1].x; - - if (colData.a < 1.0) { - /// Specular hi-light affects translucent polygons alpha channel /// - finalData.a = max(finalData.a, specularFactor); - } - - finalData.rgb += vec3(specularFactor); - } - } - - // Final clamp: we need it for proper shading in dimmed light and dark ambients - finalData.rgb = min(finalData.rgb, vec3(1.0)); - - // Spotlight on fog - vec3 lSpotFogColor = spotFogColor * fogAttenuation * fogColour.rgb * lobeFogEffect; - - // Fog & spotlight applied - finalData.rgb = mix(finalData.rgb, fogData.rgb + lSpotFogColor, fogData.a); - - gl_FragColor = finalData; -} -)glsl"; - R3DShader::R3DShader(const Util::Config::Node &config) - : m_config(config) + : m_config(config) { m_shaderProgram = 0; m_vertexShader = 0; + m_geoShader = 0; m_fragmentShader = 0; Start(); // reset attributes @@ -323,71 +34,93 @@ void R3DShader::Start() m_specularValue = 0; m_microTexScale = 0; - m_baseTexSize[0] = 0; - m_baseTexSize[1] = 0; + m_baseTexSize[0] = 0; + m_baseTexSize[1] = 0; - m_dirtyMesh = true; // dirty means all the above are dirty, ie first run - m_dirtyModel = true; + m_dirtyMesh = true; // dirty means all the above are dirty, ie first run + m_dirtyModel = true; } bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader) { - const char* vShader; - const char* fShader; - bool success; + bool quads = m_config["QuadRendering"].ValueAs(); - if (vertexShader) { - vShader = vertexShader; - } - else { - vShader = vertexShaderR3D; + const char* vShader = vertexShaderR3D; + const char* gShader = ""; + const char* fShader = fragmentShaderR3D; + + if (quads) { + vShader = vertexShaderR3DQuads; + gShader = geometryShaderR3DQuads; + fShader = fragmentShaderR3DQuads; } - if (fragmentShader) { - fShader = fragmentShader; - } - else { - fShader = fragmentShaderR3D; + m_shaderProgram = glCreateProgram(); + m_vertexShader = glCreateShader(GL_VERTEX_SHADER); + m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource(m_vertexShader, 1, (const GLchar **)&vShader, NULL); + glShaderSource(m_fragmentShader, 1, (const GLchar **)&fShader, NULL); + + glCompileShader(m_vertexShader); + glCompileShader(m_fragmentShader); + + if (quads) { + m_geoShader = glCreateShader(GL_GEOMETRY_SHADER); + glShaderSource(m_geoShader, 1, (const GLchar **)&gShader, NULL); + glCompileShader(m_geoShader); + glAttachShader(m_shaderProgram, m_geoShader); + PrintShaderResult(m_geoShader); } - success = LoadShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, m_config["VertexShader"].ValueAs(), m_config["FragmentShader"].ValueAs(), vShader, fShader); + PrintShaderResult(m_vertexShader); + PrintShaderResult(m_fragmentShader); - m_locTexture1 = glGetUniformLocation(m_shaderProgram, "tex1"); - m_locTexture2 = glGetUniformLocation(m_shaderProgram, "tex2"); - m_locTexture1Enabled= glGetUniformLocation(m_shaderProgram, "textureEnabled"); - m_locTexture2Enabled= glGetUniformLocation(m_shaderProgram, "microTexture"); - m_locTextureAlpha = glGetUniformLocation(m_shaderProgram, "textureAlpha"); - m_locAlphaTest = glGetUniformLocation(m_shaderProgram, "alphaTest"); - m_locMicroTexScale = glGetUniformLocation(m_shaderProgram, "microTextureScale"); - m_locBaseTexSize = glGetUniformLocation(m_shaderProgram, "baseTexSize"); - m_locTextureInverted= glGetUniformLocation(m_shaderProgram, "textureInverted"); + glAttachShader(m_shaderProgram, m_vertexShader); + glAttachShader(m_shaderProgram, m_fragmentShader); + glLinkProgram(m_shaderProgram); - m_locFogIntensity = glGetUniformLocation(m_shaderProgram, "fogIntensity"); - m_locFogDensity = glGetUniformLocation(m_shaderProgram, "fogDensity"); - m_locFogStart = glGetUniformLocation(m_shaderProgram, "fogStart"); - m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour"); - m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation"); - m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient"); + PrintProgramResult(m_shaderProgram); - m_locLighting = glGetUniformLocation(m_shaderProgram, "lighting"); - m_locLightEnabled = glGetUniformLocation(m_shaderProgram, "lightEnabled"); - m_locSunClamp = glGetUniformLocation(m_shaderProgram, "sunClamp"); - m_locIntensityClamp = glGetUniformLocation(m_shaderProgram, "intensityClamp"); - m_locShininess = glGetUniformLocation(m_shaderProgram, "shininess"); - m_locSpecularValue = glGetUniformLocation(m_shaderProgram, "specularValue"); - m_locSpecularEnabled= glGetUniformLocation(m_shaderProgram, "specularEnabled"); - m_locFixedShading = glGetUniformLocation(m_shaderProgram, "fixedShading"); + m_locTexture1 = glGetUniformLocation(m_shaderProgram, "tex1"); + m_locTexture2 = glGetUniformLocation(m_shaderProgram, "tex2"); + m_locTexture1Enabled = glGetUniformLocation(m_shaderProgram, "textureEnabled"); + m_locTexture2Enabled = glGetUniformLocation(m_shaderProgram, "microTexture"); + m_locTextureAlpha = glGetUniformLocation(m_shaderProgram, "textureAlpha"); + m_locAlphaTest = glGetUniformLocation(m_shaderProgram, "alphaTest"); + m_locMicroTexScale = glGetUniformLocation(m_shaderProgram, "microTextureScale"); + m_locBaseTexSize = glGetUniformLocation(m_shaderProgram, "baseTexSize"); + m_locTextureInverted = glGetUniformLocation(m_shaderProgram, "textureInverted"); - m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse"); - m_locSpotRange = glGetUniformLocation(m_shaderProgram, "spotRange"); - m_locSpotColor = glGetUniformLocation(m_shaderProgram, "spotColor"); - m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor"); - m_locModelScale = glGetUniformLocation(m_shaderProgram, "modelScale"); + m_locFogIntensity = glGetUniformLocation(m_shaderProgram, "fogIntensity"); + m_locFogDensity = glGetUniformLocation(m_shaderProgram, "fogDensity"); + m_locFogStart = glGetUniformLocation(m_shaderProgram, "fogStart"); + m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour"); + m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation"); + m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient"); - m_locHardwareStep = glGetUniformLocation(m_shaderProgram, "hardwareStep"); - m_locDiscardAlpha = glGetUniformLocation(m_shaderProgram, "discardAlpha"); - - return success; + m_locLighting = glGetUniformLocation(m_shaderProgram, "lighting"); + m_locLightEnabled = glGetUniformLocation(m_shaderProgram, "lightEnabled"); + m_locSunClamp = glGetUniformLocation(m_shaderProgram, "sunClamp"); + m_locIntensityClamp = glGetUniformLocation(m_shaderProgram, "intensityClamp"); + m_locShininess = glGetUniformLocation(m_shaderProgram, "shininess"); + m_locSpecularValue = glGetUniformLocation(m_shaderProgram, "specularValue"); + m_locSpecularEnabled = glGetUniformLocation(m_shaderProgram, "specularEnabled"); + m_locFixedShading = glGetUniformLocation(m_shaderProgram, "fixedShading"); + + m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse"); + m_locSpotRange = glGetUniformLocation(m_shaderProgram, "spotRange"); + m_locSpotColor = glGetUniformLocation(m_shaderProgram, "spotColor"); + m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor"); + m_locModelScale = glGetUniformLocation(m_shaderProgram, "modelScale"); + + m_locProjMat = glGetUniformLocation(m_shaderProgram, "projMat"); + m_locModelMat = glGetUniformLocation(m_shaderProgram, "modelMat"); + + m_locHardwareStep = glGetUniformLocation(m_shaderProgram, "hardwareStep"); + m_locDiscardAlpha = glGetUniformLocation(m_shaderProgram, "discardAlpha"); + + return true; } GLint R3DShader::GetVertexAttribPos(const char* attrib) @@ -498,23 +231,25 @@ void R3DShader::SetMeshUniforms(const Mesh* m) } void R3DShader::SetViewportUniforms(const Viewport *vp) -{ +{ //didn't bother caching these, they don't get frequently called anyway - glUniform1f (m_locFogDensity, vp->fogParams[3]); - glUniform1f (m_locFogStart, vp->fogParams[4]); + glUniform1f(m_locFogDensity, vp->fogParams[3]); + glUniform1f(m_locFogStart, vp->fogParams[4]); glUniform3fv(m_locFogColour, 1, vp->fogParams); - glUniform1f (m_locFogAttenuation, vp->fogParams[5]); - glUniform1f (m_locFogAmbient, vp->fogParams[6]); + glUniform1f(m_locFogAttenuation, vp->fogParams[5]); + glUniform1f(m_locFogAmbient, vp->fogParams[6]); glUniform3fv(m_locLighting, 2, vp->lightingParams); - glUniform1i (m_locSunClamp, vp->sunClamp); - glUniform1i (m_locIntensityClamp, vp->intensityClamp); + glUniform1i(m_locSunClamp, vp->sunClamp); + glUniform1i(m_locIntensityClamp, vp->intensityClamp); glUniform4fv(m_locSpotEllipse, 1, vp->spotEllipse); glUniform2fv(m_locSpotRange, 1, vp->spotRange); glUniform3fv(m_locSpotColor, 1, vp->spotColor); glUniform3fv(m_locSpotFogColor, 1, vp->spotFogColor); - glUniform1i (m_locHardwareStep, vp->hardwareStep); + glUniformMatrix4fv(m_locProjMat, 1, GL_FALSE, vp->projectionMatrix); + + glUniform1i(m_locHardwareStep, vp->hardwareStep); } void R3DShader::SetModelStates(const Model* model) @@ -524,6 +259,8 @@ void R3DShader::SetModelStates(const Model* model) m_modelScale = model->scale; } + glUniformMatrix4fv(m_locModelMat, 1, GL_FALSE, model->modelMat); + m_dirtyModel = false; } @@ -532,4 +269,44 @@ void R3DShader::DiscardAlpha(bool discard) glUniform1i(m_locDiscardAlpha, discard); } +void R3DShader::PrintShaderResult(GLuint shader) +{ + //=========== + GLint result; + GLint length; + //=========== + + glGetShaderiv(shader, GL_COMPILE_STATUS, &result); + + if (result == GL_FALSE) { + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); + + if (length > 0) { + std::vector msg(length); + glGetShaderInfoLog(shader, length, NULL, msg.data()); + printf("%s\n", msg.data()); + } + } +} + +void R3DShader::PrintProgramResult(GLuint program) +{ + //=========== + GLint result; + //=========== + + glGetProgramiv(program, GL_LINK_STATUS, &result); + + if (result == GL_FALSE) { + + GLint maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + //The maxLength includes the NULL character + std::vector infoLog(maxLength); + glGetProgramInfoLog(program, maxLength, &maxLength, infoLog.data()); + printf("%s\n", infoLog.data()); + } +} } // New3D diff --git a/Src/Graphics/New3D/R3DShader.h b/Src/Graphics/New3D/R3DShader.h index 54ce6ad..5eca96e 100644 --- a/Src/Graphics/New3D/R3DShader.h +++ b/Src/Graphics/New3D/R3DShader.h @@ -23,12 +23,16 @@ public: private: + void PrintShaderResult(GLuint shader); + void PrintProgramResult(GLuint program); + // run-time config const Util::Config::Node &m_config; // shader IDs GLuint m_shaderProgram; GLuint m_vertexShader; + GLuint m_geoShader; GLuint m_fragmentShader; // mesh uniform locations @@ -58,7 +62,7 @@ private: float m_microTexScale; float m_baseTexSize[2]; bool m_textureInverted; - + // cached model values float m_modelScale; @@ -73,6 +77,7 @@ private: GLint m_locFogColour; GLint m_locFogAttenuation; GLint m_locFogAmbient; + GLint m_locProjMat; // lighting / other GLint m_locLighting; @@ -88,15 +93,17 @@ private: GLint m_locSpotRange; GLint m_locSpotColor; GLint m_locSpotFogColor; - + // model uniforms GLint m_locModelScale; + GLint m_locModelMat; // global uniforms GLint m_locHardwareStep; GLint m_locDiscardAlpha; }; + } // New3D #endif \ No newline at end of file diff --git a/Src/Graphics/New3D/R3DShaderQuads.h b/Src/Graphics/New3D/R3DShaderQuads.h new file mode 100644 index 0000000..f9d9c9d --- /dev/null +++ b/Src/Graphics/New3D/R3DShaderQuads.h @@ -0,0 +1,527 @@ +#ifndef _R3DSHADERQUADS_H_ +#define _R3DSHADERQUADS_H_ + +static const char *vertexShaderR3DQuads = R"glsl( + +#version 410 core + +// uniforms +uniform float modelScale; +uniform mat4 modelMat; +uniform mat4 projMat; + +// attributes +in vec4 inVertex; +in vec3 inNormal; +in vec2 inTexCoord; +in vec3 inFaceNormal; // used to emulate r3d culling +in float inFixedShade; +in vec4 inColour; + +// outputs to geometry shader + +out VS_OUT +{ + vec3 viewVertex; + vec3 viewNormal; // per vertex normal vector + vec2 texCoord; + float fixedShade; + vec4 color; + float discardPoly; // can't have varying bool (glsl spec) +} vs_out; + +float CalcBackFace(in vec3 viewVertex) +{ + vec3 vt = viewVertex - vec3(0.0); + vec3 vn = (mat3(modelMat) * inFaceNormal); + + // dot product of face normal with view direction + return dot(vt, vn); +} + +void main(void) +{ + vs_out.viewVertex = vec3(modelMat * inVertex); + vs_out.viewNormal = (mat3(modelMat) * inNormal) / modelScale; + vs_out.discardPoly = CalcBackFace(vs_out.viewVertex); + vs_out.color = inColour; + vs_out.texCoord = inTexCoord; + vs_out.fixedShade = inFixedShade; + gl_Position = projMat * modelMat * inVertex; +} +)glsl"; + +static const char *geometryShaderR3DQuads = R"glsl( + +#version 410 core + +layout (lines_adjacency) in; +layout (triangle_strip, max_vertices = 4) out; + +in VS_OUT +{ + vec3 viewVertex; + vec3 viewNormal; // per vertex normal vector + vec2 texCoord; + float fixedShade; + vec4 color; + float discardPoly; // can't have varying bool (glsl spec) +} gs_in[4]; + +out GS_OUT +{ + noperspective vec2 v[4]; + noperspective float area[4]; + flat float oneOverW[4]; + + //our regular attributes + flat vec3 viewVertex[4]; + flat vec3 viewNormal[4]; // per vertex normal vector + flat vec2 texCoord[4]; + flat float fixedShade[4]; + flat vec4 color; +} gs_out; + +float area(vec2 a, vec2 b) +{ + return a.x*b.y - a.y*b.x; +} + +void main(void) +{ + if(gs_in[0].discardPoly>=0) { + return; //emulate back face culling here (all vertices in poly have same value) + } + + int i, j, j_next; + vec2 v[4]; + + for (i=0; i<4; i++) { + float oneOverW = 1.0 / gl_in[i].gl_Position.w; + gs_out.oneOverW[i] = oneOverW; + v[i] = gl_in[i].gl_Position.xy * oneOverW; + + // our regular vertex attribs + gs_out.viewVertex[i] = gs_in[i].viewVertex * oneOverW; + gs_out.viewNormal[i] = gs_in[i].viewNormal * oneOverW; + gs_out.texCoord[i] = gs_in[i].texCoord * oneOverW; + gs_out.fixedShade[i] = gs_in[i].fixedShade * oneOverW; + } + + // flat attributes + gs_out.color = gs_in[0].color; + + for (i=0; i<4; i++) { + // Mapping of polygon vertex order to triangle strip vertex order. + // + // Quad (lines adjacency) Triangle strip + // vertex order: vertex order: + // + // 1----2 1----3 + // | | ===> | \ | + // | | | \ | + // 0----3 0----2 + // + int reorder[4] = int[]( 1, 0, 2, 3 ); + int ii = reorder[i]; + + for (j=0; j<4; j++) { + gs_out.v[j] = v[j] - v[ii]; + } + for (j=0; j<4; j++) { + j_next = (j+1) % 4; + gs_out.area[j] = area(gs_out.v[j], gs_out.v[j_next]); + } + + gl_Position = gl_in[ii].gl_Position; + + EmitVertex(); + } +} + +)glsl"; + +static const char *fragmentShaderR3DQuads = R"glsl( + +#version 410 core + +uniform sampler2D tex1; // base tex +uniform sampler2D tex2; // micro tex (optional) + +// texturing +uniform bool textureEnabled; +uniform bool microTexture; +uniform float microTextureScale; +uniform vec2 baseTexSize; +uniform bool textureInverted; +uniform bool textureAlpha; +uniform bool alphaTest; +uniform bool discardAlpha; + +// general +uniform vec3 fogColour; +uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height) +uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit +uniform vec3 spotColor; // spotlight RGB color +uniform vec3 spotFogColor; // spotlight RGB color on fog +uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0) +uniform bool lightEnabled; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity +uniform bool sunClamp; // not used by daytona and la machine guns +uniform bool intensityClamp; // some games such as daytona and +uniform bool specularEnabled; // specular enabled +uniform float specularValue; // specular coefficient +uniform float shininess; // specular shininess +uniform float fogIntensity; +uniform float fogDensity; +uniform float fogStart; +uniform float fogAttenuation; +uniform float fogAmbient; +uniform bool fixedShading; +uniform int hardwareStep; + +// test +uniform mat4 projMat; + +//interpolated inputs from geometry shader + +in GS_OUT +{ + noperspective vec2 v[4]; + noperspective float area[4]; + flat float oneOverW[4]; + + //our regular attributes + flat vec3 viewVertex[4]; + flat vec3 viewNormal[4]; // per vertex normal vector + flat vec2 texCoord[4]; + flat float fixedShade[4]; + flat vec4 color; +} fs_in; + +//our calculated vertex attributes from the above +vec3 fsViewVertex; +vec3 fsViewNormal; +vec2 fsTexCoord; +float fsFixedShade; +vec4 fsColor; + +//outputs +out vec4 outColor; + +void QuadraticInterpolation() +{ + uint i, i_next, i_prev; + + vec2 s[4]; + float A[4]; + + for (i=0; i<4; i++) { + s[i] = fs_in.v[i]; + A[i] = fs_in.area[i]; + } + + float D[4]; + float r[4]; + + for (i=0; i<4; i++) { + i_next = (i+1)%4; + D[i] = dot(s[i], s[i_next]); + r[i] = length(s[i]); + if (fs_in.oneOverW[i] < 0) { // is w[i] negative? + r[i] = -r[i]; + } + } + + float t[4]; + + for (i=0; i<4; i++) { + i_next = (i+1)%4; + if(A[i]==0.0) t[i] = 0; // check for zero area + div by zero + else t[i] = (r[i]*r[i_next] - D[i]) / A[i]; + } + + float uSum = 0; + float u[4]; + + for (i=0; i<4; i++) { + i_prev = (i-1)%4; + u[i] = (t[i_prev] + t[i]) / r[i]; + uSum += u[i]; + } + + float lambda[4]; + + for (i=0; i<4; i++) { + lambda[i] = u[i] / uSum; + } + + /* Discard fragments when all the weights are neither all negative nor all positive. */ + + int lambdaSignCount = 0; + + for (i=0; i<4; i++) { + if (fs_in.oneOverW[i] < 0) { + if (lambda[i] > 0) { + lambdaSignCount--; + } else { + lambdaSignCount++; + } + } + else { + if (lambda[i] < 0) { + lambdaSignCount--; + } else { + lambdaSignCount++; + } + } + } + if (abs(lambdaSignCount) != 4) { + discard; // need to revisit this + } + + float interp_oneOverW = 0; + + fsViewVertex = vec3(0.0); + fsViewNormal = vec3(0.0); + fsTexCoord = vec2(0.0); + fsFixedShade = 0.0; + fsColor = fs_in.color; + + for (i=0; i<4; i++) { + fsViewVertex += lambda[i] * fs_in.viewVertex[i]; + fsViewNormal += lambda[i] * fs_in.viewNormal[i]; + fsTexCoord += lambda[i] * fs_in.texCoord[i]; + fsFixedShade += lambda[i] * fs_in.fixedShade[i]; + interp_oneOverW += lambda[i] * fs_in.oneOverW[i]; + } + + fsViewVertex /= interp_oneOverW; + fsViewNormal /= interp_oneOverW; + fsTexCoord /= interp_oneOverW; + fsFixedShade /= interp_oneOverW; + + vec4 vertex; + float depth; + + // dirty hack for co-planar polys that really need 100% identical values to depth test correctly + // the reason we waste cycles and calcute depth value here is because we have run out of vertex attribs + if(fs_in.oneOverW[0]==fs_in.oneOverW[1] && + fs_in.oneOverW[1]==fs_in.oneOverW[2] && + fs_in.oneOverW[2]==fs_in.oneOverW[3]) { + + fsViewVertex.z = fs_in.viewVertex[0].z / fs_in.oneOverW[0]; + vertex = projMat * vec4(fsViewVertex,1.0); + depth = ((vertex.z / vertex.w) + 1.0) / 2.0; + } + else { + vertex = projMat * vec4(fsViewVertex,1.0); + depth = ((vertex.z * interp_oneOverW) + 1.0) / 2.0; + } + + gl_FragDepth = depth; +} + +vec4 GetTextureValue() +{ + vec4 tex1Data = texture2D( tex1, fsTexCoord.st); + + if(textureInverted) { + tex1Data.rgb = vec3(1.0) - vec3(tex1Data.rgb); + } + + if (microTexture) { + vec2 scale = (baseTexSize / 128.0) * microTextureScale; + vec4 tex2Data = texture2D( tex2, fsTexCoord.st * scale); + tex1Data = (tex1Data+tex2Data)/2.0; + } + + if (alphaTest) { + if (tex1Data.a < (8.0/16.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; + } + } + } + + if (textureAlpha == false) { + tex1Data.a = 1.0; + } + + return tex1Data; +} + +void Step15Luminous(inout vec4 colour) +{ + // luminous polys seem to behave very differently on step 1.5 hardware + // when fixed shading is enabled the colour is modulated by the vp ambient + fixed shade value + // when disabled it appears to be multiplied by 1.5, presumably to allow a higher range + if(hardwareStep==0x15) { + if(!lightEnabled && textureEnabled) { + if(fixedShading) { + colour.rgb *= 1.0 + fsFixedShade + lighting[1].y; + } + else { + colour.rgb *= vec3(1.5); + } + } + } +} + +float CalcFog() +{ + float z = -fsViewVertex.z; + float fog = fogIntensity * clamp(fogStart + z * fogDensity, 0.0, 1.0); + + return fog; +} + +void main() +{ + vec4 tex1Data; + vec4 colData; + vec4 finalData; + vec4 fogData; + + QuadraticInterpolation(); // calculate our vertex attributes + + fogData = vec4(fogColour.rgb * fogAmbient, CalcFog()); + tex1Data = vec4(1.0, 1.0, 1.0, 1.0); + + if(textureEnabled) { + tex1Data = GetTextureValue(); + } + + colData = fsColor; + Step15Luminous(colData); // no-op for step 2.0+ + finalData = tex1Data * colData; + + if (finalData.a < (1.0/16.0)) { // basically chuck out any totally transparent pixels value = 1/16 the smallest transparency level h/w supports + discard; + } + + float ellipse; + ellipse = length((gl_FragCoord.xy - spotEllipse.xy) / spotEllipse.zw); + ellipse = pow(ellipse, 2.0); // decay rate = square of distance from center + ellipse = 1.0 - ellipse; // invert + ellipse = max(0.0, ellipse); // clamp + + // Compute spotlight and apply lighting + float enable, absExtent, d, inv_r, range; + + // start of spotlight + enable = step(spotRange.x, -fsViewVertex.z); + + if (spotRange.y == 0.0) { + range = 0.0; + } + else { + absExtent = abs(spotRange.y); + + d = spotRange.x + absExtent + fsViewVertex.z; + d = min(d, 0.0); + + // slope of decay function + inv_r = 1.0 / (1.0 + absExtent); + + // inverse-linear falloff + // Reference: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/ + // y = 1 / (d/r + 1)^2 + range = 1.0 / pow(d * inv_r - 1.0, 2.0); + range *= enable; + } + + float lobeEffect = range * ellipse; + float lobeFogEffect = enable * ellipse; + + if (lightEnabled) { + vec3 lightIntensity; + vec3 sunVector; // sun lighting vector (as reflecting away from vertex) + float sunFactor; // sun light projection along vertex normal (0.0 to 1.0) + + // Sun angle + sunVector = lighting[0]; + + // Compute diffuse factor for sunlight + if(fixedShading) { + sunFactor = fsFixedShade; + } + else { + sunFactor = dot(sunVector, fsViewNormal); + } + + // Clamp ceil, fix for upscaled models without "modelScale" defined + sunFactor = clamp(sunFactor,-1.0,1.0); + + // Optional clamping, value is allowed to be negative + if(sunClamp) { + sunFactor = max(sunFactor,0.0); + } + + // Total light intensity: sum of all components + lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient + + lightIntensity.rgb += spotColor*lobeEffect; + + // Upper clamp is optional, step 1.5+ games will drive brightness beyond 100% + if(intensityClamp) { + lightIntensity = min(lightIntensity,1.0); + } + + finalData.rgb *= lightIntensity; + + // for now assume fixed shading doesn't work with specular + if (specularEnabled) { + + float exponent, NdotL, specularFactor; + vec4 biasIndex, expIndex, multIndex; + + // Always clamp floor to zero, we don't want deep black areas + NdotL = max(0.0,sunFactor); + + expIndex = vec4(8.0, 16.0, 32.0, 64.0); + multIndex = vec4(2.0, 2.0, 3.0, 4.0); + biasIndex = vec4(0.95, 0.95, 1.05, 1.0); + exponent = expIndex[int(shininess)] / biasIndex[int(shininess)]; + + specularFactor = pow(NdotL, exponent); + specularFactor *= multIndex[int(shininess)]; + specularFactor *= biasIndex[int(shininess)]; + + specularFactor *= specularValue; + specularFactor *= lighting[1].x; + + if (colData.a < 1.0) { + /// Specular hi-light affects translucent polygons alpha channel /// + finalData.a = max(finalData.a, specularFactor); + } + + finalData.rgb += vec3(specularFactor); + } + } + + // Final clamp: we need it for proper shading in dimmed light and dark ambients + finalData.rgb = min(finalData.rgb, vec3(1.0)); + + // Spotlight on fog + vec3 lSpotFogColor = spotFogColor * fogAttenuation * fogColour.rgb * lobeFogEffect; + + // Fog & spotlight applied + finalData.rgb = mix(finalData.rgb, fogData.rgb + lSpotFogColor, fogData.a); + + // Write output + outColor = finalData; +} +)glsl"; + +#endif \ No newline at end of file diff --git a/Src/Graphics/New3D/R3DShaderTriangles.h b/Src/Graphics/New3D/R3DShaderTriangles.h new file mode 100644 index 0000000..0979d0a --- /dev/null +++ b/Src/Graphics/New3D/R3DShaderTriangles.h @@ -0,0 +1,300 @@ +#ifndef _R3DSHADERTRIANGLES_H_ +#define _R3DSHADERTRIANGLES_H_ + +static const char *vertexShaderR3D = R"glsl( + +#version 120 + +// uniforms +uniform float modelScale; +uniform mat4 modelMat; +uniform mat4 projMat; + +// attributes +attribute vec4 inVertex; +attribute vec3 inNormal; +attribute vec2 inTexCoord; +attribute vec4 inColour; +attribute vec3 inFaceNormal; // used to emulate r3d culling +attribute float inFixedShade; + +// outputs to fragment shader +varying vec3 fsViewVertex; +varying vec3 fsViewNormal; // per vertex normal vector +varying vec2 fsTexCoord; +varying vec4 fsColor; +varying float fsDiscard; // can't have varying bool (glsl spec) +varying float fsFixedShade; + +float CalcBackFace(in vec3 viewVertex) +{ + vec3 vt = viewVertex - vec3(0.0); + vec3 vn = (mat3(modelMat) * inFaceNormal); + + // dot product of face normal with view direction + return dot(vt, vn); +} + +void main(void) +{ + fsViewVertex = vec3(modelMat * inVertex); + fsViewNormal = (mat3(modelMat) * inNormal) / modelScale; + fsDiscard = CalcBackFace(fsViewVertex); + fsColor = inColour; + fsTexCoord = inTexCoord; + fsFixedShade = inFixedShade; + gl_Position = projMat * modelMat * inVertex; +} +)glsl"; + +static const char *fragmentShaderR3D = R"glsl( + +#version 120 + +uniform sampler2D tex1; // base tex +uniform sampler2D tex2; // micro tex (optional) + +// texturing +uniform bool textureEnabled; +uniform bool microTexture; +uniform float microTextureScale; +uniform vec2 baseTexSize; +uniform bool textureInverted; +uniform bool textureAlpha; +uniform bool alphaTest; +uniform bool discardAlpha; + +// general +uniform vec3 fogColour; +uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height) +uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit +uniform vec3 spotColor; // spotlight RGB color +uniform vec3 spotFogColor; // spotlight RGB color on fog +uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0) +uniform bool lightEnabled; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity +uniform bool sunClamp; // not used by daytona and la machine guns +uniform bool intensityClamp; // some games such as daytona and +uniform bool specularEnabled; // specular enabled +uniform float specularValue; // specular coefficient +uniform float shininess; // specular shininess +uniform float fogIntensity; +uniform float fogDensity; +uniform float fogStart; +uniform float fogAttenuation; +uniform float fogAmbient; +uniform bool fixedShading; +uniform int hardwareStep; + +//interpolated inputs from vertex shader +varying vec3 fsViewVertex; +varying vec3 fsViewNormal; // per vertex normal vector +varying vec4 fsColor; +varying vec2 fsTexCoord; +varying float fsDiscard; +varying float fsFixedShade; + +vec4 GetTextureValue() +{ + vec4 tex1Data = texture2D( tex1, fsTexCoord.st); + + if(textureInverted) { + tex1Data.rgb = vec3(1.0) - vec3(tex1Data.rgb); + } + + if (microTexture) { + vec2 scale = (baseTexSize / 128.0) * microTextureScale; + vec4 tex2Data = texture2D( tex2, fsTexCoord.st * scale); + tex1Data = (tex1Data+tex2Data)/2.0; + } + + if (alphaTest) { + if (tex1Data.a < (8.0/16.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; + } + } + } + + if (textureAlpha == false) { + tex1Data.a = 1.0; + } + + return tex1Data; +} + +void Step15Luminous(inout vec4 colour) +{ + // luminous polys seem to behave very differently on step 1.5 hardware + // when fixed shading is enabled the colour is modulated by the vp ambient + fixed shade value + // when disabled it appears to be multiplied by 1.5, presumably to allow a higher range + if(hardwareStep==0x15) { + if(!lightEnabled && textureEnabled) { + if(fixedShading) { + colour.rgb *= 1.0 + fsFixedShade + lighting[1].y; + } + else { + colour.rgb *= vec3(1.5); + } + } + } +} + +float CalcFog() +{ + float z = -fsViewVertex.z; + float fog = fogIntensity * clamp(fogStart + z * fogDensity, 0.0, 1.0); + + return fog; +} + +void main() +{ + vec4 tex1Data; + vec4 colData; + vec4 finalData; + vec4 fogData; + + if(fsDiscard>=0) { + discard; //emulate back face culling here + } + + fogData = vec4(fogColour.rgb * fogAmbient, CalcFog()); + tex1Data = vec4(1.0, 1.0, 1.0, 1.0); + + if(textureEnabled) { + tex1Data = GetTextureValue(); + } + + colData = fsColor; + Step15Luminous(colData); // no-op for step 2.0+ + finalData = tex1Data * colData; + + if (finalData.a < (1.0/16.0)) { // basically chuck out any totally transparent pixels value = 1/16 the smallest transparency level h/w supports + discard; + } + + float ellipse; + ellipse = length((gl_FragCoord.xy - spotEllipse.xy) / spotEllipse.zw); + ellipse = pow(ellipse, 2.0); // decay rate = square of distance from center + ellipse = 1.0 - ellipse; // invert + ellipse = max(0.0, ellipse); // clamp + + // Compute spotlight and apply lighting + float enable, absExtent, d, inv_r, range; + + // start of spotlight + enable = step(spotRange.x, -fsViewVertex.z); + + if (spotRange.y == 0.0) { + range = 0.0; + } + else { + absExtent = abs(spotRange.y); + + d = spotRange.x + absExtent + fsViewVertex.z; + d = min(d, 0.0); + + // slope of decay function + inv_r = 1.0 / (1.0 + absExtent); + + // inverse-linear falloff + // Reference: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/ + // y = 1 / (d/r + 1)^2 + range = 1.0 / pow(d * inv_r - 1.0, 2.0); + range *= enable; + } + + float lobeEffect = range * ellipse; + float lobeFogEffect = enable * ellipse; + + if (lightEnabled) { + vec3 lightIntensity; + vec3 sunVector; // sun lighting vector (as reflecting away from vertex) + float sunFactor; // sun light projection along vertex normal (0.0 to 1.0) + + // Sun angle + sunVector = lighting[0]; + + // Compute diffuse factor for sunlight + if(fixedShading) { + sunFactor = fsFixedShade; + } + else { + sunFactor = dot(sunVector, fsViewNormal); + } + + // Clamp ceil, fix for upscaled models without "modelScale" defined + sunFactor = clamp(sunFactor,-1.0,1.0); + + // Optional clamping, value is allowed to be negative + if(sunClamp) { + sunFactor = max(sunFactor,0.0); + } + + // Total light intensity: sum of all components + lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient + + lightIntensity.rgb += spotColor*lobeEffect; + + // Upper clamp is optional, step 1.5+ games will drive brightness beyond 100% + if(intensityClamp) { + lightIntensity = min(lightIntensity,1.0); + } + + finalData.rgb *= lightIntensity; + + // for now assume fixed shading doesn't work with specular + if (specularEnabled) { + + float exponent, NdotL, specularFactor; + vec4 biasIndex, expIndex, multIndex; + + // Always clamp floor to zero, we don't want deep black areas + NdotL = max(0.0,sunFactor); + + expIndex = vec4(8.0, 16.0, 32.0, 64.0); + multIndex = vec4(2.0, 2.0, 3.0, 4.0); + biasIndex = vec4(0.95, 0.95, 1.05, 1.0); + exponent = expIndex[int(shininess)] / biasIndex[int(shininess)]; + + specularFactor = pow(NdotL, exponent); + specularFactor *= multIndex[int(shininess)]; + specularFactor *= biasIndex[int(shininess)]; + + specularFactor *= specularValue; + specularFactor *= lighting[1].x; + + if (colData.a < 1.0) { + /// Specular hi-light affects translucent polygons alpha channel /// + finalData.a = max(finalData.a, specularFactor); + } + + finalData.rgb += vec3(specularFactor); + } + } + + // Final clamp: we need it for proper shading in dimmed light and dark ambients + finalData.rgb = min(finalData.rgb, vec3(1.0)); + + // Spotlight on fog + vec3 lSpotFogColor = spotFogColor * fogAttenuation * fogColour.rgb * lobeFogEffect; + + // Fog & spotlight applied + finalData.rgb = mix(finalData.rgb, fogData.rgb + lSpotFogColor, fogData.a); + + gl_FragColor = finalData; +} +)glsl"; + +#endif \ No newline at end of file diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 41408c9..92dfbbf 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -304,6 +304,7 @@ static void PrintGLInfo(bool createScreen, bool infoLog, bool printExtensions) else printf(" %s\n", strLocal); } } + free(strLocal); } if (infoLog) InfoLog(""); else printf("\n"); @@ -1333,6 +1334,7 @@ static Util::Config::Node DefaultConfig() #endif // Platform-specific/UI config.Set("New3DEngine", true); + config.Set("QuadRendering", false); config.Set("XResolution", "496"); config.Set("YResolution", "384"); config.Set("FullScreen", false); @@ -1507,6 +1509,7 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) { "-show-fps", { "ShowFrameRate", true } }, { "-no-fps", { "ShowFrameRate", false } }, { "-new3d", { "New3DEngine", true } }, + { "-quad-rendering", { "QuadRendering", true } }, { "-legacy3d", { "New3DEngine", false } }, { "-no-flip-stereo", { "FlipStereo", false } }, { "-flip-stereo", { "FlipStereo", true } }, diff --git a/VS2008/Supermodel.sln b/VS2008/Supermodel.sln index 800ef25..00d5fe8 100755 --- a/VS2008/Supermodel.sln +++ b/VS2008/Supermodel.sln @@ -13,6 +13,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZLib", "ZLib\ZLib.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Musashi68K", "Musashi68K\Musashi68K.vcxproj", "{1248CF7C-B122-461C-9624-196AEFAE5046}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E195ADFF-E02F-4AE3-88E8-D90A4EC278A0}" + ProjectSection(SolutionItems) = preProject + Performance1.psess = Performance1.psess + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -65,4 +70,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/VS2008/Supermodel.vcxproj b/VS2008/Supermodel.vcxproj index 08cab1b..e414302 100644 --- a/VS2008/Supermodel.vcxproj +++ b/VS2008/Supermodel.vcxproj @@ -550,6 +550,8 @@ xcopy /D /Y "$(ProjectDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDi + + diff --git a/VS2008/Supermodel.vcxproj.filters b/VS2008/Supermodel.vcxproj.filters index e010abd..36954d1 100644 --- a/VS2008/Supermodel.vcxproj.filters +++ b/VS2008/Supermodel.vcxproj.filters @@ -874,6 +874,12 @@ Source Files\Graphics\New + + Source Files\Graphics\New + + + Source Files\Graphics\New +