Serious-Engine/Sources/Engine/Rendering/RendASER.cpp
Daniel Gibson 72edf1c720 Commented out unused functions and variables
many unused functions and variables are now commented out

You'll still get tons of warnings, which should mostly fall in one of
the following categories:
1. Unnecessary variables or values generated from .es scripts
2. Pointers assigned to from functions with side-effects: DO NOT REMOVE!
   Like CEntity *penNew = CreateEntity_t(...); - even if penNew isn't
   used, CreateEntity() must be called there!
2016-05-09 18:51:03 +02:00

868 lines
28 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
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 General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
/*
* Add all edges in add list to active list.
*/
void CRenderer::AddAddListToActiveList(INDEX iScanLine)
{
INDEX ctAddEdges = re_actAddCounts[iScanLine];
// if the add list is empty
if (ctAddEdges==0) {
// do nothing
return;
}
_pfRenderProfile.StartTimer(CRenderProfile::PTI_ADDADDLIST);
CListHead &lhAdd = re_alhAddLists[iScanLine];
ASSERT(ctAddEdges==lhAdd.Count());
// mark that scan-line coherence is lost
re_bCoherentScanLine = 0;
// allocate space in destination for sum of source and add
INDEX ctActiveEdges = re_aaceActiveEdges.Count();
re_aaceActiveEdgesTmp.Push(ctAddEdges+ctActiveEdges);
// check that the add list is sorted right
#if ASER_EXTREME_CHECKING
{
LISTITER(CAddEdge, ade_lnInAdd) itadeAdd(lhAdd);
FIX16_16 xLastI;
xLastI.slHolder = MIN_SLONG;
while(!itadeAdd.IsPastEnd()) {
CAddEdge &adeAdd = *itadeAdd;
ASSERT(adeAdd.ade_xI==adeAdd.ade_psedEdge->sed_xI);
ASSERT(xLastI.slHolder <= adeAdd.ade_xI.slHolder);
xLastI = adeAdd.ade_xI;
itadeAdd.MoveToNext();
}
}
#endif
// check that the active list is sorted right
#if ASER_EXTREME_CHECKING
{
CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
while (paceSrc<paceEnd) {
ASSERT(paceSrc[0].ace_xI.slHolder <= paceSrc[1].ace_xI.slHolder);
paceSrc++;
};
}
#endif
// start at begining of add list, source active list and destination active list
LISTITER(CAddEdge, ade_lnInAdd) itadeAdd(lhAdd);
CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
CActiveEdge *paceDst = &re_aaceActiveEdgesTmp[0];
IFDEBUG(INDEX ctNewActive=0);
IFDEBUG(INDEX ctOldActive1=0);
IFDEBUG(INDEX ctOldActive2=0);
// for each edge in add list
while(!itadeAdd.IsPastEnd()) {
CAddEdge &ade = *itadeAdd;
// while the edge in active list is left of the edge in add list
while (paceSrc->ace_xI.slHolder < itadeAdd->ade_xI.slHolder) {
// copy the active edge
ASSERT(paceSrc<=&re_aaceActiveEdges[ctActiveEdges-1]);
*paceDst++=*paceSrc++;
IFDEBUG(ctOldActive1++);
}
// copy the add edge
ASSERT(paceDst > &re_aaceActiveEdgesTmp[0]);
ASSERT(ade.ade_xI.slHolder == ade.ade_psedEdge->sed_xI.slHolder);
ASSERT(paceDst[-1].ace_xI.slHolder <= ade.ade_xI.slHolder);
*paceDst++=CActiveEdge(itadeAdd->ade_psedEdge);
IFDEBUG(ctNewActive++);
// advance iterator in add list
itadeAdd.MoveToNext();
}
// clear the add list
lhAdd.Clear();
re_actAddCounts[iScanLine] = 0;
// copy all edges left in the active list
while (paceSrc<=&re_aaceActiveEdges[ctActiveEdges-1]) {
*paceDst++=*paceSrc++;
IFDEBUG(ctOldActive2++);
}
// swap the lists
Swap(re_aaceActiveEdges.sa_Count , re_aaceActiveEdgesTmp.sa_Count );
Swap(re_aaceActiveEdges.sa_Array , re_aaceActiveEdgesTmp.sa_Array );
Swap(re_aaceActiveEdges.sa_UsedCount, re_aaceActiveEdgesTmp.sa_UsedCount);
re_aaceActiveEdgesTmp.PopAll();
if (ctAddEdges>_ctMaxAddEdges) {
_ctMaxAddEdges=ctAddEdges;
}
if (re_aaceActiveEdges.Count()>_ctMaxActiveEdges) {
_ctMaxActiveEdges=re_aaceActiveEdges.Count();
}
// check that the active list is sorted right
#if ASER_EXTREME_CHECKING
{
CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
while (paceSrc<paceEnd) {
ASSERT(paceSrc[0].ace_xI.slHolder <= paceSrc[1].ace_xI.slHolder);
paceSrc++;
};
}
#endif
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDADDLIST);
}
/*
* Remove all edges in remove list from active list and from other lists.
*/
void CRenderer::RemRemoveListFromActiveList(CScreenEdge *psedFirst)
{
// if the remove list is empty
if (psedFirst==NULL) {
// do nothing
return;
}
_pfRenderProfile.StartTimer(CRenderProfile::PTI_REMREMLIST);
// mark that scan-line coherence is lost
re_bCoherentScanLine = 0;
// for each edge to be removed on this line
CScreenEdge *psed = psedFirst;
do {
// mark it as removed
psed->sed_xI.slHolder = ACE_REMOVED;
psed = psed->sed_psedNextRemove;
} while (psed!=NULL);
// for each active edge
CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
CActiveEdge *paceSrc = &re_aaceActiveEdges[1];
CActiveEdge *paceDst = paceSrc;
do {
// if it is not removed
if (paceSrc->ace_psedEdge->sed_xI.slHolder!=ACE_REMOVED) {
// copy it
*paceDst = *paceSrc;
paceDst++;
}
paceSrc++;
} while (paceSrc<=paceEnd);
// trim the end of active list
re_aaceActiveEdges.PopUntil(paceDst-&re_aaceActiveEdges[0]-1);
// check that the active list is sorted right
#if ASER_EXTREME_CHECKING
{
CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
while (paceSrc<paceEnd) {
ASSERT(paceSrc[0].ace_xI.slHolder <= paceSrc[1].ace_xI.slHolder);
paceSrc++;
};
}
#endif
_pfRenderProfile.StopTimer(CRenderProfile::PTI_REMREMLIST);
}
/*
* Step all edges in active list by one scan line and resort them.
*/
void CRenderer::StepAndResortActiveList(void)
{
_pfRenderProfile.StartTimer(CRenderProfile::PTI_STEPANDRESORT);
// start after the left sentinel
CActiveEdge *pace = &re_aaceActiveEdges[1];
// for all edges before right sentinel
CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
do {
// step the edge by one scan line
pace->ace_xI.slHolder += pace->ace_xIStep.slHolder;
// if the previous is right of the current
if (pace[-1].ace_xI.slHolder > pace->ace_xI.slHolder) {
// mark that scan-line coherence is lost
re_bCoherentScanLine = 0;
// find last one that is not right
CActiveEdge *pacePred = pace;
do {
pacePred--;
} while(pacePred->ace_xI.slHolder > pace->ace_xI.slHolder);
// remember the current one
CActiveEdge aceCurrent = *pace;
// move all of the edges between one place forward
CActiveEdge *paceMove=pace-1;
do {
paceMove[1]=paceMove[0];
paceMove--;
} while (paceMove>pacePred);
// insert the current to its new place
paceMove[1] = aceCurrent;
}
pace++;
} while (pace < paceEnd);
// check that the active list is sorted right
#if ASER_EXTREME_CHECKING
{
CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
while (paceSrc<paceEnd) {
ASSERT(paceSrc[0].ace_xI.slHolder <= paceSrc[1].ace_xI.slHolder);
paceSrc++;
};
}
#endif
_pfRenderProfile.StopTimer(CRenderProfile::PTI_STEPANDRESORT);
}
/* Copy I coordinates from active list to edge data. */
void CRenderer::CopyActiveCoordinates(void)
{
// for all active edges
CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
CActiveEdge *pace = &re_aaceActiveEdges[1];
do {
// copy active coordinates
pace->ace_psedEdge->sed_xI.slHolder = pace->ace_xI.slHolder;
pace++;
} while (pace<=paceEnd);
}
/*
* Remove an active portal from rendering
*/
void CRenderer::RemovePortal(CScreenPolygon &spo)
{
ASSERT(spo.IsPortal());
ASSERT(spo.spo_bActive);
spo.spo_bActive = FALSE;
// if it is a translucent portal
if (spo.spo_pbpoBrushPolygon->bpo_ulFlags & (BPOF_RENDERTRANSLUCENT|BPOF_TRANSPARENT)) {
_pfRenderProfile.StartTimer(CRenderProfile::PTI_PROCESSTRANSPORTAL);
// add polygon to scene polygons for rendering
AddPolygonToScene(&spo);
_pfRenderProfile.StopTimer(CRenderProfile::PTI_PROCESSTRANSPORTAL);
}
// if it is in the surface stack
if (spo.spo_lnInStack.IsLinked()) {
// remove it from surface stack
RemPolygonFromSurfaceStack(spo);
spo.spo_iInStack = 0;
}
}
/*
* Add sector(s) adjoined to a portal to rendering and remove the portal.
*/
void CRenderer::PassPortal(CScreenPolygon &spo)
{
ChangeStatsMode(CStatForm::STI_WORLDTRANSFORM);
_pfRenderProfile.StartTimer(CRenderProfile::PTI_PASSPORTAL);
// remove the portal from rendering
RemovePortal(spo);
// for all sectors related to the portal
{FOREACHDSTOFSRC(spo.spo_pbpoBrushPolygon->bpo_rsOtherSideSectors, CBrushSector, bsc_rdOtherSidePortals, pbsc)
// if the sector is hidden when not rendering shadows
if ((pbsc->bsc_ulFlags&BSCF_HIDDEN) && !re_bRenderingShadows) {
// skip it
continue;
}
// get brush of the sector
CBrushMip *pbmSectorMip = pbsc->bsc_pbmBrushMip;
CBrush3D &brBrush = *pbmSectorMip->bm_pbrBrush;
// prepare the brush entity for rendering if not yet prepared
PrepareBrush(brBrush.br_penEntity);
// get relevant mip factor for that brush and current rendering prefs
CBrushMip *pbmRelevantMip;
if (brBrush.br_ulFlags&BRF_DRAWFIRSTMIP) {
pbmRelevantMip = brBrush.GetBrushMipByDistance(
_wrpWorldRenderPrefs.GetCurrentMipBrushingFactor(0.0f));
} else {
pbmRelevantMip = brBrush.GetBrushMipByDistance(
_wrpWorldRenderPrefs.GetCurrentMipBrushingFactor(brBrush.br_prProjection->MipFactor()));
}
// if relevant brush mip is same as the sector's brush mip
if (pbmSectorMip==pbmRelevantMip) {
// add that sector to active sectors
AddActiveSector(*pbsc);
}
ENDFOR}
_pfRenderProfile.StopTimer(CRenderProfile::PTI_PASSPORTAL);
ChangeStatsMode(CStatForm::STI_WORLDVISIBILITY);
}
/*
* Add a sector of a brush to rendering queues.
*/
void CRenderer::AddActiveSector(CBrushSector &bscSector)
{
// if already active
if (bscSector.bsc_lnInActiveSectors.IsLinked()) {
// do nothing;
return;
}
_pfRenderProfile.StartTimer(CRenderProfile::PTI_ADDSECTOR);
// add it to active sectors list
re_lhActiveSectors.AddTail(bscSector.bsc_lnInActiveSectors);
// !!! FIXME : rcg10132001 I'm lazy.
#ifdef PLATFORM_WIN32
ASSERT((_controlfp(0, 0)&_MCW_RC)==_RC_NEAR);
#endif
CBrush3D &br = *bscSector.bsc_pbmBrushMip->bm_pbrBrush;
// if should render field brush sector
if (br.br_penEntity->en_RenderType==CEntity::RT_FIELDBRUSH
&& !_wrpWorldRenderPrefs.IsFieldBrushesOn()) {
// skip it
bscSector.bsc_ulFlags|=BSCF_INVISIBLE;
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDSECTOR);
return;
}
// test sector visibility
const INDEX iFrustrumTest = IsSectorVisible( br, bscSector);
if( iFrustrumTest==-1) {
// outside of frustrum - skip it
bscSector.bsc_ulFlags |= BSCF_INVISIBLE;
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDSECTOR);
return;
} else if( iFrustrumTest==0) {
// partially in frustrum - needs clipping
bscSector.bsc_ulFlags |= BSCF_NEEDSCLIPPING;
} else {
// completely in frustrum - doesn't need clipping
bscSector.bsc_ulFlags &= ~BSCF_NEEDSCLIPPING;
}
// mark that sector is visible
bscSector.bsc_ulFlags &= ~BSCF_INVISIBLE;
// remember current sector
re_pbscCurrent = &bscSector;
re_pbrCurrent = &br;
_sfStats.IncrementCounter(CStatForm::SCI_SECTORS);
// if projection is perspective
if( br.br_prProjection.IsPerspective()) {
// prepare fog/haze
SetupFogAndHaze();
}
// transform all vertices and planes in this sector
PreClipVertices();
PreClipPlanes();
// if polygons should be drawn
if (_wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE
||re_bRenderingShadows) {
// find which portals should be rendered as portals or as pretenders
FindPretenders();
// make screen polygons for nondetail polygons in current sector
MakeNonDetailScreenPolygons();
// clip all polygons to all clip planes
if( bscSector.bsc_ulFlags&BSCF_NEEDSCLIPPING) ClipToAllPlanes( br.br_prProjection);
// project vertices to 2d
PostClipVertices();
// make final edges for all polygons in current sector
MakeFinalPolygonEdges();
// add screen edges for all polygons in current sector
AddScreenEdges();
// make screen polygons for detail polygons in current sector
MakeDetailScreenPolygons();
}
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDSECTOR);
// get the entity the sector is in
CEntity *penSectorEntity = bscSector.bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
// if it has the entity (it is not the background brush)
if (penSectorEntity != NULL) {
// add all other entities near the sector
AddEntitiesInSector(&bscSector);
}
}
/*
* Initialize list of active edges.
*/
void CRenderer::InitScanEdges(void)
{
_pfRenderProfile.StartTimer(CRenderProfile::PTI_INITSCANEDGES);
// empty active lists
re_aaceActiveEdges.PopAll(); re_aaceActiveEdges.SetAllocationStep(256);
re_aaceActiveEdgesTmp.PopAll(); re_aaceActiveEdgesTmp.SetAllocationStep(256);
// set up left sentinel as left edge of screen and add it to head of active list
re_sedLeftSentinel.sed_xI = FIX16_16(re_fbbClipBox.Min()(1)-SENTINELEDGE_EPSILON);
re_sedLeftSentinel.sed_xIStep = FIX16_16(0);
re_sedLeftSentinel.sed_pspo = &re_spoFarSentinel;
re_aaceActiveEdges.Push() = CActiveEdge(&re_sedLeftSentinel);
// set up right sentinel as right edge of screen and add it to tail of active list
re_sedRightSentinel.sed_xI = FIX16_16(re_fbbClipBox.Max()(1)+SENTINELEDGE_EPSILON);
re_sedRightSentinel.sed_xIStep = FIX16_16(0);
re_sedRightSentinel.sed_pspo = &re_spoFarSentinel;
re_aaceActiveEdges.Push() = CActiveEdge(&re_sedRightSentinel);
// set up far sentinel as infinitely far polygon
re_spoFarSentinel.spo_pgOoK.Constant(-999999.0f); // further than infinity
re_spoFarSentinel.spo_iInStack = 1;
re_spoFarSentinel.spo_psedSpanStart = &re_sedLeftSentinel;
re_spoFarSentinel.spo_pbpoBrushPolygon = NULL;
re_spoFarSentinel.spo_ubIllumination = 0;
// initialize list of spans for far sentinel
re_spoFarSentinel.spo_spoScenePolygon.spo_cColor = re_pwoWorld->wo_colBackground;
re_spoFarSentinel.spo_spoScenePolygon.spo_aptoTextures[0] = NULL;
re_spoFarSentinel.spo_spoScenePolygon.spo_aptoTextures[1] = NULL;
re_spoFarSentinel.spo_spoScenePolygon.spo_aptoTextures[2] = NULL;
re_spoFarSentinel.spo_spoScenePolygon.spo_psmShadowMap = NULL;
re_spoFarSentinel.spo_spoScenePolygon.spo_ulFlags = SPOF_BACKLIGHT;
// add it to the list of background span polygons in this renderer
re_spoFarSentinel.spo_spoScenePolygon.spo_pspoSucc = re_pspoFirstBackground;
re_pspoFirstBackground = &re_spoFarSentinel.spo_spoScenePolygon;
// add far sentinel as bottom of surface stack
ASSERT(re_lhSurfaceStack.IsEmpty());
re_lhSurfaceStack.AddTail(re_spoFarSentinel.spo_lnInStack);
_pfRenderProfile.StopTimer(CRenderProfile::PTI_INITSCANEDGES);
}
/*
* Clean up list of active edges.
*/
void CRenderer::EndScanEdges(void)
{
_pfRenderProfile.StartTimer(CRenderProfile::PTI_ENDSCANEDGES);
// remove far sentinel from surface stack
ASSERT(re_spoFarSentinel.spo_iInStack == 1);
re_spoFarSentinel.spo_lnInStack.Remove();
re_spoFarSentinel.spo_iInStack = 0;
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ENDSCANEDGES);
}
/*
* Add a polygon to surface stack.
*/
BOOL CRenderer::AddPolygonToSurfaceStack(CScreenPolygon &spo)
{
// increment in-stack counter
spo.spo_iInStack++;
// if it doesn't have to be added
if (spo.spo_iInStack!=1) {
// return that this is not new top
return FALSE;
}
#define BIAS (1)
//#define BIAS (0)
FLOAT fScanI = FLOAT(re_xCurrentScanI)+BIAS;
FLOAT fScanJ = re_fCurrentScanJ;//+re_fMinJ;
// calculate 1/k for new polygon
CPlanarGradients &pg = spo.spo_pgOoK;
FLOAT fOoK = pg.pg_f00 + pg.pg_fDOverDI*fScanI + pg.pg_fDOverDJ*fScanJ;
// bias for right edges - fix against generating extra trapezoids
fOoK*=re_fEdgeAdjustK;
// must not be infinitely far, except if background polygon
//ASSERT(fOneOverK>0.0f || fOneOverK==-9999.0f);
// cannot assert on this, because of +1 bias
// start at top surface in stack
LISTITER(CScreenPolygon, spo_lnInStack) itspo(re_lhSurfaceStack);
// if the projection is not perspective
if (!re_prProjection.IsPerspective()) {
// while new polygon is further than polygon in stack
while(
((fOoK -
itspo->spo_pgOoK.pg_f00 -
itspo->spo_pgOoK.pg_fDOverDI*fScanI -
itspo->spo_pgOoK.pg_fDOverDJ*fScanJ)<0)
&& (&*itspo != &re_spoFarSentinel)) {
// move to next polygon in stack
itspo.MoveToNext();
}
} else {
// while new polygon is further than polygon in stack
FLOAT fDelta = fOoK -
itspo->spo_pgOoK.pg_f00 -
itspo->spo_pgOoK.pg_fDOverDI*fScanI -
itspo->spo_pgOoK.pg_fDOverDJ*fScanJ;
if (((SLONG &)fDelta) < 0) {
do {
// the polygon in stack must not be far sentinel
ASSERT(&*itspo != &re_spoFarSentinel);
// move to next polygon in stack
itspo.MoveToNext();
fDelta = fOoK -
itspo->spo_pgOoK.pg_f00 -
itspo->spo_pgOoK.pg_fDOverDI*fScanI -
itspo->spo_pgOoK.pg_fDOverDJ*fScanJ;
} while (((SLONG &)fDelta) < 0);
}
}
// add the new polygon before the one in stack
itspo.InsertBeforeCurrent(spo.spo_lnInStack);
// return if this is new top of stack
return spo.spo_lnInStack.IsHead();
}
/*
* Remove a polygon from surface stack.
*/
BOOL CRenderer::RemPolygonFromSurfaceStack(CScreenPolygon &spo)
{
// decrement in-stack counter
spo.spo_iInStack--;
// if it doesn't have to be removed
if (spo.spo_iInStack!=0) {
// return that this was not top
return FALSE;
}
// if the polygon is top of stack
if (spo.spo_lnInStack.IsHead()) {
// remove the polygon from stack
spo.spo_lnInStack.Remove();
// return that this was top
return TRUE;
// if the polygon is not top of stack
} else {
// remove the polygon from stack
spo.spo_lnInStack.Remove();
// return that this was not top
return FALSE;
}
}
/*
* Swap two polygons in surface stack.
*/
BOOL CRenderer::SwapPolygonsInSurfaceStack(CScreenPolygon &spoOld, CScreenPolygon &spoNew)
{
// !!!!! fix the problems with surfaces beeing multiple times added
// to the stack before reenabling this feature!
ASSERT(FALSE);
// decrement/increment in-stack counters
spoOld.spo_iInStack--;
spoNew.spo_iInStack++;
ASSERT(spoOld.spo_iInStack==0);
ASSERT(spoNew.spo_iInStack==1);
// if the left polygon is top of stack
if (spoOld.spo_lnInStack.IsHead()) {
// swap them
CListNode &lnBefore = spoOld.spo_lnInStack.IterationPred();
spoOld.spo_lnInStack.Remove();
lnBefore.IterationInsertAfter(spoNew.spo_lnInStack);
return TRUE;
// if the polygon is not top of stack
} else {
// swap them
CListNode &lnBefore = spoOld.spo_lnInStack.IterationPred();
spoOld.spo_lnInStack.Remove();
lnBefore.IterationInsertAfter(spoNew.spo_lnInStack);
// return that this was not top
return FALSE;
}
}
/*
* Remove all polygons from surface stack.
*/
void CRenderer::FlushSurfaceStack(void)
{
// while there is some polygon above far sentinel in surface stack
CScreenPolygon *pspoTop;
while ((pspoTop = LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack))
!= &re_spoFarSentinel) {
// it must be linked in stack
#if 0
if (pspoTop->spo_iInStack<=0) {
_pSCape->DebugSave();
FatalError("Surface stack bug encountered!\nDebug game saved!");
}
ASSERT(pspoTop->spo_iInStack>0);
#endif
// remove it from stack
pspoTop->spo_lnInStack.Remove();
pspoTop->spo_iInStack = 0;
}
}
/*
* Scan list of active edges into spans.
*/
CScreenPolygon *CRenderer::ScanOneLine(void)
{
_pfRenderProfile.StartTimer(CRenderProfile::PTI_SCANONELINE);
IFDEBUG(FIX16_16 xCurrentI = FIX16_16(-10));
// reinit far sentinel
re_spoFarSentinel.spo_iInStack = 1;
re_spoFarSentinel.spo_psedSpanStart = &re_sedLeftSentinel;
// clear list of spans for current line
re_aspSpans.PopAll();
// if left and right sentinels are sorted wrong
if (re_aaceActiveEdges[0].ace_psedEdge!=&re_sedLeftSentinel
||re_aaceActiveEdges[re_aaceActiveEdges.Count()-1].ace_psedEdge!=&re_sedRightSentinel) {
// skip entire line (this patches some extremely rare crash situations)
_pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANONELINE);
return NULL;
}
// for all edges in the line
CActiveEdge *pace = &re_aaceActiveEdges[1];
CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
while (pace<paceEnd) {
CScreenEdge &sed = *pace->ace_psedEdge;
ASSERT(&sed!=&re_sedLeftSentinel);
ASSERT(&sed!=&re_sedRightSentinel);
// set up current I coordinate on the scan line
re_xCurrentScanI = sed.sed_xI = pace->ace_xI;
// check that edges are sorted ok
ASSERT(xCurrentI <= sed.sed_xI);
// count edge transitions
_pfRenderProfile.IncrementCounter(CRenderProfile::PCI_EDGETRANSITIONS);
_sfStats.IncrementCounter(CStatForm::SCI_EDGETRANSITIONS);
// if this edge has active polygon
if (sed.sed_pspo!= NULL && sed.sed_pspo->spo_bActive) {
CScreenPolygon &spo = *sed.sed_pspo;
// if it is right edge of the polygon
if (sed.sed_ldtDirection==LDT_ASCENDING) {
// remove the left polygon from stack
BOOL bWasTop = RemPolygonFromSurfaceStack(spo);
// if that was top polygon in surface stack
if (bWasTop) {
// if it is portal
if (spo.IsPortal() &&
(re_ubLightIllumination==0||re_ubLightIllumination!=spo.spo_ubIllumination)) {
// fail scanning and add that portal
_pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANONELINE);
return &spo;
}
// generate a span for it
MakeSpan(spo, spo.spo_psedSpanStart, &sed);
// mark that span of new top starts here
CScreenPolygon *pspoNewTop =
LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack);
pspoNewTop->spo_psedSpanStart = &sed;
}
// if it is left edge of the polygon
} else {
ASSERT(sed.sed_ldtDirection==LDT_DESCENDING);
// add the right polygon to stack
BOOL bIsTop = AddPolygonToSurfaceStack(spo);
// if it is the new top of surface stack
if (bIsTop) {
// get the old top
CScreenPolygon &spoOldTop = *LIST_SUCC(spo, CScreenPolygon, spo_lnInStack);
// if it is portal
if (spoOldTop.IsPortal() &&
(re_ubLightIllumination==0||re_ubLightIllumination!=spoOldTop.spo_ubIllumination)) {
// if its span has at least one pixel in length
if ( PIXCoord(re_xCurrentScanI)-PIXCoord(spoOldTop.spo_psedSpanStart->sed_xI)>0) {
// fail scanning and add that portal
_pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANONELINE);
return &spoOldTop;
}
// if it is not portal
} else {
// generate span for old top
MakeSpan(spoOldTop, spoOldTop.spo_psedSpanStart, &sed);
}
// mark that span of new polygon starts here
spo.spo_psedSpanStart = &sed;
}
}
IFDEBUG(xCurrentI = pace->ace_xI);
// if this edge has no active polygon
} else {
// mark it for removal
sed.sed_xI.slHolder = ACE_REMOVED;
}
pace++;
}
// NOTE: In some rare and extreme situations (usually when casting shadows)
// the stack might not be empty after scanning - this code fixes that.
// if surface stack contains something else except background
CScreenPolygon *pspoTop = LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack);
if (&re_spoFarSentinel != pspoTop) {
// ASSERTALWAYS("Bug in ASER: Surface stack not empty!");
CScreenPolygon &spo = *pspoTop;
// generate span of the top polygon to the right border
if (!(spo.IsPortal()
&& (re_ubLightIllumination==0 || re_ubLightIllumination!=spo.spo_ubIllumination))) {
MakeSpan(spo, spo.spo_psedSpanStart, &re_sedRightSentinel);
}
// remove all left-over polygons from stack
do {
BOOL bWasTop = RemPolygonFromSurfaceStack(*pspoTop);
pspoTop = LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack);
} while (&re_spoFarSentinel != pspoTop);
// mark start of background span at right border
re_spoFarSentinel.spo_psedSpanStart = &re_sedRightSentinel;
}
// generate span for far sentinel
MakeSpan(re_spoFarSentinel, re_spoFarSentinel.spo_psedSpanStart, &re_sedRightSentinel);
// return that no portal was encountered
_pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANONELINE);
return NULL;
}
/*
* Rasterize edges into spans.
*/
void CRenderer::ScanEdges(void)
{
_pfRenderProfile.StartTimer(CRenderProfile::PTI_SCANEDGES);
// set up the list of active edges, surface stack and the sentinels
InitScanEdges();
// mark that first line is never coherent with previous one
re_bCoherentScanLine = 0;
// for each scan line, top to bottom
for (re_iCurrentScan = 0; re_iCurrentScan<re_ctScanLines; re_iCurrentScan++) {
re_pixCurrentScanJ = re_iCurrentScan + re_pixTopScanLineJ;
re_fCurrentScanJ = FLOAT(re_pixCurrentScanJ);
_pfRenderProfile.IncrementCounter(CRenderProfile::PCI_OVERALLSCANLINES);
CScreenPolygon *pspoPortal; // pointer to portal encountered while scanning
// add all edges that start on this scan line to active list
AddAddListToActiveList(re_iCurrentScan);
// if scan-line is coherent with the last one
/*!!!! if (re_bCoherentScanLine>0) {
// increment counter of coherent scan lines
_pfRenderProfile.IncrementCounter(CRenderProfile::PCI_COHERENTSCANLINES);
// just copy I coordinates from active list to edge data
CopyActiveCoordinates();
// if scan-line is not coherent with the last one
} else*/ {
// scan list of active edges into spans
pspoPortal = ScanOneLine();
// while portal is encountered during scanning
while (pspoPortal != NULL) {
// increment counter of portal retries
_pfRenderProfile.IncrementCounter(CRenderProfile::PCI_SCANLINEPORTALRETRIES);
// remove all polygons from surface stack
FlushSurfaceStack();
// add sectors near the encountered portal to rendering
PassPortal(*pspoPortal);
// add all newly added edges that start on this scan line to active list
AddAddListToActiveList(re_iCurrentScan);
// rescan list of active edges into spans again
pspoPortal = ScanOneLine();
}
}
// set scan-line coherence marker
re_bCoherentScanLine++;
// surface stack must contain only background
ASSERT(&re_spoFarSentinel == LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack)
&& &re_spoFarSentinel == LIST_TAIL(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack));
// add spans in this line to the scene
AddSpansToScene();
// uncomment this for extreme checking of surface stack management -- very slow
#if 0
// all surfaces must have in-stack counter of zero
FOREACHINDYNAMICARRAY(re_aspoScreenPolygons, CScreenPolygon, itspo) {
CScreenPolygon &spo = itspo.Current();
ASSERT(spo.spo_iInStack == 0);
}
#endif
// remove all edges that stop on this scan from active list and from other lists.
RemRemoveListFromActiveList(re_apsedRemoveFirst[re_iCurrentScan]);
// step all remaining edges by one scan line and resort the active list
StepAndResortActiveList();
}
// clean up the list of active edges, surface stack and the sentinels
EndScanEdges();
_pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANEDGES);
}