318 lines
11 KiB
C#
318 lines
11 KiB
C#
using System.Diagnostics;
|
|
using System.Linq;
|
|
using Godot;
|
|
using Godot.Collections;
|
|
|
|
public class WorldChunk : Spatial
|
|
{
|
|
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
|
|
private MultiMeshInstance _multiMeshInstance;
|
|
private readonly Array<int> _tileInstanceIndices = new();
|
|
|
|
private readonly SpatialMaterial _rectMaterial = new();
|
|
private Sprite _heightmapSprite;
|
|
private TextureRect _heightmapTextureRect;
|
|
private Sprite _noiseMask;
|
|
|
|
private Sprite _noiseSprite;
|
|
private bool _showTextureOverlay;
|
|
|
|
[Export] public Vector2 ChunkIndex;
|
|
|
|
public Color DebugColor = Colors.White;
|
|
public Spatial Entities;
|
|
public Spatial Tiles;
|
|
private readonly HexGrid _hexGrid = new();
|
|
private readonly bool _showHexTiles;
|
|
|
|
[Export] public Texture HeightMap;
|
|
public int HeightMapFrameCount;
|
|
|
|
public Viewport HeightmapOffscreenViewport;
|
|
[Export] public Texture NavigationMap;
|
|
|
|
public bool NoiseTextureCheckerboardOverlay = true;
|
|
|
|
// signals
|
|
[Signal]
|
|
private delegate void TileClicked(HexTile3D tile3d);
|
|
|
|
[Signal]
|
|
private delegate void TileHovered(HexTile3D tile3d);
|
|
|
|
// other members
|
|
public Rect2 PlaneRect;
|
|
|
|
// ui elements
|
|
|
|
// scene nodes
|
|
private MeshInstance PlaneRectMesh;
|
|
[Export] public int Size = 32;
|
|
|
|
// resources
|
|
|
|
// exports
|
|
[Export] public Texture TileTypeMap;
|
|
public int TileTypeMapFrameCount;
|
|
public Viewport TileTypeOffscreenViewport;
|
|
|
|
public WorldChunk()
|
|
{
|
|
}
|
|
|
|
public WorldChunk(int 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;
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
Entities = (Spatial)FindNode("Entities");
|
|
Debug.Assert(Entities != null);
|
|
|
|
Tiles = (Spatial)FindNode("Tiles");
|
|
Debug.Assert(Tiles != null);
|
|
|
|
SetSize(World.ChunkSize);
|
|
}
|
|
|
|
public void SetSize(int size)
|
|
{
|
|
Size = size;
|
|
|
|
if (TileTypeOffscreenViewport != null)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
public void SetChunkIndex(Vector2 chunkIndex, HexGrid hexGrid)
|
|
{
|
|
ChunkIndex = chunkIndex;
|
|
float chunkSize = World.ChunkSize;
|
|
|
|
Vector2 planeCoordSouthWest = hexGrid.GetHexCenterFromOffset(chunkIndex * chunkSize);
|
|
|
|
Transform = new Transform(Basis.Identity, new Vector3(planeCoordSouthWest.x, 0, planeCoordSouthWest.y));
|
|
|
|
Vector2 localPlaneCoordSouthWest = new Vector2(-hexGrid.HexSize.x, hexGrid.HexSize.y) * 0.5f;
|
|
Vector2 localPlaneCoordNorthEast = hexGrid.GetHexCenterFromOffset(Vector2.One * chunkSize) +
|
|
new Vector2(hexGrid.HexSize.x, -hexGrid.HexSize.y) * 0.5f;
|
|
|
|
PlaneRect = new Rect2(
|
|
new Vector2(localPlaneCoordSouthWest.x, localPlaneCoordNorthEast.y),
|
|
new Vector2(localPlaneCoordNorthEast.x - localPlaneCoordSouthWest.x,
|
|
localPlaneCoordSouthWest.y - localPlaneCoordNorthEast.y)
|
|
);
|
|
}
|
|
|
|
public void InitializeTileInstances(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance,
|
|
int tileInstanceIndexStart)
|
|
{
|
|
_multiMeshInstance = multiMeshInstance;
|
|
_tileInstanceIndices.Clear();
|
|
|
|
int chunkSize = World.ChunkSize;
|
|
|
|
foreach (Spatial node in Tiles.GetChildren())
|
|
node.QueueFree();
|
|
|
|
foreach (int i in Enumerable.Range(0, chunkSize))
|
|
foreach (int j in Enumerable.Range(0, chunkSize))
|
|
{
|
|
HexTile3D tile3D = (HexTile3D)_hexTile3DScene.Instance();
|
|
tile3D.Connect("TileClicked", this, nameof(OnTileClicked));
|
|
tile3D.Connect("TileHovered", this, nameof(OnTileHovered));
|
|
|
|
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * World.ChunkSize + new Vector2(i, j));
|
|
_tileInstanceIndices.Add(tileInstanceIndexStart + _tileInstanceIndices.Count);
|
|
|
|
Transform tileTransform = Transform.Identity;
|
|
Vector2 centerPlaneCoord = _hexGrid.GetHexCenterFromOffset(new Vector2(i, j));
|
|
tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y);
|
|
tile3D.Transform = tileTransform;
|
|
|
|
Tiles.AddChild(tile3D);
|
|
}
|
|
|
|
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
|
|
}
|
|
|
|
public void ClearContent()
|
|
{
|
|
foreach (Spatial child in Entities.GetChildren())
|
|
child.QueueFree();
|
|
}
|
|
|
|
public void UpdateTileTransforms()
|
|
{
|
|
Transform chunkTransform = Transform.Identity;
|
|
Vector2 chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(ChunkIndex * World.ChunkSize);
|
|
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
|
|
Transform = chunkTransform;
|
|
|
|
Basis tileOrientation = new(Vector3.Up, 90f * Mathf.Pi / 180f);
|
|
|
|
GD.Print("Updating transforms for instances of chunk " + ChunkIndex + " origin: " + chunkTransform.origin);
|
|
|
|
foreach (int i in Enumerable.Range(0, _tileInstanceIndices.Count))
|
|
{
|
|
int column = i % World.ChunkSize;
|
|
int row = i / World.ChunkSize;
|
|
|
|
Vector2 tilePlaneCoord =
|
|
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
|
|
|
|
Transform hexTransform = new(tileOrientation,
|
|
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
|
|
|
|
if (_showHexTiles)
|
|
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
|
|
hexTransform.origin);
|
|
|
|
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
|
|
}
|
|
}
|
|
|
|
// other members
|
|
public void SaveToFile(string chunkName)
|
|
{
|
|
Image image = new();
|
|
|
|
image.CreateFromData(Size, Size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData());
|
|
image.SavePng(chunkName + "_tileType.png");
|
|
|
|
image.CreateFromData(Size, Size, false, Image.Format.Rgba8, NavigationMap.GetData().GetData());
|
|
image.SavePng(chunkName + "_navigationMap.png");
|
|
|
|
image.CreateFromData(Size, Size, false, Image.Format.Rgba8, HeightMap.GetData().GetData());
|
|
image.SavePng(chunkName + "_heightMap.png");
|
|
}
|
|
|
|
public void LoadFromFile(string chunkName)
|
|
{
|
|
}
|
|
|
|
|
|
public void SetNoisemap(Texture texture)
|
|
{
|
|
_noiseSprite.Texture = texture;
|
|
_noiseSprite.Transform =
|
|
Transform2D.Identity.Scaled(HeightmapOffscreenViewport.Size / _noiseSprite.Texture.GetSize().x);
|
|
HeightmapOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once;
|
|
HeightMapFrameCount = 1;
|
|
}
|
|
|
|
public void SetHeightmap(Texture texture)
|
|
{
|
|
_heightmapSprite.Texture = texture;
|
|
_heightmapSprite.Transform =
|
|
Transform2D.Identity.Scaled(TileTypeOffscreenViewport.Size / _heightmapSprite.Texture.GetSize());
|
|
|
|
TileTypeOffscreenViewport.RenderTargetUpdateMode = Viewport.UpdateMode.Once;
|
|
TileTypeMapFrameCount = 1;
|
|
}
|
|
|
|
public override void _Process(float delta)
|
|
{
|
|
Texture tileTypeTexture = TileTypeOffscreenViewport.GetTexture();
|
|
|
|
if (NoiseTextureCheckerboardOverlay)
|
|
{
|
|
Image tileTypeImage = tileTypeTexture.GetData();
|
|
tileTypeImage.Lock();
|
|
|
|
foreach (int i in Enumerable.Range(0, Size))
|
|
foreach (int j in Enumerable.Range(0, Size))
|
|
{
|
|
Vector2 textureCoord = new(i, j);
|
|
Color baseColor = tileTypeImage.GetPixelv(textureCoord);
|
|
|
|
if ((i + j) % 2 == 0)
|
|
tileTypeImage.SetPixelv(textureCoord, baseColor);
|
|
else
|
|
tileTypeImage.SetPixelv(textureCoord, baseColor * 0.6f);
|
|
}
|
|
|
|
tileTypeImage.Unlock();
|
|
ImageTexture imageTexture = new();
|
|
imageTexture.CreateFromImage(tileTypeImage, 0);
|
|
tileTypeTexture = imageTexture;
|
|
}
|
|
|
|
_rectMaterial.AlbedoTexture = tileTypeTexture;
|
|
|
|
_rectMaterial.FlagsTransparent = true;
|
|
// _rectMaterial.AlbedoTexture = _heightmapRect.Texture;
|
|
_rectMaterial.Uv1Scale = new Vector3(-3, -2, 1);
|
|
_rectMaterial.Uv1Offset = Vector3.One * 2;
|
|
|
|
//RectMaterial.Uv1Triplanar = true;
|
|
PlaneRectMesh.SetSurfaceMaterial(0, _rectMaterial);
|
|
|
|
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;
|
|
}
|
|
|
|
public void OnTileClicked(HexTile3D tile)
|
|
{
|
|
EmitSignal("TileClicked", tile);
|
|
}
|
|
|
|
public void OnTileHovered(HexTile3D tile)
|
|
{
|
|
EmitSignal("TileHovered", tile);
|
|
}
|
|
} |