mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2025-01-10 02:00:44 +00:00
8b6e09ca09
This was done to clean up the license and to ensure that any commercial fork of the engine has to obey the far stricter requirements concerning source distribution. The old license was compatible with GPLv2 whereas combining GPLv2 and LGPLv3 force a license upgrade to GPLv3. The license of code that originates from ZDoomGL has not been changed.
1298 lines
32 KiB
C++
1298 lines
32 KiB
C++
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright(C) 2002-2016 Christoph Oelckers
|
|
// All rights reserved.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with this program. If not, see http://www.gnu.org/licenses/
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
/*
|
|
** gl_drawinfo.cpp
|
|
** Implements the draw info structure which contains most of the
|
|
** data in a scene and the draw lists - including a very thorough BSP
|
|
** style sorting algorithm for translucent objects.
|
|
**
|
|
*/
|
|
|
|
#include "gl/system/gl_system.h"
|
|
#include "r_sky.h"
|
|
#include "r_utility.h"
|
|
#include "r_state.h"
|
|
#include "doomstat.h"
|
|
|
|
#include "gl/system/gl_cvars.h"
|
|
#include "gl/data/gl_data.h"
|
|
#include "gl/data/gl_vertexbuffer.h"
|
|
#include "gl/scene/gl_drawinfo.h"
|
|
#include "gl/scene/gl_portal.h"
|
|
#include "gl/renderer/gl_lightdata.h"
|
|
#include "gl/renderer/gl_renderstate.h"
|
|
#include "gl/textures/gl_material.h"
|
|
#include "gl/utility/gl_clock.h"
|
|
#include "gl/utility/gl_templates.h"
|
|
#include "gl/shaders/gl_shader.h"
|
|
#include "gl/stereo3d/scoped_color_mask.h"
|
|
#include "gl/renderer/gl_quaddrawer.h"
|
|
|
|
FDrawInfo * gl_drawinfo;
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
class StaticSortNodeArray : public TDeletingArray<SortNode*>
|
|
{
|
|
unsigned usecount;
|
|
public:
|
|
unsigned Size() { return usecount; }
|
|
void Clear() { usecount=0; }
|
|
void Release(int start) { usecount=start; }
|
|
SortNode * GetNew();
|
|
};
|
|
|
|
|
|
SortNode * StaticSortNodeArray::GetNew()
|
|
{
|
|
if (usecount==TArray<SortNode*>::Size())
|
|
{
|
|
Push(new SortNode);
|
|
}
|
|
return operator[](usecount++);
|
|
}
|
|
|
|
|
|
static StaticSortNodeArray SortNodes;
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::Reset()
|
|
{
|
|
if (sorted) SortNodes.Release(SortNodeStart);
|
|
sorted=NULL;
|
|
walls.Clear();
|
|
flats.Clear();
|
|
sprites.Clear();
|
|
drawitems.Clear();
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Translucent polygon sorting - uses a BSP algorithm with an additional 'equal' branch
|
|
|
|
inline double GLSprite::CalcIntersectionVertex(GLWall * w2)
|
|
{
|
|
float ax = x1, ay=y1;
|
|
float bx = x2, by=y2;
|
|
float cx = w2->glseg.x1, cy=w2->glseg.y1;
|
|
float dx = w2->glseg.x2, dy=w2->glseg.y2;
|
|
return ((ay-cy)*(dx-cx)-(ax-cx)*(dy-cy)) / ((bx-ax)*(dy-cy)-(by-ay)*(dx-cx));
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
inline void SortNode::UnlinkFromChain()
|
|
{
|
|
if (parent) parent->next=next;
|
|
if (next) next->parent=parent;
|
|
parent=next=NULL;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
inline void SortNode::Link(SortNode * hook)
|
|
{
|
|
if (hook)
|
|
{
|
|
parent=hook->parent;
|
|
hook->parent=this;
|
|
}
|
|
next=hook;
|
|
if (parent) parent->next=this;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
inline void SortNode::AddToEqual(SortNode *child)
|
|
{
|
|
child->UnlinkFromChain();
|
|
child->equal=equal;
|
|
equal=child;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
inline void SortNode::AddToLeft(SortNode * child)
|
|
{
|
|
child->UnlinkFromChain();
|
|
child->Link(left);
|
|
left=child;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
inline void SortNode::AddToRight(SortNode * child)
|
|
{
|
|
child->UnlinkFromChain();
|
|
child->Link(right);
|
|
right=child;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::MakeSortList()
|
|
{
|
|
SortNode * p, * n, * c;
|
|
unsigned i;
|
|
|
|
SortNodeStart=SortNodes.Size();
|
|
p=NULL;
|
|
n=SortNodes.GetNew();
|
|
for(i=0;i<drawitems.Size();i++)
|
|
{
|
|
n->itemindex=(int)i;
|
|
n->left=n->equal=n->right=NULL;
|
|
n->parent=p;
|
|
p=n;
|
|
if (i!=drawitems.Size()-1)
|
|
{
|
|
c=SortNodes.GetNew();
|
|
n->next=c;
|
|
n=c;
|
|
}
|
|
else
|
|
{
|
|
n->next=NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
SortNode * GLDrawList::FindSortPlane(SortNode * head)
|
|
{
|
|
while (head->next && drawitems[head->itemindex].rendertype!=GLDIT_FLAT)
|
|
head=head->next;
|
|
if (drawitems[head->itemindex].rendertype==GLDIT_FLAT) return head;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
SortNode * GLDrawList::FindSortWall(SortNode * head)
|
|
{
|
|
float farthest = -FLT_MAX;
|
|
float nearest = FLT_MAX;
|
|
SortNode * best = NULL;
|
|
SortNode * node = head;
|
|
float bestdist = FLT_MAX;
|
|
|
|
while (node)
|
|
{
|
|
GLDrawItem * it = &drawitems[node->itemindex];
|
|
if (it->rendertype == GLDIT_WALL)
|
|
{
|
|
float d = walls[it->index].ViewDistance;
|
|
if (d > farthest) farthest = d;
|
|
if (d < nearest) nearest = d;
|
|
}
|
|
node = node->next;
|
|
}
|
|
if (farthest == INT_MIN) return NULL;
|
|
node = head;
|
|
farthest = (farthest + nearest) / 2;
|
|
while (node)
|
|
{
|
|
GLDrawItem * it = &drawitems[node->itemindex];
|
|
if (it->rendertype == GLDIT_WALL)
|
|
{
|
|
float di = fabsf(walls[it->index].ViewDistance - farthest);
|
|
if (!best || di < bestdist)
|
|
{
|
|
best = node;
|
|
bestdist = di;
|
|
}
|
|
}
|
|
node = node->next;
|
|
}
|
|
return best;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Note: sloped planes are a huge problem...
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::SortPlaneIntoPlane(SortNode * head,SortNode * sort)
|
|
{
|
|
GLFlat * fh=&flats[drawitems[head->itemindex].index];
|
|
GLFlat * fs=&flats[drawitems[sort->itemindex].index];
|
|
|
|
if (fh->z==fs->z)
|
|
head->AddToEqual(sort);
|
|
else if ( (fh->z<fs->z && fh->ceiling) || (fh->z>fs->z && !fh->ceiling))
|
|
head->AddToLeft(sort);
|
|
else
|
|
head->AddToRight(sort);
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::SortWallIntoPlane(SortNode * head,SortNode * sort)
|
|
{
|
|
GLFlat * fh=&flats[drawitems[head->itemindex].index];
|
|
GLWall * ws=&walls[drawitems[sort->itemindex].index];
|
|
|
|
bool ceiling = fh->z > ViewPos.Z;
|
|
|
|
if ((ws->ztop[0] > fh->z || ws->ztop[1] > fh->z) && (ws->zbottom[0] < fh->z || ws->zbottom[1] < fh->z))
|
|
{
|
|
// We have to split this wall!
|
|
|
|
// WARNING: NEVER EVER push a member of an array onto the array itself.
|
|
// Bad things will happen if the memory must be reallocated!
|
|
GLWall w = *ws;
|
|
AddWall(&w);
|
|
|
|
// Splitting is done in the shader with clip planes, if available
|
|
if (gl.flags & RFL_NO_CLIP_PLANES)
|
|
{
|
|
GLWall * ws1;
|
|
ws->vertcount = 0; // invalidate current vertices.
|
|
ws1=&walls[walls.Size()-1];
|
|
ws=&walls[drawitems[sort->itemindex].index]; // may have been reallocated!
|
|
float newtexv = ws->tcs[GLWall::UPLFT].v + ((ws->tcs[GLWall::LOLFT].v - ws->tcs[GLWall::UPLFT].v) / (ws->zbottom[0] - ws->ztop[0])) * (fh->z - ws->ztop[0]);
|
|
|
|
// I make the very big assumption here that translucent walls in sloped sectors
|
|
// and 3D-floors never coexist in the same level. If that were the case this
|
|
// code would become extremely more complicated.
|
|
if (!ceiling)
|
|
{
|
|
ws->ztop[1] = ws1->zbottom[1] = ws->ztop[0] = ws1->zbottom[0] = fh->z;
|
|
ws->tcs[GLWall::UPRGT].v = ws1->tcs[GLWall::LORGT].v = ws->tcs[GLWall::UPLFT].v = ws1->tcs[GLWall::LOLFT].v = newtexv;
|
|
}
|
|
else
|
|
{
|
|
ws1->ztop[1] = ws->zbottom[1] = ws1->ztop[0] = ws->zbottom[0] = fh->z;
|
|
ws1->tcs[GLWall::UPLFT].v = ws->tcs[GLWall::LOLFT].v = ws1->tcs[GLWall::UPRGT].v = ws->tcs[GLWall::LORGT].v=newtexv;
|
|
}
|
|
}
|
|
|
|
SortNode * sort2 = SortNodes.GetNew();
|
|
memset(sort2, 0, sizeof(SortNode));
|
|
sort2->itemindex = drawitems.Size() - 1;
|
|
|
|
head->AddToLeft(sort);
|
|
head->AddToRight(sort2);
|
|
}
|
|
else if ((ws->zbottom[0]<fh->z && !ceiling) || (ws->ztop[0]>fh->z && ceiling)) // completely on the left side
|
|
{
|
|
head->AddToLeft(sort);
|
|
}
|
|
else
|
|
{
|
|
head->AddToRight(sort);
|
|
}
|
|
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::SortSpriteIntoPlane(SortNode * head,SortNode * sort)
|
|
{
|
|
GLFlat * fh=&flats[drawitems[head->itemindex].index];
|
|
GLSprite * ss=&sprites[drawitems[sort->itemindex].index];
|
|
|
|
bool ceiling = fh->z > ViewPos.Z;
|
|
|
|
if ((ss->z1>fh->z && ss->z2<fh->z) || ss->modelframe)
|
|
{
|
|
// We have to split this sprite
|
|
GLSprite s=*ss;
|
|
AddSprite(&s); // add a copy to avoid reallocation issues.
|
|
|
|
// Splitting is done in the shader with clip planes, if available
|
|
if (gl.flags & RFL_NO_CLIP_PLANES)
|
|
{
|
|
GLSprite * ss1;
|
|
ss1=&sprites[sprites.Size()-1];
|
|
ss=&sprites[drawitems[sort->itemindex].index]; // may have been reallocated!
|
|
float newtexv=ss->vt + ((ss->vb-ss->vt)/(ss->z2-ss->z1))*(fh->z-ss->z1);
|
|
|
|
if (!ceiling)
|
|
{
|
|
ss->z1=ss1->z2=fh->z;
|
|
ss->vt=ss1->vb=newtexv;
|
|
}
|
|
else
|
|
{
|
|
ss1->z1=ss->z2=fh->z;
|
|
ss1->vt=ss->vb=newtexv;
|
|
}
|
|
}
|
|
|
|
SortNode * sort2=SortNodes.GetNew();
|
|
memset(sort2,0,sizeof(SortNode));
|
|
sort2->itemindex=drawitems.Size()-1;
|
|
|
|
head->AddToLeft(sort);
|
|
head->AddToRight(sort2);
|
|
}
|
|
else if ((ss->z2<fh->z && !ceiling) || (ss->z1>fh->z && ceiling)) // completely on the left side
|
|
{
|
|
head->AddToLeft(sort);
|
|
}
|
|
else
|
|
{
|
|
head->AddToRight(sort);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
#define MIN_EQ (0.0005f)
|
|
|
|
void GLDrawList::SortWallIntoWall(SortNode * head,SortNode * sort)
|
|
{
|
|
GLWall * wh=&walls[drawitems[head->itemindex].index];
|
|
GLWall * ws=&walls[drawitems[sort->itemindex].index];
|
|
GLWall * ws1;
|
|
float v1=wh->PointOnSide(ws->glseg.x1,ws->glseg.y1);
|
|
float v2=wh->PointOnSide(ws->glseg.x2,ws->glseg.y2);
|
|
|
|
if (fabs(v1)<MIN_EQ && fabs(v2)<MIN_EQ)
|
|
{
|
|
if (ws->type==RENDERWALL_FOGBOUNDARY && wh->type!=RENDERWALL_FOGBOUNDARY)
|
|
{
|
|
head->AddToRight(sort);
|
|
}
|
|
else if (ws->type!=RENDERWALL_FOGBOUNDARY && wh->type==RENDERWALL_FOGBOUNDARY)
|
|
{
|
|
head->AddToLeft(sort);
|
|
}
|
|
else
|
|
{
|
|
head->AddToEqual(sort);
|
|
}
|
|
}
|
|
else if (v1<MIN_EQ && v2<MIN_EQ)
|
|
{
|
|
head->AddToLeft(sort);
|
|
}
|
|
else if (v1>-MIN_EQ && v2>-MIN_EQ)
|
|
{
|
|
head->AddToRight(sort);
|
|
}
|
|
else
|
|
{
|
|
double r=ws->CalcIntersectionVertex(wh);
|
|
|
|
float ix=(float)(ws->glseg.x1+r*(ws->glseg.x2-ws->glseg.x1));
|
|
float iy=(float)(ws->glseg.y1+r*(ws->glseg.y2-ws->glseg.y1));
|
|
float iu=(float)(ws->tcs[GLWall::UPLFT].u + r * (ws->tcs[GLWall::UPRGT].u - ws->tcs[GLWall::UPLFT].u));
|
|
float izt=(float)(ws->ztop[0]+r*(ws->ztop[1]-ws->ztop[0]));
|
|
float izb=(float)(ws->zbottom[0]+r*(ws->zbottom[1]-ws->zbottom[0]));
|
|
|
|
ws->vertcount = 0; // invalidate current vertices.
|
|
GLWall w=*ws;
|
|
AddWall(&w);
|
|
ws1=&walls[walls.Size()-1];
|
|
ws=&walls[drawitems[sort->itemindex].index]; // may have been reallocated!
|
|
|
|
ws1->glseg.x1=ws->glseg.x2=ix;
|
|
ws1->glseg.y1=ws->glseg.y2=iy;
|
|
ws1->glseg.fracleft = ws->glseg.fracright = ws->glseg.fracleft + r*(ws->glseg.fracright - ws->glseg.fracleft);
|
|
ws1->ztop[0]=ws->ztop[1]=izt;
|
|
ws1->zbottom[0]=ws->zbottom[1]=izb;
|
|
ws1->tcs[GLWall::LOLFT].u = ws1->tcs[GLWall::UPLFT].u = ws->tcs[GLWall::LORGT].u = ws->tcs[GLWall::UPRGT].u = iu;
|
|
if (gl.buffermethod == BM_DEFERRED)
|
|
{
|
|
ws->MakeVertices(false);
|
|
ws1->MakeVertices(false);
|
|
}
|
|
|
|
SortNode * sort2=SortNodes.GetNew();
|
|
memset(sort2,0,sizeof(SortNode));
|
|
sort2->itemindex=drawitems.Size()-1;
|
|
|
|
if (v1>0)
|
|
{
|
|
head->AddToLeft(sort2);
|
|
head->AddToRight(sort);
|
|
}
|
|
else
|
|
{
|
|
head->AddToLeft(sort);
|
|
head->AddToRight(sort2);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::SortSpriteIntoWall(SortNode * head,SortNode * sort)
|
|
{
|
|
GLWall * wh=&walls[drawitems[head->itemindex].index];
|
|
GLSprite * ss=&sprites[drawitems[sort->itemindex].index];
|
|
GLSprite * ss1;
|
|
|
|
float v1 = wh->PointOnSide(ss->x1, ss->y1);
|
|
float v2 = wh->PointOnSide(ss->x2, ss->y2);
|
|
|
|
if (fabs(v1)<MIN_EQ && fabs(v2)<MIN_EQ)
|
|
{
|
|
if (wh->type==RENDERWALL_FOGBOUNDARY)
|
|
{
|
|
head->AddToLeft(sort);
|
|
}
|
|
else
|
|
{
|
|
head->AddToEqual(sort);
|
|
}
|
|
}
|
|
else if (v1<MIN_EQ && v2<MIN_EQ)
|
|
{
|
|
head->AddToLeft(sort);
|
|
}
|
|
else if (v1>-MIN_EQ && v2>-MIN_EQ)
|
|
{
|
|
head->AddToRight(sort);
|
|
}
|
|
else
|
|
{
|
|
double r=ss->CalcIntersectionVertex(wh);
|
|
|
|
float ix=(float)(ss->x1 + r * (ss->x2-ss->x1));
|
|
float iy=(float)(ss->y1 + r * (ss->y2-ss->y1));
|
|
float iu=(float)(ss->ul + r * (ss->ur-ss->ul));
|
|
|
|
GLSprite s=*ss;
|
|
AddSprite(&s);
|
|
ss1=&sprites[sprites.Size()-1];
|
|
ss=&sprites[drawitems[sort->itemindex].index]; // may have been reallocated!
|
|
|
|
ss1->x1=ss->x2=ix;
|
|
ss1->y1=ss->y2=iy;
|
|
ss1->ul=ss->ur=iu;
|
|
|
|
SortNode * sort2=SortNodes.GetNew();
|
|
memset(sort2,0,sizeof(SortNode));
|
|
sort2->itemindex=drawitems.Size()-1;
|
|
|
|
if (v1>0)
|
|
{
|
|
head->AddToLeft(sort2);
|
|
head->AddToRight(sort);
|
|
}
|
|
else
|
|
{
|
|
head->AddToLeft(sort);
|
|
head->AddToRight(sort2);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
inline int GLDrawList::CompareSprites(SortNode * a,SortNode * b)
|
|
{
|
|
GLSprite * s1=&sprites[drawitems[a->itemindex].index];
|
|
GLSprite * s2=&sprites[drawitems[b->itemindex].index];
|
|
|
|
int res = s1->depth - s2->depth;
|
|
|
|
if (res != 0) return -res;
|
|
else return (i_compatflags & COMPATF_SPRITESORT)? s1->index-s2->index : s2->index-s1->index;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
static GLDrawList * gd;
|
|
int CompareSprite(const void * a,const void * b)
|
|
{
|
|
return gd->CompareSprites(*(SortNode**)a,*(SortNode**)b);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
SortNode * GLDrawList::SortSpriteList(SortNode * head)
|
|
{
|
|
SortNode * n;
|
|
int count;
|
|
unsigned i;
|
|
|
|
static TArray<SortNode*> sortspritelist;
|
|
|
|
SortNode * parent=head->parent;
|
|
|
|
sortspritelist.Clear();
|
|
for(count=0,n=head;n;n=n->next) sortspritelist.Push(n);
|
|
gd=this;
|
|
qsort(&sortspritelist[0],sortspritelist.Size(),sizeof(SortNode *),CompareSprite);
|
|
for(i=0;i<sortspritelist.Size();i++)
|
|
{
|
|
sortspritelist[i]->next=NULL;
|
|
if (parent) parent->equal=sortspritelist[i];
|
|
parent=sortspritelist[i];
|
|
}
|
|
return sortspritelist[0];
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
SortNode * GLDrawList::DoSort(SortNode * head)
|
|
{
|
|
SortNode * node, * sn, * next;
|
|
|
|
sn=FindSortPlane(head);
|
|
if (sn)
|
|
{
|
|
if (sn==head) head=head->next;
|
|
sn->UnlinkFromChain();
|
|
node=head;
|
|
head=sn;
|
|
while (node)
|
|
{
|
|
next=node->next;
|
|
switch(drawitems[node->itemindex].rendertype)
|
|
{
|
|
case GLDIT_FLAT:
|
|
SortPlaneIntoPlane(head,node);
|
|
break;
|
|
|
|
case GLDIT_WALL:
|
|
SortWallIntoPlane(head,node);
|
|
break;
|
|
|
|
case GLDIT_SPRITE:
|
|
SortSpriteIntoPlane(head,node);
|
|
break;
|
|
}
|
|
node=next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sn=FindSortWall(head);
|
|
if (sn)
|
|
{
|
|
if (sn==head) head=head->next;
|
|
sn->UnlinkFromChain();
|
|
node=head;
|
|
head=sn;
|
|
while (node)
|
|
{
|
|
next=node->next;
|
|
switch(drawitems[node->itemindex].rendertype)
|
|
{
|
|
case GLDIT_WALL:
|
|
SortWallIntoWall(head,node);
|
|
break;
|
|
|
|
case GLDIT_SPRITE:
|
|
SortSpriteIntoWall(head,node);
|
|
break;
|
|
|
|
case GLDIT_FLAT: break;
|
|
}
|
|
node=next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return SortSpriteList(head);
|
|
}
|
|
}
|
|
if (head->left) head->left=DoSort(head->left);
|
|
if (head->right) head->right=DoSort(head->right);
|
|
return sn;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::DoDraw(int pass, int i, bool trans)
|
|
{
|
|
switch(drawitems[i].rendertype)
|
|
{
|
|
case GLDIT_FLAT:
|
|
{
|
|
GLFlat * f=&flats[drawitems[i].index];
|
|
RenderFlat.Clock();
|
|
f->Draw(pass, trans);
|
|
RenderFlat.Unclock();
|
|
}
|
|
break;
|
|
|
|
case GLDIT_WALL:
|
|
{
|
|
GLWall * w=&walls[drawitems[i].index];
|
|
RenderWall.Clock();
|
|
w->Draw(pass);
|
|
RenderWall.Unclock();
|
|
}
|
|
break;
|
|
|
|
case GLDIT_SPRITE:
|
|
{
|
|
GLSprite * s=&sprites[drawitems[i].index];
|
|
RenderSprite.Clock();
|
|
s->Draw(pass);
|
|
RenderSprite.Unclock();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::DoDrawSorted(SortNode * head)
|
|
{
|
|
float clipsplit[2];
|
|
int relation = 0;
|
|
float z = 0.f;
|
|
|
|
gl_RenderState.GetClipSplit(clipsplit);
|
|
|
|
if (drawitems[head->itemindex].rendertype == GLDIT_FLAT)
|
|
{
|
|
z = flats[drawitems[head->itemindex].index].z;
|
|
relation = z > ViewPos.Z ? 1 : -1;
|
|
}
|
|
|
|
|
|
// left is further away, i.e. for stuff above viewz its z coordinate higher, for stuff below viewz its z coordinate is lower
|
|
if (head->left)
|
|
{
|
|
if (relation == -1)
|
|
{
|
|
gl_RenderState.SetClipSplit(clipsplit[0], z); // render below: set flat as top clip plane
|
|
}
|
|
else if (relation == 1)
|
|
{
|
|
gl_RenderState.SetClipSplit(z, clipsplit[1]); // render above: set flat as bottom clip plane
|
|
}
|
|
DoDrawSorted(head->left);
|
|
gl_RenderState.SetClipSplit(clipsplit);
|
|
}
|
|
DoDraw(GLPASS_TRANSLUCENT, head->itemindex, true);
|
|
if (head->equal)
|
|
{
|
|
SortNode * ehead=head->equal;
|
|
while (ehead)
|
|
{
|
|
DoDraw(GLPASS_TRANSLUCENT, ehead->itemindex, true);
|
|
ehead=ehead->equal;
|
|
}
|
|
}
|
|
// right is closer, i.e. for stuff above viewz its z coordinate is lower, for stuff below viewz its z coordinate is higher
|
|
if (head->right)
|
|
{
|
|
if (relation == 1)
|
|
{
|
|
gl_RenderState.SetClipSplit(clipsplit[0], z); // render below: set flat as top clip plane
|
|
}
|
|
else if (relation == -1)
|
|
{
|
|
gl_RenderState.SetClipSplit(z, clipsplit[1]); // render above: set flat as bottom clip plane
|
|
}
|
|
DoDrawSorted(head->right);
|
|
gl_RenderState.SetClipSplit(clipsplit);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::DrawSorted()
|
|
{
|
|
if (drawitems.Size()==0) return;
|
|
|
|
if (!sorted)
|
|
{
|
|
GLRenderer->mVBO->Map();
|
|
MakeSortList();
|
|
sorted=DoSort(SortNodes[SortNodeStart]);
|
|
GLRenderer->mVBO->Unmap();
|
|
}
|
|
gl_RenderState.ClearClipSplit();
|
|
if (!(gl.flags & RFL_NO_CLIP_PLANES))
|
|
{
|
|
glEnable(GL_CLIP_DISTANCE1);
|
|
glEnable(GL_CLIP_DISTANCE2);
|
|
}
|
|
DoDrawSorted(sorted);
|
|
if (!(gl.flags & RFL_NO_CLIP_PLANES))
|
|
{
|
|
glDisable(GL_CLIP_DISTANCE1);
|
|
glDisable(GL_CLIP_DISTANCE2);
|
|
}
|
|
gl_RenderState.ClearClipSplit();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::Draw(int pass, bool trans)
|
|
{
|
|
for(unsigned i=0;i<drawitems.Size();i++)
|
|
{
|
|
DoDraw(pass, i, trans);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::DrawWalls(int pass)
|
|
{
|
|
RenderWall.Clock();
|
|
for(unsigned i=0;i<drawitems.Size();i++)
|
|
{
|
|
walls[drawitems[i].index].Draw(pass);
|
|
}
|
|
RenderWall.Unclock();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::DrawFlats(int pass)
|
|
{
|
|
RenderFlat.Clock();
|
|
for(unsigned i=0;i<drawitems.Size();i++)
|
|
{
|
|
flats[drawitems[i].index].Draw(pass, false);
|
|
}
|
|
RenderFlat.Unclock();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::DrawDecals()
|
|
{
|
|
for(unsigned i=0;i<drawitems.Size();i++)
|
|
{
|
|
walls[drawitems[i].index].DoDrawDecals();
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Sorting the drawitems first by texture and then by light level.
|
|
//
|
|
//==========================================================================
|
|
static GLDrawList * sortinfo;
|
|
|
|
static int diwcmp (const void *a, const void *b)
|
|
{
|
|
const GLDrawItem * di1 = (const GLDrawItem *)a;
|
|
GLWall * w1=&sortinfo->walls[di1->index];
|
|
|
|
const GLDrawItem * di2 = (const GLDrawItem *)b;
|
|
GLWall * w2=&sortinfo->walls[di2->index];
|
|
|
|
if (w1->gltexture != w2->gltexture) return w1->gltexture - w2->gltexture;
|
|
return ((w1->flags & 3) - (w2->flags & 3));
|
|
}
|
|
|
|
static int difcmp (const void *a, const void *b)
|
|
{
|
|
const GLDrawItem * di1 = (const GLDrawItem *)a;
|
|
GLFlat * w1=&sortinfo->flats[di1->index];
|
|
|
|
const GLDrawItem * di2 = (const GLDrawItem *)b;
|
|
GLFlat* w2=&sortinfo->flats[di2->index];
|
|
|
|
return w1->gltexture - w2->gltexture;
|
|
}
|
|
|
|
|
|
void GLDrawList::SortWalls()
|
|
{
|
|
if (drawitems.Size() > 1)
|
|
{
|
|
sortinfo=this;
|
|
qsort(&drawitems[0], drawitems.Size(), sizeof(drawitems[0]), diwcmp);
|
|
}
|
|
}
|
|
|
|
void GLDrawList::SortFlats()
|
|
{
|
|
if (drawitems.Size() > 1)
|
|
{
|
|
sortinfo=this;
|
|
qsort(&drawitems[0], drawitems.Size(), sizeof(drawitems[0]), difcmp);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::AddWall(GLWall * wall)
|
|
{
|
|
drawitems.Push(GLDrawItem(GLDIT_WALL,walls.Push(*wall)));
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::AddFlat(GLFlat * flat)
|
|
{
|
|
drawitems.Push(GLDrawItem(GLDIT_FLAT,flats.Push(*flat)));
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void GLDrawList::AddSprite(GLSprite * sprite)
|
|
{
|
|
drawitems.Push(GLDrawItem(GLDIT_SPRITE,sprites.Push(*sprite)));
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// Try to reuse the lists as often as possible as they contain resources that
|
|
// are expensive to create and delete.
|
|
//
|
|
//==========================================================================
|
|
|
|
FDrawInfo *FDrawInfoList::GetNew()
|
|
{
|
|
if (mList.Size() > 0)
|
|
{
|
|
FDrawInfo *di;
|
|
mList.Pop(di);
|
|
return di;
|
|
}
|
|
return new FDrawInfo;
|
|
}
|
|
|
|
void FDrawInfoList::Release(FDrawInfo * di)
|
|
{
|
|
di->ClearBuffers();
|
|
mList.Push(di);
|
|
}
|
|
|
|
static FDrawInfoList di_list;
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
FDrawInfo::FDrawInfo()
|
|
{
|
|
next = NULL;
|
|
if (gl.legacyMode)
|
|
{
|
|
dldrawlists = new GLDrawList[GLLDL_TYPES];
|
|
}
|
|
}
|
|
|
|
FDrawInfo::~FDrawInfo()
|
|
{
|
|
if (dldrawlists != NULL) delete[] dldrawlists;
|
|
ClearBuffers();
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// Sets up a new drawinfo struct
|
|
//
|
|
//==========================================================================
|
|
void FDrawInfo::StartDrawInfo()
|
|
{
|
|
FDrawInfo *di=di_list.GetNew();
|
|
di->StartScene();
|
|
}
|
|
|
|
void FDrawInfo::StartScene()
|
|
{
|
|
ClearBuffers();
|
|
|
|
sectorrenderflags.Resize(numsectors);
|
|
ss_renderflags.Resize(numsubsectors);
|
|
no_renderflags.Resize(numsubsectors);
|
|
|
|
memset(§orrenderflags[0], 0, numsectors * sizeof(sectorrenderflags[0]));
|
|
memset(&ss_renderflags[0], 0, numsubsectors * sizeof(ss_renderflags[0]));
|
|
memset(&no_renderflags[0], 0, numnodes * sizeof(no_renderflags[0]));
|
|
|
|
next = gl_drawinfo;
|
|
gl_drawinfo = this;
|
|
for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset();
|
|
if (dldrawlists != NULL)
|
|
{
|
|
for (int i = 0; i < GLLDL_TYPES; i++) dldrawlists[i].Reset();
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void FDrawInfo::EndDrawInfo()
|
|
{
|
|
FDrawInfo * di = gl_drawinfo;
|
|
|
|
for(int i=0;i<GLDL_TYPES;i++) di->drawlists[i].Reset();
|
|
if (di->dldrawlists != NULL)
|
|
{
|
|
for (int i = 0; i < GLLDL_TYPES; i++) di->dldrawlists[i].Reset();
|
|
}
|
|
gl_drawinfo=di->next;
|
|
di_list.Release(di);
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// Flood gaps with the back side's ceiling/floor texture
|
|
// This requires a stencil because the projected plane interferes with
|
|
// the depth buffer
|
|
//
|
|
//==========================================================================
|
|
|
|
void FDrawInfo::SetupFloodStencil(wallseg * ws)
|
|
{
|
|
int recursion = GLPortal::GetRecursion();
|
|
|
|
// Create stencil
|
|
glStencilFunc(GL_EQUAL, recursion, ~0); // create stencil
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels
|
|
{
|
|
// Use revertible color mask, to avoid stomping on anaglyph 3D state
|
|
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0, 0, 0, 0); // don't write to the graphics buffer
|
|
gl_RenderState.EnableTexture(false);
|
|
gl_RenderState.ResetColor();
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(true);
|
|
|
|
gl_RenderState.Apply();
|
|
FQuadDrawer qd;
|
|
qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0);
|
|
qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0);
|
|
qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0);
|
|
qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0);
|
|
qd.Render(GL_TRIANGLE_FAN);
|
|
|
|
glStencilFunc(GL_EQUAL, recursion + 1, ~0); // draw sky into stencil
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
|
|
|
|
} // glColorMask(1, 1, 1, 1); // don't write to the graphics buffer
|
|
gl_RenderState.EnableTexture(true);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(false);
|
|
}
|
|
|
|
void FDrawInfo::ClearFloodStencil(wallseg * ws)
|
|
{
|
|
int recursion = GLPortal::GetRecursion();
|
|
|
|
glStencilOp(GL_KEEP,GL_KEEP,GL_DECR);
|
|
gl_RenderState.EnableTexture(false);
|
|
{
|
|
// Use revertible color mask, to avoid stomping on anaglyph 3D state
|
|
ScopedColorMask colorMask(0, 0, 0, 0); // glColorMask(0,0,0,0); // don't write to the graphics buffer
|
|
gl_RenderState.ResetColor();
|
|
|
|
gl_RenderState.Apply();
|
|
FQuadDrawer qd;
|
|
qd.Set(0, ws->x1, ws->z1, ws->y1, 0, 0);
|
|
qd.Set(1, ws->x1, ws->z2, ws->y1, 0, 0);
|
|
qd.Set(2, ws->x2, ws->z2, ws->y2, 0, 0);
|
|
qd.Set(3, ws->x2, ws->z1, ws->y2, 0, 0);
|
|
qd.Render(GL_TRIANGLE_FAN);
|
|
|
|
// restore old stencil op.
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
glStencilFunc(GL_EQUAL, recursion, ~0);
|
|
gl_RenderState.EnableTexture(true);
|
|
} // glColorMask(1, 1, 1, 1);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(true);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Draw the plane segment into the gap
|
|
//
|
|
//==========================================================================
|
|
void FDrawInfo::DrawFloodedPlane(wallseg * ws, float planez, sector_t * sec, bool ceiling)
|
|
{
|
|
GLSectorPlane plane;
|
|
int lightlevel;
|
|
FColormap Colormap;
|
|
FMaterial * gltexture;
|
|
|
|
plane.GetFromSector(sec, ceiling);
|
|
|
|
gltexture=FMaterial::ValidateTexture(plane.texture, false, true);
|
|
if (!gltexture) return;
|
|
|
|
if (gl_fixedcolormap)
|
|
{
|
|
Colormap.Clear();
|
|
lightlevel=255;
|
|
}
|
|
else
|
|
{
|
|
Colormap=sec->ColorMap;
|
|
if (gltexture->tex->isFullbright())
|
|
{
|
|
Colormap.LightColor.r = Colormap.LightColor.g = Colormap.LightColor.b = 0xff;
|
|
lightlevel=255;
|
|
}
|
|
else lightlevel=abs(ceiling? sec->GetCeilingLight() : sec->GetFloorLight());
|
|
}
|
|
|
|
int rel = getExtraLight();
|
|
gl_SetColor(lightlevel, rel, Colormap, 1.0f);
|
|
gl_SetFog(lightlevel, rel, &Colormap, false);
|
|
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
|
|
|
|
float fviewx = ViewPos.X;
|
|
float fviewy = ViewPos.Y;
|
|
float fviewz = ViewPos.Z;
|
|
|
|
gl_SetPlaneTextureRotation(&plane, gltexture);
|
|
gl_RenderState.Apply();
|
|
|
|
float prj_fac1 = (planez-fviewz)/(ws->z1-fviewz);
|
|
float prj_fac2 = (planez-fviewz)/(ws->z2-fviewz);
|
|
|
|
float px1 = fviewx + prj_fac1 * (ws->x1-fviewx);
|
|
float py1 = fviewy + prj_fac1 * (ws->y1-fviewy);
|
|
|
|
float px2 = fviewx + prj_fac2 * (ws->x1-fviewx);
|
|
float py2 = fviewy + prj_fac2 * (ws->y1-fviewy);
|
|
|
|
float px3 = fviewx + prj_fac2 * (ws->x2-fviewx);
|
|
float py3 = fviewy + prj_fac2 * (ws->y2-fviewy);
|
|
|
|
float px4 = fviewx + prj_fac1 * (ws->x2-fviewx);
|
|
float py4 = fviewy + prj_fac1 * (ws->y2-fviewy);
|
|
|
|
FQuadDrawer qd;
|
|
qd.Set(0, px1, planez, py1, px1 / 64, -py1 / 64);
|
|
qd.Set(1, px2, planez, py2, px2 / 64, -py2 / 64);
|
|
qd.Set(2, px3, planez, py3, px3 / 64, -py3 / 64);
|
|
qd.Set(3, px4, planez, py4, px4 / 64, -py4 / 64);
|
|
qd.Render(GL_TRIANGLE_FAN);
|
|
|
|
gl_RenderState.EnableTextureMatrix(false);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void FDrawInfo::FloodUpperGap(seg_t * seg)
|
|
{
|
|
wallseg ws;
|
|
sector_t ffake, bfake;
|
|
sector_t * fakefsector = gl_FakeFlat(seg->frontsector, &ffake, true);
|
|
sector_t * fakebsector = gl_FakeFlat(seg->backsector, &bfake, false);
|
|
|
|
vertex_t * v1, * v2;
|
|
|
|
// Although the plane can be sloped this code will only be called
|
|
// when the edge itself is not.
|
|
double backz = fakebsector->ceilingplane.ZatPoint(seg->v1);
|
|
double frontz = fakefsector->ceilingplane.ZatPoint(seg->v1);
|
|
|
|
if (fakebsector->GetTexture(sector_t::ceiling)==skyflatnum) return;
|
|
if (backz < ViewPos.Z) return;
|
|
|
|
if (seg->sidedef == seg->linedef->sidedef[0])
|
|
{
|
|
v1=seg->linedef->v1;
|
|
v2=seg->linedef->v2;
|
|
}
|
|
else
|
|
{
|
|
v1=seg->linedef->v2;
|
|
v2=seg->linedef->v1;
|
|
}
|
|
|
|
ws.x1 = v1->fX();
|
|
ws.y1 = v1->fY();
|
|
ws.x2 = v2->fX();
|
|
ws.y2 = v2->fY();
|
|
|
|
ws.z1= frontz;
|
|
ws.z2= backz;
|
|
|
|
// Step1: Draw a stencil into the gap
|
|
SetupFloodStencil(&ws);
|
|
|
|
// Step2: Project the ceiling plane into the gap
|
|
DrawFloodedPlane(&ws, ws.z2, fakebsector, true);
|
|
|
|
// Step3: Delete the stencil
|
|
ClearFloodStencil(&ws);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void FDrawInfo::FloodLowerGap(seg_t * seg)
|
|
{
|
|
wallseg ws;
|
|
sector_t ffake, bfake;
|
|
sector_t * fakefsector = gl_FakeFlat(seg->frontsector, &ffake, true);
|
|
sector_t * fakebsector = gl_FakeFlat(seg->backsector, &bfake, false);
|
|
|
|
vertex_t * v1, * v2;
|
|
|
|
// Although the plane can be sloped this code will only be called
|
|
// when the edge itself is not.
|
|
double backz = fakebsector->floorplane.ZatPoint(seg->v1);
|
|
double frontz = fakefsector->floorplane.ZatPoint(seg->v1);
|
|
|
|
|
|
if (fakebsector->GetTexture(sector_t::floor) == skyflatnum) return;
|
|
if (fakebsector->GetPlaneTexZ(sector_t::floor) > ViewPos.Z) return;
|
|
|
|
if (seg->sidedef == seg->linedef->sidedef[0])
|
|
{
|
|
v1=seg->linedef->v1;
|
|
v2=seg->linedef->v2;
|
|
}
|
|
else
|
|
{
|
|
v1=seg->linedef->v2;
|
|
v2=seg->linedef->v1;
|
|
}
|
|
|
|
ws.x1 = v1->fX();
|
|
ws.y1 = v1->fY();
|
|
ws.x2 = v2->fX();
|
|
ws.y2 = v2->fY();
|
|
|
|
ws.z2= frontz;
|
|
ws.z1= backz;
|
|
|
|
// Step1: Draw a stencil into the gap
|
|
SetupFloodStencil(&ws);
|
|
|
|
// Step2: Project the ceiling plane into the gap
|
|
DrawFloodedPlane(&ws, ws.z1, fakebsector, false);
|
|
|
|
// Step3: Delete the stencil
|
|
ClearFloodStencil(&ws);
|
|
}
|