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.

This commit is contained in:
Ian Curtis 2017-08-14 09:14:06 +00:00
parent 8f622714a7
commit c12e4c3215
6 changed files with 99 additions and 60 deletions

View file

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

View file

@ -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("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)
@ -781,8 +784,7 @@ void CNew3D::RenderViewport(UINT32 addr)
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->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<Poly>& 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<Poly>& 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;

View file

@ -216,7 +216,6 @@ private:
TextureSheet m_texSheet;
NodeAttributes m_nodeAttribs;
Mat4 m_modelMat; // current modelview matrix
float m_vpAmbient; // cached value
std::vector<Node> m_nodes; // this represents the entire render frame
std::vector<Poly> m_polyBufferRam; // dynamic polys

View file

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

View file

@ -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 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,6 +330,7 @@ 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");
@ -309,6 +338,8 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
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)

View file

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