290 lines
7.4 KiB
C#
290 lines
7.4 KiB
C#
using Godot;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Godot.Collections;
|
|
|
|
public enum DebugType
|
|
{
|
|
None,
|
|
Slope,
|
|
Height,
|
|
Temperature,
|
|
Moisture
|
|
}
|
|
|
|
[Tool]
|
|
public partial class World : Node2D
|
|
{
|
|
|
|
|
|
private int _chunksX = 32;
|
|
[Export]
|
|
public int ChunkXCount
|
|
{
|
|
get => _chunksX;
|
|
set
|
|
{
|
|
_chunksX = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
public Vector2I ChunksPerAxis
|
|
{
|
|
get
|
|
{
|
|
|
|
if (HeightMapImage == null)
|
|
{
|
|
return new Vector2I(_chunksX, _chunksX);
|
|
}
|
|
|
|
Vector2I heightMapResolution = new Vector2I(HeightMapImage.GetWidth(), HeightMapImage.GetHeight());
|
|
int chunksY = (int)(heightMapResolution.Y / (heightMapResolution.X / _chunksX));
|
|
return new Vector2I(_chunksX, chunksY);
|
|
}
|
|
}
|
|
|
|
private int _tileSize = 96;
|
|
[Export]
|
|
public int TileSize
|
|
{
|
|
get => _tileSize;
|
|
set
|
|
{
|
|
_tileSize = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
[Export]
|
|
public Texture2D HeightMapTexture;
|
|
|
|
private float _rockThreshold = 0.05f;
|
|
[Export]
|
|
public float RockThreshold
|
|
{
|
|
get => _rockThreshold;
|
|
set
|
|
{
|
|
_rockThreshold = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
private float _snowHeightThreshold = 0.8f;
|
|
[Export]
|
|
public float SnowHeightThreshold
|
|
{
|
|
get => _snowHeightThreshold;
|
|
set
|
|
{
|
|
_snowHeightThreshold = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
private float _waterThreshold = 0.0f;
|
|
[Export]
|
|
public float WaterThreshold
|
|
{
|
|
get => _waterThreshold;
|
|
set
|
|
{
|
|
_waterThreshold = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
private float _desertTemperatureThreshold = 0.7f;
|
|
[Export]
|
|
public float DesertTemperatureThreshold
|
|
{
|
|
get => _desertTemperatureThreshold;
|
|
set
|
|
{
|
|
_desertTemperatureThreshold = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
private float _desertMoistureThreshold = 0.3f;
|
|
[Export]
|
|
public float DesertMoistureThreshold
|
|
{
|
|
get => _desertMoistureThreshold;
|
|
set
|
|
{
|
|
_desertMoistureThreshold = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
private float _temperatureNoiseFrequency = 0.01f;
|
|
[Export]
|
|
public float TemperatureNoiseFrequency
|
|
{
|
|
get => _temperatureNoiseFrequency;
|
|
set
|
|
{
|
|
_temperatureNoiseFrequency = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
private float _moistureNoiseFrequency = 0.01f;
|
|
[Export]
|
|
public float MoistureNoiseFrequency
|
|
{
|
|
get => _moistureNoiseFrequency;
|
|
set
|
|
{
|
|
_moistureNoiseFrequency = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
private DebugType _debugType = DebugType.None;
|
|
[Export]
|
|
public DebugType DebugType
|
|
{
|
|
get => _debugType;
|
|
set
|
|
{
|
|
_debugType = value;
|
|
LoadTiles();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ConcurrentDictionary<Vector2I, Tile> _tiles = new ConcurrentDictionary<Vector2I, Tile>();
|
|
private CancellationTokenSource _cancellationTokenSource;
|
|
|
|
public World()
|
|
{
|
|
|
|
}
|
|
|
|
public override void _Ready()
|
|
{
|
|
|
|
// If the height map image is not assigned, log an error and return
|
|
if (HeightMapTexture == null)
|
|
{
|
|
GD.PrintErr("Height map image is not assigned.");
|
|
return;
|
|
}
|
|
|
|
LoadTiles();
|
|
}
|
|
|
|
private void LoadTiles()
|
|
{
|
|
// Check if HeightMapImage is available
|
|
if (HeightMapImage == null)
|
|
{
|
|
GD.PrintErr("Cannot load tiles: HeightMapImage is null");
|
|
return;
|
|
}
|
|
|
|
// Cancel any existing generation
|
|
_cancellationTokenSource?.Cancel();
|
|
_cancellationTokenSource = new CancellationTokenSource();
|
|
var cancellationToken = _cancellationTokenSource.Token;
|
|
|
|
// Clear existing tiles
|
|
foreach (var tile in _tiles.Values)
|
|
{
|
|
tile.Visible = false;
|
|
|
|
// Delete and delete its children
|
|
foreach (Node child in tile.GetChildren())
|
|
{
|
|
child.QueueFree();
|
|
}
|
|
tile.QueueFree();
|
|
|
|
}
|
|
_tiles.Clear();
|
|
|
|
int scale = 1;
|
|
|
|
// Store local copies of parameters to avoid them changing mid-generation
|
|
int tileSize = TileSize;
|
|
int chunksX = _chunksX;
|
|
|
|
// Get the resolution of the height map image
|
|
Vector2I heightMapResolution = new Vector2I(HeightMapImage.GetWidth(), HeightMapImage.GetHeight());
|
|
|
|
// Calculate the number of chunks in the Y direction based on the aspect ratio
|
|
int chunksY = (int)(heightMapResolution.Y / (heightMapResolution.X / _chunksX));
|
|
|
|
// Loop through each chunk position and create a Tile
|
|
Task.Run(() =>
|
|
{
|
|
try
|
|
{
|
|
for (int x = 0; x < chunksX; x++)
|
|
{
|
|
if (cancellationToken.IsCancellationRequested) return;
|
|
|
|
Parallel.For(0, chunksY, new ParallelOptions { CancellationToken = cancellationToken }, y =>
|
|
{
|
|
if (cancellationToken.IsCancellationRequested) return;
|
|
|
|
Vector2I chunkId = new Vector2I(x, y);
|
|
|
|
Tile tile = new Tile(new Vector2I(tileSize, tileSize), chunkId, this);
|
|
tile.CancellationToken = cancellationToken;
|
|
tile.Position = new Vector2(chunkId.X * tileSize, chunkId.Y * tileSize) * scale;
|
|
tile.Scale = new Vector2(scale, scale);
|
|
|
|
if (!cancellationToken.IsCancellationRequested)
|
|
{
|
|
CallDeferred("add_child", tile);
|
|
_tiles[chunkId] = tile;
|
|
GD.Print("Loaded tile at chunk " + chunkId);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
GD.Print("Tile generation cancelled");
|
|
}
|
|
}, cancellationToken);
|
|
|
|
}
|
|
|
|
private Image _heightMapCache;
|
|
public Image HeightMapImage
|
|
{
|
|
get
|
|
{
|
|
// If not cached, load and cache it
|
|
if (_heightMapCache == null && HeightMapTexture != null)
|
|
{
|
|
_heightMapCache = HeightMapTexture.GetImage();
|
|
|
|
// If GetImage() returns null, try loading directly from file
|
|
if (_heightMapCache == null && HeightMapTexture.ResourcePath != "")
|
|
{
|
|
_heightMapCache = Image.LoadFromFile(HeightMapTexture.ResourcePath);
|
|
|
|
if (_heightMapCache == null)
|
|
{
|
|
GD.PushError($"Could not load heightmap from {HeightMapTexture.ResourcePath}");
|
|
}
|
|
}
|
|
}
|
|
|
|
return _heightMapCache;
|
|
}
|
|
}
|
|
}
|