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
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
#include <Engine/StdH.h>
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
#include <Engine/Brushes/Brush.h>
|
|
|
|
#include <Engine/Brushes/BrushTransformed.h>
|
|
|
|
#include <Engine/Rendering/Render.h>
|
|
|
|
#include <Engine/Rendering/Render_internal.h>
|
|
|
|
#include <Engine/Base/Console.h>
|
|
|
|
#include <Engine/Templates/DynamicContainer.h>
|
|
|
|
#include <Engine/Templates/DynamicContainer.cpp>
|
|
|
|
|
|
|
|
#include <Engine/Light/LightSource.h>
|
|
|
|
#include <Engine/Light/Gradient.h>
|
|
|
|
#include <Engine/Base/ListIterator.inl>
|
|
|
|
#include <Engine/World/World.h>
|
|
|
|
#include <Engine/Entities/Entity.h>
|
|
|
|
#include <Engine/Templates/StaticArray.cpp>
|
|
|
|
#include <Engine/Math/Clipping.inl>
|
|
|
|
#include <Engine/Entities/EntityClass.h>
|
|
|
|
#include <Engine/World/WorldSettings.h>
|
|
|
|
#include <Engine/Entities/EntityProperties.h>
|
|
|
|
#include <Engine/Entities/FieldSettings.h>
|
|
|
|
#include <Engine/Entities/ShadingInfo.h>
|
|
|
|
#include <Engine/Light/LensFlares.h>
|
|
|
|
#include <Engine/Models/ModelObject.h>
|
|
|
|
#include <Engine/Models/RenderModel.h>
|
|
|
|
#include <Engine/Ska/Render.h>
|
|
|
|
#include <Engine/Terrain/Terrain.h>
|
|
|
|
#include <Engine/Templates/BSP.h>
|
|
|
|
#include <Engine/World/WorldEditingProfile.h>
|
|
|
|
#include <Engine/Brushes/BrushArchive.h>
|
|
|
|
#include <Engine/Math/Float.h>
|
|
|
|
#include <Engine/Math/OBBox.h>
|
|
|
|
#include <Engine/Math/Geometry.inl>
|
|
|
|
|
|
|
|
#include <Engine/Graphics/DrawPort.h>
|
|
|
|
#include <Engine/Graphics/GfxLibrary.h>
|
|
|
|
#include <Engine/Graphics/Fog_internal.h>
|
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
#include <Engine/Base/Statistics_Internal.h>
|
2016-03-11 14:57:17 +01:00
|
|
|
#include <Engine/Rendering/RenderProfile.h>
|
|
|
|
|
|
|
|
#include <Engine/Templates/LinearAllocator.cpp>
|
|
|
|
#include <Engine/Templates/DynamicArray.cpp>
|
|
|
|
#include <Engine/Templates/StaticStackArray.cpp>
|
|
|
|
#include <Engine/Templates/DynamicStackArray.cpp>
|
|
|
|
|
|
|
|
extern BOOL _bSomeDarkExists;
|
|
|
|
extern INDEX d3d_bAlternateDepthReads;
|
|
|
|
|
|
|
|
// general coordinate stack referenced by the scene polygons
|
|
|
|
extern CStaticStackArray<GFXVertex3> _avtxScene;
|
|
|
|
|
|
|
|
|
|
|
|
//#pragma optimize ("gt", on)
|
|
|
|
#pragma inline_depth(255)
|
|
|
|
#pragma inline_recursion(on)
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
//#define ASER_EXTREME_CHECKING 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// the renderer structures used in rendering
|
|
|
|
#define MAX_RENDERERS 2
|
|
|
|
static CRenderer _areRenderers[MAX_RENDERERS];
|
|
|
|
static BOOL _bMirrorDrawn = FALSE;
|
|
|
|
|
|
|
|
extern INDEX wld_bAlwaysAddAll;
|
|
|
|
extern INDEX wld_bRenderEmptyBrushes;
|
|
|
|
extern INDEX wld_bRenderDetailPolygons;
|
|
|
|
extern INDEX gfx_bRenderParticles;
|
|
|
|
extern INDEX gfx_bRenderModels;
|
|
|
|
extern INDEX gfx_bRenderFog;
|
|
|
|
extern INDEX gfx_bRenderPredicted;
|
|
|
|
extern INDEX gfx_iLensFlareQuality;
|
|
|
|
extern BOOL _bMultiPlayer;
|
|
|
|
|
|
|
|
// variables for selection on rendering
|
2016-03-29 03:03:54 +02:00
|
|
|
CBrushVertexSelection *_pselbvxtSelectOnRender = NULL;
|
|
|
|
CStaticStackArray<PIX2D> *_pavpixSelectLasso = NULL;
|
|
|
|
CEntitySelection *_pselenSelectOnRender = NULL;
|
|
|
|
PIX2D _vpixSelectNearPoint = PIX2D(0,0);
|
|
|
|
BOOL _bSelectAlternative = FALSE;
|
|
|
|
PIX _pixDeltaAroundVertex = 10;
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
// shading info for viewer of last rendered view
|
|
|
|
FLOAT3D _vViewerLightDirection;
|
|
|
|
COLOR _colViewerLight;
|
|
|
|
COLOR _colViewerAmbient;
|
|
|
|
|
|
|
|
|
|
|
|
// handy statistic helper routines
|
|
|
|
|
|
|
|
static enum CStatForm::StatTimerIndex _stiLastStatsMode = (enum CStatForm::StatTimerIndex)-1;
|
|
|
|
|
|
|
|
void StopStatsMode(void)
|
|
|
|
{
|
|
|
|
ASSERT( (INDEX)_stiLastStatsMode != -1);
|
|
|
|
if( _stiLastStatsMode>=0) _sfStats.StopTimer(_stiLastStatsMode);
|
|
|
|
_stiLastStatsMode = (enum CStatForm::StatTimerIndex)-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StartStatsMode( enum CStatForm::StatTimerIndex sti)
|
|
|
|
{
|
|
|
|
ASSERT( (INDEX)sti != -1);
|
|
|
|
ASSERT( (INDEX)_stiLastStatsMode == -1);
|
|
|
|
if( sti>=0) _sfStats.StartTimer(sti);
|
|
|
|
_stiLastStatsMode = sti;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChangeStatsMode( enum CStatForm::StatTimerIndex sti)
|
|
|
|
{
|
|
|
|
StopStatsMode();
|
|
|
|
StartStatsMode(sti);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// screen edges, polygons and trapezoids used in rasterizing
|
|
|
|
CDynamicStackArray<CAddEdge> CRenderer::re_aadeAddEdges;
|
|
|
|
CDynamicStackArray<CScreenEdge> CRenderer::re_asedScreenEdges;
|
|
|
|
// spans for current scan line
|
|
|
|
CDynamicStackArray<CSpan> CRenderer::re_aspSpans;
|
|
|
|
|
|
|
|
// vertices clipped to current clip plane
|
|
|
|
CStaticStackArray<INDEX> CRenderer::re_aiClipBuffer;
|
|
|
|
// buffers for edges of polygons
|
|
|
|
CStaticStackArray<INDEX> CRenderer::re_aiEdgeVxClipSrc;
|
|
|
|
CStaticStackArray<INDEX> CRenderer::re_aiEdgeVxClipDst;
|
|
|
|
|
|
|
|
// add and remove lists for each scan line
|
|
|
|
CStaticArray<CListHead> CRenderer::re_alhAddLists;
|
|
|
|
CStaticArray<INDEX> CRenderer::re_actAddCounts; // count of edges in given add list
|
|
|
|
CStaticArray<CScreenEdge *> CRenderer::re_apsedRemoveFirst;
|
|
|
|
CStaticStackArray<CActiveEdge> CRenderer::re_aaceActiveEdgesTmp;
|
|
|
|
CStaticStackArray<CActiveEdge> CRenderer::re_aaceActiveEdges;
|
|
|
|
|
|
|
|
// container for sorting translucent polygons
|
|
|
|
CDynamicStackArray<CTranslucentPolygon> CRenderer::re_atcTranslucentPolygons;
|
|
|
|
|
|
|
|
// container for all light influencing current model
|
|
|
|
struct ModelLight {
|
|
|
|
CLightSource *ml_plsLight; // the light source
|
|
|
|
FLOAT3D ml_vDirection; // direction from light to the model position (normalized)
|
|
|
|
FLOAT ml_fShadowIntensity; // intensity at the model position (for shadow)
|
|
|
|
FLOAT ml_fR, ml_fG, ml_fB; // light components at light source (0..255)
|
|
|
|
inline void Clear(void) {};
|
|
|
|
};
|
|
|
|
static CDynamicStackArray<struct ModelLight> _amlLights;
|
|
|
|
|
|
|
|
static INDEX _ctMaxAddEdges=0;
|
|
|
|
static INDEX _ctMaxActiveEdges=0;
|
|
|
|
|
|
|
|
void RendererInfo(void)
|
|
|
|
{
|
|
|
|
CPrintF("Renderer information:\n");
|
|
|
|
|
|
|
|
SLONG slMem = 0;
|
|
|
|
|
|
|
|
slMem += CRenderer::re_aadeAddEdges.da_Count*sizeof(CAddEdge);
|
|
|
|
slMem += CRenderer::re_asedScreenEdges.da_Count*sizeof(CScreenEdge);
|
|
|
|
|
|
|
|
slMem += CRenderer::re_aspSpans.da_Count*sizeof(CSpan);
|
|
|
|
|
|
|
|
slMem += CRenderer::re_aiClipBuffer.sa_Count*sizeof(INDEX);
|
|
|
|
slMem += CRenderer::re_aiEdgeVxClipSrc.sa_Count*sizeof(INDEX);
|
|
|
|
slMem += CRenderer::re_aiEdgeVxClipDst.sa_Count*sizeof(INDEX);
|
|
|
|
|
|
|
|
slMem += CRenderer::re_alhAddLists.sa_Count*sizeof(CListHead);
|
|
|
|
slMem += CRenderer::re_actAddCounts.sa_Count*sizeof(INDEX);
|
|
|
|
slMem += CRenderer::re_apsedRemoveFirst.sa_Count*sizeof(CScreenEdge *);
|
|
|
|
|
|
|
|
slMem += CRenderer::re_atcTranslucentPolygons.da_Count*sizeof(CTranslucentPolygon);
|
|
|
|
slMem += CRenderer::re_aaceActiveEdges.sa_Count*sizeof(CActiveEdge);
|
|
|
|
slMem += CRenderer::re_aaceActiveEdgesTmp.sa_Count*sizeof(CActiveEdge);
|
|
|
|
|
|
|
|
for (INDEX ire = 0; ire<MAX_RENDERERS; ire++) {
|
|
|
|
CRenderer &re = _areRenderers[ire];
|
|
|
|
slMem += re.re_aspoScreenPolygons.da_Count*sizeof(CScreenPolygon);
|
|
|
|
slMem += re.re_admDelayedModels.da_Count*sizeof(CDelayedModel);
|
|
|
|
slMem += re.re_cenDrawn.sa_Count*sizeof(CEntity*);
|
|
|
|
slMem += re.re_alfiLensFlares.sa_Count*sizeof(CLensFlareInfo);
|
|
|
|
slMem += re.re_amiMirrors.da_Count*sizeof(CMirror);
|
|
|
|
slMem += re.re_avvxViewVertices.sa_Count*sizeof(CViewVertex);
|
|
|
|
slMem += re.re_aiEdgeVxMain.sa_Count*sizeof(INDEX);
|
|
|
|
}
|
|
|
|
|
|
|
|
CPrintF("Temporary memory used: %dk\n", slMem/1024);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClearRenderer(void)
|
|
|
|
{
|
|
|
|
CRenderer::re_aadeAddEdges.Clear();
|
|
|
|
CRenderer::re_asedScreenEdges.Clear();
|
|
|
|
CRenderer::re_aspSpans.Clear();
|
|
|
|
|
|
|
|
CRenderer::re_aiClipBuffer.Clear();
|
|
|
|
CRenderer::re_aiEdgeVxClipSrc.Clear();
|
|
|
|
CRenderer::re_aiEdgeVxClipDst.Clear();
|
|
|
|
|
|
|
|
CRenderer::re_alhAddLists.Clear();
|
|
|
|
CRenderer::re_actAddCounts.Clear();
|
|
|
|
CRenderer::re_apsedRemoveFirst.Clear();
|
|
|
|
CRenderer::re_atcTranslucentPolygons.Clear();
|
|
|
|
CRenderer::re_aaceActiveEdges.Clear();
|
|
|
|
CRenderer::re_aaceActiveEdgesTmp.Clear();
|
|
|
|
|
|
|
|
for (INDEX ire = 0; ire<MAX_RENDERERS; ire++) {
|
|
|
|
CRenderer &re = _areRenderers[ire];
|
|
|
|
|
|
|
|
re.re_aspoScreenPolygons.Clear();
|
|
|
|
re.re_admDelayedModels.Clear();
|
|
|
|
re.re_cenDrawn.Clear();
|
|
|
|
re.re_alfiLensFlares.Clear();
|
|
|
|
re.re_amiMirrors.Clear();
|
|
|
|
re.re_avvxViewVertices.Clear();
|
|
|
|
re.re_aiEdgeVxMain.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
CPrintF("Renderer buffers cleared.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* How much to offset left, right, top and bottom clipping towards inside (in pixels).
|
|
|
|
* This can be used to test clipping or to add an epsilon value for it.
|
|
|
|
*/
|
|
|
|
//#define CLIPMARGIN 10.0f // used for debugging clipping
|
|
|
|
#define CLIPMARGIN 0.0f
|
|
|
|
#define CLIPEPSILON 0.5f
|
|
|
|
#define CLIPMARGADD (CLIPMARGIN-CLIPEPSILON)
|
|
|
|
#define CLIPMARGSUB (CLIPMARGIN+CLIPEPSILON)
|
|
|
|
#define SENTINELEDGE_EPSILON 0.4f
|
|
|
|
|
|
|
|
#include "RendMisc.cpp"
|
|
|
|
#include "RenCache.cpp"
|
|
|
|
#include "RendClip.cpp"
|
|
|
|
#include "RendASER.cpp"
|
|
|
|
#include "RenderModels.cpp"
|
|
|
|
#include "RenderBrushes.cpp"
|
|
|
|
#include "RenderAdding.cpp"
|
|
|
|
|
|
|
|
extern FLOAT wld_fEdgeOffsetI;
|
|
|
|
extern FLOAT wld_fEdgeAdjustK;
|
|
|
|
|
|
|
|
// initialize all rendering structures
|
|
|
|
void CRenderer::Initialize(void)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_INITIALIZATION);
|
|
|
|
|
|
|
|
// used for fixing problems with extra trapezoids generated on t-junctions
|
|
|
|
if( !re_bRenderingShadows) {
|
|
|
|
re_fEdgeOffsetI = wld_fEdgeOffsetI; //0.125f;
|
|
|
|
re_fEdgeAdjustK = wld_fEdgeAdjustK; //1.0001f;
|
|
|
|
} else {
|
|
|
|
re_fEdgeOffsetI = 0.0f;
|
|
|
|
re_fEdgeAdjustK = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// prepare the raw projection (used for rendering target lines and getting object distances)
|
|
|
|
re_prProjection->ObjectPlacementL() = CPlacement3D(FLOAT3D(0.0f,0.0f,0.0f), ANGLE3D(0,0,0));
|
|
|
|
re_prProjection->ObjectFaceForwardL() = FALSE;
|
|
|
|
re_prProjection->ObjectStretchL() = FLOAT3D(1.0f, 1.0f, 1.0f);
|
|
|
|
re_prProjection->DepthBufferNearL() = 0.0f;
|
|
|
|
re_prProjection->DepthBufferFarL() = 0.9f;
|
|
|
|
re_prProjection->Prepare();
|
|
|
|
|
|
|
|
re_asedScreenEdges.PopAll();
|
|
|
|
re_aadeAddEdges.PopAll();
|
|
|
|
re_aspSpans.PopAll();
|
|
|
|
re_avvxViewVertices.PopAll();
|
|
|
|
re_aiEdgeVxMain.PopAll();
|
|
|
|
|
|
|
|
// if more scan lines are needed than last time
|
|
|
|
if (re_alhAddLists.Count()<re_ctScanLines) {
|
|
|
|
re_alhAddLists.Clear();
|
|
|
|
re_alhAddLists.New(re_ctScanLines);
|
|
|
|
re_actAddCounts.Clear();
|
|
|
|
re_actAddCounts.New(re_ctScanLines);
|
|
|
|
|
|
|
|
re_apsedRemoveFirst.Clear();
|
|
|
|
re_apsedRemoveFirst.New(re_ctScanLines);
|
|
|
|
}
|
|
|
|
// clear all add/remove lists
|
|
|
|
for(INDEX iScan=0; iScan<re_ctScanLines; iScan++) {
|
|
|
|
re_actAddCounts[iScan] = 0;
|
|
|
|
re_apsedRemoveFirst[iScan] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find selection color
|
|
|
|
re_colSelection = C_RED;
|
|
|
|
if (_wrpWorldRenderPrefs.GetSelectionType() == CWorldRenderPrefs::ST_POLYGONS) {
|
|
|
|
re_colSelection = C_YELLOW;
|
|
|
|
} else if (_wrpWorldRenderPrefs.GetSelectionType() == CWorldRenderPrefs::ST_SECTORS) {
|
|
|
|
re_colSelection = C_GREEN;
|
|
|
|
} else if (_wrpWorldRenderPrefs.GetSelectionType() == CWorldRenderPrefs::ST_ENTITIES) {
|
|
|
|
re_colSelection = C_BLUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set up renderer for first scan line
|
|
|
|
re_iCurrentScan = 0;
|
|
|
|
re_pixCurrentScanJ = re_iCurrentScan + re_pixTopScanLineJ;
|
|
|
|
re_fCurrentScanJ = FLOAT(re_pixCurrentScanJ);
|
|
|
|
|
|
|
|
// no fog or haze initially
|
|
|
|
re_bCurrentSectorHasHaze = FALSE;
|
|
|
|
re_bCurrentSectorHasFog = FALSE;
|
|
|
|
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_INITIALIZATION);
|
|
|
|
}
|
|
|
|
// add initial sectors to active lists
|
|
|
|
void CRenderer::AddInitialSectors(void)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_ADDINITIAL);
|
|
|
|
|
|
|
|
re_bViewerInHaze = FALSE;
|
|
|
|
re_ulVisExclude = 0;
|
|
|
|
re_ulVisInclude = 0;
|
|
|
|
|
|
|
|
// if showing vis tweaks
|
|
|
|
if (_wrpWorldRenderPrefs.wrp_bShowVisTweaksOn && _pselbscVisTweaks!=NULL) {
|
|
|
|
// add flags for selected flags
|
|
|
|
if (_pselbscVisTweaks->Count()>0) {
|
|
|
|
re_ulVisExclude = VISM_INCLUDEEXCLUDE;
|
|
|
|
}
|
|
|
|
FOREACHINDYNAMICCONTAINER(*_pselbscVisTweaks, CBrushSector, itbsc) {
|
|
|
|
if (itbsc->bsc_ulFlags2&BSCF2_VISIBILITYINCLUDE) {
|
|
|
|
re_ulVisInclude = itbsc->bsc_ulVisFlags&VISM_INCLUDEEXCLUDE;
|
|
|
|
} else {
|
|
|
|
re_ulVisExclude &= itbsc->bsc_ulVisFlags&VISM_INCLUDEEXCLUDE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if the background is needed
|
|
|
|
re_bBackgroundEnabled = FALSE;
|
|
|
|
if (!re_bRenderingShadows && _wrpWorldRenderPrefs.wrp_bBackgroundTextureOn) {
|
|
|
|
CEntity *penBackgroundViewer = re_pwoWorld->GetBackgroundViewer();
|
|
|
|
if (penBackgroundViewer!=NULL) {
|
|
|
|
re_bBackgroundEnabled = TRUE;
|
|
|
|
re_penBackgroundViewer = penBackgroundViewer;
|
|
|
|
re_prBackgroundProjection = re_prProjection;
|
|
|
|
CPlacement3D plViewer = re_prProjection->ViewerPlacementR();
|
|
|
|
plViewer.pl_PositionVector = FLOAT3D(0,0,0);
|
|
|
|
CPlacement3D plBcgViewer = penBackgroundViewer->GetLerpedPlacement();
|
|
|
|
if (re_prProjection->pr_bMirror) {
|
|
|
|
ReflectPositionVectorByPlane(re_prProjection->pr_plMirror, plBcgViewer.pl_PositionVector);
|
|
|
|
}
|
|
|
|
plViewer.RelativeToAbsoluteSmooth(plBcgViewer);
|
|
|
|
re_prBackgroundProjection->ViewerPlacementL() = plViewer;
|
|
|
|
re_prBackgroundProjection->ObjectPlacementL() = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0));
|
|
|
|
re_prBackgroundProjection->FarClipDistanceL() = -1.0f;
|
|
|
|
re_prBackgroundProjection->DepthBufferNearL() = 0.9f;
|
|
|
|
re_prBackgroundProjection->DepthBufferFarL() = 1.0f;
|
|
|
|
re_prBackgroundProjection->TurnOffWarpPlane(); // background never needs warp-plane clipping
|
|
|
|
re_prBackgroundProjection->Prepare();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if a viewer entity is given
|
|
|
|
if (re_penViewer!=NULL) {
|
|
|
|
// add all zoning sectors near the entity
|
|
|
|
AddZoningSectorsAroundEntity(re_penViewer, re_prProjection->ViewerPlacementR().pl_PositionVector);
|
|
|
|
// make sure the viewer is always added (if model)
|
|
|
|
if(re_penViewer->en_RenderType==CEntity::RT_MODEL ||
|
|
|
|
re_penViewer->en_RenderType==CEntity::RT_EDITORMODEL) {
|
|
|
|
AddModelEntity(re_penViewer);
|
|
|
|
}
|
|
|
|
// if a viewer polygons are given
|
|
|
|
} else if (re_pcspoViewPolygons!=NULL) {
|
|
|
|
// for each polygon
|
|
|
|
FOREACHINDYNAMICCONTAINER(*re_pcspoViewPolygons, CScreenPolygon, itspo) {
|
|
|
|
CBrushPolygon *pbpo = itspo->spo_pbpoBrushPolygon;
|
|
|
|
// get the sector, sector's brush mip, brush and entity
|
|
|
|
CBrushSector *pbsc = pbpo->bpo_pbscSector;
|
|
|
|
CBrushMip *pbmBrushMip = pbsc->bsc_pbmBrushMip;
|
|
|
|
CBrush3D *pbrBrush = pbmBrushMip->bm_pbrBrush;
|
|
|
|
ASSERT(pbrBrush!=NULL);
|
|
|
|
CEntity *penBrush = pbrBrush->br_penEntity;
|
|
|
|
// if the brush is zoning
|
|
|
|
if (penBrush->en_ulFlags&ENF_ZONING) {
|
|
|
|
// add the sector that the polygon is in
|
|
|
|
AddGivenZoningSector(pbsc);
|
|
|
|
// if the brush is non-zoning
|
|
|
|
} else {
|
|
|
|
// add sectors around it
|
|
|
|
AddZoningSectorsAroundEntity(penBrush, penBrush->GetPlacement().pl_PositionVector);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if there is no viewer entity/polygon
|
|
|
|
} else {
|
|
|
|
// set up viewer bounding box as box of minimum redraw range around viewer position
|
|
|
|
if (re_bRenderingShadows) {
|
|
|
|
// NOTE: when rendering shadows, this is set in ::RenderShadows()
|
|
|
|
//re_boxViewer = FLOATaabbox3D(re_prProjection->ViewerPlacementR().pl_PositionVector,
|
|
|
|
// 1.0f);
|
|
|
|
} else {
|
|
|
|
re_boxViewer = FLOATaabbox3D(re_prProjection->ViewerPlacementR().pl_PositionVector,
|
|
|
|
_wrpWorldRenderPrefs.wrp_fMinimumRenderRange);
|
|
|
|
}
|
|
|
|
// add all zoning sectors near viewer box
|
|
|
|
AddZoningSectorsAroundBox(re_boxViewer);
|
|
|
|
// NOTE: this is so entities outside of world can be edited in WEd
|
|
|
|
// if editor models should be rendered
|
|
|
|
if (_wrpWorldRenderPrefs.IsEditorModelsOn()) {
|
|
|
|
// add all nonzoning entities near viewer box
|
|
|
|
AddEntitiesInBox(re_boxViewer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( wld_bAlwaysAddAll) {
|
|
|
|
AddAllEntities(); // used for profiling
|
|
|
|
} else {
|
|
|
|
// NOTE: this is so that world can be viewed from the outside in game
|
|
|
|
// if no brush sectors have been added so far
|
|
|
|
if (!re_bRenderingShadows && re_lhActiveSectors.IsEmpty()) {
|
|
|
|
// add all entities in the world
|
|
|
|
AddAllEntities();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the background if needed
|
|
|
|
if (re_bBackgroundEnabled) {
|
|
|
|
AddZoningSectorsAroundEntity(re_penBackgroundViewer,
|
|
|
|
re_penBackgroundViewer->GetPlacement().pl_PositionVector);
|
|
|
|
}
|
|
|
|
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_ADDINITIAL);
|
|
|
|
}
|
|
|
|
// scan through portals for other sectors
|
|
|
|
void CRenderer::ScanForOtherSectors(void)
|
|
|
|
{
|
|
|
|
ChangeStatsMode(CStatForm::STI_WORLDVISIBILITY);
|
|
|
|
// if shadows or polygons should be drawn
|
|
|
|
if (re_bRenderingShadows
|
|
|
|
||_wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE) {
|
|
|
|
// rasterize edges into spans
|
|
|
|
ScanEdges();
|
|
|
|
}
|
|
|
|
// for each of models that were kept for delayed rendering
|
|
|
|
for(INDEX iModel=0; iModel<re_admDelayedModels.Count(); iModel++) {
|
|
|
|
// mark the entity as not active in rendering anymore
|
|
|
|
re_admDelayedModels[iModel].dm_penModel->en_ulFlags &= ~ENF_INRENDERING;
|
|
|
|
}
|
|
|
|
ChangeStatsMode(CStatForm::STI_WORLDTRANSFORM);
|
|
|
|
}
|
|
|
|
// cleanup after scanning
|
|
|
|
void CRenderer::CleanupScanning(void)
|
|
|
|
{
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_CLEANUP);
|
|
|
|
|
|
|
|
// for all active sectors
|
|
|
|
{FORDELETELIST(CBrushSector, bsc_lnInActiveSectors, re_lhActiveSectors, itbsc) {
|
|
|
|
// remove it from list
|
|
|
|
itbsc->bsc_lnInActiveSectors.Remove();
|
|
|
|
|
|
|
|
// for all polygons in sector
|
|
|
|
FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itpo) {
|
|
|
|
CBrushPolygon &bpo = *itpo;
|
|
|
|
// clear screen polygon pointers
|
|
|
|
bpo.bpo_pspoScreenPolygon = NULL;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
ASSERT(re_lhActiveSectors.IsEmpty());
|
|
|
|
|
|
|
|
// for all active brushes
|
|
|
|
{FORDELETELIST(CBrush3D, br_lnInActiveBrushes, re_lhActiveBrushes, itbr) {
|
|
|
|
// remove it from list
|
|
|
|
itbr->br_lnInActiveBrushes.Remove();
|
|
|
|
}}
|
|
|
|
ASSERT(re_lhActiveBrushes.IsEmpty());
|
|
|
|
|
|
|
|
// for all active terrains
|
|
|
|
{FORDELETELIST(CTerrain, tr_lnInActiveTerrains, re_lhActiveTerrains, ittr) {
|
|
|
|
// remove it from list
|
|
|
|
ittr->tr_lnInActiveTerrains.Remove();
|
|
|
|
}}
|
|
|
|
ASSERT(re_lhActiveTerrains.IsEmpty());
|
|
|
|
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_CLEANUP);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render active terrains
|
|
|
|
void CRenderer::RenderTerrains(void)
|
|
|
|
{
|
|
|
|
CAnyProjection3D *papr;
|
|
|
|
papr = &re_prProjection;
|
|
|
|
|
|
|
|
// for all active terrains
|
|
|
|
{FORDELETELIST(CTerrain, tr_lnInActiveTerrains, re_lhActiveTerrains, ittr) {
|
|
|
|
// render terrain
|
|
|
|
ittr->Render(*papr, re_pdpDrawPort);
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render active terrains in wireframe mode
|
|
|
|
void CRenderer::RenderWireFrameTerrains(void)
|
|
|
|
{
|
|
|
|
CAnyProjection3D *papr;
|
|
|
|
papr = &re_prProjection;
|
|
|
|
|
|
|
|
BOOL bShowEdges = _wrpWorldRenderPrefs.wrp_ftEdges != CWorldRenderPrefs::FT_NONE;
|
2016-04-24 01:01:37 +02:00
|
|
|
//BOOL bShowVertices = _wrpWorldRenderPrefs.wrp_ftVertices != CWorldRenderPrefs::FT_NONE;
|
2016-03-11 14:57:17 +01:00
|
|
|
// BOOL bForceRegenerate = _wrpWorldRenderPrefs.wrp_ftPolygons
|
|
|
|
|
|
|
|
COLOR colEdges = _wrpWorldRenderPrefs.wrp_colEdges;
|
2016-04-24 01:01:37 +02:00
|
|
|
//COLOR colVertices = 0xFF0000FF;
|
2016-03-11 14:57:17 +01:00
|
|
|
// for all active terrains
|
|
|
|
{FORDELETELIST(CTerrain, tr_lnInActiveTerrains, re_lhActiveTerrains, ittr) {
|
|
|
|
// render terrain
|
|
|
|
if(bShowEdges) {
|
|
|
|
ittr->RenderWireFrame(*papr, re_pdpDrawPort,colEdges);
|
|
|
|
}
|
2016-04-24 01:01:37 +02:00
|
|
|
/*if(bShowVertices) {
|
2016-03-11 14:57:17 +01:00
|
|
|
//ittr->RenderVertices(*papr, re_pdpDrawPort,colVertices);
|
2016-04-24 01:01:37 +02:00
|
|
|
}*/
|
2016-03-11 14:57:17 +01:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
// draw the prepared things to screen
|
|
|
|
void CRenderer::DrawToScreen(void)
|
|
|
|
{
|
|
|
|
ChangeStatsMode(CStatForm::STI_WORLDRENDERING);
|
|
|
|
|
|
|
|
//------------------------------------------------- first render background
|
|
|
|
// if polygons should be drawn
|
|
|
|
if (!re_bRenderingShadows &&
|
|
|
|
_wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE) {
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERSCENE);
|
|
|
|
if( re_bBackgroundEnabled) {
|
|
|
|
// render the polygons to screen
|
|
|
|
CPerspectiveProjection3D *pprPerspective =
|
|
|
|
(CPerspectiveProjection3D *)(CProjection3D *)(re_prBackgroundProjection);
|
|
|
|
pprPerspective->Prepare();
|
|
|
|
RenderScene( re_pdpDrawPort, re_pspoFirstBackground, re_prBackgroundProjection, re_colSelection, FALSE);
|
|
|
|
} else {
|
|
|
|
// this is just for far sentinel
|
|
|
|
RenderSceneBackground( re_pdpDrawPort, re_spoFarSentinel.spo_spoScenePolygon.spo_cColor);
|
|
|
|
}
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_RENDERSCENE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (re_bBackgroundEnabled) {
|
|
|
|
// render models that were kept for delayed rendering.
|
|
|
|
ChangeStatsMode(CStatForm::STI_MODELSETUP);
|
|
|
|
RenderModels(TRUE); // render background models
|
|
|
|
ChangeStatsMode(CStatForm::STI_WORLDRENDERING);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if polygons should be drawn
|
|
|
|
if (!re_bRenderingShadows &&
|
|
|
|
re_bBackgroundEnabled
|
|
|
|
&&_wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE) {
|
|
|
|
// render translucent portals
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERSCENE);
|
2016-04-24 01:01:37 +02:00
|
|
|
//CPerspectiveProjection3D *pprPerspective = (CPerspectiveProjection3D*)(CProjection3D*)(re_prBackgroundProjection);
|
2016-03-11 14:57:17 +01:00
|
|
|
RenderScene( re_pdpDrawPort, SortTranslucentPolygons(re_pspoFirstBackgroundTranslucent),
|
|
|
|
re_prBackgroundProjection, re_colSelection, TRUE);
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_RENDERSCENE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( re_bBackgroundEnabled) {
|
|
|
|
ChangeStatsMode(CStatForm::STI_PARTICLERENDERING);
|
2016-03-29 03:03:54 +02:00
|
|
|
RenderParticles(TRUE); // render background particles
|
2016-03-11 14:57:17 +01:00
|
|
|
ChangeStatsMode(CStatForm::STI_WORLDRENDERING);
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------- second render non-background
|
|
|
|
// if polygons should be drawn
|
|
|
|
if( !re_bRenderingShadows
|
|
|
|
&& _wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE) {
|
|
|
|
// render the spans to screen
|
|
|
|
re_prProjection->Prepare();
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERSCENE);
|
2016-04-24 01:01:37 +02:00
|
|
|
//CPerspectiveProjection3D *pprPerspective = (CPerspectiveProjection3D*)(CProjection3D*)re_prProjection;
|
2016-03-11 14:57:17 +01:00
|
|
|
RenderScene( re_pdpDrawPort, re_pspoFirst, re_prProjection, re_colSelection, FALSE);
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_RENDERSCENE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render active terrains
|
|
|
|
if( !re_bRenderingShadows
|
|
|
|
&& _wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE) {
|
|
|
|
RenderTerrains();
|
|
|
|
}
|
|
|
|
|
|
|
|
// if wireframe should be drawn
|
|
|
|
if( !re_bRenderingShadows &&
|
|
|
|
( _wrpWorldRenderPrefs.wrp_ftEdges != CWorldRenderPrefs::FT_NONE
|
|
|
|
|| _wrpWorldRenderPrefs.wrp_ftVertices != CWorldRenderPrefs::FT_NONE
|
|
|
|
|| _wrpWorldRenderPrefs.wrp_stSelection == CWorldRenderPrefs::ST_VERTICES
|
|
|
|
|| _wrpWorldRenderPrefs.IsFieldBrushesOn())) {
|
|
|
|
// render in wireframe all brushes that were added (in orthographic projection!)
|
|
|
|
re_pdpDrawPort->SetOrtho();
|
|
|
|
RenderWireFrameBrushes();
|
|
|
|
RenderWireFrameTerrains();
|
|
|
|
}
|
|
|
|
|
|
|
|
// render models that were kept for delayed rendering
|
|
|
|
ChangeStatsMode(CStatForm::STI_MODELSETUP);
|
|
|
|
RenderModels(FALSE); // render non-background models
|
|
|
|
ChangeStatsMode(CStatForm::STI_PARTICLERENDERING);
|
|
|
|
RenderParticles(FALSE); // render non-background particles
|
|
|
|
ChangeStatsMode(CStatForm::STI_WORLDRENDERING);
|
|
|
|
|
|
|
|
// if polygons should be drawn
|
|
|
|
if (!re_bRenderingShadows
|
|
|
|
&&_wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE) {
|
|
|
|
// render translucent portals
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERSCENE);
|
|
|
|
CPerspectiveProjection3D *pprPerspective = (CPerspectiveProjection3D*)(CProjection3D*)re_prProjection;
|
|
|
|
pprPerspective->Prepare();
|
|
|
|
RenderScene( re_pdpDrawPort, SortTranslucentPolygons(re_pspoFirstTranslucent),
|
|
|
|
re_prProjection, re_colSelection, TRUE);
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_RENDERSCENE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// render lens flares
|
|
|
|
if( !re_bRenderingShadows) {
|
|
|
|
ChangeStatsMode(CStatForm::STI_FLARESRENDERING);
|
|
|
|
RenderLensFlares(); // (this also sets orthographic projection!)
|
|
|
|
ChangeStatsMode(CStatForm::STI_WORLDRENDERING);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if entity targets should be drawn
|
|
|
|
if( !re_bRenderingShadows && _wrpWorldRenderPrefs.wrp_bShowTargetsOn) {
|
|
|
|
// render entity targets
|
|
|
|
RenderEntityTargets();
|
|
|
|
}
|
|
|
|
|
|
|
|
// if entity targets should be drawn
|
|
|
|
if( !re_bRenderingShadows && _wrpWorldRenderPrefs.wrp_bShowEntityNames) {
|
|
|
|
RenderEntityNames();
|
|
|
|
}
|
|
|
|
|
|
|
|
// clean all buffers after rendering
|
|
|
|
re_aspoScreenPolygons.PopAll();
|
|
|
|
re_admDelayedModels.PopAll();
|
|
|
|
re_cenDrawn.PopAll();
|
|
|
|
re_avvxViewVertices.PopAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// draw mirror polygons to z-buffer to enable drawing of mirror
|
|
|
|
void CRenderer::FillMirrorDepth(CMirror &mi)
|
|
|
|
{
|
|
|
|
// create a list of scene polygons for mirror
|
|
|
|
ScenePolygon *pspoFirst = NULL;
|
|
|
|
// for each polygon
|
|
|
|
FOREACHINDYNAMICCONTAINER(mi.mi_cspoPolygons, CScreenPolygon, itspo) {
|
|
|
|
CScreenPolygon &spo = *itspo;
|
2016-04-24 01:01:37 +02:00
|
|
|
//CBrushPolygon &bpo = *spo.spo_pbpoBrushPolygon;
|
2016-03-11 14:57:17 +01:00
|
|
|
// create a new screen polygon
|
|
|
|
CScreenPolygon &spoNew = re_aspoScreenPolygons.Push();
|
|
|
|
ScenePolygon &sppoNew = spoNew.spo_spoScenePolygon;
|
|
|
|
// add it to mirror list
|
|
|
|
sppoNew.spo_pspoSucc = pspoFirst;
|
|
|
|
pspoFirst = &sppoNew;
|
|
|
|
|
|
|
|
// use same triangles
|
|
|
|
sppoNew.spo_iVtx0 = spo.spo_spoScenePolygon.spo_iVtx0;
|
|
|
|
sppoNew.spo_ctVtx = spo.spo_spoScenePolygon.spo_ctVtx;
|
|
|
|
sppoNew.spo_piElements = spo.spo_spoScenePolygon.spo_piElements;
|
|
|
|
sppoNew.spo_ctElements = spo.spo_spoScenePolygon.spo_ctElements;
|
|
|
|
}
|
|
|
|
|
|
|
|
// render all those polygons just to clear z-buffer
|
|
|
|
RenderSceneZOnly( re_pdpDrawPort, pspoFirst, re_prProjection);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// do the rendering
|
|
|
|
void CRenderer::Render(void)
|
|
|
|
{
|
|
|
|
// if the world doesn't have all portal-sector links updated
|
|
|
|
if( !re_pwoWorld->wo_bPortalLinksUpToDate) {
|
|
|
|
// update the links
|
|
|
|
CSetFPUPrecision FPUPrecision(FPT_53BIT);
|
|
|
|
re_pwoWorld->wo_baBrushes.LinkPortalsAndSectors();
|
|
|
|
re_pwoWorld->wo_bPortalLinksUpToDate = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
StartStatsMode(CStatForm::STI_WORLDTRANSFORM);
|
|
|
|
_pfRenderProfile.IncrementAveragingCounter();
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERING);
|
|
|
|
|
|
|
|
// set FPU to single precision while rendering
|
|
|
|
CSetFPUPrecision FPUPrecision(FPT_24BIT);
|
|
|
|
|
|
|
|
// initialize all rendering structures
|
|
|
|
Initialize();
|
|
|
|
// init select-on-render functionality if not rendering shadows
|
|
|
|
extern void InitSelectOnRender( PIX pixSizeI, PIX pixSizeJ);
|
|
|
|
if( re_pdpDrawPort!=NULL) InitSelectOnRender( re_pdpDrawPort->GetWidth(), re_pdpDrawPort->GetHeight());
|
|
|
|
// add initial sectors to active lists
|
|
|
|
AddInitialSectors();
|
|
|
|
// scan through portals for other sectors
|
|
|
|
ScanForOtherSectors();
|
|
|
|
|
|
|
|
// force finishing of all OpenGL pending operations, if required
|
|
|
|
ChangeStatsMode(CStatForm::STI_SWAPBUFFERS);
|
2016-04-07 05:16:30 +02:00
|
|
|
extern INDEX ogl_iFinish; ogl_iFinish = Clamp( ogl_iFinish, 0, 3);
|
|
|
|
extern INDEX d3d_iFinish; d3d_iFinish = Clamp( d3d_iFinish, 0, 3);
|
2016-03-11 14:57:17 +01:00
|
|
|
if( (ogl_iFinish==1 && _pGfx->gl_eCurrentAPI==GAT_OGL)
|
|
|
|
#ifdef SE1_D3D
|
|
|
|
|| (d3d_iFinish==1 && _pGfx->gl_eCurrentAPI==GAT_D3D)
|
|
|
|
#endif // SE1_D3D
|
|
|
|
)
|
|
|
|
gfxFinish();
|
|
|
|
|
|
|
|
// check any eventual delayed depth points outside the mirror (if API and time allows)
|
|
|
|
if( !re_bRenderingShadows && re_iIndex==0) {
|
|
|
|
// OpenGL allows us to check z-buffer from previous frame - cool deal!
|
|
|
|
// Direct3D is, of course, totally different story. :(
|
|
|
|
if( _pGfx->gl_eCurrentAPI==GAT_OGL || d3d_bAlternateDepthReads) {
|
|
|
|
ChangeStatsMode(CStatForm::STI_FLARESRENDERING);
|
|
|
|
extern void CheckDelayedDepthPoints( const CDrawPort *pdp, INDEX iMirrorLevel=0);
|
|
|
|
CheckDelayedDepthPoints(re_pdpDrawPort);
|
|
|
|
}
|
|
|
|
// in 1st pass - mirrors are not drawn
|
|
|
|
_bMirrorDrawn = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if may render one more mirror recursion
|
|
|
|
ChangeStatsMode(CStatForm::STI_WORLDTRANSFORM);
|
|
|
|
if( !re_bRenderingShadows
|
|
|
|
&& re_prProjection.IsPerspective()
|
|
|
|
&& re_iIndex<MAX_RENDERERS-1
|
|
|
|
&& re_amiMirrors.Count()>0
|
|
|
|
&& !re_pdpDrawPort->IsOverlappedRendering())
|
|
|
|
{
|
|
|
|
// cleanup after scanning
|
|
|
|
CleanupScanning();
|
|
|
|
|
|
|
|
// take next renderer
|
|
|
|
CRenderer &re = _areRenderers[re_iIndex+1];
|
|
|
|
// for each mirror
|
|
|
|
for( INDEX i=0; i<re_amiMirrors.Count(); i++)
|
|
|
|
{
|
|
|
|
// skip invalid mirrors
|
|
|
|
CMirror &mi = re_amiMirrors[i];
|
|
|
|
if( mi.mi_iMirrorType<0) continue;
|
|
|
|
|
|
|
|
// calculate all needed data for the mirror
|
|
|
|
mi.FinishAdding();
|
|
|
|
// skip mirror that has no significant area
|
|
|
|
if( mi.mi_fpixMaxPolygonArea<5) continue;
|
|
|
|
|
|
|
|
// expand mirror in each direction, but keep it inside drawport
|
|
|
|
PIX pixDPSizeI = re_pdpDrawPort->GetWidth();
|
|
|
|
PIX pixDPSizeJ = re_pdpDrawPort->GetHeight();
|
|
|
|
mi.mi_boxOnScreen.Expand(1);
|
|
|
|
mi.mi_boxOnScreen &= PIXaabbox2D( PIX2D(0,0), PIX2D(pixDPSizeI,pixDPSizeJ));
|
|
|
|
|
|
|
|
// get drawport and mirror coordinates
|
|
|
|
PIX pixMirrorMinI = mi.mi_boxOnScreen.Min()(1);
|
|
|
|
PIX pixMirrorMinJ = mi.mi_boxOnScreen.Min()(2);
|
|
|
|
PIX pixMirrorMaxI = mi.mi_boxOnScreen.Max()(1);
|
|
|
|
PIX pixMirrorMaxJ = mi.mi_boxOnScreen.Max()(2);
|
|
|
|
|
|
|
|
// calculate mirror size
|
|
|
|
PIX pixMirrorSizeI = pixMirrorMaxI-pixMirrorMinI;
|
|
|
|
PIX pixMirrorSizeJ = pixMirrorMaxJ-pixMirrorMinJ;
|
|
|
|
// clone drawport (must specify doubles here, to keep the precision)
|
|
|
|
re_pdpDrawPort->Unlock();
|
|
|
|
CDrawPort dpMirror( re_pdpDrawPort, pixMirrorMinI /(DOUBLE)pixDPSizeI, pixMirrorMinJ /(DOUBLE)pixDPSizeJ,
|
|
|
|
pixMirrorSizeI/(DOUBLE)pixDPSizeI, pixMirrorSizeJ/(DOUBLE)pixDPSizeJ);
|
|
|
|
// skip if cannot be locked
|
|
|
|
if( !dpMirror.Lock()) {
|
|
|
|
// lock back the original drawport
|
|
|
|
re_pdpDrawPort->Lock();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// recalculate mirror size to compensate for possible lost precision
|
|
|
|
pixMirrorMinI = dpMirror.dp_MinI - re_pdpDrawPort->dp_MinI;
|
|
|
|
pixMirrorMinJ = dpMirror.dp_MinJ - re_pdpDrawPort->dp_MinJ;
|
|
|
|
pixMirrorMaxI = dpMirror.dp_MaxI - re_pdpDrawPort->dp_MinI +1;
|
|
|
|
pixMirrorMaxJ = dpMirror.dp_MaxJ - re_pdpDrawPort->dp_MinJ +1;
|
|
|
|
pixMirrorSizeI = pixMirrorMaxI-pixMirrorMinI;
|
|
|
|
pixMirrorSizeJ = pixMirrorMaxJ-pixMirrorMinJ;
|
|
|
|
ASSERT( pixMirrorSizeI==dpMirror.dp_Width && pixMirrorSizeJ==dpMirror.dp_Height);
|
|
|
|
|
|
|
|
// set it up for rendering
|
|
|
|
re.re_pwoWorld = re_pwoWorld;
|
|
|
|
re.re_prProjection = re_prProjection;
|
|
|
|
re.re_pdpDrawPort = &dpMirror;
|
|
|
|
// initialize clipping rectangle around the mirror size
|
|
|
|
re.InitClippingRectangle( 0, 0, pixMirrorSizeI, pixMirrorSizeJ);
|
|
|
|
// setup projection to use the mirror drawport and keep same perspective as before
|
|
|
|
re.re_prProjection->ScreenBBoxL() = FLOATaabbox2D( FLOAT2D(0,0), FLOAT2D(pixDPSizeI, pixDPSizeJ));
|
|
|
|
((CPerspectiveProjection3D&)(*re.re_prProjection)).ppr_boxSubScreen =
|
|
|
|
FLOATaabbox2D( FLOAT2D(pixMirrorMinI, pixMirrorMinJ), FLOAT2D(pixMirrorMaxI, pixMirrorMaxJ));
|
|
|
|
|
|
|
|
// warp?
|
|
|
|
if( mi.mi_mp.mp_ulFlags&MPF_WARP) {
|
|
|
|
// warp clip plane is parallel to view plane and contains the closest point
|
|
|
|
re.re_penViewer = mi.mi_mp.mp_penWarpViewer;
|
|
|
|
re.re_pcspoViewPolygons = NULL;
|
|
|
|
re.re_prProjection->WarpPlaneL() = FLOATplane3D(FLOAT3D(0,0,-1), mi.mi_vClosest);
|
|
|
|
// create new viewer placement
|
|
|
|
CPlacement3D pl = re.re_prProjection->ViewerPlacementR();
|
|
|
|
FLOATmatrix3D m;
|
|
|
|
MakeRotationMatrixFast(m, pl.pl_OrientationAngle);
|
|
|
|
pl.AbsoluteToRelativeSmooth(mi.mi_mp.mp_plWarpIn);
|
|
|
|
pl.RelativeToAbsoluteSmooth(mi.mi_mp.mp_plWarpOut);
|
|
|
|
re.re_prProjection->ViewerPlacementL() = pl;
|
|
|
|
if (re.re_prProjection.IsPerspective() && mi.mi_mp.mp_fWarpFOV>=1 && mi.mi_mp.mp_fWarpFOV<=170) {
|
|
|
|
((CPerspectiveProjection3D&)*re.re_prProjection).FOVL() = mi.mi_mp.mp_fWarpFOV;
|
|
|
|
}
|
|
|
|
// mirror!
|
|
|
|
} else {
|
|
|
|
re.re_penViewer = NULL;
|
|
|
|
re.re_pcspoViewPolygons = &mi.mi_cspoPolygons;
|
|
|
|
re.re_prProjection->MirrorPlaneL() = mi.mi_plPlane;
|
|
|
|
re.re_prProjection->MirrorPlaneL().Offset(0.05f); // move projection towards mirror a bit, to avoid cracks
|
|
|
|
}
|
|
|
|
re.re_bRenderingShadows = FALSE;
|
|
|
|
re.re_ubLightIllumination = 0;
|
|
|
|
|
|
|
|
// just flat-fill if mirrors are disabled
|
|
|
|
extern INDEX wld_bRenderMirrors;
|
|
|
|
if( !wld_bRenderMirrors) dpMirror.Fill(C_GRAY|CT_OPAQUE);
|
|
|
|
else {
|
|
|
|
// render the view inside mirror
|
|
|
|
StopStatsMode();
|
|
|
|
_pfRenderProfile.StopTimer(CRenderProfile::PTI_RENDERING);
|
|
|
|
re.Render();
|
|
|
|
_pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERING);
|
|
|
|
StartStatsMode(CStatForm::STI_WORLDTRANSFORM);
|
|
|
|
}
|
|
|
|
// unlock mirror's and lock back the original drawport
|
|
|
|
dpMirror.Unlock();
|
|
|
|
re_pdpDrawPort->Lock();
|
|
|
|
// clear entire buffer to back value
|
|
|
|
re_pdpDrawPort->FillZBuffer(ZBUF_BACK);
|
|
|
|
// fill depth buffer of the mirror, so that scene cannot be drawn through it
|
|
|
|
FillMirrorDepth(mi);
|
|
|
|
_bMirrorDrawn = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// flush all mirrors
|
|
|
|
re_amiMirrors.PopAll();
|
|
|
|
|
|
|
|
// fill z-buffer only if no mirrors have been drawn, not rendering second layer in world editor and not in wireframe mode
|
|
|
|
if( !_bMirrorDrawn
|
|
|
|
&& !re_pdpDrawPort->IsOverlappedRendering()
|
|
|
|
&& _wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE) {
|
|
|
|
re_pdpDrawPort->FillZBuffer(ZBUF_BACK);
|
|
|
|
}
|
|
|
|
// draw the prepared things to screen
|
|
|
|
DrawToScreen();
|
|
|
|
}
|
|
|
|
|
|
|
|
// no mirrors
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if rendering a mirror
|
|
|
|
// or not rendering second layer in world editor
|
|
|
|
// and not in wireframe mode
|
|
|
|
if( re_iIndex>0
|
2016-04-23 20:25:08 +02:00
|
|
|
|| (!re_bRenderingShadows
|
|
|
|
&& !re_pdpDrawPort->IsOverlappedRendering()
|
|
|
|
&& _wrpWorldRenderPrefs.wrp_ftPolygons != CWorldRenderPrefs::FT_NONE)) {
|
2016-03-11 14:57:17 +01:00
|
|
|
re_pdpDrawPort->FillZBuffer(ZBUF_BACK);
|
|
|
|
}
|
|
|
|
// draw the prepared things to screen and finish
|
|
|
|
DrawToScreen();
|
|
|
|
CleanupScanning();
|
|
|
|
}
|
|
|
|
|
|
|
|
// disable fog/haze
|
|
|
|
StopFog();
|
|
|
|
StopHaze();
|
|
|
|
// reset vertex arrays if this is the last renderer
|
|
|
|
if( re_iIndex==0) _avtxScene.PopAll();
|
|
|
|
|
|
|
|
// for D3D (or mirror) we have to check depth points now, because we need back (not depth!) buffer for it,
|
|
|
|
// and D3D can't guarantee that it won't be discarded upon swapbuffers (especially if multisampling is on!) :(
|
|
|
|
#ifdef SE1_D3D
|
|
|
|
if( !re_bRenderingShadows && ((_pGfx->gl_eCurrentAPI==GAT_D3D && !d3d_bAlternateDepthReads) || re_iIndex>0)) {
|
|
|
|
extern void CheckDelayedDepthPoints( const CDrawPort *pdp, INDEX iMirrorLevel=0);
|
|
|
|
CheckDelayedDepthPoints( re_pdpDrawPort, re_iIndex);
|
|
|
|
}
|
|
|
|
#endif // SE1_D3D
|
|
|
|
|
|
|
|
// end select-on-render functionality
|
|
|
|
extern void EndSelectOnRender(void);
|
|
|
|
EndSelectOnRender();
|
|
|
|
|
|
|
|
// assure that FPU precision was low all the rendering time
|
|
|
|
ASSERT( GetFPUPrecision()==FPT_24BIT);
|
|
|
|
StopStatsMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Constructor.
|
|
|
|
*/
|
|
|
|
CRenderer::CRenderer(void)
|
|
|
|
{
|
|
|
|
// setup self index
|
|
|
|
INDEX i = this-_areRenderers;
|
|
|
|
ASSERT(i>=0 && i<MAX_RENDERERS);
|
|
|
|
re_iIndex = i;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Destructor.
|
|
|
|
*/
|
|
|
|
CRenderer::~CRenderer(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize clipping rectangle
|
|
|
|
void CRenderer::InitClippingRectangle(PIX pixMinI, PIX pixMinJ, PIX pixSizeI, PIX pixSizeJ)
|
|
|
|
{
|
|
|
|
re_pspoFirst = NULL;
|
|
|
|
re_pspoFirstTranslucent = NULL;
|
|
|
|
re_pspoFirstBackground = NULL;
|
|
|
|
re_pspoFirstBackgroundTranslucent = NULL;
|
|
|
|
|
|
|
|
re_fMinJ = (FLOAT) pixMinJ;
|
|
|
|
re_fMaxJ = (FLOAT) pixSizeJ+pixMinJ;
|
|
|
|
re_pixSizeI = pixSizeI;
|
|
|
|
re_fbbClipBox =
|
|
|
|
FLOATaabbox2D( FLOAT2D((FLOAT) pixMinI+CLIPMARGADD,
|
|
|
|
(FLOAT) pixMinJ+CLIPMARGADD),
|
|
|
|
FLOAT2D((FLOAT) pixMinI+pixSizeI-CLIPMARGSUB,
|
|
|
|
(FLOAT) pixMinJ+pixSizeJ-CLIPMARGSUB));
|
|
|
|
re_pixTopScanLineJ = PIXCoord(pixMinJ+CLIPMARGADD);
|
|
|
|
re_ctScanLines =
|
|
|
|
PIXCoord(pixSizeJ-CLIPMARGSUB) - PIXCoord(CLIPMARGADD)/* +1*/;
|
|
|
|
re_pixBottomScanLineJ = re_pixTopScanLineJ+re_ctScanLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
// render a 3D view to a drawport
|
|
|
|
void RenderView(CWorld &woWorld, CEntity &enViewer,
|
|
|
|
CAnyProjection3D &prProjection, CDrawPort &dpDrawport)
|
|
|
|
{
|
|
|
|
// let the worldbase execute its render function
|
|
|
|
if (woWorld.wo_pecWorldBaseClass!=NULL
|
|
|
|
&&woWorld.wo_pecWorldBaseClass->ec_pdecDLLClass!=NULL
|
|
|
|
&&woWorld.wo_pecWorldBaseClass->ec_pdecDLLClass->dec_OnWorldRender!=NULL) {
|
|
|
|
woWorld.wo_pecWorldBaseClass->ec_pdecDLLClass->dec_OnWorldRender(&woWorld);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_wrpWorldRenderPrefs.GetShadowsType() == CWorldRenderPrefs::SHT_FULL)
|
|
|
|
{
|
|
|
|
// calculate all non directional shadows that are not up to date
|
|
|
|
woWorld.CalculateNonDirectionalShadows();
|
|
|
|
}
|
|
|
|
|
|
|
|
// take first renderer object
|
|
|
|
CRenderer &re = _areRenderers[0];
|
|
|
|
// set it up for rendering
|
|
|
|
re.re_penViewer = &enViewer;
|
|
|
|
re.re_pcspoViewPolygons = NULL;
|
|
|
|
re.re_pwoWorld = &woWorld;
|
|
|
|
re.re_prProjection = prProjection;
|
|
|
|
re.re_pdpDrawPort = &dpDrawport;
|
|
|
|
// initialize clipping rectangle around the drawport
|
|
|
|
re.InitClippingRectangle(0, 0, dpDrawport.GetWidth(), dpDrawport.GetHeight());
|
|
|
|
prProjection->ScreenBBoxL() = FLOATaabbox2D(
|
|
|
|
FLOAT2D(0.0f, 0.0f),
|
|
|
|
FLOAT2D((float)dpDrawport.GetWidth(), (float)dpDrawport.GetHeight())
|
|
|
|
);
|
|
|
|
re.re_bRenderingShadows = FALSE;
|
|
|
|
re.re_ubLightIllumination = 0;
|
|
|
|
|
|
|
|
// render the view (with eventuall t-buffer effect)
|
|
|
|
extern void SetTBufferEffect( BOOL bEnable);
|
|
|
|
SetTBufferEffect(TRUE);
|
|
|
|
re.Render();
|
|
|
|
SetTBufferEffect(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Render a world with some viewer, projection and drawport. (viewer may be NULL)
|
|
|
|
// internal version used for rendering shadows
|
|
|
|
ULONG RenderShadows(CWorld &woWorld, CEntity &enViewer,
|
|
|
|
CAnyProjection3D &prProjection, const FLOATaabbox3D &boxViewer,
|
|
|
|
UBYTE *pubShadowMask, SLONG slShadowWidth, SLONG slShadowHeight,
|
|
|
|
UBYTE ubIllumination)
|
|
|
|
{
|
|
|
|
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_RENDERSHADOWS);
|
|
|
|
|
|
|
|
// take a renderer object
|
|
|
|
CRenderer &re = _areRenderers[0];
|
|
|
|
// set it up for rendering
|
|
|
|
re.re_penViewer = &enViewer;
|
|
|
|
re.re_pcspoViewPolygons = NULL;
|
|
|
|
re.re_pwoWorld = &woWorld;
|
|
|
|
re.re_prProjection = prProjection;
|
|
|
|
re.re_pdpDrawPort = NULL;
|
|
|
|
re.re_boxViewer = boxViewer;
|
|
|
|
// initialize clipping rectangle around the drawport
|
|
|
|
const FLOATaabbox2D &box = prProjection->ScreenBBoxR();
|
|
|
|
//re.InitClippingRectangle(box.Min()(1), box.Min()(2), box.Size()(1), box.Size()(2));
|
2016-03-29 03:03:54 +02:00
|
|
|
re.InitClippingRectangle(0, 0, (PIX) box.Size()(1), (PIX) box.Size()(2));
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
re.re_bRenderingShadows = TRUE;
|
|
|
|
re.re_bDirectionalShadows = prProjection.IsParallel();
|
|
|
|
re.re_bSomeLightExists = FALSE;
|
|
|
|
re.re_bSomeDarkExists = FALSE;
|
|
|
|
_bSomeDarkExists = FALSE;
|
|
|
|
re.re_pubShadow = pubShadowMask;
|
|
|
|
re.re_slShadowWidth = slShadowWidth;
|
|
|
|
re.re_slShadowHeight = slShadowHeight;
|
|
|
|
re.re_ubLightIllumination = ubIllumination;
|
|
|
|
// render the view
|
|
|
|
re.Render();
|
|
|
|
|
|
|
|
ULONG ulFlags = 0;
|
|
|
|
if (!re.re_bSomeLightExists) {
|
|
|
|
ulFlags|=BSLF_ALLDARK;
|
|
|
|
}
|
|
|
|
if (!(re.re_bSomeDarkExists|_bSomeDarkExists)) {
|
|
|
|
ulFlags|=BSLF_ALLLIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_RENDERSHADOWS);
|
|
|
|
|
|
|
|
return ulFlags;
|
|
|
|
}
|