An editor tool option that stamps a seeded cluster of irregular boulders into the map. Each boulder is a random-sided prism, with a smaller tapered cap on the first half of them.
namespace AdvancedMappingTools;
/// <summary>
/// A cluster of irregular boulders, seeded so the same seed gives the same rocks.
/// </summary>
[ToolCategory( "Vegetation" )]
[Title( "Rock Formation" ), Icon( "landscape" )]
public sealed class RockEditor( PrimitiveTool tool ) : StampEditor( tool )
{
[Property, Group( "Rock" ), Range( 4, 2048, slider: false ), WideMode]
public float Radius { get; set; } = 96.0f;
[Property, Group( "Rock" ), Range( 4, 2048, slider: false ), WideMode]
public float Height { get; set; } = 72.0f;
[Property, Group( "Rock" ), Range( 1, 24, slider: false ), WideMode]
public int Boulders { get; set; } = 5;
[Property, Group( "Rock" ), Range( 0, 1 ), WideMode, Description( "How much the boulders scatter around the centre." )]
public float Spread { get; set; } = 0.6f;
[Property, Group( "Rock" ), Range( 3, 16, slider: false ), WideMode]
public int Sides { get; set; } = 7;
protected override void Stamp( MeshKit kit, Transform frame, Random random )
{
var count = Math.Clamp( Boulders, 1, 24 );
var sides = Math.Clamp( Sides, 3, 16 );
var baseRadius = MathF.Max( Radius, 4.0f ) / (float)Math.Sqrt( count );
var height = MathF.Max( Height, 4.0f );
for ( var i = 0; i < count; i++ )
{
var angle = (float)random.NextDouble() * MathF.PI * 2.0f;
var distance = (float)random.NextDouble() * Spread * MathF.Max( Radius, 4.0f ) * 0.7f;
var center = new Vector3( MathF.Cos( angle ) * distance, MathF.Sin( angle ) * distance, 0.0f );
var size = baseRadius * (0.55f + (float)random.NextDouble() * 0.9f);
var tall = height * (0.45f + (float)random.NextDouble() * 0.8f);
var points = new List<Vector3>( sides );
for ( var s = 0; s < sides; s++ )
{
var a = s * MathF.PI * 2.0f / sides + (float)random.NextDouble() * 0.25f;
var r = size * (0.7f + (float)random.NextDouble() * 0.5f);
points.Add( center + new Vector3( MathF.Cos( a ) * r, MathF.Sin( a ) * r, 0.0f ) );
}
kit.Prism( points, 0.0f, tall );
if ( i < count / 2 )
{
var top = new List<Vector3>( sides );
foreach ( var p in points )
top.Add( center + new Vector3( (p.x - center.x) * 0.55f, (p.y - center.y) * 0.55f, 0.0f ) );
kit.Prism( top, tall * 0.8f, tall * 1.15f );
}
}
}
}