2016-03-12 01:20:51 +01:00
|
|
|
/* 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. */
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
#define OFFSET_DN (0.0625f)
|
|
|
|
|
|
|
|
#define CStaticArray_sa_Count 0
|
|
|
|
#define CStaticArray_sa_Array 4
|
|
|
|
|
|
|
|
#define CDynamicArray_da_Pointers 12
|
|
|
|
#define CDynamicArray_da_Count 16
|
|
|
|
#define CDynamicStackArray_da_ctUsed 24
|
|
|
|
|
|
|
|
#define sizeof_CWorkingVertex 32
|
|
|
|
#define sizeof_CWorkingEdge 32
|
|
|
|
|
|
|
|
static FLOATaabbox2D *pfbbClipBox;
|
|
|
|
static PIX pixCurrentScanJ;
|
|
|
|
static PIX pixBottomScanJ;
|
|
|
|
static const FLOAT f65536=65536.0f;
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// Functions for support of transformed caches
|
|
|
|
|
|
|
|
static FLOAT fCenterI, fCenterJ, fRatioI, fRatioJ;
|
|
|
|
static FLOAT fNearClipDistance, fooNearClipDistance;
|
|
|
|
static FLOAT fFarClipDistance, fooFarClipDistance;
|
|
|
|
static FLOAT fooNearRatioI, fooNearRatioJ;
|
|
|
|
static FLOAT fooFarRatioI, fooFarRatioJ;
|
|
|
|
|
|
|
|
|
|
|
|
static CRenderer *_preThis;
|
|
|
|
|
|
|
|
// check if a sector is inside view frustum
|
|
|
|
__forceinline INDEX CRenderer::IsSectorVisible(CBrush3D &br, CBrushSector &bsc)
|
|
|
|
{
|
|
|
|
// project the bounding sphere of sector bbox to view
|
|
|
|
FLOAT3D vCenter;
|
|
|
|
CProjection3D *ppr;
|
|
|
|
if (re_bBackgroundEnabled && (br.br_penEntity->en_ulFlags & ENF_BACKGROUND)) {
|
|
|
|
ppr = re_prBackgroundProjection;
|
|
|
|
} else {
|
|
|
|
ppr = re_prProjection;
|
|
|
|
}
|
|
|
|
ppr->PreClip(bsc.bsc_boxBoundingBox.Center(), vCenter);
|
|
|
|
FLOAT fR = bsc.bsc_boxBoundingBox.Size().Length()/2.0f;
|
|
|
|
|
|
|
|
// if the sector bounding sphere is inside view frustum
|
|
|
|
INDEX iFrustumTest = ppr->TestSphereToFrustum( vCenter, fR);
|
|
|
|
if( iFrustumTest==0) { // if test was indeterminate
|
|
|
|
// create oriented box and test it to frustum
|
|
|
|
FLOATobbox3D boxEntity( bsc.bsc_boxBoundingBox, ppr->pr_TranslationVector, ppr->pr_ViewerRotationMatrix);
|
|
|
|
iFrustumTest = ppr->TestBoxToFrustum(boxEntity);
|
|
|
|
}
|
|
|
|
// done
|
|
|
|
return iFrustumTest;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Transform vertices in one sector before clipping. */
|
|
|
|
void CRenderer::PreClipVertices(void)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_TRANSFORMVERTICES);
|
|
|
|
|
|
|
|
const FLOATmatrix3D &m = re_pbrCurrent->br_prProjection->pr_RotationMatrix;
|
|
|
|
const FLOAT3D &v = re_pbrCurrent->br_prProjection->pr_TranslationVector;
|
|
|
|
re_pbscCurrent->bsc_ivvx0 = re_iViewVx0 = re_avvxViewVertices.Count();
|
|
|
|
INDEX ctvx = re_pbscCurrent->bsc_awvxVertices.Count();
|
|
|
|
CViewVertex *avvx = re_avvxViewVertices.Push(ctvx);
|
|
|
|
// for each vertex in sector
|
|
|
|
for( INDEX ivx=0; ivx<ctvx; ivx++)
|
|
|
|
{
|
|
|
|
const CWorkingVertex &wvx = re_pbscCurrent->bsc_awvxVertices[ivx];
|
|
|
|
// transform it to view space
|
|
|
|
const FLOAT fx = wvx.wvx_vRelative(1);
|
|
|
|
const FLOAT fy = wvx.wvx_vRelative(2);
|
|
|
|
const FLOAT fz = wvx.wvx_vRelative(3);
|
|
|
|
avvx[ivx].vvx_vView(1) = fx*m(1, 1)+fy*m(1, 2)+fz*m(1, 3)+v(1);
|
|
|
|
avvx[ivx].vvx_vView(2) = fx*m(2, 1)+fy*m(2, 2)+fz*m(2, 3)+v(2);
|
|
|
|
avvx[ivx].vvx_vView(3) = fx*m(3, 1)+fy*m(3, 2)+fz*m(3, 3)+v(3);
|
|
|
|
// clear the outcode initially
|
|
|
|
avvx[ivx].vvx_ulOutcode = 0;
|
|
|
|
}
|
|
|
|
_pfRenderProfile.IncrementCounter(CRenderProfile::PCI_TRANSFORMEDVERTICES, ctvx);
|
|
|
|
_pfRenderProfile.IncrementTimerAveragingCounter(CRenderProfile::PTI_TRANSFORMVERTICES, ctvx);
|
|
|
|
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_TRANSFORMVERTICES);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transform planes in one sector before clipping. */
|
|
|
|
void CRenderer::PreClipPlanes(void)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_TRANSFORMPLANES);
|
|
|
|
|
|
|
|
INDEX ctpl =re_pbscCurrent->bsc_awplPlanes.Count();
|
|
|
|
CProjection3D *ppr = &*re_pbrCurrent->br_prProjection;
|
|
|
|
const FLOATmatrix3D &m = ppr->pr_RotationMatrix;
|
|
|
|
const FLOAT3D &v = ppr->pr_TranslationVector;
|
|
|
|
|
|
|
|
// if the projection is perspective
|
|
|
|
if (re_pbrCurrent->br_prProjection.IsPerspective()) {
|
|
|
|
|
|
|
|
// for each plane in sector
|
|
|
|
for(INDEX ipl=0; ipl<ctpl; ipl++){
|
|
|
|
// transform it to view space
|
|
|
|
CWorkingPlane &wpl = re_pbscCurrent->bsc_awplPlanes[ipl];
|
|
|
|
const FLOAT fx = wpl.wpl_plRelative(1);
|
|
|
|
const FLOAT fy = wpl.wpl_plRelative(2);
|
|
|
|
const FLOAT fz = wpl.wpl_plRelative(3);
|
2016-04-24 01:01:37 +02:00
|
|
|
//const FLOAT fd = wpl.wpl_plRelative.Distance();
|
2016-03-11 14:57:17 +01:00
|
|
|
wpl.wpl_plView(1) = fx*m(1, 1)+fy*m(1, 2)+fz*m(1, 3);
|
|
|
|
wpl.wpl_plView(2) = fx*m(2, 1)+fy*m(2, 2)+fz*m(2, 3);
|
|
|
|
wpl.wpl_plView(3) = fx*m(3, 1)+fy*m(3, 2)+fz*m(3, 3);
|
|
|
|
wpl.wpl_plView.Distance() =
|
|
|
|
wpl.wpl_plView(1)*v(1)+
|
|
|
|
wpl.wpl_plView(2)*v(2)+
|
|
|
|
wpl.wpl_plView(3)*v(3)+
|
|
|
|
wpl.wpl_plRelative.Distance();
|
|
|
|
// test if the plane is visible
|
|
|
|
wpl.wpl_bVisible = (wpl.wpl_plView.Distance() < -0.01f);
|
|
|
|
}
|
|
|
|
// if the projection is not perspective
|
|
|
|
} else {
|
|
|
|
// !!!! speed this up for other projections too ?
|
|
|
|
// for each plane in sector
|
|
|
|
for(INDEX ipl=0; ipl<ctpl; ipl++){
|
|
|
|
// transform it to view space
|
|
|
|
CWorkingPlane &wpl = re_pbscCurrent->bsc_awplPlanes[ipl];
|
|
|
|
ppr->Project(wpl.wpl_plRelative, wpl.wpl_plView);
|
|
|
|
// test if the plane is visible
|
|
|
|
wpl.wpl_bVisible = ppr->IsViewerPlaneVisible(wpl.wpl_plView);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// for each plane
|
|
|
|
{for(INDEX ipl=0; ipl<ctpl; ipl++){
|
|
|
|
CWorkingPlane &wpl = re_pbscCurrent->bsc_awplPlanes[ipl];
|
|
|
|
// make gradients without fog
|
|
|
|
ppr->MakeOoKGradient(wpl.wpl_plView, wpl.wpl_pgOoK);
|
|
|
|
// transform it to view space
|
|
|
|
const FLOAT fxO = wpl.wpl_mvRelative.mv_vO(1);
|
|
|
|
const FLOAT fyO = wpl.wpl_mvRelative.mv_vO(2);
|
|
|
|
const FLOAT fzO = wpl.wpl_mvRelative.mv_vO(3);
|
|
|
|
const FLOAT fxU = wpl.wpl_mvRelative.mv_vU(1);
|
|
|
|
const FLOAT fyU = wpl.wpl_mvRelative.mv_vU(2);
|
|
|
|
const FLOAT fzU = wpl.wpl_mvRelative.mv_vU(3);
|
|
|
|
const FLOAT fxV = wpl.wpl_mvRelative.mv_vV(1);
|
|
|
|
const FLOAT fyV = wpl.wpl_mvRelative.mv_vV(2);
|
|
|
|
const FLOAT fzV = wpl.wpl_mvRelative.mv_vV(3);
|
|
|
|
wpl.wpl_mvView.mv_vO(1) = fxO*m(1, 1)+fyO*m(1, 2)+fzO*m(1, 3)+v(1);
|
|
|
|
wpl.wpl_mvView.mv_vO(2) = fxO*m(2, 1)+fyO*m(2, 2)+fzO*m(2, 3)+v(2);
|
|
|
|
wpl.wpl_mvView.mv_vO(3) = fxO*m(3, 1)+fyO*m(3, 2)+fzO*m(3, 3)+v(3);
|
|
|
|
wpl.wpl_mvView.mv_vU(1) = fxU*m(1, 1)+fyU*m(1, 2)+fzU*m(1, 3);
|
|
|
|
wpl.wpl_mvView.mv_vU(2) = fxU*m(2, 1)+fyU*m(2, 2)+fzU*m(2, 3);
|
|
|
|
wpl.wpl_mvView.mv_vU(3) = fxU*m(3, 1)+fyU*m(3, 2)+fzU*m(3, 3);
|
|
|
|
wpl.wpl_mvView.mv_vV(1) = fxV*m(1, 1)+fyV*m(1, 2)+fzV*m(1, 3);
|
|
|
|
wpl.wpl_mvView.mv_vV(2) = fxV*m(2, 1)+fyV*m(2, 2)+fzV*m(2, 3);
|
|
|
|
wpl.wpl_mvView.mv_vV(3) = fxV*m(3, 1)+fyV*m(3, 2)+fzV*m(3, 3);
|
|
|
|
}}
|
|
|
|
|
|
|
|
_pfRenderProfile.IncrementCounter(CRenderProfile::PCI_TRANSFORMEDPLANES, ctpl);
|
|
|
|
_pfRenderProfile.IncrementTimerAveragingCounter(CRenderProfile::PTI_TRANSFORMPLANES, ctpl);
|
|
|
|
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_TRANSFORMPLANES);
|
|
|
|
}
|
|
|
|
|
|
|
|
// project vertices in current sector after clipping
|
|
|
|
void CRenderer::PostClipVertices(void)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_PROJECTVERTICES);
|
2016-04-24 01:01:37 +02:00
|
|
|
//const FLOATmatrix3D &m = re_pbrCurrent->br_prProjection->pr_RotationMatrix;
|
|
|
|
//const FLOAT3D &v = re_pbrCurrent->br_prProjection->pr_TranslationVector;
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
// if the projection is perspective
|
|
|
|
if (re_pbrCurrent->br_prProjection.IsPerspective()) {
|
|
|
|
CPerspectiveProjection3D &prPerspective = (CPerspectiveProjection3D &)*re_pbrCurrent->br_prProjection;
|
|
|
|
|
|
|
|
fCenterI = prPerspective.pr_ScreenCenter(1);
|
|
|
|
fCenterJ = prPerspective.pr_ScreenCenter(2);
|
|
|
|
fRatioI = prPerspective.ppr_PerspectiveRatios(1);
|
|
|
|
fRatioJ = prPerspective.ppr_PerspectiveRatios(2);
|
|
|
|
|
|
|
|
// for each active view vertex
|
|
|
|
INDEX iVxTop = re_avvxViewVertices.Count();
|
|
|
|
for(INDEX ivx = re_iViewVx0; ivx<iVxTop; ivx++) {
|
|
|
|
CViewVertex &vvx = re_avvxViewVertices[ivx];
|
|
|
|
// transform it to view space
|
|
|
|
FLOAT fooz = 1.0f/vvx.vvx_vView(3);
|
|
|
|
vvx.vvx_fI = fCenterI+vvx.vvx_vView(1)*fRatioI*fooz;
|
|
|
|
vvx.vvx_fJ = fCenterJ-vvx.vvx_vView(2)*fRatioJ*fooz;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the projection is not perspective
|
|
|
|
} else {
|
|
|
|
// !!!! speed this up for other projections too ?
|
|
|
|
// for each active view vertex
|
|
|
|
INDEX iVxTop = re_avvxViewVertices.Count();
|
|
|
|
for(INDEX ivx = re_iViewVx0; ivx<iVxTop; ivx++) {
|
|
|
|
CViewVertex &vvx = re_avvxViewVertices[ivx];
|
|
|
|
// transform it to view space
|
|
|
|
FLOAT3D v;
|
|
|
|
re_pbrCurrent->br_prProjection->PostClip(vvx.vvx_vView, v);
|
|
|
|
vvx.vvx_fI = v(1);
|
|
|
|
vvx.vvx_fJ = v(2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_PROJECTVERTICES);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// setup fog/haze for a sector
|
|
|
|
void CRenderer::SetupFogAndHaze(void)
|
|
|
|
{
|
|
|
|
CBrush3D &br = *re_pbrCurrent;
|
|
|
|
CBrushSector &bsc = *re_pbscCurrent;
|
|
|
|
if( _bMultiPlayer) gfx_bRenderFog = 1; // must render fog in multiplayer mode!
|
|
|
|
|
|
|
|
// if the sector is not part of a zoning brush
|
|
|
|
if (!(br.br_penEntity->en_ulFlags&ENF_ZONING)) {
|
|
|
|
// do nothing
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if fog is enabled
|
|
|
|
re_bCurrentSectorHasFog = FALSE;
|
|
|
|
if( _wrpWorldRenderPrefs.wrp_bFogOn && gfx_bRenderFog)
|
|
|
|
{ // if the sector has fog
|
|
|
|
CFogParameters fp;
|
|
|
|
if( bsc.bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->GetFog(bsc.GetFogType(), fp)) {
|
|
|
|
// activate fog if not already active
|
|
|
|
if( !_fog_bActive) {
|
|
|
|
StartFog( fp, br.br_prProjection->pr_vViewerPosition,
|
|
|
|
br.br_prProjection->pr_ViewerRotationMatrix);
|
|
|
|
}
|
|
|
|
// mark that current sector has fog
|
|
|
|
re_bCurrentSectorHasFog = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if haze is enabled
|
|
|
|
re_bCurrentSectorHasHaze = FALSE;
|
|
|
|
if( _wrpWorldRenderPrefs.wrp_bHazeOn && gfx_bRenderFog)
|
|
|
|
{ // if the sector has haze
|
|
|
|
CHazeParameters hp;
|
|
|
|
FLOAT3D vViewDir;
|
|
|
|
FLOATmatrix3D &mAbsToView = bsc.bsc_pbmBrushMip->bm_pbrBrush->br_prProjection->pr_ViewerRotationMatrix;
|
|
|
|
vViewDir(1) = -mAbsToView(3, 1);
|
|
|
|
vViewDir(2) = -mAbsToView(3, 2);
|
|
|
|
vViewDir(3) = -mAbsToView(3, 3);
|
|
|
|
if( bsc.bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->GetHaze(bsc.GetHazeType(), hp, vViewDir)) {
|
|
|
|
// if viewer is not in haze
|
|
|
|
if( !re_bViewerInHaze) {
|
|
|
|
// if viewer is in this sector
|
2017-05-28 15:17:22 +02:00
|
|
|
if( bsc.bsc_bspBSPTree.TestSphere(re_vdViewSphere, 0.01f)>=0) {
|
2016-03-11 14:57:17 +01:00
|
|
|
// mark that viewer is in haze
|
|
|
|
re_bViewerInHaze = TRUE;
|
|
|
|
}
|
|
|
|
// if there is a viewer
|
|
|
|
else if( re_penViewer!=NULL) {
|
|
|
|
// check rest of sectors the viewer is in
|
|
|
|
{FOREACHSRCOFDST( re_penViewer->en_rdSectors, CBrushSector, bsc_rsEntities, pbsc)
|
|
|
|
CHazeParameters hpDummy;
|
|
|
|
if( pbsc->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->GetHaze( pbsc->GetHazeType(), hpDummy, vViewDir)) {
|
|
|
|
// if viewer is in this sector
|
|
|
|
if( pbsc->bsc_bspBSPTree.TestSphere(re_vdViewSphere, 0.01)>=0) {
|
|
|
|
// mark that viewer is in haze
|
|
|
|
re_bViewerInHaze = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ENDFOR}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if viewer is in haze, or haze can be viewed from outside
|
|
|
|
if( re_bViewerInHaze || (hp.hp_ulFlags&HPF_VISIBLEFROMOUTSIDE)) {
|
|
|
|
// activate haze if not already active
|
|
|
|
if( !_haze_bActive) {
|
|
|
|
StartHaze( hp, br.br_prProjection->pr_vViewerPosition,
|
|
|
|
br.br_prProjection->pr_ViewerRotationMatrix);
|
|
|
|
}
|
|
|
|
// mark that current sector has haze
|
|
|
|
re_bCurrentSectorHasHaze = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add an edge to add list of its top scanline.
|
|
|
|
*/
|
|
|
|
void CRenderer::AddEdgeToAddAndRemoveLists(CScreenEdge &sed)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.IncrementTimerAveragingCounter(CRenderProfile::PTI_ADDEDGETOADDLIST, 1);
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_ADDEDGETOADDLIST);
|
|
|
|
|
|
|
|
// add it to the remove list at its bottom scan line
|
|
|
|
ASSERT(sed.sed_pixBottomJ-1-re_pixTopScanLineJ < re_ctScanLines);
|
|
|
|
INDEX iBottomLine = sed.sed_pixBottomJ-1-re_pixTopScanLineJ;
|
|
|
|
sed.sed_psedNextRemove = re_apsedRemoveFirst[iBottomLine];
|
|
|
|
re_apsedRemoveFirst[iBottomLine] = &sed;
|
|
|
|
|
|
|
|
// search all edges in the add list of top scan line of this edge
|
|
|
|
INDEX iTopLine = sed.sed_pixTopJ-re_pixTopScanLineJ;
|
|
|
|
CListNode *plnInList = re_alhAddLists[iTopLine].lh_Head;
|
|
|
|
re_actAddCounts[iTopLine]++;
|
|
|
|
SLONG slIThis = sed.sed_xI.slHolder;
|
|
|
|
|
|
|
|
ASSERT(sed.sed_xI > FIX16_16(re_fbbClipBox.Min()(1)-SENTINELEDGE_EPSILON));
|
|
|
|
ASSERT(sed.sed_xI < FIX16_16(re_fbbClipBox.Max()(1)+SENTINELEDGE_EPSILON));
|
|
|
|
|
|
|
|
SLONG slIInList;
|
|
|
|
while(plnInList->ln_Succ!=NULL) {
|
|
|
|
slIInList =
|
2016-03-29 03:03:54 +02:00
|
|
|
((CAddEdge*)((UBYTE*)plnInList-_offsetof(CAddEdge, ade_lnInAdd))) -> ade_xI.slHolder;
|
2016-03-11 14:57:17 +01:00
|
|
|
// if the edge in list is right of the one to add
|
|
|
|
if (slIInList>slIThis) {
|
|
|
|
// stop searching
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
plnInList = plnInList->ln_Succ;
|
|
|
|
}
|
|
|
|
// add it to add list
|
|
|
|
CAddEdge &ade = re_aadeAddEdges.Push();
|
|
|
|
ade = CAddEdge(&sed);
|
|
|
|
CListNode *plnThis = &ade.ade_lnInAdd;
|
|
|
|
CListNode *plnAfter = plnInList;
|
|
|
|
CListNode *plnBefore = plnInList->ln_Pred;
|
|
|
|
plnThis->ln_Succ = plnAfter;
|
|
|
|
plnThis->ln_Pred = plnBefore;
|
|
|
|
plnBefore->ln_Succ = plnThis;
|
|
|
|
plnAfter->ln_Pred = plnThis;
|
|
|
|
// mark that it is added
|
|
|
|
sed.sed_bAdded = TRUE;
|
|
|
|
ASSERT(re_actAddCounts[iTopLine]==re_alhAddLists[iTopLine].Count());
|
|
|
|
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDEDGETOADDLIST);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a screen edge from two vertices doing 2D clipping.
|
|
|
|
*/
|
|
|
|
inline void CRenderer::MakeScreenEdge(
|
|
|
|
CScreenEdge &sed, FLOAT fI0, FLOAT fJ0, FLOAT fI1, FLOAT fJ1)
|
|
|
|
{
|
|
|
|
// mark both vertices as not clipped
|
|
|
|
_pfRenderProfile.IncrementTimerAveragingCounter(CRenderProfile::PTI_MAKESCREENEDGE, 1);
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_MAKESCREENEDGE);
|
|
|
|
enum LineDirectionType ldtDirection;
|
|
|
|
|
|
|
|
// bias edges towards each others - fix against generating extra trapezoids
|
|
|
|
if (fJ0<fJ1) {
|
|
|
|
fI0-=re_fEdgeOffsetI;
|
|
|
|
fI1-=re_fEdgeOffsetI;
|
|
|
|
} else {
|
|
|
|
fI0+=re_fEdgeOffsetI;
|
|
|
|
fI1+=re_fEdgeOffsetI;
|
|
|
|
}
|
|
|
|
|
|
|
|
// clamp vertices left and right
|
|
|
|
if (fI0<re_fbbClipBox.Min()(1)) {
|
|
|
|
fI0 = re_fbbClipBox.Min()(1);
|
|
|
|
}
|
|
|
|
if (fI1<re_fbbClipBox.Min()(1)) {
|
|
|
|
fI1 = re_fbbClipBox.Min()(1);
|
|
|
|
}
|
|
|
|
if (fI0>re_fbbClipBox.Max()(1)) {
|
|
|
|
fI0 = re_fbbClipBox.Max()(1);
|
|
|
|
}
|
|
|
|
if (fI1>re_fbbClipBox.Max()(1)) {
|
|
|
|
fI1 = re_fbbClipBox.Max()(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
FLOAT fDJ=fJ1-fJ0;
|
|
|
|
FLOAT fDI=fI1-fI0;
|
|
|
|
|
|
|
|
FLOAT fDIoDJ = fDI/fDJ;
|
|
|
|
|
|
|
|
// mark edge as descending
|
|
|
|
ldtDirection = LDT_DESCENDING;
|
|
|
|
|
|
|
|
// if vertex 0 is below vertex 1
|
|
|
|
if ((SLONG&)fDJ<0 ) { // fJ0>fJ1
|
|
|
|
// mark edge as ascending
|
|
|
|
ldtDirection = LDT_ASCENDING;
|
|
|
|
Swap(fI0, fI1);
|
|
|
|
Swap(fJ0, fJ1);
|
|
|
|
fDI = -fDI;
|
|
|
|
fDJ = -fDJ;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(fI0>=re_fbbClipBox.Min()(1));
|
|
|
|
ASSERT(fI1>=re_fbbClipBox.Min()(1));
|
|
|
|
ASSERT(fI0>=-0.5f);
|
|
|
|
ASSERT(fI1>=-0.5f);
|
|
|
|
ASSERT(fI0<=re_fbbClipBox.Max()(1));
|
|
|
|
ASSERT(fI1<=re_fbbClipBox.Max()(1));
|
|
|
|
|
|
|
|
// set line direction
|
|
|
|
sed.sed_ldtDirection = ldtDirection;
|
|
|
|
|
|
|
|
// edge has polygon initially
|
|
|
|
sed.sed_pspo = NULL;
|
|
|
|
// edge is not linked to add and remove lists initially
|
|
|
|
sed.sed_bAdded = FALSE;
|
|
|
|
sed.sed_psedNextRemove = NULL;
|
|
|
|
|
|
|
|
// if bottom vertex is above screen top or top vertex is below screen bottom
|
|
|
|
FLOAT fDJ1Up = fJ1-re_fbbClipBox.Min()(2);
|
|
|
|
FLOAT fDJ0Dn = re_fbbClipBox.Max()(2)-fJ0;
|
|
|
|
if ((SLONG&)(fDJ1Up)<0 || (SLONG&)(fDJ0Dn)<0) {
|
|
|
|
// generate dummy horizontal screen edge
|
|
|
|
sed.sed_pixTopJ = (PIX) 0;
|
|
|
|
sed.sed_pixBottomJ = (PIX) 0;
|
|
|
|
sed.sed_ldtDirection = LDT_HORIZONTAL;
|
|
|
|
|
|
|
|
// otherwise
|
|
|
|
} else {
|
|
|
|
// calculate edge slope and convert it to fixed integer
|
|
|
|
sed.sed_xIStep = (FIX16_16)fDIoDJ;
|
|
|
|
// convert J coordinates to integers
|
|
|
|
sed.sed_pixTopJ = PIXCoord(fJ0);
|
|
|
|
sed.sed_pixBottomJ = PIXCoord(fJ1);
|
|
|
|
|
|
|
|
// if the edge bottom is below screen bottom
|
|
|
|
if (sed.sed_pixTopJ<re_pixCurrentScanJ) {
|
|
|
|
// set it to bottom
|
|
|
|
sed.sed_pixTopJ = re_pixCurrentScanJ;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the edge bottom is below screen bottom
|
|
|
|
if (sed.sed_pixBottomJ>re_pixBottomScanLineJ) {
|
|
|
|
// set it to bottom
|
|
|
|
sed.sed_pixBottomJ = re_pixBottomScanLineJ;
|
|
|
|
}
|
|
|
|
|
|
|
|
// make fixed integer representation of top I coordinate with correction
|
|
|
|
sed.sed_xI = (FIX16_16) (fI0 + ((FLOAT)sed.sed_pixTopJ-fJ0) * fDIoDJ );
|
|
|
|
}
|
|
|
|
|
2016-04-23 20:25:08 +02:00
|
|
|
ASSERT( (sed.sed_xI > FIX16_16(-1.0f)
|
|
|
|
&& sed.sed_xI < FIX16_16(re_fbbClipBox.Max()(1) + SENTINELEDGE_EPSILON))
|
2016-03-11 14:57:17 +01:00
|
|
|
|| (sed.sed_pixTopJ >= sed.sed_pixBottomJ));
|
|
|
|
|
|
|
|
// return the screen edge
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_MAKESCREENEDGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add screen edges for all polygons in current sector
|
|
|
|
void CRenderer::AddScreenEdges(void)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_ADDSCREENEDGES);
|
|
|
|
// for each polygon
|
|
|
|
INDEX ispo0 = re_pbscCurrent->bsc_ispo0;
|
|
|
|
INDEX ispoTop = re_pbscCurrent->bsc_ispo0+re_pbscCurrent->bsc_ctspo;
|
|
|
|
for(INDEX ispo = ispo0; ispo<ispoTop; ispo++) {
|
|
|
|
CScreenPolygon &spo = re_aspoScreenPolygons[ispo];
|
|
|
|
|
|
|
|
// if polygon has no edges
|
|
|
|
if (spo.spo_ctEdgeVx==0) {
|
|
|
|
// skip it
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create as much screen edges as needed
|
|
|
|
spo.spo_ised0 = re_asedScreenEdges.Count();
|
|
|
|
spo.spo_ctsed = spo.spo_ctEdgeVx/2;
|
|
|
|
re_asedScreenEdges.Push(spo.spo_ctsed);
|
|
|
|
_sfStats.IncrementCounter(CStatForm::SCI_POLYGONEDGES, spo.spo_ctsed);
|
|
|
|
|
|
|
|
// for each edge
|
|
|
|
INDEX ivxTop = spo.spo_iEdgeVx0+spo.spo_ctEdgeVx;
|
|
|
|
INDEX ised = spo.spo_ised0;
|
|
|
|
for(INDEX ivx=spo.spo_iEdgeVx0; ivx<ivxTop; ivx+=2) {
|
|
|
|
INDEX ivx0 = re_aiEdgeVxMain[ivx+0];
|
|
|
|
INDEX ivx1 = re_aiEdgeVxMain[ivx+1];
|
|
|
|
if (spo.spo_ubDirectionFlags) {
|
|
|
|
Swap(ivx0, ivx1);
|
|
|
|
}
|
|
|
|
CScreenEdge &sed = re_asedScreenEdges[ised];
|
|
|
|
// take vertices
|
|
|
|
CViewVertex &vvx0 = re_avvxViewVertices[ivx0];
|
|
|
|
CViewVertex &vvx1 = re_avvxViewVertices[ivx1];
|
|
|
|
// make screen edge
|
|
|
|
MakeScreenEdge(sed, vvx0.vvx_fI, vvx0.vvx_fJ, vvx1.vvx_fI, vvx1.vvx_fJ);
|
|
|
|
// if it crosses any lines and is not past
|
|
|
|
if (sed.sed_pixTopJ<sed.sed_pixBottomJ && sed.sed_pixBottomJ>re_pixCurrentScanJ) {
|
|
|
|
// set its polygon
|
|
|
|
sed.sed_pspo = &spo;
|
|
|
|
// add it
|
|
|
|
AddEdgeToAddAndRemoveLists(sed);
|
|
|
|
}
|
|
|
|
|
|
|
|
// go to next edge
|
|
|
|
ised++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDSCREENEDGES);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set scene rendering parameters for one polygon texture
|
|
|
|
void CRenderer::SetOneTextureParameters(CBrushPolygon &bpo, ScenePolygon &spo, INDEX iLayer)
|
|
|
|
{
|
|
|
|
spo.spo_aptoTextures[iLayer] = NULL;
|
|
|
|
CTextureData *ptd = (CTextureData *)bpo.bpo_abptTextures[iLayer].bpt_toTexture.GetData();
|
|
|
|
|
|
|
|
// if there is no texture or it should not be shown
|
|
|
|
if (ptd==NULL || !_wrpWorldRenderPrefs.wrp_abTextureLayers[iLayer]) {
|
|
|
|
// do nothing
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CWorkingPlane &wpl = *bpo.bpo_pbplPlane->bpl_pwplWorking;
|
|
|
|
// set texture and its parameters
|
|
|
|
spo.spo_aptoTextures[iLayer] = &bpo.bpo_abptTextures[iLayer].bpt_toTexture;
|
|
|
|
|
|
|
|
// get texture blending type
|
|
|
|
CTextureBlending &tb = re_pwoWorld->wo_atbTextureBlendings[bpo.bpo_abptTextures[iLayer].s.bpt_ubBlend];
|
|
|
|
|
|
|
|
// set texture blending flags
|
|
|
|
ASSERT( BPTF_CLAMPU==STXF_CLAMPU && BPTF_CLAMPV==STXF_CLAMPV && BPTF_AFTERSHADOW==STXF_AFTERSHADOW);
|
|
|
|
spo.spo_aubTextureFlags[iLayer] =
|
|
|
|
(bpo.bpo_abptTextures[iLayer].s.bpt_ubFlags & (BPTF_CLAMPU|BPTF_CLAMPV|BPTF_AFTERSHADOW))
|
|
|
|
| (tb.tb_ubBlendingType);
|
|
|
|
if( bpo.bpo_abptTextures[iLayer].s.bpt_ubFlags & BPTF_REFLECTION) spo.spo_aubTextureFlags[iLayer] |= STXF_REFLECTION;
|
|
|
|
|
|
|
|
// set texture blending color
|
|
|
|
spo.spo_acolColors[iLayer] = MulColors( bpo.bpo_abptTextures[iLayer].s.bpt_colColor, tb.tb_colMultiply);
|
|
|
|
|
|
|
|
// if texture should be not transformed
|
|
|
|
INDEX iTransformation = bpo.bpo_abptTextures[iLayer].s.bpt_ubScroll;
|
|
|
|
if( iTransformation==0)
|
|
|
|
{
|
|
|
|
// if texture is wrapped on both axes
|
|
|
|
if( (bpo.bpo_abptTextures[iLayer].s.bpt_ubFlags&(BPTF_CLAMPU|BPTF_CLAMPV))==0)
|
|
|
|
{ // make a mapping adjusted for texture wrapping
|
|
|
|
const MEX mexMaskU = ptd->GetWidth() -1;
|
|
|
|
const MEX mexMaskV = ptd->GetHeight() -1;
|
|
|
|
CMappingDefinition mdTmp = bpo.bpo_abptTextures[iLayer].bpt_mdMapping;
|
|
|
|
mdTmp.md_fUOffset = (FloatToInt(mdTmp.md_fUOffset*1024.0f) & mexMaskU) /1024.0f;
|
|
|
|
mdTmp.md_fVOffset = (FloatToInt(mdTmp.md_fVOffset*1024.0f) & mexMaskV) /1024.0f;
|
|
|
|
const FLOAT3D vOffset = wpl.wpl_plView.ReferencePoint() - wpl.wpl_mvView.mv_vO;
|
|
|
|
const FLOAT fS = vOffset % wpl.wpl_mvView.mv_vU;
|
|
|
|
const FLOAT fT = vOffset % wpl.wpl_mvView.mv_vV;
|
|
|
|
const FLOAT fU = fS*mdTmp.md_fUoS + fT*mdTmp.md_fUoT + mdTmp.md_fUOffset;
|
|
|
|
const FLOAT fV = fS*mdTmp.md_fVoS + fT*mdTmp.md_fVoT + mdTmp.md_fVOffset;
|
|
|
|
mdTmp.md_fUOffset += (FloatToInt(fU*1024.0f) & ~mexMaskU) /1024.0f;
|
|
|
|
mdTmp.md_fVOffset += (FloatToInt(fV*1024.0f) & ~mexMaskV) /1024.0f;
|
|
|
|
// make texture mapping vectors from default vectors of the plane
|
|
|
|
mdTmp.MakeMappingVectors( wpl.wpl_mvView, spo.spo_amvMapping[iLayer]);
|
|
|
|
}
|
|
|
|
// if texture is clamped
|
|
|
|
else {
|
|
|
|
// just make texture mapping vectors from default vectors of the plane
|
|
|
|
bpo.bpo_abptTextures[iLayer].bpt_mdMapping.MakeMappingVectors( wpl.wpl_mvView, spo.spo_amvMapping[iLayer]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if texture should be transformed
|
|
|
|
else {
|
|
|
|
// make mapping vectors as normal and then transform them
|
|
|
|
CMappingDefinition &mdBase = bpo.bpo_abptTextures[iLayer].bpt_mdMapping;
|
|
|
|
CMappingDefinition &mdScroll = re_pwoWorld->wo_attTextureTransformations[iTransformation].tt_mdTransformation;
|
|
|
|
CMappingVectors mvTmp;
|
|
|
|
mdBase.MakeMappingVectors( wpl.wpl_mvView, mvTmp);
|
|
|
|
mdScroll.TransformMappingVectors( mvTmp, spo.spo_amvMapping[iLayer]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a screen polygon for a brush polygon
|
|
|
|
*/
|
|
|
|
CScreenPolygon *CRenderer::MakeScreenPolygon(CBrushPolygon &bpo)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_MAKESCREENPOLYGON);
|
|
|
|
_pfRenderProfile.IncrementTimerAveragingCounter(CRenderProfile::PTI_MAKESCREENPOLYGON, 1);
|
|
|
|
// create a new screen polygon
|
|
|
|
CScreenPolygon &spo = re_aspoScreenPolygons.Push();
|
|
|
|
ScenePolygon &sppo = spo.spo_spoScenePolygon;
|
|
|
|
bpo.bpo_pspoScreenPolygon = &spo;
|
|
|
|
CBrush3D &br = *re_pbrCurrent;
|
|
|
|
CBrushSector &bsc = *re_pbscCurrent;
|
|
|
|
sppo.spo_pvPolygon = &bpo;
|
|
|
|
sppo.spo_iVtx0 = 0;
|
|
|
|
sppo.spo_ctVtx = 0;
|
|
|
|
sppo.spo_piElements = NULL;
|
|
|
|
sppo.spo_ctElements = 0;
|
|
|
|
spo.spo_bActive = TRUE;
|
|
|
|
spo.spo_ubSpanAdded = 0;
|
|
|
|
// link with brush polygon
|
|
|
|
spo.spo_pbpoBrushPolygon = &bpo;
|
|
|
|
spo.spo_ubIllumination = bpo.bpo_bppProperties.bpp_ubIlluminationType;
|
|
|
|
|
|
|
|
// if this is a field brush
|
|
|
|
if (br.br_pfsFieldSettings!=NULL) {
|
|
|
|
// set the polygon up to render as a field brush
|
|
|
|
bpo.bpo_abptTextures[0].bpt_toTexture.SetData(br.br_pfsFieldSettings->fs_toTexture.GetData());
|
|
|
|
bpo.bpo_abptTextures[0].s.bpt_colColor = br.br_pfsFieldSettings->fs_colColor;
|
|
|
|
bpo.bpo_abptTextures[0].s.bpt_ubScroll = 0;
|
|
|
|
bpo.bpo_abptTextures[0].s.bpt_ubFlags = 0;
|
|
|
|
bpo.bpo_abptTextures[0].s.bpt_ubBlend = BPT_BLEND_BLEND;
|
|
|
|
bpo.bpo_abptTextures[1].bpt_toTexture.SetData(NULL);
|
|
|
|
bpo.bpo_abptTextures[2].bpt_toTexture.SetData(NULL);
|
|
|
|
bpo.bpo_ulFlags = BPOF_PORTAL|BPOF_RENDERASPORTAL|BPOF_FULLBRIGHT|BPOF_TRANSLUCENT|BPOF_DETAILPOLYGON;
|
|
|
|
}
|
|
|
|
|
|
|
|
// just copy depth gradient from plane
|
|
|
|
CWorkingPlane &wpl = *bpo.bpo_pbplPlane->bpl_pwplWorking;
|
|
|
|
spo.spo_pgOoK = wpl.wpl_pgOoK;
|
|
|
|
spo.spo_spoScenePolygon.spo_cColor = ColorForPolygons(bpo.bpo_colColor, bsc.bsc_colColor);
|
|
|
|
|
|
|
|
// set polygon shadow
|
|
|
|
sppo.spo_psmShadowMap = NULL;
|
|
|
|
if(_wrpWorldRenderPrefs.wrp_shtShadows != CWorldRenderPrefs::SHT_NONE
|
|
|
|
&&!(bpo.bpo_ulFlags&BPOF_FULLBRIGHT)) {
|
|
|
|
sppo.spo_psmShadowMap = &bpo.bpo_smShadowMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
// make texture mapping vectors from default vectors of the plane
|
|
|
|
CMappingDefinition mdShadow;
|
|
|
|
mdShadow.md_fUoS = 1;
|
|
|
|
mdShadow.md_fUoT = 0;
|
|
|
|
mdShadow.md_fVoS = 0;
|
|
|
|
mdShadow.md_fVoT = 1;
|
|
|
|
mdShadow.md_fUOffset = -bpo.bpo_smShadowMap.sm_mexOffsetX/1024.0f;
|
|
|
|
mdShadow.md_fVOffset = -bpo.bpo_smShadowMap.sm_mexOffsetY/1024.0f;
|
|
|
|
mdShadow.MakeMappingVectors(wpl.wpl_mvView, sppo.spo_amvMapping[3]);
|
|
|
|
// adjust shadow blending type
|
|
|
|
CTextureBlending &tbShadow = re_pwoWorld->wo_atbTextureBlendings[bpo.bpo_bppProperties.bpp_ubShadowBlend];
|
|
|
|
sppo.spo_aubTextureFlags[3] = STXF_CLAMPU|STXF_CLAMPV|tbShadow.tb_ubBlendingType;
|
|
|
|
// set shadow blending color
|
|
|
|
sppo.spo_acolColors[3] = MulColors(bpo.bpo_colShadow, tbShadow.tb_colMultiply);
|
|
|
|
|
|
|
|
// set textures for the polygon
|
|
|
|
SetOneTextureParameters( bpo, sppo, 0);
|
|
|
|
SetOneTextureParameters( bpo, sppo, 1);
|
|
|
|
SetOneTextureParameters( bpo, sppo, 2);
|
|
|
|
|
|
|
|
// clear polygon flags
|
|
|
|
sppo.spo_ulFlags = 0;
|
|
|
|
if (_wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_TEXTURE) {
|
|
|
|
sppo.spo_aptoTextures[0] = NULL;
|
|
|
|
sppo.spo_aptoTextures[1] = NULL;
|
|
|
|
sppo.spo_aptoTextures[2] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the sector has fog
|
|
|
|
if (re_bCurrentSectorHasFog) {
|
|
|
|
// mark for rendering with fog
|
|
|
|
sppo.spo_ulFlags |= SPOF_RENDERFOG;
|
|
|
|
}
|
|
|
|
// if the sector has haze
|
|
|
|
if (re_bCurrentSectorHasHaze) {
|
|
|
|
// mark polygon for haze rendering
|
|
|
|
sppo.spo_ulFlags |= SPOF_RENDERHAZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the polygon is selected
|
|
|
|
BOOL bSelected = PolygonIsSelected( bpo, br, bsc);
|
|
|
|
if (bSelected) {
|
|
|
|
// mark this polygon for drawing as selected
|
|
|
|
sppo.spo_ulFlags |= SPOF_SELECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the polygon is transparent
|
|
|
|
if( bpo.bpo_ulFlags & BPOF_TRANSPARENT) {
|
|
|
|
// mark that this polygon will need alpha keying
|
|
|
|
sppo.spo_ulFlags |= SPOF_TRANSPARENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL bBackgroundPolygon = re_bBackgroundEnabled &&
|
|
|
|
(bpo.bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->en_ulFlags&ENF_BACKGROUND);
|
|
|
|
// if this is a backfill polygon or illumination polygon for rendering lights
|
|
|
|
if (bBackgroundPolygon
|
|
|
|
||(re_ubLightIllumination!=0 && re_ubLightIllumination==bpo.bpo_bppProperties.bpp_ubIlluminationType)) {
|
|
|
|
// mark this polygon as backlight (for shadow rendering)
|
|
|
|
sppo.spo_ulFlags |= SPOF_BACKLIGHT;
|
|
|
|
// adjust gradients used for sorting to be just before the far sentinel
|
|
|
|
spo.spo_pgOoK.Add(-2.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
// init as not in stack
|
|
|
|
ASSERT(!spo.spo_lnInStack.IsLinked());
|
|
|
|
spo.spo_iInStack = 0;
|
|
|
|
spo.spo_psedSpanStart = NULL;
|
|
|
|
|
|
|
|
// eventually adjust polygon opacity depending on brush entity variable
|
|
|
|
BOOL bForceTraslucency = FALSE;
|
|
|
|
const FLOAT fOpacity = br.br_penEntity->GetOpacity();
|
|
|
|
if( fOpacity<1)
|
|
|
|
{ // better to hold opacity in integer
|
|
|
|
const SLONG slOpacity = NormFloatToByte(fOpacity);
|
|
|
|
// for all texture layers (not shadowmap!)
|
|
|
|
for( INDEX i=0; i<3; i++) {
|
|
|
|
// if texture is opaque
|
|
|
|
if( (sppo.spo_aubTextureFlags[i] & STXF_BLEND_MASK) == 0) {
|
|
|
|
// set it to blend with opaque alpha
|
|
|
|
sppo.spo_aubTextureFlags[i] |= STXF_BLEND_ALPHA;
|
|
|
|
sppo.spo_acolColors[i] |= CT_AMASK;
|
|
|
|
}
|
|
|
|
// if texture is blended
|
|
|
|
if( sppo.spo_aubTextureFlags[i] & STXF_BLEND_ALPHA) {
|
|
|
|
// adjust it's alpha factor
|
|
|
|
SLONG slAlpha = (sppo.spo_acolColors[i] & CT_AMASK) >>CT_ASHIFT;
|
|
|
|
slAlpha = (slAlpha*slOpacity)>>8;
|
|
|
|
sppo.spo_acolColors[i] &= ~CT_AMASK;
|
|
|
|
sppo.spo_acolColors[i] |= slAlpha;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// mark that we need translucency
|
|
|
|
bForceTraslucency = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// not translucent by default
|
|
|
|
bpo.bpo_ulFlags &= ~BPOF_RENDERTRANSLUCENT;
|
|
|
|
|
|
|
|
// if the polygon is a portal that is either translucent or selected
|
|
|
|
if( bForceTraslucency || ((bpo.bpo_ulFlags & BPOF_RENDERASPORTAL)
|
|
|
|
&& ((bpo.bpo_ulFlags & BPOF_TRANSLUCENT) || bSelected))) {
|
|
|
|
// if not rendering shadows
|
|
|
|
if (!re_bRenderingShadows) {
|
|
|
|
// mark for rendering as translucent
|
|
|
|
bpo.bpo_ulFlags |= BPOF_RENDERTRANSLUCENT;
|
|
|
|
// add it to the list of translucent span polygons in this renderer
|
|
|
|
if (bBackgroundPolygon) {
|
|
|
|
spo.spo_spoScenePolygon.spo_pspoSucc = re_pspoFirstBackgroundTranslucent;
|
|
|
|
re_pspoFirstBackgroundTranslucent = &spo.spo_spoScenePolygon;
|
|
|
|
} else {
|
|
|
|
spo.spo_spoScenePolygon.spo_pspoSucc = re_pspoFirstTranslucent;
|
|
|
|
re_pspoFirstTranslucent = &spo.spo_spoScenePolygon;
|
|
|
|
}
|
|
|
|
// if it is not translucent (ie. it is just plain portal, but selected)
|
|
|
|
if( !(bpo.bpo_ulFlags & BPOF_TRANSLUCENT) && !bForceTraslucency) {
|
|
|
|
// set its texture for selection
|
|
|
|
CModelObject *pmoSelectedPortal = _wrpWorldRenderPrefs.wrp_pmoSelectedPortal;
|
|
|
|
if (pmoSelectedPortal!=NULL) {
|
|
|
|
sppo.spo_aptoTextures[0] = &pmoSelectedPortal->mo_toTexture;
|
|
|
|
sppo.spo_acolColors[0] = C_WHITE|CT_OPAQUE;
|
|
|
|
sppo.spo_aubTextureFlags[0] = STXF_BLEND_ALPHA;
|
|
|
|
}
|
|
|
|
// get its mapping gradients from shadowmap and stretch
|
2016-04-24 01:01:37 +02:00
|
|
|
//CWorkingPlane &wpl = *bpo.bpo_pbplPlane->bpl_pwplWorking;
|
2016-03-11 14:57:17 +01:00
|
|
|
sppo.spo_amvMapping[0] = sppo.spo_amvMapping[3];
|
|
|
|
FLOAT fStretch = bpo.bpo_boxBoundingBox.Size().Length()/1000;
|
|
|
|
sppo.spo_amvMapping[0].mv_vU *= fStretch;
|
|
|
|
sppo.spo_amvMapping[0].mv_vV *= fStretch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if the polygon is ordinary wall
|
|
|
|
else {
|
|
|
|
// add it to the list of span polygons in this renderer
|
|
|
|
if (bBackgroundPolygon) {
|
|
|
|
spo.spo_spoScenePolygon.spo_pspoSucc = re_pspoFirstBackground;
|
|
|
|
re_pspoFirstBackground = &spo.spo_spoScenePolygon;
|
|
|
|
} else {
|
|
|
|
spo.spo_spoScenePolygon.spo_pspoSucc = re_pspoFirst;
|
|
|
|
re_pspoFirst = &spo.spo_spoScenePolygon;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return the screen polygon
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_MAKESCREENPOLYGON);
|
|
|
|
return &spo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Add a polygon to scene rendering. */
|
|
|
|
void CRenderer::AddPolygonToScene( CScreenPolygon *pspo)
|
|
|
|
{
|
|
|
|
// if the polygon is not falid or occluder and not selected
|
2016-04-23 23:57:23 +02:00
|
|
|
CBrushPolygon *pbpo = pspo->spo_pbpoBrushPolygon;
|
|
|
|
if(pbpo==NULL || ((pbpo->bpo_ulFlags&BPOF_OCCLUDER) && (!(pbpo->bpo_ulFlags&BPOF_SELECTED) ||
|
2016-03-11 14:57:17 +01:00
|
|
|
_wrpWorldRenderPrefs.GetSelectionType()!=CWorldRenderPrefs::ST_POLYGONS))) {
|
|
|
|
// do not add it to rendering
|
|
|
|
return;
|
|
|
|
}
|
2016-04-23 23:57:23 +02:00
|
|
|
CBrushSector &bsc = *pbpo->bpo_pbscSector;
|
2016-03-11 14:57:17 +01:00
|
|
|
ScenePolygon &sppo = pspo->spo_spoScenePolygon;
|
|
|
|
const CViewVertex *pvvx0 = &re_avvxViewVertices[bsc.bsc_ivvx0];
|
2016-04-23 23:57:23 +02:00
|
|
|
const INDEX ctVtx = pbpo->bpo_apbvxTriangleVertices.Count();
|
2016-03-11 14:57:17 +01:00
|
|
|
sppo.spo_iVtx0 = _avtxScene.Count();
|
|
|
|
GFXVertex3 *pvtx = _avtxScene.Push(ctVtx);
|
|
|
|
|
|
|
|
// find vertex with nearest Z distance while copying vertices
|
|
|
|
FLOAT fNearestZ = 123456789.0f;
|
|
|
|
for( INDEX i=0; i<ctVtx; i++) {
|
2016-04-23 23:57:23 +02:00
|
|
|
CBrushVertex *pbvx = pbpo->bpo_apbvxTriangleVertices[i];
|
2016-03-11 14:57:17 +01:00
|
|
|
const INDEX iVtx = bsc.bsc_abvxVertices.Index(pbvx);
|
|
|
|
const FLOAT3D &v = pvvx0[iVtx].vvx_vView;
|
|
|
|
if( -v(3)<fNearestZ) fNearestZ = -v(3); // inverted because of negative sign
|
|
|
|
pvtx[i].x = v(1);
|
|
|
|
pvtx[i].y = v(2);
|
|
|
|
pvtx[i].z = v(3);
|
|
|
|
}
|
|
|
|
// nearestZ is larger one of plane distance and nearest vertex distance
|
|
|
|
const FLOAT fNearestD = -pspo->spo_pbpoBrushPolygon->bpo_pbplPlane->bpl_pwplWorking->wpl_plView.Distance();
|
|
|
|
sppo.spo_fNearestZ = Max( fNearestZ, fNearestD);
|
|
|
|
|
|
|
|
// all done
|
|
|
|
sppo.spo_ctVtx = ctVtx;
|
2016-04-23 23:57:23 +02:00
|
|
|
sppo.spo_ctElements = pbpo->bpo_aiTriangleElements.Count();
|
|
|
|
sppo.spo_piElements = sppo.spo_ctElements ? &pbpo->bpo_aiTriangleElements[0] : NULL;
|
2016-03-11 14:57:17 +01:00
|
|
|
_sfStats.IncrementCounter(CStatForm::SCI_SCENE_TRIANGLES, sppo.spo_ctElements/3);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate a span for a polygon on current scan line.
|
|
|
|
*/
|
|
|
|
void CRenderer::MakeSpan(CScreenPolygon &spo, CScreenEdge *psed0, CScreenEdge *psed1)
|
|
|
|
{
|
|
|
|
// the polygon must not be portal and not illuminating for rendering lights
|
|
|
|
ASSERT(!(spo.IsPortal()
|
|
|
|
&& (re_ubLightIllumination==0 || re_ubLightIllumination!=spo.spo_ubIllumination)));
|
|
|
|
|
|
|
|
// if rendering shadows
|
|
|
|
if( re_bRenderingShadows) {
|
|
|
|
// create a new span for it
|
|
|
|
CSpan *pspSpan = &re_aspSpans.Push();
|
|
|
|
// set up span values
|
|
|
|
pspSpan->sp_psedEdge0 = psed0;
|
|
|
|
pspSpan->sp_psedEdge1 = psed1;
|
|
|
|
pspSpan->sp_pspoPolygon = &spo;
|
|
|
|
}
|
|
|
|
// if rendering view
|
|
|
|
else {
|
|
|
|
// if no span added to this polygon yet
|
|
|
|
if( !spo.spo_ubSpanAdded) {
|
|
|
|
spo.spo_ubSpanAdded = 1;
|
|
|
|
// add mirror if needed
|
|
|
|
AddMirror(spo);
|
|
|
|
// add polygon to scene polygons
|
|
|
|
AddPolygonToScene(&spo);
|
|
|
|
const PIX pixI0 = PIXCoord(psed0->sed_xI);
|
|
|
|
const PIX pixI1 = PIXCoord(psed1->sed_xI);
|
|
|
|
spo.spo_pixMinI = pixI0;
|
|
|
|
spo.spo_pixMaxI = pixI1;
|
|
|
|
spo.spo_pixMinJ = re_pixCurrentScanJ;
|
|
|
|
spo.spo_pixMaxJ = re_pixCurrentScanJ;
|
|
|
|
spo.spo_pixTotalArea = pixI1-pixI0;
|
|
|
|
} else {
|
|
|
|
const PIX pixI0 = PIXCoord(psed0->sed_xI);
|
|
|
|
const PIX pixI1 = PIXCoord(psed1->sed_xI);
|
|
|
|
spo.spo_pixMinI = Min(spo.spo_pixMinI, pixI0);
|
|
|
|
spo.spo_pixMaxI = Max(spo.spo_pixMaxI, pixI1);
|
|
|
|
spo.spo_pixMinJ = Min(spo.spo_pixMinJ, re_pixCurrentScanJ);
|
|
|
|
spo.spo_pixMaxJ = Max(spo.spo_pixMaxJ, re_pixCurrentScanJ);
|
|
|
|
spo.spo_pixTotalArea += pixI1-pixI0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add spans in current line to scene.
|
|
|
|
*/
|
|
|
|
void CRenderer::AddSpansToScene(void)
|
|
|
|
{
|
|
|
|
if( !re_bRenderingShadows) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-24 01:01:37 +02:00
|
|
|
//FLOAT fpixLastScanJOffseted = re_pixCurrentScanJ-1 +OFFSET_DN;
|
2016-03-11 14:57:17 +01:00
|
|
|
// first, little safety check - quit if zero spans in line!
|
|
|
|
INDEX ctSpans = re_aspSpans.Count();
|
|
|
|
if( ctSpans==0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_ADDSPANSTOSCENE);
|
|
|
|
UBYTE *pubShadow = re_pubShadow+re_slShadowWidth*re_iCurrentScan;
|
|
|
|
INDEX ctPixels = 0;
|
|
|
|
// for all spans in the current line
|
|
|
|
for( INDEX iSpan=0; iSpan<ctSpans; iSpan++)
|
|
|
|
{ // get span start and stop I from edges
|
|
|
|
const CSpan &spSpan = re_aspSpans[iSpan];
|
|
|
|
PIX pixI0 = PIXCoord( spSpan.sp_psedEdge0->sed_xI);
|
|
|
|
PIX pixI1 = PIXCoord( spSpan.sp_psedEdge1->sed_xI);
|
|
|
|
// get its length
|
|
|
|
PIX pixLen = pixI1-pixI0;
|
|
|
|
// skip this span if zero pixels long
|
|
|
|
if( pixLen<=0) continue;
|
|
|
|
|
|
|
|
// if the span's polygon is background and of proper illumination
|
|
|
|
if ((spSpan.sp_pspoPolygon->spo_spoScenePolygon.spo_ulFlags & SPOF_BACKLIGHT)
|
|
|
|
&&(spSpan.sp_pspoPolygon->spo_ubIllumination==re_ubLightIllumination)) {
|
|
|
|
// mark those pixels as lighted
|
|
|
|
memset(pubShadow, 255, pixLen);
|
|
|
|
pubShadow+=pixLen;
|
|
|
|
// mark that at least one pixel is lighted
|
|
|
|
re_bSomeLightExists = TRUE;
|
|
|
|
// if the spans polygon is some other polygon
|
|
|
|
} else {
|
|
|
|
// mark those pixels as shadowed
|
|
|
|
memset(pubShadow, 0, pixLen);
|
|
|
|
pubShadow+=pixLen;
|
|
|
|
// mark that at least one pixel is darkened
|
|
|
|
re_bSomeDarkExists = TRUE;
|
|
|
|
}
|
|
|
|
// add to pixel counter
|
|
|
|
ctPixels+=pixLen;
|
|
|
|
}
|
|
|
|
ASSERT(ctPixels<=re_pixSizeI);
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDSPANSTOSCENE);
|
|
|
|
}
|
|
|
|
|