/*==LICENSE==*
CyanWorlds . com Engine - MMOG client , server and tools
Copyright ( C ) 2011 Cyan Worlds , Inc .
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
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 , see < http : //www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program , or any covered work , by linking or
combining it with any of RAD Game Tools Bink SDK , Autodesk 3 ds Max SDK ,
NVIDIA PhysX SDK , Microsoft DirectX SDK , OpenSSL library , Independent
JPEG Group JPEG library , Microsoft Windows Media SDK , or Apple QuickTime SDK
( or a modified version of those libraries ) ,
containing parts covered by the terms of the Bink SDK EULA , 3 ds Max EULA ,
PhysX SDK EULA , DirectX SDK EULA , OpenSSL and SSLeay licenses , IJG
JPEG Library README , Windows Media SDK EULA , or QuickTime SDK EULA , the
licensors of this Program grant you additional
permission to convey the resulting work . Corresponding Source for a
non - source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work .
You can contact Cyan Worlds , Inc . by email legal @ cyan . com
or by snail mail at :
Cyan Worlds , Inc .
14617 N Newport Hwy
Mead , WA 99021
* = = LICENSE = = */
///////////////////////////////////////////////////////////////////////////////
// //
// plDXPipeline Class Functions //
// plPipeline derivative for DirectX //
// Cyan, Inc. //
// //
//// Version History //////////////////////////////////////////////////////////
// //
// 2.23.2001 mcn - Created. //
// //
///////////////////////////////////////////////////////////////////////////////
# include "hsConfig.h"
# include "hsWindows.h"
# include <d3d9.h>
# include <ddraw.h>
# include <d3dx9mesh.h>
# include <dxerr9.h>
# include "hsWinRef.h"
# include "hsTypes.h"
# include "plDXPipeline.h"
# include "plPipelineCreate.h"
# include "plDebugText.h"
# include "plDXEnumerate.h"
# include "hsG3DDeviceSelector.h"
# include "hsGDDrawDllLoad.h"
# include "hsResMgr.h"
# include "plStatusLogDrawer.h"
# include "plQuality.h"
# include "plPipeDebugFlags.h"
# include "hsTemplates.h"
//#include "hsGEnviron.h"
# include "plProfile.h"
# include "../plMessage/plDeviceRecreateMsg.h"
# include "../pnMessage/plSelfDestructMsg.h"
# include "../pnMessage/plClientMsg.h"
# include "../plSurface/hsGMaterial.h"
# include "../plSurface/plLayerInterface.h"
# include "../plSurface/plLayerShadowBase.h"
# include "../plGImage/plMipmap.h"
# include "../plGImage/plCubicEnvironmap.h"
# include "../plDrawable/plDrawableSpans.h"
# include "../plDrawable/plGeometrySpan.h"
# include "../plDrawable/plSpaceTree.h"
# include "../plDrawable/plDrawableGenerator.h"
# include "../plDrawable/plSpanTypes.h"
# include "../plDrawable/plAccessSpan.h"
# include "../plDrawable/plAuxSpan.h"
# include "../pnSceneObject/plSceneObject.h"
# include "../pnSceneObject/plDrawInterface.h"
# include "hsFastMath.h"
# include "../plGLight/plLightInfo.h"
# include "../plParticleSystem/plParticleEmitter.h"
# include "../plParticleSystem/plParticle.h"
# include "../plAvatar/plAvatarClothing.h"
# include "plDebugText.h"
# include "plFogEnvironment.h"
# include "plDXTextFont.h"
# include "plGBufferGroup.h"
# include "hsTimer.h"
# include "plgDispatch.h"
# include "../plScene/plRenderRequest.h"
# include "../plScene/plVisMgr.h"
# include "plRenderTarget.h"
# include "plCubicRenderTarget.h"
# include "plDynamicEnvMap.h"
# include "../../FeatureLib/pfCamera/plVirtualCamNeu.h"
# include "plDXBufferRefs.h"
# include "plDXTextureRef.h"
# include "plDXLightRef.h"
# include "plDXRenderTargetRef.h"
# include "plDXVertexShader.h"
# include "plDXPixelShader.h"
# include "../plGLight/plShadowSlave.h"
# include "../plGLight/plShadowCaster.h"
# include "hsGMatState.inl"
# include "../plSurface/plShader.h"
# include "plDXVertexShader.h"
# include "plDXPixelShader.h"
# include "../pnMessage/plPipeResMakeMsg.h"
# include "plPipeResReq.h"
# include "../pnNetCommon/plNetApp.h" // for dbg logging
# include "../../FeatureLib/pfCamera/plVirtualCamNeu.h"
# include "../../FeatureLib/pfCamera/plCameraModifier.h"
# include "../plResMgr/plLocalization.h"
// mf horse - test hack, nuke this later
# include "../plSurface/plLayerDepth.h"
# include "../plGImage/hsCodecManager.h"
//#include "../plGImage/hsDXTDirectXCodec.h"
# ifdef HS_DEBUGGING
// This is so VC++ will let us view the contents of plIcicle::fOwnerKey
# include "../pnKeyedObject/plKey.h"
# endif
# include "plCullTree.h"
# include "plTweak.h"
# include <algorithm>
//#define MF_TOSSER
int mfCurrentTest = 100 ;
PipelineParams plPipeline : : fDefaultPipeParams ;
PipelineParams plPipeline : : fInitialPipeParams ;
//#define MF_ENABLE_HACKOFF
# ifdef MF_ENABLE_HACKOFF
//WHITE
static hsTArray < plRenderTarget * > hackOffscreens ;
UInt32 doHackPlate = UInt32 ( - 1 ) ;
# endif // MF_ENABLE_HACKOFF
UInt32 fDbgSetupInitFlags ; // HACK temp only
# ifdef HS_DEBUGGING
void plReleaseObject ( IUnknown * x )
{
if ( x )
{
int refs = x - > Release ( ) ;
if ( refs )
refs = 0 ;
}
}
# else // HS_DEBUGGING
void plReleaseObject ( IUnknown * x )
{
if ( x )
x - > Release ( ) ;
}
# endif // HS_DEBUGGING
//// Local Static Stuff ///////////////////////////////////////////////////////
/// Macros for getting/setting data in a D3D vertex buffer
inline UInt8 * inlStuffPoint ( UInt8 * ptr , const hsScalarTriple & point )
{
register float * dst = ( float * ) ptr ;
register const float * src = ( float * ) & point . fX ;
* dst + + = * src + + ;
* dst + + = * src + + ;
* dst + + = * src + + ;
return ( UInt8 * ) dst ;
}
inline UInt8 * inlStuffUInt32 ( UInt8 * ptr , const UInt32 uint )
{
* ( UInt32 * ) ptr = uint ;
return ptr + sizeof ( uint ) ;
}
inline UInt8 * inlExtractPoint ( const UInt8 * ptr , const hsScalarTriple & pt )
{
register const float * src = ( float * ) ptr ;
register float * dst = ( float * ) & pt . fX ;
* dst + + = * src + + ;
* dst + + = * src + + ;
* dst + + = * src + + ;
return ( UInt8 * ) src ;
}
inline UInt8 * inlExtractFloat ( const UInt8 * & ptr , float & f )
{
register const float * src = ( float * ) ptr ;
f = * src + + ;
return ( UInt8 * ) src ;
}
inline UInt8 * inlExtractUInt32 ( const UInt8 * & ptr , UInt32 & uint )
{
const UInt32 * src = ( UInt32 * ) ptr ;
uint = * src + + ;
return ( UInt8 * ) src ;
}
inline DWORD F2DW ( FLOAT f )
{
return * ( ( DWORD * ) & f ) ;
}
//// Macros for D3D error handling
# define INIT_ERROR_CHECK( cond, errMsg ) if( FAILED( fSettings.fDXError = cond ) ) { return ICreateFail( errMsg ); }
# if 1 // DEBUG
# define STRONG_ERROR_CHECK( cond ) if( FAILED( fSettings.fDXError = cond ) ) { IGetD3DError(); IShowErrorMessage(); }
# define WEAK_ERROR_CHECK( cond ) STRONG_ERROR_CHECK( cond )
# else
# define STRONG_ERROR_CHECK( cond ) if( FAILED( fSettings.fDXError = cond ) ) { IGetD3DError(); }
# define WEAK_ERROR_CHECK( cond ) cond
# endif
static D3DXMATRIX d3dIdentityMatrix ( 1.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 1.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 1.0f , 0.0f ,
0.0f , 0.0f , 0.0f , 1.0f ) ;
static const enum _D3DTRANSFORMSTATETYPE sTextureStages [ 8 ] =
{
D3DTS_TEXTURE0 , D3DTS_TEXTURE1 , D3DTS_TEXTURE2 , D3DTS_TEXTURE3 ,
D3DTS_TEXTURE4 , D3DTS_TEXTURE5 , D3DTS_TEXTURE6 , D3DTS_TEXTURE7
} ;
static const float kPerspLayerScale = 0.00001f ;
static const float kPerspLayerScaleW = 0.001f ;
static const float kPerspLayerTrans = 0.00002f ;
static const hsScalar kAvTexPoolShrinkThresh = 30.f ; // seconds
// This caps the number of D3D lights we use. We'll use up to the max allowed
// or this number, whichever is smaller. (This is to prevent us going haywire
// on trying to allocate an array for ALL of the lights in the Ref device.)
//#define kD3DMaxTotalLights 32
///HAAAAACK Let's be mean and limit the artists to only 4 run-time lights.... hehehehhehe (not my idea!!!)
const int kD3DMaxTotalLights = 8 ;
// The framerate is the limit on the number of projected lights an object can have.
const int kMaxProjectors = 100 ;
/// This controls whether we can draw bounds boxes around all the ice spans.
//#ifdef HS_DEBUGGING
# define MCN_BOUNDS_SPANS 1
//#endif
# define MF_BOUNDS_LEVEL_ICE 1
//#define HS_D3D_USE_SPECULAR
/// Define this to write out z-buffer debug info to plasmalog.txt
# ifdef HS_DEBUGGING
//#define DBG_WRITE_FORMATS
# endif
plProfile_CreateMemCounter ( " Pipeline Surfaces " , " Memory " , MemPipelineSurfaces ) ;
plProfile_Extern ( MemVertex ) ;
plProfile_Extern ( MemIndex ) ;
plProfile_CreateCounter ( " Feed Triangles " , " Draw " , DrawFeedTriangles ) ;
plProfile_CreateCounter ( " Polys " , " General " , DrawTriangles ) ;
plProfile_CreateCounter ( " Draw Prim Static " , " Draw " , DrawPrimStatic ) ;
plProfile_CreateMemCounter ( " Total Texture Size " , " Draw " , TotalTexSize ) ;
plProfile_CreateTimer ( " Harvest " , " Draw " , Harvest ) ;
plProfile_CreateCounter ( " Material Change " , " Draw " , MatChange ) ;
plProfile_CreateCounter ( " Layer Change " , " Draw " , LayChange ) ;
plProfile_Extern ( DrawOccBuild ) ;
plProfile_CreateCounterNoReset ( " Reload " , " PipeC " , PipeReload ) ;
plProfile_CreateTimer ( " RenderScene " , " PipeT " , RenderScene ) ;
plProfile_CreateTimer ( " VisEval " , " PipeT " , VisEval ) ;
plProfile_CreateTimer ( " VisSelect " , " PipeT " , VisSelect ) ;
plProfile_CreateTimer ( " FindSceneLights " , " PipeT " , FindSceneLights ) ;
plProfile_CreateTimer ( " PrepShadows " , " PipeT " , PrepShadows ) ;
plProfile_CreateTimer ( " PrepDrawable " , " PipeT " , PrepDrawable ) ;
plProfile_CreateTimer ( " Skin " , " PipeT " , Skin ) ;
plProfile_CreateTimer ( " AvSort " , " PipeT " , AvatarSort ) ;
plProfile_CreateTimer ( " Find Lights " , " PipeT " , FindLights ) ;
plProfile_CreateTimer ( " Find Perms " , " PipeT " , FindPerm ) ;
plProfile_CreateTimer ( " FindSpan " , " PipeT " , FindSpan ) ;
plProfile_CreateTimer ( " FindActiveLights " , " PipeT " , FindActiveLights ) ;
plProfile_CreateTimer ( " ApplyActiveLights " , " PipeT " , ApplyActiveLights ) ;
plProfile_CreateTimer ( " ApplyMoving " , " PipeT " , ApplyMoving ) ;
plProfile_CreateTimer ( " ApplyToSpec " , " PipeT " , ApplyToSpec ) ;
plProfile_CreateTimer ( " ApplyToMoving " , " PipeT " , ApplyToMoving ) ;
plProfile_CreateTimer ( " ClearLights " , " PipeT " , ClearLights ) ;
plProfile_CreateTimer ( " RenderSpan " , " PipeT " , RenderSpan ) ;
plProfile_CreateTimer ( " MergeCheck " , " PipeT " , MergeCheck ) ;
plProfile_CreateTimer ( " MergeSpan " , " PipeT " , MergeSpan ) ;
plProfile_CreateTimer ( " SpanTransforms " , " PipeT " , SpanTransforms ) ;
plProfile_CreateTimer ( " SpanFog " , " PipeT " , SpanFog ) ;
plProfile_CreateTimer ( " SelectLights " , " PipeT " , SelectLights ) ;
plProfile_CreateTimer ( " SelectProj " , " PipeT " , SelectProj ) ;
plProfile_CreateTimer ( " CheckDyn " , " PipeT " , CheckDyn ) ;
plProfile_CreateTimer ( " CheckStat " , " PipeT " , CheckStat ) ;
plProfile_CreateTimer ( " RenderBuff " , " PipeT " , RenderBuff ) ;
plProfile_CreateTimer ( " RenderPrim " , " PipeT " , RenderPrim ) ;
plProfile_CreateTimer ( " PlateMgr " , " PipeT " , PlateMgr ) ;
plProfile_CreateTimer ( " DebugText " , " PipeT " , DebugText ) ;
plProfile_CreateTimer ( " Reset " , " PipeT " , Reset ) ;
plProfile_CreateMemCounter ( " DefMem " , " PipeC " , DefaultMem ) ;
plProfile_CreateMemCounter ( " ManMem " , " PipeC " , ManagedMem ) ;
plProfile_CreateMemCounterReset ( " CurrTex " , " PipeC " , CurrTex ) ;
plProfile_CreateMemCounterReset ( " CurrVB " , " PipeC " , CurrVB ) ;
plProfile_CreateMemCounter ( " TexTot " , " PipeC " , TexTot ) ;
plProfile_CreateMemCounterReset ( " fTexUsed " , " PipeC " , fTexUsed ) ;
plProfile_CreateMemCounterReset ( " fTexManaged " , " PipeC " , fTexManaged ) ;
plProfile_CreateMemCounterReset ( " fVtxUsed " , " PipeC " , fVtxUsed ) ;
plProfile_CreateMemCounterReset ( " fVtxManaged " , " PipeC " , fVtxManaged ) ;
plProfile_CreateMemCounter ( " ManSeen " , " PipeC " , ManSeen ) ;
plProfile_CreateCounterNoReset ( " ManEvict " , " PipeC " , ManEvict ) ;
plProfile_CreateCounter ( " LightOn " , " PipeC " , LightOn ) ;
plProfile_CreateCounter ( " LightVis " , " PipeC " , LightVis ) ;
plProfile_CreateCounter ( " LightChar " , " PipeC " , LightChar ) ;
plProfile_CreateCounter ( " LightActive " , " PipeC " , LightActive ) ;
plProfile_CreateCounter ( " Lights Found " , " PipeC " , FindLightsFound ) ;
plProfile_CreateCounter ( " Perms Found " , " PipeC " , FindLightsPerm ) ;
plProfile_CreateCounter ( " Merge " , " PipeC " , SpanMerge ) ;
plProfile_CreateCounter ( " TexNum " , " PipeC " , NumTex ) ;
plProfile_CreateCounter ( " LiState " , " PipeC " , MatLightState ) ;
plProfile_CreateCounter ( " OccPoly " , " PipeC " , OccPolyUsed ) ;
plProfile_CreateCounter ( " OccNode " , " PipeC " , OccNodeUsed ) ;
plProfile_CreateCounter ( " NumSkin " , " PipeC " , NumSkin ) ;
plProfile_CreateCounter ( " AvatarFaces " , " PipeC " , AvatarFaces ) ;
plProfile_CreateCounter ( " VertexChange " , " PipeC " , VertexChange ) ;
plProfile_CreateCounter ( " IndexChange " , " PipeC " , IndexChange ) ;
plProfile_CreateCounter ( " DynVBuffs " , " PipeC " , DynVBuffs ) ;
plProfile_CreateCounter ( " EmptyList " , " PipeC " , EmptyList ) ;
plProfile_CreateCounter ( " AvRTPoolUsed " , " PipeC " , AvRTPoolUsed ) ;
plProfile_CreateCounter ( " AvRTPoolCount " , " PipeC " , AvRTPoolCount ) ;
plProfile_CreateCounter ( " AvRTPoolRes " , " PipeC " , AvRTPoolRes ) ;
plProfile_CreateCounter ( " AvRTShrinkTime " , " PipeC " , AvRTShrinkTime ) ;
# ifndef PLASMA_EXTERNAL_RELEASE
/// Fun inlines for keeping track of surface creation/deletion memory
void D3DSURF_MEMNEW ( IDirect3DSurface9 * surf )
{
if ( surf )
{
D3DSURFACE_DESC info ;
surf - > GetDesc ( & info ) ;
PROFILE_POOL_MEM ( D3DPOOL_DEFAULT , info . Width * info . Height * plDXPipeline : : GetDXBitDepth ( info . Format ) / 8 + sizeof ( IDirect3DSurface9 ) , true , " D3DSurface " ) ;
plProfile_NewMem ( MemPipelineSurfaces , info . Width * info . Height * plDXPipeline : : GetDXBitDepth ( info . Format ) / 8 + sizeof ( IDirect3DSurface9 ) ) ;
}
}
void D3DSURF_MEMNEW ( IDirect3DTexture9 * tex )
{
if ( tex )
{
IDirect3DSurface9 * surf ;
tex - > GetSurfaceLevel ( 0 , & surf ) ;
if ( surf )
{
D3DSURF_MEMNEW ( surf ) ;
surf - > Release ( ) ;
}
}
}
void D3DSURF_MEMNEW ( IDirect3DCubeTexture9 * cTex )
{
if ( cTex )
{
IDirect3DSurface9 * surf ;
cTex - > GetCubeMapSurface ( D3DCUBEMAP_FACE_POSITIVE_X , 0 , & surf ) ;
if ( surf )
{
D3DSURF_MEMNEW ( surf ) ;
D3DSURF_MEMNEW ( surf ) ;
D3DSURF_MEMNEW ( surf ) ;
D3DSURF_MEMNEW ( surf ) ;
D3DSURF_MEMNEW ( surf ) ;
D3DSURF_MEMNEW ( surf ) ;
surf - > Release ( ) ;
}
}
}
void D3DSURF_MEMDEL ( IDirect3DSurface9 * surf )
{
if ( surf )
{
D3DSURFACE_DESC info ;
surf - > GetDesc ( & info ) ;
PROFILE_POOL_MEM ( D3DPOOL_DEFAULT , info . Width * info . Height * plDXPipeline : : GetDXBitDepth ( info . Format ) / 8 + sizeof ( IDirect3DSurface9 ) , false , " D3DSurface " ) ;
plProfile_DelMem ( MemPipelineSurfaces , info . Width * info . Height * plDXPipeline : : GetDXBitDepth ( info . Format ) / 8 + sizeof ( IDirect3DSurface9 ) ) ;
}
}
void D3DSURF_MEMDEL ( IDirect3DTexture9 * tex )
{
if ( tex )
{
IDirect3DSurface9 * surf ;
tex - > GetSurfaceLevel ( 0 , & surf ) ;
if ( surf )
{
D3DSURF_MEMDEL ( surf ) ;
surf - > Release ( ) ;
}
}
}
void D3DSURF_MEMDEL ( IDirect3DCubeTexture9 * cTex )
{
if ( cTex )
{
IDirect3DSurface9 * surf ;
cTex - > GetCubeMapSurface ( D3DCUBEMAP_FACE_POSITIVE_X , 0 , & surf ) ;
if ( surf )
{
D3DSURF_MEMDEL ( surf ) ;
D3DSURF_MEMDEL ( surf ) ;
D3DSURF_MEMDEL ( surf ) ;
D3DSURF_MEMDEL ( surf ) ;
D3DSURF_MEMDEL ( surf ) ;
D3DSURF_MEMDEL ( surf ) ;
surf - > Release ( ) ;
}
}
}
# else
void D3DSURF_MEMNEW ( IDirect3DSurface9 * surf ) { }
void D3DSURF_MEMNEW ( IDirect3DTexture9 * tex ) { }
void D3DSURF_MEMNEW ( IDirect3DCubeTexture9 * cTex ) { }
void D3DSURF_MEMDEL ( IDirect3DSurface9 * surf ) { }
void D3DSURF_MEMDEL ( IDirect3DTexture9 * tex ) { }
void D3DSURF_MEMDEL ( IDirect3DCubeTexture9 * cTex ) { }
# endif // PLASMA_EXTERNAL_RELEASE
# ifndef PLASMA_EXTERNAL_RELEASE
void plDXPipeline : : ProfilePoolMem ( D3DPOOL poolType , UInt32 size , hsBool add , char * id )
{
switch ( poolType )
{
case D3DPOOL_MANAGED :
if ( add )
{
plProfile_NewMem ( ManagedMem , size ) ;
//plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Adding MANAGED mem. Size: %10d, Total: %10d ID: %s",
// size, gProfileVarManagedMem.GetValue(), id);
}
else
{
plProfile_DelMem ( ManagedMem , size ) ;
//plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Deleting MANAGED mem. Size: %10d, Total: %10d ID: %s",
// size, gProfileVarManagedMem.GetValue(), id);
}
break ;
default :
if ( add )
{
plProfile_NewMem ( DefaultMem , size ) ;
//plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Adding DEFAULT mem. Size: %10d, Total: %10d ID: %s",
// size, gProfileVarDefaultMem.GetValue(), id);
}
else
{
plProfile_DelMem ( DefaultMem , size ) ;
//plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Deleting DEFAULT mem. Size: %10d, Total: %10d ID: %s",
// size, gProfileVarDefaultMem.GetValue(), id);
}
break ;
}
}
# endif // PLASMA_EXTERNAL_RELEASE
/////////////////////////////////////////////////////////////////////////////////////////
// Implementations of RenderPrims types.
// Currently support render tri list
// These allow the same setup code path to be followed, no matter what the primitive type
// (i.e. data-type/draw-call is going to happen once the render state is set.
// Originally useful to make one code path for trilists, tri-patches, and rect-patches, but
// we've since dropped support for patches. We still use the RenderNil function to allow the
// code to go through all the state setup without knowing whether a render call is going to
// come out the other end.
// Would allow easy extension for supporting tristrips or pointsprites, but we've never had
// a strong reason to use either.
// First, Declarations.
// Adding a nil RenderPrim for turning off drawing
class plRenderNilFunc : public plRenderPrimFunc
{
public :
plRenderNilFunc ( ) { }
virtual hsBool RenderPrims ( ) const { return false ; }
} ;
static plRenderNilFunc sRenderNil ;
class plRenderTriListFunc : public plRenderPrimFunc
{
protected :
LPDIRECT3DDEVICE9 fD3DDevice ;
int fBaseVertexIndex ;
int fVStart ;
int fVLength ;
int fIStart ;
int fNumTris ;
public :
plRenderTriListFunc ( LPDIRECT3DDEVICE9 d3dDevice , int baseVertexIndex ,
int vStart , int vLength , int iStart , int iNumTris )
: fD3DDevice ( d3dDevice ) , fBaseVertexIndex ( baseVertexIndex ) , fVStart ( vStart ) , fVLength ( vLength ) , fIStart ( iStart ) , fNumTris ( iNumTris ) { }
virtual hsBool RenderPrims ( ) const ;
} ;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementations
hsBool plRenderTriListFunc : : RenderPrims ( ) const
{
plProfile_IncCount ( DrawFeedTriangles , fNumTris ) ;
plProfile_IncCount ( DrawTriangles , fNumTris ) ;
plProfile_Inc ( DrawPrimStatic ) ;
return FAILED ( fD3DDevice - > DrawIndexedPrimitive ( D3DPT_TRIANGLELIST , fBaseVertexIndex , fVStart , fVLength , fIStart , fNumTris ) ) ;
}
//// Constructor & Destructor /////////////////////////////////////////////////
UInt32 plDXPipeline : : fTexUsed ( 0 ) ;
UInt32 plDXPipeline : : fTexManaged ( 0 ) ;
UInt32 plDXPipeline : : fVtxUsed ( 0 ) ;
UInt32 plDXPipeline : : fVtxManaged ( 0 ) ;
plDXPipeline : : plDXPipeline ( hsWinRef hWnd , const hsG3DDeviceModeRecord * devModeRec )
: fManagedAlloced ( false ) ,
fAllocUnManaged ( false )
{
hsAssert ( D3DTSS_TCI_PASSTHRU = = plLayerInterface : : kUVWPassThru , " D3D Enum has changed. Notify graphics department. " ) ;
hsAssert ( D3DTSS_TCI_CAMERASPACENORMAL = = plLayerInterface : : kUVWNormal , " D3D Enum has changed. Notify graphics department. " ) ;
hsAssert ( D3DTSS_TCI_CAMERASPACEPOSITION = = plLayerInterface : : kUVWPosition , " D3D Enum has changed. Notify graphics department. " ) ;
hsAssert ( D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR = = plLayerInterface : : kUVWReflect , " D3D Enum has changed. Notify graphics department. " ) ;
// Initialize everything to NULL.
IClearMembers ( ) ;
// Get the requested mode and setup
const hsG3DDeviceRecord * devRec = devModeRec - > GetDevice ( ) ;
const hsG3DDeviceMode * devMode = devModeRec - > GetMode ( ) ;
/// Init our screen mode
fSettings . fHWnd = hWnd ;
if ( ! fInitialPipeParams . Windowed )
{
fSettings . fOrigWidth = devMode - > GetWidth ( ) ;
fSettings . fOrigHeight = devMode - > GetHeight ( ) ;
}
else
{
// windowed can run in any mode
fSettings . fOrigHeight = fInitialPipeParams . Height ;
fSettings . fOrigWidth = fInitialPipeParams . Width ;
}
IGetViewTransform ( ) . SetScreenSize ( ( UInt16 ) ( fSettings . fOrigWidth ) , ( UInt16 ) ( fSettings . fOrigHeight ) ) ;
fSettings . fColorDepth = devMode - > GetColorDepth ( ) ;
fVSync = fInitialPipeParams . VSync ;
if ( devRec - > GetAASetting ( ) = = 0 )
fSettings . fNumAASamples = 0 ;
else
fSettings . fNumAASamples = devMode - > GetFSAAType ( devRec - > GetAASetting ( ) - 1 ) ;
hsGDirect3DTnLEnumerate d3dEnum ;
if ( d3dEnum . GetEnumeErrorStr ( ) [ 0 ] )
{
IShowErrorMessage ( ( char * ) d3dEnum . GetEnumeErrorStr ( ) ) ;
return ;
}
if ( d3dEnum . SelectFromDevMode ( devRec , devMode ) )
{
IShowErrorMessage ( ( char * ) d3dEnum . GetEnumeErrorStr ( ) ) ;
return ;
}
// Gotta create this very first, so that the device/driver init works
if ( ! fD3DObject )
{
if ( ICreateMaster ( ) )
{
IShowErrorMessage ( " Cannot create D3D master object " ) ;
return ;
}
}
// Record the requested mode/setup.
ISetCurrentDriver ( d3dEnum . GetCurrentDriver ( ) ) ;
ISetCurrentDevice ( d3dEnum . GetCurrentDevice ( ) ) ;
D3DEnum_ModeInfo * pModeInfo = d3dEnum . GetCurrentMode ( ) ;
pModeInfo - > fWindowed = fInitialPipeParams . Windowed ; // set windowed mode from ini file
ISetCurrentMode ( d3dEnum . GetCurrentMode ( ) ) ;
fSettings . fFullscreen = ! fCurrentMode - > fWindowed ;
fSettings . fNumAASamples = fInitialPipeParams . AntiAliasingAmount ;
// ISetCaps just records the card capabilities that were passed in.
ISetCaps ( ) ;
// IRestrictCaps looks over those explicit caps and makes some decisions on
// what the card can really do.
IRestrictCaps ( * devRec ) ;
fSettings . fMaxAnisotropicSamples = fInitialPipeParams . AnisotropicLevel ;
if ( fSettings . fMaxAnisotropicSamples > fCurrentDevice - > fDDCaps . MaxAnisotropy )
fSettings . fMaxAnisotropicSamples = ( UInt8 ) fCurrentDevice - > fDDCaps . MaxAnisotropy ;
plConst ( UInt32 ) kDefaultDynVtxSize ( 32000 * 44 ) ;
plConst ( UInt32 ) kDefaultDynIdxSize ( 0 * plGBufferGroup : : kMaxNumIndicesPerBuffer * 2 ) ;
fDynVtxSize = kDefaultDynVtxSize ;
fVtxRefTime = 0 ;
// Go create surfaces and DX-dependent objects
if ( ICreateDeviceObjects ( ) )
{
IShowErrorMessage ( " Cannot create Direct3D device " ) ;
return ;
}
/*plStatusLog::AddLineS("pipeline.log", "Supported Resolutions:");
std : : vector < plDisplayMode > temp ;
GetSupportedDisplayModes ( & temp , 16 ) ;
for ( int i = 0 ; i < temp . size ( ) ; i + + )
{
plStatusLog : : AddLineS ( " pipeline.log " , " %d, %d, %d " , temp [ i ] . Width , temp [ i ] . Height , 16 ) ;
}
temp . clear ( ) ;
GetSupportedDisplayModes ( & temp , 32 ) ;
for ( int i = 0 ; i < temp . size ( ) ; i + + )
{
plStatusLog : : AddLineS ( " pipeline.log " , " %d, %d, %d " , temp [ i ] . Width , temp [ i ] . Height , 32 ) ;
} */
}
// Cleanup - Most happens in IReleaseDeviceObject().
plDXPipeline : : ~ plDXPipeline ( )
{
fCurrLay = nil ;
hsAssert ( fCurrMaterial = = nil , " Current material not unrefed properly " ) ;
// fCullProxy is a debugging representation of our CullTree. See plCullTree.cpp,
// plScene/plOccluder.cpp and plScene/plOccluderProxy.cpp for more info
if ( fCullProxy )
fCullProxy - > GetKey ( ) - > UnRefObject ( ) ;
delete fCurrentDriver ;
delete fCurrentDevice ;
delete fCurrentMode ;
IReleaseDeviceObjects ( ) ;
IClearClothingOutfits ( & fClothingOutfits ) ;
IClearClothingOutfits ( & fPrevClothingOutfits ) ;
}
//// IClearMembers ////////////////////////////////////////////////////////////
// Initialize everything to a nil state.
// This does not initialize to a working state, but to a state that can be
// built from. For example, the fD3DObject pointer is set to nil so that it's safe
// to delete or set to a valid pointer. It must be set to a valid pointer
// before the pipeline can be used for much.
// After the core initialization is done (in ICreateMaster and ICreateDeviceObjects)
// render state will be initialized in IInitDeviceState.
void plDXPipeline : : IClearMembers ( )
{
/// Clear some stuff
fVtxBuffRefList = nil ;
fIdxBuffRefList = nil ;
fTextureRefList = nil ;
fTextFontRefList = nil ;
fRenderTargetRefList = nil ;
fVShaderRefList = nil ;
fPShaderRefList = nil ;
fCurrMaterial = nil ;
fCurrLay = nil ;
fCurrRenderLayer = 0 ;
# if MCN_BOUNDS_SPANS
fBoundsMat = nil ;
fBoundsSpans = nil ;
# endif
fPlateMgr = nil ;
fLogDrawer = nil ;
fDebugTextMgr = nil ;
fCurrLightingMethod = plSpan : : kLiteMaterial ;
fCurrCullMode = D3DCULL_CW ;
fTexturing = false ;
fCurrNumLayers = 0 ;
fLastEndingStage = - 1 ;
fSettings . Reset ( ) ;
fStencil . Reset ( ) ;
fTweaks . Reset ( ) ;
fLights . Reset ( this ) ;
fCurrFog . Reset ( ) ;
fDeviceLost = false ;
fDevWasLost = false ;
fSettings . fCurrFVFFormat = 0 ;
fDynVtxBuff = nil ;
fNextDynVtx = 0 ;
int i ;
for ( i = 0 ; i < 8 ; i + + )
fLayerRef [ i ] = nil ;
IResetRenderTargetPools ( ) ;
fULutTextureRef = nil ;
for ( i = 0 ; i < kMaxRenderTargetNext ; i + + )
fBlurVBuffers [ i ] = nil ;
fBlurVSHandle = nil ;
fD3DObject = nil ;
fD3DDevice = nil ;
fD3DBackBuff = nil ;
fD3DDepthSurface = nil ;
fD3DMainSurface = nil ;
fSharedDepthSurface [ 0 ] = nil ;
fSharedDepthFormat [ 0 ] = D3DFMT_UNKNOWN ;
fSharedDepthSurface [ 1 ] = nil ;
fSharedDepthFormat [ 1 ] = D3DFMT_UNKNOWN ;
fCurrentMode = nil ;
fCurrentDriver = nil ;
fCurrentDevice = nil ;
fOverLayerStack . Reset ( ) ;
fOverBaseLayer = nil ;
fOverAllLayer = nil ;
fPiggyBackStack . Reset ( ) ;
fMatPiggyBacks = 0 ;
fActivePiggyBacks = 0 ;
for ( i = 0 ; i < 8 ; i + + )
{
fLayerState [ i ] . Reset ( ) ;
fOldLayerState [ i ] . Reset ( ) ;
}
fMatOverOn . Reset ( ) ;
fMatOverOff . Reset ( ) ;
// SetMaterialOverride( hsGMatState::kShade, hsGMatState::kShadeSpecularHighlight, false );
fView . Reset ( ) ;
fCullProxy = nil ;
fTime = 0 ;
fFrame = 0 ;
fInSceneDepth = 0 ;
fTextUseTime = 0 ;
fEvictTime = 0 ;
fManagedSeen = 0 ;
fManagedCutoff = 0 ;
fRenderCnt = 0 ;
fDebugFlags . Clear ( ) ;
fForceMatHandle = true ;
fAvRTShrinkValidSince = 0 ;
fAvRTWidth = 1024 ;
fAvNextFreeRT = 0 ;
}
// plDXViewSettings are just a convenience member struct to segregate the current view settings.
//
// Reset - Initialize the ViewSettings to default (normal/neutral) values.
void plDXViewSettings : : Reset ( )
{
// Normal render, on clear, clear the color buffer and depth buffer.
fRenderState = plPipeline : : kRenderNormal | plPipeline : : kRenderClearColor | plPipeline : : kRenderClearDepth ;
fRenderRequest = nil ;
fDrawableTypeMask = plDrawable : : kNormal ;
fSubDrawableTypeMask = plDrawable : : kSubNormal ;
// Clear color to black, depth to yon.
fClearColor = 0 ;
fClearDepth = 1.f ;
fDefaultFog . Clear ( ) ;
// Want to limit the number of nodes in the cull tree. After adding so many nodes,
// the benefits (#objects culled) falls off, but the cost (evaluating objects against
// node planes) keeps rising.
const UInt16 kCullMaxNodes = 250 ;
fCullTree . Reset ( ) ;
fCullTreeDirty = true ;
fCullMaxNodes = kCullMaxNodes ;
// Object Local to world transform and its inverse.
fLocalToWorld . Reset ( ) ;
fWorldToLocal . Reset ( ) ;
// see Core/plViewTransform.h
fTransform . Reset ( ) ;
fTransform . SetScreenSize ( 800 , 600 ) ;
// Keep track of handedness of local to world and camera transform for winding.
fLocalToWorldLeftHanded = false ;
fWorldToCamLeftHanded = false ;
}
//// plDXGeneralSettings::Reset //////////////////////////////////////////////
// Catch all struct of general settings plus pointers to current d3d objects.
void plDXGeneralSettings : : Reset ( )
{
fCurrVertexBuffRef = nil ;
fCurrIndexBuffRef = nil ;
fFullscreen = false ;
fHWnd = nil ;
fColorDepth = 32 ;
fD3DCaps = 0 ;
fBoardKluge = 0 ;
fStageEnd = 0 ;
fMaxNumLights = kD3DMaxTotalLights ;
fMaxNumProjectors = kMaxProjectors ;
fMaxLayersAtOnce = 1 ;
fMaxPiggyBacks = 0 ;
fBoundsDrawLevel = - 1 ;
fProperties = 0 ;
fClearColor = 0 ;
fNoGammaCorrect = false ;
fMaxUVWSrc = 8 ;
fCantProj = false ;
fLimitedProj = false ;
fBadManaged = false ;
fShareDepth = false ;
fCurrAnisotropy = false ;
fIsIntel = false ;
fDXError = D3D_OK ;
memset ( fErrorStr , 0 , sizeof ( fErrorStr ) ) ;
fCurrRenderTarget = nil ;
fCurrBaseRenderTarget = nil ;
fCurrD3DMainSurface = nil ;
fCurrD3DDepthSurface = nil ;
fCurrRenderTargetRef = nil ;
fCurrFVFFormat = 0 ;
fCurrVertexShader = nil ;
fCurrPixelShader = nil ;
fVeryAnnoyingTextureInvalidFlag = false ;
}
//// IInitDeviceState /////////////////////////////////////////////////////////
// Initialize the device to a known state. This also syncs it up with our internal state
// as recorded in the fLayerStates.
// Some of these states reflect the caps of the device, but for the most part, the
// important thing here is NOT what state we're in coming out of this function, but
// that we are in a known state, and that the known state is recorded in fLayerStates.
void plDXPipeline : : IInitDeviceState ( )
{
fLayerState [ 0 ] . Reset ( ) ;
fCurrCullMode = D3DCULL_CW ;
/// Set D3D states
fCurrFog . Reset ( ) ;
ISetFogParameters ( nil , nil ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_LESSEQUAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZENABLE , ( fSettings . fD3DCaps & kCapsWBuffer ) ? D3DZB_USEW : D3DZB_TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_CLIPPING , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_CULLMODE , fCurrCullMode ) ;
ISetCullMode ( ) ;
fD3DDevice - > SetRenderState ( D3DRS_ALPHATESTENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_ALPHAFUNC , D3DCMP_GREATEREQUAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_ALPHAREF , 0x00000001 ) ;
fD3DDevice - > SetRenderState ( D3DRS_MULTISAMPLEANTIALIAS , ( fSettings . fD3DCaps & kCapsFSAntiAlias ) ? TRUE : FALSE ) ;
fD3DDevice - > SetRenderState ( D3DRS_ANTIALIASEDLINEENABLE , FALSE ) ;
fD3DDevice - > SetRenderState ( D3DRS_DITHERENABLE , ( fSettings . fD3DCaps & kCapsDither ) ? TRUE : FALSE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARENABLE , FALSE ) ;
fD3DDevice - > SetRenderState ( D3DRS_LIGHTING , FALSE ) ;
fCurrD3DLiteState = false ;
fD3DDevice - > SetRenderState ( D3DRS_TEXTUREFACTOR , 0x0 ) ;
fD3DDevice - > SetRenderState ( D3DRS_STENCILENABLE , FALSE ) ;
fD3DDevice - > SetTransform ( D3DTS_TEXTURE0 , & d3dIdentityMatrix ) ;
fD3DDevice - > SetTransform ( D3DTS_WORLD , & d3dIdentityMatrix ) ;
/// NEW: to compensate for scaling transformations that might screw up our nicely
/// normalized normals. Note: nVidia says this is as fast or faster than with
/// this disabled, but who knows what it'll do on other cards...
fD3DDevice - > SetRenderState ( D3DRS_NORMALIZENORMALS , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_LOCALVIEWER , TRUE ) ;
UInt32 totalMem = fD3DDevice - > GetAvailableTextureMem ( ) ;
plProfile_Set ( TotalTexSize , totalMem ) ;
// Initialization for all 8 stages (even though we only use a few of them).
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
fLayerLODBias [ i ] = fTweaks . fDefaultLODBias ;
fLayerTransform [ i ] = false ;
fLayerRef [ i ] = nil ;
fLayerUVWSrcs [ i ] = i ;
fLayerState [ i ] . Reset ( ) ;
fD3DDevice - > SetTexture ( i , nil ) ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_TEXCOORDINDEX , i ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_ADDRESSU , D3DTADDRESS_WRAP ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_ADDRESSV , D3DTADDRESS_WRAP ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MIPMAPLODBIAS , * ( DWORD * ) ( & fLayerLODBias [ i ] ) ) ;
if ( fSettings . fMaxAnisotropicSamples > 0 & & ! IsDebugFlagSet ( plPipeDbg : : kFlagNoAnisotropy ) )
{
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MINFILTER , D3DTEXF_ANISOTROPIC ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MAGFILTER , D3DTEXF_LINEAR ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MAXANISOTROPY , ( DWORD ) fSettings . fMaxAnisotropicSamples ) ;
fSettings . fCurrAnisotropy = true ;
}
else
{
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MINFILTER , D3DTEXF_LINEAR ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MAGFILTER , D3DTEXF_LINEAR ) ;
fSettings . fCurrAnisotropy = false ;
}
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MIPFILTER , D3DTEXF_LINEAR ) ;
fD3DDevice - > SetTransform ( sTextureStages [ i ] , & d3dIdentityMatrix ) ;
fLayerXformFlags [ i ] = D3DTTFF_COUNT2 ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_TEXTURETRANSFORMFLAGS , D3DTTFF_COUNT2 ) ;
}
// Initialize our bump mapping matrices.
for ( i = 0 ; i < 4 ; i + + )
{
int j ;
for ( j = 0 ; j < 4 ; j + + )
{
fBumpDuMatrix . fMap [ i ] [ j ] = 0 ;
fBumpDvMatrix . fMap [ i ] [ j ] = 0 ;
fBumpDwMatrix . fMap [ i ] [ j ] = 0 ;
}
}
fBumpDuMatrix . NotIdentity ( ) ;
fBumpDvMatrix . NotIdentity ( ) ;
fBumpDwMatrix . NotIdentity ( ) ;
PushMaterialOverride ( hsGMatState : : kShade , hsGMatState : : kShadeSpecularHighlight , false ) ;
fLights . Reset ( this ) ;
return ;
}
//// ISetCaps /////////////////////////////////////////////////////////////////
// We've recorded the capabilities of the current device in fCurrentDevice (traditionally in the setup program),
// now translate that into our own caps flags.
void plDXPipeline : : ISetCaps ( )
{
fSettings . fD3DCaps = kCapsNone ;
// Set relevant caps (ones we can do something about).
if ( fCurrentDevice - > fDDCaps . RasterCaps & D3DPRASTERCAPS_DEPTHBIAS )
fSettings . fD3DCaps | = kCapsZBias ;
if ( fCurrentDevice - > fDDCaps . RasterCaps & D3DPRASTERCAPS_FOGRANGE )
fSettings . fD3DCaps | = kCapsRangeFog ;
if ( fCurrentDevice - > fDDCaps . RasterCaps & D3DPRASTERCAPS_FOGTABLE )
fSettings . fD3DCaps | = kCapsLinearFog | kCapsExpFog | kCapsExp2Fog | kCapsPixelFog ;
else
fSettings . fD3DCaps | = kCapsLinearFog ;
if ( fCurrentDevice - > fDDCaps . TextureFilterCaps & D3DPTFILTERCAPS_MIPFLINEAR )
fSettings . fD3DCaps | = kCapsMipmap ;
if ( fCurrentDevice - > fDDCaps . TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP )
fSettings . fD3DCaps | = kCapsCubicMipmap ;
if ( fCurrentDevice - > fDDCaps . RasterCaps & D3DPRASTERCAPS_WBUFFER )
fSettings . fD3DCaps | = kCapsWBuffer ;
if ( fCurrentDevice - > fDDCaps . RasterCaps & D3DPRASTERCAPS_DITHER )
fSettings . fD3DCaps | = kCapsDither ;
if ( fSettings . fNumAASamples > 0 )
fSettings . fD3DCaps | = kCapsFSAntiAlias ;
if ( fCurrentDevice - > fDDCaps . RasterCaps & D3DPRASTERCAPS_WFOG )
fSettings . fD3DCaps | = kCapsDoesWFog ;
if ( fCurrentDevice - > fDDCaps . TextureCaps & D3DPTEXTURECAPS_CUBEMAP )
fSettings . fD3DCaps | = kCapsCubicTextures ;
/// New 1.5.2000 - cull out mixed vertex processing
if ( fCurrentDevice - > fDDCaps . DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT
& & fCurrentMode - > fDDBehavior = = D3DCREATE_HARDWARE_VERTEXPROCESSING
)
fSettings . fD3DCaps | = kCapsHWTransform ;
// Currently always want d3d to transform
fSettings . fD3DCaps | = kCapsHWTransform ;
/// Always assume we can do small textures (IRestrictCaps will turn this off
/// if necessary)
fSettings . fD3DCaps | = kCapsDoesSmallTextures ;
/// Look for supported texture formats
if ( IFindCompressedFormats ( ) )
fSettings . fD3DCaps | = kCapsCompressTextures ;
if ( IFindLuminanceFormats ( ) )
fSettings . fD3DCaps | = kCapsLuminanceTextures ;
/// Max # of hardware lights
fSettings . fMaxNumLights = fCurrentDevice - > fDDCaps . MaxActiveLights ;
if ( fSettings . fMaxNumLights > kD3DMaxTotalLights )
fSettings . fMaxNumLights = kD3DMaxTotalLights ;
// Intel Extreme chips report 0 lights, meaning T&L is done
// in software, so you can have as many lights as you want.
// We only need 8, so set that here. Also turn off shadows,
// since the extreme can't really afford them, and record
// the fact this is the extreme for other driver problem
// workarounds.
if ( ! fSettings . fMaxNumLights )
{
fSettings . fMaxNumLights = kD3DMaxTotalLights ;
fSettings . fIsIntel = true ;
plShadowCaster : : SetCanShadowCast ( false ) ;
}
/// Max # of textures at once
fSettings . fMaxLayersAtOnce = fCurrentDevice - > fDDCaps . MaxSimultaneousTextures ;
if ( fCurrentDevice - > fDDCaps . DevCaps & D3DDEVCAPS_SEPARATETEXTUREMEMORIES )
fSettings . fMaxLayersAtOnce = 1 ;
// Alloc half our simultaneous textures to piggybacks.
// Won't hurt us unless we try to many things at once.
fSettings . fMaxPiggyBacks = fSettings . fMaxLayersAtOnce > > 1 ;
// Less than 4 layers at once means we have to fallback on uv bumpmapping
if ( fSettings . fMaxLayersAtOnce < 4 )
SetDebugFlag ( plPipeDbg : : kFlagBumpUV , true ) ;
fSettings . fMaxAnisotropicSamples = ( UInt8 ) ( fCurrentDevice - > fDDCaps . MaxAnisotropy ) ;
fSettings . fNoGammaCorrect = ! ( fCurrentDevice - > fDDCaps . Caps2 & D3DCAPS2_FULLSCREENGAMMA ) ;
if ( ! ( fCurrentDevice - > fDDCaps . TextureCaps & D3DPTEXTURECAPS_PROJECTED ) )
plDynamicCamMap : : SetCapable ( false ) ;
ISetGraphicsCapability ( fCurrentDevice - > fDDCaps . PixelShaderVersion ) ;
}
// ISetGraphicsCapability ///////////////////////////////////////////////////////
// Tell our global quality settings what we can do. We'll use this to only load
// versions we can render. So if we can render it, we load it and skip its low quality substitute,
// if we can't render it, we skip it and load its low quality substitute.
// Naturally, this must happen before we do any loading.
void plDXPipeline : : ISetGraphicsCapability ( UInt32 v )
{
int pixelMajor = D3DSHADER_VERSION_MAJOR ( v ) ;
int pixelMinor = D3DSHADER_VERSION_MINOR ( v ) ;
if ( pixelMajor > 1 )
{
plQuality : : SetCapability ( plQuality : : kPS_2_Plus ) ;
}
else if ( pixelMajor > 0 )
{
if ( pixelMinor > = 4 )
plQuality : : SetCapability ( plQuality : : kPS_1_4 ) ;
else if ( pixelMinor > 0 )
plQuality : : SetCapability ( plQuality : : kPS_1_1 ) ;
}
}
//// IRestrictCaps ////////////////////////////////////////////////////////////
// ISetCaps() sets our native caps based on the D3D caps bits D3D returns.
// IRestrictCaps looks at our hsG3DDeviceSelector flags and translates those
// into our runtime native caps.
// The DeviceSelector flags aren't set by what the board claims, but rather
// we try to identify the board and set them according to previous knowledge.
// For example, the ATI7500 will only use uvw coordinates 0 or 1. There's
// no d3d cap to reflect this, and it really should support [0..7], but
// there's no way to force it to be d3d compliant. So when we see we have
// an ATI7500, we set the cap kCapsMaxUVWSrc2.
// See hsG3DDeviceSelector.cpp for details and implementation.
void plDXPipeline : : IRestrictCaps ( const hsG3DDeviceRecord & devRec )
{
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsMipmap ) )
fSettings . fD3DCaps & = ~ kCapsMipmap ;
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsCubicMipmap ) )
fSettings . fD3DCaps & = ~ kCapsCubicMipmap ;
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsWBuffer ) )
fSettings . fD3DCaps & = ~ kCapsWBuffer ;
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsZBias ) )
fSettings . fD3DCaps & = ~ kCapsZBias ;
// if( !devRec.GetCap( hsG3DDeviceSelector::kCapsHWTransform ) )
// fSettings.fD3DCaps &= ~kCapsHWTransform;
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsDither ) )
fSettings . fD3DCaps & = ~ kCapsDither ;
// if( devRec.GetAASetting() == 0 )
// fSettings.fD3DCaps &= ~kCapsFSAntiAlias;
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsFogExp ) )
fSettings . fD3DCaps & = ~ kCapsExpFog ;
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsCubicTextures ) )
fSettings . fD3DCaps & = ~ kCapsCubicTextures ;
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsCantShadow ) )
plShadowCaster : : SetCanShadowCast ( false ) ;
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsCantProj ) )
fSettings . fCantProj = true ;
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsLimitedProj ) )
fSettings . fLimitedProj = true ;
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsBadManaged ) )
fSettings . fBadManaged = true ;
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsShareDepth ) )
fSettings . fShareDepth = true ;
/// Added 9.6.2000 mcn - shouldn't they be here anyway?
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsFogExp2 ) )
fSettings . fD3DCaps & = ~ kCapsExp2Fog ;
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsDoesSmallTextures ) )
fSettings . fD3DCaps & = ~ kCapsDoesSmallTextures ;
/// 9.22.2000 mcn - dFlag for bad (savage4) yon fix
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsBadYonStuff ) )
fSettings . fD3DCaps | = kCapsHasBadYonStuff ;
/// 10.31.2000 mcn - Flag for can't-handle-under-8-pixel-dimensions-on-textures
/// (see, isn't the name flag actually better in retrospect? :)
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsNoKindaSmallTexs ) )
fSettings . fD3DCaps | = kCapsNoKindaSmallTexs ;
/// Note: the following SHOULD be here, but we later detect for texture
/// formats and reset this flag. It should only be set if it is set already,
/// but that means ensuring it's set beforehand, which it might not be.
if ( ! devRec . GetCap ( hsG3DDeviceSelector : : kCapsCompressTextures ) )
fSettings . fD3DCaps & = ~ kCapsCompressTextures ;
/// Set up tweaks
SetZBiasScale ( ( float ) devRec . GetZBiasRating ( ) ) ;
fTweaks . fDefaultLODBias = ( float ) - ( 0.25 + ( float ) devRec . GetLODBiasRating ( ) ) ;
devRec . GetFogApproxStarts ( fTweaks . fFogExpApproxStart , fTweaks . fFogExp2ApproxStart ) ;
fTweaks . fFogEndBias = ( float ) devRec . GetFogEndBias ( ) ;
// Fog knee stuff
devRec . GetFogKneeParams ( hsG3DDeviceRecord : : kFogExp , fTweaks . fExpFogKnee , fTweaks . fExpFogKneeVal ) ;
devRec . GetFogKneeParams ( hsG3DDeviceRecord : : kFogExp2 , fTweaks . fExp2FogKnee , fTweaks . fExp2FogKneeVal ) ;
// Max # of layers
UInt32 max = devRec . GetLayersAtOnce ( ) ;
if ( max > 0 & & max < fSettings . fMaxLayersAtOnce )
fSettings . fMaxLayersAtOnce = max ;
/// Debug flag to force high-level cards down to GeForce 2 caps
if ( fDbgSetupInitFlags & 0x00000004 )
{
fSettings . fD3DCaps & = ~ kCapsFSAntiAlias ;
if ( fSettings . fMaxLayersAtOnce > 2 )
fSettings . fMaxLayersAtOnce = 2 ;
fSettings . fMaxAnisotropicSamples = 0 ;
plQuality : : SetCapability ( plQuality : : kMinimum ) ;
}
// There's a bug in NVidia drivers on Windows 2000 for GeForce1-4 (all flavors, including MX).
// When the amount allocated into managed memory approaches the on board memory size, the performance
// severely degrades, no matter how little is actually in use in the current rendering. So say all
// our d3d textures are created into managed memory at age load. Also say you are
// consistently viewing only 5Mb of managed materials (texture + vertex buffer). So as
// you walk through the age, the new textures you see get loaded on demand into video memory.
// Once you've seen enough to fill the on board memory, your frame rate starts falling and
// continues to fall as more textures get loaded. So either the memory manager is not letting
// go of LRU textures, or fragmentation is so horrible as to make the manager useless.
// So on these boards and with this OS, we keep track of how much managed memory we've seen,
// and when it reaches a threshhold, we flush managed memory with an EvictManagedResources() call.
// There's an unfortunate glitch, and then the frame rate is fine again.
// So if we need this workaround, we set fManagedCutoff to 1 here, and then once we have our
// D3D device, we query for the amount of memory and set the threshhold for flushing memory
// based on that.
OSVERSIONINFO osinfo ;
memset ( & osinfo , 0 , sizeof ( osinfo ) ) ;
osinfo . dwOSVersionInfoSize = sizeof ( osinfo ) ;
GetVersionEx ( & osinfo ) ;
if ( ( osinfo . dwMajorVersion = = 5 )
& & ( osinfo . dwMinorVersion = = 0 ) )
{
// It's the dreaded win2k
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsDoubleFlush ) )
fManagedCutoff = 1 ;
else if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsSingleFlush ) )
fManagedCutoff = 1 ;
}
//// Our temp debug flag to force z-buffering...
if ( ! ( fDbgSetupInitFlags & 0x00000001 ) )
fSettings . fD3DCaps & = ~ kCapsWBuffer ;
/// Set up the z-bias scale values, based on z- or w-buffering
if ( fSettings . fD3DCaps & kCapsWBuffer )
fTweaks . fDefaultPerspLayerScale = kPerspLayerScaleW ;
else
fTweaks . fDefaultPerspLayerScale = kPerspLayerScale ;
// Less than 4 layers at once means we have to fallback on uv bumpmapping
if ( fSettings . fMaxLayersAtOnce < 4 )
SetDebugFlag ( plPipeDbg : : kFlagBumpUV , true ) ;
if ( ( fSettings . fD3DCaps & kCapsHWTransform ) & & ( fCurrentMode - > fDDBehavior = = D3DCREATE_SOFTWARE_VERTEXPROCESSING ) )
fSettings . fD3DCaps & = ~ kCapsHWTransform ;
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsMaxUVWSrc2 ) )
fSettings . fMaxUVWSrc = 2 ;
/// Anisotropy stuff
//if( devRec.GetMaxAnisotropicSamples() < fSettings.fMaxAnisotropicSamples )
// fSettings.fMaxAnisotropicSamples = devRec.GetMaxAnisotropicSamples();
if ( devRec . GetCap ( hsG3DDeviceSelector : : kCapsNoAniso ) | | ( fSettings . fMaxAnisotropicSamples < = 1 ) )
fSettings . fMaxAnisotropicSamples = 0 ;
}
//// Get/SetZBiasScale ////////////////////////////////////////////////////////
// If the board really doesn't support Z-biasing, we adjust the perspective matrix in IGetCameraToNDC
// The layer scale and translation are tailored to the current hardware.
hsScalar plDXPipeline : : GetZBiasScale ( ) const
{
return ( fTweaks . fPerspLayerScale / fTweaks . fDefaultPerspLayerScale ) - 1.0f ;
}
void plDXPipeline : : SetZBiasScale ( hsScalar scale )
{
scale + = 1.0f ;
fTweaks . fPerspLayerScale = fTweaks . fDefaultPerspLayerScale * scale ;
fTweaks . fPerspLayerTrans = kPerspLayerTrans * scale ;
}
// Create all our video memory consuming D3D objects.
hsBool plDXPipeline : : ICreateDynDeviceObjects ( )
{
// Front/Back/Depth buffers
if ( ICreateNormalSurfaces ( ) )
return true ;
// RenderTarget pools are shared for our shadow generation algorithm.
// Different sizes for different resolutions.
IMakeRenderTargetPools ( ) ;
// Create device-specific stuff
fDebugTextMgr = TRACKED_NEW plDebugTextManager ( ) ;
if ( fDebugTextMgr = = nil )
return true ;
// Vertex buffers, index buffers, textures, etc.
LoadResources ( ) ;
return false ;
}
//// ICreateDeviceObjects /////////////////////////////////////////////////////
// Create all of our steady state D3D objects. More D3D objects will be created
// and destroyed as ages are loaded and unloaded, but these are the things that
// only go away when we lose the device.
hsBool plDXPipeline : : ICreateDeviceObjects ( )
{
// The D3D device
if ( ICreateDevice ( ! fSettings . fFullscreen ) )
return true ;
// Most everything else D3D
if ( ICreateDynDeviceObjects ( ) )
return true ;
// PlateMgr is largely for debugging and performance stats,
// but also gets used for some things like the cursor and
// linking fade to/from black.
fPlateMgr = TRACKED_NEW plDXPlateManager ( this , fD3DDevice ) ;
if ( fPlateMgr = = nil | | ! fPlateMgr - > IsValid ( ) )
return true ;
// We've got everything created now, initialize to a known state.
IInitDeviceState ( ) ;
if ( FAILED ( fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER , fSettings . fClearColor , 1.0f , 0L ) ) )
return true ;
// You may be wondering what this is. It's a workaround for a GeForce2 driver bug, where
// clears to the Zbuffer (but not color) are getting partially ignored. Don't even ask.
// So this is just to try and get the board used to the kind of foolishness it can expect
// from here out.
if ( FAILED ( fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_ZBUFFER , fSettings . fClearColor , 1.0f , 0L ) ) )
return true ;
if ( FAILED ( fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_ZBUFFER , fSettings . fClearColor , 1.0f , 0L ) ) )
return true ;
if ( FAILED ( fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_ZBUFFER , fSettings . fClearColor , 1.0f , 0L ) ) )
return true ;
/// Log renderer
fLogDrawer = TRACKED_NEW plStatusLogDrawer ( this ) ;
plStatusLogMgr : : GetInstance ( ) . SetDrawer ( fLogDrawer ) ;
/// Ok, we're done now
# if MCN_BOUNDS_SPANS
fBoundsSpans = TRACKED_NEW plDrawableSpans ( ) ;
hsgResMgr : : ResMgr ( ) - > NewKey ( " BoundsSpans " , fBoundsSpans , plLocation : : kGlobalFixedLoc ) ;
fBoundsSpans - > SetNativeProperty ( plDrawable : : kPropVolatile , true ) ;
fBoundsMat = TRACKED_NEW hsGMaterial ( ) ;
hsgResMgr : : ResMgr ( ) - > NewKey ( " BoundsMaterial " , fBoundsMat , plLocation : : kGlobalFixedLoc ) ;
plLayer * lay = fBoundsMat - > MakeBaseLayer ( ) ;
lay - > SetMiscFlags ( hsGMatState : : kMiscWireFrame | hsGMatState : : kMiscTwoSided ) ;
lay - > SetShadeFlags ( lay - > GetShadeFlags ( ) | hsGMatState : : kShadeWhite ) ;
// Set up a ref to these. Since we don't have a key, we use the
// generic RefObject() (and matching UnRefObject() when we're done).
// If we had a key, we would use myKey->AddViaNotify(otherKey) and myKey->Release(otherKey).
fBoundsMat - > GetKey ( ) - > RefObject ( ) ;
fBoundsSpans - > GetKey ( ) - > RefObject ( ) ;
# endif
return false ;
}
//// ISetCurrentDriver ////////////////////////////////////////////////////////
// Copy over the driver info.
void plDXPipeline : : ISetCurrentDriver ( D3DEnum_DriverInfo * driv )
{
if ( fCurrentDriver ! = nil )
delete fCurrentDriver ;
fCurrentDriver = TRACKED_NEW D3DEnum_DriverInfo ;
fCurrentDriver - > fGuid = driv - > fGuid ;
hsStrncpy ( fCurrentDriver - > fStrDesc , driv - > fStrDesc , 40 ) ;
hsStrncpy ( fCurrentDriver - > fStrName , driv - > fStrName , 40 ) ;
fCurrentDriver - > fDesktopMode = driv - > fDesktopMode ;
fCurrentDriver - > fAdapterInfo = driv - > fAdapterInfo ;
fCurrentDriver - > fCurrentMode = nil ;
fCurrentDriver - > fCurrentDevice = nil ;
/// Go looking for an adapter to match this one
UINT iAdapter ;
for ( fCurrentAdapter = 0 , iAdapter = 0 ; iAdapter < fD3DObject - > GetAdapterCount ( ) ; iAdapter + + )
{
D3DADAPTER_IDENTIFIER9 adapterInfo ;
fD3DObject - > GetAdapterIdentifier ( iAdapter , 0 , & adapterInfo ) ;
if ( adapterInfo . DeviceIdentifier = = fCurrentDriver - > fAdapterInfo . DeviceIdentifier )
{
fCurrentAdapter = iAdapter ;
break ;
}
}
}
//// ISetCurrentDevice ////////////////////////////////////////////////////////
// Copy over the device info.
void plDXPipeline : : ISetCurrentDevice ( D3DEnum_DeviceInfo * dev )
{
if ( fCurrentDevice ! = nil )
delete fCurrentDevice ;
fCurrentDevice = TRACKED_NEW D3DEnum_DeviceInfo ;
hsStrncpy ( fCurrentDevice - > fStrName , dev - > fStrName , 40 ) ;
fCurrentDevice - > fDDCaps = dev - > fDDCaps ;
fCurrentDevice - > fDDType = dev - > fDDType ;
fCurrentDevice - > fIsHardware = dev - > fIsHardware ;
fCurrentDevice - > fCanWindow = dev - > fCanWindow ;
// fCurrentDevice->fCanAntialias = dev->fCanAntialias;
fCurrentDevice - > fCompatibleWithDesktop = dev - > fCompatibleWithDesktop ;
// copy over supported device modes
D3DEnum_ModeInfo currMode ;
for ( int i = 0 ; i < dev - > fModes . Count ( ) ; i + + )
{
// filter unusable modes
if ( dev - > fModes [ i ] . fDDmode . Width < MIN_WIDTH | | dev - > fModes [ i ] . fDDmode . Height < MIN_HEIGHT )
continue ;
currMode . fBitDepth = dev - > fModes [ i ] . fBitDepth ;
currMode . fCanRenderToCubic = dev - > fModes [ i ] . fCanRenderToCubic ;
currMode . fDDBehavior = dev - > fModes [ i ] . fDDBehavior ;
currMode . fDepthFormats = dev - > fModes [ i ] . fDepthFormats ;
currMode . fFSAATypes = dev - > fModes [ i ] . fFSAATypes ;
memcpy ( & currMode . fDDmode , & dev - > fModes [ i ] . fDDmode , sizeof ( D3DDISPLAYMODE ) ) ;
strcpy ( currMode . fStrDesc , dev - > fModes [ i ] . fStrDesc ) ;
currMode . fWindowed = dev - > fModes [ i ] . fWindowed ;
fCurrentDevice - > fModes . Push ( currMode ) ;
}
}
//// ISetCurrentMode //////////////////////////////////////////////////////////
// Copy over the mode info.
void plDXPipeline : : ISetCurrentMode ( D3DEnum_ModeInfo * mode )
{
if ( fCurrentMode ! = nil )
delete fCurrentMode ;
fCurrentMode = TRACKED_NEW D3DEnum_ModeInfo ;
* fCurrentMode = * mode ;
}
//// IFindCompressedFormats ///////////////////////////////////////////////////
//
// New DX Way: Check to see if each format is valid.
hsBool plDXPipeline : : IFindCompressedFormats ( )
{
D3DFORMAT toCheckFor [ ] = { D3DFMT_DXT1 ,
//D3DFMT_DXT2,
//D3DFMT_DXT3,
//D3DFMT_DXT4,
D3DFMT_DXT5 ,
D3DFMT_UNKNOWN } ;
short i = 0 ;
for ( i = 0 ; toCheckFor [ i ] ! = D3DFMT_UNKNOWN ; i + + )
{
if ( FAILED ( fD3DObject - > CheckDeviceFormat ( fCurrentAdapter , fCurrentDevice - > fDDType ,
fCurrentMode - > fDDmode . Format ,
0 , D3DRTYPE_TEXTURE , toCheckFor [ i ] ) ) )
return false ;
}
/// Got here, must have found them all
return true ;
}
//// IFindLuminanceFormats ////////////////////////////////////////////////////
//
// New DX Way: Check to see if each format we want is valid
hsBool plDXPipeline : : IFindLuminanceFormats ( )
{
D3DFORMAT toCheckFor [ ] = { D3DFMT_L8 , D3DFMT_A8L8 , D3DFMT_UNKNOWN } ;
short i = 0 ;
for ( i = 0 ; toCheckFor [ i ] ! = D3DFMT_UNKNOWN ; i + + )
{
if ( FAILED ( fD3DObject - > CheckDeviceFormat ( fCurrentAdapter , fCurrentDevice - > fDDType ,
fCurrentMode - > fDDmode . Format ,
0 , D3DRTYPE_TEXTURE , toCheckFor [ i ] ) ) )
return false ;
}
/// Got here, must have found them all
return true ;
}
//// ITextureFormatAllowed ////////////////////////////////////////////////////
//
// Returns true if the given format is supported on the current device and
// mode, false if it isn't.
hsBool plDXPipeline : : ITextureFormatAllowed ( D3DFORMAT format )
{
if ( FAILED ( fD3DObject - > CheckDeviceFormat ( fCurrentAdapter , fCurrentDevice - > fDDType ,
fCurrentMode - > fDDmode . Format ,
0 , D3DRTYPE_TEXTURE , format ) ) )
return false ;
return true ;
}
//// SetDebugFlag /////////////////////////////////////////////////////////////
// Debug flags should never be employed to do a game effect, although they can
// be useful for developing effects. Mostly they help in diagnosing problems
// in rendering or performance.
void plDXPipeline : : SetDebugFlag ( UInt32 flag , hsBool on )
{
fDebugFlags . SetBit ( flag , on ) ;
if ( flag = = plPipeDbg : : kFlagColorizeMipmaps )
{
// Force textures to reload
plDXTextureRef * ref = fTextureRefList ;
while ( ref ! = nil )
{
ref - > SetDirty ( true ) ;
ref = ref - > GetNext ( ) ;
}
// Reset mipmap filtering state (usually is LINEAR, but we set it to POINT for coloring)
int i ;
for ( i = 0 ; i < 8 ; i + + )
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MIPFILTER , on ? D3DTEXF_POINT : D3DTEXF_LINEAR ) ;
}
if ( flag = = plPipeDbg : : kFlagNoAnisotropy )
{
ISetAnisotropy ( ! on ) ;
}
}
hsBool plDXPipeline : : IsDebugFlagSet ( UInt32 flag ) const
{
return fDebugFlags . IsBitSet ( flag ) ;
}
///////////////////////////////////////////////////////////////////////////////
//// Device Creation //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// ICreateMaster ////////////////////////////////////////////////////////////
// Creates the master Direct3D objects. I guess just in case you want
// multiple Direct3D devices.... :~
hsBool plDXPipeline : : ICreateMaster ( )
{
hsAssert ( ! fD3DObject , " ICreateMaster() should only be called for Master Direct3DDevice " ) ;
/// The new DirectX Way: Create a Direct3D object, out of which everything else springs
if ( hsGDDrawDllLoad : : GetD3DDll ( ) = = nil )
return ICreateFail ( " Cannot load Direct3D driver! " ) ;
Direct3DCreateProc procPtr ;
procPtr = ( Direct3DCreateProc ) GetProcAddress ( hsGDDrawDllLoad : : GetD3DDll ( ) , " Direct3DCreate9 " ) ;
if ( procPtr = = nil )
return ICreateFail ( " Cannot load D3D Create Proc! " ) ;
// Create a D3D object to use
fD3DObject = procPtr ( D3D_SDK_VERSION ) ;
if ( fD3DObject = = nil )
return ICreateFail ( " Cannot create Direct3D object " ) ;
return false ;
}
//// ICreateDevice ////////////////////////////////////////////////////
//
// Creates the device. Surfaces, buffers, etc. created separately (in case of lost device).
// See ICreateDeviceObjects.
hsBool plDXPipeline : : ICreateDevice ( hsBool windowed )
{
/// First, create the D3D Device object
D3DPRESENT_PARAMETERS params ;
D3DDISPLAYMODE dispMode ;
int i ;
# ifdef DBG_WRITE_FORMATS
char msg [ 256 ] ;
# endif // DBG_WRITE_FORMATS
INIT_ERROR_CHECK ( fD3DObject - > GetAdapterDisplayMode ( fCurrentAdapter , & dispMode ) ,
" Cannot get desktop display mode " ) ;
// save desktop properties
fDesktopParams . Width = dispMode . Width ;
fDesktopParams . Height = dispMode . Height ;
fDesktopParams . ColorDepth = GetDXBitDepth ( dispMode . Format ) ;
if ( windowed )
{
// Reset fColor, since we're getting the desktop bitdepth
fSettings . fColorDepth = GetDXBitDepth ( dispMode . Format ) ;
if ( fSettings . fOrigWidth > fDesktopParams . Width | | fSettings . fOrigHeight > fDesktopParams . Height )
{
fSettings . fOrigWidth = fDesktopParams . Width ;
fSettings . fOrigHeight = fDesktopParams . Height ;
IGetViewTransform ( ) . SetScreenSize ( fDesktopParams . Width , fDesktopParams . Height ) ;
}
}
memset ( & params , 0 , sizeof ( params ) ) ;
params . Windowed = ( windowed ? TRUE : FALSE ) ;
params . Flags = 0 ; //D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
params . BackBufferCount = 1 ;
params . BackBufferWidth = GetViewTransform ( ) . GetScreenWidth ( ) ;
params . BackBufferHeight = GetViewTransform ( ) . GetScreenHeight ( ) ;
params . EnableAutoDepthStencil = TRUE ;
// NOTE: This was changed 5.29.2001 mcn to avoid the nasty flashing bug on nVidia's 12.60 beta drivers
// SWAPEFFECT must be _DISCARD when using antialiasing, so we'll just go with _DISCARD for the time being. mf
params . SwapEffect = D3DSWAPEFFECT_DISCARD ;
params . FullScreen_RefreshRateInHz = ( windowed ? 0 : D3DPRESENT_RATE_DEFAULT ) ;
if ( windowed )
{
params . PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT ;
}
else
{
params . PresentationInterval = ( fVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE ) ;
}
# ifdef DBG_WRITE_FORMATS
for ( i = 0 ; i < fCurrentMode - > fDepthFormats . GetCount ( ) ; i + + )
{
sprintf ( msg , " -- Valid depth buffer format: %s " , IGetDXFormatName ( fCurrentMode - > fDepthFormats [ i ] ) ) ;
hsDebugMessage ( msg , 0 ) ;
}
# endif
// Attempt to find the closest AA setting we can
params . MultiSampleType = D3DMULTISAMPLE_NONE ;
for ( i = fSettings . fNumAASamples ; i > = 2 ; i - - )
{
if ( fCurrentMode - > fFSAATypes . Find ( ( D3DMULTISAMPLE_TYPE ) i ) ! = fCurrentMode - > fFSAATypes . kMissingIndex )
{
params . MultiSampleType = ( D3DMULTISAMPLE_TYPE ) i ;
break ;
}
}
if ( ! IFindDepthFormat ( params ) )
{
// If we haven't found a depth format, turn off multisampling and try it again.
params . MultiSampleType = D3DMULTISAMPLE_NONE ;
if ( ! IFindDepthFormat ( params ) )
// Okay, we're screwed here, we might as well bail.
return ICreateFail ( " Can't find a Depth Buffer format " ) ;
}
/// TEMP HACK--if we're running 16-bit z-buffer or below, use our z-bias (go figure, it works better
/// in 16-bit, worse in 24 and 32)
if ( params . AutoDepthStencilFormat = = D3DFMT_D15S1 | |
params . AutoDepthStencilFormat = = D3DFMT_D16 | |
params . AutoDepthStencilFormat = = D3DFMT_D16_LOCKABLE )
fSettings . fD3DCaps & = ~ kCapsZBias ;
# ifdef DBG_WRITE_FORMATS
sprintf ( msg , " -- Requesting depth buffer format: %s " , IGetDXFormatName ( params . AutoDepthStencilFormat ) ) ;
hsDebugMessage ( msg , 0 ) ;
# endif
params . BackBufferFormat = ( windowed ? dispMode . Format : fCurrentMode - > fDDmode . Format ) ;
# ifdef DBG_WRITE_FORMATS
sprintf ( msg , " -- Requesting back buffer format: %s " , IGetDXFormatName ( params . BackBufferFormat ) ) ;
hsDebugMessage ( msg , 0 ) ;
# endif
params . hDeviceWindow = fSettings . fHWnd ;
// Enable this to switch to a pure device.
// fCurrentMode->fDDBehavior |= D3DCREATE_PUREDEVICE;
// fCurrentMode->fDDBehavior |= D3DCREATE_DISABLE_DRIVER_MANAGEMENT;
# ifndef PLASMA_EXTERNAL_RELEASE
UINT adapter ;
for ( adapter = 0 ; adapter < fD3DObject - > GetAdapterCount ( ) ; adapter + + )
{
D3DADAPTER_IDENTIFIER9 id ;
fD3DObject - > GetAdapterIdentifier ( adapter , 0 , & id ) ;
// We should be matching against "NVIDIA NVPerfHUD", but the space
// in the description seems to be bogus. This seems to be a fair
// alternative
if ( strstr ( id . Description , " NVPerfHUD " ) )
{
// This won't actually use the REF device, but we ask for
// it as part of the handshake to let NVPerfHUD know we give
// it permission to analyze us.
fCurrentAdapter = adapter ;
fCurrentDevice - > fDDType = D3DDEVTYPE_REF ;
SetDebugFlag ( plPipeDbg : : kFlagNVPerfHUD , true ) ;
break ;
}
}
# endif // PLASMA_EXTERNAL_RELEASE
INIT_ERROR_CHECK ( fD3DObject - > CreateDevice ( fCurrentAdapter , fCurrentDevice - > fDDType ,
fSettings . fHWnd , fCurrentMode - > fDDBehavior ,
& params , & fD3DDevice ) ,
" Cannot create primary display surface via CreateDevice() " ) ;
fSettings . fPresentParams = params ;
// This bit matches up with the fManagedCutoff workaround for a problem
// with the NVidia drivers on win2k. Search for "GetVersionEx" in IRestrictCaps
// for more info.
UInt32 mem = fD3DDevice - > GetAvailableTextureMem ( ) ;
plProfile_IncCount ( TexTot , mem ) ;
const UInt32 kSingleFlush ( 40000000 ) ;
const UInt32 kDoubleFlush ( 24000000 ) ;
if ( fManagedCutoff )
{
if ( mem < 64000000 )
fManagedCutoff = kDoubleFlush ;
else
fManagedCutoff = kSingleFlush ;
}
return false ;
}
// IFindDepthFormat //////////////////////////////////////////////////////////////
// Look through available depth formats for the closest to what we want that
// will work.
hsBool plDXPipeline : : IFindDepthFormat ( D3DPRESENT_PARAMETERS & params )
{
// Okay, we're not using the stencil buffer right now, and it's bringing out
// some painful driver bugs on the GeForce2. So rather than go out of our way
// looking for trouble, we're going to look for a depth buffer with NO STENCIL.
int i ;
for ( i = fCurrentMode - > fDepthFormats . GetCount ( ) - 1 ; i > = 0 ; i - - )
{
D3DFORMAT fmt = fCurrentMode - > fDepthFormats [ i ] ;
if ( ( fmt = = D3DFMT_D32 )
| | ( fmt = = D3DFMT_D24X8 )
| | ( fmt = = D3DFMT_D16 ) )
{
HRESULT hr = fD3DObject - > CheckDeviceMultiSampleType ( fCurrentAdapter ,
fCurrentDevice - > fDDType ,
fmt ,
fCurrentMode - > fWindowed ? TRUE : FALSE ,
params . MultiSampleType , NULL ) ;
if ( ! FAILED ( hr ) )
{
params . AutoDepthStencilFormat = fmt ;
fStencil . fDepth = 0 ;
break ;
}
}
}
if ( i < 0 )
{
for ( i = fCurrentMode - > fDepthFormats . GetCount ( ) - 1 ; i > = 0 ; i - - )
{
D3DFORMAT fmt = fCurrentMode - > fDepthFormats [ i ] ;
if ( fmt = = D3DFMT_D15S1 | | fmt = = D3DFMT_D24X4S4 | | fmt = = D3DFMT_D24S8 )
{
HRESULT hr = fD3DObject - > CheckDeviceMultiSampleType ( fCurrentAdapter ,
fCurrentDevice - > fDDType ,
fmt ,
fCurrentMode - > fWindowed ? TRUE : FALSE ,
params . MultiSampleType , NULL ) ;
if ( ! FAILED ( hr ) )
{
params . AutoDepthStencilFormat = fmt ;
if ( fmt = = D3DFMT_D15S1 )
fStencil . fDepth = 1 ;
else if ( fmt = = D3DFMT_D24X4S4 )
fStencil . fDepth = 4 ;
else
fStencil . fDepth = 8 ;
break ;
}
}
}
}
return i > = 0 ;
}
// ICreateNormalSurfaces //////////////////////////////////////////////////////
// Create the primary color and depth buffers.
//
hsBool plDXPipeline : : ICreateNormalSurfaces ( )
{
/// Now get the backbuffer surface pointer
INIT_ERROR_CHECK ( fD3DDevice - > GetBackBuffer ( 0 , 0 , D3DBACKBUFFER_TYPE_MONO , & fD3DBackBuff ) ,
" Cannot get primary surface's back buffer " ) ;
/// And finally, get the main D3D surfaces (for restoring after rendertargets )
INIT_ERROR_CHECK ( fD3DDevice - > GetRenderTarget ( 0 , & fD3DMainSurface ) , " Cannot capture primary surface " ) ;
INIT_ERROR_CHECK ( fD3DDevice - > GetDepthStencilSurface ( & fD3DDepthSurface ) , " Cannot capture primary depth surface " ) ;
fSettings . fCurrD3DMainSurface = fD3DMainSurface ;
fSettings . fCurrD3DDepthSurface = fD3DDepthSurface ;
D3DSURF_MEMNEW ( fD3DMainSurface ) ;
D3DSURF_MEMNEW ( fD3DDepthSurface ) ;
D3DSURF_MEMNEW ( fD3DBackBuff ) ;
D3DSURFACE_DESC info ;
fD3DMainSurface - > GetDesc ( & info ) ;
fD3DDepthSurface - > GetDesc ( & info ) ;
fD3DBackBuff - > GetDesc ( & info ) ;
return false ;
}
// IReleaseRenderTargetPools //////////////////////////////////////////////////
// Free up all resources assosiated with our pools of rendertargets of varying
// sizes. Primary user of these pools is the shadow generation.
void plDXPipeline : : IReleaseRenderTargetPools ( )
{
int i ;
for ( i = 0 ; i < fRenderTargetPool512 . GetCount ( ) ; i + + )
{
delete fRenderTargetPool512 [ i ] ;
fRenderTargetPool512 [ i ] = nil ;
}
fRenderTargetPool512 . SetCount ( 0 ) ;
for ( i = 0 ; i < fRenderTargetPool256 . GetCount ( ) ; i + + )
{
delete fRenderTargetPool256 [ i ] ;
fRenderTargetPool256 [ i ] = nil ;
}
fRenderTargetPool256 . SetCount ( 0 ) ;
for ( i = 0 ; i < fRenderTargetPool128 . GetCount ( ) ; i + + )
{
delete fRenderTargetPool128 [ i ] ;
fRenderTargetPool128 [ i ] = nil ;
}
fRenderTargetPool128 . SetCount ( 0 ) ;
for ( i = 0 ; i < fRenderTargetPool64 . GetCount ( ) ; i + + )
{
delete fRenderTargetPool64 [ i ] ;
fRenderTargetPool64 [ i ] = nil ;
}
fRenderTargetPool64 . SetCount ( 0 ) ;
for ( i = 0 ; i < fRenderTargetPool32 . GetCount ( ) ; i + + )
{
delete fRenderTargetPool32 [ i ] ;
fRenderTargetPool32 [ i ] = nil ;
}
fRenderTargetPool32 . SetCount ( 0 ) ;
for ( i = 0 ; i < kMaxRenderTargetNext ; i + + )
{
fRenderTargetNext [ i ] = 0 ;
fBlurScratchRTs [ i ] = nil ;
fBlurDestRTs [ i ] = nil ;
}
# ifdef MF_ENABLE_HACKOFF
hackOffscreens . Reset ( ) ;
# endif // MF_ENABLE_HACKOFF
}
// IReleaseDynDeviceObjects //////////////////////////////////////////////
// Make sure we aren't holding on to anything, and release all of
// the D3D resources that we normally hang on to forever. Meaning things
// that persist through unloading one age and loading the next.
void plDXPipeline : : IReleaseDynDeviceObjects ( )
{
// We should do this earlier, but the textFont objects don't remove
// themselves from their parent objects yet
delete fDebugTextMgr ;
fDebugTextMgr = nil ;
if ( fD3DDevice )
{
fD3DDevice - > SetStreamSource ( 0 , nil , 0 , 0 ) ;
fD3DDevice - > SetIndices ( nil ) ;
}
/// Delete actual d3d objects
hsRefCnt_SafeUnRef ( fSettings . fCurrVertexBuffRef ) ;
fSettings . fCurrVertexBuffRef = nil ;
hsRefCnt_SafeUnRef ( fSettings . fCurrIndexBuffRef ) ;
fSettings . fCurrIndexBuffRef = nil ;
while ( fTextFontRefList )
delete fTextFontRefList ;
while ( fRenderTargetRefList )
{
plDXRenderTargetRef * rtRef = fRenderTargetRefList ;
rtRef - > Release ( ) ;
rtRef - > Unlink ( ) ;
}
// The shared dynamic vertex buffers used by things like objects skinned on CPU, or
// particle systems.
IReleaseDynamicBuffers ( ) ;
IReleaseAvRTPool ( ) ;
IReleaseRenderTargetPools ( ) ;
if ( fSharedDepthSurface [ 0 ] )
{
D3DSURF_MEMDEL ( fSharedDepthSurface [ 0 ] ) ;
ReleaseObject ( fSharedDepthSurface [ 0 ] ) ;
fSharedDepthFormat [ 0 ] = D3DFMT_UNKNOWN ;
}
if ( fSharedDepthSurface [ 1 ] )
{
D3DSURF_MEMDEL ( fSharedDepthSurface [ 1 ] ) ;
ReleaseObject ( fSharedDepthSurface [ 1 ] ) ;
fSharedDepthFormat [ 1 ] = D3DFMT_UNKNOWN ;
}
D3DSURF_MEMDEL ( fD3DMainSurface ) ;
D3DSURF_MEMDEL ( fD3DDepthSurface ) ;
D3DSURF_MEMDEL ( fD3DBackBuff ) ;
ReleaseObject ( fD3DBackBuff ) ;
ReleaseObject ( fD3DDepthSurface ) ;
ReleaseObject ( fD3DMainSurface ) ;
}
// IReleaseShaders ///////////////////////////////////////////////////////////////
// Delete our vertex and pixel shaders. Releasing the plasma ref will release the
// D3D handle.
void plDXPipeline : : IReleaseShaders ( )
{
while ( fVShaderRefList )
{
plDXVertexShader * ref = fVShaderRefList ;
ref - > Release ( ) ;
ref - > Unlink ( ) ;
}
while ( fPShaderRefList )
{
plDXPixelShader * ref = fPShaderRefList ;
ref - > Release ( ) ;
ref - > Unlink ( ) ;
}
}
//// IReleaseDeviceObjects ///////////////////////////////////////////////////////
// Release everything we've created. This is the main cleanup function.
void plDXPipeline : : IReleaseDeviceObjects ( )
{
plDXDeviceRef * ref ;
/// Delete d3d-dependent objects
# if MCN_BOUNDS_SPANS
if ( fBoundsSpans )
fBoundsSpans - > GetKey ( ) - > UnRefObject ( ) ;
fBoundsSpans = nil ;
if ( fBoundsMat )
fBoundsMat - > GetKey ( ) - > UnRefObject ( ) ;
fBoundsMat = nil ;
# endif
plStatusLogMgr : : GetInstance ( ) . SetDrawer ( nil ) ;
delete fLogDrawer ;
fLogDrawer = nil ;
IGetPixelScratch ( 0 ) ;
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
if ( fLayerRef [ i ] )
{
hsRefCnt_SafeUnRef ( fLayerRef [ i ] ) ;
fLayerRef [ i ] = nil ;
}
}
# ifdef MF_ENABLE_HACKOFF
//WHITE
hackOffscreens . SetCount ( 0 ) ;
# endif // MF_ENABLE_HACKOFF
if ( fULutTextureRef )
delete [ ] fULutTextureRef - > fData ;
hsRefCnt_SafeUnRef ( fULutTextureRef ) ;
fULutTextureRef = nil ;
while ( fVtxBuffRefList )
{
ref = fVtxBuffRefList ;
ref - > Release ( ) ;
ref - > Unlink ( ) ;
}
while ( fIdxBuffRefList )
{
ref = fIdxBuffRefList ;
ref - > Release ( ) ;
ref - > Unlink ( ) ;
}
while ( fTextureRefList )
{
ref = fTextureRefList ;
ref - > Release ( ) ;
ref - > Unlink ( ) ;
}
IReleaseShaders ( ) ;
fLights . Release ( ) ;
IReleaseDynDeviceObjects ( ) ;
delete fPlateMgr ;
fPlateMgr = nil ;
if ( fD3DDevice ! = nil )
{
LONG ret ;
while ( ret = fD3DDevice - > Release ( ) )
{
hsStatusMessageF ( " %d - Error releasing device " , ret ) ;
}
fD3DDevice = nil ;
}
if ( fD3DObject ! = nil )
{
LONG ret ;
while ( ret = fD3DObject - > Release ( ) )
{
hsStatusMessageF ( " %d - Error releasing Direct3D Object " , ret ) ;
}
fD3DObject = nil ;
}
fManagedAlloced = false ;
fAllocUnManaged = false ;
}
// IReleaseDynamicBuffers /////////////////////////////////////////////////
// Release everything we've created in POOL_DEFAULT.
// This is called on shutdown or when we lose the device. Search for D3DERR_DEVICELOST.
void plDXPipeline : : IReleaseDynamicBuffers ( )
{
// Workaround for ATI driver bug.
if ( fSettings . fBadManaged )
{
plDXTextureRef * tRef = fTextureRefList ;
while ( tRef )
{
tRef - > Release ( ) ;
tRef = tRef - > GetNext ( ) ;
}
}
plDXVertexBufferRef * vbRef = fVtxBuffRefList ;
while ( vbRef )
{
if ( vbRef - > Volatile ( ) & & vbRef - > fD3DBuffer )
{
vbRef - > fD3DBuffer - > Release ( ) ;
vbRef - > fD3DBuffer = nil ;
// Actually, if it's volatile, it's sharing the global dynamic vertex buff, so we're already
// accounting for the memory when we clear the global buffer.
//PROFILE_POOL_MEM(D3DPOOL_DEFAULT, vbRef->fCount * vbRef->fVertexSize, false, "VtxBuff");
}
// 9600 THRASH
else if ( fSettings . fBadManaged )
{
vbRef - > Release ( ) ;
}
vbRef = vbRef - > GetNext ( ) ;
}
plDXIndexBufferRef * iRef = fIdxBuffRefList ;
while ( iRef )
{
// If it's volatile, we have to release it.
// If it's not, we want to release it so
// we can make it volatile (D3DPOOL_DEFAULT)
if ( iRef - > fD3DBuffer )
{
iRef - > fD3DBuffer - > Release ( ) ;
iRef - > fD3DBuffer = nil ;
PROFILE_POOL_MEM ( iRef - > fPoolType , iRef - > fCount * sizeof ( UInt16 ) , false , " IndexBuff " ) ;
}
iRef = iRef - > GetNext ( ) ;
}
if ( fDynVtxBuff )
{
ReleaseObject ( fDynVtxBuff ) ;
PROFILE_POOL_MEM ( D3DPOOL_DEFAULT , fDynVtxSize , false , " DynVtxBuff " ) ;
fDynVtxBuff = nil ;
}
fNextDynVtx = 0 ;
fVtxRefTime + + ;
// PlateMgr has a POOL_DEFAULT vertex buffer for drawing quads.
if ( fPlateMgr )
fPlateMgr - > IReleaseGeometry ( ) ;
// Also has POOL_DEFAULT vertex buffer.
plDXTextFont : : ReleaseShared ( fD3DDevice ) ;
IReleaseBlurVBuffers ( ) ;
}
// ICreateDynamicBuffers /////////////////////////////////////////////////////
// Create the things we need in POOL_DEFAULT. We clump them into this function,
// because they must be created before anything in POOL_MANAGED.
// So we create these global POOL_DEFAULT objects here, then send out a message
// to the objects in the scene to create anything they need in POOL_DEFAULT,
// then go on to create things on POOL_MANAGED.
// Set LoadResources().
void plDXPipeline : : ICreateDynamicBuffers ( )
{
ICreateBlurVBuffers ( ) ;
plDXTextFont : : CreateShared ( fD3DDevice ) ;
if ( fPlateMgr )
fPlateMgr - > ICreateGeometry ( this ) ;
fNextDynVtx = 0 ;
fVtxRefTime + + ;
DWORD usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC ;
hsAssert ( ! fManagedAlloced , " Alloc default with managed alloc'd " ) ;
D3DPOOL poolType = D3DPOOL_DEFAULT ;
if ( fDynVtxSize )
{
PROFILE_POOL_MEM ( poolType , fDynVtxSize , true , " DynVtxBuff " ) ;
if ( FAILED ( fD3DDevice - > CreateVertexBuffer ( fDynVtxSize ,
usage ,
0 ,
poolType ,
& fDynVtxBuff , NULL ) ) )
{
hsAssert ( false , " Don't know what to do here. " ) ;
}
}
}
void plDXPipeline : : IPrintDeviceInitError ( )
{
char str [ 256 ] ;
char err [ 16 ] ;
switch ( plLocalization : : GetLanguage ( ) )
{
case plLocalization : : kFrench : strcpy ( err , " Erreur " ) ; strcpy ( str , " Erreur d'initialisation de votre carte graphique. Les valeurs par d<EFBFBD> faut de ses param<EFBFBD> tres ont <EFBFBD> t<EFBFBD> r<EFBFBD> tablis. " ) ; break ;
case plLocalization : : kGerman : strcpy ( err , " Fehler " ) ; strcpy ( str , " Bei der Initialisierung Ihrer Grafikkarte ist ein Fehler aufgetreten. Standardeinstellungen werden wiederhergestellt. " ) ; break ;
case plLocalization : : kSpanish : strcpy ( err , " Error " ) ; strcpy ( str , " Ocurri<EFBFBD> un error al inicializar tu tarjeta de v<EFBFBD> deo. Hemos restaurado los ajustes por defecto. " ) ; break ;
case plLocalization : : kItalian : strcpy ( err , " Errore " ) ; strcpy ( str , " Errore di inizializzazione della scheda video. Sono state ripristinate le impostazioni predefinite. " ) ; break ;
default : strcpy ( err , " Error " ) ; strcpy ( str , " There was an error initializing your video card. We have reset it to its Default settings. " ) ; break ;
}
hsMessageBox ( str , err , hsMessageBoxNormal , hsMessageBoxIconError ) ;
}
// Reset device creation parameters to default and write to ini file
void plDXPipeline : : IResetToDefaults ( D3DPRESENT_PARAMETERS * params )
{
// this will reset device parameters to default and make sure all other necessary parameters are updated
params - > BackBufferWidth = fDefaultPipeParams . Width ;
params - > BackBufferHeight = fDefaultPipeParams . Height ;
fSettings . fOrigWidth = fDefaultPipeParams . Width ;
fSettings . fOrigHeight = fDefaultPipeParams . Height ;
IGetViewTransform ( ) . SetScreenSize ( fDefaultPipeParams . Width , fDefaultPipeParams . Height ) ;
params - > BackBufferFormat = D3DFMT_X8R8G8B8 ;
fSettings . fColorDepth = fDefaultPipeParams . ColorDepth ;
int i ;
hsTArray < D3DEnum_ModeInfo > * modes = & fCurrentDevice - > fModes ;
for ( i = 0 ; i < modes - > Count ( ) ; i + + )
{
D3DEnum_ModeInfo * mode = & ( * modes ) [ i ] ;
if ( mode - > fDDmode . Width = = params - > BackBufferWidth & &
mode - > fDDmode . Height = = params - > BackBufferHeight & &
mode - > fBitDepth = = 32 )
{
ISetCurrentMode ( & ( * modes ) [ i ] ) ;
break ;
}
}
params - > Windowed = fDefaultPipeParams . Windowed ;
fSettings . fFullscreen = ! fDefaultPipeParams . Windowed ;
fCurrentMode - > fWindowed = fDefaultPipeParams . Windowed ;
// Attempt to find the closest AA setting we can
params - > MultiSampleType = D3DMULTISAMPLE_NONE ;
fSettings . fNumAASamples = 0 ;
for ( int i = fDefaultPipeParams . AntiAliasingAmount ; i > = 2 ; i - - )
{
if ( fCurrentMode - > fFSAATypes . Find ( ( D3DMULTISAMPLE_TYPE ) i ) ! = fCurrentMode - > fFSAATypes . kMissingIndex )
{
fSettings . fNumAASamples = i ;
params - > MultiSampleType = ( D3DMULTISAMPLE_TYPE ) i ;
break ;
}
}
fSettings . fMaxAnisotropicSamples = fDefaultPipeParams . AnisotropicLevel ;
fVSync = false ;
params - > PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE ;
plShadowCaster : : EnableShadowCast ( fDefaultPipeParams . Shadows ? true : false ) ;
plQuality : : SetQuality ( fDefaultPipeParams . VideoQuality ) ;
plQuality : : SetCapability ( fDefaultPipeParams . VideoQuality ) ;
plDynamicCamMap : : SetEnabled ( fDefaultPipeParams . PlanarReflections ? true : false ) ;
plBitmap : : SetGlobalLevelChopCount ( 2 - fDefaultPipeParams . TextureQuality ) ;
// adjust camera properties
plVirtualCam1 : : SetAspectRatio ( ( float ) fSettings . fOrigWidth / ( float ) fSettings . fOrigHeight ) ;
plVirtualCam1 : : SetFOV ( plVirtualCam1 : : GetFOVw ( ) , plVirtualCam1 : : GetFOVh ( ) ) ;
// fire off a message to the client so we can write defaults to the ini file, and adjust the window size
plKey clientKey = hsgResMgr : : ResMgr ( ) - > FindKey ( kClient_KEY ) ;
plClientMsg * clientMsg = TRACKED_NEW plClientMsg ( plClientMsg : : kSetGraphicsDefaults ) ;
clientMsg - > Send ( clientKey ) ;
}
// IResetDevice
// reset the device to its operational state.
// returns true if not ready yet, false if the reset was successful.
// All this is generally in response to a fullscreen alt-tab.
hsBool plDXPipeline : : IResetDevice ( )
{
hsBool fakeDevLost ( false ) ;
if ( fakeDevLost )
fDeviceLost = true ;
if ( fDeviceLost )
{
IClearShadowSlaves ( ) ;
ReleaseCapture ( ) ;
Sleep ( 100 ) ;
HRESULT coopLev = fD3DDevice - > TestCooperativeLevel ( ) ;
if ( coopLev = = D3DERR_DEVICELOST )
{
// Nothing to do yet.
return true ;
}
if ( fakeDevLost )
coopLev = D3DERR_DEVICENOTRESET ;
if ( coopLev = = D3DERR_DEVICENOTRESET | | fForceDeviceReset )
{
plStatusLog : : AddLineS ( " pipeline.log " , 0xffff0000 , " Resetting Device " ) ;
IReleaseDynDeviceObjects ( ) ;
if ( ! IFindDepthFormat ( fSettings . fPresentParams ) )
{
// If we haven't found a depth format, turn off multisampling and try it again.
fSettings . fPresentParams . MultiSampleType = D3DMULTISAMPLE_NONE ;
IFindDepthFormat ( fSettings . fPresentParams ) ;
}
HRESULT hr = fD3DDevice - > Reset ( & fSettings . fPresentParams ) ;
int count = 0 ;
while ( FAILED ( hr ) )
{
if ( count + + = = 25 )
{
IPrintDeviceInitError ( ) ;
IResetToDefaults ( & fSettings . fPresentParams ) ;
}
// Still not ready? This is bad.
// Until we called Reset(), we could make any D3D call we wanted,
// and it would turn into a no-op. But once we call Reset(), until
// the device really is reset, anything but TestCoop/Reset/Release
// has just become illegal. We've already released everything, Reset
// just failed, not much to do but wait and try again.
: : Sleep ( 250 ) ;
hr = fD3DDevice - > Reset ( & fSettings . fPresentParams ) ;
}
fSettings . fCurrFVFFormat = 0 ;
fSettings . fCurrVertexShader = NULL ;
fManagedAlloced = false ;
ICreateDynDeviceObjects ( ) ;
IInitDeviceState ( ) ;
/// Broadcast a message letting everyone know that we were recreated and that
/// all device-specific stuff needs to be recreated
plDeviceRecreateMsg * clean = TRACKED_NEW plDeviceRecreateMsg ( ) ;
plgDispatch : : MsgSend ( clean ) ;
SetCapture ( fSettings . fHWnd ) ;
}
fDevWasLost = true ;
fDeviceLost = false ;
// We return true here, even though we've successfully recreated, to take
// another spin around the update loop and give everyone a chance to
// get back in sync.
return true ;
}
return false ;
}
void plDXPipeline : : ResetDisplayDevice ( int Width , int Height , int ColorDepth , hsBool Windowed , int NumAASamples , int MaxAnisotropicSamples , hsBool VSync /* = false */ )
{
if ( fSettings . fPresentParams . BackBufferWidth = = Width & &
fSettings . fPresentParams . BackBufferHeight = = Height & &
( fSettings . fPresentParams . Windowed ? 1 : fSettings . fColorDepth = = ColorDepth ) & & // if we're windowed dont check color depth we just use the desktop colordepth
( ( fSettings . fPresentParams . Windowed & & Windowed ) | | ( ! fSettings . fPresentParams . Windowed & & ! Windowed ) ) & &
fSettings . fNumAASamples = = NumAASamples & &
fSettings . fMaxAnisotropicSamples = = MaxAnisotropicSamples & &
fVSync = = VSync
)
{
return ; // nothing has changed
}
fVSync = VSync ;
int i = 0 ;
hsTArray < D3DEnum_ModeInfo > * modes = & fCurrentDevice - > fModes ;
// check for supported resolution if we're not going to windowed mode
if ( ! Windowed )
{
for ( i = 0 ; i < modes - > Count ( ) ; i + + )
{
D3DEnum_ModeInfo * mode = & ( * modes ) [ i ] ;
if ( mode - > fDDmode . Width = = Width & &
mode - > fDDmode . Height = = Height & &
mode - > fBitDepth = = ColorDepth )
{
ISetCurrentMode ( & ( * modes ) [ i ] ) ;
break ;
}
}
}
if ( i ! = modes - > Count ( ) )
{
// Set Resolution
fSettings . fOrigWidth = Width ;
fSettings . fOrigHeight = Height ;
IGetViewTransform ( ) . SetScreenSize ( Width , Height ) ;
fSettings . fPresentParams . BackBufferWidth = Width ;
fSettings . fPresentParams . BackBufferHeight = Height ;
fSettings . fColorDepth = ColorDepth ;
fSettings . fPresentParams . BackBufferFormat = D3DFMT_X8R8G8B8 ;
}
// set windowed/fullscreen mode
fCurrentMode - > fWindowed = Windowed ;
fSettings . fPresentParams . Windowed = Windowed ;
fSettings . fFullscreen = ! Windowed ;
// set Antialiasing
fSettings . fNumAASamples = 0 ;
// Attempt to find the closest AA setting we can
fSettings . fPresentParams . MultiSampleType = D3DMULTISAMPLE_NONE ;
for ( i = NumAASamples ; i > = 2 ; i - - )
{
if ( fCurrentMode - > fFSAATypes . Find ( ( D3DMULTISAMPLE_TYPE ) i ) ! = fCurrentMode - > fFSAATypes . kMissingIndex )
{
fSettings . fNumAASamples = i ;
fSettings . fPresentParams . MultiSampleType = ( D3DMULTISAMPLE_TYPE ) i ;
break ;
}
}
if ( fSettings . fNumAASamples > 0 )
fSettings . fD3DCaps | = kCapsFSAntiAlias ;
else
fSettings . fD3DCaps & = ~ kCapsFSAntiAlias ;
// Set Anisotropic filtering
fSettings . fMaxAnisotropicSamples = MaxAnisotropicSamples ;
ISetAnisotropy ( MaxAnisotropicSamples > 0 ) ;
if ( Windowed )
{
fSettings . fPresentParams . PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT ;
}
else
{
fSettings . fPresentParams . PresentationInterval = ( fVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE ) ;
}
// Force a device reset
fDeviceLost = true ;
fForceDeviceReset = true ;
plVirtualCam1 : : SetAspectRatio ( ( float ) Width / ( float ) Height ) ;
plVirtualCam1 : : SetFOV ( plVirtualCam1 : : GetFOVw ( ) , plVirtualCam1 : : GetFOVh ( ) ) ;
IResetDevice ( ) ;
return ;
}
void plDXPipeline : : GetSupportedColorDepths ( hsTArray < int > & ColorDepths )
{
int i , j ;
// iterate through display modes
for ( i = 0 ; i < fCurrentDevice - > fModes . Count ( ) ; i + + )
{
// Check to see if color depth has been added already
for ( j = 0 ; j < ColorDepths . Count ( ) ; j + + )
{
if ( fCurrentDevice - > fModes [ i ] . fBitDepth = = ColorDepths [ i ] )
break ;
}
if ( j = = ColorDepths . Count ( ) )
{
//add it
ColorDepths . Push ( fCurrentDevice - > fModes [ i ] . fBitDepth ) ;
}
}
}
void plDXPipeline : : GetSupportedDisplayModes ( std : : vector < plDisplayMode > * res , int ColorDepth )
{
int i , j ;
std : : vector < plDisplayMode > supported ;
// loop through display modes
for ( i = 0 ; i < fCurrentDevice - > fModes . Count ( ) ; i + + )
{
if ( fCurrentDevice - > fModes [ i ] . fBitDepth = = ColorDepth )
{
// check for duplicate mode
for ( j = 0 ; j < supported . size ( ) ; j + + )
{
if ( supported [ j ] . Width = = fCurrentDevice - > fModes [ i ] . fDDmode . Width & & supported [ j ] . Height = = fCurrentDevice - > fModes [ i ] . fDDmode . Height )
break ;
}
if ( j = = supported . size ( ) )
{
// new mode, add it
plDisplayMode mode ;
mode . Width = fCurrentDevice - > fModes [ i ] . fDDmode . Width ;
mode . Height = fCurrentDevice - > fModes [ i ] . fDDmode . Height ;
mode . ColorDepth = ColorDepth ;
supported . push_back ( mode ) ;
}
}
}
* res = supported ;
}
// Get max anitialias for the specified displaymode
int plDXPipeline : : GetMaxAntiAlias ( int Width , int Height , int ColorDepth )
{
int max = 0 ;
D3DEnum_ModeInfo * pCurrMode = nil ;
hsTArray < D3DEnum_ModeInfo > * modes = & fCurrentDevice - > fModes ;
for ( int i = 0 ; i < modes - > Count ( ) ; i + + )
{
D3DEnum_ModeInfo * mode = & ( * modes ) [ i ] ;
if ( mode - > fDDmode . Width = = Width & &
mode - > fDDmode . Height = = Height & &
mode - > fBitDepth = = ColorDepth )
{
pCurrMode = mode ;
}
}
if ( pCurrMode )
{
for ( int i = 0 ; i < pCurrMode - > fFSAATypes . Count ( ) ; i + + )
{
if ( pCurrMode - > fFSAATypes [ i ] > max )
max = pCurrMode - > fFSAATypes [ i ] ;
}
}
return max ;
}
int plDXPipeline : : GetMaxAnisotropicSamples ( )
{
return fCurrentDevice ? fCurrentDevice - > fDDCaps . MaxAnisotropy : 0 ;
}
//// Resize ///////////////////////////////////////////////////////////////////
// Resize is fairly obsolete, having been replaced by IResetDevice, which is
// automatically called if needed on BeginRender.
// This Resize function used to serve as both to Resize the primary buffers and
// to restore after losing the device (alt-tab). It didn't actually do either
// very well, so I'm not sure why I haven't deleted it.
void plDXPipeline : : Resize ( UInt32 width , UInt32 height )
{
hsMatrix44 w2c , c2w , proj ;
HRESULT coopLev = fD3DDevice - > TestCooperativeLevel ( ) ;
if ( coopLev = = D3DERR_DEVICELOST )
{
/// Direct3D is reporting that we lost the device but are unable to reset
/// it yet, so ignore.
hsStatusMessage ( " Received Resize() request at an invalid time. Ignoring... \n " ) ;
return ;
}
if ( ! width & & ! height )
{
if ( D3D_OK = = coopLev )
return ;
IReleaseDynDeviceObjects ( ) ;
HRESULT hr = fD3DDevice - > Reset ( & fSettings . fPresentParams ) ;
fManagedAlloced = false ;
if ( ! FAILED ( hr ) )
{
ICreateDynDeviceObjects ( ) ;
IInitDeviceState ( ) ;
return ;
}
}
// Store some states that we *want* to restore back...
plViewTransform resetTransform = GetViewTransform ( ) ;
/// HACK: Don't recreate if we're windowed, bad things happen
/// Comment out this if if you want to test the crashing thing in windowed alt-tabbing
#if 0
if ( ( width = = 0 | | height = = 0 ) & & ! fSettings . fFullscreen )
return ;
# endif
// Destroy old
IReleaseDeviceObjects ( ) ;
// Reset width and height
if ( width ! = 0 & & height ! = 0 )
{
// Width and height of zero mean just recreate
fSettings . fOrigWidth = width ;
fSettings . fOrigHeight = height ;
IGetViewTransform ( ) . SetScreenSize ( ( UInt16 ) ( fSettings . fOrigWidth ) , ( UInt16 ) ( fSettings . fOrigHeight ) ) ;
resetTransform . SetScreenSize ( ( UInt16 ) ( fSettings . fOrigWidth ) , ( UInt16 ) ( fSettings . fOrigHeight ) ) ;
}
else
{
// Just for debug
hsStatusMessage ( " Recreating the pipeline... \n " ) ;
}
// Recreate
if ( ! fD3DObject )
{
if ( ICreateMaster ( ) )
{
IShowErrorMessage ( " Cannot create D3D master object " ) ;
return ;
}
}
// Go recreate surfaces and DX-dependent objects
if ( ICreateDeviceObjects ( ) )
{
IShowErrorMessage ( " Cannot create Direct3D device " ) ;
return ;
}
// Restore states
SetViewTransform ( resetTransform ) ;
IProjectionMatrixToD3D ( ) ;
/// Broadcast a message letting everyone know that we were recreated and that
/// all device-specific stuff needs to be recreated
plDeviceRecreateMsg * clean = TRACKED_NEW plDeviceRecreateMsg ( ) ;
plgDispatch : : MsgSend ( clean ) ;
}
///////////////////////////////////////////////////////////////////////////////
//// Debug Text ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// MakeTextFont /////////////////////////////////////////////////////////////
plTextFont * plDXPipeline : : MakeTextFont ( char * face , UInt16 size )
{
plTextFont * font ;
font = TRACKED_NEW plDXTextFont ( this , fD3DDevice ) ;
if ( font = = nil )
return nil ;
font - > Create ( face , size ) ;
font - > Link ( & fTextFontRefList ) ;
return font ;
}
///////////////////////////////////////////////////////////////////////////////
//// Drawable Stuff ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// Draw /////////////////////////////////////////////////////////////////////
// PreRender //////////////////////////////////////////////////////////////////
// Most of this is debugging stuff, drawing the bounds, drawing the normals, etc.
// The functional part is in IGetVisibleSpans, which creates a list of the visible (non-culled)
// span indices within this drawable.
// This is called once per render, and generally well before rendering begins (as part of the
// cull phase).
hsBool plDXPipeline : : PreRender ( plDrawable * drawable , hsTArray < Int16 > & visList , plVisMgr * visMgr )
{
plDrawableSpans * ds = plDrawableSpans : : ConvertNoRef ( drawable ) ;
if ( ! ds )
return false ;
if ( ( ds - > GetType ( ) & fView . fDrawableTypeMask ) = = 0 )
return false ;
IGetVisibleSpans ( ds , visList , visMgr ) ;
# if MCN_BOUNDS_SPANS
if ( ( drawable ! = fBoundsSpans ) & & IsDebugFlagSet ( plPipeDbg : : kFlagShowAllBounds ) )
{
const hsTArray < plSpan * > & spans = ds - > GetSpanArray ( ) ;
int i ;
for ( i = 0 ; i < visList . GetCount ( ) ; i + + )
{
/// Add a span to our boundsIce to show this
IAddBoundsSpan ( fBoundsSpans , & spans [ visList [ i ] ] - > fWorldBounds ) ;
}
}
else if ( ( drawable ! = fBoundsSpans ) & & IsDebugFlagSet ( plPipeDbg : : kFlagShowNormals ) )
{
const hsTArray < plSpan * > & spans = ds - > GetSpanArray ( ) ;
int i ;
for ( i = 0 ; i < visList . GetCount ( ) ; i + + )
{
/// Add a span to our boundsIce to show this
plIcicle * span = ( plIcicle * ) spans [ visList [ i ] ] ;
if ( span - > fTypeMask & plSpan : : kIcicleSpan )
{
IAddNormalsSpan ( fBoundsSpans , span , ( plDXVertexBufferRef * ) ds - > GetVertexRef ( span - > fGroupIdx , span - > fVBufferIdx ) , 0xff0000ff ) ;
}
}
}
# endif
# if MF_BOUNDS_LEVEL_ICE
if ( ( fSettings . fBoundsDrawLevel > = 0 ) & & ( drawable ! = fBoundsSpans ) )
{
hsTArray < Int16 > bndList ;
drawable - > GetSpaceTree ( ) - > HarvestLevel ( fSettings . fBoundsDrawLevel , bndList ) ;
int i ;
for ( i = 0 ; i < bndList . GetCount ( ) ; i + + )
{
IAddBoundsSpan ( fBoundsSpans , & hsBounds3Ext ( drawable - > GetSpaceTree ( ) - > GetNode ( bndList [ i ] ) . GetWorldBounds ( ) ) , 0xff000000 | ( 0xf < < ( ( fSettings . fBoundsDrawLevel % 6 ) < < 2 ) ) ) ;
}
}
# endif // MF_BOUNDS_LEVEL_ICE
return visList . GetCount ( ) > 0 ;
}
struct plSortFace
{
UInt16 fIdx [ 3 ] ;
hsScalar fDist ;
} ;
struct plCompSortFace : public std : : binary_function < plSortFace , plSortFace , bool >
{
bool operator ( ) ( const plSortFace & lhs , const plSortFace & rhs ) const
{
return lhs . fDist > rhs . fDist ;
}
} ;
// IAvatarSort /////////////////////////////////////////////////////////////////////////
// We handle avatar sort differently from the rest of the face sort. The reason is that
// within the single avatar index buffer, we want to only sort the faces of spans requesting
// a sort, and sort them in place.
// Contrast that with the normal scene translucency sort. There, we sort all the spans in a drawble,
// then we sort all the faces in that drawable, then for each span in the sorted span list, we extract
// the faces for that span appending onto the index buffer. This gives great efficiency because
// only the visible faces are sorted and they wind up packed into the front of the index buffer, which
// permits more batching. See plDrawableSpans::SortVisibleSpans.
// For the avatar, it's generally the case that all the avatar is visible or not, and there is only
// one material, so neither of those efficiencies is helpful. Moreover, for the avatar the faces we
// want sorted are a tiny subset of the avatar's faces. Moreover, and most importantly, for the avatar, we
// want to preserve the order that spans are drawn, so, for example, the opaque base head will always be
// drawn before the translucent hair fringe, which will always be drawn before the pink clear plastic baseball cap.
hsBool plDXPipeline : : IAvatarSort ( plDrawableSpans * d , const hsTArray < Int16 > & visList )
{
plProfile_BeginTiming ( AvatarSort ) ;
int i ;
for ( i = 0 ; i < visList . GetCount ( ) ; i + + )
{
hsAssert ( d - > GetSpan ( visList [ i ] ) - > fTypeMask & plSpan : : kIcicleSpan , " Unknown type for sorting faces " ) ;
plIcicle * span = ( plIcicle * ) d - > GetSpan ( visList [ i ] ) ;
if ( span - > fProps & plSpan : : kPartialSort )
{
hsAssert ( d - > GetBufferGroup ( span - > fGroupIdx ) - > AreIdxVolatile ( ) , " Badly setup buffer group - set PartialSort too late? " ) ;
const hsPoint3 viewPos = GetViewPositionWorld ( ) ;
plGBufferGroup * group = d - > GetBufferGroup ( span - > fGroupIdx ) ;
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) group - > GetVertexBufferRef ( span - > fVBufferIdx ) ;
const UInt8 * vdata = vRef - > fData ;
const UInt32 stride = vRef - > fVertexSize ;
const int numTris = span - > fILength / 3 ;
static hsTArray < plSortFace > sortScratch ;
sortScratch . SetCount ( numTris ) ;
plProfile_IncCount ( AvatarFaces , numTris ) ;
plSortFace * begin = sortScratch . AcquireArray ( ) ;
plSortFace * end = begin + numTris ;
//
// Have three very similar sorts here, differing only on where the "position" of
// each triangle is defined, either as the center of the triangle, the nearest
// point on the triangle, or the farthest point on the triangle.
// Having tried all three on the avatar (the only thing this sort is used on),
// the best results surprisingly came from using the center of the triangle.
UInt16 * indices = group - > GetIndexBufferData ( span - > fIBufferIdx ) + span - > fIStartIdx ;
int j ;
for ( j = 0 ; j < numTris ; j + + )
{
# if 1 // TRICENTER
UInt16 idx = * indices + + ;
sortScratch [ j ] . fIdx [ 0 ] = idx ;
hsPoint3 pos = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
idx = * indices + + ;
sortScratch [ j ] . fIdx [ 1 ] = idx ;
pos + = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
idx = * indices + + ;
sortScratch [ j ] . fIdx [ 2 ] = idx ;
pos + = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
pos * = 0.3333f ;
sortScratch [ j ] . fDist = hsVector3 ( & pos , & viewPos ) . MagnitudeSquared ( ) ;
# elif 0 // NEAREST
UInt16 idx = * indices + + ;
sortScratch [ j ] . fIdx [ 0 ] = idx ;
hsPoint3 pos = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
hsScalar dist = hsVector3 ( & pos , & viewPos ) . MagnitudeSquared ( ) ;
hsScalar minDist = dist ;
idx = * indices + + ;
sortScratch [ j ] . fIdx [ 1 ] = idx ;
pos = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
dist = hsVector3 ( & pos , & viewPos ) . MagnitudeSquared ( ) ;
if ( dist < minDist )
minDist = dist ;
idx = * indices + + ;
sortScratch [ j ] . fIdx [ 2 ] = idx ;
pos = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
dist = hsVector3 ( & pos , & viewPos ) . MagnitudeSquared ( ) ;
if ( dist < minDist )
minDist = dist ;
sortScratch [ j ] . fDist = minDist ;
# elif 1 // FURTHEST
UInt16 idx = * indices + + ;
sortScratch [ j ] . fIdx [ 0 ] = idx ;
hsPoint3 pos = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
hsScalar dist = hsVector3 ( & pos , & viewPos ) . MagnitudeSquared ( ) ;
hsScalar maxDist = dist ;
idx = * indices + + ;
sortScratch [ j ] . fIdx [ 1 ] = idx ;
pos = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
dist = hsVector3 ( & pos , & viewPos ) . MagnitudeSquared ( ) ;
if ( dist > maxDist )
maxDist = dist ;
idx = * indices + + ;
sortScratch [ j ] . fIdx [ 2 ] = idx ;
pos = * ( hsPoint3 * ) ( vdata + idx * stride ) ;
dist = hsVector3 ( & pos , & viewPos ) . MagnitudeSquared ( ) ;
if ( dist > maxDist )
maxDist = dist ;
sortScratch [ j ] . fDist = maxDist ;
# endif // SORTTYPES
}
std : : sort ( begin , end , plCompSortFace ( ) ) ;
indices = group - > GetIndexBufferData ( span - > fIBufferIdx ) + span - > fIStartIdx ;
plSortFace * iter = sortScratch . AcquireArray ( ) ; ;
for ( j = 0 ; j < numTris ; j + + )
{
* indices + + = iter - > fIdx [ 0 ] ;
* indices + + = iter - > fIdx [ 1 ] ;
* indices + + = iter - > fIdx [ 2 ] ;
iter + + ;
}
group - > DirtyIndexBuffer ( span - > fIBufferIdx ) ;
}
}
plProfile_EndTiming ( AvatarSort ) ;
return true ;
}
// PrepForRender //////////////////////////////////////////////////////////////////
// Make sure the given drawable and each of the spans to be drawn (as noted in the
// indices in visList) is ready to be rendered.
// This means:
// a) select which lights will be used for each span
// b) do any necessary sorting (if required, spans are already in sorted order in visList,
// so this only means face sorting).
// c) do any necessary software skinning.
// This is called once per render, and before any rendering actually starts. See plPageTreeMgr.cpp.
// So any preperation needs to last until rendering actually begins. So cached information, like
// which lights a span will use, needs to be stored on the span.
hsBool plDXPipeline : : PrepForRender ( plDrawable * d , hsTArray < Int16 > & visList , plVisMgr * visMgr )
{
plProfile_BeginTiming ( PrepDrawable ) ;
plDrawableSpans * drawable = plDrawableSpans : : ConvertNoRef ( d ) ;
if ( ! drawable )
{
plProfile_EndTiming ( PrepDrawable ) ;
return false ;
}
// Find our lights
ICheckLighting ( drawable , visList , visMgr ) ;
// Sort our faces
if ( drawable - > GetNativeProperty ( plDrawable : : kPropSortFaces ) )
{
drawable - > SortVisibleSpans ( visList , this ) ;
}
// Prep for render. This is gives the drawable a chance to
// do any last minute updates for its buffers, including
// generating particle tri lists.
drawable - > PrepForRender ( this ) ;
// Any skinning necessary
if ( ! ISoftwareVertexBlend ( drawable , visList ) )
{
plProfile_EndTiming ( PrepDrawable ) ;
return false ;
}
// Avatar face sorting happens after the software skin.
if ( drawable - > GetNativeProperty ( plDrawable : : kPropPartialSort ) )
{
IAvatarSort ( drawable , visList ) ;
}
plProfile_EndTiming ( PrepDrawable ) ;
return true ;
}
// Draw ///////////////////////////////////////////////////////////
// Convenience function for a drawable that needs to get drawn outside of
// the normal scene graph render (i.e. something not managed by the plPageTreeMgr).
// Not nearly as efficient, so only useful as a special case.
void plDXPipeline : : Draw ( plDrawable * d )
{
plDrawableSpans * ds = plDrawableSpans : : ConvertNoRef ( d ) ;
if ( ds )
{
if ( ( ds - > GetType ( ) & fView . fDrawableTypeMask ) = = 0 )
return ;
static hsTArray < Int16 > visList ;
PreRender ( ds , visList ) ;
PrepForRender ( ds , visList ) ;
Render ( ds , visList ) ;
}
}
// Render ////////////////////////////////////////////////////////////////////////////////
// The normal way to render a subset of a drawable.
// This assumes that PreRender and PrepForRender have already been called.
// Note that PreRender and PrepForRender are called once per drawable per render
// with a visList containing all of the spans which will be rendered, but
// Render itself may be called with multiple visList subsets which union to
// the visList passed into PreRender/PrepForRender. This happens when drawing
// sorted spans, because some spans from drawable B may be in the middle of
// the spans of drawable A, so the sequence would be:
//
// PreRender(A, ATotalVisList);
// PreRender(B, BTotalVisList);
// PrepForRender(A, ATotalVisList);
// PrepForRender(B, BTotalVisList);
// Render(A, AFarHalfVisList);
// Render(B, BTotalVisList);
// Render(A, ANearHalfVisList);
// See plPageTreeMgr, which handles all this.
void plDXPipeline : : Render ( plDrawable * d , const hsTArray < Int16 > & visList )
{
// Reset here, since we can push/pop renderTargets after BeginRender() but before
// this function, which necessitates this being called
if ( fView . fXformResetFlags ! = 0 )
ITransformsToD3D ( ) ;
plDrawableSpans * ds = plDrawableSpans : : ConvertNoRef ( d ) ;
if ( ds )
{
IRenderSpans ( ds , visList ) ;
}
}
//// BeginDrawable ////////////////////////////////////////////////////////////
// Obsolete, should be removed
hsBool plDXPipeline : : BeginDrawable ( plDrawable * d )
{
return true ;
}
//// EndDrawable //////////////////////////////////////////////////////////////
// Obsolete, should be removed
hsBool plDXPipeline : : EndDrawable ( plDrawable * d )
{
return true ;
}
// IMakeLightLists ///////////////////////////////////////////////////////////
// Look through all the current lights, and fill out two lists.
// Only active lights (not disabled, not exactly black, and not
// ignored because of visibility regions by plVisMgr) will
// be considered.
// The first list is lights that will affect the avatar and similar
// indeterminately mobile (physical) objects - fLights.fCharLights.
// The second list is lights that aren't restricted by light include
// lists.
// These two abbreviated lists will be further refined for each object
// and avatar to find the strongest 8 lights which affect that object.
// A light with an include list, or LightGroup Component) has
// been explicitly told which objects it affects, so they don't
// need to be in the search lists.
// These lists are only constructed once per render, but searched
// multiple times
void plDXPipeline : : IMakeLightLists ( plVisMgr * visMgr )
{
plProfile_BeginTiming ( FindSceneLights ) ;
fLights . fCharLights . SetCount ( 0 ) ;
fLights . fVisLights . SetCount ( 0 ) ;
if ( visMgr )
{
const hsBitVector & visSet = visMgr - > GetVisSet ( ) ;
const hsBitVector & visNot = visMgr - > GetVisNot ( ) ;
plLightInfo * light ;
for ( light = fLights . fActiveList ; light ! = nil ; light = light - > GetNext ( ) )
{
plProfile_IncCount ( LightActive , 1 ) ;
if ( ! light - > IsIdle ( ) & & ! light - > InVisNot ( visNot ) & & light - > InVisSet ( visSet ) )
{
plProfile_IncCount ( LightOn , 1 ) ;
if ( light - > GetProperty ( plLightInfo : : kLPHasIncludes ) )
{
if ( light - > GetProperty ( plLightInfo : : kLPIncludesChars ) )
fLights . fCharLights . Append ( light ) ;
}
else
{
fLights . fVisLights . Append ( light ) ;
fLights . fCharLights . Append ( light ) ;
}
}
}
}
else
{
plLightInfo * light ;
for ( light = fLights . fActiveList ; light ! = nil ; light = light - > GetNext ( ) )
{
plProfile_IncCount ( LightActive , 1 ) ;
if ( ! light - > IsIdle ( ) )
{
plProfile_IncCount ( LightOn , 1 ) ;
if ( light - > GetProperty ( plLightInfo : : kLPHasIncludes ) )
{
if ( light - > GetProperty ( plLightInfo : : kLPIncludesChars ) )
fLights . fCharLights . Append ( light ) ;
}
else
{
fLights . fVisLights . Append ( light ) ;
fLights . fCharLights . Append ( light ) ;
}
}
}
}
plProfile_IncCount ( LightVis , fLights . fVisLights . GetCount ( ) ) ;
plProfile_IncCount ( LightChar , fLights . fCharLights . GetCount ( ) ) ;
plProfile_EndTiming ( FindSceneLights ) ;
}
// BeginVisMgr /////////////////////////////////////////////////////////
// Marks the beginning of a render with the given visibility manager.
// In particular, we cache which lights the visMgr believes to be
// currently active
void plDXPipeline : : BeginVisMgr ( plVisMgr * visMgr )
{
IMakeLightLists ( visMgr ) ;
}
// EndVisMgr ///////////////////////////////////////////////////////////
// Marks the end of a render with the given visibility manager.
void plDXPipeline : : EndVisMgr ( plVisMgr * visMgr )
{
fLights . fCharLights . SetCount ( 0 ) ;
fLights . fVisLights . SetCount ( 0 ) ;
}
// ICheckLighting ///////////////////////////////////////////////////////
// For every span in the list of visible span indices, find the list of
// lights that currently affect the span with an estimate of the strength
// of how much the light affects it. The strongest 8 lights will be used
// to illuminate that span.
// For projective lights, there is no limit on how many are supported, other
// than performance (usually fill rate limited).
// The permaLights and permaProjs are lights explicitly selected for a span
// via the LightGroup component.
// For static objects and static lights, the lighting was done offline and stored
// in the vertex diffuse color.
// So here we're only looking for:
// A) moving objects, which can't be staticly lit, so are affected by all runtime lights.
// B) moving lights, which can't staticly light, so affect all objects
// C) specular objects + specular lights, since specular can't be precomputed.
void plDXPipeline : : ICheckLighting ( plDrawableSpans * drawable , hsTArray < Int16 > & visList , plVisMgr * visMgr )
{
if ( fView . fRenderState & kRenderNoLights )
return ;
if ( ! visList . GetCount ( ) )
return ;
plLightInfo * light ;
int j ;
// First add in the explicit lights (from LightGroups).
// Refresh the lights as they are added (actually a lazy eval).
plProfile_BeginTiming ( FindLights ) ;
plProfile_BeginTiming ( FindPerm ) ;
for ( j = 0 ; j < visList . GetCount ( ) ; j + + )
{
drawable - > GetSpan ( visList [ j ] ) - > ClearLights ( ) ;
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoRuntimeLights ) )
continue ;
// Set the bits for the lights added from the permanent lists (during ClearLights()).
int k ;
const hsTArray < plLightInfo * > & permaLights = drawable - > GetSpan ( visList [ j ] ) - > fPermaLights ;
for ( k = 0 ; k < permaLights . GetCount ( ) ; k + + )
{
permaLights [ k ] - > Refresh ( ) ;
if ( permaLights [ k ] - > GetProperty ( plLightInfo : : kLPShadowLightGroup ) & & ! permaLights [ k ] - > IsIdle ( ) )
{
// If it casts a shadow, attach the shadow now.
ISetShadowFromGroup ( drawable , drawable - > GetSpan ( visList [ j ] ) , permaLights [ k ] ) ;
}
}
const hsTArray < plLightInfo * > & permaProjs = drawable - > GetSpan ( visList [ j ] ) - > fPermaProjs ;
for ( k = 0 ; k < permaProjs . GetCount ( ) ; k + + )
{
permaProjs [ k ] - > Refresh ( ) ;
if ( permaProjs [ k ] - > GetProperty ( plLightInfo : : kLPShadowLightGroup ) & & ! permaProjs [ k ] - > IsIdle ( ) )
{
// If it casts a shadow, attach the shadow now.
ISetShadowFromGroup ( drawable , drawable - > GetSpan ( visList [ j ] ) , permaProjs [ k ] ) ;
}
}
}
plProfile_EndTiming ( FindPerm ) ;
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoRuntimeLights ) )
{
plProfile_EndTiming ( FindLights ) ;
return ;
}
// Sort the incoming spans as either
// A) moving - affected by all lights - moveList
// B) specular - affected by specular lights - specList
// C) visible - affected by moving lights - visList
static hsTArray < Int16 > tmpList ;
static hsTArray < Int16 > moveList ;
static hsTArray < Int16 > specList ;
moveList . SetCount ( 0 ) ;
specList . SetCount ( 0 ) ;
plProfile_BeginTiming ( FindSpan ) ;
int k ;
for ( k = 0 ; k < visList . GetCount ( ) ; k + + )
{
const plSpan * span = drawable - > GetSpan ( visList [ k ] ) ;
if ( span - > fProps & plSpan : : kPropRunTimeLight )
{
moveList . Append ( visList [ k ] ) ;
specList . Append ( visList [ k ] ) ;
}
else if ( span - > fProps & plSpan : : kPropMatHasSpecular )
specList . Append ( visList [ k ] ) ;
}
plProfile_EndTiming ( FindSpan ) ;
// Make a list of lights that can potentially affect spans in this drawable
// based on the drawables bounds and properties.
// If the drawable has the PropCharacter property, it is affected by lights
// in fLights.fCharLights, else only by the smaller list of fLights.fVisLights.
plProfile_BeginTiming ( FindActiveLights ) ;
static hsTArray < plLightInfo * > lightList ;
lightList . SetCount ( 0 ) ;
const hsBool isChar = 0 ! = drawable - > GetNativeProperty ( plDrawable : : kPropCharacter ) ;
if ( isChar )
{
int i ;
for ( i = 0 ; i < fLights . fCharLights . GetCount ( ) ; i + + )
{
if ( fLights . fCharLights [ i ] - > AffectsBound ( drawable - > GetSpaceTree ( ) - > GetWorldBounds ( ) ) )
lightList . Append ( fLights . fCharLights [ i ] ) ;
}
}
else
{
int i ;
for ( i = 0 ; i < fLights . fVisLights . GetCount ( ) ; i + + )
{
if ( fLights . fVisLights [ i ] - > AffectsBound ( drawable - > GetSpaceTree ( ) - > GetWorldBounds ( ) ) )
lightList . Append ( fLights . fVisLights [ i ] ) ;
}
}
plProfile_EndTiming ( FindActiveLights ) ;
// Loop over the lights and for each light, extract a list of the spans that light
// affects. Append the light to each spans list with a scalar strength of how strongly
// the light affects it. Since the strength is based on the object's center position,
// it's not very accurate, but good enough for selecting which lights to use.
plProfile_BeginTiming ( ApplyActiveLights ) ;
for ( k = 0 ; k < lightList . GetCount ( ) ; k + + )
{
light = lightList [ k ] ;
tmpList . SetCount ( 0 ) ;
if ( light - > GetProperty ( plLightInfo : : kLPMovable ) )
{
plProfile_BeginTiming ( ApplyMoving ) ;
const hsTArray < Int16 > & litList = light - > GetAffected ( drawable - > GetSpaceTree ( ) ,
visList ,
tmpList ,
drawable - > GetNativeProperty ( plDrawable : : kPropCharacter ) ) ;
// PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!!
hsBool proj = nil ! = light - > GetProjection ( ) ;
if ( fView . fRenderState & kRenderNoProjection )
proj = false ;
for ( j = 0 ; j < litList . GetCount ( ) ; j + + )
{
// Use the light IF light is enabled and
// 1) light is movable
// 2) span is movable, or
// 3) Both the light and the span have specular
const plSpan * span = drawable - > GetSpan ( litList [ j ] ) ;
hsBool currProj = proj ;
if ( span - > fProps & plSpan : : kPropProjAsVtx )
currProj = false ;
if ( ! ( currProj & & ( span - > fProps & plSpan : : kPropSkipProjection ) ) )
{
plDXLightRef * ref = ( plDXLightRef * ) light - > GetDeviceRef ( ) ;
hsScalar strength , scale ;
light - > GetStrengthAndScale ( span - > fWorldBounds , strength , scale ) ;
// We can't pitch a light because it's "strength" is zero, because the strength is based
// on the center of the span and isn't conservative enough. We can pitch based on the
// scale though, since a light scaled down to zero will have no effect no where.
if ( scale > 0 )
{
plProfile_Inc ( FindLightsFound ) ;
span - > AddLight ( light , strength , scale , currProj ) ;
}
}
}
plProfile_EndTiming ( ApplyMoving ) ;
}
else if ( light - > GetProperty ( plLightInfo : : kLPHasSpecular ) )
{
if ( ! specList . GetCount ( ) )
continue ;
plProfile_BeginTiming ( ApplyToSpec ) ;
const hsTArray < Int16 > & litList = light - > GetAffected ( drawable - > GetSpaceTree ( ) ,
specList ,
tmpList ,
drawable - > GetNativeProperty ( plDrawable : : kPropCharacter ) ) ;
// PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!!
hsBool proj = nil ! = light - > GetProjection ( ) ;
if ( fView . fRenderState & kRenderNoProjection )
proj = false ;
for ( j = 0 ; j < litList . GetCount ( ) ; j + + )
{
// Use the light IF light is enabled and
// 1) light is movable
// 2) span is movable, or
// 3) Both the light and the span have specular
const plSpan * span = drawable - > GetSpan ( litList [ j ] ) ;
hsBool currProj = proj ;
if ( span - > fProps & plSpan : : kPropProjAsVtx )
currProj = false ;
if ( ! ( currProj & & ( span - > fProps & plSpan : : kPropSkipProjection ) ) )
{
plDXLightRef * ref = ( plDXLightRef * ) light - > GetDeviceRef ( ) ;
hsScalar strength , scale ;
light - > GetStrengthAndScale ( span - > fWorldBounds , strength , scale ) ;
// We can't pitch a light because it's "strength" is zero, because the strength is based
// on the center of the span and isn't conservative enough. We can pitch based on the
// scale though, since a light scaled down to zero will have no effect no where.
if ( scale > 0 )
{
plProfile_Inc ( FindLightsFound ) ;
span - > AddLight ( light , strength , scale , currProj ) ;
}
}
}
plProfile_EndTiming ( ApplyToSpec ) ;
}
else
{
if ( ! moveList . GetCount ( ) )
continue ;
plProfile_BeginTiming ( ApplyToMoving ) ;
const hsTArray < Int16 > & litList = light - > GetAffected ( drawable - > GetSpaceTree ( ) ,
moveList ,
tmpList ,
drawable - > GetNativeProperty ( plDrawable : : kPropCharacter ) ) ;
// PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!!
hsBool proj = nil ! = light - > GetProjection ( ) ;
if ( fView . fRenderState & kRenderNoProjection )
proj = false ;
for ( j = 0 ; j < litList . GetCount ( ) ; j + + )
{
// Use the light IF light is enabled and
// 1) light is movable
// 2) span is movable, or
// 3) Both the light and the span have specular
const plSpan * span = drawable - > GetSpan ( litList [ j ] ) ;
hsBool currProj = proj ;
if ( span - > fProps & plSpan : : kPropProjAsVtx )
currProj = false ;
if ( ! ( currProj & & ( span - > fProps & plSpan : : kPropSkipProjection ) ) )
{
plDXLightRef * ref = ( plDXLightRef * ) light - > GetDeviceRef ( ) ;
hsScalar strength , scale ;
light - > GetStrengthAndScale ( span - > fWorldBounds , strength , scale ) ;
// We can't pitch a light because it's "strength" is zero, because the strength is based
// on the center of the span and isn't conservative enough. We can pitch based on the
// scale though, since a light scaled down to zero will have no effect no where.
if ( scale > 0 )
{
plProfile_Inc ( FindLightsFound ) ;
span - > AddLight ( light , strength , scale , currProj ) ;
}
}
}
plProfile_EndTiming ( ApplyToMoving ) ;
}
}
plProfile_EndTiming ( ApplyActiveLights ) ;
IAttachShadowsToReceivers ( drawable , visList ) ;
plProfile_EndTiming ( FindLights ) ;
}
// HarvestVisible ////////////////////////////////////////////////////////////////////////
// Contruct a list of the indices of leaf nodes in the given spacetree which are currently
// visible according to the current cull tree. The cull tree factors in camera frustum and
// occluder polys, but _not_ the current visibility regions, plVisMgr.
// This is the normal path for visibility culling at a gross level (e.g. which SceneNodes
// to bother with, which drawables within the SceneNode). For finer objects, like the spans
// themselves, the culling is done via IGetVisibleSpans, which also takes the plVisMgr into
// account.
hsBool plDXPipeline : : HarvestVisible ( plSpaceTree * space , hsTArray < Int16 > & visList )
{
if ( ! space )
return false ;
space - > SetViewPos ( GetViewPositionWorld ( ) ) ;
space - > Refresh ( ) ;
if ( fView . fCullTreeDirty )
IRefreshCullTree ( ) ;
plProfile_BeginTiming ( Harvest ) ;
fView . fCullTree . Harvest ( space , visList ) ;
plProfile_EndTiming ( Harvest ) ;
return visList . GetCount ( ) ! = 0 ;
}
//// IGetVisibleSpans /////////////////////////////////////////////////////
// Given a drawable, returns a list of visible span indices. Disabled spans will not
// show up in the list, behaving as if they were culled.
// See plCullTree (in plPipeline) and plSpaceTree (in plDrawable) and plVisMgr (in plScene).
void plDXPipeline : : IGetVisibleSpans ( plDrawableSpans * drawable , hsTArray < Int16 > & visList , plVisMgr * visMgr )
{
static hsTArray < Int16 > tmpVis ;
tmpVis . SetCount ( 0 ) ;
visList . SetCount ( 0 ) ;
drawable - > GetSpaceTree ( ) - > SetViewPos ( GetViewPositionWorld ( ) ) ;
drawable - > GetSpaceTree ( ) - > Refresh ( ) ;
if ( fView . fCullTreeDirty )
IRefreshCullTree ( ) ;
const hsScalar viewDist = GetViewDirWorld ( ) . InnerProduct ( GetViewPositionWorld ( ) ) ;
const hsTArray < plSpan * > & spans = drawable - > GetSpanArray ( ) ;
plProfile_BeginTiming ( Harvest ) ;
if ( visMgr )
{
drawable - > SetVisSet ( visMgr ) ;
fView . fCullTree . Harvest ( drawable - > GetSpaceTree ( ) , tmpVis ) ;
drawable - > SetVisSet ( nil ) ;
}
else
{
fView . fCullTree . Harvest ( drawable - > GetSpaceTree ( ) , tmpVis ) ;
}
// This is a big waste of time, As a desparate "optimization" pass, the artists
// insist on going through and marking objects to fade or pop out of rendering
// past a certain distance. This breaks the batching and requires more CPU to
// check the objects by distance. Since there is no pattern to the distance at
// which objects will be told not to draw, there's no way to make this hierarchical,
// which is what it would take to make it a performance win. So they succeed in
// reducing the poly count, but generally the frame rate goes _down_ as well.
// Unfortunately, this technique actually does work in a few key areas, so
// I haven't been able to purge it.
if ( IsDebugFlagSet ( plPipeDbg : : kFlagSkipVisDist ) )
{
int i ;
for ( i = 0 ; i < tmpVis . GetCount ( ) ; i + + )
{
if ( spans [ tmpVis [ i ] ] - > fSubType & GetSubDrawableTypeMask ( ) )
{
visList . Append ( tmpVis [ i ] ) ;
}
}
}
else
{
int i ;
for ( i = 0 ; i < tmpVis . GetCount ( ) ; i + + )
{
if ( spans [ tmpVis [ i ] ] - > fSubType & GetSubDrawableTypeMask ( ) )
{
// We'll check here for spans we can discard because they've completely distance faded out.
// Note this is based on view direction distance (because the fade is), rather than the
// preferrable distance to camera we sort by.
hsScalar minDist , maxDist ;
if ( drawable - > GetSubVisDists ( tmpVis [ i ] , minDist , maxDist ) )
{
const hsBounds3Ext & bnd = drawable - > GetSpaceTree ( ) - > GetNode ( tmpVis [ i ] ) . fWorldBounds ;
hsPoint2 depth ;
bnd . TestPlane ( GetViewDirWorld ( ) , depth ) ;
if ( ( 0 < minDist + viewDist - depth . fY )
| | ( 0 > maxDist + viewDist - depth . fX ) )
continue ;
}
visList . Append ( tmpVis [ i ] ) ;
}
}
}
plProfile_EndTiming ( Harvest ) ;
}
// ISetupTransforms //////////////////////////////////////////////////////////////////////////////////
// Set the D3D world transform according to the input span.
// Engine currently supports HW vertex blending with 2 matrices,
// else a single Local To World.
// If software skinning is being used, the WORLD matrix will be identity,
// because the full local to world is folded into the skinned vertices.
void plDXPipeline : : ISetupTransforms ( plDrawableSpans * drawable , const plSpan & span , hsMatrix44 & lastL2W )
{
if ( span . fNumMatrices )
{
if ( span . fNumMatrices < = 2 )
{
ISetLocalToWorld ( span . fLocalToWorld , span . fWorldToLocal ) ;
lastL2W = span . fLocalToWorld ;
}
else
{
lastL2W . Reset ( ) ;
ISetLocalToWorld ( lastL2W , lastL2W ) ;
fView . fLocalToWorldLeftHanded = span . fLocalToWorld . GetParity ( ) ;
}
}
else
if ( lastL2W ! = span . fLocalToWorld )
{
ISetLocalToWorld ( span . fLocalToWorld , span . fWorldToLocal ) ;
lastL2W = span . fLocalToWorld ;
}
else
{
fView . fLocalToWorldLeftHanded = lastL2W . GetParity ( ) ;
}
if ( span . fNumMatrices = = 2 )
{
D3DXMATRIX mat ;
IMatrix44ToD3DMatrix ( mat , drawable - > GetPaletteMatrix ( span . fBaseMatrix + 1 ) ) ;
fD3DDevice - > SetTransform ( D3DTS_WORLDMATRIX ( 1 ) , & mat ) ;
fD3DDevice - > SetRenderState ( D3DRS_VERTEXBLEND , D3DVBF_1WEIGHTS ) ;
}
else
{
fD3DDevice - > SetRenderState ( D3DRS_VERTEXBLEND , D3DVBF_DISABLE ) ;
}
}
// IRefreshDynVertices ////////////////////////////////////////////////////////////////////////
// All dynamic vertices share a single dynamic vertex buffer. They are cycled through
// that buffer using the NOOVERWRITE/DISCARD paradigm. Since the vertices sharing that
// buffer may be of different formats, care is taken to always start a group of vertices
// a the next available position in the buffer aligned with that vertex size.
// Only software skinned objects, dynamic decals, and particle systems currently use the
// dynamic vertex buffer.
hsBool plDXPipeline : : IRefreshDynVertices ( plGBufferGroup * group , plDXVertexBufferRef * vRef )
{
// First, pad out our next slot to be on a vertex boundary (for this vertex size).
fNextDynVtx = ( ( fNextDynVtx + vRef - > fVertexSize - 1 ) / vRef - > fVertexSize ) * vRef - > fVertexSize ;
Int32 size = ( group - > GetVertBufferEnd ( vRef - > fIndex ) - group - > GetVertBufferStart ( vRef - > fIndex ) ) * vRef - > fVertexSize ;
if ( ! size )
return false ; // No error, just nothing to do.
hsAssert ( size > 0 , " Bad start and end counts in a group " ) ;
// If we DON'T have room in our dynamic buffer
if ( fNextDynVtx + size > fDynVtxSize )
{
plProfile_IncCount ( DynVBuffs , 1 ) ;
// Advance the timestamp, because we're about to reuse the buffer
fVtxRefTime + + ;
// Reset next available spot index to zero
fNextDynVtx = 0 ;
}
// Point our ref at the next available spot
Int32 newStart = fNextDynVtx / vRef - > fVertexSize ;
vRef - > fOffset = newStart - group - > GetVertBufferStart ( vRef - > fIndex ) ;
// Lock the buffer
// If index is zero, lock with discard, else with overwrite.
DWORD lockFlag = fNextDynVtx ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD ;
UInt8 * destPtr = nil ;
if ( FAILED ( fDynVtxBuff - > Lock ( fNextDynVtx ,
size ,
( void * * ) & destPtr ,
lockFlag ) ) )
{
hsAssert ( false , " Cannot lock vertex buffer for writing " ) ;
return true ;
}
UInt8 * vData ;
if ( vRef - > fData )
{
vData = vRef - > fData ;
}
else
{
vData = group - > GetVertBufferData ( vRef - > fIndex ) + group - > GetVertBufferStart ( vRef - > fIndex ) * vRef - > fVertexSize ;
}
memcpy ( destPtr , vData , size ) ;
// Unlock the buffer
fDynVtxBuff - > Unlock ( ) ;
// Advance next available spot index
fNextDynVtx + = size ;
// Set the timestamp
vRef - > fRefTime = fVtxRefTime ;
vRef - > SetDirty ( false ) ;
if ( ! vRef - > fD3DBuffer )
{
vRef - > fD3DBuffer = fDynVtxBuff ;
fDynVtxBuff - > AddRef ( ) ;
}
hsAssert ( vRef - > fD3DBuffer = = fDynVtxBuff , " Holding on to an old dynamic buffer? " ) ;
// vRef->SetRebuiltSinceUsed(true);
return false ;
}
// ICheckAuxBuffers ///////////////////////////////////////////////////////////////////////
// The AuxBuffers are associated with drawables for things to be drawn right after that
// drawable's contents. In particular, see the plDynaDecal, which includes things like
// water ripples, bullet hits, and footprints.
// This function just makes sure they are ready to be rendered, called right before
// the rendering.
hsBool plDXPipeline : : ICheckAuxBuffers ( const plAuxSpan * span )
{
plGBufferGroup * group = span - > fGroup ;
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) group - > GetVertexBufferRef ( span - > fVBufferIdx ) ;
if ( ! vRef )
return true ;
plDXIndexBufferRef * iRef = ( plDXIndexBufferRef * ) group - > GetIndexBufferRef ( span - > fIBufferIdx ) ;
if ( ! iRef )
return true ;
// If our vertex buffer ref is volatile and the timestamp is off
// then it needs to be refilled
if ( vRef - > Expired ( fVtxRefTime ) )
{
IRefreshDynVertices ( group , vRef ) ;
}
if ( vRef - > fOffset ! = iRef - > fOffset )
{
iRef - > fOffset = vRef - > fOffset ;
iRef - > SetRebuiltSinceUsed ( true ) ;
}
return false ; // No error
}
// ICheckDynBuffers ////////////////////////////////////////////////////////////////////////////////////////
// Make sure the buffers underlying this span are ready to be rendered. Meaning that the underlying
// D3D buffers are in sync with the plasma buffers.
hsBool plDXPipeline : : ICheckDynBuffers ( plDrawableSpans * drawable , plGBufferGroup * group , const plSpan * spanBase )
{
if ( ! ( spanBase - > fTypeMask & plSpan : : kVertexSpan ) )
return false ;
// If we arent' an trilist, we're toast.
if ( ! ( spanBase - > fTypeMask & plSpan : : kIcicleSpan ) )
return false ;
plIcicle * span = ( plIcicle * ) spanBase ;
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) group - > GetVertexBufferRef ( span - > fVBufferIdx ) ;
if ( ! vRef )
return true ;
plDXIndexBufferRef * iRef = ( plDXIndexBufferRef * ) group - > GetIndexBufferRef ( span - > fIBufferIdx ) ;
if ( ! iRef )
return true ;
// If our vertex buffer ref is volatile and the timestamp is off
// then it needs to be refilled
if ( vRef - > Expired ( fVtxRefTime ) )
{
IRefreshDynVertices ( group , vRef ) ;
}
if ( vRef - > fOffset ! = iRef - > fOffset )
{
iRef - > fOffset = vRef - > fOffset ;
iRef - > SetRebuiltSinceUsed ( true ) ;
}
if ( iRef - > IsDirty ( ) )
{
IFillIndexBufferRef ( iRef , group , span - > fIBufferIdx ) ;
iRef - > SetRebuiltSinceUsed ( true ) ;
}
return false ; // No error
}
//// IRenderSpans /////////////////////////////////////////////////////////////
// Renders an array of spans obtained from a plDrawableSpans object
// The incoming visList gives the indices of the spans which are visible and should
// be drawn now, and gives them in sorted order.
void plDXPipeline : : IRenderSpans ( plDrawableSpans * drawable , const hsTArray < Int16 > & visList )
{
plProfile_BeginTiming ( RenderSpan ) ;
hsMatrix44 lastL2W ;
UInt32 i , j ;
bool drewPatch = false ;
hsGMaterial * material ;
const hsTArray < plSpan * > & spans = drawable - > GetSpanArray ( ) ;
plProfile_IncCount ( EmptyList , ! visList . GetCount ( ) ) ;
/// Set this (*before* we do our TestVisibleWorld stuff...)
lastL2W . Reset ( ) ;
ISetLocalToWorld ( lastL2W , lastL2W ) ; // This is necessary; otherwise, we have to test for
// the first transform set, since this'll be identity
// but the actual device transform won't be (unless
// we do this)
/// Loop through our spans, combining them when possible
for ( i = 0 ; i < visList . GetCount ( ) ; )
{
material = GetOverrideMaterial ( ) ? GetOverrideMaterial ( ) : drawable - > GetMaterial ( spans [ visList [ i ] ] - > fMaterialIdx ) ;
/// It's an icicle--do our icicle merge loop
plIcicle tempIce ( * ( ( plIcicle * ) spans [ visList [ i ] ] ) ) ;
// Start at i + 1, look for as many spans as we can add to tempIce
for ( j = i + 1 ; j < visList . GetCount ( ) ; j + + )
{
if ( GetOverrideMaterial ( ) )
tempIce . fMaterialIdx = spans [ visList [ j ] ] - > fMaterialIdx ;
plProfile_BeginTiming ( MergeCheck ) ;
if ( ! spans [ visList [ j ] ] - > CanMergeInto ( & tempIce ) )
{
plProfile_EndTiming ( MergeCheck ) ;
break ;
}
plProfile_EndTiming ( MergeCheck ) ;
plProfile_Inc ( SpanMerge ) ;
plProfile_BeginTiming ( MergeSpan ) ;
spans [ visList [ j ] ] - > MergeInto ( & tempIce ) ;
plProfile_EndTiming ( MergeSpan ) ;
}
if ( material ! = nil )
{
// What do we change?
plProfile_BeginTiming ( SpanTransforms ) ;
ISetupTransforms ( drawable , tempIce , lastL2W ) ;
plProfile_EndTiming ( SpanTransforms ) ;
// Turn on this spans lights and turn off the rest.
IEnableLights ( & tempIce ) ;
// Check that the underlying buffers are ready to go.
plProfile_BeginTiming ( CheckDyn ) ;
ICheckDynBuffers ( drawable , drawable - > GetBufferGroup ( tempIce . fGroupIdx ) , & tempIce ) ;
plProfile_EndTiming ( CheckDyn ) ;
plProfile_BeginTiming ( CheckStat ) ;
CheckVertexBufferRef ( drawable - > GetBufferGroup ( tempIce . fGroupIdx ) , tempIce . fVBufferIdx ) ;
CheckIndexBufferRef ( drawable - > GetBufferGroup ( tempIce . fGroupIdx ) , tempIce . fIBufferIdx ) ;
plProfile_EndTiming ( CheckStat ) ;
// Draw this span now
IRenderBufferSpan ( tempIce ,
drawable - > GetVertexRef ( tempIce . fGroupIdx , tempIce . fVBufferIdx ) ,
drawable - > GetIndexRef ( tempIce . fGroupIdx , tempIce . fIBufferIdx ) ,
material ,
tempIce . fVStartIdx , tempIce . fVLength , // These are used as our accumulated range
tempIce . fIPackedIdx , tempIce . fILength ) ;
}
// Restart our search...
i = j ;
}
plProfile_EndTiming ( RenderSpan ) ;
/// All done!
}
//// IAddBoundsSpan ///////////////////////////////////////////////////////////
// Creates a new span for the given drawable to represent the specified
// world bounds.
// Debugging only.
void plDXPipeline : : IAddBoundsSpan ( plDrawableSpans * ice , const hsBounds3Ext * bounds , UInt32 bndColor )
{
# if MCN_BOUNDS_SPANS
static hsTArray < plGeometrySpan * > spanArray ;
static hsMatrix44 identMatrix ;
static hsPoint3 c [ 8 ] , n [ 8 ] ;
static int nPts [ 8 ] [ 3 ] = { { - 1 , - 1 , - 1 } , { 1 , - 1 , - 1 } , { - 1 , 1 , - 1 } , { 1 , 1 , - 1 } ,
{ - 1 , - 1 , 1 } , { 1 , - 1 , 1 } , { - 1 , 1 , 1 } , { 1 , 1 , 1 } } ;
int i ;
plGeometrySpan * newSpan ;
if ( spanArray . GetCount ( ) = = 0 )
{
spanArray . Reset ( ) ;
spanArray . Append ( TRACKED_NEW plGeometrySpan ( ) ) ;
identMatrix . Reset ( ) ;
// Make normals
for ( i = 0 ; i < 8 ; i + + )
{
n [ i ] . fX = ( float ) nPts [ i ] [ 0 ] ;
n [ i ] . fY = ( float ) nPts [ i ] [ 1 ] ;
n [ i ] . fZ = ( float ) nPts [ i ] [ 2 ] ;
}
}
else
spanArray [ 0 ] = TRACKED_NEW plGeometrySpan ( ) ;
newSpan = spanArray [ 0 ] ;
newSpan - > BeginCreate ( fBoundsMat , identMatrix , 0 ) ;
// Make corners
c [ 1 ] = c [ 2 ] = c [ 4 ] = * bounds - > GetCorner ( & c [ 0 ] ) ;
hsVector3 axes [ 3 ] ;
bounds - > GetAxes ( axes + 0 , axes + 1 , axes + 2 ) ;
c [ 1 ] + = axes [ 0 ] ;
c [ 2 ] + = axes [ 1 ] ;
c [ 4 ] + = axes [ 2 ] ;
c [ 3 ] = c [ 1 ] ;
c [ 3 ] + = axes [ 1 ] ;
c [ 5 ] = c [ 1 ] ;
c [ 5 ] + = axes [ 2 ] ;
c [ 6 ] = c [ 2 ] ;
c [ 6 ] + = axes [ 2 ] ;
c [ 7 ] = c [ 6 ] ;
c [ 7 ] + = axes [ 0 ] ;
for ( i = 0 ; i < 8 ; i + + )
newSpan - > AddVertex ( & c [ i ] , & n [ i ] , bndColor ) ;
newSpan - > AddTriIndices ( 0 , 1 , 2 ) ;
newSpan - > AddTriIndices ( 2 , 1 , 3 ) ;
newSpan - > AddTriIndices ( 6 , 3 , 7 ) ;
newSpan - > AddTriIndices ( 7 , 1 , 5 ) ;
newSpan - > AddTriIndices ( 5 , 0 , 4 ) ;
newSpan - > AddTriIndices ( 4 , 2 , 6 ) ;
newSpan - > EndCreate ( ) ;
fBSpansToDelete . Append ( ice - > AppendDISpans ( spanArray ) ) ;
# endif
}
//// IAddNormalsSpan //////////////////////////////////////////////////////////
// Creates a new span for the given drawable to represent the specified
// world bounds.
// Debugging only.
void plDXPipeline : : IAddNormalsSpan ( plDrawableSpans * ice , plIcicle * span , plDXVertexBufferRef * vRef , UInt32 bndColor )
{
# if MCN_BOUNDS_SPANS
static hsTArray < plGeometrySpan * > spanArray ;
static hsMatrix44 identMatrix ;
static hsPoint3 point , off , blank ;
hsVector3 b2 ;
UInt16 v1 , v2 , v3 ;
int i ;
plGeometrySpan * newSpan ;
if ( spanArray . GetCount ( ) = = 0 )
{
spanArray . Reset ( ) ;
spanArray . Append ( TRACKED_NEW plGeometrySpan ( ) ) ;
identMatrix . Reset ( ) ;
}
else
spanArray [ 0 ] = TRACKED_NEW plGeometrySpan ( ) ;
newSpan = spanArray [ 0 ] ;
newSpan - > BeginCreate ( fBoundsMat , span - > fLocalToWorld , 0 ) ;
for ( i = 0 ; i < span - > fVLength ; i + + )
{
point = vRef - > fOwner - > Position ( span - > fVBufferIdx , span - > fCellIdx , span - > fCellOffset + i ) ;
b2 = vRef - > fOwner - > Normal ( span - > fVBufferIdx , span - > fCellIdx , span - > fCellOffset + i ) ;
off . Set ( point . fX + b2 . fX , point . fY + b2 . fY , point . fZ + b2 . fZ ) ;
v1 = newSpan - > AddVertex ( & point , & blank , bndColor ) ;
v2 = newSpan - > AddVertex ( & off , & blank , bndColor ) ;
v3 = newSpan - > AddVertex ( & point , & blank , bndColor ) ;
newSpan - > AddTriIndices ( v1 , v2 , v3 ) ;
}
newSpan - > EndCreate ( ) ;
fBSpansToDelete . Append ( ice - > AppendDISpans ( spanArray ) ) ;
# endif
}
//// BeginRender //////////////////////////////////////////////////////////////
// Specifies the beginning of the render frame.
// If this succeeds (returns false) it must be matched with a call to EndRender.
// Normally, the main client loop will wrap the entire scene render (including
// any offscreen rendering) in a BeginRender/EndRender pair. There is no need
// for further calls for sub-renders.
hsBool plDXPipeline : : BeginRender ( )
{
// Do we have some restoration work ahead of us?
// Checks for Device Lost condition
if ( IResetDevice ( ) )
return true ;
// We were lost, but now we're found! Spread the good word brother!
if ( fDevWasLost )
{
/// Broadcast a message letting everyone know that we were recreated and that
/// all device-specific stuff needs to be recreated
// plDeviceRecreateMsg* clean = TRACKED_NEW plDeviceRecreateMsg();
// plgDispatch::MsgSend(clean);
fDevWasLost = false ;
}
if ( IsDebugFlagSet ( plPipeDbg : : kFlagReload ) )
{
IReleaseShaders ( ) ;
fD3DDevice - > EvictManagedResources ( ) ;
fEvictTime = fTextUseTime ;
fManagedSeen = 0 ;
SetDebugFlag ( plPipeDbg : : kFlagReload , false ) ;
}
// offset transform
RefreshScreenMatrices ( ) ;
// If this is the primary BeginRender, make sure we're really ready.
if ( ! fInSceneDepth + + )
{
// Workaround for NVidia memory manager bug. Search for "OSVERSIONINFO" to
// find notes on the bug. This is where we purge managed memory periodically.
plProfile_Set ( ManSeen , fManagedSeen ) ;
if ( fManagedCutoff )
{
plConst ( UInt32 ) kMinEvictTime ( 1800 ) ; // ~2 minutes @ 15FPS
if ( ( fManagedSeen > fManagedCutoff ) & & ( fTexUsed + fVtxUsed < fManagedCutoff ) & & ( fTextUseTime - fEvictTime > kMinEvictTime ) )
{
fD3DDevice - > EvictManagedResources ( ) ;
fManagedSeen = 0 ;
fEvictTime = fTextUseTime ;
plProfile_IncCount ( ManEvict , 1 ) ;
}
}
// Superfluous setting of Z state.
fD3DDevice - > SetRenderState ( D3DRS_ZENABLE ,
( fView . IsPerspective ( ) & & ( fSettings . fD3DCaps & kCapsWBuffer ) )
? D3DZB_USEW : D3DZB_TRUE ) ;
/// If we have a renderTarget active, use its viewport
ISetViewport ( ) ;
// Tell D3D we're ready to start rendering.
if ( FAILED ( fD3DDevice - > BeginScene ( ) ) )
{
fDeviceLost = true ;
}
// Reset all our buffer/image usage counters
fNextDynVtx = 0 ;
fVtxRefTime + + ;
fTexUsed = 0 ;
fVtxUsed = 0 ;
fTextUseTime + + ;
// Render any shadow maps that have been submitted for this frame.
IPreprocessShadows ( ) ;
IPreprocessAvatarTextures ( ) ;
}
fRenderCnt + + ;
// Would probably rather this be an input.
fTime = hsTimer : : GetSysSeconds ( ) ;
return false ;
}
//// ISetViewport /////////////////////////////////////////////////////////////
// Translate our viewport into a D3D viewport
void plDXPipeline : : ISetViewport ( )
{
D3DVIEWPORT9 vp = { GetViewTransform ( ) . GetViewPortLeft ( ) ,
GetViewTransform ( ) . GetViewPortTop ( ) ,
GetViewTransform ( ) . GetViewPortWidth ( ) ,
GetViewTransform ( ) . GetViewPortHeight ( ) ,
0.f , 1.f } ;
WEAK_ERROR_CHECK ( fD3DDevice - > SetViewport ( & vp ) ) ;
}
//// RenderScreenElements /////////////////////////////////////////////////////
// Renders all the screen elements, such as debug text and plates. Also puts
// up all the info about vertex buffers and such. Should be called right
// before EndRender(), but only on the main surface (not on renderTargets,
// for example).
void plDXPipeline : : RenderScreenElements ( )
{
bool reset = false ;
# if MCN_BOUNDS_SPANS
if ( fBoundsSpans & & fBSpansToDelete . GetCount ( ) > 0 )
{
Draw ( fBoundsSpans ) ;
int i ;
for ( i = 0 ; i < fBSpansToDelete . GetCount ( ) ; i + + )
fBoundsSpans - > RemoveDISpans ( fBSpansToDelete [ i ] ) ;
fBSpansToDelete . Reset ( ) ;
}
# endif
if ( fCullProxy )
Draw ( fCullProxy ) ;
# ifdef MF_ENABLE_HACKOFF
//WHITE
static plPlate * hackPlate = nil ;
if ( doHackPlate < hackOffscreens . GetCount ( ) )
{
if ( ! hackPlate )
{
fPlateMgr - > CreatePlate ( & hackPlate , 0.5f , 0.5f , 1.0f , 1.0f ) ;
hackPlate - > CreateBlankMaterial ( 32 , 32 , false ) ;
}
}
if ( hackPlate )
{
if ( doHackPlate < hackOffscreens . GetCount ( ) )
{
hsGMaterial * hackMat = hackPlate - > GetMaterial ( ) ;
plLayer * lay = plLayer : : ConvertNoRef ( hackMat - > GetLayer ( 0 ) ) ;
if ( lay )
lay - > SetTexture ( hackOffscreens [ doHackPlate ] ) ;
hackPlate - > SetVisible ( true ) ;
}
else
{
hackPlate - > SetVisible ( false ) ;
}
}
# endif // MF_ENABLE_HACKOFF
hsGMatState tHack = PushMaterialOverride ( hsGMatState : : kMisc , hsGMatState : : kMiscWireFrame , false ) ;
hsGMatState ambHack = PushMaterialOverride ( hsGMatState : : kShade , hsGMatState : : kShadeWhite , true ) ;
plProfile_BeginTiming ( PlateMgr ) ;
/// Plates
if ( fPlateMgr )
{
fPlateMgr - > DrawToDevice ( this ) ;
reset = true ;
}
plProfile_EndTiming ( PlateMgr ) ;
PopMaterialOverride ( ambHack , true ) ;
PopMaterialOverride ( tHack , false ) ;
plProfile_BeginTiming ( DebugText ) ;
/// Debug text
if ( fDebugTextMgr & & plDebugText : : Instance ( ) . IsEnabled ( ) )
{
fDebugTextMgr - > DrawToDevice ( this ) ;
reset = true ;
}
plProfile_EndTiming ( DebugText ) ;
plProfile_BeginTiming ( Reset ) ;
if ( reset )
{
// Reset these since the drawing might have trashed them
hsRefCnt_SafeUnRef ( fSettings . fCurrVertexBuffRef ) ;
hsRefCnt_SafeUnRef ( fSettings . fCurrIndexBuffRef ) ;
fSettings . fCurrVertexBuffRef = nil ;
fSettings . fCurrIndexBuffRef = nil ;
fView . fXformResetFlags = fView . kResetAll ; // Text destroys view transforms
hsRefCnt_SafeUnRef ( fLayerRef [ 0 ] ) ;
fLayerRef [ 0 ] = nil ; // Text destroys stage 0 texture
}
plProfile_EndTiming ( Reset ) ;
}
//// EndRender ////////////////////////////////////////////////////////////////
// Tell D3D we're through rendering for this frame, and flip the back buffer to front.
// Also includes a bit of making sure we're not holding onto anything that might
// get deleted before the next render.
hsBool plDXPipeline : : EndRender ( )
{
# ifdef MF_ENABLE_HACKOFF
hackOffscreens . SetCount ( 0 ) ;
# endif // MF_ENABLE_HACKOFF
IBottomLayer ( ) ;
hsBool retVal = false ;
/// Actually end the scene
if ( ! - - fInSceneDepth )
{
WEAK_ERROR_CHECK ( fD3DDevice - > EndScene ( ) ) ;
retVal = IFlipSurface ( ) ;
IClearShadowSlaves ( ) ;
}
// Do this last, after we've drawn everything
// Just letting go of things we're done with for the frame.
fForceMatHandle = true ;
hsRefCnt_SafeUnRef ( fCurrMaterial ) ;
fCurrMaterial = nil ;
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
if ( fLayerRef [ i ] )
{
hsRefCnt_SafeUnRef ( fLayerRef [ i ] ) ;
fLayerRef [ i ] = nil ;
}
}
return retVal ;
}
// SetGamma ////////////////////////////////////////////////////////////
// Create and set a gamma table based on the input exponent values for
// R, G, and B. Can also set explicit table using the other SetGamma().
hsBool plDXPipeline : : SetGamma ( hsScalar eR , hsScalar eG , hsScalar eB )
{
if ( fSettings . fNoGammaCorrect )
return false ;
D3DGAMMARAMP ramp ;
ramp . red [ 0 ] = ramp . green [ 0 ] = ramp . blue [ 0 ] = 0L ;
plConst ( hsScalar ) kMinE ( 0.1f ) ;
if ( eR > kMinE )
eR = 1.f / eR ;
else
eR = 1.f / kMinE ;
if ( eG > kMinE )
eG = 1.f / eG ;
else
eG = 1.f / kMinE ;
if ( eB > kMinE )
eB = 1.f / eB ;
else
eB = 1.f / kMinE ;
int i ;
for ( i = 1 ; i < 256 ; i + + )
{
hsScalar orig = hsScalar ( i ) / 255.f ;
hsScalar gamm ;
gamm = pow ( orig , eR ) ;
gamm * = hsScalar ( UInt16 ( - 1 ) ) ;
ramp . red [ i ] = UInt16 ( gamm ) ;
gamm = pow ( orig , eG ) ;
gamm * = hsScalar ( UInt16 ( - 1 ) ) ;
ramp . green [ i ] = UInt16 ( gamm ) ;
gamm = pow ( orig , eB ) ;
gamm * = hsScalar ( UInt16 ( - 1 ) ) ;
ramp . blue [ i ] = UInt16 ( gamm ) ;
}
fD3DDevice - > SetGammaRamp ( 0 , D3DSGR_NO_CALIBRATION , & ramp ) ;
return true ;
}
// SetGamma
// Copy the input gamma tables and pass them to the hardware.
hsBool plDXPipeline : : SetGamma ( const UInt16 * const tabR , const UInt16 * const tabG , const UInt16 * const tabB )
{
if ( fSettings . fNoGammaCorrect )
return false ;
D3DGAMMARAMP ramp ;
memcpy ( ramp . red , tabR , 256 * sizeof ( WORD ) ) ;
memcpy ( ramp . green , tabG , 256 * sizeof ( WORD ) ) ;
memcpy ( ramp . blue , tabB , 256 * sizeof ( WORD ) ) ;
fD3DDevice - > SetGammaRamp ( 0 , D3DSGR_NO_CALIBRATION , & ramp ) ;
return true ;
}
//// IFlipSurface /////////////////////////////////////////////////////////////
// Initiate moving the back buffer contents to the front buffer. Will detect
// and set the device lost condition when it occurs.
hsBool plDXPipeline : : IFlipSurface ( )
{
/// Works now for both fullscreen and windowed modes
HRESULT hr = D3D_OK ;
if ( fSettings . fCurrRenderTarget = = nil )
{
hr = fD3DDevice - > Present ( nil , nil , fSettings . fHWnd , nil ) ;
}
if ( FAILED ( hr ) )
{
fDeviceLost = true ;
}
return fDeviceLost ;
}
// ExtractMipMap
// This code works and is fairly fast for creating a new mipmap
// as a copy of the data in an offscreen render target. It's not
// currently used, because of driver bugs found in rendering to
// offscreen render targets.
plMipmap * plDXPipeline : : ExtractMipMap ( plRenderTarget * targ )
{
if ( plCubicRenderTarget : : ConvertNoRef ( targ ) )
return nil ;
if ( targ - > GetPixelSize ( ) ! = 32 )
{
hsAssert ( false , " Only RGBA8888 currently implemented " ) ;
return nil ;
}
plDXRenderTargetRef * ref = ( plDXRenderTargetRef * ) targ - > GetDeviceRef ( ) ;
if ( ! ref )
return nil ;
IDirect3DSurface9 * surf = ref - > GetColorSurface ( ) ;
if ( ! surf )
return nil ;
D3DLOCKED_RECT rect ;
if ( FAILED ( surf - > LockRect ( & rect , nil , D3DLOCK_READONLY ) ) )
{
return nil ;
}
const int width = targ - > GetWidth ( ) ;
const int height = targ - > GetHeight ( ) ;
plMipmap * mipMap = TRACKED_NEW plMipmap ( width , height , plMipmap : : kARGB32Config , 1 ) ;
UInt8 * ptr = ( UInt8 * ) ( rect . pBits ) ;
const int pitch = rect . Pitch ;
const UInt32 blackOpaque = 0xff000000 ;
int y ;
for ( y = 0 ; y < height ; y + + )
{
UInt32 * destPtr = mipMap - > GetAddr32 ( 0 , y ) ;
UInt32 * srcPtr = ( UInt32 * ) ptr ;
int x ;
for ( x = 0 ; x < width ; x + + )
{
destPtr [ x ] = srcPtr [ x ] | blackOpaque ;
}
ptr + = pitch ;
}
surf - > UnlockRect ( ) ;
return mipMap ;
}
//// CaptureScreen ////////////////////////////////////////////////////////////
// Copy the current contents of the front buffer to the destination mipmap, with optional
// rescaling. Note that the mipmap function which does this rescaling is of low quality
// (pyramid filter even though it claims a box filter) and low performance (slow).
// If it mattered, it would take about an hour to have a higher performance, higher quality,
// more robust rescale function.
// This function is fairly straightforward, the complexity only comes from making sure
// all pixels in dest get written to, even though the client window may be partially
// offscreen. If the client window is partially offscreen, there will be no values
// for the "offscreen pixels" to copy to dest, so opaque black is used.
hsBool plDXPipeline : : CaptureScreen ( plMipmap * dest , bool flipVertical , UInt16 desiredWidth , UInt16 desiredHeight )
{
UInt32 y , * destPtr , * srcPtr , width , height , bigWidth , bigHeight ;
IDirect3DSurface9 * surface ;
D3DLOCKED_RECT rect ;
RECT rToLock ;
width = GetViewTransform ( ) . GetViewPortWidth ( ) ;
height = GetViewTransform ( ) . GetViewPortHeight ( ) ;
int left = 0 ;
int right = width ;
int top = 0 ;
int bottom = height ;
if ( fSettings . fFullscreen )
{
if ( FAILED ( fD3DDevice - > CreateOffscreenPlainSurface ( width , height , D3DFMT_A8R8G8B8 , D3DPOOL_SCRATCH , & surface , NULL ) ) )
return false ;
rToLock . left = GetViewTransform ( ) . GetViewPortLeft ( ) ;
rToLock . top = GetViewTransform ( ) . GetViewPortTop ( ) ;
rToLock . right = GetViewTransform ( ) . GetViewPortRight ( ) ;
rToLock . bottom = GetViewTransform ( ) . GetViewPortBottom ( ) ;
}
else
{
bigWidth = GetSystemMetrics ( SM_CXSCREEN ) ;
bigHeight = GetSystemMetrics ( SM_CYSCREEN ) ;
if ( FAILED ( fD3DDevice - > CreateOffscreenPlainSurface ( bigWidth , bigHeight , D3DFMT_A8R8G8B8 , D3DPOOL_SCRATCH , & surface , NULL ) ) )
return false ;
GetClientRect ( fSettings . fHWnd , & rToLock ) ;
MapWindowPoints ( fSettings . fHWnd , nil , ( POINT * ) & rToLock , 2 ) ;
if ( rToLock . right > bigWidth )
{
right - = ( rToLock . right - bigWidth ) ;
rToLock . right = bigWidth ;
}
if ( rToLock . bottom > bigHeight )
{
bottom - = ( rToLock . bottom - bigHeight ) ;
rToLock . bottom = bigHeight ;
}
if ( rToLock . top < 0 )
{
top - = rToLock . top ;
rToLock . top = 0 ;
}
if ( rToLock . left < 0 )
{
left - = rToLock . left ;
rToLock . left = 0 ;
}
}
UINT swapChain = 0 ;
if ( FAILED ( fD3DDevice - > GetFrontBufferData ( swapChain , surface ) ) )
{
ReleaseObject ( surface ) ;
return false ;
}
if ( FAILED ( surface - > LockRect ( & rect , & rToLock , D3DLOCK_READONLY ) ) )
{
ReleaseObject ( surface ) ;
return false ;
}
if ( dest - > GetWidth ( ) ! = width | | dest - > GetHeight ( ) ! = height | |
dest - > GetPixelSize ( ) ! = 32 )
{
dest - > Reset ( ) ;
dest - > Create ( width , height , plMipmap : : kARGB32Config , 1 ) ;
}
const UInt32 blackOpaque = 0xff000000 ;
/// Copy over
for ( y = 0 ; y < top ; y + + )
{
if ( flipVertical )
destPtr = dest - > GetAddr32 ( 0 , height - 1 - y ) ;
else
destPtr = dest - > GetAddr32 ( 0 , y ) ;
int x ;
for ( x = 0 ; x < width ; x + + )
{
* destPtr + + = blackOpaque ;
}
}
for ( y = top ; y < bottom ; y + + )
{
srcPtr = ( UInt32 * ) ( ( UInt8 * ) rect . pBits + rect . Pitch * y ) ;
if ( flipVertical )
destPtr = dest - > GetAddr32 ( 0 , height - 1 - y ) ;
else
destPtr = dest - > GetAddr32 ( 0 , y ) ;
int x ;
for ( x = 0 ; x < left ; x + + )
* destPtr + + = blackOpaque ;
memcpy ( destPtr , srcPtr , ( right - left ) * sizeof ( UInt32 ) ) ;
destPtr + = ( right - left ) ;
for ( x = right ; x < width ; x + + )
* destPtr + + = blackOpaque ;
}
for ( y = bottom ; y < height ; y + + )
{
if ( flipVertical )
destPtr = dest - > GetAddr32 ( 0 , height - 1 - y ) ;
else
destPtr = dest - > GetAddr32 ( 0 , y ) ;
int x ;
for ( x = 0 ; x < width ; x + + )
{
* destPtr + + = blackOpaque ;
}
}
surface - > UnlockRect ( ) ;
ReleaseObject ( surface ) ;
if ( desiredWidth ! = 0 & & desiredHeight ! = nil )
{
// Rescale to the right size
dest - > ResizeNicely ( desiredWidth , desiredHeight , plMipmap : : kDefaultFilter ) ;
}
return true ;
}
///////////////////////////////////////////////////////////////////////////////
//// Render Targets ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// MakeRenderTargetRef //////////////////////////////////////////////////////
// Create the a Plasma render target ref, filling in the underlying D3D resources
// (e.g. color/depth buffers).
// Note that for ATI boards, we create a single depth surface for them to share.
// That can actually be 2 depth surfaces, if some color surfaces are 16 bit and
// others are 24/32 bit, since the ATI's want to match color depth with depth depth.
hsGDeviceRef * plDXPipeline : : MakeRenderTargetRef ( plRenderTarget * owner )
{
plDXRenderTargetRef * ref = nil ;
IDirect3DSurface9 * surface = nil , * depthSurface = nil ;
IDirect3DTexture9 * texture = nil ;
IDirect3DCubeTexture9 * cTexture = nil ;
D3DFORMAT surfFormat = D3DFMT_UNKNOWN , depthFormat = D3DFMT_UNKNOWN ;
D3DRESOURCETYPE resType ;
int i ;
plCubicRenderTarget * cubicRT ;
UInt16 width , height ;
hsAssert ( ! fManagedAlloced , " Allocating non-managed resource with managed resources alloc'd " ) ;
/// Check--is this renderTarget really a child of a cubicRenderTarget?
if ( owner - > GetParent ( ) ! = nil )
{
/// This'll create the deviceRefs for all of its children as well
MakeRenderTargetRef ( owner - > GetParent ( ) ) ;
return owner - > GetDeviceRef ( ) ;
}
// If we already have a rendertargetref, we just need it filled out with D3D resources.
if ( owner - > GetDeviceRef ( ) ! = nil )
ref = ( plDXRenderTargetRef * ) owner - > GetDeviceRef ( ) ;
// Look for supported format. Note that the surfFormat and depthFormat are
// passed in by ref, so they may be different after this function call (if
// an exact match isn't supported, but something similar is).
if ( ! IPrepRenderTargetInfo ( owner , surfFormat , depthFormat , resType ) )
{
hsAssert ( false , " Error getting renderTarget info " ) ;
return nil ;
}
/// Create the render target now
// Start with the depth surface.
// Note that we only ever give a cubic rendertarget a single shared depth buffer,
// since we only render one face at a time. If we were rendering part of face X, then part
// of face Y, then more of face X, then they would all need their own depth buffers.
if ( owner - > GetZDepth ( ) & & ( owner - > GetFlags ( ) & ( plRenderTarget : : kIsTexture | plRenderTarget : : kIsOffscreen ) ) )
{
// 9600 THRASH
if ( ! fSettings . fShareDepth )
{
/// Create the depthbuffer
if ( FAILED ( fD3DDevice - > CreateDepthStencilSurface (
owner - > GetWidth ( ) , owner - > GetHeight ( ) , depthFormat ,
D3DMULTISAMPLE_NONE , 0 , FALSE ,
& depthSurface , NULL ) ) )
{
return nil ;
}
// See plDXRenderTargetRef::Release()
//D3DSURF_MEMNEW(depthSurface);
}
else
{
const int iZ = owner - > GetZDepth ( ) / 24 ;
if ( ! fSharedDepthSurface [ iZ ] )
{
plConst ( DWORD ) kSharedWidth ( 800 ) ;
plConst ( DWORD ) kSharedHeight ( 600 ) ;
if ( FAILED ( fD3DDevice - > CreateDepthStencilSurface (
kSharedWidth , kSharedHeight , depthFormat ,
D3DMULTISAMPLE_NONE , 0 , FALSE ,
& fSharedDepthSurface [ iZ ] , NULL ) ) )
{
return nil ;
}
// See plDXRenderTargetRef::Release()
//D3DSURF_MEMNEW(fSharedDepthSurface[iZ]);
fSharedDepthFormat [ iZ ] = depthFormat ;
}
hsAssert ( depthFormat = = fSharedDepthFormat [ iZ ] , " Mismatch on render target types " ) ;
fSharedDepthSurface [ iZ ] - > AddRef ( ) ;
depthSurface = fSharedDepthSurface [ iZ ] ;
}
}
// See if it's a cubic render target.
// Primary consumer here is the vertex/pixel shader water.
cubicRT = plCubicRenderTarget : : ConvertNoRef ( owner ) ;
if ( cubicRT ! = nil )
{
/// And create the ref (it'll know how to set all the flags)
if ( ref ! = nil )
ref - > Set ( surfFormat , 0 , owner ) ;
else
ref = TRACKED_NEW plDXRenderTargetRef ( surfFormat , 0 , owner ) ;
if ( ! FAILED ( fD3DDevice - > CreateCubeTexture ( owner - > GetWidth ( ) , 1 , D3DUSAGE_RENDERTARGET , surfFormat ,
D3DPOOL_DEFAULT , ( IDirect3DCubeTexture9 * * ) & cTexture , NULL ) ) )
{
/// Create a CUBIC texture
for ( i = 0 ; i < 6 ; i + + )
{
plRenderTarget * face = cubicRT - > GetFace ( i ) ;
plDXRenderTargetRef * fRef ;
if ( face - > GetDeviceRef ( ) ! = nil )
{
fRef = ( plDXRenderTargetRef * ) face - > GetDeviceRef ( ) ;
fRef - > Set ( surfFormat , 0 , face ) ;
if ( ! fRef - > IsLinked ( ) )
fRef - > Link ( & fRenderTargetRefList ) ;
}
else
{
face - > SetDeviceRef ( TRACKED_NEW plDXRenderTargetRef ( surfFormat , 0 , face , false ) ) ;
( ( plDXRenderTargetRef * ) face - > GetDeviceRef ( ) ) - > Link ( & fRenderTargetRefList ) ;
// Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least)
hsRefCnt_SafeUnRef ( face - > GetDeviceRef ( ) ) ;
}
}
D3DSURF_MEMNEW ( cTexture ) ;
ref - > SetTexture ( cTexture , depthSurface ) ;
}
else
{
ReleaseObject ( depthSurface ) ;
hsRefCnt_SafeUnRef ( ref ) ;
ref = nil ;
}
}
// Not a cubic, is it a texture render target? These are currently used
// primarily for shadow map generation.
else if ( owner - > GetFlags ( ) & plRenderTarget : : kIsTexture )
{
/// Create a normal texture
if ( ref ! = nil )
ref - > Set ( surfFormat , 0 , owner ) ;
else
ref = TRACKED_NEW plDXRenderTargetRef ( surfFormat , 0 , owner ) ;
if ( ! FAILED ( fD3DDevice - > CreateTexture ( owner - > GetWidth ( ) , owner - > GetHeight ( ) , 1 , D3DUSAGE_RENDERTARGET , surfFormat ,
D3DPOOL_DEFAULT , ( IDirect3DTexture9 * * ) & texture , NULL ) ) )
{
D3DSURF_MEMNEW ( texture ) ;
ref - > SetTexture ( texture , depthSurface ) ;
}
else
{
ReleaseObject ( depthSurface ) ;
hsRefCnt_SafeUnRef ( ref ) ;
ref = nil ;
}
}
// Not a texture either, must be a plain offscreen.
// Note that the plain offscreen code path works and was used until recently,
// until it turned up that some hardware had bugs on rendering to
// an offscreen.
// Some GeForce1's had lighting anomolies, although my GeForce1 DDR didn't.
// Some ATI's showed a momemtary glitch of corrupted rendering on the frame
// when rendering both to the primary and an offscreen (again, not mine).
// So the Offscreen isn't currently used for anything.
else if ( owner - > GetFlags ( ) & plRenderTarget : : kIsOffscreen )
{
/// Create a blank surface
if ( ref ! = nil )
ref - > Set ( surfFormat , 0 , owner ) ;
else
ref = TRACKED_NEW plDXRenderTargetRef ( surfFormat , 0 , owner ) ;
width = owner - > GetWidth ( ) ;
height = owner - > GetHeight ( ) ;
// Specify true for lockable, otherwise I'm not sure what we'd do with it. I guess we
// could copyrect to another surface, presumably a texture. But right now the only
// thing we use this for is to render a snapshot and copy it to sysmem, which implies
// lockable.
if ( ! FAILED ( fD3DDevice - > CreateRenderTarget ( width , height , surfFormat ,
D3DMULTISAMPLE_NONE , 0 ,
TRUE , & surface , NULL ) ) )
{
D3DSURF_MEMNEW ( surface ) ;
ref - > SetTexture ( surface , depthSurface ) ;
}
else
{
ReleaseObject ( depthSurface ) ;
hsRefCnt_SafeUnRef ( ref ) ;
ref = nil ;
}
}
// Keep it in a linked list for ready destruction.
if ( owner - > GetDeviceRef ( ) ! = ref )
{
owner - > SetDeviceRef ( ref ) ;
// Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least)
hsRefCnt_SafeUnRef ( ref ) ;
if ( ref ! = nil & & ! ref - > IsLinked ( ) )
ref - > Link ( & fRenderTargetRefList ) ;
}
else
{
if ( ref ! = nil & & ! ref - > IsLinked ( ) )
ref - > Link ( & fRenderTargetRefList ) ;
}
// Mark as dirty.
if ( ref ! = nil )
{
ref - > SetDirty ( false ) ;
}
return ref ;
}
//// SharedRenderTargetRef //////////////////////////////////////////////////////
// Same as MakeRenderTargetRef, except specialized for the shadow map generation.
// The shadow map pools of a given dimension (called RenderTargetPool) all share
// a single depth buffer of that size. This allows sharing on NVidia hardware
// that wants the depth buffer dimensions to match the color buffer size.
// It may be that NVidia hardware doesn't care any more. Contact Matthias
// about that.
hsGDeviceRef * plDXPipeline : : SharedRenderTargetRef ( plRenderTarget * share , plRenderTarget * owner )
{
plDXRenderTargetRef * ref = nil ;
IDirect3DSurface9 * surface = nil ;
IDirect3DSurface9 * depthSurface = nil ;
IDirect3DTexture9 * texture = nil ;
IDirect3DCubeTexture9 * cTexture = nil ;
D3DFORMAT surfFormat = D3DFMT_UNKNOWN , depthFormat = D3DFMT_UNKNOWN ;
D3DRESOURCETYPE resType ;
int i ;
plCubicRenderTarget * cubicRT ;
UInt16 width , height ;
// If we don't already have one to share from, start from scratch.
if ( ! share )
return MakeRenderTargetRef ( owner ) ;
hsAssert ( ! fManagedAlloced , " Allocating non-managed resource with managed resources alloc'd " ) ;
# ifdef HS_DEBUGGING
// Check out the validity of the match. Debug only.
hsAssert ( ! owner - > GetParent ( ) = = ! share - > GetParent ( ) , " Mismatch on shared render target " ) ;
hsAssert ( owner - > GetWidth ( ) = = share - > GetWidth ( ) , " Mismatch on shared render target " ) ;
hsAssert ( owner - > GetHeight ( ) = = share - > GetHeight ( ) , " Mismatch on shared render target " ) ;
hsAssert ( owner - > GetZDepth ( ) = = share - > GetZDepth ( ) , " Mismatch on shared render target " ) ;
hsAssert ( owner - > GetStencilDepth ( ) = = share - > GetStencilDepth ( ) , " Mismatch on shared render target " ) ;
# endif // HS_DEBUGGING
/// Check--is this renderTarget really a child of a cubicRenderTarget?
if ( owner - > GetParent ( ) ! = nil )
{
/// This'll create the deviceRefs for all of its children as well
SharedRenderTargetRef ( share - > GetParent ( ) , owner - > GetParent ( ) ) ;
return owner - > GetDeviceRef ( ) ;
}
if ( owner - > GetDeviceRef ( ) ! = nil )
ref = ( plDXRenderTargetRef * ) owner - > GetDeviceRef ( ) ;
// Look for a good format of matching color and depth size.
if ( ! IFindRenderTargetInfo ( owner , surfFormat , resType ) )
{
hsAssert ( false , " Error getting renderTarget info " ) ;
return nil ;
}
/// Create the render target now
// Start with the depth. We're just going to share the depth surface on the
// input shareRef.
plDXRenderTargetRef * shareRef = ( plDXRenderTargetRef * ) share - > GetDeviceRef ( ) ;
hsAssert ( shareRef , " Trying to share from a render target with no ref " ) ;
if ( shareRef - > fD3DDepthSurface )
shareRef - > fD3DDepthSurface - > AddRef ( ) ;
depthSurface = shareRef - > fD3DDepthSurface ;
// Check for Cubic. This is unlikely, since this function is currently only
// used for the shadow map pools.
cubicRT = plCubicRenderTarget : : ConvertNoRef ( owner ) ;
if ( cubicRT ! = nil )
{
/// And create the ref (it'll know how to set all the flags)
if ( ref ! = nil )
ref - > Set ( surfFormat , 0 , owner ) ;
else
ref = TRACKED_NEW plDXRenderTargetRef ( surfFormat , 0 , owner ) ;
hsAssert ( ! fManagedAlloced , " Alloc default with managed alloc'd " ) ;
if ( ! FAILED ( fD3DDevice - > CreateCubeTexture ( owner - > GetWidth ( ) , 1 , D3DUSAGE_RENDERTARGET , surfFormat ,
D3DPOOL_DEFAULT , ( IDirect3DCubeTexture9 * * ) & cTexture , NULL ) ) )
{
/// Create a CUBIC texture
for ( i = 0 ; i < 6 ; i + + )
{
plRenderTarget * face = cubicRT - > GetFace ( i ) ;
plDXRenderTargetRef * fRef ;
if ( face - > GetDeviceRef ( ) ! = nil )
{
fRef = ( plDXRenderTargetRef * ) face - > GetDeviceRef ( ) ;
fRef - > Set ( surfFormat , 0 , face ) ;
if ( ! fRef - > IsLinked ( ) )
fRef - > Link ( & fRenderTargetRefList ) ;
}
else
{
face - > SetDeviceRef ( TRACKED_NEW plDXRenderTargetRef ( surfFormat , 0 , face , false ) ) ;
( ( plDXRenderTargetRef * ) face - > GetDeviceRef ( ) ) - > Link ( & fRenderTargetRefList ) ;
// Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least)
hsRefCnt_SafeUnRef ( face - > GetDeviceRef ( ) ) ;
}
}
D3DSURF_MEMNEW ( cTexture ) ;
ref - > SetTexture ( cTexture , depthSurface ) ;
}
else
{
ReleaseObject ( depthSurface ) ;
hsRefCnt_SafeUnRef ( ref ) ;
ref = nil ;
}
}
// Is it a texture render target? Probably, since shadow maps are all we use this for.
else if ( owner - > GetFlags ( ) & plRenderTarget : : kIsTexture )
{
/// Create a normal texture
if ( ref ! = nil )
ref - > Set ( surfFormat , 0 , owner ) ;
else
ref = TRACKED_NEW plDXRenderTargetRef ( surfFormat , 0 , owner ) ;
hsAssert ( ! fManagedAlloced , " Alloc default with managed alloc'd " ) ;
if ( ! FAILED ( fD3DDevice - > CreateTexture ( owner - > GetWidth ( ) , owner - > GetHeight ( ) , 1 , D3DUSAGE_RENDERTARGET , surfFormat ,
D3DPOOL_DEFAULT , ( IDirect3DTexture9 * * ) & texture , NULL ) ) )
{
D3DSURF_MEMNEW ( texture ) ;
ref - > SetTexture ( texture , depthSurface ) ;
}
else
{
ReleaseObject ( depthSurface ) ;
hsRefCnt_SafeUnRef ( ref ) ;
ref = nil ;
}
}
// Pretty sure this code path has never been followed.
else if ( owner - > GetFlags ( ) & plRenderTarget : : kIsOffscreen )
{
/// Create a blank surface
if ( ref ! = nil )
ref - > Set ( surfFormat , 0 , owner ) ;
else
ref = TRACKED_NEW plDXRenderTargetRef ( surfFormat , 0 , owner ) ;
width = owner - > GetWidth ( ) ;
height = owner - > GetHeight ( ) ;
if ( ! FAILED ( fD3DDevice - > CreateRenderTarget ( width , height , surfFormat ,
D3DMULTISAMPLE_NONE , 0 ,
FALSE , & surface , NULL ) ) )
{
D3DSURF_MEMNEW ( surface ) ;
ref - > SetTexture ( surface , depthSurface ) ;
}
else
{
ReleaseObject ( depthSurface ) ;
hsRefCnt_SafeUnRef ( ref ) ;
ref = nil ;
}
}
if ( owner - > GetDeviceRef ( ) ! = ref )
{
owner - > SetDeviceRef ( ref ) ;
// Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least)
hsRefCnt_SafeUnRef ( ref ) ;
if ( ref ! = nil & & ! ref - > IsLinked ( ) )
ref - > Link ( & fRenderTargetRefList ) ;
}
else
{
if ( ref ! = nil & & ! ref - > IsLinked ( ) )
ref - > Link ( & fRenderTargetRefList ) ;
}
if ( ref ! = nil )
{
ref - > SetDirty ( false ) ;
}
return ref ;
}
//// IPrepRenderTargetInfo ////////////////////////////////////////////////////
// Shared processing of render target creation parameters. Also does the
// dirty work of finding a good surface format to use.
hsBool plDXPipeline : : IPrepRenderTargetInfo ( plRenderTarget * owner , D3DFORMAT & surfFormat ,
D3DFORMAT & depthFormat , D3DRESOURCETYPE & resType )
{
int i , j ;
UInt16 flags , width , height ;
Int8 bitDepth , zDepth , stencilDepth , stencilIndex ;
D3DFORMAT depthFormats [ ] = { D3DFMT_D24X8 , D3DFMT_D24X4S4 , D3DFMT_D24S8 } ;
flags = owner - > GetFlags ( ) ;
width = owner - > GetWidth ( ) ;
height = owner - > GetHeight ( ) ;
bitDepth = owner - > GetPixelSize ( ) ;
zDepth = owner - > GetZDepth ( ) ;
stencilDepth = owner - > GetStencilDepth ( ) ;
if ( flags ! = 0 )
{
if ( flags & plRenderTarget : : kIsTexture )
{
/// Do an extra check for width and height here
for ( i = width > > 1 , j = 0 ; i ! = 0 ; i > > = 1 , j + + ) ;
if ( width ! = ( 1 < < j ) )
return false ;
for ( i = height > > 1 , j = 0 ; i ! = 0 ; i > > = 1 , j + + ) ;
if ( height ! = ( 1 < < j ) )
return false ;
resType = D3DRTYPE_TEXTURE ;
}
else
resType = D3DRTYPE_SURFACE ;
if ( bitDepth = = 16 )
surfFormat = D3DFMT_A4R4G4B4 ;
else if ( bitDepth = = 32 )
surfFormat = D3DFMT_A8R8G8B8 ;
/// Get the backbuffer format (if one is requested)
if ( zDepth )
{
if ( zDepth = = 16 & & stencilDepth = = 0 )
depthFormat = D3DFMT_D16 ;
else if ( zDepth = = 24 )
{
if ( stencilDepth = = 0 ) stencilIndex = 0 ;
else if ( stencilDepth < = 4 ) stencilIndex = 1 ;
else if ( stencilDepth < = 8 ) stencilIndex = 2 ;
else
stencilIndex = 2 ;
depthFormat = depthFormats [ stencilIndex ] ;
}
else if ( zDepth = = 32 & & stencilDepth = = 0 )
depthFormat = D3DFMT_D32 ;
else if ( zDepth = = 15 & & stencilDepth = = 1 )
depthFormat = D3DFMT_D15S1 ;
if ( surfFormat = = D3DFMT_UNKNOWN | | depthFormat = = D3DFMT_UNKNOWN )
{
return false ;
}
}
else
{
depthFormat = D3DFMT_UNKNOWN ;
}
/// Check the device format
if ( FAILED ( fSettings . fDXError = fD3DObject - > CheckDeviceFormat ( fCurrentAdapter , fCurrentDevice - > fDDType , fCurrentMode - > fDDmode . Format ,
D3DUSAGE_RENDERTARGET , resType , surfFormat ) ) )
{
if ( bitDepth = = 16 )
{
bitDepth = 32 ;
surfFormat = D3DFMT_A8R8G8B8 ;
}
else if ( bitDepth = = 32 )
{
bitDepth = 16 ;
surfFormat = D3DFMT_A4R4G4B4 ;
}
if ( FAILED ( fSettings . fDXError = fD3DObject - > CheckDeviceFormat ( fCurrentAdapter , fCurrentDevice - > fDDType , fCurrentMode - > fDDmode . Format ,
D3DUSAGE_RENDERTARGET , resType , surfFormat ) ) )
{
IGetD3DError ( ) ;
return false ;
}
}
if ( zDepth )
{
while ( FAILED ( fSettings . fDXError = fD3DObject - > CheckDeviceFormat ( fCurrentAdapter , fCurrentDevice - > fDDType , fCurrentMode - > fDDmode . Format ,
D3DUSAGE_DEPTHSTENCIL , D3DRTYPE_SURFACE , depthFormat ) ) )
{
if ( stencilIndex < sizeof ( depthFormats ) / sizeof ( depthFormats [ 0 ] ) - 1 )
{
stencilIndex + + ;
depthFormat = depthFormats [ stencilIndex ] ;
}
else
{
IGetD3DError ( ) ;
return false ;
}
}
if ( FAILED ( fSettings . fDXError = fD3DObject - > CheckDepthStencilMatch ( fCurrentAdapter , fCurrentDevice - > fDDType , fCurrentMode - > fDDmode . Format ,
surfFormat , depthFormat ) ) )
{
IGetD3DError ( ) ;
return false ;
}
}
}
return true ;
}
//// IFindRenderTargetInfo ////////////////////////////////////////////////////
// Shared processing of render target creation parameters. Also does the
// dirty work of finding a good surface format to use.
// Doesn't bother checking depth buffer, since this is only used for a render target
// that's going to share a depth buffer that's already been created.
hsBool plDXPipeline : : IFindRenderTargetInfo ( plRenderTarget * owner , D3DFORMAT & surfFormat , D3DRESOURCETYPE & resType )
{
UInt16 flags , width , height ;
Int8 bitDepth ;
flags = owner - > GetFlags ( ) ;
width = owner - > GetWidth ( ) ;
height = owner - > GetHeight ( ) ;
bitDepth = owner - > GetPixelSize ( ) ;
if ( flags ! = 0 )
{
if ( flags & plRenderTarget : : kIsTexture )
{
resType = D3DRTYPE_TEXTURE ;
}
else
resType = D3DRTYPE_SURFACE ;
if ( bitDepth = = 16 )
surfFormat = D3DFMT_A4R4G4B4 ;
else if ( bitDepth = = 32 )
surfFormat = D3DFMT_A8R8G8B8 ;
if ( surfFormat = = D3DFMT_UNKNOWN )
{
return false ;
}
/// Check the device format
if ( FAILED ( fSettings . fDXError = fD3DObject - > CheckDeviceFormat ( fCurrentAdapter , fCurrentDevice - > fDDType , fCurrentMode - > fDDmode . Format ,
D3DUSAGE_RENDERTARGET , resType , surfFormat ) ) )
{
if ( bitDepth = = 16 )
{
bitDepth = 32 ;
surfFormat = D3DFMT_A8R8G8B8 ;
}
else if ( bitDepth = = 32 )
{
bitDepth = 16 ;
surfFormat = D3DFMT_A4R4G4B4 ;
}
if ( FAILED ( fSettings . fDXError = fD3DObject - > CheckDeviceFormat ( fCurrentAdapter , fCurrentDevice - > fDDType , fCurrentMode - > fDDmode . Format ,
D3DUSAGE_RENDERTARGET , resType , surfFormat ) ) )
{
IGetD3DError ( ) ;
return false ;
}
}
}
return true ;
}
// PushRenderRequest ///////////////////////////////////////////////
// We're moving from our current render (probably to primary) onto
// another specialized render request. This may be to the primary (if req->GetRenderTarget() is nil)
// or to a texture. This function saves enough state to resume rendering on PopRenderRequest.
// The render request may just be a new camera position.
void plDXPipeline : : PushRenderRequest ( plRenderRequest * req )
{
// Save these, since we want to copy them to our current view
hsMatrix44 l2w = fView . fLocalToWorld ;
hsMatrix44 w2l = fView . fWorldToLocal ;
plFogEnvironment defFog = fView . fDefaultFog ;
fSettings . fViewStack . Push ( fView ) ;
SetViewTransform ( req - > GetViewTransform ( ) ) ;
PushRenderTarget ( req - > GetRenderTarget ( ) ) ;
fView . fRenderState = req - > GetRenderState ( ) ;
fView . fRenderRequest = req ;
hsRefCnt_SafeRef ( fView . fRenderRequest ) ;
SetDrawableTypeMask ( req - > GetDrawableMask ( ) ) ;
SetSubDrawableTypeMask ( req - > GetSubDrawableMask ( ) ) ;
fView . fClearColor = inlGetD3DColor ( req - > GetClearColor ( ) ) ;
fView . fClearDepth = req - > GetClearDepth ( ) ;
if ( req - > GetFogStart ( ) < 0 )
{
fView . fDefaultFog = defFog ;
}
else
{
fView . fDefaultFog . Set ( req - > GetYon ( ) * ( 1.f - req - > GetFogStart ( ) ) , req - > GetYon ( ) , 1.f , & req - > GetClearColor ( ) ) ;
fCurrFog . fEnvPtr = nil ;
}
if ( req - > GetOverrideMat ( ) )
PushOverrideMaterial ( req - > GetOverrideMat ( ) ) ;
// Set from our saved ones...
fView . fWorldToLocal = w2l ;
fView . fLocalToWorld = l2w ;
RefreshMatrices ( ) ;
if ( req - > GetIgnoreOccluders ( ) )
fView . fCullMaxNodes = 0 ;
fView . fCullTreeDirty = true ;
}
// PopRenderRequest //////////////////////////////////////////////////
// Restore state to resume rendering as before the preceding PushRenderRequest.
void plDXPipeline : : PopRenderRequest ( plRenderRequest * req )
{
if ( req - > GetOverrideMat ( ) )
PopOverrideMaterial ( nil ) ;
hsRefCnt_SafeUnRef ( fView . fRenderRequest ) ;
fView = fSettings . fViewStack . Pop ( ) ;
// Force the next thing drawn to update the fog settings.
fD3DDevice - > SetRenderState ( D3DRS_FOGENABLE , FALSE ) ;
fCurrFog . fEnvPtr = nil ;
PopRenderTarget ( ) ;
fView . fXformResetFlags = fView . kResetProjection | fView . kResetCamera ;
}
//// PushRenderTarget /////////////////////////////////////////////////////////
// Begin rendering to the specified target. If target is nil, that's the primary surface.
void plDXPipeline : : PushRenderTarget ( plRenderTarget * target )
{
//WHITE
# ifdef MF_ENABLE_HACKOFF
if ( target & & ( hackOffscreens . kMissingIndex = = hackOffscreens . Find ( target ) ) )
hackOffscreens . Append ( target ) ;
# endif // MF_ENABLE_HACKOFF
fSettings . fCurrRenderTarget = target ;
hsRefCnt_SafeAssign ( fSettings . fCurrRenderTargetRef , ( target ! = nil ) ? ( plDXDeviceRef * ) target - > GetDeviceRef ( ) : nil ) ;
while ( target ! = nil )
{
fSettings . fCurrBaseRenderTarget = target ;
target = target - > GetParent ( ) ;
}
fSettings . fRenderTargets . Push ( fSettings . fCurrRenderTarget ) ;
ISetRenderTarget ( fSettings . fCurrRenderTarget ) ;
}
//// PopRenderTarget //////////////////////////////////////////////////////////
// Resume rendering to the render target before the last PushRenderTarget,
// making sure we aren't holding on to anything from the render target getting
// popped.
plRenderTarget * plDXPipeline : : PopRenderTarget ( )
{
plRenderTarget * old = fSettings . fRenderTargets . Pop ( ) , * temp ;
int i = fSettings . fRenderTargets . GetCount ( ) ;
if ( i = = 0 )
{
fSettings . fCurrRenderTarget = nil ;
fSettings . fCurrBaseRenderTarget = nil ;
hsRefCnt_SafeUnRef ( fSettings . fCurrRenderTargetRef ) ;
fSettings . fCurrRenderTargetRef = nil ;
}
else
{
fSettings . fCurrRenderTarget = fSettings . fRenderTargets [ i - 1 ] ;
temp = fSettings . fCurrRenderTarget ;
while ( temp ! = nil )
{
fSettings . fCurrBaseRenderTarget = temp ;
temp = temp - > GetParent ( ) ;
}
hsRefCnt_SafeAssign ( fSettings . fCurrRenderTargetRef ,
( fSettings . fCurrRenderTarget ! = nil ) ?
( plDXDeviceRef * ) fSettings . fCurrRenderTarget - > GetDeviceRef ( )
: nil ) ;
}
ISetRenderTarget ( fSettings . fCurrRenderTarget ) ;
return old ;
}
// ISetAnisotropy ///////////////////////////////////////////////////////////
// Set the current anisotropic filtering settings to D3D
void plDXPipeline : : ISetAnisotropy ( hsBool on )
{
if ( ( fSettings . fMaxAnisotropicSamples < = 0 ) | | IsDebugFlagSet ( plPipeDbg : : kFlagNoAnisotropy ) )
on = false ;
if ( on = = fSettings . fCurrAnisotropy )
return ;
if ( on )
{
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
// GeForce cards have decided that they no longer handle anisotropic as a mag filter.
// We could detect caps... but I don't think we'd notice if we just made the mag
// filter always be linear.
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MINFILTER , D3DTEXF_ANISOTROPIC ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MAGFILTER , D3DTEXF_LINEAR ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MAXANISOTROPY , ( DWORD ) fSettings . fMaxAnisotropicSamples ) ;
}
fSettings . fCurrAnisotropy = true ;
}
else
{
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MINFILTER , D3DTEXF_LINEAR ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_MAGFILTER , D3DTEXF_LINEAR ) ;
}
fSettings . fCurrAnisotropy = false ;
}
}
//// ISetRenderTarget /////////////////////////////////////////////////////////
// Set rendering to the specified render target. Nil rendertarget is the primary.
// Invalidates the state as required by experience, not documentation.
void plDXPipeline : : ISetRenderTarget ( plRenderTarget * target )
{
IDirect3DSurface9 * main , * depth ;
plDXRenderTargetRef * ref = nil ;
if ( target ! = nil )
{
ref = ( plDXRenderTargetRef * ) target - > GetDeviceRef ( ) ;
if ( ref = = nil | | ref - > IsDirty ( ) )
ref = ( plDXRenderTargetRef * ) MakeRenderTargetRef ( target ) ;
}
if ( ref = = nil | | ref - > GetColorSurface ( ) = = nil )
{
/// Set to main screen
main = fD3DMainSurface ;
depth = fD3DDepthSurface ;
ISetAnisotropy ( true ) ;
}
else
{
/// Set to this target
main = ref - > GetColorSurface ( ) ;
depth = ref - > fD3DDepthSurface ;
ISetAnisotropy ( false ) ;
}
if ( main ! = fSettings . fCurrD3DMainSurface | | depth ! = fSettings . fCurrD3DDepthSurface )
{
fSettings . fCurrD3DMainSurface = main ;
fSettings . fCurrD3DDepthSurface = depth ;
fD3DDevice - > SetRenderTarget ( 0 , main ) ;
fD3DDevice - > SetDepthStencilSurface ( depth ) ;
}
IInvalidateState ( ) ;
ISetViewport ( ) ;
}
// SetClear /////////////////////////////////////////////////////////////////////
// Set the color and depth clear values.
void plDXPipeline : : SetClear ( const hsColorRGBA * col , const hsScalar * depth )
{
if ( col )
fView . fClearColor = inlGetD3DColor ( * col ) ;
if ( depth )
fView . fClearDepth = * depth ;
}
// GetClearColor ////////////////////////////////////////////////////////////////
// Return the current clear color.
hsColorRGBA plDXPipeline : : GetClearColor ( ) const
{
return hsColorRGBA ( ) . FromARGB32 ( fView . fClearColor ) ;
}
// GetClearDepth ////////////////////////////////////////////////////////////////
// Return the current clear depth.
hsScalar plDXPipeline : : GetClearDepth ( ) const
{
return fView . fClearDepth ;
}
//// ClearRenderTarget ////////////////////////////////////////////////////////
// Clear the current color and depth buffers. If a drawable is passed in, then
// the color buffer will be cleared by rendering that drawable.
// The depth buffer is always cleared with a clear call.
// Clearing of depth and/or color may be turned off by setting the kRenderClearDepth
// and kRenderClearColor bits in fView.fRenderState to false.
void plDXPipeline : : ClearRenderTarget ( plDrawable * d )
{
plDrawableSpans * src = plDrawableSpans : : ConvertNoRef ( d ) ;
if ( ! src )
{
ClearRenderTarget ( ) ;
return ;
}
// First clear the depth buffer as normal.
if ( fView . fRenderState & kRenderClearDepth )
{
D3DRECT r ;
hsBool useRect = IGetClearViewPort ( r ) ;
if ( useRect )
{
WEAK_ERROR_CHECK ( fD3DDevice - > Clear ( 1 , & r , D3DCLEAR_ZBUFFER , 0 , fView . fClearDepth , 0L ) ) ;
}
else
{
WEAK_ERROR_CHECK ( fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_ZBUFFER , 0 , fView . fClearDepth , 0L ) ) ;
// debug, clears to red WEAK_ERROR_CHECK( fD3DDevice->Clear( 0, nil, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, 0xffff0000, fView.fClearDepth, 0L ) );
}
}
UInt32 s = fView . fRenderState ;
UInt32 dtm = fView . fDrawableTypeMask ;
UInt32 sdtm = fView . fSubDrawableTypeMask ;
fView . fDrawableTypeMask = plDrawable : : kNormal ;
fView . fSubDrawableTypeMask = UInt32 ( - 1 ) ;
BeginDrawable ( d ) ;
Draw ( d ) ;
EndDrawable ( d ) ;
fView . fSubDrawableTypeMask = sdtm ;
fView . fDrawableTypeMask = dtm ;
fView . fRenderState = s ;
}
// IGetClearViewPort //////////////////////////////////////////////
// Sets the input rect to the current viewport. Returns true if
// that is a subset of the current render target, else false.
hsBool plDXPipeline : : IGetClearViewPort ( D3DRECT & r )
{
r . x1 = GetViewTransform ( ) . GetViewPortLeft ( ) ;
r . y1 = GetViewTransform ( ) . GetViewPortTop ( ) ;
r . x2 = GetViewTransform ( ) . GetViewPortRight ( ) ;
r . y2 = GetViewTransform ( ) . GetViewPortBottom ( ) ;
hsBool useRect = false ;
if ( fSettings . fCurrRenderTarget ! = nil )
{
useRect = ( ( r . x1 ! = 0 ) | | ( r . y1 ! = 0 ) | | ( r . x2 ! = fSettings . fCurrRenderTarget - > GetWidth ( ) ) | | ( r . y2 ! = fSettings . fCurrRenderTarget - > GetHeight ( ) ) ) ;
}
else
{
useRect = ( ( r . x1 ! = 0 ) | | ( r . y1 ! = 0 ) | | ( r . x2 ! = fSettings . fOrigWidth ) | | ( r . y2 ! = fSettings . fOrigHeight ) ) ;
}
return useRect ;
}
// ClearRenderTarget //////////////////////////////////////////////////////////////////////////////
// Flat fill the current render target with the specified color and depth values.
void plDXPipeline : : ClearRenderTarget ( const hsColorRGBA * col , const hsScalar * depth )
{
if ( fView . fRenderState & ( kRenderClearColor | kRenderClearDepth ) )
{
DWORD clearColor = inlGetD3DColor ( col ? * col : GetClearColor ( ) ) ;
hsScalar clearDepth = depth ? * depth : fView . fClearDepth ;
DWORD dwFlags = 0 ; //fStencil.fDepth > 0 ? D3DCLEAR_STENCIL : 0;
if ( fView . fRenderState & kRenderClearColor )
dwFlags | = D3DCLEAR_TARGET ;
if ( fView . fRenderState & kRenderClearDepth )
dwFlags | = D3DCLEAR_ZBUFFER ;
D3DRECT r ;
hsBool useRect = IGetClearViewPort ( r ) ;
if ( useRect )
{
WEAK_ERROR_CHECK ( fD3DDevice - > Clear ( 1 , & r , dwFlags , clearColor , clearDepth , 0L ) ) ;
}
else
{
WEAK_ERROR_CHECK ( fD3DDevice - > Clear ( 0 , nil , dwFlags , clearColor , clearDepth , 0L ) ) ;
}
}
}
///////////////////////////////////////////////////////////////////////////////
//// Fog //////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// The current fog system sucks. It was never meant to get used this way, but
// the production artists started using it with debug commands that were around,
// and before they could be stopped it was too late.
// The good news is that there's a lot that could be done with fog here that
// would be greatly appreciated.
// IGetVSFogSet ///////////////////////////////////////////////////////////////
// Translate the current fog settings into a linear fog that the current
// vertex shaders can use.
void plDXPipeline : : IGetVSFogSet ( float * const set ) const
{
set [ 2 ] = 0.f ;
set [ 3 ] = 1.f ;
if ( fCurrFog . fEnvPtr )
{
hsColorRGBA colorTrash ;
hsScalar start ;
hsScalar end ;
fCurrFog . fEnvPtr - > GetPipelineParams ( & start , & end , & colorTrash ) ;
if ( end > start )
{
set [ 0 ] = - end ;
set [ 1 ] = 1.f / ( start - end ) ;
}
else
{
set [ 0 ] = 1.f ;
set [ 1 ] = 0.f ;
}
}
else
{
set [ 0 ] = 1.f ;
set [ 1 ] = 0.f ;
}
}
//// ISetFogParameters ////////////////////////////////////////////////////////
// So looking at this function, one might guess that fog parameters were settable
// individually for different objects, and that was the original intent, with transitions
// as something like the avatar moved from one fog region to another.
// Never happened.
// So the current state is that there is one set of fog parameters per age, and things
// are either fogged, or not fogged.
// This is complicated by the DX vertex/pixel shaders only supporting per-vertex fog,
// so the same plasma fog settings may turn into differing D3D fog state.
void plDXPipeline : : ISetFogParameters ( const plSpan * span , const plLayerInterface * baseLay )
{
# ifndef PLASMA_EXTERNAL_RELEASE
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoFog ) )
{
fCurrFog . fEnvPtr = nil ;
fD3DDevice - > SetRenderState ( D3DRS_FOGENABLE , FALSE ) ;
return ;
}
# endif // PLASMA_EXTERNAL_RELEASE
plFogEnvironment * fog = ( span ? ( span - > fFogEnvironment ? span - > fFogEnvironment : & fView . fDefaultFog ) : nil ) ;
UInt8 isVertex = 0 ;
UInt8 isShader = false ;
if ( baseLay )
{
if ( ( baseLay - > GetShadeFlags ( ) & hsGMatState : : kShadeReallyNoFog ) & & ! ( fMatOverOff . fShadeFlags & hsGMatState : : kShadeReallyNoFog ) )
fog = nil ;
if ( baseLay - > GetVertexShader ( ) )
isShader = true ;
}
if ( fMatOverOn . fShadeFlags & hsGMatState : : kShadeReallyNoFog )
fog = nil ;
bool forceLoad = false ;
D3DRENDERSTATETYPE d3dFogType = D3DRS_FOGTABLEMODE ; // Use VERTEXMODE for vertex fog
# if !HS_BUILD_FOR_XBOX
if ( ! ( fSettings . fD3DCaps & kCapsPixelFog ) | | isShader )
{
d3dFogType = D3DRS_FOGVERTEXMODE ;
isVertex = true ;
}
# endif
// Quick check
if ( ( fCurrFog . fEnvPtr = = fog ) & & ( fCurrFog . fIsVertex = = isVertex ) & & ( fCurrFog . fIsShader = = isShader ) )
return ;
UInt8 type = ( fog = = nil ) ? plFogEnvironment : : kNoFog : fog - > GetType ( ) ;
if ( type = = plFogEnvironment : : kNoFog )
{
/// No fog, just disable
fD3DDevice - > SetRenderState ( D3DRS_FOGENABLE , FALSE ) ;
fCurrFog . fEnvPtr = nil ;
return ;
}
else if ( fCurrFog . fEnvPtr ! = fog )
{
fD3DDevice - > SetRenderState ( D3DRS_FOGENABLE , TRUE ) ;
forceLoad = true ;
fCurrFog . fEnvPtr = fog ;
}
if ( isShader )
type = plFogEnvironment : : kLinearFog ;
if ( fCurrFog . fIsShader ! = isShader )
forceLoad = true ;
if ( fCurrFog . fIsVertex ! = isVertex )
forceLoad = true ;
fCurrFog . fIsShader = isShader ;
fCurrFog . fIsVertex = isVertex ;
hsScalar startOrDensity , end ;
hsColorRGBA color ;
/// Get params
if ( type = = plFogEnvironment : : kLinearFog )
{
fog - > GetPipelineParams ( & startOrDensity , & end , & color ) ;
if ( startOrDensity = = end )
{
// This should be legal, but some cards don't like it. Just disable. Same thing.
fD3DDevice - > SetRenderState ( D3DRS_FOGENABLE , FALSE ) ;
return ;
}
}
else
fog - > GetPipelineParams ( & startOrDensity , & color ) ;
if ( isShader )
{
// None of this is technically necessary, but it's to work around
// a known goofiness in the NVidia drivers. Actually, I don't think
// having to set the tablemode fog to linear in addition to setting
// the vertexmode is even a "known" issue. But turns out to be
// necessary on GeForceFX latest drivers.
startOrDensity = 1.f ;
end = 0.f ;
// Setting FOGTABLEMODE to none seems to work on both ATI and NVidia,
// but I haven't tried it on the GeForceFX yet.
// if( fCurrFog.fMode != D3DFOG_LINEAR )
// fD3DDevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_LINEAR);
fD3DDevice - > SetRenderState ( D3DRS_FOGTABLEMODE , D3DFOG_NONE ) ;
}
/// Set color
if ( ! ( fCurrFog . fColor = = color ) | | forceLoad )
{
fCurrFog . fColor = color ;
fCurrFog . fHexColor = inlGetD3DColor ( color ) ;
fD3DDevice - > SetRenderState ( D3DRS_FOGCOLOR , fCurrFog . fHexColor ) ;
}
D3DFOGMODE modes [ 4 ] = { D3DFOG_LINEAR , D3DFOG_EXP , D3DFOG_EXP2 , D3DFOG_NONE } ;
/// Set type
if ( fCurrFog . fMode ! = modes [ type ] | | forceLoad )
{
fCurrFog . fMode = modes [ type ] ;
if ( fCurrFog . fMode = = D3DFOG_LINEAR )
{
fCurrFog . fStart = startOrDensity ;
fCurrFog . fEnd = end ;
fD3DDevice - > SetRenderState ( d3dFogType , fCurrFog . fMode ) ;
fD3DDevice - > SetRenderState ( D3DRS_FOGSTART , * ( DWORD * ) ( & fCurrFog . fStart ) ) ;
fD3DDevice - > SetRenderState ( D3DRS_FOGEND , * ( DWORD * ) ( & fCurrFog . fEnd ) ) ;
}
else
{
fCurrFog . fDensity = startOrDensity ;
fD3DDevice - > SetRenderState ( d3dFogType , fCurrFog . fMode ) ;
fD3DDevice - > SetRenderState ( D3DRS_FOGDENSITY , * ( DWORD * ) ( & fCurrFog . fDensity ) ) ;
}
}
else
{
// Type is the same, but are the params?
if ( fCurrFog . fMode = = D3DFOG_LINEAR )
{
if ( fCurrFog . fStart ! = startOrDensity )
{
fCurrFog . fStart = startOrDensity ;
fD3DDevice - > SetRenderState ( D3DRS_FOGSTART , * ( DWORD * ) ( & fCurrFog . fStart ) ) ;
}
if ( fCurrFog . fEnd ! = end )
{
fCurrFog . fEnd = end ;
fD3DDevice - > SetRenderState ( D3DRS_FOGEND , * ( DWORD * ) ( & fCurrFog . fEnd ) ) ;
}
}
else
{
if ( fCurrFog . fDensity ! = startOrDensity )
{
fCurrFog . fDensity = startOrDensity ;
fD3DDevice - > SetRenderState ( D3DRS_FOGDENSITY , * ( DWORD * ) ( & fCurrFog . fDensity ) ) ;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
//// Stenciling ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// I know that none of this stencil code has ever been used in production.
// To my knowledge, none of this stencil code was ever even tested.
// It may save you some time as a starting point, but don't trust it.
//// StencilEnable ////////////////////////////////////////////////////////////
hsBool plDXPipeline : : StencilEnable ( hsBool enable )
{
if ( fStencil . fEnabled = = enable )
return true ;
if ( enable & & fStencil . fDepth = = 0 )
return false ; // Can't enable stenciling when we don't support it!
fD3DDevice - > SetRenderState ( D3DRS_STENCILENABLE , enable ? TRUE : FALSE ) ;
return true ;
}
//// StencilSetCompareFunc ////////////////////////////////////////////////////
void plDXPipeline : : StencilSetCompareFunc ( UInt8 func , UInt32 refValue )
{
D3DCMPFUNC newFunc ;
switch ( func )
{
case plStencilCaps : : kCmpNever : newFunc = D3DCMP_NEVER ; break ;
case plStencilCaps : : kCmpLessThan : newFunc = D3DCMP_LESS ; break ;
case plStencilCaps : : kCmpEqual : newFunc = D3DCMP_EQUAL ; break ;
case plStencilCaps : : kCmpLessThanOrEqual : newFunc = D3DCMP_LESSEQUAL ; break ;
case plStencilCaps : : kCmpGreaterThan : newFunc = D3DCMP_GREATER ; break ;
case plStencilCaps : : kCmpNotEqual : newFunc = D3DCMP_NOTEQUAL ; break ;
case plStencilCaps : : kCmpGreaterThanOrEqual : newFunc = D3DCMP_GREATEREQUAL ; break ;
case plStencilCaps : : kCmpAlways : newFunc = D3DCMP_ALWAYS ; break ;
default : hsAssert ( false , " Invalid compare function to StencilSetCompareFunc() " ) ; return ;
}
if ( fStencil . fCmpFunc ! = newFunc )
{
fD3DDevice - > SetRenderState ( D3DRS_STENCILFUNC , newFunc ) ;
fStencil . fCmpFunc = newFunc ;
}
if ( fStencil . fRefValue ! = refValue )
{
fD3DDevice - > SetRenderState ( D3DRS_STENCILREF , refValue ) ;
fStencil . fRefValue = refValue ;
}
}
//// StencilSetMask ///////////////////////////////////////////////////////////
void plDXPipeline : : StencilSetMask ( UInt32 mask , UInt32 writeMask )
{
if ( fStencil . fMask ! = mask )
{
fD3DDevice - > SetRenderState ( D3DRS_STENCILMASK , mask ) ;
fStencil . fMask = mask ;
}
if ( fStencil . fWriteMask ! = writeMask )
{
fD3DDevice - > SetRenderState ( D3DRS_STENCILWRITEMASK , writeMask ) ;
fStencil . fWriteMask = writeMask ;
}
}
//// StencilSetOps ////////////////////////////////////////////////////////////
void plDXPipeline : : StencilSetOps ( UInt8 passOp , UInt8 failOp , UInt8 passButZFailOp )
{
D3DSTENCILOP op ;
/// Pass op
switch ( passOp )
{
case plStencilCaps : : kOpKeep : op = D3DSTENCILOP_KEEP ; break ;
case plStencilCaps : : kOpSetToZero : op = D3DSTENCILOP_ZERO ; break ;
case plStencilCaps : : kOpReplace : op = D3DSTENCILOP_REPLACE ; break ;
case plStencilCaps : : kOpIncClamp : op = D3DSTENCILOP_INCRSAT ; break ;
case plStencilCaps : : kOpDecClamp : op = D3DSTENCILOP_DECRSAT ; break ;
case plStencilCaps : : kOpInvert : op = D3DSTENCILOP_INVERT ; break ;
case plStencilCaps : : kOpIncWrap : op = D3DSTENCILOP_INCR ; break ;
case plStencilCaps : : kOpDecWrap : op = D3DSTENCILOP_DECR ; break ;
default : hsAssert ( false , " Invalid op to StencilSetOps() " ) ; return ;
}
if ( fStencil . fPassOp ! = op )
{
fD3DDevice - > SetRenderState ( D3DRS_STENCILPASS , op ) ;
fStencil . fPassOp = op ;
}
/// Fail op
switch ( failOp )
{
case plStencilCaps : : kOpKeep : op = D3DSTENCILOP_KEEP ; break ;
case plStencilCaps : : kOpSetToZero : op = D3DSTENCILOP_ZERO ; break ;
case plStencilCaps : : kOpReplace : op = D3DSTENCILOP_REPLACE ; break ;
case plStencilCaps : : kOpIncClamp : op = D3DSTENCILOP_INCRSAT ; break ;
case plStencilCaps : : kOpDecClamp : op = D3DSTENCILOP_DECRSAT ; break ;
case plStencilCaps : : kOpInvert : op = D3DSTENCILOP_INVERT ; break ;
case plStencilCaps : : kOpIncWrap : op = D3DSTENCILOP_INCR ; break ;
case plStencilCaps : : kOpDecWrap : op = D3DSTENCILOP_DECR ; break ;
default : hsAssert ( false , " Invalid op to StencilSetOps() " ) ; return ;
}
if ( fStencil . fFailOp ! = op )
{
fD3DDevice - > SetRenderState ( D3DRS_STENCILFAIL , op ) ;
fStencil . fFailOp = op ;
}
/// Pass-but-z-fail op
switch ( passButZFailOp )
{
case plStencilCaps : : kOpKeep : op = D3DSTENCILOP_KEEP ; break ;
case plStencilCaps : : kOpSetToZero : op = D3DSTENCILOP_ZERO ; break ;
case plStencilCaps : : kOpReplace : op = D3DSTENCILOP_REPLACE ; break ;
case plStencilCaps : : kOpIncClamp : op = D3DSTENCILOP_INCRSAT ; break ;
case plStencilCaps : : kOpDecClamp : op = D3DSTENCILOP_DECRSAT ; break ;
case plStencilCaps : : kOpInvert : op = D3DSTENCILOP_INVERT ; break ;
case plStencilCaps : : kOpIncWrap : op = D3DSTENCILOP_INCR ; break ;
case plStencilCaps : : kOpDecWrap : op = D3DSTENCILOP_DECR ; break ;
default : hsAssert ( false , " Invalid op to StencilSetOps() " ) ; return ;
}
if ( fStencil . fPassButZFailOp ! = op )
{
fD3DDevice - > SetRenderState ( D3DRS_STENCILZFAIL , op ) ;
fStencil . fPassButZFailOp = op ;
}
}
//// StencilGetCaps ///////////////////////////////////////////////////////////
hsBool plDXPipeline : : StencilGetCaps ( plStencilCaps * caps )
{
hsAssert ( caps ! = nil , " Invalid pointer to StencilGetCaps() " ) ;
int i ;
/// Find supported depths
caps - > fSupportedDepths = 0 ;
for ( i = 0 ; i < fCurrentMode - > fDepthFormats . GetCount ( ) ; i + + )
{
switch ( fCurrentMode - > fDepthFormats [ i ] )
{
case D3DFMT_D15S1 : caps - > fSupportedDepths | = plStencilCaps : : kDepth1Bit ; break ;
case D3DFMT_D24X4S4 : caps - > fSupportedDepths | = plStencilCaps : : kDepth4Bits ; break ;
case D3DFMT_D24S8 : caps - > fSupportedDepths | = plStencilCaps : : kDepth8Bits ; break ;
}
}
if ( caps - > fSupportedDepths = = 0 )
{
caps - > fIsSupported = false ;
return false ;
}
/// Get supported ops
caps - > fSupportedOps = 0 ;
if ( fCurrentDevice - > fDDCaps . StencilCaps & D3DSTENCILCAPS_DECR )
caps - > fSupportedOps | = plStencilCaps : : kOpDecWrap ;
if ( fCurrentDevice - > fDDCaps . StencilCaps & D3DSTENCILCAPS_DECRSAT )
caps - > fSupportedOps | = plStencilCaps : : kOpDecClamp ;
if ( fCurrentDevice - > fDDCaps . StencilCaps & D3DSTENCILCAPS_INCR )
caps - > fSupportedOps | = plStencilCaps : : kOpIncWrap ;
if ( fCurrentDevice - > fDDCaps . StencilCaps & D3DSTENCILCAPS_INCRSAT )
caps - > fSupportedOps | = plStencilCaps : : kOpIncClamp ;
if ( fCurrentDevice - > fDDCaps . StencilCaps & D3DSTENCILCAPS_INVERT )
caps - > fSupportedOps | = plStencilCaps : : kOpInvert ;
if ( fCurrentDevice - > fDDCaps . StencilCaps & D3DSTENCILCAPS_KEEP )
caps - > fSupportedOps | = plStencilCaps : : kOpKeep ;
if ( fCurrentDevice - > fDDCaps . StencilCaps & D3DSTENCILCAPS_REPLACE )
caps - > fSupportedOps | = plStencilCaps : : kOpReplace ;
if ( fCurrentDevice - > fDDCaps . StencilCaps & D3DSTENCILCAPS_ZERO )
caps - > fSupportedOps | = plStencilCaps : : kOpSetToZero ;
return true ;
}
///////////////////////////////////////////////////////////////////////////////
//// Lighting /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// IMakeLightRef ////////////////////////////////////////////////////////////
// Create a plasma device ref for a light. Includes reserving a D3D light
// index for the light. Ref is kept in a linked list for ready disposal
// as well as attached to the light.
hsGDeviceRef * plDXPipeline : : IMakeLightRef ( plLightInfo * owner )
{
plDXLightRef * lRef = TRACKED_NEW plDXLightRef ( ) ;
/// Assign stuff and update
lRef - > fD3DIndex = fLights . ReserveD3DIndex ( ) ;
lRef - > fOwner = owner ;
owner - > SetDeviceRef ( lRef ) ;
// Unref now, since for now ONLY the BG owns the ref, not us (not until we use it, at least)
hsRefCnt_SafeUnRef ( lRef ) ;
lRef - > Link ( & fLights . fRefList ) ;
lRef - > UpdateD3DInfo ( fD3DDevice , & fLights ) ;
// Neutralize it until we need it.
fD3DDevice - > LightEnable ( lRef - > fD3DIndex , false ) ;
return lRef ;
}
//// RegisterLight ////////////////////////////////////////////////////////////
// Register a light with the pipeline. Light become immediately
// ready to illuminate the scene.
void plDXPipeline : : RegisterLight ( plLightInfo * liInfo )
{
if ( liInfo - > IsLinked ( ) )
return ;
liInfo - > Link ( & fLights . fActiveList ) ;
liInfo - > SetDeviceRef ( IMakeLightRef ( liInfo ) ) ;
fLights . fTime + + ;
}
//// UnRegisterLight //////////////////////////////////////////////////////////
// Remove a light from the pipeline's active light list. Light will
// no longer illuminate the scene.
void plDXPipeline : : UnRegisterLight ( plLightInfo * liInfo )
{
liInfo - > SetDeviceRef ( nil ) ;
liInfo - > Unlink ( ) ;
fLights . fTime + + ;
}
//// IEnableLights ////////////////////////////////////////////////////////////
// Does the lighting enable pass. Given a span with lights to use, builds
// a bit vector representing the lights to use, then uses that to mask off
// which lights actually need to be enabled/disabled.
// Constructs 2 lists on the span, one for normal lights, and one for projective lights.
void plDXPipeline : : IEnableLights ( plSpan * span )
{
plProfile_BeginTiming ( SelectLights ) ;
ISelectLights ( span , fSettings . fMaxNumLights , false ) ;
plProfile_EndTiming ( SelectLights ) ;
if ( ! ( fView . fRenderState & kRenderNoProjection ) )
{
plProfile_BeginTiming ( SelectProj ) ;
ISelectLights ( span , fSettings . fMaxNumProjectors , true ) ;
plProfile_EndTiming ( SelectProj ) ;
}
}
// ISelectLights ///////////////////////////////////////////////////////////////
// Find the strongest numLights lights to illuminate the span with.
// Weaker lights are faded out in effect so they won't pop when the
// strongest N changes membership.
void plDXPipeline : : ISelectLights ( plSpan * span , int numLights , hsBool proj )
{
int i , startScale ;
static hsBitVector newFlags ;
static hsTArray < plLightInfo * > onLights ;
plDXLightRef * ref ;
float threshhold , overHold = 0.3 , scale ;
/// Build new flags
/// Step 1: Find the n strongest lights
newFlags . Clear ( ) ;
onLights . SetCount ( 0 ) ;
if ( ! IsDebugFlagSet ( plPipeDbg : : kFlagNoRuntimeLights ) & &
! ( IsDebugFlagSet ( plPipeDbg : : kFlagNoApplyProjLights ) & & proj ) & &
! ( IsDebugFlagSet ( plPipeDbg : : kFlagOnlyApplyProjLights ) & & ! proj ) )
{
hsTArray < plLightInfo * > & spanLights = span - > GetLightList ( proj ) ;
for ( i = 0 ; i < spanLights . GetCount ( ) & & i < numLights ; i + + )
{
ref = ( plDXLightRef * ) spanLights [ i ] - > GetDeviceRef ( ) ;
if ( ref - > IsDirty ( ) )
{
if ( ref - > fD3DIndex = = 0 )
ref - > fD3DIndex = fLights . ReserveD3DIndex ( ) ;
ref - > UpdateD3DInfo ( fD3DDevice , & fLights ) ;
ref - > SetDirty ( false ) ;
}
newFlags . SetBit ( ref - > fD3DIndex ) ;
onLights . Append ( spanLights [ i ] ) ;
}
startScale = i ;
/// Attempt #2: Take some of the n strongest lights (below a given threshhold) and
/// fade them out to nothing as they get closer to the bottom. This way, they fade
/// out of existence instead of pop out.
if ( i < spanLights . GetCount ( ) - 1 & & i > 0 )
{
threshhold = span - > GetLightStrength ( i , proj ) ;
i - - ;
overHold = threshhold * 1.5f ;
if ( overHold > span - > GetLightStrength ( 0 , proj ) )
overHold = span - > GetLightStrength ( 0 , proj ) ;
for ( ; i > 0 & & span - > GetLightStrength ( i , proj ) < overHold ; i - - )
{
scale = ( overHold - span - > GetLightStrength ( i , proj ) ) / ( overHold - threshhold ) ;
ref = ( plDXLightRef * ) spanLights [ i ] - > GetDeviceRef ( ) ;
IScaleD3DLight ( ref , ( 1 - scale ) * span - > GetLightScale ( i , proj ) ) ;
}
startScale = i + 1 ;
}
/// Make sure those lights that aren't scaled....aren't
for ( i = 0 ; i < startScale ; i + + )
{
ref = ( plDXLightRef * ) spanLights [ i ] - > GetDeviceRef ( ) ;
IScaleD3DLight ( ref , span - > GetLightScale ( i , proj ) ) ;
}
}
// If these are non-projected lights, go ahead and enable them.
// For the projected lights, don't enable, just remember who they are.
if ( ! proj )
{
// A little change here. Some boards get sticky about exactly
// how many lights you have enabled, whether you are currently
// rendering or not. So if we go through enabling the lights
// we want and disabling the ones we don't, then even though
// at the end of the loop, less than MaxNumLights are enabled,
// we can still wind up screwed.
// Think about if we have 8 lights enabled, and they all happen
// to be at the end of fLights. Now we want to enable a different
// 8 lights, which happen to be at the beginning of the list.
// So we loop through and enable the lights we want, and then later
// in the loop disable the lights we don't want. Problem is that
// when we were enabling the ones we want we went over our 8 light
// limit, and some boards (ATI) react by ignoring the enable request.
// So then we disable the other lights at the end of the loop, but
// it's too late because our enable requests at the beginning of the
// loop were ignored.
// Solution is to go through the list twice, first disabling, then
// enabling. mf
hsBitVector newOff = fLights . fEnabledFlags - newFlags ;
hsBitIterator iterOff ( newOff ) ;
for ( iterOff . Begin ( ) ; ! iterOff . End ( ) ; iterOff . Advance ( ) )
fD3DDevice - > LightEnable ( iterOff . Current ( ) , false ) ;
hsBitVector newOn = newFlags - fLights . fEnabledFlags ;
hsBitIterator iterOn ( newOn ) ;
for ( iterOn . Begin ( ) ; ! iterOn . End ( ) ; iterOn . Advance ( ) )
fD3DDevice - > LightEnable ( iterOn . Current ( ) , true ) ;
fLights . fEnabledFlags = newFlags ;
}
else
{
fLights . fProjAll . SetCount ( 0 ) ;
fLights . fProjEach . SetCount ( 0 ) ;
for ( i = 0 ; i < onLights . GetCount ( ) ; i + + )
{
if ( onLights [ i ] - > OverAll ( ) )
fLights . fProjAll . Append ( onLights [ i ] ) ;
else
fLights . fProjEach . Append ( onLights [ i ] ) ;
}
onLights . SetCount ( 0 ) ;
}
}
// IDisableSpanLights /////////////////////////////////////////////////////
// Disable all the enabled lights, remembering which they are for
// quick reenabling.
void plDXPipeline : : IDisableSpanLights ( )
{
int i ;
for ( i = 0 ; i < fLights . fLastIndex + 1 ; i + + )
{
if ( fLights . fEnabledFlags . IsBitSet ( i ) )
{
fD3DDevice - > LightEnable ( i , false ) ;
fLights . fHoldFlags . SetBit ( i ) ;
}
}
fLights . fEnabledFlags . Clear ( ) ;
}
// IRestoreSpanLights //////////////////////////////////////////////////////
// Re-enable all the lights disabled by the matching IDisableSpanLights.
void plDXPipeline : : IRestoreSpanLights ( )
{
int i ;
for ( i = 0 ; i < fLights . fLastIndex + 1 ; i + + )
{
if ( fLights . fHoldFlags . IsBitSet ( i ) )
{
fD3DDevice - > LightEnable ( i , true ) ;
fLights . fEnabledFlags . SetBit ( i ) ;
}
}
fLights . fHoldFlags . Clear ( ) ;
}
//// IScaleD3DLight ///////////////////////////////////////////////////////////
// Scale the D3D light by the given scale factor, used for fading lights
// in and out by importance.
void plDXPipeline : : IScaleD3DLight ( plDXLightRef * ref , hsScalar scale )
{
scale = int ( scale * 1.e1 f ) * 1.e-1 f ;
if ( ref - > fScale ! = scale )
{
D3DLIGHT9 light = ref - > fD3DInfo ;
light . Diffuse . r * = scale ;
light . Diffuse . g * = scale ;
light . Diffuse . b * = scale ;
light . Ambient . r * = scale ;
light . Ambient . g * = scale ;
light . Ambient . b * = scale ;
light . Specular . r * = scale ;
light . Specular . g * = scale ;
light . Specular . b * = scale ;
fD3DDevice - > SetLight ( ref - > fD3DIndex , & light ) ;
ref - > fScale = scale ;
}
}
// inlPlToDWORDColor /////////////////////////////////////////////////
// Convert a plasma floating point color to a D3D DWORD color
static inline DWORD inlPlToDWORDColor ( const hsColorRGBA & c )
{
return ( DWORD ( c . a * 255.99f ) < < 24 )
| ( DWORD ( c . r * 255.99f ) < < 16 )
| ( DWORD ( c . g * 255.99f ) < < 8 )
| ( DWORD ( c . b * 255.99f ) < < 0 ) ;
}
// inlPlToD3DColor ////////////////////////////////////////////////////
// Convert a plasma floating point color to a D3D floating point color.
inline D3DCOLORVALUE plDXPipeline : : inlPlToD3DColor ( const hsColorRGBA & c , float a ) const
{
D3DCOLORVALUE ret ;
ret . r = c . r ;
ret . g = c . g ;
ret . b = c . b ;
ret . a = a ;
return ret ;
}
// inlEnsureLightingOn ////////////////////////////////////////////////
// Turn D3D lighting on if it isn't already.
inline void plDXPipeline : : inlEnsureLightingOn ( )
{
if ( ! fCurrD3DLiteState )
{
fD3DDevice - > SetRenderState ( D3DRS_LIGHTING , TRUE ) ;
fCurrD3DLiteState = true ;
}
}
// inlEnsureLightingOff ///////////////////////////////////////////////
// Turn D3D lighting off if it isn't already.
inline void plDXPipeline : : inlEnsureLightingOff ( )
{
if ( fCurrD3DLiteState )
{
fD3DDevice - > SetRenderState ( D3DRS_LIGHTING , FALSE ) ;
fCurrD3DLiteState = false ;
}
}
// ColorMul ///////////////////////////////////////////////////////////
// Multiply a D3D floating point color by a plasma floating point color,
// returning the result as a D3D floating point color.
static inline D3DCOLORVALUE ColorMul ( const D3DCOLORVALUE & c0 , const hsColorRGBA & c1 )
{
D3DCOLORVALUE ret ;
ret . r = c0 . r * c1 . r ;
ret . g = c0 . g * c1 . g ;
ret . b = c0 . b * c1 . b ;
ret . a = c0 . a * c1 . a ;
return ret ;
}
//// ICalcLighting ////////////////////////////////////////////////////////////
// Kind of misnamed. Sets the D3D material lighting model based on what we're
// currently doing.
void plDXPipeline : : ICalcLighting ( const plLayerInterface * currLayer , const plSpan * currSpan )
{
D3DMATERIAL9 mat ;
static hsScalar diffScale = 1.f ;
static hsScalar ambScale = 1.f ;
UInt32 props ;
plProfile_Inc ( MatLightState ) ;
/// New (temporary) lighting method:
/// The vertices now include the following:
/// diffuse = maxVertexColor * matDiffuse + matAmbient
/// specular = ( maxLighting + maxIllum ) * matDiffuse + matAmbient
/// And we want the lighting set up like:
/// L = I*v1 + v2 + (sigma)(light stuff * v3 + 0)
/// Where I = 0 for now (will be the environmental light constant eventually),
/// v1 is the diffuse vertex color and v2 is the specular vertex color.
/// So it basically translates into:
/// D3D ambient color = diffuse vertex color
/// D3D ambient constant = environmental light constant (0 for now)
/// D3D emissive color = specular vertex color
/// D3D diffuse color = diffuse vertex color
/// We now provide three lighting equations at the pipeline's disposal:
/// Material: (the one we all know and love)
/// MATd * VTXd + MATa + <sigma of lighting w/ MATd>
/// Vtx preshaded: (particle systems)
/// MATa * VTXd + 0 + <sigma of lighting w/ VTXd>
/// Vtx non-preshaded:
/// white * VTXd + MATa + <sigma of lighting w/ VTXd>
/// We also have a few more for shadows and such, which are handled individually
memset ( & mat , 0 , sizeof ( mat ) ) ;
/// Normal rendering--select the right lighting equation
if ( IsDebugFlagSet ( plPipeDbg : : kFlagAllBright ) )
{
inlEnsureLightingOn ( ) ;
mat . Diffuse . r = mat . Diffuse . g = mat . Diffuse . b = mat . Diffuse . a = 1.f ;
mat . Ambient . r = mat . Ambient . g = mat . Ambient . b = mat . Ambient . a = 1.f ;
mat . Emissive . r = mat . Emissive . g = mat . Emissive . b = mat . Emissive . a = 1.f ;
fD3DDevice - > SetMaterial ( & mat ) ;
fD3DDevice - > SetRenderState ( D3DRS_DIFFUSEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_EMISSIVEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , 0xffffffff ) ;
return ;
}
props = ( currSpan ! = nil ) ? ( currSpan - > fProps & plSpan : : kLiteMask ) : plSpan : : kLiteMaterial ;
if ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscBumpChans )
{
props = plSpan : : kLiteMaterial ;
fLayerState [ 0 ] . fShadeFlags | = hsGMatState : : kShadeNoShade | hsGMatState : : kShadeWhite ;
}
/// Select one of our three lighting methods
switch ( props )
{
case plSpan : : kLiteMaterial : // Material shading
/// Material: (the one we all know and love)
/// MATd * VTXd + MATa + <sigma of lighting w/ MATd>
inlEnsureLightingOn ( ) ;
// D3D ambient - give it our material static diffuse, since it will be multiplied by the vertex color
if ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeWhite )
{
mat . Ambient . r = mat . Ambient . g = mat . Ambient . b = diffScale ;
mat . Ambient . a = 1.f ;
}
else if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoPreShade ) )
{
mat . Ambient . r = mat . Ambient . g = mat . Ambient . b = 0 ;
mat . Ambient . a = 1.f ;
}
else
mat . Ambient = inlPlToD3DColor ( currLayer - > GetPreshadeColor ( ) * diffScale , 1.f ) ;
// D3D diffuse - give it our runtime material diffuse
mat . Diffuse = inlPlToD3DColor ( currLayer - > GetRuntimeColor ( ) * diffScale , currLayer - > GetOpacity ( ) ) ;
// D3D emissive - give it our material ambient
mat . Emissive = inlPlToD3DColor ( currLayer - > GetAmbientColor ( ) * ambScale , 1.f ) ;
// Set specular properties
if ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeSpecular )
{
mat . Specular = inlPlToD3DColor ( currLayer - > GetSpecularColor ( ) , 1.f ) ;
mat . Power = currLayer - > GetSpecularPower ( ) ;
}
fD3DDevice - > SetMaterial ( & mat ) ;
fD3DDevice - > SetRenderState ( D3DRS_DIFFUSEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_EMISSIVEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
if ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeWhite )
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , 0xffffffff ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , inlGetD3DColor ( * ( hsColorRGBA * ) & mat . Ambient ) ) ;
if ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeNoShade )
fD3DDevice - > SetRenderState ( D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_MATERIAL ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_COLOR1 ) ;
fCurrLightingMethod = plSpan : : kLiteMaterial ;
break ;
case plSpan : : kLiteVtxPreshaded : // Vtx preshaded
// MATa * VTXd + 0 + <sigma of lighting w/ VTXd>
// Mapping to: GLa * AMSrc + EMSrc + <.....................DMSrc>
#if 0 // PARTICLESHADE
if ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeEmissive )
{
inlEnsureLightingOff ( ) ;
}
else
{
inlEnsureLightingOn ( ) ;
// Set a black material (we ONLY care about vertex color when doing particles,
// er I mean, vtxPreshaded)
fD3DDevice - > SetMaterial ( & mat ) ;
fD3DDevice - > SetRenderState ( D3DRS_DIFFUSEMATERIALSOURCE , D3DMCS_COLOR1 ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , 0 ) ;
fD3DDevice - > SetRenderState ( D3DRS_EMISSIVEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARMATERIALSOURCE , D3DMCS_MATERIAL ) ;
}
# else // PARTICLESHADE
inlEnsureLightingOn ( ) ;
// MATa * white + 0 + <sigma of lighting with VTXd>
fD3DDevice - > SetMaterial ( & mat ) ;
fD3DDevice - > SetRenderState ( D3DRS_DIFFUSEMATERIALSOURCE , D3DMCS_COLOR1 ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , 0 ) ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARMATERIALSOURCE , D3DMCS_MATERIAL ) ;
if ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeEmissive )
fD3DDevice - > SetRenderState ( D3DRS_EMISSIVEMATERIALSOURCE , D3DMCS_COLOR1 ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_EMISSIVEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
# endif // PARTICLESHADE
fCurrLightingMethod = plSpan : : kLiteVtxPreshaded ;
break ;
case plSpan : : kLiteVtxNonPreshaded : // Vtx non-preshaded
// white * VTXd + MATa + <sigma of lighting w/ VTXd>
// Mapping to: GLa * AMSrc + EMSrc + <.....................DMSrc>
inlEnsureLightingOn ( ) ;
// D3D emissive - give it our material ambient
mat . Emissive = inlPlToD3DColor ( currLayer - > GetAmbientColor ( ) * ambScale , 1.f ) ;
// Set specular properties
if ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeSpecular )
{
mat . Specular = inlPlToD3DColor ( currLayer - > GetSpecularColor ( ) , 1.f ) ;
mat . Power = currLayer - > GetSpecularPower ( ) ;
}
fD3DDevice - > SetMaterial ( & mat ) ;
// Lightmaps want WHITE here, otherwise we want BLACK
DWORD preShadeStrength ;
preShadeStrength = inlPlToDWORDColor ( currLayer - > GetPreshadeColor ( ) ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , preShadeStrength ) ;
fD3DDevice - > SetRenderState ( D3DRS_DIFFUSEMATERIALSOURCE , D3DMCS_COLOR1 ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_COLOR1 ) ;
fD3DDevice - > SetRenderState ( D3DRS_EMISSIVEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fCurrLightingMethod = plSpan : : kLiteVtxNonPreshaded ;
break ;
default :
hsAssert ( false , " Bad lighting type " ) ;
break ;
}
}
///////////////////////////////////////////////////////////////////////////////
//// plDXLightSettings Functions /////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
plDXLightSettings : : plDXLightSettings ( )
: fActiveList ( nil ) ,
fRefList ( nil ) ,
fPipeline ( nil )
{
}
//// Reset ////////////////////////////////////////////////////////////////////
// Sets member variables to initial states.
void plDXLightSettings : : Reset ( plDXPipeline * pipe )
{
Release ( ) ;
fNextShadowLight = 0 ;
fUsedFlags . Clear ( ) ;
fEnabledFlags . Clear ( ) ;
fHoldFlags . Clear ( ) ;
fProjEach . Reset ( ) ;
fProjAll . Reset ( ) ;
fNextIndex = 1 ; /// Light 0 is reserved
fLastIndex = 1 ;
fTime = 0 ;
fRefList = nil ;
fPipeline = pipe ;
}
//// Release //////////////////////////////////////////////////////////////////
// Releases/deletes anything associated with these settings.
// This includes unregistering all lights.
void plDXLightSettings : : Release ( )
{
plDXLightRef * ref ;
fProjEach . Reset ( ) ;
fProjAll . Reset ( ) ;
while ( fRefList )
{
ref = fRefList ;
ref - > Release ( ) ;
ref - > Unlink ( ) ;
}
// Tell the light infos to unlink themselves
while ( fActiveList )
fPipeline - > UnRegisterLight ( fActiveList ) ;
fShadowLights . SetCount ( fShadowLights . GetNumAlloc ( ) ) ;
int i ;
for ( i = 0 ; i < fShadowLights . GetCount ( ) ; i + + )
{
hsRefCnt_SafeUnRef ( fShadowLights [ i ] ) ;
fShadowLights [ i ] = nil ;
}
fShadowLights . SetCount ( 0 ) ;
}
//// ReserveD3DIndex //////////////////////////////////////////////////////////
// Reserve a D3D light index.
UInt32 plDXLightSettings : : ReserveD3DIndex ( )
{
for ( ; fNextIndex < ( UInt32 ) - 1 ; fNextIndex + + )
{
if ( ! fUsedFlags . IsBitSet ( fNextIndex ) )
break ;
}
fUsedFlags . SetBit ( fNextIndex ) ;
fEnabledFlags . ClearBit ( fNextIndex ) ; // Ensure it's cleared
fHoldFlags . ClearBit ( fNextIndex ) ;
if ( fNextIndex > fLastIndex )
fLastIndex = fNextIndex ;
return fNextIndex ;
}
//// ReleaseD3DIndex //////////////////////////////////////////////////////////
// Release a reserved D3D light index to be reused.
void plDXLightSettings : : ReleaseD3DIndex ( UInt32 idx )
{
fUsedFlags . SetBit ( idx , false ) ;
if ( fNextIndex > idx )
fNextIndex = idx ; // Forces search to start here next time
// Dec down fLastIndex
while ( fLastIndex > 0 & & ! fUsedFlags . IsBitSet ( fLastIndex ) )
fLastIndex - - ;
if ( fNextIndex > fLastIndex )
fNextIndex = fLastIndex ;
}
///////////////////////////////////////////////////////////////////////////////
//// Materials ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// ISetLayer ////////////////////////////////////////////////////////////////
// Sets whether we're rendering a base layer or upper layer. Upper layer has
// a Z bias to avoid Z fighting.
void plDXPipeline : : ISetLayer ( UInt32 lay )
{
if ( lay )
{
if ( fCurrRenderLayer ! = lay )
{
fCurrRenderLayer = lay ;
plCONST ( int ) kBiasMult = 8 ;
if ( ! ( fSettings . fD3DCaps & kCapsZBias ) )
IProjectionMatrixToD3D ( ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_DEPTHBIAS , kBiasMult * fCurrRenderLayer ) ;
}
}
else
IBottomLayer ( ) ;
}
//// IBottomLayer /////////////////////////////////////////////////////////////
// Turn off any Z bias.
void plDXPipeline : : IBottomLayer ( )
{
if ( fCurrRenderLayer ! = 0 )
{
fCurrRenderLayer = 0 ;
if ( ! ( fSettings . fD3DCaps & kCapsZBias ) )
IProjectionMatrixToD3D ( ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_DEPTHBIAS , 0 ) ;
}
}
// Special effects /////////////////////////////////////////////////////////////
// IPushOverBaseLayer /////////////////////////////////////////////////////////
// Sets fOverBaseLayer (if any) as a wrapper on top of input layer.
// This allows the OverBaseLayer to intercept and modify queries of
// the real current layer's properties (e.g. color or state).
// fOverBaseLayer is set to only get applied to the base layer during
// multitexturing.
// Must be matched with call to IPopOverBaseLayer.
plLayerInterface * plDXPipeline : : IPushOverBaseLayer ( plLayerInterface * li )
{
if ( ! li )
return nil ;
fOverLayerStack . Push ( li ) ;
if ( ! fOverBaseLayer )
return fOverBaseLayer = li ;
fForceMatHandle = true ;
fOverBaseLayer = fOverBaseLayer - > Attach ( li ) ;
fOverBaseLayer - > Eval ( fTime , fFrame , 0 ) ;
return fOverBaseLayer ;
}
// IPopOverBaseLayer /////////////////////////////////////////////////////////
// Removes fOverBaseLayer as wrapper on top of input layer.
// Should match calls to IPushOverBaseLayer.
plLayerInterface * plDXPipeline : : IPopOverBaseLayer ( plLayerInterface * li )
{
if ( ! li )
return nil ;
fForceMatHandle = true ;
plLayerInterface * pop = fOverLayerStack . Pop ( ) ;
fOverBaseLayer = fOverBaseLayer - > Detach ( pop ) ;
return pop ;
}
// IPushOverAllLayer ///////////////////////////////////////////////////
// Push fOverAllLayer (if any) as wrapper around the input layer.
// fOverAllLayer is set to be applied to each layer during multitexturing.
// Must be matched by call to IPopOverAllLayer
plLayerInterface * plDXPipeline : : IPushOverAllLayer ( plLayerInterface * li )
{
if ( ! li )
return nil ;
fOverLayerStack . Push ( li ) ;
if ( ! fOverAllLayer )
{
fOverAllLayer = li ;
fOverAllLayer - > Eval ( fTime , fFrame , 0 ) ;
return fOverAllLayer ;
}
fForceMatHandle = true ;
fOverAllLayer = fOverAllLayer - > Attach ( li ) ;
fOverAllLayer - > Eval ( fTime , fFrame , 0 ) ;
return fOverAllLayer ;
}
// IPopOverAllLayer //////////////////////////////////////////////////
// Remove fOverAllLayer as wrapper on top of input layer.
// Should match calls to IPushOverAllLayer.
plLayerInterface * plDXPipeline : : IPopOverAllLayer ( plLayerInterface * li )
{
if ( ! li )
return nil ;
fForceMatHandle = true ;
plLayerInterface * pop = fOverLayerStack . Pop ( ) ;
fOverAllLayer = fOverAllLayer - > Detach ( pop ) ;
return pop ;
}
// PiggyBacks - used in techniques like projective lighting.
// PiggyBacks are layers appended to each drawprimitive pass.
// For example, if a material has 3 layers which will be drawn
// in 2 passes,
// pass0: layer0+layer1
// pass1: layer2
// Then if a piggyback layer layerPB is active, the actual rendering would be
// pass0: layer0+layer1+layerPB
// pass1: layer2 + layerPB
// ISetNumActivePiggyBacks /////////////////////////////////////////////
// Calculate the number of active piggy backs.
int plDXPipeline : : ISetNumActivePiggyBacks ( )
{
return fActivePiggyBacks = hsMinimum ( fSettings . fMaxPiggyBacks , fPiggyBackStack . GetCount ( ) ) ;
}
// IPushProjPiggyBack //////////////////////////////////////////////////
// Push a projected texture on as a piggy back.
void plDXPipeline : : IPushProjPiggyBack ( plLayerInterface * li )
{
if ( fView . fRenderState & plPipeline : : kRenderNoPiggyBacks )
return ;
fPiggyBackStack . Push ( li ) ;
fActivePiggyBacks = fPiggyBackStack . GetCount ( ) - fMatPiggyBacks ;
fForceMatHandle = true ;
}
// IPopProjPiggyBacks /////////////////////////////////////////////////
// Remove a projected texture from use as a piggy back.
void plDXPipeline : : IPopProjPiggyBacks ( )
{
if ( fView . fRenderState & plPipeline : : kRenderNoPiggyBacks )
return ;
fPiggyBackStack . SetCount ( fMatPiggyBacks ) ;
ISetNumActivePiggyBacks ( ) ;
fForceMatHandle = true ;
}
// IPushPiggyBacks ////////////////////////////////////////////////////
// Push any piggy backs associated with a material, presumed to
// be a light map because that's all they are used for.
// Matched with IPopPiggyBacks
void plDXPipeline : : IPushPiggyBacks ( hsGMaterial * mat )
{
hsAssert ( ! fMatPiggyBacks , " Push/Pop Piggy mismatch " ) ;
if ( fView . fRenderState & plPipeline : : kRenderNoPiggyBacks )
return ;
int i ;
for ( i = 0 ; i < mat - > GetNumPiggyBacks ( ) ; i + + )
{
if ( ! mat - > GetPiggyBack ( i ) )
continue ;
if ( ( mat - > GetPiggyBack ( i ) - > GetMiscFlags ( ) & hsGMatState : : kMiscLightMap )
& & IsDebugFlagSet ( plPipeDbg : : kFlagNoLightmaps ) )
continue ;
fPiggyBackStack . Push ( mat - > GetPiggyBack ( i ) ) ;
fMatPiggyBacks + + ;
}
ISetNumActivePiggyBacks ( ) ;
fForceMatHandle = true ;
}
// IPopPiggyBacks ///////////////////////////////////////////////////////
// Pop any current piggy backs set from IPushPiggyBacks.
// Matches IPushPiggyBacks.
void plDXPipeline : : IPopPiggyBacks ( )
{
if ( fView . fRenderState & plPipeline : : kRenderNoPiggyBacks )
return ;
fPiggyBackStack . SetCount ( fPiggyBackStack . GetCount ( ) - fMatPiggyBacks ) ;
fMatPiggyBacks = 0 ;
ISetNumActivePiggyBacks ( ) ;
fForceMatHandle = true ;
}
//// IHandleMaterial //////////////////////////////////////////////////////////
// Takes the starting "layer" and uses as many layers as possible in the given
// material and sets up the device to draw with it. Returns the first layer
// index not yet used. (I.e. if we ate layers 0 and 1, it'll return 2).
// A return value of -1 means don't bother rendering.
Int32 plDXPipeline : : IHandleMaterial ( hsGMaterial * newMat , UInt32 layer , const plSpan * currSpan )
{
// No material means no draw.
if ( ! newMat & & newMat - > GetLayer ( layer ) )
return - 1 ;
// If this is a bump mapping pass but the object isn't currently runtime lit, just skip.
// Note that <layer> may change here, if we're skipping past the bump layers but there
// are more layers (passes) to do after that.
if ( ISkipBumpMap ( newMat , layer , currSpan ) )
{
return - 1 ;
}
// Workaround for the ATI Radeon 7500's inability to use uvw coordinates above 1.
// If we have a layer trying to use uvw 2 or higher, skip it and any layers bound to
// it.
while ( ( layer < newMat - > GetNumLayers ( ) )
& & newMat - > GetLayer ( layer )
& & ( ( newMat - > GetLayer ( layer ) - > GetUVWSrc ( ) & 0xf ) > fSettings . fMaxUVWSrc ) )
{
if ( newMat - > GetLayer ( layer ) - > GetMiscFlags ( ) & hsGMatState : : kMiscBindNext )
layer + + ;
layer + + ;
}
if ( layer > = newMat - > GetNumLayers ( ) )
return - 1 ;
// If nothing has changed, we don't need to recompute and set state.
if ( ! fForceMatHandle & & ( newMat = = fCurrMaterial & & layer = = fCurrLayerIdx ) )
{
// Before returning, check if we have to redo our lighting
UInt32 lightType = ( currSpan ! = nil ) ? ( currSpan - > fProps & plSpan : : kLiteMask ) : plSpan : : kLiteMaterial ;
if ( lightType ! = fCurrLightingMethod )
ICalcLighting ( fCurrLay , currSpan ) ;
if ( fLayerState [ 0 ] . fMiscFlags & ( hsGMatState : : kMiscBumpDu | hsGMatState : : kMiscBumpDw ) )
ISetBumpMatrices ( fCurrLay , currSpan ) ;
return layer + fCurrNumLayers ;
}
fForceMatHandle = false ;
fCurrLayerIdx = layer ;
// fCurrNumLayers = newMat->GetNumLayers();
if ( newMat ! = fCurrMaterial )
plProfile_Inc ( MatChange ) ;
plProfile_Inc ( LayChange ) ;
/// Test for fail states
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoDecals ) & & ( newMat - > GetCompositeFlags ( ) & hsGMaterial : : kCompDecal ) )
{
return - 1 ;
}
/// Workaround for a D3D limitation--you're not allowed to render with a texture that you're
/// rendering INTO. Hence we can't have self-reflecting cubicRenderTargets (damn)
if ( fSettings . fCurrBaseRenderTarget ! = nil & &
newMat - > GetLayer ( layer ) - > GetTexture ( ) = = plBitmap : : ConvertNoRef ( fSettings . fCurrBaseRenderTarget ) )
{
return - 1 ;
}
/// Figure out our current states
// Start with the base layer.
plLayerInterface * currLay = IPushOverBaseLayer ( newMat - > GetLayer ( layer ) ) ;
if ( IsDebugFlagSet ( plPipeDbg : : kFlagBumpW ) & & ( currLay - > GetMiscFlags ( ) & hsGMatState : : kMiscBumpDu ) )
currLay = newMat - > GetLayer ( fCurrLayerIdx = + + layer ) ;
currLay = IPushOverAllLayer ( currLay ) ;
/// Save stuff for next time around
ICompositeLayerState ( 0 , currLay ) ;
hsRefCnt_SafeAssign ( fCurrMaterial , newMat ) ;
fCurrLayerIdx = layer ;
fCurrLay = currLay ;
if ( IsDebugFlagSet ( plPipeDbg : : kFlagDisableSpecular ) )
fLayerState [ 0 ] . fShadeFlags & = ~ hsGMatState : : kShadeSpecular ;
// ZIncLayer requests Z bias for upper layers.
if ( fLayerState [ 0 ] . fZFlags & hsGMatState : : kZIncLayer )
ISetLayer ( 1 ) ;
else
IBottomLayer ( ) ;
/// A few debugging things
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoAlphaBlending ) )
fLayerState [ 0 ] . fBlendFlags & = ~ hsGMatState : : kBlendMask ;
if ( ( IsDebugFlagSet ( plPipeDbg : : kFlagBumpUV ) | | IsDebugFlagSet ( plPipeDbg : : kFlagBumpW ) ) & & ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscBumpChans ) )
{
switch ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscBumpChans )
{
case hsGMatState : : kMiscBumpDu :
break ;
case hsGMatState : : kMiscBumpDv :
if ( ! ( fCurrMaterial - > GetLayer ( layer - 2 ) - > GetBlendFlags ( ) & hsGMatState : : kBlendAdd ) )
{
fLayerState [ 0 ] . fBlendFlags & = ~ hsGMatState : : kBlendMask ;
fLayerState [ 0 ] . fBlendFlags | = hsGMatState : : kBlendMADD ;
}
break ;
case hsGMatState : : kMiscBumpDw :
if ( ! ( fCurrMaterial - > GetLayer ( layer - 1 ) - > GetBlendFlags ( ) & hsGMatState : : kBlendAdd ) )
{
fLayerState [ 0 ] . fBlendFlags & = ~ hsGMatState : : kBlendMask ;
fLayerState [ 0 ] . fBlendFlags | = hsGMatState : : kBlendMADD ;
}
break ;
default :
break ;
}
}
/// Get the # of layers we can draw in this pass into fCurrNumLayers
int oldNumLayers = fCurrNumLayers ;
ILayersAtOnce ( newMat , layer ) ;
if ( oldNumLayers ! = fCurrNumLayers )
{
// This hack is necessary to cover a hack necessary to cover a "limitation" in the GeForce2 drivers.
// Basically, we have to handle NoTexAlpha/Color differently if it's stage 1 than other stages,
// so even though the BlendFlags haven't changed, the calls to D3D are different. Another
// way to handle this would be to have a different handler based on whether we are 2 TMU limited
// or not, but whatever.
if ( fLayerState [ 1 ] . fBlendFlags & ( hsGMatState : : kBlendNoTexAlpha | hsGMatState : : kBlendNoTexColor ) )
fLayerState [ 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
}
// Placed here, since it's material-dependent (or more accurately, current-layer-dependent)
ICalcLighting ( currLay , currSpan ) ;
// If we're bump mapping, compute the texture transforms.
if ( fLayerState [ 0 ] . fMiscFlags & ( hsGMatState : : kMiscBumpDu | hsGMatState : : kMiscBumpDw ) )
ISetBumpMatrices ( currLay , currSpan ) ;
/// Transfer states to D3D now
IHandleFirstTextureStage ( currLay ) ;
currLay = IPopOverAllLayer ( currLay ) ;
currLay = IPopOverBaseLayer ( currLay ) ;
fCurrLay = currLay ;
int nextLayer = fCurrLayerIdx + fCurrNumLayers ;
if ( IsDebugFlagSet ( plPipeDbg : : kFlagBumpW ) & & ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscBumpDw ) )
{
// Bump mapping approximation using only the W (normal direction) component of lighting.
plLayerInterface * layPtr = IPushOverAllLayer ( newMat - > GetLayer ( fCurrLayerIdx + 2 ) ) ;
if ( ! layPtr )
return - 1 ;
ICompositeLayerState ( 1 , layPtr ) ;
IHandleTextureStage ( 1 , layPtr ) ;
layPtr = IPopOverAllLayer ( layPtr ) ;
nextLayer = fCurrLayerIdx + 3 ;
}
else if ( IsDebugFlagSet ( plPipeDbg : : kFlagBumpUV ) & & ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscBumpDu ) )
{
// Bump mapping approximation using only the UV (surface tangent directions) component of lighting.
plLayerInterface * layPtr = IPushOverAllLayer ( newMat - > GetLayer ( fCurrLayerIdx + 3 ) ) ;
if ( ! layPtr )
return - 1 ;
ICompositeLayerState ( 1 , layPtr ) ;
IHandleTextureStage ( 1 , layPtr ) ;
layPtr = IPopOverAllLayer ( layPtr ) ;
nextLayer = fCurrLayerIdx + 2 ;
}
else
{
// Normal multi texturing.
/// Loop through all multitexturing layers
int i ;
if ( fView . fRenderState & plPipeline : : kRenderBaseLayerOnly )
nextLayer = newMat - > GetNumLayers ( ) ;
for ( i = 1 ; i < fCurrNumLayers ; i + + )
{
plLayerInterface * layPtr = newMat - > GetLayer ( fCurrLayerIdx + i ) ;
if ( ! layPtr )
return - 1 ;
// Can't render into a render target using same rendertarget as a texture.
if ( fSettings . fCurrBaseRenderTarget
& &
layPtr - > GetTexture ( ) = = ( plBitmap * ) ( fSettings . fCurrBaseRenderTarget ) )
{
// Oops, just bail
return - 1 ;
}
layPtr = IPushOverAllLayer ( layPtr ) ;
ICompositeLayerState ( i , layPtr ) ;
IHandleTextureStage ( i , layPtr ) ;
layPtr = IPopOverAllLayer ( layPtr ) ;
}
}
// More cleanup for the DX9.0c 2 texture limitation. See ILayersAtOnce()
if ( fSettings . fMaxLayersAtOnce = = 2 )
{
if ( ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendAdd )
& & ( newMat - > GetNumLayers ( ) > fCurrLayerIdx + 1 )
& & ( newMat - > GetLayer ( fCurrLayerIdx + 1 ) - > GetUVWSrc ( ) & plLayerInterface : : kUVWPosition ) )
{
// If we're doing additive blending and the next layer is based on position,
// it's probably a distance fade. We'd rather have our diffuse color.
// ILayersAtOnce will already have told us we can't use it this pass.
// Skip it so it won't draw on its own next pass.
nextLayer + + ;
}
}
int numActivePiggyBacks = 0 ;
if ( ! ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscBumpChans ) & & ! ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeEmissive ) )
{
/// Tack lightmap onto last stage if we have one
numActivePiggyBacks = fActivePiggyBacks ;
if ( numActivePiggyBacks > fSettings . fMaxLayersAtOnce - fCurrNumLayers )
numActivePiggyBacks = fSettings . fMaxLayersAtOnce - fCurrNumLayers ;
if ( numActivePiggyBacks )
{
int i ;
for ( i = 0 ; i < numActivePiggyBacks ; i + + )
{
// Note that we take piggybacks off the end of fPiggyBackStack.
plLayerInterface * layPtr = IPushOverAllLayer ( fPiggyBackStack [ fPiggyBackStack . GetCount ( ) - 1 - i ] ) ;
if ( ! layPtr )
return - 1 ;
ICompositeLayerState ( fCurrNumLayers + i , layPtr ) ;
IHandleTextureStage ( fCurrNumLayers + i , layPtr ) ;
layPtr = IPopOverAllLayer ( layPtr ) ;
}
// If we've got a piggyback, plus two layers that must be drawn together, but
// only two TMU's to work with, we're screwed. Someone has got to get skipped and
// hope no one notices. Typically, the first (base) layer has the color info,
// and the second the opacity. So we'll try using the projection to brighten
// the color, ignoring the opacity.
// if( ((fCurrNumLayers + numActivePiggyBacks) == fSettings.fMaxLayersAtOnce)
// && (fLayerState[0].fMiscFlags & hsGMatState::kMiscBindNext) )
if ( ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscBindNext )
& & ( fCurrNumLayers < 2 ) )
nextLayer + + ;
}
}
// Declare we won't be using any more texture stages.
IStageStop ( fCurrNumLayers + numActivePiggyBacks ) ;
return nextLayer ;
}
// ICompositeLayerState /////////////////////////////////////////////////////////////////
// Set the current Plasma state based on the input layer state and the material overrides.
// fMatOverOn overrides to set a state bit whether it is set in the layer or not.
// fMatOverOff overrides to clear a state bit whether it is set in the layer or not.
const hsGMatState & plDXPipeline : : ICompositeLayerState ( int which , plLayerInterface * layer )
{
fOldLayerState [ which ] = fLayerState [ which ] ;
fLayerState [ which ] . Composite ( layer - > GetState ( ) , fMatOverOn , fMatOverOff ) ;
if ( fOldLayerState [ which ] . fBlendFlags = = UInt32 ( - 1 ) )
fOldLayerState [ which ] . fBlendFlags = ~ fLayerState [ which ] . fBlendFlags ;
return fLayerState [ which ] ;
}
//// IHandleFirstTextureStage /////////////////////////////////////////////////
// Convert internal material state to D3D state for the base layer.
void plDXPipeline : : IHandleFirstTextureStage ( plLayerInterface * layer )
{
IHandleTextureMode ( layer ) ;
IHandleShadeMode ( ) ;
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fZFlags , fOldLayerState [ 0 ] . fZFlags , hsGMatState : : kZMask ) )
IHandleZMode ( ) ;
IHandleMiscMode ( ) ;
IHandleTextureStage ( 0 , layer ) ;
}
//// IHandleShadeMode /////////////////////////////////////////////////////////
// Convert shade state into D3D settings.
void plDXPipeline : : IHandleShadeMode ( )
{
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fShadeFlags , fOldLayerState [ 0 ] . fShadeFlags , hsGMatState : : kShadeSpecular ) )
{
if ( fLayerState [ 0 ] . fShadeFlags & hsGMatState : : kShadeSpecular )
fD3DDevice - > SetRenderState ( D3DRS_SPECULARENABLE , TRUE ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_SPECULARENABLE , FALSE ) ;
}
}
//// IHandleZMode /////////////////////////////////////////////////////////////
// Convert Z state into D3D settings.
void plDXPipeline : : IHandleZMode ( )
{
switch ( fLayerState [ 0 ] . fZFlags & hsGMatState : : kZMask )
{
case hsGMatState : : kZClearZ :
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_ALWAYS ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , TRUE ) ;
break ;
case hsGMatState : : kZNoZRead :
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_ALWAYS ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , TRUE ) ;
break ;
case hsGMatState : : kZNoZWrite :
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_LESSEQUAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , FALSE ) ;
break ;
case hsGMatState : : kZNoZRead | hsGMatState : : kZClearZ :
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_ALWAYS ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , TRUE ) ;
break ;
case hsGMatState : : kZNoZRead | hsGMatState : : kZNoZWrite :
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , FALSE ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_ALWAYS ) ;
break ;
case 0 :
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_LESSEQUAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , TRUE ) ;
break ;
// illegal combinations
case hsGMatState : : kZClearZ | hsGMatState : : kZNoZWrite :
case hsGMatState : : kZClearZ | hsGMatState : : kZNoZWrite | hsGMatState : : kZNoZRead :
hsAssert ( false , " Illegal combination of Z Buffer modes (Clear but don't write) " ) ;
break ;
}
}
//// IHandleMiscMode //////////////////////////////////////////////////////////
// Convert Misc state into D3D settings.
void plDXPipeline : : IHandleMiscMode ( )
{
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fMiscFlags , fOldLayerState [ 0 ] . fMiscFlags , hsGMatState : : kMiscWireFrame ) )
{
if ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscWireFrame )
fD3DDevice - > SetRenderState ( D3DRS_FILLMODE , D3DFILL_WIREFRAME ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_FILLMODE , D3DFILL_SOLID ) ;
}
}
//// IHandleTextureStage //////////////////////////////////////////////////////
// Issue D3D calls to enable rendering the given layer at the given texture stage.
void plDXPipeline : : IHandleTextureStage ( UInt32 stage , plLayerInterface * layer )
{
hsGDeviceRef * ref = nil ;
plBitmap * texture ;
// Blend mode
const hsGMatState & layState = fLayerState [ stage ] ;
if ( fLayerState [ stage ] . fBlendFlags ^ fOldLayerState [ stage ] . fBlendFlags )
IHandleStageBlend ( stage ) ;
// Texture wrap/clamp mode
if ( fLayerState [ stage ] . fClampFlags ^ fOldLayerState [ stage ] . fClampFlags )
IHandleStageClamp ( stage ) ;
// UVW transform
IHandleStageTransform ( stage , layer ) ;
// Create the D3D texture (if necessary) and set it to the device.
if ( ( texture = layer - > GetTexture ( ) ) ! = nil )
{
ref = texture - > GetDeviceRef ( ) ;
if ( ref = = nil | | ref - > IsDirty ( ) )
{
// Normal textures
plMipmap * mip ;
plCubicEnvironmap * cubic ;
if ( ( mip = plMipmap : : ConvertNoRef ( texture ) ) ! = nil )
ref = MakeTextureRef ( layer , mip ) ;
// Cubic environment maps
else if ( ( cubic = plCubicEnvironmap : : ConvertNoRef ( texture ) ) ! = nil )
ref = IMakeCubicTextureRef ( layer , cubic ) ;
}
}
if ( ref ! = nil )
IUseTextureRef ( stage , ref , layer ) ;
else
{
fD3DDevice - > SetTexture ( stage , NULL ) ;
hsRefCnt_SafeUnRef ( fLayerRef [ stage ] ) ;
fLayerRef [ stage ] = nil ;
}
}
// CheckTextureRef //////////////////////////////////////////////////////
// Make sure the given layer's texture has background D3D resources allocated.
void plDXPipeline : : CheckTextureRef ( plLayerInterface * layer )
{
plBitmap * bitmap = layer - > GetTexture ( ) ;
if ( bitmap )
{
hsGDeviceRef * ref = bitmap - > GetDeviceRef ( ) ;
if ( ! ref )
{
plMipmap * mip = plMipmap : : ConvertNoRef ( bitmap ) ;
if ( mip )
{
MakeTextureRef ( layer , mip ) ;
return ;
}
plCubicEnvironmap * cubic = plCubicEnvironmap : : ConvertNoRef ( bitmap ) ;
if ( cubic )
{
IMakeCubicTextureRef ( layer , cubic ) ;
return ;
}
}
}
}
// IHandleBumpEnv //////////////////////////////////////////////////////////////
// D3D settings for BUMPENVMAPLUMINANCE.
// This has never been used in production assets, because I never got
// a good effect out of it, and BUMPENVMAPLUMINANCE isn't universally
// supported in hardware.
void plDXPipeline : : IHandleBumpEnv ( int stage , UInt32 blendFlags )
{
DWORD current = stage ? D3DTA_CURRENT : D3DTA_DIFFUSE ;
UInt32 colorSrc = blendFlags & hsGMatState : : kBlendInvertColor ? D3DTA_TEXTURE | D3DTA_COMPLEMENT : D3DTA_TEXTURE ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_BUMPENVMAPLUMINANCE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , current ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
const hsMatrix44 & envXfm = fCurrLay - > GetBumpEnvMatrix ( ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_BUMPENVMAT00 , F2DW ( envXfm . fMap [ 0 ] [ 0 ] ) ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_BUMPENVMAT01 , F2DW ( envXfm . fMap [ 1 ] [ 0 ] ) ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_BUMPENVMAT10 , F2DW ( envXfm . fMap [ 0 ] [ 1 ] ) ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_BUMPENVMAT11 , F2DW ( envXfm . fMap [ 1 ] [ 1 ] ) ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_BUMPENVLSCALE , F2DW ( envXfm . fMap [ 2 ] [ 2 ] ) ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_BUMPENVLOFFSET , F2DW ( envXfm . fMap [ 2 ] [ 3 ] ) ) ;
}
//// IHandleStageBlend ////////////////////////////////////////////////////////
// Translate current blend state for this stage into D3D settings.
void plDXPipeline : : IHandleStageBlend ( int stage )
{
const UInt32 blendFlags = fLayerState [ stage ] . fBlendFlags ;
// If it's the base layer, handle that differently, because it's not really
// texture stage settings, but frame buffer blend settings.
if ( stage = = 0 )
{
IHandleFirstStageBlend ( ) ;
return ;
}
UInt32 colorSrc = D3DTA_TEXTURE ;
if ( blendFlags & hsGMatState : : kBlendInvertColor )
colorSrc | = D3DTA_COMPLEMENT ;
// kBlendEnvBumpNext not really used.
if ( blendFlags & hsGMatState : : kBlendEnvBumpNext )
{
IHandleBumpEnv ( stage , blendFlags ) ;
}
else switch ( blendFlags & hsGMatState : : kBlendMask )
{
// Alpha blending. Complicated by the ability to ignore either
// color or alpha for any given texture. The lower end GeForces
// don't orthogonally support settings, especially when the final
// (3rd) stage is the diffuse color/alpha modulate and the board
// really only wants to support 2 stages.
// So we couldn't just translate our internal plasma stage states
// into D3D states, we had to do some rearranging.
// Note that by the time we get here, we _know_ that this isn't the
// base layer (stage 0), because that's handled elsewhere.
case hsGMatState : : kBlendAlpha :
// If the current number of layers is 2, then we've already handled the
// base layer, so this must be layer 1 and the final layer.
// If the base layer has NoTexColor or this layer has NoTexColor, we need
// to do some rearranging.
if ( ( fCurrNumLayers = = 2 )
& & ( ( blendFlags | fLayerState [ 0 ] . fBlendFlags ) & hsGMatState : : kBlendNoTexColor ) )
{
// If this layer AND base layer are NoTexColor, then we just want the diffuse color.
if ( ( blendFlags & hsGMatState : : kBlendNoTexColor )
& & ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoTexColor ) )
{
// select diffuse color
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_SELECTARG2 ) ;
}
// If the base layer has NoTexColor but this layer doesn't, then we
// want the output to be this texture color times diffuse (ignoring base texture color).
else if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoTexColor )
{
// diffuse is arg2, modulate
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
}
// If base layer doesn't have NoTexColor, but this layer does, then
// we want the output to be diffuse times base texture, which is in current.
else if ( blendFlags & hsGMatState : : kBlendNoTexColor )
{
// diffuse is arg1, modulate
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
}
}
// If we get here and this layer has NoTexColor, then we MUST be on a layer
// above 1, which means we're on an advanced enough board to handle this orthogonally,
// i.e. one with more than 2 texture stages.
else if ( blendFlags & hsGMatState : : kBlendNoTexColor )
{
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_SELECTARG2 ) ;
}
// Finally, no NoTexColor in sight, just set it.
else
{
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP ,
blendFlags & hsGMatState : : kBlendInvertAlpha
? D3DTOP_MODULATEINVALPHA_ADDCOLOR
: D3DTOP_BLENDTEXTUREALPHA ) ;
}
// The same ordeal for alpha, and the ability to ignore the alpha on any texture.
// Note the additional logic for how to combine the alphas of multiple textures
// into a final FB alpha.
// This is orthogonal to using the alpha to combine colors of two different textures.
// The default behavior is to use the upper texture alpha to blend the upper layer color
// with the lower texture color, but retain the lower texture alpha (modulated by diffuse)
// for the frame buffer alpha.
switch ( blendFlags & ( hsGMatState : : kBlendAlphaAdd | hsGMatState : : kBlendAlphaMult ) )
{
default :
case 0 :
// Using alpha to blend textures, but this layer's alpha doesn't affect final FB
// alpha.
// Two layer setup with one or the other (or both) ignoring alpha.
if ( ( fCurrNumLayers = = 2 )
& & ( ( blendFlags | fLayerState [ 0 ] . fBlendFlags ) & hsGMatState : : kBlendNoTexAlpha ) )
{
// Both ignoring alpha, use diffuse.
if ( ( blendFlags & hsGMatState : : kBlendNoTexAlpha )
& & ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoTexAlpha ) )
{
// select diffuse alpha
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
}
// Base ignoring alpha, use diffuse times this texure alpha.
else if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoTexAlpha )
{
// diffuse is arg2, modulate
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG1 ,
blendFlags & hsGMatState : : kBlendInvertAlpha
? D3DTA_TEXTURE | D3DTA_COMPLEMENT
: D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE ) ;
}
// This ignoring alpha, use diffuse times base alpha (in current).
else if ( blendFlags & hsGMatState : : kBlendNoTexAlpha )
{
// diffuse is arg1, modulate
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG1 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
}
}
// Ignoring alpha or not, with more than 2 texture stages,
// Either way, we'll ignore this texture's alpha, because it's an upper layer
// and has already been used (if it's going to get used) to blend this texture's
// color with the lower layers.
else
{
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
}
break ;
// Alpha coming out of this stage is lower stage alpha plus this texture alpha.
case hsGMatState : : kBlendAlphaAdd :
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_ADD ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG1 ,
blendFlags & hsGMatState : : kBlendInvertAlpha
? D3DTA_TEXTURE | D3DTA_COMPLEMENT
: D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
break ;
// Alpha coming out of this stage is lower stage alpha times this texture alpha.
case hsGMatState : : kBlendAlphaMult :
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG1 ,
blendFlags & hsGMatState : : kBlendInvertAlpha
? D3DTA_TEXTURE | D3DTA_COMPLEMENT
: D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
break ;
}
break ;
// Add texture colors, pass through current alpha.
case hsGMatState : : kBlendAdd :
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_ADD ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
break ;
// Multiply texture colors, pass through current alpha
case hsGMatState : : kBlendMult :
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
if ( fSettings . fMaxLayersAtOnce = = 2 & & stage = = 1 )
{
// On these boards, the only way we can do 2 textures plus diffuse is to
// multiply it in during stage 0, but that only gives the same result
// when doing a mult blend, which we won't know when setting up stage 0.
// Now that we know, adjust stage 0 settings.
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
}
break ;
// Dot3 texture colors, pass through current alpha.
case hsGMatState : : kBlendDot3 :
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_DOTPRODUCT3 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
break ;
// Add signed texture colors, pass through current alpha.
case hsGMatState : : kBlendAddSigned :
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_ADDSIGNED ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
break ;
// Add signed * 2 texture colors, pass through current alpha.
case hsGMatState : : kBlendAddSigned2X :
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_ADDSIGNED2X ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
break ;
// kBlendAddColorTimesAlpha is only supported for the base layer.
case hsGMatState : : kBlendAddColorTimesAlpha :
hsAssert ( false , " Blend mode unsupported on upper layers " ) ;
break ;
// No blend, select this texture color and pass through current alpha
case 0 :
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , colorSrc ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_SELECTARG1 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
break ;
}
}
//// IHandleFirstStageBlend ///////////////////////////////////////////////////
// Set frame buffer blend mode for blending the base layer
// For the case of rendering to a texture with alpha, the alpha written to
// the render target will be computed exactly as the color (limitation of D3D).
void plDXPipeline : : IHandleFirstStageBlend ( )
{
// No color, just writing out Z values.
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoColor )
{
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_ZERO ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_ONE ) ;
fLayerState [ 0 ] . fBlendFlags | = 0x80000000 ;
}
else
{
switch ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendMask )
{
// Detail is just a special case of alpha, handled in construction of the texture
// mip chain by making higher levels of the chain more transparent.
case hsGMatState : : kBlendDetail :
case hsGMatState : : kBlendAlpha :
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendInvertFinalAlpha )
{
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_INVSRCALPHA ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_SRCALPHA ) ;
}
else
{
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_SRCALPHA ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_INVSRCALPHA ) ;
}
break ;
// Multiply the final color onto the frame buffer.
case hsGMatState : : kBlendMult :
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendInvertFinalColor )
{
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_ZERO ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_INVSRCCOLOR ) ;
}
else
{
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_ZERO ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_SRCCOLOR ) ;
}
break ;
// Add final color to FB.
case hsGMatState : : kBlendAdd :
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_ONE ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_ONE ) ;
break ;
// Multiply final color by FB color and add it into the FB.
case hsGMatState : : kBlendMADD :
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_DESTCOLOR ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_ONE ) ;
break ;
// Final color times final alpha, added into the FB.
case hsGMatState : : kBlendAddColorTimesAlpha :
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendInvertFinalAlpha )
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_INVSRCALPHA ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_SRCALPHA ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_ONE ) ;
break ;
// Overwrite final color onto FB
case 0 :
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_ONE ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_ZERO ) ;
break ;
default :
{
hsAssert ( false , " Too many blend modes specified in material " ) ;
plLayer * lay = plLayer : : ConvertNoRef ( fCurrMaterial - > GetLayer ( fCurrLayerIdx ) - > BottomOfStack ( ) ) ;
if ( lay )
{
if ( lay - > GetBlendFlags ( ) & hsGMatState : : kBlendAlpha )
{
lay - > SetBlendFlags ( ( lay - > GetBlendFlags ( ) & ~ hsGMatState : : kBlendMask ) | hsGMatState : : kBlendAlpha ) ;
}
else
{
lay - > SetBlendFlags ( ( lay - > GetBlendFlags ( ) & ~ hsGMatState : : kBlendMask ) | hsGMatState : : kBlendAdd ) ;
}
}
}
break ;
}
}
// Blend ops, not currently used in production.
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fBlendFlags , fOldLayerState [ 0 ] . fBlendFlags , ( hsGMatState : : kBlendSubtract | hsGMatState : : kBlendRevSubtract ) ) )
{
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendSubtract )
fD3DDevice - > SetRenderState ( D3DRS_BLENDOP , D3DBLENDOP_SUBTRACT ) ;
else if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendRevSubtract )
fD3DDevice - > SetRenderState ( D3DRS_BLENDOP , D3DBLENDOP_REVSUBTRACT ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_BLENDOP , D3DBLENDOP_ADD ) ;
}
// AlphaTestHigh is used for reducing sort artifacts on textures that are mostly opaque or transparent, but
// have regions of translucency in transition. Like a texture for a bush billboard. It lets there be some
// transparency falloff, but quit drawing before it gets so transparent that draw order problems (halos)
// become apparent.
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fBlendFlags , fOldLayerState [ 0 ] . fBlendFlags , hsGMatState : : kBlendAlphaTestHigh ) )
{
plConst ( UInt32 ) kHighAlphaTest ( 0x40 ) ;
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendAlphaTestHigh )
fD3DDevice - > SetRenderState ( D3DRS_ALPHAREF , kHighAlphaTest ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_ALPHAREF , 0x00000001 ) ;
}
// Set the alpha test function, turn on for alpha blending, else off.
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fBlendFlags , fOldLayerState [ 0 ] . fBlendFlags , hsGMatState : : kBlendAlpha | hsGMatState : : kBlendTest | hsGMatState : : kBlendAlphaAlways | hsGMatState : : kBlendAddColorTimesAlpha ) )
{
if ( ( fLayerState [ 0 ] . fBlendFlags & ( hsGMatState : : kBlendAlpha | hsGMatState : : kBlendTest | hsGMatState : : kBlendAddColorTimesAlpha ) )
& & ! ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendAlphaAlways ) )
fD3DDevice - > SetRenderState ( D3DRS_ALPHAFUNC , D3DCMP_GREATER ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_ALPHAFUNC , D3DCMP_ALWAYS ) ;
}
// Adjust the fog color based on the blend mode. Setting fog color to black for additive modes is
// an exact solution, setting it to white for multipication is as close of an approximation to correct
// as you're going to get with DX.
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fBlendFlags , fOldLayerState [ 0 ] . fBlendFlags , hsGMatState : : kBlendAdd | hsGMatState : : kBlendMult | hsGMatState : : kBlendMADD | hsGMatState : : kBlendAddColorTimesAlpha ) )
{
if ( fLayerState [ 0 ] . fBlendFlags & ( hsGMatState : : kBlendAdd | hsGMatState : : kBlendMADD | hsGMatState : : kBlendAddColorTimesAlpha ) )
fD3DDevice - > SetRenderState ( D3DRS_FOGCOLOR , 0 ) ;
else if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendMult )
fD3DDevice - > SetRenderState ( D3DRS_FOGCOLOR , 0xffffffff ) ;
else
fD3DDevice - > SetRenderState ( D3DRS_FOGCOLOR , fCurrFog . fHexColor ) ;
}
}
//// IHandleTextureMode ///////////////////////////////////////////////////////
// Handle the texture stage state for the base layer.
void plDXPipeline : : IHandleTextureMode ( plLayerInterface * layer )
{
plBitmap * bitmap = layer - > GetTexture ( ) ;
if ( bitmap )
{
// EnvBumpNext not used in production.
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendEnvBumpNext )
{
IHandleBumpEnv ( 0 , fLayerState [ 0 ] . fBlendFlags ) ;
}
// If the texture stage settings have changed. Note that this
// is a bad test, we should just be doing something like keeping
// an array of D3D TextureStageStates as we set them and checking against
// that directly rather than trying to infer from higher level state
// whether we need to make the D3D call.
else if ( fSettings . fVeryAnnoyingTextureInvalidFlag
| | ! fTexturing
| | ( fLayerState [ 0 ] . fBlendFlags ^ fOldLayerState [ 0 ] . fBlendFlags )
| | ( fCurrNumLayers + fActivePiggyBacks ! = fLastEndingStage )
)
{
// If we're only doing one layer, just modulate texture color by diffuse and we're done.
if ( ( fCurrNumLayers + fActivePiggyBacks ) < = 1 )
{
// See IHandleStageBlend for notes on NoTexColor.
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoTexColor )
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_SELECTARG2 ) ;
else
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 ,
fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendInvertColor
? D3DTA_TEXTURE | D3DTA_COMPLEMENT
: D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_DIFFUSE ) ;
}
else
{
// See the check in IHandleStageBlend for fSettings.fMaxLayersAtOnce == 2.
// It depends on these settings and adjusts what it needs.
// Multitexturing, select texture color to make its way upstream on stages.
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_SELECTARG1 ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 ,
fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendInvertColor
? D3DTA_TEXTURE | D3DTA_COMPLEMENT
: D3DTA_TEXTURE ) ;
// If our NoTexColor setting has changed, for a refresh of blend state on the next stage
// since it's affected by our NoTexColor state.
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fBlendFlags , fOldLayerState [ 0 ] . fBlendFlags , hsGMatState : : kBlendNoTexColor ) )
fLayerState [ 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
}
// Alpha Arg1 is texture alpha (possibly complemented), and Arg2 is diffuse (possibly complemented).
// If we want to ignore vertex alpha, select arg1
// If we want to ignore texture alpha, select arg2
// Otherwise (and normally) multiply the two.
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAOP ,
fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoVtxAlpha
? D3DTOP_SELECTARG1
: fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoTexAlpha
? D3DTOP_SELECTARG2
: D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 ,
fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendInvertAlpha
? D3DTA_TEXTURE | D3DTA_COMPLEMENT
: D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE |
( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendInvertVtxAlpha
? D3DTA_COMPLEMENT
: 0 ) ) ;
fTexturing = true ;
}
}
// Here we've no texture for the base layer, but we have more than layer.
// Select diffuse color and alpha, and pretend we have a texture but we're ignoring its
// color and alpha.
else if ( fCurrNumLayers + fActivePiggyBacks > 1 )
{
fLayerState [ 0 ] . fBlendFlags | = hsGMatState : : kBlendNoTexColor | hsGMatState : : kBlendNoTexAlpha ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_DIFFUSE ) ;
if ( fLayerState [ 0 ] . Differs ( fLayerState [ 0 ] . fBlendFlags , fOldLayerState [ 0 ] . fBlendFlags , ( hsGMatState : : kBlendNoTexColor | hsGMatState : : kBlendNoTexAlpha ) ) )
fLayerState [ 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
fTexturing = false ;
}
// Finally, a color only (non-textured) pass. Just select diffuse.
else
{
if ( fTexturing | | fSettings . fVeryAnnoyingTextureInvalidFlag )
{
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_SELECTARG1 ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_SELECTARG1 ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_DIFFUSE |
( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendInvertVtxAlpha ? D3DTA_COMPLEMENT : 0 ) ) ;
fTexturing = false ;
}
}
fSettings . fVeryAnnoyingTextureInvalidFlag = false ;
}
//// IHandleStageClamp ////////////////////////////////////////////////////////
// Translate our current wrap/clamp mode to D3D calls.
void plDXPipeline : : IHandleStageClamp ( int stage )
{
const UInt32 flags = fLayerState [ stage ] . fClampFlags ;
switch ( flags )
{
case 0 :
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_ADDRESSU , D3DTADDRESS_WRAP ) ;
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_ADDRESSV , D3DTADDRESS_WRAP ) ;
break ;
case hsGMatState : : kClampTextureU :
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ) ;
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_ADDRESSV , D3DTADDRESS_WRAP ) ;
break ;
case hsGMatState : : kClampTextureV :
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_ADDRESSU , D3DTADDRESS_WRAP ) ;
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ) ;
break ;
case hsGMatState : : kClampTexture :
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ) ;
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ) ;
break ;
}
}
void plDXPipeline : : ISetBumpMatrices ( const plLayerInterface * layer , const plSpan * span )
{
//#define BUMP_COMPARE_MATH
# ifdef BUMP_COMPARE_MATH
// This section is just debugging, to compute the matrices that will be set.
static hsMatrix44 preMDu ;
static hsMatrix44 preMDv ;
static hsMatrix44 preMDw ;
static int preMInit = false ;
if ( ! preMInit )
{
hsMatrix44 rotAndCollapseToX ;
int i , j ;
for ( i = 0 ; i < 4 ; i + + )
{
for ( j = 0 ; j < 4 ; j + + )
{
rotAndCollapseToX . fMap [ i ] [ j ] = 0 ;
}
}
rotAndCollapseToX . fMap [ 0 ] [ 2 ] = 1.f ;
rotAndCollapseToX . fMap [ 3 ] [ 3 ] = 1.f ;
rotAndCollapseToX . NotIdentity ( ) ;
hsMatrix44 offset ;
offset . Reset ( ) ;
offset . fMap [ 0 ] [ 0 ] = 0.5f ;
offset . fMap [ 0 ] [ 3 ] = 0.5f ;
offset . NotIdentity ( ) ;
preMDu = offset * rotAndCollapseToX ;
offset . fMap [ 1 ] [ 3 ] = 0.5f ;
preMDv = offset * rotAndCollapseToX ;
offset . fMap [ 1 ] [ 3 ] = 1.f ;
preMDw = offset * rotAndCollapseToX ;
preMInit = true ;
}
hsMatrix44 localToLight = span - > GetLight ( 0 , false ) - > GetWorldToLight ( ) * span - > fLocalToWorld ;
localToLight . fMap [ 0 ] [ 3 ] = localToLight . fMap [ 1 ] [ 3 ] = localToLight . fMap [ 2 ] [ 3 ] = 0 ;
fBumpDuMatrix = preMDu * localToLight ;
fBumpDvMatrix = preMDv * localToLight ;
hsMatrix44 c2w = fView . fCameraToWorld ;
hsMatrix44 cameraToLight = span - > GetLight ( 0 , false ) - > GetWorldToLight ( ) * c2w ;
cameraToLight . fMap [ 0 ] [ 3 ] = cameraToLight . fMap [ 1 ] [ 3 ] = cameraToLight . fMap [ 2 ] [ 3 ] = 0 ;
fBumpDwMatrix = preMDw * cameraToLight ;
// HACK PART - FOR COMPARISON
hsMatrix44 bDu = fBumpDuMatrix ;
hsMatrix44 bDv = fBumpDvMatrix ;
hsMatrix44 bDw = fBumpDwMatrix ;
static hsMatrix44 zeroMatrix ;
fBumpDuMatrix = zeroMatrix ;
fBumpDvMatrix = zeroMatrix ;
fBumpDwMatrix = zeroMatrix ;
// HACK PART - FOR COMPARISON
# endif // BUMP_COMPARE_MATH
// Here's the math
// The incoming uv coordinate is either:
// kMiscBumpDu - dPos/dU (in other words, the direction in space from this vertex where U increases and V remains constant) in local space.
// kMiscBumpDv - dPos/dV (in other words, the direction in space from this vertex where V increases and U remains constant) in local space.
// kMiscBumpDw - the normal in camera space.
//
// In each case, we need to transform the vector (uvw coord) into light space, and dot it with the light direction.
// Well, in light space, the light direction is always (0,0,1).
// So really, we just transform the vector into light space, and the z component is what we want.
// Then, for each of these, we take that z value (the dot product) and put it into a color channel.
// R = dPos/dU dot liDir
// G = dPos/dV dot liDir
// B = dPos/dW dot liDir
//
// That's what we want, here's how we get it.
// Here, Li(vec) means the vector in light space, Loc(vec) is local space, Tan(vec) is tangent space
//
// Li(uvw) = local2Light * Loc(uvw) (uvw comes in in local space, ie input uvw == Loc(uvw)
// Then we want to:
// a) Rotate the Z component to be along X (U) axis
// b) Zero out the new Y and Z
// c) Scale and offset our new X (the old Z) so -1 => 0, 1 => 1 (scale by 0.5, add 0.5).
// The following matrix does all this (it's just a concatenation of the above 3 simple matrices).
// M = |0 0 0.5 0.5|
// |0 0 0 0 |
// |0 0 0 0 |
// |0 0 0 1 |
//
// Our lookup texture that these transformed coords will read into has three horizontal bands,
// the bottom 3rd is a ramp along U of 0->red
// middle 3rd is a ramp along U of 0->green
// last third (highest V) is a ramp along U of 0->blue.
// So we can do the conversion from our dot to a color with an appropriate V offset in the above M.
//
// dPos/dU and dPos/dV are both input in local space, so the transform to get them into light space is
// the same for each, and that's obviously WorldToLight * LocalToWorld.
// That's a little inconvenient and inefficient. It's inconvenient, because for an omni light, we
// can easily fake a light direction (span position - light position), but the full matrix is kind
// of arbitrary. We could fake it, but instead we move on. It's inefficient because, looking at the
// form of matrix M, we know we'll be throwing away a lot of it anyway. So we work through the matrix
// math and find that we're going to wind up with:
//
// M1 = | M[0][2] * loc2li[2][0] M[0][2] * loc2li[2][1] M[0][2] * loc2li[2][2] 0.5 |
// | 0 0 0 0 |
// | 0 0 0 0 |
// | 0 0 0 1 |
//
// So all we really need is loc2li[2] (row 2). A little more matrix math gives us:
//
// loc2li[2] = (w2li[2] dot loc2wT[0], w2li[2] dot loc2wT[1], w2li[2] dot loc2wT[2]) (where loc2wT is Transpose(loc2w)
//
// And hey, that's just dependent on the light's direction w2li[2]. The same thing works out for dPos/dW, except
// substitue cam2w for loc2w (since input is in camera space instead of world space).
//
// And that's about it. We don't actually have to multiply all those matrices at run-time, because
// we know what the answer will be anyway. We just construct the matrices, making sure we set the
// appropriate translate for V to get each into the right color channel. The hardware does the three
// uv transforms and lookups, sums the results, and the output is:
// (dPos/dU dot liDir, dPos/dV dot liDir, dPos/dW dot liDir), which also happens to be the light direction
// transformed into tangent space. We dot that with our bump map (which has the normals in tangent space),
// and we've got per-pixel shading for this light direction.
hsPoint3 spanPos = span - > fWorldBounds . GetCenter ( ) ;
hsVector3 liDir ( 0 , 0 , 0 ) ;
int i ;
const hsTArray < plLightInfo * > & spanLights = span - > GetLightList ( false ) ;
hsScalar maxStrength = 0 ;
for ( i = 0 ; i < spanLights . GetCount ( ) ; i + + )
{
hsScalar liWgt = span - > GetLightStrength ( i , false ) ;
// A light strength of 2.f means it's from a light group, and we haven't actually calculated
// the strength. So calculate it now.
if ( liWgt = = 2.f )
{
hsScalar scale ;
spanLights [ i ] - > GetStrengthAndScale ( span - > fWorldBounds , liWgt , scale ) ;
}
if ( liWgt > maxStrength )
maxStrength = liWgt ;
liDir + = spanLights [ i ] - > GetNegativeWorldDirection ( spanPos ) * liWgt ;
}
hsFastMath : : NormalizeAppr ( liDir ) ;
static hsScalar kUVWScale = 1.f ;
hsScalar uvwScale = kUVWScale ;
if ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendAdd )
{
hsVector3 cam2span ( & GetViewPositionWorld ( ) , & spanPos ) ;
hsFastMath : : NormalizeAppr ( cam2span ) ;
liDir + = cam2span ;
hsFastMath : : NormalizeAppr ( liDir ) ;
static hsScalar kSpecularMax = 0.1f ;
static hsScalar kSpecularMaxUV = 0.5f ;
if ( IsDebugFlagSet ( plPipeDbg : : kFlagBumpUV ) )
uvwScale * = kSpecularMaxUV ;
else
uvwScale * = kSpecularMax ;
}
switch ( fCurrMaterial - > GetLayer ( fCurrLayerIdx ) - > GetMiscFlags ( ) & hsGMatState : : kMiscBumpChans )
{
case hsGMatState : : kMiscBumpDu :
uvwScale * = fCurrMaterial - > GetLayer ( fCurrLayerIdx + 3 ) - > GetRuntimeColor ( ) . r ;
break ;
case hsGMatState : : kMiscBumpDv : // This currently should never happen
uvwScale * = fCurrMaterial - > GetLayer ( fCurrLayerIdx + 1 ) - > GetRuntimeColor ( ) . r ;
break ;
case hsGMatState : : kMiscBumpDw :
uvwScale * = fCurrMaterial - > GetLayer ( fCurrLayerIdx + 2 ) - > GetRuntimeColor ( ) . r ;
break ;
}
maxStrength * = 20.f ;
if ( maxStrength > 1.f )
maxStrength = 1.f ;
liDir * = uvwScale * maxStrength ;
const hsScalar kUVWOffset = 0.5f ;
hsScalar kOffsetToRed ;
hsScalar kOffsetToGreen ;
hsScalar kOffsetToBlue ;
if ( IsDebugFlagSet ( plPipeDbg : : kFlagBumpUV ) | | IsDebugFlagSet ( plPipeDbg : : kFlagBumpW ) )
{
kOffsetToRed = 0.2f ;
kOffsetToGreen = 0.6f ;
kOffsetToBlue = 1.f ;
}
else
{
kOffsetToRed = 0.f ;
kOffsetToGreen = 0.4f ;
kOffsetToBlue = 0.8f ;
}
const hsMatrix44 & l2w = span - > fLocalToWorld ;
fBumpDvMatrix . fMap [ 0 ] [ 0 ] = fBumpDuMatrix . fMap [ 0 ] [ 0 ] = ( liDir . fX * l2w . fMap [ 0 ] [ 0 ] + liDir . fY * l2w . fMap [ 1 ] [ 0 ] + liDir . fZ * l2w . fMap [ 2 ] [ 0 ] ) ;
fBumpDvMatrix . fMap [ 0 ] [ 1 ] = fBumpDuMatrix . fMap [ 0 ] [ 1 ] = ( liDir . fX * l2w . fMap [ 0 ] [ 1 ] + liDir . fY * l2w . fMap [ 1 ] [ 1 ] + liDir . fZ * l2w . fMap [ 2 ] [ 1 ] ) ;
fBumpDvMatrix . fMap [ 0 ] [ 2 ] = fBumpDuMatrix . fMap [ 0 ] [ 2 ] = ( liDir . fX * l2w . fMap [ 0 ] [ 2 ] + liDir . fY * l2w . fMap [ 1 ] [ 2 ] + liDir . fZ * l2w . fMap [ 2 ] [ 2 ] ) ;
fBumpDvMatrix . fMap [ 0 ] [ 3 ] = fBumpDuMatrix . fMap [ 0 ] [ 3 ] = kUVWOffset ;
fBumpDuMatrix . fMap [ 1 ] [ 3 ] = kOffsetToRed ;
fBumpDvMatrix . fMap [ 1 ] [ 3 ] = kOffsetToGreen ;
# ifndef BUMP_COMPARE_MATH
hsMatrix44 c2w = fView . GetCameraToWorld ( ) ;
# endif // BUMP_COMPARE_MATH
// The bump textures created so far have very strong blue components, which make anything
// bump mapped glow. The ideal fix would be to have the artists adjust the blue component
// to a better (lower) value, so there would be a little extra illumination where the bump
// is straight out into the normal direction, to complement the lateral illumination.
// Attempts so far have been unsuccessful in getting them to get a better understanding
// of bump maps, so I've just zeroed out the contribution in the normal direction.
plConst ( int ) kBumpUVOnly ( true ) ;
if ( ! kBumpUVOnly )
{
fBumpDwMatrix . fMap [ 0 ] [ 0 ] = ( liDir . fX * c2w . fMap [ 0 ] [ 0 ] + liDir . fY * c2w . fMap [ 1 ] [ 0 ] + liDir . fZ * c2w . fMap [ 2 ] [ 0 ] ) ;
fBumpDwMatrix . fMap [ 0 ] [ 1 ] = ( liDir . fX * c2w . fMap [ 0 ] [ 1 ] + liDir . fY * c2w . fMap [ 1 ] [ 1 ] + liDir . fZ * c2w . fMap [ 2 ] [ 1 ] ) ;
fBumpDwMatrix . fMap [ 0 ] [ 2 ] = ( liDir . fX * c2w . fMap [ 0 ] [ 2 ] + liDir . fY * c2w . fMap [ 1 ] [ 2 ] + liDir . fZ * c2w . fMap [ 2 ] [ 2 ] ) ;
}
else
{
fBumpDwMatrix . fMap [ 0 ] [ 0 ] = 0 ;
fBumpDwMatrix . fMap [ 0 ] [ 1 ] = 0 ;
fBumpDwMatrix . fMap [ 0 ] [ 2 ] = 0 ;
}
fBumpDwMatrix . fMap [ 0 ] [ 3 ] = kUVWOffset ;
fBumpDwMatrix . fMap [ 1 ] [ 3 ] = kOffsetToBlue ;
}
// IGetBumpMatrix ///////////////////////////////////////////////////////
// Return the correct uvw transform for the bump map channel implied
// in the miscFlags. The matrices have been previously set in ISetBumpMatrices.
const hsMatrix44 & plDXPipeline : : IGetBumpMatrix ( UInt32 miscFlags ) const
{
switch ( miscFlags & hsGMatState : : kMiscBumpChans )
{
case hsGMatState : : kMiscBumpDu :
return fBumpDuMatrix ;
case hsGMatState : : kMiscBumpDv :
return fBumpDvMatrix ;
case hsGMatState : : kMiscBumpDw :
default :
return fBumpDwMatrix ;
}
}
// ISkipBumpMap /////////////////////////////////////////////////////////////////////////
// Determine whether to skip bumpmapping on this object/material/layer combination.
// We skip if the span isn't illuminated by any lights, or bump mapping is disabled.
// If skipping, we advance <layer> past the bump layers.
// If there are no more layers after that, we return true (to abort further rendering of currSpan),
// else false to continue rendering.
hsBool plDXPipeline : : ISkipBumpMap ( hsGMaterial * newMat , UInt32 & layer , const plSpan * currSpan ) const
{
if ( newMat & & currSpan )
{
if ( newMat - > GetLayer ( layer )
& & ( newMat - > GetLayer ( layer ) - > GetMiscFlags ( ) & hsGMatState : : kMiscBumpChans )
& & ( ! currSpan - > GetNumLights ( false ) | | IsDebugFlagSet ( plPipeDbg : : kFlagNoBump ) ) )
{
layer + = 4 ;
if ( layer > = newMat - > GetNumLayers ( ) )
return true ;
}
}
return false ;
}
//// IHandleStageTransform ////////////////////////////////////////////////////
// Compute and set the UVW transform to D3D.
// This only gets interesting if the transform is dependent on on the current camera transform,
// as is the case with Reflection, Projection, or bump mapping.
void plDXPipeline : : IHandleStageTransform ( int stage , plLayerInterface * layer )
{
if ( 1
| | ! ( layer - > GetTransform ( ) . fFlags & hsMatrix44 : : kIsIdent )
| | ( fLayerState [ stage ] . fMiscFlags & ( hsGMatState : : kMiscUseReflectionXform | hsGMatState : : kMiscUseRefractionXform | hsGMatState : : kMiscProjection | hsGMatState : : kMiscBumpChans ) ) )
{
D3DXMATRIX tXfm ;
if ( fLayerState [ stage ] . fMiscFlags & ( hsGMatState : : kMiscUseReflectionXform | hsGMatState : : kMiscUseRefractionXform ) )
{
// Reflection - this is just the camera to world, with translation removed,
// and rotated to match cube map conventions.
hsMatrix44 c2env = fView . GetCameraToWorld ( ) ;
c2env = fView . GetCameraToWorld ( ) ;
c2env . fMap [ 0 ] [ 3 ]
= c2env . fMap [ 1 ] [ 3 ]
= c2env . fMap [ 2 ] [ 3 ]
= 0.f ;
if ( fLayerState [ stage ] . fMiscFlags & hsGMatState : : kMiscUseReflectionXform )
{
// This is just a rotation about X of Pi/2 (y = z, z = -y),
// followed by flipping Z to reflect back towards us (z = -z).
hsScalar t = c2env . fMap [ 1 ] [ 0 ] ;
c2env . fMap [ 1 ] [ 0 ] = c2env . fMap [ 2 ] [ 0 ] ;
c2env . fMap [ 2 ] [ 0 ] = t ;
t = c2env . fMap [ 1 ] [ 1 ] ;
c2env . fMap [ 1 ] [ 1 ] = c2env . fMap [ 2 ] [ 1 ] ;
c2env . fMap [ 2 ] [ 1 ] = t ;
t = c2env . fMap [ 1 ] [ 2 ] ;
c2env . fMap [ 1 ] [ 2 ] = c2env . fMap [ 2 ] [ 2 ] ;
c2env . fMap [ 2 ] [ 2 ] = t ;
}
else // must be kMiscUseRefractionXform
{
// Okay, I know this refraction isn't any where near
// right, so don't sit down and try to figure out the
// math and hook it to the refractive index.
// It's just a hack that will fool anyone that isn't
// really paying attention.
// This is just a rotation about X of Pi/2 (y = z, z = -y),
// followed by NOT flipping Z to reflect back towards us (z = -z).
// In other words, same as reflection, but then c2env = c2env * scaleMatNegateZ.
hsScalar t = c2env . fMap [ 1 ] [ 0 ] ;
c2env . fMap [ 1 ] [ 0 ] = c2env . fMap [ 2 ] [ 0 ] ;
c2env . fMap [ 2 ] [ 0 ] = t ;
t = c2env . fMap [ 1 ] [ 1 ] ;
c2env . fMap [ 1 ] [ 1 ] = c2env . fMap [ 2 ] [ 1 ] ;
c2env . fMap [ 2 ] [ 1 ] = t ;
t = c2env . fMap [ 1 ] [ 2 ] ;
c2env . fMap [ 1 ] [ 2 ] = c2env . fMap [ 2 ] [ 2 ] ;
c2env . fMap [ 2 ] [ 2 ] = t ;
c2env . fMap [ 0 ] [ 2 ] = - c2env . fMap [ 0 ] [ 2 ] ;
c2env . fMap [ 1 ] [ 2 ] = - c2env . fMap [ 1 ] [ 2 ] ;
c2env . fMap [ 2 ] [ 2 ] = - c2env . fMap [ 2 ] [ 2 ] ;
#if 0
const hsScalar kFishEyeScale = 0.5f ;
// You can adjust the fish-eye-ness of this by scaling
// X and Y as well. Eventually, you wind up with the same
// as c2env * scaleMatXYAndNegateZ, but this is shorter.
// kFishEyeScale gets pretty fish-eye at about 0.5, and
// like you're looking through the wrong end of a telescope
// at about 1.5.
// Ideally kFishEyeScale would be a parameter of the layer.
c2env . fMap [ 0 ] [ 0 ] * = kFishEyeScale ;
c2env . fMap [ 1 ] [ 0 ] * = kFishEyeScale ;
c2env . fMap [ 2 ] [ 0 ] * = kFishEyeScale ;
c2env . fMap [ 0 ] [ 1 ] * = kFishEyeScale ;
c2env . fMap [ 1 ] [ 1 ] * = kFishEyeScale ;
c2env . fMap [ 2 ] [ 1 ] * = kFishEyeScale ;
# endif
}
IMatrix44ToD3DMatrix ( tXfm , c2env ) ;
}
// cam2Screen will also have the kMiscPerspProjection flag set, so this needs
// to go before the regular kMiscProjection check.
else if ( fLayerState [ stage ] . fMiscFlags & hsGMatState : : kMiscCam2Screen )
{
// Still needs a bit of cleaning...
static hsVector3 camScale ( 0.5f , - 0.5f , 1.f ) ;
static hsVector3 camTrans ( 0.5f , 0.5f , 0.f ) ;
hsMatrix44 p2s ;
p2s . MakeScaleMat ( & camScale ) ;
p2s . fMap [ 0 ] [ 3 ] + = camTrans . fX ;
p2s . fMap [ 1 ] [ 3 ] + = camTrans . fY ;
// The scale and trans move us from NDC to Screen space. We need to swap
// the Z and W coordinates so that the texture projection will divide by W
// and give us projected 2D coordinates.
hsScalar temp = p2s . fMap [ 2 ] [ 2 ] ;
p2s . fMap [ 2 ] [ 2 ] = p2s . fMap [ 3 ] [ 2 ] ;
p2s . fMap [ 3 ] [ 2 ] = temp ;
temp = p2s . fMap [ 2 ] [ 3 ] ;
p2s . fMap [ 2 ] [ 3 ] = p2s . fMap [ 3 ] [ 3 ] ;
p2s . fMap [ 3 ] [ 3 ] = temp ;
IMatrix44ToD3DMatrix ( tXfm , p2s * IGetCameraToNDC ( ) ) ;
}
else if ( fLayerState [ stage ] . fMiscFlags & hsGMatState : : kMiscProjection )
{
// For projection, the worldToLight transform is in the layer transform,
// so we append the cameraToWorld, getting cameraToLight
hsMatrix44 c2w = fView . GetCameraToWorld ( ) ;
if ( ! ( layer - > GetUVWSrc ( ) & plLayerInterface : : kUVWPosition ) )
{
c2w . fMap [ 0 ] [ 3 ] = 0 ;
c2w . fMap [ 1 ] [ 3 ] = 0 ;
c2w . fMap [ 2 ] [ 3 ] = 0 ;
}
// We've already stuffed the worldToLight transform into the layer.
hsMatrix44 c2l = layer - > GetTransform ( ) * c2w ;
IMatrix44ToD3DMatrix ( tXfm , c2l ) ;
}
else if ( fLayerState [ stage ] . fMiscFlags & hsGMatState : : kMiscBumpChans )
{
// Bump matrices are already set, just get the right one and stuff it in.
hsMatrix44 m = IGetBumpMatrix ( fLayerState [ stage ] . fMiscFlags ) ;
IMatrix44ToD3DMatrix ( tXfm , m ) ;
}
else
{
// Just put take the layer transform and stuff it in.
IMatrix44ToD3DMatrix ( tXfm , layer - > GetTransform ( ) ) ;
}
fD3DDevice - > SetTransform ( sTextureStages [ stage ] , & tXfm ) ;
fLayerTransform [ stage ] = true ;
}
else if ( fLayerTransform [ stage ] )
{
// We'd like to just turn it off, but the Voodoo board freaks if the
// texture coordinates are 3-tuple for no apparent reason.
fD3DDevice - > SetTransform ( sTextureStages [ stage ] , & d3dIdentityMatrix ) ;
fLayerTransform [ stage ] = false ;
}
// If there's an lod bias associated with the layer, set it here.
// There usually isn't.
float newBias = fLayerState [ stage ] . fZFlags & hsGMatState : : kZLODBias ? layer - > GetLODBias ( ) : fTweaks . fDefaultLODBias ;
if ( newBias ! = fLayerLODBias [ stage ] )
{
fLayerLODBias [ stage ] = newBias ;
fD3DDevice - > SetSamplerState ( stage , D3DSAMP_MIPMAPLODBIAS , * ( DWORD * ) ( & fLayerLODBias [ stage ] ) ) ;
}
}
//// IUseTextureRef ///////////////////////////////////////////////////////////
// Set the texturing flags and texture.
void plDXPipeline : : IUseTextureRef ( int stage , hsGDeviceRef * dRef , plLayerInterface * layer )
{
plDXTextureRef * ref = ( plDXTextureRef * ) dRef ;
UInt32 xformFlags ;
UInt32 uvwSrc = layer - > GetUVWSrc ( ) ;
// Keep track of how much managed memory has been "seen" since the last
// evict, for that NVidia bug. Look for OSVERSIONINFO for more notes.
if ( ref - > fUseTime < = fEvictTime )
fManagedSeen + = ref - > fDataSize ;
// Also used for the same thing.
if ( ref - > fUseTime ^ fTextUseTime )
{
plProfile_NewMem ( CurrTex , ref - > fDataSize ) ;
plProfile_Inc ( NumTex ) ;
ref - > fUseTime = fTextUseTime ;
fTexUsed + = ref - > fDataSize ;
}
// DX pixel shaders require the TEXCOORDINDEX to be equal to the stage,
// even though its ignored.
if ( layer - > GetPixelShader ( ) & & ( stage ! = uvwSrc ) )
uvwSrc = stage ;
// Update our UVW source
if ( fLayerUVWSrcs [ stage ] ! = uvwSrc )
{
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_TEXCOORDINDEX , uvwSrc ) ;
fLayerUVWSrcs [ stage ] = uvwSrc ;
}
if ( ! layer - > GetVertexShader ( ) & & ! layer - > GetPixelShader ( ) )
{
/// Set the transform flags
/// Note: the perspective projection flag must be taken from the layer, since it's layer-specific.
/// Storing it on the texture ref is bad, because the texture ref can be shared among layers whose
/// projection flags might not match. This should probably be cleaned up, but for now this fixes the
/// problem.
if ( ref - > GetFlags ( ) & plDXTextureRef : : kCubicMap )
xformFlags = D3DTTFF_COUNT3 ;
else if ( layer - > GetMiscFlags ( ) & hsGMatState : : kMiscPerspProjection )
xformFlags = D3DTTFF_COUNT3 | D3DTTFF_PROJECTED ;
else
xformFlags = D3DTTFF_COUNT2 ;
if ( xformFlags ! = fLayerXformFlags [ stage ] )
{
fLayerXformFlags [ stage ] = xformFlags ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_TEXTURETRANSFORMFLAGS , xformFlags ) ;
}
}
// Update our current ref
if ( ! ref - > fD3DTexture )
{
if ( ref - > fData )
IReloadTexture ( ref ) ;
}
else if ( dRef = = fLayerRef [ stage ] )
{
return ;
}
hsRefCnt_SafeAssign ( fLayerRef [ stage ] , dRef ) ;
/// Actually make it active!
fD3DDevice - > SetTexture ( stage , ref - > fD3DTexture ) ;
}
//// IStageStop ///////////////////////////////////////////////////////////////
// Tell the hardware we won't be using any more stages.
// This is more complicated than it sounds. Cases:
// a) single texture stage, we're done (because we've already set
// texture times diffuse), so just disable stage 1.
// b) we have 2 stages active.
// b.0) we're skipping texture color on one of those 2 stages. In that
// case, we've already modulated in our diffuse, so just
// disable stage 2.
// b.1) we're using texture color from both stages 0 and 1, and still need
// to modulate in diffuse. So set stage 2 to modulate in diffuse,
// and disable stage 3.
// c) we have 3 or more stages active. Append a modulation by diffuse
// Note that this only applies to color, because diffuse alpha is always modulated
// in from the start.
void plDXPipeline : : IStageStop ( UInt32 stage )
{
int disableStage = stage ;
// Note: even if we don't have a texture, we handle it similar to if we had one,
// so the only special case we need here is if we only had one stage to set up -mcn
if ( ( stage < = 1 ) )
{
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLayerState [ stage ] . fBlendFlags = UInt32 ( - 1 ) ;
disableStage = stage ;
}
else if ( stage = = 2 )
{
// The fMaxLayersAtOnce == 2 check is for the DX9.0c 2 texture limitation.
// See ILayersAtOnce()
if ( ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendNoTexColor )
| | ( fLayerState [ 1 ] . fBlendFlags & hsGMatState : : kBlendNoTexColor )
| | fSettings . fMaxLayersAtOnce = = 2 )
{
fD3DDevice - > SetTextureStageState ( 2 , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
disableStage = 2 ;
}
else
{
fD3DDevice - > SetTextureStageState ( 2 , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( 2 , D3DTSS_COLORARG1 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( 2 , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( 3 , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
disableStage = 3 ;
}
fD3DDevice - > SetTextureStageState ( 2 , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLayerState [ 2 ] . fBlendFlags = UInt32 ( - 1 ) ;
fLayerState [ 3 ] . fBlendFlags = UInt32 ( - 1 ) ;
}
else
{
// This is directly contrary to the DX documentation, but in line with
// the code generated by MFCTex (which works). The docs say:
// "Alpha operations cannot be disabled when color operations are enabled.
// Setting the alpha operation to D3DTOP_DISABLE when color blending
// is enabled causes undefined behavior."
// But not disabling the earliest possible alpha stage causes the driver
// to choke.
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG1 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( stage , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLayerState [ stage ] . fBlendFlags = UInt32 ( - 1 ) ;
fD3DDevice - > SetTextureStageState ( stage + 1 , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
fLayerState [ stage + 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
disableStage = stage + 1 ;
}
fLastEndingStage = stage ;
if ( fSettings . fIsIntel )
{
int maxUVW = 0 ;
int k ;
for ( k = 0 ; k < fCurrNumLayers ; k + + )
{
if ( ( fCurrMaterial - > GetLayer ( k + fCurrLayerIdx ) - > GetUVWSrc ( ) & 0xf ) > maxUVW )
maxUVW = fCurrMaterial - > GetLayer ( k + fCurrLayerIdx ) - > GetUVWSrc ( ) & 0xf ;
}
for ( k = disableStage ; k < = maxUVW ; k + + )
{
fD3DDevice - > SetTextureStageState ( k , D3DTSS_COLOROP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( k , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
}
fD3DDevice - > SetTextureStageState ( k , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
}
}
// IInvalidateState /////////////////////////////////////////////////////////////
// Documentation is unclear on what state persists or becomes invalid on switching
// a render target or finishing a frame. I put into this function things that show
// up as suspect, whether they "ought" to be here or not.
void plDXPipeline : : IInvalidateState ( )
{
fLastEndingStage = 0 ;
fTexturing = false ;
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
hsRefCnt_SafeUnRef ( fLayerRef [ i ] ) ;
fLayerRef [ i ] = nil ;
fD3DDevice - > SetTexture ( i , nil ) ;
}
fLayerState [ 0 ] . fZFlags = 0 ;
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_LESSEQUAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , TRUE ) ;
// This is a workaround for the latest ATI drivers (6.14.10.6422).
// They seem to be caching something on lights (possibly only specular
// lights, but I haven't been able to prove it) the first time they
// are used in a render, and then not letting go when the camera
// is moved for another render in the same frame (same BeginScene/EndScene pair).
// The effect is very incorrect lighting. Moreover, if the multiple renders
// per frame are infrequent (e.g. refreshing an environment map every few
// seconds), you'll get flashes after the double render frames.
// Workaround is to Disable all lights at render target switch, although
// a more correct workaround might be to disable all lights at camera move.
// All of this is strictly conjecture, so I'm going with what works.
// Note also that I'm only disabling lights that are currently enabled
// at the time of the render target switch. Since this is dealing with
// a driver bug, it might be safer to disable them all, but timings
// show that looping through all the lights in a scene like Teledahn exterior,
// with hundreds of active lights, incurs a measurable expense (some milliseconds),
// whereas disabling only the active lights fixes the known problem but costs
// zero.
plProfile_BeginTiming ( ClearLights ) ;
hsBitIterator iterOff ( fLights . fEnabledFlags ) ;
for ( iterOff . Begin ( ) ; ! iterOff . End ( ) ; iterOff . Advance ( ) )
fD3DDevice - > LightEnable ( iterOff . Current ( ) , false ) ;
fLights . fEnabledFlags . Clear ( ) ;
fLights . fHoldFlags . Clear ( ) ;
plProfile_EndTiming ( ClearLights ) ;
// This is very annoying. Set fTexturing to false doesn't work if the next layer
// we draw doesn't have a texture. So we have to set this flag instead to force
// a state update. I have an idea about how to do all of this a lot better, but
// it's not time to do it...not yet at least.... --mcn
fSettings . fVeryAnnoyingTextureInvalidFlag = true ;
}
//// ILayersAtOnce ////////////////////////////////////////////////////////////
// Compute how many of the upcoming layers we can render in a single pass on the
// current hardware.
UInt32 plDXPipeline : : ILayersAtOnce ( hsGMaterial * mat , UInt32 which )
{
fCurrNumLayers = 1 ;
if ( fView . fRenderState & plPipeline : : kRenderBaseLayerOnly )
return fCurrNumLayers ;
plLayerInterface * lay = mat - > GetLayer ( which ) ;
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoMultitexture ) )
return fCurrNumLayers ;
if ( ( IsDebugFlagSet ( plPipeDbg : : kFlagBumpUV ) | | IsDebugFlagSet ( plPipeDbg : : kFlagBumpW ) ) & & ( lay - > GetMiscFlags ( ) & hsGMatState : : kMiscBumpChans ) )
return fCurrNumLayers = 2 ;
if ( ( lay - > GetBlendFlags ( ) & hsGMatState : : kBlendNoColor )
| | ( lay - > GetMiscFlags ( ) & hsGMatState : : kMiscTroubledLoner )
)
return fCurrNumLayers ;
// New DX9.0c limitation for cards that can only do 2 textures per pass.
// We used to be able to set stage 0 and 1 to textures, and set stage 2 to the
// diffuse color. With DX9.0c we just get two texture stages. Period.
// Either we give up a texture or the diffuse color.
if ( fSettings . fMaxLayersAtOnce = = 2 )
{
if ( ( mat - > GetNumLayers ( ) > which + 1 )
& & ! ( mat - > GetLayer ( which + 1 ) - > GetBlendFlags ( ) & hsGMatState : : kBlendNoTexColor ) )
{
// If we're just using the texture for alpha, we can multiply
// the diffuse color in stage 1. Otherwise, save it for the next pass.
return fCurrNumLayers ;
}
}
int i ;
int maxLayersAtOnce = fSettings . fMaxLayersAtOnce ;
// Now Reserve space for piggy backs, and see if there are
// are any more layers we can pick up.
//
maxLayersAtOnce = fSettings . fMaxLayersAtOnce - fActivePiggyBacks ;
if ( which + maxLayersAtOnce > mat - > GetNumLayers ( ) )
maxLayersAtOnce = mat - > GetNumLayers ( ) - which ;
for ( i = fCurrNumLayers ; i < maxLayersAtOnce ; i + + )
{
plLayerInterface * lay = mat - > GetLayer ( which + i ) ;
if ( ( lay - > GetUVWSrc ( ) & 0xf ) > fSettings . fMaxUVWSrc )
break ;
if ( ( lay - > GetMiscFlags ( ) & hsGMatState : : kMiscBindNext )
& & ( i + 1 > = maxLayersAtOnce ) )
break ;
if ( lay - > GetMiscFlags ( ) & hsGMatState : : kMiscRestartPassHere )
break ;
if ( ! ( mat - > GetLayer ( which + i - 1 ) - > GetMiscFlags ( ) & hsGMatState : : kMiscBindNext )
& & ! ICanEatLayer ( lay ) )
break ;
fCurrNumLayers + + ;
}
return fCurrNumLayers ;
}
//// ICanEatLayer /////////////////////////////////////////////////////////////
// Determine if this layer can be an upper layer, or if it needs
// to be the base on another pass.
hsBool plDXPipeline : : ICanEatLayer ( plLayerInterface * lay )
{
if ( ! lay - > GetTexture ( ) )
return false ;
if ( ( lay - > GetBlendFlags ( ) & hsGMatState : : kBlendNoColor )
| | ( lay - > GetBlendFlags ( ) & hsGMatState : : kBlendAddColorTimesAlpha ) // has to be base layer
| | ( lay - > GetMiscFlags ( ) & hsGMatState : : kMiscTroubledLoner ) )
return false ;
if ( ( lay - > GetBlendFlags ( ) & hsGMatState : : kBlendAlpha )
& & ( lay - > GetAmbientColor ( ) . a < hsScalar1 ) )
return false ;
if ( ! ( lay - > GetZFlags ( ) & hsGMatState : : kZNoZWrite ) )
return false ;
return true ;
}
///////////////////////////////////////////////////////////////////////////////
//// Textures /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// IReloadTexture ///////////////////////////////////////////////////////////
// Fills in D3D texture resource, creating it if necessary.
void plDXPipeline : : IReloadTexture ( plDXTextureRef * ref )
{
if ( ref - > GetFlags ( ) & plDXTextureRef : : kCubicMap )
{
if ( ref - > fD3DTexture = = nil )
ref - > fD3DTexture = IMakeD3DCubeTexture ( ref , ref - > fFormatType ) ;
if ( ref - > fD3DTexture ! = nil )
IFillD3DCubeTexture ( ( plDXCubeTextureRef * ) ref ) ;
}
else
{
if ( ref - > fD3DTexture = = nil )
ref - > fD3DTexture = IMakeD3DTexture ( ref , ref - > fFormatType ) ;
if ( ref - > fD3DTexture ! = nil )
IFillD3DTexture ( ref ) ;
}
}
//// IMakeD3DTexture //////////////////////////////////////////////////////////
// Makes a DX Texture object based on the ref given.
IDirect3DTexture9 * plDXPipeline : : IMakeD3DTexture ( plDXTextureRef * ref , D3DFORMAT formatType )
{
D3DPOOL poolType = D3DPOOL_MANAGED ;
IDirect3DTexture9 * texPtr ;
fManagedAlloced = true ;
if ( FAILED ( fSettings . fDXError = fD3DDevice - > CreateTexture ( ref - > fMaxWidth , ref - > fMaxHeight ,
ref - > fMMLvs ,
0 ,
formatType ,
poolType ,
& texPtr , NULL ) ) )
{
IGetD3DError ( ) ;
plStatusLog : : AddLineS ( " pipeline.log " , 0xffff0000 , " Unable to create texture (%s) Owner: %s "
" Size: %d x %d NumLvls: %d Flags: %x " ,
fSettings . fErrorStr , ref - > fOwner ? ref - > fOwner - > GetKey ( ) ? ref - > fOwner - > GetKey ( ) - > GetUoid ( ) . GetObjectName ( ) : " " : " " ,
ref - > fMaxWidth , ref - > fMaxHeight , ref - > fMMLvs , ref - > GetFlags ( ) ) ;
return nil ;
}
PROFILE_POOL_MEM ( poolType , ref - > fDataSize , true , ( ref - > fOwner ? ref - > fOwner - > GetKey ( ) ? ref - > fOwner - > GetKey ( ) - > GetUoid ( ) . GetObjectName ( ) : " (UnknownTexture) " : " (UnknownTexture) " ) ) ;
fTexManaged + = ref - > fDataSize ;
return texPtr ;
}
//// IFillD3DTexture //////////////////////////////////////////////////////////
// Copies the data from the ref into the D3D texture, filling in all
// mip levels.
void plDXPipeline : : IFillD3DTexture ( plDXTextureRef * ref )
{
int i ;
UInt8 * pTexDat = ( UInt8 * ) ref - > fData ;
if ( pTexDat = = nil )
{
plStatusLog : : AddLineS ( " pipeline.log " , 0xffff0000 , " Unable to fill texture ref (data is nil) Owner: %s " ,
ref - > fOwner ? ref - > fOwner - > GetKey ( ) ? ref - > fOwner - > GetKey ( ) - > GetUoid ( ) . GetObjectName ( ) : " " : " " ) ;
return ;
}
IDirect3DTexture9 * lpDst = ( IDirect3DTexture9 * ) ref - > fD3DTexture ;
for ( i = 0 ; i < ref - > fMMLvs ; i + + )
{
D3DLOCKED_RECT lockInfo ;
if ( FAILED ( fSettings . fDXError = lpDst - > LockRect ( i , & lockInfo , nil , 0 ) ) )
{
IGetD3DError ( ) ;
plStatusLog : : AddLineS ( " pipeline.log " , 0xffff0000 , " Unable to lock texture level %d for filling (%s) Owner: %s "
" Size: %d x %d NumLvls: %d Flags: %x " ,
i , fSettings . fErrorStr , ref - > fOwner ? ref - > fOwner - > GetKey ( ) ? ref - > fOwner - > GetKey ( ) - > GetUoid ( ) . GetObjectName ( ) : " " : " " ,
ref - > fMaxWidth , ref - > fMaxHeight , ref - > fMMLvs , ref - > GetFlags ( ) ) ;
return ;
}
memcpy ( ( char * ) lockInfo . pBits , pTexDat , ref - > fLevelSizes [ i ] ) ;
pTexDat + = ref - > fLevelSizes [ i ] ;
lpDst - > UnlockRect ( i ) ;
}
}
//// IMakeD3DCubeTexture //////////////////////////////////////////////////////
// Makes a DX Cubic Texture object based on the ref given.
IDirect3DCubeTexture9 * plDXPipeline : : IMakeD3DCubeTexture ( plDXTextureRef * ref , D3DFORMAT formatType )
{
D3DPOOL poolType = D3DPOOL_MANAGED ;
IDirect3DCubeTexture9 * texPtr = nil ;
fManagedAlloced = true ;
WEAK_ERROR_CHECK ( fD3DDevice - > CreateCubeTexture ( ref - > fMaxWidth , ref - > fMMLvs , 0 , formatType , poolType , & texPtr , NULL ) ) ;
PROFILE_POOL_MEM ( poolType , ref - > fDataSize , true , ( ref - > fOwner ? ref - > fOwner - > GetKey ( ) ? ref - > fOwner - > GetKey ( ) - > GetUoid ( ) . GetObjectName ( ) : " (UnknownTexture) " : " (UnknownTexture) " ) ) ;
fTexManaged + = ref - > fDataSize ;
return texPtr ;
}
//// IFillD3DCubeTexture //////////////////////////////////////////////////////
// Fill in all faces of the D3D cube map from the input reference.
void plDXPipeline : : IFillD3DCubeTexture ( plDXCubeTextureRef * ref )
{
int i , f ;
D3DCUBEMAP_FACES faces [ 6 ] = { D3DCUBEMAP_FACE_NEGATIVE_X , // Left
D3DCUBEMAP_FACE_POSITIVE_X , // Right
D3DCUBEMAP_FACE_POSITIVE_Z , // Front
D3DCUBEMAP_FACE_NEGATIVE_Z , // Back
D3DCUBEMAP_FACE_POSITIVE_Y , // Top
D3DCUBEMAP_FACE_NEGATIVE_Y } ; // Bottom
for ( f = 0 ; f < 6 ; f + + )
{
UInt8 * pTexDat = ( f = = 0 ) ? ( UInt8 * ) ref - > fData : ( UInt8 * ) ref - > fFaceData [ f - 1 ] ;
IDirect3DCubeTexture9 * lpDst = ( IDirect3DCubeTexture9 * ) ref - > fD3DTexture ;
for ( i = 0 ; i < ref - > fMMLvs ; i + + )
{
D3DLOCKED_RECT lockInfo ;
lpDst - > LockRect ( faces [ f ] , i , & lockInfo , nil , 0 ) ;
memcpy ( ( char * ) lockInfo . pBits , pTexDat , ref - > fLevelSizes [ i ] ) ;
pTexDat + = ref - > fLevelSizes [ i ] ;
lpDst - > UnlockRect ( faces [ f ] , i ) ;
}
}
}
//// MakeTextureRef ///////////////////////////////////////////////////////////
// Creates a hsGDeviceRef for a texture.
// May have to decompress the texture if the hardware doesn't support compressed textures (unlikely).
hsGDeviceRef * plDXPipeline : : MakeTextureRef ( plLayerInterface * layer , plMipmap * b )
{
plMipmap * original = b , * colorized = nil ;
// If the hardware doesn't support Luminance maps, we'll just treat as ARGB.
if ( ! ( fSettings . fD3DCaps & kCapsLuminanceTextures ) )
b - > SetFlags ( b - > GetFlags ( ) & ~ plMipmap : : kIntensityMap ) ;
/// Colorize if we're supposed to (8.21.2000 mcn)
// Debugging only.
if ( IsDebugFlagSet ( plPipeDbg : : kFlagColorizeMipmaps ) )
{
b = original - > Clone ( ) ;
if ( b ! = nil )
b - > Colorize ( ) ;
else
b = original ;
}
if ( ! ( fSettings . fD3DCaps & kCapsCompressTextures ) & & b - > IsCompressed ( ) )
b = hsCodecManager : : Instance ( ) . CreateUncompressedMipmap ( b , hsCodecManager : : k16BitDepth ) ;
/// Set up some stuff
UInt32 mmlvs = 1 ;
D3DFORMAT formatType = D3DFMT_UNKNOWN ; // D3D Format
UInt32 formatSize = 0 ;
UInt32 totalSize = 0 ;
UInt32 * levelSizes = nil ;
UInt32 numPix = 0 ;
UInt32 externData = false ;
void * tData ;
hsBool noMip = ! ( fSettings . fD3DCaps & kCapsMipmap ) ;
/// Convert the bitmap over
// Select a target format
IGetD3DTextureFormat ( b , formatType , formatSize ) ;
// Process the texture data into a format that can be directly copied to the D3D texture.
// externData returned as true means that tData just points directly into the mipmap's fImage,
// so don't delete it when deleting the texture device ref. externData false means this is
// a reformatted copy, so the ref owns it.
externData = IProcessMipmapLevels ( b , mmlvs , levelSizes , totalSize , numPix , tData , noMip ) ;
// If the texture has a device ref, just re-purpose it, else make one and initialize it.
plDXTextureRef * ref = ( plDXTextureRef * ) b - > GetDeviceRef ( ) ;
if ( ! ref )
{
ref = TRACKED_NEW plDXTextureRef ( formatType ,
mmlvs , b - > GetWidth ( ) , b - > GetHeight ( ) ,
numPix , totalSize , totalSize , levelSizes ,
tData , externData ) ;
ref - > fOwner = original ;
ref - > Link ( & fTextureRefList ) ;
original - > SetDeviceRef ( ref ) ;
// Note: this is because SetDeviceRef() will ref it, and at this point,
// only the bitmap should own the ref, not us. We ref/unref it on Use()
hsRefCnt_SafeUnRef ( ref ) ;
}
else
ref - > Set ( formatType , mmlvs , b - > GetWidth ( ) , b - > GetHeight ( ) ,
numPix , totalSize , totalSize , levelSizes , tData , externData ) ;
// Keep the refs in a linked list for easy disposal.
if ( ! ref - > IsLinked ( ) )
{
// Re-linking
ref - > Link ( & fTextureRefList ) ;
}
/// Copy the data into the ref
IReloadTexture ( ref ) ;
ref - > fData = nil ;
ref - > SetDirty ( false ) ;
// Set any implied flags.
if ( layer )
{
if ( layer - > GetMiscFlags ( ) & hsGMatState : : kMiscPerspProjection )
ref - > SetFlags ( ref - > GetFlags ( ) | plDXTextureRef : : kPerspProjection ) ;
else if ( layer - > GetMiscFlags ( ) & hsGMatState : : kMiscOrthoProjection )
ref - > SetFlags ( ref - > GetFlags ( ) | plDXTextureRef : : kOrthoProjection ) ;
if ( layer - > GetMiscFlags ( ) & hsGMatState : : kMiscBumpDw )
ref - > SetFlags ( ref - > GetFlags ( ) | plDXTextureRef : : kUVWNormal ) ;
}
if ( b ! = original )
delete b ; // Delete if we created a new (temporary) one
// Turn this on to delete the plasma system memory copy once we have a D3D managed version.
// Currently disabled, because there are still mipmaps that are read from after their managed
// versions are created, but aren't flagged DontThrowAwayImage or kUserOwnesBitmap.
if ( ! ( original - > GetFlags ( ) & ( plMipmap : : kUserOwnsBitmap | plMipmap : : kDontThrowAwayImage ) )
& & ! GetProperty ( kPropDontDeleteTextures ) )
{
# ifdef MF_TOSSER
original - > Reset ( ) ;
# endif // MF_TOSSER
}
return ref ;
}
//// IMakeCubicTextureRef /////////////////////////////////////////////////////
// Same as MakeTextureRef, except done for the six faces of a cube map.
hsGDeviceRef * plDXPipeline : : IMakeCubicTextureRef ( plLayerInterface * layer , plCubicEnvironmap * cubic )
{
plDXCubeTextureRef * ref ;
plMipmap * faces [ 6 ] ;
int i ;
D3DFORMAT formatType = D3DFMT_UNKNOWN ;
UInt32 formatSize = 0 ;
UInt32 numLevels = 1 ;
UInt32 totalSize = 0 ;
UInt32 * levelSizes = nil ;
UInt32 numPixels = 0 ;
UInt32 externData ;
void * textureData [ 6 ] ;
if ( cubic = = nil | | ! ( fSettings . fD3DCaps & kCapsCubicTextures ) )
return nil ;
hsBool noMip = ! ( fSettings . fD3DCaps & kCapsMipmap ) | | ! ( fSettings . fD3DCaps & kCapsCubicMipmap ) ;
/// Get the mips
if ( ! ( fSettings . fD3DCaps & kCapsCompressTextures ) )
{
for ( i = 0 ; i < 6 ; i + + )
{
faces [ i ] = cubic - > GetFace ( i ) ;
if ( faces [ i ] - > IsCompressed ( ) )
faces [ i ] = hsCodecManager : : Instance ( ) . CreateUncompressedMipmap ( faces [ i ] , hsCodecManager : : k16BitDepth ) ;
}
}
else
{
for ( i = 0 ; i < 6 ; i + + )
faces [ i ] = cubic - > GetFace ( i ) ;
}
/// Create the ref
// Get format
IGetD3DTextureFormat ( faces [ 0 ] , formatType , formatSize ) ;
// Process the data.
if ( faces [ 0 ] - > IsCompressed ( ) | | ( faces [ 0 ] - > GetPixelSize ( ) < 32 ) )
{
/// For this, we just take the image data pointers directly, so only call IProcess once
externData = IProcessMipmapLevels ( faces [ 0 ] , numLevels , levelSizes , totalSize , numPixels , textureData [ 0 ] , noMip ) ;
for ( i = 1 ; i < 6 ; i + + )
textureData [ i ] = faces [ i ] - > GetImage ( ) ;
}
else
{
for ( i = 0 ; i < 6 ; i + + )
{
/// Some of this will be redundant, but oh well
externData = IProcessMipmapLevels ( faces [ i ] , numLevels , levelSizes , totalSize , numPixels , textureData [ i ] , noMip ) ;
}
}
ref = ( plDXCubeTextureRef * ) cubic - > GetDeviceRef ( ) ;
if ( ! ref )
{
ref = TRACKED_NEW plDXCubeTextureRef ( formatType ,
numLevels , faces [ 0 ] - > GetWidth ( ) , faces [ 0 ] - > GetHeight ( ) ,
numPixels , totalSize , totalSize * 6 , levelSizes ,
textureData [ 0 ] , externData ) ;
ref - > fOwner = cubic ;
ref - > Link ( & fTextureRefList ) ; // So we don't ref later on down
for ( i = 0 ; i < 5 ; i + + )
ref - > fFaceData [ i ] = textureData [ i + 1 ] ;
cubic - > SetDeviceRef ( ref ) ;
// Note: this is because SetDeviceRef() will ref it, and at this point,
// only the bitmap should own the ref, not us. We ref/unref it on Use()
hsRefCnt_SafeUnRef ( ref ) ;
}
else
{
ref - > Set ( formatType , numLevels , faces [ 0 ] - > GetWidth ( ) , faces [ 0 ] - > GetHeight ( ) ,
numPixels , totalSize , totalSize * 6 , levelSizes , textureData [ 0 ] , externData ) ;
for ( i = 0 ; i < 5 ; i + + )
ref - > fFaceData [ i ] = textureData [ i + 1 ] ;
}
ref - > SetFlags ( ref - > GetFlags ( ) | plDXTextureRef : : kCubicMap ) ;
// Put in linked list for easy disposal.
if ( ! ref - > IsLinked ( ) )
{
// Re-linking
ref - > Link ( & fTextureRefList ) ;
}
/// Copy the data into the ref
IReloadTexture ( ref ) ;
ref - > SetDirty ( false ) ;
/// Cleanup
for ( i = 0 ; i < 6 ; i + + )
{
if ( faces [ i ] ! = cubic - > GetFace ( i ) )
delete faces [ i ] ;
if ( ! ( cubic - > GetFace ( i ) - > GetFlags ( ) & ( plMipmap : : kUserOwnsBitmap | plMipmap : : kDontThrowAwayImage ) ) & & ! GetProperty ( kPropDontDeleteTextures ) )
{
// Turn this on to delete the plasma system memory copy once we have a D3D managed version.
// Currently disabled, because there are still mipmaps that are read from after their managed
// versions are created, but aren't flagged DontThrowAwayImage or kUserOwnesBitmap.
// cubic->GetFace(i)->Reset();
}
}
return ref ;
}
//// IProcessMipmapLevels /////////////////////////////////////////////////////
// Compute proper values for the arguments passed in.
// Return true if the data returned points directly into the mipmap data,
// return false if textureData is a reformatted copy of the mipmap's data.
hsBool plDXPipeline : : IProcessMipmapLevels ( plMipmap * mipmap , UInt32 & numLevels ,
UInt32 * & levelSizes , UInt32 & totalSize ,
UInt32 & numPixels , void * & textureData , hsBool noMip )
{
hsBool externData = false ;
D3DFORMAT formatType = D3DFMT_UNKNOWN ; // D3D Format
UInt32 formatSize ;
IGetD3DTextureFormat ( mipmap , formatType , formatSize ) ;
// Compressed or 16 bit, we can use directly.
if ( mipmap - > IsCompressed ( ) | | ( mipmap - > GetPixelSize ( ) < 32 ) )
{
numPixels = 0 ;
if ( noMip )
{
numLevels = 1 ;
levelSizes = nil ;
totalSize = mipmap - > GetLevelSize ( 0 ) ;
}
else
{
UInt32 sizeMask = 0x03 ;
/// 10.31.2000 - If we have this flag set, we really have to cut out
/// sizes under 8x8. So far only true on the KYRO...
if ( fSettings . fD3DCaps & kCapsNoKindaSmallTexs )
sizeMask = 0x07 ;
int maxLevel = mipmap - > GetNumLevels ( ) - 1 ;
/// 9.7.2000 - Also do this test if the card doesn't support
/// itty bitty textures
if ( mipmap - > IsCompressed ( ) | | ! ( fSettings . fD3DCaps & kCapsDoesSmallTextures ) )
{
mipmap - > SetCurrLevel ( maxLevel ) ;
while ( ( mipmap - > GetCurrWidth ( ) | mipmap - > GetCurrHeight ( ) ) & sizeMask )
{
maxLevel - - ;
hsAssert ( maxLevel > = 0 , " How was this ever compressed? " ) ;
mipmap - > SetCurrLevel ( maxLevel ) ;
}
}
mipmap - > SetCurrLevel ( 0 ) ;
totalSize = 0 ;
numLevels = maxLevel + 1 ;
levelSizes = TRACKED_NEW UInt32 [ numLevels ] ;
int i ;
for ( i = 0 ; i < numLevels ; i + + )
{
levelSizes [ i ] = mipmap - > GetLevelSize ( i ) ;
totalSize + = mipmap - > GetLevelSize ( i ) ;
}
}
textureData = mipmap - > GetImage ( ) ;
externData = true ;
}
else
{
// 32 bit uncompressed data. In general, we reformat to 16 bit if we're running
// 16 bit, or if 32 bit leave it at 32. All subject to what the hardware can do
// and what the texture is for. See IGetD3DTextureFormat.
formatSize > > = 3 ;
if ( ! noMip )
{
numPixels = mipmap - > GetTotalSize ( ) * 8 / mipmap - > GetPixelSize ( ) ;
numLevels = mipmap - > GetNumLevels ( ) ;
levelSizes = TRACKED_NEW UInt32 [ numLevels ] ;
int i ;
UInt32 w , h ;
for ( i = 0 ; i < numLevels ; i + + )
{
mipmap - > GetLevelPtr ( i , & w , & h ) ;
levelSizes [ i ] = w * h * formatSize ;
}
}
else
{
numPixels = mipmap - > GetWidth ( ) * mipmap - > GetHeight ( ) ;
numLevels = 1 ;
levelSizes = nil ;
}
totalSize = numPixels * formatSize ;
// Shared scratch space to reformat a texture before it's copied into
// the D3D surface.
textureData = IGetPixelScratch ( totalSize ) ;
// Convert it to the requested format.
IFormatTextureData ( formatType , numPixels , ( hsRGBAColor32 * ) mipmap - > GetImage ( ) , textureData ) ;
}
return externData ;
}
//// IGetPixelScratch /////////////////////////////////////////////////////////
// Return scratch space at least of at least size bytes, to reformat a mipmap into.
void * plDXPipeline : : IGetPixelScratch ( UInt32 size )
{
static char * sPtr = nil ;
static UInt32 sSize = 0 ;
if ( size > sSize )
{
if ( sPtr ! = nil )
delete [ ] sPtr ;
if ( size > 0 )
sPtr = TRACKED_NEW char [ sSize = size ] ;
else
sPtr = nil ;
}
else if ( size = = 0 )
{
if ( sPtr ! = nil )
delete [ ] sPtr ;
sPtr = nil ;
sSize = 0 ;
}
return sPtr ;
}
//// IGetD3DTextureFormat /////////////////////////////////////////////////////
// Given a bitmap, finds the matching D3D format.
void plDXPipeline : : IGetD3DTextureFormat ( plBitmap * b , D3DFORMAT & formatType , UInt32 & texSize )
{
hsAssert ( b , " Nil input to GetTextureFormat() " ) ;
hsBool prefer32bit = 0 ! = ( b - > GetFlags ( ) & plBitmap : : kForce32Bit ) ;
if ( b - > IsCompressed ( ) )
{
hsAssert ( plMipmap : : kDirectXCompression = = b - > fCompressionType , " Unsupported compression format " ) ;
texSize = 0 ;
switch ( b - > fDirectXInfo . fCompressionType )
{
case plMipmap : : DirectXInfo : : kDXT1 :
formatType = D3DFMT_DXT1 ;
break ;
// case plMipmap::DirectXInfo::kDXT2:
// formatType = D3DFMT_DXT2;
// break;
// case plMipmap::DirectXInfo::kDXT3:
// formatType = D3DFMT_DXT3;
// break;
// case plMipmap::DirectXInfo::kDXT4:
// formatType = D3DFMT_DXT4;
// break;
case plMipmap : : DirectXInfo : : kDXT5 :
formatType = D3DFMT_DXT5 ;
break ;
default :
hsAssert ( false , " Unknown DirectX compression format " ) ;
}
}
else if ( b - > GetFlags ( ) & plMipmap : : kBumpEnvMap )
{
texSize = 16 ;
if ( b - > GetFlags ( ) & plMipmap : : kAlphaChannelFlag )
formatType = D3DFMT_L6V5U5 ;
else
formatType = D3DFMT_V8U8 ;
}
else if ( b - > GetPixelSize ( ) = = 16 )
{
texSize = 16 ;
if ( b - > GetFlags ( ) & plMipmap : : kIntensityMap )
{
if ( b - > GetFlags ( ) & plMipmap : : kAlphaChannelFlag )
formatType = D3DFMT_A8L8 ;
else
formatType = D3DFMT_L8 ;
}
else
{
if ( b - > GetFlags ( ) & plMipmap : : kAlphaChannelFlag )
formatType = D3DFMT_A4R4G4B4 ;
else
formatType = D3DFMT_A1R5G5B5 ;
}
}
else if ( b - > GetFlags ( ) & plMipmap : : kIntensityMap )
{
if ( b - > GetFlags ( ) & plMipmap : : kAlphaChannelFlag )
{
if ( ITextureFormatAllowed ( D3DFMT_A8L8 ) )
{
formatType = D3DFMT_A8L8 ;
texSize = 16 ;
}
else if ( ! prefer32bit & & ( fSettings . fColorDepth = = 16 ) & & ITextureFormatAllowed ( D3DFMT_A4R4G4B4 ) )
{
formatType = D3DFMT_A4R4G4B4 ;
texSize = 16 ;
}
else if ( ITextureFormatAllowed ( D3DFMT_A8R8G8B8 ) )
{
formatType = D3DFMT_A8R8G8B8 ;
texSize = 32 ;
}
else if ( ITextureFormatAllowed ( D3DFMT_A4R4G4B4 ) )
{
formatType = D3DFMT_A4R4G4B4 ;
texSize = 16 ;
}
}
else
{
if ( ITextureFormatAllowed ( D3DFMT_L8 ) )
{
formatType = D3DFMT_L8 ;
texSize = 8 ;
}
else if ( ! prefer32bit & & ( fSettings . fColorDepth = = 16 ) & & ITextureFormatAllowed ( D3DFMT_A1R5G5B5 ) )
{
formatType = D3DFMT_A1R5G5B5 ;
texSize = 16 ;
}
else if ( ITextureFormatAllowed ( D3DFMT_A8R8G8B8 ) )
{
formatType = D3DFMT_A8R8G8B8 ;
texSize = 32 ;
}
else if ( ITextureFormatAllowed ( D3DFMT_A1R5G5B5 ) )
{
formatType = D3DFMT_A1R5G5B5 ;
texSize = 16 ;
}
}
}
else
{
if ( b - > GetFlags ( ) & plMipmap : : kAlphaChannelFlag )
{
if ( ! prefer32bit & & ( fSettings . fColorDepth = = 16 ) & & ITextureFormatAllowed ( D3DFMT_A4R4G4B4 ) )
{
formatType = D3DFMT_A4R4G4B4 ;
texSize = 16 ;
}
else if ( ITextureFormatAllowed ( D3DFMT_A8R8G8B8 ) )
{
formatType = D3DFMT_A8R8G8B8 ;
texSize = 32 ;
}
else if ( ITextureFormatAllowed ( D3DFMT_A4R4G4B4 ) )
{
formatType = D3DFMT_A4R4G4B4 ;
texSize = 16 ;
}
}
else
{
if ( ! prefer32bit & & ( fSettings . fColorDepth = = 16 ) & & ITextureFormatAllowed ( D3DFMT_A1R5G5B5 ) )
{
formatType = D3DFMT_A1R5G5B5 ;
texSize = 16 ;
}
else if ( ITextureFormatAllowed ( D3DFMT_A8R8G8B8 ) )
{
formatType = D3DFMT_A8R8G8B8 ;
texSize = 32 ;
}
else if ( ITextureFormatAllowed ( D3DFMT_A1R5G5B5 ) )
{
formatType = D3DFMT_A1R5G5B5 ;
texSize = 16 ;
}
}
}
hsAssert ( formatType , " failing to find format type " ) ;
}
//// IFormatTextureData ///////////////////////////////////////////////////////
// Convert the input 32 bit uncompressed RGBA data into the requested format.
void plDXPipeline : : IFormatTextureData ( UInt32 formatType , UInt32 numPix , hsRGBAColor32 * const src , void * dst )
{
switch ( formatType )
{
case D3DFMT_L6V5U5 :
{
UInt16 * pixels = ( UInt16 * ) dst ;
hsRGBAColor32 * p = src ;
hsRGBAColor32 * end = src + numPix ;
while ( p < end )
{
* pixels = ( ( p - > a < < 8 ) & 0xfc00 )
| ( ( p - > g < < 2 ) & 0x03e0 )
| ( ( p - > r > > 3 ) & 0x001f ) ;
# ifdef HS_DEBUGGING
if ( * pixels & 0xfc00 )
pixels + + ;
else if ( * pixels & 0x03e0 )
pixels + + ;
else if ( * pixels & 0x001f )
pixels + + ;
else
# endif // HS_DEBUGGING
pixels + + ;
p + + ;
}
}
break ;
case D3DFMT_V8U8 :
{
UInt16 * pixels = ( UInt16 * ) dst ;
hsRGBAColor32 * p = src ;
hsRGBAColor32 * end = src + numPix ;
while ( p < end )
{
* pixels = ( p - > g < < 8 )
| ( p - > r < < 0 ) ;
pixels + + ;
p + + ;
}
}
break ;
case D3DFMT_A8L8 :
{
UInt16 * pixels = ( UInt16 * ) dst ;
int i ;
hsRGBAColor32 * const p = src ;
for ( i = 0 ; i < numPix ; i + + )
pixels [ i ] = ( ( p [ i ] . a & 0xff ) < < 8 ) | ( p [ i ] . r & 0xff ) ;
}
break ;
case D3DFMT_A4R4G4B4 :
{
UInt16 * pixels = ( UInt16 * ) dst ;
int i ;
hsRGBAColor32 * const p = src ;
for ( i = 0 ; i < numPix ; i + + )
{
pixels [ i ] = ( ( ( p [ i ] . r > > 4 ) & 0xf ) < < 8 )
| ( ( ( p [ i ] . g > > 4 ) & 0xf ) < < 4 )
| ( ( ( p [ i ] . b > > 4 ) & 0xf ) )
| ( ( ( p [ i ] . a > > 4 ) & 0xf ) < < 12 ) ;
}
}
break ;
case D3DFMT_A1R5G5B5 :
{
UInt16 * pixels = ( UInt16 * ) dst ;
int i ;
hsRGBAColor32 * const p = src ;
for ( i = 0 ; i < numPix ; i + + )
{
pixels [ i ] = ( ( ( p [ i ] . r > > 3 ) & 0x1f ) < < 10 ) |
( ( ( p [ i ] . g > > 3 ) & 0x1f ) < < 5 ) |
( ( p [ i ] . b > > 3 ) & 0x1f ) | ( ( p [ i ] . a = = 0 ) ? 0 : 0x8000 ) ;
}
}
break ;
case D3DFMT_L8 :
{
UInt8 * pixels = ( UInt8 * ) dst ;
int i ;
hsRGBAColor32 * const p = src ;
for ( i = 0 ; i < numPix ; i + + )
pixels [ i ] = p [ i ] . r ;
}
break ;
case D3DFMT_A8R8G8B8 :
{
UInt32 * pixels = ( UInt32 * ) dst ;
int i ;
hsRGBAColor32 * const p = src ;
for ( i = 0 ; i < numPix ; i + + )
pixels [ i ] = ( ( p [ i ] . a < < 24 ) | ( p [ i ] . r < < 16 ) | ( p [ i ] . g < < 8 ) | p [ i ] . b ) ;
}
break ;
default :
hsAssert ( false , " Unknown texture format selected " ) ;
break ;
}
}
///////////////////////////////////////////////////////////////////////////////
//// View Stuff ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// TestVisibleWorld /////////////////////////////////////////////////////////
// Check if the world space bounds are visible within the current view frustum.
hsBool plDXPipeline : : TestVisibleWorld ( const hsBounds3Ext & wBnd )
{
if ( fView . fCullTreeDirty )
IRefreshCullTree ( ) ;
return fView . fCullTree . BoundsVisible ( wBnd ) ;
}
hsBool plDXPipeline : : TestVisibleWorld ( const plSceneObject * sObj )
{
const plDrawInterface * di = sObj - > GetDrawInterface ( ) ;
if ( ! di )
return false ;
const int numDraw = di - > GetNumDrawables ( ) ;
int i ;
for ( i = 0 ; i < numDraw ; i + + )
{
plDrawableSpans * dr = plDrawableSpans : : ConvertNoRef ( di - > GetDrawable ( i ) ) ;
if ( ! dr )
continue ;
plDISpanIndex & diIndex = dr - > GetDISpans ( di - > GetDrawableMeshIndex ( i ) ) ;
if ( diIndex . IsMatrixOnly ( ) )
continue ;
const int numSpan = diIndex . GetCount ( ) ;
int j ;
for ( j = 0 ; j < numSpan ; j + + )
{
const plSpan * span = dr - > GetSpan ( diIndex [ j ] ) ;
if ( span - > fProps & plSpan : : kPropNoDraw )
continue ;
if ( ! span - > GetVisSet ( ) . Overlap ( plGlobalVisMgr : : Instance ( ) - > GetVisSet ( ) )
| | span - > GetVisSet ( ) . Overlap ( plGlobalVisMgr : : Instance ( ) - > GetVisNot ( ) ) )
continue ;
if ( ! TestVisibleWorld ( span - > fWorldBounds ) )
continue ;
return true ;
}
}
return false ;
}
//// GetViewAxesWorld /////////////////////////////////////////////////////////
// Get the current view direction, up and direction X up.
void plDXPipeline : : GetViewAxesWorld ( hsVector3 axes [ 3 ] /* ac,up,at */ ) const
{
axes [ 0 ] = GetViewAcrossWorld ( ) ;
axes [ 1 ] = GetViewUpWorld ( ) ;
axes [ 2 ] = GetViewDirWorld ( ) ;
}
//// GetFOV ///////////////////////////////////////////////////////////////////
// Get the current FOV in degrees.
void plDXPipeline : : GetFOV ( hsScalar & fovX , hsScalar & fovY ) const
{
fovX = GetViewTransform ( ) . GetFovXDeg ( ) ;
fovY = GetViewTransform ( ) . GetFovYDeg ( ) ;
}
//// SetFOV ///////////////////////////////////////////////////////////////////
// Set the current FOV in degrees. Forces perspective rendering to be true.
void plDXPipeline : : SetFOV ( hsScalar fovX , hsScalar fovY )
{
IGetViewTransform ( ) . SetFovDeg ( fovX , fovY ) ;
IGetViewTransform ( ) . SetPerspective ( true ) ;
}
// Get the orthogonal projection view size in world units (e.g. feet).
void plDXPipeline : : GetSize ( hsScalar & width , hsScalar & height ) const
{
width = GetViewTransform ( ) . GetScreenWidth ( ) ;
height = GetViewTransform ( ) . GetScreenHeight ( ) ;
}
// Set the orthogonal projection view size in world units (e.g. feet).
// Forces projection to orthogonal if it wasn't.
void plDXPipeline : : SetSize ( hsScalar width , hsScalar height )
{
IGetViewTransform ( ) . SetWidth ( width ) ;
IGetViewTransform ( ) . SetHeight ( height ) ;
IGetViewTransform ( ) . SetOrthogonal ( true ) ;
}
//// GetDepth /////////////////////////////////////////////////////////////////
// Get the current hither and yon.
void plDXPipeline : : GetDepth ( hsScalar & hither , hsScalar & yon ) const
{
GetViewTransform ( ) . GetDepth ( hither , yon ) ;
}
//// SetDepth /////////////////////////////////////////////////////////////////
// Set the current hither and yon.
void plDXPipeline : : SetDepth ( hsScalar hither , hsScalar yon )
{
IGetViewTransform ( ) . SetDepth ( hither , yon ) ;
}
//// ISavageYonHack ///////////////////////////////////////////////////////////
// Corrects the yon for the *#(&$*#&$(*& Savage4 chipset (ex. Diamond Stealth
// III S540). Let's just say this card SUCKS.
// Obsolete since we don't support the Savage4 chipset any more.
void plDXPipeline : : ISavageYonHack ( )
{
hsScalar yon = GetViewTransform ( ) . GetYon ( ) ;
if ( ( yon > 128.f - 5.0f ) & & ( yon < 128.f + 1.01f ) )
yon = 128.f + 1.01f ;
else if ( ( yon > 256.f - 10.0f ) & & ( yon < 256.f + 1.02f ) )
yon = 256.f + 1.02f ;
else if ( ( yon > 512.f - 35.0f ) & & ( yon < 512.f + 1.02f ) )
yon = 512.f + 1.02f ;
else if ( ( yon > 1024.f - 120.0f ) & & ( yon < 1024.f + 1.f ) )
yon = 1024.f + 1.f ;
}
//// GetWorldToCamera /////////////////////////////////////////////////////////
// Return current world to camera transform.
const hsMatrix44 & plDXPipeline : : GetWorldToCamera ( ) const
{
return fView . GetWorldToCamera ( ) ;
}
//// GetCameraToWorld /////////////////////////////////////////////////////////
// Return current camera to world transform.
const hsMatrix44 & plDXPipeline : : GetCameraToWorld ( ) const
{
return fView . GetCameraToWorld ( ) ;
}
// IUpdateViewFlags /////////////////////////////////////////////////////////
// Dirty anything cached dependent on the current camera matrix.
void plDXPipeline : : IUpdateViewFlags ( )
{
fView . fCullTreeDirty = true ;
fView . fWorldToCamLeftHanded = fView . GetWorldToCamera ( ) . GetParity ( ) ;
}
//// SetWorldToCamera /////////////////////////////////////////////////////////
// Immediate set of camera transform.
void plDXPipeline : : SetWorldToCamera ( const hsMatrix44 & w2c , const hsMatrix44 & c2w )
{
IGetViewTransform ( ) . SetCameraTransform ( w2c , c2w ) ;
IUpdateViewFlags ( ) ;
IWorldToCameraToD3D ( ) ;
}
// IWorldToCameraToD3D ///////////////////////////////////////////////////////
// Pass the current camera transform through to D3D.
void plDXPipeline : : IWorldToCameraToD3D ( )
{
D3DXMATRIX mat ;
IMatrix44ToD3DMatrix ( mat , fView . GetWorldToCamera ( ) ) ;
fD3DDevice - > SetTransform ( D3DTS_VIEW , & mat ) ;
fView . fXformResetFlags & = ~ fView . kResetCamera ;
fFrame + + ;
}
// SetViewTransform ///////////////////////////////////////////////////////////
// ViewTransform encapsulates everything about the current camera, viewport and
// window necessary to render or convert from world space to pixel space. Doesn't
// include the object dependent local to world transform.
// Set plViewTransform.h
void plDXPipeline : : SetViewTransform ( const plViewTransform & v )
{
fView . fTransform = v ;
if ( ! v . GetScreenWidth ( ) | | ! v . GetScreenHeight ( ) )
{
fView . fTransform . SetScreenSize ( ( UInt16 ) ( fSettings . fOrigWidth ) , ( UInt16 ) ( fSettings . fOrigHeight ) ) ;
}
IUpdateViewFlags ( ) ;
IWorldToCameraToD3D ( ) ;
}
//// GetWorldToLocal //////////////////////////////////////////////////////////
// Return current World to Local transform. Note that this is only meaningful while an
// object is being rendered, so this function is pretty worthless.
const hsMatrix44 & plDXPipeline : : GetWorldToLocal ( ) const
{
return fView . fWorldToLocal ;
}
//// GetLocalToWorld //////////////////////////////////////////////////////////
// Return current Local to World transform. Note that this is only meaningful while an
// object is being rendered, so this function is pretty worthless.
const hsMatrix44 & plDXPipeline : : GetLocalToWorld ( ) const
{
return fView . fLocalToWorld ;
}
//// ISetLocalToWorld /////////////////////////////////////////////////////////
// Record and pass on to D3D the current local to world transform for the object
// about to be rendered.
void plDXPipeline : : ISetLocalToWorld ( const hsMatrix44 & l2w , const hsMatrix44 & w2l )
{
fView . fLocalToWorld = l2w ;
fView . fWorldToLocal = w2l ;
fView . fViewVectorsDirty = true ;
// We keep track of parity for winding order culling.
fView . fLocalToWorldLeftHanded = fView . fLocalToWorld . GetParity ( ) ;
ILocalToWorldToD3D ( ) ;
}
// ILocalToWorldToD3D ///////////////////////////////////////////////////////////
// pass the current local to world tranform on to D3D.
void plDXPipeline : : ILocalToWorldToD3D ( )
{
D3DXMATRIX mat ;
if ( fView . fLocalToWorld . fFlags & hsMatrix44 : : kIsIdent )
fD3DDevice - > SetTransform ( D3DTS_WORLD , & d3dIdentityMatrix ) ;
else
{
IMatrix44ToD3DMatrix ( mat , fView . fLocalToWorld ) ;
fD3DDevice - > SetTransform ( D3DTS_WORLD , & mat ) ;
}
fView . fXformResetFlags & = ~ fView . kResetL2W ;
}
//// IIsViewLeftHanded ////////////////////////////////////////////////////////
// Returns true if the combination of the local2world and world2camera
// matrices is left-handed.
hsBool plDXPipeline : : IIsViewLeftHanded ( )
{
return fView . fTransform . GetOrthogonal ( ) ^ ( fView . fLocalToWorldLeftHanded ^ fView . fWorldToCamLeftHanded ) ? true : false ;
}
//// ScreenToWorldPoint ///////////////////////////////////////////////////////
// Given a screen space pixel position, and a world space distance from the camera, return a
// full world space position. I.e. cast a ray through a screen pixel dist feet, and where
// is it.
void plDXPipeline : : ScreenToWorldPoint ( int n , UInt32 stride , Int32 * scrX , Int32 * scrY , hsScalar dist , UInt32 strideOut , hsPoint3 * worldOut )
{
while ( n - - )
{
hsPoint3 scrP ;
scrP . Set ( float ( * scrX + + ) , float ( * scrY + + ) , float ( dist ) ) ;
* worldOut + + = GetViewTransform ( ) . ScreenToWorld ( scrP ) ;
}
}
// IRefreshCullTree ////////////////////////////////////////////////////////////////////
// The cull tree captures the view frustum and any occluders in the scene into a single
// BSP tree. See plCullTree.h. It must be recomputed any time the camera moves.
void plDXPipeline : : IRefreshCullTree ( )
{
if ( fView . fCullTreeDirty )
{
plProfile_BeginTiming ( DrawOccBuild ) ;
fView . fCullTree . Reset ( ) ;
fView . fCullTree . SetViewPos ( GetViewPositionWorld ( ) ) ;
if ( fCullProxy & & ! IsDebugFlagSet ( plPipeDbg : : kFlagOcclusionSnap ) )
{
fCullProxy - > GetKey ( ) - > UnRefObject ( ) ;
fCullProxy = nil ;
SetDrawableTypeMask ( GetDrawableTypeMask ( ) & ~ plDrawable : : kOccSnapProxy ) ;
}
hsBool doCullSnap = IsDebugFlagSet ( plPipeDbg : : kFlagOcclusionSnap ) & & ! fCullProxy & & ! fSettings . fViewStack . GetCount ( ) ;
if ( doCullSnap )
{
fView . fCullTree . BeginCapturePolys ( ) ;
fView . fCullTree . SetVisualizationYon ( GetViewTransform ( ) . GetYon ( ) ) ;
}
fView . fCullTree . InitFrustum ( GetViewTransform ( ) . GetWorldToNDC ( ) ) ;
fView . fCullTreeDirty = false ;
if ( fView . fCullMaxNodes )
{
int i ;
for ( i = 0 ; i < fCullPolys . GetCount ( ) ; i + + )
{
fView . fCullTree . AddPoly ( * fCullPolys [ i ] ) ;
if ( fView . fCullTree . GetNumNodes ( ) > = fView . fCullMaxNodes )
break ;
}
fCullPolys . SetCount ( 0 ) ;
plProfile_Set ( OccPolyUsed , i ) ;
for ( i = 0 ; i < fCullHoles . GetCount ( ) ; i + + )
{
fView . fCullTree . AddPoly ( * fCullHoles [ i ] ) ;
}
fCullHoles . SetCount ( 0 ) ;
plProfile_Set ( OccNodeUsed , fView . fCullTree . GetNumNodes ( ) ) ;
}
if ( doCullSnap )
{
fView . fCullTree . EndCapturePolys ( ) ;
IMakeOcclusionSnap ( ) ;
}
plProfile_EndTiming ( DrawOccBuild ) ;
}
}
// IMakeOcclusionSnap /////////////////////////////////////////////////////////////////////
// Debugging visualization tool only. Takes a snapshot of the current occlusion
// BSP tree and renders it until told to stop.
void plDXPipeline : : IMakeOcclusionSnap ( )
{
hsTArray < hsPoint3 > & pos = fView . fCullTree . GetCaptureVerts ( ) ;
hsTArray < hsVector3 > & norm = fView . fCullTree . GetCaptureNorms ( ) ;
hsTArray < hsColorRGBA > & color = fView . fCullTree . GetCaptureColors ( ) ;
hsTArray < UInt16 > & tris = fView . fCullTree . GetCaptureTris ( ) ;
if ( tris . GetCount ( ) )
{
hsMatrix44 ident ;
ident . Reset ( ) ;
hsGMaterial * mat = TRACKED_NEW hsGMaterial ;
hsgResMgr : : ResMgr ( ) - > NewKey ( " OcclusionSnapMat " , mat , plLocation : : kGlobalFixedLoc ) ;
plLayer * lay = mat - > MakeBaseLayer ( ) ;
lay - > SetZFlags ( hsGMatState : : kZNoZWrite ) ;
lay - > SetPreshadeColor ( hsColorRGBA ( ) . Set ( 1.f , 0.5f , 0.5f , 1.f ) ) ;
lay - > SetRuntimeColor ( hsColorRGBA ( ) . Set ( 1.f , 0.5f , 0.5f , 1.f ) ) ;
lay - > SetAmbientColor ( hsColorRGBA ( ) . Set ( 0 , 0 , 0 , 1.f ) ) ;
lay - > SetOpacity ( 0.5f ) ;
lay - > SetBlendFlags ( lay - > GetBlendFlags ( ) | hsGMatState : : kBlendAlpha ) ;
fCullProxy = plDrawableGenerator : : GenerateDrawable ( pos . GetCount ( ) ,
pos . AcquireArray ( ) ,
norm . AcquireArray ( ) ,
nil ,
0 ,
color . AcquireArray ( ) ,
true ,
nil ,
tris . GetCount ( ) ,
tris . AcquireArray ( ) ,
mat ,
ident ,
true ,
nil ,
nil ) ;
if ( fCullProxy )
{
fCullProxy - > GetKey ( ) - > RefObject ( ) ;
fCullProxy - > SetType ( plDrawable : : kOccSnapProxy ) ;
SetDrawableTypeMask ( GetDrawableTypeMask ( ) | plDrawable : : kOccSnapProxy ) ;
fCullProxy - > PrepForRender ( this ) ;
}
}
fView . fCullTree . ReleaseCapture ( ) ;
}
// SubmitOccluders /////////////////////////////////////////////////////////////
// Add the input polys into the list of polys from which to generate the cull tree.
hsBool plDXPipeline : : SubmitOccluders ( const hsTArray < const plCullPoly * > & polyList )
{
fCullPolys . SetCount ( 0 ) ;
fCullHoles . SetCount ( 0 ) ;
int i ;
for ( i = 0 ; i < polyList . GetCount ( ) ; i + + )
{
if ( polyList [ i ] - > IsHole ( ) )
fCullHoles . Append ( polyList [ i ] ) ;
else
fCullPolys . Append ( polyList [ i ] ) ;
}
fView . fCullTreeDirty = true ;
return true ;
}
//// RefreshScreenMatrices ////////////////////////////////////////////////////
// Force a refresh of cached state when the projection matrix changes.
void plDXPipeline : : RefreshScreenMatrices ( )
{
fView . fCullTreeDirty = true ;
IProjectionMatrixToD3D ( ) ;
}
//// RefreshMatrices //////////////////////////////////////////////////////////
// Just a wrapper
void plDXPipeline : : RefreshMatrices ( )
{
RefreshScreenMatrices ( ) ;
}
///////////////////////////////////////////////////////////////////////////////
//// Overrides ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// PushOverrideMaterial /////////////////////////////////////////////////////
// Push a material to be used instead of the material associated with objects
// for rendering.
// Must be matched with a PopOverrideMaterial.
hsGMaterial * plDXPipeline : : PushOverrideMaterial ( hsGMaterial * mat )
{
hsGMaterial * ret = GetOverrideMaterial ( ) ;
hsRefCnt_SafeRef ( mat ) ;
fOverrideMat . Push ( mat ) ;
fForceMatHandle = true ;
return ret ;
}
//// PopOverrideMaterial //////////////////////////////////////////////////////
// Stop overriding with the current override material.
// Must match a preceding PushOverrideMaterial.
void plDXPipeline : : PopOverrideMaterial ( hsGMaterial * restore )
{
hsGMaterial * pop = fOverrideMat . Pop ( ) ;
hsRefCnt_SafeUnRef ( pop ) ;
if ( fCurrMaterial = = pop )
{
fForceMatHandle = true ;
}
}
//// GetOverrideMaterial //////////////////////////////////////////////////////
// Return the current override material, or nil if there isn't any.
hsGMaterial * plDXPipeline : : GetOverrideMaterial ( ) const
{
return fOverrideMat . GetCount ( ) ? fOverrideMat . Peek ( ) : nil ;
}
//// GetMaterialOverrideOn ////////////////////////////////////////////////////
// Return the current bits set to be always on for the given category (e.g. ZFlags).
UInt32 plDXPipeline : : GetMaterialOverrideOn ( hsGMatState : : StateIdx category ) const
{
return fMatOverOn . Value ( category ) ;
}
//// GetMaterialOverrideOff ///////////////////////////////////////////////////
// Return the current bits set to be always off for the given category (e.g. ZFlags).
UInt32 plDXPipeline : : GetMaterialOverrideOff ( hsGMatState : : StateIdx category ) const
{
return fMatOverOff . Value ( category ) ;
}
//// PushMaterialOverride /////////////////////////////////////////////////////
// Force material state bits on or off. If you use this, save the return value
// as input to PopMaterialOverride, to restore previous values.
hsGMatState plDXPipeline : : PushMaterialOverride ( const hsGMatState & state , hsBool on )
{
hsGMatState ret = GetMaterialOverride ( on ) ;
if ( on )
{
fMatOverOn | = state ;
fMatOverOff - = state ;
}
else
{
fMatOverOff | = state ;
fMatOverOn - = state ;
}
fForceMatHandle = true ;
return ret ;
}
// PushMaterialOverride ///////////////////////////////////////////////////////
// Force material state bits on or off. If you use this, save the return value
// as input to PopMaterialOverride, to restore previous values.
// This version just sets for one category (e.g. Z flags).
hsGMatState plDXPipeline : : PushMaterialOverride ( hsGMatState : : StateIdx cat , UInt32 which , hsBool on )
{
hsGMatState ret = GetMaterialOverride ( on ) ;
if ( on )
{
fMatOverOn [ cat ] | = which ;
fMatOverOff [ cat ] & = ~ which ;
}
else
{
fMatOverOn [ cat ] & = ~ which ;
fMatOverOff [ cat ] | = which ;
}
fForceMatHandle = true ;
return ret ;
}
//// PopMaterialOverride //////////////////////////////////////////////////////
// Restore the previous settings returned from the matching PushMaterialOverride.
void plDXPipeline : : PopMaterialOverride ( const hsGMatState & restore , hsBool on )
{
if ( on )
{
fMatOverOn = restore ;
fMatOverOff . Clear ( restore ) ;
}
else
{
fMatOverOff = restore ;
fMatOverOn . Clear ( restore ) ;
}
fForceMatHandle = true ;
}
//// GetMaterialOverride //////////////////////////////////////////////////////
// Return the current material state bits force to on or off, depending on input <on>.
const hsGMatState & plDXPipeline : : GetMaterialOverride ( hsBool on ) const
{
return on ? fMatOverOn : fMatOverOff ;
}
//// PushColorOverride //////////////////////////////////////////////////
// Obsolete and unused.
hsColorOverride plDXPipeline : : PushColorOverride ( const hsColorOverride & over )
{
hsColorOverride ret = GetColorOverride ( ) ;
PopColorOverride ( over ) ;
return ret ;
}
// PopColorOverride ////////////////////////////////////////////////////////
// Obsolete and unused.
void plDXPipeline : : PopColorOverride ( const hsColorOverride & restore )
{
return ;
/*
hsColorOverride cpy = restore ;
if ( ! ( cpy . fFlags & hsColorOverride : : kModAlpha ) )
cpy . fColor . a = 1.f ;
if ( ! ( cpy . fFlags & ( hsColorOverride : : kModAlpha | hsColorOverride : : kModColor ) ) )
fDev - > SetColorNormal ( ) ;
else
fDev - > SetColorOverride ( cpy . fColor , ! ( cpy . fFlags & hsColorOverride : : kModColor ) ) ;
*/
}
//// GetColorOverride /////////////////////////////////////////////////////////
// Obsolete and unused.
const hsColorOverride & plDXPipeline : : GetColorOverride ( ) const
{
static hsColorOverride ret ;
return ret ;
/* ret.fFlags = hsColorOverride::kNone;
if ( fDev - > GetDebugFlags ( ) & hsG3DDevice : : kDeviceColor )
ret . fFlags | = hsColorOverride : : kModColor ;
if ( fDev - > GetDebugFlags ( ) & hsG3DDevice : : kDeviceAlpha )
ret . fFlags | = hsColorOverride : : kModAlpha ;
ret . fColor = fDev - > GetColorOverride ( ) ;
*/
return ret ;
}
///////////////////////////////////////////////////////////////////////////////
//// Transforms ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// IMatrix44ToD3DMatrix /////////////////////////////////////////////////////
// Make a D3DXMATRIX matching the input plasma matrix. Mostly a transpose.
D3DXMATRIX & plDXPipeline : : IMatrix44ToD3DMatrix ( D3DXMATRIX & dst , const hsMatrix44 & src )
{
if ( src . fFlags & hsMatrix44 : : kIsIdent )
{
dst = d3dIdentityMatrix ;
}
else
{
dst ( 0 , 0 ) = src . fMap [ 0 ] [ 0 ] ;
dst ( 1 , 0 ) = src . fMap [ 0 ] [ 1 ] ;
dst ( 2 , 0 ) = src . fMap [ 0 ] [ 2 ] ;
dst ( 3 , 0 ) = src . fMap [ 0 ] [ 3 ] ;
dst ( 0 , 1 ) = src . fMap [ 1 ] [ 0 ] ;
dst ( 1 , 1 ) = src . fMap [ 1 ] [ 1 ] ;
dst ( 2 , 1 ) = src . fMap [ 1 ] [ 2 ] ;
dst ( 3 , 1 ) = src . fMap [ 1 ] [ 3 ] ;
dst ( 0 , 2 ) = src . fMap [ 2 ] [ 0 ] ;
dst ( 1 , 2 ) = src . fMap [ 2 ] [ 1 ] ;
dst ( 2 , 2 ) = src . fMap [ 2 ] [ 2 ] ;
dst ( 3 , 2 ) = src . fMap [ 2 ] [ 3 ] ;
dst ( 0 , 3 ) = src . fMap [ 3 ] [ 0 ] ;
dst ( 1 , 3 ) = src . fMap [ 3 ] [ 1 ] ;
dst ( 2 , 3 ) = src . fMap [ 3 ] [ 2 ] ;
dst ( 3 , 3 ) = src . fMap [ 3 ] [ 3 ] ;
}
return dst ;
}
/////////////////////////////////////////////////////////
// IGetCameraToNDC /////////////////////////////////////////////
// Get the camera to NDC transform. This may be adjusted to create
// a Z bias towards the camera for cases where the D3D Z bias fails us.
hsMatrix44 plDXPipeline : : IGetCameraToNDC ( )
{
hsMatrix44 cam2ndc = GetViewTransform ( ) . GetCameraToNDC ( ) ;
if ( fView . IsPerspective ( ) )
{
// Want to scale down W and offset in Z without
// changing values of x/w, y/w. This is just
// minimal math for
// Mproj' * p = Mscaletrans * Mproj * p
// where Mscaletrans =
// [ s 0 0 0 ]
// [ 0 s 0 0 ]
// [ 0 0 s 0 ]
// [ 0 0 t s ]
// Resulting matrix Mproj' is not exactly "Fog Friendly",
// but is close enough.
// Resulting point is [sx, sy, sz + tw, sw] and after divide
// is [x/w, y/w, z/w + t/s, 1/sw]
if ( fSettings . fD3DCaps & kCapsWBuffer )
{
// W-buffering is only true w-buffering on 3dfx cards. On everything else,
// they REALLY base it off the Z value. So we want to scale (but NOT translate)
// the Z...
// Note: the base value for perspLayerScale should be 0.001 for w-buffering,
// not the normal 0.00001
float scale = 1.f - float ( fCurrRenderLayer ) * fTweaks . fPerspLayerScale ;
cam2ndc . fMap [ 0 ] [ 0 ] * = scale ;
cam2ndc . fMap [ 1 ] [ 1 ] * = scale ;
cam2ndc . fMap [ 2 ] [ 2 ] * = scale ;
cam2ndc . fMap [ 3 ] [ 2 ] * = scale ;
}
else
{
// Z-buffering, so do it the traditional way
float scale = 1.f - float ( fCurrRenderLayer ) * fTweaks . fPerspLayerScale ;
// scale = -1.f;
float zTrans = - scale * float ( fCurrRenderLayer ) * fTweaks . fPerspLayerTrans ;
cam2ndc . fMap [ 0 ] [ 0 ] * = scale ;
cam2ndc . fMap [ 1 ] [ 1 ] * = scale ;
cam2ndc . fMap [ 2 ] [ 2 ] * = scale ;
cam2ndc . fMap [ 2 ] [ 2 ] + = zTrans * cam2ndc . fMap [ 3 ] [ 2 ] ;
cam2ndc . fMap [ 3 ] [ 2 ] * = scale ;
}
}
else
{
plConst ( float ) kZTrans = - 1.e-4 f ;
cam2ndc . fMap [ 2 ] [ 3 ] + = kZTrans * fCurrRenderLayer ;
}
return cam2ndc ;
}
// IProjectionMatrixToD3D //////////////////////////////////////////////////////////
// Send the current camera to NDC transform to D3D.
void plDXPipeline : : IProjectionMatrixToD3D ( )
{
D3DXMATRIX matProjection ;
IMatrix44ToD3DMatrix ( matProjection , IGetCameraToNDC ( ) ) ;
fD3DDevice - > SetTransform ( D3DTS_PROJECTION , & matProjection ) ;
fView . fXformResetFlags & = ~ fView . kResetProjection ;
}
//// ISetCullMode /////////////////////////////////////////////////////////////
// Tests and sets the current winding order cull mode (CW, CCW, or none).
// Will reverse the cull mode as necessary for left handed camera or local to world
// transforms.
void plDXPipeline : : ISetCullMode ( hsBool flip )
{
D3DCULL newCull = D3DCULL_NONE ;
if ( ! ( fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscTwoSided ) )
newCull = ! IIsViewLeftHanded ( ) ^ ! flip ? D3DCULL_CW : D3DCULL_CCW ;
if ( newCull ! = fCurrCullMode )
{
fCurrCullMode = newCull ;
fD3DDevice - > SetRenderState ( D3DRS_CULLMODE , fCurrCullMode ) ;
}
}
//// ITransformsToD3D //////////////////////////////////////////////////////////
// Refreshes all transforms. Useful after popping renderTargets :)
void plDXPipeline : : ITransformsToD3D ( )
{
hsBool resetCullMode = fView . fXformResetFlags & ( fView . kResetCamera | fView . kResetL2W ) ;
if ( fView . fXformResetFlags & fView . kResetCamera )
IWorldToCameraToD3D ( ) ;
if ( fView . fXformResetFlags & fView . kResetL2W )
ILocalToWorldToD3D ( ) ;
if ( fView . fXformResetFlags & fView . kResetProjection )
IProjectionMatrixToD3D ( ) ;
}
// ISetupVertexBufferRef /////////////////////////////////////////////////////////
// Initialize input vertex buffer ref according to source.
void plDXPipeline : : ISetupVertexBufferRef ( plGBufferGroup * owner , UInt32 idx , plDXVertexBufferRef * vRef )
{
// Initialize to nil, in case something goes wrong.
vRef - > fD3DBuffer = nil ;
UInt8 format = owner - > GetVertexFormat ( ) ;
// All indexed skinning is currently done on CPU, so the source data
// will have indices, but we strip them out for the D3D buffer.
if ( format & plGBufferGroup : : kSkinIndices )
{
format & = ~ ( plGBufferGroup : : kSkinWeightMask | plGBufferGroup : : kSkinIndices ) ;
format | = plGBufferGroup : : kSkinNoWeights ; // Should do nothing, but just in case...
vRef - > SetSkinned ( true ) ;
vRef - > SetVolatile ( true ) ;
}
UInt32 vertSize = IGetBufferFormatSize ( format ) ; // vertex stride
UInt32 numVerts = owner - > GetVertBufferCount ( idx ) ;
vRef - > fDevice = fD3DDevice ;
vRef - > fOwner = owner ;
vRef - > fCount = numVerts ;
vRef - > fVertexSize = vertSize ;
vRef - > fFormat = format ;
vRef - > fRefTime = 0 ;
vRef - > SetDirty ( true ) ;
vRef - > SetRebuiltSinceUsed ( true ) ;
vRef - > fData = nil ;
vRef - > SetVolatile ( vRef - > Volatile ( ) | | owner - > AreVertsVolatile ( ) ) ;
vRef - > fIndex = idx ;
owner - > SetVertexBufferRef ( idx , vRef ) ;
hsRefCnt_SafeUnRef ( vRef ) ;
}
// ICheckStaticVertexBuffer ///////////////////////////////////////////////////////////////////////
// Ensure a static vertex buffer has any D3D resources necessary for rendering created and filled
// with proper vertex data.
void plDXPipeline : : ICheckStaticVertexBuffer ( plDXVertexBufferRef * vRef , plGBufferGroup * owner , UInt32 idx )
{
hsAssert ( ! vRef - > Volatile ( ) , " Creating a managed vertex buffer for a volatile buffer ref " ) ;
if ( ! vRef - > fD3DBuffer )
{
// Okay, haven't done this one.
DWORD fvfFormat = IGetBufferD3DFormat ( vRef - > fFormat ) ;
D3DPOOL poolType = D3DPOOL_MANAGED ;
// DWORD usage = D3DUSAGE_WRITEONLY;
DWORD usage = 0 ;
const int numVerts = vRef - > fCount ;
const int vertSize = vRef - > fVertexSize ;
fManagedAlloced = true ;
if ( FAILED ( fD3DDevice - > CreateVertexBuffer ( numVerts * vertSize ,
usage ,
fvfFormat ,
poolType ,
& vRef - > fD3DBuffer , NULL ) ) )
{
hsAssert ( false , " CreateVertexBuffer() call failed! " ) ;
vRef - > fD3DBuffer = nil ;
return ;
}
PROFILE_POOL_MEM ( poolType , numVerts * vertSize , true , " VtxBuff " ) ;
// Record that we've allocated this into managed memory, in case we're
// fighting that NVidia driver bug. Search for OSVERSION for mor info.
AllocManagedVertex ( numVerts * vertSize ) ;
// Fill in the vertex data.
IFillStaticVertexBufferRef ( vRef , owner , idx ) ;
// This is currently a no op, but this would let the buffer know it can
// unload the system memory copy, since we have a managed version now.
owner - > PurgeVertBuffer ( idx ) ;
}
}
// IFillStaticVertexBufferRef //////////////////////////////////////////////////
// BufferRef is set up, just copy the data in.
// This is uglied up hugely by the insane non-interleaved data case with cells
// and whatever else.
void plDXPipeline : : IFillStaticVertexBufferRef ( plDXVertexBufferRef * ref , plGBufferGroup * group , UInt32 idx )
{
IDirect3DVertexBuffer9 * vertexBuff = ref - > fD3DBuffer ;
if ( ! vertexBuff )
{
// We most likely already warned about this earlier, best to just quietly return now
return ;
}
const UInt32 vertSize = ref - > fVertexSize ;
const UInt32 vertStart = group - > GetVertBufferStart ( idx ) * vertSize ;
const UInt32 size = group - > GetVertBufferEnd ( idx ) * vertSize - vertStart ;
if ( ! size )
return ;
/// Lock the buffer
UInt8 * ptr ;
if ( FAILED ( vertexBuff - > Lock ( vertStart , size , ( void * * ) & ptr , group - > AreVertsVolatile ( ) ? D3DLOCK_DISCARD : 0 ) ) )
{
hsAssert ( false , " Failed to lock vertex buffer for writing " ) ;
}
if ( ref - > fData )
{
memcpy ( ptr , ref - > fData + vertStart , size ) ;
}
else
{
hsAssert ( 0 = = vertStart , " Offsets on non-interleaved data not supported " ) ;
hsAssert ( group - > GetVertBufferCount ( idx ) * vertSize = = size , " Trailing dead space on non-interleaved data not supported " ) ;
const UInt32 vertSmallSize = group - > GetVertexLiteStride ( ) - sizeof ( hsPoint3 ) * 2 ;
UInt8 * srcVPtr = group - > GetVertBufferData ( idx ) ;
plGBufferColor * const srcCPtr = group - > GetColorBufferData ( idx ) ;
const int numCells = group - > GetNumCells ( idx ) ;
int i ;
for ( i = 0 ; i < numCells ; i + + )
{
plGBufferCell * cell = group - > GetCell ( idx , i ) ;
if ( cell - > fColorStart = = ( UInt32 ) - 1 )
{
/// Interleaved, do straight copy
memcpy ( ptr , srcVPtr + cell - > fVtxStart , cell - > fLength * vertSize ) ;
ptr + = cell - > fLength * vertSize ;
}
else
{
/// Separated, gotta interleave
UInt8 * tempVPtr = srcVPtr + cell - > fVtxStart ;
plGBufferColor * tempCPtr = srcCPtr + cell - > fColorStart ;
int j ;
for ( j = 0 ; j < cell - > fLength ; j + + )
{
memcpy ( ptr , tempVPtr , sizeof ( hsPoint3 ) * 2 ) ;
ptr + = sizeof ( hsPoint3 ) * 2 ;
tempVPtr + = sizeof ( hsPoint3 ) * 2 ;
memcpy ( ptr , & tempCPtr - > fDiffuse , sizeof ( UInt32 ) ) ;
ptr + = sizeof ( UInt32 ) ;
memcpy ( ptr , & tempCPtr - > fSpecular , sizeof ( UInt32 ) ) ;
ptr + = sizeof ( UInt32 ) ;
memcpy ( ptr , tempVPtr , vertSmallSize ) ;
ptr + = vertSmallSize ;
tempVPtr + = vertSmallSize ;
tempCPtr + + ;
}
}
}
}
/// Unlock and clean up
vertexBuff - > Unlock ( ) ;
ref - > SetRebuiltSinceUsed ( true ) ;
ref - > SetDirty ( false ) ;
}
// OpenAccess ////////////////////////////////////////////////////////////////////////////////////////
// Lock the managed buffer and setup the accessSpan to point into the buffers data.
hsBool plDXPipeline : : OpenAccess ( plAccessSpan & dst , plDrawableSpans * drawable , const plVertexSpan * span , hsBool readOnly )
{
plGBufferGroup * grp = drawable - > GetBufferGroup ( span - > fGroupIdx ) ;
hsAssert ( ! grp - > AreVertsVolatile ( ) , " Don't ask for D3DBuffer data on a volatile buffer " ) ;
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) grp - > GetVertexBufferRef ( span - > fVBufferIdx ) ;
if ( ! vRef )
{
dst . SetType ( plAccessSpan : : kUndefined ) ;
return false ;
}
IDirect3DVertexBuffer9 * vertexBuff = vRef - > fD3DBuffer ;
if ( ! vertexBuff )
{
dst . SetType ( plAccessSpan : : kUndefined ) ;
return false ;
}
const UInt32 stride = vRef - > fVertexSize ;
const UInt32 vertStart = span - > fVStartIdx * stride ;
const UInt32 size = span - > fVLength * stride ;
if ( ! size )
{
dst . SetType ( plAccessSpan : : kUndefined ) ;
return false ;
}
DWORD lockFlags = readOnly ? D3DLOCK_READONLY : 0 ;
UInt8 * ptr ;
if ( FAILED ( vertexBuff - > Lock ( vertStart , size , ( void * * ) & ptr , lockFlags ) ) )
{
hsAssert ( false , " Failed to lock vertex buffer for writing " ) ;
dst . SetType ( plAccessSpan : : kUndefined ) ;
return false ;
}
plAccessVtxSpan & acc = dst . AccessVtx ( ) ;
acc . SetVertCount ( ( UInt16 ) ( span - > fVLength ) ) ;
Int32 offset = ( - ( Int32 ) ( span - > fVStartIdx ) ) * ( ( Int32 ) stride ) ;
acc . PositionStream ( ptr , ( UInt16 ) stride , offset ) ;
ptr + = sizeof ( hsPoint3 ) ;
int numWgts = grp - > GetNumWeights ( ) ;
if ( numWgts )
{
acc . SetNumWeights ( numWgts ) ;
acc . WeightStream ( ptr , ( UInt16 ) stride , offset ) ;
ptr + = numWgts * sizeof ( hsScalar ) ;
if ( grp - > GetVertexFormat ( ) & plGBufferGroup : : kSkinIndices )
{
acc . WgtIndexStream ( ptr , ( UInt16 ) stride , offset ) ;
ptr + = sizeof ( UInt32 ) ;
}
else
{
acc . WgtIndexStream ( nil , 0 , offset ) ;
}
}
else
{
acc . SetNumWeights ( 0 ) ;
}
acc . NormalStream ( ptr , ( UInt16 ) stride , offset ) ;
ptr + = sizeof ( hsVector3 ) ;
acc . DiffuseStream ( ptr , ( UInt16 ) stride , offset ) ;
ptr + = sizeof ( UInt32 ) ;
acc . SpecularStream ( ptr , ( UInt16 ) stride , offset ) ;
ptr + = sizeof ( UInt32 ) ;
acc . UVWStream ( ptr , ( UInt16 ) stride , offset ) ;
acc . SetNumUVWs ( grp - > GetNumUVs ( ) ) ;
acc . SetVtxDeviceRef ( vRef ) ;
return true ;
}
// CloseAccess /////////////////////////////////////////////////////////////////////
// Unlock the buffer, invalidating the accessSpan.
hsBool plDXPipeline : : CloseAccess ( plAccessSpan & dst )
{
if ( ! dst . HasAccessVtx ( ) )
return false ;
plAccessVtxSpan & acc = dst . AccessVtx ( ) ;
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) acc . GetVtxDeviceRef ( ) ;
if ( ! vRef )
return false ;
IDirect3DVertexBuffer9 * vertexBuff = vRef - > fD3DBuffer ;
if ( ! vertexBuff )
return false ;
vertexBuff - > Unlock ( ) ;
return true ;
}
// CheckVertexBufferRef /////////////////////////////////////////////////////
// Make sure the buffer group has a valid buffer ref and that it is up to date.
void plDXPipeline : : CheckVertexBufferRef ( plGBufferGroup * owner , UInt32 idx )
{
// First, do we have a device ref at this index?
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) owner - > GetVertexBufferRef ( idx ) ;
// If not
if ( ! vRef )
{
// Make the blank ref
vRef = TRACKED_NEW plDXVertexBufferRef ;
ISetupVertexBufferRef ( owner , idx , vRef ) ;
}
if ( ! vRef - > IsLinked ( ) )
vRef - > Link ( & fVtxBuffRefList ) ;
// One way or another, we now have a vbufferref[idx] in owner.
// Now, does it need to be (re)filled?
// If the owner is volatile, then we hold off. It might not
// be visible, and we might need to refill it again if we
// have an overrun of our dynamic D3D buffer.
if ( ! vRef - > Volatile ( ) )
{
if ( fAllocUnManaged )
return ;
// If it's a static buffer, allocate a D3D vertex buffer for it. Otherwise, it'll
// be sharing the global D3D dynamic buffer, and marked as volatile.
ICheckStaticVertexBuffer ( vRef , owner , idx ) ;
// Might want to remove this assert, and replace it with a dirty check if
// we have static buffers that change very seldom rather than never.
hsAssert ( ! vRef - > IsDirty ( ) , " Non-volatile vertex buffers should never get dirty " ) ;
}
else
{
// Make sure we're going to be ready to fill it.
if ( ! vRef - > fData & & ( vRef - > fFormat ! = owner - > GetVertexFormat ( ) ) )
{
vRef - > fData = TRACKED_NEW UInt8 [ vRef - > fCount * vRef - > fVertexSize ] ;
}
}
}
// CheckIndexBufferRef /////////////////////////////////////////////////////
// Make sure the buffer group has an index buffer ref and that its data is current.
void plDXPipeline : : CheckIndexBufferRef ( plGBufferGroup * owner , UInt32 idx )
{
plDXIndexBufferRef * iRef = ( plDXIndexBufferRef * ) owner - > GetIndexBufferRef ( idx ) ;
if ( ! iRef )
{
// Create one from scratch.
iRef = TRACKED_NEW plDXIndexBufferRef ;
ISetupIndexBufferRef ( owner , idx , iRef ) ;
}
if ( ! iRef - > IsLinked ( ) )
iRef - > Link ( & fIdxBuffRefList ) ;
// Make sure it has all D3D resources created.
ICheckIndexBuffer ( iRef ) ;
// If it's dirty, refill it.
if ( iRef - > IsDirty ( ) )
IFillIndexBufferRef ( iRef , owner , idx ) ;
}
// IFillIndexBufferRef ////////////////////////////////////////////////////////////
// Refresh the D3D index buffer from the plasma index buffer.
void plDXPipeline : : IFillIndexBufferRef ( plDXIndexBufferRef * iRef , plGBufferGroup * owner , UInt32 idx )
{
UInt32 startIdx = owner - > GetIndexBufferStart ( idx ) ;
UInt32 size = ( owner - > GetIndexBufferEnd ( idx ) - startIdx ) * sizeof ( UInt16 ) ;
if ( ! size )
return ;
DWORD lockFlags = iRef - > Volatile ( ) ? D3DLOCK_DISCARD : 0 ;
UInt16 * destPtr = nil ;
if ( FAILED ( iRef - > fD3DBuffer - > Lock ( startIdx * sizeof ( UInt16 ) , size , ( void * * ) & destPtr , lockFlags ) ) )
{
hsAssert ( false , " Cannot lock index buffer for writing " ) ;
return ;
}
memcpy ( destPtr , owner - > GetIndexBufferData ( idx ) + startIdx , size ) ;
iRef - > fD3DBuffer - > Unlock ( ) ;
iRef - > SetDirty ( false ) ;
}
// ICheckIndexBuffer ////////////////////////////////////////////////////////
// Make sure index buffer ref has any D3D resources it needs.
void plDXPipeline : : ICheckIndexBuffer ( plDXIndexBufferRef * iRef )
{
if ( ! iRef - > fD3DBuffer & & iRef - > fCount )
{
D3DPOOL poolType = fAllocUnManaged ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED ;
DWORD usage = D3DUSAGE_WRITEONLY ;
iRef - > SetVolatile ( false ) ;
if ( FAILED ( fD3DDevice - > CreateIndexBuffer ( sizeof ( UInt16 ) * iRef - > fCount ,
usage ,
D3DFMT_INDEX16 ,
poolType ,
& iRef - > fD3DBuffer , NULL ) ) )
{
hsAssert ( false , " CreateIndexBuffer() call failed! " ) ;
iRef - > fD3DBuffer = nil ;
return ;
}
PROFILE_POOL_MEM ( poolType , sizeof ( UInt16 ) * iRef - > fCount , true , " IndexBuff " ) ;
iRef - > fPoolType = poolType ;
iRef - > SetDirty ( true ) ;
iRef - > SetRebuiltSinceUsed ( true ) ;
}
}
// ISetupIndexBufferRef ////////////////////////////////////////////////////////////////
// Initialize the index buffer ref, but don't create anything for it.
void plDXPipeline : : ISetupIndexBufferRef ( plGBufferGroup * owner , UInt32 idx , plDXIndexBufferRef * iRef )
{
UInt32 numIndices = owner - > GetIndexBufferCount ( idx ) ;
iRef - > fCount = numIndices ;
iRef - > fOwner = owner ;
iRef - > fIndex = idx ;
iRef - > fRefTime = 0 ;
iRef - > SetDirty ( true ) ;
iRef - > SetRebuiltSinceUsed ( true ) ;
owner - > SetIndexBufferRef ( idx , iRef ) ;
hsRefCnt_SafeUnRef ( iRef ) ;
iRef - > SetVolatile ( owner - > AreIdxVolatile ( ) ) ;
}
//// ISoftwareVertexBlend ///////////////////////////////////////////////////////
// Emulate matrix palette operations in software. The big difference between the hardware
// and software versions is we only want to lock the vertex buffer once and blend all the
// verts we're going to in software, so the vertex blend happens once for an entire drawable.
// In hardware, we want the opposite, to break it into managable chunks, manageable meaning
// few enough matrices to fit into hardware registers. So for hardware version, we set up
// our palette, draw a span or few, setup our matrix palette with new matrices, draw, repeat.
hsBool plDXPipeline : : ISoftwareVertexBlend ( plDrawableSpans * drawable , const hsTArray < Int16 > & visList )
{
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoSkinning ) )
return true ;
if ( drawable - > GetSkinTime ( ) = = fRenderCnt )
return true ;
const hsBitVector & blendBits = drawable - > GetBlendingSpanVector ( ) ;
if ( drawable - > GetBlendingSpanVector ( ) . Empty ( ) )
{
// This sucker doesn't have any skinning spans anyway. Just return
drawable - > SetSkinTime ( fRenderCnt ) ;
return true ;
}
plProfile_BeginTiming ( Skin ) ;
// lock the data buffer
// First, figure out which buffers we need to blend.
const int kMaxBufferGroups = 20 ;
const int kMaxVertexBuffers = 20 ;
static char blendBuffers [ kMaxBufferGroups ] [ kMaxVertexBuffers ] ;
memset ( blendBuffers , 0 , kMaxBufferGroups * kMaxVertexBuffers * sizeof ( * * blendBuffers ) ) ;
hsAssert ( kMaxBufferGroups > = drawable - > GetNumBufferGroups ( ) , " Bigger than we counted on num groups skin. " ) ;
const hsTArray < plSpan * > & spans = drawable - > GetSpanArray ( ) ;
int i ;
for ( i = 0 ; i < visList . GetCount ( ) ; i + + )
{
if ( blendBits . IsBitSet ( visList [ i ] ) )
{
const plVertexSpan & vSpan = * ( plVertexSpan * ) spans [ visList [ i ] ] ;
hsAssert ( kMaxVertexBuffers > vSpan . fVBufferIdx , " Bigger than we counted on num buffers skin. " ) ;
blendBuffers [ vSpan . fGroupIdx ] [ vSpan . fVBufferIdx ] = 1 ;
drawable - > SetBlendingSpanVectorBit ( visList [ i ] , false ) ;
}
}
// Now go through each of the group/buffer (= a real vertex buffer) pairs we found,
// and blend into it. We'll lock the buffer once, and then for each span that
// uses it, set the matrix palette and and then do the blend for that span.
// When we've done all the spans for a group/buffer, we unlock it and move on.
int j ;
for ( i = 0 ; i < kMaxBufferGroups ; i + + )
{
for ( j = 0 ; j < kMaxVertexBuffers ; j + + )
{
if ( blendBuffers [ i ] [ j ] )
{
// Found one. Do the lock.
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) drawable - > GetVertexRef ( i , j ) ;
hsAssert ( vRef - > fData , " Going into skinning with no place to put results! " ) ;
UInt8 * destPtr = vRef - > fData ;
int k ;
for ( k = 0 ; k < visList . GetCount ( ) ; k + + )
{
const plIcicle & span = * ( plIcicle * ) spans [ visList [ k ] ] ;
if ( ( span . fGroupIdx = = i ) & & ( span . fVBufferIdx = = j ) )
{
plProfile_Inc ( NumSkin ) ;
hsMatrix44 * matrixPalette = drawable - > GetMatrixPalette ( span . fBaseMatrix ) ;
matrixPalette [ 0 ] = span . fLocalToWorld ;
UInt8 * ptr = vRef - > fOwner - > GetVertBufferData ( vRef - > fIndex ) ;
ptr + = span . fVStartIdx * vRef - > fOwner - > GetVertexSize ( ) ;
IBlendVertsIntoBuffer ( ( plSpan * ) & span ,
matrixPalette , span . fNumMatrices ,
ptr ,
vRef - > fOwner - > GetVertexFormat ( ) ,
vRef - > fOwner - > GetVertexSize ( ) ,
destPtr + span . fVStartIdx * vRef - > fVertexSize ,
vRef - > fVertexSize ,
span . fVLength ,
span . fLocalUVWChans ) ;
vRef - > SetDirty ( true ) ;
}
}
// Unlock and move on.
}
}
}
plProfile_EndTiming ( Skin ) ;
if ( drawable - > GetBlendingSpanVector ( ) . Empty ( ) )
{
// Only do this if we've blended ALL of the spans. Thus, this becomes a trivial
// rejection for all the skinning flags being cleared
drawable - > SetSkinTime ( fRenderCnt ) ;
}
return true ;
}
// IBeginAllocUnmanaged ///////////////////////////////////////////////////////////////////
// Before allocating anything into POOL_DEFAULT, we must evict managed memory.
// See LoadResources.
void plDXPipeline : : IBeginAllocUnManaged ( )
{
// Flush out all managed resources to make room for unmanaged resources.
fD3DDevice - > EvictManagedResources ( ) ;
fEvictTime = fTextUseTime ;
fManagedSeen = 0 ;
fManagedAlloced = false ;
fAllocUnManaged = true ; // we're currently only allocating POOL_DEFAULT
}
// IEndAllocUnManged.
// Before allocating anything into POOL_DEFAULT, we must evict managed memory.
// See LoadResources.
void plDXPipeline : : IEndAllocUnManaged ( )
{
fAllocUnManaged = false ;
// Flush the (should be empty) resource manager to reset its internal allocation pool.
fD3DDevice - > EvictManagedResources ( ) ;
fEvictTime = fTextUseTime ;
fManagedSeen = 0 ;
}
// ICheckTextureUsage ////////////////////////////////////////////////////////////////////
// Obsolete, unused.
// Deletes textures LRU to try to get around NVidia memory manager bug. Found a
// more robust/efficent way. Besides, it didn't help. See OSVERSION.
void plDXPipeline : : ICheckTextureUsage ( )
{
plProfile_IncCount ( fTexUsed , fTexUsed ) ;
plProfile_IncCount ( fTexManaged , fTexManaged ) ;
plConst ( UInt32 ) kMinTexManaged ( 5000000 ) ;
if ( fTexManaged < kMinTexManaged )
return ;
plConst ( UInt32 ) kScale ( 2 ) ;
if ( fTexUsed * kScale < fTexManaged )
{
// Find the stalest
UInt32 stalest = fTextUseTime ;
plDXTextureRef * ref = fTextureRefList ;
while ( ref )
{
// I don't know if render targets even get put in this list.
if ( ! ( ref - > GetFlags ( ) & plDXTextureRef : : kRenderTarget ) & & ( ref - > fUseTime < stalest ) )
stalest = ref - > fUseTime ;
ref = ref - > GetNext ( ) ;
}
stalest = fTextUseTime - stalest ;
// If the stalest is fresh, live with thrashing
plConst ( UInt32 ) kMinAge ( 60 ) ;
if ( stalest < kMinAge )
return ;
// Kill the stalest, and everything more than half as stale
stalest / = 2 ;
if ( stalest < kMinAge )
stalest = kMinAge ;
stalest = fTextUseTime - stalest ;
// Go through again slaughtering left and right
ref = fTextureRefList ;
while ( ref )
{
if ( ! ( ref - > GetFlags ( ) & plDXTextureRef : : kRenderTarget ) & & ( ref - > fUseTime < stalest ) )
{
plDXTextureRef * nuke = ref ;
ref = ref - > GetNext ( ) ;
nuke - > Release ( ) ;
nuke - > Unlink ( ) ;
}
else
{
ref = ref - > GetNext ( ) ;
}
}
}
}
// ICheckVtxUsage ////////////////////////////////////////////////////////////////////
// Obsolete, unused.
// Deletes textures LRU to try to get around NVidia memory manager bug. Found a
// more robust/efficent way. Besides, it didn't help. See OSVERSION.
void plDXPipeline : : ICheckVtxUsage ( )
{
plProfile_IncCount ( fVtxUsed , fVtxUsed ) ;
plProfile_IncCount ( fVtxManaged , fVtxManaged ) ;
plConst ( UInt32 ) kMinVtxManaged ( 5000000 ) ;
if ( fVtxManaged < kMinVtxManaged )
return ;
plConst ( UInt32 ) kScale ( 2 ) ;
if ( fVtxUsed * kScale < fVtxManaged )
{
// Find the stalest
UInt32 stalest = fTextUseTime ;
plDXVertexBufferRef * ref = fVtxBuffRefList ;
while ( ref )
{
if ( ! ref - > Volatile ( ) & & ( ref - > fUseTime < stalest ) )
stalest = ref - > fUseTime ;
ref = ref - > GetNext ( ) ;
}
stalest = fTextUseTime - stalest ;
// If the stalest is fresh, live with thrashing
plConst ( UInt32 ) kMinAge ( 60 ) ;
if ( stalest < kMinAge )
return ;
// Kill the stalest, and everything more than half as stale
stalest / = 2 ;
if ( stalest < kMinAge )
stalest = kMinAge ;
stalest = fTextUseTime - stalest ;
// Go through again slaughtering left and right
ref = fVtxBuffRefList ;
while ( ref )
{
if ( ! ref - > Volatile ( ) & & ( ref - > fUseTime < stalest ) )
{
plDXVertexBufferRef * nuke = ref ;
ref = ref - > GetNext ( ) ;
nuke - > Release ( ) ;
nuke - > Unlink ( ) ;
}
else
{
ref = ref - > GetNext ( ) ;
}
}
}
}
hsBool plDXPipeline : : CheckResources ( )
{
if ( ( fClothingOutfits . GetCount ( ) < = 1 & & fAvRTPool . GetCount ( ) > 1 ) | |
( fAvRTPool . GetCount ( ) > = 16 & & ( fAvRTPool . GetCount ( ) / 2 > = fClothingOutfits . GetCount ( ) ) ) )
{
return ( hsTimer : : GetSysSeconds ( ) - fAvRTShrinkValidSince > kAvTexPoolShrinkThresh ) ;
}
fAvRTShrinkValidSince = hsTimer : : GetSysSeconds ( ) ;
return ( fAvRTPool . GetCount ( ) < fClothingOutfits . GetCount ( ) ) ;
}
// LoadResources ///////////////////////////////////////////////////////////////////////
// Basically, we have to load anything that goes into POOL_DEFAULT before
// anything into POOL_MANAGED, or the memory manager gets confused.
// More precisely, we have to evict everything from POOL_MANAGED before we
// can allocate anything into POOL_DEFAULT.
// So, this function frees up everything in POOL_DEFAULT, evicts managed memory,
// calls out for anything needing to be created POOL_DEFAULT to do so,
// Then we're free to load into POOL_MANAGED on demand.
// This is typically called at the beginning of the first render after loading
// a new age.
void plDXPipeline : : LoadResources ( )
{
hsStatusMessageF ( " Begin Device Reload t=%f " , hsTimer : : GetSeconds ( ) ) ;
plNetClientApp : : StaticDebugMsg ( " Begin Device Reload " ) ;
// Just to be safe.
IInitDeviceState ( ) ; // 9700 THRASH
// Evict mananged memory.
IBeginAllocUnManaged ( ) ;
// Release everything we have in POOL_DEFAULT.
IReleaseDynamicBuffers ( ) ;
IReleaseAvRTPool ( ) ;
// Create all RenderTargets
plPipeRTMakeMsg * rtMake = TRACKED_NEW plPipeRTMakeMsg ( this ) ;
rtMake - > Send ( ) ;
// Create all our shadow render targets and pipeline specific POOL_DEFAULT vertex buffers.
// This includes our single dynamic vertex buffer that we cycle through for software
// skinned, particle systems, etc.
ICreateDynamicBuffers ( ) ;
// Create all POOL_DEFAULT (sorted) index buffers in the scene.
plPipeGeoMakeMsg * defMake = TRACKED_NEW plPipeGeoMakeMsg ( this , true ) ;
defMake - > Send ( ) ;
// This can be a bit of a mem hog and will use more mem if available, so keep it last in the
// POOL_DEFAULT allocs.
IFillAvRTPool ( ) ;
// We should have everything POOL_DEFAULT we need now.
IEndAllocUnManaged ( ) ;
// Force a create of all our static D3D vertex buffers.
# define MF_PRELOAD_MANAGEDBUFFERS
# ifdef MF_PRELOAD_MANAGEDBUFFERS
plPipeGeoMakeMsg * manMake = TRACKED_NEW plPipeGeoMakeMsg ( this , false ) ;
manMake - > Send ( ) ;
# endif // MF_PRELOAD_MANAGEDBUFFERS
// Forcing a preload of textures turned out to not be so great,
// since there are typically so many in an age, it swamped out
// VM.
# ifdef MF_TOSSER
# define MF_PRELOAD_TEXTURES
# endif // MF_TOSSER
# ifdef MF_PRELOAD_TEXTURES
plPipeTexMakeMsg * texMake = TRACKED_NEW plPipeTexMakeMsg ( this ) ;
texMake - > Send ( ) ;
# endif // MF_PRELOAD_TEXTURES
fD3DDevice - > EvictManagedResources ( ) ;
// Okay, we've done it, clear the request.
plPipeResReq : : Clear ( ) ;
plProfile_IncCount ( PipeReload , 1 ) ;
hsStatusMessageF ( " End Device Reload t=%f " , hsTimer : : GetSeconds ( ) ) ;
plNetClientApp : : StaticDebugMsg ( " End Device Reload " ) ;
}
// Sorry about this, but it really did speed up the skinning.
// Just some macros for the inner loop of IBlendVertsIntoBuffer.
# define MATRIXMULTBEGIN(xfm, wgt) \
register float m00 = xfm . fMap [ 0 ] [ 0 ] ; \
register float m01 = xfm . fMap [ 0 ] [ 1 ] ; \
register float m02 = xfm . fMap [ 0 ] [ 2 ] ; \
register float m03 = xfm . fMap [ 0 ] [ 3 ] ; \
register float m10 = xfm . fMap [ 1 ] [ 0 ] ; \
register float m11 = xfm . fMap [ 1 ] [ 1 ] ; \
register float m12 = xfm . fMap [ 1 ] [ 2 ] ; \
register float m13 = xfm . fMap [ 1 ] [ 3 ] ; \
register float m20 = xfm . fMap [ 2 ] [ 0 ] ; \
register float m21 = xfm . fMap [ 2 ] [ 1 ] ; \
register float m22 = xfm . fMap [ 2 ] [ 2 ] ; \
register float m23 = xfm . fMap [ 2 ] [ 3 ] ; \
register float m_wgt = wgt ; \
register float srcX , srcY , srcZ ;
# define MATRIXMULTPOINTADD(dst, src) \
srcX = src . fX ; \
srcY = src . fY ; \
srcZ = src . fZ ; \
\
dst . fX + = ( srcX * m00 + srcY * m01 + srcZ * m02 + m03 ) * m_wgt ; \
dst . fY + = ( srcX * m10 + srcY * m11 + srcZ * m12 + m13 ) * m_wgt ; \
dst . fZ + = ( srcX * m20 + srcY * m21 + srcZ * m22 + m23 ) * m_wgt ;
# define MATRIXMULTVECTORADD(dst, src) \
srcX = src . fX ; \
srcY = src . fY ; \
srcZ = src . fZ ; \
\
dst . fX + = ( srcX * m00 + srcY * m01 + srcZ * m02 ) * m_wgt ; \
dst . fY + = ( srcX * m10 + srcY * m11 + srcZ * m12 ) * m_wgt ; \
dst . fZ + = ( srcX * m20 + srcY * m21 + srcZ * m22 ) * m_wgt ;
// inlTESTPOINT /////////////////////////////////////////
// Update mins and maxs if destP is outside.
inline void inlTESTPOINT ( const hsPoint3 & destP ,
hsScalar & minX , hsScalar & minY , hsScalar & minZ ,
hsScalar & maxX , hsScalar & maxY , hsScalar & maxZ )
{
if ( destP . fX < minX )
minX = destP . fX ;
else if ( destP . fX > maxX )
maxX = destP . fX ;
if ( destP . fY < minY )
minY = destP . fY ;
else if ( destP . fY > maxY )
maxY = destP . fY ;
if ( destP . fZ < minZ )
minZ = destP . fZ ;
else if ( destP . fZ > maxZ )
maxZ = destP . fZ ;
}
//// IBlendVertsIntoBuffer ////////////////////////////////////////////////////
// Given a pointer into a buffer of verts that have blending data in the D3D
// format, blends them into the destination buffer given without the blending
// info.
void plDXPipeline : : IBlendVertsIntoBuffer ( plSpan * span ,
hsMatrix44 * matrixPalette , int numMatrices ,
UInt8 * src , UInt8 format , UInt32 srcStride ,
UInt8 * dest , UInt32 destStride , UInt32 count ,
UInt16 localUVWChans )
{
UInt8 numUVs , numWeights ;
UInt32 i , j , indices , color , specColor , uvChanSize ;
float weights [ 4 ] , weightSum ;
hsPoint3 pt , tempPt , destPt ;
hsVector3 vec , tempNorm , destNorm ;
/// Get some counts
switch ( format & plGBufferGroup : : kSkinWeightMask )
{
case plGBufferGroup : : kSkin1Weight : numWeights = 1 ; break ;
case plGBufferGroup : : kSkin2Weights : numWeights = 2 ; break ;
case plGBufferGroup : : kSkin3Weights : numWeights = 3 ; break ;
default : hsAssert ( false , " Invalid weight count in IBlendVertsIntoBuffer() " ) ;
}
numUVs = plGBufferGroup : : CalcNumUVs ( format ) ;
uvChanSize = numUVs * sizeof ( float ) * 3 ;
//#define MF_RECALC_BOUNDS
# ifdef MF_RECALC_BOUNDS
hsScalar minX = 1.e33 f ;
hsScalar minY = 1.e33 f ;
hsScalar minZ = 1.e33 f ;
hsScalar maxX = - 1.e33 f ;
hsScalar maxY = - 1.e33 f ;
hsScalar maxZ = - 1.e33 f ;
# endif // MF_RECALC_BOUNDS
// localUVWChans is bump mapping tangent space vectors, which need to
// be skinned like the normal, as opposed to passed through like
// garden variety UVW coordinates.
// There are no localUVWChans that I know of in production assets (i.e.
// the avatar is not skinned).
if ( ! localUVWChans )
{
/// Copy whilst blending
for ( i = 0 ; i < count ; i + + )
{
// Extract data
src = inlExtractPoint ( src , pt ) ;
for ( j = 0 , weightSum = 0 ; j < numWeights ; j + + )
{
src = inlExtractFloat ( src , weights [ j ] ) ;
weightSum + = weights [ j ] ;
}
weights [ j ] = 1 - weightSum ;
if ( format & plGBufferGroup : : kSkinIndices )
{
src = inlExtractUInt32 ( src , indices ) ;
}
else
{
indices = 1 < < 8 ;
}
src = inlExtractPoint ( src , vec ) ;
src = inlExtractUInt32 ( src , color ) ;
src = inlExtractUInt32 ( src , specColor ) ;
// Blend
destPt . Set ( 0 , 0 , 0 ) ;
destNorm . Set ( 0 , 0 , 0 ) ;
for ( j = 0 ; j < numWeights + 1 ; j + + )
{
if ( weights [ j ] )
{
MATRIXMULTBEGIN ( matrixPalette [ indices & 0xff ] , weights [ j ] ) ;
MATRIXMULTPOINTADD ( destPt , pt ) ;
MATRIXMULTVECTORADD ( destNorm , vec ) ;
}
indices > > = 8 ;
}
// Probably don't really need to renormalize this. There errors are
// going to be subtle and "smooth".
// hsFastMath::NormalizeAppr(destNorm);
# ifdef MF_RECALC_BOUNDS
inlTESTPOINT ( destPt , minX , minY , minZ , maxX , maxY , maxZ ) ;
# endif // MF_RECALC_BOUNDS
// Slam data into position now
dest = inlStuffPoint ( dest , destPt ) ;
dest = inlStuffPoint ( dest , destNorm ) ;
dest = inlStuffUInt32 ( dest , color ) ;
dest = inlStuffUInt32 ( dest , specColor ) ;
memcpy ( dest , src , uvChanSize ) ;
src + = uvChanSize ;
dest + = uvChanSize ;
}
}
else
{
UInt8 hiChan = localUVWChans > > 8 ;
UInt8 loChan = localUVWChans & 0xff ;
/// Copy whilst blending
for ( i = 0 ; i < count ; i + + )
{
hsVector3 srcUVWs [ plGeometrySpan : : kMaxNumUVChannels ] ;
hsVector3 dstUVWs [ plGeometrySpan : : kMaxNumUVChannels ] ;
// Extract data
src = inlExtractPoint ( src , pt ) ;
for ( j = 0 , weightSum = 0 ; j < numWeights ; j + + )
{
src = inlExtractFloat ( src , weights [ j ] ) ;
weightSum + = weights [ j ] ;
}
weights [ j ] = 1 - weightSum ;
if ( format & plGBufferGroup : : kSkinIndices )
{
src = inlExtractUInt32 ( src , indices ) ;
}
else
{
indices = 1 < < 8 ;
}
src = inlExtractPoint ( src , vec ) ;
src = inlExtractUInt32 ( src , color ) ;
src = inlExtractUInt32 ( src , specColor ) ;
UInt8 k ;
for ( k = 0 ; k < numUVs ; k + + )
{
src = inlExtractPoint ( src , srcUVWs [ k ] ) ;
}
memcpy ( dstUVWs , srcUVWs , uvChanSize ) ;
dstUVWs [ loChan ] . Set ( 0 , 0 , 0 ) ;
dstUVWs [ hiChan ] . Set ( 0 , 0 , 0 ) ;
// Blend
destPt . Set ( 0 , 0 , 0 ) ;
destNorm . Set ( 0 , 0 , 0 ) ;
for ( j = 0 ; j < numWeights + 1 ; j + + )
{
if ( weights [ j ] )
{
MATRIXMULTBEGIN ( matrixPalette [ indices & 0xff ] , weights [ j ] ) ;
MATRIXMULTPOINTADD ( destPt , pt ) ;
MATRIXMULTVECTORADD ( destNorm , vec ) ;
MATRIXMULTVECTORADD ( dstUVWs [ loChan ] , srcUVWs [ loChan ] ) ;
MATRIXMULTVECTORADD ( dstUVWs [ hiChan ] , srcUVWs [ hiChan ] ) ;
}
indices > > = 8 ;
}
// Probably don't really need to renormalize this. There errors are
// going to be subtle and "smooth".
// hsFastMath::NormalizeAppr(destNorm);
// hsFastMath::NormalizeAppr(dstUVWs[loChan]);
// hsFastMath::NormalizeAppr(dstUVWs[hiChan]);
# ifdef MF_RECALC_BOUNDS
inlTESTPOINT ( destPt , minX , minY , minZ , maxX , maxY , maxZ ) ;
# endif // MF_RECALC_BOUNDS
// Slam data into position now
dest = inlStuffPoint ( dest , destPt ) ;
dest = inlStuffPoint ( dest , destNorm ) ;
dest = inlStuffUInt32 ( dest , color ) ;
dest = inlStuffUInt32 ( dest , specColor ) ;
memcpy ( dest , dstUVWs , uvChanSize ) ;
dest + = uvChanSize ;
}
}
# ifdef MF_RECALC_BOUNDS
hsBounds3Ext wBnd ;
wBnd . Reset ( & hsPoint3 ( minX , minY , minZ ) ) ;
wBnd . Union ( & hsPoint3 ( maxX , maxY , maxZ ) ) ;
span - > fWorldBounds = wBnd ;
# endif // MF_RECALC_BOUNDS
}
// ISetPipeConsts //////////////////////////////////////////////////////////////////
// A shader can request that the pipeline fill in certain constants that are indeterminate
// until the pipeline is about to render the object the shader is applied to. For example,
// the object's local to world. A single shader may be used on multiple objects with
// multiple local to world transforms. This ensures the pipeline will shove the proper
// local to world into the shader immediately before the render.
// See plShader.h for the list of available pipe constants.
// Note that the lighting pipe constants are NOT implemented.
void plDXPipeline : : ISetPipeConsts ( plShader * shader )
{
int n = shader - > GetNumPipeConsts ( ) ;
int i ;
for ( i = 0 ; i < n ; i + + )
{
const plPipeConst & pc = shader - > GetPipeConst ( i ) ;
switch ( pc . fType )
{
case plPipeConst : : kFogSet :
{
float set [ 4 ] ;
IGetVSFogSet ( set ) ;
shader - > SetFloat4 ( pc . fReg , set ) ;
}
break ;
case plPipeConst : : kLayAmbient :
{
hsColorRGBA col = fCurrLay - > GetAmbientColor ( ) ;
shader - > SetColor ( pc . fReg , col ) ;
}
break ;
case plPipeConst : : kLayRuntime :
{
hsColorRGBA col = fCurrLay - > GetRuntimeColor ( ) ;
col . a = fCurrLay - > GetOpacity ( ) ;
shader - > SetColor ( pc . fReg , col ) ;
}
break ;
case plPipeConst : : kLaySpecular :
{
hsColorRGBA col = fCurrLay - > GetSpecularColor ( ) ;
shader - > SetColor ( pc . fReg , col ) ;
}
break ;
case plPipeConst : : kTex3x4_0 :
case plPipeConst : : kTex3x4_1 :
case plPipeConst : : kTex3x4_2 :
case plPipeConst : : kTex3x4_3 :
case plPipeConst : : kTex3x4_4 :
case plPipeConst : : kTex3x4_5 :
case plPipeConst : : kTex3x4_6 :
case plPipeConst : : kTex3x4_7 :
{
int stage = pc . fType - plPipeConst : : kTex3x4_0 ;
if ( stage > fCurrNumLayers )
{
// Ooops. This is bad, means the shader is expecting more layers than
// we actually have (or is just bogus). Assert and quietly continue.
hsAssert ( false , " Shader asking for higher stage transform than we have " ) ;
continue ;
}
const hsMatrix44 & xfm = fCurrMaterial - > GetLayer ( fCurrLayerIdx + stage ) - > GetTransform ( ) ;
shader - > SetMatrix34 ( pc . fReg , xfm ) ;
}
break ;
case plPipeConst : : kTex2x4_0 :
case plPipeConst : : kTex2x4_1 :
case plPipeConst : : kTex2x4_2 :
case plPipeConst : : kTex2x4_3 :
case plPipeConst : : kTex2x4_4 :
case plPipeConst : : kTex2x4_5 :
case plPipeConst : : kTex2x4_6 :
case plPipeConst : : kTex2x4_7 :
{
int stage = pc . fType - plPipeConst : : kTex2x4_0 ;
if ( stage > fCurrNumLayers )
{
// Ooops. This is bad, means the shader is expecting more layers than
// we actually have (or is just bogus). Assert and quietly continue.
hsAssert ( false , " Shader asking for higher stage transform than we have " ) ;
continue ;
}
const hsMatrix44 & xfm = fCurrMaterial - > GetLayer ( fCurrLayerIdx + stage ) - > GetTransform ( ) ;
shader - > SetMatrix24 ( pc . fReg , xfm ) ;
}
break ;
case plPipeConst : : kTex1x4_0 :
case plPipeConst : : kTex1x4_1 :
case plPipeConst : : kTex1x4_2 :
case plPipeConst : : kTex1x4_3 :
case plPipeConst : : kTex1x4_4 :
case plPipeConst : : kTex1x4_5 :
case plPipeConst : : kTex1x4_6 :
case plPipeConst : : kTex1x4_7 :
{
int stage = pc . fType - plPipeConst : : kTex1x4_0 ;
if ( stage > fCurrNumLayers )
{
// Ooops. This is bad, means the shader is expecting more layers than
// we actually have (or is just bogus). Assert and quietly continue.
hsAssert ( false , " Shader asking for higher stage transform than we have " ) ;
continue ;
}
const hsMatrix44 & xfm = fCurrMaterial - > GetLayer ( fCurrLayerIdx + stage ) - > GetTransform ( ) ;
shader - > SetFloat4 ( pc . fReg , xfm . fMap [ 0 ] ) ;
}
break ;
case plPipeConst : : kLocalToNDC :
{
hsMatrix44 cam2ndc = IGetCameraToNDC ( ) ;
hsMatrix44 world2cam = GetViewTransform ( ) . GetWorldToCamera ( ) ;
hsMatrix44 local2ndc = cam2ndc * world2cam * GetLocalToWorld ( ) ;
shader - > SetMatrix44 ( pc . fReg , local2ndc ) ;
}
break ;
case plPipeConst : : kCameraToNDC :
{
hsMatrix44 cam2ndc = IGetCameraToNDC ( ) ;
shader - > SetMatrix44 ( pc . fReg , cam2ndc ) ;
}
break ;
case plPipeConst : : kWorldToNDC :
{
hsMatrix44 cam2ndc = IGetCameraToNDC ( ) ;
hsMatrix44 world2cam = GetViewTransform ( ) . GetWorldToCamera ( ) ;
hsMatrix44 world2ndc = cam2ndc * world2cam ;
shader - > SetMatrix44 ( pc . fReg , world2ndc ) ;
}
break ;
case plPipeConst : : kLocalToWorld :
shader - > SetMatrix34 ( pc . fReg , GetLocalToWorld ( ) ) ;
break ;
case plPipeConst : : kWorldToLocal :
shader - > SetMatrix34 ( pc . fReg , GetWorldToLocal ( ) ) ;
break ;
case plPipeConst : : kWorldToCamera :
{
hsMatrix44 world2cam = GetViewTransform ( ) . GetWorldToCamera ( ) ;
shader - > SetMatrix34 ( pc . fReg , world2cam ) ;
}
break ;
case plPipeConst : : kCameraToWorld :
{
hsMatrix44 cam2world = GetViewTransform ( ) . GetCameraToWorld ( ) ;
shader - > SetMatrix34 ( pc . fReg , cam2world ) ;
}
break ;
case plPipeConst : : kLocalToCamera :
{
hsMatrix44 world2cam = GetViewTransform ( ) . GetWorldToCamera ( ) ;
hsMatrix44 local2cam = world2cam * GetLocalToWorld ( ) ;
shader - > SetMatrix34 ( pc . fReg , local2cam ) ;
}
break ;
case plPipeConst : : kCameraToLocal :
{
hsMatrix44 cam2world = GetViewTransform ( ) . GetCameraToWorld ( ) ;
hsMatrix44 cam2local = GetWorldToLocal ( ) * cam2world ;
shader - > SetMatrix34 ( pc . fReg , cam2local ) ;
}
break ;
case plPipeConst : : kCamPosWorld :
{
shader - > SetVectorW ( pc . fReg , GetViewTransform ( ) . GetCameraToWorld ( ) . GetTranslate ( ) , 1.f ) ;
}
break ;
case plPipeConst : : kCamPosLocal :
{
hsPoint3 localCam = GetWorldToLocal ( ) * GetViewTransform ( ) . GetCameraToWorld ( ) . GetTranslate ( ) ;
shader - > SetVectorW ( pc . fReg , localCam , 1.f ) ;
}
break ;
case plPipeConst : : kObjPosWorld :
{
shader - > SetVectorW ( pc . fReg , GetLocalToWorld ( ) . GetTranslate ( ) , 1.f ) ;
}
break ;
// UNIMPLEMENTED
case plPipeConst : : kDirLight1 :
case plPipeConst : : kDirLight2 :
case plPipeConst : : kDirLight3 :
case plPipeConst : : kDirLight4 :
case plPipeConst : : kPointLight1 :
case plPipeConst : : kPointLight2 :
case plPipeConst : : kPointLight3 :
case plPipeConst : : kPointLight4 :
break ;
}
}
}
// ISetShaders /////////////////////////////////////////////////////////////////////////////////////
// Setup to render using the input vertex and pixel shader. Either or both may
// be nil, in which case the fixed function pipeline is indicated.
// Any Pipe Constants the non-FFP shader wants will be set here.
// Lastly, all constants will be set (as a block) for any non-FFP vertex or pixel shader.
HRESULT plDXPipeline : : ISetShaders ( plShader * vShader , plShader * pShader )
{
IDirect3DVertexShader9 * vsHandle = NULL ;
if ( vShader )
{
hsAssert ( vShader - > IsVertexShader ( ) , " Wrong type shader as vertex shader " ) ;
ISetPipeConsts ( vShader ) ;
plDXVertexShader * vRef = ( plDXVertexShader * ) vShader - > GetDeviceRef ( ) ;
if ( ! vRef )
{
vRef = TRACKED_NEW plDXVertexShader ( vShader ) ;
hsRefCnt_SafeUnRef ( vRef ) ;
}
if ( ! vRef - > IsLinked ( ) )
vRef - > Link ( & fVShaderRefList ) ;
vsHandle = vRef - > GetShader ( this ) ;
// This is truly obnoxious, but D3D insists that, while using the progammable pipeline,
// all stages be set up like this, not just the ones we're going to use. We have to
// do this if we have either a vertex or a pixel shader. See below. Whatever. mf
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
fD3DDevice - > SetTextureStageState ( i , D3DTSS_TEXCOORDINDEX , fLayerUVWSrcs [ i ] = i ) ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_TEXTURETRANSFORMFLAGS , fLayerXformFlags [ i ] = 0 ) ;
}
}
IDirect3DPixelShader9 * psHandle = NULL ;
if ( pShader )
{
hsAssert ( pShader - > IsPixelShader ( ) , " Wrong type shader as pixel shader " ) ;
ISetPipeConsts ( pShader ) ;
plDXPixelShader * pRef = ( plDXPixelShader * ) pShader - > GetDeviceRef ( ) ;
if ( ! pRef )
{
pRef = TRACKED_NEW plDXPixelShader ( pShader ) ;
hsRefCnt_SafeUnRef ( pRef ) ;
}
if ( ! pRef - > IsLinked ( ) )
pRef - > Link ( & fPShaderRefList ) ;
psHandle = pRef - > GetShader ( this ) ;
if ( ! vShader )
{
int i ;
for ( i = 0 ; i < 8 ; i + + )
{
fD3DDevice - > SetTextureStageState ( i , D3DTSS_TEXCOORDINDEX , fLayerUVWSrcs [ i ] = i ) ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_TEXTURETRANSFORMFLAGS , fLayerXformFlags [ i ] = 0 ) ;
}
}
}
if ( vsHandle ! = fSettings . fCurrVertexShader )
{
HRESULT hr = fD3DDevice - > SetVertexShader ( fSettings . fCurrVertexShader = vsHandle ) ;
hsAssert ( ! FAILED ( hr ) , " Error setting vertex shader " ) ;
}
if ( psHandle ! = fSettings . fCurrPixelShader )
{
HRESULT hr = fD3DDevice - > SetPixelShader ( fSettings . fCurrPixelShader = psHandle ) ;
hsAssert ( ! FAILED ( hr ) , " Error setting pixel shader " ) ;
}
// Handle cull mode here, because current cullmode is dependent on
// the handedness of the LocalToCamera AND whether we are twosided.
ISetCullMode ( ) ;
return S_OK ;
}
// IRenderAuxSpan //////////////////////////////////////////////////////////
// Aux spans (auxilliary) are geometry rendered immediately after, and therefore dependent, on
// other normal geometry. They don't have SceneObjects, Drawables, DrawInterfaces or
// any of that, and therefore don't correspond to any object in the scene.
// They are dynamic procedural decals. See plDynaDecal.cpp and plDynaDecalMgr.cpp.
// This is wrapped by IRenderAuxSpans, which makes sure state is restored to resume
// normal rendering after the AuxSpan is rendered.
void plDXPipeline : : IRenderAuxSpan ( const plSpan & span , const plAuxSpan * aux )
{
// Make sure the underlying resources are created and filled in with current data.
CheckVertexBufferRef ( aux - > fGroup , aux - > fVBufferIdx ) ;
CheckIndexBufferRef ( aux - > fGroup , aux - > fIBufferIdx ) ;
ICheckAuxBuffers ( aux ) ;
// Set to render from the aux spans buffers.
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) aux - > fGroup - > GetVertexBufferRef ( aux - > fVBufferIdx ) ;
if ( ! vRef )
return ;
plDXIndexBufferRef * iRef = ( plDXIndexBufferRef * ) aux - > fGroup - > GetIndexBufferRef ( aux - > fIBufferIdx ) ;
if ( ! iRef )
return ;
HRESULT r ;
r = fD3DDevice - > SetStreamSource ( 0 , vRef - > fD3DBuffer , 0 , vRef - > fVertexSize ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the stream source! " ) ;
plProfile_Inc ( VertexChange ) ;
fD3DDevice - > SetFVF ( fSettings . fCurrFVFFormat = IGetBufferD3DFormat ( vRef - > fFormat ) ) ;
r = fD3DDevice - > SetIndices ( iRef - > fD3DBuffer ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the indices! " ) ;
plRenderTriListFunc render ( fD3DDevice , iRef - > fOffset , aux - > fVStartIdx , aux - > fVLength , aux - > fIStartIdx , aux - > fILength / 3 ) ;
// Now just loop through the aux material, rendering in as many passes as it takes.
hsGMaterial * material = aux - > fMaterial ;
int j ;
for ( j = 0 ; j < material - > GetNumLayers ( ) ; )
{
int iCurrMat = j ;
j = IHandleMaterial ( material , iCurrMat , & span ) ;
if ( j = = - 1 )
break ;
ISetShaders ( material - > GetLayer ( iCurrMat ) - > GetVertexShader ( ) , material - > GetLayer ( iCurrMat ) - > GetPixelShader ( ) ) ;
if ( aux - > fFlags & plAuxSpan : : kOverrideLiteModel )
{
static D3DMATERIAL9 mat ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , 0xffffffff ) ;
fD3DDevice - > SetRenderState ( D3DRS_DIFFUSEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_COLOR1 ) ;
fD3DDevice - > SetRenderState ( D3DRS_EMISSIVEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetMaterial ( & mat ) ;
}
render . RenderPrims ( ) ;
}
}
// IRenderAuxSpans ////////////////////////////////////////////////////////////////////////////
// Save and restore render state around calls to IRenderAuxSpan. This lets
// a list of aux spans get rendered with only one save/restore state.
void plDXPipeline : : IRenderAuxSpans ( const plSpan & span )
{
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoAuxSpans ) )
return ;
plDXVertexBufferRef * oldVRef = fSettings . fCurrVertexBuffRef ;
plDXIndexBufferRef * oldIRef = fSettings . fCurrIndexBuffRef ;
ISetLocalToWorld ( hsMatrix44 : : IdentityMatrix ( ) , hsMatrix44 : : IdentityMatrix ( ) ) ;
int i ;
for ( i = 0 ; i < span . GetNumAuxSpans ( ) ; i + + )
IRenderAuxSpan ( span , span . GetAuxSpan ( i ) ) ;
ISetLocalToWorld ( span . fLocalToWorld , span . fWorldToLocal ) ;
HRESULT r ;
r = fD3DDevice - > SetStreamSource ( 0 , oldVRef - > fD3DBuffer , 0 , oldVRef - > fVertexSize ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the stream source! " ) ;
plProfile_Inc ( VertexChange ) ;
r = fD3DDevice - > SetFVF ( fSettings . fCurrFVFFormat = IGetBufferD3DFormat ( oldVRef - > fFormat ) ) ;
r = fD3DDevice - > SetIndices ( oldIRef - > fD3DBuffer ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the indices! " ) ;
}
// ICheckVBUsage //////////////////////////////////////////////////////////////
// Keep track of how much managed vertex buffer memory is being used and
// has been used since the last evict.
inline void plDXPipeline : : ICheckVBUsage ( plDXVertexBufferRef * vRef )
{
if ( ! vRef - > fOwner - > AreVertsVolatile ( ) )
{
if ( vRef - > fUseTime < = fEvictTime )
fManagedSeen + = vRef - > fVertexSize * vRef - > fCount ;
if ( vRef - > fUseTime ! = fTextUseTime )
{
plProfile_NewMem ( CurrVB , vRef - > fVertexSize * vRef - > fCount ) ;
fVtxUsed + = vRef - > fVertexSize * vRef - > fCount ;
vRef - > fUseTime = fTextUseTime ;
}
}
}
//// IRenderBufferSpan ////////////////////////////////////////////////////////
// Sets up the vertex and index buffers for a span, and then
// renders it in as many passes as it takes in ILoopOverLayers.
void plDXPipeline : : IRenderBufferSpan ( const plIcicle & span ,
hsGDeviceRef * vb , hsGDeviceRef * ib ,
hsGMaterial * material , UInt32 vStart , UInt32 vLength ,
UInt32 iStart , UInt32 iLength )
{
plProfile_BeginTiming ( RenderBuff ) ;
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) vb ;
plDXIndexBufferRef * iRef = ( plDXIndexBufferRef * ) ib ;
HRESULT r ;
if ( vRef - > fD3DBuffer = = nil | | iRef - > fD3DBuffer = = nil )
{
plProfile_EndTiming ( RenderBuff ) ;
hsAssert ( false , " Trying to render a nil buffer pair! " ) ;
return ;
}
/// Switch to the vertex buffer we want
if ( fSettings . fCurrVertexBuffRef ! = vRef )
{
hsRefCnt_SafeAssign ( fSettings . fCurrVertexBuffRef , vRef ) ;
hsAssert ( vRef - > fD3DBuffer ! = nil , " Trying to render a buffer pair without a vertex buffer! " ) ;
vRef - > SetRebuiltSinceUsed ( true ) ;
}
if ( vRef - > RebuiltSinceUsed ( ) )
{
r = fD3DDevice - > SetStreamSource ( 0 , vRef - > fD3DBuffer , 0 , vRef - > fVertexSize ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the stream source! " ) ;
plProfile_Inc ( VertexChange ) ;
DWORD fvf = IGetBufferD3DFormat ( vRef - > fFormat ) ;
if ( fSettings . fCurrFVFFormat ! = fvf )
fD3DDevice - > SetFVF ( fSettings . fCurrFVFFormat = fvf ) ;
vRef - > SetRebuiltSinceUsed ( false ) ;
ICheckVBUsage ( vRef ) ;
}
// Note: both these stats are the same, since we don't do any culling or clipping on the tris
if ( fSettings . fCurrIndexBuffRef ! = iRef )
{
hsRefCnt_SafeAssign ( fSettings . fCurrIndexBuffRef , iRef ) ;
hsAssert ( iRef - > fD3DBuffer ! = nil , " Trying to render with a nil index buffer " ) ;
iRef - > SetRebuiltSinceUsed ( true ) ;
}
if ( iRef - > RebuiltSinceUsed ( ) )
{
r = fD3DDevice - > SetIndices ( iRef - > fD3DBuffer ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the indices! " ) ;
plProfile_Inc ( IndexChange ) ;
iRef - > SetRebuiltSinceUsed ( false ) ;
}
plRenderTriListFunc render ( fD3DDevice , iRef - > fOffset , vStart , vLength , iStart , iLength / 3 ) ;
plProfile_EndTiming ( RenderBuff ) ;
ILoopOverLayers ( render , material , span ) ;
}
// ILoopOverLayers /////////////////////////////////////////////////////////////////////////////////
// Render the input span with the input material in as many passes as it takes.
// Also handles rendering projected lights, either onto each pass or
// once onto the FB after all the passes, as appropriate.
hsBool plDXPipeline : : ILoopOverLayers ( const plRenderPrimFunc & inRender , hsGMaterial * material , const plSpan & span )
{
plProfile_BeginTiming ( RenderPrim ) ;
const plRenderPrimFunc & render = IsDebugFlagSet ( plPipeDbg : : kFlagNoRender ) ? ( const plRenderPrimFunc & ) sRenderNil : inRender ;
if ( GetOverrideMaterial ( ) )
material = GetOverrideMaterial ( ) ;
IPushPiggyBacks ( material ) ;
hsBool normalLightsDisabled = false ;
// Loop across all the layers we need to draw
int j ;
for ( j = 0 ; j < material - > GetNumLayers ( ) ; )
{
int iCurrMat = j ;
j = IHandleMaterial ( material , iCurrMat , & span ) ;
if ( j = = - 1 )
break ;
if ( ( fLayerState [ 0 ] . fBlendFlags & hsGMatState : : kBlendAlpha )
& & ( material - > GetLayer ( iCurrMat ) - > GetOpacity ( ) < = 0 )
& & ( fCurrLightingMethod ! = plSpan : : kLiteVtxPreshaded ) ) // This opt isn't good for particles, since their
// material opacity is undefined/unused... -mcn
continue ;
plProfile_BeginTiming ( SpanFog ) ;
ISetFogParameters ( & span , material - > GetLayer ( iCurrMat ) ) ;
plProfile_EndTiming ( SpanFog ) ;
ISetShaders ( material - > GetLayer ( iCurrMat ) - > GetVertexShader ( ) , material - > GetLayer ( iCurrMat ) - > GetPixelShader ( ) ) ;
if ( normalLightsDisabled )
IRestoreSpanLights ( ) ;
# ifdef HS_DEBUGGING
DWORD nPass ;
fSettings . fDXError = fD3DDevice - > ValidateDevice ( & nPass ) ;
if ( fSettings . fDXError ! = D3D_OK )
IGetD3DError ( ) ;
# endif // HS_DEBUGGING
// Do the regular draw.
render . RenderPrims ( ) ;
// Take care of projections that get applied to each pass.
if ( fLights . fProjEach . GetCount ( ) & & ! ( fView . fRenderState & kRenderNoProjection ) )
{
// Disable all the normal lights.
IDisableSpanLights ( ) ;
normalLightsDisabled = true ;
IRenderProjectionEach ( render , material , iCurrMat , span ) ;
}
if ( IsDebugFlagSet ( plPipeDbg : : kFlagNoUpperLayers ) )
j = material - > GetNumLayers ( ) ;
}
IPopPiggyBacks ( ) ;
// If we disabled lighting, re-enable it.
if ( normalLightsDisabled )
IRestoreSpanLights ( ) ;
// Render any aux spans associated.
if ( span . GetNumAuxSpans ( ) )
IRenderAuxSpans ( span ) ;
// Only render projections and shadows if we successfully rendered the span.
// j == -1 means we aborted render.
if ( j > = 0 )
{
// Projections that get applied to the frame buffer (after all passes).
if ( fLights . fProjAll . GetCount ( ) & & ! ( fView . fRenderState & kRenderNoProjection ) )
IRenderProjections ( render ) ;
// Handle render of shadows onto geometry.
if ( fShadows . GetCount ( ) )
IRenderShadowsOntoSpan ( render , & span , material ) ;
}
// Debug only
if ( IsDebugFlagSet ( plPipeDbg : : kFlagOverlayWire ) )
{
IRenderOverWire ( render , material , span ) ;
}
plProfile_EndTiming ( RenderPrim ) ;
return false ;
}
// IRenderOverWire ///////////////////////////////////////////////////////////////////////////////
// Debug only, renders wireframe on top of normal render.
void plDXPipeline : : IRenderOverWire ( const plRenderPrimFunc & render , hsGMaterial * material , const plSpan & span )
{
UInt32 state = fView . fRenderState ;
fView . fRenderState | = plPipeline : : kRenderBaseLayerOnly ;
static plLayerDepth depth ;
depth . SetMiscFlags ( depth . GetMiscFlags ( ) | hsGMatState : : kMiscWireFrame | hsGMatState : : kMiscTwoSided ) ;
depth . SetZFlags ( ( depth . GetZFlags ( ) & ~ hsGMatState : : kZNoZRead ) | hsGMatState : : kZIncLayer ) ;
AppendLayerInterface ( & depth , false ) ;
if ( IHandleMaterial ( material , 0 , & span ) > = 0 )
{
ISetShaders ( nil , nil ) ;
render . RenderPrims ( ) ;
}
RemoveLayerInterface ( & depth , false ) ;
fView . fRenderState = state ;
}
// IRenderProjectionEach ///////////////////////////////////////////////////////////////////////////////////////
// Render any lights that are to be projected onto each pass of the object.
void plDXPipeline : : IRenderProjectionEach ( const plRenderPrimFunc & render , hsGMaterial * material , int iPass , const plSpan & span )
{
// If this is a bump map pass, forget it, we've already "done" per-pixel lighting.
if ( fLayerState [ iPass ] . fMiscFlags & ( hsGMatState : : kMiscBumpLayer | hsGMatState : : kMiscBumpChans ) )
return ;
// Push the LayerShadowBase override. This sets the blend
// to framebuffer as Add/ZNoWrite and AmbientColor = 0.
static plLayerLightBase layLightBase ;
int iNextPass = iPass + fCurrNumLayers ;
if ( fSettings . fLimitedProj & & ( material - > GetLayer ( iPass ) - > GetUVWSrc ( ) & ~ plLayerInterface : : kUVWIdxMask ) )
return ;
// For each projector:
int k ;
for ( k = 0 ; k < fLights . fProjEach . GetCount ( ) ; k + + )
{
// Push it's projected texture as a piggyback.
plLightInfo * li = fLights . fProjEach [ k ] ;
// Lower end boards are iffy on when they'll project correctly.
if ( fSettings . fCantProj & & ! li - > GetProperty ( plLightInfo : : kLPForceProj ) )
continue ;
plLayerInterface * proj = li - > GetProjection ( ) ;
hsAssert ( proj , " A projector with no texture to project? " ) ;
IPushProjPiggyBack ( proj ) ;
// Enable the projecting light only.
plDXLightRef * ref = ( plDXLightRef * ) li - > GetDeviceRef ( ) ;
fD3DDevice - > LightEnable ( ref - > fD3DIndex , true ) ;
AppendLayerInterface ( & layLightBase , false ) ;
// Render until it's done.
int iRePass = iPass ;
while ( iRePass < iNextPass )
{
iRePass = IHandleMaterial ( material , iRePass , & span ) ;
ISetShaders ( nil , nil ) ;
// Do the render with projection.
render . RenderPrims ( ) ;
}
RemoveLayerInterface ( & layLightBase , false ) ;
// Disable the projecting light
fD3DDevice - > LightEnable ( ref - > fD3DIndex , false ) ;
// Pop it's projected texture off piggyback
IPopProjPiggyBacks ( ) ;
}
}
// IRenderProjections ///////////////////////////////////////////////////////////
// Render any projected lights that want to be rendered a single time after
// all passes on the object are complete.
void plDXPipeline : : IRenderProjections ( const plRenderPrimFunc & render )
{
IDisableSpanLights ( ) ;
int i ;
for ( i = 0 ; i < fLights . fProjAll . GetCount ( ) ; i + + )
{
plLightInfo * li = fLights . fProjAll [ i ] ;
if ( fSettings . fCantProj & & ! li - > GetProperty ( plLightInfo : : kLPForceProj ) )
continue ;
IRenderProjection ( render , li ) ;
}
IRestoreSpanLights ( ) ;
}
// IRenderProjection //////////////////////////////////////////////////////////////
// Render this light's projection onto the frame buffer.
void plDXPipeline : : IRenderProjection ( const plRenderPrimFunc & render , plLightInfo * li )
{
plDXLightRef * ref = ( plDXLightRef * ) li - > GetDeviceRef ( ) ;
fD3DDevice - > LightEnable ( ref - > fD3DIndex , true ) ;
plLayerInterface * proj = li - > GetProjection ( ) ;
static D3DMATERIAL9 mat ;
mat . Diffuse . r = mat . Diffuse . g = mat . Diffuse . b = mat . Diffuse . a = 1.f ;
fD3DDevice - > SetMaterial ( & mat ) ;
fD3DDevice - > SetRenderState ( D3DRS_DIFFUSEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_EMISSIVEMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_MATERIAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , 0xffffffff ) ; //@@@
// Set the FB blend mode, texture, all that.
ICompositeLayerState ( 0 , proj ) ;
// We should have put ZNoZWrite on during export, but we didn't.
fLayerState [ 0 ] . fZFlags = hsGMatState : : kZNoZWrite ;
fCurrNumLayers = 1 ;
IHandleFirstTextureStage ( proj ) ;
if ( proj - > GetBlendFlags ( ) & hsGMatState : : kBlendInvertFinalColor )
{
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_DIFFUSE | D3DTA_COMPLEMENT ) ;
}
// Seal it up
fLastEndingStage = 1 ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLayerState [ 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
# ifdef HS_DEBUGGING
DWORD nPass ;
fSettings . fDXError = fD3DDevice - > ValidateDevice ( & nPass ) ;
if ( fSettings . fDXError ! = D3D_OK )
IGetD3DError ( ) ;
# endif // HS_DEBUGGING
// Okay, render it already.
render . RenderPrims ( ) ;
fD3DDevice - > LightEnable ( ref - > fD3DIndex , false ) ;
}
//// IGetBufferD3DFormat //////////////////////////////////////////////////////
// Convert the dumbest vertex format on the planet (ours) into an FVF code.
// Note the assumption of position, normal, diffuse, and specular.
// We no longer use FVF codes, just shader handles.
long plDXPipeline : : IGetBufferD3DFormat ( UInt8 format ) const
{
long fmt , i ;
switch ( format & plGBufferGroup : : kSkinWeightMask )
{
case plGBufferGroup : : kSkinNoWeights :
fmt = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL ;
break ;
case plGBufferGroup : : kSkin1Weight :
fmt = D3DFVF_XYZB1 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL ;
break ;
case plGBufferGroup : : kSkin2Weights :
fmt = D3DFVF_XYZB2 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL ;
break ;
case plGBufferGroup : : kSkin3Weights :
fmt = D3DFVF_XYZB3 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL ;
break ;
default :
hsAssert ( false , " Bad skin weight value in IGetBufferD3DFormat() " ) ;
}
if ( format & plGBufferGroup : : kSkinIndices )
{
hsAssert ( false , " Indexed skinning not supported " ) ;
fmt | = D3DFVF_LASTBETA_UBYTE4 ;
}
switch ( plGBufferGroup : : CalcNumUVs ( format ) )
{
case 0 : fmt | = D3DFVF_TEX0 ; break ;
case 1 : fmt | = D3DFVF_TEX1 ; break ;
case 2 : fmt | = D3DFVF_TEX2 ; break ;
case 3 : fmt | = D3DFVF_TEX3 ; break ;
case 4 : fmt | = D3DFVF_TEX4 ; break ;
case 5 : fmt | = D3DFVF_TEX5 ; break ;
case 6 : fmt | = D3DFVF_TEX6 ; break ;
case 7 : fmt | = D3DFVF_TEX7 ; break ;
case 8 : fmt | = D3DFVF_TEX8 ; break ;
}
for ( i = 0 ; i < plGBufferGroup : : CalcNumUVs ( format ) ; i + + )
fmt | = D3DFVF_TEXCOORDSIZE3 ( i ) ;
return fmt ;
}
//// IGetBufferFormatSize /////////////////////////////////////////////////////
// Calculate the vertex stride from the given format.
UInt32 plDXPipeline : : IGetBufferFormatSize ( UInt8 format ) const
{
UInt32 size = sizeof ( float ) * 6 + sizeof ( UInt32 ) * 2 ; // Position and normal, and two packed colors
switch ( format & plGBufferGroup : : kSkinWeightMask )
{
case plGBufferGroup : : kSkinNoWeights :
break ;
case plGBufferGroup : : kSkin1Weight :
size + = sizeof ( float ) ;
break ;
default :
hsAssert ( false , " Invalid skin weight value in IGetBufferFormatSize() " ) ;
}
size + = sizeof ( float ) * 3 * plGBufferGroup : : CalcNumUVs ( format ) ;
return size ;
}
///////////////////////////////////////////////////////////////////////////////
//// Plate and PlateManager Functions /////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// None of this plate code is mine, so your guess is as good as mine.
// I'll throw in comments where I happen to know what it's doing, but a lot
// of this is just ugly.
// The plates are mostly used for debugging/performance tools, but they do
// unfortunately get used for some production things like the cursor.
// By the way, a Plate is just a screen aligned textured quad that is rendered
// on top of the normal scene. mf
// ICreateGeometry /////////////////////////////////////////////////////////
// Make a quad suitable for rendering as a tristrip.
void plDXPlateManager : : ICreateGeometry ( plDXPipeline * pipe )
{
UInt32 fvfFormat = PLD3D_PLATEFVF ;
D3DPOOL poolType = D3DPOOL_DEFAULT ;
hsAssert ( ! pipe - > ManagedAlloced ( ) , " Alloc default with managed alloc'd " ) ;
if ( FAILED ( fD3DDevice - > CreateVertexBuffer ( 4 * sizeof ( plPlateVertex ) ,
D3DUSAGE_WRITEONLY ,
fvfFormat ,
poolType , & fVertBuffer , NULL ) ) )
{
hsAssert ( false , " CreateVertexBuffer() call failed! " ) ;
fCreatedSucessfully = false ;
return ;
}
PROFILE_POOL_MEM ( poolType , 4 * sizeof ( plPlateVertex ) , true , " PlateMgrVtxBuff " ) ;
/// Lock the buffer
plPlateVertex * ptr ;
if ( FAILED ( fVertBuffer - > Lock ( 0 , 0 , ( void * * ) & ptr , D3DLOCK_NOSYSLOCK ) ) )
{
hsAssert ( false , " Failed to lock vertex buffer for writing " ) ;
fCreatedSucessfully = false ;
return ;
}
/// Set 'em up
ptr [ 0 ] . fPoint . Set ( - 0.5f , - 0.5f , 0.0f ) ;
ptr [ 0 ] . fColor = 0xffffffff ;
ptr [ 0 ] . fUV . Set ( 0.0f , 0.0f , 0.0f ) ;
ptr [ 1 ] . fPoint . Set ( - 0.5f , 0.5f , 0.0f ) ;
ptr [ 1 ] . fColor = 0xffffffff ;
ptr [ 1 ] . fUV . Set ( 0.0f , 1.0f , 0.0f ) ;
ptr [ 2 ] . fPoint . Set ( 0.5f , - 0.5f , 0.0f ) ;
ptr [ 2 ] . fColor = 0xffffffff ;
ptr [ 2 ] . fUV . Set ( 1.0f , 0.0f , 0.0f ) ;
ptr [ 3 ] . fPoint . Set ( 0.5f , 0.5f , 0.0f ) ;
ptr [ 3 ] . fColor = 0xffffffff ;
ptr [ 3 ] . fUV . Set ( 1.0f , 1.0f , 0.0f ) ;
/// Unlock and we're done!
fVertBuffer - > Unlock ( ) ;
fCreatedSucessfully = true ;
}
// IReleaseGeometry ////////////////////////////////////////////////////////////
// Let go of any D3D resources created for this.
void plDXPlateManager : : IReleaseGeometry ( )
{
if ( fVertBuffer )
{
ReleaseObject ( fVertBuffer ) ;
PROFILE_POOL_MEM ( D3DPOOL_DEFAULT , 4 * sizeof ( plPlateVertex ) , false , " PlateMgrVtxBuff " ) ;
fVertBuffer = nil ;
}
}
//// Constructor & Destructor /////////////////////////////////////////////////
plDXPlateManager : : plDXPlateManager ( plDXPipeline * pipe , IDirect3DDevice9 * device ) : plPlateManager ( pipe ) ,
PLD3D_PLATEFVF ( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3 ( 0 ) ) ,
fD3DDevice ( device ) ,
fVertBuffer ( nil )
{
}
plDXPlateManager : : ~ plDXPlateManager ( )
{
IReleaseGeometry ( ) ;
}
//// IDrawPlate ///////////////////////////////////////////////////////////////
// Render all currently enabled plates to the screen.
void plDXPlateManager : : IDrawToDevice ( plPipeline * pipe )
{
plDXPipeline * dxPipe = ( plDXPipeline * ) pipe ;
plPlate * plate ;
UInt32 scrnWidthDiv2 = fOwner - > Width ( ) > > 1 ;
UInt32 scrnHeightDiv2 = fOwner - > Height ( ) > > 1 ;
D3DXMATRIX mat ;
D3DCULL oldCullMode ;
if ( ! fVertBuffer )
return ;
// Make sure skinning is disabled.
fD3DDevice - > SetRenderState ( D3DRS_VERTEXBLEND , D3DVBF_DISABLE ) ;
fD3DDevice - > SetVertexShader ( dxPipe - > fSettings . fCurrVertexShader = NULL ) ;
fD3DDevice - > SetFVF ( dxPipe - > fSettings . fCurrFVFFormat = PLD3D_PLATEFVF ) ;
fD3DDevice - > SetStreamSource ( 0 , fVertBuffer , 0 , sizeof ( plPlateVertex ) ) ;
plProfile_Inc ( VertexChange ) ;
fD3DDevice - > SetTransform ( D3DTS_VIEW , & d3dIdentityMatrix ) ;
oldCullMode = dxPipe - > fCurrCullMode ;
for ( plate = fPlates ; plate ! = nil ; plate = plate - > GetNext ( ) )
{
if ( plate - > IsVisible ( ) )
{
dxPipe - > IDrawPlate ( plate ) ;
const char * title = plate - > GetTitle ( ) ;
if ( plDebugText : : Instance ( ) . IsEnabled ( ) & & title [ 0 ] ! = 0 )
{
hsPoint3 pt ;
pt . Set ( 0 , - 0.5 , 0 ) ;
pt = plate - > GetTransform ( ) * pt ;
pt . fX = pt . fX * scrnWidthDiv2 + scrnWidthDiv2 ;
pt . fY = pt . fY * scrnHeightDiv2 + scrnHeightDiv2 ;
pt . fX - = plDebugText : : Instance ( ) . CalcStringWidth ( title ) > > 1 ;
plDebugText : : Instance ( ) . DrawString ( ( UInt16 ) pt . fX , ( UInt16 ) pt . fY + 1 , title , 255 , 255 , 255 , 255 , plDebugText : : kStyleBold ) ;
}
if ( plate - > GetFlags ( ) & plPlate : : kFlagIsAGraph )
{
plGraphPlate * graph = ( plGraphPlate * ) plate ;
hsPoint3 pt , pt2 ;
int i ;
if ( graph - > GetLabelText ( 0 ) [ 0 ] ! = 0 )
{
/// Draw key
const char * str ;
pt . Set ( - 0.5 , - 0.5 , 0 ) ;
pt = plate - > GetTransform ( ) * pt ;
pt . fX = pt . fX * scrnWidthDiv2 + scrnWidthDiv2 ;
pt . fY = pt . fY * scrnHeightDiv2 + scrnHeightDiv2 ;
pt . fY + = plDebugText : : Instance ( ) . GetFontHeight ( ) ;
UInt32 numLabels = graph - > GetNumLabels ( ) ;
if ( numLabels > graph - > GetNumColors ( ) )
numLabels = graph - > GetNumColors ( ) ;
for ( i = 0 ; i < numLabels ; i + + )
{
str = graph - > GetLabelText ( i ) ;
if ( str [ 0 ] = = 0 )
break ;
pt2 = pt ;
pt2 . fX - = plDebugText : : Instance ( ) . CalcStringWidth ( str ) ;
plDebugText : : Instance ( ) . DrawString ( ( UInt16 ) pt2 . fX , ( UInt16 ) pt2 . fY , str ,
graph - > GetDataColor ( i ) , plDebugText : : kStyleBold ) ;
pt . fY + = plDebugText : : Instance ( ) . GetFontHeight ( ) ;
}
}
}
}
}
dxPipe - > fCurrCullMode = ( dxPipe - > fLayerState [ 0 ] . fMiscFlags & hsGMatState : : kMiscTwoSided ) ? D3DCULL_NONE : oldCullMode ;
fD3DDevice - > SetRenderState ( D3DRS_CULLMODE , dxPipe - > fCurrCullMode ) ;
}
// IDrawPlate ///////////////////////////////////////////////////////////////////////
// Render this plate, in as many passes as it takes.
void plDXPipeline : : IDrawPlate ( plPlate * plate )
{
int i ;
hsGMaterial * material = plate - > GetMaterial ( ) ;
D3DXMATRIX mat ;
/// Set up the D3D transform directly
IMatrix44ToD3DMatrix ( mat , plate - > GetTransform ( ) ) ;
fD3DDevice - > SetTransform ( D3DTS_WORLD , & mat ) ;
mat = d3dIdentityMatrix ;
mat ( 1 , 1 ) = - 1.0f ;
mat ( 2 , 2 ) = 2.0f ;
mat ( 2 , 3 ) = 1.0f ;
mat ( 3 , 2 ) = - 2.0f ;
mat ( 3 , 3 ) = 0.0f ;
IPushPiggyBacks ( material ) ;
/// Draw the vertex buffer once for each material pass
for ( i = 0 ; i < material - > GetNumLayers ( ) ; )
{
// Stat gather adjust: since IHandleMaterial will count this in the stat gather,
// artificially decrement here so that the plates don't skew the stat gathering
// Taking this out. If the plates are causing more material changes, they should
// show up in the stats. mf
i = IHandleMaterial ( material , i , nil ) ;
ISetShaders ( nil , nil ) ;
// To override the transform done by the z-bias
fD3DDevice - > SetTransform ( D3DTS_PROJECTION , & mat ) ;
// And this to override cullmode set based on material 2-sidedness.
fD3DDevice - > SetRenderState ( D3DRS_CULLMODE , fCurrCullMode = D3DCULL_CW ) ;
WEAK_ERROR_CHECK ( fD3DDevice - > DrawPrimitive ( D3DPT_TRIANGLESTRIP , 0 , 2 ) ) ;
}
IPopPiggyBacks ( ) ;
}
///////////////////////////////////////////////////////////////////////////////
//// Error Message Stuff //////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// IAddErrorMessage ////////////////////////////////////////////////////
// Append the error string to the current error string.
void plDXPipeline : : IAddErrorMessage ( char * errStr )
{
static char str [ 512 ] ;
if ( errStr & & strlen ( errStr ) + strlen ( fSettings . fErrorStr ) < sizeof ( fSettings . fErrorStr ) - 4 )
{
strcpy ( str , fSettings . fErrorStr ) ;
sprintf ( fSettings . fErrorStr , " %s \n (%s) " , errStr , str ) ;
plStatusLog : : AddLineS ( " pipeline.log " , fSettings . fErrorStr ) ;
}
}
// ISetErrorMessage //////////////////////////////////////////////////////////
// Clear the current error string to the input string.
void plDXPipeline : : ISetErrorMessage ( char * errStr )
{
if ( errStr )
{
strcpy ( fSettings . fErrorStr , errStr ) ;
plStatusLog : : AddLineS ( " pipeline.log " , fSettings . fErrorStr ) ;
}
else
fSettings . fErrorStr [ 0 ] = nil ;
}
// IGetD3DError /////////////////////////////////////////////////////////////////
// Convert the last D3D error code to a string (probably "Conflicting Render State").
void plDXPipeline : : IGetD3DError ( )
{
sprintf ( fSettings . fErrorStr , " D3DError : %s " , ( char * ) DXGetErrorString9 ( fSettings . fDXError ) ) ;
}
// IShowErrorMessage /////////////////////////////////////////////////////////////
// Append the string to the running error string.
void plDXPipeline : : IShowErrorMessage ( char * errStr )
{
if ( errStr ! = nil )
IAddErrorMessage ( errStr ) ;
// hsAssert( false, fSettings.fErrorStr );
}
// ICreateFail ////////////////////////////////////////////////////////////////////
// Called on unrecoverable error during device creation. Frees up anything
// allocated so far, sets the error string, and returns true.
hsBool plDXPipeline : : ICreateFail ( char * errStr )
{
// Don't overwrite any error string we already had
if ( fSettings . fErrorStr [ 0 ] = = 0 )
IGetD3DError ( ) ;
if ( errStr & & * errStr )
{
IAddErrorMessage ( errStr ) ;
}
else if ( ! * fSettings . fErrorStr )
IAddErrorMessage ( " unknown " ) ;
IReleaseDeviceObjects ( ) ;
return true ;
}
// GetErrorString ///////////////////////////////////////////////////////////////////////////
// Return the current error string.
const char * plDXPipeline : : GetErrorString ( )
{
if ( fSettings . fErrorStr [ 0 ] = = 0 )
return nil ;
return fSettings . fErrorStr ;
}
///////////////////////////////////////////////////////////////////////////////
//// Miscellaneous Utility Functions //////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// GetDXBitDepth //////////////////////////////////////////////////////////
//
// From a D3DFORMAT enumeration, return the bit depth associated with it.
short plDXPipeline : : GetDXBitDepth ( D3DFORMAT format )
{
if ( format = = D3DFMT_UNKNOWN )
return 0 ;
else if ( format = = D3DFMT_R8G8B8 )
return 24 ;
else if ( format = = D3DFMT_A8R8G8B8 )
return 32 ;
else if ( format = = D3DFMT_X8R8G8B8 )
return 32 ;
else if ( format = = D3DFMT_R5G6B5 )
return 16 ;
else if ( format = = D3DFMT_X1R5G5B5 )
return 16 ;
else if ( format = = D3DFMT_A1R5G5B5 )
return 16 ;
else if ( format = = D3DFMT_A4R4G4B4 )
return 16 ;
else if ( format = = D3DFMT_R3G3B2 )
return 8 ;
else if ( format = = D3DFMT_A8 )
return 8 ;
else if ( format = = D3DFMT_A8R3G3B2 )
return 16 ;
else if ( format = = D3DFMT_X4R4G4B4 )
return 16 ;
else if ( format = = D3DFMT_A8P8 )
return 16 ;
else if ( format = = D3DFMT_P8 )
return 8 ;
else if ( format = = D3DFMT_L8 )
return 8 ;
else if ( format = = D3DFMT_A8L8 )
return 16 ;
else if ( format = = D3DFMT_A4L4 )
return 8 ;
else if ( format = = D3DFMT_V8U8 )
return 16 ;
else if ( format = = D3DFMT_L6V5U5 )
return 16 ;
else if ( format = = D3DFMT_X8L8V8U8 )
return 32 ;
else if ( format = = D3DFMT_Q8W8V8U8 )
return 32 ;
else if ( format = = D3DFMT_V16U16 )
return 32 ;
// else if( format == D3DFMT_W11V11U10 )
// return 32;
/* /// These formats really don't have bit depths associated with them
D3DFMT_UYVY
D3DFMT_YUY2
D3DFMT_DXT1
D3DFMT_DXT2
D3DFMT_DXT3
D3DFMT_DXT4
D3DFMT_DXT5
D3DFMT_VERTEXDATA
*/
else if ( format = = D3DFMT_D16_LOCKABLE )
return 16 ;
else if ( format = = D3DFMT_D32 )
return 32 ;
else if ( format = = D3DFMT_D15S1 )
return 16 ;
else if ( format = = D3DFMT_D24S8 )
return 32 ;
else if ( format = = D3DFMT_D16 )
return 16 ;
else if ( format = = D3DFMT_D24X8 )
return 32 ;
else if ( format = = D3DFMT_D24X4S4 )
return 32 ;
else if ( format = = D3DFMT_INDEX16 )
return 16 ;
else if ( format = = D3DFMT_INDEX32 )
return 32 ;
// Unsupported translation format--return 0
return 0 ;
}
//// IGetDXFormatName ////////////////////////////////////////////////////////
//
// From a D3DFORMAT enumeration, return the string for it.
const char * plDXPipeline : : IGetDXFormatName ( D3DFORMAT format )
{
switch ( format )
{
case D3DFMT_UNKNOWN : return " D3DFMT_UNKNOWN " ;
case D3DFMT_R8G8B8 : return " D3DFMT_R8G8B8 " ;
case D3DFMT_A8R8G8B8 : return " D3DFMT_A8R8G8B8 " ;
case D3DFMT_X8R8G8B8 : return " D3DFMT_X8R8G8B8 " ;
case D3DFMT_R5G6B5 : return " D3DFMT_R5G6B5 " ;
case D3DFMT_X1R5G5B5 : return " D3DFMT_X1R5G5B5 " ;
case D3DFMT_A1R5G5B5 : return " D3DFMT_A1R5G5B5 " ;
case D3DFMT_A4R4G4B4 : return " D3DFMT_A4R4G4B4 " ;
case D3DFMT_R3G3B2 : return " D3DFMT_R3G3B2 " ;
case D3DFMT_A8 : return " D3DFMT_A8 " ;
case D3DFMT_A8R3G3B2 : return " D3DFMT_A8R3G3B2 " ;
case D3DFMT_X4R4G4B4 : return " D3DFMT_X4R4G4B4 " ;
case D3DFMT_A8P8 : return " D3DFMT_A8P8 " ;
case D3DFMT_P8 : return " D3DFMT_P8 " ;
case D3DFMT_L8 : return " D3DFMT_L8 " ;
case D3DFMT_A8L8 : return " D3DFMT_A8L8 " ;
case D3DFMT_A4L4 : return " D3DFMT_A4L4 " ;
case D3DFMT_V8U8 : return " D3DFMT_V8U8 " ;
case D3DFMT_L6V5U5 : return " D3DFMT_L6V5U5 " ;
case D3DFMT_X8L8V8U8 : return " D3DFMT_X8L8V8U8 " ;
case D3DFMT_Q8W8V8U8 : return " D3DFMT_Q8W8V8U8 " ;
case D3DFMT_V16U16 : return " D3DFMT_V16U16 " ;
//case D3DFMT_W11V11U10: return "D3DFMT_W11V11U10";
case D3DFMT_UYVY : return " D3DFMT_UYVY " ;
case D3DFMT_YUY2 : return " D3DFMT_YUY2 " ;
case D3DFMT_DXT1 : return " D3DFMT_DXT1 " ;
// case D3DFMT_DXT2: return "D3DFMT_DXT2";
// case D3DFMT_DXT3: return "D3DFMT_DXT3";
// case D3DFMT_DXT4: return "D3DFMT_DXT4";
case D3DFMT_DXT5 : return " D3DFMT_DXT5 " ;
case D3DFMT_VERTEXDATA : return " D3DFMT_VERTEXDATA " ;
case D3DFMT_D16_LOCKABLE : return " D3DFMT_D16_LOCKABLE " ;
case D3DFMT_D32 : return " D3DFMT_D32 " ;
case D3DFMT_D15S1 : return " D3DFMT_D15S1 " ;
case D3DFMT_D24S8 : return " D3DFMT_D24S8 " ;
case D3DFMT_D16 : return " D3DFMT_D16 " ;
case D3DFMT_D24X8 : return " D3DFMT_D24X8 " ;
case D3DFMT_D24X4S4 : return " D3DFMT_D24X4S4 " ;
case D3DFMT_INDEX16 : return " D3DFMT_INDEX16 " ;
case D3DFMT_INDEX32 : return " D3DFMT_INDEX32 " ;
default : return " Bad format " ;
}
}
//// IFPUCheck ////////////////////////////////////////////////////////////////
// Checks the FPU to make sure it's in the right mode
// This should return wSave to allow it to be restored after rendering.
// This is obsolete as of DX8
void plDXPipeline : : IFPUCheck ( )
{
WORD wSave , wTemp ;
__asm fstcw wSave
if ( wSave & 0x300 | | // Not single mode
0x3f ! = ( wSave & 0x3f ) | | // Exceptions enabled
wSave & 0xC00 ) // Not round to nearest mode
{
__asm
{
mov ax , wSave
and ax , not 0x300 ; ; single mode
or ax , 0x3f ; ; disable all exceptions
and ax , not 0xC00 ; ; round to nearest mode
mov wTemp , ax
fldcw wTemp
}
}
}
// PushPiggyBackLayer /////////////////////////////////////////////////////
// Push a piggy back onto the stack.
plLayerInterface * plDXPipeline : : PushPiggyBackLayer ( plLayerInterface * li )
{
fPiggyBackStack . Push ( li ) ;
ISetNumActivePiggyBacks ( ) ;
fForceMatHandle = true ;
return li ;
}
// PopPiggyBackLayer ///////////////////////////////////////////////////////////////////
// Pull the piggy back out of the stack (if it's there).
plLayerInterface * plDXPipeline : : PopPiggyBackLayer ( plLayerInterface * li )
{
int idx = fPiggyBackStack . Find ( li ) ;
if ( fPiggyBackStack . kMissingIndex = = idx )
return nil ;
fPiggyBackStack . Remove ( idx ) ;
ISetNumActivePiggyBacks ( ) ;
fForceMatHandle = true ;
return li ;
}
// AppendLayerInterface ///////////////////////////////////////////////////////////////////
// Setup a layer wrapper to wrap around either all layers rendered with or just the base layers.
// Note that a single material has multiple base layers if it takes mutliple passes to render.
// Stays in effect until removed by RemoveLayerInterface.
plLayerInterface * plDXPipeline : : AppendLayerInterface ( plLayerInterface * li , hsBool onAllLayers )
{
fForceMatHandle = true ;
if ( onAllLayers )
return fOverAllLayer = li - > Attach ( fOverAllLayer ) ;
else
return fOverBaseLayer = li - > Attach ( fOverBaseLayer ) ;
}
// RemoveLayerInterface //////////////////////////////////////////////////////////////////
// Removes a layer wrapper installed by AppendLayerInterface.
plLayerInterface * plDXPipeline : : RemoveLayerInterface ( plLayerInterface * li , hsBool onAllLayers )
{
fForceMatHandle = true ;
if ( onAllLayers )
{
if ( ! fOverAllLayer )
return nil ;
return fOverAllLayer = fOverAllLayer - > Remove ( li ) ;
}
if ( ! fOverBaseLayer )
return nil ;
return fOverBaseLayer = fOverBaseLayer - > Remove ( li ) ;
}
///////////////////////////////////////////////////////////////////////////////
//// ShadowSection
//// Shadow specific internal functions
///////////////////////////////////////////////////////////////////////////////
// See plGLight/plShadowMaster.cpp for more notes.
// IAttachShadowsToReceivers ///////////////////////////////////////////////////////////
// For each active shadow map (in fShadows), attach it to all of the visible spans in drawable
// that it affects. Shadows explicitly attached via light groups are handled separately in ISetShadowFromGroup.
void plDXPipeline : : IAttachShadowsToReceivers ( plDrawableSpans * drawable , const hsTArray < Int16 > & visList )
{
int i ;
for ( i = 0 ; i < fShadows . GetCount ( ) ; i + + )
IAttachSlaveToReceivers ( i , drawable , visList ) ;
}
// IAttachSlaveToReceivers /////////////////////////////////////////////////////
// Find all the visible spans in this drawable affected by this shadow map,
// and attach it to them.
void plDXPipeline : : IAttachSlaveToReceivers ( int which , plDrawableSpans * drawable , const hsTArray < Int16 > & visList )
{
plShadowSlave * slave = fShadows [ which ] ;
// Whether the drawable is a character affects which lights/shadows affect it.
hsBool isChar = drawable - > GetNativeProperty ( plDrawable : : kPropCharacter ) ;
// If the shadow is part of a light group, it gets handled in ISetShadowFromGroup.
// Unless the drawable is a character (something that moves around indeterminately,
// like the avatar or a physical object), and the shadow affects all characters.
if ( slave - > ObeysLightGroups ( ) & & ! ( slave - > IncludesChars ( ) & & isChar ) )
return ;
// Do a space tree harvest looking for spans that are visible and whose bounds
// intercect the shadow volume.
plSpaceTree * space = drawable - > GetSpaceTree ( ) ;
static hsBitVector cache ;
cache . Clear ( ) ;
space - > EnableLeaves ( visList , cache ) ;
static hsTArray < Int16 > hitList ;
hitList . SetCount ( 0 ) ;
space - > HarvestEnabledLeaves ( slave - > fIsect , cache , hitList ) ;
// For the visible spans that intercect the shadow volume, attach the shadow
// to all appropriate for receiving this shadow map.
int i ;
for ( i = 0 ; i < hitList . GetCount ( ) ; i + + )
{
const plSpan * span = drawable - > GetSpan ( hitList [ i ] ) ;
hsGMaterial * mat = drawable - > GetMaterial ( span - > fMaterialIdx ) ;
// Check that the span isn't flagged as unshadowable, or has
// a material that we can't shadow onto.
if ( ! IReceivesShadows ( span , mat ) )
continue ;
// Check for self shadowing. If the shadow doesn't want self shadowing,
// and the span is part of the shadow caster, then skip.
if ( ! IAcceptsShadow ( span , slave ) )
continue ;
// Add it to this span's shadow list for this frame.
span - > AddShadowSlave ( fShadows [ which ] - > fIndex ) ;
}
}
// ISetShadowFromGroup ////////////////////////////////////////////////////////////////////////
// The light casting this shadow has been explicitly attached to this span, so no need
// for checking bounds, but we do anyway because the artists aren't very conservative
// along those lines. The light has a bitvector indicating which of the current shadows
// are from it (there will be a shadow map for each shadow-light/shadow-caster pair),
// so we look through those shadow maps and if they are acceptable, attach them to
// the span.
// Note that a shadow slave corresponds to a shadow map.
void plDXPipeline : : ISetShadowFromGroup ( plDrawableSpans * drawable , const plSpan * span , plLightInfo * liInfo )
{
hsGMaterial * mat = drawable - > GetMaterial ( span - > fMaterialIdx ) ;
// Check that this span/material combo can receive shadows at all.
if ( ! IReceivesShadows ( span , mat ) )
return ;
const hsBitVector & slaveBits = liInfo - > GetSlaveBits ( ) ;
int i ;
for ( i = 0 ; i < fShadows . GetCount ( ) ; i + + )
{
if ( slaveBits . IsBitSet ( fShadows [ i ] - > fIndex ) )
{
// Check self shadowing.
if ( IAcceptsShadow ( span , fShadows [ i ] ) )
{
// Check for overlapping bounds.
if ( fShadows [ i ] - > fIsect - > Test ( span - > fWorldBounds ) ! = kVolumeCulled )
span - > AddShadowSlave ( fShadows [ i ] - > fIndex ) ;
}
}
}
}
// SubmitShadowSlave ////////////////////////////////////////////////////////
// Puts the slave in a list valid for this frame only. The list will
// be preprocessed at BeginRender. See IPreprocessShadows.
void plDXPipeline : : SubmitShadowSlave ( plShadowSlave * slave )
{
// Check that it's a valid slave.
if ( ! ( slave & & slave - > fCaster & & slave - > fCaster - > GetKey ( ) ) )
return ;
// A board with limited projection capability (i.e. GeForce1) can't
// do perspective shadows (from point source lights) because it
// requires a count3 uvw on 2 texture units (0,1) simultaneously. Just skip.
if ( ( fSettings . fLimitedProj | | fSettings . fCantProj ) & & slave - > fView . GetPerspective ( ) )
return ;
// Ref the shadow caster so we're sure it will still be around when we go to
// render it.
slave - > fCaster - > GetKey ( ) - > RefObject ( ) ;
// Keep the shadow slaves in a priority sorted list. For performance reasons,
// we may want only the strongest N or those of a minimum priority.
int i ;
for ( i = 0 ; i < fShadows . GetCount ( ) ; i + + )
{
if ( slave - > fPriority < = fShadows [ i ] - > fPriority )
break ;
}
// Note that fIndex is no longer the index in the fShadows list, but
// is still used as a unique identifier for this slave.
slave - > fIndex = fShadows . GetCount ( ) ;
fShadows . Insert ( i , slave ) ;
}
hsScalar blurScale = - 1.f ;
static const int kL2NumSamples = 3 ; // Log2(4)
// IBlurShadowMap //////////////////////////////////////////////////////////////////
// For a shadow map, we've got a specific (non-general) blurring in mind.
// This could be used as a loose model for more general blurring, but you
// wouldn't want to run a generic texture or render target through this.
// Specifically, we assume:
// Input:
// An RGBA rendertarget with an alpha we want to preserve, and color
// going from black (unused) to white (written).
// A blur factor
// Output:
// The rendertarget with alpha preserved, and the color channel blurred
// appropriately.
// We'll want to minimize our render target changes, so
// we clear our scratch render target to black/white (color/alpha), then
// render additively the color of our input with a zero alpha. The scratch
// accumulates the color sum, but the alpha starts and stays saturated to 100%.
// Then we modulate that back into the input, so the alpha is unchanged, the
// color (within the white region) falls off at the edges. The color outside the
// white region is black and stays black, but we don't care because we'll be ignoring
// that anyway.
// Notice that this depends on the input, each pixel having been all black or all "white".
// Also depends on "white" having 1/N premodulated in, where N is the number of samples.
// That's why we can just sum up the colors, without needing to do a divide. Otherwise
// we'd saturate at 255 during the sum, and the divide would be pointless.
// One other thing we're counting on here, is that we've just been rendering to an
// offscreen, we're done, and we're about to pop our rendertarget, which is going
// to reset a lot of render state that we would otherwise be responsible for here.
// We're hoping that this blur function (if efficient enough) can get called enough times
// per frame to warrant the sins described above.
void plDXPipeline : : IBlurShadowMap ( plShadowSlave * slave )
{
plRenderTarget * smap = ( plRenderTarget * ) slave - > fPipeData ;
hsScalar scale = slave - > fBlurScale ;
// Find a scratch rendertarget which matches the input.
int which = IGetScratchRenderTarget ( smap ) ;
plRenderTarget * scratchRT = fBlurScratchRTs [ which ] ;
if ( ! scratchRT )
return ;
plRenderTarget * destRT = fBlurDestRTs [ which ] ;
if ( ! destRT )
return ;
// Set up to render into it.
IBlurSetRenderTarget ( scratchRT ) ;
// Clear it appropriately
fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_TARGET , 0xff000000L , 1.0f , 0L ) ;
// Setup our quad for rendering
ISetBlurQuadToRender ( smap ) ;
// Render the input image into the scratch image, creating the blur.
IRenderBlurFromShadowMap ( scratchRT , smap , scale ) ;
// Set the rendertarget back to src
// Setup renderstate to render it back modulating.
// Render the scratch back into the input.
IRenderBlurBackToShadowMap ( smap , scratchRT , destRT ) ;
// dst is now now slave's rendertarget and smap is the new scratch dst
// for this size.
slave - > fPipeData = ( void * ) destRT ;
fBlurDestRTs [ which ] = smap ;
}
// IGetScratchRenderTarget ////////////////////////////////////////////
// Look for a render target for as scratch space for blurring the input render target.
// Note that the whole blur process requires 3 render targets, the source,
// an intermediate, and the destination (which gets swapped with the source).
// But that's only an extra 2 render targets for all shadow maps of a given
// size.
// Note also that the intermediate is one size smaller than the source,
// to get better blurring through bilerp magnification.
int plDXPipeline : : IGetScratchRenderTarget ( plRenderTarget * smap )
{
int which = - 1 ;
switch ( smap - > GetHeight ( ) )
{
case 512 :
which = 9 ;
break ;
case 256 :
which = 8 ;
break ;
case 128 :
which = 7 ;
break ;
case 64 :
which = 6 ;
break ;
case 32 :
which = 5 ;
break ;
default :
return false ;
}
if ( ! fBlurScratchRTs [ which ] )
{
// We may or may not get back the size we requested here, but if we didn't,
// we aren't going to later, so we might as well stuff the smaller render target
// into the bigger slot. Bad thing is that we might want a smaller render target
// later, and we won't know to look in the bigger slot for it, so we could wind
// up using say two 128x128's (one in the 256 slot, one in the 128 slot).
// This intermediate is one power of 2 smaller than the source.
UInt32 width = smap - > GetWidth ( ) ;
UInt32 height = smap - > GetHeight ( ) ;
if ( width > 32 )
{
width > > = 1 ;
height > > = 1 ;
}
fBlurScratchRTs [ which ] = IFindRenderTarget ( width , height , smap - > GetFlags ( ) & plRenderTarget : : kIsOrtho ) ;
}
if ( ! fBlurDestRTs [ which ] )
{
// Destination is same size as source.
UInt32 width = smap - > GetWidth ( ) ;
UInt32 height = smap - > GetHeight ( ) ;
fBlurDestRTs [ which ] = IFindRenderTarget ( width , height , smap - > GetFlags ( ) & plRenderTarget : : kIsOrtho ) ;
}
# ifdef MF_ENABLE_HACKOFF
if ( hackOffscreens . kMissingIndex = = hackOffscreens . Find ( fBlurScratchRTs [ which ] ) )
hackOffscreens . Append ( fBlurScratchRTs [ which ] ) ;
if ( hackOffscreens . kMissingIndex = = hackOffscreens . Find ( fBlurDestRTs [ which ] ) )
hackOffscreens . Append ( fBlurDestRTs [ which ] ) ;
# endif // MF_ENABLE_HACKOFF
return which ;
}
// IBlurSetRenderTarget /////////////////////////////////////////////////////////////////////
// Set the input render target up to be rendered into. This abbreviated version
// of PushRenderTarget is possible because of the special case of the state coming
// in, and that we know we're going to immediately pop the previous render target
// when we're done.
void plDXPipeline : : IBlurSetRenderTarget ( plRenderTarget * rt )
{
plDXRenderTargetRef * ref = ( plDXRenderTargetRef * ) rt - > GetDeviceRef ( ) ;
// Set the rendertarget
IDirect3DSurface9 * main = ref - > GetColorSurface ( ) ;
IDirect3DSurface9 * depth = ref - > fD3DDepthSurface ;
fSettings . fCurrD3DMainSurface = main ;
fSettings . fCurrD3DDepthSurface = depth ;
fD3DDevice - > SetRenderTarget ( 0 , main ) ;
fD3DDevice - > SetDepthStencilSurface ( depth ) ;
// Now set the correct viewport
D3DVIEWPORT9 vp = { 0 ,
0 ,
rt - > GetWidth ( ) ,
rt - > GetHeight ( ) ,
0.f , 1.f } ;
WEAK_ERROR_CHECK ( fD3DDevice - > SetViewport ( & vp ) ) ;
}
// IRenderBlurFromShadowMap ////////////////////////////////////////////////////////////////////////////////
// Render a shadow map into a scratch render target multiple times offset slightly to create a blur
// in the color, preserving alpha exactly. It's just rendering a single quad with slight offsets
// in the UVW transform.
void plDXPipeline : : IRenderBlurFromShadowMap ( plRenderTarget * scratchRT , plRenderTarget * smap , hsScalar scale )
{
// Quad is set up in camera space.
fD3DDevice - > SetTransform ( D3DTS_VIEW , & d3dIdentityMatrix ) ;
fD3DDevice - > SetTransform ( D3DTS_WORLD , & d3dIdentityMatrix ) ;
fD3DDevice - > SetTransform ( D3DTS_PROJECTION , & d3dIdentityMatrix ) ;
// Figure out how many passes we'll need.
// const int kNumSamples = 1 << kL2NumSamples; // HACKSAMPLE
const int kNumSamples = mfCurrentTest > 101 ? 8 : 4 ;
int nPasses = ( int ) hsCeil ( float ( kNumSamples ) / fSettings . fMaxLayersAtOnce ) ;
int nSamplesPerPass = kNumSamples / nPasses ;
// Attenuate by number of passes, to average as we sum.
DWORD atten = 255 / nPasses ;
plConst ( float ) kAtten ( 1.f ) ;
atten = DWORD ( atten * kAtten ) ;
atten = ( atten < < 24 )
| ( atten < < 16 )
| ( atten < < 8 )
| ( atten < < 0 ) ;
// Disable skinning
fD3DDevice - > SetRenderState ( D3DRS_VERTEXBLEND , D3DVBF_DISABLE ) ;
//
// AlphaEnable = true
// AlphaTest OFF
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_SRCALPHA ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_ONE ) ;
fD3DDevice - > SetRenderState ( D3DRS_ALPHAFUNC , D3DCMP_ALWAYS ) ;
// ZBUFFER disabled
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_ALWAYS ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , FALSE ) ;
fLayerState [ 0 ] . fZFlags & = ~ hsGMatState : : kZMask ;
fLayerState [ 0 ] . fZFlags | = hsGMatState : : kZNoZWrite | hsGMatState : : kZNoZRead ;
//
// Cullmode is NONE
fCurrCullMode = D3DCULL_NONE ;
fD3DDevice - > SetRenderState ( D3DRS_CULLMODE , fCurrCullMode ) ;
plDXTextureRef * ref = ( plDXTextureRef * ) smap - > GetDeviceRef ( ) ;
hsAssert ( ref , " Shadow map ref should have been made when it was rendered " ) ;
if ( ! ref )
return ;
// TFactor contains the attenuation
fD3DDevice - > SetRenderState ( D3DRS_TEXTUREFACTOR , atten ) ;
// Set the N texture stages all to use the same
// src rendertarget texture.
// Blend modes are:
// Stage0:
// Color
// Arg1 = texture
// Op = selectArg1
// Alpha
// Arg1 = TFACTOR = white
// Op = selectArg1
// Stage[1..n-1]
// Color
// Arg1 = texture
// Arg2 = current
// Op = AddSigned
// Alpha
// Arg1 = texture
// Arg2 = current
// Op = SelectArg2
// StageN
// Color/Alpha
// Op = disable
//
// Frame buffer blend is
// SRCBLEND = ONE
// DSTBLEND = ONE
// All texture stages are clamped
//
// Set stage0, then loop over the rest
fD3DDevice - > SetSamplerState ( 0 , D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ) ;
fD3DDevice - > SetSamplerState ( 0 , D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ) ;
fLayerState [ 0 ] . fClampFlags = hsGMatState : : kClampTexture ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_SELECTARG1 ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TFACTOR ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_SELECTARG1 ) ;
fLayerState [ 0 ] . fBlendFlags = UInt32 ( - 1 ) ;
hsRefCnt_SafeAssign ( fLayerRef [ 0 ] , ref ) ;
fD3DDevice - > SetTexture ( 0 , ref - > fD3DTexture ) ;
if ( D3DTTFF_COUNT2 ! = fLayerXformFlags [ 0 ] )
{
fLayerXformFlags [ 0 ] = D3DTTFF_COUNT2 ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_TEXTURETRANSFORMFLAGS , D3DTTFF_COUNT2 ) ;
}
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_TEXCOORDINDEX , 0 ) ;
fLayerUVWSrcs [ 0 ] = 0 ;
int i ;
for ( i = 1 ; i < nSamplesPerPass ; i + + )
{
fD3DDevice - > SetSamplerState ( i , D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ) ;
fD3DDevice - > SetSamplerState ( i , D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ) ;
fLayerState [ i ] . fClampFlags = hsGMatState : : kClampTexture ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_COLORARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_COLOROP , D3DTOP_ADDSIGNED ) ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
fLayerState [ i ] . fBlendFlags = UInt32 ( - 1 ) ;
hsRefCnt_SafeAssign ( fLayerRef [ i ] , ref ) ;
fD3DDevice - > SetTexture ( i , ref - > fD3DTexture ) ;
if ( D3DTTFF_COUNT2 ! = fLayerXformFlags [ i ] )
{
fLayerXformFlags [ i ] = D3DTTFF_COUNT2 ;
fD3DDevice - > SetTextureStageState ( i , D3DTSS_TEXTURETRANSFORMFLAGS , D3DTTFF_COUNT2 ) ;
}
fD3DDevice - > SetTextureStageState ( i , D3DTSS_TEXCOORDINDEX , 0 ) ;
fLayerUVWSrcs [ i ] = 0 ;
}
fD3DDevice - > SetTextureStageState ( nSamplesPerPass , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
fD3DDevice - > SetTextureStageState ( nSamplesPerPass , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
// N offsets are { (-1,-1), (1, -1), (1, 1), (-1, 1) } * offsetScale / size, with
// useful offsetScales probably going from 0.5 to 1.5, but we'll just have
// to experiment and see. Larger values likely to require more than the current
// 4 samples
struct offsetStruct
{
float fU ;
float fV ;
} ;
offsetStruct offsetScale = { scale / scratchRT - > GetWidth ( ) , scale / scratchRT - > GetHeight ( ) } ;
static offsetStruct offsets [ 8 ] = {
{ - 1.f , - 1.f } ,
{ 1.f , - 1.f } ,
{ 1.f , 1.f } ,
{ - 1.f , 1.f } ,
{ 0.f , - 0.5f } ,
{ 0.f , 0.5f } ,
{ - 0.5f , 0.f } ,
{ 0.5f , 0.f }
} ;
int iSample = 0 ;
// For each pass,
for ( i = 0 ; i < nPasses ; i + + )
{
// Set the N texture stage uv transforms to the
// next N offsets.
int j ;
for ( j = 0 ; j < nSamplesPerPass ; j + + )
{
D3DXMATRIX offXfm = d3dIdentityMatrix ;
offXfm ( 2 , 0 ) = offsets [ iSample ] . fU * offsetScale . fU ;
offXfm ( 2 , 1 ) = offsets [ iSample ] . fV * offsetScale . fV ;
fD3DDevice - > SetTransform ( sTextureStages [ j ] , & offXfm ) ;
fLayerTransform [ j ] = true ;
iSample + + ;
}
// Render our quad
fD3DDevice - > DrawPrimitive ( D3DPT_TRIANGLESTRIP , 0 , 2 ) ;
// fD3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, 0L);
// fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
// fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
// fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADDSIGNED);
// fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
}
}
// IRenderBlurBackToShadowMap /////////////////////////////////////////////////////////////////////
// Render our intermediate blurred map back into a useable shadow map.
void plDXPipeline : : IRenderBlurBackToShadowMap ( plRenderTarget * smap , plRenderTarget * scratch , plRenderTarget * dst )
{
// Set the rendertarget
IBlurSetRenderTarget ( dst ) ;
// Clear it appropriately. This might not be necessary, since we're just going to overwrite.
fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_TARGET , 0xff000000L , 1.0f , 0L ) ;
// Scratch has an all white alpha, and the blurred color from smap. But the color
// is a signed biased color. We need to remap [128..255] from scratch into [0..255]
// on dst. Plus, we need to copy the alpha as is from smap into dst.
// So, scratch is texture0, smap is texture1. TFACTOR is 0.
// Color is ADDSIGNED2X(TFACTOR, texture0).
// Alpha is SELECTARG1(texture1, current).
// Then FB blend is just opaque copy.
// Set Stage0 texture transform
// Clamp still on (from RBFSM)
D3DXMATRIX offXfm = d3dIdentityMatrix ;
fD3DDevice - > SetTransform ( sTextureStages [ 0 ] , & offXfm ) ;
fD3DDevice - > SetTransform ( sTextureStages [ 1 ] , & offXfm ) ;
fLayerTransform [ 0 ] = false ;
fLayerTransform [ 1 ] = false ;
plDXTextureRef * ref = ( plDXTextureRef * ) scratch - > GetDeviceRef ( ) ;
hsAssert ( ref , " Blur scratch map ref should have been made when it was rendered " ) ;
if ( ! ref )
return ;
hsRefCnt_SafeAssign ( fLayerRef [ 0 ] , ref ) ;
fD3DDevice - > SetTexture ( 0 , ref - > fD3DTexture ) ;
ref = ( plDXTextureRef * ) smap - > GetDeviceRef ( ) ;
hsAssert ( ref , " Blur src map ref should have been made when it was rendered " ) ;
if ( ! ref )
return ;
hsRefCnt_SafeAssign ( fLayerRef [ 1 ] , ref ) ;
fD3DDevice - > SetTexture ( 1 , ref - > fD3DTexture ) ;
// Stage0:
// Color
// Arg1 = TFACTOR = black
// Arg2 = texture
// Op = ADDSIGNED2X
// Alpha
// Arg1 = texture
// Op = selectArg1
// Texture = scratch
// Stage1:
// Color
// Arg1 = texture
// Arg2 = current
// Op = selectArg2
// Alpha
// Arg1 = texture
// Op = selectArg1
// Texture = smap
// FB blend
// SRCBLEND = ONE
// DSTBLEND = ZERO
fD3DDevice - > SetRenderState ( D3DRS_TEXTUREFACTOR , 0L ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TFACTOR ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_ADDSIGNED2X ) ;
// This alpha will be ignored, because in the next stage we select texture alpha.
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_SELECTARG1 ) ;
fLayerState [ 0 ] . fBlendFlags = UInt32 ( - 1 ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_COLOROP , D3DTOP_SELECTARG2 ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_ALPHAOP , D3DTOP_SELECTARG1 ) ;
fLayerState [ 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
fD3DDevice - > SetTextureStageState ( 2 , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
fD3DDevice - > SetTextureStageState ( 2 , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLastEndingStage = 2 ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_ONE ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_ZERO ) ;
// Our quad should still be setup to go.
fD3DDevice - > DrawPrimitive ( D3DPT_TRIANGLESTRIP , 0 , 2 ) ;
}
struct plShadowVertStruct
{
float fPos [ 3 ] ;
float fUv [ 2 ] ;
} ;
// IReleaseBlurVBuffers //////////////////////////////////////////////////////////
// Free up our blur quad vertex buffers. Note these are in POOL_DEFAULT
void plDXPipeline : : IReleaseBlurVBuffers ( )
{
const UInt32 kVSize = sizeof ( plShadowVertStruct ) ;
int i ;
for ( i = 0 ; i < kMaxRenderTargetNext ; i + + )
{
if ( fBlurVBuffers [ i ] )
{
ReleaseObject ( fBlurVBuffers [ i ] ) ;
PROFILE_POOL_MEM ( D3DPOOL_DEFAULT , 4 * kVSize , false , " BlurVtxBuff " ) ;
fBlurVBuffers [ i ] = nil ;
}
}
}
// ICreateBlurVBuffers //////////////////////////////////////////////////////////////////
// We need a quad for each size of shadow map, because there's a slight dependency
// of UVW coordinates on size of render target. Sucks but it's true.
hsBool plDXPipeline : : ICreateBlurVBuffers ( )
{
// vertex size is 4 verts, with 4 floats each for position, and 2 floats each for uv.
const UInt32 kVSize = sizeof ( plShadowVertStruct ) ;
const UInt32 kVFormat = D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2 ( 0 ) ;
int i ;
for ( i = 0 ; i < kMaxRenderTargetNext ; i + + )
{
int width = 0 ;
int height = 0 ;
int which = - 1 ;
switch ( i )
{
default :
case 0 :
case 1 :
case 2 :
case 3 :
case 4 :
break ;
case 5 :
width = height = 1 < < i ;
which = i ;
break ;
case 6 :
width = height = 1 < < i ;
which = i ;
break ;
case 7 :
width = height = 1 < < i ;
which = i ;
break ;
case 8 :
width = height = 1 < < i ;
which = i ;
break ;
case 9 :
width = height = 1 < < i ;
which = i ;
break ;
}
if ( which < 0 )
continue ;
// positions are { (-0.5,-0.5,0,1), (w-0.5,-0.5,0,1), (w-0.5,h-0.5,0,1), (-0.5,h-0.5,0,1) }
// UVs are { (0,0), (1,0), (1,1), (0,1) }
// So we won't have to bother with indices, we'll put them in as
// p1, p2, p0, p3 and render tristrip
// Create the buffer.
IDirect3DVertexBuffer9 * vBuffer = nil ;
UInt32 fvfFormat = kVFormat ;
hsAssert ( ! ManagedAlloced ( ) , " Alloc default with managed alloc'd " ) ;
if ( FAILED ( fD3DDevice - > CreateVertexBuffer ( 4 * kVSize ,
D3DUSAGE_WRITEONLY ,
fvfFormat ,
D3DPOOL_DEFAULT ,
& vBuffer , NULL ) ) )
{
hsAssert ( false , " CreateVertexBuffer() call failed! " ) ;
return false ;
}
plShadowVertStruct * ptr = nil ;
/// Lock the buffer and fill it in.
if ( FAILED ( vBuffer - > Lock ( 0 , 0 , ( void * * ) & ptr , 0 ) ) )
{
hsAssert ( false , " Failed to lock vertex buffer for writing " ) ;
vBuffer - > Release ( ) ;
return false ;
}
PROFILE_POOL_MEM ( D3DPOOL_DEFAULT , 4 * kVSize , true , " BlurVtxBuff " ) ;
plShadowVertStruct vert ;
vert . fPos [ 0 ] = - 1.f ;
vert . fPos [ 1 ] = - 1.f ;
vert . fPos [ 2 ] = 0.5f ;
vert . fUv [ 0 ] = 0.5f / width ;
vert . fUv [ 1 ] = 1.f + 0.5f / height ;
// P0
ptr [ 2 ] = vert ;
// P1
ptr [ 0 ] = vert ;
ptr [ 0 ] . fPos [ 0 ] + = 2.f ;
ptr [ 0 ] . fUv [ 0 ] + = 1.f ;
// P2
ptr [ 1 ] = vert ;
ptr [ 1 ] . fPos [ 0 ] + = 2.f ;
ptr [ 1 ] . fUv [ 0 ] + = 1.f ;
ptr [ 1 ] . fPos [ 1 ] + = 2.f ;
ptr [ 1 ] . fUv [ 1 ] - = 1.f ;
// P3
ptr [ 3 ] = vert ;
ptr [ 3 ] . fPos [ 1 ] + = 2.f ;
ptr [ 3 ] . fUv [ 1 ] - = 1.f ;
vBuffer - > Unlock ( ) ;
fBlurVBuffers [ which ] = vBuffer ;
}
return true ;
}
// ISetBlurQuadToRender ////////////////////////////////////////////////////
// Select the appropriate blur quad (based on size of shadow map) and set it up to render.
hsBool plDXPipeline : : ISetBlurQuadToRender ( plRenderTarget * smap )
{
const UInt32 kVSize = sizeof ( plShadowVertStruct ) ;
const UInt32 kVFormat = D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2 ( 0 ) ;
// Each vb will be rendertarget size specific, so select one based on input rendertarget
int which = - 1 ;
switch ( smap - > GetHeight ( ) )
{
case 512 :
which = 9 ;
break ;
case 256 :
which = 8 ;
break ;
case 128 :
which = 7 ;
break ;
case 64 :
which = 6 ;
break ;
case 32 :
which = 5 ;
break ;
default :
return false ;
}
// If we haven't created (or have lost) our d3d resources, make them
IDirect3DVertexBuffer9 * vBuffer = fBlurVBuffers [ which ] ;
if ( ! vBuffer )
{
ICreateBlurVBuffers ( ) ;
vBuffer = fBlurVBuffers [ which ] ;
hsAssert ( vBuffer , " AllocBlurVBuffers failed " ) ;
}
HRESULT r = fD3DDevice - > SetVertexShader ( fSettings . fCurrVertexShader = NULL ) ;
fD3DDevice - > SetFVF ( fSettings . fCurrFVFFormat = kVFormat ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the vertex shader! " ) ;
hsRefCnt_SafeUnRef ( fSettings . fCurrVertexBuffRef ) ;
fSettings . fCurrVertexBuffRef = nil ;
r = fD3DDevice - > SetStreamSource ( 0 , vBuffer , 0 , kVSize ) ;
plProfile_Inc ( VertexChange ) ;
// No SetIndices, we'll do a direct DrawPrimitive (not DrawIndexedPrimitive)
// No transforms, we're supplying screen ready verts.
return true ;
}
// IRenderShadowCasterSpan //////////////////////////////////////////////////////////////////////
// Render the span into a rendertarget of the correct size, generating
// a depth map from this light to that span.
void plDXPipeline : : IRenderShadowCasterSpan ( plShadowSlave * slave , plDrawableSpans * drawable , const plIcicle & span )
{
// Check that it's ready to render.
plProfile_BeginTiming ( CheckDyn ) ;
ICheckDynBuffers ( drawable , drawable - > GetBufferGroup ( span . fGroupIdx ) , & span ) ;
plProfile_EndTiming ( CheckDyn ) ;
plDXVertexBufferRef * vRef = ( plDXVertexBufferRef * ) drawable - > GetVertexRef ( span . fGroupIdx , span . fVBufferIdx ) ;
plDXIndexBufferRef * iRef = ( plDXIndexBufferRef * ) drawable - > GetIndexRef ( span . fGroupIdx , span . fIBufferIdx ) ;
HRESULT r ;
if ( vRef - > fD3DBuffer = = nil | | iRef - > fD3DBuffer = = nil )
{
hsAssert ( false , " Trying to render a nil buffer pair! " ) ;
return ;
}
/// Switch to the vertex buffer we want
if ( fSettings . fCurrVertexBuffRef ! = vRef )
{
hsRefCnt_SafeAssign ( fSettings . fCurrVertexBuffRef , vRef ) ;
hsAssert ( vRef - > fD3DBuffer ! = nil , " Trying to render a buffer pair without a vertex buffer! " ) ;
vRef - > SetRebuiltSinceUsed ( true ) ;
}
if ( vRef - > RebuiltSinceUsed ( ) )
{
r = fD3DDevice - > SetStreamSource ( 0 , vRef - > fD3DBuffer , 0 , vRef - > fVertexSize ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the stream source! " ) ;
plProfile_Inc ( VertexChange ) ;
fSettings . fCurrFVFFormat = IGetBufferD3DFormat ( vRef - > fFormat ) ;
r = fD3DDevice - > SetVertexShader ( fSettings . fCurrVertexShader = NULL ) ;
fD3DDevice - > SetFVF ( fSettings . fCurrFVFFormat ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the vertex shader! " ) ;
vRef - > SetRebuiltSinceUsed ( false ) ;
}
if ( fSettings . fCurrIndexBuffRef ! = iRef )
{
hsRefCnt_SafeAssign ( fSettings . fCurrIndexBuffRef , iRef ) ;
hsAssert ( iRef - > fD3DBuffer ! = nil , " Trying to render with a nil index buffer " ) ;
iRef - > SetRebuiltSinceUsed ( true ) ;
}
if ( iRef - > RebuiltSinceUsed ( ) )
{
r = fD3DDevice - > SetIndices ( iRef - > fD3DBuffer ) ;
hsAssert ( r = = D3D_OK , " Error trying to set the indices! " ) ;
plProfile_Inc ( IndexChange ) ;
iRef - > SetRebuiltSinceUsed ( false ) ;
}
UInt32 vStart = span . fVStartIdx ;
UInt32 vLength = span . fVLength ;
UInt32 iStart = span . fIPackedIdx ;
UInt32 iLength = span . fILength ;
plRenderTriListFunc render ( fD3DDevice , iRef - > fOffset , vStart , vLength , iStart , iLength / 3 ) ;
static hsMatrix44 emptyMatrix ;
hsMatrix44 m = emptyMatrix ;
ISetupTransforms ( drawable , span , m ) ;
hsBool flip = slave - > ReverseCull ( ) ;
ISetCullMode ( flip ) ;
render . RenderPrims ( ) ;
}
// IGetULutTextureRef ///////////////////////////////////////////////////////////
// The ULut just translates a U coordinate in range [0..1] into
// color and alpha of U * 255.9f. We just have the one we keep
// lying around.
plDXTextureRef * plDXPipeline : : IGetULutTextureRef ( )
{
const int width = 256 ;
const int height = 1 ;
if ( ! fULutTextureRef )
{
UInt32 * tData = TRACKED_NEW UInt32 [ width * height ] ;
UInt32 * pData = tData ;
int j ;
for ( j = 0 ; j < height ; j + + )
{
int i ;
for ( i = 0 ; i < width ; i + + )
{
* pData = ( i < < 24 )
| ( i < < 16 )
| ( i < < 8 )
| ( i < < 0 ) ;
pData + + ;
}
}
plDXTextureRef * ref = TRACKED_NEW plDXTextureRef ( D3DFMT_A8R8G8B8 ,
1 , // Num mip levels
width , height , // width by height
width * height , // numpix
width * height * sizeof ( UInt32 ) , // totalsize
width * height * sizeof ( UInt32 ) ,
nil , // levels data
tData ,
false // externData
) ;
ref - > Link ( & fTextureRefList ) ;
fULutTextureRef = ref ;
}
return fULutTextureRef ;
}
// IFindRenderTarget //////////////////////////////////////////////////////////////////
// Find a matching render target from the pools. We prefer the requested size, but
// will look for a smaller size if there isn't one available.
// Param ortho indicates whether it will be used for orthogonal projection as opposed
// to perspective (directional light vs. point light), but is no longer used.
plRenderTarget * plDXPipeline : : IFindRenderTarget ( UInt32 & width , UInt32 & height , hsBool ortho )
{
hsTArray < plRenderTarget * > * pool = nil ;
UInt32 * iNext = nil ;
// NOT CURRENTLY SUPPORTING NON-SQUARE SHADOWS. IF WE DO, CHANGE THIS.
switch ( height )
{
case 512 :
pool = & fRenderTargetPool512 ;
iNext = & fRenderTargetNext [ 9 ] ;
break ;
case 256 :
pool = & fRenderTargetPool256 ;
iNext = & fRenderTargetNext [ 8 ] ;
break ;
case 128 :
pool = & fRenderTargetPool128 ;
iNext = & fRenderTargetNext [ 7 ] ;
break ;
case 64 :
pool = & fRenderTargetPool64 ;
iNext = & fRenderTargetNext [ 6 ] ;
break ;
case 32 :
pool = & fRenderTargetPool32 ;
iNext = & fRenderTargetNext [ 5 ] ;
break ;
default :
return nil ;
}
plRenderTarget * rt = ( * pool ) [ * iNext ] ;
if ( ! rt )
{
// We didn't find one, try again the next size down.
if ( height > 32 )
return IFindRenderTarget ( width > > = 1 , height > > = 1 , ortho ) ;
// We must be totally out. Oh well.
return nil ;
}
( * iNext ) + + ;
return rt ;
}
// IPushShadowCastState ////////////////////////////////////////////////////////////////////////////////
// Push all the state necessary to start rendering this shadow map, but independent of the
// actual shadow caster to be rendered into the map.
hsBool plDXPipeline : : IPushShadowCastState ( plShadowSlave * slave )
{
plRenderTarget * renderTarg = IFindRenderTarget ( slave - > fWidth , slave - > fHeight , slave - > fView . GetOrthogonal ( ) ) ;
if ( ! renderTarg )
return false ;
// Let the slave setup the transforms, viewport, etc. necessary to render it's shadow
// map. This just goes into a plViewTransform, we translate that into D3D state ourselves below.
if ( ! slave - > SetupViewTransform ( this ) )
return false ;
// Turn off fogging and specular.
fD3DDevice - > SetRenderState ( D3DRS_FOGENABLE , FALSE ) ;
fCurrFog . fEnvPtr = nil ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARENABLE , FALSE ) ;
fLayerState [ 0 ] . fShadeFlags & = ~ hsGMatState : : kShadeSpecular ;
// Push the shadow slave's view transform as our current render state.
fSettings . fViewStack . Push ( fView ) ;
fView . fCullMaxNodes = 0 ;
SetViewTransform ( slave - > fView ) ;
IProjectionMatrixToD3D ( ) ;
// Push the shadow map as the current render target
PushRenderTarget ( renderTarg ) ;
// We'll be rendering the light space distance to the span fragment into
// alpha (color is white), so our camera space position, transformed into light space
// and then converted to [0..255] via our ULut.
// For stage 0:
// Set uvw src
if ( fLayerUVWSrcs [ 0 ] ! = D3DTSS_TCI_CAMERASPACEPOSITION )
{
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_TEXCOORDINDEX , D3DTSS_TCI_CAMERASPACEPOSITION ) ;
fLayerUVWSrcs [ 0 ] = D3DTSS_TCI_CAMERASPACEPOSITION ;
}
UInt32 xformFlags = D3DTTFF_COUNT3 ;
if ( xformFlags ! = fLayerXformFlags [ 0 ] )
{
fLayerXformFlags [ 0 ] = xformFlags ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_TEXTURETRANSFORMFLAGS , xformFlags ) ;
}
// Set texture transform to slave's lut transform. See plShadowMaster::IComputeLUT().
hsMatrix44 castLUT = slave - > fCastLUT ;
if ( slave - > fFlags & plShadowSlave : : kCastInCameraSpace )
{
hsMatrix44 c2w = GetCameraToWorld ( ) ;
castLUT = castLUT * c2w ;
}
D3DXMATRIX tXfm ;
IMatrix44ToD3DMatrix ( tXfm , castLUT ) ;
fD3DDevice - > SetTransform ( sTextureStages [ 0 ] , & tXfm ) ;
fLayerTransform [ 0 ] = true ;
// Set texture to clamp
fD3DDevice - > SetSamplerState ( 0 , D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ) ;
fD3DDevice - > SetSamplerState ( 0 , D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ) ;
fLayerState [ 0 ] . fClampFlags = hsGMatState : : kClampTexture ;
DWORD clearColor = 0xff000000L ;
// const int l2NumSamples = kL2NumSamples; // HACKSAMPLE
const int l2NumSamples = mfCurrentTest > 101 ? 3 : 2 ;
DWORD intens ;
if ( slave - > fBlurScale > 0 )
{
const int kNumSamples = mfCurrentTest > 101 ? 8 : 4 ;
int nPasses = ( int ) hsCeil ( float ( kNumSamples ) / fSettings . fMaxLayersAtOnce ) ;
int nSamplesPerPass = kNumSamples / nPasses ;
DWORD k = int ( 128.f / float ( nSamplesPerPass ) ) ;
intens = ( 0xff < < 24 )
| ( ( 128 + k ) < < 16 )
| ( ( 128 + k ) < < 8 )
| ( ( 128 + k ) < < 0 ) ;
clearColor = ( 0xff < < 24 )
| ( ( 128 - k ) < < 16 )
| ( ( 128 - k ) < < 8 )
| ( ( 128 - k ) < < 0 ) ;
}
else
intens = 0xffffffff ;
// Note that we discard the shadow caster's alpha here, although we don't
// need to. Even on a 2 texture stage system, we could include the diffuse
// alpha and the texture alpha from the base texture. But we don't.
// Set color to white. We could accomplish this easier by making the color
// in our ULut white.
fD3DDevice - > SetRenderState ( D3DRS_TEXTUREFACTOR , intens ) ;
fSettings . fVeryAnnoyingTextureInvalidFlag = true ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TFACTOR ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_SELECTARG1 ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_SELECTARG1 ) ;
fLayerState [ 0 ] . fBlendFlags = UInt32 ( - 1 ) ;
// For stage 1 - disable
fLastEndingStage = 1 ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLayerState [ 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
// Set texture to U_LUT
plDXTextureRef * ref = IGetULutTextureRef ( ) ;
if ( ! ref - > fD3DTexture )
{
if ( ref - > fData )
IReloadTexture ( ref ) ;
}
hsRefCnt_SafeAssign ( fLayerRef [ 0 ] , ref ) ;
fD3DDevice - > SetTexture ( 0 , ref - > fD3DTexture ) ;
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_ONE ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_ZERO ) ;
fD3DDevice - > SetRenderState ( D3DRS_ALPHAFUNC , D3DCMP_ALWAYS ) ;
slave - > fPipeData = renderTarg ;
// Enable ZBuffering w/ write
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , TRUE ) ;
fLayerState [ 0 ] . fZFlags & = ~ hsGMatState : : kZMask ;
// Clear the render target:
// alpha to white ensures no shadow where there's no caster
// color to black in case we ever get blurring going
// Z to 1
// Stencil ignored
if ( slave - > ReverseZ ( ) )
{
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_GREATEREQUAL ) ;
fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER , clearColor , 0.0f , 0L ) ;
}
else
{
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_LESSEQUAL ) ;
fD3DDevice - > Clear ( 0 , nil , D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER , clearColor , 1.0f , 0L ) ;
}
// Bring the viewport in (AFTER THE CLEAR) to protect the alpha boundary.
fView . fTransform . SetViewPort ( 1 , 1 , ( float ) ( slave - > fWidth - 2 ) , ( float ) ( slave - > fHeight - 2 ) , false ) ;
ISetViewport ( ) ;
inlEnsureLightingOff ( ) ;
// See ISetupShadowLight below for how the shadow light is used.
// The shadow light isn't used in generating the shadow map, it's used
// in projecting the shadow map onto the scene.
ISetupShadowLight ( slave ) ;
return true ;
}
// ISetupShadowLight //////////////////////////////////////////////////////////////////
// We use the shadow light to modulate the shadow effect in two ways while
// projecting the shadow map onto the scene.
// First, the intensity of the shadow follows the N dot L of the light on
// the surface being projected onto. So on a sphere, the darkening effect
// of the shadow will fall off as the normals go from pointing to the light to
// pointing 90 degrees off.
// Second, we attenuate the whole shadow effect through the lights diffuse color.
// We attenuate for different reasons, like the intensity of the light, or
// to fade out a shadow as it gets too far in the distance to matter.
void plDXPipeline : : ISetupShadowLight ( plShadowSlave * slave )
{
plDXLightRef * lRef = INextShadowLight ( slave ) ;
lRef - > fD3DInfo . Diffuse . r
= lRef - > fD3DInfo . Diffuse . g
= lRef - > fD3DInfo . Diffuse . b
= slave - > fPower ;
slave - > fSelfShadowOn = false ;
if ( slave - > Positional ( ) )
{
hsPoint3 position = slave - > fLightPos ;
lRef - > fD3DInfo . Position . x = position . fX ;
lRef - > fD3DInfo . Position . y = position . fY ;
lRef - > fD3DInfo . Position . z = position . fZ ;
const float maxRange = 32767.f ;
lRef - > fD3DInfo . Range = maxRange ;
lRef - > fD3DInfo . Attenuation0 = 1.f ;
lRef - > fD3DInfo . Attenuation1 = 0 ;
lRef - > fD3DInfo . Attenuation2 = 0 ;
lRef - > fD3DInfo . Type = D3DLIGHT_POINT ;
}
else
{
hsVector3 dir = slave - > fLightDir ;
lRef - > fD3DInfo . Direction . x = dir . fX ;
lRef - > fD3DInfo . Direction . y = dir . fY ;
lRef - > fD3DInfo . Direction . z = dir . fZ ;
lRef - > fD3DInfo . Type = D3DLIGHT_DIRECTIONAL ;
}
fD3DDevice - > SetLight ( lRef - > fD3DIndex , & lRef - > fD3DInfo ) ;
slave - > fLightIndex = lRef - > fD3DIndex ;
}
// INextShadowLight /////////////////////////////////////////////////////
// Get a scratch light for this shadow slave and assign it. The slave
// only keeps it for this render frame.
plDXLightRef * plDXPipeline : : INextShadowLight ( plShadowSlave * slave )
{
fLights . fShadowLights . ExpandAndZero ( fLights . fNextShadowLight + 1 ) ;
if ( ! fLights . fShadowLights [ fLights . fNextShadowLight ] )
{
plDXLightRef * lRef = TRACKED_NEW plDXLightRef ( ) ;
/// Assign stuff and update
lRef - > fD3DIndex = fLights . ReserveD3DIndex ( ) ;
lRef - > fOwner = nil ;
lRef - > fD3DDevice = fD3DDevice ;
lRef - > Link ( & fLights . fRefList ) ;
fLights . fShadowLights [ fLights . fNextShadowLight ] = lRef ;
// Neutralize it until we need it.
fD3DDevice - > LightEnable ( lRef - > fD3DIndex , false ) ;
// Some things never change.
memset ( & lRef - > fD3DInfo , 0 , sizeof ( lRef - > fD3DInfo ) ) ;
lRef - > fD3DInfo . Ambient . r = lRef - > fD3DInfo . Ambient . g = lRef - > fD3DInfo . Ambient . b = 0 ;
lRef - > fD3DInfo . Specular . r = lRef - > fD3DInfo . Specular . g = lRef - > fD3DInfo . Specular . b = 0 ;
}
slave - > fLightRefIdx = fLights . fNextShadowLight ;
return fLights . fShadowLights [ fLights . fNextShadowLight + + ] ;
}
// IPopShadowCastState ///////////////////////////////////////////////////
// Pop the state set to render this shadow caster, so we're ready to render
// a different shadow caster, or go on to our main render.
hsBool plDXPipeline : : IPopShadowCastState ( plShadowSlave * slave )
{
fView = fSettings . fViewStack . Pop ( ) ;
PopRenderTarget ( ) ;
fView . fXformResetFlags = fView . kResetProjection | fView . kResetCamera ;
return true ;
}
// IMakeRenderTargetPools /////////////////////////////////////////////////////////////
// These are actually only used as shadow map pools, but they could be used for other
// render targets.
// All these are created here in a single call because they go in POOL_DEFAULT, so they
// must be created before we start creating things in POOL_MANAGED.
void plDXPipeline : : IMakeRenderTargetPools ( )
{
hsAssert ( ! fManagedAlloced , " Allocating rendertargets with managed resources alloced " ) ;
IReleaseRenderTargetPools ( ) ; // Just to be sure.
// Numbers of render targets to be created for each size.
// These numbers were set with multi-player in mind, so should be reconsidered.
// But do keep in mind that there are many things in production assets that cast
// shadows besides the avatar.
plConst ( hsScalar ) kCount [ kMaxRenderTargetNext ] = {
0 , // 1x1
0 , // 2x2
0 , // 4x4
0 , // 8x8
0 , // 16x16
32 , // 32x32
16 , // 64x64
8 , // 128x128
4 , // 256x256
0 // 512x512
} ;
int i ;
for ( i = 0 ; i < kMaxRenderTargetNext ; i + + )
{
hsTArray < plRenderTarget * > * pool = nil ;
switch ( i )
{
default :
case 0 :
case 1 :
case 2 :
case 3 :
case 4 :
break ;
case 5 :
pool = & fRenderTargetPool32 ;
break ;
case 6 :
pool = & fRenderTargetPool64 ;
break ;
case 7 :
pool = & fRenderTargetPool128 ;
break ;
case 8 :
pool = & fRenderTargetPool256 ;
break ;
case 9 :
pool = & fRenderTargetPool512 ;
break ;
}
if ( pool )
{
pool - > SetCount ( ( int ) ( kCount [ i ] + 1 ) ) ;
( * pool ) [ 0 ] = nil ;
( * pool ) [ ( int ) ( kCount [ i ] ) ] = nil ;
int j ;
for ( j = 0 ; j < kCount [ i ] ; j + + )
{
UInt16 flags = plRenderTarget : : kIsTexture | plRenderTarget : : kIsProjected ;
UInt8 bitDepth = 32 ;
UInt8 zDepth = 24 ;
UInt8 stencilDepth = 0 ;
// If we ever allow non-square shadows, change this.
int width = 1 < < i ;
int height = width ;
plRenderTarget * rt = TRACKED_NEW plRenderTarget ( flags , width , height , bitDepth , zDepth , stencilDepth ) ;
// If we've failed to create our render target ref, we're probably out of
// video memory. We'll return nil, and this guy just doesn't get a shadow
// until more video memory turns up (not likely).
if ( ! SharedRenderTargetRef ( ( * pool ) [ 0 ] , rt ) )
{
delete rt ;
pool - > SetCount ( j + 1 ) ;
( * pool ) [ j ] = nil ;
break ;
}
( * pool ) [ j ] = rt ;
}
}
}
}
// IResetRenderTargetPools /////////////////////////////////////////////////////////////////
// No release of resources, this just resets for the start of a frame. So if a shadow
// slave gets a render target from a pool, once this is called (conceptually at the
// end of the frame), the slave no longer owns that render target.
void plDXPipeline : : IResetRenderTargetPools ( )
{
int i ;
for ( i = 0 ; i < kMaxRenderTargetNext ; i + + )
{
fRenderTargetNext [ i ] = 0 ;
fBlurScratchRTs [ i ] = nil ;
fBlurDestRTs [ i ] = nil ;
}
fLights . fNextShadowLight = 0 ;
}
// IPrepShadowCaster ////////////////////////////////////////////////////////////////////////
// Make sure all the geometry in this shadow caster is ready to be rendered.
// Keep in mind the single shadow caster may be multiple spans possibly in
// multiple drawables.
// The tricky part here is that we need to prep each drawable involved,
// but only prep it once. Say the caster is composed of:
// drawableA, span0
// drawableA, span1
// drawableB, span0
// Then we need to call plDrawable::PrepForRender() ONCE on drawableA,
// and once on drawableB. Further, we need to do any necessary CPU
// skinning with ISofwareVertexBlend(drawableA, visList={0,1}) and
// ISofwareVertexBlend(drawableB, visList={1}).
hsBool plDXPipeline : : IPrepShadowCaster ( const plShadowCaster * caster )
{
static hsBitVector done ;
done . Clear ( ) ;
const hsTArray < plShadowCastSpan > & castSpans = caster - > Spans ( ) ;
int i ;
for ( i = 0 ; i < castSpans . GetCount ( ) ; i + + )
{
if ( ! done . IsBitSet ( i ) )
{
// We haven't already done this castSpan
plDrawableSpans * drawable = castSpans [ i ] . fDraw ;
// Start a visList with this index.
static hsTArray < Int16 > visList ;
visList . SetCount ( 0 ) ;
visList . Append ( ( Int16 ) ( castSpans [ i ] . fIndex ) ) ;
// We're about to have done this castSpan.
done . SetBit ( i ) ;
// Look forward through castSpans for any other spans
// with the same drawable, and add them to visList.
// We'll handle all the spans from this drawable at once.
int j ;
for ( j = i + 1 ; j < castSpans . GetCount ( ) ; j + + )
{
if ( ! done . IsBitSet ( j ) & & ( castSpans [ j ] . fDraw = = drawable ) )
{
// Add to list
visList . Append ( ( Int16 ) ( castSpans [ j ] . fIndex ) ) ;
// We're about to have done this castSpan.
done . SetBit ( j ) ;
}
}
// That's all, prep the drawable.
drawable - > PrepForRender ( this ) ;
// Do any software skinning.
if ( ! ISoftwareVertexBlend ( drawable , visList ) )
return false ;
}
}
return true ;
}
// IRenderShadowCaster ////////////////////////////////////////////////
// Render the shadow caster into the slave's render target, creating a shadow map.
hsBool plDXPipeline : : IRenderShadowCaster ( plShadowSlave * slave )
{
const plShadowCaster * caster = slave - > fCaster ;
// Setup to render into the slave's render target.
if ( ! IPushShadowCastState ( slave ) )
return false ;
// Get the shadow caster ready to render.
if ( ! IPrepShadowCaster ( slave - > fCaster ) )
return false ;
// for each shadowCaster.fSpans
int iSpan ;
for ( iSpan = 0 ; iSpan < caster - > Spans ( ) . GetCount ( ) ; iSpan + + )
{
plDrawableSpans * dr = caster - > Spans ( ) [ iSpan ] . fDraw ;
const plSpan * sp = caster - > Spans ( ) [ iSpan ] . fSpan ;
UInt32 spIdx = caster - > Spans ( ) [ iSpan ] . fIndex ;
hsAssert ( sp - > fTypeMask & plSpan : : kIcicleSpan , " Shadow casting from non-trimeshes not currently supported " ) ;
// render shadowcaster.fSpans[i] to rendertarget
if ( ! ( sp - > fProps & plSpan : : kPropNoShadowCast ) )
IRenderShadowCasterSpan ( slave , dr , * ( const plIcicle * ) sp ) ;
// Keep track of which shadow slaves this span was rendered into.
// If self-shadowing is off, we use that to determine not to
// project the shadow map onto its source geometry.
sp - > SetShadowBit ( slave - > fIndex ) ; //index set in SubmitShadowSlave
}
// Debug only.
if ( blurScale > = 0.f )
slave - > fBlurScale = blurScale ;
// If this shadow requests being blurred, do it.
if ( slave - > fBlurScale > 0.f )
IBlurShadowMap ( slave ) ;
// Finished up, restore previous state.
IPopShadowCastState ( slave ) ;
# if MCN_BOUNDS_SPANS
if ( IsDebugFlagSet ( plPipeDbg : : kFlagShowShadowBounds ) )
{
/// Add a span to our boundsIce to show this
IAddBoundsSpan ( fBoundsSpans , & slave - > fWorldBounds ) ;
}
# endif // MCN_BOUNDS_SPANS
return true ;
}
// We have a (possibly empty) list of shadows submitted for this frame.
// At BeginRender, we need to accomplish:
// Find render targets for each shadow request of the requested size.
// Render the associated spans into the render targets. Something like the following:
void plDXPipeline : : IPreprocessShadows ( )
{
plProfile_BeginTiming ( PrepShadows ) ;
// Mark our shared resources as free to be used.
IResetRenderTargetPools ( ) ;
// Some board (possibly the Parhelia) freaked if anistropic filtering
// was enabled when rendering to a render target. We never need it for
// shadow maps, and it is slower, so we just kill it here.
ISetAnisotropy ( false ) ;
// Generate a shadow map for each submitted shadow slave.
// Shadow slave corresponds to one shadow caster paired
// with one shadow light that affects it. So a single caster
// may be in multiple slaves (from different lights), or a
// single light may be in different slaves (affecting different
// casters). The overall number is low in spite of the possible
// permutation explosion, because a slave is only generated
// for a caster being affected (in range etc.) by a light.
int iSlave ;
for ( iSlave = 0 ; iSlave < fShadows . GetCount ( ) ; iSlave + + )
{
plShadowSlave * slave = fShadows [ iSlave ] ;
// Any trouble, remove it from the list for this frame.
if ( ! IRenderShadowCaster ( slave ) )
{
fShadows . Remove ( iSlave ) ;
iSlave - - ;
continue ;
}
}
// Restore
ISetAnisotropy ( true ) ;
plProfile_EndTiming ( PrepShadows ) ;
}
// IClearShadowSlaves ///////////////////////////////////////////////////////////////////////////
// At EndRender(), we need to clear our list of shadow slaves. They are only valid for one frame.
void plDXPipeline : : IClearShadowSlaves ( )
{
int i ;
for ( i = 0 ; i < fShadows . GetCount ( ) ; i + + )
{
const plShadowCaster * caster = fShadows [ i ] - > fCaster ;
caster - > GetKey ( ) - > UnRefObject ( ) ;
}
fShadows . SetCount ( 0 ) ;
}
// IRenderShadowsOntoSpan /////////////////////////////////////////////////////////////////////
// After doing the usual render for a span (all passes), we call the following.
// If the span accepts shadows, this will loop over all the shadows active this
// frame, and apply the ones that intersect this spans bounds. See below for details.
void plDXPipeline : : IRenderShadowsOntoSpan ( const plRenderPrimFunc & render , const plSpan * span , hsGMaterial * mat )
{
// We've already computed which shadows affect this span. That's recorded in slaveBits.
const hsBitVector & slaveBits = span - > GetShadowSlaves ( ) ;
hsBool first = true ;
int i ;
for ( i = 0 ; i < fShadows . GetCount ( ) ; i + + )
{
if ( slaveBits . IsBitSet ( fShadows [ i ] - > fIndex ) )
{
// This slave affects this span.
if ( first )
{
// On the first, we do all the setup that is independent of
// the shadow slave, so state that needs to get set once before
// projecting any number of shadow maps.
ISetupShadowRcvTextureStages ( mat ) ;
first = false ;
}
// Now setup any state specific to this shadow slave.
ISetupShadowSlaveTextures ( fShadows [ i ] ) ;
int selfShadowNow = span - > IsShadowBitSet ( fShadows [ i ] - > fIndex ) ;
// We vary the shadow intensity when self shadowing (see below),
// so we cache whether the shadow light is set for regular or
// self shadowing intensity. If what we're doing now is different
// than what we're currently set for, set it again.
if ( selfShadowNow ! = fShadows [ i ] - > fSelfShadowOn )
{
plDXLightRef * lRef = fLights . fShadowLights [ fShadows [ i ] - > fLightRefIdx ] ;
// We lower the power on self shadowing, because the artists like to
// crank up the shadow strength to huge values to get a darker shadow
// on the environment, which causes the shadow on the avatar to get
// way too dark. Another way to look at it is when self shadowing,
// the surface being projected onto is going to be very close to
// the surface casting the shadow (because they are the same object).
if ( selfShadowNow )
{
plConst ( hsScalar ) kMaxSelfPower = 0.3f ;
hsScalar power = fShadows [ i ] - > fPower > kMaxSelfPower ? kMaxSelfPower : fShadows [ i ] - > fPower ;
lRef - > fD3DInfo . Diffuse . r
= lRef - > fD3DInfo . Diffuse . g
= lRef - > fD3DInfo . Diffuse . b
= power ;
}
else
{
lRef - > fD3DInfo . Diffuse . r
= lRef - > fD3DInfo . Diffuse . g
= lRef - > fD3DInfo . Diffuse . b
= fShadows [ i ] - > fPower ;
}
fD3DDevice - > SetLight ( lRef - > fD3DIndex , & lRef - > fD3DInfo ) ;
// record which our intensity is now set for.
fShadows [ i ] - > fSelfShadowOn = selfShadowNow ;
}
// Enable the light.
fD3DDevice - > LightEnable ( fShadows [ i ] - > fLightIndex , true ) ;
# ifdef HS_DEBUGGING
DWORD nPass ;
fSettings . fDXError = fD3DDevice - > ValidateDevice ( & nPass ) ;
if ( fSettings . fDXError ! = D3D_OK )
IGetD3DError ( ) ;
# endif // HS_DEBUGGING
# ifndef PLASMA_EXTERNAL_RELEASE
if ( ! IsDebugFlagSet ( plPipeDbg : : kFlagNoShadowApply ) )
# endif // PLASMA_EXTERNAL_RELEASE
render . RenderPrims ( ) ;
// Disable it again.
fD3DDevice - > LightEnable ( fShadows [ i ] - > fLightIndex , false ) ;
}
}
}
// ISetupShadowRcvTextureStages ////////////////////////////////////////////
// Set the generic stage states. We'll fill in the specific textures
// for each slave later.
void plDXPipeline : : ISetupShadowRcvTextureStages ( hsGMaterial * mat )
{
// Setup for nil shaders to get us back to fixed function pipeline.
ISetShaders ( nil , nil ) ;
// We're whacking about with renderstate independent of current material,
// so make sure the next span processes it's material, even if it's the
// same one.
fForceMatHandle = true ;
// Set the D3D lighting/material model
ISetShadowLightState ( mat ) ;
// Zbuffering on read-only
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_LESSEQUAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , FALSE ) ;
fLayerState [ 0 ] . fZFlags & = ~ hsGMatState : : kZMask ;
fLayerState [ 0 ] . fZFlags | = hsGMatState : : kZNoZWrite ;
// Stage 0:
// Texture is slave specific
// Texture transform is slave specific
// ColorArg1 = texture
// ColorArg2 = diffuse
// ColorOp = modulate
// AlphaArg1 = texture
// AlphaOp = SelectArg1
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_DIFFUSE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_SELECTARG1 ) ;
if ( fLayerUVWSrcs [ 0 ] ! = D3DTSS_TCI_CAMERASPACEPOSITION )
{
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_TEXCOORDINDEX , D3DTSS_TCI_CAMERASPACEPOSITION ) ;
fLayerUVWSrcs [ 0 ] = D3DTSS_TCI_CAMERASPACEPOSITION ;
}
// Set texture to clamp
fD3DDevice - > SetSamplerState ( 0 , D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ) ;
fD3DDevice - > SetSamplerState ( 0 , D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ) ;
fLayerState [ 0 ] . fClampFlags = hsGMatState : : kClampTexture ;
// Stage 1:
// Set texture to ULut
// Texture transform is slave specific
// *** With the optional texture blurring, the state here becomes
// *** partially slave dependent. Specifically, if we've done a blur,
// *** then we want to modulate the lut color value by current (which is
// *** the blurred color), else just select the lut. So we'll just move
// *** the ColorOp down to the slave specific section.
// %%% Okay, get this. The GeForce2 won't take a SelectArg1 on Stage1 if
// %%% we're also trying to use Stage2 to modulate in the diffuse. But
// %%% it WILL let us do a modulate on Stage1. So we're going to make sure
// %%% that our shadowmap texture is white, then we can just modulate them
// %%% with no effect. If we're blurring, we already wanted to modulate, so
// %%% no change there. This means we can set the ColorOp now, rather than
// %%% having to wait for the Slave specific section later.
// ColorArg1 = 1 - ULut
// ColorArg2 = Current
// ColorOp = Modulate
// AlphaArg1 = ULut
// AlphaArg2 = Current
// AlphaOp = Subtract
plDXTextureRef * ref = IGetULutTextureRef ( ) ;
if ( ! ref - > fD3DTexture )
{
if ( ref - > fData )
IReloadTexture ( ref ) ;
}
hsRefCnt_SafeAssign ( fLayerRef [ 1 ] , ref ) ;
fD3DDevice - > SetTexture ( 1 , ref - > fD3DTexture ) ;
// The following commented out block is kind of cool, because it
// bases the darkness of the shadow on the distance between the
// shadow caster and the point receiving the shadow. So, for example,
// the hand's shadow would get darker as it reaches for the lever.
// Unfortunately, it doesn't guarantee that the shadow will completely
// attenuate out at the fAttenDist (in fact, it pretty much guarantees
// that it won't), so shadows will pop in and out. So instead, we'll
// base the color on the distance from the start of the slave. The
// difference is subtle, and usually unnoticable, and we get no popping.
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_COLORARG1 , D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_ALPHAOP , D3DTOP_SUBTRACT ) ;
fLayerState [ 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
if ( fLayerUVWSrcs [ 1 ] ! = D3DTSS_TCI_CAMERASPACEPOSITION )
{
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_TEXCOORDINDEX , D3DTSS_TCI_CAMERASPACEPOSITION ) ;
fLayerUVWSrcs [ 1 ] = D3DTSS_TCI_CAMERASPACEPOSITION ;
}
if ( D3DTTFF_COUNT3 ! = fLayerXformFlags [ 1 ] )
{
fLayerXformFlags [ 1 ] = D3DTTFF_COUNT3 ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_TEXTURETRANSFORMFLAGS , D3DTTFF_COUNT3 ) ;
}
// Set texture to clamp
fD3DDevice - > SetSamplerState ( 1 , D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ) ;
fD3DDevice - > SetSamplerState ( 1 , D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ) ;
fLayerState [ 1 ] . fClampFlags = hsGMatState : : kClampTexture ;
int iNextStage = 2 ;
// If mat's base layer is alpha'd, and we have > 3 TMU's factor
// in the base layer's alpha.
if ( ( fSettings . fMaxLayersAtOnce > 3 ) & & mat - > GetLayer ( 0 ) - > GetTexture ( ) & & ( mat - > GetLayer ( 0 ) - > GetBlendFlags ( ) & hsGMatState : : kBlendAlpha ) )
{
plLayerInterface * layer = mat - > GetLayer ( 0 ) ;
// If the following conditions are met, it means that layer 1 is a better choice to
// get the transparency from. The specific case we're looking for is vertex alpha
// simulated by an invisible second layer alpha LUT (known as the alpha hack).
if ( ( layer - > GetMiscFlags ( ) & hsGMatState : : kMiscBindNext )
& & mat - > GetLayer ( 1 )
& & ! ( mat - > GetLayer ( 1 ) - > GetMiscFlags ( ) & hsGMatState : : kMiscNoShadowAlpha )
& & ! ( mat - > GetLayer ( 1 ) - > GetBlendFlags ( ) & hsGMatState : : kBlendNoTexAlpha )
& & mat - > GetLayer ( 1 ) - > GetTexture ( ) )
layer = mat - > GetLayer ( 1 ) ;
// Take the texture alpha and modulate the color so far with it. In
// the final shadow map, black will have no effect, white will be maximal
// darkening.
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_COLORARG1 , D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE ) ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_ALPHAARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 ) ;
// Blend flags to layer blend (alpha +- complement)
fLayerState [ iNextStage ] . fBlendFlags = UInt32 ( - 1 ) ;
// Clamp to whatever the texture wants.
if ( fLayerState [ iNextStage ] . fClampFlags ^ layer - > GetClampFlags ( ) )
{
fLayerState [ iNextStage ] . fClampFlags = layer - > GetClampFlags ( ) ;
IHandleStageClamp ( iNextStage ) ;
}
// Shade to 0
fLayerState [ iNextStage ] . fShadeFlags = 0 ;
// ZFlags to ZNoZWrite
fLayerState [ iNextStage ] . fZFlags = hsGMatState : : kZNoZWrite ;
// MiscFlags to layer's misc flags
fLayerState [ iNextStage ] . fMiscFlags = layer - > GetMiscFlags ( ) ;
// Set up whatever UVW transform the layer normally uses.
IHandleStageTransform ( iNextStage , layer ) ;
// Normal UVW source.
UInt32 uvwSrc = layer - > GetUVWSrc ( ) ;
if ( fLayerUVWSrcs [ iNextStage ] ! = uvwSrc )
{
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_TEXCOORDINDEX , uvwSrc ) ;
fLayerUVWSrcs [ iNextStage ] = uvwSrc ;
}
UInt32 xformFlags ;
if ( layer - > GetMiscFlags ( ) & hsGMatState : : kMiscPerspProjection )
xformFlags = D3DTTFF_COUNT3 | D3DTTFF_PROJECTED ;
else if ( uvwSrc & ( plLayerInterface : : kUVWNormal | plLayerInterface : : kUVWPosition | plLayerInterface : : kUVWReflect ) )
xformFlags = D3DTTFF_COUNT3 ;
else
xformFlags = D3DTTFF_COUNT2 ;
if ( xformFlags ! = fLayerXformFlags [ iNextStage ] )
{
fLayerXformFlags [ iNextStage ] = xformFlags ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_TEXTURETRANSFORMFLAGS , xformFlags ) ;
}
// This ref should be pretty safe to use, because we just rendered it.
ref = ( plDXTextureRef * ) layer - > GetTexture ( ) - > GetDeviceRef ( ) ;
hsRefCnt_SafeAssign ( fLayerRef [ iNextStage ] , ref ) ;
fD3DDevice - > SetTexture ( iNextStage , ref - > fD3DTexture ) ;
iNextStage + + ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_COLORARG1 , D3DTA_DIFFUSE | D3DTA_ALPHAREPLICATE ) ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_COLORARG2 , D3DTA_CURRENT ) ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLayerState [ iNextStage ] . fBlendFlags = UInt32 ( - 1 ) ;
iNextStage + + ;
}
fLayerState [ iNextStage ] . fBlendFlags = UInt32 ( - 1 ) ;
// And seal it up
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
fD3DDevice - > SetTextureStageState ( iNextStage , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLayerState [ iNextStage ] . fBlendFlags = UInt32 ( - 1 ) ;
fLastEndingStage = 0 ;
// Now set the frame buffer blend
// Remember that white darkens and black is no effect.
// Form is Src * SrcBlend + Dst * DstBlend
// We want inverse Src * Dst, so
// Src * ZERO + Dst * InvSrc
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_ZERO ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_INVSRCCOLOR ) ;
fLayerState [ 0 ] . fBlendFlags = UInt32 ( - 1 ) ;
// Turn on alpha test. Alpha of zero means the shadow map depth
// is greater or equal to the surface depth, i.e. the surface
// is between the shadow caster and the light and doesn't receive
// shadow.
fD3DDevice - > SetRenderState ( D3DRS_ALPHAFUNC , D3DCMP_GREATEREQUAL ) ;
fD3DDevice - > SetRenderState ( D3DRS_ALPHAREF , 0x00000001 ) ;
fLayerState [ 0 ] . fBlendFlags | = hsGMatState : : kBlendTest ;
fD3DDevice - > SetRenderState ( D3DRS_SPECULARENABLE , FALSE ) ;
fLayerState [ 0 ] . fShadeFlags & = ~ hsGMatState : : kShadeSpecular ;
// Set fog color to black
// We should automatically reset it, because our blend mode is -1'd.
fD3DDevice - > SetRenderState ( D3DRS_FOGCOLOR , 0 ) ;
# ifdef HS_DEBUGGING
DWORD nPass ;
fSettings . fDXError = fD3DDevice - > ValidateDevice ( & nPass ) ;
if ( fSettings . fDXError ! = D3D_OK )
IGetD3DError ( ) ;
# endif // HS_DEBUGGING
}
// ISetupShadowSlaveTextures //////////////////////////////////////////////
// Set any state specific to this shadow slave for projecting the slave's
// shadow map onto the surface.
void plDXPipeline : : ISetupShadowSlaveTextures ( plShadowSlave * slave )
{
D3DXMATRIX tXfm ;
hsMatrix44 c2w = GetCameraToWorld ( ) ;
// Stage 0:
// Set Stage 0's texture to the slave's rendertarget.
// Set texture transform to slave's camera to texture transform
plRenderTarget * renderTarg = ( plRenderTarget * ) slave - > fPipeData ;
hsAssert ( renderTarg , " Processing a slave that hasn't been rendered " ) ;
if ( ! renderTarg )
return ;
plDXTextureRef * ref = ( plDXTextureRef * ) renderTarg - > GetDeviceRef ( ) ;
hsAssert ( ref , " Shadow map ref should have been made when it was rendered " ) ;
if ( ! ref )
return ;
hsRefCnt_SafeAssign ( fLayerRef [ 0 ] , ref ) ;
fD3DDevice - > SetTexture ( 0 , ref - > fD3DTexture ) ;
hsMatrix44 cameraToTexture = slave - > fWorldToTexture * c2w ;
IMatrix44ToD3DMatrix ( tXfm , cameraToTexture ) ;
fD3DDevice - > SetTransform ( sTextureStages [ 0 ] , & tXfm ) ;
fLayerTransform [ 0 ] = true ;
// Directional lights (ortho projection) just use COUNT2, point lights use COUNT3|PROJECTED.
UInt32 xformFlags = slave - > fView . GetOrthogonal ( ) ? D3DTTFF_COUNT2 : D3DTTFF_COUNT3 | D3DTTFF_PROJECTED ;
if ( xformFlags ! = fLayerXformFlags [ 0 ] )
{
fLayerXformFlags [ 0 ] = xformFlags ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_TEXTURETRANSFORMFLAGS , xformFlags ) ;
}
// Stage 1: the lut
// Set the texture transform to slave's fRcvLUT
hsMatrix44 cameraToLut = slave - > fRcvLUT * c2w ;
IMatrix44ToD3DMatrix ( tXfm , cameraToLut ) ;
fD3DDevice - > SetTransform ( sTextureStages [ 1 ] , & tXfm ) ;
fLayerTransform [ 1 ] = true ;
}
// ISetShadowLightState //////////////////////////////////////////////////////////////////
// Set the D3D lighting/material model for projecting the shadow map onto this material.
void plDXPipeline : : ISetShadowLightState ( hsGMaterial * mat )
{
IDisableLightsForShadow ( ) ;
inlEnsureLightingOn ( ) ;
fCurrLightingMethod = plSpan : : kLiteShadow ;
static D3DMATERIAL9 d3dMat ;
if ( mat & & mat - > GetNumLayers ( ) & & mat - > GetLayer ( 0 ) )
d3dMat . Diffuse . r = d3dMat . Diffuse . g = d3dMat . Diffuse . b = mat - > GetLayer ( 0 ) - > GetOpacity ( ) ;
else
d3dMat . Diffuse . r = d3dMat . Diffuse . g = d3dMat . Diffuse . b = 1.f ;
d3dMat . Diffuse . a = 1.f ;
fD3DDevice - > SetMaterial ( & d3dMat ) ;
fD3DDevice - > SetRenderState ( D3DRS_AMBIENT , 0 ) ;
}
// IDisableLightsForShadow ///////////////////////////////////////////////////////////
// Disable any lights that are enabled. We'll only want the shadow light illuminating
// the surface.
void plDXPipeline : : IDisableLightsForShadow ( )
{
int i ;
for ( i = 0 ; i < fLights . fLastIndex + 1 ; i + + )
{
if ( fLights . fEnabledFlags . IsBitSet ( i ) )
{
fD3DDevice - > LightEnable ( i , false ) ;
}
}
fLights . fEnabledFlags . Clear ( ) ;
}
// IEnableShadowLight ///////////////////////////////////////////////
// Enable this shadow slave's light.
// NOT USED.
void plDXPipeline : : IEnableShadowLight ( plShadowSlave * slave )
{
fD3DDevice - > LightEnable ( slave - > fLightIndex , true ) ;
}
// IAcceptsShadow ////////////////////////////////////////////////////////////////
// Only allow self shadowing if requested.
hsBool plDXPipeline : : IAcceptsShadow ( const plSpan * span , plShadowSlave * slave )
{
// The span's shadow bits records which shadow maps that span was rendered
// into.
return slave - > SelfShadow ( ) | | ! span - > IsShadowBitSet ( slave - > fIndex ) ;
}
// IReceivesShadows ////////////////////////////////////////////////////////////////////
// Want artists to be able to just disable shadows for spans where they'll either
// look goofy, or won't contribute.
// Also, if we have less than 3 simultaneous textures, we want to skip anything with
// an alpha'd base layer, unless it's been overriden.
hsBool plDXPipeline : : IReceivesShadows ( const plSpan * span , hsGMaterial * mat )
{
if ( span - > fProps & plSpan : : kPropNoShadow )
return false ;
if ( span - > fProps & plSpan : : kPropForceShadow )
return true ;
if ( span - > fProps & ( plSpan : : kPropSkipProjection | plSpan : : kPropProjAsVtx ) )
return false ;
if ( ( fSettings . fMaxLayersAtOnce < 3 )
& & mat - > GetLayer ( 0 ) - > GetTexture ( )
& & ( mat - > GetLayer ( 0 ) - > GetBlendFlags ( ) & hsGMatState : : kBlendAlpha ) )
return false ;
# ifdef ENABLE_INTEL_SHADOWS
// Shouldn't hit this, since we're disabling shadows on the Intel chips,
// but just in case.
// To enable this, you'll need to start passing in the drawable as well.
if ( fSettings . fIsIntel )
{
const plVertexSpan * vertSpan = static_cast < const plVertexSpan * > ( span ) ;
plGBufferGroup * group = drawable - > GetBufferGroup ( vertSpan - > fGroupIdx ) ;
if ( ! group - > GetNumUVs ( ) )
return false ;
}
# endif // ENABLE_INTEL_SHADOWS
return true ;
}
void plDXPipeline : : SubmitClothingOutfit ( plClothingOutfit * co )
{
if ( fClothingOutfits . Find ( co ) = = fClothingOutfits . kMissingIndex )
{
fClothingOutfits . Append ( co ) ;
if ( ! fPrevClothingOutfits . RemoveItem ( co ) )
co - > GetKey ( ) - > RefObject ( ) ;
}
}
void plDXPipeline : : IClearClothingOutfits ( hsTArray < plClothingOutfit * > * outfits )
{
int i ;
for ( i = outfits - > GetCount ( ) - 1 ; i > = 0 ; i - - )
{
plClothingOutfit * co = outfits - > Get ( i ) ;
outfits - > Remove ( i ) ;
IFreeAvRT ( ( plRenderTarget * ) co - > fTargetLayer - > GetTexture ( ) ) ;
co - > fTargetLayer - > SetTexture ( nil ) ;
co - > GetKey ( ) - > UnRefObject ( ) ;
}
}
void plDXPipeline : : IFillAvRTPool ( )
{
fAvNextFreeRT = 0 ;
fAvRTShrinkValidSince = hsTimer : : GetSysSeconds ( ) ;
int numRTs = 1 ;
if ( fClothingOutfits . GetCount ( ) > 1 )
{
// Just jump to 8 for starters so we don't have to refresh for the 2nd, 4th, AND 8th player
numRTs = 8 ;
while ( numRTs < fClothingOutfits . GetCount ( ) )
numRTs * = 2 ;
}
// I could see a 32MB video card going down to 64x64 RTs in extreme cases
// (over 100 players onscreen at once), but really, if such hardware is ever trying to push
// that, the low texture resolution is not going to be your major concern.
for ( fAvRTWidth = 1024 > > plMipmap : : GetGlobalLevelChopCount ( ) ; fAvRTWidth > = 32 ; fAvRTWidth / = 2 )
{
if ( IFillAvRTPool ( numRTs , fAvRTWidth ) )
return ;
// Nope? Ok, lower the resolution and try again.
}
}
hsBool plDXPipeline : : IFillAvRTPool ( UInt16 numRTs , UInt16 width )
{
fAvRTPool . SetCount ( numRTs ) ;
int i ;
for ( i = 0 ; i < numRTs ; i + + )
{
UInt16 flags = plRenderTarget : : kIsTexture | plRenderTarget : : kIsProjected ;
UInt8 bitDepth = 32 ;
UInt8 zDepth = 0 ;
UInt8 stencilDepth = 0 ;
fAvRTPool [ i ] = TRACKED_NEW plRenderTarget ( flags , width , width , bitDepth , zDepth , stencilDepth ) ;
// If anyone fails, release everyone we've created.
if ( ! MakeRenderTargetRef ( fAvRTPool [ i ] ) )
{
int j ;
for ( j = 0 ; j < = i ; j + + )
{
delete fAvRTPool [ j ] ;
}
return false ;
}
}
return true ;
}
void plDXPipeline : : IReleaseAvRTPool ( )
{
int i ;
for ( i = 0 ; i < fClothingOutfits . GetCount ( ) ; i + + )
{
fClothingOutfits [ i ] - > fTargetLayer - > SetTexture ( nil ) ;
}
for ( i = 0 ; i < fPrevClothingOutfits . GetCount ( ) ; i + + )
{
fPrevClothingOutfits [ i ] - > fTargetLayer - > SetTexture ( nil ) ;
}
for ( i = 0 ; i < fAvRTPool . GetCount ( ) ; i + + )
{
delete ( fAvRTPool [ i ] ) ;
}
fAvRTPool . Reset ( ) ;
}
plRenderTarget * plDXPipeline : : IGetNextAvRT ( )
{
return fAvRTPool [ fAvNextFreeRT + + ] ;
}
void plDXPipeline : : IFreeAvRT ( plRenderTarget * tex )
{
UInt32 index = fAvRTPool . Find ( tex ) ;
if ( index ! = fAvRTPool . kMissingIndex )
{
hsAssert ( index < fAvNextFreeRT , " Freeing an avatar RT that's already free? " ) ;
fAvRTPool [ index ] = fAvRTPool [ fAvNextFreeRT - 1 ] ;
fAvRTPool [ fAvNextFreeRT - 1 ] = tex ;
fAvNextFreeRT - - ;
}
}
struct plAVTexVert
{
float fPos [ 3 ] ;
float fUv [ 2 ] ;
} ;
void plDXPipeline : : IPreprocessAvatarTextures ( )
{
plProfile_Set ( AvRTPoolUsed , fClothingOutfits . GetCount ( ) ) ;
plProfile_Set ( AvRTPoolCount , fAvRTPool . GetCount ( ) ) ;
plProfile_Set ( AvRTPoolRes , fAvRTWidth ) ;
plProfile_Set ( AvRTShrinkTime , UInt32 ( hsTimer : : GetSysSeconds ( ) - fAvRTShrinkValidSince ) ) ;
IClearClothingOutfits ( & fPrevClothingOutfits ) ; // Frees anyone used last frame that we don't need this frame
const UInt32 kVFormat = D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2 ( 0 ) ;
if ( fClothingOutfits . GetCount ( ) = = 0 )
return ;
plMipmap * itemBufferTex = nil ;
fForceMatHandle = true ;
ISetShaders ( nil , nil ) ; // Has a side effect of futzing with our cull settings...
// Even though we're going to use DrawPrimitiveUP, we explicitly set the current VB ref to nil,
// otherwise we might try and use the same VB ref later, think it hasn't changed, and
// not update our FVF.
hsRefCnt_SafeUnRef ( fSettings . fCurrVertexBuffRef ) ;
fSettings . fCurrVertexBuffRef = nil ;
fD3DDevice - > SetStreamSource ( 0 , NULL , 0 , 0 ) ;
fD3DDevice - > SetFVF ( fSettings . fCurrFVFFormat = kVFormat ) ;
fD3DDevice - > SetTransform ( D3DTS_VIEW , & d3dIdentityMatrix ) ;
fD3DDevice - > SetTransform ( D3DTS_WORLD , & d3dIdentityMatrix ) ;
fD3DDevice - > SetTransform ( D3DTS_PROJECTION , & d3dIdentityMatrix ) ;
fD3DDevice - > SetRenderState ( D3DRS_CULLMODE , fCurrCullMode = D3DCULL_NONE ) ;
fD3DDevice - > SetRenderState ( D3DRS_VERTEXBLEND , D3DVBF_DISABLE ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZFUNC , D3DCMP_ALWAYS ) ;
fD3DDevice - > SetRenderState ( D3DRS_ZWRITEENABLE , FALSE ) ;
fLayerState [ 0 ] . fZFlags & = ~ hsGMatState : : kZMask ;
fLayerState [ 0 ] . fZFlags | = hsGMatState : : kZNoZWrite | hsGMatState : : kZNoZRead ;
if ( fLayerUVWSrcs [ 0 ] ! = 0 )
{
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_TEXCOORDINDEX , 0 ) ;
fLayerUVWSrcs [ 0 ] = 0 ;
}
fD3DDevice - > SetSamplerState ( 0 , D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ) ;
fD3DDevice - > SetSamplerState ( 0 , D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ) ;
fLayerState [ 0 ] . fClampFlags = hsGMatState : : kClampTexture ;
if ( D3DTTFF_DISABLE ! = fLayerXformFlags [ 0 ] )
{
fLayerXformFlags [ 0 ] = D3DTTFF_DISABLE ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_TEXTURETRANSFORMFLAGS , D3DTTFF_DISABLE ) ;
}
fD3DDevice - > SetRenderState ( D3DRS_SPECULARENABLE , FALSE ) ;
fLayerState [ 0 ] . fShadeFlags & = ~ hsGMatState : : kShadeSpecular ;
fD3DDevice - > SetRenderState ( D3DRS_FOGENABLE , FALSE ) ;
fCurrFog . fEnvPtr = nil ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TFACTOR ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_MODULATE ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TFACTOR ) ;
fD3DDevice - > SetTextureStageState ( 0 , D3DTSS_ALPHAARG2 , D3DTA_TEXTURE ) ;
fD3DDevice - > SetRenderState ( D3DRS_ALPHAFUNC , D3DCMP_ALWAYS ) ;
fLayerState [ 0 ] . fBlendFlags = UInt32 ( - 1 ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_COLOROP , D3DTOP_DISABLE ) ;
fD3DDevice - > SetTextureStageState ( 1 , D3DTSS_ALPHAOP , D3DTOP_DISABLE ) ;
fLayerState [ 1 ] . fBlendFlags = UInt32 ( - 1 ) ;
inlEnsureLightingOff ( ) ;
int oIdx ;
for ( oIdx = 0 ; oIdx < fClothingOutfits . GetCount ( ) ; oIdx + + )
{
plClothingOutfit * co = fClothingOutfits [ oIdx ] ;
if ( co - > fBase = = nil | | co - > fBase - > fBaseTexture = = nil )
continue ;
plRenderTarget * rt = plRenderTarget : : ConvertNoRef ( co - > fTargetLayer - > GetTexture ( ) ) ;
if ( rt ! = nil & & co - > fDirtyItems . Empty ( ) )
{
// we've still got our valid RT from last frame and we have nothing to do.
continue ;
}
if ( rt = = nil )
{
rt = IGetNextAvRT ( ) ;
co - > fTargetLayer - > SetTexture ( rt ) ;
}
PushRenderTarget ( rt ) ;
D3DVIEWPORT9 vp = { 0 , 0 , rt - > GetWidth ( ) , rt - > GetHeight ( ) , 0.f , 1.f } ;
WEAK_ERROR_CHECK ( fD3DDevice - > SetViewport ( & vp ) ) ;
hsScalar uOff = 0.5f / rt - > GetWidth ( ) ;
hsScalar vOff = 0.5f / rt - > GetHeight ( ) ;
// Copy over the base
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , FALSE ) ;
fD3DDevice - > SetRenderState ( D3DRS_COLORWRITEENABLE , D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA ) ;
fD3DDevice - > SetRenderState ( D3DRS_TEXTUREFACTOR , 0xffffffff ) ;
fLayerState [ 0 ] . fBlendFlags = UInt32 ( - 1 ) ;
IDrawClothingQuad ( - 1.f , - 1.f , 2.f , 2.f , uOff , vOff , co - > fBase - > fBaseTexture ) ;
plClothingLayout * layout = plClothingMgr : : GetClothingMgr ( ) - > GetLayout ( co - > fBase - > fLayoutName ) ;
int i , j , k ;
for ( i = 0 ; i < co - > fItems . GetCount ( ) ; i + + )
{
plClothingItem * item = co - > fItems [ i ] ;
//if (!co->fDirtyItems.IsBitSet(item->fTileset))
// continue; // Not dirty, don't update
for ( j = 0 ; j < item - > fElements . GetCount ( ) ; j + + )
{
for ( k = 0 ; k < plClothingElement : : kLayerMax ; k + + )
{
if ( item - > fTextures [ j ] [ k ] = = nil )
continue ;
itemBufferTex = item - > fTextures [ j ] [ k ] ;
hsColorRGBA tint = co - > GetItemTint ( item , k ) ;
if ( k > = plClothingElement : : kLayerSkinBlend1 & & k < = plClothingElement : : kLayerSkinLast )
tint . a = co - > fSkinBlends [ k - plClothingElement : : kLayerSkinBlend1 ] ;
if ( k = = plClothingElement : : kLayerBase )
{
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , FALSE ) ;
fD3DDevice - > SetRenderState ( D3DRS_COLORWRITEENABLE , D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA ) ;
}
else
{
fD3DDevice - > SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE ) ;
fD3DDevice - > SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_SRCALPHA ) ;
fD3DDevice - > SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_INVSRCALPHA ) ;
fD3DDevice - > SetRenderState ( D3DRS_COLORWRITEENABLE , D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE ) ;
}
fD3DDevice - > SetRenderState ( D3DRS_TEXTUREFACTOR , tint . ToARGB32 ( ) ) ;
fLayerState [ 0 ] . fBlendFlags = UInt32 ( - 1 ) ;
hsScalar screenW = ( hsScalar ) item - > fElements [ j ] - > fWidth / layout - > fOrigWidth * 2.f ;
hsScalar screenH = ( hsScalar ) item - > fElements [ j ] - > fHeight / layout - > fOrigWidth * 2.f ;
hsScalar screenX = ( hsScalar ) item - > fElements [ j ] - > fXPos / layout - > fOrigWidth * 2.f - 1.f ;
hsScalar screenY = ( 1.f - ( hsScalar ) item - > fElements [ j ] - > fYPos / layout - > fOrigWidth ) * 2.f - 1.f - screenH ;
IDrawClothingQuad ( screenX , screenY , screenW , screenH , uOff , vOff , itemBufferTex ) ;
}
}
}
PopRenderTarget ( ) ;
co - > fDirtyItems . Clear ( ) ;
}
// Nothing else sets this render state, so let's just set it back to the default to be safe
fD3DDevice - > SetRenderState ( D3DRS_COLORWRITEENABLE , D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA ) ;
fView . fXformResetFlags = fView . kResetAll ;
fClothingOutfits . Swap ( fPrevClothingOutfits ) ;
}
void plDXPipeline : : IDrawClothingQuad ( hsScalar x , hsScalar y , hsScalar w , hsScalar h ,
hsScalar uOff , hsScalar vOff , plMipmap * tex )
{
const UInt32 kVSize = sizeof ( plAVTexVert ) ;
plDXTextureRef * ref = ( plDXTextureRef * ) tex - > GetDeviceRef ( ) ;
if ( ! ref | | ref - > IsDirty ( ) )
{
MakeTextureRef ( nil , tex ) ;
ref = ( plDXTextureRef * ) tex - > GetDeviceRef ( ) ;
}
if ( ! ref - > fD3DTexture )
{
if ( ref - > fData )
IReloadTexture ( ref ) ;
}
hsRefCnt_SafeAssign ( fLayerRef [ 0 ] , ref ) ;
fD3DDevice - > SetTexture ( 0 , ref - > fD3DTexture ) ;
plAVTexVert ptr [ 4 ] ;
plAVTexVert vert ;
vert . fPos [ 0 ] = x ;
vert . fPos [ 1 ] = y ;
vert . fPos [ 2 ] = 0.5f ;
vert . fUv [ 0 ] = uOff ;
vert . fUv [ 1 ] = 1.f + vOff ;
// P0
ptr [ 2 ] = vert ;
// P1
ptr [ 0 ] = vert ;
ptr [ 0 ] . fPos [ 0 ] + = w ;
ptr [ 0 ] . fUv [ 0 ] + = 1.f ;
// P2
ptr [ 1 ] = vert ;
ptr [ 1 ] . fPos [ 0 ] + = w ;
ptr [ 1 ] . fUv [ 0 ] + = 1.f ;
ptr [ 1 ] . fPos [ 1 ] + = h ;
ptr [ 1 ] . fUv [ 1 ] - = 1.f ;
// P3
ptr [ 3 ] = vert ;
ptr [ 3 ] . fPos [ 1 ] + = h ;
ptr [ 3 ] . fUv [ 1 ] - = 1.f ;
# ifdef HS_DEBUGGING
DWORD nPass ;
fSettings . fDXError = fD3DDevice - > ValidateDevice ( & nPass ) ;
if ( fSettings . fDXError ! = D3D_OK )
IGetD3DError ( ) ;
# endif // HS_DEBUGGING
fD3DDevice - > DrawPrimitiveUP ( D3DPT_TRIANGLESTRIP , 2 , ptr , kVSize ) ;
}
///////////////////////////////////////////////////////////////////////////////
// Test hackery as R&D for water
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// End Test hackery as R&D for water
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// Functions from Other Classes That Need to Be Here to Compile Right ///////
///////////////////////////////////////////////////////////////////////////////
plPipeline * plPipelineCreate : : ICreateDXPipeline ( hsWinRef hWnd , const hsG3DDeviceModeRecord * devMode )
{
plDXPipeline * pipe = TRACKED_NEW plDXPipeline ( hWnd , devMode ) ;
// Taken out 8.1.2001 mcn - If we have an error, still return so the client can grab the string
// if( pipe->GetErrorString() != nil )
// {
// delete pipe;
// pipe = nil;
// }
return pipe ;
}