rj-action-library/Runtime/WorldMap/Layers/Grid/GridWorldMapLayer.cs

284 lines
6.9 KiB
C#

using Godot;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class GridWorldMapLayer:WorldMapLayerType
{
[Export]
public float regionSize = 250;
[Export]
public float hueScale;
public override void Update( WorldMapLayer layer, WorldMap map )
{
UpdateLayer( layer, map );
}
public enum LoadingStatus
{
Not_Loaded,
Loading,
Loaded,
Unloading,
Error
}
public class WorldMapRegionInfo
{
public float loadingTime;
public string id;
public LoadingStatus status;
public GridWorldRegion region;
public List<WorldMapLayerInfo> layerInfos = new List<WorldMapLayerInfo>();
}
public class WorldMapLayerInfo
{
public LoadingStatus status;
public WorldMapLayer layer;
}
public override void OnWorldEditorNodeUpdate( NodeUpdate update, WorldMapLayer mapLayer, Node node )
{
var position = GetPositionOfNode( node );
var regionID = WorldToRegions( position );
var parentRegion = node.FindParentThatIs<GridWorldRegion>();
if ( NodeUpdate.Deleted == update )
{
node.RemoveFromParent();
if ( parentRegion != null )
{
parentRegion.UpdateRegion();
}
return;
}
var region = mapLayer.FindDirectChild<GridWorldRegion>( r => r.regionID == regionID );
if ( region == null )
{
region = mapLayer.CreateChild<GridWorldRegion>( "Region-" + regionID.X + "-" + regionID.Y );
}
if ( region == parentRegion )
{
parentRegion.UpdateRegion();
return;
}
node.RemoveFromParent();
parentRegion.UpdateRegion();
region.AddChild( node );
region.UpdateRegion();
}
public string CreateRegionID( Vector2 regionsPosition )
{
var x = Mathf.RoundToInt( regionsPosition.X );
var y = Mathf.RoundToInt( regionsPosition.Y );
return CreateRegionID( x, y );
}
public string CreateRegionID( int x, int y)
{
return x + "," + y;
}
public Vector2I ParseID( string id )
{
var numbers = id.Split( "," );
var x = RegexUtility.ParseInt( numbers[ 0 ] );
var y = RegexUtility.ParseInt( numbers[ 1 ] );
return new Vector2I( x, y );
}
public Box2 GetXZWorldBoxOfRegion( int regionX, int regionY )
{
var boxStart = RegionsToWorld( new Vector2( regionX, regionY ) ) ;
var boxEnd = boxStart + new Vector3( regionSize, 0, regionSize );
return new Box2( Math2D.XZ( boxStart ), Math2D.XZ( boxEnd ) );
}
protected Dictionary<string,WorldMapRegionInfo> _loadedRegions = new Dictionary<string, WorldMapRegionInfo>();
protected WorldMapLayer _layer;
protected void ClearRegions()
{
Nodes.RemoveAndDeleteChildren( _layer );
_loadedRegions.Clear();
}
void UpdateLayer( WorldMapLayer layer, WorldMap map )
{
_layer = layer;
var streamTargets = map.streamers;
var regionsToStream = new HashSet<string>();
for ( int i = 0; i < streamTargets.Length; i++ )
{
GetStreamRegions( streamTargets[ i ], regionsToStream );
}
foreach ( var s in regionsToStream )
{
if ( _loadedRegions.ContainsKey( s ) )
{
continue;
}
LoadRegion( s );
}
foreach ( var s in _loadedRegions )
{
if ( regionsToStream.Contains( s.Key ) )
{
continue;
}
UnloadRegion( s.Key );
}
}
public Vector2 WorldToRegions( Vector3 worldPosition )
{
return Math2D.XZ( worldPosition / regionSize );
}
public Vector3 RegionsToWorld( Vector2 regionsPosition )
{
return Math3D.XYasXZ( regionsPosition ) * regionSize;
}
void GetStreamRegions( WorldStreamer streamer, HashSet<string> regions )
{
var streamPosition = streamer.GetStreamCenter();
var streamRadius = streamer.GetStreamRadius();
var streamMin = streamPosition - Vector3.One * streamRadius;
var streamMax = streamPosition + Vector3.One * streamRadius;
var positionMin = WorldToRegions( streamPosition - Vector3.One * streamRadius );
var positionMax = WorldToRegions( streamPosition + Vector3.One * streamRadius );
positionMin = positionMin.Floor();
positionMax = positionMax.Ceil();
// RJLog.Log( "CheckRegions", streamMin, streamMax, positionMin, positionMax );
var min = new Vector2I( (int)positionMin.X, (int)positionMin.Y );
var max = new Vector2I( (int)positionMax.X, (int)positionMax.Y );
var index = 0;
var streamPositionXZ = Math2D.XZ( streamPosition );
for ( int i = min.X; i <= max.X; i++ )
{
for ( int j = min.Y; j <= max.Y; j++ )
{
var regionID = CreateRegionID( i, j );
if ( regions.Contains( regionID ) )
{
continue;
}
var boxXZ = GetXZWorldBoxOfRegion( i, j );
var distance = boxXZ.DistanceTo( streamPositionXZ );
if ( index < 3 )
{
// RJLog.Log( i, j, boxXZ.center, distance );
index++;
}
if ( distance > streamRadius )
{
continue;
}
regions.Add( regionID );
}
}
}
void LoadRegion( string regionID )
{
var regionIndex = ParseID( regionID );
// RJLog.Log( "Loading regionID:", regionID, regionIndex.X, regionIndex.Y );
var regionInfo = new WorldMapRegionInfo();
regionInfo.id = regionID;
regionInfo.loadingTime = Time.GetTicksMsec() / 1000f;
_loadedRegions[ regionID ] = regionInfo;
var worldRegion = _layer.CreateChild<GridWorldRegion>( regionID );
worldRegion.indexX = regionIndex.X;
worldRegion.indexZ = regionIndex.Y;
var s = regionSize;
var h = s / 2f;
var hue = Noise.PerlinXZ( RegionsToWorld( regionIndex ) * hueScale );
var material = new StandardMaterial3D();
material.AlbedoColor = new HSLColor( hue * 360, 1, 0.5f );
material.Metallic = 0.2f;
material.Roughness = 0.6f;
worldRegion.GlobalPosition = RegionsToWorld( regionIndex ) + new Vector3( h, 0, h );
// var lodHM = worldRegion.CreateChild<LODHeightMapGeometry>();
// lodHM.material = material;
// lodHM.Create();
var box = worldRegion.CreateChild<CsgBox3D>( "Box" );
box.Size = new Vector3( s, 1, s );
box.Material = material;
regionInfo.region = worldRegion;
}
void UnloadRegion( string regionID )
{
// RJLog.Log( "Unloading regionID:", regionID );
var info = _loadedRegions[ regionID ];
Nodes.RemoveAndDelete( info.region );
_loadedRegions.Remove( regionID );
}
}
}