1
0
mirror of https://github.com/OpenTrespasser/JurassicParkTrespasser.git synced 2024-12-23 00:51:57 +00:00
JurassicParkTrespasser/jp2_pc/Source/Lib/Renderer/ScreenRender.cpp
2018-01-01 23:07:24 +01:00

1731 lines
49 KiB
C++

/***********************************************************************************************
*
* Copyright © DreamWorks Interactive. 1996
*
* Implementation of ScreenRender.hpp
*
***********************************************************************************************
*
* $Log:: /JP2_PC/Source/Lib/Renderer/ScreenRender.cpp $
*
* 148 98.09.24 1:40a Mmouni
* Shrunk CRenderPolygon by a large amount.
* Multiple list depth sort stuff is now on a compile switch.
*
* 147 98.09.19 12:39a Mmouni
* Added "forceinline" to many small inline functions.
* Inlined some functions for speed.
*
* 146 9/15/98 4:19p Pkeet
* Reduced problem with objects flashing white after loading a new level.
*
* 145 98.09.10 4:08p Mmouni
* Removed area culling since polygons are already culled if they do not light any pixels.
* Disabled turing off texturing for bump map polys since they do not get lighting values that
* are useable without the bump map.
*
* 144 8/18/98 8:47p Pkeet
* Removed the 'bFIX_ADJACENT_CRACKS' macro.
*
* 143 8/18/98 3:49p Pkeet
* Hardware mip levels now take into account the recommended maximum dimension.
*
* 142 8/12/98 6:57p Pkeet
* Implemented a trial fix for terrain cracking.
*
* 141 8/04/98 6:16p Pkeet
* Made 'ReduceFeatures' handle hardware polygons correctly.
*
* 140 8/03/98 8:53p Agrant
* bogus assert fixed per Paul
*
* 139 8/03/98 11:05a Pkeet
* Commented out asserts.
*
* 138 8/02/98 8:37p Pkeet
* Added the 'SetHardwareOut' member function.
*
* 137 98.08.02 7:26p Mmouni
* Changed default alt. perspective error to 2.0
*
* 136 8/01/98 4:42p Pkeet
* Added the 'pixSetBackground' member function.
*
* 135 7/30/98 4:10p Pkeet
* Large textures are disabled in hardware if texture create times are slow.
*
* 134 98.07.30 11:43a Mmouni
* Switched to area based mip-mapping.
*
* 133 7/29/98 2:24p Pkeet
* Disabled feature reduction for hardware.
*
* 132 7/29/98 1:57p Pkeet
* Added a parameter to the 'SetMipLevel' member function of 'CRenderPolygon' to reduce the
* burden on the VM loader by making VM requests only when the Direct3D version is required and
* is present.
*
* 131 7/28/98 8:26p Pkeet
* Added the member function 'Grow' to 'CRenderPolygon,' but did not implement any code for it.
*
* 130 98.07.24 8:47p Mmouni
* Added Gouraud shading threshold area.
*
* 129 7/23/98 6:24p Pkeet
* Added support for texturing in hardware out of system memory.
*
* 128 6/24/98 3:21p Rwyatt
* Now longer check if all VM is commited before checking available memory. This is now part of
* the VM.
*
* 127 6/21/98 8:01p Pkeet
* Polygons affected by feature reduction now have their hardware flags reset. This prevents a
* nasty 'flashing' bug.
*
* 126 98.06.18 3:55p Mmouni
* Added software alpha switch.
*
* 125 6/16/98 7:28p Pkeet
* Maded 'bCalculateScreenExtents' return false only if the polygon is not terrain.
*
* 124 6/15/98 7:16p Pkeet
* Fixed bug that caused small terrain polygons to dissappear.
*
* 123 6/14/98 2:48p Pkeet
* Added the 'fGetFarthestZ' member function.
*
* 122 6/09/98 3:52p Pkeet
* Added a flag to 'SetD3DFlagForPolygons' to use Direct3D or not.
*
* 121 6/08/98 8:08p Pkeet
* Modified the 'SetD3DFlagForPolygons' parameters to work in the pipeline.
*
* 120 6/02/98 11:12a Pkeet
* Added the 'SetD3DFlagForPolygons' member function.
*
* 119 98.05.23 8:10p Mmouni
* Made bi-linear filter default to on in K63D build.
*
* 118 98.05.21 11:31p Mmouni
* Added validate function to CRenderPolygon.
*
* 117 4/28/98 4:59p Rwyatt
* When checking the availability of VM textures we now use the correct length.
*
* 116 98.04.24 6:59p Mmouni
* Changed alternate perspective correction setting.
*
* 115 4/21/98 3:28p Rwyatt
* Fixed merge problems
*
* 114 4/21/98 2:51p Rwyatt
* Added memory present and memory request functions to the mip selection code.
*
* 113 98.04.16 8:00p Mmouni
* Changed mip-map selection algorithm.
*
* 112 98.04.08 7:45p Mmouni
* Fixed a potential access violation reading a value off the end of the vertex array.
*
* 111 4/01/98 5:43p Pkeet
* Added the 'erfD3D_CACHE' render enumeration.
*
* 110 3/30/98 3:10p Agrant
* Placeholder code for mip selection based on raster availability.
*
* 109 98.03.24 8:16p Mmouni
* Made changes to support alternate perspective correction settings.
*
* 108 3/17/98 4:42p Pkeet
* Fixed culling of thin horizontal or vertical polygons.
*
* 107 3/17/98 3:23p Pkeet
* Fixed bug that caused horizontal gaps in the terrain.
*
* 106 98/03/02 21:42 Speter
* Combined SetAccept() into ReduceFeatures(). Do not disable texture for software terrain.
*
* 105 98/02/26 15:38 Speter
* Removed erfALPHA_SHADE and erfALPHA_TEXTURE.
*
* 104 98/02/26 13:52 Speter
* Moved seterfFeatures to CTexture from SSurface, removing redundant flags.
*
* 103 2/06/98 4:17p Agrant
* Turn on fogging by default
*
* 102 98.02.05 4:50p Mmouni
* Removed K6-3D inline assembly.
*
* 101 98.02.04 5:01p Mmouni
* Changes bInFrontOf() to use a looser in-front-of condition.
*
* 100 98/02/04 14:44 Speter
* Removed fGetRoughScreenArea().
*
* 99 98/01/30 15:55 Speter
* Moved transparent poly rejection into texture rejection block; shadows have erfTEXTURE off to
* start with.
*
* 98 1/26/98 3:50p Pkeet
* D3D renderer only renders flat-filled polygons if they originate from the terrain.
*
* 97 1/26/98 11:29a Pkeet
* Adjusted settings.
*
* 96 1/25/98 3:39p Pkeet
* Added the 'SetAccept' and 'ReduceFeatures' member functions.
*
* 95 1/24/98 4:42p Pkeet
* Fixed bug in screen area function.
*
* 94 1/24/98 3:18p Pkeet
* Added the use of screen area for feature reduction.
*
* 93 1/23/98 5:40p Pkeet
* Added the 'fArea' member variable and the 'SetArea' member function to the screen polygon
* class.
*
* 92 98.01.15 6:09p Mmouni
* Optimized mip-map selection.
*
* 91 1/07/98 6:22p Pkeet
* Tiny terrain polygons for a Direct3D target are no longer culled.
*
* 90 1/23/96 4:37p Pkeet
* Initializes the 'cvFace' data member to prevent later debug build asserts.
*
* 89 97/11/19 18:11 Speter
* Adjusted ValidatePolygon()
*
* 88 11/10/97 10:02p Gfavor
* Removed FEMMS's from esfSides (to higher level).
*
* 87 11/09/97 10:07p Gfavor
* Eliminated SetPlane3DX.
*
* 86 97/11/07 12:00p Pkeet
* Optimized the 'SetPlane' function.
*
* 84 11/05/97 11:09a Gfavor
* Fixed bInFrontOf to avoid generation of x87 code
*
* 83 11/05/97 11:02a Gfavor
* Eliminated bIntersect and modified 3DX version of esfSides.
*
* 82 10/29/97 8:09p Gfavor
* Converted esfSides to 3DX.
*
* 81 97/10/23 10:57a Pkeet
* Added a K6 3D switch.
*
* 80 10/13/97 8:16p Rwyatt
* Render settings have a new flag called bDrawSky. This controls if the sky is drawn or not. By
* default this flag is set to true
*
* 79 97/10/13 2:15p Pkeet
* Fixed bug with alpha.
*
* 78 97/10/12 21:51 Speter
* TReflectVal rvIntensity/rvFace changed to TClutVal cvIntensity/cvFace. Main gamma object
* moved to CClut.
*
* 77 97/10/01 4:40p Pkeet
* Added the 'erfOCCLUDE' flag.
*
* 76 97/09/10 11:35 Speter
* Modified asserts on lighting values.
*
* 75 8/18/97 2:49p Bbell
* Depth sorting demo stuff.
*
* 74 97/08/17 4:15p Pkeet
* Added the 'erfSOURCE_WATER' flag.
*
* 73 8/17/97 12:18a Agrant
* Put texture coord clamping on a VER switch.
*
* 72 97/08/15 12:08 Speter
* Moved ValidatePolygon and ValidateVertex to CScreenRender base class. Removed commented
* code. Now call ValidatePolygon for every polygon in DrawPolygons.
*
* 71 97/08/09 1:56p Pkeet
* Added flags for alpha blending.
*
* 70 97/08/08 5:50p Pkeet
* Fixed bug in mipmap calculations of delta u and delta v. Organized and commented code.
*
* 69 97/08/08 11:51a Pkeet
* Function now selects mip level. Added the render feature flag for mipmapping.
*
* 68 97/08/07 11:35a Pkeet
* Added interface support for mipmapping.
*
* 67 97/07/30 2:42p Pkeet
* Added support for separate tolerances.
*
* 66 97/07/29 6:31p Pkeet
* Added the 'fGetAverageZ' member function.
*
* 65 97/07/28 11:17a Pkeet
* Added the 'erfSOURCE_TERRAIN' and 'erfDRAW_CLIP' flags to the default settings constructor.
*
* 64 97/07/25 16:57 Speter
* SetPlane now uses points distributed around the polygon rather than first 3.
*
* 63 97/07/23 18:11 Speter
* Replaced CCamera argument in clipping functions with b_perspective flag. Again use vertex
* interpolation for screen coords rather than camera reprojection.
*
* 62 97/07/08 20:19 Speter
* Moved bHalfScanlines and bDoubleVertical here.
*
* 61 97/07/07 13:55 Speter
* Removed SRenderVertex.iX and .iY.
*
* 60 97/06/27 15:36 Speter
* Added erfTRAPEZOIDS feature.
*
* 59 97/06/25 1:14p Pkeet
* Includes code for displaying depth sort clipping.
*
* 58 97/06/23 20:32 Speter
* Made gcfScreen a static member of CScreenRender, avoiding per-CScreenRender calculation (e.g.
* in render caching).
*
* 57 97/06/17 2:03p Pkeet
* Removed the reference counting pointer from prasScreen.
*
* 56 6/16/97 3:00p Mlange
* Updated for fast float to integer conversion functions' name change.
*
* 55 97/06/11 12:30 Speter
* Restored b_cull_tiny param to bCalculateScreenExtents. Made SRenderVertex default
* constructor null.
*
* 54 97/06/10 15:43 Speter
* Added iX member to SRenderVertex. Now pass camera down to interpolation constructor.
* Changed CRenderPolygon::[b]InitializePolygonForDepthSort() to bCalculateScreenExtents(),
* which relies on values preset by pipeline.
*
* 53 97/06/03 18:52 Speter
* Added #include FloatDef.hpp.
*
* 52 97/06/02 15:55 Speter
* Put back camera projection for v3Screen only, to allow for parallel cameras.
*
* 51 97/05/24 3:56p Pkeet
* Added the 'InitializePolygonForDepthSort' member function. Utilized optimized integer
* conversion functions.
*
* 50 97/05/23 6:28p Pkeet
* Added asserts and the fast make call for setting the plane of the polygon.
*
* 49 97/05/23 17:46 Speter
* Bye bye SRenderTriangle. Merged into CRenderPolygon, removed some unused fields and
* constructors. Made FindBestSlope and BuildVertexArray member functions. Added
* fRoughScreenArea function.
*
* 48 97/05/22 3:59p Pkeet
* Made 'InitializePolygonForDepthSort' return a boolean value to indicate if the polygon is
* usable or has been culled.
*
* 47 97/05/21 4:16p Pkeet
* Added 'fPixelBuffer' tolerances.
*
* 46 97/05/21 10:40a Pkeet
* Uses the minimum tolerance of two polygons.
*
* 45 97/05/20 11:40a Pkeet
* Added variable plane tolerances for depth sorting.
*
* 44 97/05/17 4:34p Pkeet
* Further assembly optimizations.
*
* 43 97/05/17 3:38p Pkeet
* Improvement in the ASM and debugging of the non-asm plane side test.
*
* 42 97/05/15 7:20p Pkeet
* Fixed bug in ASM.
*
* 41 97/05/15 6:34p Pkeet
* Assembly optimization of plane intersection test.
*
* 40 97-05-08 13:19 Speter
* Moved initialisation of SBumpLighting member from SRenderTriangle, etc. to SBumpLighting
* constructor. Made SRenderTriangle constructor inline.
*
* 39 97/05/06 4:19p Pkeet
* Added a sorting key object.
*
* 38 97-05-06 16:12 Speter
* Replaced call to AddRenderDescDWI() with explicit add of CRenderDescDWI instance to array.
*
* 37 97/04/28 6:14p Pkeet
* Fixed conversion bug.
*
* 36 97/04/28 5:43p Pkeet
* Added the 'bBehind,' 'bInFrontOf' and 'bIntersect' member functions to CRenderPolygon. Moved
* plane tests here from the depth sort module.
*
* 35 97/04/25 2:51p Pkeet
* Changed the interpolating SRenderVertex constructor to use the camera projection method
* instead of the unproject/reproject method. This change results in less errors in the current
* implementation of the depth sort routine.
*
* 34 97-04-21 16:50 Speter
* Changed optcCam to v3Cam. In vertex interpolation, re-enabled direct v3Screen interpolation.
* Now check for v3Screen.tZ == 0 before doing v3Screen interpolation.
*
* 33 97/04/16 5:15p Pkeet
* Fixed assert problem in higher screen resolutions.
*
* 32 97/04/16 3:01p Pkeet
* Fixed scope bug in the interpolation constructor of SRenderVertex.
*
* 31 97-04-16 14:18 Speter
* Added correct screen vertex interpolation.
*
* 30 97/04/15 1:46p Pkeet
* Added improved polygon to triangle subdivision code.
*
* 29 97/04/14 11:56a Pkeet
* Changed 'MinInv' value to 'rMaxLevel.'
*
* 28 97/04/13 2:16p Pkeet
* Added the 'InitializePolygonForDepthSort' member function to CRenderPolygon. Added the global
* function 'SetCurrentCamera.' Reworked the SRenderVertex interpolation constructor used by the
* clipper to use a camera set by SetCurrentCamera. This is a temporary measure to overcome
* current bugs in the unproject/project code currently in the constructor. Changed the
* DrawPolygon function so that if the polygon is a triangle the contents of 'aprvVertices' will
* point to the same render vertices as 'paprvPolyVertices.' Also fixed bug in the
* retriangulization code.
*
* 27 97/04/10 11:33a Pkeet
* Added the 'iClipped' member variable to the polygon definition.
*
* 26 97-04-10 11:31 Speter
* Added v3Screen interpolation in interpolator constructor. Changed rvIntensity to use 2D
* interpolation.
*
* 25 97-04-09 16:23 Speter
* Added SRenderVertex interpolation constructor.
*
* 24 97/04/09 3:13p Pkeet
* Probable fix to probable bug in the naive polygon triangulation implementation. Added code
* for a polygon triangulation algorithm which should produce the best triangles for rendering.
*
* 23 97/04/09 12:20p Pkeet
* The 'DrawPolygon' member function now busts polygons up into triangles.
*
* 22 97/04/07 5:31p Pkeet
* Added the 'bMovedInList' member variable to CRenderPolygon.
*
* 21 97/04/07 2:28p Pkeet
* Added an assignment operator and copy constructor to CRenderPolygon that ensures the polygon
* uses the base class vertex array whenever appropriate.
*
* 20 97-04-04 12:38 Speter
* A big change: now pipeline uses CRenderPolygon rather than SRenderTriangle (currently always
* as a triangle). Changed associated variable names and comments.
*
* 19 97-03-28 17:21 Speter
* Removed erroneous symbol.
*
* 18 97-03-28 16:00 Speter
* Renamed SRenderSettings to CScreenRender::SSettings; moved bClip member to new
* CRenderer::Settings.
* Removed psrMain, replaced with prenMain.
*
* 17 97/02/25 12:51p Pkeet
* Added a switch for including and using Direct3D, and set the switch to 'false.'
*
* 16 97/02/21 15:20 Speter
* Moved prasScreen to CScreenRender from derived classes.
*
* 15 97/02/16 16:59 Speter
* Turn on specular by default.
*
* 14 97/02/13 4:22p Pkeet
* Added an init colour for the background. Set the default colour for the background to black.
*
* 13 97/02/13 14:18 Speter
* Incorporated new SBumpLighting struct into SRenderTriangle.
*
* 12 97/01/29 18:51 Speter
* Made SRenderTriangle default constructor noninline, as it wasn't being inlined.
*
* 11 97/01/20 18:32 Speter
* Added bClip to SRenderSettings.
*
* 10 97/01/20 11:50 Speter
* Moved pixBackground and gcfScreen vars from CScreenRender to derived classes. Added erfCOPY
* to default settings.
*
* 9 97/01/16 11:44 Speter
* Made SRenderSettings set the default state to reasonable values.
* Removed base class version of CScreenRender::UpdateSettings().
*
* 8 96/12/31 16:54 Speter
* Updated for rptr.
*
* 7 96/12/17 13:13 Speter
* Added gamma correction functionality.
* Moved psrMain global variable here from Render.cpp.
*
* 6 96/12/06 15:12 Speter
* Added SRenderSettings structure, and UpdateSettings() function for CScreenRender.
*
*
* 5 96/12/02 17:47 Speter
* Replaced ValidateRenderState() with CheckRenderState() and CorrectRenderState().
*
* 4 96/09/25 15:33 Speter
* Moved ValidateRenderState to .cpp file.
*
* 3 96/08/22 15:28 Speter
* Put back D3D support.
*
* 2 96/08/21 16:45 Speter
* Remove D3D support temporarily to sort out MFC link problem.
*
* 1 96/08/19 12:51 Speter
* First version, initialises the CArrayRenderDesc variable.
*
**********************************************************************************************/
#include "Common.hpp"
#include "Lib/W95/Direct3D.hpp"
#include "Lib/Sys/VirtualMem.hpp"
#include "Lib/Loader/TextureManager.hpp"
#include "ScreenRender.hpp"
#include "Camera.hpp"
#include "ScreenRenderDWI.hpp"
#include "DepthSort.hpp"
#include "Lib/Math/FloatDef.hpp"
#include "Lib/Math/FastInverse.hpp"
#include "AsmSupport.hpp"
#include "Lib/Renderer/ScreenRenderAuxD3D.hpp"
#include "Lib/Renderer/ScreenRenderAuxD3DUtilities.hpp"
#include <Memory.h>
//
// Module defines.
//
// Represents the maximum number of vertices a temporary polygon may have.
#define iMAX_TEMP_VERTICES (40)
// Flag to do some depth sorting hack stuff.
#define bDEMO_BUILD (0)
// Flag to use screen area for feature reduction.
#define bUSE_SCREEN_AREA (1)
#if bUSE_SCREEN_AREA
// Screen area threshold for polygon to use the smallest mip level.
const float fUseSmallestMipArea = 20.0f;
#endif // bUSE_SCREEN_AREA
// Thresholds for feature reduction.
const float fTextureThresholdArea = 4.0f;
const float fPerspectiveThresholdArea = 32.0f;
const float fPerspectiveThresholdLen = 12.0f;
const float fGourShadeThresholdArea = 16.0f;
float fPerspectivePixelError = 1.0;
float fAltPerspectivePixelError = 2.0;
float fMinZPerspective = 0.3;
//
// Module specific variables.
//
CColour clrInitColour = CColour(0, 0, 0);
//
// Module specific inline functions.
//
//**********************************************************************************************
//
inline int iMaxDim
(
rptr<CRaster> pras
)
//
// Returns the maximum dimension of the raster.
//
//**********************************
{
return Max(pras->iWidth, pras->iHeight);
}
//**********************************************************************************************
//
inline float fGetSlope
(
const CVector3<>& v3_0,
const CVector3<>& v3_1
)
//
// Returns:
// The y/x slope.
//
//**********************************
{
// Prevent division by zero.
if (v3_0.tX == v3_1.tX)
return 99999.0f;
return Abs((v3_0.tY - v3_1.tY) / (v3_0.tX - v3_1.tX));
}
//**********************************************************************************************
//
inline ESideOf iPlaneDirection
(
const CPlane& pl // Plane to test direction.
)
//
// Returns 'esfON, 'esfOUTSIDE,' or 'esfINSIDE.'
//
//**********************************
{
return (u4FromFloat(pl.rD) & 0x80000000L) ? (esfINSIDE) : (esfOUTSIDE);
}
//**********************************************************************************************
//
inline ESideOf esfSides
(
const CPlane& pl, // Plane to test against.
const CRenderPolygon* prpoly, // Pointer to the render polygon.
int esf_side, // Flags.
TReal r_plane_tolerance // Tolerance for plane test.
)
//
// Returns 'true' if the polygon intersects the plane.
//
//**********************************
{
Assert(prpoly);
// Iterate through the vertices to get the side the polygon is on.
for (int i_vertex = 0; i_vertex < prpoly->paprvPolyVertices.uLen; i_vertex++)
{
float f_dist = pl.rDistance(prpoly->paprvPolyVertices[i_vertex]->v3Cam);
#if VER_ASM
__asm
{
mov eax, f_dist
mov ecx, 0x00000002
and eax, 0x7FFFFFFF
mov ebx, [esf_side]
cmp eax, [r_plane_tolerance]
jle short CONTINUE_SIDE
sub eax, f_dist
sbb ecx, 0
or ecx, ebx
cmp ecx, esfINTERSECT
je short RETURN_INTERSECT
mov [esf_side], ecx
CONTINUE_SIDE:
}
#else // VER_ASM
// Is on the negative side?
if (r_dist < -r_plane_tolerance)
esf_side |= esfINSIDE;
// Is on the positive side?
if (r_dist > r_plane_tolerance)
esf_side |= esfOUTSIDE;
// Must lay on the plane, do nothing.
// Return early if the polygon intersects the plane.
if (esf_side == esfINTERSECT)
return esfINTERSECT;
#endif // VER_ASM
}
return esf_side;
RETURN_INTERSECT:
return esfINTERSECT;
}
//**********************************************************************************************
//
inline bool bAllOppositeSide
(
const CPlane& pl, // Plane to test against.
const CRenderPolygon* prpoly, // Pointer to the render polygon.
TReal r_plane_tolerance // Tolerance for plane test.
)
//
// Returns 'true' if the all the points of a polygon are on the opposite side of the plane from
// the camera.
//
// Notes:
// The camera is assumed to be at the origin. This is step 3 of the ambiguity resolution.
//
// To do:
// This routine is inefficient and can be optimized.
//
//**********************************
{
int esf_side = esfSides(pl, prpoly, 0, r_plane_tolerance);
if (esf_side == esfINTERSECT)
return false;
if (esf_side == esfON)
return true;
return esf_side != iPlaneDirection(pl);
}
//**********************************************************************************************
//
inline bool bAllSameSide
(
const CPlane& pl, // Plane to test against.
const CRenderPolygon* prpoly, // Pointer to the render polygon.
TReal r_plane_tolerance // Tolerance for plane test.
)
//
// Returns 'true' if the all the points of a polygon are on the same side of the plane as the
// camera.
//
// Notes:
// The camera is assumed to be at the origin. This is step 4 of the ambiguity resolution.
//
// To do:
// This routine is inefficient and can be optimized.
//
//**********************************
{
return esfSides(pl, prpoly, iPlaneDirection(pl), r_plane_tolerance) != esfINTERSECT;
}
//*********************************************************************************************
//
inline bool bRequireGouraudShading(const CRenderPolygon& rp)
//
// Returns 'true' if the polygon has to use gouraud shading, otherwise returns 'false.'
//
//**************************************
{
// Compare all vertex shading values with the first one.
for (uint u = 1; u < rp.paprvPolyVertices.uLen; u++)
{
if (iPosFloatCast(rp.paprvPolyVertices[u]->cvIntensity) != iPosFloatCast(rp.paprvPolyVertices[0]->cvIntensity))
return true;
}
return false;
}
//
// Class definitions.
//
//**********************************************************************************************
//
// CScreenRender implementation.
//
//**********************************************************************************************
//
struct CVertExtent
//
// Class used for finding vertex extents for mipmapping.
//
// Prefix: ve
//
//**********************************
{
public:
float f_value;
int i_index;
//******************************************************************************************
//
// Member functions.
//
//******************************************************************************************
//
void Set(float f_v, int i)
//
// Sets the internal values.
//
//**********************************
{
f_value = f_v;
i_index = i;
}
};
//
// Class implementations.
//
//**********************************************************************************************
//
// SRenderVertex implementation.
//
//******************************************************************************************
SRenderVertex::SRenderVertex(const SRenderVertex& rv_0, const SRenderVertex& rv_1, TReal r_t,
bool b_perspective)
{
// The intersection must occur along the line segment.
Assert(bWithin(r_t, 0.0, 1.0));
const SRenderVertex* prv_0 = &rv_0;
const SRenderVertex* prv_1 = &rv_1;
//
// Keep the direction uniform. This is to prevent two adjacent polygons that are both
// clipped from generating slightly different vertices. The apparently strange procedure
// of adding 1 to r_t and immediately subtracting it again is to normalize inaccuracies.
// If the r_t is very small, adding 1 will normalize the range, e.g.:
//
// Suppose:
//
// float f = 0.000000001;
//
// f == 0.000000001 is true, but if we do this:
//
// f += 1.0f;
// f -= 1.0f;
//
// then f == 0.000000001 is not true, f == 0.0.
//
// To do:
// Remove this code if cracking persists, make it more efficient if it does not.
//
#if VER_ASM
__asm
{
fld1
fadd r_t
fld1
fsub
fstp r_t
}
#else
r_t += 1.0;
r_t -= 1.0;
#endif VER_ASM
if (prv_0->v3Cam.tX < prv_1->v3Cam.tX)
{
r_t = 1.0 - r_t;
prv_0 = &rv_1;
prv_1 = &rv_0;
}
TReal r_s = 1.0 - r_t;
v3Cam = CVector3<>
(
r_s * prv_0->v3Cam.tX + r_t * prv_1->v3Cam.tX,
r_s * prv_0->v3Cam.tY + r_t * prv_1->v3Cam.tY,
r_s * prv_0->v3Cam.tZ + r_t * prv_1->v3Cam.tZ
);
tcTex = CTexCoord
(
r_s * prv_0->tcTex.tX + r_t * prv_1->tcTex.tX,
r_s * prv_0->tcTex.tY + r_t * prv_1->tcTex.tY
);
//
// Calculate 2D interpolation parameter as well, for screen coords and shading value.
//
TReal r_t2D;
if (v3Cam.tY <= 0)
// This vertex will surely be rejected later.
r_t2D = 0.0;
else if (prv_0->v3Cam.tY <= 0)
r_t2D = 1.0;
else if (prv_1->v3Cam.tY <= 0)
r_t2D = 0.0;
else
{
r_t2D = r_t * prv_1->v3Cam.tY / v3Cam.tY;
Assert(bWithin(r_t2D, 0.0, 1.0));
}
TReal r_s2D = 1.0f - r_t2D;
if (b_perspective)
{
if (prv_0->v3Screen.tZ && prv_1->v3Screen.tZ)
{
// Vertices have been projected, so interpolate screen coordinates.
v3Screen = CVector3<>
(
r_s2D * prv_0->v3Screen.tX + r_t2D * prv_1->v3Screen.tX,
r_s2D * prv_0->v3Screen.tY + r_t2D * prv_1->v3Screen.tY,
r_s2D * prv_0->v3Screen.tZ + r_t2D * prv_1->v3Screen.tZ
);
}
}
else
{
// Interpolate the screen coords linearly as well, without projection.
v3Screen = CVector3<>
(
r_s * prv_0->v3Screen.tX + r_t * prv_1->v3Screen.tX,
r_s * prv_0->v3Screen.tY + r_t * prv_1->v3Screen.tY,
r_s * prv_0->v3Screen.tZ + r_t * prv_1->v3Screen.tZ
);
}
// Use 2D interpolation for shading as well, because it's not rendered perspective correct.
cvIntensity = r_s2D * prv_0->cvIntensity + r_t2D * prv_1->cvIntensity;
}
//**********************************************************************************************
//
// CRenderPolygon implementation.
//
//******************************************************************************************
CRenderPolygon::CRenderPolygon()
: bMovedInList(false), iMipLevel(0),
cvFace(0.0f)
{
#if (VER_DEBUG)
bClippedByDepthSort = false;
iClipped = 0;
#endif
}
//******************************************************************************************
void CRenderPolygon::SetPlane()
{
// Choose points for the plane evenly distributed around the polygon.
plPlane.FastMake
(
paprvPolyVertices[0]->v3Cam,
paprvPolyVertices[1]->v3Cam,
paprvPolyVertices[(paprvPolyVertices.uLen >> 1) + 1]->v3Cam
);
ptsTolerances.SetTolerance(this);
}
//******************************************************************************************
float CRenderPolygon::fGetFarthestZ() const
{
float f_z = 0.0f;
// Sum the z values of the vertices.
for (int i = 0; i < paprvPolyVertices.size(); ++i)
if (paprvPolyVertices[i]->v3Cam.tY > f_z)
f_z = paprvPolyVertices[i]->v3Cam.tY;
// Return the average value.
return f_z;
}
//******************************************************************************************
bool CRenderPolygon::bCalculateScreenExtents(bool b_cull_tiny)
{
Assert(paprvPolyVertices);
Assert(paprvPolyVertices[0]);
#if (VER_DEBUG)
bClippedByDepthSort = false;
#endif
//
// Build the bounding box in screen space.
//
float f_min_x, f_max_x;
float f_min_y, f_max_y;
float f_min_z, f_max_z;
// Set initial min and max values for the screen.
f_min_x = f_max_x = paprvPolyVertices[0]->v3Screen.tX;
f_min_y = f_max_y = paprvPolyVertices[0]->v3Screen.tY;
f_min_z = f_max_z = paprvPolyVertices[0]->v3Cam.tY;
Assert(f_min_x >= 0.0f);
Assert(f_min_y >= 0.0f);
Assert(f_min_z >= 0.0f);
//
// Iterate through remaining vertices setting min and max values.
//
for (int i_vert = 1; i_vert < paprvPolyVertices.uLen; i_vert++)
{
Assert(paprvPolyVertices[i_vert]);
// Set x range.
{
// Get the screen value.
float f_x = paprvPolyVertices[i_vert]->v3Screen.tX;
Assert(f_x >= 0.0f);
// Set the minimum and maximum x values.
if (u4FromFloat(f_x) < u4FromFloat(f_min_x))
f_min_x = f_x;
if (u4FromFloat(f_x) > u4FromFloat(f_max_x))
f_max_x = f_x;
}
// Set y range.
{
// Get the screen value.
float f_y = paprvPolyVertices[i_vert]->v3Screen.tY;
Assert(f_y >= 0.0f);
// Set the minimum and maximum x values.
if (u4FromFloat(f_y) < u4FromFloat(f_min_y))
f_min_y = f_y;
if (u4FromFloat(f_y) > u4FromFloat(f_max_y))
f_max_y = f_y;
}
// Set z range.
{
// Get the screen value.
float f_z = paprvPolyVertices[i_vert]->v3Cam.tY;
Assert(f_z >= 0.0f);
// Set the minimum and maximum x values.
if (u4FromFloat(f_z) < u4FromFloat(f_min_z))
f_min_z = f_z;
if (u4FromFloat(f_z) > u4FromFloat(f_max_z))
f_max_z = f_z;
}
}
if (b_cull_tiny && ehwHardwareFlags != ehwTerrain)
if ((iPosFloatCast(f_max_x) == iPosFloatCast(f_min_x)) ||
(iPosFloatCast(f_max_y) == iPosFloatCast(f_min_y)))
return false;
f_min_x += ptsTolerances.fPixelBuffer;
f_min_y += ptsTolerances.fPixelBuffer;
if (u4FromFloat(f_max_x) >= u4FromFloat(ptsTolerances.fPixelBuffer))
f_max_x -= ptsTolerances.fPixelBuffer;
if (u4FromFloat(f_max_y) >= u4FromFloat(ptsTolerances.fPixelBuffer))
f_max_y -= ptsTolerances.fPixelBuffer;
// Copy the minimum and maximum values to the class structure.
v2ScreenMinInt.tX = Fist(f_min_x);
v2ScreenMinInt.tY = Fist(f_min_y);
v2ScreenMaxInt.tX = Fist(f_max_x);
v2ScreenMaxInt.tY = Fist(f_max_y);
fMinZ = f_min_z;
fMaxZ = rMaxLevel = f_max_z;
bMovedInList = false;
return true;
//return (v2ScreenMaxInt.tX == v2ScreenMinInt.tX) || (v2ScreenMaxInt.tY == v2ScreenMinInt.tY);
}
//******************************************************************************************
bool CRenderPolygon::bBehind(const CRenderPolygon* prpoly) const
{
// This polygon is 'P,' prpoly is 'Q.'
#if bDEMO_BUILD
//
// Hack! If P is a terrain polygon, and Q is not, bypass this test.
//
if (seterfFace[erfSOURCE_TERRAIN] && !prpoly->seterfFace[erfSOURCE_TERRAIN])
return true;
#endif // bDEMO_BUILD
// Use P's tolerance.
// Step 4: Is Q entirely on the same side of P's plane from the viewpoint?
if (bAllSameSide(plPlane, prpoly, rPlaneTolerance))
return true;
// Step 3: Is P entirely on the opposite side of Q's plane from the viewpoint?
if (bAllOppositeSide(prpoly->plPlane, this, rPlaneTolerance))
return true;
return false;
}
//******************************************************************************************
bool CRenderPolygon::bInFrontOf(const CRenderPolygon* prpoly) const
{
// This polygon is 'P,' prpoly is 'Q.'
#if bDEMO_BUILD
//
// Hack! If P is a terrain polygon, and Q is not, bypass this test.
//
if (prpoly->seterfFace[erfSOURCE_TERRAIN] && !seterfFace[erfSOURCE_TERRAIN])
return true;
#endif // bDEMO_BUILD
// Use P's tolerance.
#if (BROKEN_DEPTH_SORT)
// Used when the depth sort can't handle reversing polygons in complex
// situations.
// Step 3': Is Q entirely on the opposite side of P's plane from the viewpoint?
if (bAllOppositeSide(plPlane, prpoly, rPlaneTolerance))
{
// Step 4': Is P entirely on the same side of Q's plane from the viewpoint?
if (bAllSameSide(prpoly->plPlane, this, rPlaneTolerance))
return true;
}
#else
// Step 3': Is Q entirely on the opposite side of P's plane from the viewpoint?
if (bAllOppositeSide(plPlane, prpoly, rPlaneTolerance))
return true;
// Step 4': Is P entirely on the same side of Q's plane from the viewpoint?
if (bAllSameSide(prpoly->plPlane, this, rPlaneTolerance))
return true;
#endif
return false;
}
//******************************************************************************************
void CRenderPolygon::ReduceFeatures()
{
bAccept = false;
bool b_hardware = bFullHardware || ehwHardwareFlags != ehwSoftware;
// Cull based on screen extents.
if (!bCalculateScreenExtents(!b_hardware))
return;
// Compute screen area.
SetArea();
// Don't feature reduce or cull hardware polygons.
if (b_hardware)
{
bAccept = true;
return;
}
// Cull if the polygons is zero or negative area.
if (fArea <= 0.0f)
return;
// Accept the polygon.
bAccept = true;
// Do not do feature reduction if the polygon is water.
if (seterfFace[erfSOURCE_WATER])
return;
Assert(fArea > 0.0f);
// Turn off texture if possible.
if (seterfFace[erfTEXTURE])
{
// Do not kill texture for terrain or for bump maps.
if (!seterfFace[erfSOURCE_TERRAIN] && !seterfFace[erfBUMP] &&
u4FromFloat(fArea) <= u4FromFloat(fTextureThresholdArea))
{
seterfFace[erfTEXTURE][erfBUMP][erfPERSPECTIVE] = 0;
// Do not render the polygon if it is transparent.
if (seterfFace[erfTRANSPARENT])
{
bAccept = false;
return;
}
srd3dRenderer.SetD3DFlagForPolygon(*this);
}
else
{
if (seterfFace[erfPERSPECTIVE])
{
// Turn off perspective correction if possible.
if (u4FromFloat(fArea) <= u4FromFloat(fPerspectiveThresholdArea))
{
seterfFace[erfPERSPECTIVE] = 0;
}
else
{
TReal r_len = Max
(
v2ScreenMaxInt.tX - v2ScreenMinInt.tX,
v2ScreenMaxInt.tY - v2ScreenMinInt.tY
);
if (u4FromFloat(r_len) <= u4FromFloat(fPerspectiveThresholdLen))
{
seterfFace[erfPERSPECTIVE] = 0;
}
else
{
//
// Pixel error is (zmax/zmin - 1)/4 * scr-length.
// So, perspective correct if (zmax/zmin - 1)/4 * scr-length >= ERR.
// (zmax - zmin) / (4 * zmin) * scr-length >= ERR.
// (zmax - zmin) * scr-length >= ERR * 4 * zmin.
//
TReal r_err;
if (seterfFace[erfSOURCE_TERRAIN])
r_err = fAltPerspectivePixelError * 4.0 * fMinZ;
else
r_err = fPerspectivePixelError * 4.0 * fMinZ;
if ((fMaxZ - fMinZ) * r_len < r_err)
{
seterfFace[erfPERSPECTIVE] = 0;
}
}
}
}
}
}
// Remove Gouraud shading if possible.
if (seterfFace[erfLIGHT_SHADE])
{
if (u4FromFloat(fArea) <= u4FromFloat(fGourShadeThresholdArea) || !bRequireGouraudShading(*this))
{
cvFace = paprvPolyVertices[0]->cvIntensity;
seterfFace[erfLIGHT_SHADE] = 0;
}
}
}
//**********************************************************************************************
void CRenderPolygon::FindBestSlope(int& ri_best_vert_a, int& ri_best_vert_b) const
{
bool b_found_first = false; // Flag to find first slope.
float f_best_slope; // Best (smallest) slope found.
//
// Iterate through all combinations of vertices to find the line with the smallest
// slope.
//
for (int i_vert_0 = 0; i_vert_0 < paprvPolyVertices.uLen - 2; i_vert_0++)
{
for (int i_vert_1 = i_vert_0 + 2; i_vert_1 < paprvPolyVertices.uLen; i_vert_1++)
{
// Make sure that vertex 0 and the last vertex are not on the same edge.
if (i_vert_0 == 0 && i_vert_1 == paprvPolyVertices.uLen - 1)
continue;
// Get the slope of the subdividing line.
float f_new_slope = fGetSlope
(
paprvPolyVertices[i_vert_0]->v3Screen,
paprvPolyVertices[i_vert_1]->v3Screen
);
// Record the best subdivision.
if (!b_found_first)
{
b_found_first = true;
ri_best_vert_a = i_vert_0;
ri_best_vert_b = i_vert_1;
f_best_slope = f_new_slope;
}
else
{
if (f_new_slope < f_best_slope)
{
ri_best_vert_a = i_vert_0;
ri_best_vert_b = i_vert_1;
f_best_slope = f_new_slope;
}
}
}
}
if (ri_best_vert_a > ri_best_vert_b)
Swap(ri_best_vert_a, ri_best_vert_b);
}
//**********************************************************************************************
void CRenderPolygon::BuildVertexArray(CRenderPolygon& rpoly_new, int i_from, int i_to,
CPArray<SRenderVertex*> paprv_scratch) const
{
int i_len = i_to + 1 - i_from;
if (i_len < 0)
i_len += paprvPolyVertices.uLen;
rpoly_new.paprvPolyVertices.uLen = i_len;
// Use passed temporary array.
rpoly_new.paprvPolyVertices.atArray = paprv_scratch;
Assert(paprv_scratch.uLen >= i_len);
// Iterate from the first vertex to the final.
int i_vert_master = i_from; // Vertex index for the master polygon.
for (int i_vert_new = 0; i_vert_new < rpoly_new.paprvPolyVertices.uLen; i_vert_new++)
{
rpoly_new.paprvPolyVertices[i_vert_new] = paprvPolyVertices[i_vert_master++];
// Wrap.
if (i_vert_master == paprvPolyVertices.uLen)
i_vert_master = 0;
}
}
//******************************************************************************************
void CRenderPolygon::SetMipLevel(bool b_target_hardware)
{
// Do nothing if the polygon is to be rejected.
if (!bAccept)
{
// Why am I calling a function for every rejected polygon?
iMipLevel = 0;
return;
}
// Do nothing if there is no texture or only one mip level.
if (!ptexTexture || ptexTexture->iGetNumMipLevels() <= 1)
{
iMipLevel = 0;
return;
}
#if bUSE_SCREEN_AREA
// Use the smallest mip level if possible.
if (u4FromFloat(fArea) <= u4FromFloat(fUseSmallestMipArea))
{
iMipLevel = ptexTexture->iGetNumMipLevels() - 1;
return;
}
#endif // bUSE_SCREEN_AREA
//
// Compute texture area of this polygon.
//
float fTexArea = 0.0f;
// Break the polygon into triangles and sum their area.
for (uint u_3 = 2; u_3 < paprvPolyVertices.uLen; ++u_3)
{
uint u_1 = 0;
uint u_2 = u_3 - 1;
//
// Formula:
//
// Area = (u2 - u1) * (v3 - v1) - (u3 - u1) * (v2 - v1)
// ---------------------------------------------
// 2
//
// Note the reverse order of the screen vertices. Also note that the CVector2D cross
// product could be used here.
//
float f_sub_area = (paprvPolyVertices[u_2]->tcTex.tX - paprvPolyVertices[u_3]->tcTex.tX) *
(paprvPolyVertices[u_1]->tcTex.tY - paprvPolyVertices[u_3]->tcTex.tY) -
(paprvPolyVertices[u_1]->tcTex.tX - paprvPolyVertices[u_3]->tcTex.tX) *
(paprvPolyVertices[u_2]->tcTex.tY - paprvPolyVertices[u_3]->tcTex.tY);
// Sum the areas.
fTexArea += fabs(f_sub_area);
}
// Divide by two.
fTexArea *= 0.5f;
// Modify the target hardware flag to determine if polygon is to be rendered using hardware.
b_target_hardware = b_target_hardware && (bFullHardware || ehwHardwareFlags != ehwSoftware);
// Set the mip level.
int i_desired_mip = ptexTexture->iSelectMipLevel(fArea, fTexArea);
//
// Nothing further is required if the polygons uses hardware and the mip level is present for
// hardware.
//
if (b_target_hardware)
{
if (bAvailableD3DRaster((CTexture*)ptexTexture.ptGet(), i_desired_mip))
{
iMipLevel = i_desired_mip;
return;
}
}
// Prevent large mip levels from being loaded if the hardware is slow at creating textures.
if (b_target_hardware)
{
for (;;)
{
if (iMaxDim(ptexTexture->prasGetTexture(i_desired_mip)) <= d3dDriver.iGetRecommendedMaxDim())
break;
if (i_desired_mip >= ptexTexture->iGetNumMipLevels() - 1)
break;
++i_desired_mip;
}
if (bAvailableD3DRaster((CTexture*)ptexTexture.ptGet(), i_desired_mip))
{
iMipLevel = i_desired_mip;
return;
}
}
#if (0)
// If the texture manager has all the textures loaded then there is no need to substitute anothe level or
// request a load.
if (gtxmTexMan.bAllPresent())
{
iMipLevel = i_desired_mip;
return;
}
#endif
//
// We are paging textures and therefore need to ensure that the texture we want to use exists.
//
if (gtxmTexMan.bIsAvailable(ptexTexture->prasGetTexture(i_desired_mip)->pSurface,
ptexTexture->prasGetTexture(i_desired_mip)->iByteSpan()) )
{
iMipLevel = i_desired_mip;
return;
}
else
{
gtxmTexMan.RequestMemory(ptexTexture->prasGetTexture(i_desired_mip)->pSurface,
ptexTexture->prasGetTexture(i_desired_mip)->iByteSpan());
}
// The following test is safe because the lowest mip is always available.
// We will never add one past the end of the array because the previous test will cause a return.
if (gtxmTexMan.bIsAvailable(ptexTexture->prasGetTexture(i_desired_mip + 1)->pSurface,
ptexTexture->prasGetTexture(i_desired_mip + 1)->iByteSpan()) )
{
// Use the next smaller mip.
iMipLevel = i_desired_mip + 1;
}
else if (i_desired_mip > 0 && gtxmTexMan.bIsAvailable( ptexTexture->prasGetTexture(i_desired_mip - 1)->pSurface,
ptexTexture->prasGetTexture(i_desired_mip - 1)->iByteSpan()) )
{
// Use the next bigger mip.
iMipLevel = i_desired_mip - 1;
}
else
{
// The last mip to check for validity. Sub 2 or 3 instead of 1 to skip the lowest 1 or 2 levels..
int i_smallest_mip = ptexTexture->iGetNumMipLevels() - 1;
// Already tried the next smaller, so skip it.
++i_desired_mip;
// As long as there's something we haven't tried....
while (i_desired_mip <= i_smallest_mip)
{
// Try each mip.
if (gtxmTexMan.bIsAvailable(ptexTexture->prasGetTexture(i_desired_mip)->pSurface,
ptexTexture->prasGetTexture(i_desired_mip)->iByteSpan()) )
{
iMipLevel = i_desired_mip;
return;
}
++i_desired_mip;
}
// Didn't find anything. Use smallest.
iMipLevel = i_smallest_mip;
AlwaysAssert(0);
}
}
//******************************************************************************************
void CRenderPolygon::Validate()
{
// Verify that the screen co-ordinates are in range.
for (int i = 0; i < paprvPolyVertices.uLen; i++)
{
float tX = paprvPolyVertices[i]->v3Screen.tX;
float tY = paprvPolyVertices[i]->v3Screen.tY;
AlwaysAssert(tX < 2048.0f && tX >= 0.0f);
AlwaysAssert(tY < 2048.0f && tY >= 0.0f);
}
// Verify that the texture co-ordinates are in range.
if (seterfFace[erfTEXTURE])
{
rptr<CRaster> pras_texture = ptexTexture->prasGetTexture(iMipLevel);
if (pras_texture->bNotTileable)
{
for (int i = 0; i < paprvPolyVertices.uLen; i++)
{
float tX = paprvPolyVertices[i]->tcTex.tX * pras_texture->fWidth + fTexEdgeTolerance;
float tY = paprvPolyVertices[i]->tcTex.tY * pras_texture->fHeight + fTexEdgeTolerance;
AlwaysAssert(tX < 32767.0f && tX > -32767.0f);
AlwaysAssert(tY < 32767.0f && tY > -32767.0f);
}
}
else
{
for (int i = 0; i < paprvPolyVertices.uLen; i++)
{
float tX = paprvPolyVertices[i]->tcTex.tX * pras_texture->fWidth;
float tY = paprvPolyVertices[i]->tcTex.tY * pras_texture->fHeight;
AlwaysAssert(tX < 32767.0f && tX > -32767.0f);
AlwaysAssert(tY < 32767.0f && tY > -32767.0f);
}
}
}
// Verify that the shading values are in range.
if (seterfFace[erfLIGHT_SHADE])
{
for (int i = 0; i < paprvPolyVertices.uLen; i++)
{
AlwaysAssert(paprvPolyVertices[i]->cvIntensity >= 0.05 &&
paprvPolyVertices[i]->cvIntensity <= ptexTexture->ppcePalClut->pclutClut->iNumRampValues-0.05);
}
}
}
//******************************************************************************************
void CRenderPolygon::Grow(float f_amount)
{
}
//**********************************************************************************************
//
// CScreenRender implementation.
//
//******************************************************************************************
//
// SSettings implementation.
//
//**************************************************************************************
CScreenRender::SSettings::SSettings()
:
bClearBackground(true),
bDrawSky(true),
//clrBackground(clrDefEndDepth), // Default BG colour.
clrBackground(clrInitColour), // Default BG colour.
bTestGamma(false),
bHalfScanlines(false),
bDoubleVertical(false),
bSoftwareAlpha(false)
{
seterfState = Set
(erfZ_BUFFER)
+ erfLIGHT
+ erfLIGHT_SHADE
+ erfFOG
+ erfFOG_SHADE
+ erfSPECULAR
+ erfCOPY
+ erfTEXTURE
+ erfTRANSPARENT
+ erfBUMP
+ erfSUBPIXEL
+ erfPERSPECTIVE
+ erfTRAPEZOIDS
+ erfSOURCE_TERRAIN
+ erfSOURCE_WATER
+ erfOCCLUDE
+ erfDRAW_CLIP
+ erfMIPMAP
+ erfALPHA_COLOUR
+ erfD3D_CACHE
#if (BILINEAR_FILTER)
+ erfFILTER
#endif
;
}
//******************************************************************************************
CScreenRender::CScreenRender(SSettings* pscrenset, rptr<CRaster> pras_screen, rptr<CRaster> pras_zbuffer)
: prasScreen(pras_screen.ptGet()), pSettings(pscrenset)
{
Assert(pscrenset);
}
//******************************************************************************************
void CScreenRender::CorrectRenderState(CSet<ERenderFeature>& seterf)
{
// Enforce a few dependencies.
if (seterf[erfLIGHT_SHADE])
seterf[erfLIGHT] = 1;
if (seterf[erfFOG_SHADE])
seterf[erfFOG] = 1;
// Ensure nothing was changed that cannot be.
seterf = (seterf & seterfModify()) + (seterfDefault() - seterfModify());
}
//******************************************************************************************
void CScreenRender::DrawPolygons(CPArray<CRenderPolygon*> paprpoly)
{
// Default version calls DrawPolygon.
for (uint u_poly = 0; u_poly < paprpoly.uLen; u_poly++)
{
#if VER_DEBUG
ValidatePolygon(*paprpoly[u_poly]);
#endif
DrawPolygon(*paprpoly[u_poly]);
}
}
//******************************************************************************************
void CScreenRender::SetD3DFlagForPolygons(CPArray<CRenderPolygon> parpoly, bool b_d3d)
{
//
// Set D3D flags.
//
if (d3dDriver.bUseD3D() && b_d3d)
{
srd3dRenderer.SetD3DFlagForPolygons(parpoly);
return;
}
//
// Set flag default.
//
for (uint u_poly = 0; u_poly < parpoly.uLen; u_poly++)
{
parpoly[u_poly].bFullHardware = false;;
parpoly[u_poly].ehwHardwareFlags = ehwSoftware;
}
}
//**********************************************************************************************
void CScreenRender::ValidatePolygon(const CRenderPolygon& rpoly)
{
// There must be a texture pointer; if we are mapping, it must have a raster.
Assert(rpoly.ptexTexture);
if (rpoly.seterfFace[erfTEXTURE][erfTRANSPARENT][erfBUMP][erfCOPY])
{
Assert(rpoly.ptexTexture->prasGetTexture(rpoly.iMipLevel));
if (rpoly.seterfFace[erfTRANSPARENT])
Assert(rpoly.ptexTexture->seterfFeatures[erfTRANSPARENT]);
if (rpoly.seterfFace[erfCOPY])
{
// Make sure it's screen format.
Assert(rpoly.ptexTexture->prasGetTexture(rpoly.iMipLevel)->iPixelBits == prasScreen->iPixelBits);
}
else if (rpoly.seterfFace[erfBUMP])
{
Assert(rpoly.ptexTexture->seterfFeatures[erfBUMP]);
}
}
if (rpoly.seterfFace[erfLIGHT])
{
Assert(rpoly.ptexTexture && rpoly.ptexTexture->ppcePalClut && rpoly.ptexTexture->ppcePalClut->pclutClut);
if (!rpoly.seterfFace[erfLIGHT_SHADE][erfBUMP])
// If not bump-mapping or vertex lighting, we need valid face lighting here.
Assert(rpoly.cvFace >= 0 && rpoly.cvFace < rpoly.ptexTexture->ppcePalClut->pclutClut->iNumRampValues);
}
for (int i = 0; i < rpoly.paprvPolyVertices.uLen; i++)
{
ValidateVertex(*rpoly.paprvPolyVertices[i]);
if (rpoly.seterfFace[erfLIGHT_SHADE])
// We need valid vertex lighting.
Assert(rpoly.paprvPolyVertices[i]->cvIntensity >= 0 &&
rpoly.paprvPolyVertices[i]->cvIntensity < rpoly.ptexTexture->ppcePalClut->pclutClut->iNumRampValues);
}
}
//**********************************************************************************************
void CScreenRender::ValidateVertex(const SRenderVertex& rv)
{
const float fTEXTURE_TOLERANCE = 0.001;
Assert(bWithin(int(rv.v3Screen.tX), 0, prasScreen->iWidth));
Assert(bWithin(int(rv.v3Screen.tY), 0, prasScreen->iHeight));
Assert(rv.v3Screen.tZ > 0.0f);
// No, we are tiling now! Do not clamp the nice texture coordinate.
#if VER_CLAMP_UV_TILE
SetMinMax(NonConst(rv.tcTex.tX), 0, 1);
SetMinMax(NonConst(rv.tcTex.tY), 0, 1);
#endif
// Assert(bWithin(rv.tcTex.tX, -fTEXTURE_TOLERANCE, 1.0 + fTEXTURE_TOLERANCE));
// Assert(bWithin(rv.tcTex.tY, -fTEXTURE_TOLERANCE, 1.0 + fTEXTURE_TOLERANCE));
}
//******************************************************************************************
TPixel CScreenRender::pixSetBackground(TPixel pix_background)
{
// Function should never be called.
//Assert(0);
return 0;
}
//******************************************************************************************
void CScreenRender::SetHardwareOut(bool b_allow_hardware)
{
// Function should never be called.
//Assert(0);
}
//**********************************************************************************************
//
// CArrayRenderDesc implementation.
//
CArrayRenderDesc::CArrayRenderDesc()
{
//
// Add the known render descs.
// This is the only place in the code that must specifically know of all RenderDescs,
// and must include the ScreenRender*.hpp files.
//
sapRenderDesc << &screndescDWI;
}
//**********************************************************************************************
//
// CArrayRenderDesc global variable.
//
CArrayRenderDesc sapRenderDesc;
// Static key variable.
uint32 CSortKey::u4UniqueSubKeyUp;
uint32 CSortKey::u4UniqueSubKeyDown;
uint32 CSortKey::u4UniqueSubKeyStep;
float fMaxAreaCull = 0.05f;
float fMaxAreaCullTerrain;