using Godot; using System.Collections; using System.Collections.Generic; using System.Text.RegularExpressions; namespace Rokojori { [Tool] [GlobalClass] public partial class WorldMap:Node { [Export] public WorldMapDefinition worldMapDefinition; [Export] public Node regionsContainer; [Export] public Node3D[] streamTargets; [Export] public float streamTargetRadius = 500; [Export] public WorldMapLayerSetting[] layerSettings; [Export] public float hueScale = 0.01f; public enum LoadingStatus { Not_Loaded, Loading, Loaded, Unloading, Error } public class WorldMapRegionInfo { public float loadingTime; public string id; public LoadingStatus status; public WorldRegion region; public List layerInfos = new List(); } public class WorldMapLayerInfo { public LoadingStatus status; public WorldMapLayer layer; } protected Dictionary _loadedRegions = new Dictionary(); public override void _Process( double delta ) { StreamRegions(); } public Vector2 WorldToRegions( Vector3 worldPosition ) { return Math2D.XZ( worldPosition / worldMapDefinition.regionSize ); } public Vector3 RegionsToWorld( Vector2 regionsPosition ) { return Math3D.XYasXZ( regionsPosition ) * worldMapDefinition.regionSize; } 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( worldMapDefinition.regionSize, 0, worldMapDefinition.regionSize ); return new Box2( Math2D.XZ( boxStart ), Math2D.XZ( boxEnd ) ); } protected void ClearRegions() { Nodes.RemoveAndDeleteChildren( regionsContainer ); _loadedRegions.Clear(); } protected void StreamRegions() { 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 ); } } void GetStreamRegions( Node3D streamTarget, HashSet regions ) { var streamPosition = streamTarget.GlobalPosition; var streamMin = streamPosition - Vector3.One * streamTargetRadius; var streamMax = streamPosition + Vector3.One * streamTargetRadius; var positionMin = WorldToRegions( streamPosition - Vector3.One * streamTargetRadius ); var positionMax = WorldToRegions( streamPosition + Vector3.One * streamTargetRadius ); 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 > streamTargetRadius ) { 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 = regionsContainer.CreateChild( regionID ); worldRegion.indexX = regionIndex.X; worldRegion.indexZ = regionIndex.Y; var s = worldMapDefinition.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 ); } } }