From a7c5340a51e91f57d489c351276a83e90de1c91d Mon Sep 17 00:00:00 2001 From: hlstriker Date: Thu, 7 Nov 2013 14:29:40 +0000 Subject: [PATCH] Implemented shader editor. Add "-shaderedit" to startup line to use it. See https://developer.valvesoftware.com/wiki/Category:SourceShaderEditor for details. --- .../ShaderEditor/Grass/CGrassCluster.cpp | 1400 ++++++++++++++ .../client/ShaderEditor/Grass/CGrassCluster.h | 132 ++ .../ShaderEditor/Grass/README_GRASS.txt | 144 ++ .../Grass/dprop_and_shadow_patch.patch | 80 + .../client/ShaderEditor/ISEdit_ModelRender.h | 41 + .../game/client/ShaderEditor/IVShaderEditor.h | 128 ++ .../client/ShaderEditor/SEdit_ModelRender.cpp | 424 +++++ .../client/ShaderEditor/SEdit_ModelRender.h | 48 + .../ShaderEditor/ShaderEditorSystem.cpp | 1629 +++++++++++++++++ .../client/ShaderEditor/ShaderEditorSystem.h | 59 + mp/src/game/client/client_ff.vpc | 21 +- mp/src/game/client/viewrender.cpp | 15 + 12 files changed, 4120 insertions(+), 1 deletion(-) create mode 100644 mp/src/game/client/ShaderEditor/Grass/CGrassCluster.cpp create mode 100644 mp/src/game/client/ShaderEditor/Grass/CGrassCluster.h create mode 100644 mp/src/game/client/ShaderEditor/Grass/README_GRASS.txt create mode 100644 mp/src/game/client/ShaderEditor/Grass/dprop_and_shadow_patch.patch create mode 100644 mp/src/game/client/ShaderEditor/ISEdit_ModelRender.h create mode 100644 mp/src/game/client/ShaderEditor/IVShaderEditor.h create mode 100644 mp/src/game/client/ShaderEditor/SEdit_ModelRender.cpp create mode 100644 mp/src/game/client/ShaderEditor/SEdit_ModelRender.h create mode 100644 mp/src/game/client/ShaderEditor/ShaderEditorSystem.cpp create mode 100644 mp/src/game/client/ShaderEditor/ShaderEditorSystem.h diff --git a/mp/src/game/client/ShaderEditor/Grass/CGrassCluster.cpp b/mp/src/game/client/ShaderEditor/Grass/CGrassCluster.cpp new file mode 100644 index 00000000..8cc07cad --- /dev/null +++ b/mp/src/game/client/ShaderEditor/Grass/CGrassCluster.cpp @@ -0,0 +1,1400 @@ + +#include "cbase.h" +#include "ShaderEditor/Grass/CGrassCluster.h" +#include "view.h" +#include "viewrender.h" +#include "idebugoverlaypanel.h" +#include "engine/IVDebugOverlay.h" +#include "iclientshadowmgr.h" +#include "clientleafsystem.h" +#include "materialsystem/ITexture.h" +#include "materialsystem/IMaterialVar.h" +#include "collisionutils.h" +#include "fasttimer.h" + +#define CONSTBOXEXTENT_LIGHT 1000 +#define CONSTBOXEXTENT_COL 500 + +static ConVar gcluster_objectsPerHint( "grasscluster_objects_per_hint", "8" ); +static ConVar gcluster_debug( "grasscluster_debug", "0" ); +static ConVar gcluster_enable( "grasscluster_enable", "1" ); +static ConVar gcluster_enable_flashlight( "grasscluster_enable_flashlightSupport", "1" ); +static ConVar gcluster_enable_morph( "grasscluster_enable_morph", "1" ); +static ConVar gcluster_cullDist( "grasscluster_cullDist", "4096" ); + +static ConVar gcluster_LOD_enable( "grasscluster_LOD_enable", "1" ); +static ConVar gcluster_LOD_transitionDist( "grasscluster_LOD_transitionDist", "2048" ); +static ConVar gcluster_LOD_objects_per_hint( "grasscluster_LOD_objects_per_hint", "1" ); + +static ConVar gcluster_grass_height_small_min( "grasscluster_grass_height_small_min", "20" ); +static ConVar gcluster_grass_height_small_max( "grasscluster_grass_height_small_max", "30" ); +static ConVar gcluster_grass_height_med_min( "grasscluster_grass_height_med_min", "30" ); +static ConVar gcluster_grass_height_med_max( "grasscluster_grass_height_med_max", "50" ); +static ConVar gcluster_grass_height_huge_min( "grasscluster_grass_height_huge_min", "50" ); +static ConVar gcluster_grass_height_huge_max( "grasscluster_grass_height_huge_max", "70" ); + +static ConVar gcluster_grass_width_small_min( "grasscluster_grass_width_small_min", "25" ); +static ConVar gcluster_grass_width_small_max( "grasscluster_grass_width_small_max", "35" ); +static ConVar gcluster_grass_width_med_min( "grasscluster_grass_width_med_min", "40" ); +static ConVar gcluster_grass_width_med_max( "grasscluster_grass_width_med_max", "60" ); +static ConVar gcluster_grass_width_huge_min( "grasscluster_grass_width_huge_min", "60" ); +static ConVar gcluster_grass_width_huge_max( "grasscluster_grass_width_huge_max", "80" ); + +static ConVar gcluster_clusterMaxQuads( "grasscluster_grass_clusterMaxQuads", "-1" ); + +static ConVar gcluster_grass_type_huge_oddness( "grasscluster_grass_type_huge_oddness", "20" ); +static ConVar gcluster_grass_type_small_oddness( "grasscluster_grass_type_small_oddness", "2" ); + +static ConVar gcluster_grass_meadow_scale( "grasscluster_grass_meadow_scale", "1" ); + +static ConVar gcluster_grass_wind_angle( "grasscluster_grass_wind_angle", "70" ); +static ConVar gcluster_grass_wind_strength( "grasscluster_grass_wind_strength", "1" ); + +static ConVar gcluster_sprite_index( "grasscluster_sprite_index", "0" ); + +static ConVar gcluster_grass_morph_delay( "grasscluster_grass_meadow_morph_delay", "5" ); +static ConVar gcluster_grass_morph_framelag( "grasscluster_grass_meadow_morph_framelag", "0.1" ); +static ConVar gcluster_grass_morph_speed( "grasscluster_grass_meadow_morph_speed", "1" ); + +static ConVar gcluster_grass_terrain_offset_min( "grasscluster_grass_terrain_offset_min", "20" ); +static ConVar gcluster_grass_terrain_offset_exp( "grasscluster_grass_terrain_offset_exp", "0.5" ); +static ConVar gcluster_grass_terrain_offset_multi( "grasscluster_grass_terrain_offset_multi", "2" ); + +extern ConVar r_DrawDetailProps; + +const char *szSpriteMaterials[] = { + "detail/detailsprites_editor", + "detail/grass_lawn_cut", + "detail/grass_lawn_cut_dark", + "detail/grass_lawn_cut_lite", +}; +const int iSpriteMaterialCount = ARRAYSIZE( szSpriteMaterials ); + +CON_COMMAND( grasscluster_flush, "" ) +{ + CGrassClusterManager::GetInstance()->ClearClusterData(); +} + +CON_COMMAND( grasscluster_preset_density_high, "" ) +{ + gcluster_grass_type_huge_oddness.Revert(); + gcluster_objectsPerHint.Revert(); + gcluster_LOD_objects_per_hint.Revert(); + gcluster_cullDist.Revert(); + CGrassClusterManager::GetInstance()->ClearClusterData(); +} +CON_COMMAND( grasscluster_preset_density_med, "" ) +{ + gcluster_grass_type_huge_oddness.SetValue( 17 ); + gcluster_objectsPerHint.SetValue( "5" ); + gcluster_LOD_objects_per_hint.Revert(); + gcluster_cullDist.SetValue( gcluster_cullDist.GetDefault() ); + CGrassClusterManager::GetInstance()->ClearClusterData(); +} +CON_COMMAND( grasscluster_preset_density_low, "" ) +{ + gcluster_grass_type_huge_oddness.SetValue( 10 ); + gcluster_objectsPerHint.SetValue( "2" ); + gcluster_LOD_objects_per_hint.Revert(); + gcluster_cullDist.SetValue( gcluster_cullDist.GetDefault() ); + CGrassClusterManager::GetInstance()->ClearClusterData(); +} + +CON_COMMAND( grasscluster_preset_height_monstrous, "" ) +{ + gcluster_grass_height_small_min.SetValue( 50 ); + gcluster_grass_height_small_max.SetValue( 60 ); + gcluster_grass_height_med_min.SetValue( 80 ); + gcluster_grass_height_med_max.SetValue( 95 ); + gcluster_grass_height_huge_min.SetValue( 100 ); + gcluster_grass_height_huge_max.SetValue( 130 ); + + gcluster_grass_width_small_min.SetValue( 60 ); + gcluster_grass_width_small_max.SetValue( 70 ); + gcluster_grass_width_med_min.SetValue( 85 ); + gcluster_grass_width_med_max.SetValue( 100 ); + gcluster_grass_width_huge_min.SetValue( 110 ); + gcluster_grass_width_huge_max.SetValue( 140 ); + CGrassClusterManager::GetInstance()->ClearClusterData(); +} +CON_COMMAND( grasscluster_preset_height_high, "" ) +{ + gcluster_grass_height_small_min.SetValue( gcluster_grass_height_small_min.GetDefault() ); + gcluster_grass_height_small_max.SetValue( gcluster_grass_height_small_max.GetDefault() ); + gcluster_grass_height_med_min.SetValue( gcluster_grass_height_med_min.GetDefault() ); + gcluster_grass_height_med_max.SetValue( gcluster_grass_height_med_max.GetDefault() ); + gcluster_grass_height_huge_min.SetValue( gcluster_grass_height_huge_min.GetDefault() ); + gcluster_grass_height_huge_max.SetValue( gcluster_grass_height_huge_max.GetDefault() ); + + gcluster_grass_width_small_min.SetValue( gcluster_grass_width_small_min.GetDefault() ); + gcluster_grass_width_small_max.SetValue( gcluster_grass_width_small_max.GetDefault() ); + gcluster_grass_width_med_min.SetValue( gcluster_grass_width_med_min.GetDefault() ); + gcluster_grass_width_med_max.SetValue( gcluster_grass_width_med_max.GetDefault() ); + gcluster_grass_width_huge_min.SetValue( gcluster_grass_width_huge_min.GetDefault() ); + gcluster_grass_width_huge_max.SetValue( gcluster_grass_width_huge_max.GetDefault() ); + CGrassClusterManager::GetInstance()->ClearClusterData(); +} +CON_COMMAND( grasscluster_preset_height_med, "" ) +{ + gcluster_grass_height_small_min.SetValue( 10 ); + gcluster_grass_height_small_max.SetValue( 15 ); + gcluster_grass_height_med_min.SetValue( 15 ); + gcluster_grass_height_med_max.SetValue( 30 ); + gcluster_grass_height_huge_min.SetValue( 25 ); + gcluster_grass_height_huge_max.SetValue( 45 ); + + gcluster_grass_width_small_min.SetValue( 15 ); + gcluster_grass_width_small_max.SetValue( 20 ); + gcluster_grass_width_med_min.SetValue( 20 ); + gcluster_grass_width_med_max.SetValue( 35 ); + gcluster_grass_width_huge_min.SetValue( 30 ); + gcluster_grass_width_huge_max.SetValue( 50 ); + CGrassClusterManager::GetInstance()->ClearClusterData(); +} +CON_COMMAND( grasscluster_preset_height_low, "" ) +{ + gcluster_grass_height_small_min.SetValue( 8 ); + gcluster_grass_height_small_max.SetValue( 12 ); + gcluster_grass_height_med_min.SetValue( 10 ); + gcluster_grass_height_med_max.SetValue( 15 ); + gcluster_grass_height_huge_min.SetValue( 12 ); + gcluster_grass_height_huge_max.SetValue( 25 ); + + gcluster_grass_width_small_min.SetValue( 10 ); + gcluster_grass_width_small_max.SetValue( 15 ); + gcluster_grass_width_med_min.SetValue( 15 ); + gcluster_grass_width_med_max.SetValue( 20 ); + gcluster_grass_width_huge_min.SetValue( 20 ); + gcluster_grass_width_huge_max.SetValue( 30 ); + CGrassClusterManager::GetInstance()->ClearClusterData(); +} + +CON_COMMAND( grasscluster_preset_meadow_ultra, "" ) +{ + gcluster_grass_height_small_min.SetValue( 8 ); + gcluster_grass_height_small_max.SetValue( 12 ); + gcluster_grass_height_med_min.SetValue( 10 ); + gcluster_grass_height_med_max.SetValue( 15 ); + gcluster_grass_height_huge_min.SetValue( 12 ); + gcluster_grass_height_huge_max.SetValue( 25 ); + + gcluster_grass_width_small_min.SetValue( 10 ); + gcluster_grass_width_small_max.SetValue( 15 ); + gcluster_grass_width_med_min.SetValue( 15 ); + gcluster_grass_width_med_max.SetValue( 20 ); + gcluster_grass_width_huge_min.SetValue( 20 ); + gcluster_grass_width_huge_max.SetValue( 30 ); + + gcluster_grass_type_huge_oddness.SetValue( 100 ); + gcluster_cullDist.SetValue( 4096 ); + gcluster_objectsPerHint.SetValue( 40 ); + gcluster_LOD_objects_per_hint.SetValue( 5 ); + //gcluster_sprite_index.SetValue( 2 ); + CGrassClusterManager::GetInstance()->ClearClusterData(); +} + +#ifdef SHADEREDITOR_FORCE_ENABLED +#endif + +Vector FetchLightSamples( Vector pos ) +{ + //return engine->GetLightForPoint( pos, true ); + Vector dir; + QAngle ang; + Vector res( vec3_origin ); + for ( int y = 0; y < 8; y++ ) + { + for ( int x = 0; x < 4; x++ ) + { + ang.Init( x * (-90.0f/7.0f) - (90.0f/3.0f), y * (360.0f/9.0f), 0 ); + AngleVectors( ang, &dir ); + dir = dir * 32.0f + pos; + res += engine->GetLightForPoint( dir, true ); + } + } + res /= 32.0f; + + for ( int i = 0; i < 3; i++ ) + res[i] = RemapValClamped( res[i], 0, 0.5f, 0, 1 ); + + return res; +} + +int GrassInfoSort( _grassClusterInfo const *c1, _grassClusterInfo const *c2 ) +{ + return ( c1->flSortDist < c2->flSortDist ) ? -1 : 1; +} + +_grassPressureData::_grassPressureData() +{ + iNumGrassObjects = 0; + + vecDir = NULL; + vecPos = NULL; + flAmt = NULL; + flHeight = NULL; + flLastMoveTime = NULL; + flLastUpdateTime = NULL; + bDirty = NULL; +} +_grassPressureData::~_grassPressureData() +{ + Assert( flAmt && vecDir && bDirty && vecPos ); + + delete [] vecDir; + delete [] vecPos; + delete [] flAmt; + delete [] flHeight; + delete [] flLastMoveTime; + delete [] flLastUpdateTime; + delete [] bDirty; +} +void _grassPressureData::Init( int num ) +{ + delete [] vecDir; + delete [] vecPos; + delete [] flAmt; + delete [] flHeight; + delete [] flLastMoveTime; + delete [] flLastUpdateTime; + delete [] bDirty; + + iNumGrassObjects = num; + + vecDir = new Vector[ iNumGrassObjects ]; + vecPos = new Vector[ iNumGrassObjects ]; + flAmt = new float[ iNumGrassObjects ]; + flHeight = new float[ iNumGrassObjects ]; + flLastMoveTime = new float[ iNumGrassObjects ]; + flLastUpdateTime = new float[ iNumGrassObjects ]; + bDirty = new bool[ iNumGrassObjects ]; + + Q_memset( vecDir, 0, sizeof( Vector ) * iNumGrassObjects ); + Q_memset( vecPos, 0, sizeof( Vector ) * iNumGrassObjects ); + Q_memset( flAmt, 0, sizeof( float ) * iNumGrassObjects ); + Q_memset( flHeight, 0, sizeof( float ) * iNumGrassObjects ); + Q_memset( flLastMoveTime, 0, sizeof( float ) * iNumGrassObjects ); + Q_memset( flLastUpdateTime, 0, sizeof( float ) * iNumGrassObjects ); + Q_memset( bDirty, 0, sizeof( bool ) * iNumGrassObjects ); +} + + +_grassClusterInfo::_grassClusterInfo() +{ + flSortDist = -1; +} + + +_grassClusterData::_grassClusterData() +{ + pGrassMesh = NULL; + pPressureInfo = NULL; + pLOD = NULL; + iNextLodThreshold = 0; + iNumQuads = -1; + lighting = NULL; + iLPatchSize_x = iLPatchSize_y = 0; + flLPatchStep_x = flLPatchStep_y = 0; +} +_grassClusterData::~_grassClusterData() +{ + // DestroyLightingPatch(); +} +void _grassClusterData::Destroy() +{ + if ( pLOD ) + pLOD->Destroy(); + delete pLOD; + + if ( pGrassMesh ) + { + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->DestroyStaticMesh( pGrassMesh ); + pGrassMesh = NULL; + } + + if ( pPressureInfo ) + { + delete pPressureInfo; + pPressureInfo = NULL; + } +} +int _grassClusterData::Draw() +{ + const bool bDrawLOD = pLOD != NULL && + (MainViewOrigin() - pos).LengthSqr() > iNextLodThreshold; + + if ( bDrawLOD ) + return pLOD->Draw(); + else + { + Assert( pGrassMesh ); + + pGrassMesh->Draw(); + return iNumQuads; + } +} +void _grassClusterData::CreateLightingPatch( const CUtlVector< _grassClusterInfo > &hints ) +{ + Assert( !lighting ); + + CUtlVector< _grassClusterInfo > localHints; + localHints.AddVectorToTail( hints ); + + for ( int i = 0; i < localHints.Count(); i++ ) + { + if ( !IsPointInBox( localHints[i].orig, + extents_min - Vector(1,1,1) * CONSTBOXEXTENT_LIGHT, + extents_max + Vector(1,1,1) * CONSTBOXEXTENT_LIGHT ) ) + { + localHints.Remove(i); + i--; + } + } + + const float sizeScaling = 0.006f; //0.006f; + float deltax = extents_max.x - extents_min.x; + float deltay = extents_max.y - extents_min.y; + iLPatchSize_x = deltax * sizeScaling + 1; + iLPatchSize_y = deltay * sizeScaling + 1; + flLPatchStep_x = (iLPatchSize_x > 0) ? deltax / (iLPatchSize_x-1) : 0; + flLPatchStep_y = (iLPatchSize_y > 0) ? deltay / (iLPatchSize_y-1) : 0; + + lighting = new Vector[iLPatchSize_x * iLPatchSize_y]; + + for ( int x = 0; x < iLPatchSize_x; x++ ) + { + for ( int y = 0; y < iLPatchSize_y; y++ ) + { + int slot = y * iLPatchSize_x + x; + const Vector pos = extents_min + + Vector( x * flLPatchStep_x, y * flLPatchStep_y, (extents_max.z-extents_min.z) * 0.5f ); + + //DebugDrawLine( pos, pos+Vector(0,0,100), 0,0,255,false, 10.0f ); + + Vector &light = lighting[ slot ]; + + for ( int i = 0; i < localHints.Count(); i++ ) + localHints[i].flSortDist = (localHints[i].orig - pos).LengthSqr(); + localHints.Sort( GrassInfoSort ); + + light.Init( 0, 0, 0 ); + int l = 0; + for ( ; l < min( localHints.Count(), 20 ); l++ ) + light += localHints[l].color.AsVector3D(); + + if ( l ) + light /= (float)l; + + //Vector sample = FetchLightSamples( pos + Vector( 0, 0, 40 ) ); + //for ( int i = 0; i < 3; i++ ) + // light[i] = min( light[i], sample[i] ); + //light = ( light + ) * 0.5f; + + float intensity = (light.x+light.y+light.z) * 0.333334f; + for ( int i = 0; i < 3; i++ ) + light[i] *= intensity; // * intensity; + + //float intensity = light.Length(); + //intensity *= intensity; + //light.NormalizeInPlace(); + //light *= intensity; + + Assert( IsFinite( light.x ) && IsFinite( light.y ) && IsFinite( light.z ) ); + } + } + + localHints.Purge(); +} +const Vector _grassClusterData::GetLightingForPoint( const Vector &pos ) +{ + Assert( lighting ); + Assert( iLPatchSize_x && iLPatchSize_y ); + + if ( iLPatchSize_x == 1 && iLPatchSize_y == 1 ) + return lighting[0]; + + float delta_x = ( pos.x - extents_min.x ); + float delta_y = ( pos.y - extents_min.y ); + int x0 = delta_x / flLPatchStep_x; + int y0 = delta_y / flLPatchStep_y; + float interp_x = clamp( ( delta_x - x0 * flLPatchStep_x ) / flLPatchStep_x, 0, 1 ); + float interp_y = clamp( ( delta_y - y0 * flLPatchStep_y ) / flLPatchStep_y, 0, 1 ); + + x0 = max( 0, min( x0, iLPatchSize_x - 2 ) ); + y0 = max( 0, min( y0, iLPatchSize_y - 2 ) ); + + int x1 = min( x0 + 1, iLPatchSize_x - 1 ); + int y1 = min( y0 + 1, iLPatchSize_y - 1 ); + + Assert( x1 + iLPatchSize_x * y1 < iLPatchSize_x * iLPatchSize_y ); + + Vector samples[2][2]; + samples[0][0] = lighting[ x0 + iLPatchSize_x * y0 ]; + samples[0][1] = lighting[ x0 + iLPatchSize_x * y1 ]; + samples[1][1] = lighting[ x1 + iLPatchSize_x * y1 ]; + samples[1][0] = lighting[ x1 + iLPatchSize_x * y0 ]; + + Assert( IsFinite( samples[0][0].x ) && IsFinite( samples[0][0].y ) && IsFinite( samples[0][0].z ) ); + Assert( IsFinite( samples[0][1].x ) && IsFinite( samples[0][1].y ) && IsFinite( samples[0][1].z ) ); + Assert( IsFinite( samples[1][0].x ) && IsFinite( samples[1][0].y ) && IsFinite( samples[1][0].z ) ); + Assert( IsFinite( samples[1][1].x ) && IsFinite( samples[1][1].y ) && IsFinite( samples[1][1].z ) ); + Assert( interp_x >= 0 && interp_x <= 1 ); + Assert( interp_y >= 0 && interp_y <= 1 ); + + return Lerp( interp_y, + Lerp( interp_x, samples[0][0], samples[1][0] ), + Lerp( interp_x, samples[0][1], samples[1][1] ) ); +} +void _grassClusterData::DestroyLightingPatch() +{ + Assert( lighting ); + + delete [] lighting; + lighting = NULL; +} + + +clusterMaterial::clusterMaterial() +{ + ivar_dir = ivar_ang = 0; +} +void clusterMaterial::Init( const char *pszMat ) +{ + ivar_dir = ivar_ang = 0; + mat.Init( pszMat, TEXTURE_GROUP_OTHER ); + + if ( mat.IsValid() && IsErrorMaterial( mat ) ) + mat.Shutdown(); +} +void clusterMaterial::Shutdown() +{ + ivar_dir = ivar_ang = 0; + mat.Shutdown(); +} +bool clusterMaterial::IsValid() +{ + return mat.IsValid(); +} +IMaterial *clusterMaterial::GetMaterial() +{ + if ( mat.IsValid() ) + return mat; + return NULL; + //ivar_dir +} +IMaterialVar *clusterMaterial::GetVarDir() +{ + if ( !GetMaterial() ) + return NULL; + return GetMaterial()->FindVarFast( "$MUTABLE_01", &ivar_dir ); +} +IMaterialVar *clusterMaterial::GetVarAng() +{ + if ( !GetMaterial() ) + return NULL; + return GetMaterial()->FindVarFast( "$MUTABLE_02", &ivar_ang ); +} + + +void ReleaseGrassCluster() +{ + CGrassClusterManager::GetInstance()->ClearClusterData(); +} + +CGrassClusterManager::CGrassClusterManager() : CAutoGameSystemPerFrame( "grassclustersystem" ) +{ + m_iDrawnQuads = 0; + m_iDrawnCluster = 0; + m_iDrawnPerDrawcall = 0; + m_iDrawnEngineMax = 0; + m_flMorphTime = 0; + + //m_refMaterial = NULL; + m_refMaterials = NULL; +} + +CGrassClusterManager::~CGrassClusterManager() +{ + m_hClusterInfo.Purge(); + m_hClusterData.Purge(); + + //delete [] m_refMaterial; + delete [] m_refMaterials; +} + +static CGrassClusterManager g_pGrassClusterManager; + +CGrassClusterManager *CGrassClusterManager::GetInstance() +{ + return &g_pGrassClusterManager; +} + +bool CGrassClusterManager::Init() +{ + materials->AddReleaseFunc( &ReleaseGrassCluster ); + + return true; +} + +void CGrassClusterManager::Shutdown() +{ + materials->RemoveReleaseFunc( &ReleaseGrassCluster ); +} + +IMaterial *CGrassClusterManager::GetActiveMaterial() +{ + if ( !m_refMaterials ) + return NULL; + + if ( m_iCurrentMaterial < 0 || m_iCurrentMaterial >= iSpriteMaterialCount ) + return NULL; + + return m_refMaterials[ m_iCurrentMaterial ].GetMaterial(); +} + +void CGrassClusterManager::LevelInitPostEntity() +{ + delete [] m_refMaterials; + m_refMaterials = new clusterMaterial[ iSpriteMaterialCount ]; + + for ( int i = 0; i < iSpriteMaterialCount; i++ ) + { + m_refMaterials[i].Init( szSpriteMaterials[i] ); + + if ( !m_refMaterials[i].IsValid() ) + Warning( "unable to initialize grass material: %s\n", szSpriteMaterials[i] ); + } + + + //m_refMaterial.Init( "detail/detailsprites_editor", TEXTURE_GROUP_OTHER ); + //m_refMaterial.Init( "debug/debugspritewireframe", TEXTURE_GROUP_OTHER ); + +} + +void CGrassClusterManager::LevelShutdownPostEntity() +{ + m_hClusterInfo.Purge(); + + ClearClusterData(); + + for ( int i = 0; i < iSpriteMaterialCount; i++ ) + m_refMaterials[i].Shutdown(); +} + +void CGrassClusterManager::Update( float frametime ) +{ + if ( !gcluster_enable.GetInt() ) + return; + + m_iCurrentMaterial = clamp( gcluster_sprite_index.GetInt(), 0, iSpriteMaterialCount - 1 ); + + const bool matValid = GetActiveMaterial() != NULL; + + if ( matValid ) + { + QAngle wAng( 0, gcluster_grass_wind_angle.GetFloat(), 0 ); + Vector wVec; + AngleVectors( wAng, &wVec ); + wVec.y *= -1.0f; + wVec *= gcluster_grass_wind_strength.GetFloat(); + + m_refMaterials[ m_iCurrentMaterial ].GetVarDir()->SetVecValue( wVec.Base(), 3 ); + m_refMaterials[ m_iCurrentMaterial ].GetVarAng()->SetFloatValue( DEG2RAD( wAng.y + 225.0f ) ); + } + + if ( m_hClusterInfo.Count() && !m_hClusterData.Count() && matValid ) + { + GenerateClusterData(); + Assert( m_hClusterInfo.Count() >= m_hClusterData.Count() ); + } + + m_iDrawnQuads = 0; + m_iDrawnCluster = 0; + m_flMorphTime = 0; + UpdateMorphInfo(); +} + +class EnumFlashlights : public IClientLeafShadowEnum +{ +public: + virtual void EnumShadow( ClientShadowHandle_t clienthandle ) + { + const ShadowType_t type = g_pClientShadowMgr->GetActualShadowCastType( clienthandle ); + if ( type != SHADOWS_RENDER_TO_DEPTH_TEXTURE ) + return; + + ShadowHandle_t handle = g_pClientShadowMgr->GetShadowHandle( clienthandle ); + + shadowList.AddToTail( handle ); + }; + + CUtlVector< ShadowHandle_t >shadowList; +}; + +void CGrassClusterManager::PreRender() +{ +} + +void CGrassClusterManager::PostRender() +{ + if ( gcluster_debug.GetInt() ) + { + int numQuads = 0; + for ( int i = 0; i < m_hClusterData.Count(); i++ ) + numQuads += m_hClusterData[i].iNumQuads; + + engine->Con_NPrintf( 9, "grass cluster debugging:" ); + engine->Con_NPrintf( 10, "quads drawn: %i // clusters drawn: %i", m_iDrawnQuads, m_iDrawnCluster ); + engine->Con_NPrintf( 11, "quads generated: %i // clusters generated: %i", numQuads, m_hClusterData.Count() ); + + float efficiency_cur = 100.0f; + float efficiency_avg = 100.0f; + if ( m_iDrawnCluster && m_iDrawnPerDrawcall ) + efficiency_cur = m_iDrawnQuads / (float)m_iDrawnCluster / m_iDrawnPerDrawcall * 100.0f; + if ( numQuads && m_iDrawnPerDrawcall ) + efficiency_avg = numQuads / (float)m_hClusterData.Count() / m_iDrawnPerDrawcall * 100.0f; + + engine->Con_NPrintf( 13, "current efficiency: %3.1f%% // average efficiency: %3.1f%%", efficiency_cur, efficiency_avg ); + engine->Con_NPrintf( 14, "engine max quads: %i // cluster max quads: %i", m_iDrawnEngineMax, m_iDrawnPerDrawcall ); + + engine->Con_NPrintf( 16, "morphing took: %3.3f msec", m_flMorphTime ); + } +} + +void CGrassClusterManager::RenderClusters( bool bShadowDepth ) +{ + const bool bSupportFlashlight = gcluster_enable_flashlight.GetBool(); + const bool bFullDbg = gcluster_debug.GetInt() > 1; + + if ( !gcluster_enable.GetInt() ) + return; + + if ( bShadowDepth && !bSupportFlashlight ) + return; + + if ( view->GetDrawFlags() & DF_DRAWSKYBOX ) + return; + + IMaterial *pMat = GetActiveMaterial(); + + if ( !pMat ) + return; + + CUtlVector< Frustum_t > flashlightFrusta; + EnumFlashlights enumList; + + if ( !bShadowDepth && bSupportFlashlight ) + { + IWorldRenderList *pWorldRenderList = render->CreateWorldList(); + WorldListInfo_t *pListInfo = new WorldListInfo_t(); + VisOverrideData_t vOverride; + vOverride.m_vecVisOrigin = CurrentViewOrigin(); + vOverride.m_fDistToAreaPortalTolerance = FLT_MAX; + render->BuildWorldLists( pWorldRenderList, pListInfo, -1, &vOverride ); + + ClientLeafSystem()->EnumerateShadowsInLeaves( pListInfo->m_LeafCount, pListInfo->m_pLeafList, &enumList ); + for ( int i = 0; i < enumList.shadowList.Count(); i++ ) + flashlightFrusta.AddToTail( shadowmgr->GetFlashlightFrustum( enumList.shadowList[i] ) ); + + Assert( enumList.shadowList.Count() == flashlightFrusta.Count() ); + + SafeRelease( pWorldRenderList ); + delete pListInfo; + } + + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->Bind( pMat ); + + const float flCullDist = gcluster_cullDist.GetFloat(); + const float flCullDistSqr = flCullDist * flCullDist; + + for ( int i = 0; i < m_hClusterData.Count(); i++ ) + { + Assert( m_hClusterData[ i ].pGrassMesh ); + + Vector pos = m_hClusterData[i].pos; + //DebugDrawLine( pos, pos + Vector( 0, 0, 150 ), 255, 0, 0, false, 0.1f ); + + const float distSqr = ( CurrentViewOrigin() - m_hClusterData[ i ].pos ).LengthSqr(); + + if ( distSqr > flCullDistSqr ) + continue; + + if ( !engine->IsBoxInViewCluster( m_hClusterData[ i ].extents_min, m_hClusterData[ i ].extents_max ) ) + continue; + + if ( engine->CullBox( m_hClusterData[ i ].extents_min, m_hClusterData[ i ].extents_max ) ) + continue; + + //m_hClusterData[ i ].pGrassMesh->Draw(); + //m_iDrawnQuads += m_hClusterData[ i ].iNumQuads; + m_iDrawnQuads += m_hClusterData[ i ].Draw(); + m_iDrawnCluster++; + + if ( bFullDbg ) + debugoverlay->AddBoxOverlay( m_hClusterData[ i ].extents_min, vec3_origin, m_hClusterData[ i ].extents_max - m_hClusterData[ i ].extents_min, vec3_angle, 0, 255, 0, 20, -1 ); + } + + if ( !bShadowDepth && bSupportFlashlight && flashlightFrusta.Count() ) + { + Assert( g_pClientShadowMgr->GetNumShadowDepthtextures() >= flashlightFrusta.Count() ); + pRenderContext->SetFlashlightMode( true ); + + for ( int i = 0; i < flashlightFrusta.Count(); i++ ) + { + const FlashlightState_t &flState = shadowmgr->GetFlashlightState( enumList.shadowList[i] ); + ITexture *pTex = g_pClientShadowMgr->GetShadowDepthTex( i ); + + VMatrix a,b,c,d; + CViewSetup shadowView; + shadowView.m_flAspectRatio = 1.0f; + shadowView.x = shadowView.y = 0; + shadowView.width = pTex->GetActualWidth(); + shadowView.height = pTex->GetActualHeight(); + shadowView.m_bOrtho = false; + shadowView.origin = flState.m_vecLightOrigin; + QuaternionAngles( flState.m_quatOrientation, shadowView.angles ); + shadowView.fov = flState.m_fHorizontalFOVDegrees; + shadowView.zFar = shadowView.zFarViewmodel = flState.m_FarZ; + shadowView.zNear = shadowView.zNearViewmodel = flState.m_NearZ; + render->GetMatricesForView( shadowView, &a, &b, &c, &d ); + + VMatrix shadowToUnit; + MatrixBuildScale( shadowToUnit, 1.0f / 2, 1.0f / -2, 1.0f ); + shadowToUnit[0][3] = shadowToUnit[1][3] = 0.5f; + MatrixMultiply( shadowToUnit, c, d ); + + pRenderContext->SetFlashlightStateEx( flState, d, pTex ); + + for ( int c = 0; c < m_hClusterData.Count(); c++ ) + { + if ( R_CullBox( m_hClusterData[c].extents_min, m_hClusterData[c].extents_max, flashlightFrusta[i] ) ) + continue; + + //m_hClusterData[ c ].pGrassMesh->Draw(); + //m_iDrawnQuads += m_hClusterData[ c ].iNumQuads; + m_iDrawnQuads += m_hClusterData[ c ].Draw(); + m_iDrawnCluster++; + } + } + + pRenderContext->SetFlashlightMode( false ); + } + + pRenderContext->Flush(); +} + +void CGrassClusterManager::UpdateMorphInfo() +{ + if ( gcluster_enable_morph.GetInt() == 0 ) + { + for ( int i = 0; i < m_hClusterData.Count(); i++ ) + { + for ( int m = 0; m < m_hClusterData[i].pPressureInfo->iNumGrassObjects; m++ ) + { + if ( m_hClusterData[i].pPressureInfo->flAmt[m] != 0 ) + { + m_hClusterData[i].pPressureInfo->flAmt[m] = 0.0f; + m_hClusterData[i].pPressureInfo->bDirty[m] = true; + } + } + } + + for ( int i = 0; i < m_hClusterData.Count(); i++ ) + InjectMorph( i ); + gcluster_enable_morph.SetValue( -1 ); + } + + if ( gcluster_enable_morph.GetInt() <= 0 ) + return; + + const bool bDebugging = gcluster_debug.GetBool(); + + CFastTimer timer; + if ( bDebugging ) + timer.Start(); + + for ( int i = 0; i < m_hClusterData.Count(); i++ ) + { + for ( int m = 0; m < m_hClusterData[i].pPressureInfo->iNumGrassObjects; m++ ) + { + if ( m_hClusterData[i].pPressureInfo->flAmt[m] != 0 && + m_hClusterData[i].pPressureInfo->flLastMoveTime[m] < gpGlobals->curtime && + m_hClusterData[i].pPressureInfo->flLastUpdateTime[m] < gpGlobals->curtime ) + { + m_hClusterData[i].pPressureInfo->flAmt[m] = Approach( 0, + m_hClusterData[i].pPressureInfo->flAmt[m], + gpGlobals->frametime * gcluster_grass_morph_speed.GetFloat() ); + m_hClusterData[i].pPressureInfo->bDirty[m] = true; + m_hClusterData[i].pPressureInfo->flLastUpdateTime[m] = gpGlobals->curtime + gcluster_grass_morph_framelag.GetFloat(); + } + } + } + + float flGrassColScale = ( gcluster_grass_width_med_min.GetFloat() + + ( gcluster_grass_width_med_max.GetFloat() - gcluster_grass_width_med_min.GetFloat() ) * 0.5f ) / 50.0f; + flGrassColScale = clamp( flGrassColScale, 0.3f, 1.6f ); + + C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer(); + for ( C_BaseEntity *pEnt = ClientEntityList().FirstBaseEntity(); pEnt; pEnt = ClientEntityList().NextBaseEntity( pEnt ) ) + { + if ( !pEnt || !pEnt->IsVisible() && pEnt != pLocal ) + continue; + + const bool bPlayer = pEnt->IsPlayer(); + + if ( bPlayer ) + { + C_BasePlayer *player = (C_BasePlayer*)pEnt; + if ( player->m_Local.m_bDucked ) + continue; + } + else if ( dynamic_cast< CBaseViewModel* >( pEnt ) != NULL ) + continue; + + CCollisionProperty *pCProp = pEnt->CollisionProp(); + + if ( !pCProp ) + continue; + + //const bool bIsPhys = pEnt->GetMoveType() == MOVETYPE_VPHYSICS; + Vector vPos = bPlayer ? pEnt->GetLocalOrigin() : pEnt->WorldSpaceCenter(); + + Vector colMin = pCProp->OBBMins(); + Vector colMax = pCProp->OBBMaxs(); + colMax-=colMin; + if ( !bPlayer ) + vPos.z -= colMax.z * 0.5f; + colMax.z = 0; + + Vector vVel = pEnt->GetLocalVelocity(); + + if ( vVel.IsZero() ) + pEnt->EstimateAbsVelocity( vVel ); + + if ( vVel.IsZero() && pEnt->GetMoveType() == MOVETYPE_VPHYSICS && pEnt->VPhysicsGetObject() ) + pEnt->VPhysicsGetObject()->GetVelocity( &vVel, NULL ); + + IPhysicsObject *dummyList[2]; + if ( pEnt->VPhysicsGetObjectList( dummyList, 2 ) > 1 || + pEnt->GetClientVehicle() != NULL ) + colMax *= 0.25f; + + const float flBoundsSqr = max( 1500, colMax.LengthSqr() * flGrassColScale ); + const float flLenSqr = vVel.LengthSqr(); + + if ( flLenSqr < 10 ) + continue; + + vVel *= 1.0f / FastSqrt( flLenSqr ); + + for ( int i = 0; i < m_hClusterData.Count(); i++ ) + { + const _grassClusterData &data = m_hClusterData[i]; + if ( !IsPointInBox( vPos, data.extents_min - Vector(CONSTBOXEXTENT_COL,CONSTBOXEXTENT_COL,CONSTBOXEXTENT_COL), + data.extents_max + Vector(CONSTBOXEXTENT_COL,CONSTBOXEXTENT_COL,CONSTBOXEXTENT_COL) ) ) + continue; + + _grassPressureData *morphData = data.pPressureInfo; + Vector delta; + + for ( int o = 0; o < morphData->iNumGrassObjects; o++ ) + { + delta = morphData->vecPos[o] - vPos; + if ( delta.LengthSqr() > flBoundsSqr ) + continue; + + if ( morphData->flAmt[o] < 0.5f ) + Q_memcpy( morphData->vecDir[o].Base(), vVel.Base(), sizeof(Vector) ); + + morphData->flAmt[o] = 1.0f; //morphData->flHeight[o]; + morphData->bDirty[o] = true; + morphData->flLastMoveTime[o] = gpGlobals->curtime + gcluster_grass_morph_delay.GetFloat(); + + //DebugDrawLine( morphData->vecPos[o], morphData->vecPos[o] + Vector( 0, 0, 100 ), 255, 0, 0, false, 0.1f ); + } + } + } + + for ( int i = 0; i < m_hClusterData.Count(); i++ ) + InjectMorph( i ); + + if ( bDebugging ) + { + timer.End(); + m_flMorphTime += timer.GetDuration().GetMillisecondsF(); + } +} +void CGrassClusterManager::InjectMorph( int i ) +{ + Assert( i >= 0 && i < m_hClusterData.Count() ); + + _grassClusterData *data = &m_hClusterData[ i ]; + _grassPressureData *morphInfo = data->pPressureInfo; + + Assert( data->pGrassMesh ); + Assert( morphInfo && morphInfo->iNumGrassObjects > 0 ); + + CMeshBuilder pMeshBuilder; + + int grassObject, iPlane; + int NumVerticesPerGrassObject = 4 * 3; + + for ( grassObject = 0; grassObject < morphInfo->iNumGrassObjects; grassObject++ ) + { + if ( !morphInfo->bDirty[grassObject] ) + continue; + + int continuousObjects = 1; + for ( int x = grassObject+1; x < morphInfo->iNumGrassObjects; x++ ) + { + if ( !morphInfo->bDirty[x] ) + break; + continuousObjects++; + } + + pMeshBuilder.BeginModify( data->pGrassMesh, + grassObject * NumVerticesPerGrassObject, + NumVerticesPerGrassObject * continuousObjects ); + + for ( int x = 0; x < continuousObjects; x++ ) + { + const int curIndex = grassObject + x; + const float &amt = morphInfo->flAmt[ curIndex ]; + const float amt_scaled = amt * morphInfo->flHeight[ curIndex ]; + const Vector &dir = morphInfo->vecDir[ curIndex ]; + + morphInfo->bDirty[ curIndex ] = false; + + for ( iPlane = 0; iPlane < 3; iPlane++ ) + { + pMeshBuilder.TexCoord3f( 1, 1, amt, amt_scaled ); + pMeshBuilder.TexCoord3f( 2, dir.x, dir.y, dir.z ); + pMeshBuilder.AdvanceVertex(); + pMeshBuilder.AdvanceVertex(); + pMeshBuilder.AdvanceVertex(); + pMeshBuilder.TexCoord3f( 1, 1, amt, amt_scaled ); + pMeshBuilder.TexCoord3f( 2, dir.x, dir.y, dir.z ); + pMeshBuilder.AdvanceVertex(); + } + } + + pMeshBuilder.EndModify(); + + grassObject += continuousObjects - 1; + } +} + +void CGrassClusterManager::AddClusterHint( _grassClusterInfo hint ) +{ + m_hClusterInfo.AddToTail( hint ); +} + +void CGrassClusterManager::ClearClusterData() +{ + for ( int i = 0; i < m_hClusterData.Count(); i++ ) + m_hClusterData[i].Destroy(); + m_hClusterData.Purge(); +} + +void CGrassClusterManager::GenerateClusterData() +{ + m_iCurObjectsPerHint = gcluster_objectsPerHint.GetInt(); + + CMatRenderContextPtr pRenderContext( materials ); + IMesh *pMeshDummy = pRenderContext->GetDynamicMesh( true, NULL, NULL, GetActiveMaterial() ); + int nMaxVerts, nMaxIndices; + pRenderContext->GetMaxToRender( pMeshDummy, false, &nMaxVerts, &nMaxIndices ); + pMeshDummy->Draw(); + + m_iDrawnEngineMax = nMaxIndices / 6; + if ( m_iDrawnEngineMax > nMaxVerts / 4 ) + m_iDrawnEngineMax = nMaxVerts / 4; + int maxQuads = m_iDrawnEngineMax; + + if ( gcluster_clusterMaxQuads.GetInt() > 0 ) + maxQuads = min( maxQuads, gcluster_clusterMaxQuads.GetInt() ); + + int maxClusterHints = maxQuads / ( 3 * m_iCurObjectsPerHint ); + + m_iDrawnPerDrawcall = maxClusterHints * 3 * m_iCurObjectsPerHint; + + if ( !maxClusterHints ) + return; + + CUtlVector< _grassClusterInfo >hClusterInfoLocal; + hClusterInfoLocal.AddVectorToTail( m_hClusterInfo ); + + CUtlVector< _grassClusterInfo >hClusterInfoSorted; + + //float step = 0; + //for ( int i = 0; i < m_hClusterInfo.Count(); i += skipAmt ) + //const float skipDistSqr = 2500.0f * 2500.0f; + const float skipDistSqr = 1500.0f * 1500.0f; + + while ( hClusterInfoLocal.Count() ) + { + const Vector ref = hClusterInfoLocal[0].orig; + hClusterInfoLocal[0].flSortDist = 0; + + for ( int i = 1; i < hClusterInfoLocal.Count(); i++ ) + hClusterInfoLocal[i].flSortDist = ( hClusterInfoLocal[i].orig - ref ).LengthSqr(); + + hClusterInfoLocal.Sort( GrassInfoSort ); + + hClusterInfoSorted.Purge(); + int count = min( maxClusterHints, hClusterInfoLocal.Count() ); + for ( int i = 0; i < count; i++ ) + { + hClusterInfoSorted.AddToTail( hClusterInfoLocal[i] ); + } + hClusterInfoLocal.RemoveMultiple( 0, count ); + + bool bFinished = hClusterInfoLocal.Count() == 0; + + if ( hClusterInfoSorted.Count() < 5 ) + continue; + + for ( int i = 0; i < hClusterInfoSorted.Count(); i++ ) + { + //const float dist = ( hClusterInfoSorted[i].orig - ref ).LengthSqr(); + //if ( dist > skipDistSqr ) + if ( hClusterInfoSorted[i].flSortDist > skipDistSqr ) + { + //DebugDrawLine( hClusterInfoSorted[i].orig, hClusterInfoSorted[i].orig + Vector( 0, 0, 300 ), 255, 0, 0, false, 1 ); + hClusterInfoLocal.AddToHead( hClusterInfoSorted[i] ); + hClusterInfoSorted.Remove( i ); + i--; + } + } + + count = hClusterInfoSorted.Count(); + + if ( !hClusterInfoSorted.Count() ) + { + if ( bFinished ) + break; + //Assert(0); + continue; + } + + Vector avgPos( vec3_origin ); + for ( int i = 0; i < count; i++ ) + avgPos += hClusterInfoSorted[i].orig; + avgPos /= (float)count; + + //int r = ( step * 10 - floor( step * 10 ) ) * 255; + //int g = ( step * 13 - floor( step * 13 ) ) * 255; + //int b = ( step * 19 - floor( step * 19 ) ) * 255; + + //for ( int i = 0; i < hClusterInfoSorted.Count(); i++ ) + // DebugDrawLine( hClusterInfoSorted[i].orig, hClusterInfoSorted[i].orig + Vector( 0, 0, 100 ), r, g, b, false, 1 ); + + //DebugDrawLine( avgPos, avgPos + Vector( 0, 0, 300 ), 255, 0, 0, false, 1 ); + + int numGrassObjects = hClusterInfoSorted.Count() * m_iCurObjectsPerHint; + + _grassPressureData *morphData = new _grassPressureData(); + morphData->Init( numGrassObjects ); + + _grassClusterData data; + + BuildClusterMesh( data, hClusterInfoSorted, m_iCurObjectsPerHint, morphData ); + + if ( gcluster_LOD_enable.GetInt() ) + { + _grassClusterData *dataLOD = new _grassClusterData(); + BuildClusterMesh( *dataLOD, hClusterInfoSorted, min( m_iCurObjectsPerHint, gcluster_LOD_objects_per_hint.GetInt() ) ); + dataLOD->pos = avgPos; + data.iNextLodThreshold = gcluster_LOD_transitionDist.GetInt() * gcluster_LOD_transitionDist.GetInt(); + data.pLOD = dataLOD; + } + + Assert( data.pGrassMesh ); + data.pos = avgPos; + data.pPressureInfo = morphData; + + m_hClusterData.AddToTail( data ); + + //step+=0.1f; + } + + hClusterInfoLocal.Purge(); + hClusterInfoSorted.Purge(); + + r_DrawDetailProps.SetValue( "0" ); +} + +void CGrassClusterManager::BuildClusterMesh( _grassClusterData &data, const CUtlVector< _grassClusterInfo > &hints, + int iObjectMultiplier, _grassPressureData *pMorphInfo ) +{ + const int hintCount = hints.Count(); + const int numObjectsPerHint = iObjectMultiplier; //m_iCurObjectsPerHint; + const int numQuads = numObjectsPerHint * 3 * hintCount; + + Assert( hintCount && numQuads && numObjectsPerHint ); + + float flAverageMinDist = 0; + for ( int i = 0; i < hintCount; i++ ) + { + const Vector &ref = hints[i].orig; + + CUtlVector< _grassClusterInfo >hClusterInfoSortedSub; + hClusterInfoSortedSub.AddVectorToTail( hints ); + + for ( int s = 0; s < hintCount; s++ ) + { + if ( s == i ) + continue; + + hClusterInfoSortedSub[s].flSortDist = ( hClusterInfoSortedSub[s].orig - ref ).LengthSqr(); + } + + hClusterInfoSortedSub.Sort( GrassInfoSort ); + + for ( int s = 0; s < hintCount; s++ ) + { + if ( s == i ) + continue; + flAverageMinDist += hClusterInfoSortedSub[s].flSortDist; + break; + } + + hClusterInfoSortedSub.Purge(); + } + flAverageMinDist = FastSqrt( flAverageMinDist/(float)hintCount ); + + data.iNumQuads = numQuads; + data.extents_min = hints[0].orig; + data.extents_max = hints[0].orig; + for ( int i = 1; i < hintCount; i++ ) + for ( int v = 0; v < 3; v++ ) + data.extents_min[v] = min( data.extents_min[v], hints[i].orig[v] ); + for ( int i = 1; i < hintCount; i++ ) + for ( int v = 0; v < 3; v++ ) + data.extents_max[v] = max( data.extents_max[v], hints[i].orig[v] ); + data.extents_max.z += 60; + data.extents_min -= Vector( 40, 40, 40 ); + data.extents_max += Vector( 40, 40, 40 ); + + CMatRenderContextPtr pRenderContext( materials ); + CMeshBuilder pMeshBuilder; + + VertexFormat_t format = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_COLOR | + VERTEX_FORMAT_VERTEX_SHADER | VERTEX_TEXCOORD_SIZE( 0, 2 ) | VERTEX_TEXCOORD_SIZE( 1, 3 ) | VERTEX_TEXCOORD_SIZE( 2, 3 ); + IMesh *pMesh = pRenderContext->CreateStaticMesh( format, TEXTURE_GROUP_OTHER, GetActiveMaterial() ); + Assert( pMesh ); + + pMeshBuilder.Begin( pMesh, MATERIAL_QUADS, numQuads ); + + //data.CreateLightingPatch( hints ); + data.CreateLightingPatch( m_hClusterInfo ); + + for ( int i = 0; i < hintCount; i++ ) + for ( int m = 0; m < numObjectsPerHint; m++ ) + BuildSingleGrassObject( pMeshBuilder, data, hints[i], flAverageMinDist, i * numObjectsPerHint + m, pMorphInfo ); + + data.DestroyLightingPatch(); + + pMeshBuilder.End(); + + data.pGrassMesh = pMesh; +} + +void CGrassClusterManager::BuildSingleGrassObject( CMeshBuilder &builder, _grassClusterData &clusterData, const _grassClusterInfo &hint, + const float avgDist, + const int grassObjectIndex, _grassPressureData *pMorphInfo ) +{ + //bool bStarShape = !!RandomInt( 0, 1 ); + const _grassClusterInfo &uvData = m_hClusterInfo[ RandomInt( 0, m_hClusterInfo.Count() - 1 ) ]; + const Vector2D un_min = uvData.uv_min; + const Vector2D un_max = uvData.uv_max; + //engine->GetLightForPoint + trace_t tr; + //UTIL_TraceLine( hint.orig + Vector( 0, 0, 10 ), hint.orig - Vector( 0, 0, 10 ), MASK_SOLID, NULL, COLLISION_GROUP_DEBRIS, &tr ); + CTraceFilterWorldOnly filter; + UTIL_TraceLine( hint.orig + Vector( 0, 0, 10 ), hint.orig - Vector( 0, 0, 10 ), MASK_ALL, &filter, &tr ); + Assert( tr.DidHit() ); + + Vector normal = tr.DidHitWorld() ? tr.plane.normal : Vector( 0, 0, 1 ); + Vector orig = hint.orig; //tr.endpos; + if ( ( !tr.DidHit() && !tr.allsolid) || normal.IsZero() ) + { + normal.Init( 0, 0, 1 ); + orig = hint.orig; + + //Assert( 0 ); + } + Vector right, up; + + const float ranExp = gcluster_grass_terrain_offset_exp.GetFloat(); + + const float min_Dist = gcluster_grass_terrain_offset_min.GetFloat(); + const float max_Dist = max( min_Dist, avgDist ) * gcluster_grass_terrain_offset_multi.GetFloat(); + const float maxAng = cos( DEG2RAD( 60.0f ) ); + + int attempts = 0; + const int maxAttempts = 24; + + for ( attempts = 0; attempts < maxAttempts; attempts++ ) + { + QAngle orient; + VectorAngles( normal, orient ); + orient.z += RandomFloat( 0, 360.0f ); + AngleVectors( orient, NULL, &right, &up ); + //VectorVectors( normal, right, up ); + right *= RandomFloatExp( min_Dist, max_Dist, ranExp ) * (RandomInt(0,1)?-1:1); + up *= RandomFloatExp( min_Dist, max_Dist, ranExp ) * (RandomInt(0,1)?-1:1); + + Vector tr_start = hint.orig + normal, tr_end = hint.orig + normal * 50; + UTIL_TraceLine( tr_start, tr_end, MASK_ALL, &filter, &tr ); + tr_end = tr.endpos + right + up; + UTIL_TraceLine( tr.endpos, tr_end, MASK_ALL, &filter, &tr ); + tr_end = tr.endpos - normal * 100.0f; + UTIL_TraceLine( tr.endpos, tr_end, MASK_ALL, &filter, &tr ); + + if ( !tr.DidHit() ) + continue; + + const float dot = DotProduct( normal, tr.plane.normal ); + if ( dot < maxAng ) + continue; + + orig = tr.endpos; + normal = tr.plane.normal; + break; + } + + Assert( attempts < maxAttempts ); + + const bool bSmall = !RandomInt( 0, gcluster_grass_type_small_oddness.GetInt() ); + const bool bHuge = !RandomInt( 0, gcluster_grass_type_huge_oddness.GetInt() ); + const float flSize_up = bHuge ? RandomFloat( gcluster_grass_height_huge_min.GetFloat(), gcluster_grass_height_huge_max.GetFloat() ) : + bSmall ? RandomFloat( gcluster_grass_height_small_min.GetFloat(), gcluster_grass_height_small_max.GetFloat() ) : + RandomFloat( gcluster_grass_height_med_min.GetFloat(), gcluster_grass_height_med_max.GetFloat() ); + + const float flSize_side = bHuge ? RandomFloat( gcluster_grass_width_huge_min.GetFloat(), gcluster_grass_width_huge_max.GetFloat() ) : + bSmall ? RandomFloat( gcluster_grass_width_small_min.GetFloat(), gcluster_grass_width_small_max.GetFloat() ) : + RandomFloat( gcluster_grass_width_med_min.GetFloat(), gcluster_grass_width_med_max.GetFloat() ); + + if ( pMorphInfo != NULL ) + { + pMorphInfo->vecPos[grassObjectIndex] = orig; + pMorphInfo->flHeight[grassObjectIndex] = flSize_up; + } + + Vector orig_top = orig + normal * flSize_up; + + QAngle orientation; + VectorAngles( normal, orientation ); + + orientation.z += RandomFloat( 0, 60 ); + + Vector planePos[4]; + Vector2D uvs[4]; + + //Vector4D AverageColor( hint.color ); + //Vector col, diff; + + //if ( tr.DidHitWorld() && tr.hitbox == 0 && engine->TraceLineMaterialAndLighting( orig, normal * 1.1f, col, diff ) ) + //if ( tr.DidHitWorld() && tr.hitbox == 0 ) + { + //col = FetchLightSamples( orig ) * 1.4f; //engine->GetLightForPoint( orig, false ); + //for ( int i = 0; i < 3; i++ ) + // col[i] = min( col[i], hint.color[i] ); + //col = ( col + hint.color.AsVector3D() * 1.5f ) * 0.5f; + //col = clusterData.GetLightingForPoint( orig ); + //AverageColor.Init( col.x, col.y, col.z, 1 ); + } + + float meadowAccum = RandomFloat( 2, 3 ); + meadowAccum += sin( orig[0] * 0.005f ) * 0.5f + 0.5f; + meadowAccum += cos( DotProduct(Vector(0.707f,0.707f,0), orig) * 0.007f ) * 0.5f + 0.5f; + meadowAccum += sin( DotProduct(Vector(-0.638224f, -0.304417f, -0.707107), orig) * 0.0055f ) * 0.5f + 0.5f; + meadowAccum += cos( orig[0] * 0.006f ) * 0.5f + 0.5f; + meadowAccum = Bias( meadowAccum / 7.0f, 0.4f ); + + meadowAccum = Lerp( gcluster_grass_meadow_scale.GetFloat(), 1.0f, meadowAccum ); + + //for ( int i = 0; i < 3; i++ ) + //{ + // AverageColor[i] *= meadowAccum; + // AverageColor[i] = clamp( AverageColor[i], 0, 1 ); + //} + + Vector4D vPosInfo( 1, 0, 0, 1 ); + + //Vector col = clusterData.GetLightingForPoint( orig ); + //for ( int v = 0; v < 3; v++ ) + // col[v] = clamp( col[v], 0, 1 ); + + for ( int i = 0; i < 3; i++ ) + { + AngleVectors( orientation, NULL, &right, &up ); + + planePos[0] = orig_top + up * flSize_side; + planePos[1] = orig + up * flSize_side; + planePos[2] = orig - up * flSize_side; + planePos[3] = orig_top - up * flSize_side; + + for ( int p = 0; p < 4; p++ ) + { + float len = flSize_up * 0.2f * (i==1 ? -1 : 1); + //float len = flSize_side * 0.15f * (i==1 ? -1 : 1); + if ( p < 1 || p > 2 ) + len *= 3.0f; + planePos[p] += right * len; + } + + Vector colors[4]; + for ( int c = 0; c < 4; c++ ) + { + colors[c] = clusterData.GetLightingForPoint( planePos[c] ); + for ( int v = 0; v < 3; v++ ) + colors[c][v] = clamp( colors[c][v], 0, 1 ); + colors[c] *= meadowAccum; + } + + + //uvs[0].Init( un_min.x, un_max.y ); + //uvs[1].Init( un_min.x, un_min.y ); + //uvs[2].Init( un_max.x, un_min.y ); + //uvs[3].Init( un_max.x, un_max.y ); + + if ( !RandomInt( 0, 20 ) ) + { + uvs[0].Init( 0, 0.0f ); + uvs[1].Init( 0, 0.5f ); + uvs[2].Init( 1, 0.5f ); + uvs[3].Init( 1, 0.0f ); + } + else + { + uvs[0].Init( 0, 0.51f + (0.5f/128.0f) ); + uvs[1].Init( 0, 1 ); + uvs[2].Init( 1, 1 ); + uvs[3].Init( 1, 0.51f + (0.5f/128.0f) ); + } + + //uvs[0].Init( 0, 1 ); + //uvs[1].Init( 0, 0 ); + //uvs[2].Init( 1, 0 ); + //uvs[3].Init( 1, 1 ); + + for ( int t = 0; t < 4; t++ ) + { + builder.Position3fv( planePos[t].Base() ); + //builder.Color4fv( AverageColor.Base() ); + builder.Color4f( colors[t][0], colors[t][1], colors[t][2], 1 ); + //builder.Color4f( col.x, col.y, col.z, 1 ); + builder.TexCoord2f( 0, uvs[t][0], uvs[t][1] ); + builder.TexCoord3f( 1, vPosInfo[t], 0, 0 ); + builder.TexCoord3f( 2, 0, 0, 0 ); + builder.Normal3fv( normal.Base() ); + builder.AdvanceVertex(); + } + + orientation.z += 60.0f; + } +} \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/Grass/CGrassCluster.h b/mp/src/game/client/ShaderEditor/Grass/CGrassCluster.h new file mode 100644 index 00000000..6e8f881a --- /dev/null +++ b/mp/src/game/client/ShaderEditor/Grass/CGrassCluster.h @@ -0,0 +1,132 @@ +#ifndef GRASS_CLUSTER_H +#define GRASS_CLUSTER_H + +#include "cbase.h" + +class CFastTimer; + +struct _grassPressureData +{ + _grassPressureData(); + ~_grassPressureData(); + + void Init( int num ); + + int iNumGrassObjects; + + Vector *vecPos; + Vector *vecDir; + float *flAmt; + float *flHeight; + float *flLastMoveTime; + float *flLastUpdateTime; + bool *bDirty; +}; + +struct _grassClusterInfo +{ +public: + _grassClusterInfo(); + + Vector orig; + Vector4D color; + Vector2D uv_min, uv_max; + float flSortDist; +}; + +struct _grassClusterData +{ +public: + _grassClusterData(); + ~_grassClusterData(); + // flat copy on purpose!!! + + void Destroy(); + int Draw(); + + IMesh *pGrassMesh; + _grassPressureData *pPressureInfo; + Vector pos; + + int iNumQuads; + Vector extents_min, extents_max; + + int iNextLodThreshold; + _grassClusterData *pLOD; + + + void CreateLightingPatch( const CUtlVector< _grassClusterInfo > &hints ); + const Vector GetLightingForPoint( const Vector &pos ); + void DestroyLightingPatch(); + int iLPatchSize_x, iLPatchSize_y; + float flLPatchStep_x, flLPatchStep_y; + Vector *lighting; +}; + +struct clusterMaterial +{ + clusterMaterial(); + void Init( const char *pszMat ); + void Shutdown(); + bool IsValid(); + + IMaterialVar *GetVarDir(); + IMaterialVar *GetVarAng(); + IMaterial *GetMaterial(); + + CMaterialReference mat; + unsigned int ivar_dir; + unsigned int ivar_ang; +}; + +class CGrassClusterManager : public CAutoGameSystemPerFrame +{ +public: + + CGrassClusterManager(); + ~CGrassClusterManager(); + + static CGrassClusterManager *GetInstance(); + + bool Init(); + void Shutdown(); + void LevelInitPostEntity(); + void LevelShutdownPostEntity(); + + void Update( float frametime ); + void PreRender(); + void PostRender(); + void RenderClusters( bool bShadowDepth ); + + void AddClusterHint( _grassClusterInfo hint ); + void ClearClusterData(); + +private: + + void GenerateClusterData(); + void BuildClusterMesh( _grassClusterData &data, const CUtlVector< _grassClusterInfo > &hints, int iObjectMultiplier, _grassPressureData *pMorphInfo = NULL ); + void BuildSingleGrassObject( CMeshBuilder &builder, _grassClusterData &clusterData, const _grassClusterInfo &hint, + const float avgDist, const int grassObjectIndex, _grassPressureData *pMorphInfo = NULL ); + + void UpdateMorphInfo(); + void InjectMorph( int i ); + + CUtlVector< _grassClusterInfo >m_hClusterInfo; + CUtlVector< _grassClusterData >m_hClusterData; + + IMaterial *GetActiveMaterial(); + //CMaterialReference *m_refMaterial; + clusterMaterial *m_refMaterials; + int m_iCurrentMaterial; + + int m_iCurObjectsPerHint; + int m_iDrawnQuads; + int m_iDrawnCluster; + int m_iDrawnPerDrawcall; + int m_iDrawnEngineMax; + + double m_flMorphTime; +}; + + +#endif \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/Grass/README_GRASS.txt b/mp/src/game/client/ShaderEditor/Grass/README_GRASS.txt new file mode 100644 index 00000000..582b92bc --- /dev/null +++ b/mp/src/game/client/ShaderEditor/Grass/README_GRASS.txt @@ -0,0 +1,144 @@ +//**********************************************// +// // +// Installation instructions for grass clusters // +// // +//**********************************************// + +*** ONLY TESTED WITH SOURCE 2007, SO DON'T EXPECT SWARM COMPATIBILITY *** + +The grass clusters are meant to replace the existing detail props. They'll use +the lighting and positioning information from them to generate their geometry. +Do not use a huge texture atlas for them from which you only read some parts but +use actual unique materials for each type of grass (PERFO), note that you have +to add any map dependent functionality yourself for that matter! +Also, don't use alpha blending on them, it WON'T WORK. And that won't change because +grass objects should stay batched. If you can't stand alpha testing use alpha to coverage, +which requires more performance. + + + +Make sure to have the shader editor and its example materials and shaders installed too +or export them and update all references accordingly. VERIFY THAT YOU HAVE THE SHADER 'detail_prop_shader' +COMPILED AND IN YOUR PRECACHE LIST. + + + +In case you're experiencing errors that you don't understand, go there: http://www.learncpp.com/ +and READ it. 'Reading' in a sense of actually understanding what's written there. That includes the possibility +of iteratively examining a passage for a variable number of times until comprehension has been acquired. + + + +If you're using the source sdk base 2007 you can apply the subversion patch (dprop_and_shadow_patch.patch) +and skip to step 5 now, other branches MIGHT NOT BE COMPATIBLE. + + + +Step 1. + +client/clientshadowmgr.cpp + + +Find this line: + +ShadowType_t GetActualShadowCastType( ClientShadowHandle_t handle ) const; + + +Replace it with these: + +public: + ShadowHandle_t GetShadowHandle( ClientShadowHandle_t clienthandle ){ return m_Shadows[ clienthandle ].m_ShadowHandle; }; + int GetNumShadowDepthtextures(){ return m_DepthTextureCache.Count(); }; + CTextureReference GetShadowDepthTex( int num ){ return m_DepthTextureCache[num]; }; + + ShadowType_t GetActualShadowCastType( ClientShadowHandle_t handle ) const; +private: + + +Step 2. + +client/detailobjectsystem.cpp + + +Find this line: + +#include "c_world.h" + + +Add this line below: + +#include "ShaderEditor/Grass/CGrassCluster.h" + + +Find this line: + +pSpritex4->m_pSpriteDefs[nSubField] = pSDef; + + +Add these lines below: + + _grassClusterInfo clusterHint; + clusterHint.orig = pos; + clusterHint.color.Init( color[0], color[1], color[2], 1 ); + clusterHint.uv_min = pSDef->m_TexLR; + clusterHint.uv_max = pSDef->m_TexUL; + CGrassClusterManager::GetInstance()->AddClusterHint( clusterHint ); + + +Step 3. + +client/iclientshadowmgr.h + + +Find this line: + +virtual void ComputeShadowDepthTextures( const CViewSetup &pView ) = 0; + + +Add these lines below: + + virtual ShadowHandle_t GetShadowHandle( ClientShadowHandle_t clienthandle ) = 0; + virtual ShadowType_t GetActualShadowCastType( ClientShadowHandle_t handle ) const = 0; + virtual int GetNumShadowDepthtextures() = 0; + virtual CTextureReference GetShadowDepthTex( int num ) = 0; + + +Step 4. + +client/viewrender.cpp + + +Find this line: + +#include "con_nprint.h" + + +Add this line below: + +#include "ShaderEditor/Grass/CGrassCluster.h" + + +Find this line in the function void CRendering3dView::DrawOpaqueRenderables( bool bShadowDepth ): + +g_pParticleSystemMgr->DrawRenderCache( bShadowDepth ); + + +Add this line below for 2007 / swarm: + +CGrassClusterManager::GetInstance()->RenderClusters( bShadowDepth ); + +OR this line for 2013: + +CGrassClusterManager::GetInstance()->RenderClusters( DepthMode == DEPTH_MODE_SHADOW ); + + +Step 5. + +Copy the folder 'Grass' and both files to /client/ShaderEditor/. If you want to put them elsewhere, +you'll have to change the include paths in the respective files. + + +Step 6. + +Add both files in /Grass/ to your client project and compile it. Grass clusters will now be +created wherever you placed standard simple detail sprites. \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/Grass/dprop_and_shadow_patch.patch b/mp/src/game/client/ShaderEditor/Grass/dprop_and_shadow_patch.patch new file mode 100644 index 00000000..b2f68bd8 --- /dev/null +++ b/mp/src/game/client/ShaderEditor/Grass/dprop_and_shadow_patch.patch @@ -0,0 +1,80 @@ +Index: client/clientshadowmgr.cpp +=================================================================== +--- client/clientshadowmgr.cpp (revision 1) ++++ client/clientshadowmgr.cpp (working copy) +@@ -853,7 +853,13 @@ + void RemoveShadowFromDirtyList( ClientShadowHandle_t handle ); + + // NOTE: this will ONLY return SHADOWS_NONE, SHADOWS_SIMPLE, or SHADOW_RENDER_TO_TEXTURE. ++public: ++ ShadowHandle_t GetShadowHandle( ClientShadowHandle_t clienthandle ){ return m_Shadows[ clienthandle ].m_ShadowHandle; }; ++ int GetNumShadowDepthtextures(){ return m_DepthTextureCache.Count(); }; ++ CTextureReference GetShadowDepthTex( int num ){ return m_DepthTextureCache[num]; }; ++ + ShadowType_t GetActualShadowCastType( ClientShadowHandle_t handle ) const; ++private: + ShadowType_t GetActualShadowCastType( IClientRenderable *pRenderable ) const; + + // Builds a simple blobby shadow +Index: client/detailobjectsystem.cpp +=================================================================== +--- client/detailobjectsystem.cpp (revision 1) ++++ client/detailobjectsystem.cpp (working copy) +@@ -22,6 +22,7 @@ + #include "env_detail_controller.h" + #include "tier0/icommandline.h" + #include "c_world.h" ++#include "ShaderEditor/Grass/CGrassCluster.h" + + //Tony; add the SDK into this as well by default. + #if defined(DOD_DLL) || defined(CSTRIKE_DLL) || defined( SDK_DLL ) +@@ -1940,6 +1941,13 @@ + pSpritex4->m_RGBColor[nSubField][3] = 255; + + pSpritex4->m_pSpriteDefs[nSubField] = pSDef; ++ ++ _grassClusterInfo clusterHint; ++ clusterHint.orig = pos; ++ clusterHint.color.Init( color[0], color[1], color[2], 1 ); ++ clusterHint.uv_min = pSDef->m_TexLR; ++ clusterHint.uv_max = pSDef->m_TexUL; ++ CGrassClusterManager::GetInstance()->AddClusterHint( clusterHint ); + } + + +Index: client/iclientshadowmgr.h +=================================================================== +--- client/iclientshadowmgr.h (revision 1) ++++ client/iclientshadowmgr.h (working copy) +@@ -101,6 +100,10 @@ + + virtual void ComputeShadowDepthTextures( const CViewSetup &pView ) = 0; + ++ virtual ShadowHandle_t GetShadowHandle( ClientShadowHandle_t clienthandle ) = 0; ++ virtual ShadowType_t GetActualShadowCastType( ClientShadowHandle_t handle ) const = 0; ++ virtual int GetNumShadowDepthtextures() = 0; ++ virtual CTextureReference GetShadowDepthTex( int num ) = 0; + }; + + +Index: client/viewrender.cpp +=================================================================== +--- client/viewrender.cpp (revision 1) ++++ client/viewrender.cpp (working copy) +@@ -48,6 +48,7 @@ + #include "keyvalues.h" + #include "renderparm.h" + #include "con_nprint.h" ++#include "ShaderEditor/Grass/CGrassCluster.h" + + #ifdef PORTAL + //#include "C_Portal_Player.h" +@@ -3856,6 +3870,8 @@ + // + RopeManager()->DrawRenderCache( bShadowDepth ); + g_pParticleSystemMgr->DrawRenderCache( bShadowDepth ); ++ ++ CGrassClusterManager::GetInstance()->RenderClusters( bShadowDepth ); + } + + \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/ISEdit_ModelRender.h b/mp/src/game/client/ShaderEditor/ISEdit_ModelRender.h new file mode 100644 index 00000000..a4eb5710 --- /dev/null +++ b/mp/src/game/client/ShaderEditor/ISEdit_ModelRender.h @@ -0,0 +1,41 @@ +#ifndef IV_SHADEREDITOR_MRENDER +#define IV_SHADEREDITOR_MRENDER + +#ifdef _WIN32 +#pragma once +#endif // _WIN32 + +#ifdef SHADER_EDITOR_DLL +#include "../public/tier1/interface.h" +#else +#include "interface.h" +#include "ShaderEditor/ShaderEditorSystem.h" +#endif // NOT SHADER_EDITOR_DLL + + +class ISEditModelRender +{ +public: + virtual bool LoadModel( const char *localPath ) = 0; + virtual void DestroyModel() = 0; + virtual void GetModelCenter( float *pFl3_ViewOffset ) = 0; + + virtual int QuerySequences( char ***list ) = 0; + virtual void SetSequence( const char *name ) = 0; + + virtual void ExecRender() = 0; + virtual void DoPostProc( int x, int y, int w, int h ) = 0; + virtual int MaterialPicker( char ***szMat ) = 0; + + virtual void DestroyCharPtrList( char ***szList ) = 0; +}; + + +#ifdef SHADER_EDITOR_DLL +extern ISEditModelRender *sEditMRender; +#else +class SEditModelRender; +extern SEditModelRender *sEditMRender; +#endif + +#endif \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/IVShaderEditor.h b/mp/src/game/client/ShaderEditor/IVShaderEditor.h new file mode 100644 index 00000000..9e74435b --- /dev/null +++ b/mp/src/game/client/ShaderEditor/IVShaderEditor.h @@ -0,0 +1,128 @@ +#ifndef IV_SHADEREDITOR +#define IV_SHADEREDITOR + +#ifdef _WIN32 +#pragma once +#endif // _WIN32 + +#define pFnClCallback( x ) void(* x )( float *pfl4 ) +#define pFnClCallback_Declare( x ) void x( float *pfl4 ) + +#define pFnVrCallback( x ) void(* x )( bool * const pbOptions, int * const piOptions,\ + float * const pflOptions, char ** const pszOptions ) +#define pFnVrCallback_Declare( x ) void x( bool * const pbOptions, int * const piOptions,\ + float * const pflOptions, char ** const pszOptions ) + +#ifndef PROCSHADER_DLL + +#ifdef SHADER_EDITOR_DLL +#include "../public/tier1/interface.h" +#include "view_shared.h" +#else +#include "interface.h" +#include "ShaderEditor/ShaderEditorSystem.h" +#endif // NOT SHADER_EDITOR_DLL + +enum SEDIT_SKYMASK_MODE +{ + SKYMASK_OFF = 0, + SKYMASK_QUARTER, // render at 1/4 fb size where possible + SKYMASK_FULL, // render at full fb size +}; + +class CViewSetup_SEdit_Shared +{ +public: + CViewSetup_SEdit_Shared() + { + Q_memset( this, 0, sizeof( CViewSetup_SEdit_Shared ) ); + }; + CViewSetup_SEdit_Shared( const CViewSetup &o ) + { + x = o.x; + y = o.y; + width = o.width; + height = o.height; + fov = o.fov; + fovViewmodel = o.fovViewmodel; + origin = o.origin; + angles = o.angles; + zNear = o.zNear; + zFar = o.zFar; + zNearViewmodel = o.zNearViewmodel; + zFarViewmodel = o.zFarViewmodel; + m_flAspectRatio = o.m_flAspectRatio; + }; + int x,y,width,height; + float fov,fovViewmodel; + Vector origin; + QAngle angles; + float zNear,zFar,zNearViewmodel,zFarViewmodel; + float m_flAspectRatio; +}; + + +class IVShaderEditor : public IBaseInterface +{ +public: + virtual bool Init( CreateInterfaceFn appSystemFactory, CGlobalVarsBase *pGlobals, + void *pSEditMRender, + bool bCreateEditor, bool bEnablePrimaryDebug, int iSkymaskMode ) = 0; + virtual void Shutdown() = 0; + virtual void PrecacheData() = 0; + + // call before Init() to overwrite any paths, pass in NULL for the ones that shouldn't be overwritten + virtual void OverridePaths( const char *pszWorkingDirectory, + const char *pszCompilePath = NULL, // abs path to compiler binaries + const char *pszLocalCompilePath = NULL, // local path to compiler binaries, relative to shader source directory + const char *pszGamePath = NULL, + const char *pszCanvas = NULL, // path to canvas files + const char *pszShaderSource = NULL, // path to shader source files + const char *pszDumps = NULL, // path to shader configuration files + const char *pszUserFunctions = NULL, // path to custom function bodies + const char *pszEditorRoot = NULL ) = 0; // path to 'shadereditorui' home directory + + // update the lib + virtual void OnFrame( float frametime ) = 0; + virtual void OnPreRender( void *viewsetup ) = 0; + virtual void OnSceneRender() = 0; + virtual void OnUpdateSkymask( bool bCombineMode ) = 0; + virtual void OnPostRender( bool bUpdateFB ) = 0; + + // data callbacks for hlsl constants + virtual void RegisterClientCallback( const char *name, pFnClCallback(callback), int numComponents ) = 0; + virtual void LockClientCallbacks() = 0; + + // view render callbacks for post processing graphs + virtual void RegisterViewRenderCallback( const char *pszVrCName, pFnVrCallback(callback), + const char **pszBoolNames = NULL, const bool *pBoolDefaults = NULL, const int numBoolParams = 0, + const char **pszIntNames = NULL, const int *pIntDefaults = NULL, const int numIntParams = 0, + const char **pszFloatNames = NULL, const float *pFloatDefaults = NULL, const int numFloatParams = 0, + const char **pszStringNames = NULL, const char **pStringDefaults = NULL, const int numStringParams = 0 ) = 0; + virtual void LockViewRenderCallbacks() = 0; + + // post processing effect manipulation (precached effects accessible) + // the index becomes invalid when editing the precache list + virtual int GetPPEIndex( const char *pszName ) = 0; // returns -1 when not found, case insensitive + virtual bool IsPPEEnabled( const int &index ) = 0; + virtual void SetPPEEnabled( const int &index, const bool &bEnabled ) = 0; + virtual IMaterial *GetPPEMaterial( const int &index, const char *pszNodeName ) = 0; + + // Draws a PPE graph right now or adds it to the render queue (r_queued_post_processing!) + // Does not push a new RT but uses the current one + // If you have 'during scene' nodes, make sure to call it twice in the appropriate places + virtual void DrawPPEOnDemand( const int &index, const bool bInScene = false ) = 0; +}; + +#define SHADEREDIT_INTERFACE_VERSION "ShaderEditor005" + +#ifdef SHADER_EDITOR_DLL +class ShaderEditorInterface; +extern ShaderEditorInterface *shaderEdit; +#else +extern IVShaderEditor *shaderEdit; +#endif // NOT SHADER_EDITOR_DLL + +#endif // NOT PROCSHADER_DLL + +#endif // NOT IV_SHADEREDITOR \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/SEdit_ModelRender.cpp b/mp/src/game/client/ShaderEditor/SEdit_ModelRender.cpp new file mode 100644 index 00000000..601f89dc --- /dev/null +++ b/mp/src/game/client/ShaderEditor/SEdit_ModelRender.cpp @@ -0,0 +1,424 @@ +// ****************************************************** +// +// Purpose: +// - Handles model rendering requests from the +// shader editor library +// +// ****************************************************** + +#include "cbase.h" + +#include "vgui/iinput.h" +#include "vgui_controls/controls.h" + +#include "ShaderEditor/SEdit_ModelRender.h" +#include "model_types.h" + +#ifndef SOURCE_2006 +#include "viewpostprocess.h" +#endif + +#include "view.h" +#include "input.h" + +#include "beamdraw.h" + +#ifdef SOURCE_2006 +void ScreenToWorld( int mousex, int mousey, float fov, + const Vector& vecRenderOrigin, + const QAngle& vecRenderAngles, + Vector& vecPickingRay ) +{ + float dx, dy; + float c_x, c_y; + float dist; + Vector vpn, vup, vright; + + float scaled_fov = ScaleFOVByWidthRatio( fov, engine->GetScreenAspectRatio() * 0.75f ); + + c_x = ScreenWidth() / 2; + c_y = ScreenHeight() / 2; + + dx = (float)mousex - c_x; + dy = c_y - (float)mousey; + + float dist_denom = tan(M_PI * scaled_fov / 360.0f); + dist = c_x / dist_denom; + AngleVectors( vecRenderAngles, &vpn, &vright, &vup ); + vecPickingRay = vpn * dist + vright * ( dx ) + vup * ( dy ); + VectorNormalize( vecPickingRay ); +} +#else +extern void ScreenToWorld( int mousex, int mousey, float fov, + const Vector& vecRenderOrigin, + const QAngle& vecRenderAngles, + Vector& vecPickingRay ); +#endif + +SEditModelRender __g_ShaderEditorMReder( "ShEditMRender" ); +SEditModelRender *sEditMRender = &__g_ShaderEditorMReder; + +SEditModelRender::SEditModelRender( char const *name ) : CAutoGameSystemPerFrame( name ) +{ + pModelInstance = NULL; + m_iNumPoseParams = 0; + DestroyModel(); +} +SEditModelRender::~SEditModelRender() +{ + DestroyModel(); +} + +bool SEditModelRender::Init() +{ + return true; +} +void SEditModelRender::Shutdown() +{ +} +void SEditModelRender::Update( float frametime ) +{ + if ( !IsModelReady() ) + return; + + pModelInstance->StudioFrameAdvance(); + if ( pModelInstance->GetCycle() >= 1.0f ) + pModelInstance->SetCycle( pModelInstance->GetCycle() - 1.0f ); +} +void SEditModelRender::LevelInitPostEntity() +{ + ResetModel(); +} +void SEditModelRender::LevelShutdownPostEntity() +{ + ResetModel(); +} +void SEditModelRender::ResetModel() +{ + if ( !IsModelReady() ) + return; + pModelInstance->m_flAnimTime = gpGlobals->curtime; + pModelInstance->m_flOldAnimTime = gpGlobals->curtime; +} +bool SEditModelRender::IsModelReady() +{ + if ( !pModelInstance ) + return false; + + bool bValid = !!pModelInstance->GetModel(); + + if ( bValid && Q_strlen( m_szModelPath ) ) + { + const model_t *pMdl = modelinfo ? modelinfo->FindOrLoadModel( m_szModelPath ) : NULL; + if ( pMdl ) + pModelInstance->SetModelPointer( pMdl ); + bValid = !!pMdl; + } + + if ( !bValid ) + DestroyModel(); + + return bValid; +} +bool SEditModelRender::LoadModel( const char *localPath ) +{ + DestroyModel(); + + const model_t *mdl = modelinfo->FindOrLoadModel( localPath ); + if ( !mdl ) + return false; + + Q_strcpy( m_szModelPath, localPath ); + + C_BaseFlex *pEnt = new C_BaseFlex(); + pEnt->InitializeAsClientEntity( NULL, +#if SWARM_DLL + false +#else + RENDER_GROUP_OPAQUE_ENTITY +#endif + ); + MDLCACHE_CRITICAL_SECTION(); + pEnt->SetModelPointer( mdl ); + pEnt->Spawn(); + + pEnt->SetAbsAngles( vec3_angle ); + pEnt->SetAbsOrigin( vec3_origin ); + + pEnt->AddEffects( EF_NODRAW | EF_NOINTERP ); + pEnt->m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK; + + // leave it alone. + pEnt->RemoveFromLeafSystem(); + cl_entitylist->RemoveEntity( pEnt->GetRefEHandle() ); + pEnt->CollisionProp()->DestroyPartitionHandle(); + + CStudioHdr *pHdr = pEnt->GetModelPtr(); + m_iNumPoseParams = pHdr ? pHdr->GetNumPoseParameters() : 0; + + pModelInstance = pEnt; + return true; +} +void SEditModelRender::DestroyModel() +{ + if ( pModelInstance ) + pModelInstance->Remove(); + + pModelInstance = NULL; + m_szModelPath[0] = '\0'; + m_iNumPoseParams = 0; +} +void SEditModelRender::GetModelCenter( float *pFl3_ViewOffset ) +{ + Q_memset( pFl3_ViewOffset, 0, sizeof(float) * 3 ); + if ( IsModelReady() ) + { + MDLCACHE_CRITICAL_SECTION(); + if ( pModelInstance->GetModelPtr() ) + { + const Vector &vecMin = pModelInstance->GetModelPtr()->hull_min(); + const Vector &vecMax = pModelInstance->GetModelPtr()->hull_max(); + Vector vecPos = ( vecMin + ( vecMax - vecMin ) * 0.5f ); + if ( pFl3_ViewOffset ) + Q_memcpy( pFl3_ViewOffset, vecPos.Base(), sizeof(float) * 3 ); + } + } +} +void SEditModelRender::DestroyCharPtrList( char ***szList ) +{ + Assert( szList ); + if ( *szList ) + { + delete [] (**szList); + delete [] (*szList); + *szList = NULL; + } +} + +int SequenceSort( mstudioseqdesc_t *const *seq1, mstudioseqdesc_t *const *seq2 ) +{ + return Q_stricmp( ( *seq1 )->pszLabel(), ( *seq2 )->pszLabel() ); +} +int SEditModelRender::QuerySequences( char ***list ) +{ + if ( !IsModelReady() ) + return 0; + + MDLCACHE_CRITICAL_SECTION(); + CStudioHdr *pHdr = pModelInstance->GetModelPtr(); + if ( !pHdr ) + return 0; + + CUtlVector< mstudioseqdesc_t* >hSeqs; + for ( int i = 0; i < pHdr->GetNumSeq(); i++ ) + if ( !( pHdr->pSeqdesc( i ).flags & STUDIO_HIDDEN ) ) + hSeqs.AddToTail( &pHdr->pSeqdesc( i ) ); + + int numSequences = hSeqs.Count(); + + if ( !numSequences ) + return 0; + + hSeqs.Sort( SequenceSort ); + + CUtlVector< const char* >hNameList; + for ( int i = 0; i < numSequences; i++ ) + { + const char *seqName = NULL; + const mstudioseqdesc_t &seqPtr = *hSeqs[ i ]; + if ( seqPtr.pszLabel() ) + seqName = seqPtr.pszLabel(); + else + seqName = "Unknown Sequence"; + + hNameList.AddToTail( seqName ); + } + + *list = new char*[numSequences]; + + int iTotalLength = 0; + for ( int i = 0; i < numSequences; i++ ) + iTotalLength += Q_strlen( hNameList[i] ) + 1; + + **list = new char[ iTotalLength ]; + + int curpos = 0; + for ( int i = 0; i < numSequences; i++ ) + { + int curLength = Q_strlen( hNameList[i] ) + 1; + (*list)[ i ] = **list + curpos; + Q_strcpy( (*list)[ i ], hNameList[i] ); + curpos += curLength; + } + + hNameList.Purge(); + hSeqs.Purge(); + return numSequences; +} +void SEditModelRender::SetSequence( const char *name ) +{ + if ( !IsModelReady() ) + return; + + MDLCACHE_CRITICAL_SECTION(); + pModelInstance->ResetSequence( pModelInstance->LookupSequence( name ) ); +} +void SEditModelRender::ExecRender() +{ + if ( !IsModelReady() ) + return; + + MDLCACHE_CRITICAL_SECTION(); + for ( int i = 0; i < m_iNumPoseParams; i++ ) + pModelInstance->SetPoseParameter( i, 0 ); + +#if SWARM_DLL + RenderableInstance_t instance; + instance.m_nAlpha = 255; +#endif + pModelInstance->DrawModel( STUDIO_RENDER +#if SWARM_DLL + , instance +#endif + ); +} +void SEditModelRender::DoPostProc( int x, int y, int w, int h ) +{ +#ifndef SOURCE_2006 + if ( view && view->GetPlayerViewSetup()->m_bDoBloomAndToneMapping ) + DoEnginePostProcessing( x, y, w, h, false, false ); +#endif +} +int SEditModelRender::MaterialPicker( char ***szMat ) +{ + int mx, my; +#ifdef SOURCE_2006 + vgui::input()->GetCursorPos( mx, my ); +#else + vgui::input()->GetCursorPosition( mx, my ); +#endif + + Vector ray; + const CViewSetup *pViewSetup = view->GetPlayerViewSetup(); + float ratio =engine->GetScreenAspectRatio( +#if SWARM_DLL + pViewSetup->width, pViewSetup->height +#endif + ); + + ratio = ( 1.0f / ratio ) * (4.0f/3.0f); + float flFov = ScaleFOVByWidthRatio( pViewSetup->fov, ratio ); + ScreenToWorld( mx, my, flFov, pViewSetup->origin, pViewSetup->angles, ray ); + + Vector start = pViewSetup->origin; + Vector end = start + ray * MAX_TRACE_LENGTH; + trace_t tr; + C_BaseEntity *pIgnore = input->CAM_IsThirdPerson() ? NULL : C_BasePlayer::GetLocalPlayer(); + UTIL_TraceLine( start, end, MASK_SOLID, pIgnore, COLLISION_GROUP_NONE, &tr ); + + if ( !tr.DidHit() ) + return 0; + + int numMaterials = 0; + IMaterial **MatList = NULL; + studiohdr_t *pSHdr = NULL; + + if ( tr.DidHitWorld() ) + { + if ( tr.hitbox == 0 ) + { + Vector dummy; + IMaterial *pMat = engine->TraceLineMaterialAndLighting( start, end, dummy, dummy ); + if ( pMat ) + { + numMaterials = 1; + MatList = new IMaterial*[1]; + MatList[0] = pMat; + } + } + else + { + ICollideable *prop = staticpropmgr->GetStaticPropByIndex( tr.hitbox - 1 ); + if ( prop ) + { + IClientRenderable *pRenderProp = prop->GetIClientUnknown()->GetClientRenderable(); + if ( pRenderProp ) + { + const model_t *pModel = pRenderProp->GetModel(); + if ( pModel ) + pSHdr = modelinfo->GetStudiomodel( pModel ); + } + } + } + } + else if ( tr.m_pEnt ) + { + const model_t *pModel = tr.m_pEnt->GetModel(); + if ( pModel ) + pSHdr = modelinfo->GetStudiomodel( pModel ); + } + + if ( pSHdr ) + { + Assert( !numMaterials && !MatList ); + numMaterials = pSHdr->numtextures; + const int numPaths = pSHdr->numcdtextures; + + if ( numMaterials ) + { + CUtlVector< IMaterial* >hValidMaterials; + for ( int i = 0; i < numMaterials; i++ ) + { + mstudiotexture_t *pStudioTex = pSHdr->pTexture( i ); + const char *matName = pStudioTex->pszName(); + + for ( int p = 0; p < numPaths; p++ ) + { + char tmpPath[MAX_PATH]; + Q_snprintf( tmpPath, MAX_PATH, "%s%s\0", pSHdr->pCdtexture( p ), matName ); + Q_FixSlashes( tmpPath ); + IMaterial *pTempMat = materials->FindMaterial( tmpPath, TEXTURE_GROUP_MODEL ); + if ( !IsErrorMaterial( pTempMat ) ) + { + hValidMaterials.AddToTail( pTempMat ); + break; + } + } + } + + numMaterials = hValidMaterials.Count(); + if ( numMaterials ) + { + MatList = new IMaterial*[ numMaterials ]; + for ( int i = 0; i < numMaterials; i++ ) + MatList[i] = hValidMaterials[i]; + } + + hValidMaterials.Purge(); + } + } + + *szMat = new char*[ numMaterials ]; + + int iTotalLength = 0; + for ( int i = 0; i < numMaterials; i++ ) + iTotalLength += Q_strlen( MatList[i]->GetName() ) + 1; + + **szMat = new char[ iTotalLength ]; + + int curpos = 0; + for ( int i = 0; i < numMaterials; i++ ) + { + const char *pszName = MatList[i]->GetName(); + + int curLength = Q_strlen( pszName ) + 1; + (*szMat)[ i ] = **szMat + curpos; + Q_strcpy( (*szMat)[ i ], pszName ); + curpos += curLength; + } + + if ( MatList ) + delete [] MatList; + + return numMaterials; +} \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/SEdit_ModelRender.h b/mp/src/game/client/ShaderEditor/SEdit_ModelRender.h new file mode 100644 index 00000000..1a147fc4 --- /dev/null +++ b/mp/src/game/client/ShaderEditor/SEdit_ModelRender.h @@ -0,0 +1,48 @@ +#ifndef SHEDITMRENDER_H +#define SHEDITMRENDER_H + +#include "cbase.h" +#include "ShaderEditor/ISEdit_ModelRender.h" + +class C_BaseFlex_OverrideLod; + +class SEditModelRender : public ISEditModelRender, public CAutoGameSystemPerFrame +{ +public: + SEditModelRender( char const *name ); + ~SEditModelRender(); + +// autogamesystem + virtual bool Init(); + virtual void Shutdown(); + virtual void Update( float frametime ); + + virtual void LevelInitPostEntity(); + virtual void LevelShutdownPostEntity(); + +// interface + virtual bool LoadModel( const char *localPath ); + virtual void DestroyModel(); + virtual void GetModelCenter( float *pFl3_ViewOffset ); + + virtual int QuerySequences( char ***list ); + virtual void SetSequence( const char *name ); + + virtual void ExecRender(); + virtual void DoPostProc( int x, int y, int w, int h ); + + virtual int MaterialPicker( char ***szMat ); + + virtual void DestroyCharPtrList( char ***szList ); + +private: + + bool IsModelReady(); + void ResetModel(); + + C_BaseFlex *pModelInstance; + char m_szModelPath[MAX_PATH]; + int m_iNumPoseParams; +}; + +#endif \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/ShaderEditorSystem.cpp b/mp/src/game/client/ShaderEditor/ShaderEditorSystem.cpp new file mode 100644 index 00000000..211bb382 --- /dev/null +++ b/mp/src/game/client/ShaderEditor/ShaderEditorSystem.cpp @@ -0,0 +1,1629 @@ +// ****************************************************** +// +// Purpose: +// - Connects the shader editor +// - Sends data from the main viewsetup +// - exposes client callbacks to shaders +// +// ****************************************************** + +#include "cbase.h" +#include "client_factorylist.h" +#include "ShaderEditor/IVShaderEditor.h" +#include "ShaderEditor/SEdit_ModelRender.h" +#include "ivrenderview.h" +#include "iviewrender.h" +#include "viewrender.h" +#include "view.h" +#include "view_scene.h" +#include "view_shared.h" +#include "beamdraw.h" +#include "c_sun.h" +#include "tier0/icommandline.h" +#include "rendertexture.h" +#include "c_rope.h" +#include "model_types.h" +#ifdef SWARM_DLL +#include "modelrendersystem.h" +#endif + + +#if SWARM_DLL +#define Editor_MainViewOrigin MainViewOrigin( 0 ) +#define Editor_MainViewForward MainViewForward( 0 ) +#else +#define Editor_MainViewOrigin MainViewOrigin() +#define Editor_MainViewForward MainViewForward() +#endif + + +ShaderEditorHandler __g_ShaderEditorSystem( "ShEditUpdate" ); +ShaderEditorHandler *g_ShaderEditorSystem = &__g_ShaderEditorSystem; + +CSysModule *shaderEditorModule = NULL; +IVShaderEditor *shaderEdit = NULL; + +ShaderEditorHandler::ShaderEditorHandler( char const *name ) : CAutoGameSystemPerFrame( name ) +{ + m_bReady = false; + m_piCurrentViewId = NULL; +} + +ShaderEditorHandler::~ShaderEditorHandler() +{ +} + +const bool ShaderEditorHandler::IsReady() +{ + return m_bReady; +} + +bool ShaderEditorHandler::Init() +{ + factorylist_t factories; + FactoryList_Retrieve( factories ); + +#ifdef SOURCE_2006 + ConVar *pCVarDev = cvar->FindVar( "developer" ); + bool bShowPrimDebug = pCVarDev != NULL && pCVarDev->GetInt() != 0; +#else + ConVarRef devEnabled( "developer", true ); + bool bShowPrimDebug = devEnabled.GetInt() != 0; +#endif + + bool bCreateEditor = ( CommandLine() != NULL ) && ( CommandLine()->FindParm( "-shaderedit" ) != 0 ); + SEDIT_SKYMASK_MODE iEnableSkymask = SKYMASK_OFF; + +#ifdef SHADEREDITOR_FORCE_ENABLED + bCreateEditor = true; + iEnableSkymask = SKYMASK_QUARTER; +#endif + + char modulePath[MAX_PATH*4]; +#ifdef SWARM_DLL + Q_snprintf( modulePath, sizeof( modulePath ), "%s/bin/shadereditor_swarm.dll\0", engine->GetGameDirectory() ); +#elif SOURCE_2006 + Q_snprintf( modulePath, sizeof( modulePath ), "%s/bin/shadereditor_2006.dll\0", engine->GetGameDirectory() ); +#elif SOURCE_2013 + Q_snprintf( modulePath, sizeof( modulePath ), "%s/bin/shadereditor_2013.dll\0", engine->GetGameDirectory() ); +#else + Q_snprintf( modulePath, sizeof( modulePath ), "%s/bin/shadereditor_2007.dll\0", engine->GetGameDirectory() ); +#endif + shaderEditorModule = Sys_LoadModule( modulePath ); + if ( shaderEditorModule ) + { + CreateInterfaceFn shaderEditorDLLFactory = Sys_GetFactory( shaderEditorModule ); + shaderEdit = shaderEditorDLLFactory ? ((IVShaderEditor *) shaderEditorDLLFactory( SHADEREDIT_INTERFACE_VERSION, NULL )) : NULL; + + if ( !shaderEdit ) + { + Warning( "Unable to pull IVShaderEditor interface.\n" ); + } + else if ( !shaderEdit->Init( factories.appSystemFactory, gpGlobals, sEditMRender, + bCreateEditor, bShowPrimDebug, iEnableSkymask ) ) + { + Warning( "Cannot initialize IVShaderEditor.\n" ); + shaderEdit = NULL; + } + } + else + { + Warning( "Cannot load shadereditor.dll from %s!\n", modulePath ); + } + + m_bReady = shaderEdit != NULL; + + RegisterCallbacks(); + RegisterViewRenderCallbacks(); + + if ( IsReady() ) + { + shaderEdit->PrecacheData(); + } + + return true; +} + +#ifdef SHADEREDITOR_FORCE_ENABLED +CON_COMMAND( sedit_debug_toggle_ppe, "" ) +{ + if ( !g_ShaderEditorSystem->IsReady() ) + return Warning( "lib not ready.\n" ); + + if ( args.ArgC() < 2 ) + return; + + const int idx = shaderEdit->GetPPEIndex( args[1] ); + if ( idx < 0 ) + return Warning( "can't find ppe named: %s\n", args[1] ); + + shaderEdit->SetPPEEnabled( idx, !shaderEdit->IsPPEEnabled( idx ) ); +} +#endif + +void ShaderEditorHandler::Shutdown() +{ + if ( shaderEdit ) + shaderEdit->Shutdown(); + if ( shaderEditorModule ) + Sys_UnloadModule( shaderEditorModule ); +} + +void ShaderEditorHandler::Update( float frametime ) +{ + if ( IsReady() ) + shaderEdit->OnFrame( frametime ); +} + +CThreadMutex m_Lock; + +void ShaderEditorHandler::PreRender() +{ + if ( IsReady() && view ) + { + // make sure the class matches + const CViewSetup *v = view->GetPlayerViewSetup(); + CViewSetup_SEdit_Shared stableVSetup( *v ); + shaderEdit->OnPreRender( &stableVSetup ); + + m_Lock.Lock(); + PrepareCallbackData(); + m_Lock.Unlock(); + } +} +void ShaderEditorHandler::PostRender() +{ +} +#ifdef SOURCE_2006 +void ShaderEditorHandler::CustomViewRender( int *viewId, const VisibleFogVolumeInfo_t &fogVolumeInfo ) +#else +void ShaderEditorHandler::CustomViewRender( int *viewId, const VisibleFogVolumeInfo_t &fogVolumeInfo, const WaterRenderInfo_t &waterRenderInfo ) +#endif +{ + m_piCurrentViewId = viewId; + m_tFogVolumeInfo = fogVolumeInfo; + +#ifndef SOURCE_2006 + m_tWaterRenderInfo = waterRenderInfo; +#endif + + if ( IsReady() ) + shaderEdit->OnSceneRender(); +} +void ShaderEditorHandler::UpdateSkymask( bool bCombineMode ) +{ + if ( IsReady() ) + shaderEdit->OnUpdateSkymask( bCombineMode ); +} +void ShaderEditorHandler::CustomPostRender() +{ + if ( IsReady() ) + shaderEdit->OnPostRender( true ); +} + +struct CallbackData_t +{ + void Reset() + { + sun_data.Init(); + sun_dir.Init(); + + player_speed.Init(); + player_pos.Init(); + }; + Vector4D sun_data; + Vector sun_dir; + + Vector4D player_speed; + Vector player_pos; +}; + +static CallbackData_t clCallback_data; + +void ShaderEditorHandler::PrepareCallbackData() +{ + clCallback_data.Reset(); + + float flSunAmt_Goal = 0; + static float s_flSunAmt_Last = 0; + + C_BaseEntity *pEnt = ClientEntityList().FirstBaseEntity(); + while ( pEnt ) + { + if ( !Q_stricmp( pEnt->GetClassname(), "class C_Sun" ) ) + { + C_Sun *pSun = ( C_Sun* )pEnt; + Vector dir = pSun->m_vDirection; + dir.NormalizeInPlace(); + + Vector screen; + + if ( ScreenTransform( Editor_MainViewOrigin + dir * 512, screen ) ) + ScreenTransform( (Editor_MainViewOrigin - dir * 512), screen ); + + screen = screen * Vector( 0.5f, -0.5f, 0 ) + Vector( 0.5f, 0.5f, 0 ); + + Q_memcpy( clCallback_data.sun_data.Base(), screen.Base(), sizeof(float) * 2 ); + clCallback_data.sun_data[ 2 ] = DotProduct( dir, Editor_MainViewForward ); + clCallback_data.sun_dir = dir; + + trace_t tr; + UTIL_TraceLine( Editor_MainViewOrigin, Editor_MainViewOrigin + dir * MAX_TRACE_LENGTH, MASK_SOLID, NULL, COLLISION_GROUP_DEBRIS, &tr ); + if ( !tr.DidHitWorld() ) + break; + + if ( tr.surface.flags & SURF_SKY ) + flSunAmt_Goal = 1; + + break; + } + pEnt = ClientEntityList().NextBaseEntity( pEnt ); + } + + if ( s_flSunAmt_Last != flSunAmt_Goal ) + s_flSunAmt_Last = Approach( flSunAmt_Goal, s_flSunAmt_Last, gpGlobals->frametime * ( (!!flSunAmt_Goal) ? 4.0f : 0.75f ) ); + + clCallback_data.sun_data[ 3 ] = s_flSunAmt_Last; + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( pPlayer ) + { + Vector velo = pPlayer->GetLocalVelocity(); + clCallback_data.player_speed[ 3 ] = velo.NormalizeInPlace(); + Q_memcpy( clCallback_data.player_speed.Base(), velo.Base(), sizeof(float) * 3 ); + + clCallback_data.player_pos = pPlayer->GetLocalOrigin(); + } +} + +pFnClCallback_Declare( ClCallback_SunData ) +{ + m_Lock.Lock(); + Q_memcpy( pfl4, clCallback_data.sun_data.Base(), sizeof(float) * 4 ); + m_Lock.Unlock(); +} + +pFnClCallback_Declare( ClCallback_SunDirection ) +{ + m_Lock.Lock(); + Q_memcpy( pfl4, clCallback_data.sun_dir.Base(), sizeof(float) * 3 ); + m_Lock.Unlock(); +} + +pFnClCallback_Declare( ClCallback_PlayerVelocity ) +{ + m_Lock.Lock(); + Q_memcpy( pfl4, clCallback_data.player_speed.Base(), sizeof(float) * 4 ); + m_Lock.Unlock(); +} + +pFnClCallback_Declare( ClCallback_PlayerPos ) +{ + m_Lock.Lock(); + Q_memcpy( pfl4, clCallback_data.player_pos.Base(), sizeof(float) * 3 ); + m_Lock.Unlock(); +} + +void ShaderEditorHandler::RegisterCallbacks() +{ + if ( !IsReady() ) + return; + + // 4 components max + shaderEdit->RegisterClientCallback( "sun data", ClCallback_SunData, 4 ); + shaderEdit->RegisterClientCallback( "sun dir", ClCallback_SunDirection, 3 ); + shaderEdit->RegisterClientCallback( "local player velocity", ClCallback_PlayerVelocity, 4 ); + shaderEdit->RegisterClientCallback( "local player position", ClCallback_PlayerPos, 3 ); + + shaderEdit->LockClientCallbacks(); +} + +#ifdef SOURCE_2006 + +void ShaderEditorHandler::RegisterViewRenderCallbacks(){} + +#else + +extern bool DoesViewPlaneIntersectWater( float waterZ, int leafWaterDataID ); + +// copy pasta from baseworldview +class CBaseVCallbackView : public CRendering3dView +{ + DECLARE_CLASS( CBaseVCallbackView, CRendering3dView ); +protected: + + CBaseVCallbackView( CViewRender *pMainView ) : CRendering3dView( pMainView ) + { + }; + + virtual bool AdjustView( float waterHeight ){ return false; }; + + virtual void CallbackInitRenderList( int viewId ) + { + BuildRenderableRenderLists( viewId ); + }; + + virtual bool ShouldDrawParticles() + { + return true; + }; + + virtual bool ShouldDrawRopes() + { + return true; + }; + + virtual bool ShouldDrawWorld() + { + return true; + }; + + virtual bool ShouldDrawTranslucents() + { + return true; + }; + + virtual bool ShouldDrawTranslucentWorld() + { + return true; + }; + + void DrawSetup( float waterHeight, int nSetupFlags, float waterZAdjust, int iForceViewLeaf = -1 ) + { + int savedViewID = g_ShaderEditorSystem->GetViewIdForModify(); + + g_ShaderEditorSystem->GetViewIdForModify() = VIEW_ILLEGAL; + + render->BeginUpdateLightmaps(); + + bool bDrawEntities = ( nSetupFlags & DF_DRAW_ENTITITES ) != 0; + BuildWorldRenderLists( bDrawEntities, iForceViewLeaf, true, false, NULL ); + PruneWorldListInfo(); + + if ( bDrawEntities ) + CallbackInitRenderList( savedViewID ); + + render->EndUpdateLightmaps(); + + g_ShaderEditorSystem->GetViewIdForModify() = savedViewID; + }; + + void DrawExecute( float waterHeight, view_id_t viewID, float waterZAdjust ) + { + // ClientWorldListInfo_t is defined in viewrender.cpp... + //g_pClientShadowMgr->ComputeShadowTextures( *this, m_pWorldListInfo->m_LeafCount, m_pWorldListInfo->m_pLeafList ); + + engine->Sound_ExtraUpdate(); + + int savedViewID = g_ShaderEditorSystem->GetViewIdForModify(); + g_ShaderEditorSystem->GetViewIdForModify() = viewID; + + int iDrawFlagsBackup = m_DrawFlags; + m_DrawFlags |= m_pMainView->GetBaseDrawFlags(); + + PushView( waterHeight ); + + CMatRenderContextPtr pRenderContext( materials ); + + ITexture *pSaveFrameBufferCopyTexture = pRenderContext->GetFrameBufferCopyTexture( 0 ); + if ( engine->GetDXSupportLevel() >= 80 ) + { + pRenderContext->SetFrameBufferCopyTexture( GetPowerOfTwoFrameBufferTexture() ); + } + + pRenderContext.SafeRelease(); + + static ConVarRef translucentNoWorld( "r_drawtranslucentworld" ); + const int tnoWorldSaved = translucentNoWorld.GetInt(); + translucentNoWorld.SetValue( ShouldDrawWorld() ? 0 : 1 ); + + if ( m_DrawFlags & DF_DRAW_ENTITITES ) + { + if ( ShouldDrawWorld() ) + DrawWorld( waterZAdjust ); + + DrawOpaqueRenderables_Custom( false ); + + if ( ShouldDrawTranslucents() && ShouldDrawTranslucentWorld() ) + DrawTranslucentRenderables( false, false ); + else if ( ShouldDrawTranslucents() ) + DrawTranslucentRenderablesNoWorld( false ); + else if ( ShouldDrawTranslucentWorld() ) + DrawTranslucentWorldInLeaves( false ); + } + else if ( ShouldDrawWorld() ) + { + DrawWorld( waterZAdjust ); + + if ( ShouldDrawTranslucentWorld() ) + DrawTranslucentWorldInLeaves( false ); + } + + translucentNoWorld.SetValue( tnoWorldSaved ); + + if ( CurrentViewID() != VIEW_MAIN && CurrentViewID() != VIEW_INTRO_CAMERA ) + PixelVisibility_EndCurrentView(); + + pRenderContext.GetFrom( materials ); + pRenderContext->SetFrameBufferCopyTexture( pSaveFrameBufferCopyTexture ); + PopView(); + + m_DrawFlags = iDrawFlagsBackup; + + g_ShaderEditorSystem->GetViewIdForModify() = savedViewID; + }; + + virtual void PushView( float waterHeight ) + { + float spread = 2.0f; + if( m_DrawFlags & DF_FUDGE_UP ) + { + waterHeight += spread; + } + else + { + waterHeight -= spread; + } + + MaterialHeightClipMode_t clipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE; + + if ( ( m_DrawFlags & DF_CLIP_Z ) ) + { + if( m_DrawFlags & DF_CLIP_BELOW ) + { + clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT; + } + else + { + clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT; + } + } + + CMatRenderContextPtr pRenderContext( materials ); + + if ( m_ClearFlags & ( VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_STENCIL ) ) + { + if ( m_ClearFlags & VIEW_CLEAR_OBEY_STENCIL ) + { + pRenderContext->ClearBuffersObeyStencil( (m_ClearFlags & VIEW_CLEAR_COLOR) != 0, (m_ClearFlags & VIEW_CLEAR_DEPTH) != 0 ); + } + else + { + pRenderContext->ClearBuffers( (m_ClearFlags & VIEW_CLEAR_COLOR) != 0, (m_ClearFlags & VIEW_CLEAR_DEPTH) != 0, (m_ClearFlags & VIEW_CLEAR_STENCIL) != 0 ); + } + } + + pRenderContext->SetHeightClipMode( clipMode ); + if ( clipMode != MATERIAL_HEIGHTCLIPMODE_DISABLE ) + { + pRenderContext->SetHeightClipZ( waterHeight ); + } + }; + + virtual void PopView() + { + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE ); + }; + + void DrawOpaqueRenderables_Custom( bool bShadowDepth ) + { + //if( !r_drawopaquerenderables.GetBool() ) + // return; + + if( !m_pMainView->ShouldDrawEntities() ) + return; + + render->SetBlend( 1 ); + + const bool bRopes = ShouldDrawRopes(); + const bool bParticles = ShouldDrawParticles(); + + // + // Prepare to iterate over all leaves that were visible, and draw opaque things in them. + // + if ( bRopes ) + RopeManager()->ResetRenderCache(); + if ( bParticles ) + g_pParticleSystemMgr->ResetRenderCache(); + +#ifdef SWARM_DLL + + extern ConVar cl_modelfastpath; + extern ConVar r_drawothermodels; + + // Categorize models by type + int nOpaqueRenderableCount = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_OPAQUE]; + CUtlVector< CClientRenderablesList::CEntry* > brushModels( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount ); + CUtlVector< CClientRenderablesList::CEntry* > staticProps( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount ); + CUtlVector< CClientRenderablesList::CEntry* > otherRenderables( (CClientRenderablesList::CEntry **)stackalloc( nOpaqueRenderableCount * sizeof( CClientRenderablesList::CEntry* ) ), nOpaqueRenderableCount ); + CClientRenderablesList::CEntry *pOpaqueList = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_OPAQUE]; + for ( int i = 0; i < nOpaqueRenderableCount; ++i ) + { + switch( pOpaqueList[i].m_nModelType ) + { + case RENDERABLE_MODEL_BRUSH: brushModels.AddToTail( &pOpaqueList[i] ); break; + case RENDERABLE_MODEL_STATIC_PROP: staticProps.AddToTail( &pOpaqueList[i] ); break; + default: otherRenderables.AddToTail( &pOpaqueList[i] ); break; + } + } + + // + // First do the brush models + // + DrawOpaqueRenderables_DrawBrushModels( brushModels.Count(), brushModels.Base(), bShadowDepth ); + + // Move all static props to modelrendersystem + bool bUseFastPath = ( cl_modelfastpath.GetInt() != 0 ); + + // + // Sort everything that's not a static prop + // + int nStaticPropCount = staticProps.Count(); + int numOpaqueEnts = otherRenderables.Count(); + CUtlVector< CClientRenderablesList::CEntry* > arrRenderEntsNpcsFirst( (CClientRenderablesList::CEntry **)stackalloc( numOpaqueEnts * sizeof( CClientRenderablesList::CEntry ) ), numOpaqueEnts ); + CUtlVector< ModelRenderSystemData_t > arrModelRenderables( (ModelRenderSystemData_t *)stackalloc( ( numOpaqueEnts + nStaticPropCount ) * sizeof( ModelRenderSystemData_t ) ), numOpaqueEnts + nStaticPropCount ); + + // Queue up RENDER_GROUP_OPAQUE_ENTITY entities to be rendered later. + CClientRenderablesList::CEntry *itEntity; + if( r_drawothermodels.GetBool() ) + { + for ( int i = 0; i < numOpaqueEnts; ++i ) + { + itEntity = otherRenderables[i]; + if ( !itEntity->m_pRenderable ) + continue; + + IClientUnknown *pUnknown = itEntity->m_pRenderable->GetIClientUnknown(); + IClientModelRenderable *pModelRenderable = pUnknown->GetClientModelRenderable(); + C_BaseEntity *pEntity = pUnknown->GetBaseEntity(); + + // FIXME: Strangely, some static props are in the non-static prop bucket + // which is what the last case in this if statement is for + if ( bUseFastPath && pModelRenderable ) + { + ModelRenderSystemData_t data; + data.m_pRenderable = itEntity->m_pRenderable; + data.m_pModelRenderable = pModelRenderable; + data.m_InstanceData = itEntity->m_InstanceData; + arrModelRenderables.AddToTail( data ); + otherRenderables.FastRemove( i ); + --i; --numOpaqueEnts; + continue; + } + + if ( !pEntity ) + continue; + + if ( pEntity->IsNPC() ) + { + arrRenderEntsNpcsFirst.AddToTail( itEntity ); + otherRenderables.FastRemove( i ); + --i; --numOpaqueEnts; + continue; + } + } + } + + // Queue up the static props to be rendered later. + for ( int i = 0; i < nStaticPropCount; ++i ) + { + itEntity = staticProps[i]; + if ( !itEntity->m_pRenderable ) + continue; + + IClientUnknown *pUnknown = itEntity->m_pRenderable->GetIClientUnknown(); + IClientModelRenderable *pModelRenderable = pUnknown->GetClientModelRenderable(); + if ( !bUseFastPath || !pModelRenderable ) + continue; + + ModelRenderSystemData_t data; + data.m_pRenderable = itEntity->m_pRenderable; + data.m_pModelRenderable = pModelRenderable; + data.m_InstanceData = itEntity->m_InstanceData; + arrModelRenderables.AddToTail( data ); + + staticProps.FastRemove( i ); + --i; --nStaticPropCount; + } + + // + // Draw model renderables now (ie. models that use the fast path) + // + DrawOpaqueRenderables_ModelRenderables( arrModelRenderables.Count(), arrModelRenderables.Base(), bShadowDepth ); + + // Turn off z pass here. Don't want non-fastpath models with potentially large dynamic VB requirements overwrite + // stuff in the dynamic VB ringbuffer. We're calling End360ZPass again in DrawExecute, but that's not a problem. + // Begin360ZPass/End360ZPass don't have to be matched exactly. + End360ZPass(); + + // + // Draw static props + opaque entities that aren't using the fast path. + // + DrawOpaqueRenderables_Range( otherRenderables.Count(), otherRenderables.Base(), bShadowDepth ); + DrawOpaqueRenderables_DrawStaticProps( staticProps.Count(), staticProps.Base(), bShadowDepth ); + + // + // Draw NPCs now + // + DrawOpaqueRenderables_NPCs( arrRenderEntsNpcsFirst.Count(), arrRenderEntsNpcsFirst.Base(), bShadowDepth ); +#else + + bool const bDrawopaquestaticpropslast = false; //r_drawopaquestaticpropslast.GetBool(); + + + // + // First do the brush models + // + { + CClientRenderablesList::CEntry *pEntitiesBegin, *pEntitiesEnd; + pEntitiesBegin = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_OPAQUE_BRUSH]; + pEntitiesEnd = pEntitiesBegin + m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_OPAQUE_BRUSH]; + DrawOpaqueRenderables_DrawBrushModels( pEntitiesBegin, pEntitiesEnd, bShadowDepth ); + } + + + // + // Sort everything that's not a static prop + // + int numOpaqueEnts = 0; + for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket ) + numOpaqueEnts += m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ]; + + CUtlVector< C_BaseAnimating * > arrBoneSetupNpcsLast( (C_BaseAnimating **)_alloca( numOpaqueEnts * sizeof( C_BaseAnimating * ) ), numOpaqueEnts, numOpaqueEnts ); + CUtlVector< CClientRenderablesList::CEntry > arrRenderEntsNpcsFirst( (CClientRenderablesList::CEntry *)_alloca( numOpaqueEnts * sizeof( CClientRenderablesList::CEntry ) ), numOpaqueEnts, numOpaqueEnts ); + int numNpcs = 0, numNonNpcsAnimating = 0; + + for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket ) + { + for( CClientRenderablesList::CEntry + * const pEntitiesBegin = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ], + * const pEntitiesEnd = pEntitiesBegin + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ], + *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity ) + { + C_BaseEntity *pEntity = itEntity->m_pRenderable ? itEntity->m_pRenderable->GetIClientUnknown()->GetBaseEntity() : NULL; + if ( pEntity ) + { + if ( pEntity->IsNPC() ) + { + C_BaseAnimating *pba = assert_cast( pEntity ); + arrRenderEntsNpcsFirst[ numNpcs ++ ] = *itEntity; + arrBoneSetupNpcsLast[ numOpaqueEnts - numNpcs ] = pba; + + itEntity->m_pRenderable = NULL; // We will render NPCs separately + itEntity->m_RenderHandle = NULL; + + continue; + } + else if ( pEntity->GetBaseAnimating() ) + { + C_BaseAnimating *pba = assert_cast( pEntity ); + arrBoneSetupNpcsLast[ numNonNpcsAnimating ++ ] = pba; + // fall through + } + } + } + } + + // + // Draw static props + opaque entities from the biggest bucket to the smallest + // + { + CClientRenderablesList::CEntry * pEnts[ RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS ][2]; + CClientRenderablesList::CEntry * pProps[ RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS ][2]; + + for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket ) + { + pEnts[bucket][0] = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ]; + pEnts[bucket][1] = pEnts[bucket][0] + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ]; + + pProps[bucket][0] = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_STATIC_HUGE + 2 * bucket ]; + pProps[bucket][1] = pProps[bucket][0] + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_STATIC_HUGE + 2 * bucket ]; + } + + for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket ) + { + if ( bDrawopaquestaticpropslast ) + { + DrawOpaqueRenderables_Range( pEnts[bucket][0], pEnts[bucket][1], bShadowDepth ); + DrawOpaqueRenderables_DrawStaticProps( pProps[bucket][0], pProps[bucket][1], bShadowDepth ); + } + else + { + DrawOpaqueRenderables_Range( pEnts[bucket][0], pEnts[bucket][1], bShadowDepth ); + DrawOpaqueRenderables_DrawStaticProps( pProps[bucket][0], pProps[bucket][1], bShadowDepth ); + } + } + } + + // + // Draw NPCs now + // + DrawOpaqueRenderables_Range( arrRenderEntsNpcsFirst.Base(), arrRenderEntsNpcsFirst.Base() + numNpcs, bShadowDepth ); +#endif + // + // Ropes and particles + // + if ( bRopes ) + RopeManager()->DrawRenderCache( bShadowDepth ); + if ( bParticles ) + g_pParticleSystemMgr->DrawRenderCache( bShadowDepth ); + }; + +#ifdef SWARM_DLL + void DrawOpaqueRenderables_ModelRenderables( int nCount, ModelRenderSystemData_t* pModelRenderables, bool bShadowDepth ) + { + g_pModelRenderSystem->DrawModels( pModelRenderables, nCount, bShadowDepth ? MODEL_RENDER_MODE_SHADOW_DEPTH : MODEL_RENDER_MODE_NORMAL ); + } + void DrawOpaqueRenderables_NPCs( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bShadowDepth ) + { + DrawOpaqueRenderables_Range( nCount, ppEntities, bShadowDepth ); + } + void DrawRenderable( IClientRenderable *pEnt, int flags, const RenderableInstance_t &instance ) + { + extern ConVar r_entityclips; + float *pRenderClipPlane = NULL; + if( r_entityclips.GetBool() ) + pRenderClipPlane = pEnt->GetRenderClipPlane(); + + if( pRenderClipPlane ) + { + CMatRenderContextPtr pRenderContext( materials ); + if( !materials->UsingFastClipping() ) //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though + pRenderContext->PushCustomClipPlane( pRenderClipPlane ); +#if DEBUG + else + AssertMsg( 0, "can't link DrawClippedDepthBox externally so you either have to cope with even more redundancy or move all this crap to viewrender" ); +#endif + // DrawClippedDepthBox( pEnt, pRenderClipPlane ); + Assert( view->GetCurrentlyDrawingEntity() == NULL ); + view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() ); + bool bBlockNormalDraw = false; //BlurTest( pEnt, flags, true, instance ); + if( !bBlockNormalDraw ) + pEnt->DrawModel( flags, instance ); + //BlurTest( pEnt, flags, false, instance ); + view->SetCurrentlyDrawingEntity( NULL ); + + if( !materials->UsingFastClipping() ) + pRenderContext->PopCustomClipPlane(); + } + else + { + Assert( view->GetCurrentlyDrawingEntity() == NULL ); + view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() ); + bool bBlockNormalDraw = false; //BlurTest( pEnt, flags, true, instance ); + if( !bBlockNormalDraw ) + pEnt->DrawModel( flags, instance ); + //BlurTest( pEnt, flags, false, instance ); + view->SetCurrentlyDrawingEntity( NULL ); + } + }; + void DrawOpaqueRenderable( IClientRenderable *pEnt, bool bTwoPass, bool bShadowDepth ) + { + ASSERT_LOCAL_PLAYER_RESOLVABLE(); + float color[3]; + + Assert( !IsSplitScreenSupported() || pEnt->ShouldDrawForSplitScreenUser( GET_ACTIVE_SPLITSCREEN_SLOT() ) ); + Assert( (pEnt->GetIClientUnknown() == NULL) || (pEnt->GetIClientUnknown()->GetIClientEntity() == NULL) || (pEnt->GetIClientUnknown()->GetIClientEntity()->IsBlurred() == false) ); + pEnt->GetColorModulation( color ); + render->SetColorModulation( color ); + + int flags = STUDIO_RENDER; + if ( bTwoPass ) + { + flags |= STUDIO_TWOPASS; + } + + if ( bShadowDepth ) + { + flags |= STUDIO_SHADOWDEPTHTEXTURE; + } + + RenderableInstance_t instance; + instance.m_nAlpha = 255; + DrawRenderable( pEnt, flags, instance ); + }; +#else + void DrawOpaqueRenderable( IClientRenderable *pEnt, bool bTwoPass, bool bShadowDepth ) + { + float color[3]; + + pEnt->GetColorModulation( color ); + render->SetColorModulation( color ); + + int flags = STUDIO_RENDER; + if ( bTwoPass ) + { + flags |= STUDIO_TWOPASS; + } + + if ( bShadowDepth ) + { + flags |= STUDIO_SHADOWDEPTHTEXTURE; + } + + float *pRenderClipPlane = NULL; + if( true ) //r_entityclips.GetBool() ) + pRenderClipPlane = pEnt->GetRenderClipPlane(); + + if( pRenderClipPlane ) + { + CMatRenderContextPtr pRenderContext( materials ); + if( !materials->UsingFastClipping() ) //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though + pRenderContext->PushCustomClipPlane( pRenderClipPlane ); +#if DEBUG + else + AssertMsg( 0, "can't link DrawClippedDepthBox externally so you either have to cope with even more redundancy or move all this crap to viewrender" ); +#endif + // DrawClippedDepthBox( pEnt, pRenderClipPlane ); + Assert( view->GetCurrentlyDrawingEntity() == NULL ); + view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() ); + pEnt->DrawModel( flags ); + view->SetCurrentlyDrawingEntity( NULL ); + if( pRenderClipPlane && !materials->UsingFastClipping() ) + pRenderContext->PopCustomClipPlane(); + } + else + { + Assert( view->GetCurrentlyDrawingEntity() == NULL ); + view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() ); + pEnt->DrawModel( flags ); + view->SetCurrentlyDrawingEntity( NULL ); + } + }; +#endif + +#ifdef SWARM_DLL + void DrawOpaqueRenderables_DrawBrushModels( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bShadowDepth ) + { + for( int i = 0; i < nCount; ++i ) + DrawOpaqueRenderable( ppEntities[i]->m_pRenderable, false, bShadowDepth ); + }; +#else + void DrawOpaqueRenderables_DrawBrushModels( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, bool bShadowDepth ) + { + for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity ) + DrawOpaqueRenderable( itEntity->m_pRenderable, false, bShadowDepth ); + }; +#endif + +#ifdef SWARM_DLL + void DrawOpaqueRenderables_DrawStaticProps( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bShadowDepth ) + { + if ( nCount == 0 ) + return; + + float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + render->SetColorModulation( one ); + render->SetBlend( 1.0f ); + + const int MAX_STATICS_PER_BATCH = 512; + IClientRenderable *pStatics[ MAX_STATICS_PER_BATCH ]; + RenderableInstance_t pInstances[ MAX_STATICS_PER_BATCH ]; + + int numScheduled = 0, numAvailable = MAX_STATICS_PER_BATCH; + + for( int i = 0; i < nCount; ++i ) + { + CClientRenderablesList::CEntry *itEntity = ppEntities[i]; + if ( itEntity->m_pRenderable ) + NULL; + else + continue; + + pInstances[ numScheduled ] = itEntity->m_InstanceData; + pStatics[ numScheduled ++ ] = itEntity->m_pRenderable; + if ( -- numAvailable > 0 ) + continue; // place a hint for compiler to predict more common case in the loop + + staticpropmgr->DrawStaticProps( pStatics, pInstances, numScheduled, bShadowDepth, vcollide_wireframe.GetBool() ); + numScheduled = 0; + numAvailable = MAX_STATICS_PER_BATCH; + } + + if ( numScheduled ) + staticpropmgr->DrawStaticProps( pStatics, pInstances, numScheduled, bShadowDepth, vcollide_wireframe.GetBool() ); + } +#else + void DrawOpaqueRenderables_DrawStaticProps( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, bool bShadowDepth ) + { + if ( pEntitiesEnd == pEntitiesBegin ) + return; + + float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + render->SetColorModulation( one ); + render->SetBlend( 1.0f ); + + const int MAX_STATICS_PER_BATCH = 512; + IClientRenderable *pStatics[ MAX_STATICS_PER_BATCH ]; + + int numScheduled = 0, numAvailable = MAX_STATICS_PER_BATCH; + + for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity ) + { + if ( itEntity->m_pRenderable ) + NULL; + else + continue; + + pStatics[ numScheduled ++ ] = itEntity->m_pRenderable; + if ( -- numAvailable > 0 ) + continue; // place a hint for compiler to predict more common case in the loop + + staticpropmgr->DrawStaticProps( pStatics, numScheduled, bShadowDepth, vcollide_wireframe.GetBool() ); + numScheduled = 0; + numAvailable = MAX_STATICS_PER_BATCH; + } + + if ( numScheduled ) + staticpropmgr->DrawStaticProps( pStatics, numScheduled, bShadowDepth, vcollide_wireframe.GetBool() ); + }; +#endif + +#ifdef SWARM_DLL + void DrawOpaqueRenderables_Range( int nCount, CClientRenderablesList::CEntry **ppEntities, bool bShadowDepth ) + { + for ( int i = 0; i < nCount; ++i ) + { + CClientRenderablesList::CEntry *itEntity = ppEntities[i]; + if ( itEntity->m_pRenderable ) + DrawOpaqueRenderable( itEntity->m_pRenderable, ( itEntity->m_TwoPass != 0 ), bShadowDepth ); + } + }; +#else + void DrawOpaqueRenderables_Range( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, bool bShadowDepth ) + { + for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity ) + if ( itEntity->m_pRenderable ) + DrawOpaqueRenderable( itEntity->m_pRenderable, ( itEntity->m_TwoPass != 0 ), bShadowDepth ); + }; +#endif +}; + + +class CSimpleVCallbackView : public CBaseVCallbackView +{ + DECLARE_CLASS( CSimpleVCallbackView, CBaseVCallbackView ); +public: + CSimpleVCallbackView(CViewRender *pMainView) : CBaseVCallbackView( pMainView ) {} + + struct EditorViewSettings + { + public: + bool bDrawPlayers; + bool bDrawWeapons; + bool bDrawStaticProps; + bool bDrawMisc; + bool bDrawTranslucents; + bool bDrawWater; + bool bDrawWorld; + bool bDrawParticles; + bool bDrawRopes; + bool bDrawSkybox; + bool bClipSkybox; + bool bClearColor; + bool bClearDepth; + bool bClearStencil; + bool bClearObeyStencil; + bool bFogOverride; + bool bFogEnabled; + + int iClearColorR; + int iClearColorG; + int iClearColorB; + int iClearColorA; + int iFogColorR; + int iFogColorG; + int iFogColorB; + + float flFogStart; + float flFogEnd; + float flFogDensity; + }; + + EditorViewSettings settings; + + void Setup( const CViewSetup &view, CSimpleVCallbackView::EditorViewSettings settings, + const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& info ) + { + this->settings = settings; + + BaseClass::Setup( view ); + + m_ClearFlags = (settings.bClearColor ? VIEW_CLEAR_COLOR : 0) | + (settings.bClearDepth ? VIEW_CLEAR_DEPTH : 0) | + (settings.bClearStencil ? VIEW_CLEAR_STENCIL : 0) | + (settings.bClearObeyStencil ? VIEW_CLEAR_OBEY_STENCIL : 0); + + m_DrawFlags = (settings.bDrawPlayers || settings.bDrawStaticProps || + settings.bDrawTranslucents || settings.bDrawWeapons || + settings.bDrawMisc) ? DF_DRAW_ENTITITES : 0; + + //if ( settings.bDrawWorld ) + { + if ( !info.m_bOpaqueWater ) + { + m_DrawFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER; + } + else + { + bool bViewIntersectsWater = DoesViewPlaneIntersectWater( fogInfo.m_flWaterHeight, fogInfo.m_nVisibleFogVolume ); + if( bViewIntersectsWater ) + { + // have to draw both sides if we can see both. + m_DrawFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER; + } + else if ( fogInfo.m_bEyeInFogVolume ) + { + m_DrawFlags |= DF_RENDER_UNDERWATER; + } + else + { + m_DrawFlags |= DF_RENDER_ABOVEWATER; + } + } + } + + if ( info.m_bDrawWaterSurface && settings.bDrawWater ) + { + m_DrawFlags |= DF_RENDER_WATER; + } + + if ( !fogInfo.m_bEyeInFogVolume && settings.bDrawSkybox ) + { + m_DrawFlags |= DF_DRAWSKYBOX; + } + + if ( settings.bClipSkybox ) + m_DrawFlags |= DF_CLIP_SKYBOX; + + m_pCustomVisibility = NULL; + m_fogInfo = fogInfo; + }; + + void Draw() + { + DrawSetup( 0, m_DrawFlags, 0 ); + + CMatRenderContextPtr pRenderContext( materials ); + + pRenderContext->ClearColor4ub( (unsigned char)settings.iClearColorR, + (unsigned char)settings.iClearColorG, + (unsigned char)settings.iClearColorB, + (unsigned char)settings.iClearColorA ); + + if ( settings.bFogOverride ) + { + if ( !settings.bFogEnabled ) + pRenderContext->FogMode( MATERIAL_FOG_NONE ); + else + { + pRenderContext->FogMode( MATERIAL_FOG_LINEAR ); + pRenderContext->FogColor3ub( (unsigned char)settings.iFogColorR, + (unsigned char)settings.iFogColorG, + (unsigned char)settings.iFogColorB ); + pRenderContext->FogStart( settings.flFogStart ); + pRenderContext->FogEnd( settings.flFogEnd ); + pRenderContext->FogMaxDensity( settings.flFogDensity ); + } + } + else if ( !m_fogInfo.m_bEyeInFogVolume ) + { + EnableWorldFog(); + } + else + { + m_ClearFlags |= VIEW_CLEAR_COLOR; + + SetFogVolumeState( m_fogInfo, false ); + + pRenderContext.GetFrom( materials ); + + unsigned char ucFogColor[3]; + pRenderContext->GetFogColor( ucFogColor ); + pRenderContext->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 ); + } + + pRenderContext.SafeRelease(); + + DrawExecute( 0, CurrentViewID(), 0 ); + + pRenderContext.GetFrom( materials ); + pRenderContext->ClearColor4ub( 0, 0, 0, 255 ); + + m_pMainView->DisableFog(); + }; + + virtual void CallbackInitRenderList( int viewId ) + { + BaseClass::CallbackInitRenderList( viewId ); + + if ( settings.bDrawPlayers && settings.bDrawStaticProps && + settings.bDrawTranslucents && settings.bDrawWeapons && + settings.bDrawMisc ) + return; + + for ( int i = 0; i < RENDER_GROUP_COUNT; i++ ) + { +#ifndef SWARM_DLL + const bool bStaticProp = i == 0 || i == 2 || i == 4 || i == 6; +#endif + + for ( int e = 0; e < m_pRenderablesList->m_RenderGroupCounts[i]; e++ ) + { + CClientRenderablesList::CEntry *pEntry = m_pRenderablesList->m_RenderGroups[i] + e; + + if ( !pEntry || !pEntry->m_pRenderable ) + continue; + +#ifdef SWARM_DLL + const bool bStaticProp = pEntry->m_nModelType == RENDERABLE_MODEL_STATIC_PROP; +#endif + + bool bRemove = false; + if ( bStaticProp ) + bRemove = !settings.bDrawStaticProps; + else + { + IClientUnknown *pUnknown = pEntry->m_pRenderable->GetIClientUnknown(); + + if ( !pUnknown || !pUnknown->GetBaseEntity() ) + continue; + + C_BaseEntity *pEntity = pUnknown->GetBaseEntity(); + + if ( pEntity->IsPlayer() ) + bRemove = !settings.bDrawPlayers; + else if ( dynamic_cast< CBaseCombatWeapon* >( pEntity ) != NULL ) + bRemove = !settings.bDrawWeapons; +#ifdef SWARM_DLL + else if ( pEntity->ComputeTranslucencyType() != RENDERABLE_IS_OPAQUE ) +#else + else if ( pEntry->m_pRenderable->IsTransparent() ) +#endif + bRemove = !settings.bDrawTranslucents; + else + bRemove = !settings.bDrawMisc; + } + + if ( bRemove ) + { + pEntry->m_pRenderable = NULL; +#ifndef SWARM_DLL + pEntry->m_RenderHandle = NULL; +#endif + } + } + + int eLast = -1; + for ( int e = 0; e < m_pRenderablesList->m_RenderGroupCounts[i]; e++ ) + { + CClientRenderablesList::CEntry *pEntry = m_pRenderablesList->m_RenderGroups[i] + e; + + if ( !pEntry || !pEntry->m_pRenderable +#ifndef SWARM_DLL + || !pEntry->m_RenderHandle +#endif + ) + { + for ( int e2 = e + 1; e2 < m_pRenderablesList->m_RenderGroupCounts[i]; e2++ ) + { + CClientRenderablesList::CEntry *pEntry2 = m_pRenderablesList->m_RenderGroups[i] + e2; + if ( pEntry2 && pEntry2->m_pRenderable +#ifndef SWARM_DLL + && pEntry2->m_RenderHandle +#endif + ) + { + CClientRenderablesList::CEntry tmp = *pEntry; + *pEntry = *pEntry2; + *pEntry2 = tmp; + break; + } + } + } + + if ( pEntry && pEntry->m_pRenderable +#ifndef SWARM_DLL + && pEntry->m_RenderHandle +#endif + ) + eLast = e; + } + + m_pRenderablesList->m_RenderGroupCounts[i] = eLast + 1; + } + }; + + virtual bool ShouldDrawParticles() + { + return settings.bDrawParticles; + }; + + virtual bool ShouldDrawRopes() + { + return settings.bDrawRopes; + }; + + virtual bool ShouldDrawWorld() + { + return settings.bDrawWorld; + }; + + virtual bool ShouldDrawTranslucents() + { + return settings.bDrawTranslucents; + }; + + virtual bool ShouldDrawTranslucentWorld() + { + return settings.bDrawWorld && settings.bDrawTranslucents; + }; + +private: + VisibleFogVolumeInfo_t m_fogInfo; + +}; + +#ifdef SWARM_DLL +bool UpdateRefractIfNeededByList( CViewModelRenderablesList::RenderGroups_t &list ) +{ + int nCount = list.Count(); + for( int i=0; i < nCount; ++i ) + { + IClientRenderable *pRenderable = list[i].m_pRenderable; + Assert( pRenderable ); + if ( pRenderable->GetRenderFlags() & ERENDERFLAGS_NEEDS_POWER_OF_TWO_FB ) + { + UpdateRefractTexture(); + return true; + } + } + return false; +} +void DrawRenderablesInList( CViewModelRenderablesList::RenderGroups_t &renderGroups, int flags = 0 ) +{ + CViewRender *pCView = assert_cast< CViewRender* >( view ); + Assert( pCView->GetCurrentlyDrawingEntity() == NULL ); + + ASSERT_LOCAL_PLAYER_RESOLVABLE(); +#if defined( DBGFLAG_ASSERT ) + int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT(); +#endif + Assert( pCView->GetCurrentlyDrawingEntity() == NULL ); + int nCount = renderGroups.Count(); + for( int i=0; i < nCount; ++i ) + { + IClientRenderable *pRenderable = renderGroups[i].m_pRenderable; + Assert( pRenderable ); + + // Non-view models wanting to render in view model list... + if ( pRenderable->ShouldDraw() ) + { + Assert( !IsSplitScreenSupported() || pRenderable->ShouldDrawForSplitScreenUser( nSlot ) ); + pCView->SetCurrentlyDrawingEntity( pRenderable->GetIClientUnknown()->GetBaseEntity() ); + pRenderable->DrawModel( STUDIO_RENDER | flags, renderGroups[i].m_InstanceData ); + } + } + pCView->SetCurrentlyDrawingEntity( NULL ); +} +#else +static inline bool UpdateRefractIfNeededByList( CUtlVector< IClientRenderable * > &list ) +{ + int nCount = list.Count(); + for( int i=0; i < nCount; ++i ) + { + IClientUnknown *pUnk = list[i]->GetIClientUnknown(); + Assert( pUnk ); + + IClientRenderable *pRenderable = pUnk->GetClientRenderable(); + Assert( pRenderable ); + + if ( pRenderable->UsesPowerOfTwoFrameBufferTexture() ) + { + UpdateRefractTexture(); + return true; + } + } + + return false; +} +static inline void DrawRenderablesInList( CUtlVector< IClientRenderable * > &list, int flags = 0 ) +{ + CViewRender *pCView = assert_cast< CViewRender* >( view ); + Assert( pCView->GetCurrentlyDrawingEntity() == NULL ); + + int nCount = list.Count(); + for( int i=0; i < nCount; ++i ) + { + IClientUnknown *pUnk = list[i]->GetIClientUnknown(); + Assert( pUnk ); + + IClientRenderable *pRenderable = pUnk->GetClientRenderable(); + Assert( pRenderable ); + + // Non-view models wanting to render in view model list... + if ( pRenderable->ShouldDraw() ) + { + pCView->SetCurrentlyDrawingEntity( pUnk->GetBaseEntity() ); + pRenderable->DrawModel( STUDIO_RENDER | flags ); + } + } + pCView->SetCurrentlyDrawingEntity( NULL ); +} +#endif + + +int &ShaderEditorHandler::GetViewIdForModify() +{ + Assert( m_piCurrentViewId != NULL ); + + return *m_piCurrentViewId; +} +const VisibleFogVolumeInfo_t &ShaderEditorHandler::GetFogVolumeInfo() +{ + return m_tFogVolumeInfo; +} +const WaterRenderInfo_t &ShaderEditorHandler::GetWaterRenderInfo() +{ + return m_tWaterRenderInfo; +} + +pFnVrCallback_Declare( VrCallback_General ) +{ + CViewRender *pCView = assert_cast< CViewRender* >( view ); + Assert( pCView->GetViewSetup() != NULL ); + + const CViewSetup *setup = pCView->GetViewSetup(); + + CSimpleVCallbackView::EditorViewSettings settings; + + settings.bDrawPlayers = pbOptions[0]; + settings.bDrawWeapons = pbOptions[1]; + settings.bDrawStaticProps = pbOptions[2]; + settings.bDrawMisc = pbOptions[3]; + settings.bDrawTranslucents = pbOptions[4]; + settings.bDrawWater = pbOptions[5]; + settings.bDrawWorld = pbOptions[6]; + settings.bDrawParticles = pbOptions[7]; + settings.bDrawRopes = pbOptions[8]; + settings.bDrawSkybox = pbOptions[9]; + settings.bClipSkybox = pbOptions[10]; + settings.bClearColor = pbOptions[11]; + settings.bClearDepth = pbOptions[12]; + settings.bClearStencil = pbOptions[13]; + settings.bClearObeyStencil = pbOptions[14]; + settings.bFogOverride = pbOptions[15]; + settings.bFogEnabled = pbOptions[16]; + + settings.iClearColorR = piOptions[0]; + settings.iClearColorG = piOptions[1]; + settings.iClearColorB = piOptions[2]; + settings.iClearColorA = piOptions[3]; + settings.iFogColorR = piOptions[4]; + settings.iFogColorG = piOptions[5]; + settings.iFogColorB = piOptions[6]; + + settings.flFogStart = pflOptions[0]; + settings.flFogEnd = pflOptions[1]; + settings.flFogDensity = pflOptions[2]; + + if ( settings.flFogEnd < 0 ) + settings.flFogEnd = setup->zFar; + + CRefPtr pGeneralCallbackView = new CSimpleVCallbackView( pCView ); + pGeneralCallbackView->Setup( *setup, settings, + g_ShaderEditorSystem->GetFogVolumeInfo(), g_ShaderEditorSystem->GetWaterRenderInfo() ); + pCView->AddViewToScene( pGeneralCallbackView ); +} + +pFnVrCallback_Declare( VrCallback_ViewModel ) +{ + CViewRender *pCView = assert_cast< CViewRender* >( view ); + Assert( pCView->GetViewSetup() != NULL ); + + CMatRenderContextPtr pRenderContext( materials ); + + static ConVarRef drawVM( "r_drawviewmodel" ); + + const bool bHideVM = pbOptions[0]; + const bool bFogOverride = pbOptions[5]; + const int iClearFlags = (pbOptions[1] ? VIEW_CLEAR_COLOR : 0) | + (pbOptions[2] ? VIEW_CLEAR_DEPTH : 0) | + (pbOptions[3] ? VIEW_CLEAR_STENCIL : 0) | + (pbOptions[4] ? VIEW_CLEAR_OBEY_STENCIL : 0); + + drawVM.SetValue( !bHideVM ); + + if ( bFogOverride ) + { + if ( !pbOptions[6] ) + pCView->DisableFog(); + else + { + pRenderContext->FogMode( MATERIAL_FOG_LINEAR ); + pRenderContext->FogColor3ub( (unsigned char)piOptions[4], + (unsigned char)piOptions[5], + (unsigned char)piOptions[6] ); + pRenderContext->FogStart( pflOptions[0] ); + pRenderContext->FogEnd( pflOptions[1] ); + pRenderContext->FogMaxDensity( pflOptions[2] ); + } + } + + int bbx, bby; + materials->GetBackBufferDimensions( bbx, bby ); + + // Restore the matrices + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PushMatrix(); + + ITexture *pTex = pRenderContext->GetRenderTarget(); + const CViewSetup &view = *pCView->GetViewSetup(); + CViewSetup viewModelSetup( view ); + viewModelSetup.zNear = view.zNearViewmodel; + viewModelSetup.zFar = view.zFarViewmodel; + viewModelSetup.fov = view.fovViewmodel; +#ifdef SWARM_DLL + viewModelSetup.m_flAspectRatio = engine->GetScreenAspectRatio( view.width, view.height ); +#else + viewModelSetup.m_flAspectRatio = engine->GetScreenAspectRatio(); +#endif + viewModelSetup.width = pTex ? pTex->GetActualWidth() : bbx; + viewModelSetup.height = pTex ? pTex->GetActualHeight() : bby; + + if ( iClearFlags & VIEW_CLEAR_COLOR ) + { + pRenderContext->ClearColor4ub( (unsigned char)piOptions[0], + (unsigned char)piOptions[1], + (unsigned char)piOptions[2], + (unsigned char)piOptions[3] ); + } + + render->Push3DView( viewModelSetup, iClearFlags, pTex, pCView->GetFrustum() ); + const bool bUseDepthHack = true; + + float depthmin = 0.0f; + float depthmax = 1.0f; + + // HACK HACK: Munge the depth range to prevent view model from poking into walls, etc. + // Force clipped down range + if( bUseDepthHack ) + pRenderContext->DepthRange( 0.0f, 0.1f ); + +#ifdef SWARM_DLL + CViewModelRenderablesList list; + ClientLeafSystem()->CollateViewModelRenderables( &list ); + CViewModelRenderablesList::RenderGroups_t &opaqueViewModelList = list.m_RenderGroups[ CViewModelRenderablesList::VM_GROUP_OPAQUE ]; + CViewModelRenderablesList::RenderGroups_t &translucentViewModelList = list.m_RenderGroups[ CViewModelRenderablesList::VM_GROUP_TRANSLUCENT ]; +#else + CUtlVector< IClientRenderable * > opaqueViewModelList( 32 ); + CUtlVector< IClientRenderable * > translucentViewModelList( 32 ); + ClientLeafSystem()->CollateViewModelRenderables( opaqueViewModelList, translucentViewModelList ); +#endif + + const bool bUpdateRefractForOpaque = UpdateRefractIfNeededByList( opaqueViewModelList ); + DrawRenderablesInList( opaqueViewModelList ); + + if ( !bUpdateRefractForOpaque ) + UpdateRefractIfNeededByList( translucentViewModelList ); + + DrawRenderablesInList( translucentViewModelList, STUDIO_TRANSPARENCY ); + + // Reset the depth range to the original values + if( bUseDepthHack ) + pRenderContext->DepthRange( depthmin, depthmax ); + + render->PopView( pCView->GetFrustum() ); + + // Restore the matrices + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PopMatrix(); + + if ( bFogOverride ) + pCView->DisableFog(); +} + + +void ShaderEditorHandler::RegisterViewRenderCallbacks() +{ + if ( !IsReady() ) + return; + + const char *boolNames_generalVrc[] = { + "Draw players", + "Draw weapons", + "Draw static props", + "Draw misc", + "Draw translucents", + "Draw water", + "Draw world", + "Draw particles", + "Draw ropes", + "Draw skybox (2D)", + "Clip skybox", + "Clear color", + "Clear depth", + "Clear stencil", + "Clear obey stencil", + "Fog override", + "Fog force enabled", + }; + const bool boolDefaults_generalVrc[] = { + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + false, + true, + true, + false, + false, + false, + true, + }; + const char *intNames_generalVrc[] = { + "Clear color R (0-255)", + "Clear color G (0-255)", + "Clear color B (0-255)", + "Clear color A (0-255)", + "Fog color R (0-255)", + "Fog color G (0-255)", + "Fog color B (0-255)", + }; + + const char *floatNames_generalVrc[] = { + "Fog start (units)", + "Fog end (units)", + "Fog density (0-1)", + }; + const float floatDefaults_generalVrc[] = { + 0, + 2000, + 1, + }; + + + const char *boolNames_vmVrc[] = { + "Hide default viewmodel", + "Clear color", + "Clear depth", + "Clear stencil", + "Clear obey stencil", + "Fog override", + "Fog force enabled", + }; + const bool boolDefaults_vmVrc[] = { + false, + true, + true, + false, + false, + false, + true, + }; + + Assert( ARRAYSIZE(boolNames_generalVrc) == ARRAYSIZE(boolDefaults_generalVrc) ); + Assert( ARRAYSIZE(floatNames_generalVrc) == ARRAYSIZE(floatDefaults_generalVrc) ); + Assert( ARRAYSIZE(boolNames_vmVrc) == ARRAYSIZE(boolDefaults_vmVrc) ); + + shaderEdit->RegisterViewRenderCallback( "General view", VrCallback_General, + boolNames_generalVrc, boolDefaults_generalVrc, ARRAYSIZE(boolNames_generalVrc), + intNames_generalVrc, NULL, ARRAYSIZE(intNames_generalVrc), + floatNames_generalVrc, floatDefaults_generalVrc, ARRAYSIZE(floatNames_generalVrc) ); + + shaderEdit->RegisterViewRenderCallback( "Viewmodel view", VrCallback_ViewModel, + boolNames_vmVrc, boolDefaults_vmVrc, ARRAYSIZE(boolNames_vmVrc), + intNames_generalVrc, NULL, ARRAYSIZE(intNames_generalVrc), + floatNames_generalVrc, floatDefaults_generalVrc, ARRAYSIZE(floatNames_generalVrc) ); + + shaderEdit->LockViewRenderCallbacks(); +} + +#endif \ No newline at end of file diff --git a/mp/src/game/client/ShaderEditor/ShaderEditorSystem.h b/mp/src/game/client/ShaderEditor/ShaderEditorSystem.h new file mode 100644 index 00000000..2ca319f0 --- /dev/null +++ b/mp/src/game/client/ShaderEditor/ShaderEditorSystem.h @@ -0,0 +1,59 @@ +#ifndef SHEDITSYSTEM_H +#define SHEDITSYSTEM_H + +#include "cbase.h" + +#include "datacache/imdlcache.h" + +#include "iviewrender.h" +#include "view_shared.h" +#include "viewrender.h" + + +class ShaderEditorHandler : public CAutoGameSystemPerFrame +{ +public: + ShaderEditorHandler( char const *name ); + ~ShaderEditorHandler(); + + virtual bool Init(); + virtual void Shutdown(); + + virtual void Update( float frametime ); + virtual void PreRender(); + virtual void PostRender(); + +#ifdef SOURCE_2006 + void CustomViewRender( int *viewId, const VisibleFogVolumeInfo_t &fogVolumeInfo ); +#else + void CustomViewRender( int *viewId, const VisibleFogVolumeInfo_t &fogVolumeInfo, const WaterRenderInfo_t &waterRenderInfo ); +#endif + void CustomPostRender(); + void UpdateSkymask( bool bCombineMode = false ); + + const bool IsReady(); + int &GetViewIdForModify(); + const VisibleFogVolumeInfo_t &GetFogVolumeInfo(); +#ifndef SOURCE_2006 + const WaterRenderInfo_t &GetWaterRenderInfo(); +#endif + +private: + bool m_bReady; + + void RegisterCallbacks(); + void PrepareCallbackData(); + + void RegisterViewRenderCallbacks(); + + int *m_piCurrentViewId; + VisibleFogVolumeInfo_t m_tFogVolumeInfo; +#ifndef SOURCE_2006 + WaterRenderInfo_t m_tWaterRenderInfo; +#endif +}; + +extern ShaderEditorHandler *g_ShaderEditorSystem; + + +#endif \ No newline at end of file diff --git a/mp/src/game/client/client_ff.vpc b/mp/src/game/client/client_ff.vpc index 9873a3b1..d89372f3 100644 --- a/mp/src/game/client/client_ff.vpc +++ b/mp/src/game/client/client_ff.vpc @@ -19,7 +19,7 @@ $Configuration $Compiler { $AdditionalIncludeDirectories "$BASE;.\ff;$SRCDIR\game\shared\ff;$THIRDPARTYDIR\lua;$THIRDPARTYDIR" - $PreprocessorDefinitions "$BASE;FF;FF_CLIENT_DLL" + $PreprocessorDefinitions "$BASE;FF;FF_CLIENT_DLL;SOURCE_2013" } } @@ -73,5 +73,24 @@ $Project "Client (FF)" -$File "$SRCDIR\game\shared\hl2mp\hl2mp_player_shared.cpp" -$File "$SRCDIR\game\shared\hl2mp\hl2mp_player_shared.h" } + + $Folder "Shader Editor" [$WIN32] + { + $Folder "Header Files" + { + $File "$SRCDIR\game\client\ShaderEditor\ISEdit_ModelRender.h" + $File "$SRCDIR\game\client\ShaderEditor\IVShaderEditor.h" + $File "$SRCDIR\game\client\ShaderEditor\ShaderEditorSystem.h" + $File "$SRCDIR\game\client\ShaderEditor\SEdit_ModelRender.h" + //$File "$SRCDIR\game\client\ShaderEditor\Grass\CGrassCluster.h" + } + + $Folder "Implementation Files" + { + $File "$SRCDIR\game\client\ShaderEditor\ShaderEditorSystem.cpp" + $File "$SRCDIR\game\client\ShaderEditor\SEdit_ModelRender.cpp" + //$File "$SRCDIR\game\client\ShaderEditor\Grass\CGrassCluster.cpp" + } + } } } diff --git a/mp/src/game/client/viewrender.cpp b/mp/src/game/client/viewrender.cpp index 1bc3a904..29ee7bb2 100644 --- a/mp/src/game/client/viewrender.cpp +++ b/mp/src/game/client/viewrender.cpp @@ -77,6 +77,8 @@ // Projective textures #include "C_Env_Projected_Texture.h" +#include "ShaderEditor/ShaderEditorSystem.h" // FF --> hlstriker: Added + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -1351,6 +1353,14 @@ void CViewRender::ViewDrawScene( bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxV DrawWorldAndEntities( drawSkybox, view, nClearFlags, pCustomVisibility ); + // FF --> hlstriker: Added + VisibleFogVolumeInfo_t fogVolumeInfo; + render->GetVisibleFogVolume( view.origin, &fogVolumeInfo ); + WaterRenderInfo_t info; + DetermineWaterRenderInfo( fogVolumeInfo, info ); + g_ShaderEditorSystem->CustomViewRender( &g_CurrentViewID, fogVolumeInfo, info ); + // <-- + // Disable fog for the rest of the stuff DisableFog(); @@ -1969,6 +1979,7 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT if ( ( bDrew3dSkybox = pSkyView->Setup( view, &nClearFlags, &nSkyboxVisible ) ) != false ) { AddViewToScene( pSkyView ); + g_ShaderEditorSystem->UpdateSkymask(); // FF --> hlstriker: Added } SafeRelease( pSkyView ); @@ -2026,6 +2037,8 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT // Now actually draw the viewmodel DrawViewModels( view, whatToDraw & RENDERVIEW_DRAWVIEWMODEL ); + g_ShaderEditorSystem->UpdateSkymask( bDrew3dSkybox ); // FF --> hlstriker: Added + DrawUnderwaterOverlay(); PixelVisibility_EndScene(); @@ -2063,6 +2076,8 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT pRenderContext.SafeRelease(); } + g_ShaderEditorSystem->CustomPostRender(); // FF --> hlstriker: Added + // And here are the screen-space effects if ( IsPC() )