/* 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 (paceSrcace_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 (paceSrcsed_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 (paceSrcace_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 (paceSrcace_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 (paceace_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_iCurrentScan0) { // 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); }