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 layerInfos = new List(); } 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(); if ( NodeUpdate.Deleted == update ) { node.RemoveFromParent(); if ( parentRegion != null ) { parentRegion.UpdateRegion(); } return; } var region = mapLayer.FindDirectChild( r => r.regionID == regionID ); if ( region == null ) { region = mapLayer.CreateChild( "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 _loadedRegions = new Dictionary(); 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(); 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 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( 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(); // lodHM.material = material; // lodHM.Create(); var box = worldRegion.CreateChild( "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 ); } } }