123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865 |
- /* 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);
- ASSERT((_controlfp(0, 0)&_MCW_RC)==_RC_NEAR);
- 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);
- }
|