Refactored map generation and masking.

World now has textures for height and tile types.
WorldChunkRefactoring
Martin Felis 2023-11-01 16:02:39 +01:00
parent a1f02a8820
commit 36be4bc4c8
6 changed files with 464 additions and 403 deletions

View File

@ -1,55 +1,55 @@
using Godot;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using Godot;
using Array = Godot.Collections.Array;
public class Game : Spatial public class Game : Spatial
{ {
private ImageTexture _blackWhitePatternTexture;
private Camera _camera;
private Vector3 _cameraOffset;
private Label _centerLabel;
private HexCell _currentTile;
// ui elements // ui elements
private Label _framesPerSecondLabel; private Label _framesPerSecondLabel;
private Label _centerLabel;
private Label _tileOffsetLabel;
private Label _numTilesLabel;
private Label _mouseWorldLabel;
private Label _mouseTileOffsetLabel;
private Label _mouseTileCubeLabel;
private Label _numCoordsAddedLabel;
private Label _numCoordsRemovedLabel;
private TextureRect _worldTextureRect;
private TextureRect _heightTextureRect;
private Button _generateWorldButton;
private Control _gameUi; private Control _gameUi;
private Button _generateWorldButton;
private Label _goldCountLabel; private Label _goldCountLabel;
private TextureRect _heightTextureRect;
// scene nodes
private Spatial _tileHighlight;
private Spatial _mouseTileHighlight;
private StreamContainer _streamContainer;
private Area _streamContainerArea;
private Spatial _streamContainerActiveTiles;
private Player _player;
private TileWorld _tileWorld;
private Camera _camera;
private World _world;
private WorldView _worldView;
// Resources
private PackedScene _tileHighlightScene;
private ShaderMaterial _tileMaterial;
// other members // other members
private HexGrid _hexGrid; private HexGrid _hexGrid;
private HexCell _lastTile;
private HexCell _currentTile;
private Vector3 _cameraOffset;
private ImageTexture _blackWhitePatternTexture;
private InteractionSystem _interactionSystem; private InteractionSystem _interactionSystem;
private HexCell _lastTile;
private Label _mouseTileCubeLabel;
private Spatial _mouseTileHighlight;
private Label _mouseTileOffsetLabel;
private Label _mouseWorldLabel;
private Label _numCoordsAddedLabel;
private Label _numCoordsRemovedLabel;
private Label _numTilesLabel;
private Player _player;
private StreamContainer _streamContainer;
private Spatial _streamContainerActiveTiles;
private Area _streamContainerArea;
// scene nodes
private Spatial _tileHighlight;
// Resources
private PackedScene _tileHighlightScene;
private TileInstanceManager _tileInstanceManager;
private ShaderMaterial _tileMaterial;
private Label _tileOffsetLabel;
private TileWorld _tileWorld;
private World _world;
private TextureRect _worldTextureRect;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
{ {
// debugStatsContainer // debugStatsContainer
Container debugStatsContainer = (Container)FindNode("DebugStatsContainer"); var debugStatsContainer = (Container)FindNode("DebugStatsContainer");
_framesPerSecondLabel = debugStatsContainer.GetNode<Label>("fps_label"); _framesPerSecondLabel = debugStatsContainer.GetNode<Label>("fps_label");
_centerLabel = debugStatsContainer.GetNode<Label>("center_label"); _centerLabel = debugStatsContainer.GetNode<Label>("center_label");
@ -62,7 +62,7 @@ public class Game : Spatial
_numCoordsRemovedLabel = debugStatsContainer.GetNode<Label>("num_coords_removed_label"); _numCoordsRemovedLabel = debugStatsContainer.GetNode<Label>("num_coords_removed_label");
// UI elements // UI elements
Container worldGeneratorContainer = (Container)FindNode("WorldGeneratorContainer"); var worldGeneratorContainer = (Container)FindNode("WorldGeneratorContainer");
_worldTextureRect = worldGeneratorContainer.GetNode<TextureRect>("WorldTextureRect"); _worldTextureRect = worldGeneratorContainer.GetNode<TextureRect>("WorldTextureRect");
_heightTextureRect = worldGeneratorContainer.GetNode<TextureRect>("HeightTextureRect"); _heightTextureRect = worldGeneratorContainer.GetNode<TextureRect>("HeightTextureRect");
_generateWorldButton = worldGeneratorContainer.GetNode<Button>("WorldGenerateButton"); _generateWorldButton = worldGeneratorContainer.GetNode<Button>("WorldGenerateButton");
@ -84,10 +84,10 @@ public class Game : Spatial
_cameraOffset = _camera.GlobalTranslation - _player.GlobalTranslation; _cameraOffset = _camera.GlobalTranslation - _player.GlobalTranslation;
_world = (World)FindNode("World"); _world = (World)FindNode("World");
_worldView = (WorldView)FindNode("WorldView"); _tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
// populate UI values // populate UI values
Slider generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider"); var generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider");
generatorWorldSizeSlider.Value = _tileWorld.Size; generatorWorldSizeSlider.Value = _tileWorld.Size;
Debug.Assert(_tileWorld != null); Debug.Assert(_tileWorld != null);
@ -98,7 +98,7 @@ public class Game : Spatial
Debug.Assert(_tileMaterial != null); Debug.Assert(_tileMaterial != null);
_blackWhitePatternTexture = new ImageTexture(); _blackWhitePatternTexture = new ImageTexture();
Image image = new Image(); var image = new Image();
image.Load("assets/4x4checker.png"); image.Load("assets/4x4checker.png");
_blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); _blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
@ -122,26 +122,20 @@ public class Game : Spatial
_player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem, _player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem,
nameof(_interactionSystem.OnStartInteraction)); nameof(_interactionSystem.OnStartInteraction));
_player.Connect("GoldCountChanged", this, nameof(OnGoldCountChanged)); _player.Connect("GoldCountChanged", this, nameof(OnGoldCountChanged));
_worldView.Connect("TileClicked", this, nameof(OnTileClicked)); _tileInstanceManager.Connect("TileClicked", this, nameof(OnTileClicked));
_worldView.Connect("TileHovered", this, nameof(OnTileHovered)); _tileInstanceManager.Connect("TileHovered", this, nameof(OnTileHovered));
_worldView.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged)); _world.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged));
_world.Connect("OnHeightmapImageChanged", this, nameof(OnHeightmapImageChanged));
// register entity events // register entity events
foreach (Node node in GetNode("Entities").GetChildren()) foreach (Node node in GetNode("Entities").GetChildren())
{
if (node.HasSignal("EntityClicked")) if (node.HasSignal("EntityClicked"))
{
node.Connect("EntityClicked", this, nameof(OnEntityClicked)); node.Connect("EntityClicked", this, nameof(OnEntityClicked));
}
}
// perform dependency injection // perform dependency injection
//_streamContainer.SetWorld(_tileWorld);Clicked //_streamContainer.SetWorld(_tileWorld);Clicked
WorldInfoComponent worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo"); var worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
if (worldInfoComponent != null) if (worldInfoComponent != null) worldInfoComponent.SetWorld(_tileWorld);
{
worldInfoComponent.SetWorld(_tileWorld);
}
_tileWorld.Generate(_tileWorld.Size); _tileWorld.Generate(_tileWorld.Size);
UpdateCurrentTile(); UpdateCurrentTile();
@ -151,23 +145,17 @@ public class Game : Spatial
public override void _Input(InputEvent inputEvent) public override void _Input(InputEvent inputEvent)
{ {
if (inputEvent.IsAction("Forward")) if (inputEvent.IsAction("Forward")) GD.Print("Forward");
{
GD.Print("Forward");
}
if (inputEvent.IsAction("Back")) if (inputEvent.IsAction("Back")) GD.Print("Back");
{
GD.Print("Back");
}
} }
public void UpdateCurrentTile() public void UpdateCurrentTile()
{ {
// cast a ray from the camera to center // cast a ray from the camera to center
Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f); var cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f);
Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f); var cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f);
Vector3 cameraDir = cameraNormal - cameraPosition; var cameraDir = cameraNormal - cameraPosition;
Vector3 centerCoord; Vector3 centerCoord;
@ -197,8 +185,8 @@ public class Game : Spatial
UpdateCurrentTile(); UpdateCurrentTile();
Transform tileHighlightTransform = Transform.Identity; var tileHighlightTransform = Transform.Identity;
Vector2 currentTileCenter = _hexGrid.GetHexCenter(_currentTile); var currentTileCenter = _hexGrid.GetHexCenter(_currentTile);
tileHighlightTransform.origin.x = currentTileCenter.x; tileHighlightTransform.origin.x = currentTileCenter.x;
tileHighlightTransform.origin.z = currentTileCenter.y; tileHighlightTransform.origin.z = currentTileCenter.y;
_tileHighlight.Transform = tileHighlightTransform; _tileHighlight.Transform = tileHighlightTransform;
@ -212,7 +200,7 @@ public class Game : Spatial
_numCoordsRemovedLabel.Text = _streamContainer.RemovedCoords.Count.ToString(); _numCoordsRemovedLabel.Text = _streamContainer.RemovedCoords.Count.ToString();
} }
Transform cameraTransform = _camera.Transform; var cameraTransform = _camera.Transform;
cameraTransform.origin = _player.GlobalTranslation + _cameraOffset; cameraTransform.origin = _player.GlobalTranslation + _cameraOffset;
_camera.Transform = cameraTransform; _camera.Transform = cameraTransform;
} }
@ -221,7 +209,7 @@ public class Game : Spatial
public void OnGenerateButton() public void OnGenerateButton()
{ {
GD.Print("Generating"); GD.Print("Generating");
Slider worldSizeSlider = (Slider)FindNode("WorldSizeSlider"); var worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
if (worldSizeSlider == null) if (worldSizeSlider == null)
{ {
GD.PrintErr("Could not find WorldSizeSlider!"); GD.PrintErr("Could not find WorldSizeSlider!");
@ -236,8 +224,8 @@ public class Game : Spatial
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex) int shapeIndex)
{ {
HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z)); var cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z));
Transform highlightTransform = Transform.Identity; var highlightTransform = Transform.Identity;
_mouseWorldLabel.Text = position.ToString("F3"); _mouseWorldLabel.Text = position.ToString("F3");
_mouseTileOffsetLabel.Text = cellAtCursor.OffsetCoords.ToString("N"); _mouseTileOffsetLabel.Text = cellAtCursor.OffsetCoords.ToString("N");
@ -245,23 +233,15 @@ public class Game : Spatial
_mouseTileHighlight.Transform = highlightTransform; _mouseTileHighlight.Transform = highlightTransform;
if (inputEvent is InputEventMouseButton && ((InputEventMouseButton)inputEvent).Pressed) if (inputEvent is InputEventMouseButton && ((InputEventMouseButton)inputEvent).Pressed)
{
_streamContainer.EmitSignal("TileClicked", _streamContainer.GetTile3dAt(cellAtCursor.OffsetCoords)); _streamContainer.EmitSignal("TileClicked", _streamContainer.GetTile3dAt(cellAtCursor.OffsetCoords));
}
} }
public void OnTileClicked(HexTile3D tile) public void OnTileClicked(HexTile3D tile)
{ {
if (_player == null) if (_player == null) return;
{
return;
}
if (_player.InteractionComponent != null) if (_player.InteractionComponent != null) _player.InteractionComponent.EmitSignal("InteractionEnd");
{
_player.InteractionComponent.EmitSignal("InteractionEnd");
}
_player.TaskQueueComponent.Reset(); _player.TaskQueueComponent.Reset();
_player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask( _player.TaskQueueComponent.Queue.Enqueue(new TaskQueueComponent.NavigationTask(
@ -270,7 +250,7 @@ public class Game : Spatial
public void OnTileHovered(HexTile3D tile) public void OnTileHovered(HexTile3D tile)
{ {
Transform highlightTransform = tile.GlobalTransform; var highlightTransform = tile.GlobalTransform;
_mouseTileHighlight.Transform = highlightTransform; _mouseTileHighlight.Transform = highlightTransform;
_mouseWorldLabel.Text = highlightTransform.origin.ToString("F3"); _mouseWorldLabel.Text = highlightTransform.origin.ToString("F3");
_mouseTileOffsetLabel.Text = tile.OffsetCoords.ToString("N"); _mouseTileOffsetLabel.Text = tile.OffsetCoords.ToString("N");
@ -283,7 +263,7 @@ public class Game : Spatial
{ {
GD.Print("Clicked on entity at " + entity.GlobalTranslation); GD.Print("Clicked on entity at " + entity.GlobalTranslation);
Spatial mountPoint = (Spatial)entity.FindNode("MountPoint"); var mountPoint = (Spatial)entity.FindNode("MountPoint");
if (mountPoint != null) if (mountPoint != null)
{ {
_player.TaskQueueComponent.Reset(); _player.TaskQueueComponent.Reset();
@ -296,8 +276,8 @@ public class Game : Spatial
public void ResetGameState() public void ResetGameState()
{ {
Transform playerStartTransform = Transform.Identity; var playerStartTransform = Transform.Identity;
float height = _tileWorld.GetHeightAtOffset(new Vector2(0, 0)); var height = _tileWorld.GetHeightAtOffset(new Vector2(0, 0));
playerStartTransform.origin.y = height; playerStartTransform.origin.y = height;
_player.Transform = playerStartTransform; _player.Transform = playerStartTransform;
_player.TaskQueueComponent.Reset(); _player.TaskQueueComponent.Reset();
@ -308,10 +288,10 @@ public class Game : Spatial
foreach (Spatial entity in GetNode("Entities").GetChildren()) foreach (Spatial entity in GetNode("Entities").GetChildren())
{ {
Transform entityTransform = entity.Transform; var entityTransform = entity.Transform;
Vector2 entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z); var entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z);
Vector2 entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords; var entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
float entityHeight = _tileWorld.GetHeightAtOffset(entityOffsetCoordinates); var entityHeight = _tileWorld.GetHeightAtOffset(entityOffsetCoordinates);
entityTransform.origin.y = entityHeight; entityTransform.origin.y = entityHeight;
entity.Transform = entityTransform; entity.Transform = entityTransform;
} }
@ -332,48 +312,53 @@ public class Game : Spatial
// Connect all signals of the generated world // Connect all signals of the generated world
foreach (Node node in _tileWorld.Entities.GetChildren()) foreach (Node node in _tileWorld.Entities.GetChildren())
{
if (node.HasSignal("EntityClicked")) if (node.HasSignal("EntityClicked"))
{
node.Connect("EntityClicked", this, nameof(OnEntityClicked)); node.Connect("EntityClicked", this, nameof(OnEntityClicked));
}
}
} }
private void UpdateWorldTextures() private void UpdateWorldTextures()
{ {
GD.Print("Updating World textures"); GD.Print("Updating World textures");
ImageTexture newWorldTexture = new ImageTexture(); var newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(_tileWorld.ColormapImage, newWorldTexture.CreateFromImage(_tileWorld.ColormapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_worldTextureRect.Texture = newWorldTexture; _worldTextureRect.Texture = newWorldTexture;
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
_tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.ColormapImage.GetSize().x); _tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.ColormapImage.GetSize().x);
ImageTexture newHeightTexture = new ImageTexture(); var newHeightTexture = new ImageTexture();
newHeightTexture.CreateFromImage(_tileWorld.HeightmapImage, newHeightTexture.CreateFromImage(_tileWorld.HeightmapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_heightTextureRect.Texture = newHeightTexture; _heightTextureRect.Texture = newHeightTexture;
} }
private void OnHeightmapImageChanged(Image heightmapImage)
{
var newHeightmapTexture = new ImageTexture();
newHeightmapTexture.CreateFromImage(heightmapImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_heightTextureRect.Texture = newHeightmapTexture;
}
private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage) private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage)
{ {
ImageTexture newWorldTexture = new ImageTexture(); var newWorldTexture = new ImageTexture();
newWorldTexture.CreateFromImage(viewTileTypeImage, newWorldTexture.CreateFromImage(viewTileTypeImage,
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat)); (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
_worldTextureRect.Texture = newWorldTexture; _worldTextureRect.Texture = newWorldTexture;
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture); _tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
_tileMaterial.SetShaderParam("TextureSize", (int)newWorldTexture.GetSize().x); _tileMaterial.SetShaderParam("TextureSize", (int)newWorldTexture.GetSize().x);
_tileMaterial.SetShaderParam("CoordinateOffsetU", (int) _worldView.WorldTextureCoordinateOffset.x); _tileMaterial.SetShaderParam("CoordinateOffsetU", (int)_tileInstanceManager.WorldTextureCoordinateOffset.x);
_tileMaterial.SetShaderParam("CoordinateOffsetV", (int) _worldView.WorldTextureCoordinateOffset.y); _tileMaterial.SetShaderParam("CoordinateOffsetV", (int)_tileInstanceManager.WorldTextureCoordinateOffset.y);
} }
public void OnGoldCountChanged(int goldCount) public void OnGoldCountChanged(int goldCount)
{ {
AnimationPlayer animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer"); var animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer");
_goldCountLabel.Text = goldCount.ToString(); _goldCountLabel.Text = goldCount.ToString();
animationPlayer.CurrentAnimation = "FlashLabel"; animationPlayer.CurrentAnimation = "FlashLabel";
animationPlayer.Seek(0); animationPlayer.Seek(0);

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@ using Godot.Collections;
using Vector2 = Godot.Vector2; using Vector2 = Godot.Vector2;
using Vector3 = Godot.Vector3; using Vector3 = Godot.Vector3;
public class WorldView : Spatial public class TileInstanceManager : Spatial
{ {
// ui elements // ui elements
@ -22,8 +22,6 @@ public class WorldView : Spatial
delegate void TileClicked(HexTile3D tile3d); delegate void TileClicked(HexTile3D tile3d);
[Signal] [Signal]
delegate void TileHovered(HexTile3D tile3d); delegate void TileHovered(HexTile3D tile3d);
[Signal]
delegate void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage);
// other members // other members
public Vector2 WorldTextureCoordinateOffset = Vector2.Zero; public Vector2 WorldTextureCoordinateOffset = Vector2.Zero;
@ -128,41 +126,6 @@ public class WorldView : Spatial
return null; 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<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices) private void HandleWorldTileChange(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices)
{ {
@ -206,8 +169,6 @@ public class WorldView : Spatial
_sceneTileChunks.Add(sceneTileChunk); _sceneTileChunks.Add(sceneTileChunk);
} }
UpdateWorldViewTexture();
GD.Print("Removed Chunks " + removedChunkIndices.Count); GD.Print("Removed Chunks " + removedChunkIndices.Count);
GD.Print("Added Chunks " + addedChunkIndices.Count); GD.Print("Added Chunks " + addedChunkIndices.Count);
GD.Print("Removed chunk count: " + removedChunks.Count); GD.Print("Removed chunk count: " + removedChunks.Count);

View File

@ -1,8 +1,8 @@
using Godot;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Godot;
using Godot.Collections; using Godot.Collections;
public class World : Spatial public class World : Spatial
@ -15,46 +15,41 @@ public class World : Spatial
Objects, Objects,
Done Done
} }
public GenerationState State = GenerationState.Done;
public Vector2 CenterChunkIndex = Vector2.Zero;
// referenced scenes
private PackedScene _worldChunkScene = GD.Load<PackedScene>("res://scenes/WorldChunk.tscn");
// constants // constants
public const int ChunkSize = 16; public const int ChunkSize = 16;
public const int NumChunkRows = 3; public const int NumChunkRows = 3;
public const int NumChunkColumns = NumChunkRows; public const int NumChunkColumns = NumChunkRows;
public int Seed = 0; private List<Vector2> _activeChunkIndices = new();
public HexGrid HexGrid = new HexGrid(); private readonly List<Vector2> _addedChunkIndices = new();
public Spatial Chunks; private readonly Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks;
public Color DebugColor; private Rect2 _centerChunkRect;
// ui elements
// scene nodes
// resources
// exports
// [Export] public Vector2 Size = new Vector2(1, 1);
// signals
[Signal]
delegate void OnTilesChanged(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices);
// delegate void OnCoordClicked(Vector2 world_pos); // delegate void OnCoordClicked(Vector2 world_pos);
// other members // other members
private Vector2 _centerPlaneCoord; private Vector2 _centerPlaneCoord;
private Rect2 _centerChunkRect = new Rect2();
private Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks; private readonly Image _heightmapImage = new();
private List<Vector2> _activeChunkIndices = new(); private ImageTexture _heightmapTexture;
private List<Vector2> _addedChunkIndices = new(); private readonly List<Vector2> _removedChunkIndices = new();
private List<Vector2> _removedChunkIndices = new(); private TileInstanceManager _tileInstanceManager;
private readonly Image _tileTypeMapImage = new();
private ImageTexture _viewTileTypeTexture;
// referenced scenes
private readonly PackedScene _worldChunkScene = GD.Load<PackedScene>("res://scenes/WorldChunk.tscn");
public Vector2 CenterChunkIndex = Vector2.Zero;
public Spatial Chunks;
public Color DebugColor;
public HexGrid HexGrid = new();
private OpenSimplexNoise noiseGenerator = new(); private OpenSimplexNoise noiseGenerator = new();
public int Seed = 0;
public GenerationState State = GenerationState.Done;
public World() public World()
{ {
Debug.Assert(ChunkSize % 2 == 0); Debug.Assert(ChunkSize % 2 == 0);
@ -68,8 +63,11 @@ public class World : Spatial
Chunks = (Spatial)FindNode("Chunks"); Chunks = (Spatial)FindNode("Chunks");
Debug.Assert(Chunks != null); Debug.Assert(Chunks != null);
_tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
Debug.Assert(_tileInstanceManager != null);
InitNoiseGenerator(); InitNoiseGenerator();
SetCenterPlaneCoord(Vector2.Zero); SetCenterPlaneCoord(Vector2.Zero);
} }
@ -88,7 +86,7 @@ public class World : Spatial
{ {
if (IsTileCached(xIndex, yIndex)) if (IsTileCached(xIndex, yIndex))
{ {
WorldChunk cachedChunk = _cachedWorldChunks[new Vector2(xIndex, yIndex)]; var cachedChunk = _cachedWorldChunks[new Vector2(xIndex, yIndex)];
return cachedChunk; return cachedChunk;
} }
@ -102,18 +100,18 @@ public class World : Spatial
private WorldChunk CreateWorldChunk(int xIndex, int yIndex, Color debugColor) private WorldChunk CreateWorldChunk(int xIndex, int yIndex, Color debugColor)
{ {
WorldChunk result = (WorldChunk)_worldChunkScene.Instance(); var result = (WorldChunk)_worldChunkScene.Instance();
result.SetSize(ChunkSize); result.SetSize(ChunkSize);
Vector2 offsetCoordSouthWest = new Vector2(xIndex, yIndex) * ChunkSize; var offsetCoordSouthWest = new Vector2(xIndex, yIndex) * ChunkSize;
Vector2 offsetCoordNorthEast = offsetCoordSouthWest + new Vector2(1, 1) * (ChunkSize - 1); var offsetCoordNorthEast = offsetCoordSouthWest + new Vector2(1, 1) * (ChunkSize - 1);
Vector2 planeCoordSouthWest = HexGrid.GetHexCenterFromOffset(offsetCoordSouthWest) + var planeCoordSouthWest = HexGrid.GetHexCenterFromOffset(offsetCoordSouthWest) +
new Vector2(-HexGrid.HexSize.x, HexGrid.HexSize.y) * 0.5f; new Vector2(-HexGrid.HexSize.x, HexGrid.HexSize.y) * 0.5f;
Vector2 planeCoordNorthEast = HexGrid.GetHexCenterFromOffset(offsetCoordNorthEast) + var planeCoordNorthEast = HexGrid.GetHexCenterFromOffset(offsetCoordNorthEast) +
new Vector2(HexGrid.HexSize.x, -HexGrid.HexSize.y) * 0.5f; new Vector2(HexGrid.HexSize.x, -HexGrid.HexSize.y) * 0.5f;
result.ChunkAddress = new Vector2(xIndex, yIndex); result.ChunkIndex = new Vector2(xIndex, yIndex);
result.PlaneRect = new Rect2( result.PlaneRect = new Rect2(
new Vector2(planeCoordSouthWest.x, planeCoordNorthEast.y), new Vector2(planeCoordSouthWest.x, planeCoordNorthEast.y),
new Vector2(planeCoordNorthEast.x - planeCoordSouthWest.x, planeCoordSouthWest.y - planeCoordNorthEast.y)); new Vector2(planeCoordNorthEast.x - planeCoordSouthWest.x, planeCoordSouthWest.y - planeCoordNorthEast.y));
@ -122,7 +120,7 @@ public class World : Spatial
result.DebugColor.a = 0.6f; result.DebugColor.a = 0.6f;
Chunks.AddChild(result); Chunks.AddChild(result);
Vector2 chunkIndex = new Vector2(xIndex, yIndex); var chunkIndex = new Vector2(xIndex, yIndex);
_cachedWorldChunks.Add(chunkIndex, result); _cachedWorldChunks.Add(chunkIndex, result);
return result; return result;
@ -135,156 +133,234 @@ public class World : Spatial
GD.PrintErr("Cannot update chunk to new planeCoord " + planeCoord + ": Chunk generation not yet finished!"); GD.PrintErr("Cannot update chunk to new planeCoord " + planeCoord + ": Chunk generation not yet finished!");
return; return;
} }
// mark all chunks as retired // mark all chunks as retired
Godot.Collections.Dictionary<Vector2, WorldChunk> oldCachedChunks = new(_cachedWorldChunks); Godot.Collections.Dictionary<Vector2, WorldChunk> oldCachedChunks = new(_cachedWorldChunks);
// set new center chunk // set new center chunk
var chunkIndex = GetChunkTupleFromPlaneCoord(planeCoord); var chunkIndex = GetChunkTupleFromPlaneCoord(planeCoord);
CenterChunkIndex = new Vector2(chunkIndex.Item1, chunkIndex.Item2); CenterChunkIndex = new Vector2(chunkIndex.Item1, chunkIndex.Item2);
WorldChunk currentChunk = GetOrCreateWorldChunk(chunkIndex.Item1, chunkIndex.Item2, var currentChunk = GetOrCreateWorldChunk(chunkIndex.Item1, chunkIndex.Item2,
new Color(GD.Randf(), GD.Randf(), GD.Randf())); new Color(GD.Randf(), GD.Randf(), GD.Randf()));
_centerChunkRect = currentChunk.PlaneRect; _centerChunkRect = currentChunk.PlaneRect;
// load or create adjacent chunks // load or create adjacent chunks
_activeChunkIndices = new List<Vector2>(); _activeChunkIndices = new List<Vector2>();
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 - 1)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 - 1)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 - 1)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 - 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 + 1)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 + 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 + 1)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 + 1));
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 + 1)); _activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 + 1));
Debug.Assert(_activeChunkIndices.Count == NumChunkRows * NumChunkColumns); Debug.Assert(_activeChunkIndices.Count == NumChunkRows * NumChunkColumns);
foreach(Vector2 activeChunkIndex in _activeChunkIndices) foreach (var activeChunkIndex in _activeChunkIndices)
{ GetOrCreateWorldChunk((int)activeChunkIndex.x, (int)activeChunkIndex.y,
GetOrCreateWorldChunk((int) activeChunkIndex.x, (int) activeChunkIndex.y, new Color(GD.Randf(), GD.Randf(), GD.Randf())); new Color(GD.Randf(), GD.Randf(), GD.Randf()));
}
// unload retired chunks // unload retired chunks
_removedChunkIndices.Clear(); _removedChunkIndices.Clear();
_addedChunkIndices.Clear(); _addedChunkIndices.Clear();
foreach (var cachedChunkKey in oldCachedChunks.Keys) foreach (var cachedChunkKey in oldCachedChunks.Keys)
{
if (!_activeChunkIndices.Contains(cachedChunkKey)) if (!_activeChunkIndices.Contains(cachedChunkKey))
{
RemoveChunk(cachedChunkKey); RemoveChunk(cachedChunkKey);
}
}
foreach (var chunkKey in _activeChunkIndices) foreach (var chunkKey in _activeChunkIndices)
{
if (!oldCachedChunks.ContainsKey(chunkKey)) if (!oldCachedChunks.ContainsKey(chunkKey))
{ {
_addedChunkIndices.Add(chunkKey); _addedChunkIndices.Add(chunkKey);
}
}
if (_addedChunkIndices.Count > 0) var chunk = _cachedWorldChunks[chunkKey];
{ GenerateChunkNoiseMap(chunk);
State = GenerationState.Heightmap;
} State = GenerationState.Heightmap;
}
}
private void GenerateChunkNoiseMap(WorldChunk chunk)
{
var chunkIndex = chunk.ChunkIndex;
var debugChunkColor = new Color(Mathf.Abs(chunkIndex.x) / 5, Mathf.Abs(chunkIndex.y) / 5,
Mathf.RoundToInt(Mathf.Abs(chunkIndex.x + chunkIndex.y)) % 2);
var noiseImageTexture = new ImageTexture();
noiseImageTexture.CreateFromImage(noiseGenerator.GetImage(ChunkSize, ChunkSize, chunkIndex * ChunkSize),
0);
// Debug Texture
var simpleImage = new Image();
simpleImage.Create(ChunkSize, ChunkSize, false, Image.Format.Rgb8);
simpleImage.Lock();
foreach (var i in Enumerable.Range(0, ChunkSize))
foreach (var j in Enumerable.Range(0, ChunkSize))
if ((i + j) % 2 == 0)
simpleImage.SetPixelv(new Vector2(i, j), Colors.Aqua);
else
simpleImage.SetPixelv(new Vector2(i, j), debugChunkColor);
simpleImage.Unlock();
// noiseImageTexture.CreateFromImage(simpleImage, 0);
chunk.SetNoisemap(noiseImageTexture);
} }
private void RemoveChunk(Vector2 cachedChunkKey) private void RemoveChunk(Vector2 cachedChunkKey)
{ {
_cachedWorldChunks.Remove(cachedChunkKey); _cachedWorldChunks.Remove(cachedChunkKey);
_removedChunkIndices.Add(cachedChunkKey); _removedChunkIndices.Add(cachedChunkKey);
foreach (WorldChunk chunk in Chunks.GetChildren()) foreach (WorldChunk chunk in Chunks.GetChildren())
{ if (chunk.ChunkIndex == new Vector2(cachedChunkKey.x, cachedChunkKey.y))
if (chunk.ChunkAddress == new Vector2(cachedChunkKey.x, cachedChunkKey.y))
{
chunk.QueueFree(); chunk.QueueFree();
}
}
} }
private Tuple<int, int> GetChunkTupleFromPlaneCoord(Vector2 planeCoord) private Tuple<int, int> GetChunkTupleFromPlaneCoord(Vector2 planeCoord)
{ {
HexCell centerOffsetCoord = HexGrid.GetHexAt(planeCoord); var centerOffsetCoord = HexGrid.GetHexAt(planeCoord);
Vector2 chunkIndexFloat = (centerOffsetCoord.OffsetCoords / (float)ChunkSize).Floor(); var chunkIndexFloat = (centerOffsetCoord.OffsetCoords / ChunkSize).Floor();
Tuple<int, int> chunkIndex = new Tuple<int, int>((int)chunkIndexFloat.x, (int)chunkIndexFloat.y); var chunkIndex = new Tuple<int, int>((int)chunkIndexFloat.x, (int)chunkIndexFloat.y);
return chunkIndex; return chunkIndex;
} }
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord) public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
{ {
if (!_centerChunkRect.HasPoint(centerPlaneCoord)) if (!_centerChunkRect.HasPoint(centerPlaneCoord)) UpdateCenterChunkFromPlaneCoord(centerPlaneCoord);
}
private void UpdateWorldViewTexture()
{
var worldChunkSize = ChunkSize;
var numWorldChunkRows = NumChunkRows;
var numWorldChunkColumns = NumChunkColumns;
_heightmapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
Image.Format.Rgba8);
_tileTypeMapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
Image.Format.Rgba8);
var chunkIndexSouthWest = Vector2.Inf;
var chunkIndexNorthEast = -Vector2.Inf;
foreach (var chunkIndex in _activeChunkIndices)
{ {
UpdateCenterChunkFromPlaneCoord(centerPlaneCoord); var worldChunk = GetOrCreateWorldChunk((int)chunkIndex.x, (int)chunkIndex.y, Colors.White);
if (chunkIndex.x <= chunkIndexSouthWest.x && chunkIndex.y <= chunkIndexSouthWest.y)
chunkIndexSouthWest = chunkIndex;
else if (chunkIndex.x >= chunkIndexNorthEast.x && chunkIndex.y >= chunkIndexNorthEast.y)
chunkIndexNorthEast = chunkIndex;
_heightmapImage.BlendRect(
worldChunk.HeightmapOffscreenViewport.GetTexture().GetData(),
new Rect2(Vector2.Zero, Vector2.One * worldChunkSize),
(chunkIndex - CenterChunkIndex + Vector2.One) * worldChunkSize);
_tileTypeMapImage.BlendRect(
worldChunk.TileTypeOffscreenViewport.GetTexture().GetData(),
new Rect2(Vector2.Zero, Vector2.One * worldChunkSize),
(chunkIndex - CenterChunkIndex + Vector2.One) * worldChunkSize);
}
_heightmapTexture = new ImageTexture();
_heightmapTexture.CreateFromImage(_heightmapImage);
_viewTileTypeTexture = new ImageTexture();
_viewTileTypeTexture.CreateFromImage(_tileTypeMapImage);
_tileInstanceManager.WorldTextureCoordinateOffset = chunkIndexSouthWest * worldChunkSize;
EmitSignal("OnWorldViewTileTypeImageChanged", _tileTypeMapImage);
EmitSignal("OnHeightmapImageChanged", _heightmapImage);
}
public override void _Process(float delta)
{
if (State == GenerationState.Heightmap)
{
// generate heightmap for all new chunks
foreach (var chunkIndex in _addedChunkIndices)
{
}
State = GenerationState.Heightmap;
}
var oldState = State;
if (State == GenerationState.Heightmap)
{
var numChunksGeneratingHeightmap = 0;
foreach (var chunkIndex in _addedChunkIndices)
{
var chunk = _cachedWorldChunks[chunkIndex];
if (chunk.HeightMapFrameCount > 0) numChunksGeneratingHeightmap++;
}
if (numChunksGeneratingHeightmap == 0)
{
// assign height map images
State = GenerationState.TileType;
foreach (var chunkIndex in _addedChunkIndices)
{
var chunk = _cachedWorldChunks[chunkIndex];
chunk.SetHeightmap(chunk.HeightmapOffscreenViewport.GetTexture());
}
}
}
else if (State == GenerationState.TileType)
{
// assign tile type images
// generate heightmap for all new chunks
foreach (var chunkIndex in _addedChunkIndices)
{
var chunk = _cachedWorldChunks[chunkIndex];
chunk.SetHeightmap(chunk.HeightmapOffscreenViewport.GetTexture());
}
State = GenerationState.Objects;
}
else if (State == GenerationState.Objects)
{
// generate objects
State = GenerationState.Done;
}
if (oldState != GenerationState.Done && State == GenerationState.Done)
{
UpdateWorldViewTexture();
EmitSignal("OnTilesChanged", _removedChunkIndices.ToArray(), _addedChunkIndices.ToArray());
} }
} }
public override void _Process(float delta) // ui elements
{
GenerationState oldState = State;
if (State == GenerationState.Heightmap)
{
// generate heightmap for all new chunks
foreach (Vector2 chunkIndex in _addedChunkIndices)
{
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
Color debugChunkColor = new Color(Mathf.Abs(chunkIndex.x) / 5, Mathf.Abs(chunkIndex.y) / 5, Mathf.RoundToInt(Mathf.Abs(chunkIndex.x + chunkIndex.y)) % 2);
GD.Print("Generating for offset " + chunkIndex + " chunk: " + chunk + " debugChunkColor: " + debugChunkColor);
ImageTexture noiseImageTexture = new ImageTexture();
noiseImageTexture.CreateFromImage(noiseGenerator.GetImage(ChunkSize, ChunkSize, chunkIndex * ChunkSize), (uint) 0);
// Debug Texture
Image simpleImage = new Image();
simpleImage.Create(ChunkSize, ChunkSize, false, Image.Format.Rgb8);
simpleImage.Lock();
foreach (int i in Enumerable.Range(0, ChunkSize)) // scene nodes
{
foreach (int j in Enumerable.Range(0, ChunkSize))
{
if ((i + j) % 2 == 0)
{
simpleImage.SetPixelv(new Vector2(i, j), Colors.Aqua);
}
else
{
simpleImage.SetPixelv(new Vector2(i, j), debugChunkColor);
}
}
}
simpleImage.Unlock(); // resources
// noiseImageTexture.CreateFromImage(simpleImage, 0);
chunk.SetHeightmap(noiseImageTexture);
}
// assign height map images
State = GenerationState.TileType;
} else if (State == GenerationState.TileType)
{
// assign tile type images
State = GenerationState.Objects; // exports
} else if (State == GenerationState.Objects) // [Export] public Vector2 Size = new Vector2(1, 1);
{
// generate objects
State = GenerationState.Done; // signals
} [Signal]
private delegate void OnTilesChanged(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices);
if (oldState != GenerationState.Done && State == GenerationState.Done) [Signal]
{ private delegate void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage);
EmitSignal("OnTilesChanged", _removedChunkIndices.ToArray(), _addedChunkIndices.ToArray());
} [Signal]
} private delegate void OnHeightmapImageChanged(Image heightmapImage);
} }

