rj-action-library/Runtime/WorldMap/WorldMap.cs

254 lines
6.1 KiB
C#

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<WorldMapLayerInfo> layerInfos = new List<WorldMapLayerInfo>();
}
public class WorldMapLayerInfo
{
public LoadingStatus status;
public WorldMapLayer layer;
}
protected Dictionary<string,WorldMapRegionInfo> _loadedRegions = new Dictionary<string, WorldMapRegionInfo>();
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<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 );
}
}
void GetStreamRegions( Node3D streamTarget, HashSet<string> 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<WorldRegion>( 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<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 );
}
}
}