Quad rendering engine. Set QuadRendering = 1 in the ini file to use, or -quad-rendering at the end of the command line to use.

This commit is contained in:
Ian Curtis 2018-09-13 12:50:34 +00:00
parent e4f5f0bcaf
commit b5f9ad9651
11 changed files with 1123 additions and 463 deletions

View file

@ -22,12 +22,43 @@ struct ClipPoly
int count = 0; int count = 0;
}; };
struct Vertex // half vertex struct Vertex // half vertex
{ {
float pos[4]; float pos[4];
float normal[3]; float normal[3];
float texcoords[2]; float texcoords[2];
float fixedShade; 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 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)); memcpy(this, &vertex, sizeof(Vertex));
return *this; return *this;
} }
};
struct R3DPoly FVertex() {}
{ FVertex(const R3DPoly& r3dPoly, int index)
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 for (int i = 0; i < 4; i++) { faceColour[i] = r3dPoly.faceColour[i]; }
UINT8 faceColour[4]; // per face colour for (int i = 0; i < 3; i++) { faceNormal[i] = r3dPoly.faceNormal[i]; }
int number = 4;
};
struct Poly // our polys are always 3 triangles, unlike the real h/w *this = r3dPoly.v[index];
{
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];
}
} }
FVertex p1; FVertex(const R3DPoly& r3dPoly, int index1, int index2) // average of 2 points
FVertex p2; {
FVertex p3; 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 }; enum class Layer { colour, trans1, trans2, all, none };
@ -150,12 +162,12 @@ struct Mesh
// opengl resources // opengl resources
int vboOffset = 0; // this will be calculated later 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 struct SortingMesh : public Mesh // This struct temporarily holds the model data, before it gets copied to the main buffer
{ {
std::vector<Poly> polys; std::vector<FVertex> verts;
}; };
struct Model struct Model

View file

