Rewrite the whole project for GL4+. I figured if we removed the limitation of a legacy rendering API we could improve things a bit. With GL4+ we can do unsigned integer math in the shaders. This allows us to upload a direct copy of the real3d texture sheet, and texture directly from this memory given the x/y pos and type. This massively simplifies the binding and invalidation code. Also the crazy corner cases will work because it essentially works the same way as the original hardware.

The standard triangle render requires gl 4.1 core, so should work on mac. The quad renderer runs on 4.5 core. The legacy renderer should still work, and when enabled a regular opengl context will be created, which allows functions marked depreciated in the core profiles to still work. This will only work in windows/linux I think. Apple doesn't support this.

A GL 4.1 GPU is now the min required spec. Sorry if you have an OLDER gpu. GL 4.1 is over 12 years old now.

This is a big update so I apologise in advance if I accidently broke something :]
This commit is contained in:
Ian Curtis 2022-11-07 21:33:01 +00:00
parent 121f81c742
commit 40c8259130
23 changed files with 941 additions and 1171 deletions

View file

@ -5,7 +5,7 @@
#include <unordered_map>
#include <memory>
#include <cstring>
#include "Texture.h"
#include "Types.h"
#include "Mat4.h"
namespace New3D {

View file

@ -1,5 +1,4 @@
#include "New3D.h"
#include "Texture.h"
#include "Vec.h"
#include <cmath>
#include <algorithm>
@ -16,10 +15,12 @@
namespace New3D {
CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName)
: m_r3dShader(config),
m_r3dScrollFog(config),
m_gameName(gameName)
CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName) :
m_r3dShader(config),
m_r3dScrollFog(config),
m_gameName(gameName),
m_textureBuffer(0),
m_vao(0)
{
m_cullingRAMLo = nullptr;
m_cullingRAMHi = nullptr;
@ -40,6 +41,17 @@ CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName)
CNew3D::~CNew3D()
{
m_vbo.Destroy();
if (m_vao) {
glDeleteVertexArrays(1, &m_vao);
m_vao = 0;
}
if (m_textureBuffer) {
glDeleteTextures(1, &m_textureBuffer);
m_textureBuffer = 0;
}
m_r3dShader.UnloadShader();
}
void CNew3D::AttachMemory(const UINT32 *cullingRAMLoPtr, const UINT32 *cullingRAMHiPtr, const UINT32 *polyRAMPtr, const UINT32 *vromPtr, const UINT16 *textureRAMPtr)
@ -67,8 +79,6 @@ void CNew3D::SetStepping(int stepping)
m_offset = 2; // 8 words
m_vertexFactor = (1.0f / 128.0f); // 17.7
}
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)
@ -84,34 +94,55 @@ bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yR
m_totalYRes = totalYResParam;
m_r3dShader.LoadShader();
glUseProgram(0);
m_r3dFrameBuffers.CreateFBO(totalXResParam, totalYResParam);
glUseProgram(0);
// setup our texture memory
return OKAY; // OKAY ? wtf ..
glGenTextures(1, &m_textureBuffer);
glBindTexture(GL_TEXTURE_2D, m_textureBuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, 2048, 2048, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, nullptr); // allocate storage
// setup up our vertex buffer memory
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(FVertex) * (MAX_RAM_VERTS + MAX_ROM_VERTS));
m_vbo.Bind(true);
glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inVertex"));
glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inNormal"));
glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inTexCoord"));
glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inColour"));
glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inFaceNormal"));
glEnableVertexAttribArray(m_r3dShader.GetVertexAttribPos("inFixedShade"));
// before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil ..
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inVertex"), 4, GL_FLOAT, GL_FALSE, sizeof(FVertex), 0);
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, normal));
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inTexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, texcoords));
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inColour"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FVertex), (void*)offsetof(FVertex, faceColour));
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFaceNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, faceNormal));
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFixedShade"), 1, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, fixedShade));
glBindVertexArray(0);
m_vbo.Bind(false);
return OKAY;
}
void CNew3D::UploadTextures(unsigned level, unsigned x, unsigned y, unsigned width, unsigned height)
{
if (level == 0) {
m_texSheet.Invalidate(x, y, width, height); // base textures only
}
else if (level == 1) {
// we want to work out what the base level is, and invalidate the entire texture
// the mipmap data in some cases is being sent later
glBindTexture(GL_TEXTURE_2D, m_textureBuffer);
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
int page = y / 1024;
y -= (page * 1024); // remove page from tex y
int xPos = (x - 1024) * 2;
int yPos = (y - 512) * 2;
yPos += page * 1024;
width *= 2;
height *= 2;
m_texSheet.Invalidate(xPos, yPos, width, height);
for (unsigned i = 0; i < height; i++) {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y + i, width, 1, GL_RED_INTEGER, GL_UNSIGNED_SHORT, m_textureRAM + ((y + i) * 2048) + x);
}
}
@ -170,6 +201,9 @@ CheckScroll:
bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_textureBuffer);
bool hasOverlay = false; // (high priority polys)
for (auto &n : m_nodes) {
@ -178,8 +212,6 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer)
continue;
}
std::shared_ptr<Texture> tex1;
CalcViewport(&n.viewport, std::abs(m_nfPairs[priority].zNear*0.96f), 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);
@ -207,34 +239,6 @@ bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer)
matrixLoaded = true; // do this here to stop loading matrices we don't need. Ie when rendering non transparent etc
}
if (mesh.textured) {
int x, y;
CalcTexOffset(m.textureOffsetX, m.textureOffsetY, m.page, mesh.x, mesh.y, x, y);
if (tex1 && tex1->Compare(x, y, mesh.width, mesh.height, mesh.format)) {
// texture already bound
}
else {
tex1 = m_texSheet.BindTexture(m_textureRAM, mesh.format, x, y, mesh.width, mesh.height);
if (tex1) {
tex1->BindTexture();
}
}
if (mesh.microTexture) {
int mX, mY;
glActiveTexture(GL_TEXTURE1);
m_texSheet.GetMicrotexPos(y / 1024, mesh.microTextureID, mX, mY);
auto tex2 = m_texSheet.BindTexture(m_textureRAM, 0, mX, mY, 128, 128);
if (tex2) {
tex2->BindTexture();
}
glActiveTexture(GL_TEXTURE0);
}
}
m_r3dShader.SetMeshUniforms(&mesh);
glDrawArrays(m_primType, mesh.vboOffset, mesh.vertexCount);
}
@ -260,23 +264,10 @@ bool CNew3D::SkipLayer(int layer)
void CNew3D::SetRenderStates()
{
m_vbo.Bind(true);
glBindVertexArray(m_vao);
m_r3dShader.SetShader(true);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
// before draw, specify vertex and index arrays with their offsets, offsetof is maybe evil ..
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inVertex"), 4, GL_FLOAT, GL_FALSE, sizeof(FVertex), 0);
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, normal));
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inTexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, texcoords));
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inColour"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FVertex), (void*)offsetof(FVertex, faceColour));
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFaceNormal"), 3, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, faceNormal));
glVertexAttribPointer(m_r3dShader.GetVertexAttribPos("inFixedShade"), 1, GL_FLOAT, GL_FALSE, sizeof(FVertex), (void*)offsetof(FVertex, fixedShade));
glDepthFunc (GL_LEQUAL);
glEnable (GL_DEPTH_TEST);
glDepthMask (GL_TRUE);
@ -292,16 +283,11 @@ void CNew3D::SetRenderStates()
void CNew3D::DisableRenderStates()
{
m_vbo.Bind(false);
glBindVertexArray(0);
m_r3dShader.SetShader(false);
glDisable(GL_STENCIL_TEST);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(4);
glDisableVertexAttribArray(5);
}
void CNew3D::RenderFrame(void)
@ -992,6 +978,30 @@ void CNew3D::CopyVertexData(const R3DPoly& r3dPoly, std::vector<FVertex>& vertex
}
}
void CNew3D::GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
{
uOut = (uIn * uvScale) / width;
vOut = (vIn * uvScale) / height;
}
int CNew3D::GetTexFormat(int originalFormat, bool contour)
{
if (!contour) {
return originalFormat; // the same
}
switch (originalFormat)
{
case 1:
case 2:
case 3:
case 4:
return originalFormat + 7; // these formats are identical to 1-4, except they lose the 4 bit alpha part when contour is enabled
default:
return originalFormat;
}
}
void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph)
{
//copy attributes
@ -1012,7 +1022,7 @@ void CNew3D::SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph)
if (currentMesh->textured) {
currentMesh->format = m_texSheet.GetTexFormat(ph.TexFormat(), ph.AlphaTest());
currentMesh->format = GetTexFormat(ph.TexFormat(), ph.AlphaTest());
if (currentMesh->format == 7) {
currentMesh->alphaTest = false; // alpha test is a 1 bit test, this format needs a lower threshold, since it has 16 levels of transparency
@ -1127,7 +1137,7 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
//check if we need to recalc tex coords - will only happen if tex tiles are different + sharing vertices
if (hash != lastHash) {
if (currentMesh->textured) {
Texture::GetCoordinates(currentMesh->width, currentMesh->height, texCoords[j][0], texCoords[j][1], uvScale, p.v[j].texcoords[0], p.v[j].texcoords[1]);
GetCoordinates(currentMesh->width, currentMesh->height, texCoords[j][0], texCoords[j][1], uvScale, p.v[j].texcoords[0], p.v[j].texcoords[1]);
}
}
@ -1209,7 +1219,7 @@ void CNew3D::CacheModel(Model *m, const UINT32 *data)
// tex coords
if (currentMesh->textured) {
Texture::GetCoordinates(currentMesh->width, currentMesh->height, (UINT16)(it >> 16), (UINT16)(it & 0xFFFF), uvScale, texU, texV);
GetCoordinates(currentMesh->width, currentMesh->height, (UINT16)(it >> 16), (UINT16)(it & 0xFFFF), uvScale, texU, texV);
}
p.v[j].texcoords[0] = texU;

View file

@ -31,7 +31,6 @@
#include <GL/glew.h>
#include "Types.h"
#include "TextureSheet.h"
#include "Graphics/IRender3D.h"
#include "Model.h"
#include "Mat4.h"
@ -207,9 +206,11 @@ private:
void RenderViewport(UINT32 addr);
// building the scene
int GetTexFormat(int originalFormat, bool contour);
void SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph);
void CacheModel(Model *m, const UINT32 *data);
void CopyVertexData(const R3DPoly& r3dPoly, std::vector<FVertex>& vertexArray);
void GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut);
bool RenderScene(int priority, bool renderOverlay, Layer layer); // returns if has overlay plane
bool IsDynamicModel(UINT32 *data); // check if the model has a colour palette
@ -259,7 +260,7 @@ private:
UINT32 m_colorTableAddr = 0x400; // address of color table in polygon RAM
LODBlendTable* m_LODBlendTable;
TextureSheet m_texSheet;
GLuint m_textureBuffer;
NodeAttributes m_nodeAttribs;
Mat4 m_modelMat; // current modelview matrix
@ -280,6 +281,7 @@ private:
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
GLuint m_vao;
VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow
R3DShader m_r3dShader;
R3DScrollFog m_r3dScrollFog;

