RendASER.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. /*
  13. * Add all edges in add list to active list.
  14. */
  15. void CRenderer::AddAddListToActiveList(INDEX iScanLine)
  16. {
  17. INDEX ctAddEdges = re_actAddCounts[iScanLine];
  18. // if the add list is empty
  19. if (ctAddEdges==0) {
  20. // do nothing
  21. return;
  22. }
  23. _pfRenderProfile.StartTimer(CRenderProfile::PTI_ADDADDLIST);
  24. CListHead &lhAdd = re_alhAddLists[iScanLine];
  25. ASSERT(ctAddEdges==lhAdd.Count());
  26. // mark that scan-line coherence is lost
  27. re_bCoherentScanLine = 0;
  28. // allocate space in destination for sum of source and add
  29. INDEX ctActiveEdges = re_aaceActiveEdges.Count();
  30. re_aaceActiveEdgesTmp.Push(ctAddEdges+ctActiveEdges);
  31. // check that the add list is sorted right
  32. #if ASER_EXTREME_CHECKING
  33. {
  34. LISTITER(CAddEdge, ade_lnInAdd) itadeAdd(lhAdd);
  35. FIX16_16 xLastI;
  36. xLastI.slHolder = MIN_SLONG;
  37. while(!itadeAdd.IsPastEnd()) {
  38. CAddEdge &adeAdd = *itadeAdd;
  39. ASSERT(adeAdd.ade_xI==adeAdd.ade_psedEdge->sed_xI);
  40. ASSERT(xLastI.slHolder <= adeAdd.ade_xI.slHolder);
  41. xLastI = adeAdd.ade_xI;
  42. itadeAdd.MoveToNext();
  43. }
  44. }
  45. #endif
  46. // check that the active list is sorted right
  47. #if ASER_EXTREME_CHECKING
  48. {
  49. CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
  50. CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
  51. while (paceSrc<paceEnd) {
  52. ASSERT(paceSrc[0].ace_xI.slHolder <= paceSrc[1].ace_xI.slHolder);
  53. paceSrc++;
  54. };
  55. }
  56. #endif
  57. // start at begining of add list, source active list and destination active list
  58. LISTITER(CAddEdge, ade_lnInAdd) itadeAdd(lhAdd);
  59. CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
  60. CActiveEdge *paceDst = &re_aaceActiveEdgesTmp[0];
  61. IFDEBUG(INDEX ctNewActive=0);
  62. IFDEBUG(INDEX ctOldActive1=0);
  63. IFDEBUG(INDEX ctOldActive2=0);
  64. // for each edge in add list
  65. while(!itadeAdd.IsPastEnd()) {
  66. CAddEdge &ade = *itadeAdd;
  67. // while the edge in active list is left of the edge in add list
  68. while (paceSrc->ace_xI.slHolder < itadeAdd->ade_xI.slHolder) {
  69. // copy the active edge
  70. ASSERT(paceSrc<=&re_aaceActiveEdges[ctActiveEdges-1]);
  71. *paceDst++=*paceSrc++;
  72. IFDEBUG(ctOldActive1++);
  73. }
  74. // copy the add edge
  75. ASSERT(paceDst > &re_aaceActiveEdgesTmp[0]);
  76. ASSERT(ade.ade_xI.slHolder == ade.ade_psedEdge->sed_xI.slHolder);
  77. ASSERT(paceDst[-1].ace_xI.slHolder <= ade.ade_xI.slHolder);
  78. *paceDst++=CActiveEdge(itadeAdd->ade_psedEdge);
  79. IFDEBUG(ctNewActive++);
  80. // advance iterator in add list
  81. itadeAdd.MoveToNext();
  82. }
  83. // clear the add list
  84. lhAdd.Clear();
  85. re_actAddCounts[iScanLine] = 0;
  86. // copy all edges left in the active list
  87. while (paceSrc<=&re_aaceActiveEdges[ctActiveEdges-1]) {
  88. *paceDst++=*paceSrc++;
  89. IFDEBUG(ctOldActive2++);
  90. }
  91. // swap the lists
  92. Swap(re_aaceActiveEdges.sa_Count , re_aaceActiveEdgesTmp.sa_Count );
  93. Swap(re_aaceActiveEdges.sa_Array , re_aaceActiveEdgesTmp.sa_Array );
  94. Swap(re_aaceActiveEdges.sa_UsedCount, re_aaceActiveEdgesTmp.sa_UsedCount);
  95. re_aaceActiveEdgesTmp.PopAll();
  96. if (ctAddEdges>_ctMaxAddEdges) {
  97. _ctMaxAddEdges=ctAddEdges;
  98. }
  99. if (re_aaceActiveEdges.Count()>_ctMaxActiveEdges) {
  100. _ctMaxActiveEdges=re_aaceActiveEdges.Count();
  101. }
  102. // check that the active list is sorted right
  103. #if ASER_EXTREME_CHECKING
  104. {
  105. CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
  106. CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
  107. while (paceSrc<paceEnd) {
  108. ASSERT(paceSrc[0].ace_xI.slHolder <= paceSrc[1].ace_xI.slHolder);
  109. paceSrc++;
  110. };
  111. }
  112. #endif
  113. _pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDADDLIST);
  114. }
  115. /*
  116. * Remove all edges in remove list from active list and from other lists.
  117. */
  118. void CRenderer::RemRemoveListFromActiveList(CScreenEdge *psedFirst)
  119. {
  120. // if the remove list is empty
  121. if (psedFirst==NULL) {
  122. // do nothing
  123. return;
  124. }
  125. _pfRenderProfile.StartTimer(CRenderProfile::PTI_REMREMLIST);
  126. // mark that scan-line coherence is lost
  127. re_bCoherentScanLine = 0;
  128. // for each edge to be removed on this line
  129. CScreenEdge *psed = psedFirst;
  130. do {
  131. // mark it as removed
  132. psed->sed_xI.slHolder = ACE_REMOVED;
  133. psed = psed->sed_psedNextRemove;
  134. } while (psed!=NULL);
  135. // for each active edge
  136. CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
  137. CActiveEdge *paceSrc = &re_aaceActiveEdges[1];
  138. CActiveEdge *paceDst = paceSrc;
  139. do {
  140. // if it is not removed
  141. if (paceSrc->ace_psedEdge->sed_xI.slHolder!=ACE_REMOVED) {
  142. // copy it
  143. *paceDst = *paceSrc;
  144. paceDst++;
  145. }
  146. paceSrc++;
  147. } while (paceSrc<=paceEnd);
  148. // trim the end of active list
  149. re_aaceActiveEdges.PopUntil(paceDst-&re_aaceActiveEdges[0]-1);
  150. // check that the active list is sorted right
  151. #if ASER_EXTREME_CHECKING
  152. {
  153. CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
  154. CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
  155. while (paceSrc<paceEnd) {
  156. ASSERT(paceSrc[0].ace_xI.slHolder <= paceSrc[1].ace_xI.slHolder);
  157. paceSrc++;
  158. };
  159. }
  160. #endif
  161. _pfRenderProfile.StopTimer(CRenderProfile::PTI_REMREMLIST);
  162. }
  163. /*
  164. * Step all edges in active list by one scan line and resort them.
  165. */
  166. void CRenderer::StepAndResortActiveList(void)
  167. {
  168. _pfRenderProfile.StartTimer(CRenderProfile::PTI_STEPANDRESORT);
  169. // start after the left sentinel
  170. CActiveEdge *pace = &re_aaceActiveEdges[1];
  171. // for all edges before right sentinel
  172. CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
  173. do {
  174. // step the edge by one scan line
  175. pace->ace_xI.slHolder += pace->ace_xIStep.slHolder;
  176. // if the previous is right of the current
  177. if (pace[-1].ace_xI.slHolder > pace->ace_xI.slHolder) {
  178. // mark that scan-line coherence is lost
  179. re_bCoherentScanLine = 0;
  180. // find last one that is not right
  181. CActiveEdge *pacePred = pace;
  182. do {
  183. pacePred--;
  184. } while(pacePred->ace_xI.slHolder > pace->ace_xI.slHolder);
  185. // remember the current one
  186. CActiveEdge aceCurrent = *pace;
  187. // move all of the edges between one place forward
  188. CActiveEdge *paceMove=pace-1;
  189. do {
  190. paceMove[1]=paceMove[0];
  191. paceMove--;
  192. } while (paceMove>pacePred);
  193. // insert the current to its new place
  194. paceMove[1] = aceCurrent;
  195. }
  196. pace++;
  197. } while (pace < paceEnd);
  198. // check that the active list is sorted right
  199. #if ASER_EXTREME_CHECKING
  200. {
  201. CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
  202. CActiveEdge *paceSrc = &re_aaceActiveEdges[0];
  203. while (paceSrc<paceEnd) {
  204. ASSERT(paceSrc[0].ace_xI.slHolder <= paceSrc[1].ace_xI.slHolder);
  205. paceSrc++;
  206. };
  207. }
  208. #endif
  209. _pfRenderProfile.StopTimer(CRenderProfile::PTI_STEPANDRESORT);
  210. }
  211. /* Copy I coordinates from active list to edge data. */
  212. void CRenderer::CopyActiveCoordinates(void)
  213. {
  214. // for all active edges
  215. CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
  216. CActiveEdge *pace = &re_aaceActiveEdges[1];
  217. do {
  218. // copy active coordinates
  219. pace->ace_psedEdge->sed_xI.slHolder = pace->ace_xI.slHolder;
  220. pace++;
  221. } while (pace<=paceEnd);
  222. }
  223. /*
  224. * Remove an active portal from rendering
  225. */
  226. void CRenderer::RemovePortal(CScreenPolygon &spo)
  227. {
  228. ASSERT(spo.IsPortal());
  229. ASSERT(spo.spo_bActive);
  230. spo.spo_bActive = FALSE;
  231. // if it is a translucent portal
  232. if (spo.spo_pbpoBrushPolygon->bpo_ulFlags & (BPOF_RENDERTRANSLUCENT|BPOF_TRANSPARENT)) {
  233. _pfRenderProfile.StartTimer(CRenderProfile::PTI_PROCESSTRANSPORTAL);
  234. // add polygon to scene polygons for rendering
  235. AddPolygonToScene(&spo);
  236. _pfRenderProfile.StopTimer(CRenderProfile::PTI_PROCESSTRANSPORTAL);
  237. }
  238. // if it is in the surface stack
  239. if (spo.spo_lnInStack.IsLinked()) {
  240. // remove it from surface stack
  241. RemPolygonFromSurfaceStack(spo);
  242. spo.spo_iInStack = 0;
  243. }
  244. }
  245. /*
  246. * Add sector(s) adjoined to a portal to rendering and remove the portal.
  247. */
  248. void CRenderer::PassPortal(CScreenPolygon &spo)
  249. {
  250. ChangeStatsMode(CStatForm::STI_WORLDTRANSFORM);
  251. _pfRenderProfile.StartTimer(CRenderProfile::PTI_PASSPORTAL);
  252. // remove the portal from rendering
  253. RemovePortal(spo);
  254. // for all sectors related to the portal
  255. {FOREACHDSTOFSRC(spo.spo_pbpoBrushPolygon->bpo_rsOtherSideSectors, CBrushSector, bsc_rdOtherSidePortals, pbsc)
  256. // if the sector is hidden when not rendering shadows
  257. if ((pbsc->bsc_ulFlags&BSCF_HIDDEN) && !re_bRenderingShadows) {
  258. // skip it
  259. continue;
  260. }
  261. // get brush of the sector
  262. CBrushMip *pbmSectorMip = pbsc->bsc_pbmBrushMip;
  263. CBrush3D &brBrush = *pbmSectorMip->bm_pbrBrush;
  264. // prepare the brush entity for rendering if not yet prepared
  265. PrepareBrush(brBrush.br_penEntity);
  266. // get relevant mip factor for that brush and current rendering prefs
  267. CBrushMip *pbmRelevantMip;
  268. if (brBrush.br_ulFlags&BRF_DRAWFIRSTMIP) {
  269. pbmRelevantMip = brBrush.GetBrushMipByDistance(
  270. _wrpWorldRenderPrefs.GetCurrentMipBrushingFactor(0.0f));
  271. } else {
  272. pbmRelevantMip = brBrush.GetBrushMipByDistance(
  273. _wrpWorldRenderPrefs.GetCurrentMipBrushingFactor(brBrush.br_prProjection->MipFactor()));
  274. }
  275. // if relevant brush mip is same as the sector's brush mip
  276. if (pbmSectorMip==pbmRelevantMip) {
  277. // add that sector to active sectors
  278. AddActiveSector(*pbsc);
  279. }
  280. ENDFOR}
  281. _pfRenderProfile.StopTimer(CRenderProfile::PTI_PASSPORTAL);
  282. ChangeStatsMode(CStatForm::STI_WORLDVISIBILITY);
  283. }
  284. /*
  285. * Add a sector of a brush to rendering queues.
  286. */
  287. void CRenderer::AddActiveSector(CBrushSector &bscSector)
  288. {
  289. // if already active
  290. if (bscSector.bsc_lnInActiveSectors.IsLinked()) {
  291. // do nothing;
  292. return;
  293. }
  294. _pfRenderProfile.StartTimer(CRenderProfile::PTI_ADDSECTOR);
  295. // add it to active sectors list
  296. re_lhActiveSectors.AddTail(bscSector.bsc_lnInActiveSectors);
  297. ASSERT((_controlfp(0, 0)&_MCW_RC)==_RC_NEAR);
  298. CBrush3D &br = *bscSector.bsc_pbmBrushMip->bm_pbrBrush;
  299. // if should render field brush sector
  300. if (br.br_penEntity->en_RenderType==CEntity::RT_FIELDBRUSH
  301. && !_wrpWorldRenderPrefs.IsFieldBrushesOn()) {
  302. // skip it
  303. bscSector.bsc_ulFlags|=BSCF_INVISIBLE;
  304. _pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDSECTOR);
  305. return;
  306. }
  307. // test sector visibility
  308. const INDEX iFrustrumTest = IsSectorVisible( br, bscSector);
  309. if( iFrustrumTest==-1) {
  310. // outside of frustrum - skip it
  311. bscSector.bsc_ulFlags |= BSCF_INVISIBLE;
  312. _pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDSECTOR);
  313. return;
  314. } else if( iFrustrumTest==0) {
  315. // partially in frustrum - needs clipping
  316. bscSector.bsc_ulFlags |= BSCF_NEEDSCLIPPING;
  317. } else {
  318. // completely in frustrum - doesn't need clipping
  319. bscSector.bsc_ulFlags &= ~BSCF_NEEDSCLIPPING;
  320. }
  321. // mark that sector is visible
  322. bscSector.bsc_ulFlags &= ~BSCF_INVISIBLE;
  323. // remember current sector
  324. re_pbscCurrent = &bscSector;
  325. re_pbrCurrent = &br;
  326. _sfStats.IncrementCounter(CStatForm::SCI_SECTORS);
  327. // if projection is perspective
  328. if( br.br_prProjection.IsPerspective()) {
  329. // prepare fog/haze
  330. SetupFogAndHaze();
  331. }
  332. // transform all vertices and planes in this sector
  333. PreClipVertices();
  334. PreClipPlanes();
  335. // if polygons should be drawn
  336. if (_wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE
  337. ||re_bRenderingShadows) {
  338. // find which portals should be rendered as portals or as pretenders
  339. FindPretenders();
  340. // make screen polygons for nondetail polygons in current sector
  341. MakeNonDetailScreenPolygons();
  342. // clip all polygons to all clip planes
  343. if( bscSector.bsc_ulFlags&BSCF_NEEDSCLIPPING) ClipToAllPlanes( br.br_prProjection);
  344. // project vertices to 2d
  345. PostClipVertices();
  346. // make final edges for all polygons in current sector
  347. MakeFinalPolygonEdges();
  348. // add screen edges for all polygons in current sector
  349. AddScreenEdges();
  350. // make screen polygons for detail polygons in current sector
  351. MakeDetailScreenPolygons();
  352. }
  353. _pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDSECTOR);
  354. // get the entity the sector is in
  355. CEntity *penSectorEntity = bscSector.bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
  356. // if it has the entity (it is not the background brush)
  357. if (penSectorEntity != NULL) {
  358. // add all other entities near the sector
  359. AddEntitiesInSector(&bscSector);
  360. }
  361. }
  362. /*
  363. * Initialize list of active edges.
  364. */
  365. void CRenderer::InitScanEdges(void)
  366. {
  367. _pfRenderProfile.StartTimer(CRenderProfile::PTI_INITSCANEDGES);
  368. // empty active lists
  369. re_aaceActiveEdges.PopAll(); re_aaceActiveEdges.SetAllocationStep(256);
  370. re_aaceActiveEdgesTmp.PopAll(); re_aaceActiveEdgesTmp.SetAllocationStep(256);
  371. // set up left sentinel as left edge of screen and add it to head of active list
  372. re_sedLeftSentinel.sed_xI = FIX16_16(re_fbbClipBox.Min()(1)-SENTINELEDGE_EPSILON);
  373. re_sedLeftSentinel.sed_xIStep = FIX16_16(0);
  374. re_sedLeftSentinel.sed_pspo = &re_spoFarSentinel;
  375. re_aaceActiveEdges.Push() = CActiveEdge(&re_sedLeftSentinel);
  376. // set up right sentinel as right edge of screen and add it to tail of active list
  377. re_sedRightSentinel.sed_xI = FIX16_16(re_fbbClipBox.Max()(1)+SENTINELEDGE_EPSILON);
  378. re_sedRightSentinel.sed_xIStep = FIX16_16(0);
  379. re_sedRightSentinel.sed_pspo = &re_spoFarSentinel;
  380. re_aaceActiveEdges.Push() = CActiveEdge(&re_sedRightSentinel);
  381. // set up far sentinel as infinitely far polygon
  382. re_spoFarSentinel.spo_pgOoK.Constant(-999999.0f); // further than infinity
  383. re_spoFarSentinel.spo_iInStack = 1;
  384. re_spoFarSentinel.spo_psedSpanStart = &re_sedLeftSentinel;
  385. re_spoFarSentinel.spo_pbpoBrushPolygon = NULL;
  386. re_spoFarSentinel.spo_ubIllumination = 0;
  387. // initialize list of spans for far sentinel
  388. re_spoFarSentinel.spo_spoScenePolygon.spo_cColor = re_pwoWorld->wo_colBackground;
  389. re_spoFarSentinel.spo_spoScenePolygon.spo_aptoTextures[0] = NULL;
  390. re_spoFarSentinel.spo_spoScenePolygon.spo_aptoTextures[1] = NULL;
  391. re_spoFarSentinel.spo_spoScenePolygon.spo_aptoTextures[2] = NULL;
  392. re_spoFarSentinel.spo_spoScenePolygon.spo_psmShadowMap = NULL;
  393. re_spoFarSentinel.spo_spoScenePolygon.spo_ulFlags = SPOF_BACKLIGHT;
  394. // add it to the list of background span polygons in this renderer
  395. re_spoFarSentinel.spo_spoScenePolygon.spo_pspoSucc = re_pspoFirstBackground;
  396. re_pspoFirstBackground = &re_spoFarSentinel.spo_spoScenePolygon;
  397. // add far sentinel as bottom of surface stack
  398. ASSERT(re_lhSurfaceStack.IsEmpty());
  399. re_lhSurfaceStack.AddTail(re_spoFarSentinel.spo_lnInStack);
  400. _pfRenderProfile.StopTimer(CRenderProfile::PTI_INITSCANEDGES);
  401. }
  402. /*
  403. * Clean up list of active edges.
  404. */
  405. void CRenderer::EndScanEdges(void)
  406. {
  407. _pfRenderProfile.StartTimer(CRenderProfile::PTI_ENDSCANEDGES);
  408. // remove far sentinel from surface stack
  409. ASSERT(re_spoFarSentinel.spo_iInStack == 1);
  410. re_spoFarSentinel.spo_lnInStack.Remove();
  411. re_spoFarSentinel.spo_iInStack = 0;
  412. _pfRenderProfile.StopTimer(CRenderProfile::PTI_ENDSCANEDGES);
  413. }
  414. /*
  415. * Add a polygon to surface stack.
  416. */
  417. BOOL CRenderer::AddPolygonToSurfaceStack(CScreenPolygon &spo)
  418. {
  419. // increment in-stack counter
  420. spo.spo_iInStack++;
  421. // if it doesn't have to be added
  422. if (spo.spo_iInStack!=1) {
  423. // return that this is not new top
  424. return FALSE;
  425. }
  426. #define BIAS (1)
  427. //#define BIAS (0)
  428. FLOAT fScanI = FLOAT(re_xCurrentScanI)+BIAS;
  429. FLOAT fScanJ = re_fCurrentScanJ;//+re_fMinJ;
  430. // calculate 1/k for new polygon
  431. CPlanarGradients &pg = spo.spo_pgOoK;
  432. FLOAT fOoK = pg.pg_f00 + pg.pg_fDOverDI*fScanI + pg.pg_fDOverDJ*fScanJ;
  433. // bias for right edges - fix against generating extra trapezoids
  434. fOoK*=re_fEdgeAdjustK;
  435. // must not be infinitely far, except if background polygon
  436. //ASSERT(fOneOverK>0.0f || fOneOverK==-9999.0f);
  437. // cannot assert on this, because of +1 bias
  438. // start at top surface in stack
  439. LISTITER(CScreenPolygon, spo_lnInStack) itspo(re_lhSurfaceStack);
  440. // if the projection is not perspective
  441. if (!re_prProjection.IsPerspective()) {
  442. // while new polygon is further than polygon in stack
  443. while(
  444. ((fOoK -
  445. itspo->spo_pgOoK.pg_f00 -
  446. itspo->spo_pgOoK.pg_fDOverDI*fScanI -
  447. itspo->spo_pgOoK.pg_fDOverDJ*fScanJ)<0)
  448. && (&*itspo != &re_spoFarSentinel)) {
  449. // move to next polygon in stack
  450. itspo.MoveToNext();
  451. }
  452. } else {
  453. // while new polygon is further than polygon in stack
  454. FLOAT fDelta = fOoK -
  455. itspo->spo_pgOoK.pg_f00 -
  456. itspo->spo_pgOoK.pg_fDOverDI*fScanI -
  457. itspo->spo_pgOoK.pg_fDOverDJ*fScanJ;
  458. if (((SLONG &)fDelta) < 0) {
  459. do {
  460. // the polygon in stack must not be far sentinel
  461. ASSERT(&*itspo != &re_spoFarSentinel);
  462. // move to next polygon in stack
  463. itspo.MoveToNext();
  464. fDelta = fOoK -
  465. itspo->spo_pgOoK.pg_f00 -
  466. itspo->spo_pgOoK.pg_fDOverDI*fScanI -
  467. itspo->spo_pgOoK.pg_fDOverDJ*fScanJ;
  468. } while (((SLONG &)fDelta) < 0);
  469. }
  470. }
  471. // add the new polygon before the one in stack
  472. itspo.InsertBeforeCurrent(spo.spo_lnInStack);
  473. // return if this is new top of stack
  474. return spo.spo_lnInStack.IsHead();
  475. }
  476. /*
  477. * Remove a polygon from surface stack.
  478. */
  479. BOOL CRenderer::RemPolygonFromSurfaceStack(CScreenPolygon &spo)
  480. {
  481. // decrement in-stack counter
  482. spo.spo_iInStack--;
  483. // if it doesn't have to be removed
  484. if (spo.spo_iInStack!=0) {
  485. // return that this was not top
  486. return FALSE;
  487. }
  488. // if the polygon is top of stack
  489. if (spo.spo_lnInStack.IsHead()) {
  490. // remove the polygon from stack
  491. spo.spo_lnInStack.Remove();
  492. // return that this was top
  493. return TRUE;
  494. // if the polygon is not top of stack
  495. } else {
  496. // remove the polygon from stack
  497. spo.spo_lnInStack.Remove();
  498. // return that this was not top
  499. return FALSE;
  500. }
  501. }
  502. /*
  503. * Swap two polygons in surface stack.
  504. */
  505. BOOL CRenderer::SwapPolygonsInSurfaceStack(CScreenPolygon &spoOld, CScreenPolygon &spoNew)
  506. {
  507. // !!!!! fix the problems with surfaces beeing multiple times added
  508. // to the stack before reenabling this feature!
  509. ASSERT(FALSE);
  510. // decrement/increment in-stack counters
  511. spoOld.spo_iInStack--;
  512. spoNew.spo_iInStack++;
  513. ASSERT(spoOld.spo_iInStack==0);
  514. ASSERT(spoNew.spo_iInStack==1);
  515. // if the left polygon is top of stack
  516. if (spoOld.spo_lnInStack.IsHead()) {
  517. // swap them
  518. CListNode &lnBefore = spoOld.spo_lnInStack.IterationPred();
  519. spoOld.spo_lnInStack.Remove();
  520. lnBefore.IterationInsertAfter(spoNew.spo_lnInStack);
  521. return TRUE;
  522. // if the polygon is not top of stack
  523. } else {
  524. // swap them
  525. CListNode &lnBefore = spoOld.spo_lnInStack.IterationPred();
  526. spoOld.spo_lnInStack.Remove();
  527. lnBefore.IterationInsertAfter(spoNew.spo_lnInStack);
  528. // return that this was not top
  529. return FALSE;
  530. }
  531. }
  532. /*
  533. * Remove all polygons from surface stack.
  534. */
  535. void CRenderer::FlushSurfaceStack(void)
  536. {
  537. // while there is some polygon above far sentinel in surface stack
  538. CScreenPolygon *pspoTop;
  539. while ((pspoTop = LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack))
  540. != &re_spoFarSentinel) {
  541. // it must be linked in stack
  542. #if 0
  543. if (pspoTop->spo_iInStack<=0) {
  544. _pSCape->DebugSave();
  545. FatalError("Surface stack bug encountered!\nDebug game saved!");
  546. }
  547. ASSERT(pspoTop->spo_iInStack>0);
  548. #endif
  549. // remove it from stack
  550. pspoTop->spo_lnInStack.Remove();
  551. pspoTop->spo_iInStack = 0;
  552. }
  553. }
  554. /*
  555. * Scan list of active edges into spans.
  556. */
  557. CScreenPolygon *CRenderer::ScanOneLine(void)
  558. {
  559. _pfRenderProfile.StartTimer(CRenderProfile::PTI_SCANONELINE);
  560. IFDEBUG(FIX16_16 xCurrentI = FIX16_16(-10));
  561. // reinit far sentinel
  562. re_spoFarSentinel.spo_iInStack = 1;
  563. re_spoFarSentinel.spo_psedSpanStart = &re_sedLeftSentinel;
  564. // clear list of spans for current line
  565. re_aspSpans.PopAll();
  566. // if left and right sentinels are sorted wrong
  567. if (re_aaceActiveEdges[0].ace_psedEdge!=&re_sedLeftSentinel
  568. ||re_aaceActiveEdges[re_aaceActiveEdges.Count()-1].ace_psedEdge!=&re_sedRightSentinel) {
  569. // skip entire line (this patches some extremely rare crash situations)
  570. _pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANONELINE);
  571. return NULL;
  572. }
  573. // for all edges in the line
  574. CActiveEdge *pace = &re_aaceActiveEdges[1];
  575. CActiveEdge *paceEnd = &re_aaceActiveEdges[re_aaceActiveEdges.Count()-1];
  576. while (pace<paceEnd) {
  577. CScreenEdge &sed = *pace->ace_psedEdge;
  578. ASSERT(&sed!=&re_sedLeftSentinel);
  579. ASSERT(&sed!=&re_sedRightSentinel);
  580. // set up current I coordinate on the scan line
  581. re_xCurrentScanI = sed.sed_xI = pace->ace_xI;
  582. // check that edges are sorted ok
  583. ASSERT(xCurrentI <= sed.sed_xI);
  584. // count edge transitions
  585. _pfRenderProfile.IncrementCounter(CRenderProfile::PCI_EDGETRANSITIONS);
  586. _sfStats.IncrementCounter(CStatForm::SCI_EDGETRANSITIONS);
  587. // if this edge has active polygon
  588. if (sed.sed_pspo!= NULL && sed.sed_pspo->spo_bActive) {
  589. CScreenPolygon &spo = *sed.sed_pspo;
  590. // if it is right edge of the polygon
  591. if (sed.sed_ldtDirection==LDT_ASCENDING) {
  592. // remove the left polygon from stack
  593. BOOL bWasTop = RemPolygonFromSurfaceStack(spo);
  594. // if that was top polygon in surface stack
  595. if (bWasTop) {
  596. // if it is portal
  597. if (spo.IsPortal() &&
  598. (re_ubLightIllumination==0||re_ubLightIllumination!=spo.spo_ubIllumination)) {
  599. // fail scanning and add that portal
  600. _pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANONELINE);
  601. return &spo;
  602. }
  603. // generate a span for it
  604. MakeSpan(spo, spo.spo_psedSpanStart, &sed);
  605. // mark that span of new top starts here
  606. CScreenPolygon *pspoNewTop =
  607. LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack);
  608. pspoNewTop->spo_psedSpanStart = &sed;
  609. }
  610. // if it is left edge of the polygon
  611. } else {
  612. ASSERT(sed.sed_ldtDirection==LDT_DESCENDING);
  613. // add the right polygon to stack
  614. BOOL bIsTop = AddPolygonToSurfaceStack(spo);
  615. // if it is the new top of surface stack
  616. if (bIsTop) {
  617. // get the old top
  618. CScreenPolygon &spoOldTop = *LIST_SUCC(spo, CScreenPolygon, spo_lnInStack);
  619. // if it is portal
  620. if (spoOldTop.IsPortal() &&
  621. (re_ubLightIllumination==0||re_ubLightIllumination!=spoOldTop.spo_ubIllumination)) {
  622. // if its span has at least one pixel in length
  623. if ( PIXCoord(re_xCurrentScanI)-PIXCoord(spoOldTop.spo_psedSpanStart->sed_xI)>0) {
  624. // fail scanning and add that portal
  625. _pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANONELINE);
  626. return &spoOldTop;
  627. }
  628. // if it is not portal
  629. } else {
  630. // generate span for old top
  631. MakeSpan(spoOldTop, spoOldTop.spo_psedSpanStart, &sed);
  632. }
  633. // mark that span of new polygon starts here
  634. spo.spo_psedSpanStart = &sed;
  635. }
  636. }
  637. IFDEBUG(xCurrentI = pace->ace_xI);
  638. // if this edge has no active polygon
  639. } else {
  640. // mark it for removal
  641. sed.sed_xI.slHolder = ACE_REMOVED;
  642. }
  643. pace++;
  644. }
  645. // NOTE: In some rare and extreme situations (usually when casting shadows)
  646. // the stack might not be empty after scanning - this code fixes that.
  647. // if surface stack contains something else except background
  648. CScreenPolygon *pspoTop = LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack);
  649. if (&re_spoFarSentinel != pspoTop) {
  650. // ASSERTALWAYS("Bug in ASER: Surface stack not empty!");
  651. CScreenPolygon &spo = *pspoTop;
  652. // generate span of the top polygon to the right border
  653. if (!(spo.IsPortal()
  654. && (re_ubLightIllumination==0 || re_ubLightIllumination!=spo.spo_ubIllumination))) {
  655. MakeSpan(spo, spo.spo_psedSpanStart, &re_sedRightSentinel);
  656. }
  657. // remove all left-over polygons from stack
  658. do {
  659. BOOL bWasTop = RemPolygonFromSurfaceStack(*pspoTop);
  660. pspoTop = LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack);
  661. } while (&re_spoFarSentinel != pspoTop);
  662. // mark start of background span at right border
  663. re_spoFarSentinel.spo_psedSpanStart = &re_sedRightSentinel;
  664. }
  665. // generate span for far sentinel
  666. MakeSpan(re_spoFarSentinel, re_spoFarSentinel.spo_psedSpanStart, &re_sedRightSentinel);
  667. // return that no portal was encountered
  668. _pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANONELINE);
  669. return NULL;
  670. }
  671. /*
  672. * Rasterize edges into spans.
  673. */
  674. void CRenderer::ScanEdges(void)
  675. {
  676. _pfRenderProfile.StartTimer(CRenderProfile::PTI_SCANEDGES);
  677. // set up the list of active edges, surface stack and the sentinels
  678. InitScanEdges();
  679. // mark that first line is never coherent with previous one
  680. re_bCoherentScanLine = 0;
  681. // for each scan line, top to bottom
  682. for (re_iCurrentScan = 0; re_iCurrentScan<re_ctScanLines; re_iCurrentScan++) {
  683. re_pixCurrentScanJ = re_iCurrentScan + re_pixTopScanLineJ;
  684. re_fCurrentScanJ = FLOAT(re_pixCurrentScanJ);
  685. _pfRenderProfile.IncrementCounter(CRenderProfile::PCI_OVERALLSCANLINES);
  686. CScreenPolygon *pspoPortal; // pointer to portal encountered while scanning
  687. // add all edges that start on this scan line to active list
  688. AddAddListToActiveList(re_iCurrentScan);
  689. // if scan-line is coherent with the last one
  690. /*!!!! if (re_bCoherentScanLine>0) {
  691. // increment counter of coherent scan lines
  692. _pfRenderProfile.IncrementCounter(CRenderProfile::PCI_COHERENTSCANLINES);
  693. // just copy I coordinates from active list to edge data
  694. CopyActiveCoordinates();
  695. // if scan-line is not coherent with the last one
  696. } else/**/ {
  697. // scan list of active edges into spans
  698. pspoPortal = ScanOneLine();
  699. // while portal is encountered during scanning
  700. while (pspoPortal != NULL) {
  701. // increment counter of portal retries
  702. _pfRenderProfile.IncrementCounter(CRenderProfile::PCI_SCANLINEPORTALRETRIES);
  703. // remove all polygons from surface stack
  704. FlushSurfaceStack();
  705. // add sectors near the encountered portal to rendering
  706. PassPortal(*pspoPortal);
  707. // add all newly added edges that start on this scan line to active list
  708. AddAddListToActiveList(re_iCurrentScan);
  709. // rescan list of active edges into spans again
  710. pspoPortal = ScanOneLine();
  711. }
  712. }
  713. // set scan-line coherence marker
  714. re_bCoherentScanLine++;
  715. // surface stack must contain only background
  716. ASSERT(&re_spoFarSentinel == LIST_HEAD(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack)
  717. && &re_spoFarSentinel == LIST_TAIL(re_lhSurfaceStack, CScreenPolygon, spo_lnInStack));
  718. // add spans in this line to the scene
  719. AddSpansToScene();
  720. // uncomment this for extreme checking of surface stack management -- very slow
  721. #if 0
  722. // all surfaces must have in-stack counter of zero
  723. FOREACHINDYNAMICARRAY(re_aspoScreenPolygons, CScreenPolygon, itspo) {
  724. CScreenPolygon &spo = itspo.Current();
  725. ASSERT(spo.spo_iInStack == 0);
  726. }
  727. #endif
  728. // remove all edges that stop on this scan from active list and from other lists.
  729. RemRemoveListFromActiveList(re_apsedRemoveFirst[re_iCurrentScan]);
  730. // step all remaining edges by one scan line and resort the active list
  731. StepAndResortActiveList();
  732. }
  733. // clean up the list of active edges, surface stack and the sentinels
  734. EndScanEdges();
  735. _pfRenderProfile.StopTimer(CRenderProfile::PTI_SCANEDGES);
  736. }