A Scene component that manages a grass rendering system. It creates and configures a GrassRenderObject, syncs various rendering and streaming parameters from editable properties, updates interaction fields and streaming each frame, and draws editor gizmos for active chunks when an editor painter mode is enabled.
using System;
using Sandbox;
using Sandbox.ui;
public sealed class SceneGrassComponent : Component
{
public static bool EditorPainterActive { get; set; }
public GrassRenderObject Renderer { get; private set; }
[Property] public GrassDensityMapResource DensityMapResource { get; set; }
[Property] public float ChunkSize { get; set; } = 256.0f;
[Property] public int ChunkResolution { get; set; } = 64;
[Property] public int RenderRadius { get; set; } = 6;
[Property] public float StreamingRadius { get; set; } = 1536.0f;
[Property] public float LodCutoff { get; set; } = 2048.0f;
[Property] public float DistanceCutoff { get; set; } = 4096.0f;
[Property] public float LodTransitionRange { get; set; } = 200.0f;
[Property] public float DistanceTransitionRange { get; set; } = 200.0f;
[Property] public float DisplacementStrength { get; set; } = 200.0f;
[Property] public float TerrainProbeTop { get; set; } = 4096.0f;
[Property] public float TerrainProbeBottom { get; set; } = -4096.0f;
[Property] public float TerrainHeightOffset { get; set; } = 0.0f;
[Property] public float GrassHeightPadding { get; set; } = 128.0f;
[Property] public float FallbackHeight { get; set; } = 0.0f;
[Property] public float InteractionStrength { get; set; } = 8.0f;
[Property] public float InteractionStampRate { get; set; } = 36.0f;
[Property] public float InteractionBendHoldDuration { get; set; } = 0.5f;
[Property] public float InteractionDecayUpdateInterval { get; set; } = 0.05f;
[Property] public float CutDuration { get; set; } = 8.0f;
protected override void OnAwake()
{
base.OnAwake();
using ( Scene.Push() )
{
Renderer = new GrassRenderObject( Scene.SceneWorld );
}
SyncRendererSettings();
}
private void SyncRendererSettings()
{
if ( Renderer == null )
return;
float streamingRadius = StreamingRadius;
if ( EditorPainterActive )
streamingRadius *= 10.0f;
Renderer.ChunkSize = ChunkSize;
Renderer.ChunkResolution = ChunkResolution;
float requiredStreamingRadius = Math.Max( streamingRadius, Math.Max( LodCutoff, DistanceCutoff ) + ChunkSize );
Renderer.RenderRadius = Math.Max( 1, MathX.CeilToInt( requiredStreamingRadius / Math.Max( ChunkSize, 0.001f ) ) );
Renderer.LodCutoff = LodCutoff;
Renderer.LodTransitionRange = LodTransitionRange;
Renderer.DistanceTransitionRange = DistanceTransitionRange;
Renderer.DistanceCutoff = DistanceCutoff;
Renderer.DisplacementStrength = DisplacementStrength;
Renderer.TerrainProbeTop = TerrainProbeTop;
Renderer.TerrainProbeBottom = TerrainProbeBottom;
Renderer.TerrainHeightOffset = TerrainHeightOffset;
Renderer.GrassHeightPadding = GrassHeightPadding;
Renderer.FallbackHeight = FallbackHeight;
Renderer.InteractionStrength = InteractionStrength;
Renderer.InteractionStampRate = InteractionStampRate;
Renderer.InteractionBendHoldDuration = InteractionBendHoldDuration;
Renderer.CutDuration = CutDuration;
Renderer.InteractionDecayUpdateInterval = InteractionDecayUpdateInterval;
Renderer.SetDensityResource( DensityMapResource );
Renderer.CullingCamera = Scene.Camera;
}
protected override void OnUpdate()
{
base.OnUpdate();
UpdateRenderer();
}
private void UpdateRenderer()
{
if ( Renderer == null )
return;
if ( Scene.Camera == null )
return;
SyncRendererSettings();
Renderer.SetDensityResource( DensityMapResource );
Renderer.CullingCamera = Scene.Camera;
Renderer.UpdateInteractionField( Scene.GetAllComponents<GrassInteractionSourceComponent>(), Time.Delta );
Vector3 camPos = Scene.Camera.WorldPosition;
Renderer.UpdateStreaming( camPos );
Renderer.ProcessPendingDestroy();
}
protected override void OnDisabled()
{
base.OnDisabled();
Renderer?.Disable();
Renderer = null;
}
protected override void DrawGizmos()
{
base.DrawGizmos();
UpdateRenderer();
if ( !EditorPainterActive )
return;
if ( Renderer == null )
return;
Gizmo.Draw.Color = Color.Yellow;
foreach ( var pair in Renderer.ActiveChunks )
{
BBox bounds = BBox.FromPositionAndSize( pair.Value.Bounds.Center, pair.Value.Bounds.Size.WithZ( 0 ) );
Gizmo.Draw.LineBBox( bounds );
}
}
}