View file

@ -13,6 +13,7 @@ R3DFrameBuffers::R3DFrameBuffers()
m_renderBufferIDCopy = 0;
m_width = 0;
m_height = 0;
m_vao = 0;
for (auto &i : m_texIDs) {
i = 0;
@ -24,13 +25,10 @@ R3DFrameBuffers::R3DFrameBuffers()
AllocShaderBase();
AllocShaderWipe();
FBVertex vertices[4];
vertices[0].Set(-1,-1, 0, 0);
vertices[1].Set(-1, 1, 0, 1);
vertices[2].Set( 1,-1, 1, 0);
vertices[3].Set( 1, 1, 1, 1);
m_vbo.Create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, sizeof(vertices), vertices);
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// no states needed since we do it in the shader
glBindVertexArray(0);
}
R3DFrameBuffers::~R3DFrameBuffers()
@ -39,7 +37,10 @@ R3DFrameBuffers::~R3DFrameBuffers()
m_shaderTrans.UnloadShaders();
m_shaderBase.UnloadShaders();
m_shaderWipe.UnloadShaders();
m_vbo.Destroy();
if (m_vao) {
glDeleteVertexArrays(1, &m_vao);
m_vao = 0;
}
}
bool R3DFrameBuffers::CreateFBO(int width, int height)
@ -201,80 +202,86 @@ void R3DFrameBuffers::AllocShaderBase()
{
const char *vertexShader = R"glsl(
#version 120
// inputs
attribute vec3 inVertex;
attribute vec2 inTexCoord;
#version 410 core
// outputs
varying vec2 fsTexCoord;
out vec2 fsTexCoord;
void main(void)
{
fsTexCoord = inTexCoord;
gl_Position = vec4(inVertex,1.0);
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0;
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
const char *fragmentShader = R"glsl(
#version 120
#version 410 core
// inputs
uniform sampler2D tex1; // base tex
in vec2 fsTexCoord;
varying vec2 fsTexCoord;
// outputs
out vec4 fragColor;
void main()
{
vec4 colBase = texture2D( tex1, fsTexCoord);
vec4 colBase = texture(tex1, fsTexCoord);
if(colBase.a < 1.0) discard;
gl_FragColor = colBase;
fragColor = colBase;
}
)glsl";
m_shaderBase.LoadShaders(vertexShader, fragmentShader);
m_shaderBase.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1");
m_shaderBase.attribLoc[0] = m_shaderTrans.GetAttributeLocation("inVertex");
m_shaderBase.attribLoc[1] = m_shaderTrans.GetAttributeLocation("inTexCoord");
}
void R3DFrameBuffers::AllocShaderTrans()
{
const char *vertexShader = R"glsl(
#version 120
// inputs
attribute vec3 inVertex;
attribute vec2 inTexCoord;
#version 410 core
// outputs
varying vec2 fsTexCoord;
out vec2 fsTexCoord;
void main(void)
{
fsTexCoord = inTexCoord;
gl_Position = vec4(inVertex,1.0);
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0;
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
const char *fragmentShader = R"glsl(
#version 120
#version 410 core
uniform sampler2D tex1; // trans layer 1
uniform sampler2D tex2; // trans layer 2
varying vec2 fsTexCoord;
in vec2 fsTexCoord;
// outputs
out vec4 fragColor;
void main()
{
vec4 colTrans1 = texture2D( tex1, fsTexCoord);
vec4 colTrans2 = texture2D( tex2, fsTexCoord);
vec4 colTrans1 = texture( tex1, fsTexCoord);
vec4 colTrans2 = texture( tex2, fsTexCoord);
if(colTrans1.a+colTrans2.a > 0.0) {
vec3 col1 = colTrans1.rgb * colTrans1.a;
@ -284,7 +291,7 @@ void R3DFrameBuffers::AllocShaderTrans()
colTrans1.a+colTrans2.a);
}
gl_FragColor = colTrans1;
fragColor = colTrans1;
}
)glsl";
@ -293,49 +300,51 @@ void R3DFrameBuffers::AllocShaderTrans()
m_shaderTrans.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1");
m_shaderTrans.uniformLoc[1] = m_shaderTrans.GetUniformLocation("tex2");
m_shaderTrans.attribLoc[0] = m_shaderTrans.GetAttributeLocation("inVertex");
m_shaderTrans.attribLoc[1] = m_shaderTrans.GetAttributeLocation("inTexCoord");
}
void R3DFrameBuffers::AllocShaderWipe()
{
const char *vertexShader = R"glsl(
#version 120
// inputs
attribute vec3 inVertex;
attribute vec2 inTexCoord;
#version 410 core
// outputs
varying vec2 fsTexCoord;
out vec2 fsTexCoord;
void main(void)
{
fsTexCoord = inTexCoord;
gl_Position = vec4(inVertex,1.0);
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0;
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
const char *fragmentShader = R"glsl(
#version 120
#version 410 core
uniform sampler2D texColor; // base colour layer
varying vec2 fsTexCoord;
in vec2 fsTexCoord;
// outputs
layout (location = 0) out vec4 fragColor0;
layout (location = 1) out vec4 fragColor1;
void main()
{
vec4 colBase = texture2D( texColor, fsTexCoord);
vec4 colBase = texture(texColor, fsTexCoord);
if(colBase.a == 0.0) {
discard; // no colour pixels have been written
discard; // no colour pixels have been written
}
gl_FragData[0] = vec4(0.0); // wipe these parts of the alpha buffer
gl_FragData[1] = vec4(0.0); // since they have been overwritten by the next priority layer
fragColor0 = vec4(0.0); // wipe these parts of the alpha buffer
fragColor1 = vec4(0.0); // since they have been overwritten by the next priority layer
}
)glsl";
@ -343,9 +352,6 @@ void R3DFrameBuffers::AllocShaderWipe()
m_shaderWipe.LoadShaders(vertexShader, fragmentShader);
m_shaderWipe.uniformLoc[0] = m_shaderTrans.GetUniformLocation("texColor");
m_shaderWipe.attribLoc[0] = m_shaderTrans.GetAttributeLocation("inVertex");
m_shaderWipe.attribLoc[1] = m_shaderTrans.GetAttributeLocation("inTexCoord");
}
void R3DFrameBuffers::Draw()
@ -361,18 +367,18 @@ void R3DFrameBuffers::Draw()
glBindTexture(GL_TEXTURE_2D, m_texIDs[i]);
}
glActiveTexture (GL_TEXTURE0);
m_vbo.Bind (true);
glActiveTexture (GL_TEXTURE0);
glBindVertexArray (m_vao);
DrawBaseLayer ();
DrawBaseLayer ();
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable (GL_BLEND);
DrawAlphaLayer ();
DrawAlphaLayer ();
glDisable (GL_BLEND);
m_vbo.Bind (false);
glDisable (GL_BLEND);
glBindVertexArray (0);
}
void R3DFrameBuffers::CompositeBaseLayer()
@ -389,11 +395,11 @@ void R3DFrameBuffers::CompositeBaseLayer()
}
glActiveTexture(GL_TEXTURE0);
m_vbo.Bind(true);
glBindVertexArray(m_vao);
DrawBaseLayer();
m_vbo.Bind(false);
glBindVertexArray(0);
}
void R3DFrameBuffers::CompositeAlphaLayer()
@ -409,15 +415,15 @@ void R3DFrameBuffers::CompositeAlphaLayer()
}
glActiveTexture(GL_TEXTURE0);
m_vbo.Bind(true);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glBindVertexArray(m_vao);
DrawAlphaLayer();
glDisable(GL_BLEND);
m_vbo.Bind(false);
glBindVertexArray(0);
}
void R3DFrameBuffers::DrawOverTransLayers()
@ -431,44 +437,24 @@ void R3DFrameBuffers::DrawOverTransLayers()
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, m_texIDs[0]);
m_vbo.Bind(true);
glBindVertexArray(m_vao);
m_shaderWipe.EnableShader();
glUniform1i(m_shaderWipe.uniformLoc[0], 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(m_shaderWipe.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts));
glVertexAttribPointer(m_shaderWipe.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_shaderWipe.DisableShader();
m_vbo.Bind(false);
glBindVertexArray(0);
}
void R3DFrameBuffers::DrawBaseLayer()
{
m_shaderBase.EnableShader();
glUniform1i(m_shaderTrans.uniformLoc[0], 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(m_shaderTrans.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts));
glVertexAttribPointer(m_shaderTrans.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords));
glUniform1i(m_shaderTrans.uniformLoc[0], 0); // to do check this
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
m_shaderBase.DisableShader();
}
@ -478,17 +464,8 @@ void R3DFrameBuffers::DrawAlphaLayer()
glUniform1i(m_shaderTrans.uniformLoc[0], 1); // tex unit 1
glUniform1i(m_shaderTrans.uniformLoc[1], 2); // tex unit 2
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(m_shaderTrans.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts));
glVertexAttribPointer(m_shaderTrans.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
m_shaderTrans.DisableShader();
}

View file

@ -29,21 +29,6 @@ public:
private:
struct FBVertex
{
void Set(float x, float y, float s, float t)
{
texCoords[0] = s;
texCoords[1] = t;
verts[0] = x;
verts[1] = y;
verts[2] = 0.f; // z = 0
}
float texCoords[2];
float verts[3];
};
bool CreateFBODepthCopy(int width, int height);
GLuint CreateTexture(int width, int height);
void AllocShaderTrans();
@ -67,8 +52,8 @@ private:
GLSLShader m_shaderTrans;
GLSLShader m_shaderWipe;
// vertices for fbo
VBO m_vbo;
// vao
GLuint m_vao; // this really needed if we don't actually use vertex attribs?
};
}

View file

