Update project to use Godot.NET.Sdk 4.3.0 and improve terrain generation performance
This commit is contained in:
1
.idea/.idea.Frontiers/.idea/.name
generated
1
.idea/.idea.Frontiers/.idea/.name
generated
@@ -1 +0,0 @@
|
|||||||
Frontiers
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="GdSdk Master" type="GdScript">
|
|
||||||
<properties path="$PROJECT_DIR$/../Library/Caches/JetBrains/Rider2025.1/projects/.idea.frontiers.5687bf8b/sdk/GdSdk Master" version="Master" date="2024-06-01T15:14:16.000+02:00" />
|
|
||||||
<CLASSES />
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES>
|
|
||||||
<root url="file://$PROJECT_DIR$/../Library/Caches/JetBrains/Rider2025.1/projects/.idea.frontiers.5687bf8b/sdk/GdSdk Master" />
|
|
||||||
</SOURCES>
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Godot.NET.Sdk/4.4.1">
|
<Project Sdk="Godot.NET.Sdk/4.3.0">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
|
|||||||
@@ -48,12 +48,18 @@ public partial class Tile : Node2D
|
|||||||
|
|
||||||
DebugType debugType = _world.DebugType;
|
DebugType debugType = _world.DebugType;
|
||||||
|
|
||||||
// Run in thread
|
// Use limited parallelism instead of unlimited Task.Run
|
||||||
|
var parallelOptions = new ParallelOptions
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = Math.Max(1, (int)(System.Environment.ProcessorCount * 0.5)),
|
||||||
|
CancellationToken = CancellationToken
|
||||||
|
};
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (int x = 0; x < _size.X; x++)
|
Parallel.For(0, _size.X, parallelOptions, x =>
|
||||||
{
|
{
|
||||||
if (CancellationToken.IsCancellationRequested) return;
|
if (CancellationToken.IsCancellationRequested) return;
|
||||||
|
|
||||||
@@ -88,7 +94,7 @@ public partial class Tile : Node2D
|
|||||||
|
|
||||||
float neighborHeight = GetValueAtMapPosition(neighborCoords);
|
float neighborHeight = GetValueAtMapPosition(neighborCoords);
|
||||||
|
|
||||||
if (IsSeaAtMapPosition(neighborCoords, waterThreshold))
|
if (IsSeaAtMapPosition(neighborCoords))
|
||||||
{
|
{
|
||||||
bordersSea = true;
|
bordersSea = true;
|
||||||
}
|
}
|
||||||
@@ -109,14 +115,14 @@ public partial class Tile : Node2D
|
|||||||
// Grass (default)
|
// Grass (default)
|
||||||
image.SetPixel(x, y, Color.FromString("#bed58a", Colors.Purple));
|
image.SetPixel(x, y, Color.FromString("#bed58a", Colors.Purple));
|
||||||
|
|
||||||
if (IsSeaAtMapPosition(pixelOffset, waterThreshold))
|
if (IsSeaAtMapPosition(pixelOffset))
|
||||||
{
|
{
|
||||||
// Water
|
// Water
|
||||||
image.SetPixel(x, y, Color.FromString("#4380b0", Colors.Purple));
|
image.SetPixel(x, y, Color.FromString("#4380b0", Colors.Purple));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moistureValue < desertMoistureThreshold && heatValue > desertTemperatureThreshold)
|
if (isDesertAtMapPosition(pixelOffset))
|
||||||
{
|
{
|
||||||
// Desert
|
// Desert
|
||||||
image.SetPixel(x, y, Color.FromString("#edc9af", Colors.Purple));
|
image.SetPixel(x, y, Color.FromString("#edc9af", Colors.Purple));
|
||||||
@@ -129,7 +135,7 @@ public partial class Tile : Node2D
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heightValue > snowHeightThreshold)
|
if (heightValue > snowHeightThreshold && !isDesertAtMapPosition(pixelOffset))
|
||||||
{
|
{
|
||||||
// Snow
|
// Snow
|
||||||
image.SetPixel(x, y, Color.FromString("#f4f4f4", Colors.Purple));
|
image.SetPixel(x, y, Color.FromString("#f4f4f4", Colors.Purple));
|
||||||
@@ -165,7 +171,7 @@ public partial class Tile : Node2D
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (!CancellationToken.IsCancellationRequested)
|
if (!CancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -186,13 +192,8 @@ public partial class Tile : Node2D
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Terrain Query Methods
|
// Terrain Query Methods
|
||||||
private float GetValueAtMapPosition(Vector2I position)
|
private Vector2I ToHeightMapCoordinates(Vector2I position)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Clamp position to map bounds
|
|
||||||
position.X = Mathf.Clamp(position.X, 0, _world.ChunksPerAxis.X * _world.TileSize - 1);
|
|
||||||
position.Y = Mathf.Clamp(position.Y, 0, _world.ChunksPerAxis.Y * _world.TileSize - 1);
|
|
||||||
|
|
||||||
Vector2I heightMapResolution = new Vector2I(
|
Vector2I heightMapResolution = new Vector2I(
|
||||||
_world.HeightMapImage.GetWidth(),
|
_world.HeightMapImage.GetWidth(),
|
||||||
_world.HeightMapImage.GetHeight()
|
_world.HeightMapImage.GetHeight()
|
||||||
@@ -207,6 +208,19 @@ public partial class Tile : Node2D
|
|||||||
(int)(mapDelta.Y * heightMapResolution.Y)
|
(int)(mapDelta.Y * heightMapResolution.Y)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Clamp to valid range
|
||||||
|
heightMapCoords.X = Mathf.Clamp(heightMapCoords.X, 0, heightMapResolution.X - 1);
|
||||||
|
heightMapCoords.Y = Mathf.Clamp(heightMapCoords.Y, 0, heightMapResolution.Y - 1);
|
||||||
|
|
||||||
|
return heightMapCoords;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetValueAtMapPosition(Vector2I position)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
Vector2I heightMapCoords = ToHeightMapCoordinates(position);
|
||||||
|
|
||||||
Color hmColor = _world.HeightMapImage.GetPixel(heightMapCoords.X, heightMapCoords.Y);
|
Color hmColor = _world.HeightMapImage.GetPixel(heightMapCoords.X, heightMapCoords.Y);
|
||||||
|
|
||||||
return hmColor.R;
|
return hmColor.R;
|
||||||
@@ -218,10 +232,17 @@ public partial class Tile : Node2D
|
|||||||
return heightValue <= _world.WaterThreshold;
|
return heightValue <= _world.WaterThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsSeaAtMapPosition(Vector2I position, float waterThreshold)
|
private bool isDesertAtMapPosition(Vector2I position)
|
||||||
|
{
|
||||||
|
float heatValue = GetHeatAtMapPosition(position);
|
||||||
|
float moistureValue = GetMoistureAtMapPosition(position);
|
||||||
|
return moistureValue < _world.DesertMoistureThreshold && heatValue > _world.DesertTemperatureThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsSeaAtMapPosition(Vector2I position)
|
||||||
{
|
{
|
||||||
float heightValue = GetValueAtMapPosition(position);
|
float heightValue = GetValueAtMapPosition(position);
|
||||||
return heightValue <= waterThreshold;
|
return heightValue <= _world.WaterThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FastNoiseLite _heatNoise = null;
|
private FastNoiseLite _heatNoise = null;
|
||||||
@@ -233,21 +254,21 @@ public partial class Tile : Node2D
|
|||||||
_heatNoise = new FastNoiseLite();
|
_heatNoise = new FastNoiseLite();
|
||||||
_heatNoise.SetNoiseType(FastNoiseLite.NoiseTypeEnum.Perlin);
|
_heatNoise.SetNoiseType(FastNoiseLite.NoiseTypeEnum.Perlin);
|
||||||
_heatNoise.SetFrequency(1/_world.TemperatureNoiseFrequency);
|
_heatNoise.SetFrequency(1/_world.TemperatureNoiseFrequency);
|
||||||
|
_heatNoise.SetFractalType(FastNoiseLite.FractalTypeEnum.Fbm);
|
||||||
|
_heatNoise.SetFractalOctaves(12);
|
||||||
_heatNoise.SetSeed(1738);
|
_heatNoise.SetSeed(1738);
|
||||||
}
|
}
|
||||||
|
|
||||||
float heat = _heatNoise.GetNoise2D(position.X, position.Y);
|
Vector2I heightMapCoords = ToHeightMapCoordinates(position);
|
||||||
heat *= 0.2f;
|
|
||||||
|
float heat = _heatNoise.GetNoise2D(heightMapCoords.X, heightMapCoords.Y);
|
||||||
|
heat *= _world.TemperatureNoiseAmplitude;
|
||||||
|
|
||||||
// Fall off towards the poles. Use sine function for a globe effect.
|
// Fall off towards the poles. Use sine function for a globe effect.
|
||||||
float mapResolutionY = _world.ChunksPerAxis.Y * _world.TileSize;
|
float mapResolutionY = _world.HeightMapImage.GetHeight();
|
||||||
float latitudeFactor = Mathf.Sin(Mathf.Pi * position.Y / mapResolutionY);
|
float latitudeFactor = Mathf.Sin(Mathf.Pi * heightMapCoords.Y / mapResolutionY);
|
||||||
heat = latitudeFactor + heat;
|
heat = latitudeFactor + heat;
|
||||||
heat = Mathf.Clamp(
|
|
||||||
heat,
|
|
||||||
0.0f,
|
|
||||||
1.0f
|
|
||||||
);
|
|
||||||
|
|
||||||
return heat;
|
return heat;
|
||||||
}
|
}
|
||||||
@@ -261,21 +282,20 @@ public partial class Tile : Node2D
|
|||||||
_moistureNoise = new FastNoiseLite();
|
_moistureNoise = new FastNoiseLite();
|
||||||
_moistureNoise.SetNoiseType(FastNoiseLite.NoiseTypeEnum.Perlin);
|
_moistureNoise.SetNoiseType(FastNoiseLite.NoiseTypeEnum.Perlin);
|
||||||
_moistureNoise.SetFrequency(1/_world.MoistureNoiseFrequency);
|
_moistureNoise.SetFrequency(1/_world.MoistureNoiseFrequency);
|
||||||
|
_moistureNoise.SetFractalType(FastNoiseLite.FractalTypeEnum.Fbm);
|
||||||
|
_moistureNoise.SetFractalOctaves(12);
|
||||||
_moistureNoise.SetSeed(1337);
|
_moistureNoise.SetSeed(1337);
|
||||||
}
|
}
|
||||||
|
|
||||||
float moisture = _moistureNoise.GetNoise2D(position.X, position.Y);
|
Vector2I heightMapCoords = ToHeightMapCoordinates(position);
|
||||||
moisture *= 0.2f;
|
|
||||||
|
float moisture = _moistureNoise.GetNoise2D(heightMapCoords.X, heightMapCoords.Y);
|
||||||
|
moisture *= _world.MoistureNoiseAmplitude;
|
||||||
|
|
||||||
// Fall off towards the poles. Use sine function for a globe effect.
|
// Fall off towards the poles. Use sine function for a globe effect.
|
||||||
float mapResolutionY = _world.ChunksPerAxis.Y * _world.TileSize;
|
float mapResolutionY = _world.HeightMapImage.GetHeight();
|
||||||
float latitudeFactor = Mathf.Sin(Mathf.Pi * position.Y / mapResolutionY);
|
float latitudeFactor = Mathf.Sin(Mathf.Pi * heightMapCoords.Y / mapResolutionY);
|
||||||
moisture = latitudeFactor + moisture;
|
moisture = latitudeFactor + moisture;
|
||||||
moisture = Mathf.Clamp(
|
|
||||||
moisture,
|
|
||||||
0.0f,
|
|
||||||
1.0f
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return moisture;
|
return moisture;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Diagnostics;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Godot.Collections;
|
using Godot.Collections;
|
||||||
|
using Environment = Godot.Environment;
|
||||||
|
|
||||||
public enum DebugType
|
public enum DebugType
|
||||||
{
|
{
|
||||||
@@ -148,6 +149,30 @@ public partial class World : Node2D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float _TemperatureNoiseAmplitude = 0.1f;
|
||||||
|
[Export]
|
||||||
|
public float TemperatureNoiseAmplitude
|
||||||
|
{
|
||||||
|
get => _TemperatureNoiseAmplitude;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_TemperatureNoiseAmplitude = value;
|
||||||
|
LoadTiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float _MoistureNoiseAmplitude = 0.1f;
|
||||||
|
[Export]
|
||||||
|
public float MoistureNoiseAmplitude
|
||||||
|
{
|
||||||
|
get => _MoistureNoiseAmplitude;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_MoistureNoiseAmplitude = value;
|
||||||
|
LoadTiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private DebugType _debugType = DebugType.None;
|
private DebugType _debugType = DebugType.None;
|
||||||
[Export]
|
[Export]
|
||||||
public DebugType DebugType
|
public DebugType DebugType
|
||||||
@@ -162,8 +187,10 @@ public partial class World : Node2D
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ConcurrentDictionary<Vector2I, Tile> _tiles = new ConcurrentDictionary<Vector2I, Tile>();
|
ConcurrentDictionary<Vector2I, Tile> _tiles = new ConcurrentDictionary<Vector2I, Tile>();
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private static readonly int MaxDegreeOfParallelism = Math.Max(1, (int)(System.Environment.ProcessorCount * 0.5));
|
||||||
|
|
||||||
public World()
|
public World()
|
||||||
{
|
{
|
||||||
@@ -233,7 +260,7 @@ public partial class World : Node2D
|
|||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested) return;
|
if (cancellationToken.IsCancellationRequested) return;
|
||||||
|
|
||||||
Parallel.For(0, chunksY, new ParallelOptions { CancellationToken = cancellationToken }, y =>
|
for (int y = 0; y < chunksY; y++)
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested) return;
|
if (cancellationToken.IsCancellationRequested) return;
|
||||||
|
|
||||||
@@ -248,14 +275,14 @@ public partial class World : Node2D
|
|||||||
{
|
{
|
||||||
CallDeferred("add_child", tile);
|
CallDeferred("add_child", tile);
|
||||||
_tiles[chunkId] = tile;
|
_tiles[chunkId] = tile;
|
||||||
GD.Print("Loaded tile at chunk " + chunkId);
|
// GD.Print("Loaded tile at chunk " + chunkId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
GD.Print("Tile generation cancelled");
|
// GD.Print("Tile generation cancelled");
|
||||||
}
|
}
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
|
|
||||||
|
|||||||
10
main.tscn
10
main.tscn
@@ -1,7 +1,7 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://c10nqwr7qp0ai"]
|
[gd_scene load_steps=4 format=3 uid="uid://c10nqwr7qp0ai"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://btqabtn0awg6k" path="res://Source/World.cs" id="1_0xm2m"]
|
[ext_resource type="Script" path="res://Source/World.cs" id="1_0xm2m"]
|
||||||
[ext_resource type="Script" uid="uid://ceahximwi24jm" path="res://Source/CameraController.cs" id="3_camera"]
|
[ext_resource type="Script" path="res://Source/CameraController.cs" id="3_camera"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dteqog3a5k8qx" path="res://assets/World_Elevation_Map_8_bit_(World_Height_map)_(alterative_version).png" id="3_h2yge"]
|
[ext_resource type="Texture2D" uid="uid://dteqog3a5k8qx" path="res://assets/World_Elevation_Map_8_bit_(World_Height_map)_(alterative_version).png" id="3_h2yge"]
|
||||||
|
|
||||||
[node name="Main" type="Node2D"]
|
[node name="Main" type="Node2D"]
|
||||||
@@ -17,5 +17,7 @@ HeightMapTexture = ExtResource("3_h2yge")
|
|||||||
RockThreshold = 0.04
|
RockThreshold = 0.04
|
||||||
SnowHeightThreshold = 0.32
|
SnowHeightThreshold = 0.32
|
||||||
WaterThreshold = 0.24
|
WaterThreshold = 0.24
|
||||||
DesertTemperatureThreshold = 0.82
|
DesertTemperatureThreshold = 0.84
|
||||||
DesertMoistureThreshold = 0.985
|
DesertMoistureThreshold = 0.96
|
||||||
|
TemperatureNoiseFrequency = 1500.0
|
||||||
|
MoistureNoiseFrequency = 1500.0
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ config_version=5
|
|||||||
|
|
||||||
config/name="Frontiers"
|
config/name="Frontiers"
|
||||||
run/main_scene="uid://c10nqwr7qp0ai"
|
run/main_scene="uid://c10nqwr7qp0ai"
|
||||||
config/features=PackedStringArray("4.4", "C#", "Forward Plus")
|
config/features=PackedStringArray("4.3", "C#", "Forward Plus")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
[dotnet]
|
[dotnet]
|
||||||
|
|||||||
Reference in New Issue
Block a user