From c12e4c321585d3bd69134f6a40ed5bc508ce750d Mon Sep 17 00:00:00 2001 From: Ian Curtis Date: Mon, 14 Aug 2017 09:14:06 +0000 Subject: [PATCH] Fixed shading (per vertex poly colours) on step 1.5 hardware have the viewport ambient value added to them. This fixes various shading on scud. To do this had to switch the maths to the vertex shader. --- Src/Graphics/New3D/Model.h | 8 ++- Src/Graphics/New3D/New3D.cpp | 99 ++++++++++++++----------------- Src/Graphics/New3D/New3D.h | 1 - Src/Graphics/New3D/PolyHeader.cpp | 2 +- Src/Graphics/New3D/R3DShader.cpp | 44 +++++++++++++- Src/Graphics/New3D/R3DShader.h | 5 ++ 6 files changed, 99 insertions(+), 60 deletions(-) diff --git a/Src/Graphics/New3D/Model.h b/Src/Graphics/New3D/Model.h index b9f7d85..e01caad 100644 --- a/Src/Graphics/New3D/Model.h +++ b/Src/Graphics/New3D/Model.h @@ -26,7 +26,8 @@ struct Vertex float pos[3]; float normal[3]; float texcoords[2]; - float color[4]; //rgba + UINT8 color[4]; + UINT8 fixedShade[4]; }; struct Poly // our polys are always 3 triangles, unlike the real h/w @@ -40,7 +41,7 @@ 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 - float faceColour[4]; // per face colour + UINT8 faceColour[4]; // per face colour int number = 4; }; @@ -85,6 +86,7 @@ struct Mesh bool highPriority = false; // rendered over the top // lighting + bool fixedShading = false; bool lighting = false; bool specular = false; float shininess = 0; @@ -151,6 +153,8 @@ struct Viewport int number; // viewport number float spotFogColor[3]; // spotlight color on fog float scrollAtt; + + int hardwareStep; // not really a viewport param but will do here }; enum class Clip { INSIDE, OUTSIDE, INTERCEPT, NOT_SET }; diff --git a/Src/Graphics/New3D/New3D.cpp b/Src/Graphics/New3D/New3D.cpp index dc8c87a..b487b56 100644 --- a/Src/Graphics/New3D/New3D.cpp +++ b/Src/Graphics/New3D/New3D.cpp @@ -282,12 +282,14 @@ void CNew3D::RenderFrame(void) glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); + glEnableVertexAttribArray(4); // before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil .. - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inVertex"), 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0); - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal)); - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inTexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoords)); - glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inColour"), 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, color)); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inVertex"), 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal)); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inTexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoords)); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inColour"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color)); + glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFixedShade"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, fixedShade)); m_r3dShader.SetShader(true); @@ -321,6 +323,7 @@ void CNew3D::RenderFrame(void) glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); glDisableVertexAttribArray(3); + glDisableVertexAttribArray(4); } void CNew3D::BeginFrame(void) @@ -779,10 +782,9 @@ void CNew3D::RenderViewport(UINT32 addr) vp->lightingParams[4] = (float)((vpnode[0x24] >> 8) & 0xFF) * (1.0f / 255.0f); // ambient intensity vp->lightingParams[5] = 0.0; // reserved - vp->sunClamp = 1; // TODO work out how this is passed, doesn't appear to be in the viewport .. or in the model data - vp->intensityClamp = (m_step == 0x10); // just step 1.0 ? - - m_vpAmbient = vp->lightingParams[4]; // cache this + vp->sunClamp = 1; // TODO work out how this is passed, doesn't appear to be in the viewport .. or in the model data + vp->intensityClamp = (m_step == 0x10); // just step 1.0 ? + vp->hardwareStep = m_step; // Spotlight int spotColorIdx = (vpnode[0x20] >> 11) & 7; // spotlight color index @@ -904,11 +906,11 @@ void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector& polyArray p.p3 = r3dPoly.v[0]; } - //multiply face attributes with vertex attributes if required + // Copy face colour to vertices for (int i = 0; i < 4; i++) { - p.p1.color[i] = p.p1.color[i] * r3dPoly.faceColour[i]; - p.p2.color[i] = p.p2.color[i] * r3dPoly.faceColour[i]; - p.p3.color[i] = p.p3.color[i] * r3dPoly.faceColour[i]; + p.p1.color[i] = r3dPoly.faceColour[i]; + p.p2.color[i] = r3dPoly.faceColour[i]; + p.p3.color[i] = r3dPoly.faceColour[i]; } polyArray.emplace_back(p); @@ -931,11 +933,11 @@ void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector& polyArray p.p3 = r3dPoly.v[2]; } - //multiply face attributes with vertex attributes if required + // Copy face colour to vertices for (int i = 0; i < 4; i++) { - p.p1.color[i] = p.p1.color[i] * r3dPoly.faceColour[i]; - p.p2.color[i] = p.p2.color[i] * r3dPoly.faceColour[i]; - p.p3.color[i] = p.p3.color[i] * r3dPoly.faceColour[i]; + p.p1.color[i] = r3dPoly.faceColour[i]; + p.p2.color[i] = r3dPoly.faceColour[i]; + p.p3.color[i] = r3dPoly.faceColour[i]; } polyArray.emplace_back(p); @@ -997,7 +999,8 @@ void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph) currentMesh->alphaTest = ph.AlphaTest(); currentMesh->textureAlpha = ph.TextureAlpha(); currentMesh->polyAlpha = ph.PolyAlpha(); - currentMesh->lighting = ph.LightEnabled() && !ph.FixedShading(); + currentMesh->lighting = ph.LightEnabled(); + currentMesh->fixedShading = ph.FixedShading() && ph.TexEnabled() && !ph.SmoothShading(); currentMesh->highPriority = ph.HighPriority(); if (ph.Layered() || (!ph.TexEnabled() && ph.PolyAlpha())) { @@ -1119,29 +1122,29 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) // copy face attributes - if ((ph.header[1] & 2) == 0) { + if (!ph.PolyColor()) { int colorIdx = ph.ColorIndex(); - p.faceColour[2] = (m_polyRAM[m_colorTableAddr + colorIdx] & 0xFF) / 255.f; - p.faceColour[1] = ((m_polyRAM[m_colorTableAddr + colorIdx] >> 8) & 0xFF) / 255.f; - p.faceColour[0] = ((m_polyRAM[m_colorTableAddr + colorIdx] >> 16) & 0xFF) / 255.f; + p.faceColour[2] = (m_polyRAM[m_colorTableAddr + colorIdx] & 0xFF); + p.faceColour[1] = ((m_polyRAM[m_colorTableAddr + colorIdx] >> 8) & 0xFF); + p.faceColour[0] = ((m_polyRAM[m_colorTableAddr + colorIdx] >> 16) & 0xFF); } else { - p.faceColour[0] = ((ph.header[4] >> 24)) / 255.f; - p.faceColour[1] = ((ph.header[4] >> 16) & 0xFF) / 255.f; - p.faceColour[2] = ((ph.header[4] >> 8) & 0xFF) / 255.f; + p.faceColour[0] = ((ph.header[4] >> 24)); + p.faceColour[1] = ((ph.header[4] >> 16) & 0xFF); + p.faceColour[2] = ((ph.header[4] >> 8) & 0xFF); if (ph.TranslatorMap()) { - p.faceColour[0] *= 15.9375f; // not using 16, as 16x16=256 not 255 and that would overflow (potentially) - p.faceColour[1] *= 15.9375f; - p.faceColour[2] *= 15.9375f; + p.faceColour[0] = (p.faceColour[0] * 255) / 16; // When the translator map is enabled, max colour seems + p.faceColour[1] = (p.faceColour[1] * 255) / 16; // to be 16. Scaling these up gives the correct colours. + p.faceColour[2] = (p.faceColour[2] * 255) / 16; // Not sure why didn't allow 32 colours with 4 bits? } } - p.faceColour[3] = ph.Transparency() / 255.f; + p.faceColour[3] = ph.Transparency(); if (ph.Discard1() && !ph.Discard2()) { - p.faceColour[3] *= 0.5f; + p.faceColour[3] /= 2; } // if we have flat shading, we can't re-use normals from shared vertices @@ -1176,42 +1179,32 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data) if (ph.FixedShading() && ph.TexEnabled() && !ph.SmoothShading()) { // fixed shading seems to be disabled if actual normals are set - //=========== - float shade; - float offset; // if lighting is disabled colour seems to be an offset - //=========== - - offset = !ph.LightEnabled() ? 1.f : 0.f; + //========== + UINT8 shade; + //========== if (m_step <= 0x15) { - - shade = ((ix & 0x7F) / 127.f); // this matches the sdk (values are from 0-127 only) and the intensity is clamped to to 0-1 - - if (m_vpAmbient > 0) { - shade = (shade + 1) / 2; // Viewport ambient seems to effect fixed shading. Technically vp ambient can change dynamically, but not an issue in practise. If it was we would need this logic in shader - } + shade = ((ix & 0x7F) * 255) / 127; // this matches the sdk (values are from 0-127 only) and the intensity is clamped to to 0-1 } else { if (ph.SpecularEnabled()) { - shade = (ix & 0xFF) / 255.f; // Star wars is the only game to use unsigned fixed shaded values. It's also the only game to set the specular flag on these polys + shade = ix & 0xFF; // Star wars is the only game to use unsigned fixed shaded values. It's also the only game to set the specular flag on these polys } else { - shade = (((ix + 128) & 0xFF) / 255.f); // Step 2+ uses signed or unsigned values for lighting 0-255. Todo finish this logic + shade = (ix + 128) & 0xFF; // Step 2+ uses signed or unsigned values for lighting 0-255. Todo finish this logic } } - shade += offset; - - p.v[j].color[0] = shade; // hardware doesn't really have per vertex colours, only per poly - p.v[j].color[1] = shade; - p.v[j].color[2] = shade; - p.v[j].color[3] = 1; + p.v[j].fixedShade[0] = shade; // hardware doesn't really have per vertex colours, only per poly + p.v[j].fixedShade[1] = shade; + p.v[j].fixedShade[2] = shade; + p.v[j].fixedShade[3] = 255; } else { - p.v[j].color[0] = 1; - p.v[j].color[1] = 1; - p.v[j].color[2] = 1; - p.v[j].color[3] = 1; + p.v[j].fixedShade[0] = 255; + p.v[j].fixedShade[1] = 255; + p.v[j].fixedShade[2] = 255; + p.v[j].fixedShade[3] = 255; } float texU, texV = 0; diff --git a/Src/Graphics/New3D/New3D.h b/Src/Graphics/New3D/New3D.h index 67e35d4..81983d0 100644 --- a/Src/Graphics/New3D/New3D.h +++ b/Src/Graphics/New3D/New3D.h @@ -216,7 +216,6 @@ private: TextureSheet m_texSheet; NodeAttributes m_nodeAttribs; Mat4 m_modelMat; // current modelview matrix - float m_vpAmbient; // cached value std::vector m_nodes; // this represents the entire render frame std::vector m_polyBufferRam; // dynamic polys diff --git a/Src/Graphics/New3D/PolyHeader.cpp b/Src/Graphics/New3D/PolyHeader.cpp index 6249059..a2dd088 100644 --- a/Src/Graphics/New3D/PolyHeader.cpp +++ b/Src/Graphics/New3D/PolyHeader.cpp @@ -344,7 +344,7 @@ UINT8 PolyHeader::Transparency() return 255; // without this check we get overflow. In the SDK, values are explicitly clamped to 0-32. } - return (UINT8)((((header[6] >> 18) & 0x3F) * 255) / 32.f); + return (((header[6] >> 18) & 0x3F) * 255) / 32; } bool PolyHeader::PolyAlpha() diff --git a/Src/Graphics/New3D/R3DShader.cpp b/Src/Graphics/New3D/R3DShader.cpp index fce4fbe..0137104 100644 --- a/Src/Graphics/New3D/R3DShader.cpp +++ b/Src/Graphics/New3D/R3DShader.cpp @@ -12,12 +12,17 @@ uniform float fogIntensity; uniform float fogDensity; uniform float fogStart; uniform float modelScale; +uniform int hardwareStep; +uniform vec3 lighting[2]; // also used in fragment shader +uniform bool lightEnabled; // also used in fragment shader +uniform bool fixedShading; // also used in fragment shader // attributes attribute vec3 inVertex; attribute vec3 inNormal; attribute vec2 inTexCoord; -attribute vec4 inColour; +attribute vec4 inColour; +attribute vec4 inFixedShade; // outputs to fragment shader varying float fsFogFactor; @@ -26,6 +31,27 @@ varying vec3 fsViewNormal; // per vertex normal vector varying vec2 fsTexCoord; varying vec4 fsColor; +vec4 GetVertexColour() +{ + vec4 polyColour = inColour; + + if(fixedShading) { + if(hardwareStep==0x15) { + if(!lightEnabled) { + polyColour += inFixedShade; // + vp ambient?? + } + else { + polyColour *= (inFixedShade + lighting[1].y); // fixed shade value + viewport ambient + } + } + else { + polyColour *= inFixedShade; //todo work out what ambient does. Probably a min clamp or 1-min clamp for signed values + } + } + + return polyColour; +} + void main(void) { fsViewVertex = vec3(gl_ModelViewMatrix * vec4(inVertex,1.0)); @@ -33,7 +59,7 @@ void main(void) float z = length(fsViewVertex); fsFogFactor = fogIntensity * clamp(fogStart + z * fogDensity, 0.0, 1.0); - fsColor = inColour; + fsColor = GetVertexColour(); fsTexCoord = inTexCoord; gl_Position = gl_ModelViewProjectionMatrix * vec4(inVertex,1.0); } @@ -70,6 +96,7 @@ uniform float specularValue; // specular coefficient uniform float shininess; // specular shininess uniform float fogAttenuation; uniform float fogAmbient; +uniform bool fixedShading; //interpolated inputs from vertex shader varying float fsFogFactor; @@ -132,7 +159,7 @@ void main() ellipse = 1.0 - ellipse; // invert ellipse = max(0.0, ellipse); // clamp - if (lightEnabled) { + if (lightEnabled && !fixedShading) { 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) @@ -242,6 +269,7 @@ void R3DShader::Start() m_specularEnabled = false; m_layered = false; m_textureInverted = false; + m_fixedShading = false; m_modelScale = 1.0f; m_shininess = 0; m_specularValue = 0; @@ -302,12 +330,15 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader) 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_locHardwareStep = glGetUniformLocation(m_shaderProgram, "hardwareStep"); return success; } @@ -400,6 +431,11 @@ void R3DShader::SetMeshUniforms(const Mesh* m) m_specularValue = m->specularValue; } + if (m_dirtyMesh || m->fixedShading != m_fixedShading) { + glUniform1i(m_locFixedShading, m->fixedShading); + m_fixedShading = m->fixedShading; + } + if (m_dirtyMesh || m->layered != m_layered) { m_layered = m->layered; if (m_layered) { @@ -445,6 +481,8 @@ void R3DShader::SetViewportUniforms(const Viewport *vp) glUniform2fv(m_locSpotRange, 1, vp->spotRange); glUniform3fv(m_locSpotColor, 1, vp->spotColor); glUniform3fv(m_locSpotFogColor, 1, vp->spotFogColor); + + glUniform1i (m_locHardwareStep, vp->hardwareStep); } void R3DShader::SetModelStates(const Model* model) diff --git a/Src/Graphics/New3D/R3DShader.h b/Src/Graphics/New3D/R3DShader.h index 0202b62..ee6006c 100644 --- a/Src/Graphics/New3D/R3DShader.h +++ b/Src/Graphics/New3D/R3DShader.h @@ -52,6 +52,7 @@ private: float m_shininess; float m_specularValue; bool m_specularEnabled; + bool m_fixedShading; bool m_layered; float m_microTexScale; @@ -83,6 +84,7 @@ private: GLint m_locShininess; GLint m_locSpecularValue; GLint m_locSpecularEnabled; + GLint m_locFixedShading; GLint m_locSpotEllipse; GLint m_locSpotRange; @@ -91,6 +93,9 @@ private: // model uniforms GLint m_locModelScale; + + // global uniforms + GLint m_locHardwareStep; }; } // New3D