GodotComponentTest/scenes/TileWorld.cs

254 lines
7.8 KiB
C#

using Godot;
using System;
using System.Linq;
using System.Numerics;
using System.Diagnostics;
using GoDotLog;
using Vector2 = Godot.Vector2;
using Vector3 = Godot.Vector3;
public class TileWorld : Spatial
{
private enum GenerationState
{
Heightmap,
Color,
Done
}
private GenerationState CurrentGenerationState = GenerationState.Heightmap;
// signals
[Signal]
delegate void WorldGenerated();
// public members
public float Size = 100;
public float HeightScale = 10.0f;
public Image Heightmap;
public Image Colormap;
public int Seed = 0;
// private members
private HexGrid _hexGrid;
private Random _tileTypeRandom;
private Viewport _worldOffscreenViewport;
private TextureRect _worldOffscreenTextureRect;
private Viewport _heightmapOffscreenViewport;
private TextureRect _heightmapOffscreenTextureRect;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
_hexGrid = new HexGrid();
_tileTypeRandom = new Random();
_worldOffscreenViewport = (Viewport)GetNode("WorldOffscreenViewport");
Debug.Assert(_worldOffscreenViewport != null);
_worldOffscreenViewport.Size = new Vector2(Size, Size);
_worldOffscreenTextureRect = (TextureRect)GetNode("WorldOffscreenViewport/TextureRect");
Debug.Assert(_worldOffscreenTextureRect != null);
_worldOffscreenTextureRect.SetSize(new Vector2(Size, Size));
_heightmapOffscreenViewport = (Viewport)GetNode("HeightmapOffscreenViewport");
Debug.Assert(_heightmapOffscreenViewport != null);
_heightmapOffscreenViewport.Size = new Vector2(Size, Size);
_heightmapOffscreenTextureRect = (TextureRect)GetNode("HeightmapOffscreenViewport/TextureRect");
Debug.Assert(_heightmapOffscreenTextureRect != null);
_heightmapOffscreenTextureRect.SetSize(new Vector2(Size, Size));
Generate();
}
public void Generate()
{
GenerateNoiseMap();
//GenerateDebugMap();
}
private void GenerateDebugMap()
{
Colormap = new Image();
Colormap.Create((int)Size, (int)Size, false, Image.Format.Rgba8);
Heightmap = new Image();
Heightmap.Create((int)Size, (int)Size, false, Image.Format.Rf);
Heightmap.Lock();
Colormap.Lock();
foreach (int coord_x in Enumerable.Range(0, (int)Size))
{
foreach (int coord_y in Enumerable.Range(0, (int)Size))
{
// Colormap.SetPixel(coord_x, coord_y, new Color((float) coord_x, (float) coord_y, 0, 1));
float coord_to_height =
(float)coord_y / Size * 0f;
if (coord_x == 1 && coord_y == 4)
{
coord_to_height = 1;
}
SetHeightAtOffset(new Vector2(coord_x, coord_y), coord_to_height);
}
}
Colormap.Unlock();
ImageTexture imageTexture = new ImageTexture();
imageTexture.CreateFromImage(Heightmap);
imageTexture.Flags = 0;
_worldOffscreenTextureRect.Texture = imageTexture;
Colormap.CopyFrom(imageTexture.GetData());
}
private void GenerateSimpleMap()
{
Heightmap = new Image();
Heightmap.Create((int)Size, (int)Size, false, Image.Format.Rf);
Heightmap.Lock();
foreach (int coord_x in Enumerable.Range(-(int)Size / 2, (int)Size))
{
foreach (int coord_y in Enumerable.Range(-(int)Size / 2, (int)Size))
{
SetHeightAtOffset(new Vector2(coord_x, coord_y), 5f);
}
}
}
private void GenerateNoiseMap()
{
Heightmap = new Image();
Heightmap.Create((int)Size, (int)Size, false, Image.Format.Rgba8);
OpenSimplexNoise noiseGenerator = new OpenSimplexNoise();
noiseGenerator.Seed = Seed;
noiseGenerator.Octaves = 4;
noiseGenerator.Period = 20;
noiseGenerator.Persistence = 0.2f;
noiseGenerator.Lacunarity = 4;
ImageTexture heightmapTexture = new ImageTexture();
// heightmapTexture.CreateFromImage(noiseGenerator.GetImage((int)Size, (int)Size));
heightmapTexture.CreateFromImage(noiseGenerator.GetSeamlessImage((int) Size));
heightmapTexture.Flags = 0;
_heightmapOffscreenTextureRect.Texture = heightmapTexture;
Heightmap.CopyFrom(_heightmapOffscreenViewport.GetTexture().GetData());
CurrentGenerationState = GenerationState.Heightmap;
}
public override void _Process(float delta)
{
if (CurrentGenerationState == GenerationState.Heightmap)
{
CurrentGenerationState = GenerationState.Color;
ImageTexture imageTexture = new ImageTexture();
imageTexture.CreateFromImage(Heightmap);
imageTexture.Flags = 0;
_worldOffscreenTextureRect.Texture = imageTexture;
}
else if (CurrentGenerationState == GenerationState.Color)
{
Colormap = new Image();
Colormap.Create((int)Size, (int)Size, false, Image.Format.Rgba8);
Colormap.CopyFrom(_worldOffscreenViewport.GetTexture().GetData());
Heightmap.Lock();
CurrentGenerationState = GenerationState.Done;
EmitSignal("WorldGenerated");
}
}
public void ApplyHeightmap(Image heightmap)
{
foreach (int coord_x in Enumerable.Range(-(int)Size / 2, (int)Size))
{
foreach (int coord_y in Enumerable.Range(-(int)Size / 2, (int)Size))
{
Vector2 textureCoord = OffsetToTextureCoord(new Vector2(coord_x, coord_y));
float height = heightmap.GetPixel((int)textureCoord.x, (int)textureCoord.y).r;
SetHeightAtOffset(new Vector2(coord_x, coord_y), height);
}
}
}
public bool IsOffsetCoordValid(Vector2 offsetCoord)
{
return ((int)Math.Clamp(offsetCoord.x, -Size / 2, Size / 2 - 1) == (int)offsetCoord.x
&& (int)Math.Clamp(offsetCoord.y, -Size / 2, Size / 2 - 1) == (int)offsetCoord.y);
}
public HexTile3D.TileType GetTileTypeAtOffset(Vector2 offsetCoord)
{
if (!IsOffsetCoordValid(offsetCoord))
{
return HexTile3D.TileType.Undefined;
}
return HexTile3D.ValidTileTypes[_tileTypeRandom.Next(HexTile3D.ValidTileTypes.Length)];
}
public Vector2 OffsetToTextureCoord(Vector2 offsetCoord)
{
Vector2 textureCoord = (offsetCoord - Vector2.One * (Mathf.Floor(Size / 2) + 1)) % (Vector2.One * Size);
if (textureCoord[0] < 0)
{
textureCoord[0] += Size;
}
if (textureCoord[1] < 0)
{
textureCoord[1] += Size;
}
return textureCoord;
}
public void SetHeightAtOffset(Vector2 offsetCoord, float height)
{
Vector2 textureCoord = OffsetToTextureCoord(offsetCoord);
Heightmap.SetPixel((int)textureCoord.x, (int)textureCoord.y, new Color(height, 0f, 0f));
}
public float GetHeightAtOffset(Vector2 offsetCoord)
{
if (CurrentGenerationState != GenerationState.Done)
{
return 0f;
}
Vector2 textureCoord = OffsetToTextureCoord(offsetCoord);
return Heightmap.GetPixel((int)textureCoord.x, (int)(textureCoord.y)).r * HeightScale;
}
public Vector2 WorldToOffsetCoords(Vector3 worldCoord)
{
return _hexGrid.GetHexAt(new Vector2(worldCoord.x, worldCoord.z)).OffsetCoords;
}
public Vector3 GetTileWorldCenterFromOffset(Vector2 offsetCoord)
{
Vector2 tileCenter = _hexGrid.GetHexCenterFromOffset(offsetCoord);
return new Vector3(
tileCenter.x,
GetHeightAtOffset(offsetCoord),
tileCenter.y);
}
}