@ -7,8 +7,8 @@
#include <string.h> #include <string.h>
#include "R3DFloat.h" #include "R3DFloat.h"
#define MAX_RAM_POLYS 100000 #define MAX_RAM_VERTS 300000
#define MAX_ROM_POLYS 500000 #define MAX_ROM_VERTS 1500000
#define BYTE_TO_FLOAT(B) ((2.0f * (B) + 1.0f) * (1.0F/255.0f)) #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_textureRAM = nullptr;
m_sunClamp = true; m_sunClamp = true;
m_shadeIsSigned = true; m_shadeIsSigned = true;
m_numPolyVerts = 3;
m_primType = GL_TRIANGLES;
if (config["QuadRendering"].ValueAs<bool>()) {
m_numPolyVerts = 4;
m_primType = GL_LINES_ADJACENCY;
}
} }
CNew3D::~CNew3D() CNew3D::~CNew3D()
@ -59,7 +66,7 @@ void CNew3D::SetStepping(int stepping)
m_vertexFactor = (1.0f / 128.0f); // 17.7 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) 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<Texture> tex1; std::shared_ptr<Texture> tex1;
CalcViewport(&n.viewport, std::abs(m_nfPairs[priority].zNear*0.95f), std::abs(m_nfPairs[priority].zFar*1.05f)); // make planes 5% bigger 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);
glViewport (n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height);
glMatrixMode (GL_PROJECTION);
glLoadMatrixf (n.viewport.projectionMatrix);
glMatrixMode (GL_MODELVIEW);
m_r3dShader.SetViewportUniforms(&n.viewport); m_r3dShader.SetViewportUniforms(&n.viewport);
@ -221,7 +224,7 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer)
} }
m_r3dShader.SetMeshUniforms(&mesh); 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 DrawScrollFog(); // fog layer if applicable must be drawn here
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_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()) { if (m_polyBufferRom.size()) {
// sync rom memory with vbo // 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 vboBytes = m_vbo.GetSize();
int size = romBytes - vboBytes; int size = romBytes - vboBytes;
if (size) { if (size) {
//check we haven't blown up the memory buffers //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 //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_polyBufferRom.clear();
m_romMap.clear(); m_romMap.clear();
m_vbo.Reset(); m_vbo.Reset();
} }
else { 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); modelAddress = TranslateModelAddress(modelAddr);
// create a new model to push onto the vector // 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 // get the last model in the array
m = &m_nodes.back().models.back(); m = &m_nodes.back().models.back();
@ -811,7 +814,6 @@ void CNew3D::RenderViewport(UINT32 addr)
vp->angle_top = (1.0f / cw) * -(0.0f - io); vp->angle_top = (1.0f / cw) * -(0.0f - io);
// calculate the frustum shape, near/far pair are dummy values // calculate the frustum shape, near/far pair are dummy values
CalcViewport(vp, 1, 1000); CalcViewport(vp, 1, 1000);
// calculate frustum planes // calculate frustum planes
@ -924,12 +926,46 @@ void CNew3D::RenderViewport(UINT32 addr)
} }
} }
void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray) void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector<FVertex>& 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) { // check for identical points (ie forced triangle) and replace with average point
polyArray.emplace_back(Poly(false, r3dPoly)); // copy second triangle // 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]; currentMesh = &sMap[hash];
//make space for our vertices //make space for our vertices
currentMesh->polys.reserve(numTriangles); currentMesh->verts.reserve(numTriangles * 3);
//set mesh values //set mesh values
SetMeshValues(currentMesh, ph); SetMeshValues(currentMesh, ph);
@ -1222,12 +1258,12 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
V3::inverse(tempP.v[i].normal); V3::inverse(tempP.v[i].normal);
} }
CopyVertexData(tempP, currentMesh->polys); CopyVertexData(tempP, currentMesh->verts);
} }
// Copy this polygon into the model buffer // Copy this polygon into the model buffer
if (!ph.Discard()) { if (!ph.Discard()) {
CopyVertexData(p, currentMesh->polys); CopyVertexData(p, currentMesh->verts);
} }
// Copy current vertices into previous vertex array // Copy current vertices into previous vertex array
@ -1249,19 +1285,19 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
if (m->dynamic) { if (m->dynamic) {
// calculate VBO values for current mesh // calculate VBO values for current mesh
it.second.vboOffset = m_polyBufferRam.size() + MAX_ROM_POLYS; it.second.vboOffset = (int)m_polyBufferRam.size() + MAX_ROM_VERTS;
it.second.triangleCount = it.second.polys.size(); it.second.vertexCount = (int)it.second.verts.size();
// copy poly data to main buffer // 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 { else {
// calculate VBO values for current mesh // calculate VBO values for current mesh
it.second.vboOffset = m_polyBufferRom.size(); it.second.vboOffset = (int)m_polyBufferRom.size();
it.second.triangleCount = it.second.polys.size(); it.second.vertexCount = (int)it.second.verts.size();
// copy poly data to main buffer // 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 //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) bool CNew3D::IsDynamicModel(UINT32 *data)
{ {
if (data == NULL) { if (data == NULL) {
@ -1551,18 +1572,18 @@ void CNew3D::ClipPolygon(ClipPoly& clipPoly, Plane planes[4])
void CNew3D::ClipModel(const Model *m) void CNew3D::ClipModel(const Model *m)
{ {
//================ //===============================
ClipPoly clipPoly; ClipPoly clipPoly;
std::vector<Poly> *polys; std::vector<FVertex>* vertices;
int offset; int offset;
//================ //===============================
if (m->dynamic) { if (m->dynamic) {
polys = &m_polyBufferRam; vertices = &m_polyBufferRam;
offset = MAX_ROM_POLYS; offset = MAX_ROM_VERTS;
} }
else { else {
polys = &m_polyBufferRom; vertices = &m_polyBufferRom;
offset = 0; offset = 0;
} }
@ -1570,17 +1591,13 @@ void CNew3D::ClipModel(const Model *m)
int start = mesh.vboOffset - offset; 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
//================================== for (int j = 0; j < m_numPolyVerts; j++) {
Poly& poly = (*polys)[start + i]; 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); clipPoly.count = m_numPolyVerts;
MultVec(m->modelMat, poly.p2.pos, clipPoly.list[1].pos);
MultVec(m->modelMat, poly.p3.pos, clipPoly.list[2].pos);
clipPoly.count = 3;
ClipPolygon(clipPoly, m_planes); ClipPolygon(clipPoly, m_planes);

View file

@ -198,11 +198,10 @@ private:
// building the scene // building the scene
void SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph); void SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph);
void CacheModel(Model *m, const UINT32 *data); void CacheModel(Model *m, const UINT32 *data);
void CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray); void CopyVertexData(const R3DPoly& r3dPoly, std::vector<FVertex>& vertexArray);
void OffsetTexCoords(R3DPoly& r3dPoly, float offset[2]); void OffsetTexCoords(R3DPoly& r3dPoly, float offset[2]);
bool RenderScene(int priority, bool renderOverlay, Layer layer); // returns if has overlay plane 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 IsDynamicModel(UINT32 *data); // check if the model has a colour palette
bool IsVROMModel(UINT32 modelAddr); bool IsVROMModel(UINT32 modelAddr);
void DrawScrollFog(); void DrawScrollFog();
@ -218,6 +217,8 @@ private:
// Misc // Misc
std::string m_gameName; std::string m_gameName;
int m_numPolyVerts;
GLenum m_primType;
// GPU configuration // GPU configuration
bool m_sunClamp; 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 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 UINT16 m_prevTexCoords[4][2]; // basically relying on undefined behavour
std::vector<Node> m_nodes; // this represents the entire render frame std::vector<Node> m_nodes; // this represents the entire render frame
std::vector<Poly> m_polyBufferRam; // dynamic polys std::vector<FVertex> m_polyBufferRam; // dynamic polys
std::vector<Poly> m_polyBufferRom; // rom polys std::vector<FVertex> m_polyBufferRom; // rom polys
std::unordered_map<UINT32, std::shared_ptr<std::vector<Mesh>>> m_romMap; // a hash table for all the ROM models. The meshes don't have model matrices or tex offsets yet std::unordered_map<UINT32, std::shared_ptr<std::vector<Mesh>>> 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 VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow

View file

@ -1,307 +1,18 @@
#include "R3DShader.h" #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 { 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) R3DShader::R3DShader(const Util::Config::Node &config)
: m_config(config) : m_config(config)
{ {
m_shaderProgram = 0; m_shaderProgram = 0;
m_vertexShader = 0; m_vertexShader = 0;
m_geoShader = 0;
m_fragmentShader = 0; m_fragmentShader = 0;
Start(); // reset attributes Start(); // reset attributes
@ -323,71 +34,93 @@ void R3DShader::Start()
m_specularValue = 0; m_specularValue = 0;
m_microTexScale = 0; m_microTexScale = 0;
m_baseTexSize[0] = 0; m_baseTexSize[0] = 0;
m_baseTexSize[1] = 0; m_baseTexSize[1] = 0;
m_dirtyMesh = true; // dirty means all the above are dirty, ie first run m_dirtyMesh = true; // dirty means all the above are dirty, ie first run
m_dirtyModel = true; m_dirtyModel = true;
} }
bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader) bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
{ {
const char* vShader; bool quads = m_config["QuadRendering"].ValueAs<bool>();
const char* fShader;
bool success;
if (vertexShader) { const char* vShader = vertexShaderR3D;
vShader = vertexShader; const char* gShader = "";
} const char* fShader = fragmentShaderR3D;
else {
vShader = vertexShaderR3D; if (quads) {
vShader = vertexShaderR3DQuads;
gShader = geometryShaderR3DQuads;
fShader = fragmentShaderR3DQuads;
} }
if (fragmentShader) { m_shaderProgram = glCreateProgram();
fShader = fragmentShader; m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
} m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
else {
fShader = fragmentShaderR3D; 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<std::string>(), m_config["FragmentShader"].ValueAs<std::string>(), vShader, fShader); PrintShaderResult(m_vertexShader);
PrintShaderResult(m_fragmentShader);
m_locTexture1 = glGetUniformLocation(m_shaderProgram, "tex1"); glAttachShader(m_shaderProgram, m_vertexShader);
m_locTexture2 = glGetUniformLocation(m_shaderProgram, "tex2"); glAttachShader(m_shaderProgram, m_fragmentShader);
m_locTexture1Enabled= glGetUniformLocation(m_shaderProgram, "textureEnabled"); glLinkProgram(m_shaderProgram);
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_locFogIntensity = glGetUniformLocation(m_shaderProgram, "fogIntensity"); PrintProgramResult(m_shaderProgram);
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_locLighting = glGetUniformLocation(m_shaderProgram, "lighting"); m_locTexture1 = glGetUniformLocation(m_shaderProgram, "tex1");
m_locLightEnabled = glGetUniformLocation(m_shaderProgram, "lightEnabled"); m_locTexture2 = glGetUniformLocation(m_shaderProgram, "tex2");
m_locSunClamp = glGetUniformLocation(m_shaderProgram, "sunClamp"); m_locTexture1Enabled = glGetUniformLocation(m_shaderProgram, "textureEnabled");
m_locIntensityClamp = glGetUniformLocation(m_shaderProgram, "intensityClamp"); m_locTexture2Enabled = glGetUniformLocation(m_shaderProgram, "microTexture");
m_locShininess = glGetUniformLocation(m_shaderProgram, "shininess"); m_locTextureAlpha = glGetUniformLocation(m_shaderProgram, "textureAlpha");
m_locSpecularValue = glGetUniformLocation(m_shaderProgram, "specularValue"); m_locAlphaTest = glGetUniformLocation(m_shaderProgram, "alphaTest");
m_locSpecularEnabled= glGetUniformLocation(m_shaderProgram, "specularEnabled"); m_locMicroTexScale = glGetUniformLocation(m_shaderProgram, "microTextureScale");
m_locFixedShading = glGetUniformLocation(m_shaderProgram, "fixedShading"); m_locBaseTexSize = glGetUniformLocation(m_shaderProgram, "baseTexSize");
m_locTextureInverted = glGetUniformLocation(m_shaderProgram, "textureInverted");
m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse"); m_locFogIntensity = glGetUniformLocation(m_shaderProgram, "fogIntensity");
m_locSpotRange = glGetUniformLocation(m_shaderProgram, "spotRange"); m_locFogDensity = glGetUniformLocation(m_shaderProgram, "fogDensity");
m_locSpotColor = glGetUniformLocation(m_shaderProgram, "spotColor"); m_locFogStart = glGetUniformLocation(m_shaderProgram, "fogStart");
m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor"); m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour");
m_locModelScale = glGetUniformLocation(m_shaderProgram, "modelScale"); m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation");
m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient");
m_locHardwareStep = glGetUniformLocation(m_shaderProgram, "hardwareStep"); m_locLighting = glGetUniformLocation(m_shaderProgram, "lighting");
m_locDiscardAlpha = glGetUniformLocation(m_shaderProgram, "discardAlpha"); m_locLightEnabled = glGetUniformLocation(m_shaderProgram, "lightEnabled");
m_locSunClamp = glGetUniformLocation(m_shaderProgram, "sunClamp");
return success; 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) GLint R3DShader::GetVertexAttribPos(const char* attrib)
@ -498,23 +231,25 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
} }
void R3DShader::SetViewportUniforms(const Viewport *vp) void R3DShader::SetViewportUniforms(const Viewport *vp)
{ {
//didn't bother caching these, they don't get frequently called anyway //didn't bother caching these, they don't get frequently called anyway
glUniform1f (m_locFogDensity, vp->fogParams[3]); glUniform1f(m_locFogDensity, vp->fogParams[3]);
glUniform1f (m_locFogStart, vp->fogParams[4]); glUniform1f(m_locFogStart, vp->fogParams[4]);
glUniform3fv(m_locFogColour, 1, vp->fogParams); glUniform3fv(m_locFogColour, 1, vp->fogParams);
glUniform1f (m_locFogAttenuation, vp->fogParams[5]); glUniform1f(m_locFogAttenuation, vp->fogParams[5]);
glUniform1f (m_locFogAmbient, vp->fogParams[6]); glUniform1f(m_locFogAmbient, vp->fogParams[6]);
glUniform3fv(m_locLighting, 2, vp->lightingParams); glUniform3fv(m_locLighting, 2, vp->lightingParams);
glUniform1i (m_locSunClamp, vp->sunClamp); glUniform1i(m_locSunClamp, vp->sunClamp);
glUniform1i (m_locIntensityClamp, vp->intensityClamp); glUniform1i(m_locIntensityClamp, vp->intensityClamp);
glUniform4fv(m_locSpotEllipse, 1, vp->spotEllipse); glUniform4fv(m_locSpotEllipse, 1, vp->spotEllipse);
glUniform2fv(m_locSpotRange, 1, vp->spotRange); glUniform2fv(m_locSpotRange, 1, vp->spotRange);
glUniform3fv(m_locSpotColor, 1, vp->spotColor); glUniform3fv(m_locSpotColor, 1, vp->spotColor);
glUniform3fv(m_locSpotFogColor, 1, vp->spotFogColor); 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) void R3DShader::SetModelStates(const Model* model)
@ -524,6 +259,8 @@ void R3DShader::SetModelStates(const Model* model)
m_modelScale = model->scale; m_modelScale = model->scale;
} }
glUniformMatrix4fv(m_locModelMat, 1, GL_FALSE, model->modelMat);
m_dirtyModel = false; m_dirtyModel = false;
} }
@ -532,4 +269,44 @@ void R3DShader::DiscardAlpha(bool discard)
glUniform1i(m_locDiscardAlpha, 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<char> 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<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, infoLog.data());
printf("%s\n", infoLog.data());
}
}
} // New3D } // New3D

