using Godot; using System; using System.Linq; using System.Diagnostics; 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 int Size = 11; public float HeightScale = 2.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(Size); } public void Generate(int size) { GD.Print("Triggering generation for size: " + size); Size = size; _worldOffscreenViewport.Size = new Vector2(size, size); _heightmapOffscreenViewport.Size = new Vector2(size, size); GenerateNoiseMap(); // GenerateDebugMap(); } private void GenerateDebugMap() { Colormap = new Image(); Colormap.Create(Size, Size, false, Image.Format.Rgba8); Heightmap = new Image(); Heightmap.Create(Size, Size, false, Image.Format.Rf); Heightmap.Lock(); Colormap.Lock(); foreach (int coord_x in Enumerable.Range(0, Size)) { foreach (int coord_y in Enumerable.Range(0, Size)) { Colormap.SetPixel(coord_x, coord_y, new Color((float) Mathf.Min(coord_x, coord_y) / Size, (float) 0, 0, 1)); Heightmap.SetPixel(coord_x, coord_y, new Color((float) Mathf.Min(coord_x, coord_y) / Size, (float) 0, 0, 1)); } } Colormap.SetPixel(Size - 1, Size -1, new Color(1, 1, 1, 1)); Colormap.Unlock(); _currentGenerationState = GenerationState.Done;; EmitSignal("WorldGenerated"); } private void GenerateNoiseMap() { Heightmap = new Image(); Heightmap.Create(Size, 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.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(Size, Size, false, Image.Format.Rgba8); Colormap.CopyFrom(_worldOffscreenViewport.GetTexture().GetData()); Heightmap.Lock(); _currentGenerationState = GenerationState.Done; EmitSignal("WorldGenerated"); } } 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))) % (Vector2.One * Size); Vector2 mapSize = Vector2.One * Size; Vector2 textureCoord = (offsetCoord + mapSize / 2).PosMod(mapSize); 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); float heightmapHeight = Heightmap.GetPixel((int)textureCoord.x, (int)(textureCoord.y)).r * HeightScale; // heightmapHeight = Mathf.Floor(heightmapHeight); // heightmapHeight = heightmapHeight * 10) // heightmapHeight = Mathf.Clamp(heightmapHeight, -1f, 5); return heightmapHeight; } 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); } }