505 lines
17 KiB
C#
505 lines
17 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Godot;
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
namespace Rokojori
|
|
{
|
|
/** <summary for="class FoliageRenderLayer">
|
|
|
|
<title>
|
|
Is used by the FoliageRenderer to book keep and render a layer defined by the FoliageData.
|
|
|
|
</title>
|
|
|
|
<description>
|
|
|
|
|
|
</description>
|
|
|
|
</summary>
|
|
*/
|
|
|
|
public class FoliageRenderLayer
|
|
{
|
|
public FoliageRenderer renderer;
|
|
public FoliageData data;
|
|
public int subLayerIndex = -1;
|
|
public GpuParticles3D gpuParticles3D;
|
|
public GPUFoliageShaderMaterial gpuFoliageShaderMaterial;
|
|
public Node3D collidersContainer;
|
|
|
|
float _lastCellSize = -1;
|
|
float _lastMaxVisibility = -1;
|
|
|
|
Vector2I lastCameraPosition;
|
|
|
|
|
|
public void UpdateColliders()
|
|
{
|
|
if ( ! data.collidersEnabled )
|
|
{
|
|
return;
|
|
}
|
|
|
|
var cameraPosition = gpuFoliageShaderMaterial.cameraPosition.GetCached();
|
|
var camXZ = cameraPosition.XZ();
|
|
var floored = (Vector2I) Math2D.SnapFloored( camXZ, data.colliderCellSize * Vector2.One );
|
|
|
|
if ( floored == lastCameraPosition && ! data.updateCollidersAlways )
|
|
{
|
|
return;
|
|
}
|
|
|
|
lastCameraPosition = floored;
|
|
|
|
// collidersContainer.LogInfo( "Updating Colliders:", lastCameraPosition );
|
|
|
|
var side = numParticlesPerSide();
|
|
|
|
var num = side * side;
|
|
var list = new List<Transform3D>( num );
|
|
var centerDistance = new Dictionary<Transform3D,float>();
|
|
|
|
var numInvalid = 0;
|
|
|
|
for ( int i = 0; i < num; i ++ )
|
|
{
|
|
var trsf = ComputeTransform( i );
|
|
|
|
if ( ! trsf.IsValid() )
|
|
{
|
|
if ( numInvalid == 0 )
|
|
{
|
|
RJLog.Log( trsf );
|
|
}
|
|
|
|
numInvalid ++;
|
|
|
|
|
|
continue;
|
|
}
|
|
|
|
list.Add( trsf );
|
|
|
|
var distance = ( trsf.Origin.XZ() - camXZ ).Length();
|
|
centerDistance[ trsf ] = distance;
|
|
}
|
|
|
|
list.Sort(
|
|
( a, b )=>
|
|
{
|
|
var dA = centerDistance[ a ];
|
|
var dB = centerDistance[ b ];
|
|
|
|
return Mathf.Sign( dA - dB );
|
|
}
|
|
);
|
|
|
|
if ( numInvalid > 0 )
|
|
{
|
|
RJLog.Log( "Invalud:", numInvalid );
|
|
}
|
|
|
|
CreateCollidersPool();
|
|
|
|
for ( int i = 0; i < data.numMaxColliders; i++ )
|
|
{
|
|
var child = (StaticBody3D) collidersContainer.GetChild( i );
|
|
|
|
NodeState.SetEnabledState( child, i < list.Count );
|
|
|
|
if ( i < list.Count )
|
|
{
|
|
child.GlobalTransform = list[ i ];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
void CreateCollidersPool()
|
|
{
|
|
if ( data.numMaxColliders == collidersContainer.GetChildCount() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
collidersContainer.DestroyChildren();
|
|
|
|
for ( int i = 0; i < data.numMaxColliders; i++ )
|
|
{
|
|
var staticBody = collidersContainer.CreateChild<StaticBody3D>( "Collider Body " + i );
|
|
var shape3D = staticBody.CreateChild<CollisionShape3D>( "Collision Shape " + i );
|
|
shape3D.Shape = data.colliderShape;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
public static FoliageRenderLayer Create( FoliageRenderer renderer, FoliageData data, int subIndex = -1 )
|
|
{
|
|
var rl = new FoliageRenderLayer();
|
|
rl.renderer = renderer;
|
|
rl.data = data;
|
|
rl.subLayerIndex = subIndex;
|
|
|
|
|
|
return rl;
|
|
}
|
|
|
|
bool _isEnabled = false;
|
|
|
|
public void Update( double delta )
|
|
{
|
|
if ( gpuFoliageShaderMaterial == null || data == null )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( data.enabled != _isEnabled)
|
|
{
|
|
_isEnabled = data.enabled;
|
|
gpuParticles3D.SetEnabledState( _isEnabled );
|
|
return;
|
|
}
|
|
|
|
if ( ! _isEnabled )
|
|
{
|
|
return;
|
|
}
|
|
|
|
gpuFoliageShaderMaterial.cameraPosition.SetCached( renderer.GetAssignedCamera().GlobalPosition );
|
|
|
|
if ( ! data.updateSettings )
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateProcessMaterial();
|
|
|
|
|
|
|
|
data.materialOverride?.UpdateFoliageOverideMaterial( this );
|
|
|
|
var material = data.materialOverride != null ?
|
|
data.materialOverride.GetOverrideMaterial( this ) :
|
|
gpuParticles3D?.DrawPass1?.SurfaceGetMaterial( 0 );
|
|
|
|
if ( material == null )
|
|
{
|
|
return;
|
|
}
|
|
|
|
data.materialOverride?.translucencySettings?.ApplyTranslucency( material, this );
|
|
|
|
UpdateColliders();
|
|
|
|
// if ( data.overwriteAlphaScissorToDepthPrepass && gpuParticles3D.DrawPass1.SurfaceGetMaterial( 0 ) is StandardMaterial3D standardDrawMaterial )
|
|
// {
|
|
// if ( BaseMaterial3D.TransparencyEnum.AlphaScissor == standardDrawMaterial.Transparency )
|
|
// {
|
|
// var overrideMaterial = (StandardMaterial3D) standardDrawMaterial.Duplicate();
|
|
// overrideMaterial.Transparency = BaseMaterial3D.TransparencyEnum.AlphaDepthPrePass;
|
|
// gpuParticles3D.MaterialOverride = overrideMaterial;
|
|
// overrideMaterial.RenderPriority = data.renderPriority;
|
|
// }
|
|
|
|
// }
|
|
|
|
// if ( gpuParticles3D.DrawPass1.SurfaceGetMaterial( 0 ) is ShaderMaterial drawMaterial )
|
|
// {
|
|
// drawMaterial.RenderPriority = data.renderPriority;
|
|
|
|
// drawMaterial.SetShaderParameter( "obstacle1", renderer.GetObstacleData( 0 ) );
|
|
// drawMaterial.SetShaderParameter( "obstacle2", renderer.GetObstacleData( 1 ) );
|
|
// drawMaterial.SetShaderParameter( "obstacle3", renderer.GetObstacleData( 2 ) );
|
|
// drawMaterial.SetShaderParameter( "obstacle4", renderer.GetObstacleData( 3 ) );
|
|
|
|
// // RJLog.Log( drawMaterial, renderer.GetObstacleData( 0 ) );
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
public int numParticlesPerSide()
|
|
{
|
|
var cellSize = FoliageQualitySettings.GetCellSize( renderer.quality, renderer.qualitySettingsAll, data.qualitySettings, data.GetCellSize( subLayerIndex ) );
|
|
var visibilityRange = FoliageQualitySettings.GetVisibilityRange( renderer.quality, renderer.qualitySettingsAll, data.qualitySettings, data.GetVisibilityRange( subLayerIndex ) );
|
|
|
|
|
|
return Mathf.CeilToInt( visibilityRange / cellSize ) * 2;
|
|
}
|
|
|
|
void UpdateProcessMaterial()
|
|
{
|
|
var cellSize = FoliageQualitySettings.GetCellSize( renderer.quality, renderer.qualitySettingsAll, data.qualitySettings, data.GetCellSize( subLayerIndex ) );
|
|
var visibilityRange = FoliageQualitySettings.GetVisibilityRange( renderer.quality, renderer.qualitySettingsAll, data.qualitySettings, data.GetVisibilityRange( subLayerIndex ) );
|
|
|
|
var sizeInt = Mathf.CeilToInt( visibilityRange / cellSize ) * 2;
|
|
|
|
if ( _lastCellSize != cellSize || _lastMaxVisibility != visibilityRange )
|
|
{
|
|
_lastCellSize = cellSize;
|
|
_lastMaxVisibility = visibilityRange;
|
|
|
|
gpuFoliageShaderMaterial.cellSize.SetCached( cellSize );
|
|
|
|
gpuParticles3D.Amount = sizeInt * sizeInt;
|
|
gpuFoliageShaderMaterial.width.SetCached( sizeInt );
|
|
gpuFoliageShaderMaterial.height.SetCached( sizeInt );
|
|
|
|
}
|
|
|
|
var hideStart = visibilityRange - ( visibilityRange * data.GetVisibilityFadeRelative( subLayerIndex ) + data.GetVisibilityFadeAbsolute( subLayerIndex ) );
|
|
gpuFoliageShaderMaterial.hideMax.SetCached( visibilityRange );
|
|
gpuFoliageShaderMaterial.hideStart.SetCached( hideStart );
|
|
gpuFoliageShaderMaterial.hideOffset.SetCached( data.GetVisibilityFadeHidingOffset( subLayerIndex ) );
|
|
|
|
gpuFoliageShaderMaterial.mapCenter.SetCached( renderer.globalMapSizeXZ );
|
|
gpuFoliageShaderMaterial.mapSize.SetCached( renderer.globalMapSizeXZ );
|
|
|
|
gpuFoliageShaderMaterial.heightMap.SetCached( renderer.heightMap );
|
|
gpuFoliageShaderMaterial.minHeight.SetCached( renderer.minHeight );
|
|
gpuFoliageShaderMaterial.maxHeight.SetCached( renderer.maxHeight );
|
|
|
|
gpuFoliageShaderMaterial.coverageMap.SetCached( renderer.coverageMap );
|
|
|
|
|
|
gpuFoliageShaderMaterial.positionVariance.SetCached( renderer.noise );
|
|
gpuFoliageShaderMaterial.rotationVariance.SetCached( renderer.noise );
|
|
gpuFoliageShaderMaterial.scaleVariance.SetCached( renderer.noise );
|
|
gpuFoliageShaderMaterial.occupancyVariance.SetCached( renderer.noise );
|
|
|
|
gpuFoliageShaderMaterial.maxPositionOffset.SetCached( data.positionVarianceAbsoluteOffset + data.positionVarianceCellSizeRelativeOffset * cellSize );
|
|
gpuFoliageShaderMaterial.positionOffset.SetCached( data.GetPositionOffset( subLayerIndex ) );
|
|
gpuFoliageShaderMaterial.positionUvScale.SetCached( Vector2.One * data.positionVarianceScale );
|
|
gpuFoliageShaderMaterial.positionUvOffset.SetCached( Vector2.One * data.positionVarianceOffset );
|
|
|
|
gpuFoliageShaderMaterial.minRotation.SetCached( data.rotationMin );
|
|
gpuFoliageShaderMaterial.maxRotation.SetCached( data.rotationMax );
|
|
gpuFoliageShaderMaterial.rotationUvScale.SetCached( Vector2.One * data.rotationVarianceScale );
|
|
gpuFoliageShaderMaterial.rotationUvOffset.SetCached( Vector2.One * data.rotationVarianceOffset );
|
|
|
|
gpuFoliageShaderMaterial.minScale.SetCached( data.scaleVarianceMinScale );
|
|
gpuFoliageShaderMaterial.maxScale.SetCached( data.scaleVarianceMaxScale );
|
|
|
|
gpuFoliageShaderMaterial.uniScaleMin.SetCached( data.uniScaleMin );
|
|
gpuFoliageShaderMaterial.uniScaleMax.SetCached( data.uniScaleMax );
|
|
gpuFoliageShaderMaterial.scaleVarianceContrastScale.SetCached( data.uniScaleVarianceContrastScale );
|
|
gpuFoliageShaderMaterial.scaleVarianceContrastCenter.SetCached( data.uniScaleVarianceContrastCenter );
|
|
// gpuFoliageShaderMaterial.uniScaleMax.Set( data.uniScaleVarianceOffset );
|
|
|
|
gpuFoliageShaderMaterial.uniScaleUvOffset.SetCached( data.uniScaleVarianceOffset );
|
|
|
|
gpuFoliageShaderMaterial.scaleUvScale.SetCached( Vector2.One * data.scaleVarianceScale );
|
|
gpuFoliageShaderMaterial.scaleUvOffset.SetCached( Vector2.One * data.scaleVarianceOffset );
|
|
|
|
gpuFoliageShaderMaterial.occupancyAmount.SetCached( data.occupancyVarianceAmount );
|
|
gpuFoliageShaderMaterial.occupancyPower.SetCached( data.occupancyVariancePower );
|
|
gpuFoliageShaderMaterial.occupancyTreshold.SetCached( data.occupancyTreshold );
|
|
|
|
gpuFoliageShaderMaterial.occupancyHideOffset.SetCached( data.occupancyHideOffset );
|
|
gpuFoliageShaderMaterial.occupancyHideScale.SetCached( data.occupancyHideScale );
|
|
|
|
gpuFoliageShaderMaterial.occupancyUvScale.SetCached( Vector2.One * data.occupancyVarianceScale );
|
|
gpuFoliageShaderMaterial.occupancyUvOffset.SetCached( Vector2.One * data.occupancyVarianceOffset );
|
|
|
|
if ( data.GetSort( subLayerIndex ) )
|
|
{
|
|
gpuParticles3D.DrawOrder = GpuParticles3D.DrawOrderEnum.ViewDepth;
|
|
}
|
|
}
|
|
|
|
Vector2 IndexToPosition( int index, int width, int height )
|
|
{
|
|
int x = index % width;
|
|
int y = index / height;
|
|
|
|
return new Vector2( x, y );
|
|
}
|
|
|
|
Vector2 rotate_v2( Vector2 uv, float angle )
|
|
{
|
|
float s = Mathf.Sin( angle );
|
|
float c = Mathf.Cos( angle );
|
|
|
|
|
|
float x = uv.X;
|
|
float y = uv.Y;
|
|
|
|
uv.Y = c * x - s * y;
|
|
uv.Y = s * x + c * y;
|
|
|
|
return uv;
|
|
}
|
|
|
|
Vector2 tilingOffset( Vector2 uv, Vector2 scale, Vector2 offset )
|
|
{
|
|
return uv * scale + offset;
|
|
}
|
|
|
|
|
|
public Vector4 textureLod( Texture2D texture2D, Vector2 uv, int lod )
|
|
{
|
|
if ( texture2D == null )
|
|
{
|
|
return Vector4.One;
|
|
}
|
|
|
|
var image = texture2D.GetImage();
|
|
var color = image.SampleNearest( uv );
|
|
|
|
return color.ToVector4();
|
|
// return color.LinearToSRGB().ToVector4();
|
|
}
|
|
|
|
public Transform3D ComputeTransform( int INDEX )
|
|
{
|
|
|
|
// gpuFoliageShaderMaterial.(\w+).+;
|
|
// var $1 = m.$1.GetCached();
|
|
var m = gpuFoliageShaderMaterial;
|
|
|
|
var yaw = m.yaw.GetCached();
|
|
var width = m.width.GetCached();
|
|
var height = m.height.GetCached();
|
|
var cellSize = m.cellSize.GetCached();
|
|
var cameraPosition = m.cameraPosition.GetCached();
|
|
|
|
var heightOffset = m.heightOffset.GetCached();
|
|
|
|
var hideStart = m.hideStart.GetCached();
|
|
var hideMax = m.hideMax.GetCached();
|
|
var hideOffset = m.hideOffset.GetCached();
|
|
|
|
var mapSize = m.mapSize.GetCached();
|
|
var mapCenter = m.mapCenter.GetCached();
|
|
|
|
var positionUVScale = m.positionUvScale.GetCached();
|
|
var positionUVOffset = m.positionUvOffset.GetCached();
|
|
var maxPositionOffset = m.maxPositionOffset.GetCached();
|
|
|
|
var positionVariance = m.positionVariance.Get();
|
|
var positionOffset = m.positionOffset.GetCached();
|
|
|
|
var minRotation = m.minRotation.GetCached();
|
|
var maxRotation = m.maxRotation.GetCached();
|
|
var rotationUVScale = m.rotationUvScale.GetCached();
|
|
var rotationUVOffset = m.rotationUvOffset.GetCached();
|
|
|
|
var rotationVariance = m.rotationVariance.Get();
|
|
|
|
var scaleVariance = m.scaleVariance.Get();
|
|
|
|
var minScale = m.minScale.GetCached();
|
|
var maxScale = m.maxScale.GetCached();
|
|
|
|
var uniScaleMin = m.uniScaleMin.GetCached();
|
|
var uniScaleMax = m.uniScaleMax.GetCached();
|
|
var scaleVarianceContrastScale = m.scaleVarianceContrastScale.GetCached();
|
|
var scaleVarianceContrastCenter = m.scaleVarianceContrastCenter.GetCached();
|
|
|
|
var uniScaleUVOffset = m.uniScaleUvOffset.GetCached();
|
|
|
|
var scaleUVScale = m.scaleUvScale.GetCached();
|
|
var scaleUVOffset = m.scaleUvOffset.GetCached();
|
|
|
|
var occupancyAmount = m.occupancyAmount.GetCached();
|
|
var occupancyPower = m.occupancyPower.GetCached();
|
|
var occupancyTreshold = m.occupancyTreshold.GetCached();
|
|
|
|
var occupancyHideOffset = m.occupancyHideOffset.GetCached();
|
|
var occupancyHideScale = m.occupancyHideScale.GetCached();
|
|
|
|
var occupancyUVScale = m.occupancyUvScale.GetCached();
|
|
var occupancyUVOffset = m.occupancyUvOffset.GetCached();
|
|
|
|
var heightMap = renderer.heightMap;
|
|
var occupancyVariance = renderer.noise;
|
|
var coverageMap = renderer.coverageMap;
|
|
|
|
var minHeight = renderer.minHeight;
|
|
var maxHeight = renderer.maxHeight;
|
|
|
|
var discardTreshold = 0.01f;
|
|
var discardOffset = 1000000f;
|
|
|
|
Vector2 position = IndexToPosition( INDEX, width, height );
|
|
float rotation = Mathf.Round( 4.0f * yaw / 360.0f ) * Mathf.Pi / 2.0f;
|
|
|
|
Vector3 snappedCameraPosition = new Vector3(
|
|
Mathf.Floor( cameraPosition.X / cellSize ) * cellSize,
|
|
0,
|
|
Mathf.Floor( cameraPosition.Z/ cellSize ) * cellSize
|
|
);
|
|
|
|
Vector2 origin = new Vector2( width/2.0f, height/2.0f );
|
|
position -= origin;
|
|
|
|
position = rotate_v2( position, rotation ) * cellSize;
|
|
|
|
|
|
Vector3 position3D = new Vector3( position.X, heightOffset, position.Y ) + snappedCameraPosition;
|
|
|
|
float d = ( position3D - cameraPosition ).Length();
|
|
float yOffset = MathX.RemapClamped( d, hideStart, hideMax, 0, hideOffset );
|
|
position3D.Y += yOffset;
|
|
|
|
|
|
Vector2 mapOffset = mapCenter - mapSize/2.0f;
|
|
Vector2 uv = ( new Vector2( position3D.X, position3D.Z ) - mapOffset ) / mapSize;
|
|
|
|
|
|
|
|
Vector3 offset = (textureLod( positionVariance, tilingOffset( uv, positionUVScale, positionUVOffset ), 0 ).rgb() - new Vector3(0.5f,0.5f,0.5f) ) * maxPositionOffset;
|
|
position3D += offset + positionOffset;
|
|
Vector3 rotSampled = textureLod( rotationVariance, tilingOffset( uv, rotationUVScale, rotationUVOffset ), 0 ).rgb();
|
|
Vector3 rot = ( rotSampled * ( maxRotation - minRotation ) + minRotation ) * Mathf.Pi * 2.0f;
|
|
// rot.Y = round( 4.0 * rot.Y / PI * 2.0 ) / 4.0 * PI * 2.0;
|
|
Vector3 sca = textureLod( scaleVariance, tilingOffset( uv, scaleUVScale, scaleUVOffset ), 0 ).rgb();
|
|
sca = ( sca - Vector3.One * scaleVarianceContrastCenter ) * scaleVarianceContrastScale + Vector3.One * scaleVarianceContrastCenter;
|
|
sca = Math3D.Clamp( sca, Vector3.Zero, Vector3.One );
|
|
sca = sca * ( maxScale - minScale ) + minScale;
|
|
|
|
float uniScale = textureLod( scaleVariance, tilingOffset( uv + uniScaleUVOffset, scaleUVScale, scaleUVOffset ), 0 ).r();
|
|
uniScale = ( uniScale - scaleVarianceContrastCenter ) * scaleVarianceContrastScale + scaleVarianceContrastCenter;
|
|
uniScale = Mathf.Clamp( uniScale, 0.0f, 1.0f );
|
|
uniScale = uniScale * ( uniScaleMax - uniScaleMin ) + uniScaleMin;
|
|
|
|
sca *= uniScale;
|
|
Vector2 uv2 = ( new Vector2( position3D.X, position3D.Z ) - mapOffset ) / mapSize;
|
|
float sampledHeight = textureLod( heightMap, uv2, 0 ).r();
|
|
float occ = textureLod( occupancyVariance, tilingOffset( uv2, occupancyUVScale, occupancyUVOffset ), 0 ).r();
|
|
float sampledCoverage = textureLod( coverageMap, uv2, 0 ).r();
|
|
|
|
occ = Mathf.Clamp( ( occ - occupancyTreshold ) * occupancyPower + occupancyTreshold, 0.0f, 1.0f );
|
|
occ = 1.0f - occ;
|
|
|
|
occ = Mathf.Min( occ, 1.0f - sampledCoverage );
|
|
|
|
sca *= Mathf.Lerp( 1.0f, occupancyHideScale , occ * occupancyAmount );
|
|
|
|
float combinedOcc = occ * occupancyAmount; ;
|
|
position3D.Y += combinedOcc * occupancyHideOffset;
|
|
position3D.Y += Mathf.Lerp( minHeight, maxHeight, sampledHeight );
|
|
|
|
if ( combinedOcc >= ( 1.0f - MathX.Clamp01( discardTreshold ) ) )
|
|
{
|
|
position3D.Y += discardOffset;
|
|
}
|
|
|
|
position3D += data.colliderOffset;
|
|
|
|
return Math3D.TRS( position3D, rot, sca );
|
|
}
|
|
}
|
|
} |