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 ); } } }