254 lines
6.1 KiB
C#
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 );
|
|
}
|
|
|
|
|
|
}
|
|
} |