146 lines
3.2 KiB
C#
146 lines
3.2 KiB
C#
![]() |
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using Godot;
|
||
|
using System;
|
||
|
|
||
|
|
||
|
|
||
|
namespace Rokojori
|
||
|
{
|
||
|
[Tool]
|
||
|
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Scatterer.svg") ]
|
||
|
public partial class BillboardTree:Node3D
|
||
|
{
|
||
|
float _startHeight = 2;
|
||
|
[Export]
|
||
|
public float startHeight
|
||
|
{
|
||
|
get => _startHeight;
|
||
|
set { _startHeight = value; }
|
||
|
}
|
||
|
|
||
|
float _endHeight = 7;
|
||
|
[Export]
|
||
|
public float endHeight
|
||
|
{
|
||
|
get => _endHeight;
|
||
|
set { _endHeight = value; }
|
||
|
}
|
||
|
|
||
|
[Export]
|
||
|
public Curve radiusShape;
|
||
|
|
||
|
[Export]
|
||
|
public MeshInstance3D leavesMesh;
|
||
|
|
||
|
[Export]
|
||
|
public Material leavesMaterial;
|
||
|
|
||
|
[Export]
|
||
|
public int numRows = 5;
|
||
|
|
||
|
[Export]
|
||
|
public Curve rowDensity = MathX.Curve( 0.2f, 0.8f );
|
||
|
|
||
|
[Export]
|
||
|
public Curve leavesSize = MathX.Curve( 0.2f, 0.8f );
|
||
|
|
||
|
[Export]
|
||
|
public Curve leavesRotation = MathX.Curve( 0f, 360f );
|
||
|
|
||
|
[Export]
|
||
|
public int seed = 2000;
|
||
|
|
||
|
|
||
|
MeshGeometry mg;
|
||
|
RandomEngine random;
|
||
|
|
||
|
|
||
|
[Export]
|
||
|
public bool update = false;
|
||
|
|
||
|
[Export]
|
||
|
public bool updateAlways = false;
|
||
|
|
||
|
|
||
|
public override void _Process( double d )
|
||
|
{
|
||
|
if ( ! ( updateAlways || update ) )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
update = false;
|
||
|
|
||
|
Generate();
|
||
|
}
|
||
|
|
||
|
void Generate()
|
||
|
{
|
||
|
if ( leavesMesh == null )
|
||
|
{
|
||
|
leavesMesh = this.CreateChild<MeshInstance3D>( "Leaves" );
|
||
|
}
|
||
|
|
||
|
mg = new MeshGeometry();
|
||
|
random = LCG.WithSeed( seed );
|
||
|
|
||
|
|
||
|
for ( int i = 0; i < numRows; i++ )
|
||
|
{
|
||
|
CreateRow( i );
|
||
|
}
|
||
|
|
||
|
|
||
|
leavesMesh.Mesh = mg.GenerateMesh();
|
||
|
|
||
|
Materials.Set( leavesMesh, leavesMaterial );
|
||
|
}
|
||
|
|
||
|
void CreateRow( int rowIndex )
|
||
|
{
|
||
|
|
||
|
var rowNormalized = rowIndex / (float) ( numRows - 1 );
|
||
|
var rowRadius = radiusShape.Sample( rowNormalized );
|
||
|
var rowDensityValue = random.Sample( rowDensity );
|
||
|
var numLeaves = Mathf.Ceil( rowRadius / rowDensityValue );
|
||
|
|
||
|
var rowAngleOffset = random.Range( 0, 360f );
|
||
|
|
||
|
//this.LogInfo( "index:", rowIndex, "leaves:", numLeaves );
|
||
|
|
||
|
for ( int i = 0; i < numLeaves; i++ )
|
||
|
{
|
||
|
var size = random.Sample( leavesSize );
|
||
|
var leave = MeshGeometry.BillboardQuad( size );
|
||
|
|
||
|
var angle = 360f * i / ( (float) numLeaves ) + rowAngleOffset;
|
||
|
|
||
|
|
||
|
|
||
|
var position = Math3D.OnCircleXZ( MathX.DegreesToRadians * angle ) * rowRadius;
|
||
|
position.Y = Mathf.Lerp( startHeight, endHeight, rowNormalized );
|
||
|
|
||
|
var rotation = random.Sample( leavesRotation );
|
||
|
//this.LogInfo( "index:", rowIndex, "leaves:", numLeaves, i, "size/angle", size, "/", angle, "pos:", position.X, position.Z );
|
||
|
|
||
|
leave.ApplyTransform( position, Math3D.RotateZ( MathX.DegreesToRadians * rotation ), Vector3.One );
|
||
|
|
||
|
var normal = position - new Vector3( 0, ( startHeight + endHeight ) / 2f, 0);
|
||
|
normal = normal.Normalized();
|
||
|
|
||
|
this.LogInfo( "index:", rowIndex, "leaves:", numLeaves, i, normal );
|
||
|
|
||
|
mg.Add( leave );
|
||
|
mg.NormalsLookAt( normal, 1 );
|
||
|
|
||
|
var clone = leave.Clone();
|
||
|
clone.FlipNormalDirection();
|
||
|
clone.NormalsLookAt( normal, 1 );
|
||
|
|
||
|
mg.Add( clone );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|