mirror of
https://github.com/Gigaslav/HL2Overcharged.git
synced 2026-09-04 11:44:45 +03:00
1498 lines
45 KiB
C++
1498 lines
45 KiB
C++
#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"
|
|
#include "OverCharged/Tools/SharedVars.h"
|
|
|
|
#define CONSTBOXEXTENT_LIGHT 1000
|
|
#define CONSTBOXEXTENT_COL 500
|
|
|
|
ConVar gcluster_objectsPerHint("grasscluster_objects_per_hint", "8");
|
|
ConVar gcluster_debug("grasscluster_debug", "0");
|
|
ConVar gcluster_enable("grasscluster_enable", "1");
|
|
ConVar gcluster_enable_flashlight("grasscluster_enable_flashlightSupport", "1");
|
|
ConVar gcluster_enable_morph("grasscluster_enable_morph", "1");
|
|
ConVar gcluster_cullDist("grasscluster_cullDist", "4096");
|
|
|
|
ConVar gcluster_LOD_enable("grasscluster_LOD_enable", "1");
|
|
ConVar gcluster_LOD_transitionDist("grasscluster_LOD_transitionDist", "2048");
|
|
ConVar gcluster_LOD_objects_per_hint("grasscluster_LOD_objects_per_hint", "1");
|
|
|
|
ConVar gcluster_grass_height_small_min("grasscluster_grass_height_small_min", "20");
|
|
ConVar gcluster_grass_height_small_max("grasscluster_grass_height_small_max", "30");
|
|
ConVar gcluster_grass_height_med_min("grasscluster_grass_height_med_min", "30");
|
|
ConVar gcluster_grass_height_med_max("grasscluster_grass_height_med_max", "50");
|
|
ConVar gcluster_grass_height_huge_min("grasscluster_grass_height_huge_min", "50");
|
|
ConVar gcluster_grass_height_huge_max("grasscluster_grass_height_huge_max", "70");
|
|
|
|
ConVar gcluster_grass_width_small_min("grasscluster_grass_width_small_min", "25");
|
|
ConVar gcluster_grass_width_small_max("grasscluster_grass_width_small_max", "35");
|
|
ConVar gcluster_grass_width_med_min("grasscluster_grass_width_med_min", "40");
|
|
ConVar gcluster_grass_width_med_max("grasscluster_grass_width_med_max", "60");
|
|
ConVar gcluster_grass_width_huge_min("grasscluster_grass_width_huge_min", "60");
|
|
ConVar gcluster_grass_width_huge_max("grasscluster_grass_width_huge_max", "80");
|
|
|
|
ConVar gcluster_clusterMaxQuads("grasscluster_grass_clusterMaxQuads", "-1");
|
|
|
|
ConVar gcluster_grass_type_huge_oddness("grasscluster_grass_type_huge_oddness", "20");
|
|
ConVar gcluster_grass_type_small_oddness("grasscluster_grass_type_small_oddness", "2");
|
|
|
|
ConVar gcluster_grass_meadow_scale("grasscluster_grass_meadow_scale", "1");
|
|
|
|
ConVar gcluster_grass_wind_angle("grasscluster_grass_wind_angle", "70");
|
|
ConVar gcluster_grass_wind_strength("grasscluster_grass_wind_strength", "1");
|
|
|
|
ConVar gcluster_sprite_index("grasscluster_sprite_index", "0");
|
|
|
|
ConVar gcluster_grass_morph_delay("grasscluster_grass_meadow_morph_delay", "5");
|
|
ConVar gcluster_grass_morph_framelag("grasscluster_grass_meadow_morph_framelag", "0.1");
|
|
ConVar gcluster_grass_morph_speed("grasscluster_grass_meadow_morph_speed", "1");
|
|
|
|
ConVar gcluster_grass_terrain_offset_min("grasscluster_grass_terrain_offset_min", "20");
|
|
ConVar gcluster_grass_terrain_offset_exp("grasscluster_grass_terrain_offset_exp", "0.5");
|
|
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();
|
|
}
|
|
|
|
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);
|
|
}
|
|
IMaterialVar *clusterMaterial::GetTimeScale()
|
|
{
|
|
if (!GetMaterial())
|
|
return NULL;
|
|
return GetMaterial()->FindVar("$MUTABLE_08", NULL);
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
float i;
|
|
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 ((GP->newPitch() == 0.0f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.0f);
|
|
}
|
|
if ((GP->newPitch() <= 0.1f) && (GP->newPitch() > 0.0f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.05f);
|
|
}
|
|
if ((GP->newPitch() <= 0.2f) && (GP->newPitch() > 0.1f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.15f);
|
|
}
|
|
if ((GP->newPitch() <= 0.3f) && (GP->newPitch() > 0.2f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.2f);
|
|
}
|
|
if ((GP->newPitch() <= 0.4f) && (GP->newPitch() > 0.3f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.25f);
|
|
}
|
|
if ((GP->newPitch() <= 0.5f) && (GP->newPitch() > 0.4f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.3f);
|
|
}
|
|
if ((GP->newPitch() <= 0.6f) && (GP->newPitch() > 0.5f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.35f);
|
|
}
|
|
if ((GP->newPitch() <= 0.7f) && (GP->newPitch() > 0.6f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.4f);
|
|
}
|
|
if ((GP->newPitch() <= 0.8f) && (GP->newPitch() > 0.7f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.5f);
|
|
}
|
|
if ((GP->newPitch() <= 0.9f) && (GP->newPitch() > 0.8f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.6f);
|
|
}
|
|
if ((GP->newPitch() <= 1.0f) && (GP->newPitch() > 0.9f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.7f);
|
|
}
|
|
if ((GP->newPitch() <= 1.1f) && (GP->newPitch() > 1.0f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.8f);
|
|
}
|
|
if ((GP->newPitch() <= 1.2f) && (GP->newPitch() > 1.1f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(0.9f);
|
|
}
|
|
if ((GP->newPitch() <= 1.3f) && (GP->newPitch() > 1.2f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(1.05f);
|
|
}
|
|
if ((GP->newPitch() <= 1.4f) && (GP->newPitch() > 1.3f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(1.2f);
|
|
}
|
|
if ((GP->newPitch() <= 1.5f) && (GP->newPitch() > 1.4f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(1.35f);
|
|
}
|
|
if ((GP->newPitch() <= 1.6f) && (GP->newPitch() > 1.5f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(1.5f);
|
|
}
|
|
if ((GP->newPitch() <= 1.7f) && (GP->newPitch() > 1.6f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(1.7f);
|
|
}
|
|
if ((GP->newPitch() <= 1.8f) && (GP->newPitch() > 1.7f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(1.95f);
|
|
}
|
|
if ((GP->newPitch() <= 1.9f) && (GP->newPitch() > 1.8f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(2.25f);
|
|
}
|
|
if ((GP->newPitch() <= 2.0f) && (GP->newPitch() > 1.9f))
|
|
{
|
|
m_refMaterials[m_iCurrentMaterial].GetTimeScale()->SetFloatValue(2.6f);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
|
|
//DevMsg("!bShadowDepth && bSupportFlashlight \n");
|
|
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]));
|
|
//DevMsg("flashlightFrusta %i \n", flashlightFrusta.Count());
|
|
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++)
|
|
{
|
|
|
|
//DevMsg("!bShadowDepth && bSupportFlashlight %i \n", m_hClusterData.Count());
|
|
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 0
|
|
if (!bShadowDepth && bSupportFlashlight)// && flashlightFrusta.Count())
|
|
{
|
|
|
|
//DevMsg("!bShadowDepth && bSupportFlashlight && flashlightFrusta.Count() \n");
|
|
|
|
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);
|
|
}
|
|
#endif
|
|
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;
|
|
}
|
|
} |