@ -1,26 +1,27 @@
#include "R3DScrollFog.h"
#include "Graphics/Shader.h"
#include "Mat4.h"
namespace New3D {
static const char *vertexShaderFog = R"glsl(
#version 120
uniform mat4 mvp;
attribute vec3 inVertex;
#version 410 core
void main(void)
{
gl_Position = mvp * vec4(inVertex,1.0);
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
static const char *fragmentShaderFog = R"glsl(
#version 120
#version 410 core
uniform float fogAttenuation;
uniform float fogAmbient;
@ -38,6 +39,9 @@ float lfogAttenuation;
vec3 lFogColor;
vec4 scrollFog;
// outputs
out vec4 fragColor;
void main()
{
// Scroll fog base color
@ -58,67 +62,59 @@ void main()
scrollFog = vec4(lFogColor + lSpotFogColor, fogColour.a);
// Final Color
gl_FragColor = scrollFog;
fragColor = scrollFog;
}
)glsl";
R3DScrollFog::R3DScrollFog(const Util::Config::Node &config)
: m_config(config)
: m_config(config),
m_vao(0)
{
//default coordinates are NDC -1,1 etc
m_triangles[0].p1.Set(-1,-1, 0);
m_triangles[0].p2.Set(-1, 1, 0);
m_triangles[0].p3.Set( 1, 1, 0);
m_triangles[1].p1.Set(-1,-1, 0);
m_triangles[1].p2.Set( 1, 1, 0);
m_triangles[1].p3.Set( 1,-1, 0);
m_shaderProgram = 0;
m_vertexShader = 0;
m_fragmentShader = 0;
AllocResources();
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// no states needed since we do it in the shader
glBindVertexArray(0);
}
R3DScrollFog::~R3DScrollFog()
{
DeallocResources();
if (m_vao) {
glDeleteVertexArrays(1, &m_vao);
m_vao = 0;
}
}
void R3DScrollFog::DrawScrollFog(float rgba[4], float attenuation, float ambient, float *spotRGB, float *spotEllipse)
{
//=======
Mat4 mvp;
//=======
// yeah this would have been much easier with immediate mode and fixed function .. >_<
// some ogl states
glDepthMask (GL_FALSE); // disable z writes
glDisable (GL_DEPTH_TEST); // disable depth testing
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_vbo.Bind (true);
glBindVertexArray (m_vao);
glUseProgram (m_shaderProgram);
glUniform4f (m_locFogColour, rgba[0], rgba[1], rgba[2], rgba[3]);
glUniform4fv (m_locFogColour, 1, rgba);
glUniform1f (m_locFogAttenuation, attenuation);
glUniform1f (m_locFogAmbient, ambient);
glUniform3f (m_locSpotFogColor, spotRGB[0], spotRGB[1], spotRGB[2]);
glUniform4f (m_locSpotEllipse, spotEllipse[0], spotEllipse[1], spotEllipse[2], spotEllipse[3]);
glUniformMatrix4fv (m_locMVP, 1, GL_FALSE, mvp);
glUniform3fv (m_locSpotFogColor, 1, spotRGB);
glUniform4fv (m_locSpotEllipse, 1, spotEllipse);
glEnableVertexAttribArray (0);
glVertexAttribPointer (m_locInVertex, 3, GL_FLOAT, GL_FALSE, sizeof(SFVertex), 0);
glDrawArrays (GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray (0);
glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
glUseProgram (0);
m_vbo.Bind (false);
glBindVertexArray (0);
glDisable (GL_BLEND);
glDepthMask (GL_TRUE);
@ -128,16 +124,11 @@ void R3DScrollFog::AllocResources()
{
bool success = LoadShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, m_config["VertexShaderFog"].ValueAs<std::string>(), m_config["FragmentShaderFog"].ValueAs<std::string>(), vertexShaderFog, fragmentShaderFog);
m_locMVP = glGetUniformLocation(m_shaderProgram, "mvp");
m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour");
m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation");
m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient");
m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor");
m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse");
m_locInVertex = glGetAttribLocation(m_shaderProgram, "inVertex");
m_vbo.Create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, sizeof(SFTriangle) * (2), m_triangles);
}
void R3DScrollFog::DeallocResources()
@ -149,8 +140,6 @@ void R3DScrollFog::DeallocResources()
m_shaderProgram = 0;
m_vertexShader = 0;
m_fragmentShader = 0;
m_vbo.Destroy();
}
}

View file

@ -2,7 +2,7 @@
#define _R3DSCROLLFOG_H_
#include "Util/NewConfig.h"
#include "VBO.h"
#include <GL/glew.h>
namespace New3D {
@ -22,41 +22,17 @@ private:
const Util::Config::Node &m_config;
struct SFVertex
{
void Set(float x, float y, float z) {
v[0] = x;
v[1] = y;
v[2] = z;
}
float v[3];
};
struct SFTriangle
{
SFVertex p1;
SFVertex p2;
SFVertex p3;
};
SFTriangle m_triangles[2];
GLuint m_shaderProgram;
GLuint m_vertexShader;
GLuint m_fragmentShader;
GLint m_locFogColour;
GLint m_locMVP;
GLint m_locFogAttenuation;
GLint m_locFogAmbient;
GLint m_locSpotFogColor;
GLint m_locSpotEllipse;
// vertex attrib locs
GLint m_locInVertex;
VBO m_vbo;
GLuint m_vao;
};
}

View file

@ -34,9 +34,18 @@ void R3DShader::Start()
m_shininess = 0;
m_specularValue = 0;
m_microTexScale = 0;
m_microTexID = -1;
m_baseTexSize[0] = 0;
m_baseTexSize[1] = 0;
m_baseTexInfo[0] = -1;
m_baseTexInfo[1] = -1;
m_baseTexInfo[2] = -1;
m_baseTexInfo[3] = -1;
m_baseTexType = -1;
m_transX = -1;
m_transY = -1;
m_transPage = -1;
m_texWrapMode[0] = 0;
m_texWrapMode[1] = 0;
@ -53,10 +62,15 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
const char* gShader = "";
const char* fShader = fragmentShaderR3D;
std::string fragmentShaderCombined;
if (quads) {
vShader = vertexShaderR3DQuads;
gShader = geometryShaderR3DQuads;
fShader = fragmentShaderR3DQuads;
fragmentShaderCombined += fragmentShaderR3DQuads1;
fragmentShaderCombined += fragmentShaderR3DQuads2;
fShader = fragmentShaderCombined.c_str();
}
m_shaderProgram = glCreateProgram();
@ -87,13 +101,14 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
PrintProgramResult(m_shaderProgram);
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_locMicroTexID = glGetUniformLocation(m_shaderProgram, "microTextureID");
m_locBaseTexInfo = glGetUniformLocation(m_shaderProgram, "baseTexInfo");
m_locBaseTexType = glGetUniformLocation(m_shaderProgram, "baseTexType");
m_locTextureInverted = glGetUniformLocation(m_shaderProgram, "textureInverted");
m_locTexWrapMode = glGetUniformLocation(m_shaderProgram, "textureWrapMode");
@ -129,12 +144,37 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
return true;
}
void R3DShader::UnloadShader()
{
// make sure no shader is bound
glUseProgram(0);
if (m_vertexShader) {
glDeleteShader(m_vertexShader);
m_vertexShader = 0;
}
if (m_geoShader) {
glDeleteShader(m_geoShader);
m_geoShader = 0;
}
if (m_fragmentShader) {
glDeleteShader(m_fragmentShader);
m_fragmentShader = 0;
}
if (m_shaderProgram) {
glDeleteProgram(m_shaderProgram);
m_shaderProgram = 0;
}
}
GLint R3DShader::GetVertexAttribPos(const std::string& attrib)
{
if (m_vertexLocCache.count(attrib)==0) {
auto pos = glGetAttribLocation(m_shaderProgram, attrib.c_str());
m_vertexLocCache[attrib] = pos;
return pos;
}
return m_vertexLocCache[attrib];
@ -160,7 +200,6 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
if (m_dirtyMesh) {
glUniform1i(m_locTexture1, 0);
glUniform1i(m_locTexture2, 1);
}
if (m_dirtyMesh || m->textured != m_textured1) {
@ -178,10 +217,27 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
m_microTexScale = m->microTextureScale;
}
if (m_dirtyMesh || (m_baseTexSize[0] != m->width || m_baseTexSize[1] != m->height)) {
m_baseTexSize[0] = (float)m->width;
m_baseTexSize[1] = (float)m->height;
glUniform2fv(m_locBaseTexSize, 1, m_baseTexSize);
if (m_dirtyMesh || m->microTextureID != m_microTexID) {
glUniform1i(m_locMicroTexID, m->microTextureID);
m_microTexID = m->microTextureID;
}
if (m_dirtyMesh || (m_baseTexInfo[0] != m->x || m_baseTexInfo[1] != m->y) || m_baseTexInfo[2] != m->width || m_baseTexInfo[3] != m->height) {
m_baseTexInfo[0] = m->x;
m_baseTexInfo[1] = m->y;
m_baseTexInfo[2] = m->width;
m_baseTexInfo[3] = m->height;
int translatedX, translatedY;
CalcTexOffset(m_transX, m_transY, m_transPage, m->x, m->y, translatedX, translatedY); // need to apply model translation
glUniform4i(m_locBaseTexInfo, translatedX, translatedY, m->width, m->height);
}
if (m_dirtyMesh || m_baseTexType != m->format) {
m_baseTexType = m->format;
glUniform1i(m_locBaseTexType, m_baseTexType);
}
if (m_dirtyMesh || m->inverted != m_textureInverted) {
@ -282,6 +338,13 @@ void R3DShader::SetModelStates(const Model* model)
m_modelScale = model->scale;
}
m_transX = model->textureOffsetX;
m_transY = model->textureOffsetY;
m_transPage = model->page;
// reset texture values
for (auto& i : m_baseTexInfo) { i = -1; }
glUniformMatrix4fv(m_locModelMat, 1, GL_FALSE, model->modelMat);
m_dirtyModel = false;
@ -332,4 +395,22 @@ void R3DShader::PrintProgramResult(GLuint program)
printf("%s\n", infoLog.data());
}
}
void R3DShader::CalcTexOffset(int offX, int offY, int page, int x, int y, int& newX, int& newY)
{
newX = (x + offX) & 2047; // wrap around 2048, shouldn't be required
int oldPage = y / 1024;
y -= (oldPage * 1024); // remove page from tex y
// calc newY with wrap around, wraps around in the same sheet, not into another memory sheet
newY = (y + offY) & 1023;
// add page to Y
newY += ((oldPage + page) & 1) * 1024; // max page 0-1
}
} // New3D

View file

@ -4,7 +4,7 @@
#include <GL/glew.h>
#include "Util/NewConfig.h"
#include "Model.h"
#include <unordered_map>
#include <map>
#include <string>
namespace New3D {
@ -15,6 +15,7 @@ public:
R3DShader(const Util::Config::Node &config);
bool LoadShader (const char* vertexShader = nullptr, const char* fragmentShader = nullptr);
void UnloadShader ();
void SetMeshUniforms (const Mesh* m);
void SetModelStates (const Model* model);
void SetViewportUniforms (const Viewport *vp);
@ -28,6 +29,8 @@ private:
void PrintShaderResult(GLuint shader);
void PrintProgramResult(GLuint program);
void CalcTexOffset(int offX, int offY, int page, int x, int y, int& newX, int& newY);
// run-time config
const Util::Config::Node &m_config;
@ -39,13 +42,14 @@ private:
// mesh uniform locations
GLint m_locTexture1;
GLint m_locTexture2;
GLint m_locTexture1Enabled;
GLint m_locTexture2Enabled;
GLint m_locTextureAlpha;
GLint m_locAlphaTest;
GLint m_locMicroTexScale;
GLint m_locBaseTexSize;
GLint m_locMicroTexID;
GLint m_locBaseTexInfo;
GLint m_locBaseTexType;
GLint m_locTextureInverted;
GLint m_locTexWrapMode;
GLint m_locTranslatorMap;
@ -65,12 +69,17 @@ private:
bool m_layered;
float m_microTexScale;
float m_baseTexSize[2];
int m_microTexID;
int m_baseTexInfo[4];
int m_baseTexType;
int m_texWrapMode[2];
bool m_textureInverted;
// cached model values
float m_modelScale;
int m_transX;
int m_transY;
int m_transPage;
// are our cache values dirty
bool m_dirtyMesh;
@ -109,11 +118,11 @@ private:
GLint m_locDiscardAlpha;
// vertex attribute position cache
std::unordered_map<std::string, GLint> m_vertexLocCache;
std::map<std::string, GLint> m_vertexLocCache;
};
} // New3D
#endif
#endif