View file

@ -23,12 +23,16 @@ public:
private: private:
void PrintShaderResult(GLuint shader);
void PrintProgramResult(GLuint program);
// run-time config // run-time config
const Util::Config::Node &m_config; const Util::Config::Node &m_config;
// shader IDs // shader IDs
GLuint m_shaderProgram; GLuint m_shaderProgram;
GLuint m_vertexShader; GLuint m_vertexShader;
GLuint m_geoShader;
GLuint m_fragmentShader; GLuint m_fragmentShader;
// mesh uniform locations // mesh uniform locations
@ -58,7 +62,7 @@ private:
float m_microTexScale; float m_microTexScale;
float m_baseTexSize[2]; float m_baseTexSize[2];
bool m_textureInverted; bool m_textureInverted;
// cached model values // cached model values
float m_modelScale; float m_modelScale;
@ -73,6 +77,7 @@ private:
GLint m_locFogColour; GLint m_locFogColour;
GLint m_locFogAttenuation; GLint m_locFogAttenuation;
GLint m_locFogAmbient; GLint m_locFogAmbient;
GLint m_locProjMat;
// lighting / other // lighting / other
GLint m_locLighting; GLint m_locLighting;
@ -88,15 +93,17 @@ private:
GLint m_locSpotRange; GLint m_locSpotRange;
GLint m_locSpotColor; GLint m_locSpotColor;
GLint m_locSpotFogColor; GLint m_locSpotFogColor;
// model uniforms // model uniforms
GLint m_locModelScale; GLint m_locModelScale;
GLint m_locModelMat;
// global uniforms // global uniforms
GLint m_locHardwareStep; GLint m_locHardwareStep;
GLint m_locDiscardAlpha; GLint m_locDiscardAlpha;
}; };
} // New3D } // New3D
#endif #endif

