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 { // signals [Signal] delegate void WorldGenerated(); // public members public float Size = 5; 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 _offscreenViewport; private TextureRect _offscreenTextureRect; // Called when the node enters the scene tree for the first time. public override void _Ready() { _hexGrid = new HexGrid(); _tileTypeRandom = new Random(); _offscreenViewport = (Viewport)GetNode("OffscreenViewport"); Debug.Assert(_offscreenViewport != null); _offscreenViewport.Size = new Vector2(Size, Size); _offscreenTextureRect = (TextureRect)GetNode("OffscreenViewport/TextureRect"); Debug.Assert(_offscreenTextureRect != null); _offscreenTextureRect.SetSize(new Vector2(Size, Size)); //VisualServer.Singleton.Connect("frame_post_draw", this, nameof(GenerateNoiseColorMap)); } public void Generate() { //GenerateSimpleMap(); //GenerateNoiseMap(); GenerateNoiseColorMap(); //GenerateDebugMap(); EmitSignal("WorldGenerated"); } 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; _offscreenTextureRect.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.Rf); NoiseTexture noiseTexture = new NoiseTexture(); OpenSimplexNoise noiseGenerator = new OpenSimplexNoise(); noiseGenerator.Seed = Seed; noiseGenerator.Octaves = 4; noiseGenerator.Period = 20; noiseGenerator.Persistence = 0.1f; noiseGenerator.Lacunarity = 2; Heightmap.CopyFrom(noiseGenerator.GetImage((int)Size, (int)Size, null)); } private void GenerateNoiseColorMap() { Colormap = new Image(); Colormap.Create((int)Size, (int)Size, false, Image.Format.Rgba8); NoiseTexture noiseTexture = new NoiseTexture(); OpenSimplexNoise noiseGenerator = new OpenSimplexNoise(); noiseGenerator.Seed = Seed; noiseGenerator.Octaves = 4; noiseGenerator.Period = 20; noiseGenerator.Persistence = 0.2f; noiseGenerator.Lacunarity = 4; ImageTexture imageTexture = new ImageTexture(); Heightmap = noiseGenerator.GetSeamlessImage((int)Size); imageTexture.CreateFromImage(Heightmap); imageTexture.Flags = 0; _offscreenTextureRect.Texture = imageTexture; Colormap.CopyFrom(_offscreenViewport.GetTexture().GetData()); Heightmap.Lock(); } 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) { 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); } }