View file

@ -168,18 +168,19 @@ void main(void)
)glsl";
static const char *fragmentShaderR3DQuads = R"glsl(
static const char *fragmentShaderR3DQuads1 = R"glsl(
#version 450 core
uniform sampler2D tex1; // base tex
uniform sampler2D tex2; // micro tex (optional)
uniform usampler2D tex1; // entire texture sheet
// texturing
uniform bool textureEnabled;
uniform bool microTexture;
uniform float microTextureScale;
uniform vec2 baseTexSize;
uniform int microTextureID;
uniform ivec4 baseTexInfo; // x/y are x,y positions in the texture sheet. z/w are with and height
uniform int baseTexType;
uniform bool textureInverted;
uniform bool textureAlpha;
uniform bool alphaTest;
@ -207,7 +208,7 @@ uniform float fogAmbient;
uniform bool fixedShading;
uniform int hardwareStep;
// test
// matrices (shared with vertex shader)
uniform mat4 projMat;
//interpolated inputs from geometry shader
@ -340,6 +341,140 @@ void QuadraticInterpolation()
gl_FragDepth = depth * 0.5 + 0.5;
}
vec4 ExtractColour(int type, uint value)
{
vec4 c = vec4(0.0);
if(type==0) { // T1RGB5
c.r = float((value >> 10) & 0x1F) / 31.0;
c.g = float((value >> 5 ) & 0x1F) / 31.0;
c.b = float((value ) & 0x1F) / 31.0;
c.a = 1.0 - float((value >> 15) & 0x1);
}
else if(type==1) { // Interleaved A4L4 (low byte)
c.rgb = vec3(float(value&0xF) / 15.0);
c.a = float((value >> 4) & 0xF) / 15.0;
}
else if(type==2) {
c.a = float(value&0xF) / 15.0;
c.rgb = vec3(float((value >> 4) & 0xF) / 15.0);
}
else if(type==3) {
c.rgb = vec3(float((value>>8)&0xF) / 15.0);
c.a = float((value >> 12) & 0xF) / 15.0;
}
else if(type==4) {
c.a = float((value>>8)&0xF) / 15.0;
c.rgb = vec3(float((value >> 12) & 0xF) / 15.0);
}
else if(type==5) {
c = vec4(float(value&0xFF) / 255.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==6) {
c = vec4(float((value>>8)&0xFF) / 255.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==7) { // RGBA4
c.r = float((value>>12)&0xF) / 15.0;
c.g = float((value>> 8)&0xF) / 15.0;
c.b = float((value>> 4)&0xF) / 15.0;
c.a = float((value>> 0)&0xF) / 15.0;
}
else if(type==8) { // low byte, low nibble
c = vec4(float(value&0xF) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==9) { // low byte, high nibble
c = vec4(float((value>>4)&0xF) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==10) { // high byte, low nibble
c = vec4(float((value>>8)&0xF) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==11) { // high byte, high nibble
c = vec4(float((value>>12)&0xF) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
return c;
}
ivec2 GetTexturePosition(int level, ivec2 pos)
{
const int mipXBase[] = { 0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047 };
const int mipYBase[] = { 0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023 };
int mipDivisor = 1 << level;
int page = pos.y / 1024;
pos.y -= (page * 1024); // remove page from tex y
ivec2 retPos;
retPos.x = mipXBase[level] + (pos.x / mipDivisor);
retPos.y = mipYBase[level] + (pos.y / mipDivisor);
retPos.y += (page * 1024); // add page back to tex y
return retPos;
}
ivec2 GetTextureSize(int level, ivec2 size)
{
int mipDivisor = 1 << level;
return size / mipDivisor;
}
ivec2 GetMicroTexturePos(int id)
{
int xCoords[8] = { 0, 0, 128, 128, 0, 0, 128, 128 };
int yCoords[8] = { 0, 128, 0, 128, 256, 384, 256, 384 };
return ivec2(xCoords[id],yCoords[id]);
}
int GetPage(int yCoord)
{
return yCoord / 1024;
}
int GetNextPage(int yCoord)
{
return (GetPage(yCoord) + 1) & 1;
}
int GetNextPageOffset(int yCoord)
{
return GetNextPage(yCoord) * 1024;
}
// wrapping tex coords would be super easy but we combined tex sheets so have to handle wrap around between sheets
// hardware testing would be useful because i don't know exactly what happens if you try to read outside the texture sheet
// wrap around is a good guess
ivec2 WrapTexCoords(ivec2 pos, ivec2 coordinate)
{
ivec2 newCoord;
newCoord.x = coordinate.x & 2047;
newCoord.y = coordinate.y;
int page = GetPage(pos.y);
newCoord.y -= (page * 1024); // remove page
newCoord.y &= 1023; // wrap around in the same sheet
newCoord.y += (page * 1024); // add page back
return newCoord;
}
float mip_map_level(in vec2 texture_coordinate) // in texel units
{
vec2 dx_vtc = dFdx(texture_coordinate);
@ -396,16 +531,16 @@ float LinearTexLocations(int wrapMode, float size, float u, out float u0, out fl
}
}
vec4 texBiLinear(sampler2D texSampler, float level, ivec2 wrapMode, vec2 texSize, vec2 texCoord)
vec4 texBiLinear(usampler2D texSampler, ivec2 wrapMode, vec2 texSize, ivec2 texPos, vec2 texCoord)
{
float tx[2], ty[2];
float a = LinearTexLocations(wrapMode.s, texSize.x, texCoord.x, tx[0], tx[1]);
float b = LinearTexLocations(wrapMode.t, texSize.y, texCoord.y, ty[0], ty[1]);
vec4 p0q0 = textureLod(texSampler, vec2(tx[0],ty[0]), level);
vec4 p1q0 = textureLod(texSampler, vec2(tx[1],ty[0]), level);
vec4 p0q1 = textureLod(texSampler, vec2(tx[0],ty[1]), level);
vec4 p1q1 = textureLod(texSampler, vec2(tx[1],ty[1]), level);
vec4 p0q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[0]) * texSize + texPos)), 0).r);
vec4 p1q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[0]) * texSize + texPos)), 0).r);
vec4 p0q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[1]) * texSize + texPos)), 0).r);
vec4 p1q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[1]) * texSize + texPos)), 0).r);
if(alphaTest) {
if(p0q0.a > p1q0.a) { p1q0.rgb = p0q0.rgb; }
@ -428,47 +563,58 @@ vec4 texBiLinear(sampler2D texSampler, float level, ivec2 wrapMode, vec2 texSize
return mix( pInterp_q0, pInterp_q1, b ); // Interpolate in Y direction.
}
vec4 textureR3D(sampler2D texSampler, ivec2 wrapMode, vec2 texSize, vec2 texCoord)
vec4 textureR3D(usampler2D texSampler, ivec2 wrapMode, ivec2 texSize, ivec2 texPos, vec2 texCoord)
{
float numLevels = floor(log2(min(texSize.x, texSize.y))); // r3d only generates down to 1:1 for square textures, otherwise its the min dimension
float fLevel = min(mip_map_level(texCoord * texSize), numLevels);
float numLevels = floor(log2(min(float(texSize.x), float(texSize.y)))); // r3d only generates down to 1:1 for square textures, otherwise its the min dimension
float fLevel = min(mip_map_level(texCoord * vec2(texSize)), numLevels);
fLevel *= alphaTest ? 0.5 : 0.8;
if(alphaTest) fLevel *= 0.5;
else fLevel *= 0.8;
float iLevel = floor(fLevel); // value as an 'int'
int iLevel = int(fLevel);
vec2 texSize0 = texSize / exp2(iLevel);
vec2 texSize1 = texSize / exp2(iLevel+1.0);
ivec2 texPos0 = GetTexturePosition(iLevel,texPos);
ivec2 texPos1 = GetTexturePosition(iLevel+1,texPos);
vec4 texLevel0 = texBiLinear(texSampler, iLevel, wrapMode, texSize0, texCoord);
vec4 texLevel1 = texBiLinear(texSampler, iLevel+1.0, wrapMode, texSize1, texCoord);
ivec2 texSize0 = GetTextureSize(iLevel, texSize);
ivec2 texSize1 = GetTextureSize(iLevel+1, texSize);
vec4 texLevel0 = texBiLinear(texSampler, wrapMode, vec2(texSize0), texPos0, texCoord);
vec4 texLevel1 = texBiLinear(texSampler, wrapMode, vec2(texSize1), texPos1, texCoord);
return mix(texLevel0, texLevel1, fract(fLevel)); // linear blend between our mipmap levels
}
vec4 GetTextureValue()
{
vec4 tex1Data = textureR3D(tex1, textureWrapMode, baseTexSize, fsTexCoord);
vec4 tex1Data = textureR3D(tex1, textureWrapMode, ivec2(baseTexInfo.zw), ivec2(baseTexInfo.xy), fsTexCoord);
if(textureInverted) {
tex1Data.rgb = vec3(1.0) - vec3(tex1Data.rgb);
}
if (microTexture) {
vec2 scale = (baseTexSize / 128.0) * microTextureScale;
vec4 tex2Data = textureR3D( tex2, ivec2(0.0), vec2(128.0), fsTexCoord * scale);
vec2 scale = (vec2(baseTexInfo.zw) / 128.0) * microTextureScale;
ivec2 pos = GetMicroTexturePos(microTextureID);
// add page offset to microtexture position
pos.y += GetNextPageOffset(baseTexInfo.y);
vec4 tex2Data = textureR3D(tex1, ivec2(0), ivec2(128), pos, fsTexCoord * scale);
float lod = mip_map_level(fsTexCoord * scale * vec2(128.0));
float blendFactor = max(lod - 1.5, 0.0); // bias -1.5
blendFactor = min(blendFactor, 1.0); // clamp to max value 1
blendFactor = blendFactor * 0.5 + 0.5; // 0.5 - 1 range
blendFactor = (blendFactor + 1.0) / 2.0; // 0.5 - 1 range
tex1Data = mix(tex2Data, tex1Data, blendFactor);
}
if (alphaTest && (tex1Data.a < (32.0/255.0))) {
discard;
if (alphaTest) {
if (tex1Data.a < (32.0/255.0)) {
discard;
}
}
if(textureAlpha) {
@ -484,7 +630,7 @@ vec4 GetTextureValue()
}
}
if (!textureAlpha) {
if (textureAlpha == false) {
tex1Data.a = 1.0;
}
@ -526,6 +672,10 @@ float sqr_length(vec2 a)
return a.x*a.x + a.y*a.y;
}
)glsl";
static const char* fragmentShaderR3DQuads2 = R"glsl(
void main()
{
vec4 tex1Data;

View file

@ -3,7 +3,7 @@
static const char *vertexShaderR3D = R"glsl(
#version 120
#version 410 core
// uniforms
uniform float modelScale;
@ -12,20 +12,20 @@ uniform mat4 projMat;
uniform bool translatorMap;
// attributes
attribute vec4 inVertex;
attribute vec3 inNormal;
attribute vec2 inTexCoord;
attribute vec4 inColour;
attribute vec3 inFaceNormal; // used to emulate r3d culling
attribute float inFixedShade;
in vec4 inVertex;
in vec3 inNormal;
in vec2 inTexCoord;
in vec4 inColour;
in vec3 inFaceNormal; // used to emulate r3d culling
in 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;
out vec3 fsViewVertex;
out vec3 fsViewNormal; // per vertex normal vector
out vec2 fsTexCoord;
out vec4 fsColor;
out float fsDiscard; // can't have varying bool (glsl spec)
out float fsFixedShade;
vec4 GetColour(vec4 colour)
{
@ -61,17 +61,17 @@ void main(void)
static const char *fragmentShaderR3D = R"glsl(
#version 120
#extension GL_ARB_shader_texture_lod : require
#version 410 core
uniform sampler2D tex1; // base tex
uniform sampler2D tex2; // micro tex (optional)
uniform usampler2D tex1; // entire texture sheet
// texturing
uniform bool textureEnabled;
uniform bool microTexture;
uniform float microTextureScale;
uniform vec2 baseTexSize;
uniform int microTextureID;
uniform ivec4 baseTexInfo; // x/y are x,y positions in the texture sheet. z/w are with and height
uniform int baseTexType;
uniform bool textureInverted;
uniform bool textureAlpha;
uniform bool alphaTest;
@ -100,12 +100,149 @@ 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;
in vec3 fsViewVertex;
in vec3 fsViewNormal; // per vertex normal vector
in vec4 fsColor;
in vec2 fsTexCoord;
in float fsDiscard;
in float fsFixedShade;
//outputs
out vec4 outColor;
vec4 ExtractColour(int type, uint value)
{
vec4 c = vec4(0.0);
if(type==0) { // T1RGB5
c.r = float((value >> 10) & 0x1F) / 31.0;
c.g = float((value >> 5 ) & 0x1F) / 31.0;
c.b = float((value ) & 0x1F) / 31.0;
c.a = 1.0 - float((value >> 15) & 0x1);
}
else if(type==1) { // Interleaved A4L4 (low byte)
c.rgb = vec3(float(value&0xF) / 15.0);
c.a = float((value >> 4) & 0xF) / 15.0;
}
else if(type==2) {
c.a = float(value&0xF) / 15.0;
c.rgb = vec3(float((value >> 4) & 0xF) / 15.0);
}
else if(type==3) {
c.rgb = vec3(float((value>>8)&0xF) / 15.0);
c.a = float((value >> 12) & 0xF) / 15.0;
}
else if(type==4) {
c.a = float((value>>8)&0xF) / 15.0;
c.rgb = vec3(float((value >> 12) & 0xF) / 15.0);
}
else if(type==5) {
c = vec4(float(value&0xFF) / 255.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==6) {
c = vec4(float((value>>8)&0xFF) / 255.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==7) { // RGBA4
c.r = float((value>>12)&0xF) / 15.0;
c.g = float((value>> 8)&0xF) / 15.0;
c.b = float((value>> 4)&0xF) / 15.0;
c.a = float((value>> 0)&0xF) / 15.0;
}
else if(type==8) { // low byte, low nibble
c = vec4(float(value&0xF) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==9) { // low byte, high nibble
c = vec4(float((value>>4)&0xF) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==10) { // high byte, low nibble
c = vec4(float((value>>8)&0xF) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==11) { // high byte, high nibble
c = vec4(float((value>>12)&0xF) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
return c;
}
ivec2 GetTexturePosition(int level, ivec2 pos)
{
const int mipXBase[] = int[](0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047);
const int mipYBase[] = int[](0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023);
int mipDivisor = 1 << level;
int page = pos.y / 1024;
pos.y -= (page * 1024); // remove page from tex y
ivec2 retPos;
retPos.x = mipXBase[level] + (pos.x / mipDivisor);
retPos.y = mipYBase[level] + (pos.y / mipDivisor);
retPos.y += (page * 1024); // add page back to tex y
return retPos;
}
ivec2 GetTextureSize(int level, ivec2 size)
{
int mipDivisor = 1 << level;
return size / mipDivisor;
}
ivec2 GetMicroTexturePos(int id)
{
const int xCoords[8] = int[](0, 0, 128, 128, 0, 0, 128, 128);
const int yCoords[8] = int[](0, 128, 0, 128, 256, 384, 256, 384);
return ivec2(xCoords[id],yCoords[id]);
}
int GetPage(int yCoord)
{
return yCoord / 1024;
}
int GetNextPage(int yCoord)
{
return (GetPage(yCoord) + 1) & 1;
}
int GetNextPageOffset(int yCoord)
{
return GetNextPage(yCoord) * 1024;
}
// wrapping tex coords would be super easy but we combined tex sheets so have to handle wrap around between sheets
// hardware testing would be useful because i don't know exactly what happens if you try to read outside the texture sheet
// wrap around is a good guess
ivec2 WrapTexCoords(ivec2 pos, ivec2 coordinate)
{
ivec2 newCoord;
newCoord.x = coordinate.x & 2047;
newCoord.y = coordinate.y;
int page = GetPage(pos.y);
newCoord.y -= (page * 1024); // remove page
newCoord.y &= 1023; // wrap around in the same sheet
newCoord.y += (page * 1024); // add page back
return newCoord;
}
float mip_map_level(in vec2 texture_coordinate) // in texel units
{
@ -163,16 +300,16 @@ float LinearTexLocations(int wrapMode, float size, float u, out float u0, out fl
}
}
vec4 texBiLinear(sampler2D texSampler, float level, ivec2 wrapMode, vec2 texSize, vec2 texCoord)
vec4 texBiLinear(usampler2D texSampler, ivec2 wrapMode, vec2 texSize, ivec2 texPos, vec2 texCoord)
{
float tx[2], ty[2];
float a = LinearTexLocations(wrapMode.s, texSize.x, texCoord.x, tx[0], tx[1]);
float b = LinearTexLocations(wrapMode.t, texSize.y, texCoord.y, ty[0], ty[1]);
vec4 p0q0 = texture2DLod(texSampler, vec2(tx[0],ty[0]), level);
vec4 p1q0 = texture2DLod(texSampler, vec2(tx[1],ty[0]), level);
vec4 p0q1 = texture2DLod(texSampler, vec2(tx[0],ty[1]), level);
vec4 p1q1 = texture2DLod(texSampler, vec2(tx[1],ty[1]), level);
vec4 p0q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[0]) * texSize + texPos)), 0).r);
vec4 p1q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[0]) * texSize + texPos)), 0).r);
vec4 p0q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[1]) * texSize + texPos)), 0).r);
vec4 p1q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[1]) * texSize + texPos)), 0).r);
if(alphaTest) {
if(p0q0.a > p1q0.a) { p1q0.rgb = p0q0.rgb; }
@ -195,43 +332,51 @@ vec4 texBiLinear(sampler2D texSampler, float level, ivec2 wrapMode, vec2 texSize
return mix( pInterp_q0, pInterp_q1, b ); // Interpolate in Y direction.
}
vec4 textureR3D(sampler2D texSampler, ivec2 wrapMode, vec2 texSize, vec2 texCoord)
vec4 textureR3D(usampler2D texSampler, ivec2 wrapMode, ivec2 texSize, ivec2 texPos, vec2 texCoord)
{
float numLevels = floor(log2(min(texSize.x, texSize.y))); // r3d only generates down to 1:1 for square textures, otherwise its the min dimension
float fLevel = min(mip_map_level(texCoord * texSize), numLevels);
float numLevels = floor(log2(min(float(texSize.x), float(texSize.y)))); // r3d only generates down to 1:1 for square textures, otherwise its the min dimension
float fLevel = min(mip_map_level(texCoord * vec2(texSize)), numLevels);
if(alphaTest) fLevel *= 0.5;
else fLevel *= 0.8;
float iLevel = floor(fLevel); // value as an 'int'
int iLevel = int(fLevel);
vec2 texSize0 = texSize / pow(2, iLevel);
vec2 texSize1 = texSize / pow(2, iLevel+1.0);
ivec2 texPos0 = GetTexturePosition(iLevel,texPos);
ivec2 texPos1 = GetTexturePosition(iLevel+1,texPos);
vec4 texLevel0 = texBiLinear(texSampler, iLevel, wrapMode, texSize0, texCoord);
vec4 texLevel1 = texBiLinear(texSampler, iLevel+1.0, wrapMode, texSize1, texCoord);
ivec2 texSize0 = GetTextureSize(iLevel, texSize);
ivec2 texSize1 = GetTextureSize(iLevel+1, texSize);
vec4 texLevel0 = texBiLinear(texSampler, wrapMode, vec2(texSize0), texPos0, texCoord);
vec4 texLevel1 = texBiLinear(texSampler, wrapMode, vec2(texSize1), texPos1, texCoord);
return mix(texLevel0, texLevel1, fract(fLevel)); // linear blend between our mipmap levels
}
vec4 GetTextureValue()
{
vec4 tex1Data = textureR3D(tex1, textureWrapMode, baseTexSize, fsTexCoord);
vec4 tex1Data = textureR3D(tex1, textureWrapMode, ivec2(baseTexInfo.zw), ivec2(baseTexInfo.xy), fsTexCoord);
if(textureInverted) {
tex1Data.rgb = vec3(1.0) - vec3(tex1Data.rgb);
}
if (microTexture) {
vec2 scale = (baseTexSize / 128.0) * microTextureScale;
vec4 tex2Data = textureR3D( tex2, ivec2(0), vec2(128.0), fsTexCoord * scale);
float lod = mip_map_level(fsTexCoord * scale * vec2(128.0));
float blendFactor = max(lod - 1.5, 0.0); // bias -1.5
blendFactor = min(blendFactor, 1.0); // clamp to max value 1
blendFactor = (blendFactor + 1.0) / 2.0; // 0.5 - 1 range
vec2 scale = (vec2(baseTexInfo.zw) / 128.0) * microTextureScale;
ivec2 pos = GetMicroTexturePos(microTextureID);
// add page offset to microtexture position
pos.y += GetNextPageOffset(baseTexInfo.y);
vec4 tex2Data = textureR3D(tex1, ivec2(0), ivec2(128), pos, fsTexCoord * scale);
float lod = mip_map_level(fsTexCoord * scale * vec2(128.0));
float blendFactor = max(lod - 1.5, 0.0); // bias -1.5
blendFactor = min(blendFactor, 1.0); // clamp to max value 1
blendFactor = (blendFactor + 1.0) / 2.0; // 0.5 - 1 range
tex1Data = mix(tex2Data, tex1Data, blendFactor);
}
@ -421,7 +566,7 @@ void main()
// Fog & spotlight applied
finalData.rgb = mix(finalData.rgb, fogData.rgb + lSpotFogColor, fogData.a);
gl_FragColor = finalData;
outColor = finalData;
}
)glsl";

