mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-24 14:45:40 +00:00
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:
parent
e4f5f0bcaf
commit
b5f9ad9651
|
@ -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<Poly> polys;
|
||||
std::vector<FVertex> verts;
|
||||
};
|
||||
|
||||
struct Model
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
#include <string.h>
|
||||
#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<bool>()) {
|
||||
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<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
|
||||
|
||||
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<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) {
|
||||
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<Poly> *polys;
|
||||
int offset;
|
||||
//================
|
||||
//===============================
|
||||
ClipPoly clipPoly;
|
||||
std::vector<FVertex>* 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);
|
||||
|
||||
|
|
|
@ -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<Poly>& polyArray);
|
||||
void CopyVertexData(const R3DPoly& r3dPoly, std::vector<FVertex>& 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<Node> m_nodes; // this represents the entire render frame
|
||||
std::vector<Poly> m_polyBufferRam; // dynamic polys
|
||||
std::vector<Poly> m_polyBufferRom; // rom polys
|
||||
std::vector<Node> m_nodes; // this represents the entire render frame
|
||||
std::vector<FVertex> m_polyBufferRam; // dynamic 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
|
||||
|
||||
VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow
|
||||
|
|
|
@ -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<bool>();
|
||||
|
||||
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<std::string>(), m_config["FragmentShader"].ValueAs<std::string>(), 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<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
|
||||
|
|
|
@ -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
|
527
Src/Graphics/New3D/R3DShaderQuads.h
Normal file
527
Src/Graphics/New3D/R3DShaderQuads.h
Normal 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
|
300
Src/Graphics/New3D/R3DShaderTriangles.h
Normal file
300
Src/Graphics/New3D/R3DShaderTriangles.h
Normal 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
|
|
@ -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 } },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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\R3DScrollFog.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\TextureSheet.h" />
|
||||
<ClInclude Include="..\Src\Graphics\New3D\VBO.h" />
|
||||
|
|
|
@ -874,6 +874,12 @@
|
|||
<ClInclude Include="..\Src\Graphics\New3D\GLSLShader.h">
|
||||
<Filter>Source Files\Graphics\New</Filter>
|
||||
</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>
|
||||
<CustomBuild Include="..\Src\Debugger\ReadMe.txt">
|
||||
|
|
Loading…
Reference in a new issue