mirror of
https://github.com/blawar/GLideN64.git
synced 2024-07-04 10:03:36 +00:00
1389 lines
41 KiB
C++
1389 lines
41 KiB
C++
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <time.h> /* time_t, struct tm, difftime, time, mktime */
|
|
|
|
//// paulscode, added for SDL linkage:
|
|
#if defined(GLES2)
|
|
#include "ae_bridge.h"
|
|
#endif // GLES2
|
|
////
|
|
|
|
#include "Types.h"
|
|
#include "GLideN64.h"
|
|
#include "OpenGL.h"
|
|
#include "RDP.h"
|
|
#include "RSP.h"
|
|
#include "N64.h"
|
|
#include "gSP.h"
|
|
#include "gDP.h"
|
|
#include "Textures.h"
|
|
#include "Combiner.h"
|
|
#include "GLSLCombiner.h"
|
|
#include "FrameBuffer.h"
|
|
#include "DepthBuffer.h"
|
|
#include "GLideNHQ/Ext_TxFilter.h"
|
|
#include "VI.h"
|
|
#include "Config.h"
|
|
#include "Log.h"
|
|
#include "TextDrawer.h"
|
|
#include "PostProcessor.h"
|
|
|
|
using namespace std;
|
|
|
|
bool checkFBO() {
|
|
GLenum e = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
switch (e) {
|
|
// case GL_FRAMEBUFFER_UNDEFINED:
|
|
// printf("FBO Undefined\n");
|
|
// break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT :
|
|
LOG(LOG_ERROR, "[gles2GlideN64]: FBO Incomplete Attachment\n");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT :
|
|
LOG(LOG_ERROR, "[gles2GlideN64]: FBO Missing Attachment\n");
|
|
break;
|
|
// case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER :
|
|
// printf("FBO Incomplete Draw Buffer\n");
|
|
// break;
|
|
case GL_FRAMEBUFFER_UNSUPPORTED :
|
|
LOG(LOG_ERROR, "[gles2GlideN64]: FBO Unsupported\n");
|
|
break;
|
|
case GL_FRAMEBUFFER_COMPLETE:
|
|
LOG(LOG_VERBOSE, "[gles2GlideN64]: FBO OK\n");
|
|
break;
|
|
// case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
|
|
// printf("framebuffer FRAMEBUFFER_DIMENSIONS\n");
|
|
// break;
|
|
// case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
|
|
// printf("framebuffer INCOMPLETE_FORMATS\n");
|
|
// break;
|
|
default:
|
|
LOG(LOG_ERROR, "[gles2GlideN64]: FBO Problem?\n");
|
|
}
|
|
return e == GL_FRAMEBUFFER_COMPLETE;
|
|
}
|
|
|
|
const char* GLErrorString(GLenum errorCode)
|
|
{
|
|
static const struct {
|
|
GLenum code;
|
|
const char *string;
|
|
} errors[]=
|
|
{
|
|
/* GL */
|
|
{GL_NO_ERROR, "no error"},
|
|
{GL_INVALID_ENUM, "invalid enumerant"},
|
|
{GL_INVALID_VALUE, "invalid value"},
|
|
{GL_INVALID_OPERATION, "invalid operation"},
|
|
{GL_STACK_OVERFLOW, "stack overflow"},
|
|
{GL_STACK_UNDERFLOW, "stack underflow"},
|
|
{GL_OUT_OF_MEMORY, "out of memory"},
|
|
|
|
{0, NULL }
|
|
};
|
|
|
|
int i;
|
|
|
|
for (i=0; errors[i].string; i++)
|
|
{
|
|
if (errors[i].code == errorCode)
|
|
{
|
|
return errors[i].string;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool isGLError()
|
|
{
|
|
GLenum errCode;
|
|
const char* errString;
|
|
|
|
if ((errCode = glGetError()) != GL_NO_ERROR) {
|
|
errString = GLErrorString(errCode);
|
|
if (errString != NULL)
|
|
fprintf (stderr, "OpenGL Error: %s\n", errString);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void OGLVideo::start()
|
|
{
|
|
_start(); // TODO: process initialization error
|
|
initGLFunctions();
|
|
m_render._initData();
|
|
}
|
|
|
|
void OGLVideo::stop()
|
|
{
|
|
m_render._destroyData();
|
|
_stop();
|
|
}
|
|
|
|
void OGLVideo::restart()
|
|
{
|
|
m_bResizeWindow = true;
|
|
}
|
|
|
|
void OGLVideo::swapBuffers()
|
|
{
|
|
_swapBuffers();
|
|
gDP.otherMode.l = 0;
|
|
gDPSetTextureLUT(G_TT_NONE);
|
|
++RSP.DList;
|
|
}
|
|
|
|
void OGLVideo::setCaptureScreen(const char * const _strDirectory)
|
|
{
|
|
::mbstowcs(m_strScreenDirectory, _strDirectory, PLUGIN_PATH_SIZE-1);
|
|
m_bCaptureScreen = true;
|
|
}
|
|
|
|
void OGLVideo::saveScreenshot()
|
|
{
|
|
if (!m_bCaptureScreen)
|
|
return;
|
|
_saveScreenshot();
|
|
m_bCaptureScreen = false;
|
|
}
|
|
|
|
bool OGLVideo::changeWindow()
|
|
{
|
|
if (!m_bToggleFullscreen)
|
|
return false;
|
|
m_render._destroyData();
|
|
_changeWindow();
|
|
updateScale();
|
|
m_render._initData();
|
|
m_bToggleFullscreen = false;
|
|
return true;
|
|
}
|
|
|
|
void OGLVideo::setWindowSize(u32 _width, u32 _height)
|
|
{
|
|
if (m_width != _width || m_height != _height) {
|
|
m_resizeWidth = _width;
|
|
m_resizeHeight = _height;
|
|
m_bResizeWindow = true;
|
|
}
|
|
}
|
|
|
|
bool OGLVideo::resizeWindow()
|
|
{
|
|
if (!m_bResizeWindow)
|
|
return false;
|
|
m_render._destroyData();
|
|
if (!_resizeWindow())
|
|
_start();
|
|
m_render._initData();
|
|
m_bResizeWindow = false;
|
|
return true;
|
|
}
|
|
|
|
void OGLVideo::updateScale()
|
|
{
|
|
if (VI.width == 0 || VI.height == 0)
|
|
return;
|
|
m_scaleX = m_width / (float)VI.width;
|
|
m_scaleY = m_height / (float)VI.height;
|
|
}
|
|
|
|
void OGLVideo::_setBufferSize()
|
|
{
|
|
m_bAdjustScreen = false;
|
|
if (config.frameBufferEmulation.enable) {
|
|
switch (config.frameBufferEmulation.aspect) {
|
|
case Config::aStretch: // stretch
|
|
m_width = m_screenWidth;
|
|
m_height = m_screenHeight;
|
|
break;
|
|
case Config::a43: // force 4/3
|
|
if (m_screenWidth * 3 / 4 > m_screenHeight) {
|
|
m_height = m_screenHeight;
|
|
m_width = m_screenHeight * 4 / 3;
|
|
} else if (m_screenHeight * 4 / 3 > m_screenWidth) {
|
|
m_width = m_screenWidth;
|
|
m_height = m_screenWidth * 3 / 4;
|
|
} else {
|
|
m_width = m_screenWidth;
|
|
m_height = m_screenHeight;
|
|
}
|
|
break;
|
|
case Config::a169: // force 16/9
|
|
if (m_screenWidth * 9 / 16 > m_screenHeight) {
|
|
m_height = m_screenHeight;
|
|
m_width = m_screenHeight * 16 / 9;
|
|
} else if (m_screenHeight * 16 / 9 > m_screenWidth) {
|
|
m_width = m_screenWidth;
|
|
m_height = m_screenWidth * 9 / 16;
|
|
} else {
|
|
m_width = m_screenWidth;
|
|
m_height = m_screenHeight;
|
|
}
|
|
break;
|
|
case Config::aAdjust: // adjust
|
|
m_width = m_screenWidth;
|
|
m_height = m_screenHeight;
|
|
if (m_screenWidth * 3 / 4 > m_screenHeight) {
|
|
f32 width43 = m_screenHeight * 4.0f / 3.0f;
|
|
m_adjustScale = width43 / m_screenWidth;
|
|
m_bAdjustScreen = true;
|
|
}
|
|
break;
|
|
default:
|
|
assert(false && "Unknown aspect ratio");
|
|
m_width = m_screenWidth;
|
|
m_height = m_screenHeight;
|
|
}
|
|
} else {
|
|
m_width = m_screenWidth;
|
|
m_height = m_screenHeight;
|
|
}
|
|
}
|
|
|
|
void OGLVideo::readScreen(void **_pDest, long *_pWidth, long *_pHeight )
|
|
{
|
|
*_pWidth = m_width;
|
|
*_pHeight = m_height;
|
|
|
|
*_pDest = malloc( m_height * m_width * 3 );
|
|
if (*_pDest == NULL)
|
|
return;
|
|
|
|
#ifndef GLES2
|
|
const GLenum format = GL_BGR_EXT;
|
|
glReadBuffer( GL_FRONT );
|
|
#else
|
|
const GLenum format = GL_RGB;
|
|
#endif
|
|
glReadPixels( 0, m_heightOffset, m_width, m_height, format, GL_UNSIGNED_BYTE, *_pDest );
|
|
}
|
|
|
|
void OGLVideo::readScreen2(void * _dest, int * _width, int * _height, int _front)
|
|
{
|
|
if (_width == NULL || _height == NULL)
|
|
return;
|
|
|
|
*_width = m_screenWidth;
|
|
*_height = m_screenHeight;
|
|
|
|
if (_dest == NULL)
|
|
return;
|
|
|
|
GLint oldMode;
|
|
glGetIntegerv(GL_READ_BUFFER, &oldMode);
|
|
if (_front != 0)
|
|
glReadBuffer(GL_FRONT);
|
|
else
|
|
glReadBuffer(GL_BACK);
|
|
glReadPixels(0, m_heightOffset, m_screenWidth, m_screenHeight, GL_RGB, GL_UNSIGNED_BYTE, _dest);
|
|
glReadBuffer(oldMode);
|
|
}
|
|
|
|
void OGLRender::addTriangle(int _v0, int _v1, int _v2)
|
|
{
|
|
triangles.elements[triangles.num++] = _v0;
|
|
triangles.elements[triangles.num++] = _v1;
|
|
triangles.elements[triangles.num++] = _v2;
|
|
|
|
if ((gSP.geometryMode & G_SHADE) == 0) {
|
|
// Prim shading
|
|
for (u32 i = triangles.num - 3; i < triangles.num; ++i) {
|
|
SPVertex & vtx = triangles.vertices[triangles.elements[i]];
|
|
vtx.flat_r = gDP.primColor.r;
|
|
vtx.flat_g = gDP.primColor.g;
|
|
vtx.flat_b = gDP.primColor.b;
|
|
vtx.flat_a = gDP.primColor.a;
|
|
}
|
|
} else if ((gSP.geometryMode & G_SHADING_SMOOTH) == 0) {
|
|
// Flat shading
|
|
SPVertex & vtx0 = triangles.vertices[_v0];
|
|
for (u32 i = triangles.num - 3; i < triangles.num; ++i) {
|
|
SPVertex & vtx = triangles.vertices[triangles.elements[i]];
|
|
vtx.flat_r = vtx0.r;
|
|
vtx.flat_g = vtx0.g;
|
|
vtx.flat_b = vtx0.b;
|
|
vtx.flat_a = vtx0.a;
|
|
}
|
|
}
|
|
|
|
if (gDP.otherMode.depthSource == G_ZS_PRIM) {
|
|
for (u32 i = triangles.num - 3; i < triangles.num; ++i) {
|
|
SPVertex & vtx = triangles.vertices[triangles.elements[i]];
|
|
vtx.z = gDP.primDepth.z * vtx.w;
|
|
}
|
|
}
|
|
}
|
|
|
|
void OGLRender::_setBlendMode() const
|
|
{
|
|
const u32 blendmode = gDP.otherMode.l >> 16;
|
|
// 0x7000 = CVG_X_ALPHA|ALPHA_CVG_SEL|FORCE_BL
|
|
if (gDP.otherMode.alphaCvgSel != 0 && (gDP.otherMode.l & 0x7000) != 0x7000) {
|
|
switch (blendmode) {
|
|
case 0x4055: // Mario Golf
|
|
case 0x5055: // Paper Mario intro clr_mem * a_in + clr_mem * a_mem
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_ZERO, GL_ONE);
|
|
break;
|
|
default:
|
|
glDisable(GL_BLEND);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (gDP.otherMode.forceBlender != 0 && gDP.otherMode.cycleType < G_CYC_COPY) {
|
|
glEnable( GL_BLEND );
|
|
|
|
switch (blendmode)
|
|
{
|
|
// Mace objects
|
|
case 0x0382:
|
|
// Mace special blend mode, see GLSLCombiner.cpp
|
|
case 0x0091:
|
|
// 1080 Sky
|
|
case 0x0C08:
|
|
// Used LOTS of places
|
|
case 0x0F0A:
|
|
//DK64 blue prints
|
|
case 0x0302:
|
|
// Bomberman 2 special blend mode, see GLSLCombiner.cpp
|
|
case 0xA500:
|
|
//Sin and Punishment
|
|
case 0xCB02:
|
|
// Battlezone
|
|
// clr_in * a + clr_in * (1-a)
|
|
case 0xC800:
|
|
// Conker BFD
|
|
// clr_in * a_fog + clr_fog * (1-a)
|
|
// clr_in * 0 + clr_in * 1
|
|
case 0x07C2:
|
|
case 0x00C0:
|
|
//ISS64
|
|
case 0xC302:
|
|
// Donald Duck
|
|
case 0xC702:
|
|
glBlendFunc(GL_ONE, GL_ZERO);
|
|
break;
|
|
|
|
case 0x0F1A:
|
|
if (gDP.otherMode.cycleType == G_CYC_1CYCLE)
|
|
glBlendFunc(GL_ONE, GL_ZERO);
|
|
else
|
|
glBlendFunc(GL_ZERO, GL_ONE);
|
|
break;
|
|
|
|
//Space Invaders
|
|
case 0x0448: // Add
|
|
case 0x055A:
|
|
glBlendFunc( GL_ONE, GL_ONE );
|
|
break;
|
|
|
|
case 0xc712: // Pokemon Stadium?
|
|
case 0xAF50: // LOT in Zelda: MM
|
|
case 0x0F5A: // LOT in Zelda: MM
|
|
case 0x0FA5: // Seems to be doing just blend color - maybe combiner can be used for this?
|
|
case 0x5055: // Used in Paper Mario intro, I'm not sure if this is right...
|
|
//clr_in * 0 + clr_mem * 1
|
|
glBlendFunc( GL_ZERO, GL_ONE );
|
|
break;
|
|
|
|
case 0x5F50: //clr_mem * 0 + clr_mem * (1-a)
|
|
glBlendFunc( GL_ZERO, GL_ONE_MINUS_SRC_ALPHA );
|
|
break;
|
|
|
|
case 0xF550: //clr_fog * a_fog + clr_mem * (1-a)
|
|
case 0x0150: // spiderman
|
|
case 0x0550: // bomberman 64
|
|
case 0x0D18: //clr_in * a_fog + clr_mem * (1-a)
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
|
|
case 0xC912: //40 winks, clr_in * a_fog + clr_mem * 1
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
break;
|
|
|
|
case 0x0040: // Fzero
|
|
case 0xC810: // Blends fog
|
|
case 0xC811: // Blends fog
|
|
case 0x0C18: // Standard interpolated blend
|
|
case 0x0C19: // Used for antialiasing
|
|
case 0x0050: // Standard interpolated blend
|
|
case 0x0051: // Standard interpolated blend
|
|
case 0x0055: // Used for antialiasing
|
|
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
break;
|
|
|
|
|
|
default:
|
|
LOG(LOG_VERBOSE, "Unhandled blend mode=%x", gDP.otherMode.l >> 16);
|
|
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
break;
|
|
}
|
|
} else if ((config.generalEmulation.hacks & hack_pilotWings) != 0 && (gDP.otherMode.l & 0x80) != 0) { //CLR_ON_CVG without FORCE_BL
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_ZERO, GL_ONE);
|
|
} else if ((config.generalEmulation.hacks & hack_blastCorps) != 0 && gSP.texture.on == 0 && currentCombiner()->usesTex()) { // Blast Corps
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_ZERO, GL_ONE);
|
|
} else {
|
|
glDisable( GL_BLEND );
|
|
}
|
|
}
|
|
|
|
void OGLRender::_updateCullFace() const
|
|
{
|
|
if (gSP.geometryMode & G_CULL_BOTH) {
|
|
glEnable( GL_CULL_FACE );
|
|
|
|
if (gSP.geometryMode & G_CULL_BACK)
|
|
glCullFace(GL_BACK);
|
|
else
|
|
glCullFace(GL_FRONT);
|
|
}
|
|
else
|
|
glDisable( GL_CULL_FACE );
|
|
}
|
|
|
|
inline
|
|
float _adjustViewportX(f32 _X0)
|
|
{
|
|
const float halfX = gDP.colorImage.width / 2.0f;
|
|
const float halfVP = gSP.viewport.width / 2.0f;
|
|
return (_X0 + halfVP - halfX) * video().getAdjustScale() + halfX - halfVP;
|
|
}
|
|
|
|
void OGLRender::_updateViewport() const
|
|
{
|
|
OGLVideo & ogl = video();
|
|
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
|
|
if (pCurrentBuffer == NULL) {
|
|
const f32 scaleX = ogl.getScaleX();
|
|
const f32 scaleY = ogl.getScaleY();
|
|
float Xf = gSP.viewport.vscale[0] < 0 ? (gSP.viewport.x + gSP.viewport.vscale[0] * 2.0f) : gSP.viewport.x;
|
|
if (ogl.isAdjustScreen() && gSP.viewport.width < gDP.colorImage.width && gDP.colorImage.width > VI.width * 98 / 100)
|
|
Xf = _adjustViewportX(Xf);
|
|
const GLint X = (GLint)(Xf * scaleX);
|
|
const GLint Y = gSP.viewport.vscale[1] < 0 ? (GLint)((gSP.viewport.y + gSP.viewport.vscale[1] * 2.0f) * scaleY) : (GLint)((VI.height - (gSP.viewport.y + gSP.viewport.height)) * scaleY);
|
|
glViewport(X, Y + ogl.getHeightOffset(),
|
|
max((GLint)(gSP.viewport.width * scaleX), 0), max((GLint)(gSP.viewport.height * scaleY), 0));
|
|
} else {
|
|
const f32 scaleX = pCurrentBuffer->m_scaleX;
|
|
const f32 scaleY = pCurrentBuffer->m_scaleY;
|
|
float Xf = gSP.viewport.vscale[0] < 0 ? (gSP.viewport.x + gSP.viewport.vscale[0] * 2.0f) : gSP.viewport.x;
|
|
if (ogl.isAdjustScreen() && gSP.viewport.width < gDP.colorImage.width && gDP.colorImage.width > VI.width * 98 / 100)
|
|
Xf = _adjustViewportX(Xf);
|
|
const GLint X = (GLint)(Xf * scaleX);
|
|
const GLint Y = gSP.viewport.vscale[1] < 0 ? (GLint)((gSP.viewport.y + gSP.viewport.vscale[1] * 2.0f) * scaleY) : (GLint)((pCurrentBuffer->m_height - (gSP.viewport.y + gSP.viewport.height)) * scaleY);
|
|
glViewport(X, Y,
|
|
max((GLint)(gSP.viewport.width * scaleX), 0), max((GLint)(gSP.viewport.height * scaleY), 0));
|
|
}
|
|
gSP.changed &= ~CHANGED_VIEWPORT;
|
|
}
|
|
|
|
inline
|
|
void _adjustScissorX(f32 & _X0, f32 & _X1, float _scale)
|
|
{
|
|
const float halfX = gDP.colorImage.width / 2.0f;
|
|
_X0 = (_X0 - halfX) * _scale + halfX;
|
|
_X1 = (_X1 - halfX) * _scale + halfX;
|
|
}
|
|
|
|
void OGLRender::_updateScissor() const
|
|
{
|
|
OGLVideo & ogl = video();
|
|
f32 scaleX, scaleY;
|
|
u32 heightOffset, screenHeight;
|
|
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
|
|
if (pCurrentBuffer == NULL) {
|
|
scaleX = ogl.getScaleX();
|
|
scaleY = ogl.getScaleY();
|
|
heightOffset = ogl.getHeightOffset();
|
|
screenHeight = VI.height;
|
|
}
|
|
else {
|
|
scaleX = pCurrentBuffer->m_scaleX;
|
|
scaleY = pCurrentBuffer->m_scaleY;
|
|
heightOffset = 0;
|
|
screenHeight = (pCurrentBuffer->m_height == 0) ? VI.height : pCurrentBuffer->m_height;
|
|
}
|
|
|
|
float SX0 = gDP.scissor.ulx;
|
|
float SX1 = gDP.scissor.lrx;
|
|
if (ogl.isAdjustScreen() && gSP.viewport.width < gDP.colorImage.width && gDP.colorImage.width > VI.width * 98 / 100)
|
|
_adjustScissorX(SX0, SX1, ogl.getAdjustScale());
|
|
|
|
glScissor((GLint)(SX0 * scaleX), (GLint)((screenHeight - gDP.scissor.lry) * scaleY + heightOffset),
|
|
max((GLint)((SX1 - SX0) * scaleX), 0), max((GLint)((gDP.scissor.lry - gDP.scissor.uly) * scaleY), 0));
|
|
gDP.changed &= ~CHANGED_SCISSOR;
|
|
}
|
|
|
|
void OGLRender::_updateDepthUpdate() const
|
|
{
|
|
if (gDP.otherMode.depthUpdate != 0)
|
|
glDepthMask( TRUE );
|
|
else
|
|
glDepthMask( FALSE );
|
|
}
|
|
|
|
void OGLRender::_updateStates(RENDER_STATE _renderState) const
|
|
{
|
|
OGLVideo & ogl = video();
|
|
|
|
CombinerInfo & cmbInfo = CombinerInfo::get();
|
|
cmbInfo.update();
|
|
|
|
if (gSP.changed & CHANGED_GEOMETRYMODE) {
|
|
_updateCullFace();
|
|
gSP.changed &= ~CHANGED_GEOMETRYMODE;
|
|
}
|
|
|
|
if (config.frameBufferEmulation.N64DepthCompare) {
|
|
glDisable( GL_DEPTH_TEST );
|
|
glDepthMask( FALSE );
|
|
} else if ((gDP.changed & (CHANGED_RENDERMODE | CHANGED_CYCLETYPE)) != 0) {
|
|
if (((gSP.geometryMode & G_ZBUFFER) || gDP.otherMode.depthSource == G_ZS_PRIM) && gDP.otherMode.cycleType <= G_CYC_2CYCLE) {
|
|
if (gDP.otherMode.depthCompare != 0) {
|
|
switch (gDP.otherMode.depthMode) {
|
|
case ZMODE_OPA:
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glDepthFunc(GL_LEQUAL);
|
|
break;
|
|
case ZMODE_INTER:
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glDepthFunc(GL_LEQUAL);
|
|
break;
|
|
case ZMODE_XLU:
|
|
// Max || Infront;
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
if (gDP.otherMode.depthSource == G_ZS_PRIM && gDP.primDepth.z == 1.0f)
|
|
// Max
|
|
glDepthFunc(GL_LEQUAL);
|
|
else
|
|
// Infront
|
|
glDepthFunc(GL_LESS);
|
|
break;
|
|
case ZMODE_DEC:
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
glDepthFunc(GL_LEQUAL);
|
|
break;
|
|
}
|
|
} else {
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glDepthFunc(GL_ALWAYS);
|
|
}
|
|
|
|
_updateDepthUpdate();
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
if (!GBI.isNoN())
|
|
glDisable(GL_DEPTH_CLAMP);
|
|
} else {
|
|
glDisable(GL_DEPTH_TEST);
|
|
if (!GBI.isNoN())
|
|
glEnable(GL_DEPTH_CLAMP);
|
|
}
|
|
}
|
|
|
|
if (gDP.changed & CHANGED_SCISSOR)
|
|
_updateScissor();
|
|
|
|
if (gSP.changed & CHANGED_VIEWPORT)
|
|
_updateViewport();
|
|
|
|
if (gSP.changed & CHANGED_LIGHT)
|
|
cmbInfo.updateLightParameters();
|
|
|
|
if ((gSP.changed & CHANGED_TEXTURE) || (gDP.changed & CHANGED_TILE) || (gDP.changed & CHANGED_TMEM) || cmbInfo.isChanged()) {
|
|
//For some reason updating the texture cache on the first frame of LOZ:OOT causes a NULL Pointer exception...
|
|
ShaderCombiner * pCurrentCombiner = cmbInfo.getCurrent();
|
|
if (pCurrentCombiner != NULL) {
|
|
if (pCurrentCombiner->usesT0())
|
|
textureCache().update(0);
|
|
else
|
|
textureCache().activateDummy(0);
|
|
|
|
//Note: enabling dummies makes some F-zero X textures flicker.... strange.
|
|
|
|
if (pCurrentCombiner->usesT1())
|
|
textureCache().update(1);
|
|
else
|
|
textureCache().activateDummy(1);
|
|
|
|
pCurrentCombiner->updateFBInfo();
|
|
}
|
|
if (_renderState == rsTriangle || _renderState == rsLine)
|
|
cmbInfo.updateTextureParameters();
|
|
gDP.changed &= ~(CHANGED_TILE | CHANGED_TMEM);
|
|
gSP.changed &= ~(CHANGED_TEXTURE);
|
|
}
|
|
|
|
if ((gDP.changed & (CHANGED_RENDERMODE | CHANGED_CYCLETYPE))) {
|
|
_setBlendMode();
|
|
gDP.changed &= ~(CHANGED_RENDERMODE | CHANGED_CYCLETYPE);
|
|
}
|
|
}
|
|
|
|
void OGLRender::_setColorArray() const
|
|
{
|
|
if (currentCombiner()->usesShade())
|
|
glEnableVertexAttribArray(SC_COLOR);
|
|
else
|
|
glDisableVertexAttribArray(SC_COLOR);
|
|
}
|
|
|
|
void OGLRender::_setTexCoordArrays() const
|
|
{
|
|
if (m_renderState == rsTriangle) {
|
|
glDisableVertexAttribArray(SC_TEXCOORD1);
|
|
if (currentCombiner()->usesT0() || currentCombiner()->usesT1())
|
|
glEnableVertexAttribArray(SC_TEXCOORD0);
|
|
else
|
|
glDisableVertexAttribArray(SC_TEXCOORD0);
|
|
} else {
|
|
if (currentCombiner()->usesT0())
|
|
glEnableVertexAttribArray(SC_TEXCOORD0);
|
|
else
|
|
glDisableVertexAttribArray(SC_TEXCOORD0);
|
|
|
|
if (currentCombiner()->usesT1())
|
|
glEnableVertexAttribArray(SC_TEXCOORD1);
|
|
else
|
|
glDisableVertexAttribArray(SC_TEXCOORD1);
|
|
}
|
|
}
|
|
|
|
void OGLRender::_prepareDrawTriangle(bool _dma)
|
|
{
|
|
#ifdef GL_IMAGE_TEXTURES_SUPPORT
|
|
if (m_bImageTexture && config.frameBufferEmulation.N64DepthCompare != 0)
|
|
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
|
|
#endif // GL_IMAGE_TEXTURES_SUPPORT
|
|
|
|
if (gSP.changed || gDP.changed)
|
|
_updateStates(rsTriangle);
|
|
|
|
const bool updateArrays = m_renderState != rsTriangle;
|
|
if (updateArrays || CombinerInfo::get().isChanged()) {
|
|
m_renderState = rsTriangle;
|
|
_setColorArray();
|
|
_setTexCoordArrays();
|
|
}
|
|
currentCombiner()->updateRenderState();
|
|
|
|
const bool updateColorArrays = m_bFlatColors != (!RSP.bLLE && (gSP.geometryMode & G_SHADING_SMOOTH) == 0);
|
|
if (updateColorArrays)
|
|
m_bFlatColors = !m_bFlatColors;
|
|
|
|
if (updateArrays) {
|
|
SPVertex * pVtx = _dma ? triangles.dmaVertices.data() : &triangles.vertices[0];
|
|
glVertexAttribPointer(SC_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), &pVtx->x);
|
|
if (m_bFlatColors)
|
|
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), &pVtx->flat_r);
|
|
else
|
|
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), &pVtx->r);
|
|
glVertexAttribPointer(SC_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(SPVertex), &pVtx->s);
|
|
if (config.generalEmulation.enableHWLighting) {
|
|
glEnableVertexAttribArray(SC_NUMLIGHTS);
|
|
glVertexAttribPointer(SC_NUMLIGHTS, 1, GL_BYTE, GL_FALSE, sizeof(SPVertex), &pVtx->HWLight);
|
|
}
|
|
|
|
} else if (updateColorArrays) {
|
|
SPVertex * pVtx = _dma ? triangles.dmaVertices.data() : &triangles.vertices[0];
|
|
if (m_bFlatColors)
|
|
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), &pVtx->flat_r);
|
|
else
|
|
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), &pVtx->r);
|
|
}
|
|
}
|
|
|
|
void OGLRender::drawLLETriangle(u32 _numVtx)
|
|
{
|
|
if (_numVtx == 0)
|
|
return;
|
|
|
|
gSP.changed &= ~CHANGED_GEOMETRYMODE; // Don't update cull mode
|
|
_prepareDrawTriangle(false);
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
OGLVideo & ogl = video();
|
|
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
|
|
if (pCurrentBuffer == NULL)
|
|
glViewport( 0, ogl.getHeightOffset(), ogl.getScreenWidth(), ogl.getScreenHeight());
|
|
else
|
|
glViewport(0, 0, pCurrentBuffer->m_width*pCurrentBuffer->m_scaleX, pCurrentBuffer->m_height*pCurrentBuffer->m_scaleY);
|
|
|
|
const float scaleX = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_width : VI.rwidth;
|
|
const float scaleY = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_height : VI.rheight;
|
|
|
|
for (u32 i = 0; i < _numVtx; ++i) {
|
|
SPVertex & vtx = triangles.vertices[i];
|
|
vtx.HWLight = 0;
|
|
vtx.x = vtx.x * (2.0f * scaleX) - 1.0f;
|
|
vtx.x *= vtx.w;
|
|
vtx.y = vtx.y * (-2.0f * scaleY) + 1.0f;
|
|
vtx.y *= vtx.w;
|
|
vtx.z *= vtx.w;
|
|
if (gDP.otherMode.texturePersp == 0) {
|
|
vtx.s *= 2.0f;
|
|
vtx.t *= 2.0f;
|
|
}
|
|
}
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVtx);
|
|
triangles.num = 0;
|
|
|
|
frameBufferList().setBufferChanged();
|
|
gSP.changed |= CHANGED_VIEWPORT | CHANGED_GEOMETRYMODE;
|
|
}
|
|
|
|
void OGLRender::drawDMATriangles(u32 _numVtx)
|
|
{
|
|
if (_numVtx == 0)
|
|
return;
|
|
_prepareDrawTriangle(true);
|
|
glDrawArrays(GL_TRIANGLES, 0, _numVtx);
|
|
}
|
|
|
|
void OGLRender::drawTriangles()
|
|
{
|
|
if (triangles.num == 0) return;
|
|
|
|
_prepareDrawTriangle(false);
|
|
glDrawElements(GL_TRIANGLES, triangles.num, GL_UNSIGNED_BYTE, triangles.elements);
|
|
triangles.num = 0;
|
|
}
|
|
|
|
void OGLRender::drawLine(int _v0, int _v1, float _width)
|
|
{
|
|
if (gSP.changed || gDP.changed)
|
|
_updateStates(rsLine);
|
|
|
|
if (m_renderState != rsLine || CombinerInfo::get().isChanged()) {
|
|
_setColorArray();
|
|
glDisableVertexAttribArray(SC_TEXCOORD0);
|
|
glDisableVertexAttribArray(SC_TEXCOORD1);
|
|
glVertexAttribPointer(SC_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), &triangles.vertices[0].x);
|
|
glVertexAttribPointer(SC_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(SPVertex), &triangles.vertices[0].r);
|
|
|
|
_updateCullFace();
|
|
_updateViewport();
|
|
m_renderState = rsLine;
|
|
}
|
|
currentCombiner()->updateRenderState();
|
|
|
|
unsigned short elem[2];
|
|
elem[0] = _v0;
|
|
elem[1] = _v1;
|
|
glLineWidth(_width * video().getScaleX());
|
|
glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, elem);
|
|
|
|
}
|
|
|
|
void OGLRender::drawRect(int _ulx, int _uly, int _lrx, int _lry, float *_pColor)
|
|
{
|
|
gSP.changed &= ~CHANGED_GEOMETRYMODE; // Don't update cull mode
|
|
if (gSP.changed || gDP.changed)
|
|
_updateStates(rsRect);
|
|
|
|
const bool updateArrays = m_renderState != rsRect;
|
|
if (updateArrays || CombinerInfo::get().isChanged()) {
|
|
m_renderState = rsRect;
|
|
glDisableVertexAttribArray(SC_COLOR);
|
|
glDisableVertexAttribArray(SC_TEXCOORD0);
|
|
glDisableVertexAttribArray(SC_TEXCOORD1);
|
|
}
|
|
|
|
if (updateArrays)
|
|
glVertexAttribPointer(SC_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(GLVertex), &m_rect[0].x);
|
|
currentCombiner()->updateRenderState();
|
|
|
|
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
|
|
OGLVideo & ogl = video();
|
|
if (pCurrentBuffer == NULL)
|
|
glViewport( 0, ogl.getHeightOffset(), ogl.getScreenWidth(), ogl.getScreenHeight());
|
|
else {
|
|
glViewport(0, 0, pCurrentBuffer->m_width*pCurrentBuffer->m_scaleX, pCurrentBuffer->m_height*pCurrentBuffer->m_scaleY);
|
|
}
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
const float scaleX = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_width : VI.rwidth;
|
|
const float scaleY = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_height : VI.rheight;
|
|
const float Z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz;
|
|
const float W = 1.0f;
|
|
m_rect[0].x = (float)_ulx * (2.0f * scaleX) - 1.0;
|
|
m_rect[0].y = (float)_uly * (-2.0f * scaleY) + 1.0;
|
|
m_rect[0].z = Z;
|
|
m_rect[0].w = W;
|
|
m_rect[1].x = (float)_lrx * (2.0f * scaleX) - 1.0;
|
|
m_rect[1].y = m_rect[0].y;
|
|
m_rect[1].z = Z;
|
|
m_rect[1].w = W;
|
|
m_rect[2].x = m_rect[0].x;
|
|
m_rect[2].y = (float)_lry * (-2.0f * scaleY) + 1.0;
|
|
m_rect[2].z = Z;
|
|
m_rect[2].w = W;
|
|
m_rect[3].x = m_rect[1].x;
|
|
m_rect[3].y = m_rect[2].y;
|
|
m_rect[3].z = Z;
|
|
m_rect[3].w = W;
|
|
|
|
if (ogl.isAdjustScreen() && (gDP.colorImage.width > VI.width * 98 / 100) && (_lrx - _ulx < VI.width * 9 / 10)) {
|
|
const float scale = ogl.getAdjustScale();
|
|
for (u32 i = 0; i < 4; ++i)
|
|
m_rect[i].x *= scale;
|
|
}
|
|
|
|
if (gDP.otherMode.cycleType == G_CYC_FILL)
|
|
glVertexAttrib4fv(SC_COLOR, _pColor);
|
|
else
|
|
glVertexAttrib4f(SC_COLOR, 0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
gSP.changed |= CHANGED_GEOMETRYMODE | CHANGED_VIEWPORT;
|
|
}
|
|
|
|
static
|
|
bool texturedRectShadowMap(const OGLRender::TexturedRectParams &)
|
|
{
|
|
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
|
|
if (pCurrentBuffer != NULL) {
|
|
if (gDP.textureImage.size == 2 && gDP.textureImage.address >= gDP.depthImageAddress && gDP.textureImage.address < (gDP.depthImageAddress + gDP.colorImage.width*gDP.colorImage.width * 6 / 4)) {
|
|
pCurrentBuffer->m_pDepthBuffer->activateDepthBufferTexture(pCurrentBuffer);
|
|
SetDepthFogCombiner();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static
|
|
bool texturedRectDepthBufferCopy(const OGLRender::TexturedRectParams & _params)
|
|
{
|
|
// Copy one line from depth buffer into auxiliary color buffer with height = 1.
|
|
// Data from depth buffer loaded into TMEM and then rendered to RDRAM by texrect.
|
|
// Works only with depth buffer emulation enabled.
|
|
// Load of arbitrary data to that area causes weird camera rotation in CBFD.
|
|
const gDPTile * pTile = gSP.textureTile[0];
|
|
if (pTile->loadType == LOADTYPE_BLOCK && gDP.textureImage.size == 2 && gDP.textureImage.address >= gDP.depthImageAddress && gDP.textureImage.address < (gDP.depthImageAddress + gDP.colorImage.width*gDP.colorImage.width * 6 / 4)) {
|
|
if (config.frameBufferEmulation.enable == 0)
|
|
return true;
|
|
frameBufferList().getCurrent()->m_cleared = true;
|
|
if (config.frameBufferEmulation.copyDepthToRDRAM == 0)
|
|
return true;
|
|
if (FrameBuffer_CopyDepthBuffer(gDP.colorImage.address))
|
|
RDP_RepeatLastLoadBlock();
|
|
|
|
const u32 width = (u32)(_params.lrx - _params.ulx);
|
|
const u32 ulx = (u32)_params.ulx;
|
|
u16 * pSrc = ((u16*)TMEM) + (u32)floorf(_params.uls + 0.5f);
|
|
u16 *pDst = (u16*)(RDRAM + gDP.colorImage.address);
|
|
for (u32 x = 0; x < width; ++x)
|
|
pDst[(ulx + x) ^ 1] = swapword(pSrc[x]);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static
|
|
bool texturedRectCopyToItself(const OGLRender::TexturedRectParams & _params)
|
|
{
|
|
FrameBuffer * pCurrent = frameBufferList().getCurrent();
|
|
if (pCurrent != NULL && pCurrent->m_size == G_IM_SIZ_8b && gSP.textureTile[0]->frameBuffer == pCurrent)
|
|
return true;
|
|
return texturedRectDepthBufferCopy(_params);
|
|
}
|
|
|
|
static
|
|
bool texturedRectBGCopy(const OGLRender::TexturedRectParams & _params)
|
|
{
|
|
if (GBI.getMicrocodeType() != S2DEX)
|
|
return false;
|
|
|
|
float flry = _params.lry;
|
|
if (flry > gDP.scissor.lry)
|
|
flry = gDP.scissor.lry;
|
|
|
|
const u32 width = (u32)(_params.lrx - _params.ulx);
|
|
const u32 tex_width = gSP.textureTile[0]->line << 3;
|
|
const u32 uly = (u32)_params.uly;
|
|
const u32 lry = flry;
|
|
|
|
u8 * texaddr = RDRAM + gDP.loadInfo[gSP.textureTile[0]->tmem].texAddress + tex_width*(u32)_params.ult + (u32)_params.uls;
|
|
u8 * fbaddr = RDRAM + gDP.colorImage.address + (u32)_params.ulx;
|
|
// LOG(LOG_VERBOSE, "memrect (%d, %d, %d, %d), ci_width: %d texaddr: 0x%08lx fbaddr: 0x%08lx\n", (u32)_params.ulx, uly, (u32)_params.lrx, lry, gDP.colorImage.width, gSP.textureTile[0]->imageAddress + tex_width*(u32)_params.ult + (u32)_params.uls, gDP.colorImage.address + (u32)_params.ulx);
|
|
|
|
for (u32 y = uly; y < lry; ++y) {
|
|
u8 *src = texaddr + (y - uly) * tex_width;
|
|
u8 *dst = fbaddr + y * gDP.colorImage.width;
|
|
memcpy(dst, src, width);
|
|
}
|
|
frameBufferList().removeBuffer(gDP.colorImage.address);
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool texturedRectPaletteMod(const OGLRender::TexturedRectParams & _params)
|
|
{
|
|
if (gDP.scissor.lrx != 16 || gDP.scissor.lry != 1 || _params.lrx != 16 || _params.lry != 1)
|
|
return false;
|
|
u8 envr = (u8)(gDP.envColor.r * 31.0f);
|
|
u8 envg = (u8)(gDP.envColor.g * 31.0f);
|
|
u8 envb = (u8)(gDP.envColor.b * 31.0f);
|
|
u16 env16 = (u16)((envr << 11) | (envg << 6) | (envb << 1) | 1);
|
|
u8 prmr = (u8)(gDP.primColor.r * 31.0f);
|
|
u8 prmg = (u8)(gDP.primColor.g * 31.0f);
|
|
u8 prmb = (u8)(gDP.primColor.b * 31.0f);
|
|
u16 prim16 = (u16)((prmr << 11) | (prmg << 6) | (prmb << 1) | 1);
|
|
u16 * src = (u16*)&TMEM[256];
|
|
u16 * dst = (u16*)(RDRAM + gDP.colorImage.address);
|
|
for (u32 i = 0; i < 16; ++i)
|
|
dst[i ^ 1] = (src[i<<2] & 0x100) ? prim16 : env16;
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool texturedRectMonochromeBackground(const OGLRender::TexturedRectParams & _params)
|
|
{
|
|
if (gDP.textureImage.address >= gDP.colorImage.address && gDP.textureImage.address <= (gDP.colorImage.address + gDP.colorImage.width*gDP.colorImage.height * 2)) {
|
|
#ifdef GL_IMAGE_TEXTURES_SUPPORT
|
|
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
|
|
if (pCurrentBuffer != NULL) {
|
|
FrameBuffer_ActivateBufferTexture(0, pCurrentBuffer);
|
|
SetMonochromeCombiner();
|
|
return false;
|
|
} else
|
|
#endif
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Special processing of textured rect.
|
|
// Return true if actuial rendering is not necessary
|
|
bool(*texturedRectSpecial)(const OGLRender::TexturedRectParams & _params) = NULL;
|
|
|
|
void OGLRender::drawTexturedRect(const TexturedRectParams & _params)
|
|
{
|
|
gSP.changed &= ~CHANGED_GEOMETRYMODE; // Don't update cull mode
|
|
if (gSP.changed || gDP.changed)
|
|
_updateStates(rsTexRect);
|
|
|
|
const bool updateArrays = m_renderState != rsTexRect;
|
|
if (updateArrays || CombinerInfo::get().isChanged()) {
|
|
m_renderState = rsTexRect;
|
|
glDisableVertexAttribArray(SC_COLOR);
|
|
_setTexCoordArrays();
|
|
}
|
|
|
|
if (updateArrays) {
|
|
#ifdef RENDERSTATE_TEST
|
|
StateChanges++;
|
|
#endif
|
|
glVertexAttrib4f(SC_COLOR, 0, 0, 0, 0);
|
|
glVertexAttribPointer(SC_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(GLVertex), &m_rect[0].x);
|
|
glVertexAttribPointer(SC_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), &m_rect[0].s0);
|
|
glVertexAttribPointer(SC_TEXCOORD1, 2, GL_FLOAT, GL_FALSE, sizeof(GLVertex), &m_rect[0].s1);
|
|
}
|
|
currentCombiner()->updateRenderState();
|
|
|
|
if (RSP.cmd == 0xE4 && texturedRectSpecial != NULL && texturedRectSpecial(_params)) {
|
|
gSP.changed |= CHANGED_GEOMETRYMODE;
|
|
return;
|
|
}
|
|
|
|
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
|
|
OGLVideo & ogl = video();
|
|
if (pCurrentBuffer == NULL)
|
|
glViewport( 0, ogl.getHeightOffset(), ogl.getScreenWidth(), ogl.getScreenHeight());
|
|
else
|
|
glViewport(0, 0, pCurrentBuffer->m_width*pCurrentBuffer->m_scaleX, pCurrentBuffer->m_height*pCurrentBuffer->m_scaleY);
|
|
glDisable( GL_CULL_FACE );
|
|
|
|
const float scaleX = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_width : VI.rwidth;
|
|
const float scaleY = pCurrentBuffer != NULL ? 1.0f / pCurrentBuffer->m_height : VI.rheight;
|
|
const float Z = (gDP.otherMode.depthSource == G_ZS_PRIM) ? gDP.primDepth.z : gSP.viewport.nearz;
|
|
const float W = 1.0f;
|
|
m_rect[0].x = (float)_params.ulx * (2.0f * scaleX) - 1.0f;
|
|
m_rect[0].y = (float)_params.uly * (-2.0f * scaleY) + 1.0f;
|
|
m_rect[0].z = Z;
|
|
m_rect[0].w = W;
|
|
m_rect[1].x = (float)(_params.lrx) * (2.0f * scaleX) - 1.0f;
|
|
m_rect[1].y = m_rect[0].y;
|
|
m_rect[1].z = Z;
|
|
m_rect[1].w = W;
|
|
m_rect[2].x = m_rect[0].x;
|
|
m_rect[2].y = (float)(_params.lry) * (-2.0f * scaleY) + 1.0f;
|
|
m_rect[2].z = Z;
|
|
m_rect[2].w = W;
|
|
m_rect[3].x = m_rect[1].x;
|
|
m_rect[3].y = m_rect[2].y;
|
|
m_rect[3].z = Z;
|
|
m_rect[3].w = W;
|
|
|
|
TextureCache & cache = textureCache();
|
|
struct
|
|
{
|
|
float s0, t0, s1, t1;
|
|
} texST[2] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; //struct for texture coordinates
|
|
|
|
if (currentCombiner()->usesT0() && cache.current[0] && gSP.textureTile[0]) {
|
|
f32 shiftScaleS = 1.0f;
|
|
f32 shiftScaleT = 1.0f;
|
|
getTextureShiftScale(0, cache, shiftScaleS, shiftScaleT);
|
|
texST[0].s0 = _params.uls * shiftScaleS - gSP.textureTile[0]->fuls;
|
|
texST[0].t0 = _params.ult * shiftScaleT - gSP.textureTile[0]->fult;
|
|
texST[0].s1 = (_params.lrs + 1.0f) * shiftScaleS - gSP.textureTile[0]->fuls;
|
|
texST[0].t1 = (_params.lrt + 1.0f) * shiftScaleT - gSP.textureTile[0]->fult;
|
|
|
|
if (cache.current[0]->frameBufferTexture) {
|
|
texST[0].s0 = cache.current[0]->offsetS + texST[0].s0;
|
|
texST[0].t0 = cache.current[0]->offsetT - texST[0].t0;
|
|
texST[0].s1 = cache.current[0]->offsetS + texST[0].s1;
|
|
texST[0].t1 = cache.current[0]->offsetT - texST[0].t1;
|
|
}
|
|
|
|
glActiveTexture( GL_TEXTURE0 );
|
|
|
|
if ((texST[0].s0 < texST[0].s1 && texST[0].s0 >= 0.0 && texST[0].s1 <= cache.current[0]->width) || (cache.current[0]->maskS + cache.current[0]->mirrorS == 0 && (texST[0].s0 < -1024.0f || texST[0].s1 > 1023.99f)))
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
if (texST[0].t0 < texST[0].t1 && texST[0].t0 >= 0.0f && texST[0].t1 <= cache.current[0]->height)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
texST[0].s0 *= cache.current[0]->scaleS;
|
|
texST[0].t0 *= cache.current[0]->scaleT;
|
|
texST[0].s1 *= cache.current[0]->scaleS;
|
|
texST[0].t1 *= cache.current[0]->scaleT;
|
|
}
|
|
|
|
if (currentCombiner()->usesT1() && cache.current[1] && gSP.textureTile[1]) {
|
|
f32 shiftScaleS = 1.0f;
|
|
f32 shiftScaleT = 1.0f;
|
|
getTextureShiftScale(1, cache, shiftScaleS, shiftScaleT);
|
|
texST[1].s0 = _params.uls * shiftScaleS - gSP.textureTile[1]->fuls;
|
|
texST[1].t0 = _params.ult * shiftScaleT - gSP.textureTile[1]->fult;
|
|
texST[1].s1 = (_params.lrs + 1.0f) * shiftScaleS - gSP.textureTile[1]->fuls;
|
|
texST[1].t1 = (_params.lrt + 1.0f) * shiftScaleT - gSP.textureTile[1]->fult;
|
|
|
|
if (cache.current[1]->frameBufferTexture) {
|
|
texST[1].s0 = cache.current[1]->offsetS + texST[1].s0;
|
|
texST[1].t0 = cache.current[1]->offsetT - texST[1].t0;
|
|
texST[1].s1 = cache.current[1]->offsetS + texST[1].s1;
|
|
texST[1].t1 = cache.current[1]->offsetT - texST[1].t1;
|
|
}
|
|
|
|
glActiveTexture( GL_TEXTURE1 );
|
|
|
|
if ((texST[1].s0 == 0.0f) && (texST[1].s1 <= cache.current[1]->width))
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
|
|
|
if ((texST[1].t0 == 0.0f) && (texST[1].t1 <= cache.current[1]->height))
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
|
|
|
texST[1].s0 *= cache.current[1]->scaleS;
|
|
texST[1].t0 *= cache.current[1]->scaleT;
|
|
texST[1].s1 *= cache.current[1]->scaleS;
|
|
texST[1].t1 *= cache.current[1]->scaleT;
|
|
}
|
|
|
|
if (gDP.otherMode.cycleType == G_CYC_COPY) {
|
|
glActiveTexture( GL_TEXTURE0 );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
|
}
|
|
|
|
m_rect[0].s0 = texST[0].s0;
|
|
m_rect[0].t0 = texST[0].t0;
|
|
m_rect[0].s1 = texST[1].s0;
|
|
m_rect[0].t1 = texST[1].t0;
|
|
|
|
m_rect[3].s0 = texST[0].s1;
|
|
m_rect[3].t0 = texST[0].t1;
|
|
m_rect[3].s1 = texST[1].s1;
|
|
m_rect[3].t1 = texST[1].t1;
|
|
|
|
if (_params.flip) {
|
|
m_rect[1].s0 = texST[0].s0;
|
|
m_rect[1].t0 = texST[0].t1;
|
|
m_rect[1].s1 = texST[1].s0;
|
|
m_rect[1].t1 = texST[1].t1;
|
|
|
|
m_rect[2].s0 = texST[0].s1;
|
|
m_rect[2].t0 = texST[0].t0;
|
|
m_rect[2].s1 = texST[1].s1;
|
|
m_rect[2].t1 = texST[1].t0;
|
|
} else {
|
|
m_rect[1].s0 = texST[0].s1;
|
|
m_rect[1].t0 = texST[0].t0;
|
|
m_rect[1].s1 = texST[1].s1;
|
|
m_rect[1].t1 = texST[1].t0;
|
|
|
|
m_rect[2].s0 = texST[0].s0;
|
|
m_rect[2].t0 = texST[0].t1;
|
|
m_rect[2].s1 = texST[1].s0;
|
|
m_rect[2].t1 = texST[1].t1;
|
|
}
|
|
|
|
if (ogl.isAdjustScreen() && (gDP.colorImage.width > VI.width * 98 / 100) && (_params.lrx - _params.ulx < VI.width * 9 / 10)) {
|
|
const float scale = ogl.getAdjustScale();
|
|
for (u32 i = 0; i < 4; ++i)
|
|
m_rect[i].x *= scale;
|
|
}
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
gSP.changed |= CHANGED_GEOMETRYMODE | CHANGED_VIEWPORT;
|
|
}
|
|
|
|
void OGLRender::drawText(const char *_pText, float x, float y)
|
|
{
|
|
m_renderState = rsNone;
|
|
TextDrawer::get().renderText(_pText, x, y);
|
|
}
|
|
|
|
void OGLRender::clearDepthBuffer(u32 _uly, u32 _lry)
|
|
{
|
|
if (config.frameBufferEmulation.enable && frameBufferList().getCurrent() == NULL)
|
|
return;
|
|
|
|
depthBufferList().clearBuffer(_uly, _lry);
|
|
|
|
glDisable( GL_SCISSOR_TEST );
|
|
glDepthMask( TRUE );
|
|
glClear( GL_DEPTH_BUFFER_BIT );
|
|
|
|
_updateDepthUpdate();
|
|
|
|
glEnable( GL_SCISSOR_TEST );
|
|
}
|
|
|
|
void OGLRender::clearColorBuffer(float *_pColor )
|
|
{
|
|
glDisable( GL_SCISSOR_TEST );
|
|
|
|
glClearColor( _pColor[0], _pColor[1], _pColor[2], _pColor[3] );
|
|
glClear( GL_COLOR_BUFFER_BIT );
|
|
|
|
glEnable( GL_SCISSOR_TEST );
|
|
}
|
|
|
|
void OGLRender::_initExtensions()
|
|
{
|
|
GLint majorVersion = 0;
|
|
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
|
|
assert(majorVersion >= 3 && "Plugin requires GL version 3 or higher.");
|
|
|
|
#ifdef GL_IMAGE_TEXTURES_SUPPORT
|
|
GLint minorVersion = 0;
|
|
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
|
|
m_bImageTexture = (majorVersion >= 4) && (minorVersion >= 3) && (glBindImageTexture != NULL);
|
|
#else
|
|
m_bImageTexture = false;
|
|
#endif
|
|
|
|
if (config.texture.maxAnisotropy != 0) {
|
|
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &config.texture.maxAnisotropyF);
|
|
config.texture.maxAnisotropyF = min(config.texture.maxAnisotropyF, (f32)config.texture.maxAnisotropy);
|
|
} else
|
|
config.texture.maxAnisotropyF = 0.0f;
|
|
}
|
|
|
|
void OGLRender::_initStates()
|
|
{
|
|
glDisable(GL_CULL_FACE);
|
|
glEnableVertexAttribArray(SC_POSITION);
|
|
glEnable( GL_DEPTH_TEST );
|
|
glDepthFunc( GL_ALWAYS );
|
|
glDepthMask( GL_FALSE );
|
|
glEnable( GL_SCISSOR_TEST );
|
|
|
|
if (config.frameBufferEmulation.N64DepthCompare != 0) {
|
|
glDisable( GL_DEPTH_TEST );
|
|
glDisable( GL_POLYGON_OFFSET_FILL );
|
|
glDepthFunc( GL_ALWAYS );
|
|
glDepthMask( FALSE );
|
|
} else
|
|
glPolygonOffset( -3.0f, -3.0f );
|
|
|
|
OGLVideo & ogl = video();
|
|
glViewport(0, ogl.getHeightOffset(), ogl.getScreenWidth(), ogl.getScreenHeight());
|
|
|
|
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
|
|
glClear( GL_COLOR_BUFFER_BIT );
|
|
|
|
srand( time(NULL) );
|
|
|
|
ogl.swapBuffers();
|
|
}
|
|
|
|
void OGLRender::_initData()
|
|
{
|
|
glState.reset();
|
|
_initExtensions();
|
|
_initStates();
|
|
_setSpecialTexrect();
|
|
|
|
textureCache().init();
|
|
DepthBuffer_Init();
|
|
FrameBuffer_Init();
|
|
Combiner_Init();
|
|
TextDrawer::get().init();
|
|
TFH.init();
|
|
if (config.bloomFilter.enable != 0)
|
|
PostProcessor::get().init();
|
|
m_renderState = rsNone;
|
|
|
|
gSP.changed = gDP.changed = 0xFFFFFFFF;
|
|
|
|
memset(triangles.vertices, 0, VERTBUFF_SIZE * sizeof(SPVertex));
|
|
memset(triangles.elements, 0, ELEMBUFF_SIZE * sizeof(GLubyte));
|
|
for (u32 i = 0; i < VERTBUFF_SIZE; ++i)
|
|
triangles.vertices[i].w = 1.0f;
|
|
triangles.num = 0;
|
|
}
|
|
|
|
void OGLRender::_destroyData()
|
|
{
|
|
m_renderState = rsNone;
|
|
if (config.bloomFilter.enable != 0)
|
|
PostProcessor::get().destroy();
|
|
TextDrawer::get().destroy();
|
|
Combiner_Destroy();
|
|
FrameBuffer_Destroy();
|
|
DepthBuffer_Destroy();
|
|
textureCache().destroy();
|
|
}
|
|
|
|
void OGLRender::_setSpecialTexrect() const
|
|
{
|
|
const char * name = RSP.romname;
|
|
if (strstr(name, (const char *)"Beetle") || strstr(name, (const char *)"BEETLE") || strstr(name, (const char *)"HSV")
|
|
|| strstr(name, (const char *)"DUCK DODGERS") || strstr(name, (const char *)"DAFFY DUCK"))
|
|
texturedRectSpecial = texturedRectShadowMap;
|
|
else if (strstr(name, (const char *)"Perfect Dark") || strstr(name, (const char *)"PERFECT DARK"))
|
|
texturedRectSpecial = texturedRectDepthBufferCopy; // See comments to that function!
|
|
else if (strstr(name, (const char *)"CONKER BFD"))
|
|
texturedRectSpecial = texturedRectCopyToItself;
|
|
else if (strstr(name, (const char *)"YOSHI STORY"))
|
|
texturedRectSpecial = texturedRectBGCopy;
|
|
else if (strstr(name, (const char *)"PAPER MARIO") || strstr(name, (const char *)"MARIO STORY"))
|
|
texturedRectSpecial = texturedRectPaletteMod;
|
|
else if (strstr(name, (const char *)"ZELDA"))
|
|
texturedRectSpecial = texturedRectMonochromeBackground;
|
|
else
|
|
texturedRectSpecial = NULL;
|
|
}
|
|
|
|
static
|
|
u32 textureFilters[] = {
|
|
NO_FILTER, //"None"
|
|
SMOOTH_FILTER_1, //"Smooth filtering 1"
|
|
SMOOTH_FILTER_2, //"Smooth filtering 2"
|
|
SMOOTH_FILTER_3, //"Smooth filtering 3"
|
|
SMOOTH_FILTER_4, //"Smooth filtering 4"
|
|
SHARP_FILTER_1, //"Sharp filtering 1"
|
|
SHARP_FILTER_2, //"Sharp filtering 2"
|
|
};
|
|
|
|
static
|
|
u32 textureEnhancements[] = {
|
|
NO_ENHANCEMENT, //"None"
|
|
NO_ENHANCEMENT, //"Store"
|
|
X2_ENHANCEMENT, //"X2"
|
|
X2SAI_ENHANCEMENT, //"X2SAI"
|
|
HQ2X_ENHANCEMENT, //"HQ2X"
|
|
HQ2XS_ENHANCEMENT, //"HQ2XS"
|
|
LQ2X_ENHANCEMENT, //"LQ2X"
|
|
LQ2XS_ENHANCEMENT, //"LQ2XS"
|
|
HQ4X_ENHANCEMENT, //"HQ4X"
|
|
BRZ2X_ENHANCEMENT, //"2XBRZ"
|
|
BRZ3X_ENHANCEMENT, //"3XBRZ"
|
|
BRZ4X_ENHANCEMENT, //"4XBRZ"
|
|
BRZ5X_ENHANCEMENT //"5XBRZ"
|
|
};
|
|
|
|
void displayLoadProgress(const wchar_t *format, ...)
|
|
{
|
|
va_list args;
|
|
wchar_t wbuf[INFO_BUF];
|
|
char buf[INFO_BUF];
|
|
|
|
// process input
|
|
va_start(args, format);
|
|
vswprintf(wbuf, INFO_BUF, format, args);
|
|
va_end(args);
|
|
|
|
// XXX: convert to multibyte
|
|
wcstombs(buf, wbuf, INFO_BUF);
|
|
|
|
FrameBuffer* pBuffer = frameBufferList().getCurrent();
|
|
if (pBuffer != NULL)
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
OGLRender & render = video().getRender();
|
|
float black[4] = {0, 0, 0, 0};
|
|
render.clearColorBuffer(black);
|
|
render.drawText(buf, -0.9f, 0);
|
|
video().swapBuffers();
|
|
|
|
if (pBuffer != NULL)
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pBuffer->m_FBO);
|
|
}
|
|
|
|
void TextureFilterHandler::init()
|
|
{
|
|
if (!isInited()) {
|
|
m_inited = config.textureFilter.txFilterMode | config.textureFilter.txEnhancementMode | config.textureFilter.txHiresEnable;
|
|
if (m_inited != 0) {
|
|
u32 options = textureFilters[config.textureFilter.txFilterMode] | textureEnhancements[config.textureFilter.txEnhancementMode];
|
|
if (config.textureFilter.txHiresEnable)
|
|
options |= RICE_HIRESTEXTURES;
|
|
if (config.textureFilter.txForce16bpp)
|
|
options |= FORCE16BPP_TEX | FORCE16BPP_HIRESTEX;
|
|
if (config.textureFilter.txCacheCompression)
|
|
options |= GZ_TEXCACHE | GZ_HIRESTEXCACHE;
|
|
if (config.textureFilter.txSaveCache)
|
|
options |= (DUMP_TEXCACHE | DUMP_HIRESTEXCACHE);
|
|
if (config.textureFilter.txHiresFullAlphaChannel)
|
|
options |= LET_TEXARTISTS_FLY;
|
|
if (config.textureFilter.txDump)
|
|
options |= DUMP_TEX;
|
|
|
|
GLint maxTextureSize;
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
|
wchar_t wRomName[32];
|
|
::mbstowcs(wRomName, RSP.romname, 32);
|
|
wchar_t txPath[PLUGIN_PATH_SIZE+16];
|
|
wchar_t * pTexPackPath = config.textureFilter.txPath;
|
|
if (::wcslen(config.textureFilter.txPath) == 0) {
|
|
wcscpy(txPath, RSP.pluginpath);
|
|
wcscat(txPath, L"/hires_texture");
|
|
pTexPackPath = txPath;
|
|
}
|
|
|
|
m_inited = txfilter_init(maxTextureSize, // max texture width supported by hardware
|
|
maxTextureSize, // max texture height supported by hardware
|
|
32, // max texture bpp supported by hardware
|
|
options,
|
|
config.textureFilter.txCacheSize, // cache texture to system memory
|
|
RSP.pluginpath, // plugin path
|
|
pTexPackPath, // path to texture packs folder
|
|
wRomName, // name of ROM. must be no longer than 256 characters
|
|
displayLoadProgress);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TextureFilterHandler::shutdown()
|
|
{
|
|
if (isInited()) {
|
|
txfilter_shutdown();
|
|
m_inited = 0;
|
|
}
|
|
}
|
|
|
|
TextureFilterHandler TFH;
|