1
0
mirror of https://github.com/blawar/GLideN64.git synced 2024-07-07 03:13:49 +00:00
GLideN64/OpenGL.cpp
Sergey Lipskiy c6083851fc Add special texrect function for Conker BFD shadow.
Problem:
shadow is rendered into an auxilary buffer. At the end of rendering the buffer is
renders to itself as texture, using texrect command. FBO work in undefined
when it's color attachment is used as input and output. As result the
shadow is incorrect.

Thus, it's better to skip that texrect. Hack!
2015-05-13 10:19:31 +06:00

1579 lines
45 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();
gDPSetRenderMode(0, 0);
gDPSetTextureLUT(G_TT_NONE);
gDPSetAlphaCompare(G_AC_NONE);
++RSP.DList;
}
void OGLVideo::setCaptureScreen(const char * const _strDirectory)
{
m_strScreenDirectory = _strDirectory;
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();
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()
{
if (config.frameBufferEmulation.enable) {
switch (config.frameBufferEmulation.aspect) {
case 0: // stretch
m_width = m_screenWidth;
m_height = m_screenHeight;
break;
case 1: // 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 2: // 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;
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;
}
}
}
#define ORKIN_BLENDMODE
#ifdef RICE_BLENDMODE
//copied from RICE VIDEO
void OGLVideo::_setBlendMode() const
{
#define BLEND_NOOP 0x0000
#define BLEND_NOOP5 0xcc48 // Fog * 0 + Mem * 1
#define BLEND_NOOP4 0xcc08 // Fog * 0 + In * 1
#define BLEND_FOG_ASHADE 0xc800
#define BLEND_FOG_3 0xc000 // Fog * AIn + In * 1-A
#define BLEND_FOG_MEM 0xc440 // Fog * AFog + Mem * 1-A
#define BLEND_FOG_APRIM 0xc400 // Fog * AFog + In * 1-A
#define BLEND_BLENDCOLOR 0x8c88
#define BLEND_BI_AFOG 0x8400 // Bl * AFog + In * 1-A
#define BLEND_BI_AIN 0x8040 // Bl * AIn + Mem * 1-A
#define BLEND_MEM 0x4c40 // Mem*0 + Mem*(1-0)?!
#define BLEND_FOG_MEM_3 0x44c0 // Mem * AFog + Fog * 1-A
#define BLEND_NOOP3 0x0c48 // In * 0 + Mem * 1
#define BLEND_PASS 0x0c08 // In * 0 + In * 1
#define BLEND_FOG_MEM_IN_MEM 0x0440 // In * AFog + Mem * 1-A
#define BLEND_FOG_MEM_FOG_MEM 0x04c0 // In * AFog + Fog * 1-A
#define BLEND_OPA 0x0044 // In * AIn + Mem * AMem
#define BLEND_XLU 0x0040
#define BLEND_MEM_ALPHA_IN 0x4044 // Mem * AIn + Mem * AMem
u32 blender = gDP.otherMode.l >> 16;
u32 blendmode_1 = blender&0xcccc;
u32 blendmode_2 = blender&0x3333;
glEnable(GL_BLEND);
switch(gDP.otherMode.cycleType)
{
case G_CYC_FILL:
glDisable(GL_BLEND);
break;
case G_CYC_COPY:
glBlendFunc(GL_ONE, GL_ZERO);
break;
case G_CYC_2CYCLE:
if (gDP.otherMode.forceBlender && gDP.otherMode.depthCompare)
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
}
switch(blendmode_1+blendmode_2)
{
case BLEND_PASS+(BLEND_PASS>>2): // In * 0 + In * 1
case BLEND_FOG_APRIM+(BLEND_PASS>>2):
case BLEND_FOG_MEM_FOG_MEM + (BLEND_OPA>>2):
case BLEND_FOG_APRIM + (BLEND_OPA>>2):
case BLEND_FOG_ASHADE + (BLEND_OPA>>2):
case BLEND_BI_AFOG + (BLEND_OPA>>2):
case BLEND_FOG_ASHADE + (BLEND_NOOP>>2):
case BLEND_NOOP + (BLEND_OPA>>2):
case BLEND_NOOP4 + (BLEND_NOOP>>2):
case BLEND_FOG_ASHADE+(BLEND_PASS>>2):
case BLEND_FOG_3+(BLEND_PASS>>2):
glDisable(GL_BLEND);
break;
case BLEND_PASS+(BLEND_OPA>>2):
if (gDP.otherMode.cvgXAlpha && gDP.otherMode.alphaCvgSel)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
else
glDisable(GL_BLEND);
break;
case BLEND_PASS + (BLEND_XLU>>2):
case BLEND_FOG_ASHADE + (BLEND_XLU>>2):
case BLEND_FOG_APRIM + (BLEND_XLU>>2):
case BLEND_FOG_MEM_FOG_MEM + (BLEND_PASS>>2):
case BLEND_XLU + (BLEND_XLU>>2):
case BLEND_BI_AFOG + (BLEND_XLU>>2):
case BLEND_XLU + (BLEND_FOG_MEM_IN_MEM>>2):
case BLEND_PASS + (BLEND_FOG_MEM_IN_MEM>>2):
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case BLEND_FOG_ASHADE+0x0301:
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
break;
case 0x0c08+0x1111:
glBlendFunc(GL_ZERO, GL_DST_ALPHA);
break;
default:
if (blendmode_2 == (BLEND_PASS>>2))
glDisable(GL_BLEND);
else
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
}
break;
default:
if (gDP.otherMode.forceBlender && gDP.otherMode.depthCompare && blendmode_1 != BLEND_FOG_ASHADE )
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
}
switch (blendmode_1)
{
case BLEND_XLU:
case BLEND_BI_AIN:
case BLEND_FOG_MEM:
case BLEND_FOG_MEM_IN_MEM:
case BLEND_BLENDCOLOR:
case 0x00c0:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case BLEND_MEM_ALPHA_IN:
glBlendFunc(GL_ZERO, GL_DST_ALPHA);
break;
case BLEND_OPA:
glDisable(GL_BLEND);
break;
case BLEND_PASS:
case BLEND_NOOP:
case BLEND_FOG_ASHADE:
case BLEND_FOG_MEM_3:
case BLEND_BI_AFOG:
glDisable(GL_BLEND);
break;
case BLEND_FOG_APRIM:
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ZERO);
break;
case BLEND_NOOP3:
case BLEND_NOOP5:
case BLEND_MEM:
glBlendFunc(GL_ZERO, GL_ONE);
break;
default:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
}
}
#else
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;
//Space Invaders
case 0x0448: // Add
case 0x055A:
glBlendFunc( GL_ONE, GL_ONE );
break;
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 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
{
glDisable( GL_BLEND );
}
}
#endif
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 );
}
void OGLRender::_updateViewport() const
{
OGLVideo & ogl = video();
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
if (pCurrentBuffer == NULL) {
const GLint X = gSP.viewport.vscale[0] < 0 ? (GLint)((gSP.viewport.x + gSP.viewport.vscale[0] * 2.0f) * ogl.getScaleX()) : (GLint)(gSP.viewport.x * ogl.getScaleX());
const GLint Y = gSP.viewport.vscale[1] < 0 ? (GLint)((gSP.viewport.y + gSP.viewport.vscale[1] * 2.0f) * ogl.getScaleY()) : (GLint)((VI.height - (gSP.viewport.y + gSP.viewport.height)) * ogl.getScaleY());
glViewport( X, Y + ogl.getHeightOffset(),
max((GLint)(gSP.viewport.width * ogl.getScaleX()), 0), max((GLint)(gSP.viewport.height * ogl.getScaleY()), 0) );
} else {
const GLint X = gSP.viewport.vscale[0] < 0 ? (GLint)((gSP.viewport.x + gSP.viewport.vscale[0] * 2.0f) * ogl.getScaleX()) : (GLint)(gSP.viewport.x * ogl.getScaleX());
const GLint Y = gSP.viewport.vscale[1] < 0 ? (GLint)((gSP.viewport.y + gSP.viewport.vscale[1] * 2.0f) * ogl.getScaleY()) : (GLint)((pCurrentBuffer->m_height - (gSP.viewport.y + gSP.viewport.height)) * ogl.getScaleY());
glViewport(X, Y,
max((GLint)(gSP.viewport.width * ogl.getScaleX()), 0), max((GLint)(gSP.viewport.height * ogl.getScaleY()), 0) );
}
}
void OGLRender::_updateDepthUpdate() const
{
if (gDP.otherMode.depthUpdate != 0)
glDepthMask( TRUE );
else
glDepthMask( FALSE );
}
void OGLRender::_updateStates() const
{
OGLVideo & ogl = video();
CombinerInfo::get().update();
if (gSP.changed & CHANGED_GEOMETRYMODE)
_updateCullFace();
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) {
FrameBuffer * pCurrentBuffer = frameBufferList().getCurrent();
const u32 screenHeight = (pCurrentBuffer == NULL || pCurrentBuffer->m_height == 0) ? VI.height : pCurrentBuffer->m_height;
glScissor((GLint)(gDP.scissor.ulx * ogl.getScaleX()), (GLint)((screenHeight - gDP.scissor.lry) * ogl.getScaleY() + (pCurrentBuffer != NULL ? 0 : ogl.getHeightOffset())),
max((GLint)((gDP.scissor.lrx - gDP.scissor.ulx) * ogl.getScaleX()), 0), max((GLint)((gDP.scissor.lry - gDP.scissor.uly) * ogl.getScaleY()), 0) );
}
if (gSP.changed & CHANGED_VIEWPORT)
_updateViewport();
if ((gSP.changed & CHANGED_TEXTURE) || (gDP.changed & CHANGED_TILE) || (gDP.changed & CHANGED_TMEM)) {
//For some reason updating the texture cache on the first frame of LOZ:OOT causes a NULL Pointer exception...
if (currentCombiner() != NULL) {
if (currentCombiner()->usesT0())
textureCache().update(0);
else
textureCache().activateDummy(0);
//Note: enabling dummies makes some F-zero X textures flicker.... strange.
if (currentCombiner()->usesT1())
textureCache().update(1);
else
textureCache().activateDummy(1);
currentCombiner()->updateTextureInfo();
currentCombiner()->updateFBInfo();
}
}
if ((gDP.changed & CHANGED_RENDERMODE) || (gDP.changed & CHANGED_CYCLETYPE))
_setBlendMode();
gDP.changed &= CHANGED_TILE | CHANGED_TMEM;
gSP.changed &= CHANGED_TEXTURE | CHANGED_MATRIX;
}
void OGLRender::_setColorArray() const
{
if (currentCombiner()->usesShadeColor())
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)
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
#endif // GL_IMAGE_TEXTURES_SUPPORT
if (gSP.changed || gDP.changed)
_updateStates();
const bool updateArrays = m_renderState != rsTriangle;
if (updateArrays || CombinerInfo::get().isChanged()) {
m_renderState = rsTriangle;
_setColorArray();
_setTexCoordArrays();
}
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);
}
_updateCullFace();
_updateViewport();
glEnable(GL_SCISSOR_TEST);
currentCombiner()->updateRenderState();
} 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;
_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;
#ifdef __TRIBUFFER_OPT
_indexmap_clear();
#endif
}
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;
#ifdef __TRIBUFFER_OPT
_indexmap_clear();
#endif
}
void OGLRender::drawLine(int _v0, int _v1, float _width)
{
if (gSP.changed || gDP.changed)
_updateStates();
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)
{
if (gSP.changed || gDP.changed)
_updateStates();
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) {
glVertexAttrib4f(SC_COLOR, 0, 0, 0, 0);
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;
glVertexAttrib4fv(SC_COLOR, _pColor);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
_updateViewport();
}
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)
return true;
frameBufferList().getCurrent()->m_cleared = true;
if (!config.frameBufferEmulation.copyDepthToRDRAM)
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)
{
if (gSP.textureTile[0]->frameBuffer == frameBufferList().getCurrent())
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)
{
if (gSP.changed || gDP.changed)
_updateStates();
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))
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]) {
texST[0].s0 = _params.uls * cache.current[0]->shiftScaleS - gSP.textureTile[0]->fuls;
texST[0].t0 = _params.ult * cache.current[0]->shiftScaleT - gSP.textureTile[0]->fult;
texST[0].s1 = (_params.lrs + 1.0f) * cache.current[0]->shiftScaleS - gSP.textureTile[0]->fuls;
texST[0].t1 = (_params.lrt + 1.0f) * cache.current[0]->shiftScaleT - gSP.textureTile[0]->fult;
if ((cache.current[0]->maskS) && !(cache.current[0]->mirrorS) && (fmod(texST[0].s0, cache.current[0]->width) == 0.0f)) {
texST[0].s1 -= texST[0].s0;
texST[0].s0 = 0.0f;
}
if ((cache.current[0]->maskT) && !(cache.current[0]->mirrorT) && (fmod(texST[0].t0, cache.current[0]->height) == 0.0f)) {
texST[0].t1 -= texST[0].t0;
texST[0].t0 = 0.0f;
}
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]) {
texST[1].s0 = _params.uls * cache.current[1]->shiftScaleS - gSP.textureTile[1]->fuls;
texST[1].t0 = _params.ult * cache.current[1]->shiftScaleT - gSP.textureTile[1]->fult;
texST[1].s1 = (_params.lrs + 1.0f) * cache.current[1]->shiftScaleS - gSP.textureTile[1]->fuls;
texST[1].t1 = (_params.lrt + 1.0f) * cache.current[1]->shiftScaleT - gSP.textureTile[1]->fult;
if ((cache.current[1]->maskS) && (fmod(texST[1].s0, cache.current[1]->width) == 0.0f) && !(cache.current[1]->mirrorS)) {
texST[1].s1 -= texST[1].s0;
texST[1].s0 = 0.0f;
}
if ((cache.current[1]->maskT) && (fmod(texST[1].t0, cache.current[1]->height) == 0.0f) && !(cache.current[1]->mirrorT)) {
texST[1].t1 -= texST[1].t0;
texST[1].t0 = 0.0f;
}
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;
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
_updateViewport();
}
void OGLRender::drawText(const char *_pText, float x, float y)
{
m_renderState = rsNone;
TextDrawer::get().renderText(_pText, x, y);
}
void OGLRender::clearDepthBuffer()
{
if (config.frameBufferEmulation.enable && frameBufferList().getCurrent() == NULL)
return;
depthBufferList().clearBuffer();
_updateStates();
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()
{
#ifdef GL_IMAGE_TEXTURES_SUPPORT
const char *version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
const u32 uVersion = atol(version);
assert(uVersion >= 3 && "Plugin requires GL version 3 or higher.");
GLint majorVersion = 0, minorVersion = 0;
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
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()
{
glEnable( GL_CULL_FACE );
glEnableVertexAttribArray( SC_POSITION );
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_ALWAYS );
glDepthMask( GL_FALSE );
glEnable( GL_SCISSOR_TEST );
if (config.frameBufferEmulation.N64DepthCompare) {
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;
#ifdef __TRIBUFFER_OPT
_indexmap_init();
#endif
}
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);
//OutputDebugStringW(wbuf);
}
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;
#ifdef __TRIBUFFER_OPT
void OGLRender::_indexmap_init()
{
memset(triangles.indexmapinv, 0xFF, VERTBUFF_SIZE*sizeof(u32));
for(int i = 0; i<INDEXMAP_SIZE; ++i) {
triangles.indexmap[i] = i;
//triangles.indexmapinv[i] = i;
}
triangles.indexmap_prev = -1;
triangles.indexmap_nomap = 0;
}
void OGLRender::_indexmap_clear()
{
memset(triangles.indexmapinv, 0xFF, VERTBUFF_SIZE * sizeof(u32));
for(int i = 0; i < INDEXMAP_SIZE; ++i)
triangles.indexmapinv[triangles.indexmap[i]] = i;
}
u32 OGLRender::_indexmap_findunused(u32 num)
{
u32 c = 0;
u32 i = std::min(triangles.indexmap_prev + 1, VERTBUFF_SIZE - 1);
u32 n = 0;
while(n < VERTBUFF_SIZE) {
c = (triangles.indexmapinv[i] == 0xFFFFFFFF) ? (c + 1) : 0;
if ((c == num) && (i < (VERTBUFF_SIZE - num)))
break;
i = i + 1;
if (i >= VERTBUFF_SIZE)
{i = 0; c = 0;}
++n;
}
return (c == num) ? (i-num+1) : (0xFFFFFFFF);
}
void OGLRender::indexmapUndo()
{
SPVertex tmp[INDEXMAP_SIZE];
memset(triangles.indexmapinv, 0xFF, VERTBUFF_SIZE * sizeof(u32));
for(int i=0; i < INDEXMAP_SIZE; ++i) {
u32 ind = triangles.indexmap[i];
tmp[i] = triangles.vertices[ind];
triangles.indexmap[i] = i;
triangles.indexmapinv[i] = i;
}
memcpy(triangles.vertices, tmp, INDEXMAP_SIZE * sizeof(SPVertex));
triangles.indexmap_nomap = 1;
}
u32 OGLRender::getIndexmapNew(u32 _index, u32 _num)
{
u32 ind;
//test to see if unmapped
u32 unmapped = 1;
for(int i = 0; i < _num; ++i) {
if (triangles.indexmap[i] != 0xFFFFFFFF) {
unmapped = 0;
break;
}
}
if (unmapped)
ind = _index;
else {
ind = _indexmap_findunused(_num);
//no more room in buffer....
if (ind > VERTBUFF_SIZE) {
drawTriangles();
ind = _indexmap_findunused(_num);
//OK the indices are spread so sparsely, we cannot find a num element block.
if (ind > VERTBUFF_SIZE) {
indexmapUndo();
ind = _indexmap_findunused(_num);
if (ind > VERTBUFF_SIZE) {
LOG(LOG_ERROR, "Could not allocate %i indices\n", _num);
LOG(LOG_VERBOSE, "indexmap=[");
for(int i=0;i<INDEXMAP_SIZE;i++)
LOG(LOG_VERBOSE, "%i,", triangles.indexmap[i]);
LOG(LOG_VERBOSE, "]\n");
LOG(LOG_VERBOSE, "indexmapinv=[");
for(int i=0;i<VERTBUFF_SIZE;i++)
LOG(LOG_VERBOSE, "%i,", triangles.indexmapinv[i]);
LOG(LOG_VERBOSE, "]\n");
}
return ind;
}
}
}
for(int i = 0; i < _num; ++i) {
triangles.indexmap[_index + i] = ind + i;
triangles.indexmapinv[ind + i] = _index + i;
}
triangles.indexmap_prev = ind + _num - 1;
triangles.indexmap_nomap = 0;
return ind;
}
#endif // __TRIBUFFER_OPT