2018-05-15 13:22:26 +00:00
|
|
|
#include "glcache.h"
|
2013-12-19 17:10:14 +00:00
|
|
|
#include "rend/rend.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
/*
|
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
Drawing and related state management
|
2013-12-19 17:10:14 +00:00
|
|
|
Takes vertex, textures and renders to the currently set up target
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
const static u32 CullMode[]=
|
|
|
|
{
|
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
GL_NONE, //0 No culling No culling
|
|
|
|
GL_NONE, //1 Cull if Small Cull if ( |det| < fpu_cull_val )
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
GL_FRONT, //2 Cull if Negative Cull if ( |det| < 0 ) or ( |det| < fpu_cull_val )
|
|
|
|
GL_BACK, //3 Cull if Positive Cull if ( |det| > 0 ) or ( |det| < fpu_cull_val )
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
|
|
|
const static u32 Zfunction[]=
|
|
|
|
{
|
2013-12-24 00:56:44 +00:00
|
|
|
GL_NEVER, //GL_NEVER, //0 Never
|
|
|
|
GL_LESS, //GL_LESS/*EQUAL*/, //1 Less
|
|
|
|
GL_EQUAL, //GL_EQUAL, //2 Equal
|
|
|
|
GL_LEQUAL, //GL_LEQUAL, //3 Less Or Equal
|
|
|
|
GL_GREATER, //GL_GREATER/*EQUAL*/, //4 Greater
|
|
|
|
GL_NOTEQUAL, //GL_NOTEQUAL, //5 Not Equal
|
|
|
|
GL_GEQUAL, //GL_GEQUAL, //6 Greater Or Equal
|
|
|
|
GL_ALWAYS, //GL_ALWAYS, //7 Always
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2013-12-24 00:56:44 +00:00
|
|
|
0 Zero (0, 0, 0, 0)
|
|
|
|
1 One (1, 1, 1, 1)
|
|
|
|
2 Dither Color (OR, OG, OB, OA)
|
|
|
|
3 Inverse Dither Color (1-OR, 1-OG, 1-OB, 1-OA)
|
|
|
|
4 SRC Alpha (SA, SA, SA, SA)
|
|
|
|
5 Inverse SRC Alpha (1-SA, 1-SA, 1-SA, 1-SA)
|
|
|
|
6 DST Alpha (DA, DA, DA, DA)
|
|
|
|
7 Inverse DST Alpha (1-DA, 1-DA, 1-DA, 1-DA)
|
2013-12-19 17:10:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
const static u32 DstBlendGL[] =
|
|
|
|
{
|
|
|
|
GL_ZERO,
|
|
|
|
GL_ONE,
|
|
|
|
GL_SRC_COLOR,
|
|
|
|
GL_ONE_MINUS_SRC_COLOR,
|
|
|
|
GL_SRC_ALPHA,
|
|
|
|
GL_ONE_MINUS_SRC_ALPHA,
|
|
|
|
GL_DST_ALPHA,
|
|
|
|
GL_ONE_MINUS_DST_ALPHA
|
|
|
|
};
|
|
|
|
|
|
|
|
const static u32 SrcBlendGL[] =
|
|
|
|
{
|
|
|
|
GL_ZERO,
|
|
|
|
GL_ONE,
|
|
|
|
GL_DST_COLOR,
|
|
|
|
GL_ONE_MINUS_DST_COLOR,
|
|
|
|
GL_SRC_ALPHA,
|
|
|
|
GL_ONE_MINUS_SRC_ALPHA,
|
|
|
|
GL_DST_ALPHA,
|
|
|
|
GL_ONE_MINUS_DST_ALPHA
|
|
|
|
};
|
|
|
|
|
2018-05-17 09:17:51 +00:00
|
|
|
extern int screen_width;
|
|
|
|
extern int screen_height;
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
PipelineShader* CurrentShader;
|
|
|
|
u32 gcflip;
|
2019-02-14 11:40:17 +00:00
|
|
|
static GLuint g_previous_frame_tex;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-10-04 10:32:26 +00:00
|
|
|
s32 SetTileClip(u32 val, GLint uniform)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-05-02 13:41:42 +00:00
|
|
|
if (!settings.rend.Clipping)
|
|
|
|
return 0;
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
u32 clipmode=val>>28;
|
|
|
|
s32 clip_mode;
|
|
|
|
if (clipmode<2)
|
|
|
|
{
|
2013-12-24 00:56:44 +00:00
|
|
|
clip_mode=0; //always passes
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else if (clipmode&1)
|
2018-05-02 13:41:42 +00:00
|
|
|
clip_mode=-1; //render stuff outside the region
|
2013-12-19 17:10:14 +00:00
|
|
|
else
|
2018-05-02 13:41:42 +00:00
|
|
|
clip_mode=1; //render stuff inside the region
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
float csx=0,csy=0,cex=0,cey=0;
|
|
|
|
|
|
|
|
|
|
|
|
csx=(float)(val&63);
|
|
|
|
cex=(float)((val>>6)&63);
|
|
|
|
csy=(float)((val>>12)&31);
|
|
|
|
cey=(float)((val>>17)&31);
|
|
|
|
csx=csx*32;
|
|
|
|
cex=cex*32 +32;
|
|
|
|
csy=csy*32;
|
|
|
|
cey=cey*32 +32;
|
|
|
|
|
2018-05-02 13:41:42 +00:00
|
|
|
if (csx <= 0 && csy <= 0 && cex >= 640 && cey >= 480)
|
2013-12-19 17:10:14 +00:00
|
|
|
return 0;
|
2018-07-29 15:31:14 +00:00
|
|
|
|
2018-10-04 10:32:26 +00:00
|
|
|
if (uniform >= 0 && clip_mode)
|
2018-07-30 16:57:32 +00:00
|
|
|
{
|
|
|
|
if (!pvrrc.isRTT)
|
|
|
|
{
|
|
|
|
csx /= scale_x;
|
|
|
|
csy /= scale_y;
|
|
|
|
cex /= scale_x;
|
|
|
|
cey /= scale_y;
|
2019-04-09 13:18:48 +00:00
|
|
|
float dc2s_scale_h;
|
|
|
|
float ds2s_offs_x;
|
|
|
|
float screen_stretching = settings.rend.ScreenStretching / 100.f;
|
|
|
|
|
|
|
|
if (settings.rend.Rotate90)
|
|
|
|
{
|
|
|
|
float t = cex;
|
|
|
|
cex = cey;
|
|
|
|
cey = 640 - csx;
|
|
|
|
csx = csy;
|
|
|
|
csy = 640 - t;
|
|
|
|
dc2s_scale_h = screen_height / 640.0f;
|
|
|
|
ds2s_offs_x = (screen_width - dc2s_scale_h * 480.0 * screen_stretching) / 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float t = cey;
|
|
|
|
cey = 480 - csy;
|
|
|
|
csy = 480 - t;
|
|
|
|
dc2s_scale_h = screen_height / 480.0f;
|
|
|
|
ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2;
|
|
|
|
}
|
|
|
|
csx = csx * dc2s_scale_h * screen_stretching + ds2s_offs_x;
|
|
|
|
cex = cex * dc2s_scale_h * screen_stretching + ds2s_offs_x;
|
2018-05-17 09:17:51 +00:00
|
|
|
csy = csy * dc2s_scale_h;
|
|
|
|
cey = cey * dc2s_scale_h;
|
|
|
|
}
|
2019-03-08 19:06:17 +00:00
|
|
|
else if (!settings.rend.RenderToTextureBuffer)
|
2018-09-05 13:07:30 +00:00
|
|
|
{
|
|
|
|
csx *= settings.rend.RenderToTextureUpscale;
|
|
|
|
csy *= settings.rend.RenderToTextureUpscale;
|
|
|
|
cex *= settings.rend.RenderToTextureUpscale;
|
|
|
|
cey *= settings.rend.RenderToTextureUpscale;
|
|
|
|
}
|
2018-10-04 10:32:26 +00:00
|
|
|
glUniform4f(uniform, csx, csy, cex, cey);
|
2018-05-17 09:17:51 +00:00
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
return clip_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetCull(u32 CulliMode)
|
|
|
|
{
|
|
|
|
if (CullMode[CulliMode]==GL_NONE)
|
|
|
|
{
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Disable(GL_CULL_FACE);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Enable(GL_CULL_FACE);
|
|
|
|
glcache.CullFace(CullMode[CulliMode]); //GL_FRONT/GL_BACK, ...
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-16 17:34:45 +00:00
|
|
|
static void SetTextureRepeatMode(GLuint dir, u32 clamp, u32 mirror)
|
|
|
|
{
|
|
|
|
if (clamp)
|
|
|
|
glcache.TexParameteri(GL_TEXTURE_2D, dir, GL_CLAMP_TO_EDGE);
|
|
|
|
else
|
|
|
|
glcache.TexParameteri(GL_TEXTURE_2D, dir, mirror ? GL_MIRRORED_REPEAT : GL_REPEAT);
|
|
|
|
}
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
template <u32 Type, bool SortingEnabled>
|
|
|
|
__forceinline
|
|
|
|
void SetGPState(const PolyParam* gp,u32 cflip=0)
|
|
|
|
{
|
2018-07-03 18:36:13 +00:00
|
|
|
if (gp->pcw.Texture && gp->tsp.FilterMode > 1)
|
|
|
|
{
|
|
|
|
ShaderUniforms.trilinear_alpha = 0.25 * (gp->tsp.MipMapD & 0x3);
|
|
|
|
if (gp->tsp.FilterMode == 2)
|
|
|
|
// Trilinear pass A
|
|
|
|
ShaderUniforms.trilinear_alpha = 1.0 - ShaderUniforms.trilinear_alpha;
|
|
|
|
}
|
|
|
|
else
|
2018-11-23 21:33:51 +00:00
|
|
|
ShaderUniforms.trilinear_alpha = 1.f;
|
2018-07-03 18:36:13 +00:00
|
|
|
|
2018-09-12 15:50:42 +00:00
|
|
|
bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff);
|
2019-04-04 17:26:15 +00:00
|
|
|
int fog_ctrl = settings.rend.Fog ? gp->tsp.FogCtrl : 2;
|
2018-09-12 15:50:42 +00:00
|
|
|
|
2019-04-04 17:08:21 +00:00
|
|
|
CurrentShader = GetProgram(Type == ListType_Punch_Through ? 1 : 0,
|
|
|
|
SetTileClip(gp->tileclip, -1) + 1,
|
|
|
|
gp->pcw.Texture,
|
|
|
|
gp->tsp.UseAlpha,
|
|
|
|
gp->tsp.IgnoreTexA,
|
|
|
|
gp->tsp.ShadInstr,
|
|
|
|
gp->pcw.Offset,
|
2019-04-04 17:26:15 +00:00
|
|
|
fog_ctrl,
|
2019-04-04 17:08:21 +00:00
|
|
|
gp->pcw.Gouraud,
|
|
|
|
gp->tcw.PixelFmt == PixelBumpMap,
|
|
|
|
color_clamp,
|
|
|
|
ShaderUniforms.trilinear_alpha != 1.f);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2019-04-04 17:08:21 +00:00
|
|
|
glcache.UseProgram(CurrentShader->program);
|
|
|
|
if (CurrentShader->trilinear_alpha != -1)
|
|
|
|
glUniform1f(CurrentShader->trilinear_alpha, ShaderUniforms.trilinear_alpha);
|
|
|
|
|
2018-10-04 10:32:26 +00:00
|
|
|
SetTileClip(gp->tileclip, CurrentShader->pp_ClipTest);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
//This bit control which pixels are affected
|
2013-12-19 17:10:14 +00:00
|
|
|
//by modvols
|
|
|
|
const u32 stencil=(gp->pcw.Shadow!=0)?0x80:0x0;
|
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.StencilFunc(GL_ALWAYS,stencil,stencil);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-16 17:34:45 +00:00
|
|
|
glcache.BindTexture(GL_TEXTURE_2D, gp->texid == -1 ? 0 : gp->texid);
|
2018-05-11 13:29:24 +00:00
|
|
|
|
2018-05-16 17:34:45 +00:00
|
|
|
SetTextureRepeatMode(GL_TEXTURE_WRAP_S, gp->tsp.ClampU, gp->tsp.FlipU);
|
|
|
|
SetTextureRepeatMode(GL_TEXTURE_WRAP_T, gp->tsp.ClampV, gp->tsp.FlipV);
|
|
|
|
|
|
|
|
//set texture filter mode
|
|
|
|
if (gp->tsp.FilterMode == 0)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-05-16 17:34:45 +00:00
|
|
|
//disable filtering, mipmaps
|
|
|
|
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//bilinear filtering
|
|
|
|
//PowerVR supports also trilinear via two passes, but we ignore that for now
|
|
|
|
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (gp->tcw.MipMapped && settings.rend.UseMipmaps) ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
|
|
|
|
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
2014-05-02 00:43:34 +00:00
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
if (Type==ListType_Translucent)
|
|
|
|
{
|
|
|
|
glcache.Enable(GL_BLEND);
|
|
|
|
glcache.BlendFunc(SrcBlendGL[gp->tsp.SrcInstr],DstBlendGL[gp->tsp.DstInstr]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
glcache.Disable(GL_BLEND);
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
//set cull mode !
|
|
|
|
//cflip is required when exploding triangles for triangle sorting
|
|
|
|
//gcflip is global clip flip, needed for when rendering to texture due to mirrored Y direction
|
|
|
|
SetCull(gp->isp.CullMode^cflip^gcflip);
|
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
//set Z mode, only if required
|
|
|
|
if (Type == ListType_Punch_Through || (Type == ListType_Translucent && SortingEnabled))
|
2018-05-20 19:41:31 +00:00
|
|
|
{
|
2018-06-30 10:33:11 +00:00
|
|
|
glcache.DepthFunc(GL_GEQUAL);
|
2018-05-20 19:41:31 +00:00
|
|
|
}
|
2018-05-15 13:22:26 +00:00
|
|
|
else
|
2018-05-20 19:41:31 +00:00
|
|
|
{
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.DepthFunc(Zfunction[gp->isp.DepthMode]);
|
2018-05-20 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
#if TRIG_SORT
|
2018-05-15 13:22:26 +00:00
|
|
|
if (SortingEnabled)
|
2018-05-20 19:41:31 +00:00
|
|
|
glcache.DepthMask(GL_FALSE);
|
2018-05-15 13:22:26 +00:00
|
|
|
else
|
2013-12-19 17:10:14 +00:00
|
|
|
#endif
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.DepthMask(!gp->isp.ZWriteDis);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <u32 Type, bool SortingEnabled>
|
2018-05-10 19:28:20 +00:00
|
|
|
void DrawList(const List<PolyParam>& gply, int first, int count)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-05-10 19:28:20 +00:00
|
|
|
PolyParam* params = &gply.head()[first];
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (count==0)
|
|
|
|
return;
|
|
|
|
//we want at least 1 PParam
|
|
|
|
|
|
|
|
|
|
|
|
//set some 'global' modes for all primitives
|
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Enable(GL_STENCIL_TEST);
|
|
|
|
glcache.StencilFunc(GL_ALWAYS,0,0);
|
|
|
|
glcache.StencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
while(count-->0)
|
2013-12-24 00:56:44 +00:00
|
|
|
{
|
2013-12-19 17:10:14 +00:00
|
|
|
if (params->count>2) //this actually happens for some games. No idea why ..
|
2013-12-24 00:56:44 +00:00
|
|
|
{
|
2013-12-19 17:10:14 +00:00
|
|
|
SetGPState<Type,SortingEnabled>(params);
|
2018-12-13 21:26:25 +00:00
|
|
|
glDrawElements(GL_TRIANGLE_STRIP, params->count, gl.index_type,
|
|
|
|
(GLvoid*)(gl.get_index_size() * params->first)); glCheck();
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
params++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator<(const PolyParam &left, const PolyParam &right)
|
|
|
|
{
|
|
|
|
/* put any condition you want to sort on here */
|
|
|
|
return left.zvZ<right.zvZ;
|
|
|
|
//return left.zMin<right.zMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Sort based on min-z of each strip
|
2018-05-10 19:28:20 +00:00
|
|
|
void SortPParams(int first, int count)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-05-10 19:28:20 +00:00
|
|
|
if (pvrrc.verts.used() == 0 || count <= 1)
|
2013-12-19 17:10:14 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
Vertex* vtx_base=pvrrc.verts.head();
|
2018-12-13 09:57:51 +00:00
|
|
|
u32* idx_base = pvrrc.idx.head();
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-10 19:28:20 +00:00
|
|
|
PolyParam* pp = &pvrrc.global_param_tr.head()[first];
|
|
|
|
PolyParam* pp_end = pp + count;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
while(pp!=pp_end)
|
|
|
|
{
|
|
|
|
if (pp->count<2)
|
|
|
|
{
|
|
|
|
pp->zvZ=0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-13 09:57:51 +00:00
|
|
|
u32* idx = idx_base + pp->first;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
Vertex* vtx=vtx_base+idx[0];
|
|
|
|
Vertex* vtx_end=vtx_base + idx[pp->count-1]+1;
|
|
|
|
|
|
|
|
u32 zv=0xFFFFFFFF;
|
|
|
|
while(vtx!=vtx_end)
|
|
|
|
{
|
|
|
|
zv=min(zv,(u32&)vtx->z);
|
|
|
|
vtx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
pp->zvZ=(f32&)zv;
|
|
|
|
}
|
|
|
|
pp++;
|
|
|
|
}
|
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
std::stable_sort(pvrrc.global_param_tr.head() + first, pvrrc.global_param_tr.head() + first + count);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Vertex* vtx_sort_base;
|
|
|
|
|
|
|
|
|
|
|
|
struct IndexTrig
|
|
|
|
{
|
2018-12-13 09:57:51 +00:00
|
|
|
u32 id[3];
|
2013-12-19 17:10:14 +00:00
|
|
|
u16 pid;
|
|
|
|
f32 z;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct SortTrigDrawParam
|
|
|
|
{
|
|
|
|
PolyParam* ppid;
|
2018-12-13 09:57:51 +00:00
|
|
|
u32 first;
|
|
|
|
u32 count;
|
2013-12-19 17:10:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
float min3(float v0,float v1,float v2)
|
|
|
|
{
|
|
|
|
return min(min(v0,v1),v2);
|
|
|
|
}
|
|
|
|
|
|
|
|
float max3(float v0,float v1,float v2)
|
|
|
|
{
|
|
|
|
return max(max(v0,v1),v2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-13 09:57:51 +00:00
|
|
|
float minZ(Vertex* v, u32* mod)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
return min(min(v[mod[0]].z,v[mod[1]].z),v[mod[2]].z);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator<(const IndexTrig &left, const IndexTrig &right)
|
|
|
|
{
|
|
|
|
return left.z<right.z;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
|
|
|
|
Per triangle sorting experiments
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
//approximate the triangle area
|
2013-12-19 17:10:14 +00:00
|
|
|
float area_x2(Vertex* v)
|
|
|
|
{
|
|
|
|
return 2/3*fabs( (v[0].x-v[2].x)*(v[1].y-v[0].y) - (v[0].x-v[1].x)*(v[2].y-v[0].y)) ;
|
|
|
|
}
|
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
//approximate the distance ^2
|
2013-12-19 17:10:14 +00:00
|
|
|
float distance_apprx(Vertex* a, Vertex* b)
|
|
|
|
{
|
|
|
|
float xd=a->x-b->x;
|
|
|
|
float yd=a->y-b->y;
|
|
|
|
|
|
|
|
return xd*xd+yd*yd;
|
|
|
|
}
|
|
|
|
|
|
|
|
//was good idea, but not really working ..
|
|
|
|
bool Intersect(Vertex* a, Vertex* b)
|
|
|
|
{
|
|
|
|
float a1=area_x2(a);
|
|
|
|
float a2=area_x2(b);
|
|
|
|
|
|
|
|
float d = distance_apprx(a,b);
|
|
|
|
|
|
|
|
return (a1+a1)>d;
|
|
|
|
}
|
|
|
|
|
|
|
|
//root for quick-union
|
|
|
|
u16 rid(vector<u16>& v, u16 id)
|
|
|
|
{
|
|
|
|
while(id!=v[id]) id=v[id];
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TrigBounds
|
|
|
|
{
|
|
|
|
float xs,xe;
|
|
|
|
float ys,ye;
|
|
|
|
float zs,ze;
|
|
|
|
};
|
|
|
|
|
|
|
|
//find 3d bounding box for triangle
|
|
|
|
TrigBounds bound(Vertex* v)
|
|
|
|
{
|
|
|
|
TrigBounds rv = { min(min(v[0].x,v[1].x),v[2].x), max(max(v[0].x,v[1].x),v[2].x),
|
|
|
|
min(min(v[0].y,v[1].y),v[2].y), max(max(v[0].y,v[1].y),v[2].y),
|
|
|
|
min(min(v[0].z,v[1].z),v[2].z), max(max(v[0].z,v[1].z),v[2].z),
|
|
|
|
};
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
//bounding box 2d intersection
|
|
|
|
bool Intersect(TrigBounds& a, TrigBounds& b)
|
|
|
|
{
|
|
|
|
return ( !(a.xe<b.xs || a.xs>b.xe) && !(a.ye<b.ys || a.ys>b.ye) /*&& !(a.ze<b.zs || a.zs>b.ze)*/ );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool operator<(const IndexTrig &left, const IndexTrig &right)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
TrigBounds l=bound(vtx_sort_base+left.id);
|
|
|
|
TrigBounds r=bound(vtx_sort_base+right.id);
|
|
|
|
|
|
|
|
if (!Intersect(l,r))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return (l.zs + l.ze) < (r.zs + r.ze);
|
|
|
|
}*/
|
|
|
|
|
|
|
|
return minZ(&vtx_sort_base[left.id])<minZ(&vtx_sort_base[right.id]);
|
|
|
|
}
|
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
//Not really working cuz of broken intersect
|
2013-12-19 17:10:14 +00:00
|
|
|
bool Intersect(const IndexTrig &left, const IndexTrig &right)
|
|
|
|
{
|
|
|
|
TrigBounds l=bound(vtx_sort_base+left.id);
|
|
|
|
TrigBounds r=bound(vtx_sort_base+right.id);
|
|
|
|
|
|
|
|
return Intersect(l,r);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//are two poly params the same?
|
|
|
|
bool PP_EQ(PolyParam* pp0, PolyParam* pp1)
|
|
|
|
{
|
|
|
|
return (pp0->pcw.full&PCW_DRAW_MASK)==(pp1->pcw.full&PCW_DRAW_MASK) && pp0->isp.full==pp1->isp.full && pp0->tcw.full==pp1->tcw.full && pp0->tsp.full==pp1->tsp.full && pp0->tileclip==pp1->tileclip;
|
|
|
|
}
|
|
|
|
|
|
|
|
static vector<SortTrigDrawParam> pidx_sort;
|
|
|
|
|
2018-12-13 09:57:51 +00:00
|
|
|
void fill_id(u32* d, Vertex* v0, Vertex* v1, Vertex* v2, Vertex* vb)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
d[0]=v0-vb;
|
|
|
|
d[1]=v1-vb;
|
|
|
|
d[2]=v2-vb;
|
|
|
|
}
|
|
|
|
|
2018-05-10 19:28:20 +00:00
|
|
|
void GenSorted(int first, int count)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
u32 tess_gen=0;
|
|
|
|
|
|
|
|
pidx_sort.clear();
|
|
|
|
|
2018-05-10 19:28:20 +00:00
|
|
|
if (pvrrc.verts.used() == 0 || count <= 1)
|
2013-12-19 17:10:14 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
Vertex* vtx_base=pvrrc.verts.head();
|
2018-12-13 09:57:51 +00:00
|
|
|
u32* idx_base = pvrrc.idx.head();
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-10 19:28:20 +00:00
|
|
|
PolyParam* pp_base = &pvrrc.global_param_tr.head()[first];
|
|
|
|
PolyParam* pp = pp_base;
|
|
|
|
PolyParam* pp_end = pp + count;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
Vertex* vtx_arr=vtx_base+idx_base[pp->first];
|
|
|
|
vtx_sort_base=vtx_base;
|
|
|
|
|
|
|
|
static u32 vtx_cnt;
|
|
|
|
|
|
|
|
int vtx_count=idx_base[pp_end[-1].first+pp_end[-1].count-1]-idx_base[pp->first];
|
|
|
|
if (vtx_count>vtx_cnt)
|
|
|
|
vtx_cnt=vtx_count;
|
|
|
|
|
|
|
|
#if PRINT_SORT_STATS
|
|
|
|
printf("TVTX: %d || %d\n",vtx_cnt,vtx_count);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (vtx_count<=0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
//make lists of all triangles, with their pid and vid
|
|
|
|
static vector<IndexTrig> lst;
|
|
|
|
|
|
|
|
lst.resize(vtx_count*4);
|
|
|
|
|
|
|
|
|
|
|
|
int pfsti=0;
|
|
|
|
|
|
|
|
while(pp!=pp_end)
|
|
|
|
{
|
|
|
|
u32 ppid=(pp-pp_base);
|
|
|
|
|
|
|
|
if (pp->count>2)
|
|
|
|
{
|
2018-12-13 09:57:51 +00:00
|
|
|
u32* idx = idx_base + pp->first;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
Vertex* vtx=vtx_base+idx[0];
|
|
|
|
Vertex* vtx_end=vtx_base + idx[pp->count-1]-1;
|
|
|
|
u32 flip=0;
|
|
|
|
while(vtx!=vtx_end)
|
|
|
|
{
|
|
|
|
Vertex* v0, * v1, * v2, * v3, * v4, * v5;
|
|
|
|
|
|
|
|
if (flip)
|
|
|
|
{
|
2018-07-06 18:02:43 +00:00
|
|
|
v0=&vtx[1];
|
|
|
|
v1=&vtx[0];
|
|
|
|
v2=&vtx[2];
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
v0=&vtx[0];
|
|
|
|
v1=&vtx[1];
|
|
|
|
v2=&vtx[2];
|
|
|
|
}
|
2018-05-15 13:22:26 +00:00
|
|
|
#if 0
|
2013-12-19 17:10:14 +00:00
|
|
|
if (settings.pvr.subdivide_transp)
|
|
|
|
{
|
|
|
|
u32 tess_x=(max3(v0->x,v1->x,v2->x)-min3(v0->x,v1->x,v2->x))/32;
|
|
|
|
u32 tess_y=(max3(v0->y,v1->y,v2->y)-min3(v0->y,v1->y,v2->y))/32;
|
|
|
|
|
|
|
|
if (tess_x==1) tess_x=0;
|
|
|
|
if (tess_y==1) tess_y=0;
|
|
|
|
|
|
|
|
//bool tess=(maxZ(v0,v1,v2)/minZ(v0,v1,v2))>=1.2;
|
|
|
|
|
|
|
|
if (tess_x + tess_y)
|
|
|
|
{
|
|
|
|
v3=pvrrc.verts.Append(3);
|
|
|
|
v4=v3+1;
|
|
|
|
v5=v4+1;
|
|
|
|
|
|
|
|
//xyz
|
|
|
|
for (int i=0;i<3;i++)
|
|
|
|
{
|
|
|
|
((float*)&v3->x)[i]=((float*)&v0->x)[i]*0.5f+((float*)&v2->x)[i]*0.5f;
|
|
|
|
((float*)&v4->x)[i]=((float*)&v0->x)[i]*0.5f+((float*)&v1->x)[i]*0.5f;
|
|
|
|
((float*)&v5->x)[i]=((float*)&v1->x)[i]*0.5f+((float*)&v2->x)[i]*0.5f;
|
|
|
|
}
|
|
|
|
|
|
|
|
//*TODO* Make it perspective correct
|
|
|
|
|
|
|
|
//uv
|
|
|
|
for (int i=0;i<2;i++)
|
|
|
|
{
|
|
|
|
((float*)&v3->u)[i]=((float*)&v0->u)[i]*0.5f+((float*)&v2->u)[i]*0.5f;
|
|
|
|
((float*)&v4->u)[i]=((float*)&v0->u)[i]*0.5f+((float*)&v1->u)[i]*0.5f;
|
|
|
|
((float*)&v5->u)[i]=((float*)&v1->u)[i]*0.5f+((float*)&v2->u)[i]*0.5f;
|
|
|
|
}
|
|
|
|
|
|
|
|
//color
|
2013-12-23 14:38:03 +00:00
|
|
|
for (int i=0;i<4;i++)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
v3->col[i]=v0->col[i]/2+v2->col[i]/2;
|
|
|
|
v4->col[i]=v0->col[i]/2+v1->col[i]/2;
|
|
|
|
v5->col[i]=v1->col[i]/2+v2->col[i]/2;
|
|
|
|
}
|
|
|
|
|
|
|
|
fill_id(lst[pfsti].id,v0,v3,v4,vtx_base);
|
|
|
|
lst[pfsti].pid= ppid ;
|
|
|
|
lst[pfsti].z = minZ(vtx_base,lst[pfsti].id);
|
|
|
|
pfsti++;
|
|
|
|
|
|
|
|
fill_id(lst[pfsti].id,v2,v3,v5,vtx_base);
|
|
|
|
lst[pfsti].pid= ppid ;
|
|
|
|
lst[pfsti].z = minZ(vtx_base,lst[pfsti].id);
|
|
|
|
pfsti++;
|
|
|
|
|
|
|
|
fill_id(lst[pfsti].id,v3,v4,v5,vtx_base);
|
|
|
|
lst[pfsti].pid= ppid ;
|
|
|
|
lst[pfsti].z = minZ(vtx_base,lst[pfsti].id);
|
|
|
|
pfsti++;
|
|
|
|
|
|
|
|
fill_id(lst[pfsti].id,v5,v4,v1,vtx_base);
|
|
|
|
lst[pfsti].pid= ppid ;
|
|
|
|
lst[pfsti].z = minZ(vtx_base,lst[pfsti].id);
|
|
|
|
pfsti++;
|
|
|
|
|
|
|
|
tess_gen+=3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fill_id(lst[pfsti].id,v0,v1,v2,vtx_base);
|
|
|
|
lst[pfsti].pid= ppid ;
|
|
|
|
lst[pfsti].z = minZ(vtx_base,lst[pfsti].id);
|
|
|
|
pfsti++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2018-05-15 13:22:26 +00:00
|
|
|
#endif
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
fill_id(lst[pfsti].id,v0,v1,v2,vtx_base);
|
|
|
|
lst[pfsti].pid= ppid ;
|
2018-07-07 06:46:29 +00:00
|
|
|
lst[pfsti].z = minZ(vtx_base,lst[pfsti].id);
|
2013-12-19 17:10:14 +00:00
|
|
|
pfsti++;
|
|
|
|
}
|
|
|
|
|
|
|
|
flip ^= 1;
|
|
|
|
|
|
|
|
vtx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 aused=pfsti;
|
|
|
|
|
|
|
|
lst.resize(aused);
|
|
|
|
|
|
|
|
//sort them
|
|
|
|
#if 1
|
|
|
|
std::stable_sort(lst.begin(),lst.end());
|
|
|
|
|
|
|
|
//Merge pids/draw cmds if two different pids are actually equal
|
|
|
|
if (true)
|
|
|
|
{
|
2013-12-25 17:47:51 +00:00
|
|
|
for (u32 k=1;k<aused;k++)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
if (lst[k].pid!=lst[k-1].pid)
|
|
|
|
{
|
|
|
|
if (PP_EQ(&pp_base[lst[k].pid],&pp_base[lst[k-1].pid]))
|
|
|
|
{
|
|
|
|
lst[k].pid=lst[k-1].pid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
//tries to optimise draw calls by reordering non-intersecting polygons
|
|
|
|
//uber slow and not very effective
|
|
|
|
{
|
|
|
|
int opid=lst[0].pid;
|
|
|
|
|
|
|
|
for (int k=1;k<aused;k++)
|
|
|
|
{
|
|
|
|
if (lst[k].pid!=opid)
|
|
|
|
{
|
|
|
|
if (opid>lst[k].pid)
|
|
|
|
{
|
|
|
|
//MOVE UP
|
|
|
|
for (int j=k;j>0 && lst[j].pid!=lst[j-1].pid && !Intersect(lst[j],lst[j-1]);j--)
|
|
|
|
{
|
|
|
|
swap(lst[j],lst[j-1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//move down
|
|
|
|
for (int j=k+1;j<aused && lst[j].pid!=lst[j-1].pid && !Intersect(lst[j],lst[j-1]);j++)
|
|
|
|
{
|
|
|
|
swap(lst[j],lst[j-1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
opid=lst[k].pid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//re-assemble them into drawing commands
|
2018-12-13 09:57:51 +00:00
|
|
|
static vector<u32> vidx_sort;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
vidx_sort.resize(aused*3);
|
|
|
|
|
|
|
|
int idx=-1;
|
|
|
|
|
2013-12-25 17:47:51 +00:00
|
|
|
for (u32 i=0; i<aused; i++)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
int pid=lst[i].pid;
|
2018-12-13 09:57:51 +00:00
|
|
|
u32* midx = lst[i].id;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
vidx_sort[i*3 + 0]=midx[0];
|
|
|
|
vidx_sort[i*3 + 1]=midx[1];
|
|
|
|
vidx_sort[i*3 + 2]=midx[2];
|
|
|
|
|
|
|
|
if (idx!=pid /* && !PP_EQ(&pp_base[pid],&pp_base[idx]) */ )
|
|
|
|
{
|
2018-12-13 09:57:51 +00:00
|
|
|
SortTrigDrawParam stdp = { pp_base + pid, i * 3, 0 };
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
if (idx!=-1)
|
|
|
|
{
|
|
|
|
SortTrigDrawParam* last=&pidx_sort[pidx_sort.size()-1];
|
|
|
|
last->count=stdp.first-last->first;
|
|
|
|
}
|
|
|
|
|
|
|
|
pidx_sort.push_back(stdp);
|
|
|
|
idx=pid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SortTrigDrawParam* stdp=&pidx_sort[pidx_sort.size()-1];
|
|
|
|
stdp->count=aused*3-stdp->first;
|
|
|
|
|
|
|
|
#if PRINT_SORT_STATS
|
2013-12-24 00:56:44 +00:00
|
|
|
printf("Reassembled into %d from %d\n",pidx_sort.size(),pp_end-pp_base);
|
2013-12-19 17:10:14 +00:00
|
|
|
#endif
|
|
|
|
|
2013-12-24 00:56:44 +00:00
|
|
|
//Upload to GPU if needed
|
2013-12-19 17:10:14 +00:00
|
|
|
if (pidx_sort.size())
|
|
|
|
{
|
|
|
|
//Bind and upload sorted index buffer
|
2018-05-15 14:09:50 +00:00
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs2); glCheck();
|
2018-12-13 21:26:25 +00:00
|
|
|
if (gl.index_type == GL_UNSIGNED_SHORT)
|
|
|
|
{
|
|
|
|
static bool overrun;
|
|
|
|
static List<u16> short_vidx;
|
|
|
|
if (short_vidx.daty != NULL)
|
|
|
|
short_vidx.Free();
|
|
|
|
short_vidx.Init(vidx_sort.size(), &overrun, NULL);
|
|
|
|
for (int i = 0; i < vidx_sort.size(); i++)
|
|
|
|
*(short_vidx.Append()) = vidx_sort[i];
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, short_vidx.bytes(), short_vidx.head(), GL_STREAM_DRAW);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, vidx_sort.size() * sizeof(u32), &vidx_sort[0], GL_STREAM_DRAW);
|
|
|
|
glCheck();
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
if (tess_gen) printf("Generated %.2fK Triangles !\n",tess_gen/1000.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-20 19:41:31 +00:00
|
|
|
void DrawSorted(bool multipass)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
//if any drawing commands, draw them
|
|
|
|
if (pidx_sort.size())
|
|
|
|
{
|
|
|
|
u32 count=pidx_sort.size();
|
|
|
|
|
|
|
|
{
|
|
|
|
//set some 'global' modes for all primitives
|
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Enable(GL_STENCIL_TEST);
|
|
|
|
glcache.StencilFunc(GL_ALWAYS,0,0);
|
|
|
|
glcache.StencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2013-12-25 17:47:51 +00:00
|
|
|
for (u32 p=0; p<count; p++)
|
|
|
|
{
|
2013-12-19 17:10:14 +00:00
|
|
|
PolyParam* params = pidx_sort[p].ppid;
|
|
|
|
if (pidx_sort[p].count>2) //this actually happens for some games. No idea why ..
|
|
|
|
{
|
|
|
|
SetGPState<ListType_Translucent,true>(params);
|
2018-12-13 21:26:25 +00:00
|
|
|
glDrawElements(GL_TRIANGLES, pidx_sort[p].count, gl.index_type,
|
|
|
|
(GLvoid*)(gl.get_index_size() * pidx_sort[p].first)); glCheck();
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
//Verify restriping -- only valid if no sort
|
|
|
|
int fs=pidx_sort[p].first;
|
|
|
|
|
2013-12-25 17:47:51 +00:00
|
|
|
for (u32 j=0; j<(params->count-2); j++)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2013-12-25 17:47:51 +00:00
|
|
|
for (u32 k=0; k<3; k++)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
verify(idx_base[params->first+j+k]==vidx_sort[fs++]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
verify(fs==(pidx_sort[p].first+pidx_sort[p].count));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
params++;
|
|
|
|
}
|
2018-05-22 13:47:02 +00:00
|
|
|
|
|
|
|
if (multipass && settings.rend.TranslucentPolygonDepthMask)
|
2018-05-20 19:41:31 +00:00
|
|
|
{
|
|
|
|
// Write to the depth buffer now. The next render pass might need it. (Cosmic Smash)
|
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
2018-05-22 13:47:02 +00:00
|
|
|
glcache.Disable(GL_BLEND);
|
|
|
|
|
2018-05-20 19:41:31 +00:00
|
|
|
glcache.StencilMask(0);
|
2018-05-22 13:47:02 +00:00
|
|
|
|
|
|
|
// We use the modifier volumes shader because it's fast. We don't need textures, etc.
|
|
|
|
glcache.UseProgram(gl.modvol_shader.program);
|
|
|
|
glUniform1f(gl.modvol_shader.sp_ShaderColor, 1.f);
|
|
|
|
|
|
|
|
glcache.DepthFunc(GL_GEQUAL);
|
|
|
|
glcache.DepthMask(GL_TRUE);
|
|
|
|
|
2018-05-20 19:41:31 +00:00
|
|
|
for (u32 p = 0; p < count; p++)
|
|
|
|
{
|
|
|
|
PolyParam* params = pidx_sort[p].ppid;
|
|
|
|
if (pidx_sort[p].count > 2 && !params->isp.ZWriteDis) {
|
2018-05-22 13:47:02 +00:00
|
|
|
// FIXME no clipping in modvol shader
|
|
|
|
//SetTileClip(gp->tileclip,true);
|
|
|
|
|
|
|
|
SetCull(params->isp.CullMode ^ gcflip);
|
|
|
|
|
2018-12-13 21:26:25 +00:00
|
|
|
glDrawElements(GL_TRIANGLES, pidx_sort[p].count, gl.index_type,
|
|
|
|
(GLvoid*)(gl.get_index_size() * pidx_sort[p].first));
|
2018-05-20 19:41:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
glcache.StencilMask(0xFF);
|
|
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
2018-05-20 12:16:43 +00:00
|
|
|
// Re-bind the previous index buffer for subsequent render passes
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//All pixels are in area 0 by default.
|
|
|
|
//If inside an 'in' volume, they are in area 1
|
|
|
|
//if inside an 'out' volume, they are in area 0
|
|
|
|
/*
|
|
|
|
Stencil bits:
|
|
|
|
bit 7: mv affected (must be preserved)
|
|
|
|
bit 1: current volume state
|
|
|
|
but 0: summary result (starts off as 0)
|
|
|
|
|
|
|
|
Lower 2 bits:
|
|
|
|
|
|
|
|
IN volume (logical OR):
|
|
|
|
00 -> 00
|
|
|
|
01 -> 01
|
|
|
|
10 -> 01
|
|
|
|
11 -> 01
|
|
|
|
|
|
|
|
Out volume (logical AND):
|
|
|
|
00 -> 00
|
|
|
|
01 -> 00
|
|
|
|
10 -> 00
|
|
|
|
11 -> 01
|
|
|
|
*/
|
2018-06-26 12:24:45 +00:00
|
|
|
void SetMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-06-26 12:24:45 +00:00
|
|
|
if (mv_mode == Xor)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-06-26 12:24:45 +00:00
|
|
|
// set states
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Enable(GL_DEPTH_TEST);
|
2018-06-26 12:24:45 +00:00
|
|
|
// write only bit 1
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.StencilMask(2);
|
2018-06-26 12:24:45 +00:00
|
|
|
// no stencil testing
|
|
|
|
glcache.StencilFunc(GL_ALWAYS, 0, 2);
|
|
|
|
// count the number of pixels in front of the Z buffer (xor zpass)
|
|
|
|
glcache.StencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
|
2018-05-15 13:22:26 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
// Cull mode needs to be set
|
2013-12-19 17:10:14 +00:00
|
|
|
SetCull(ispc.CullMode);
|
|
|
|
}
|
2018-06-26 12:24:45 +00:00
|
|
|
else if (mv_mode == Or)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-06-26 12:24:45 +00:00
|
|
|
// set states
|
|
|
|
glcache.Enable(GL_DEPTH_TEST);
|
|
|
|
// write only bit 1
|
|
|
|
glcache.StencilMask(2);
|
|
|
|
// no stencil testing
|
|
|
|
glcache.StencilFunc(GL_ALWAYS, 2, 2);
|
|
|
|
// Or'ing of all triangles
|
|
|
|
glcache.StencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
// Cull mode needs to be set
|
|
|
|
SetCull(ispc.CullMode);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Inclusion or Exclusion volume
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
// no depth test
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Disable(GL_DEPTH_TEST);
|
2018-06-26 12:24:45 +00:00
|
|
|
// write bits 1:0
|
2018-05-16 17:34:45 +00:00
|
|
|
glcache.StencilMask(3);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
if (mv_mode == Inclusion)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-05-16 17:34:45 +00:00
|
|
|
// Inclusion volume
|
2013-12-19 17:10:14 +00:00
|
|
|
//res : old : final
|
|
|
|
//0 : 0 : 00
|
|
|
|
//0 : 1 : 01
|
|
|
|
//1 : 0 : 01
|
|
|
|
//1 : 1 : 01
|
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
// if (1<=st) st=1; else st=0;
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.StencilFunc(GL_LEQUAL,1,3);
|
2018-06-26 12:24:45 +00:00
|
|
|
glcache.StencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-05-16 17:34:45 +00:00
|
|
|
// Exclusion volume
|
2013-12-19 17:10:14 +00:00
|
|
|
/*
|
|
|
|
I've only seen a single game use it, so i guess it doesn't matter ? (Zombie revenge)
|
2013-12-24 00:56:44 +00:00
|
|
|
(actually, i think there was also another, racing game)
|
2013-12-19 17:10:14 +00:00
|
|
|
*/
|
2018-05-16 17:34:45 +00:00
|
|
|
// The initial value for exclusion volumes is 1 so we need to invert the result before and'ing.
|
2013-12-19 17:10:14 +00:00
|
|
|
//res : old : final
|
|
|
|
//0 : 0 : 00
|
2018-05-16 17:34:45 +00:00
|
|
|
//0 : 1 : 01
|
2013-12-19 17:10:14 +00:00
|
|
|
//1 : 0 : 00
|
2018-05-16 17:34:45 +00:00
|
|
|
//1 : 1 : 00
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-16 17:34:45 +00:00
|
|
|
// if (1 == st) st = 1; else st = 0;
|
|
|
|
glcache.StencilFunc(GL_EQUAL, 1, 3);
|
2018-06-26 12:24:45 +00:00
|
|
|
glcache.StencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-26 21:51:57 +00:00
|
|
|
|
2018-10-04 08:58:33 +00:00
|
|
|
static void SetupMainVBO()
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2019-04-04 08:10:32 +00:00
|
|
|
#if !defined(GLES) || defined(_ANDROID)
|
2019-02-19 20:40:21 +00:00
|
|
|
if (gl.gl_major >= 3)
|
|
|
|
glBindVertexArray(gl.vbo.vao);
|
2019-04-04 08:10:32 +00:00
|
|
|
#endif
|
2018-05-15 14:09:50 +00:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.geometry); glCheck();
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs); glCheck();
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//setup vertex buffers attrib pointers
|
|
|
|
glEnableVertexAttribArray(VERTEX_POS_ARRAY); glCheck();
|
|
|
|
glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,x)); glCheck();
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(VERTEX_COL_BASE_ARRAY); glCheck();
|
|
|
|
glVertexAttribPointer(VERTEX_COL_BASE_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex,col)); glCheck();
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(VERTEX_COL_OFFS_ARRAY); glCheck();
|
|
|
|
glVertexAttribPointer(VERTEX_COL_OFFS_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex,spc)); glCheck();
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(VERTEX_UV_ARRAY); glCheck();
|
|
|
|
glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,u)); glCheck();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupModvolVBO()
|
|
|
|
{
|
2019-04-04 08:10:32 +00:00
|
|
|
#if !defined(GLES) || defined(_ANDROID)
|
2019-02-19 20:40:21 +00:00
|
|
|
if (gl.gl_major >= 3)
|
|
|
|
glBindVertexArray(gl.vbo.vao);
|
2019-04-04 08:10:32 +00:00
|
|
|
#endif
|
2018-05-15 14:09:50 +00:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.modvols); glCheck();
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//setup vertex buffers attrib pointers
|
|
|
|
glEnableVertexAttribArray(VERTEX_POS_ARRAY); glCheck();
|
|
|
|
glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(float)*3, (void*)0); glCheck();
|
|
|
|
|
|
|
|
glDisableVertexAttribArray(VERTEX_UV_ARRAY);
|
|
|
|
glDisableVertexAttribArray(VERTEX_COL_OFFS_ARRAY);
|
|
|
|
glDisableVertexAttribArray(VERTEX_COL_BASE_ARRAY);
|
|
|
|
}
|
2018-05-10 19:28:20 +00:00
|
|
|
void DrawModVols(int first, int count)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-05-15 13:22:26 +00:00
|
|
|
if (count == 0 || pvrrc.modtrig.used() == 0)
|
2013-12-19 17:10:14 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
SetupModvolVBO();
|
|
|
|
|
2019-02-18 16:42:07 +00:00
|
|
|
glcache.Disable(GL_BLEND);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.UseProgram(gl.modvol_shader.program);
|
2018-05-23 10:26:24 +00:00
|
|
|
glUniform1f(gl.modvol_shader.sp_ShaderColor, 1 - FPU_SHAD_SCALE.scale_factor / 256.f);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.DepthMask(GL_FALSE);
|
|
|
|
glcache.DepthFunc(GL_GREATER);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-16 17:34:45 +00:00
|
|
|
if(0)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
|
|
|
//simply draw the volumes -- for debugging
|
|
|
|
SetCull(0);
|
2018-05-10 19:28:20 +00:00
|
|
|
glDrawArrays(GL_TRIANGLES, first, count * 3);
|
2013-12-19 17:10:14 +00:00
|
|
|
SetupMainVBO();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-06-26 12:24:45 +00:00
|
|
|
//Full emulation
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
ModifierVolumeParam* params = &pvrrc.global_param_mvo.head()[first];
|
2018-05-04 16:18:04 +00:00
|
|
|
|
2018-06-26 13:38:55 +00:00
|
|
|
int mod_base = -1;
|
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
for (u32 cmv = 0; cmv < count; cmv++)
|
2013-12-19 17:10:14 +00:00
|
|
|
{
|
2018-06-26 12:24:45 +00:00
|
|
|
ModifierVolumeParam& param = params[cmv];
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
if (param.count == 0)
|
|
|
|
continue;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
u32 mv_mode = param.isp.DepthMode;
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 13:38:55 +00:00
|
|
|
if (mod_base == -1)
|
|
|
|
mod_base = param.first;
|
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
if (!param.isp.VolumeLast && mv_mode > 0)
|
|
|
|
SetMVS_Mode(Or, param.isp); // OR'ing (open volume or quad)
|
|
|
|
else
|
|
|
|
SetMVS_Mode(Xor, param.isp); // XOR'ing (closed volume)
|
2018-06-26 13:38:55 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
glDrawArrays(GL_TRIANGLES, param.first * 3, param.count * 3);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-06-26 12:24:45 +00:00
|
|
|
if (mv_mode == 1 || mv_mode == 2)
|
|
|
|
{
|
|
|
|
// Sum the area
|
|
|
|
SetMVS_Mode(mv_mode == 1 ? Inclusion : Exclusion, param.isp);
|
2018-06-26 13:38:55 +00:00
|
|
|
glDrawArrays(GL_TRIANGLES, mod_base * 3, (param.first + param.count - mod_base) * 3);
|
|
|
|
mod_base = -1;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
//disable culling
|
|
|
|
SetCull(0);
|
|
|
|
//enable color writes
|
|
|
|
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
|
|
|
|
|
|
|
|
//black out any stencil with '1'
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Enable(GL_BLEND);
|
|
|
|
glcache.BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Enable(GL_STENCIL_TEST);
|
|
|
|
glcache.StencilFunc(GL_EQUAL,0x81,0x81); //only pixels that are Modvol enabled, and in area 1
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//clear the stencil result bit
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.StencilMask(0x3); //write to lsb
|
|
|
|
glcache.StencilOp(GL_ZERO,GL_ZERO,GL_ZERO);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
//don't do depth testing
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Disable(GL_DEPTH_TEST);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
|
|
|
SetupMainVBO();
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
|
|
|
|
|
|
|
|
//Draw and blend
|
|
|
|
//glDrawArrays(GL_TRIANGLES,pvrrc.modtrig.used(),2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//restore states
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Enable(GL_DEPTH_TEST);
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DrawStrips()
|
|
|
|
{
|
|
|
|
SetupMainVBO();
|
|
|
|
//Draw the strips !
|
|
|
|
|
|
|
|
//We use sampler 0
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
|
2018-05-10 19:28:20 +00:00
|
|
|
RenderPass previous_pass = {0};
|
2018-05-14 10:48:22 +00:00
|
|
|
for (int render_pass = 0; render_pass < pvrrc.render_passes.used(); render_pass++) {
|
|
|
|
const RenderPass& current_pass = pvrrc.render_passes.head()[render_pass];
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-10 19:28:20 +00:00
|
|
|
//initial state
|
2018-05-15 13:22:26 +00:00
|
|
|
glcache.Enable(GL_DEPTH_TEST);
|
|
|
|
glcache.DepthMask(GL_TRUE);
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2018-05-10 19:28:20 +00:00
|
|
|
//Opaque
|
|
|
|
DrawList<ListType_Opaque,false>(pvrrc.global_param_op, previous_pass.op_count, current_pass.op_count - previous_pass.op_count);
|
|
|
|
|
|
|
|
//Alpha tested
|
|
|
|
DrawList<ListType_Punch_Through,false>(pvrrc.global_param_pt, previous_pass.pt_count, current_pass.pt_count - previous_pass.pt_count);
|
|
|
|
|
2018-05-23 12:31:11 +00:00
|
|
|
// Modifier volumes
|
2018-07-03 13:46:25 +00:00
|
|
|
if (settings.rend.ModifierVolumes)
|
|
|
|
DrawModVols(previous_pass.mvo_count, current_pass.mvo_count - previous_pass.mvo_count);
|
2018-05-10 19:28:20 +00:00
|
|
|
|
|
|
|
//Alpha blended
|
|
|
|
{
|
2018-06-30 10:33:11 +00:00
|
|
|
if (current_pass.autosort)
|
2018-08-20 13:51:55 +00:00
|
|
|
{
|
2013-12-19 17:10:14 +00:00
|
|
|
#if TRIG_SORT
|
2018-08-20 13:51:55 +00:00
|
|
|
GenSorted(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count);
|
2018-05-20 19:41:31 +00:00
|
|
|
DrawSorted(render_pass < pvrrc.render_passes.used() - 1);
|
2013-12-19 17:10:14 +00:00
|
|
|
#else
|
2018-08-20 13:51:55 +00:00
|
|
|
SortPParams(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count);
|
|
|
|
DrawList<ListType_Translucent,true>(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count);
|
2013-12-19 17:10:14 +00:00
|
|
|
#endif
|
2018-08-20 13:51:55 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
DrawList<ListType_Translucent,false>(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count);
|
2018-05-10 19:28:20 +00:00
|
|
|
}
|
|
|
|
previous_pass = current_pass;
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
2018-08-26 14:58:10 +00:00
|
|
|
|
2019-02-14 11:40:17 +00:00
|
|
|
static void DrawQuad(GLuint texId, float x, float y, float w, float h, float u0, float v0, float u1, float v1)
|
2018-08-26 14:58:10 +00:00
|
|
|
{
|
|
|
|
struct Vertex vertices[] = {
|
2019-02-14 11:40:17 +00:00
|
|
|
{ x, y + h, 1, { 255, 255, 255, 255 }, { 0, 0, 0, 0 }, u0, v1 },
|
|
|
|
{ x, y, 1, { 255, 255, 255, 255 }, { 0, 0, 0, 0 }, u0, v0 },
|
|
|
|
{ x + w, y + h, 1, { 255, 255, 255, 255 }, { 0, 0, 0, 0 }, u1, v1 },
|
|
|
|
{ x + w, y, 1, { 255, 255, 255, 255 }, { 0, 0, 0, 0 }, u1, v0 },
|
2018-08-26 14:58:10 +00:00
|
|
|
};
|
|
|
|
GLushort indices[] = { 0, 1, 2, 1, 3 };
|
|
|
|
|
|
|
|
glcache.Disable(GL_SCISSOR_TEST);
|
|
|
|
glcache.Disable(GL_DEPTH_TEST);
|
|
|
|
glcache.Disable(GL_STENCIL_TEST);
|
|
|
|
glcache.Disable(GL_CULL_FACE);
|
|
|
|
glcache.Disable(GL_BLEND);
|
|
|
|
|
|
|
|
ShaderUniforms.trilinear_alpha = 1.0;
|
|
|
|
|
2019-04-04 17:08:21 +00:00
|
|
|
PipelineShader *shader = GetProgram(0, 1, 1, 0, 1, 0, 0, 2, false, false, false, false);
|
|
|
|
glcache.UseProgram(shader->program);
|
2018-08-26 14:58:10 +00:00
|
|
|
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2019-02-14 11:40:17 +00:00
|
|
|
glcache.BindTexture(GL_TEXTURE_2D, texId);
|
2018-08-26 14:58:10 +00:00
|
|
|
|
2018-09-03 13:05:37 +00:00
|
|
|
SetupMainVBO();
|
2018-08-26 14:58:10 +00:00
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW);
|
2018-09-03 13:05:37 +00:00
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STREAM_DRAW);
|
2018-08-26 14:58:10 +00:00
|
|
|
|
|
|
|
glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_SHORT, (void *)0);
|
2019-02-14 11:40:17 +00:00
|
|
|
}
|
2018-08-26 14:58:10 +00:00
|
|
|
|
2019-02-14 11:40:17 +00:00
|
|
|
void DrawFramebuffer(float w, float h)
|
|
|
|
{
|
|
|
|
DrawQuad(fbTextureId, 0, 0, 640.f, 480.f, 0, 0, 1, 1);
|
2018-08-26 14:58:10 +00:00
|
|
|
glcache.DeleteTextures(1, &fbTextureId);
|
|
|
|
fbTextureId = 0;
|
|
|
|
}
|
2019-02-14 11:40:17 +00:00
|
|
|
|
2019-02-19 10:36:59 +00:00
|
|
|
bool render_output_framebuffer()
|
2019-02-14 11:40:17 +00:00
|
|
|
{
|
2019-04-07 22:21:06 +00:00
|
|
|
glcache.Disable(GL_SCISSOR_TEST);
|
|
|
|
if (gl.gl_major < 3)
|
|
|
|
{
|
|
|
|
glViewport(0, 0, screen_width, screen_height);
|
|
|
|
if (gl.ofbo.tex == 0)
|
|
|
|
return false;
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
float scl = 480.f / screen_height;
|
|
|
|
float tx = (screen_width * scl - 640.f) / 2;
|
|
|
|
DrawQuad(gl.ofbo.tex, -tx, 0, 640.f + tx * 2, 480.f, 0, 1, 1, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (gl.ofbo.fbo == 0)
|
|
|
|
return false;
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
glBlitFramebuffer(0, 0, gl.ofbo.width, gl.ofbo.height,
|
|
|
|
0, 0, screen_width, screen_height,
|
|
|
|
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
}
|
2019-02-19 10:36:59 +00:00
|
|
|
return true;
|
2019-02-14 11:40:17 +00:00
|
|
|
}
|