summaryrefslogtreecommitdiff
path: root/studiorender/r_studio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'studiorender/r_studio.cpp')
-rw-r--r--studiorender/r_studio.cpp392
1 files changed, 392 insertions, 0 deletions
diff --git a/studiorender/r_studio.cpp b/studiorender/r_studio.cpp
new file mode 100644
index 0000000..51fb01c
--- /dev/null
+++ b/studiorender/r_studio.cpp
@@ -0,0 +1,392 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// r_studio.cpp: routines for setting up to draw 3DStudio models
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//===========================================================================//
+
+
+#include "studio.h"
+#include "studiorender.h"
+#include "studiorendercontext.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "tier0/vprof.h"
+#include "tier3/tier3.h"
+#include "datacache/imdlcache.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Figures out what kind of lighting we're gonna want
+//-----------------------------------------------------------------------------
+FORCEINLINE StudioModelLighting_t CStudioRender::R_StudioComputeLighting( IMaterial *pMaterial, int materialFlags, ColorMeshInfo_t *pColorMeshes )
+{
+ // Here, we only do software lighting when the following conditions are met.
+ // 1) The material is vertex lit and we don't have hardware lighting
+ // 2) We're drawing an eyeball
+ // 3) We're drawing mouth-lit stuff
+
+ // FIXME: When we move software lighting into the material system, only need to
+ // test if it's vertex lit
+
+ Assert( pMaterial );
+ bool doMouthLighting = materialFlags && (m_pStudioHdr->nummouths >= 1);
+
+ if ( IsX360() )
+ {
+ // 360 does not do software lighting
+ return doMouthLighting ? LIGHTING_MOUTH : LIGHTING_HARDWARE;
+ }
+
+ bool doSoftwareLighting = doMouthLighting ||
+ (pMaterial->IsVertexLit() && pMaterial->NeedsSoftwareLighting() );
+
+ if ( !m_pRC->m_Config.m_bSupportsVertexAndPixelShaders )
+ {
+ if ( !doSoftwareLighting && pColorMeshes )
+ {
+ pMaterial->SetUseFixedFunctionBakedLighting( true );
+ }
+ else
+ {
+ doSoftwareLighting = true;
+ pMaterial->SetUseFixedFunctionBakedLighting( false );
+ }
+ }
+
+ StudioModelLighting_t lighting = LIGHTING_HARDWARE;
+ if ( doMouthLighting )
+ lighting = LIGHTING_MOUTH;
+ else if ( doSoftwareLighting )
+ lighting = LIGHTING_SOFTWARE;
+
+ return lighting;
+}
+
+
+IMaterial* CStudioRender::R_StudioSetupSkinAndLighting( IMatRenderContext *pRenderContext, int index, IMaterial **ppMaterials, int materialFlags,
+ void /*IClientRenderable*/ *pClientRenderable, ColorMeshInfo_t *pColorMeshes, StudioModelLighting_t &lighting )
+{
+ VPROF( "R_StudioSetupSkin" );
+ IMaterial *pMaterial = NULL;
+ bool bCheckForConVarDrawTranslucentSubModels = false;
+ if( m_pRC->m_Config.bWireframe && !m_pRC->m_pForcedMaterial )
+ {
+ if ( m_pRC->m_Config.bDrawZBufferedWireframe )
+ pMaterial = m_pMaterialMRMWireframeZBuffer;
+ else
+ pMaterial = m_pMaterialMRMWireframe;
+ }
+ else if( m_pRC->m_Config.bShowEnvCubemapOnly )
+ {
+ pMaterial = m_pMaterialModelEnvCubemap;
+ }
+ else
+ {
+ if ( !m_pRC->m_pForcedMaterial && ( m_pRC->m_nForcedMaterialType != OVERRIDE_DEPTH_WRITE && m_pRC->m_nForcedMaterialType != OVERRIDE_SSAO_DEPTH_WRITE ) )
+ {
+ pMaterial = ppMaterials[index];
+ if ( !pMaterial )
+ {
+ Assert( 0 );
+ return 0;
+ }
+ }
+ else
+ {
+ materialFlags = 0;
+ pMaterial = m_pRC->m_pForcedMaterial;
+ if (m_pRC->m_nForcedMaterialType == OVERRIDE_BUILD_SHADOWS)
+ {
+ // Connect the original material up to the shadow building material
+ // Also bind the original material so its proxies are in the correct state
+ static unsigned int translucentCache = 0;
+ IMaterialVar* pOriginalMaterialVar = pMaterial->FindVarFast( "$translucent_material", &translucentCache );
+ Assert( pOriginalMaterialVar );
+ IMaterial *pOriginalMaterial = ppMaterials[index];
+ if ( pOriginalMaterial )
+ {
+ // Disable any alpha modulation on the original material that was left over from when it was last rendered
+ pOriginalMaterial->AlphaModulate( 1.0f );
+ pRenderContext->Bind( pOriginalMaterial, pClientRenderable );
+ if ( pOriginalMaterial->IsTranslucent() || pOriginalMaterial->IsAlphaTested() )
+ {
+ if ( pOriginalMaterialVar )
+ pOriginalMaterialVar->SetMaterialValue( pOriginalMaterial );
+ }
+ else
+ {
+ if ( pOriginalMaterialVar )
+ pOriginalMaterialVar->SetMaterialValue( NULL );
+ }
+ }
+ else
+ {
+ if ( pOriginalMaterialVar )
+ pOriginalMaterialVar->SetMaterialValue( NULL );
+ }
+ }
+ else if ( m_pRC->m_nForcedMaterialType == OVERRIDE_DEPTH_WRITE || m_pRC->m_nForcedMaterialType == OVERRIDE_SSAO_DEPTH_WRITE )
+ {
+ // Disable any alpha modulation on the original material that was left over from when it was last rendered
+ ppMaterials[index]->AlphaModulate( 1.0f );
+
+ // Bail if the material is still considered translucent after setting the AlphaModulate to 1.0
+ if ( ppMaterials[index]->IsTranslucent() )
+ {
+ return NULL;
+ }
+
+ static unsigned int originalTextureVarCache = 0;
+ IMaterialVar *pOriginalTextureVar = ppMaterials[index]->FindVarFast( "$basetexture", &originalTextureVarCache );
+
+ // Select proper override material
+ int nAlphaTest = (int) ( ppMaterials[index]->IsAlphaTested() && pOriginalTextureVar->IsTexture() ); // alpha tested base texture
+ int nNoCull = (int) ppMaterials[index]->IsTwoSided();
+ if ( m_pRC->m_nForcedMaterialType == OVERRIDE_SSAO_DEPTH_WRITE )
+ {
+ pMaterial = m_pSSAODepthWrite[nAlphaTest][nNoCull];
+ }
+ else
+ {
+ pMaterial = m_pDepthWrite[nAlphaTest][nNoCull];
+ }
+
+ // If we're alpha tested, we should set up the texture variables from the original material
+ if ( nAlphaTest != 0 )
+ {
+ static unsigned int originalTextureFrameVarCache = 0;
+ IMaterialVar *pOriginalTextureFrameVar = ppMaterials[index]->FindVarFast( "$frame", &originalTextureFrameVarCache );
+ static unsigned int originalAlphaRefCache = 0;
+ IMaterialVar *pOriginalAlphaRefVar = ppMaterials[index]->FindVarFast( "$AlphaTestReference", &originalAlphaRefCache );
+
+ static unsigned int textureVarCache = 0;
+ IMaterialVar *pTextureVar = pMaterial->FindVarFast( "$basetexture", &textureVarCache );
+ static unsigned int textureFrameVarCache = 0;
+ IMaterialVar *pTextureFrameVar = pMaterial->FindVarFast( "$frame", &textureFrameVarCache );
+ static unsigned int alphaRefCache = 0;
+ IMaterialVar *pAlphaRefVar = pMaterial->FindVarFast( "$AlphaTestReference", &alphaRefCache );
+
+ if ( pOriginalTextureVar->IsTexture() ) // If $basetexture is defined
+ {
+ if( pTextureVar && pOriginalTextureVar )
+ {
+ pTextureVar->SetTextureValue( pOriginalTextureVar->GetTextureValue() );
+ }
+
+ if( pTextureFrameVar && pOriginalTextureFrameVar )
+ {
+ pTextureFrameVar->SetIntValue( pOriginalTextureFrameVar->GetIntValue() );
+ }
+
+ if( pAlphaRefVar && pOriginalAlphaRefVar )
+ {
+ pAlphaRefVar->SetFloatValue( pOriginalAlphaRefVar->GetFloatValue() );
+ }
+ }
+ }
+ }
+ }
+
+ // Set this bool to check after the bind below
+ bCheckForConVarDrawTranslucentSubModels = true;
+
+ if ( m_pRC->m_nForcedMaterialType != OVERRIDE_DEPTH_WRITE && m_pRC->m_nForcedMaterialType != OVERRIDE_SSAO_DEPTH_WRITE)
+ {
+ // Try to set the alpha based on the blend
+ pMaterial->AlphaModulate( m_pRC->m_AlphaMod );
+
+ // Try to set the color based on the colormod
+ pMaterial->ColorModulate( m_pRC->m_ColorMod[0], m_pRC->m_ColorMod[1], m_pRC->m_ColorMod[2] );
+ }
+ }
+
+ lighting = R_StudioComputeLighting( pMaterial, materialFlags, pColorMeshes );
+ if ( lighting == LIGHTING_MOUTH )
+ {
+ if ( !m_pRC->m_Config.bTeeth || !R_TeethAreVisible() )
+ return NULL;
+ // skin it and light it, but only if we need to.
+ if ( m_pRC->m_Config.m_bSupportsVertexAndPixelShaders )
+ {
+ R_MouthSetupVertexShader( pMaterial );
+ }
+ }
+
+ // TODO: It's possible we don't want to use the color texels--for example because of a convar.
+ // We should check that here in addition to whether or not we have the data available.
+ static unsigned int lightmapVarCache = 0;
+ IMaterialVar *pLightmapVar = pMaterial->FindVarFast( "$lightmap", &lightmapVarCache );
+ if ( pLightmapVar )
+ {
+ ITexture* newTex = pColorMeshes ? pColorMeshes->m_pLightmap : NULL;
+
+ if (newTex)
+ pLightmapVar->SetTextureValue(newTex);
+ else
+ pLightmapVar->SetUndefined();
+ }
+
+ pRenderContext->Bind( pMaterial, pClientRenderable );
+
+ if ( bCheckForConVarDrawTranslucentSubModels )
+ {
+ bool translucent = pMaterial->IsTranslucent();
+
+ if (( m_bDrawTranslucentSubModels && !translucent ) ||
+ ( !m_bDrawTranslucentSubModels && translucent ))
+ {
+ m_bSkippedMeshes = true;
+ return NULL;
+ }
+ }
+
+ return pMaterial;
+}
+
+
+
+//=============================================================================
+
+
+/*
+=================
+R_StudioSetupModel
+ based on the body part, figure out which mesh it should be using.
+inputs:
+outputs:
+ pstudiomesh
+ pmdl
+=================
+*/
+int R_StudioSetupModel( int bodypart, int entity_body, mstudiomodel_t **ppSubModel,
+ const studiohdr_t *pStudioHdr )
+{
+ int index;
+ mstudiobodyparts_t *pbodypart;
+
+ if (bodypart > pStudioHdr->numbodyparts)
+ {
+ ConDMsg ("R_StudioSetupModel: no such bodypart %d\n", bodypart);
+ bodypart = 0;
+ }
+
+ pbodypart = pStudioHdr->pBodypart( bodypart );
+
+ if ( pbodypart->base == 0 )
+ {
+ Warning( "Model has missing body part: %s\n", pStudioHdr->pszName() );
+ Assert( 0 );
+ }
+ index = entity_body / pbodypart->base;
+ index = index % pbodypart->nummodels;
+
+ Assert( ppSubModel );
+ *ppSubModel = pbodypart->pModel( index );
+ return index;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Generates the PoseToBone Matrix nessecary to align the given bone with the
+// world.
+//-----------------------------------------------------------------------------
+static void ScreenAlignBone( matrix3x4_t *pPoseToWorld, mstudiobone_t *pCurBone,
+ const Vector& vecViewOrigin, const matrix3x4_t &boneToWorld )
+{
+ // Grab the world translation:
+ Vector vT( boneToWorld[0][3], boneToWorld[1][3], boneToWorld[2][3] );
+
+ // Construct the coordinate frame:
+ // Initialized to get rid of compiler
+ Vector vX, vY, vZ;
+
+ if( pCurBone->flags & BONE_SCREEN_ALIGN_SPHERE )
+ {
+ vX = vecViewOrigin - vT;
+ VectorNormalize(vX);
+ vZ = Vector(0,0,1);
+ vY = vZ.Cross(vX);
+ VectorNormalize(vY);
+ vZ = vX.Cross(vY);
+ VectorNormalize(vZ);
+ }
+ else
+ {
+ Assert( pCurBone->flags & BONE_SCREEN_ALIGN_CYLINDER );
+ vX.Init( boneToWorld[0][0], boneToWorld[1][0], boneToWorld[2][0] );
+ vZ = vecViewOrigin - vT;
+ VectorNormalize(vZ);
+ vY = vZ.Cross(vX);
+ VectorNormalize(vY);
+ vZ = vX.Cross(vY);
+ VectorNormalize(vZ);
+ }
+
+ matrix3x4_t matBoneBillboard(
+ vX.x, vY.x, vZ.x, vT.x,
+ vX.y, vY.y, vZ.y, vT.y,
+ vX.z, vY.z, vZ.z, vT.z );
+ ConcatTransforms( matBoneBillboard, pCurBone->poseToBone, *pPoseToWorld );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes PoseToWorld from BoneToWorld
+//-----------------------------------------------------------------------------
+void ComputePoseToWorld( matrix3x4_t *pPoseToWorld, studiohdr_t *pStudioHdr, int boneMask, const Vector& vecViewOrigin, const matrix3x4_t *pBoneToWorld )
+{
+ if ( pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP )
+ {
+ // by definition, these always have an identity poseToBone transform
+ MatrixCopy( pBoneToWorld[ 0 ], pPoseToWorld[ 0 ] );
+ return;
+ }
+
+ if ( !pStudioHdr->pLinearBones() )
+ {
+ // convert bone to world transformations into pose to world transformations
+ for (int i = 0; i < pStudioHdr->numbones; i++)
+ {
+ mstudiobone_t *pCurBone = pStudioHdr->pBone( i );
+ if ( !(pCurBone->flags & boneMask) )
+ continue;
+
+ ConcatTransforms( pBoneToWorld[ i ], pCurBone->poseToBone, pPoseToWorld[ i ] );
+ }
+ }
+ else
+ {
+ mstudiolinearbone_t *pLinearBones = pStudioHdr->pLinearBones();
+
+ // convert bone to world transformations into pose to world transformations
+ for (int i = 0; i < pStudioHdr->numbones; i++)
+ {
+ if ( !(pLinearBones->flags(i) & boneMask) )
+ continue;
+
+ ConcatTransforms( pBoneToWorld[ i ], pLinearBones->poseToBone(i), pPoseToWorld[ i ] );
+ }
+ }
+
+#if 0
+ // These don't seem to be used in any existing QC file, re-enable in a future project?
+ // Pretransform
+ if( !( pCurBone->flags & ( BONE_SCREEN_ALIGN_SPHERE | BONE_SCREEN_ALIGN_CYLINDER )))
+ {
+ ConcatTransforms( pBoneToWorld[ i ], pCurBone->poseToBone, pPoseToWorld[ i ] );
+ }
+ else
+ {
+ // If this bone is screen aligned, then generate a PoseToWorld matrix that billboards the bone
+ ScreenAlignBone( &pPoseToWorld[i], pCurBone, vecViewOrigin, pBoneToWorld[i] );
+ }
+#endif
+}
+
+