using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Threading.Tasks;
namespace Rokojori
{
/**
Is used by the FoliageRenderer to book keep and render a layer defined by the FoliageData.
*/
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( num );
var centerDistance = new Dictionary();
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( "Collider Body " + i );
var shape3D = staticBody.CreateChild( "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 );
}
}
}