View file

@ -1,370 +0,0 @@
#include "Texture.h"
#include <stdio.h>
#include <math.h>
#include <algorithm>
namespace New3D {
Texture::Texture()
{
Reset();
}
Texture::~Texture()
{
DeleteTexture(); // make sure to have valid context before destroying
}
void Texture::DeleteTexture()
{
if (m_textureID) {
glDeleteTextures(1, &m_textureID);
Reset();
}
}
void Texture::Reset()
{
m_x = 0;
m_y = 0;
m_width = 0;
m_height = 0;
m_format = 0;
m_textureID = 0;
}
void Texture::BindTexture()
{
glBindTexture(GL_TEXTURE_2D, m_textureID);
}
void Texture::GetCoordinates(UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
{
uOut = (uIn*uvScale) / m_width;
vOut = (vIn*uvScale) / m_height;
}
void Texture::GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
{
uOut = (uIn*uvScale) / width;
vOut = (vIn*uvScale) / height;
}
void Texture::UploadTextureMip(int level, const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height)
{
int xi, yi, i;
int subWidth, subHeight;
GLubyte texel;
GLubyte c, a;
i = 0;
subWidth = width;
subHeight = height;
if (subWidth + x > 2048) {
subWidth = 2048 - x;
}
if (subHeight + y > 2048) {
subHeight = 2048 - y;
}
switch (format)
{
default: // Debug texture
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
scratch[i++] = 255; // R
scratch[i++] = 0; // G
scratch[i++] = 0; // B
scratch[i++] = 255; // A
}
}
break;
case 0: // T1RGB5 <- correct
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
scratch[i++] = ((src[yi * 2048 + xi] >> 10) & 0x1F) * 255 / 0x1F; // R
scratch[i++] = ((src[yi * 2048 + xi] >> 5) & 0x1F) * 255 / 0x1F; // G
scratch[i++] = ((src[yi * 2048 + xi] >> 0) & 0x1F) * 255 / 0x1F; // B
scratch[i++] = ((src[yi * 2048 + xi] & 0x8000) ? 0 : 255); // T
}
}
break;
case 1: // Interleaved A4L4 (low byte)
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
// Interpret as A4L4
texel = src[yi * 2048 + xi] & 0xFF;
c = (texel & 0xF) * 17;
a = (texel >> 4) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = a;
}
}
break;
case 2: // luminance alpha texture <- this one is correct
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] & 0xFF;
c = ((texel >> 4) & 0xF) * 17;
a = (texel & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = a;
}
}
break;
case 3: // 8-bit, A4L4 (high byte)
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
c = (texel & 0xF) * 17;
a = (texel >> 4) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = a;
}
}
break;
case 4: // 8-bit, L4A4 (high byte)
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
c = ((texel >> 4) & 0xF) * 17;
a = (texel & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = a;
}
}
break;
case 5: // 8-bit grayscale
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] & 0xFF;
scratch[i++] = texel;
scratch[i++] = texel;
scratch[i++] = texel;
scratch[i++] = (texel == 255 ? 0 : 255);
}
}
break;
case 6: // 8-bit grayscale <-- this one is correct
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
scratch[i++] = texel;
scratch[i++] = texel;
scratch[i++] = texel;
scratch[i++] = (texel == 255 ? 0 : 255);
}
}
break;
case 7: // RGBA4
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
scratch[i++] = ((src[yi * 2048 + xi] >> 12) & 0xF) * 17;// R
scratch[i++] = ((src[yi * 2048 + xi] >> 8) & 0xF) * 17; // G
scratch[i++] = ((src[yi * 2048 + xi] >> 4) & 0xF) * 17; // B
scratch[i++] = ((src[yi * 2048 + xi] >> 0) & 0xF) * 17; // A
}
}
break;
//
// 4 bit texture types - all luminance textures (no alpha), only seem to be enabled when contour is enabled ( white = contour value )
//
case 8: // low byte, low nibble
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] & 0xFF;
c = (texel & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = (c == 255 ? 0 : 255);
}
}
break;
case 9: // low byte, high nibble
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] & 0xFF;
c = ((texel >> 4) & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = (c == 255 ? 0 : 255);
}
}
break;
case 10: // high byte, low nibble
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
c = (texel & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = (c == 255 ? 0 : 255);
}
}
break;
case 11: // high byte, high nibble
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
c = ((texel >> 4) & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = (c == 255 ? 0 : 255);
}
}
break;
}
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, subWidth, subHeight, GL_RGBA, GL_UNSIGNED_BYTE, scratch);
}
UINT32 Texture::UploadTexture(const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height)
{
const int mipXBase[] = { 0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047 };
const int mipYBase[] = { 0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023 };
const int mipDivisor[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 };
if (!src || !scratch) {
return 0; // sanity checking
}
DeleteTexture(); // free any existing texture
CreateTextureObject(format, x, y, width, height);
int page = y / 1024;
y -= (page * 1024); // remove page from tex y
for (int i = 0; width > 0 && height > 0; i++) {
int xPos = mipXBase[i] + (x / mipDivisor[i]);
int yPos = mipYBase[i] + (y / mipDivisor[i]);
UploadTextureMip(i, src, scratch, format, xPos, yPos + (page * 1024), width, height);
width /= 2;
height /= 2;
}
return m_textureID;
}
void Texture::GetDetails(int& x, int&y, int& width, int& height, int& format)
{
x = m_x;
y = m_y;
width = m_width;
height = m_height;
format = m_format;
}
bool Texture::Compare(int x, int y, int width, int height, int format)
{
if (m_x == x && m_y == y && m_width == width && m_height == height && m_format == format) {
return true;
}
return false;
}
bool Texture::CheckMapPos(int ax1, int ax2, int ay1, int ay2)
{
int bx1 = m_x;
int bx2 = m_x + m_width;
int by1 = m_y;
int by2 = m_y + m_height;
if (ax1<bx2 && ax2>bx1 &&
ay1<by2 && ay2>by1) {
return true; // rectangles overlap
}
return false;
}
void Texture::CreateTextureObject(int format, int x, int y, int width, int height)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // rgba is always 4 byte aligned
glGenTextures(1, &m_textureID);
glBindTexture(GL_TEXTURE_2D, m_textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
int minD = std::min(width, height);
int count = 0;
while (minD > 1) {
minD /= 2;
count++;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count);
m_x = x;
m_y = y;
m_width = width;
m_height = height;
m_format = format;
}
} // New3D