View File

@ -1,56 +1,45 @@
using Godot;
using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using Godot;
public class WorldChunk : Spatial public class WorldChunk : Spatial
{ {
// ui elements private Sprite _heightmapSprite;
private TextureRect _heightmapTextureRect;
private Sprite _noiseMask;
// scene nodes private Sprite _noiseSprite;
private MeshInstance PlaneRectMesh;
// resources private readonly SpatialMaterial _rectMaterial = new();
private bool _showTextureOverlay;
// exports [Export] public Vector2 ChunkIndex;
[Export] public Texture TileTypeMap; public Color DebugColor = Colors.White;
[Export] public Texture NavigationMap;
[Export] public Texture HeightMap; [Export] public Texture HeightMap;
[Export] public int Size = 32; public int HeightMapFrameCount;
[Export] public Vector2 ChunkAddress;
[Export] public Viewport HeightmapOffscreenViewport;
public bool ShowTextureOverlay [Export] public Texture NavigationMap;
{
get public bool NoiseTextureCheckerboardOverlay = true;
{
return _showTextureOverlay;
}
set
{
if (PlaneRectMesh != null)
{
PlaneRectMesh.Visible = value;
}
}
}
// signals // signals
// delegate void OnCoordClicked(Vector2 world_pos); // delegate void OnCoordClicked(Vector2 world_pos);
// other members // other members
public Rect2 PlaneRect; public Rect2 PlaneRect;
public Color DebugColor = Colors.White; // ui elements
public Viewport TileTypeOffscreenViewport;
public bool NoiseTextureCheckerboardOverlay = true; // scene nodes
private MeshInstance PlaneRectMesh;
[Export] public int Size = 32;
// resources
// exports
[Export] public Texture TileTypeMap;
public int TileTypeMapFrameCount;
public Viewport TileTypeOffscreenViewport;
private TextureRect _heightmapRect;
private SpatialMaterial _rectMaterial = new SpatialMaterial();
private bool _showTextureOverlay = false;
public WorldChunk() public WorldChunk()
{ {
} }
@ -60,20 +49,70 @@ public class WorldChunk : Spatial
SetSize(size); SetSize(size);
} }
[Export]
public bool ShowTextureOverlay
{
get => _showTextureOverlay;
set
{
if (PlaneRectMesh != null) PlaneRectMesh.Visible = value;
}
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
PlaneRectMesh = (MeshInstance)FindNode("PlaneRectMesh");
Debug.Assert(PlaneRectMesh != null);
if (PlaneRectMesh.Visible) _showTextureOverlay = true;
var planeRectTransform = Transform.Identity;
planeRectTransform =
planeRectTransform.Scaled(new Vector3(PlaneRect.Size.x, 0.125f, PlaneRect.Size.y));
planeRectTransform.origin.x = PlaneRect.GetCenter().x;
planeRectTransform.origin.z = PlaneRect.GetCenter().y;
PlaneRectMesh.Transform = planeRectTransform;
// PlaneRectMesh.MaterialOverride = new SpatialMaterial();
// ((SpatialMaterial)PlaneRectMesh.MaterialOverride).AlbedoColor = DebugColor;
// ((SpatialMaterial)PlaneRectMesh.MaterialOverride).FlagsTransparent = true;
HeightmapOffscreenViewport = (Viewport)FindNode("HeightmapOffscreenViewport");
HeightmapOffscreenViewport.Size = Vector2.One * Size;
Debug.Assert(HeightmapOffscreenViewport != null);
_noiseSprite = (Sprite)FindNode("NoiseSprite");
_noiseMask = (Sprite)FindNode("NoiseMask");
_heightmapSprite = (Sprite)FindNode("HeightmapSprite");
TileTypeOffscreenViewport = (Viewport)FindNode("TileTypeOffscreenViewport");
TileTypeOffscreenViewport.Size = Vector2.One * Size;
Debug.Assert(TileTypeOffscreenViewport != null);
SetSize(World.ChunkSize);
}
public void SetSize(int size) public void SetSize(int size)
{ {
Size = size; Size = size;
if (TileTypeOffscreenViewport != null) if (TileTypeOffscreenViewport != null)
{ {
TileTypeOffscreenViewport.Size = Vector2.One * size; TileTypeOffscreenViewport.Size = Vector2.One * size;
HeightmapOffscreenViewport.Size = Vector2.One * size;
_noiseMask.Transform = Transform2D.Identity.Scaled(Vector2.One * size / _noiseMask.Texture.GetSize().x);
_noiseSprite.Transform = Transform2D.Identity.Scaled(Vector2.One * size / _noiseSprite.Texture.GetSize().x);
_heightmapSprite.Transform =
Transform2D.Identity.Scaled(Vector2.One * size / _heightmapSprite.Texture.GetSize().x);
} }
} }
// other members // other members
public void SaveToFile(String chunkName) public void SaveToFile(string chunkName)
{ {
Image image = new Image(); var image = new Image();
image.CreateFromData(Size, Size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData()); image.CreateFromData(Size, Size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData());
image.SavePng(chunkName + "_tileType.png"); image.SavePng(chunkName + "_tileType.png");
@ -85,91 +124,75 @@ public class WorldChunk : Spatial
image.SavePng(chunkName + "_heightMap.png"); image.SavePng(chunkName + "_heightMap.png");
} }
public void LoadFromFile(String chunkName) public void LoadFromFile(string chunkName)
{ {
} }
// Called when the node enters the scene tree for the first time.
public override void _Ready() public void SetNoisemap(Texture texture)
{ {
PlaneRectMesh = (MeshInstance)FindNode("PlaneRectMesh"); _noiseSprite.Texture = texture;
Debug.Assert(PlaneRectMesh != null); _noiseSprite.Transform =
if (PlaneRectMesh.Visible) Transform2D.Identity.Scaled(HeightmapOffscreenViewport.Size / _noiseSprite.Texture.GetSize().x);
{ HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once;
_showTextureOverlay = true; HeightMapFrameCount = 1;
}
Transform planeRectTransform = Transform.Identity;
planeRectTransform =
planeRectTransform.Scaled(new Vector3(PlaneRect.Size.x, 0.125f, PlaneRect.Size.y));
planeRectTransform.origin.x = PlaneRect.GetCenter().x;
planeRectTransform.origin.z = PlaneRect.GetCenter().y;
PlaneRectMesh.Transform = planeRectTransform;
// PlaneRectMesh.MaterialOverride = new SpatialMaterial();
// ((SpatialMaterial)PlaneRectMesh.MaterialOverride).AlbedoColor = DebugColor;
// ((SpatialMaterial)PlaneRectMesh.MaterialOverride).FlagsTransparent = true;
TileTypeOffscreenViewport = (Viewport)FindNode("TileTypeOffscreenViewport");
TileTypeOffscreenViewport.Size = Vector2.One * Size;
Debug.Assert(TileTypeOffscreenViewport != null);
_heightmapRect = (TextureRect)FindNode("HeightmapTexture");
} }
public void SetHeightmap(Texture texture) public void SetHeightmap(Texture texture)
{ {
GD.Print("Setting HeightmapRect Texture: " + _heightmapRect + " with size " + _heightmapRect.GetSize()); _heightmapSprite.Texture = texture;
_heightmapRect.Texture = texture; _heightmapSprite.Transform =
Transform2D.Identity.Scaled(TileTypeOffscreenViewport.Size / _heightmapSprite.Texture.GetSize());
TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once;
TileTypeMapFrameCount = 1;
} }
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(float delta) public override void _Process(float delta)
{ {
Texture tileTypeTexture = TileTypeOffscreenViewport.GetTexture(); Texture tileTypeTexture = TileTypeOffscreenViewport.GetTexture();
if (NoiseTextureCheckerboardOverlay) if (NoiseTextureCheckerboardOverlay)
{ {
Image tileTypeImage = tileTypeTexture.GetData(); var tileTypeImage = tileTypeTexture.GetData();
tileTypeImage.Lock(); tileTypeImage.Lock();
foreach (int i in Enumerable.Range(0, Size)) foreach (var i in Enumerable.Range(0, Size))
foreach (var j in Enumerable.Range(0, Size))
{ {
foreach (int j in Enumerable.Range(0, Size)) var textureCoord = new Vector2(i, j);
{ var baseColor = tileTypeImage.GetPixelv(textureCoord);
Vector2 textureCoord = new Vector2(i, j);
Color baseColor = tileTypeImage.GetPixelv(textureCoord); if ((i + j) % 2 == 0)
tileTypeImage.SetPixelv(textureCoord, baseColor);
if ((i + j) % 2 == 0) else
{ tileTypeImage.SetPixelv(textureCoord, baseColor * 0.6f);
tileTypeImage.SetPixelv(textureCoord, baseColor);
}
else
{
tileTypeImage.SetPixelv(textureCoord, baseColor * 0.6f);
}
}
} }
tileTypeImage.Unlock(); tileTypeImage.Unlock();
ImageTexture imageTexture = new ImageTexture(); var imageTexture = new ImageTexture();
imageTexture.CreateFromImage(tileTypeImage, (uint) 0); imageTexture.CreateFromImage(tileTypeImage, 0);
tileTypeTexture = imageTexture; tileTypeTexture = imageTexture;
} }
_rectMaterial.AlbedoTexture = tileTypeTexture; _rectMaterial.AlbedoTexture = tileTypeTexture;
_rectMaterial.FlagsTransparent = true; _rectMaterial.FlagsTransparent = true;
// _rectMaterial.AlbedoTexture = _heightmapRect.Texture; // _rectMaterial.AlbedoTexture = _heightmapRect.Texture;
_rectMaterial.Uv1Scale = new Vector3(-3, -2, 1); _rectMaterial.Uv1Scale = new Vector3(-3, -2, 1);
_rectMaterial.Uv1Offset = Vector3.One * 2; _rectMaterial.Uv1Offset = Vector3.One * 2;
//RectMaterial.Uv1Triplanar = true; //RectMaterial.Uv1Triplanar = true;
PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial); PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial);
TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once; if (HeightMapFrameCount == 0) HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
HeightMapFrameCount = HeightMapFrameCount > 0 ? HeightMapFrameCount - 1 : 0;
if (TileTypeMapFrameCount == 0) TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Disabled;
TileTypeMapFrameCount = TileTypeMapFrameCount > 0 ? TileTypeMapFrameCount - 1 : 0;
PlaneRectMesh.MaterialOverride = null; PlaneRectMesh.MaterialOverride = null;
} }
} }

