From a0a3fea5981760e984ecd9356094ba8fffd516a0 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Mon, 30 Oct 2023 22:20:32 +0100 Subject: [PATCH] Chunk based world generation works. Still missing: - world population (trees, grass, rocks) - chunk saving/loading --- components/NavigationComponent.cs | 2 +- materials/HexTileTextureLookup.tres | 2 + materials/IslandColorRampShader.tres | 8 ++++ materials/IslandHeightmapFalloffShader.tres | 1 + materials/shader/HexToTexture.gdshader | 7 +-- .../shader/IslandColorRampShader.gdshader | 44 +++++++++++------- scenes/Game.cs | 15 ++++++ scenes/Game.tscn | 5 ++ scenes/World.cs | 10 ++-- scenes/WorldView.cs | 46 +++++++++++++++++++ 10 files changed, 117 insertions(+), 23 deletions(-) diff --git a/components/NavigationComponent.cs b/components/NavigationComponent.cs index b29c205..0465ca1 100644 --- a/components/NavigationComponent.cs +++ b/components/NavigationComponent.cs @@ -153,7 +153,7 @@ public class NavigationComponent : Spatial } // Ensure the last point coincides with the target position - if ((_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() < + if (_planningPathWorldNavigationPoints.Count > 0 && (_planningPathWorldNavigationPoints.Last().WorldPosition - toPositionWorld).LengthSquared() < 0.5f * 0.5f) { _planningPathWorldNavigationPoints[_planningPathWorldNavigationPoints.Count - 1].WorldPosition = toPositionWorld; diff --git a/materials/HexTileTextureLookup.tres b/materials/HexTileTextureLookup.tres index 23fdb6c..5bb5cfc 100644 --- a/materials/HexTileTextureLookup.tres +++ b/materials/HexTileTextureLookup.tres @@ -6,4 +6,6 @@ [resource] shader = ExtResource( 2 ) shader_param/TextureSize = 4 +shader_param/CoordinateOffsetU = null +shader_param/CoordinateOffsetV = null shader_param/MapAlbedoTexture = ExtResource( 1 ) diff --git a/materials/IslandColorRampShader.tres b/materials/IslandColorRampShader.tres index adcdfdd..e6b7cb0 100644 --- a/materials/IslandColorRampShader.tres +++ b/materials/IslandColorRampShader.tres @@ -4,3 +4,11 @@ [resource] shader = ExtResource( 1 ) +shader_param/DeepWaterColor = Color( 0, 0, 0.6, 1 ) +shader_param/WaterColor = Color( 0, 0, 0.7, 1 ) +shader_param/LightWaterColor = Color( 0, 0, 1, 1 ) +shader_param/SandColor = Color( 0.8, 0.8, 0.1, 1 ) +shader_param/GrassColor = Color( 0, 0.6, 0, 1 ) +shader_param/ForestColor = Color( 0, 0.4, 0, 1 ) +shader_param/RockColor = Color( 0.5, 0.5, 0.4, 1 ) +shader_param/SnowColor = Color( 1, 1, 1, 1 ) diff --git a/materials/IslandHeightmapFalloffShader.tres b/materials/IslandHeightmapFalloffShader.tres index 03cd8ab..1585702 100644 --- a/materials/IslandHeightmapFalloffShader.tres +++ b/materials/IslandHeightmapFalloffShader.tres @@ -3,4 +3,5 @@ [ext_resource path="res://materials/shader/IslandHeightmapFalloffShader.gdshader" type="Shader" id=1] [resource] +render_priority = -1 shader = ExtResource( 1 ) diff --git a/materials/shader/HexToTexture.gdshader b/materials/shader/HexToTexture.gdshader index 517277a..eb61ea2 100644 --- a/materials/shader/HexToTexture.gdshader +++ b/materials/shader/HexToTexture.gdshader @@ -3,6 +3,8 @@ render_mode specular_schlick_ggx, async_visible; uniform sampler2D MapAlbedoTexture : hint_black_albedo; uniform int TextureSize: hint_range(0, 1024, 4); +uniform int CoordinateOffsetU: hint_range(-32000, 32000); +uniform int CoordinateOffsetV: hint_range(-32000, 32000); varying vec2 map_coord; const mat2 _HexAffineInverse = mat2(vec2(1.333333, -0.6666667), vec2(0, -1.154701)); @@ -41,13 +43,12 @@ void vertex() { vec3 origin = vec4(WORLD_MATRIX * vec4(0, 0, 0, 1)).xyz; vec3 axial_coords = vec3(_HexAffineInverse * origin.xz, 0); - map_coord = axial_to_offset(axial_coords.xy); + map_coord = axial_to_offset(axial_coords.xy) - vec2(ivec2(CoordinateOffsetU, CoordinateOffsetV)); } void fragment() { float size = float(TextureSize); - vec2 texel_offset = vec2(ceil(size / 2.0)); - ivec2 texel_coord = ivec2(mod(map_coord.xy - texel_offset, vec2(size))); + ivec2 texel_coord = ivec2(mod(map_coord.xy, vec2(size))); vec4 texel_value = texelFetch(MapAlbedoTexture, texel_coord, 0); ALBEDO = texelFetch(MapAlbedoTexture, texel_coord, 0).rgb; diff --git a/materials/shader/IslandColorRampShader.gdshader b/materials/shader/IslandColorRampShader.gdshader index 00b9e71..fe73ef6 100644 --- a/materials/shader/IslandColorRampShader.gdshader +++ b/materials/shader/IslandColorRampShader.gdshader @@ -1,28 +1,38 @@ shader_type canvas_item; render_mode skip_vertex_transform; +uniform vec4 DeepWaterColor : hint_color = vec4(0, 0, 0.6, 1); +uniform vec4 WaterColor : hint_color = vec4(0, 0, 0.7, 1); +uniform vec4 LightWaterColor : hint_color = vec4(0, 0, 1, 1); +uniform vec4 SandColor : hint_color = vec4(0.8, 0.8, 0.1, 1); +uniform vec4 GrassColor : hint_color = vec4(0, 0.6, 0, 1); +uniform vec4 ForestColor : hint_color = vec4(0, 0.4, 0, 1); +uniform vec4 RockColor : hint_color = vec4(0.5, 0.5, 0.4, 1); +uniform vec4 SnowColor : hint_color = vec4(1); +uniform sampler2D TextureMask : hint_albedo; + void vertex() { VERTEX = (WORLD_MATRIX * (EXTRA_MATRIX * vec4(VERTEX, 0.0, 1.0))).xy; } vec3 color_ramp(float h) { - if (h < 0.4f) { - return vec3(0, 0, 0.6); + if (h < 0.1f) { + return DeepWaterColor.rgb; + } else if (h < 0.12f) { + return WaterColor.rgb; } else if (h < 0.45f) { - return vec3(0, 0, 0.7); - } else if (h < 0.5f) { - return vec3(0, 0, 1); - } else if (h < 0.55){ - return vec3(0.8, 0.8, 0.1); - } else if (h < 0.6){ - return vec3(0, 0.6, 0); - } else if (h < 0.78){ - return vec3(0, 0.4, 0); + return LightWaterColor.rgb; +// } else if (h < 0.25){ +// return SandColor.rgb; + } else if (h < 0.75){ + return GrassColor.rgb; +// } else if (h < 0.78){ +// return ForestColor.rgb; } else if (h < 1.0){ - return vec3(0.5, 0.5, 0.4); + return RockColor.rgb; } - return vec3(1.0); + return SnowColor.rgb; } vec3 rgbToGrayscale(vec3 color) { @@ -42,9 +52,11 @@ float borderFalloffCircle(vec2 uv) { void fragment() { vec4 texture_color = texture(TEXTURE, UV); - COLOR.rgb = vec3(abs(UV.x - 0.5) * 2.0); - //COLOR.rgb = color_ramp(texture_color.r * borderFalloffCircle(UV) * 1.8); - COLOR.rgb = color_ramp(texture_color.r); + + // COLOR.rgb = color_ramp(texture_color.r * borderFalloffCircle(UV) * 1.8); + COLOR.rgb = color_ramp(texture_color.r * texture(TextureMask, UV).r); + COLOR.a = 0.8; + // COLOR.rgb = vec3(1, 0, 0); } diff --git a/scenes/Game.cs b/scenes/Game.cs index 5d048c4..4b6026a 100644 --- a/scenes/Game.cs +++ b/scenes/Game.cs @@ -124,6 +124,7 @@ public class Game : Spatial _player.Connect("GoldCountChanged", this, nameof(OnGoldCountChanged)); _worldView.Connect("TileClicked", this, nameof(OnTileClicked)); _worldView.Connect("TileHovered", this, nameof(OnTileHovered)); + _worldView.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged)); // register entity events foreach (Node node in GetNode("Entities").GetChildren()) @@ -356,6 +357,20 @@ public class Game : Spatial _heightTextureRect.Texture = newHeightTexture; } + private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) + { + ImageTexture newWorldTexture = new ImageTexture(); + newWorldTexture.CreateFromImage(viewTileTypeImage, + (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); + + _worldTextureRect.Texture = newWorldTexture; + + _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); + _tileMaterial.SetShaderParam("TextureSize", (int)newWorldTexture.GetSize().x); + _tileMaterial.SetShaderParam("CoordinateOffsetU", (int) _worldView.WorldTextureCoordinateOffset.x); + _tileMaterial.SetShaderParam("CoordinateOffsetV", (int) _worldView.WorldTextureCoordinateOffset.y); + } + public void OnGoldCountChanged(int goldCount) { AnimationPlayer animationPlayer = _gameUi.GetNode("AnimationPlayer"); diff --git a/scenes/Game.tscn b/scenes/Game.tscn index 01d5dad..43d3a6a 100644 --- a/scenes/Game.tscn +++ b/scenes/Game.tscn @@ -10,6 +10,7 @@ [ext_resource path="res://scenes/TileWorld.tscn" type="PackedScene" id=8] [ext_resource path="res://scenes/Game.cs" type="Script" id=9] [ext_resource path="res://scenes/WorldView.cs" type="Script" id=10] +[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11] [ext_resource path="res://ui/WorldGeneratorUI.gd" type="Script" id=12] [ext_resource path="res://entities/Axe.tscn" type="PackedScene" id=14] [ext_resource path="res://systems/InteractionSystem.cs" type="Script" id=15] @@ -355,9 +356,13 @@ parameters/playback = SubResource( 26 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.79762, 0, 0 ) input_ray_pickable = false +[node name="Chest" parent="Entities" instance=ExtResource( 11 )] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3.27709, 0, 1.02593 ) + [node name="WorldView" type="Spatial" parent="."] script = ExtResource( 10 ) World = NodePath("../World") +ShowHexTiles = true [node name="World" type="Spatial" parent="."] script = ExtResource( 7 ) diff --git a/scenes/World.cs b/scenes/World.cs index 5e5b32a..3198feb 100644 --- a/scenes/World.cs +++ b/scenes/World.cs @@ -16,12 +16,15 @@ public class World : Spatial Done } public GenerationState State = GenerationState.Done; + public Vector2 CenterChunkIndex = Vector2.Zero; // referenced scenes private PackedScene _worldChunkScene = GD.Load("res://scenes/WorldChunk.tscn"); // constants public const int ChunkSize = 16; + public const int NumChunkRows = 3; + public const int NumChunkColumns = NumChunkRows; public int Seed = 0; public HexGrid HexGrid = new HexGrid(); public Spatial Chunks; @@ -44,10 +47,7 @@ public class World : Spatial // other members private Vector2 _centerPlaneCoord; - private int[] _centerChunkCoord = { 0, 0 }; - private int[] _previousCenterChunkCoord = { 0, 0 }; private Rect2 _centerChunkRect = new Rect2(); - private Random _debugColorRandom = new Random(); private Godot.Collections.Dictionary _cachedWorldChunks; private List _activeChunkIndices = new(); private List _addedChunkIndices = new(); @@ -141,6 +141,8 @@ public class World : Spatial // set new center chunk var chunkIndex = GetChunkTupleFromPlaneCoord(planeCoord); + CenterChunkIndex = new Vector2(chunkIndex.Item1, chunkIndex.Item2); + WorldChunk currentChunk = GetOrCreateWorldChunk(chunkIndex.Item1, chunkIndex.Item2, new Color(GD.Randf(), GD.Randf(), GD.Randf())); _centerChunkRect = currentChunk.PlaneRect; @@ -159,6 +161,8 @@ public class World : Spatial _activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 + 1)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 + 1)); + Debug.Assert(_activeChunkIndices.Count == NumChunkRows * NumChunkColumns); + foreach(Vector2 activeChunkIndex in _activeChunkIndices) { GetOrCreateWorldChunk((int) activeChunkIndex.x, (int) activeChunkIndex.y, new Color(GD.Randf(), GD.Randf(), GD.Randf())); diff --git a/scenes/WorldView.cs b/scenes/WorldView.cs index bf40a33..1acedca 100644 --- a/scenes/WorldView.cs +++ b/scenes/WorldView.cs @@ -1,6 +1,8 @@ using System.Linq; using Godot; using Godot.Collections; +using Vector2 = Godot.Vector2; +using Vector3 = Godot.Vector3; public class WorldView : Spatial { @@ -20,9 +22,15 @@ public class WorldView : Spatial delegate void TileClicked(HexTile3D tile3d); [Signal] delegate void TileHovered(HexTile3D tile3d); + [Signal] + delegate void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage); // other members + public Vector2 WorldTextureCoordinateOffset = Vector2.Zero; + private World _world; + private ImageTexture _viewTileTypeTexture; + private Image _viewTileTypeImage = new(); private class SceneTileChunk : Spatial { @@ -119,6 +127,42 @@ public class WorldView : Spatial return null; } + private void UpdateWorldViewTexture() + { + int worldChunkSize = global::World.ChunkSize; + int numWorldChunkRows = global::World.NumChunkRows; + int numWorldChunkColumns = global::World.NumChunkColumns; + + _viewTileTypeImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false, Image.Format.Rgba8); + + Vector2 chunkIndexSouthWest = Vector2.Inf; + Vector2 chunkIndexNorthEast = -Vector2.Inf; + foreach (SceneTileChunk chunk in _sceneTileChunks) + { + WorldChunk worldChunk = _world.GetOrCreateWorldChunk((int) chunk.ChunkIndex.x, (int)chunk.ChunkIndex.y, Colors.White); + + if (chunk.ChunkIndex.x <= chunkIndexSouthWest.x && chunk.ChunkIndex.y <= chunkIndexSouthWest.y) + { + chunkIndexSouthWest = chunk.ChunkIndex; + } else if (chunk.ChunkIndex.x >= chunkIndexNorthEast.x && chunk.ChunkIndex.y >= chunkIndexNorthEast.y) + { + chunkIndexNorthEast = chunk.ChunkIndex; + } + + _viewTileTypeImage.BlendRect( + worldChunk.TileTypeOffscreenViewport.GetTexture().GetData(), + new Rect2(Vector2.Zero, Vector2.One * worldChunkSize), + (chunk.ChunkIndex - _world.CenterChunkIndex + Vector2.One) * worldChunkSize); + } + + _viewTileTypeTexture = new ImageTexture(); + _viewTileTypeTexture.CreateFromImage(_viewTileTypeImage); + + WorldTextureCoordinateOffset = chunkIndexSouthWest * worldChunkSize; + + EmitSignal("OnWorldViewTileTypeImageChanged", _viewTileTypeImage); + } + private void HandleWorldTileChange(Array removedChunkIndices, Array addedChunkIndices) { Array removedChunks = new(); @@ -161,6 +205,8 @@ public class WorldView : Spatial _sceneTileChunks.Add(sceneTileChunk); } + UpdateWorldViewTexture(); + GD.Print("Removed Chunks " + removedChunkIndices.Count); GD.Print("Added Chunks " + addedChunkIndices.Count); GD.Print("Removed chunk count: " + removedChunks.Count);