View file

@ -1,42 +0,0 @@
#ifndef _TEXTURE_H_
#define _TEXTURE_H_
#include "Types.h"
#include <GL/glew.h>
namespace New3D {
class Texture
{
public:
Texture();
~Texture();
UINT32 UploadTexture (const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height);
void DeleteTexture ();
void BindTexture ();
void GetCoordinates (UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut);
void GetDetails (int& x, int&y, int& width, int& height, int& format);
bool Compare (int x, int y, int width, int height, int format);
bool CheckMapPos (int ax1, int ax2, int ay1, int ay2); //check to see if textures overlap
static void GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut);
private:
void CreateTextureObject(int format, int x, int y, int width, int height);
void UploadTextureMip(int level, const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height);
void Reset();
int m_x;
int m_y;
int m_width;
int m_height;
int m_format;
GLuint m_textureID;
};
} // New3D
#endif

View file

@ -1,181 +0,0 @@
#include "TextureSheet.h"
namespace New3D {
TextureSheet::TextureSheet()
{
m_temp.resize(1024 * 1024 * 4); // temporary buffer for textures
}
int TextureSheet::ToIndex(int x, int y)
{
return (y * 2048) + x;
}
std::shared_ptr<Texture> TextureSheet::BindTexture(const UINT16* src, int format, int x, int y, int width, int height)
{
//========
int index;
//========
x &= 2047;
y &= 2047;
if (width > 1024 || height > 1024) { // sanity checking
return nullptr;
}
index = ToIndex(x, y);
auto range = m_texMap.equal_range(index);
if (range.first == range.second) {
// nothing found so create a new texture
std::shared_ptr<Texture> t(new Texture());
m_texMap.insert(std::pair<int, std::shared_ptr<Texture>>(index, t));
t->UploadTexture(src, m_temp.data(), format, x, y, width, height);
return t;
}
else {
// iterate to try and find a match
for (auto it = range.first; it != range.second; ++it) {
int x2, y2, width2, height2, format2;
it->second->GetDetails(x2, y2, width2, height2, format2);
if (width == width2 && height == height2 && format == format2) {
return it->second;
}
}
// nothing found so create a new entry
std::shared_ptr<Texture> t(new Texture());
m_texMap.insert(std::pair<int, std::shared_ptr<Texture>>(index, t));
t->UploadTexture(src, m_temp.data(), format, x, y, width, height);
return t;
}
}
void TextureSheet::Release()
{
m_texMap.clear();
}
void TextureSheet::Invalidate(int x, int y, int width, int height)
{
//============
int newX;
int newY;
int newWidth;
int newHeight;
int count;
int sWidth; // sample width
int sHeight; // sample height
//============
if (width <= 512) {
newX = (x + width) - 512;
newWidth = 512;
}
else {
newX = x;
newWidth = width;
}
if (height <= 512) {
newY = (y + height) - 512;
newHeight = 512;
}
else {
newY = y;
newHeight = height;
}
CropTile(x, y, newX, newY, newWidth, newHeight);
sWidth = newWidth / 32;
sHeight = newHeight / 32;
count = sWidth * sHeight;
for (int i = 0; i < count; i++) {
int posX = newX + ((i%sWidth) * 32);
int posY = newY + ((i / sWidth) * 32);
int index = ToIndex(posX, posY);
if (posX >= x && posY >= y) { // invalidate this area of memory
m_texMap.erase(index);
}
else { // check for overlapping data tiles and invalidate as necessary
auto range = m_texMap.equal_range(index);
for (auto it = range.first; it != range.second; ++it) {
if (it->second->CheckMapPos(x, x + width, y, y + height)) {
m_texMap.erase(index);
break;
}
}
}
}
}
void TextureSheet::CropTile(int oldX, int oldY, int &newX, int &newY, int &newWidth, int &newHeight)
{
if (newX < 0) {
newWidth += newX;
newX = 0;
}
if (newY < 0) {
newHeight += newY;
newY = 0;
}
if (oldY >= 1024 && newY < 1024) { // gone into next memory page, limitation of our flat model
newHeight -= 1024 - newY;
newY = 1024;
}
}
int TextureSheet::GetTexFormat(int originalFormat, bool contour)
{
if (!contour) {
return originalFormat; // the same
}
switch (originalFormat)
{
case 1:
case 2:
case 3:
case 4:
return originalFormat + 7; // these formats are identical to 1-4, except they lose the 4 bit alpha part when contour is enabled
default:
return originalFormat;
}
}
void TextureSheet::GetMicrotexPos(int basePage, int id, int& x, int& y)
{
int xCoords[8] = { 0, 0, 128, 128, 0, 0, 128, 128 };
int yCoords[8] = { 0, 128, 0, 128, 256, 384, 256, 384 };
// i'm assuming .. the micro texture map is always on the other memory bank to the base texture
// this logic works for all our current games
// the microtextures are always on the top left of each texture sheet
basePage = (basePage + 1) & 1; // wrap around base page
x = xCoords[id];
y = yCoords[id] + (basePage * 1024);
}
} // New3D

