flycast/newdc/rend/gles/gldraw.cpp

1144 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "gles.h"
#include "rend/rend.h"
#include <algorithm>
/*
Drawing and related state managment
Takes vertex, textures and renders to the currently set up target
*/
//Uncomment this to disable the stencil work around
//Seems like there's a bug either on the wrapper, or nvogl making
//stencil not work properly (requiring some double calls to get proper results)
//#define NO_STENCIL_WORKAROUND
const static u32 CullMode[]=
{
GL_NONE, //0 No culling no culling
GL_NONE, //1 Cull if Small Cull if ( |det| < fpu_cull_val )
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 )
};
const static u32 Zfunction[]=
{
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
};
/*
0 Zero (0, 0, 0, 0)
1 One (1, 1, 1, 1)
2 Other Color (OR, OG, OB, OA)
3 Inverse Other 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)
*/
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
};
PipelineShader* CurrentShader;
u32 gcflip;
static struct
{
TSP tsp;
TCW tcw;
PCW pcw;
ISP_TSP isp;
u32 clipmode;
u32 texture_enabled;
u32 stencil_modvol_on;
u32 program;
GLuint texture;
void Reset(const PolyParam* gp)
{
program=~0;
texture=~0;
tsp.full = ~gp->tsp.full;
tcw.full = ~gp->tcw.full;
pcw.full = ~gp->pcw.full;
isp.full = ~gp->isp.full;
clipmode=0xFFFFFFFF;
texture_enabled=~gp->pcw.Texture;
stencil_modvol_on=false;
}
} cache;
s32 SetTileClip(u32 val, bool set)
{
/*
if (set)
{
if (cache.clipmode==val)
return clip_mode;
cache.clipmode=val;
}*/
u32 clipmode=val>>28;
s32 clip_mode;
if (clipmode<2)
{
clip_mode=0; //always passes
}
else if (clipmode&1)
clip_mode=-1; //render stuff inside the region
else
clip_mode=1; //render stuff outside the region
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;
if (csx==0 && csy==0 && cex==640 && cey==480)
return 0;
if (set)
glUniform4f(CurrentShader->pp_ClipTest,-csx,-csy,-cex,-cey);
return clip_mode;
}
void SetCull(u32 CulliMode)
{
if (CullMode[CulliMode]==GL_NONE)
{
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
glCullFace(CullMode[CulliMode]); //GL_FRONT/GL_BACK, ...
}
}
template <u32 Type, bool SortingEnabled>
__forceinline
void SetGPState(const PolyParam* gp,u32 cflip=0)
{
//has to preserve cache_tsp/cache_isp
//can freely use cache_tcw
CurrentShader=&gl.pogram_table[GetProgramID(Type==ListType_Punch_Through?1:0,SetTileClip(gp->tileclip,false)+1,gp->pcw.Texture,gp->tsp.UseAlpha,gp->tsp.IgnoreTexA,gp->tsp.ShadInstr,gp->pcw.Offset,gp->tsp.FogCtrl)];
if (CurrentShader->program == -1)
CompilePipelineShader(CurrentShader);
if (CurrentShader->program != cache.program)
{
cache.program=CurrentShader->program;
glUseProgram(CurrentShader->program);
SetTileClip(gp->tileclip,true);
}
//This for some reason doesn't work properly
//So, shadow bit emulation is disabled.
//This bit normally control which pixels are affected
//by modvols
#ifdef NO_STENCIL_WORKAROUND
const u32 stencil=(gp->pcw.Shadow!=0)?0x80:0x0;
#else
//force everything to be shadowed
const u32 stencil=0x80;
#endif
if (cache.stencil_modvol_on!=stencil)
{
cache.stencil_modvol_on=stencil;
glStencilFunc(GL_ALWAYS,stencil,stencil);
}
if ((gp->tcw.full != cache.tcw.full) || (gp->tsp.full!=cache.tsp.full) || (cache.texture_enabled!= gp->pcw.Texture))
{
cache.tcw=gp->tcw;
cache.texture_enabled = gp->pcw.Texture;
if (gp->pcw.Texture)
{
GLuint tex=GetTexture(gp->tsp,gp->tcw);
if (tex!=cache.texture)
{
glBindTexture(GL_TEXTURE_2D,tex);
cache.texture=tex;
}
}
if (gp->tsp.full!=cache.tsp.full)
{
cache.tsp=gp->tsp;
if (Type==ListType_Translucent)
{
glBlendFunc(SrcBlendGL[gp->tsp.SrcInstr],DstBlendGL[gp->tsp.DstInstr]);
#ifdef WEIRD_SLOWNESS
//SGX seems to be super slow with discard enabled blended pixels
//can't cache this -- due to opengl shader api
bool clip_alpha_on_zero=gp->tsp.SrcInstr==4 && (gp->tsp.DstInstr==1 || gp->tsp.DstInstr==5);
glUniform1f(CurrentShader->cp_AlphaTestValue,clip_alpha_on_zero?(1/255.f):(-2.f));
#endif
}
}
}
//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);
if (gp->isp.full!= cache.isp.full)
{
cache.isp.full=gp->isp.full;
//set Z mode, only if required
if (!(Type==ListType_Punch_Through || (Type==ListType_Translucent && SortingEnabled)))
glDepthFunc(Zfunction[gp->isp.DepthMode]);
#if TRIG_SORT
if (SortingEnabled)
glDepthMask(GL_FALSE);
else
#endif
glDepthMask(!gp->isp.ZWriteDis);
}
}
template <u32 Type, bool SortingEnabled>
void DrawList(const List<PolyParam>& gply)
{
PolyParam* params=gply.head();
int count=gply.used();
if (count==0)
return;
//we want at least 1 PParam
//reset the cache state
cache.Reset(params);
//set some 'global' modes for all primitives
//Z funct. can be fixed on these combinations, avoid setting it all the time
if (Type==ListType_Punch_Through || (Type==ListType_Translucent && SortingEnabled))
glDepthFunc(Zfunction[6]);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS,0,0);
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
#ifndef NO_STENCIL_WORKAROUND
//This looks like a driver bug
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
#endif
while(count-->0)
{
if (params->count>2) //this actually happens for some games. No idea why ..
{
SetGPState<Type,SortingEnabled>(params);
glDrawElements(GL_TRIANGLE_STRIP, params->count, GL_UNSIGNED_SHORT, (GLvoid*)(2*params->first)); glCheck();
}
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
void SortPParams()
{
if (pvrrc.verts.used()==0 || pvrrc.global_param_tr.used()<=1)
return;
Vertex* vtx_base=pvrrc.verts.head();
u16* idx_base=pvrrc.idx.head();
PolyParam* pp=pvrrc.global_param_tr.head();
PolyParam* pp_end= pp + pvrrc.global_param_tr.used();
while(pp!=pp_end)
{
if (pp->count<2)
{
pp->zvZ=0;
}
else
{
u16* idx=idx_base+pp->first;
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++;
}
std::stable_sort(pvrrc.global_param_tr.head(),pvrrc.global_param_tr.head()+pvrrc.global_param_tr.used());
}
Vertex* vtx_sort_base;
struct IndexTrig
{
u16 id[3];
u16 pid;
f32 z;
};
struct SortTrigDrawParam
{
PolyParam* ppid;
u16 first;
u16 count;
};
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);
}
float minZ(Vertex* v,u16* mod)
{
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
*/
//aproximate the triangle area
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)) ;
}
//aproximate the distance ^2
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]);
}
//Not really working cuz of borken intersect
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;
void fill_id(u16* d, Vertex* v0, Vertex* v1, Vertex* v2, Vertex* vb)
{
d[0]=v0-vb;
d[1]=v1-vb;
d[2]=v2-vb;
}
void GenSorted()
{
u32 tess_gen=0;
pidx_sort.clear();
if (pvrrc.verts.used()==0 || pvrrc.global_param_tr.used()<=1)
return;
Vertex* vtx_base=pvrrc.verts.head();
u16* idx_base=pvrrc.idx.head();
PolyParam* pp_base=pvrrc.global_param_tr.head();
PolyParam* pp=pp_base;
PolyParam* pp_end= pp + pvrrc.global_param_tr.used();
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)
{
u16* idx=idx_base+pp->first;
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)
{
v0=&vtx[2];
v1=&vtx[1];
v2=&vtx[0];
}
else
{
v0=&vtx[0];
v1=&vtx[1];
v2=&vtx[2];
}
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
for (int i=0;i<8;i++)
{
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
{
fill_id(lst[pfsti].id,v0,v1,v2,vtx_base);
lst[pfsti].pid= ppid ;
lst[pfsti].z = minZ(vtx_base,lst[pfsti].id);
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)
{
for (int k=1;k<aused;k++)
{
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
static vector<u16> vidx_sort;
vidx_sort.resize(aused*3);
int idx=-1;
for (int i=0;i<aused;i++)
{
int pid=lst[i].pid;
u16* midx=lst[i].id;
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]) */ )
{
SortTrigDrawParam stdp={pp_base + pid, i*3, 0};
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
printf("Reassmelbed into %d from %d\n",pidx_sort.size(),pp_end-pp_base);
#endif
//Upload to gpu if needed
if (pidx_sort.size())
{
//Bind and upload sorted index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs2); glCheck();
glBufferData(GL_ELEMENT_ARRAY_BUFFER,vidx_sort.size()*2,&vidx_sort[0],GL_STREAM_DRAW);
if (tess_gen) printf("Generated %.2fK Triangles !\n",tess_gen/1000.0);
}
}
void DrawSorted()
{
//if any drawing commands, draw them
if (pidx_sort.size())
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs2); glCheck();
u32 count=pidx_sort.size();
{
cache.Reset(pidx_sort[0].ppid);
//set some 'global' modes for all primitives
//Z sorting is fixed for .. sorted stuff
glDepthFunc(Zfunction[6]);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS,0,0);
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
#ifndef NO_STENCIL_WORKAROUND
//This looks like a driver bug
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
#endif
for (int p=0;p<count;p++)
{
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);
glDrawElements(GL_TRIANGLES, pidx_sort[p].count, GL_UNSIGNED_SHORT, (GLvoid*)(2*pidx_sort[p].first)); glCheck();
#if 0
//Verify restriping -- only valid if no sort
int fs=pidx_sort[p].first;
for (int j=0;j<(params->count-2);j++)
{
for (int k=0;k<3;k++)
{
verify(idx_base[params->first+j+k]==vidx_sort[fs++]);
}
}
verify(fs==(pidx_sort[p].first+pidx_sort[p].count));
#endif
}
params++;
}
}
}
}
//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
*/
void SetMVS_Mode(u32 mv_mode,ISP_Modvol ispc)
{
if (mv_mode==0) //normal trigs
{
//set states
glEnable(GL_DEPTH_TEST);
//write only bit 1
glStencilMask(2);
//no stencil testing
glStencilFunc(GL_ALWAYS,0,2);
//count the number of pixels in front of the Z buffer (and only keep the lower bit of the count)
glStencilOp(GL_KEEP,GL_KEEP,GL_INVERT);
#ifndef NO_STENCIL_WORKAROUND
//this needs to be done .. twice ? looks like
//a bug somewhere, on gles/nvgl ?
glStencilOp(GL_KEEP,GL_KEEP,GL_INVERT);
#endif
//Cull mode needs to be set
SetCull(ispc.CullMode);
}
else
{
//1 (last in) or 2 (last out)
//each trinagle forms the last of a volume
//common states
//no depth test
glDisable(GL_DEPTH_TEST);
//write bits 1:0
glStencilMask(3);
if (mv_mode==1)
{
//res : old : final
//0 : 0 : 00
//0 : 1 : 01
//1 : 0 : 01
//1 : 1 : 01
//if (1<=st) st=1; else st=0;
glStencilFunc(GL_LEQUAL,1,3);
glStencilOp(GL_ZERO,GL_ZERO,GL_REPLACE);
#ifndef NO_STENCIL_WORKAROUND
//Look @ comment above -- this looks like a driver bug
glStencilOp(GL_ZERO,GL_ZERO,GL_REPLACE);
#endif
/*
//if !=0 -> set to 10
verifyc(dev->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_LESSEQUAL));
verifyc(dev->SetRenderState(D3DRS_STENCILREF,1));
verifyc(dev->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_REPLACE));
verifyc(dev->SetRenderState(D3DRS_STENCILFAIL,D3DSTENCILOP_ZERO));
*/
}
else
{
/*
this is bugged. a lot.
I've only seen a single game use it, so i guess it doesn't matter ? (Zombie revenge)
(actually, i think there was aslo another, racing game)
*/
//res : old : final
//0 : 0 : 00
//0 : 1 : 00
//1 : 0 : 00
//1 : 1 : 01
//if (2>st) st=1; else st=0; //can't be done with a single pass
glStencilFunc(GL_GREATER,1,3);
glStencilOp(GL_ZERO,GL_KEEP,GL_REPLACE);
#ifndef NO_STENCIL_WORKAROUND
//Look @ comment above -- this looks like a driver bug
glStencilOp(GL_ZERO,GL_KEEP,GL_REPLACE);
#endif
}
}
}
void SetupMainVBO()
{
glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.geometry); glCheck();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs); glCheck();
//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()
{
glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.modvols); glCheck();
//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);
}
void DrawModVols()
{
if (pvrrc.modtrig.used()==0 /*|| GetAsyncKeyState(VK_F4)*/)
return;
SetupModvolVBO();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(gl.modvol_shader.program);
glUniform1f(gl.modvol_shader.sp_ShaderColor,0.5f);
glDepthMask(GL_FALSE);
glDepthFunc(GL_GREATER);
if(0 /*|| GetAsyncKeyState(VK_F5)*/ )
{
//simply draw the volumes -- for debugging
SetCull(0);
glDrawArrays(GL_TRIANGLES,0,pvrrc.modtrig.used()*3);
SetupMainVBO();
}
else
{
/*
mode :
normal trig : flip
last *in* : flip, merge*in* &clear from last merge
last *out* : flip, merge*out* &clear from last merge
*/
/*
Do not write to color
Do not write to depth
read from stencil bits 1:0
write to stencil bits 1:0
*/
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
glDepthFunc(GL_GREATER);
if ( 0 /* || GetAsyncKeyState(VK_F6)*/ )
{
//simple single level stencil
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS,0x1,0x1);
glStencilOp(GL_KEEP,GL_KEEP,GL_INVERT);
#ifndef NO_STENCIL_WORKAROUND
//looks like a driver bug
glStencilOp(GL_KEEP,GL_KEEP,GL_INVERT);
#endif
glStencilMask(0x1);
SetCull(0);
glDrawArrays(GL_TRIANGLES,0,pvrrc.modtrig.used()*3);
}
else if (true)
{
//Full emulation
//the *out* mode is buggy
u32 mod_base=0; //cur start triangle
u32 mod_last=0; //last merge
u32 cmv_count=(pvrrc.global_param_mvo.used()-1);
ISP_Modvol* params=pvrrc.global_param_mvo.head();
//ISP_Modvol
for (u32 cmv=0;cmv<cmv_count;cmv++)
{
ISP_Modvol ispc=params[cmv];
mod_base=ispc.id;
u32 sz=params[cmv+1].id-mod_base;
u32 mv_mode = ispc.DepthMode;
if (mv_mode==0) //normal trigs
{
SetMVS_Mode(0,ispc);
//Render em (counts intersections)
//verifyc(dev->DrawPrimitiveUP(D3DPT_TRIANGLELIST,sz,pvrrc.modtrig.data+mod_base,3*4));
glDrawArrays(GL_TRIANGLES,mod_base*3,sz*3);
}
else if (mv_mode<3)
{
while(sz)
{
//merge and clear all the prev. stencil bits
//Count Intersections (last poly)
SetMVS_Mode(0,ispc);
glDrawArrays(GL_TRIANGLES,mod_base*3,3);
//Sum the area
SetMVS_Mode(mv_mode,ispc);
glDrawArrays(GL_TRIANGLES,mod_last*3,(mod_base-mod_last+1)*3);
//update pointers
mod_last=mod_base+1;
sz--;
mod_base++;
}
}
}
}
//disable culling
SetCull(0);
//enable color writes
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
//black out any stencil with '1'
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL,0x81,0x81); //only pixels that are Modvol enabled, and in area 1
//clear the stencil result bit
glStencilMask(0x3); //write to lsb
glStencilOp(GL_ZERO,GL_ZERO,GL_ZERO);
#ifndef NO_STENCIL_WORKAROUND
//looks like a driver bug ?
glStencilOp(GL_ZERO,GL_ZERO,GL_ZERO);
#endif
//don't do depth testing
glDisable(GL_DEPTH_TEST);
SetupMainVBO();
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
//Draw and blend
//glDrawArrays(GL_TRIANGLES,pvrrc.modtrig.used(),2);
}
//restore states
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
}
void DrawStrips()
{
SetupMainVBO();
//Draw the strips !
//initial state
glDisable(GL_BLEND); glCheck();
glEnable(GL_DEPTH_TEST);
//We use sampler 0
glActiveTexture(GL_TEXTURE0);
//Opaque
//Nothing extra needs to be setup here
/*if (!GetAsyncKeyState(VK_F1))*/
DrawList<ListType_Opaque,false>(pvrrc.global_param_op);
//DrawModVols();
//Alpha tested
//setup alpha test state
/*if (!GetAsyncKeyState(VK_F2))*/
DrawList<ListType_Punch_Through,false>(pvrrc.global_param_pt);
//Alpha blended
//Setup blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
/*if (!GetAsyncKeyState(VK_F3))*/
{
/*
if (UsingAutoSort())
SortRendPolyParamList(pvrrc.global_param_tr);
else
*/
#if TRIG_SORT
if (pvrrc.isAutoSort)
DrawSorted();
else
DrawList<ListType_Translucent,false>(pvrrc.global_param_tr);
#else
if (pvrrc.isAutoSort)
SortPParams();
DrawList<ListType_Translucent,true>(pvrrc.global_param_tr);
#endif
}
}