View File

@ -1,9 +1,10 @@
[gd_scene load_steps=9 format=2] [gd_scene load_steps=10 format=2]
[ext_resource path="res://scenes/WorldChunk.cs" type="Script" id=1] [ext_resource path="res://scenes/WorldChunk.cs" type="Script" id=1]
[ext_resource path="res://materials/IslandColorRampShader.tres" type="Material" id=2] [ext_resource path="res://materials/IslandColorRampShader.tres" type="Material" id=2]
[ext_resource path="res://assets/TestHeightmap.tres" type="Texture" id=3] [ext_resource path="res://assets/TestHeightmap.tres" type="Texture" id=3]
[ext_resource path="res://assets/4x4checkerColor.png" type="Texture" id=4] [ext_resource path="res://assets/4x4checkerColor.png" type="Texture" id=4]
[ext_resource path="res://addons/gdhexgrid/icon.png" type="Texture" id=6]
[sub_resource type="CubeMesh" id=27] [sub_resource type="CubeMesh" id=27]
size = Vector3( 1, 1, 1 ) size = Vector3( 1, 1, 1 )
@ -15,15 +16,10 @@ albedo_texture = ExtResource( 4 )
uv1_scale = Vector3( -3, -2, 0 ) uv1_scale = Vector3( -3, -2, 0 )
uv1_offset = Vector3( 2, 2, 0 ) uv1_offset = Vector3( 2, 2, 0 )
[sub_resource type="OpenSimplexNoise" id=29] [sub_resource type="CanvasItemMaterial" id=29]
octaves = 1 blend_mode = 3
period = 9.0
persistence = 0.0
[sub_resource type="NoiseTexture" id=30] [sub_resource type="ImageTexture" id=30]
width = 8
height = 8
noise = SubResource( 29 )
[node name="WorldChunk" type="Spatial"] [node name="WorldChunk" type="Spatial"]
script = ExtResource( 1 ) script = ExtResource( 1 )
@ -36,8 +32,8 @@ visible = false
mesh = SubResource( 27 ) mesh = SubResource( 27 )
material/0 = SubResource( 28 ) material/0 = SubResource( 28 )
[node name="TileTypeOffscreenViewport" type="Viewport" parent="."] [node name="HeightmapOffscreenViewport" type="Viewport" parent="."]
size = Vector2( 8, 8 ) size = Vector2( 50, 50 )
handle_input_locally = false handle_input_locally = false
hdr = false hdr = false
disable_3d = true disable_3d = true
@ -49,19 +45,29 @@ shadow_atlas_quad_1 = 0
shadow_atlas_quad_2 = 0 shadow_atlas_quad_2 = 0
shadow_atlas_quad_3 = 0 shadow_atlas_quad_3 = 0
[node name="NoiseTexture" type="TextureRect" parent="TileTypeOffscreenViewport"] [node name="NoiseSprite" type="Sprite" parent="HeightmapOffscreenViewport"]
visible = false texture = ExtResource( 6 )
margin_right = 40.0 centered = false
margin_bottom = 40.0
[node name="NoiseMask" type="Sprite" parent="HeightmapOffscreenViewport"]
material = SubResource( 29 )
texture = SubResource( 30 ) texture = SubResource( 30 )
centered = false
[node name="IslandShader" type="TextureRect" parent="TileTypeOffscreenViewport"] [node name="TileTypeOffscreenViewport" type="Viewport" parent="."]
size = Vector2( 100, 100 )
handle_input_locally = false
hdr = false
disable_3d = true
usage = 0
render_target_v_flip = true
render_target_update_mode = 3
shadow_atlas_quad_0 = 0
shadow_atlas_quad_1 = 0
shadow_atlas_quad_2 = 0
shadow_atlas_quad_3 = 0
[node name="HeightmapSprite" type="Sprite" parent="TileTypeOffscreenViewport"]
material = ExtResource( 2 ) material = ExtResource( 2 )
margin_right = 100.0
margin_bottom = 100.0
[node name="HeightmapTexture" type="TextureRect" parent="TileTypeOffscreenViewport/IslandShader"]
use_parent_material = true
margin_right = 100.0
margin_bottom = 100.0
texture = ExtResource( 3 ) texture = ExtResource( 3 )
centered = false