View file

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

View file

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

View file

@ -304,6 +304,7 @@ static void PrintGLInfo(bool createScreen, bool infoLog, bool printExtensions)
else printf(" %s\n", strLocal); else printf(" %s\n", strLocal);
} }
} }
free(strLocal);
} }
if (infoLog) InfoLog(""); if (infoLog) InfoLog("");
else printf("\n"); else printf("\n");
@ -1333,6 +1334,7 @@ static Util::Config::Node DefaultConfig()
#endif #endif
// Platform-specific/UI // Platform-specific/UI
config.Set("New3DEngine", true); config.Set("New3DEngine", true);
config.Set("QuadRendering", false);
config.Set("XResolution", "496"); config.Set("XResolution", "496");
config.Set("YResolution", "384"); config.Set("YResolution", "384");
config.Set("FullScreen", false); config.Set("FullScreen", false);
@ -1507,6 +1509,7 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv)
{ "-show-fps", { "ShowFrameRate", true } }, { "-show-fps", { "ShowFrameRate", true } },
{ "-no-fps", { "ShowFrameRate", false } }, { "-no-fps", { "ShowFrameRate", false } },
{ "-new3d", { "New3DEngine", true } }, { "-new3d", { "New3DEngine", true } },
{ "-quad-rendering", { "QuadRendering", true } },
{ "-legacy3d", { "New3DEngine", false } }, { "-legacy3d", { "New3DEngine", false } },
{ "-no-flip-stereo", { "FlipStereo", false } }, { "-no-flip-stereo", { "FlipStereo", false } },
{ "-flip-stereo", { "FlipStereo", true } }, { "-flip-stereo", { "FlipStereo", true } },

