RendClip.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  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. // Clipping functions
  14. // check if a polygon is to be visible
  15. __forceinline ULONG CRenderer::GetPolygonVisibility(const CBrushPolygon &bpo)
  16. {
  17. // get transformed polygon's plane
  18. CWorkingPlane *pwplPolygonPlane = bpo.bpo_pbplPlane->bpl_pwplWorking;
  19. CWorkingPlane wplReverse;
  20. BOOL bInvertPolygon = FALSE;
  21. // if the polygon should be inverted or double sided
  22. if((re_bRenderingShadows
  23. &&!re_bDirectionalShadows
  24. &&re_ubLightIllumination!=0
  25. &&bpo.bpo_bppProperties.bpp_ubIlluminationType==re_ubLightIllumination)
  26. || (re_pbrCurrent->br_pfsFieldSettings!=NULL && !pwplPolygonPlane->wpl_bVisible)
  27. ) {
  28. bInvertPolygon = TRUE;
  29. }
  30. if (bInvertPolygon) {
  31. // make temporary inverted polygon plane
  32. pwplPolygonPlane = &wplReverse;
  33. pwplPolygonPlane->wpl_plView = -bpo.bpo_pbplPlane->bpl_pwplWorking->wpl_plView;
  34. pwplPolygonPlane->wpl_bVisible =
  35. re_pbrCurrent->br_prProjection->IsViewerPlaneVisible(pwplPolygonPlane->wpl_plView);
  36. }
  37. // if the poly is double-sided and detail
  38. if( !re_bRenderingShadows && (bpo.bpo_ulFlags&BPOF_DOUBLESIDED) && (bpo.bpo_ulFlags&BPOF_DETAILPOLYGON)) {
  39. // it's definately visible
  40. return PDF_POLYGONVISIBLE;
  41. }
  42. // if the plane is invisible
  43. if (!pwplPolygonPlane->wpl_bVisible) {
  44. // polygon is invisible
  45. return 0;
  46. }
  47. // if the polygon is invisible
  48. if ((bpo.bpo_ulFlags&BPOF_INVISIBLE)
  49. ||(re_bRenderingShadows && (bpo.bpo_ulFlags&BPOF_DOESNOTCASTSHADOW))) {
  50. // skip it
  51. return 0;
  52. }
  53. ULONG ulDirection = PDF_POLYGONVISIBLE;
  54. BOOL bProjectionInverted = re_prProjection->pr_bInverted;
  55. if (bProjectionInverted && !bInvertPolygon) {
  56. ulDirection |= PDF_FLIPEDGESPRE;
  57. } else if (!bProjectionInverted && bInvertPolygon){
  58. ulDirection |= PDF_FLIPEDGESPOST;
  59. }
  60. // else, polygon is visible
  61. return ulDirection;
  62. }
  63. // check if polygon is outside viewfrustum
  64. __forceinline BOOL CRenderer::IsPolygonCulled(const CBrushPolygon &bpo)
  65. {
  66. CBrushSector &bsc = *bpo.bpo_pbscSector;
  67. // setup initial mask
  68. ULONG ulMask = 0xFFFFFFFF;
  69. // for each vertex
  70. INDEX ctVtx = bpo.bpo_apbvxTriangleVertices.Count();
  71. {for(INDEX i=0; i<ctVtx; i++) {
  72. CBrushVertex *pbvx = bpo.bpo_apbvxTriangleVertices[i];
  73. INDEX ivx = bsc.bsc_abvxVertices.Index(pbvx);
  74. // get the outcodes for that vertex
  75. ULONG ulCode = re_avvxViewVertices[bsc.bsc_ivvx0+ivx].vvx_ulOutcode;
  76. // and them to the mask
  77. ulMask &= ulCode;
  78. }}
  79. // if any bit in the mask is still set, it means that all points are out
  80. // wtr to that plane
  81. return ulMask;
  82. }
  83. // find which portals should be rendered as portals or as pretenders
  84. void CRenderer::FindPretenders(void)
  85. {
  86. re_pbscCurrent->bsc_ispo0 = re_aspoScreenPolygons.Count();
  87. // for all polygons in sector
  88. FOREACHINSTATICARRAY(re_pbscCurrent->bsc_abpoPolygons, CBrushPolygon, itpo) {
  89. CBrushPolygon &bpo = *itpo;
  90. // initially not rendered as portal
  91. bpo.bpo_ulFlags&=~BPOF_RENDERASPORTAL;
  92. // if it is portal,
  93. if (bpo.bpo_ulFlags&BPOF_PORTAL) {
  94. // initially rendered as portal
  95. bpo.bpo_ulFlags|=BPOF_RENDERASPORTAL;
  96. // if could be a pretender
  97. if (bpo.bpo_bppProperties.bpp_uwPretenderDistance!=0) {
  98. // get distance at which it is a pretender
  99. FLOAT fPretenderDistance = bpo.bpo_bppProperties.bpp_uwPretenderDistance;
  100. // for each vertex in the polygon
  101. INDEX ctVtx = bpo.bpo_apbvxTriangleVertices.Count();
  102. CBrushSector &bsc = *bpo.bpo_pbscSector;
  103. {for(INDEX i=0; i<ctVtx; i++) {
  104. CBrushVertex *pbvx = bpo.bpo_apbvxTriangleVertices[i];
  105. INDEX ivx = bsc.bsc_abvxVertices.Index(pbvx);
  106. // get distance of the vertex from the view plane
  107. FLOAT fx = re_avvxViewVertices[bsc.bsc_ivvx0+ivx].vvx_vView(1);
  108. FLOAT fy = re_avvxViewVertices[bsc.bsc_ivvx0+ivx].vvx_vView(2);
  109. FLOAT fz = re_avvxViewVertices[bsc.bsc_ivvx0+ivx].vvx_vView(3);
  110. FLOAT fD = fx*fx+fy*fy+fz*fz;
  111. // if nearer than allowed pretender distance
  112. if (fD<fPretenderDistance*fPretenderDistance) {
  113. // this polygon is not a pretender
  114. goto nextpolygon;
  115. }
  116. }}
  117. // if all vertices passed the check, mark as pretender
  118. bpo.bpo_ulFlags&=~BPOF_RENDERASPORTAL;
  119. }
  120. }
  121. nextpolygon:;
  122. }
  123. }
  124. // make screen polygons for nondetail polygons in current sector
  125. void CRenderer::MakeNonDetailScreenPolygons(void)
  126. {
  127. _pfRenderProfile.StartTimer(CRenderProfile::PTI_MAKENONDETAILSCREENPOLYGONS);
  128. re_pbscCurrent->bsc_ispo0 = re_aspoScreenPolygons.Count();
  129. // detail polygons are not skipped if rendering shadows
  130. const ULONG ulDetailMask = re_bRenderingShadows ? 0 : BPOF_DETAILPOLYGON;
  131. // for all polygons in sector
  132. FOREACHINSTATICARRAY(re_pbscCurrent->bsc_abpoPolygons, CBrushPolygon, itpo) {
  133. CBrushPolygon &bpo = *itpo;
  134. // if polygon does not contribute to the visibility determination
  135. if ( (bpo.bpo_ulFlags&ulDetailMask)
  136. &&!(bpo.bpo_ulFlags&BPOF_RENDERASPORTAL)) {
  137. // skip it
  138. continue;
  139. }
  140. // no screen polygon by default
  141. bpo.bpo_pspoScreenPolygon = NULL;
  142. // skip if the polygon is not visible
  143. ASSERT( !IsPolygonCulled(bpo)); // cannot be culled yet!
  144. const ULONG ulVisible = GetPolygonVisibility(bpo);
  145. if( ulVisible==0) continue;
  146. _sfStats.IncrementCounter(CStatForm::SCI_POLYGONS);
  147. _pfRenderProfile.IncrementCounter(CRenderProfile::PCI_NONDETAILPOLYGONS);
  148. // make screen polygon for the polygon
  149. CScreenPolygon &spo = *MakeScreenPolygon(bpo);
  150. // add its edges
  151. MakeInitialPolygonEdges(bpo, spo, ulVisible);
  152. }
  153. // remember number of polygons in sector
  154. re_pbscCurrent->bsc_ctspo = re_aspoScreenPolygons.Count()-re_pbscCurrent->bsc_ispo0;
  155. _pfRenderProfile.StopTimer(CRenderProfile::PTI_MAKENONDETAILSCREENPOLYGONS);
  156. }
  157. // make screen polygons for detail polygons in current sector
  158. void CRenderer::MakeDetailScreenPolygons(void)
  159. {
  160. // if rendering shadows or not rendering detail polygons
  161. if (re_bRenderingShadows
  162. ||!wld_bRenderDetailPolygons) {
  163. // do nothing
  164. return;
  165. }
  166. _pfRenderProfile.StartTimer(CRenderProfile::PTI_MAKEDETAILSCREENPOLYGONS);
  167. // for all polygons in sector
  168. FOREACHINSTATICARRAY(re_pbscCurrent->bsc_abpoPolygons, CBrushPolygon, itpo) {
  169. CBrushPolygon &bpo = *itpo;
  170. // if polygon is not detail
  171. if (!(bpo.bpo_ulFlags&BPOF_DETAILPOLYGON)
  172. || (bpo.bpo_ulFlags&BPOF_RENDERASPORTAL)) {
  173. // skip it
  174. continue;
  175. }
  176. // no screen polygon by default
  177. bpo.bpo_pspoScreenPolygon = NULL;
  178. // skip if the polygon is not visible
  179. if( GetPolygonVisibility(bpo)==0) continue;
  180. // skip if outside the frustum
  181. if( (re_pbscCurrent->bsc_ulFlags&BSCF_NEEDSCLIPPING) && IsPolygonCulled(bpo)) continue;
  182. _sfStats.IncrementCounter(CStatForm::SCI_DETAILPOLYGONS);
  183. _pfRenderProfile.IncrementCounter(CRenderProfile::PCI_DETAILPOLYGONS);
  184. // make screen polygon for the polygon
  185. CScreenPolygon &spo = *MakeScreenPolygon(bpo);
  186. // if it is portal
  187. if (spo.IsPortal()) {
  188. // pass it immediately
  189. PassPortal(spo);
  190. } else {
  191. // add polygon to scene polygons for rendering
  192. AddPolygonToScene(&spo);
  193. }
  194. }
  195. _pfRenderProfile.StopTimer(CRenderProfile::PTI_MAKEDETAILSCREENPOLYGONS);
  196. }
  197. // make initial edges for a polygon
  198. void CRenderer::MakeInitialPolygonEdges(CBrushPolygon &bpo, CScreenPolygon &spo, BOOL ulDirection)
  199. {
  200. // get number of edges
  201. INDEX ctEdges = bpo.bpo_abpePolygonEdges.Count();
  202. spo.spo_ubDirectionFlags = ulDirection&PDF_FLIPEDGESPOST;
  203. BOOL bInvert = (ulDirection&PDF_FLIPEDGESPRE)!=0;
  204. // remember edge vertex start and count
  205. spo.spo_ctEdgeVx = ctEdges*2;
  206. spo.spo_iEdgeVx0 = re_aiEdgeVxClipSrc.Count();
  207. // create edge vertices
  208. INDEX *ai = re_aiEdgeVxClipSrc.Push(ctEdges*2);
  209. // for each edge
  210. for (INDEX iEdge=0; iEdge<ctEdges; iEdge++) {
  211. // set the two vertices
  212. CBrushPolygonEdge &bpe = bpo.bpo_abpePolygonEdges[iEdge];
  213. CWorkingEdge &wed = *bpe.bpe_pbedEdge->bed_pwedWorking;
  214. if (bpe.bpe_bReverse^bInvert) {
  215. ai[iEdge*2+0] = wed.wed_iwvx1+re_iViewVx0;
  216. ai[iEdge*2+1] = wed.wed_iwvx0+re_iViewVx0;
  217. } else {
  218. ai[iEdge*2+0] = wed.wed_iwvx0+re_iViewVx0;
  219. ai[iEdge*2+1] = wed.wed_iwvx1+re_iViewVx0;
  220. }
  221. }
  222. }
  223. // make final edges for all polygons in current sector
  224. void CRenderer::MakeFinalPolygonEdges(void)
  225. {
  226. _pfRenderProfile.StartTimer(CRenderProfile::PTI_MAKEFINALPOLYGONEDGES);
  227. // for each polygon
  228. INDEX ispo0 = re_pbscCurrent->bsc_ispo0;
  229. INDEX ispoTop = re_pbscCurrent->bsc_ispo0+re_pbscCurrent->bsc_ctspo;
  230. for(INDEX ispo = ispo0; ispo<ispoTop; ispo++) {
  231. CScreenPolygon &spo = re_aspoScreenPolygons[ispo];
  232. // if polygon has no edges
  233. if (spo.spo_ctEdgeVx==0) {
  234. // skip it
  235. continue;
  236. }
  237. INDEX iEdgeVx0New = re_aiEdgeVxMain.Count();
  238. INDEX *piVertices = re_aiEdgeVxMain.Push(spo.spo_ctEdgeVx);
  239. // for each vertex
  240. INDEX ivxTop = spo.spo_iEdgeVx0+spo.spo_ctEdgeVx;
  241. for(INDEX ivx=spo.spo_iEdgeVx0; ivx<ivxTop; ivx++) {
  242. // copy to final array
  243. *piVertices++ = re_aiEdgeVxClipSrc[ivx];
  244. }
  245. // remember new edge vertex positions
  246. spo.spo_iEdgeVx0 = iEdgeVx0New;
  247. }
  248. // clear temporary arrays
  249. re_aiEdgeVxClipSrc.PopAll();
  250. re_aiEdgeVxClipDst.PopAll();
  251. _pfRenderProfile.StopTimer(CRenderProfile::PTI_MAKEFINALPOLYGONEDGES);
  252. }
  253. // clip all polygons to one clip plane
  254. void CRenderer::ClipToOnePlane(const FLOATplane3D &plView)
  255. {
  256. // remember clip plane
  257. re_plClip = plView;
  258. // no need for clipping if no vertices are outside
  259. ASSERT( re_pbscCurrent->bsc_ulFlags&BSCF_NEEDSCLIPPING);
  260. if( !MakeOutcodes()) return;
  261. // for each polygon
  262. INDEX ispo0 = re_pbscCurrent->bsc_ispo0;
  263. INDEX ispoTop = re_pbscCurrent->bsc_ispo0+re_pbscCurrent->bsc_ctspo;
  264. for(INDEX ispo = ispo0; ispo<ispoTop; ispo++) {
  265. // clip to the plane
  266. ClipOnePolygon(re_aspoScreenPolygons[ispo]);
  267. }
  268. // swap edge buffers
  269. Swap(re_aiEdgeVxClipSrc.sa_Count , re_aiEdgeVxClipDst.sa_Count );
  270. Swap(re_aiEdgeVxClipSrc.sa_Array , re_aiEdgeVxClipDst.sa_Array );
  271. Swap(re_aiEdgeVxClipSrc.sa_UsedCount, re_aiEdgeVxClipDst.sa_UsedCount);
  272. re_aiEdgeVxClipDst.PopAll();
  273. }
  274. // clip all polygons to all clip planes of a projection
  275. void CRenderer::ClipToAllPlanes(CAnyProjection3D &pr)
  276. {
  277. _pfRenderProfile.StartTimer(CRenderProfile::PTI_CLIPTOALLPLANES);
  278. // clip to up/down/left/right clip planes
  279. FLOATplane3D pl;
  280. pl = pr->pr_plClipU; pl.Offset(-0.001f); ClipToOnePlane(pl);
  281. pl = pr->pr_plClipD; pl.Offset(-0.001f); ClipToOnePlane(pl);
  282. pl = pr->pr_plClipL; pl.Offset(-0.001f); ClipToOnePlane(pl);
  283. pl = pr->pr_plClipR; pl.Offset(-0.001f); ClipToOnePlane(pl);
  284. // clip to near clip plane
  285. ClipToOnePlane(FLOATplane3D(FLOAT3D(0,0,-1), pr->pr_NearClipDistance));
  286. // clip to far clip plane if existing
  287. if (pr->pr_FarClipDistance>0) {
  288. ClipToOnePlane(FLOATplane3D(FLOAT3D(0,0,1), -pr->pr_FarClipDistance));
  289. }
  290. // if projection is mirrored or warped
  291. if (pr->pr_bMirror||pr->pr_bWarp) {
  292. // clip to mirror plane
  293. ClipToOnePlane(pr->pr_plMirrorView);
  294. }
  295. _pfRenderProfile.StopTimer(CRenderProfile::PTI_CLIPTOALLPLANES);
  296. }
  297. // make outcodes for current clip plane for all active vertices
  298. __forceinline BOOL CRenderer::MakeOutcodes(void)
  299. {
  300. SLONG slMask = 0;
  301. // for each active view vertex
  302. INDEX iVxTop = re_avvxViewVertices.Count();
  303. for(INDEX ivx = re_iViewVx0; ivx<iVxTop; ivx++) {
  304. CViewVertex &vvx = re_avvxViewVertices[ivx];
  305. // calculate the distance
  306. vvx.vvx_fD = re_plClip.PointDistance(vvx.vvx_vView);
  307. // calculate the outcode
  308. const ULONG ulOutCode = (*(SLONG*)&vvx.vvx_fD) & 0x80000000;
  309. // add to the outcode of the vertex
  310. vvx.vvx_ulOutcode = (vvx.vvx_ulOutcode>>1)|ulOutCode;
  311. // add to mask
  312. slMask|=ulOutCode;
  313. }
  314. // if any was negative, return true -- needs clipping
  315. return slMask;
  316. }
  317. // clip one polygon to current clip plane
  318. void CRenderer::ClipOnePolygon(CScreenPolygon &spo)
  319. {
  320. //
  321. // NOTE: There is one ugly problem with this loop.
  322. // I don't know any better way to fix it, so I have commented it.
  323. // If the vertex references (vvx0 and vvx1) are taken _before_ pushing the vvxNew,
  324. // it can cause access violation, because pushing can move array in memory.
  325. // Therefore, it is important to take the references _after_ the Push() call.
  326. INDEX iEdgeVx0New = re_aiEdgeVxClipDst.Count();
  327. // for each edge
  328. INDEX ivxTop = spo.spo_iEdgeVx0+spo.spo_ctEdgeVx;
  329. for(INDEX ivx=spo.spo_iEdgeVx0; ivx<ivxTop; ivx+=2) {
  330. INDEX ivx0 = re_aiEdgeVxClipSrc[ivx+0];
  331. INDEX ivx1 = re_aiEdgeVxClipSrc[ivx+1];
  332. // get vertices
  333. FLOAT fD0 = re_avvxViewVertices[ivx0].vvx_fD;
  334. FLOAT fD1 = re_avvxViewVertices[ivx1].vvx_fD;
  335. if (fD0<=0) {
  336. // if both are back
  337. if (fD1<=0) {
  338. // no screen edge remains
  339. continue;
  340. // if first is back, second front
  341. } else {
  342. // make new vertex
  343. INDEX ivxNew = re_avvxViewVertices.Count();
  344. CViewVertex &vvxNew = re_avvxViewVertices.Push();
  345. CViewVertex &vvx0 = re_avvxViewVertices[ivx0]; // must do this after push, see note above!
  346. CViewVertex &vvx1 = re_avvxViewVertices[ivx1]; // must do this after push, see note above!
  347. // clip first
  348. FLOAT fDivisor = 1.0f/(fD0-fD1);
  349. FLOAT fFactor = fD0*fDivisor;
  350. vvxNew.vvx_vView(1) = vvx0.vvx_vView(1)-(vvx0.vvx_vView(1)-vvx1.vvx_vView(1))*fFactor;
  351. vvxNew.vvx_vView(2) = vvx0.vvx_vView(2)-(vvx0.vvx_vView(2)-vvx1.vvx_vView(2))*fFactor;
  352. vvxNew.vvx_vView(3) = vvx0.vvx_vView(3)-(vvx0.vvx_vView(3)-vvx1.vvx_vView(3))*fFactor;
  353. // remember new edge
  354. re_aiEdgeVxClipDst.Push() = ivxNew;
  355. re_aiEdgeVxClipDst.Push() = ivx1;
  356. // add new vertex to clip buffer
  357. re_aiClipBuffer.Push() = ivxNew;
  358. }
  359. } else {
  360. // if first is front, second back
  361. if ((SLONG&)fD1<=0) {
  362. // make new vertex
  363. INDEX ivxNew = re_avvxViewVertices.Count();
  364. CViewVertex &vvxNew = re_avvxViewVertices.Push();
  365. CViewVertex &vvx0 = re_avvxViewVertices[ivx0]; // must do this after push, see note above!
  366. CViewVertex &vvx1 = re_avvxViewVertices[ivx1]; // must do this after push, see note above!
  367. // clip second
  368. FLOAT fDivisor = 1.0f/(fD0-fD1);
  369. FLOAT fFactor = fD1*fDivisor;
  370. vvxNew.vvx_vView(1) = vvx1.vvx_vView(1)-(vvx0.vvx_vView(1)-vvx1.vvx_vView(1))*fFactor;
  371. vvxNew.vvx_vView(2) = vvx1.vvx_vView(2)-(vvx0.vvx_vView(2)-vvx1.vvx_vView(2))*fFactor;
  372. vvxNew.vvx_vView(3) = vvx1.vvx_vView(3)-(vvx0.vvx_vView(3)-vvx1.vvx_vView(3))*fFactor;
  373. // remember new edge
  374. re_aiEdgeVxClipDst.Push() = ivx0;
  375. re_aiEdgeVxClipDst.Push() = ivxNew;
  376. // add new vertex to clip buffer
  377. re_aiClipBuffer.Push() = ivxNew;
  378. // if both are front
  379. } else {
  380. // just copy the edge
  381. re_aiEdgeVxClipDst.Push() = ivx0;
  382. re_aiEdgeVxClipDst.Push() = ivx1;
  383. }
  384. }
  385. }
  386. // if there is anything in clip buffer
  387. if (re_aiClipBuffer.Count()>0) {
  388. // generate clip edges
  389. GenerateClipEdges(spo);
  390. }
  391. // remember new edge vertex positions
  392. spo.spo_ctEdgeVx = re_aiEdgeVxClipDst.Count()-iEdgeVx0New;
  393. spo.spo_iEdgeVx0 = iEdgeVx0New;
  394. }
  395. /*
  396. * Compare two vertices for quick-sort.
  397. */
  398. static UBYTE *_aVertices=NULL;
  399. static int qsort_CompareVertices_plus( const void *ppvVertex0, const void *ppvVertex1)
  400. {
  401. INDEX ivx0 = *(const INDEX*)ppvVertex0;
  402. INDEX ivx1 = *(const INDEX*)ppvVertex1;
  403. FLOAT f0 = *(FLOAT*)(_aVertices+ivx0*sizeof(CViewVertex));
  404. FLOAT f1 = *(FLOAT*)(_aVertices+ivx1*sizeof(CViewVertex));
  405. if (f0<f1) return -1;
  406. else if (f0>f1) return +1;
  407. else return 0;
  408. }
  409. static int qsort_CompareVertices_minus( const void *ppvVertex0, const void *ppvVertex1)
  410. {
  411. INDEX ivx0 = *(const INDEX*)ppvVertex0;
  412. INDEX ivx1 = *(const INDEX*)ppvVertex1;
  413. FLOAT f0 = *(FLOAT*)(_aVertices+ivx0*sizeof(CViewVertex));
  414. FLOAT f1 = *(FLOAT*)(_aVertices+ivx1*sizeof(CViewVertex));
  415. if (f0<f1) return +1;
  416. else if (f0>f1) return -1;
  417. else return 0;
  418. }
  419. // generate clip edges for one polygon
  420. void CRenderer::GenerateClipEdges(CScreenPolygon &spo)
  421. {
  422. ASSERT(re_aiClipBuffer.Count()>0);
  423. FLOATplane3D &plPolygonPlane = spo.spo_pbpoBrushPolygon->bpo_pbplPlane->bpl_pwplWorking->wpl_plView;
  424. // calculate the clip buffer direction in 3d as:
  425. // clip_normal_vector x polygon_normal_vector
  426. FLOAT3D vClipDir = ((FLOAT3D &)re_plClip)*((FLOAT3D &)plPolygonPlane);
  427. // get max axis
  428. INDEX iMaxAxis = 1;
  429. FLOAT fMaxAbs = Abs(vClipDir(1));
  430. if (Abs(vClipDir(2))>fMaxAbs) {
  431. iMaxAxis = 2;
  432. fMaxAbs = Abs(vClipDir(2));
  433. }
  434. if (Abs(vClipDir(3))>fMaxAbs) {
  435. iMaxAxis = 3;
  436. fMaxAbs = Abs(vClipDir(3));
  437. }
  438. _aVertices = (UBYTE*) &re_avvxViewVertices[0].vvx_vView(iMaxAxis);
  439. INDEX *aIndices = &re_aiClipBuffer[0];
  440. INDEX ctIndices = re_aiClipBuffer.Count();
  441. // there must be even number of vertices in the buffer
  442. ASSERT(ctIndices%2 == 0);
  443. // if the sign of axis is negative
  444. if (vClipDir(iMaxAxis)<0) {
  445. // sort them inversely
  446. qsort(aIndices, ctIndices, sizeof(INDEX), qsort_CompareVertices_minus);
  447. // if it is negative
  448. } else {
  449. // sort them normally
  450. qsort(aIndices, ctIndices, sizeof(INDEX), qsort_CompareVertices_plus);
  451. }
  452. // for each two vertices
  453. for(INDEX iClippedVertex=0; iClippedVertex<ctIndices; iClippedVertex+=2) {
  454. // add the edge
  455. re_aiEdgeVxClipDst.Push() = aIndices[iClippedVertex+0];
  456. re_aiEdgeVxClipDst.Push() = aIndices[iClippedVertex+1];
  457. }
  458. // clear the clip buffer
  459. re_aiClipBuffer.PopAll();
  460. }