View file

@ -1,38 +0,0 @@
#ifndef _TEXTURE_SHEET_H_
#define _TEXTURE_SHEET_H_
#include "Types.h"
#include <unordered_map>
#include <vector>
#include <memory>
#include "Texture.h"
namespace New3D {
class TextureSheet
{
public:
TextureSheet();
std::shared_ptr<Texture> BindTexture (const UINT16* src, int format, int x, int y, int width, int height);
void Invalidate (int x, int y, int width, int height); // release parts of the memory
void Release (); // release all texture objects and memory
int GetTexFormat (int originalFormat, bool contour);
void GetMicrotexPos (int basePage, int id, int& x, int& y);
private:
int ToIndex(int x, int y);
void CropTile(int oldX, int oldY, int &newX, int &newY, int &newWidth, int &newHeight);
std::unordered_multimap<int, std::shared_ptr<Texture>> m_texMap;
// the key for the above maps is the x/y position in the 2048x2048 texture
// array of 8 planes for each texture type
std::vector<UINT8> m_temp;
};
} // New3D
#endif

View file

@ -1,7 +1,5 @@
#include "VBO.h"
namespace New3D {
VBO::VBO()
{
m_id = 0;
@ -17,7 +15,7 @@ void VBO::Create(GLenum target, GLenum usage, GLsizeiptr size, const void* data)
glBufferData(target, size, data, usage); // upload data to video card
m_target = target;
m_capacity = size;
m_capacity = (int)size;
m_size = 0;
Bind(false); // unbind
@ -40,7 +38,7 @@ bool VBO::AppendData(GLsizeiptr size, const GLvoid* data)
BufferSubData(m_size, size, data);
m_size += size;
m_size += (int)size;
return true;
}
@ -80,5 +78,3 @@ int VBO::GetCapacity()
{
return m_capacity;
}
} // New3D

View file

@ -3,8 +3,6 @@
#include <GL/glew.h>
namespace New3D {
class VBO
{
public:
@ -20,12 +18,10 @@ public:
int GetCapacity ();
private:
GLuint m_id;
GLenum m_target;
int m_capacity;
int m_size;
GLuint m_id;
GLenum m_target;
int m_capacity;
int m_size;
};
} // New3D
#endif

View file