View file

@ -13,6 +13,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZLib", "ZLib\ZLib.vcxproj",
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Musashi68K", "Musashi68K\Musashi68K.vcxproj", "{1248CF7C-B122-461C-9624-196AEFAE5046}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Musashi68K", "Musashi68K\Musashi68K.vcxproj", "{1248CF7C-B122-461C-9624-196AEFAE5046}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32 Debug|Win32 = Debug|Win32
@ -65,4 +70,7 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal EndGlobal

View file

@ -550,6 +550,8 @@ xcopy /D /Y "$(ProjectDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDi
<ClInclude Include="..\Src\Graphics\New3D\R3DFrameBuffers.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\R3DShaderQuads.h" />
<ClInclude Include="..\Src\Graphics\New3D\R3DShaderTriangles.h" />
<ClInclude Include="..\Src\Graphics\New3D\Texture.h" /> <ClInclude Include="..\Src\Graphics\New3D\Texture.h" />
<ClInclude Include="..\Src\Graphics\New3D\TextureSheet.h" /> <ClInclude Include="..\Src\Graphics\New3D\TextureSheet.h" />
<ClInclude Include="..\Src\Graphics\New3D\VBO.h" /> <ClInclude Include="..\Src\Graphics\New3D\VBO.h" />

View file

@ -874,6 +874,12 @@
<ClInclude Include="..\Src\Graphics\New3D\GLSLShader.h"> <ClInclude Include="..\Src\Graphics\New3D\GLSLShader.h">
<Filter>Source Files\Graphics\New</Filter> <Filter>Source Files\Graphics\New</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Src\Graphics\New3D\R3DShaderQuads.h">
<Filter>Source Files\Graphics\New</Filter>
</ClInclude>
<ClInclude Include="..\Src\Graphics\New3D\R3DShaderTriangles.h">
<Filter>Source Files\Graphics\New</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CustomBuild Include="..\Src\Debugger\ReadMe.txt"> <CustomBuild Include="..\Src\Debugger\ReadMe.txt">