DomeFox/DomeFox/Level/LevelGenerator.cs

267 lines
6.0 KiB
C#

using Godot;
using Rokojori;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
[Tool, GlobalClass]
public partial class LevelGenerator : Action
{
[Export]
public int numWalls = -1;
[Export]
public int numRooms = 100;
[Export]
public int numRoomsEditor = 20;
[Export]
public float minRadius = 200;
[Export]
public float maxRadius = 4000;
[Export]
public Material roomCenterMaterial;
[Export]
public Material lowPolyMaterial;
[Export]
public int seed = -1;
[Export]
public float wallThickness = 2f;
[Export]
public float boundaryDensity = 10;
[Export]
public float boundarySize = 10;
[Export]
public PackedScene wall;
[Export]
public float wallSize = 1f;
[Export]
public PackedScene roomCenter;
[Export]
public RoomGeneratorEntry[] rooms;
[Export]
public TextureAttributes textureAttributes;
[Export]
public Node3D[] walls = [];
protected override void _OnTrigger()
{
numWalls = 0;
this.DestroyChildren();
var points = new List<Vector2>();
var boundaryPointSize = Circle.WithRadius( maxRadius ).circumference / boundaryDensity;
this.LogInfo( "Num Boundary Points", boundaryPointSize );
for ( int i = 0; i < boundaryPointSize; i++ )
{
var t = (float) i / ( float ) ( boundaryPointSize );
var angle = t * Mathf.Pi * 2f;
var boundaryPoint = Math2D.Circle( angle, maxRadius + boundarySize );
points.Add( boundaryPoint );
}
points.Add( Vector2.Zero );
var random = seed == -1 ? LCG.Randomized() : LCG.WithSeed( seed );
var rooms = Engine.IsEditorHint() ? numRoomsEditor : numRooms;
for ( int i = 0; i < rooms; i++ )
{
var p = random.InRectangle( Vector2.One * -maxRadius, Vector2.One * maxRadius );
if ( p.Length() < minRadius || p.Length() >= maxRadius )
{
p = random.InRectangle( Vector2.One * -maxRadius, Vector2.One * maxRadius );
}
if ( p.Length() < minRadius || p.Length() >= maxRadius )
{
p = random.InRectangle( Vector2.One * -maxRadius, Vector2.One * maxRadius );
}
if ( p.Length() < minRadius || p.Length() >= maxRadius )
{
if ( p.Length() == 0 )
{
p = Vector2.One;
}
p = p.Normalized() * random.Range( minRadius, maxRadius );
}
points.Add( p );
}
var voronoi = Voronoi2D.CreateFromCellPoints( points );
var lowMG = new MeshGeometry();
lowMG.colors = new List<Color>();
var wallIndex = 0;
var wallsList =new List<Node3D>();
voronoi.edges.ForEach(
( e )=>
{
var w = CreateWallsAtEdge( e, voronoi, lowMG, wallIndex );
wallsList.Add( w );
wallIndex ++;
}
);
walls = wallsList.ToArray();
// wallActi
var lowPolyMI = this.CreateChild<MeshInstance3D>( "LowPoly" );
lowPolyMI.Mesh = lowMG.GenerateMesh();
lowPolyMI.Mesh.SurfaceSetMaterial( 0, lowPolyMaterial );
voronoi.cells.ForEach(
( vc )=>
{
if ( vc.index < boundaryPointSize )
{
return;
}
var rg = GetRoomGenerator( random );
rg.GenerateRoom( this, vc );
// var p = voronoi.cellPoints[ vc.index ];
// var center = p.To3DXZ();
// var room = this.CreateChild<Node3D>( roomCenter, "Center" + vc.index );
// room.GlobalPosition = center;
}
);
}
RoomGenerator GetRoomGenerator( RandomEngine random )
{
var index = random.IndexFromUnnormalizedWeights( rooms.Map( r => r.probability ).ToList() );
return rooms[ index ].room;
}
public override void _Ready()
{
this.LogInfo( "Ready", this.walls.Length );
InitializeWalls();
}
async Task InitializeWalls()
{
var frames = 0;
await this.RequestNextFrame();
var index = 0;
this.ForEachDirectChild<Node3D>(
( n )=>
{
if ( ! n.Name.ToString().Contains( "Wall" ) )
{
return;
}
var setAtt = n.GetAll<SetTextureAttributeChannel>();
setAtt.ForEach( s => s.index = index );
index ++;
// this.LogInfo( "SetAtt", HierarchyName.Of( n ), setAtt.index );
}
);
}
public Node3D CreateWallsAtEdge( Voronoi2D.Edge edge, Voronoi2D v, MeshGeometry output, int wallIndex )
{
var p0 = v.boundaryPoints[ edge.start ];
var p1 = v.boundaryPoints[ edge.end ];
var center = p0.Lerp( p1, 0.5f ).To3DXZ();
var yaw = Math3D.GlobalYaw( p0.To3DXZ() - p1.To3DXZ() );
var length = ( p1 - p0 ).Length();
var numWalls = length / wallSize;
var wallObject = this.CreateChild<Node3D>( wall, "Wall " + wallIndex );
wallObject.GlobalPosition = center;
wallObject.SetGlobalYaw( yaw + Mathf.Pi * 0.5f );
wallObject.Scale = new Vector3( numWalls, 1, 1 );
var setTexturesAttributes= wallObject.GetAll<SetTextureAttributeChannel>();
this.LogInfo( "Num walls:", setTexturesAttributes.Count );
setTexturesAttributes.ForEach(
( st )=>
{
st.index = wallIndex;
st.textureAttributes = textureAttributes;
this.LogInfo( "Set Index:", st.index, HierarchyName.Of( st ) );
}
);
var lowPolyWall = MeshGeometry.BillboardQuad();
var trsf = Math3D.TRS( center, Math3D.RotateY( yaw + Mathf.Pi * 0.5f ), new Vector3( length, 250, 1 ) );
lowPolyWall.ApplyTransform( trsf );
var colorIndex = MathX.FlatIndexToMultiIndex( wallIndex, new Vector2I( 255, 255 ) );
var color = new Color( colorIndex.X / 255f, colorIndex.Y / 255f, 0 );
// this.LogInfo( wallIndex, ">>", colorIndex.X, colorIndex.Y, color );
lowPolyWall.SetColor( color );
output.Add( lowPolyWall );
this.numWalls ++;
return wallObject;
// for ( int i = 0; i < numWalls; i++ )
// {
// var t = i / ( float )(numWalls );
// var p = p0.Lerp( p1, t );
// var wallObject = this.CreateChild<Node3D>( wall );
// wallObject.GlobalPosition = p.To3DXZ();
// wallObject.SetGlobalYaw( yaw );
// }
}
}