@ -495,15 +495,20 @@ std::pair<bool, bool> CRender2D::DrawTilemaps(uint32_t *pixelsBottom, uint32_t *
// Draws a surface to the screen (0 is top and 1 is bottom)
void CRender2D::DisplaySurface(int surface)
{
// Shader program
m_shader.EnableShader();
glBindVertexArray(m_vao);
// Draw the surface
glActiveTexture(GL_TEXTURE0); // texture unit 0
glBindTexture(GL_TEXTURE_2D, m_texID[surface]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 1.0f);
glEnd();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
m_shader.DisableShader();
}
// Set up viewport and OpenGL state for 2D rendering (sets up blending function but disables blending)
@ -515,9 +520,6 @@ void CRender2D::Setup2D(bool isBottom)
// Disable Z-buffering
glDisable(GL_DEPTH_TEST);
// Shader program
glUseProgram(m_shaderProgram);
// Clear everything if requested or just overscan areas for wide screen mode
if (isBottom)
{
@ -534,11 +536,6 @@ void CRender2D::Setup2D(bool isBottom)
{
glViewport(m_xOffset - m_correction, m_yOffset + m_correction, m_xPixels, m_yPixels); //Preserve aspect ratio of tile layer by constraining and centering viewport
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 1.0, 0.0, 1.0, -1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void CRender2D::BeginFrame(void)
@ -626,15 +623,6 @@ void CRender2D::AttachVRAM(const uint8_t *vramPtr)
bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes)
{
// Load shaders
if (OKAY != LoadShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, m_config["VertexShader2D"].ValueAs<std::string>(), m_config["FragmentShader2D"].ValueAs<std::string>(), s_vertexShaderSource, s_fragmentShaderSource))
return FAIL;
// Get locations of the uniforms
glUseProgram(m_shaderProgram); // bind program
m_textureMapLoc = glGetUniformLocation(m_shaderProgram, "textureMap");
glUniform1i(m_textureMapLoc, 0); // attach it to texture unit 0
// Allocate memory for layer surfaces
m_memoryPool = new(std::nothrow) uint8_t[MEMORY_POOL_SIZE];
if (NULL == m_memoryPool)
@ -654,35 +642,55 @@ bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
m_totalYPixels = totalYRes;
m_correction = (UINT32)(((yRes / 384.f) * 2) + 0.5f); // for some reason the 2d layer is 2 pixels off the 3D
DebugLog("Render2D initialized (allocated %1.1f MB)\n", float(MEMORY_POOL_SIZE) / 0x100000);
return OKAY;
}
CRender2D::CRender2D(const Util::Config::Node& config)
: m_config(config),
m_vao(0)
{
DebugLog("Built Render2D\n");
m_shader.LoadShaders(s_vertexShaderSource, s_fragmentShaderSource);
m_shader.GetUniformLocationMap("tex1");
m_shader.EnableShader();
// update uniform memory
glUniform1i(m_shader.uniformLocMap["tex1"], 0); // bind to texture unit zero
m_shader.DisableShader();
// Create textures
glActiveTexture(GL_TEXTURE0); // texture unit 0
glGenTextures(2, m_texID);
for (int i = 0; i < 2; i++)
{
glBindTexture(GL_TEXTURE_2D, m_texID[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 496, 384, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, m_texID[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 496, 384, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
DebugLog("Render2D initialized (allocated %1.1f MB)\n", float(MEMORY_POOL_SIZE) / 0x100000);
return OKAY;
}
CRender2D::CRender2D(const Util::Config::Node &config)
: m_config(config)
{
DebugLog("Built Render2D\n");
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// no states needed since we do it in the shader
glBindVertexArray(0);
}
CRender2D::~CRender2D(void)
{
DestroyShaderProgram(m_shaderProgram, m_vertexShader, m_fragmentShader);
m_shader.UnloadShaders();
glDeleteTextures(2, m_texID);
if (m_vao) {
glDeleteVertexArrays(1, &m_vao);
m_vao = 0;
}
if (m_memoryPool)
{
delete [] m_memoryPool;

View file

@ -30,6 +30,7 @@
#include <GL/glew.h>
#include "Util/NewConfig.h"
#include "New3D/GLSLShader.h"
/*
@ -195,11 +196,8 @@ private:
unsigned m_totalYPixels;
unsigned m_correction = 0;
// Shader programs and input data locations
GLuint m_shaderProgram; // shader program object
GLuint m_vertexShader; // vertex shader handle
GLuint m_fragmentShader; // fragment shader
GLuint m_textureMapLoc; // location of "textureMap" uniform
GLuint m_vao;
GLSLShader m_shader;
// PreRenderFrame() tracks which surfaces exist in current frame
std::pair<bool, bool> m_surfaces_present = std::pair<bool, bool>(false, false);

View file

@ -29,89 +29,46 @@
#define INCLUDED_SHADERS2D_H
// Vertex shader
static const char s_vertexShaderSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
"\n"
"/*\n"
" * Vertex2D.glsl\n"
" *\n"
" * Vertex shader for 2D tilemap rendering.\n"
" */\n"
" \n"
"#version 120\n"
"\n"
"void main(void)\n"
"{\n"
"\tgl_TexCoord[0] = gl_MultiTexCoord0;\n"
"\tgl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;\n"
"}\n"
};
static const char s_vertexShaderSource[] = R"glsl(
#version 410 core
// outputs
out vec2 fsTexCoord;
void main(void)
{
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
fsTexCoord = ((vertices[gl_VertexID % 4].xy + 1.0) / 2.0);
fsTexCoord.y = 1.0 - fsTexCoord.y; // flip upside down
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
// Fragment shader
static const char s_fragmentShaderSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
" \n"
"/*\n"
" * Fragment2D.glsl\n"
" *\n"
" * Fragment shader for 2D tilemap rendering.\n"
" */\n"
"\n"
"#version 120\n"
"\n"
"// Global uniforms\n"
"uniform sampler2D\ttextureMap;\t\t// 512x512 layer surface\n"
"\n"
"/*\n"
" * main():\n"
" *\n"
" * Fragment shader entry point.\n"
" */\n"
"\n"
"void main(void)\n"
"{\t\n"
"\tgl_FragColor = texture2D(textureMap, gl_TexCoord[0].st);\n"
"}\n"
};
static const char s_fragmentShaderSource[] = R"glsl(
#version 410 core
// inputs
uniform sampler2D tex1; // texture
in vec2 fsTexCoord;
// outputs
out vec4 fragColor;
void main()
{
fragColor = texture(tex1, fsTexCoord);
}
)glsl";
#endif // INCLUDED_SHADERS2D_H

View file

@ -75,6 +75,7 @@
#include "Model3/IEmulator.h"
#include "Model3/Model3.h"
#include "OSD/Audio.h"
#include "Graphics/New3D/VBO.h"
#include <iostream>
#include "Util/BMPFile.h"
@ -169,6 +170,11 @@ static bool SetGLGeometry(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *
return OKAY;
}
static void GLAPIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
printf("OGLDebug:: 0x%X: %s\n", id, message);
}
/*
* CreateGLScreen():
*
@ -181,7 +187,7 @@ static bool SetGLGeometry(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *
* NOTE: keepAspectRatio should always be true. It has not yet been tested with
* the wide screen hack.
*/
static bool CreateGLScreen(const std::string &caption, bool focusWindow, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio, bool fullScreen)
static bool CreateGLScreen(bool coreContext, bool quadRendering, const std::string &caption, bool focusWindow, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio, bool fullScreen)
{
GLenum err;
@ -205,6 +211,19 @@ static bool CreateGLScreen(const std::string &caption, bool focusWindow, unsigne
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
if (coreContext) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
if (quadRendering) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
}
else {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
}
}
// Set video mode
s_window = SDL_CreateWindow(caption.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, *xResPtr, *yResPtr, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | (fullScreen ? SDL_WINDOW_FULLSCREEN : 0));
if (nullptr == s_window)
@ -240,6 +259,26 @@ static bool CreateGLScreen(const std::string &caption, bool focusWindow, unsigne
return FAIL;
}
// print some basic GPU info
GLint profile = 0;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile);
printf("GPU info: %s ", glGetString(GL_VERSION));
if (profile & GL_CONTEXT_CORE_PROFILE_BIT) {
printf("(core profile)");
}
if (profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) {
printf("(compatability profile)");
}
printf("\n\n");
//glDebugMessageCallback(DebugCallback, NULL);
//glDebugMessageControl(GL_DONT_CARE,GL_DONT_CARE,GL_DONT_CARE, 0, 0, GL_TRUE);
//glEnable(GL_DEBUG_OUTPUT);
return SetGLGeometry(xOffsetPtr, yOffsetPtr, xResPtr, yResPtr, totalXResPtr, totalYResPtr, keepAspectRatio);
}
@ -275,7 +314,7 @@ static void PrintGLInfo(bool createScreen, bool infoLog, bool printExtensions)
unsigned xOffset, yOffset, xRes=496, yRes=384, totalXRes, totalYRes;
if (createScreen)
{
if (OKAY != CreateGLScreen("Supermodel - Querying OpenGL Information...", false, &xOffset, &yOffset, &xRes, &yRes, &totalXRes, &totalYRes, false, false))
if (OKAY != CreateGLScreen(false, false, "Supermodel - Querying OpenGL Information...", false, &xOffset, &yOffset, &xRes, &yRes, &totalXRes, &totalYRes, false, false))
{
ErrorLog("Unable to query OpenGL.\n");
return;
@ -694,31 +733,130 @@ static void LoadNVRAM(IEmulator *Model3)
Currently, only does crosshairs for light gun games.
******************************************************************************/
struct BasicDraw
{
public:
struct BasicVertex
{
BasicVertex(float x, float y, float z) : x(x), y(y), z(z) {}
BasicVertex(float x, float y) : x(x), y(y), z(0.f) {}
float x, y, z;
};
const int MaxVerts = 1024; // per draw call
void Draw(GLenum mode, const float mvpMatrix[16], const BasicVertex* vertices, int count, float r, float g, float b, float a)
{
if (count > MaxVerts) {
count = MaxVerts; // maybe we could error out somehow
}
if (!m_initialised) {
Setup();
}
m_shader.EnableShader();
// update uniform memory
glUniformMatrix4fv(m_shader.uniformLocMap["mvp"], 1, GL_FALSE, mvpMatrix);
glUniform4f(m_shader.uniformLocMap["colour"], r, g, b, a);
// update vbo mem
m_vbo.Bind(true);
m_vbo.BufferSubData(0, count * sizeof(BasicVertex), vertices);
glBindVertexArray(m_vao);
glDrawArrays(mode, 0, count);
glBindVertexArray(0);
m_shader.DisableShader();
}
private:
void Setup()
{
const char* vertexShader = R"glsl(
#version 410 core
uniform mat4 mvp;
layout(location = 0) in vec3 inVertices;
void main(void)
{
gl_Position = mvp * vec4(inVertices,1.0);
}
)glsl";
const char* fragmentShader = R"glsl(
#version 410 core
uniform vec4 colour;
out vec4 fragColour;
void main(void)
{
fragColour = colour;
}
)glsl";
m_shader.LoadShaders(vertexShader, fragmentShader);
m_shader.GetUniformLocationMap("mvp");
m_shader.GetUniformLocationMap("colour");
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(BasicVertex) * (MaxVerts));
m_vbo.Bind(true);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(BasicVertex), 0);
glBindVertexArray(0);
m_vbo.Bind(false);
m_initialised = true;
}
GLSLShader m_shader;
VBO m_vbo;
GLuint m_vao = 0;
bool m_initialised = false;
} basicDraw;
static void GunToViewCoords(float *x, float *y)
{
*x = (*x-150.0f)/(651.0f-150.0f); // Scale [150,651] -> [0.0,1.0]
*y = (*y-80.0f)/(465.0f-80.0f); // Scale [80,465] -> [0.0,1.0]
}
static void DrawCrosshair(float x, float y, float r, float g, float b)
static void DrawCrosshair(const float* matrix, float x, float y, float r, float g, float b)
{
float base = 0.01f, height = 0.02f; // geometric parameters of each triangle
float dist = 0.004f; // distance of triangle tip from center
float a = (float)xRes/(float)yRes; // aspect ratio (to square the crosshair)
glColor3f(r, g, b);
glVertex2f(x, y+dist); // bottom triangle
glVertex2f(x+base/2.0f, y+(dist+height)*a);
glVertex2f(x-base/2.0f, y+(dist+height)*a);
glVertex2f(x, y-dist); // top triangle
glVertex2f(x-base/2.0f, y-(dist+height)*a);
glVertex2f(x+base/2.0f, y-(dist+height)*a);
glVertex2f(x-dist, y); // left triangle
glVertex2f(x-dist-height, y+(base/2.0f)*a);
glVertex2f(x-dist-height, y-(base/2.0f)*a);
glVertex2f(x+dist, y); // right triangle
glVertex2f(x+dist+height, y-(base/2.0f)*a);
glVertex2f(x+dist+height, y+(base/2.0f)*a);
std::vector<BasicDraw::BasicVertex> verts;
verts.emplace_back(x, y+dist); // bottom triangle
verts.emplace_back(x+base/2.0f, y+(dist+height)*a);
verts.emplace_back(x-base/2.0f, y+(dist+height)*a);
verts.emplace_back(x, y-dist); // top triangle
verts.emplace_back(x-base/2.0f, y-(dist+height)*a);
verts.emplace_back(x+base/2.0f, y-(dist+height)*a);
verts.emplace_back(x-dist, y); // left triangle
verts.emplace_back(x-dist-height, y+(base/2.0f)*a);
verts.emplace_back(x-dist-height, y-(base/2.0f)*a);
verts.emplace_back(x+dist, y); // right triangle
verts.emplace_back(x+dist+height, y-(base/2.0f)*a);
verts.emplace_back(x+dist+height, y+(base/2.0f)*a);
basicDraw.Draw(GL_TRIANGLES, matrix, verts.data(), (int)verts.size(), r, g, b, 1.0f);
}
/*
@ -740,7 +878,6 @@ static void PrintGLError(GLenum error)
*/
static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned crosshairs)
{
bool offscreenTrigger[2];
float x[2], y[2];
@ -762,6 +899,9 @@ static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned c
glDisable(GL_DEPTH_TEST); // no Z-buffering needed
glDisable(GL_LIGHTING);
New3D::Mat4 m;
m.Ortho(0.0, 1.0, 1.0, 0.0, -1.0f, 1.0f);
// Convert gun coordinates to viewspace coordinates
if (currentInputs & Game::INPUT_ANALOG_GUN1)
{
@ -790,12 +930,10 @@ static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned c
offscreenTrigger[1] = (Inputs->trigger[1]->offscreenValue) > 0;
}
// Draw visible crosshairs
glBegin(GL_TRIANGLES);
if ((crosshairs & 1) && !offscreenTrigger[0]) // Player 1
DrawCrosshair(x[0], y[0], 1.0f, 0.0f, 0.0f);
DrawCrosshair(m,x[0], y[0], 1.0f, 0.0f, 0.0f);
if ((crosshairs & 2) && !offscreenTrigger[1]) // Player 2
DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f);
glEnd();
DrawCrosshair(m,x[1], y[1], 0.0f, 1.0f, 0.0f);
//PrintGLError(glGetError());
}
@ -1901,7 +2039,7 @@ int main(int argc, char **argv)
// Create a window
xRes = 496;
yRes = 384;
if (OKAY != CreateGLScreen("Supermodel", false, &xOffset, &yOffset, &xRes, &yRes, &totalXRes, &totalYRes, false, false))
if (OKAY != CreateGLScreen(s_runtime_config["New3DEngine"].ValueAs<bool>(), s_runtime_config["QuadRendering"].ValueAs<bool>(),"Supermodel", false, &xOffset, &yOffset, &xRes, &yRes, &totalXRes, &totalYRes, false, false))
{
exitCode = 1;
goto Exit;

View file

@ -311,8 +311,6 @@ xcopy /D /Y "$(ProjectDir)..\Config\*" "$(TargetDir)Config"</Command>
<ClCompile Include="..\Src\Graphics\New3D\R3DFrameBuffers.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\R3DScrollFog.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\R3DShader.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\Texture.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\TextureSheet.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\VBO.cpp" />
<ClCompile Include="..\Src\Graphics\New3D\Vec.cpp" />
<ClCompile Include="..\Src\Graphics\Render2D.cpp" />
@ -487,8 +485,6 @@ xcopy /D /Y "$(ProjectDir)..\Config\*" "$(TargetDir)Config"</Command>
<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" />
<ClInclude Include="..\Src\Graphics\New3D\Vec.h" />
<ClInclude Include="..\Src\Graphics\Render2D.h" />

View file

@ -377,12 +377,6 @@
<ClCompile Include="..\Src\Graphics\New3D\R3DShader.cpp">
<Filter>Source Files\Graphics\New</Filter>
</ClCompile>
<ClCompile Include="..\Src\Graphics\New3D\Texture.cpp">
<Filter>Source Files\Graphics\New</Filter>
</ClCompile>
<ClCompile Include="..\Src\Graphics\New3D\TextureSheet.cpp">
<Filter>Source Files\Graphics\New</Filter>
</ClCompile>
<ClCompile Include="..\Src\Graphics\New3D\VBO.cpp">
<Filter>Source Files\Graphics\New</Filter>
</ClCompile>
@ -835,12 +829,6 @@
<ClInclude Include="..\Src\Graphics\New3D\R3DShaderTriangles.h">
<Filter>Header Files\Graphics\New</Filter>
</ClInclude>
<ClInclude Include="..\Src\Graphics\New3D\Texture.h">
<Filter>Header Files\Graphics\New</Filter>
</ClInclude>
<ClInclude Include="..\Src\Graphics\New3D\TextureSheet.h">
<Filter>Header Files\Graphics\New</Filter>
</ClInclude>
<ClInclude Include="..\Src\Graphics\New3D\VBO.h">
<Filter>Header Files\Graphics\New</Filter>
</ClInclude>